summaryrefslogtreecommitdiff
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/Build1
-rw-r--r--tools/perf/util/arm-spe-decoder/arm-spe-decoder.c1
-rw-r--r--tools/perf/util/arm-spe-decoder/arm-spe-decoder.h12
-rw-r--r--tools/perf/util/arm-spe.c130
-rw-r--r--tools/perf/util/bpf-loader.c2
-rw-r--r--tools/perf/util/bpf_off_cpu.c53
-rw-r--r--tools/perf/util/bpf_skel/off_cpu.bpf.c38
-rw-r--r--tools/perf/util/build-id.c26
-rw-r--r--tools/perf/util/events_stats.h2
-rw-r--r--tools/perf/util/jitdump.c9
-rw-r--r--tools/perf/util/machine.c1
-rw-r--r--tools/perf/util/mem-events.c46
-rw-r--r--tools/perf/util/mem-events.h3
-rw-r--r--tools/perf/util/metricgroup.c275
-rw-r--r--tools/perf/util/metricgroup.h5
-rw-r--r--tools/perf/util/parse-events.c22
-rw-r--r--tools/perf/util/parse-events.h4
-rw-r--r--tools/perf/util/perf_api_probe.c2
-rw-r--r--tools/perf/util/pmu.c139
-rw-r--r--tools/perf/util/pmu.h10
-rw-r--r--tools/perf/util/probe-event.c6
-rw-r--r--tools/perf/util/record.c2
-rw-r--r--tools/perf/util/s390-sample-raw.c50
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c2
-rw-r--r--tools/perf/util/stat-display.c383
-rw-r--r--tools/perf/util/stat.c1
-rw-r--r--tools/perf/util/stat.h2
27 files changed, 853 insertions, 374 deletions
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index d8fe514c9ec9..9dfae1bda9cc 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -289,6 +289,7 @@ CFLAGS_hweight.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ET
CFLAGS_parse-events.o += -Wno-redundant-decls
CFLAGS_expr.o += -Wno-redundant-decls
CFLAGS_header.o += -include $(OUTPUT)PERF-VERSION-FILE
+CFLAGS_arm-spe.o += -I$(srctree)/tools/arch/arm64/include/
$(OUTPUT)util/kallsyms.o: ../lib/symbol/kallsyms.c FORCE
$(call rule_mkdir)
diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c
index 5e390a1a79ab..091987dd3966 100644
--- a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c
+++ b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c
@@ -220,6 +220,7 @@ static int arm_spe_read_record(struct arm_spe_decoder *decoder)
break;
case ARM_SPE_DATA_SOURCE:
+ decoder->record.source = payload;
break;
case ARM_SPE_BAD:
break;
diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h
index 69b31084d6be..46a61df1145b 100644
--- a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h
+++ b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h
@@ -29,6 +29,17 @@ enum arm_spe_op_type {
ARM_SPE_ST = 1 << 1,
};
+enum arm_spe_neoverse_data_source {
+ ARM_SPE_NV_L1D = 0x0,
+ ARM_SPE_NV_L2 = 0x8,
+ ARM_SPE_NV_PEER_CORE = 0x9,
+ ARM_SPE_NV_LOCAL_CLUSTER = 0xa,
+ ARM_SPE_NV_SYS_CACHE = 0xb,
+ ARM_SPE_NV_PEER_CLUSTER = 0xc,
+ ARM_SPE_NV_REMOTE = 0xd,
+ ARM_SPE_NV_DRAM = 0xe,
+};
+
struct arm_spe_record {
enum arm_spe_sample_type type;
int err;
@@ -40,6 +51,7 @@ struct arm_spe_record {
u64 virt_addr;
u64 phys_addr;
u64 context_id;
+ u16 source;
};
struct arm_spe_insn;
diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c
index d040406f3314..22dcfe07e886 100644
--- a/tools/perf/util/arm-spe.c
+++ b/tools/perf/util/arm-spe.c
@@ -34,6 +34,7 @@
#include "arm-spe-decoder/arm-spe-decoder.h"
#include "arm-spe-decoder/arm-spe-pkt-decoder.h"
+#include "../../arch/arm64/include/asm/cputype.h"
#define MAX_TIMESTAMP (~0ULL)
struct arm_spe {
@@ -45,6 +46,7 @@ struct arm_spe {
struct perf_session *session;
struct machine *machine;
u32 pmu_type;
+ u64 midr;
struct perf_tsc_conversion tc;
@@ -387,35 +389,128 @@ static int arm_spe__synth_instruction_sample(struct arm_spe_queue *speq,
return arm_spe_deliver_synth_event(spe, speq, event, &sample);
}
-static u64 arm_spe__synth_data_source(const struct arm_spe_record *record)
+static const struct midr_range neoverse_spe[] = {
+ MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N1),
+ MIDR_ALL_VERSIONS(MIDR_NEOVERSE_N2),
+ MIDR_ALL_VERSIONS(MIDR_NEOVERSE_V1),
+ {},
+};
+
+static void arm_spe__synth_data_source_neoverse(const struct arm_spe_record *record,
+ union perf_mem_data_src *data_src)
{
- union perf_mem_data_src data_src = { 0 };
+ /*
+ * Even though four levels of cache hierarchy are possible, no known
+ * production Neoverse systems currently include more than three levels
+ * so for the time being we assume three exist. If a production system
+ * is built with four the this function would have to be changed to
+ * detect the number of levels for reporting.
+ */
- if (record->op == ARM_SPE_LD)
- data_src.mem_op = PERF_MEM_OP_LOAD;
- else if (record->op == ARM_SPE_ST)
- data_src.mem_op = PERF_MEM_OP_STORE;
- else
- return 0;
+ /*
+ * We have no data on the hit level or data source for stores in the
+ * Neoverse SPE records.
+ */
+ if (record->op & ARM_SPE_ST) {
+ data_src->mem_lvl = PERF_MEM_LVL_NA;
+ data_src->mem_lvl_num = PERF_MEM_LVLNUM_NA;
+ data_src->mem_snoop = PERF_MEM_SNOOP_NA;
+ return;
+ }
+
+ switch (record->source) {
+ case ARM_SPE_NV_L1D:
+ data_src->mem_lvl = PERF_MEM_LVL_L1 | PERF_MEM_LVL_HIT;
+ data_src->mem_lvl_num = PERF_MEM_LVLNUM_L1;
+ data_src->mem_snoop = PERF_MEM_SNOOP_NONE;
+ break;
+ case ARM_SPE_NV_L2:
+ data_src->mem_lvl = PERF_MEM_LVL_L2 | PERF_MEM_LVL_HIT;
+ data_src->mem_lvl_num = PERF_MEM_LVLNUM_L2;
+ data_src->mem_snoop = PERF_MEM_SNOOP_NONE;
+ break;
+ case ARM_SPE_NV_PEER_CORE:
+ data_src->mem_lvl = PERF_MEM_LVL_L2 | PERF_MEM_LVL_HIT;
+ data_src->mem_lvl_num = PERF_MEM_LVLNUM_L2;
+ data_src->mem_snoopx = PERF_MEM_SNOOPX_PEER;
+ break;
+ /*
+ * We don't know if this is L1, L2 but we do know it was a cache-2-cache
+ * transfer, so set SNOOPX_PEER
+ */
+ case ARM_SPE_NV_LOCAL_CLUSTER:
+ case ARM_SPE_NV_PEER_CLUSTER:
+ data_src->mem_lvl = PERF_MEM_LVL_L3 | PERF_MEM_LVL_HIT;
+ data_src->mem_lvl_num = PERF_MEM_LVLNUM_L3;
+ data_src->mem_snoopx = PERF_MEM_SNOOPX_PEER;
+ break;
+ /*
+ * System cache is assumed to be L3
+ */
+ case ARM_SPE_NV_SYS_CACHE:
+ data_src->mem_lvl = PERF_MEM_LVL_L3 | PERF_MEM_LVL_HIT;
+ data_src->mem_lvl_num = PERF_MEM_LVLNUM_L3;
+ data_src->mem_snoop = PERF_MEM_SNOOP_HIT;
+ break;
+ /*
+ * We don't know what level it hit in, except it came from the other
+ * socket
+ */
+ case ARM_SPE_NV_REMOTE:
+ data_src->mem_lvl = PERF_MEM_LVL_REM_CCE1;
+ data_src->mem_lvl_num = PERF_MEM_LVLNUM_ANY_CACHE;
+ data_src->mem_remote = PERF_MEM_REMOTE_REMOTE;
+ data_src->mem_snoopx = PERF_MEM_SNOOPX_PEER;
+ break;
+ case ARM_SPE_NV_DRAM:
+ data_src->mem_lvl = PERF_MEM_LVL_LOC_RAM | PERF_MEM_LVL_HIT;
+ data_src->mem_lvl_num = PERF_MEM_LVLNUM_RAM;
+ data_src->mem_snoop = PERF_MEM_SNOOP_NONE;
+ break;
+ default:
+ break;
+ }
+}
+static void arm_spe__synth_data_source_generic(const struct arm_spe_record *record,
+ union perf_mem_data_src *data_src)
+{
if (record->type & (ARM_SPE_LLC_ACCESS | ARM_SPE_LLC_MISS)) {
- data_src.mem_lvl = PERF_MEM_LVL_L3;
+ data_src->mem_lvl = PERF_MEM_LVL_L3;
if (record->type & ARM_SPE_LLC_MISS)
- data_src.mem_lvl |= PERF_MEM_LVL_MISS;
+ data_src->mem_lvl |= PERF_MEM_LVL_MISS;
else
- data_src.mem_lvl |= PERF_MEM_LVL_HIT;
+ data_src->mem_lvl |= PERF_MEM_LVL_HIT;
} else if (record->type & (ARM_SPE_L1D_ACCESS | ARM_SPE_L1D_MISS)) {
- data_src.mem_lvl = PERF_MEM_LVL_L1;
+ data_src->mem_lvl = PERF_MEM_LVL_L1;
if (record->type & ARM_SPE_L1D_MISS)
- data_src.mem_lvl |= PERF_MEM_LVL_MISS;
+ data_src->mem_lvl |= PERF_MEM_LVL_MISS;
else
- data_src.mem_lvl |= PERF_MEM_LVL_HIT;
+ data_src->mem_lvl |= PERF_MEM_LVL_HIT;
}
if (record->type & ARM_SPE_REMOTE_ACCESS)
- data_src.mem_lvl |= PERF_MEM_LVL_REM_CCE1;
+ data_src->mem_lvl |= PERF_MEM_LVL_REM_CCE1;
+}
+
+static u64 arm_spe__synth_data_source(const struct arm_spe_record *record, u64 midr)
+{
+ union perf_mem_data_src data_src = { 0 };
+ bool is_neoverse = is_midr_in_range(midr, neoverse_spe);
+
+ if (record->op == ARM_SPE_LD)
+ data_src.mem_op = PERF_MEM_OP_LOAD;
+ else if (record->op == ARM_SPE_ST)
+ data_src.mem_op = PERF_MEM_OP_STORE;
+ else
+ return 0;
+
+ if (is_neoverse)
+ arm_spe__synth_data_source_neoverse(record, &data_src);
+ else
+ arm_spe__synth_data_source_generic(record, &data_src);
if (record->type & (ARM_SPE_TLB_ACCESS | ARM_SPE_TLB_MISS)) {
data_src.mem_dtlb = PERF_MEM_TLB_WK;
@@ -436,7 +531,7 @@ static int arm_spe_sample(struct arm_spe_queue *speq)
u64 data_src;
int err;
- data_src = arm_spe__synth_data_source(record);
+ data_src = arm_spe__synth_data_source(record, spe->midr);
if (spe->sample_flc) {
if (record->type & ARM_SPE_L1D_MISS) {
@@ -1178,6 +1273,8 @@ int arm_spe_process_auxtrace_info(union perf_event *event,
struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
size_t min_sz = sizeof(u64) * ARM_SPE_AUXTRACE_PRIV_MAX;
struct perf_record_time_conv *tc = &session->time_conv;
+ const char *cpuid = perf_env__cpuid(session->evlist->env);
+ u64 midr = strtol(cpuid, NULL, 16);
struct arm_spe *spe;
int err;
@@ -1197,6 +1294,7 @@ int arm_spe_process_auxtrace_info(union perf_event *event,
spe->machine = &session->machines.host; /* No kvm support */
spe->auxtrace_type = auxtrace_info->type;
spe->pmu_type = auxtrace_info->priv[ARM_SPE_PMU_TYPE];
+ spe->midr = midr;
spe->timeless_decoding = arm_spe__is_timeless_decoding(spe);
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index d2c9b09ddb48..e2052f4fed33 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -1879,7 +1879,7 @@ struct evsel *bpf__setup_output_event(struct evlist *evlist, const char *name)
if (asprintf(&event_definition, "bpf-output/no-inherit=1,name=%s/", name) < 0)
return ERR_PTR(-ENOMEM);
- err = parse_events(evlist, event_definition, NULL);
+ err = parse_event(evlist, event_definition);
free(event_definition);
if (err) {
diff --git a/tools/perf/util/bpf_off_cpu.c b/tools/perf/util/bpf_off_cpu.c
index f289b7713598..c257813e674e 100644
--- a/tools/perf/util/bpf_off_cpu.c
+++ b/tools/perf/util/bpf_off_cpu.c
@@ -11,11 +11,13 @@
#include "util/cpumap.h"
#include "util/thread_map.h"
#include "util/cgroup.h"
+#include "util/strlist.h"
#include <bpf/bpf.h>
#include "bpf_skel/off_cpu.skel.h"
#define MAX_STACKS 32
+#define MAX_PROC 4096
/* we don't need actual timestamp, just want to put the samples at last */
#define OFF_CPU_TIMESTAMP (~0ull << 32)
@@ -78,6 +80,7 @@ static void off_cpu_start(void *arg)
u8 val = 1;
skel->bss->has_task = 1;
+ skel->bss->uses_tgid = 1;
fd = bpf_map__fd(skel->maps.task_filter);
pid = perf_thread_map__pid(evlist->core.threads, 0);
bpf_map_update_elem(fd, &pid, &val, BPF_ANY);
@@ -124,6 +127,8 @@ int off_cpu_prepare(struct evlist *evlist, struct target *target,
{
int err, fd, i;
int ncpus = 1, ntasks = 1, ncgrps = 1;
+ struct strlist *pid_slist = NULL;
+ struct str_node *pos;
if (off_cpu_config(evlist) < 0) {
pr_err("Failed to config off-cpu BPF event\n");
@@ -142,9 +147,34 @@ int off_cpu_prepare(struct evlist *evlist, struct target *target,
bpf_map__set_max_entries(skel->maps.cpu_filter, ncpus);
}
- if (target__has_task(target)) {
+ if (target->pid) {
+ pid_slist = strlist__new(target->pid, NULL);
+ if (!pid_slist) {
+ pr_err("Failed to create a strlist for pid\n");
+ return -1;
+ }
+
+ ntasks = 0;
+ strlist__for_each_entry(pos, pid_slist) {
+ char *end_ptr;
+ int pid = strtol(pos->s, &end_ptr, 10);
+
+ if (pid == INT_MIN || pid == INT_MAX ||
+ (*end_ptr != '\0' && *end_ptr != ','))
+ continue;
+
+ ntasks++;
+ }
+
+ if (ntasks < MAX_PROC)
+ ntasks = MAX_PROC;
+
+ bpf_map__set_max_entries(skel->maps.task_filter, ntasks);
+ } else if (target__has_task(target)) {
ntasks = perf_thread_map__nr(evlist->core.threads);
bpf_map__set_max_entries(skel->maps.task_filter, ntasks);
+ } else if (target__none(target)) {
+ bpf_map__set_max_entries(skel->maps.task_filter, MAX_PROC);
}
if (evlist__first(evlist)->cgrp) {
@@ -184,7 +214,26 @@ int off_cpu_prepare(struct evlist *evlist, struct target *target,
}
}
- if (target__has_task(target)) {
+ if (target->pid) {
+ u8 val = 1;
+
+ skel->bss->has_task = 1;
+ skel->bss->uses_tgid = 1;
+ fd = bpf_map__fd(skel->maps.task_filter);
+
+ strlist__for_each_entry(pos, pid_slist) {
+ char *end_ptr;
+ u32 tgid;
+ int pid = strtol(pos->s, &end_ptr, 10);
+
+ if (pid == INT_MIN || pid == INT_MAX ||
+ (*end_ptr != '\0' && *end_ptr != ','))
+ continue;
+
+ tgid = pid;
+ bpf_map_update_elem(fd, &tgid, &val, BPF_ANY);
+ }
+ } else if (target__has_task(target)) {
u32 pid;
u8 val = 1;
diff --git a/tools/perf/util/bpf_skel/off_cpu.bpf.c b/tools/perf/util/bpf_skel/off_cpu.bpf.c
index cc6d7fd55118..c4ba2bcf179f 100644
--- a/tools/perf/util/bpf_skel/off_cpu.bpf.c
+++ b/tools/perf/util/bpf_skel/off_cpu.bpf.c
@@ -12,6 +12,9 @@
#define TASK_INTERRUPTIBLE 0x0001
#define TASK_UNINTERRUPTIBLE 0x0002
+/* create a new thread */
+#define CLONE_THREAD 0x10000
+
#define MAX_STACKS 32
#define MAX_ENTRIES 102400
@@ -85,6 +88,7 @@ int enabled = 0;
int has_cpu = 0;
int has_task = 0;
int has_cgroup = 0;
+int uses_tgid = 0;
const volatile bool has_prev_state = false;
const volatile bool needs_cgroup = false;
@@ -144,7 +148,12 @@ static inline int can_record(struct task_struct *t, int state)
if (has_task) {
__u8 *ok;
- __u32 pid = t->pid;
+ __u32 pid;
+
+ if (uses_tgid)
+ pid = t->tgid;
+ else
+ pid = t->pid;
ok = bpf_map_lookup_elem(&task_filter, &pid);
if (!ok)
@@ -214,6 +223,33 @@ next:
return 0;
}
+SEC("tp_btf/task_newtask")
+int on_newtask(u64 *ctx)
+{
+ struct task_struct *task;
+ u64 clone_flags;
+ u32 pid;
+ u8 val = 1;
+
+ if (!uses_tgid)
+ return 0;
+
+ task = (struct task_struct *)bpf_get_current_task();
+
+ pid = BPF_CORE_READ(task, tgid);
+ if (!bpf_map_lookup_elem(&task_filter, &pid))
+ return 0;
+
+ task = (struct task_struct *)ctx[0];
+ clone_flags = ctx[1];
+
+ pid = task->tgid;
+ if (!(clone_flags & CLONE_THREAD))
+ bpf_map_update_elem(&task_filter, &pid, &val, BPF_NOEXIST);
+
+ return 0;
+}
+
SEC("tp_btf/sched_switch")
int on_switch(u64 *ctx)
{
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 9e176146eb10..ec18ed5caf3e 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -652,17 +652,21 @@ static char *build_id_cache__find_debug(const char *sbuild_id,
nsinfo__mountns_exit(&nsc);
#ifdef HAVE_DEBUGINFOD_SUPPORT
- if (realname == NULL) {
- debuginfod_client* c = debuginfod_begin();
- if (c != NULL) {
- int fd = debuginfod_find_debuginfo(c,
- (const unsigned char*)sbuild_id, 0,
- &realname);
- if (fd >= 0)
- close(fd); /* retaining reference by realname */
- debuginfod_end(c);
- }
- }
+ if (realname == NULL) {
+ debuginfod_client* c;
+
+ pr_debug("Downloading debug info with build id %s\n", sbuild_id);
+
+ c = debuginfod_begin();
+ if (c != NULL) {
+ int fd = debuginfod_find_debuginfo(c,
+ (const unsigned char*)sbuild_id, 0,
+ &realname);
+ if (fd >= 0)
+ close(fd); /* retaining reference by realname */
+ debuginfod_end(c);
+ }
+ }
#endif
out:
diff --git a/tools/perf/util/events_stats.h b/tools/perf/util/events_stats.h
index 1b0006092265..040ab9d0a803 100644
--- a/tools/perf/util/events_stats.h
+++ b/tools/perf/util/events_stats.h
@@ -22,7 +22,7 @@
*
* The total_period is needed because by default auto-freq is used, so
* multiplying nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get
- * the total number of low level events, it is necessary to to sum all struct
+ * the total number of low level events, it is necessary to sum all struct
* perf_record_sample.period and stash the result in total_period.
*/
struct events_stats {
diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c
index a23255773c60..4e6632203704 100644
--- a/tools/perf/util/jitdump.c
+++ b/tools/perf/util/jitdump.c
@@ -845,8 +845,13 @@ jit_process(struct perf_session *session,
if (jit_detect(filename, pid, nsi)) {
nsinfo__put(nsi);
- // Strip //anon* mmaps if we processed a jitdump for this pid
- if (jit_has_pid(machine, pid) && (strncmp(filename, "//anon", 6) == 0))
+ /*
+ * Strip //anon*, [anon:* and /memfd:* mmaps if we processed a jitdump for this pid
+ */
+ if (jit_has_pid(machine, pid) &&
+ ((strncmp(filename, "//anon", 6) == 0) ||
+ (strncmp(filename, "[anon:", 6) == 0) ||
+ (strncmp(filename, "/memfd:", 7) == 0)))
return 1;
return 0;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index facc13fbf16e..2a16cae28407 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -236,6 +236,7 @@ void machine__exit(struct machine *machine)
zfree(&machine->root_dir);
zfree(&machine->mmap_name);
zfree(&machine->current_tid);
+ zfree(&machine->kallsyms_filename);
for (i = 0; i < THREADS__TABLE_SIZE; i++) {
struct threads *threads = &machine->threads[i];
diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c
index c3c21a9c350b..764883183519 100644
--- a/tools/perf/util/mem-events.c
+++ b/tools/perf/util/mem-events.c
@@ -410,6 +410,11 @@ static const char * const snoop_access[] = {
"HitM",
};
+static const char * const snoopx_access[] = {
+ "Fwd",
+ "Peer",
+};
+
int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
{
size_t i, l = 0;
@@ -430,13 +435,20 @@ int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
}
l += scnprintf(out + l, sz - l, snoop_access[i]);
}
- if (mem_info &&
- (mem_info->data_src.mem_snoopx & PERF_MEM_SNOOPX_FWD)) {
+
+ m = 0;
+ if (mem_info)
+ m = mem_info->data_src.mem_snoopx;
+
+ for (i = 0; m && i < ARRAY_SIZE(snoopx_access); i++, m >>= 1) {
+ if (!(m & 0x1))
+ continue;
+
if (l) {
strcat(out, " or ");
l += 4;
}
- l += scnprintf(out + l, sz - l, "Fwd");
+ l += scnprintf(out + l, sz - l, snoopx_access[i]);
}
if (*out == '\0')
@@ -513,6 +525,7 @@ int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
u64 op = data_src->mem_op;
u64 lvl = data_src->mem_lvl;
u64 snoop = data_src->mem_snoop;
+ u64 snoopx = data_src->mem_snoopx;
u64 lock = data_src->mem_lock;
u64 blk = data_src->mem_blk;
/*
@@ -532,6 +545,12 @@ do { \
stats->tot_hitm++; \
} while (0)
+#define PEER_INC(__f) \
+do { \
+ stats->__f++; \
+ stats->tot_peer++; \
+} while (0)
+
#define P(a, b) PERF_MEM_##a##_##b
stats->nr_entries++;
@@ -555,12 +574,20 @@ do { \
if (lvl & P(LVL, IO)) stats->ld_io++;
if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
- if (lvl & P(LVL, L2 )) stats->ld_l2hit++;
+ if (lvl & P(LVL, L2)) {
+ stats->ld_l2hit++;
+
+ if (snoopx & P(SNOOPX, PEER))
+ PEER_INC(lcl_peer);
+ }
if (lvl & P(LVL, L3 )) {
if (snoop & P(SNOOP, HITM))
HITM_INC(lcl_hitm);
else
stats->ld_llchit++;
+
+ if (snoopx & P(SNOOPX, PEER))
+ PEER_INC(lcl_peer);
}
if (lvl & P(LVL, LOC_RAM)) {
@@ -585,10 +612,14 @@ do { \
if ((lvl & P(LVL, REM_CCE1)) ||
(lvl & P(LVL, REM_CCE2)) ||
mrem) {
- if (snoop & P(SNOOP, HIT))
+ if (snoop & P(SNOOP, HIT)) {
stats->rmt_hit++;
- else if (snoop & P(SNOOP, HITM))
+ } else if (snoop & P(SNOOP, HITM)) {
HITM_INC(rmt_hitm);
+ } else if (snoopx & P(SNOOPX, PEER)) {
+ stats->rmt_hit++;
+ PEER_INC(rmt_peer);
+ }
}
if ((lvl & P(LVL, MISS)))
@@ -652,6 +683,9 @@ void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
stats->lcl_hitm += add->lcl_hitm;
stats->rmt_hitm += add->rmt_hitm;
stats->tot_hitm += add->tot_hitm;
+ stats->lcl_peer += add->lcl_peer;
+ stats->rmt_peer += add->rmt_peer;
+ stats->tot_peer += add->tot_peer;
stats->rmt_hit += add->rmt_hit;
stats->lcl_dram += add->lcl_dram;
stats->rmt_dram += add->rmt_dram;
diff --git a/tools/perf/util/mem-events.h b/tools/perf/util/mem-events.h
index 8a8b568baeee..12372309d60e 100644
--- a/tools/perf/util/mem-events.h
+++ b/tools/perf/util/mem-events.h
@@ -78,6 +78,9 @@ struct c2c_stats {
u32 lcl_hitm; /* count of loads with local HITM */
u32 rmt_hitm; /* count of loads with remote HITM */
u32 tot_hitm; /* count of loads with local and remote HITM */
+ u32 lcl_peer; /* count of loads with local peer cache */
+ u32 rmt_peer; /* count of loads with remote peer cache */
+ u32 tot_peer; /* count of loads with local and remote peer cache */
u32 rmt_hit; /* count of loads with remote hit clean; */
u32 lcl_dram; /* count of loads miss to local DRAM */
u32 rmt_dram; /* count of loads miss to remote DRAM */
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 8f7baeabc5cf..464475fd6b9a 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -502,14 +502,14 @@ struct metricgroup_print_sys_idata {
bool details;
};
-typedef int (*metricgroup_sys_event_iter_fn)(const struct pmu_event *pe, void *);
-
struct metricgroup_iter_data {
- metricgroup_sys_event_iter_fn fn;
+ pmu_event_iter_fn fn;
void *data;
};
-static int metricgroup__sys_event_iter(const struct pmu_event *pe, void *data)
+static int metricgroup__sys_event_iter(const struct pmu_event *pe,
+ const struct pmu_events_table *table,
+ void *data)
{
struct metricgroup_iter_data *d = data;
struct perf_pmu *pmu = NULL;
@@ -522,13 +522,15 @@ static int metricgroup__sys_event_iter(const struct pmu_event *pe, void *data)
if (!pmu->id || strcmp(pmu->id, pe->compat))
continue;
- return d->fn(pe, d->data);
+ return d->fn(pe, table, d->data);
}
return 0;
}
-static int metricgroup__print_sys_event_iter(const struct pmu_event *pe, void *data)
+static int metricgroup__print_sys_event_iter(const struct pmu_event *pe,
+ const struct pmu_events_table *table __maybe_unused,
+ void *data)
{
struct metricgroup_print_sys_idata *d = data;
@@ -536,15 +538,40 @@ static int metricgroup__print_sys_event_iter(const struct pmu_event *pe, void *d
d->details, d->groups, d->metriclist);
}
+struct metricgroup_print_data {
+ const char *pmu_name;
+ struct strlist *metriclist;
+ char *filter;
+ struct rblist *groups;
+ bool metricgroups;
+ bool raw;
+ bool details;
+};
+
+static int metricgroup__print_callback(const struct pmu_event *pe,
+ const struct pmu_events_table *table __maybe_unused,
+ void *vdata)
+{
+ struct metricgroup_print_data *data = vdata;
+
+ if (!pe->metric_expr)
+ return 0;
+
+ if (data->pmu_name && perf_pmu__is_hybrid(pe->pmu) && strcmp(data->pmu_name, pe->pmu))
+ return 0;
+
+ return metricgroup__print_pmu_event(pe, data->metricgroups, data->filter,
+ data->raw, data->details, data->groups,
+ data->metriclist);
+}
+
void metricgroup__print(bool metrics, bool metricgroups, char *filter,
bool raw, bool details, const char *pmu_name)
{
- const struct pmu_events_map *map = pmu_events_map__find();
- const struct pmu_event *pe;
- int i;
struct rblist groups;
struct rb_node *node, *next;
struct strlist *metriclist = NULL;
+ const struct pmu_events_table *table;
if (!metricgroups) {
metriclist = strlist__new(NULL, NULL);
@@ -556,23 +583,22 @@ void metricgroup__print(bool metrics, bool metricgroups, char *filter,
groups.node_new = mep_new;
groups.node_cmp = mep_cmp;
groups.node_delete = mep_delete;
- for (i = 0; map; i++) {
- pe = &map->table[i];
+ table = pmu_events_table__find();
+ if (table) {
+ struct metricgroup_print_data data = {
+ .pmu_name = pmu_name,
+ .metriclist = metriclist,
+ .metricgroups = metricgroups,
+ .filter = filter,
+ .raw = raw,
+ .details = details,
+ .groups = &groups,
+ };
- if (!pe->name && !pe->metric_group && !pe->metric_name)
- break;
- if (!pe->metric_expr)
- continue;
- if (pmu_name && perf_pmu__is_hybrid(pe->pmu) &&
- strcmp(pmu_name, pe->pmu)) {
- continue;
- }
- if (metricgroup__print_pmu_event(pe, metricgroups, filter,
- raw, details, &groups,
- metriclist) < 0)
- return;
+ pmu_events_table_for_each_event(table,
+ metricgroup__print_callback,
+ &data);
}
-
{
struct metricgroup_iter_data data = {
.fn = metricgroup__print_sys_event_iter,
@@ -850,16 +876,20 @@ struct metricgroup_add_iter_data {
bool metric_no_group;
struct metric *root_metric;
const struct visited_metric *visited;
- const struct pmu_events_map *map;
+ const struct pmu_events_table *table;
};
+static bool metricgroup__find_metric(const char *metric,
+ const struct pmu_events_table *table,
+ struct pmu_event *pe);
+
static int add_metric(struct list_head *metric_list,
const struct pmu_event *pe,
const char *modifier,
bool metric_no_group,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_events_map *map);
+ const struct pmu_events_table *table);
/**
* resolve_metric - Locate metrics within the root metric and recursively add
@@ -874,7 +904,7 @@ static int add_metric(struct list_head *metric_list,
* metrics. When adding a root this argument is NULL.
* @visited: A singly linked list of metric names being added that is used to
* detect recursion.
- * @map: The map that is searched for metrics, most commonly the table for the
+ * @table: The table that is searched for metrics, most commonly the table for the
* architecture perf is running upon.
*/
static int resolve_metric(struct list_head *metric_list,
@@ -882,13 +912,13 @@ static int resolve_metric(struct list_head *metric_list,
bool metric_no_group,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_events_map *map)
+ const struct pmu_events_table *table)
{
struct hashmap_entry *cur;
size_t bkt;
struct to_resolve {
/* The metric to resolve. */
- const struct pmu_event *pe;
+ struct pmu_event pe;
/*
* The key in the IDs map, this may differ from in case,
* etc. from pe->metric_name.
@@ -902,16 +932,15 @@ static int resolve_metric(struct list_head *metric_list,
* the pending array.
*/
hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) {
- const struct pmu_event *pe;
+ struct pmu_event pe;
- pe = metricgroup__find_metric(cur->key, map);
- if (pe) {
+ if (metricgroup__find_metric(cur->key, table, &pe)) {
pending = realloc(pending,
(pending_cnt + 1) * sizeof(struct to_resolve));
if (!pending)
return -ENOMEM;
- pending[pending_cnt].pe = pe;
+ memcpy(&pending[pending_cnt].pe, &pe, sizeof(pe));
pending[pending_cnt].key = cur->key;
pending_cnt++;
}
@@ -926,8 +955,8 @@ static int resolve_metric(struct list_head *metric_list,
* context.
*/
for (i = 0; i < pending_cnt; i++) {
- ret = add_metric(metric_list, pending[i].pe, modifier, metric_no_group,
- root_metric, visited, map);
+ ret = add_metric(metric_list, &pending[i].pe, modifier, metric_no_group,
+ root_metric, visited, table);
if (ret)
break;
}
@@ -950,7 +979,7 @@ static int resolve_metric(struct list_head *metric_list,
* metrics. When adding a root this argument is NULL.
* @visited: A singly linked list of metric names being added that is used to
* detect recursion.
- * @map: The map that is searched for metrics, most commonly the table for the
+ * @table: The table that is searched for metrics, most commonly the table for the
* architecture perf is running upon.
*/
static int __add_metric(struct list_head *metric_list,
@@ -960,7 +989,7 @@ static int __add_metric(struct list_head *metric_list,
int runtime,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_events_map *map)
+ const struct pmu_events_table *table)
{
const struct visited_metric *vm;
int ret;
@@ -1032,7 +1061,7 @@ static int __add_metric(struct list_head *metric_list,
} else {
/* Resolve referenced metrics. */
ret = resolve_metric(metric_list, modifier, metric_no_group, root_metric,
- &visited_node, map);
+ &visited_node, table);
}
if (ret) {
@@ -1045,30 +1074,35 @@ static int __add_metric(struct list_head *metric_list,
return ret;
}
-#define map_for_each_event(__pe, __idx, __map) \
- if (__map) \
- for (__idx = 0, __pe = &__map->table[__idx]; \
- __pe->name || __pe->metric_group || __pe->metric_name; \
- __pe = &__map->table[++__idx])
-
-#define map_for_each_metric(__pe, __idx, __map, __metric) \
- map_for_each_event(__pe, __idx, __map) \
- if (__pe->metric_expr && \
- (match_metric(__pe->metric_group, __metric) || \
- match_metric(__pe->metric_name, __metric)))
+struct metricgroup__find_metric_data {
+ const char *metric;
+ struct pmu_event *pe;
+};
-const struct pmu_event *metricgroup__find_metric(const char *metric,
- const struct pmu_events_map *map)
+static int metricgroup__find_metric_callback(const struct pmu_event *pe,
+ const struct pmu_events_table *table __maybe_unused,
+ void *vdata)
{
- const struct pmu_event *pe;
- int i;
+ struct metricgroup__find_metric_data *data = vdata;
- map_for_each_event(pe, i, map) {
- if (match_metric(pe->metric_name, metric))
- return pe;
- }
+ if (!match_metric(pe->metric_name, data->metric))
+ return 0;
- return NULL;
+ memcpy(data->pe, pe, sizeof(*pe));
+ return 1;
+}
+
+static bool metricgroup__find_metric(const char *metric,
+ const struct pmu_events_table *table,
+ struct pmu_event *pe)
+{
+ struct metricgroup__find_metric_data data = {
+ .metric = metric,
+ .pe = pe,
+ };
+
+ return pmu_events_table_for_each_event(table, metricgroup__find_metric_callback, &data)
+ ? true : false;
}
static int add_metric(struct list_head *metric_list,
@@ -1077,7 +1111,7 @@ static int add_metric(struct list_head *metric_list,
bool metric_no_group,
struct metric *root_metric,
const struct visited_metric *visited,
- const struct pmu_events_map *map)
+ const struct pmu_events_table *table)
{
int ret = 0;
@@ -1085,7 +1119,7 @@ static int add_metric(struct list_head *metric_list,
if (!strstr(pe->metric_expr, "?")) {
ret = __add_metric(metric_list, pe, modifier, metric_no_group, 0,
- root_metric, visited, map);
+ root_metric, visited, table);
} else {
int j, count;
@@ -1098,14 +1132,15 @@ static int add_metric(struct list_head *metric_list,
for (j = 0; j < count && !ret; j++)
ret = __add_metric(metric_list, pe, modifier, metric_no_group, j,
- root_metric, visited, map);
+ root_metric, visited, table);
}
return ret;
}
static int metricgroup__add_metric_sys_event_iter(const struct pmu_event *pe,
- void *data)
+ const struct pmu_events_table *table __maybe_unused,
+ void *data)
{
struct metricgroup_add_iter_data *d = data;
int ret;
@@ -1114,7 +1149,7 @@ static int metricgroup__add_metric_sys_event_iter(const struct pmu_event *pe,
return 0;
ret = add_metric(d->metric_list, pe, d->modifier, d->metric_no_group,
- d->root_metric, d->visited, d->map);
+ d->root_metric, d->visited, d->table);
if (ret)
goto out;
@@ -1152,6 +1187,33 @@ static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l,
return right_count - left_count;
}
+struct metricgroup__add_metric_data {
+ struct list_head *list;
+ const char *metric_name;
+ const char *modifier;
+ bool metric_no_group;
+ bool has_match;
+};
+
+static int metricgroup__add_metric_callback(const struct pmu_event *pe,
+ const struct pmu_events_table *table,
+ void *vdata)
+{
+ struct metricgroup__add_metric_data *data = vdata;
+ int ret = 0;
+
+ if (pe->metric_expr &&
+ (match_metric(pe->metric_group, data->metric_name) ||
+ match_metric(pe->metric_name, data->metric_name))) {
+
+ data->has_match = true;
+ ret = add_metric(data->list, pe, data->modifier, data->metric_no_group,
+ /*root_metric=*/NULL,
+ /*visited_metrics=*/NULL, table);
+ }
+ return ret;
+}
+
/**
* metricgroup__add_metric - Find and add a metric, or a metric group.
* @metric_name: The name of the metric or metric group. For example, "IPC"
@@ -1162,32 +1224,37 @@ static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l,
* global. Grouping is the default but due to multiplexing the
* user may override.
* @metric_list: The list that the metric or metric group are added to.
- * @map: The map that is searched for metrics, most commonly the table for the
+ * @table: The table that is searched for metrics, most commonly the table for the
* architecture perf is running upon.
*/
static int metricgroup__add_metric(const char *metric_name, const char *modifier,
bool metric_no_group,
struct list_head *metric_list,
- const struct pmu_events_map *map)
+ const struct pmu_events_table *table)
{
- const struct pmu_event *pe;
LIST_HEAD(list);
- int i, ret;
+ int ret;
bool has_match = false;
- /*
- * Iterate over all metrics seeing if metric matches either the name or
- * group. When it does add the metric to the list.
- */
- map_for_each_metric(pe, i, map, metric_name) {
- has_match = true;
- ret = add_metric(&list, pe, modifier, metric_no_group,
- /*root_metric=*/NULL,
- /*visited_metrics=*/NULL, map);
+ {
+ struct metricgroup__add_metric_data data = {
+ .list = &list,
+ .metric_name = metric_name,
+ .modifier = modifier,
+ .metric_no_group = metric_no_group,
+ .has_match = false,
+ };
+ /*
+ * Iterate over all metrics seeing if metric matches either the
+ * name or group. When it does add the metric to the list.
+ */
+ ret = pmu_events_table_for_each_event(table, metricgroup__add_metric_callback,
+ &data);
if (ret)
goto out;
- }
+ has_match = data.has_match;
+ }
{
struct metricgroup_iter_data data = {
.fn = metricgroup__add_metric_sys_event_iter,
@@ -1198,7 +1265,7 @@ static int metricgroup__add_metric(const char *metric_name, const char *modifier
.metric_no_group = metric_no_group,
.has_match = &has_match,
.ret = &ret,
- .map = map,
+ .table = table,
},
};
@@ -1227,12 +1294,12 @@ out:
* global. Grouping is the default but due to multiplexing the
* user may override.
* @metric_list: The list that metrics are added to.
- * @map: The map that is searched for metrics, most commonly the table for the
+ * @table: The table that is searched for metrics, most commonly the table for the
* architecture perf is running upon.
*/
static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
struct list_head *metric_list,
- const struct pmu_events_map *map)
+ const struct pmu_events_table *table)
{
char *list_itr, *list_copy, *metric_name, *modifier;
int ret, count = 0;
@@ -1249,7 +1316,7 @@ static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
ret = metricgroup__add_metric(metric_name, modifier,
metric_no_group, metric_list,
- map);
+ table);
if (ret == -EINVAL)
pr_err("Cannot find metric or group `%s'\n", metric_name);
@@ -1440,7 +1507,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str,
bool metric_no_merge,
struct perf_pmu *fake_pmu,
struct rblist *metric_events_list,
- const struct pmu_events_map *map)
+ const struct pmu_events_table *table)
{
struct evlist *combined_evlist = NULL;
LIST_HEAD(metric_list);
@@ -1451,7 +1518,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str,
if (metric_events_list->nr_entries == 0)
metricgroup__rblist_init(metric_events_list);
ret = metricgroup__add_metric_list(str, metric_no_group,
- &metric_list, map);
+ &metric_list, table);
if (ret)
goto out;
@@ -1586,43 +1653,47 @@ int metricgroup__parse_groups(const struct option *opt,
struct rblist *metric_events)
{
struct evlist *perf_evlist = *(struct evlist **)opt->value;
- const struct pmu_events_map *map = pmu_events_map__find();
+ const struct pmu_events_table *table = pmu_events_table__find();
return parse_groups(perf_evlist, str, metric_no_group,
- metric_no_merge, NULL, metric_events, map);
+ metric_no_merge, NULL, metric_events, table);
}
int metricgroup__parse_groups_test(struct evlist *evlist,
- const struct pmu_events_map *map,
+ const struct pmu_events_table *table,
const char *str,
bool metric_no_group,
bool metric_no_merge,
struct rblist *metric_events)
{
return parse_groups(evlist, str, metric_no_group,
- metric_no_merge, &perf_pmu__fake, metric_events, map);
+ metric_no_merge, &perf_pmu__fake, metric_events, table);
+}
+
+static int metricgroup__has_metric_callback(const struct pmu_event *pe,
+ const struct pmu_events_table *table __maybe_unused,
+ void *vdata)
+{
+ const char *metric = vdata;
+
+ if (!pe->metric_expr)
+ return 0;
+
+ if (match_metric(pe->metric_name, metric))
+ return 1;
+
+ return 0;
}
bool metricgroup__has_metric(const char *metric)
{
- const struct pmu_events_map *map = pmu_events_map__find();
- const struct pmu_event *pe;
- int i;
+ const struct pmu_events_table *table = pmu_events_table__find();
- if (!map)
+ if (!table)
return false;
- for (i = 0; ; i++) {
- pe = &map->table[i];
-
- if (!pe->name && !pe->metric_group && !pe->metric_name)
- break;
- if (!pe->metric_expr)
- continue;
- if (match_metric(pe->metric_name, metric))
- return true;
- }
- return false;
+ return pmu_events_table_for_each_event(table, metricgroup__has_metric_callback,
+ (void *)metric) ? true : false;
}
int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
diff --git a/tools/perf/util/metricgroup.h b/tools/perf/util/metricgroup.h
index 2b42b778d1bf..016b3b1a289a 100644
--- a/tools/perf/util/metricgroup.h
+++ b/tools/perf/util/metricgroup.h
@@ -11,7 +11,6 @@ struct evlist;
struct evsel;
struct option;
struct rblist;
-struct pmu_events_map;
struct cgroup;
/**
@@ -70,10 +69,8 @@ int metricgroup__parse_groups(const struct option *opt,
bool metric_no_group,
bool metric_no_merge,
struct rblist *metric_events);
-const struct pmu_event *metricgroup__find_metric(const char *metric,
- const struct pmu_events_map *map);
int metricgroup__parse_groups_test(struct evlist *evlist,
- const struct pmu_events_map *map,
+ const struct pmu_events_table *table,
const char *str,
bool metric_no_group,
bool metric_no_merge,
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 206c76623c06..f05e15acd33f 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -2240,6 +2240,17 @@ int __parse_events(struct evlist *evlist, const char *str,
return ret;
}
+int parse_event(struct evlist *evlist, const char *str)
+{
+ struct parse_events_error err;
+ int ret;
+
+ parse_events_error__init(&err);
+ ret = parse_events(evlist, str, &err);
+ parse_events_error__exit(&err);
+ return ret;
+}
+
void parse_events_error__init(struct parse_events_error *err)
{
bzero(err, sizeof(*err));
@@ -2256,10 +2267,8 @@ void parse_events_error__exit(struct parse_events_error *err)
void parse_events_error__handle(struct parse_events_error *err, int idx,
char *str, char *help)
{
- if (WARN(!str, "WARNING: failed to provide error string\n")) {
- free(help);
- return;
- }
+ if (WARN(!str || !err, "WARNING: failed to provide error string or struct\n"))
+ goto out_free;
switch (err->num_errors) {
case 0:
err->idx = idx;
@@ -2284,6 +2293,11 @@ void parse_events_error__handle(struct parse_events_error *err, int idx,
break;
}
err->num_errors++;
+ return;
+
+out_free:
+ free(str);
+ free(help);
}
#define MAX_WIDTH 1000
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index ba9fa3ddaf6e..7e6a601d9cd0 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -24,15 +24,19 @@ const char *event_type(int type);
int parse_events_option(const struct option *opt, const char *str, int unset);
int parse_events_option_new_evlist(const struct option *opt, const char *str, int unset);
+__attribute__((nonnull(1, 2, 3)))
int __parse_events(struct evlist *evlist, const char *str, struct parse_events_error *error,
struct perf_pmu *fake_pmu);
+__attribute__((nonnull))
static inline int parse_events(struct evlist *evlist, const char *str,
struct parse_events_error *err)
{
return __parse_events(evlist, str, err, NULL);
}
+int parse_event(struct evlist *evlist, const char *str);
+
int parse_events_terms(struct list_head *terms, const char *str);
int parse_filter(const struct option *opt, const char *str, int unset);
int exclude_perf(const struct option *opt, const char *arg, int unset);
diff --git a/tools/perf/util/perf_api_probe.c b/tools/perf/util/perf_api_probe.c
index c28dd50bd571..e1e2d701599c 100644
--- a/tools/perf/util/perf_api_probe.c
+++ b/tools/perf/util/perf_api_probe.c
@@ -23,7 +23,7 @@ static int perf_do_probe_api(setup_probe_fn_t fn, struct perf_cpu cpu, const cha
if (!evlist)
return -ENOMEM;
- if (parse_events(evlist, str, NULL))
+ if (parse_event(evlist, str))
goto out_delete;
evsel = evlist__first(evlist);
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 0112e1c36418..89655d53117a 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -690,7 +690,7 @@ static int is_arm_pmu_core(const char *name)
return file_available(path);
}
-static char *perf_pmu__getcpuid(struct perf_pmu *pmu)
+char *perf_pmu__getcpuid(struct perf_pmu *pmu)
{
char *cpuid;
static bool printed;
@@ -710,36 +710,9 @@ static char *perf_pmu__getcpuid(struct perf_pmu *pmu)
return cpuid;
}
-const struct pmu_events_map *perf_pmu__find_map(struct perf_pmu *pmu)
+__weak const struct pmu_events_table *pmu_events_table__find(void)
{
- const struct pmu_events_map *map;
- char *cpuid = perf_pmu__getcpuid(pmu);
- int i;
-
- /* on some platforms which uses cpus map, cpuid can be NULL for
- * PMUs other than CORE PMUs.
- */
- if (!cpuid)
- return NULL;
-
- i = 0;
- for (;;) {
- map = &pmu_events_map[i++];
- if (!map->table) {
- map = NULL;
- break;
- }
-
- if (!strcmp_cpuid_str(map->cpuid, cpuid))
- break;
- }
- free(cpuid);
- return map;
-}
-
-const struct pmu_events_map *__weak pmu_events_map__find(void)
-{
- return perf_pmu__find_map(NULL);
+ return perf_pmu__find_table(NULL);
}
/*
@@ -818,81 +791,63 @@ out:
return res;
}
-/*
- * From the pmu_events_map, find the table of PMU events that corresponds
- * to the current running CPU. Then, add all PMU events from that table
- * as aliases.
- */
-void pmu_add_cpu_aliases_map(struct list_head *head, struct perf_pmu *pmu,
- const struct pmu_events_map *map)
+struct pmu_add_cpu_aliases_map_data {
+ struct list_head *head;
+ const char *name;
+ const char *cpu_name;
+ struct perf_pmu *pmu;
+};
+
+static int pmu_add_cpu_aliases_map_callback(const struct pmu_event *pe,
+ const struct pmu_events_table *table __maybe_unused,
+ void *vdata)
{
- int i;
- const char *name = pmu->name;
- /*
- * Found a matching PMU events table. Create aliases
- */
- i = 0;
- while (1) {
- const char *cpu_name = is_arm_pmu_core(name) ? name : "cpu";
- const struct pmu_event *pe = &map->table[i++];
- const char *pname = pe->pmu ? pe->pmu : cpu_name;
+ struct pmu_add_cpu_aliases_map_data *data = vdata;
+ const char *pname = pe->pmu ? pe->pmu : data->cpu_name;
- if (!pe->name) {
- if (pe->metric_group || pe->metric_name)
- continue;
- break;
- }
+ if (!pe->name)
+ return 0;
- if (pmu->is_uncore && pmu_uncore_alias_match(pname, name))
- goto new_alias;
+ if (data->pmu->is_uncore && pmu_uncore_alias_match(pname, data->name))
+ goto new_alias;
- if (strcmp(pname, name))
- continue;
+ if (strcmp(pname, data->name))
+ return 0;
new_alias:
- /* need type casts to override 'const' */
- __perf_pmu__new_alias(head, NULL, (char *)pe->name,
- (char *)pe->desc, (char *)pe->event,
- pe);
- }
+ /* need type casts to override 'const' */
+ __perf_pmu__new_alias(data->head, NULL, (char *)pe->name, (char *)pe->desc,
+ (char *)pe->event, pe);
+ return 0;
}
-static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu)
+/*
+ * From the pmu_events_map, find the table of PMU events that corresponds
+ * to the current running CPU. Then, add all PMU events from that table
+ * as aliases.
+ */
+void pmu_add_cpu_aliases_table(struct list_head *head, struct perf_pmu *pmu,
+ const struct pmu_events_table *table)
{
- const struct pmu_events_map *map;
-
- map = perf_pmu__find_map(pmu);
- if (!map)
- return;
+ struct pmu_add_cpu_aliases_map_data data = {
+ .head = head,
+ .name = pmu->name,
+ .cpu_name = is_arm_pmu_core(pmu->name) ? pmu->name : "cpu",
+ .pmu = pmu,
+ };
- pmu_add_cpu_aliases_map(head, pmu, map);
+ pmu_events_table_for_each_event(table, pmu_add_cpu_aliases_map_callback, &data);
}
-void pmu_for_each_sys_event(pmu_sys_event_iter_fn fn, void *data)
+static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu)
{
- int i = 0;
-
- while (1) {
- const struct pmu_sys_events *event_table;
- int j = 0;
-
- event_table = &pmu_sys_event_tables[i++];
+ const struct pmu_events_table *table;
- if (!event_table->table)
- break;
-
- while (1) {
- const struct pmu_event *pe = &event_table->table[j++];
- int ret;
-
- if (!pe->name && !pe->metric_group && !pe->metric_name)
- break;
+ table = perf_pmu__find_table(pmu);
+ if (!table)
+ return;
- ret = fn(pe, data);
- if (ret)
- break;
- }
- }
+ pmu_add_cpu_aliases_table(head, pmu, table);
}
struct pmu_sys_event_iter_data {
@@ -900,7 +855,9 @@ struct pmu_sys_event_iter_data {
struct perf_pmu *pmu;
};
-static int pmu_add_sys_aliases_iter_fn(const struct pmu_event *pe, void *data)
+static int pmu_add_sys_aliases_iter_fn(const struct pmu_event *pe,
+ const struct pmu_events_table *table __maybe_unused,
+ void *data)
{
struct pmu_sys_event_iter_data *idata = data;
struct perf_pmu *pmu = idata->pmu;
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 4b45fd8da5a3..a7b0f9507510 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -125,16 +125,14 @@ int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
int perf_pmu__test(void);
struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu);
-void pmu_add_cpu_aliases_map(struct list_head *head, struct perf_pmu *pmu,
- const struct pmu_events_map *map);
+void pmu_add_cpu_aliases_table(struct list_head *head, struct perf_pmu *pmu,
+ const struct pmu_events_table *table);
-const struct pmu_events_map *perf_pmu__find_map(struct perf_pmu *pmu);
-const struct pmu_events_map *pmu_events_map__find(void);
+char *perf_pmu__getcpuid(struct perf_pmu *pmu);
+const struct pmu_events_table *pmu_events_table__find(void);
bool pmu_uncore_alias_match(const char *pmu_name, const char *name);
void perf_pmu_free_alias(struct perf_pmu_alias *alias);
-typedef int (*pmu_sys_event_iter_fn)(const struct pmu_event *pe, void *data);
-void pmu_for_each_sys_event(pmu_sys_event_iter_fn fn, void *data);
int perf_pmu__convert_scale(const char *scale, char **end, double *sval);
int perf_pmu__caps_parse(struct perf_pmu *pmu);
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 67c12d5303e7..785246ff4179 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1775,8 +1775,10 @@ int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
if (!pev->event && pev->point.function && pev->point.line
&& !pev->point.lazy_line && !pev->point.offset) {
if (asprintf(&pev->event, "%s_L%d", pev->point.function,
- pev->point.line) < 0)
- return -ENOMEM;
+ pev->point.line) < 0) {
+ ret = -ENOMEM;
+ goto out;
+ }
}
/* Copy arguments and ensure return probe has no C argument */
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c
index b529636ab3ea..7b58f6c7c69d 100644
--- a/tools/perf/util/record.c
+++ b/tools/perf/util/record.c
@@ -238,7 +238,7 @@ bool evlist__can_select_event(struct evlist *evlist, const char *str)
if (!temp_evlist)
return false;
- err = parse_events(temp_evlist, str, NULL);
+ err = parse_event(temp_evlist, str);
if (err)
goto out_delete;
diff --git a/tools/perf/util/s390-sample-raw.c b/tools/perf/util/s390-sample-raw.c
index cd3a34840389..9a631d97471c 100644
--- a/tools/perf/util/s390-sample-raw.c
+++ b/tools/perf/util/s390-sample-raw.c
@@ -129,28 +129,46 @@ static int get_counterset_start(int setnr)
}
}
+struct get_counter_name_data {
+ int wanted;
+ const char *result;
+};
+
+static int get_counter_name_callback(const struct pmu_event *evp,
+ const struct pmu_events_table *table __maybe_unused,
+ void *vdata)
+{
+ struct get_counter_name_data *data = vdata;
+ int rc, event_nr;
+
+ if (evp->name == NULL || evp->event == NULL)
+ return 0;
+ rc = sscanf(evp->event, "event=%x", &event_nr);
+ if (rc == 1 && event_nr == data->wanted) {
+ data->result = evp->name;
+ return 1; /* Terminate the search. */
+ }
+ return 0;
+}
+
/* Scan the PMU table and extract the logical name of a counter from the
* PMU events table. Input is the counter set and counter number with in the
* set. Construct the event number and use this as key. If they match return
* the name of this counter.
* If no match is found a NULL pointer is returned.
*/
-static const char *get_counter_name(int set, int nr, const struct pmu_events_map *map)
+static const char *get_counter_name(int set, int nr, const struct pmu_events_table *table)
{
- int rc, event_nr, wanted = get_counterset_start(set) + nr;
+ struct get_counter_name_data data = {
+ .wanted = get_counterset_start(set) + nr,
+ .result = NULL,
+ };
- if (map) {
- const struct pmu_event *evp = map->table;
+ if (!table)
+ return NULL;
- for (; evp->name || evp->event || evp->desc; ++evp) {
- if (evp->name == NULL || evp->event == NULL)
- continue;
- rc = sscanf(evp->event, "event=%x", &event_nr);
- if (rc == 1 && event_nr == wanted)
- return evp->name;
- }
- }
- return NULL;
+ pmu_events_table_for_each_event(table, get_counter_name_callback, &data);
+ return data.result;
}
static void s390_cpumcfdg_dump(struct perf_sample *sample)
@@ -159,10 +177,10 @@ static void s390_cpumcfdg_dump(struct perf_sample *sample)
unsigned char *buf = sample->raw_data;
const char *color = PERF_COLOR_BLUE;
struct cf_ctrset_entry *cep, ce;
- const struct pmu_events_map *map;
+ const struct pmu_events_table *table;
u64 *p;
- map = pmu_events_map__find();
+ table = pmu_events_table__find();
while (offset < len) {
cep = (struct cf_ctrset_entry *)(buf + offset);
@@ -180,7 +198,7 @@ static void s390_cpumcfdg_dump(struct perf_sample *sample)
color_fprintf(stdout, color, " [%#08zx] Counterset:%d"
" Counters:%d\n", offset, ce.set, ce.ctr);
for (i = 0, p = (u64 *)(cep + 1); i < ce.ctr; ++i, ++p) {
- const char *ev_name = get_counter_name(ce.set, i, map);
+ const char *ev_name = get_counter_name(ce.set, i, table);
color_fprintf(stdout, color,
"\tCounter:%03d %s Value:%#018lx\n", i,
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 5bbc1b16f368..9ef2406e0ede 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -131,7 +131,7 @@ static void handler_call_die(const char *handler_name)
}
/*
- * Insert val into into the dictionary and decrement the reference counter.
+ * Insert val into the dictionary and decrement the reference counter.
* This is necessary for dictionaries since PyDict_SetItemString() does not
* steal a reference, as opposed to PyTuple_SetItem().
*/
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index 44045565c8f8..b82844cb0ce7 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -28,15 +28,21 @@
static void print_running(struct perf_stat_config *config,
u64 run, u64 ena)
{
- if (config->csv_output) {
- fprintf(config->output, "%s%" PRIu64 "%s%.2f",
- config->csv_sep,
- run,
- config->csv_sep,
- ena ? 100.0 * run / ena : 100.0);
- } else if (run != ena) {
+
+ double enabled_percent = 100;
+
+ if (run != ena)
+ enabled_percent = 100 * run / ena;
+ if (config->json_output)
+ fprintf(config->output,
+ "\"event-runtime\" : %" PRIu64 ", \"pcnt-running\" : %.2f, ",
+ run, enabled_percent);
+ else if (config->csv_output)
+ fprintf(config->output,
+ "%s%" PRIu64 "%s%.2f", config->csv_sep,
+ run, config->csv_sep, enabled_percent);
+ else if (run != ena)
fprintf(config->output, " (%.2f%%)", 100.0 * run / ena);
- }
}
static void print_noise_pct(struct perf_stat_config *config,
@@ -44,7 +50,9 @@ static void print_noise_pct(struct perf_stat_config *config,
{
double pct = rel_stddev_stats(total, avg);
- if (config->csv_output)
+ if (config->json_output)
+ fprintf(config->output, "\"variance\" : %.2f, ", pct);
+ else if (config->csv_output)
fprintf(config->output, "%s%.2f%%", config->csv_sep, pct);
else if (pct)
fprintf(config->output, " ( +-%6.2f%% )", pct);
@@ -66,7 +74,11 @@ static void print_cgroup(struct perf_stat_config *config, struct evsel *evsel)
{
if (nr_cgroups) {
const char *cgrp_name = evsel->cgrp ? evsel->cgrp->name : "";
- fprintf(config->output, "%s%s", config->csv_sep, cgrp_name);
+
+ if (config->json_output)
+ fprintf(config->output, "\"cgroup\" : \"%s\", ", cgrp_name);
+ else
+ fprintf(config->output, "%s%s", config->csv_sep, cgrp_name);
}
}
@@ -74,69 +86,123 @@ static void print_cgroup(struct perf_stat_config *config, struct evsel *evsel)
static void aggr_printout(struct perf_stat_config *config,
struct evsel *evsel, struct aggr_cpu_id id, int nr)
{
+
+
+ if (config->json_output && !config->interval)
+ fprintf(config->output, "{");
+
switch (config->aggr_mode) {
case AGGR_CORE:
- fprintf(config->output, "S%d-D%d-C%*d%s%*d%s",
- id.socket,
- id.die,
- config->csv_output ? 0 : -8,
- id.core,
- config->csv_sep,
- config->csv_output ? 0 : 4,
- nr,
- config->csv_sep);
+ if (config->json_output) {
+ fprintf(config->output,
+ "\"core\" : \"S%d-D%d-C%d\", \"aggregate-number\" : %d, ",
+ id.socket,
+ id.die,
+ id.core,
+ nr);
+ } else {
+ fprintf(config->output, "S%d-D%d-C%*d%s%*d%s",
+ id.socket,
+ id.die,
+ config->csv_output ? 0 : -8,
+ id.core,
+ config->csv_sep,
+ config->csv_output ? 0 : 4,
+ nr,
+ config->csv_sep);
+ }
break;
case AGGR_DIE:
- fprintf(config->output, "S%d-D%*d%s%*d%s",
- id.socket,
- config->csv_output ? 0 : -8,
- id.die,
- config->csv_sep,
- config->csv_output ? 0 : 4,
- nr,
- config->csv_sep);
+ if (config->json_output) {
+ fprintf(config->output,
+ "\"die\" : \"S%d-D%d\", \"aggregate-number\" : %d, ",
+ id.socket,
+ id.die,
+ nr);
+ } else {
+ fprintf(config->output, "S%d-D%*d%s%*d%s",
+ id.socket,
+ config->csv_output ? 0 : -8,
+ id.die,
+ config->csv_sep,
+ config->csv_output ? 0 : 4,
+ nr,
+ config->csv_sep);
+ }
break;
case AGGR_SOCKET:
- fprintf(config->output, "S%*d%s%*d%s",
- config->csv_output ? 0 : -5,
- id.socket,
- config->csv_sep,
- config->csv_output ? 0 : 4,
- nr,
- config->csv_sep);
- break;
+ if (config->json_output) {
+ fprintf(config->output,
+ "\"socket\" : \"S%d\", \"aggregate-number\" : %d, ",
+ id.socket,
+ nr);
+ } else {
+ fprintf(config->output, "S%*d%s%*d%s",
+ config->csv_output ? 0 : -5,
+ id.socket,
+ config->csv_sep,
+ config->csv_output ? 0 : 4,
+ nr,
+ config->csv_sep);
+ }
+ break;
case AGGR_NODE:
- fprintf(config->output, "N%*d%s%*d%s",
- config->csv_output ? 0 : -5,
- id.node,
- config->csv_sep,
- config->csv_output ? 0 : 4,
- nr,
- config->csv_sep);
- break;
+ if (config->json_output) {
+ fprintf(config->output, "\"node\" : \"N%d\", \"aggregate-number\" : %d, ",
+ id.node,
+ nr);
+ } else {
+ fprintf(config->output, "N%*d%s%*d%s",
+ config->csv_output ? 0 : -5,
+ id.node,
+ config->csv_sep,
+ config->csv_output ? 0 : 4,
+ nr,
+ config->csv_sep);
+ }
+ break;
case AGGR_NONE:
- if (evsel->percore && !config->percore_show_thread) {
- fprintf(config->output, "S%d-D%d-C%*d%s",
- id.socket,
- id.die,
- config->csv_output ? 0 : -3,
- id.core, config->csv_sep);
- } else if (id.cpu.cpu > -1) {
- fprintf(config->output, "CPU%*d%s",
- config->csv_output ? 0 : -7,
- id.cpu.cpu, config->csv_sep);
+ if (config->json_output) {
+ if (evsel->percore && !config->percore_show_thread) {
+ fprintf(config->output, "\"core\" : \"S%d-D%d-C%d\"",
+ id.socket,
+ id.die,
+ id.core);
+ } else if (id.core > -1) {
+ fprintf(config->output, "\"cpu\" : \"%d\", ",
+ id.cpu.cpu);
+ }
+ } else {
+ if (evsel->percore && !config->percore_show_thread) {
+ fprintf(config->output, "S%d-D%d-C%*d%s",
+ id.socket,
+ id.die,
+ config->csv_output ? 0 : -3,
+ id.core, config->csv_sep);
+ } else if (id.core > -1) {
+ fprintf(config->output, "CPU%*d%s",
+ config->csv_output ? 0 : -7,
+ id.cpu.cpu, config->csv_sep);
+ }
}
break;
case AGGR_THREAD:
- fprintf(config->output, "%*s-%*d%s",
- config->csv_output ? 0 : 16,
- perf_thread_map__comm(evsel->core.threads, id.thread),
- config->csv_output ? 0 : -8,
- perf_thread_map__pid(evsel->core.threads, id.thread),
- config->csv_sep);
+ if (config->json_output) {
+ fprintf(config->output, "\"thread\" : \"%s-%d\", ",
+ perf_thread_map__comm(evsel->core.threads, id.thread),
+ perf_thread_map__pid(evsel->core.threads, id.thread));
+ } else {
+ fprintf(config->output, "%*s-%*d%s",
+ config->csv_output ? 0 : 16,
+ perf_thread_map__comm(evsel->core.threads, id.thread),
+ config->csv_output ? 0 : -8,
+ perf_thread_map__pid(evsel->core.threads, id.thread),
+ config->csv_sep);
+ }
break;
case AGGR_GLOBAL:
case AGGR_UNSET:
+ case AGGR_MAX:
default:
break;
}
@@ -234,6 +300,31 @@ static void print_metric_csv(struct perf_stat_config *config __maybe_unused,
fprintf(out, "%s%s%s%s", config->csv_sep, vals, config->csv_sep, skip_spaces(unit));
}
+static void print_metric_json(struct perf_stat_config *config __maybe_unused,
+ void *ctx,
+ const char *color __maybe_unused,
+ const char *fmt __maybe_unused,
+ const char *unit, double val)
+{
+ struct outstate *os = ctx;
+ FILE *out = os->fh;
+
+ fprintf(out, "\"metric-value\" : %f, ", val);
+ fprintf(out, "\"metric-unit\" : \"%s\"", unit);
+ if (!config->metric_only)
+ fprintf(out, "}");
+}
+
+static void new_line_json(struct perf_stat_config *config, void *ctx)
+{
+ struct outstate *os = ctx;
+
+ fputc('\n', os->fh);
+ if (os->prefix)
+ fprintf(os->fh, "%s", os->prefix);
+ aggr_printout(config, os->evsel, os->id, os->nr);
+}
+
/* Filter out some columns that don't work well in metrics only mode */
static bool valid_only_metric(const char *unit)
@@ -300,6 +391,27 @@ static void print_metric_only_csv(struct perf_stat_config *config __maybe_unused
fprintf(out, "%s%s", vals, config->csv_sep);
}
+static void print_metric_only_json(struct perf_stat_config *config __maybe_unused,
+ void *ctx, const char *color __maybe_unused,
+ const char *fmt,
+ const char *unit, double val)
+{
+ struct outstate *os = ctx;
+ FILE *out = os->fh;
+ char buf[64], *vals, *ends;
+ char tbuf[1024];
+
+ if (!valid_only_metric(unit))
+ return;
+ unit = fixunit(tbuf, os->evsel, unit);
+ snprintf(buf, sizeof(buf), fmt, val);
+ ends = vals = skip_spaces(buf);
+ while (isdigit(*ends) || *ends == '.')
+ ends++;
+ *ends = 0;
+ fprintf(out, "{\"metric-value\" : \"%s\"}", vals);
+}
+
static void new_line_metric(struct perf_stat_config *config __maybe_unused,
void *ctx __maybe_unused)
{
@@ -318,10 +430,13 @@ static void print_metric_header(struct perf_stat_config *config,
os->evsel->priv != os->evsel->evlist->selected->priv)
return;
- if (!valid_only_metric(unit))
+ if (!valid_only_metric(unit) && !config->json_output)
return;
unit = fixunit(tbuf, os->evsel, unit);
- if (config->csv_output)
+
+ if (config->json_output)
+ fprintf(os->fh, "\"unit\" : \"%s\"", unit);
+ else if (config->csv_output)
fprintf(os->fh, "%s%s", unit, config->csv_sep);
else
fprintf(os->fh, "%*s ", config->metric_only_len, unit);
@@ -367,14 +482,27 @@ static void abs_printout(struct perf_stat_config *config,
aggr_printout(config, evsel, id, nr);
- fprintf(output, fmt, avg, config->csv_sep);
+ if (config->json_output)
+ fprintf(output, "\"counter-value\" : \"%f\", ", avg);
+ else
+ fprintf(output, fmt, avg, config->csv_sep);
- if (evsel->unit)
- fprintf(output, "%-*s%s",
- config->csv_output ? 0 : config->unit_width,
- evsel->unit, config->csv_sep);
+ if (config->json_output) {
+ if (evsel->unit) {
+ fprintf(output, "\"unit\" : \"%s\", ",
+ evsel->unit);
+ }
+ } else {
+ if (evsel->unit)
+ fprintf(output, "%-*s%s",
+ config->csv_output ? 0 : config->unit_width,
+ evsel->unit, config->csv_sep);
+ }
- fprintf(output, "%-*s", config->csv_output ? 0 : 32, evsel__name(evsel));
+ if (config->json_output)
+ fprintf(output, "\"event\" : \"%s\", ", evsel__name(evsel));
+ else
+ fprintf(output, "%-*s", config->csv_output ? 0 : 32, evsel__name(evsel));
print_cgroup(config, evsel);
}
@@ -416,34 +544,30 @@ static void printout(struct perf_stat_config *config, struct aggr_cpu_id id, int
.nr = nr,
.evsel = counter,
};
- print_metric_t pm = print_metric_std;
+ print_metric_t pm;
new_line_t nl;
- if (config->metric_only) {
- nl = new_line_metric;
- if (config->csv_output)
- pm = print_metric_only_csv;
- else
- pm = print_metric_only;
- } else
- nl = new_line_std;
-
- if (config->csv_output && !config->metric_only) {
- static int aggr_fields[] = {
- [AGGR_GLOBAL] = 0,
- [AGGR_THREAD] = 1,
+ if (config->csv_output) {
+ static const int aggr_fields[AGGR_MAX] = {
[AGGR_NONE] = 1,
+ [AGGR_GLOBAL] = 0,
[AGGR_SOCKET] = 2,
[AGGR_DIE] = 2,
[AGGR_CORE] = 2,
+ [AGGR_THREAD] = 1,
+ [AGGR_UNSET] = 0,
+ [AGGR_NODE] = 0,
};
- pm = print_metric_csv;
- nl = new_line_csv;
- os.nfields = 3;
- os.nfields += aggr_fields[config->aggr_mode];
- if (counter->cgrp)
- os.nfields++;
+ pm = config->metric_only ? print_metric_only_csv : print_metric_csv;
+ nl = config->metric_only ? new_line_metric : new_line_csv;
+ os.nfields = 3 + aggr_fields[config->aggr_mode] + (counter->cgrp ? 1 : 0);
+ } else if (config->json_output) {
+ pm = config->metric_only ? print_metric_only_json : print_metric_json;
+ nl = config->metric_only ? new_line_metric : new_line_json;
+ } else {
+ pm = config->metric_only ? print_metric_only : print_metric_std;
+ nl = config->metric_only ? new_line_metric : new_line_std;
}
if (!config->no_csv_summary && config->csv_output &&
@@ -458,10 +582,15 @@ static void printout(struct perf_stat_config *config, struct aggr_cpu_id id, int
}
aggr_printout(config, counter, id, nr);
- fprintf(config->output, "%*s%s",
- config->csv_output ? 0 : 18,
- counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
- config->csv_sep);
+ if (config->json_output) {
+ fprintf(config->output, "\"counter-value\" : \"%s\", ",
+ counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED);
+ } else {
+ fprintf(config->output, "%*s%s",
+ config->csv_output ? 0 : 18,
+ counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
+ config->csv_sep);
+ }
if (counter->supported) {
if (!evlist__has_hybrid(counter->evlist)) {
@@ -471,21 +600,32 @@ static void printout(struct perf_stat_config *config, struct aggr_cpu_id id, int
}
}
- fprintf(config->output, "%-*s%s",
- config->csv_output ? 0 : config->unit_width,
- counter->unit, config->csv_sep);
+ if (config->json_output) {
+ fprintf(config->output, "\"unit\" : \"%s\", ", counter->unit);
+ } else {
+ fprintf(config->output, "%-*s%s",
+ config->csv_output ? 0 : config->unit_width,
+ counter->unit, config->csv_sep);
+ }
- fprintf(config->output, "%*s",
- config->csv_output ? 0 : -25, evsel__name(counter));
+ if (config->json_output) {
+ fprintf(config->output, "\"event\" : \"%s\", ",
+ evsel__name(counter));
+ } else {
+ fprintf(config->output, "%*s",
+ config->csv_output ? 0 : -25, evsel__name(counter));
+ }
print_cgroup(config, counter);
- if (!config->csv_output)
+ if (!config->csv_output && !config->json_output)
pm(config, &os, NULL, NULL, "", 0);
print_noise(config, counter, noise);
print_running(config, run, ena);
if (config->csv_output)
pm(config, &os, NULL, NULL, "", 0);
+ else if (config->json_output)
+ pm(config, &os, NULL, NULL, "", 0);
return;
}
@@ -500,12 +640,15 @@ static void printout(struct perf_stat_config *config, struct aggr_cpu_id id, int
if (config->csv_output && !config->metric_only) {
print_noise(config, counter, noise);
print_running(config, run, ena);
+ } else if (config->json_output && !config->metric_only) {
+ print_noise(config, counter, noise);
+ print_running(config, run, ena);
}
perf_stat__print_shadow_stats(config, counter, uval,
first_shadow_cpu_map_idx(config, counter, &id),
&out, &config->metric_events, st);
- if (!config->csv_output && !config->metric_only) {
+ if (!config->csv_output && !config->metric_only && !config->json_output) {
print_noise(config, counter, noise);
print_running(config, run, ena);
}
@@ -1004,8 +1147,12 @@ static void print_metric_headers(struct perf_stat_config *config,
struct outstate os = {
.fh = config->output
};
+ bool first = true;
+
+ if (config->json_output && !config->interval)
+ fprintf(config->output, "{");
- if (prefix)
+ if (prefix && !config->json_output)
fprintf(config->output, "%s", prefix);
if (!config->csv_output && !no_indent)
@@ -1025,6 +1172,9 @@ static void print_metric_headers(struct perf_stat_config *config,
os.evsel = counter;
out.ctx = &os;
out.print_metric = print_metric_header;
+ if (!first && config->json_output)
+ fprintf(config->output, ", ");
+ first = false;
out.new_line = new_line_metric;
out.force_header = true;
perf_stat__print_shadow_stats(config, counter, 0,
@@ -1033,6 +1183,8 @@ static void print_metric_headers(struct perf_stat_config *config,
&config->metric_events,
&rt_stat);
}
+ if (config->json_output)
+ fprintf(config->output, "}");
fputc('\n', config->output);
}
@@ -1048,10 +1200,18 @@ static void print_interval(struct perf_stat_config *config,
if (config->interval_clear)
puts(CONSOLE_CLEAR);
- if (!config->iostat_run)
- sprintf(prefix, "%6lu.%09lu%s", (unsigned long) ts->tv_sec, ts->tv_nsec, config->csv_sep);
-
- if ((num_print_interval == 0 && !config->csv_output) || config->interval_clear) {
+ if (!config->iostat_run && !config->json_output)
+ sprintf(prefix, "%6lu.%09lu%s", (unsigned long) ts->tv_sec,
+ ts->tv_nsec, config->csv_sep);
+ if (!config->iostat_run && config->json_output && !config->metric_only)
+ sprintf(prefix, "{\"interval\" : %lu.%09lu, ", (unsigned long)
+ ts->tv_sec, ts->tv_nsec);
+ if (!config->iostat_run && config->json_output && config->metric_only)
+ sprintf(prefix, "{\"interval\" : %lu.%09lu}", (unsigned long)
+ ts->tv_sec, ts->tv_nsec);
+
+ if ((num_print_interval == 0 && !config->csv_output && !config->json_output)
+ || config->interval_clear) {
switch (config->aggr_mode) {
case AGGR_NODE:
fprintf(output, "# time node cpus");
@@ -1091,12 +1251,19 @@ static void print_interval(struct perf_stat_config *config,
fprintf(output, " counts %*s events\n", unit_width, "unit");
}
case AGGR_UNSET:
+ case AGGR_MAX:
break;
}
}
- if ((num_print_interval == 0 || config->interval_clear) && metric_only)
+ if ((num_print_interval == 0 || config->interval_clear)
+ && metric_only && !config->json_output)
print_metric_headers(config, evlist, " ", true);
+ if ((num_print_interval == 0 || config->interval_clear)
+ && metric_only && config->json_output) {
+ fprintf(output, "{");
+ print_metric_headers(config, evlist, " ", true);
+ }
if (++num_print_interval == 25)
num_print_interval = 0;
}
@@ -1110,7 +1277,7 @@ static void print_header(struct perf_stat_config *config,
fflush(stdout);
- if (!config->csv_output) {
+ if (!config->csv_output && !config->json_output) {
fprintf(output, "\n");
fprintf(output, " Performance counter stats for ");
if (_target->bpf_str)
@@ -1303,6 +1470,9 @@ void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *conf
num_print_iv = 0;
if (config->aggr_mode == AGGR_GLOBAL && prefix && !config->iostat_run)
fprintf(config->output, "%s", prefix);
+
+ if (config->json_output && !config->metric_only)
+ fprintf(config->output, "}");
}
switch (config->aggr_mode) {
@@ -1341,12 +1511,13 @@ void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *conf
}
}
break;
+ case AGGR_MAX:
case AGGR_UNSET:
default:
break;
}
- if (!interval && !config->csv_output)
+ if (!interval && !config->csv_output && !config->json_output)
print_footer(config);
fflush(config->output);
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index 37ea2d044708..0882b4754fcf 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -401,6 +401,7 @@ process_counter_values(struct perf_stat_config *config, struct evsel *evsel,
aggr->ena += count->ena;
aggr->run += count->run;
case AGGR_UNSET:
+ case AGGR_MAX:
default:
break;
}
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
index b5aeb8e6d34b..668250022f8c 100644
--- a/tools/perf/util/stat.h
+++ b/tools/perf/util/stat.h
@@ -57,6 +57,7 @@ enum aggr_mode {
AGGR_THREAD,
AGGR_UNSET,
AGGR_NODE,
+ AGGR_MAX
};
enum {
@@ -121,6 +122,7 @@ struct perf_stat_config {
bool no_inherit;
bool identifier;
bool csv_output;
+ bool json_output;
bool interval_clear;
bool metric_only;
bool null_run;