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