1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (C) 2018 Intel Corporation. 3 * All rights reserved. 4 */ 5 6 #include "spdk/stdinc.h" 7 8 #include "spdk/env.h" 9 #include "spdk/string.h" 10 #include "spdk/trace.h" 11 #include "spdk/util.h" 12 #include "spdk/barrier.h" 13 14 #define TRACE_FILE_COPY_SIZE (32 * 1024) 15 #define TRACE_PATH_MAX 2048 16 17 static char *g_exe_name; 18 static int g_verbose = 1; 19 static uint64_t g_tsc_rate; 20 static uint64_t g_utsc_rate; 21 static bool g_shutdown = false; 22 static uint64_t g_file_size; 23 24 struct lcore_trace_record_ctx { 25 char lcore_file[TRACE_PATH_MAX]; 26 int fd; 27 bool valid; 28 struct spdk_trace_history *in_history; 29 struct spdk_trace_history *out_history; 30 31 /* Recorded next entry index in record */ 32 uint64_t rec_next_entry; 33 34 /* Record tsc for report */ 35 uint64_t first_entry_tsc; 36 uint64_t last_entry_tsc; 37 38 /* Total number of entries in lcore trace file */ 39 uint64_t num_entries; 40 }; 41 42 struct aggr_trace_record_ctx { 43 const char *out_file; 44 int out_fd; 45 int shm_fd; 46 struct lcore_trace_record_ctx lcore_ports[SPDK_TRACE_MAX_LCORE]; 47 struct spdk_trace_file *trace_file; 48 }; 49 50 static int 51 input_trace_file_mmap(struct aggr_trace_record_ctx *ctx, const char *shm_name) 52 { 53 void *history_ptr; 54 int i; 55 56 ctx->shm_fd = shm_open(shm_name, O_RDONLY, 0); 57 if (ctx->shm_fd < 0) { 58 fprintf(stderr, "Could not open %s.\n", shm_name); 59 return -1; 60 } 61 62 /* Map the header of trace file */ 63 history_ptr = mmap(NULL, sizeof(struct spdk_trace_file), PROT_READ, MAP_SHARED, ctx->shm_fd, 64 0); 65 if (history_ptr == MAP_FAILED) { 66 fprintf(stderr, "Could not mmap shm %s.\n", shm_name); 67 close(ctx->shm_fd); 68 return -1; 69 } 70 71 ctx->trace_file = (struct spdk_trace_file *)history_ptr; 72 73 g_tsc_rate = ctx->trace_file->tsc_rate; 74 g_utsc_rate = g_tsc_rate / 1000; 75 if (g_tsc_rate == 0) { 76 fprintf(stderr, "Invalid tsc_rate %ju\n", g_tsc_rate); 77 munmap(history_ptr, sizeof(struct spdk_trace_file)); 78 close(ctx->shm_fd); 79 return -1; 80 } 81 82 if (g_verbose) { 83 printf("TSC Rate: %ju\n", g_tsc_rate); 84 } 85 86 /* Remap the entire trace file */ 87 g_file_size = spdk_get_trace_file_size(ctx->trace_file); 88 munmap(history_ptr, sizeof(struct spdk_trace_file)); 89 history_ptr = mmap(NULL, g_file_size, PROT_READ, MAP_SHARED, ctx->shm_fd, 0); 90 if (history_ptr == MAP_FAILED) { 91 fprintf(stderr, "Could not remmap shm %s.\n", shm_name); 92 close(ctx->shm_fd); 93 return -1; 94 } 95 96 ctx->trace_file = (struct spdk_trace_file *)history_ptr; 97 for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) { 98 struct spdk_trace_history *history; 99 100 history = spdk_get_per_lcore_history(ctx->trace_file, i); 101 ctx->lcore_ports[i].in_history = history; 102 ctx->lcore_ports[i].valid = (history != NULL); 103 104 if (g_verbose && history) { 105 printf("Number of trace entries for lcore (%d): %ju\n", i, 106 history->num_entries); 107 } 108 } 109 110 return 0; 111 } 112 113 static int 114 output_trace_files_prepare(struct aggr_trace_record_ctx *ctx, const char *aggr_path) 115 { 116 int flags = O_CREAT | O_EXCL | O_RDWR; 117 struct lcore_trace_record_ctx *port_ctx; 118 int name_len; 119 int i, rc; 120 121 /* Assign file names for related trace files */ 122 ctx->out_file = aggr_path; 123 for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) { 124 port_ctx = &ctx->lcore_ports[i]; 125 126 /* Get the length of trace file name for each lcore with format "%s-%d" */ 127 name_len = snprintf(port_ctx->lcore_file, TRACE_PATH_MAX, "%s-%d", ctx->out_file, i); 128 if (name_len >= TRACE_PATH_MAX) { 129 fprintf(stderr, "Length of file path (%s) exceeds limitation for lcore file.\n", 130 aggr_path); 131 goto err; 132 } 133 } 134 135 /* If output trace file already exists, try to unlink it together with its temporary files */ 136 if (access(ctx->out_file, F_OK) == 0) { 137 rc = unlink(ctx->out_file); 138 if (rc) { 139 goto err; 140 } 141 142 for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) { 143 port_ctx = &ctx->lcore_ports[i]; 144 if (access(port_ctx->lcore_file, F_OK) == 0) { 145 rc = unlink(port_ctx->lcore_file); 146 if (rc) { 147 goto err; 148 } 149 } 150 } 151 152 } 153 154 for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) { 155 port_ctx = &ctx->lcore_ports[i]; 156 157 if (!port_ctx->valid) { 158 continue; 159 } 160 161 port_ctx->fd = open(port_ctx->lcore_file, flags, 0600); 162 if (port_ctx->fd < 0) { 163 fprintf(stderr, "Could not open lcore file %s.\n", port_ctx->lcore_file); 164 goto err; 165 } 166 167 if (g_verbose) { 168 printf("Create tmp lcore trace file %s for lcore %d\n", port_ctx->lcore_file, i); 169 } 170 171 port_ctx->out_history = calloc(1, sizeof(struct spdk_trace_history)); 172 if (port_ctx->out_history == NULL) { 173 fprintf(stderr, "Failed to allocate memory for out_history.\n"); 174 goto err; 175 } 176 } 177 178 return 0; 179 180 err: 181 for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) { 182 port_ctx = &ctx->lcore_ports[i]; 183 free(port_ctx->out_history); 184 185 if (port_ctx->fd > 0) { 186 close(port_ctx->fd); 187 } 188 } 189 190 return -1; 191 } 192 193 static void 194 output_trace_files_finish(struct aggr_trace_record_ctx *ctx) 195 { 196 struct lcore_trace_record_ctx *port_ctx; 197 int i; 198 199 for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) { 200 port_ctx = &ctx->lcore_ports[i]; 201 202 free(port_ctx->out_history); 203 close(port_ctx->fd); 204 unlink(port_ctx->lcore_file); 205 206 if (g_verbose) { 207 printf("Remove tmp lcore trace file %s for lcore %d\n", port_ctx->lcore_file, i); 208 } 209 } 210 } 211 212 static int 213 cont_write(int fildes, const void *buf, size_t nbyte) 214 { 215 int rc; 216 int _nbyte = nbyte; 217 218 while (_nbyte) { 219 rc = write(fildes, buf, _nbyte); 220 if (rc < 0) { 221 if (errno != EINTR) { 222 return -1; 223 } 224 225 continue; 226 } 227 228 _nbyte -= rc; 229 } 230 231 return nbyte; 232 } 233 234 static int 235 cont_read(int fildes, void *buf, size_t nbyte) 236 { 237 int rc; 238 int _nbyte = nbyte; 239 240 while (_nbyte) { 241 rc = read(fildes, buf, _nbyte); 242 if (rc == 0) { 243 return nbyte - _nbyte; 244 } else if (rc < 0) { 245 if (errno != EINTR) { 246 return -1; 247 } 248 249 continue; 250 } 251 252 _nbyte -= rc; 253 } 254 255 return nbyte; 256 } 257 258 static int 259 lcore_trace_last_entry_idx(struct spdk_trace_history *in_history, int cir_next_idx) 260 { 261 int last_idx; 262 263 if (cir_next_idx == 0) { 264 last_idx = in_history->num_entries - 1; 265 } else { 266 last_idx = cir_next_idx - 1; 267 } 268 269 return last_idx; 270 } 271 272 static int 273 circular_buffer_padding_backward(int fd, struct spdk_trace_history *in_history, 274 int cir_start, int cir_end) 275 { 276 int rc; 277 278 if (cir_end <= cir_start) { 279 fprintf(stderr, "Wrong using of circular_buffer_padding_back\n"); 280 return -1; 281 } 282 283 rc = cont_write(fd, &in_history->entries[cir_start], 284 sizeof(struct spdk_trace_entry) * (cir_end - cir_start)); 285 if (rc < 0) { 286 fprintf(stderr, "Failed to append entries into lcore file\n"); 287 return rc; 288 } 289 290 return 0; 291 } 292 293 static int 294 circular_buffer_padding_across(int fd, struct spdk_trace_history *in_history, 295 int cir_start, int cir_end) 296 { 297 int rc; 298 int num_entries = in_history->num_entries; 299 300 if (cir_end > cir_start) { 301 fprintf(stderr, "Wrong using of circular_buffer_padding_across\n"); 302 return -1; 303 } 304 305 rc = cont_write(fd, &in_history->entries[cir_start], 306 sizeof(struct spdk_trace_entry) * (num_entries - cir_start)); 307 if (rc < 0) { 308 fprintf(stderr, "Failed to append entries into lcore file backward\n"); 309 return rc; 310 } 311 312 if (cir_end == 0) { 313 return 0; 314 } 315 316 rc = cont_write(fd, &in_history->entries[0], sizeof(struct spdk_trace_entry) * cir_end); 317 if (rc < 0) { 318 fprintf(stderr, "Failed to append entries into lcore file forward\n"); 319 return rc; 320 } 321 322 return 0; 323 } 324 325 static int 326 circular_buffer_padding_all(int fd, struct spdk_trace_history *in_history, 327 int cir_end) 328 { 329 return circular_buffer_padding_across(fd, in_history, cir_end, cir_end); 330 } 331 332 static int 333 lcore_trace_record(struct lcore_trace_record_ctx *lcore_port) 334 { 335 struct spdk_trace_history *in_history = lcore_port->in_history; 336 uint64_t rec_next_entry = lcore_port->rec_next_entry; 337 uint64_t rec_num_entries = lcore_port->num_entries; 338 int fd = lcore_port->fd; 339 uint64_t shm_next_entry; 340 uint64_t num_cir_entries; 341 uint64_t shm_cir_next; 342 uint64_t rec_cir_next; 343 int rc; 344 int last_idx; 345 346 shm_next_entry = in_history->next_entry; 347 348 /* Ensure all entries of spdk_trace_history are latest to next_entry */ 349 spdk_smp_rmb(); 350 351 if (shm_next_entry == rec_next_entry) { 352 /* There is no update */ 353 return 0; 354 } else if (shm_next_entry < rec_next_entry) { 355 /* Error branch */ 356 fprintf(stderr, "Trace porting error in lcore %d, trace rollback occurs.\n", in_history->lcore); 357 fprintf(stderr, "shm_next_entry is %ju, record_next_entry is %ju.\n", shm_next_entry, 358 rec_next_entry); 359 return -1; 360 } 361 362 num_cir_entries = in_history->num_entries; 363 shm_cir_next = shm_next_entry & (num_cir_entries - 1); 364 365 /* Record first entry's tsc and corresponding entries when recording first time. */ 366 if (lcore_port->first_entry_tsc == 0) { 367 if (shm_next_entry < num_cir_entries) { 368 /* Updates haven't been across circular buffer yet. 369 * The first entry in shared memory is the eldest one. 370 */ 371 lcore_port->first_entry_tsc = in_history->entries[0].tsc; 372 373 lcore_port->num_entries += shm_cir_next; 374 rc = circular_buffer_padding_backward(fd, in_history, 0, shm_cir_next); 375 } else { 376 /* Updates have already been across circular buffer. 377 * The eldest entry in shared memory is pointed by shm_cir_next. 378 */ 379 lcore_port->first_entry_tsc = in_history->entries[shm_cir_next].tsc; 380 381 lcore_port->num_entries += num_cir_entries; 382 rc = circular_buffer_padding_all(fd, in_history, shm_cir_next); 383 } 384 385 goto out; 386 } 387 388 if (shm_next_entry - rec_next_entry > num_cir_entries) { 389 /* There must be missed updates */ 390 fprintf(stderr, "Trace-record missed %ju trace entries\n", 391 shm_next_entry - rec_next_entry - num_cir_entries); 392 393 lcore_port->num_entries += num_cir_entries; 394 rc = circular_buffer_padding_all(fd, in_history, shm_cir_next); 395 } else if (shm_next_entry - rec_next_entry == num_cir_entries) { 396 /* All circular buffer is updated */ 397 lcore_port->num_entries += num_cir_entries; 398 rc = circular_buffer_padding_all(fd, in_history, shm_cir_next); 399 } else { 400 /* Part of circular buffer is updated */ 401 rec_cir_next = rec_next_entry & (num_cir_entries - 1); 402 403 if (shm_cir_next > rec_cir_next) { 404 /* Updates are not across circular buffer */ 405 lcore_port->num_entries += shm_cir_next - rec_cir_next; 406 rc = circular_buffer_padding_backward(fd, in_history, rec_cir_next, shm_cir_next); 407 } else { 408 /* Updates are across circular buffer */ 409 lcore_port->num_entries += num_cir_entries - rec_cir_next + shm_cir_next; 410 rc = circular_buffer_padding_across(fd, in_history, rec_cir_next, shm_cir_next); 411 } 412 } 413 414 out: 415 if (rc) { 416 return rc; 417 } 418 419 if (g_verbose) { 420 printf("Append %ju trace_entry for lcore %d\n", lcore_port->num_entries - rec_num_entries, 421 in_history->lcore); 422 } 423 424 /* Update tpoint_count info */ 425 memcpy(lcore_port->out_history, lcore_port->in_history, sizeof(struct spdk_trace_history)); 426 427 /* Update last_entry_tsc to align with appended entries */ 428 last_idx = lcore_trace_last_entry_idx(in_history, shm_cir_next); 429 lcore_port->last_entry_tsc = in_history->entries[last_idx].tsc; 430 lcore_port->rec_next_entry = shm_next_entry; 431 432 return rc; 433 } 434 435 static int 436 trace_files_aggregate(struct aggr_trace_record_ctx *ctx) 437 { 438 int flags = O_CREAT | O_EXCL | O_RDWR; 439 struct lcore_trace_record_ctx *lcore_port; 440 char copy_buff[TRACE_FILE_COPY_SIZE]; 441 uint64_t lcore_offsets[SPDK_TRACE_MAX_LCORE]; 442 int rc, i; 443 ssize_t len = 0; 444 uint64_t current_offset; 445 uint64_t len_sum; 446 447 ctx->out_fd = open(ctx->out_file, flags, 0600); 448 if (ctx->out_fd < 0) { 449 fprintf(stderr, "Could not open aggregation file %s.\n", ctx->out_file); 450 return -1; 451 } 452 453 if (g_verbose) { 454 printf("Create trace file %s for output\n", ctx->out_file); 455 } 456 457 /* Calculate lcore offsets for converged trace file */ 458 current_offset = sizeof(struct spdk_trace_file); 459 for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) { 460 lcore_port = &ctx->lcore_ports[i]; 461 if (lcore_port->valid) { 462 lcore_offsets[i] = current_offset; 463 current_offset += spdk_get_trace_history_size(lcore_port->num_entries); 464 } else { 465 lcore_offsets[i] = 0; 466 } 467 } 468 469 /* Write size of converged trace file */ 470 rc = cont_write(ctx->out_fd, ¤t_offset, sizeof(ctx->trace_file->file_size)); 471 if (rc < 0) { 472 fprintf(stderr, "Failed to write file size into trace file\n"); 473 goto out; 474 } 475 476 /* Write rest of metadata (spdk_trace_file) of converged trace file */ 477 rc = cont_write(ctx->out_fd, &ctx->trace_file->tsc_rate, 478 sizeof(struct spdk_trace_file) - sizeof(lcore_offsets) - 479 sizeof(ctx->trace_file->file_size)); 480 if (rc < 0) { 481 fprintf(stderr, "Failed to write metadata into trace file\n"); 482 goto out; 483 } 484 485 /* Write lcore offsets of converged trace file */ 486 rc = cont_write(ctx->out_fd, lcore_offsets, sizeof(lcore_offsets)); 487 if (rc < 0) { 488 fprintf(stderr, "Failed to write lcore offsets into trace file\n"); 489 goto out; 490 } 491 492 /* Append each lcore trace file into converged trace file */ 493 for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) { 494 lcore_port = &ctx->lcore_ports[i]; 495 496 if (!lcore_port->valid) { 497 continue; 498 } 499 500 lcore_port->out_history->num_entries = lcore_port->num_entries; 501 rc = cont_write(ctx->out_fd, lcore_port->out_history, sizeof(struct spdk_trace_history)); 502 if (rc < 0) { 503 fprintf(stderr, "Failed to write lcore trace header into trace file\n"); 504 goto out; 505 } 506 507 /* Move file offset to the start of trace_entries */ 508 rc = lseek(lcore_port->fd, 0, SEEK_SET); 509 if (rc != 0) { 510 fprintf(stderr, "Failed to lseek lcore trace file\n"); 511 goto out; 512 } 513 514 len_sum = 0; 515 while ((len = cont_read(lcore_port->fd, copy_buff, TRACE_FILE_COPY_SIZE)) > 0) { 516 len_sum += len; 517 rc = cont_write(ctx->out_fd, copy_buff, len); 518 if (rc != len) { 519 fprintf(stderr, "Failed to write lcore trace entries into trace file\n"); 520 goto out; 521 } 522 } 523 524 /* Clear rc so that the last cont_write() doesn't get interpreted as a failure. */ 525 rc = 0; 526 527 if (len_sum != lcore_port->num_entries * sizeof(struct spdk_trace_entry)) { 528 fprintf(stderr, "Len of lcore trace file doesn't match number of entries for lcore\n"); 529 } 530 } 531 532 printf("All lcores trace entries are aggregated into trace file %s\n", ctx->out_file); 533 534 out: 535 close(ctx->out_fd); 536 537 return rc; 538 } 539 540 static void 541 __shutdown_signal(int signo) 542 { 543 g_shutdown = true; 544 } 545 546 static int 547 setup_exit_signal_handler(void) 548 { 549 struct sigaction sigact; 550 int rc; 551 552 memset(&sigact, 0, sizeof(sigact)); 553 sigemptyset(&sigact.sa_mask); 554 /* Install the same handler for SIGINT and SIGTERM */ 555 sigact.sa_handler = __shutdown_signal; 556 557 rc = sigaction(SIGINT, &sigact, NULL); 558 if (rc < 0) { 559 fprintf(stderr, "sigaction(SIGINT) failed\n"); 560 561 return rc; 562 } 563 564 rc = sigaction(SIGTERM, &sigact, NULL); 565 if (rc < 0) { 566 fprintf(stderr, "sigaction(SIGTERM) failed\n"); 567 } 568 569 return rc; 570 } 571 572 static void 573 usage(void) 574 { 575 printf("\n%s is used to record all SPDK generated trace entries\n", g_exe_name); 576 printf("from SPDK trace shared-memory to specified file.\n\n"); 577 printf("usage:\n"); 578 printf(" %s <option>\n", g_exe_name); 579 printf(" option = '-q' to disable verbose mode\n"); 580 printf(" '-s' to specify spdk_trace shm name for a\n"); 581 printf(" currently running process\n"); 582 printf(" '-i' to specify the shared memory ID\n"); 583 printf(" '-p' to specify the trace PID\n"); 584 printf(" (one of -i or -p must be specified)\n"); 585 printf(" '-f' to specify output trace file name\n"); 586 printf(" '-h' to print usage information\n"); 587 } 588 589 int 590 main(int argc, char **argv) 591 { 592 const char *app_name = NULL; 593 const char *file_name = NULL; 594 int op; 595 char shm_name[64]; 596 int shm_id = -1, shm_pid = -1; 597 int rc = 0; 598 int i; 599 struct aggr_trace_record_ctx ctx = {}; 600 struct lcore_trace_record_ctx *lcore_port; 601 602 g_exe_name = argv[0]; 603 while ((op = getopt(argc, argv, "f:i:p:qs:h")) != -1) { 604 switch (op) { 605 case 'i': 606 shm_id = spdk_strtol(optarg, 10); 607 break; 608 case 'p': 609 shm_pid = spdk_strtol(optarg, 10); 610 break; 611 case 'q': 612 g_verbose = 0; 613 break; 614 case 's': 615 app_name = optarg; 616 break; 617 case 'f': 618 file_name = optarg; 619 break; 620 case 'h': 621 usage(); 622 exit(EXIT_SUCCESS); 623 default: 624 usage(); 625 exit(1); 626 } 627 } 628 629 if (file_name == NULL) { 630 fprintf(stderr, "-f must be specified\n"); 631 usage(); 632 exit(1); 633 } 634 635 if (app_name == NULL) { 636 fprintf(stderr, "-s must be specified\n"); 637 usage(); 638 exit(1); 639 } 640 641 if (shm_id == -1 && shm_pid == -1) { 642 fprintf(stderr, "-i or -p must be specified\n"); 643 usage(); 644 exit(1); 645 } 646 647 if (shm_id >= 0) { 648 snprintf(shm_name, sizeof(shm_name), "/%s_trace.%d", app_name, shm_id); 649 } else { 650 snprintf(shm_name, sizeof(shm_name), "/%s_trace.pid%d", app_name, shm_pid); 651 } 652 653 rc = setup_exit_signal_handler(); 654 if (rc) { 655 exit(1); 656 } 657 658 rc = input_trace_file_mmap(&ctx, shm_name); 659 if (rc) { 660 exit(1); 661 } 662 663 rc = output_trace_files_prepare(&ctx, file_name); 664 if (rc) { 665 exit(1); 666 } 667 668 printf("Start to poll trace shm file %s\n", shm_name); 669 while (!g_shutdown && rc == 0) { 670 for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) { 671 lcore_port = &ctx.lcore_ports[i]; 672 673 if (!lcore_port->valid) { 674 continue; 675 } 676 rc = lcore_trace_record(lcore_port); 677 if (rc) { 678 break; 679 } 680 } 681 } 682 683 if (rc) { 684 exit(1); 685 } 686 687 printf("Start to aggregate lcore trace files\n"); 688 rc = trace_files_aggregate(&ctx); 689 if (rc) { 690 exit(1); 691 } 692 693 /* Summary report */ 694 printf("TSC Rate: %ju\n", g_tsc_rate); 695 for (i = 0; i < SPDK_TRACE_MAX_LCORE; i++) { 696 lcore_port = &ctx.lcore_ports[i]; 697 698 if (lcore_port->num_entries == 0) { 699 continue; 700 } 701 702 printf("Port %ju trace entries for lcore (%d) in %ju usec\n", 703 lcore_port->num_entries, i, 704 (lcore_port->last_entry_tsc - lcore_port->first_entry_tsc) / g_utsc_rate); 705 706 } 707 708 munmap(ctx.trace_file, g_file_size); 709 close(ctx.shm_fd); 710 711 output_trace_files_finish(&ctx); 712 713 return 0; 714 } 715