1 /*- 2 * BSD LICENSE 3 * 4 * Copyright (c) Intel Corporation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * * Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * * Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * * Neither the name of Intel Corporation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include "spdk/stdinc.h" 35 36 #include <map> 37 38 extern "C" { 39 #include "spdk/trace.h" 40 #include "spdk/util.h" 41 } 42 43 static struct spdk_trace_histories *g_histories; 44 static bool g_print_tsc = false; 45 46 static void usage(void); 47 48 struct entry_key { 49 entry_key(uint16_t _lcore, uint64_t _tsc) : lcore(_lcore), tsc(_tsc) {} 50 uint16_t lcore; 51 uint64_t tsc; 52 }; 53 54 class compare_entry_key 55 { 56 public: 57 bool operator()(const entry_key &first, const entry_key &second) const 58 { 59 if (first.tsc == second.tsc) { 60 return first.lcore < second.lcore; 61 } else { 62 return first.tsc < second.tsc; 63 } 64 } 65 }; 66 67 typedef std::map<entry_key, spdk_trace_entry *, compare_entry_key> entry_map; 68 69 entry_map g_entry_map; 70 71 struct object_stats { 72 73 std::map<uint64_t, uint64_t> start; 74 std::map<uint64_t, uint64_t> index; 75 std::map<uint64_t, uint64_t> size; 76 std::map<uint64_t, uint64_t> tpoint_id; 77 uint64_t counter; 78 79 object_stats() : start(), index(), size(), tpoint_id(), counter(0) {} 80 }; 81 82 struct object_stats g_stats[SPDK_TRACE_MAX_OBJECT]; 83 84 static char *g_exe_name; 85 static int g_verbose = 1; 86 87 static uint64_t g_tsc_rate; 88 static uint64_t g_first_tsc = 0x0; 89 90 static float 91 get_us_from_tsc(uint64_t tsc, uint64_t tsc_rate) 92 { 93 return ((float)tsc) * 1000 * 1000 / tsc_rate; 94 } 95 96 static const char * 97 format_argname(const char *name) 98 { 99 static char namebuf[16]; 100 101 snprintf(namebuf, sizeof(namebuf), "%s: ", name); 102 return namebuf; 103 } 104 105 static void 106 print_ptr(const char *arg_string, uint64_t arg) 107 { 108 printf("%-7.7s0x%-14jx ", format_argname(arg_string), arg); 109 } 110 111 static void 112 print_uint64(const char *arg_string, uint64_t arg) 113 { 114 /* 115 * Print arg as signed, since -1 is a common value especially 116 * for FLUSH WRITEBUF when writev() returns -1 due to full 117 * socket buffer. 118 */ 119 printf("%-7.7s%-16jd ", format_argname(arg_string), arg); 120 } 121 122 static void 123 print_string(const char *arg_string, uint64_t arg) 124 { 125 char *str = (char *)&arg; 126 printf("%-7.7s%.8s ", format_argname(arg_string), str); 127 } 128 129 static void 130 print_size(uint32_t size) 131 { 132 if (size > 0) { 133 printf("size: %6u ", size); 134 } else { 135 printf("%13s", " "); 136 } 137 } 138 139 static void 140 print_object_id(uint8_t type, uint64_t id) 141 { 142 printf("id: %c%-15jd ", g_histories->flags.object[type].id_prefix, id); 143 } 144 145 static void 146 print_float(const char *arg_string, float arg) 147 { 148 printf("%-7s%-16.3f ", format_argname(arg_string), arg); 149 } 150 151 static void 152 print_arg(uint8_t arg_type, const char *arg_string, uint64_t arg) 153 { 154 if (arg_string[0] == 0) { 155 printf("%24s", ""); 156 return; 157 } 158 159 switch (arg_type) { 160 case SPDK_TRACE_ARG_TYPE_PTR: 161 print_ptr(arg_string, arg); 162 break; 163 case SPDK_TRACE_ARG_TYPE_INT: 164 print_uint64(arg_string, arg); 165 break; 166 case SPDK_TRACE_ARG_TYPE_STR: 167 print_string(arg_string, arg); 168 break; 169 } 170 } 171 172 static void 173 print_event(struct spdk_trace_entry *e, uint64_t tsc_rate, 174 uint64_t tsc_offset, uint16_t lcore) 175 { 176 struct spdk_trace_tpoint *d; 177 struct object_stats *stats; 178 float us; 179 180 d = &g_histories->flags.tpoint[e->tpoint_id]; 181 stats = &g_stats[d->object_type]; 182 183 if (d->new_object) { 184 stats->index[e->object_id] = stats->counter++; 185 stats->tpoint_id[e->object_id] = e->tpoint_id; 186 stats->start[e->object_id] = e->tsc; 187 stats->size[e->object_id] = e->size; 188 } 189 190 us = get_us_from_tsc(e->tsc - tsc_offset, tsc_rate); 191 192 printf("%2d: %10.3f ", lcore, us); 193 if (g_print_tsc) { 194 printf("(%9ju) ", e->tsc - tsc_offset); 195 } 196 if (g_histories->flags.owner[d->owner_type].id_prefix) { 197 printf("%c%02d ", g_histories->flags.owner[d->owner_type].id_prefix, e->poller_id); 198 } else { 199 printf("%4s", " "); 200 } 201 202 printf("%-*s ", (int)sizeof(d->name), d->name); 203 print_size(e->size); 204 205 print_arg(d->arg1_type, d->arg1_name, e->arg1); 206 if (d->new_object) { 207 print_object_id(d->object_type, stats->index[e->object_id]); 208 } else if (d->object_type != OBJECT_NONE) { 209 if (stats->start.find(e->object_id) != stats->start.end()) { 210 us = get_us_from_tsc(e->tsc - stats->start[e->object_id], 211 tsc_rate); 212 print_object_id(d->object_type, stats->index[e->object_id]); 213 print_float("time", us); 214 } else { 215 printf("id: N/A"); 216 } 217 } else if (e->object_id != 0) { 218 print_arg(SPDK_TRACE_ARG_TYPE_PTR, "object", e->object_id); 219 } 220 printf("\n"); 221 } 222 223 static void 224 process_event(struct spdk_trace_entry *e, uint64_t tsc_rate, 225 uint64_t tsc_offset, uint16_t lcore) 226 { 227 if (g_verbose) { 228 print_event(e, tsc_rate, tsc_offset, lcore); 229 } 230 } 231 232 static int 233 populate_events(struct spdk_trace_history *history, int num_entries) 234 { 235 int i, num_entries_filled; 236 struct spdk_trace_entry *e; 237 int first, last, lcore; 238 239 lcore = history->lcore; 240 241 e = history->entries; 242 243 num_entries_filled = num_entries; 244 while (e[num_entries_filled - 1].tsc == 0) { 245 num_entries_filled--; 246 } 247 248 if (num_entries == num_entries_filled) { 249 first = last = 0; 250 for (i = 1; i < num_entries; i++) { 251 if (e[i].tsc < e[first].tsc) { 252 first = i; 253 } 254 if (e[i].tsc > e[last].tsc) { 255 last = i; 256 } 257 } 258 } else { 259 first = 0; 260 last = num_entries_filled - 1; 261 } 262 263 /* 264 * We keep track of the highest first TSC out of all reactors. 265 * We will ignore any events that occured before this TSC on any 266 * other reactors. This will ensure we only print data for the 267 * subset of time where we have data across all reactors. 268 */ 269 if (e[first].tsc > g_first_tsc) { 270 g_first_tsc = e[first].tsc; 271 } 272 273 i = first; 274 while (1) { 275 g_entry_map[entry_key(lcore, e[i].tsc)] = &e[i]; 276 if (i == last) { 277 break; 278 } 279 i++; 280 if (i == num_entries_filled) { 281 i = 0; 282 } 283 } 284 285 return (0); 286 } 287 288 static void usage(void) 289 { 290 fprintf(stderr, "usage:\n"); 291 fprintf(stderr, " %s <option> <lcore#>\n", g_exe_name); 292 fprintf(stderr, " option = '-q' to disable verbose mode\n"); 293 fprintf(stderr, " '-c' to display single lcore history\n"); 294 fprintf(stderr, " '-t' to display TSC offset for each event\n"); 295 fprintf(stderr, " '-s' to specify spdk_trace shm name for a\n"); 296 fprintf(stderr, " currently running process\n"); 297 fprintf(stderr, " '-i' to specify the shared memory ID\n"); 298 fprintf(stderr, " '-p' to specify the trace PID\n"); 299 fprintf(stderr, " (If -s is specified, then one of\n"); 300 fprintf(stderr, " -i or -p must be specified)\n"); 301 fprintf(stderr, " '-f' to specify a tracepoint file name\n"); 302 fprintf(stderr, " (-s and -f are mutually exclusive)\n"); 303 } 304 305 int main(int argc, char **argv) 306 { 307 void *history_ptr; 308 struct spdk_trace_history *history; 309 int fd, i, rc; 310 int lcore = SPDK_TRACE_MAX_LCORE; 311 uint64_t tsc_offset; 312 const char *app_name = NULL; 313 const char *file_name = NULL; 314 int op; 315 char shm_name[64]; 316 int shm_id = -1, shm_pid = -1; 317 uint64_t trace_histories_size; 318 struct stat _stat; 319 320 g_exe_name = argv[0]; 321 while ((op = getopt(argc, argv, "c:f:i:p:qs:t")) != -1) { 322 switch (op) { 323 case 'c': 324 lcore = atoi(optarg); 325 if (lcore > SPDK_TRACE_MAX_LCORE) { 326 fprintf(stderr, "Selected lcore: %d " 327 "exceeds maximum %d\n", lcore, 328 SPDK_TRACE_MAX_LCORE); 329 exit(1); 330 } 331 break; 332 case 'i': 333 shm_id = atoi(optarg); 334 break; 335 case 'p': 336 shm_pid = atoi(optarg); 337 break; 338 case 'q': 339 g_verbose = 0; 340 break; 341 case 's': 342 app_name = optarg; 343 break; 344 case 'f': 345 file_name = optarg; 346 break; 347 case 't': 348 g_print_tsc = true; 349 break; 350 default: 351 usage(); 352 exit(1); 353 } 354 } 355 356 if (file_name != NULL && app_name != NULL) { 357 fprintf(stderr, "-f and -s are mutually exclusive\n"); 358 usage(); 359 exit(1); 360 } 361 362 if (file_name == NULL && app_name == NULL) { 363 fprintf(stderr, "One of -f and -s must be specified\n"); 364 usage(); 365 exit(1); 366 } 367 368 if (file_name) { 369 fd = open(file_name, O_RDONLY); 370 } else { 371 if (shm_id >= 0) { 372 snprintf(shm_name, sizeof(shm_name), "/%s_trace.%d", app_name, shm_id); 373 } else { 374 snprintf(shm_name, sizeof(shm_name), "/%s_trace.pid%d", app_name, shm_pid); 375 } 376 fd = shm_open(shm_name, O_RDONLY, 0600); 377 file_name = shm_name; 378 } 379 if (fd < 0) { 380 fprintf(stderr, "Could not open %s.\n", file_name); 381 usage(); 382 exit(-1); 383 } 384 385 rc = fstat(fd, &_stat); 386 if (rc < 0) { 387 fprintf(stderr, "Could not get size of %s.\n", file_name); 388 usage(); 389 exit(-1); 390 } 391 if ((size_t)_stat.st_size < sizeof(*g_histories)) { 392 fprintf(stderr, "%s is not a valid trace file\n", file_name); 393 usage(); 394 exit(-1); 395 } 396 397 /* Map the header of trace file */ 398 history_ptr = mmap(NULL, sizeof(*g_histories), PROT_READ, MAP_SHARED, fd, 0); 399 if (history_ptr == MAP_FAILED) { 400 fprintf(stderr, "Could not mmap %s.\n", file_name); 401 usage(); 402 exit(-1); 403 } 404 405 g_histories = (struct spdk_trace_histories *)history_ptr; 406 407 g_tsc_rate = g_histories->flags.tsc_rate; 408 if (g_tsc_rate == 0) { 409 fprintf(stderr, "Invalid tsc_rate %ju\n", g_tsc_rate); 410 usage(); 411 exit(-1); 412 } 413 414 if (g_verbose) { 415 printf("TSC Rate: %ju\n", g_tsc_rate); 416 } 417 418 /* Remap the entire trace file */ 419 trace_histories_size = spdk_get_trace_histories_size(g_histories); 420 munmap(history_ptr, sizeof(*g_histories)); 421 if ((size_t)_stat.st_size < trace_histories_size) { 422 fprintf(stderr, "%s is not a valid trace file\n", file_name); 423 usage(); 424 exit(-1); 425 } 426 history_ptr = mmap(NULL, trace_histories_size, PROT_READ, MAP_SHARED, fd, 0); 427 if (history_ptr == MAP_FAILED) { 428 fprintf(stderr, "Could not mmap %s.\n", file_name); 429 usage(); 430 exit(-1); 431 } 432 433 g_histories = (struct spdk_trace_histories *)history_ptr; 434 435 if (lcore == SPDK_TRACE_MAX_LCORE) { 436 for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) { 437 history = spdk_get_per_lcore_history(g_histories, i); 438 if (history->num_entries == 0 || history->entries[0].tsc == 0) { 439 continue; 440 } 441 442 if (g_verbose && history->num_entries) { 443 printf("Trace Size of lcore (%d): %ju\n", i, history->num_entries); 444 } 445 446 populate_events(history, history->num_entries); 447 } 448 } else { 449 history = spdk_get_per_lcore_history(g_histories, lcore); 450 if (history->num_entries > 0 && history->entries[0].tsc != 0) { 451 if (g_verbose && history->num_entries) { 452 printf("Trace Size of lcore (%d): %ju\n", lcore, history->num_entries); 453 } 454 455 populate_events(history, history->num_entries); 456 } 457 } 458 459 tsc_offset = g_first_tsc; 460 for (entry_map::iterator it = g_entry_map.begin(); it != g_entry_map.end(); it++) { 461 if (it->first.tsc < g_first_tsc) { 462 continue; 463 } 464 process_event(it->second, g_tsc_rate, tsc_offset, it->first.lcore); 465 } 466 467 munmap(history_ptr, trace_histories_size); 468 close(fd); 469 470 return (0); 471 } 472