blob: 543c4524f8c25d32d30badf69369913d048d05b2 [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"
23
24#define SHOW_KERNEL 1
25#define SHOW_USER 2
26#define SHOW_HV 4
27
28static char const *input_name = "perf.data";
Ingo Molnar8035e422009-06-06 15:19:13 +020029
Ingo Molnar0b73da32009-06-06 15:48:52 +020030static char default_sort_order[] = "comm,symbol";
Ingo Molnar8035e422009-06-06 15:19:13 +020031static char *sort_order = default_sort_order;
32
33static int input;
34static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
35
36static int dump_trace = 0;
37#define dprintf(x...) do { if (dump_trace) printf(x); } while (0)
38
Mike Galbraith42976482009-07-02 08:09:46 +020039
40static int full_paths;
41
Frederic Weisbecker301406b2009-06-13 00:11:21 +020042static int print_line;
43
Ingo Molnar8035e422009-06-06 15:19:13 +020044static unsigned long page_size;
45static unsigned long mmap_window = 32;
46
Frederic Weisbecker301406b2009-06-13 00:11:21 +020047
48struct sym_ext {
Frederic Weisbecker971738f2009-06-13 00:11:22 +020049 struct rb_node node;
Frederic Weisbecker301406b2009-06-13 00:11:21 +020050 double percent;
51 char *path;
52};
53
Ingo Molnar8035e422009-06-06 15:19:13 +020054
55struct thread {
56 struct rb_node rb_node;
57 struct list_head maps;
58 pid_t pid;
59 char *comm;
60};
61
62static struct thread *thread__new(pid_t pid)
63{
64 struct thread *self = malloc(sizeof(*self));
65
66 if (self != NULL) {
67 self->pid = pid;
68 self->comm = malloc(32);
69 if (self->comm)
70 snprintf(self->comm, 32, ":%d", self->pid);
71 INIT_LIST_HEAD(&self->maps);
72 }
73
74 return self;
75}
76
77static int thread__set_comm(struct thread *self, const char *comm)
78{
79 if (self->comm)
80 free(self->comm);
81 self->comm = strdup(comm);
82 return self->comm ? 0 : -ENOMEM;
83}
84
85static size_t thread__fprintf(struct thread *self, FILE *fp)
86{
87 struct map *pos;
88 size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm);
89
90 list_for_each_entry(pos, &self->maps, node)
91 ret += map__fprintf(pos, fp);
92
93 return ret;
94}
95
96
97static struct rb_root threads;
98static struct thread *last_match;
99
100static struct thread *threads__findnew(pid_t pid)
101{
102 struct rb_node **p = &threads.rb_node;
103 struct rb_node *parent = NULL;
104 struct thread *th;
105
106 /*
107 * Font-end cache - PID lookups come in blocks,
108 * so most of the time we dont have to look up
109 * the full rbtree:
110 */
111 if (last_match && last_match->pid == pid)
112 return last_match;
113
114 while (*p != NULL) {
115 parent = *p;
116 th = rb_entry(parent, struct thread, rb_node);
117
118 if (th->pid == pid) {
119 last_match = th;
120 return th;
121 }
122
123 if (pid < th->pid)
124 p = &(*p)->rb_left;
125 else
126 p = &(*p)->rb_right;
127 }
128
129 th = thread__new(pid);
130 if (th != NULL) {
131 rb_link_node(&th->rb_node, parent, p);
132 rb_insert_color(&th->rb_node, &threads);
133 last_match = th;
134 }
135
136 return th;
137}
138
139static void thread__insert_map(struct thread *self, struct map *map)
140{
141 struct map *pos, *tmp;
142
143 list_for_each_entry_safe(pos, tmp, &self->maps, node) {
144 if (map__overlap(pos, map)) {
145 list_del_init(&pos->node);
146 /* XXX leaks dsos */
147 free(pos);
148 }
149 }
150
151 list_add_tail(&map->node, &self->maps);
152}
153
154static int thread__fork(struct thread *self, struct thread *parent)
155{
156 struct map *map;
157
158 if (self->comm)
159 free(self->comm);
160 self->comm = strdup(parent->comm);
161 if (!self->comm)
162 return -ENOMEM;
163
164 list_for_each_entry(map, &parent->maps, node) {
165 struct map *new = map__clone(map);
166 if (!new)
167 return -ENOMEM;
168 thread__insert_map(self, new);
169 }
170
171 return 0;
172}
173
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000174static struct map *thread__find_map(struct thread *self, u64 ip)
Ingo Molnar8035e422009-06-06 15:19:13 +0200175{
176 struct map *pos;
177
178 if (self == NULL)
179 return NULL;
180
181 list_for_each_entry(pos, &self->maps, node)
182 if (ip >= pos->start && ip <= pos->end)
183 return pos;
184
185 return NULL;
186}
187
188static size_t threads__fprintf(FILE *fp)
189{
190 size_t ret = 0;
191 struct rb_node *nd;
192
193 for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
194 struct thread *pos = rb_entry(nd, struct thread, rb_node);
195
196 ret += thread__fprintf(pos, fp);
197 }
198
199 return ret;
200}
201
202/*
203 * histogram, sorted on item, collects counts
204 */
205
206static struct rb_root hist;
207
208struct hist_entry {
209 struct rb_node rb_node;
210
211 struct thread *thread;
212 struct map *map;
213 struct dso *dso;
214 struct symbol *sym;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000215 u64 ip;
Ingo Molnar8035e422009-06-06 15:19:13 +0200216 char level;
217
218 uint32_t count;
219};
220
221/*
222 * configurable sorting bits
223 */
224
225struct sort_entry {
226 struct list_head list;
227
228 char *header;
229
230 int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
231 int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
232 size_t (*print)(FILE *fp, struct hist_entry *);
233};
234
235/* --sort pid */
236
237static int64_t
238sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
239{
240 return right->thread->pid - left->thread->pid;
241}
242
243static size_t
244sort__thread_print(FILE *fp, struct hist_entry *self)
245{
246 return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid);
247}
248
249static struct sort_entry sort_thread = {
250 .header = " Command: Pid",
251 .cmp = sort__thread_cmp,
252 .print = sort__thread_print,
253};
254
255/* --sort comm */
256
257static int64_t
258sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
259{
260 return right->thread->pid - left->thread->pid;
261}
262
263static int64_t
264sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
265{
266 char *comm_l = left->thread->comm;
267 char *comm_r = right->thread->comm;
268
269 if (!comm_l || !comm_r) {
270 if (!comm_l && !comm_r)
271 return 0;
272 else if (!comm_l)
273 return -1;
274 else
275 return 1;
276 }
277
278 return strcmp(comm_l, comm_r);
279}
280
281static size_t
282sort__comm_print(FILE *fp, struct hist_entry *self)
283{
284 return fprintf(fp, "%16s", self->thread->comm);
285}
286
287static struct sort_entry sort_comm = {
288 .header = " Command",
289 .cmp = sort__comm_cmp,
290 .collapse = sort__comm_collapse,
291 .print = sort__comm_print,
292};
293
294/* --sort dso */
295
296static int64_t
297sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
298{
299 struct dso *dso_l = left->dso;
300 struct dso *dso_r = right->dso;
301
302 if (!dso_l || !dso_r) {
303 if (!dso_l && !dso_r)
304 return 0;
305 else if (!dso_l)
306 return -1;
307 else
308 return 1;
309 }
310
311 return strcmp(dso_l->name, dso_r->name);
312}
313
314static size_t
315sort__dso_print(FILE *fp, struct hist_entry *self)
316{
317 if (self->dso)
318 return fprintf(fp, "%-25s", self->dso->name);
319
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000320 return fprintf(fp, "%016llx ", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200321}
322
323static struct sort_entry sort_dso = {
324 .header = "Shared Object ",
325 .cmp = sort__dso_cmp,
326 .print = sort__dso_print,
327};
328
329/* --sort symbol */
330
331static int64_t
332sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
333{
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000334 u64 ip_l, ip_r;
Ingo Molnar8035e422009-06-06 15:19:13 +0200335
336 if (left->sym == right->sym)
337 return 0;
338
339 ip_l = left->sym ? left->sym->start : left->ip;
340 ip_r = right->sym ? right->sym->start : right->ip;
341
342 return (int64_t)(ip_r - ip_l);
343}
344
345static size_t
346sort__sym_print(FILE *fp, struct hist_entry *self)
347{
348 size_t ret = 0;
349
350 if (verbose)
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000351 ret += fprintf(fp, "%#018llx ", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200352
353 if (self->sym) {
354 ret += fprintf(fp, "[%c] %s",
355 self->dso == kernel_dso ? 'k' : '.', self->sym->name);
356 } else {
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000357 ret += fprintf(fp, "%#016llx", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200358 }
359
360 return ret;
361}
362
363static struct sort_entry sort_sym = {
364 .header = "Symbol",
365 .cmp = sort__sym_cmp,
366 .print = sort__sym_print,
367};
368
369static int sort__need_collapse = 0;
370
371struct sort_dimension {
372 char *name;
373 struct sort_entry *entry;
374 int taken;
375};
376
377static struct sort_dimension sort_dimensions[] = {
378 { .name = "pid", .entry = &sort_thread, },
379 { .name = "comm", .entry = &sort_comm, },
380 { .name = "dso", .entry = &sort_dso, },
381 { .name = "symbol", .entry = &sort_sym, },
382};
383
384static LIST_HEAD(hist_entry__sort_list);
385
386static int sort_dimension__add(char *tok)
387{
Ingo Molnarf37a2912009-07-01 12:37:06 +0200388 unsigned int i;
Ingo Molnar8035e422009-06-06 15:19:13 +0200389
390 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
391 struct sort_dimension *sd = &sort_dimensions[i];
392
393 if (sd->taken)
394 continue;
395
396 if (strncasecmp(tok, sd->name, strlen(tok)))
397 continue;
398
399 if (sd->entry->collapse)
400 sort__need_collapse = 1;
401
402 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
403 sd->taken = 1;
404
405 return 0;
406 }
407
408 return -ESRCH;
409}
410
411static int64_t
412hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
413{
414 struct sort_entry *se;
415 int64_t cmp = 0;
416
417 list_for_each_entry(se, &hist_entry__sort_list, list) {
418 cmp = se->cmp(left, right);
419 if (cmp)
420 break;
421 }
422
423 return cmp;
424}
425
426static int64_t
427hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
428{
429 struct sort_entry *se;
430 int64_t cmp = 0;
431
432 list_for_each_entry(se, &hist_entry__sort_list, list) {
433 int64_t (*f)(struct hist_entry *, struct hist_entry *);
434
435 f = se->collapse ?: se->cmp;
436
437 cmp = f(left, right);
438 if (cmp)
439 break;
440 }
441
442 return cmp;
443}
444
Ingo Molnar8035e422009-06-06 15:19:13 +0200445/*
446 * collect histogram counts
447 */
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000448static void hist_hit(struct hist_entry *he, u64 ip)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200449{
450 unsigned int sym_size, offset;
451 struct symbol *sym = he->sym;
452
453 he->count++;
454
455 if (!sym || !sym->hist)
456 return;
457
458 sym_size = sym->end - sym->start;
459 offset = ip - sym->start;
460
461 if (offset >= sym_size)
462 return;
463
464 sym->hist_sum++;
465 sym->hist[offset]++;
466
467 if (verbose >= 3)
468 printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
Arjan van de Ven7d37a0c2009-06-06 20:36:38 +0200469 (void *)(unsigned long)he->sym->start,
Ingo Molnar0b73da32009-06-06 15:48:52 +0200470 he->sym->name,
Arjan van de Ven7d37a0c2009-06-06 20:36:38 +0200471 (void *)(unsigned long)ip, ip - he->sym->start,
Ingo Molnar0b73da32009-06-06 15:48:52 +0200472 sym->hist[offset]);
473}
Ingo Molnar8035e422009-06-06 15:19:13 +0200474
475static int
476hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000477 struct symbol *sym, u64 ip, char level)
Ingo Molnar8035e422009-06-06 15:19:13 +0200478{
479 struct rb_node **p = &hist.rb_node;
480 struct rb_node *parent = NULL;
481 struct hist_entry *he;
482 struct hist_entry entry = {
483 .thread = thread,
484 .map = map,
485 .dso = dso,
486 .sym = sym,
487 .ip = ip,
488 .level = level,
489 .count = 1,
490 };
491 int cmp;
492
493 while (*p != NULL) {
494 parent = *p;
495 he = rb_entry(parent, struct hist_entry, rb_node);
496
497 cmp = hist_entry__cmp(&entry, he);
498
499 if (!cmp) {
Ingo Molnar0b73da32009-06-06 15:48:52 +0200500 hist_hit(he, ip);
501
Ingo Molnar8035e422009-06-06 15:19:13 +0200502 return 0;
503 }
504
505 if (cmp < 0)
506 p = &(*p)->rb_left;
507 else
508 p = &(*p)->rb_right;
509 }
510
511 he = malloc(sizeof(*he));
512 if (!he)
513 return -ENOMEM;
514 *he = entry;
515 rb_link_node(&he->rb_node, parent, p);
516 rb_insert_color(&he->rb_node, &hist);
517
518 return 0;
519}
520
521static void hist_entry__free(struct hist_entry *he)
522{
523 free(he);
524}
525
526/*
527 * collapse the histogram
528 */
529
530static struct rb_root collapse_hists;
531
532static void collapse__insert_entry(struct hist_entry *he)
533{
534 struct rb_node **p = &collapse_hists.rb_node;
535 struct rb_node *parent = NULL;
536 struct hist_entry *iter;
537 int64_t cmp;
538
539 while (*p != NULL) {
540 parent = *p;
541 iter = rb_entry(parent, struct hist_entry, rb_node);
542
543 cmp = hist_entry__collapse(iter, he);
544
545 if (!cmp) {
546 iter->count += he->count;
547 hist_entry__free(he);
548 return;
549 }
550
551 if (cmp < 0)
552 p = &(*p)->rb_left;
553 else
554 p = &(*p)->rb_right;
555 }
556
557 rb_link_node(&he->rb_node, parent, p);
558 rb_insert_color(&he->rb_node, &collapse_hists);
559}
560
561static void collapse__resort(void)
562{
563 struct rb_node *next;
564 struct hist_entry *n;
565
566 if (!sort__need_collapse)
567 return;
568
569 next = rb_first(&hist);
570 while (next) {
571 n = rb_entry(next, struct hist_entry, rb_node);
572 next = rb_next(&n->rb_node);
573
574 rb_erase(&n->rb_node, &hist);
575 collapse__insert_entry(n);
576 }
577}
578
579/*
580 * reverse the map, sort on count.
581 */
582
583static struct rb_root output_hists;
584
585static void output__insert_entry(struct hist_entry *he)
586{
587 struct rb_node **p = &output_hists.rb_node;
588 struct rb_node *parent = NULL;
589 struct hist_entry *iter;
590
591 while (*p != NULL) {
592 parent = *p;
593 iter = rb_entry(parent, struct hist_entry, rb_node);
594
595 if (he->count > iter->count)
596 p = &(*p)->rb_left;
597 else
598 p = &(*p)->rb_right;
599 }
600
601 rb_link_node(&he->rb_node, parent, p);
602 rb_insert_color(&he->rb_node, &output_hists);
603}
604
605static void output__resort(void)
606{
607 struct rb_node *next;
608 struct hist_entry *n;
609 struct rb_root *tree = &hist;
610
611 if (sort__need_collapse)
612 tree = &collapse_hists;
613
614 next = rb_first(tree);
615
616 while (next) {
617 n = rb_entry(next, struct hist_entry, rb_node);
618 next = rb_next(&n->rb_node);
619
620 rb_erase(&n->rb_node, tree);
621 output__insert_entry(n);
622 }
623}
624
Ingo Molnar8035e422009-06-06 15:19:13 +0200625static void register_idle_thread(void)
626{
627 struct thread *thread = threads__findnew(0);
628
629 if (thread == NULL ||
630 thread__set_comm(thread, "[idle]")) {
631 fprintf(stderr, "problem inserting idle task.\n");
632 exit(-1);
633 }
634}
635
636static unsigned long total = 0,
637 total_mmap = 0,
638 total_comm = 0,
639 total_fork = 0,
640 total_unknown = 0;
641
642static int
Peter Zijlstrae6e18ec2009-06-25 11:27:12 +0200643process_sample_event(event_t *event, unsigned long offset, unsigned long head)
Ingo Molnar8035e422009-06-06 15:19:13 +0200644{
645 char level;
646 int show = 0;
647 struct dso *dso = NULL;
648 struct thread *thread = threads__findnew(event->ip.pid);
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000649 u64 ip = event->ip.ip;
Ingo Molnar8035e422009-06-06 15:19:13 +0200650 struct map *map = NULL;
651
652 dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
653 (void *)(offset + head),
654 (void *)(long)(event->header.size),
655 event->header.misc,
656 event->ip.pid,
657 (void *)(long)ip);
658
659 dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid);
660
661 if (thread == NULL) {
662 fprintf(stderr, "problem processing %d event, skipping it.\n",
663 event->header.type);
664 return -1;
665 }
666
667 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
668 show = SHOW_KERNEL;
669 level = 'k';
670
671 dso = kernel_dso;
672
673 dprintf(" ...... dso: %s\n", dso->name);
674
675 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
676
677 show = SHOW_USER;
678 level = '.';
679
680 map = thread__find_map(thread, ip);
681 if (map != NULL) {
682 ip = map->map_ip(map, ip);
683 dso = map->dso;
684 } else {
685 /*
686 * If this is outside of all known maps,
687 * and is a negative address, try to look it
688 * up in the kernel dso, as it might be a
689 * vsyscall (which executes in user-mode):
690 */
691 if ((long long)ip < 0)
692 dso = kernel_dso;
693 }
694 dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
695
696 } else {
697 show = SHOW_HV;
698 level = 'H';
699 dprintf(" ...... dso: [hypervisor]\n");
700 }
701
702 if (show & show_mask) {
703 struct symbol *sym = NULL;
704
705 if (dso)
706 sym = dso->find_symbol(dso, ip);
707
708 if (hist_entry__add(thread, map, dso, sym, ip, level)) {
709 fprintf(stderr,
710 "problem incrementing symbol count, skipping event\n");
711 return -1;
712 }
713 }
714 total++;
715
716 return 0;
717}
718
719static int
720process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
721{
722 struct thread *thread = threads__findnew(event->mmap.pid);
Frederic Weisbecker66e274f2009-08-12 11:07:25 +0200723 struct map *map = map__new(&event->mmap, NULL, 0);
Ingo Molnar8035e422009-06-06 15:19:13 +0200724
725 dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n",
726 (void *)(offset + head),
727 (void *)(long)(event->header.size),
728 event->mmap.pid,
729 (void *)(long)event->mmap.start,
730 (void *)(long)event->mmap.len,
731 (void *)(long)event->mmap.pgoff,
732 event->mmap.filename);
733
734 if (thread == NULL || map == NULL) {
735 dprintf("problem processing PERF_EVENT_MMAP, skipping event.\n");
736 return 0;
737 }
738
739 thread__insert_map(thread, map);
740 total_mmap++;
741
742 return 0;
743}
744
745static int
746process_comm_event(event_t *event, unsigned long offset, unsigned long head)
747{
748 struct thread *thread = threads__findnew(event->comm.pid);
749
750 dprintf("%p [%p]: PERF_EVENT_COMM: %s:%d\n",
751 (void *)(offset + head),
752 (void *)(long)(event->header.size),
753 event->comm.comm, event->comm.pid);
754
755 if (thread == NULL ||
756 thread__set_comm(thread, event->comm.comm)) {
757 dprintf("problem processing PERF_EVENT_COMM, skipping event.\n");
758 return -1;
759 }
760 total_comm++;
761
762 return 0;
763}
764
765static int
766process_fork_event(event_t *event, unsigned long offset, unsigned long head)
767{
768 struct thread *thread = threads__findnew(event->fork.pid);
769 struct thread *parent = threads__findnew(event->fork.ppid);
770
771 dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n",
772 (void *)(offset + head),
773 (void *)(long)(event->header.size),
774 event->fork.pid, event->fork.ppid);
775
776 if (!thread || !parent || thread__fork(thread, parent)) {
777 dprintf("problem processing PERF_EVENT_FORK, skipping event.\n");
778 return -1;
779 }
780 total_fork++;
781
782 return 0;
783}
784
785static int
Ingo Molnar8035e422009-06-06 15:19:13 +0200786process_event(event_t *event, unsigned long offset, unsigned long head)
787{
Ingo Molnar8035e422009-06-06 15:19:13 +0200788 switch (event->header.type) {
Peter Zijlstrae6e18ec2009-06-25 11:27:12 +0200789 case PERF_EVENT_SAMPLE:
790 return process_sample_event(event, offset, head);
791
Ingo Molnar8035e422009-06-06 15:19:13 +0200792 case PERF_EVENT_MMAP:
793 return process_mmap_event(event, offset, head);
794
795 case PERF_EVENT_COMM:
796 return process_comm_event(event, offset, head);
797
798 case PERF_EVENT_FORK:
799 return process_fork_event(event, offset, head);
Ingo Molnar8035e422009-06-06 15:19:13 +0200800 /*
801 * We dont process them right now but they are fine:
802 */
803
804 case PERF_EVENT_THROTTLE:
805 case PERF_EVENT_UNTHROTTLE:
806 return 0;
807
808 default:
809 return -1;
810 }
811
812 return 0;
813}
814
Ingo Molnar0b73da32009-06-06 15:48:52 +0200815static int
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000816parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200817{
818 char *line = NULL, *tmp, *tmp2;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200819 static const char *prev_line;
820 static const char *prev_color;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200821 unsigned int offset;
822 size_t line_len;
Ingo Molnarf37a2912009-07-01 12:37:06 +0200823 s64 line_ip;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200824 int ret;
825 char *c;
826
827 if (getline(&line, &line_len, file) < 0)
828 return -1;
829 if (!line)
830 return -1;
831
832 c = strchr(line, '\n');
833 if (c)
834 *c = 0;
835
836 line_ip = -1;
837 offset = 0;
838 ret = -2;
839
840 /*
841 * Strip leading spaces:
842 */
843 tmp = line;
844 while (*tmp) {
845 if (*tmp != ' ')
846 break;
847 tmp++;
848 }
849
850 if (*tmp) {
851 /*
852 * Parse hexa addresses followed by ':'
853 */
854 line_ip = strtoull(tmp, &tmp2, 16);
855 if (*tmp2 != ':')
856 line_ip = -1;
857 }
858
859 if (line_ip != -1) {
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200860 const char *path = NULL;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200861 unsigned int hits = 0;
862 double percent = 0.0;
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200863 char *color;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200864 struct sym_ext *sym_ext = sym->priv;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200865
866 offset = line_ip - start;
867 if (offset < len)
868 hits = sym->hist[offset];
869
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200870 if (offset < len && sym_ext) {
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200871 path = sym_ext[offset].path;
872 percent = sym_ext[offset].percent;
873 } else if (sym->hist_sum)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200874 percent = 100.0 * hits / sym->hist_sum;
875
Frederic Weisbecker1e11fd82009-07-02 20:14:34 +0200876 color = get_percent_color(percent);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200877
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200878 /*
879 * Also color the filename and line if needed, with
880 * the same color than the percentage. Don't print it
881 * twice for close colored ip with the same filename:line
882 */
883 if (path) {
884 if (!prev_line || strcmp(prev_line, path)
885 || color != prev_color) {
886 color_fprintf(stdout, color, " %s", path);
887 prev_line = path;
888 prev_color = color;
889 }
890 }
891
Ingo Molnar0b73da32009-06-06 15:48:52 +0200892 color_fprintf(stdout, color, " %7.2f", percent);
893 printf(" : ");
894 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
895 } else {
896 if (!*line)
897 printf(" :\n");
898 else
899 printf(" : %s\n", line);
900 }
901
902 return 0;
903}
904
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200905static struct rb_root root_sym_ext;
906
907static void insert_source_line(struct sym_ext *sym_ext)
908{
909 struct sym_ext *iter;
910 struct rb_node **p = &root_sym_ext.rb_node;
911 struct rb_node *parent = NULL;
912
913 while (*p != NULL) {
914 parent = *p;
915 iter = rb_entry(parent, struct sym_ext, node);
916
917 if (sym_ext->percent > iter->percent)
918 p = &(*p)->rb_left;
919 else
920 p = &(*p)->rb_right;
921 }
922
923 rb_link_node(&sym_ext->node, parent, p);
924 rb_insert_color(&sym_ext->node, &root_sym_ext);
925}
926
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200927static void free_source_line(struct symbol *sym, int len)
928{
929 struct sym_ext *sym_ext = sym->priv;
930 int i;
931
932 if (!sym_ext)
933 return;
934
935 for (i = 0; i < len; i++)
936 free(sym_ext[i].path);
937 free(sym_ext);
938
939 sym->priv = NULL;
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200940 root_sym_ext = RB_ROOT;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200941}
942
943/* Get the filename:line for the colored entries */
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200944static void
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000945get_source_line(struct symbol *sym, u64 start, int len, char *filename)
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200946{
947 int i;
948 char cmd[PATH_MAX * 2];
949 struct sym_ext *sym_ext;
950
951 if (!sym->hist_sum)
952 return;
953
954 sym->priv = calloc(len, sizeof(struct sym_ext));
955 if (!sym->priv)
956 return;
957
958 sym_ext = sym->priv;
959
960 for (i = 0; i < len; i++) {
961 char *path = NULL;
962 size_t line_len;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000963 u64 offset;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200964 FILE *fp;
965
966 sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum;
967 if (sym_ext[i].percent <= 0.5)
968 continue;
969
970 offset = start + i;
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200971 sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200972 fp = popen(cmd, "r");
973 if (!fp)
974 continue;
975
976 if (getline(&path, &line_len, fp) < 0 || !line_len)
977 goto next;
978
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200979 sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200980 if (!sym_ext[i].path)
981 goto next;
982
983 strcpy(sym_ext[i].path, path);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200984 insert_source_line(&sym_ext[i]);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200985
986 next:
987 pclose(fp);
988 }
989}
990
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200991static void print_summary(char *filename)
992{
993 struct sym_ext *sym_ext;
994 struct rb_node *node;
995
996 printf("\nSorted summary for file %s\n", filename);
997 printf("----------------------------------------------\n\n");
998
999 if (RB_EMPTY_ROOT(&root_sym_ext)) {
1000 printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
1001 return;
1002 }
1003
1004 node = rb_first(&root_sym_ext);
1005 while (node) {
1006 double percent;
1007 char *color;
1008 char *path;
1009
1010 sym_ext = rb_entry(node, struct sym_ext, node);
1011 percent = sym_ext->percent;
Frederic Weisbecker1e11fd82009-07-02 20:14:34 +02001012 color = get_percent_color(percent);
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001013 path = sym_ext->path;
1014
1015 color_fprintf(stdout, color, " %7.2f %s", percent, path);
1016 node = rb_next(node);
1017 }
1018}
1019
Ingo Molnar0b73da32009-06-06 15:48:52 +02001020static void annotate_sym(struct dso *dso, struct symbol *sym)
1021{
Mike Galbraith42976482009-07-02 08:09:46 +02001022 char *filename = dso->name, *d_filename;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +10001023 u64 start, end, len;
Ingo Molnar0b73da32009-06-06 15:48:52 +02001024 char command[PATH_MAX*2];
1025 FILE *file;
1026
1027 if (!filename)
1028 return;
Mike Galbraith42976482009-07-02 08:09:46 +02001029 if (sym->module)
1030 filename = sym->module->path;
1031 else if (dso == kernel_dso)
Ingo Molnar0b73da32009-06-06 15:48:52 +02001032 filename = vmlinux;
1033
Ingo Molnar0b73da32009-06-06 15:48:52 +02001034 start = sym->obj_start;
1035 if (!start)
1036 start = sym->start;
Mike Galbraith42976482009-07-02 08:09:46 +02001037 if (full_paths)
1038 d_filename = filename;
1039 else
1040 d_filename = basename(filename);
Ingo Molnar0b73da32009-06-06 15:48:52 +02001041
1042 end = start + sym->end - sym->start + 1;
1043 len = sym->end - sym->start;
1044
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001045 if (print_line) {
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +02001046 get_source_line(sym, start, len, filename);
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001047 print_summary(filename);
1048 }
1049
1050 printf("\n\n------------------------------------------------\n");
Mike Galbraith42976482009-07-02 08:09:46 +02001051 printf(" Percent | Source code & Disassembly of %s\n", d_filename);
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001052 printf("------------------------------------------------\n");
1053
1054 if (verbose >= 2)
1055 printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name);
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001056
Mike Galbraith42976482009-07-02 08:09:46 +02001057 sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
1058 (u64)start, (u64)end, filename, filename);
Ingo Molnar0b73da32009-06-06 15:48:52 +02001059
1060 if (verbose >= 3)
1061 printf("doing: %s\n", command);
1062
1063 file = popen(command, "r");
1064 if (!file)
1065 return;
1066
1067 while (!feof(file)) {
1068 if (parse_line(file, sym, start, len) < 0)
1069 break;
1070 }
1071
1072 pclose(file);
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001073 if (print_line)
1074 free_source_line(sym, len);
Ingo Molnar0b73da32009-06-06 15:48:52 +02001075}
1076
1077static void find_annotations(void)
1078{
1079 struct rb_node *nd;
1080 struct dso *dso;
1081 int count = 0;
1082
1083 list_for_each_entry(dso, &dsos, node) {
1084
1085 for (nd = rb_first(&dso->syms); nd; nd = rb_next(nd)) {
1086 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
1087
1088 if (sym->hist) {
1089 annotate_sym(dso, sym);
1090 count++;
1091 }
1092 }
1093 }
1094
1095 if (!count)
1096 printf(" Error: symbol '%s' not present amongst the samples.\n", sym_hist_filter);
1097}
1098
Ingo Molnar8035e422009-06-06 15:19:13 +02001099static int __cmd_annotate(void)
1100{
1101 int ret, rc = EXIT_FAILURE;
1102 unsigned long offset = 0;
1103 unsigned long head = 0;
1104 struct stat stat;
1105 event_t *event;
1106 uint32_t size;
1107 char *buf;
1108
1109 register_idle_thread();
1110
1111 input = open(input_name, O_RDONLY);
1112 if (input < 0) {
1113 perror("failed to open file");
1114 exit(-1);
1115 }
1116
1117 ret = fstat(input, &stat);
1118 if (ret < 0) {
1119 perror("failed to stat file");
1120 exit(-1);
1121 }
1122
1123 if (!stat.st_size) {
1124 fprintf(stderr, "zero-sized file, nothing to do!\n");
1125 exit(0);
1126 }
1127
1128 if (load_kernel() < 0) {
1129 perror("failed to load kernel symbols");
1130 return EXIT_FAILURE;
1131 }
1132
Ingo Molnar8035e422009-06-06 15:19:13 +02001133remap:
1134 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
1135 MAP_SHARED, input, offset);
1136 if (buf == MAP_FAILED) {
1137 perror("failed to mmap file");
1138 exit(-1);
1139 }
1140
1141more:
1142 event = (event_t *)(buf + head);
1143
1144 size = event->header.size;
1145 if (!size)
1146 size = 8;
1147
1148 if (head + event->header.size >= page_size * mmap_window) {
1149 unsigned long shift = page_size * (head / page_size);
1150 int ret;
1151
1152 ret = munmap(buf, page_size * mmap_window);
1153 assert(ret == 0);
1154
1155 offset += shift;
1156 head -= shift;
1157 goto remap;
1158 }
1159
1160 size = event->header.size;
1161
1162 dprintf("%p [%p]: event: %d\n",
1163 (void *)(offset + head),
1164 (void *)(long)event->header.size,
1165 event->header.type);
1166
1167 if (!size || process_event(event, offset, head) < 0) {
1168
1169 dprintf("%p [%p]: skipping unknown header type: %d\n",
1170 (void *)(offset + head),
1171 (void *)(long)(event->header.size),
1172 event->header.type);
1173
1174 total_unknown++;
1175
1176 /*
1177 * assume we lost track of the stream, check alignment, and
1178 * increment a single u64 in the hope to catch on again 'soon'.
1179 */
1180
1181 if (unlikely(head & 7))
1182 head &= ~7ULL;
1183
1184 size = 8;
1185 }
1186
1187 head += size;
1188
Ingo Molnarf37a2912009-07-01 12:37:06 +02001189 if (offset + head < (unsigned long)stat.st_size)
Ingo Molnar8035e422009-06-06 15:19:13 +02001190 goto more;
1191
1192 rc = EXIT_SUCCESS;
1193 close(input);
1194
1195 dprintf(" IP events: %10ld\n", total);
1196 dprintf(" mmap events: %10ld\n", total_mmap);
1197 dprintf(" comm events: %10ld\n", total_comm);
1198 dprintf(" fork events: %10ld\n", total_fork);
1199 dprintf(" unknown events: %10ld\n", total_unknown);
1200
1201 if (dump_trace)
1202 return 0;
1203
1204 if (verbose >= 3)
1205 threads__fprintf(stdout);
1206
1207 if (verbose >= 2)
1208 dsos__fprintf(stdout);
1209
1210 collapse__resort();
1211 output__resort();
Ingo Molnar0b73da32009-06-06 15:48:52 +02001212
1213 find_annotations();
Ingo Molnar8035e422009-06-06 15:19:13 +02001214
1215 return rc;
1216}
1217
1218static const char * const annotate_usage[] = {
1219 "perf annotate [<options>] <command>",
1220 NULL
1221};
1222
1223static const struct option options[] = {
1224 OPT_STRING('i', "input", &input_name, "file",
1225 "input file name"),
Ingo Molnar23b87112009-06-06 21:25:29 +02001226 OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
Ingo Molnar0b73da32009-06-06 15:48:52 +02001227 "symbol to annotate"),
Ingo Molnar8035e422009-06-06 15:19:13 +02001228 OPT_BOOLEAN('v', "verbose", &verbose,
1229 "be more verbose (show symbol address, etc)"),
1230 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1231 "dump raw trace in ASCII"),
1232 OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
Mike Galbraith42976482009-07-02 08:09:46 +02001233 OPT_BOOLEAN('m', "modules", &modules,
1234 "load module symbols - WARNING: use only with -k and LIVE kernel"),
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001235 OPT_BOOLEAN('l', "print-line", &print_line,
1236 "print matching source lines (may be slow)"),
Mike Galbraith42976482009-07-02 08:09:46 +02001237 OPT_BOOLEAN('P', "full-paths", &full_paths,
1238 "Don't shorten the displayed pathnames"),
Ingo Molnar8035e422009-06-06 15:19:13 +02001239 OPT_END()
1240};
1241
1242static void setup_sorting(void)
1243{
1244 char *tmp, *tok, *str = strdup(sort_order);
1245
1246 for (tok = strtok_r(str, ", ", &tmp);
1247 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1248 if (sort_dimension__add(tok) < 0) {
1249 error("Unknown --sort key: `%s'", tok);
1250 usage_with_options(annotate_usage, options);
1251 }
1252 }
1253
1254 free(str);
1255}
1256
Ingo Molnarf37a2912009-07-01 12:37:06 +02001257int cmd_annotate(int argc, const char **argv, const char *prefix __used)
Ingo Molnar8035e422009-06-06 15:19:13 +02001258{
1259 symbol__init();
1260
1261 page_size = getpagesize();
1262
1263 argc = parse_options(argc, argv, options, annotate_usage, 0);
1264
1265 setup_sorting();
1266
Ingo Molnar0b73da32009-06-06 15:48:52 +02001267 if (argc) {
1268 /*
1269 * Special case: if there's an argument left then assume tha
1270 * it's a symbol filter:
1271 */
1272 if (argc > 1)
1273 usage_with_options(annotate_usage, options);
1274
1275 sym_hist_filter = argv[0];
1276 }
1277
1278 if (!sym_hist_filter)
Ingo Molnar8035e422009-06-06 15:19:13 +02001279 usage_with_options(annotate_usage, options);
1280
1281 setup_pager();
1282
1283 return __cmd_annotate();
1284}