1433d6423SLionel Sambuc #include <assert.h>
2433d6423SLionel Sambuc #include <ctype.h>
3433d6423SLionel Sambuc #include <errno.h>
4433d6423SLionel Sambuc #include <limits.h>
5433d6423SLionel Sambuc #include <minix/profile.h>
6433d6423SLionel Sambuc #include <stdio.h>
7433d6423SLionel Sambuc #include <stdlib.h>
8433d6423SLionel Sambuc #include <string.h>
9433d6423SLionel Sambuc #include <unistd.h>
10433d6423SLionel Sambuc 
11433d6423SLionel Sambuc /* user-configurable settings */
12433d6423SLionel Sambuc #define BINARY_HASHTAB_SIZE 1024
13433d6423SLionel Sambuc 
14433d6423SLionel Sambuc #define ENDPOINT_HASHTAB_SIZE 1024
15433d6423SLionel Sambuc 
16433d6423SLionel Sambuc #define DEBUG 0
17433d6423SLionel Sambuc 
18433d6423SLionel Sambuc #define NM "/usr/pkg/bin/nm"
19433d6423SLionel Sambuc 
20433d6423SLionel Sambuc static const char *default_binaries[] = {
21433d6423SLionel Sambuc 	"kernel/kernel",
22433d6423SLionel Sambuc 	"servers/",
23*373b7939SDavid van Moolenbroek 	/* XXX this should not be necessary */
24*373b7939SDavid van Moolenbroek 	"drivers/audio/",
25*373b7939SDavid van Moolenbroek 	"drivers/bus/",
26*373b7939SDavid van Moolenbroek 	"drivers/clock/",
27*373b7939SDavid van Moolenbroek 	"drivers/eeprom/",
28*373b7939SDavid van Moolenbroek 	"drivers/examples/",
29*373b7939SDavid van Moolenbroek 	"drivers/hid/",
30*373b7939SDavid van Moolenbroek 	"drivers/iommu/",
31*373b7939SDavid van Moolenbroek 	"drivers/net/",
32*373b7939SDavid van Moolenbroek 	"drivers/power/",
33*373b7939SDavid van Moolenbroek 	"drivers/printer/",
34*373b7939SDavid van Moolenbroek 	"drivers/sensors/",
35*373b7939SDavid van Moolenbroek 	"drivers/storage/",
36*373b7939SDavid van Moolenbroek 	"drivers/system/",
37*373b7939SDavid van Moolenbroek 	"drivers/tty/",
38*373b7939SDavid van Moolenbroek 	"drivers/usb/",
39*373b7939SDavid van Moolenbroek 	"drivers/video/",
40*373b7939SDavid van Moolenbroek 	"drivers/vmm_guest/",
41*373b7939SDavid van Moolenbroek 	"fs/",
42*373b7939SDavid van Moolenbroek 	"net/",
43433d6423SLionel Sambuc };
44433d6423SLionel Sambuc 
45*373b7939SDavid van Moolenbroek static const char *src_path = "/usr/src/minix";
46433d6423SLionel Sambuc 
47433d6423SLionel Sambuc /* types */
48433d6423SLionel Sambuc 
49433d6423SLionel Sambuc #define LINE_WIDTH 80
50433d6423SLionel Sambuc 
51433d6423SLionel Sambuc #define SYMBOL_NAME_SIZE 52
52433d6423SLionel Sambuc 
53433d6423SLionel Sambuc #define SYMBOL_NAME_WIDTH 22
54433d6423SLionel Sambuc 
55433d6423SLionel Sambuc #define SYMBOL_SIZE_MAX 0x100000
56433d6423SLionel Sambuc 
57433d6423SLionel Sambuc #define PC_MAP_L1_SIZE 0x10000
58433d6423SLionel Sambuc #define PC_MAP_L2_SIZE 0x10000
59433d6423SLionel Sambuc 
60433d6423SLionel Sambuc struct binary_info;
61433d6423SLionel Sambuc 
62433d6423SLionel Sambuc struct symbol_count {
63433d6423SLionel Sambuc 	struct symbol_count *next;
64433d6423SLionel Sambuc 	struct binary_info *binary;
65433d6423SLionel Sambuc 	uint32_t addr;
66433d6423SLionel Sambuc 	int samples;
67433d6423SLionel Sambuc 	char name[SYMBOL_NAME_SIZE];
68433d6423SLionel Sambuc };
69433d6423SLionel Sambuc 
70433d6423SLionel Sambuc struct pc_map_l2 {
71433d6423SLionel Sambuc 	struct symbol_count *l2[PC_MAP_L2_SIZE];
72433d6423SLionel Sambuc };
73433d6423SLionel Sambuc 
74433d6423SLionel Sambuc struct pc_map_l1 {
75433d6423SLionel Sambuc 	struct pc_map_l2 *l1[PC_MAP_L1_SIZE];
76433d6423SLionel Sambuc };
77433d6423SLionel Sambuc 
78433d6423SLionel Sambuc struct binary_info {
79433d6423SLionel Sambuc 	char name[PROC_NAME_LEN];
80433d6423SLionel Sambuc 	const char *path;
81433d6423SLionel Sambuc 	int samples;
82433d6423SLionel Sambuc 	struct symbol_count *symbols;
83433d6423SLionel Sambuc 	struct pc_map_l1 *pc_map;
84433d6423SLionel Sambuc 	struct binary_info *next;
85433d6423SLionel Sambuc 	struct binary_info *hashtab_next;
86433d6423SLionel Sambuc 	char no_more_warnings;
87433d6423SLionel Sambuc };
88433d6423SLionel Sambuc 
89433d6423SLionel Sambuc struct endpoint_info {
90433d6423SLionel Sambuc 	endpoint_t endpoint;
91433d6423SLionel Sambuc 	struct binary_info *binary;
92433d6423SLionel Sambuc 	struct endpoint_info *hashtab_next;
93433d6423SLionel Sambuc 	struct endpoint_info *next;
94433d6423SLionel Sambuc 	char seen;
95433d6423SLionel Sambuc };
96433d6423SLionel Sambuc 
97433d6423SLionel Sambuc union sprof_record {
98433d6423SLionel Sambuc 	struct sprof_sample sample;
99433d6423SLionel Sambuc 	struct sprof_proc proc;
100433d6423SLionel Sambuc };
101433d6423SLionel Sambuc 
102433d6423SLionel Sambuc /* global variables */
103433d6423SLionel Sambuc static struct binary_info *binaries;
104433d6423SLionel Sambuc static struct binary_info *binary_hashtab[BINARY_HASHTAB_SIZE];
105433d6423SLionel Sambuc static struct endpoint_info *endpoint_hashtab[ENDPOINT_HASHTAB_SIZE];
106433d6423SLionel Sambuc static struct endpoint_info *endpoints;
107433d6423SLionel Sambuc static double minimum_perc = 1.0;
108433d6423SLionel Sambuc static struct sprof_info_s sprof_info;
109433d6423SLionel Sambuc 
110433d6423SLionel Sambuc /* prototypes */
111433d6423SLionel Sambuc static struct binary_info *binary_add(const char *path);
112433d6423SLionel Sambuc static struct binary_info *binary_find(const char *name);
113433d6423SLionel Sambuc static struct binary_info *binary_hashtab_get(const char *name);
114433d6423SLionel Sambuc static struct binary_info **binary_hashtab_get_ptr(const char *name);
115433d6423SLionel Sambuc static void binary_load_pc_map(struct binary_info *binary_info);
116433d6423SLionel Sambuc static const char *binary_name(const char *path);
117433d6423SLionel Sambuc static int compare_binaries(const void *p1, const void *p2);
118433d6423SLionel Sambuc static int compare_symbols(const void *p1, const void *p2);
119433d6423SLionel Sambuc static int count_symbols(const struct binary_info *binary, int threshold);
120433d6423SLionel Sambuc static void dprint_symbols(const struct binary_info *binary);
121433d6423SLionel Sambuc static struct endpoint_info **endpoint_hashtab_get_ptr(endpoint_t endpoint);
122433d6423SLionel Sambuc static void load_trace(const char *path);
123433d6423SLionel Sambuc static void *malloc_checked(size_t size);
124433d6423SLionel Sambuc static unsigned name_hash(const char *name);
125433d6423SLionel Sambuc static float percent(int value, int percent_of);
126433d6423SLionel Sambuc static void print_diff(void);
127433d6423SLionel Sambuc static void print_report(void);
128433d6423SLionel Sambuc static void print_report_overall(void);
129433d6423SLionel Sambuc static void print_report_per_binary(const struct binary_info *binary);
130433d6423SLionel Sambuc static void print_reports_per_binary(void);
131433d6423SLionel Sambuc static void print_report_symbols(struct symbol_count **symbols,
132433d6423SLionel Sambuc 	unsigned symbol_count, int total, int show_binary);
133433d6423SLionel Sambuc static void print_separator(void);
134433d6423SLionel Sambuc static int read_hex(FILE *file, unsigned long *value);
135433d6423SLionel Sambuc static int read_newline(FILE *file);
136433d6423SLionel Sambuc static void read_nm_line(FILE *file, int line, char *name, char *type,
137433d6423SLionel Sambuc 	unsigned long *addr, unsigned long *size);
138433d6423SLionel Sambuc static void read_to_whitespace(FILE *file, char *buffer, size_t size);
139433d6423SLionel Sambuc static size_t sample_process(const union sprof_record *data, size_t size,
140433d6423SLionel Sambuc 	int *samples_read);
141433d6423SLionel Sambuc static struct binary_info *sample_load_binary(const struct sprof_proc *sample);
142433d6423SLionel Sambuc static void sample_store(struct binary_info *binary,
143433d6423SLionel Sambuc 	const struct sprof_sample *sample);
144433d6423SLionel Sambuc static char *strdup_checked(const char *s);
145433d6423SLionel Sambuc static void usage(const char *argv0);
146433d6423SLionel Sambuc 
147433d6423SLionel Sambuc #define MALLOC_CHECKED(type, count) \
148433d6423SLionel Sambuc 	((type *) malloc_checked(sizeof(type) * (count)))
149433d6423SLionel Sambuc 
150433d6423SLionel Sambuc #define LENGTHOF(array) (sizeof((array)) / sizeof((array)[0]))
151433d6423SLionel Sambuc 
152433d6423SLionel Sambuc #if DEBUG
153433d6423SLionel Sambuc #define dprintf(...) do { 						\
154433d6423SLionel Sambuc 	fprintf(stderr, "debug(%s:%d): ", __FUNCTION__, __LINE__); 	\
155433d6423SLionel Sambuc 	fprintf(stderr, __VA_ARGS__); 					\
156433d6423SLionel Sambuc } while(0)
157433d6423SLionel Sambuc #else
158433d6423SLionel Sambuc #define dprintf(...)
159433d6423SLionel Sambuc #endif
160433d6423SLionel Sambuc 
main(int argc,char ** argv)161433d6423SLionel Sambuc int main(int argc, char **argv) {
162433d6423SLionel Sambuc 	int opt, sprofdiff = 0;
163433d6423SLionel Sambuc 
164433d6423SLionel Sambuc #ifdef DEBUG
165433d6423SLionel Sambuc 	/* disable buffering so the output mixes correctly */
166433d6423SLionel Sambuc 	setvbuf(stdout, NULL, _IONBF, 0);
167433d6423SLionel Sambuc 	setvbuf(stderr, NULL, _IONBF, 0);
168433d6423SLionel Sambuc #endif
169433d6423SLionel Sambuc 
170433d6423SLionel Sambuc 	/* parse arguments */
171433d6423SLionel Sambuc 	while ((opt = getopt(argc, argv, "b:dp:s:")) != -1) {
172433d6423SLionel Sambuc 		switch (opt) {
173433d6423SLionel Sambuc 		case 'b':
174433d6423SLionel Sambuc 			/* additional binary specified */
175433d6423SLionel Sambuc 			binary_add(optarg);
176433d6423SLionel Sambuc 			break;
177433d6423SLionel Sambuc 		case 'd':
178433d6423SLionel Sambuc 			/* generate output for sprofdiff */
179433d6423SLionel Sambuc 			sprofdiff = 1;
180433d6423SLionel Sambuc 			break;
181433d6423SLionel Sambuc 		case 'p':
182433d6423SLionel Sambuc 			/* minimum percentage specified */
183433d6423SLionel Sambuc 			minimum_perc = atof(optarg);
184433d6423SLionel Sambuc 			if (minimum_perc < 0 || minimum_perc > 100) {
185433d6423SLionel Sambuc 				fprintf(stderr, "error: cut-off percentage "
186433d6423SLionel Sambuc 					"makes no sense: %g\n", minimum_perc);
187433d6423SLionel Sambuc 				exit(1);
188433d6423SLionel Sambuc 			}
189433d6423SLionel Sambuc 			break;
190433d6423SLionel Sambuc 		case 's':
191433d6423SLionel Sambuc 			/* source tree directory specified */
192433d6423SLionel Sambuc 			src_path = optarg;
193433d6423SLionel Sambuc 			break;
194433d6423SLionel Sambuc 		default: usage(argv[0]);
195433d6423SLionel Sambuc 		}
196433d6423SLionel Sambuc 	}
197433d6423SLionel Sambuc 
198433d6423SLionel Sambuc 	/* load samples */
199433d6423SLionel Sambuc 	if (optind >= argc) usage(argv[0]);
200433d6423SLionel Sambuc 	for (; optind < argc; optind++) {
201433d6423SLionel Sambuc 		struct endpoint_info *e;
202433d6423SLionel Sambuc 		load_trace(argv[optind]);
203433d6423SLionel Sambuc 		for(e = endpoints; e; e = e->next)
204433d6423SLionel Sambuc 			e->seen = 0;
205433d6423SLionel Sambuc 	}
206433d6423SLionel Sambuc 
207433d6423SLionel Sambuc 	/* print report */
208433d6423SLionel Sambuc 	if (sprofdiff) {
209433d6423SLionel Sambuc 		print_diff();
210433d6423SLionel Sambuc 	} else {
211433d6423SLionel Sambuc 		print_report();
212433d6423SLionel Sambuc 	}
213433d6423SLionel Sambuc 	return 0;
214433d6423SLionel Sambuc }
215433d6423SLionel Sambuc 
binary_add(const char * path)216433d6423SLionel Sambuc static struct binary_info *binary_add(const char *path) {
217433d6423SLionel Sambuc 	struct binary_info *binary, **ptr;
218433d6423SLionel Sambuc 	const char *name;
219433d6423SLionel Sambuc 
220433d6423SLionel Sambuc 	/* assumption: path won't be overwritten or deallocated in the future */
221433d6423SLionel Sambuc 
222433d6423SLionel Sambuc 	/* not too much effort escaping for popen, prevent problems here */
223433d6423SLionel Sambuc 	assert(path);
224433d6423SLionel Sambuc 	if (strchr(path, '"')) {
225433d6423SLionel Sambuc 		fprintf(stderr, "error: path \"%s\" contains a quote\n", path);
226433d6423SLionel Sambuc 		exit(1);
227433d6423SLionel Sambuc 	}
228433d6423SLionel Sambuc 
229433d6423SLionel Sambuc 	/* get filename */
230433d6423SLionel Sambuc 	name = binary_name(path);
231433d6423SLionel Sambuc 	dprintf("adding binary \"%s\" with name \"%.*s\"\n",
232433d6423SLionel Sambuc 		path, PROC_NAME_LEN, name);
233433d6423SLionel Sambuc 	if (strlen(name) == 0) {
234433d6423SLionel Sambuc 		fprintf(stderr, "error: path \"%s\" does not "
235433d6423SLionel Sambuc 			"contain a filename\n", path);
236433d6423SLionel Sambuc 		exit(1);
237433d6423SLionel Sambuc 	}
238433d6423SLionel Sambuc 
239433d6423SLionel Sambuc 	/* check in hashtable whether this entry is indeed new */
240433d6423SLionel Sambuc 	ptr = binary_hashtab_get_ptr(name);
241433d6423SLionel Sambuc 	if (*ptr) {
242433d6423SLionel Sambuc 		fprintf(stderr, "warning: ignoring \"%s\" because \"%s\" was "
243433d6423SLionel Sambuc 			"previously specified\n", path, (*ptr)->path);
244433d6423SLionel Sambuc 		return *ptr;
245433d6423SLionel Sambuc 	}
246433d6423SLionel Sambuc 	dprintf("using %.*s from \"%s\"\n", PROC_NAME_LEN, name, path);
247433d6423SLionel Sambuc 
248433d6423SLionel Sambuc 	/* allocate new binary_info */
249433d6423SLionel Sambuc 	binary = MALLOC_CHECKED(struct binary_info, 1);
250433d6423SLionel Sambuc 	memset(binary, 0, sizeof(struct binary_info));
251433d6423SLionel Sambuc 	binary->path = path;
252433d6423SLionel Sambuc 	strncpy(binary->name, name, sizeof(binary->name));
253433d6423SLionel Sambuc 
254433d6423SLionel Sambuc 	/* insert into linked list */
255433d6423SLionel Sambuc 	binary->next = binaries;
256433d6423SLionel Sambuc 	binaries = binary;
257433d6423SLionel Sambuc 
258433d6423SLionel Sambuc 	/* insert into hashtable */
259433d6423SLionel Sambuc 	*ptr = binary;
260433d6423SLionel Sambuc 	return binary;
261433d6423SLionel Sambuc }
262433d6423SLionel Sambuc 
binary_find(const char * name)263433d6423SLionel Sambuc static struct binary_info *binary_find(const char *name) {
264433d6423SLionel Sambuc 	struct binary_info *binary;
265433d6423SLionel Sambuc 	const char *current_name;
266433d6423SLionel Sambuc 	unsigned i;
267433d6423SLionel Sambuc 	char path[PATH_MAX + 1], *path_end;
268433d6423SLionel Sambuc 
269433d6423SLionel Sambuc 	assert(name);
270433d6423SLionel Sambuc 
271433d6423SLionel Sambuc 	/* name is required */
272433d6423SLionel Sambuc 	if (!*name) {
273433d6423SLionel Sambuc 		fprintf(stderr, "warning: binary unspecified in sample\n");
274433d6423SLionel Sambuc 		return NULL;
275433d6423SLionel Sambuc 	}
276433d6423SLionel Sambuc 
277433d6423SLionel Sambuc 	/* do we already know this binary? */
278433d6423SLionel Sambuc 	binary = binary_hashtab_get(name);
279433d6423SLionel Sambuc 	if (binary) return binary;
280433d6423SLionel Sambuc 
281433d6423SLionel Sambuc 	/* search for it */
282433d6423SLionel Sambuc 	dprintf("searching for binary \"%.*s\" in \"%s\"\n",
283433d6423SLionel Sambuc 		PROC_NAME_LEN, name, src_path);
284433d6423SLionel Sambuc 	for (i = 0; i < LENGTHOF(default_binaries); i++) {
285433d6423SLionel Sambuc 		snprintf(path, sizeof(path), "%s/%s", src_path,
286433d6423SLionel Sambuc 			default_binaries[i]);
287433d6423SLionel Sambuc 		current_name = binary_name(path);
288433d6423SLionel Sambuc 		assert(current_name);
289433d6423SLionel Sambuc 		if (*current_name) {
290433d6423SLionel Sambuc 			/* paths not ending in slash: use if name matches */
291433d6423SLionel Sambuc 			if (strncmp(name, current_name,
292433d6423SLionel Sambuc 				PROC_NAME_LEN) != 0) {
293433d6423SLionel Sambuc 				continue;
294433d6423SLionel Sambuc 			}
295433d6423SLionel Sambuc 		} else {
296433d6423SLionel Sambuc 			/* paths ending in slash: look in subdir named after
297433d6423SLionel Sambuc 			 * binary
298433d6423SLionel Sambuc 			 */
299433d6423SLionel Sambuc 			path_end = path + strlen(path);
300433d6423SLionel Sambuc 			snprintf(path_end, sizeof(path) - (path_end - path),
301433d6423SLionel Sambuc 				"%.*s/%.*s", PROC_NAME_LEN, name,
302433d6423SLionel Sambuc 				PROC_NAME_LEN, name);
303433d6423SLionel Sambuc 		}
304433d6423SLionel Sambuc 
305433d6423SLionel Sambuc 		/* use access to find out whether the binary exists and is
306433d6423SLionel Sambuc 		 * readable
307433d6423SLionel Sambuc 		 */
308433d6423SLionel Sambuc 		dprintf("checking whether \"%s\" exists\n", path);
309433d6423SLionel Sambuc 		if (access(path, R_OK) < 0) continue;
310433d6423SLionel Sambuc 
311433d6423SLionel Sambuc 		/* ok, this seems to be the one */
312433d6423SLionel Sambuc 		return binary_add(strdup_checked(path));
313433d6423SLionel Sambuc 	}
314433d6423SLionel Sambuc 
315433d6423SLionel Sambuc 	/* not found */
316433d6423SLionel Sambuc 	return NULL;
317433d6423SLionel Sambuc }
318433d6423SLionel Sambuc 
binary_hashtab_get(const char * name)319433d6423SLionel Sambuc static struct binary_info *binary_hashtab_get(const char *name) {
320433d6423SLionel Sambuc 	return *binary_hashtab_get_ptr(name);
321433d6423SLionel Sambuc }
322433d6423SLionel Sambuc 
binary_hashtab_get_ptr(const char * name)323433d6423SLionel Sambuc static struct binary_info **binary_hashtab_get_ptr(const char *name) {
324433d6423SLionel Sambuc 	struct binary_info *binary, **ptr;
325433d6423SLionel Sambuc 
326433d6423SLionel Sambuc 	/* get pointer to location of the binary in hash table */
327433d6423SLionel Sambuc 	ptr = &binary_hashtab[name_hash(name) % BINARY_HASHTAB_SIZE];
328433d6423SLionel Sambuc 	while ((binary = *ptr) && strncmp(binary->name, name,
329433d6423SLionel Sambuc 		PROC_NAME_LEN) != 0) {
330433d6423SLionel Sambuc 		ptr = &binary->hashtab_next;
331433d6423SLionel Sambuc 	}
332433d6423SLionel Sambuc 	dprintf("looked up binary \"%.*s\" in hash table, %sfound\n",
333433d6423SLionel Sambuc 		PROC_NAME_LEN, name, *ptr ? "" : "not ");
334433d6423SLionel Sambuc 	return ptr;
335433d6423SLionel Sambuc }
336433d6423SLionel Sambuc 
binary_load_pc_map(struct binary_info * binary_info)337433d6423SLionel Sambuc static void binary_load_pc_map(struct binary_info *binary_info) {
338433d6423SLionel Sambuc 	unsigned long addr, size;
339433d6423SLionel Sambuc 	char *command;
340433d6423SLionel Sambuc 	size_t command_len;
341433d6423SLionel Sambuc #if DEBUG
342433d6423SLionel Sambuc 	unsigned count = 0;
343433d6423SLionel Sambuc #endif
344433d6423SLionel Sambuc 	FILE *file;
345433d6423SLionel Sambuc 	int index_l1, index_l2, line;
346433d6423SLionel Sambuc 	char name[SYMBOL_NAME_SIZE];
347433d6423SLionel Sambuc 	struct pc_map_l2 *pc_map_l2, **pc_map_l2_ptr;
348433d6423SLionel Sambuc 	struct symbol_count *symbol, **symbol_ptr;
349433d6423SLionel Sambuc 	char type;
350433d6423SLionel Sambuc 
351433d6423SLionel Sambuc 	assert(binary_info);
352433d6423SLionel Sambuc 	assert(!strchr(NM, '"'));
353433d6423SLionel Sambuc 	assert(!strchr(binary_info->path, '"'));
354433d6423SLionel Sambuc 
355433d6423SLionel Sambuc 	/* does the file exist? */
356433d6423SLionel Sambuc 	if (access(binary_info->path, R_OK) < 0) {
357433d6423SLionel Sambuc 		fprintf(stderr, "warning: \"%s\" does not exist or "
358433d6423SLionel Sambuc 			"not readable.\n", binary_info->path);
359433d6423SLionel Sambuc 		fprintf(stderr, "         Did you do a make?\n");
360433d6423SLionel Sambuc 		return;
361433d6423SLionel Sambuc 	}
362433d6423SLionel Sambuc 
363433d6423SLionel Sambuc 	/* execute nm to get symbols */
364433d6423SLionel Sambuc 	command_len = strlen(NM) + strlen(binary_info->path) + 32;
365433d6423SLionel Sambuc 	command = MALLOC_CHECKED(char, command_len);
366433d6423SLionel Sambuc 	snprintf(command, command_len, "\"%s\" -nP \"%s\"",
367433d6423SLionel Sambuc 		NM, binary_info->path);
368433d6423SLionel Sambuc 	dprintf("running command for extracting addresses: %s\n", command);
369433d6423SLionel Sambuc 	file = popen(command, "r");
370433d6423SLionel Sambuc 	if (!file) {
371433d6423SLionel Sambuc 		perror("failed to start " NM);
372433d6423SLionel Sambuc 		exit(-1);
373433d6423SLionel Sambuc 	}
374433d6423SLionel Sambuc 	free(command);
375433d6423SLionel Sambuc 
376433d6423SLionel Sambuc 	/* read symbols from nm output */
377433d6423SLionel Sambuc 	assert(!binary_info->symbols);
378433d6423SLionel Sambuc 	symbol_ptr = &binary_info->symbols;
379433d6423SLionel Sambuc 	line = 1;
380433d6423SLionel Sambuc 	while (!feof(file)) {
381433d6423SLionel Sambuc 		/* read nm output line; can't use fscanf as it doesn't know
382433d6423SLionel Sambuc 		 * where to stop
383433d6423SLionel Sambuc 		 */
384433d6423SLionel Sambuc 		read_nm_line(file, line++, name, &type, &addr, &size);
385433d6423SLionel Sambuc 
386433d6423SLionel Sambuc 		/* store only text symbols */
387433d6423SLionel Sambuc 		if (type != 't' && type != 'T') continue;
388433d6423SLionel Sambuc 
389433d6423SLionel Sambuc 		*symbol_ptr = symbol = MALLOC_CHECKED(struct symbol_count, 1);
390433d6423SLionel Sambuc 		memset(symbol, 0, sizeof(*symbol));
391433d6423SLionel Sambuc 		symbol->binary = binary_info;
392433d6423SLionel Sambuc 		symbol->addr = addr;
393433d6423SLionel Sambuc 		strncpy(symbol->name, name, SYMBOL_NAME_SIZE);
394433d6423SLionel Sambuc 		symbol_ptr = &symbol->next;
395433d6423SLionel Sambuc #if DEBUG
396433d6423SLionel Sambuc 		count++;
397433d6423SLionel Sambuc #endif
398433d6423SLionel Sambuc 	}
399433d6423SLionel Sambuc 	fclose(file);
400433d6423SLionel Sambuc 	dprintf("extracted %u symbols\n", count);
401433d6423SLionel Sambuc 
402433d6423SLionel Sambuc 	/* create program counter map from symbols */
403433d6423SLionel Sambuc 	assert(!binary_info->pc_map);
404433d6423SLionel Sambuc 	binary_info->pc_map = MALLOC_CHECKED(struct pc_map_l1, 1);
405433d6423SLionel Sambuc 	memset(binary_info->pc_map, 0, sizeof(struct pc_map_l1));
406433d6423SLionel Sambuc 	for (symbol = binary_info->symbols; symbol; symbol = symbol->next) {
407433d6423SLionel Sambuc 		/* compute size if not specified */
408433d6423SLionel Sambuc 		size = symbol->next ? (symbol->next->addr - symbol->addr) : 1;
409433d6423SLionel Sambuc 		if (size > SYMBOL_SIZE_MAX) size = SYMBOL_SIZE_MAX;
410433d6423SLionel Sambuc 
411433d6423SLionel Sambuc 		/* mark each address */
412433d6423SLionel Sambuc 		for (addr = symbol->addr; addr - symbol->addr < size; addr++) {
413433d6423SLionel Sambuc 			index_l1 = addr / PC_MAP_L2_SIZE;
414433d6423SLionel Sambuc 			assert(index_l1 < PC_MAP_L1_SIZE);
415433d6423SLionel Sambuc 			pc_map_l2_ptr = &binary_info->pc_map->l1[index_l1];
416433d6423SLionel Sambuc 			if (!(pc_map_l2 = *pc_map_l2_ptr)) {
417433d6423SLionel Sambuc 				*pc_map_l2_ptr = pc_map_l2 =
418433d6423SLionel Sambuc 					MALLOC_CHECKED(struct pc_map_l2, 1);
419433d6423SLionel Sambuc 				memset(pc_map_l2, 0, sizeof(struct pc_map_l2));
420433d6423SLionel Sambuc 			}
421433d6423SLionel Sambuc 			index_l2 = addr % PC_MAP_L2_SIZE;
422433d6423SLionel Sambuc 			pc_map_l2->l2[index_l2] = symbol;
423433d6423SLionel Sambuc 		}
424433d6423SLionel Sambuc 	}
425433d6423SLionel Sambuc }
426433d6423SLionel Sambuc 
binary_name(const char * path)427433d6423SLionel Sambuc static const char *binary_name(const char *path) {
428433d6423SLionel Sambuc 	const char *name, *p;
429433d6423SLionel Sambuc 
430433d6423SLionel Sambuc 	/* much like basename, but guarantees to not modify the path */
431433d6423SLionel Sambuc 	name = path;
432433d6423SLionel Sambuc 	for (p = path; *p; p++) {
433433d6423SLionel Sambuc 		if (*p == '/') name = p + 1;
434433d6423SLionel Sambuc 	}
435433d6423SLionel Sambuc 	return name;
436433d6423SLionel Sambuc }
437433d6423SLionel Sambuc 
compare_binaries(const void * p1,const void * p2)438433d6423SLionel Sambuc static int compare_binaries(const void *p1, const void *p2) {
439433d6423SLionel Sambuc 	const struct binary_info *const *b1 = p1, *const *b2 = p2;
440433d6423SLionel Sambuc 
441433d6423SLionel Sambuc 	/* binaries with more samples come first */
442433d6423SLionel Sambuc 	assert(b1);
443433d6423SLionel Sambuc 	assert(b2);
444433d6423SLionel Sambuc 	assert(*b1);
445433d6423SLionel Sambuc 	assert(*b2);
446433d6423SLionel Sambuc 	if ((*b1)->samples > (*b2)->samples) return -1;
447433d6423SLionel Sambuc 	if ((*b1)->samples < (*b2)->samples) return 1;
448433d6423SLionel Sambuc 	return 0;
449433d6423SLionel Sambuc }
450433d6423SLionel Sambuc 
compare_symbols(const void * p1,const void * p2)451433d6423SLionel Sambuc static int compare_symbols(const void *p1, const void *p2) {
452433d6423SLionel Sambuc 	const struct symbol_count *const *s1 = p1, *const *s2 = p2;
453433d6423SLionel Sambuc 
454433d6423SLionel Sambuc 	/* symbols with more samples come first */
455433d6423SLionel Sambuc 	assert(s1);
456433d6423SLionel Sambuc 	assert(s2);
457433d6423SLionel Sambuc 	assert(*s1);
458433d6423SLionel Sambuc 	assert(*s2);
459433d6423SLionel Sambuc 	if ((*s1)->samples > (*s2)->samples) return -1;
460433d6423SLionel Sambuc 	if ((*s1)->samples < (*s2)->samples) return 1;
461433d6423SLionel Sambuc 	return 0;
462433d6423SLionel Sambuc }
463433d6423SLionel Sambuc 
count_symbols(const struct binary_info * binary,int threshold)464433d6423SLionel Sambuc static int count_symbols(const struct binary_info *binary, int threshold) {
465433d6423SLionel Sambuc 	struct symbol_count *symbol;
466433d6423SLionel Sambuc 	int result = 0;
467433d6423SLionel Sambuc 
468433d6423SLionel Sambuc 	for (symbol = binary->symbols; symbol; symbol = symbol->next) {
469433d6423SLionel Sambuc 		if (symbol->samples >= threshold) result++;
470433d6423SLionel Sambuc 	}
471433d6423SLionel Sambuc 	return result;
472433d6423SLionel Sambuc }
473433d6423SLionel Sambuc 
dprint_symbols(const struct binary_info * binary)474433d6423SLionel Sambuc static void dprint_symbols(const struct binary_info *binary) {
475433d6423SLionel Sambuc #if DEBUG
476433d6423SLionel Sambuc 	const struct symbol_count *symbol;
477433d6423SLionel Sambuc 
478433d6423SLionel Sambuc 	for (symbol = binary->symbols; symbol; symbol = symbol->next) {
479433d6423SLionel Sambuc 		dprintf("addr=0x%.8lx samples=%8d name=\"%.*s\"\n",
480433d6423SLionel Sambuc 			(unsigned long) symbol->addr, symbol->samples,
481433d6423SLionel Sambuc 			SYMBOL_NAME_SIZE, symbol->name);
482433d6423SLionel Sambuc 	}
483433d6423SLionel Sambuc #endif
484433d6423SLionel Sambuc }
485433d6423SLionel Sambuc 
endpoint_hashtab_get_ptr(endpoint_t endpoint)486433d6423SLionel Sambuc static struct endpoint_info **endpoint_hashtab_get_ptr(endpoint_t endpoint) {
487433d6423SLionel Sambuc 	struct endpoint_info *epinfo, **ptr;
488433d6423SLionel Sambuc 
489433d6423SLionel Sambuc 	/* get pointer to location of the binary in hash table */
490433d6423SLionel Sambuc 	ptr = &endpoint_hashtab[(unsigned) endpoint % ENDPOINT_HASHTAB_SIZE];
491433d6423SLionel Sambuc 	while ((epinfo = *ptr) && epinfo->endpoint != endpoint) {
492433d6423SLionel Sambuc 		ptr = &epinfo->hashtab_next;
493433d6423SLionel Sambuc 	}
494433d6423SLionel Sambuc 	dprintf("looked up endpoint %ld in hash table, %sfound\n",
495433d6423SLionel Sambuc 		(long) endpoint, *ptr ? "" : "not ");
496433d6423SLionel Sambuc 	return ptr;
497433d6423SLionel Sambuc }
498433d6423SLionel Sambuc 
load_trace(const char * path)499433d6423SLionel Sambuc static void load_trace(const char *path) {
500433d6423SLionel Sambuc 	char buffer[1024];
501433d6423SLionel Sambuc 	size_t bufindex, bufsize;
502433d6423SLionel Sambuc 	FILE *file;
503433d6423SLionel Sambuc 	unsigned size_info, size_sample, size_proc;
504433d6423SLionel Sambuc 	int samples_read;
505433d6423SLionel Sambuc 	static struct sprof_info_s sprof_info_perfile;
506433d6423SLionel Sambuc 
507433d6423SLionel Sambuc 	/* open trace file */
508433d6423SLionel Sambuc 	file = fopen(path, "rb");
509433d6423SLionel Sambuc 	if (!file) {
510433d6423SLionel Sambuc 		fprintf(stderr, "error: cannot open trace file \"%s\": %s\n",
511433d6423SLionel Sambuc 			path, strerror(errno));
512433d6423SLionel Sambuc 		exit(1);
513433d6423SLionel Sambuc 	}
514433d6423SLionel Sambuc 
515433d6423SLionel Sambuc 	/* check file format and update totals */
516433d6423SLionel Sambuc 	if (fscanf(file, "stat\n%u %u %u",
517433d6423SLionel Sambuc 		&size_info, &size_sample, &size_proc) != 3 ||
518433d6423SLionel Sambuc 		fgetc(file) != '\n') {
519433d6423SLionel Sambuc 		fprintf(stderr, "error: file \"%s\" does not contain an "
520433d6423SLionel Sambuc 			"sprofile trace\n", path);
521433d6423SLionel Sambuc 		exit(1);
522433d6423SLionel Sambuc 	}
523433d6423SLionel Sambuc 	if ((size_info != sizeof(struct sprof_info_s)) ||
524433d6423SLionel Sambuc 		(size_sample != sizeof(struct sprof_sample)) ||
525433d6423SLionel Sambuc 		(size_proc != sizeof(struct sprof_proc))) {
526433d6423SLionel Sambuc 		fprintf(stderr, "error: file \"%s\" is incompatible with this "
527433d6423SLionel Sambuc 			"version of sprofalyze; recompile sprofalyze with the "
528433d6423SLionel Sambuc 			"MINIX version that created this file\n", path);
529433d6423SLionel Sambuc 		exit(1);
530433d6423SLionel Sambuc 	}
531433d6423SLionel Sambuc 	if (fread(&sprof_info_perfile, sizeof(sprof_info_perfile), 1, file) != 1) {
532433d6423SLionel Sambuc 		fprintf(stderr, "error: totals missing in file \"%s\"\n", path);
533433d6423SLionel Sambuc 		exit(1);
534433d6423SLionel Sambuc 	}
535433d6423SLionel Sambuc 
536433d6423SLionel Sambuc 	/* read and store samples */
537433d6423SLionel Sambuc 	samples_read = 0;
538433d6423SLionel Sambuc 	bufindex = 0;
539433d6423SLionel Sambuc 	bufsize = 0;
540433d6423SLionel Sambuc 	for (;;) {
541433d6423SLionel Sambuc 		/* enough left in the buffer? */
542433d6423SLionel Sambuc 		if (bufsize - bufindex < sizeof(union sprof_record)) {
543433d6423SLionel Sambuc 			/* not enough, read some more */
544433d6423SLionel Sambuc 			memmove(buffer, buffer + bufindex, bufsize - bufindex);
545433d6423SLionel Sambuc 			bufsize -= bufindex;
546433d6423SLionel Sambuc 			bufindex = 0;
547433d6423SLionel Sambuc 			bufsize += fread(buffer + bufsize, 1,
548433d6423SLionel Sambuc 				sizeof(buffer) - bufsize, file);
549433d6423SLionel Sambuc 
550433d6423SLionel Sambuc 			/* are we done? */
551433d6423SLionel Sambuc 			if (bufsize == 0) break;
552433d6423SLionel Sambuc 		}
553433d6423SLionel Sambuc 
554433d6423SLionel Sambuc 		/* process sample record (either struct sprof_sample or
555433d6423SLionel Sambuc 		 * struct sprof_proc)
556433d6423SLionel Sambuc 		 */
557433d6423SLionel Sambuc 		bufindex += sample_process(
558433d6423SLionel Sambuc 			(const union sprof_record *) (buffer + bufindex),
559433d6423SLionel Sambuc 			bufsize - bufindex, &samples_read);
560433d6423SLionel Sambuc 	}
561433d6423SLionel Sambuc 	if (samples_read != sprof_info_perfile.system_samples) {
562433d6423SLionel Sambuc 		fprintf(stderr, "warning: number of system samples (%d) in "
563433d6423SLionel Sambuc 			"\"%s\" does not match number of records (%d)\n",
564433d6423SLionel Sambuc 			sprof_info.system_samples, path, samples_read);
565433d6423SLionel Sambuc 	}
566433d6423SLionel Sambuc 
567433d6423SLionel Sambuc 	sprof_info.system_samples += sprof_info_perfile.system_samples;
568433d6423SLionel Sambuc 	sprof_info.total_samples += sprof_info_perfile.total_samples;
569433d6423SLionel Sambuc 	sprof_info.idle_samples += sprof_info_perfile.idle_samples;
570433d6423SLionel Sambuc 	sprof_info.user_samples += sprof_info_perfile.user_samples;
571433d6423SLionel Sambuc 
572433d6423SLionel Sambuc 	fclose(file);
573433d6423SLionel Sambuc }
574433d6423SLionel Sambuc 
malloc_checked(size_t size)575433d6423SLionel Sambuc static void *malloc_checked(size_t size) {
576433d6423SLionel Sambuc 	void *p;
577433d6423SLionel Sambuc 	if (!size) return NULL;
578433d6423SLionel Sambuc 	p = malloc(size);
579433d6423SLionel Sambuc 	if (!p) {
580433d6423SLionel Sambuc 		fprintf(stderr, "error: malloc cannot allocate %lu bytes: %s\n",
581433d6423SLionel Sambuc 			(unsigned long) size, strerror(errno));
582433d6423SLionel Sambuc 		exit(-1);
583433d6423SLionel Sambuc 	}
584433d6423SLionel Sambuc 	return p;
585433d6423SLionel Sambuc }
586433d6423SLionel Sambuc 
name_hash(const char * name)587433d6423SLionel Sambuc static unsigned name_hash(const char *name) {
588433d6423SLionel Sambuc 	int i;
589433d6423SLionel Sambuc 	unsigned r = 0;
590433d6423SLionel Sambuc 
591433d6423SLionel Sambuc 	/* remember: strncpy initializes all bytes */
592433d6423SLionel Sambuc 	for (i = 0; i < PROC_NAME_LEN && name[i]; i++) {
593433d6423SLionel Sambuc 		r = r * 31 + name[i];
594433d6423SLionel Sambuc 	}
595433d6423SLionel Sambuc 	dprintf("name_hash(\"%.*s\") = 0x%.8x\n", PROC_NAME_LEN, name, r);
596433d6423SLionel Sambuc 	return r;
597433d6423SLionel Sambuc }
598433d6423SLionel Sambuc 
percent(int value,int percent_of)599433d6423SLionel Sambuc static float percent(int value, int percent_of) {
600433d6423SLionel Sambuc 	assert(value >= 0);
601433d6423SLionel Sambuc 	assert(value <= percent_of);
602433d6423SLionel Sambuc 
603433d6423SLionel Sambuc 	return (percent_of > 0) ? (value * 100.0 / percent_of) : 0;
604433d6423SLionel Sambuc }
605433d6423SLionel Sambuc 
print_diff(void)606433d6423SLionel Sambuc static void print_diff(void) {
607433d6423SLionel Sambuc 	const struct binary_info *binary;
608433d6423SLionel Sambuc 	int binary_samples;
609433d6423SLionel Sambuc 	const struct symbol_count *symbol;
610433d6423SLionel Sambuc 
611433d6423SLionel Sambuc 	/* print out aggregates in a machine-readable format for sprofdiff */
612433d6423SLionel Sambuc 	printf("(total)\t\t%d\n", sprof_info.total_samples);
613433d6423SLionel Sambuc 	printf("(system)\t\t%d\n", sprof_info.system_samples);
614433d6423SLionel Sambuc 	printf("(idle)\t\t%d\n", sprof_info.idle_samples);
615433d6423SLionel Sambuc 	printf("(user)\t\t%d\n", sprof_info.user_samples);
616433d6423SLionel Sambuc 	for (binary = binaries; binary; binary = binary->next) {
617433d6423SLionel Sambuc 		binary_samples = 0;
618433d6423SLionel Sambuc 		for (symbol = binary->symbols; symbol; symbol = symbol->next) {
619433d6423SLionel Sambuc 			if (symbol->samples) {
620433d6423SLionel Sambuc 				printf("%.*s\t%.*s\t%d\n",
621433d6423SLionel Sambuc 					PROC_NAME_LEN, binary->name,
622433d6423SLionel Sambuc 					SYMBOL_NAME_SIZE, symbol->name,
623433d6423SLionel Sambuc 					symbol->samples);
624433d6423SLionel Sambuc 			}
625433d6423SLionel Sambuc 			binary_samples += symbol->samples;
626433d6423SLionel Sambuc 		}
627433d6423SLionel Sambuc 		printf("%.*s\t(total)\t%d\n",
628433d6423SLionel Sambuc 			PROC_NAME_LEN, binary->name,
629433d6423SLionel Sambuc 			binary_samples);
630433d6423SLionel Sambuc 	}
631433d6423SLionel Sambuc }
632433d6423SLionel Sambuc 
print_report(void)633433d6423SLionel Sambuc static void print_report(void) {
634433d6423SLionel Sambuc 	/* print out human-readable analysis */
635433d6423SLionel Sambuc 	printf("Showing processes and functions using at least %3.0f%% "
636433d6423SLionel Sambuc 		"time.\n\n", minimum_perc);
637433d6423SLionel Sambuc 	printf("  System process ticks: %10d (%3.0f%%)\n",
638433d6423SLionel Sambuc 		sprof_info.system_samples, percent(sprof_info.system_samples, sprof_info.total_samples));
639433d6423SLionel Sambuc 	printf("    User process ticks: %10d (%3.0f%%)          "
640433d6423SLionel Sambuc 		"Details of system process\n",
641433d6423SLionel Sambuc 		sprof_info.user_samples, percent(sprof_info.user_samples, sprof_info.total_samples));
642433d6423SLionel Sambuc 	printf("       Idle time ticks: %10d (%3.0f%%)          "
643433d6423SLionel Sambuc 		"samples, aggregated and\n",
644433d6423SLionel Sambuc 		sprof_info.idle_samples, percent(sprof_info.idle_samples, sprof_info.total_samples));
645433d6423SLionel Sambuc 	printf("                        ----------  ----           "
646433d6423SLionel Sambuc 		"per process, are below.\n");
647433d6423SLionel Sambuc 	printf("           Total ticks: %10d (100%%)\n\n", sprof_info.total_samples);
648433d6423SLionel Sambuc 
649433d6423SLionel Sambuc 	print_report_overall();
650433d6423SLionel Sambuc 	print_reports_per_binary();
651433d6423SLionel Sambuc }
652433d6423SLionel Sambuc 
print_report_overall(void)653433d6423SLionel Sambuc static void print_report_overall(void) {
654433d6423SLionel Sambuc 	struct binary_info *binary;
655433d6423SLionel Sambuc 	struct symbol_count *symbol, **symbols_sorted;
656433d6423SLionel Sambuc 	unsigned index, symbol_count;
657433d6423SLionel Sambuc 	int sample_threshold;
658433d6423SLionel Sambuc 
659433d6423SLionel Sambuc 	/* count number of symbols to display */
660433d6423SLionel Sambuc 	sample_threshold = sprof_info.system_samples * minimum_perc / 100;
661433d6423SLionel Sambuc 	symbol_count = 0;
662433d6423SLionel Sambuc 	for (binary = binaries; binary; binary = binary->next) {
663433d6423SLionel Sambuc 		symbol_count += count_symbols(binary, sample_threshold);
664433d6423SLionel Sambuc 	}
665433d6423SLionel Sambuc 
666433d6423SLionel Sambuc 	/* sort symbols by decreasing number of samples */
667433d6423SLionel Sambuc 	symbols_sorted = MALLOC_CHECKED(struct symbol_count *, symbol_count);
668433d6423SLionel Sambuc 	index = 0;
669433d6423SLionel Sambuc 	for (binary = binaries; binary; binary = binary->next) {
670433d6423SLionel Sambuc 		for (symbol = binary->symbols; symbol; symbol = symbol->next) {
671433d6423SLionel Sambuc 			if (symbol->samples >= sample_threshold) {
672433d6423SLionel Sambuc 				symbols_sorted[index++] = symbol;
673433d6423SLionel Sambuc 			}
674433d6423SLionel Sambuc 		}
675433d6423SLionel Sambuc 	}
676433d6423SLionel Sambuc 	assert(index == symbol_count);
677433d6423SLionel Sambuc 	qsort(symbols_sorted, symbol_count, sizeof(symbols_sorted[0]),
678433d6423SLionel Sambuc 		compare_symbols);
679433d6423SLionel Sambuc 
680433d6423SLionel Sambuc 	/* report most common symbols overall */
681433d6423SLionel Sambuc 	print_separator();
682433d6423SLionel Sambuc 	printf("Total system process time %*d samples\n",
683433d6423SLionel Sambuc 		LINE_WIDTH - 34, sprof_info.system_samples);
684433d6423SLionel Sambuc 	print_separator();
685433d6423SLionel Sambuc 	print_report_symbols(symbols_sorted, symbol_count, sprof_info.system_samples, 1);
686433d6423SLionel Sambuc 	free(symbols_sorted);
687433d6423SLionel Sambuc }
688433d6423SLionel Sambuc 
print_report_per_binary(const struct binary_info * binary)689433d6423SLionel Sambuc static void print_report_per_binary(const struct binary_info *binary) {
690433d6423SLionel Sambuc 	struct symbol_count *symbol, **symbols_sorted;
691433d6423SLionel Sambuc 	unsigned index, symbol_count;
692433d6423SLionel Sambuc 	int sample_threshold;
693433d6423SLionel Sambuc 
694433d6423SLionel Sambuc 	assert(binary->samples >= 0);
695433d6423SLionel Sambuc 
696433d6423SLionel Sambuc 	/* count number of symbols to display */
697433d6423SLionel Sambuc 	sample_threshold = binary->samples * minimum_perc / 100;
698433d6423SLionel Sambuc 	symbol_count = count_symbols(binary, sample_threshold);
699433d6423SLionel Sambuc 
700433d6423SLionel Sambuc 	/* sort symbols by decreasing number of samples */
701433d6423SLionel Sambuc 	symbols_sorted = MALLOC_CHECKED(struct symbol_count *, symbol_count);
702433d6423SLionel Sambuc 	index = 0;
703433d6423SLionel Sambuc 	for (symbol = binary->symbols; symbol; symbol = symbol->next) {
704433d6423SLionel Sambuc 		if (symbol->samples >= sample_threshold) {
705433d6423SLionel Sambuc 			symbols_sorted[index++] = symbol;
706433d6423SLionel Sambuc 		}
707433d6423SLionel Sambuc 	}
708433d6423SLionel Sambuc 	assert(index == symbol_count);
709433d6423SLionel Sambuc 	qsort(symbols_sorted, symbol_count, sizeof(symbols_sorted[0]),
710433d6423SLionel Sambuc 		compare_symbols);
711433d6423SLionel Sambuc 
712433d6423SLionel Sambuc 	/* report most common symbols for this binary */
713433d6423SLionel Sambuc 	print_separator();
714433d6423SLionel Sambuc 	printf("%-*.*s %4.1f%% of system process samples\n",
715433d6423SLionel Sambuc 		LINE_WIDTH - 32, PROC_NAME_LEN, binary->name,
716433d6423SLionel Sambuc 		percent(binary->samples, sprof_info.system_samples));
717433d6423SLionel Sambuc 	print_separator();
718433d6423SLionel Sambuc 	print_report_symbols(symbols_sorted, symbol_count, binary->samples, 0);
719433d6423SLionel Sambuc 	free(symbols_sorted);
720433d6423SLionel Sambuc }
721433d6423SLionel Sambuc 
print_reports_per_binary(void)722433d6423SLionel Sambuc static void print_reports_per_binary(void) {
723433d6423SLionel Sambuc 	struct binary_info *binary, **binaries_sorted;
724433d6423SLionel Sambuc 	unsigned binary_count, i, index;
725433d6423SLionel Sambuc 	int sample_threshold, samples_shown;
726433d6423SLionel Sambuc 	struct symbol_count *symbol;
727433d6423SLionel Sambuc 
728433d6423SLionel Sambuc 	/* count total per-binary samples */
729433d6423SLionel Sambuc 	binary_count = 0;
730433d6423SLionel Sambuc 	for (binary = binaries; binary; binary = binary->next) {
731433d6423SLionel Sambuc 		assert(!binary->samples);
732433d6423SLionel Sambuc 		for (symbol = binary->symbols; symbol; symbol = symbol->next) {
733433d6423SLionel Sambuc 			binary->samples += symbol->samples;
734433d6423SLionel Sambuc 		}
735433d6423SLionel Sambuc 		binary_count++;
736433d6423SLionel Sambuc 	}
737433d6423SLionel Sambuc 
738433d6423SLionel Sambuc 	/* sort binaries by decreasing number of samples */
739433d6423SLionel Sambuc 	binaries_sorted = MALLOC_CHECKED(struct binary_info *, binary_count);
740433d6423SLionel Sambuc 	index = 0;
741433d6423SLionel Sambuc 	for (binary = binaries; binary; binary = binary->next) {
742433d6423SLionel Sambuc 		binaries_sorted[index++] = binary;
743433d6423SLionel Sambuc 	}
744433d6423SLionel Sambuc 	assert(index == binary_count);
745433d6423SLionel Sambuc 	qsort(binaries_sorted, binary_count, sizeof(binaries_sorted[0]),
746433d6423SLionel Sambuc 		compare_binaries);
747433d6423SLionel Sambuc 
748433d6423SLionel Sambuc 	/* display reports for binaries with enough samples */
749433d6423SLionel Sambuc 	sample_threshold = sprof_info.system_samples * minimum_perc / 100;
750433d6423SLionel Sambuc 	samples_shown = 0;
751433d6423SLionel Sambuc 	for (i = 0; i < binary_count; i++) {
752433d6423SLionel Sambuc 		if (binaries_sorted[i]->samples < sample_threshold) break;
753433d6423SLionel Sambuc 		print_report_per_binary(binaries_sorted[i]);
754433d6423SLionel Sambuc 		samples_shown += binaries_sorted[i]->samples;
755433d6423SLionel Sambuc 	}
756433d6423SLionel Sambuc 	print_separator();
757433d6423SLionel Sambuc 	printf("samples: %d shown: %d\n", sprof_info.system_samples, samples_shown);
758433d6423SLionel Sambuc 	printf("processes <%3.0f%% (not showing functions) %*.1f%% of system "
759433d6423SLionel Sambuc 		"process samples\n", minimum_perc, LINE_WIDTH - 67,
760433d6423SLionel Sambuc 		percent(sprof_info.system_samples - samples_shown, sprof_info.system_samples));
761433d6423SLionel Sambuc 	print_separator();
762433d6423SLionel Sambuc 
763433d6423SLionel Sambuc 	free(binaries_sorted);
764433d6423SLionel Sambuc }
765433d6423SLionel Sambuc 
print_report_symbols(struct symbol_count ** symbols,unsigned symbol_count,int total,int show_process)766433d6423SLionel Sambuc static void print_report_symbols(struct symbol_count **symbols,
767433d6423SLionel Sambuc 	unsigned symbol_count, int total, int show_process) {
768433d6423SLionel Sambuc 	unsigned bar_dots, bar_width, i, j, process_width;
769433d6423SLionel Sambuc 	int samples, samples_shown;
770433d6423SLionel Sambuc 	struct symbol_count *symbol;
771433d6423SLionel Sambuc 
772433d6423SLionel Sambuc 	/* find out how much space we have available */
773433d6423SLionel Sambuc 	process_width = show_process ? (PROC_NAME_LEN + 1) : 0;
774433d6423SLionel Sambuc 	bar_width = LINE_WIDTH - process_width - SYMBOL_NAME_WIDTH - 17;
775433d6423SLionel Sambuc 
776433d6423SLionel Sambuc 	/* print the symbol lines */
777433d6423SLionel Sambuc 	samples_shown = 0;
778433d6423SLionel Sambuc 	for (i = 0; i <= symbol_count; i++) {
779433d6423SLionel Sambuc 		if (i < symbol_count) {
780433d6423SLionel Sambuc 			/* first list the symbols themselves */
781433d6423SLionel Sambuc 			symbol = symbols[i];
782433d6423SLionel Sambuc 			printf("%*.*s %*.*s ",
783433d6423SLionel Sambuc 				process_width,
784433d6423SLionel Sambuc 				show_process ? PROC_NAME_LEN : 0,
785433d6423SLionel Sambuc 				symbol->binary->name,
786433d6423SLionel Sambuc 				SYMBOL_NAME_WIDTH,
787433d6423SLionel Sambuc 				SYMBOL_NAME_WIDTH,
788433d6423SLionel Sambuc 				symbol->name);
789433d6423SLionel Sambuc 			samples = symbol->samples;
790433d6423SLionel Sambuc 		} else {
791433d6423SLionel Sambuc 			/* at the end, list the remainder */
792433d6423SLionel Sambuc 			printf("%*s<%3.0f%% ",
793433d6423SLionel Sambuc 				process_width + SYMBOL_NAME_WIDTH - 4,
794433d6423SLionel Sambuc 				"",
795433d6423SLionel Sambuc 				minimum_perc);
796433d6423SLionel Sambuc 			samples = total - samples_shown;
797433d6423SLionel Sambuc 		}
798433d6423SLionel Sambuc 		assert(samples >= 0);
799433d6423SLionel Sambuc 		assert(samples <= total);
800433d6423SLionel Sambuc 		bar_dots = (total > 0) ? (samples * bar_width / total) : 0;
801433d6423SLionel Sambuc 		for (j = 0; j < bar_dots; j++) printf("*");
802433d6423SLionel Sambuc 		for (; j < bar_width; j++) printf(" ");
803433d6423SLionel Sambuc 		printf("%8d %5.1f%%\n", samples, percent(samples, total));
804433d6423SLionel Sambuc 		samples_shown += samples;
805433d6423SLionel Sambuc 	}
806433d6423SLionel Sambuc 
807433d6423SLionel Sambuc 	/* print remainder and summary */
808433d6423SLionel Sambuc 	print_separator();
809433d6423SLionel Sambuc 	printf("%-*.*s%*d 100.0%%\n\n", PROC_NAME_LEN, PROC_NAME_LEN,
810433d6423SLionel Sambuc 		(show_process || symbol_count == 0) ?
811433d6423SLionel Sambuc 		"total" : symbols[0]->binary->name,
812433d6423SLionel Sambuc 		LINE_WIDTH - PROC_NAME_LEN - 7, total);
813433d6423SLionel Sambuc }
814433d6423SLionel Sambuc 
print_separator(void)815433d6423SLionel Sambuc static void print_separator(void) {
816433d6423SLionel Sambuc 	int i;
817433d6423SLionel Sambuc 	for (i = 0; i < LINE_WIDTH; i++) printf("-");
818433d6423SLionel Sambuc 	printf("\n");
819433d6423SLionel Sambuc }
820433d6423SLionel Sambuc 
read_hex(FILE * file,unsigned long * value)821433d6423SLionel Sambuc static int read_hex(FILE *file, unsigned long *value) {
822433d6423SLionel Sambuc 	int c, cvalue;
823433d6423SLionel Sambuc 	unsigned index;
824433d6423SLionel Sambuc 
825433d6423SLionel Sambuc 	assert(file);
826433d6423SLionel Sambuc 	assert(value);
827433d6423SLionel Sambuc 
828433d6423SLionel Sambuc 	index = 0;
829433d6423SLionel Sambuc 	c = fgetc(file);
830433d6423SLionel Sambuc 	*value = 0;
831433d6423SLionel Sambuc 	while (index < 8) {
832433d6423SLionel Sambuc 		if (c >= '0' && c <= '9') {
833433d6423SLionel Sambuc 			cvalue = c - '0';
834433d6423SLionel Sambuc 		} else if (c >= 'A' && c <= 'F') {
835433d6423SLionel Sambuc 			cvalue = c - 'A' + 10;
836433d6423SLionel Sambuc 		} else if (c >= 'a' && c <= 'f') {
837433d6423SLionel Sambuc 			cvalue = c - 'a' + 10;
838433d6423SLionel Sambuc 		} else {
839433d6423SLionel Sambuc 			break;
840433d6423SLionel Sambuc 		}
841433d6423SLionel Sambuc 
842433d6423SLionel Sambuc 		*value = *value * 16 + cvalue;
843433d6423SLionel Sambuc 		index++;
844433d6423SLionel Sambuc 		c = fgetc(file);
845433d6423SLionel Sambuc 	}
846433d6423SLionel Sambuc 	if (c != EOF) ungetc(c, file);
847433d6423SLionel Sambuc 
848433d6423SLionel Sambuc 	return index;
849433d6423SLionel Sambuc }
850433d6423SLionel Sambuc 
read_newline(FILE * file)851433d6423SLionel Sambuc static int read_newline(FILE *file) {
852433d6423SLionel Sambuc 	int c;
853433d6423SLionel Sambuc 
854433d6423SLionel Sambuc 	do {
855433d6423SLionel Sambuc 		c = fgetc(file);
856433d6423SLionel Sambuc 	} while (c != EOF && c != '\n' && isspace(c));
857433d6423SLionel Sambuc 	if (c == EOF || c == '\n') return 1;
858433d6423SLionel Sambuc 	ungetc(c, file);
859433d6423SLionel Sambuc 	return 0;
860433d6423SLionel Sambuc }
861433d6423SLionel Sambuc 
read_nm_line(FILE * file,int line,char * name,char * type,unsigned long * addr,unsigned long * size)862433d6423SLionel Sambuc static void read_nm_line(FILE *file, int line, char *name, char *type,
863433d6423SLionel Sambuc 	unsigned long *addr, unsigned long *size) {
864433d6423SLionel Sambuc 
865433d6423SLionel Sambuc 	assert(file);
866433d6423SLionel Sambuc 	assert(name);
867433d6423SLionel Sambuc 	assert(type);
868433d6423SLionel Sambuc 	assert(addr);
869433d6423SLionel Sambuc 	assert(size);
870433d6423SLionel Sambuc 	*type = 0;
871433d6423SLionel Sambuc 	*addr = 0;
872433d6423SLionel Sambuc 	*size = 0;
873433d6423SLionel Sambuc 	if (read_newline(file)) {
874433d6423SLionel Sambuc 		memset(name, 0, SYMBOL_NAME_SIZE);
875433d6423SLionel Sambuc 		return;
876433d6423SLionel Sambuc 	}
877433d6423SLionel Sambuc 
878433d6423SLionel Sambuc 	/* name and type are compulsory */
879433d6423SLionel Sambuc 	read_to_whitespace(file, name, SYMBOL_NAME_SIZE);
880433d6423SLionel Sambuc 	if (read_newline(file)) {
881433d6423SLionel Sambuc 		fprintf(stderr, "error: bad output format from nm: "
882433d6423SLionel Sambuc 			"symbol type missing on line %d\n", line);
883433d6423SLionel Sambuc 		exit(-1);
884433d6423SLionel Sambuc 	}
885433d6423SLionel Sambuc 	*type = fgetc(file);
886433d6423SLionel Sambuc 
887433d6423SLionel Sambuc 	/* address is optional */
888433d6423SLionel Sambuc 	if (read_newline(file)) return;
889433d6423SLionel Sambuc 	if (!read_hex(file, addr)) {
890433d6423SLionel Sambuc 		fprintf(stderr, "error: bad output format from nm: junk where "
891433d6423SLionel Sambuc 			"address should be on line %d\n", line);
892433d6423SLionel Sambuc 		exit(-1);
893433d6423SLionel Sambuc 	}
894433d6423SLionel Sambuc 
895433d6423SLionel Sambuc 	/* size is optional */
896433d6423SLionel Sambuc 	if (read_newline(file)) return;
897433d6423SLionel Sambuc 	if (!read_hex(file, size)) {
898433d6423SLionel Sambuc 		fprintf(stderr, "error: bad output format from nm: junk where "
899433d6423SLionel Sambuc 			"size should be on line %d\n", line);
900433d6423SLionel Sambuc 		exit(-1);
901433d6423SLionel Sambuc 	}
902433d6423SLionel Sambuc 
903433d6423SLionel Sambuc 	/* nothing else expected */
904433d6423SLionel Sambuc 	if (read_newline(file)) return;
905433d6423SLionel Sambuc 	fprintf(stderr, "error: bad output format from nm: junk after size "
906433d6423SLionel Sambuc 		"on line %d\n", line);
907433d6423SLionel Sambuc 	exit(-1);
908433d6423SLionel Sambuc }
909433d6423SLionel Sambuc 
read_to_whitespace(FILE * file,char * buffer,size_t size)910433d6423SLionel Sambuc static void read_to_whitespace(FILE *file, char *buffer, size_t size) {
911433d6423SLionel Sambuc 	int c;
912433d6423SLionel Sambuc 
913433d6423SLionel Sambuc 	/* read up to and incl first whitespace, store at most size chars */
914433d6423SLionel Sambuc 	while ((c = fgetc(file)) != EOF && !isspace(c)) {
915433d6423SLionel Sambuc 		if (size > 0) {
916433d6423SLionel Sambuc 			*(buffer++) = c;
917433d6423SLionel Sambuc 			size--;
918433d6423SLionel Sambuc 		}
919433d6423SLionel Sambuc 	}
920433d6423SLionel Sambuc 	if (size > 0) *buffer = 0;
921433d6423SLionel Sambuc }
922433d6423SLionel Sambuc 
sample_process(const union sprof_record * data,size_t size,int * samples_read)923433d6423SLionel Sambuc static size_t sample_process(const union sprof_record *data, size_t size,
924433d6423SLionel Sambuc 	int *samples_read) {
925433d6423SLionel Sambuc 	struct endpoint_info *epinfo, **ptr;
926433d6423SLionel Sambuc 
927433d6423SLionel Sambuc 	assert(data);
928433d6423SLionel Sambuc 	assert(samples_read);
929433d6423SLionel Sambuc 
930433d6423SLionel Sambuc 	/* do we have a proper sample? */
931433d6423SLionel Sambuc 	if (size < sizeof(data->proc) && size < sizeof(data->sample)) {
932433d6423SLionel Sambuc 		goto error;
933433d6423SLionel Sambuc 	}
934433d6423SLionel Sambuc 
935433d6423SLionel Sambuc 	/* do we know this endpoint? */
936433d6423SLionel Sambuc 	ptr = endpoint_hashtab_get_ptr(data->proc.proc);
937433d6423SLionel Sambuc 	if ((epinfo = *ptr)) {
938433d6423SLionel Sambuc 		if (!epinfo->seen) {
939433d6423SLionel Sambuc 			epinfo->seen = 1;
940433d6423SLionel Sambuc 			return sizeof(data->proc);
941433d6423SLionel Sambuc 		}
942433d6423SLionel Sambuc 
943433d6423SLionel Sambuc 		/* endpoint known, store sample */
944433d6423SLionel Sambuc 		if (size < sizeof(data->sample)) goto error;
945433d6423SLionel Sambuc 		sample_store(epinfo->binary, &data->sample);
946433d6423SLionel Sambuc 		(*samples_read)++;
947433d6423SLionel Sambuc 		return sizeof(data->sample);
948433d6423SLionel Sambuc 	}
949433d6423SLionel Sambuc 
950433d6423SLionel Sambuc 	/* endpoint not known, add it */
951433d6423SLionel Sambuc 	*ptr = epinfo = MALLOC_CHECKED(struct endpoint_info, 1);
952433d6423SLionel Sambuc 	memset(epinfo, 0, sizeof(struct endpoint_info));
953433d6423SLionel Sambuc 	epinfo->endpoint = data->proc.proc;
954433d6423SLionel Sambuc 	epinfo->seen = 1;
955433d6423SLionel Sambuc 	epinfo->next = endpoints;
956433d6423SLionel Sambuc 	endpoints = epinfo;
957433d6423SLionel Sambuc 
958433d6423SLionel Sambuc 	/* fetch binary based on process name in sample */
959433d6423SLionel Sambuc 	if (size < sizeof(data->proc)) goto error;
960433d6423SLionel Sambuc 	epinfo->binary = sample_load_binary(&data->proc);
961433d6423SLionel Sambuc 	return sizeof(data->proc);
962433d6423SLionel Sambuc 
963433d6423SLionel Sambuc error:
964433d6423SLionel Sambuc 	fprintf(stderr, "warning: partial sample at end of trace, "
965433d6423SLionel Sambuc 		"was the trace file truncated?\n");
966433d6423SLionel Sambuc 	return size;
967433d6423SLionel Sambuc }
968433d6423SLionel Sambuc 
sample_load_binary(const struct sprof_proc * sample)969433d6423SLionel Sambuc static struct binary_info *sample_load_binary(
970433d6423SLionel Sambuc 	const struct sprof_proc *sample) {
971433d6423SLionel Sambuc 	struct binary_info *binary;
972433d6423SLionel Sambuc 
973433d6423SLionel Sambuc 	/* locate binary */
974433d6423SLionel Sambuc 	binary = binary_find(sample->name);
975433d6423SLionel Sambuc 	if (!binary) {
976433d6423SLionel Sambuc 		fprintf(stderr, "warning: ignoring unknown binary \"%.*s\"\n",
977433d6423SLionel Sambuc 			PROC_NAME_LEN, sample->name);
978433d6423SLionel Sambuc 		fprintf(stderr, "         did you include this executable in "
979433d6423SLionel Sambuc 			"the configuration?\n");
980433d6423SLionel Sambuc 		fprintf(stderr, "         (use -b to add additional "
981433d6423SLionel Sambuc 			"binaries)\n");
982433d6423SLionel Sambuc 		return NULL;
983433d6423SLionel Sambuc 	}
984433d6423SLionel Sambuc 
985433d6423SLionel Sambuc 	/* load symbols if this hasn't been done yet */
986433d6423SLionel Sambuc 	if (!binary->pc_map) binary_load_pc_map(binary);
987433d6423SLionel Sambuc 
988433d6423SLionel Sambuc 	return binary;
989433d6423SLionel Sambuc }
990433d6423SLionel Sambuc 
sample_store(struct binary_info * binary,const struct sprof_sample * sample)991433d6423SLionel Sambuc static void sample_store(struct binary_info *binary,
992433d6423SLionel Sambuc 	const struct sprof_sample *sample) {
993433d6423SLionel Sambuc 	unsigned long index_l1;
994433d6423SLionel Sambuc 	struct pc_map_l2 *pc_map_l2;
995433d6423SLionel Sambuc 	struct symbol_count *symbol;
996433d6423SLionel Sambuc 
997433d6423SLionel Sambuc 	if (!binary || !binary->pc_map) return;
998433d6423SLionel Sambuc 
999433d6423SLionel Sambuc 	/* find the applicable symbol (two-level lookup) */
1000433d6423SLionel Sambuc 	index_l1 = (unsigned long) sample->pc / PC_MAP_L2_SIZE;
1001433d6423SLionel Sambuc 	assert(index_l1 < PC_MAP_L1_SIZE);
1002433d6423SLionel Sambuc 	pc_map_l2 = binary->pc_map->l1[index_l1];
1003433d6423SLionel Sambuc 	if (pc_map_l2) {
1004433d6423SLionel Sambuc 		symbol = pc_map_l2->l2[(unsigned long) sample->pc % PC_MAP_L2_SIZE];
1005433d6423SLionel Sambuc 	} else {
1006433d6423SLionel Sambuc 		symbol = NULL;
1007433d6423SLionel Sambuc 	}
1008433d6423SLionel Sambuc 
1009433d6423SLionel Sambuc 	if (symbol) {
1010433d6423SLionel Sambuc 		assert(symbol->samples >= 0);
1011433d6423SLionel Sambuc 		symbol->samples++;
1012433d6423SLionel Sambuc 		assert(symbol->samples >= 0);
1013433d6423SLionel Sambuc 	} else if (!binary->no_more_warnings) {
1014433d6423SLionel Sambuc 		fprintf(stderr, "warning: address 0x%lx not associated with a "
1015433d6423SLionel Sambuc 			"symbol\n", (unsigned long) sample->pc);
1016433d6423SLionel Sambuc 		fprintf(stderr, "         binary may not match the profiled "
1017433d6423SLionel Sambuc 			"version\n");
1018433d6423SLionel Sambuc 		fprintf(stderr, "         path: \"%s\"\n", binary->path);
1019433d6423SLionel Sambuc 		binary->no_more_warnings = 1;
1020433d6423SLionel Sambuc 		dprint_symbols(binary);
1021433d6423SLionel Sambuc 	}
1022433d6423SLionel Sambuc }
1023433d6423SLionel Sambuc 
strdup_checked(const char * s)1024433d6423SLionel Sambuc static char *strdup_checked(const char *s) {
1025433d6423SLionel Sambuc 	char *p;
1026433d6423SLionel Sambuc 	if (!s) return NULL;
1027433d6423SLionel Sambuc 	p = strdup(s);
1028433d6423SLionel Sambuc 	if (!p) {
1029433d6423SLionel Sambuc 		fprintf(stderr, "error: strdup failed: %s\n",
1030433d6423SLionel Sambuc 			strerror(errno));
1031433d6423SLionel Sambuc 		exit(-1);
1032433d6423SLionel Sambuc 	}
1033433d6423SLionel Sambuc 	return p;
1034433d6423SLionel Sambuc }
1035433d6423SLionel Sambuc 
usage(const char * argv0)1036433d6423SLionel Sambuc static void usage(const char *argv0) {
1037433d6423SLionel Sambuc 	printf("usage:\n");
1038433d6423SLionel Sambuc 	printf("  %s [-d] [-p percentage] [-s src-tree-path] "
1039433d6423SLionel Sambuc 		"[-b binary]... file...\n", argv0);
1040433d6423SLionel Sambuc 	printf("\n");
1041433d6423SLionel Sambuc 	printf("sprofalyze aggregates one or more sprofile traces and");
1042433d6423SLionel Sambuc 	printf(" reports where time was spent.\n");
1043433d6423SLionel Sambuc 	printf("\n");
1044433d6423SLionel Sambuc 	printf("arguments:\n");
1045433d6423SLionel Sambuc 	printf("-d generates output that can be compared using sprofdiff\n");
1046433d6423SLionel Sambuc 	printf("-p specifies the cut-off percentage below which binaries\n");
1047433d6423SLionel Sambuc 	printf("   and functions will not be displayed\n");
1048433d6423SLionel Sambuc 	printf("-s specifies the root of the source tree where sprofalyze\n");
1049433d6423SLionel Sambuc 	printf("   should search for unstripped binaries to extract symbols\n");
1050433d6423SLionel Sambuc 	printf("   from\n");
1051433d6423SLionel Sambuc 	printf("-b specifies an additional system binary in the trace that\n");
1052433d6423SLionel Sambuc 	printf("   is not in the source tree; may be specified multiple\n");
1053433d6423SLionel Sambuc 	printf("   times\n");
1054433d6423SLionel Sambuc 	exit(1);
1055433d6423SLionel Sambuc }
1056