Remove building with NOCRYPTO option
[minix.git] / minix / commands / sprofalyze / sprofalyze.c
blob2b99814a307e503691d7f7b9491318e0c4639e17
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 /* 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/",
45 static const char *src_path = "/usr/src/minix";
47 /* types */
49 #define LINE_WIDTH 80
51 #define SYMBOL_NAME_SIZE 52
53 #define SYMBOL_NAME_WIDTH 22
55 #define SYMBOL_SIZE_MAX 0x100000
57 #define PC_MAP_L1_SIZE 0x10000
58 #define PC_MAP_L2_SIZE 0x10000
60 struct binary_info;
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];
70 struct pc_map_l2 {
71 struct symbol_count *l2[PC_MAP_L2_SIZE];
74 struct pc_map_l1 {
75 struct pc_map_l2 *l1[PC_MAP_L1_SIZE];
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;
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;
97 union sprof_record {
98 struct sprof_sample sample;
99 struct sprof_proc proc;
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;
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);
147 #define MALLOC_CHECKED(type, count) \
148 ((type *) malloc_checked(sizeof(type) * (count)))
150 #define LENGTHOF(array) (sizeof((array)) / sizeof((array)[0]))
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
161 int main(int argc, char **argv) {
162 int opt, sprofdiff = 0;
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
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);
189 break;
190 case 's':
191 /* source tree directory specified */
192 src_path = optarg;
193 break;
194 default: usage(argv[0]);
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;
207 /* print report */
208 if (sprofdiff) {
209 print_diff();
210 } else {
211 print_report();
213 return 0;
216 static struct binary_info *binary_add(const char *path) {
217 struct binary_info *binary, **ptr;
218 const char *name;
220 /* assumption: path won't be overwritten or deallocated in the future */
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);
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);
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;
246 dprintf("using %.*s from \"%s\"\n", PROC_NAME_LEN, name, path);
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));
254 /* insert into linked list */
255 binary->next = binaries;
256 binaries = binary;
258 /* insert into hashtable */
259 *ptr = binary;
260 return binary;
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;
269 assert(name);
271 /* name is required */
272 if (!*name) {
273 fprintf(stderr, "warning: binary unspecified in sample\n");
274 return NULL;
277 /* do we already know this binary? */
278 binary = binary_hashtab_get(name);
279 if (binary) return binary;
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;
295 } else {
296 /* paths ending in slash: look in subdir named after
297 * binary
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);
305 /* use access to find out whether the binary exists and is
306 * readable
308 dprintf("checking whether \"%s\" exists\n", path);
309 if (access(path, R_OK) < 0) continue;
311 /* ok, this seems to be the one */
312 return binary_add(strdup_checked(path));
315 /* not found */
316 return NULL;
319 static struct binary_info *binary_hashtab_get(const char *name) {
320 return *binary_hashtab_get_ptr(name);
323 static struct binary_info **binary_hashtab_get_ptr(const char *name) {
324 struct binary_info *binary, **ptr;
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;
332 dprintf("looked up binary \"%.*s\" in hash table, %sfound\n",
333 PROC_NAME_LEN, name, *ptr ? "" : "not ");
334 return ptr;
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;
351 assert(binary_info);
352 assert(!strchr(NM, '"'));
353 assert(!strchr(binary_info->path, '"'));
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;
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);
374 free(command);
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
384 read_nm_line(file, line++, name, &type, &addr, &size);
386 /* store only text symbols */
387 if (type != 't' && type != 'T') continue;
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
399 fclose(file);
400 dprintf("extracted %u symbols\n", count);
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;
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));
421 index_l2 = addr % PC_MAP_L2_SIZE;
422 pc_map_l2->l2[index_l2] = symbol;
427 static const char *binary_name(const char *path) {
428 const char *name, *p;
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;
435 return name;
438 static int compare_binaries(const void *p1, const void *p2) {
439 const struct binary_info *const *b1 = p1, *const *b2 = p2;
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;
451 static int compare_symbols(const void *p1, const void *p2) {
452 const struct symbol_count *const *s1 = p1, *const *s2 = p2;
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;
464 static int count_symbols(const struct binary_info *binary, int threshold) {
465 struct symbol_count *symbol;
466 int result = 0;
468 for (symbol = binary->symbols; symbol; symbol = symbol->next) {
469 if (symbol->samples >= threshold) result++;
471 return result;
474 static void dprint_symbols(const struct binary_info *binary) {
475 #if DEBUG
476 const struct symbol_count *symbol;
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);
483 #endif
486 static struct endpoint_info **endpoint_hashtab_get_ptr(endpoint_t endpoint) {
487 struct endpoint_info *epinfo, **ptr;
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;
494 dprintf("looked up endpoint %ld in hash table, %sfound\n",
495 (long) endpoint, *ptr ? "" : "not ");
496 return ptr;
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;
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);
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);
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);
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);
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);
550 /* are we done? */
551 if (bufsize == 0) break;
554 /* process sample record (either struct sprof_sample or
555 * struct sprof_proc)
557 bufindex += sample_process(
558 (const union sprof_record *) (buffer + bufindex),
559 bufsize - bufindex, &samples_read);
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);
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;
572 fclose(file);
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);
584 return p;
587 static unsigned name_hash(const char *name) {
588 int i;
589 unsigned r = 0;
591 /* remember: strncpy initializes all bytes */
592 for (i = 0; i < PROC_NAME_LEN && name[i]; i++) {
593 r = r * 31 + name[i];
595 dprintf("name_hash(\"%.*s\") = 0x%.8x\n", PROC_NAME_LEN, name, r);
596 return r;
599 static float percent(int value, int percent_of) {
600 assert(value >= 0);
601 assert(value <= percent_of);
603 return (percent_of > 0) ? (value * 100.0 / percent_of) : 0;
606 static void print_diff(void) {
607 const struct binary_info *binary;
608 int binary_samples;
609 const struct symbol_count *symbol;
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);
625 binary_samples += symbol->samples;
627 printf("%.*s\t(total)\t%d\n",
628 PROC_NAME_LEN, binary->name,
629 binary_samples);
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);
649 print_report_overall();
650 print_reports_per_binary();
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;
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);
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;
676 assert(index == symbol_count);
677 qsort(symbols_sorted, symbol_count, sizeof(symbols_sorted[0]),
678 compare_symbols);
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);
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;
694 assert(binary->samples >= 0);
696 /* count number of symbols to display */
697 sample_threshold = binary->samples * minimum_perc / 100;
698 symbol_count = count_symbols(binary, sample_threshold);
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;
708 assert(index == symbol_count);
709 qsort(symbols_sorted, symbol_count, sizeof(symbols_sorted[0]),
710 compare_symbols);
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);
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;
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;
735 binary_count++;
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;
744 assert(index == binary_count);
745 qsort(binaries_sorted, binary_count, sizeof(binaries_sorted[0]),
746 compare_binaries);
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;
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();
763 free(binaries_sorted);
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;
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;
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,
795 minimum_perc);
796 samples = total - samples_shown;
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;
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);
815 static void print_separator(void) {
816 int i;
817 for (i = 0; i < LINE_WIDTH; i++) printf("-");
818 printf("\n");
821 static int read_hex(FILE *file, unsigned long *value) {
822 int c, cvalue;
823 unsigned index;
825 assert(file);
826 assert(value);
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;
842 *value = *value * 16 + cvalue;
843 index++;
844 c = fgetc(file);
846 if (c != EOF) ungetc(c, file);
848 return index;
851 static int read_newline(FILE *file) {
852 int c;
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;
862 static void read_nm_line(FILE *file, int line, char *name, char *type,
863 unsigned long *addr, unsigned long *size) {
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;
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);
885 *type = fgetc(file);
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);
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);
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);
910 static void read_to_whitespace(FILE *file, char *buffer, size_t size) {
911 int c;
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--;
920 if (size > 0) *buffer = 0;
923 static size_t sample_process(const union sprof_record *data, size_t size,
924 int *samples_read) {
925 struct endpoint_info *epinfo, **ptr;
927 assert(data);
928 assert(samples_read);
930 /* do we have a proper sample? */
931 if (size < sizeof(data->proc) && size < sizeof(data->sample)) {
932 goto error;
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);
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);
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;
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);
963 error:
964 fprintf(stderr, "warning: partial sample at end of trace, "
965 "was the trace file truncated?\n");
966 return size;
969 static struct binary_info *sample_load_binary(
970 const struct sprof_proc *sample) {
971 struct binary_info *binary;
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;
985 /* load symbols if this hasn't been done yet */
986 if (!binary->pc_map) binary_load_pc_map(binary);
988 return binary;
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;
997 if (!binary || !binary->pc_map) return;
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;
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);
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);
1033 return p;
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);