blob: 1a792990031a63e9095351035f81d2e94c2cb890 [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
47struct ip_event {
48 struct perf_event_header header;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +100049 u64 ip;
50 u32 pid, tid;
Ingo Molnar8035e422009-06-06 15:19:13 +020051};
52
53struct mmap_event {
54 struct perf_event_header header;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +100055 u32 pid, tid;
56 u64 start;
57 u64 len;
58 u64 pgoff;
Ingo Molnar8035e422009-06-06 15:19:13 +020059 char filename[PATH_MAX];
60};
61
62struct comm_event {
63 struct perf_event_header header;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +100064 u32 pid, tid;
Ingo Molnar8035e422009-06-06 15:19:13 +020065 char comm[16];
66};
67
68struct fork_event {
69 struct perf_event_header header;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +100070 u32 pid, ppid;
Ingo Molnar8035e422009-06-06 15:19:13 +020071};
72
Ingo Molnar8035e422009-06-06 15:19:13 +020073typedef union event_union {
74 struct perf_event_header header;
75 struct ip_event ip;
76 struct mmap_event mmap;
77 struct comm_event comm;
78 struct fork_event fork;
Ingo Molnar8035e422009-06-06 15:19:13 +020079} event_t;
80
Frederic Weisbecker301406b2009-06-13 00:11:21 +020081
82struct sym_ext {
Frederic Weisbecker971738f2009-06-13 00:11:22 +020083 struct rb_node node;
Frederic Weisbecker301406b2009-06-13 00:11:21 +020084 double percent;
85 char *path;
86};
87
Ingo Molnar8035e422009-06-06 15:19:13 +020088struct map {
89 struct list_head node;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +100090 u64 start;
91 u64 end;
92 u64 pgoff;
93 u64 (*map_ip)(struct map *, u64);
Ingo Molnar8035e422009-06-06 15:19:13 +020094 struct dso *dso;
95};
96
Paul Mackerras9cffa8d2009-06-19 22:21:42 +100097static u64 map__map_ip(struct map *map, u64 ip)
Ingo Molnar8035e422009-06-06 15:19:13 +020098{
99 return ip - map->start + map->pgoff;
100}
101
Ingo Molnarf37a2912009-07-01 12:37:06 +0200102static u64 vdso__map_ip(struct map *map __used, u64 ip)
Ingo Molnar8035e422009-06-06 15:19:13 +0200103{
104 return ip;
105}
106
107static struct map *map__new(struct mmap_event *event)
108{
109 struct map *self = malloc(sizeof(*self));
110
111 if (self != NULL) {
112 const char *filename = event->filename;
Ingo Molnar8035e422009-06-06 15:19:13 +0200113
114 self->start = event->start;
115 self->end = event->start + event->len;
116 self->pgoff = event->pgoff;
117
118 self->dso = dsos__findnew(filename);
119 if (self->dso == NULL)
120 goto out_delete;
121
122 if (self->dso == vdso)
123 self->map_ip = vdso__map_ip;
124 else
125 self->map_ip = map__map_ip;
126 }
127 return self;
128out_delete:
129 free(self);
130 return NULL;
131}
132
133static struct map *map__clone(struct map *self)
134{
135 struct map *map = malloc(sizeof(*self));
136
137 if (!map)
138 return NULL;
139
140 memcpy(map, self, sizeof(*self));
141
142 return map;
143}
144
145static int map__overlap(struct map *l, struct map *r)
146{
147 if (l->start > r->start) {
148 struct map *t = l;
149 l = r;
150 r = t;
151 }
152
153 if (l->end > r->start)
154 return 1;
155
156 return 0;
157}
158
159static size_t map__fprintf(struct map *self, FILE *fp)
160{
Ingo Molnar729ff5e22009-06-11 14:16:15 +0200161 return fprintf(fp, " %Lx-%Lx %Lx %s\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200162 self->start, self->end, self->pgoff, self->dso->name);
163}
164
165
166struct thread {
167 struct rb_node rb_node;
168 struct list_head maps;
169 pid_t pid;
170 char *comm;
171};
172
173static struct thread *thread__new(pid_t pid)
174{
175 struct thread *self = malloc(sizeof(*self));
176
177 if (self != NULL) {
178 self->pid = pid;
179 self->comm = malloc(32);
180 if (self->comm)
181 snprintf(self->comm, 32, ":%d", self->pid);
182 INIT_LIST_HEAD(&self->maps);
183 }
184
185 return self;
186}
187
188static int thread__set_comm(struct thread *self, const char *comm)
189{
190 if (self->comm)
191 free(self->comm);
192 self->comm = strdup(comm);
193 return self->comm ? 0 : -ENOMEM;
194}
195
196static size_t thread__fprintf(struct thread *self, FILE *fp)
197{
198 struct map *pos;
199 size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm);
200
201 list_for_each_entry(pos, &self->maps, node)
202 ret += map__fprintf(pos, fp);
203
204 return ret;
205}
206
207
208static struct rb_root threads;
209static struct thread *last_match;
210
211static struct thread *threads__findnew(pid_t pid)
212{
213 struct rb_node **p = &threads.rb_node;
214 struct rb_node *parent = NULL;
215 struct thread *th;
216
217 /*
218 * Font-end cache - PID lookups come in blocks,
219 * so most of the time we dont have to look up
220 * the full rbtree:
221 */
222 if (last_match && last_match->pid == pid)
223 return last_match;
224
225 while (*p != NULL) {
226 parent = *p;
227 th = rb_entry(parent, struct thread, rb_node);
228
229 if (th->pid == pid) {
230 last_match = th;
231 return th;
232 }
233
234 if (pid < th->pid)
235 p = &(*p)->rb_left;
236 else
237 p = &(*p)->rb_right;
238 }
239
240 th = thread__new(pid);
241 if (th != NULL) {
242 rb_link_node(&th->rb_node, parent, p);
243 rb_insert_color(&th->rb_node, &threads);
244 last_match = th;
245 }
246
247 return th;
248}
249
250static void thread__insert_map(struct thread *self, struct map *map)
251{
252 struct map *pos, *tmp;
253
254 list_for_each_entry_safe(pos, tmp, &self->maps, node) {
255 if (map__overlap(pos, map)) {
256 list_del_init(&pos->node);
257 /* XXX leaks dsos */
258 free(pos);
259 }
260 }
261
262 list_add_tail(&map->node, &self->maps);
263}
264
265static int thread__fork(struct thread *self, struct thread *parent)
266{
267 struct map *map;
268
269 if (self->comm)
270 free(self->comm);
271 self->comm = strdup(parent->comm);
272 if (!self->comm)
273 return -ENOMEM;
274
275 list_for_each_entry(map, &parent->maps, node) {
276 struct map *new = map__clone(map);
277 if (!new)
278 return -ENOMEM;
279 thread__insert_map(self, new);
280 }
281
282 return 0;
283}
284
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000285static struct map *thread__find_map(struct thread *self, u64 ip)
Ingo Molnar8035e422009-06-06 15:19:13 +0200286{
287 struct map *pos;
288
289 if (self == NULL)
290 return NULL;
291
292 list_for_each_entry(pos, &self->maps, node)
293 if (ip >= pos->start && ip <= pos->end)
294 return pos;
295
296 return NULL;
297}
298
299static size_t threads__fprintf(FILE *fp)
300{
301 size_t ret = 0;
302 struct rb_node *nd;
303
304 for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
305 struct thread *pos = rb_entry(nd, struct thread, rb_node);
306
307 ret += thread__fprintf(pos, fp);
308 }
309
310 return ret;
311}
312
313/*
314 * histogram, sorted on item, collects counts
315 */
316
317static struct rb_root hist;
318
319struct hist_entry {
320 struct rb_node rb_node;
321
322 struct thread *thread;
323 struct map *map;
324 struct dso *dso;
325 struct symbol *sym;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000326 u64 ip;
Ingo Molnar8035e422009-06-06 15:19:13 +0200327 char level;
328
329 uint32_t count;
330};
331
332/*
333 * configurable sorting bits
334 */
335
336struct sort_entry {
337 struct list_head list;
338
339 char *header;
340
341 int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
342 int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
343 size_t (*print)(FILE *fp, struct hist_entry *);
344};
345
346/* --sort pid */
347
348static int64_t
349sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
350{
351 return right->thread->pid - left->thread->pid;
352}
353
354static size_t
355sort__thread_print(FILE *fp, struct hist_entry *self)
356{
357 return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid);
358}
359
360static struct sort_entry sort_thread = {
361 .header = " Command: Pid",
362 .cmp = sort__thread_cmp,
363 .print = sort__thread_print,
364};
365
366/* --sort comm */
367
368static int64_t
369sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
370{
371 return right->thread->pid - left->thread->pid;
372}
373
374static int64_t
375sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
376{
377 char *comm_l = left->thread->comm;
378 char *comm_r = right->thread->comm;
379
380 if (!comm_l || !comm_r) {
381 if (!comm_l && !comm_r)
382 return 0;
383 else if (!comm_l)
384 return -1;
385 else
386 return 1;
387 }
388
389 return strcmp(comm_l, comm_r);
390}
391
392static size_t
393sort__comm_print(FILE *fp, struct hist_entry *self)
394{
395 return fprintf(fp, "%16s", self->thread->comm);
396}
397
398static struct sort_entry sort_comm = {
399 .header = " Command",
400 .cmp = sort__comm_cmp,
401 .collapse = sort__comm_collapse,
402 .print = sort__comm_print,
403};
404
405/* --sort dso */
406
407static int64_t
408sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
409{
410 struct dso *dso_l = left->dso;
411 struct dso *dso_r = right->dso;
412
413 if (!dso_l || !dso_r) {
414 if (!dso_l && !dso_r)
415 return 0;
416 else if (!dso_l)
417 return -1;
418 else
419 return 1;
420 }
421
422 return strcmp(dso_l->name, dso_r->name);
423}
424
425static size_t
426sort__dso_print(FILE *fp, struct hist_entry *self)
427{
428 if (self->dso)
429 return fprintf(fp, "%-25s", self->dso->name);
430
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000431 return fprintf(fp, "%016llx ", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200432}
433
434static struct sort_entry sort_dso = {
435 .header = "Shared Object ",
436 .cmp = sort__dso_cmp,
437 .print = sort__dso_print,
438};
439
440/* --sort symbol */
441
442static int64_t
443sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
444{
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000445 u64 ip_l, ip_r;
Ingo Molnar8035e422009-06-06 15:19:13 +0200446
447 if (left->sym == right->sym)
448 return 0;
449
450 ip_l = left->sym ? left->sym->start : left->ip;
451 ip_r = right->sym ? right->sym->start : right->ip;
452
453 return (int64_t)(ip_r - ip_l);
454}
455
456static size_t
457sort__sym_print(FILE *fp, struct hist_entry *self)
458{
459 size_t ret = 0;
460
461 if (verbose)
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000462 ret += fprintf(fp, "%#018llx ", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200463
464 if (self->sym) {
465 ret += fprintf(fp, "[%c] %s",
466 self->dso == kernel_dso ? 'k' : '.', self->sym->name);
467 } else {
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000468 ret += fprintf(fp, "%#016llx", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200469 }
470
471 return ret;
472}
473
474static struct sort_entry sort_sym = {
475 .header = "Symbol",
476 .cmp = sort__sym_cmp,
477 .print = sort__sym_print,
478};
479
480static int sort__need_collapse = 0;
481
482struct sort_dimension {
483 char *name;
484 struct sort_entry *entry;
485 int taken;
486};
487
488static struct sort_dimension sort_dimensions[] = {
489 { .name = "pid", .entry = &sort_thread, },
490 { .name = "comm", .entry = &sort_comm, },
491 { .name = "dso", .entry = &sort_dso, },
492 { .name = "symbol", .entry = &sort_sym, },
493};
494
495static LIST_HEAD(hist_entry__sort_list);
496
497static int sort_dimension__add(char *tok)
498{
Ingo Molnarf37a2912009-07-01 12:37:06 +0200499 unsigned int i;
Ingo Molnar8035e422009-06-06 15:19:13 +0200500
501 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
502 struct sort_dimension *sd = &sort_dimensions[i];
503
504 if (sd->taken)
505 continue;
506
507 if (strncasecmp(tok, sd->name, strlen(tok)))
508 continue;
509
510 if (sd->entry->collapse)
511 sort__need_collapse = 1;
512
513 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
514 sd->taken = 1;
515
516 return 0;
517 }
518
519 return -ESRCH;
520}
521
522static int64_t
523hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
524{
525 struct sort_entry *se;
526 int64_t cmp = 0;
527
528 list_for_each_entry(se, &hist_entry__sort_list, list) {
529 cmp = se->cmp(left, right);
530 if (cmp)
531 break;
532 }
533
534 return cmp;
535}
536
537static int64_t
538hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
539{
540 struct sort_entry *se;
541 int64_t cmp = 0;
542
543 list_for_each_entry(se, &hist_entry__sort_list, list) {
544 int64_t (*f)(struct hist_entry *, struct hist_entry *);
545
546 f = se->collapse ?: se->cmp;
547
548 cmp = f(left, right);
549 if (cmp)
550 break;
551 }
552
553 return cmp;
554}
555
Ingo Molnar8035e422009-06-06 15:19:13 +0200556/*
557 * collect histogram counts
558 */
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000559static void hist_hit(struct hist_entry *he, u64 ip)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200560{
561 unsigned int sym_size, offset;
562 struct symbol *sym = he->sym;
563
564 he->count++;
565
566 if (!sym || !sym->hist)
567 return;
568
569 sym_size = sym->end - sym->start;
570 offset = ip - sym->start;
571
572 if (offset >= sym_size)
573 return;
574
575 sym->hist_sum++;
576 sym->hist[offset]++;
577
578 if (verbose >= 3)
579 printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
Arjan van de Ven7d37a0c2009-06-06 20:36:38 +0200580 (void *)(unsigned long)he->sym->start,
Ingo Molnar0b73da32009-06-06 15:48:52 +0200581 he->sym->name,
Arjan van de Ven7d37a0c2009-06-06 20:36:38 +0200582 (void *)(unsigned long)ip, ip - he->sym->start,
Ingo Molnar0b73da32009-06-06 15:48:52 +0200583 sym->hist[offset]);
584}
Ingo Molnar8035e422009-06-06 15:19:13 +0200585
586static int
587hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000588 struct symbol *sym, u64 ip, char level)
Ingo Molnar8035e422009-06-06 15:19:13 +0200589{
590 struct rb_node **p = &hist.rb_node;
591 struct rb_node *parent = NULL;
592 struct hist_entry *he;
593 struct hist_entry entry = {
594 .thread = thread,
595 .map = map,
596 .dso = dso,
597 .sym = sym,
598 .ip = ip,
599 .level = level,
600 .count = 1,
601 };
602 int cmp;
603
604 while (*p != NULL) {
605 parent = *p;
606 he = rb_entry(parent, struct hist_entry, rb_node);
607
608 cmp = hist_entry__cmp(&entry, he);
609
610 if (!cmp) {
Ingo Molnar0b73da32009-06-06 15:48:52 +0200611 hist_hit(he, ip);
612
Ingo Molnar8035e422009-06-06 15:19:13 +0200613 return 0;
614 }
615
616 if (cmp < 0)
617 p = &(*p)->rb_left;
618 else
619 p = &(*p)->rb_right;
620 }
621
622 he = malloc(sizeof(*he));
623 if (!he)
624 return -ENOMEM;
625 *he = entry;
626 rb_link_node(&he->rb_node, parent, p);
627 rb_insert_color(&he->rb_node, &hist);
628
629 return 0;
630}
631
632static void hist_entry__free(struct hist_entry *he)
633{
634 free(he);
635}
636
637/*
638 * collapse the histogram
639 */
640
641static struct rb_root collapse_hists;
642
643static void collapse__insert_entry(struct hist_entry *he)
644{
645 struct rb_node **p = &collapse_hists.rb_node;
646 struct rb_node *parent = NULL;
647 struct hist_entry *iter;
648 int64_t cmp;
649
650 while (*p != NULL) {
651 parent = *p;
652 iter = rb_entry(parent, struct hist_entry, rb_node);
653
654 cmp = hist_entry__collapse(iter, he);
655
656 if (!cmp) {
657 iter->count += he->count;
658 hist_entry__free(he);
659 return;
660 }
661
662 if (cmp < 0)
663 p = &(*p)->rb_left;
664 else
665 p = &(*p)->rb_right;
666 }
667
668 rb_link_node(&he->rb_node, parent, p);
669 rb_insert_color(&he->rb_node, &collapse_hists);
670}
671
672static void collapse__resort(void)
673{
674 struct rb_node *next;
675 struct hist_entry *n;
676
677 if (!sort__need_collapse)
678 return;
679
680 next = rb_first(&hist);
681 while (next) {
682 n = rb_entry(next, struct hist_entry, rb_node);
683 next = rb_next(&n->rb_node);
684
685 rb_erase(&n->rb_node, &hist);
686 collapse__insert_entry(n);
687 }
688}
689
690/*
691 * reverse the map, sort on count.
692 */
693
694static struct rb_root output_hists;
695
696static void output__insert_entry(struct hist_entry *he)
697{
698 struct rb_node **p = &output_hists.rb_node;
699 struct rb_node *parent = NULL;
700 struct hist_entry *iter;
701
702 while (*p != NULL) {
703 parent = *p;
704 iter = rb_entry(parent, struct hist_entry, rb_node);
705
706 if (he->count > iter->count)
707 p = &(*p)->rb_left;
708 else
709 p = &(*p)->rb_right;
710 }
711
712 rb_link_node(&he->rb_node, parent, p);
713 rb_insert_color(&he->rb_node, &output_hists);
714}
715
716static void output__resort(void)
717{
718 struct rb_node *next;
719 struct hist_entry *n;
720 struct rb_root *tree = &hist;
721
722 if (sort__need_collapse)
723 tree = &collapse_hists;
724
725 next = rb_first(tree);
726
727 while (next) {
728 n = rb_entry(next, struct hist_entry, rb_node);
729 next = rb_next(&n->rb_node);
730
731 rb_erase(&n->rb_node, tree);
732 output__insert_entry(n);
733 }
734}
735
Ingo Molnar8035e422009-06-06 15:19:13 +0200736static void register_idle_thread(void)
737{
738 struct thread *thread = threads__findnew(0);
739
740 if (thread == NULL ||
741 thread__set_comm(thread, "[idle]")) {
742 fprintf(stderr, "problem inserting idle task.\n");
743 exit(-1);
744 }
745}
746
747static unsigned long total = 0,
748 total_mmap = 0,
749 total_comm = 0,
750 total_fork = 0,
751 total_unknown = 0;
752
753static int
Peter Zijlstrae6e18ec2009-06-25 11:27:12 +0200754process_sample_event(event_t *event, unsigned long offset, unsigned long head)
Ingo Molnar8035e422009-06-06 15:19:13 +0200755{
756 char level;
757 int show = 0;
758 struct dso *dso = NULL;
759 struct thread *thread = threads__findnew(event->ip.pid);
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000760 u64 ip = event->ip.ip;
Ingo Molnar8035e422009-06-06 15:19:13 +0200761 struct map *map = NULL;
762
763 dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
764 (void *)(offset + head),
765 (void *)(long)(event->header.size),
766 event->header.misc,
767 event->ip.pid,
768 (void *)(long)ip);
769
770 dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid);
771
772 if (thread == NULL) {
773 fprintf(stderr, "problem processing %d event, skipping it.\n",
774 event->header.type);
775 return -1;
776 }
777
778 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
779 show = SHOW_KERNEL;
780 level = 'k';
781
782 dso = kernel_dso;
783
784 dprintf(" ...... dso: %s\n", dso->name);
785
786 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
787
788 show = SHOW_USER;
789 level = '.';
790
791 map = thread__find_map(thread, ip);
792 if (map != NULL) {
793 ip = map->map_ip(map, ip);
794 dso = map->dso;
795 } else {
796 /*
797 * If this is outside of all known maps,
798 * and is a negative address, try to look it
799 * up in the kernel dso, as it might be a
800 * vsyscall (which executes in user-mode):
801 */
802 if ((long long)ip < 0)
803 dso = kernel_dso;
804 }
805 dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
806
807 } else {
808 show = SHOW_HV;
809 level = 'H';
810 dprintf(" ...... dso: [hypervisor]\n");
811 }
812
813 if (show & show_mask) {
814 struct symbol *sym = NULL;
815
816 if (dso)
817 sym = dso->find_symbol(dso, ip);
818
819 if (hist_entry__add(thread, map, dso, sym, ip, level)) {
820 fprintf(stderr,
821 "problem incrementing symbol count, skipping event\n");
822 return -1;
823 }
824 }
825 total++;
826
827 return 0;
828}
829
830static int
831process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
832{
833 struct thread *thread = threads__findnew(event->mmap.pid);
834 struct map *map = map__new(&event->mmap);
835
836 dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n",
837 (void *)(offset + head),
838 (void *)(long)(event->header.size),
839 event->mmap.pid,
840 (void *)(long)event->mmap.start,
841 (void *)(long)event->mmap.len,
842 (void *)(long)event->mmap.pgoff,
843 event->mmap.filename);
844
845 if (thread == NULL || map == NULL) {
846 dprintf("problem processing PERF_EVENT_MMAP, skipping event.\n");
847 return 0;
848 }
849
850 thread__insert_map(thread, map);
851 total_mmap++;
852
853 return 0;
854}
855
856static int
857process_comm_event(event_t *event, unsigned long offset, unsigned long head)
858{
859 struct thread *thread = threads__findnew(event->comm.pid);
860
861 dprintf("%p [%p]: PERF_EVENT_COMM: %s:%d\n",
862 (void *)(offset + head),
863 (void *)(long)(event->header.size),
864 event->comm.comm, event->comm.pid);
865
866 if (thread == NULL ||
867 thread__set_comm(thread, event->comm.comm)) {
868 dprintf("problem processing PERF_EVENT_COMM, skipping event.\n");
869 return -1;
870 }
871 total_comm++;
872
873 return 0;
874}
875
876static int
877process_fork_event(event_t *event, unsigned long offset, unsigned long head)
878{
879 struct thread *thread = threads__findnew(event->fork.pid);
880 struct thread *parent = threads__findnew(event->fork.ppid);
881
882 dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n",
883 (void *)(offset + head),
884 (void *)(long)(event->header.size),
885 event->fork.pid, event->fork.ppid);
886
887 if (!thread || !parent || thread__fork(thread, parent)) {
888 dprintf("problem processing PERF_EVENT_FORK, skipping event.\n");
889 return -1;
890 }
891 total_fork++;
892
893 return 0;
894}
895
896static int
Ingo Molnar8035e422009-06-06 15:19:13 +0200897process_event(event_t *event, unsigned long offset, unsigned long head)
898{
Ingo Molnar8035e422009-06-06 15:19:13 +0200899 switch (event->header.type) {
Peter Zijlstrae6e18ec2009-06-25 11:27:12 +0200900 case PERF_EVENT_SAMPLE:
901 return process_sample_event(event, offset, head);
902
Ingo Molnar8035e422009-06-06 15:19:13 +0200903 case PERF_EVENT_MMAP:
904 return process_mmap_event(event, offset, head);
905
906 case PERF_EVENT_COMM:
907 return process_comm_event(event, offset, head);
908
909 case PERF_EVENT_FORK:
910 return process_fork_event(event, offset, head);
Ingo Molnar8035e422009-06-06 15:19:13 +0200911 /*
912 * We dont process them right now but they are fine:
913 */
914
915 case PERF_EVENT_THROTTLE:
916 case PERF_EVENT_UNTHROTTLE:
917 return 0;
918
919 default:
920 return -1;
921 }
922
923 return 0;
924}
925
Ingo Molnar0b73da32009-06-06 15:48:52 +0200926static int
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000927parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200928{
929 char *line = NULL, *tmp, *tmp2;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200930 static const char *prev_line;
931 static const char *prev_color;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200932 unsigned int offset;
933 size_t line_len;
Ingo Molnarf37a2912009-07-01 12:37:06 +0200934 s64 line_ip;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200935 int ret;
936 char *c;
937
938 if (getline(&line, &line_len, file) < 0)
939 return -1;
940 if (!line)
941 return -1;
942
943 c = strchr(line, '\n');
944 if (c)
945 *c = 0;
946
947 line_ip = -1;
948 offset = 0;
949 ret = -2;
950
951 /*
952 * Strip leading spaces:
953 */
954 tmp = line;
955 while (*tmp) {
956 if (*tmp != ' ')
957 break;
958 tmp++;
959 }
960
961 if (*tmp) {
962 /*
963 * Parse hexa addresses followed by ':'
964 */
965 line_ip = strtoull(tmp, &tmp2, 16);
966 if (*tmp2 != ':')
967 line_ip = -1;
968 }
969
970 if (line_ip != -1) {
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200971 const char *path = NULL;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200972 unsigned int hits = 0;
973 double percent = 0.0;
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200974 char *color;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200975 struct sym_ext *sym_ext = sym->priv;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200976
977 offset = line_ip - start;
978 if (offset < len)
979 hits = sym->hist[offset];
980
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200981 if (offset < len && sym_ext) {
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200982 path = sym_ext[offset].path;
983 percent = sym_ext[offset].percent;
984 } else if (sym->hist_sum)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200985 percent = 100.0 * hits / sym->hist_sum;
986
Frederic Weisbecker1e11fd82009-07-02 20:14:34 +0200987 color = get_percent_color(percent);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200988
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200989 /*
990 * Also color the filename and line if needed, with
991 * the same color than the percentage. Don't print it
992 * twice for close colored ip with the same filename:line
993 */
994 if (path) {
995 if (!prev_line || strcmp(prev_line, path)
996 || color != prev_color) {
997 color_fprintf(stdout, color, " %s", path);
998 prev_line = path;
999 prev_color = color;
1000 }
1001 }
1002
Ingo Molnar0b73da32009-06-06 15:48:52 +02001003 color_fprintf(stdout, color, " %7.2f", percent);
1004 printf(" : ");
1005 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
1006 } else {
1007 if (!*line)
1008 printf(" :\n");
1009 else
1010 printf(" : %s\n", line);
1011 }
1012
1013 return 0;
1014}
1015
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001016static struct rb_root root_sym_ext;
1017
1018static void insert_source_line(struct sym_ext *sym_ext)
1019{
1020 struct sym_ext *iter;
1021 struct rb_node **p = &root_sym_ext.rb_node;
1022 struct rb_node *parent = NULL;
1023
1024 while (*p != NULL) {
1025 parent = *p;
1026 iter = rb_entry(parent, struct sym_ext, node);
1027
1028 if (sym_ext->percent > iter->percent)
1029 p = &(*p)->rb_left;
1030 else
1031 p = &(*p)->rb_right;
1032 }
1033
1034 rb_link_node(&sym_ext->node, parent, p);
1035 rb_insert_color(&sym_ext->node, &root_sym_ext);
1036}
1037
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001038static void free_source_line(struct symbol *sym, int len)
1039{
1040 struct sym_ext *sym_ext = sym->priv;
1041 int i;
1042
1043 if (!sym_ext)
1044 return;
1045
1046 for (i = 0; i < len; i++)
1047 free(sym_ext[i].path);
1048 free(sym_ext);
1049
1050 sym->priv = NULL;
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001051 root_sym_ext = RB_ROOT;
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001052}
1053
1054/* Get the filename:line for the colored entries */
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +02001055static void
Paul Mackerras9cffa8d2009-06-19 22:21:42 +10001056get_source_line(struct symbol *sym, u64 start, int len, char *filename)
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001057{
1058 int i;
1059 char cmd[PATH_MAX * 2];
1060 struct sym_ext *sym_ext;
1061
1062 if (!sym->hist_sum)
1063 return;
1064
1065 sym->priv = calloc(len, sizeof(struct sym_ext));
1066 if (!sym->priv)
1067 return;
1068
1069 sym_ext = sym->priv;
1070
1071 for (i = 0; i < len; i++) {
1072 char *path = NULL;
1073 size_t line_len;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +10001074 u64 offset;
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001075 FILE *fp;
1076
1077 sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum;
1078 if (sym_ext[i].percent <= 0.5)
1079 continue;
1080
1081 offset = start + i;
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +02001082 sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001083 fp = popen(cmd, "r");
1084 if (!fp)
1085 continue;
1086
1087 if (getline(&path, &line_len, fp) < 0 || !line_len)
1088 goto next;
1089
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +02001090 sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001091 if (!sym_ext[i].path)
1092 goto next;
1093
1094 strcpy(sym_ext[i].path, path);
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001095 insert_source_line(&sym_ext[i]);
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001096
1097 next:
1098 pclose(fp);
1099 }
1100}
1101
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001102static void print_summary(char *filename)
1103{
1104 struct sym_ext *sym_ext;
1105 struct rb_node *node;
1106
1107 printf("\nSorted summary for file %s\n", filename);
1108 printf("----------------------------------------------\n\n");
1109
1110 if (RB_EMPTY_ROOT(&root_sym_ext)) {
1111 printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
1112 return;
1113 }
1114
1115 node = rb_first(&root_sym_ext);
1116 while (node) {
1117 double percent;
1118 char *color;
1119 char *path;
1120
1121 sym_ext = rb_entry(node, struct sym_ext, node);
1122 percent = sym_ext->percent;
Frederic Weisbecker1e11fd82009-07-02 20:14:34 +02001123 color = get_percent_color(percent);
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001124 path = sym_ext->path;
1125
1126 color_fprintf(stdout, color, " %7.2f %s", percent, path);
1127 node = rb_next(node);
1128 }
1129}
1130
Ingo Molnar0b73da32009-06-06 15:48:52 +02001131static void annotate_sym(struct dso *dso, struct symbol *sym)
1132{
Mike Galbraith42976482009-07-02 08:09:46 +02001133 char *filename = dso->name, *d_filename;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +10001134 u64 start, end, len;
Ingo Molnar0b73da32009-06-06 15:48:52 +02001135 char command[PATH_MAX*2];
1136 FILE *file;
1137
1138 if (!filename)
1139 return;
Mike Galbraith42976482009-07-02 08:09:46 +02001140 if (sym->module)
1141 filename = sym->module->path;
1142 else if (dso == kernel_dso)
Ingo Molnar0b73da32009-06-06 15:48:52 +02001143 filename = vmlinux;
1144
Ingo Molnar0b73da32009-06-06 15:48:52 +02001145 start = sym->obj_start;
1146 if (!start)
1147 start = sym->start;
Mike Galbraith42976482009-07-02 08:09:46 +02001148 if (full_paths)
1149 d_filename = filename;
1150 else
1151 d_filename = basename(filename);
Ingo Molnar0b73da32009-06-06 15:48:52 +02001152
1153 end = start + sym->end - sym->start + 1;
1154 len = sym->end - sym->start;
1155
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001156 if (print_line) {
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +02001157 get_source_line(sym, start, len, filename);
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001158 print_summary(filename);
1159 }
1160
1161 printf("\n\n------------------------------------------------\n");
Mike Galbraith42976482009-07-02 08:09:46 +02001162 printf(" Percent | Source code & Disassembly of %s\n", d_filename);
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001163 printf("------------------------------------------------\n");
1164
1165 if (verbose >= 2)
1166 printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name);
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001167
Mike Galbraith42976482009-07-02 08:09:46 +02001168 sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
1169 (u64)start, (u64)end, filename, filename);
Ingo Molnar0b73da32009-06-06 15:48:52 +02001170
1171 if (verbose >= 3)
1172 printf("doing: %s\n", command);
1173
1174 file = popen(command, "r");
1175 if (!file)
1176 return;
1177
1178 while (!feof(file)) {
1179 if (parse_line(file, sym, start, len) < 0)
1180 break;
1181 }
1182
1183 pclose(file);
Frederic Weisbecker971738f2009-06-13 00:11:22 +02001184 if (print_line)
1185 free_source_line(sym, len);
Ingo Molnar0b73da32009-06-06 15:48:52 +02001186}
1187
1188static void find_annotations(void)
1189{
1190 struct rb_node *nd;
1191 struct dso *dso;
1192 int count = 0;
1193
1194 list_for_each_entry(dso, &dsos, node) {
1195
1196 for (nd = rb_first(&dso->syms); nd; nd = rb_next(nd)) {
1197 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
1198
1199 if (sym->hist) {
1200 annotate_sym(dso, sym);
1201 count++;
1202 }
1203 }
1204 }
1205
1206 if (!count)
1207 printf(" Error: symbol '%s' not present amongst the samples.\n", sym_hist_filter);
1208}
1209
Ingo Molnar8035e422009-06-06 15:19:13 +02001210static int __cmd_annotate(void)
1211{
1212 int ret, rc = EXIT_FAILURE;
1213 unsigned long offset = 0;
1214 unsigned long head = 0;
1215 struct stat stat;
1216 event_t *event;
1217 uint32_t size;
1218 char *buf;
1219
1220 register_idle_thread();
1221
1222 input = open(input_name, O_RDONLY);
1223 if (input < 0) {
1224 perror("failed to open file");
1225 exit(-1);
1226 }
1227
1228 ret = fstat(input, &stat);
1229 if (ret < 0) {
1230 perror("failed to stat file");
1231 exit(-1);
1232 }
1233
1234 if (!stat.st_size) {
1235 fprintf(stderr, "zero-sized file, nothing to do!\n");
1236 exit(0);
1237 }
1238
1239 if (load_kernel() < 0) {
1240 perror("failed to load kernel symbols");
1241 return EXIT_FAILURE;
1242 }
1243
Ingo Molnar8035e422009-06-06 15:19:13 +02001244remap:
1245 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
1246 MAP_SHARED, input, offset);
1247 if (buf == MAP_FAILED) {
1248 perror("failed to mmap file");
1249 exit(-1);
1250 }
1251
1252more:
1253 event = (event_t *)(buf + head);
1254
1255 size = event->header.size;
1256 if (!size)
1257 size = 8;
1258
1259 if (head + event->header.size >= page_size * mmap_window) {
1260 unsigned long shift = page_size * (head / page_size);
1261 int ret;
1262
1263 ret = munmap(buf, page_size * mmap_window);
1264 assert(ret == 0);
1265
1266 offset += shift;
1267 head -= shift;
1268 goto remap;
1269 }
1270
1271 size = event->header.size;
1272
1273 dprintf("%p [%p]: event: %d\n",
1274 (void *)(offset + head),
1275 (void *)(long)event->header.size,
1276 event->header.type);
1277
1278 if (!size || process_event(event, offset, head) < 0) {
1279
1280 dprintf("%p [%p]: skipping unknown header type: %d\n",
1281 (void *)(offset + head),
1282 (void *)(long)(event->header.size),
1283 event->header.type);
1284
1285 total_unknown++;
1286
1287 /*
1288 * assume we lost track of the stream, check alignment, and
1289 * increment a single u64 in the hope to catch on again 'soon'.
1290 */
1291
1292 if (unlikely(head & 7))
1293 head &= ~7ULL;
1294
1295 size = 8;
1296 }
1297
1298 head += size;
1299
Ingo Molnarf37a2912009-07-01 12:37:06 +02001300 if (offset + head < (unsigned long)stat.st_size)
Ingo Molnar8035e422009-06-06 15:19:13 +02001301 goto more;
1302
1303 rc = EXIT_SUCCESS;
1304 close(input);
1305
1306 dprintf(" IP events: %10ld\n", total);
1307 dprintf(" mmap events: %10ld\n", total_mmap);
1308 dprintf(" comm events: %10ld\n", total_comm);
1309 dprintf(" fork events: %10ld\n", total_fork);
1310 dprintf(" unknown events: %10ld\n", total_unknown);
1311
1312 if (dump_trace)
1313 return 0;
1314
1315 if (verbose >= 3)
1316 threads__fprintf(stdout);
1317
1318 if (verbose >= 2)
1319 dsos__fprintf(stdout);
1320
1321 collapse__resort();
1322 output__resort();
Ingo Molnar0b73da32009-06-06 15:48:52 +02001323
1324 find_annotations();
Ingo Molnar8035e422009-06-06 15:19:13 +02001325
1326 return rc;
1327}
1328
1329static const char * const annotate_usage[] = {
1330 "perf annotate [<options>] <command>",
1331 NULL
1332};
1333
1334static const struct option options[] = {
1335 OPT_STRING('i', "input", &input_name, "file",
1336 "input file name"),
Ingo Molnar23b87112009-06-06 21:25:29 +02001337 OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
Ingo Molnar0b73da32009-06-06 15:48:52 +02001338 "symbol to annotate"),
Ingo Molnar8035e422009-06-06 15:19:13 +02001339 OPT_BOOLEAN('v', "verbose", &verbose,
1340 "be more verbose (show symbol address, etc)"),
1341 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1342 "dump raw trace in ASCII"),
1343 OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
Mike Galbraith42976482009-07-02 08:09:46 +02001344 OPT_BOOLEAN('m', "modules", &modules,
1345 "load module symbols - WARNING: use only with -k and LIVE kernel"),
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001346 OPT_BOOLEAN('l', "print-line", &print_line,
1347 "print matching source lines (may be slow)"),
Mike Galbraith42976482009-07-02 08:09:46 +02001348 OPT_BOOLEAN('P', "full-paths", &full_paths,
1349 "Don't shorten the displayed pathnames"),
Ingo Molnar8035e422009-06-06 15:19:13 +02001350 OPT_END()
1351};
1352
1353static void setup_sorting(void)
1354{
1355 char *tmp, *tok, *str = strdup(sort_order);
1356
1357 for (tok = strtok_r(str, ", ", &tmp);
1358 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1359 if (sort_dimension__add(tok) < 0) {
1360 error("Unknown --sort key: `%s'", tok);
1361 usage_with_options(annotate_usage, options);
1362 }
1363 }
1364
1365 free(str);
1366}
1367
Ingo Molnarf37a2912009-07-01 12:37:06 +02001368int cmd_annotate(int argc, const char **argv, const char *prefix __used)
Ingo Molnar8035e422009-06-06 15:19:13 +02001369{
1370 symbol__init();
1371
1372 page_size = getpagesize();
1373
1374 argc = parse_options(argc, argv, options, annotate_usage, 0);
1375
1376 setup_sorting();
1377
Ingo Molnar0b73da32009-06-06 15:48:52 +02001378 if (argc) {
1379 /*
1380 * Special case: if there's an argument left then assume tha
1381 * it's a symbol filter:
1382 */
1383 if (argc > 1)
1384 usage_with_options(annotate_usage, options);
1385
1386 sym_hist_filter = argv[0];
1387 }
1388
1389 if (!sym_hist_filter)
Ingo Molnar8035e422009-06-06 15:19:13 +02001390 usage_with_options(annotate_usage, options);
1391
1392 setup_pager();
1393
1394 return __cmd_annotate();
1395}