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