blob: 96d421f7161dc884497087f8bbdce0d38bb0885a [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"
Frederic Weisbecker8f288272009-08-16 22:05:48 +020020#include "util/debug.h"
Ingo Molnar8035e422009-06-06 15:19:13 +020021
22#include "util/parse-options.h"
23#include "util/parse-events.h"
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +020024#include "util/thread.h"
Ingo Molnar8035e422009-06-06 15:19:13 +020025
Ingo Molnar8035e422009-06-06 15:19:13 +020026static char const *input_name = "perf.data";
Ingo Molnar8035e422009-06-06 15:19:13 +020027
Ingo Molnar0b73da32009-06-06 15:48:52 +020028static char default_sort_order[] = "comm,symbol";
Ingo Molnar8035e422009-06-06 15:19:13 +020029static char *sort_order = default_sort_order;
30
31static int input;
32static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
33
Mike Galbraith42976482009-07-02 08:09:46 +020034static int full_paths;
35
Frederic Weisbecker301406b2009-06-13 00:11:21 +020036static int print_line;
37
Ingo Molnar8035e422009-06-06 15:19:13 +020038static unsigned long page_size;
39static unsigned long mmap_window = 32;
40
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +020041static struct rb_root threads;
42static struct thread *last_match;
43
Frederic Weisbecker301406b2009-06-13 00:11:21 +020044
45struct sym_ext {
Frederic Weisbecker971738f2009-06-13 00:11:22 +020046 struct rb_node node;
Frederic Weisbecker301406b2009-06-13 00:11:21 +020047 double percent;
48 char *path;
49};
50
Ingo Molnar8035e422009-06-06 15:19:13 +020051/*
52 * histogram, sorted on item, collects counts
53 */
54
55static struct rb_root hist;
56
57struct hist_entry {
58 struct rb_node rb_node;
59
60 struct thread *thread;
61 struct map *map;
62 struct dso *dso;
63 struct symbol *sym;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +100064 u64 ip;
Ingo Molnar8035e422009-06-06 15:19:13 +020065 char level;
66
67 uint32_t count;
68};
69
70/*
71 * configurable sorting bits
72 */
73
74struct sort_entry {
75 struct list_head list;
76
Ingo Molnar83a09442009-08-15 12:26:57 +020077 const char *header;
Ingo Molnar8035e422009-06-06 15:19:13 +020078
79 int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
80 int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
81 size_t (*print)(FILE *fp, struct hist_entry *);
82};
83
84/* --sort pid */
85
86static int64_t
87sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
88{
89 return right->thread->pid - left->thread->pid;
90}
91
92static size_t
93sort__thread_print(FILE *fp, struct hist_entry *self)
94{
95 return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid);
96}
97
98static struct sort_entry sort_thread = {
99 .header = " Command: Pid",
100 .cmp = sort__thread_cmp,
101 .print = sort__thread_print,
102};
103
104/* --sort comm */
105
106static int64_t
107sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
108{
109 return right->thread->pid - left->thread->pid;
110}
111
112static int64_t
113sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
114{
115 char *comm_l = left->thread->comm;
116 char *comm_r = right->thread->comm;
117
118 if (!comm_l || !comm_r) {
119 if (!comm_l && !comm_r)
120 return 0;
121 else if (!comm_l)
122 return -1;
123 else
124 return 1;
125 }
126
127 return strcmp(comm_l, comm_r);
128}
129
130static size_t
131sort__comm_print(FILE *fp, struct hist_entry *self)
132{
133 return fprintf(fp, "%16s", self->thread->comm);
134}
135
136static struct sort_entry sort_comm = {
137 .header = " Command",
138 .cmp = sort__comm_cmp,
139 .collapse = sort__comm_collapse,
140 .print = sort__comm_print,
141};
142
143/* --sort dso */
144
145static int64_t
146sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
147{
148 struct dso *dso_l = left->dso;
149 struct dso *dso_r = right->dso;
150
151 if (!dso_l || !dso_r) {
152 if (!dso_l && !dso_r)
153 return 0;
154 else if (!dso_l)
155 return -1;
156 else
157 return 1;
158 }
159
160 return strcmp(dso_l->name, dso_r->name);
161}
162
163static size_t
164sort__dso_print(FILE *fp, struct hist_entry *self)
165{
166 if (self->dso)
167 return fprintf(fp, "%-25s", self->dso->name);
168
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000169 return fprintf(fp, "%016llx ", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200170}
171
172static struct sort_entry sort_dso = {
173 .header = "Shared Object ",
174 .cmp = sort__dso_cmp,
175 .print = sort__dso_print,
176};
177
178/* --sort symbol */
179
180static int64_t
181sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
182{
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000183 u64 ip_l, ip_r;
Ingo Molnar8035e422009-06-06 15:19:13 +0200184
185 if (left->sym == right->sym)
186 return 0;
187
188 ip_l = left->sym ? left->sym->start : left->ip;
189 ip_r = right->sym ? right->sym->start : right->ip;
190
191 return (int64_t)(ip_r - ip_l);
192}
193
194static size_t
195sort__sym_print(FILE *fp, struct hist_entry *self)
196{
197 size_t ret = 0;
198
199 if (verbose)
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000200 ret += fprintf(fp, "%#018llx ", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200201
202 if (self->sym) {
203 ret += fprintf(fp, "[%c] %s",
204 self->dso == kernel_dso ? 'k' : '.', self->sym->name);
205 } else {
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000206 ret += fprintf(fp, "%#016llx", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200207 }
208
209 return ret;
210}
211
212static struct sort_entry sort_sym = {
213 .header = "Symbol",
214 .cmp = sort__sym_cmp,
215 .print = sort__sym_print,
216};
217
218static int sort__need_collapse = 0;
219
220struct sort_dimension {
Ingo Molnar83a09442009-08-15 12:26:57 +0200221 const char *name;
Ingo Molnar8035e422009-06-06 15:19:13 +0200222 struct sort_entry *entry;
223 int taken;
224};
225
226static struct sort_dimension sort_dimensions[] = {
227 { .name = "pid", .entry = &sort_thread, },
228 { .name = "comm", .entry = &sort_comm, },
229 { .name = "dso", .entry = &sort_dso, },
230 { .name = "symbol", .entry = &sort_sym, },
231};
232
233static LIST_HEAD(hist_entry__sort_list);
234
235static int sort_dimension__add(char *tok)
236{
Ingo Molnarf37a2912009-07-01 12:37:06 +0200237 unsigned int i;
Ingo Molnar8035e422009-06-06 15:19:13 +0200238
239 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
240 struct sort_dimension *sd = &sort_dimensions[i];
241
242 if (sd->taken)
243 continue;
244
245 if (strncasecmp(tok, sd->name, strlen(tok)))
246 continue;
247
248 if (sd->entry->collapse)
249 sort__need_collapse = 1;
250
251 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
252 sd->taken = 1;
253
254 return 0;
255 }
256
257 return -ESRCH;
258}
259
260static int64_t
261hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
262{
263 struct sort_entry *se;
264 int64_t cmp = 0;
265
266 list_for_each_entry(se, &hist_entry__sort_list, list) {
267 cmp = se->cmp(left, right);
268 if (cmp)
269 break;
270 }
271
272 return cmp;
273}
274
275static int64_t
276hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
277{
278 struct sort_entry *se;
279 int64_t cmp = 0;
280
281 list_for_each_entry(se, &hist_entry__sort_list, list) {
282 int64_t (*f)(struct hist_entry *, struct hist_entry *);
283
284 f = se->collapse ?: se->cmp;
285
286 cmp = f(left, right);
287 if (cmp)
288 break;
289 }
290
291 return cmp;
292}
293
Ingo Molnar8035e422009-06-06 15:19:13 +0200294/*
295 * collect histogram counts
296 */
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000297static void hist_hit(struct hist_entry *he, u64 ip)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200298{
299 unsigned int sym_size, offset;
300 struct symbol *sym = he->sym;
301
302 he->count++;
303
304 if (!sym || !sym->hist)
305 return;
306
307 sym_size = sym->end - sym->start;
308 offset = ip - sym->start;
309
310 if (offset >= sym_size)
311 return;
312
313 sym->hist_sum++;
314 sym->hist[offset]++;
315
316 if (verbose >= 3)
317 printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
Arjan van de Ven7d37a0c2009-06-06 20:36:38 +0200318 (void *)(unsigned long)he->sym->start,
Ingo Molnar0b73da32009-06-06 15:48:52 +0200319 he->sym->name,
Arjan van de Ven7d37a0c2009-06-06 20:36:38 +0200320 (void *)(unsigned long)ip, ip - he->sym->start,
Ingo Molnar0b73da32009-06-06 15:48:52 +0200321 sym->hist[offset]);
322}
Ingo Molnar8035e422009-06-06 15:19:13 +0200323
324static int
325hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000326 struct symbol *sym, u64 ip, char level)
Ingo Molnar8035e422009-06-06 15:19:13 +0200327{
328 struct rb_node **p = &hist.rb_node;
329 struct rb_node *parent = NULL;
330 struct hist_entry *he;
331 struct hist_entry entry = {
332 .thread = thread,
333 .map = map,
334 .dso = dso,
335 .sym = sym,
336 .ip = ip,
337 .level = level,
338 .count = 1,
339 };
340 int cmp;
341
342 while (*p != NULL) {
343 parent = *p;
344 he = rb_entry(parent, struct hist_entry, rb_node);
345
346 cmp = hist_entry__cmp(&entry, he);
347
348 if (!cmp) {
Ingo Molnar0b73da32009-06-06 15:48:52 +0200349 hist_hit(he, ip);
350
Ingo Molnar8035e422009-06-06 15:19:13 +0200351 return 0;
352 }
353
354 if (cmp < 0)
355 p = &(*p)->rb_left;
356 else
357 p = &(*p)->rb_right;
358 }
359
360 he = malloc(sizeof(*he));
361 if (!he)
362 return -ENOMEM;
363 *he = entry;
364 rb_link_node(&he->rb_node, parent, p);
365 rb_insert_color(&he->rb_node, &hist);
366
367 return 0;
368}
369
370static void hist_entry__free(struct hist_entry *he)
371{
372 free(he);
373}
374
375/*
376 * collapse the histogram
377 */
378
379static struct rb_root collapse_hists;
380
381static void collapse__insert_entry(struct hist_entry *he)
382{
383 struct rb_node **p = &collapse_hists.rb_node;
384 struct rb_node *parent = NULL;
385 struct hist_entry *iter;
386 int64_t cmp;
387
388 while (*p != NULL) {
389 parent = *p;
390 iter = rb_entry(parent, struct hist_entry, rb_node);
391
392 cmp = hist_entry__collapse(iter, he);
393
394 if (!cmp) {
395 iter->count += he->count;
396 hist_entry__free(he);
397 return;
398 }
399
400 if (cmp < 0)
401 p = &(*p)->rb_left;
402 else
403 p = &(*p)->rb_right;
404 }
405
406 rb_link_node(&he->rb_node, parent, p);
407 rb_insert_color(&he->rb_node, &collapse_hists);
408}
409
410static void collapse__resort(void)
411{
412 struct rb_node *next;
413 struct hist_entry *n;
414
415 if (!sort__need_collapse)
416 return;
417
418 next = rb_first(&hist);
419 while (next) {
420 n = rb_entry(next, struct hist_entry, rb_node);
421 next = rb_next(&n->rb_node);
422
423 rb_erase(&n->rb_node, &hist);
424 collapse__insert_entry(n);
425 }
426}
427
428/*
429 * reverse the map, sort on count.
430 */
431
432static struct rb_root output_hists;
433
434static void output__insert_entry(struct hist_entry *he)
435{
436 struct rb_node **p = &output_hists.rb_node;
437 struct rb_node *parent = NULL;
438 struct hist_entry *iter;
439
440 while (*p != NULL) {
441 parent = *p;
442 iter = rb_entry(parent, struct hist_entry, rb_node);
443
444 if (he->count > iter->count)
445 p = &(*p)->rb_left;
446 else
447 p = &(*p)->rb_right;
448 }
449
450 rb_link_node(&he->rb_node, parent, p);
451 rb_insert_color(&he->rb_node, &output_hists);
452}
453
454static void output__resort(void)
455{
456 struct rb_node *next;
457 struct hist_entry *n;
458 struct rb_root *tree = &hist;
459
460 if (sort__need_collapse)
461 tree = &collapse_hists;
462
463 next = rb_first(tree);
464
465 while (next) {
466 n = rb_entry(next, struct hist_entry, rb_node);
467 next = rb_next(&n->rb_node);
468
469 rb_erase(&n->rb_node, tree);
470 output__insert_entry(n);
471 }
472}
473
Ingo Molnar8035e422009-06-06 15:19:13 +0200474static void register_idle_thread(void)
475{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200476 struct thread *thread = threads__findnew(0, &threads, &last_match);
Ingo Molnar8035e422009-06-06 15:19:13 +0200477
478 if (thread == NULL ||
479 thread__set_comm(thread, "[idle]")) {
480 fprintf(stderr, "problem inserting idle task.\n");
481 exit(-1);
482 }
483}
484
485static unsigned long total = 0,
486 total_mmap = 0,
487 total_comm = 0,
488 total_fork = 0,
489 total_unknown = 0;
490
491static int
Peter Zijlstrae6e18ec2009-06-25 11:27:12 +0200492process_sample_event(event_t *event, unsigned long offset, unsigned long head)
Ingo Molnar8035e422009-06-06 15:19:13 +0200493{
494 char level;
495 int show = 0;
496 struct dso *dso = NULL;
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200497 struct thread *thread;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000498 u64 ip = event->ip.ip;
Ingo Molnar8035e422009-06-06 15:19:13 +0200499 struct map *map = NULL;
500
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200501 thread = threads__findnew(event->ip.pid, &threads, &last_match);
502
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200503 dump_printf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200504 (void *)(offset + head),
505 (void *)(long)(event->header.size),
506 event->header.misc,
507 event->ip.pid,
508 (void *)(long)ip);
509
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200510 dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
Ingo Molnar8035e422009-06-06 15:19:13 +0200511
512 if (thread == NULL) {
513 fprintf(stderr, "problem processing %d event, skipping it.\n",
514 event->header.type);
515 return -1;
516 }
517
518 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
519 show = SHOW_KERNEL;
520 level = 'k';
521
522 dso = kernel_dso;
523
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200524 dump_printf(" ...... dso: %s\n", dso->name);
Ingo Molnar8035e422009-06-06 15:19:13 +0200525
526 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
527
528 show = SHOW_USER;
529 level = '.';
530
531 map = thread__find_map(thread, ip);
532 if (map != NULL) {
533 ip = map->map_ip(map, ip);
534 dso = map->dso;
535 } else {
536 /*
537 * If this is outside of all known maps,
538 * and is a negative address, try to look it
539 * up in the kernel dso, as it might be a
540 * vsyscall (which executes in user-mode):
541 */
542 if ((long long)ip < 0)
543 dso = kernel_dso;
544 }
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200545 dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
Ingo Molnar8035e422009-06-06 15:19:13 +0200546
547 } else {
548 show = SHOW_HV;
549 level = 'H';
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200550 dump_printf(" ...... dso: [hypervisor]\n");
Ingo Molnar8035e422009-06-06 15:19:13 +0200551 }
552
553 if (show & show_mask) {
554 struct symbol *sym = NULL;
555
556 if (dso)
557 sym = dso->find_symbol(dso, ip);
558
559 if (hist_entry__add(thread, map, dso, sym, ip, level)) {
560 fprintf(stderr,
561 "problem incrementing symbol count, skipping event\n");
562 return -1;
563 }
564 }
565 total++;
566
567 return 0;
568}
569
570static int
571process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
572{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200573 struct thread *thread;
Frederic Weisbecker66e274f2009-08-12 11:07:25 +0200574 struct map *map = map__new(&event->mmap, NULL, 0);
Ingo Molnar8035e422009-06-06 15:19:13 +0200575
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200576 thread = threads__findnew(event->mmap.pid, &threads, &last_match);
577
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200578 dump_printf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200579 (void *)(offset + head),
580 (void *)(long)(event->header.size),
581 event->mmap.pid,
582 (void *)(long)event->mmap.start,
583 (void *)(long)event->mmap.len,
584 (void *)(long)event->mmap.pgoff,
585 event->mmap.filename);
586
587 if (thread == NULL || map == NULL) {
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200588 dump_printf("problem processing PERF_EVENT_MMAP, skipping event.\n");
Ingo Molnar8035e422009-06-06 15:19:13 +0200589 return 0;
590 }
591
592 thread__insert_map(thread, map);
593 total_mmap++;
594
595 return 0;
596}
597
598static int
599process_comm_event(event_t *event, unsigned long offset, unsigned long head)
600{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200601 struct thread *thread;
Ingo Molnar8035e422009-06-06 15:19:13 +0200602
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200603 thread = threads__findnew(event->comm.pid, &threads, &last_match);
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200604 dump_printf("%p [%p]: PERF_EVENT_COMM: %s:%d\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200605 (void *)(offset + head),
606 (void *)(long)(event->header.size),
607 event->comm.comm, event->comm.pid);
608
609 if (thread == NULL ||
610 thread__set_comm(thread, event->comm.comm)) {
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200611 dump_printf("problem processing PERF_EVENT_COMM, skipping event.\n");
Ingo Molnar8035e422009-06-06 15:19:13 +0200612 return -1;
613 }
614 total_comm++;
615
616 return 0;
617}
618
619static int
620process_fork_event(event_t *event, unsigned long offset, unsigned long head)
621{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200622 struct thread *thread;
623 struct thread *parent;
Ingo Molnar8035e422009-06-06 15:19:13 +0200624
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200625 thread = threads__findnew(event->fork.pid, &threads, &last_match);
626 parent = threads__findnew(event->fork.ppid, &threads, &last_match);
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200627 dump_printf("%p [%p]: PERF_EVENT_FORK: %d:%d\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200628 (void *)(offset + head),
629 (void *)(long)(event->header.size),
630 event->fork.pid, event->fork.ppid);
631
632 if (!thread || !parent || thread__fork(thread, parent)) {
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200633 dump_printf("problem processing PERF_EVENT_FORK, skipping event.\n");
Ingo Molnar8035e422009-06-06 15:19:13 +0200634 return -1;
635 }
636 total_fork++;
637
638 return 0;
639}
640
641static int
Ingo Molnar8035e422009-06-06 15:19:13 +0200642process_event(event_t *event, unsigned long offset, unsigned long head)
643{
Ingo Molnar8035e422009-06-06 15:19:13 +0200644 switch (event->header.type) {
Peter Zijlstrae6e18ec2009-06-25 11:27:12 +0200645 case PERF_EVENT_SAMPLE:
646 return process_sample_event(event, offset, head);
647
Ingo Molnar8035e422009-06-06 15:19:13 +0200648 case PERF_EVENT_MMAP:
649 return process_mmap_event(event, offset, head);
650
651 case PERF_EVENT_COMM:
652 return process_comm_event(event, offset, head);
653
654 case PERF_EVENT_FORK:
655 return process_fork_event(event, offset, head);
Ingo Molnar8035e422009-06-06 15:19:13 +0200656 /*
657 * We dont process them right now but they are fine:
658 */
659
660 case PERF_EVENT_THROTTLE:
661 case PERF_EVENT_UNTHROTTLE:
662 return 0;
663
664 default:
665 return -1;
666 }
667
668 return 0;
669}
670
Ingo Molnar0b73da32009-06-06 15:48:52 +0200671static int
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000672parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200673{
674 char *line = NULL, *tmp, *tmp2;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200675 static const char *prev_line;
676 static const char *prev_color;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200677 unsigned int offset;
678 size_t line_len;
Ingo Molnarf37a2912009-07-01 12:37:06 +0200679 s64 line_ip;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200680 int ret;
681 char *c;
682
683 if (getline(&line, &line_len, file) < 0)
684 return -1;
685 if (!line)
686 return -1;
687
688 c = strchr(line, '\n');
689 if (c)
690 *c = 0;
691
692 line_ip = -1;
693 offset = 0;
694 ret = -2;
695
696 /*
697 * Strip leading spaces:
698 */
699 tmp = line;
700 while (*tmp) {
701 if (*tmp != ' ')
702 break;
703 tmp++;
704 }
705
706 if (*tmp) {
707 /*
708 * Parse hexa addresses followed by ':'
709 */
710 line_ip = strtoull(tmp, &tmp2, 16);
711 if (*tmp2 != ':')
712 line_ip = -1;
713 }
714
715 if (line_ip != -1) {
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200716 const char *path = NULL;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200717 unsigned int hits = 0;
718 double percent = 0.0;
Ingo Molnar83a09442009-08-15 12:26:57 +0200719 const char *color;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200720 struct sym_ext *sym_ext = sym->priv;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200721
722 offset = line_ip - start;
723 if (offset < len)
724 hits = sym->hist[offset];
725
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200726 if (offset < len && sym_ext) {
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200727 path = sym_ext[offset].path;
728 percent = sym_ext[offset].percent;
729 } else if (sym->hist_sum)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200730 percent = 100.0 * hits / sym->hist_sum;
731
Frederic Weisbecker1e11fd82009-07-02 20:14:34 +0200732 color = get_percent_color(percent);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200733
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200734 /*
735 * Also color the filename and line if needed, with
736 * the same color than the percentage. Don't print it
737 * twice for close colored ip with the same filename:line
738 */
739 if (path) {
740 if (!prev_line || strcmp(prev_line, path)
741 || color != prev_color) {
742 color_fprintf(stdout, color, " %s", path);
743 prev_line = path;
744 prev_color = color;
745 }
746 }
747
Ingo Molnar0b73da32009-06-06 15:48:52 +0200748 color_fprintf(stdout, color, " %7.2f", percent);
749 printf(" : ");
750 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
751 } else {
752 if (!*line)
753 printf(" :\n");
754 else
755 printf(" : %s\n", line);
756 }
757
758 return 0;
759}
760
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200761static struct rb_root root_sym_ext;
762
763static void insert_source_line(struct sym_ext *sym_ext)
764{
765 struct sym_ext *iter;
766 struct rb_node **p = &root_sym_ext.rb_node;
767 struct rb_node *parent = NULL;
768
769 while (*p != NULL) {
770 parent = *p;
771 iter = rb_entry(parent, struct sym_ext, node);
772
773 if (sym_ext->percent > iter->percent)
774 p = &(*p)->rb_left;
775 else
776 p = &(*p)->rb_right;
777 }
778
779 rb_link_node(&sym_ext->node, parent, p);
780 rb_insert_color(&sym_ext->node, &root_sym_ext);
781}
782
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200783static void free_source_line(struct symbol *sym, int len)
784{
785 struct sym_ext *sym_ext = sym->priv;
786 int i;
787
788 if (!sym_ext)
789 return;
790
791 for (i = 0; i < len; i++)
792 free(sym_ext[i].path);
793 free(sym_ext);
794
795 sym->priv = NULL;
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200796 root_sym_ext = RB_ROOT;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200797}
798
799/* Get the filename:line for the colored entries */
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200800static void
Ingo Molnar83a09442009-08-15 12:26:57 +0200801get_source_line(struct symbol *sym, u64 start, int len, const char *filename)
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200802{
803 int i;
804 char cmd[PATH_MAX * 2];
805 struct sym_ext *sym_ext;
806
807 if (!sym->hist_sum)
808 return;
809
810 sym->priv = calloc(len, sizeof(struct sym_ext));
811 if (!sym->priv)
812 return;
813
814 sym_ext = sym->priv;
815
816 for (i = 0; i < len; i++) {
817 char *path = NULL;
818 size_t line_len;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000819 u64 offset;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200820 FILE *fp;
821
822 sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum;
823 if (sym_ext[i].percent <= 0.5)
824 continue;
825
826 offset = start + i;
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200827 sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200828 fp = popen(cmd, "r");
829 if (!fp)
830 continue;
831
832 if (getline(&path, &line_len, fp) < 0 || !line_len)
833 goto next;
834
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200835 sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200836 if (!sym_ext[i].path)
837 goto next;
838
839 strcpy(sym_ext[i].path, path);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200840 insert_source_line(&sym_ext[i]);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200841
842 next:
843 pclose(fp);
844 }
845}
846
Ingo Molnar83a09442009-08-15 12:26:57 +0200847static void print_summary(const char *filename)
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200848{
849 struct sym_ext *sym_ext;
850 struct rb_node *node;
851
852 printf("\nSorted summary for file %s\n", filename);
853 printf("----------------------------------------------\n\n");
854
855 if (RB_EMPTY_ROOT(&root_sym_ext)) {
856 printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
857 return;
858 }
859
860 node = rb_first(&root_sym_ext);
861 while (node) {
862 double percent;
Ingo Molnar83a09442009-08-15 12:26:57 +0200863 const char *color;
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200864 char *path;
865
866 sym_ext = rb_entry(node, struct sym_ext, node);
867 percent = sym_ext->percent;
Frederic Weisbecker1e11fd82009-07-02 20:14:34 +0200868 color = get_percent_color(percent);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200869 path = sym_ext->path;
870
871 color_fprintf(stdout, color, " %7.2f %s", percent, path);
872 node = rb_next(node);
873 }
874}
875
Ingo Molnar0b73da32009-06-06 15:48:52 +0200876static void annotate_sym(struct dso *dso, struct symbol *sym)
877{
Ingo Molnar83a09442009-08-15 12:26:57 +0200878 const char *filename = dso->name, *d_filename;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000879 u64 start, end, len;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200880 char command[PATH_MAX*2];
881 FILE *file;
882
883 if (!filename)
884 return;
Mike Galbraith42976482009-07-02 08:09:46 +0200885 if (sym->module)
886 filename = sym->module->path;
887 else if (dso == kernel_dso)
Ingo Molnar83a09442009-08-15 12:26:57 +0200888 filename = vmlinux_name;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200889
Ingo Molnar0b73da32009-06-06 15:48:52 +0200890 start = sym->obj_start;
891 if (!start)
892 start = sym->start;
Mike Galbraith42976482009-07-02 08:09:46 +0200893 if (full_paths)
894 d_filename = filename;
895 else
896 d_filename = basename(filename);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200897
898 end = start + sym->end - sym->start + 1;
899 len = sym->end - sym->start;
900
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200901 if (print_line) {
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200902 get_source_line(sym, start, len, filename);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200903 print_summary(filename);
904 }
905
906 printf("\n\n------------------------------------------------\n");
Mike Galbraith42976482009-07-02 08:09:46 +0200907 printf(" Percent | Source code & Disassembly of %s\n", d_filename);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200908 printf("------------------------------------------------\n");
909
910 if (verbose >= 2)
911 printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200912
Mike Galbraith42976482009-07-02 08:09:46 +0200913 sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
914 (u64)start, (u64)end, filename, filename);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200915
916 if (verbose >= 3)
917 printf("doing: %s\n", command);
918
919 file = popen(command, "r");
920 if (!file)
921 return;
922
923 while (!feof(file)) {
924 if (parse_line(file, sym, start, len) < 0)
925 break;
926 }
927
928 pclose(file);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200929 if (print_line)
930 free_source_line(sym, len);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200931}
932
933static void find_annotations(void)
934{
935 struct rb_node *nd;
936 struct dso *dso;
937 int count = 0;
938
939 list_for_each_entry(dso, &dsos, node) {
940
941 for (nd = rb_first(&dso->syms); nd; nd = rb_next(nd)) {
942 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
943
944 if (sym->hist) {
945 annotate_sym(dso, sym);
946 count++;
947 }
948 }
949 }
950
951 if (!count)
952 printf(" Error: symbol '%s' not present amongst the samples.\n", sym_hist_filter);
953}
954
Ingo Molnar8035e422009-06-06 15:19:13 +0200955static int __cmd_annotate(void)
956{
957 int ret, rc = EXIT_FAILURE;
958 unsigned long offset = 0;
959 unsigned long head = 0;
Ingo Molnar83a09442009-08-15 12:26:57 +0200960 struct stat input_stat;
Ingo Molnar8035e422009-06-06 15:19:13 +0200961 event_t *event;
962 uint32_t size;
963 char *buf;
964
965 register_idle_thread();
966
967 input = open(input_name, O_RDONLY);
968 if (input < 0) {
969 perror("failed to open file");
970 exit(-1);
971 }
972
Ingo Molnar83a09442009-08-15 12:26:57 +0200973 ret = fstat(input, &input_stat);
Ingo Molnar8035e422009-06-06 15:19:13 +0200974 if (ret < 0) {
975 perror("failed to stat file");
976 exit(-1);
977 }
978
Ingo Molnar83a09442009-08-15 12:26:57 +0200979 if (!input_stat.st_size) {
Ingo Molnar8035e422009-06-06 15:19:13 +0200980 fprintf(stderr, "zero-sized file, nothing to do!\n");
981 exit(0);
982 }
983
984 if (load_kernel() < 0) {
985 perror("failed to load kernel symbols");
986 return EXIT_FAILURE;
987 }
988
Ingo Molnar8035e422009-06-06 15:19:13 +0200989remap:
990 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
991 MAP_SHARED, input, offset);
992 if (buf == MAP_FAILED) {
993 perror("failed to mmap file");
994 exit(-1);
995 }
996
997more:
998 event = (event_t *)(buf + head);
999
1000 size = event->header.size;
1001 if (!size)
1002 size = 8;
1003
1004 if (head + event->header.size >= page_size * mmap_window) {
1005 unsigned long shift = page_size * (head / page_size);
Ingo Molnar83a09442009-08-15 12:26:57 +02001006 int munmap_ret;
Ingo Molnar8035e422009-06-06 15:19:13 +02001007
Ingo Molnar83a09442009-08-15 12:26:57 +02001008 munmap_ret = munmap(buf, page_size * mmap_window);
1009 assert(munmap_ret == 0);
Ingo Molnar8035e422009-06-06 15:19:13 +02001010
1011 offset += shift;
1012 head -= shift;
1013 goto remap;
1014 }
1015
1016 size = event->header.size;
1017
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +02001018 dump_printf("%p [%p]: event: %d\n",
Ingo Molnar8035e422009-06-06 15:19:13 +02001019 (void *)(offset + head),
1020 (void *)(long)event->header.size,
1021 event->header.type);
1022
1023 if (!size || process_event(event, offset, head) < 0) {
1024
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +02001025 dump_printf("%p [%p]: skipping unknown header type: %d\n",
Ingo Molnar8035e422009-06-06 15:19:13 +02001026 (void *)(offset + head),
1027 (void *)(long)(event->header.size),
1028 event->header.type);
1029
1030 total_unknown++;
1031
1032 /*
1033 * assume we lost track of the stream, check alignment, and
1034 * increment a single u64 in the hope to catch on again 'soon'.
1035 */
1036
1037 if (unlikely(head & 7))
1038 head &= ~7ULL;
1039
1040 size = 8;
1041 }
1042
1043 head += size;
1044
Ingo Molnar83a09442009-08-15 12:26:57 +02001045 if (offset + head < (unsigned long)input_stat.st_size)
Ingo Molnar8035e422009-06-06 15:19:13 +02001046 goto more;
1047
1048 rc = EXIT_SUCCESS;
1049 close(input);
1050
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +02001051 dump_printf(" IP events: %10ld\n", total);
1052 dump_printf(" mmap events: %10ld\n", total_mmap);
1053 dump_printf(" comm events: %10ld\n", total_comm);
1054 dump_printf(" fork events: %10ld\n", total_fork);
1055 dump_printf(" unknown events: %10ld\n", total_unknown);
Ingo Molnar8035e422009-06-06 15:19:13 +02001056
1057 if (dump_trace)
1058 return 0;
1059
1060 if (verbose >= 3)
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +02001061 threads__fprintf(stdout, &threads);
Ingo Molnar8035e422009-06-06 15:19:13 +02001062
1063 if (verbose >= 2)
1064 dsos__fprintf(stdout);
1065
1066 collapse__resort();
1067 output__resort();
Ingo Molnar0b73da32009-06-06 15:48:52 +02001068
1069 find_annotations();
Ingo Molnar8035e422009-06-06 15:19:13 +02001070
1071 return rc;
1072}
1073
1074static const char * const annotate_usage[] = {
1075 "perf annotate [<options>] <command>",
1076 NULL
1077};
1078
1079static const struct option options[] = {
1080 OPT_STRING('i', "input", &input_name, "file",
1081 "input file name"),
Ingo Molnar23b87112009-06-06 21:25:29 +02001082 OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
Ingo Molnar0b73da32009-06-06 15:48:52 +02001083 "symbol to annotate"),
Ingo Molnar8035e422009-06-06 15:19:13 +02001084 OPT_BOOLEAN('v', "verbose", &verbose,
1085 "be more verbose (show symbol address, etc)"),
1086 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1087 "dump raw trace in ASCII"),
Ingo Molnar83a09442009-08-15 12:26:57 +02001088 OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"),
Mike Galbraith42976482009-07-02 08:09:46 +02001089 OPT_BOOLEAN('m', "modules", &modules,
1090 "load module symbols - WARNING: use only with -k and LIVE kernel"),
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001091 OPT_BOOLEAN('l', "print-line", &print_line,
1092 "print matching source lines (may be slow)"),
Mike Galbraith42976482009-07-02 08:09:46 +02001093 OPT_BOOLEAN('P', "full-paths", &full_paths,
1094 "Don't shorten the displayed pathnames"),
Ingo Molnar8035e422009-06-06 15:19:13 +02001095 OPT_END()
1096};
1097
1098static void setup_sorting(void)
1099{
1100 char *tmp, *tok, *str = strdup(sort_order);
1101
1102 for (tok = strtok_r(str, ", ", &tmp);
1103 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1104 if (sort_dimension__add(tok) < 0) {
1105 error("Unknown --sort key: `%s'", tok);
1106 usage_with_options(annotate_usage, options);
1107 }
1108 }
1109
1110 free(str);
1111}
1112
Ingo Molnarf37a2912009-07-01 12:37:06 +02001113int cmd_annotate(int argc, const char **argv, const char *prefix __used)
Ingo Molnar8035e422009-06-06 15:19:13 +02001114{
1115 symbol__init();
1116
1117 page_size = getpagesize();
1118
1119 argc = parse_options(argc, argv, options, annotate_usage, 0);
1120
1121 setup_sorting();
1122
Ingo Molnar0b73da32009-06-06 15:48:52 +02001123 if (argc) {
1124 /*
1125 * Special case: if there's an argument left then assume tha
1126 * it's a symbol filter:
1127 */
1128 if (argc > 1)
1129 usage_with_options(annotate_usage, options);
1130
1131 sym_hist_filter = argv[0];
1132 }
1133
1134 if (!sym_hist_filter)
Ingo Molnar8035e422009-06-06 15:19:13 +02001135 usage_with_options(annotate_usage, options);
1136
1137 setup_pager();
1138
1139 return __cmd_annotate();
1140}