diff options
Diffstat (limited to 'tools/perf/builtin-script.c')
| -rw-r--r-- | tools/perf/builtin-script.c | 619 |
1 files changed, 497 insertions, 122 deletions
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 33667b534634..c8ac9f01a36b 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -33,15 +33,17 @@ #include "util/path.h" #include "util/event.h" #include "util/mem-info.h" +#include "util/metricgroup.h" #include "ui/ui.h" #include "print_binary.h" #include "print_insn.h" -#include "archinsn.h" #include <linux/bitmap.h> +#include <linux/compiler.h> #include <linux/kernel.h> #include <linux/stringify.h> #include <linux/time64.h> #include <linux/zalloc.h> +#include <linux/unaligned.h> #include <sys/utsname.h> #include "asm/bug.h" #include "util/mem-events.h" @@ -50,6 +52,7 @@ #include <errno.h> #include <inttypes.h> #include <signal.h> +#include <stdio.h> #include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> @@ -86,7 +89,6 @@ static bool print_flags; static const char *cpu_list; static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); static int max_blocks; -static bool native_arch; static struct dlfilter *dlfilter; static int dlargc; static char **dlargv; @@ -164,7 +166,7 @@ struct perf_script { int range_num; }; -struct output_option { +static struct output_option { const char *str; enum perf_output_field field; } all_output_options[] = { @@ -221,7 +223,7 @@ enum { OUTPUT_TYPE_MAX }; -// We need to refactor the evsel->priv use in in 'perf script' to allow for +// We need to refactor the evsel->priv use in 'perf script' to allow for // using that area, that is being used only in some cases. #define OUTPUT_TYPE_UNSET -1 @@ -338,16 +340,8 @@ struct evsel_script { char *filename; FILE *fp; u64 samples; - /* For metric output */ - u64 val; - int gnum; }; -static inline struct evsel_script *evsel_script(struct evsel *evsel) -{ - return (struct evsel_script *)evsel->priv; -} - static struct evsel_script *evsel_script__new(struct evsel *evsel, struct perf_data *data) { struct evsel_script *es = zalloc(sizeof(*es)); @@ -400,10 +394,20 @@ static inline int output_type(unsigned int type) static inline int evsel__output_type(struct evsel *evsel) { - if (evsel->script_output_type == OUTPUT_TYPE_UNSET) - evsel->script_output_type = output_type(evsel->core.attr.type); + int type = evsel->script_output_type; + + if (type == OUTPUT_TYPE_UNSET) { + type = output_type(evsel->core.attr.type); + if (type == OUTPUT_TYPE_OTHER) { + struct perf_pmu *pmu = evsel__find_pmu(evsel); + + if (pmu && pmu->is_core) + type = PERF_TYPE_RAW; + } + evsel->script_output_type = type; + } - return evsel->script_output_type; + return type; } static bool output_set_by_user(void) @@ -670,7 +674,7 @@ static int perf_session__check_output_opt(struct perf_session *session) evlist__for_each_entry(session->evlist, evsel) { not_pipe = true; - if (evsel__has_callchain(evsel)) { + if (evsel__has_callchain(evsel) || evsel__is_offcpu_event(evsel)) { use_callchain = true; break; } @@ -702,7 +706,7 @@ static int perf_session__check_output_opt(struct perf_session *session) } } - if (tod && !session->header.env.clock.enabled) { + if (tod && !perf_session__env(session)->clock.enabled) { pr_err("Can't provide 'tod' time, missing clock data. " "Please record with -k/--clockid option.\n"); return -1; @@ -711,7 +715,8 @@ out: return 0; } -static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, const char *arch, +static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, + uint16_t e_machine, uint32_t e_flags, FILE *fp) { unsigned i = 0, r; @@ -724,7 +729,9 @@ static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, cons 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, arch), val); + printed += fprintf(fp, "%5s:0x%"PRIx64" ", + perf_reg_name(r, e_machine, e_flags), + val); } return printed; @@ -747,7 +754,7 @@ tod_scnprintf(struct perf_script *script, char *buf, int buflen, if (buflen < 64 || !script) return buf; - env = &script->session->header.env; + env = perf_session__env(script->session); if (!env->clock.enabled) { scnprintf(buf, buflen, "disabled"); return buf; @@ -781,17 +788,29 @@ tod_scnprintf(struct perf_script *script, char *buf, int buflen, } static int perf_sample__fprintf_iregs(struct perf_sample *sample, - struct perf_event_attr *attr, const char *arch, FILE *fp) + struct perf_event_attr *attr, + uint16_t e_machine, + uint32_t e_flags, + FILE *fp) { - return perf_sample__fprintf_regs(&sample->intr_regs, - attr->sample_regs_intr, arch, fp); + if (!sample->intr_regs) + return 0; + + return perf_sample__fprintf_regs(perf_sample__intr_regs(sample), + attr->sample_regs_intr, e_machine, e_flags, fp); } static int perf_sample__fprintf_uregs(struct perf_sample *sample, - struct perf_event_attr *attr, const char *arch, FILE *fp) + struct perf_event_attr *attr, + uint16_t e_machine, + uint32_t e_flags, + FILE *fp) { - return perf_sample__fprintf_regs(&sample->user_regs, - attr->sample_regs_user, arch, fp); + if (!sample->user_regs) + return 0; + + return perf_sample__fprintf_regs(perf_sample__user_regs(sample), + attr->sample_regs_user, e_machine, e_flags, fp); } static int perf_sample__fprintf_start(struct perf_script *script, @@ -929,19 +948,25 @@ static int perf_sample__fprintf_start(struct perf_script *script, return printed; } -static inline char -mispred_str(struct branch_entry *br) +static inline size_t +bstack_event_str(struct branch_entry *br, char *buf, size_t sz) { - if (!(br->flags.mispred || br->flags.predicted)) - return '-'; + if (!(br->flags.mispred || br->flags.predicted || br->flags.not_taken)) + return snprintf(buf, sz, "-"); - return br->flags.predicted ? 'P' : 'M'; + return snprintf(buf, sz, "%s%s", + br->flags.predicted ? "P" : "M", + br->flags.not_taken ? "N" : ""); } static int print_bstack_flags(FILE *fp, struct branch_entry *br) { - return fprintf(fp, "/%c/%c/%c/%d/%s/%s ", - mispred_str(br), + char events[16] = { 0 }; + size_t pos; + + pos = bstack_event_str(br, events, sizeof(events)); + return fprintf(fp, "/%s/%c/%c/%d/%s/%s ", + pos < 0 ? "-" : events, br->flags.in_tx ? 'X' : '-', br->flags.abort ? 'A' : '-', br->flags.cycles, @@ -1200,7 +1225,6 @@ static int any_dump_insn(struct evsel *evsel __maybe_unused, u8 *inbuf, int inlen, int *lenp, FILE *fp) { -#ifdef HAVE_LIBCAPSTONE_SUPPORT if (PRINT_FIELD(BRSTACKDISASM)) { int printed = fprintf_insn_asm(x->machine, x->thread, x->cpumode, x->is64bit, (uint8_t *)inbuf, inlen, ip, lenp, @@ -1209,7 +1233,6 @@ static int any_dump_insn(struct evsel *evsel __maybe_unused, if (printed > 0) return printed; } -#endif return fprintf(fp, "%s", dump_insn(x, ip, inbuf, inlen, lenp)); } @@ -1248,11 +1271,11 @@ static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, if (PRINT_FIELD(BRCNTR)) { struct evsel *pos = evsel__leader(evsel); - unsigned int i = 0, j, num, mask, width; + unsigned int i = 0, j, num, mask, width, numprinted = 0; perf_env__find_br_cntr_info(evsel__env(evsel), NULL, &width); mask = (1L << width) - 1; - printed += fprintf(fp, "br_cntr: "); + printed += fprintf(fp, "\t# br_cntr: "); evlist__for_each_entry_from(evsel->evlist, pos) { if (!(pos->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS)) continue; @@ -1260,16 +1283,20 @@ static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, break; num = (br_cntr >> (i++ * width)) & mask; + numprinted += num; if (!verbose) { for (j = 0; j < num; j++) printed += fprintf(fp, "%s", pos->abbr_name); } else printed += fprintf(fp, "%s %d ", pos->name, num); } - printed += fprintf(fp, "\t"); + if (numprinted == 0 && !verbose) + printed += fprintf(fp, "-"); + printed += fprintf(fp, " "); } - printed += fprintf(fp, "#%s%s%s%s", + printed += fprintf(fp, "%s%s%s%s%s", + !PRINT_FIELD(BRCNTR) ? "#" : "", en->flags.predicted ? " PRED" : "", en->flags.mispred ? " MISPRED" : "", en->flags.in_tx ? " INTX" : "", @@ -1602,7 +1629,7 @@ static int perf_sample__fprintf_insn(struct perf_sample *sample, { int printed = 0; - script_fetch_insn(sample, thread, machine, native_arch); + perf_sample__fetch_insn(sample, thread, machine); if (PRINT_FIELD(INSNLEN)) printed += fprintf(fp, " ilen: %d", sample->insn_len); @@ -1703,9 +1730,14 @@ static int perf_sample__fprintf_bts(struct perf_sample *sample, static int perf_sample__fprintf_flags(u32 flags, FILE *fp) { char str[SAMPLE_FLAGS_BUF_SIZE]; + int ret; - perf_sample__sprintf_flags(flags, str, sizeof(str)); - return fprintf(fp, " %-21s ", str); + ret = perf_sample__sprintf_flags(flags, str, sizeof(str)); + if (ret < 0) + return fprintf(fp, " raw flags:0x%-*x ", + SAMPLE_FLAGS_STR_ALIGNED_SIZE - 12, flags); + + return fprintf(fp, " %-*s ", SAMPLE_FLAGS_STR_ALIGNED_SIZE, str); } struct printer_data { @@ -1974,6 +2006,25 @@ static int perf_sample__fprintf_synth_iflag_chg(struct perf_sample *sample, FILE return len + perf_sample__fprintf_pt_spacing(len, fp); } +static int perf_sample__fprintf_synth_vpadtl(struct perf_sample *data, FILE *fp) +{ + struct powerpc_vpadtl_entry *dtl = (struct powerpc_vpadtl_entry *)data->raw_data; + int len; + + len = fprintf(fp, "timebase: %" PRIu64 " dispatch_reason:%s, preempt_reason:%s,\n" + "enqueue_to_dispatch_time:%d, ready_to_enqueue_time:%d," + "waiting_to_ready_time:%d, processor_id: %d", + get_unaligned_be64(&dtl->timebase), + dispatch_reasons[dtl->dispatch_reason], + preempt_reasons[dtl->preempt_reason], + be32_to_cpu(dtl->enqueue_to_dispatch_time), + be32_to_cpu(dtl->ready_to_enqueue_time), + be32_to_cpu(dtl->waiting_to_ready_time), + be16_to_cpu(dtl->processor_id)); + + return len; +} + static int perf_sample__fprintf_synth(struct perf_sample *sample, struct evsel *evsel, FILE *fp) { @@ -1996,6 +2047,8 @@ static int perf_sample__fprintf_synth(struct perf_sample *sample, return perf_sample__fprintf_synth_evt(sample, fp); case PERF_SYNTH_INTEL_IFLAG_CHG: return perf_sample__fprintf_synth_iflag_chg(sample, fp); + case PERF_SYNTH_POWERPC_VPA_DTL: + return perf_sample__fprintf_synth_vpadtl(sample, fp); default: break; } @@ -2075,13 +2128,161 @@ static void script_new_line(struct perf_stat_config *config __maybe_unused, fputs("\tmetric: ", mctx->fp); } -static void perf_sample__fprint_metric(struct perf_script *script, - struct thread *thread, +struct script_find_metrics_args { + struct evlist *evlist; + bool system_wide; +}; + +static struct evsel *map_metric_evsel_to_script_evsel(struct evlist *script_evlist, + struct evsel *metric_evsel) +{ + struct evsel *script_evsel; + + evlist__for_each_entry(script_evlist, script_evsel) { + /* Skip if perf_event_attr differ. */ + if (metric_evsel->core.attr.type != script_evsel->core.attr.type) + continue; + if (metric_evsel->core.attr.config != script_evsel->core.attr.config) + continue; + /* Skip if the script event has a metric_id that doesn't match. */ + if (script_evsel->metric_id && + strcmp(evsel__metric_id(metric_evsel), evsel__metric_id(script_evsel))) { + pr_debug("Skipping matching evsel due to differing metric ids '%s' vs '%s'\n", + evsel__metric_id(metric_evsel), evsel__metric_id(script_evsel)); + continue; + } + return script_evsel; + } + return NULL; +} + +static int script_find_metrics(const struct pmu_metric *pm, + const struct pmu_metrics_table *table __maybe_unused, + void *data) +{ + struct script_find_metrics_args *args = data; + struct evlist *script_evlist = args->evlist; + struct evlist *metric_evlist = evlist__new(); + struct evsel *metric_evsel; + int ret = metricgroup__parse_groups(metric_evlist, + /*pmu=*/"all", + pm->metric_name, + /*metric_no_group=*/false, + /*metric_no_merge=*/false, + /*metric_no_threshold=*/true, + /*user_requested_cpu_list=*/NULL, + args->system_wide, + /*hardware_aware_grouping=*/false); + + if (ret) { + /* Metric parsing failed but continue the search. */ + goto out; + } + + /* + * Check the script_evlist has an entry for each metric_evlist entry. If + * the script evsel was already set up avoid changing data that may + * break it. + */ + evlist__for_each_entry(metric_evlist, metric_evsel) { + struct evsel *script_evsel = + map_metric_evsel_to_script_evsel(script_evlist, metric_evsel); + struct evsel *new_metric_leader; + + if (!script_evsel) { + pr_debug("Skipping metric '%s' as evsel '%s' / '%s' is missing\n", + pm->metric_name, evsel__name(metric_evsel), + evsel__metric_id(metric_evsel)); + goto out; + } + + if (script_evsel->metric_leader == NULL) + continue; + + if (metric_evsel->metric_leader == metric_evsel) { + new_metric_leader = script_evsel; + } else { + new_metric_leader = + map_metric_evsel_to_script_evsel(script_evlist, + metric_evsel->metric_leader); + } + /* Mismatching evsel leaders. */ + if (script_evsel->metric_leader != new_metric_leader) { + pr_debug("Skipping metric '%s' due to mismatching evsel metric leaders '%s' vs '%s'\n", + pm->metric_name, evsel__metric_id(metric_evsel), + evsel__metric_id(script_evsel)); + goto out; + } + } + /* + * Metric events match those in the script evlist, copy metric evsel + * data into the script evlist. + */ + evlist__for_each_entry(metric_evlist, metric_evsel) { + struct evsel *script_evsel = + map_metric_evsel_to_script_evsel(script_evlist, metric_evsel); + struct metric_event *metric_me = metricgroup__lookup(&metric_evlist->metric_events, + metric_evsel, + /*create=*/false); + + if (script_evsel->metric_id == NULL) { + script_evsel->metric_id = metric_evsel->metric_id; + metric_evsel->metric_id = NULL; + } + + if (script_evsel->metric_leader == NULL) { + if (metric_evsel->metric_leader == metric_evsel) { + script_evsel->metric_leader = script_evsel; + } else { + script_evsel->metric_leader = + map_metric_evsel_to_script_evsel(script_evlist, + metric_evsel->metric_leader); + } + } + + if (metric_me) { + struct metric_expr *expr; + struct metric_event *script_me = + metricgroup__lookup(&script_evlist->metric_events, + script_evsel, + /*create=*/true); + + if (!script_me) { + /* + * As the metric_expr is created, the only + * failure is a lack of memory. + */ + goto out; + } + list_splice_init(&metric_me->head, &script_me->head); + list_for_each_entry(expr, &script_me->head, nd) { + for (int i = 0; expr->metric_events[i]; i++) { + expr->metric_events[i] = + map_metric_evsel_to_script_evsel(script_evlist, + expr->metric_events[i]); + } + } + } + } + pr_debug("Found metric '%s' whose evsels match those of in the perf data\n", + pm->metric_name); + evlist__delete(metric_evlist); +out: + return 0; +} + +static struct aggr_cpu_id script_aggr_cpu_id_get(struct perf_stat_config *config __maybe_unused, + struct perf_cpu cpu) +{ + return aggr_cpu_id__global(cpu, /*data=*/NULL); +} + +static void perf_sample__fprint_metric(struct thread *thread, struct evsel *evsel, struct perf_sample *sample, FILE *fp) { - struct evsel *leader = evsel__leader(evsel); + static bool init_metrics; struct perf_stat_output_ctx ctx = { .print_metric = script_print_metric, .new_line = script_new_line, @@ -2093,24 +2294,84 @@ static void perf_sample__fprint_metric(struct perf_script *script, }, .force_header = false, }; - struct evsel *ev2; - u64 val; + struct perf_counts_values *count, *old_count; + int cpu_map_idx, thread_map_idx, aggr_idx; + struct evsel *pos; + + if (!init_metrics) { + /* One time initialization of stat_config and metric data. */ + struct script_find_metrics_args args = { + .evlist = evsel->evlist, + .system_wide = perf_thread_map__pid(evsel->core.threads, /*idx=*/0) == -1, + + }; + if (!stat_config.output) + stat_config.output = stdout; + + if (!stat_config.aggr_map) { + /* TODO: currently only global aggregation is supported. */ + assert(stat_config.aggr_mode == AGGR_GLOBAL); + stat_config.aggr_get_id = script_aggr_cpu_id_get; + stat_config.aggr_map = + cpu_aggr_map__new(evsel->evlist->core.user_requested_cpus, + aggr_cpu_id__global, /*data=*/NULL, + /*needs_sort=*/false); + } - if (!evsel->stats) - evlist__alloc_stats(&stat_config, script->session->evlist, /*alloc_raw=*/false); - if (evsel_script(leader)->gnum++ == 0) - perf_stat__reset_shadow_stats(); - val = sample->period * evsel->scale; - evsel_script(evsel)->val = val; - 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, - &ctx, - NULL); + metricgroup__for_each_metric(pmu_metrics_table__find(), script_find_metrics, &args); + init_metrics = true; + } + + if (!evsel->stats) { + if (evlist__alloc_stats(&stat_config, evsel->evlist, /*alloc_raw=*/true) < 0) + return; + } + if (!evsel->stats->aggr) { + if (evlist__alloc_aggr_stats(evsel->evlist, stat_config.aggr_map->nr) < 0) + return; + } + + /* Update the evsel's count using the sample's data. */ + cpu_map_idx = perf_cpu_map__idx(evsel->core.cpus, (struct perf_cpu){sample->cpu}); + if (cpu_map_idx < 0) { + /* Missing CPU, check for any CPU. */ + if (perf_cpu_map__cpu(evsel->core.cpus, /*idx=*/0).cpu == -1 || + sample->cpu == (u32)-1) { + /* Place the counts in the which ever CPU is first in the map. */ + cpu_map_idx = 0; + } else { + pr_info("Missing CPU map entry for CPU %d\n", sample->cpu); + return; + } + } + thread_map_idx = perf_thread_map__idx(evsel->core.threads, sample->tid); + if (thread_map_idx < 0) { + /* Missing thread, check for any thread. */ + if (perf_thread_map__pid(evsel->core.threads, /*idx=*/0) == -1 || + sample->tid == (u32)-1) { + /* Place the counts in the which ever thread is first in the map. */ + thread_map_idx = 0; + } else { + pr_info("Missing thread map entry for thread %d\n", sample->tid); + return; + } + } + count = perf_counts(evsel->counts, cpu_map_idx, thread_map_idx); + old_count = perf_counts(evsel->prev_raw_counts, cpu_map_idx, thread_map_idx); + count->val = old_count->val + sample->period; + count->run = old_count->run + 1; + count->ena = old_count->ena + 1; + + /* Update the aggregated stats. */ + perf_stat_process_counter(&stat_config, evsel); + + /* Display all metrics. */ + evlist__for_each_entry(evsel->evlist, pos) { + cpu_aggr_map__for_each_idx(aggr_idx, stat_config.aggr_map) { + perf_stat__print_shadow_stats(&stat_config, pos, + aggr_idx, + &ctx); } - evsel_script(leader)->gnum = 0; } } @@ -2168,7 +2429,7 @@ static void process_event(struct perf_script *script, struct evsel_script *es = evsel->priv; FILE *fp = es->fp; char str[PAGE_SIZE_NAME_LEN]; - const char *arch = perf_env__arch(machine->env); + uint32_t e_flags; if (output[type].fields == 0) return; @@ -2224,7 +2485,7 @@ static void process_event(struct perf_script *script, fprintf(fp, "%16" PRIu16, sample->ins_lat); if (PRINT_FIELD(RETIRE_LAT)) - fprintf(fp, "%16" PRIu16, sample->retire_lat); + fprintf(fp, "%16" PRIu16, sample->weight3); if (PRINT_FIELD(CGROUP)) { const char *cgrp_name; @@ -2255,11 +2516,19 @@ static void process_event(struct perf_script *script, symbol_conf.bt_stop_list, fp); } - if (PRINT_FIELD(IREGS)) - perf_sample__fprintf_iregs(sample, attr, arch, fp); + if (PRINT_FIELD(IREGS)) { + perf_sample__fprintf_iregs(sample, attr, + thread__e_machine(thread, machine, &e_flags), + e_flags, + fp); + } - if (PRINT_FIELD(UREGS)) - perf_sample__fprintf_uregs(sample, attr, arch, fp); + if (PRINT_FIELD(UREGS)) { + perf_sample__fprintf_uregs(sample, attr, + thread__e_machine(thread, machine, &e_flags), + e_flags, + fp); + } if (PRINT_FIELD(BRSTACK)) perf_sample__fprintf_brstack(sample, thread, evsel, fp); @@ -2268,7 +2537,7 @@ static void process_event(struct perf_script *script, else if (PRINT_FIELD(BRSTACKOFF)) perf_sample__fprintf_brstackoff(sample, thread, evsel, fp); - if (evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT)) + if (evsel__is_bpf_output(evsel) && !evsel__is_offcpu_event(evsel) && PRINT_FIELD(BPF_OUTPUT)) perf_sample__fprintf_bpf_output(sample, fp); perf_sample__fprintf_insn(sample, evsel, attr, thread, machine, fp, al); @@ -2292,7 +2561,7 @@ static void process_event(struct perf_script *script, } if (PRINT_FIELD(METRIC)) - perf_sample__fprint_metric(script, thread, evsel, sample, fp); + perf_sample__fprint_metric(thread, evsel, sample, fp); if (verbose > 0) fflush(fp); @@ -2303,7 +2572,6 @@ 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 idx, thread; struct perf_cpu cpu; static int header_printed; @@ -2313,7 +2581,9 @@ static void __process_stat(struct evsel *counter, u64 tstamp) header_printed = 1; } - for (thread = 0; thread < nthreads; thread++) { + for (int thread = 0; thread < nthreads; thread++) { + unsigned int idx; + perf_cpu_map__for_each_cpu(cpu, idx, evsel__cpus(counter)) { struct perf_counts_values *counts; @@ -2456,6 +2726,94 @@ out_put: return ret; } +static int process_deferred_sample_event(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct evsel *evsel, + struct machine *machine) +{ + struct perf_script *scr = container_of(tool, struct perf_script, tool); + struct perf_event_attr *attr = &evsel->core.attr; + struct evsel_script *es = evsel->priv; + unsigned int type = output_type(attr->type); + struct addr_location al; + FILE *fp = es->fp; + int ret = 0; + + if (output[type].fields == 0) + return 0; + + /* Set thread to NULL to indicate addr_al and al are not initialized */ + addr_location__init(&al); + + if (perf_time__ranges_skip_sample(scr->ptime_range, scr->range_num, + sample->time)) { + goto out_put; + } + + if (debug_mode) { + if (sample->time < last_timestamp) { + pr_err("Samples misordered, previous: %" PRIu64 + " this: %" PRIu64 "\n", last_timestamp, + sample->time); + nr_unordered++; + } + last_timestamp = sample->time; + goto out_put; + } + + if (filter_cpu(sample)) + goto out_put; + + if (machine__resolve(machine, &al, sample) < 0) { + pr_err("problem processing %d event, skipping it.\n", + event->header.type); + ret = -1; + goto out_put; + } + + if (al.filtered) + goto out_put; + + if (!show_event(sample, evsel, al.thread, &al, NULL)) + goto out_put; + + if (evswitch__discard(&scr->evswitch, evsel)) + goto out_put; + + perf_sample__fprintf_start(scr, sample, al.thread, evsel, + PERF_RECORD_CALLCHAIN_DEFERRED, fp); + fprintf(fp, "DEFERRED CALLCHAIN [cookie: %llx]", + (unsigned long long)event->callchain_deferred.cookie); + + if (PRINT_FIELD(IP)) { + struct callchain_cursor *cursor = NULL; + + if (symbol_conf.use_callchain && sample->callchain) { + cursor = get_tls_callchain_cursor(); + if (thread__resolve_callchain(al.thread, cursor, evsel, + sample, NULL, NULL, + scripting_max_stack)) { + pr_info("cannot resolve deferred callchains\n"); + cursor = NULL; + } + } + + fputc(cursor ? '\n' : ' ', fp); + sample__fprintf_sym(sample, &al, 0, output[type].print_ip_opts, + cursor, symbol_conf.bt_stop_list, fp); + } + + fprintf(fp, "\n"); + + if (verbose > 0) + fflush(fp); + +out_put: + addr_location__exit(&al); + return ret; +} + // Used when scr->per_event_dump is not set static struct evsel_script es_stdout; @@ -2465,6 +2823,7 @@ static int process_attr(const 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; + uint16_t e_machine; u64 sample_type; int err; @@ -2506,7 +2865,8 @@ static int process_attr(const struct perf_tool *tool, union perf_event *event, * on events sample_type. */ sample_type = evlist__combined_sample_type(evlist); - callchain_param_setup(sample_type, perf_env__arch((*pevlist)->env)); + e_machine = perf_session__e_machine(evsel__session(evsel), /*e_flags=*/NULL); + callchain_param_setup(sample_type, e_machine); /* Enable fields for callchain entries */ if (symbol_conf.use_callchain && @@ -2550,8 +2910,12 @@ static int print_event_with_time(const struct perf_tool *tool, thread = machine__findnew_thread(machine, pid, tid); if (evsel) { + struct evsel *saved_evsel = sample->evsel; + + sample->evsel = evsel; perf_sample__fprintf_start(script, sample, thread, evsel, event->header.type, stdout); + sample->evsel = saved_evsel; } perf_event__fprintf(event, machine, stdout); @@ -2673,7 +3037,8 @@ static int process_switch_event(const struct perf_tool *tool, sample->tid); } -static int process_auxtrace_error(struct perf_session *session, +static int process_auxtrace_error(const struct perf_tool *tool, + struct perf_session *session, union perf_event *event) { if (scripting_ops && scripting_ops->process_auxtrace_error) { @@ -2681,7 +3046,7 @@ static int process_auxtrace_error(struct perf_session *session, return 0; } - return perf_event__process_auxtrace_error(session, event); + return perf_event__process_auxtrace_error(tool, session, event); } static int @@ -2728,6 +3093,15 @@ process_bpf_events(const struct perf_tool *tool __maybe_unused, sample->tid); } +static int +process_bpf_metadata_event(const struct perf_tool *tool __maybe_unused, + struct perf_session *session __maybe_unused, + union perf_event *event) +{ + perf_event__fprintf(event, NULL, stdout); + return 0; +} + static int process_text_poke_events(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, @@ -2850,8 +3224,9 @@ static int __cmd_script(struct perf_script *script) script->tool.finished_round = process_finished_round_event; } if (script->show_bpf_events) { - script->tool.ksymbol = process_bpf_events; - script->tool.bpf = process_bpf_events; + script->tool.ksymbol = process_bpf_events; + script->tool.bpf = process_bpf_events; + script->tool.bpf_metadata = process_bpf_metadata_event; } if (script->show_text_poke_events) { script->tool.ksymbol = process_bpf_events; @@ -3448,7 +3823,7 @@ out: static int have_cmd(int argc, const char **argv) { - char **__argv = malloc(sizeof(const char *) * argc); + char **__argv = calloc(argc, sizeof(const char *)); if (!__argv) { pr_err("malloc failed\n"); @@ -3470,7 +3845,7 @@ static void script__setup_sample_type(struct perf_script *script) struct perf_session *session = script->session; u64 sample_type = evlist__combined_sample_type(session->evlist); - callchain_param_setup(sample_type, perf_env__arch(session->machines.host.env)); + callchain_param_setup(sample_type, perf_session__e_machine(session, /*e_flags=*/NULL)); if (script->stitch_lbr && (callchain_param.record_mode != CALLCHAIN_LBR)) { pr_warning("Can't find LBR callchain. Switch off --stitch-lbr.\n" @@ -3479,7 +3854,8 @@ static void script__setup_sample_type(struct perf_script *script) } } -static int process_stat_round_event(struct perf_session *session, +static int process_stat_round_event(const struct perf_tool *tool __maybe_unused, + struct perf_session *session, union perf_event *event) { struct perf_record_stat_round *round = &event->stat_round; @@ -3494,7 +3870,8 @@ static int process_stat_round_event(struct perf_session *session, return 0; } -static int process_stat_config_event(struct perf_session *session __maybe_unused, +static int process_stat_config_event(const struct perf_tool *tool __maybe_unused, + struct perf_session *session __maybe_unused, union perf_event *event) { perf_event__read_stat_config(&stat_config, &event->stat_config); @@ -3528,10 +3905,10 @@ static int set_maps(struct perf_script *script) } static -int process_thread_map_event(struct perf_session *session, +int process_thread_map_event(const struct perf_tool *tool, + struct perf_session *session __maybe_unused, union perf_event *event) { - const struct perf_tool *tool = session->tool; struct perf_script *script = container_of(tool, struct perf_script, tool); if (dump_trace) @@ -3550,10 +3927,10 @@ int process_thread_map_event(struct perf_session *session, } static -int process_cpu_map_event(struct perf_session *session, +int process_cpu_map_event(const struct perf_tool *tool, + struct perf_session *session __maybe_unused, union perf_event *event) { - const struct perf_tool *tool = session->tool; struct perf_script *script = container_of(tool, struct perf_script, tool); if (dump_trace) @@ -3571,22 +3948,13 @@ int process_cpu_map_event(struct perf_session *session, return set_maps(script); } -static int process_feature_event(struct perf_session *session, - union perf_event *event) -{ - if (event->feat.feat_id < HEADER_LAST_FEATURE) - return perf_event__process_feature(session, event); - return 0; -} - -#ifdef HAVE_AUXTRACE_SUPPORT -static int perf_script__process_auxtrace_info(struct perf_session *session, +static int perf_script__process_auxtrace_info(const struct perf_tool *tool, + struct perf_session *session, union perf_event *event) { - int ret = perf_event__process_auxtrace_info(session, event); + int ret = perf_event__process_auxtrace_info(tool, session, event); if (ret == 0) { - const struct perf_tool *tool = session->tool; struct perf_script *script = container_of(tool, struct perf_script, tool); ret = perf_script__setup_per_event_dump(script); @@ -3594,9 +3962,6 @@ static int perf_script__process_auxtrace_info(struct perf_session *session, return ret; } -#else -#define perf_script__process_auxtrace_info 0 -#endif static int parse_insn_trace(const struct option *opt __maybe_unused, const char *str, int unset __maybe_unused) @@ -3661,6 +4026,7 @@ int cmd_script(int argc, const char **argv) bool header_only = false; bool script_started = false; bool unsorted_dump = false; + bool merge_deferred_callchains = true; char *rec_script_path = NULL; char *rep_script_path = NULL; struct perf_session *session; @@ -3668,7 +4034,6 @@ int cmd_script(int argc, const char **argv) .set = false, .default_no_sample = true, }; - struct utsname uts; char *script_path = NULL; const char *dlfilter_file = NULL; const char **__argv; @@ -3709,8 +4074,7 @@ int cmd_script(int argc, const char **argv) "file", "kallsyms pathname"), OPT_BOOLEAN('G', "hide-call-graph", &no_callchain, "When printing symbols do not display call chain"), - OPT_CALLBACK(0, "symfs", NULL, "directory", - "Look for files with symbols relative to this directory", + OPT_CALLBACK(0, "symfs", NULL, "directory[,layout]", SYMFS_HELP, symbol__config_symfs), OPT_CALLBACK('F', "fields", NULL, "str", "comma separated output fields prepend with 'type:'. " @@ -3814,6 +4178,8 @@ int cmd_script(int argc, const char **argv) "Guest code can be found in hypervisor process"), OPT_BOOLEAN('\0', "stitch-lbr", &script.stitch_lbr, "Enable LBR callgraph stitching approach"), + OPT_BOOLEAN('\0', "merge-callchains", &merge_deferred_callchains, + "Enable merge deferred user callchains"), OPTS_EVSWITCH(&script.evswitch), OPT_END() }; @@ -3826,6 +4192,7 @@ int cmd_script(int argc, const char **argv) "perf script [<options>] <top-script> [script-args]", NULL }; + struct perf_env *env; perf_set_singlethreaded(); @@ -3945,7 +4312,7 @@ int cmd_script(int argc, const char **argv) } } - __argv = malloc((argc + 6) * sizeof(const char *)); + __argv = calloc(argc + 6, sizeof(const char *)); if (!__argv) { pr_err("malloc failed\n"); err = -ENOMEM; @@ -3971,7 +4338,7 @@ int cmd_script(int argc, const char **argv) dup2(live_pipe[0], 0); close(live_pipe[1]); - __argv = malloc((argc + 4) * sizeof(const char *)); + __argv = calloc(argc + 4, sizeof(const char *)); if (!__argv) { pr_err("malloc failed\n"); err = -ENOMEM; @@ -4009,7 +4376,7 @@ script_found: } } - __argv = malloc((argc + 2) * sizeof(const char *)); + __argv = calloc(argc + 2, sizeof(const char *)); if (!__argv) { pr_err("malloc failed\n"); err = -ENOMEM; @@ -4042,6 +4409,7 @@ script_found: perf_tool__init(&script.tool, !unsorted_dump); script.tool.sample = process_sample_event; + script.tool.callchain_deferred = process_deferred_sample_event; script.tool.mmap = perf_event__process_mmap; script.tool.mmap2 = perf_event__process_mmap2; script.tool.comm = perf_event__process_comm; @@ -4054,7 +4422,7 @@ script_found: #ifdef HAVE_LIBTRACEEVENT script.tool.tracing_data = perf_event__process_tracing_data; #endif - script.tool.feature = process_feature_event; + script.tool.feature = perf_event__process_feature; script.tool.build_id = perf_event__process_build_id; script.tool.id_index = perf_event__process_id_index; script.tool.auxtrace_info = perf_script__process_auxtrace_info; @@ -4068,10 +4436,12 @@ script_found: script.tool.throttle = process_throttle_event; script.tool.unthrottle = process_throttle_event; script.tool.ordering_requires_timestamps = true; + script.tool.merge_deferred_callchains = merge_deferred_callchains; session = perf_session__new(&data, &script.tool); if (IS_ERR(session)) return PTR_ERR(session); + env = perf_session__env(session); if (header || header_only) { script.tool.show_feat_hdr = SHOW_FEAT_HEADER; perf_session__fprintf_info(session, stdout, show_full_info); @@ -4081,20 +4451,9 @@ script_found: if (show_full_info) script.tool.show_feat_hdr = SHOW_FEAT_HEADER_FULL_INFO; - if (symbol__init(&session->header.env) < 0) + if (symbol__init(env) < 0) goto out_delete; - uname(&uts); - 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); @@ -4129,6 +4488,7 @@ script_found: if (generate_script_lang) { struct stat perf_stat; int input; + char *filename = strdup("perf-script"); if (output_set_by_user()) { fprintf(stderr, @@ -4156,17 +4516,32 @@ script_found: } scripting_ops = script_spec__lookup(generate_script_lang); + if (!scripting_ops && ends_with(generate_script_lang, ".py")) { + scripting_ops = script_spec__lookup("python"); + free(filename); + filename = strdup(generate_script_lang); + filename[strlen(filename) - 3] = '\0'; + } else if (!scripting_ops && ends_with(generate_script_lang, ".pl")) { + scripting_ops = script_spec__lookup("perl"); + free(filename); + filename = strdup(generate_script_lang); + filename[strlen(filename) - 3] = '\0'; + } if (!scripting_ops) { - fprintf(stderr, "invalid language specifier"); + fprintf(stderr, "invalid language specifier '%s'\n", generate_script_lang); err = -ENOENT; goto out_delete; } + if (!filename) { + err = -ENOMEM; + goto out_delete; + } #ifdef HAVE_LIBTRACEEVENT - err = scripting_ops->generate_script(session->tevent.pevent, - "perf-script"); + err = scripting_ops->generate_script(session->tevent.pevent, filename); #else - err = scripting_ops->generate_script(NULL, "perf-script"); + err = scripting_ops->generate_script(NULL, filename); #endif + free(filename); goto out_delete; } |
