blob: 059c565b31eaa34346d75784bcfb2103437fdda2 [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"
John Kacurdd68ada2009-09-24 18:02:49 +020025#include "util/sort.h"
Ingo Molnar8035e422009-06-06 15:19:13 +020026
Ingo Molnar8035e422009-06-06 15:19:13 +020027static char const *input_name = "perf.data";
Ingo Molnar8035e422009-06-06 15:19:13 +020028
Peter Zijlstrafa6963b2009-08-19 11:18:26 +020029static int force;
Ingo Molnar8035e422009-06-06 15:19:13 +020030static int input;
31static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
32
Mike Galbraith42976482009-07-02 08:09:46 +020033static int full_paths;
34
Frederic Weisbecker301406b2009-06-13 00:11:21 +020035static int print_line;
36
Ingo Molnar8035e422009-06-06 15:19:13 +020037static unsigned long page_size;
38static unsigned long mmap_window = 32;
39
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +020040static struct rb_root threads;
41static struct thread *last_match;
42
Frederic Weisbecker301406b2009-06-13 00:11:21 +020043
44struct sym_ext {
Frederic Weisbecker971738f2009-06-13 00:11:22 +020045 struct rb_node node;
Frederic Weisbecker301406b2009-06-13 00:11:21 +020046 double percent;
47 char *path;
48};
49
Ingo Molnar8035e422009-06-06 15:19:13 +020050/*
51 * histogram, sorted on item, collects counts
52 */
53
54static struct rb_root hist;
55
Ingo Molnar8035e422009-06-06 15:19:13 +020056static int64_t
57hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
58{
59 struct sort_entry *se;
60 int64_t cmp = 0;
61
62 list_for_each_entry(se, &hist_entry__sort_list, list) {
63 cmp = se->cmp(left, right);
64 if (cmp)
65 break;
66 }
67
68 return cmp;
69}
70
71static int64_t
72hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
73{
74 struct sort_entry *se;
75 int64_t cmp = 0;
76
77 list_for_each_entry(se, &hist_entry__sort_list, list) {
78 int64_t (*f)(struct hist_entry *, struct hist_entry *);
79
80 f = se->collapse ?: se->cmp;
81
82 cmp = f(left, right);
83 if (cmp)
84 break;
85 }
86
87 return cmp;
88}
89
Ingo Molnar8035e422009-06-06 15:19:13 +020090/*
91 * collect histogram counts
92 */
Paul Mackerras9cffa8d2009-06-19 22:21:42 +100093static void hist_hit(struct hist_entry *he, u64 ip)
Ingo Molnar0b73da32009-06-06 15:48:52 +020094{
95 unsigned int sym_size, offset;
96 struct symbol *sym = he->sym;
97
98 he->count++;
99
100 if (!sym || !sym->hist)
101 return;
102
103 sym_size = sym->end - sym->start;
104 offset = ip - sym->start;
105
106 if (offset >= sym_size)
107 return;
108
109 sym->hist_sum++;
110 sym->hist[offset]++;
111
112 if (verbose >= 3)
113 printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
Arjan van de Ven7d37a0c2009-06-06 20:36:38 +0200114 (void *)(unsigned long)he->sym->start,
Ingo Molnar0b73da32009-06-06 15:48:52 +0200115 he->sym->name,
Arjan van de Ven7d37a0c2009-06-06 20:36:38 +0200116 (void *)(unsigned long)ip, ip - he->sym->start,
Ingo Molnar0b73da32009-06-06 15:48:52 +0200117 sym->hist[offset]);
118}
Ingo Molnar8035e422009-06-06 15:19:13 +0200119
120static int
121hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000122 struct symbol *sym, u64 ip, char level)
Ingo Molnar8035e422009-06-06 15:19:13 +0200123{
124 struct rb_node **p = &hist.rb_node;
125 struct rb_node *parent = NULL;
126 struct hist_entry *he;
127 struct hist_entry entry = {
128 .thread = thread,
129 .map = map,
130 .dso = dso,
131 .sym = sym,
132 .ip = ip,
133 .level = level,
134 .count = 1,
135 };
136 int cmp;
137
138 while (*p != NULL) {
139 parent = *p;
140 he = rb_entry(parent, struct hist_entry, rb_node);
141
142 cmp = hist_entry__cmp(&entry, he);
143
144 if (!cmp) {
Ingo Molnar0b73da32009-06-06 15:48:52 +0200145 hist_hit(he, ip);
146
Ingo Molnar8035e422009-06-06 15:19:13 +0200147 return 0;
148 }
149
150 if (cmp < 0)
151 p = &(*p)->rb_left;
152 else
153 p = &(*p)->rb_right;
154 }
155
156 he = malloc(sizeof(*he));
157 if (!he)
158 return -ENOMEM;
159 *he = entry;
160 rb_link_node(&he->rb_node, parent, p);
161 rb_insert_color(&he->rb_node, &hist);
162
163 return 0;
164}
165
166static void hist_entry__free(struct hist_entry *he)
167{
168 free(he);
169}
170
171/*
172 * collapse the histogram
173 */
174
175static struct rb_root collapse_hists;
176
177static void collapse__insert_entry(struct hist_entry *he)
178{
179 struct rb_node **p = &collapse_hists.rb_node;
180 struct rb_node *parent = NULL;
181 struct hist_entry *iter;
182 int64_t cmp;
183
184 while (*p != NULL) {
185 parent = *p;
186 iter = rb_entry(parent, struct hist_entry, rb_node);
187
188 cmp = hist_entry__collapse(iter, he);
189
190 if (!cmp) {
191 iter->count += he->count;
192 hist_entry__free(he);
193 return;
194 }
195
196 if (cmp < 0)
197 p = &(*p)->rb_left;
198 else
199 p = &(*p)->rb_right;
200 }
201
202 rb_link_node(&he->rb_node, parent, p);
203 rb_insert_color(&he->rb_node, &collapse_hists);
204}
205
206static void collapse__resort(void)
207{
208 struct rb_node *next;
209 struct hist_entry *n;
210
211 if (!sort__need_collapse)
212 return;
213
214 next = rb_first(&hist);
215 while (next) {
216 n = rb_entry(next, struct hist_entry, rb_node);
217 next = rb_next(&n->rb_node);
218
219 rb_erase(&n->rb_node, &hist);
220 collapse__insert_entry(n);
221 }
222}
223
224/*
225 * reverse the map, sort on count.
226 */
227
228static struct rb_root output_hists;
229
230static void output__insert_entry(struct hist_entry *he)
231{
232 struct rb_node **p = &output_hists.rb_node;
233 struct rb_node *parent = NULL;
234 struct hist_entry *iter;
235
236 while (*p != NULL) {
237 parent = *p;
238 iter = rb_entry(parent, struct hist_entry, rb_node);
239
240 if (he->count > iter->count)
241 p = &(*p)->rb_left;
242 else
243 p = &(*p)->rb_right;
244 }
245
246 rb_link_node(&he->rb_node, parent, p);
247 rb_insert_color(&he->rb_node, &output_hists);
248}
249
250static void output__resort(void)
251{
252 struct rb_node *next;
253 struct hist_entry *n;
254 struct rb_root *tree = &hist;
255
256 if (sort__need_collapse)
257 tree = &collapse_hists;
258
259 next = rb_first(tree);
260
261 while (next) {
262 n = rb_entry(next, struct hist_entry, rb_node);
263 next = rb_next(&n->rb_node);
264
265 rb_erase(&n->rb_node, tree);
266 output__insert_entry(n);
267 }
268}
269
Ingo Molnar8035e422009-06-06 15:19:13 +0200270static unsigned long total = 0,
271 total_mmap = 0,
272 total_comm = 0,
273 total_fork = 0,
274 total_unknown = 0;
275
276static int
Peter Zijlstrae6e18ec2009-06-25 11:27:12 +0200277process_sample_event(event_t *event, unsigned long offset, unsigned long head)
Ingo Molnar8035e422009-06-06 15:19:13 +0200278{
279 char level;
280 int show = 0;
281 struct dso *dso = NULL;
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200282 struct thread *thread;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000283 u64 ip = event->ip.ip;
Ingo Molnar8035e422009-06-06 15:19:13 +0200284 struct map *map = NULL;
285
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200286 thread = threads__findnew(event->ip.pid, &threads, &last_match);
287
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200288 dump_printf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200289 (void *)(offset + head),
290 (void *)(long)(event->header.size),
291 event->header.misc,
292 event->ip.pid,
293 (void *)(long)ip);
294
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200295 dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
Ingo Molnar8035e422009-06-06 15:19:13 +0200296
297 if (thread == NULL) {
298 fprintf(stderr, "problem processing %d event, skipping it.\n",
299 event->header.type);
300 return -1;
301 }
302
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200303 if (event->header.misc & PERF_RECORD_MISC_KERNEL) {
Ingo Molnar8035e422009-06-06 15:19:13 +0200304 show = SHOW_KERNEL;
305 level = 'k';
306
307 dso = kernel_dso;
308
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200309 dump_printf(" ...... dso: %s\n", dso->name);
Ingo Molnar8035e422009-06-06 15:19:13 +0200310
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200311 } else if (event->header.misc & PERF_RECORD_MISC_USER) {
Ingo Molnar8035e422009-06-06 15:19:13 +0200312
313 show = SHOW_USER;
314 level = '.';
315
316 map = thread__find_map(thread, ip);
317 if (map != NULL) {
318 ip = map->map_ip(map, ip);
319 dso = map->dso;
320 } else {
321 /*
322 * If this is outside of all known maps,
323 * and is a negative address, try to look it
324 * up in the kernel dso, as it might be a
325 * vsyscall (which executes in user-mode):
326 */
327 if ((long long)ip < 0)
328 dso = kernel_dso;
329 }
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200330 dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
Ingo Molnar8035e422009-06-06 15:19:13 +0200331
332 } else {
333 show = SHOW_HV;
334 level = 'H';
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200335 dump_printf(" ...... dso: [hypervisor]\n");
Ingo Molnar8035e422009-06-06 15:19:13 +0200336 }
337
338 if (show & show_mask) {
339 struct symbol *sym = NULL;
340
341 if (dso)
342 sym = dso->find_symbol(dso, ip);
343
344 if (hist_entry__add(thread, map, dso, sym, ip, level)) {
345 fprintf(stderr,
346 "problem incrementing symbol count, skipping event\n");
347 return -1;
348 }
349 }
350 total++;
351
352 return 0;
353}
354
355static int
356process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
357{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200358 struct thread *thread;
Frederic Weisbecker66e274f2009-08-12 11:07:25 +0200359 struct map *map = map__new(&event->mmap, NULL, 0);
Ingo Molnar8035e422009-06-06 15:19:13 +0200360
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200361 thread = threads__findnew(event->mmap.pid, &threads, &last_match);
362
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200363 dump_printf("%p [%p]: PERF_RECORD_MMAP %d: [%p(%p) @ %p]: %s\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200364 (void *)(offset + head),
365 (void *)(long)(event->header.size),
366 event->mmap.pid,
367 (void *)(long)event->mmap.start,
368 (void *)(long)event->mmap.len,
369 (void *)(long)event->mmap.pgoff,
370 event->mmap.filename);
371
372 if (thread == NULL || map == NULL) {
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200373 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
Ingo Molnar8035e422009-06-06 15:19:13 +0200374 return 0;
375 }
376
377 thread__insert_map(thread, map);
378 total_mmap++;
379
380 return 0;
381}
382
383static int
384process_comm_event(event_t *event, unsigned long offset, unsigned long head)
385{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200386 struct thread *thread;
Ingo Molnar8035e422009-06-06 15:19:13 +0200387
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200388 thread = threads__findnew(event->comm.pid, &threads, &last_match);
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200389 dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200390 (void *)(offset + head),
391 (void *)(long)(event->header.size),
392 event->comm.comm, event->comm.pid);
393
394 if (thread == NULL ||
395 thread__set_comm(thread, event->comm.comm)) {
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200396 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
Ingo Molnar8035e422009-06-06 15:19:13 +0200397 return -1;
398 }
399 total_comm++;
400
401 return 0;
402}
403
404static int
405process_fork_event(event_t *event, unsigned long offset, unsigned long head)
406{
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200407 struct thread *thread;
408 struct thread *parent;
Ingo Molnar8035e422009-06-06 15:19:13 +0200409
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200410 thread = threads__findnew(event->fork.pid, &threads, &last_match);
411 parent = threads__findnew(event->fork.ppid, &threads, &last_match);
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200412 dump_printf("%p [%p]: PERF_RECORD_FORK: %d:%d\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200413 (void *)(offset + head),
414 (void *)(long)(event->header.size),
415 event->fork.pid, event->fork.ppid);
416
Ingo Molnar15f3fa42009-08-18 13:52:28 +0200417 /*
418 * A thread clone will have the same PID for both
419 * parent and child.
420 */
421 if (thread == parent)
422 return 0;
423
Ingo Molnar8035e422009-06-06 15:19:13 +0200424 if (!thread || !parent || thread__fork(thread, parent)) {
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200425 dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
Ingo Molnar8035e422009-06-06 15:19:13 +0200426 return -1;
427 }
428 total_fork++;
429
430 return 0;
431}
432
433static int
Ingo Molnar8035e422009-06-06 15:19:13 +0200434process_event(event_t *event, unsigned long offset, unsigned long head)
435{
Ingo Molnar8035e422009-06-06 15:19:13 +0200436 switch (event->header.type) {
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200437 case PERF_RECORD_SAMPLE:
Peter Zijlstrae6e18ec2009-06-25 11:27:12 +0200438 return process_sample_event(event, offset, head);
439
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200440 case PERF_RECORD_MMAP:
Ingo Molnar8035e422009-06-06 15:19:13 +0200441 return process_mmap_event(event, offset, head);
442
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200443 case PERF_RECORD_COMM:
Ingo Molnar8035e422009-06-06 15:19:13 +0200444 return process_comm_event(event, offset, head);
445
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200446 case PERF_RECORD_FORK:
Ingo Molnar8035e422009-06-06 15:19:13 +0200447 return process_fork_event(event, offset, head);
Ingo Molnar8035e422009-06-06 15:19:13 +0200448 /*
449 * We dont process them right now but they are fine:
450 */
451
Ingo Molnarcdd6c482009-09-21 12:02:48 +0200452 case PERF_RECORD_THROTTLE:
453 case PERF_RECORD_UNTHROTTLE:
Ingo Molnar8035e422009-06-06 15:19:13 +0200454 return 0;
455
456 default:
457 return -1;
458 }
459
460 return 0;
461}
462
Ingo Molnar0b73da32009-06-06 15:48:52 +0200463static int
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000464parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200465{
466 char *line = NULL, *tmp, *tmp2;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200467 static const char *prev_line;
468 static const char *prev_color;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200469 unsigned int offset;
470 size_t line_len;
Ingo Molnarf37a2912009-07-01 12:37:06 +0200471 s64 line_ip;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200472 int ret;
473 char *c;
474
475 if (getline(&line, &line_len, file) < 0)
476 return -1;
477 if (!line)
478 return -1;
479
480 c = strchr(line, '\n');
481 if (c)
482 *c = 0;
483
484 line_ip = -1;
485 offset = 0;
486 ret = -2;
487
488 /*
489 * Strip leading spaces:
490 */
491 tmp = line;
492 while (*tmp) {
493 if (*tmp != ' ')
494 break;
495 tmp++;
496 }
497
498 if (*tmp) {
499 /*
500 * Parse hexa addresses followed by ':'
501 */
502 line_ip = strtoull(tmp, &tmp2, 16);
503 if (*tmp2 != ':')
504 line_ip = -1;
505 }
506
507 if (line_ip != -1) {
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200508 const char *path = NULL;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200509 unsigned int hits = 0;
510 double percent = 0.0;
Ingo Molnar83a09442009-08-15 12:26:57 +0200511 const char *color;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200512 struct sym_ext *sym_ext = sym->priv;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200513
514 offset = line_ip - start;
515 if (offset < len)
516 hits = sym->hist[offset];
517
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200518 if (offset < len && sym_ext) {
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200519 path = sym_ext[offset].path;
520 percent = sym_ext[offset].percent;
521 } else if (sym->hist_sum)
Ingo Molnar0b73da32009-06-06 15:48:52 +0200522 percent = 100.0 * hits / sym->hist_sum;
523
Frederic Weisbecker1e11fd82009-07-02 20:14:34 +0200524 color = get_percent_color(percent);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200525
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200526 /*
527 * Also color the filename and line if needed, with
528 * the same color than the percentage. Don't print it
529 * twice for close colored ip with the same filename:line
530 */
531 if (path) {
532 if (!prev_line || strcmp(prev_line, path)
533 || color != prev_color) {
534 color_fprintf(stdout, color, " %s", path);
535 prev_line = path;
536 prev_color = color;
537 }
538 }
539
Ingo Molnar0b73da32009-06-06 15:48:52 +0200540 color_fprintf(stdout, color, " %7.2f", percent);
541 printf(" : ");
542 color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line);
543 } else {
544 if (!*line)
545 printf(" :\n");
546 else
547 printf(" : %s\n", line);
548 }
549
550 return 0;
551}
552
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200553static struct rb_root root_sym_ext;
554
555static void insert_source_line(struct sym_ext *sym_ext)
556{
557 struct sym_ext *iter;
558 struct rb_node **p = &root_sym_ext.rb_node;
559 struct rb_node *parent = NULL;
560
561 while (*p != NULL) {
562 parent = *p;
563 iter = rb_entry(parent, struct sym_ext, node);
564
565 if (sym_ext->percent > iter->percent)
566 p = &(*p)->rb_left;
567 else
568 p = &(*p)->rb_right;
569 }
570
571 rb_link_node(&sym_ext->node, parent, p);
572 rb_insert_color(&sym_ext->node, &root_sym_ext);
573}
574
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200575static void free_source_line(struct symbol *sym, int len)
576{
577 struct sym_ext *sym_ext = sym->priv;
578 int i;
579
580 if (!sym_ext)
581 return;
582
583 for (i = 0; i < len; i++)
584 free(sym_ext[i].path);
585 free(sym_ext);
586
587 sym->priv = NULL;
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200588 root_sym_ext = RB_ROOT;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200589}
590
591/* Get the filename:line for the colored entries */
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200592static void
Ingo Molnar83a09442009-08-15 12:26:57 +0200593get_source_line(struct symbol *sym, u64 start, int len, const char *filename)
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200594{
595 int i;
596 char cmd[PATH_MAX * 2];
597 struct sym_ext *sym_ext;
598
599 if (!sym->hist_sum)
600 return;
601
602 sym->priv = calloc(len, sizeof(struct sym_ext));
603 if (!sym->priv)
604 return;
605
606 sym_ext = sym->priv;
607
608 for (i = 0; i < len; i++) {
609 char *path = NULL;
610 size_t line_len;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000611 u64 offset;
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200612 FILE *fp;
613
614 sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum;
615 if (sym_ext[i].percent <= 0.5)
616 continue;
617
618 offset = start + i;
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200619 sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200620 fp = popen(cmd, "r");
621 if (!fp)
622 continue;
623
624 if (getline(&path, &line_len, fp) < 0 || !line_len)
625 goto next;
626
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200627 sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200628 if (!sym_ext[i].path)
629 goto next;
630
631 strcpy(sym_ext[i].path, path);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200632 insert_source_line(&sym_ext[i]);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200633
634 next:
635 pclose(fp);
636 }
637}
638
Ingo Molnar83a09442009-08-15 12:26:57 +0200639static void print_summary(const char *filename)
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200640{
641 struct sym_ext *sym_ext;
642 struct rb_node *node;
643
644 printf("\nSorted summary for file %s\n", filename);
645 printf("----------------------------------------------\n\n");
646
647 if (RB_EMPTY_ROOT(&root_sym_ext)) {
648 printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
649 return;
650 }
651
652 node = rb_first(&root_sym_ext);
653 while (node) {
654 double percent;
Ingo Molnar83a09442009-08-15 12:26:57 +0200655 const char *color;
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200656 char *path;
657
658 sym_ext = rb_entry(node, struct sym_ext, node);
659 percent = sym_ext->percent;
Frederic Weisbecker1e11fd82009-07-02 20:14:34 +0200660 color = get_percent_color(percent);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200661 path = sym_ext->path;
662
663 color_fprintf(stdout, color, " %7.2f %s", percent, path);
664 node = rb_next(node);
665 }
666}
667
Ingo Molnar0b73da32009-06-06 15:48:52 +0200668static void annotate_sym(struct dso *dso, struct symbol *sym)
669{
Ingo Molnar83a09442009-08-15 12:26:57 +0200670 const char *filename = dso->name, *d_filename;
Paul Mackerras9cffa8d2009-06-19 22:21:42 +1000671 u64 start, end, len;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200672 char command[PATH_MAX*2];
673 FILE *file;
674
675 if (!filename)
676 return;
Mike Galbraith42976482009-07-02 08:09:46 +0200677 if (sym->module)
678 filename = sym->module->path;
679 else if (dso == kernel_dso)
Ingo Molnar83a09442009-08-15 12:26:57 +0200680 filename = vmlinux_name;
Ingo Molnar0b73da32009-06-06 15:48:52 +0200681
Ingo Molnar0b73da32009-06-06 15:48:52 +0200682 start = sym->obj_start;
683 if (!start)
684 start = sym->start;
Mike Galbraith42976482009-07-02 08:09:46 +0200685 if (full_paths)
686 d_filename = filename;
687 else
688 d_filename = basename(filename);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200689
690 end = start + sym->end - sym->start + 1;
691 len = sym->end - sym->start;
692
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200693 if (print_line) {
Frederic Weisbeckerc17c2db2009-06-13 17:39:23 +0200694 get_source_line(sym, start, len, filename);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200695 print_summary(filename);
696 }
697
698 printf("\n\n------------------------------------------------\n");
Mike Galbraith42976482009-07-02 08:09:46 +0200699 printf(" Percent | Source code & Disassembly of %s\n", d_filename);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200700 printf("------------------------------------------------\n");
701
702 if (verbose >= 2)
703 printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name);
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200704
Mike Galbraith42976482009-07-02 08:09:46 +0200705 sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
706 (u64)start, (u64)end, filename, filename);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200707
708 if (verbose >= 3)
709 printf("doing: %s\n", command);
710
711 file = popen(command, "r");
712 if (!file)
713 return;
714
715 while (!feof(file)) {
716 if (parse_line(file, sym, start, len) < 0)
717 break;
718 }
719
720 pclose(file);
Frederic Weisbecker971738f2009-06-13 00:11:22 +0200721 if (print_line)
722 free_source_line(sym, len);
Ingo Molnar0b73da32009-06-06 15:48:52 +0200723}
724
725static void find_annotations(void)
726{
727 struct rb_node *nd;
728 struct dso *dso;
729 int count = 0;
730
731 list_for_each_entry(dso, &dsos, node) {
732
733 for (nd = rb_first(&dso->syms); nd; nd = rb_next(nd)) {
734 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
735
736 if (sym->hist) {
737 annotate_sym(dso, sym);
738 count++;
739 }
740 }
741 }
742
743 if (!count)
744 printf(" Error: symbol '%s' not present amongst the samples.\n", sym_hist_filter);
745}
746
Ingo Molnar8035e422009-06-06 15:19:13 +0200747static int __cmd_annotate(void)
748{
749 int ret, rc = EXIT_FAILURE;
750 unsigned long offset = 0;
751 unsigned long head = 0;
Ingo Molnar83a09442009-08-15 12:26:57 +0200752 struct stat input_stat;
Ingo Molnar8035e422009-06-06 15:19:13 +0200753 event_t *event;
754 uint32_t size;
755 char *buf;
756
Frederic Weisbecker5b447a62009-08-31 06:45:18 +0200757 register_idle_thread(&threads, &last_match);
Ingo Molnar8035e422009-06-06 15:19:13 +0200758
759 input = open(input_name, O_RDONLY);
760 if (input < 0) {
761 perror("failed to open file");
762 exit(-1);
763 }
764
Ingo Molnar83a09442009-08-15 12:26:57 +0200765 ret = fstat(input, &input_stat);
Ingo Molnar8035e422009-06-06 15:19:13 +0200766 if (ret < 0) {
767 perror("failed to stat file");
768 exit(-1);
769 }
770
Pierre Habouzit119e7a22009-08-27 09:59:02 +0200771 if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
772 fprintf(stderr, "file: %s not owned by current user or root\n", input_name);
Peter Zijlstrafa6963b2009-08-19 11:18:26 +0200773 exit(-1);
774 }
775
Ingo Molnar83a09442009-08-15 12:26:57 +0200776 if (!input_stat.st_size) {
Ingo Molnar8035e422009-06-06 15:19:13 +0200777 fprintf(stderr, "zero-sized file, nothing to do!\n");
778 exit(0);
779 }
780
781 if (load_kernel() < 0) {
782 perror("failed to load kernel symbols");
783 return EXIT_FAILURE;
784 }
785
Ingo Molnar8035e422009-06-06 15:19:13 +0200786remap:
787 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
788 MAP_SHARED, input, offset);
789 if (buf == MAP_FAILED) {
790 perror("failed to mmap file");
791 exit(-1);
792 }
793
794more:
795 event = (event_t *)(buf + head);
796
797 size = event->header.size;
798 if (!size)
799 size = 8;
800
801 if (head + event->header.size >= page_size * mmap_window) {
802 unsigned long shift = page_size * (head / page_size);
Ingo Molnar83a09442009-08-15 12:26:57 +0200803 int munmap_ret;
Ingo Molnar8035e422009-06-06 15:19:13 +0200804
Ingo Molnar83a09442009-08-15 12:26:57 +0200805 munmap_ret = munmap(buf, page_size * mmap_window);
806 assert(munmap_ret == 0);
Ingo Molnar8035e422009-06-06 15:19:13 +0200807
808 offset += shift;
809 head -= shift;
810 goto remap;
811 }
812
813 size = event->header.size;
814
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200815 dump_printf("%p [%p]: event: %d\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200816 (void *)(offset + head),
817 (void *)(long)event->header.size,
818 event->header.type);
819
820 if (!size || process_event(event, offset, head) < 0) {
821
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200822 dump_printf("%p [%p]: skipping unknown header type: %d\n",
Ingo Molnar8035e422009-06-06 15:19:13 +0200823 (void *)(offset + head),
824 (void *)(long)(event->header.size),
825 event->header.type);
826
827 total_unknown++;
828
829 /*
830 * assume we lost track of the stream, check alignment, and
831 * increment a single u64 in the hope to catch on again 'soon'.
832 */
833
834 if (unlikely(head & 7))
835 head &= ~7ULL;
836
837 size = 8;
838 }
839
840 head += size;
841
Ingo Molnar83a09442009-08-15 12:26:57 +0200842 if (offset + head < (unsigned long)input_stat.st_size)
Ingo Molnar8035e422009-06-06 15:19:13 +0200843 goto more;
844
845 rc = EXIT_SUCCESS;
846 close(input);
847
Frederic Weisbecker2cec19d2009-08-16 19:24:21 +0200848 dump_printf(" IP events: %10ld\n", total);
849 dump_printf(" mmap events: %10ld\n", total_mmap);
850 dump_printf(" comm events: %10ld\n", total_comm);
851 dump_printf(" fork events: %10ld\n", total_fork);
852 dump_printf(" unknown events: %10ld\n", total_unknown);
Ingo Molnar8035e422009-06-06 15:19:13 +0200853
854 if (dump_trace)
855 return 0;
856
857 if (verbose >= 3)
Frederic Weisbecker6baa0a52009-08-14 12:21:53 +0200858 threads__fprintf(stdout, &threads);
Ingo Molnar8035e422009-06-06 15:19:13 +0200859
860 if (verbose >= 2)
861 dsos__fprintf(stdout);
862
863 collapse__resort();
864 output__resort();
Ingo Molnar0b73da32009-06-06 15:48:52 +0200865
866 find_annotations();
Ingo Molnar8035e422009-06-06 15:19:13 +0200867
868 return rc;
869}
870
871static const char * const annotate_usage[] = {
872 "perf annotate [<options>] <command>",
873 NULL
874};
875
876static const struct option options[] = {
877 OPT_STRING('i', "input", &input_name, "file",
878 "input file name"),
Ingo Molnar23b87112009-06-06 21:25:29 +0200879 OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
Ingo Molnar0b73da32009-06-06 15:48:52 +0200880 "symbol to annotate"),
Peter Zijlstrafa6963b2009-08-19 11:18:26 +0200881 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
Ingo Molnar8035e422009-06-06 15:19:13 +0200882 OPT_BOOLEAN('v', "verbose", &verbose,
883 "be more verbose (show symbol address, etc)"),
884 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
885 "dump raw trace in ASCII"),
Ingo Molnar83a09442009-08-15 12:26:57 +0200886 OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"),
Mike Galbraith42976482009-07-02 08:09:46 +0200887 OPT_BOOLEAN('m', "modules", &modules,
888 "load module symbols - WARNING: use only with -k and LIVE kernel"),
Frederic Weisbecker301406b2009-06-13 00:11:21 +0200889 OPT_BOOLEAN('l', "print-line", &print_line,
890 "print matching source lines (may be slow)"),
Mike Galbraith42976482009-07-02 08:09:46 +0200891 OPT_BOOLEAN('P', "full-paths", &full_paths,
892 "Don't shorten the displayed pathnames"),
Ingo Molnar8035e422009-06-06 15:19:13 +0200893 OPT_END()
894};
895
896static void setup_sorting(void)
897{
898 char *tmp, *tok, *str = strdup(sort_order);
899
900 for (tok = strtok_r(str, ", ", &tmp);
901 tok; tok = strtok_r(NULL, ", ", &tmp)) {
902 if (sort_dimension__add(tok) < 0) {
903 error("Unknown --sort key: `%s'", tok);
904 usage_with_options(annotate_usage, options);
905 }
906 }
907
908 free(str);
909}
910
Ingo Molnarf37a2912009-07-01 12:37:06 +0200911int cmd_annotate(int argc, const char **argv, const char *prefix __used)
Ingo Molnar8035e422009-06-06 15:19:13 +0200912{
913 symbol__init();
914
915 page_size = getpagesize();
916
917 argc = parse_options(argc, argv, options, annotate_usage, 0);
918
919 setup_sorting();
920
Ingo Molnar0b73da32009-06-06 15:48:52 +0200921 if (argc) {
922 /*
923 * Special case: if there's an argument left then assume tha
924 * it's a symbol filter:
925 */
926 if (argc > 1)
927 usage_with_options(annotate_usage, options);
928
929 sym_hist_filter = argv[0];
930 }
931
932 if (!sym_hist_filter)
Ingo Molnar8035e422009-06-06 15:19:13 +0200933 usage_with_options(annotate_usage, options);
934
935 setup_pager();
936
John Kacurdd68ada2009-09-24 18:02:49 +0200937 if (field_sep && *field_sep == '.') {
938 fputs("'.' is the only non valid --field-separator argument\n",
939 stderr);
940 exit(129);
941 }
942
Ingo Molnar8035e422009-06-06 15:19:13 +0200943 return __cmd_annotate();
944}