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 
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 
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 
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 
319 static struct binary_info *binary_hashtab_get(const char *name) {
320 	return *binary_hashtab_get_ptr(name);
321 }
322 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
815 static void print_separator(void) {
816 	int i;
817 	for (i = 0; i < LINE_WIDTH; i++) printf("-");
818 	printf("\n");
819 }
820 
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 
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 
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 
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 
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 
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 
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 
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 
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