perf tools: Consolidate symbol resolving across all tools
[deliverable/linux.git] / tools / perf / builtin-annotate.c
CommitLineData
8035e428
IM
1/*
2 * builtin-annotate.c
3 *
4 * Builtin annotate command: Analyze the perf.data input file,
5 * look up and read DSOs and symbol information and display
6 * a histogram of results, along various sorting keys.
7 */
8#include "builtin.h"
9
10#include "util/util.h"
11
12#include "util/color.h"
5da50258 13#include <linux/list.h>
8035e428 14#include "util/cache.h"
43cbcd8a 15#include <linux/rbtree.h>
8035e428
IM
16#include "util/symbol.h"
17#include "util/string.h"
18
19#include "perf.h"
8f28827a 20#include "util/debug.h"
8035e428 21
62daacb5 22#include "util/event.h"
8035e428
IM
23#include "util/parse-options.h"
24#include "util/parse-events.h"
6baa0a5a 25#include "util/thread.h"
dd68ada2 26#include "util/sort.h"
3d1d07ec 27#include "util/hist.h"
8035e428 28
8035e428 29static char const *input_name = "perf.data";
8035e428 30
fa6963b2 31static int force;
8035e428 32static int input;
8035e428 33
42976487
MG
34static int full_paths;
35
301406b9
FW
36static int print_line;
37
8035e428
IM
38static unsigned long page_size;
39static unsigned long mmap_window = 32;
40
e4204992
ACM
41struct sym_hist {
42 u64 sum;
43 u64 ip[0];
44};
45
301406b9 46struct sym_ext {
971738f3 47 struct rb_node node;
301406b9
FW
48 double percent;
49 char *path;
50};
51
e4204992
ACM
52struct sym_priv {
53 struct sym_hist *hist;
54 struct sym_ext *ext;
55};
56
b32d133a
ACM
57static struct symbol_conf symbol_conf = {
58 .priv_size = sizeof(struct sym_priv),
59 .try_vmlinux_path = true,
60};
61
e4204992
ACM
62static const char *sym_hist_filter;
63
00a192b3 64static int symbol_filter(struct map *map __used, struct symbol *sym)
e4204992 65{
8f0b0373
ACM
66 if (sym_hist_filter == NULL ||
67 strcmp(sym->name, sym_hist_filter) == 0) {
00a192b3 68 struct sym_priv *priv = symbol__priv(sym);
e4204992
ACM
69 const int size = (sizeof(*priv->hist) +
70 (sym->end - sym->start) * sizeof(u64));
71
72 priv->hist = malloc(size);
73 if (priv->hist)
74 memset(priv->hist, 0, size);
75 return 0;
76 }
77 /*
78 * FIXME: We should really filter it out, as we don't want to go thru symbols
79 * we're not interested, and if a DSO ends up with no symbols, delete it too,
80 * but right now the kernel loading routines in symbol.c bail out if no symbols
81 * are found, fix it later.
82 */
83 return 0;
84}
8035e428 85
0b73da3f
IM
86/*
87 * collect histogram counts
88 */
9cffa8d5 89static void hist_hit(struct hist_entry *he, u64 ip)
8035e428 90{
0b73da3f
IM
91 unsigned int sym_size, offset;
92 struct symbol *sym = he->sym;
e4204992
ACM
93 struct sym_priv *priv;
94 struct sym_hist *h;
8035e428 95
0b73da3f 96 he->count++;
8035e428 97
e4204992
ACM
98 if (!sym || !he->map)
99 return;
100
00a192b3 101 priv = symbol__priv(sym);
e4204992 102 if (!priv->hist)
0b73da3f 103 return;
8035e428 104
0b73da3f
IM
105 sym_size = sym->end - sym->start;
106 offset = ip - sym->start;
8035e428 107
ed52ce2e
ACM
108 if (verbose)
109 fprintf(stderr, "%s: ip=%Lx\n", __func__,
110 he->map->unmap_ip(he->map, ip));
111
0b73da3f
IM
112 if (offset >= sym_size)
113 return;
8035e428 114
e4204992
ACM
115 h = priv->hist;
116 h->sum++;
117 h->ip[offset]++;
8035e428 118
0b73da3f
IM
119 if (verbose >= 3)
120 printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
7d37a0cb 121 (void *)(unsigned long)he->sym->start,
0b73da3f 122 he->sym->name,
7d37a0cb 123 (void *)(unsigned long)ip, ip - he->sym->start,
e4204992 124 h->ip[offset]);
8035e428
IM
125}
126
1ed091c4 127static int hist_entry__add(struct addr_location *al, u64 count)
8035e428 128{
9735abf1 129 bool hit;
1ed091c4 130 struct hist_entry *he = __hist_entry__add(al, NULL, count, &hit);
9735abf1 131 if (he == NULL)
8035e428 132 return -ENOMEM;
1ed091c4 133 hist_hit(he, al->addr);
8035e428
IM
134 return 0;
135}
136
62daacb5 137static int process_sample_event(event_t *event)
8035e428 138{
1ed091c4 139 struct addr_location al;
6baa0a5a 140
62daacb5 141 dump_printf("(IP, %d): %d: %p\n", event->header.misc,
1ed091c4 142 event->ip.pid, (void *)(long)event->ip.ip);
8035e428 143
1ed091c4 144 if (event__preprocess_sample(event, &al, symbol_filter) < 0) {
8035e428
IM
145 fprintf(stderr, "problem processing %d event, skipping it.\n",
146 event->header.type);
147 return -1;
148 }
149
1ed091c4 150 if (hist_entry__add(&al, 1)) {
ec218fc4
ACM
151 fprintf(stderr, "problem incrementing symbol count, "
152 "skipping event\n");
153 return -1;
8035e428 154 }
8035e428
IM
155
156 return 0;
157}
158
62daacb5 159static int event__process(event_t *self)
8035e428 160{
62daacb5 161 switch (self->header.type) {
cdd6c482 162 case PERF_RECORD_SAMPLE:
62daacb5 163 return process_sample_event(self);
e6e18ec7 164
cdd6c482 165 case PERF_RECORD_MMAP:
62daacb5 166 return event__process_mmap(self);
8035e428 167
cdd6c482 168 case PERF_RECORD_COMM:
62daacb5 169 return event__process_comm(self);
8035e428 170
cdd6c482 171 case PERF_RECORD_FORK:
62daacb5 172 return event__process_task(self);
8035e428
IM
173 /*
174 * We dont process them right now but they are fine:
175 */
176
cdd6c482
IM
177 case PERF_RECORD_THROTTLE:
178 case PERF_RECORD_UNTHROTTLE:
8035e428
IM
179 return 0;
180
181 default:
182 return -1;
183 }
184
185 return 0;
186}
187
ed52ce2e 188static int parse_line(FILE *file, struct hist_entry *he, u64 len)
0b73da3f 189{
ed52ce2e 190 struct symbol *sym = he->sym;
0b73da3f 191 char *line = NULL, *tmp, *tmp2;
301406b9
FW
192 static const char *prev_line;
193 static const char *prev_color;
0b73da3f
IM
194 unsigned int offset;
195 size_t line_len;
ed52ce2e 196 u64 start;
f37a291c 197 s64 line_ip;
0b73da3f
IM
198 int ret;
199 char *c;
200
201 if (getline(&line, &line_len, file) < 0)
202 return -1;
203 if (!line)
204 return -1;
205
206 c = strchr(line, '\n');
207 if (c)
208 *c = 0;
209
210 line_ip = -1;
211 offset = 0;
212 ret = -2;
213
214 /*
215 * Strip leading spaces:
216 */
217 tmp = line;
218 while (*tmp) {
219 if (*tmp != ' ')
220 break;
221 tmp++;
222 }
223
224 if (*tmp) {
225 /*
226 * Parse hexa addresses followed by ':'
227 */
228 line_ip = strtoull(tmp, &tmp2, 16);
229 if (*tmp2 != ':')
230 line_ip = -1;
231 }
232
ed52ce2e
ACM
233 start = he->map->unmap_ip(he->map, sym->start);
234
0b73da3f 235 if (line_ip != -1) {
301406b9 236 const char *path = NULL;
0b73da3f
IM
237 unsigned int hits = 0;
238 double percent = 0.0;
83a0944f 239 const char *color;
00a192b3 240 struct sym_priv *priv = symbol__priv(sym);
e4204992
ACM
241 struct sym_ext *sym_ext = priv->ext;
242 struct sym_hist *h = priv->hist;
0b73da3f 243
ed52ce2e 244 offset = line_ip - start;
0b73da3f 245 if (offset < len)
e4204992 246 hits = h->ip[offset];
0b73da3f 247
c17c2db1 248 if (offset < len && sym_ext) {
301406b9
FW
249 path = sym_ext[offset].path;
250 percent = sym_ext[offset].percent;
e4204992
ACM
251 } else if (h->sum)
252 percent = 100.0 * hits / h->sum;
0b73da3f 253
1e11fd82 254 color = get_percent_color(percent);
0b73da3f 255
301406b9
FW
256 /*
257 * Also color the filename and line if needed, with
258 * the same color than the percentage. Don't print it
259 * twice for close colored ip with the same filename:line
260 */
261 if (path) {
262 if (!prev_line || strcmp(prev_line, path)
263 || color != prev_color) {
264 color_fprintf(stdout, color, " %s", path);
265 prev_line = path;
266 prev_color = color;
267 }
268 }
269
0b73da3f
IM
270 color_fprintf(stdout, color, " %7.2f", percent);
271 printf(" : ");
272 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
273 } else {
274 if (!*line)
275 printf(" :\n");
276 else
277 printf(" : %s\n", line);
278 }
279
280 return 0;
281}
282
971738f3
FW
283static struct rb_root root_sym_ext;
284
285static void insert_source_line(struct sym_ext *sym_ext)
286{
287 struct sym_ext *iter;
288 struct rb_node **p = &root_sym_ext.rb_node;
289 struct rb_node *parent = NULL;
290
291 while (*p != NULL) {
292 parent = *p;
293 iter = rb_entry(parent, struct sym_ext, node);
294
295 if (sym_ext->percent > iter->percent)
296 p = &(*p)->rb_left;
297 else
298 p = &(*p)->rb_right;
299 }
300
301 rb_link_node(&sym_ext->node, parent, p);
302 rb_insert_color(&sym_ext->node, &root_sym_ext);
303}
304
e4204992 305static void free_source_line(struct hist_entry *he, int len)
301406b9 306{
00a192b3 307 struct sym_priv *priv = symbol__priv(he->sym);
e4204992 308 struct sym_ext *sym_ext = priv->ext;
301406b9
FW
309 int i;
310
311 if (!sym_ext)
312 return;
313
314 for (i = 0; i < len; i++)
315 free(sym_ext[i].path);
316 free(sym_ext);
317
e4204992 318 priv->ext = NULL;
971738f3 319 root_sym_ext = RB_ROOT;
301406b9
FW
320}
321
322/* Get the filename:line for the colored entries */
c17c2db1 323static void
ed52ce2e 324get_source_line(struct hist_entry *he, int len, const char *filename)
301406b9 325{
ed52ce2e
ACM
326 struct symbol *sym = he->sym;
327 u64 start;
301406b9
FW
328 int i;
329 char cmd[PATH_MAX * 2];
330 struct sym_ext *sym_ext;
00a192b3 331 struct sym_priv *priv = symbol__priv(sym);
e4204992 332 struct sym_hist *h = priv->hist;
301406b9 333
e4204992 334 if (!h->sum)
301406b9
FW
335 return;
336
e4204992
ACM
337 sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext));
338 if (!priv->ext)
301406b9
FW
339 return;
340
ed52ce2e 341 start = he->map->unmap_ip(he->map, sym->start);
301406b9
FW
342
343 for (i = 0; i < len; i++) {
344 char *path = NULL;
345 size_t line_len;
9cffa8d5 346 u64 offset;
301406b9
FW
347 FILE *fp;
348
e4204992 349 sym_ext[i].percent = 100.0 * h->ip[i] / h->sum;
301406b9
FW
350 if (sym_ext[i].percent <= 0.5)
351 continue;
352
ed52ce2e 353 offset = start + i;
c17c2db1 354 sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
301406b9
FW
355 fp = popen(cmd, "r");
356 if (!fp)
357 continue;
358
359 if (getline(&path, &line_len, fp) < 0 || !line_len)
360 goto next;
361
c17c2db1 362 sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
301406b9
FW
363 if (!sym_ext[i].path)
364 goto next;
365
366 strcpy(sym_ext[i].path, path);
971738f3 367 insert_source_line(&sym_ext[i]);
301406b9
FW
368
369 next:
370 pclose(fp);
371 }
372}
373
83a0944f 374static void print_summary(const char *filename)
971738f3
FW
375{
376 struct sym_ext *sym_ext;
377 struct rb_node *node;
378
379 printf("\nSorted summary for file %s\n", filename);
380 printf("----------------------------------------------\n\n");
381
382 if (RB_EMPTY_ROOT(&root_sym_ext)) {
383 printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
384 return;
385 }
386
387 node = rb_first(&root_sym_ext);
388 while (node) {
389 double percent;
83a0944f 390 const char *color;
971738f3
FW
391 char *path;
392
393 sym_ext = rb_entry(node, struct sym_ext, node);
394 percent = sym_ext->percent;
1e11fd82 395 color = get_percent_color(percent);
971738f3
FW
396 path = sym_ext->path;
397
398 color_fprintf(stdout, color, " %7.2f %s", percent, path);
399 node = rb_next(node);
400 }
401}
402
ed52ce2e 403static void annotate_sym(struct hist_entry *he)
0b73da3f 404{
ed52ce2e
ACM
405 struct map *map = he->map;
406 struct dso *dso = map->dso;
407 struct symbol *sym = he->sym;
439d473b
ACM
408 const char *filename = dso->long_name, *d_filename;
409 u64 len;
0b73da3f
IM
410 char command[PATH_MAX*2];
411 FILE *file;
412
413 if (!filename)
414 return;
439d473b 415
ed52ce2e
ACM
416 if (verbose)
417 fprintf(stderr, "%s: filename=%s, sym=%s, start=%Lx, end=%Lx\n",
418 __func__, filename, sym->name,
419 map->unmap_ip(map, sym->start),
420 map->unmap_ip(map, sym->end));
421
42976487
MG
422 if (full_paths)
423 d_filename = filename;
424 else
425 d_filename = basename(filename);
0b73da3f 426
0b73da3f
IM
427 len = sym->end - sym->start;
428
971738f3 429 if (print_line) {
ed52ce2e 430 get_source_line(he, len, filename);
971738f3
FW
431 print_summary(filename);
432 }
433
434 printf("\n\n------------------------------------------------\n");
42976487 435 printf(" Percent | Source code & Disassembly of %s\n", d_filename);
971738f3
FW
436 printf("------------------------------------------------\n");
437
438 if (verbose >= 2)
439d473b
ACM
439 printf("annotating [%p] %30s : [%p] %30s\n",
440 dso, dso->long_name, sym, sym->name);
301406b9 441
42976487 442 sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
ed52ce2e
ACM
443 map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end),
444 filename, filename);
0b73da3f
IM
445
446 if (verbose >= 3)
447 printf("doing: %s\n", command);
448
449 file = popen(command, "r");
450 if (!file)
451 return;
452
453 while (!feof(file)) {
ed52ce2e 454 if (parse_line(file, he, len) < 0)
0b73da3f
IM
455 break;
456 }
457
458 pclose(file);
971738f3 459 if (print_line)
e4204992 460 free_source_line(he, len);
0b73da3f
IM
461}
462
463static void find_annotations(void)
464{
465 struct rb_node *nd;
0b73da3f 466
ed52ce2e
ACM
467 for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
468 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
e4204992 469 struct sym_priv *priv;
0b73da3f 470
e4204992
ACM
471 if (he->sym == NULL)
472 continue;
0b73da3f 473
00a192b3 474 priv = symbol__priv(he->sym);
e4204992
ACM
475 if (priv->hist == NULL)
476 continue;
477
478 annotate_sym(he);
e4204992
ACM
479 /*
480 * Since we have a hist_entry per IP for the same symbol, free
481 * he->sym->hist to signal we already processed this symbol.
482 */
483 free(priv->hist);
484 priv->hist = NULL;
0b73da3f 485 }
0b73da3f
IM
486}
487
8035e428
IM
488static int __cmd_annotate(void)
489{
490 int ret, rc = EXIT_FAILURE;
491 unsigned long offset = 0;
492 unsigned long head = 0;
83a0944f 493 struct stat input_stat;
8035e428
IM
494 event_t *event;
495 uint32_t size;
496 char *buf;
497
d5b889f2 498 register_idle_thread();
8035e428
IM
499
500 input = open(input_name, O_RDONLY);
501 if (input < 0) {
502 perror("failed to open file");
503 exit(-1);
504 }
505
83a0944f 506 ret = fstat(input, &input_stat);
8035e428
IM
507 if (ret < 0) {
508 perror("failed to stat file");
509 exit(-1);
510 }
511
119e7a22
PH
512 if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
513 fprintf(stderr, "file: %s not owned by current user or root\n", input_name);
fa6963b2
PZ
514 exit(-1);
515 }
516
83a0944f 517 if (!input_stat.st_size) {
8035e428
IM
518 fprintf(stderr, "zero-sized file, nothing to do!\n");
519 exit(0);
520 }
521
8035e428
IM
522remap:
523 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
524 MAP_SHARED, input, offset);
525 if (buf == MAP_FAILED) {
526 perror("failed to mmap file");
527 exit(-1);
528 }
529
530more:
531 event = (event_t *)(buf + head);
532
533 size = event->header.size;
534 if (!size)
535 size = 8;
536
537 if (head + event->header.size >= page_size * mmap_window) {
538 unsigned long shift = page_size * (head / page_size);
83a0944f 539 int munmap_ret;
8035e428 540
83a0944f
IM
541 munmap_ret = munmap(buf, page_size * mmap_window);
542 assert(munmap_ret == 0);
8035e428
IM
543
544 offset += shift;
545 head -= shift;
546 goto remap;
547 }
548
549 size = event->header.size;
550
2cec19d9 551 dump_printf("%p [%p]: event: %d\n",
8035e428
IM
552 (void *)(offset + head),
553 (void *)(long)event->header.size,
554 event->header.type);
555
62daacb5 556 if (!size || event__process(event) < 0) {
8035e428 557
2cec19d9 558 dump_printf("%p [%p]: skipping unknown header type: %d\n",
8035e428
IM
559 (void *)(offset + head),
560 (void *)(long)(event->header.size),
561 event->header.type);
8035e428
IM
562 /*
563 * assume we lost track of the stream, check alignment, and
564 * increment a single u64 in the hope to catch on again 'soon'.
565 */
566
567 if (unlikely(head & 7))
568 head &= ~7ULL;
569
570 size = 8;
571 }
572
573 head += size;
574
83a0944f 575 if (offset + head < (unsigned long)input_stat.st_size)
8035e428
IM
576 goto more;
577
578 rc = EXIT_SUCCESS;
579 close(input);
580
8035e428 581
62daacb5
ACM
582 if (dump_trace) {
583 event__print_totals();
8035e428 584 return 0;
62daacb5 585 }
8035e428 586
da21d1b5 587 if (verbose > 3)
d5b889f2 588 threads__fprintf(stdout);
8035e428 589
da21d1b5 590 if (verbose > 2)
8035e428
IM
591 dsos__fprintf(stdout);
592
593 collapse__resort();
62daacb5 594 output__resort(event__total[0]);
0b73da3f
IM
595
596 find_annotations();
8035e428
IM
597
598 return rc;
599}
600
601static const char * const annotate_usage[] = {
602 "perf annotate [<options>] <command>",
603 NULL
604};
605
606static const struct option options[] = {
607 OPT_STRING('i', "input", &input_name, "file",
608 "input file name"),
23b87116 609 OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
0b73da3f 610 "symbol to annotate"),
fa6963b2 611 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
8035e428
IM
612 OPT_BOOLEAN('v', "verbose", &verbose,
613 "be more verbose (show symbol address, etc)"),
614 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
615 "dump raw trace in ASCII"),
b32d133a
ACM
616 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
617 "file", "vmlinux pathname"),
618 OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
42976487 619 "load module symbols - WARNING: use only with -k and LIVE kernel"),
301406b9
FW
620 OPT_BOOLEAN('l', "print-line", &print_line,
621 "print matching source lines (may be slow)"),
42976487
MG
622 OPT_BOOLEAN('P', "full-paths", &full_paths,
623 "Don't shorten the displayed pathnames"),
8035e428
IM
624 OPT_END()
625};
626
627static void setup_sorting(void)
628{
629 char *tmp, *tok, *str = strdup(sort_order);
630
631 for (tok = strtok_r(str, ", ", &tmp);
632 tok; tok = strtok_r(NULL, ", ", &tmp)) {
633 if (sort_dimension__add(tok) < 0) {
634 error("Unknown --sort key: `%s'", tok);
635 usage_with_options(annotate_usage, options);
636 }
637 }
638
639 free(str);
640}
641
f37a291c 642int cmd_annotate(int argc, const char **argv, const char *prefix __used)
8035e428 643{
b32d133a
ACM
644 if (symbol__init(&symbol_conf) < 0)
645 return -1;
8035e428
IM
646
647 page_size = getpagesize();
648
649 argc = parse_options(argc, argv, options, annotate_usage, 0);
650
651 setup_sorting();
652
0b73da3f
IM
653 if (argc) {
654 /*
655 * Special case: if there's an argument left then assume tha
656 * it's a symbol filter:
657 */
658 if (argc > 1)
659 usage_with_options(annotate_usage, options);
660
661 sym_hist_filter = argv[0];
662 }
663
8035e428
IM
664 setup_pager();
665
dd68ada2
JK
666 if (field_sep && *field_sep == '.') {
667 fputs("'.' is the only non valid --field-separator argument\n",
668 stderr);
669 exit(129);
670 }
671
8035e428
IM
672 return __cmd_annotate();
673}
This page took 0.077824 seconds and 5 git commands to generate.