blob: a33087328bd4609ad19d9fda4cdd82308e90e77b [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
Peter Zijlstrafa6963b2009-08-19 11:18:26 +020031static int force;
Ingo Molnar8035e422009-06-06 15:19:13 +020032static int input;
33static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
34
Mike Galbraith42976482009-07-02 08:09:46 +020035static int full_paths;
36
Frederic Weisbecker301406b2009-06-13 00:11:21 +020037static int print_line;
38
Ingo Molnar8035e422009-06-06 15:19:13 +020039static unsigned long page_size;
40static unsigned long mmap_window = 32;
41
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +020042static struct rb_root threads;
43static struct thread *last_match;
44
Frederic Weisbecker301406b2009-06-13 00:11:21 +020045
46struct sym_ext {
Frederic Weisbecker971738f2009-06-13 00:11:22 +020047 struct rb_node node;
Frederic Weisbecker301406b2009-06-13 00:11:21 +020048 double percent;
49 char *path;
50};
51
Ingo Molnar8035e422009-06-06 15:19:13 +020052/*
53 * histogram, sorted on item, collects counts
54 */
55
56static struct rb_root hist;
57
58struct hist_entry {
59 struct rb_node rb_node;
60
61 struct thread *thread;
62 struct map *map;
63 struct dso *dso;
64 struct symbol *sym;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +100065 u64 ip;
Ingo Molnar8035e422009-06-06 15:19:13 +020066 char level;
67
68 uint32_t count;
69};
70
71/*
72 * configurable sorting bits
73 */
74
75struct sort_entry {
76 struct list_head list;
77
Ingo Molnar83a09442009-08-15 12:26:57 +020078 const char *header;
Ingo Molnar8035e422009-06-06 15:19:13 +020079
80 int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
81 int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
82 size_t (*print)(FILE *fp, struct hist_entry *);
83};
84
John Kacurcbfeb262009-09-24 18:01:51 +020085static int64_t cmp_null(void *l, void *r)
86{
87 if (!l && !r)
88 return 0;
89 else if (!l)
90 return -1;
91 else
92 return 1;
93}
94
Ingo Molnar8035e422009-06-06 15:19:13 +020095/* --sort pid */
96
97static int64_t
98sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
99{
100 return right->thread->pid - left->thread->pid;
101}
102
103static size_t
104sort__thread_print(FILE *fp, struct hist_entry *self)
105{
106 return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid);
107}
108
109static struct sort_entry sort_thread = {
110 .header = " Command: Pid",
111 .cmp = sort__thread_cmp,
112 .print = sort__thread_print,
113};
114
115/* --sort comm */
116
117static int64_t
118sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
119{
120 return right->thread->pid - left->thread->pid;
121}
122
123static int64_t
124sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
125{
126 char *comm_l = left->thread->comm;
127 char *comm_r = right->thread->comm;
128
John Kacurcbfeb262009-09-24 18:01:51 +0200129 if (!comm_l || !comm_r)
130 return cmp_null(comm_l, comm_r);
Ingo Molnar8035e422009-06-06 15:19:13 +0200131
132 return strcmp(comm_l, comm_r);
133}
134
135static size_t
136sort__comm_print(FILE *fp, struct hist_entry *self)
137{
138 return fprintf(fp, "%16s", self->thread->comm);
139}
140
141static struct sort_entry sort_comm = {
142 .header = " Command",
143 .cmp = sort__comm_cmp,
144 .collapse = sort__comm_collapse,
145 .print = sort__comm_print,
146};
147
148/* --sort dso */
149
150static int64_t
151sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
152{
153 struct dso *dso_l = left->dso;
154 struct dso *dso_r = right->dso;
155
John Kacurcbfeb262009-09-24 18:01:51 +0200156 if (!dso_l || !dso_r)
157 return cmp_null(dso_l, dso_r);
Ingo Molnar8035e422009-06-06 15:19:13 +0200158
159 return strcmp(dso_l->name, dso_r->name);
160}
161
162static size_t
163sort__dso_print(FILE *fp, struct hist_entry *self)
164{
165 if (self->dso)
166 return fprintf(fp, "%-25s", self->dso->name);
167
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000168 return fprintf(fp, "%016llx ", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200169}
170
171static struct sort_entry sort_dso = {
172 .header = "Shared Object ",
173 .cmp = sort__dso_cmp,
174 .print = sort__dso_print,
175};
176
177/* --sort symbol */
178
179static int64_t
180sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
181{
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000182 u64 ip_l, ip_r;
Ingo Molnar8035e422009-06-06 15:19:13 +0200183
184 if (left->sym == right->sym)
185 return 0;
186
187 ip_l = left->sym ? left->sym->start : left->ip;
188 ip_r = right->sym ? right->sym->start : right->ip;
189
190 return (int64_t)(ip_r - ip_l);
191}
192
193static size_t
194sort__sym_print(FILE *fp, struct hist_entry *self)
195{
196 size_t ret = 0;
197
198 if (verbose)
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000199 ret += fprintf(fp, "%#018llx ", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200200
201 if (self->sym) {
202 ret += fprintf(fp, "[%c] %s",
203 self->dso == kernel_dso ? 'k' : '.', self->sym->name);
204 } else {
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000205 ret += fprintf(fp, "%#016llx", (u64)self->ip);
Ingo Molnar8035e422009-06-06 15:19:13 +0200206 }
207
208 return ret;
209}
210
211static struct sort_entry sort_sym = {
212 .header = "Symbol",
213 .cmp = sort__sym_cmp,
214 .print = sort__sym_print,
215};
216
217static int sort__need_collapse = 0;
218
219struct sort_dimension {
Ingo Molnar83a09442009-08-15 12:26:57 +0200220 const char *name;
Ingo Molnar8035e422009-06-06 15:19:13 +0200221 struct sort_entry *entry;
222 int taken;
223};
224
225static struct sort_dimension sort_dimensions[] = {
226 { .name = "pid", .entry = &sort_thread, },
227 { .name = "comm", .entry = &sort_comm, },
228 { .name = "dso", .entry = &sort_dso, },
229 { .name = "symbol", .entry = &sort_sym, },
230};
231
232static LIST_HEAD(hist_entry__sort_list);
233
234static int sort_dimension__add(char *tok)
235{
Ingo Molnarf37a2912009-07-01 12:37:06 +0200236 unsigned int i;
Ingo Molnar8035e422009-06-06 15:19:13 +0200237
238 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
239 struct sort_dimension *sd = &sort_dimensions[i];
240
241 if (sd->taken)
242 continue;
243
244 if (strncasecmp(tok, sd->name, strlen(tok)))
245 continue;
246
247 if (sd->entry->collapse)
248 sort__need_collapse = 1;
249
250 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
251 sd->taken = 1;
252
253 return 0;
254 }
255
256 return -ESRCH;
257}
258
259static int64_t
260hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
261{
262 struct sort_entry *se;
263 int64_t cmp = 0;
264
265 list_for_each_entry(se, &hist_entry__sort_list, list) {
266 cmp = se->cmp(left, right);
267 if (cmp)
268 break;
269 }
270
271 return cmp;
272}
273
274static int64_t
275hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
276{
277 struct sort_entry *se;
278 int64_t cmp = 0;
279
280 list_for_each_entry(se, &hist_entry__sort_list, list) {
281 int64_t (*f)(struct hist_entry *, struct hist_entry *);
282
283 f = se->collapse ?: se->cmp;
284
285 cmp = f(left, right);
286 if (cmp)
287 break;
288 }
289
290 return cmp;
291}
292
Ingo Molnar8035e422009-06-06 15:19:13 +0200293/*
294 * collect histogram counts
295 */
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000296static void hist_hit(struct hist_entry *he, u64 ip)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200297{
298 unsigned int sym_size, offset;
299 struct symbol *sym = he->sym;
300
301 he->count++;
302
303 if (!sym || !sym->hist)
304 return;
305
306 sym_size = sym->end - sym->start;
307 offset = ip - sym->start;
308
309 if (offset >= sym_size)
310 return;
311
312 sym->hist_sum++;
313 sym->hist[offset]++;
314
315 if (verbose >= 3)
316 printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
Arjan van de Ven7d37a0c2009-06-06 20:36:38 +0200317 (void *)(unsigned long)he->sym->start,
Ingo Molnar0b73da32009-06-06 15:48:52 +0200318 he->sym->name,
Arjan van de Ven7d37a0c2009-06-06 20:36:38 +0200319 (void *)(unsigned long)ip, ip - he->sym->start,
Ingo Molnar0b73da32009-06-06 15:48:52 +0200320 sym->hist[offset]);
321}
Ingo Molnar8035e422009-06-06 15:19:13 +0200322
323static int
324hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000325 struct symbol *sym, u64 ip, char level)
Ingo Molnar8035e422009-06-06 15:19:13 +0200326{
327 struct rb_node **p = &hist.rb_node;
328 struct rb_node *parent = NULL;
329 struct hist_entry *he;
330 struct hist_entry entry = {
331 .thread = thread,
332 .map = map,
333 .dso = dso,
334 .sym = sym,
335 .ip = ip,
336 .level = level,
337 .count = 1,
338 };
339 int cmp;
340
341 while (*p != NULL) {
342 parent = *p;
343 he = rb_entry(parent, struct hist_entry, rb_node);
344
345 cmp = hist_entry__cmp(&entry, he);
346
347 if (!cmp) {
Ingo Molnar0b73da32009-06-06 15:48:52 +0200348 hist_hit(he, ip);
349
Ingo Molnar8035e422009-06-06 15:19:13 +0200350 return 0;
351 }
352
353 if (cmp < 0)
354 p = &(*p)->rb_left;
355 else
356 p = &(*p)->rb_right;
357 }
358
359 he = malloc(sizeof(*he));
360 if (!he)
361 return -ENOMEM;
362 *he = entry;
363 rb_link_node(&he->rb_node, parent, p);
364 rb_insert_color(&he->rb_node, &hist);
365
366 return 0;
367}
368
369static void hist_entry__free(struct hist_entry *he)
370{
371 free(he);
372}
373
374/*
375 * collapse the histogram
376 */
377
378static struct rb_root collapse_hists;
379
380static void collapse__insert_entry(struct hist_entry *he)
381{
382 struct rb_node **p = &collapse_hists.rb_node;
383 struct rb_node *parent = NULL;
384 struct hist_entry *iter;
385 int64_t cmp;
386
387 while (*p != NULL) {
388 parent = *p;
389 iter = rb_entry(parent, struct hist_entry, rb_node);
390
391 cmp = hist_entry__collapse(iter, he);
392
393 if (!cmp) {
394 iter->count += he->count;
395 hist_entry__free(he);
396 return;
397 }
398
399 if (cmp < 0)
400 p = &(*p)->rb_left;
401 else
402 p = &(*p)->rb_right;
403 }
404
405 rb_link_node(&he->rb_node, parent, p);
406 rb_insert_color(&he->rb_node, &collapse_hists);
407}
408
409static void collapse__resort(void)
410{
411 struct rb_node *next;
412 struct hist_entry *n;
413
414 if (!sort__need_collapse)
415 return;
416
417 next = rb_first(&hist);
418 while (next) {
419 n = rb_entry(next, struct hist_entry, rb_node);
420 next = rb_next(&n->rb_node);
421
422 rb_erase(&n->rb_node, &hist);
423 collapse__insert_entry(n);
424 }
425}
426
427/*
428 * reverse the map, sort on count.
429 */
430
431static struct rb_root output_hists;
432
433static void output__insert_entry(struct hist_entry *he)
434{
435 struct rb_node **p = &output_hists.rb_node;
436 struct rb_node *parent = NULL;
437 struct hist_entry *iter;
438
439 while (*p != NULL) {
440 parent = *p;
441 iter = rb_entry(parent, struct hist_entry, rb_node);
442
443 if (he->count > iter->count)
444 p = &(*p)->rb_left;
445 else
446 p = &(*p)->rb_right;
447 }
448
449 rb_link_node(&he->rb_node, parent, p);
450 rb_insert_color(&he->rb_node, &output_hists);
451}
452
453static void output__resort(void)
454{
455 struct rb_node *next;
456 struct hist_entry *n;
457 struct rb_root *tree = &hist;
458
459 if (sort__need_collapse)
460 tree = &collapse_hists;
461
462 next = rb_first(tree);
463
464 while (next) {
465 n = rb_entry(next, struct hist_entry, rb_node);
466 next = rb_next(&n->rb_node);
467
468 rb_erase(&n->rb_node, tree);
469 output__insert_entry(n);
470 }
471}
472
Ingo Molnar8035e422009-06-06 15:19:13 +0200473static unsigned long total = 0,
474 total_mmap = 0,
475 total_comm = 0,
476 total_fork = 0,
477 total_unknown = 0;
478
479static int
Peter Zijlstrae6e18ec2009-06-25 11:27:12 +0200480process_sample_event(event_t *event, unsigned long offset, unsigned long head)
Ingo Molnar8035e422009-06-06 15:19:13 +0200481{
482 char level;
483 int show = 0;
484 struct dso *dso = NULL;
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200485 struct thread *thread;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000486 u64 ip = event->ip.ip;
Ingo Molnar8035e422009-06-06 15:19:13 +0200487 struct map *map = NULL;
488
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200489 thread = threads__findnew(event->ip.pid, &threads, &last_match);
490
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200491 dump_printf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200492 (void *)(offset + head),
493 (void *)(long)(event->header.size),
494 event->header.misc,
495 event->ip.pid,
496 (void *)(long)ip);
497
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200498 dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
Ingo Molnar8035e422009-06-06 15:19:13 +0200499
500 if (thread == NULL) {
501 fprintf(stderr, "problem processing %d event, skipping it.\n",
502 event->header.type);
503 return -1;
504 }
505
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200506 if (event->header.misc & PERF_RECORD_MISC_KERNEL) {
Ingo Molnar8035e422009-06-06 15:19:13 +0200507 show = SHOW_KERNEL;
508 level = 'k';
509
510 dso = kernel_dso;
511
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200512 dump_printf(" ...... dso: %s\n", dso->name);
Ingo Molnar8035e422009-06-06 15:19:13 +0200513
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200514 } else if (event->header.misc & PERF_RECORD_MISC_USER) {
Ingo Molnar8035e422009-06-06 15:19:13 +0200515
516 show = SHOW_USER;
517 level = '.';
518
519 map = thread__find_map(thread, ip);
520 if (map != NULL) {
521 ip = map->map_ip(map, ip);
522 dso = map->dso;
523 } else {
524 /*
525 * If this is outside of all known maps,
526 * and is a negative address, try to look it
527 * up in the kernel dso, as it might be a
528 * vsyscall (which executes in user-mode):
529 */
530 if ((long long)ip < 0)
531 dso = kernel_dso;
532 }
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200533 dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
Ingo Molnar8035e422009-06-06 15:19:13 +0200534
535 } else {
536 show = SHOW_HV;
537 level = 'H';
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200538 dump_printf(" ...... dso: [hypervisor]\n");
Ingo Molnar8035e422009-06-06 15:19:13 +0200539 }
540
541 if (show & show_mask) {
542 struct symbol *sym = NULL;
543
544 if (dso)
545 sym = dso->find_symbol(dso, ip);
546
547 if (hist_entry__add(thread, map, dso, sym, ip, level)) {
548 fprintf(stderr,
549 "problem incrementing symbol count, skipping event\n");
550 return -1;
551 }
552 }
553 total++;
554
555 return 0;
556}
557
558static int
559process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
560{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200561 struct thread *thread;
Frederic Weisbecker66e274f2009-08-12 11:07:25 +0200562 struct map *map = map__new(&event->mmap, NULL, 0);
Ingo Molnar8035e422009-06-06 15:19:13 +0200563
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200564 thread = threads__findnew(event->mmap.pid, &threads, &last_match);
565
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200566 dump_printf("%p [%p]: PERF_RECORD_MMAP %d: [%p(%p) @ %p]: %s\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200567 (void *)(offset + head),
568 (void *)(long)(event->header.size),
569 event->mmap.pid,
570 (void *)(long)event->mmap.start,
571 (void *)(long)event->mmap.len,
572 (void *)(long)event->mmap.pgoff,
573 event->mmap.filename);
574
575 if (thread == NULL || map == NULL) {
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200576 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
Ingo Molnar8035e422009-06-06 15:19:13 +0200577 return 0;
578 }
579
580 thread__insert_map(thread, map);
581 total_mmap++;
582
583 return 0;
584}
585
586static int
587process_comm_event(event_t *event, unsigned long offset, unsigned long head)
588{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200589 struct thread *thread;
Ingo Molnar8035e422009-06-06 15:19:13 +0200590
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200591 thread = threads__findnew(event->comm.pid, &threads, &last_match);
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200592 dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200593 (void *)(offset + head),
594 (void *)(long)(event->header.size),
595 event->comm.comm, event->comm.pid);
596
597 if (thread == NULL ||
598 thread__set_comm(thread, event->comm.comm)) {
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200599 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
Ingo Molnar8035e422009-06-06 15:19:13 +0200600 return -1;
601 }
602 total_comm++;
603
604 return 0;
605}
606
607static int
608process_fork_event(event_t *event, unsigned long offset, unsigned long head)
609{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200610 struct thread *thread;
611 struct thread *parent;
Ingo Molnar8035e422009-06-06 15:19:13 +0200612
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200613 thread = threads__findnew(event->fork.pid, &threads, &last_match);
614 parent = threads__findnew(event->fork.ppid, &threads, &last_match);
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200615 dump_printf("%p [%p]: PERF_RECORD_FORK: %d:%d\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200616 (void *)(offset + head),
617 (void *)(long)(event->header.size),
618 event->fork.pid, event->fork.ppid);
619
Ingo Molnar15f3fa42009-08-18 13:52:28 +0200620 /*
621 * A thread clone will have the same PID for both
622 * parent and child.
623 */
624 if (thread == parent)
625 return 0;
626
Ingo Molnar8035e422009-06-06 15:19:13 +0200627 if (!thread || !parent || thread__fork(thread, parent)) {
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200628 dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
Ingo Molnar8035e422009-06-06 15:19:13 +0200629 return -1;
630 }
631 total_fork++;
632
633 return 0;
634}
635
636static int
Ingo Molnar8035e422009-06-06 15:19:13 +0200637process_event(event_t *event, unsigned long offset, unsigned long head)
638{
Ingo Molnar8035e422009-06-06 15:19:13 +0200639 switch (event->header.type) {
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200640 case PERF_RECORD_SAMPLE:
Peter Zijlstrae6e18ec2009-06-25 11:27:12 +0200641 return process_sample_event(event, offset, head);
642
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200643 case PERF_RECORD_MMAP:
Ingo Molnar8035e422009-06-06 15:19:13 +0200644 return process_mmap_event(event, offset, head);
645
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200646 case PERF_RECORD_COMM:
Ingo Molnar8035e422009-06-06 15:19:13 +0200647 return process_comm_event(event, offset, head);
648
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200649 case PERF_RECORD_FORK:
Ingo Molnar8035e422009-06-06 15:19:13 +0200650 return process_fork_event(event, offset, head);
Ingo Molnar8035e422009-06-06 15:19:13 +0200651 /*
652 * We dont process them right now but they are fine:
653 */
654
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200655 case PERF_RECORD_THROTTLE:
656 case PERF_RECORD_UNTHROTTLE:
Ingo Molnar8035e422009-06-06 15:19:13 +0200657 return 0;
658
659 default:
660 return -1;
661 }
662
663 return 0;
664}
665
Ingo Molnar0b73da32009-06-06 15:48:52 +0200666static int
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000667parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200668{
669 char *line = NULL, *tmp, *tmp2;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200670 static const char *prev_line;
671 static const char *prev_color;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200672 unsigned int offset;
673 size_t line_len;
Ingo Molnarf37a2912009-07-01 12:37:06 +0200674 s64 line_ip;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200675 int ret;
676 char *c;
677
678 if (getline(&line, &line_len, file) < 0)
679 return -1;
680 if (!line)
681 return -1;
682
683 c = strchr(line, '\n');
684 if (c)
685 *c = 0;
686
687 line_ip = -1;
688 offset = 0;
689 ret = -2;
690
691 /*
692 * Strip leading spaces:
693 */
694 tmp = line;
695 while (*tmp) {
696 if (*tmp != ' ')
697 break;
698 tmp++;
699 }
700
701 if (*tmp) {
702 /*
703 * Parse hexa addresses followed by ':'
704 */
705 line_ip = strtoull(tmp, &tmp2, 16);
706 if (*tmp2 != ':')
707 line_ip = -1;
708 }
709
710 if (line_ip != -1) {
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200711 const char *path = NULL;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200712 unsigned int hits = 0;
713 double percent = 0.0;
Ingo Molnar83a09442009-08-15 12:26:57 +0200714 const char *color;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200715 struct sym_ext *sym_ext = sym->priv;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200716
717 offset = line_ip - start;
718 if (offset < len)
719 hits = sym->hist[offset];
720
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200721 if (offset < len && sym_ext) {
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200722 path = sym_ext[offset].path;
723 percent = sym_ext[offset].percent;
724 } else if (sym->hist_sum)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200725 percent = 100.0 * hits / sym->hist_sum;
726
Frederic Weisbecker1e11fd82009-07-02 20:14:34 +0200727 color = get_percent_color(percent);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200728
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200729 /*
730 * Also color the filename and line if needed, with
731 * the same color than the percentage. Don't print it
732 * twice for close colored ip with the same filename:line
733 */
734 if (path) {
735 if (!prev_line || strcmp(prev_line, path)
736 || color != prev_color) {
737 color_fprintf(stdout, color, " %s", path);
738 prev_line = path;
739 prev_color = color;
740 }
741 }
742
Ingo Molnar0b73da32009-06-06 15:48:52 +0200743 color_fprintf(stdout, color, " %7.2f", percent);
744 printf(" : ");
745 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
746 } else {
747 if (!*line)
748 printf(" :\n");
749 else
750 printf(" : %s\n", line);
751 }
752
753 return 0;
754}
755
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200756static struct rb_root root_sym_ext;
757
758static void insert_source_line(struct sym_ext *sym_ext)
759{
760 struct sym_ext *iter;
761 struct rb_node **p = &root_sym_ext.rb_node;
762 struct rb_node *parent = NULL;
763
764 while (*p != NULL) {
765 parent = *p;
766 iter = rb_entry(parent, struct sym_ext, node);
767
768 if (sym_ext->percent > iter->percent)
769 p = &(*p)->rb_left;
770 else
771 p = &(*p)->rb_right;
772 }
773
774 rb_link_node(&sym_ext->node, parent, p);
775 rb_insert_color(&sym_ext->node, &root_sym_ext);
776}
777
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200778static void free_source_line(struct symbol *sym, int len)
779{
780 struct sym_ext *sym_ext = sym->priv;
781 int i;
782
783 if (!sym_ext)
784 return;
785
786 for (i = 0; i < len; i++)
787 free(sym_ext[i].path);
788 free(sym_ext);
789
790 sym->priv = NULL;
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200791 root_sym_ext = RB_ROOT;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200792}
793
794/* Get the filename:line for the colored entries */
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200795static void
Ingo Molnar83a09442009-08-15 12:26:57 +0200796get_source_line(struct symbol *sym, u64 start, int len, const char *filename)
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200797{
798 int i;
799 char cmd[PATH_MAX * 2];
800 struct sym_ext *sym_ext;
801
802 if (!sym->hist_sum)
803 return;
804
805 sym->priv = calloc(len, sizeof(struct sym_ext));
806 if (!sym->priv)
807 return;
808
809 sym_ext = sym->priv;
810
811 for (i = 0; i < len; i++) {
812 char *path = NULL;
813 size_t line_len;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000814 u64 offset;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200815 FILE *fp;
816
817 sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum;
818 if (sym_ext[i].percent <= 0.5)
819 continue;
820
821 offset = start + i;
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200822 sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200823 fp = popen(cmd, "r");
824 if (!fp)
825 continue;
826
827 if (getline(&path, &line_len, fp) < 0 || !line_len)
828 goto next;
829
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200830 sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200831 if (!sym_ext[i].path)
832 goto next;
833
834 strcpy(sym_ext[i].path, path);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200835 insert_source_line(&sym_ext[i]);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200836
837 next:
838 pclose(fp);
839 }
840}
841
Ingo Molnar83a09442009-08-15 12:26:57 +0200842static void print_summary(const char *filename)
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200843{
844 struct sym_ext *sym_ext;
845 struct rb_node *node;
846
847 printf("\nSorted summary for file %s\n", filename);
848 printf("----------------------------------------------\n\n");
849
850 if (RB_EMPTY_ROOT(&root_sym_ext)) {
851 printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
852 return;
853 }
854
855 node = rb_first(&root_sym_ext);
856 while (node) {
857 double percent;
Ingo Molnar83a09442009-08-15 12:26:57 +0200858 const char *color;
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200859 char *path;
860
861 sym_ext = rb_entry(node, struct sym_ext, node);
862 percent = sym_ext->percent;
Frederic Weisbecker1e11fd82009-07-02 20:14:34 +0200863 color = get_percent_color(percent);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200864 path = sym_ext->path;
865
866 color_fprintf(stdout, color, " %7.2f %s", percent, path);
867 node = rb_next(node);
868 }
869}
870
Ingo Molnar0b73da32009-06-06 15:48:52 +0200871static void annotate_sym(struct dso *dso, struct symbol *sym)
872{
Ingo Molnar83a09442009-08-15 12:26:57 +0200873 const char *filename = dso->name, *d_filename;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000874 u64 start, end, len;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200875 char command[PATH_MAX*2];
876 FILE *file;
877
878 if (!filename)
879 return;
Mike Galbraith42976482009-07-02 08:09:46 +0200880 if (sym->module)
881 filename = sym->module->path;
882 else if (dso == kernel_dso)
Ingo Molnar83a09442009-08-15 12:26:57 +0200883 filename = vmlinux_name;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200884
Ingo Molnar0b73da32009-06-06 15:48:52 +0200885 start = sym->obj_start;
886 if (!start)
887 start = sym->start;
Mike Galbraith42976482009-07-02 08:09:46 +0200888 if (full_paths)
889 d_filename = filename;
890 else
891 d_filename = basename(filename);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200892
893 end = start + sym->end - sym->start + 1;
894 len = sym->end - sym->start;
895
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200896 if (print_line) {
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200897 get_source_line(sym, start, len, filename);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200898 print_summary(filename);
899 }
900
901 printf("\n\n------------------------------------------------\n");
Mike Galbraith42976482009-07-02 08:09:46 +0200902 printf(" Percent | Source code & Disassembly of %s\n", d_filename);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200903 printf("------------------------------------------------\n");
904
905 if (verbose >= 2)
906 printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200907
Mike Galbraith42976482009-07-02 08:09:46 +0200908 sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
909 (u64)start, (u64)end, filename, filename);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200910
911 if (verbose >= 3)
912 printf("doing: %s\n", command);
913
914 file = popen(command, "r");
915 if (!file)
916 return;
917
918 while (!feof(file)) {
919 if (parse_line(file, sym, start, len) < 0)
920 break;
921 }
922
923 pclose(file);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200924 if (print_line)
925 free_source_line(sym, len);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200926}
927
928static void find_annotations(void)
929{
930 struct rb_node *nd;
931 struct dso *dso;
932 int count = 0;
933
934 list_for_each_entry(dso, &dsos, node) {
935
936 for (nd = rb_first(&dso->syms); nd; nd = rb_next(nd)) {
937 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
938
939 if (sym->hist) {
940 annotate_sym(dso, sym);
941 count++;
942 }
943 }
944 }
945
946 if (!count)
947 printf(" Error: symbol '%s' not present amongst the samples.\n", sym_hist_filter);
948}
949
Ingo Molnar8035e422009-06-06 15:19:13 +0200950static int __cmd_annotate(void)
951{
952 int ret, rc = EXIT_FAILURE;
953 unsigned long offset = 0;
954 unsigned long head = 0;
Ingo Molnar83a09442009-08-15 12:26:57 +0200955 struct stat input_stat;
Ingo Molnar8035e422009-06-06 15:19:13 +0200956 event_t *event;
957 uint32_t size;
958 char *buf;
959
Frederic Weisbecker5b447a62009-08-31 06:45:18 +0200960 register_idle_thread(&threads, &last_match);
Ingo Molnar8035e422009-06-06 15:19:13 +0200961
962 input = open(input_name, O_RDONLY);
963 if (input < 0) {
964 perror("failed to open file");
965 exit(-1);
966 }
967
Ingo Molnar83a09442009-08-15 12:26:57 +0200968 ret = fstat(input, &input_stat);
Ingo Molnar8035e422009-06-06 15:19:13 +0200969 if (ret < 0) {
970 perror("failed to stat file");
971 exit(-1);
972 }
973
Pierre Habouzit119e7a22009-08-27 09:59:02 +0200974 if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
975 fprintf(stderr, "file: %s not owned by current user or root\n", input_name);
Peter Zijlstrafa6963b2009-08-19 11:18:26 +0200976 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"),
Peter Zijlstrafa6963b2009-08-19 11:18:26 +02001084 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
Ingo Molnar8035e422009-06-06 15:19:13 +02001085 OPT_BOOLEAN('v', "verbose", &verbose,
1086 "be more verbose (show symbol address, etc)"),
1087 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1088 "dump raw trace in ASCII"),
Ingo Molnar83a09442009-08-15 12:26:57 +02001089 OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"),
Mike Galbraith42976482009-07-02 08:09:46 +02001090 OPT_BOOLEAN('m', "modules", &modules,
1091 "load module symbols - WARNING: use only with -k and LIVE kernel"),
Frederic Weisbecker301406b2009-06-13 00:11:21 +02001092 OPT_BOOLEAN('l', "print-line", &print_line,
1093 "print matching source lines (may be slow)"),
Mike Galbraith42976482009-07-02 08:09:46 +02001094 OPT_BOOLEAN('P', "full-paths", &full_paths,
1095 "Don't shorten the displayed pathnames"),
Ingo Molnar8035e422009-06-06 15:19:13 +02001096 OPT_END()
1097};
1098
1099static void setup_sorting(void)
1100{
1101 char *tmp, *tok, *str = strdup(sort_order);
1102
1103 for (tok = strtok_r(str, ", ", &tmp);
1104 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1105 if (sort_dimension__add(tok) < 0) {
1106 error("Unknown --sort key: `%s'", tok);
1107 usage_with_options(annotate_usage, options);
1108 }
1109 }
1110
1111 free(str);
1112}
1113
Ingo Molnarf37a2912009-07-01 12:37:06 +02001114int cmd_annotate(int argc, const char **argv, const char *prefix __used)
Ingo Molnar8035e422009-06-06 15:19:13 +02001115{
1116 symbol__init();
1117
1118 page_size = getpagesize();
1119
1120 argc = parse_options(argc, argv, options, annotate_usage, 0);
1121
1122 setup_sorting();
1123
Ingo Molnar0b73da32009-06-06 15:48:52 +02001124 if (argc) {
1125 /*
1126 * Special case: if there's an argument left then assume tha
1127 * it's a symbol filter:
1128 */
1129 if (argc > 1)
1130 usage_with_options(annotate_usage, options);
1131
1132 sym_hist_filter = argv[0];
1133 }
1134
1135 if (!sym_hist_filter)
Ingo Molnar8035e422009-06-06 15:19:13 +02001136 usage_with_options(annotate_usage, options);
1137
1138 setup_pager();
1139
1140 return __cmd_annotate();
1141}