Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2021 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : :
6 : : #include "spdk/stdinc.h"
7 : : #include "spdk/likely.h"
8 : : #include "spdk/log.h"
9 : : #include "spdk/trace_parser.h"
10 : : #include "spdk/util.h"
11 : :
12 : : #include <exception>
13 : : #include <map>
14 : : #include <new>
15 : :
16 : : struct entry_key {
17 : 1012916 : entry_key(uint16_t _lcore, uint64_t _tsc) : lcore(_lcore), tsc(_tsc) {}
18 : : uint16_t lcore;
19 : : uint64_t tsc;
20 : : };
21 : :
22 : : class compare_entry_key
23 : : {
24 : : public:
25 : 25525284 : bool operator()(const entry_key &first, const entry_key &second) const
26 : : {
27 [ - + ]: 25525284 : if (first.tsc == second.tsc) {
28 : 0 : return first.lcore < second.lcore;
29 : : } else {
30 : 25525284 : return first.tsc < second.tsc;
31 : : }
32 : : }
33 : : };
34 : :
35 : : typedef std::map<entry_key, spdk_trace_entry *, compare_entry_key> entry_map;
36 : :
37 : : struct argument_context {
38 : : spdk_trace_entry *entry;
39 : : spdk_trace_entry_buffer *buffer;
40 : : uint16_t lcore;
41 : : size_t offset;
42 : :
43 : 1012916 : argument_context(spdk_trace_entry *entry, uint16_t lcore) :
44 : 1012916 : entry(entry), lcore(lcore)
45 : : {
46 : 1012916 : buffer = reinterpret_cast<spdk_trace_entry_buffer *>(entry);
47 : :
48 : : /* The first argument resides within the spdk_trace_entry structure, so the initial
49 : : * offset needs to be adjusted to the start of the spdk_trace_entry.args array
50 : : */
51 : 1012916 : offset = offsetof(spdk_trace_entry, args) -
52 : : offsetof(spdk_trace_entry_buffer, data);
53 : 1012916 : }
54 : : };
55 : :
56 : : struct object_stats {
57 : : std::map<uint64_t, uint64_t> index;
58 : : std::map<uint64_t, uint64_t> start;
59 : : uint64_t counter;
60 : :
61 : 512 : object_stats() : counter(0) {}
62 : : };
63 : :
64 : : struct spdk_trace_parser {
65 : : spdk_trace_parser(const spdk_trace_parser_opts *opts);
66 : : ~spdk_trace_parser();
67 : : spdk_trace_parser(const spdk_trace_parser &) = delete;
68 : : spdk_trace_parser &operator=(const spdk_trace_parser &) = delete;
69 : 2 : const spdk_trace_flags *flags() const { return &_histories->flags; }
70 : 2 : uint64_t tsc_offset() const { return _tsc_offset; }
71 : : bool next_entry(spdk_trace_parser_entry *entry);
72 : : uint64_t entry_count(uint16_t lcore) const;
73 : : private:
74 : : spdk_trace_entry_buffer *get_next_buffer(spdk_trace_entry_buffer *buf, uint16_t lcore);
75 : : bool build_arg(argument_context *argctx, const spdk_trace_argument *arg, int argid,
76 : : spdk_trace_parser_entry *pe);
77 : : void populate_events(spdk_trace_history *history, int num_entries);
78 : : bool init(const spdk_trace_parser_opts *opts);
79 : : void cleanup();
80 : :
81 : : spdk_trace_histories *_histories;
82 : : size_t _map_size;
83 : : int _fd;
84 : : uint64_t _tsc_offset;
85 : : entry_map _entries;
86 : : entry_map::iterator _iter;
87 : : object_stats _stats[SPDK_TRACE_MAX_OBJECT];
88 : : };
89 : :
90 : : uint64_t
91 : 256 : spdk_trace_parser::entry_count(uint16_t lcore) const
92 : : {
93 : : spdk_trace_history *history;
94 : :
95 [ - + ]: 256 : if (lcore >= SPDK_TRACE_MAX_LCORE) {
96 : 0 : return 0;
97 : : }
98 : :
99 : 256 : history = spdk_get_per_lcore_history(_histories, lcore);
100 : :
101 [ + + ]: 256 : return history == NULL ? 0 : history->num_entries;
102 : : }
103 : :
104 : : spdk_trace_entry_buffer *
105 : 230392 : spdk_trace_parser::get_next_buffer(spdk_trace_entry_buffer *buf, uint16_t lcore)
106 : : {
107 : : spdk_trace_history *history;
108 : :
109 : 230392 : history = spdk_get_per_lcore_history(_histories, lcore);
110 [ - + ]: 230392 : assert(history);
111 : :
112 [ - + ]: 230392 : if (spdk_unlikely(static_cast<void *>(buf) ==
113 : : static_cast<void *>(&history->entries[history->num_entries - 1]))) {
114 : 0 : return reinterpret_cast<spdk_trace_entry_buffer *>(&history->entries[0]);
115 : : } else {
116 : 230392 : return buf + 1;
117 : : }
118 : : }
119 : :
120 : : bool
121 : 697666 : spdk_trace_parser::build_arg(argument_context *argctx, const spdk_trace_argument *arg, int argid,
122 : : spdk_trace_parser_entry *pe)
123 : : {
124 : 697666 : spdk_trace_entry *entry = argctx->entry;
125 : 697666 : spdk_trace_entry_buffer *buffer = argctx->buffer;
126 : : size_t curlen, argoff;
127 : :
128 : 697666 : argoff = 0;
129 : : /* Make sure that if we only copy a 4-byte integer, that the upper bytes have already been
130 : : * zeroed.
131 : : */
132 : 697666 : pe->args[argid].integer = 0;
133 [ + + ]: 1549012 : while (argoff < arg->size) {
134 [ + + ]: 851346 : if (argctx->offset == sizeof(buffer->data)) {
135 : 230392 : buffer = get_next_buffer(buffer, argctx->lcore);
136 [ + - - + : 230392 : if (spdk_unlikely(buffer->tpoint_id != SPDK_TRACE_MAX_TPOINT_ID ||
- + ]
137 : : buffer->tsc != entry->tsc)) {
138 : 0 : return false;
139 : : }
140 : :
141 : 230392 : argctx->offset = 0;
142 : 230392 : argctx->buffer = buffer;
143 : : }
144 : :
145 : 851346 : curlen = spdk_min(sizeof(buffer->data) - argctx->offset, arg->size - argoff);
146 [ + - ]: 851346 : if (argoff < sizeof(pe->args[0])) {
147 [ - + - + ]: 851346 : memcpy(&pe->args[argid].string[argoff], &buffer->data[argctx->offset],
148 : 851346 : spdk_min(curlen, sizeof(pe->args[0]) - argoff));
149 : : }
150 : :
151 : 851346 : argctx->offset += curlen;
152 : 851346 : argoff += curlen;
153 : : }
154 : :
155 : 697666 : return true;
156 : : }
157 : :
158 : : bool
159 : 1012918 : spdk_trace_parser::next_entry(spdk_trace_parser_entry *pe)
160 : : {
161 : : spdk_trace_tpoint *tpoint;
162 : : spdk_trace_entry *entry;
163 : : object_stats *stats;
164 : 1012918 : std::map<uint64_t, uint64_t>::iterator related_kv;
165 : :
166 [ + + ]: 1012918 : if (_iter == _entries.end()) {
167 : 2 : return false;
168 : : }
169 : :
170 : 1012916 : pe->entry = entry = _iter->second;
171 : 1012916 : pe->lcore = _iter->first.lcore;
172 : : /* Set related index to the max value to indicate "empty" state */
173 : 1012916 : pe->related_index = UINT64_MAX;
174 : 1012916 : pe->related_type = OBJECT_NONE;
175 : 1012916 : tpoint = &_histories->flags.tpoint[entry->tpoint_id];
176 : 1012916 : stats = &_stats[tpoint->object_type];
177 : :
178 [ + + ]: 1012916 : if (tpoint->new_object) {
179 [ + - ]: 311834 : stats->index[entry->object_id] = stats->counter++;
180 [ + - ]: 311834 : stats->start[entry->object_id] = entry->tsc;
181 : : }
182 : :
183 [ + + ]: 1012916 : if (tpoint->object_type != OBJECT_NONE) {
184 [ + - + - ]: 822474 : if (spdk_likely(stats->start.find(entry->object_id) != stats->start.end())) {
185 [ + - ]: 822474 : pe->object_index = stats->index[entry->object_id];
186 [ + - ]: 822474 : pe->object_start = stats->start[entry->object_id];
187 : : } else {
188 : 0 : pe->object_index = UINT64_MAX;
189 : 0 : pe->object_start = UINT64_MAX;
190 : : }
191 : : }
192 : :
193 : 1012916 : argument_context argctx(entry, pe->lcore);
194 [ + + ]: 1710582 : for (uint8_t i = 0; i < tpoint->num_args; ++i) {
195 [ + - - + ]: 697666 : if (!build_arg(&argctx, &tpoint->args[i], i, pe)) {
196 [ # # ]: 0 : SPDK_ERRLOG("Failed to parse tracepoint argument\n");
197 : 0 : return false;
198 : : }
199 : : }
200 : :
201 [ + - ]: 1012916 : for (uint8_t i = 0; i < SPDK_TRACE_MAX_RELATIONS; ++i) {
202 : : /* The relations are stored inside a tpoint, which means there might be
203 : : * multiple objects bound to a single tpoint. */
204 [ + - ]: 1012916 : if (tpoint->related_objects[i].object_type == OBJECT_NONE) {
205 : 1012916 : break;
206 : : }
207 : 0 : stats = &_stats[tpoint->related_objects[i].object_type];
208 [ # # ]: 0 : related_kv = stats->index.find(reinterpret_cast<uint64_t>
209 [ # # ]: 0 : (pe->args[tpoint->related_objects[i].arg_index].pointer));
210 : : /* To avoid parsing the whole array, object index and type are stored
211 : : * directly inside spdk_trace_parser_entry. */
212 [ # # ]: 0 : if (related_kv != stats->index.end()) {
213 : 0 : pe->related_index = related_kv->second;
214 : 0 : pe->related_type = tpoint->related_objects[i].object_type;
215 : 0 : break;
216 : : }
217 : : }
218 : :
219 : 1012916 : _iter++;
220 : 1012916 : return true;
221 : : }
222 : :
223 : : void
224 : 8 : spdk_trace_parser::populate_events(spdk_trace_history *history, int num_entries)
225 : : {
226 : : int i, num_entries_filled;
227 : : spdk_trace_entry *e;
228 : : int first, last, lcore;
229 : :
230 : 8 : lcore = history->lcore;
231 : 8 : e = history->entries;
232 : :
233 : 8 : num_entries_filled = num_entries;
234 [ - + ]: 8 : while (e[num_entries_filled - 1].tsc == 0) {
235 : 0 : num_entries_filled--;
236 : : }
237 : :
238 [ + - ]: 8 : if (num_entries == num_entries_filled) {
239 : 8 : first = last = 0;
240 [ + + ]: 1243308 : for (i = 1; i < num_entries; i++) {
241 [ - + ]: 1243300 : if (e[i].tsc < e[first].tsc) {
242 : 0 : first = i;
243 : : }
244 [ + + ]: 1243300 : if (e[i].tsc > e[last].tsc) {
245 : 1012908 : last = i;
246 : : }
247 : : }
248 : : } else {
249 : 0 : first = 0;
250 : 0 : last = num_entries_filled - 1;
251 : : }
252 : :
253 : : /*
254 : : * We keep track of the highest first TSC out of all reactors.
255 : : * We will ignore any events that occurred before this TSC on any
256 : : * other reactors. This will ensure we only print data for the
257 : : * subset of time where we have data across all reactors.
258 : : */
259 [ + + ]: 8 : if (e[first].tsc > _tsc_offset) {
260 : 3 : _tsc_offset = e[first].tsc;
261 : : }
262 : :
263 : 8 : i = first;
264 : : while (1) {
265 [ + + ]: 1243308 : if (e[i].tpoint_id != SPDK_TRACE_MAX_TPOINT_ID) {
266 [ + - ]: 1012916 : _entries[entry_key(lcore, e[i].tsc)] = &e[i];
267 : : }
268 [ + + ]: 1243308 : if (i == last) {
269 : 8 : break;
270 : : }
271 : 1243300 : i++;
272 [ - + ]: 1243300 : if (i == num_entries_filled) {
273 : 0 : i = 0;
274 : : }
275 : : }
276 : 8 : }
277 : :
278 : : bool
279 : 2 : spdk_trace_parser::init(const spdk_trace_parser_opts *opts)
280 : : {
281 : : spdk_trace_history *history;
282 : 0 : struct stat st;
283 : : int rc, i;
284 : :
285 [ + - - ]: 2 : switch (opts->mode) {
286 : 2 : case SPDK_TRACE_PARSER_MODE_FILE:
287 [ - + + - ]: 2 : _fd = open(opts->filename, O_RDONLY);
288 : 2 : break;
289 : 0 : case SPDK_TRACE_PARSER_MODE_SHM:
290 [ # # ]: 0 : _fd = shm_open(opts->filename, O_RDONLY, 0600);
291 : 0 : break;
292 : 0 : default:
293 [ # # ]: 0 : SPDK_ERRLOG("Invalid mode: %d\n", opts->mode);
294 : 0 : return false;
295 : : }
296 : :
297 [ - + ]: 2 : if (_fd < 0) {
298 [ # # ]: 0 : SPDK_ERRLOG("Could not open trace file: %s (%d)\n", opts->filename, errno);
299 : 0 : return false;
300 : : }
301 : :
302 : 2 : rc = fstat(_fd, &st);
303 [ - + ]: 2 : if (rc < 0) {
304 [ # # ]: 0 : SPDK_ERRLOG("Could not get size of trace file: %s\n", opts->filename);
305 : 0 : return false;
306 : : }
307 : :
308 [ - + ]: 2 : if ((size_t)st.st_size < sizeof(*_histories)) {
309 [ # # ]: 0 : SPDK_ERRLOG("Invalid trace file: %s\n", opts->filename);
310 : 0 : return false;
311 : : }
312 : :
313 : : /* Map the header of trace file */
314 : 2 : _map_size = sizeof(*_histories);
315 : 2 : _histories = static_cast<spdk_trace_histories *>(mmap(NULL, _map_size, PROT_READ,
316 : : MAP_SHARED, _fd, 0));
317 [ - + ]: 2 : if (_histories == MAP_FAILED) {
318 [ # # ]: 0 : SPDK_ERRLOG("Could not mmap trace file: %s\n", opts->filename);
319 : 0 : _histories = NULL;
320 : 0 : return false;
321 : : }
322 : :
323 : : /* Remap the entire trace file */
324 : 2 : _map_size = spdk_get_trace_histories_size(_histories);
325 : 2 : munmap(_histories, sizeof(*_histories));
326 [ - + ]: 2 : if ((size_t)st.st_size < _map_size) {
327 [ # # ]: 0 : SPDK_ERRLOG("Trace file %s is not valid\n", opts->filename);
328 : 0 : _histories = NULL;
329 : 0 : return false;
330 : : }
331 : 2 : _histories = static_cast<spdk_trace_histories *>(mmap(NULL, _map_size, PROT_READ,
332 : : MAP_SHARED, _fd, 0));
333 [ - + ]: 2 : if (_histories == MAP_FAILED) {
334 [ # # ]: 0 : SPDK_ERRLOG("Could not mmap trace file: %s\n", opts->filename);
335 : 0 : _histories = NULL;
336 : 0 : return false;
337 : : }
338 : :
339 [ + - ]: 2 : if (opts->lcore == SPDK_TRACE_MAX_LCORE) {
340 [ + + ]: 258 : for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) {
341 : 256 : history = spdk_get_per_lcore_history(_histories, i);
342 [ + + + - : 256 : if (history == NULL || history->num_entries == 0 || history->entries[0].tsc == 0) {
- + ]
343 : 248 : continue;
344 : : }
345 : :
346 [ + - ]: 8 : populate_events(history, history->num_entries);
347 : : }
348 : : } else {
349 : 0 : history = spdk_get_per_lcore_history(_histories, opts->lcore);
350 [ # # ]: 0 : if (history == NULL) {
351 [ # # ]: 0 : SPDK_ERRLOG("Trace file %s has no trace history for lcore %d\n",
352 : : opts->filename, opts->lcore);
353 : 0 : return false;
354 : : }
355 [ # # # # ]: 0 : if (history->num_entries > 0 && history->entries[0].tsc != 0) {
356 [ # # ]: 0 : populate_events(history, history->num_entries);
357 : : }
358 : : }
359 : :
360 : 2 : _iter = _entries.begin();
361 : 2 : return true;
362 : : }
363 : :
364 : : void
365 : 2 : spdk_trace_parser::cleanup()
366 : : {
367 [ + - ]: 2 : if (_histories != NULL) {
368 : 2 : munmap(_histories, _map_size);
369 : : }
370 : :
371 [ + - ]: 2 : if (_fd > 0) {
372 : 2 : close(_fd);
373 : : }
374 : 2 : }
375 : :
376 : 2 : spdk_trace_parser::spdk_trace_parser(const spdk_trace_parser_opts *opts) :
377 : 2 : _histories(NULL),
378 : 2 : _map_size(0),
379 : 2 : _fd(-1),
380 [ + + - - : 514 : _tsc_offset(0)
# # ]
381 : : {
382 [ + - - + ]: 2 : if (!init(opts)) {
383 [ # # ]: 0 : cleanup();
384 : 0 : throw std::exception();
385 : : }
386 [ - - # # ]: 2 : }
387 : :
388 [ + - # # ]: 1028 : spdk_trace_parser::~spdk_trace_parser()
389 : : {
390 : 2 : cleanup();
391 [ + + # # ]: 516 : }
392 : :
393 : : struct spdk_trace_parser *
394 : 2 : spdk_trace_parser_init(const struct spdk_trace_parser_opts *opts)
395 : : {
396 : : try {
397 [ + - + - : 2 : return new spdk_trace_parser(opts);
- - ]
398 : 0 : } catch (...) {
399 : 0 : return NULL;
400 : 0 : }
401 : : }
402 : :
403 : : void
404 : 2 : spdk_trace_parser_cleanup(struct spdk_trace_parser *parser)
405 : : {
406 [ + - ]: 2 : delete parser;
407 : 2 : }
408 : :
409 : : const struct spdk_trace_flags *
410 : 2 : spdk_trace_parser_get_flags(const struct spdk_trace_parser *parser)
411 : : {
412 : 2 : return parser->flags();
413 : : }
414 : :
415 : : uint64_t
416 : 2 : spdk_trace_parser_get_tsc_offset(const struct spdk_trace_parser *parser)
417 : : {
418 : 2 : return parser->tsc_offset();
419 : : }
420 : :
421 : : bool
422 : 1012918 : spdk_trace_parser_next_entry(struct spdk_trace_parser *parser,
423 : : struct spdk_trace_parser_entry *entry)
424 : : {
425 : 1012918 : return parser->next_entry(entry);
426 : : }
427 : :
428 : : uint64_t
429 : 256 : spdk_trace_parser_get_entry_count(const struct spdk_trace_parser *parser, uint16_t lcore)
430 : : {
431 : 256 : return parser->entry_count(lcore);
432 : : }
|