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