perf tools: Create util/sort.and use it
[deliverable/linux.git] / tools / perf / builtin-report.c
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 */
8 #include "builtin.h"
9
10 #include "util/util.h"
11
12 #include "util/color.h"
13 #include <linux/list.h>
14 #include "util/cache.h"
15 #include <linux/rbtree.h>
16 #include "util/symbol.h"
17 #include "util/string.h"
18 #include "util/callchain.h"
19 #include "util/strlist.h"
20 #include "util/values.h"
21
22 #include "perf.h"
23 #include "util/debug.h"
24 #include "util/header.h"
25
26 #include "util/parse-options.h"
27 #include "util/parse-events.h"
28
29 #include "util/thread.h"
30 #include "util/sort.h"
31
32 static char const *input_name = "perf.data";
33
34 static char *dso_list_str, *comm_list_str, *sym_list_str,
35 *col_width_list_str;
36 static struct strlist *dso_list, *comm_list, *sym_list;
37
38 static int force;
39 static int input;
40 static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;
41
42 static int full_paths;
43 static int show_nr_samples;
44
45 static int show_threads;
46 static struct perf_read_values show_threads_values;
47
48 static char default_pretty_printing_style[] = "normal";
49 static char *pretty_printing_style = default_pretty_printing_style;
50
51 static unsigned long page_size;
52 static unsigned long mmap_window = 32;
53
54 static int exclude_other = 1;
55
56 static char callchain_default_opt[] = "fractal,0.5";
57
58 static int callchain;
59
60 static char __cwd[PATH_MAX];
61 static char *cwd = __cwd;
62 static int cwdlen;
63
64 static struct rb_root threads;
65 static struct thread *last_match;
66
67 static struct perf_header *header;
68
69 static
70 struct callchain_param callchain_param = {
71 .mode = CHAIN_GRAPH_REL,
72 .min_percent = 0.5
73 };
74
75 static u64 sample_type;
76
77 static struct rb_root hist;
78
79 static int64_t
80 hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
81 {
82 struct sort_entry *se;
83 int64_t cmp = 0;
84
85 list_for_each_entry(se, &hist_entry__sort_list, list) {
86 cmp = se->cmp(left, right);
87 if (cmp)
88 break;
89 }
90
91 return cmp;
92 }
93
94 static int64_t
95 hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
96 {
97 struct sort_entry *se;
98 int64_t cmp = 0;
99
100 list_for_each_entry(se, &hist_entry__sort_list, list) {
101 int64_t (*f)(struct hist_entry *, struct hist_entry *);
102
103 f = se->collapse ?: se->cmp;
104
105 cmp = f(left, right);
106 if (cmp)
107 break;
108 }
109
110 return cmp;
111 }
112
113 static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask)
114 {
115 int i;
116 size_t ret = 0;
117
118 ret += fprintf(fp, "%s", " ");
119
120 for (i = 0; i < depth; i++)
121 if (depth_mask & (1 << i))
122 ret += fprintf(fp, "| ");
123 else
124 ret += fprintf(fp, " ");
125
126 ret += fprintf(fp, "\n");
127
128 return ret;
129 }
130 static size_t
131 ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth,
132 int depth_mask, int count, u64 total_samples,
133 int hits)
134 {
135 int i;
136 size_t ret = 0;
137
138 ret += fprintf(fp, "%s", " ");
139 for (i = 0; i < depth; i++) {
140 if (depth_mask & (1 << i))
141 ret += fprintf(fp, "|");
142 else
143 ret += fprintf(fp, " ");
144 if (!count && i == depth - 1) {
145 double percent;
146
147 percent = hits * 100.0 / total_samples;
148 ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
149 } else
150 ret += fprintf(fp, "%s", " ");
151 }
152 if (chain->sym)
153 ret += fprintf(fp, "%s\n", chain->sym->name);
154 else
155 ret += fprintf(fp, "%p\n", (void *)(long)chain->ip);
156
157 return ret;
158 }
159
160 static struct symbol *rem_sq_bracket;
161 static struct callchain_list rem_hits;
162
163 static void init_rem_hits(void)
164 {
165 rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
166 if (!rem_sq_bracket) {
167 fprintf(stderr, "Not enough memory to display remaining hits\n");
168 return;
169 }
170
171 strcpy(rem_sq_bracket->name, "[...]");
172 rem_hits.sym = rem_sq_bracket;
173 }
174
175 static size_t
176 callchain__fprintf_graph(FILE *fp, struct callchain_node *self,
177 u64 total_samples, int depth, int depth_mask)
178 {
179 struct rb_node *node, *next;
180 struct callchain_node *child;
181 struct callchain_list *chain;
182 int new_depth_mask = depth_mask;
183 u64 new_total;
184 u64 remaining;
185 size_t ret = 0;
186 int i;
187
188 if (callchain_param.mode == CHAIN_GRAPH_REL)
189 new_total = self->children_hit;
190 else
191 new_total = total_samples;
192
193 remaining = new_total;
194
195 node = rb_first(&self->rb_root);
196 while (node) {
197 u64 cumul;
198
199 child = rb_entry(node, struct callchain_node, rb_node);
200 cumul = cumul_hits(child);
201 remaining -= cumul;
202
203 /*
204 * The depth mask manages the output of pipes that show
205 * the depth. We don't want to keep the pipes of the current
206 * level for the last child of this depth.
207 * Except if we have remaining filtered hits. They will
208 * supersede the last child
209 */
210 next = rb_next(node);
211 if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
212 new_depth_mask &= ~(1 << (depth - 1));
213
214 /*
215 * But we keep the older depth mask for the line seperator
216 * to keep the level link until we reach the last child
217 */
218 ret += ipchain__fprintf_graph_line(fp, depth, depth_mask);
219 i = 0;
220 list_for_each_entry(chain, &child->val, list) {
221 if (chain->ip >= PERF_CONTEXT_MAX)
222 continue;
223 ret += ipchain__fprintf_graph(fp, chain, depth,
224 new_depth_mask, i++,
225 new_total,
226 cumul);
227 }
228 ret += callchain__fprintf_graph(fp, child, new_total,
229 depth + 1,
230 new_depth_mask | (1 << depth));
231 node = next;
232 }
233
234 if (callchain_param.mode == CHAIN_GRAPH_REL &&
235 remaining && remaining != new_total) {
236
237 if (!rem_sq_bracket)
238 return ret;
239
240 new_depth_mask &= ~(1 << (depth - 1));
241
242 ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
243 new_depth_mask, 0, new_total,
244 remaining);
245 }
246
247 return ret;
248 }
249
250 static size_t
251 callchain__fprintf_flat(FILE *fp, struct callchain_node *self,
252 u64 total_samples)
253 {
254 struct callchain_list *chain;
255 size_t ret = 0;
256
257 if (!self)
258 return 0;
259
260 ret += callchain__fprintf_flat(fp, self->parent, total_samples);
261
262
263 list_for_each_entry(chain, &self->val, list) {
264 if (chain->ip >= PERF_CONTEXT_MAX)
265 continue;
266 if (chain->sym)
267 ret += fprintf(fp, " %s\n", chain->sym->name);
268 else
269 ret += fprintf(fp, " %p\n",
270 (void *)(long)chain->ip);
271 }
272
273 return ret;
274 }
275
276 static size_t
277 hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
278 u64 total_samples)
279 {
280 struct rb_node *rb_node;
281 struct callchain_node *chain;
282 size_t ret = 0;
283
284 rb_node = rb_first(&self->sorted_chain);
285 while (rb_node) {
286 double percent;
287
288 chain = rb_entry(rb_node, struct callchain_node, rb_node);
289 percent = chain->hit * 100.0 / total_samples;
290 switch (callchain_param.mode) {
291 case CHAIN_FLAT:
292 ret += percent_color_fprintf(fp, " %6.2f%%\n",
293 percent);
294 ret += callchain__fprintf_flat(fp, chain, total_samples);
295 break;
296 case CHAIN_GRAPH_ABS: /* Falldown */
297 case CHAIN_GRAPH_REL:
298 ret += callchain__fprintf_graph(fp, chain,
299 total_samples, 1, 1);
300 case CHAIN_NONE:
301 default:
302 break;
303 }
304 ret += fprintf(fp, "\n");
305 rb_node = rb_next(rb_node);
306 }
307
308 return ret;
309 }
310
311
312 static size_t
313 hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)
314 {
315 struct sort_entry *se;
316 size_t ret;
317
318 if (exclude_other && !self->parent)
319 return 0;
320
321 if (total_samples)
322 ret = percent_color_fprintf(fp,
323 field_sep ? "%.2f" : " %6.2f%%",
324 (self->count * 100.0) / total_samples);
325 else
326 ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count);
327
328 if (show_nr_samples) {
329 if (field_sep)
330 fprintf(fp, "%c%lld", *field_sep, self->count);
331 else
332 fprintf(fp, "%11lld", self->count);
333 }
334
335 list_for_each_entry(se, &hist_entry__sort_list, list) {
336 if (se->elide)
337 continue;
338
339 fprintf(fp, "%s", field_sep ?: " ");
340 ret += se->print(fp, self, se->width ? *se->width : 0);
341 }
342
343 ret += fprintf(fp, "\n");
344
345 if (callchain)
346 hist_entry_callchain__fprintf(fp, self, total_samples);
347
348 return ret;
349 }
350
351 /*
352 *
353 */
354
355 static void dso__calc_col_width(struct dso *self)
356 {
357 if (!col_width_list_str && !field_sep &&
358 (!dso_list || strlist__has_entry(dso_list, self->name))) {
359 unsigned int slen = strlen(self->name);
360 if (slen > dsos__col_width)
361 dsos__col_width = slen;
362 }
363
364 self->slen_calculated = 1;
365 }
366
367 static void thread__comm_adjust(struct thread *self)
368 {
369 char *comm = self->comm;
370
371 if (!col_width_list_str && !field_sep &&
372 (!comm_list || strlist__has_entry(comm_list, comm))) {
373 unsigned int slen = strlen(comm);
374
375 if (slen > comms__col_width) {
376 comms__col_width = slen;
377 threads__col_width = slen + 6;
378 }
379 }
380 }
381
382 static int thread__set_comm_adjust(struct thread *self, const char *comm)
383 {
384 int ret = thread__set_comm(self, comm);
385
386 if (ret)
387 return ret;
388
389 thread__comm_adjust(self);
390
391 return 0;
392 }
393
394
395 static struct symbol *
396 resolve_symbol(struct thread *thread, struct map **mapp,
397 struct dso **dsop, u64 *ipp)
398 {
399 struct dso *dso = dsop ? *dsop : NULL;
400 struct map *map = mapp ? *mapp : NULL;
401 u64 ip = *ipp;
402
403 if (!thread)
404 return NULL;
405
406 if (dso)
407 goto got_dso;
408
409 if (map)
410 goto got_map;
411
412 map = thread__find_map(thread, ip);
413 if (map != NULL) {
414 /*
415 * We have to do this here as we may have a dso
416 * with no symbol hit that has a name longer than
417 * the ones with symbols sampled.
418 */
419 if (!sort_dso.elide && !map->dso->slen_calculated)
420 dso__calc_col_width(map->dso);
421
422 if (mapp)
423 *mapp = map;
424 got_map:
425 ip = map->map_ip(map, ip);
426
427 dso = map->dso;
428 } else {
429 /*
430 * If this is outside of all known maps,
431 * and is a negative address, try to look it
432 * up in the kernel dso, as it might be a
433 * vsyscall (which executes in user-mode):
434 */
435 if ((long long)ip < 0)
436 dso = kernel_dso;
437 }
438 dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>");
439 dump_printf(" ...... map: %Lx -> %Lx\n", *ipp, ip);
440 *ipp = ip;
441
442 if (dsop)
443 *dsop = dso;
444
445 if (!dso)
446 return NULL;
447 got_dso:
448 return dso->find_symbol(dso, ip);
449 }
450
451 static int call__match(struct symbol *sym)
452 {
453 if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
454 return 1;
455
456 return 0;
457 }
458
459 static struct symbol **
460 resolve_callchain(struct thread *thread, struct map *map __used,
461 struct ip_callchain *chain, struct hist_entry *entry)
462 {
463 u64 context = PERF_CONTEXT_MAX;
464 struct symbol **syms = NULL;
465 unsigned int i;
466
467 if (callchain) {
468 syms = calloc(chain->nr, sizeof(*syms));
469 if (!syms) {
470 fprintf(stderr, "Can't allocate memory for symbols\n");
471 exit(-1);
472 }
473 }
474
475 for (i = 0; i < chain->nr; i++) {
476 u64 ip = chain->ips[i];
477 struct dso *dso = NULL;
478 struct symbol *sym;
479
480 if (ip >= PERF_CONTEXT_MAX) {
481 context = ip;
482 continue;
483 }
484
485 switch (context) {
486 case PERF_CONTEXT_HV:
487 dso = hypervisor_dso;
488 break;
489 case PERF_CONTEXT_KERNEL:
490 dso = kernel_dso;
491 break;
492 default:
493 break;
494 }
495
496 sym = resolve_symbol(thread, NULL, &dso, &ip);
497
498 if (sym) {
499 if (sort__has_parent && call__match(sym) &&
500 !entry->parent)
501 entry->parent = sym;
502 if (!callchain)
503 break;
504 syms[i] = sym;
505 }
506 }
507
508 return syms;
509 }
510
511 /*
512 * collect histogram counts
513 */
514
515 static int
516 hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
517 struct symbol *sym, u64 ip, struct ip_callchain *chain,
518 char level, u64 count)
519 {
520 struct rb_node **p = &hist.rb_node;
521 struct rb_node *parent = NULL;
522 struct hist_entry *he;
523 struct symbol **syms = NULL;
524 struct hist_entry entry = {
525 .thread = thread,
526 .map = map,
527 .dso = dso,
528 .sym = sym,
529 .ip = ip,
530 .level = level,
531 .count = count,
532 .parent = NULL,
533 .sorted_chain = RB_ROOT
534 };
535 int cmp;
536
537 if ((sort__has_parent || callchain) && chain)
538 syms = resolve_callchain(thread, map, chain, &entry);
539
540 while (*p != NULL) {
541 parent = *p;
542 he = rb_entry(parent, struct hist_entry, rb_node);
543
544 cmp = hist_entry__cmp(&entry, he);
545
546 if (!cmp) {
547 he->count += count;
548 if (callchain) {
549 append_chain(&he->callchain, chain, syms);
550 free(syms);
551 }
552 return 0;
553 }
554
555 if (cmp < 0)
556 p = &(*p)->rb_left;
557 else
558 p = &(*p)->rb_right;
559 }
560
561 he = malloc(sizeof(*he));
562 if (!he)
563 return -ENOMEM;
564 *he = entry;
565 if (callchain) {
566 callchain_init(&he->callchain);
567 append_chain(&he->callchain, chain, syms);
568 free(syms);
569 }
570 rb_link_node(&he->rb_node, parent, p);
571 rb_insert_color(&he->rb_node, &hist);
572
573 return 0;
574 }
575
576 static void hist_entry__free(struct hist_entry *he)
577 {
578 free(he);
579 }
580
581 /*
582 * collapse the histogram
583 */
584
585 static struct rb_root collapse_hists;
586
587 static void collapse__insert_entry(struct hist_entry *he)
588 {
589 struct rb_node **p = &collapse_hists.rb_node;
590 struct rb_node *parent = NULL;
591 struct hist_entry *iter;
592 int64_t cmp;
593
594 while (*p != NULL) {
595 parent = *p;
596 iter = rb_entry(parent, struct hist_entry, rb_node);
597
598 cmp = hist_entry__collapse(iter, he);
599
600 if (!cmp) {
601 iter->count += he->count;
602 hist_entry__free(he);
603 return;
604 }
605
606 if (cmp < 0)
607 p = &(*p)->rb_left;
608 else
609 p = &(*p)->rb_right;
610 }
611
612 rb_link_node(&he->rb_node, parent, p);
613 rb_insert_color(&he->rb_node, &collapse_hists);
614 }
615
616 static void collapse__resort(void)
617 {
618 struct rb_node *next;
619 struct hist_entry *n;
620
621 if (!sort__need_collapse)
622 return;
623
624 next = rb_first(&hist);
625 while (next) {
626 n = rb_entry(next, struct hist_entry, rb_node);
627 next = rb_next(&n->rb_node);
628
629 rb_erase(&n->rb_node, &hist);
630 collapse__insert_entry(n);
631 }
632 }
633
634 /*
635 * reverse the map, sort on count.
636 */
637
638 static struct rb_root output_hists;
639
640 static void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits)
641 {
642 struct rb_node **p = &output_hists.rb_node;
643 struct rb_node *parent = NULL;
644 struct hist_entry *iter;
645
646 if (callchain)
647 callchain_param.sort(&he->sorted_chain, &he->callchain,
648 min_callchain_hits, &callchain_param);
649
650 while (*p != NULL) {
651 parent = *p;
652 iter = rb_entry(parent, struct hist_entry, rb_node);
653
654 if (he->count > iter->count)
655 p = &(*p)->rb_left;
656 else
657 p = &(*p)->rb_right;
658 }
659
660 rb_link_node(&he->rb_node, parent, p);
661 rb_insert_color(&he->rb_node, &output_hists);
662 }
663
664 static void output__resort(u64 total_samples)
665 {
666 struct rb_node *next;
667 struct hist_entry *n;
668 struct rb_root *tree = &hist;
669 u64 min_callchain_hits;
670
671 min_callchain_hits = total_samples * (callchain_param.min_percent / 100);
672
673 if (sort__need_collapse)
674 tree = &collapse_hists;
675
676 next = rb_first(tree);
677
678 while (next) {
679 n = rb_entry(next, struct hist_entry, rb_node);
680 next = rb_next(&n->rb_node);
681
682 rb_erase(&n->rb_node, tree);
683 output__insert_entry(n, min_callchain_hits);
684 }
685 }
686
687 static size_t output__fprintf(FILE *fp, u64 total_samples)
688 {
689 struct hist_entry *pos;
690 struct sort_entry *se;
691 struct rb_node *nd;
692 size_t ret = 0;
693 unsigned int width;
694 char *col_width = col_width_list_str;
695 int raw_printing_style;
696
697 raw_printing_style = !strcmp(pretty_printing_style, "raw");
698
699 init_rem_hits();
700
701 fprintf(fp, "# Samples: %Ld\n", (u64)total_samples);
702 fprintf(fp, "#\n");
703
704 fprintf(fp, "# Overhead");
705 if (show_nr_samples) {
706 if (field_sep)
707 fprintf(fp, "%cSamples", *field_sep);
708 else
709 fputs(" Samples ", fp);
710 }
711 list_for_each_entry(se, &hist_entry__sort_list, list) {
712 if (se->elide)
713 continue;
714 if (field_sep) {
715 fprintf(fp, "%c%s", *field_sep, se->header);
716 continue;
717 }
718 width = strlen(se->header);
719 if (se->width) {
720 if (col_width_list_str) {
721 if (col_width) {
722 *se->width = atoi(col_width);
723 col_width = strchr(col_width, ',');
724 if (col_width)
725 ++col_width;
726 }
727 }
728 width = *se->width = max(*se->width, width);
729 }
730 fprintf(fp, " %*s", width, se->header);
731 }
732 fprintf(fp, "\n");
733
734 if (field_sep)
735 goto print_entries;
736
737 fprintf(fp, "# ........");
738 if (show_nr_samples)
739 fprintf(fp, " ..........");
740 list_for_each_entry(se, &hist_entry__sort_list, list) {
741 unsigned int i;
742
743 if (se->elide)
744 continue;
745
746 fprintf(fp, " ");
747 if (se->width)
748 width = *se->width;
749 else
750 width = strlen(se->header);
751 for (i = 0; i < width; i++)
752 fprintf(fp, ".");
753 }
754 fprintf(fp, "\n");
755
756 fprintf(fp, "#\n");
757
758 print_entries:
759 for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
760 pos = rb_entry(nd, struct hist_entry, rb_node);
761 ret += hist_entry__fprintf(fp, pos, total_samples);
762 }
763
764 if (sort_order == default_sort_order &&
765 parent_pattern == default_parent_pattern) {
766 fprintf(fp, "#\n");
767 fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n");
768 fprintf(fp, "#\n");
769 }
770 fprintf(fp, "\n");
771
772 free(rem_sq_bracket);
773
774 if (show_threads)
775 perf_read_values_display(fp, &show_threads_values,
776 raw_printing_style);
777
778 return ret;
779 }
780
781 static unsigned long total = 0,
782 total_mmap = 0,
783 total_comm = 0,
784 total_fork = 0,
785 total_unknown = 0,
786 total_lost = 0;
787
788 static int validate_chain(struct ip_callchain *chain, event_t *event)
789 {
790 unsigned int chain_size;
791
792 chain_size = event->header.size;
793 chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
794
795 if (chain->nr*sizeof(u64) > chain_size)
796 return -1;
797
798 return 0;
799 }
800
801 static int
802 process_sample_event(event_t *event, unsigned long offset, unsigned long head)
803 {
804 char level;
805 int show = 0;
806 struct dso *dso = NULL;
807 struct thread *thread;
808 u64 ip = event->ip.ip;
809 u64 period = 1;
810 struct map *map = NULL;
811 void *more_data = event->ip.__more_data;
812 struct ip_callchain *chain = NULL;
813 int cpumode;
814
815 thread = threads__findnew(event->ip.pid, &threads, &last_match);
816
817 if (sample_type & PERF_SAMPLE_PERIOD) {
818 period = *(u64 *)more_data;
819 more_data += sizeof(u64);
820 }
821
822 dump_printf("%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n",
823 (void *)(offset + head),
824 (void *)(long)(event->header.size),
825 event->header.misc,
826 event->ip.pid, event->ip.tid,
827 (void *)(long)ip,
828 (long long)period);
829
830 if (sample_type & PERF_SAMPLE_CALLCHAIN) {
831 unsigned int i;
832
833 chain = (void *)more_data;
834
835 dump_printf("... chain: nr:%Lu\n", chain->nr);
836
837 if (validate_chain(chain, event) < 0) {
838 eprintf("call-chain problem with event, skipping it.\n");
839 return 0;
840 }
841
842 if (dump_trace) {
843 for (i = 0; i < chain->nr; i++)
844 dump_printf("..... %2d: %016Lx\n", i, chain->ips[i]);
845 }
846 }
847
848 dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
849
850 if (thread == NULL) {
851 eprintf("problem processing %d event, skipping it.\n",
852 event->header.type);
853 return -1;
854 }
855
856 if (comm_list && !strlist__has_entry(comm_list, thread->comm))
857 return 0;
858
859 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
860
861 if (cpumode == PERF_RECORD_MISC_KERNEL) {
862 show = SHOW_KERNEL;
863 level = 'k';
864
865 dso = kernel_dso;
866
867 dump_printf(" ...... dso: %s\n", dso->name);
868
869 } else if (cpumode == PERF_RECORD_MISC_USER) {
870
871 show = SHOW_USER;
872 level = '.';
873
874 } else {
875 show = SHOW_HV;
876 level = 'H';
877
878 dso = hypervisor_dso;
879
880 dump_printf(" ...... dso: [hypervisor]\n");
881 }
882
883 if (show & show_mask) {
884 struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);
885
886 if (dso_list && (!dso || !dso->name ||
887 !strlist__has_entry(dso_list, dso->name)))
888 return 0;
889
890 if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name)))
891 return 0;
892
893 if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
894 eprintf("problem incrementing symbol count, skipping event\n");
895 return -1;
896 }
897 }
898 total += period;
899
900 return 0;
901 }
902
903 static int
904 process_mmap_event(event_t *event, unsigned long offset, unsigned long head)
905 {
906 struct thread *thread;
907 struct map *map = map__new(&event->mmap, cwd, cwdlen);
908
909 thread = threads__findnew(event->mmap.pid, &threads, &last_match);
910
911 dump_printf("%p [%p]: PERF_RECORD_MMAP %d/%d: [%p(%p) @ %p]: %s\n",
912 (void *)(offset + head),
913 (void *)(long)(event->header.size),
914 event->mmap.pid,
915 event->mmap.tid,
916 (void *)(long)event->mmap.start,
917 (void *)(long)event->mmap.len,
918 (void *)(long)event->mmap.pgoff,
919 event->mmap.filename);
920
921 if (thread == NULL || map == NULL) {
922 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
923 return 0;
924 }
925
926 thread__insert_map(thread, map);
927 total_mmap++;
928
929 return 0;
930 }
931
932 static int
933 process_comm_event(event_t *event, unsigned long offset, unsigned long head)
934 {
935 struct thread *thread;
936
937 thread = threads__findnew(event->comm.pid, &threads, &last_match);
938
939 dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n",
940 (void *)(offset + head),
941 (void *)(long)(event->header.size),
942 event->comm.comm, event->comm.pid);
943
944 if (thread == NULL ||
945 thread__set_comm_adjust(thread, event->comm.comm)) {
946 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
947 return -1;
948 }
949 total_comm++;
950
951 return 0;
952 }
953
954 static int
955 process_task_event(event_t *event, unsigned long offset, unsigned long head)
956 {
957 struct thread *thread;
958 struct thread *parent;
959
960 thread = threads__findnew(event->fork.pid, &threads, &last_match);
961 parent = threads__findnew(event->fork.ppid, &threads, &last_match);
962
963 dump_printf("%p [%p]: PERF_RECORD_%s: (%d:%d):(%d:%d)\n",
964 (void *)(offset + head),
965 (void *)(long)(event->header.size),
966 event->header.type == PERF_RECORD_FORK ? "FORK" : "EXIT",
967 event->fork.pid, event->fork.tid,
968 event->fork.ppid, event->fork.ptid);
969
970 /*
971 * A thread clone will have the same PID for both
972 * parent and child.
973 */
974 if (thread == parent)
975 return 0;
976
977 if (event->header.type == PERF_RECORD_EXIT)
978 return 0;
979
980 if (!thread || !parent || thread__fork(thread, parent)) {
981 dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
982 return -1;
983 }
984 total_fork++;
985
986 return 0;
987 }
988
989 static int
990 process_lost_event(event_t *event, unsigned long offset, unsigned long head)
991 {
992 dump_printf("%p [%p]: PERF_RECORD_LOST: id:%Ld: lost:%Ld\n",
993 (void *)(offset + head),
994 (void *)(long)(event->header.size),
995 event->lost.id,
996 event->lost.lost);
997
998 total_lost += event->lost.lost;
999
1000 return 0;
1001 }
1002
1003 static int
1004 process_read_event(event_t *event, unsigned long offset, unsigned long head)
1005 {
1006 struct perf_event_attr *attr;
1007
1008 attr = perf_header__find_attr(event->read.id, header);
1009
1010 if (show_threads) {
1011 const char *name = attr ? __event_name(attr->type, attr->config)
1012 : "unknown";
1013 perf_read_values_add_value(&show_threads_values,
1014 event->read.pid, event->read.tid,
1015 event->read.id,
1016 name,
1017 event->read.value);
1018 }
1019
1020 dump_printf("%p [%p]: PERF_RECORD_READ: %d %d %s %Lu\n",
1021 (void *)(offset + head),
1022 (void *)(long)(event->header.size),
1023 event->read.pid,
1024 event->read.tid,
1025 attr ? __event_name(attr->type, attr->config)
1026 : "FAIL",
1027 event->read.value);
1028
1029 return 0;
1030 }
1031
1032 static int
1033 process_event(event_t *event, unsigned long offset, unsigned long head)
1034 {
1035 trace_event(event);
1036
1037 switch (event->header.type) {
1038 case PERF_RECORD_SAMPLE:
1039 return process_sample_event(event, offset, head);
1040
1041 case PERF_RECORD_MMAP:
1042 return process_mmap_event(event, offset, head);
1043
1044 case PERF_RECORD_COMM:
1045 return process_comm_event(event, offset, head);
1046
1047 case PERF_RECORD_FORK:
1048 case PERF_RECORD_EXIT:
1049 return process_task_event(event, offset, head);
1050
1051 case PERF_RECORD_LOST:
1052 return process_lost_event(event, offset, head);
1053
1054 case PERF_RECORD_READ:
1055 return process_read_event(event, offset, head);
1056
1057 /*
1058 * We dont process them right now but they are fine:
1059 */
1060
1061 case PERF_RECORD_THROTTLE:
1062 case PERF_RECORD_UNTHROTTLE:
1063 return 0;
1064
1065 default:
1066 return -1;
1067 }
1068
1069 return 0;
1070 }
1071
1072 static int __cmd_report(void)
1073 {
1074 int ret, rc = EXIT_FAILURE;
1075 unsigned long offset = 0;
1076 unsigned long head, shift;
1077 struct stat input_stat;
1078 struct thread *idle;
1079 event_t *event;
1080 uint32_t size;
1081 char *buf;
1082
1083 idle = register_idle_thread(&threads, &last_match);
1084 thread__comm_adjust(idle);
1085
1086 if (show_threads)
1087 perf_read_values_init(&show_threads_values);
1088
1089 input = open(input_name, O_RDONLY);
1090 if (input < 0) {
1091 fprintf(stderr, " failed to open file: %s", input_name);
1092 if (!strcmp(input_name, "perf.data"))
1093 fprintf(stderr, " (try 'perf record' first)");
1094 fprintf(stderr, "\n");
1095 exit(-1);
1096 }
1097
1098 ret = fstat(input, &input_stat);
1099 if (ret < 0) {
1100 perror("failed to stat file");
1101 exit(-1);
1102 }
1103
1104 if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
1105 fprintf(stderr, "file: %s not owned by current user or root\n", input_name);
1106 exit(-1);
1107 }
1108
1109 if (!input_stat.st_size) {
1110 fprintf(stderr, "zero-sized file, nothing to do!\n");
1111 exit(0);
1112 }
1113
1114 header = perf_header__read(input);
1115 head = header->data_offset;
1116
1117 sample_type = perf_header__sample_type(header);
1118
1119 if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) {
1120 if (sort__has_parent) {
1121 fprintf(stderr, "selected --sort parent, but no"
1122 " callchain data. Did you call"
1123 " perf record without -g?\n");
1124 exit(-1);
1125 }
1126 if (callchain) {
1127 fprintf(stderr, "selected -g but no callchain data."
1128 " Did you call perf record without"
1129 " -g?\n");
1130 exit(-1);
1131 }
1132 } else if (callchain_param.mode != CHAIN_NONE && !callchain) {
1133 callchain = 1;
1134 if (register_callchain_param(&callchain_param) < 0) {
1135 fprintf(stderr, "Can't register callchain"
1136 " params\n");
1137 exit(-1);
1138 }
1139 }
1140
1141 if (load_kernel() < 0) {
1142 perror("failed to load kernel symbols");
1143 return EXIT_FAILURE;
1144 }
1145
1146 if (!full_paths) {
1147 if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
1148 perror("failed to get the current directory");
1149 return EXIT_FAILURE;
1150 }
1151 cwdlen = strlen(cwd);
1152 } else {
1153 cwd = NULL;
1154 cwdlen = 0;
1155 }
1156
1157 shift = page_size * (head / page_size);
1158 offset += shift;
1159 head -= shift;
1160
1161 remap:
1162 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
1163 MAP_SHARED, input, offset);
1164 if (buf == MAP_FAILED) {
1165 perror("failed to mmap file");
1166 exit(-1);
1167 }
1168
1169 more:
1170 event = (event_t *)(buf + head);
1171
1172 size = event->header.size;
1173 if (!size)
1174 size = 8;
1175
1176 if (head + event->header.size >= page_size * mmap_window) {
1177 int munmap_ret;
1178
1179 shift = page_size * (head / page_size);
1180
1181 munmap_ret = munmap(buf, page_size * mmap_window);
1182 assert(munmap_ret == 0);
1183
1184 offset += shift;
1185 head -= shift;
1186 goto remap;
1187 }
1188
1189 size = event->header.size;
1190
1191 dump_printf("\n%p [%p]: event: %d\n",
1192 (void *)(offset + head),
1193 (void *)(long)event->header.size,
1194 event->header.type);
1195
1196 if (!size || process_event(event, offset, head) < 0) {
1197
1198 dump_printf("%p [%p]: skipping unknown header type: %d\n",
1199 (void *)(offset + head),
1200 (void *)(long)(event->header.size),
1201 event->header.type);
1202
1203 total_unknown++;
1204
1205 /*
1206 * assume we lost track of the stream, check alignment, and
1207 * increment a single u64 in the hope to catch on again 'soon'.
1208 */
1209
1210 if (unlikely(head & 7))
1211 head &= ~7ULL;
1212
1213 size = 8;
1214 }
1215
1216 head += size;
1217
1218 if (offset + head >= header->data_offset + header->data_size)
1219 goto done;
1220
1221 if (offset + head < (unsigned long)input_stat.st_size)
1222 goto more;
1223
1224 done:
1225 rc = EXIT_SUCCESS;
1226 close(input);
1227
1228 dump_printf(" IP events: %10ld\n", total);
1229 dump_printf(" mmap events: %10ld\n", total_mmap);
1230 dump_printf(" comm events: %10ld\n", total_comm);
1231 dump_printf(" fork events: %10ld\n", total_fork);
1232 dump_printf(" lost events: %10ld\n", total_lost);
1233 dump_printf(" unknown events: %10ld\n", total_unknown);
1234
1235 if (dump_trace)
1236 return 0;
1237
1238 if (verbose >= 3)
1239 threads__fprintf(stdout, &threads);
1240
1241 if (verbose >= 2)
1242 dsos__fprintf(stdout);
1243
1244 collapse__resort();
1245 output__resort(total);
1246 output__fprintf(stdout, total);
1247
1248 if (show_threads)
1249 perf_read_values_destroy(&show_threads_values);
1250
1251 return rc;
1252 }
1253
1254 static int
1255 parse_callchain_opt(const struct option *opt __used, const char *arg,
1256 int unset __used)
1257 {
1258 char *tok;
1259 char *endptr;
1260
1261 callchain = 1;
1262
1263 if (!arg)
1264 return 0;
1265
1266 tok = strtok((char *)arg, ",");
1267 if (!tok)
1268 return -1;
1269
1270 /* get the output mode */
1271 if (!strncmp(tok, "graph", strlen(arg)))
1272 callchain_param.mode = CHAIN_GRAPH_ABS;
1273
1274 else if (!strncmp(tok, "flat", strlen(arg)))
1275 callchain_param.mode = CHAIN_FLAT;
1276
1277 else if (!strncmp(tok, "fractal", strlen(arg)))
1278 callchain_param.mode = CHAIN_GRAPH_REL;
1279
1280 else if (!strncmp(tok, "none", strlen(arg))) {
1281 callchain_param.mode = CHAIN_NONE;
1282 callchain = 0;
1283
1284 return 0;
1285 }
1286
1287 else
1288 return -1;
1289
1290 /* get the min percentage */
1291 tok = strtok(NULL, ",");
1292 if (!tok)
1293 goto setup;
1294
1295 callchain_param.min_percent = strtod(tok, &endptr);
1296 if (tok == endptr)
1297 return -1;
1298
1299 setup:
1300 if (register_callchain_param(&callchain_param) < 0) {
1301 fprintf(stderr, "Can't register callchain params\n");
1302 return -1;
1303 }
1304 return 0;
1305 }
1306
1307 //static const char * const report_usage[] = {
1308 const char * const report_usage[] = {
1309 "perf report [<options>] <command>",
1310 NULL
1311 };
1312
1313 static const struct option options[] = {
1314 OPT_STRING('i', "input", &input_name, "file",
1315 "input file name"),
1316 OPT_BOOLEAN('v', "verbose", &verbose,
1317 "be more verbose (show symbol address, etc)"),
1318 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
1319 "dump raw trace in ASCII"),
1320 OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"),
1321 OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
1322 OPT_BOOLEAN('m', "modules", &modules,
1323 "load module symbols - WARNING: use only with -k and LIVE kernel"),
1324 OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples,
1325 "Show a column with the number of samples"),
1326 OPT_BOOLEAN('T', "threads", &show_threads,
1327 "Show per-thread event counters"),
1328 OPT_STRING(0, "pretty", &pretty_printing_style, "key",
1329 "pretty printing style key: normal raw"),
1330 OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
1331 "sort by key(s): pid, comm, dso, symbol, parent"),
1332 OPT_BOOLEAN('P', "full-paths", &full_paths,
1333 "Don't shorten the pathnames taking into account the cwd"),
1334 OPT_STRING('p', "parent", &parent_pattern, "regex",
1335 "regex filter to identify parent, see: '--sort parent'"),
1336 OPT_BOOLEAN('x', "exclude-other", &exclude_other,
1337 "Only display entries with parent-match"),
1338 OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent",
1339 "Display callchains using output_type and min percent threshold. "
1340 "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt),
1341 OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]",
1342 "only consider symbols in these dsos"),
1343 OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]",
1344 "only consider symbols in these comms"),
1345 OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]",
1346 "only consider these symbols"),
1347 OPT_STRING('w', "column-widths", &col_width_list_str,
1348 "width[,width...]",
1349 "don't try to adjust column width, use these fixed values"),
1350 OPT_STRING('t', "field-separator", &field_sep, "separator",
1351 "separator for columns, no spaces will be added between "
1352 "columns '.' is reserved."),
1353 OPT_END()
1354 };
1355
1356 static void setup_sorting(void)
1357 {
1358 char *tmp, *tok, *str = strdup(sort_order);
1359
1360 for (tok = strtok_r(str, ", ", &tmp);
1361 tok; tok = strtok_r(NULL, ", ", &tmp)) {
1362 if (sort_dimension__add(tok) < 0) {
1363 error("Unknown --sort key: `%s'", tok);
1364 usage_with_options(report_usage, options);
1365 }
1366 }
1367
1368 free(str);
1369 }
1370
1371 static void setup_list(struct strlist **list, const char *list_str,
1372 struct sort_entry *se, const char *list_name,
1373 FILE *fp)
1374 {
1375 if (list_str) {
1376 *list = strlist__new(true, list_str);
1377 if (!*list) {
1378 fprintf(stderr, "problems parsing %s list\n",
1379 list_name);
1380 exit(129);
1381 }
1382 if (strlist__nr_entries(*list) == 1) {
1383 fprintf(fp, "# %s: %s\n", list_name,
1384 strlist__entry(*list, 0)->s);
1385 se->elide = true;
1386 }
1387 }
1388 }
1389
1390 int cmd_report(int argc, const char **argv, const char *prefix __used)
1391 {
1392 symbol__init();
1393
1394 page_size = getpagesize();
1395
1396 argc = parse_options(argc, argv, options, report_usage, 0);
1397
1398 setup_sorting();
1399
1400 if (parent_pattern != default_parent_pattern) {
1401 sort_dimension__add("parent");
1402 sort_parent.elide = 1;
1403 } else
1404 exclude_other = 0;
1405
1406 /*
1407 * Any (unrecognized) arguments left?
1408 */
1409 if (argc)
1410 usage_with_options(report_usage, options);
1411
1412 setup_pager();
1413
1414 setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout);
1415 setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout);
1416 setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout);
1417
1418 if (field_sep && *field_sep == '.') {
1419 fputs("'.' is the only non valid --field-separator argument\n",
1420 stderr);
1421 exit(129);
1422 }
1423
1424 return __cmd_report();
1425 }
This page took 0.067809 seconds and 5 git commands to generate.