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