aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/ui/stdio/hist.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-10-01 13:28:49 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-01 13:28:49 -0400
commit7e92daaefa68e5ef1e1732e45231e73adbb724e7 (patch)
tree8e7f8ac9d82654df4c65939c6682f95510e22977 /tools/perf/ui/stdio/hist.c
parent7a68294278ae714ce2632a54f0f46916dca64f56 (diff)
parent1d787d37c8ff6612b8151c6dff15bfa7347bcbdf (diff)
Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf update from Ingo Molnar: "Lots of changes in this cycle as well, with hundreds of commits from over 30 contributors. Most of the activity was on the tooling side. Higher level changes: - New 'perf kvm' analysis tool, from Xiao Guangrong. - New 'perf trace' system-wide tracing tool - uprobes fixes + cleanups from Oleg Nesterov. - Lots of patches to make perf build on Android out of box, from Irina Tirdea - Extend ftrace function tracing utility to be more dynamic for its users. It allows for data passing to the callback functions, as well as reading regs as if a breakpoint were to trigger at function entry. The main goal of this patch series was to allow kprobes to use ftrace as an optimized probe point when a probe is placed on an ftrace nop. With lots of help from Masami Hiramatsu, and going through lots of iterations, we finally came up with a good solution. - Add cpumask for uncore pmu, use it in 'stat', from Yan, Zheng. - Various tracing updates from Steve Rostedt - Clean up and improve 'perf sched' performance by elliminating lots of needless calls to libtraceevent. - Event group parsing support, from Jiri Olsa - UI/gtk refactorings and improvements from Namhyung Kim - Add support for non-tracepoint events in perf script python, from Feng Tang - Add --symbols to 'script', similar to the one in 'report', from Feng Tang. Infrastructure enhancements and fixes: - Convert the trace builtins to use the growing evsel/evlist tracepoint infrastructure, removing several open coded constructs like switch like series of strcmp to dispatch events, etc. Basically what had already been showcased in 'perf sched'. - Add evsel constructor for tracepoints, that uses libtraceevent just to parse the /format events file, use it in a new 'perf test' to make sure the libtraceevent format parsing regressions can be more readily caught. - Some strange errors were happening in some builds, but not on the next, reported by several people, problem was some parser related files, generated during the build, didn't had proper make deps, fix from Eric Sandeen. - Introduce struct and cache information about the environment where a perf.data file was captured, from Namhyung Kim. - Fix handling of unresolved samples when --symbols is used in 'report', from Feng Tang. - Add union member access support to 'probe', from Hyeoncheol Lee. - Fixups to die() removal, from Namhyung Kim. - Render fixes for the TUI, from Namhyung Kim. - Don't enable annotation in non symbolic view, from Namhyung Kim. - Fix pipe mode in 'report', from Namhyung Kim. - Move related stats code from stat to util/, will be used by the 'stat' kvm tool, from Xiao Guangrong. - Remove die()/exit() calls from several tools. - Resolve vdso callchains, from Jiri Olsa - Don't pass const char pointers to basename, so that we can unconditionally use libgen.h and thus avoid ifdef BIONIC lines, from David Ahern - Refactor hist formatting so that it can be reused with the GTK browser, From Namhyung Kim - Fix build for another rbtree.c change, from Adrian Hunter. - Make 'perf diff' command work with evsel hists, from Jiri Olsa. - Use the only field_sep var that is set up: symbol_conf.field_sep, fix from Jiri Olsa. - .gitignore compiled python binaries, from Namhyung Kim. - Get rid of die() in more libtraceevent places, from Namhyung Kim. - Rename libtraceevent 'private' struct member to 'priv' so that it works in C++, from Steven Rostedt - Remove lots of exit()/die() calls from tools so that the main perf exit routine can take place, from David Ahern - Fix x86 build on x86-64, from David Ahern. - {int,str,rb}list fixes from Suzuki K Poulose - perf.data header fixes from Namhyung Kim - Allow user to indicate objdump path, needed in cross environments, from Maciek Borzecki - Fix hardware cache event name generation, fix from Jiri Olsa - Add round trip test for sw, hw and cache event names, catching the problem Jiri fixed, after Jiri's patch, the test passes successfully. - Clean target should do clean for lib/traceevent too, fix from David Ahern - Check the right variable for allocation failure, fix from Namhyung Kim - Set up evsel->tp_format regardless of evsel->name being set already, fix from Namhyung Kim - Oprofile fixes from Robert Richter. - Remove perf_event_attr needless version inflation, from Jiri Olsa - Introduce libtraceevent strerror like error reporting facility, from Namhyung Kim - Add pmu mappings to perf.data header and use event names from cmd line, from Robert Richter - Fix include order for bison/flex-generated C files, from Ben Hutchings - Build fixes and documentation corrections from David Ahern - Assorted cleanups from Robert Richter - Let O= makes handle relative paths, from Steven Rostedt - perf script python fixes, from Feng Tang. - Initial bash completion support, from Frederic Weisbecker - Allow building without libelf, from Namhyung Kim. - Support DWARF CFI based unwind to have callchains when %bp based unwinding is not possible, from Jiri Olsa. - Symbol resolution fixes, while fixing support PPC64 files with an .opt ELF section was the end goal, several fixes for code that handles all architectures and cleanups are included, from Cody Schafer. - Assorted fixes for Documentation and build in 32 bit, from Robert Richter - Cache the libtraceevent event_format associated to each evsel early, so that we avoid relookups, i.e. calling pevent_find_event repeatedly when processing tracepoint events. [ This is to reduce the surface contact with libtraceevents and make clear what is that the perf tools needs from that lib: so far parsing the common and per event fields. ] - Don't stop the build if the audit libraries are not installed, fix from Namhyung Kim. - Fix bfd.h/libbfd detection with recent binutils, from Markus Trippelsdorf. - Improve warning message when libunwind devel packages not present, from Jiri Olsa" * 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (282 commits) perf trace: Add aliases for some syscalls perf probe: Print an enum type variable in "enum variable-name" format when showing accessible variables perf tools: Check libaudit availability for perf-trace builtin perf hists: Add missing period_* fields when collapsing a hist entry perf trace: New tool perf evsel: Export the event_format constructor perf evsel: Introduce rawptr() method perf tools: Use perf_evsel__newtp in the event parser perf evsel: The tracepoint constructor should store sys:name perf evlist: Introduce set_filter() method perf evlist: Renane set_filters method to apply_filters perf test: Add test to check we correctly parse and match syscall open parms perf evsel: Handle endianity in intval method perf evsel: Know if byte swap is needed perf tools: Allow handling a NULL cpu_map as meaning "all cpus" perf evsel: Improve tracepoint constructor setup tools lib traceevent: Fix error path on pevent_parse_event perf test: Fix build failure trace: Move trace event enable from fs_initcall to core_initcall tracing: Add an option for disabling markers ...
Diffstat (limited to 'tools/perf/ui/stdio/hist.c')
-rw-r--r--tools/perf/ui/stdio/hist.c498
1 files changed, 498 insertions, 0 deletions
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
new file mode 100644
index 000000000000..882461a42830
--- /dev/null
+++ b/tools/perf/ui/stdio/hist.c
@@ -0,0 +1,498 @@
1#include <stdio.h>
2
3#include "../../util/util.h"
4#include "../../util/hist.h"
5#include "../../util/sort.h"
6
7
8static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
9{
10 int i;
11 int ret = fprintf(fp, " ");
12
13 for (i = 0; i < left_margin; i++)
14 ret += fprintf(fp, " ");
15
16 return ret;
17}
18
19static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
20 int left_margin)
21{
22 int i;
23 size_t ret = callchain__fprintf_left_margin(fp, left_margin);
24
25 for (i = 0; i < depth; i++)
26 if (depth_mask & (1 << i))
27 ret += fprintf(fp, "| ");
28 else
29 ret += fprintf(fp, " ");
30
31 ret += fprintf(fp, "\n");
32
33 return ret;
34}
35
36static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
37 int depth, int depth_mask, int period,
38 u64 total_samples, u64 hits,
39 int left_margin)
40{
41 int i;
42 size_t ret = 0;
43
44 ret += callchain__fprintf_left_margin(fp, left_margin);
45 for (i = 0; i < depth; i++) {
46 if (depth_mask & (1 << i))
47 ret += fprintf(fp, "|");
48 else
49 ret += fprintf(fp, " ");
50 if (!period && i == depth - 1) {
51 double percent;
52
53 percent = hits * 100.0 / total_samples;
54 ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
55 } else
56 ret += fprintf(fp, "%s", " ");
57 }
58 if (chain->ms.sym)
59 ret += fprintf(fp, "%s\n", chain->ms.sym->name);
60 else
61 ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip);
62
63 return ret;
64}
65
66static struct symbol *rem_sq_bracket;
67static struct callchain_list rem_hits;
68
69static void init_rem_hits(void)
70{
71 rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
72 if (!rem_sq_bracket) {
73 fprintf(stderr, "Not enough memory to display remaining hits\n");
74 return;
75 }
76
77 strcpy(rem_sq_bracket->name, "[...]");
78 rem_hits.ms.sym = rem_sq_bracket;
79}
80
81static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
82 u64 total_samples, int depth,
83 int depth_mask, int left_margin)
84{
85 struct rb_node *node, *next;
86 struct callchain_node *child;
87 struct callchain_list *chain;
88 int new_depth_mask = depth_mask;
89 u64 remaining;
90 size_t ret = 0;
91 int i;
92 uint entries_printed = 0;
93
94 remaining = total_samples;
95
96 node = rb_first(root);
97 while (node) {
98 u64 new_total;
99 u64 cumul;
100
101 child = rb_entry(node, struct callchain_node, rb_node);
102 cumul = callchain_cumul_hits(child);
103 remaining -= cumul;
104
105 /*
106 * The depth mask manages the output of pipes that show
107 * the depth. We don't want to keep the pipes of the current
108 * level for the last child of this depth.
109 * Except if we have remaining filtered hits. They will
110 * supersede the last child
111 */
112 next = rb_next(node);
113 if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
114 new_depth_mask &= ~(1 << (depth - 1));
115
116 /*
117 * But we keep the older depth mask for the line separator
118 * to keep the level link until we reach the last child
119 */
120 ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
121 left_margin);
122 i = 0;
123 list_for_each_entry(chain, &child->val, list) {
124 ret += ipchain__fprintf_graph(fp, chain, depth,
125 new_depth_mask, i++,
126 total_samples,
127 cumul,
128 left_margin);
129 }
130
131 if (callchain_param.mode == CHAIN_GRAPH_REL)
132 new_total = child->children_hit;
133 else
134 new_total = total_samples;
135
136 ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
137 depth + 1,
138 new_depth_mask | (1 << depth),
139 left_margin);
140 node = next;
141 if (++entries_printed == callchain_param.print_limit)
142 break;
143 }
144
145 if (callchain_param.mode == CHAIN_GRAPH_REL &&
146 remaining && remaining != total_samples) {
147
148 if (!rem_sq_bracket)
149 return ret;
150
151 new_depth_mask &= ~(1 << (depth - 1));
152 ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
153 new_depth_mask, 0, total_samples,
154 remaining, left_margin);
155 }
156
157 return ret;
158}
159
160static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
161 u64 total_samples, int left_margin)
162{
163 struct callchain_node *cnode;
164 struct callchain_list *chain;
165 u32 entries_printed = 0;
166 bool printed = false;
167 struct rb_node *node;
168 int i = 0;
169 int ret = 0;
170
171 /*
172 * If have one single callchain root, don't bother printing
173 * its percentage (100 % in fractal mode and the same percentage
174 * than the hist in graph mode). This also avoid one level of column.
175 */
176 node = rb_first(root);
177 if (node && !rb_next(node)) {
178 cnode = rb_entry(node, struct callchain_node, rb_node);
179 list_for_each_entry(chain, &cnode->val, list) {
180 /*
181 * If we sort by symbol, the first entry is the same than
182 * the symbol. No need to print it otherwise it appears as
183 * displayed twice.
184 */
185 if (!i++ && sort__first_dimension == SORT_SYM)
186 continue;
187 if (!printed) {
188 ret += callchain__fprintf_left_margin(fp, left_margin);
189 ret += fprintf(fp, "|\n");
190 ret += callchain__fprintf_left_margin(fp, left_margin);
191 ret += fprintf(fp, "---");
192 left_margin += 3;
193 printed = true;
194 } else
195 ret += callchain__fprintf_left_margin(fp, left_margin);
196
197 if (chain->ms.sym)
198 ret += fprintf(fp, " %s\n", chain->ms.sym->name);
199 else
200 ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
201
202 if (++entries_printed == callchain_param.print_limit)
203 break;
204 }
205 root = &cnode->rb_root;
206 }
207
208 ret += __callchain__fprintf_graph(fp, root, total_samples,
209 1, 1, left_margin);
210 ret += fprintf(fp, "\n");
211
212 return ret;
213}
214
215static size_t __callchain__fprintf_flat(FILE *fp,
216 struct callchain_node *self,
217 u64 total_samples)
218{
219 struct callchain_list *chain;
220 size_t ret = 0;
221
222 if (!self)
223 return 0;
224
225 ret += __callchain__fprintf_flat(fp, self->parent, total_samples);
226
227
228 list_for_each_entry(chain, &self->val, list) {
229 if (chain->ip >= PERF_CONTEXT_MAX)
230 continue;
231 if (chain->ms.sym)
232 ret += fprintf(fp, " %s\n", chain->ms.sym->name);
233 else
234 ret += fprintf(fp, " %p\n",
235 (void *)(long)chain->ip);
236 }
237
238 return ret;
239}
240
241static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self,
242 u64 total_samples)
243{
244 size_t ret = 0;
245 u32 entries_printed = 0;
246 struct rb_node *rb_node;
247 struct callchain_node *chain;
248
249 rb_node = rb_first(self);
250 while (rb_node) {
251 double percent;
252
253 chain = rb_entry(rb_node, struct callchain_node, rb_node);
254 percent = chain->hit * 100.0 / total_samples;
255
256 ret = percent_color_fprintf(fp, " %6.2f%%\n", percent);
257 ret += __callchain__fprintf_flat(fp, chain, total_samples);
258 ret += fprintf(fp, "\n");
259 if (++entries_printed == callchain_param.print_limit)
260 break;
261
262 rb_node = rb_next(rb_node);
263 }
264
265 return ret;
266}
267
268static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
269 u64 total_samples, int left_margin,
270 FILE *fp)
271{
272 switch (callchain_param.mode) {
273 case CHAIN_GRAPH_REL:
274 return callchain__fprintf_graph(fp, &he->sorted_chain, he->period,
275 left_margin);
276 break;
277 case CHAIN_GRAPH_ABS:
278 return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
279 left_margin);
280 break;
281 case CHAIN_FLAT:
282 return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
283 break;
284 case CHAIN_NONE:
285 break;
286 default:
287 pr_err("Bad callchain mode\n");
288 }
289
290 return 0;
291}
292
293static size_t hist_entry__callchain_fprintf(struct hist_entry *he,
294 struct hists *hists,
295 u64 total_period, FILE *fp)
296{
297 int left_margin = 0;
298
299 if (sort__first_dimension == SORT_COMM) {
300 struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
301 typeof(*se), list);
302 left_margin = hists__col_len(hists, se->se_width_idx);
303 left_margin -= thread__comm_len(he->thread);
304 }
305
306 return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
307}
308
309static int hist_entry__fprintf(struct hist_entry *he, size_t size,
310 struct hists *hists, struct hists *pair_hists,
311 long displacement, u64 total_period, FILE *fp)
312{
313 char bf[512];
314 int ret;
315 struct perf_hpp hpp = {
316 .buf = bf,
317 .size = size,
318 .total_period = total_period,
319 .displacement = displacement,
320 .ptr = pair_hists,
321 };
322 bool color = !symbol_conf.field_sep;
323
324 if (size == 0 || size > sizeof(bf))
325 size = hpp.size = sizeof(bf);
326
327 ret = hist_entry__period_snprintf(&hpp, he, color);
328 hist_entry__sort_snprintf(he, bf + ret, size - ret, hists);
329
330 ret = fprintf(fp, "%s\n", bf);
331
332 if (symbol_conf.use_callchain)
333 ret += hist_entry__callchain_fprintf(he, hists,
334 total_period, fp);
335
336 return ret;
337}
338
339size_t hists__fprintf(struct hists *hists, struct hists *pair,
340 bool show_displacement, bool show_header, int max_rows,
341 int max_cols, FILE *fp)
342{
343 struct sort_entry *se;
344 struct rb_node *nd;
345 size_t ret = 0;
346 u64 total_period;
347 unsigned long position = 1;
348 long displacement = 0;
349 unsigned int width;
350 const char *sep = symbol_conf.field_sep;
351 const char *col_width = symbol_conf.col_width_list_str;
352 int idx, nr_rows = 0;
353 char bf[64];
354 struct perf_hpp dummy_hpp = {
355 .buf = bf,
356 .size = sizeof(bf),
357 .ptr = pair,
358 };
359
360 init_rem_hits();
361
362 if (!show_header)
363 goto print_entries;
364
365 fprintf(fp, "# ");
366 for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) {
367 if (!perf_hpp__format[idx].cond)
368 continue;
369
370 if (idx)
371 fprintf(fp, "%s", sep ?: " ");
372
373 perf_hpp__format[idx].header(&dummy_hpp);
374 fprintf(fp, "%s", bf);
375 }
376
377 list_for_each_entry(se, &hist_entry__sort_list, list) {
378 if (se->elide)
379 continue;
380 if (sep) {
381 fprintf(fp, "%c%s", *sep, se->se_header);
382 continue;
383 }
384 width = strlen(se->se_header);
385 if (symbol_conf.col_width_list_str) {
386 if (col_width) {
387 hists__set_col_len(hists, se->se_width_idx,
388 atoi(col_width));
389 col_width = strchr(col_width, ',');
390 if (col_width)
391 ++col_width;
392 }
393 }
394 if (!hists__new_col_len(hists, se->se_width_idx, width))
395 width = hists__col_len(hists, se->se_width_idx);
396 fprintf(fp, " %*s", width, se->se_header);
397 }
398
399 fprintf(fp, "\n");
400 if (max_rows && ++nr_rows >= max_rows)
401 goto out;
402
403 if (sep)
404 goto print_entries;
405
406 fprintf(fp, "# ");
407 for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) {
408 unsigned int i;
409
410 if (!perf_hpp__format[idx].cond)
411 continue;
412
413 if (idx)
414 fprintf(fp, "%s", sep ?: " ");
415
416 width = perf_hpp__format[idx].width(&dummy_hpp);
417 for (i = 0; i < width; i++)
418 fprintf(fp, ".");
419 }
420
421 list_for_each_entry(se, &hist_entry__sort_list, list) {
422 unsigned int i;
423
424 if (se->elide)
425 continue;
426
427 fprintf(fp, " ");
428 width = hists__col_len(hists, se->se_width_idx);
429 if (width == 0)
430 width = strlen(se->se_header);
431 for (i = 0; i < width; i++)
432 fprintf(fp, ".");
433 }
434
435 fprintf(fp, "\n");
436 if (max_rows && ++nr_rows >= max_rows)
437 goto out;
438
439 fprintf(fp, "#\n");
440 if (max_rows && ++nr_rows >= max_rows)
441 goto out;
442
443print_entries:
444 total_period = hists->stats.total_period;
445
446 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
447 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
448
449 if (h->filtered)
450 continue;
451
452 if (show_displacement) {
453 if (h->pair != NULL)
454 displacement = ((long)h->pair->position -
455 (long)position);
456 else
457 displacement = 0;
458 ++position;
459 }
460 ret += hist_entry__fprintf(h, max_cols, hists, pair, displacement,
461 total_period, fp);
462
463 if (max_rows && ++nr_rows >= max_rows)
464 goto out;
465
466 if (h->ms.map == NULL && verbose > 1) {
467 __map_groups__fprintf_maps(&h->thread->mg,
468 MAP__FUNCTION, verbose, fp);
469 fprintf(fp, "%.10s end\n", graph_dotted_line);
470 }
471 }
472out:
473 free(rem_sq_bracket);
474
475 return ret;
476}
477
478size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
479{
480 int i;
481 size_t ret = 0;
482
483 for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
484 const char *name;
485
486 if (hists->stats.nr_events[i] == 0)
487 continue;
488
489 name = perf_event__name(i);
490 if (!strcmp(name, "UNKNOWN"))
491 continue;
492
493 ret += fprintf(fp, "%16s events: %10d\n", name,
494 hists->stats.nr_events[i]);
495 }
496
497 return ret;
498}