]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - tools/perf/builtin-annotate.c
perf tools: Create util/sort.and use it
[mirror_ubuntu-artful-kernel.git] / tools / perf / builtin-annotate.c
CommitLineData
8035e428
IM
1/*
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"
5da50258 13#include <linux/list.h>
8035e428 14#include "util/cache.h"
43cbcd8a 15#include <linux/rbtree.h>
8035e428
IM
16#include "util/symbol.h"
17#include "util/string.h"
18
19#include "perf.h"
8f28827a 20#include "util/debug.h"
8035e428
IM
21
22#include "util/parse-options.h"
23#include "util/parse-events.h"
6baa0a5a 24#include "util/thread.h"
dd68ada2 25#include "util/sort.h"
8035e428 26
8035e428 27static char const *input_name = "perf.data";
8035e428 28
fa6963b2 29static int force;
8035e428
IM
30static int input;
31static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
32
42976487
MG
33static int full_paths;
34
301406b9
FW
35static int print_line;
36
8035e428
IM
37static unsigned long page_size;
38static unsigned long mmap_window = 32;
39
6baa0a5a
FW
40static struct rb_root threads;
41static struct thread *last_match;
42
301406b9
FW
43
44struct sym_ext {
971738f3 45 struct rb_node node;
301406b9
FW
46 double percent;
47 char *path;
48};
49
8035e428
IM
50/*
51 * histogram, sorted on item, collects counts
52 */
53
54static struct rb_root hist;
55
8035e428
IM
56static 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
0b73da3f
IM
90/*
91 * collect histogram counts
92 */
9cffa8d5 93static void hist_hit(struct hist_entry *he, u64 ip)
8035e428 94{
0b73da3f
IM
95 unsigned int sym_size, offset;
96 struct symbol *sym = he->sym;
8035e428 97
0b73da3f 98 he->count++;
8035e428 99
0b73da3f
IM
100 if (!sym || !sym->hist)
101 return;
8035e428 102
0b73da3f
IM
103 sym_size = sym->end - sym->start;
104 offset = ip - sym->start;
8035e428 105
0b73da3f
IM
106 if (offset >= sym_size)
107 return;
8035e428 108
0b73da3f
IM
109 sym->hist_sum++;
110 sym->hist[offset]++;
8035e428 111
0b73da3f
IM
112 if (verbose >= 3)
113 printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n",
7d37a0cb 114 (void *)(unsigned long)he->sym->start,
0b73da3f 115 he->sym->name,
7d37a0cb 116 (void *)(unsigned long)ip, ip - he->sym->start,
0b73da3f 117 sym->hist[offset]);
8035e428
IM
118}
119
8035e428
IM
120static int
121hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
9cffa8d5 122 struct symbol *sym, u64 ip, char level)
8035e428
IM
123{
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) {
0b73da3f
IM
145 hist_hit(he, ip);
146
8035e428
IM
147 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
8035e428
IM
270static unsigned long total = 0,
271 total_mmap = 0,
272 total_comm = 0,
273 total_fork = 0,
274 total_unknown = 0;
275
276static int
e6e18ec7 277process_sample_event(event_t *event, unsigned long offset, unsigned long head)
8035e428
IM
278{
279 char level;
280 int show = 0;
281 struct dso *dso = NULL;
6baa0a5a 282 struct thread *thread;
9cffa8d5 283 u64 ip = event->ip.ip;
8035e428
IM
284 struct map *map = NULL;
285
6baa0a5a
FW
286 thread = threads__findnew(event->ip.pid, &threads, &last_match);
287
2cec19d9 288 dump_printf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
8035e428
IM
289 (void *)(offset + head),
290 (void *)(long)(event->header.size),
291 event->header.misc,
292 event->ip.pid,
293 (void *)(long)ip);
294
2cec19d9 295 dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
8035e428
IM
296
297 if (thread == NULL) {
298 fprintf(stderr, "problem processing %d event, skipping it.\n",
299 event->header.type);
300 return -1;
301 }
302
cdd6c482 303 if (event->header.misc & PERF_RECORD_MISC_KERNEL) {
8035e428
IM
304 show = SHOW_KERNEL;
305 level = 'k';
306
307 dso = kernel_dso;
308
2cec19d9 309 dump_printf(" ...... dso: %s\n", dso->name);
8035e428 310
cdd6c482 311 } else if (event->header.misc & PERF_RECORD_MISC_USER) {
8035e428
IM
312
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 }
2cec19d9 330 dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
8035e428
IM
331
332 } else {
333 show = SHOW_HV;
334 level = 'H';
2cec19d9 335 dump_printf(" ...... dso: [hypervisor]\n");
8035e428
IM
336 }
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{
6baa0a5a 358 struct thread *thread;
66e274f3 359 struct map *map = map__new(&event->mmap, NULL, 0);
8035e428 360
6baa0a5a
FW
361 thread = threads__findnew(event->mmap.pid, &threads, &last_match);
362
cdd6c482 363 dump_printf("%p [%p]: PERF_RECORD_MMAP %d: [%p(%p) @ %p]: %s\n",
8035e428
IM
364 (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) {
cdd6c482 373 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
8035e428
IM
374 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{
6baa0a5a 386 struct thread *thread;
8035e428 387
6baa0a5a 388 thread = threads__findnew(event->comm.pid, &threads, &last_match);
cdd6c482 389 dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n",
8035e428
IM
390 (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)) {
cdd6c482 396 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
8035e428
IM
397 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{
6baa0a5a
FW
407 struct thread *thread;
408 struct thread *parent;
8035e428 409
6baa0a5a
FW
410 thread = threads__findnew(event->fork.pid, &threads, &last_match);
411 parent = threads__findnew(event->fork.ppid, &threads, &last_match);
cdd6c482 412 dump_printf("%p [%p]: PERF_RECORD_FORK: %d:%d\n",
8035e428
IM
413 (void *)(offset + head),
414 (void *)(long)(event->header.size),
415 event->fork.pid, event->fork.ppid);
416
15f3fa4e
IM
417 /*
418 * A thread clone will have the same PID for both
419 * parent and child.
420 */
421 if (thread == parent)
422 return 0;
423
8035e428 424 if (!thread || !parent || thread__fork(thread, parent)) {
cdd6c482 425 dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
8035e428
IM
426 return -1;
427 }
428 total_fork++;
429
430 return 0;
431}
432
8035e428
IM
433static int
434process_event(event_t *event, unsigned long offset, unsigned long head)
435{
8035e428 436 switch (event->header.type) {
cdd6c482 437 case PERF_RECORD_SAMPLE:
e6e18ec7
PZ
438 return process_sample_event(event, offset, head);
439
cdd6c482 440 case PERF_RECORD_MMAP:
8035e428
IM
441 return process_mmap_event(event, offset, head);
442
cdd6c482 443 case PERF_RECORD_COMM:
8035e428
IM
444 return process_comm_event(event, offset, head);
445
cdd6c482 446 case PERF_RECORD_FORK:
8035e428 447 return process_fork_event(event, offset, head);
8035e428
IM
448 /*
449 * We dont process them right now but they are fine:
450 */
451
cdd6c482
IM
452 case PERF_RECORD_THROTTLE:
453 case PERF_RECORD_UNTHROTTLE:
8035e428
IM
454 return 0;
455
456 default:
457 return -1;
458 }
459
460 return 0;
461}
462
0b73da3f 463static int
9cffa8d5 464parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)
0b73da3f
IM
465{
466 char *line = NULL, *tmp, *tmp2;
301406b9
FW
467 static const char *prev_line;
468 static const char *prev_color;
0b73da3f
IM
469 unsigned int offset;
470 size_t line_len;
f37a291c 471 s64 line_ip;
0b73da3f
IM
472 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) {
301406b9 508 const char *path = NULL;
0b73da3f
IM
509 unsigned int hits = 0;
510 double percent = 0.0;
83a0944f 511 const char *color;
301406b9 512 struct sym_ext *sym_ext = sym->priv;
0b73da3f
IM
513
514 offset = line_ip - start;
515 if (offset < len)
516 hits = sym->hist[offset];
517
c17c2db1 518 if (offset < len && sym_ext) {
301406b9
FW
519 path = sym_ext[offset].path;
520 percent = sym_ext[offset].percent;
521 } else if (sym->hist_sum)
0b73da3f
IM
522 percent = 100.0 * hits / sym->hist_sum;
523
1e11fd82 524 color = get_percent_color(percent);
0b73da3f 525
301406b9
FW
526 /*
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
0b73da3f
IM
540 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
971738f3
FW
553static 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
301406b9
FW
575static 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;
971738f3 588 root_sym_ext = RB_ROOT;
301406b9
FW
589}
590
591/* Get the filename:line for the colored entries */
c17c2db1 592static void
83a0944f 593get_source_line(struct symbol *sym, u64 start, int len, const char *filename)
301406b9
FW
594{
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;
9cffa8d5 611 u64 offset;
301406b9
FW
612 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;
c17c2db1 619 sprintf(cmd, "addr2line -e %s %016llx", filename, offset);
301406b9
FW
620 fp = popen(cmd, "r");
621 if (!fp)
622 continue;
623
624 if (getline(&path, &line_len, fp) < 0 || !line_len)
625 goto next;
626
c17c2db1 627 sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
301406b9
FW
628 if (!sym_ext[i].path)
629 goto next;
630
631 strcpy(sym_ext[i].path, path);
971738f3 632 insert_source_line(&sym_ext[i]);
301406b9
FW
633
634 next:
635 pclose(fp);
636 }
637}
638
83a0944f 639static void print_summary(const char *filename)
971738f3
FW
640{
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;
83a0944f 655 const char *color;
971738f3
FW
656 char *path;
657
658 sym_ext = rb_entry(node, struct sym_ext, node);
659 percent = sym_ext->percent;
1e11fd82 660 color = get_percent_color(percent);
971738f3
FW
661 path = sym_ext->path;
662
663 color_fprintf(stdout, color, " %7.2f %s", percent, path);
664 node = rb_next(node);
665 }
666}
667
0b73da3f
IM
668static void annotate_sym(struct dso *dso, struct symbol *sym)
669{
83a0944f 670 const char *filename = dso->name, *d_filename;
9cffa8d5 671 u64 start, end, len;
0b73da3f
IM
672 char command[PATH_MAX*2];
673 FILE *file;
674
675 if (!filename)
676 return;
42976487
MG
677 if (sym->module)
678 filename = sym->module->path;
679 else if (dso == kernel_dso)
83a0944f 680 filename = vmlinux_name;
0b73da3f 681
0b73da3f
IM
682 start = sym->obj_start;
683 if (!start)
684 start = sym->start;
42976487
MG
685 if (full_paths)
686 d_filename = filename;
687 else
688 d_filename = basename(filename);
0b73da3f
IM
689
690 end = start + sym->end - sym->start + 1;
691 len = sym->end - sym->start;
692
971738f3 693 if (print_line) {
c17c2db1 694 get_source_line(sym, start, len, filename);
971738f3
FW
695 print_summary(filename);
696 }
697
698 printf("\n\n------------------------------------------------\n");
42976487 699 printf(" Percent | Source code & Disassembly of %s\n", d_filename);
971738f3
FW
700 printf("------------------------------------------------\n");
701
702 if (verbose >= 2)
703 printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name);
301406b9 704
42976487
MG
705 sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s",
706 (u64)start, (u64)end, filename, filename);
0b73da3f
IM
707
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);
971738f3
FW
721 if (print_line)
722 free_source_line(sym, len);
0b73da3f
IM
723}
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
8035e428
IM
747static int __cmd_annotate(void)
748{
749 int ret, rc = EXIT_FAILURE;
750 unsigned long offset = 0;
751 unsigned long head = 0;
83a0944f 752 struct stat input_stat;
8035e428
IM
753 event_t *event;
754 uint32_t size;
755 char *buf;
756
5b447a6a 757 register_idle_thread(&threads, &last_match);
8035e428
IM
758
759 input = open(input_name, O_RDONLY);
760 if (input < 0) {
761 perror("failed to open file");
762 exit(-1);
763 }
764
83a0944f 765 ret = fstat(input, &input_stat);
8035e428
IM
766 if (ret < 0) {
767 perror("failed to stat file");
768 exit(-1);
769 }
770
119e7a22
PH
771 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);
fa6963b2
PZ
773 exit(-1);
774 }
775
83a0944f 776 if (!input_stat.st_size) {
8035e428
IM
777 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
8035e428
IM
786remap:
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);
83a0944f 803 int munmap_ret;
8035e428 804
83a0944f
IM
805 munmap_ret = munmap(buf, page_size * mmap_window);
806 assert(munmap_ret == 0);
8035e428
IM
807
808 offset += shift;
809 head -= shift;
810 goto remap;
811 }
812
813 size = event->header.size;
814
2cec19d9 815 dump_printf("%p [%p]: event: %d\n",
8035e428
IM
816 (void *)(offset + head),
817 (void *)(long)event->header.size,
818 event->header.type);
819
820 if (!size || process_event(event, offset, head) < 0) {
821
2cec19d9 822 dump_printf("%p [%p]: skipping unknown header type: %d\n",
8035e428
IM
823 (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
83a0944f 842 if (offset + head < (unsigned long)input_stat.st_size)
8035e428
IM
843 goto more;
844
845 rc = EXIT_SUCCESS;
846 close(input);
847
2cec19d9
FW
848 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);
8035e428
IM
853
854 if (dump_trace)
855 return 0;
856
857 if (verbose >= 3)
6baa0a5a 858 threads__fprintf(stdout, &threads);
8035e428
IM
859
860 if (verbose >= 2)
861 dsos__fprintf(stdout);
862
863 collapse__resort();
864 output__resort();
0b73da3f
IM
865
866 find_annotations();
8035e428
IM
867
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"),
23b87116 879 OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
0b73da3f 880 "symbol to annotate"),
fa6963b2 881 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
8035e428
IM
882 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"),
83a0944f 886 OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"),
42976487
MG
887 OPT_BOOLEAN('m', "modules", &modules,
888 "load module symbols - WARNING: use only with -k and LIVE kernel"),
301406b9
FW
889 OPT_BOOLEAN('l', "print-line", &print_line,
890 "print matching source lines (may be slow)"),
42976487
MG
891 OPT_BOOLEAN('P', "full-paths", &full_paths,
892 "Don't shorten the displayed pathnames"),
8035e428
IM
893 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
f37a291c 911int cmd_annotate(int argc, const char **argv, const char *prefix __used)
8035e428
IM
912{
913 symbol__init();
914
915 page_size = getpagesize();
916
917 argc = parse_options(argc, argv, options, annotate_usage, 0);
918
919 setup_sorting();
920
0b73da3f
IM
921 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)
8035e428
IM
933 usage_with_options(annotate_usage, options);
934
935 setup_pager();
936
dd68ada2
JK
937 if (field_sep && *field_sep == '.') {
938 fputs("'.' is the only non valid --field-separator argument\n",
939 stderr);
940 exit(129);
941 }
942
8035e428
IM
943 return __cmd_annotate();
944}