1 #include <assert.h> 2 #include <ctype.h> 3 #include <errno.h> 4 #include <limits.h> 5 #include <minix/profile.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <unistd.h> 10 11 /* user-configurable settings */ 12 #define BINARY_HASHTAB_SIZE 1024 13 14 #define ENDPOINT_HASHTAB_SIZE 1024 15 16 #define DEBUG 0 17 18 #define NM "/usr/pkg/bin/nm" 19 20 static const char *default_binaries[] = { 21 "kernel/kernel", 22 "servers/", 23 "drivers/", 24 }; 25 26 static const char *src_path = "/usr/src"; 27 28 /* types */ 29 30 #define LINE_WIDTH 80 31 32 #define SYMBOL_NAME_SIZE 52 33 34 #define SYMBOL_NAME_WIDTH 22 35 36 #define SYMBOL_SIZE_MAX 0x100000 37 38 #define PC_MAP_L1_SIZE 0x10000 39 #define PC_MAP_L2_SIZE 0x10000 40 41 struct binary_info; 42 43 struct symbol_count { 44 struct symbol_count *next; 45 struct binary_info *binary; 46 uint32_t addr; 47 int samples; 48 char name[SYMBOL_NAME_SIZE]; 49 }; 50 51 struct pc_map_l2 { 52 struct symbol_count *l2[PC_MAP_L2_SIZE]; 53 }; 54 55 struct pc_map_l1 { 56 struct pc_map_l2 *l1[PC_MAP_L1_SIZE]; 57 }; 58 59 struct binary_info { 60 char name[PROC_NAME_LEN]; 61 const char *path; 62 int samples; 63 struct symbol_count *symbols; 64 struct pc_map_l1 *pc_map; 65 struct binary_info *next; 66 struct binary_info *hashtab_next; 67 char no_more_warnings; 68 }; 69 70 struct endpoint_info { 71 endpoint_t endpoint; 72 struct binary_info *binary; 73 struct endpoint_info *hashtab_next; 74 struct endpoint_info *next; 75 char seen; 76 }; 77 78 union sprof_record { 79 struct sprof_sample sample; 80 struct sprof_proc proc; 81 }; 82 83 /* global variables */ 84 static struct binary_info *binaries; 85 static struct binary_info *binary_hashtab[BINARY_HASHTAB_SIZE]; 86 static struct endpoint_info *endpoint_hashtab[ENDPOINT_HASHTAB_SIZE]; 87 static struct endpoint_info *endpoints; 88 static double minimum_perc = 1.0; 89 static struct sprof_info_s sprof_info; 90 91 /* prototypes */ 92 static struct binary_info *binary_add(const char *path); 93 static struct binary_info *binary_find(const char *name); 94 static struct binary_info *binary_hashtab_get(const char *name); 95 static struct binary_info **binary_hashtab_get_ptr(const char *name); 96 static void binary_load_pc_map(struct binary_info *binary_info); 97 static const char *binary_name(const char *path); 98 static int compare_binaries(const void *p1, const void *p2); 99 static int compare_symbols(const void *p1, const void *p2); 100 static int count_symbols(const struct binary_info *binary, int threshold); 101 static void dprint_symbols(const struct binary_info *binary); 102 static struct endpoint_info **endpoint_hashtab_get_ptr(endpoint_t endpoint); 103 static void load_trace(const char *path); 104 static void *malloc_checked(size_t size); 105 static unsigned name_hash(const char *name); 106 static float percent(int value, int percent_of); 107 static void print_diff(void); 108 static void print_report(void); 109 static void print_report_overall(void); 110 static void print_report_per_binary(const struct binary_info *binary); 111 static void print_reports_per_binary(void); 112 static void print_report_symbols(struct symbol_count **symbols, 113 unsigned symbol_count, int total, int show_binary); 114 static void print_separator(void); 115 static int read_hex(FILE *file, unsigned long *value); 116 static int read_newline(FILE *file); 117 static void read_nm_line(FILE *file, int line, char *name, char *type, 118 unsigned long *addr, unsigned long *size); 119 static void read_to_whitespace(FILE *file, char *buffer, size_t size); 120 static size_t sample_process(const union sprof_record *data, size_t size, 121 int *samples_read); 122 static struct binary_info *sample_load_binary(const struct sprof_proc *sample); 123 static void sample_store(struct binary_info *binary, 124 const struct sprof_sample *sample); 125 static char *strdup_checked(const char *s); 126 static void usage(const char *argv0); 127 128 #define MALLOC_CHECKED(type, count) \ 129 ((type *) malloc_checked(sizeof(type) * (count))) 130 131 #define LENGTHOF(array) (sizeof((array)) / sizeof((array)[0])) 132 133 #if DEBUG 134 #define dprintf(...) do { \ 135 fprintf(stderr, "debug(%s:%d): ", __FUNCTION__, __LINE__); \ 136 fprintf(stderr, __VA_ARGS__); \ 137 } while(0) 138 #else 139 #define dprintf(...) 140 #endif 141 142 int main(int argc, char **argv) { 143 int opt, sprofdiff = 0; 144 145 #ifdef DEBUG 146 /* disable buffering so the output mixes correctly */ 147 setvbuf(stdout, NULL, _IONBF, 0); 148 setvbuf(stderr, NULL, _IONBF, 0); 149 #endif 150 151 /* parse arguments */ 152 while ((opt = getopt(argc, argv, "b:dp:s:")) != -1) { 153 switch (opt) { 154 case 'b': 155 /* additional binary specified */ 156 binary_add(optarg); 157 break; 158 case 'd': 159 /* generate output for sprofdiff */ 160 sprofdiff = 1; 161 break; 162 case 'p': 163 /* minimum percentage specified */ 164 minimum_perc = atof(optarg); 165 if (minimum_perc < 0 || minimum_perc > 100) { 166 fprintf(stderr, "error: cut-off percentage " 167 "makes no sense: %g\n", minimum_perc); 168 exit(1); 169 } 170 break; 171 case 's': 172 /* source tree directory specified */ 173 src_path = optarg; 174 break; 175 default: usage(argv[0]); 176 } 177 } 178 179 /* load samples */ 180 if (optind >= argc) usage(argv[0]); 181 for (; optind < argc; optind++) { 182 struct endpoint_info *e; 183 load_trace(argv[optind]); 184 for(e = endpoints; e; e = e->next) 185 e->seen = 0; 186 } 187 188 /* print report */ 189 if (sprofdiff) { 190 print_diff(); 191 } else { 192 print_report(); 193 } 194 return 0; 195 } 196 197 static struct binary_info *binary_add(const char *path) { 198 struct binary_info *binary, **ptr; 199 const char *name; 200 201 /* assumption: path won't be overwritten or deallocated in the future */ 202 203 /* not too much effort escaping for popen, prevent problems here */ 204 assert(path); 205 if (strchr(path, '"')) { 206 fprintf(stderr, "error: path \"%s\" contains a quote\n", path); 207 exit(1); 208 } 209 210 /* get filename */ 211 name = binary_name(path); 212 dprintf("adding binary \"%s\" with name \"%.*s\"\n", 213 path, PROC_NAME_LEN, name); 214 if (strlen(name) == 0) { 215 fprintf(stderr, "error: path \"%s\" does not " 216 "contain a filename\n", path); 217 exit(1); 218 } 219 220 /* check in hashtable whether this entry is indeed new */ 221 ptr = binary_hashtab_get_ptr(name); 222 if (*ptr) { 223 fprintf(stderr, "warning: ignoring \"%s\" because \"%s\" was " 224 "previously specified\n", path, (*ptr)->path); 225 return *ptr; 226 } 227 dprintf("using %.*s from \"%s\"\n", PROC_NAME_LEN, name, path); 228 229 /* allocate new binary_info */ 230 binary = MALLOC_CHECKED(struct binary_info, 1); 231 memset(binary, 0, sizeof(struct binary_info)); 232 binary->path = path; 233 strncpy(binary->name, name, sizeof(binary->name)); 234 235 /* insert into linked list */ 236 binary->next = binaries; 237 binaries = binary; 238 239 /* insert into hashtable */ 240 *ptr = binary; 241 return binary; 242 } 243 244 static struct binary_info *binary_find(const char *name) { 245 struct binary_info *binary; 246 const char *current_name; 247 unsigned i; 248 char path[PATH_MAX + 1], *path_end; 249 250 assert(name); 251 252 /* name is required */ 253 if (!*name) { 254 fprintf(stderr, "warning: binary unspecified in sample\n"); 255 return NULL; 256 } 257 258 /* do we already know this binary? */ 259 binary = binary_hashtab_get(name); 260 if (binary) return binary; 261 262 /* search for it */ 263 dprintf("searching for binary \"%.*s\" in \"%s\"\n", 264 PROC_NAME_LEN, name, src_path); 265 for (i = 0; i < LENGTHOF(default_binaries); i++) { 266 snprintf(path, sizeof(path), "%s/%s", src_path, 267 default_binaries[i]); 268 current_name = binary_name(path); 269 assert(current_name); 270 if (*current_name) { 271 /* paths not ending in slash: use if name matches */ 272 if (strncmp(name, current_name, 273 PROC_NAME_LEN) != 0) { 274 continue; 275 } 276 } else { 277 /* paths ending in slash: look in subdir named after 278 * binary 279 */ 280 path_end = path + strlen(path); 281 snprintf(path_end, sizeof(path) - (path_end - path), 282 "%.*s/%.*s", PROC_NAME_LEN, name, 283 PROC_NAME_LEN, name); 284 } 285 286 /* use access to find out whether the binary exists and is 287 * readable 288 */ 289 dprintf("checking whether \"%s\" exists\n", path); 290 if (access(path, R_OK) < 0) continue; 291 292 /* ok, this seems to be the one */ 293 return binary_add(strdup_checked(path)); 294 } 295 296 /* not found */ 297 return NULL; 298 } 299 300 static struct binary_info *binary_hashtab_get(const char *name) { 301 return *binary_hashtab_get_ptr(name); 302 } 303 304 static struct binary_info **binary_hashtab_get_ptr(const char *name) { 305 struct binary_info *binary, **ptr; 306 307 /* get pointer to location of the binary in hash table */ 308 ptr = &binary_hashtab[name_hash(name) % BINARY_HASHTAB_SIZE]; 309 while ((binary = *ptr) && strncmp(binary->name, name, 310 PROC_NAME_LEN) != 0) { 311 ptr = &binary->hashtab_next; 312 } 313 dprintf("looked up binary \"%.*s\" in hash table, %sfound\n", 314 PROC_NAME_LEN, name, *ptr ? "" : "not "); 315 return ptr; 316 } 317 318 static void binary_load_pc_map(struct binary_info *binary_info) { 319 unsigned long addr, size; 320 char *command; 321 size_t command_len; 322 #if DEBUG 323 unsigned count = 0; 324 #endif 325 FILE *file; 326 int index_l1, index_l2, line; 327 char name[SYMBOL_NAME_SIZE]; 328 struct pc_map_l2 *pc_map_l2, **pc_map_l2_ptr; 329 struct symbol_count *symbol, **symbol_ptr; 330 char type; 331 332 assert(binary_info); 333 assert(!strchr(NM, '"')); 334 assert(!strchr(binary_info->path, '"')); 335 336 /* does the file exist? */ 337 if (access(binary_info->path, R_OK) < 0) { 338 fprintf(stderr, "warning: \"%s\" does not exist or " 339 "not readable.\n", binary_info->path); 340 fprintf(stderr, " Did you do a make?\n"); 341 return; 342 } 343 344 /* execute nm to get symbols */ 345 command_len = strlen(NM) + strlen(binary_info->path) + 32; 346 command = MALLOC_CHECKED(char, command_len); 347 snprintf(command, command_len, "\"%s\" -nP \"%s\"", 348 NM, binary_info->path); 349 dprintf("running command for extracting addresses: %s\n", command); 350 file = popen(command, "r"); 351 if (!file) { 352 perror("failed to start " NM); 353 exit(-1); 354 } 355 free(command); 356 357 /* read symbols from nm output */ 358 assert(!binary_info->symbols); 359 symbol_ptr = &binary_info->symbols; 360 line = 1; 361 while (!feof(file)) { 362 /* read nm output line; can't use fscanf as it doesn't know 363 * where to stop 364 */ 365 read_nm_line(file, line++, name, &type, &addr, &size); 366 367 /* store only text symbols */ 368 if (type != 't' && type != 'T') continue; 369 370 *symbol_ptr = symbol = MALLOC_CHECKED(struct symbol_count, 1); 371 memset(symbol, 0, sizeof(*symbol)); 372 symbol->binary = binary_info; 373 symbol->addr = addr; 374 strncpy(symbol->name, name, SYMBOL_NAME_SIZE); 375 symbol_ptr = &symbol->next; 376 #if DEBUG 377 count++; 378 #endif 379 } 380 fclose(file); 381 dprintf("extracted %u symbols\n", count); 382 383 /* create program counter map from symbols */ 384 assert(!binary_info->pc_map); 385 binary_info->pc_map = MALLOC_CHECKED(struct pc_map_l1, 1); 386 memset(binary_info->pc_map, 0, sizeof(struct pc_map_l1)); 387 for (symbol = binary_info->symbols; symbol; symbol = symbol->next) { 388 /* compute size if not specified */ 389 size = symbol->next ? (symbol->next->addr - symbol->addr) : 1; 390 if (size > SYMBOL_SIZE_MAX) size = SYMBOL_SIZE_MAX; 391 392 /* mark each address */ 393 for (addr = symbol->addr; addr - symbol->addr < size; addr++) { 394 index_l1 = addr / PC_MAP_L2_SIZE; 395 assert(index_l1 < PC_MAP_L1_SIZE); 396 pc_map_l2_ptr = &binary_info->pc_map->l1[index_l1]; 397 if (!(pc_map_l2 = *pc_map_l2_ptr)) { 398 *pc_map_l2_ptr = pc_map_l2 = 399 MALLOC_CHECKED(struct pc_map_l2, 1); 400 memset(pc_map_l2, 0, sizeof(struct pc_map_l2)); 401 } 402 index_l2 = addr % PC_MAP_L2_SIZE; 403 pc_map_l2->l2[index_l2] = symbol; 404 } 405 } 406 } 407 408 static const char *binary_name(const char *path) { 409 const char *name, *p; 410 411 /* much like basename, but guarantees to not modify the path */ 412 name = path; 413 for (p = path; *p; p++) { 414 if (*p == '/') name = p + 1; 415 } 416 return name; 417 } 418 419 static int compare_binaries(const void *p1, const void *p2) { 420 const struct binary_info *const *b1 = p1, *const *b2 = p2; 421 422 /* binaries with more samples come first */ 423 assert(b1); 424 assert(b2); 425 assert(*b1); 426 assert(*b2); 427 if ((*b1)->samples > (*b2)->samples) return -1; 428 if ((*b1)->samples < (*b2)->samples) return 1; 429 return 0; 430 } 431 432 static int compare_symbols(const void *p1, const void *p2) { 433 const struct symbol_count *const *s1 = p1, *const *s2 = p2; 434 435 /* symbols with more samples come first */ 436 assert(s1); 437 assert(s2); 438 assert(*s1); 439 assert(*s2); 440 if ((*s1)->samples > (*s2)->samples) return -1; 441 if ((*s1)->samples < (*s2)->samples) return 1; 442 return 0; 443 } 444 445 static int count_symbols(const struct binary_info *binary, int threshold) { 446 struct symbol_count *symbol; 447 int result = 0; 448 449 for (symbol = binary->symbols; symbol; symbol = symbol->next) { 450 if (symbol->samples >= threshold) result++; 451 } 452 return result; 453 } 454 455 static void dprint_symbols(const struct binary_info *binary) { 456 #if DEBUG 457 const struct symbol_count *symbol; 458 459 for (symbol = binary->symbols; symbol; symbol = symbol->next) { 460 dprintf("addr=0x%.8lx samples=%8d name=\"%.*s\"\n", 461 (unsigned long) symbol->addr, symbol->samples, 462 SYMBOL_NAME_SIZE, symbol->name); 463 } 464 #endif 465 } 466 467 static struct endpoint_info **endpoint_hashtab_get_ptr(endpoint_t endpoint) { 468 struct endpoint_info *epinfo, **ptr; 469 470 /* get pointer to location of the binary in hash table */ 471 ptr = &endpoint_hashtab[(unsigned) endpoint % ENDPOINT_HASHTAB_SIZE]; 472 while ((epinfo = *ptr) && epinfo->endpoint != endpoint) { 473 ptr = &epinfo->hashtab_next; 474 } 475 dprintf("looked up endpoint %ld in hash table, %sfound\n", 476 (long) endpoint, *ptr ? "" : "not "); 477 return ptr; 478 } 479 480 static void load_trace(const char *path) { 481 char buffer[1024]; 482 size_t bufindex, bufsize; 483 FILE *file; 484 unsigned size_info, size_sample, size_proc; 485 int samples_read; 486 static struct sprof_info_s sprof_info_perfile; 487 488 /* open trace file */ 489 file = fopen(path, "rb"); 490 if (!file) { 491 fprintf(stderr, "error: cannot open trace file \"%s\": %s\n", 492 path, strerror(errno)); 493 exit(1); 494 } 495 496 /* check file format and update totals */ 497 if (fscanf(file, "stat\n%u %u %u", 498 &size_info, &size_sample, &size_proc) != 3 || 499 fgetc(file) != '\n') { 500 fprintf(stderr, "error: file \"%s\" does not contain an " 501 "sprofile trace\n", path); 502 exit(1); 503 } 504 if ((size_info != sizeof(struct sprof_info_s)) || 505 (size_sample != sizeof(struct sprof_sample)) || 506 (size_proc != sizeof(struct sprof_proc))) { 507 fprintf(stderr, "error: file \"%s\" is incompatible with this " 508 "version of sprofalyze; recompile sprofalyze with the " 509 "MINIX version that created this file\n", path); 510 exit(1); 511 } 512 if (fread(&sprof_info_perfile, sizeof(sprof_info_perfile), 1, file) != 1) { 513 fprintf(stderr, "error: totals missing in file \"%s\"\n", path); 514 exit(1); 515 } 516 517 /* read and store samples */ 518 samples_read = 0; 519 bufindex = 0; 520 bufsize = 0; 521 for (;;) { 522 /* enough left in the buffer? */ 523 if (bufsize - bufindex < sizeof(union sprof_record)) { 524 /* not enough, read some more */ 525 memmove(buffer, buffer + bufindex, bufsize - bufindex); 526 bufsize -= bufindex; 527 bufindex = 0; 528 bufsize += fread(buffer + bufsize, 1, 529 sizeof(buffer) - bufsize, file); 530 531 /* are we done? */ 532 if (bufsize == 0) break; 533 } 534 535 /* process sample record (either struct sprof_sample or 536 * struct sprof_proc) 537 */ 538 bufindex += sample_process( 539 (const union sprof_record *) (buffer + bufindex), 540 bufsize - bufindex, &samples_read); 541 } 542 if (samples_read != sprof_info_perfile.system_samples) { 543 fprintf(stderr, "warning: number of system samples (%d) in " 544 "\"%s\" does not match number of records (%d)\n", 545 sprof_info.system_samples, path, samples_read); 546 } 547 548 sprof_info.system_samples += sprof_info_perfile.system_samples; 549 sprof_info.total_samples += sprof_info_perfile.total_samples; 550 sprof_info.idle_samples += sprof_info_perfile.idle_samples; 551 sprof_info.user_samples += sprof_info_perfile.user_samples; 552 553 fclose(file); 554 } 555 556 static void *malloc_checked(size_t size) { 557 void *p; 558 if (!size) return NULL; 559 p = malloc(size); 560 if (!p) { 561 fprintf(stderr, "error: malloc cannot allocate %lu bytes: %s\n", 562 (unsigned long) size, strerror(errno)); 563 exit(-1); 564 } 565 return p; 566 } 567 568 static unsigned name_hash(const char *name) { 569 int i; 570 unsigned r = 0; 571 572 /* remember: strncpy initializes all bytes */ 573 for (i = 0; i < PROC_NAME_LEN && name[i]; i++) { 574 r = r * 31 + name[i]; 575 } 576 dprintf("name_hash(\"%.*s\") = 0x%.8x\n", PROC_NAME_LEN, name, r); 577 return r; 578 } 579 580 static float percent(int value, int percent_of) { 581 assert(value >= 0); 582 assert(value <= percent_of); 583 584 return (percent_of > 0) ? (value * 100.0 / percent_of) : 0; 585 } 586 587 static void print_diff(void) { 588 const struct binary_info *binary; 589 int binary_samples; 590 const struct symbol_count *symbol; 591 592 /* print out aggregates in a machine-readable format for sprofdiff */ 593 printf("(total)\t\t%d\n", sprof_info.total_samples); 594 printf("(system)\t\t%d\n", sprof_info.system_samples); 595 printf("(idle)\t\t%d\n", sprof_info.idle_samples); 596 printf("(user)\t\t%d\n", sprof_info.user_samples); 597 for (binary = binaries; binary; binary = binary->next) { 598 binary_samples = 0; 599 for (symbol = binary->symbols; symbol; symbol = symbol->next) { 600 if (symbol->samples) { 601 printf("%.*s\t%.*s\t%d\n", 602 PROC_NAME_LEN, binary->name, 603 SYMBOL_NAME_SIZE, symbol->name, 604 symbol->samples); 605 } 606 binary_samples += symbol->samples; 607 } 608 printf("%.*s\t(total)\t%d\n", 609 PROC_NAME_LEN, binary->name, 610 binary_samples); 611 } 612 } 613 614 static void print_report(void) { 615 /* print out human-readable analysis */ 616 printf("Showing processes and functions using at least %3.0f%% " 617 "time.\n\n", minimum_perc); 618 printf(" System process ticks: %10d (%3.0f%%)\n", 619 sprof_info.system_samples, percent(sprof_info.system_samples, sprof_info.total_samples)); 620 printf(" User process ticks: %10d (%3.0f%%) " 621 "Details of system process\n", 622 sprof_info.user_samples, percent(sprof_info.user_samples, sprof_info.total_samples)); 623 printf(" Idle time ticks: %10d (%3.0f%%) " 624 "samples, aggregated and\n", 625 sprof_info.idle_samples, percent(sprof_info.idle_samples, sprof_info.total_samples)); 626 printf(" ---------- ---- " 627 "per process, are below.\n"); 628 printf(" Total ticks: %10d (100%%)\n\n", sprof_info.total_samples); 629 630 print_report_overall(); 631 print_reports_per_binary(); 632 } 633 634 static void print_report_overall(void) { 635 struct binary_info *binary; 636 struct symbol_count *symbol, **symbols_sorted; 637 unsigned index, symbol_count; 638 int sample_threshold; 639 640 /* count number of symbols to display */ 641 sample_threshold = sprof_info.system_samples * minimum_perc / 100; 642 symbol_count = 0; 643 for (binary = binaries; binary; binary = binary->next) { 644 symbol_count += count_symbols(binary, sample_threshold); 645 } 646 647 /* sort symbols by decreasing number of samples */ 648 symbols_sorted = MALLOC_CHECKED(struct symbol_count *, symbol_count); 649 index = 0; 650 for (binary = binaries; binary; binary = binary->next) { 651 for (symbol = binary->symbols; symbol; symbol = symbol->next) { 652 if (symbol->samples >= sample_threshold) { 653 symbols_sorted[index++] = symbol; 654 } 655 } 656 } 657 assert(index == symbol_count); 658 qsort(symbols_sorted, symbol_count, sizeof(symbols_sorted[0]), 659 compare_symbols); 660 661 /* report most common symbols overall */ 662 print_separator(); 663 printf("Total system process time %*d samples\n", 664 LINE_WIDTH - 34, sprof_info.system_samples); 665 print_separator(); 666 print_report_symbols(symbols_sorted, symbol_count, sprof_info.system_samples, 1); 667 free(symbols_sorted); 668 } 669 670 static void print_report_per_binary(const struct binary_info *binary) { 671 struct symbol_count *symbol, **symbols_sorted; 672 unsigned index, symbol_count; 673 int sample_threshold; 674 675 assert(binary->samples >= 0); 676 677 /* count number of symbols to display */ 678 sample_threshold = binary->samples * minimum_perc / 100; 679 symbol_count = count_symbols(binary, sample_threshold); 680 681 /* sort symbols by decreasing number of samples */ 682 symbols_sorted = MALLOC_CHECKED(struct symbol_count *, symbol_count); 683 index = 0; 684 for (symbol = binary->symbols; symbol; symbol = symbol->next) { 685 if (symbol->samples >= sample_threshold) { 686 symbols_sorted[index++] = symbol; 687 } 688 } 689 assert(index == symbol_count); 690 qsort(symbols_sorted, symbol_count, sizeof(symbols_sorted[0]), 691 compare_symbols); 692 693 /* report most common symbols for this binary */ 694 print_separator(); 695 printf("%-*.*s %4.1f%% of system process samples\n", 696 LINE_WIDTH - 32, PROC_NAME_LEN, binary->name, 697 percent(binary->samples, sprof_info.system_samples)); 698 print_separator(); 699 print_report_symbols(symbols_sorted, symbol_count, binary->samples, 0); 700 free(symbols_sorted); 701 } 702 703 static void print_reports_per_binary(void) { 704 struct binary_info *binary, **binaries_sorted; 705 unsigned binary_count, i, index; 706 int sample_threshold, samples_shown; 707 struct symbol_count *symbol; 708 709 /* count total per-binary samples */ 710 binary_count = 0; 711 for (binary = binaries; binary; binary = binary->next) { 712 assert(!binary->samples); 713 for (symbol = binary->symbols; symbol; symbol = symbol->next) { 714 binary->samples += symbol->samples; 715 } 716 binary_count++; 717 } 718 719 /* sort binaries by decreasing number of samples */ 720 binaries_sorted = MALLOC_CHECKED(struct binary_info *, binary_count); 721 index = 0; 722 for (binary = binaries; binary; binary = binary->next) { 723 binaries_sorted[index++] = binary; 724 } 725 assert(index == binary_count); 726 qsort(binaries_sorted, binary_count, sizeof(binaries_sorted[0]), 727 compare_binaries); 728 729 /* display reports for binaries with enough samples */ 730 sample_threshold = sprof_info.system_samples * minimum_perc / 100; 731 samples_shown = 0; 732 for (i = 0; i < binary_count; i++) { 733 if (binaries_sorted[i]->samples < sample_threshold) break; 734 print_report_per_binary(binaries_sorted[i]); 735 samples_shown += binaries_sorted[i]->samples; 736 } 737 print_separator(); 738 printf("samples: %d shown: %d\n", sprof_info.system_samples, samples_shown); 739 printf("processes <%3.0f%% (not showing functions) %*.1f%% of system " 740 "process samples\n", minimum_perc, LINE_WIDTH - 67, 741 percent(sprof_info.system_samples - samples_shown, sprof_info.system_samples)); 742 print_separator(); 743 744 free(binaries_sorted); 745 } 746 747 static void print_report_symbols(struct symbol_count **symbols, 748 unsigned symbol_count, int total, int show_process) { 749 unsigned bar_dots, bar_width, i, j, process_width; 750 int samples, samples_shown; 751 struct symbol_count *symbol; 752 753 /* find out how much space we have available */ 754 process_width = show_process ? (PROC_NAME_LEN + 1) : 0; 755 bar_width = LINE_WIDTH - process_width - SYMBOL_NAME_WIDTH - 17; 756 757 /* print the symbol lines */ 758 samples_shown = 0; 759 for (i = 0; i <= symbol_count; i++) { 760 if (i < symbol_count) { 761 /* first list the symbols themselves */ 762 symbol = symbols[i]; 763 printf("%*.*s %*.*s ", 764 process_width, 765 show_process ? PROC_NAME_LEN : 0, 766 symbol->binary->name, 767 SYMBOL_NAME_WIDTH, 768 SYMBOL_NAME_WIDTH, 769 symbol->name); 770 samples = symbol->samples; 771 } else { 772 /* at the end, list the remainder */ 773 printf("%*s<%3.0f%% ", 774 process_width + SYMBOL_NAME_WIDTH - 4, 775 "", 776 minimum_perc); 777 samples = total - samples_shown; 778 } 779 assert(samples >= 0); 780 assert(samples <= total); 781 bar_dots = (total > 0) ? (samples * bar_width / total) : 0; 782 for (j = 0; j < bar_dots; j++) printf("*"); 783 for (; j < bar_width; j++) printf(" "); 784 printf("%8d %5.1f%%\n", samples, percent(samples, total)); 785 samples_shown += samples; 786 } 787 788 /* print remainder and summary */ 789 print_separator(); 790 printf("%-*.*s%*d 100.0%%\n\n", PROC_NAME_LEN, PROC_NAME_LEN, 791 (show_process || symbol_count == 0) ? 792 "total" : symbols[0]->binary->name, 793 LINE_WIDTH - PROC_NAME_LEN - 7, total); 794 } 795 796 static void print_separator(void) { 797 int i; 798 for (i = 0; i < LINE_WIDTH; i++) printf("-"); 799 printf("\n"); 800 } 801 802 static int read_hex(FILE *file, unsigned long *value) { 803 int c, cvalue; 804 unsigned index; 805 806 assert(file); 807 assert(value); 808 809 index = 0; 810 c = fgetc(file); 811 *value = 0; 812 while (index < 8) { 813 if (c >= '0' && c <= '9') { 814 cvalue = c - '0'; 815 } else if (c >= 'A' && c <= 'F') { 816 cvalue = c - 'A' + 10; 817 } else if (c >= 'a' && c <= 'f') { 818 cvalue = c - 'a' + 10; 819 } else { 820 break; 821 } 822 823 *value = *value * 16 + cvalue; 824 index++; 825 c = fgetc(file); 826 } 827 if (c != EOF) ungetc(c, file); 828 829 return index; 830 } 831 832 static int read_newline(FILE *file) { 833 int c; 834 835 do { 836 c = fgetc(file); 837 } while (c != EOF && c != '\n' && isspace(c)); 838 if (c == EOF || c == '\n') return 1; 839 ungetc(c, file); 840 return 0; 841 } 842 843 static void read_nm_line(FILE *file, int line, char *name, char *type, 844 unsigned long *addr, unsigned long *size) { 845 846 assert(file); 847 assert(name); 848 assert(type); 849 assert(addr); 850 assert(size); 851 *type = 0; 852 *addr = 0; 853 *size = 0; 854 if (read_newline(file)) { 855 memset(name, 0, SYMBOL_NAME_SIZE); 856 return; 857 } 858 859 /* name and type are compulsory */ 860 read_to_whitespace(file, name, SYMBOL_NAME_SIZE); 861 if (read_newline(file)) { 862 fprintf(stderr, "error: bad output format from nm: " 863 "symbol type missing on line %d\n", line); 864 exit(-1); 865 } 866 *type = fgetc(file); 867 868 /* address is optional */ 869 if (read_newline(file)) return; 870 if (!read_hex(file, addr)) { 871 fprintf(stderr, "error: bad output format from nm: junk where " 872 "address should be on line %d\n", line); 873 exit(-1); 874 } 875 876 /* size is optional */ 877 if (read_newline(file)) return; 878 if (!read_hex(file, size)) { 879 fprintf(stderr, "error: bad output format from nm: junk where " 880 "size should be on line %d\n", line); 881 exit(-1); 882 } 883 884 /* nothing else expected */ 885 if (read_newline(file)) return; 886 fprintf(stderr, "error: bad output format from nm: junk after size " 887 "on line %d\n", line); 888 exit(-1); 889 } 890 891 static void read_to_whitespace(FILE *file, char *buffer, size_t size) { 892 int c; 893 894 /* read up to and incl first whitespace, store at most size chars */ 895 while ((c = fgetc(file)) != EOF && !isspace(c)) { 896 if (size > 0) { 897 *(buffer++) = c; 898 size--; 899 } 900 } 901 if (size > 0) *buffer = 0; 902 } 903 904 static size_t sample_process(const union sprof_record *data, size_t size, 905 int *samples_read) { 906 struct endpoint_info *epinfo, **ptr; 907 908 assert(data); 909 assert(samples_read); 910 911 /* do we have a proper sample? */ 912 if (size < sizeof(data->proc) && size < sizeof(data->sample)) { 913 goto error; 914 } 915 916 /* do we know this endpoint? */ 917 ptr = endpoint_hashtab_get_ptr(data->proc.proc); 918 if ((epinfo = *ptr)) { 919 if (!epinfo->seen) { 920 epinfo->seen = 1; 921 return sizeof(data->proc); 922 } 923 924 /* endpoint known, store sample */ 925 if (size < sizeof(data->sample)) goto error; 926 sample_store(epinfo->binary, &data->sample); 927 (*samples_read)++; 928 return sizeof(data->sample); 929 } 930 931 /* endpoint not known, add it */ 932 *ptr = epinfo = MALLOC_CHECKED(struct endpoint_info, 1); 933 memset(epinfo, 0, sizeof(struct endpoint_info)); 934 epinfo->endpoint = data->proc.proc; 935 epinfo->seen = 1; 936 epinfo->next = endpoints; 937 endpoints = epinfo; 938 939 /* fetch binary based on process name in sample */ 940 if (size < sizeof(data->proc)) goto error; 941 epinfo->binary = sample_load_binary(&data->proc); 942 return sizeof(data->proc); 943 944 error: 945 fprintf(stderr, "warning: partial sample at end of trace, " 946 "was the trace file truncated?\n"); 947 return size; 948 } 949 950 static struct binary_info *sample_load_binary( 951 const struct sprof_proc *sample) { 952 struct binary_info *binary; 953 954 /* locate binary */ 955 binary = binary_find(sample->name); 956 if (!binary) { 957 fprintf(stderr, "warning: ignoring unknown binary \"%.*s\"\n", 958 PROC_NAME_LEN, sample->name); 959 fprintf(stderr, " did you include this executable in " 960 "the configuration?\n"); 961 fprintf(stderr, " (use -b to add additional " 962 "binaries)\n"); 963 return NULL; 964 } 965 966 /* load symbols if this hasn't been done yet */ 967 if (!binary->pc_map) binary_load_pc_map(binary); 968 969 return binary; 970 } 971 972 static void sample_store(struct binary_info *binary, 973 const struct sprof_sample *sample) { 974 unsigned long index_l1; 975 struct pc_map_l2 *pc_map_l2; 976 struct symbol_count *symbol; 977 978 if (!binary || !binary->pc_map) return; 979 980 /* find the applicable symbol (two-level lookup) */ 981 index_l1 = (unsigned long) sample->pc / PC_MAP_L2_SIZE; 982 assert(index_l1 < PC_MAP_L1_SIZE); 983 pc_map_l2 = binary->pc_map->l1[index_l1]; 984 if (pc_map_l2) { 985 symbol = pc_map_l2->l2[(unsigned long) sample->pc % PC_MAP_L2_SIZE]; 986 } else { 987 symbol = NULL; 988 } 989 990 if (symbol) { 991 assert(symbol->samples >= 0); 992 symbol->samples++; 993 assert(symbol->samples >= 0); 994 } else if (!binary->no_more_warnings) { 995 fprintf(stderr, "warning: address 0x%lx not associated with a " 996 "symbol\n", (unsigned long) sample->pc); 997 fprintf(stderr, " binary may not match the profiled " 998 "version\n"); 999 fprintf(stderr, " path: \"%s\"\n", binary->path); 1000 binary->no_more_warnings = 1; 1001 dprint_symbols(binary); 1002 } 1003 } 1004 1005 static char *strdup_checked(const char *s) { 1006 char *p; 1007 if (!s) return NULL; 1008 p = strdup(s); 1009 if (!p) { 1010 fprintf(stderr, "error: strdup failed: %s\n", 1011 strerror(errno)); 1012 exit(-1); 1013 } 1014 return p; 1015 } 1016 1017 static void usage(const char *argv0) { 1018 printf("usage:\n"); 1019 printf(" %s [-d] [-p percentage] [-s src-tree-path] " 1020 "[-b binary]... file...\n", argv0); 1021 printf("\n"); 1022 printf("sprofalyze aggregates one or more sprofile traces and"); 1023 printf("reports where time was spent.\n"); 1024 printf("\n"); 1025 printf("arguments:\n"); 1026 printf("-d generates output that can be compared using sprofdiff\n"); 1027 printf("-p specifies the cut-off percentage below which binaries\n"); 1028 printf(" and functions will not be displayed\n"); 1029 printf("-s specifies the root of the source tree where sprofalyze\n"); 1030 printf(" should search for unstripped binaries to extract symbols\n"); 1031 printf(" from\n"); 1032 printf("-b specifies an additional system binary in the trace that\n"); 1033 printf(" is not in the source tree; may be specified multiple\n"); 1034 printf(" times\n"); 1035 exit(1); 1036 } 1037