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
[] = {
23 /* XXX this should not be necessary */
45 static const char *src_path
= "/usr/src/minix";
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
63 struct symbol_count
*next
;
64 struct binary_info
*binary
;
67 char name
[SYMBOL_NAME_SIZE
];
71 struct symbol_count
*l2
[PC_MAP_L2_SIZE
];
75 struct pc_map_l2
*l1
[PC_MAP_L1_SIZE
];
79 char name
[PROC_NAME_LEN
];
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
{
91 struct binary_info
*binary
;
92 struct endpoint_info
*hashtab_next
;
93 struct endpoint_info
*next
;
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
;
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
,
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]))
153 #define dprintf(...) do { \
154 fprintf(stderr, "debug(%s:%d): ", __FUNCTION__, __LINE__); \
155 fprintf(stderr, __VA_ARGS__); \
161 int main(int argc
, char **argv
) {
162 int opt
, sprofdiff
= 0;
165 /* disable buffering so the output mixes correctly */
166 setvbuf(stdout
, NULL
, _IONBF
, 0);
167 setvbuf(stderr
, NULL
, _IONBF
, 0);
170 /* parse arguments */
171 while ((opt
= getopt(argc
, argv
, "b:dp:s:")) != -1) {
174 /* additional binary specified */
178 /* generate output for sprofdiff */
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
);
191 /* source tree directory specified */
194 default: usage(argv
[0]);
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
)
216 static struct binary_info
*binary_add(const char *path
) {
217 struct binary_info
*binary
, **ptr
;
220 /* assumption: path won't be overwritten or deallocated in the future */
222 /* not too much effort escaping for popen, prevent problems here */
224 if (strchr(path
, '"')) {
225 fprintf(stderr
, "error: path \"%s\" contains a quote\n", path
);
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
);
239 /* check in hashtable whether this entry is indeed new */
240 ptr
= binary_hashtab_get_ptr(name
);
242 fprintf(stderr
, "warning: ignoring \"%s\" because \"%s\" was "
243 "previously specified\n", path
, (*ptr
)->path
);
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
));
252 strncpy(binary
->name
, name
, sizeof(binary
->name
));
254 /* insert into linked list */
255 binary
->next
= binaries
;
258 /* insert into hashtable */
263 static struct binary_info
*binary_find(const char *name
) {
264 struct binary_info
*binary
;
265 const char *current_name
;
267 char path
[PATH_MAX
+ 1], *path_end
;
271 /* name is required */
273 fprintf(stderr
, "warning: binary unspecified in sample\n");
277 /* do we already know this binary? */
278 binary
= binary_hashtab_get(name
);
279 if (binary
) return binary
;
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
);
290 /* paths not ending in slash: use if name matches */
291 if (strncmp(name
, current_name
,
292 PROC_NAME_LEN
) != 0) {
296 /* paths ending in slash: look in subdir named after
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
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
));
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 ");
337 static void binary_load_pc_map(struct binary_info
*binary_info
) {
338 unsigned long addr
, size
;
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
;
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");
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");
371 perror("failed to start " NM
);
376 /* read symbols from nm output */
377 assert(!binary_info
->symbols
);
378 symbol_ptr
= &binary_info
->symbols
;
380 while (!feof(file
)) {
381 /* read nm output line; can't use fscanf as it doesn't know
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
;
393 strncpy(symbol
->name
, name
, SYMBOL_NAME_SIZE
);
394 symbol_ptr
= &symbol
->next
;
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 */
432 for (p
= path
; *p
; p
++) {
433 if (*p
== '/') name
= p
+ 1;
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 */
446 if ((*b1
)->samples
> (*b2
)->samples
) return -1;
447 if ((*b1
)->samples
< (*b2
)->samples
) return 1;
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 */
459 if ((*s1
)->samples
> (*s2
)->samples
) return -1;
460 if ((*s1
)->samples
< (*s2
)->samples
) return 1;
464 static int count_symbols(const struct binary_info
*binary
, int threshold
) {
465 struct symbol_count
*symbol
;
468 for (symbol
= binary
->symbols
; symbol
; symbol
= symbol
->next
) {
469 if (symbol
->samples
>= threshold
) result
++;
474 static void dprint_symbols(const struct binary_info
*binary
) {
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
);
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 ");
499 static void load_trace(const char *path
) {
501 size_t bufindex
, bufsize
;
503 unsigned size_info
, size_sample
, size_proc
;
505 static struct sprof_info_s sprof_info_perfile
;
507 /* open trace file */
508 file
= fopen(path
, "rb");
510 fprintf(stderr
, "error: cannot open trace file \"%s\": %s\n",
511 path
, strerror(errno
));
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
);
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
);
531 if (fread(&sprof_info_perfile
, sizeof(sprof_info_perfile
), 1, file
) != 1) {
532 fprintf(stderr
, "error: totals missing in file \"%s\"\n", path
);
536 /* read and store samples */
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
);
547 bufsize
+= fread(buffer
+ bufsize
, 1,
548 sizeof(buffer
) - bufsize
, file
);
551 if (bufsize
== 0) break;
554 /* process sample record (either struct sprof_sample or
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
;
575 static void *malloc_checked(size_t size
) {
577 if (!size
) return NULL
;
580 fprintf(stderr
, "error: malloc cannot allocate %lu bytes: %s\n",
581 (unsigned long) size
, strerror(errno
));
587 static unsigned name_hash(const char *name
) {
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
);
599 static float percent(int value
, int percent_of
) {
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
;
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
) {
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
,
625 binary_samples
+= symbol
->samples
;
627 printf("%.*s\t(total)\t%d\n",
628 PROC_NAME_LEN
, binary
->name
,
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;
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
);
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]),
680 /* report most common symbols overall */
682 printf("Total system process time %*d samples\n",
683 LINE_WIDTH
- 34, sprof_info
.system_samples
);
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
);
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]),
712 /* report most common symbols for this binary */
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
));
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 */
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
;
738 /* sort binaries by decreasing number of samples */
739 binaries_sorted
= MALLOC_CHECKED(struct binary_info
*, binary_count
);
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]),
748 /* display reports for binaries with enough samples */
749 sample_threshold
= sprof_info
.system_samples
* minimum_perc
/ 100;
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
;
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
));
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 */
778 for (i
= 0; i
<= symbol_count
; i
++) {
779 if (i
< symbol_count
) {
780 /* first list the symbols themselves */
782 printf("%*.*s %*.*s ",
784 show_process
? PROC_NAME_LEN
: 0,
785 symbol
->binary
->name
,
789 samples
= symbol
->samples
;
791 /* at the end, list the remainder */
792 printf("%*s<%3.0f%% ",
793 process_width
+ SYMBOL_NAME_WIDTH
- 4,
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 */
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) {
817 for (i
= 0; i
< LINE_WIDTH
; i
++) printf("-");
821 static int read_hex(FILE *file
, unsigned long *value
) {
832 if (c
>= '0' && c
<= '9') {
834 } else if (c
>= 'A' && c
<= 'F') {
835 cvalue
= c
- 'A' + 10;
836 } else if (c
>= 'a' && c
<= 'f') {
837 cvalue
= c
- 'a' + 10;
842 *value
= *value
* 16 + cvalue
;
846 if (c
!= EOF
) ungetc(c
, file
);
851 static int read_newline(FILE *file
) {
856 } while (c
!= EOF
&& c
!= '\n' && isspace(c
));
857 if (c
== EOF
|| c
== '\n') return 1;
862 static void read_nm_line(FILE *file
, int line
, char *name
, char *type
,
863 unsigned long *addr
, unsigned long *size
) {
873 if (read_newline(file
)) {
874 memset(name
, 0, SYMBOL_NAME_SIZE
);
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
);
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
);
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
);
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
);
910 static void read_to_whitespace(FILE *file
, char *buffer
, size_t size
) {
913 /* read up to and incl first whitespace, store at most size chars */
914 while ((c
= fgetc(file
)) != EOF
&& !isspace(c
)) {
920 if (size
> 0) *buffer
= 0;
923 static size_t sample_process(const union sprof_record
*data
, size_t size
,
925 struct endpoint_info
*epinfo
, **ptr
;
928 assert(samples_read
);
930 /* do we have a proper sample? */
931 if (size
< sizeof(data
->proc
) && size
< sizeof(data
->sample
)) {
935 /* do we know this endpoint? */
936 ptr
= endpoint_hashtab_get_ptr(data
->proc
.proc
);
937 if ((epinfo
= *ptr
)) {
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
);
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
;
955 epinfo
->next
= endpoints
;
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
);
964 fprintf(stderr
, "warning: partial sample at end of trace, "
965 "was the trace file truncated?\n");
969 static struct binary_info
*sample_load_binary(
970 const struct sprof_proc
*sample
) {
971 struct binary_info
*binary
;
974 binary
= binary_find(sample
->name
);
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 "
985 /* load symbols if this hasn't been done yet */
986 if (!binary
->pc_map
) binary_load_pc_map(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
];
1004 symbol
= pc_map_l2
->l2
[(unsigned long) sample
->pc
% PC_MAP_L2_SIZE
];
1010 assert(symbol
->samples
>= 0);
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 "
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
) {
1026 if (!s
) return NULL
;
1029 fprintf(stderr
, "error: strdup failed: %s\n",
1036 static void usage(const char *argv0
) {
1038 printf(" %s [-d] [-p percentage] [-s src-tree-path] "
1039 "[-b binary]... file...\n", argv0
);
1041 printf("sprofalyze aggregates one or more sprofile traces and");
1042 printf(" reports where time was spent.\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");
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");