diff options
Diffstat (limited to 'tools/perf/builtin-script.c')
-rw-r--r-- | tools/perf/builtin-script.c | 353 |
1 files changed, 320 insertions, 33 deletions
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 4761b0d7fcb5..83cdc0a61fd6 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -85,6 +85,8 @@ enum perf_output_field { PERF_OUTPUT_INSN = 1U << 21, PERF_OUTPUT_INSNLEN = 1U << 22, PERF_OUTPUT_BRSTACKINSN = 1U << 23, + PERF_OUTPUT_BRSTACKOFF = 1U << 24, + PERF_OUTPUT_SYNTH = 1U << 25, }; struct output_option { @@ -115,6 +117,13 @@ struct output_option { {.str = "insn", .field = PERF_OUTPUT_INSN}, {.str = "insnlen", .field = PERF_OUTPUT_INSNLEN}, {.str = "brstackinsn", .field = PERF_OUTPUT_BRSTACKINSN}, + {.str = "brstackoff", .field = PERF_OUTPUT_BRSTACKOFF}, + {.str = "synth", .field = PERF_OUTPUT_SYNTH}, +}; + +enum { + OUTPUT_TYPE_SYNTH = PERF_TYPE_MAX, + OUTPUT_TYPE_MAX }; /* default set to maintain compatibility with current format */ @@ -124,7 +133,7 @@ static struct { unsigned int print_ip_opts; u64 fields; u64 invalid_fields; -} output[PERF_TYPE_MAX] = { +} output[OUTPUT_TYPE_MAX] = { [PERF_TYPE_HARDWARE] = { .user_set = false, @@ -182,12 +191,44 @@ static struct { .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, }, + + [OUTPUT_TYPE_SYNTH] = { + .user_set = false, + + .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | + PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | + PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | + PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | + PERF_OUTPUT_SYNTH, + + .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, + }, }; +static inline int output_type(unsigned int type) +{ + switch (type) { + case PERF_TYPE_SYNTH: + return OUTPUT_TYPE_SYNTH; + default: + return type; + } +} + +static inline unsigned int attr_type(unsigned int type) +{ + switch (type) { + case OUTPUT_TYPE_SYNTH: + return PERF_TYPE_SYNTH; + default: + return type; + } +} + static bool output_set_by_user(void) { int j; - for (j = 0; j < PERF_TYPE_MAX; ++j) { + for (j = 0; j < OUTPUT_TYPE_MAX; ++j) { if (output[j].user_set) return true; } @@ -208,7 +249,7 @@ static const char *output_field2str(enum perf_output_field field) return str; } -#define PRINT_FIELD(x) (output[attr->type].fields & PERF_OUTPUT_##x) +#define PRINT_FIELD(x) (output[output_type(attr->type)].fields & PERF_OUTPUT_##x) static int perf_evsel__do_check_stype(struct perf_evsel *evsel, u64 sample_type, const char *sample_msg, @@ -216,7 +257,7 @@ static int perf_evsel__do_check_stype(struct perf_evsel *evsel, bool allow_user_set) { struct perf_event_attr *attr = &evsel->attr; - int type = attr->type; + int type = output_type(attr->type); const char *evname; if (attr->sample_type & sample_type) @@ -298,10 +339,10 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, "selected.\n"); return -EINVAL; } - if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { - pr_err("Display of DSO requested but neither sample IP nor " - "sample address\nis selected. Hence, no addresses to convert " - "to DSO.\n"); + if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR) && + !PRINT_FIELD(BRSTACK) && !PRINT_FIELD(BRSTACKSYM) && !PRINT_FIELD(BRSTACKOFF)) { + pr_err("Display of DSO requested but no address to convert. Select\n" + "sample IP, sample address, brstack, brstacksym, or brstackoff.\n"); return -EINVAL; } if (PRINT_FIELD(SRCLINE) && !PRINT_FIELD(IP)) { @@ -346,7 +387,7 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, static void set_print_ip_opts(struct perf_event_attr *attr) { - unsigned int type = attr->type; + unsigned int type = output_type(attr->type); output[type].print_ip_opts = 0; if (PRINT_FIELD(IP)) @@ -374,16 +415,17 @@ static int perf_session__check_output_opt(struct perf_session *session) unsigned int j; struct perf_evsel *evsel; - for (j = 0; j < PERF_TYPE_MAX; ++j) { - evsel = perf_session__find_first_evtype(session, j); + for (j = 0; j < OUTPUT_TYPE_MAX; ++j) { + evsel = perf_session__find_first_evtype(session, attr_type(j)); /* * even if fields is set to 0 (ie., show nothing) event must * exist if user explicitly includes it on the command line */ - if (!evsel && output[j].user_set && !output[j].wildcard_set) { + if (!evsel && output[j].user_set && !output[j].wildcard_set && + j != OUTPUT_TYPE_SYNTH) { pr_err("%s events do not exist. " - "Remove corresponding -f option to proceed.\n", + "Remove corresponding -F option to proceed.\n", event_type(j)); return -1; } @@ -514,18 +556,43 @@ mispred_str(struct branch_entry *br) return br->flags.predicted ? 'P' : 'M'; } -static void print_sample_brstack(struct perf_sample *sample) +static void print_sample_brstack(struct perf_sample *sample, + struct thread *thread, + struct perf_event_attr *attr) { struct branch_stack *br = sample->branch_stack; - u64 i; + struct addr_location alf, alt; + u64 i, from, to; if (!(br && br->nr)) return; for (i = 0; i < br->nr; i++) { - printf(" 0x%"PRIx64"/0x%"PRIx64"/%c/%c/%c/%d ", - br->entries[i].from, - br->entries[i].to, + from = br->entries[i].from; + to = br->entries[i].to; + + if (PRINT_FIELD(DSO)) { + memset(&alf, 0, sizeof(alf)); + memset(&alt, 0, sizeof(alt)); + thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, from, &alf); + thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, to, &alt); + } + + printf("0x%"PRIx64, from); + if (PRINT_FIELD(DSO)) { + printf("("); + map__fprintf_dsoname(alf.map, stdout); + printf(")"); + } + + printf("/0x%"PRIx64, to); + if (PRINT_FIELD(DSO)) { + printf("("); + map__fprintf_dsoname(alt.map, stdout); + printf(")"); + } + + printf("/%c/%c/%c/%d ", mispred_str( br->entries + i), br->entries[i].flags.in_tx? 'X' : '-', br->entries[i].flags.abort? 'A' : '-', @@ -534,7 +601,8 @@ static void print_sample_brstack(struct perf_sample *sample) } static void print_sample_brstacksym(struct perf_sample *sample, - struct thread *thread) + struct thread *thread, + struct perf_event_attr *attr) { struct branch_stack *br = sample->branch_stack; struct addr_location alf, alt; @@ -559,8 +627,18 @@ static void print_sample_brstacksym(struct perf_sample *sample, alt.sym = map__find_symbol(alt.map, alt.addr); symbol__fprintf_symname_offs(alf.sym, &alf, stdout); + if (PRINT_FIELD(DSO)) { + printf("("); + map__fprintf_dsoname(alf.map, stdout); + printf(")"); + } putchar('/'); symbol__fprintf_symname_offs(alt.sym, &alt, stdout); + if (PRINT_FIELD(DSO)) { + printf("("); + map__fprintf_dsoname(alt.map, stdout); + printf(")"); + } printf("/%c/%c/%c/%d ", mispred_str( br->entries + i), br->entries[i].flags.in_tx? 'X' : '-', @@ -569,6 +647,51 @@ static void print_sample_brstacksym(struct perf_sample *sample, } } +static void print_sample_brstackoff(struct perf_sample *sample, + struct thread *thread, + struct perf_event_attr *attr) +{ + struct branch_stack *br = sample->branch_stack; + struct addr_location alf, alt; + u64 i, from, to; + + if (!(br && br->nr)) + return; + + for (i = 0; i < br->nr; i++) { + + memset(&alf, 0, sizeof(alf)); + memset(&alt, 0, sizeof(alt)); + from = br->entries[i].from; + to = br->entries[i].to; + + thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, from, &alf); + if (alf.map && !alf.map->dso->adjust_symbols) + from = map__map_ip(alf.map, from); + + thread__find_addr_map(thread, sample->cpumode, MAP__FUNCTION, to, &alt); + if (alt.map && !alt.map->dso->adjust_symbols) + to = map__map_ip(alt.map, to); + + printf("0x%"PRIx64, from); + if (PRINT_FIELD(DSO)) { + printf("("); + map__fprintf_dsoname(alf.map, stdout); + printf(")"); + } + printf("/0x%"PRIx64, to); + if (PRINT_FIELD(DSO)) { + printf("("); + map__fprintf_dsoname(alt.map, stdout); + printf(")"); + } + printf("/%c/%c/%c/%d ", + mispred_str(br->entries + i), + br->entries[i].flags.in_tx ? 'X' : '-', + br->entries[i].flags.abort ? 'A' : '-', + br->entries[i].flags.cycles); + } +} #define MAXBB 16384UL static int grab_bb(u8 *buffer, u64 start, u64 end, @@ -906,6 +1029,7 @@ static void print_sample_bts(struct perf_sample *sample, struct machine *machine) { struct perf_event_attr *attr = &evsel->attr; + unsigned int type = output_type(attr->type); bool print_srcline_last = false; if (PRINT_FIELD(CALLINDENT)) @@ -913,7 +1037,7 @@ static void print_sample_bts(struct perf_sample *sample, /* print branch_from information */ if (PRINT_FIELD(IP)) { - unsigned int print_opts = output[attr->type].print_ip_opts; + unsigned int print_opts = output[type].print_ip_opts; struct callchain_cursor *cursor = NULL; if (symbol_conf.use_callchain && sample->callchain && @@ -936,7 +1060,7 @@ static void print_sample_bts(struct perf_sample *sample, /* print branch_to information */ if (PRINT_FIELD(ADDR) || ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && - !output[attr->type].user_set)) { + !output[type].user_set)) { printf(" => "); print_sample_addr(sample, thread, attr); } @@ -1079,6 +1203,127 @@ static void print_sample_bpf_output(struct perf_sample *sample) (char *)(sample->raw_data)); } +static void print_sample_spacing(int len, int spacing) +{ + if (len > 0 && len < spacing) + printf("%*s", spacing - len, ""); +} + +static void print_sample_pt_spacing(int len) +{ + print_sample_spacing(len, 34); +} + +static void print_sample_synth_ptwrite(struct perf_sample *sample) +{ + struct perf_synth_intel_ptwrite *data = perf_sample__synth_ptr(sample); + int len; + + if (perf_sample__bad_synth_size(sample, *data)) + return; + + len = printf(" IP: %u payload: %#" PRIx64 " ", + data->ip, le64_to_cpu(data->payload)); + print_sample_pt_spacing(len); +} + +static void print_sample_synth_mwait(struct perf_sample *sample) +{ + struct perf_synth_intel_mwait *data = perf_sample__synth_ptr(sample); + int len; + + if (perf_sample__bad_synth_size(sample, *data)) + return; + + len = printf(" hints: %#x extensions: %#x ", + data->hints, data->extensions); + print_sample_pt_spacing(len); +} + +static void print_sample_synth_pwre(struct perf_sample *sample) +{ + struct perf_synth_intel_pwre *data = perf_sample__synth_ptr(sample); + int len; + + if (perf_sample__bad_synth_size(sample, *data)) + return; + + len = printf(" hw: %u cstate: %u sub-cstate: %u ", + data->hw, data->cstate, data->subcstate); + print_sample_pt_spacing(len); +} + +static void print_sample_synth_exstop(struct perf_sample *sample) +{ + struct perf_synth_intel_exstop *data = perf_sample__synth_ptr(sample); + int len; + + if (perf_sample__bad_synth_size(sample, *data)) + return; + + len = printf(" IP: %u ", data->ip); + print_sample_pt_spacing(len); +} + +static void print_sample_synth_pwrx(struct perf_sample *sample) +{ + struct perf_synth_intel_pwrx *data = perf_sample__synth_ptr(sample); + int len; + + if (perf_sample__bad_synth_size(sample, *data)) + return; + + len = printf(" deepest cstate: %u last cstate: %u wake reason: %#x ", + data->deepest_cstate, data->last_cstate, + data->wake_reason); + print_sample_pt_spacing(len); +} + +static void print_sample_synth_cbr(struct perf_sample *sample) +{ + struct perf_synth_intel_cbr *data = perf_sample__synth_ptr(sample); + unsigned int percent, freq; + int len; + + if (perf_sample__bad_synth_size(sample, *data)) + return; + + freq = (le32_to_cpu(data->freq) + 500) / 1000; + len = printf(" cbr: %2u freq: %4u MHz ", data->cbr, freq); + if (data->max_nonturbo) { + percent = (5 + (1000 * data->cbr) / data->max_nonturbo) / 10; + len += printf("(%3u%%) ", percent); + } + print_sample_pt_spacing(len); +} + +static void print_sample_synth(struct perf_sample *sample, + struct perf_evsel *evsel) +{ + switch (evsel->attr.config) { + case PERF_SYNTH_INTEL_PTWRITE: + print_sample_synth_ptwrite(sample); + break; + case PERF_SYNTH_INTEL_MWAIT: + print_sample_synth_mwait(sample); + break; + case PERF_SYNTH_INTEL_PWRE: + print_sample_synth_pwre(sample); + break; + case PERF_SYNTH_INTEL_EXSTOP: + print_sample_synth_exstop(sample); + break; + case PERF_SYNTH_INTEL_PWRX: + print_sample_synth_pwrx(sample); + break; + case PERF_SYNTH_INTEL_CBR: + print_sample_synth_cbr(sample); + break; + default: + break; + } +} + struct perf_script { struct perf_tool tool; struct perf_session *session; @@ -1132,8 +1377,9 @@ static void process_event(struct perf_script *script, { struct thread *thread = al->thread; struct perf_event_attr *attr = &evsel->attr; + unsigned int type = output_type(attr->type); - if (output[attr->type].fields == 0) + if (output[type].fields == 0) return; print_sample_start(sample, thread, evsel); @@ -1162,6 +1408,10 @@ static void process_event(struct perf_script *script, if (PRINT_FIELD(TRACE)) event_format__print(evsel->tp_format, sample->cpu, sample->raw_data, sample->raw_size); + + if (attr->type == PERF_TYPE_SYNTH && PRINT_FIELD(SYNTH)) + print_sample_synth(sample, evsel); + if (PRINT_FIELD(ADDR)) print_sample_addr(sample, thread, attr); @@ -1180,16 +1430,18 @@ static void process_event(struct perf_script *script, cursor = &callchain_cursor; putchar(cursor ? '\n' : ' '); - sample__fprintf_sym(sample, al, 0, output[attr->type].print_ip_opts, cursor, stdout); + sample__fprintf_sym(sample, al, 0, output[type].print_ip_opts, cursor, stdout); } if (PRINT_FIELD(IREGS)) print_sample_iregs(sample, attr); if (PRINT_FIELD(BRSTACK)) - print_sample_brstack(sample); + print_sample_brstack(sample, thread, attr); else if (PRINT_FIELD(BRSTACKSYM)) - print_sample_brstacksym(sample, thread); + print_sample_brstacksym(sample, thread, attr); + else if (PRINT_FIELD(BRSTACKOFF)) + print_sample_brstackoff(sample, thread, attr); if (perf_evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT)) print_sample_bpf_output(sample); @@ -1325,7 +1577,8 @@ static int process_attr(struct perf_tool *tool, union perf_event *event, evlist = *pevlist; evsel = perf_evlist__last(*pevlist); - if (evsel->attr.type >= PERF_TYPE_MAX) + if (evsel->attr.type >= PERF_TYPE_MAX && + evsel->attr.type != PERF_TYPE_SYNTH) return 0; evlist__for_each_entry(evlist, pos) { @@ -1727,6 +1980,7 @@ static int parse_output_fields(const struct option *opt __maybe_unused, int rc = 0; char *str = strdup(arg); int type = -1; + enum { DEFAULT, SET, ADD, REMOVE } change = DEFAULT; if (!str) return -ENOMEM; @@ -1749,6 +2003,8 @@ static int parse_output_fields(const struct option *opt __maybe_unused, type = PERF_TYPE_RAW; else if (!strcmp(str, "break")) type = PERF_TYPE_BREAKPOINT; + else if (!strcmp(str, "synth")) + type = OUTPUT_TYPE_SYNTH; else { fprintf(stderr, "Invalid event type in field string.\n"); rc = -EINVAL; @@ -1772,23 +2028,44 @@ static int parse_output_fields(const struct option *opt __maybe_unused, goto out; } + /* Don't override defaults for +- */ + if (strchr(str, '+') || strchr(str, '-')) + goto parse; + if (output_set_by_user()) pr_warning("Overriding previous field request for all events.\n"); - for (j = 0; j < PERF_TYPE_MAX; ++j) { + for (j = 0; j < OUTPUT_TYPE_MAX; ++j) { output[j].fields = 0; output[j].user_set = true; output[j].wildcard_set = true; } } +parse: for (tok = strtok_r(tok, ",", &strtok_saveptr); tok; tok = strtok_r(NULL, ",", &strtok_saveptr)) { + if (*tok == '+') { + if (change == SET) + goto out_badmix; + change = ADD; + tok++; + } else if (*tok == '-') { + if (change == SET) + goto out_badmix; + change = REMOVE; + tok++; + } else { + if (change != SET && change != DEFAULT) + goto out_badmix; + change = SET; + } + for (i = 0; i < imax; ++i) { if (strcmp(tok, all_output_options[i].str) == 0) break; } if (i == imax && strcmp(tok, "flags") == 0) { - print_flags = true; + print_flags = change == REMOVE ? false : true; continue; } if (i == imax) { @@ -1801,12 +2078,16 @@ static int parse_output_fields(const struct option *opt __maybe_unused, /* add user option to all events types for * which it is valid */ - for (j = 0; j < PERF_TYPE_MAX; ++j) { + for (j = 0; j < OUTPUT_TYPE_MAX; ++j) { if (output[j].invalid_fields & all_output_options[i].field) { pr_warning("\'%s\' not valid for %s events. Ignoring.\n", all_output_options[i].str, event_type(j)); - } else - output[j].fields |= all_output_options[i].field; + } else { + if (change == REMOVE) + output[j].fields &= ~all_output_options[i].field; + else + output[j].fields |= all_output_options[i].field; + } } } else { if (output[type].invalid_fields & all_output_options[i].field) { @@ -1826,7 +2107,11 @@ static int parse_output_fields(const struct option *opt __maybe_unused, "Events will not be displayed.\n", event_type(type)); } } + goto out; +out_badmix: + fprintf(stderr, "Cannot mix +-field with overridden fields\n"); + rc = -EINVAL; out: free(str); return rc; @@ -2444,10 +2729,11 @@ int cmd_script(int argc, const char **argv) symbol__config_symfs), OPT_CALLBACK('F', "fields", NULL, "str", "comma separated output fields prepend with 'type:'. " - "Valid types: hw,sw,trace,raw. " + "+field to add and -field to remove." + "Valid types: hw,sw,trace,raw,synth. " "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso," "addr,symoff,period,iregs,brstack,brstacksym,flags," - "bpf-output,callindent,insn,insnlen,brstackinsn", + "bpf-output,callindent,insn,insnlen,brstackinsn,synth", parse_output_fields), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), @@ -2706,6 +2992,7 @@ int cmd_script(int argc, const char **argv) err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); if (err < 0) goto out_delete; + itrace_synth_opts.cpu_bitmap = cpu_bitmap; } if (!no_callchain) |