summaryrefslogtreecommitdiff
path: root/tools/perf/builtin-script.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-script.c')
-rw-r--r--tools/perf/builtin-script.c619
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;
}