diff options
Diffstat (limited to 'tools/perf/builtin-script.c')
-rw-r--r-- | tools/perf/builtin-script.c | 1376 |
1 files changed, 880 insertions, 496 deletions
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index e2406b291c1c..7ca238277d83 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -15,6 +15,7 @@ #include "util/symbol.h" #include "util/thread.h" #include "util/trace-event.h" +#include "util/env.h" #include "util/evlist.h" #include "util/evsel.h" #include "util/evsel_fprintf.h" @@ -30,6 +31,7 @@ #include "util/thread-stack.h" #include "util/time-utils.h" #include "util/path.h" +#include "util/event.h" #include "ui/ui.h" #include "print_binary.h" #include "archinsn.h" @@ -54,6 +56,7 @@ #include <subcmd/pager.h> #include <perf/evlist.h> #include <linux/err.h> +#include "util/dlfilter.h" #include "util/record.h" #include "util/util.h" #include "perf.h" @@ -63,7 +66,9 @@ static char const *script_name; static char const *generate_script_lang; static bool reltime; +static bool deltatime; static u64 initial_time; +static u64 previous_time; static bool debug_mode; static u64 last_timestamp; static u64 nr_unordered; @@ -76,42 +81,77 @@ static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); static struct perf_stat_config stat_config; static int max_blocks; static bool native_arch; +static struct dlfilter *dlfilter; +static int dlargc; +static char **dlargv; unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH; enum perf_output_field { - PERF_OUTPUT_COMM = 1U << 0, - PERF_OUTPUT_TID = 1U << 1, - PERF_OUTPUT_PID = 1U << 2, - PERF_OUTPUT_TIME = 1U << 3, - PERF_OUTPUT_CPU = 1U << 4, - PERF_OUTPUT_EVNAME = 1U << 5, - PERF_OUTPUT_TRACE = 1U << 6, - PERF_OUTPUT_IP = 1U << 7, - PERF_OUTPUT_SYM = 1U << 8, - PERF_OUTPUT_DSO = 1U << 9, - PERF_OUTPUT_ADDR = 1U << 10, - PERF_OUTPUT_SYMOFFSET = 1U << 11, - PERF_OUTPUT_SRCLINE = 1U << 12, - PERF_OUTPUT_PERIOD = 1U << 13, - PERF_OUTPUT_IREGS = 1U << 14, - PERF_OUTPUT_BRSTACK = 1U << 15, - PERF_OUTPUT_BRSTACKSYM = 1U << 16, - PERF_OUTPUT_DATA_SRC = 1U << 17, - PERF_OUTPUT_WEIGHT = 1U << 18, - PERF_OUTPUT_BPF_OUTPUT = 1U << 19, - PERF_OUTPUT_CALLINDENT = 1U << 20, - 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, - PERF_OUTPUT_PHYS_ADDR = 1U << 26, - PERF_OUTPUT_UREGS = 1U << 27, - PERF_OUTPUT_METRIC = 1U << 28, - PERF_OUTPUT_MISC = 1U << 29, - PERF_OUTPUT_SRCCODE = 1U << 30, - PERF_OUTPUT_IPC = 1U << 31, + PERF_OUTPUT_COMM = 1ULL << 0, + PERF_OUTPUT_TID = 1ULL << 1, + PERF_OUTPUT_PID = 1ULL << 2, + PERF_OUTPUT_TIME = 1ULL << 3, + PERF_OUTPUT_CPU = 1ULL << 4, + PERF_OUTPUT_EVNAME = 1ULL << 5, + PERF_OUTPUT_TRACE = 1ULL << 6, + PERF_OUTPUT_IP = 1ULL << 7, + PERF_OUTPUT_SYM = 1ULL << 8, + PERF_OUTPUT_DSO = 1ULL << 9, + PERF_OUTPUT_ADDR = 1ULL << 10, + PERF_OUTPUT_SYMOFFSET = 1ULL << 11, + PERF_OUTPUT_SRCLINE = 1ULL << 12, + PERF_OUTPUT_PERIOD = 1ULL << 13, + PERF_OUTPUT_IREGS = 1ULL << 14, + PERF_OUTPUT_BRSTACK = 1ULL << 15, + PERF_OUTPUT_BRSTACKSYM = 1ULL << 16, + PERF_OUTPUT_DATA_SRC = 1ULL << 17, + PERF_OUTPUT_WEIGHT = 1ULL << 18, + PERF_OUTPUT_BPF_OUTPUT = 1ULL << 19, + PERF_OUTPUT_CALLINDENT = 1ULL << 20, + PERF_OUTPUT_INSN = 1ULL << 21, + PERF_OUTPUT_INSNLEN = 1ULL << 22, + PERF_OUTPUT_BRSTACKINSN = 1ULL << 23, + PERF_OUTPUT_BRSTACKOFF = 1ULL << 24, + PERF_OUTPUT_SYNTH = 1ULL << 25, + PERF_OUTPUT_PHYS_ADDR = 1ULL << 26, + PERF_OUTPUT_UREGS = 1ULL << 27, + PERF_OUTPUT_METRIC = 1ULL << 28, + PERF_OUTPUT_MISC = 1ULL << 29, + PERF_OUTPUT_SRCCODE = 1ULL << 30, + PERF_OUTPUT_IPC = 1ULL << 31, + PERF_OUTPUT_TOD = 1ULL << 32, + PERF_OUTPUT_DATA_PAGE_SIZE = 1ULL << 33, + PERF_OUTPUT_CODE_PAGE_SIZE = 1ULL << 34, + PERF_OUTPUT_INS_LAT = 1ULL << 35, + PERF_OUTPUT_BRSTACKINSNLEN = 1ULL << 36, + PERF_OUTPUT_MACHINE_PID = 1ULL << 37, + PERF_OUTPUT_VCPU = 1ULL << 38, +}; + +struct perf_script { + struct perf_tool tool; + struct perf_session *session; + bool show_task_events; + bool show_mmap_events; + bool show_switch_events; + bool show_namespace_events; + bool show_lost_events; + bool show_round_events; + bool show_bpf_events; + bool show_cgroup_events; + bool show_text_poke_events; + bool allocated; + bool per_event_dump; + bool stitch_lbr; + struct evswitch evswitch; + struct perf_cpu_map *cpus; + struct perf_thread_map *threads; + int name_width; + const char *time_str; + struct perf_time_interval *ptime_range; + int range_size; + int range_num; }; struct output_option { @@ -150,10 +190,18 @@ struct output_option { {.str = "misc", .field = PERF_OUTPUT_MISC}, {.str = "srccode", .field = PERF_OUTPUT_SRCCODE}, {.str = "ipc", .field = PERF_OUTPUT_IPC}, + {.str = "tod", .field = PERF_OUTPUT_TOD}, + {.str = "data_page_size", .field = PERF_OUTPUT_DATA_PAGE_SIZE}, + {.str = "code_page_size", .field = PERF_OUTPUT_CODE_PAGE_SIZE}, + {.str = "ins_lat", .field = PERF_OUTPUT_INS_LAT}, + {.str = "brstackinsnlen", .field = PERF_OUTPUT_BRSTACKINSNLEN}, + {.str = "machine_pid", .field = PERF_OUTPUT_MACHINE_PID}, + {.str = "vcpu", .field = PERF_OUTPUT_VCPU}, }; enum { OUTPUT_TYPE_SYNTH = PERF_TYPE_MAX, + OUTPUT_TYPE_OTHER, OUTPUT_TYPE_MAX }; @@ -165,6 +213,7 @@ static struct { u64 fields; u64 invalid_fields; u64 user_set_fields; + u64 user_unset_fields; } output[OUTPUT_TYPE_MAX] = { [PERF_TYPE_HARDWARE] = { @@ -221,7 +270,9 @@ static struct { PERF_OUTPUT_SYM | PERF_OUTPUT_SYMOFFSET | PERF_OUTPUT_DSO | PERF_OUTPUT_PERIOD | PERF_OUTPUT_ADDR | PERF_OUTPUT_DATA_SRC | - PERF_OUTPUT_WEIGHT | PERF_OUTPUT_PHYS_ADDR, + PERF_OUTPUT_WEIGHT | PERF_OUTPUT_PHYS_ADDR | + PERF_OUTPUT_DATA_PAGE_SIZE | PERF_OUTPUT_CODE_PAGE_SIZE | + PERF_OUTPUT_INS_LAT, .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, }, @@ -249,6 +300,18 @@ static struct { .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, }, + + [OUTPUT_TYPE_OTHER] = { + .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_SYMOFFSET | + PERF_OUTPUT_DSO | PERF_OUTPUT_PERIOD, + + .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, + }, }; struct evsel_script { @@ -265,13 +328,12 @@ static inline struct evsel_script *evsel_script(struct evsel *evsel) return (struct evsel_script *)evsel->priv; } -static struct evsel_script *perf_evsel_script__new(struct evsel *evsel, - struct perf_data *data) +static struct evsel_script *evsel_script__new(struct evsel *evsel, struct perf_data *data) { struct evsel_script *es = zalloc(sizeof(*es)); if (es != NULL) { - if (asprintf(&es->filename, "%s.%s.dump", data->file.path, perf_evsel__name(evsel)) < 0) + if (asprintf(&es->filename, "%s.%s.dump", data->file.path, evsel__name(evsel)) < 0) goto out_free; es->fp = fopen(es->filename, "w"); if (es->fp == NULL) @@ -286,7 +348,7 @@ out_free: return NULL; } -static void perf_evsel_script__delete(struct evsel_script *es) +static void evsel_script__delete(struct evsel_script *es) { zfree(&es->filename); fclose(es->fp); @@ -294,7 +356,7 @@ static void perf_evsel_script__delete(struct evsel_script *es) free(es); } -static int perf_evsel_script__fprintf(struct evsel_script *es, FILE *fp) +static int evsel_script__fprintf(struct evsel_script *es, FILE *fp) { struct stat st; @@ -309,18 +371,11 @@ static inline int output_type(unsigned int type) case PERF_TYPE_SYNTH: return OUTPUT_TYPE_SYNTH; default: - return type; + if (type < PERF_TYPE_MAX) + return type; } -} -static inline unsigned int attr_type(unsigned int type) -{ - switch (type) { - case OUTPUT_TYPE_SYNTH: - return PERF_TYPE_SYNTH; - default: - return type; - } + return OUTPUT_TYPE_OTHER; } static bool output_set_by_user(void) @@ -349,10 +404,8 @@ static const char *output_field2str(enum perf_output_field field) #define PRINT_FIELD(x) (output[output_type(attr->type)].fields & PERF_OUTPUT_##x) -static int perf_evsel__do_check_stype(struct evsel *evsel, - u64 sample_type, const char *sample_msg, - enum perf_output_field field, - bool allow_user_set) +static int evsel__do_check_stype(struct evsel *evsel, u64 sample_type, const char *sample_msg, + enum perf_output_field field, bool allow_user_set) { struct perf_event_attr *attr = &evsel->core.attr; int type = output_type(attr->type); @@ -364,7 +417,7 @@ static int perf_evsel__do_check_stype(struct evsel *evsel, if (output[type].user_set_fields & field) { if (allow_user_set) return 0; - evname = perf_evsel__name(evsel); + evname = evsel__name(evsel); pr_err("Samples for '%s' event do not have %s attribute set. " "Cannot print '%s' field.\n", evname, sample_msg, output_field2str(field)); @@ -373,7 +426,7 @@ static int perf_evsel__do_check_stype(struct evsel *evsel, /* user did not ask for it explicitly so remove from the default list */ output[type].fields &= ~field; - evname = perf_evsel__name(evsel); + evname = evsel__name(evsel); pr_debug("Samples for '%s' event do not have %s attribute set. " "Skipping '%s' field.\n", evname, sample_msg, output_field2str(field)); @@ -381,20 +434,20 @@ static int perf_evsel__do_check_stype(struct evsel *evsel, return 0; } -static int perf_evsel__check_stype(struct evsel *evsel, - u64 sample_type, const char *sample_msg, - enum perf_output_field field) +static int evsel__check_stype(struct evsel *evsel, u64 sample_type, const char *sample_msg, + enum perf_output_field field) { - return perf_evsel__do_check_stype(evsel, sample_type, sample_msg, field, - false); + return evsel__do_check_stype(evsel, sample_type, sample_msg, field, false); } -static int perf_evsel__check_attr(struct evsel *evsel, - struct perf_session *session) +static int evsel__check_attr(struct evsel *evsel, struct perf_session *session) { struct perf_event_attr *attr = &evsel->core.attr; bool allow_user_set; + if (evsel__is_dummy_event(evsel)) + return 0; + if (perf_header__has_feat(&session->header, HEADER_STAT)) return 0; @@ -402,32 +455,28 @@ static int perf_evsel__check_attr(struct evsel *evsel, HEADER_AUXTRACE); if (PRINT_FIELD(TRACE) && - !perf_session__has_traces(session, "record -R")) + !perf_session__has_traces(session, "record -R")) return -EINVAL; if (PRINT_FIELD(IP)) { - if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP", - PERF_OUTPUT_IP)) + if (evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP", PERF_OUTPUT_IP)) return -EINVAL; } if (PRINT_FIELD(ADDR) && - perf_evsel__do_check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR", - PERF_OUTPUT_ADDR, allow_user_set)) + evsel__do_check_stype(evsel, PERF_SAMPLE_ADDR, "ADDR", PERF_OUTPUT_ADDR, allow_user_set)) return -EINVAL; if (PRINT_FIELD(DATA_SRC) && - perf_evsel__check_stype(evsel, PERF_SAMPLE_DATA_SRC, "DATA_SRC", - PERF_OUTPUT_DATA_SRC)) + evsel__do_check_stype(evsel, PERF_SAMPLE_DATA_SRC, "DATA_SRC", PERF_OUTPUT_DATA_SRC, allow_user_set)) return -EINVAL; if (PRINT_FIELD(WEIGHT) && - perf_evsel__check_stype(evsel, PERF_SAMPLE_WEIGHT, "WEIGHT", - PERF_OUTPUT_WEIGHT)) + evsel__do_check_stype(evsel, PERF_SAMPLE_WEIGHT_TYPE, "WEIGHT", PERF_OUTPUT_WEIGHT, allow_user_set)) return -EINVAL; if (PRINT_FIELD(SYM) && - !(evsel->core.attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) { + !(evsel->core.attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) { pr_err("Display of symbols requested but neither sample IP nor " "sample address\navailable. Hence, no addresses to convert " "to symbols.\n"); @@ -439,7 +488,7 @@ static int perf_evsel__check_attr(struct evsel *evsel, return -EINVAL; } if (PRINT_FIELD(DSO) && - !(evsel->core.attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) { + !(evsel->core.attr.sample_type & (PERF_SAMPLE_IP|PERF_SAMPLE_ADDR))) { pr_err("Display of DSO requested but no address to convert.\n"); return -EINVAL; } @@ -448,41 +497,46 @@ static int perf_evsel__check_attr(struct evsel *evsel, "selected. Hence, no address to lookup the source line number.\n"); return -EINVAL; } - if (PRINT_FIELD(BRSTACKINSN) && !allow_user_set && - !(perf_evlist__combined_branch_type(session->evlist) & - PERF_SAMPLE_BRANCH_ANY)) { + if ((PRINT_FIELD(BRSTACKINSN) || PRINT_FIELD(BRSTACKINSNLEN)) && !allow_user_set && + !(evlist__combined_branch_type(session->evlist) & PERF_SAMPLE_BRANCH_ANY)) { pr_err("Display of branch stack assembler requested, but non all-branch filter set\n" "Hint: run 'perf record -b ...'\n"); return -EINVAL; } if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) && - perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID", - PERF_OUTPUT_TID|PERF_OUTPUT_PID)) + evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID", PERF_OUTPUT_TID|PERF_OUTPUT_PID)) return -EINVAL; if (PRINT_FIELD(TIME) && - perf_evsel__check_stype(evsel, PERF_SAMPLE_TIME, "TIME", - PERF_OUTPUT_TIME)) + evsel__check_stype(evsel, PERF_SAMPLE_TIME, "TIME", PERF_OUTPUT_TIME)) return -EINVAL; if (PRINT_FIELD(CPU) && - perf_evsel__do_check_stype(evsel, PERF_SAMPLE_CPU, "CPU", - PERF_OUTPUT_CPU, allow_user_set)) + evsel__do_check_stype(evsel, PERF_SAMPLE_CPU, "CPU", PERF_OUTPUT_CPU, allow_user_set)) return -EINVAL; if (PRINT_FIELD(IREGS) && - perf_evsel__check_stype(evsel, PERF_SAMPLE_REGS_INTR, "IREGS", - PERF_OUTPUT_IREGS)) + evsel__do_check_stype(evsel, PERF_SAMPLE_REGS_INTR, "IREGS", PERF_OUTPUT_IREGS, allow_user_set)) return -EINVAL; if (PRINT_FIELD(UREGS) && - perf_evsel__check_stype(evsel, PERF_SAMPLE_REGS_USER, "UREGS", - PERF_OUTPUT_UREGS)) + evsel__check_stype(evsel, PERF_SAMPLE_REGS_USER, "UREGS", PERF_OUTPUT_UREGS)) return -EINVAL; if (PRINT_FIELD(PHYS_ADDR) && - perf_evsel__check_stype(evsel, PERF_SAMPLE_PHYS_ADDR, "PHYS_ADDR", - PERF_OUTPUT_PHYS_ADDR)) + evsel__do_check_stype(evsel, PERF_SAMPLE_PHYS_ADDR, "PHYS_ADDR", PERF_OUTPUT_PHYS_ADDR, allow_user_set)) + return -EINVAL; + + if (PRINT_FIELD(DATA_PAGE_SIZE) && + evsel__check_stype(evsel, PERF_SAMPLE_DATA_PAGE_SIZE, "DATA_PAGE_SIZE", PERF_OUTPUT_DATA_PAGE_SIZE)) + return -EINVAL; + + if (PRINT_FIELD(CODE_PAGE_SIZE) && + evsel__check_stype(evsel, PERF_SAMPLE_CODE_PAGE_SIZE, "CODE_PAGE_SIZE", PERF_OUTPUT_CODE_PAGE_SIZE)) + return -EINVAL; + + if (PRINT_FIELD(INS_LAT) && + evsel__check_stype(evsel, PERF_SAMPLE_WEIGHT_STRUCT, "WEIGHT_STRUCT", PERF_OUTPUT_INS_LAT)) return -EINVAL; return 0; @@ -509,17 +563,32 @@ static void set_print_ip_opts(struct perf_event_attr *attr) output[type].print_ip_opts |= EVSEL__PRINT_SRCLINE; } +static struct evsel *find_first_output_type(struct evlist *evlist, + unsigned int type) +{ + struct evsel *evsel; + + evlist__for_each_entry(evlist, evsel) { + if (evsel__is_dummy_event(evsel)) + continue; + if (output_type(evsel->core.attr.type) == (int)type) + return evsel; + } + return NULL; +} + /* * verify all user requested events exist and the samples * have the expected data */ static int perf_session__check_output_opt(struct perf_session *session) { + bool tod = false; unsigned int j; struct evsel *evsel; for (j = 0; j < OUTPUT_TYPE_MAX; ++j) { - evsel = perf_session__find_first_evtype(session, attr_type(j)); + evsel = find_first_output_type(session->evlist, j); /* * even if fields is set to 0 (ie., show nothing) event must @@ -534,13 +603,14 @@ static int perf_session__check_output_opt(struct perf_session *session) } if (evsel && output[j].fields && - perf_evsel__check_attr(evsel, session)) + evsel__check_attr(evsel, session)) return -1; if (evsel == NULL) continue; set_print_ip_opts(&evsel->core.attr); + tod |= output[j].fields & PERF_OUTPUT_TOD; } if (!no_callchain) { @@ -581,13 +651,17 @@ static int perf_session__check_output_opt(struct perf_session *session) } } + if (tod && !session->header.env.clock.enabled) { + pr_err("Can't provide 'tod' time, missing clock data. " + "Please record with -k/--clockid option.\n"); + return -1; + } out: return 0; } -static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, - FILE *fp -) +static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, const char *arch, + FILE *fp) { unsigned i = 0, r; int printed = 0; @@ -599,29 +673,78 @@ static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, for_each_set_bit(r, (unsigned long *) &mask, sizeof(mask) * 8) { u64 val = regs->regs[i++]; - printed += fprintf(fp, "%5s:0x%"PRIx64" ", perf_reg_name(r), val); + printed += fprintf(fp, "%5s:0x%"PRIx64" ", perf_reg_name(r, arch), val); } - fprintf(fp, "\n"); - return printed; } +#define DEFAULT_TOD_FMT "%F %H:%M:%S" + +static char* +tod_scnprintf(struct perf_script *script, char *buf, int buflen, + u64 timestamp) +{ + u64 tod_ns, clockid_ns; + struct perf_env *env; + unsigned long nsec; + struct tm ltime; + char date[64]; + time_t sec; + + buf[0] = '\0'; + if (buflen < 64 || !script) + return buf; + + env = &script->session->header.env; + if (!env->clock.enabled) { + scnprintf(buf, buflen, "disabled"); + return buf; + } + + clockid_ns = env->clock.clockid_ns; + tod_ns = env->clock.tod_ns; + + if (timestamp > clockid_ns) + tod_ns += timestamp - clockid_ns; + else + tod_ns -= clockid_ns - timestamp; + + sec = (time_t) (tod_ns / NSEC_PER_SEC); + nsec = tod_ns - sec * NSEC_PER_SEC; + + if (localtime_r(&sec, <ime) == NULL) { + scnprintf(buf, buflen, "failed"); + } else { + strftime(date, sizeof(date), DEFAULT_TOD_FMT, <ime); + + if (symbol_conf.nanosecs) { + snprintf(buf, buflen, "%s.%09lu", date, nsec); + } else { + snprintf(buf, buflen, "%s.%06lu", + date, nsec / NSEC_PER_USEC); + } + } + + return buf; +} + static int perf_sample__fprintf_iregs(struct perf_sample *sample, - struct perf_event_attr *attr, FILE *fp) + struct perf_event_attr *attr, const char *arch, FILE *fp) { return perf_sample__fprintf_regs(&sample->intr_regs, - attr->sample_regs_intr, fp); + attr->sample_regs_intr, arch, fp); } static int perf_sample__fprintf_uregs(struct perf_sample *sample, - struct perf_event_attr *attr, FILE *fp) + struct perf_event_attr *attr, const char *arch, FILE *fp) { return perf_sample__fprintf_regs(&sample->user_regs, - attr->sample_regs_user, fp); + attr->sample_regs_user, arch, fp); } -static int perf_sample__fprintf_start(struct perf_sample *sample, +static int perf_sample__fprintf_start(struct perf_script *script, + struct perf_sample *sample, struct thread *thread, struct evsel *evsel, u32 type, FILE *fp) @@ -630,14 +753,24 @@ static int perf_sample__fprintf_start(struct perf_sample *sample, unsigned long secs; unsigned long long nsecs; int printed = 0; + char tstr[128]; + + if (PRINT_FIELD(MACHINE_PID) && sample->machine_pid) + printed += fprintf(fp, "VM:%5d ", sample->machine_pid); + + /* Print VCPU only for guest events i.e. with machine_pid */ + if (PRINT_FIELD(VCPU) && sample->machine_pid) + printed += fprintf(fp, "VCPU:%03d ", sample->vcpu); if (PRINT_FIELD(COMM)) { + const char *comm = thread ? thread__comm_str(thread) : ":-1"; + if (latency_format) - printed += fprintf(fp, "%8.8s ", thread__comm_str(thread)); + printed += fprintf(fp, "%8.8s ", comm); else if (PRINT_FIELD(IP) && evsel__has_callchain(evsel) && symbol_conf.use_callchain) - printed += fprintf(fp, "%s ", thread__comm_str(thread)); + printed += fprintf(fp, "%s ", comm); else - printed += fprintf(fp, "%16s ", thread__comm_str(thread)); + printed += fprintf(fp, "%16s ", comm); } if (PRINT_FIELD(PID) && PRINT_FIELD(TID)) @@ -698,12 +831,24 @@ static int perf_sample__fprintf_start(struct perf_sample *sample, printed += ret; } + if (PRINT_FIELD(TOD)) { + tod_scnprintf(script, tstr, sizeof(tstr), sample->time); + printed += fprintf(fp, "%s ", tstr); + } + if (PRINT_FIELD(TIME)) { u64 t = sample->time; if (reltime) { if (!initial_time) initial_time = sample->time; t = sample->time - initial_time; + } else if (deltatime) { + if (previous_time) + t = sample->time - previous_time; + else { + t = 0; + } + previous_time = sample->time; } nsecs = t; secs = nsecs / NSEC_PER_SEC; @@ -730,11 +875,22 @@ mispred_str(struct branch_entry *br) return br->flags.predicted ? 'P' : 'M'; } +static int print_bstack_flags(FILE *fp, struct branch_entry *br) +{ + return fprintf(fp, "/%c/%c/%c/%d/%s ", + mispred_str(br), + br->flags.in_tx ? 'X' : '-', + br->flags.abort ? 'A' : '-', + br->flags.cycles, + get_branch_type(br)); +} + static int perf_sample__fprintf_brstack(struct perf_sample *sample, struct thread *thread, struct perf_event_attr *attr, FILE *fp) { struct branch_stack *br = sample->branch_stack; + struct branch_entry *entries = perf_sample__branch_entries(sample); struct addr_location alf, alt; u64 i, from, to; int printed = 0; @@ -743,8 +899,8 @@ static int perf_sample__fprintf_brstack(struct perf_sample *sample, return 0; for (i = 0; i < br->nr; i++) { - from = br->entries[i].from; - to = br->entries[i].to; + from = entries[i].from; + to = entries[i].to; if (PRINT_FIELD(DSO)) { memset(&alf, 0, sizeof(alf)); @@ -767,11 +923,7 @@ static int perf_sample__fprintf_brstack(struct perf_sample *sample, printed += fprintf(fp, ")"); } - printed += fprintf(fp, "/%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); + printed += print_bstack_flags(fp, entries + i); } return printed; @@ -782,6 +934,7 @@ static int perf_sample__fprintf_brstacksym(struct perf_sample *sample, struct perf_event_attr *attr, FILE *fp) { struct branch_stack *br = sample->branch_stack; + struct branch_entry *entries = perf_sample__branch_entries(sample); struct addr_location alf, alt; u64 i, from, to; int printed = 0; @@ -793,8 +946,8 @@ static int perf_sample__fprintf_brstacksym(struct perf_sample *sample, memset(&alf, 0, sizeof(alf)); memset(&alt, 0, sizeof(alt)); - from = br->entries[i].from; - to = br->entries[i].to; + from = entries[i].from; + to = entries[i].to; thread__find_symbol_fb(thread, sample->cpumode, from, &alf); thread__find_symbol_fb(thread, sample->cpumode, to, &alt); @@ -812,11 +965,7 @@ static int perf_sample__fprintf_brstacksym(struct perf_sample *sample, printed += map__fprintf_dsoname(alt.map, fp); printed += fprintf(fp, ")"); } - printed += fprintf(fp, "/%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); + printed += print_bstack_flags(fp, entries + i); } return printed; @@ -827,6 +976,7 @@ static int perf_sample__fprintf_brstackoff(struct perf_sample *sample, struct perf_event_attr *attr, FILE *fp) { struct branch_stack *br = sample->branch_stack; + struct branch_entry *entries = perf_sample__branch_entries(sample); struct addr_location alf, alt; u64 i, from, to; int printed = 0; @@ -838,8 +988,8 @@ static int perf_sample__fprintf_brstackoff(struct perf_sample *sample, memset(&alf, 0, sizeof(alf)); memset(&alt, 0, sizeof(alt)); - from = br->entries[i].from; - to = br->entries[i].to; + from = entries[i].from; + to = entries[i].to; if (thread__find_map_fb(thread, sample->cpumode, from, &alf) && !alf.map->dso->adjust_symbols) @@ -861,11 +1011,7 @@ static int perf_sample__fprintf_brstackoff(struct perf_sample *sample, printed += map__fprintf_dsoname(alt.map, fp); printed += fprintf(fp, ")"); } - printed += fprintf(fp, "/%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); + printed += print_bstack_flags(fp, entries + i); } return printed; @@ -992,10 +1138,17 @@ static int print_srccode(struct thread *thread, u8 cpumode, uint64_t addr) static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, struct perf_insn *x, u8 *inbuf, int len, - int insn, FILE *fp, int *total_cycles) + int insn, FILE *fp, int *total_cycles, + struct perf_event_attr *attr) { - int printed = fprintf(fp, "\t%016" PRIx64 "\t%-30s\t#%s%s%s%s", ip, - dump_insn(x, ip, inbuf, len, NULL), + int ilen = 0; + int printed = fprintf(fp, "\t%016" PRIx64 "\t%-30s\t", ip, + dump_insn(x, ip, inbuf, len, &ilen)); + + if (PRINT_FIELD(BRSTACKINSNLEN)) + printed += fprintf(fp, "ilen: %d\t", ilen); + + printed += fprintf(fp, "#%s%s%s%s", en->flags.predicted ? " PRED" : "", en->flags.mispred ? " MISPRED" : "", en->flags.in_tx ? " INTX" : "", @@ -1053,6 +1206,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, struct machine *machine, FILE *fp) { struct branch_stack *br = sample->branch_stack; + struct branch_entry *entries = perf_sample__branch_entries(sample); u64 start, end; int i, insn, len, nr, ilen, printed = 0; struct perf_insn x; @@ -1073,31 +1227,32 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, printed += fprintf(fp, "%c", '\n'); /* Handle first from jump, of which we don't know the entry. */ - len = grab_bb(buffer, br->entries[nr-1].from, - br->entries[nr-1].from, + len = grab_bb(buffer, entries[nr-1].from, + entries[nr-1].from, machine, thread, &x.is64bit, &x.cpumode, false); if (len > 0) { - printed += ip__fprintf_sym(br->entries[nr - 1].from, thread, + printed += ip__fprintf_sym(entries[nr - 1].from, thread, x.cpumode, x.cpu, &lastsym, attr, fp); - printed += ip__fprintf_jump(br->entries[nr - 1].from, &br->entries[nr - 1], - &x, buffer, len, 0, fp, &total_cycles); + printed += ip__fprintf_jump(entries[nr - 1].from, &entries[nr - 1], + &x, buffer, len, 0, fp, &total_cycles, + attr); if (PRINT_FIELD(SRCCODE)) - printed += print_srccode(thread, x.cpumode, br->entries[nr - 1].from); + printed += print_srccode(thread, x.cpumode, entries[nr - 1].from); } /* Print all blocks */ for (i = nr - 2; i >= 0; i--) { - if (br->entries[i].from || br->entries[i].to) + if (entries[i].from || entries[i].to) pr_debug("%d: %" PRIx64 "-%" PRIx64 "\n", i, - br->entries[i].from, - br->entries[i].to); - start = br->entries[i + 1].to; - end = br->entries[i].from; + entries[i].from, + entries[i].to); + start = entries[i + 1].to; + end = entries[i].from; len = grab_bb(buffer, start, end, machine, thread, &x.is64bit, &x.cpumode, false); /* Patch up missing kernel transfers due to ring filters */ if (len == -ENXIO && i > 0) { - end = br->entries[--i].from; + end = entries[--i].from; pr_debug("\tpatching up to %" PRIx64 "-%" PRIx64 "\n", start, end); len = grab_bb(buffer, start, end, machine, thread, &x.is64bit, &x.cpumode, false); } @@ -1110,15 +1265,18 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, printed += ip__fprintf_sym(ip, thread, x.cpumode, x.cpu, &lastsym, attr, fp); if (ip == end) { - printed += ip__fprintf_jump(ip, &br->entries[i], &x, buffer + off, len - off, ++insn, fp, - &total_cycles); + printed += ip__fprintf_jump(ip, &entries[i], &x, buffer + off, len - off, ++insn, fp, + &total_cycles, attr); if (PRINT_FIELD(SRCCODE)) printed += print_srccode(thread, x.cpumode, ip); break; } else { ilen = 0; - printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", ip, + printed += fprintf(fp, "\t%016" PRIx64 "\t%s", ip, dump_insn(&x, ip, buffer + off, len - off, &ilen)); + if (PRINT_FIELD(BRSTACKINSNLEN)) + printed += fprintf(fp, "\tilen: %d", ilen); + printed += fprintf(fp, "\n"); if (ilen == 0) break; if (PRINT_FIELD(SRCCODE)) @@ -1134,9 +1292,9 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, * Hit the branch? In this case we are already done, and the target * has not been executed yet. */ - if (br->entries[0].from == sample->ip) + if (entries[0].from == sample->ip) goto out; - if (br->entries[0].flags.abort) + if (entries[0].flags.abort) goto out; /* @@ -1147,7 +1305,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, * between final branch and sample. When this happens just * continue walking after the last TO until we hit a branch. */ - start = br->entries[0].to; + start = entries[0].to; end = sample->ip; if (end < start) { /* Missing jump. Scan 128 bytes for the next branch */ @@ -1161,16 +1319,23 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample, machine, thread, &x.is64bit, &x.cpumode, false); if (len <= 0) goto out; - printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", sample->ip, - dump_insn(&x, sample->ip, buffer, len, NULL)); + ilen = 0; + printed += fprintf(fp, "\t%016" PRIx64 "\t%s", sample->ip, + dump_insn(&x, sample->ip, buffer, len, &ilen)); + if (PRINT_FIELD(BRSTACKINSNLEN)) + printed += fprintf(fp, "\tilen: %d", ilen); + printed += fprintf(fp, "\n"); if (PRINT_FIELD(SRCCODE)) print_srccode(thread, x.cpumode, sample->ip); goto out; } for (off = 0; off <= end - start; off += ilen) { ilen = 0; - printed += fprintf(fp, "\t%016" PRIx64 "\t%s\n", start + off, + printed += fprintf(fp, "\t%016" PRIx64 "\t%s", start + off, dump_insn(&x, start + off, buffer + off, len - off, &ilen)); + if (PRINT_FIELD(BRSTACKINSNLEN)) + printed += fprintf(fp, "\tilen: %d", ilen); + printed += fprintf(fp, "\n"); if (ilen == 0) break; if (arch_is_branch(buffer + off, len - off, x.is64bit) && start + off != sample->ip) { @@ -1220,17 +1385,18 @@ static const char *resolve_branch_sym(struct perf_sample *sample, struct evsel *evsel, struct thread *thread, struct addr_location *al, + struct addr_location *addr_al, u64 *ip) { - struct addr_location addr_al; struct perf_event_attr *attr = &evsel->core.attr; const char *name = NULL; if (sample->flags & (PERF_IP_FLAG_CALL | PERF_IP_FLAG_TRACE_BEGIN)) { if (sample_addr_correlates_sym(attr)) { - thread__resolve(thread, &addr_al, sample); - if (addr_al.sym) - name = addr_al.sym->name; + if (!addr_al->thread) + thread__resolve(thread, addr_al, sample); + if (addr_al->sym) + name = addr_al->sym->name; else *ip = sample->addr; } else { @@ -1248,7 +1414,9 @@ static const char *resolve_branch_sym(struct perf_sample *sample, static int perf_sample__fprintf_callindent(struct perf_sample *sample, struct evsel *evsel, struct thread *thread, - struct addr_location *al, FILE *fp) + struct addr_location *al, + struct addr_location *addr_al, + FILE *fp) { struct perf_event_attr *attr = &evsel->core.attr; size_t depth = thread_stack__depth(thread, sample->cpu); @@ -1265,7 +1433,7 @@ static int perf_sample__fprintf_callindent(struct perf_sample *sample, if (thread->ts && sample->flags & PERF_IP_FLAG_RETURN) depth += 1; - name = resolve_branch_sym(sample, evsel, thread, al, &ip); + name = resolve_branch_sym(sample, evsel, thread, al, addr_al, &ip); if (PRINT_FIELD(DSO) && !(PRINT_FIELD(IP) || PRINT_FIELD(ADDR))) { dlen += fprintf(fp, "("); @@ -1300,6 +1468,13 @@ __weak void arch_fetch_insn(struct perf_sample *sample __maybe_unused, { } +void script_fetch_insn(struct perf_sample *sample, struct thread *thread, + struct machine *machine) +{ + if (sample->insn_len == 0 && native_arch) + arch_fetch_insn(sample, thread, machine); +} + static int perf_sample__fprintf_insn(struct perf_sample *sample, struct perf_event_attr *attr, struct thread *thread, @@ -1307,8 +1482,7 @@ static int perf_sample__fprintf_insn(struct perf_sample *sample, { int printed = 0; - if (sample->insn_len == 0 && native_arch) - arch_fetch_insn(sample, thread, machine); + script_fetch_insn(sample, thread, machine); if (PRINT_FIELD(INSNLEN)) printed += fprintf(fp, " ilen: %d", sample->insn_len); @@ -1319,7 +1493,7 @@ static int perf_sample__fprintf_insn(struct perf_sample *sample, for (i = 0; i < sample->insn_len; i++) printed += fprintf(fp, " %02x", (unsigned char)sample->insn[i]); } - if (PRINT_FIELD(BRSTACKINSN)) + if (PRINT_FIELD(BRSTACKINSN) || PRINT_FIELD(BRSTACKINSNLEN)) printed += perf_sample__fprintf_brstackinsn(sample, thread, attr, machine, fp); return printed; @@ -1343,6 +1517,7 @@ static int perf_sample__fprintf_bts(struct perf_sample *sample, struct evsel *evsel, struct thread *thread, struct addr_location *al, + struct addr_location *addr_al, struct machine *machine, FILE *fp) { struct perf_event_attr *attr = &evsel->core.attr; @@ -1351,7 +1526,7 @@ static int perf_sample__fprintf_bts(struct perf_sample *sample, int printed = 0; if (PRINT_FIELD(CALLINDENT)) - printed += perf_sample__fprintf_callindent(sample, evsel, thread, al, fp); + printed += perf_sample__fprintf_callindent(sample, evsel, thread, al, addr_al, fp); /* print branch_from information */ if (PRINT_FIELD(IP)) { @@ -1419,6 +1594,8 @@ static struct { {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TX_ABORT, "tx abrt"}, {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_BEGIN, "tr strt"}, {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_TRACE_END, "tr end"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMENTRY, "vmentry"}, + {PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CALL | PERF_IP_FLAG_VMEXIT, "vmexit"}, {0, NULL} }; @@ -1434,42 +1611,58 @@ static const char *sample_flags_to_name(u32 flags) return NULL; } -static int perf_sample__fprintf_flags(u32 flags, FILE *fp) +int perf_sample__sprintf_flags(u32 flags, char *str, size_t sz) { + u32 xf = PERF_IP_FLAG_IN_TX | PERF_IP_FLAG_INTR_DISABLE | + PERF_IP_FLAG_INTR_TOGGLE; const char *chars = PERF_IP_FLAG_CHARS; - const int n = strlen(PERF_IP_FLAG_CHARS); - bool in_tx = flags & PERF_IP_FLAG_IN_TX; + const size_t n = strlen(PERF_IP_FLAG_CHARS); const char *name = NULL; - char str[33]; - int i, pos = 0; + size_t i, pos = 0; + char xs[16] = {0}; + + if (flags & xf) + snprintf(xs, sizeof(xs), "(%s%s%s)", + flags & PERF_IP_FLAG_IN_TX ? "x" : "", + flags & PERF_IP_FLAG_INTR_DISABLE ? "D" : "", + flags & PERF_IP_FLAG_INTR_TOGGLE ? "t" : ""); - name = sample_flags_to_name(flags & ~PERF_IP_FLAG_IN_TX); + name = sample_flags_to_name(flags & ~xf); if (name) - return fprintf(fp, " %-15s%4s ", name, in_tx ? "(x)" : ""); + return snprintf(str, sz, "%-15s%6s", name, xs); if (flags & PERF_IP_FLAG_TRACE_BEGIN) { - name = sample_flags_to_name(flags & ~(PERF_IP_FLAG_IN_TX | PERF_IP_FLAG_TRACE_BEGIN)); + name = sample_flags_to_name(flags & ~(xf | PERF_IP_FLAG_TRACE_BEGIN)); if (name) - return fprintf(fp, " tr strt %-7s%4s ", name, in_tx ? "(x)" : ""); + return snprintf(str, sz, "tr strt %-7s%6s", name, xs); } if (flags & PERF_IP_FLAG_TRACE_END) { - name = sample_flags_to_name(flags & ~(PERF_IP_FLAG_IN_TX | PERF_IP_FLAG_TRACE_END)); + name = sample_flags_to_name(flags & ~(xf | PERF_IP_FLAG_TRACE_END)); if (name) - return fprintf(fp, " tr end %-7s%4s ", name, in_tx ? "(x)" : ""); + return snprintf(str, sz, "tr end %-7s%6s", name, xs); } for (i = 0; i < n; i++, flags >>= 1) { - if (flags & 1) + if ((flags & 1) && pos < sz) str[pos++] = chars[i]; } for (; i < 32; i++, flags >>= 1) { - if (flags & 1) + if ((flags & 1) && pos < sz) str[pos++] = '?'; } - str[pos] = 0; + if (pos < sz) + str[pos] = 0; - return fprintf(fp, " %-19s ", str); + return pos; +} + +static int perf_sample__fprintf_flags(u32 flags, FILE *fp) +{ + char str[SAMPLE_FLAGS_BUF_SIZE]; + + perf_sample__sprintf_flags(flags, str, sizeof(str)); + return fprintf(fp, " %-21s ", str); } struct printer_data { @@ -1565,16 +1758,44 @@ static int perf_sample__fprintf_pt_spacing(int len, FILE *fp) return perf_sample__fprintf_spacing(len, 34, fp); } +/* If a value contains only printable ASCII characters padded with NULLs */ +static bool ptw_is_prt(u64 val) +{ + char c; + u32 i; + + for (i = 0; i < sizeof(val); i++) { + c = ((char *)&val)[i]; + if (!c) + break; + if (!isprint(c) || !isascii(c)) + return false; + } + for (; i < sizeof(val); i++) { + c = ((char *)&val)[i]; + if (c) + return false; + } + return true; +} + static int perf_sample__fprintf_synth_ptwrite(struct perf_sample *sample, FILE *fp) { struct perf_synth_intel_ptwrite *data = perf_sample__synth_ptr(sample); + char str[sizeof(u64) + 1] = ""; int len; + u64 val; if (perf_sample__bad_synth_size(sample, *data)) return 0; - len = fprintf(fp, " IP: %u payload: %#" PRIx64 " ", - data->ip, le64_to_cpu(data->payload)); + val = le64_to_cpu(data->payload); + if (ptw_is_prt(val)) { + memcpy(str, &val, sizeof(val)); + str[sizeof(val)] = 0; + } + len = fprintf(fp, " IP: %u payload: %#" PRIx64 " %s ", + data->ip, val, str); return len + perf_sample__fprintf_pt_spacing(len, fp); } @@ -1648,6 +1869,68 @@ static int perf_sample__fprintf_synth_cbr(struct perf_sample *sample, FILE *fp) return len + perf_sample__fprintf_pt_spacing(len, fp); } +static int perf_sample__fprintf_synth_psb(struct perf_sample *sample, FILE *fp) +{ + struct perf_synth_intel_psb *data = perf_sample__synth_ptr(sample); + int len; + + if (perf_sample__bad_synth_size(sample, *data)) + return 0; + + len = fprintf(fp, " psb offs: %#" PRIx64, data->offset); + return len + perf_sample__fprintf_pt_spacing(len, fp); +} + +/* Intel PT Event Trace */ +static int perf_sample__fprintf_synth_evt(struct perf_sample *sample, FILE *fp) +{ + struct perf_synth_intel_evt *data = perf_sample__synth_ptr(sample); + const char *cfe[32] = {NULL, "INTR", "IRET", "SMI", "RSM", "SIPI", + "INIT", "VMENTRY", "VMEXIT", "VMEXIT_INTR", + "SHUTDOWN"}; + const char *evd[64] = {"PFA", "VMXQ", "VMXR"}; + const char *s; + int len, i; + + if (perf_sample__bad_synth_size(sample, *data)) + return 0; + + s = cfe[data->type]; + if (s) { + len = fprintf(fp, " cfe: %s IP: %d vector: %u", + s, data->ip, data->vector); + } else { + len = fprintf(fp, " cfe: %u IP: %d vector: %u", + data->type, data->ip, data->vector); + } + for (i = 0; i < data->evd_cnt; i++) { + unsigned int et = data->evd[i].evd_type & 0x3f; + + s = evd[et]; + if (s) { + len += fprintf(fp, " %s: %#" PRIx64, + s, data->evd[i].payload); + } else { + len += fprintf(fp, " EVD_%u: %#" PRIx64, + et, data->evd[i].payload); + } + } + return len + perf_sample__fprintf_pt_spacing(len, fp); +} + +static int perf_sample__fprintf_synth_iflag_chg(struct perf_sample *sample, FILE *fp) +{ + struct perf_synth_intel_iflag_chg *data = perf_sample__synth_ptr(sample); + int len; + + if (perf_sample__bad_synth_size(sample, *data)) + return 0; + + len = fprintf(fp, " IFLAG: %d->%d %s branch", !data->iflag, data->iflag, + data->via_branch ? "via" : "non"); + return len + perf_sample__fprintf_pt_spacing(len, fp); +} + static int perf_sample__fprintf_synth(struct perf_sample *sample, struct evsel *evsel, FILE *fp) { @@ -1664,6 +1947,12 @@ static int perf_sample__fprintf_synth(struct perf_sample *sample, return perf_sample__fprintf_synth_pwrx(sample, fp); case PERF_SYNTH_INTEL_CBR: return perf_sample__fprintf_synth_cbr(sample, fp); + case PERF_SYNTH_INTEL_PSB: + return perf_sample__fprintf_synth_psb(sample, fp); + case PERF_SYNTH_INTEL_EVT: + return perf_sample__fprintf_synth_evt(sample, fp); + case PERF_SYNTH_INTEL_IFLAG_CHG: + return perf_sample__fprintf_synth_iflag_chg(sample, fp); default: break; } @@ -1671,35 +1960,13 @@ static int perf_sample__fprintf_synth(struct perf_sample *sample, return 0; } -struct perf_script { - struct perf_tool tool; - struct perf_session *session; - bool show_task_events; - bool show_mmap_events; - bool show_switch_events; - bool show_namespace_events; - bool show_lost_events; - bool show_round_events; - bool show_bpf_events; - bool allocated; - bool per_event_dump; - struct evswitch evswitch; - struct perf_cpu_map *cpus; - struct perf_thread_map *threads; - int name_width; - const char *time_str; - struct perf_time_interval *ptime_range; - int range_size; - int range_num; -}; - -static int perf_evlist__max_name_len(struct evlist *evlist) +static int evlist__max_name_len(struct evlist *evlist) { struct evsel *evsel; int max = 0; evlist__for_each_entry(evlist, evsel) { - int len = strlen(perf_evsel__name(evsel)); + int len = strlen(evsel__name(evsel)); max = MAX(len, max); } @@ -1740,7 +2007,7 @@ static void script_print_metric(struct perf_stat_config *config __maybe_unused, if (!fmt) return; - perf_sample__fprintf_start(mctx->sample, mctx->thread, mctx->evsel, + perf_sample__fprintf_start(NULL, mctx->sample, mctx->thread, mctx->evsel, PERF_RECORD_SAMPLE, mctx->fp); fputs("\tmetric: ", mctx->fp); if (color) @@ -1755,7 +2022,7 @@ static void script_new_line(struct perf_stat_config *config __maybe_unused, { struct metric_ctx *mctx = ctx; - perf_sample__fprintf_start(mctx->sample, mctx->thread, mctx->evsel, + perf_sample__fprintf_start(NULL, mctx->sample, mctx->thread, mctx->evsel, PERF_RECORD_SAMPLE, mctx->fp); fputs("\tmetric: ", mctx->fp); } @@ -1766,6 +2033,7 @@ static void perf_sample__fprint_metric(struct perf_script *script, struct perf_sample *sample, FILE *fp) { + struct evsel *leader = evsel__leader(evsel); struct perf_stat_output_ctx ctx = { .print_metric = script_print_metric, .new_line = script_new_line, @@ -1781,8 +2049,8 @@ static void perf_sample__fprint_metric(struct perf_script *script, u64 val; if (!evsel->stats) - perf_evlist__alloc_stats(script->session->evlist, false); - if (evsel_script(evsel->leader)->gnum++ == 0) + evlist__alloc_stats(script->session->evlist, false); + if (evsel_script(leader)->gnum++ == 0) perf_stat__reset_shadow_stats(); val = sample->period * evsel->scale; perf_stat__update_shadow_stats(evsel, @@ -1790,8 +2058,8 @@ static void perf_sample__fprint_metric(struct perf_script *script, sample->cpu, &rt_stat); evsel_script(evsel)->val = val; - if (evsel_script(evsel->leader)->gnum == evsel->leader->core.nr_members) { - for_each_group_member (ev2, evsel->leader) { + if (evsel_script(leader)->gnum == leader->core.nr_members) { + for_each_group_member (ev2, leader) { perf_stat__print_shadow_stats(&stat_config, ev2, evsel_script(ev2)->val, sample->cpu, @@ -1799,14 +2067,15 @@ static void perf_sample__fprint_metric(struct perf_script *script, NULL, &rt_stat); } - evsel_script(evsel->leader)->gnum = 0; + evsel_script(leader)->gnum = 0; } } static bool show_event(struct perf_sample *sample, struct evsel *evsel, struct thread *thread, - struct addr_location *al) + struct addr_location *al, + struct addr_location *addr_al) { int depth = thread_stack__depth(thread, sample->cpu); @@ -1822,7 +2091,7 @@ static bool show_event(struct perf_sample *sample, } else { const char *s = symbol_conf.graph_function; u64 ip; - const char *name = resolve_branch_sym(sample, evsel, thread, al, + const char *name = resolve_branch_sym(sample, evsel, thread, al, addr_al, &ip); unsigned nlen; @@ -1847,6 +2116,7 @@ static bool show_event(struct perf_sample *sample, static void process_event(struct perf_script *script, struct perf_sample *sample, struct evsel *evsel, struct addr_location *al, + struct addr_location *addr_al, struct machine *machine) { struct thread *thread = al->thread; @@ -1854,29 +2124,25 @@ static void process_event(struct perf_script *script, unsigned int type = output_type(attr->type); struct evsel_script *es = evsel->priv; FILE *fp = es->fp; + char str[PAGE_SIZE_NAME_LEN]; + const char *arch = perf_env__arch(machine->env); if (output[type].fields == 0) return; - if (!show_event(sample, evsel, thread, al)) - return; - - if (evswitch__discard(&script->evswitch, evsel)) - return; - ++es->samples; - perf_sample__fprintf_start(sample, thread, evsel, + perf_sample__fprintf_start(script, sample, thread, evsel, PERF_RECORD_SAMPLE, fp); if (PRINT_FIELD(PERIOD)) fprintf(fp, "%10" PRIu64 " ", sample->period); if (PRINT_FIELD(EVNAME)) { - const char *evname = perf_evsel__name(evsel); + const char *evname = evsel__name(evsel); if (!script->name_width) - script->name_width = perf_evlist__max_name_len(script->session->evlist); + script->name_width = evlist__max_name_len(script->session->evlist); fprintf(fp, "%*s: ", script->name_width, evname ?: "[unknown]"); } @@ -1885,7 +2151,7 @@ static void process_event(struct perf_script *script, perf_sample__fprintf_flags(sample->flags, fp); if (is_bts_event(attr)) { - perf_sample__fprintf_bts(sample, evsel, thread, al, machine, fp); + perf_sample__fprintf_bts(sample, evsel, thread, al, addr_al, machine, fp); return; } @@ -1906,9 +2172,15 @@ static void process_event(struct perf_script *script, if (PRINT_FIELD(WEIGHT)) fprintf(fp, "%16" PRIu64, sample->weight); + if (PRINT_FIELD(INS_LAT)) + fprintf(fp, "%16" PRIu16, sample->ins_lat); + if (PRINT_FIELD(IP)) { struct callchain_cursor *cursor = NULL; + if (script->stitch_lbr) + al->thread->lbr_stitch_enable = true; + if (symbol_conf.use_callchain && sample->callchain && thread__resolve_callchain(al->thread, &callchain_cursor, evsel, sample, NULL, NULL, scripting_max_stack) == 0) @@ -1920,10 +2192,10 @@ static void process_event(struct perf_script *script, } if (PRINT_FIELD(IREGS)) - perf_sample__fprintf_iregs(sample, attr, fp); + perf_sample__fprintf_iregs(sample, attr, arch, fp); if (PRINT_FIELD(UREGS)) - perf_sample__fprintf_uregs(sample, attr, fp); + perf_sample__fprintf_uregs(sample, attr, arch, fp); if (PRINT_FIELD(BRSTACK)) perf_sample__fprintf_brstack(sample, thread, attr, fp); @@ -1932,13 +2204,19 @@ static void process_event(struct perf_script *script, else if (PRINT_FIELD(BRSTACKOFF)) perf_sample__fprintf_brstackoff(sample, thread, attr, fp); - if (perf_evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT)) + if (evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT)) perf_sample__fprintf_bpf_output(sample, fp); perf_sample__fprintf_insn(sample, attr, thread, machine, fp); if (PRINT_FIELD(PHYS_ADDR)) fprintf(fp, "%16" PRIx64, sample->phys_addr); + if (PRINT_FIELD(DATA_PAGE_SIZE)) + fprintf(fp, " %s", get_page_size_name(sample->data_page_size, str)); + + if (PRINT_FIELD(CODE_PAGE_SIZE)) + fprintf(fp, " %s", get_page_size_name(sample->code_page_size, str)); + perf_sample__fprintf_ipc(sample, attr, fp); fprintf(fp, "\n"); @@ -1961,13 +2239,10 @@ static struct scripting_ops *scripting_ops; static void __process_stat(struct evsel *counter, u64 tstamp) { int nthreads = perf_thread_map__nr(counter->core.threads); - int ncpus = perf_evsel__nr_cpus(counter); - int cpu, thread; + int idx, thread; + struct perf_cpu cpu; static int header_printed; - if (counter->core.system_wide) - nthreads = 1; - if (!header_printed) { printf("%3s %8s %15s %15s %15s %15s %s\n", "CPU", "THREAD", "VAL", "ENA", "RUN", "TIME", "EVENT"); @@ -1975,19 +2250,19 @@ static void __process_stat(struct evsel *counter, u64 tstamp) } for (thread = 0; thread < nthreads; thread++) { - for (cpu = 0; cpu < ncpus; cpu++) { + perf_cpu_map__for_each_cpu(cpu, idx, evsel__cpus(counter)) { struct perf_counts_values *counts; - counts = perf_counts(counter->counts, cpu, thread); + counts = perf_counts(counter->counts, idx, thread); printf("%3d %8d %15" PRIu64 " %15" PRIu64 " %15" PRIu64 " %15" PRIu64 " %s\n", - counter->core.cpus->map[cpu], + cpu.cpu, perf_thread_map__pid(counter->core.threads, thread), counts->val, counts->ena, counts->run, tstamp, - perf_evsel__name(counter)); + evsel__name(counter)); } } } @@ -2026,7 +2301,7 @@ static int cleanup_scripting(void) static bool filter_cpu(struct perf_sample *sample) { - if (cpu_list) + if (cpu_list && sample->cpu != (u32)-1) return !test_bit(sample->cpu, cpu_bitmap); return false; } @@ -2039,10 +2314,23 @@ static int process_sample_event(struct perf_tool *tool, { struct perf_script *scr = container_of(tool, struct perf_script, tool); struct addr_location al; + struct addr_location addr_al; + int ret = 0; + + /* Set thread to NULL to indicate addr_al and al are not initialized */ + addr_al.thread = NULL; + al.thread = NULL; + + ret = dlfilter__filter_event_early(dlfilter, event, sample, evsel, machine, &al, &addr_al); + if (ret) { + if (ret > 0) + ret = 0; + goto out_put; + } if (perf_time__ranges_skip_sample(scr->ptime_range, scr->range_num, sample->time)) { - return 0; + goto out_put; } if (debug_mode) { @@ -2053,29 +2341,53 @@ static int process_sample_event(struct perf_tool *tool, nr_unordered++; } last_timestamp = sample->time; - return 0; + goto out_put; } - if (machine__resolve(machine, &al, sample) < 0) { + if (filter_cpu(sample)) + goto out_put; + + if (!al.thread && machine__resolve(machine, &al, sample) < 0) { pr_err("problem processing %d event, skipping it.\n", event->header.type); - return -1; + ret = -1; + goto out_put; } if (al.filtered) goto out_put; - if (filter_cpu(sample)) + if (!show_event(sample, evsel, al.thread, &al, &addr_al)) goto out_put; - if (scripting_ops) - scripting_ops->process_event(event, sample, evsel, &al); - else - process_event(scr, sample, evsel, &al, machine); + if (evswitch__discard(&scr->evswitch, evsel)) + goto out_put; + + ret = dlfilter__filter_event(dlfilter, event, sample, evsel, machine, &al, &addr_al); + if (ret) { + if (ret > 0) + ret = 0; + goto out_put; + } + + if (scripting_ops) { + struct addr_location *addr_al_ptr = NULL; + + if ((evsel->core.attr.sample_type & PERF_SAMPLE_ADDR) && + sample_addr_correlates_sym(&evsel->core.attr)) { + if (!addr_al.thread) + thread__resolve(al.thread, &addr_al, sample); + addr_al_ptr = &addr_al; + } + scripting_ops->process_event(event, sample, evsel, &al, addr_al_ptr); + } else { + process_event(scr, sample, evsel, &al, &addr_al, machine); + } out_put: - addr_location__put(&al); - return 0; + if (al.thread) + addr_location__put(&al); + return ret; } static int process_attr(struct perf_tool *tool, union perf_event *event, @@ -2084,6 +2396,7 @@ static int process_attr(struct perf_tool *tool, union perf_event *event, struct perf_script *scr = container_of(tool, struct perf_script, tool); struct evlist *evlist; struct evsel *evsel, *pos; + u64 sample_type; int err; static struct evsel_script *es; @@ -2096,8 +2409,7 @@ static int process_attr(struct perf_tool *tool, union perf_event *event, if (!evsel->priv) { if (scr->per_event_dump) { - evsel->priv = perf_evsel_script__new(evsel, - scr->session->data); + evsel->priv = evsel_script__new(evsel, scr->session->data); } else { es = zalloc(sizeof(*es)); if (!es) @@ -2116,49 +2428,89 @@ static int process_attr(struct perf_tool *tool, union perf_event *event, return 0; } - set_print_ip_opts(&evsel->core.attr); + if (evsel->core.attr.sample_type) { + err = evsel__check_attr(evsel, scr->session); + if (err) + return err; + } + + /* + * Check if we need to enable callchains based + * on events sample_type. + */ + sample_type = evlist__combined_sample_type(evlist); + callchain_param_setup(sample_type, perf_env__arch((*pevlist)->env)); - if (evsel->core.attr.sample_type) - err = perf_evsel__check_attr(evsel, scr->session); + /* Enable fields for callchain entries */ + if (symbol_conf.use_callchain && + (sample_type & PERF_SAMPLE_CALLCHAIN || + sample_type & PERF_SAMPLE_BRANCH_STACK || + (sample_type & PERF_SAMPLE_REGS_USER && + sample_type & PERF_SAMPLE_STACK_USER))) { + int type = output_type(evsel->core.attr.type); - return err; + if (!(output[type].user_unset_fields & PERF_OUTPUT_IP)) + output[type].fields |= PERF_OUTPUT_IP; + if (!(output[type].user_unset_fields & PERF_OUTPUT_SYM)) + output[type].fields |= PERF_OUTPUT_SYM; + } + set_print_ip_opts(&evsel->core.attr); + return 0; } -static int process_comm_event(struct perf_tool *tool, - union perf_event *event, - struct perf_sample *sample, - struct machine *machine) +static int print_event_with_time(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine, + pid_t pid, pid_t tid, u64 timestamp) { - struct thread *thread; struct perf_script *script = container_of(tool, struct perf_script, tool); struct perf_session *session = script->session; - struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id); - int ret = -1; + struct evsel *evsel = evlist__id2evsel(session->evlist, sample->id); + struct thread *thread = NULL; - thread = machine__findnew_thread(machine, event->comm.pid, event->comm.tid); - if (thread == NULL) { - pr_debug("problem processing COMM event, skipping it.\n"); - return -1; + if (evsel && !evsel->core.attr.sample_id_all) { + sample->cpu = 0; + sample->time = timestamp; + sample->pid = pid; + sample->tid = tid; } - if (perf_event__process_comm(tool, event, sample, machine) < 0) - goto out; + if (filter_cpu(sample)) + return 0; - if (!evsel->core.attr.sample_id_all) { - sample->cpu = 0; - sample->time = 0; - sample->tid = event->comm.tid; - sample->pid = event->comm.pid; - } - if (!filter_cpu(sample)) { - perf_sample__fprintf_start(sample, thread, evsel, - PERF_RECORD_COMM, stdout); - perf_event__fprintf(event, stdout); + if (tid != -1) + thread = machine__findnew_thread(machine, pid, tid); + + if (evsel) { + perf_sample__fprintf_start(script, sample, thread, evsel, + event->header.type, stdout); } - ret = 0; -out: + + perf_event__fprintf(event, machine, stdout); + thread__put(thread); - return ret; + + return 0; +} + +static int print_event(struct perf_tool *tool, union perf_event *event, + struct perf_sample *sample, struct machine *machine, + pid_t pid, pid_t tid) +{ + return print_event_with_time(tool, event, sample, machine, pid, tid, 0); +} + +static int process_comm_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + if (perf_event__process_comm(tool, event, sample, machine) < 0) + return -1; + + return print_event(tool, event, sample, machine, event->comm.pid, + event->comm.tid); } static int process_namespaces_event(struct perf_tool *tool, @@ -2166,37 +2518,23 @@ static int process_namespaces_event(struct perf_tool *tool, struct perf_sample *sample, struct machine *machine) { - struct thread *thread; - struct perf_script *script = container_of(tool, struct perf_script, tool); - struct perf_session *session = script->session; - struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id); - int ret = -1; - - thread = machine__findnew_thread(machine, event->namespaces.pid, - event->namespaces.tid); - if (thread == NULL) { - pr_debug("problem processing NAMESPACES event, skipping it.\n"); + if (perf_event__process_namespaces(tool, event, sample, machine) < 0) return -1; - } - if (perf_event__process_namespaces(tool, event, sample, machine) < 0) - goto out; + return print_event(tool, event, sample, machine, event->namespaces.pid, + event->namespaces.tid); +} - if (!evsel->core.attr.sample_id_all) { - sample->cpu = 0; - sample->time = 0; - sample->tid = event->namespaces.tid; - sample->pid = event->namespaces.pid; - } - if (!filter_cpu(sample)) { - perf_sample__fprintf_start(sample, thread, evsel, - PERF_RECORD_NAMESPACES, stdout); - perf_event__fprintf(event, stdout); - } - ret = 0; -out: - thread__put(thread); - return ret; +static int process_cgroup_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + if (perf_event__process_cgroup(tool, event, sample, machine) < 0) + return -1; + + return print_event(tool, event, sample, machine, sample->pid, + sample->tid); } static int process_fork_event(struct perf_tool *tool, @@ -2204,69 +2542,24 @@ static int process_fork_event(struct perf_tool *tool, struct perf_sample *sample, struct machine *machine) { - struct thread *thread; - struct perf_script *script = container_of(tool, struct perf_script, tool); - struct perf_session *session = script->session; - struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id); - if (perf_event__process_fork(tool, event, sample, machine) < 0) return -1; - thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid); - if (thread == NULL) { - pr_debug("problem processing FORK event, skipping it.\n"); - return -1; - } - - if (!evsel->core.attr.sample_id_all) { - sample->cpu = 0; - sample->time = event->fork.time; - sample->tid = event->fork.tid; - sample->pid = event->fork.pid; - } - if (!filter_cpu(sample)) { - perf_sample__fprintf_start(sample, thread, evsel, - PERF_RECORD_FORK, stdout); - perf_event__fprintf(event, stdout); - } - thread__put(thread); - - return 0; + return print_event_with_time(tool, event, sample, machine, + event->fork.pid, event->fork.tid, + event->fork.time); } static int process_exit_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) { - int err = 0; - struct thread *thread; - struct perf_script *script = container_of(tool, struct perf_script, tool); - struct perf_session *session = script->session; - struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id); - - thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid); - if (thread == NULL) { - pr_debug("problem processing EXIT event, skipping it.\n"); + /* Print before 'exit' deletes anything */ + if (print_event_with_time(tool, event, sample, machine, event->fork.pid, + event->fork.tid, event->fork.time)) return -1; - } - - if (!evsel->core.attr.sample_id_all) { - sample->cpu = 0; - sample->time = 0; - sample->tid = event->fork.tid; - sample->pid = event->fork.pid; - } - if (!filter_cpu(sample)) { - perf_sample__fprintf_start(sample, thread, evsel, - PERF_RECORD_EXIT, stdout); - perf_event__fprintf(event, stdout); - } - if (perf_event__process_exit(tool, event, sample, machine) < 0) - err = -1; - - thread__put(thread); - return err; + return perf_event__process_exit(tool, event, sample, machine); } static int process_mmap_event(struct perf_tool *tool, @@ -2274,33 +2567,11 @@ static int process_mmap_event(struct perf_tool *tool, struct perf_sample *sample, struct machine *machine) { - struct thread *thread; - struct perf_script *script = container_of(tool, struct perf_script, tool); - struct perf_session *session = script->session; - struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id); - if (perf_event__process_mmap(tool, event, sample, machine) < 0) return -1; - thread = machine__findnew_thread(machine, event->mmap.pid, event->mmap.tid); - if (thread == NULL) { - pr_debug("problem processing MMAP event, skipping it.\n"); - return -1; - } - - if (!evsel->core.attr.sample_id_all) { - sample->cpu = 0; - sample->time = 0; - sample->tid = event->mmap.tid; - sample->pid = event->mmap.pid; - } - if (!filter_cpu(sample)) { - perf_sample__fprintf_start(sample, thread, evsel, - PERF_RECORD_MMAP, stdout); - perf_event__fprintf(event, stdout); - } - thread__put(thread); - return 0; + return print_event(tool, event, sample, machine, event->mmap.pid, + event->mmap.tid); } static int process_mmap2_event(struct perf_tool *tool, @@ -2308,33 +2579,11 @@ static int process_mmap2_event(struct perf_tool *tool, struct perf_sample *sample, struct machine *machine) { - struct thread *thread; - struct perf_script *script = container_of(tool, struct perf_script, tool); - struct perf_session *session = script->session; - struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id); - if (perf_event__process_mmap2(tool, event, sample, machine) < 0) return -1; - thread = machine__findnew_thread(machine, event->mmap2.pid, event->mmap2.tid); - if (thread == NULL) { - pr_debug("problem processing MMAP2 event, skipping it.\n"); - return -1; - } - - if (!evsel->core.attr.sample_id_all) { - sample->cpu = 0; - sample->time = 0; - sample->tid = event->mmap2.tid; - sample->pid = event->mmap2.pid; - } - if (!filter_cpu(sample)) { - perf_sample__fprintf_start(sample, thread, evsel, - PERF_RECORD_MMAP2, stdout); - perf_event__fprintf(event, stdout); - } - thread__put(thread); - return 0; + return print_event(tool, event, sample, machine, event->mmap2.pid, + event->mmap2.tid); } static int process_switch_event(struct perf_tool *tool, @@ -2342,34 +2591,30 @@ static int process_switch_event(struct perf_tool *tool, struct perf_sample *sample, struct machine *machine) { - struct thread *thread; struct perf_script *script = container_of(tool, struct perf_script, tool); - struct perf_session *session = script->session; - struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id); if (perf_event__process_switch(tool, event, sample, machine) < 0) return -1; - if (scripting_ops && scripting_ops->process_switch) + if (scripting_ops && scripting_ops->process_switch && !filter_cpu(sample)) scripting_ops->process_switch(event, sample, machine); if (!script->show_switch_events) return 0; - thread = machine__findnew_thread(machine, sample->pid, - sample->tid); - if (thread == NULL) { - pr_debug("problem processing SWITCH event, skipping it.\n"); - return -1; - } + return print_event(tool, event, sample, machine, sample->pid, + sample->tid); +} - if (!filter_cpu(sample)) { - perf_sample__fprintf_start(sample, thread, evsel, - PERF_RECORD_SWITCH, stdout); - perf_event__fprintf(event, stdout); +static int process_auxtrace_error(struct perf_session *session, + union perf_event *event) +{ + if (scripting_ops && scripting_ops->process_auxtrace_error) { + scripting_ops->process_auxtrace_error(session, event); + return 0; } - thread__put(thread); - return 0; + + return perf_event__process_auxtrace_error(session, event); } static int @@ -2378,22 +2623,18 @@ process_lost_event(struct perf_tool *tool, struct perf_sample *sample, struct machine *machine) { - struct perf_script *script = container_of(tool, struct perf_script, tool); - struct perf_session *session = script->session; - struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id); - struct thread *thread; - - thread = machine__findnew_thread(machine, sample->pid, - sample->tid); - if (thread == NULL) - return -1; + return print_event(tool, event, sample, machine, sample->pid, + sample->tid); +} - if (!filter_cpu(sample)) { - perf_sample__fprintf_start(sample, thread, evsel, - PERF_RECORD_LOST, stdout); - perf_event__fprintf(event, stdout); - } - thread__put(thread); +static int +process_throttle_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + if (scripting_ops && scripting_ops->process_throttle) + scripting_ops->process_throttle(event, sample, machine); return 0; } @@ -2403,7 +2644,7 @@ process_finished_round_event(struct perf_tool *tool __maybe_unused, struct ordered_events *oe __maybe_unused) { - perf_event__fprintf(event, stdout); + perf_event__fprintf(event, NULL, stdout); return 0; } @@ -2413,33 +2654,23 @@ process_bpf_events(struct perf_tool *tool __maybe_unused, struct perf_sample *sample, struct machine *machine) { - struct thread *thread; - struct perf_script *script = container_of(tool, struct perf_script, tool); - struct perf_session *session = script->session; - struct evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id); - if (machine__process_ksymbol(machine, event, sample) < 0) return -1; - if (!evsel->core.attr.sample_id_all) { - perf_event__fprintf(event, stdout); - return 0; - } + return print_event(tool, event, sample, machine, sample->pid, + sample->tid); +} - thread = machine__findnew_thread(machine, sample->pid, sample->tid); - if (thread == NULL) { - pr_debug("problem processing MMAP event, skipping it.\n"); +static int process_text_poke_events(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + if (perf_event__process_text_poke(tool, event, sample, machine) < 0) return -1; - } - - if (!filter_cpu(sample)) { - perf_sample__fprintf_start(sample, thread, evsel, - event->header.type, stdout); - perf_event__fprintf(event, stdout); - } - thread__put(thread); - return 0; + return print_event(tool, event, sample, machine, sample->pid, + sample->tid); } static void sig_handler(int sig __maybe_unused) @@ -2455,7 +2686,7 @@ static void perf_script__fclose_per_event_dump(struct perf_script *script) evlist__for_each_entry(evlist, evsel) { if (!evsel->priv) break; - perf_evsel_script__delete(evsel->priv); + evsel_script__delete(evsel->priv); evsel->priv = NULL; } } @@ -2468,14 +2699,14 @@ static int perf_script__fopen_per_event_dump(struct perf_script *script) /* * Already setup? I.e. we may be called twice in cases like * Intel PT, one for the intel_pt// and dummy events, then - * for the evsels syntheized from the auxtrace info. + * for the evsels synthesized from the auxtrace info. * * Ses perf_script__process_auxtrace_info. */ if (evsel->priv != NULL) continue; - evsel->priv = perf_evsel_script__new(evsel, script->session->data); + evsel->priv = evsel_script__new(evsel, script->session->data); if (evsel->priv == NULL) goto out_err_fclose; } @@ -2510,12 +2741,18 @@ static void perf_script__exit_per_event_dump_stats(struct perf_script *script) evlist__for_each_entry(script->session->evlist, evsel) { struct evsel_script *es = evsel->priv; - perf_evsel_script__fprintf(es, stdout); - perf_evsel_script__delete(es); + evsel_script__fprintf(es, stdout); + evsel_script__delete(es); evsel->priv = NULL; } } +static void perf_script__exit(struct perf_script *script) +{ + perf_thread_map__put(script->threads); + perf_cpu_map__put(script->cpus); +} + static int __cmd_script(struct perf_script *script) { int ret; @@ -2536,8 +2773,12 @@ static int __cmd_script(struct perf_script *script) } if (script->show_switch_events || (scripting_ops && scripting_ops->process_switch)) script->tool.context_switch = process_switch_event; + if (scripting_ops && scripting_ops->process_auxtrace_error) + script->tool.auxtrace_error = process_auxtrace_error; if (script->show_namespace_events) script->tool.namespaces = process_namespaces_event; + if (script->show_cgroup_events) + script->tool.cgroup = process_cgroup_event; if (script->show_lost_events) script->tool.lost = process_lost_event; if (script->show_round_events) { @@ -2548,6 +2789,10 @@ static int __cmd_script(struct perf_script *script) script->tool.ksymbol = process_bpf_events; script->tool.bpf = process_bpf_events; } + if (script->show_text_poke_events) { + script->tool.ksymbol = process_bpf_events; + script->tool.text_poke = process_text_poke_events; + } if (perf_script__setup_per_event_dump(script)) { pr_err("Couldn't create the per event dump files\n"); @@ -2568,7 +2813,7 @@ static int __cmd_script(struct perf_script *script) struct script_spec { struct list_head node; struct scripting_ops *ops; - char spec[0]; + char spec[]; }; static LIST_HEAD(script_specs); @@ -2641,6 +2886,37 @@ static void list_available_languages(void) fprintf(stderr, "\n"); } +/* Find script file relative to current directory or exec path */ +static char *find_script(const char *script) +{ + char path[PATH_MAX]; + + if (!scripting_ops) { + const char *ext = strrchr(script, '.'); + + if (!ext) + return NULL; + + scripting_ops = script_spec__lookup(++ext); + if (!scripting_ops) + return NULL; + } + + if (access(script, R_OK)) { + char *exec_path = get_argv_exec_path(); + + if (!exec_path) + return NULL; + snprintf(path, sizeof(path), "%s/scripts/%s/%s", + exec_path, scripting_ops->dirname, script); + free(exec_path); + script = path; + if (access(script, R_OK)) + return NULL; + } + return strdup(script); +} + static int parse_scriptname(const struct option *opt __maybe_unused, const char *str, int unset __maybe_unused) { @@ -2682,7 +2958,9 @@ static int parse_scriptname(const struct option *opt __maybe_unused, } } - script_name = strdup(script); + script_name = find_script(script); + if (!script_name) + script_name = strdup(script); return 0; } @@ -2785,7 +3063,7 @@ parse: break; } if (i == imax && strcmp(tok, "flags") == 0) { - print_flags = change == REMOVE ? false : true; + print_flags = change != REMOVE; continue; } if (i == imax) { @@ -2806,9 +3084,11 @@ parse: if (change == REMOVE) { output[j].fields &= ~all_output_options[i].field; output[j].user_set_fields &= ~all_output_options[i].field; + output[j].user_unset_fields |= all_output_options[i].field; } else { output[j].fields |= all_output_options[i].field; output[j].user_set_fields |= all_output_options[i].field; + output[j].user_unset_fields &= ~all_output_options[i].field; } output[j].user_set = true; output[j].wildcard_set = true; @@ -3050,6 +3330,34 @@ static int list_available_scripts(const struct option *opt __maybe_unused, exit(0); } +static int add_dlarg(const struct option *opt __maybe_unused, + const char *s, int unset __maybe_unused) +{ + char *arg = strdup(s); + void *a; + + if (!arg) + return -1; + + a = realloc(dlargv, sizeof(dlargv[0]) * (dlargc + 1)); + if (!a) { + free(arg); + return -1; + } + + dlargv = a; + dlargv[dlargc++] = arg; + + return 0; +} + +static void free_dlarg(void) +{ + while (dlargc--) + free(dlargv[dlargc]); + free(dlargv); +} + /* * Some scripts specify the required events in their "xxx-record" file, * this function will check if the events in perf.data match those @@ -3057,7 +3365,7 @@ static int list_available_scripts(const struct option *opt __maybe_unused, * * Fixme: All existing "xxx-record" are all in good formats "-e event ", * which is covered well now. And new parsing code should be added to - * cover the future complexing formats like event groups etc. + * cover the future complex formats like event groups etc. */ static int check_ev_match(char *dir_name, char *scriptname, struct perf_session *session) @@ -3094,7 +3402,7 @@ static int check_ev_match(char *dir_name, char *scriptname, match = 0; evlist__for_each_entry(session->evlist, pos) { - if (!strcmp(perf_evsel__name(pos), evname)) { + if (!strcmp(evsel__name(pos), evname)) { match = 1; break; } @@ -3132,7 +3440,7 @@ int find_scripts(char **scripts_array, char **scripts_path_array, int num, char *temp; int i = 0; - session = perf_session__new(&data, false, NULL); + session = perf_session__new(&data, NULL); if (IS_ERR(session)) return PTR_ERR(session); @@ -3214,10 +3522,10 @@ static char *get_script_path(const char *script_root, const char *suffix) __script_root = get_script_root(script_dirent, suffix); if (__script_root && !strcmp(script_root, __script_root)) { free(__script_root); - closedir(lang_dir); closedir(scripts_dir); scnprintf(script_path, MAXPATHLEN, "%s/%s", lang_path, script_dirent->d_name); + closedir(lang_dir); return strdup(script_path); } free(__script_root); @@ -3231,7 +3539,7 @@ static char *get_script_path(const char *script_root, const char *suffix) static bool is_top_script(const char *script_path) { - return ends_with(script_path, "top") == NULL ? false : true; + return ends_with(script_path, "top") != NULL; } static int has_required_arg(char *script_path) @@ -3279,17 +3587,14 @@ static int have_cmd(int argc, const char **argv) static void script__setup_sample_type(struct perf_script *script) { struct perf_session *session = script->session; - u64 sample_type = perf_evlist__combined_sample_type(session->evlist); - - if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) { - if ((sample_type & PERF_SAMPLE_REGS_USER) && - (sample_type & PERF_SAMPLE_STACK_USER)) { - callchain_param.record_mode = CALLCHAIN_DWARF; - dwarf_callchain_users = true; - } else if (sample_type & PERF_SAMPLE_BRANCH_STACK) - callchain_param.record_mode = CALLCHAIN_LBR; - else - callchain_param.record_mode = CALLCHAIN_FP; + u64 sample_type = evlist__combined_sample_type(session->evlist); + + callchain_param_setup(sample_type, perf_env__arch(session->machines.host.env)); + + if (script->stitch_lbr && (callchain_param.record_mode != CALLCHAIN_LBR)) { + pr_warning("Can't find LBR callchain. Switch off --stitch-lbr.\n" + "Please apply --call-graph lbr when recording.\n"); + script->stitch_lbr = false; } } @@ -3327,7 +3632,7 @@ static int set_maps(struct perf_script *script) perf_evlist__set_maps(&evlist->core, script->cpus, script->threads); - if (perf_evlist__alloc_stats(evlist, true)) + if (evlist__alloc_stats(evlist, true)) return -ENOMEM; script->allocated = true; @@ -3341,6 +3646,9 @@ int process_thread_map_event(struct perf_session *session, struct perf_tool *tool = session->tool; struct perf_script *script = container_of(tool, struct perf_script, tool); + if (dump_trace) + perf_event__fprintf_thread_map(event, stdout); + if (script->threads) { pr_warning("Extra thread map event, ignoring.\n"); return 0; @@ -3360,6 +3668,9 @@ int process_cpu_map_event(struct perf_session *session, struct perf_tool *tool = session->tool; struct perf_script *script = container_of(tool, struct perf_script, tool); + if (dump_trace) + perf_event__fprintf_cpu_map(event, stdout); + if (script->cpus) { pr_warning("Extra cpu map event, ignoring.\n"); return 0; @@ -3414,7 +3725,10 @@ static int parse_xed(const struct option *opt __maybe_unused, const char *str __maybe_unused, int unset __maybe_unused) { - force_pager("xed -F insn: -A -64 | less"); + if (isatty(1)) + force_pager("xed -F insn: -A -64 | less"); + else + force_pager("xed -F insn: -A -64"); return 0; } @@ -3445,6 +3759,7 @@ int cmd_script(int argc, const char **argv) bool header = false; bool header_only = false; bool script_started = false; + bool unsorted_dump = false; char *rec_script_path = NULL; char *rep_script_path = NULL; struct perf_session *session; @@ -3454,6 +3769,7 @@ int cmd_script(int argc, const char **argv) }; struct utsname uts; char *script_path = NULL; + const char *dlfilter_file = NULL; const char **__argv; int i, j, err = 0; struct perf_script script = { @@ -3463,6 +3779,7 @@ int cmd_script(int argc, const char **argv) .mmap2 = perf_event__process_mmap2, .comm = perf_event__process_comm, .namespaces = perf_event__process_namespaces, + .cgroup = perf_event__process_cgroup, .exit = perf_event__process_exit, .fork = perf_event__process_fork, .attr = process_attr, @@ -3479,6 +3796,8 @@ int cmd_script(int argc, const char **argv) .stat_config = process_stat_config_event, .thread_map = process_thread_map_event, .cpu_map = process_cpu_map_event, + .throttle = process_throttle_event, + .unthrottle = process_throttle_event, .ordered_events = true, .ordering_requires_timestamps = true, }, @@ -3489,17 +3808,24 @@ int cmd_script(int argc, const char **argv) const struct option options[] = { OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), + OPT_BOOLEAN(0, "dump-unsorted-raw-trace", &unsorted_dump, + "dump unsorted raw trace in ASCII"), OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('L', "Latency", &latency_format, "show latency attributes (irqs/preemption disabled, etc)"), OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts", list_available_scripts), + OPT_CALLBACK_NOOPT(0, "list-dlfilters", NULL, NULL, "list available dlfilters", + list_available_dlfilters), OPT_CALLBACK('s', "script", NULL, "name", "script file name (lang:script name, script name, or *)", parse_scriptname), OPT_STRING('g', "gen-script", &generate_script_lang, "lang", "generate perf-script.xx script in specified language"), + OPT_STRING(0, "dlfilter", &dlfilter_file, "file", "filter .so file name"), + OPT_CALLBACK(0, "dlarg", NULL, "argument", "filter argument", + add_dlarg), OPT_STRING('i', "input", &input_name, "file", "input file name"), OPT_BOOLEAN('d', "debug-mode", &debug_mode, "do various checks like samples ordering and lost events"), @@ -3520,19 +3846,25 @@ int cmd_script(int argc, const char **argv) "Valid types: hw,sw,trace,raw,synth. " "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso," "addr,symoff,srcline,period,iregs,uregs,brstack," - "brstacksym,flags,bpf-output,brstackinsn,brstackoff," - "callindent,insn,insnlen,synth,phys_addr,metric,misc,ipc", + "brstacksym,flags,data_src,weight,bpf-output,brstackinsn," + "brstackinsnlen,brstackoff,callindent,insn,insnlen,synth," + "phys_addr,metric,misc,srccode,ipc,tod,data_page_size," + "code_page_size,ins_lat", parse_output_fields), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), + OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", + "only consider symbols in these DSOs"), OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", "only consider these symbols"), + OPT_INTEGER(0, "addr-range", &symbol_conf.addr_range, + "Use with -S to list traced records within address range"), OPT_CALLBACK_OPTARG(0, "insn-trace", &itrace_synth_opts, NULL, NULL, "Decode instructions from itrace", parse_insn_trace), OPT_CALLBACK_OPTARG(0, "xed", NULL, NULL, NULL, "Run xed disassembler on output", parse_xed), OPT_CALLBACK_OPTARG(0, "call-trace", &itrace_synth_opts, NULL, NULL, - "Decode calls from from itrace", parse_call_trace), + "Decode calls from itrace", parse_call_trace), OPT_CALLBACK_OPTARG(0, "call-ret-trace", &itrace_synth_opts, NULL, NULL, "Decode calls and returns from itrace", parse_callret_trace), OPT_STRING(0, "graph-function", &symbol_conf.graph_function, "symbol[,symbol...]", @@ -3551,6 +3883,7 @@ int cmd_script(int argc, const char **argv) "anything beyond the specified depth will be ignored. " "Default: kernel.perf_event_max_stack or " __stringify(PERF_MAX_STACK_DEPTH)), OPT_BOOLEAN(0, "reltime", &reltime, "Show time stamps relative to start"), + OPT_BOOLEAN(0, "deltatime", &deltatime, "Show time stamps relative to previous event"), OPT_BOOLEAN('I', "show-info", &show_full_info, "display extended information from perf.data file"), OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path, @@ -3563,12 +3896,16 @@ int cmd_script(int argc, const char **argv) "Show context switch events (if recorded)"), OPT_BOOLEAN('\0', "show-namespace-events", &script.show_namespace_events, "Show namespace events (if recorded)"), + OPT_BOOLEAN('\0', "show-cgroup-events", &script.show_cgroup_events, + "Show cgroup events (if recorded)"), OPT_BOOLEAN('\0', "show-lost-events", &script.show_lost_events, "Show lost events (if recorded)"), OPT_BOOLEAN('\0', "show-round-events", &script.show_round_events, "Show round events (if recorded)"), OPT_BOOLEAN('\0', "show-bpf-events", &script.show_bpf_events, "Show bpf related events (if recorded)"), + OPT_BOOLEAN('\0', "show-text-poke-events", &script.show_text_poke_events, + "Show text poke related events (if recorded)"), OPT_BOOLEAN('\0', "per-event-dump", &script.per_event_dump, "Dump trace output to files named by the monitored events"), OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"), @@ -3598,6 +3935,10 @@ int cmd_script(int argc, const char **argv) "file", "file saving guest os /proc/kallsyms"), OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules, "file", "file saving guest os /proc/modules"), + OPT_BOOLEAN(0, "guest-code", &symbol_conf.guest_code, + "Guest code can be found in hypervisor process"), + OPT_BOOLEAN('\0', "stitch-lbr", &script.stitch_lbr, + "Enable LBR callgraph stitching approach"), OPTS_EVSWITCH(&script.evswitch), OPT_END() }; @@ -3621,7 +3962,8 @@ int cmd_script(int argc, const char **argv) if (symbol_conf.guestmount || symbol_conf.default_guest_vmlinux_name || symbol_conf.default_guest_kallsyms || - symbol_conf.default_guest_modules) { + symbol_conf.default_guest_modules || + symbol_conf.guest_code) { /* * Enable guest sample processing. */ @@ -3631,13 +3973,21 @@ int cmd_script(int argc, const char **argv) data.path = input_name; data.force = symbol_conf.force; - if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) { + if (unsorted_dump) { + dump_trace = true; + script.tool.ordered_events = false; + } + + if (symbol__validate_sym_arguments()) + return -1; + + if (argc > 1 && strlen(argv[0]) > 2 && strstarts("record", argv[0])) { rec_script_path = get_script_path(argv[1], RECORD_SUFFIX); if (!rec_script_path) return cmd_record(argc, argv); } - if (argc > 1 && !strncmp(argv[0], "rep", strlen("rep"))) { + if (argc > 1 && strlen(argv[0]) > 2 && strstarts("report", argv[0])) { rep_script_path = get_script_path(argv[1], REPORT_SUFFIX); if (!rep_script_path) { fprintf(stderr, @@ -3647,7 +3997,14 @@ int cmd_script(int argc, const char **argv) } } - if (itrace_synth_opts.callchain && + if (reltime && deltatime) { + fprintf(stderr, + "reltime and deltatime - the two don't get along well. " + "Please limit to --reltime or --deltatime.\n"); + return -1; + } + + if ((itrace_synth_opts.callchain || itrace_synth_opts.add_callchain) && itrace_synth_opts.callchain_sz > scripting_max_stack) scripting_max_stack = itrace_synth_opts.callchain_sz; @@ -3663,6 +4020,12 @@ int cmd_script(int argc, const char **argv) rep_script_path = get_script_path(argv[0], REPORT_SUFFIX); if (!rec_script_path && !rep_script_path) { + script_name = find_script(argv[0]); + if (script_name) { + argc -= 1; + argv += 1; + goto script_found; + } usage_with_options_msg(script_usage, options, "Couldn't find script `%s'\n\n See perf" " script -l for available scripts.\n", argv[0]); @@ -3755,7 +4118,7 @@ int cmd_script(int argc, const char **argv) free(__argv); exit(-1); } - +script_found: if (rec_script_path) script_path = rec_script_path; if (rep_script_path) @@ -3793,12 +4156,18 @@ int cmd_script(int argc, const char **argv) exit(-1); } + if (dlfilter_file) { + dlfilter = dlfilter__new(dlfilter_file, dlargc, dlargv); + if (!dlfilter) + return -1; + } + if (!script_name) { setup_pager(); use_browser = 0; } - session = perf_session__new(&data, false, &script.tool); + session = perf_session__new(&data, &script.tool); if (IS_ERR(session)) return PTR_ERR(session); @@ -3815,11 +4184,15 @@ int cmd_script(int argc, const char **argv) goto out_delete; uname(&uts); - if (data.is_pipe || /* assume pipe_mode indicates native_arch */ - !strcmp(uts.machine, session->header.env.arch) || - (!strcmp(uts.machine, "x86_64") && - !strcmp(session->header.env.arch, "i386"))) + if (data.is_pipe) { /* Assume pipe_mode indicates native_arch */ native_arch = true; + } else if (session->header.env.arch) { + if (!strcmp(uts.machine, session->header.env.arch)) + native_arch = true; + else if (!strcmp(uts.machine, "x86_64") && + !strcmp(session->header.env.arch, "i386")) + native_arch = true; + } script.session = session; script__setup_sample_type(&script); @@ -3892,8 +4265,12 @@ int cmd_script(int argc, const char **argv) goto out_delete; } + err = dlfilter__start(dlfilter, session); + if (err) + goto out_delete; + if (script_name) { - err = scripting_ops->start_script(script_name, argc, argv); + err = scripting_ops->start_script(script_name, argc, argv, session); if (err) goto out_delete; pr_debug("perf script started with script %s\n\n", script_name); @@ -3923,6 +4300,9 @@ int cmd_script(int argc, const char **argv) if (err) goto out_delete; + if (zstd_init(&(session->zstd_data), 0) < 0) + pr_warning("Decompression initialization failed. Reported data may be incomplete.\n"); + err = __cmd_script(&script); flush_scripting(); @@ -3933,11 +4313,15 @@ out_delete: zfree(&script.ptime_range); } - perf_evlist__free_stats(session->evlist); + zstd_fini(&(session->zstd_data)); + evlist__free_stats(session->evlist); perf_session__delete(session); + perf_script__exit(&script); if (script_started) cleanup_scripting(); + dlfilter__cleanup(dlfilter); + free_dlarg(); out: return err; } |