.. | .. |
---|
8 | 8 | */ |
---|
9 | 9 | #include "builtin.h" |
---|
10 | 10 | |
---|
11 | | -#include "util/util.h" |
---|
12 | 11 | #include "util/config.h" |
---|
13 | 12 | |
---|
14 | 13 | #include "util/annotate.h" |
---|
15 | 14 | #include "util/color.h" |
---|
| 15 | +#include "util/dso.h" |
---|
16 | 16 | #include <linux/list.h> |
---|
17 | 17 | #include <linux/rbtree.h> |
---|
18 | 18 | #include <linux/err.h> |
---|
| 19 | +#include <linux/zalloc.h> |
---|
| 20 | +#include "util/map.h" |
---|
19 | 21 | #include "util/symbol.h" |
---|
| 22 | +#include "util/map_symbol.h" |
---|
| 23 | +#include "util/mem-events.h" |
---|
| 24 | +#include "util/branch.h" |
---|
20 | 25 | #include "util/callchain.h" |
---|
21 | 26 | #include "util/values.h" |
---|
22 | 27 | |
---|
.. | .. |
---|
24 | 29 | #include "util/debug.h" |
---|
25 | 30 | #include "util/evlist.h" |
---|
26 | 31 | #include "util/evsel.h" |
---|
| 32 | +#include "util/evswitch.h" |
---|
27 | 33 | #include "util/header.h" |
---|
28 | 34 | #include "util/session.h" |
---|
| 35 | +#include "util/srcline.h" |
---|
29 | 36 | #include "util/tool.h" |
---|
30 | 37 | |
---|
31 | 38 | #include <subcmd/parse-options.h> |
---|
.. | .. |
---|
40 | 47 | #include "util/time-utils.h" |
---|
41 | 48 | #include "util/auxtrace.h" |
---|
42 | 49 | #include "util/units.h" |
---|
43 | | -#include "util/branch.h" |
---|
| 50 | +#include "util/util.h" // perf_tip() |
---|
| 51 | +#include "ui/ui.h" |
---|
| 52 | +#include "ui/progress.h" |
---|
| 53 | +#include "util/block-info.h" |
---|
44 | 54 | |
---|
45 | 55 | #include <dlfcn.h> |
---|
46 | 56 | #include <errno.h> |
---|
47 | 57 | #include <inttypes.h> |
---|
48 | 58 | #include <regex.h> |
---|
| 59 | +#include <linux/ctype.h> |
---|
49 | 60 | #include <signal.h> |
---|
50 | 61 | #include <linux/bitmap.h> |
---|
| 62 | +#include <linux/string.h> |
---|
51 | 63 | #include <linux/stringify.h> |
---|
| 64 | +#include <linux/time64.h> |
---|
52 | 65 | #include <sys/types.h> |
---|
53 | 66 | #include <sys/stat.h> |
---|
54 | 67 | #include <unistd.h> |
---|
.. | .. |
---|
57 | 70 | struct report { |
---|
58 | 71 | struct perf_tool tool; |
---|
59 | 72 | struct perf_session *session; |
---|
| 73 | + struct evswitch evswitch; |
---|
60 | 74 | bool use_tui, use_gtk, use_stdio; |
---|
61 | 75 | bool show_full_info; |
---|
62 | 76 | bool show_threads; |
---|
.. | .. |
---|
69 | 83 | bool header_only; |
---|
70 | 84 | bool nonany_branch_mode; |
---|
71 | 85 | bool group_set; |
---|
| 86 | + bool stitch_lbr; |
---|
72 | 87 | int max_stack; |
---|
73 | 88 | struct perf_read_values show_threads_values; |
---|
74 | 89 | struct annotation_options annotation_opts; |
---|
.. | .. |
---|
82 | 97 | float min_percent; |
---|
83 | 98 | u64 nr_entries; |
---|
84 | 99 | u64 queue_size; |
---|
| 100 | + u64 total_cycles; |
---|
85 | 101 | int socket_filter; |
---|
86 | 102 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
---|
87 | 103 | struct branch_type_stat brtype_stat; |
---|
| 104 | + bool symbol_ipc; |
---|
| 105 | + bool total_cycles_mode; |
---|
| 106 | + struct block_report *block_reports; |
---|
| 107 | + int nr_block_reports; |
---|
88 | 108 | }; |
---|
89 | 109 | |
---|
90 | 110 | static int report__config(const char *var, const char *value, void *cb) |
---|
.. | .. |
---|
124 | 144 | int err = 0; |
---|
125 | 145 | struct report *rep = arg; |
---|
126 | 146 | struct hist_entry *he = iter->he; |
---|
127 | | - struct perf_evsel *evsel = iter->evsel; |
---|
| 147 | + struct evsel *evsel = iter->evsel; |
---|
128 | 148 | struct perf_sample *sample = iter->sample; |
---|
129 | 149 | struct mem_info *mi; |
---|
130 | 150 | struct branch_info *bi; |
---|
131 | 151 | |
---|
132 | | - if (!ui__has_annotation()) |
---|
| 152 | + if (!ui__has_annotation() && !rep->symbol_ipc) |
---|
133 | 153 | return 0; |
---|
134 | | - |
---|
135 | | - hist__account_cycles(sample->branch_stack, al, sample, |
---|
136 | | - rep->nonany_branch_mode); |
---|
137 | 154 | |
---|
138 | 155 | if (sort__mode == SORT_MODE__BRANCH) { |
---|
139 | 156 | bi = he->branch_info; |
---|
.. | .. |
---|
169 | 186 | { |
---|
170 | 187 | struct hist_entry *he = iter->he; |
---|
171 | 188 | struct report *rep = arg; |
---|
172 | | - struct branch_info *bi; |
---|
| 189 | + struct branch_info *bi = he->branch_info; |
---|
173 | 190 | struct perf_sample *sample = iter->sample; |
---|
174 | | - struct perf_evsel *evsel = iter->evsel; |
---|
| 191 | + struct evsel *evsel = iter->evsel; |
---|
175 | 192 | int err; |
---|
176 | 193 | |
---|
177 | | - if (!ui__has_annotation()) |
---|
| 194 | + branch_type_count(&rep->brtype_stat, &bi->flags, |
---|
| 195 | + bi->from.addr, bi->to.addr); |
---|
| 196 | + |
---|
| 197 | + if (!ui__has_annotation() && !rep->symbol_ipc) |
---|
178 | 198 | return 0; |
---|
179 | 199 | |
---|
180 | | - hist__account_cycles(sample->branch_stack, al, sample, |
---|
181 | | - rep->nonany_branch_mode); |
---|
182 | | - |
---|
183 | | - bi = he->branch_info; |
---|
184 | 200 | err = addr_map_symbol__inc_samples(&bi->from, sample, evsel); |
---|
185 | 201 | if (err) |
---|
186 | 202 | goto out; |
---|
187 | 203 | |
---|
188 | 204 | err = addr_map_symbol__inc_samples(&bi->to, sample, evsel); |
---|
189 | 205 | |
---|
190 | | - branch_type_count(&rep->brtype_stat, &bi->flags, |
---|
191 | | - bi->from.addr, bi->to.addr); |
---|
192 | | - |
---|
193 | 206 | out: |
---|
194 | 207 | return err; |
---|
195 | 208 | } |
---|
196 | 209 | |
---|
197 | 210 | static void setup_forced_leader(struct report *report, |
---|
198 | | - struct perf_evlist *evlist) |
---|
| 211 | + struct evlist *evlist) |
---|
199 | 212 | { |
---|
200 | 213 | if (report->group_set) |
---|
201 | 214 | perf_evlist__force_leader(evlist); |
---|
202 | 215 | } |
---|
203 | 216 | |
---|
204 | | -static int process_feature_event(struct perf_tool *tool, |
---|
205 | | - union perf_event *event, |
---|
206 | | - struct perf_session *session __maybe_unused) |
---|
| 217 | +static int process_feature_event(struct perf_session *session, |
---|
| 218 | + union perf_event *event) |
---|
207 | 219 | { |
---|
208 | | - struct report *rep = container_of(tool, struct report, tool); |
---|
| 220 | + struct report *rep = container_of(session->tool, struct report, tool); |
---|
209 | 221 | |
---|
210 | 222 | if (event->feat.feat_id < HEADER_LAST_FEATURE) |
---|
211 | | - return perf_event__process_feature(tool, event, session); |
---|
| 223 | + return perf_event__process_feature(session, event); |
---|
212 | 224 | |
---|
213 | 225 | if (event->feat.feat_id != HEADER_LAST_FEATURE) { |
---|
214 | | - pr_err("failed: wrong feature ID: %" PRIu64 "\n", |
---|
| 226 | + pr_err("failed: wrong feature ID: %" PRI_lu64 "\n", |
---|
215 | 227 | event->feat.feat_id); |
---|
216 | 228 | return -1; |
---|
217 | 229 | } |
---|
.. | .. |
---|
228 | 240 | static int process_sample_event(struct perf_tool *tool, |
---|
229 | 241 | union perf_event *event, |
---|
230 | 242 | struct perf_sample *sample, |
---|
231 | | - struct perf_evsel *evsel, |
---|
| 243 | + struct evsel *evsel, |
---|
232 | 244 | struct machine *machine) |
---|
233 | 245 | { |
---|
234 | 246 | struct report *rep = container_of(tool, struct report, tool); |
---|
.. | .. |
---|
246 | 258 | return 0; |
---|
247 | 259 | } |
---|
248 | 260 | |
---|
| 261 | + if (evswitch__discard(&rep->evswitch, evsel)) |
---|
| 262 | + return 0; |
---|
| 263 | + |
---|
249 | 264 | if (machine__resolve(machine, &al, sample) < 0) { |
---|
250 | 265 | pr_debug("problem processing %d event, skipping it.\n", |
---|
251 | 266 | event->header.type); |
---|
252 | 267 | return -1; |
---|
253 | 268 | } |
---|
| 269 | + |
---|
| 270 | + if (rep->stitch_lbr) |
---|
| 271 | + al.thread->lbr_stitch_enable = true; |
---|
254 | 272 | |
---|
255 | 273 | if (symbol_conf.hide_unresolved && al.sym == NULL) |
---|
256 | 274 | goto out_put; |
---|
.. | .. |
---|
279 | 297 | if (al.map != NULL) |
---|
280 | 298 | al.map->dso->hit = 1; |
---|
281 | 299 | |
---|
| 300 | + if (ui__has_annotation() || rep->symbol_ipc || rep->total_cycles_mode) { |
---|
| 301 | + hist__account_cycles(sample->branch_stack, &al, sample, |
---|
| 302 | + rep->nonany_branch_mode, |
---|
| 303 | + &rep->total_cycles); |
---|
| 304 | + } |
---|
| 305 | + |
---|
282 | 306 | ret = hist_entry_iter__add(&iter, &al, rep->max_stack, rep); |
---|
283 | 307 | if (ret < 0) |
---|
284 | 308 | pr_debug("problem adding hist entry, skipping event\n"); |
---|
.. | .. |
---|
290 | 314 | static int process_read_event(struct perf_tool *tool, |
---|
291 | 315 | union perf_event *event, |
---|
292 | 316 | struct perf_sample *sample __maybe_unused, |
---|
293 | | - struct perf_evsel *evsel, |
---|
| 317 | + struct evsel *evsel, |
---|
294 | 318 | struct machine *machine __maybe_unused) |
---|
295 | 319 | { |
---|
296 | 320 | struct report *rep = container_of(tool, struct report, tool); |
---|
297 | 321 | |
---|
298 | 322 | if (rep->show_threads) { |
---|
299 | | - const char *name = evsel ? perf_evsel__name(evsel) : "unknown"; |
---|
| 323 | + const char *name = evsel__name(evsel); |
---|
300 | 324 | int err = perf_read_values_add_value(&rep->show_threads_values, |
---|
301 | 325 | event->read.pid, event->read.tid, |
---|
302 | 326 | evsel->idx, |
---|
.. | .. |
---|
314 | 338 | static int report__setup_sample_type(struct report *rep) |
---|
315 | 339 | { |
---|
316 | 340 | struct perf_session *session = rep->session; |
---|
317 | | - u64 sample_type = perf_evlist__combined_sample_type(session->evlist); |
---|
| 341 | + u64 sample_type = evlist__combined_sample_type(session->evlist); |
---|
318 | 342 | bool is_pipe = perf_data__is_pipe(session->data); |
---|
| 343 | + struct evsel *evsel; |
---|
319 | 344 | |
---|
320 | 345 | if (session->itrace_synth_opts->callchain || |
---|
| 346 | + session->itrace_synth_opts->add_callchain || |
---|
321 | 347 | (!is_pipe && |
---|
322 | 348 | perf_header__has_feat(&session->header, HEADER_AUXTRACE) && |
---|
323 | 349 | !session->itrace_synth_opts->set)) |
---|
324 | 350 | sample_type |= PERF_SAMPLE_CALLCHAIN; |
---|
325 | 351 | |
---|
326 | | - if (session->itrace_synth_opts->last_branch) |
---|
| 352 | + if (session->itrace_synth_opts->last_branch || |
---|
| 353 | + session->itrace_synth_opts->add_last_branch) |
---|
327 | 354 | sample_type |= PERF_SAMPLE_BRANCH_STACK; |
---|
328 | 355 | |
---|
329 | 356 | if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { |
---|
.. | .. |
---|
367 | 394 | } |
---|
368 | 395 | } |
---|
369 | 396 | |
---|
370 | | - if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) { |
---|
371 | | - if ((sample_type & PERF_SAMPLE_REGS_USER) && |
---|
372 | | - (sample_type & PERF_SAMPLE_STACK_USER)) { |
---|
373 | | - callchain_param.record_mode = CALLCHAIN_DWARF; |
---|
374 | | - dwarf_callchain_users = true; |
---|
375 | | - } else if (sample_type & PERF_SAMPLE_BRANCH_STACK) |
---|
376 | | - callchain_param.record_mode = CALLCHAIN_LBR; |
---|
377 | | - else |
---|
378 | | - callchain_param.record_mode = CALLCHAIN_FP; |
---|
| 397 | + if (sort__mode == SORT_MODE__MEMORY) { |
---|
| 398 | + /* |
---|
| 399 | + * FIXUP: prior to kernel 5.18, Arm SPE missed to set |
---|
| 400 | + * PERF_SAMPLE_DATA_SRC bit in sample type. For backward |
---|
| 401 | + * compatibility, set the bit if it's an old perf data file. |
---|
| 402 | + */ |
---|
| 403 | + evlist__for_each_entry(session->evlist, evsel) { |
---|
| 404 | + if (strstr(evsel->name, "arm_spe") && |
---|
| 405 | + !(sample_type & PERF_SAMPLE_DATA_SRC)) { |
---|
| 406 | + evsel->core.attr.sample_type |= PERF_SAMPLE_DATA_SRC; |
---|
| 407 | + sample_type |= PERF_SAMPLE_DATA_SRC; |
---|
| 408 | + } |
---|
| 409 | + } |
---|
| 410 | + |
---|
| 411 | + if (!is_pipe && !(sample_type & PERF_SAMPLE_DATA_SRC)) { |
---|
| 412 | + ui__error("Selected --mem-mode but no mem data. " |
---|
| 413 | + "Did you call perf record without -d?\n"); |
---|
| 414 | + return -1; |
---|
| 415 | + } |
---|
| 416 | + } |
---|
| 417 | + |
---|
| 418 | + callchain_param_setup(sample_type); |
---|
| 419 | + |
---|
| 420 | + if (rep->stitch_lbr && (callchain_param.record_mode != CALLCHAIN_LBR)) { |
---|
| 421 | + ui__warning("Can't find LBR callchain. Switch off --stitch-lbr.\n" |
---|
| 422 | + "Please apply --call-graph lbr when recording.\n"); |
---|
| 423 | + rep->stitch_lbr = false; |
---|
379 | 424 | } |
---|
380 | 425 | |
---|
381 | 426 | /* ??? handle more cases than just ANY? */ |
---|
382 | | - if (!(perf_evlist__combined_branch_type(session->evlist) & |
---|
383 | | - PERF_SAMPLE_BRANCH_ANY)) |
---|
| 427 | + if (!(evlist__combined_branch_type(session->evlist) & PERF_SAMPLE_BRANCH_ANY)) |
---|
384 | 428 | rep->nonany_branch_mode = true; |
---|
385 | 429 | |
---|
386 | 430 | #if !defined(HAVE_LIBUNWIND_SUPPORT) && !defined(HAVE_DWARF_SUPPORT) |
---|
.. | .. |
---|
405 | 449 | char unit; |
---|
406 | 450 | unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
---|
407 | 451 | u64 nr_events = hists->stats.total_period; |
---|
408 | | - struct perf_evsel *evsel = hists_to_evsel(hists); |
---|
| 452 | + struct evsel *evsel = hists_to_evsel(hists); |
---|
409 | 453 | char buf[512]; |
---|
410 | 454 | size_t size = sizeof(buf); |
---|
411 | 455 | int socked_id = hists->socket_filter; |
---|
.. | .. |
---|
418 | 462 | nr_events = hists->stats.total_non_filtered_period; |
---|
419 | 463 | } |
---|
420 | 464 | |
---|
421 | | - if (perf_evsel__is_group_event(evsel)) { |
---|
422 | | - struct perf_evsel *pos; |
---|
| 465 | + if (evsel__is_group_event(evsel)) { |
---|
| 466 | + struct evsel *pos; |
---|
423 | 467 | |
---|
424 | | - perf_evsel__group_desc(evsel, buf, size); |
---|
| 468 | + evsel__group_desc(evsel, buf, size); |
---|
425 | 469 | evname = buf; |
---|
426 | 470 | |
---|
427 | 471 | for_each_group_member(pos, evsel) { |
---|
.. | .. |
---|
441 | 485 | ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit); |
---|
442 | 486 | if (evname != NULL) { |
---|
443 | 487 | ret += fprintf(fp, " of event%s '%s'", |
---|
444 | | - evsel->nr_members > 1 ? "s" : "", evname); |
---|
| 488 | + evsel->core.nr_members > 1 ? "s" : "", evname); |
---|
445 | 489 | } |
---|
446 | 490 | |
---|
447 | 491 | if (rep->time_str) |
---|
.. | .. |
---|
463 | 507 | return ret + fprintf(fp, "\n#\n"); |
---|
464 | 508 | } |
---|
465 | 509 | |
---|
466 | | -static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, |
---|
| 510 | +static int perf_evlist__tui_block_hists_browse(struct evlist *evlist, |
---|
| 511 | + struct report *rep) |
---|
| 512 | +{ |
---|
| 513 | + struct evsel *pos; |
---|
| 514 | + int i = 0, ret; |
---|
| 515 | + |
---|
| 516 | + evlist__for_each_entry(evlist, pos) { |
---|
| 517 | + ret = report__browse_block_hists(&rep->block_reports[i++].hist, |
---|
| 518 | + rep->min_percent, pos, |
---|
| 519 | + &rep->session->header.env, |
---|
| 520 | + &rep->annotation_opts); |
---|
| 521 | + if (ret != 0) |
---|
| 522 | + return ret; |
---|
| 523 | + } |
---|
| 524 | + |
---|
| 525 | + return 0; |
---|
| 526 | +} |
---|
| 527 | + |
---|
| 528 | +static int perf_evlist__tty_browse_hists(struct evlist *evlist, |
---|
467 | 529 | struct report *rep, |
---|
468 | 530 | const char *help) |
---|
469 | 531 | { |
---|
470 | | - struct perf_evsel *pos; |
---|
| 532 | + struct evsel *pos; |
---|
| 533 | + int i = 0; |
---|
471 | 534 | |
---|
472 | 535 | if (!quiet) { |
---|
473 | 536 | fprintf(stdout, "#\n# Total Lost Samples: %" PRIu64 "\n#\n", |
---|
.. | .. |
---|
476 | 539 | |
---|
477 | 540 | evlist__for_each_entry(evlist, pos) { |
---|
478 | 541 | struct hists *hists = evsel__hists(pos); |
---|
479 | | - const char *evname = perf_evsel__name(pos); |
---|
| 542 | + const char *evname = evsel__name(pos); |
---|
480 | 543 | |
---|
481 | | - if (symbol_conf.event_group && |
---|
482 | | - !perf_evsel__is_group_leader(pos)) |
---|
| 544 | + if (symbol_conf.event_group && !evsel__is_group_leader(pos)) |
---|
483 | 545 | continue; |
---|
484 | 546 | |
---|
485 | 547 | hists__fprintf_nr_sample_events(hists, rep, evname, stdout); |
---|
| 548 | + |
---|
| 549 | + if (rep->total_cycles_mode) { |
---|
| 550 | + report__browse_block_hists(&rep->block_reports[i++].hist, |
---|
| 551 | + rep->min_percent, pos, |
---|
| 552 | + NULL, NULL); |
---|
| 553 | + continue; |
---|
| 554 | + } |
---|
| 555 | + |
---|
486 | 556 | hists__fprintf(hists, !quiet, 0, 0, rep->min_percent, stdout, |
---|
487 | 557 | !(symbol_conf.use_callchain || |
---|
488 | 558 | symbol_conf.show_branchflag_count)); |
---|
.. | .. |
---|
536 | 606 | |
---|
537 | 607 | static int report__gtk_browse_hists(struct report *rep, const char *help) |
---|
538 | 608 | { |
---|
539 | | - int (*hist_browser)(struct perf_evlist *evlist, const char *help, |
---|
| 609 | + int (*hist_browser)(struct evlist *evlist, const char *help, |
---|
540 | 610 | struct hist_browser_timer *timer, float min_pcnt); |
---|
541 | 611 | |
---|
542 | 612 | hist_browser = dlsym(perf_gtk_handle, "perf_evlist__gtk_browse_hists"); |
---|
.. | .. |
---|
553 | 623 | { |
---|
554 | 624 | int ret; |
---|
555 | 625 | struct perf_session *session = rep->session; |
---|
556 | | - struct perf_evlist *evlist = session->evlist; |
---|
557 | | - const char *help = perf_tip(system_path(TIPDIR)); |
---|
| 626 | + struct evlist *evlist = session->evlist; |
---|
| 627 | + char *help = NULL, *path = NULL; |
---|
558 | 628 | |
---|
559 | | - if (help == NULL) { |
---|
| 629 | + path = system_path(TIPDIR); |
---|
| 630 | + if (perf_tip(&help, path) || help == NULL) { |
---|
560 | 631 | /* fallback for people who don't install perf ;-) */ |
---|
561 | | - help = perf_tip(DOCDIR); |
---|
562 | | - if (help == NULL) |
---|
563 | | - help = "Cannot load tips.txt file, please install perf!"; |
---|
| 632 | + free(path); |
---|
| 633 | + path = system_path(DOCDIR); |
---|
| 634 | + if (perf_tip(&help, path) || help == NULL) |
---|
| 635 | + help = strdup("Cannot load tips.txt file, please install perf!"); |
---|
564 | 636 | } |
---|
| 637 | + free(path); |
---|
565 | 638 | |
---|
566 | 639 | switch (use_browser) { |
---|
567 | 640 | case 1: |
---|
| 641 | + if (rep->total_cycles_mode) { |
---|
| 642 | + ret = perf_evlist__tui_block_hists_browse(evlist, rep); |
---|
| 643 | + break; |
---|
| 644 | + } |
---|
| 645 | + |
---|
568 | 646 | ret = perf_evlist__tui_browse_hists(evlist, help, NULL, |
---|
569 | 647 | rep->min_percent, |
---|
570 | 648 | &session->header.env, |
---|
.. | .. |
---|
573 | 651 | * Usually "ret" is the last pressed key, and we only |
---|
574 | 652 | * care if the key notifies us to switch data file. |
---|
575 | 653 | */ |
---|
576 | | - if (ret != K_SWITCH_INPUT_DATA) |
---|
| 654 | + if (ret != K_SWITCH_INPUT_DATA && ret != K_RELOAD) |
---|
577 | 655 | ret = 0; |
---|
578 | 656 | break; |
---|
579 | 657 | case 2: |
---|
.. | .. |
---|
583 | 661 | ret = perf_evlist__tty_browse_hists(evlist, rep, help); |
---|
584 | 662 | break; |
---|
585 | 663 | } |
---|
586 | | - |
---|
| 664 | + free(help); |
---|
587 | 665 | return ret; |
---|
588 | 666 | } |
---|
589 | 667 | |
---|
590 | 668 | static int report__collapse_hists(struct report *rep) |
---|
591 | 669 | { |
---|
592 | 670 | struct ui_progress prog; |
---|
593 | | - struct perf_evsel *pos; |
---|
| 671 | + struct evsel *pos; |
---|
594 | 672 | int ret = 0; |
---|
595 | 673 | |
---|
596 | 674 | ui_progress__init(&prog, rep->nr_entries, "Merging related events..."); |
---|
.. | .. |
---|
608 | 686 | break; |
---|
609 | 687 | |
---|
610 | 688 | /* Non-group events are considered as leader */ |
---|
611 | | - if (symbol_conf.event_group && |
---|
612 | | - !perf_evsel__is_group_leader(pos)) { |
---|
| 689 | + if (symbol_conf.event_group && !evsel__is_group_leader(pos)) { |
---|
613 | 690 | struct hists *leader_hists = evsel__hists(pos->leader); |
---|
614 | 691 | |
---|
615 | 692 | hists__match(leader_hists, hists); |
---|
.. | .. |
---|
621 | 698 | return ret; |
---|
622 | 699 | } |
---|
623 | 700 | |
---|
| 701 | +static int hists__resort_cb(struct hist_entry *he, void *arg) |
---|
| 702 | +{ |
---|
| 703 | + struct report *rep = arg; |
---|
| 704 | + struct symbol *sym = he->ms.sym; |
---|
| 705 | + |
---|
| 706 | + if (rep->symbol_ipc && sym && !sym->annotate2) { |
---|
| 707 | + struct evsel *evsel = hists_to_evsel(he->hists); |
---|
| 708 | + |
---|
| 709 | + symbol__annotate2(&he->ms, evsel, |
---|
| 710 | + &annotation__default_options, NULL); |
---|
| 711 | + } |
---|
| 712 | + |
---|
| 713 | + return 0; |
---|
| 714 | +} |
---|
| 715 | + |
---|
624 | 716 | static void report__output_resort(struct report *rep) |
---|
625 | 717 | { |
---|
626 | 718 | struct ui_progress prog; |
---|
627 | | - struct perf_evsel *pos; |
---|
| 719 | + struct evsel *pos; |
---|
628 | 720 | |
---|
629 | 721 | ui_progress__init(&prog, rep->nr_entries, "Sorting events for output..."); |
---|
630 | 722 | |
---|
631 | | - evlist__for_each_entry(rep->session->evlist, pos) |
---|
632 | | - perf_evsel__output_resort(pos, &prog); |
---|
| 723 | + evlist__for_each_entry(rep->session->evlist, pos) { |
---|
| 724 | + evsel__output_resort_cb(pos, &prog, hists__resort_cb, rep); |
---|
| 725 | + } |
---|
633 | 726 | |
---|
634 | 727 | ui_progress__finish(); |
---|
635 | 728 | } |
---|
.. | .. |
---|
693 | 786 | static size_t maps__fprintf_task(struct maps *maps, int indent, FILE *fp) |
---|
694 | 787 | { |
---|
695 | 788 | size_t printed = 0; |
---|
696 | | - struct rb_node *nd; |
---|
| 789 | + struct map *map; |
---|
697 | 790 | |
---|
698 | | - for (nd = rb_first(&maps->entries); nd; nd = rb_next(nd)) { |
---|
699 | | - struct map *map = rb_entry(nd, struct map, rb_node); |
---|
700 | | - |
---|
| 791 | + maps__for_each_entry(maps, map) { |
---|
701 | 792 | printed += fprintf(fp, "%*s %" PRIx64 "-%" PRIx64 " %c%c%c%c %08" PRIx64 " %" PRIu64 " %s\n", |
---|
702 | 793 | indent, "", map->start, map->end, |
---|
703 | 794 | map->prot & PROT_READ ? 'r' : '-', |
---|
.. | .. |
---|
705 | 796 | map->prot & PROT_EXEC ? 'x' : '-', |
---|
706 | 797 | map->flags & MAP_SHARED ? 's' : 'p', |
---|
707 | 798 | map->pgoff, |
---|
708 | | - map->ino, map->dso->name); |
---|
| 799 | + map->dso->id.ino, map->dso->name); |
---|
709 | 800 | } |
---|
710 | 801 | |
---|
711 | 802 | return printed; |
---|
712 | | -} |
---|
713 | | - |
---|
714 | | -static int map_groups__fprintf_task(struct map_groups *mg, int indent, FILE *fp) |
---|
715 | | -{ |
---|
716 | | - return maps__fprintf_task(&mg->maps, indent, fp); |
---|
717 | 803 | } |
---|
718 | 804 | |
---|
719 | 805 | static void task__print_level(struct task *task, FILE *fp, int level) |
---|
.. | .. |
---|
726 | 812 | |
---|
727 | 813 | fprintf(fp, "%s\n", thread__comm_str(thread)); |
---|
728 | 814 | |
---|
729 | | - map_groups__fprintf_task(thread->mg, comm_indent, fp); |
---|
| 815 | + maps__fprintf_task(thread->maps, comm_indent, fp); |
---|
730 | 816 | |
---|
731 | 817 | if (!list_empty(&task->children)) { |
---|
732 | 818 | list_for_each_entry(child, &task->children, list) |
---|
.. | .. |
---|
759 | 845 | for (i = 0; i < THREADS__TABLE_SIZE; i++) { |
---|
760 | 846 | struct threads *threads = &machine->threads[i]; |
---|
761 | 847 | |
---|
762 | | - for (nd = rb_first(&threads->entries); nd; nd = rb_next(nd)) { |
---|
| 848 | + for (nd = rb_first_cached(&threads->entries); nd; |
---|
| 849 | + nd = rb_next(nd)) { |
---|
763 | 850 | task = tasks + itask++; |
---|
764 | 851 | |
---|
765 | 852 | task->thread = rb_entry(nd, struct thread, rb_node); |
---|
.. | .. |
---|
804 | 891 | { |
---|
805 | 892 | int ret; |
---|
806 | 893 | struct perf_session *session = rep->session; |
---|
807 | | - struct perf_evsel *pos; |
---|
| 894 | + struct evsel *pos; |
---|
808 | 895 | struct perf_data *data = session->data; |
---|
809 | 896 | |
---|
810 | 897 | signal(SIGINT, sig_handler); |
---|
.. | .. |
---|
886 | 973 | rep->nr_entries += evsel__hists(pos)->nr_entries; |
---|
887 | 974 | |
---|
888 | 975 | if (rep->nr_entries == 0) { |
---|
889 | | - ui__error("The %s file has no samples!\n", data->file.path); |
---|
| 976 | + ui__error("The %s data has no samples!\n", data->path); |
---|
890 | 977 | return 0; |
---|
891 | 978 | } |
---|
892 | 979 | |
---|
893 | 980 | report__output_resort(rep); |
---|
| 981 | + |
---|
| 982 | + if (rep->total_cycles_mode) { |
---|
| 983 | + int block_hpps[6] = { |
---|
| 984 | + PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT, |
---|
| 985 | + PERF_HPP_REPORT__BLOCK_LBR_CYCLES, |
---|
| 986 | + PERF_HPP_REPORT__BLOCK_CYCLES_PCT, |
---|
| 987 | + PERF_HPP_REPORT__BLOCK_AVG_CYCLES, |
---|
| 988 | + PERF_HPP_REPORT__BLOCK_RANGE, |
---|
| 989 | + PERF_HPP_REPORT__BLOCK_DSO, |
---|
| 990 | + }; |
---|
| 991 | + |
---|
| 992 | + rep->block_reports = block_info__create_report(session->evlist, |
---|
| 993 | + rep->total_cycles, |
---|
| 994 | + block_hpps, 6, |
---|
| 995 | + &rep->nr_block_reports); |
---|
| 996 | + if (!rep->block_reports) |
---|
| 997 | + return -1; |
---|
| 998 | + } |
---|
894 | 999 | |
---|
895 | 1000 | return report__browse_hists(rep); |
---|
896 | 1001 | } |
---|
.. | .. |
---|
911 | 1016 | } |
---|
912 | 1017 | |
---|
913 | 1018 | return parse_callchain_report_opt(arg); |
---|
| 1019 | +} |
---|
| 1020 | + |
---|
| 1021 | +static int |
---|
| 1022 | +parse_time_quantum(const struct option *opt, const char *arg, |
---|
| 1023 | + int unset __maybe_unused) |
---|
| 1024 | +{ |
---|
| 1025 | + unsigned long *time_q = opt->value; |
---|
| 1026 | + char *end; |
---|
| 1027 | + |
---|
| 1028 | + *time_q = strtoul(arg, &end, 0); |
---|
| 1029 | + if (end == arg) |
---|
| 1030 | + goto parse_err; |
---|
| 1031 | + if (*time_q == 0) { |
---|
| 1032 | + pr_err("time quantum cannot be 0"); |
---|
| 1033 | + return -1; |
---|
| 1034 | + } |
---|
| 1035 | + end = skip_spaces(end); |
---|
| 1036 | + if (*end == 0) |
---|
| 1037 | + return 0; |
---|
| 1038 | + if (!strcmp(end, "s")) { |
---|
| 1039 | + *time_q *= NSEC_PER_SEC; |
---|
| 1040 | + return 0; |
---|
| 1041 | + } |
---|
| 1042 | + if (!strcmp(end, "ms")) { |
---|
| 1043 | + *time_q *= NSEC_PER_MSEC; |
---|
| 1044 | + return 0; |
---|
| 1045 | + } |
---|
| 1046 | + if (!strcmp(end, "us")) { |
---|
| 1047 | + *time_q *= NSEC_PER_USEC; |
---|
| 1048 | + return 0; |
---|
| 1049 | + } |
---|
| 1050 | + if (!strcmp(end, "ns")) |
---|
| 1051 | + return 0; |
---|
| 1052 | +parse_err: |
---|
| 1053 | + pr_err("Cannot parse time quantum `%s'\n", arg); |
---|
| 1054 | + return -1; |
---|
914 | 1055 | } |
---|
915 | 1056 | |
---|
916 | 1057 | int |
---|
.. | .. |
---|
953 | 1094 | return 0; |
---|
954 | 1095 | } |
---|
955 | 1096 | |
---|
| 1097 | +static int process_attr(struct perf_tool *tool __maybe_unused, |
---|
| 1098 | + union perf_event *event, |
---|
| 1099 | + struct evlist **pevlist) |
---|
| 1100 | +{ |
---|
| 1101 | + u64 sample_type; |
---|
| 1102 | + int err; |
---|
| 1103 | + |
---|
| 1104 | + err = perf_event__process_attr(tool, event, pevlist); |
---|
| 1105 | + if (err) |
---|
| 1106 | + return err; |
---|
| 1107 | + |
---|
| 1108 | + /* |
---|
| 1109 | + * Check if we need to enable callchains based |
---|
| 1110 | + * on events sample_type. |
---|
| 1111 | + */ |
---|
| 1112 | + sample_type = evlist__combined_sample_type(*pevlist); |
---|
| 1113 | + callchain_param_setup(sample_type); |
---|
| 1114 | + return 0; |
---|
| 1115 | +} |
---|
| 1116 | + |
---|
956 | 1117 | int cmd_report(int argc, const char **argv) |
---|
957 | 1118 | { |
---|
958 | 1119 | struct perf_session *session; |
---|
.. | .. |
---|
963 | 1124 | int last_key = 0; |
---|
964 | 1125 | bool branch_call_mode = false; |
---|
965 | 1126 | #define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function,percent" |
---|
966 | | - const char report_callchain_help[] = "Display call graph (stack chain/backtrace):\n\n" |
---|
967 | | - CALLCHAIN_REPORT_HELP |
---|
968 | | - "\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT; |
---|
| 1127 | + static const char report_callchain_help[] = "Display call graph (stack chain/backtrace):\n\n" |
---|
| 1128 | + CALLCHAIN_REPORT_HELP |
---|
| 1129 | + "\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT; |
---|
969 | 1130 | char callchain_default_opt[] = CALLCHAIN_DEFAULT_OPT; |
---|
970 | 1131 | const char * const report_usage[] = { |
---|
971 | 1132 | "perf report [<options>]", |
---|
.. | .. |
---|
978 | 1139 | .mmap2 = perf_event__process_mmap2, |
---|
979 | 1140 | .comm = perf_event__process_comm, |
---|
980 | 1141 | .namespaces = perf_event__process_namespaces, |
---|
| 1142 | + .cgroup = perf_event__process_cgroup, |
---|
981 | 1143 | .exit = perf_event__process_exit, |
---|
982 | 1144 | .fork = perf_event__process_fork, |
---|
983 | 1145 | .lost = perf_event__process_lost, |
---|
984 | 1146 | .read = process_read_event, |
---|
985 | | - .attr = perf_event__process_attr, |
---|
| 1147 | + .attr = process_attr, |
---|
986 | 1148 | .tracing_data = perf_event__process_tracing_data, |
---|
987 | 1149 | .build_id = perf_event__process_build_id, |
---|
988 | 1150 | .id_index = perf_event__process_id_index, |
---|
.. | .. |
---|
998 | 1160 | .socket_filter = -1, |
---|
999 | 1161 | .annotation_opts = annotation__default_options, |
---|
1000 | 1162 | }; |
---|
| 1163 | + char *sort_order_help = sort_help("sort by key(s):"); |
---|
| 1164 | + char *field_order_help = sort_help("output field(s): overhead period sample "); |
---|
1001 | 1165 | const struct option options[] = { |
---|
1002 | 1166 | OPT_STRING('i', "input", &input_name, "file", |
---|
1003 | 1167 | "input file name"), |
---|
.. | .. |
---|
1032 | 1196 | OPT_BOOLEAN(0, "header-only", &report.header_only, |
---|
1033 | 1197 | "Show only data header."), |
---|
1034 | 1198 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
---|
1035 | | - "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline, ..." |
---|
1036 | | - " Please refer the man page for the complete list."), |
---|
| 1199 | + sort_order_help), |
---|
1037 | 1200 | OPT_STRING('F', "fields", &field_order, "key[,keys...]", |
---|
1038 | | - "output field(s): overhead, period, sample plus all of sort keys"), |
---|
| 1201 | + field_order_help), |
---|
1039 | 1202 | OPT_BOOLEAN(0, "show-cpu-utilization", &symbol_conf.show_cpu_utilization, |
---|
1040 | 1203 | "Show sample percentage for different cpu modes"), |
---|
1041 | 1204 | OPT_BOOLEAN_FLAG(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, |
---|
.. | .. |
---|
1049 | 1212 | report_callchain_help, &report_parse_callchain_opt, |
---|
1050 | 1213 | callchain_default_opt), |
---|
1051 | 1214 | OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain, |
---|
1052 | | - "Accumulate callchains of children and show total overhead as well"), |
---|
| 1215 | + "Accumulate callchains of children and show total overhead as well. " |
---|
| 1216 | + "Enabled by default, use --no-children to disable."), |
---|
1053 | 1217 | OPT_INTEGER(0, "max-stack", &report.max_stack, |
---|
1054 | 1218 | "Set the maximum stack depth when parsing the callchain, " |
---|
1055 | 1219 | "anything beyond the specified depth will be ignored. " |
---|
.. | .. |
---|
1092 | 1256 | "Display raw encoding of assembly instructions (default)"), |
---|
1093 | 1257 | OPT_STRING('M', "disassembler-style", &report.annotation_opts.disassembler_style, "disassembler style", |
---|
1094 | 1258 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
---|
| 1259 | + OPT_STRING(0, "prefix", &report.annotation_opts.prefix, "prefix", |
---|
| 1260 | + "Add prefix to source file path names in programs (with --prefix-strip)"), |
---|
| 1261 | + OPT_STRING(0, "prefix-strip", &report.annotation_opts.prefix_strip, "N", |
---|
| 1262 | + "Strip first N entries of source file path name in programs (with --prefix)"), |
---|
1095 | 1263 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, |
---|
1096 | 1264 | "Show a column with the sum of periods"), |
---|
1097 | 1265 | OPT_BOOLEAN_SET(0, "group", &symbol_conf.event_group, &report.group_set, |
---|
1098 | 1266 | "Show event group information together"), |
---|
| 1267 | + OPT_INTEGER(0, "group-sort-idx", &symbol_conf.group_sort_idx, |
---|
| 1268 | + "Sort the output by the event at the index n in group. " |
---|
| 1269 | + "If n is invalid, sort by the first event. " |
---|
| 1270 | + "WARNING: should be used on grouped events."), |
---|
1099 | 1271 | OPT_CALLBACK_NOOPT('b', "branch-stack", &branch_mode, "", |
---|
1100 | 1272 | "use branch records for per branch histogram filling", |
---|
1101 | 1273 | parse_branch_mode), |
---|
.. | .. |
---|
1108 | 1280 | OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, |
---|
1109 | 1281 | "Enable kernel symbol demangling"), |
---|
1110 | 1282 | OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), |
---|
| 1283 | + OPT_INTEGER(0, "samples", &symbol_conf.res_sample, |
---|
| 1284 | + "Number of samples to save per histogram entry for individual browsing"), |
---|
1111 | 1285 | OPT_CALLBACK(0, "percent-limit", &report, "percent", |
---|
1112 | 1286 | "Don't show entries under that percent", parse_percent_limit), |
---|
1113 | 1287 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", |
---|
1114 | 1288 | "how to display percentage of filtered entries", parse_filter_percentage), |
---|
1115 | 1289 | OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts", |
---|
1116 | | - "Instruction Tracing options", |
---|
| 1290 | + "Instruction Tracing options\n" ITRACE_HELP, |
---|
1117 | 1291 | itrace_parse_synth_opts), |
---|
1118 | 1292 | OPT_BOOLEAN(0, "full-source-path", &srcline_full_filename, |
---|
1119 | 1293 | "Show full source file name path for source lines"), |
---|
1120 | 1294 | OPT_BOOLEAN(0, "show-ref-call-graph", &symbol_conf.show_ref_callgraph, |
---|
1121 | 1295 | "Show callgraph from reference event"), |
---|
| 1296 | + OPT_BOOLEAN(0, "stitch-lbr", &report.stitch_lbr, |
---|
| 1297 | + "Enable LBR callgraph stitching approach"), |
---|
1122 | 1298 | OPT_INTEGER(0, "socket-filter", &report.socket_filter, |
---|
1123 | 1299 | "only show processor socket that match with this filter"), |
---|
1124 | 1300 | OPT_BOOLEAN(0, "raw-trace", &symbol_conf.raw_trace, |
---|
.. | .. |
---|
1135 | 1311 | OPT_CALLBACK(0, "percent-type", &report.annotation_opts, "local-period", |
---|
1136 | 1312 | "Set percent type local/global-period/hits", |
---|
1137 | 1313 | annotate_parse_percent_type), |
---|
| 1314 | + OPT_BOOLEAN(0, "ns", &symbol_conf.nanosecs, "Show times in nanosecs"), |
---|
| 1315 | + OPT_CALLBACK(0, "time-quantum", &symbol_conf.time_quantum, "time (ms|us|ns|s)", |
---|
| 1316 | + "Set time quantum for time sort key (default 100ms)", |
---|
| 1317 | + parse_time_quantum), |
---|
| 1318 | + OPTS_EVSWITCH(&report.evswitch), |
---|
| 1319 | + OPT_BOOLEAN(0, "total-cycles", &report.total_cycles_mode, |
---|
| 1320 | + "Sort all blocks by 'Sampled Cycles%'"), |
---|
1138 | 1321 | OPT_END() |
---|
1139 | 1322 | }; |
---|
1140 | 1323 | struct perf_data data = { |
---|
1141 | 1324 | .mode = PERF_DATA_MODE_READ, |
---|
1142 | 1325 | }; |
---|
1143 | 1326 | int ret = hists__init(); |
---|
| 1327 | + char sort_tmp[128]; |
---|
1144 | 1328 | |
---|
1145 | 1329 | if (ret < 0) |
---|
1146 | | - return ret; |
---|
| 1330 | + goto exit; |
---|
1147 | 1331 | |
---|
1148 | 1332 | ret = perf_config(report__config, &report); |
---|
1149 | 1333 | if (ret) |
---|
1150 | | - return ret; |
---|
| 1334 | + goto exit; |
---|
1151 | 1335 | |
---|
1152 | 1336 | argc = parse_options(argc, argv, options, report_usage, 0); |
---|
1153 | 1337 | if (argc) { |
---|
.. | .. |
---|
1161 | 1345 | report.symbol_filter_str = argv[0]; |
---|
1162 | 1346 | } |
---|
1163 | 1347 | |
---|
| 1348 | + if (annotate_check_args(&report.annotation_opts) < 0) { |
---|
| 1349 | + ret = -EINVAL; |
---|
| 1350 | + goto exit; |
---|
| 1351 | + } |
---|
| 1352 | + |
---|
1164 | 1353 | if (report.mmaps_mode) |
---|
1165 | 1354 | report.tasks_mode = true; |
---|
| 1355 | + |
---|
| 1356 | + if (dump_trace) |
---|
| 1357 | + report.tool.ordered_events = false; |
---|
1166 | 1358 | |
---|
1167 | 1359 | if (quiet) |
---|
1168 | 1360 | perf_quiet_option(); |
---|
.. | .. |
---|
1170 | 1362 | if (symbol_conf.vmlinux_name && |
---|
1171 | 1363 | access(symbol_conf.vmlinux_name, R_OK)) { |
---|
1172 | 1364 | pr_err("Invalid file: %s\n", symbol_conf.vmlinux_name); |
---|
1173 | | - return -EINVAL; |
---|
| 1365 | + ret = -EINVAL; |
---|
| 1366 | + goto exit; |
---|
1174 | 1367 | } |
---|
1175 | 1368 | if (symbol_conf.kallsyms_name && |
---|
1176 | 1369 | access(symbol_conf.kallsyms_name, R_OK)) { |
---|
1177 | 1370 | pr_err("Invalid file: %s\n", symbol_conf.kallsyms_name); |
---|
1178 | | - return -EINVAL; |
---|
| 1371 | + ret = -EINVAL; |
---|
| 1372 | + goto exit; |
---|
1179 | 1373 | } |
---|
1180 | 1374 | |
---|
1181 | 1375 | if (report.inverted_callchain) |
---|
.. | .. |
---|
1183 | 1377 | if (symbol_conf.cumulate_callchain && !callchain_param.order_set) |
---|
1184 | 1378 | callchain_param.order = ORDER_CALLER; |
---|
1185 | 1379 | |
---|
1186 | | - if (itrace_synth_opts.callchain && |
---|
| 1380 | + if ((itrace_synth_opts.callchain || itrace_synth_opts.add_callchain) && |
---|
1187 | 1381 | (int)itrace_synth_opts.callchain_sz > report.max_stack) |
---|
1188 | 1382 | report.max_stack = itrace_synth_opts.callchain_sz; |
---|
1189 | 1383 | |
---|
.. | .. |
---|
1194 | 1388 | input_name = "perf.data"; |
---|
1195 | 1389 | } |
---|
1196 | 1390 | |
---|
1197 | | - data.file.path = input_name; |
---|
1198 | | - data.force = symbol_conf.force; |
---|
| 1391 | + data.path = input_name; |
---|
| 1392 | + data.force = symbol_conf.force; |
---|
1199 | 1393 | |
---|
1200 | 1394 | repeat: |
---|
1201 | 1395 | session = perf_session__new(&data, false, &report.tool); |
---|
1202 | | - if (session == NULL) |
---|
1203 | | - return -1; |
---|
| 1396 | + if (IS_ERR(session)) { |
---|
| 1397 | + ret = PTR_ERR(session); |
---|
| 1398 | + goto exit; |
---|
| 1399 | + } |
---|
| 1400 | + |
---|
| 1401 | + ret = evswitch__init(&report.evswitch, session->evlist, stderr); |
---|
| 1402 | + if (ret) |
---|
| 1403 | + goto exit; |
---|
| 1404 | + |
---|
| 1405 | + if (zstd_init(&(session->zstd_data), 0) < 0) |
---|
| 1406 | + pr_warning("Decompression initialization failed. Reported data may be incomplete.\n"); |
---|
1204 | 1407 | |
---|
1205 | 1408 | if (report.queue_size) { |
---|
1206 | 1409 | ordered_events__set_alloc_size(&session->ordered_events, |
---|
.. | .. |
---|
1213 | 1416 | |
---|
1214 | 1417 | has_br_stack = perf_header__has_feat(&session->header, |
---|
1215 | 1418 | HEADER_BRANCH_STACK); |
---|
| 1419 | + if (evlist__combined_sample_type(session->evlist) & PERF_SAMPLE_STACK_USER) |
---|
| 1420 | + has_br_stack = false; |
---|
1216 | 1421 | |
---|
1217 | 1422 | setup_forced_leader(&report, session->evlist); |
---|
1218 | 1423 | |
---|
1219 | | - if (itrace_synth_opts.last_branch) |
---|
| 1424 | + if (symbol_conf.group_sort_idx && !session->evlist->nr_groups) { |
---|
| 1425 | + parse_options_usage(NULL, options, "group-sort-idx", 0); |
---|
| 1426 | + ret = -EINVAL; |
---|
| 1427 | + goto error; |
---|
| 1428 | + } |
---|
| 1429 | + |
---|
| 1430 | + if (itrace_synth_opts.last_branch || itrace_synth_opts.add_last_branch) |
---|
1220 | 1431 | has_br_stack = true; |
---|
1221 | 1432 | |
---|
1222 | 1433 | if (has_br_stack && branch_call_mode) |
---|
.. | .. |
---|
1236 | 1447 | } |
---|
1237 | 1448 | if (branch_call_mode) { |
---|
1238 | 1449 | callchain_param.key = CCKEY_ADDRESS; |
---|
1239 | | - callchain_param.branch_callstack = 1; |
---|
| 1450 | + callchain_param.branch_callstack = true; |
---|
1240 | 1451 | symbol_conf.use_callchain = true; |
---|
1241 | 1452 | callchain_register_param(&callchain_param); |
---|
1242 | 1453 | if (sort_order == NULL) |
---|
.. | .. |
---|
1287 | 1498 | goto error; |
---|
1288 | 1499 | } |
---|
1289 | 1500 | |
---|
| 1501 | + if (report.total_cycles_mode) { |
---|
| 1502 | + if (sort__mode != SORT_MODE__BRANCH) |
---|
| 1503 | + report.total_cycles_mode = false; |
---|
| 1504 | + else |
---|
| 1505 | + sort_order = NULL; |
---|
| 1506 | + } |
---|
| 1507 | + |
---|
1290 | 1508 | if (strcmp(input_name, "-") != 0) |
---|
1291 | 1509 | setup_browser(true); |
---|
1292 | 1510 | else |
---|
1293 | 1511 | use_browser = 0; |
---|
1294 | 1512 | |
---|
1295 | | - if ((last_key != K_SWITCH_INPUT_DATA) && |
---|
| 1513 | + if (sort_order && strstr(sort_order, "ipc")) { |
---|
| 1514 | + parse_options_usage(report_usage, options, "s", 1); |
---|
| 1515 | + goto error; |
---|
| 1516 | + } |
---|
| 1517 | + |
---|
| 1518 | + if (sort_order && strstr(sort_order, "symbol")) { |
---|
| 1519 | + if (sort__mode == SORT_MODE__BRANCH) { |
---|
| 1520 | + snprintf(sort_tmp, sizeof(sort_tmp), "%s,%s", |
---|
| 1521 | + sort_order, "ipc_lbr"); |
---|
| 1522 | + report.symbol_ipc = true; |
---|
| 1523 | + } else { |
---|
| 1524 | + snprintf(sort_tmp, sizeof(sort_tmp), "%s,%s", |
---|
| 1525 | + sort_order, "ipc_null"); |
---|
| 1526 | + } |
---|
| 1527 | + |
---|
| 1528 | + sort_order = sort_tmp; |
---|
| 1529 | + } |
---|
| 1530 | + |
---|
| 1531 | + if ((last_key != K_SWITCH_INPUT_DATA && last_key != K_RELOAD) && |
---|
1296 | 1532 | (setup_sorting(session->evlist) < 0)) { |
---|
1297 | 1533 | if (sort_order) |
---|
1298 | 1534 | parse_options_usage(report_usage, options, "s", 1); |
---|
.. | .. |
---|
1320 | 1556 | * so don't allocate extra space that won't be used in the stdio |
---|
1321 | 1557 | * implementation. |
---|
1322 | 1558 | */ |
---|
1323 | | - if (ui__has_annotation()) { |
---|
| 1559 | + if (ui__has_annotation() || report.symbol_ipc || |
---|
| 1560 | + report.total_cycles_mode) { |
---|
1324 | 1561 | ret = symbol__annotation_init(); |
---|
1325 | 1562 | if (ret < 0) |
---|
1326 | 1563 | goto error; |
---|
.. | .. |
---|
1339 | 1576 | symbol_conf.priv_size += sizeof(u32); |
---|
1340 | 1577 | symbol_conf.sort_by_name = true; |
---|
1341 | 1578 | } |
---|
1342 | | - annotation_config__init(); |
---|
| 1579 | + annotation_config__init(&report.annotation_opts); |
---|
1343 | 1580 | } |
---|
1344 | 1581 | |
---|
1345 | 1582 | if (symbol__init(&session->header.env) < 0) |
---|
1346 | 1583 | goto error; |
---|
1347 | 1584 | |
---|
1348 | | - report.ptime_range = perf_time__range_alloc(report.time_str, |
---|
1349 | | - &report.range_size); |
---|
1350 | | - if (!report.ptime_range) { |
---|
1351 | | - ret = -ENOMEM; |
---|
1352 | | - goto error; |
---|
1353 | | - } |
---|
1354 | | - |
---|
1355 | | - if (perf_time__parse_str(report.ptime_range, report.time_str) != 0) { |
---|
1356 | | - if (session->evlist->first_sample_time == 0 && |
---|
1357 | | - session->evlist->last_sample_time == 0) { |
---|
1358 | | - pr_err("HINT: no first/last sample time found in perf data.\n" |
---|
1359 | | - "Please use latest perf binary to execute 'perf record'\n" |
---|
1360 | | - "(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n"); |
---|
1361 | | - ret = -EINVAL; |
---|
| 1585 | + if (report.time_str) { |
---|
| 1586 | + ret = perf_time__parse_for_ranges(report.time_str, session, |
---|
| 1587 | + &report.ptime_range, |
---|
| 1588 | + &report.range_size, |
---|
| 1589 | + &report.range_num); |
---|
| 1590 | + if (ret < 0) |
---|
1362 | 1591 | goto error; |
---|
1363 | | - } |
---|
1364 | 1592 | |
---|
1365 | | - report.range_num = perf_time__percent_parse_str( |
---|
1366 | | - report.ptime_range, report.range_size, |
---|
1367 | | - report.time_str, |
---|
1368 | | - session->evlist->first_sample_time, |
---|
1369 | | - session->evlist->last_sample_time); |
---|
1370 | | - |
---|
1371 | | - if (report.range_num < 0) { |
---|
1372 | | - pr_err("Invalid time string\n"); |
---|
1373 | | - ret = -EINVAL; |
---|
1374 | | - goto error; |
---|
1375 | | - } |
---|
1376 | | - } else { |
---|
1377 | | - report.range_num = 1; |
---|
| 1593 | + itrace_synth_opts__set_time_range(&itrace_synth_opts, |
---|
| 1594 | + report.ptime_range, |
---|
| 1595 | + report.range_num); |
---|
1378 | 1596 | } |
---|
1379 | 1597 | |
---|
1380 | 1598 | if (session->tevent.pevent && |
---|
.. | .. |
---|
1389 | 1607 | sort__setup_elide(stdout); |
---|
1390 | 1608 | |
---|
1391 | 1609 | ret = __cmd_report(&report); |
---|
1392 | | - if (ret == K_SWITCH_INPUT_DATA) { |
---|
| 1610 | + if (ret == K_SWITCH_INPUT_DATA || ret == K_RELOAD) { |
---|
1393 | 1611 | perf_session__delete(session); |
---|
1394 | 1612 | last_key = K_SWITCH_INPUT_DATA; |
---|
1395 | 1613 | goto repeat; |
---|
.. | .. |
---|
1397 | 1615 | ret = 0; |
---|
1398 | 1616 | |
---|
1399 | 1617 | error: |
---|
1400 | | - zfree(&report.ptime_range); |
---|
| 1618 | + if (report.ptime_range) { |
---|
| 1619 | + itrace_synth_opts__clear_time_range(&itrace_synth_opts); |
---|
| 1620 | + zfree(&report.ptime_range); |
---|
| 1621 | + } |
---|
1401 | 1622 | |
---|
| 1623 | + if (report.block_reports) { |
---|
| 1624 | + block_info__free_report(report.block_reports, |
---|
| 1625 | + report.nr_block_reports); |
---|
| 1626 | + report.block_reports = NULL; |
---|
| 1627 | + } |
---|
| 1628 | + |
---|
| 1629 | + zstd_fini(&(session->zstd_data)); |
---|
1402 | 1630 | perf_session__delete(session); |
---|
| 1631 | +exit: |
---|
| 1632 | + free(sort_order_help); |
---|
| 1633 | + free(field_order_help); |
---|
1403 | 1634 | return ret; |
---|
1404 | 1635 | } |
---|