Commit 343a031f3c4a7a663192cf56368bb5a6c56870c0

Authored by Ingo Molnar

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);