diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r-- | tools/perf/builtin-report.c | 751 |
1 files changed, 589 insertions, 162 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 135b7837e6bf..b53a60fc12de 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_REL, | ||
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 { |
@@ -104,7 +112,9 @@ struct read_event { | |||
104 | struct perf_event_header header; | 112 | struct perf_event_header header; |
105 | u32 pid,tid; | 113 | u32 pid,tid; |
106 | u64 value; | 114 | u64 value; |
107 | u64 format[3]; | 115 | u64 time_enabled; |
116 | u64 time_running; | ||
117 | u64 id; | ||
108 | }; | 118 | }; |
109 | 119 | ||
110 | typedef union event_union { | 120 | typedef union event_union { |
@@ -113,14 +123,41 @@ typedef union event_union { | |||
113 | struct mmap_event mmap; | 123 | struct mmap_event mmap; |
114 | struct comm_event comm; | 124 | struct comm_event comm; |
115 | struct fork_event fork; | 125 | struct fork_event fork; |
116 | struct period_event period; | ||
117 | struct lost_event lost; | 126 | struct lost_event lost; |
118 | struct read_event read; | 127 | struct read_event read; |
119 | } event_t; | 128 | } event_t; |
120 | 129 | ||
130 | static int repsep_fprintf(FILE *fp, const char *fmt, ...) | ||
131 | { | ||
132 | int n; | ||
133 | va_list ap; | ||
134 | |||
135 | va_start(ap, fmt); | ||
136 | if (!field_sep) | ||
137 | n = vfprintf(fp, fmt, ap); | ||
138 | else { | ||
139 | char *bf = NULL; | ||
140 | n = vasprintf(&bf, fmt, ap); | ||
141 | if (n > 0) { | ||
142 | char *sep = bf; | ||
143 | while (1) { | ||
144 | sep = strchr(sep, *field_sep); | ||
145 | if (sep == NULL) | ||
146 | break; | ||
147 | *sep = '.'; | ||
148 | } | ||
149 | } | ||
150 | fputs(bf, fp); | ||
151 | free(bf); | ||
152 | } | ||
153 | va_end(ap); | ||
154 | return n; | ||
155 | } | ||
156 | |||
121 | static LIST_HEAD(dsos); | 157 | static LIST_HEAD(dsos); |
122 | static struct dso *kernel_dso; | 158 | static struct dso *kernel_dso; |
123 | static struct dso *vdso; | 159 | static struct dso *vdso; |
160 | static struct dso *hypervisor_dso; | ||
124 | 161 | ||
125 | static void dsos__add(struct dso *dso) | 162 | static void dsos__add(struct dso *dso) |
126 | { | 163 | { |
@@ -176,7 +213,7 @@ static void dsos__fprintf(FILE *fp) | |||
176 | 213 | ||
177 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) | 214 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) |
178 | { | 215 | { |
179 | return dso__find_symbol(kernel_dso, ip); | 216 | return dso__find_symbol(dso, ip); |
180 | } | 217 | } |
181 | 218 | ||
182 | static int load_kernel(void) | 219 | static int load_kernel(void) |
@@ -187,8 +224,8 @@ static int load_kernel(void) | |||
187 | if (!kernel_dso) | 224 | if (!kernel_dso) |
188 | return -1; | 225 | return -1; |
189 | 226 | ||
190 | err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose); | 227 | err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose, modules); |
191 | if (err) { | 228 | if (err <= 0) { |
192 | dso__delete(kernel_dso); | 229 | dso__delete(kernel_dso); |
193 | kernel_dso = NULL; | 230 | kernel_dso = NULL; |
194 | } else | 231 | } else |
@@ -202,6 +239,11 @@ static int load_kernel(void) | |||
202 | 239 | ||
203 | dsos__add(vdso); | 240 | dsos__add(vdso); |
204 | 241 | ||
242 | hypervisor_dso = dso__new("[hypervisor]", 0); | ||
243 | if (!hypervisor_dso) | ||
244 | return -1; | ||
245 | dsos__add(hypervisor_dso); | ||
246 | |||
205 | return err; | 247 | return err; |
206 | } | 248 | } |
207 | 249 | ||
@@ -213,7 +255,7 @@ static int strcommon(const char *pathname) | |||
213 | { | 255 | { |
214 | int n = 0; | 256 | int n = 0; |
215 | 257 | ||
216 | while (pathname[n] == cwd[n] && n < cwdlen) | 258 | while (n < cwdlen && pathname[n] == cwd[n]) |
217 | ++n; | 259 | ++n; |
218 | 260 | ||
219 | return n; | 261 | return n; |
@@ -233,7 +275,7 @@ static u64 map__map_ip(struct map *map, u64 ip) | |||
233 | return ip - map->start + map->pgoff; | 275 | return ip - map->start + map->pgoff; |
234 | } | 276 | } |
235 | 277 | ||
236 | static u64 vdso__map_ip(struct map *map, u64 ip) | 278 | static u64 vdso__map_ip(struct map *map __used, u64 ip) |
237 | { | 279 | { |
238 | return ip; | 280 | return ip; |
239 | } | 281 | } |
@@ -343,12 +385,28 @@ static struct thread *thread__new(pid_t pid) | |||
343 | return self; | 385 | return self; |
344 | } | 386 | } |
345 | 387 | ||
388 | static unsigned int dsos__col_width, | ||
389 | comms__col_width, | ||
390 | threads__col_width; | ||
391 | |||
346 | static int thread__set_comm(struct thread *self, const char *comm) | 392 | static int thread__set_comm(struct thread *self, const char *comm) |
347 | { | 393 | { |
348 | if (self->comm) | 394 | if (self->comm) |
349 | free(self->comm); | 395 | free(self->comm); |
350 | self->comm = strdup(comm); | 396 | self->comm = strdup(comm); |
351 | return self->comm ? 0 : -ENOMEM; | 397 | if (!self->comm) |
398 | return -ENOMEM; | ||
399 | |||
400 | if (!col_width_list_str && !field_sep && | ||
401 | (!comm_list || strlist__has_entry(comm_list, comm))) { | ||
402 | unsigned int slen = strlen(comm); | ||
403 | if (slen > comms__col_width) { | ||
404 | comms__col_width = slen; | ||
405 | threads__col_width = slen + 6; | ||
406 | } | ||
407 | } | ||
408 | |||
409 | return 0; | ||
352 | } | 410 | } |
353 | 411 | ||
354 | static size_t thread__fprintf(struct thread *self, FILE *fp) | 412 | static size_t thread__fprintf(struct thread *self, FILE *fp) |
@@ -519,7 +577,9 @@ struct sort_entry { | |||
519 | 577 | ||
520 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); | 578 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); |
521 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); | 579 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); |
522 | size_t (*print)(FILE *fp, struct hist_entry *); | 580 | size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); |
581 | unsigned int *width; | ||
582 | bool elide; | ||
523 | }; | 583 | }; |
524 | 584 | ||
525 | static int64_t cmp_null(void *l, void *r) | 585 | static int64_t cmp_null(void *l, void *r) |
@@ -541,15 +601,17 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | |||
541 | } | 601 | } |
542 | 602 | ||
543 | static size_t | 603 | static size_t |
544 | sort__thread_print(FILE *fp, struct hist_entry *self) | 604 | sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) |
545 | { | 605 | { |
546 | return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid); | 606 | return repsep_fprintf(fp, "%*s:%5d", width - 6, |
607 | self->thread->comm ?: "", self->thread->pid); | ||
547 | } | 608 | } |
548 | 609 | ||
549 | static struct sort_entry sort_thread = { | 610 | static struct sort_entry sort_thread = { |
550 | .header = " Command: Pid", | 611 | .header = "Command: Pid", |
551 | .cmp = sort__thread_cmp, | 612 | .cmp = sort__thread_cmp, |
552 | .print = sort__thread_print, | 613 | .print = sort__thread_print, |
614 | .width = &threads__col_width, | ||
553 | }; | 615 | }; |
554 | 616 | ||
555 | /* --sort comm */ | 617 | /* --sort comm */ |
@@ -573,16 +635,17 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | |||
573 | } | 635 | } |
574 | 636 | ||
575 | static size_t | 637 | static size_t |
576 | sort__comm_print(FILE *fp, struct hist_entry *self) | 638 | sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) |
577 | { | 639 | { |
578 | return fprintf(fp, "%16s", self->thread->comm); | 640 | return repsep_fprintf(fp, "%*s", width, self->thread->comm); |
579 | } | 641 | } |
580 | 642 | ||
581 | static struct sort_entry sort_comm = { | 643 | static struct sort_entry sort_comm = { |
582 | .header = " Command", | 644 | .header = "Command", |
583 | .cmp = sort__comm_cmp, | 645 | .cmp = sort__comm_cmp, |
584 | .collapse = sort__comm_collapse, | 646 | .collapse = sort__comm_collapse, |
585 | .print = sort__comm_print, | 647 | .print = sort__comm_print, |
648 | .width = &comms__col_width, | ||
586 | }; | 649 | }; |
587 | 650 | ||
588 | /* --sort dso */ | 651 | /* --sort dso */ |
@@ -600,18 +663,19 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | |||
600 | } | 663 | } |
601 | 664 | ||
602 | static size_t | 665 | static size_t |
603 | sort__dso_print(FILE *fp, struct hist_entry *self) | 666 | sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) |
604 | { | 667 | { |
605 | if (self->dso) | 668 | if (self->dso) |
606 | return fprintf(fp, "%-25s", self->dso->name); | 669 | return repsep_fprintf(fp, "%-*s", width, self->dso->name); |
607 | 670 | ||
608 | return fprintf(fp, "%016llx ", (u64)self->ip); | 671 | return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); |
609 | } | 672 | } |
610 | 673 | ||
611 | static struct sort_entry sort_dso = { | 674 | static struct sort_entry sort_dso = { |
612 | .header = "Shared Object ", | 675 | .header = "Shared Object", |
613 | .cmp = sort__dso_cmp, | 676 | .cmp = sort__dso_cmp, |
614 | .print = sort__dso_print, | 677 | .print = sort__dso_print, |
678 | .width = &dsos__col_width, | ||
615 | }; | 679 | }; |
616 | 680 | ||
617 | /* --sort symbol */ | 681 | /* --sort symbol */ |
@@ -631,18 +695,23 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | |||
631 | } | 695 | } |
632 | 696 | ||
633 | static size_t | 697 | static size_t |
634 | sort__sym_print(FILE *fp, struct hist_entry *self) | 698 | sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) |
635 | { | 699 | { |
636 | size_t ret = 0; | 700 | size_t ret = 0; |
637 | 701 | ||
638 | if (verbose) | 702 | if (verbose) |
639 | ret += fprintf(fp, "%#018llx ", (u64)self->ip); | 703 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, |
704 | dso__symtab_origin(self->dso)); | ||
640 | 705 | ||
706 | ret += repsep_fprintf(fp, "[%c] ", self->level); | ||
641 | if (self->sym) { | 707 | if (self->sym) { |
642 | ret += fprintf(fp, "[%c] %s", | 708 | ret += repsep_fprintf(fp, "%s", self->sym->name); |
643 | self->dso == kernel_dso ? 'k' : '.', self->sym->name); | 709 | |
710 | if (self->sym->module) | ||
711 | ret += repsep_fprintf(fp, "\t[%s]", | ||
712 | self->sym->module->name); | ||
644 | } else { | 713 | } else { |
645 | ret += fprintf(fp, "%#016llx", (u64)self->ip); | 714 | ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); |
646 | } | 715 | } |
647 | 716 | ||
648 | return ret; | 717 | return ret; |
@@ -669,19 +738,19 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | |||
669 | } | 738 | } |
670 | 739 | ||
671 | static size_t | 740 | static size_t |
672 | sort__parent_print(FILE *fp, struct hist_entry *self) | 741 | sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) |
673 | { | 742 | { |
674 | size_t ret = 0; | 743 | return repsep_fprintf(fp, "%-*s", width, |
675 | 744 | self->parent ? self->parent->name : "[other]"); | |
676 | ret += fprintf(fp, "%-20s", self->parent ? self->parent->name : "[other]"); | ||
677 | |||
678 | return ret; | ||
679 | } | 745 | } |
680 | 746 | ||
747 | static unsigned int parent_symbol__col_width; | ||
748 | |||
681 | static struct sort_entry sort_parent = { | 749 | static struct sort_entry sort_parent = { |
682 | .header = "Parent symbol ", | 750 | .header = "Parent symbol", |
683 | .cmp = sort__parent_cmp, | 751 | .cmp = sort__parent_cmp, |
684 | .print = sort__parent_print, | 752 | .print = sort__parent_print, |
753 | .width = &parent_symbol__col_width, | ||
685 | }; | 754 | }; |
686 | 755 | ||
687 | static int sort__need_collapse = 0; | 756 | static int sort__need_collapse = 0; |
@@ -705,7 +774,7 @@ static LIST_HEAD(hist_entry__sort_list); | |||
705 | 774 | ||
706 | static int sort_dimension__add(char *tok) | 775 | static int sort_dimension__add(char *tok) |
707 | { | 776 | { |
708 | int i; | 777 | unsigned int i; |
709 | 778 | ||
710 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | 779 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { |
711 | struct sort_dimension *sd = &sort_dimensions[i]; | 780 | struct sort_dimension *sd = &sort_dimensions[i]; |
@@ -775,8 +844,146 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
775 | return cmp; | 844 | return cmp; |
776 | } | 845 | } |
777 | 846 | ||
847 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask) | ||
848 | { | ||
849 | int i; | ||
850 | size_t ret = 0; | ||
851 | |||
852 | ret += fprintf(fp, "%s", " "); | ||
853 | |||
854 | for (i = 0; i < depth; i++) | ||
855 | if (depth_mask & (1 << i)) | ||
856 | ret += fprintf(fp, "| "); | ||
857 | else | ||
858 | ret += fprintf(fp, " "); | ||
859 | |||
860 | ret += fprintf(fp, "\n"); | ||
861 | |||
862 | return ret; | ||
863 | } | ||
778 | static size_t | 864 | static size_t |
779 | callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples) | 865 | ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, |
866 | int depth_mask, int count, u64 total_samples, | ||
867 | int hits) | ||
868 | { | ||
869 | int i; | ||
870 | size_t ret = 0; | ||
871 | |||
872 | ret += fprintf(fp, "%s", " "); | ||
873 | for (i = 0; i < depth; i++) { | ||
874 | if (depth_mask & (1 << i)) | ||
875 | ret += fprintf(fp, "|"); | ||
876 | else | ||
877 | ret += fprintf(fp, " "); | ||
878 | if (!count && i == depth - 1) { | ||
879 | double percent; | ||
880 | |||
881 | percent = hits * 100.0 / total_samples; | ||
882 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); | ||
883 | } else | ||
884 | ret += fprintf(fp, "%s", " "); | ||
885 | } | ||
886 | if (chain->sym) | ||
887 | ret += fprintf(fp, "%s\n", chain->sym->name); | ||
888 | else | ||
889 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | ||
890 | |||
891 | return ret; | ||
892 | } | ||
893 | |||
894 | static struct symbol *rem_sq_bracket; | ||
895 | static struct callchain_list rem_hits; | ||
896 | |||
897 | static void init_rem_hits(void) | ||
898 | { | ||
899 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
900 | if (!rem_sq_bracket) { | ||
901 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
902 | return; | ||
903 | } | ||
904 | |||
905 | strcpy(rem_sq_bracket->name, "[...]"); | ||
906 | rem_hits.sym = rem_sq_bracket; | ||
907 | } | ||
908 | |||
909 | static size_t | ||
910 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
911 | u64 total_samples, int depth, int depth_mask) | ||
912 | { | ||
913 | struct rb_node *node, *next; | ||
914 | struct callchain_node *child; | ||
915 | struct callchain_list *chain; | ||
916 | int new_depth_mask = depth_mask; | ||
917 | u64 new_total; | ||
918 | u64 remaining; | ||
919 | size_t ret = 0; | ||
920 | int i; | ||
921 | |||
922 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
923 | new_total = self->children_hit; | ||
924 | else | ||
925 | new_total = total_samples; | ||
926 | |||
927 | remaining = new_total; | ||
928 | |||
929 | node = rb_first(&self->rb_root); | ||
930 | while (node) { | ||
931 | u64 cumul; | ||
932 | |||
933 | child = rb_entry(node, struct callchain_node, rb_node); | ||
934 | cumul = cumul_hits(child); | ||
935 | remaining -= cumul; | ||
936 | |||
937 | /* | ||
938 | * The depth mask manages the output of pipes that show | ||
939 | * the depth. We don't want to keep the pipes of the current | ||
940 | * level for the last child of this depth. | ||
941 | * Except if we have remaining filtered hits. They will | ||
942 | * supersede the last child | ||
943 | */ | ||
944 | next = rb_next(node); | ||
945 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) | ||
946 | new_depth_mask &= ~(1 << (depth - 1)); | ||
947 | |||
948 | /* | ||
949 | * But we keep the older depth mask for the line seperator | ||
950 | * to keep the level link until we reach the last child | ||
951 | */ | ||
952 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask); | ||
953 | i = 0; | ||
954 | list_for_each_entry(chain, &child->val, list) { | ||
955 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
956 | continue; | ||
957 | ret += ipchain__fprintf_graph(fp, chain, depth, | ||
958 | new_depth_mask, i++, | ||
959 | new_total, | ||
960 | cumul); | ||
961 | } | ||
962 | ret += callchain__fprintf_graph(fp, child, new_total, | ||
963 | depth + 1, | ||
964 | new_depth_mask | (1 << depth)); | ||
965 | node = next; | ||
966 | } | ||
967 | |||
968 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
969 | remaining && remaining != new_total) { | ||
970 | |||
971 | if (!rem_sq_bracket) | ||
972 | return ret; | ||
973 | |||
974 | new_depth_mask &= ~(1 << (depth - 1)); | ||
975 | |||
976 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
977 | new_depth_mask, 0, new_total, | ||
978 | remaining); | ||
979 | } | ||
980 | |||
981 | return ret; | ||
982 | } | ||
983 | |||
984 | static size_t | ||
985 | callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | ||
986 | u64 total_samples) | ||
780 | { | 987 | { |
781 | struct callchain_list *chain; | 988 | struct callchain_list *chain; |
782 | size_t ret = 0; | 989 | size_t ret = 0; |
@@ -784,11 +991,18 @@ callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples) | |||
784 | if (!self) | 991 | if (!self) |
785 | return 0; | 992 | return 0; |
786 | 993 | ||
787 | ret += callchain__fprintf(fp, self->parent, total_samples); | 994 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); |
788 | 995 | ||
789 | 996 | ||
790 | list_for_each_entry(chain, &self->val, list) | 997 | list_for_each_entry(chain, &self->val, list) { |
791 | ret += fprintf(fp, " %p\n", (void *)chain->ip); | 998 | if (chain->ip >= PERF_CONTEXT_MAX) |
999 | continue; | ||
1000 | if (chain->sym) | ||
1001 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
1002 | else | ||
1003 | ret += fprintf(fp, " %p\n", | ||
1004 | (void *)(long)chain->ip); | ||
1005 | } | ||
792 | 1006 | ||
793 | return ret; | 1007 | return ret; |
794 | } | 1008 | } |
@@ -807,8 +1021,19 @@ hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
807 | 1021 | ||
808 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | 1022 | chain = rb_entry(rb_node, struct callchain_node, rb_node); |
809 | percent = chain->hit * 100.0 / total_samples; | 1023 | percent = chain->hit * 100.0 / total_samples; |
810 | ret += fprintf(fp, " %6.2f%%\n", percent); | 1024 | switch (callchain_param.mode) { |
811 | ret += callchain__fprintf(fp, chain, total_samples); | 1025 | case CHAIN_FLAT: |
1026 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | ||
1027 | percent); | ||
1028 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
1029 | break; | ||
1030 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
1031 | case CHAIN_GRAPH_REL: | ||
1032 | ret += callchain__fprintf_graph(fp, chain, | ||
1033 | total_samples, 1, 1); | ||
1034 | default: | ||
1035 | break; | ||
1036 | } | ||
812 | ret += fprintf(fp, "\n"); | 1037 | ret += fprintf(fp, "\n"); |
813 | rb_node = rb_next(rb_node); | 1038 | rb_node = rb_next(rb_node); |
814 | } | 1039 | } |
@@ -826,33 +1051,26 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | |||
826 | if (exclude_other && !self->parent) | 1051 | if (exclude_other && !self->parent) |
827 | return 0; | 1052 | return 0; |
828 | 1053 | ||
829 | if (total_samples) { | 1054 | if (total_samples) |
830 | double percent = self->count * 100.0 / total_samples; | 1055 | ret = percent_color_fprintf(fp, |
831 | char *color = PERF_COLOR_NORMAL; | 1056 | field_sep ? "%.2f" : " %6.2f%%", |
832 | 1057 | (self->count * 100.0) / total_samples); | |
833 | /* | 1058 | else |
834 | * We color high-overhead entries in red, mid-overhead | 1059 | 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 | 1060 | ||
845 | ret = color_fprintf(fp, color, " %6.2f%%", | 1061 | if (show_nr_samples) { |
846 | (self->count * 100.0) / total_samples); | 1062 | if (field_sep) |
847 | } else | 1063 | fprintf(fp, "%c%lld", *field_sep, self->count); |
848 | ret = fprintf(fp, "%12Ld ", self->count); | 1064 | else |
1065 | fprintf(fp, "%11lld", self->count); | ||
1066 | } | ||
849 | 1067 | ||
850 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1068 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
851 | if (exclude_other && (se == &sort_parent)) | 1069 | if (se->elide) |
852 | continue; | 1070 | continue; |
853 | 1071 | ||
854 | fprintf(fp, " "); | 1072 | fprintf(fp, "%s", field_sep ?: " "); |
855 | ret += se->print(fp, self); | 1073 | ret += se->print(fp, self, se->width ? *se->width : 0); |
856 | } | 1074 | } |
857 | 1075 | ||
858 | ret += fprintf(fp, "\n"); | 1076 | ret += fprintf(fp, "\n"); |
@@ -867,6 +1085,18 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | |||
867 | * | 1085 | * |
868 | */ | 1086 | */ |
869 | 1087 | ||
1088 | static void dso__calc_col_width(struct dso *self) | ||
1089 | { | ||
1090 | if (!col_width_list_str && !field_sep && | ||
1091 | (!dso_list || strlist__has_entry(dso_list, self->name))) { | ||
1092 | unsigned int slen = strlen(self->name); | ||
1093 | if (slen > dsos__col_width) | ||
1094 | dsos__col_width = slen; | ||
1095 | } | ||
1096 | |||
1097 | self->slen_calculated = 1; | ||
1098 | } | ||
1099 | |||
870 | static struct symbol * | 1100 | static struct symbol * |
871 | resolve_symbol(struct thread *thread, struct map **mapp, | 1101 | resolve_symbol(struct thread *thread, struct map **mapp, |
872 | struct dso **dsop, u64 *ipp) | 1102 | struct dso **dsop, u64 *ipp) |
@@ -886,6 +1116,14 @@ resolve_symbol(struct thread *thread, struct map **mapp, | |||
886 | 1116 | ||
887 | map = thread__find_map(thread, ip); | 1117 | map = thread__find_map(thread, ip); |
888 | if (map != NULL) { | 1118 | if (map != NULL) { |
1119 | /* | ||
1120 | * We have to do this here as we may have a dso | ||
1121 | * with no symbol hit that has a name longer than | ||
1122 | * the ones with symbols sampled. | ||
1123 | */ | ||
1124 | if (!sort_dso.elide && !map->dso->slen_calculated) | ||
1125 | dso__calc_col_width(map->dso); | ||
1126 | |||
889 | if (mapp) | 1127 | if (mapp) |
890 | *mapp = map; | 1128 | *mapp = map; |
891 | got_map: | 1129 | got_map: |
@@ -923,6 +1161,58 @@ static int call__match(struct symbol *sym) | |||
923 | return 0; | 1161 | return 0; |
924 | } | 1162 | } |
925 | 1163 | ||
1164 | static struct symbol ** | ||
1165 | resolve_callchain(struct thread *thread, struct map *map __used, | ||
1166 | struct ip_callchain *chain, struct hist_entry *entry) | ||
1167 | { | ||
1168 | u64 context = PERF_CONTEXT_MAX; | ||
1169 | struct symbol **syms = NULL; | ||
1170 | unsigned int i; | ||
1171 | |||
1172 | if (callchain) { | ||
1173 | syms = calloc(chain->nr, sizeof(*syms)); | ||
1174 | if (!syms) { | ||
1175 | fprintf(stderr, "Can't allocate memory for symbols\n"); | ||
1176 | exit(-1); | ||
1177 | } | ||
1178 | } | ||
1179 | |||
1180 | for (i = 0; i < chain->nr; i++) { | ||
1181 | u64 ip = chain->ips[i]; | ||
1182 | struct dso *dso = NULL; | ||
1183 | struct symbol *sym; | ||
1184 | |||
1185 | if (ip >= PERF_CONTEXT_MAX) { | ||
1186 | context = ip; | ||
1187 | continue; | ||
1188 | } | ||
1189 | |||
1190 | switch (context) { | ||
1191 | case PERF_CONTEXT_HV: | ||
1192 | dso = hypervisor_dso; | ||
1193 | break; | ||
1194 | case PERF_CONTEXT_KERNEL: | ||
1195 | dso = kernel_dso; | ||
1196 | break; | ||
1197 | default: | ||
1198 | break; | ||
1199 | } | ||
1200 | |||
1201 | sym = resolve_symbol(thread, NULL, &dso, &ip); | ||
1202 | |||
1203 | if (sym) { | ||
1204 | if (sort__has_parent && call__match(sym) && | ||
1205 | !entry->parent) | ||
1206 | entry->parent = sym; | ||
1207 | if (!callchain) | ||
1208 | break; | ||
1209 | syms[i] = sym; | ||
1210 | } | ||
1211 | } | ||
1212 | |||
1213 | return syms; | ||
1214 | } | ||
1215 | |||
926 | /* | 1216 | /* |
927 | * collect histogram counts | 1217 | * collect histogram counts |
928 | */ | 1218 | */ |
@@ -935,6 +1225,7 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
935 | struct rb_node **p = &hist.rb_node; | 1225 | struct rb_node **p = &hist.rb_node; |
936 | struct rb_node *parent = NULL; | 1226 | struct rb_node *parent = NULL; |
937 | struct hist_entry *he; | 1227 | struct hist_entry *he; |
1228 | struct symbol **syms = NULL; | ||
938 | struct hist_entry entry = { | 1229 | struct hist_entry entry = { |
939 | .thread = thread, | 1230 | .thread = thread, |
940 | .map = map, | 1231 | .map = map, |
@@ -948,36 +1239,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
948 | }; | 1239 | }; |
949 | int cmp; | 1240 | int cmp; |
950 | 1241 | ||
951 | if (sort__has_parent && chain) { | 1242 | if ((sort__has_parent || callchain) && chain) |
952 | u64 context = PERF_CONTEXT_MAX; | 1243 | 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 | 1244 | ||
982 | while (*p != NULL) { | 1245 | while (*p != NULL) { |
983 | parent = *p; | 1246 | parent = *p; |
@@ -987,8 +1250,10 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
987 | 1250 | ||
988 | if (!cmp) { | 1251 | if (!cmp) { |
989 | he->count += count; | 1252 | he->count += count; |
990 | if (callchain) | 1253 | if (callchain) { |
991 | append_chain(&he->callchain, chain); | 1254 | append_chain(&he->callchain, chain, syms); |
1255 | free(syms); | ||
1256 | } | ||
992 | return 0; | 1257 | return 0; |
993 | } | 1258 | } |
994 | 1259 | ||
@@ -1004,7 +1269,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
1004 | *he = entry; | 1269 | *he = entry; |
1005 | if (callchain) { | 1270 | if (callchain) { |
1006 | callchain_init(&he->callchain); | 1271 | callchain_init(&he->callchain); |
1007 | append_chain(&he->callchain, chain); | 1272 | append_chain(&he->callchain, chain, syms); |
1273 | free(syms); | ||
1008 | } | 1274 | } |
1009 | rb_link_node(&he->rb_node, parent, p); | 1275 | rb_link_node(&he->rb_node, parent, p); |
1010 | rb_insert_color(&he->rb_node, &hist); | 1276 | rb_insert_color(&he->rb_node, &hist); |
@@ -1076,14 +1342,15 @@ static void collapse__resort(void) | |||
1076 | 1342 | ||
1077 | static struct rb_root output_hists; | 1343 | static struct rb_root output_hists; |
1078 | 1344 | ||
1079 | static void output__insert_entry(struct hist_entry *he) | 1345 | static void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits) |
1080 | { | 1346 | { |
1081 | struct rb_node **p = &output_hists.rb_node; | 1347 | struct rb_node **p = &output_hists.rb_node; |
1082 | struct rb_node *parent = NULL; | 1348 | struct rb_node *parent = NULL; |
1083 | struct hist_entry *iter; | 1349 | struct hist_entry *iter; |
1084 | 1350 | ||
1085 | if (callchain) | 1351 | if (callchain) |
1086 | sort_chain_to_rbtree(&he->sorted_chain, &he->callchain); | 1352 | callchain_param.sort(&he->sorted_chain, &he->callchain, |
1353 | min_callchain_hits, &callchain_param); | ||
1087 | 1354 | ||
1088 | while (*p != NULL) { | 1355 | while (*p != NULL) { |
1089 | parent = *p; | 1356 | parent = *p; |
@@ -1099,11 +1366,14 @@ static void output__insert_entry(struct hist_entry *he) | |||
1099 | rb_insert_color(&he->rb_node, &output_hists); | 1366 | rb_insert_color(&he->rb_node, &output_hists); |
1100 | } | 1367 | } |
1101 | 1368 | ||
1102 | static void output__resort(void) | 1369 | static void output__resort(u64 total_samples) |
1103 | { | 1370 | { |
1104 | struct rb_node *next; | 1371 | struct rb_node *next; |
1105 | struct hist_entry *n; | 1372 | struct hist_entry *n; |
1106 | struct rb_root *tree = &hist; | 1373 | struct rb_root *tree = &hist; |
1374 | u64 min_callchain_hits; | ||
1375 | |||
1376 | min_callchain_hits = total_samples * (callchain_param.min_percent / 100); | ||
1107 | 1377 | ||
1108 | if (sort__need_collapse) | 1378 | if (sort__need_collapse) |
1109 | tree = &collapse_hists; | 1379 | tree = &collapse_hists; |
@@ -1115,7 +1385,7 @@ static void output__resort(void) | |||
1115 | next = rb_next(&n->rb_node); | 1385 | next = rb_next(&n->rb_node); |
1116 | 1386 | ||
1117 | rb_erase(&n->rb_node, tree); | 1387 | rb_erase(&n->rb_node, tree); |
1118 | output__insert_entry(n); | 1388 | output__insert_entry(n, min_callchain_hits); |
1119 | } | 1389 | } |
1120 | } | 1390 | } |
1121 | 1391 | ||
@@ -1125,35 +1395,69 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
1125 | struct sort_entry *se; | 1395 | struct sort_entry *se; |
1126 | struct rb_node *nd; | 1396 | struct rb_node *nd; |
1127 | size_t ret = 0; | 1397 | size_t ret = 0; |
1398 | unsigned int width; | ||
1399 | char *col_width = col_width_list_str; | ||
1128 | 1400 | ||
1129 | fprintf(fp, "\n"); | 1401 | init_rem_hits(); |
1130 | fprintf(fp, "#\n"); | 1402 | |
1131 | fprintf(fp, "# (%Ld samples)\n", (u64)total_samples); | 1403 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); |
1132 | fprintf(fp, "#\n"); | 1404 | fprintf(fp, "#\n"); |
1133 | 1405 | ||
1134 | fprintf(fp, "# Overhead"); | 1406 | fprintf(fp, "# Overhead"); |
1407 | if (show_nr_samples) { | ||
1408 | if (field_sep) | ||
1409 | fprintf(fp, "%cSamples", *field_sep); | ||
1410 | else | ||
1411 | fputs(" Samples ", fp); | ||
1412 | } | ||
1135 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1413 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
1136 | if (exclude_other && (se == &sort_parent)) | 1414 | if (se->elide) |
1137 | continue; | 1415 | continue; |
1138 | fprintf(fp, " %s", se->header); | 1416 | if (field_sep) { |
1417 | fprintf(fp, "%c%s", *field_sep, se->header); | ||
1418 | continue; | ||
1419 | } | ||
1420 | width = strlen(se->header); | ||
1421 | if (se->width) { | ||
1422 | if (col_width_list_str) { | ||
1423 | if (col_width) { | ||
1424 | *se->width = atoi(col_width); | ||
1425 | col_width = strchr(col_width, ','); | ||
1426 | if (col_width) | ||
1427 | ++col_width; | ||
1428 | } | ||
1429 | } | ||
1430 | width = *se->width = max(*se->width, width); | ||
1431 | } | ||
1432 | fprintf(fp, " %*s", width, se->header); | ||
1139 | } | 1433 | } |
1140 | fprintf(fp, "\n"); | 1434 | fprintf(fp, "\n"); |
1141 | 1435 | ||
1436 | if (field_sep) | ||
1437 | goto print_entries; | ||
1438 | |||
1142 | fprintf(fp, "# ........"); | 1439 | fprintf(fp, "# ........"); |
1440 | if (show_nr_samples) | ||
1441 | fprintf(fp, " .........."); | ||
1143 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1442 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
1144 | int i; | 1443 | unsigned int i; |
1145 | 1444 | ||
1146 | if (exclude_other && (se == &sort_parent)) | 1445 | if (se->elide) |
1147 | continue; | 1446 | continue; |
1148 | 1447 | ||
1149 | fprintf(fp, " "); | 1448 | fprintf(fp, " "); |
1150 | for (i = 0; i < strlen(se->header); i++) | 1449 | if (se->width) |
1450 | width = *se->width; | ||
1451 | else | ||
1452 | width = strlen(se->header); | ||
1453 | for (i = 0; i < width; i++) | ||
1151 | fprintf(fp, "."); | 1454 | fprintf(fp, "."); |
1152 | } | 1455 | } |
1153 | fprintf(fp, "\n"); | 1456 | fprintf(fp, "\n"); |
1154 | 1457 | ||
1155 | fprintf(fp, "#\n"); | 1458 | fprintf(fp, "#\n"); |
1156 | 1459 | ||
1460 | print_entries: | ||
1157 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { | 1461 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { |
1158 | pos = rb_entry(nd, struct hist_entry, rb_node); | 1462 | pos = rb_entry(nd, struct hist_entry, rb_node); |
1159 | ret += hist_entry__fprintf(fp, pos, total_samples); | 1463 | ret += hist_entry__fprintf(fp, pos, total_samples); |
@@ -1162,11 +1466,13 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
1162 | if (sort_order == default_sort_order && | 1466 | if (sort_order == default_sort_order && |
1163 | parent_pattern == default_parent_pattern) { | 1467 | parent_pattern == default_parent_pattern) { |
1164 | fprintf(fp, "#\n"); | 1468 | fprintf(fp, "#\n"); |
1165 | fprintf(fp, "# (For more details, try: perf report --sort comm,dso,symbol)\n"); | 1469 | fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n"); |
1166 | fprintf(fp, "#\n"); | 1470 | fprintf(fp, "#\n"); |
1167 | } | 1471 | } |
1168 | fprintf(fp, "\n"); | 1472 | fprintf(fp, "\n"); |
1169 | 1473 | ||
1474 | free(rem_sq_bracket); | ||
1475 | |||
1170 | return ret; | 1476 | return ret; |
1171 | } | 1477 | } |
1172 | 1478 | ||
@@ -1213,22 +1519,23 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1213 | struct map *map = NULL; | 1519 | struct map *map = NULL; |
1214 | void *more_data = event->ip.__more_data; | 1520 | void *more_data = event->ip.__more_data; |
1215 | struct ip_callchain *chain = NULL; | 1521 | struct ip_callchain *chain = NULL; |
1522 | int cpumode; | ||
1216 | 1523 | ||
1217 | if (sample_type & PERF_SAMPLE_PERIOD) { | 1524 | if (sample_type & PERF_SAMPLE_PERIOD) { |
1218 | period = *(u64 *)more_data; | 1525 | period = *(u64 *)more_data; |
1219 | more_data += sizeof(u64); | 1526 | more_data += sizeof(u64); |
1220 | } | 1527 | } |
1221 | 1528 | ||
1222 | dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d: %p period: %Ld\n", | 1529 | dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", |
1223 | (void *)(offset + head), | 1530 | (void *)(offset + head), |
1224 | (void *)(long)(event->header.size), | 1531 | (void *)(long)(event->header.size), |
1225 | event->header.misc, | 1532 | event->header.misc, |
1226 | event->ip.pid, | 1533 | event->ip.pid, event->ip.tid, |
1227 | (void *)(long)ip, | 1534 | (void *)(long)ip, |
1228 | (long long)period); | 1535 | (long long)period); |
1229 | 1536 | ||
1230 | if (sample_type & PERF_SAMPLE_CALLCHAIN) { | 1537 | if (sample_type & PERF_SAMPLE_CALLCHAIN) { |
1231 | int i; | 1538 | unsigned int i; |
1232 | 1539 | ||
1233 | chain = (void *)more_data; | 1540 | chain = (void *)more_data; |
1234 | 1541 | ||
@@ -1256,7 +1563,9 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1256 | if (comm_list && !strlist__has_entry(comm_list, thread->comm)) | 1563 | if (comm_list && !strlist__has_entry(comm_list, thread->comm)) |
1257 | return 0; | 1564 | return 0; |
1258 | 1565 | ||
1259 | if (event->header.misc & PERF_EVENT_MISC_KERNEL) { | 1566 | cpumode = event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK; |
1567 | |||
1568 | if (cpumode == PERF_EVENT_MISC_KERNEL) { | ||
1260 | show = SHOW_KERNEL; | 1569 | show = SHOW_KERNEL; |
1261 | level = 'k'; | 1570 | level = 'k'; |
1262 | 1571 | ||
@@ -1264,7 +1573,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1264 | 1573 | ||
1265 | dprintf(" ...... dso: %s\n", dso->name); | 1574 | dprintf(" ...... dso: %s\n", dso->name); |
1266 | 1575 | ||
1267 | } else if (event->header.misc & PERF_EVENT_MISC_USER) { | 1576 | } else if (cpumode == PERF_EVENT_MISC_USER) { |
1268 | 1577 | ||
1269 | show = SHOW_USER; | 1578 | show = SHOW_USER; |
1270 | level = '.'; | 1579 | level = '.'; |
@@ -1272,16 +1581,20 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1272 | } else { | 1581 | } else { |
1273 | show = SHOW_HV; | 1582 | show = SHOW_HV; |
1274 | level = 'H'; | 1583 | level = 'H'; |
1584 | |||
1585 | dso = hypervisor_dso; | ||
1586 | |||
1275 | dprintf(" ...... dso: [hypervisor]\n"); | 1587 | dprintf(" ...... dso: [hypervisor]\n"); |
1276 | } | 1588 | } |
1277 | 1589 | ||
1278 | if (show & show_mask) { | 1590 | if (show & show_mask) { |
1279 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); | 1591 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); |
1280 | 1592 | ||
1281 | if (dso_list && dso && dso->name && !strlist__has_entry(dso_list, dso->name)) | 1593 | if (dso_list && (!dso || !dso->name || |
1594 | !strlist__has_entry(dso_list, dso->name))) | ||
1282 | return 0; | 1595 | return 0; |
1283 | 1596 | ||
1284 | if (sym_list && sym && !strlist__has_entry(sym_list, sym->name)) | 1597 | if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name))) |
1285 | return 0; | 1598 | return 0; |
1286 | 1599 | ||
1287 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { | 1600 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { |
@@ -1300,10 +1613,11 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | |||
1300 | struct thread *thread = threads__findnew(event->mmap.pid); | 1613 | struct thread *thread = threads__findnew(event->mmap.pid); |
1301 | struct map *map = map__new(&event->mmap); | 1614 | struct map *map = map__new(&event->mmap); |
1302 | 1615 | ||
1303 | dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n", | 1616 | dprintf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n", |
1304 | (void *)(offset + head), | 1617 | (void *)(offset + head), |
1305 | (void *)(long)(event->header.size), | 1618 | (void *)(long)(event->header.size), |
1306 | event->mmap.pid, | 1619 | event->mmap.pid, |
1620 | event->mmap.tid, | ||
1307 | (void *)(long)event->mmap.start, | 1621 | (void *)(long)event->mmap.start, |
1308 | (void *)(long)event->mmap.len, | 1622 | (void *)(long)event->mmap.len, |
1309 | (void *)(long)event->mmap.pgoff, | 1623 | (void *)(long)event->mmap.pgoff, |
@@ -1341,15 +1655,27 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head) | |||
1341 | } | 1655 | } |
1342 | 1656 | ||
1343 | static int | 1657 | static int |
1344 | process_fork_event(event_t *event, unsigned long offset, unsigned long head) | 1658 | process_task_event(event_t *event, unsigned long offset, unsigned long head) |
1345 | { | 1659 | { |
1346 | struct thread *thread = threads__findnew(event->fork.pid); | 1660 | struct thread *thread = threads__findnew(event->fork.pid); |
1347 | struct thread *parent = threads__findnew(event->fork.ppid); | 1661 | struct thread *parent = threads__findnew(event->fork.ppid); |
1348 | 1662 | ||
1349 | dprintf("%p [%p]: PERF_EVENT_FORK: %d:%d\n", | 1663 | dprintf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n", |
1350 | (void *)(offset + head), | 1664 | (void *)(offset + head), |
1351 | (void *)(long)(event->header.size), | 1665 | (void *)(long)(event->header.size), |
1352 | event->fork.pid, event->fork.ppid); | 1666 | event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT", |
1667 | event->fork.pid, event->fork.tid, | ||
1668 | event->fork.ppid, event->fork.ptid); | ||
1669 | |||
1670 | /* | ||
1671 | * A thread clone will have the same PID for both | ||
1672 | * parent and child. | ||
1673 | */ | ||
1674 | if (thread == parent) | ||
1675 | return 0; | ||
1676 | |||
1677 | if (event->header.type == PERF_EVENT_EXIT) | ||
1678 | return 0; | ||
1353 | 1679 | ||
1354 | if (!thread || !parent || thread__fork(thread, parent)) { | 1680 | if (!thread || !parent || thread__fork(thread, parent)) { |
1355 | dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); | 1681 | dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); |
@@ -1361,19 +1687,6 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head) | |||
1361 | } | 1687 | } |
1362 | 1688 | ||
1363 | static int | 1689 | 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) | 1690 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) |
1378 | { | 1691 | { |
1379 | dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", | 1692 | dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", |
@@ -1423,14 +1736,37 @@ static void trace_event(event_t *event) | |||
1423 | dprintf(".\n"); | 1736 | dprintf(".\n"); |
1424 | } | 1737 | } |
1425 | 1738 | ||
1739 | static struct perf_header *header; | ||
1740 | |||
1741 | static struct perf_counter_attr *perf_header__find_attr(u64 id) | ||
1742 | { | ||
1743 | int i; | ||
1744 | |||
1745 | for (i = 0; i < header->attrs; i++) { | ||
1746 | struct perf_header_attr *attr = header->attr[i]; | ||
1747 | int j; | ||
1748 | |||
1749 | for (j = 0; j < attr->ids; j++) { | ||
1750 | if (attr->id[j] == id) | ||
1751 | return &attr->attr; | ||
1752 | } | ||
1753 | } | ||
1754 | |||
1755 | return NULL; | ||
1756 | } | ||
1757 | |||
1426 | static int | 1758 | static int |
1427 | process_read_event(event_t *event, unsigned long offset, unsigned long head) | 1759 | process_read_event(event_t *event, unsigned long offset, unsigned long head) |
1428 | { | 1760 | { |
1429 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n", | 1761 | struct perf_counter_attr *attr = perf_header__find_attr(event->read.id); |
1762 | |||
1763 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", | ||
1430 | (void *)(offset + head), | 1764 | (void *)(offset + head), |
1431 | (void *)(long)(event->header.size), | 1765 | (void *)(long)(event->header.size), |
1432 | event->read.pid, | 1766 | event->read.pid, |
1433 | event->read.tid, | 1767 | event->read.tid, |
1768 | attr ? __event_name(attr->type, attr->config) | ||
1769 | : "FAIL", | ||
1434 | event->read.value); | 1770 | event->read.value); |
1435 | 1771 | ||
1436 | return 0; | 1772 | return 0; |
@@ -1452,10 +1788,8 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
1452 | return process_comm_event(event, offset, head); | 1788 | return process_comm_event(event, offset, head); |
1453 | 1789 | ||
1454 | case PERF_EVENT_FORK: | 1790 | case PERF_EVENT_FORK: |
1455 | return process_fork_event(event, offset, head); | 1791 | case PERF_EVENT_EXIT: |
1456 | 1792 | return process_task_event(event, offset, head); | |
1457 | case PERF_EVENT_PERIOD: | ||
1458 | return process_period_event(event, offset, head); | ||
1459 | 1793 | ||
1460 | case PERF_EVENT_LOST: | 1794 | case PERF_EVENT_LOST: |
1461 | return process_lost_event(event, offset, head); | 1795 | return process_lost_event(event, offset, head); |
@@ -1478,8 +1812,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
1478 | return 0; | 1812 | return 0; |
1479 | } | 1813 | } |
1480 | 1814 | ||
1481 | static struct perf_header *header; | ||
1482 | |||
1483 | static u64 perf_header__sample_type(void) | 1815 | static u64 perf_header__sample_type(void) |
1484 | { | 1816 | { |
1485 | u64 sample_type = 0; | 1817 | u64 sample_type = 0; |
@@ -1534,9 +1866,26 @@ static int __cmd_report(void) | |||
1534 | 1866 | ||
1535 | sample_type = perf_header__sample_type(); | 1867 | sample_type = perf_header__sample_type(); |
1536 | 1868 | ||
1537 | if (sort__has_parent && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { | 1869 | if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) { |
1538 | fprintf(stderr, "selected --sort parent, but no callchain data\n"); | 1870 | if (sort__has_parent) { |
1539 | exit(-1); | 1871 | fprintf(stderr, "selected --sort parent, but no" |
1872 | " callchain data. Did you call" | ||
1873 | " perf record without -g?\n"); | ||
1874 | exit(-1); | ||
1875 | } | ||
1876 | if (callchain) { | ||
1877 | fprintf(stderr, "selected -c but no callchain data." | ||
1878 | " Did you call perf record without" | ||
1879 | " -g?\n"); | ||
1880 | exit(-1); | ||
1881 | } | ||
1882 | } else if (callchain_param.mode != CHAIN_NONE && !callchain) { | ||
1883 | callchain = 1; | ||
1884 | if (register_callchain_param(&callchain_param) < 0) { | ||
1885 | fprintf(stderr, "Can't register callchain" | ||
1886 | " params\n"); | ||
1887 | exit(-1); | ||
1888 | } | ||
1540 | } | 1889 | } |
1541 | 1890 | ||
1542 | if (load_kernel() < 0) { | 1891 | if (load_kernel() < 0) { |
@@ -1619,7 +1968,7 @@ more: | |||
1619 | if (offset + head >= header->data_offset + header->data_size) | 1968 | if (offset + head >= header->data_offset + header->data_size) |
1620 | goto done; | 1969 | goto done; |
1621 | 1970 | ||
1622 | if (offset + head < stat.st_size) | 1971 | if (offset + head < (unsigned long)stat.st_size) |
1623 | goto more; | 1972 | goto more; |
1624 | 1973 | ||
1625 | done: | 1974 | done: |
@@ -1643,12 +1992,65 @@ done: | |||
1643 | dsos__fprintf(stdout); | 1992 | dsos__fprintf(stdout); |
1644 | 1993 | ||
1645 | collapse__resort(); | 1994 | collapse__resort(); |
1646 | output__resort(); | 1995 | output__resort(total); |
1647 | output__fprintf(stdout, total); | 1996 | output__fprintf(stdout, total); |
1648 | 1997 | ||
1649 | return rc; | 1998 | return rc; |
1650 | } | 1999 | } |
1651 | 2000 | ||
2001 | static int | ||
2002 | parse_callchain_opt(const struct option *opt __used, const char *arg, | ||
2003 | int unset __used) | ||
2004 | { | ||
2005 | char *tok; | ||
2006 | char *endptr; | ||
2007 | |||
2008 | callchain = 1; | ||
2009 | |||
2010 | if (!arg) | ||
2011 | return 0; | ||
2012 | |||
2013 | tok = strtok((char *)arg, ","); | ||
2014 | if (!tok) | ||
2015 | return -1; | ||
2016 | |||
2017 | /* get the output mode */ | ||
2018 | if (!strncmp(tok, "graph", strlen(arg))) | ||
2019 | callchain_param.mode = CHAIN_GRAPH_ABS; | ||
2020 | |||
2021 | else if (!strncmp(tok, "flat", strlen(arg))) | ||
2022 | callchain_param.mode = CHAIN_FLAT; | ||
2023 | |||
2024 | else if (!strncmp(tok, "fractal", strlen(arg))) | ||
2025 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
2026 | |||
2027 | else if (!strncmp(tok, "none", strlen(arg))) { | ||
2028 | callchain_param.mode = CHAIN_NONE; | ||
2029 | callchain = 0; | ||
2030 | |||
2031 | return 0; | ||
2032 | } | ||
2033 | |||
2034 | else | ||
2035 | return -1; | ||
2036 | |||
2037 | /* get the min percentage */ | ||
2038 | tok = strtok(NULL, ","); | ||
2039 | if (!tok) | ||
2040 | goto setup; | ||
2041 | |||
2042 | callchain_param.min_percent = strtod(tok, &endptr); | ||
2043 | if (tok == endptr) | ||
2044 | return -1; | ||
2045 | |||
2046 | setup: | ||
2047 | if (register_callchain_param(&callchain_param) < 0) { | ||
2048 | fprintf(stderr, "Can't register callchain params\n"); | ||
2049 | return -1; | ||
2050 | } | ||
2051 | return 0; | ||
2052 | } | ||
2053 | |||
1652 | static const char * const report_usage[] = { | 2054 | static const char * const report_usage[] = { |
1653 | "perf report [<options>] <command>", | 2055 | "perf report [<options>] <command>", |
1654 | NULL | 2056 | NULL |
@@ -1662,6 +2064,10 @@ static const struct option options[] = { | |||
1662 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 2064 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1663 | "dump raw trace in ASCII"), | 2065 | "dump raw trace in ASCII"), |
1664 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | 2066 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), |
2067 | OPT_BOOLEAN('m', "modules", &modules, | ||
2068 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | ||
2069 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, | ||
2070 | "Show a column with the number of samples"), | ||
1665 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 2071 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
1666 | "sort by key(s): pid, comm, dso, symbol, parent"), | 2072 | "sort by key(s): pid, comm, dso, symbol, parent"), |
1667 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 2073 | OPT_BOOLEAN('P', "full-paths", &full_paths, |
@@ -1670,13 +2076,21 @@ static const struct option options[] = { | |||
1670 | "regex filter to identify parent, see: '--sort parent'"), | 2076 | "regex filter to identify parent, see: '--sort parent'"), |
1671 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, | 2077 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, |
1672 | "Only display entries with parent-match"), | 2078 | "Only display entries with parent-match"), |
1673 | OPT_BOOLEAN('c', "callchain", &callchain, "Display callchains"), | 2079 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", |
2080 | "Display callchains using output_type and min percent threshold. " | ||
2081 | "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), | ||
1674 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", | 2082 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", |
1675 | "only consider symbols in these dsos"), | 2083 | "only consider symbols in these dsos"), |
1676 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", | 2084 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", |
1677 | "only consider symbols in these comms"), | 2085 | "only consider symbols in these comms"), |
1678 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", | 2086 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", |
1679 | "only consider these symbols"), | 2087 | "only consider these symbols"), |
2088 | OPT_STRING('w', "column-widths", &col_width_list_str, | ||
2089 | "width[,width...]", | ||
2090 | "don't try to adjust column width, use these fixed values"), | ||
2091 | OPT_STRING('t', "field-separator", &field_sep, "separator", | ||
2092 | "separator for columns, no spaces will be added between " | ||
2093 | "columns '.' is reserved."), | ||
1680 | OPT_END() | 2094 | OPT_END() |
1681 | }; | 2095 | }; |
1682 | 2096 | ||
@@ -1696,7 +2110,8 @@ static void setup_sorting(void) | |||
1696 | } | 2110 | } |
1697 | 2111 | ||
1698 | static void setup_list(struct strlist **list, const char *list_str, | 2112 | static void setup_list(struct strlist **list, const char *list_str, |
1699 | const char *list_name) | 2113 | struct sort_entry *se, const char *list_name, |
2114 | FILE *fp) | ||
1700 | { | 2115 | { |
1701 | if (list_str) { | 2116 | if (list_str) { |
1702 | *list = strlist__new(true, list_str); | 2117 | *list = strlist__new(true, list_str); |
@@ -1705,10 +2120,15 @@ static void setup_list(struct strlist **list, const char *list_str, | |||
1705 | list_name); | 2120 | list_name); |
1706 | exit(129); | 2121 | exit(129); |
1707 | } | 2122 | } |
2123 | if (strlist__nr_entries(*list) == 1) { | ||
2124 | fprintf(fp, "# %s: %s\n", list_name, | ||
2125 | strlist__entry(*list, 0)->s); | ||
2126 | se->elide = true; | ||
2127 | } | ||
1708 | } | 2128 | } |
1709 | } | 2129 | } |
1710 | 2130 | ||
1711 | int cmd_report(int argc, const char **argv, const char *prefix) | 2131 | int cmd_report(int argc, const char **argv, const char *prefix __used) |
1712 | { | 2132 | { |
1713 | symbol__init(); | 2133 | symbol__init(); |
1714 | 2134 | ||
@@ -1718,9 +2138,10 @@ int cmd_report(int argc, const char **argv, const char *prefix) | |||
1718 | 2138 | ||
1719 | setup_sorting(); | 2139 | setup_sorting(); |
1720 | 2140 | ||
1721 | if (parent_pattern != default_parent_pattern) | 2141 | if (parent_pattern != default_parent_pattern) { |
1722 | sort_dimension__add("parent"); | 2142 | sort_dimension__add("parent"); |
1723 | else | 2143 | sort_parent.elide = 1; |
2144 | } else | ||
1724 | exclude_other = 0; | 2145 | exclude_other = 0; |
1725 | 2146 | ||
1726 | /* | 2147 | /* |
@@ -1729,11 +2150,17 @@ int cmd_report(int argc, const char **argv, const char *prefix) | |||
1729 | if (argc) | 2150 | if (argc) |
1730 | usage_with_options(report_usage, options); | 2151 | usage_with_options(report_usage, options); |
1731 | 2152 | ||
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(); | 2153 | setup_pager(); |
1737 | 2154 | ||
2155 | setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout); | ||
2156 | setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout); | ||
2157 | setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout); | ||
2158 | |||
2159 | if (field_sep && *field_sep == '.') { | ||
2160 | fputs("'.' is the only non valid --field-separator argument\n", | ||
2161 | stderr); | ||
2162 | exit(129); | ||
2163 | } | ||
2164 | |||
1738 | return __cmd_report(); | 2165 | return __cmd_report(); |
1739 | } | 2166 | } |