aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/builtin-report.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r--tools/perf/builtin-report.c228
1 files changed, 137 insertions, 91 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index f815de25d0f..1d3c1003b43 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -14,7 +14,6 @@
14#include "util/cache.h" 14#include "util/cache.h"
15#include <linux/rbtree.h> 15#include <linux/rbtree.h>
16#include "util/symbol.h" 16#include "util/symbol.h"
17#include "util/string.h"
18#include "util/callchain.h" 17#include "util/callchain.h"
19#include "util/strlist.h" 18#include "util/strlist.h"
20#include "util/values.h" 19#include "util/values.h"
@@ -33,28 +32,29 @@
33 32
34static char const *input_name = "perf.data"; 33static char const *input_name = "perf.data";
35 34
36static int force; 35static bool force;
37static bool hide_unresolved; 36static bool hide_unresolved;
38static bool dont_use_callchains; 37static bool dont_use_callchains;
39 38
40static int show_threads; 39static bool show_threads;
41static struct perf_read_values show_threads_values; 40static struct perf_read_values show_threads_values;
42 41
43static char default_pretty_printing_style[] = "normal"; 42static const char default_pretty_printing_style[] = "normal";
44static char *pretty_printing_style = default_pretty_printing_style; 43static const char *pretty_printing_style = default_pretty_printing_style;
45 44
46static char callchain_default_opt[] = "fractal,0.5"; 45static char callchain_default_opt[] = "fractal,0.5";
47 46
48static struct event_stat_id *get_stats(struct perf_session *self, 47static struct hists *perf_session__hists_findnew(struct perf_session *self,
49 u64 event_stream, u32 type, u64 config) 48 u64 event_stream, u32 type,
49 u64 config)
50{ 50{
51 struct rb_node **p = &self->stats_by_id.rb_node; 51 struct rb_node **p = &self->hists_tree.rb_node;
52 struct rb_node *parent = NULL; 52 struct rb_node *parent = NULL;
53 struct event_stat_id *iter, *new; 53 struct hists *iter, *new;
54 54
55 while (*p != NULL) { 55 while (*p != NULL) {
56 parent = *p; 56 parent = *p;
57 iter = rb_entry(parent, struct event_stat_id, rb_node); 57 iter = rb_entry(parent, struct hists, rb_node);
58 if (iter->config == config) 58 if (iter->config == config)
59 return iter; 59 return iter;
60 60
@@ -65,15 +65,15 @@ static struct event_stat_id *get_stats(struct perf_session *self,
65 p = &(*p)->rb_left; 65 p = &(*p)->rb_left;
66 } 66 }
67 67
68 new = malloc(sizeof(struct event_stat_id)); 68 new = malloc(sizeof(struct hists));
69 if (new == NULL) 69 if (new == NULL)
70 return NULL; 70 return NULL;
71 memset(new, 0, sizeof(struct event_stat_id)); 71 memset(new, 0, sizeof(struct hists));
72 new->event_stream = event_stream; 72 new->event_stream = event_stream;
73 new->config = config; 73 new->config = config;
74 new->type = type; 74 new->type = type;
75 rb_link_node(&new->rb_node, parent, p); 75 rb_link_node(&new->rb_node, parent, p);
76 rb_insert_color(&new->rb_node, &self->stats_by_id); 76 rb_insert_color(&new->rb_node, &self->hists_tree);
77 return new; 77 return new;
78} 78}
79 79
@@ -81,70 +81,71 @@ static int perf_session__add_hist_entry(struct perf_session *self,
81 struct addr_location *al, 81 struct addr_location *al,
82 struct sample_data *data) 82 struct sample_data *data)
83{ 83{
84 struct symbol **syms = NULL, *parent = NULL; 84 struct map_symbol *syms = NULL;
85 bool hit; 85 struct symbol *parent = NULL;
86 int err = -ENOMEM;
86 struct hist_entry *he; 87 struct hist_entry *he;
87 struct event_stat_id *stats; 88 struct hists *hists;
88 struct perf_event_attr *attr; 89 struct perf_event_attr *attr;
89 90
90 if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) 91 if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) {
91 syms = perf_session__resolve_callchain(self, al->thread, 92 syms = perf_session__resolve_callchain(self, al->thread,
92 data->callchain, &parent); 93 data->callchain, &parent);
94 if (syms == NULL)
95 return -ENOMEM;
96 }
93 97
94 attr = perf_header__find_attr(data->id, &self->header); 98 attr = perf_header__find_attr(data->id, &self->header);
95 if (attr) 99 if (attr)
96 stats = get_stats(self, data->id, attr->type, attr->config); 100 hists = perf_session__hists_findnew(self, data->id, attr->type, attr->config);
97 else 101 else
98 stats = get_stats(self, data->id, 0, 0); 102 hists = perf_session__hists_findnew(self, data->id, 0, 0);
99 if (stats == NULL) 103 if (hists == NULL)
100 return -ENOMEM; 104 goto out_free_syms;
101 he = __perf_session__add_hist_entry(&stats->hists, al, parent, 105 he = __hists__add_entry(hists, al, parent, data->period);
102 data->period, &hit);
103 if (he == NULL) 106 if (he == NULL)
104 return -ENOMEM; 107 goto out_free_syms;
105 108 err = 0;
106 if (hit)
107 he->count += data->period;
108
109 if (symbol_conf.use_callchain) { 109 if (symbol_conf.use_callchain) {
110 if (!hit) 110 err = append_chain(he->callchain, data->callchain, syms);
111 callchain_init(&he->callchain); 111 if (err)
112 append_chain(&he->callchain, data->callchain, syms); 112 goto out_free_syms;
113 free(syms);
114 } 113 }
115 114 /*
116 return 0; 115 * Only in the newt browser we are doing integrated annotation,
117} 116 * so we don't allocated the extra space needed because the stdio
118 117 * code will not use it.
119static int validate_chain(struct ip_callchain *chain, event_t *event) 118 */
120{ 119 if (use_browser)
121 unsigned int chain_size; 120 err = hist_entry__inc_addr_samples(he, al->addr);
122 121out_free_syms:
123 chain_size = event->header.size; 122 free(syms);
124 chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; 123 return err;
125
126 if (chain->nr*sizeof(u64) > chain_size)
127 return -1;
128
129 return 0;
130} 124}
131 125
132static int add_event_total(struct perf_session *session, 126static int add_event_total(struct perf_session *session,
133 struct sample_data *data, 127 struct sample_data *data,
134 struct perf_event_attr *attr) 128 struct perf_event_attr *attr)
135{ 129{
136 struct event_stat_id *stats; 130 struct hists *hists;
137 131
138 if (attr) 132 if (attr)
139 stats = get_stats(session, data->id, attr->type, attr->config); 133 hists = perf_session__hists_findnew(session, data->id,
134 attr->type, attr->config);
140 else 135 else
141 stats = get_stats(session, data->id, 0, 0); 136 hists = perf_session__hists_findnew(session, data->id, 0, 0);
142 137
143 if (!stats) 138 if (!hists)
144 return -ENOMEM; 139 return -ENOMEM;
145 140
146 stats->stats.total += data->period; 141 hists->stats.total_period += data->period;
147 session->events_stats.total += data->period; 142 /*
143 * FIXME: add_event_total should be moved from here to
144 * perf_session__process_event so that the proper hist is passed to
145 * the event_op methods.
146 */
147 hists__inc_nr_events(hists, PERF_RECORD_SAMPLE);
148 session->hists.stats.total_period += data->period;
148 return 0; 149 return 0;
149} 150}
150 151
@@ -164,7 +165,7 @@ static int process_sample_event(event_t *event, struct perf_session *session)
164 165
165 dump_printf("... chain: nr:%Lu\n", data.callchain->nr); 166 dump_printf("... chain: nr:%Lu\n", data.callchain->nr);
166 167
167 if (validate_chain(data.callchain, event) < 0) { 168 if (!ip_callchain__valid(data.callchain, event)) {
168 pr_debug("call-chain problem with event, " 169 pr_debug("call-chain problem with event, "
169 "skipping it.\n"); 170 "skipping it.\n");
170 return 0; 171 return 0;
@@ -187,14 +188,14 @@ static int process_sample_event(event_t *event, struct perf_session *session)
187 return 0; 188 return 0;
188 189
189 if (perf_session__add_hist_entry(session, &al, &data)) { 190 if (perf_session__add_hist_entry(session, &al, &data)) {
190 pr_debug("problem incrementing symbol count, skipping event\n"); 191 pr_debug("problem incrementing symbol period, skipping event\n");
191 return -1; 192 return -1;
192 } 193 }
193 194
194 attr = perf_header__find_attr(data.id, &session->header); 195 attr = perf_header__find_attr(data.id, &session->header);
195 196
196 if (add_event_total(session, &data, attr)) { 197 if (add_event_total(session, &data, attr)) {
197 pr_debug("problem adding event count\n"); 198 pr_debug("problem adding event period\n");
198 return -1; 199 return -1;
199 } 200 }
200 201
@@ -260,15 +261,43 @@ static struct perf_event_ops event_ops = {
260 .fork = event__process_task, 261 .fork = event__process_task,
261 .lost = event__process_lost, 262 .lost = event__process_lost,
262 .read = process_read_event, 263 .read = process_read_event,
264 .attr = event__process_attr,
265 .event_type = event__process_event_type,
266 .tracing_data = event__process_tracing_data,
267 .build_id = event__process_build_id,
263}; 268};
264 269
270extern volatile int session_done;
271
272static void sig_handler(int sig __used)
273{
274 session_done = 1;
275}
276
277static size_t hists__fprintf_nr_sample_events(struct hists *self,
278 const char *evname, FILE *fp)
279{
280 size_t ret;
281 char unit;
282 unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
283
284 nr_events = convert_unit(nr_events, &unit);
285 ret = fprintf(fp, "# Events: %lu%c", nr_events, unit);
286 if (evname != NULL)
287 ret += fprintf(fp, " %s", evname);
288 return ret + fprintf(fp, "\n#\n");
289}
290
265static int __cmd_report(void) 291static int __cmd_report(void)
266{ 292{
267 int ret = -EINVAL; 293 int ret = -EINVAL;
268 struct perf_session *session; 294 struct perf_session *session;
269 struct rb_node *next; 295 struct rb_node *next;
296 const char *help = "For a higher level overview, try: perf report --sort comm,dso";
297
298 signal(SIGINT, sig_handler);
270 299
271 session = perf_session__new(input_name, O_RDONLY, force); 300 session = perf_session__new(input_name, O_RDONLY, force, false);
272 if (session == NULL) 301 if (session == NULL)
273 return -ENOMEM; 302 return -ENOMEM;
274 303
@@ -284,7 +313,7 @@ static int __cmd_report(void)
284 goto out_delete; 313 goto out_delete;
285 314
286 if (dump_trace) { 315 if (dump_trace) {
287 event__print_totals(); 316 perf_session__fprintf_nr_events(session, stdout);
288 goto out_delete; 317 goto out_delete;
289 } 318 }
290 319
@@ -292,39 +321,42 @@ static int __cmd_report(void)
292 perf_session__fprintf(session, stdout); 321 perf_session__fprintf(session, stdout);
293 322
294 if (verbose > 2) 323 if (verbose > 2)
295 dsos__fprintf(stdout); 324 perf_session__fprintf_dsos(session, stdout);
296 325
297 next = rb_first(&session->stats_by_id); 326 next = rb_first(&session->hists_tree);
298 while (next) { 327 while (next) {
299 struct event_stat_id *stats; 328 struct hists *hists;
300 329
301 stats = rb_entry(next, struct event_stat_id, rb_node); 330 hists = rb_entry(next, struct hists, rb_node);
302 perf_session__collapse_resort(&stats->hists); 331 hists__collapse_resort(hists);
303 perf_session__output_resort(&stats->hists, stats->stats.total); 332 hists__output_resort(hists);
304 if (rb_first(&session->stats_by_id) == 333 if (use_browser)
305 rb_last(&session->stats_by_id)) 334 hists__browse(hists, help, input_name);
306 fprintf(stdout, "# Samples: %Ld\n#\n", 335 else {
307 stats->stats.total); 336 const char *evname = NULL;
308 else 337 if (rb_first(&session->hists.entries) !=
309 fprintf(stdout, "# Samples: %Ld %s\n#\n", 338 rb_last(&session->hists.entries))
310 stats->stats.total, 339 evname = __event_name(hists->type, hists->config);
311 __event_name(stats->type, stats->config)); 340
312 341 hists__fprintf_nr_sample_events(hists, evname, stdout);
313 perf_session__fprintf_hists(&stats->hists, NULL, false, stdout, 342
314 stats->stats.total); 343 hists__fprintf(hists, NULL, false, stdout);
315 fprintf(stdout, "\n\n"); 344 fprintf(stdout, "\n\n");
316 next = rb_next(&stats->rb_node); 345 }
346
347 next = rb_next(&hists->rb_node);
317 } 348 }
318 349
319 if (sort_order == default_sort_order && 350 if (!use_browser && sort_order == default_sort_order &&
320 parent_pattern == default_parent_pattern) 351 parent_pattern == default_parent_pattern) {
321 fprintf(stdout, "#\n# (For a higher level overview, try: perf report --sort comm,dso)\n#\n"); 352 fprintf(stdout, "#\n# (%s)\n#\n", help);
322 353
323 if (show_threads) { 354 if (show_threads) {
324 bool raw_printing_style = !strcmp(pretty_printing_style, "raw"); 355 bool style = !strcmp(pretty_printing_style, "raw");
325 perf_read_values_display(stdout, &show_threads_values, 356 perf_read_values_display(stdout, &show_threads_values,
326 raw_printing_style); 357 style);
327 perf_read_values_destroy(&show_threads_values); 358 perf_read_values_destroy(&show_threads_values);
359 }
328 } 360 }
329out_delete: 361out_delete:
330 perf_session__delete(session); 362 perf_session__delete(session);
@@ -335,7 +367,7 @@ static int
335parse_callchain_opt(const struct option *opt __used, const char *arg, 367parse_callchain_opt(const struct option *opt __used, const char *arg,
336 int unset) 368 int unset)
337{ 369{
338 char *tok; 370 char *tok, *tok2;
339 char *endptr; 371 char *endptr;
340 372
341 /* 373 /*
@@ -380,10 +412,13 @@ parse_callchain_opt(const struct option *opt __used, const char *arg,
380 if (!tok) 412 if (!tok)
381 goto setup; 413 goto setup;
382 414
415 tok2 = strtok(NULL, ",");
383 callchain_param.min_percent = strtod(tok, &endptr); 416 callchain_param.min_percent = strtod(tok, &endptr);
384 if (tok == endptr) 417 if (tok == endptr)
385 return -1; 418 return -1;
386 419
420 if (tok2)
421 callchain_param.print_limit = strtod(tok2, &endptr);
387setup: 422setup:
388 if (register_callchain_param(&callchain_param) < 0) { 423 if (register_callchain_param(&callchain_param) < 0) {
389 fprintf(stderr, "Can't register callchain params\n"); 424 fprintf(stderr, "Can't register callchain params\n");
@@ -400,7 +435,7 @@ static const char * const report_usage[] = {
400static const struct option options[] = { 435static const struct option options[] = {
401 OPT_STRING('i', "input", &input_name, "file", 436 OPT_STRING('i', "input", &input_name, "file",
402 "input file name"), 437 "input file name"),
403 OPT_BOOLEAN('v', "verbose", &verbose, 438 OPT_INCR('v', "verbose", &verbose,
404 "be more verbose (show symbol address, etc)"), 439 "be more verbose (show symbol address, etc)"),
405 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 440 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
406 "dump raw trace in ASCII"), 441 "dump raw trace in ASCII"),
@@ -419,12 +454,14 @@ static const struct option options[] = {
419 "sort by key(s): pid, comm, dso, symbol, parent"), 454 "sort by key(s): pid, comm, dso, symbol, parent"),
420 OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, 455 OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths,
421 "Don't shorten the pathnames taking into account the cwd"), 456 "Don't shorten the pathnames taking into account the cwd"),
457 OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
458 "Show sample percentage for different cpu modes"),
422 OPT_STRING('p', "parent", &parent_pattern, "regex", 459 OPT_STRING('p', "parent", &parent_pattern, "regex",
423 "regex filter to identify parent, see: '--sort parent'"), 460 "regex filter to identify parent, see: '--sort parent'"),
424 OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, 461 OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
425 "Only display entries with parent-match"), 462 "Only display entries with parent-match"),
426 OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", 463 OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent",
427 "Display callchains using output_type and min percent threshold. " 464 "Display callchains using output_type (graph, flat, fractal, or none) and min percent threshold. "
428 "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), 465 "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt),
429 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", 466 OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
430 "only consider symbols in these dsos"), 467 "only consider symbols in these dsos"),
@@ -447,7 +484,15 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
447{ 484{
448 argc = parse_options(argc, argv, options, report_usage, 0); 485 argc = parse_options(argc, argv, options, report_usage, 0);
449 486
450 setup_pager(); 487 if (strcmp(input_name, "-") != 0)
488 setup_browser();
489 /*
490 * Only in the newt browser we are doing integrated annotation,
491 * so don't allocate extra space that won't be used in the stdio
492 * implementation.
493 */
494 if (use_browser)
495 symbol_conf.priv_size = sizeof(struct sym_priv);
451 496
452 if (symbol__init() < 0) 497 if (symbol__init() < 0)
453 return -1; 498 return -1;
@@ -455,7 +500,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
455 setup_sorting(report_usage, options); 500 setup_sorting(report_usage, options);
456 501
457 if (parent_pattern != default_parent_pattern) { 502 if (parent_pattern != default_parent_pattern) {
458 sort_dimension__add("parent"); 503 if (sort_dimension__add("parent") < 0)
504 return -1;
459 sort_parent.elide = 1; 505 sort_parent.elide = 1;
460 } else 506 } else
461 symbol_conf.exclude_other = false; 507 symbol_conf.exclude_other = false;