| .. | .. |
|---|
| 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 | } |
|---|