diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r-- | tools/perf/builtin-report.c | 415 |
1 files changed, 187 insertions, 228 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 8cf8e66ba594..02f985f3a396 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -39,7 +39,7 @@ | |||
39 | #include <dlfcn.h> | 39 | #include <dlfcn.h> |
40 | #include <linux/bitmap.h> | 40 | #include <linux/bitmap.h> |
41 | 41 | ||
42 | struct perf_report { | 42 | struct report { |
43 | struct perf_tool tool; | 43 | struct perf_tool tool; |
44 | struct perf_session *session; | 44 | struct perf_session *session; |
45 | bool force, use_tui, use_gtk, use_stdio; | 45 | bool force, use_tui, use_gtk, use_stdio; |
@@ -49,6 +49,8 @@ struct perf_report { | |||
49 | bool show_threads; | 49 | bool show_threads; |
50 | bool inverted_callchain; | 50 | bool inverted_callchain; |
51 | bool mem_mode; | 51 | bool mem_mode; |
52 | bool header; | ||
53 | bool header_only; | ||
52 | int max_stack; | 54 | int max_stack; |
53 | struct perf_read_values show_threads_values; | 55 | struct perf_read_values show_threads_values; |
54 | const char *pretty_printing_style; | 56 | const char *pretty_printing_style; |
@@ -58,14 +60,14 @@ struct perf_report { | |||
58 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 60 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
59 | }; | 61 | }; |
60 | 62 | ||
61 | static int perf_report_config(const char *var, const char *value, void *cb) | 63 | static int report__config(const char *var, const char *value, void *cb) |
62 | { | 64 | { |
63 | if (!strcmp(var, "report.group")) { | 65 | if (!strcmp(var, "report.group")) { |
64 | symbol_conf.event_group = perf_config_bool(var, value); | 66 | symbol_conf.event_group = perf_config_bool(var, value); |
65 | return 0; | 67 | return 0; |
66 | } | 68 | } |
67 | if (!strcmp(var, "report.percent-limit")) { | 69 | if (!strcmp(var, "report.percent-limit")) { |
68 | struct perf_report *rep = cb; | 70 | struct report *rep = cb; |
69 | rep->min_percent = strtof(value, NULL); | 71 | rep->min_percent = strtof(value, NULL); |
70 | return 0; | 72 | return 0; |
71 | } | 73 | } |
@@ -73,31 +75,22 @@ static int perf_report_config(const char *var, const char *value, void *cb) | |||
73 | return perf_default_config(var, value, cb); | 75 | return perf_default_config(var, value, cb); |
74 | } | 76 | } |
75 | 77 | ||
76 | static int perf_report__add_mem_hist_entry(struct perf_tool *tool, | 78 | static int report__add_mem_hist_entry(struct perf_tool *tool, struct addr_location *al, |
77 | struct addr_location *al, | 79 | struct perf_sample *sample, struct perf_evsel *evsel, |
78 | struct perf_sample *sample, | 80 | union perf_event *event) |
79 | struct perf_evsel *evsel, | ||
80 | struct machine *machine, | ||
81 | union perf_event *event) | ||
82 | { | 81 | { |
83 | struct perf_report *rep = container_of(tool, struct perf_report, tool); | 82 | struct report *rep = container_of(tool, struct report, tool); |
84 | struct symbol *parent = NULL; | 83 | struct symbol *parent = NULL; |
85 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 84 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
86 | int err = 0; | ||
87 | struct hist_entry *he; | 85 | struct hist_entry *he; |
88 | struct mem_info *mi, *mx; | 86 | struct mem_info *mi, *mx; |
89 | uint64_t cost; | 87 | uint64_t cost; |
88 | int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); | ||
90 | 89 | ||
91 | if ((sort__has_parent || symbol_conf.use_callchain) && | 90 | if (err) |
92 | sample->callchain) { | 91 | return err; |
93 | err = machine__resolve_callchain(machine, evsel, al->thread, | ||
94 | sample, &parent, al, | ||
95 | rep->max_stack); | ||
96 | if (err) | ||
97 | return err; | ||
98 | } | ||
99 | 92 | ||
100 | mi = machine__resolve_mem(machine, al->thread, sample, cpumode); | 93 | mi = machine__resolve_mem(al->machine, al->thread, sample, cpumode); |
101 | if (!mi) | 94 | if (!mi) |
102 | return -ENOMEM; | 95 | return -ENOMEM; |
103 | 96 | ||
@@ -120,77 +113,38 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool, | |||
120 | if (!he) | 113 | if (!he) |
121 | return -ENOMEM; | 114 | return -ENOMEM; |
122 | 115 | ||
123 | /* | 116 | if (ui__has_annotation()) { |
124 | * In the TUI browser, we are doing integrated annotation, | ||
125 | * so we don't allocate the extra space needed because the stdio | ||
126 | * code will not use it. | ||
127 | */ | ||
128 | if (sort__has_sym && he->ms.sym && use_browser > 0) { | ||
129 | struct annotation *notes = symbol__annotation(he->ms.sym); | ||
130 | |||
131 | assert(evsel != NULL); | ||
132 | |||
133 | if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0) | ||
134 | goto out; | ||
135 | |||
136 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | 117 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); |
137 | if (err) | 118 | if (err) |
138 | goto out; | 119 | goto out; |
139 | } | ||
140 | |||
141 | if (sort__has_sym && he->mem_info->daddr.sym && use_browser > 0) { | ||
142 | struct annotation *notes; | ||
143 | 120 | ||
144 | mx = he->mem_info; | 121 | mx = he->mem_info; |
145 | 122 | err = addr_map_symbol__inc_samples(&mx->daddr, evsel->idx); | |
146 | notes = symbol__annotation(mx->daddr.sym); | ||
147 | if (notes->src == NULL && symbol__alloc_hist(mx->daddr.sym) < 0) | ||
148 | goto out; | ||
149 | |||
150 | err = symbol__inc_addr_samples(mx->daddr.sym, | ||
151 | mx->daddr.map, | ||
152 | evsel->idx, | ||
153 | mx->daddr.al_addr); | ||
154 | if (err) | 123 | if (err) |
155 | goto out; | 124 | goto out; |
156 | } | 125 | } |
157 | 126 | ||
158 | evsel->hists.stats.total_period += cost; | 127 | evsel->hists.stats.total_period += cost; |
159 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | 128 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); |
160 | err = 0; | 129 | err = hist_entry__append_callchain(he, sample); |
161 | |||
162 | if (symbol_conf.use_callchain) { | ||
163 | err = callchain_append(he->callchain, | ||
164 | &callchain_cursor, | ||
165 | sample->period); | ||
166 | } | ||
167 | out: | 130 | out: |
168 | return err; | 131 | return err; |
169 | } | 132 | } |
170 | 133 | ||
171 | static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | 134 | static int report__add_branch_hist_entry(struct perf_tool *tool, struct addr_location *al, |
172 | struct addr_location *al, | 135 | struct perf_sample *sample, struct perf_evsel *evsel) |
173 | struct perf_sample *sample, | ||
174 | struct perf_evsel *evsel, | ||
175 | struct machine *machine) | ||
176 | { | 136 | { |
177 | struct perf_report *rep = container_of(tool, struct perf_report, tool); | 137 | struct report *rep = container_of(tool, struct report, tool); |
178 | struct symbol *parent = NULL; | 138 | struct symbol *parent = NULL; |
179 | int err = 0; | ||
180 | unsigned i; | 139 | unsigned i; |
181 | struct hist_entry *he; | 140 | struct hist_entry *he; |
182 | struct branch_info *bi, *bx; | 141 | struct branch_info *bi, *bx; |
142 | int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); | ||
183 | 143 | ||
184 | if ((sort__has_parent || symbol_conf.use_callchain) | 144 | if (err) |
185 | && sample->callchain) { | 145 | return err; |
186 | err = machine__resolve_callchain(machine, evsel, al->thread, | ||
187 | sample, &parent, al, | ||
188 | rep->max_stack); | ||
189 | if (err) | ||
190 | return err; | ||
191 | } | ||
192 | 146 | ||
193 | bi = machine__resolve_bstack(machine, al->thread, | 147 | bi = machine__resolve_bstack(al->machine, al->thread, |
194 | sample->branch_stack); | 148 | sample->branch_stack); |
195 | if (!bi) | 149 | if (!bi) |
196 | return -ENOMEM; | 150 | return -ENOMEM; |
@@ -212,35 +166,19 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | |||
212 | he = __hists__add_entry(&evsel->hists, al, parent, &bi[i], NULL, | 166 | he = __hists__add_entry(&evsel->hists, al, parent, &bi[i], NULL, |
213 | 1, 1, 0); | 167 | 1, 1, 0); |
214 | if (he) { | 168 | if (he) { |
215 | struct annotation *notes; | 169 | if (ui__has_annotation()) { |
216 | bx = he->branch_info; | 170 | bx = he->branch_info; |
217 | if (bx->from.sym && use_browser == 1 && sort__has_sym) { | 171 | err = addr_map_symbol__inc_samples(&bx->from, |
218 | notes = symbol__annotation(bx->from.sym); | 172 | evsel->idx); |
219 | if (!notes->src | ||
220 | && symbol__alloc_hist(bx->from.sym) < 0) | ||
221 | goto out; | ||
222 | |||
223 | err = symbol__inc_addr_samples(bx->from.sym, | ||
224 | bx->from.map, | ||
225 | evsel->idx, | ||
226 | bx->from.al_addr); | ||
227 | if (err) | 173 | if (err) |
228 | goto out; | 174 | goto out; |
229 | } | ||
230 | 175 | ||
231 | if (bx->to.sym && use_browser == 1 && sort__has_sym) { | 176 | err = addr_map_symbol__inc_samples(&bx->to, |
232 | notes = symbol__annotation(bx->to.sym); | 177 | evsel->idx); |
233 | if (!notes->src | ||
234 | && symbol__alloc_hist(bx->to.sym) < 0) | ||
235 | goto out; | ||
236 | |||
237 | err = symbol__inc_addr_samples(bx->to.sym, | ||
238 | bx->to.map, | ||
239 | evsel->idx, | ||
240 | bx->to.al_addr); | ||
241 | if (err) | 178 | if (err) |
242 | goto out; | 179 | goto out; |
243 | } | 180 | } |
181 | |||
244 | evsel->hists.stats.total_period += 1; | 182 | evsel->hists.stats.total_period += 1; |
245 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | 183 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); |
246 | } else | 184 | } else |
@@ -252,24 +190,16 @@ out: | |||
252 | return err; | 190 | return err; |
253 | } | 191 | } |
254 | 192 | ||
255 | static int perf_evsel__add_hist_entry(struct perf_tool *tool, | 193 | static int report__add_hist_entry(struct perf_tool *tool, struct perf_evsel *evsel, |
256 | struct perf_evsel *evsel, | 194 | struct addr_location *al, struct perf_sample *sample) |
257 | struct addr_location *al, | ||
258 | struct perf_sample *sample, | ||
259 | struct machine *machine) | ||
260 | { | 195 | { |
261 | struct perf_report *rep = container_of(tool, struct perf_report, tool); | 196 | struct report *rep = container_of(tool, struct report, tool); |
262 | struct symbol *parent = NULL; | 197 | struct symbol *parent = NULL; |
263 | int err = 0; | ||
264 | struct hist_entry *he; | 198 | struct hist_entry *he; |
199 | int err = sample__resolve_callchain(sample, &parent, evsel, al, rep->max_stack); | ||
265 | 200 | ||
266 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { | 201 | if (err) |
267 | err = machine__resolve_callchain(machine, evsel, al->thread, | 202 | return err; |
268 | sample, &parent, al, | ||
269 | rep->max_stack); | ||
270 | if (err) | ||
271 | return err; | ||
272 | } | ||
273 | 203 | ||
274 | he = __hists__add_entry(&evsel->hists, al, parent, NULL, NULL, | 204 | he = __hists__add_entry(&evsel->hists, al, parent, NULL, NULL, |
275 | sample->period, sample->weight, | 205 | sample->period, sample->weight, |
@@ -277,29 +207,12 @@ static int perf_evsel__add_hist_entry(struct perf_tool *tool, | |||
277 | if (he == NULL) | 207 | if (he == NULL) |
278 | return -ENOMEM; | 208 | return -ENOMEM; |
279 | 209 | ||
280 | if (symbol_conf.use_callchain) { | 210 | err = hist_entry__append_callchain(he, sample); |
281 | err = callchain_append(he->callchain, | 211 | if (err) |
282 | &callchain_cursor, | 212 | goto out; |
283 | sample->period); | ||
284 | if (err) | ||
285 | return err; | ||
286 | } | ||
287 | /* | ||
288 | * Only in the TUI browser we are doing integrated annotation, | ||
289 | * so we don't allocated the extra space needed because the stdio | ||
290 | * code will not use it. | ||
291 | */ | ||
292 | if (he->ms.sym != NULL && use_browser == 1 && sort__has_sym) { | ||
293 | struct annotation *notes = symbol__annotation(he->ms.sym); | ||
294 | |||
295 | assert(evsel != NULL); | ||
296 | |||
297 | err = -ENOMEM; | ||
298 | if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0) | ||
299 | goto out; | ||
300 | 213 | ||
214 | if (ui__has_annotation()) | ||
301 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | 215 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); |
302 | } | ||
303 | 216 | ||
304 | evsel->hists.stats.total_period += sample->period; | 217 | evsel->hists.stats.total_period += sample->period; |
305 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | 218 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); |
@@ -314,13 +227,13 @@ static int process_sample_event(struct perf_tool *tool, | |||
314 | struct perf_evsel *evsel, | 227 | struct perf_evsel *evsel, |
315 | struct machine *machine) | 228 | struct machine *machine) |
316 | { | 229 | { |
317 | struct perf_report *rep = container_of(tool, struct perf_report, tool); | 230 | struct report *rep = container_of(tool, struct report, tool); |
318 | struct addr_location al; | 231 | struct addr_location al; |
319 | int ret; | 232 | int ret; |
320 | 233 | ||
321 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { | 234 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { |
322 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 235 | pr_debug("problem processing %d event, skipping it.\n", |
323 | event->header.type); | 236 | event->header.type); |
324 | return -1; | 237 | return -1; |
325 | } | 238 | } |
326 | 239 | ||
@@ -331,21 +244,18 @@ static int process_sample_event(struct perf_tool *tool, | |||
331 | return 0; | 244 | return 0; |
332 | 245 | ||
333 | if (sort__mode == SORT_MODE__BRANCH) { | 246 | if (sort__mode == SORT_MODE__BRANCH) { |
334 | ret = perf_report__add_branch_hist_entry(tool, &al, sample, | 247 | ret = report__add_branch_hist_entry(tool, &al, sample, evsel); |
335 | evsel, machine); | ||
336 | if (ret < 0) | 248 | if (ret < 0) |
337 | pr_debug("problem adding lbr entry, skipping event\n"); | 249 | pr_debug("problem adding lbr entry, skipping event\n"); |
338 | } else if (rep->mem_mode == 1) { | 250 | } else if (rep->mem_mode == 1) { |
339 | ret = perf_report__add_mem_hist_entry(tool, &al, sample, | 251 | ret = report__add_mem_hist_entry(tool, &al, sample, evsel, event); |
340 | evsel, machine, event); | ||
341 | if (ret < 0) | 252 | if (ret < 0) |
342 | pr_debug("problem adding mem entry, skipping event\n"); | 253 | pr_debug("problem adding mem entry, skipping event\n"); |
343 | } else { | 254 | } else { |
344 | if (al.map != NULL) | 255 | if (al.map != NULL) |
345 | al.map->dso->hit = 1; | 256 | al.map->dso->hit = 1; |
346 | 257 | ||
347 | ret = perf_evsel__add_hist_entry(tool, evsel, &al, sample, | 258 | ret = report__add_hist_entry(tool, evsel, &al, sample); |
348 | machine); | ||
349 | if (ret < 0) | 259 | if (ret < 0) |
350 | pr_debug("problem incrementing symbol period, skipping event\n"); | 260 | pr_debug("problem incrementing symbol period, skipping event\n"); |
351 | } | 261 | } |
@@ -358,7 +268,7 @@ static int process_read_event(struct perf_tool *tool, | |||
358 | struct perf_evsel *evsel, | 268 | struct perf_evsel *evsel, |
359 | struct machine *machine __maybe_unused) | 269 | struct machine *machine __maybe_unused) |
360 | { | 270 | { |
361 | struct perf_report *rep = container_of(tool, struct perf_report, tool); | 271 | struct report *rep = container_of(tool, struct report, tool); |
362 | 272 | ||
363 | if (rep->show_threads) { | 273 | if (rep->show_threads) { |
364 | const char *name = evsel ? perf_evsel__name(evsel) : "unknown"; | 274 | const char *name = evsel ? perf_evsel__name(evsel) : "unknown"; |
@@ -377,7 +287,7 @@ static int process_read_event(struct perf_tool *tool, | |||
377 | } | 287 | } |
378 | 288 | ||
379 | /* For pipe mode, sample_type is not currently set */ | 289 | /* For pipe mode, sample_type is not currently set */ |
380 | static int perf_report__setup_sample_type(struct perf_report *rep) | 290 | static int report__setup_sample_type(struct report *rep) |
381 | { | 291 | { |
382 | struct perf_session *session = rep->session; | 292 | struct perf_session *session = rep->session; |
383 | u64 sample_type = perf_evlist__combined_sample_type(session->evlist); | 293 | u64 sample_type = perf_evlist__combined_sample_type(session->evlist); |
@@ -422,8 +332,7 @@ static void sig_handler(int sig __maybe_unused) | |||
422 | session_done = 1; | 332 | session_done = 1; |
423 | } | 333 | } |
424 | 334 | ||
425 | static size_t hists__fprintf_nr_sample_events(struct perf_report *rep, | 335 | static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report *rep, |
426 | struct hists *hists, | ||
427 | const char *evname, FILE *fp) | 336 | const char *evname, FILE *fp) |
428 | { | 337 | { |
429 | size_t ret; | 338 | size_t ret; |
@@ -460,12 +369,12 @@ static size_t hists__fprintf_nr_sample_events(struct perf_report *rep, | |||
460 | } | 369 | } |
461 | 370 | ||
462 | static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, | 371 | static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, |
463 | struct perf_report *rep, | 372 | struct report *rep, |
464 | const char *help) | 373 | const char *help) |
465 | { | 374 | { |
466 | struct perf_evsel *pos; | 375 | struct perf_evsel *pos; |
467 | 376 | ||
468 | list_for_each_entry(pos, &evlist->entries, node) { | 377 | evlist__for_each(evlist, pos) { |
469 | struct hists *hists = &pos->hists; | 378 | struct hists *hists = &pos->hists; |
470 | const char *evname = perf_evsel__name(pos); | 379 | const char *evname = perf_evsel__name(pos); |
471 | 380 | ||
@@ -473,7 +382,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, | |||
473 | !perf_evsel__is_group_leader(pos)) | 382 | !perf_evsel__is_group_leader(pos)) |
474 | continue; | 383 | continue; |
475 | 384 | ||
476 | hists__fprintf_nr_sample_events(rep, hists, evname, stdout); | 385 | hists__fprintf_nr_sample_events(hists, rep, evname, stdout); |
477 | hists__fprintf(hists, true, 0, 0, rep->min_percent, stdout); | 386 | hists__fprintf(hists, true, 0, 0, rep->min_percent, stdout); |
478 | fprintf(stdout, "\n\n"); | 387 | fprintf(stdout, "\n\n"); |
479 | } | 388 | } |
@@ -493,43 +402,11 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, | |||
493 | return 0; | 402 | return 0; |
494 | } | 403 | } |
495 | 404 | ||
496 | static int __cmd_report(struct perf_report *rep) | 405 | static void report__warn_kptr_restrict(const struct report *rep) |
497 | { | 406 | { |
498 | int ret = -EINVAL; | 407 | struct map *kernel_map = rep->session->machines.host.vmlinux_maps[MAP__FUNCTION]; |
499 | u64 nr_samples; | 408 | struct kmap *kernel_kmap = map__kmap(kernel_map); |
500 | struct perf_session *session = rep->session; | ||
501 | struct perf_evsel *pos; | ||
502 | struct map *kernel_map; | ||
503 | struct kmap *kernel_kmap; | ||
504 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; | ||
505 | struct ui_progress prog; | ||
506 | struct perf_data_file *file = session->file; | ||
507 | |||
508 | signal(SIGINT, sig_handler); | ||
509 | |||
510 | if (rep->cpu_list) { | ||
511 | ret = perf_session__cpu_bitmap(session, rep->cpu_list, | ||
512 | rep->cpu_bitmap); | ||
513 | if (ret) | ||
514 | return ret; | ||
515 | } | ||
516 | 409 | ||
517 | if (use_browser <= 0) | ||
518 | perf_session__fprintf_info(session, stdout, rep->show_full_info); | ||
519 | |||
520 | if (rep->show_threads) | ||
521 | perf_read_values_init(&rep->show_threads_values); | ||
522 | |||
523 | ret = perf_report__setup_sample_type(rep); | ||
524 | if (ret) | ||
525 | return ret; | ||
526 | |||
527 | ret = perf_session__process_events(session, &rep->tool); | ||
528 | if (ret) | ||
529 | return ret; | ||
530 | |||
531 | kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION]; | ||
532 | kernel_kmap = map__kmap(kernel_map); | ||
533 | if (kernel_map == NULL || | 410 | if (kernel_map == NULL || |
534 | (kernel_map->dso->hit && | 411 | (kernel_map->dso->hit && |
535 | (kernel_kmap->ref_reloc_sym == NULL || | 412 | (kernel_kmap->ref_reloc_sym == NULL || |
@@ -552,26 +429,73 @@ static int __cmd_report(struct perf_report *rep) | |||
552 | "Samples in kernel modules can't be resolved as well.\n\n", | 429 | "Samples in kernel modules can't be resolved as well.\n\n", |
553 | desc); | 430 | desc); |
554 | } | 431 | } |
432 | } | ||
555 | 433 | ||
556 | if (verbose > 3) | 434 | static int report__gtk_browse_hists(struct report *rep, const char *help) |
557 | perf_session__fprintf(session, stdout); | 435 | { |
436 | int (*hist_browser)(struct perf_evlist *evlist, const char *help, | ||
437 | struct hist_browser_timer *timer, float min_pcnt); | ||
558 | 438 | ||
559 | if (verbose > 2) | 439 | hist_browser = dlsym(perf_gtk_handle, "perf_evlist__gtk_browse_hists"); |
560 | perf_session__fprintf_dsos(session, stdout); | ||
561 | 440 | ||
562 | if (dump_trace) { | 441 | if (hist_browser == NULL) { |
563 | perf_session__fprintf_nr_events(session, stdout); | 442 | ui__error("GTK browser not found!\n"); |
564 | return 0; | 443 | return -1; |
565 | } | 444 | } |
566 | 445 | ||
567 | nr_samples = 0; | 446 | return hist_browser(rep->session->evlist, help, NULL, rep->min_percent); |
568 | list_for_each_entry(pos, &session->evlist->entries, node) | 447 | } |
448 | |||
449 | static int report__browse_hists(struct report *rep) | ||
450 | { | ||
451 | int ret; | ||
452 | struct perf_session *session = rep->session; | ||
453 | struct perf_evlist *evlist = session->evlist; | ||
454 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; | ||
455 | |||
456 | switch (use_browser) { | ||
457 | case 1: | ||
458 | ret = perf_evlist__tui_browse_hists(evlist, help, NULL, | ||
459 | rep->min_percent, | ||
460 | &session->header.env); | ||
461 | /* | ||
462 | * Usually "ret" is the last pressed key, and we only | ||
463 | * care if the key notifies us to switch data file. | ||
464 | */ | ||
465 | if (ret != K_SWITCH_INPUT_DATA) | ||
466 | ret = 0; | ||
467 | break; | ||
468 | case 2: | ||
469 | ret = report__gtk_browse_hists(rep, help); | ||
470 | break; | ||
471 | default: | ||
472 | ret = perf_evlist__tty_browse_hists(evlist, rep, help); | ||
473 | break; | ||
474 | } | ||
475 | |||
476 | return ret; | ||
477 | } | ||
478 | |||
479 | static u64 report__collapse_hists(struct report *rep) | ||
480 | { | ||
481 | struct ui_progress prog; | ||
482 | struct perf_evsel *pos; | ||
483 | u64 nr_samples = 0; | ||
484 | /* | ||
485 | * Count number of histogram entries to use when showing progress, | ||
486 | * reusing nr_samples variable. | ||
487 | */ | ||
488 | evlist__for_each(rep->session->evlist, pos) | ||
569 | nr_samples += pos->hists.nr_entries; | 489 | nr_samples += pos->hists.nr_entries; |
570 | 490 | ||
571 | ui_progress__init(&prog, nr_samples, "Merging related events..."); | 491 | ui_progress__init(&prog, nr_samples, "Merging related events..."); |
572 | 492 | /* | |
493 | * Count total number of samples, will be used to check if this | ||
494 | * session had any. | ||
495 | */ | ||
573 | nr_samples = 0; | 496 | nr_samples = 0; |
574 | list_for_each_entry(pos, &session->evlist->entries, node) { | 497 | |
498 | evlist__for_each(rep->session->evlist, pos) { | ||
575 | struct hists *hists = &pos->hists; | 499 | struct hists *hists = &pos->hists; |
576 | 500 | ||
577 | if (pos->idx == 0) | 501 | if (pos->idx == 0) |
@@ -589,8 +513,57 @@ static int __cmd_report(struct perf_report *rep) | |||
589 | hists__link(leader_hists, hists); | 513 | hists__link(leader_hists, hists); |
590 | } | 514 | } |
591 | } | 515 | } |
516 | |||
592 | ui_progress__finish(); | 517 | ui_progress__finish(); |
593 | 518 | ||
519 | return nr_samples; | ||
520 | } | ||
521 | |||
522 | static int __cmd_report(struct report *rep) | ||
523 | { | ||
524 | int ret; | ||
525 | u64 nr_samples; | ||
526 | struct perf_session *session = rep->session; | ||
527 | struct perf_evsel *pos; | ||
528 | struct perf_data_file *file = session->file; | ||
529 | |||
530 | signal(SIGINT, sig_handler); | ||
531 | |||
532 | if (rep->cpu_list) { | ||
533 | ret = perf_session__cpu_bitmap(session, rep->cpu_list, | ||
534 | rep->cpu_bitmap); | ||
535 | if (ret) | ||
536 | return ret; | ||
537 | } | ||
538 | |||
539 | if (rep->show_threads) | ||
540 | perf_read_values_init(&rep->show_threads_values); | ||
541 | |||
542 | ret = report__setup_sample_type(rep); | ||
543 | if (ret) | ||
544 | return ret; | ||
545 | |||
546 | ret = perf_session__process_events(session, &rep->tool); | ||
547 | if (ret) | ||
548 | return ret; | ||
549 | |||
550 | report__warn_kptr_restrict(rep); | ||
551 | |||
552 | if (use_browser == 0) { | ||
553 | if (verbose > 3) | ||
554 | perf_session__fprintf(session, stdout); | ||
555 | |||
556 | if (verbose > 2) | ||
557 | perf_session__fprintf_dsos(session, stdout); | ||
558 | |||
559 | if (dump_trace) { | ||
560 | perf_session__fprintf_nr_events(session, stdout); | ||
561 | return 0; | ||
562 | } | ||
563 | } | ||
564 | |||
565 | nr_samples = report__collapse_hists(rep); | ||
566 | |||
594 | if (session_done()) | 567 | if (session_done()) |
595 | return 0; | 568 | return 0; |
596 | 569 | ||
@@ -599,47 +572,16 @@ static int __cmd_report(struct perf_report *rep) | |||
599 | return 0; | 572 | return 0; |
600 | } | 573 | } |
601 | 574 | ||
602 | list_for_each_entry(pos, &session->evlist->entries, node) | 575 | evlist__for_each(session->evlist, pos) |
603 | hists__output_resort(&pos->hists); | 576 | hists__output_resort(&pos->hists); |
604 | 577 | ||
605 | if (use_browser > 0) { | 578 | return report__browse_hists(rep); |
606 | if (use_browser == 1) { | ||
607 | ret = perf_evlist__tui_browse_hists(session->evlist, | ||
608 | help, NULL, | ||
609 | rep->min_percent, | ||
610 | &session->header.env); | ||
611 | /* | ||
612 | * Usually "ret" is the last pressed key, and we only | ||
613 | * care if the key notifies us to switch data file. | ||
614 | */ | ||
615 | if (ret != K_SWITCH_INPUT_DATA) | ||
616 | ret = 0; | ||
617 | |||
618 | } else if (use_browser == 2) { | ||
619 | int (*hist_browser)(struct perf_evlist *, | ||
620 | const char *, | ||
621 | struct hist_browser_timer *, | ||
622 | float min_pcnt); | ||
623 | |||
624 | hist_browser = dlsym(perf_gtk_handle, | ||
625 | "perf_evlist__gtk_browse_hists"); | ||
626 | if (hist_browser == NULL) { | ||
627 | ui__error("GTK browser not found!\n"); | ||
628 | return ret; | ||
629 | } | ||
630 | hist_browser(session->evlist, help, NULL, | ||
631 | rep->min_percent); | ||
632 | } | ||
633 | } else | ||
634 | perf_evlist__tty_browse_hists(session->evlist, rep, help); | ||
635 | |||
636 | return ret; | ||
637 | } | 579 | } |
638 | 580 | ||
639 | static int | 581 | static int |
640 | parse_callchain_opt(const struct option *opt, const char *arg, int unset) | 582 | parse_callchain_opt(const struct option *opt, const char *arg, int unset) |
641 | { | 583 | { |
642 | struct perf_report *rep = (struct perf_report *)opt->value; | 584 | struct report *rep = (struct report *)opt->value; |
643 | char *tok, *tok2; | 585 | char *tok, *tok2; |
644 | char *endptr; | 586 | char *endptr; |
645 | 587 | ||
@@ -721,7 +663,7 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) | |||
721 | return -1; | 663 | return -1; |
722 | setup: | 664 | setup: |
723 | if (callchain_register_param(&callchain_param) < 0) { | 665 | if (callchain_register_param(&callchain_param) < 0) { |
724 | fprintf(stderr, "Can't register callchain params\n"); | 666 | pr_err("Can't register callchain params\n"); |
725 | return -1; | 667 | return -1; |
726 | } | 668 | } |
727 | return 0; | 669 | return 0; |
@@ -759,7 +701,7 @@ static int | |||
759 | parse_percent_limit(const struct option *opt, const char *str, | 701 | parse_percent_limit(const struct option *opt, const char *str, |
760 | int unset __maybe_unused) | 702 | int unset __maybe_unused) |
761 | { | 703 | { |
762 | struct perf_report *rep = opt->value; | 704 | struct report *rep = opt->value; |
763 | 705 | ||
764 | rep->min_percent = strtof(str, NULL); | 706 | rep->min_percent = strtof(str, NULL); |
765 | return 0; | 707 | return 0; |
@@ -777,7 +719,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
777 | "perf report [<options>]", | 719 | "perf report [<options>]", |
778 | NULL | 720 | NULL |
779 | }; | 721 | }; |
780 | struct perf_report report = { | 722 | struct report report = { |
781 | .tool = { | 723 | .tool = { |
782 | .sample = process_sample_event, | 724 | .sample = process_sample_event, |
783 | .mmap = perf_event__process_mmap, | 725 | .mmap = perf_event__process_mmap, |
@@ -820,6 +762,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
820 | OPT_BOOLEAN(0, "gtk", &report.use_gtk, "Use the GTK2 interface"), | 762 | OPT_BOOLEAN(0, "gtk", &report.use_gtk, "Use the GTK2 interface"), |
821 | OPT_BOOLEAN(0, "stdio", &report.use_stdio, | 763 | OPT_BOOLEAN(0, "stdio", &report.use_stdio, |
822 | "Use the stdio interface"), | 764 | "Use the stdio interface"), |
765 | OPT_BOOLEAN(0, "header", &report.header, "Show data header."), | ||
766 | OPT_BOOLEAN(0, "header-only", &report.header_only, | ||
767 | "Show only data header."), | ||
823 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 768 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
824 | "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," | 769 | "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," |
825 | " dso_to, dso_from, symbol_to, symbol_from, mispredict," | 770 | " dso_to, dso_from, symbol_to, symbol_from, mispredict," |
@@ -890,7 +835,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
890 | .mode = PERF_DATA_MODE_READ, | 835 | .mode = PERF_DATA_MODE_READ, |
891 | }; | 836 | }; |
892 | 837 | ||
893 | perf_config(perf_report_config, &report); | 838 | perf_config(report__config, &report); |
894 | 839 | ||
895 | argc = parse_options(argc, argv, options, report_usage, 0); | 840 | argc = parse_options(argc, argv, options, report_usage, 0); |
896 | 841 | ||
@@ -940,7 +885,7 @@ repeat: | |||
940 | } | 885 | } |
941 | if (report.mem_mode) { | 886 | if (report.mem_mode) { |
942 | if (sort__mode == SORT_MODE__BRANCH) { | 887 | if (sort__mode == SORT_MODE__BRANCH) { |
943 | fprintf(stderr, "branch and mem mode incompatible\n"); | 888 | pr_err("branch and mem mode incompatible\n"); |
944 | goto error; | 889 | goto error; |
945 | } | 890 | } |
946 | sort__mode = SORT_MODE__MEMORY; | 891 | sort__mode = SORT_MODE__MEMORY; |
@@ -963,6 +908,10 @@ repeat: | |||
963 | goto error; | 908 | goto error; |
964 | } | 909 | } |
965 | 910 | ||
911 | /* Force tty output for header output. */ | ||
912 | if (report.header || report.header_only) | ||
913 | use_browser = 0; | ||
914 | |||
966 | if (strcmp(input_name, "-") != 0) | 915 | if (strcmp(input_name, "-") != 0) |
967 | setup_browser(true); | 916 | setup_browser(true); |
968 | else { | 917 | else { |
@@ -970,6 +919,16 @@ repeat: | |||
970 | perf_hpp__init(); | 919 | perf_hpp__init(); |
971 | } | 920 | } |
972 | 921 | ||
922 | if (report.header || report.header_only) { | ||
923 | perf_session__fprintf_info(session, stdout, | ||
924 | report.show_full_info); | ||
925 | if (report.header_only) | ||
926 | return 0; | ||
927 | } else if (use_browser == 0) { | ||
928 | fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n", | ||
929 | stdout); | ||
930 | } | ||
931 | |||
973 | /* | 932 | /* |
974 | * Only in the TUI browser we are doing integrated annotation, | 933 | * Only in the TUI browser we are doing integrated annotation, |
975 | * so don't allocate extra space that won't be used in the stdio | 934 | * so don't allocate extra space that won't be used in the stdio |