Commit 343a031f3c4a7a663192cf56368bb5a6c56870c0
Exists in
master
and in
38 other branches
Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/freder…
…ic/random-tracing into perf/core
Showing 7 changed files Side-by-side Diff
tools/perf/Documentation/perf-report.txt
... | ... | @@ -80,15 +80,24 @@ |
80 | 80 | --dump-raw-trace:: |
81 | 81 | Dump raw trace in ASCII. |
82 | 82 | |
83 | --g [type,min]:: | |
83 | +-g [type,min,order]:: | |
84 | 84 | --call-graph:: |
85 | - Display call chains using type and min percent threshold. | |
85 | + Display call chains using type, min percent threshold and order. | |
86 | 86 | type can be either: |
87 | 87 | - flat: single column, linear exposure of call chains. |
88 | 88 | - graph: use a graph tree, displaying absolute overhead rates. |
89 | 89 | - fractal: like graph, but displays relative rates. Each branch of |
90 | 90 | the tree is considered as a new profiled object. + |
91 | - Default: fractal,0.5. | |
91 | + | |
92 | + order can be either: | |
93 | + - callee: callee based call graph. | |
94 | + - caller: inverted caller based call graph. | |
95 | + | |
96 | + Default: fractal,0.5,callee. | |
97 | + | |
98 | +-G:: | |
99 | +--inverted:: | |
100 | + alias for inverted caller based call graph. | |
92 | 101 | |
93 | 102 | --pretty=<key>:: |
94 | 103 | Pretty printing style. key: normal, raw |
tools/perf/builtin-report.c
... | ... | @@ -45,7 +45,8 @@ |
45 | 45 | static const char default_pretty_printing_style[] = "normal"; |
46 | 46 | static const char *pretty_printing_style = default_pretty_printing_style; |
47 | 47 | |
48 | -static char callchain_default_opt[] = "fractal,0.5"; | |
48 | +static char callchain_default_opt[] = "fractal,0.5,callee"; | |
49 | +static bool inverted_callchain; | |
49 | 50 | static symbol_filter_t annotate_init; |
50 | 51 | |
51 | 52 | static int perf_session__add_hist_entry(struct perf_session *session, |
52 | 53 | |
53 | 54 | |
... | ... | @@ -386,13 +387,29 @@ |
386 | 387 | if (!tok) |
387 | 388 | goto setup; |
388 | 389 | |
389 | - tok2 = strtok(NULL, ","); | |
390 | 390 | callchain_param.min_percent = strtod(tok, &endptr); |
391 | 391 | if (tok == endptr) |
392 | 392 | return -1; |
393 | 393 | |
394 | - if (tok2) | |
394 | + /* get the print limit */ | |
395 | + tok2 = strtok(NULL, ","); | |
396 | + if (!tok2) | |
397 | + goto setup; | |
398 | + | |
399 | + if (tok2[0] != 'c') { | |
395 | 400 | callchain_param.print_limit = strtod(tok2, &endptr); |
401 | + tok2 = strtok(NULL, ","); | |
402 | + if (!tok2) | |
403 | + goto setup; | |
404 | + } | |
405 | + | |
406 | + /* get the call chain order */ | |
407 | + if (!strcmp(tok2, "caller")) | |
408 | + callchain_param.order = ORDER_CALLER; | |
409 | + else if (!strcmp(tok2, "callee")) | |
410 | + callchain_param.order = ORDER_CALLEE; | |
411 | + else | |
412 | + return -1; | |
396 | 413 | setup: |
397 | 414 | if (callchain_register_param(&callchain_param) < 0) { |
398 | 415 | fprintf(stderr, "Can't register callchain params\n"); |
... | ... | @@ -436,9 +453,10 @@ |
436 | 453 | "regex filter to identify parent, see: '--sort parent'"), |
437 | 454 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, |
438 | 455 | "Only display entries with parent-match"), |
439 | - OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", | |
440 | - "Display callchains using output_type (graph, flat, fractal, or none) and min percent threshold. " | |
441 | - "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), | |
456 | + OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent, call_order", | |
457 | + "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold and callchain order. " | |
458 | + "Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt), | |
459 | + OPT_BOOLEAN('G', "inverted", &inverted_callchain, "alias for inverted call graph"), | |
442 | 460 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
443 | 461 | "only consider symbols in these dsos"), |
444 | 462 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", |
... | ... | @@ -467,6 +485,9 @@ |
467 | 485 | else if (use_tui) |
468 | 486 | use_browser = 1; |
469 | 487 | |
488 | + if (inverted_callchain) | |
489 | + callchain_param.order = ORDER_CALLER; | |
490 | + | |
470 | 491 | if (strcmp(input_name, "-") != 0) |
471 | 492 | setup_browser(true); |
472 | 493 | else |
... | ... | @@ -504,7 +525,14 @@ |
504 | 525 | if (parent_pattern != default_parent_pattern) { |
505 | 526 | if (sort_dimension__add("parent") < 0) |
506 | 527 | return -1; |
507 | - sort_parent.elide = 1; | |
528 | + | |
529 | + /* | |
530 | + * Only show the parent fields if we explicitly | |
531 | + * sort that way. If we only use parent machinery | |
532 | + * for filtering, we don't want it. | |
533 | + */ | |
534 | + if (!strstr(sort_order, "parent")) | |
535 | + sort_parent.elide = 1; | |
508 | 536 | } else |
509 | 537 | symbol_conf.exclude_other = false; |
510 | 538 |
tools/perf/util/callchain.h
... | ... | @@ -14,6 +14,11 @@ |
14 | 14 | CHAIN_GRAPH_REL |
15 | 15 | }; |
16 | 16 | |
17 | +enum chain_order { | |
18 | + ORDER_CALLER, | |
19 | + ORDER_CALLEE | |
20 | +}; | |
21 | + | |
17 | 22 | struct callchain_node { |
18 | 23 | struct callchain_node *parent; |
19 | 24 | struct list_head siblings; |
... | ... | @@ -41,6 +46,7 @@ |
41 | 46 | u32 print_limit; |
42 | 47 | double min_percent; |
43 | 48 | sort_chain_func_t sort; |
49 | + enum chain_order order; | |
44 | 50 | }; |
45 | 51 | |
46 | 52 | struct callchain_list { |
tools/perf/util/hist.c
... | ... | @@ -14,7 +14,8 @@ |
14 | 14 | |
15 | 15 | struct callchain_param callchain_param = { |
16 | 16 | .mode = CHAIN_GRAPH_REL, |
17 | - .min_percent = 0.5 | |
17 | + .min_percent = 0.5, | |
18 | + .order = ORDER_CALLEE | |
18 | 19 | }; |
19 | 20 | |
20 | 21 | u16 hists__col_len(struct hists *self, enum hist_column col) |
... | ... | @@ -845,6 +846,9 @@ |
845 | 846 | print_entries: |
846 | 847 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
847 | 848 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
849 | + | |
850 | + if (h->filtered) | |
851 | + continue; | |
848 | 852 | |
849 | 853 | if (show_displacement) { |
850 | 854 | if (h->pair != NULL) |
tools/perf/util/session.c
... | ... | @@ -247,8 +247,13 @@ |
247 | 247 | callchain_cursor_reset(&self->callchain_cursor); |
248 | 248 | |
249 | 249 | for (i = 0; i < chain->nr; i++) { |
250 | - u64 ip = chain->ips[i]; | |
250 | + u64 ip; | |
251 | 251 | struct addr_location al; |
252 | + | |
253 | + if (callchain_param.order == ORDER_CALLEE) | |
254 | + ip = chain->ips[i]; | |
255 | + else | |
256 | + ip = chain->ips[chain->nr - i - 1]; | |
252 | 257 | |
253 | 258 | if (ip >= PERF_CONTEXT_MAX) { |
254 | 259 | switch (ip) { |
tools/perf/util/sort.c
... | ... | @@ -15,95 +15,6 @@ |
15 | 15 | |
16 | 16 | LIST_HEAD(hist_entry__sort_list); |
17 | 17 | |
18 | -static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | |
19 | - size_t size, unsigned int width); | |
20 | -static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, | |
21 | - size_t size, unsigned int width); | |
22 | -static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | |
23 | - size_t size, unsigned int width); | |
24 | -static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | |
25 | - size_t size, unsigned int width); | |
26 | -static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | |
27 | - size_t size, unsigned int width); | |
28 | -static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | |
29 | - size_t size, unsigned int width); | |
30 | - | |
31 | -struct sort_entry sort_thread = { | |
32 | - .se_header = "Command: Pid", | |
33 | - .se_cmp = sort__thread_cmp, | |
34 | - .se_snprintf = hist_entry__thread_snprintf, | |
35 | - .se_width_idx = HISTC_THREAD, | |
36 | -}; | |
37 | - | |
38 | -struct sort_entry sort_comm = { | |
39 | - .se_header = "Command", | |
40 | - .se_cmp = sort__comm_cmp, | |
41 | - .se_collapse = sort__comm_collapse, | |
42 | - .se_snprintf = hist_entry__comm_snprintf, | |
43 | - .se_width_idx = HISTC_COMM, | |
44 | -}; | |
45 | - | |
46 | -struct sort_entry sort_dso = { | |
47 | - .se_header = "Shared Object", | |
48 | - .se_cmp = sort__dso_cmp, | |
49 | - .se_snprintf = hist_entry__dso_snprintf, | |
50 | - .se_width_idx = HISTC_DSO, | |
51 | -}; | |
52 | - | |
53 | -struct sort_entry sort_sym = { | |
54 | - .se_header = "Symbol", | |
55 | - .se_cmp = sort__sym_cmp, | |
56 | - .se_snprintf = hist_entry__sym_snprintf, | |
57 | - .se_width_idx = HISTC_SYMBOL, | |
58 | -}; | |
59 | - | |
60 | -struct sort_entry sort_parent = { | |
61 | - .se_header = "Parent symbol", | |
62 | - .se_cmp = sort__parent_cmp, | |
63 | - .se_snprintf = hist_entry__parent_snprintf, | |
64 | - .se_width_idx = HISTC_PARENT, | |
65 | -}; | |
66 | - | |
67 | -struct sort_entry sort_cpu = { | |
68 | - .se_header = "CPU", | |
69 | - .se_cmp = sort__cpu_cmp, | |
70 | - .se_snprintf = hist_entry__cpu_snprintf, | |
71 | - .se_width_idx = HISTC_CPU, | |
72 | -}; | |
73 | - | |
74 | -struct sort_dimension { | |
75 | - const char *name; | |
76 | - struct sort_entry *entry; | |
77 | - int taken; | |
78 | -}; | |
79 | - | |
80 | -static struct sort_dimension sort_dimensions[] = { | |
81 | - { .name = "pid", .entry = &sort_thread, }, | |
82 | - { .name = "comm", .entry = &sort_comm, }, | |
83 | - { .name = "dso", .entry = &sort_dso, }, | |
84 | - { .name = "symbol", .entry = &sort_sym, }, | |
85 | - { .name = "parent", .entry = &sort_parent, }, | |
86 | - { .name = "cpu", .entry = &sort_cpu, }, | |
87 | -}; | |
88 | - | |
89 | -int64_t cmp_null(void *l, void *r) | |
90 | -{ | |
91 | - if (!l && !r) | |
92 | - return 0; | |
93 | - else if (!l) | |
94 | - return -1; | |
95 | - else | |
96 | - return 1; | |
97 | -} | |
98 | - | |
99 | -/* --sort pid */ | |
100 | - | |
101 | -int64_t | |
102 | -sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | |
103 | -{ | |
104 | - return right->thread->pid - left->thread->pid; | |
105 | -} | |
106 | - | |
107 | 18 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) |
108 | 19 | { |
109 | 20 | int n; |
... | ... | @@ -125,6 +36,24 @@ |
125 | 36 | return n; |
126 | 37 | } |
127 | 38 | |
39 | +static int64_t cmp_null(void *l, void *r) | |
40 | +{ | |
41 | + if (!l && !r) | |
42 | + return 0; | |
43 | + else if (!l) | |
44 | + return -1; | |
45 | + else | |
46 | + return 1; | |
47 | +} | |
48 | + | |
49 | +/* --sort pid */ | |
50 | + | |
51 | +static int64_t | |
52 | +sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | |
53 | +{ | |
54 | + return right->thread->pid - left->thread->pid; | |
55 | +} | |
56 | + | |
128 | 57 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, |
129 | 58 | size_t size, unsigned int width) |
130 | 59 | { |
131 | 60 | |
132 | 61 | |
... | ... | @@ -132,15 +61,50 @@ |
132 | 61 | self->thread->comm ?: "", self->thread->pid); |
133 | 62 | } |
134 | 63 | |
64 | +struct sort_entry sort_thread = { | |
65 | + .se_header = "Command: Pid", | |
66 | + .se_cmp = sort__thread_cmp, | |
67 | + .se_snprintf = hist_entry__thread_snprintf, | |
68 | + .se_width_idx = HISTC_THREAD, | |
69 | +}; | |
70 | + | |
71 | +/* --sort comm */ | |
72 | + | |
73 | +static int64_t | |
74 | +sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | |
75 | +{ | |
76 | + return right->thread->pid - left->thread->pid; | |
77 | +} | |
78 | + | |
79 | +static int64_t | |
80 | +sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | |
81 | +{ | |
82 | + char *comm_l = left->thread->comm; | |
83 | + char *comm_r = right->thread->comm; | |
84 | + | |
85 | + if (!comm_l || !comm_r) | |
86 | + return cmp_null(comm_l, comm_r); | |
87 | + | |
88 | + return strcmp(comm_l, comm_r); | |
89 | +} | |
90 | + | |
135 | 91 | static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, |
136 | 92 | size_t size, unsigned int width) |
137 | 93 | { |
138 | 94 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); |
139 | 95 | } |
140 | 96 | |
97 | +struct sort_entry sort_comm = { | |
98 | + .se_header = "Command", | |
99 | + .se_cmp = sort__comm_cmp, | |
100 | + .se_collapse = sort__comm_collapse, | |
101 | + .se_snprintf = hist_entry__comm_snprintf, | |
102 | + .se_width_idx = HISTC_COMM, | |
103 | +}; | |
104 | + | |
141 | 105 | /* --sort dso */ |
142 | 106 | |
143 | -int64_t | |
107 | +static int64_t | |
144 | 108 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) |
145 | 109 | { |
146 | 110 | struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; |
147 | 111 | |
... | ... | @@ -173,9 +137,16 @@ |
173 | 137 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); |
174 | 138 | } |
175 | 139 | |
140 | +struct sort_entry sort_dso = { | |
141 | + .se_header = "Shared Object", | |
142 | + .se_cmp = sort__dso_cmp, | |
143 | + .se_snprintf = hist_entry__dso_snprintf, | |
144 | + .se_width_idx = HISTC_DSO, | |
145 | +}; | |
146 | + | |
176 | 147 | /* --sort symbol */ |
177 | 148 | |
178 | -int64_t | |
149 | +static int64_t | |
179 | 150 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) |
180 | 151 | { |
181 | 152 | u64 ip_l, ip_r; |
182 | 153 | |
183 | 154 | |
... | ... | @@ -211,29 +182,16 @@ |
211 | 182 | return ret; |
212 | 183 | } |
213 | 184 | |
214 | -/* --sort comm */ | |
185 | +struct sort_entry sort_sym = { | |
186 | + .se_header = "Symbol", | |
187 | + .se_cmp = sort__sym_cmp, | |
188 | + .se_snprintf = hist_entry__sym_snprintf, | |
189 | + .se_width_idx = HISTC_SYMBOL, | |
190 | +}; | |
215 | 191 | |
216 | -int64_t | |
217 | -sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | |
218 | -{ | |
219 | - return right->thread->pid - left->thread->pid; | |
220 | -} | |
221 | - | |
222 | -int64_t | |
223 | -sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | |
224 | -{ | |
225 | - char *comm_l = left->thread->comm; | |
226 | - char *comm_r = right->thread->comm; | |
227 | - | |
228 | - if (!comm_l || !comm_r) | |
229 | - return cmp_null(comm_l, comm_r); | |
230 | - | |
231 | - return strcmp(comm_l, comm_r); | |
232 | -} | |
233 | - | |
234 | 192 | /* --sort parent */ |
235 | 193 | |
236 | -int64_t | |
194 | +static int64_t | |
237 | 195 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) |
238 | 196 | { |
239 | 197 | struct symbol *sym_l = left->parent; |
240 | 198 | |
... | ... | @@ -252,9 +210,16 @@ |
252 | 210 | self->parent ? self->parent->name : "[other]"); |
253 | 211 | } |
254 | 212 | |
213 | +struct sort_entry sort_parent = { | |
214 | + .se_header = "Parent symbol", | |
215 | + .se_cmp = sort__parent_cmp, | |
216 | + .se_snprintf = hist_entry__parent_snprintf, | |
217 | + .se_width_idx = HISTC_PARENT, | |
218 | +}; | |
219 | + | |
255 | 220 | /* --sort cpu */ |
256 | 221 | |
257 | -int64_t | |
222 | +static int64_t | |
258 | 223 | sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) |
259 | 224 | { |
260 | 225 | return right->cpu - left->cpu; |
... | ... | @@ -266,6 +231,28 @@ |
266 | 231 | return repsep_snprintf(bf, size, "%-*d", width, self->cpu); |
267 | 232 | } |
268 | 233 | |
234 | +struct sort_entry sort_cpu = { | |
235 | + .se_header = "CPU", | |
236 | + .se_cmp = sort__cpu_cmp, | |
237 | + .se_snprintf = hist_entry__cpu_snprintf, | |
238 | + .se_width_idx = HISTC_CPU, | |
239 | +}; | |
240 | + | |
241 | +struct sort_dimension { | |
242 | + const char *name; | |
243 | + struct sort_entry *entry; | |
244 | + int taken; | |
245 | +}; | |
246 | + | |
247 | +static struct sort_dimension sort_dimensions[] = { | |
248 | + { .name = "pid", .entry = &sort_thread, }, | |
249 | + { .name = "comm", .entry = &sort_comm, }, | |
250 | + { .name = "dso", .entry = &sort_dso, }, | |
251 | + { .name = "symbol", .entry = &sort_sym, }, | |
252 | + { .name = "parent", .entry = &sort_parent, }, | |
253 | + { .name = "cpu", .entry = &sort_cpu, }, | |
254 | +}; | |
255 | + | |
269 | 256 | int sort_dimension__add(const char *tok) |
270 | 257 | { |
271 | 258 | unsigned int i; |
272 | 259 | |
... | ... | @@ -273,15 +260,9 @@ |
273 | 260 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { |
274 | 261 | struct sort_dimension *sd = &sort_dimensions[i]; |
275 | 262 | |
276 | - if (sd->taken) | |
277 | - continue; | |
278 | - | |
279 | 263 | if (strncasecmp(tok, sd->name, strlen(tok))) |
280 | 264 | continue; |
281 | 265 | |
282 | - if (sd->entry->se_collapse) | |
283 | - sort__need_collapse = 1; | |
284 | - | |
285 | 266 | if (sd->entry == &sort_parent) { |
286 | 267 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); |
287 | 268 | if (ret) { |
... | ... | @@ -293,6 +274,12 @@ |
293 | 274 | } |
294 | 275 | sort__has_parent = 1; |
295 | 276 | } |
277 | + | |
278 | + if (sd->taken) | |
279 | + return 0; | |
280 | + | |
281 | + if (sd->entry->se_collapse) | |
282 | + sort__need_collapse = 1; | |
296 | 283 | |
297 | 284 | if (list_empty(&hist_entry__sort_list)) { |
298 | 285 | if (!strcmp(sd->name, "pid")) |
tools/perf/util/sort.h
... | ... | @@ -103,20 +103,6 @@ |
103 | 103 | extern struct list_head hist_entry__sort_list; |
104 | 104 | |
105 | 105 | void setup_sorting(const char * const usagestr[], const struct option *opts); |
106 | - | |
107 | -extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); | |
108 | -extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); | |
109 | -extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int); | |
110 | -extern size_t sort__sym_print(FILE *, struct hist_entry *, unsigned int __used); | |
111 | -extern int64_t cmp_null(void *, void *); | |
112 | -extern int64_t sort__thread_cmp(struct hist_entry *, struct hist_entry *); | |
113 | -extern int64_t sort__comm_cmp(struct hist_entry *, struct hist_entry *); | |
114 | -extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *); | |
115 | -extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); | |
116 | -extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); | |
117 | -extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); | |
118 | -int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right); | |
119 | -extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); | |
120 | 106 | extern int sort_dimension__add(const char *); |
121 | 107 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, |
122 | 108 | const char *list_name, FILE *fp); |