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