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
main(int argc,char ** argv)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
binary_add(const char * path)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
binary_find(const char * name)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
binary_hashtab_get(const char * name)319 static struct binary_info *binary_hashtab_get(const char *name) {
320 return *binary_hashtab_get_ptr(name);
321 }
322
binary_hashtab_get_ptr(const char * name)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
binary_load_pc_map(struct binary_info * binary_info)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
binary_name(const char * path)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
compare_binaries(const void * p1,const void * p2)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
compare_symbols(const void * p1,const void * p2)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
count_symbols(const struct binary_info * binary,int threshold)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
dprint_symbols(const struct binary_info * binary)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
endpoint_hashtab_get_ptr(endpoint_t endpoint)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
load_trace(const char * path)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
malloc_checked(size_t size)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
name_hash(const char * name)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
percent(int value,int percent_of)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
print_diff(void)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
print_report(void)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
print_report_overall(void)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
print_report_per_binary(const struct binary_info * binary)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
print_reports_per_binary(void)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
print_report_symbols(struct symbol_count ** symbols,unsigned symbol_count,int total,int show_process)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
print_separator(void)815 static void print_separator(void) {
816 int i;
817 for (i = 0; i < LINE_WIDTH; i++) printf("-");
818 printf("\n");
819 }
820
read_hex(FILE * file,unsigned long * value)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
read_newline(FILE * file)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
read_nm_line(FILE * file,int line,char * name,char * type,unsigned long * addr,unsigned long * size)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
read_to_whitespace(FILE * file,char * buffer,size_t size)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
sample_process(const union sprof_record * data,size_t size,int * samples_read)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
sample_load_binary(const struct sprof_proc * sample)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
sample_store(struct binary_info * binary,const struct sprof_sample * sample)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
strdup_checked(const char * s)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
usage(const char * argv0)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