perf_counter: Fix frequency adjustment for < HZ
[deliverable/linux.git] / Documentation / perf_counter / builtin-report.c
CommitLineData
bf9e1876
IM
1/*
2 * builtin-report.c
3 *
4 * Builtin report 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 */
16f762a2 8#include "builtin.h"
53cb8bc2 9
bf9e1876
IM
10#include "util/util.h"
11
8fc0321f 12#include "util/color.h"
35a50c8a 13#include "util/list.h"
a930d2c0 14#include "util/cache.h"
35a50c8a 15#include "util/rbtree.h"
a2928c42 16#include "util/symbol.h"
a0055ae2 17#include "util/string.h"
8fa66bdc 18
53cb8bc2
IM
19#include "perf.h"
20
21#include "util/parse-options.h"
22#include "util/parse-events.h"
23
8fa66bdc
ACM
24#define SHOW_KERNEL 1
25#define SHOW_USER 2
26#define SHOW_HV 4
27
23ac9cbe 28static char const *input_name = "perf.data";
450aaa2b 29static char *vmlinux = NULL;
bd74137e
IM
30
31static char default_sort_order[] = "comm,dso";
32static char *sort_order = default_sort_order;
33
8fa66bdc
ACM
34static int input;
35static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
36
97b07b69 37static int dump_trace = 0;
3502973d
IM
38#define dprintf(x...) do { if (dump_trace) printf(x); } while (0)
39
16f762a2 40static int verbose;
b78c07d4 41static int full_paths;
97b07b69 42
8fa66bdc
ACM
43static unsigned long page_size;
44static unsigned long mmap_window = 32;
45
8fa66bdc
ACM
46struct ip_event {
47 struct perf_event_header header;
48 __u64 ip;
49 __u32 pid, tid;
50};
75051724 51
8fa66bdc
ACM
52struct mmap_event {
53 struct perf_event_header header;
54 __u32 pid, tid;
55 __u64 start;
56 __u64 len;
57 __u64 pgoff;
58 char filename[PATH_MAX];
59};
75051724 60
8fa66bdc
ACM
61struct comm_event {
62 struct perf_event_header header;
75051724 63 __u32 pid, tid;
8fa66bdc
ACM
64 char comm[16];
65};
66
62fc4453
PZ
67struct fork_event {
68 struct perf_event_header header;
69 __u32 pid, ppid;
70};
71
8fa66bdc
ACM
72typedef union event_union {
73 struct perf_event_header header;
74 struct ip_event ip;
75 struct mmap_event mmap;
76 struct comm_event comm;
62fc4453 77 struct fork_event fork;
8fa66bdc
ACM
78} event_t;
79
8fa66bdc
ACM
80static LIST_HEAD(dsos);
81static struct dso *kernel_dso;
fc54db51 82static struct dso *vdso;
8fa66bdc
ACM
83
84static void dsos__add(struct dso *dso)
85{
86 list_add_tail(&dso->node, &dsos);
87}
88
89static struct dso *dsos__find(const char *name)
90{
91 struct dso *pos;
92
93 list_for_each_entry(pos, &dsos, node)
94 if (strcmp(pos->name, name) == 0)
95 return pos;
96 return NULL;
97}
98
99static struct dso *dsos__findnew(const char *name)
100{
101 struct dso *dso = dsos__find(name);
b7a16eac 102 int nr;
8fa66bdc 103
4593bba8
IM
104 if (dso)
105 return dso;
106
107 dso = dso__new(name, 0);
108 if (!dso)
109 goto out_delete_dso;
8fa66bdc 110
bd74137e 111 nr = dso__load(dso, NULL, verbose);
4593bba8 112 if (nr < 0) {
bd74137e
IM
113 if (verbose)
114 fprintf(stderr, "Failed to open: %s\n", name);
4593bba8 115 goto out_delete_dso;
8fa66bdc 116 }
4593bba8
IM
117 if (!nr && verbose) {
118 fprintf(stderr,
119 "No symbols found in: %s, maybe install a debug package?\n",
120 name);
121 }
122
123 dsos__add(dso);
8fa66bdc
ACM
124
125 return dso;
126
127out_delete_dso:
128 dso__delete(dso);
129 return NULL;
130}
131
16f762a2 132static void dsos__fprintf(FILE *fp)
8fa66bdc
ACM
133{
134 struct dso *pos;
135
136 list_for_each_entry(pos, &dsos, node)
137 dso__fprintf(pos, fp);
138}
139
fc54db51
PZ
140static struct symbol *vdso__find_symbol(struct dso *dso, uint64_t ip)
141{
142 return dso__find_symbol(kernel_dso, ip);
143}
144
450aaa2b
PZ
145static int load_kernel(void)
146{
a827c875 147 int err;
450aaa2b 148
0085c954 149 kernel_dso = dso__new("[kernel]", 0);
450aaa2b 150 if (!kernel_dso)
a2928c42 151 return -1;
450aaa2b 152
bd74137e 153 err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose);
a2928c42
ACM
154 if (err) {
155 dso__delete(kernel_dso);
156 kernel_dso = NULL;
157 } else
158 dsos__add(kernel_dso);
450aaa2b 159
fc54db51
PZ
160 vdso = dso__new("[vdso]", 0);
161 if (!vdso)
162 return -1;
163
164 vdso->find_symbol = vdso__find_symbol;
165
166 dsos__add(vdso);
167
a2928c42 168 return err;
450aaa2b
PZ
169}
170
d80d338d
IM
171static char __cwd[PATH_MAX];
172static char *cwd = __cwd;
173static int cwdlen;
174
175static int strcommon(const char *pathname)
b78c07d4
ACM
176{
177 int n = 0;
178
179 while (pathname[n] == cwd[n] && n < cwdlen)
180 ++n;
181
182 return n;
183}
184
8fa66bdc
ACM
185struct map {
186 struct list_head node;
187 uint64_t start;
188 uint64_t end;
189 uint64_t pgoff;
fc54db51 190 uint64_t (*map_ip)(struct map *, uint64_t);
8fa66bdc
ACM
191 struct dso *dso;
192};
193
fc54db51
PZ
194static uint64_t map__map_ip(struct map *map, uint64_t ip)
195{
196 return ip - map->start + map->pgoff;
197}
198
199static uint64_t vdso__map_ip(struct map *map, uint64_t ip)
200{
201 return ip;
202}
203
d80d338d 204static struct map *map__new(struct mmap_event *event)
8fa66bdc
ACM
205{
206 struct map *self = malloc(sizeof(*self));
207
208 if (self != NULL) {
b78c07d4
ACM
209 const char *filename = event->filename;
210 char newfilename[PATH_MAX];
211
212 if (cwd) {
d80d338d
IM
213 int n = strcommon(filename);
214
b78c07d4
ACM
215 if (n == cwdlen) {
216 snprintf(newfilename, sizeof(newfilename),
217 ".%s", filename + n);
218 filename = newfilename;
219 }
220 }
221
8fa66bdc
ACM
222 self->start = event->start;
223 self->end = event->start + event->len;
224 self->pgoff = event->pgoff;
225
b78c07d4 226 self->dso = dsos__findnew(filename);
8fa66bdc
ACM
227 if (self->dso == NULL)
228 goto out_delete;
fc54db51
PZ
229
230 if (self->dso == vdso)
231 self->map_ip = vdso__map_ip;
232 else
233 self->map_ip = map__map_ip;
8fa66bdc
ACM
234 }
235 return self;
236out_delete:
237 free(self);
238 return NULL;
239}
240
62fc4453
PZ
241static struct map *map__clone(struct map *self)
242{
243 struct map *map = malloc(sizeof(*self));
244
245 if (!map)
246 return NULL;
247
248 memcpy(map, self, sizeof(*self));
249
250 return map;
251}
252
253static int map__overlap(struct map *l, struct map *r)
254{
255 if (l->start > r->start) {
256 struct map *t = l;
257 l = r;
258 r = t;
259 }
260
261 if (l->end > r->start)
262 return 1;
263
264 return 0;
265}
3a4b8cc7 266
9ac99545
ACM
267static size_t map__fprintf(struct map *self, FILE *fp)
268{
ee7b31fe 269 return fprintf(fp, " %"PRIx64"-%"PRIx64" %"PRIx64" %s\n",
9ac99545
ACM
270 self->start, self->end, self->pgoff, self->dso->name);
271}
272
273
8fa66bdc 274struct thread {
ce7e4365 275 struct rb_node rb_node;
8fa66bdc 276 struct list_head maps;
8fa66bdc
ACM
277 pid_t pid;
278 char *comm;
279};
280
281static struct thread *thread__new(pid_t pid)
282{
283 struct thread *self = malloc(sizeof(*self));
284
285 if (self != NULL) {
286 self->pid = pid;
8229289b 287 self->comm = malloc(32);
0a520c63 288 if (self->comm)
8229289b 289 snprintf(self->comm, 32, ":%d", self->pid);
8fa66bdc 290 INIT_LIST_HEAD(&self->maps);
8fa66bdc
ACM
291 }
292
293 return self;
294}
295
8fa66bdc
ACM
296static int thread__set_comm(struct thread *self, const char *comm)
297{
8229289b
PZ
298 if (self->comm)
299 free(self->comm);
8fa66bdc
ACM
300 self->comm = strdup(comm);
301 return self->comm ? 0 : -ENOMEM;
302}
303
9ac99545
ACM
304static size_t thread__fprintf(struct thread *self, FILE *fp)
305{
306 struct map *pos;
307 size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm);
308
309 list_for_each_entry(pos, &self->maps, node)
310 ret += map__fprintf(pos, fp);
311
312 return ret;
313}
314
315
16f762a2 316static struct rb_root threads;
eed4dcd4 317static struct thread *last_match;
8fa66bdc 318
ce7e4365 319static struct thread *threads__findnew(pid_t pid)
8fa66bdc 320{
ce7e4365
ACM
321 struct rb_node **p = &threads.rb_node;
322 struct rb_node *parent = NULL;
323 struct thread *th;
8fa66bdc 324
eed4dcd4
IM
325 /*
326 * Font-end cache - PID lookups come in blocks,
327 * so most of the time we dont have to look up
328 * the full rbtree:
329 */
330 if (last_match && last_match->pid == pid)
331 return last_match;
332
ce7e4365
ACM
333 while (*p != NULL) {
334 parent = *p;
335 th = rb_entry(parent, struct thread, rb_node);
8fa66bdc 336
eed4dcd4
IM
337 if (th->pid == pid) {
338 last_match = th;
ce7e4365 339 return th;
eed4dcd4 340 }
8fa66bdc 341
ce7e4365
ACM
342 if (pid < th->pid)
343 p = &(*p)->rb_left;
344 else
345 p = &(*p)->rb_right;
8fa66bdc
ACM
346 }
347
ce7e4365
ACM
348 th = thread__new(pid);
349 if (th != NULL) {
350 rb_link_node(&th->rb_node, parent, p);
351 rb_insert_color(&th->rb_node, &threads);
eed4dcd4 352 last_match = th;
ce7e4365 353 }
eed4dcd4 354
ce7e4365 355 return th;
8fa66bdc
ACM
356}
357
358static void thread__insert_map(struct thread *self, struct map *map)
359{
62fc4453
PZ
360 struct map *pos, *tmp;
361
362 list_for_each_entry_safe(pos, tmp, &self->maps, node) {
363 if (map__overlap(pos, map)) {
364 list_del_init(&pos->node);
365 /* XXX leaks dsos */
366 free(pos);
367 }
368 }
369
8fa66bdc
ACM
370 list_add_tail(&map->node, &self->maps);
371}
372
62fc4453
PZ
373static int thread__fork(struct thread *self, struct thread *parent)
374{
375 struct map *map;
376
377 if (self->comm)
378 free(self->comm);
379 self->comm = strdup(parent->comm);
380 if (!self->comm)
381 return -ENOMEM;
382
383 list_for_each_entry(map, &parent->maps, node) {
384 struct map *new = map__clone(map);
385 if (!new)
386 return -ENOMEM;
387 thread__insert_map(self, new);
388 }
389
390 return 0;
391}
392
8fa66bdc
ACM
393static struct map *thread__find_map(struct thread *self, uint64_t ip)
394{
16f762a2
IM
395 struct map *pos;
396
8fa66bdc
ACM
397 if (self == NULL)
398 return NULL;
399
8fa66bdc
ACM
400 list_for_each_entry(pos, &self->maps, node)
401 if (ip >= pos->start && ip <= pos->end)
402 return pos;
403
404 return NULL;
405}
406
9ac99545
ACM
407static size_t threads__fprintf(FILE *fp)
408{
409 size_t ret = 0;
410 struct rb_node *nd;
411
412 for (nd = rb_first(&threads); nd; nd = rb_next(nd)) {
413 struct thread *pos = rb_entry(nd, struct thread, rb_node);
414
415 ret += thread__fprintf(pos, fp);
416 }
417
418 return ret;
419}
420
e7fb08b1
PZ
421/*
422 * histogram, sorted on item, collects counts
423 */
424
425static struct rb_root hist;
426
427struct hist_entry {
428 struct rb_node rb_node;
429
430 struct thread *thread;
431 struct map *map;
432 struct dso *dso;
433 struct symbol *sym;
434 uint64_t ip;
435 char level;
436
437 uint32_t count;
438};
439
1aa16738
PZ
440/*
441 * configurable sorting bits
442 */
443
444struct sort_entry {
445 struct list_head list;
446
ca8cdeef
PZ
447 char *header;
448
1aa16738 449 int64_t (*cmp)(struct hist_entry *, struct hist_entry *);
8229289b 450 int64_t (*collapse)(struct hist_entry *, struct hist_entry *);
1aa16738
PZ
451 size_t (*print)(FILE *fp, struct hist_entry *);
452};
453
8229289b
PZ
454/* --sort pid */
455
e7fb08b1 456static int64_t
1aa16738 457sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
e7fb08b1 458{
1aa16738
PZ
459 return right->thread->pid - left->thread->pid;
460}
461
462static size_t
463sort__thread_print(FILE *fp, struct hist_entry *self)
464{
71dd8945 465 return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid);
1aa16738 466}
e7fb08b1 467
1aa16738 468static struct sort_entry sort_thread = {
71dd8945 469 .header = " Command: Pid",
1aa16738
PZ
470 .cmp = sort__thread_cmp,
471 .print = sort__thread_print,
472};
473
8229289b
PZ
474/* --sort comm */
475
992444b1
PZ
476static int64_t
477sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
8229289b
PZ
478{
479 return right->thread->pid - left->thread->pid;
480}
481
482static int64_t
483sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
992444b1
PZ
484{
485 char *comm_l = left->thread->comm;
486 char *comm_r = right->thread->comm;
487
488 if (!comm_l || !comm_r) {
489 if (!comm_l && !comm_r)
490 return 0;
491 else if (!comm_l)
492 return -1;
493 else
494 return 1;
495 }
496
497 return strcmp(comm_l, comm_r);
498}
499
500static size_t
501sort__comm_print(FILE *fp, struct hist_entry *self)
502{
71dd8945 503 return fprintf(fp, "%16s", self->thread->comm);
992444b1
PZ
504}
505
506static struct sort_entry sort_comm = {
8edd4286 507 .header = " Command",
8229289b
PZ
508 .cmp = sort__comm_cmp,
509 .collapse = sort__comm_collapse,
510 .print = sort__comm_print,
992444b1
PZ
511};
512
8229289b
PZ
513/* --sort dso */
514
55e5ec41
PZ
515static int64_t
516sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
517{
518 struct dso *dso_l = left->dso;
519 struct dso *dso_r = right->dso;
520
521 if (!dso_l || !dso_r) {
522 if (!dso_l && !dso_r)
523 return 0;
524 else if (!dso_l)
525 return -1;
526 else
527 return 1;
528 }
529
530 return strcmp(dso_l->name, dso_r->name);
531}
532
533static size_t
534sort__dso_print(FILE *fp, struct hist_entry *self)
535{
0a520c63 536 if (self->dso)
71dd8945 537 return fprintf(fp, "%-25s", self->dso->name);
0a520c63 538
71dd8945 539 return fprintf(fp, "%016llx ", (__u64)self->ip);
55e5ec41
PZ
540}
541
542static struct sort_entry sort_dso = {
71dd8945 543 .header = "Shared Object ",
55e5ec41
PZ
544 .cmp = sort__dso_cmp,
545 .print = sort__dso_print,
546};
547
8229289b
PZ
548/* --sort symbol */
549
1aa16738
PZ
550static int64_t
551sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
552{
553 uint64_t ip_l, ip_r;
e7fb08b1
PZ
554
555 if (left->sym == right->sym)
556 return 0;
557
558 ip_l = left->sym ? left->sym->start : left->ip;
559 ip_r = right->sym ? right->sym->start : right->ip;
560
561 return (int64_t)(ip_r - ip_l);
562}
563
1aa16738
PZ
564static size_t
565sort__sym_print(FILE *fp, struct hist_entry *self)
566{
567 size_t ret = 0;
568
1aa16738 569 if (verbose)
71dd8945 570 ret += fprintf(fp, "%#018llx ", (__u64)self->ip);
0a520c63 571
8edd4286
IM
572 if (self->sym) {
573 ret += fprintf(fp, "[%c] %s",
574 self->dso == kernel_dso ? 'k' : '.', self->sym->name);
575 } else {
71dd8945 576 ret += fprintf(fp, "%#016llx", (__u64)self->ip);
8edd4286 577 }
1aa16738
PZ
578
579 return ret;
580}
581
582static struct sort_entry sort_sym = {
71dd8945 583 .header = "Symbol",
ca8cdeef
PZ
584 .cmp = sort__sym_cmp,
585 .print = sort__sym_print,
1aa16738
PZ
586};
587
8229289b
PZ
588static int sort__need_collapse = 0;
589
37f440cb 590struct sort_dimension {
8edd4286
IM
591 char *name;
592 struct sort_entry *entry;
593 int taken;
37f440cb
PZ
594};
595
596static struct sort_dimension sort_dimensions[] = {
597 { .name = "pid", .entry = &sort_thread, },
992444b1 598 { .name = "comm", .entry = &sort_comm, },
55e5ec41 599 { .name = "dso", .entry = &sort_dso, },
37f440cb
PZ
600 { .name = "symbol", .entry = &sort_sym, },
601};
602
1aa16738
PZ
603static LIST_HEAD(hist_entry__sort_list);
604
37f440cb
PZ
605static int sort_dimension__add(char *tok)
606{
607 int i;
608
609 for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {
610 struct sort_dimension *sd = &sort_dimensions[i];
611
612 if (sd->taken)
613 continue;
614
5352f35d 615 if (strncasecmp(tok, sd->name, strlen(tok)))
37f440cb
PZ
616 continue;
617
8229289b
PZ
618 if (sd->entry->collapse)
619 sort__need_collapse = 1;
620
37f440cb
PZ
621 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
622 sd->taken = 1;
5352f35d 623
37f440cb
PZ
624 return 0;
625 }
626
627 return -ESRCH;
628}
629
1aa16738
PZ
630static int64_t
631hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
632{
633 struct sort_entry *se;
634 int64_t cmp = 0;
635
636 list_for_each_entry(se, &hist_entry__sort_list, list) {
637 cmp = se->cmp(left, right);
638 if (cmp)
639 break;
640 }
641
642 return cmp;
643}
644
8229289b
PZ
645static int64_t
646hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
647{
648 struct sort_entry *se;
649 int64_t cmp = 0;
650
651 list_for_each_entry(se, &hist_entry__sort_list, list) {
652 int64_t (*f)(struct hist_entry *, struct hist_entry *);
653
654 f = se->collapse ?: se->cmp;
655
656 cmp = f(left, right);
657 if (cmp)
658 break;
659 }
660
661 return cmp;
662}
663
1aa16738
PZ
664static size_t
665hist_entry__fprintf(FILE *fp, struct hist_entry *self, uint64_t total_samples)
666{
667 struct sort_entry *se;
668 size_t ret;
669
670 if (total_samples) {
8fc0321f
IM
671 double percent = self->count * 100.0 / total_samples;
672 char *color = PERF_COLOR_NORMAL;
673
674 /*
675 * We color high-overhead entries in red, low-overhead
676 * entries in green - and keep the middle ground normal:
677 */
678 if (percent >= 5.0)
679 color = PERF_COLOR_RED;
680 if (percent < 0.5)
681 color = PERF_COLOR_GREEN;
682
683 ret = color_fprintf(fp, color, " %6.2f%%",
1aa16738
PZ
684 (self->count * 100.0) / total_samples);
685 } else
686 ret = fprintf(fp, "%12d ", self->count);
687
71dd8945
PZ
688 list_for_each_entry(se, &hist_entry__sort_list, list) {
689 fprintf(fp, " ");
1aa16738 690 ret += se->print(fp, self);
71dd8945 691 }
1aa16738
PZ
692
693 ret += fprintf(fp, "\n");
694
695 return ret;
696}
697
698/*
699 * collect histogram counts
700 */
701
e7fb08b1
PZ
702static int
703hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
704 struct symbol *sym, uint64_t ip, char level)
8fa66bdc 705{
e7fb08b1
PZ
706 struct rb_node **p = &hist.rb_node;
707 struct rb_node *parent = NULL;
708 struct hist_entry *he;
709 struct hist_entry entry = {
710 .thread = thread,
711 .map = map,
712 .dso = dso,
713 .sym = sym,
714 .ip = ip,
715 .level = level,
716 .count = 1,
717 };
718 int cmp;
719
720 while (*p != NULL) {
721 parent = *p;
722 he = rb_entry(parent, struct hist_entry, rb_node);
723
724 cmp = hist_entry__cmp(&entry, he);
725
726 if (!cmp) {
727 he->count++;
728 return 0;
729 }
730
731 if (cmp < 0)
732 p = &(*p)->rb_left;
733 else
734 p = &(*p)->rb_right;
ce7e4365 735 }
e7fb08b1
PZ
736
737 he = malloc(sizeof(*he));
738 if (!he)
739 return -ENOMEM;
740 *he = entry;
741 rb_link_node(&he->rb_node, parent, p);
742 rb_insert_color(&he->rb_node, &hist);
743
744 return 0;
8fa66bdc
ACM
745}
746
8229289b
PZ
747static void hist_entry__free(struct hist_entry *he)
748{
749 free(he);
750}
751
752/*
753 * collapse the histogram
754 */
755
756static struct rb_root collapse_hists;
757
758static void collapse__insert_entry(struct hist_entry *he)
759{
760 struct rb_node **p = &collapse_hists.rb_node;
761 struct rb_node *parent = NULL;
762 struct hist_entry *iter;
763 int64_t cmp;
764
765 while (*p != NULL) {
766 parent = *p;
767 iter = rb_entry(parent, struct hist_entry, rb_node);
768
769 cmp = hist_entry__collapse(iter, he);
770
771 if (!cmp) {
772 iter->count += he->count;
773 hist_entry__free(he);
774 return;
775 }
776
777 if (cmp < 0)
778 p = &(*p)->rb_left;
779 else
780 p = &(*p)->rb_right;
781 }
782
783 rb_link_node(&he->rb_node, parent, p);
784 rb_insert_color(&he->rb_node, &collapse_hists);
785}
786
787static void collapse__resort(void)
788{
789 struct rb_node *next;
790 struct hist_entry *n;
791
792 if (!sort__need_collapse)
793 return;
794
795 next = rb_first(&hist);
796 while (next) {
797 n = rb_entry(next, struct hist_entry, rb_node);
798 next = rb_next(&n->rb_node);
799
800 rb_erase(&n->rb_node, &hist);
801 collapse__insert_entry(n);
802 }
803}
804
e7fb08b1
PZ
805/*
806 * reverse the map, sort on count.
807 */
808
809static struct rb_root output_hists;
810
811static void output__insert_entry(struct hist_entry *he)
3a4b8cc7 812{
e7fb08b1 813 struct rb_node **p = &output_hists.rb_node;
3a4b8cc7 814 struct rb_node *parent = NULL;
e7fb08b1 815 struct hist_entry *iter;
3a4b8cc7
ACM
816
817 while (*p != NULL) {
818 parent = *p;
e7fb08b1 819 iter = rb_entry(parent, struct hist_entry, rb_node);
3a4b8cc7 820
e7fb08b1 821 if (he->count > iter->count)
3a4b8cc7
ACM
822 p = &(*p)->rb_left;
823 else
824 p = &(*p)->rb_right;
825 }
826
e7fb08b1
PZ
827 rb_link_node(&he->rb_node, parent, p);
828 rb_insert_color(&he->rb_node, &output_hists);
3a4b8cc7
ACM
829}
830
e7fb08b1 831static void output__resort(void)
3a4b8cc7 832{
8229289b 833 struct rb_node *next;
e7fb08b1 834 struct hist_entry *n;
a4c43bea 835 struct rb_root *tree = &hist;
3a4b8cc7 836
8229289b 837 if (sort__need_collapse)
a4c43bea
ACM
838 tree = &collapse_hists;
839
840 next = rb_first(tree);
8229289b 841
e7fb08b1
PZ
842 while (next) {
843 n = rb_entry(next, struct hist_entry, rb_node);
844 next = rb_next(&n->rb_node);
3a4b8cc7 845
a4c43bea 846 rb_erase(&n->rb_node, tree);
e7fb08b1 847 output__insert_entry(n);
3a4b8cc7
ACM
848 }
849}
850
e7fb08b1 851static size_t output__fprintf(FILE *fp, uint64_t total_samples)
3a4b8cc7 852{
e7fb08b1 853 struct hist_entry *pos;
2d65537e 854 struct sort_entry *se;
3a4b8cc7
ACM
855 struct rb_node *nd;
856 size_t ret = 0;
857
71dd8945 858 fprintf(fp, "\n");
05ca061e 859 fprintf(fp, "#\n");
2debbc83 860 fprintf(fp, "# (%Ld samples)\n", (__u64)total_samples);
ca8cdeef
PZ
861 fprintf(fp, "#\n");
862
863 fprintf(fp, "# Overhead");
864 list_for_each_entry(se, &hist_entry__sort_list, list)
71dd8945 865 fprintf(fp, " %s", se->header);
ca8cdeef
PZ
866 fprintf(fp, "\n");
867
868 fprintf(fp, "# ........");
2d65537e 869 list_for_each_entry(se, &hist_entry__sort_list, list) {
ca8cdeef
PZ
870 int i;
871
4593bba8 872 fprintf(fp, " ");
71dd8945 873 for (i = 0; i < strlen(se->header); i++)
ca8cdeef 874 fprintf(fp, ".");
2d65537e 875 }
ca8cdeef
PZ
876 fprintf(fp, "\n");
877
878 fprintf(fp, "#\n");
2d65537e 879
e7fb08b1
PZ
880 for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
881 pos = rb_entry(nd, struct hist_entry, rb_node);
882 ret += hist_entry__fprintf(fp, pos, total_samples);
3a4b8cc7
ACM
883 }
884
bd74137e
IM
885 if (!strcmp(sort_order, default_sort_order)) {
886 fprintf(fp, "#\n");
71dd8945 887 fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n");
bd74137e
IM
888 fprintf(fp, "#\n");
889 }
71dd8945 890 fprintf(fp, "\n");
bd74137e 891
3a4b8cc7
ACM
892 return ret;
893}
894
436224a6
PZ
895static void register_idle_thread(void)
896{
897 struct thread *thread = threads__findnew(0);
898
899 if (thread == NULL ||
900 thread__set_comm(thread, "[idle]")) {
901 fprintf(stderr, "problem inserting idle task.\n");
902 exit(-1);
903 }
904}
905
62fc4453
PZ
906static unsigned long total = 0,
907 total_mmap = 0,
908 total_comm = 0,
909 total_fork = 0,
910 total_unknown = 0;
e7fb08b1 911
d80d338d 912static int
75051724
IM
913process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
914{
915 char level;
916 int show = 0;
917 struct dso *dso = NULL;
918 struct thread *thread = threads__findnew(event->ip.pid);
919 uint64_t ip = event->ip.ip;
920 struct map *map = NULL;
921
922 dprintf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n",
923 (void *)(offset + head),
924 (void *)(long)(event->header.size),
925 event->header.misc,
926 event->ip.pid,
927 (void *)(long)ip);
928
929 dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid);
930
931 if (thread == NULL) {
932 fprintf(stderr, "problem processing %d event, skipping it.\n",
933 event->header.type);
934 return -1;
935 }
e7fb08b1 936
75051724
IM
937 if (event->header.misc & PERF_EVENT_MISC_KERNEL) {
938 show = SHOW_KERNEL;
939 level = 'k';
e7fb08b1 940
75051724 941 dso = kernel_dso;
ed966aac 942
75051724 943 dprintf(" ...... dso: %s\n", dso->name);
16f762a2 944
75051724 945 } else if (event->header.misc & PERF_EVENT_MISC_USER) {
16f762a2 946
75051724
IM
947 show = SHOW_USER;
948 level = '.';
e7fb08b1 949
75051724
IM
950 map = thread__find_map(thread, ip);
951 if (map != NULL) {
fc54db51 952 ip = map->map_ip(map, ip);
75051724 953 dso = map->dso;
8fa66bdc 954 } else {
75051724
IM
955 /*
956 * If this is outside of all known maps,
957 * and is a negative address, try to look it
958 * up in the kernel dso, as it might be a
959 * vsyscall (which executes in user-mode):
960 */
961 if ((long long)ip < 0)
962 dso = kernel_dso;
8fa66bdc 963 }
75051724
IM
964 dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
965
966 } else {
967 show = SHOW_HV;
968 level = 'H';
969 dprintf(" ...... dso: [hypervisor]\n");
970 }
8fa66bdc 971
75051724 972 if (show & show_mask) {
fc54db51
PZ
973 struct symbol *sym = NULL;
974
975 if (dso)
976 sym = dso->find_symbol(dso, ip);
8fa66bdc 977
75051724
IM
978 if (hist_entry__add(thread, map, dso, sym, ip, level)) {
979 fprintf(stderr,
55717314 980 "problem incrementing symbol count, skipping event\n");
d80d338d 981 return -1;
ce7e4365 982 }
8fa66bdc 983 }
75051724 984 total++;
8fa66bdc 985
75051724
IM
986 return 0;
987}
3502973d 988
75051724
IM
989static int
990process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
991{
992 struct thread *thread = threads__findnew(event->mmap.pid);
993 struct map *map = map__new(&event->mmap);
994
62fc4453 995 dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n",
75051724
IM
996 (void *)(offset + head),
997 (void *)(long)(event->header.size),
62fc4453 998 event->mmap.pid,
75051724
IM
999 (void *)(long)event->mmap.start,
1000 (void *)(long)event->mmap.len,
1001 (void *)(long)event->mmap.pgoff,
1002 event->mmap.filename);
1003
1004 if (thread == NULL || map == NULL) {
1005 dprintf("problem processing PERF_EVENT_MMAP, skipping event.\n");
df97992c 1006 return 0;
75051724
IM
1007 }
1008
1009 thread__insert_map(thread, map);
1010 total_mmap++;
1011
1012 return 0;
1013}
1014
1015static int
1016process_comm_event(event_t *event, unsigned long offset, unsigned long head)
1017{
1018 struct thread *thread = threads__findnew(event->comm.pid);
1019
1020 dprintf("%p [%p]: PERF_EVENT_COMM: %s:%d\n",
1021 (void *)(offset + head),
1022 (void *)(long)(event->header.size),
1023 event->comm.comm, event->comm.pid);
1024
1025 if (thread == NULL ||
1026 thread__set_comm(thread, event->comm.comm)) {
1027 dprintf("problem processing PERF_EVENT_COMM, skipping event.\n");
1028 return -1;
8fa66bdc 1029 }
75051724
IM
1030 total_comm++;
1031
1032 return 0;
1033}
1034
62fc4453
PZ
1035static int
1036process_fork_event(event_t *event, unsigned long offset, unsigned long head)
1037{
1038 struct thread *thread = threads__findnew(event->fork.pid);
1039 struct thread *parent = threads__findnew(event->fork.ppid);
1040
1041 dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n",
1042 (void *)(offset + head),
1043 (void *)(long)(event->header.size),
1044 event->fork.pid, event->fork.ppid);
1045
1046 if (!thread || !parent || thread__fork(thread, parent)) {
1047 dprintf("problem processing PERF_EVENT_FORK, skipping event.\n");
1048 return -1;
1049 }
1050 total_fork++;
1051
1052 return 0;
1053}
1054
75051724
IM
1055static int
1056process_event(event_t *event, unsigned long offset, unsigned long head)
1057{
1058 if (event->header.misc & PERF_EVENT_MISC_OVERFLOW)
1059 return process_overflow_event(event, offset, head);
1060
1061 switch (event->header.type) {
1062 case PERF_EVENT_MMAP:
1063 return process_mmap_event(event, offset, head);
1064
1065 case PERF_EVENT_COMM:
1066 return process_comm_event(event, offset, head);
1067
62fc4453
PZ
1068 case PERF_EVENT_FORK:
1069 return process_fork_event(event, offset, head);
1070
d11444df
IM
1071 /*
1072 * We dont process them right now but they are fine:
1073 */
62fc4453 1074
d11444df
IM
1075 case PERF_EVENT_PERIOD:
1076 case PERF_EVENT_THROTTLE:
1077 case PERF_EVENT_UNTHROTTLE:
1078 return 0;
1079
d80d338d
IM
1080 default:
1081 return -1;
1082 }
1083
1084 return 0;
1085}
1086
1087static int __cmd_report(void)
1088{
75051724 1089 int ret, rc = EXIT_FAILURE;
d80d338d
IM
1090 unsigned long offset = 0;
1091 unsigned long head = 0;
1092 struct stat stat;
d80d338d 1093 event_t *event;
d80d338d 1094 uint32_t size;
75051724 1095 char *buf;
d80d338d
IM
1096
1097 register_idle_thread();
1098
1099 input = open(input_name, O_RDONLY);
1100 if (input < 0) {
1101 perror("failed to open file");
1102 exit(-1);
1103 }
1104
1105 ret = fstat(input, &stat);
1106 if (ret < 0) {
1107 perror("failed to stat file");
1108 exit(-1);
1109 }
1110
1111 if (!stat.st_size) {
1112 fprintf(stderr, "zero-sized file, nothing to do!\n");
1113 exit(0);
1114 }
1115
1116 if (load_kernel() < 0) {
1117 perror("failed to load kernel symbols");
1118 return EXIT_FAILURE;
1119 }
1120
1121 if (!full_paths) {
1122 if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
1123 perror("failed to get the current directory");
1124 return EXIT_FAILURE;
1125 }
1126 cwdlen = strlen(cwd);
1127 } else {
1128 cwd = NULL;
1129 cwdlen = 0;
1130 }
1131remap:
1132 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
1133 MAP_SHARED, input, offset);
1134 if (buf == MAP_FAILED) {
1135 perror("failed to mmap file");
1136 exit(-1);
1137 }
1138
1139more:
1140 event = (event_t *)(buf + head);
1141
1142 size = event->header.size;
1143 if (!size)
1144 size = 8;
1145
1146 if (head + event->header.size >= page_size * mmap_window) {
1147 unsigned long shift = page_size * (head / page_size);
1148 int ret;
1149
1150 ret = munmap(buf, page_size * mmap_window);
1151 assert(ret == 0);
1152
1153 offset += shift;
1154 head -= shift;
1155 goto remap;
1156 }
1157
1158 size = event->header.size;
1159
1160 if (!size || process_event(event, offset, head) < 0) {
1161
3502973d
IM
1162 dprintf("%p [%p]: skipping unknown header type: %d\n",
1163 (void *)(offset + head),
1164 (void *)(long)(event->header.size),
1165 event->header.type);
b7a16eac 1166
3e706114 1167 total_unknown++;
6142f9ec
PZ
1168
1169 /*
1170 * assume we lost track of the stream, check alignment, and
1171 * increment a single u64 in the hope to catch on again 'soon'.
1172 */
1173
1174 if (unlikely(head & 7))
1175 head &= ~7ULL;
1176
1177 size = 8;
97b07b69 1178 }
8fa66bdc 1179
6142f9ec 1180 head += size;
f49515b1 1181
8fa66bdc
ACM
1182 if (offset + head < stat.st_size)
1183 goto more;
1184
1185 rc = EXIT_SUCCESS;
8fa66bdc 1186 close(input);
97b07b69 1187
3502973d
IM
1188 dprintf(" IP events: %10ld\n", total);
1189 dprintf(" mmap events: %10ld\n", total_mmap);
1190 dprintf(" comm events: %10ld\n", total_comm);
62fc4453 1191 dprintf(" fork events: %10ld\n", total_fork);
3502973d 1192 dprintf(" unknown events: %10ld\n", total_unknown);
97b07b69 1193
3502973d 1194 if (dump_trace)
97b07b69 1195 return 0;
97b07b69 1196
9ac99545
ACM
1197 if (verbose >= 3)
1198 threads__fprintf(stdout);
1199
e7fb08b1 1200 if (verbose >= 2)
16f762a2 1201 dsos__fprintf(stdout);
16f762a2 1202
8229289b 1203 collapse__resort();
e7fb08b1
PZ
1204 output__resort();
1205 output__fprintf(stdout, total);
8fa66bdc 1206
8fa66bdc
ACM
1207 return rc;
1208}
1209
53cb8bc2
IM
1210static const char * const report_usage[] = {
1211 "perf report [<options>] <command>",
1212 NULL
1213};
1214
1215static const struct option options[] = {
1216 OPT_STRING('i', "input", &input_name, "file",
1217 "input file name"),
815e777f
ACM
1218 OPT_BOOLEAN('v', "verbose", &verbose,
1219 "be more verbose (show symbol address, etc)"),
97b07b69
IM
1220 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1221 "dump raw trace in ASCII"),
450aaa2b 1222 OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"),
63299f05
IM
1223 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
1224 "sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"),
b78c07d4
ACM
1225 OPT_BOOLEAN('P', "full-paths", &full_paths,
1226 "Don't shorten the pathnames taking into account the cwd"),
53cb8bc2
IM
1227 OPT_END()
1228};
1229
5352f35d
IM
1230static void setup_sorting(void)
1231{
1232 char *tmp, *tok, *str = strdup(sort_order);
1233
1234 for (tok = strtok_r(str, ", ", &tmp);
1235 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1236 if (sort_dimension__add(tok) < 0) {
1237 error("Unknown --sort key: `%s'", tok);
1238 usage_with_options(report_usage, options);
1239 }
1240 }
1241
1242 free(str);
1243}
1244
53cb8bc2
IM
1245int cmd_report(int argc, const char **argv, const char *prefix)
1246{
a2928c42 1247 symbol__init();
53cb8bc2
IM
1248
1249 page_size = getpagesize();
1250
edc52dea 1251 argc = parse_options(argc, argv, options, report_usage, 0);
53cb8bc2 1252
1aa16738
PZ
1253 setup_sorting();
1254
edc52dea
IM
1255 /*
1256 * Any (unrecognized) arguments left?
1257 */
1258 if (argc)
1259 usage_with_options(report_usage, options);
1260
a930d2c0
IM
1261 setup_pager();
1262
53cb8bc2
IM
1263 return __cmd_report();
1264}
This page took 0.099963 seconds and 5 git commands to generate.