diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r-- | tools/perf/builtin-report.c | 652 |
1 files changed, 499 insertions, 153 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 135b7837e6bf..8cb58d68a006 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -10,9 +10,9 @@ | |||
10 | #include "util/util.h" | 10 | #include "util/util.h" |
11 | 11 | ||
12 | #include "util/color.h" | 12 | #include "util/color.h" |
13 | #include "util/list.h" | 13 | #include <linux/list.h> |
14 | #include "util/cache.h" | 14 | #include "util/cache.h" |
15 | #include "util/rbtree.h" | 15 | #include <linux/rbtree.h> |
16 | #include "util/symbol.h" | 16 | #include "util/symbol.h" |
17 | #include "util/string.h" | 17 | #include "util/string.h" |
18 | #include "util/callchain.h" | 18 | #include "util/callchain.h" |
@@ -31,10 +31,12 @@ | |||
31 | static char const *input_name = "perf.data"; | 31 | static char const *input_name = "perf.data"; |
32 | static char *vmlinux = NULL; | 32 | static char *vmlinux = NULL; |
33 | 33 | ||
34 | static char default_sort_order[] = "comm,dso"; | 34 | static char default_sort_order[] = "comm,dso,symbol"; |
35 | static char *sort_order = default_sort_order; | 35 | static char *sort_order = default_sort_order; |
36 | static char *dso_list_str, *comm_list_str, *sym_list_str; | 36 | static char *dso_list_str, *comm_list_str, *sym_list_str, |
37 | *col_width_list_str; | ||
37 | static struct strlist *dso_list, *comm_list, *sym_list; | 38 | static struct strlist *dso_list, *comm_list, *sym_list; |
39 | static char *field_sep; | ||
38 | 40 | ||
39 | static int input; | 41 | static int input; |
40 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | 42 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; |
@@ -46,7 +48,10 @@ static int dump_trace = 0; | |||
46 | static int verbose; | 48 | static int verbose; |
47 | #define eprintf(x...) do { if (verbose) fprintf(stderr, x); } while (0) | 49 | #define eprintf(x...) do { if (verbose) fprintf(stderr, x); } while (0) |
48 | 50 | ||
51 | static int modules; | ||
52 | |||
49 | static int full_paths; | 53 | static int full_paths; |
54 | static int show_nr_samples; | ||
50 | 55 | ||
51 | static unsigned long page_size; | 56 | static unsigned long page_size; |
52 | static unsigned long mmap_window = 32; | 57 | static unsigned long mmap_window = 32; |
@@ -56,8 +61,17 @@ static char *parent_pattern = default_parent_pattern; | |||
56 | static regex_t parent_regex; | 61 | static regex_t parent_regex; |
57 | 62 | ||
58 | static int exclude_other = 1; | 63 | static int exclude_other = 1; |
64 | |||
65 | static char callchain_default_opt[] = "fractal,0.5"; | ||
66 | |||
59 | static int callchain; | 67 | static int callchain; |
60 | 68 | ||
69 | static | ||
70 | struct callchain_param callchain_param = { | ||
71 | .mode = CHAIN_GRAPH_ABS, | ||
72 | .min_percent = 0.5 | ||
73 | }; | ||
74 | |||
61 | static u64 sample_type; | 75 | static u64 sample_type; |
62 | 76 | ||
63 | struct ip_event { | 77 | struct ip_event { |
@@ -85,13 +99,7 @@ struct comm_event { | |||
85 | struct fork_event { | 99 | struct fork_event { |
86 | struct perf_event_header header; | 100 | struct perf_event_header header; |
87 | u32 pid, ppid; | 101 | u32 pid, ppid; |
88 | }; | 102 | u32 tid, ptid; |
89 | |||
90 | struct period_event { | ||
91 | struct perf_event_header header; | ||
92 | u64 time; | ||
93 | u64 id; | ||
94 | u64 sample_period; | ||
95 | }; | 103 | }; |
96 | 104 | ||
97 | struct lost_event { | 105 | struct lost_event { |
@@ -113,14 +121,41 @@ typedef union event_union { | |||
113 | struct mmap_event mmap; | 121 | struct mmap_event mmap; |
114 | struct comm_event comm; | 122 | struct comm_event comm; |
115 | struct fork_event fork; | 123 | struct fork_event fork; |
116 | struct period_event period; | ||
117 | struct lost_event lost; | 124 | struct lost_event lost; |
118 | struct read_event read; | 125 | struct read_event read; |
119 | } event_t; | 126 | } event_t; |
120 | 127 | ||
128 | static int repsep_fprintf(FILE *fp, const char *fmt, ...) | ||
129 | { | ||
130 | int n; | ||
131 | va_list ap; | ||
132 | |||
133 | va_start(ap, fmt); | ||
134 | if (!field_sep) | ||
135 | n = vfprintf(fp, fmt, ap); | ||
136 | else { | ||
137 | char *bf = NULL; | ||
138 | n = vasprintf(&bf, fmt, ap); | ||
139 | if (n > 0) { | ||
140 | char *sep = bf; | ||
141 | while (1) { | ||
142 | sep = strchr(sep, *field_sep); | ||
143 | if (sep == NULL) | ||
144 | break; | ||
145 | *sep = '.'; | ||
146 | } | ||
147 | } | ||
148 | fputs(bf, fp); | ||
149 | free(bf); | ||
150 | } | ||
151 | va_end(ap); | ||
152 | return n; | ||
153 | } | ||
154 | |||
121 | static LIST_HEAD(dsos); | 155 | static LIST_HEAD(dsos); |
122 | static struct dso *kernel_dso; | 156 | static struct dso *kernel_dso; |
123 | static struct dso *vdso; | 157 | static struct dso *vdso; |
158 | static struct dso *hypervisor_dso; | ||
124 | 159 | ||
125 | static void dsos__add(struct dso *dso) | 160 | static void dsos__add(struct dso *dso) |
126 | { | 161 | { |
@@ -176,7 +211,7 @@ static void dsos__fprintf(FILE *fp) | |||
176 | 211 | ||
177 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) | 212 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) |
178 | { | 213 | { |
179 | return dso__find_symbol(kernel_dso, ip); | 214 | return dso__find_symbol(dso, ip); |
180 | } | 215 | } |
181 | 216 | ||
182 | static int load_kernel(void) | 217 | static int load_kernel(void) |
@@ -187,8 +222,8 @@ static int load_kernel(void) | |||
187 | if (!kernel_dso) | 222 | if (!kernel_dso) |
188 | return -1; | 223 | return -1; |
189 | 224 | ||
190 | err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose); | 225 | err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose, modules); |
191 | if (err) { | 226 | if (err <= 0) { |
192 | dso__delete(kernel_dso); | 227 | dso__delete(kernel_dso); |
193 | kernel_dso = NULL; | 228 | kernel_dso = NULL; |
194 | } else | 229 | } else |
@@ -202,6 +237,11 @@ static int load_kernel(void) | |||
202 | 237 | ||
203 | dsos__add(vdso); | 238 | dsos__add(vdso); |
204 | 239 | ||
240 | hypervisor_dso = dso__new("[hypervisor]", 0); | ||
241 | if (!hypervisor_dso) | ||
242 | return -1; | ||
243 | dsos__add(hypervisor_dso); | ||
244 | |||
205 | return err; | 245 | return err; |
206 | } | 246 | } |
207 | 247 | ||
@@ -213,7 +253,7 @@ static int strcommon(const char *pathname) | |||
213 | { | 253 | { |
214 | int n = 0; | 254 | int n = 0; |
215 | 255 | ||
216 | while (pathname[n] == cwd[n] && n < cwdlen) | 256 | while (n < cwdlen && pathname[n] == cwd[n]) |
217 | ++n; | 257 | ++n; |
218 | 258 | ||
219 | return n; | 259 | return n; |
@@ -233,7 +273,7 @@ static u64 map__map_ip(struct map *map, u64 ip) | |||
233 | return ip - map->start + map->pgoff; | 273 | return ip - map->start + map->pgoff; |
234 | } | 274 | } |
235 | 275 | ||
236 | static u64 vdso__map_ip(struct map *map, u64 ip) | 276 | static u64 vdso__map_ip(struct map *map __used, u64 ip) |
237 | { | 277 | { |
238 | return ip; | 278 | return ip; |
239 | } | 279 | } |
@@ -343,12 +383,28 @@ static struct thread *thread__new(pid_t pid) | |||
343 | return self; | 383 | return self; |
344 | } | 384 | } |
345 | 385 | ||
386 | static unsigned int dsos__col_width, | ||
387 | comms__col_width, | ||
388 | threads__col_width; | ||
389 | |||
346 | static int thread__set_comm(struct thread *self, const char *comm) | 390 | static int thread__set_comm(struct thread *self, const char *comm) |
347 | { | 391 | { |
348 | if (self->comm) | 392 | if (self->comm) |
349 | free(self->comm); | 393 | free(self->comm); |
350 | self->comm = strdup(comm); | 394 | self->comm = strdup(comm); |
351 | return self->comm ? 0 : -ENOMEM; | 395 | if (!self->comm) |
396 | return -ENOMEM; | ||
397 | |||
398 | if (!col_width_list_str && !field_sep && | ||
399 | (!comm_list || strlist__has_entry(comm_list, comm))) { | ||
400 | unsigned int slen = strlen(comm); | ||
401 | if (slen > comms__col_width) { | ||
402 | comms__col_width = slen; | ||
403 | threads__col_width = slen + 6; | ||
404 | } | ||
405 | } | ||
406 | |||
407 | return 0; | ||
352 | } | 408 | } |
353 | 409 | ||
354 | static size_t thread__fprintf(struct thread *self, FILE *fp) | 410 | static size_t thread__fprintf(struct thread *self, FILE *fp) |
@@ -519,7 +575,9 @@ struct sort_entry { | |||
519 | 575 | ||
520 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); | 576 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); |
521 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); | 577 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); |
522 | size_t (*print)(FILE *fp, struct hist_entry *); | 578 | size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); |
579 | unsigned int *width; | ||
580 | bool elide; | ||
523 | }; | 581 | }; |
524 | 582 | ||
525 | static int64_t cmp_null(void *l, void *r) | 583 | static int64_t cmp_null(void *l, void *r) |
@@ -541,15 +599,17 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | |||
541 | } | 599 | } |
542 | 600 | ||
543 | static size_t | 601 | static size_t |
544 | sort__thread_print(FILE *fp, struct hist_entry *self) | 602 | sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) |
545 | { | 603 | { |
546 | return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid); | 604 | return repsep_fprintf(fp, "%*s:%5d", width - 6, |
605 | self->thread->comm ?: "", self->thread->pid); | ||
547 | } | 606 | } |
548 | 607 | ||
549 | static struct sort_entry sort_thread = { | 608 | static struct sort_entry sort_thread = { |
550 | .header = " Command: Pid", | 609 | .header = "Command: Pid", |
551 | .cmp = sort__thread_cmp, | 610 | .cmp = sort__thread_cmp, |
552 | .print = sort__thread_print, | 611 | .print = sort__thread_print, |
612 | .width = &threads__col_width, | ||
553 | }; | 613 | }; |
554 | 614 | ||
555 | /* --sort comm */ | 615 | /* --sort comm */ |
@@ -573,16 +633,17 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | |||
573 | } | 633 | } |
574 | 634 | ||
575 | static size_t | 635 | static size_t |
576 | sort__comm_print(FILE *fp, struct hist_entry *self) | 636 | sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) |
577 | { | 637 | { |
578 | return fprintf(fp, "%16s", self->thread->comm); | 638 | return repsep_fprintf(fp, "%*s", width, self->thread->comm); |
579 | } | 639 | } |
580 | 640 | ||
581 | static struct sort_entry sort_comm = { | 641 | static struct sort_entry sort_comm = { |
582 | .header = " Command", | 642 | .header = "Command", |
583 | .cmp = sort__comm_cmp, | 643 | .cmp = sort__comm_cmp, |
584 | .collapse = sort__comm_collapse, | 644 | .collapse = sort__comm_collapse, |
585 | .print = sort__comm_print, | 645 | .print = sort__comm_print, |
646 | .width = &comms__col_width, | ||
586 | }; | 647 | }; |
587 | 648 | ||
588 | /* --sort dso */ | 649 | /* --sort dso */ |
@@ -600,18 +661,19 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | |||
600 | } | 661 | } |
601 | 662 | ||
602 | static size_t | 663 | static size_t |
603 | sort__dso_print(FILE *fp, struct hist_entry *self) | 664 | sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) |
604 | { | 665 | { |
605 | if (self->dso) | 666 | if (self->dso) |
606 | return fprintf(fp, "%-25s", self->dso->name); | 667 | return repsep_fprintf(fp, "%-*s", width, self->dso->name); |
607 | 668 | ||
608 | return fprintf(fp, "%016llx ", (u64)self->ip); | 669 | return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); |
609 | } | 670 | } |
610 | 671 | ||
611 | static struct sort_entry sort_dso = { | 672 | static struct sort_entry sort_dso = { |
612 | .header = "Shared Object ", | 673 | .header = "Shared Object", |
613 | .cmp = sort__dso_cmp, | 674 | .cmp = sort__dso_cmp, |
614 | .print = sort__dso_print, | 675 | .print = sort__dso_print, |
676 | .width = &dsos__col_width, | ||
615 | }; | 677 | }; |
616 | 678 | ||
617 | /* --sort symbol */ | 679 | /* --sort symbol */ |
@@ -631,18 +693,22 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | |||
631 | } | 693 | } |
632 | 694 | ||
633 | static size_t | 695 | static size_t |
634 | sort__sym_print(FILE *fp, struct hist_entry *self) | 696 | sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) |
635 | { | 697 | { |
636 | size_t ret = 0; | 698 | size_t ret = 0; |
637 | 699 | ||
638 | if (verbose) | 700 | if (verbose) |
639 | ret += fprintf(fp, "%#018llx ", (u64)self->ip); | 701 | ret += repsep_fprintf(fp, "%#018llx ", (u64)self->ip); |
640 | 702 | ||
703 | ret += repsep_fprintf(fp, "[%c] ", self->level); | ||
641 | if (self->sym) { | 704 | if (self->sym) { |
642 | ret += fprintf(fp, "[%c] %s", | 705 | ret += repsep_fprintf(fp, "%s", self->sym->name); |
643 | self->dso == kernel_dso ? 'k' : '.', self->sym->name); | 706 | |
707 | if (self->sym->module) | ||
708 | ret += repsep_fprintf(fp, "\t[%s]", | ||
709 | self->sym->module->name); | ||
644 | } else { | 710 | } else { |
645 | ret += fprintf(fp, "%#016llx", (u64)self->ip); | 711 | ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); |
646 | } | 712 | } |
647 | 713 | ||
648 | return ret; | 714 | return ret; |
@@ -669,19 +735,19 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | |||
669 | } | 735 | } |
670 | 736 | ||
671 | static size_t | 737 | static size_t |
672 | sort__parent_print(FILE *fp, struct hist_entry *self) | 738 | sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) |
673 | { | 739 | { |
674 | size_t ret = 0; | 740 | return repsep_fprintf(fp, "%-*s", width, |
675 | 741 | self->parent ? self->parent->name : "[other]"); | |
676 | ret += fprintf(fp, "%-20s", self->parent ? self->parent->name : "[other]"); | ||
677 | |||
678 | return ret; | ||
679 | } | 742 | } |
680 | 743 | ||
744 | static unsigned int parent_symbol__col_width; | ||
745 | |||
681 | static struct sort_entry sort_parent = { | 746 | static struct sort_entry sort_parent = { |
682 | .header = "Parent symbol ", | 747 | .header = "Parent symbol", |
683 | .cmp = sort__parent_cmp, | 748 | .cmp = sort__parent_cmp, |
684 | .print = sort__parent_print, | 749 | .print = sort__parent_print, |
750 | .width = &parent_symbol__col_width, | ||
685 | }; | 751 | }; |
686 | 752 | ||
687 | static int sort__need_collapse = 0; | 753 | static int sort__need_collapse = 0; |
@@ -705,7 +771,7 @@ static LIST_HEAD(hist_entry__sort_list); | |||
705 | 771 | ||
706 | static int sort_dimension__add(char *tok) | 772 | static int sort_dimension__add(char *tok) |
707 | { | 773 | { |
708 | int i; | 774 | unsigned int i; |
709 | 775 | ||
710 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | 776 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { |
711 | struct sort_dimension *sd = &sort_dimensions[i]; | 777 | struct sort_dimension *sd = &sort_dimensions[i]; |
@@ -775,8 +841,109 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
775 | return cmp; | 841 | return cmp; |
776 | } | 842 | } |
777 | 843 | ||
844 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask) | ||
845 | { | ||
846 | int i; | ||
847 | size_t ret = 0; | ||
848 | |||
849 | ret += fprintf(fp, "%s", " "); | ||
850 | |||
851 | for (i = 0; i < depth; i++) | ||
852 | if (depth_mask & (1 << i)) | ||
853 | ret += fprintf(fp, "| "); | ||
854 | else | ||
855 | ret += fprintf(fp, " "); | ||
856 | |||
857 | ret += fprintf(fp, "\n"); | ||
858 | |||
859 | return ret; | ||
860 | } | ||
778 | static size_t | 861 | static size_t |
779 | callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples) | 862 | ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, |
863 | int depth_mask, int count, u64 total_samples, | ||
864 | int hits) | ||
865 | { | ||
866 | int i; | ||
867 | size_t ret = 0; | ||
868 | |||
869 | ret += fprintf(fp, "%s", " "); | ||
870 | for (i = 0; i < depth; i++) { | ||
871 | if (depth_mask & (1 << i)) | ||
872 | ret += fprintf(fp, "|"); | ||
873 | else | ||
874 | ret += fprintf(fp, " "); | ||
875 | if (!count && i == depth - 1) { | ||
876 | double percent; | ||
877 | |||
878 | percent = hits * 100.0 / total_samples; | ||
879 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); | ||
880 | } else | ||
881 | ret += fprintf(fp, "%s", " "); | ||
882 | } | ||
883 | if (chain->sym) | ||
884 | ret += fprintf(fp, "%s\n", chain->sym->name); | ||
885 | else | ||
886 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | ||
887 | |||
888 | return ret; | ||
889 | } | ||
890 | |||
891 | static size_t | ||
892 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
893 | u64 total_samples, int depth, int depth_mask) | ||
894 | { | ||
895 | struct rb_node *node, *next; | ||
896 | struct callchain_node *child; | ||
897 | struct callchain_list *chain; | ||
898 | int new_depth_mask = depth_mask; | ||
899 | u64 new_total; | ||
900 | size_t ret = 0; | ||
901 | int i; | ||
902 | |||
903 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
904 | new_total = self->cumul_hit; | ||
905 | else | ||
906 | new_total = total_samples; | ||
907 | |||
908 | node = rb_first(&self->rb_root); | ||
909 | while (node) { | ||
910 | child = rb_entry(node, struct callchain_node, rb_node); | ||
911 | |||
912 | /* | ||
913 | * The depth mask manages the output of pipes that show | ||
914 | * the depth. We don't want to keep the pipes of the current | ||
915 | * level for the last child of this depth | ||
916 | */ | ||
917 | next = rb_next(node); | ||
918 | if (!next) | ||
919 | new_depth_mask &= ~(1 << (depth - 1)); | ||
920 | |||
921 | /* | ||
922 | * But we keep the older depth mask for the line seperator | ||
923 | * to keep the level link until we reach the last child | ||
924 | */ | ||
925 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask); | ||
926 | i = 0; | ||
927 | list_for_each_entry(chain, &child->val, list) { | ||
928 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
929 | continue; | ||
930 | ret += ipchain__fprintf_graph(fp, chain, depth, | ||
931 | new_depth_mask, i++, | ||
932 | new_total, | ||
933 | child->cumul_hit); | ||
934 | } | ||
935 | ret += callchain__fprintf_graph(fp, child, new_total, | ||
936 | depth + 1, | ||
937 | new_depth_mask | (1 << depth)); | ||
938 | node = next; | ||
939 | } | ||
940 | |||
941 | return ret; | ||
942 | } | ||
943 | |||
944 | static size_t | ||
945 | callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | ||
946 | u64 total_samples) | ||
780 | { | 947 | { |
781 | struct callchain_list *chain; | 948 | struct callchain_list *chain; |
782 | size_t ret = 0; | 949 | size_t ret = 0; |
@@ -784,11 +951,18 @@ callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples) | |||
784 | if (!self) | 951 | if (!self) |
785 | return 0; | 952 | return 0; |
786 | 953 | ||
787 | ret += callchain__fprintf(fp, self->parent, total_samples); | 954 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); |
788 | 955 | ||
789 | 956 | ||
790 | list_for_each_entry(chain, &self->val, list) | 957 | list_for_each_entry(chain, &self->val, list) { |
791 | ret += fprintf(fp, " %p\n", (void *)chain->ip); | 958 | if (chain->ip >= PERF_CONTEXT_MAX) |
959 | continue; | ||
960 | if (chain->sym) | ||
961 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
962 | else | ||
963 | ret += fprintf(fp, " %p\n", | ||
964 | (void *)(long)chain->ip); | ||
965 | } | ||
792 | 966 | ||
793 | return ret; | 967 | return ret; |
794 | } | 968 | } |
@@ -807,8 +981,19 @@ hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
807 | 981 | ||
808 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | 982 | chain = rb_entry(rb_node, struct callchain_node, rb_node); |
809 | percent = chain->hit * 100.0 / total_samples; | 983 | percent = chain->hit * 100.0 / total_samples; |
810 | ret += fprintf(fp, " %6.2f%%\n", percent); | 984 | switch (callchain_param.mode) { |
811 | ret += callchain__fprintf(fp, chain, total_samples); | 985 | case CHAIN_FLAT: |
986 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | ||
987 | percent); | ||
988 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
989 | break; | ||
990 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
991 | case CHAIN_GRAPH_REL: | ||
992 | ret += callchain__fprintf_graph(fp, chain, | ||
993 | total_samples, 1, 1); | ||
994 | default: | ||
995 | break; | ||
996 | } | ||
812 | ret += fprintf(fp, "\n"); | 997 | ret += fprintf(fp, "\n"); |
813 | rb_node = rb_next(rb_node); | 998 | rb_node = rb_next(rb_node); |
814 | } | 999 | } |
@@ -826,33 +1011,26 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | |||
826 | if (exclude_other && !self->parent) | 1011 | if (exclude_other && !self->parent) |
827 | return 0; | 1012 | return 0; |
828 | 1013 | ||
829 | if (total_samples) { | 1014 | if (total_samples) |
830 | double percent = self->count * 100.0 / total_samples; | 1015 | ret = percent_color_fprintf(fp, |
831 | char *color = PERF_COLOR_NORMAL; | 1016 | field_sep ? "%.2f" : " %6.2f%%", |
832 | 1017 | (self->count * 100.0) / total_samples); | |
833 | /* | 1018 | else |
834 | * We color high-overhead entries in red, mid-overhead | 1019 | ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count); |
835 | * entries in green - and keep the low overhead places | ||
836 | * normal: | ||
837 | */ | ||
838 | if (percent >= 5.0) { | ||
839 | color = PERF_COLOR_RED; | ||
840 | } else { | ||
841 | if (percent >= 0.5) | ||
842 | color = PERF_COLOR_GREEN; | ||
843 | } | ||
844 | 1020 | ||
845 | ret = color_fprintf(fp, color, " %6.2f%%", | 1021 | if (show_nr_samples) { |
846 | (self->count * 100.0) / total_samples); | 1022 | if (field_sep) |
847 | } else | 1023 | fprintf(fp, "%c%lld", *field_sep, self->count); |
848 | ret = fprintf(fp, "%12Ld ", self->count); | 1024 | else |
1025 | fprintf(fp, "%11lld", self->count); | ||
1026 | } | ||
849 | 1027 | ||
850 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1028 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
851 | if (exclude_other && (se == &sort_parent)) | 1029 | if (se->elide) |
852 | continue; | 1030 | continue; |
853 | 1031 | ||
854 | fprintf(fp, " "); | 1032 | fprintf(fp, "%s", field_sep ?: " "); |
855 | ret += se->print(fp, self); | 1033 | ret += se->print(fp, self, se->width ? *se->width : 0); |
856 | } | 1034 | } |
857 | 1035 | ||
858 | ret += fprintf(fp, "\n"); | 1036 | ret += fprintf(fp, "\n"); |
@@ -867,6 +1045,18 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | |||
867 | * | 1045 | * |
868 | */ | 1046 | */ |
869 | 1047 | ||
1048 | static void dso__calc_col_width(struct dso *self) | ||
1049 | { | ||
1050 | if (!col_width_list_str && !field_sep && | ||
1051 | (!dso_list || strlist__has_entry(dso_list, self->name))) { | ||
1052 | unsigned int slen = strlen(self->name); | ||
1053 | if (slen > dsos__col_width) | ||
1054 | dsos__col_width = slen; | ||
1055 | } | ||
1056 | |||
1057 | self->slen_calculated = 1; | ||
1058 | } | ||
1059 | |||
870 | static struct symbol * | 1060 | static struct symbol * |
871 | resolve_symbol(struct thread *thread, struct map **mapp, | 1061 | resolve_symbol(struct thread *thread, struct map **mapp, |
872 | struct dso **dsop, u64 *ipp) | 1062 | struct dso **dsop, u64 *ipp) |
@@ -886,6 +1076,14 @@ resolve_symbol(struct thread *thread, struct map **mapp, | |||
886 | 1076 | ||
887 | map = thread__find_map(thread, ip); | 1077 | map = thread__find_map(thread, ip); |
888 | if (map != NULL) { | 1078 | if (map != NULL) { |
1079 | /* | ||
1080 | * We have to do this here as we may have a dso | ||
1081 | * with no symbol hit that has a name longer than | ||
1082 | * the ones with symbols sampled. | ||
1083 | */ | ||
1084 | if (!sort_dso.elide && !map->dso->slen_calculated) | ||
1085 | dso__calc_col_width(map->dso); | ||
1086 | |||
889 | if (mapp) | 1087 | if (mapp) |
890 | *mapp = map; | 1088 | *mapp = map; |
891 | got_map: | 1089 | got_map: |
@@ -923,6 +1121,58 @@ static int call__match(struct symbol *sym) | |||
923 | return 0; | 1121 | return 0; |
924 | } | 1122 | } |
925 | 1123 | ||
1124 | static struct symbol ** | ||
1125 | resolve_callchain(struct thread *thread, struct map *map __used, | ||
1126 | struct ip_callchain *chain, struct hist_entry *entry) | ||
1127 | { | ||
1128 | u64 context = PERF_CONTEXT_MAX; | ||
1129 | struct symbol **syms = NULL; | ||
1130 | unsigned int i; | ||
1131 | |||
1132 | if (callchain) { | ||
1133 | syms = calloc(chain->nr, sizeof(*syms)); | ||
1134 | if (!syms) { | ||
1135 | fprintf(stderr, "Can't allocate memory for symbols\n"); | ||
1136 | exit(-1); | ||
1137 | } | ||
1138 | } | ||
1139 | |||
1140 | for (i = 0; i < chain->nr; i++) { | ||
1141 | u64 ip = chain->ips[i]; | ||
1142 | struct dso *dso = NULL; | ||
1143 | struct symbol *sym; | ||
1144 | |||
1145 | if (ip >= PERF_CONTEXT_MAX) { | ||
1146 | context = ip; | ||
1147 | continue; | ||
1148 | } | ||
1149 | |||
1150 | switch (context) { | ||
1151 | case PERF_CONTEXT_HV: | ||
1152 | dso = hypervisor_dso; | ||
1153 | break; | ||
1154 | case PERF_CONTEXT_KERNEL: | ||
1155 | dso = kernel_dso; | ||
1156 | break; | ||
1157 | default: | ||
1158 | break; | ||
1159 | } | ||
1160 | |||
1161 | sym = resolve_symbol(thread, NULL, &dso, &ip); | ||
1162 | |||
1163 | if (sym) { | ||
1164 | if (sort__has_parent && call__match(sym) && | ||
1165 | !entry->parent) | ||
1166 | entry->parent = sym; | ||
1167 | if (!callchain) | ||
1168 | break; | ||
1169 | syms[i] = sym; | ||
1170 | } | ||
1171 | } | ||
1172 | |||
1173 | return syms; | ||
1174 | } | ||
1175 | |||
926 | /* | 1176 | /* |
927 | * collect histogram counts | 1177 | * collect histogram counts |
928 | */ | 1178 | */ |
@@ -935,6 +1185,7 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
935 | struct rb_node **p = &hist.rb_node; | 1185 | struct rb_node **p = &hist.rb_node; |
936 | struct rb_node *parent = NULL; | 1186 | struct rb_node *parent = NULL; |
937 | struct hist_entry *he; | 1187 | struct hist_entry *he; |
1188 | struct symbol **syms = NULL; | ||
938 | struct hist_entry entry = { | 1189 | struct hist_entry entry = { |
939 | .thread = thread, | 1190 | .thread = thread, |
940 | .map = map, | 1191 | .map = map, |
@@ -948,36 +1199,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
948 | }; | 1199 | }; |
949 | int cmp; | 1200 | int cmp; |
950 | 1201 | ||
951 | if (sort__has_parent && chain) { | 1202 | if ((sort__has_parent || callchain) && chain) |
952 | u64 context = PERF_CONTEXT_MAX; | 1203 | syms = resolve_callchain(thread, map, chain, &entry); |
953 | int i; | ||
954 | |||
955 | for (i = 0; i < chain->nr; i++) { | ||
956 | u64 ip = chain->ips[i]; | ||
957 | struct dso *dso = NULL; | ||
958 | struct symbol *sym; | ||
959 | |||
960 | if (ip >= PERF_CONTEXT_MAX) { | ||
961 | context = ip; | ||
962 | continue; | ||
963 | } | ||
964 | |||
965 | switch (context) { | ||
966 | case PERF_CONTEXT_KERNEL: | ||
967 | dso = kernel_dso; | ||
968 | break; | ||
969 | default: | ||
970 | break; | ||
971 | } | ||
972 | |||
973 | sym = resolve_symbol(thread, NULL, &dso, &ip); | ||
974 | |||
975 | if (sym && call__match(sym)) { | ||
976 | entry.parent = sym; | ||
977 | break; | ||
978 | } | ||
979 | } | ||
980 | } | ||
981 | 1204 | ||
982 | while (*p != NULL) { | 1205 | while (*p != NULL) { |
983 | parent = *p; | 1206 | parent = *p; |
@@ -987,8 +1210,10 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
987 | 1210 | ||
988 | if (!cmp) { | 1211 | if (!cmp) { |
989 | he->count += count; | 1212 | he->count += count; |
990 | if (callchain) | 1213 | if (callchain) { |
991 | append_chain(&he->callchain, chain); | 1214 | append_chain(&he->callchain, chain, syms); |
1215 | free(syms); | ||
1216 | } | ||
992 | return 0; | 1217 | return 0; |
993 | } | 1218 | } |
994 | 1219 | ||
@@ -1004,7 +1229,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
1004 | *he = entry; | 1229 | *he = entry; |
1005 | if (callchain) { | 1230 | if (callchain) { |
1006 | callchain_init(&he->callchain); | 1231 | callchain_init(&he->callchain); |
1007 | append_chain(&he->callchain, chain); | 1232 | append_chain(&he->callchain, chain, syms); |
1233 | free(syms); | ||
1008 | } | 1234 | } |
1009 | rb_link_node(&he->rb_node, parent, p); | 1235 | rb_link_node(&he->rb_node, parent, p); |
1010 | rb_insert_color(&he->rb_node, &hist); | 1236 | rb_insert_color(&he->rb_node, &hist); |
@@ -1076,14 +1302,15 @@ static void collapse__resort(void) | |||
1076 | 1302 | ||
1077 | static struct rb_root output_hists; | 1303 | static struct rb_root output_hists; |
1078 | 1304 | ||
1079 | static void output__insert_entry(struct hist_entry *he) | 1305 | static void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits) |
1080 | { | 1306 | { |
1081 | struct rb_node **p = &output_hists.rb_node; | 1307 | struct rb_node **p = &output_hists.rb_node; |
1082 | struct rb_node *parent = NULL; | 1308 | struct rb_node *parent = NULL; |
1083 | struct hist_entry *iter; | 1309 | struct hist_entry *iter; |
1084 | 1310 | ||
1085 | if (callchain) | 1311 | if (callchain) |
1086 | sort_chain_to_rbtree(&he->sorted_chain, &he->callchain); | 1312 | callchain_param.sort(&he->sorted_chain, &he->callchain, |
1313 | min_callchain_hits, &callchain_param); | ||
1087 | 1314 | ||
1088 | while (*p != NULL) { | 1315 | while (*p != NULL) { |
1089 | parent = *p; | 1316 | parent = *p; |
@@ -1099,11 +1326,14 @@ static void output__insert_entry(struct hist_entry *he) | |||
1099 | rb_insert_color(&he->rb_node, &output_hists); | 1326 | rb_insert_color(&he->rb_node, &output_hists); |
1100 | } | 1327 | } |
1101 | 1328 | ||
1102 | static void output__resort(void) | 1329 | static void output__resort(u64 total_samples) |
1103 | { | 1330 | { |
1104 | struct rb_node *next; | 1331 | struct rb_node *next; |
1105 | struct hist_entry *n; | 1332 | struct hist_entry *n; |
1106 | struct rb_root *tree = &hist; | 1333 | struct rb_root *tree = &hist; |
1334 | u64 min_callchain_hits; | ||
1335 | |||
1336 | min_callchain_hits = total_samples * (callchain_param.min_percent / 100); | ||
1107 | 1337 | ||
1108 | if (sort__need_collapse) | 1338 | if (sort__need_collapse) |
1109 | tree = &collapse_hists; | 1339 | tree = &collapse_hists; |
@@ -1115,7 +1345,7 @@ static void output__resort(void) | |||
1115 | next = rb_next(&n->rb_node); | 1345 | next = rb_next(&n->rb_node); |
1116 | 1346 | ||
1117 | rb_erase(&n->rb_node, tree); | 1347 | rb_erase(&n->rb_node, tree); |
1118 | output__insert_entry(n); | 1348 | output__insert_entry(n, min_callchain_hits); |
1119 | } | 1349 | } |
1120 | } | 1350 | } |
1121 | 1351 | ||
@@ -1125,35 +1355,67 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
1125 | struct sort_entry *se; | 1355 | struct sort_entry *se; |
1126 | struct rb_node *nd; | 1356 | struct rb_node *nd; |
1127 | size_t ret = 0; | 1357 | size_t ret = 0; |
1358 | unsigned int width; | ||
1359 | char *col_width = col_width_list_str; | ||
1128 | 1360 | ||
1129 | fprintf(fp, "\n"); | 1361 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); |
1130 | fprintf(fp, "#\n"); | ||
1131 | fprintf(fp, "# (%Ld samples)\n", (u64)total_samples); | ||
1132 | fprintf(fp, "#\n"); | 1362 | fprintf(fp, "#\n"); |
1133 | 1363 | ||
1134 | fprintf(fp, "# Overhead"); | 1364 | fprintf(fp, "# Overhead"); |
1365 | if (show_nr_samples) { | ||
1366 | if (field_sep) | ||
1367 | fprintf(fp, "%cSamples", *field_sep); | ||
1368 | else | ||
1369 | fputs(" Samples ", fp); | ||
1370 | } | ||
1135 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1371 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
1136 | if (exclude_other && (se == &sort_parent)) | 1372 | if (se->elide) |
1373 | continue; | ||
1374 | if (field_sep) { | ||
1375 | fprintf(fp, "%c%s", *field_sep, se->header); | ||
1137 | continue; | 1376 | continue; |
1138 | fprintf(fp, " %s", se->header); | 1377 | } |
1378 | width = strlen(se->header); | ||
1379 | if (se->width) { | ||
1380 | if (col_width_list_str) { | ||
1381 | if (col_width) { | ||
1382 | *se->width = atoi(col_width); | ||
1383 | col_width = strchr(col_width, ','); | ||
1384 | if (col_width) | ||
1385 | ++col_width; | ||
1386 | } | ||
1387 | } | ||
1388 | width = *se->width = max(*se->width, width); | ||
1389 | } | ||
1390 | fprintf(fp, " %*s", width, se->header); | ||
1139 | } | 1391 | } |
1140 | fprintf(fp, "\n"); | 1392 | fprintf(fp, "\n"); |
1141 | 1393 | ||
1394 | if (field_sep) | ||
1395 | goto print_entries; | ||
1396 | |||
1142 | fprintf(fp, "# ........"); | 1397 | fprintf(fp, "# ........"); |
1398 | if (show_nr_samples) | ||
1399 | fprintf(fp, " .........."); | ||
1143 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1400 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
1144 | int i; | 1401 | unsigned int i; |
1145 | 1402 | ||
1146 | if (exclude_other && (se == &sort_parent)) | 1403 | if (se->elide) |
1147 | continue; | 1404 | continue; |
1148 | 1405 | ||
1149 | fprintf(fp, " "); | 1406 | fprintf(fp, " "); |
1150 | for (i = 0; i < strlen(se->header); i++) | 1407 | if (se->width) |
1408 | width = *se->width; | ||
1409 | else | ||
1410 | width = strlen(se->header); | ||
1411 | for (i = 0; i < width; i++) | ||
1151 | fprintf(fp, "."); | 1412 | fprintf(fp, "."); |
1152 | } | 1413 | } |
1153 | fprintf(fp, "\n"); | 1414 | fprintf(fp, "\n"); |
1154 | 1415 | ||
1155 | fprintf(fp, "#\n"); | 1416 | fprintf(fp, "#\n"); |
1156 | 1417 | ||
1418 | print_entries: | ||
1157 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { | 1419 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { |
1158 | pos = rb_entry(nd, struct hist_entry, rb_node); | 1420 | pos = rb_entry(nd, struct hist_entry, rb_node); |
1159 | ret += hist_entry__fprintf(fp, pos, total_samples); | 1421 | ret += hist_entry__fprintf(fp, pos, total_samples); |
@@ -1162,7 +1424,7 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
1162 | if (sort_order == default_sort_order && | 1424 | if (sort_order == default_sort_order && |
1163 | parent_pattern == default_parent_pattern) { | 1425 | parent_pattern == default_parent_pattern) { |
1164 | fprintf(fp, "#\n"); | 1426 | fprintf(fp, "#\n"); |
1165 | fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n"); | 1427 | fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n"); |
1166 | fprintf(fp, "#\n"); | 1428 | fprintf(fp, "#\n"); |
1167 | } | 1429 | } |
1168 | fprintf(fp, "\n"); | 1430 | fprintf(fp, "\n"); |
@@ -1213,6 +1475,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1213 | struct map *map = NULL; | 1475 | struct map *map = NULL; |
1214 | void *more_data = event->ip.__more_data; | 1476 | void *more_data = event->ip.__more_data; |
1215 | struct ip_callchain *chain = NULL; | 1477 | struct ip_callchain *chain = NULL; |
1478 | int cpumode; | ||
1216 | 1479 | ||
1217 | if (sample_type & PERF_SAMPLE_PERIOD) { | 1480 | if (sample_type & PERF_SAMPLE_PERIOD) { |
1218 | period = *(u64 *)more_data; | 1481 | period = *(u64 *)more_data; |
@@ -1228,7 +1491,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1228 | (long long)period); | 1491 | (long long)period); |
1229 | 1492 | ||
1230 | if (sample_type & PERF_SAMPLE_CALLCHAIN) { | 1493 | if (sample_type & PERF_SAMPLE_CALLCHAIN) { |
1231 | int i; | 1494 | unsigned int i; |
1232 | 1495 | ||
1233 | chain = (void *)more_data; | 1496 | chain = (void *)more_data; |
1234 | 1497 | ||
@@ -1256,7 +1519,9 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1256 | if (comm_list && !strlist__has_entry(comm_list, thread->comm)) | 1519 | if (comm_list && !strlist__has_entry(comm_list, thread->comm)) |
1257 | return 0; | 1520 | return 0; |
1258 | 1521 | ||
1259 | if (event->header.misc & PERF_EVENT_MISC_KERNEL) { | 1522 | cpumode = event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK; |
1523 | |||
1524 | if (cpumode == PERF_EVENT_MISC_KERNEL) { | ||
1260 | show = SHOW_KERNEL; | 1525 | show = SHOW_KERNEL; |
1261 | level = 'k'; | 1526 | level = 'k'; |
1262 | 1527 | ||
@@ -1264,7 +1529,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1264 | 1529 | ||
1265 | dprintf(" ...... dso: %s\n", dso->name); | 1530 | dprintf(" ...... dso: %s\n", dso->name); |
1266 | 1531 | ||
1267 | } else if (event->header.misc & PERF_EVENT_MISC_USER) { | 1532 | } else if (cpumode == PERF_EVENT_MISC_USER) { |
1268 | 1533 | ||
1269 | show = SHOW_USER; | 1534 | show = SHOW_USER; |
1270 | level = '.'; | 1535 | level = '.'; |
@@ -1272,6 +1537,9 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1272 | } else { | 1537 | } else { |
1273 | show = SHOW_HV; | 1538 | show = SHOW_HV; |
1274 | level = 'H'; | 1539 | level = 'H'; |
1540 | |||
1541 | dso = hypervisor_dso; | ||
1542 | |||
1275 | dprintf(" ...... dso: [hypervisor]\n"); | 1543 | dprintf(" ...... dso: [hypervisor]\n"); |
1276 | } | 1544 | } |
1277 | 1545 | ||
@@ -1341,15 +1609,27 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head) | |||
1341 | } | 1609 | } |
1342 | 1610 | ||
1343 | static int | 1611 | static int |
1344 | process_fork_event(event_t *event, unsigned long offset, unsigned long head) | 1612 | process_task_event(event_t *event, unsigned long offset, unsigned long head) |
1345 | { | 1613 | { |
1346 | struct thread *thread = threads__findnew(event->fork.pid); | 1614 | struct thread *thread = threads__findnew(event->fork.pid); |
1347 | struct thread *parent = threads__findnew(event->fork.ppid); | 1615 | struct thread *parent = threads__findnew(event->fork.ppid); |
1348 | 1616 | ||
1349 | dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n", | 1617 | dprintf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n", |
1350 | (void *)(offset + head), | 1618 | (void *)(offset + head), |
1351 | (void *)(long)(event->header.size), | 1619 | (void *)(long)(event->header.size), |
1352 | event->fork.pid, event->fork.ppid); | 1620 | event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT", |
1621 | event->fork.pid, event->fork.tid, | ||
1622 | event->fork.ppid, event->fork.ptid); | ||
1623 | |||
1624 | /* | ||
1625 | * A thread clone will have the same PID for both | ||
1626 | * parent and child. | ||
1627 | */ | ||
1628 | if (thread == parent) | ||
1629 | return 0; | ||
1630 | |||
1631 | if (event->header.type == PERF_EVENT_EXIT) | ||
1632 | return 0; | ||
1353 | 1633 | ||
1354 | if (!thread || !parent || thread__fork(thread, parent)) { | 1634 | if (!thread || !parent || thread__fork(thread, parent)) { |
1355 | dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); | 1635 | dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); |
@@ -1361,19 +1641,6 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head) | |||
1361 | } | 1641 | } |
1362 | 1642 | ||
1363 | static int | 1643 | static int |
1364 | process_period_event(event_t *event, unsigned long offset, unsigned long head) | ||
1365 | { | ||
1366 | dprintf("%p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld\n", | ||
1367 | (void *)(offset + head), | ||
1368 | (void *)(long)(event->header.size), | ||
1369 | event->period.time, | ||
1370 | event->period.id, | ||
1371 | event->period.sample_period); | ||
1372 | |||
1373 | return 0; | ||
1374 | } | ||
1375 | |||
1376 | static int | ||
1377 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) | 1644 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) |
1378 | { | 1645 | { |
1379 | dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", | 1646 | dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", |
@@ -1452,10 +1719,8 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
1452 | return process_comm_event(event, offset, head); | 1719 | return process_comm_event(event, offset, head); |
1453 | 1720 | ||
1454 | case PERF_EVENT_FORK: | 1721 | case PERF_EVENT_FORK: |
1455 | return process_fork_event(event, offset, head); | 1722 | case PERF_EVENT_EXIT: |
1456 | 1723 | return process_task_event(event, offset, head); | |
1457 | case PERF_EVENT_PERIOD: | ||
1458 | return process_period_event(event, offset, head); | ||
1459 | 1724 | ||
1460 | case PERF_EVENT_LOST: | 1725 | case PERF_EVENT_LOST: |
1461 | return process_lost_event(event, offset, head); | 1726 | return process_lost_event(event, offset, head); |
@@ -1534,9 +1799,19 @@ static int __cmd_report(void) | |||
1534 | 1799 | ||
1535 | sample_type = perf_header__sample_type(); | 1800 | sample_type = perf_header__sample_type(); |
1536 | 1801 | ||
1537 | if (sort__has_parent && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { | 1802 | if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) { |
1538 | fprintf(stderr, "selected --sort parent, but no callchain data\n"); | 1803 | if (sort__has_parent) { |
1539 | exit(-1); | 1804 | fprintf(stderr, "selected --sort parent, but no" |
1805 | " callchain data. Did you call" | ||
1806 | " perf record without -g?\n"); | ||
1807 | exit(-1); | ||
1808 | } | ||
1809 | if (callchain) { | ||
1810 | fprintf(stderr, "selected -c but no callchain data." | ||
1811 | " Did you call perf record without" | ||
1812 | " -g?\n"); | ||
1813 | exit(-1); | ||
1814 | } | ||
1540 | } | 1815 | } |
1541 | 1816 | ||
1542 | if (load_kernel() < 0) { | 1817 | if (load_kernel() < 0) { |
@@ -1619,7 +1894,7 @@ more: | |||
1619 | if (offset + head >= header->data_offset + header->data_size) | 1894 | if (offset + head >= header->data_offset + header->data_size) |
1620 | goto done; | 1895 | goto done; |
1621 | 1896 | ||
1622 | if (offset + head < stat.st_size) | 1897 | if (offset + head < (unsigned long)stat.st_size) |
1623 | goto more; | 1898 | goto more; |
1624 | 1899 | ||
1625 | done: | 1900 | done: |
@@ -1643,12 +1918,58 @@ done: | |||
1643 | dsos__fprintf(stdout); | 1918 | dsos__fprintf(stdout); |
1644 | 1919 | ||
1645 | collapse__resort(); | 1920 | collapse__resort(); |
1646 | output__resort(); | 1921 | output__resort(total); |
1647 | output__fprintf(stdout, total); | 1922 | output__fprintf(stdout, total); |
1648 | 1923 | ||
1649 | return rc; | 1924 | return rc; |
1650 | } | 1925 | } |
1651 | 1926 | ||
1927 | static int | ||
1928 | parse_callchain_opt(const struct option *opt __used, const char *arg, | ||
1929 | int unset __used) | ||
1930 | { | ||
1931 | char *tok; | ||
1932 | char *endptr; | ||
1933 | |||
1934 | callchain = 1; | ||
1935 | |||
1936 | if (!arg) | ||
1937 | return 0; | ||
1938 | |||
1939 | tok = strtok((char *)arg, ","); | ||
1940 | if (!tok) | ||
1941 | return -1; | ||
1942 | |||
1943 | /* get the output mode */ | ||
1944 | if (!strncmp(tok, "graph", strlen(arg))) | ||
1945 | callchain_param.mode = CHAIN_GRAPH_ABS; | ||
1946 | |||
1947 | else if (!strncmp(tok, "flat", strlen(arg))) | ||
1948 | callchain_param.mode = CHAIN_FLAT; | ||
1949 | |||
1950 | else if (!strncmp(tok, "fractal", strlen(arg))) | ||
1951 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
1952 | |||
1953 | else | ||
1954 | return -1; | ||
1955 | |||
1956 | /* get the min percentage */ | ||
1957 | tok = strtok(NULL, ","); | ||
1958 | if (!tok) | ||
1959 | goto setup; | ||
1960 | |||
1961 | callchain_param.min_percent = strtod(tok, &endptr); | ||
1962 | if (tok == endptr) | ||
1963 | return -1; | ||
1964 | |||
1965 | setup: | ||
1966 | if (register_callchain_param(&callchain_param) < 0) { | ||
1967 | fprintf(stderr, "Can't register callchain params\n"); | ||
1968 | return -1; | ||
1969 | } | ||
1970 | return 0; | ||
1971 | } | ||
1972 | |||
1652 | static const char * const report_usage[] = { | 1973 | static const char * const report_usage[] = { |
1653 | "perf report [<options>] <command>", | 1974 | "perf report [<options>] <command>", |
1654 | NULL | 1975 | NULL |
@@ -1662,6 +1983,10 @@ static const struct option options[] = { | |||
1662 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 1983 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1663 | "dump raw trace in ASCII"), | 1984 | "dump raw trace in ASCII"), |
1664 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | 1985 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), |
1986 | OPT_BOOLEAN('m', "modules", &modules, | ||
1987 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | ||
1988 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, | ||
1989 | "Show a column with the number of samples"), | ||
1665 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 1990 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
1666 | "sort by key(s): pid, comm, dso, symbol, parent"), | 1991 | "sort by key(s): pid, comm, dso, symbol, parent"), |
1667 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 1992 | OPT_BOOLEAN('P', "full-paths", &full_paths, |
@@ -1670,13 +1995,21 @@ static const struct option options[] = { | |||
1670 | "regex filter to identify parent, see: '--sort parent'"), | 1995 | "regex filter to identify parent, see: '--sort parent'"), |
1671 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, | 1996 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, |
1672 | "Only display entries with parent-match"), | 1997 | "Only display entries with parent-match"), |
1673 | OPT_BOOLEAN('c', "callchain", &callchain, "Display callchains"), | 1998 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", |
1999 | "Display callchains using output_type and min percent threshold. " | ||
2000 | "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), | ||
1674 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", | 2001 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", |
1675 | "only consider symbols in these dsos"), | 2002 | "only consider symbols in these dsos"), |
1676 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", | 2003 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", |
1677 | "only consider symbols in these comms"), | 2004 | "only consider symbols in these comms"), |
1678 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", | 2005 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", |
1679 | "only consider these symbols"), | 2006 | "only consider these symbols"), |
2007 | OPT_STRING('w', "column-widths", &col_width_list_str, | ||
2008 | "width[,width...]", | ||
2009 | "don't try to adjust column width, use these fixed values"), | ||
2010 | OPT_STRING('t', "field-separator", &field_sep, "separator", | ||
2011 | "separator for columns, no spaces will be added between " | ||
2012 | "columns '.' is reserved."), | ||
1680 | OPT_END() | 2013 | OPT_END() |
1681 | }; | 2014 | }; |
1682 | 2015 | ||
@@ -1696,7 +2029,8 @@ static void setup_sorting(void) | |||
1696 | } | 2029 | } |
1697 | 2030 | ||
1698 | static void setup_list(struct strlist **list, const char *list_str, | 2031 | static void setup_list(struct strlist **list, const char *list_str, |
1699 | const char *list_name) | 2032 | struct sort_entry *se, const char *list_name, |
2033 | FILE *fp) | ||
1700 | { | 2034 | { |
1701 | if (list_str) { | 2035 | if (list_str) { |
1702 | *list = strlist__new(true, list_str); | 2036 | *list = strlist__new(true, list_str); |
@@ -1705,10 +2039,15 @@ static void setup_list(struct strlist **list, const char *list_str, | |||
1705 | list_name); | 2039 | list_name); |
1706 | exit(129); | 2040 | exit(129); |
1707 | } | 2041 | } |
2042 | if (strlist__nr_entries(*list) == 1) { | ||
2043 | fprintf(fp, "# %s: %s\n", list_name, | ||
2044 | strlist__entry(*list, 0)->s); | ||
2045 | se->elide = true; | ||
2046 | } | ||
1708 | } | 2047 | } |
1709 | } | 2048 | } |
1710 | 2049 | ||
1711 | int cmd_report(int argc, const char **argv, const char *prefix) | 2050 | int cmd_report(int argc, const char **argv, const char *prefix __used) |
1712 | { | 2051 | { |
1713 | symbol__init(); | 2052 | symbol__init(); |
1714 | 2053 | ||
@@ -1718,9 +2057,10 @@ int cmd_report(int argc, const char **argv, const char *prefix) | |||
1718 | 2057 | ||
1719 | setup_sorting(); | 2058 | setup_sorting(); |
1720 | 2059 | ||
1721 | if (parent_pattern != default_parent_pattern) | 2060 | if (parent_pattern != default_parent_pattern) { |
1722 | sort_dimension__add("parent"); | 2061 | sort_dimension__add("parent"); |
1723 | else | 2062 | sort_parent.elide = 1; |
2063 | } else | ||
1724 | exclude_other = 0; | 2064 | exclude_other = 0; |
1725 | 2065 | ||
1726 | /* | 2066 | /* |
@@ -1729,11 +2069,17 @@ int cmd_report(int argc, const char **argv, const char *prefix) | |||
1729 | if (argc) | 2069 | if (argc) |
1730 | usage_with_options(report_usage, options); | 2070 | usage_with_options(report_usage, options); |
1731 | 2071 | ||
1732 | setup_list(&dso_list, dso_list_str, "dso"); | ||
1733 | setup_list(&comm_list, comm_list_str, "comm"); | ||
1734 | setup_list(&sym_list, sym_list_str, "symbol"); | ||
1735 | |||
1736 | setup_pager(); | 2072 | setup_pager(); |
1737 | 2073 | ||
2074 | setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout); | ||
2075 | setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout); | ||
2076 | setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout); | ||
2077 | |||
2078 | if (field_sep && *field_sep == '.') { | ||
2079 | fputs("'.' is the only non valid --field-separator argument\n", | ||
2080 | stderr); | ||
2081 | exit(129); | ||
2082 | } | ||
2083 | |||
1738 | return __cmd_report(); | 2084 | return __cmd_report(); |
1739 | } | 2085 | } |