blob: 3bedaa5d21d29882e021021e34811822304c3d2e [file] [log] [blame]
Ingo Molnar8035e422009-06-06 15:19:13 +02001/*
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"
Arnaldo Carvalho de Melo5da50252009-07-01 14:46:08 -030013#include <linux/list.h>
Ingo Molnar8035e422009-06-06 15:19:13 +020014#include "util/cache.h"
Arnaldo Carvalho de Melo43cbcd82009-07-01 12:28:37 -030015#include <linux/rbtree.h>
Ingo Molnar8035e422009-06-06 15:19:13 +020016#include "util/symbol.h"
17#include "util/string.h"
18
19#include "perf.h"
20
21#include "util/parse-options.h"
22#include "util/parse-events.h"
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +020023#include "util/thread.h"
Ingo Molnar8035e422009-06-06 15:19:13 +020024
25#define SHOW_KERNEL 1
26#define SHOW_USER 2
27#define SHOW_HV 4
28
29static char const *input_name = "perf.data";
Ingo Molnar8035e422009-06-06 15:19:13 +020030
Ingo Molnar0b73da32009-06-06 15:48:52 +020031static char default_sort_order[] = "comm,symbol";
Ingo Molnar8035e422009-06-06 15:19:13 +020032static char *sort_order = default_sort_order;
33
34static int input;
35static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
36
37static int dump_trace = 0;
38#define dprintf(x...) do { if (dump_trace) printf(x); } while (0)
39
Mike Galbraith42976482009-07-02 08:09:46 +020040
41static int full_paths;
42
Frederic Weisbecker301406b2009-06-13 00:11:21 +020043static int print_line;
44
Ingo Molnar8035e422009-06-06 15:19:13 +020045static unsigned long page_size;
46static unsigned long mmap_window = 32;
47
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +020048static struct rb_root threads;
49static struct thread *last_match;
50
Frederic Weisbecker301406b2009-06-13 00:11:21 +020051
52struct sym_ext {
Frederic Weisbecker971738f2009-06-13 00:11:22 +020053 struct rb_node node;
Frederic Weisbecker301406b2009-06-13 00:11:21 +020054 double percent;
55 char *path;
56};
57
Ingo Molnar8035e422009-06-06 15:19:13 +020058/*
59 * histogram, sorted on item, collects counts
60 */
61
62static struct rb_root hist;
63
64struct hist_entry {
65 struct rb_node rb_node;
66
67 struct thread *thread;
68 struct map *map;
69 struct dso *dso;
70 struct symbol *sym;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +100071 u64 ip;
Ingo Molnar8035e422009-06-06 15:19:13 +020072 char level;
73
74 uint32_t count;
75};
76
77/*
78 * configurable sorting bits
79 */
80
81struct sort_entry {
82 struct list_head list;
83
84 char *header;
85
86 int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
87 int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
88 size_t (*print)(FILE *fp, struct hist_entry *);
89};
90
91/* --sort pid */
92
93static int64_t
94sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
95{
96 return right->thread->pid - left->thread->pid;
97}
98
99static size_t
100sort__thread_print(FILE *fp, struct hist_entry *self)
101{
102 return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid);
103}
104
105static struct sort_entry sort_thread = {
106 .header = " Command: Pid",
107 .cmp = sort__thread_cmp,
108 .print = sort__thread_print,
109};
110
111/* --sort comm */
112
113static int64_t
114sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
115{
116 return right->thread->pid - left->thread->pid;
117}
118
119static int64_t
120sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
121{
122 char *comm_l = left->thread->comm;
123 char *comm_r = right->thread->comm;
124
125 if (!comm_l || !comm_r) {
126 if (!comm_l && !comm_r)
127 return 0;
128 else if (!comm_l)
129 return -1;
130 else
131 return 1;
132 }
133
134 return strcmp(comm_l, comm_r);
135}
136
137static size_t
138sort__comm_print(FILE *fp, struct hist_entry *self)
139{
140 return fprintf(fp, "%16s", self->thread->comm);
141}
142
143static struct sort_entry sort_comm = {
144 .header = " Command",
145 .cmp = sort__comm_cmp,
146 .collapse = sort__comm_collapse,
147 .print = sort__comm_print,
148};
149
150/* --sort dso */
151
152static int64_t
153sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
154{
155 struct dso *dso_l = left->dso;
156 struct dso *dso_r = right->dso;
157
158 if (!dso_l || !dso_r) {
159 if (!dso_l && !dso_r)
160 return 0;
161 else if (!dso_l)
162 return -1;
163 else
164 return 1;
165 }
166
167 return strcmp(dso_l->name, dso_r->name);
168}
169
170static size_t
171sort__dso_print(FILE *fp, struct hist_entry *self)
172{
173 if (self->dso)
174 return fprintf(fp, "%-25s", self->dso->name);
175
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000176 return fprintf(fp, "%016llx ", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200177}
178
179static struct sort_entry sort_dso = {
180 .header = "Shared Object ",
181 .cmp = sort__dso_cmp,
182 .print = sort__dso_print,
183};
184
185/* --sort symbol */
186
187static int64_t
188sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
189{
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000190 u64 ip_l, ip_r;
Ingo Molnar8035e422009-06-06 15:19:13 +0200191
192 if (left->sym == right->sym)
193 return 0;
194
195 ip_l = left->sym ? left->sym->start : left->ip;
196 ip_r = right->sym ? right->sym->start : right->ip;
197
198 return (int64_t)(ip_r - ip_l);
199}
200
201static size_t
202sort__sym_print(FILE *fp, struct hist_entry *self)
203{
204 size_t ret = 0;
205
206 if (verbose)
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000207 ret += fprintf(fp, "%#018llx ", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200208
209 if (self->sym) {
210 ret += fprintf(fp, "[%c] %s",
211 self->dso == kernel_dso ? 'k' : '.', self->sym->name);
212 } else {
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000213 ret += fprintf(fp, "%#016llx", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200214 }
215
216 return ret;
217}
218
219static struct sort_entry sort_sym = {
220 .header = "Symbol",
221 .cmp = sort__sym_cmp,
222 .print = sort__sym_print,
223};
224
225static int sort__need_collapse = 0;
226
227struct sort_dimension {
228 char *name;
229 struct sort_entry *entry;
230 int taken;
231};
232
233static struct sort_dimension sort_dimensions[] = {
234 { .name = "pid", .entry = &sort_thread, },
235 { .name = "comm", .entry = &sort_comm, },
236 { .name = "dso", .entry = &sort_dso, },
237 { .name = "symbol", .entry = &sort_sym, },
238};
239
240static LIST_HEAD(hist_entry__sort_list);
241
242static int sort_dimension__add(char *tok)
243{
Ingo Molnarf37a2912009-07-01 12:37:06 +0200244 unsigned int i;
Ingo Molnar8035e422009-06-06 15:19:13 +0200245
246 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
247 struct sort_dimension *sd = &sort_dimensions[i];
248
249 if (sd->taken)
250 continue;
251
252 if (strncasecmp(tok, sd->name, strlen(tok)))
253 continue;
254
255 if (sd->entry->collapse)
256 sort__need_collapse = 1;
257
258 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
259 sd->taken = 1;
260
261 return 0;
262 }
263
264 return -ESRCH;
265}
266
267static int64_t
268hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
269{
270 struct sort_entry *se;
271 int64_t cmp = 0;
272
273 list_for_each_entry(se, &hist_entry__sort_list, list) {
274 cmp = se->cmp(left, right);
275 if (cmp)
276 break;
277 }
278
279 return cmp;
280}
281
282static int64_t
283hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
284{
285 struct sort_entry *se;
286 int64_t cmp = 0;
287
288 list_for_each_entry(se, &hist_entry__sort_list, list) {
289 int64_t (*f)(struct hist_entry *, struct hist_entry *);
290
291 f = se->collapse ?: se->cmp;
292
293 cmp = f(left, right);
294 if (cmp)
295 break;
296 }
297
298 return cmp;
299}
300
Ingo Molnar8035e422009-06-06 15:19:13 +0200301/*
302 * collect histogram counts
303 */
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000304static void hist_hit(struct hist_entry *he, u64 ip)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200305{
306 unsigned int sym_size, offset;
307 struct symbol *sym = he->sym;
308
309 he->count++;
310
311 if (!sym || !sym->hist)
312 return;
313
314 sym_size = sym->end - sym->start;
315 offset = ip - sym->start;
316
317 if (offset >= sym_size)
318 return;
319
320 sym->hist_sum++;
321 sym->hist[offset]++;
322
323 if (verbose >= 3)
324 printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
Arjan van de Ven7d37a0c2009-06-06 20:36:38 +0200325 (void *)(unsigned long)he->sym->start,
Ingo Molnar0b73da32009-06-06 15:48:52 +0200326 he->sym->name,
Arjan van de Ven7d37a0c2009-06-06 20:36:38 +0200327 (void *)(unsigned long)ip, ip - he->sym->start,
Ingo Molnar0b73da32009-06-06 15:48:52 +0200328 sym->hist[offset]);
329}
Ingo Molnar8035e422009-06-06 15:19:13 +0200330
331static int
332hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000333 struct symbol *sym, u64 ip, char level)
Ingo Molnar8035e422009-06-06 15:19:13 +0200334{
335 struct rb_node **p = &hist.rb_node;
336 struct rb_node *parent = NULL;
337 struct hist_entry *he;
338 struct hist_entry entry = {
339 .thread = thread,
340 .map = map,
341 .dso = dso,
342 .sym = sym,
343 .ip = ip,
344 .level = level,
345 .count = 1,
346 };
347 int cmp;
348
349 while (*p != NULL) {
350 parent = *p;
351 he = rb_entry(parent, struct hist_entry, rb_node);
352
353 cmp = hist_entry__cmp(&entry, he);
354
355 if (!cmp) {
Ingo Molnar0b73da32009-06-06 15:48:52 +0200356 hist_hit(he, ip);
357
Ingo Molnar8035e422009-06-06 15:19:13 +0200358 return 0;
359 }
360
361 if (cmp < 0)
362 p = &(*p)->rb_left;
363 else
364 p = &(*p)->rb_right;
365 }
366
367 he = malloc(sizeof(*he));
368 if (!he)
369 return -ENOMEM;
370 *he = entry;
371 rb_link_node(&he->rb_node, parent, p);
372 rb_insert_color(&he->rb_node, &hist);
373
374 return 0;
375}
376
377static void hist_entry__free(struct hist_entry *he)
378{
379 free(he);
380}
381
382/*
383 * collapse the histogram
384 */
385
386static struct rb_root collapse_hists;
387
388static void collapse__insert_entry(struct hist_entry *he)
389{
390 struct rb_node **p = &collapse_hists.rb_node;
391 struct rb_node *parent = NULL;
392 struct hist_entry *iter;
393 int64_t cmp;
394
395 while (*p != NULL) {
396 parent = *p;
397 iter = rb_entry(parent, struct hist_entry, rb_node);
398
399 cmp = hist_entry__collapse(iter, he);
400
401 if (!cmp) {
402 iter->count += he->count;
403 hist_entry__free(he);
404 return;
405 }
406
407 if (cmp < 0)
408 p = &(*p)->rb_left;
409 else
410 p = &(*p)->rb_right;
411 }
412
413 rb_link_node(&he->rb_node, parent, p);
414 rb_insert_color(&he->rb_node, &collapse_hists);
415}
416
417static void collapse__resort(void)
418{
419 struct rb_node *next;
420 struct hist_entry *n;
421
422 if (!sort__need_collapse)
423 return;
424
425 next = rb_first(&hist);
426 while (next) {
427 n = rb_entry(next, struct hist_entry, rb_node);
428 next = rb_next(&n->rb_node);
429
430 rb_erase(&n->rb_node, &hist);
431 collapse__insert_entry(n);
432 }
433}
434
435/*
436 * reverse the map, sort on count.
437 */
438
439static struct rb_root output_hists;
440
441static void output__insert_entry(struct hist_entry *he)
442{
443 struct rb_node **p = &output_hists.rb_node;
444 struct rb_node *parent = NULL;
445 struct hist_entry *iter;
446
447 while (*p != NULL) {
448 parent = *p;
449 iter = rb_entry(parent, struct hist_entry, rb_node);
450
451 if (he->count > iter->count)
452 p = &(*p)->rb_left;
453 else
454 p = &(*p)->rb_right;
455 }
456
457 rb_link_node(&he->rb_node, parent, p);
458 rb_insert_color(&he->rb_node, &output_hists);
459}
460
461static void output__resort(void)
462{
463 struct rb_node *next;
464 struct hist_entry *n;
465 struct rb_root *tree = &hist;
466
467 if (sort__need_collapse)
468 tree = &collapse_hists;
469
470 next = rb_first(tree);
471
472 while (next) {
473 n = rb_entry(next, struct hist_entry, rb_node);
474 next = rb_next(&n->rb_node);
475
476 rb_erase(&n->rb_node, tree);
477 output__insert_entry(n);
478 }
479}
480
Ingo Molnar8035e422009-06-06 15:19:13 +0200481static void register_idle_thread(void)
482{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200483 struct thread *thread = threads__findnew(0, &threads, &last_match);
Ingo Molnar8035e422009-06-06 15:19:13 +0200484
485 if (thread == NULL ||
486 thread__set_comm(thread, "[idle]")) {
487 fprintf(stderr, "problem inserting idle task.\n");
488 exit(-1);
489 }
490}
491
492static unsigned long total = 0,
493 total_mmap = 0,
494 total_comm = 0,
495 total_fork = 0,
496 total_unknown = 0;
497
498static int
Peter Zijlstrae6e18ec2009-06-25 11:27:12 +0200499process_sample_event(event_t *event, unsigned long offset, unsigned long head)
Ingo Molnar8035e422009-06-06 15:19:13 +0200500{
501 char level;
502 int show = 0;
503 struct dso *dso = NULL;
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200504 struct thread *thread;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000505 u64 ip = event->ip.ip;
Ingo Molnar8035e422009-06-06 15:19:13 +0200506 struct map *map = NULL;
507
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200508 thread = threads__findnew(event->ip.pid, &threads, &last_match);
509
Ingo Molnar8035e422009-06-06 15:19:13 +0200510 dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
511 (void *)(offset + head),
512 (void *)(long)(event->header.size),
513 event->header.misc,
514 event->ip.pid,
515 (void *)(long)ip);
516
517 dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid);
518
519 if (thread == NULL) {
520 fprintf(stderr, "problem processing %d event, skipping it.\n",
521 event->header.type);
522 return -1;
523 }
524
525 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
526 show = SHOW_KERNEL;
527 level = 'k';
528
529 dso = kernel_dso;
530
531 dprintf(" ...... dso: %s\n", dso->name);
532
533 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
534
535 show = SHOW_USER;
536 level = '.';
537
538 map = thread__find_map(thread, ip);
539 if (map != NULL) {
540 ip = map->map_ip(map, ip);
541 dso = map->dso;
542 } else {
543 /*
544 * If this is outside of all known maps,
545 * and is a negative address, try to look it
546 * up in the kernel dso, as it might be a
547 * vsyscall (which executes in user-mode):
548 */
549 if ((long long)ip < 0)
550 dso = kernel_dso;
551 }
552 dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
553
554 } else {
555 show = SHOW_HV;
556 level = 'H';
557 dprintf(" ...... dso: [hypervisor]\n");
558 }
559
560 if (show & show_mask) {
561 struct symbol *sym = NULL;
562
563 if (dso)
564 sym = dso->find_symbol(dso, ip);
565
566 if (hist_entry__add(thread, map, dso, sym, ip, level)) {
567 fprintf(stderr,
568 "problem incrementing symbol count, skipping event\n");
569 return -1;
570 }
571 }
572 total++;
573
574 return 0;
575}
576
577static int
578process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
579{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200580 struct thread *thread;
Frederic Weisbecker66e274f2009-08-12 11:07:25 +0200581 struct map *map = map__new(&event->mmap, NULL, 0);
Ingo Molnar8035e422009-06-06 15:19:13 +0200582
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200583 thread = threads__findnew(event->mmap.pid, &threads, &last_match);
584
Ingo Molnar8035e422009-06-06 15:19:13 +0200585 dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n",
586 (void *)(offset + head),
587 (void *)(long)(event->header.size),
588 event->mmap.pid,
589 (void *)(long)event->mmap.start,
590 (void *)(long)event->mmap.len,
591 (void *)(long)event->mmap.pgoff,
592 event->mmap.filename);
593
594 if (thread == NULL || map == NULL) {
595 dprintf("problem processing PERF_EVENT_MMAP, skipping event.\n");
596 return 0;
597 }
598
599 thread__insert_map(thread, map);
600 total_mmap++;
601
602 return 0;
603}
604
605static int
606process_comm_event(event_t *event, unsigned long offset, unsigned long head)
607{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200608 struct thread *thread;
Ingo Molnar8035e422009-06-06 15:19:13 +0200609
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200610 thread = threads__findnew(event->comm.pid, &threads, &last_match);
Ingo Molnar8035e422009-06-06 15:19:13 +0200611 dprintf("%p [%p]: PERF_EVENT_COMM: %s:%d\n",
612 (void *)(offset + head),
613 (void *)(long)(event->header.size),
614 event->comm.comm, event->comm.pid);
615
616 if (thread == NULL ||
617 thread__set_comm(thread, event->comm.comm)) {
618 dprintf("problem processing PERF_EVENT_COMM, skipping event.\n");
619 return -1;
620 }
621 total_comm++;
622
623 return 0;
624}
625
626static int
627process_fork_event(event_t *event, unsigned long offset, unsigned long head)
628{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200629 struct thread *thread;
630 struct thread *parent;
Ingo Molnar8035e422009-06-06 15:19:13 +0200631
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200632 thread = threads__findnew(event->fork.pid, &threads, &last_match);
633 parent = threads__findnew(event->fork.ppid, &threads, &last_match);
Ingo Molnar8035e422009-06-06 15:19:13 +0200634 dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n",
635 (void *)(offset + head),
636 (void *)(long)(event->header.size),
637 event->fork.pid, event->fork.ppid);
638
639 if (!thread || !parent || thread__fork(thread, parent)) {
640 dprintf("problem processing PERF_EVENT_FORK, skipping event.\n");
641 return -1;
642 }
643 total_fork++;
644
645 return 0;
646}
647
648static int
Ingo Molnar8035e422009-06-06 15:19:13 +0200649process_event(event_t *event, unsigned long offset, unsigned long head)
650{
Ingo Molnar8035e422009-06-06 15:19:13 +0200651 switch (event->header.type) {
Peter Zijlstrae6e18ec2009-06-25 11:27:12 +0200652 case PERF_EVENT_SAMPLE:
653 return process_sample_event(event, offset, head);
654
Ingo Molnar8035e422009-06-06 15:19:13 +0200655 case PERF_EVENT_MMAP:
656 return process_mmap_event(event, offset, head);
657
658 case PERF_EVENT_COMM:
659 return process_comm_event(event, offset, head);
660
661 case PERF_EVENT_FORK:
662 return process_fork_event(event, offset, head);
Ingo Molnar8035e422009-06-06 15:19:13 +0200663 /*
664 * We dont process them right now but they are fine:
665 */
666
667 case PERF_EVENT_THROTTLE:
668 case PERF_EVENT_UNTHROTTLE:
669 return 0;
670
671 default:
672 return -1;
673 }
674
675 return 0;
676}
677
Ingo Molnar0b73da32009-06-06 15:48:52 +0200678static int
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000679parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200680{
681 char *line = NULL, *tmp, *tmp2;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200682 static const char *prev_line;
683 static const char *prev_color;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200684 unsigned int offset;
685 size_t line_len;
Ingo Molnarf37a2912009-07-01 12:37:06 +0200686 s64 line_ip;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200687 int ret;
688 char *c;
689
690 if (getline(&line, &line_len, file) < 0)
691 return -1;
692 if (!line)
693 return -1;
694
695 c = strchr(line, '\n');
696 if (c)
697 *c = 0;
698
699 line_ip = -1;
700 offset = 0;
701 ret = -2;
702
703 /*
704 * Strip leading spaces:
705 */
706 tmp = line;
707 while (*tmp) {
708 if (*tmp != ' ')
709 break;
710 tmp++;
711 }
712
713 if (*tmp) {
714 /*
715 * Parse hexa addresses followed by ':'
716 */
717 line_ip = strtoull(tmp, &tmp2, 16);
718 if (*tmp2 != ':')
719 line_ip = -1;
720 }
721
722 if (line_ip != -1) {
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200723 const char *path = NULL;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200724 unsigned int hits = 0;
725 double percent = 0.0;
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200726 char *color;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200727 struct sym_ext *sym_ext = sym->priv;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200728
729 offset = line_ip - start;
730 if (offset < len)
731 hits = sym->hist[offset];
732
Frederic Weisbeckerc17c2db12009-06-13 17:39:23 +0200733 if (offset < len && sym_ext) {
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200734 path = sym_ext[offset].path;
735 percent = sym_ext[offset].percent;
736 } else if (sym->hist_sum)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200737 percent = 100.0 * hits / sym->hist_sum;
738
Frederic Weisbecker1e11fd82009-07-02 20:14:34 +0200739 color = get_percent_color(percent);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200740
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200741 /*
742 * Also color the filename and line if needed, with
743 * the same color than the percentage. Don't print it
744 * twice for close colored ip with the same filename:line
745 */
746 if (path) {
747 if (!prev_line || strcmp(prev_line, path)
748 || color != prev_color) {
749 color_fprintf(stdout, color, " %s", path);
750 prev_line = path;
751 prev_color = color;
752 }
753 }
754
Ingo Molnar0b73da32009-06-06 15:48:52 +0200755 color_fprintf(stdout, color, " %7.2f", percent);
756 printf(" : ");
757 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
758 } else {
759 if (!*line)
760 printf(" :\n");
761 else
762 printf(" : %s\n", line);
763 }
764
765 return 0;
766}
767
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200768static struct rb_root root_sym_ext;
769
770static void insert_source_line(struct sym_ext *sym_ext)
771{
772 struct sym_ext *iter;
773 struct rb_node **p = &root_sym_ext.rb_node;
774 struct rb_node *parent = NULL;
775
776 while (*p != NULL) {
777 parent = *p;
778 iter = rb_entry(parent, struct sym_ext, node);
779
780 if (sym_ext->percent > iter->percent)
781 p = &(*p)->rb_left;
782 else
783 p = &(*p)->rb_right;
784 }
785
786 rb_link_node(&sym_ext->node, parent, p);
787 rb_insert_color(&sym_ext->node, &root_sym_ext);
788}
789
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200790static void free_source_line(struct symbol *sym, int len)
791{
792 struct sym_ext *sym_ext = sym->priv;
793 int i;
794
795 if (!sym_ext)
796 return;
797
798 for (i = 0; i < len; i++)
799 free(sym_ext[i].path);
800 free(sym_ext);
801
802 sym->priv = NULL;
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200803 root_sym_ext = RB_ROOT;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200804}
805
806/* Get the filename:line for the colored entries */
Frederic Weisbeckerc17c2db12009-06-13 17:39:23 +0200807static void
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000808get_source_line(struct symbol *sym, u64 start, int len, char *filename)
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200809{
810 int i;
811 char cmd[PATH_MAX * 2];
812 struct sym_ext *sym_ext;
813
814 if (!sym->hist_sum)
815 return;
816
817 sym->priv = calloc(len, sizeof(struct sym_ext));
818 if (!sym->priv)
819 return;
820
821 sym_ext = sym->priv;
822
823 for (i = 0; i < len; i++) {
824 char *path = NULL;
825 size_t line_len;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000826 u64 offset;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200827 FILE *fp;
828
829 sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum;
830 if (sym_ext[i].percent <= 0.5)
831 continue;
832
833 offset = start + i;
Frederic Weisbeckerc17c2db12009-06-13 17:39:23 +0200834 sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200835 fp = popen(cmd, "r");
836 if (!fp)
837 continue;
838
839 if (getline(&path, &line_len, fp) < 0 || !line_len)
840 goto next;
841
Frederic Weisbeckerc17c2db12009-06-13 17:39:23 +0200842 sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200843 if (!sym_ext[i].path)
844 goto next;
845
846 strcpy(sym_ext[i].path, path);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200847 insert_source_line(&sym_ext[i]);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200848
849 next:
850 pclose(fp);
851 }
852}
853
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200854static void print_summary(char *filename)
855{
856 struct sym_ext *sym_ext;
857 struct rb_node *node;
858
859 printf("\nSorted summary for file %s\n", filename);
860 printf("----------------------------------------------\n\n");
861
862 if (RB_EMPTY_ROOT(&root_sym_ext)) {
863 printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
864 return;
865 }
866
867 node = rb_first(&root_sym_ext);
868 while (node) {
869 double percent;
870 char *color;
871 char *path;
872
873 sym_ext = rb_entry(node, struct sym_ext, node);
874 percent = sym_ext->percent;
Frederic Weisbecker1e11fd82009-07-02 20:14:34 +0200875 color = get_percent_color(percent);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200876 path = sym_ext->path;
877
878 color_fprintf(stdout, color, " %7.2f %s", percent, path);
879 node = rb_next(node);
880 }
881}
882
Ingo Molnar0b73da32009-06-06 15:48:52 +0200883static void annotate_sym(struct dso *dso, struct symbol *sym)
884{
Mike Galbraith42976482009-07-02 08:09:46 +0200885 char *filename = dso->name, *d_filename;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000886 u64 start, end, len;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200887 char command[PATH_MAX*2];
888 FILE *file;
889
890 if (!filename)
891 return;
Mike Galbraith42976482009-07-02 08:09:46 +0200892 if (sym->module)
893 filename = sym->module->path;
894 else if (dso == kernel_dso)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200895 filename = vmlinux;
896
Ingo Molnar0b73da32009-06-06 15:48:52 +0200897 start = sym->obj_start;
898 if (!start)
899 start = sym->start;
Mike Galbraith42976482009-07-02 08:09:46 +0200900 if (full_paths)
901 d_filename = filename;
902 else
903 d_filename = basename(filename);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200904
905 end = start + sym->end - sym->start + 1;
906 len = sym->end - sym->start;
907
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200908 if (print_line) {
Frederic Weisbeckerc17c2db12009-06-13 17:39:23 +0200909 get_source_line(sym, start, len, filename);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200910 print_summary(filename);
911 }
912
913 printf("\n\n------------------------------------------------\n");
Mike Galbraith42976482009-07-02 08:09:46 +0200914 printf(" Percent | Source code & Disassembly of %s\n", d_filename);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200915 printf("------------------------------------------------\n");
916
917 if (verbose >= 2)
918 printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200919
Mike Galbraith42976482009-07-02 08:09:46 +0200920 sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
921 (u64)start, (u64)end, filename, filename);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200922
923 if (verbose >= 3)
924 printf("doing: %s\n", command);
925
926 file = popen(command, "r");
927 if (!file)
928 return;
929
930 while (!feof(file)) {
931 if (parse_line(file, sym, start, len) < 0)
932 break;
933 }
934
935 pclose(file);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200936 if (print_line)
937 free_source_line(sym, len);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200938}
939
940static void find_annotations(void)
941{
942 struct rb_node *nd;
943 struct dso *dso;
944 int count = 0;
945
946 list_for_each_entry(dso, &dsos, node) {
947
948 for (nd = rb_first(&dso->syms); nd; nd = rb_next(nd)) {
949 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
950
951 if (sym->hist) {
952 annotate_sym(dso, sym);
953 count++;
954 }
955 }
956 }
957
958 if (!count)
959 printf(" Error: symbol '%s' not present amongst the samples.\n", sym_hist_filter);
960}
961
Ingo Molnar8035e422009-06-06 15:19:13 +0200962static int __cmd_annotate(void)
963{
964 int ret, rc = EXIT_FAILURE;
965 unsigned long offset = 0;
966 unsigned long head = 0;
967 struct stat stat;
968 event_t *event;
969 uint32_t size;
970 char *buf;
971
972 register_idle_thread();
973
974 input = open(input_name, O_RDONLY);
975 if (input < 0) {
976 perror("failed to open file");
977 exit(-1);
978 }
979
980 ret = fstat(input, &stat);
981 if (ret < 0) {
982 perror("failed to stat file");
983 exit(-1);
984 }
985
986 if (!stat.st_size) {
987 fprintf(stderr, "zero-sized file, nothing to do!\n");
988 exit(0);
989 }
990
991 if (load_kernel() < 0) {
992 perror("failed to load kernel symbols");
993 return EXIT_FAILURE;
994 }
995
Ingo Molnar8035e422009-06-06 15:19:13 +0200996remap:
997 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
998 MAP_SHARED, input, offset);
999 if (buf == MAP_FAILED) {
1000 perror("failed to mmap file");
1001 exit(-1);
1002 }
1003
1004more:
1005 event = (event_t *)(buf + head);
1006
1007 size = event->header.size;
1008 if (!size)
1009 size = 8;
1010
1011 if (head + event->header.size >= page_size * mmap_window) {
1012 unsigned long shift = page_size * (head / page_size);
1013 int ret;
1014
1015 ret = munmap(buf, page_size * mmap_window);
1016 assert(ret == 0);
1017
1018 offset += shift;
1019 head -= shift;
1020 goto remap;
1021 }
1022
1023 size = event->header.size;
1024
1025 dprintf("%p [%p]: event: %d\n",
1026 (void *)(offset + head),
1027 (void *)(long)event->header.size,
1028 event->header.type);
1029
1030 if (!size || process_event(event, offset, head) < 0) {
1031
1032 dprintf("%p [%p]: skipping unknown header type: %d\n",
1033 (void *)(offset + head),
1034 (void *)(long)(event->header.size),
1035 event->header.type);
1036
1037 total_unknown++;
1038
1039 /*
1040 * assume we lost track of the stream, check alignment, and
1041 * increment a single u64 in the hope to catch on again 'soon'.
1042 */
1043
1044 if (unlikely(head & 7))
1045 head &= ~7ULL;
1046
1047 size = 8;
1048 }
1049
1050 head += size;
1051
Ingo Molnarf37a2912009-07-01 12:37:06 +02001052 if (offset + head < (unsigned long)stat.st_size)
Ingo Molnar8035e422009-06-06 15:19:13 +02001053 goto more;
1054
1055 rc = EXIT_SUCCESS;
1056 close(input);
1057
1058 dprintf(" IP events: %10ld\n", total);
1059 dprintf(" mmap events: %10ld\n", total_mmap);
1060 dprintf(" comm events: %10ld\n", total_comm);
1061 dprintf(" fork events: %10ld\n", total_fork);
1062 dprintf(" unknown events: %10ld\n", total_unknown);
1063
1064 if (dump_trace)
1065 return 0;
1066
1067 if (verbose >= 3)
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +02001068 threads__fprintf(stdout, &threads);
Ingo Molnar8035e422009-06-06 15:19:13 +02001069
1070 if (verbose >= 2)
1071 dsos__fprintf(stdout);
1072
1073 collapse__resort();
1074 output__resort();
Ingo Molnar0b73da32009-06-06 15:48:52 +02001075
1076 find_annotations();
Ingo Molnar8035e422009-06-06 15:19:13 +02001077
1078 return rc;
1079}
1080
1081static const char * const annotate_usage[] = {
1082 "perf annotate [<options>] <command>",
1083 NULL
1084};
1085
1086static const struct option options[] = {
1087 OPT_STRING('i', "input", &input_name, "file",
1088 "input file name"),
Ingo Molnar23b87112009-06-06 21:25:29 +02001089 OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
Ingo Molnar0b73da32009-06-06 15:48:52 +02001090 "symbol to annotate"),
Ingo Molnar8035e422009-06-06 15:19:13 +02001091 OPT_BOOLEAN('v', "verbose", &verbose,
1092 "be more verbose (show symbol address, etc)"),
1093 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1094 "dump raw trace in ASCII"),
1095 OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
Mike Galbraith42976482009-07-02 08:09:46 +02001096 OPT_BOOLEAN('m', "modules", &modules,
1097 "load module symbols - WARNING: use only with -k and LIVE kernel"),
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001098 OPT_BOOLEAN('l', "print-line", &print_line,
1099 "print matching source lines (may be slow)"),
Mike Galbraith42976482009-07-02 08:09:46 +02001100 OPT_BOOLEAN('P', "full-paths", &full_paths,
1101 "Don't shorten the displayed pathnames"),
Ingo Molnar8035e422009-06-06 15:19:13 +02001102 OPT_END()
1103};
1104
1105static void setup_sorting(void)
1106{
1107 char *tmp, *tok, *str = strdup(sort_order);
1108
1109 for (tok = strtok_r(str, ", ", &tmp);
1110 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1111 if (sort_dimension__add(tok) < 0) {
1112 error("Unknown --sort key: `%s'", tok);
1113 usage_with_options(annotate_usage, options);
1114 }
1115 }
1116
1117 free(str);
1118}
1119
Ingo Molnarf37a2912009-07-01 12:37:06 +02001120int cmd_annotate(int argc, const char **argv, const char *prefix __used)
Ingo Molnar8035e422009-06-06 15:19:13 +02001121{
1122 symbol__init();
1123
1124 page_size = getpagesize();
1125
1126 argc = parse_options(argc, argv, options, annotate_usage, 0);
1127
1128 setup_sorting();
1129
Ingo Molnar0b73da32009-06-06 15:48:52 +02001130 if (argc) {
1131 /*
1132 * Special case: if there's an argument left then assume tha
1133 * it's a symbol filter:
1134 */
1135 if (argc > 1)
1136 usage_with_options(annotate_usage, options);
1137
1138 sym_hist_filter = argv[0];
1139 }
1140
1141 if (!sym_hist_filter)
Ingo Molnar8035e422009-06-06 15:19:13 +02001142 usage_with_options(annotate_usage, options);
1143
1144 setup_pager();
1145
1146 return __cmd_annotate();
1147}