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