diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r-- | tools/perf/builtin-report.c | 225 |
1 files changed, 92 insertions, 133 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index c27e31f289e6..b1b82009ab9b 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -9,6 +9,7 @@ | |||
9 | 9 | ||
10 | #include "util/util.h" | 10 | #include "util/util.h" |
11 | 11 | ||
12 | #include "util/annotate.h" | ||
12 | #include "util/color.h" | 13 | #include "util/color.h" |
13 | #include <linux/list.h> | 14 | #include <linux/list.h> |
14 | #include "util/cache.h" | 15 | #include "util/cache.h" |
@@ -20,6 +21,8 @@ | |||
20 | 21 | ||
21 | #include "perf.h" | 22 | #include "perf.h" |
22 | #include "util/debug.h" | 23 | #include "util/debug.h" |
24 | #include "util/evlist.h" | ||
25 | #include "util/evsel.h" | ||
23 | #include "util/header.h" | 26 | #include "util/header.h" |
24 | #include "util/session.h" | 27 | #include "util/session.h" |
25 | 28 | ||
@@ -43,120 +46,79 @@ static const char default_pretty_printing_style[] = "normal"; | |||
43 | static const char *pretty_printing_style = default_pretty_printing_style; | 46 | static const char *pretty_printing_style = default_pretty_printing_style; |
44 | 47 | ||
45 | static char callchain_default_opt[] = "fractal,0.5"; | 48 | static char callchain_default_opt[] = "fractal,0.5"; |
49 | static symbol_filter_t annotate_init; | ||
46 | 50 | ||
47 | static struct hists *perf_session__hists_findnew(struct perf_session *self, | 51 | static int perf_session__add_hist_entry(struct perf_session *session, |
48 | u64 event_stream, u32 type, | ||
49 | u64 config) | ||
50 | { | ||
51 | struct rb_node **p = &self->hists_tree.rb_node; | ||
52 | struct rb_node *parent = NULL; | ||
53 | struct hists *iter, *new; | ||
54 | |||
55 | while (*p != NULL) { | ||
56 | parent = *p; | ||
57 | iter = rb_entry(parent, struct hists, rb_node); | ||
58 | if (iter->config == config) | ||
59 | return iter; | ||
60 | |||
61 | |||
62 | if (config > iter->config) | ||
63 | p = &(*p)->rb_right; | ||
64 | else | ||
65 | p = &(*p)->rb_left; | ||
66 | } | ||
67 | |||
68 | new = malloc(sizeof(struct hists)); | ||
69 | if (new == NULL) | ||
70 | return NULL; | ||
71 | memset(new, 0, sizeof(struct hists)); | ||
72 | new->event_stream = event_stream; | ||
73 | new->config = config; | ||
74 | new->type = type; | ||
75 | rb_link_node(&new->rb_node, parent, p); | ||
76 | rb_insert_color(&new->rb_node, &self->hists_tree); | ||
77 | return new; | ||
78 | } | ||
79 | |||
80 | static int perf_session__add_hist_entry(struct perf_session *self, | ||
81 | struct addr_location *al, | 52 | struct addr_location *al, |
82 | struct sample_data *data) | 53 | struct perf_sample *sample) |
83 | { | 54 | { |
84 | struct map_symbol *syms = NULL; | ||
85 | struct symbol *parent = NULL; | 55 | struct symbol *parent = NULL; |
86 | int err = -ENOMEM; | 56 | int err = 0; |
87 | struct hist_entry *he; | 57 | struct hist_entry *he; |
88 | struct hists *hists; | 58 | struct perf_evsel *evsel; |
89 | struct perf_event_attr *attr; | 59 | |
90 | 60 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { | |
91 | if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) { | 61 | err = perf_session__resolve_callchain(session, al->thread, |
92 | syms = perf_session__resolve_callchain(self, al->thread, | 62 | sample->callchain, &parent); |
93 | data->callchain, &parent); | 63 | if (err) |
94 | if (syms == NULL) | 64 | return err; |
95 | return -ENOMEM; | ||
96 | } | 65 | } |
97 | 66 | ||
98 | attr = perf_header__find_attr(data->id, &self->header); | 67 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); |
99 | if (attr) | 68 | if (evsel == NULL) { |
100 | hists = perf_session__hists_findnew(self, data->id, attr->type, attr->config); | 69 | /* |
101 | else | 70 | * FIXME: Propagate this back, but at least we're in a builtin, |
102 | hists = perf_session__hists_findnew(self, data->id, 0, 0); | 71 | * where exit() is allowed. ;-) |
103 | if (hists == NULL) | 72 | */ |
104 | goto out_free_syms; | 73 | ui__warning("Invalid %s file, contains samples with id %" PRIu64 " not in " |
105 | he = __hists__add_entry(hists, al, parent, data->period); | 74 | "its header!\n", input_name, sample->id); |
75 | exit_browser(0); | ||
76 | exit(1); | ||
77 | } | ||
78 | |||
79 | he = __hists__add_entry(&evsel->hists, al, parent, sample->period); | ||
106 | if (he == NULL) | 80 | if (he == NULL) |
107 | goto out_free_syms; | 81 | return -ENOMEM; |
108 | err = 0; | 82 | |
109 | if (symbol_conf.use_callchain) { | 83 | if (symbol_conf.use_callchain) { |
110 | err = callchain_append(he->callchain, data->callchain, syms, | 84 | err = callchain_append(he->callchain, &session->callchain_cursor, |
111 | data->period); | 85 | sample->period); |
112 | if (err) | 86 | if (err) |
113 | goto out_free_syms; | 87 | return err; |
114 | } | 88 | } |
115 | /* | 89 | /* |
116 | * Only in the newt browser we are doing integrated annotation, | 90 | * Only in the newt browser we are doing integrated annotation, |
117 | * so we don't allocated the extra space needed because the stdio | 91 | * so we don't allocated the extra space needed because the stdio |
118 | * code will not use it. | 92 | * code will not use it. |
119 | */ | 93 | */ |
120 | if (use_browser > 0) | 94 | if (al->sym != NULL && use_browser > 0) { |
121 | err = hist_entry__inc_addr_samples(he, al->addr); | 95 | struct annotation *notes = symbol__annotation(he->ms.sym); |
122 | out_free_syms: | ||
123 | free(syms); | ||
124 | return err; | ||
125 | } | ||
126 | 96 | ||
127 | static int add_event_total(struct perf_session *session, | 97 | assert(evsel != NULL); |
128 | struct sample_data *data, | ||
129 | struct perf_event_attr *attr) | ||
130 | { | ||
131 | struct hists *hists; | ||
132 | 98 | ||
133 | if (attr) | 99 | err = -ENOMEM; |
134 | hists = perf_session__hists_findnew(session, data->id, | 100 | if (notes->src == NULL && |
135 | attr->type, attr->config); | 101 | symbol__alloc_hist(he->ms.sym, session->evlist->nr_entries) < 0) |
136 | else | 102 | goto out; |
137 | hists = perf_session__hists_findnew(session, data->id, 0, 0); | ||
138 | 103 | ||
139 | if (!hists) | 104 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); |
140 | return -ENOMEM; | 105 | } |
141 | 106 | ||
142 | hists->stats.total_period += data->period; | 107 | evsel->hists.stats.total_period += sample->period; |
143 | /* | 108 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); |
144 | * FIXME: add_event_total should be moved from here to | 109 | out: |
145 | * perf_session__process_event so that the proper hist is passed to | 110 | return err; |
146 | * the event_op methods. | ||
147 | */ | ||
148 | hists__inc_nr_events(hists, PERF_RECORD_SAMPLE); | ||
149 | session->hists.stats.total_period += data->period; | ||
150 | return 0; | ||
151 | } | 111 | } |
152 | 112 | ||
153 | static int process_sample_event(event_t *event, struct sample_data *sample, | 113 | |
114 | static int process_sample_event(union perf_event *event, | ||
115 | struct perf_sample *sample, | ||
154 | struct perf_session *session) | 116 | struct perf_session *session) |
155 | { | 117 | { |
156 | struct addr_location al; | 118 | struct addr_location al; |
157 | struct perf_event_attr *attr; | ||
158 | 119 | ||
159 | if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { | 120 | if (perf_event__preprocess_sample(event, session, &al, sample, |
121 | annotate_init) < 0) { | ||
160 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 122 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
161 | event->header.type); | 123 | event->header.type); |
162 | return -1; | 124 | return -1; |
@@ -170,26 +132,17 @@ static int process_sample_event(event_t *event, struct sample_data *sample, | |||
170 | return -1; | 132 | return -1; |
171 | } | 133 | } |
172 | 134 | ||
173 | attr = perf_header__find_attr(sample->id, &session->header); | ||
174 | |||
175 | if (add_event_total(session, sample, attr)) { | ||
176 | pr_debug("problem adding event period\n"); | ||
177 | return -1; | ||
178 | } | ||
179 | |||
180 | return 0; | 135 | return 0; |
181 | } | 136 | } |
182 | 137 | ||
183 | static int process_read_event(event_t *event, struct sample_data *sample __used, | 138 | static int process_read_event(union perf_event *event, |
184 | struct perf_session *session __used) | 139 | struct perf_sample *sample __used, |
140 | struct perf_session *session) | ||
185 | { | 141 | { |
186 | struct perf_event_attr *attr; | 142 | struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, |
187 | 143 | event->read.id); | |
188 | attr = perf_header__find_attr(event->read.id, &session->header); | ||
189 | |||
190 | if (show_threads) { | 144 | if (show_threads) { |
191 | const char *name = attr ? __event_name(attr->type, attr->config) | 145 | const char *name = evsel ? event_name(evsel) : "unknown"; |
192 | : "unknown"; | ||
193 | perf_read_values_add_value(&show_threads_values, | 146 | perf_read_values_add_value(&show_threads_values, |
194 | event->read.pid, event->read.tid, | 147 | event->read.pid, event->read.tid, |
195 | event->read.id, | 148 | event->read.id, |
@@ -198,7 +151,7 @@ static int process_read_event(event_t *event, struct sample_data *sample __used, | |||
198 | } | 151 | } |
199 | 152 | ||
200 | dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, | 153 | dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, |
201 | attr ? __event_name(attr->type, attr->config) : "FAIL", | 154 | evsel ? event_name(evsel) : "FAIL", |
202 | event->read.value); | 155 | event->read.value); |
203 | 156 | ||
204 | return 0; | 157 | return 0; |
@@ -222,7 +175,7 @@ static int perf_session__setup_sample_type(struct perf_session *self) | |||
222 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && | 175 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && |
223 | !symbol_conf.use_callchain) { | 176 | !symbol_conf.use_callchain) { |
224 | symbol_conf.use_callchain = true; | 177 | symbol_conf.use_callchain = true; |
225 | if (register_callchain_param(&callchain_param) < 0) { | 178 | if (callchain_register_param(&callchain_param) < 0) { |
226 | fprintf(stderr, "Can't register callchain" | 179 | fprintf(stderr, "Can't register callchain" |
227 | " params\n"); | 180 | " params\n"); |
228 | return -EINVAL; | 181 | return -EINVAL; |
@@ -233,17 +186,17 @@ static int perf_session__setup_sample_type(struct perf_session *self) | |||
233 | } | 186 | } |
234 | 187 | ||
235 | static struct perf_event_ops event_ops = { | 188 | static struct perf_event_ops event_ops = { |
236 | .sample = process_sample_event, | 189 | .sample = process_sample_event, |
237 | .mmap = event__process_mmap, | 190 | .mmap = perf_event__process_mmap, |
238 | .comm = event__process_comm, | 191 | .comm = perf_event__process_comm, |
239 | .exit = event__process_task, | 192 | .exit = perf_event__process_task, |
240 | .fork = event__process_task, | 193 | .fork = perf_event__process_task, |
241 | .lost = event__process_lost, | 194 | .lost = perf_event__process_lost, |
242 | .read = process_read_event, | 195 | .read = process_read_event, |
243 | .attr = event__process_attr, | 196 | .attr = perf_event__process_attr, |
244 | .event_type = event__process_event_type, | 197 | .event_type = perf_event__process_event_type, |
245 | .tracing_data = event__process_tracing_data, | 198 | .tracing_data = perf_event__process_tracing_data, |
246 | .build_id = event__process_build_id, | 199 | .build_id = perf_event__process_build_id, |
247 | .ordered_samples = true, | 200 | .ordered_samples = true, |
248 | .ordering_requires_timestamps = true, | 201 | .ordering_requires_timestamps = true, |
249 | }; | 202 | }; |
@@ -269,21 +222,21 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self, | |||
269 | return ret + fprintf(fp, "\n#\n"); | 222 | return ret + fprintf(fp, "\n#\n"); |
270 | } | 223 | } |
271 | 224 | ||
272 | static int hists__tty_browse_tree(struct rb_root *tree, const char *help) | 225 | static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, |
226 | const char *help) | ||
273 | { | 227 | { |
274 | struct rb_node *next = rb_first(tree); | 228 | struct perf_evsel *pos; |
275 | 229 | ||
276 | while (next) { | 230 | list_for_each_entry(pos, &evlist->entries, node) { |
277 | struct hists *hists = rb_entry(next, struct hists, rb_node); | 231 | struct hists *hists = &pos->hists; |
278 | const char *evname = NULL; | 232 | const char *evname = NULL; |
279 | 233 | ||
280 | if (rb_first(&hists->entries) != rb_last(&hists->entries)) | 234 | if (rb_first(&hists->entries) != rb_last(&hists->entries)) |
281 | evname = __event_name(hists->type, hists->config); | 235 | evname = event_name(pos); |
282 | 236 | ||
283 | hists__fprintf_nr_sample_events(hists, evname, stdout); | 237 | hists__fprintf_nr_sample_events(hists, evname, stdout); |
284 | hists__fprintf(hists, NULL, false, stdout); | 238 | hists__fprintf(hists, NULL, false, stdout); |
285 | fprintf(stdout, "\n\n"); | 239 | fprintf(stdout, "\n\n"); |
286 | next = rb_next(&hists->rb_node); | ||
287 | } | 240 | } |
288 | 241 | ||
289 | if (sort_order == default_sort_order && | 242 | if (sort_order == default_sort_order && |
@@ -304,8 +257,9 @@ static int hists__tty_browse_tree(struct rb_root *tree, const char *help) | |||
304 | static int __cmd_report(void) | 257 | static int __cmd_report(void) |
305 | { | 258 | { |
306 | int ret = -EINVAL; | 259 | int ret = -EINVAL; |
260 | u64 nr_samples; | ||
307 | struct perf_session *session; | 261 | struct perf_session *session; |
308 | struct rb_node *next; | 262 | struct perf_evsel *pos; |
309 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; | 263 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; |
310 | 264 | ||
311 | signal(SIGINT, sig_handler); | 265 | signal(SIGINT, sig_handler); |
@@ -336,20 +290,24 @@ static int __cmd_report(void) | |||
336 | if (verbose > 2) | 290 | if (verbose > 2) |
337 | perf_session__fprintf_dsos(session, stdout); | 291 | perf_session__fprintf_dsos(session, stdout); |
338 | 292 | ||
339 | next = rb_first(&session->hists_tree); | 293 | nr_samples = 0; |
340 | while (next) { | 294 | list_for_each_entry(pos, &session->evlist->entries, node) { |
341 | struct hists *hists; | 295 | struct hists *hists = &pos->hists; |
342 | 296 | ||
343 | hists = rb_entry(next, struct hists, rb_node); | ||
344 | hists__collapse_resort(hists); | 297 | hists__collapse_resort(hists); |
345 | hists__output_resort(hists); | 298 | hists__output_resort(hists); |
346 | next = rb_next(&hists->rb_node); | 299 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
300 | } | ||
301 | |||
302 | if (nr_samples == 0) { | ||
303 | ui__warning("The %s file has no samples!\n", input_name); | ||
304 | goto out_delete; | ||
347 | } | 305 | } |
348 | 306 | ||
349 | if (use_browser > 0) | 307 | if (use_browser > 0) |
350 | hists__tui_browse_tree(&session->hists_tree, help); | 308 | perf_evlist__tui_browse_hists(session->evlist, help); |
351 | else | 309 | else |
352 | hists__tty_browse_tree(&session->hists_tree, help); | 310 | perf_evlist__tty_browse_hists(session->evlist, help); |
353 | 311 | ||
354 | out_delete: | 312 | out_delete: |
355 | /* | 313 | /* |
@@ -424,7 +382,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
424 | if (tok2) | 382 | if (tok2) |
425 | callchain_param.print_limit = strtod(tok2, &endptr); | 383 | callchain_param.print_limit = strtod(tok2, &endptr); |
426 | setup: | 384 | setup: |
427 | if (register_callchain_param(&callchain_param) < 0) { | 385 | if (callchain_register_param(&callchain_param) < 0) { |
428 | fprintf(stderr, "Can't register callchain params\n"); | 386 | fprintf(stderr, "Can't register callchain params\n"); |
429 | return -1; | 387 | return -1; |
430 | } | 388 | } |
@@ -498,7 +456,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
498 | use_browser = 1; | 456 | use_browser = 1; |
499 | 457 | ||
500 | if (strcmp(input_name, "-") != 0) | 458 | if (strcmp(input_name, "-") != 0) |
501 | setup_browser(); | 459 | setup_browser(true); |
502 | else | 460 | else |
503 | use_browser = 0; | 461 | use_browser = 0; |
504 | /* | 462 | /* |
@@ -507,7 +465,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
507 | * implementation. | 465 | * implementation. |
508 | */ | 466 | */ |
509 | if (use_browser > 0) { | 467 | if (use_browser > 0) { |
510 | symbol_conf.priv_size = sizeof(struct sym_priv); | 468 | symbol_conf.priv_size = sizeof(struct annotation); |
469 | annotate_init = symbol__annotate_init; | ||
511 | /* | 470 | /* |
512 | * For searching by name on the "Browse map details". | 471 | * For searching by name on the "Browse map details". |
513 | * providing it only in verbose mode not to bloat too | 472 | * providing it only in verbose mode not to bloat too |