summaryrefslogtreecommitdiff
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/Build72
-rw-r--r--tools/perf/util/amd-sample-raw.c1
-rw-r--r--tools/perf/util/annotate.c10
-rw-r--r--tools/perf/util/bpf-filter.c14
-rw-r--r--tools/perf/util/bpf-filter.y2
-rw-r--r--tools/perf/util/bpf-loader.c2110
-rw-r--r--tools/perf/util/bpf-loader.h216
-rw-r--r--tools/perf/util/bpf-prologue.c508
-rw-r--r--tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c422
-rw-r--r--tools/perf/util/bpf_skel/bench_uprobe.bpf.c23
-rw-r--r--tools/perf/util/build-id.c6
-rw-r--r--tools/perf/util/c++/Build2
-rw-r--r--tools/perf/util/c++/clang-c.h43
-rw-r--r--tools/perf/util/c++/clang-test.cpp67
-rw-r--r--tools/perf/util/c++/clang.cpp225
-rw-r--r--tools/perf/util/c++/clang.h27
-rw-r--r--tools/perf/util/config.c4
-rw-r--r--tools/perf/util/cs-etm.c14
-rw-r--r--tools/perf/util/dlfilter.c32
-rw-r--r--tools/perf/util/env.c8
-rw-r--r--tools/perf/util/event.c30
-rw-r--r--tools/perf/util/event.h2
-rw-r--r--tools/perf/util/evsel.c10
-rw-r--r--tools/perf/util/expr.c29
-rw-r--r--tools/perf/util/expr.h1
-rw-r--r--tools/perf/util/expr.l1
-rw-r--r--tools/perf/util/expr.y12
-rw-r--r--tools/perf/util/hashmap.h10
-rw-r--r--tools/perf/util/header.c52
-rw-r--r--tools/perf/util/libunwind/arm64.c2
-rw-r--r--tools/perf/util/libunwind/x86_32.c2
-rw-r--r--tools/perf/util/llvm-utils.c612
-rw-r--r--tools/perf/util/llvm-utils.h69
-rw-r--r--tools/perf/util/lzma.c12
-rw-r--r--tools/perf/util/machine.c4
-rw-r--r--tools/perf/util/mem-events.c16
-rw-r--r--tools/perf/util/mem-events.h2
-rw-r--r--tools/perf/util/metricgroup.c10
-rw-r--r--tools/perf/util/parse-events.c723
-rw-r--r--tools/perf/util/parse-events.h104
-rw-r--r--tools/perf/util/parse-events.l44
-rw-r--r--tools/perf/util/parse-events.y448
-rw-r--r--tools/perf/util/perf-regs-arch/Build9
-rw-r--r--tools/perf/util/perf-regs-arch/perf_regs_aarch64.c96
-rw-r--r--tools/perf/util/perf-regs-arch/perf_regs_arm.c60
-rw-r--r--tools/perf/util/perf-regs-arch/perf_regs_csky.c100
-rw-r--r--tools/perf/util/perf-regs-arch/perf_regs_loongarch.c91
-rw-r--r--tools/perf/util/perf-regs-arch/perf_regs_mips.c87
-rw-r--r--tools/perf/util/perf-regs-arch/perf_regs_powerpc.c145
-rw-r--r--tools/perf/util/perf-regs-arch/perf_regs_riscv.c92
-rw-r--r--tools/perf/util/perf-regs-arch/perf_regs_s390.c96
-rw-r--r--tools/perf/util/perf-regs-arch/perf_regs_x86.c98
-rw-r--r--tools/perf/util/perf_regs.c772
-rw-r--r--tools/perf/util/perf_regs.h56
-rw-r--r--tools/perf/util/pmu.c936
-rw-r--r--tools/perf/util/pmu.h122
-rw-r--r--tools/perf/util/pmu.y35
-rw-r--r--tools/perf/util/pmus.c326
-rw-r--r--tools/perf/util/pmus.h2
-rw-r--r--tools/perf/util/print-events.h1
-rw-r--r--tools/perf/util/probe-event.c30
-rw-r--r--tools/perf/util/probe-event.h1
-rw-r--r--tools/perf/util/python-ext-sources9
-rw-r--r--tools/perf/util/python.c5
-rw-r--r--tools/perf/util/s390-sample-raw.c51
-rw-r--r--tools/perf/util/scripting-engines/Build3
-rw-r--r--tools/perf/util/session.c4
-rw-r--r--tools/perf/util/setup.py3
-rw-r--r--tools/perf/util/stat-display.c4
-rw-r--r--tools/perf/util/stat.c2
-rw-r--r--tools/perf/util/svghelper.c2
-rw-r--r--tools/perf/util/symbol-elf.c2
-rw-r--r--tools/perf/util/symbol.c15
-rw-r--r--tools/perf/util/synthetic-events.c2
-rw-r--r--tools/perf/util/thread.c13
-rw-r--r--tools/perf/util/thread.h2
-rw-r--r--tools/perf/util/unwind-libdw.c8
-rw-r--r--tools/perf/util/unwind-libunwind-local.c6
-rw-r--r--tools/perf/util/unwind.h8
79 files changed, 3129 insertions, 6166 deletions
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 96f4ea1d45c5..6d657c9927f7 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -1,3 +1,6 @@
+include $(srctree)/tools/scripts/Makefile.include
+include $(srctree)/tools/scripts/utilities.mak
+
perf-y += arm64-frame-pointer-unwind-support.o
perf-y += addr_location.o
perf-y += annotate.o
@@ -20,13 +23,13 @@ perf-y += evswitch.o
perf-y += find_bit.o
perf-y += get_current_dir_name.o
perf-y += levenshtein.o
-perf-y += llvm-utils.o
perf-y += mmap.o
perf-y += memswap.o
perf-y += parse-events.o
perf-y += print-events.o
perf-y += tracepoint.o
perf-y += perf_regs.o
+perf-y += perf-regs-arch/
perf-y += path.o
perf-y += print_binary.o
perf-y += rlimit.o
@@ -147,7 +150,6 @@ perf-y += list_sort.o
perf-y += mutex.o
perf-y += sharded_mutex.o
-perf-$(CONFIG_LIBBPF) += bpf-loader.o
perf-$(CONFIG_LIBBPF) += bpf_map.o
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_counter.o
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_counter_cgroup.o
@@ -165,7 +167,6 @@ ifeq ($(CONFIG_LIBTRACEEVENT),y)
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_kwork.o
endif
-perf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o
perf-$(CONFIG_LIBELF) += symbol-elf.o
perf-$(CONFIG_LIBELF) += probe-file.o
perf-$(CONFIG_LIBELF) += probe-event.o
@@ -229,12 +230,9 @@ perf-y += perf-hooks.o
perf-$(CONFIG_LIBBPF) += bpf-event.o
perf-$(CONFIG_LIBBPF) += bpf-utils.o
-perf-$(CONFIG_CXX) += c++/
-
perf-$(CONFIG_LIBPFM4) += pfm.o
CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
-CFLAGS_llvm-utils.o += -DLIBBPF_INCLUDE_DIR="BUILD_STR($(libbpf_include_dir_SQ))"
# avoid compiler warnings in 32-bit mode
CFLAGS_genelf_debug.o += -Wno-packed
@@ -246,7 +244,7 @@ $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-flex.h: util/parse-
$(OUTPUT)util/parse-events-bison.c $(OUTPUT)util/parse-events-bison.h: util/parse-events.y
$(call rule_mkdir)
- $(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) \
+ $(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) $(BISON_FALLBACK_FLAGS) \
-o $(OUTPUT)util/parse-events-bison.c -p parse_events_
$(OUTPUT)util/expr-flex.c $(OUTPUT)util/expr-flex.h: util/expr.l $(OUTPUT)util/expr-bison.c
@@ -279,28 +277,58 @@ $(OUTPUT)util/bpf-filter-bison.c $(OUTPUT)util/bpf-filter-bison.h: util/bpf-filt
$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) \
-o $(OUTPUT)util/bpf-filter-bison.c -p perf_bpf_filter_
-FLEX_GE_26 := $(shell expr $(shell $(FLEX) --version | sed -e 's/flex \([0-9]\+\).\([0-9]\+\)/\1\2/g') \>\= 26)
-ifeq ($(FLEX_GE_26),1)
- flex_flags := -Wno-switch-enum -Wno-switch-default -Wno-unused-function -Wno-redundant-decls -Wno-sign-compare -Wno-unused-parameter -Wno-missing-prototypes -Wno-missing-declarations
- CC_HASNT_MISLEADING_INDENTATION := $(shell echo "int main(void) { return 0 }" | $(CC) -Werror -Wno-misleading-indentation -o /dev/null -xc - 2>&1 | grep -q -- -Wno-misleading-indentation ; echo $$?)
- ifeq ($(CC_HASNT_MISLEADING_INDENTATION), 1)
- flex_flags += -Wno-misleading-indentation
+FLEX_VERSION := $(shell $(FLEX) --version | cut -d' ' -f2)
+
+FLEX_GE_260 := $(call version-ge3,$(FLEX_VERSION),2.6.0)
+ifeq ($(FLEX_GE_260),1)
+ flex_flags := -Wno-redundant-decls -Wno-switch-default -Wno-unused-function -Wno-misleading-indentation
+
+ # Some newer clang and gcc version complain about this
+ # util/parse-events-bison.c:1317:9: error: variable 'parse_events_nerrs' set but not used [-Werror,-Wunused-but-set-variable]
+ # int yynerrs = 0;
+
+ flex_flags += -Wno-unused-but-set-variable
+
+ FLEX_LT_262 := $(call version-lt3,$(FLEX_VERSION),2.6.2)
+ ifeq ($(FLEX_LT_262),1)
+ flex_flags += -Wno-sign-compare
endif
else
flex_flags := -w
endif
-CFLAGS_parse-events-flex.o += $(flex_flags)
-CFLAGS_pmu-flex.o += $(flex_flags)
-CFLAGS_expr-flex.o += $(flex_flags)
-CFLAGS_bpf-filter-flex.o += $(flex_flags)
-bison_flags := -DYYENABLE_NLS=0
-BISON_GE_35 := $(shell expr $(shell $(BISON) --version | grep bison | sed -e 's/.\+ \([0-9]\+\).\([0-9]\+\)/\1\2/g') \>\= 35)
-ifeq ($(BISON_GE_35),1)
- bison_flags += -Wno-unused-parameter -Wno-nested-externs -Wno-implicit-function-declaration -Wno-switch-enum -Wno-unused-but-set-variable -Wno-unknown-warning-option
+# Some newer clang and gcc version complain about this
+# util/parse-events-bison.c:1317:9: error: variable 'parse_events_nerrs' set but not used [-Werror,-Wunused-but-set-variable]
+# int yynerrs = 0;
+
+bison_flags := -DYYENABLE_NLS=0 -Wno-unused-but-set-variable
+
+# Old clangs don't grok -Wno-unused-but-set-variable, remove it
+ifeq ($(CC_NO_CLANG), 0)
+ CLANG_VERSION := $(shell $(CLANG) --version | head -1 | sed 's/.*clang version \([[:digit:]]\+.[[:digit:]]\+.[[:digit:]]\+\).*/\1/g')
+ ifeq ($(call version-lt3,$(CLANG_VERSION),13.0.0),1)
+ bison_flags := $(subst -Wno-unused-but-set-variable,,$(bison_flags))
+ flex_flags := $(subst -Wno-unused-but-set-variable,,$(flex_flags))
+ endif
+endif
+
+BISON_GE_382 := $(shell expr $(shell $(BISON) --version | grep bison | sed -e 's/.\+ \([0-9]\+\).\([0-9]\+\).\([0-9]\+\)/\1\2\3/g') \>\= 382)
+ifeq ($(BISON_GE_382),1)
+ bison_flags += -Wno-switch-enum
else
bison_flags += -w
endif
+
+BISON_LT_381 := $(shell expr $(shell $(BISON) --version | grep bison | sed -e 's/.\+ \([0-9]\+\).\([0-9]\+\).\([0-9]\+\)/\1\2\3/g') \< 381)
+ifeq ($(BISON_LT_381),1)
+ bison_flags += -DYYNOMEM=YYABORT
+endif
+
+CFLAGS_parse-events-flex.o += $(flex_flags) -Wno-unused-label
+CFLAGS_pmu-flex.o += $(flex_flags)
+CFLAGS_expr-flex.o += $(flex_flags)
+CFLAGS_bpf-filter-flex.o += $(flex_flags)
+
CFLAGS_parse-events-bison.o += $(bison_flags)
CFLAGS_pmu-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
CFLAGS_expr-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
@@ -316,8 +344,6 @@ CFLAGS_find_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ET
CFLAGS_rbtree.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
CFLAGS_libstring.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
CFLAGS_hweight.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
-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/
diff --git a/tools/perf/util/amd-sample-raw.c b/tools/perf/util/amd-sample-raw.c
index 6a6ddba76c75..9d0ce88e90e4 100644
--- a/tools/perf/util/amd-sample-raw.c
+++ b/tools/perf/util/amd-sample-raw.c
@@ -15,7 +15,6 @@
#include "session.h"
#include "evlist.h"
#include "sample-raw.h"
-#include "pmu-events/pmu-events.h"
#include "util/sample.h"
static u32 cpu_family, cpu_model, ibs_fetch_type, ibs_op_type;
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index ba988a13dacb..82956adf9963 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -1846,8 +1846,11 @@ static int symbol__disassemble_bpf(struct symbol *sym,
perf_exe(tpath, sizeof(tpath));
bfdf = bfd_openr(tpath, NULL);
- assert(bfdf);
- assert(bfd_check_format(bfdf, bfd_object));
+ if (bfdf == NULL)
+ abort();
+
+ if (!bfd_check_format(bfdf, bfd_object))
+ abort();
s = open_memstream(&buf, &buf_size);
if (!s) {
@@ -1895,7 +1898,8 @@ static int symbol__disassemble_bpf(struct symbol *sym,
#else
disassemble = disassembler(bfdf);
#endif
- assert(disassemble);
+ if (disassemble == NULL)
+ abort();
fflush(s);
do {
diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c
index 0b30688d78a7..b51544996046 100644
--- a/tools/perf/util/bpf-filter.c
+++ b/tools/perf/util/bpf-filter.c
@@ -9,8 +9,8 @@
#include "util/evsel.h"
#include "util/bpf-filter.h"
-#include "util/bpf-filter-flex.h"
-#include "util/bpf-filter-bison.h"
+#include <util/bpf-filter-flex.h>
+#include <util/bpf-filter-bison.h>
#include "bpf_skel/sample-filter.h"
#include "bpf_skel/sample_filter.skel.h"
@@ -62,6 +62,16 @@ static int check_sample_flags(struct evsel *evsel, struct perf_bpf_filter_expr *
if (evsel->core.attr.sample_type & expr->sample_flags)
return 0;
+ if (expr->op == PBF_OP_GROUP_BEGIN) {
+ struct perf_bpf_filter_expr *group;
+
+ list_for_each_entry(group, &expr->groups, list) {
+ if (check_sample_flags(evsel, group) < 0)
+ return -1;
+ }
+ return 0;
+ }
+
info = get_sample_info(expr->sample_flags);
if (info == NULL) {
pr_err("Error: %s event does not have sample flags %lx\n",
diff --git a/tools/perf/util/bpf-filter.y b/tools/perf/util/bpf-filter.y
index 07d6c7926c13..5dfa948fc986 100644
--- a/tools/perf/util/bpf-filter.y
+++ b/tools/perf/util/bpf-filter.y
@@ -9,6 +9,8 @@
#include <linux/list.h>
#include "bpf-filter.h"
+int perf_bpf_filter_lex(void);
+
static void perf_bpf_filter_error(struct list_head *expr __maybe_unused,
char const *msg)
{
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
deleted file mode 100644
index 44cde27d6389..000000000000
--- a/tools/perf/util/bpf-loader.c
+++ /dev/null
@@ -1,2110 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * bpf-loader.c
- *
- * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
- * Copyright (C) 2015 Huawei Inc.
- */
-
-#include <linux/bpf.h>
-#include <bpf/libbpf.h>
-#include <bpf/bpf.h>
-#include <linux/filter.h>
-#include <linux/err.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/zalloc.h>
-#include <errno.h>
-#include <stdlib.h>
-#include "debug.h"
-#include "evlist.h"
-#include "bpf-loader.h"
-#include "bpf-prologue.h"
-#include "probe-event.h"
-#include "probe-finder.h" // for MAX_PROBES
-#include "parse-events.h"
-#include "strfilter.h"
-#include "util.h"
-#include "llvm-utils.h"
-#include "c++/clang-c.h"
-#include "util/hashmap.h"
-#include "asm/bug.h"
-
-#include <internal/xyarray.h>
-
-/* temporarily disable libbpf deprecation warnings */
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-
-static int libbpf_perf_print(enum libbpf_print_level level __attribute__((unused)),
- const char *fmt, va_list args)
-{
- return veprintf(1, verbose, pr_fmt(fmt), args);
-}
-
-struct bpf_prog_priv {
- bool is_tp;
- char *sys_name;
- char *evt_name;
- struct perf_probe_event pev;
- bool need_prologue;
- struct bpf_insn *insns_buf;
- int nr_types;
- int *type_mapping;
- int *prologue_fds;
-};
-
-struct bpf_perf_object {
- struct list_head list;
- struct bpf_object *obj;
-};
-
-struct bpf_preproc_result {
- struct bpf_insn *new_insn_ptr;
- int new_insn_cnt;
-};
-
-static LIST_HEAD(bpf_objects_list);
-static struct hashmap *bpf_program_hash;
-static struct hashmap *bpf_map_hash;
-
-static struct bpf_perf_object *
-bpf_perf_object__next(struct bpf_perf_object *prev)
-{
- if (!prev) {
- if (list_empty(&bpf_objects_list))
- return NULL;
-
- return list_first_entry(&bpf_objects_list, struct bpf_perf_object, list);
- }
- if (list_is_last(&prev->list, &bpf_objects_list))
- return NULL;
-
- return list_next_entry(prev, list);
-}
-
-#define bpf_perf_object__for_each(perf_obj, tmp) \
- for ((perf_obj) = bpf_perf_object__next(NULL), \
- (tmp) = bpf_perf_object__next(perf_obj); \
- (perf_obj) != NULL; \
- (perf_obj) = (tmp), (tmp) = bpf_perf_object__next(tmp))
-
-static bool libbpf_initialized;
-static int libbpf_sec_handler;
-
-static int bpf_perf_object__add(struct bpf_object *obj)
-{
- struct bpf_perf_object *perf_obj = zalloc(sizeof(*perf_obj));
-
- if (perf_obj) {
- INIT_LIST_HEAD(&perf_obj->list);
- perf_obj->obj = obj;
- list_add_tail(&perf_obj->list, &bpf_objects_list);
- }
- return perf_obj ? 0 : -ENOMEM;
-}
-
-static void *program_priv(const struct bpf_program *prog)
-{
- void *priv;
-
- if (IS_ERR_OR_NULL(bpf_program_hash))
- return NULL;
- if (!hashmap__find(bpf_program_hash, prog, &priv))
- return NULL;
- return priv;
-}
-
-static struct bpf_insn prologue_init_insn[] = {
- BPF_MOV64_IMM(BPF_REG_2, 0),
- BPF_MOV64_IMM(BPF_REG_3, 0),
- BPF_MOV64_IMM(BPF_REG_4, 0),
- BPF_MOV64_IMM(BPF_REG_5, 0),
-};
-
-static int libbpf_prog_prepare_load_fn(struct bpf_program *prog,
- struct bpf_prog_load_opts *opts __maybe_unused,
- long cookie __maybe_unused)
-{
- size_t init_size_cnt = ARRAY_SIZE(prologue_init_insn);
- size_t orig_insn_cnt, insn_cnt, init_size, orig_size;
- struct bpf_prog_priv *priv = program_priv(prog);
- const struct bpf_insn *orig_insn;
- struct bpf_insn *insn;
-
- if (IS_ERR_OR_NULL(priv)) {
- pr_debug("bpf: failed to get private field\n");
- return -BPF_LOADER_ERRNO__INTERNAL;
- }
-
- if (!priv->need_prologue)
- return 0;
-
- /* prepend initialization code to program instructions */
- orig_insn = bpf_program__insns(prog);
- orig_insn_cnt = bpf_program__insn_cnt(prog);
- init_size = init_size_cnt * sizeof(*insn);
- orig_size = orig_insn_cnt * sizeof(*insn);
-
- insn_cnt = orig_insn_cnt + init_size_cnt;
- insn = malloc(insn_cnt * sizeof(*insn));
- if (!insn)
- return -ENOMEM;
-
- memcpy(insn, prologue_init_insn, init_size);
- memcpy((char *) insn + init_size, orig_insn, orig_size);
- bpf_program__set_insns(prog, insn, insn_cnt);
- return 0;
-}
-
-static int libbpf_init(void)
-{
- LIBBPF_OPTS(libbpf_prog_handler_opts, handler_opts,
- .prog_prepare_load_fn = libbpf_prog_prepare_load_fn,
- );
-
- if (libbpf_initialized)
- return 0;
-
- libbpf_set_print(libbpf_perf_print);
- libbpf_sec_handler = libbpf_register_prog_handler(NULL, BPF_PROG_TYPE_KPROBE,
- 0, &handler_opts);
- if (libbpf_sec_handler < 0) {
- pr_debug("bpf: failed to register libbpf section handler: %d\n",
- libbpf_sec_handler);
- return -BPF_LOADER_ERRNO__INTERNAL;
- }
- libbpf_initialized = true;
- return 0;
-}
-
-struct bpf_object *
-bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name)
-{
- LIBBPF_OPTS(bpf_object_open_opts, opts, .object_name = name);
- struct bpf_object *obj;
- int err;
-
- err = libbpf_init();
- if (err)
- return ERR_PTR(err);
-
- obj = bpf_object__open_mem(obj_buf, obj_buf_sz, &opts);
- if (IS_ERR_OR_NULL(obj)) {
- pr_debug("bpf: failed to load buffer\n");
- return ERR_PTR(-EINVAL);
- }
-
- if (bpf_perf_object__add(obj)) {
- bpf_object__close(obj);
- return ERR_PTR(-ENOMEM);
- }
-
- return obj;
-}
-
-static void bpf_perf_object__close(struct bpf_perf_object *perf_obj)
-{
- list_del(&perf_obj->list);
- bpf_object__close(perf_obj->obj);
- free(perf_obj);
-}
-
-struct bpf_object *bpf__prepare_load(const char *filename, bool source)
-{
- LIBBPF_OPTS(bpf_object_open_opts, opts, .object_name = filename);
- struct bpf_object *obj;
- int err;
-
- err = libbpf_init();
- if (err)
- return ERR_PTR(err);
-
- if (source) {
- void *obj_buf;
- size_t obj_buf_sz;
-
- perf_clang__init();
- err = perf_clang__compile_bpf(filename, &obj_buf, &obj_buf_sz);
- perf_clang__cleanup();
- if (err) {
- pr_debug("bpf: builtin compilation failed: %d, try external compiler\n", err);
- err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz);
- if (err)
- return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE);
- } else
- pr_debug("bpf: successful builtin compilation\n");
- obj = bpf_object__open_mem(obj_buf, obj_buf_sz, &opts);
-
- if (!IS_ERR_OR_NULL(obj) && llvm_param.dump_obj)
- llvm__dump_obj(filename, obj_buf, obj_buf_sz);
-
- free(obj_buf);
- } else {
- obj = bpf_object__open(filename);
- }
-
- if (IS_ERR_OR_NULL(obj)) {
- pr_debug("bpf: failed to load %s\n", filename);
- return obj;
- }
-
- if (bpf_perf_object__add(obj)) {
- bpf_object__close(obj);
- return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE);
- }
-
- return obj;
-}
-
-static void close_prologue_programs(struct bpf_prog_priv *priv)
-{
- struct perf_probe_event *pev;
- int i, fd;
-
- if (!priv->need_prologue)
- return;
- pev = &priv->pev;
- for (i = 0; i < pev->ntevs; i++) {
- fd = priv->prologue_fds[i];
- if (fd != -1)
- close(fd);
- }
-}
-
-static void
-clear_prog_priv(const struct bpf_program *prog __maybe_unused,
- void *_priv)
-{
- struct bpf_prog_priv *priv = _priv;
-
- close_prologue_programs(priv);
- cleanup_perf_probe_events(&priv->pev, 1);
- zfree(&priv->insns_buf);
- zfree(&priv->prologue_fds);
- zfree(&priv->type_mapping);
- zfree(&priv->sys_name);
- zfree(&priv->evt_name);
- free(priv);
-}
-
-static void bpf_program_hash_free(void)
-{
- struct hashmap_entry *cur;
- size_t bkt;
-
- if (IS_ERR_OR_NULL(bpf_program_hash))
- return;
-
- hashmap__for_each_entry(bpf_program_hash, cur, bkt)
- clear_prog_priv(cur->pkey, cur->pvalue);
-
- hashmap__free(bpf_program_hash);
- bpf_program_hash = NULL;
-}
-
-static void bpf_map_hash_free(void);
-
-void bpf__clear(void)
-{
- struct bpf_perf_object *perf_obj, *tmp;
-
- bpf_perf_object__for_each(perf_obj, tmp) {
- bpf__unprobe(perf_obj->obj);
- bpf_perf_object__close(perf_obj);
- }
-
- bpf_program_hash_free();
- bpf_map_hash_free();
-}
-
-static size_t ptr_hash(const long __key, void *ctx __maybe_unused)
-{
- return __key;
-}
-
-static bool ptr_equal(long key1, long key2, void *ctx __maybe_unused)
-{
- return key1 == key2;
-}
-
-static int program_set_priv(struct bpf_program *prog, void *priv)
-{
- void *old_priv;
-
- /*
- * Should not happen, we warn about it in the
- * caller function - config_bpf_program
- */
- if (IS_ERR(bpf_program_hash))
- return PTR_ERR(bpf_program_hash);
-
- if (!bpf_program_hash) {
- bpf_program_hash = hashmap__new(ptr_hash, ptr_equal, NULL);
- if (IS_ERR(bpf_program_hash))
- return PTR_ERR(bpf_program_hash);
- }
-
- old_priv = program_priv(prog);
- if (old_priv) {
- clear_prog_priv(prog, old_priv);
- return hashmap__set(bpf_program_hash, prog, priv, NULL, NULL);
- }
- return hashmap__add(bpf_program_hash, prog, priv);
-}
-
-static int
-prog_config__exec(const char *value, struct perf_probe_event *pev)
-{
- pev->uprobes = true;
- pev->target = strdup(value);
- if (!pev->target)
- return -ENOMEM;
- return 0;
-}
-
-static int
-prog_config__module(const char *value, struct perf_probe_event *pev)
-{
- pev->uprobes = false;
- pev->target = strdup(value);
- if (!pev->target)
- return -ENOMEM;
- return 0;
-}
-
-static int
-prog_config__bool(const char *value, bool *pbool, bool invert)
-{
- int err;
- bool bool_value;
-
- if (!pbool)
- return -EINVAL;
-
- err = strtobool(value, &bool_value);
- if (err)
- return err;
-
- *pbool = invert ? !bool_value : bool_value;
- return 0;
-}
-
-static int
-prog_config__inlines(const char *value,
- struct perf_probe_event *pev __maybe_unused)
-{
- return prog_config__bool(value, &probe_conf.no_inlines, true);
-}
-
-static int
-prog_config__force(const char *value,
- struct perf_probe_event *pev __maybe_unused)
-{
- return prog_config__bool(value, &probe_conf.force_add, false);
-}
-
-static struct {
- const char *key;
- const char *usage;
- const char *desc;
- int (*func)(const char *, struct perf_probe_event *);
-} bpf_prog_config_terms[] = {
- {
- .key = "exec",
- .usage = "exec=<full path of file>",
- .desc = "Set uprobe target",
- .func = prog_config__exec,
- },
- {
- .key = "module",
- .usage = "module=<module name> ",
- .desc = "Set kprobe module",
- .func = prog_config__module,
- },
- {
- .key = "inlines",
- .usage = "inlines=[yes|no] ",
- .desc = "Probe at inline symbol",
- .func = prog_config__inlines,
- },
- {
- .key = "force",
- .usage = "force=[yes|no] ",
- .desc = "Forcibly add events with existing name",
- .func = prog_config__force,
- },
-};
-
-static int
-do_prog_config(const char *key, const char *value,
- struct perf_probe_event *pev)
-{
- unsigned int i;
-
- pr_debug("config bpf program: %s=%s\n", key, value);
- for (i = 0; i < ARRAY_SIZE(bpf_prog_config_terms); i++)
- if (strcmp(key, bpf_prog_config_terms[i].key) == 0)
- return bpf_prog_config_terms[i].func(value, pev);
-
- pr_debug("BPF: ERROR: invalid program config option: %s=%s\n",
- key, value);
-
- pr_debug("\nHint: Valid options are:\n");
- for (i = 0; i < ARRAY_SIZE(bpf_prog_config_terms); i++)
- pr_debug("\t%s:\t%s\n", bpf_prog_config_terms[i].usage,
- bpf_prog_config_terms[i].desc);
- pr_debug("\n");
-
- return -BPF_LOADER_ERRNO__PROGCONF_TERM;
-}
-
-static const char *
-parse_prog_config_kvpair(const char *config_str, struct perf_probe_event *pev)
-{
- char *text = strdup(config_str);
- char *sep, *line;
- const char *main_str = NULL;
- int err = 0;
-
- if (!text) {
- pr_debug("Not enough memory: dup config_str failed\n");
- return ERR_PTR(-ENOMEM);
- }
-
- line = text;
- while ((sep = strchr(line, ';'))) {
- char *equ;
-
- *sep = '\0';
- equ = strchr(line, '=');
- if (!equ) {
- pr_warning("WARNING: invalid config in BPF object: %s\n",
- line);
- pr_warning("\tShould be 'key=value'.\n");
- goto nextline;
- }
- *equ = '\0';
-
- err = do_prog_config(line, equ + 1, pev);
- if (err)
- break;
-nextline:
- line = sep + 1;
- }
-
- if (!err)
- main_str = config_str + (line - text);
- free(text);
-
- return err ? ERR_PTR(err) : main_str;
-}
-
-static int
-parse_prog_config(const char *config_str, const char **p_main_str,
- bool *is_tp, struct perf_probe_event *pev)
-{
- int err;
- const char *main_str = parse_prog_config_kvpair(config_str, pev);
-
- if (IS_ERR(main_str))
- return PTR_ERR(main_str);
-
- *p_main_str = main_str;
- if (!strchr(main_str, '=')) {
- /* Is a tracepoint event? */
- const char *s = strchr(main_str, ':');
-
- if (!s) {
- pr_debug("bpf: '%s' is not a valid tracepoint\n",
- config_str);
- return -BPF_LOADER_ERRNO__CONFIG;
- }
-
- *is_tp = true;
- return 0;
- }
-
- *is_tp = false;
- err = parse_perf_probe_command(main_str, pev);
- if (err < 0) {
- pr_debug("bpf: '%s' is not a valid config string\n",
- config_str);
- /* parse failed, don't need clear pev. */
- return -BPF_LOADER_ERRNO__CONFIG;
- }
- return 0;
-}
-
-static int
-config_bpf_program(struct bpf_program *prog)
-{
- struct perf_probe_event *pev = NULL;
- struct bpf_prog_priv *priv = NULL;
- const char *config_str, *main_str;
- bool is_tp = false;
- int err;
-
- /* Initialize per-program probing setting */
- probe_conf.no_inlines = false;
- probe_conf.force_add = false;
-
- priv = calloc(sizeof(*priv), 1);
- if (!priv) {
- pr_debug("bpf: failed to alloc priv\n");
- return -ENOMEM;
- }
- pev = &priv->pev;
-
- config_str = bpf_program__section_name(prog);
- pr_debug("bpf: config program '%s'\n", config_str);
- err = parse_prog_config(config_str, &main_str, &is_tp, pev);
- if (err)
- goto errout;
-
- if (is_tp) {
- char *s = strchr(main_str, ':');
-
- priv->is_tp = true;
- priv->sys_name = strndup(main_str, s - main_str);
- priv->evt_name = strdup(s + 1);
- goto set_priv;
- }
-
- if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
- pr_debug("bpf: '%s': group for event is set and not '%s'.\n",
- config_str, PERF_BPF_PROBE_GROUP);
- err = -BPF_LOADER_ERRNO__GROUP;
- goto errout;
- } else if (!pev->group)
- pev->group = strdup(PERF_BPF_PROBE_GROUP);
-
- if (!pev->group) {
- pr_debug("bpf: strdup failed\n");
- err = -ENOMEM;
- goto errout;
- }
-
- if (!pev->event) {
- pr_debug("bpf: '%s': event name is missing. Section name should be 'key=value'\n",
- config_str);
- err = -BPF_LOADER_ERRNO__EVENTNAME;
- goto errout;
- }
- pr_debug("bpf: config '%s' is ok\n", config_str);
-
-set_priv:
- err = program_set_priv(prog, priv);
- if (err) {
- pr_debug("Failed to set priv for program '%s'\n", config_str);
- goto errout;
- }
-
- return 0;
-
-errout:
- if (pev)
- clear_perf_probe_event(pev);
- free(priv);
- return err;
-}
-
-static int bpf__prepare_probe(void)
-{
- static int err = 0;
- static bool initialized = false;
-
- /*
- * Make err static, so if init failed the first, bpf__prepare_probe()
- * fails each time without calling init_probe_symbol_maps multiple
- * times.
- */
- if (initialized)
- return err;
-
- initialized = true;
- err = init_probe_symbol_maps(false);
- if (err < 0)
- pr_debug("Failed to init_probe_symbol_maps\n");
- probe_conf.max_probes = MAX_PROBES;
- return err;
-}
-
-static int
-preproc_gen_prologue(struct bpf_program *prog, int n,
- const struct bpf_insn *orig_insns, int orig_insns_cnt,
- struct bpf_preproc_result *res)
-{
- struct bpf_prog_priv *priv = program_priv(prog);
- struct probe_trace_event *tev;
- struct perf_probe_event *pev;
- struct bpf_insn *buf;
- size_t prologue_cnt = 0;
- int i, err;
-
- if (IS_ERR_OR_NULL(priv) || priv->is_tp)
- goto errout;
-
- pev = &priv->pev;
-
- if (n < 0 || n >= priv->nr_types)
- goto errout;
-
- /* Find a tev belongs to that type */
- for (i = 0; i < pev->ntevs; i++) {
- if (priv->type_mapping[i] == n)
- break;
- }
-
- if (i >= pev->ntevs) {
- pr_debug("Internal error: prologue type %d not found\n", n);
- return -BPF_LOADER_ERRNO__PROLOGUE;
- }
-
- tev = &pev->tevs[i];
-
- buf = priv->insns_buf;
- err = bpf__gen_prologue(tev->args, tev->nargs,
- buf, &prologue_cnt,
- BPF_MAXINSNS - orig_insns_cnt);
- if (err) {
- const char *title;
-
- title = bpf_program__section_name(prog);
- pr_debug("Failed to generate prologue for program %s\n",
- title);
- return err;
- }
-
- memcpy(&buf[prologue_cnt], orig_insns,
- sizeof(struct bpf_insn) * orig_insns_cnt);
-
- res->new_insn_ptr = buf;
- res->new_insn_cnt = prologue_cnt + orig_insns_cnt;
- return 0;
-
-errout:
- pr_debug("Internal error in preproc_gen_prologue\n");
- return -BPF_LOADER_ERRNO__PROLOGUE;
-}
-
-/*
- * compare_tev_args is reflexive, transitive and antisymmetric.
- * I can proof it but this margin is too narrow to contain.
- */
-static int compare_tev_args(const void *ptev1, const void *ptev2)
-{
- int i, ret;
- const struct probe_trace_event *tev1 =
- *(const struct probe_trace_event **)ptev1;
- const struct probe_trace_event *tev2 =
- *(const struct probe_trace_event **)ptev2;
-
- ret = tev2->nargs - tev1->nargs;
- if (ret)
- return ret;
-
- for (i = 0; i < tev1->nargs; i++) {
- struct probe_trace_arg *arg1, *arg2;
- struct probe_trace_arg_ref *ref1, *ref2;
-
- arg1 = &tev1->args[i];
- arg2 = &tev2->args[i];
-
- ret = strcmp(arg1->value, arg2->value);
- if (ret)
- return ret;
-
- ref1 = arg1->ref;
- ref2 = arg2->ref;
-
- while (ref1 && ref2) {
- ret = ref2->offset - ref1->offset;
- if (ret)
- return ret;
-
- ref1 = ref1->next;
- ref2 = ref2->next;
- }
-
- if (ref1 || ref2)
- return ref2 ? 1 : -1;
- }
-
- return 0;
-}
-
-/*
- * Assign a type number to each tevs in a pev.
- * mapping is an array with same slots as tevs in that pev.
- * nr_types will be set to number of types.
- */
-static int map_prologue(struct perf_probe_event *pev, int *mapping,
- int *nr_types)
-{
- int i, type = 0;
- struct probe_trace_event **ptevs;
-
- size_t array_sz = sizeof(*ptevs) * pev->ntevs;
-
- ptevs = malloc(array_sz);
- if (!ptevs) {
- pr_debug("Not enough memory: alloc ptevs failed\n");
- return -ENOMEM;
- }
-
- pr_debug("In map_prologue, ntevs=%d\n", pev->ntevs);
- for (i = 0; i < pev->ntevs; i++)
- ptevs[i] = &pev->tevs[i];
-
- qsort(ptevs, pev->ntevs, sizeof(*ptevs),
- compare_tev_args);
-
- for (i = 0; i < pev->ntevs; i++) {
- int n;
-
- n = ptevs[i] - pev->tevs;
- if (i == 0) {
- mapping[n] = type;
- pr_debug("mapping[%d]=%d\n", n, type);
- continue;
- }
-
- if (compare_tev_args(ptevs + i, ptevs + i - 1) == 0)
- mapping[n] = type;
- else
- mapping[n] = ++type;
-
- pr_debug("mapping[%d]=%d\n", n, mapping[n]);
- }
- free(ptevs);
- *nr_types = type + 1;
-
- return 0;
-}
-
-static int hook_load_preprocessor(struct bpf_program *prog)
-{
- struct bpf_prog_priv *priv = program_priv(prog);
- struct perf_probe_event *pev;
- bool need_prologue = false;
- int i;
-
- if (IS_ERR_OR_NULL(priv)) {
- pr_debug("Internal error when hook preprocessor\n");
- return -BPF_LOADER_ERRNO__INTERNAL;
- }
-
- if (priv->is_tp) {
- priv->need_prologue = false;
- return 0;
- }
-
- pev = &priv->pev;
- for (i = 0; i < pev->ntevs; i++) {
- struct probe_trace_event *tev = &pev->tevs[i];
-
- if (tev->nargs > 0) {
- need_prologue = true;
- break;
- }
- }
-
- /*
- * Since all tevs don't have argument, we don't need generate
- * prologue.
- */
- if (!need_prologue) {
- priv->need_prologue = false;
- return 0;
- }
-
- priv->need_prologue = true;
- priv->insns_buf = malloc(sizeof(struct bpf_insn) * BPF_MAXINSNS);
- if (!priv->insns_buf) {
- pr_debug("Not enough memory: alloc insns_buf failed\n");
- return -ENOMEM;
- }
-
- priv->prologue_fds = malloc(sizeof(int) * pev->ntevs);
- if (!priv->prologue_fds) {
- pr_debug("Not enough memory: alloc prologue fds failed\n");
- return -ENOMEM;
- }
- memset(priv->prologue_fds, -1, sizeof(int) * pev->ntevs);
-
- priv->type_mapping = malloc(sizeof(int) * pev->ntevs);
- if (!priv->type_mapping) {
- pr_debug("Not enough memory: alloc type_mapping failed\n");
- return -ENOMEM;
- }
- memset(priv->type_mapping, -1,
- sizeof(int) * pev->ntevs);
-
- return map_prologue(pev, priv->type_mapping, &priv->nr_types);
-}
-
-int bpf__probe(struct bpf_object *obj)
-{
- int err = 0;
- struct bpf_program *prog;
- struct bpf_prog_priv *priv;
- struct perf_probe_event *pev;
-
- err = bpf__prepare_probe();
- if (err) {
- pr_debug("bpf__prepare_probe failed\n");
- return err;
- }
-
- bpf_object__for_each_program(prog, obj) {
- err = config_bpf_program(prog);
- if (err)
- goto out;
-
- priv = program_priv(prog);
- if (IS_ERR_OR_NULL(priv)) {
- if (!priv)
- err = -BPF_LOADER_ERRNO__INTERNAL;
- else
- err = PTR_ERR(priv);
- goto out;
- }
-
- if (priv->is_tp) {
- bpf_program__set_type(prog, BPF_PROG_TYPE_TRACEPOINT);
- continue;
- }
-
- bpf_program__set_type(prog, BPF_PROG_TYPE_KPROBE);
- pev = &priv->pev;
-
- err = convert_perf_probe_events(pev, 1);
- if (err < 0) {
- pr_debug("bpf_probe: failed to convert perf probe events\n");
- goto out;
- }
-
- err = apply_perf_probe_events(pev, 1);
- if (err < 0) {
- pr_debug("bpf_probe: failed to apply perf probe events\n");
- goto out;
- }
-
- /*
- * After probing, let's consider prologue, which
- * adds program fetcher to BPF programs.
- *
- * hook_load_preprocessor() hooks pre-processor
- * to bpf_program, let it generate prologue
- * dynamically during loading.
- */
- err = hook_load_preprocessor(prog);
- if (err)
- goto out;
- }
-out:
- return err < 0 ? err : 0;
-}
-
-#define EVENTS_WRITE_BUFSIZE 4096
-int bpf__unprobe(struct bpf_object *obj)
-{
- int err, ret = 0;
- struct bpf_program *prog;
-
- bpf_object__for_each_program(prog, obj) {
- struct bpf_prog_priv *priv = program_priv(prog);
- int i;
-
- if (IS_ERR_OR_NULL(priv) || priv->is_tp)
- continue;
-
- for (i = 0; i < priv->pev.ntevs; i++) {
- struct probe_trace_event *tev = &priv->pev.tevs[i];
- char name_buf[EVENTS_WRITE_BUFSIZE];
- struct strfilter *delfilter;
-
- snprintf(name_buf, EVENTS_WRITE_BUFSIZE,
- "%s:%s", tev->group, tev->event);
- name_buf[EVENTS_WRITE_BUFSIZE - 1] = '\0';
-
- delfilter = strfilter__new(name_buf, NULL);
- if (!delfilter) {
- pr_debug("Failed to create filter for unprobing\n");
- ret = -ENOMEM;
- continue;
- }
-
- err = del_perf_probe_events(delfilter);
- strfilter__delete(delfilter);
- if (err) {
- pr_debug("Failed to delete %s\n", name_buf);
- ret = err;
- continue;
- }
- }
- }
- return ret;
-}
-
-static int bpf_object__load_prologue(struct bpf_object *obj)
-{
- int init_cnt = ARRAY_SIZE(prologue_init_insn);
- const struct bpf_insn *orig_insns;
- struct bpf_preproc_result res;
- struct perf_probe_event *pev;
- struct bpf_program *prog;
- int orig_insns_cnt;
-
- bpf_object__for_each_program(prog, obj) {
- struct bpf_prog_priv *priv = program_priv(prog);
- int err, i, fd;
-
- if (IS_ERR_OR_NULL(priv)) {
- pr_debug("bpf: failed to get private field\n");
- return -BPF_LOADER_ERRNO__INTERNAL;
- }
-
- if (!priv->need_prologue)
- continue;
-
- /*
- * For each program that needs prologue we do following:
- *
- * - take its current instructions and use them
- * to generate the new code with prologue
- * - load new instructions with bpf_prog_load
- * and keep the fd in prologue_fds
- * - new fd will be used in bpf__foreach_event
- * to connect this program with perf evsel
- */
- orig_insns = bpf_program__insns(prog);
- orig_insns_cnt = bpf_program__insn_cnt(prog);
-
- pev = &priv->pev;
- for (i = 0; i < pev->ntevs; i++) {
- /*
- * Skipping artificall prologue_init_insn instructions
- * (init_cnt), so the prologue can be generated instead
- * of them.
- */
- err = preproc_gen_prologue(prog, i,
- orig_insns + init_cnt,
- orig_insns_cnt - init_cnt,
- &res);
- if (err)
- return err;
-
- fd = bpf_prog_load(bpf_program__get_type(prog),
- bpf_program__name(prog), "GPL",
- res.new_insn_ptr,
- res.new_insn_cnt, NULL);
- if (fd < 0) {
- char bf[128];
-
- libbpf_strerror(-errno, bf, sizeof(bf));
- pr_debug("bpf: load objects with prologue failed: err=%d: (%s)\n",
- -errno, bf);
- return -errno;
- }
- priv->prologue_fds[i] = fd;
- }
- /*
- * We no longer need the original program,
- * we can unload it.
- */
- bpf_program__unload(prog);
- }
- return 0;
-}
-
-int bpf__load(struct bpf_object *obj)
-{
- int err;
-
- err = bpf_object__load(obj);
- if (err) {
- char bf[128];
- libbpf_strerror(err, bf, sizeof(bf));
- pr_debug("bpf: load objects failed: err=%d: (%s)\n", err, bf);
- return err;
- }
- return bpf_object__load_prologue(obj);
-}
-
-int bpf__foreach_event(struct bpf_object *obj,
- bpf_prog_iter_callback_t func,
- void *arg)
-{
- struct bpf_program *prog;
- int err;
-
- bpf_object__for_each_program(prog, obj) {
- struct bpf_prog_priv *priv = program_priv(prog);
- struct probe_trace_event *tev;
- struct perf_probe_event *pev;
- int i, fd;
-
- if (IS_ERR_OR_NULL(priv)) {
- pr_debug("bpf: failed to get private field\n");
- return -BPF_LOADER_ERRNO__INTERNAL;
- }
-
- if (priv->is_tp) {
- fd = bpf_program__fd(prog);
- err = (*func)(priv->sys_name, priv->evt_name, fd, obj, arg);
- if (err) {
- pr_debug("bpf: tracepoint call back failed, stop iterate\n");
- return err;
- }
- continue;
- }
-
- pev = &priv->pev;
- for (i = 0; i < pev->ntevs; i++) {
- tev = &pev->tevs[i];
-
- if (priv->need_prologue)
- fd = priv->prologue_fds[i];
- else
- fd = bpf_program__fd(prog);
-
- if (fd < 0) {
- pr_debug("bpf: failed to get file descriptor\n");
- return fd;
- }
-
- err = (*func)(tev->group, tev->event, fd, obj, arg);
- if (err) {
- pr_debug("bpf: call back failed, stop iterate\n");
- return err;
- }
- }
- }
- return 0;
-}
-
-enum bpf_map_op_type {
- BPF_MAP_OP_SET_VALUE,
- BPF_MAP_OP_SET_EVSEL,
-};
-
-enum bpf_map_key_type {
- BPF_MAP_KEY_ALL,
- BPF_MAP_KEY_RANGES,
-};
-
-struct bpf_map_op {
- struct list_head list;
- enum bpf_map_op_type op_type;
- enum bpf_map_key_type key_type;
- union {
- struct parse_events_array array;
- } k;
- union {
- u64 value;
- struct evsel *evsel;
- } v;
-};
-
-struct bpf_map_priv {
- struct list_head ops_list;
-};
-
-static void
-bpf_map_op__delete(struct bpf_map_op *op)
-{
- if (!list_empty(&op->list))
- list_del_init(&op->list);
- if (op->key_type == BPF_MAP_KEY_RANGES)
- parse_events__clear_array(&op->k.array);
- free(op);
-}
-
-static void
-bpf_map_priv__purge(struct bpf_map_priv *priv)
-{
- struct bpf_map_op *pos, *n;
-
- list_for_each_entry_safe(pos, n, &priv->ops_list, list) {
- list_del_init(&pos->list);
- bpf_map_op__delete(pos);
- }
-}
-
-static void
-bpf_map_priv__clear(const struct bpf_map *map __maybe_unused,
- void *_priv)
-{
- struct bpf_map_priv *priv = _priv;
-
- bpf_map_priv__purge(priv);
- free(priv);
-}
-
-static void *map_priv(const struct bpf_map *map)
-{
- void *priv;
-
- if (IS_ERR_OR_NULL(bpf_map_hash))
- return NULL;
- if (!hashmap__find(bpf_map_hash, map, &priv))
- return NULL;
- return priv;
-}
-
-static void bpf_map_hash_free(void)
-{
- struct hashmap_entry *cur;
- size_t bkt;
-
- if (IS_ERR_OR_NULL(bpf_map_hash))
- return;
-
- hashmap__for_each_entry(bpf_map_hash, cur, bkt)
- bpf_map_priv__clear(cur->pkey, cur->pvalue);
-
- hashmap__free(bpf_map_hash);
- bpf_map_hash = NULL;
-}
-
-static int map_set_priv(struct bpf_map *map, void *priv)
-{
- void *old_priv;
-
- if (WARN_ON_ONCE(IS_ERR(bpf_map_hash)))
- return PTR_ERR(bpf_program_hash);
-
- if (!bpf_map_hash) {
- bpf_map_hash = hashmap__new(ptr_hash, ptr_equal, NULL);
- if (IS_ERR(bpf_map_hash))
- return PTR_ERR(bpf_map_hash);
- }
-
- old_priv = map_priv(map);
- if (old_priv) {
- bpf_map_priv__clear(map, old_priv);
- return hashmap__set(bpf_map_hash, map, priv, NULL, NULL);
- }
- return hashmap__add(bpf_map_hash, map, priv);
-}
-
-static int
-bpf_map_op_setkey(struct bpf_map_op *op, struct parse_events_term *term)
-{
- op->key_type = BPF_MAP_KEY_ALL;
- if (!term)
- return 0;
-
- if (term->array.nr_ranges) {
- size_t memsz = term->array.nr_ranges *
- sizeof(op->k.array.ranges[0]);
-
- op->k.array.ranges = memdup(term->array.ranges, memsz);
- if (!op->k.array.ranges) {
- pr_debug("Not enough memory to alloc indices for map\n");
- return -ENOMEM;
- }
- op->key_type = BPF_MAP_KEY_RANGES;
- op->k.array.nr_ranges = term->array.nr_ranges;
- }
- return 0;
-}
-
-static struct bpf_map_op *
-bpf_map_op__new(struct parse_events_term *term)
-{
- struct bpf_map_op *op;
- int err;
-
- op = zalloc(sizeof(*op));
- if (!op) {
- pr_debug("Failed to alloc bpf_map_op\n");
- return ERR_PTR(-ENOMEM);
- }
- INIT_LIST_HEAD(&op->list);
-
- err = bpf_map_op_setkey(op, term);
- if (err) {
- free(op);
- return ERR_PTR(err);
- }
- return op;
-}
-
-static struct bpf_map_op *
-bpf_map_op__clone(struct bpf_map_op *op)
-{
- struct bpf_map_op *newop;
-
- newop = memdup(op, sizeof(*op));
- if (!newop) {
- pr_debug("Failed to alloc bpf_map_op\n");
- return NULL;
- }
-
- INIT_LIST_HEAD(&newop->list);
- if (op->key_type == BPF_MAP_KEY_RANGES) {
- size_t memsz = op->k.array.nr_ranges *
- sizeof(op->k.array.ranges[0]);
-
- newop->k.array.ranges = memdup(op->k.array.ranges, memsz);
- if (!newop->k.array.ranges) {
- pr_debug("Failed to alloc indices for map\n");
- free(newop);
- return NULL;
- }
- }
-
- return newop;
-}
-
-static struct bpf_map_priv *
-bpf_map_priv__clone(struct bpf_map_priv *priv)
-{
- struct bpf_map_priv *newpriv;
- struct bpf_map_op *pos, *newop;
-
- newpriv = zalloc(sizeof(*newpriv));
- if (!newpriv) {
- pr_debug("Not enough memory to alloc map private\n");
- return NULL;
- }
- INIT_LIST_HEAD(&newpriv->ops_list);
-
- list_for_each_entry(pos, &priv->ops_list, list) {
- newop = bpf_map_op__clone(pos);
- if (!newop) {
- bpf_map_priv__purge(newpriv);
- return NULL;
- }
- list_add_tail(&newop->list, &newpriv->ops_list);
- }
-
- return newpriv;
-}
-
-static int
-bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op)
-{
- const char *map_name = bpf_map__name(map);
- struct bpf_map_priv *priv = map_priv(map);
-
- if (IS_ERR(priv)) {
- pr_debug("Failed to get private from map %s\n", map_name);
- return PTR_ERR(priv);
- }
-
- if (!priv) {
- priv = zalloc(sizeof(*priv));
- if (!priv) {
- pr_debug("Not enough memory to alloc map private\n");
- return -ENOMEM;
- }
- INIT_LIST_HEAD(&priv->ops_list);
-
- if (map_set_priv(map, priv)) {
- free(priv);
- return -BPF_LOADER_ERRNO__INTERNAL;
- }
- }
-
- list_add_tail(&op->list, &priv->ops_list);
- return 0;
-}
-
-static struct bpf_map_op *
-bpf_map__add_newop(struct bpf_map *map, struct parse_events_term *term)
-{
- struct bpf_map_op *op;
- int err;
-
- op = bpf_map_op__new(term);
- if (IS_ERR(op))
- return op;
-
- err = bpf_map__add_op(map, op);
- if (err) {
- bpf_map_op__delete(op);
- return ERR_PTR(err);
- }
- return op;
-}
-
-static int
-__bpf_map__config_value(struct bpf_map *map,
- struct parse_events_term *term)
-{
- struct bpf_map_op *op;
- const char *map_name = bpf_map__name(map);
-
- if (!map) {
- pr_debug("Map '%s' is invalid\n", map_name);
- return -BPF_LOADER_ERRNO__INTERNAL;
- }
-
- if (bpf_map__type(map) != BPF_MAP_TYPE_ARRAY) {
- pr_debug("Map %s type is not BPF_MAP_TYPE_ARRAY\n",
- map_name);
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
- }
- if (bpf_map__key_size(map) < sizeof(unsigned int)) {
- pr_debug("Map %s has incorrect key size\n", map_name);
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE;
- }
- switch (bpf_map__value_size(map)) {
- case 1:
- case 2:
- case 4:
- case 8:
- break;
- default:
- pr_debug("Map %s has incorrect value size\n", map_name);
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE;
- }
-
- op = bpf_map__add_newop(map, term);
- if (IS_ERR(op))
- return PTR_ERR(op);
- op->op_type = BPF_MAP_OP_SET_VALUE;
- op->v.value = term->val.num;
- return 0;
-}
-
-static int
-bpf_map__config_value(struct bpf_map *map,
- struct parse_events_term *term,
- struct evlist *evlist __maybe_unused)
-{
- if (!term->err_val) {
- pr_debug("Config value not set\n");
- return -BPF_LOADER_ERRNO__OBJCONF_CONF;
- }
-
- if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM) {
- pr_debug("ERROR: wrong value type for 'value'\n");
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE;
- }
-
- return __bpf_map__config_value(map, term);
-}
-
-static int
-__bpf_map__config_event(struct bpf_map *map,
- struct parse_events_term *term,
- struct evlist *evlist)
-{
- struct bpf_map_op *op;
- const char *map_name = bpf_map__name(map);
- struct evsel *evsel = evlist__find_evsel_by_str(evlist, term->val.str);
-
- if (!evsel) {
- pr_debug("Event (for '%s') '%s' doesn't exist\n",
- map_name, term->val.str);
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT;
- }
-
- if (!map) {
- pr_debug("Map '%s' is invalid\n", map_name);
- return PTR_ERR(map);
- }
-
- /*
- * No need to check key_size and value_size:
- * kernel has already checked them.
- */
- if (bpf_map__type(map) != BPF_MAP_TYPE_PERF_EVENT_ARRAY) {
- pr_debug("Map %s type is not BPF_MAP_TYPE_PERF_EVENT_ARRAY\n",
- map_name);
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
- }
-
- op = bpf_map__add_newop(map, term);
- if (IS_ERR(op))
- return PTR_ERR(op);
- op->op_type = BPF_MAP_OP_SET_EVSEL;
- op->v.evsel = evsel;
- return 0;
-}
-
-static int
-bpf_map__config_event(struct bpf_map *map,
- struct parse_events_term *term,
- struct evlist *evlist)
-{
- if (!term->err_val) {
- pr_debug("Config value not set\n");
- return -BPF_LOADER_ERRNO__OBJCONF_CONF;
- }
-
- if (term->type_val != PARSE_EVENTS__TERM_TYPE_STR) {
- pr_debug("ERROR: wrong value type for 'event'\n");
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE;
- }
-
- return __bpf_map__config_event(map, term, evlist);
-}
-
-struct bpf_obj_config__map_func {
- const char *config_opt;
- int (*config_func)(struct bpf_map *, struct parse_events_term *,
- struct evlist *);
-};
-
-struct bpf_obj_config__map_func bpf_obj_config__map_funcs[] = {
- {"value", bpf_map__config_value},
- {"event", bpf_map__config_event},
-};
-
-static int
-config_map_indices_range_check(struct parse_events_term *term,
- struct bpf_map *map,
- const char *map_name)
-{
- struct parse_events_array *array = &term->array;
- unsigned int i;
-
- if (!array->nr_ranges)
- return 0;
- if (!array->ranges) {
- pr_debug("ERROR: map %s: array->nr_ranges is %d but range array is NULL\n",
- map_name, (int)array->nr_ranges);
- return -BPF_LOADER_ERRNO__INTERNAL;
- }
-
- if (!map) {
- pr_debug("Map '%s' is invalid\n", map_name);
- return -BPF_LOADER_ERRNO__INTERNAL;
- }
-
- for (i = 0; i < array->nr_ranges; i++) {
- unsigned int start = array->ranges[i].start;
- size_t length = array->ranges[i].length;
- unsigned int idx = start + length - 1;
-
- if (idx >= bpf_map__max_entries(map)) {
- pr_debug("ERROR: index %d too large\n", idx);
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG;
- }
- }
- return 0;
-}
-
-static int
-bpf__obj_config_map(struct bpf_object *obj,
- struct parse_events_term *term,
- struct evlist *evlist,
- int *key_scan_pos)
-{
- /* key is "map:<mapname>.<config opt>" */
- char *map_name = strdup(term->config + sizeof("map:") - 1);
- struct bpf_map *map;
- int err = -BPF_LOADER_ERRNO__OBJCONF_OPT;
- char *map_opt;
- size_t i;
-
- if (!map_name)
- return -ENOMEM;
-
- map_opt = strchr(map_name, '.');
- if (!map_opt) {
- pr_debug("ERROR: Invalid map config: %s\n", map_name);
- goto out;
- }
-
- *map_opt++ = '\0';
- if (*map_opt == '\0') {
- pr_debug("ERROR: Invalid map option: %s\n", term->config);
- goto out;
- }
-
- map = bpf_object__find_map_by_name(obj, map_name);
- if (!map) {
- pr_debug("ERROR: Map %s doesn't exist\n", map_name);
- err = -BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST;
- goto out;
- }
-
- *key_scan_pos += strlen(map_opt);
- err = config_map_indices_range_check(term, map, map_name);
- if (err)
- goto out;
- *key_scan_pos -= strlen(map_opt);
-
- for (i = 0; i < ARRAY_SIZE(bpf_obj_config__map_funcs); i++) {
- struct bpf_obj_config__map_func *func =
- &bpf_obj_config__map_funcs[i];
-
- if (strcmp(map_opt, func->config_opt) == 0) {
- err = func->config_func(map, term, evlist);
- goto out;
- }
- }
-
- pr_debug("ERROR: Invalid map config option '%s'\n", map_opt);
- err = -BPF_LOADER_ERRNO__OBJCONF_MAP_OPT;
-out:
- if (!err)
- *key_scan_pos += strlen(map_opt);
-
- free(map_name);
- return err;
-}
-
-int bpf__config_obj(struct bpf_object *obj,
- struct parse_events_term *term,
- struct evlist *evlist,
- int *error_pos)
-{
- int key_scan_pos = 0;
- int err;
-
- if (!obj || !term || !term->config)
- return -EINVAL;
-
- if (strstarts(term->config, "map:")) {
- key_scan_pos = sizeof("map:") - 1;
- err = bpf__obj_config_map(obj, term, evlist, &key_scan_pos);
- goto out;
- }
- err = -BPF_LOADER_ERRNO__OBJCONF_OPT;
-out:
- if (error_pos)
- *error_pos = key_scan_pos;
- return err;
-
-}
-
-typedef int (*map_config_func_t)(const char *name, int map_fd,
- const struct bpf_map *map,
- struct bpf_map_op *op,
- void *pkey, void *arg);
-
-static int
-foreach_key_array_all(map_config_func_t func,
- void *arg, const char *name,
- int map_fd, const struct bpf_map *map,
- struct bpf_map_op *op)
-{
- unsigned int i;
- int err;
-
- for (i = 0; i < bpf_map__max_entries(map); i++) {
- err = func(name, map_fd, map, op, &i, arg);
- if (err) {
- pr_debug("ERROR: failed to insert value to %s[%u]\n",
- name, i);
- return err;
- }
- }
- return 0;
-}
-
-static int
-foreach_key_array_ranges(map_config_func_t func, void *arg,
- const char *name, int map_fd,
- const struct bpf_map *map,
- struct bpf_map_op *op)
-{
- unsigned int i, j;
- int err;
-
- for (i = 0; i < op->k.array.nr_ranges; i++) {
- unsigned int start = op->k.array.ranges[i].start;
- size_t length = op->k.array.ranges[i].length;
-
- for (j = 0; j < length; j++) {
- unsigned int idx = start + j;
-
- err = func(name, map_fd, map, op, &idx, arg);
- if (err) {
- pr_debug("ERROR: failed to insert value to %s[%u]\n",
- name, idx);
- return err;
- }
- }
- }
- return 0;
-}
-
-static int
-bpf_map_config_foreach_key(struct bpf_map *map,
- map_config_func_t func,
- void *arg)
-{
- int err, map_fd, type;
- struct bpf_map_op *op;
- const char *name = bpf_map__name(map);
- struct bpf_map_priv *priv = map_priv(map);
-
- if (IS_ERR(priv)) {
- pr_debug("ERROR: failed to get private from map %s\n", name);
- return -BPF_LOADER_ERRNO__INTERNAL;
- }
- if (!priv || list_empty(&priv->ops_list)) {
- pr_debug("INFO: nothing to config for map %s\n", name);
- return 0;
- }
-
- if (!map) {
- pr_debug("Map '%s' is invalid\n", name);
- return -BPF_LOADER_ERRNO__INTERNAL;
- }
- map_fd = bpf_map__fd(map);
- if (map_fd < 0) {
- pr_debug("ERROR: failed to get fd from map %s\n", name);
- return map_fd;
- }
-
- type = bpf_map__type(map);
- list_for_each_entry(op, &priv->ops_list, list) {
- switch (type) {
- case BPF_MAP_TYPE_ARRAY:
- case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
- switch (op->key_type) {
- case BPF_MAP_KEY_ALL:
- err = foreach_key_array_all(func, arg, name,
- map_fd, map, op);
- break;
- case BPF_MAP_KEY_RANGES:
- err = foreach_key_array_ranges(func, arg, name,
- map_fd, map, op);
- break;
- default:
- pr_debug("ERROR: keytype for map '%s' invalid\n",
- name);
- return -BPF_LOADER_ERRNO__INTERNAL;
- }
- if (err)
- return err;
- break;
- default:
- pr_debug("ERROR: type of '%s' incorrect\n", name);
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
- }
- }
-
- return 0;
-}
-
-static int
-apply_config_value_for_key(int map_fd, void *pkey,
- size_t val_size, u64 val)
-{
- int err = 0;
-
- switch (val_size) {
- case 1: {
- u8 _val = (u8)(val);
- err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY);
- break;
- }
- case 2: {
- u16 _val = (u16)(val);
- err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY);
- break;
- }
- case 4: {
- u32 _val = (u32)(val);
- err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY);
- break;
- }
- case 8: {
- err = bpf_map_update_elem(map_fd, pkey, &val, BPF_ANY);
- break;
- }
- default:
- pr_debug("ERROR: invalid value size\n");
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE;
- }
- if (err && errno)
- err = -errno;
- return err;
-}
-
-static int
-apply_config_evsel_for_key(const char *name, int map_fd, void *pkey,
- struct evsel *evsel)
-{
- struct xyarray *xy = evsel->core.fd;
- struct perf_event_attr *attr;
- unsigned int key, events;
- bool check_pass = false;
- int *evt_fd;
- int err;
-
- if (!xy) {
- pr_debug("ERROR: evsel not ready for map %s\n", name);
- return -BPF_LOADER_ERRNO__INTERNAL;
- }
-
- if (xy->row_size / xy->entry_size != 1) {
- pr_debug("ERROR: Dimension of target event is incorrect for map %s\n",
- name);
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM;
- }
-
- attr = &evsel->core.attr;
- if (attr->inherit) {
- pr_debug("ERROR: Can't put inherit event into map %s\n", name);
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH;
- }
-
- if (evsel__is_bpf_output(evsel))
- check_pass = true;
- if (attr->type == PERF_TYPE_RAW)
- check_pass = true;
- if (attr->type == PERF_TYPE_HARDWARE)
- check_pass = true;
- if (!check_pass) {
- pr_debug("ERROR: Event type is wrong for map %s\n", name);
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE;
- }
-
- events = xy->entries / (xy->row_size / xy->entry_size);
- key = *((unsigned int *)pkey);
- if (key >= events) {
- pr_debug("ERROR: there is no event %d for map %s\n",
- key, name);
- return -BPF_LOADER_ERRNO__OBJCONF_MAP_MAPSIZE;
- }
- evt_fd = xyarray__entry(xy, key, 0);
- err = bpf_map_update_elem(map_fd, pkey, evt_fd, BPF_ANY);
- if (err && errno)
- err = -errno;
- return err;
-}
-
-static int
-apply_obj_config_map_for_key(const char *name, int map_fd,
- const struct bpf_map *map,
- struct bpf_map_op *op,
- void *pkey, void *arg __maybe_unused)
-{
- int err;
-
- switch (op->op_type) {
- case BPF_MAP_OP_SET_VALUE:
- err = apply_config_value_for_key(map_fd, pkey,
- bpf_map__value_size(map),
- op->v.value);
- break;
- case BPF_MAP_OP_SET_EVSEL:
- err = apply_config_evsel_for_key(name, map_fd, pkey,
- op->v.evsel);
- break;
- default:
- pr_debug("ERROR: unknown value type for '%s'\n", name);
- err = -BPF_LOADER_ERRNO__INTERNAL;
- }
- return err;
-}
-
-static int
-apply_obj_config_map(struct bpf_map *map)
-{
- return bpf_map_config_foreach_key(map,
- apply_obj_config_map_for_key,
- NULL);
-}
-
-static int
-apply_obj_config_object(struct bpf_object *obj)
-{
- struct bpf_map *map;
- int err;
-
- bpf_object__for_each_map(map, obj) {
- err = apply_obj_config_map(map);
- if (err)
- return err;
- }
- return 0;
-}
-
-int bpf__apply_obj_config(void)
-{
- struct bpf_perf_object *perf_obj, *tmp;
- int err;
-
- bpf_perf_object__for_each(perf_obj, tmp) {
- err = apply_obj_config_object(perf_obj->obj);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-#define bpf__perf_for_each_map(map, pobj, tmp) \
- bpf_perf_object__for_each(pobj, tmp) \
- bpf_object__for_each_map(map, pobj->obj)
-
-#define bpf__perf_for_each_map_named(map, pobj, pobjtmp, name) \
- bpf__perf_for_each_map(map, pobj, pobjtmp) \
- if (bpf_map__name(map) && (strcmp(name, bpf_map__name(map)) == 0))
-
-struct evsel *bpf__setup_output_event(struct evlist *evlist, const char *name)
-{
- struct bpf_map_priv *tmpl_priv = NULL;
- struct bpf_perf_object *perf_obj, *tmp;
- struct evsel *evsel = NULL;
- struct bpf_map *map;
- int err;
- bool need_init = false;
-
- bpf__perf_for_each_map_named(map, perf_obj, tmp, name) {
- struct bpf_map_priv *priv = map_priv(map);
-
- if (IS_ERR(priv))
- return ERR_PTR(-BPF_LOADER_ERRNO__INTERNAL);
-
- /*
- * No need to check map type: type should have been
- * verified by kernel.
- */
- if (!need_init && !priv)
- need_init = !priv;
- if (!tmpl_priv && priv)
- tmpl_priv = priv;
- }
-
- if (!need_init)
- return NULL;
-
- if (!tmpl_priv) {
- char *event_definition = NULL;
-
- if (asprintf(&event_definition, "bpf-output/no-inherit=1,name=%s/", name) < 0)
- return ERR_PTR(-ENOMEM);
-
- err = parse_event(evlist, event_definition);
- free(event_definition);
-
- if (err) {
- pr_debug("ERROR: failed to create the \"%s\" bpf-output event\n", name);
- return ERR_PTR(-err);
- }
-
- evsel = evlist__last(evlist);
- }
-
- bpf__perf_for_each_map_named(map, perf_obj, tmp, name) {
- struct bpf_map_priv *priv = map_priv(map);
-
- if (IS_ERR(priv))
- return ERR_PTR(-BPF_LOADER_ERRNO__INTERNAL);
- if (priv)
- continue;
-
- if (tmpl_priv) {
- priv = bpf_map_priv__clone(tmpl_priv);
- if (!priv)
- return ERR_PTR(-ENOMEM);
-
- err = map_set_priv(map, priv);
- if (err) {
- bpf_map_priv__clear(map, priv);
- return ERR_PTR(err);
- }
- } else if (evsel) {
- struct bpf_map_op *op;
-
- op = bpf_map__add_newop(map, NULL);
- if (IS_ERR(op))
- return ERR_CAST(op);
- op->op_type = BPF_MAP_OP_SET_EVSEL;
- op->v.evsel = evsel;
- }
- }
-
- return evsel;
-}
-
-int bpf__setup_stdout(struct evlist *evlist)
-{
- struct evsel *evsel = bpf__setup_output_event(evlist, "__bpf_stdout__");
- return PTR_ERR_OR_ZERO(evsel);
-}
-
-#define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START)
-#define ERRCODE_OFFSET(c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c)
-#define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START)
-
-static const char *bpf_loader_strerror_table[NR_ERRNO] = {
- [ERRCODE_OFFSET(CONFIG)] = "Invalid config string",
- [ERRCODE_OFFSET(GROUP)] = "Invalid group name",
- [ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string",
- [ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error",
- [ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet",
- [ERRCODE_OFFSET(PROGCONF_TERM)] = "Invalid program config term in config string",
- [ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue",
- [ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program",
- [ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue",
- [ERRCODE_OFFSET(OBJCONF_OPT)] = "Invalid object config option",
- [ERRCODE_OFFSET(OBJCONF_CONF)] = "Config value not set (missing '=')",
- [ERRCODE_OFFSET(OBJCONF_MAP_OPT)] = "Invalid object map config option",
- [ERRCODE_OFFSET(OBJCONF_MAP_NOTEXIST)] = "Target map doesn't exist",
- [ERRCODE_OFFSET(OBJCONF_MAP_VALUE)] = "Incorrect value type for map",
- [ERRCODE_OFFSET(OBJCONF_MAP_TYPE)] = "Incorrect map type",
- [ERRCODE_OFFSET(OBJCONF_MAP_KEYSIZE)] = "Incorrect map key size",
- [ERRCODE_OFFSET(OBJCONF_MAP_VALUESIZE)] = "Incorrect map value size",
- [ERRCODE_OFFSET(OBJCONF_MAP_NOEVT)] = "Event not found for map setting",
- [ERRCODE_OFFSET(OBJCONF_MAP_MAPSIZE)] = "Invalid map size for event setting",
- [ERRCODE_OFFSET(OBJCONF_MAP_EVTDIM)] = "Event dimension too large",
- [ERRCODE_OFFSET(OBJCONF_MAP_EVTINH)] = "Doesn't support inherit event",
- [ERRCODE_OFFSET(OBJCONF_MAP_EVTTYPE)] = "Wrong event type for map",
- [ERRCODE_OFFSET(OBJCONF_MAP_IDX2BIG)] = "Index too large",
-};
-
-static int
-bpf_loader_strerror(int err, char *buf, size_t size)
-{
- char sbuf[STRERR_BUFSIZE];
- const char *msg;
-
- if (!buf || !size)
- return -1;
-
- err = err > 0 ? err : -err;
-
- if (err >= __LIBBPF_ERRNO__START)
- return libbpf_strerror(err, buf, size);
-
- if (err >= __BPF_LOADER_ERRNO__START && err < __BPF_LOADER_ERRNO__END) {
- msg = bpf_loader_strerror_table[ERRNO_OFFSET(err)];
- snprintf(buf, size, "%s", msg);
- buf[size - 1] = '\0';
- return 0;
- }
-
- if (err >= __BPF_LOADER_ERRNO__END)
- snprintf(buf, size, "Unknown bpf loader error %d", err);
- else
- snprintf(buf, size, "%s",
- str_error_r(err, sbuf, sizeof(sbuf)));
-
- buf[size - 1] = '\0';
- return -1;
-}
-
-#define bpf__strerror_head(err, buf, size) \
- char sbuf[STRERR_BUFSIZE], *emsg;\
- if (!size)\
- return 0;\
- if (err < 0)\
- err = -err;\
- bpf_loader_strerror(err, sbuf, sizeof(sbuf));\
- emsg = sbuf;\
- switch (err) {\
- default:\
- scnprintf(buf, size, "%s", emsg);\
- break;
-
-#define bpf__strerror_entry(val, fmt...)\
- case val: {\
- scnprintf(buf, size, fmt);\
- break;\
- }
-
-#define bpf__strerror_end(buf, size)\
- }\
- buf[size - 1] = '\0';
-
-int bpf__strerror_prepare_load(const char *filename, bool source,
- int err, char *buf, size_t size)
-{
- size_t n;
- int ret;
-
- n = snprintf(buf, size, "Failed to load %s%s: ",
- filename, source ? " from source" : "");
- if (n >= size) {
- buf[size - 1] = '\0';
- return 0;
- }
- buf += n;
- size -= n;
-
- ret = bpf_loader_strerror(err, buf, size);
- buf[size - 1] = '\0';
- return ret;
-}
-
-int bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
- int err, char *buf, size_t size)
-{
- bpf__strerror_head(err, buf, size);
- case BPF_LOADER_ERRNO__PROGCONF_TERM: {
- scnprintf(buf, size, "%s (add -v to see detail)", emsg);
- break;
- }
- bpf__strerror_entry(EEXIST, "Probe point exist. Try 'perf probe -d \"*\"' and set 'force=yes'");
- bpf__strerror_entry(EACCES, "You need to be root");
- bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0");
- bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file");
- bpf__strerror_end(buf, size);
- return 0;
-}
-
-int bpf__strerror_load(struct bpf_object *obj,
- int err, char *buf, size_t size)
-{
- bpf__strerror_head(err, buf, size);
- case LIBBPF_ERRNO__KVER: {
- unsigned int obj_kver = bpf_object__kversion(obj);
- unsigned int real_kver;
-
- if (fetch_kernel_version(&real_kver, NULL, 0)) {
- scnprintf(buf, size, "Unable to fetch kernel version");
- break;
- }
-
- if (obj_kver != real_kver) {
- scnprintf(buf, size,
- "'version' ("KVER_FMT") doesn't match running kernel ("KVER_FMT")",
- KVER_PARAM(obj_kver),
- KVER_PARAM(real_kver));
- break;
- }
-
- scnprintf(buf, size, "Failed to load program for unknown reason");
- break;
- }
- bpf__strerror_end(buf, size);
- return 0;
-}
-
-int bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,
- struct parse_events_term *term __maybe_unused,
- struct evlist *evlist __maybe_unused,
- int *error_pos __maybe_unused, int err,
- char *buf, size_t size)
-{
- bpf__strerror_head(err, buf, size);
- bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE,
- "Can't use this config term with this map type");
- bpf__strerror_end(buf, size);
- return 0;
-}
-
-int bpf__strerror_apply_obj_config(int err, char *buf, size_t size)
-{
- bpf__strerror_head(err, buf, size);
- bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM,
- "Cannot set event to BPF map in multi-thread tracing");
- bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH,
- "%s (Hint: use -i to turn off inherit)", emsg);
- bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE,
- "Can only put raw, hardware and BPF output event into a BPF map");
- bpf__strerror_end(buf, size);
- return 0;
-}
-
-int bpf__strerror_setup_output_event(struct evlist *evlist __maybe_unused,
- int err, char *buf, size_t size)
-{
- bpf__strerror_head(err, buf, size);
- bpf__strerror_end(buf, size);
- return 0;
-}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
deleted file mode 100644
index 5d1c725cea29..000000000000
--- a/tools/perf/util/bpf-loader.h
+++ /dev/null
@@ -1,216 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (C) 2015, Wang Nan <wangnan0@huawei.com>
- * Copyright (C) 2015, Huawei Inc.
- */
-#ifndef __BPF_LOADER_H
-#define __BPF_LOADER_H
-
-#include <linux/compiler.h>
-#include <linux/err.h>
-
-#ifdef HAVE_LIBBPF_SUPPORT
-#include <bpf/libbpf.h>
-
-enum bpf_loader_errno {
- __BPF_LOADER_ERRNO__START = __LIBBPF_ERRNO__START - 100,
- /* Invalid config string */
- BPF_LOADER_ERRNO__CONFIG = __BPF_LOADER_ERRNO__START,
- BPF_LOADER_ERRNO__GROUP, /* Invalid group name */
- BPF_LOADER_ERRNO__EVENTNAME, /* Event name is missing */
- BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */
- BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */
- BPF_LOADER_ERRNO__PROGCONF_TERM,/* Invalid program config term in config string */
- BPF_LOADER_ERRNO__PROLOGUE, /* Failed to generate prologue */
- BPF_LOADER_ERRNO__PROLOGUE2BIG, /* Prologue too big for program */
- BPF_LOADER_ERRNO__PROLOGUEOOB, /* Offset out of bound for prologue */
- BPF_LOADER_ERRNO__OBJCONF_OPT, /* Invalid object config option */
- BPF_LOADER_ERRNO__OBJCONF_CONF, /* Config value not set (lost '=')) */
- BPF_LOADER_ERRNO__OBJCONF_MAP_OPT, /* Invalid object map config option */
- BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST, /* Target map not exist */
- BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE, /* Incorrect value type for map */
- BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE, /* Incorrect map type */
- BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE, /* Incorrect map key size */
- BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE,/* Incorrect map value size */
- BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT, /* Event not found for map setting */
- BPF_LOADER_ERRNO__OBJCONF_MAP_MAPSIZE, /* Invalid map size for event setting */
- BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM, /* Event dimension too large */
- BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH, /* Doesn't support inherit event */
- BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE, /* Wrong event type for map */
- BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG, /* Index too large */
- __BPF_LOADER_ERRNO__END,
-};
-#endif // HAVE_LIBBPF_SUPPORT
-
-struct evsel;
-struct evlist;
-struct bpf_object;
-struct parse_events_term;
-#define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
-
-typedef int (*bpf_prog_iter_callback_t)(const char *group, const char *event,
- int fd, struct bpf_object *obj, void *arg);
-
-#ifdef HAVE_LIBBPF_SUPPORT
-struct bpf_object *bpf__prepare_load(const char *filename, bool source);
-int bpf__strerror_prepare_load(const char *filename, bool source,
- int err, char *buf, size_t size);
-
-struct bpf_object *bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz,
- const char *name);
-
-void bpf__clear(void);
-
-int bpf__probe(struct bpf_object *obj);
-int bpf__unprobe(struct bpf_object *obj);
-int bpf__strerror_probe(struct bpf_object *obj, int err,
- char *buf, size_t size);
-
-int bpf__load(struct bpf_object *obj);
-int bpf__strerror_load(struct bpf_object *obj, int err,
- char *buf, size_t size);
-int bpf__foreach_event(struct bpf_object *obj,
- bpf_prog_iter_callback_t func, void *arg);
-
-int bpf__config_obj(struct bpf_object *obj, struct parse_events_term *term,
- struct evlist *evlist, int *error_pos);
-int bpf__strerror_config_obj(struct bpf_object *obj,
- struct parse_events_term *term,
- struct evlist *evlist,
- int *error_pos, int err, char *buf,
- size_t size);
-int bpf__apply_obj_config(void);
-int bpf__strerror_apply_obj_config(int err, char *buf, size_t size);
-
-int bpf__setup_stdout(struct evlist *evlist);
-struct evsel *bpf__setup_output_event(struct evlist *evlist, const char *name);
-int bpf__strerror_setup_output_event(struct evlist *evlist, int err, char *buf, size_t size);
-#else
-#include <errno.h>
-#include <string.h>
-#include "debug.h"
-
-static inline struct bpf_object *
-bpf__prepare_load(const char *filename __maybe_unused,
- bool source __maybe_unused)
-{
- pr_debug("ERROR: eBPF object loading is disabled during compiling.\n");
- return ERR_PTR(-ENOTSUP);
-}
-
-static inline struct bpf_object *
-bpf__prepare_load_buffer(void *obj_buf __maybe_unused,
- size_t obj_buf_sz __maybe_unused)
-{
- return ERR_PTR(-ENOTSUP);
-}
-
-static inline void bpf__clear(void) { }
-
-static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 0;}
-static inline int bpf__unprobe(struct bpf_object *obj __maybe_unused) { return 0;}
-static inline int bpf__load(struct bpf_object *obj __maybe_unused) { return 0; }
-
-static inline int
-bpf__foreach_event(struct bpf_object *obj __maybe_unused,
- bpf_prog_iter_callback_t func __maybe_unused,
- void *arg __maybe_unused)
-{
- return 0;
-}
-
-static inline int
-bpf__config_obj(struct bpf_object *obj __maybe_unused,
- struct parse_events_term *term __maybe_unused,
- struct evlist *evlist __maybe_unused,
- int *error_pos __maybe_unused)
-{
- return 0;
-}
-
-static inline int
-bpf__apply_obj_config(void)
-{
- return 0;
-}
-
-static inline int
-bpf__setup_stdout(struct evlist *evlist __maybe_unused)
-{
- return 0;
-}
-
-static inline struct evsel *
-bpf__setup_output_event(struct evlist *evlist __maybe_unused, const char *name __maybe_unused)
-{
- return NULL;
-}
-
-static inline int
-__bpf_strerror(char *buf, size_t size)
-{
- if (!size)
- return 0;
- strncpy(buf,
- "ERROR: eBPF object loading is disabled during compiling.\n",
- size);
- buf[size - 1] = '\0';
- return 0;
-}
-
-static inline
-int bpf__strerror_prepare_load(const char *filename __maybe_unused,
- bool source __maybe_unused,
- int err __maybe_unused,
- char *buf, size_t size)
-{
- return __bpf_strerror(buf, size);
-}
-
-static inline int
-bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
- int err __maybe_unused,
- char *buf, size_t size)
-{
- return __bpf_strerror(buf, size);
-}
-
-static inline int bpf__strerror_load(struct bpf_object *obj __maybe_unused,
- int err __maybe_unused,
- char *buf, size_t size)
-{
- return __bpf_strerror(buf, size);
-}
-
-static inline int
-bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,
- struct parse_events_term *term __maybe_unused,
- struct evlist *evlist __maybe_unused,
- int *error_pos __maybe_unused,
- int err __maybe_unused,
- char *buf, size_t size)
-{
- return __bpf_strerror(buf, size);
-}
-
-static inline int
-bpf__strerror_apply_obj_config(int err __maybe_unused,
- char *buf, size_t size)
-{
- return __bpf_strerror(buf, size);
-}
-
-static inline int
-bpf__strerror_setup_output_event(struct evlist *evlist __maybe_unused,
- int err __maybe_unused, char *buf, size_t size)
-{
- return __bpf_strerror(buf, size);
-}
-
-#endif
-
-static inline int bpf__strerror_setup_stdout(struct evlist *evlist, int err, char *buf, size_t size)
-{
- return bpf__strerror_setup_output_event(evlist, err, buf, size);
-}
-#endif
diff --git a/tools/perf/util/bpf-prologue.c b/tools/perf/util/bpf-prologue.c
deleted file mode 100644
index 9887ae09242d..000000000000
--- a/tools/perf/util/bpf-prologue.c
+++ /dev/null
@@ -1,508 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * bpf-prologue.c
- *
- * Copyright (C) 2015 He Kuang <hekuang@huawei.com>
- * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
- * Copyright (C) 2015 Huawei Inc.
- */
-
-#include <bpf/libbpf.h>
-#include "debug.h"
-#include "bpf-loader.h"
-#include "bpf-prologue.h"
-#include "probe-finder.h"
-#include <errno.h>
-#include <stdlib.h>
-#include <dwarf-regs.h>
-#include <linux/filter.h>
-
-#define BPF_REG_SIZE 8
-
-#define JMP_TO_ERROR_CODE -1
-#define JMP_TO_SUCCESS_CODE -2
-#define JMP_TO_USER_CODE -3
-
-struct bpf_insn_pos {
- struct bpf_insn *begin;
- struct bpf_insn *end;
- struct bpf_insn *pos;
-};
-
-static inline int
-pos_get_cnt(struct bpf_insn_pos *pos)
-{
- return pos->pos - pos->begin;
-}
-
-static int
-append_insn(struct bpf_insn new_insn, struct bpf_insn_pos *pos)
-{
- if (!pos->pos)
- return -BPF_LOADER_ERRNO__PROLOGUE2BIG;
-
- if (pos->pos + 1 >= pos->end) {
- pr_err("bpf prologue: prologue too long\n");
- pos->pos = NULL;
- return -BPF_LOADER_ERRNO__PROLOGUE2BIG;
- }
-
- *(pos->pos)++ = new_insn;
- return 0;
-}
-
-static int
-check_pos(struct bpf_insn_pos *pos)
-{
- if (!pos->pos || pos->pos >= pos->end)
- return -BPF_LOADER_ERRNO__PROLOGUE2BIG;
- return 0;
-}
-
-/*
- * Convert type string (u8/u16/u32/u64/s8/s16/s32/s64 ..., see
- * Documentation/trace/kprobetrace.rst) to size field of BPF_LDX_MEM
- * instruction (BPF_{B,H,W,DW}).
- */
-static int
-argtype_to_ldx_size(const char *type)
-{
- int arg_size = type ? atoi(&type[1]) : 64;
-
- switch (arg_size) {
- case 8:
- return BPF_B;
- case 16:
- return BPF_H;
- case 32:
- return BPF_W;
- case 64:
- default:
- return BPF_DW;
- }
-}
-
-static const char *
-insn_sz_to_str(int insn_sz)
-{
- switch (insn_sz) {
- case BPF_B:
- return "BPF_B";
- case BPF_H:
- return "BPF_H";
- case BPF_W:
- return "BPF_W";
- case BPF_DW:
- return "BPF_DW";
- default:
- return "UNKNOWN";
- }
-}
-
-/* Give it a shorter name */
-#define ins(i, p) append_insn((i), (p))
-
-/*
- * Give a register name (in 'reg'), generate instruction to
- * load register into an eBPF register rd:
- * 'ldd target_reg, offset(ctx_reg)', where:
- * ctx_reg is pre initialized to pointer of 'struct pt_regs'.
- */
-static int
-gen_ldx_reg_from_ctx(struct bpf_insn_pos *pos, int ctx_reg,
- const char *reg, int target_reg)
-{
- int offset = regs_query_register_offset(reg);
-
- if (offset < 0) {
- pr_err("bpf: prologue: failed to get register %s\n",
- reg);
- return offset;
- }
- ins(BPF_LDX_MEM(BPF_DW, target_reg, ctx_reg, offset), pos);
-
- return check_pos(pos);
-}
-
-/*
- * Generate a BPF_FUNC_probe_read function call.
- *
- * src_base_addr_reg is a register holding base address,
- * dst_addr_reg is a register holding dest address (on stack),
- * result is:
- *
- * *[dst_addr_reg] = *([src_base_addr_reg] + offset)
- *
- * Arguments of BPF_FUNC_probe_read:
- * ARG1: ptr to stack (dest)
- * ARG2: size (8)
- * ARG3: unsafe ptr (src)
- */
-static int
-gen_read_mem(struct bpf_insn_pos *pos,
- int src_base_addr_reg,
- int dst_addr_reg,
- long offset,
- int probeid)
-{
- /* mov arg3, src_base_addr_reg */
- if (src_base_addr_reg != BPF_REG_ARG3)
- ins(BPF_MOV64_REG(BPF_REG_ARG3, src_base_addr_reg), pos);
- /* add arg3, #offset */
- if (offset)
- ins(BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, offset), pos);
-
- /* mov arg2, #reg_size */
- ins(BPF_ALU64_IMM(BPF_MOV, BPF_REG_ARG2, BPF_REG_SIZE), pos);
-
- /* mov arg1, dst_addr_reg */
- if (dst_addr_reg != BPF_REG_ARG1)
- ins(BPF_MOV64_REG(BPF_REG_ARG1, dst_addr_reg), pos);
-
- /* Call probe_read */
- ins(BPF_EMIT_CALL(probeid), pos);
- /*
- * Error processing: if read fail, goto error code,
- * will be relocated. Target should be the start of
- * error processing code.
- */
- ins(BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, JMP_TO_ERROR_CODE),
- pos);
-
- return check_pos(pos);
-}
-
-/*
- * Each arg should be bare register. Fetch and save them into argument
- * registers (r3 - r5).
- *
- * BPF_REG_1 should have been initialized with pointer to
- * 'struct pt_regs'.
- */
-static int
-gen_prologue_fastpath(struct bpf_insn_pos *pos,
- struct probe_trace_arg *args, int nargs)
-{
- int i, err = 0;
-
- for (i = 0; i < nargs; i++) {
- err = gen_ldx_reg_from_ctx(pos, BPF_REG_1, args[i].value,
- BPF_PROLOGUE_START_ARG_REG + i);
- if (err)
- goto errout;
- }
-
- return check_pos(pos);
-errout:
- return err;
-}
-
-/*
- * Slow path:
- * At least one argument has the form of 'offset($rx)'.
- *
- * Following code first stores them into stack, then loads all of then
- * to r2 - r5.
- * Before final loading, the final result should be:
- *
- * low address
- * BPF_REG_FP - 24 ARG3
- * BPF_REG_FP - 16 ARG2
- * BPF_REG_FP - 8 ARG1
- * BPF_REG_FP
- * high address
- *
- * For each argument (described as: offn(...off2(off1(reg)))),
- * generates following code:
- *
- * r7 <- fp
- * r7 <- r7 - stack_offset // Ideal code should initialize r7 using
- * // fp before generating args. However,
- * // eBPF won't regard r7 as stack pointer
- * // if it is generated by minus 8 from
- * // another stack pointer except fp.
- * // This is why we have to set r7
- * // to fp for each variable.
- * r3 <- value of 'reg'-> generated using gen_ldx_reg_from_ctx()
- * (r7) <- r3 // skip following instructions for bare reg
- * r3 <- r3 + off1 . // skip if off1 == 0
- * r2 <- 8 \
- * r1 <- r7 |-> generated by gen_read_mem()
- * call probe_read /
- * jnei r0, 0, err ./
- * r3 <- (r7)
- * r3 <- r3 + off2 . // skip if off2 == 0
- * r2 <- 8 \ // r2 may be broken by probe_read, so set again
- * r1 <- r7 |-> generated by gen_read_mem()
- * call probe_read /
- * jnei r0, 0, err ./
- * ...
- */
-static int
-gen_prologue_slowpath(struct bpf_insn_pos *pos,
- struct probe_trace_arg *args, int nargs)
-{
- int err, i, probeid;
-
- for (i = 0; i < nargs; i++) {
- struct probe_trace_arg *arg = &args[i];
- const char *reg = arg->value;
- struct probe_trace_arg_ref *ref = NULL;
- int stack_offset = (i + 1) * -8;
-
- pr_debug("prologue: fetch arg %d, base reg is %s\n",
- i, reg);
-
- /* value of base register is stored into ARG3 */
- err = gen_ldx_reg_from_ctx(pos, BPF_REG_CTX, reg,
- BPF_REG_ARG3);
- if (err) {
- pr_err("prologue: failed to get offset of register %s\n",
- reg);
- goto errout;
- }
-
- /* Make r7 the stack pointer. */
- ins(BPF_MOV64_REG(BPF_REG_7, BPF_REG_FP), pos);
- /* r7 += -8 */
- ins(BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, stack_offset), pos);
- /*
- * Store r3 (base register) onto stack
- * Ensure fp[offset] is set.
- * fp is the only valid base register when storing
- * into stack. We are not allowed to use r7 as base
- * register here.
- */
- ins(BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG3,
- stack_offset), pos);
-
- ref = arg->ref;
- probeid = BPF_FUNC_probe_read_kernel;
- while (ref) {
- pr_debug("prologue: arg %d: offset %ld\n",
- i, ref->offset);
-
- if (ref->user_access)
- probeid = BPF_FUNC_probe_read_user;
-
- err = gen_read_mem(pos, BPF_REG_3, BPF_REG_7,
- ref->offset, probeid);
- if (err) {
- pr_err("prologue: failed to generate probe_read function call\n");
- goto errout;
- }
-
- ref = ref->next;
- /*
- * Load previous result into ARG3. Use
- * BPF_REG_FP instead of r7 because verifier
- * allows FP based addressing only.
- */
- if (ref)
- ins(BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3,
- BPF_REG_FP, stack_offset), pos);
- }
- }
-
- /* Final pass: read to registers */
- for (i = 0; i < nargs; i++) {
- int insn_sz = (args[i].ref) ? argtype_to_ldx_size(args[i].type) : BPF_DW;
-
- pr_debug("prologue: load arg %d, insn_sz is %s\n",
- i, insn_sz_to_str(insn_sz));
- ins(BPF_LDX_MEM(insn_sz, BPF_PROLOGUE_START_ARG_REG + i,
- BPF_REG_FP, -BPF_REG_SIZE * (i + 1)), pos);
- }
-
- ins(BPF_JMP_IMM(BPF_JA, BPF_REG_0, 0, JMP_TO_SUCCESS_CODE), pos);
-
- return check_pos(pos);
-errout:
- return err;
-}
-
-static int
-prologue_relocate(struct bpf_insn_pos *pos, struct bpf_insn *error_code,
- struct bpf_insn *success_code, struct bpf_insn *user_code)
-{
- struct bpf_insn *insn;
-
- if (check_pos(pos))
- return -BPF_LOADER_ERRNO__PROLOGUE2BIG;
-
- for (insn = pos->begin; insn < pos->pos; insn++) {
- struct bpf_insn *target;
- u8 class = BPF_CLASS(insn->code);
- u8 opcode;
-
- if (class != BPF_JMP)
- continue;
- opcode = BPF_OP(insn->code);
- if (opcode == BPF_CALL)
- continue;
-
- switch (insn->off) {
- case JMP_TO_ERROR_CODE:
- target = error_code;
- break;
- case JMP_TO_SUCCESS_CODE:
- target = success_code;
- break;
- case JMP_TO_USER_CODE:
- target = user_code;
- break;
- default:
- pr_err("bpf prologue: internal error: relocation failed\n");
- return -BPF_LOADER_ERRNO__PROLOGUE;
- }
-
- insn->off = target - (insn + 1);
- }
- return 0;
-}
-
-int bpf__gen_prologue(struct probe_trace_arg *args, int nargs,
- struct bpf_insn *new_prog, size_t *new_cnt,
- size_t cnt_space)
-{
- struct bpf_insn *success_code = NULL;
- struct bpf_insn *error_code = NULL;
- struct bpf_insn *user_code = NULL;
- struct bpf_insn_pos pos;
- bool fastpath = true;
- int err = 0, i;
-
- if (!new_prog || !new_cnt)
- return -EINVAL;
-
- if (cnt_space > BPF_MAXINSNS)
- cnt_space = BPF_MAXINSNS;
-
- pos.begin = new_prog;
- pos.end = new_prog + cnt_space;
- pos.pos = new_prog;
-
- if (!nargs) {
- ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 0),
- &pos);
-
- if (check_pos(&pos))
- goto errout;
-
- *new_cnt = pos_get_cnt(&pos);
- return 0;
- }
-
- if (nargs > BPF_PROLOGUE_MAX_ARGS) {
- pr_warning("bpf: prologue: %d arguments are dropped\n",
- nargs - BPF_PROLOGUE_MAX_ARGS);
- nargs = BPF_PROLOGUE_MAX_ARGS;
- }
-
- /* First pass: validation */
- for (i = 0; i < nargs; i++) {
- struct probe_trace_arg_ref *ref = args[i].ref;
-
- if (args[i].value[0] == '@') {
- /* TODO: fetch global variable */
- pr_err("bpf: prologue: global %s%+ld not support\n",
- args[i].value, ref ? ref->offset : 0);
- return -ENOTSUP;
- }
-
- while (ref) {
- /* fastpath is true if all args has ref == NULL */
- fastpath = false;
-
- /*
- * Instruction encodes immediate value using
- * s32, ref->offset is long. On systems which
- * can't fill long in s32, refuse to process if
- * ref->offset too large (or small).
- */
-#ifdef __LP64__
-#define OFFSET_MAX ((1LL << 31) - 1)
-#define OFFSET_MIN ((1LL << 31) * -1)
- if (ref->offset > OFFSET_MAX ||
- ref->offset < OFFSET_MIN) {
- pr_err("bpf: prologue: offset out of bound: %ld\n",
- ref->offset);
- return -BPF_LOADER_ERRNO__PROLOGUEOOB;
- }
-#endif
- ref = ref->next;
- }
- }
- pr_debug("prologue: pass validation\n");
-
- if (fastpath) {
- /* If all variables are registers... */
- pr_debug("prologue: fast path\n");
- err = gen_prologue_fastpath(&pos, args, nargs);
- if (err)
- goto errout;
- } else {
- pr_debug("prologue: slow path\n");
-
- /* Initialization: move ctx to a callee saved register. */
- ins(BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1), &pos);
-
- err = gen_prologue_slowpath(&pos, args, nargs);
- if (err)
- goto errout;
- /*
- * start of ERROR_CODE (only slow pass needs error code)
- * mov r2 <- 1 // r2 is error number
- * mov r3 <- 0 // r3, r4... should be touched or
- * // verifier would complain
- * mov r4 <- 0
- * ...
- * goto usercode
- */
- error_code = pos.pos;
- ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 1),
- &pos);
-
- for (i = 0; i < nargs; i++)
- ins(BPF_ALU64_IMM(BPF_MOV,
- BPF_PROLOGUE_START_ARG_REG + i,
- 0),
- &pos);
- ins(BPF_JMP_IMM(BPF_JA, BPF_REG_0, 0, JMP_TO_USER_CODE),
- &pos);
- }
-
- /*
- * start of SUCCESS_CODE:
- * mov r2 <- 0
- * goto usercode // skip
- */
- success_code = pos.pos;
- ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 0), &pos);
-
- /*
- * start of USER_CODE:
- * Restore ctx to r1
- */
- user_code = pos.pos;
- if (!fastpath) {
- /*
- * Only slow path needs restoring of ctx. In fast path,
- * register are loaded directly from r1.
- */
- ins(BPF_MOV64_REG(BPF_REG_ARG1, BPF_REG_CTX), &pos);
- err = prologue_relocate(&pos, error_code, success_code,
- user_code);
- if (err)
- goto errout;
- }
-
- err = check_pos(&pos);
- if (err)
- goto errout;
-
- *new_cnt = pos_get_cnt(&pos);
- return 0;
-errout:
- return err;
-}
diff --git a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c
new file mode 100644
index 000000000000..939ec769bf4a
--- /dev/null
+++ b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Augment the raw_syscalls tracepoints with the contents of the pointer arguments.
+ *
+ * This exactly matches what is marshalled into the raw_syscall:sys_enter
+ * payload expected by the 'perf trace' beautifiers.
+ */
+
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <linux/limits.h>
+
+/**
+ * is_power_of_2() - check if a value is a power of two
+ * @n: the value to check
+ *
+ * Determine whether some value is a power of two, where zero is *not*
+ * considered a power of two. Return: true if @n is a power of 2, otherwise
+ * false.
+ */
+#define is_power_of_2(n) (n != 0 && ((n & (n - 1)) == 0))
+
+#define MAX_CPUS 4096
+
+// FIXME: These should come from system headers
+#ifndef bool
+typedef char bool;
+#endif
+typedef int pid_t;
+typedef long long int __s64;
+typedef __s64 time64_t;
+
+struct timespec64 {
+ time64_t tv_sec;
+ long int tv_nsec;
+};
+
+/* bpf-output associated map */
+struct __augmented_syscalls__ {
+ __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
+ __type(key, int);
+ __type(value, __u32);
+ __uint(max_entries, MAX_CPUS);
+} __augmented_syscalls__ SEC(".maps");
+
+/*
+ * What to augment at entry?
+ *
+ * Pointer arg payloads (filenames, etc) passed from userspace to the kernel
+ */
+struct syscalls_sys_enter {
+ __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+ __type(key, __u32);
+ __type(value, __u32);
+ __uint(max_entries, 512);
+} syscalls_sys_enter SEC(".maps");
+
+/*
+ * What to augment at exit?
+ *
+ * Pointer arg payloads returned from the kernel (struct stat, etc) to userspace.
+ */
+struct syscalls_sys_exit {
+ __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+ __type(key, __u32);
+ __type(value, __u32);
+ __uint(max_entries, 512);
+} syscalls_sys_exit SEC(".maps");
+
+struct syscall_enter_args {
+ unsigned long long common_tp_fields;
+ long syscall_nr;
+ unsigned long args[6];
+};
+
+struct syscall_exit_args {
+ unsigned long long common_tp_fields;
+ long syscall_nr;
+ long ret;
+};
+
+struct augmented_arg {
+ unsigned int size;
+ int err;
+ char value[PATH_MAX];
+};
+
+struct pids_filtered {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __type(key, pid_t);
+ __type(value, bool);
+ __uint(max_entries, 64);
+} pids_filtered SEC(".maps");
+
+/*
+ * Desired design of maximum size and alignment (see RFC2553)
+ */
+#define SS_MAXSIZE 128 /* Implementation specific max size */
+
+typedef unsigned short sa_family_t;
+
+/*
+ * FIXME: Should come from system headers
+ *
+ * The definition uses anonymous union and struct in order to control the
+ * default alignment.
+ */
+struct sockaddr_storage {
+ union {
+ struct {
+ sa_family_t ss_family; /* address family */
+ /* Following field(s) are implementation specific */
+ char __data[SS_MAXSIZE - sizeof(unsigned short)];
+ /* space to achieve desired size, */
+ /* _SS_MAXSIZE value minus size of ss_family */
+ };
+ void *__align; /* implementation specific desired alignment */
+ };
+};
+
+struct augmented_args_payload {
+ struct syscall_enter_args args;
+ union {
+ struct {
+ struct augmented_arg arg, arg2;
+ };
+ struct sockaddr_storage saddr;
+ char __data[sizeof(struct augmented_arg)];
+ };
+};
+
+// We need more tmp space than the BPF stack can give us
+struct augmented_args_tmp {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __type(key, int);
+ __type(value, struct augmented_args_payload);
+ __uint(max_entries, 1);
+} augmented_args_tmp SEC(".maps");
+
+static inline struct augmented_args_payload *augmented_args_payload(void)
+{
+ int key = 0;
+ return bpf_map_lookup_elem(&augmented_args_tmp, &key);
+}
+
+static inline int augmented__output(void *ctx, struct augmented_args_payload *args, int len)
+{
+ /* If perf_event_output fails, return non-zero so that it gets recorded unaugmented */
+ return bpf_perf_event_output(ctx, &__augmented_syscalls__, BPF_F_CURRENT_CPU, args, len);
+}
+
+static inline
+unsigned int augmented_arg__read_str(struct augmented_arg *augmented_arg, const void *arg, unsigned int arg_len)
+{
+ unsigned int augmented_len = sizeof(*augmented_arg);
+ int string_len = bpf_probe_read_str(&augmented_arg->value, arg_len, arg);
+
+ augmented_arg->size = augmented_arg->err = 0;
+ /*
+ * probe_read_str may return < 0, e.g. -EFAULT
+ * So we leave that in the augmented_arg->size that userspace will
+ */
+ if (string_len > 0) {
+ augmented_len -= sizeof(augmented_arg->value) - string_len;
+ _Static_assert(is_power_of_2(sizeof(augmented_arg->value)), "sizeof(augmented_arg->value) needs to be a power of two");
+ augmented_len &= sizeof(augmented_arg->value) - 1;
+ augmented_arg->size = string_len;
+ } else {
+ /*
+ * So that username notice the error while still being able
+ * to skip this augmented arg record
+ */
+ augmented_arg->err = string_len;
+ augmented_len = offsetof(struct augmented_arg, value);
+ }
+
+ return augmented_len;
+}
+
+SEC("tp/raw_syscalls/sys_enter")
+int syscall_unaugmented(struct syscall_enter_args *args)
+{
+ return 1;
+}
+
+/*
+ * These will be tail_called from SEC("raw_syscalls:sys_enter"), so will find in
+ * augmented_args_tmp what was read by that raw_syscalls:sys_enter and go
+ * on from there, reading the first syscall arg as a string, i.e. open's
+ * filename.
+ */
+SEC("tp/syscalls/sys_enter_connect")
+int sys_enter_connect(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const void *sockaddr_arg = (const void *)args->args[1];
+ unsigned int socklen = args->args[2];
+ unsigned int len = sizeof(augmented_args->args);
+
+ if (augmented_args == NULL)
+ return 1; /* Failure: don't filter */
+
+ _Static_assert(is_power_of_2(sizeof(augmented_args->saddr)), "sizeof(augmented_args->saddr) needs to be a power of two");
+ socklen &= sizeof(augmented_args->saddr) - 1;
+
+ bpf_probe_read(&augmented_args->saddr, socklen, sockaddr_arg);
+
+ return augmented__output(args, augmented_args, len + socklen);
+}
+
+SEC("tp/syscalls/sys_enter_sendto")
+int sys_enter_sendto(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const void *sockaddr_arg = (const void *)args->args[4];
+ unsigned int socklen = args->args[5];
+ unsigned int len = sizeof(augmented_args->args);
+
+ if (augmented_args == NULL)
+ return 1; /* Failure: don't filter */
+
+ socklen &= sizeof(augmented_args->saddr) - 1;
+
+ bpf_probe_read(&augmented_args->saddr, socklen, sockaddr_arg);
+
+ return augmented__output(args, augmented_args, len + socklen);
+}
+
+SEC("tp/syscalls/sys_enter_open")
+int sys_enter_open(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const void *filename_arg = (const void *)args->args[0];
+ unsigned int len = sizeof(augmented_args->args);
+
+ if (augmented_args == NULL)
+ return 1; /* Failure: don't filter */
+
+ len += augmented_arg__read_str(&augmented_args->arg, filename_arg, sizeof(augmented_args->arg.value));
+
+ return augmented__output(args, augmented_args, len);
+}
+
+SEC("tp/syscalls/sys_enter_openat")
+int sys_enter_openat(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const void *filename_arg = (const void *)args->args[1];
+ unsigned int len = sizeof(augmented_args->args);
+
+ if (augmented_args == NULL)
+ return 1; /* Failure: don't filter */
+
+ len += augmented_arg__read_str(&augmented_args->arg, filename_arg, sizeof(augmented_args->arg.value));
+
+ return augmented__output(args, augmented_args, len);
+}
+
+SEC("tp/syscalls/sys_enter_rename")
+int sys_enter_rename(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const void *oldpath_arg = (const void *)args->args[0],
+ *newpath_arg = (const void *)args->args[1];
+ unsigned int len = sizeof(augmented_args->args), oldpath_len;
+
+ if (augmented_args == NULL)
+ return 1; /* Failure: don't filter */
+
+ oldpath_len = augmented_arg__read_str(&augmented_args->arg, oldpath_arg, sizeof(augmented_args->arg.value));
+ len += oldpath_len + augmented_arg__read_str((void *)(&augmented_args->arg) + oldpath_len, newpath_arg, sizeof(augmented_args->arg.value));
+
+ return augmented__output(args, augmented_args, len);
+}
+
+SEC("tp/syscalls/sys_enter_renameat")
+int sys_enter_renameat(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const void *oldpath_arg = (const void *)args->args[1],
+ *newpath_arg = (const void *)args->args[3];
+ unsigned int len = sizeof(augmented_args->args), oldpath_len;
+
+ if (augmented_args == NULL)
+ return 1; /* Failure: don't filter */
+
+ oldpath_len = augmented_arg__read_str(&augmented_args->arg, oldpath_arg, sizeof(augmented_args->arg.value));
+ len += oldpath_len + augmented_arg__read_str((void *)(&augmented_args->arg) + oldpath_len, newpath_arg, sizeof(augmented_args->arg.value));
+
+ return augmented__output(args, augmented_args, len);
+}
+
+#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */
+
+// we need just the start, get the size to then copy it
+struct perf_event_attr_size {
+ __u32 type;
+ /*
+ * Size of the attr structure, for fwd/bwd compat.
+ */
+ __u32 size;
+};
+
+SEC("tp/syscalls/sys_enter_perf_event_open")
+int sys_enter_perf_event_open(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const struct perf_event_attr_size *attr = (const struct perf_event_attr_size *)args->args[0], *attr_read;
+ unsigned int len = sizeof(augmented_args->args);
+
+ if (augmented_args == NULL)
+ goto failure;
+
+ if (bpf_probe_read(&augmented_args->__data, sizeof(*attr), attr) < 0)
+ goto failure;
+
+ attr_read = (const struct perf_event_attr_size *)augmented_args->__data;
+
+ __u32 size = attr_read->size;
+
+ if (!size)
+ size = PERF_ATTR_SIZE_VER0;
+
+ if (size > sizeof(augmented_args->__data))
+ goto failure;
+
+ // Now that we read attr->size and tested it against the size limits, read it completely
+ if (bpf_probe_read(&augmented_args->__data, size, attr) < 0)
+ goto failure;
+
+ return augmented__output(args, augmented_args, len + size);
+failure:
+ return 1; /* Failure: don't filter */
+}
+
+SEC("tp/syscalls/sys_enter_clock_nanosleep")
+int sys_enter_clock_nanosleep(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args = augmented_args_payload();
+ const void *rqtp_arg = (const void *)args->args[2];
+ unsigned int len = sizeof(augmented_args->args);
+ __u32 size = sizeof(struct timespec64);
+
+ if (augmented_args == NULL)
+ goto failure;
+
+ if (size > sizeof(augmented_args->__data))
+ goto failure;
+
+ bpf_probe_read(&augmented_args->__data, size, rqtp_arg);
+
+ return augmented__output(args, augmented_args, len + size);
+failure:
+ return 1; /* Failure: don't filter */
+}
+
+static pid_t getpid(void)
+{
+ return bpf_get_current_pid_tgid();
+}
+
+static bool pid_filter__has(struct pids_filtered *pids, pid_t pid)
+{
+ return bpf_map_lookup_elem(pids, &pid) != NULL;
+}
+
+SEC("tp/raw_syscalls/sys_enter")
+int sys_enter(struct syscall_enter_args *args)
+{
+ struct augmented_args_payload *augmented_args;
+ /*
+ * We start len, the amount of data that will be in the perf ring
+ * buffer, if this is not filtered out by one of pid_filter__has(),
+ * syscall->enabled, etc, with the non-augmented raw syscall payload,
+ * i.e. sizeof(augmented_args->args).
+ *
+ * We'll add to this as we add augmented syscalls right after that
+ * initial, non-augmented raw_syscalls:sys_enter payload.
+ */
+
+ if (pid_filter__has(&pids_filtered, getpid()))
+ return 0;
+
+ augmented_args = augmented_args_payload();
+ if (augmented_args == NULL)
+ return 1;
+
+ bpf_probe_read(&augmented_args->args, sizeof(augmented_args->args), args);
+
+ /*
+ * Jump to syscall specific augmenter, even if the default one,
+ * "!raw_syscalls:unaugmented" that will just return 1 to return the
+ * unaugmented tracepoint payload.
+ */
+ bpf_tail_call(args, &syscalls_sys_enter, augmented_args->args.syscall_nr);
+
+ // If not found on the PROG_ARRAY syscalls map, then we're filtering it:
+ return 0;
+}
+
+SEC("tp/raw_syscalls/sys_exit")
+int sys_exit(struct syscall_exit_args *args)
+{
+ struct syscall_exit_args exit_args;
+
+ if (pid_filter__has(&pids_filtered, getpid()))
+ return 0;
+
+ bpf_probe_read(&exit_args, sizeof(exit_args), args);
+ /*
+ * Jump to syscall specific return augmenter, even if the default one,
+ * "!raw_syscalls:unaugmented" that will just return 1 to return the
+ * unaugmented tracepoint payload.
+ */
+ bpf_tail_call(args, &syscalls_sys_exit, exit_args.syscall_nr);
+ /*
+ * If not found on the PROG_ARRAY syscalls map, then we're filtering it:
+ */
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/perf/util/bpf_skel/bench_uprobe.bpf.c b/tools/perf/util/bpf_skel/bench_uprobe.bpf.c
new file mode 100644
index 000000000000..2c55896bb33c
--- /dev/null
+++ b/tools/perf/util/bpf_skel/bench_uprobe.bpf.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+// Copyright (c) 2023 Red Hat
+#include "vmlinux.h"
+#include <bpf/bpf_tracing.h>
+
+unsigned int nr_uprobes;
+
+SEC("uprobe")
+int BPF_UPROBE(empty)
+{
+ return 0;
+}
+
+SEC("uprobe")
+int BPF_UPROBE(trace_printk)
+{
+ char fmt[] = "perf bench uprobe %u";
+
+ bpf_trace_printk(fmt, sizeof(fmt), ++nr_uprobes);
+ return 0;
+}
+
+char LICENSE[] SEC("license") = "Dual BSD/GPL";
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 36728222a5b4..03c64b85383b 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -560,7 +560,7 @@ char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
struct nsinfo *nsi, bool is_kallsyms,
bool is_vdso)
{
- char *realname = (char *)name, *filename;
+ char *realname = NULL, *filename;
bool slash = is_kallsyms || is_vdso;
if (!slash)
@@ -571,9 +571,7 @@ char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
sbuild_id ? "/" : "", sbuild_id ?: "") < 0)
filename = NULL;
- if (!slash)
- free(realname);
-
+ free(realname);
return filename;
}
diff --git a/tools/perf/util/c++/Build b/tools/perf/util/c++/Build
deleted file mode 100644
index 613ecfd76527..000000000000
--- a/tools/perf/util/c++/Build
+++ /dev/null
@@ -1,2 +0,0 @@
-perf-$(CONFIG_CLANGLLVM) += clang.o
-perf-$(CONFIG_CLANGLLVM) += clang-test.o
diff --git a/tools/perf/util/c++/clang-c.h b/tools/perf/util/c++/clang-c.h
deleted file mode 100644
index d3731a876b6c..000000000000
--- a/tools/perf/util/c++/clang-c.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef PERF_UTIL_CLANG_C_H
-#define PERF_UTIL_CLANG_C_H
-
-#include <stddef.h> /* for size_t */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef HAVE_LIBCLANGLLVM_SUPPORT
-extern void perf_clang__init(void);
-extern void perf_clang__cleanup(void);
-
-struct test_suite;
-extern int test__clang_to_IR(struct test_suite *test, int subtest);
-extern int test__clang_to_obj(struct test_suite *test, int subtest);
-
-extern int perf_clang__compile_bpf(const char *filename,
- void **p_obj_buf,
- size_t *p_obj_buf_sz);
-#else
-
-#include <errno.h>
-#include <linux/compiler.h> /* for __maybe_unused */
-
-static inline void perf_clang__init(void) { }
-static inline void perf_clang__cleanup(void) { }
-
-static inline int
-perf_clang__compile_bpf(const char *filename __maybe_unused,
- void **p_obj_buf __maybe_unused,
- size_t *p_obj_buf_sz __maybe_unused)
-{
- return -ENOTSUP;
-}
-
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tools/perf/util/c++/clang-test.cpp b/tools/perf/util/c++/clang-test.cpp
deleted file mode 100644
index a4683ca53697..000000000000
--- a/tools/perf/util/c++/clang-test.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include "clang.h"
-#include "clang-c.h"
-extern "C" {
-#include "../util.h"
-}
-#include "llvm/IR/Function.h"
-#include "llvm/IR/LLVMContext.h"
-
-#include <tests/llvm.h>
-#include <string>
-
-class perf_clang_scope {
-public:
- explicit perf_clang_scope() {perf_clang__init();}
- ~perf_clang_scope() {perf_clang__cleanup();}
-};
-
-static std::unique_ptr<llvm::Module>
-__test__clang_to_IR(void)
-{
- unsigned int kernel_version;
-
- if (fetch_kernel_version(&kernel_version, NULL, 0))
- return std::unique_ptr<llvm::Module>(nullptr);
-
- std::string cflag_kver("-DLINUX_VERSION_CODE=" +
- std::to_string(kernel_version));
-
- std::unique_ptr<llvm::Module> M =
- perf::getModuleFromSource({cflag_kver.c_str()},
- "perf-test.c",
- test_llvm__bpf_base_prog);
- return M;
-}
-
-extern "C" {
-int test__clang_to_IR(struct test_suite *test __maybe_unused,
- int subtest __maybe_unused)
-{
- perf_clang_scope _scope;
-
- auto M = __test__clang_to_IR();
- if (!M)
- return -1;
- for (llvm::Function& F : *M)
- if (F.getName() == "bpf_func__SyS_epoll_pwait")
- return 0;
- return -1;
-}
-
-int test__clang_to_obj(struct test_suite *test __maybe_unused,
- int subtest __maybe_unused)
-{
- perf_clang_scope _scope;
-
- auto M = __test__clang_to_IR();
- if (!M)
- return -1;
-
- auto Buffer = perf::getBPFObjectFromModule(&*M);
- if (!Buffer)
- return -1;
- return 0;
-}
-
-}
diff --git a/tools/perf/util/c++/clang.cpp b/tools/perf/util/c++/clang.cpp
deleted file mode 100644
index 1aad7d6d34aa..000000000000
--- a/tools/perf/util/c++/clang.cpp
+++ /dev/null
@@ -1,225 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * llvm C frontend for perf. Support dynamically compile C file
- *
- * Inspired by clang example code:
- * http://llvm.org/svn/llvm-project/cfe/trunk/examples/clang-interpreter/main.cpp
- *
- * Copyright (C) 2016 Wang Nan <wangnan0@huawei.com>
- * Copyright (C) 2016 Huawei Inc.
- */
-
-#include "clang/Basic/Version.h"
-#include "clang/CodeGen/CodeGenAction.h"
-#include "clang/Frontend/CompilerInvocation.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Frontend/TextDiagnosticPrinter.h"
-#include "clang/Tooling/Tooling.h"
-#include "llvm/IR/LegacyPassManager.h"
-#include "llvm/IR/Module.h"
-#include "llvm/Option/Option.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/ManagedStatic.h"
-#if CLANG_VERSION_MAJOR >= 14
-#include "llvm/MC/TargetRegistry.h"
-#else
-#include "llvm/Support/TargetRegistry.h"
-#endif
-#include "llvm/Support/TargetSelect.h"
-#include "llvm/Target/TargetMachine.h"
-#include "llvm/Target/TargetOptions.h"
-#include <memory>
-
-#include "clang.h"
-#include "clang-c.h"
-
-namespace perf {
-
-static std::unique_ptr<llvm::LLVMContext> LLVMCtx;
-
-using namespace clang;
-
-static CompilerInvocation *
-createCompilerInvocation(llvm::opt::ArgStringList CFlags, StringRef& Path,
- DiagnosticsEngine& Diags)
-{
- llvm::opt::ArgStringList CCArgs {
- "-cc1",
- "-triple", "bpf-pc-linux",
- "-fsyntax-only",
- "-O2",
- "-nostdsysteminc",
- "-nobuiltininc",
- "-vectorize-loops",
- "-vectorize-slp",
- "-Wno-unused-value",
- "-Wno-pointer-sign",
- "-x", "c"};
-
- CCArgs.append(CFlags.begin(), CFlags.end());
- CompilerInvocation *CI = tooling::newInvocation(&Diags, CCArgs
-#if CLANG_VERSION_MAJOR >= 11
- ,/*BinaryName=*/nullptr
-#endif
- );
-
- FrontendOptions& Opts = CI->getFrontendOpts();
- Opts.Inputs.clear();
- Opts.Inputs.emplace_back(Path,
- FrontendOptions::getInputKindForExtension("c"));
- return CI;
-}
-
-static std::unique_ptr<llvm::Module>
-getModuleFromSource(llvm::opt::ArgStringList CFlags,
- StringRef Path, IntrusiveRefCntPtr<vfs::FileSystem> VFS)
-{
- CompilerInstance Clang;
- Clang.createDiagnostics();
-
-#if CLANG_VERSION_MAJOR < 9
- Clang.setVirtualFileSystem(&*VFS);
-#else
- Clang.createFileManager(&*VFS);
-#endif
-
-#if CLANG_VERSION_MAJOR < 4
- IntrusiveRefCntPtr<CompilerInvocation> CI =
- createCompilerInvocation(std::move(CFlags), Path,
- Clang.getDiagnostics());
- Clang.setInvocation(&*CI);
-#else
- std::shared_ptr<CompilerInvocation> CI(
- createCompilerInvocation(std::move(CFlags), Path,
- Clang.getDiagnostics()));
- Clang.setInvocation(CI);
-#endif
-
- std::unique_ptr<CodeGenAction> Act(new EmitLLVMOnlyAction(&*LLVMCtx));
- if (!Clang.ExecuteAction(*Act))
- return std::unique_ptr<llvm::Module>(nullptr);
-
- return Act->takeModule();
-}
-
-std::unique_ptr<llvm::Module>
-getModuleFromSource(llvm::opt::ArgStringList CFlags,
- StringRef Name, StringRef Content)
-{
- using namespace vfs;
-
- llvm::IntrusiveRefCntPtr<OverlayFileSystem> OverlayFS(
- new OverlayFileSystem(getRealFileSystem()));
- llvm::IntrusiveRefCntPtr<InMemoryFileSystem> MemFS(
- new InMemoryFileSystem(true));
-
- /*
- * pushOverlay helps setting working dir for MemFS. Must call
- * before addFile.
- */
- OverlayFS->pushOverlay(MemFS);
- MemFS->addFile(Twine(Name), 0, llvm::MemoryBuffer::getMemBuffer(Content));
-
- return getModuleFromSource(std::move(CFlags), Name, OverlayFS);
-}
-
-std::unique_ptr<llvm::Module>
-getModuleFromSource(llvm::opt::ArgStringList CFlags, StringRef Path)
-{
- IntrusiveRefCntPtr<vfs::FileSystem> VFS(vfs::getRealFileSystem());
- return getModuleFromSource(std::move(CFlags), Path, VFS);
-}
-
-std::unique_ptr<llvm::SmallVectorImpl<char>>
-getBPFObjectFromModule(llvm::Module *Module)
-{
- using namespace llvm;
-
- std::string TargetTriple("bpf-pc-linux");
- std::string Error;
- const Target* Target = TargetRegistry::lookupTarget(TargetTriple, Error);
- if (!Target) {
- llvm::errs() << Error;
- return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr);
- }
-
- llvm::TargetOptions Opt;
- TargetMachine *TargetMachine =
- Target->createTargetMachine(TargetTriple,
- "generic", "",
- Opt, Reloc::Static);
-
- Module->setDataLayout(TargetMachine->createDataLayout());
- Module->setTargetTriple(TargetTriple);
-
- std::unique_ptr<SmallVectorImpl<char>> Buffer(new SmallVector<char, 0>());
- raw_svector_ostream ostream(*Buffer);
-
- legacy::PassManager PM;
- bool NotAdded;
- NotAdded = TargetMachine->addPassesToEmitFile(PM, ostream
-#if CLANG_VERSION_MAJOR >= 7
- , /*DwoOut=*/nullptr
-#endif
-#if CLANG_VERSION_MAJOR < 10
- , TargetMachine::CGFT_ObjectFile
-#else
- , llvm::CGFT_ObjectFile
-#endif
- );
- if (NotAdded) {
- llvm::errs() << "TargetMachine can't emit a file of this type\n";
- return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr);
- }
- PM.run(*Module);
-
- return Buffer;
-}
-
-}
-
-extern "C" {
-void perf_clang__init(void)
-{
- perf::LLVMCtx.reset(new llvm::LLVMContext());
- LLVMInitializeBPFTargetInfo();
- LLVMInitializeBPFTarget();
- LLVMInitializeBPFTargetMC();
- LLVMInitializeBPFAsmPrinter();
-}
-
-void perf_clang__cleanup(void)
-{
- perf::LLVMCtx.reset(nullptr);
- llvm::llvm_shutdown();
-}
-
-int perf_clang__compile_bpf(const char *filename,
- void **p_obj_buf,
- size_t *p_obj_buf_sz)
-{
- using namespace perf;
-
- if (!p_obj_buf || !p_obj_buf_sz)
- return -EINVAL;
-
- llvm::opt::ArgStringList CFlags;
- auto M = getModuleFromSource(std::move(CFlags), filename);
- if (!M)
- return -EINVAL;
- auto O = getBPFObjectFromModule(&*M);
- if (!O)
- return -EINVAL;
-
- size_t size = O->size_in_bytes();
- void *buffer;
-
- buffer = malloc(size);
- if (!buffer)
- return -ENOMEM;
- memcpy(buffer, O->data(), size);
- *p_obj_buf = buffer;
- *p_obj_buf_sz = size;
- return 0;
-}
-}
diff --git a/tools/perf/util/c++/clang.h b/tools/perf/util/c++/clang.h
deleted file mode 100644
index 6ce33e22f23c..000000000000
--- a/tools/perf/util/c++/clang.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef PERF_UTIL_CLANG_H
-#define PERF_UTIL_CLANG_H
-
-#include "llvm/ADT/StringRef.h"
-#include "llvm/IR/LLVMContext.h"
-#include "llvm/IR/Module.h"
-#include "llvm/Option/Option.h"
-#include <memory>
-
-namespace perf {
-
-using namespace llvm;
-
-std::unique_ptr<Module>
-getModuleFromSource(opt::ArgStringList CFlags,
- StringRef Name, StringRef Content);
-
-std::unique_ptr<Module>
-getModuleFromSource(opt::ArgStringList CFlags,
- StringRef Path);
-
-std::unique_ptr<llvm::SmallVectorImpl<char>>
-getBPFObjectFromModule(llvm::Module *Module);
-
-}
-#endif
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 46f144c46827..7a650de0db83 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -16,7 +16,6 @@
#include <subcmd/exec-cmd.h>
#include "util/event.h" /* proc_map_timeout */
#include "util/hist.h" /* perf_hist_config */
-#include "util/llvm-utils.h" /* perf_llvm_config */
#include "util/stat.h" /* perf_stat__set_big_num */
#include "util/evsel.h" /* evsel__hw_names, evsel__use_bpf_counters */
#include "util/srcline.h" /* addr2line_timeout_ms */
@@ -486,9 +485,6 @@ int perf_default_config(const char *var, const char *value,
if (strstarts(var, "call-graph."))
return perf_callchain_config(var, value);
- if (strstarts(var, "llvm."))
- return perf_llvm_config(var, value);
-
if (strstarts(var, "buildid."))
return perf_buildid_config(var, value);
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index 1419b40dfbe8..9729d006550d 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -6,10 +6,11 @@
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
*/
+#include <linux/kernel.h>
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/coresight-pmu.h>
#include <linux/err.h>
-#include <linux/kernel.h>
#include <linux/log2.h>
#include <linux/types.h>
#include <linux/zalloc.h>
@@ -282,17 +283,6 @@ static int cs_etm__metadata_set_trace_id(u8 trace_chan_id, u64 *cpu_metadata)
}
/*
- * FIELD_GET (linux/bitfield.h) not available outside kernel code,
- * and the header contains too many dependencies to just copy over,
- * so roll our own based on the original
- */
-#define __bf_shf(x) (__builtin_ffsll(x) - 1)
-#define FIELD_GET(_mask, _reg) \
- ({ \
- (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
- })
-
-/*
* Get a metadata for a specific cpu from an array.
*
*/
diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c
index 46f74b2344db..1dbf27822ee2 100644
--- a/tools/perf/util/dlfilter.c
+++ b/tools/perf/util/dlfilter.c
@@ -10,6 +10,8 @@
#include <subcmd/exec-cmd.h>
#include <linux/zalloc.h>
#include <linux/build_bug.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
#include "debug.h"
#include "event.h"
@@ -63,6 +65,7 @@ static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al)
d_al->addr = al->addr;
d_al->comm = NULL;
d_al->filtered = 0;
+ d_al->priv = NULL;
}
static struct addr_location *get_al(struct dlfilter *d)
@@ -151,6 +154,11 @@ static char **dlfilter__args(void *ctx, int *dlargc)
return d->dlargv;
}
+static bool has_priv(struct perf_dlfilter_al *d_al_p)
+{
+ return d_al_p->size >= offsetof(struct perf_dlfilter_al, priv) + sizeof(d_al_p->priv);
+}
+
static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlfilter_al *d_al_p)
{
struct dlfilter *d = (struct dlfilter *)ctx;
@@ -166,6 +174,7 @@ static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlf
if (!thread)
return -1;
+ addr_location__init(&al);
thread__find_symbol_fb(thread, d->sample->cpumode, address, &al);
al_to_d_al(&al, &d_al);
@@ -176,9 +185,31 @@ static __s32 dlfilter__resolve_address(void *ctx, __u64 address, struct perf_dlf
memcpy(d_al_p, &d_al, min((size_t)sz, sizeof(d_al)));
d_al_p->size = sz;
+ if (has_priv(d_al_p))
+ d_al_p->priv = memdup(&al, sizeof(al));
+ else /* Avoid leak for v0 API */
+ addr_location__exit(&al);
+
return 0;
}
+static void dlfilter__al_cleanup(void *ctx __maybe_unused, struct perf_dlfilter_al *d_al_p)
+{
+ struct addr_location *al;
+
+ /* Ensure backward compatibility */
+ if (!has_priv(d_al_p) || !d_al_p->priv)
+ return;
+
+ al = d_al_p->priv;
+
+ d_al_p->priv = NULL;
+
+ addr_location__exit(al);
+
+ free(al);
+}
+
static const __u8 *dlfilter__insn(void *ctx, __u32 *len)
{
struct dlfilter *d = (struct dlfilter *)ctx;
@@ -296,6 +327,7 @@ static const struct perf_dlfilter_fns perf_dlfilter_fns = {
.resolve_addr = dlfilter__resolve_addr,
.args = dlfilter__args,
.resolve_address = dlfilter__resolve_address,
+ .al_cleanup = dlfilter__al_cleanup,
.insn = dlfilter__insn,
.srcline = dlfilter__srcline,
.attr = dlfilter__attr,
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 9eabf3ec56e9..a164164001fb 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -324,11 +324,9 @@ int perf_env__read_pmu_mappings(struct perf_env *env)
u32 pmu_num = 0;
struct strbuf sb;
- while ((pmu = perf_pmus__scan(pmu))) {
- if (!pmu->name)
- continue;
+ while ((pmu = perf_pmus__scan(pmu)))
pmu_num++;
- }
+
if (!pmu_num) {
pr_debug("pmu mappings not available\n");
return -ENOENT;
@@ -339,8 +337,6 @@ int perf_env__read_pmu_mappings(struct perf_env *env)
return -ENOMEM;
while ((pmu = perf_pmus__scan(pmu))) {
- if (!pmu->name)
- continue;
if (strbuf_addf(&sb, "%u:%s", pmu->type, pmu->name) < 0)
goto error;
/* include a NULL character at the end */
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 4cbb092e0684..923c0fb15122 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -93,8 +93,8 @@ struct process_symbol_args {
u64 start;
};
-static int find_symbol_cb(void *arg, const char *name, char type,
- u64 start)
+static int find_func_symbol_cb(void *arg, const char *name, char type,
+ u64 start)
{
struct process_symbol_args *args = arg;
@@ -110,12 +110,36 @@ static int find_symbol_cb(void *arg, const char *name, char type,
return 1;
}
+static int find_any_symbol_cb(void *arg, const char *name,
+ char type __maybe_unused, u64 start)
+{
+ struct process_symbol_args *args = arg;
+
+ if (strcmp(name, args->name))
+ return 0;
+
+ args->start = start;
+ return 1;
+}
+
int kallsyms__get_function_start(const char *kallsyms_filename,
const char *symbol_name, u64 *addr)
{
struct process_symbol_args args = { .name = symbol_name, };
- if (kallsyms__parse(kallsyms_filename, &args, find_symbol_cb) <= 0)
+ if (kallsyms__parse(kallsyms_filename, &args, find_func_symbol_cb) <= 0)
+ return -1;
+
+ *addr = args.start;
+ return 0;
+}
+
+int kallsyms__get_symbol_start(const char *kallsyms_filename,
+ const char *symbol_name, u64 *addr)
+{
+ struct process_symbol_args args = { .name = symbol_name, };
+
+ if (kallsyms__parse(kallsyms_filename, &args, find_any_symbol_cb) <= 0)
return -1;
*addr = args.start;
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index de20e01c9d72..d8bcee2e9b93 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -360,6 +360,8 @@ size_t perf_event__fprintf(union perf_event *event, struct machine *machine, FIL
int kallsyms__get_function_start(const char *kallsyms_filename,
const char *symbol_name, u64 *addr);
+int kallsyms__get_symbol_start(const char *kallsyms_filename,
+ const char *symbol_name, u64 *addr);
void event_attr_init(struct perf_event_attr *attr);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 762e2b2634a5..a8a5ff87cc1f 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -845,6 +845,7 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o
{
bool function = evsel__is_function_event(evsel);
struct perf_event_attr *attr = &evsel->core.attr;
+ const char *arch = perf_env__arch(evsel__env(evsel));
evsel__set_sample_bit(evsel, CALLCHAIN);
@@ -877,8 +878,9 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o
if (!function) {
evsel__set_sample_bit(evsel, REGS_USER);
evsel__set_sample_bit(evsel, STACK_USER);
- if (opts->sample_user_regs && DWARF_MINIMAL_REGS != PERF_REGS_MASK) {
- attr->sample_regs_user |= DWARF_MINIMAL_REGS;
+ if (opts->sample_user_regs &&
+ DWARF_MINIMAL_REGS(arch) != arch__user_reg_mask()) {
+ attr->sample_regs_user |= DWARF_MINIMAL_REGS(arch);
pr_warning("WARNING: The use of --call-graph=dwarf may require all the user registers, "
"specifying a subset with --user-regs may render DWARF unwinding unreliable, "
"so the minimal registers set (IP, SP) is explicitly forced.\n");
@@ -1474,6 +1476,7 @@ void evsel__exit(struct evsel *evsel)
perf_thread_map__put(evsel->core.threads);
zfree(&evsel->group_name);
zfree(&evsel->name);
+ zfree(&evsel->filter);
zfree(&evsel->pmu_name);
zfree(&evsel->group_pmu_name);
zfree(&evsel->unit);
@@ -2826,9 +2829,6 @@ u64 evsel__intval(struct evsel *evsel, struct perf_sample *sample, const char *n
{
struct tep_format_field *field = evsel__field(evsel, name);
- if (!field)
- return 0;
-
return field ? format_field__intval(field, sample, evsel->needs_swap) : 0;
}
#endif
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index 4814262e3805..4488f306de78 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -10,9 +10,11 @@
#include "debug.h"
#include "evlist.h"
#include "expr.h"
-#include "expr-bison.h"
-#include "expr-flex.h"
+#include <util/expr-bison.h>
+#include <util/expr-flex.h>
#include "util/hashmap.h"
+#include "util/header.h"
+#include "util/pmu.h"
#include "smt.h"
#include "tsc.h"
#include <api/fs/fs.h>
@@ -425,6 +427,13 @@ double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx
result = cpu__max_present_cpu().cpu;
goto out;
}
+ if (!strcmp("#num_cpus_online", literal)) {
+ struct perf_cpu_map *online = cpu_map__online();
+
+ if (online)
+ result = perf_cpu_map__nr(online);
+ goto out;
+ }
if (!strcasecmp("#system_tsc_freq", literal)) {
result = arch_get_tsc_freq();
@@ -495,3 +504,19 @@ double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const
evlist__delete(tmp);
return ret;
}
+
+double expr__strcmp_cpuid_str(const struct expr_parse_ctx *ctx __maybe_unused,
+ bool compute_ids __maybe_unused, const char *test_id)
+{
+ double ret;
+ struct perf_pmu *pmu = pmu__find_core_pmu();
+ char *cpuid = perf_pmu__getcpuid(pmu);
+
+ if (!cpuid)
+ return NAN;
+
+ ret = !strcmp_cpuid_str(test_id, cpuid);
+
+ free(cpuid);
+ return ret;
+}
diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h
index 3c1e49b3e35d..c0cec29ddc29 100644
--- a/tools/perf/util/expr.h
+++ b/tools/perf/util/expr.h
@@ -55,5 +55,6 @@ double expr_id_data__value(const struct expr_id_data *data);
double expr_id_data__source_count(const struct expr_id_data *data);
double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx);
double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id);
+double expr__strcmp_cpuid_str(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id);
#endif
diff --git a/tools/perf/util/expr.l b/tools/perf/util/expr.l
index dbb117414710..0feef0726c48 100644
--- a/tools/perf/util/expr.l
+++ b/tools/perf/util/expr.l
@@ -114,6 +114,7 @@ if { return IF; }
else { return ELSE; }
source_count { return SOURCE_COUNT; }
has_event { return HAS_EVENT; }
+strcmp_cpuid_str { return STRCMP_CPUID_STR; }
{literal} { return literal(yyscanner, sctx); }
{number} { return value(yyscanner); }
{symbol} { return str(yyscanner, ID, sctx->runtime); }
diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y
index dd504afd8f36..6c93b358cc2d 100644
--- a/tools/perf/util/expr.y
+++ b/tools/perf/util/expr.y
@@ -7,6 +7,8 @@
#include "util/debug.h"
#define IN_EXPR_Y 1
#include "expr.h"
+#include "expr-bison.h"
+int expr_lex(YYSTYPE * yylval_param , void *yyscanner);
%}
%define api.pure full
@@ -37,7 +39,7 @@
} ids;
}
-%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT EXPR_ERROR
+%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT STRCMP_CPUID_STR EXPR_ERROR
%left MIN MAX IF
%left '|'
%left '^'
@@ -56,7 +58,7 @@
static void expr_error(double *final_val __maybe_unused,
struct expr_parse_ctx *ctx __maybe_unused,
bool compute_ids __maybe_unused,
- void *scanner,
+ void *scanner __maybe_unused,
const char *s)
{
pr_debug("%s\n", s);
@@ -205,6 +207,12 @@ expr: NUMBER
$$.ids = NULL;
free($3);
}
+| STRCMP_CPUID_STR '(' ID ')'
+{
+ $$.val = expr__strcmp_cpuid_str(ctx, compute_ids, $3);
+ $$.ids = NULL;
+ free($3);
+}
| expr '|' expr
{
if (is_const($1.val) && is_const($3.val)) {
diff --git a/tools/perf/util/hashmap.h b/tools/perf/util/hashmap.h
index 0a5bf1937a7c..c12f8320e668 100644
--- a/tools/perf/util/hashmap.h
+++ b/tools/perf/util/hashmap.h
@@ -80,16 +80,6 @@ struct hashmap {
size_t sz;
};
-#define HASHMAP_INIT(hash_fn, equal_fn, ctx) { \
- .hash_fn = (hash_fn), \
- .equal_fn = (equal_fn), \
- .ctx = (ctx), \
- .buckets = NULL, \
- .cap = 0, \
- .cap_bits = 0, \
- .sz = 0, \
-}
-
void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
hashmap_equal_fn equal_fn, void *ctx);
struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 52fbf526fe74..d812e1e371a7 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -456,6 +456,8 @@ static int write_cpudesc(struct feat_fd *ff,
#define CPUINFO_PROC { "Processor", }
#elif defined(__xtensa__)
#define CPUINFO_PROC { "core ID", }
+#elif defined(__loongarch__)
+#define CPUINFO_PROC { "Model Name", }
#else
#define CPUINFO_PROC { "model name", }
#endif
@@ -746,20 +748,14 @@ static int write_pmu_mappings(struct feat_fd *ff,
* Do a first pass to count number of pmu to avoid lseek so this
* works in pipe mode as well.
*/
- while ((pmu = perf_pmus__scan(pmu))) {
- if (!pmu->name)
- continue;
+ while ((pmu = perf_pmus__scan(pmu)))
pmu_num++;
- }
ret = do_write(ff, &pmu_num, sizeof(pmu_num));
if (ret < 0)
return ret;
while ((pmu = perf_pmus__scan(pmu))) {
- if (!pmu->name)
- continue;
-
ret = do_write(ff, &pmu->type, sizeof(pmu->type));
if (ret < 0)
return ret;
@@ -1605,8 +1601,15 @@ static int write_pmu_caps(struct feat_fd *ff,
int ret;
while ((pmu = perf_pmus__scan(pmu))) {
- if (!pmu->name || !strcmp(pmu->name, "cpu") ||
- perf_pmu__caps_parse(pmu) <= 0)
+ if (!strcmp(pmu->name, "cpu")) {
+ /*
+ * The "cpu" PMU is special and covered by
+ * HEADER_CPU_PMU_CAPS. Note, core PMUs are
+ * counted/written here for ARM, s390 and Intel hybrid.
+ */
+ continue;
+ }
+ if (perf_pmu__caps_parse(pmu) <= 0)
continue;
nr_pmu++;
}
@@ -1619,23 +1622,17 @@ static int write_pmu_caps(struct feat_fd *ff,
return 0;
/*
- * Write hybrid pmu caps first to maintain compatibility with
- * older perf tool.
+ * Note older perf tools assume core PMUs come first, this is a property
+ * of perf_pmus__scan.
*/
- if (perf_pmus__num_core_pmus() > 1) {
- pmu = NULL;
- while ((pmu = perf_pmus__scan_core(pmu))) {
- ret = __write_pmu_caps(ff, pmu, true);
- if (ret < 0)
- return ret;
- }
- }
-
pmu = NULL;
while ((pmu = perf_pmus__scan(pmu))) {
- if (pmu->is_core || !pmu->nr_caps)
+ if (!strcmp(pmu->name, "cpu")) {
+ /* Skip as above. */
+ continue;
+ }
+ if (perf_pmu__caps_parse(pmu) <= 0)
continue;
-
ret = __write_pmu_caps(ff, pmu, true);
if (ret < 0)
return ret;
@@ -4381,7 +4378,8 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct evlist **pevlist)
{
- u32 i, ids, n_ids;
+ u32 i, n_ids;
+ u64 *ids;
struct evsel *evsel;
struct evlist *evlist = *pevlist;
@@ -4397,9 +4395,8 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused,
evlist__add(evlist, evsel);
- ids = event->header.size;
- ids -= (void *)&event->attr.id - (void *)event;
- n_ids = ids / sizeof(u64);
+ n_ids = event->header.size - sizeof(event->header) - event->attr.attr.size;
+ n_ids = n_ids / sizeof(u64);
/*
* We don't have the cpu and thread maps on the header, so
* for allocating the perf_sample_id table we fake 1 cpu and
@@ -4408,8 +4405,9 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused,
if (perf_evsel__alloc_id(&evsel->core, 1, n_ids))
return -ENOMEM;
+ ids = perf_record_header_attr_id(event);
for (i = 0; i < n_ids; i++) {
- perf_evlist__id_add(&evlist->core, &evsel->core, 0, i, event->attr.id[i]);
+ perf_evlist__id_add(&evlist->core, &evsel->core, 0, i, ids[i]);
}
return 0;
diff --git a/tools/perf/util/libunwind/arm64.c b/tools/perf/util/libunwind/arm64.c
index 014d82159656..37ecef0c53b9 100644
--- a/tools/perf/util/libunwind/arm64.c
+++ b/tools/perf/util/libunwind/arm64.c
@@ -18,8 +18,6 @@
* defined before including "unwind.h"
*/
#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arm64_reg_id(regnum)
-#define LIBUNWIND__ARCH_REG_IP PERF_REG_ARM64_PC
-#define LIBUNWIND__ARCH_REG_SP PERF_REG_ARM64_SP
#include "unwind.h"
#include "libunwind-aarch64.h"
diff --git a/tools/perf/util/libunwind/x86_32.c b/tools/perf/util/libunwind/x86_32.c
index b2b92d030aef..1697dece1b74 100644
--- a/tools/perf/util/libunwind/x86_32.c
+++ b/tools/perf/util/libunwind/x86_32.c
@@ -18,8 +18,6 @@
* defined before including "unwind.h"
*/
#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__x86_reg_id(regnum)
-#define LIBUNWIND__ARCH_REG_IP PERF_REG_X86_IP
-#define LIBUNWIND__ARCH_REG_SP PERF_REG_X86_SP
#include "unwind.h"
#include "libunwind-x86.h"
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
deleted file mode 100644
index c6c9c2228578..000000000000
--- a/tools/perf/util/llvm-utils.c
+++ /dev/null
@@ -1,612 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2015, Wang Nan <wangnan0@huawei.com>
- * Copyright (C) 2015, Huawei Inc.
- */
-
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <linux/err.h>
-#include <linux/string.h>
-#include <linux/zalloc.h>
-#include "debug.h"
-#include "llvm-utils.h"
-#include "config.h"
-#include "util.h"
-#include <sys/wait.h>
-#include <subcmd/exec-cmd.h>
-
-#define CLANG_BPF_CMD_DEFAULT_TEMPLATE \
- "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\
- "-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE " \
- "$CLANG_OPTIONS $PERF_BPF_INC_OPTIONS $KERNEL_INC_OPTIONS " \
- "-Wno-unused-value -Wno-pointer-sign " \
- "-working-directory $WORKING_DIR " \
- "-c \"$CLANG_SOURCE\" --target=bpf $CLANG_EMIT_LLVM -g -O2 -o - $LLVM_OPTIONS_PIPE"
-
-struct llvm_param llvm_param = {
- .clang_path = "clang",
- .llc_path = "llc",
- .clang_bpf_cmd_template = CLANG_BPF_CMD_DEFAULT_TEMPLATE,
- .clang_opt = NULL,
- .opts = NULL,
- .kbuild_dir = NULL,
- .kbuild_opts = NULL,
- .user_set_param = false,
-};
-
-static void version_notice(void);
-
-int perf_llvm_config(const char *var, const char *value)
-{
- if (!strstarts(var, "llvm."))
- return 0;
- var += sizeof("llvm.") - 1;
-
- if (!strcmp(var, "clang-path"))
- llvm_param.clang_path = strdup(value);
- else if (!strcmp(var, "clang-bpf-cmd-template"))
- llvm_param.clang_bpf_cmd_template = strdup(value);
- else if (!strcmp(var, "clang-opt"))
- llvm_param.clang_opt = strdup(value);
- else if (!strcmp(var, "kbuild-dir"))
- llvm_param.kbuild_dir = strdup(value);
- else if (!strcmp(var, "kbuild-opts"))
- llvm_param.kbuild_opts = strdup(value);
- else if (!strcmp(var, "dump-obj"))
- llvm_param.dump_obj = !!perf_config_bool(var, value);
- else if (!strcmp(var, "opts"))
- llvm_param.opts = strdup(value);
- else {
- pr_debug("Invalid LLVM config option: %s\n", value);
- return -1;
- }
- llvm_param.user_set_param = true;
- return 0;
-}
-
-static int
-search_program(const char *def, const char *name,
- char *output)
-{
- char *env, *path, *tmp = NULL;
- char buf[PATH_MAX];
- int ret;
-
- output[0] = '\0';
- if (def && def[0] != '\0') {
- if (def[0] == '/') {
- if (access(def, F_OK) == 0) {
- strlcpy(output, def, PATH_MAX);
- return 0;
- }
- } else if (def[0] != '\0')
- name = def;
- }
-
- env = getenv("PATH");
- if (!env)
- return -1;
- env = strdup(env);
- if (!env)
- return -1;
-
- ret = -ENOENT;
- path = strtok_r(env, ":", &tmp);
- while (path) {
- scnprintf(buf, sizeof(buf), "%s/%s", path, name);
- if (access(buf, F_OK) == 0) {
- strlcpy(output, buf, PATH_MAX);
- ret = 0;
- break;
- }
- path = strtok_r(NULL, ":", &tmp);
- }
-
- free(env);
- return ret;
-}
-
-static int search_program_and_warn(const char *def, const char *name,
- char *output)
-{
- int ret = search_program(def, name, output);
-
- if (ret) {
- pr_err("ERROR:\tunable to find %s.\n"
- "Hint:\tTry to install latest clang/llvm to support BPF. Check your $PATH\n"
- " \tand '%s-path' option in [llvm] section of ~/.perfconfig.\n",
- name, name);
- version_notice();
- }
- return ret;
-}
-
-#define READ_SIZE 4096
-static int
-read_from_pipe(const char *cmd, void **p_buf, size_t *p_read_sz)
-{
- int err = 0;
- void *buf = NULL;
- FILE *file = NULL;
- size_t read_sz = 0, buf_sz = 0;
- char serr[STRERR_BUFSIZE];
-
- file = popen(cmd, "r");
- if (!file) {
- pr_err("ERROR: unable to popen cmd: %s\n",
- str_error_r(errno, serr, sizeof(serr)));
- return -EINVAL;
- }
-
- while (!feof(file) && !ferror(file)) {
- /*
- * Make buf_sz always have obe byte extra space so we
- * can put '\0' there.
- */
- if (buf_sz - read_sz < READ_SIZE + 1) {
- void *new_buf;
-
- buf_sz = read_sz + READ_SIZE + 1;
- new_buf = realloc(buf, buf_sz);
-
- if (!new_buf) {
- pr_err("ERROR: failed to realloc memory\n");
- err = -ENOMEM;
- goto errout;
- }
-
- buf = new_buf;
- }
- read_sz += fread(buf + read_sz, 1, READ_SIZE, file);
- }
-
- if (buf_sz - read_sz < 1) {
- pr_err("ERROR: internal error\n");
- err = -EINVAL;
- goto errout;
- }
-
- if (ferror(file)) {
- pr_err("ERROR: error occurred when reading from pipe: %s\n",
- str_error_r(errno, serr, sizeof(serr)));
- err = -EIO;
- goto errout;
- }
-
- err = WEXITSTATUS(pclose(file));
- file = NULL;
- if (err) {
- err = -EINVAL;
- goto errout;
- }
-
- /*
- * If buf is string, give it terminal '\0' to make our life
- * easier. If buf is not string, that '\0' is out of space
- * indicated by read_sz so caller won't even notice it.
- */
- ((char *)buf)[read_sz] = '\0';
-
- if (!p_buf)
- free(buf);
- else
- *p_buf = buf;
-
- if (p_read_sz)
- *p_read_sz = read_sz;
- return 0;
-
-errout:
- if (file)
- pclose(file);
- free(buf);
- if (p_buf)
- *p_buf = NULL;
- if (p_read_sz)
- *p_read_sz = 0;
- return err;
-}
-
-static inline void
-force_set_env(const char *var, const char *value)
-{
- if (value) {
- setenv(var, value, 1);
- pr_debug("set env: %s=%s\n", var, value);
- } else {
- unsetenv(var);
- pr_debug("unset env: %s\n", var);
- }
-}
-
-static void
-version_notice(void)
-{
- pr_err(
-" \tLLVM 3.7 or newer is required. Which can be found from http://llvm.org\n"
-" \tYou may want to try git trunk:\n"
-" \t\tgit clone http://llvm.org/git/llvm.git\n"
-" \t\t and\n"
-" \t\tgit clone http://llvm.org/git/clang.git\n\n"
-" \tOr fetch the latest clang/llvm 3.7 from pre-built llvm packages for\n"
-" \tdebian/ubuntu:\n"
-" \t\thttps://apt.llvm.org/\n\n"
-" \tIf you are using old version of clang, change 'clang-bpf-cmd-template'\n"
-" \toption in [llvm] section of ~/.perfconfig to:\n\n"
-" \t \"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS $PERF_BPF_INC_OPTIONS \\\n"
-" \t -working-directory $WORKING_DIR -c $CLANG_SOURCE \\\n"
-" \t -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -\"\n"
-" \t(Replace /path/to/llc with path to your llc)\n\n"
-);
-}
-
-static int detect_kbuild_dir(char **kbuild_dir)
-{
- const char *test_dir = llvm_param.kbuild_dir;
- const char *prefix_dir = "";
- const char *suffix_dir = "";
-
- /* _UTSNAME_LENGTH is 65 */
- char release[128];
-
- char *autoconf_path;
-
- int err;
-
- if (!test_dir) {
- err = fetch_kernel_version(NULL, release,
- sizeof(release));
- if (err)
- return -EINVAL;
-
- test_dir = release;
- prefix_dir = "/lib/modules/";
- suffix_dir = "/build";
- }
-
- err = asprintf(&autoconf_path, "%s%s%s/include/generated/autoconf.h",
- prefix_dir, test_dir, suffix_dir);
- if (err < 0)
- return -ENOMEM;
-
- if (access(autoconf_path, R_OK) == 0) {
- free(autoconf_path);
-
- err = asprintf(kbuild_dir, "%s%s%s", prefix_dir, test_dir,
- suffix_dir);
- if (err < 0)
- return -ENOMEM;
- return 0;
- }
- pr_debug("%s: Couldn't find \"%s\", missing kernel-devel package?.\n",
- __func__, autoconf_path);
- free(autoconf_path);
- return -ENOENT;
-}
-
-static const char *kinc_fetch_script =
-"#!/usr/bin/env sh\n"
-"if ! test -d \"$KBUILD_DIR\"\n"
-"then\n"
-" exit 1\n"
-"fi\n"
-"if ! test -f \"$KBUILD_DIR/include/generated/autoconf.h\"\n"
-"then\n"
-" exit 1\n"
-"fi\n"
-"TMPDIR=`mktemp -d`\n"
-"if test -z \"$TMPDIR\"\n"
-"then\n"
-" exit 1\n"
-"fi\n"
-"cat << EOF > $TMPDIR/Makefile\n"
-"obj-y := dummy.o\n"
-"\\$(obj)/%.o: \\$(src)/%.c\n"
-"\t@echo -n \"\\$(NOSTDINC_FLAGS) \\$(LINUXINCLUDE) \\$(EXTRA_CFLAGS)\"\n"
-"\t\\$(CC) -c -o \\$@ \\$<\n"
-"EOF\n"
-"touch $TMPDIR/dummy.c\n"
-"make -s -C $KBUILD_DIR M=$TMPDIR $KBUILD_OPTS dummy.o 2>/dev/null\n"
-"RET=$?\n"
-"rm -rf $TMPDIR\n"
-"exit $RET\n";
-
-void llvm__get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts)
-{
- static char *saved_kbuild_dir;
- static char *saved_kbuild_include_opts;
- int err;
-
- if (!kbuild_dir || !kbuild_include_opts)
- return;
-
- *kbuild_dir = NULL;
- *kbuild_include_opts = NULL;
-
- if (saved_kbuild_dir && saved_kbuild_include_opts &&
- !IS_ERR(saved_kbuild_dir) && !IS_ERR(saved_kbuild_include_opts)) {
- *kbuild_dir = strdup(saved_kbuild_dir);
- *kbuild_include_opts = strdup(saved_kbuild_include_opts);
-
- if (*kbuild_dir && *kbuild_include_opts)
- return;
-
- zfree(kbuild_dir);
- zfree(kbuild_include_opts);
- /*
- * Don't fall through: it may breaks saved_kbuild_dir and
- * saved_kbuild_include_opts if detect them again when
- * memory is low.
- */
- return;
- }
-
- if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) {
- pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n");
- pr_debug("Skip kbuild options detection.\n");
- goto errout;
- }
-
- err = detect_kbuild_dir(kbuild_dir);
- if (err) {
- pr_warning(
-"WARNING:\tunable to get correct kernel building directory.\n"
-"Hint:\tSet correct kbuild directory using 'kbuild-dir' option in [llvm]\n"
-" \tsection of ~/.perfconfig or set it to \"\" to suppress kbuild\n"
-" \tdetection.\n\n");
- goto errout;
- }
-
- pr_debug("Kernel build dir is set to %s\n", *kbuild_dir);
- force_set_env("KBUILD_DIR", *kbuild_dir);
- force_set_env("KBUILD_OPTS", llvm_param.kbuild_opts);
- err = read_from_pipe(kinc_fetch_script,
- (void **)kbuild_include_opts,
- NULL);
- if (err) {
- pr_warning(
-"WARNING:\tunable to get kernel include directories from '%s'\n"
-"Hint:\tTry set clang include options using 'clang-bpf-cmd-template'\n"
-" \toption in [llvm] section of ~/.perfconfig and set 'kbuild-dir'\n"
-" \toption in [llvm] to \"\" to suppress this detection.\n\n",
- *kbuild_dir);
-
- zfree(kbuild_dir);
- goto errout;
- }
-
- pr_debug("include option is set to %s\n", *kbuild_include_opts);
-
- saved_kbuild_dir = strdup(*kbuild_dir);
- saved_kbuild_include_opts = strdup(*kbuild_include_opts);
-
- if (!saved_kbuild_dir || !saved_kbuild_include_opts) {
- zfree(&saved_kbuild_dir);
- zfree(&saved_kbuild_include_opts);
- }
- return;
-errout:
- saved_kbuild_dir = ERR_PTR(-EINVAL);
- saved_kbuild_include_opts = ERR_PTR(-EINVAL);
-}
-
-int llvm__get_nr_cpus(void)
-{
- static int nr_cpus_avail = 0;
- char serr[STRERR_BUFSIZE];
-
- if (nr_cpus_avail > 0)
- return nr_cpus_avail;
-
- nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF);
- if (nr_cpus_avail <= 0) {
- pr_err(
-"WARNING:\tunable to get available CPUs in this system: %s\n"
-" \tUse 128 instead.\n", str_error_r(errno, serr, sizeof(serr)));
- nr_cpus_avail = 128;
- }
- return nr_cpus_avail;
-}
-
-void llvm__dump_obj(const char *path, void *obj_buf, size_t size)
-{
- char *obj_path = strdup(path);
- FILE *fp;
- char *p;
-
- if (!obj_path) {
- pr_warning("WARNING: Not enough memory, skip object dumping\n");
- return;
- }
-
- p = strrchr(obj_path, '.');
- if (!p || (strcmp(p, ".c") != 0)) {
- pr_warning("WARNING: invalid llvm source path: '%s', skip object dumping\n",
- obj_path);
- goto out;
- }
-
- p[1] = 'o';
- fp = fopen(obj_path, "wb");
- if (!fp) {
- pr_warning("WARNING: failed to open '%s': %s, skip object dumping\n",
- obj_path, strerror(errno));
- goto out;
- }
-
- pr_debug("LLVM: dumping %s\n", obj_path);
- if (fwrite(obj_buf, size, 1, fp) != 1)
- pr_debug("WARNING: failed to write to file '%s': %s, skip object dumping\n", obj_path, strerror(errno));
- fclose(fp);
-out:
- free(obj_path);
-}
-
-int llvm__compile_bpf(const char *path, void **p_obj_buf,
- size_t *p_obj_buf_sz)
-{
- size_t obj_buf_sz;
- void *obj_buf = NULL;
- int err, nr_cpus_avail;
- unsigned int kernel_version;
- char linux_version_code_str[64];
- const char *clang_opt = llvm_param.clang_opt;
- char clang_path[PATH_MAX], llc_path[PATH_MAX], abspath[PATH_MAX], nr_cpus_avail_str[64];
- char serr[STRERR_BUFSIZE];
- char *kbuild_dir = NULL, *kbuild_include_opts = NULL,
- *perf_bpf_include_opts = NULL;
- const char *template = llvm_param.clang_bpf_cmd_template;
- char *pipe_template = NULL;
- const char *opts = llvm_param.opts;
- char *command_echo = NULL, *command_out;
- char *libbpf_include_dir = system_path(LIBBPF_INCLUDE_DIR);
-
- if (path[0] != '-' && realpath(path, abspath) == NULL) {
- err = errno;
- pr_err("ERROR: problems with path %s: %s\n",
- path, str_error_r(err, serr, sizeof(serr)));
- return -err;
- }
-
- if (!template)
- template = CLANG_BPF_CMD_DEFAULT_TEMPLATE;
-
- err = search_program_and_warn(llvm_param.clang_path,
- "clang", clang_path);
- if (err)
- return -ENOENT;
-
- /*
- * This is an optional work. Even it fail we can continue our
- * work. Needn't check error return.
- */
- llvm__get_kbuild_opts(&kbuild_dir, &kbuild_include_opts);
-
- nr_cpus_avail = llvm__get_nr_cpus();
- snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d",
- nr_cpus_avail);
-
- if (fetch_kernel_version(&kernel_version, NULL, 0))
- kernel_version = 0;
-
- snprintf(linux_version_code_str, sizeof(linux_version_code_str),
- "0x%x", kernel_version);
- if (asprintf(&perf_bpf_include_opts, "-I%s/", libbpf_include_dir) < 0)
- goto errout;
- force_set_env("NR_CPUS", nr_cpus_avail_str);
- force_set_env("LINUX_VERSION_CODE", linux_version_code_str);
- force_set_env("CLANG_EXEC", clang_path);
- force_set_env("CLANG_OPTIONS", clang_opt);
- force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts);
- force_set_env("PERF_BPF_INC_OPTIONS", perf_bpf_include_opts);
- force_set_env("WORKING_DIR", kbuild_dir ? : ".");
-
- if (opts) {
- err = search_program_and_warn(llvm_param.llc_path, "llc", llc_path);
- if (err)
- goto errout;
-
- err = -ENOMEM;
- if (asprintf(&pipe_template, "%s -emit-llvm | %s -march=bpf %s -filetype=obj -o -",
- template, llc_path, opts) < 0) {
- pr_err("ERROR:\tnot enough memory to setup command line\n");
- goto errout;
- }
-
- template = pipe_template;
-
- }
-
- /*
- * Since we may reset clang's working dir, path of source file
- * should be transferred into absolute path, except we want
- * stdin to be source file (testing).
- */
- force_set_env("CLANG_SOURCE",
- (path[0] == '-') ? path : abspath);
-
- pr_debug("llvm compiling command template: %s\n", template);
-
- /*
- * Below, substitute control characters for values that can cause the
- * echo to misbehave, then substitute the values back.
- */
- err = -ENOMEM;
- if (asprintf(&command_echo, "echo -n \a%s\a", template) < 0)
- goto errout;
-
-#define SWAP_CHAR(a, b) do { if (*p == a) *p = b; } while (0)
- for (char *p = command_echo; *p; p++) {
- SWAP_CHAR('<', '\001');
- SWAP_CHAR('>', '\002');
- SWAP_CHAR('"', '\003');
- SWAP_CHAR('\'', '\004');
- SWAP_CHAR('|', '\005');
- SWAP_CHAR('&', '\006');
- SWAP_CHAR('\a', '"');
- }
- err = read_from_pipe(command_echo, (void **) &command_out, NULL);
- if (err)
- goto errout;
-
- for (char *p = command_out; *p; p++) {
- SWAP_CHAR('\001', '<');
- SWAP_CHAR('\002', '>');
- SWAP_CHAR('\003', '"');
- SWAP_CHAR('\004', '\'');
- SWAP_CHAR('\005', '|');
- SWAP_CHAR('\006', '&');
- }
-#undef SWAP_CHAR
- pr_debug("llvm compiling command : %s\n", command_out);
-
- err = read_from_pipe(template, &obj_buf, &obj_buf_sz);
- if (err) {
- pr_err("ERROR:\tunable to compile %s\n", path);
- pr_err("Hint:\tCheck error message shown above.\n");
- pr_err("Hint:\tYou can also pre-compile it into .o using:\n");
- pr_err(" \t\tclang --target=bpf -O2 -c %s\n", path);
- pr_err(" \twith proper -I and -D options.\n");
- goto errout;
- }
-
- free(command_echo);
- free(command_out);
- free(kbuild_dir);
- free(kbuild_include_opts);
- free(perf_bpf_include_opts);
- free(libbpf_include_dir);
-
- if (!p_obj_buf)
- free(obj_buf);
- else
- *p_obj_buf = obj_buf;
-
- if (p_obj_buf_sz)
- *p_obj_buf_sz = obj_buf_sz;
- return 0;
-errout:
- free(command_echo);
- free(kbuild_dir);
- free(kbuild_include_opts);
- free(obj_buf);
- free(perf_bpf_include_opts);
- free(libbpf_include_dir);
- free(pipe_template);
- if (p_obj_buf)
- *p_obj_buf = NULL;
- if (p_obj_buf_sz)
- *p_obj_buf_sz = 0;
- return err;
-}
-
-int llvm__search_clang(void)
-{
- char clang_path[PATH_MAX];
-
- return search_program_and_warn(llvm_param.clang_path, "clang", clang_path);
-}
diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h
deleted file mode 100644
index 7878a0e3fa98..000000000000
--- a/tools/perf/util/llvm-utils.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (C) 2015, Wang Nan <wangnan0@huawei.com>
- * Copyright (C) 2015, Huawei Inc.
- */
-#ifndef __LLVM_UTILS_H
-#define __LLVM_UTILS_H
-
-#include <stdbool.h>
-
-struct llvm_param {
- /* Path of clang executable */
- const char *clang_path;
- /* Path of llc executable */
- const char *llc_path;
- /*
- * Template of clang bpf compiling. 5 env variables
- * can be used:
- * $CLANG_EXEC: Path to clang.
- * $CLANG_OPTIONS: Extra options to clang.
- * $KERNEL_INC_OPTIONS: Kernel include directories.
- * $WORKING_DIR: Kernel source directory.
- * $CLANG_SOURCE: Source file to be compiled.
- */
- const char *clang_bpf_cmd_template;
- /* Will be filled in $CLANG_OPTIONS */
- const char *clang_opt;
- /*
- * If present it'll add -emit-llvm to $CLANG_OPTIONS to pipe
- * the clang output to llc, useful for new llvm options not
- * yet selectable via 'clang -mllvm option', such as -mattr=dwarfris
- * in clang 6.0/llvm 7
- */
- const char *opts;
- /* Where to find kbuild system */
- const char *kbuild_dir;
- /*
- * Arguments passed to make, like 'ARCH=arm' if doing cross
- * compiling. Should not be used for dynamic compiling.
- */
- const char *kbuild_opts;
- /*
- * Default is false. If set to true, write compiling result
- * to object file.
- */
- bool dump_obj;
- /*
- * Default is false. If one of the above fields is set by user
- * explicitly then user_set_llvm is set to true. This is used
- * for perf test. If user doesn't set anything in .perfconfig
- * and clang is not found, don't trigger llvm test.
- */
- bool user_set_param;
-};
-
-extern struct llvm_param llvm_param;
-int perf_llvm_config(const char *var, const char *value);
-
-int llvm__compile_bpf(const char *path, void **p_obj_buf, size_t *p_obj_buf_sz);
-
-/* This function is for test__llvm() use only */
-int llvm__search_clang(void);
-
-/* Following functions are reused by builtin clang support */
-void llvm__get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts);
-int llvm__get_nr_cpus(void);
-
-void llvm__dump_obj(const char *path, void *obj_buf, size_t size);
-#endif
diff --git a/tools/perf/util/lzma.c b/tools/perf/util/lzma.c
index 51424cdc3b68..af9a97612f9d 100644
--- a/tools/perf/util/lzma.c
+++ b/tools/perf/util/lzma.c
@@ -45,15 +45,13 @@ int lzma_decompress_to_file(const char *input, int output_fd)
infile = fopen(input, "rb");
if (!infile) {
- pr_err("lzma: fopen failed on %s: '%s'\n",
- input, strerror(errno));
+ pr_debug("lzma: fopen failed on %s: '%s'\n", input, strerror(errno));
return -1;
}
ret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
if (ret != LZMA_OK) {
- pr_err("lzma: lzma_stream_decoder failed %s (%d)\n",
- lzma_strerror(ret), ret);
+ pr_debug("lzma: lzma_stream_decoder failed %s (%d)\n", lzma_strerror(ret), ret);
goto err_fclose;
}
@@ -68,7 +66,7 @@ int lzma_decompress_to_file(const char *input, int output_fd)
strm.avail_in = fread(buf_in, 1, sizeof(buf_in), infile);
if (ferror(infile)) {
- pr_err("lzma: read error: %s\n", strerror(errno));
+ pr_debug("lzma: read error: %s\n", strerror(errno));
goto err_lzma_end;
}
@@ -82,7 +80,7 @@ int lzma_decompress_to_file(const char *input, int output_fd)
ssize_t write_size = sizeof(buf_out) - strm.avail_out;
if (writen(output_fd, buf_out, write_size) != write_size) {
- pr_err("lzma: write error: %s\n", strerror(errno));
+ pr_debug("lzma: write error: %s\n", strerror(errno));
goto err_lzma_end;
}
@@ -94,7 +92,7 @@ int lzma_decompress_to_file(const char *input, int output_fd)
if (ret == LZMA_STREAM_END)
break;
- pr_err("lzma: failed %s\n", lzma_strerror(ret));
+ pr_debug("lzma: failed %s\n", lzma_strerror(ret));
goto err_lzma_end;
}
}
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index f4cb41ee23cd..88f31b3a63ac 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1215,7 +1215,9 @@ static int machine__get_running_kernel_start(struct machine *machine,
*start = addr;
- err = kallsyms__get_function_start(filename, "_etext", &addr);
+ err = kallsyms__get_symbol_start(filename, "_edata", &addr);
+ if (err)
+ err = kallsyms__get_function_start(filename, "_etext", &addr);
if (!err)
*end = addr;
diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c
index c07fe3a90722..39ffe8ceb380 100644
--- a/tools/perf/util/mem-events.c
+++ b/tools/perf/util/mem-events.c
@@ -37,7 +37,7 @@ struct perf_mem_event * __weak perf_mem_events__ptr(int i)
return &perf_mem_events[i];
}
-char * __weak perf_mem_events__name(int i, char *pmu_name __maybe_unused)
+const char * __weak perf_mem_events__name(int i, const char *pmu_name __maybe_unused)
{
struct perf_mem_event *e = perf_mem_events__ptr(i);
@@ -53,7 +53,7 @@ char * __weak perf_mem_events__name(int i, char *pmu_name __maybe_unused)
return mem_loads_name;
}
- return (char *)e->name;
+ return e->name;
}
__weak bool is_mem_loads_aux_event(struct evsel *leader __maybe_unused)
@@ -186,7 +186,6 @@ int perf_mem_events__record_args(const char **rec_argv, int *argv_nr,
int i = *argv_nr, k = 0;
struct perf_mem_event *e;
struct perf_pmu *pmu;
- char *s;
for (int j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
e = perf_mem_events__ptr(j);
@@ -209,15 +208,16 @@ int perf_mem_events__record_args(const char **rec_argv, int *argv_nr,
}
while ((pmu = perf_pmus__scan(pmu)) != NULL) {
+ const char *s = perf_mem_events__name(j, pmu->name);
+
rec_argv[i++] = "-e";
- s = perf_mem_events__name(j, pmu->name);
if (s) {
- s = strdup(s);
- if (!s)
+ char *copy = strdup(s);
+ if (!copy)
return -1;
- rec_argv[i++] = s;
- rec_tmp[k++] = s;
+ rec_argv[i++] = copy;
+ rec_tmp[k++] = copy;
}
}
}
diff --git a/tools/perf/util/mem-events.h b/tools/perf/util/mem-events.h
index 12372309d60e..b40ad6ea93fc 100644
--- a/tools/perf/util/mem-events.h
+++ b/tools/perf/util/mem-events.h
@@ -38,7 +38,7 @@ extern unsigned int perf_mem_events__loads_ldlat;
int perf_mem_events__parse(const char *str);
int perf_mem_events__init(void);
-char *perf_mem_events__name(int i, char *pmu_name);
+const char *perf_mem_events__name(int i, const char *pmu_name);
struct perf_mem_event *perf_mem_events__ptr(int i);
bool is_mem_loads_aux_event(struct evsel *leader);
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index a6a5ed44a679..6231044a491e 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -527,7 +527,7 @@ void metricgroup__print(const struct print_callbacks *print_cb, void *print_stat
groups.node_delete = mep_delete;
table = pmu_metrics_table__find();
if (table) {
- pmu_metrics_table_for_each_metric(table,
+ pmu_metrics_table__for_each_metric(table,
metricgroup__add_to_mep_groups_callback,
&groups);
}
@@ -1069,7 +1069,7 @@ static bool metricgroup__find_metric(const char *pmu,
.pm = pm,
};
- return pmu_metrics_table_for_each_metric(table, metricgroup__find_metric_callback, &data)
+ return pmu_metrics_table__for_each_metric(table, metricgroup__find_metric_callback, &data)
? true : false;
}
@@ -1255,7 +1255,7 @@ static int metricgroup__add_metric(const char *pmu, const char *metric_name, con
* Iterate over all metrics seeing if metric matches either the
* name or group. When it does add the metric to the list.
*/
- ret = pmu_metrics_table_for_each_metric(table, metricgroup__add_metric_callback,
+ ret = pmu_metrics_table__for_each_metric(table, metricgroup__add_metric_callback,
&data);
if (ret)
goto out;
@@ -1740,7 +1740,7 @@ bool metricgroup__has_metric(const char *pmu, const char *metric)
if (!table)
return false;
- return pmu_metrics_table_for_each_metric(table, metricgroup__has_metric_callback, &data)
+ return pmu_metrics_table__for_each_metric(table, metricgroup__has_metric_callback, &data)
? true : false;
}
@@ -1770,7 +1770,7 @@ unsigned int metricgroups__topdown_max_level(void)
if (!table)
return false;
- pmu_metrics_table_for_each_metric(table, metricgroup__topdown_max_level_callback,
+ pmu_metrics_table__for_each_metric(table, metricgroup__topdown_max_level_callback,
&max_level);
return max_level;
}
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index c9ec0cafb69d..65608a3cba81 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -13,13 +13,12 @@
#include <subcmd/parse-options.h>
#include "parse-events.h"
#include "string2.h"
-#include "strlist.h"
-#include "bpf-loader.h"
+#include "strbuf.h"
#include "debug.h"
#include <api/fs/tracing_path.h>
#include <perf/cpumap.h>
-#include "parse-events-bison.h"
-#include "parse-events-flex.h"
+#include <util/parse-events-bison.h>
+#include <util/parse-events-flex.h>
#include "pmu.h"
#include "pmus.h"
#include "asm/bug.h"
@@ -35,7 +34,6 @@
#ifdef PARSER_DEBUG
extern int parse_events_debug;
#endif
-int parse_events_parse(void *parse_state, void *scanner);
static int get_config_terms(struct list_head *head_config,
struct list_head *head_terms __maybe_unused);
@@ -155,7 +153,7 @@ const char *event_type(int type)
return "unknown";
}
-static char *get_config_str(struct list_head *head_terms, int type_term)
+static char *get_config_str(struct list_head *head_terms, enum parse_events__term_type type_term)
{
struct parse_events_term *term;
@@ -195,38 +193,31 @@ static void fix_raw(struct list_head *config_terms, struct perf_pmu *pmu)
struct parse_events_term *term;
list_for_each_entry(term, config_terms, list) {
- struct perf_pmu_alias *alias;
- bool matched = false;
+ u64 num;
if (term->type_term != PARSE_EVENTS__TERM_TYPE_RAW)
continue;
- list_for_each_entry(alias, &pmu->aliases, list) {
- if (!strcmp(alias->name, term->val.str)) {
- free(term->config);
- term->config = term->val.str;
- term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
- term->type_term = PARSE_EVENTS__TERM_TYPE_USER;
- term->val.num = 1;
- term->no_value = true;
- matched = true;
- break;
- }
- }
- if (!matched) {
- u64 num;
-
- free(term->config);
- term->config = strdup("config");
- errno = 0;
- num = strtoull(term->val.str + 1, NULL, 16);
- assert(errno == 0);
- free(term->val.str);
+ if (perf_pmu__have_event(pmu, term->val.str)) {
+ zfree(&term->config);
+ term->config = term->val.str;
term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
- term->type_term = PARSE_EVENTS__TERM_TYPE_CONFIG;
- term->val.num = num;
- term->no_value = false;
+ term->type_term = PARSE_EVENTS__TERM_TYPE_USER;
+ term->val.num = 1;
+ term->no_value = true;
+ continue;
}
+
+ zfree(&term->config);
+ term->config = strdup("config");
+ errno = 0;
+ num = strtoull(term->val.str + 1, NULL, 16);
+ assert(errno == 0);
+ free(term->val.str);
+ term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
+ term->type_term = PARSE_EVENTS__TERM_TYPE_CONFIG;
+ term->val.num = num;
+ term->no_value = false;
}
}
@@ -271,7 +262,7 @@ __add_event(struct list_head *list, int *idx,
evsel->core.is_pmu_core = pmu ? pmu->is_core : false;
evsel->auto_merge_stats = auto_merge_stats;
evsel->pmu = pmu;
- evsel->pmu_name = pmu && pmu->name ? strdup(pmu->name) : NULL;
+ evsel->pmu_name = pmu ? strdup(pmu->name) : NULL;
if (name)
evsel->name = strdup(name);
@@ -446,9 +437,6 @@ bool parse_events__filter_pmu(const struct parse_events_state *parse_state,
if (parse_state->pmu_filter == NULL)
return false;
- if (pmu->name == NULL)
- return true;
-
return strcmp(parse_state->pmu_filter, pmu->name) != 0;
}
@@ -499,7 +487,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, const char *name,
#ifdef HAVE_LIBTRACEEVENT
static void tracepoint_error(struct parse_events_error *e, int err,
- const char *sys, const char *name)
+ const char *sys, const char *name, int column)
{
const char *str;
char help[BUFSIZ];
@@ -526,18 +514,19 @@ static void tracepoint_error(struct parse_events_error *e, int err,
}
tracing_path__strerror_open_tp(err, help, sizeof(help), sys, name);
- parse_events_error__handle(e, 0, strdup(str), strdup(help));
+ parse_events_error__handle(e, column, strdup(str), strdup(help));
}
static int add_tracepoint(struct list_head *list, int *idx,
const char *sys_name, const char *evt_name,
struct parse_events_error *err,
- struct list_head *head_config)
+ struct list_head *head_config, void *loc_)
{
+ YYLTYPE *loc = loc_;
struct evsel *evsel = evsel__newtp_idx(sys_name, evt_name, (*idx)++);
if (IS_ERR(evsel)) {
- tracepoint_error(err, PTR_ERR(evsel), sys_name, evt_name);
+ tracepoint_error(err, PTR_ERR(evsel), sys_name, evt_name, loc->first_column);
return PTR_ERR(evsel);
}
@@ -556,7 +545,7 @@ static int add_tracepoint(struct list_head *list, int *idx,
static int add_tracepoint_multi_event(struct list_head *list, int *idx,
const char *sys_name, const char *evt_name,
struct parse_events_error *err,
- struct list_head *head_config)
+ struct list_head *head_config, YYLTYPE *loc)
{
char *evt_path;
struct dirent *evt_ent;
@@ -565,13 +554,13 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx,
evt_path = get_events_file(sys_name);
if (!evt_path) {
- tracepoint_error(err, errno, sys_name, evt_name);
+ tracepoint_error(err, errno, sys_name, evt_name, loc->first_column);
return -1;
}
evt_dir = opendir(evt_path);
if (!evt_dir) {
put_events_file(evt_path);
- tracepoint_error(err, errno, sys_name, evt_name);
+ tracepoint_error(err, errno, sys_name, evt_name, loc->first_column);
return -1;
}
@@ -588,11 +577,11 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx,
found++;
ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name,
- err, head_config);
+ err, head_config, loc);
}
if (!found) {
- tracepoint_error(err, ENOENT, sys_name, evt_name);
+ tracepoint_error(err, ENOENT, sys_name, evt_name, loc->first_column);
ret = -1;
}
@@ -604,19 +593,19 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx,
static int add_tracepoint_event(struct list_head *list, int *idx,
const char *sys_name, const char *evt_name,
struct parse_events_error *err,
- struct list_head *head_config)
+ struct list_head *head_config, YYLTYPE *loc)
{
return strpbrk(evt_name, "*?") ?
- add_tracepoint_multi_event(list, idx, sys_name, evt_name,
- err, head_config) :
- add_tracepoint(list, idx, sys_name, evt_name,
- err, head_config);
+ add_tracepoint_multi_event(list, idx, sys_name, evt_name,
+ err, head_config, loc) :
+ add_tracepoint(list, idx, sys_name, evt_name,
+ err, head_config, loc);
}
static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
const char *sys_name, const char *evt_name,
struct parse_events_error *err,
- struct list_head *head_config)
+ struct list_head *head_config, YYLTYPE *loc)
{
struct dirent *events_ent;
DIR *events_dir;
@@ -624,7 +613,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
events_dir = tracing_events__opendir();
if (!events_dir) {
- tracepoint_error(err, errno, sys_name, evt_name);
+ tracepoint_error(err, errno, sys_name, evt_name, loc->first_column);
return -1;
}
@@ -640,7 +629,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
continue;
ret = add_tracepoint_event(list, idx, events_ent->d_name,
- evt_name, err, head_config);
+ evt_name, err, head_config, loc);
}
closedir(events_dir);
@@ -648,264 +637,6 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
}
#endif /* HAVE_LIBTRACEEVENT */
-#ifdef HAVE_LIBBPF_SUPPORT
-struct __add_bpf_event_param {
- struct parse_events_state *parse_state;
- struct list_head *list;
- struct list_head *head_config;
-};
-
-static int add_bpf_event(const char *group, const char *event, int fd, struct bpf_object *obj,
- void *_param)
-{
- LIST_HEAD(new_evsels);
- struct __add_bpf_event_param *param = _param;
- struct parse_events_state *parse_state = param->parse_state;
- struct list_head *list = param->list;
- struct evsel *pos;
- int err;
- /*
- * Check if we should add the event, i.e. if it is a TP but starts with a '!',
- * then don't add the tracepoint, this will be used for something else, like
- * adding to a BPF_MAP_TYPE_PROG_ARRAY.
- *
- * See tools/perf/examples/bpf/augmented_raw_syscalls.c
- */
- if (group[0] == '!')
- return 0;
-
- pr_debug("add bpf event %s:%s and attach bpf program %d\n",
- group, event, fd);
-
- err = parse_events_add_tracepoint(&new_evsels, &parse_state->idx, group,
- event, parse_state->error,
- param->head_config);
- if (err) {
- struct evsel *evsel, *tmp;
-
- pr_debug("Failed to add BPF event %s:%s\n",
- group, event);
- list_for_each_entry_safe(evsel, tmp, &new_evsels, core.node) {
- list_del_init(&evsel->core.node);
- evsel__delete(evsel);
- }
- return err;
- }
- pr_debug("adding %s:%s\n", group, event);
-
- list_for_each_entry(pos, &new_evsels, core.node) {
- pr_debug("adding %s:%s to %p\n",
- group, event, pos);
- pos->bpf_fd = fd;
- pos->bpf_obj = obj;
- }
- list_splice(&new_evsels, list);
- return 0;
-}
-
-int parse_events_load_bpf_obj(struct parse_events_state *parse_state,
- struct list_head *list,
- struct bpf_object *obj,
- struct list_head *head_config)
-{
- int err;
- char errbuf[BUFSIZ];
- struct __add_bpf_event_param param = {parse_state, list, head_config};
- static bool registered_unprobe_atexit = false;
-
- if (IS_ERR(obj) || !obj) {
- snprintf(errbuf, sizeof(errbuf),
- "Internal error: load bpf obj with NULL");
- err = -EINVAL;
- goto errout;
- }
-
- /*
- * Register atexit handler before calling bpf__probe() so
- * bpf__probe() don't need to unprobe probe points its already
- * created when failure.
- */
- if (!registered_unprobe_atexit) {
- atexit(bpf__clear);
- registered_unprobe_atexit = true;
- }
-
- err = bpf__probe(obj);
- if (err) {
- bpf__strerror_probe(obj, err, errbuf, sizeof(errbuf));
- goto errout;
- }
-
- err = bpf__load(obj);
- if (err) {
- bpf__strerror_load(obj, err, errbuf, sizeof(errbuf));
- goto errout;
- }
-
- err = bpf__foreach_event(obj, add_bpf_event, &param);
- if (err) {
- snprintf(errbuf, sizeof(errbuf),
- "Attach events in BPF object failed");
- goto errout;
- }
-
- return 0;
-errout:
- parse_events_error__handle(parse_state->error, 0,
- strdup(errbuf), strdup("(add -v to see detail)"));
- return err;
-}
-
-static int
-parse_events_config_bpf(struct parse_events_state *parse_state,
- struct bpf_object *obj,
- struct list_head *head_config)
-{
- struct parse_events_term *term;
- int error_pos;
-
- if (!head_config || list_empty(head_config))
- return 0;
-
- list_for_each_entry(term, head_config, list) {
- int err;
-
- if (term->type_term != PARSE_EVENTS__TERM_TYPE_USER) {
- parse_events_error__handle(parse_state->error, term->err_term,
- strdup("Invalid config term for BPF object"),
- NULL);
- return -EINVAL;
- }
-
- err = bpf__config_obj(obj, term, parse_state->evlist, &error_pos);
- if (err) {
- char errbuf[BUFSIZ];
- int idx;
-
- bpf__strerror_config_obj(obj, term, parse_state->evlist,
- &error_pos, err, errbuf,
- sizeof(errbuf));
-
- if (err == -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE)
- idx = term->err_val;
- else
- idx = term->err_term + error_pos;
-
- parse_events_error__handle(parse_state->error, idx,
- strdup(errbuf),
- strdup(
-"Hint:\tValid config terms:\n"
-" \tmap:[<arraymap>].value<indices>=[value]\n"
-" \tmap:[<eventmap>].event<indices>=[event]\n"
-"\n"
-" \twhere <indices> is something like [0,3...5] or [all]\n"
-" \t(add -v to see detail)"));
- return err;
- }
- }
- return 0;
-}
-
-/*
- * Split config terms:
- * perf record -e bpf.c/call-graph=fp,map:array.value[0]=1/ ...
- * 'call-graph=fp' is 'evt config', should be applied to each
- * events in bpf.c.
- * 'map:array.value[0]=1' is 'obj config', should be processed
- * with parse_events_config_bpf.
- *
- * Move object config terms from the first list to obj_head_config.
- */
-static void
-split_bpf_config_terms(struct list_head *evt_head_config,
- struct list_head *obj_head_config)
-{
- struct parse_events_term *term, *temp;
-
- /*
- * Currently, all possible user config term
- * belong to bpf object. parse_events__is_hardcoded_term()
- * happens to be a good flag.
- *
- * See parse_events_config_bpf() and
- * config_term_tracepoint().
- */
- list_for_each_entry_safe(term, temp, evt_head_config, list)
- if (!parse_events__is_hardcoded_term(term))
- list_move_tail(&term->list, obj_head_config);
-}
-
-int parse_events_load_bpf(struct parse_events_state *parse_state,
- struct list_head *list,
- char *bpf_file_name,
- bool source,
- struct list_head *head_config)
-{
- int err;
- struct bpf_object *obj;
- LIST_HEAD(obj_head_config);
-
- if (head_config)
- split_bpf_config_terms(head_config, &obj_head_config);
-
- obj = bpf__prepare_load(bpf_file_name, source);
- if (IS_ERR(obj)) {
- char errbuf[BUFSIZ];
-
- err = PTR_ERR(obj);
-
- if (err == -ENOTSUP)
- snprintf(errbuf, sizeof(errbuf),
- "BPF support is not compiled");
- else
- bpf__strerror_prepare_load(bpf_file_name,
- source,
- -err, errbuf,
- sizeof(errbuf));
-
- parse_events_error__handle(parse_state->error, 0,
- strdup(errbuf), strdup("(add -v to see detail)"));
- return err;
- }
-
- err = parse_events_load_bpf_obj(parse_state, list, obj, head_config);
- if (err)
- return err;
- err = parse_events_config_bpf(parse_state, obj, &obj_head_config);
-
- /*
- * Caller doesn't know anything about obj_head_config,
- * so combine them together again before returning.
- */
- if (head_config)
- list_splice_tail(&obj_head_config, head_config);
- return err;
-}
-#else // HAVE_LIBBPF_SUPPORT
-int parse_events_load_bpf_obj(struct parse_events_state *parse_state,
- struct list_head *list __maybe_unused,
- struct bpf_object *obj __maybe_unused,
- struct list_head *head_config __maybe_unused)
-{
- parse_events_error__handle(parse_state->error, 0,
- strdup("BPF support is not compiled"),
- strdup("Make sure libbpf-devel is available at build time."));
- return -ENOTSUP;
-}
-
-int parse_events_load_bpf(struct parse_events_state *parse_state,
- struct list_head *list __maybe_unused,
- char *bpf_file_name __maybe_unused,
- bool source __maybe_unused,
- struct list_head *head_config __maybe_unused)
-{
- parse_events_error__handle(parse_state->error, 0,
- strdup("BPF support is not compiled"),
- strdup("Make sure libbpf-devel is available at build time."));
- return -ENOTSUP;
-}
-#endif // HAVE_LIBBPF_SUPPORT
-
static int
parse_breakpoint_type(const char *type, struct perf_event_attr *attr)
{
@@ -991,7 +722,7 @@ int parse_events_add_breakpoint(struct parse_events_state *parse_state,
static int check_type_val(struct parse_events_term *term,
struct parse_events_error *err,
- int type)
+ enum parse_events__term_val_type type)
{
if (type == term->type_val)
return 0;
@@ -1006,42 +737,49 @@ static int check_type_val(struct parse_events_term *term,
return -EINVAL;
}
-/*
- * Update according to parse-events.l
- */
-static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = {
- [PARSE_EVENTS__TERM_TYPE_USER] = "<sysfs term>",
- [PARSE_EVENTS__TERM_TYPE_CONFIG] = "config",
- [PARSE_EVENTS__TERM_TYPE_CONFIG1] = "config1",
- [PARSE_EVENTS__TERM_TYPE_CONFIG2] = "config2",
- [PARSE_EVENTS__TERM_TYPE_CONFIG3] = "config3",
- [PARSE_EVENTS__TERM_TYPE_NAME] = "name",
- [PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD] = "period",
- [PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ] = "freq",
- [PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE] = "branch_type",
- [PARSE_EVENTS__TERM_TYPE_TIME] = "time",
- [PARSE_EVENTS__TERM_TYPE_CALLGRAPH] = "call-graph",
- [PARSE_EVENTS__TERM_TYPE_STACKSIZE] = "stack-size",
- [PARSE_EVENTS__TERM_TYPE_NOINHERIT] = "no-inherit",
- [PARSE_EVENTS__TERM_TYPE_INHERIT] = "inherit",
- [PARSE_EVENTS__TERM_TYPE_MAX_STACK] = "max-stack",
- [PARSE_EVENTS__TERM_TYPE_MAX_EVENTS] = "nr",
- [PARSE_EVENTS__TERM_TYPE_OVERWRITE] = "overwrite",
- [PARSE_EVENTS__TERM_TYPE_NOOVERWRITE] = "no-overwrite",
- [PARSE_EVENTS__TERM_TYPE_DRV_CFG] = "driver-config",
- [PARSE_EVENTS__TERM_TYPE_PERCORE] = "percore",
- [PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT] = "aux-output",
- [PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE] = "aux-sample-size",
- [PARSE_EVENTS__TERM_TYPE_METRIC_ID] = "metric-id",
- [PARSE_EVENTS__TERM_TYPE_RAW] = "raw",
- [PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE] = "legacy-cache",
- [PARSE_EVENTS__TERM_TYPE_HARDWARE] = "hardware",
-};
-
static bool config_term_shrinked;
+static const char *config_term_name(enum parse_events__term_type term_type)
+{
+ /*
+ * Update according to parse-events.l
+ */
+ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = {
+ [PARSE_EVENTS__TERM_TYPE_USER] = "<sysfs term>",
+ [PARSE_EVENTS__TERM_TYPE_CONFIG] = "config",
+ [PARSE_EVENTS__TERM_TYPE_CONFIG1] = "config1",
+ [PARSE_EVENTS__TERM_TYPE_CONFIG2] = "config2",
+ [PARSE_EVENTS__TERM_TYPE_CONFIG3] = "config3",
+ [PARSE_EVENTS__TERM_TYPE_NAME] = "name",
+ [PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD] = "period",
+ [PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ] = "freq",
+ [PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE] = "branch_type",
+ [PARSE_EVENTS__TERM_TYPE_TIME] = "time",
+ [PARSE_EVENTS__TERM_TYPE_CALLGRAPH] = "call-graph",
+ [PARSE_EVENTS__TERM_TYPE_STACKSIZE] = "stack-size",
+ [PARSE_EVENTS__TERM_TYPE_NOINHERIT] = "no-inherit",
+ [PARSE_EVENTS__TERM_TYPE_INHERIT] = "inherit",
+ [PARSE_EVENTS__TERM_TYPE_MAX_STACK] = "max-stack",
+ [PARSE_EVENTS__TERM_TYPE_MAX_EVENTS] = "nr",
+ [PARSE_EVENTS__TERM_TYPE_OVERWRITE] = "overwrite",
+ [PARSE_EVENTS__TERM_TYPE_NOOVERWRITE] = "no-overwrite",
+ [PARSE_EVENTS__TERM_TYPE_DRV_CFG] = "driver-config",
+ [PARSE_EVENTS__TERM_TYPE_PERCORE] = "percore",
+ [PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT] = "aux-output",
+ [PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE] = "aux-sample-size",
+ [PARSE_EVENTS__TERM_TYPE_METRIC_ID] = "metric-id",
+ [PARSE_EVENTS__TERM_TYPE_RAW] = "raw",
+ [PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE] = "legacy-cache",
+ [PARSE_EVENTS__TERM_TYPE_HARDWARE] = "hardware",
+ };
+ if ((unsigned int)term_type >= __PARSE_EVENTS__TERM_TYPE_NR)
+ return "unknown term";
+
+ return config_term_names[term_type];
+}
+
static bool
-config_term_avail(int term_type, struct parse_events_error *err)
+config_term_avail(enum parse_events__term_type term_type, struct parse_events_error *err)
{
char *err_str;
@@ -1063,13 +801,31 @@ config_term_avail(int term_type, struct parse_events_error *err)
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
case PARSE_EVENTS__TERM_TYPE_PERCORE:
return true;
+ case PARSE_EVENTS__TERM_TYPE_USER:
+ case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ:
+ case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
+ case PARSE_EVENTS__TERM_TYPE_TIME:
+ case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
+ case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
+ case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
+ case PARSE_EVENTS__TERM_TYPE_INHERIT:
+ case PARSE_EVENTS__TERM_TYPE_MAX_STACK:
+ case PARSE_EVENTS__TERM_TYPE_MAX_EVENTS:
+ case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE:
+ case PARSE_EVENTS__TERM_TYPE_OVERWRITE:
+ case PARSE_EVENTS__TERM_TYPE_DRV_CFG:
+ case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
+ case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE:
+ case PARSE_EVENTS__TERM_TYPE_RAW:
+ case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE:
+ case PARSE_EVENTS__TERM_TYPE_HARDWARE:
default:
if (!err)
return false;
/* term_type is validated so indexing is safe */
if (asprintf(&err_str, "'%s' is not usable in 'perf stat'",
- config_term_names[term_type]) >= 0)
+ config_term_name(term_type)) >= 0)
parse_events_error__handle(err, -1, err_str, NULL);
return false;
}
@@ -1187,10 +943,14 @@ do { \
return -EINVAL;
}
break;
+ case PARSE_EVENTS__TERM_TYPE_DRV_CFG:
+ case PARSE_EVENTS__TERM_TYPE_USER:
+ case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE:
+ case PARSE_EVENTS__TERM_TYPE_HARDWARE:
default:
parse_events_error__handle(err, term->err_term,
- strdup("unknown term"),
- parse_events_formats_error_string(NULL));
+ strdup(config_term_name(term->type_term)),
+ parse_events_formats_error_string(NULL));
return -EINVAL;
}
@@ -1276,10 +1036,26 @@ static int config_term_tracepoint(struct perf_event_attr *attr,
case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE:
return config_term_common(attr, term, err);
+ case PARSE_EVENTS__TERM_TYPE_USER:
+ case PARSE_EVENTS__TERM_TYPE_CONFIG:
+ case PARSE_EVENTS__TERM_TYPE_CONFIG1:
+ case PARSE_EVENTS__TERM_TYPE_CONFIG2:
+ case PARSE_EVENTS__TERM_TYPE_CONFIG3:
+ case PARSE_EVENTS__TERM_TYPE_NAME:
+ case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
+ case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ:
+ case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
+ case PARSE_EVENTS__TERM_TYPE_TIME:
+ case PARSE_EVENTS__TERM_TYPE_DRV_CFG:
+ case PARSE_EVENTS__TERM_TYPE_PERCORE:
+ case PARSE_EVENTS__TERM_TYPE_METRIC_ID:
+ case PARSE_EVENTS__TERM_TYPE_RAW:
+ case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE:
+ case PARSE_EVENTS__TERM_TYPE_HARDWARE:
default:
if (err) {
parse_events_error__handle(err, term->err_term,
- strdup("unknown term"),
+ strdup(config_term_name(term->type_term)),
strdup("valid terms: call-graph,stack-size\n"));
}
return -EINVAL;
@@ -1397,6 +1173,16 @@ do { \
ADD_CONFIG_TERM_VAL(AUX_SAMPLE_SIZE, aux_sample_size,
term->val.num, term->weak);
break;
+ case PARSE_EVENTS__TERM_TYPE_USER:
+ case PARSE_EVENTS__TERM_TYPE_CONFIG:
+ case PARSE_EVENTS__TERM_TYPE_CONFIG1:
+ case PARSE_EVENTS__TERM_TYPE_CONFIG2:
+ case PARSE_EVENTS__TERM_TYPE_CONFIG3:
+ case PARSE_EVENTS__TERM_TYPE_NAME:
+ case PARSE_EVENTS__TERM_TYPE_METRIC_ID:
+ case PARSE_EVENTS__TERM_TYPE_RAW:
+ case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE:
+ case PARSE_EVENTS__TERM_TYPE_HARDWARE:
default:
break;
}
@@ -1418,14 +1204,38 @@ static int get_config_chgs(struct perf_pmu *pmu, struct list_head *head_config,
list_for_each_entry(term, head_config, list) {
switch (term->type_term) {
case PARSE_EVENTS__TERM_TYPE_USER:
- type = perf_pmu__format_type(&pmu->format, term->config);
+ type = perf_pmu__format_type(pmu, term->config);
if (type != PERF_PMU_FORMAT_VALUE_CONFIG)
continue;
- bits |= perf_pmu__format_bits(&pmu->format, term->config);
+ bits |= perf_pmu__format_bits(pmu, term->config);
break;
case PARSE_EVENTS__TERM_TYPE_CONFIG:
bits = ~(u64)0;
break;
+ case PARSE_EVENTS__TERM_TYPE_CONFIG1:
+ case PARSE_EVENTS__TERM_TYPE_CONFIG2:
+ case PARSE_EVENTS__TERM_TYPE_CONFIG3:
+ case PARSE_EVENTS__TERM_TYPE_NAME:
+ case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
+ case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ:
+ case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
+ case PARSE_EVENTS__TERM_TYPE_TIME:
+ case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
+ case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
+ case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
+ case PARSE_EVENTS__TERM_TYPE_INHERIT:
+ case PARSE_EVENTS__TERM_TYPE_MAX_STACK:
+ case PARSE_EVENTS__TERM_TYPE_MAX_EVENTS:
+ case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE:
+ case PARSE_EVENTS__TERM_TYPE_OVERWRITE:
+ case PARSE_EVENTS__TERM_TYPE_DRV_CFG:
+ case PARSE_EVENTS__TERM_TYPE_PERCORE:
+ case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
+ case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE:
+ case PARSE_EVENTS__TERM_TYPE_METRIC_ID:
+ case PARSE_EVENTS__TERM_TYPE_RAW:
+ case PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE:
+ case PARSE_EVENTS__TERM_TYPE_HARDWARE:
default:
break;
}
@@ -1441,8 +1251,9 @@ static int get_config_chgs(struct perf_pmu *pmu, struct list_head *head_config,
int parse_events_add_tracepoint(struct list_head *list, int *idx,
const char *sys, const char *event,
struct parse_events_error *err,
- struct list_head *head_config)
+ struct list_head *head_config, void *loc_)
{
+ YYLTYPE *loc = loc_;
#ifdef HAVE_LIBTRACEEVENT
if (head_config) {
struct perf_event_attr attr;
@@ -1454,17 +1265,17 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx,
if (strpbrk(sys, "*?"))
return add_tracepoint_multi_sys(list, idx, sys, event,
- err, head_config);
+ err, head_config, loc);
else
return add_tracepoint_event(list, idx, sys, event,
- err, head_config);
+ err, head_config, loc);
#else
(void)list;
(void)idx;
(void)sys;
(void)event;
(void)head_config;
- parse_events_error__handle(err, 0, strdup("unsupported tracepoint"),
+ parse_events_error__handle(err, loc->first_column, strdup("unsupported tracepoint"),
strdup("libtraceevent is necessary for tracepoint support"));
return -1;
#endif
@@ -1557,41 +1368,44 @@ static bool config_term_percore(struct list_head *config_terms)
}
int parse_events_add_pmu(struct parse_events_state *parse_state,
- struct list_head *list, char *name,
+ struct list_head *list, const char *name,
struct list_head *head_config,
- bool auto_merge_stats)
+ bool auto_merge_stats, void *loc_)
{
struct perf_event_attr attr;
struct perf_pmu_info info;
struct perf_pmu *pmu;
struct evsel *evsel;
struct parse_events_error *err = parse_state->error;
+ YYLTYPE *loc = loc_;
LIST_HEAD(config_terms);
pmu = parse_state->fake_pmu ?: perf_pmus__find(name);
- if (verbose > 1 && !(pmu && pmu->selectable)) {
- fprintf(stderr, "Attempting to add event pmu '%s' with '",
- name);
- if (head_config) {
- struct parse_events_term *term;
-
- list_for_each_entry(term, head_config, list) {
- fprintf(stderr, "%s,", term->config);
- }
- }
- fprintf(stderr, "' that may result in non-fatal errors\n");
- }
-
if (!pmu) {
char *err_str;
if (asprintf(&err_str,
"Cannot find PMU `%s'. Missing kernel support?",
name) >= 0)
- parse_events_error__handle(err, 0, err_str, NULL);
+ parse_events_error__handle(err, loc->first_column, err_str, NULL);
return -EINVAL;
}
+
+ if (verbose > 1) {
+ struct strbuf sb;
+
+ strbuf_init(&sb, /*hint=*/ 0);
+ if (pmu->selectable && !head_config) {
+ strbuf_addf(&sb, "%s//", name);
+ } else {
+ strbuf_addf(&sb, "%s/", name);
+ parse_events_term__to_strbuf(head_config, &sb);
+ strbuf_addch(&sb, '/');
+ }
+ fprintf(stderr, "Attempt to add: %s\n", sb.buf);
+ strbuf_release(&sb);
+ }
if (head_config)
fix_raw(head_config, pmu);
@@ -1612,20 +1426,16 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
return evsel ? 0 : -ENOMEM;
}
- if (!parse_state->fake_pmu && perf_pmu__check_alias(pmu, head_config, &info))
+ if (!parse_state->fake_pmu && perf_pmu__check_alias(pmu, head_config, &info, err))
return -EINVAL;
if (verbose > 1) {
- fprintf(stderr, "After aliases, add event pmu '%s' with '",
- name);
- if (head_config) {
- struct parse_events_term *term;
+ struct strbuf sb;
- list_for_each_entry(term, head_config, list) {
- fprintf(stderr, "%s,", term->config);
- }
- }
- fprintf(stderr, "' that may result in non-fatal errors\n");
+ strbuf_init(&sb, /*hint=*/ 0);
+ parse_events_term__to_strbuf(head_config, &sb);
+ fprintf(stderr, "..after resolving event: %s/%s/\n", name, sb.buf);
+ strbuf_release(&sb);
}
/*
@@ -1675,14 +1485,15 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
char *str, struct list_head *head,
- struct list_head **listp)
+ struct list_head **listp, void *loc_)
{
struct parse_events_term *term;
struct list_head *list = NULL;
struct list_head *orig_head = NULL;
struct perf_pmu *pmu = NULL;
+ YYLTYPE *loc = loc_;
int ok = 0;
- char *config;
+ const char *config;
*listp = NULL;
@@ -1699,9 +1510,9 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
if (parse_events_term__num(&term,
PARSE_EVENTS__TERM_TYPE_USER,
- config, 1, false, NULL,
- NULL) < 0) {
- free(config);
+ config, /*num=*/1, /*novalue=*/true,
+ loc, /*loc_val=*/NULL) < 0) {
+ zfree(&config);
goto out_err;
}
list_add_tail(&term->list, head);
@@ -1714,33 +1525,38 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
INIT_LIST_HEAD(list);
while ((pmu = perf_pmus__scan(pmu)) != NULL) {
- struct perf_pmu_alias *alias;
bool auto_merge_stats;
if (parse_events__filter_pmu(parse_state, pmu))
continue;
- auto_merge_stats = perf_pmu__auto_merge_stats(pmu);
+ if (!perf_pmu__have_event(pmu, str))
+ continue;
- list_for_each_entry(alias, &pmu->aliases, list) {
- if (!strcasecmp(alias->name, str)) {
- parse_events_copy_term_list(head, &orig_head);
- if (!parse_events_add_pmu(parse_state, list,
- pmu->name, orig_head,
- auto_merge_stats)) {
- pr_debug("%s -> %s/%s/\n", str,
- pmu->name, alias->str);
- ok++;
- }
- parse_events_terms__delete(orig_head);
- }
+ auto_merge_stats = perf_pmu__auto_merge_stats(pmu);
+ parse_events_copy_term_list(head, &orig_head);
+ if (!parse_events_add_pmu(parse_state, list, pmu->name,
+ orig_head, auto_merge_stats, loc)) {
+ struct strbuf sb;
+
+ strbuf_init(&sb, /*hint=*/ 0);
+ parse_events_term__to_strbuf(orig_head, &sb);
+ pr_debug("%s -> %s/%s/\n", str, pmu->name, sb.buf);
+ strbuf_release(&sb);
+ ok++;
}
+ parse_events_terms__delete(orig_head);
}
if (parse_state->fake_pmu) {
if (!parse_events_add_pmu(parse_state, list, str, head,
- /*auto_merge_stats=*/true)) {
- pr_debug("%s -> %s/%s/\n", str, "fake_pmu", str);
+ /*auto_merge_stats=*/true, loc)) {
+ struct strbuf sb;
+
+ strbuf_init(&sb, /*hint=*/ 0);
+ parse_events_term__to_strbuf(head, &sb);
+ pr_debug("%s -> %s/%s/\n", str, "fake_pmu", sb.buf);
+ strbuf_release(&sb);
ok++;
}
}
@@ -1972,14 +1788,18 @@ int parse_events_name(struct list_head *list, const char *name)
struct evsel *evsel;
__evlist__for_each_entry(list, evsel) {
- if (!evsel->name)
+ if (!evsel->name) {
evsel->name = strdup(name);
+ if (!evsel->name)
+ return -ENOMEM;
+ }
}
return 0;
}
static int parse_events__scanner(const char *str,
+ FILE *input,
struct parse_events_state *parse_state)
{
YY_BUFFER_STATE buffer;
@@ -1990,7 +1810,10 @@ static int parse_events__scanner(const char *str,
if (ret)
return ret;
- buffer = parse_events__scan_string(str, scanner);
+ if (str)
+ buffer = parse_events__scan_string(str, scanner);
+ else
+ parse_events_set_in(input, scanner);
#ifdef PARSER_DEBUG
parse_events_debug = 1;
@@ -1998,8 +1821,10 @@ static int parse_events__scanner(const char *str,
#endif
ret = parse_events_parse(parse_state, scanner);
- parse_events__flush_buffer(buffer, scanner);
- parse_events__delete_buffer(buffer, scanner);
+ if (str) {
+ parse_events__flush_buffer(buffer, scanner);
+ parse_events__delete_buffer(buffer, scanner);
+ }
parse_events_lex_destroy(scanner);
return ret;
}
@@ -2007,7 +1832,7 @@ static int parse_events__scanner(const char *str,
/*
* parse event config string, return a list of event terms.
*/
-int parse_events_terms(struct list_head *terms, const char *str)
+int parse_events_terms(struct list_head *terms, const char *str, FILE *input)
{
struct parse_events_state parse_state = {
.terms = NULL,
@@ -2015,7 +1840,7 @@ int parse_events_terms(struct list_head *terms, const char *str)
};
int ret;
- ret = parse_events__scanner(str, &parse_state);
+ ret = parse_events__scanner(str, input, &parse_state);
if (!ret) {
list_splice(parse_state.terms, terms);
@@ -2259,7 +2084,6 @@ int __parse_events(struct evlist *evlist, const char *str, const char *pmu_filte
.list = LIST_HEAD_INIT(parse_state.list),
.idx = evlist->core.nr_entries,
.error = err,
- .evlist = evlist,
.stoken = PE_START_EVENTS,
.fake_pmu = fake_pmu,
.pmu_filter = pmu_filter,
@@ -2267,7 +2091,7 @@ int __parse_events(struct evlist *evlist, const char *str, const char *pmu_filte
};
int ret, ret2;
- ret = parse_events__scanner(str, &parse_state);
+ ret = parse_events__scanner(str, /*input=*/ NULL, &parse_state);
if (!ret && list_empty(&parse_state.list)) {
WARN_ONCE(true, "WARNING: event parser found nothing\n");
@@ -2348,7 +2172,7 @@ void parse_events_error__handle(struct parse_events_error *err, int idx,
break;
default:
pr_debug("Multiple errors dropping message: %s (%s)\n",
- err->str, err->help);
+ err->str, err->help ?: "<no help>");
free(err->str);
err->str = str;
free(err->help);
@@ -2641,7 +2465,8 @@ static int new_term(struct parse_events_term **_term,
}
int parse_events_term__num(struct parse_events_term **term,
- int type_term, char *config, u64 num,
+ enum parse_events__term_type type_term,
+ const char *config, u64 num,
bool no_value,
void *loc_term_, void *loc_val_)
{
@@ -2651,17 +2476,18 @@ int parse_events_term__num(struct parse_events_term **term,
struct parse_events_term temp = {
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
.type_term = type_term,
- .config = config ? : strdup(config_term_names[type_term]),
+ .config = config ? : strdup(config_term_name(type_term)),
.no_value = no_value,
.err_term = loc_term ? loc_term->first_column : 0,
.err_val = loc_val ? loc_val->first_column : 0,
};
- return new_term(term, &temp, NULL, num);
+ return new_term(term, &temp, /*str=*/NULL, num);
}
int parse_events_term__str(struct parse_events_term **term,
- int type_term, char *config, char *str,
+ enum parse_events__term_type type_term,
+ char *config, char *str,
void *loc_term_, void *loc_val_)
{
YYLTYPE *loc_term = loc_term_;
@@ -2675,15 +2501,16 @@ int parse_events_term__str(struct parse_events_term **term,
.err_val = loc_val ? loc_val->first_column : 0,
};
- return new_term(term, &temp, str, 0);
+ return new_term(term, &temp, str, /*num=*/0);
}
int parse_events_term__term(struct parse_events_term **term,
- int term_lhs, int term_rhs,
+ enum parse_events__term_type term_lhs,
+ enum parse_events__term_type term_rhs,
void *loc_term, void *loc_val)
{
return parse_events_term__str(term, term_lhs, NULL,
- strdup(config_term_names[term_rhs]),
+ strdup(config_term_name(term_rhs)),
loc_term, loc_val);
}
@@ -2691,33 +2518,25 @@ int parse_events_term__clone(struct parse_events_term **new,
struct parse_events_term *term)
{
char *str;
- struct parse_events_term temp = {
- .type_val = term->type_val,
- .type_term = term->type_term,
- .config = NULL,
- .err_term = term->err_term,
- .err_val = term->err_val,
- };
+ struct parse_events_term temp = *term;
+ temp.used = false;
if (term->config) {
temp.config = strdup(term->config);
if (!temp.config)
return -ENOMEM;
}
if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM)
- return new_term(new, &temp, NULL, term->val.num);
+ return new_term(new, &temp, /*str=*/NULL, term->val.num);
str = strdup(term->val.str);
if (!str)
return -ENOMEM;
- return new_term(new, &temp, str, 0);
+ return new_term(new, &temp, str, /*num=*/0);
}
void parse_events_term__delete(struct parse_events_term *term)
{
- if (term->array.nr_ranges)
- zfree(&term->array.ranges);
-
if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM)
zfree(&term->val.str);
@@ -2768,9 +2587,47 @@ void parse_events_terms__delete(struct list_head *terms)
free(terms);
}
-void parse_events__clear_array(struct parse_events_array *a)
+int parse_events_term__to_strbuf(struct list_head *term_list, struct strbuf *sb)
{
- zfree(&a->ranges);
+ struct parse_events_term *term;
+ bool first = true;
+
+ if (!term_list)
+ return 0;
+
+ list_for_each_entry(term, term_list, list) {
+ int ret;
+
+ if (!first) {
+ ret = strbuf_addch(sb, ',');
+ if (ret < 0)
+ return ret;
+ }
+ first = false;
+
+ if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM)
+ if (term->no_value) {
+ assert(term->val.num == 1);
+ ret = strbuf_addf(sb, "%s", term->config);
+ } else
+ ret = strbuf_addf(sb, "%s=%#"PRIx64, term->config, term->val.num);
+ else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) {
+ if (term->config) {
+ ret = strbuf_addf(sb, "%s=", term->config);
+ if (ret < 0)
+ return ret;
+ } else if ((unsigned int)term->type_term < __PARSE_EVENTS__TERM_TYPE_NR) {
+ ret = strbuf_addf(sb, "%s=", config_term_name(term->type_term));
+ if (ret < 0)
+ return ret;
+ }
+ assert(!term->no_value);
+ ret = strbuf_addf(sb, "%s", term->val.str);
+ }
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
}
void parse_events_evlist_error(struct parse_events_state *parse_state,
@@ -2789,7 +2646,7 @@ static void config_terms_list(char *buf, size_t buf_sz)
buf[0] = '\0';
for (i = 0; i < __PARSE_EVENTS__TERM_TYPE_NR; i++) {
- const char *name = config_term_names[i];
+ const char *name = config_term_name(i);
if (!config_term_avail(i, NULL))
continue;
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index b0eb95f93e9c..594e5d2dc67f 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -9,6 +9,7 @@
#include <stdbool.h>
#include <linux/types.h>
#include <linux/perf_event.h>
+#include <stdio.h>
#include <string.h>
struct evsel;
@@ -17,6 +18,7 @@ struct parse_events_error;
struct option;
struct perf_pmu;
+struct strbuf;
const char *event_type(int type);
@@ -42,16 +44,16 @@ static inline int parse_events(struct evlist *evlist, const char *str,
int parse_event(struct evlist *evlist, const char *str);
-int parse_events_terms(struct list_head *terms, const char *str);
+int parse_events_terms(struct list_head *terms, const char *str, FILE *input);
int parse_filter(const struct option *opt, const char *str, int unset);
int exclude_perf(const struct option *opt, const char *arg, int unset);
-enum {
+enum parse_events__term_val_type {
PARSE_EVENTS__TERM_TYPE_NUM,
PARSE_EVENTS__TERM_TYPE_STR,
};
-enum {
+enum parse_events__term_type {
PARSE_EVENTS__TERM_TYPE_USER,
PARSE_EVENTS__TERM_TYPE_CONFIG,
PARSE_EVENTS__TERM_TYPE_CONFIG1,
@@ -78,36 +80,54 @@ enum {
PARSE_EVENTS__TERM_TYPE_RAW,
PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE,
PARSE_EVENTS__TERM_TYPE_HARDWARE,
- __PARSE_EVENTS__TERM_TYPE_NR,
-};
-
-struct parse_events_array {
- size_t nr_ranges;
- struct {
- unsigned int start;
- size_t length;
- } *ranges;
+#define __PARSE_EVENTS__TERM_TYPE_NR (PARSE_EVENTS__TERM_TYPE_HARDWARE + 1)
};
struct parse_events_term {
- char *config;
- struct parse_events_array array;
+ /** @list: The term list the term is a part of. */
+ struct list_head list;
+ /**
+ * @config: The left-hand side of a term assignment, so the term
+ * "event=8" would have the config be "event"
+ */
+ const char *config;
+ /**
+ * @val: The right-hand side of a term assignment that can either be a
+ * string or a number depending on type_val.
+ */
union {
char *str;
u64 num;
} val;
- int type_val;
- int type_term;
- struct list_head list;
- bool used;
- bool no_value;
-
- /* error string indexes for within parsed string */
+ /** @type_val: The union variable in val to be used for the term. */
+ enum parse_events__term_val_type type_val;
+ /**
+ * @type_term: A predefined term type or PARSE_EVENTS__TERM_TYPE_USER
+ * when not inbuilt.
+ */
+ enum parse_events__term_type type_term;
+ /**
+ * @err_term: The column index of the term from parsing, used during
+ * error output.
+ */
int err_term;
+ /**
+ * @err_val: The column index of the val from parsing, used during error
+ * output.
+ */
int err_val;
-
- /* Coming from implicit alias */
+ /** @used: Was the term used during parameterized-eval. */
+ bool used;
+ /**
+ * @weak: A term from the sysfs or json encoding of an event that
+ * shouldn't override terms coming from the command line.
+ */
bool weak;
+ /**
+ * @no_value: Is there no value. If a numeric term has no value then the
+ * value is assumed to be 1. An event name also has no value.
+ */
+ bool no_value;
};
struct parse_events_error {
@@ -121,17 +141,23 @@ struct parse_events_error {
};
struct parse_events_state {
+ /* The list parsed events are placed on. */
struct list_head list;
+ /* The updated index used by entries as they are added. */
int idx;
+ /* Error information. */
struct parse_events_error *error;
- struct evlist *evlist;
+ /* Holds returned terms for term parsing. */
struct list_head *terms;
+ /* Start token. */
int stoken;
+ /* Special fake PMU marker for testing. */
struct perf_pmu *fake_pmu;
/* If non-null, when wildcard matching only match the given PMU. */
const char *pmu_filter;
/* Should PE_LEGACY_NAME tokens be generated for config terms? */
bool match_legacy_cache_terms;
+ /* Were multiple PMUs scanned to find events? */
bool wild_card_pmus;
};
@@ -140,39 +166,31 @@ bool parse_events__filter_pmu(const struct parse_events_state *parse_state,
void parse_events__shrink_config_terms(void);
int parse_events__is_hardcoded_term(struct parse_events_term *term);
int parse_events_term__num(struct parse_events_term **term,
- int type_term, char *config, u64 num,
+ enum parse_events__term_type type_term,
+ const char *config, u64 num,
bool novalue,
void *loc_term, void *loc_val);
int parse_events_term__str(struct parse_events_term **term,
- int type_term, char *config, char *str,
+ enum parse_events__term_type type_term,
+ char *config, char *str,
void *loc_term, void *loc_val);
int parse_events_term__term(struct parse_events_term **term,
- int term_lhs, int term_rhs,
+ enum parse_events__term_type term_lhs,
+ enum parse_events__term_type term_rhs,
void *loc_term, void *loc_val);
int parse_events_term__clone(struct parse_events_term **new,
struct parse_events_term *term);
void parse_events_term__delete(struct parse_events_term *term);
void parse_events_terms__delete(struct list_head *terms);
void parse_events_terms__purge(struct list_head *terms);
-void parse_events__clear_array(struct parse_events_array *a);
+int parse_events_term__to_strbuf(struct list_head *term_list, struct strbuf *sb);
int parse_events__modifier_event(struct list_head *list, char *str, bool add);
int parse_events__modifier_group(struct list_head *list, char *event_mod);
int parse_events_name(struct list_head *list, const char *name);
int parse_events_add_tracepoint(struct list_head *list, int *idx,
const char *sys, const char *event,
struct parse_events_error *error,
- struct list_head *head_config);
-int parse_events_load_bpf(struct parse_events_state *parse_state,
- struct list_head *list,
- char *bpf_file_name,
- bool source,
- struct list_head *head_config);
-/* Provide this function for perf test */
-struct bpf_object;
-int parse_events_load_bpf_obj(struct parse_events_state *parse_state,
- struct list_head *list,
- struct bpf_object *obj,
- struct list_head *head_config);
+ struct list_head *head_config, void *loc);
int parse_events_add_numeric(struct parse_events_state *parse_state,
struct list_head *list,
u32 type, u64 config,
@@ -190,9 +208,9 @@ int parse_events_add_breakpoint(struct parse_events_state *parse_state,
u64 addr, char *type, u64 len,
struct list_head *head_config);
int parse_events_add_pmu(struct parse_events_state *parse_state,
- struct list_head *list, char *name,
+ struct list_head *list, const char *name,
struct list_head *head_config,
- bool auto_merge_stats);
+ bool auto_merge_stats, void *loc);
struct evsel *parse_events__add_event(int idx, struct perf_event_attr *attr,
const char *name, const char *metric_id,
@@ -201,7 +219,7 @@ struct evsel *parse_events__add_event(int idx, struct perf_event_attr *attr,
int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
char *str,
struct list_head *head_config,
- struct list_head **listp);
+ struct list_head **listp, void *loc);
int parse_events_copy_term_list(struct list_head *old,
struct list_head **new);
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 99335ec586ae..4ef4b6f171a0 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -68,31 +68,6 @@ static int lc_str(yyscan_t scanner, const struct parse_events_state *state)
return str(scanner, state->match_legacy_cache_terms ? PE_LEGACY_CACHE : PE_NAME);
}
-static bool isbpf_suffix(char *text)
-{
- int len = strlen(text);
-
- if (len < 2)
- return false;
- if ((text[len - 1] == 'c' || text[len - 1] == 'o') &&
- text[len - 2] == '.')
- return true;
- if (len > 4 && !strcmp(text + len - 4, ".obj"))
- return true;
- return false;
-}
-
-static bool isbpf(yyscan_t scanner)
-{
- char *text = parse_events_get_text(scanner);
- struct stat st;
-
- if (!isbpf_suffix(text))
- return false;
-
- return stat(text, &st) == 0;
-}
-
/*
* This function is called when the parser gets two kind of input:
*
@@ -141,7 +116,7 @@ static int tool(yyscan_t scanner, enum perf_tool_event event)
return PE_VALUE_SYM_TOOL;
}
-static int term(yyscan_t scanner, int type)
+static int term(yyscan_t scanner, enum parse_events__term_type type)
{
YYSTYPE *yylval = parse_events_get_lval(scanner);
@@ -175,13 +150,10 @@ do { \
%x mem
%s config
%x event
-%x array
group [^,{}/]*[{][^}]*[}][^,{}/]*
event_pmu [^,{}/]+[/][^/]*[/][^,{}/]*
event [^,{}/]+
-bpf_object [^,{}]+\.(o|bpf)[a-zA-Z0-9._]*
-bpf_source [^,{}]+\.c[a-zA-Z0-9._]*
num_dec [0-9]+
num_hex 0x[a-fA-F0-9]+
@@ -234,8 +206,6 @@ non_digit [^0-9]
}
{event_pmu} |
-{bpf_object} |
-{bpf_source} |
{event} {
BEGIN(INITIAL);
REWIND(1);
@@ -251,14 +221,6 @@ non_digit [^0-9]
}
}
-<array>{
-"]" { BEGIN(config); return ']'; }
-{num_dec} { return value(yyscanner, 10); }
-{num_hex} { return value(yyscanner, 16); }
-, { return ','; }
-"\.\.\." { return PE_ARRAY_RANGE; }
-}
-
<config>{
/*
* Please update config_term_names when new static term is added.
@@ -302,8 +264,6 @@ r0x{num_raw_hex} { return str(yyscanner, PE_RAW); }
{lc_type}-{lc_op_result} { return lc_str(yyscanner, _parse_state); }
{lc_type}-{lc_op_result}-{lc_op_result} { return lc_str(yyscanner, _parse_state); }
{name_minus} { return str(yyscanner, PE_NAME); }
-\[all\] { return PE_ARRAY_ALL; }
-"[" { BEGIN(array); return '['; }
@{drv_cfg_term} { return drv_str(yyscanner, PE_DRV_CFG_TERM); }
}
@@ -374,8 +334,6 @@ r{num_raw_hex} { return str(yyscanner, PE_RAW); }
{num_hex} { return value(yyscanner, 16); }
{modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); }
-{bpf_object} { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_OBJECT); }
-{bpf_source} { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_SOURCE); }
{name} { return str(yyscanner, PE_NAME); }
{name_tag} { return str(yyscanner, PE_NAME); }
"/" { BEGIN(config); return '/'; }
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 9f28d4b5502f..21bfe7e0d944 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -20,12 +20,14 @@
#include "parse-events.h"
#include "parse-events-bison.h"
+int parse_events_lex(YYSTYPE * yylval_param, YYLTYPE * yylloc_param , void *yyscanner);
void parse_events_error(YYLTYPE *loc, void *parse_state, void *scanner, char const *msg);
-#define ABORT_ON(val) \
+#define PE_ABORT(val) \
do { \
- if (val) \
- YYABORT; \
+ if (val == -ENOMEM) \
+ YYNOMEM; \
+ YYABORT; \
} while (0)
static struct list_head* alloc_list(void)
@@ -58,13 +60,10 @@ static void free_list_evsel(struct list_head* list_evsel)
%token PE_VALUE_SYM_TOOL
%token PE_EVENT_NAME
%token PE_RAW PE_NAME
-%token PE_BPF_OBJECT PE_BPF_SOURCE
%token PE_MODIFIER_EVENT PE_MODIFIER_BP PE_BP_COLON PE_BP_SLASH
%token PE_LEGACY_CACHE
-%token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
+%token PE_PREFIX_MEM
%token PE_ERROR
-%token PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE
-%token PE_ARRAY_ALL PE_ARRAY_RANGE
%token PE_DRV_CFG_TERM
%token PE_TERM_HW
%type <num> PE_VALUE
@@ -75,13 +74,10 @@ static void free_list_evsel(struct list_head* list_evsel)
%type <num> value_sym
%type <str> PE_RAW
%type <str> PE_NAME
-%type <str> PE_BPF_OBJECT
-%type <str> PE_BPF_SOURCE
%type <str> PE_LEGACY_CACHE
%type <str> PE_MODIFIER_EVENT
%type <str> PE_MODIFIER_BP
%type <str> PE_EVENT_NAME
-%type <str> PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE
%type <str> PE_DRV_CFG_TERM
%type <str> name_or_raw name_or_legacy
%destructor { free ($$); } <str>
@@ -98,7 +94,6 @@ static void free_list_evsel(struct list_head* list_evsel)
%type <list_evsel> event_legacy_tracepoint
%type <list_evsel> event_legacy_numeric
%type <list_evsel> event_legacy_raw
-%type <list_evsel> event_bpf_file
%type <list_evsel> event_def
%type <list_evsel> event_mod
%type <list_evsel> event_name
@@ -109,11 +104,6 @@ static void free_list_evsel(struct list_head* list_evsel)
%type <list_evsel> groups
%destructor { free_list_evsel ($$); } <list_evsel>
%type <tracepoint_name> tracepoint_name
-%destructor { free ($$.sys); free ($$.event); } <tracepoint_name>
-%type <array> array
-%type <array> array_term
-%type <array> array_terms
-%destructor { free ($$.ranges); } <array>
%type <hardware_term> PE_TERM_HW
%destructor { free ($$.str); } <hardware_term>
@@ -128,7 +118,6 @@ static void free_list_evsel(struct list_head* list_evsel)
char *sys;
char *event;
} tracepoint_name;
- struct parse_events_array array;
struct hardware_term {
char *str;
u64 num;
@@ -265,7 +254,7 @@ PE_EVENT_NAME event_def
free($1);
if (err) {
free_list_evsel($2);
- YYABORT;
+ YYNOMEM;
}
$$ = $2;
}
@@ -278,47 +267,47 @@ event_def: event_pmu |
event_legacy_mem sep_dc |
event_legacy_tracepoint sep_dc |
event_legacy_numeric sep_dc |
- event_legacy_raw sep_dc |
- event_bpf_file
+ event_legacy_raw sep_dc
event_pmu:
PE_NAME opt_pmu_config
{
struct parse_events_state *parse_state = _parse_state;
- struct parse_events_error *error = parse_state->error;
struct list_head *list = NULL, *orig_terms = NULL, *terms= NULL;
char *pattern = NULL;
-#define CLEANUP_YYABORT \
+#define CLEANUP \
do { \
parse_events_terms__delete($2); \
parse_events_terms__delete(orig_terms); \
free(list); \
free($1); \
free(pattern); \
- YYABORT; \
} while(0)
- if (parse_events_copy_term_list($2, &orig_terms))
- CLEANUP_YYABORT;
-
- if (error)
- error->idx = @1.first_column;
+ if (parse_events_copy_term_list($2, &orig_terms)) {
+ CLEANUP;
+ YYNOMEM;
+ }
list = alloc_list();
- if (!list)
- CLEANUP_YYABORT;
+ if (!list) {
+ CLEANUP;
+ YYNOMEM;
+ }
/* Attempt to add to list assuming $1 is a PMU name. */
- if (parse_events_add_pmu(parse_state, list, $1, $2, /*auto_merge_stats=*/false)) {
+ if (parse_events_add_pmu(parse_state, list, $1, $2, /*auto_merge_stats=*/false, &@1)) {
struct perf_pmu *pmu = NULL;
int ok = 0;
/* Failure to add, try wildcard expansion of $1 as a PMU name. */
- if (asprintf(&pattern, "%s*", $1) < 0)
- CLEANUP_YYABORT;
+ if (asprintf(&pattern, "%s*", $1) < 0) {
+ CLEANUP;
+ YYNOMEM;
+ }
while ((pmu = perf_pmus__scan(pmu)) != NULL) {
- char *name = pmu->name;
+ const char *name = pmu->name;
if (parse_events__filter_pmu(parse_state, pmu))
continue;
@@ -330,10 +319,12 @@ PE_NAME opt_pmu_config
!perf_pmu__match(pattern, pmu->alias_name, $1)) {
bool auto_merge_stats = perf_pmu__auto_merge_stats(pmu);
- if (parse_events_copy_term_list(orig_terms, &terms))
- CLEANUP_YYABORT;
+ if (parse_events_copy_term_list(orig_terms, &terms)) {
+ CLEANUP;
+ YYNOMEM;
+ }
if (!parse_events_add_pmu(parse_state, list, pmu->name, terms,
- auto_merge_stats)) {
+ auto_merge_stats, &@1)) {
ok++;
parse_state->wild_card_pmus = true;
}
@@ -344,30 +335,26 @@ PE_NAME opt_pmu_config
if (!ok) {
/* Failure to add, assume $1 is an event name. */
zfree(&list);
- ok = !parse_events_multi_pmu_add(parse_state, $1, $2, &list);
+ ok = !parse_events_multi_pmu_add(parse_state, $1, $2, &list, &@1);
$2 = NULL;
}
- if (!ok)
- CLEANUP_YYABORT;
+ if (!ok) {
+ struct parse_events_error *error = parse_state->error;
+ char *help;
+
+ if (asprintf(&help, "Unable to find PMU or event on a PMU of '%s'", $1) < 0)
+ help = NULL;
+ parse_events_error__handle(error, @1.first_column,
+ strdup("Bad event or PMU"),
+ help);
+ CLEANUP;
+ YYABORT;
+ }
}
- parse_events_terms__delete($2);
- parse_events_terms__delete(orig_terms);
- free(pattern);
- free($1);
- $$ = list;
-#undef CLEANUP_YYABORT
-}
-|
-PE_KERNEL_PMU_EVENT sep_dc
-{
- struct list_head *list;
- int err;
-
- err = parse_events_multi_pmu_add(_parse_state, $1, NULL, &list);
- free($1);
- if (err < 0)
- YYABORT;
$$ = list;
+ list = NULL;
+ CLEANUP;
+#undef CLEANUP
}
|
PE_NAME sep_dc
@@ -375,61 +362,19 @@ PE_NAME sep_dc
struct list_head *list;
int err;
- err = parse_events_multi_pmu_add(_parse_state, $1, NULL, &list);
- free($1);
- if (err < 0)
- YYABORT;
- $$ = list;
-}
-|
-PE_KERNEL_PMU_EVENT opt_pmu_config
-{
- struct list_head *list;
- int err;
-
- /* frees $2 */
- err = parse_events_multi_pmu_add(_parse_state, $1, $2, &list);
- free($1);
- if (err < 0)
- YYABORT;
- $$ = list;
-}
-|
-PE_PMU_EVENT_FAKE sep_dc
-{
- struct list_head *list;
- int err;
-
- list = alloc_list();
- if (!list)
- YYABORT;
-
- err = parse_events_add_pmu(_parse_state, list, $1, /*head_config=*/NULL,
- /*auto_merge_stats=*/false);
- free($1);
+ err = parse_events_multi_pmu_add(_parse_state, $1, NULL, &list, &@1);
if (err < 0) {
- free(list);
- YYABORT;
- }
- $$ = list;
-}
-|
-PE_PMU_EVENT_FAKE opt_pmu_config
-{
- struct list_head *list;
- int err;
-
- list = alloc_list();
- if (!list)
- YYABORT;
+ struct parse_events_state *parse_state = _parse_state;
+ struct parse_events_error *error = parse_state->error;
+ char *help;
- err = parse_events_add_pmu(_parse_state, list, $1, $2, /*auto_merge_stats=*/false);
- free($1);
- parse_events_terms__delete($2);
- if (err < 0) {
- free(list);
- YYABORT;
+ if (asprintf(&help, "Unable to find event on a PMU of '%s'", $1) < 0)
+ help = NULL;
+ parse_events_error__handle(error, @1.first_column, strdup("Bad event name"), help);
+ free($1);
+ PE_ABORT(err);
}
+ free($1);
$$ = list;
}
@@ -448,12 +393,13 @@ value_sym '/' event_config '/'
bool wildcard = (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE);
list = alloc_list();
- ABORT_ON(!list);
+ if (!list)
+ YYNOMEM;
err = parse_events_add_numeric(_parse_state, list, type, config, $3, wildcard);
parse_events_terms__delete($3);
if (err) {
free_list_evsel(list);
- YYABORT;
+ PE_ABORT(err);
}
$$ = list;
}
@@ -464,21 +410,28 @@ value_sym sep_slash_slash_dc
int type = $1 >> 16;
int config = $1 & 255;
bool wildcard = (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE);
+ int err;
list = alloc_list();
- ABORT_ON(!list);
- ABORT_ON(parse_events_add_numeric(_parse_state, list, type, config,
- /*head_config=*/NULL, wildcard));
+ if (!list)
+ YYNOMEM;
+ err = parse_events_add_numeric(_parse_state, list, type, config, /*head_config=*/NULL, wildcard);
+ if (err)
+ PE_ABORT(err);
$$ = list;
}
|
PE_VALUE_SYM_TOOL sep_slash_slash_dc
{
struct list_head *list;
+ int err;
list = alloc_list();
- ABORT_ON(!list);
- ABORT_ON(parse_events_add_tool(_parse_state, list, $1));
+ if (!list)
+ YYNOMEM;
+ err = parse_events_add_tool(_parse_state, list, $1);
+ if (err)
+ YYNOMEM;
$$ = list;
}
@@ -490,14 +443,16 @@ PE_LEGACY_CACHE opt_event_config
int err;
list = alloc_list();
- ABORT_ON(!list);
+ if (!list)
+ YYNOMEM;
+
err = parse_events_add_cache(list, &parse_state->idx, $1, parse_state, $2);
parse_events_terms__delete($2);
free($1);
if (err) {
free_list_evsel(list);
- YYABORT;
+ PE_ABORT(err);
}
$$ = list;
}
@@ -509,14 +464,16 @@ PE_PREFIX_MEM PE_VALUE PE_BP_SLASH PE_VALUE PE_BP_COLON PE_MODIFIER_BP opt_event
int err;
list = alloc_list();
- ABORT_ON(!list);
+ if (!list)
+ YYNOMEM;
+
err = parse_events_add_breakpoint(_parse_state, list,
$2, $6, $4, $7);
parse_events_terms__delete($7);
free($6);
if (err) {
free(list);
- YYABORT;
+ PE_ABORT(err);
}
$$ = list;
}
@@ -527,13 +484,15 @@ PE_PREFIX_MEM PE_VALUE PE_BP_SLASH PE_VALUE opt_event_config
int err;
list = alloc_list();
- ABORT_ON(!list);
+ if (!list)
+ YYNOMEM;
+
err = parse_events_add_breakpoint(_parse_state, list,
$2, NULL, $4, $5);
parse_events_terms__delete($5);
if (err) {
free(list);
- YYABORT;
+ PE_ABORT(err);
}
$$ = list;
}
@@ -544,14 +503,16 @@ PE_PREFIX_MEM PE_VALUE PE_BP_COLON PE_MODIFIER_BP opt_event_config
int err;
list = alloc_list();
- ABORT_ON(!list);
+ if (!list)
+ YYNOMEM;
+
err = parse_events_add_breakpoint(_parse_state, list,
$2, $4, 0, $5);
parse_events_terms__delete($5);
free($4);
if (err) {
free(list);
- YYABORT;
+ PE_ABORT(err);
}
$$ = list;
}
@@ -562,13 +523,14 @@ PE_PREFIX_MEM PE_VALUE opt_event_config
int err;
list = alloc_list();
- ABORT_ON(!list);
+ if (!list)
+ YYNOMEM;
err = parse_events_add_breakpoint(_parse_state, list,
$2, NULL, 0, $3);
parse_events_terms__delete($3);
if (err) {
free(list);
- YYABORT;
+ PE_ABORT(err);
}
$$ = list;
}
@@ -582,19 +544,20 @@ tracepoint_name opt_event_config
int err;
list = alloc_list();
- ABORT_ON(!list);
+ if (!list)
+ YYNOMEM;
if (error)
error->idx = @1.first_column;
err = parse_events_add_tracepoint(list, &parse_state->idx, $1.sys, $1.event,
- error, $2);
+ error, $2, &@1);
parse_events_terms__delete($2);
free($1.sys);
free($1.event);
if (err) {
free(list);
- YYABORT;
+ PE_ABORT(err);
}
$$ = list;
}
@@ -614,13 +577,14 @@ PE_VALUE ':' PE_VALUE opt_event_config
int err;
list = alloc_list();
- ABORT_ON(!list);
+ if (!list)
+ YYNOMEM;
err = parse_events_add_numeric(_parse_state, list, (u32)$1, $3, $4,
/*wildcard=*/false);
parse_events_terms__delete($4);
if (err) {
free(list);
- YYABORT;
+ PE_ABORT(err);
}
$$ = list;
}
@@ -633,52 +597,20 @@ PE_RAW opt_event_config
u64 num;
list = alloc_list();
- ABORT_ON(!list);
+ if (!list)
+ YYNOMEM;
errno = 0;
num = strtoull($1 + 1, NULL, 16);
- ABORT_ON(errno);
+ /* Given the lexer will only give [a-fA-F0-9]+ a failure here should be impossible. */
+ if (errno)
+ YYABORT;
free($1);
err = parse_events_add_numeric(_parse_state, list, PERF_TYPE_RAW, num, $2,
/*wildcard=*/false);
parse_events_terms__delete($2);
if (err) {
free(list);
- YYABORT;
- }
- $$ = list;
-}
-
-event_bpf_file:
-PE_BPF_OBJECT opt_event_config
-{
- struct parse_events_state *parse_state = _parse_state;
- struct list_head *list;
- int err;
-
- list = alloc_list();
- ABORT_ON(!list);
- err = parse_events_load_bpf(parse_state, list, $1, false, $2);
- parse_events_terms__delete($2);
- free($1);
- if (err) {
- free(list);
- YYABORT;
- }
- $$ = list;
-}
-|
-PE_BPF_SOURCE opt_event_config
-{
- struct list_head *list;
- int err;
-
- list = alloc_list();
- ABORT_ON(!list);
- err = parse_events_load_bpf(_parse_state, list, $1, true, $2);
- parse_events_terms__delete($2);
- if (err) {
- free(list);
- YYABORT;
+ PE_ABORT(err);
}
$$ = list;
}
@@ -738,7 +670,8 @@ event_term
struct list_head *head = malloc(sizeof(*head));
struct parse_events_term *term = $1;
- ABORT_ON(!head);
+ if (!head)
+ YYNOMEM;
INIT_LIST_HEAD(head);
list_add_tail(&term->list, head);
$$ = head;
@@ -752,11 +685,12 @@ event_term:
PE_RAW
{
struct parse_events_term *term;
+ int err = parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_RAW,
+ strdup("raw"), $1, &@1, &@1);
- if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_RAW,
- strdup("raw"), $1, &@1, &@1)) {
+ if (err) {
free($1);
- YYABORT;
+ PE_ABORT(err);
}
$$ = term;
}
@@ -764,12 +698,12 @@ PE_RAW
name_or_raw '=' name_or_legacy
{
struct parse_events_term *term;
+ int err = parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER, $1, $3, &@1, &@3);
- if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
- $1, $3, &@1, &@3)) {
+ if (err) {
free($1);
free($3);
- YYABORT;
+ PE_ABORT(err);
}
$$ = term;
}
@@ -777,11 +711,12 @@ name_or_raw '=' name_or_legacy
name_or_raw '=' PE_VALUE
{
struct parse_events_term *term;
+ int err = parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
+ $1, $3, /*novalue=*/false, &@1, &@3);
- if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
- $1, $3, false, &@1, &@3)) {
+ if (err) {
free($1);
- YYABORT;
+ PE_ABORT(err);
}
$$ = term;
}
@@ -789,12 +724,13 @@ name_or_raw '=' PE_VALUE
name_or_raw '=' PE_TERM_HW
{
struct parse_events_term *term;
+ int err = parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
+ $1, $3.str, &@1, &@3);
- if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
- $1, $3.str, &@1, &@3)) {
+ if (err) {
free($1);
free($3.str);
- YYABORT;
+ PE_ABORT(err);
}
$$ = term;
}
@@ -802,11 +738,12 @@ name_or_raw '=' PE_TERM_HW
PE_LEGACY_CACHE
{
struct parse_events_term *term;
+ int err = parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE,
+ $1, /*num=*/1, /*novalue=*/true, &@1, /*loc_val=*/NULL);
- if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_LEGACY_CACHE,
- $1, 1, true, &@1, NULL)) {
+ if (err) {
free($1);
- YYABORT;
+ PE_ABORT(err);
}
$$ = term;
}
@@ -814,11 +751,12 @@ PE_LEGACY_CACHE
PE_NAME
{
struct parse_events_term *term;
+ int err = parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
+ $1, /*num=*/1, /*novalue=*/true, &@1, /*loc_val=*/NULL);
- if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
- $1, 1, true, &@1, NULL)) {
+ if (err) {
free($1);
- YYABORT;
+ PE_ABORT(err);
}
$$ = term;
}
@@ -826,11 +764,13 @@ PE_NAME
PE_TERM_HW
{
struct parse_events_term *term;
+ int err = parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_HARDWARE,
+ $1.str, $1.num & 255, /*novalue=*/false,
+ &@1, /*loc_val=*/NULL);
- if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_HARDWARE,
- $1.str, $1.num & 255, false, &@1, NULL)) {
+ if (err) {
free($1.str);
- YYABORT;
+ PE_ABORT(err);
}
$$ = term;
}
@@ -838,10 +778,12 @@ PE_TERM_HW
PE_TERM '=' name_or_legacy
{
struct parse_events_term *term;
+ int err = parse_events_term__str(&term, (enum parse_events__term_type)$1,
+ /*config=*/NULL, $3, &@1, &@3);
- if (parse_events_term__str(&term, (int)$1, NULL, $3, &@1, &@3)) {
+ if (err) {
free($3);
- YYABORT;
+ PE_ABORT(err);
}
$$ = term;
}
@@ -849,10 +791,12 @@ PE_TERM '=' name_or_legacy
PE_TERM '=' PE_TERM_HW
{
struct parse_events_term *term;
+ int err = parse_events_term__str(&term, (enum parse_events__term_type)$1,
+ /*config=*/NULL, $3.str, &@1, &@3);
- if (parse_events_term__str(&term, (int)$1, NULL, $3.str, &@1, &@3)) {
+ if (err) {
free($3.str);
- YYABORT;
+ PE_ABORT(err);
}
$$ = term;
}
@@ -860,53 +804,39 @@ PE_TERM '=' PE_TERM_HW
PE_TERM '=' PE_TERM
{
struct parse_events_term *term;
+ int err = parse_events_term__term(&term,
+ (enum parse_events__term_type)$1,
+ (enum parse_events__term_type)$3,
+ &@1, &@3);
+
+ if (err)
+ PE_ABORT(err);
- ABORT_ON(parse_events_term__term(&term, (int)$1, (int)$3, &@1, &@3));
$$ = term;
}
|
PE_TERM '=' PE_VALUE
{
struct parse_events_term *term;
+ int err = parse_events_term__num(&term, (enum parse_events__term_type)$1,
+ /*config=*/NULL, $3, /*novalue=*/false, &@1, &@3);
+
+ if (err)
+ PE_ABORT(err);
- ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, $3, false, &@1, &@3));
$$ = term;
}
|
PE_TERM
{
struct parse_events_term *term;
+ int err = parse_events_term__num(&term, (enum parse_events__term_type)$1,
+ /*config=*/NULL, /*num=*/1, /*novalue=*/true,
+ &@1, /*loc_val=*/NULL);
- ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1, true, &@1, NULL));
- $$ = term;
-}
-|
-name_or_raw array '=' name_or_legacy
-{
- struct parse_events_term *term;
+ if (err)
+ PE_ABORT(err);
- if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
- $1, $4, &@1, &@4)) {
- free($1);
- free($4);
- free($2.ranges);
- YYABORT;
- }
- term->array = $2;
- $$ = term;
-}
-|
-name_or_raw array '=' PE_VALUE
-{
- struct parse_events_term *term;
-
- if (parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
- $1, $4, false, &@1, &@4)) {
- free($1);
- free($2.ranges);
- YYABORT;
- }
- term->array = $2;
$$ = term;
}
|
@@ -914,73 +844,19 @@ PE_DRV_CFG_TERM
{
struct parse_events_term *term;
char *config = strdup($1);
+ int err;
- ABORT_ON(!config);
- if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_DRV_CFG,
- config, $1, &@1, NULL)) {
+ if (!config)
+ YYNOMEM;
+ err = parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_DRV_CFG, config, $1, &@1, NULL);
+ if (err) {
free($1);
free(config);
- YYABORT;
+ PE_ABORT(err);
}
$$ = term;
}
-array:
-'[' array_terms ']'
-{
- $$ = $2;
-}
-|
-PE_ARRAY_ALL
-{
- $$.nr_ranges = 0;
- $$.ranges = NULL;
-}
-
-array_terms:
-array_terms ',' array_term
-{
- struct parse_events_array new_array;
-
- new_array.nr_ranges = $1.nr_ranges + $3.nr_ranges;
- new_array.ranges = realloc($1.ranges,
- sizeof(new_array.ranges[0]) *
- new_array.nr_ranges);
- ABORT_ON(!new_array.ranges);
- memcpy(&new_array.ranges[$1.nr_ranges], $3.ranges,
- $3.nr_ranges * sizeof(new_array.ranges[0]));
- free($3.ranges);
- $$ = new_array;
-}
-|
-array_term
-
-array_term:
-PE_VALUE
-{
- struct parse_events_array array;
-
- array.nr_ranges = 1;
- array.ranges = malloc(sizeof(array.ranges[0]));
- ABORT_ON(!array.ranges);
- array.ranges[0].start = $1;
- array.ranges[0].length = 1;
- $$ = array;
-}
-|
-PE_VALUE PE_ARRAY_RANGE PE_VALUE
-{
- struct parse_events_array array;
-
- ABORT_ON($3 < $1);
- array.nr_ranges = 1;
- array.ranges = malloc(sizeof(array.ranges[0]));
- ABORT_ON(!array.ranges);
- array.ranges[0].start = $1;
- array.ranges[0].length = $3 - $1 + 1;
- $$ = array;
-}
-
sep_dc: ':' |
sep_slash_slash_dc: '/' '/' | ':' |
diff --git a/tools/perf/util/perf-regs-arch/Build b/tools/perf/util/perf-regs-arch/Build
new file mode 100644
index 000000000000..d9d596d330a7
--- /dev/null
+++ b/tools/perf/util/perf-regs-arch/Build
@@ -0,0 +1,9 @@
+perf-y += perf_regs_aarch64.o
+perf-y += perf_regs_arm.o
+perf-y += perf_regs_csky.o
+perf-y += perf_regs_loongarch.o
+perf-y += perf_regs_mips.o
+perf-y += perf_regs_powerpc.o
+perf-y += perf_regs_riscv.o
+perf-y += perf_regs_s390.o
+perf-y += perf_regs_x86.o
diff --git a/tools/perf/util/perf-regs-arch/perf_regs_aarch64.c b/tools/perf/util/perf-regs-arch/perf_regs_aarch64.c
new file mode 100644
index 000000000000..696566c54768
--- /dev/null
+++ b/tools/perf/util/perf-regs-arch/perf_regs_aarch64.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifdef HAVE_PERF_REGS_SUPPORT
+
+#include "../perf_regs.h"
+#include "../../../arch/arm64/include/uapi/asm/perf_regs.h"
+
+const char *__perf_reg_name_arm64(int id)
+{
+ switch (id) {
+ case PERF_REG_ARM64_X0:
+ return "x0";
+ case PERF_REG_ARM64_X1:
+ return "x1";
+ case PERF_REG_ARM64_X2:
+ return "x2";
+ case PERF_REG_ARM64_X3:
+ return "x3";
+ case PERF_REG_ARM64_X4:
+ return "x4";
+ case PERF_REG_ARM64_X5:
+ return "x5";
+ case PERF_REG_ARM64_X6:
+ return "x6";
+ case PERF_REG_ARM64_X7:
+ return "x7";
+ case PERF_REG_ARM64_X8:
+ return "x8";
+ case PERF_REG_ARM64_X9:
+ return "x9";
+ case PERF_REG_ARM64_X10:
+ return "x10";
+ case PERF_REG_ARM64_X11:
+ return "x11";
+ case PERF_REG_ARM64_X12:
+ return "x12";
+ case PERF_REG_ARM64_X13:
+ return "x13";
+ case PERF_REG_ARM64_X14:
+ return "x14";
+ case PERF_REG_ARM64_X15:
+ return "x15";
+ case PERF_REG_ARM64_X16:
+ return "x16";
+ case PERF_REG_ARM64_X17:
+ return "x17";
+ case PERF_REG_ARM64_X18:
+ return "x18";
+ case PERF_REG_ARM64_X19:
+ return "x19";
+ case PERF_REG_ARM64_X20:
+ return "x20";
+ case PERF_REG_ARM64_X21:
+ return "x21";
+ case PERF_REG_ARM64_X22:
+ return "x22";
+ case PERF_REG_ARM64_X23:
+ return "x23";
+ case PERF_REG_ARM64_X24:
+ return "x24";
+ case PERF_REG_ARM64_X25:
+ return "x25";
+ case PERF_REG_ARM64_X26:
+ return "x26";
+ case PERF_REG_ARM64_X27:
+ return "x27";
+ case PERF_REG_ARM64_X28:
+ return "x28";
+ case PERF_REG_ARM64_X29:
+ return "x29";
+ case PERF_REG_ARM64_SP:
+ return "sp";
+ case PERF_REG_ARM64_LR:
+ return "lr";
+ case PERF_REG_ARM64_PC:
+ return "pc";
+ case PERF_REG_ARM64_VG:
+ return "vg";
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+uint64_t __perf_reg_ip_arm64(void)
+{
+ return PERF_REG_ARM64_PC;
+}
+
+uint64_t __perf_reg_sp_arm64(void)
+{
+ return PERF_REG_ARM64_SP;
+}
+
+#endif
diff --git a/tools/perf/util/perf-regs-arch/perf_regs_arm.c b/tools/perf/util/perf-regs-arch/perf_regs_arm.c
new file mode 100644
index 000000000000..700fd07cd2aa
--- /dev/null
+++ b/tools/perf/util/perf-regs-arch/perf_regs_arm.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifdef HAVE_PERF_REGS_SUPPORT
+
+#include "../perf_regs.h"
+#include "../../../arch/arm/include/uapi/asm/perf_regs.h"
+
+const char *__perf_reg_name_arm(int id)
+{
+ switch (id) {
+ case PERF_REG_ARM_R0:
+ return "r0";
+ case PERF_REG_ARM_R1:
+ return "r1";
+ case PERF_REG_ARM_R2:
+ return "r2";
+ case PERF_REG_ARM_R3:
+ return "r3";
+ case PERF_REG_ARM_R4:
+ return "r4";
+ case PERF_REG_ARM_R5:
+ return "r5";
+ case PERF_REG_ARM_R6:
+ return "r6";
+ case PERF_REG_ARM_R7:
+ return "r7";
+ case PERF_REG_ARM_R8:
+ return "r8";
+ case PERF_REG_ARM_R9:
+ return "r9";
+ case PERF_REG_ARM_R10:
+ return "r10";
+ case PERF_REG_ARM_FP:
+ return "fp";
+ case PERF_REG_ARM_IP:
+ return "ip";
+ case PERF_REG_ARM_SP:
+ return "sp";
+ case PERF_REG_ARM_LR:
+ return "lr";
+ case PERF_REG_ARM_PC:
+ return "pc";
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+uint64_t __perf_reg_ip_arm(void)
+{
+ return PERF_REG_ARM_PC;
+}
+
+uint64_t __perf_reg_sp_arm(void)
+{
+ return PERF_REG_ARM_SP;
+}
+
+#endif
diff --git a/tools/perf/util/perf-regs-arch/perf_regs_csky.c b/tools/perf/util/perf-regs-arch/perf_regs_csky.c
new file mode 100644
index 000000000000..a2841094e096
--- /dev/null
+++ b/tools/perf/util/perf-regs-arch/perf_regs_csky.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifdef HAVE_PERF_REGS_SUPPORT
+
+#include "../perf_regs.h"
+#include "../../arch/csky/include/uapi/asm/perf_regs.h"
+
+const char *__perf_reg_name_csky(int id)
+{
+ switch (id) {
+ case PERF_REG_CSKY_A0:
+ return "a0";
+ case PERF_REG_CSKY_A1:
+ return "a1";
+ case PERF_REG_CSKY_A2:
+ return "a2";
+ case PERF_REG_CSKY_A3:
+ return "a3";
+ case PERF_REG_CSKY_REGS0:
+ return "regs0";
+ case PERF_REG_CSKY_REGS1:
+ return "regs1";
+ case PERF_REG_CSKY_REGS2:
+ return "regs2";
+ case PERF_REG_CSKY_REGS3:
+ return "regs3";
+ case PERF_REG_CSKY_REGS4:
+ return "regs4";
+ case PERF_REG_CSKY_REGS5:
+ return "regs5";
+ case PERF_REG_CSKY_REGS6:
+ return "regs6";
+ case PERF_REG_CSKY_REGS7:
+ return "regs7";
+ case PERF_REG_CSKY_REGS8:
+ return "regs8";
+ case PERF_REG_CSKY_REGS9:
+ return "regs9";
+ case PERF_REG_CSKY_SP:
+ return "sp";
+ case PERF_REG_CSKY_LR:
+ return "lr";
+ case PERF_REG_CSKY_PC:
+ return "pc";
+#if defined(__CSKYABIV2__)
+ case PERF_REG_CSKY_EXREGS0:
+ return "exregs0";
+ case PERF_REG_CSKY_EXREGS1:
+ return "exregs1";
+ case PERF_REG_CSKY_EXREGS2:
+ return "exregs2";
+ case PERF_REG_CSKY_EXREGS3:
+ return "exregs3";
+ case PERF_REG_CSKY_EXREGS4:
+ return "exregs4";
+ case PERF_REG_CSKY_EXREGS5:
+ return "exregs5";
+ case PERF_REG_CSKY_EXREGS6:
+ return "exregs6";
+ case PERF_REG_CSKY_EXREGS7:
+ return "exregs7";
+ case PERF_REG_CSKY_EXREGS8:
+ return "exregs8";
+ case PERF_REG_CSKY_EXREGS9:
+ return "exregs9";
+ case PERF_REG_CSKY_EXREGS10:
+ return "exregs10";
+ case PERF_REG_CSKY_EXREGS11:
+ return "exregs11";
+ case PERF_REG_CSKY_EXREGS12:
+ return "exregs12";
+ case PERF_REG_CSKY_EXREGS13:
+ return "exregs13";
+ case PERF_REG_CSKY_EXREGS14:
+ return "exregs14";
+ case PERF_REG_CSKY_TLS:
+ return "tls";
+ case PERF_REG_CSKY_HI:
+ return "hi";
+ case PERF_REG_CSKY_LO:
+ return "lo";
+#endif
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+uint64_t __perf_reg_ip_csky(void)
+{
+ return PERF_REG_CSKY_PC;
+}
+
+uint64_t __perf_reg_sp_csky(void)
+{
+ return PERF_REG_CSKY_SP;
+}
+
+#endif
diff --git a/tools/perf/util/perf-regs-arch/perf_regs_loongarch.c b/tools/perf/util/perf-regs-arch/perf_regs_loongarch.c
new file mode 100644
index 000000000000..a9ba0f934123
--- /dev/null
+++ b/tools/perf/util/perf-regs-arch/perf_regs_loongarch.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifdef HAVE_PERF_REGS_SUPPORT
+
+#include "../perf_regs.h"
+#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h"
+
+const char *__perf_reg_name_loongarch(int id)
+{
+ switch (id) {
+ case PERF_REG_LOONGARCH_PC:
+ return "PC";
+ case PERF_REG_LOONGARCH_R1:
+ return "%r1";
+ case PERF_REG_LOONGARCH_R2:
+ return "%r2";
+ case PERF_REG_LOONGARCH_R3:
+ return "%r3";
+ case PERF_REG_LOONGARCH_R4:
+ return "%r4";
+ case PERF_REG_LOONGARCH_R5:
+ return "%r5";
+ case PERF_REG_LOONGARCH_R6:
+ return "%r6";
+ case PERF_REG_LOONGARCH_R7:
+ return "%r7";
+ case PERF_REG_LOONGARCH_R8:
+ return "%r8";
+ case PERF_REG_LOONGARCH_R9:
+ return "%r9";
+ case PERF_REG_LOONGARCH_R10:
+ return "%r10";
+ case PERF_REG_LOONGARCH_R11:
+ return "%r11";
+ case PERF_REG_LOONGARCH_R12:
+ return "%r12";
+ case PERF_REG_LOONGARCH_R13:
+ return "%r13";
+ case PERF_REG_LOONGARCH_R14:
+ return "%r14";
+ case PERF_REG_LOONGARCH_R15:
+ return "%r15";
+ case PERF_REG_LOONGARCH_R16:
+ return "%r16";
+ case PERF_REG_LOONGARCH_R17:
+ return "%r17";
+ case PERF_REG_LOONGARCH_R18:
+ return "%r18";
+ case PERF_REG_LOONGARCH_R19:
+ return "%r19";
+ case PERF_REG_LOONGARCH_R20:
+ return "%r20";
+ case PERF_REG_LOONGARCH_R21:
+ return "%r21";
+ case PERF_REG_LOONGARCH_R22:
+ return "%r22";
+ case PERF_REG_LOONGARCH_R23:
+ return "%r23";
+ case PERF_REG_LOONGARCH_R24:
+ return "%r24";
+ case PERF_REG_LOONGARCH_R25:
+ return "%r25";
+ case PERF_REG_LOONGARCH_R26:
+ return "%r26";
+ case PERF_REG_LOONGARCH_R27:
+ return "%r27";
+ case PERF_REG_LOONGARCH_R28:
+ return "%r28";
+ case PERF_REG_LOONGARCH_R29:
+ return "%r29";
+ case PERF_REG_LOONGARCH_R30:
+ return "%r30";
+ case PERF_REG_LOONGARCH_R31:
+ return "%r31";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+uint64_t __perf_reg_ip_loongarch(void)
+{
+ return PERF_REG_LOONGARCH_PC;
+}
+
+uint64_t __perf_reg_sp_loongarch(void)
+{
+ return PERF_REG_LOONGARCH_R3;
+}
+
+#endif
diff --git a/tools/perf/util/perf-regs-arch/perf_regs_mips.c b/tools/perf/util/perf-regs-arch/perf_regs_mips.c
new file mode 100644
index 000000000000..5a45830cfbf5
--- /dev/null
+++ b/tools/perf/util/perf-regs-arch/perf_regs_mips.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifdef HAVE_PERF_REGS_SUPPORT
+
+#include "../perf_regs.h"
+#include "../../../arch/mips/include/uapi/asm/perf_regs.h"
+
+const char *__perf_reg_name_mips(int id)
+{
+ switch (id) {
+ case PERF_REG_MIPS_PC:
+ return "PC";
+ case PERF_REG_MIPS_R1:
+ return "$1";
+ case PERF_REG_MIPS_R2:
+ return "$2";
+ case PERF_REG_MIPS_R3:
+ return "$3";
+ case PERF_REG_MIPS_R4:
+ return "$4";
+ case PERF_REG_MIPS_R5:
+ return "$5";
+ case PERF_REG_MIPS_R6:
+ return "$6";
+ case PERF_REG_MIPS_R7:
+ return "$7";
+ case PERF_REG_MIPS_R8:
+ return "$8";
+ case PERF_REG_MIPS_R9:
+ return "$9";
+ case PERF_REG_MIPS_R10:
+ return "$10";
+ case PERF_REG_MIPS_R11:
+ return "$11";
+ case PERF_REG_MIPS_R12:
+ return "$12";
+ case PERF_REG_MIPS_R13:
+ return "$13";
+ case PERF_REG_MIPS_R14:
+ return "$14";
+ case PERF_REG_MIPS_R15:
+ return "$15";
+ case PERF_REG_MIPS_R16:
+ return "$16";
+ case PERF_REG_MIPS_R17:
+ return "$17";
+ case PERF_REG_MIPS_R18:
+ return "$18";
+ case PERF_REG_MIPS_R19:
+ return "$19";
+ case PERF_REG_MIPS_R20:
+ return "$20";
+ case PERF_REG_MIPS_R21:
+ return "$21";
+ case PERF_REG_MIPS_R22:
+ return "$22";
+ case PERF_REG_MIPS_R23:
+ return "$23";
+ case PERF_REG_MIPS_R24:
+ return "$24";
+ case PERF_REG_MIPS_R25:
+ return "$25";
+ case PERF_REG_MIPS_R28:
+ return "$28";
+ case PERF_REG_MIPS_R29:
+ return "$29";
+ case PERF_REG_MIPS_R30:
+ return "$30";
+ case PERF_REG_MIPS_R31:
+ return "$31";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+uint64_t __perf_reg_ip_mips(void)
+{
+ return PERF_REG_MIPS_PC;
+}
+
+uint64_t __perf_reg_sp_mips(void)
+{
+ return PERF_REG_MIPS_R29;
+}
+
+#endif
diff --git a/tools/perf/util/perf-regs-arch/perf_regs_powerpc.c b/tools/perf/util/perf-regs-arch/perf_regs_powerpc.c
new file mode 100644
index 000000000000..1f0d682db74a
--- /dev/null
+++ b/tools/perf/util/perf-regs-arch/perf_regs_powerpc.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifdef HAVE_PERF_REGS_SUPPORT
+
+#include "../perf_regs.h"
+#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
+
+const char *__perf_reg_name_powerpc(int id)
+{
+ switch (id) {
+ case PERF_REG_POWERPC_R0:
+ return "r0";
+ case PERF_REG_POWERPC_R1:
+ return "r1";
+ case PERF_REG_POWERPC_R2:
+ return "r2";
+ case PERF_REG_POWERPC_R3:
+ return "r3";
+ case PERF_REG_POWERPC_R4:
+ return "r4";
+ case PERF_REG_POWERPC_R5:
+ return "r5";
+ case PERF_REG_POWERPC_R6:
+ return "r6";
+ case PERF_REG_POWERPC_R7:
+ return "r7";
+ case PERF_REG_POWERPC_R8:
+ return "r8";
+ case PERF_REG_POWERPC_R9:
+ return "r9";
+ case PERF_REG_POWERPC_R10:
+ return "r10";
+ case PERF_REG_POWERPC_R11:
+ return "r11";
+ case PERF_REG_POWERPC_R12:
+ return "r12";
+ case PERF_REG_POWERPC_R13:
+ return "r13";
+ case PERF_REG_POWERPC_R14:
+ return "r14";
+ case PERF_REG_POWERPC_R15:
+ return "r15";
+ case PERF_REG_POWERPC_R16:
+ return "r16";
+ case PERF_REG_POWERPC_R17:
+ return "r17";
+ case PERF_REG_POWERPC_R18:
+ return "r18";
+ case PERF_REG_POWERPC_R19:
+ return "r19";
+ case PERF_REG_POWERPC_R20:
+ return "r20";
+ case PERF_REG_POWERPC_R21:
+ return "r21";
+ case PERF_REG_POWERPC_R22:
+ return "r22";
+ case PERF_REG_POWERPC_R23:
+ return "r23";
+ case PERF_REG_POWERPC_R24:
+ return "r24";
+ case PERF_REG_POWERPC_R25:
+ return "r25";
+ case PERF_REG_POWERPC_R26:
+ return "r26";
+ case PERF_REG_POWERPC_R27:
+ return "r27";
+ case PERF_REG_POWERPC_R28:
+ return "r28";
+ case PERF_REG_POWERPC_R29:
+ return "r29";
+ case PERF_REG_POWERPC_R30:
+ return "r30";
+ case PERF_REG_POWERPC_R31:
+ return "r31";
+ case PERF_REG_POWERPC_NIP:
+ return "nip";
+ case PERF_REG_POWERPC_MSR:
+ return "msr";
+ case PERF_REG_POWERPC_ORIG_R3:
+ return "orig_r3";
+ case PERF_REG_POWERPC_CTR:
+ return "ctr";
+ case PERF_REG_POWERPC_LINK:
+ return "link";
+ case PERF_REG_POWERPC_XER:
+ return "xer";
+ case PERF_REG_POWERPC_CCR:
+ return "ccr";
+ case PERF_REG_POWERPC_SOFTE:
+ return "softe";
+ case PERF_REG_POWERPC_TRAP:
+ return "trap";
+ case PERF_REG_POWERPC_DAR:
+ return "dar";
+ case PERF_REG_POWERPC_DSISR:
+ return "dsisr";
+ case PERF_REG_POWERPC_SIER:
+ return "sier";
+ case PERF_REG_POWERPC_MMCRA:
+ return "mmcra";
+ case PERF_REG_POWERPC_MMCR0:
+ return "mmcr0";
+ case PERF_REG_POWERPC_MMCR1:
+ return "mmcr1";
+ case PERF_REG_POWERPC_MMCR2:
+ return "mmcr2";
+ case PERF_REG_POWERPC_MMCR3:
+ return "mmcr3";
+ case PERF_REG_POWERPC_SIER2:
+ return "sier2";
+ case PERF_REG_POWERPC_SIER3:
+ return "sier3";
+ case PERF_REG_POWERPC_PMC1:
+ return "pmc1";
+ case PERF_REG_POWERPC_PMC2:
+ return "pmc2";
+ case PERF_REG_POWERPC_PMC3:
+ return "pmc3";
+ case PERF_REG_POWERPC_PMC4:
+ return "pmc4";
+ case PERF_REG_POWERPC_PMC5:
+ return "pmc5";
+ case PERF_REG_POWERPC_PMC6:
+ return "pmc6";
+ case PERF_REG_POWERPC_SDAR:
+ return "sdar";
+ case PERF_REG_POWERPC_SIAR:
+ return "siar";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+uint64_t __perf_reg_ip_powerpc(void)
+{
+ return PERF_REG_POWERPC_NIP;
+}
+
+uint64_t __perf_reg_sp_powerpc(void)
+{
+ return PERF_REG_POWERPC_R1;
+}
+
+#endif
diff --git a/tools/perf/util/perf-regs-arch/perf_regs_riscv.c b/tools/perf/util/perf-regs-arch/perf_regs_riscv.c
new file mode 100644
index 000000000000..e432630be4c5
--- /dev/null
+++ b/tools/perf/util/perf-regs-arch/perf_regs_riscv.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifdef HAVE_PERF_REGS_SUPPORT
+
+#include "../perf_regs.h"
+#include "../../../arch/riscv/include/uapi/asm/perf_regs.h"
+
+const char *__perf_reg_name_riscv(int id)
+{
+ switch (id) {
+ case PERF_REG_RISCV_PC:
+ return "pc";
+ case PERF_REG_RISCV_RA:
+ return "ra";
+ case PERF_REG_RISCV_SP:
+ return "sp";
+ case PERF_REG_RISCV_GP:
+ return "gp";
+ case PERF_REG_RISCV_TP:
+ return "tp";
+ case PERF_REG_RISCV_T0:
+ return "t0";
+ case PERF_REG_RISCV_T1:
+ return "t1";
+ case PERF_REG_RISCV_T2:
+ return "t2";
+ case PERF_REG_RISCV_S0:
+ return "s0";
+ case PERF_REG_RISCV_S1:
+ return "s1";
+ case PERF_REG_RISCV_A0:
+ return "a0";
+ case PERF_REG_RISCV_A1:
+ return "a1";
+ case PERF_REG_RISCV_A2:
+ return "a2";
+ case PERF_REG_RISCV_A3:
+ return "a3";
+ case PERF_REG_RISCV_A4:
+ return "a4";
+ case PERF_REG_RISCV_A5:
+ return "a5";
+ case PERF_REG_RISCV_A6:
+ return "a6";
+ case PERF_REG_RISCV_A7:
+ return "a7";
+ case PERF_REG_RISCV_S2:
+ return "s2";
+ case PERF_REG_RISCV_S3:
+ return "s3";
+ case PERF_REG_RISCV_S4:
+ return "s4";
+ case PERF_REG_RISCV_S5:
+ return "s5";
+ case PERF_REG_RISCV_S6:
+ return "s6";
+ case PERF_REG_RISCV_S7:
+ return "s7";
+ case PERF_REG_RISCV_S8:
+ return "s8";
+ case PERF_REG_RISCV_S9:
+ return "s9";
+ case PERF_REG_RISCV_S10:
+ return "s10";
+ case PERF_REG_RISCV_S11:
+ return "s11";
+ case PERF_REG_RISCV_T3:
+ return "t3";
+ case PERF_REG_RISCV_T4:
+ return "t4";
+ case PERF_REG_RISCV_T5:
+ return "t5";
+ case PERF_REG_RISCV_T6:
+ return "t6";
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+uint64_t __perf_reg_ip_riscv(void)
+{
+ return PERF_REG_RISCV_PC;
+}
+
+uint64_t __perf_reg_sp_riscv(void)
+{
+ return PERF_REG_RISCV_SP;
+}
+
+#endif
diff --git a/tools/perf/util/perf-regs-arch/perf_regs_s390.c b/tools/perf/util/perf-regs-arch/perf_regs_s390.c
new file mode 100644
index 000000000000..1c7a46db778c
--- /dev/null
+++ b/tools/perf/util/perf-regs-arch/perf_regs_s390.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifdef HAVE_PERF_REGS_SUPPORT
+
+#include "../perf_regs.h"
+#include "../../../arch/s390/include/uapi/asm/perf_regs.h"
+
+const char *__perf_reg_name_s390(int id)
+{
+ switch (id) {
+ case PERF_REG_S390_R0:
+ return "R0";
+ case PERF_REG_S390_R1:
+ return "R1";
+ case PERF_REG_S390_R2:
+ return "R2";
+ case PERF_REG_S390_R3:
+ return "R3";
+ case PERF_REG_S390_R4:
+ return "R4";
+ case PERF_REG_S390_R5:
+ return "R5";
+ case PERF_REG_S390_R6:
+ return "R6";
+ case PERF_REG_S390_R7:
+ return "R7";
+ case PERF_REG_S390_R8:
+ return "R8";
+ case PERF_REG_S390_R9:
+ return "R9";
+ case PERF_REG_S390_R10:
+ return "R10";
+ case PERF_REG_S390_R11:
+ return "R11";
+ case PERF_REG_S390_R12:
+ return "R12";
+ case PERF_REG_S390_R13:
+ return "R13";
+ case PERF_REG_S390_R14:
+ return "R14";
+ case PERF_REG_S390_R15:
+ return "R15";
+ case PERF_REG_S390_FP0:
+ return "FP0";
+ case PERF_REG_S390_FP1:
+ return "FP1";
+ case PERF_REG_S390_FP2:
+ return "FP2";
+ case PERF_REG_S390_FP3:
+ return "FP3";
+ case PERF_REG_S390_FP4:
+ return "FP4";
+ case PERF_REG_S390_FP5:
+ return "FP5";
+ case PERF_REG_S390_FP6:
+ return "FP6";
+ case PERF_REG_S390_FP7:
+ return "FP7";
+ case PERF_REG_S390_FP8:
+ return "FP8";
+ case PERF_REG_S390_FP9:
+ return "FP9";
+ case PERF_REG_S390_FP10:
+ return "FP10";
+ case PERF_REG_S390_FP11:
+ return "FP11";
+ case PERF_REG_S390_FP12:
+ return "FP12";
+ case PERF_REG_S390_FP13:
+ return "FP13";
+ case PERF_REG_S390_FP14:
+ return "FP14";
+ case PERF_REG_S390_FP15:
+ return "FP15";
+ case PERF_REG_S390_MASK:
+ return "MASK";
+ case PERF_REG_S390_PC:
+ return "PC";
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+uint64_t __perf_reg_ip_s390(void)
+{
+ return PERF_REG_S390_PC;
+}
+
+uint64_t __perf_reg_sp_s390(void)
+{
+ return PERF_REG_S390_R15;
+}
+
+#endif
diff --git a/tools/perf/util/perf-regs-arch/perf_regs_x86.c b/tools/perf/util/perf-regs-arch/perf_regs_x86.c
new file mode 100644
index 000000000000..873c620f0634
--- /dev/null
+++ b/tools/perf/util/perf-regs-arch/perf_regs_x86.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifdef HAVE_PERF_REGS_SUPPORT
+
+#include "../perf_regs.h"
+#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
+
+const char *__perf_reg_name_x86(int id)
+{
+ switch (id) {
+ case PERF_REG_X86_AX:
+ return "AX";
+ case PERF_REG_X86_BX:
+ return "BX";
+ case PERF_REG_X86_CX:
+ return "CX";
+ case PERF_REG_X86_DX:
+ return "DX";
+ case PERF_REG_X86_SI:
+ return "SI";
+ case PERF_REG_X86_DI:
+ return "DI";
+ case PERF_REG_X86_BP:
+ return "BP";
+ case PERF_REG_X86_SP:
+ return "SP";
+ case PERF_REG_X86_IP:
+ return "IP";
+ case PERF_REG_X86_FLAGS:
+ return "FLAGS";
+ case PERF_REG_X86_CS:
+ return "CS";
+ case PERF_REG_X86_SS:
+ return "SS";
+ case PERF_REG_X86_DS:
+ return "DS";
+ case PERF_REG_X86_ES:
+ return "ES";
+ case PERF_REG_X86_FS:
+ return "FS";
+ case PERF_REG_X86_GS:
+ return "GS";
+ case PERF_REG_X86_R8:
+ return "R8";
+ case PERF_REG_X86_R9:
+ return "R9";
+ case PERF_REG_X86_R10:
+ return "R10";
+ case PERF_REG_X86_R11:
+ return "R11";
+ case PERF_REG_X86_R12:
+ return "R12";
+ case PERF_REG_X86_R13:
+ return "R13";
+ case PERF_REG_X86_R14:
+ return "R14";
+ case PERF_REG_X86_R15:
+ return "R15";
+
+#define XMM(x) \
+ case PERF_REG_X86_XMM ## x: \
+ case PERF_REG_X86_XMM ## x + 1: \
+ return "XMM" #x;
+ XMM(0)
+ XMM(1)
+ XMM(2)
+ XMM(3)
+ XMM(4)
+ XMM(5)
+ XMM(6)
+ XMM(7)
+ XMM(8)
+ XMM(9)
+ XMM(10)
+ XMM(11)
+ XMM(12)
+ XMM(13)
+ XMM(14)
+ XMM(15)
+#undef XMM
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+uint64_t __perf_reg_ip_x86(void)
+{
+ return PERF_REG_X86_IP;
+}
+
+uint64_t __perf_reg_sp_x86(void)
+{
+ return PERF_REG_X86_SP;
+}
+
+#endif
diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c
index 9bdbaa37f813..e2275856b570 100644
--- a/tools/perf/util/perf_regs.c
+++ b/tools/perf/util/perf_regs.c
@@ -3,6 +3,7 @@
#include <string.h>
#include "perf_regs.h"
#include "util/sample.h"
+#include "debug.h"
int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused,
char **new_op __maybe_unused)
@@ -12,732 +13,16 @@ int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused,
uint64_t __weak arch__intr_reg_mask(void)
{
- return PERF_REGS_MASK;
+ return 0;
}
uint64_t __weak arch__user_reg_mask(void)
{
- return PERF_REGS_MASK;
+ return 0;
}
#ifdef HAVE_PERF_REGS_SUPPORT
-#define perf_event_arm_regs perf_event_arm64_regs
-#include "../../arch/arm64/include/uapi/asm/perf_regs.h"
-#undef perf_event_arm_regs
-
-#include "../../arch/arm/include/uapi/asm/perf_regs.h"
-#include "../../arch/csky/include/uapi/asm/perf_regs.h"
-#include "../../arch/loongarch/include/uapi/asm/perf_regs.h"
-#include "../../arch/mips/include/uapi/asm/perf_regs.h"
-#include "../../arch/powerpc/include/uapi/asm/perf_regs.h"
-#include "../../arch/riscv/include/uapi/asm/perf_regs.h"
-#include "../../arch/s390/include/uapi/asm/perf_regs.h"
-#include "../../arch/x86/include/uapi/asm/perf_regs.h"
-
-static const char *__perf_reg_name_arm64(int id)
-{
- switch (id) {
- case PERF_REG_ARM64_X0:
- return "x0";
- case PERF_REG_ARM64_X1:
- return "x1";
- case PERF_REG_ARM64_X2:
- return "x2";
- case PERF_REG_ARM64_X3:
- return "x3";
- case PERF_REG_ARM64_X4:
- return "x4";
- case PERF_REG_ARM64_X5:
- return "x5";
- case PERF_REG_ARM64_X6:
- return "x6";
- case PERF_REG_ARM64_X7:
- return "x7";
- case PERF_REG_ARM64_X8:
- return "x8";
- case PERF_REG_ARM64_X9:
- return "x9";
- case PERF_REG_ARM64_X10:
- return "x10";
- case PERF_REG_ARM64_X11:
- return "x11";
- case PERF_REG_ARM64_X12:
- return "x12";
- case PERF_REG_ARM64_X13:
- return "x13";
- case PERF_REG_ARM64_X14:
- return "x14";
- case PERF_REG_ARM64_X15:
- return "x15";
- case PERF_REG_ARM64_X16:
- return "x16";
- case PERF_REG_ARM64_X17:
- return "x17";
- case PERF_REG_ARM64_X18:
- return "x18";
- case PERF_REG_ARM64_X19:
- return "x19";
- case PERF_REG_ARM64_X20:
- return "x20";
- case PERF_REG_ARM64_X21:
- return "x21";
- case PERF_REG_ARM64_X22:
- return "x22";
- case PERF_REG_ARM64_X23:
- return "x23";
- case PERF_REG_ARM64_X24:
- return "x24";
- case PERF_REG_ARM64_X25:
- return "x25";
- case PERF_REG_ARM64_X26:
- return "x26";
- case PERF_REG_ARM64_X27:
- return "x27";
- case PERF_REG_ARM64_X28:
- return "x28";
- case PERF_REG_ARM64_X29:
- return "x29";
- case PERF_REG_ARM64_SP:
- return "sp";
- case PERF_REG_ARM64_LR:
- return "lr";
- case PERF_REG_ARM64_PC:
- return "pc";
- case PERF_REG_ARM64_VG:
- return "vg";
- default:
- return NULL;
- }
-
- return NULL;
-}
-
-static const char *__perf_reg_name_arm(int id)
-{
- switch (id) {
- case PERF_REG_ARM_R0:
- return "r0";
- case PERF_REG_ARM_R1:
- return "r1";
- case PERF_REG_ARM_R2:
- return "r2";
- case PERF_REG_ARM_R3:
- return "r3";
- case PERF_REG_ARM_R4:
- return "r4";
- case PERF_REG_ARM_R5:
- return "r5";
- case PERF_REG_ARM_R6:
- return "r6";
- case PERF_REG_ARM_R7:
- return "r7";
- case PERF_REG_ARM_R8:
- return "r8";
- case PERF_REG_ARM_R9:
- return "r9";
- case PERF_REG_ARM_R10:
- return "r10";
- case PERF_REG_ARM_FP:
- return "fp";
- case PERF_REG_ARM_IP:
- return "ip";
- case PERF_REG_ARM_SP:
- return "sp";
- case PERF_REG_ARM_LR:
- return "lr";
- case PERF_REG_ARM_PC:
- return "pc";
- default:
- return NULL;
- }
-
- return NULL;
-}
-
-static const char *__perf_reg_name_csky(int id)
-{
- switch (id) {
- case PERF_REG_CSKY_A0:
- return "a0";
- case PERF_REG_CSKY_A1:
- return "a1";
- case PERF_REG_CSKY_A2:
- return "a2";
- case PERF_REG_CSKY_A3:
- return "a3";
- case PERF_REG_CSKY_REGS0:
- return "regs0";
- case PERF_REG_CSKY_REGS1:
- return "regs1";
- case PERF_REG_CSKY_REGS2:
- return "regs2";
- case PERF_REG_CSKY_REGS3:
- return "regs3";
- case PERF_REG_CSKY_REGS4:
- return "regs4";
- case PERF_REG_CSKY_REGS5:
- return "regs5";
- case PERF_REG_CSKY_REGS6:
- return "regs6";
- case PERF_REG_CSKY_REGS7:
- return "regs7";
- case PERF_REG_CSKY_REGS8:
- return "regs8";
- case PERF_REG_CSKY_REGS9:
- return "regs9";
- case PERF_REG_CSKY_SP:
- return "sp";
- case PERF_REG_CSKY_LR:
- return "lr";
- case PERF_REG_CSKY_PC:
- return "pc";
-#if defined(__CSKYABIV2__)
- case PERF_REG_CSKY_EXREGS0:
- return "exregs0";
- case PERF_REG_CSKY_EXREGS1:
- return "exregs1";
- case PERF_REG_CSKY_EXREGS2:
- return "exregs2";
- case PERF_REG_CSKY_EXREGS3:
- return "exregs3";
- case PERF_REG_CSKY_EXREGS4:
- return "exregs4";
- case PERF_REG_CSKY_EXREGS5:
- return "exregs5";
- case PERF_REG_CSKY_EXREGS6:
- return "exregs6";
- case PERF_REG_CSKY_EXREGS7:
- return "exregs7";
- case PERF_REG_CSKY_EXREGS8:
- return "exregs8";
- case PERF_REG_CSKY_EXREGS9:
- return "exregs9";
- case PERF_REG_CSKY_EXREGS10:
- return "exregs10";
- case PERF_REG_CSKY_EXREGS11:
- return "exregs11";
- case PERF_REG_CSKY_EXREGS12:
- return "exregs12";
- case PERF_REG_CSKY_EXREGS13:
- return "exregs13";
- case PERF_REG_CSKY_EXREGS14:
- return "exregs14";
- case PERF_REG_CSKY_TLS:
- return "tls";
- case PERF_REG_CSKY_HI:
- return "hi";
- case PERF_REG_CSKY_LO:
- return "lo";
-#endif
- default:
- return NULL;
- }
-
- return NULL;
-}
-
-static inline const char *__perf_reg_name_loongarch(int id)
-{
- switch (id) {
- case PERF_REG_LOONGARCH_PC:
- return "PC";
- case PERF_REG_LOONGARCH_R1:
- return "%r1";
- case PERF_REG_LOONGARCH_R2:
- return "%r2";
- case PERF_REG_LOONGARCH_R3:
- return "%r3";
- case PERF_REG_LOONGARCH_R4:
- return "%r4";
- case PERF_REG_LOONGARCH_R5:
- return "%r5";
- case PERF_REG_LOONGARCH_R6:
- return "%r6";
- case PERF_REG_LOONGARCH_R7:
- return "%r7";
- case PERF_REG_LOONGARCH_R8:
- return "%r8";
- case PERF_REG_LOONGARCH_R9:
- return "%r9";
- case PERF_REG_LOONGARCH_R10:
- return "%r10";
- case PERF_REG_LOONGARCH_R11:
- return "%r11";
- case PERF_REG_LOONGARCH_R12:
- return "%r12";
- case PERF_REG_LOONGARCH_R13:
- return "%r13";
- case PERF_REG_LOONGARCH_R14:
- return "%r14";
- case PERF_REG_LOONGARCH_R15:
- return "%r15";
- case PERF_REG_LOONGARCH_R16:
- return "%r16";
- case PERF_REG_LOONGARCH_R17:
- return "%r17";
- case PERF_REG_LOONGARCH_R18:
- return "%r18";
- case PERF_REG_LOONGARCH_R19:
- return "%r19";
- case PERF_REG_LOONGARCH_R20:
- return "%r20";
- case PERF_REG_LOONGARCH_R21:
- return "%r21";
- case PERF_REG_LOONGARCH_R22:
- return "%r22";
- case PERF_REG_LOONGARCH_R23:
- return "%r23";
- case PERF_REG_LOONGARCH_R24:
- return "%r24";
- case PERF_REG_LOONGARCH_R25:
- return "%r25";
- case PERF_REG_LOONGARCH_R26:
- return "%r26";
- case PERF_REG_LOONGARCH_R27:
- return "%r27";
- case PERF_REG_LOONGARCH_R28:
- return "%r28";
- case PERF_REG_LOONGARCH_R29:
- return "%r29";
- case PERF_REG_LOONGARCH_R30:
- return "%r30";
- case PERF_REG_LOONGARCH_R31:
- return "%r31";
- default:
- break;
- }
- return NULL;
-}
-
-static const char *__perf_reg_name_mips(int id)
-{
- switch (id) {
- case PERF_REG_MIPS_PC:
- return "PC";
- case PERF_REG_MIPS_R1:
- return "$1";
- case PERF_REG_MIPS_R2:
- return "$2";
- case PERF_REG_MIPS_R3:
- return "$3";
- case PERF_REG_MIPS_R4:
- return "$4";
- case PERF_REG_MIPS_R5:
- return "$5";
- case PERF_REG_MIPS_R6:
- return "$6";
- case PERF_REG_MIPS_R7:
- return "$7";
- case PERF_REG_MIPS_R8:
- return "$8";
- case PERF_REG_MIPS_R9:
- return "$9";
- case PERF_REG_MIPS_R10:
- return "$10";
- case PERF_REG_MIPS_R11:
- return "$11";
- case PERF_REG_MIPS_R12:
- return "$12";
- case PERF_REG_MIPS_R13:
- return "$13";
- case PERF_REG_MIPS_R14:
- return "$14";
- case PERF_REG_MIPS_R15:
- return "$15";
- case PERF_REG_MIPS_R16:
- return "$16";
- case PERF_REG_MIPS_R17:
- return "$17";
- case PERF_REG_MIPS_R18:
- return "$18";
- case PERF_REG_MIPS_R19:
- return "$19";
- case PERF_REG_MIPS_R20:
- return "$20";
- case PERF_REG_MIPS_R21:
- return "$21";
- case PERF_REG_MIPS_R22:
- return "$22";
- case PERF_REG_MIPS_R23:
- return "$23";
- case PERF_REG_MIPS_R24:
- return "$24";
- case PERF_REG_MIPS_R25:
- return "$25";
- case PERF_REG_MIPS_R28:
- return "$28";
- case PERF_REG_MIPS_R29:
- return "$29";
- case PERF_REG_MIPS_R30:
- return "$30";
- case PERF_REG_MIPS_R31:
- return "$31";
- default:
- break;
- }
- return NULL;
-}
-
-static const char *__perf_reg_name_powerpc(int id)
-{
- switch (id) {
- case PERF_REG_POWERPC_R0:
- return "r0";
- case PERF_REG_POWERPC_R1:
- return "r1";
- case PERF_REG_POWERPC_R2:
- return "r2";
- case PERF_REG_POWERPC_R3:
- return "r3";
- case PERF_REG_POWERPC_R4:
- return "r4";
- case PERF_REG_POWERPC_R5:
- return "r5";
- case PERF_REG_POWERPC_R6:
- return "r6";
- case PERF_REG_POWERPC_R7:
- return "r7";
- case PERF_REG_POWERPC_R8:
- return "r8";
- case PERF_REG_POWERPC_R9:
- return "r9";
- case PERF_REG_POWERPC_R10:
- return "r10";
- case PERF_REG_POWERPC_R11:
- return "r11";
- case PERF_REG_POWERPC_R12:
- return "r12";
- case PERF_REG_POWERPC_R13:
- return "r13";
- case PERF_REG_POWERPC_R14:
- return "r14";
- case PERF_REG_POWERPC_R15:
- return "r15";
- case PERF_REG_POWERPC_R16:
- return "r16";
- case PERF_REG_POWERPC_R17:
- return "r17";
- case PERF_REG_POWERPC_R18:
- return "r18";
- case PERF_REG_POWERPC_R19:
- return "r19";
- case PERF_REG_POWERPC_R20:
- return "r20";
- case PERF_REG_POWERPC_R21:
- return "r21";
- case PERF_REG_POWERPC_R22:
- return "r22";
- case PERF_REG_POWERPC_R23:
- return "r23";
- case PERF_REG_POWERPC_R24:
- return "r24";
- case PERF_REG_POWERPC_R25:
- return "r25";
- case PERF_REG_POWERPC_R26:
- return "r26";
- case PERF_REG_POWERPC_R27:
- return "r27";
- case PERF_REG_POWERPC_R28:
- return "r28";
- case PERF_REG_POWERPC_R29:
- return "r29";
- case PERF_REG_POWERPC_R30:
- return "r30";
- case PERF_REG_POWERPC_R31:
- return "r31";
- case PERF_REG_POWERPC_NIP:
- return "nip";
- case PERF_REG_POWERPC_MSR:
- return "msr";
- case PERF_REG_POWERPC_ORIG_R3:
- return "orig_r3";
- case PERF_REG_POWERPC_CTR:
- return "ctr";
- case PERF_REG_POWERPC_LINK:
- return "link";
- case PERF_REG_POWERPC_XER:
- return "xer";
- case PERF_REG_POWERPC_CCR:
- return "ccr";
- case PERF_REG_POWERPC_SOFTE:
- return "softe";
- case PERF_REG_POWERPC_TRAP:
- return "trap";
- case PERF_REG_POWERPC_DAR:
- return "dar";
- case PERF_REG_POWERPC_DSISR:
- return "dsisr";
- case PERF_REG_POWERPC_SIER:
- return "sier";
- case PERF_REG_POWERPC_MMCRA:
- return "mmcra";
- case PERF_REG_POWERPC_MMCR0:
- return "mmcr0";
- case PERF_REG_POWERPC_MMCR1:
- return "mmcr1";
- case PERF_REG_POWERPC_MMCR2:
- return "mmcr2";
- case PERF_REG_POWERPC_MMCR3:
- return "mmcr3";
- case PERF_REG_POWERPC_SIER2:
- return "sier2";
- case PERF_REG_POWERPC_SIER3:
- return "sier3";
- case PERF_REG_POWERPC_PMC1:
- return "pmc1";
- case PERF_REG_POWERPC_PMC2:
- return "pmc2";
- case PERF_REG_POWERPC_PMC3:
- return "pmc3";
- case PERF_REG_POWERPC_PMC4:
- return "pmc4";
- case PERF_REG_POWERPC_PMC5:
- return "pmc5";
- case PERF_REG_POWERPC_PMC6:
- return "pmc6";
- case PERF_REG_POWERPC_SDAR:
- return "sdar";
- case PERF_REG_POWERPC_SIAR:
- return "siar";
- default:
- break;
- }
- return NULL;
-}
-
-static const char *__perf_reg_name_riscv(int id)
-{
- switch (id) {
- case PERF_REG_RISCV_PC:
- return "pc";
- case PERF_REG_RISCV_RA:
- return "ra";
- case PERF_REG_RISCV_SP:
- return "sp";
- case PERF_REG_RISCV_GP:
- return "gp";
- case PERF_REG_RISCV_TP:
- return "tp";
- case PERF_REG_RISCV_T0:
- return "t0";
- case PERF_REG_RISCV_T1:
- return "t1";
- case PERF_REG_RISCV_T2:
- return "t2";
- case PERF_REG_RISCV_S0:
- return "s0";
- case PERF_REG_RISCV_S1:
- return "s1";
- case PERF_REG_RISCV_A0:
- return "a0";
- case PERF_REG_RISCV_A1:
- return "a1";
- case PERF_REG_RISCV_A2:
- return "a2";
- case PERF_REG_RISCV_A3:
- return "a3";
- case PERF_REG_RISCV_A4:
- return "a4";
- case PERF_REG_RISCV_A5:
- return "a5";
- case PERF_REG_RISCV_A6:
- return "a6";
- case PERF_REG_RISCV_A7:
- return "a7";
- case PERF_REG_RISCV_S2:
- return "s2";
- case PERF_REG_RISCV_S3:
- return "s3";
- case PERF_REG_RISCV_S4:
- return "s4";
- case PERF_REG_RISCV_S5:
- return "s5";
- case PERF_REG_RISCV_S6:
- return "s6";
- case PERF_REG_RISCV_S7:
- return "s7";
- case PERF_REG_RISCV_S8:
- return "s8";
- case PERF_REG_RISCV_S9:
- return "s9";
- case PERF_REG_RISCV_S10:
- return "s10";
- case PERF_REG_RISCV_S11:
- return "s11";
- case PERF_REG_RISCV_T3:
- return "t3";
- case PERF_REG_RISCV_T4:
- return "t4";
- case PERF_REG_RISCV_T5:
- return "t5";
- case PERF_REG_RISCV_T6:
- return "t6";
- default:
- return NULL;
- }
-
- return NULL;
-}
-
-static const char *__perf_reg_name_s390(int id)
-{
- switch (id) {
- case PERF_REG_S390_R0:
- return "R0";
- case PERF_REG_S390_R1:
- return "R1";
- case PERF_REG_S390_R2:
- return "R2";
- case PERF_REG_S390_R3:
- return "R3";
- case PERF_REG_S390_R4:
- return "R4";
- case PERF_REG_S390_R5:
- return "R5";
- case PERF_REG_S390_R6:
- return "R6";
- case PERF_REG_S390_R7:
- return "R7";
- case PERF_REG_S390_R8:
- return "R8";
- case PERF_REG_S390_R9:
- return "R9";
- case PERF_REG_S390_R10:
- return "R10";
- case PERF_REG_S390_R11:
- return "R11";
- case PERF_REG_S390_R12:
- return "R12";
- case PERF_REG_S390_R13:
- return "R13";
- case PERF_REG_S390_R14:
- return "R14";
- case PERF_REG_S390_R15:
- return "R15";
- case PERF_REG_S390_FP0:
- return "FP0";
- case PERF_REG_S390_FP1:
- return "FP1";
- case PERF_REG_S390_FP2:
- return "FP2";
- case PERF_REG_S390_FP3:
- return "FP3";
- case PERF_REG_S390_FP4:
- return "FP4";
- case PERF_REG_S390_FP5:
- return "FP5";
- case PERF_REG_S390_FP6:
- return "FP6";
- case PERF_REG_S390_FP7:
- return "FP7";
- case PERF_REG_S390_FP8:
- return "FP8";
- case PERF_REG_S390_FP9:
- return "FP9";
- case PERF_REG_S390_FP10:
- return "FP10";
- case PERF_REG_S390_FP11:
- return "FP11";
- case PERF_REG_S390_FP12:
- return "FP12";
- case PERF_REG_S390_FP13:
- return "FP13";
- case PERF_REG_S390_FP14:
- return "FP14";
- case PERF_REG_S390_FP15:
- return "FP15";
- case PERF_REG_S390_MASK:
- return "MASK";
- case PERF_REG_S390_PC:
- return "PC";
- default:
- return NULL;
- }
-
- return NULL;
-}
-
-static const char *__perf_reg_name_x86(int id)
-{
- switch (id) {
- case PERF_REG_X86_AX:
- return "AX";
- case PERF_REG_X86_BX:
- return "BX";
- case PERF_REG_X86_CX:
- return "CX";
- case PERF_REG_X86_DX:
- return "DX";
- case PERF_REG_X86_SI:
- return "SI";
- case PERF_REG_X86_DI:
- return "DI";
- case PERF_REG_X86_BP:
- return "BP";
- case PERF_REG_X86_SP:
- return "SP";
- case PERF_REG_X86_IP:
- return "IP";
- case PERF_REG_X86_FLAGS:
- return "FLAGS";
- case PERF_REG_X86_CS:
- return "CS";
- case PERF_REG_X86_SS:
- return "SS";
- case PERF_REG_X86_DS:
- return "DS";
- case PERF_REG_X86_ES:
- return "ES";
- case PERF_REG_X86_FS:
- return "FS";
- case PERF_REG_X86_GS:
- return "GS";
- case PERF_REG_X86_R8:
- return "R8";
- case PERF_REG_X86_R9:
- return "R9";
- case PERF_REG_X86_R10:
- return "R10";
- case PERF_REG_X86_R11:
- return "R11";
- case PERF_REG_X86_R12:
- return "R12";
- case PERF_REG_X86_R13:
- return "R13";
- case PERF_REG_X86_R14:
- return "R14";
- case PERF_REG_X86_R15:
- return "R15";
-
-#define XMM(x) \
- case PERF_REG_X86_XMM ## x: \
- case PERF_REG_X86_XMM ## x + 1: \
- return "XMM" #x;
- XMM(0)
- XMM(1)
- XMM(2)
- XMM(3)
- XMM(4)
- XMM(5)
- XMM(6)
- XMM(7)
- XMM(8)
- XMM(9)
- XMM(10)
- XMM(11)
- XMM(12)
- XMM(13)
- XMM(14)
- XMM(15)
-#undef XMM
- default:
- return NULL;
- }
-
- return NULL;
-}
-
const char *perf_reg_name(int id, const char *arch)
{
const char *reg_name = NULL;
@@ -790,4 +75,55 @@ out:
*valp = regs->cache_regs[id];
return 0;
}
+
+uint64_t perf_arch_reg_ip(const char *arch)
+{
+ if (!strcmp(arch, "arm"))
+ return __perf_reg_ip_arm();
+ else if (!strcmp(arch, "arm64"))
+ return __perf_reg_ip_arm64();
+ else if (!strcmp(arch, "csky"))
+ return __perf_reg_ip_csky();
+ else if (!strcmp(arch, "loongarch"))
+ return __perf_reg_ip_loongarch();
+ else if (!strcmp(arch, "mips"))
+ return __perf_reg_ip_mips();
+ else if (!strcmp(arch, "powerpc"))
+ return __perf_reg_ip_powerpc();
+ else if (!strcmp(arch, "riscv"))
+ return __perf_reg_ip_riscv();
+ else if (!strcmp(arch, "s390"))
+ return __perf_reg_ip_s390();
+ else if (!strcmp(arch, "x86"))
+ return __perf_reg_ip_x86();
+
+ pr_err("Fail to find IP register for arch %s, returns 0\n", arch);
+ return 0;
+}
+
+uint64_t perf_arch_reg_sp(const char *arch)
+{
+ if (!strcmp(arch, "arm"))
+ return __perf_reg_sp_arm();
+ else if (!strcmp(arch, "arm64"))
+ return __perf_reg_sp_arm64();
+ else if (!strcmp(arch, "csky"))
+ return __perf_reg_sp_csky();
+ else if (!strcmp(arch, "loongarch"))
+ return __perf_reg_sp_loongarch();
+ else if (!strcmp(arch, "mips"))
+ return __perf_reg_sp_mips();
+ else if (!strcmp(arch, "powerpc"))
+ return __perf_reg_sp_powerpc();
+ else if (!strcmp(arch, "riscv"))
+ return __perf_reg_sp_riscv();
+ else if (!strcmp(arch, "s390"))
+ return __perf_reg_sp_s390();
+ else if (!strcmp(arch, "x86"))
+ return __perf_reg_sp_x86();
+
+ pr_err("Fail to find SP register for arch %s, returns 0\n", arch);
+ return 0;
+}
+
#endif
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h
index ce1127af05e4..ecd2a5362042 100644
--- a/tools/perf/util/perf_regs.h
+++ b/tools/perf/util/perf_regs.h
@@ -30,18 +30,49 @@ uint64_t arch__user_reg_mask(void);
#ifdef HAVE_PERF_REGS_SUPPORT
extern const struct sample_reg sample_reg_masks[];
-#include <perf_regs.h>
-
-#define DWARF_MINIMAL_REGS ((1ULL << PERF_REG_IP) | (1ULL << PERF_REG_SP))
-
const char *perf_reg_name(int id, const char *arch);
int perf_reg_value(u64 *valp, struct regs_dump *regs, int id);
+uint64_t perf_arch_reg_ip(const char *arch);
+uint64_t perf_arch_reg_sp(const char *arch);
+const char *__perf_reg_name_arm64(int id);
+uint64_t __perf_reg_ip_arm64(void);
+uint64_t __perf_reg_sp_arm64(void);
+const char *__perf_reg_name_arm(int id);
+uint64_t __perf_reg_ip_arm(void);
+uint64_t __perf_reg_sp_arm(void);
+const char *__perf_reg_name_csky(int id);
+uint64_t __perf_reg_ip_csky(void);
+uint64_t __perf_reg_sp_csky(void);
+const char *__perf_reg_name_loongarch(int id);
+uint64_t __perf_reg_ip_loongarch(void);
+uint64_t __perf_reg_sp_loongarch(void);
+const char *__perf_reg_name_mips(int id);
+uint64_t __perf_reg_ip_mips(void);
+uint64_t __perf_reg_sp_mips(void);
+const char *__perf_reg_name_powerpc(int id);
+uint64_t __perf_reg_ip_powerpc(void);
+uint64_t __perf_reg_sp_powerpc(void);
+const char *__perf_reg_name_riscv(int id);
+uint64_t __perf_reg_ip_riscv(void);
+uint64_t __perf_reg_sp_riscv(void);
+const char *__perf_reg_name_s390(int id);
+uint64_t __perf_reg_ip_s390(void);
+uint64_t __perf_reg_sp_s390(void);
+const char *__perf_reg_name_x86(int id);
+uint64_t __perf_reg_ip_x86(void);
+uint64_t __perf_reg_sp_x86(void);
+
+static inline uint64_t DWARF_MINIMAL_REGS(const char *arch)
+{
+ return (1ULL << perf_arch_reg_ip(arch)) | (1ULL << perf_arch_reg_sp(arch));
+}
#else
-#define PERF_REGS_MASK 0
-#define PERF_REGS_MAX 0
-#define DWARF_MINIMAL_REGS PERF_REGS_MASK
+static inline uint64_t DWARF_MINIMAL_REGS(const char *arch __maybe_unused)
+{
+ return 0;
+}
static inline const char *perf_reg_name(int id __maybe_unused, const char *arch __maybe_unused)
{
@@ -54,5 +85,16 @@ static inline int perf_reg_value(u64 *valp __maybe_unused,
{
return 0;
}
+
+static inline uint64_t perf_arch_reg_ip(const char *arch __maybe_unused)
+{
+ return 0;
+}
+
+static inline uint64_t perf_arch_reg_sp(const char *arch __maybe_unused)
+{
+ return 0;
+}
+
#endif /* HAVE_PERF_REGS_SUPPORT */
#endif /* __PERF_REGS_H */
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 28380e7aa8d0..8de6f39abd1b 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -19,8 +19,8 @@
#include "evsel.h"
#include "pmu.h"
#include "pmus.h"
-#include "pmu-bison.h"
-#include "pmu-flex.h"
+#include <util/pmu-bison.h>
+#include <util/pmu-flex.h>
#include "parse-events.h"
#include "print-events.h"
#include "header.h"
@@ -29,7 +29,63 @@
#include "fncache.h"
#include "util/evsel_config.h"
-struct perf_pmu perf_pmu__fake;
+struct perf_pmu perf_pmu__fake = {
+ .name = "fake",
+};
+
+#define UNIT_MAX_LEN 31 /* max length for event unit name */
+
+/**
+ * struct perf_pmu_alias - An event either read from sysfs or builtin in
+ * pmu-events.c, created by parsing the pmu-events json files.
+ */
+struct perf_pmu_alias {
+ /** @name: Name of the event like "mem-loads". */
+ char *name;
+ /** @desc: Optional short description of the event. */
+ char *desc;
+ /** @long_desc: Optional long description. */
+ char *long_desc;
+ /**
+ * @topic: Optional topic such as cache or pipeline, particularly for
+ * json events.
+ */
+ char *topic;
+ /** @terms: Owned list of the original parsed parameters. */
+ struct list_head terms;
+ /** @list: List element of struct perf_pmu aliases. */
+ struct list_head list;
+ /**
+ * @pmu_name: The name copied from the json struct pmu_event. This can
+ * differ from the PMU name as it won't have suffixes.
+ */
+ char *pmu_name;
+ /** @unit: Units for the event, such as bytes or cache lines. */
+ char unit[UNIT_MAX_LEN+1];
+ /** @scale: Value to scale read counter values by. */
+ double scale;
+ /**
+ * @per_pkg: Does the file
+ * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.per-pkg or
+ * equivalent json value exist and have the value 1.
+ */
+ bool per_pkg;
+ /**
+ * @snapshot: Does the file
+ * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.snapshot
+ * exist and have the value 1.
+ */
+ bool snapshot;
+ /**
+ * @deprecated: Is the event hidden and so not shown in perf list by
+ * default.
+ */
+ bool deprecated;
+ /** @from_sysfs: Was the alias from sysfs or a json event? */
+ bool from_sysfs;
+ /** @info_loaded: Have the scale, unit and other values been read from disk? */
+ bool info_loaded;
+};
/**
* struct perf_pmu_format - Values from a format file read from
@@ -40,6 +96,10 @@ struct perf_pmu perf_pmu__fake;
* value=PERF_PMU_FORMAT_VALUE_CONFIG and bits 0 to 7 will be set.
*/
struct perf_pmu_format {
+ /** @list: Element on list within struct perf_pmu. */
+ struct list_head list;
+ /** @bits: Which config bits are set by this format value. */
+ DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
/** @name: The modifier/file name. */
char *name;
/**
@@ -47,18 +107,81 @@ struct perf_pmu_format {
* are from PERF_PMU_FORMAT_VALUE_CONFIG to
* PERF_PMU_FORMAT_VALUE_CONFIG_END.
*/
- int value;
- /** @bits: Which config bits are set by this format value. */
- DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS);
- /** @list: Element on list within struct perf_pmu. */
- struct list_head list;
+ u16 value;
+ /** @loaded: Has the contents been loaded/parsed. */
+ bool loaded;
};
+static int pmu_aliases_parse(struct perf_pmu *pmu);
+
+static struct perf_pmu_format *perf_pmu__new_format(struct list_head *list, char *name)
+{
+ struct perf_pmu_format *format;
+
+ format = zalloc(sizeof(*format));
+ if (!format)
+ return NULL;
+
+ format->name = strdup(name);
+ if (!format->name) {
+ free(format);
+ return NULL;
+ }
+ list_add_tail(&format->list, list);
+ return format;
+}
+
+/* Called at the end of parsing a format. */
+void perf_pmu_format__set_value(void *vformat, int config, unsigned long *bits)
+{
+ struct perf_pmu_format *format = vformat;
+
+ format->value = config;
+ memcpy(format->bits, bits, sizeof(format->bits));
+}
+
+static void __perf_pmu_format__load(struct perf_pmu_format *format, FILE *file)
+{
+ void *scanner;
+ int ret;
+
+ ret = perf_pmu_lex_init(&scanner);
+ if (ret)
+ return;
+
+ perf_pmu_set_in(file, scanner);
+ ret = perf_pmu_parse(format, scanner);
+ perf_pmu_lex_destroy(scanner);
+ format->loaded = true;
+}
+
+static void perf_pmu_format__load(struct perf_pmu *pmu, struct perf_pmu_format *format)
+{
+ char path[PATH_MAX];
+ FILE *file = NULL;
+
+ if (format->loaded)
+ return;
+
+ if (!perf_pmu__pathname_scnprintf(path, sizeof(path), pmu->name, "format"))
+ return;
+
+ assert(strlen(path) + strlen(format->name) + 2 < sizeof(path));
+ strcat(path, "/");
+ strcat(path, format->name);
+
+ file = fopen(path, "r");
+ if (!file)
+ return;
+ __perf_pmu_format__load(format, file);
+ fclose(file);
+}
+
/*
* Parse & process all the sysfs attributes located under
* the directory specified in 'dir' parameter.
*/
-int perf_pmu__format_parse(int dirfd, struct list_head *head)
+int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load)
{
struct dirent *evt_ent;
DIR *format_dir;
@@ -68,37 +191,35 @@ int perf_pmu__format_parse(int dirfd, struct list_head *head)
if (!format_dir)
return -EINVAL;
- while (!ret && (evt_ent = readdir(format_dir))) {
+ while ((evt_ent = readdir(format_dir)) != NULL) {
+ struct perf_pmu_format *format;
char *name = evt_ent->d_name;
- int fd;
- void *scanner;
- FILE *file;
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
-
- ret = -EINVAL;
- fd = openat(dirfd, name, O_RDONLY);
- if (fd < 0)
- break;
-
- file = fdopen(fd, "r");
- if (!file) {
- close(fd);
+ format = perf_pmu__new_format(&pmu->format, name);
+ if (!format) {
+ ret = -ENOMEM;
break;
}
- ret = perf_pmu_lex_init(&scanner);
- if (ret) {
+ if (eager_load) {
+ FILE *file;
+ int fd = openat(dirfd, name, O_RDONLY);
+
+ if (fd < 0) {
+ ret = -errno;
+ break;
+ }
+ file = fdopen(fd, "r");
+ if (!file) {
+ close(fd);
+ break;
+ }
+ __perf_pmu_format__load(format, file);
fclose(file);
- break;
}
-
- perf_pmu_set_in(file, scanner);
- ret = perf_pmu_parse(head, name, scanner);
- perf_pmu_lex_destroy(scanner);
- fclose(file);
}
closedir(format_dir);
@@ -110,7 +231,7 @@ int perf_pmu__format_parse(int dirfd, struct list_head *head)
* located at:
* /sys/bus/event_source/devices/<dev>/format as sysfs group attributes.
*/
-static int pmu_format(int dirfd, const char *name, struct list_head *format)
+static int pmu_format(struct perf_pmu *pmu, int dirfd, const char *name)
{
int fd;
@@ -119,7 +240,7 @@ static int pmu_format(int dirfd, const char *name, struct list_head *format)
return 0;
/* it'll close the fd */
- if (perf_pmu__format_parse(fd, format))
+ if (perf_pmu__format_parse(pmu, fd, /*eager_load=*/false))
return -1;
return 0;
@@ -162,17 +283,21 @@ out:
return ret;
}
-static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, int dirfd, char *name)
+static int perf_pmu__parse_scale(struct perf_pmu *pmu, struct perf_pmu_alias *alias)
{
struct stat st;
ssize_t sret;
+ size_t len;
char scale[128];
int fd, ret = -1;
char path[PATH_MAX];
- scnprintf(path, PATH_MAX, "%s.scale", name);
+ len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path));
+ if (!len)
+ return 0;
+ scnprintf(path + len, sizeof(path) - len, "%s/%s.scale", pmu->name, alias->name);
- fd = openat(dirfd, path, O_RDONLY);
+ fd = open(path, O_RDONLY);
if (fd == -1)
return -1;
@@ -194,15 +319,20 @@ error:
return ret;
}
-static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, int dirfd, char *name)
+static int perf_pmu__parse_unit(struct perf_pmu *pmu, struct perf_pmu_alias *alias)
{
char path[PATH_MAX];
+ size_t len;
ssize_t sret;
int fd;
- scnprintf(path, PATH_MAX, "%s.unit", name);
- fd = openat(dirfd, path, O_RDONLY);
+ len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path));
+ if (!len)
+ return 0;
+ scnprintf(path + len, sizeof(path) - len, "%s/%s.unit", pmu->name, alias->name);
+
+ fd = open(path, O_RDONLY);
if (fd == -1)
return -1;
@@ -225,14 +355,18 @@ error:
}
static int
-perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, int dirfd, char *name)
+perf_pmu__parse_per_pkg(struct perf_pmu *pmu, struct perf_pmu_alias *alias)
{
char path[PATH_MAX];
+ size_t len;
int fd;
- scnprintf(path, PATH_MAX, "%s.per-pkg", name);
+ len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path));
+ if (!len)
+ return 0;
+ scnprintf(path + len, sizeof(path) - len, "%s/%s.per-pkg", pmu->name, alias->name);
- fd = openat(dirfd, path, O_RDONLY);
+ fd = open(path, O_RDONLY);
if (fd == -1)
return -1;
@@ -242,15 +376,18 @@ perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, int dirfd, char *name)
return 0;
}
-static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias,
- int dirfd, char *name)
+static int perf_pmu__parse_snapshot(struct perf_pmu *pmu, struct perf_pmu_alias *alias)
{
char path[PATH_MAX];
+ size_t len;
int fd;
- scnprintf(path, PATH_MAX, "%s.snapshot", name);
+ len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path));
+ if (!len)
+ return 0;
+ scnprintf(path + len, sizeof(path) - len, "%s/%s.snapshot", pmu->name, alias->name);
- fd = openat(dirfd, path, O_RDONLY);
+ fd = open(path, O_RDONLY);
if (fd == -1)
return -1;
@@ -259,46 +396,13 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias,
return 0;
}
-static void perf_pmu_assign_str(char *name, const char *field, char **old_str,
- char **new_str)
-{
- if (!*old_str)
- goto set_new;
-
- if (*new_str) { /* Have new string, check with old */
- if (strcasecmp(*old_str, *new_str))
- pr_debug("alias %s differs in field '%s'\n",
- name, field);
- zfree(old_str);
- } else /* Nothing new --> keep old string */
- return;
-set_new:
- *old_str = *new_str;
- *new_str = NULL;
-}
-
-static void perf_pmu_update_alias(struct perf_pmu_alias *old,
- struct perf_pmu_alias *newalias)
-{
- perf_pmu_assign_str(old->name, "desc", &old->desc, &newalias->desc);
- perf_pmu_assign_str(old->name, "long_desc", &old->long_desc,
- &newalias->long_desc);
- perf_pmu_assign_str(old->name, "topic", &old->topic, &newalias->topic);
- perf_pmu_assign_str(old->name, "value", &old->str, &newalias->str);
- old->scale = newalias->scale;
- old->per_pkg = newalias->per_pkg;
- old->snapshot = newalias->snapshot;
- memcpy(old->unit, newalias->unit, sizeof(old->unit));
-}
-
/* Delete an alias entry. */
-void perf_pmu_free_alias(struct perf_pmu_alias *newalias)
+static void perf_pmu_free_alias(struct perf_pmu_alias *newalias)
{
zfree(&newalias->name);
zfree(&newalias->desc);
zfree(&newalias->long_desc);
zfree(&newalias->topic);
- zfree(&newalias->str);
zfree(&newalias->pmu_name);
parse_events_terms__purge(&newalias->terms);
free(newalias);
@@ -314,38 +418,99 @@ static void perf_pmu__del_aliases(struct perf_pmu *pmu)
}
}
-/* Merge an alias, search in alias list. If this name is already
- * present merge both of them to combine all information.
- */
-static bool perf_pmu_merge_alias(struct perf_pmu_alias *newalias,
- struct list_head *alist)
+static struct perf_pmu_alias *perf_pmu__find_alias(struct perf_pmu *pmu,
+ const char *name,
+ bool load)
{
- struct perf_pmu_alias *a;
+ struct perf_pmu_alias *alias;
- list_for_each_entry(a, alist, list) {
- if (!strcasecmp(newalias->name, a->name)) {
- if (newalias->pmu_name && a->pmu_name &&
- !strcasecmp(newalias->pmu_name, a->pmu_name)) {
- continue;
- }
- perf_pmu_update_alias(a, newalias);
- perf_pmu_free_alias(newalias);
- return true;
- }
+ if (load && !pmu->sysfs_aliases_loaded)
+ pmu_aliases_parse(pmu);
+
+ list_for_each_entry(alias, &pmu->aliases, list) {
+ if (!strcasecmp(alias->name, name))
+ return alias;
}
- return false;
+ return NULL;
}
-static int __perf_pmu__new_alias(struct list_head *list, int dirfd, char *name,
- char *desc, char *val, const struct pmu_event *pe)
+static bool assign_str(const char *name, const char *field, char **old_str,
+ const char *new_str)
+{
+ if (!*old_str && new_str) {
+ *old_str = strdup(new_str);
+ return true;
+ }
+
+ if (!new_str || !strcasecmp(*old_str, new_str))
+ return false; /* Nothing to update. */
+
+ pr_debug("alias %s differs in field '%s' ('%s' != '%s')\n",
+ name, field, *old_str, new_str);
+ zfree(old_str);
+ *old_str = strdup(new_str);
+ return true;
+}
+
+static void read_alias_info(struct perf_pmu *pmu, struct perf_pmu_alias *alias)
+{
+ if (!alias->from_sysfs || alias->info_loaded)
+ return;
+
+ /*
+ * load unit name and scale if available
+ */
+ perf_pmu__parse_unit(pmu, alias);
+ perf_pmu__parse_scale(pmu, alias);
+ perf_pmu__parse_per_pkg(pmu, alias);
+ perf_pmu__parse_snapshot(pmu, alias);
+}
+
+struct update_alias_data {
+ struct perf_pmu *pmu;
+ struct perf_pmu_alias *alias;
+};
+
+static int update_alias(const struct pmu_event *pe,
+ const struct pmu_events_table *table __maybe_unused,
+ void *vdata)
+{
+ struct update_alias_data *data = vdata;
+ int ret = 0;
+
+ read_alias_info(data->pmu, data->alias);
+ assign_str(pe->name, "desc", &data->alias->desc, pe->desc);
+ assign_str(pe->name, "long_desc", &data->alias->long_desc, pe->long_desc);
+ assign_str(pe->name, "topic", &data->alias->topic, pe->topic);
+ data->alias->per_pkg = pe->perpkg;
+ if (pe->event) {
+ parse_events_terms__purge(&data->alias->terms);
+ ret = parse_events_terms(&data->alias->terms, pe->event, /*input=*/NULL);
+ }
+ if (!ret && pe->unit) {
+ char *unit;
+
+ ret = perf_pmu__convert_scale(pe->unit, &unit, &data->alias->scale);
+ if (!ret)
+ snprintf(data->alias->unit, sizeof(data->alias->unit), "%s", unit);
+ }
+ return ret;
+}
+
+static int perf_pmu__new_alias(struct perf_pmu *pmu, const char *name,
+ const char *desc, const char *val, FILE *val_fd,
+ const struct pmu_event *pe)
{
- struct parse_events_term *term;
struct perf_pmu_alias *alias;
int ret;
- char newval[256];
const char *long_desc = NULL, *topic = NULL, *unit = NULL, *pmu_name = NULL;
bool deprecated = false, perpkg = false;
+ if (perf_pmu__find_alias(pmu, name, /*load=*/ false)) {
+ /* Alias was already created/loaded. */
+ return 0;
+ }
+
if (pe) {
long_desc = pe->long_desc;
topic = pe->topic;
@@ -355,7 +520,7 @@ static int __perf_pmu__new_alias(struct list_head *list, int dirfd, char *name,
pmu_name = pe->pmu;
}
- alias = malloc(sizeof(*alias));
+ alias = zalloc(sizeof(*alias));
if (!alias)
return -ENOMEM;
@@ -366,80 +531,49 @@ static int __perf_pmu__new_alias(struct list_head *list, int dirfd, char *name,
alias->snapshot = false;
alias->deprecated = deprecated;
- ret = parse_events_terms(&alias->terms, val);
+ ret = parse_events_terms(&alias->terms, val, val_fd);
if (ret) {
pr_err("Cannot parse alias %s: %d\n", val, ret);
free(alias);
return ret;
}
- /* Scan event and remove leading zeroes, spaces, newlines, some
- * platforms have terms specified as
- * event=0x0091 (read from files ../<PMU>/events/<FILE>
- * and terms specified as event=0x91 (read from JSON files).
- *
- * Rebuild string to make alias->str member comparable.
- */
- memset(newval, 0, sizeof(newval));
- ret = 0;
- list_for_each_entry(term, &alias->terms, list) {
- if (ret)
- ret += scnprintf(newval + ret, sizeof(newval) - ret,
- ",");
- if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM)
- ret += scnprintf(newval + ret, sizeof(newval) - ret,
- "%s=%#x", term->config, term->val.num);
- else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
- ret += scnprintf(newval + ret, sizeof(newval) - ret,
- "%s=%s", term->config, term->val.str);
- }
-
alias->name = strdup(name);
- if (dirfd >= 0) {
- /*
- * load unit name and scale if available
- */
- perf_pmu__parse_unit(alias, dirfd, name);
- perf_pmu__parse_scale(alias, dirfd, name);
- perf_pmu__parse_per_pkg(alias, dirfd, name);
- perf_pmu__parse_snapshot(alias, dirfd, name);
- }
-
alias->desc = desc ? strdup(desc) : NULL;
alias->long_desc = long_desc ? strdup(long_desc) :
desc ? strdup(desc) : NULL;
alias->topic = topic ? strdup(topic) : NULL;
+ alias->pmu_name = pmu_name ? strdup(pmu_name) : NULL;
if (unit) {
- if (perf_pmu__convert_scale(unit, (char **)&unit, &alias->scale) < 0)
+ if (perf_pmu__convert_scale(unit, (char **)&unit, &alias->scale) < 0) {
+ perf_pmu_free_alias(alias);
return -1;
+ }
snprintf(alias->unit, sizeof(alias->unit), "%s", unit);
}
- alias->str = strdup(newval);
- alias->pmu_name = pmu_name ? strdup(pmu_name) : NULL;
-
- if (!perf_pmu_merge_alias(alias, list))
- list_add_tail(&alias->list, list);
+ if (!pe) {
+ /* Update an event from sysfs with json data. */
+ struct update_alias_data data = {
+ .pmu = pmu,
+ .alias = alias,
+ };
+
+ alias->from_sysfs = true;
+ if (pmu->events_table) {
+ if (pmu_events_table__find_event(pmu->events_table, pmu, name,
+ update_alias, &data) == 0)
+ pmu->loaded_json_aliases++;
+ }
+ }
+ if (!pe)
+ pmu->sysfs_aliases++;
+ else
+ pmu->loaded_json_aliases++;
+ list_add_tail(&alias->list, &pmu->aliases);
return 0;
}
-static int perf_pmu__new_alias(struct list_head *list, int dirfd, char *name, FILE *file)
-{
- char buf[256];
- int ret;
-
- ret = fread(buf, 1, sizeof(buf), file);
- if (ret == 0)
- return -EINVAL;
-
- buf[ret] = 0;
-
- /* Remove trailing newline from sysfs file */
- strim(buf);
-
- return __perf_pmu__new_alias(list, dirfd, name, NULL, buf, NULL);
-}
-
static inline bool pmu_alias_info_file(char *name)
{
size_t len;
@@ -458,18 +592,33 @@ static inline bool pmu_alias_info_file(char *name)
}
/*
- * Process all the sysfs attributes located under the directory
- * specified in 'dir' parameter.
+ * Reading the pmu event aliases definition, which should be located at:
+ * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes.
*/
-static int pmu_aliases_parse(int dirfd, struct list_head *head)
+static int pmu_aliases_parse(struct perf_pmu *pmu)
{
+ char path[PATH_MAX];
struct dirent *evt_ent;
DIR *event_dir;
- int fd;
+ size_t len;
+ int fd, dir_fd;
- event_dir = fdopendir(dirfd);
- if (!event_dir)
+ len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path));
+ if (!len)
+ return 0;
+ scnprintf(path + len, sizeof(path) - len, "%s/events", pmu->name);
+
+ dir_fd = open(path, O_DIRECTORY);
+ if (dir_fd == -1) {
+ pmu->sysfs_aliases_loaded = true;
+ return 0;
+ }
+
+ event_dir = fdopendir(dir_fd);
+ if (!event_dir){
+ close (dir_fd);
return -EINVAL;
+ }
while ((evt_ent = readdir(event_dir))) {
char *name = evt_ent->d_name;
@@ -484,7 +633,7 @@ static int pmu_aliases_parse(int dirfd, struct list_head *head)
if (pmu_alias_info_file(name))
continue;
- fd = openat(dirfd, name, O_RDONLY);
+ fd = openat(dir_fd, name, O_RDONLY);
if (fd == -1) {
pr_debug("Cannot open %s\n", name);
continue;
@@ -495,31 +644,15 @@ static int pmu_aliases_parse(int dirfd, struct list_head *head)
continue;
}
- if (perf_pmu__new_alias(head, dirfd, name, file) < 0)
+ if (perf_pmu__new_alias(pmu, name, /*desc=*/ NULL,
+ /*val=*/ NULL, file, /*pe=*/ NULL) < 0)
pr_debug("Cannot set up %s\n", name);
fclose(file);
}
closedir(event_dir);
- return 0;
-}
-
-/*
- * Reading the pmu event aliases definition, which should be located at:
- * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes.
- */
-static int pmu_aliases(int dirfd, const char *name, struct list_head *head)
-{
- int fd;
-
- fd = perf_pmu__pathname_fd(dirfd, name, "events", O_DIRECTORY);
- if (fd < 0)
- return 0;
-
- /* it'll close the fd */
- if (pmu_aliases_parse(fd, head))
- return -1;
-
+ close (dir_fd);
+ pmu->sysfs_aliases_loaded = true;
return 0;
}
@@ -741,28 +874,13 @@ out:
return res;
}
-struct pmu_add_cpu_aliases_map_data {
- /* List being added to. */
- struct list_head *head;
- /* If a pmu_event lacks a given PMU the default used. */
- char *default_pmu_name;
- /* The PMU that we're searching for events for. */
- 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)
{
- struct pmu_add_cpu_aliases_map_data *data = vdata;
- const char *pname = pe->pmu ?: data->default_pmu_name;
+ struct perf_pmu *pmu = vdata;
- if (!strcmp(pname, data->pmu->name) ||
- (data->pmu->is_uncore && pmu_uncore_alias_match(pname, data->pmu->name))) {
- /* need type casts to override 'const' */
- __perf_pmu__new_alias(data->head, -1, (char *)pe->name, (char *)pe->desc,
- (char *)pe->event, pe);
- }
+ perf_pmu__new_alias(pmu, pe->name, pe->desc, pe->event, /*val_fd=*/ NULL, pe);
return 0;
}
@@ -770,68 +888,51 @@ static int pmu_add_cpu_aliases_map_callback(const struct pmu_event *pe,
* From the pmu_events_table, find the events that correspond to the given
* PMU and add them to the list 'head'.
*/
-void pmu_add_cpu_aliases_table(struct list_head *head, struct perf_pmu *pmu,
- const struct pmu_events_table *table)
+void pmu_add_cpu_aliases_table(struct perf_pmu *pmu, const struct pmu_events_table *table)
{
- struct pmu_add_cpu_aliases_map_data data = {
- .head = head,
- .default_pmu_name = perf_pmus__default_pmu_name(),
- .pmu = pmu,
- };
-
- pmu_events_table_for_each_event(table, pmu_add_cpu_aliases_map_callback, &data);
- free(data.default_pmu_name);
+ pmu_events_table__for_each_event(table, pmu, pmu_add_cpu_aliases_map_callback, pmu);
}
-static void pmu_add_cpu_aliases(struct list_head *head, struct perf_pmu *pmu)
+static void pmu_add_cpu_aliases(struct perf_pmu *pmu)
{
- const struct pmu_events_table *table;
+ if (!pmu->events_table)
+ return;
- table = perf_pmu__find_events_table(pmu);
- if (!table)
+ if (pmu->cpu_aliases_added)
return;
- pmu_add_cpu_aliases_table(head, pmu, table);
+ pmu_add_cpu_aliases_table(pmu, pmu->events_table);
+ pmu->cpu_aliases_added = true;
}
-struct pmu_sys_event_iter_data {
- struct list_head *head;
- struct perf_pmu *pmu;
-};
-
static int pmu_add_sys_aliases_iter_fn(const struct pmu_event *pe,
const struct pmu_events_table *table __maybe_unused,
- void *data)
+ void *vdata)
{
- struct pmu_sys_event_iter_data *idata = data;
- struct perf_pmu *pmu = idata->pmu;
+ struct perf_pmu *pmu = vdata;
if (!pe->compat || !pe->pmu)
return 0;
if (!strcmp(pmu->id, pe->compat) &&
pmu_uncore_alias_match(pe->pmu, pmu->name)) {
- __perf_pmu__new_alias(idata->head, -1,
- (char *)pe->name,
- (char *)pe->desc,
- (char *)pe->event,
- pe);
+ perf_pmu__new_alias(pmu,
+ pe->name,
+ pe->desc,
+ pe->event,
+ /*val_fd=*/ NULL,
+ pe);
}
return 0;
}
-void pmu_add_sys_aliases(struct list_head *head, struct perf_pmu *pmu)
+void pmu_add_sys_aliases(struct perf_pmu *pmu)
{
- struct pmu_sys_event_iter_data idata = {
- .head = head,
- .pmu = pmu,
- };
-
if (!pmu->id)
return;
- pmu_for_each_sys_event(pmu_add_sys_aliases_iter_fn, &idata);
+ pmu_for_each_sys_event(pmu_add_sys_aliases_iter_fn, pmu);
}
struct perf_event_attr * __weak
@@ -840,13 +941,13 @@ perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
return NULL;
}
-char * __weak
+const char * __weak
pmu_find_real_name(const char *name)
{
- return (char *)name;
+ return name;
}
-char * __weak
+const char * __weak
pmu_find_alias_name(const char *name __maybe_unused)
{
return NULL;
@@ -863,40 +964,41 @@ static int pmu_max_precise(int dirfd, struct perf_pmu *pmu)
struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *lookup_name)
{
struct perf_pmu *pmu;
- LIST_HEAD(format);
- LIST_HEAD(aliases);
__u32 type;
- char *name = pmu_find_real_name(lookup_name);
- char *alias_name;
-
- /*
- * The pmu data we store & need consists of the pmu
- * type value and format definitions. Load both right
- * now.
- */
- if (pmu_format(dirfd, name, &format))
- return NULL;
-
- /*
- * Check the aliases first to avoid unnecessary work.
- */
- if (pmu_aliases(dirfd, name, &aliases))
- return NULL;
+ const char *name = pmu_find_real_name(lookup_name);
+ const char *alias_name;
pmu = zalloc(sizeof(*pmu));
if (!pmu)
return NULL;
- pmu->is_core = is_pmu_core(name);
- pmu->cpus = pmu_cpumask(dirfd, name, pmu->is_core);
pmu->name = strdup(name);
if (!pmu->name)
goto err;
- /* Read type, and ensure that type value is successfully assigned (return 1) */
+ /*
+ * Read type early to fail fast if a lookup name isn't a PMU. Ensure
+ * that type value is successfully assigned (return 1).
+ */
if (perf_pmu__scan_file_at(pmu, dirfd, "type", "%u", &type) != 1)
goto err;
+ INIT_LIST_HEAD(&pmu->format);
+ INIT_LIST_HEAD(&pmu->aliases);
+ INIT_LIST_HEAD(&pmu->caps);
+
+ /*
+ * The pmu data we store & need consists of the pmu
+ * type value and format definitions. Load both right
+ * now.
+ */
+ if (pmu_format(pmu, dirfd, name)) {
+ free(pmu);
+ return NULL;
+ }
+ pmu->is_core = is_pmu_core(name);
+ pmu->cpus = pmu_cpumask(dirfd, name, pmu->is_core);
+
alias_name = pmu_find_alias_name(name);
if (alias_name) {
pmu->alias_name = strdup(alias_name);
@@ -909,14 +1011,8 @@ struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char
if (pmu->is_uncore)
pmu->id = pmu_id(name);
pmu->max_precise = pmu_max_precise(dirfd, pmu);
- pmu_add_cpu_aliases(&aliases, pmu);
- pmu_add_sys_aliases(&aliases, pmu);
-
- INIT_LIST_HEAD(&pmu->format);
- INIT_LIST_HEAD(&pmu->aliases);
- INIT_LIST_HEAD(&pmu->caps);
- list_splice(&format, &pmu->format);
- list_splice(&aliases, &pmu->aliases);
+ pmu->events_table = perf_pmu__find_events_table(pmu);
+ pmu_add_sys_aliases(pmu);
list_add_tail(&pmu->list, pmus);
pmu->default_config = perf_pmu__get_default_config(pmu);
@@ -966,13 +1062,15 @@ void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu)
if (pmu == &perf_pmu__fake)
return;
- list_for_each_entry(format, &pmu->format, list)
+ list_for_each_entry(format, &pmu->format, list) {
+ perf_pmu_format__load(pmu, format);
if (format->value >= PERF_PMU_FORMAT_VALUE_CONFIG_END) {
pr_warning("WARNING: '%s' format '%s' requires 'perf_event_attr::config%d'"
"which is not supported by this version of perf!\n",
pmu->name, format->name, format->value);
return;
}
+ }
}
bool evsel__is_aux_event(const struct evsel *evsel)
@@ -1000,7 +1098,7 @@ void evsel__set_config_if_unset(struct perf_pmu *pmu, struct evsel *evsel,
if (term)
user_bits = term->val.cfg_chg;
- bits = perf_pmu__format_bits(&pmu->format, config_name);
+ bits = perf_pmu__format_bits(pmu, config_name);
/* Do nothing if the user changed the value */
if (bits & user_bits)
@@ -1023,9 +1121,9 @@ pmu_find_format(struct list_head *formats, const char *name)
return NULL;
}
-__u64 perf_pmu__format_bits(struct list_head *formats, const char *name)
+__u64 perf_pmu__format_bits(struct perf_pmu *pmu, const char *name)
{
- struct perf_pmu_format *format = pmu_find_format(formats, name);
+ struct perf_pmu_format *format = pmu_find_format(&pmu->format, name);
__u64 bits = 0;
int fbit;
@@ -1038,13 +1136,14 @@ __u64 perf_pmu__format_bits(struct list_head *formats, const char *name)
return bits;
}
-int perf_pmu__format_type(struct list_head *formats, const char *name)
+int perf_pmu__format_type(struct perf_pmu *pmu, const char *name)
{
- struct perf_pmu_format *format = pmu_find_format(formats, name);
+ struct perf_pmu_format *format = pmu_find_format(&pmu->format, name);
if (!format)
return -1;
+ perf_pmu_format__load(pmu, format);
return format->value;
}
@@ -1135,8 +1234,7 @@ error:
* Setup one of config[12] attr members based on the
* user input data - term parameter.
*/
-static int pmu_config_term(const char *pmu_name,
- struct list_head *formats,
+static int pmu_config_term(struct perf_pmu *pmu,
struct perf_event_attr *attr,
struct parse_events_term *term,
struct list_head *head_terms,
@@ -1160,15 +1258,15 @@ static int pmu_config_term(const char *pmu_name,
if (parse_events__is_hardcoded_term(term))
return 0;
- format = pmu_find_format(formats, term->config);
+ format = pmu_find_format(&pmu->format, term->config);
if (!format) {
- char *pmu_term = pmu_formats_string(formats);
+ char *pmu_term = pmu_formats_string(&pmu->format);
char *unknown_term;
char *help_msg;
if (asprintf(&unknown_term,
"unknown term '%s' for pmu '%s'",
- term->config, pmu_name) < 0)
+ term->config, pmu->name) < 0)
unknown_term = NULL;
help_msg = parse_events_formats_error_string(pmu_term);
if (err) {
@@ -1182,7 +1280,7 @@ static int pmu_config_term(const char *pmu_name,
free(pmu_term);
return -EINVAL;
}
-
+ perf_pmu_format__load(pmu, format);
switch (format->value) {
case PERF_PMU_FORMAT_VALUE_CONFIG:
vp = &attr->config;
@@ -1259,7 +1357,7 @@ static int pmu_config_term(const char *pmu_name,
return 0;
}
-int perf_pmu__config_terms(const char *pmu_name, struct list_head *formats,
+int perf_pmu__config_terms(struct perf_pmu *pmu,
struct perf_event_attr *attr,
struct list_head *head_terms,
bool zero, struct parse_events_error *err)
@@ -1267,8 +1365,7 @@ int perf_pmu__config_terms(const char *pmu_name, struct list_head *formats,
struct parse_events_term *term;
list_for_each_entry(term, head_terms, list) {
- if (pmu_config_term(pmu_name, formats, attr, term, head_terms,
- zero, err))
+ if (pmu_config_term(pmu, attr, term, head_terms, zero, err))
return -EINVAL;
}
@@ -1286,25 +1383,25 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
{
bool zero = !!pmu->default_config;
- return perf_pmu__config_terms(pmu->name, &pmu->format, attr,
- head_terms, zero, err);
+ return perf_pmu__config_terms(pmu, attr, head_terms, zero, err);
}
static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
struct parse_events_term *term)
{
struct perf_pmu_alias *alias;
- char *name;
+ const char *name;
if (parse_events__is_hardcoded_term(term))
return NULL;
if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) {
- if (term->val.num != 1)
+ if (!term->no_value)
return NULL;
if (pmu_find_format(&pmu->format, term->config))
return NULL;
name = term->config;
+
} else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) {
if (strcasecmp(term->config, "event"))
return NULL;
@@ -1313,26 +1410,51 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
return NULL;
}
- list_for_each_entry(alias, &pmu->aliases, list) {
- if (!strcasecmp(alias->name, name))
- return alias;
+ alias = perf_pmu__find_alias(pmu, name, /*load=*/ true);
+ if (alias || pmu->cpu_aliases_added)
+ return alias;
+
+ /* Alias doesn't exist, try to get it from the json events. */
+ if (pmu->events_table &&
+ pmu_events_table__find_event(pmu->events_table, pmu, name,
+ pmu_add_cpu_aliases_map_callback,
+ pmu) == 0) {
+ alias = perf_pmu__find_alias(pmu, name, /*load=*/ false);
}
- return NULL;
+ return alias;
}
-static int check_info_data(struct perf_pmu_alias *alias,
- struct perf_pmu_info *info)
+static int check_info_data(struct perf_pmu *pmu,
+ struct perf_pmu_alias *alias,
+ struct perf_pmu_info *info,
+ struct parse_events_error *err,
+ int column)
{
+ read_alias_info(pmu, alias);
/*
* Only one term in event definition can
* define unit, scale and snapshot, fail
* if there's more than one.
*/
- if ((info->unit && alias->unit[0]) ||
- (info->scale && alias->scale) ||
- (info->snapshot && alias->snapshot))
+ if (info->unit && alias->unit[0]) {
+ parse_events_error__handle(err, column,
+ strdup("Attempt to set event's unit twice"),
+ NULL);
return -EINVAL;
+ }
+ if (info->scale && alias->scale) {
+ parse_events_error__handle(err, column,
+ strdup("Attempt to set event's scale twice"),
+ NULL);
+ return -EINVAL;
+ }
+ if (info->snapshot && alias->snapshot) {
+ parse_events_error__handle(err, column,
+ strdup("Attempt to set event snapshot twice"),
+ NULL);
+ return -EINVAL;
+ }
if (alias->unit[0])
info->unit = alias->unit;
@@ -1351,7 +1473,7 @@ static int check_info_data(struct perf_pmu_alias *alias,
* defined for the alias
*/
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
- struct perf_pmu_info *info)
+ struct perf_pmu_info *info, struct parse_events_error *err)
{
struct parse_events_term *term, *h;
struct perf_pmu_alias *alias;
@@ -1372,10 +1494,14 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
if (!alias)
continue;
ret = pmu_alias_terms(alias, &term->list);
- if (ret)
+ if (ret) {
+ parse_events_error__handle(err, term->err_term,
+ strdup("Failure to duplicate terms"),
+ NULL);
return ret;
+ }
- ret = check_info_data(alias, info);
+ ret = check_info_data(pmu, alias, info, err, term->err_term);
if (ret)
return ret;
@@ -1400,36 +1526,36 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
return 0;
}
-int perf_pmu__new_format(struct list_head *list, char *name,
- int config, unsigned long *bits)
-{
- struct perf_pmu_format *format;
+struct find_event_args {
+ const char *event;
+ void *state;
+ pmu_event_callback cb;
+};
- format = zalloc(sizeof(*format));
- if (!format)
- return -ENOMEM;
+static int find_event_callback(void *state, struct pmu_event_info *info)
+{
+ struct find_event_args *args = state;
- format->name = strdup(name);
- format->value = config;
- memcpy(format->bits, bits, sizeof(format->bits));
+ if (!strcmp(args->event, info->name))
+ return args->cb(args->state, info);
- list_add_tail(&format->list, list);
return 0;
}
-void perf_pmu__set_format(unsigned long *bits, long from, long to)
+int perf_pmu__find_event(struct perf_pmu *pmu, const char *event, void *state, pmu_event_callback cb)
{
- long b;
-
- if (!to)
- to = from;
+ struct find_event_args args = {
+ .event = event,
+ .state = state,
+ .cb = cb,
+ };
- memset(bits, 0, BITS_TO_BYTES(PERF_PMU_FORMAT_BITS));
- for (b = from; b <= to; b++)
- __set_bit(b, bits);
+ /* Sub-optimal, but function is only used by tests. */
+ return perf_pmu__for_each_event(pmu, /*skip_duplicate_pmus=*/ false,
+ &args, find_event_callback);
}
-void perf_pmu__del_formats(struct list_head *formats)
+static void perf_pmu__del_formats(struct list_head *formats)
{
struct perf_pmu_format *fmt, *tmp;
@@ -1466,15 +1592,145 @@ bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu)
return !pmu->is_core || perf_pmus__num_core_pmus() == 1;
}
-bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name)
+bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name)
{
- struct perf_pmu_alias *alias;
+ if (perf_pmu__find_alias(pmu, name, /*load=*/ true) != NULL)
+ return true;
+ if (pmu->cpu_aliases_added || !pmu->events_table)
+ return false;
+ return pmu_events_table__find_event(pmu->events_table, pmu, name, NULL, NULL) == 0;
+}
- list_for_each_entry(alias, &pmu->aliases, list) {
- if (!strcmp(alias->name, name))
- return true;
+size_t perf_pmu__num_events(struct perf_pmu *pmu)
+{
+ size_t nr;
+
+ if (!pmu->sysfs_aliases_loaded)
+ pmu_aliases_parse(pmu);
+
+ nr = pmu->sysfs_aliases;
+
+ if (pmu->cpu_aliases_added)
+ nr += pmu->loaded_json_aliases;
+ else if (pmu->events_table)
+ nr += pmu_events_table__num_events(pmu->events_table, pmu) - pmu->loaded_json_aliases;
+
+ return pmu->selectable ? nr + 1 : nr;
+}
+
+static int sub_non_neg(int a, int b)
+{
+ if (b > a)
+ return 0;
+ return a - b;
+}
+
+static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
+ const struct perf_pmu_alias *alias, bool skip_duplicate_pmus)
+{
+ struct parse_events_term *term;
+ int pmu_name_len = skip_duplicate_pmus
+ ? pmu_name_len_no_suffix(pmu->name, /*num=*/NULL)
+ : (int)strlen(pmu->name);
+ int used = snprintf(buf, len, "%.*s/%s", pmu_name_len, pmu->name, alias->name);
+
+ list_for_each_entry(term, &alias->terms, list) {
+ if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
+ used += snprintf(buf + used, sub_non_neg(len, used),
+ ",%s=%s", term->config,
+ term->val.str);
}
- return false;
+
+ if (sub_non_neg(len, used) > 0) {
+ buf[used] = '/';
+ used++;
+ }
+ if (sub_non_neg(len, used) > 0) {
+ buf[used] = '\0';
+ used++;
+ } else
+ buf[len - 1] = '\0';
+
+ return buf;
+}
+
+int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
+ void *state, pmu_event_callback cb)
+{
+ char buf[1024];
+ struct perf_pmu_alias *event;
+ struct pmu_event_info info = {
+ .pmu = pmu,
+ };
+ int ret = 0;
+ struct strbuf sb;
+
+ strbuf_init(&sb, /*hint=*/ 0);
+ pmu_add_cpu_aliases(pmu);
+ list_for_each_entry(event, &pmu->aliases, list) {
+ size_t buf_used;
+
+ info.pmu_name = event->pmu_name ?: pmu->name;
+ info.alias = NULL;
+ if (event->desc) {
+ info.name = event->name;
+ buf_used = 0;
+ } else {
+ info.name = format_alias(buf, sizeof(buf), pmu, event,
+ skip_duplicate_pmus);
+ if (pmu->is_core) {
+ info.alias = info.name;
+ info.name = event->name;
+ }
+ buf_used = strlen(buf) + 1;
+ }
+ info.scale_unit = NULL;
+ if (strlen(event->unit) || event->scale != 1.0) {
+ info.scale_unit = buf + buf_used;
+ buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
+ "%G%s", event->scale, event->unit) + 1;
+ }
+ info.desc = event->desc;
+ info.long_desc = event->long_desc;
+ info.encoding_desc = buf + buf_used;
+ parse_events_term__to_strbuf(&event->terms, &sb);
+ buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
+ "%s/%s/", info.pmu_name, sb.buf) + 1;
+ info.topic = event->topic;
+ info.str = sb.buf;
+ info.deprecated = event->deprecated;
+ ret = cb(state, &info);
+ if (ret)
+ goto out;
+ strbuf_setlen(&sb, /*len=*/ 0);
+ }
+ if (pmu->selectable) {
+ info.name = buf;
+ snprintf(buf, sizeof(buf), "%s//", pmu->name);
+ info.alias = NULL;
+ info.scale_unit = NULL;
+ info.desc = NULL;
+ info.long_desc = NULL;
+ info.encoding_desc = NULL;
+ info.topic = NULL;
+ info.pmu_name = pmu->name;
+ info.deprecated = false;
+ ret = cb(state, &info);
+ }
+out:
+ strbuf_release(&sb);
+ return ret;
+}
+
+bool pmu__name_match(const struct perf_pmu *pmu, const char *pmu_name)
+{
+ return !strcmp(pmu->name, pmu_name) ||
+ (pmu->is_uncore && pmu_uncore_alias_match(pmu_name, pmu->name)) ||
+ /*
+ * jevents and tests use default_core as a marker for any core
+ * PMU as the PMU name varies across architectures.
+ */
+ (pmu->is_core && !strcmp(pmu_name, "default_core"));
}
bool perf_pmu__is_software(const struct perf_pmu *pmu)
@@ -1710,7 +1966,7 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config,
name ?: "N/A", buf, config_name, config);
}
-int perf_pmu__match(char *pattern, char *name, char *tok)
+int perf_pmu__match(const char *pattern, const char *name, const char *tok)
{
if (!name)
return -1;
@@ -1756,17 +2012,19 @@ int perf_pmu__event_source_devices_fd(void)
* then pathname will be filled with
* "/sys/bus/event_source/devices/cs_etm/format"
*
- * Return 0 if the sysfs mountpoint couldn't be found or if no
- * characters were written.
+ * Return 0 if the sysfs mountpoint couldn't be found, if no characters were
+ * written or if the buffer size is exceeded.
*/
int perf_pmu__pathname_scnprintf(char *buf, size_t size,
const char *pmu_name, const char *filename)
{
- char base_path[PATH_MAX];
+ size_t len;
- if (!perf_pmu__event_source_devices_scnprintf(base_path, sizeof(base_path)))
+ len = perf_pmu__event_source_devices_scnprintf(buf, size);
+ if (!len || (len + strlen(pmu_name) + strlen(filename) + 1) >= size)
return 0;
- return scnprintf(buf, size, "%s%s/%s", base_path, pmu_name, filename);
+
+ return scnprintf(buf + len, size - len, "%s/%s", pmu_name, filename);
}
int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename, int flags)
@@ -1788,5 +2046,23 @@ void perf_pmu__delete(struct perf_pmu *pmu)
zfree(&pmu->default_config);
zfree(&pmu->name);
zfree(&pmu->alias_name);
+ zfree(&pmu->id);
free(pmu);
}
+
+struct perf_pmu *pmu__find_core_pmu(void)
+{
+ struct perf_pmu *pmu = NULL;
+
+ while ((pmu = perf_pmus__scan_core(pmu))) {
+ /*
+ * The cpumap should cover all CPUs. Otherwise, some CPUs may
+ * not support some events or have different event IDs.
+ */
+ if (RC_CHK_ACCESS(pmu->cpus)->nr != cpu__max_cpu().cpu)
+ return NULL;
+
+ return pmu;
+ }
+ return NULL;
+}
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 6b414cecbad2..6a4e170c61d6 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -39,7 +39,7 @@ struct perf_pmu_caps {
*/
struct perf_pmu {
/** @name: The name of the PMU such as "cpu". */
- char *name;
+ const char *name;
/**
* @alias_name: Optional alternate name for the PMU determined in
* architecture specific code.
@@ -49,7 +49,7 @@ struct perf_pmu {
* @id: Optional PMU identifier read from
* <sysfs>/bus/event_source/devices/<name>/identifier.
*/
- char *id;
+ const char *id;
/**
* @type: Perf event attributed type value, read from
* <sysfs>/bus/event_source/devices/<name>/type.
@@ -114,6 +114,21 @@ struct perf_pmu {
* from json events in pmu-events.c.
*/
struct list_head aliases;
+ /**
+ * @events_table: The events table for json events in pmu-events.c.
+ */
+ const struct pmu_events_table *events_table;
+ /** @sysfs_aliases: Number of sysfs aliases loaded. */
+ uint32_t sysfs_aliases;
+ /** @sysfs_aliases: Number of json event aliases loaded. */
+ uint32_t loaded_json_aliases;
+ /** @sysfs_aliases_loaded: Are sysfs aliases loaded from disk? */
+ bool sysfs_aliases_loaded;
+ /**
+ * @cpu_aliases_added: Have all json events table entries for the PMU
+ * been added?
+ */
+ bool cpu_aliases_added;
/** @caps_initialized: Has the list caps been initialized? */
bool caps_initialized;
/** @nr_caps: The length of the list caps. */
@@ -158,88 +173,49 @@ struct perf_pmu_info {
bool snapshot;
};
-#define UNIT_MAX_LEN 31 /* max length for event unit name */
-
-/**
- * struct perf_pmu_alias - An event either read from sysfs or builtin in
- * pmu-events.c, created by parsing the pmu-events json files.
- */
-struct perf_pmu_alias {
- /** @name: Name of the event like "mem-loads". */
- char *name;
- /** @desc: Optional short description of the event. */
- char *desc;
- /** @long_desc: Optional long description. */
- char *long_desc;
- /**
- * @topic: Optional topic such as cache or pipeline, particularly for
- * json events.
- */
- char *topic;
- /**
- * @str: Comma separated parameter list like
- * "event=0xcd,umask=0x1,ldlat=0x3".
- */
- char *str;
- /** @terms: Owned list of the original parsed parameters. */
- struct list_head terms;
- /** @list: List element of struct perf_pmu aliases. */
- struct list_head list;
- /** @unit: Units for the event, such as bytes or cache lines. */
- char unit[UNIT_MAX_LEN+1];
- /** @scale: Value to scale read counter values by. */
- double scale;
- /**
- * @per_pkg: Does the file
- * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.per-pkg or
- * equivalent json value exist and have the value 1.
- */
- bool per_pkg;
- /**
- * @snapshot: Does the file
- * <sysfs>/bus/event_source/devices/<pmu_name>/events/<name>.snapshot
- * exist and have the value 1.
- */
- bool snapshot;
- /**
- * @deprecated: Is the event hidden and so not shown in perf list by
- * default.
- */
+struct pmu_event_info {
+ const struct perf_pmu *pmu;
+ const char *name;
+ const char* alias;
+ const char *scale_unit;
+ const char *desc;
+ const char *long_desc;
+ const char *encoding_desc;
+ const char *topic;
+ const char *pmu_name;
+ const char *str;
bool deprecated;
- /**
- * @pmu_name: The name copied from the json struct pmu_event. This can
- * differ from the PMU name as it won't have suffixes.
- */
- char *pmu_name;
};
-void pmu_add_sys_aliases(struct list_head *head, struct perf_pmu *pmu);
+typedef int (*pmu_event_callback)(void *state, struct pmu_event_info *info);
+
+void pmu_add_sys_aliases(struct perf_pmu *pmu);
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
struct list_head *head_terms,
struct parse_events_error *error);
-int perf_pmu__config_terms(const char *pmu_name, struct list_head *formats,
+int perf_pmu__config_terms(struct perf_pmu *pmu,
struct perf_event_attr *attr,
struct list_head *head_terms,
bool zero, struct parse_events_error *error);
-__u64 perf_pmu__format_bits(struct list_head *formats, const char *name);
-int perf_pmu__format_type(struct list_head *formats, const char *name);
+__u64 perf_pmu__format_bits(struct perf_pmu *pmu, const char *name);
+int perf_pmu__format_type(struct perf_pmu *pmu, const char *name);
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
- struct perf_pmu_info *info);
-struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
- struct list_head *head_terms);
-void perf_pmu_error(struct list_head *list, char *name, void *scanner, char const *msg);
+ struct perf_pmu_info *info, struct parse_events_error *err);
+int perf_pmu__find_event(struct perf_pmu *pmu, const char *event, void *state, pmu_event_callback cb);
-int perf_pmu__new_format(struct list_head *list, char *name,
- int config, unsigned long *bits);
-void perf_pmu__set_format(unsigned long *bits, long from, long to);
-int perf_pmu__format_parse(int dirfd, struct list_head *head);
-void perf_pmu__del_formats(struct list_head *formats);
+int perf_pmu__format_parse(struct perf_pmu *pmu, int dirfd, bool eager_load);
+void perf_pmu_format__set_value(void *format, int config, unsigned long *bits);
bool perf_pmu__has_format(const struct perf_pmu *pmu, const char *name);
bool is_pmu_core(const char *name);
bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu);
bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu);
-bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name);
+bool perf_pmu__have_event(struct perf_pmu *pmu, const char *name);
+size_t perf_pmu__num_events(struct perf_pmu *pmu);
+int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
+ void *state, pmu_event_callback cb);
+bool pmu__name_match(const struct perf_pmu *pmu, const char *pmu_name);
+
/**
* perf_pmu_is_software - is the PMU a software PMU as in it uses the
* perf_sw_context in the kernel?
@@ -258,13 +234,12 @@ bool perf_pmu__file_exists(struct perf_pmu *pmu, const char *name);
int perf_pmu__test(void);
struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu);
-void pmu_add_cpu_aliases_table(struct list_head *head, struct perf_pmu *pmu,
+void pmu_add_cpu_aliases_table(struct perf_pmu *pmu,
const struct pmu_events_table *table);
char *perf_pmu__getcpuid(struct perf_pmu *pmu);
const struct pmu_events_table *pmu_events_table__find(void);
const struct pmu_metrics_table *pmu_metrics_table__find(void);
-void perf_pmu_free_alias(struct perf_pmu_alias *alias);
int perf_pmu__convert_scale(const char *scale, char **end, double *sval);
@@ -275,10 +250,10 @@ void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config,
const char *config_name);
void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu);
-int perf_pmu__match(char *pattern, char *name, char *tok);
+int perf_pmu__match(const char *pattern, const char *name, const char *tok);
-char *pmu_find_real_name(const char *name);
-char *pmu_find_alias_name(const char *name);
+const char *pmu_find_real_name(const char *name);
+const char *pmu_find_alias_name(const char *name);
double perf_pmu__cpu_slots_per_cycle(void);
int perf_pmu__event_source_devices_scnprintf(char *pathname, size_t size);
int perf_pmu__pathname_scnprintf(char *buf, size_t size,
@@ -289,5 +264,6 @@ int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename,
struct perf_pmu *perf_pmu__lookup(struct list_head *pmus, int dirfd, const char *lookup_name);
struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pmus);
void perf_pmu__delete(struct perf_pmu *pmu);
+struct perf_pmu *pmu__find_core_pmu(void);
#endif /* __PMU_H */
diff --git a/tools/perf/util/pmu.y b/tools/perf/util/pmu.y
index dff4e892ac4d..600c8c158c8e 100644
--- a/tools/perf/util/pmu.y
+++ b/tools/perf/util/pmu.y
@@ -1,6 +1,5 @@
%define api.pure full
-%parse-param {struct list_head *format}
-%parse-param {char *name}
+%parse-param {void *format}
%parse-param {void *scanner}
%lex-param {void* scanner}
@@ -11,6 +10,9 @@
#include <linux/bitmap.h>
#include <string.h>
#include "pmu.h"
+#include "pmu-bison.h"
+
+int perf_pmu_lex(YYSTYPE * yylval_param , void *yyscanner);
#define ABORT_ON(val) \
do { \
@@ -18,6 +20,20 @@ do { \
YYABORT; \
} while (0)
+static void perf_pmu_error(void *format, void *scanner, const char *msg);
+
+static void perf_pmu__set_format(unsigned long *bits, long from, long to)
+{
+ long b;
+
+ if (!to)
+ to = from;
+
+ memset(bits, 0, BITS_TO_BYTES(PERF_PMU_FORMAT_BITS));
+ for (b = from; b <= to; b++)
+ __set_bit(b, bits);
+}
+
%}
%token PP_CONFIG
@@ -42,16 +58,12 @@ format_term
format_term:
PP_CONFIG ':' bits
{
- ABORT_ON(perf_pmu__new_format(format, name,
- PERF_PMU_FORMAT_VALUE_CONFIG,
- $3));
+ perf_pmu_format__set_value(format, PERF_PMU_FORMAT_VALUE_CONFIG, $3);
}
|
PP_CONFIG PP_VALUE ':' bits
{
- ABORT_ON(perf_pmu__new_format(format, name,
- $2,
- $4));
+ perf_pmu_format__set_value(format, $2, $4);
}
bits:
@@ -78,9 +90,8 @@ PP_VALUE
%%
-void perf_pmu_error(struct list_head *list __maybe_unused,
- char *name __maybe_unused,
- void *scanner __maybe_unused,
- char const *msg __maybe_unused)
+static void perf_pmu_error(void *format __maybe_unused,
+ void *scanner __maybe_unused,
+ const char *msg __maybe_unused)
{
}
diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index c58ba9fb6a36..6631367c756f 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -1,8 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/list.h>
+#include <linux/list_sort.h>
+#include <linux/string.h>
#include <linux/zalloc.h>
#include <subcmd/pager.h>
#include <sys/types.h>
+#include <ctype.h>
#include <dirent.h>
#include <pthread.h>
#include <string.h>
@@ -33,6 +36,31 @@ static LIST_HEAD(other_pmus);
static bool read_sysfs_core_pmus;
static bool read_sysfs_all_pmus;
+int pmu_name_len_no_suffix(const char *str, unsigned long *num)
+{
+ int orig_len, len;
+
+ orig_len = len = strlen(str);
+
+ /* Non-uncore PMUs have their full length, for example, i915. */
+ if (!strstarts(str, "uncore_"))
+ return len;
+
+ /*
+ * Count trailing digits and '_', if '_{num}' suffix isn't present use
+ * the full length.
+ */
+ while (len > 0 && isdigit(str[len - 1]))
+ len--;
+
+ if (len > 0 && len != orig_len && str[len - 1] == '_') {
+ if (num)
+ *num = strtoul(&str[len], NULL, 10);
+ return len - 1;
+ }
+ return orig_len;
+}
+
void perf_pmus__destroy(void)
{
struct perf_pmu *pmu, *tmp;
@@ -122,6 +150,25 @@ static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)
return perf_pmu__lookup(core_pmu ? &core_pmus : &other_pmus, dirfd, name);
}
+static int pmus_cmp(void *priv __maybe_unused,
+ const struct list_head *lhs, const struct list_head *rhs)
+{
+ unsigned long lhs_num = 0, rhs_num = 0;
+ struct perf_pmu *lhs_pmu = container_of(lhs, struct perf_pmu, list);
+ struct perf_pmu *rhs_pmu = container_of(rhs, struct perf_pmu, list);
+ const char *lhs_pmu_name = lhs_pmu->name ?: "";
+ const char *rhs_pmu_name = rhs_pmu->name ?: "";
+ int lhs_pmu_name_len = pmu_name_len_no_suffix(lhs_pmu_name, &lhs_num);
+ int rhs_pmu_name_len = pmu_name_len_no_suffix(rhs_pmu_name, &rhs_num);
+ int ret = strncmp(lhs_pmu_name, rhs_pmu_name,
+ lhs_pmu_name_len < rhs_pmu_name_len ? lhs_pmu_name_len : rhs_pmu_name_len);
+
+ if (lhs_pmu_name_len != rhs_pmu_name_len || ret != 0 || lhs_pmu_name_len == 0)
+ return ret;
+
+ return lhs_num < rhs_num ? -1 : (lhs_num > rhs_num ? 1 : 0);
+}
+
/* Add all pmus in sysfs to pmu list: */
static void pmu_read_sysfs(bool core_only)
{
@@ -156,6 +203,8 @@ static void pmu_read_sysfs(bool core_only)
if (!perf_pmu__create_placeholder_core_pmu(&core_pmus))
pr_err("Failure to set up any core PMUs\n");
}
+ list_sort(NULL, &core_pmus, pmus_cmp);
+ list_sort(NULL, &other_pmus, pmus_cmp);
if (!list_empty(&core_pmus)) {
read_sysfs_core_pmus = true;
if (!core_only)
@@ -227,6 +276,43 @@ struct perf_pmu *perf_pmus__scan_core(struct perf_pmu *pmu)
return NULL;
}
+static struct perf_pmu *perf_pmus__scan_skip_duplicates(struct perf_pmu *pmu)
+{
+ bool use_core_pmus = !pmu || pmu->is_core;
+ int last_pmu_name_len = 0;
+ const char *last_pmu_name = (pmu && pmu->name) ? pmu->name : "";
+
+ if (!pmu) {
+ pmu_read_sysfs(/*core_only=*/false);
+ pmu = list_prepare_entry(pmu, &core_pmus, list);
+ } else
+ last_pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "", NULL);
+
+ if (use_core_pmus) {
+ list_for_each_entry_continue(pmu, &core_pmus, list) {
+ int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "", /*num=*/NULL);
+
+ if (last_pmu_name_len == pmu_name_len &&
+ !strncmp(last_pmu_name, pmu->name ?: "", pmu_name_len))
+ continue;
+
+ return pmu;
+ }
+ pmu = NULL;
+ pmu = list_prepare_entry(pmu, &other_pmus, list);
+ }
+ list_for_each_entry_continue(pmu, &other_pmus, list) {
+ int pmu_name_len = pmu_name_len_no_suffix(pmu->name ?: "", /*num=*/NULL);
+
+ if (last_pmu_name_len == pmu_name_len &&
+ !strncmp(last_pmu_name, pmu->name ?: "", pmu_name_len))
+ continue;
+
+ return pmu;
+ }
+ return NULL;
+}
+
const struct perf_pmu *perf_pmus__pmu_for_pmu_filter(const char *str)
{
struct perf_pmu *pmu = NULL;
@@ -258,219 +344,153 @@ int __weak perf_pmus__num_mem_pmus(void)
struct sevent {
/** PMU for event. */
const struct perf_pmu *pmu;
- /**
- * Optional event for name, desc, etc. If not present then this is a
- * selectable PMU and the event name is shown as "//".
- */
- const struct perf_pmu_alias *event;
- /** Is the PMU for the CPU? */
- bool is_cpu;
+ const char *name;
+ const char* alias;
+ const char *scale_unit;
+ const char *desc;
+ const char *long_desc;
+ const char *encoding_desc;
+ const char *topic;
+ const char *pmu_name;
+ bool deprecated;
};
static int cmp_sevent(const void *a, const void *b)
{
const struct sevent *as = a;
const struct sevent *bs = b;
- const char *a_pmu_name = NULL, *b_pmu_name = NULL;
- const char *a_name = "//", *a_desc = NULL, *a_topic = "";
- const char *b_name = "//", *b_desc = NULL, *b_topic = "";
+ bool a_iscpu, b_iscpu;
int ret;
- if (as->event) {
- a_name = as->event->name;
- a_desc = as->event->desc;
- a_topic = as->event->topic ?: "";
- a_pmu_name = as->event->pmu_name;
- }
- if (bs->event) {
- b_name = bs->event->name;
- b_desc = bs->event->desc;
- b_topic = bs->event->topic ?: "";
- b_pmu_name = bs->event->pmu_name;
- }
/* Put extra events last. */
- if (!!a_desc != !!b_desc)
- return !!a_desc - !!b_desc;
+ if (!!as->desc != !!bs->desc)
+ return !!as->desc - !!bs->desc;
/* Order by topics. */
- ret = strcmp(a_topic, b_topic);
+ ret = strcmp(as->topic ?: "", bs->topic ?: "");
if (ret)
return ret;
/* Order CPU core events to be first */
- if (as->is_cpu != bs->is_cpu)
- return as->is_cpu ? -1 : 1;
+ a_iscpu = as->pmu ? as->pmu->is_core : true;
+ b_iscpu = bs->pmu ? bs->pmu->is_core : true;
+ if (a_iscpu != b_iscpu)
+ return a_iscpu ? -1 : 1;
/* Order by PMU name. */
if (as->pmu != bs->pmu) {
- a_pmu_name = a_pmu_name ?: (as->pmu->name ?: "");
- b_pmu_name = b_pmu_name ?: (bs->pmu->name ?: "");
- ret = strcmp(a_pmu_name, b_pmu_name);
+ ret = strcmp(as->pmu_name ?: "", bs->pmu_name ?: "");
if (ret)
return ret;
}
/* Order by event name. */
- return strcmp(a_name, b_name);
+ return strcmp(as->name, bs->name);
}
-static bool pmu_alias_is_duplicate(struct sevent *alias_a,
- struct sevent *alias_b)
+static bool pmu_alias_is_duplicate(struct sevent *a, struct sevent *b)
{
- const char *a_pmu_name = NULL, *b_pmu_name = NULL;
- const char *a_name = "//", *b_name = "//";
-
-
- if (alias_a->event) {
- a_name = alias_a->event->name;
- a_pmu_name = alias_a->event->pmu_name;
- }
- if (alias_b->event) {
- b_name = alias_b->event->name;
- b_pmu_name = alias_b->event->pmu_name;
- }
-
/* Different names -> never duplicates */
- if (strcmp(a_name, b_name))
+ if (strcmp(a->name ?: "//", b->name ?: "//"))
return false;
/* Don't remove duplicates for different PMUs */
- a_pmu_name = a_pmu_name ?: (alias_a->pmu->name ?: "");
- b_pmu_name = b_pmu_name ?: (alias_b->pmu->name ?: "");
- return strcmp(a_pmu_name, b_pmu_name) == 0;
+ return strcmp(a->pmu_name, b->pmu_name) == 0;
}
-static int sub_non_neg(int a, int b)
-{
- if (b > a)
- return 0;
- return a - b;
-}
+struct events_callback_state {
+ struct sevent *aliases;
+ size_t aliases_len;
+ size_t index;
+};
-static char *format_alias(char *buf, int len, const struct perf_pmu *pmu,
- const struct perf_pmu_alias *alias)
+static int perf_pmus__print_pmu_events__callback(void *vstate,
+ struct pmu_event_info *info)
{
- struct parse_events_term *term;
- int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name);
-
- list_for_each_entry(term, &alias->terms, list) {
- if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
- used += snprintf(buf + used, sub_non_neg(len, used),
- ",%s=%s", term->config,
- term->val.str);
- }
+ struct events_callback_state *state = vstate;
+ struct sevent *s;
- if (sub_non_neg(len, used) > 0) {
- buf[used] = '/';
- used++;
+ if (state->index >= state->aliases_len) {
+ pr_err("Unexpected event %s/%s/\n", info->pmu->name, info->name);
+ return 1;
}
- if (sub_non_neg(len, used) > 0) {
- buf[used] = '\0';
- used++;
- } else
- buf[len - 1] = '\0';
-
- return buf;
+ s = &state->aliases[state->index];
+ s->pmu = info->pmu;
+#define COPY_STR(str) s->str = info->str ? strdup(info->str) : NULL
+ COPY_STR(name);
+ COPY_STR(alias);
+ COPY_STR(scale_unit);
+ COPY_STR(desc);
+ COPY_STR(long_desc);
+ COPY_STR(encoding_desc);
+ COPY_STR(topic);
+ COPY_STR(pmu_name);
+#undef COPY_STR
+ s->deprecated = info->deprecated;
+ state->index++;
+ return 0;
}
void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
{
struct perf_pmu *pmu;
- struct perf_pmu_alias *event;
- char buf[1024];
int printed = 0;
- int len, j;
+ int len;
struct sevent *aliases;
+ struct events_callback_state state;
+ bool skip_duplicate_pmus = print_cb->skip_duplicate_pmus(print_state);
+ struct perf_pmu *(*scan_fn)(struct perf_pmu *);
+
+ if (skip_duplicate_pmus)
+ scan_fn = perf_pmus__scan_skip_duplicates;
+ else
+ scan_fn = perf_pmus__scan;
pmu = NULL;
len = 0;
- while ((pmu = perf_pmus__scan(pmu)) != NULL) {
- list_for_each_entry(event, &pmu->aliases, list)
- len++;
- if (pmu->selectable)
- len++;
- }
+ while ((pmu = scan_fn(pmu)) != NULL)
+ len += perf_pmu__num_events(pmu);
+
aliases = zalloc(sizeof(struct sevent) * len);
if (!aliases) {
pr_err("FATAL: not enough memory to print PMU events\n");
return;
}
pmu = NULL;
- j = 0;
- while ((pmu = perf_pmus__scan(pmu)) != NULL) {
- bool is_cpu = pmu->is_core;
-
- list_for_each_entry(event, &pmu->aliases, list) {
- aliases[j].event = event;
- aliases[j].pmu = pmu;
- aliases[j].is_cpu = is_cpu;
- j++;
- }
- if (pmu->selectable) {
- aliases[j].event = NULL;
- aliases[j].pmu = pmu;
- aliases[j].is_cpu = is_cpu;
- j++;
- }
+ state = (struct events_callback_state) {
+ .aliases = aliases,
+ .aliases_len = len,
+ .index = 0,
+ };
+ while ((pmu = scan_fn(pmu)) != NULL) {
+ perf_pmu__for_each_event(pmu, skip_duplicate_pmus, &state,
+ perf_pmus__print_pmu_events__callback);
}
- len = j;
qsort(aliases, len, sizeof(struct sevent), cmp_sevent);
- for (j = 0; j < len; j++) {
- const char *name, *alias = NULL, *scale_unit = NULL,
- *desc = NULL, *long_desc = NULL,
- *encoding_desc = NULL, *topic = NULL,
- *pmu_name = NULL;
- bool deprecated = false;
- size_t buf_used;
-
+ for (int j = 0; j < len; j++) {
/* Skip duplicates */
if (j > 0 && pmu_alias_is_duplicate(&aliases[j], &aliases[j - 1]))
continue;
- if (!aliases[j].event) {
- /* A selectable event. */
- pmu_name = aliases[j].pmu->name;
- buf_used = snprintf(buf, sizeof(buf), "%s//", pmu_name) + 1;
- name = buf;
- } else {
- if (aliases[j].event->desc) {
- name = aliases[j].event->name;
- buf_used = 0;
- } else {
- name = format_alias(buf, sizeof(buf), aliases[j].pmu,
- aliases[j].event);
- if (aliases[j].is_cpu) {
- alias = name;
- name = aliases[j].event->name;
- }
- buf_used = strlen(buf) + 1;
- }
- pmu_name = aliases[j].event->pmu_name ?: (aliases[j].pmu->name ?: "");
- if (strlen(aliases[j].event->unit) || aliases[j].event->scale != 1.0) {
- scale_unit = buf + buf_used;
- buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
- "%G%s", aliases[j].event->scale,
- aliases[j].event->unit) + 1;
- }
- desc = aliases[j].event->desc;
- long_desc = aliases[j].event->long_desc;
- topic = aliases[j].event->topic;
- encoding_desc = buf + buf_used;
- buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
- "%s/%s/", pmu_name, aliases[j].event->str) + 1;
- deprecated = aliases[j].event->deprecated;
- }
print_cb->print_event(print_state,
- pmu_name,
- topic,
- name,
- alias,
- scale_unit,
- deprecated,
+ aliases[j].pmu_name,
+ aliases[j].topic,
+ aliases[j].name,
+ aliases[j].alias,
+ aliases[j].scale_unit,
+ aliases[j].deprecated,
"Kernel PMU event",
- desc,
- long_desc,
- encoding_desc);
+ aliases[j].desc,
+ aliases[j].long_desc,
+ aliases[j].encoding_desc);
+ zfree(&aliases[j].name);
+ zfree(&aliases[j].alias);
+ zfree(&aliases[j].scale_unit);
+ zfree(&aliases[j].desc);
+ zfree(&aliases[j].long_desc);
+ zfree(&aliases[j].encoding_desc);
+ zfree(&aliases[j].topic);
+ zfree(&aliases[j].pmu_name);
}
if (printed && pager_in_use())
printf("\n");
diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h
index a21464432d0f..4c67153ac257 100644
--- a/tools/perf/util/pmus.h
+++ b/tools/perf/util/pmus.h
@@ -5,6 +5,8 @@
struct perf_pmu;
struct print_callbacks;
+int pmu_name_len_no_suffix(const char *str, unsigned long *num);
+
void perf_pmus__destroy(void);
struct perf_pmu *perf_pmus__find(const char *name);
diff --git a/tools/perf/util/print-events.h b/tools/perf/util/print-events.h
index d7fab411e75c..bf4290bef0cd 100644
--- a/tools/perf/util/print-events.h
+++ b/tools/perf/util/print-events.h
@@ -26,6 +26,7 @@ struct print_callbacks {
const char *expr,
const char *threshold,
const char *unit);
+ bool (*skip_duplicate_pmus)(void *print_state);
};
/** Print all events, the default when no options are specified. */
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 16822a8a540f..1a5b7fa459b2 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -53,6 +53,8 @@
bool probe_event_dry_run; /* Dry run flag */
struct probe_conf probe_conf = { .magic_num = DEFAULT_PROBE_MAGIC_NUM };
+static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
+
#define semantic_error(msg ...) pr_err("Semantic error :" msg)
int e_snprintf(char *str, size_t size, const char *format, ...)
@@ -961,8 +963,9 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
debuginfo__delete(dinfo);
if (ntevs == 0) { /* No error but failed to find probe point. */
- pr_warning("Probe point '%s' not found.\n",
- synthesize_perf_probe_point(&pev->point));
+ char *probe_point = synthesize_perf_probe_point(&pev->point);
+ pr_warning("Probe point '%s' not found.\n", probe_point);
+ free(probe_point);
return -ENODEV;
} else if (ntevs < 0) {
/* Error path : ntevs < 0 */
@@ -2009,7 +2012,7 @@ out:
}
/* Compose only probe point (not argument) */
-char *synthesize_perf_probe_point(struct perf_probe_point *pp)
+static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
{
struct strbuf buf;
char *tmp, *ret = NULL;
@@ -2062,14 +2065,18 @@ char *synthesize_perf_probe_command(struct perf_probe_event *pev)
goto out;
tmp = synthesize_perf_probe_point(&pev->point);
- if (!tmp || strbuf_addstr(&buf, tmp) < 0)
+ if (!tmp || strbuf_addstr(&buf, tmp) < 0) {
+ free(tmp);
goto out;
+ }
free(tmp);
for (i = 0; i < pev->nargs; i++) {
tmp = synthesize_perf_probe_arg(pev->args + i);
- if (!tmp || strbuf_addf(&buf, " %s", tmp) < 0)
+ if (!tmp || strbuf_addf(&buf, " %s", tmp) < 0) {
+ free(tmp);
goto out;
+ }
free(tmp);
}
@@ -2800,13 +2807,18 @@ static void warn_uprobe_event_compat(struct probe_trace_event *tev)
if (!tev->uprobes || tev->nargs == 0 || !buf)
goto out;
- for (i = 0; i < tev->nargs; i++)
- if (strglobmatch(tev->args[i].value, "[$@+-]*")) {
- pr_warning("Please upgrade your kernel to at least "
- "3.14 to have access to feature %s\n",
+ for (i = 0; i < tev->nargs; i++) {
+ if (strchr(tev->args[i].value, '@')) {
+ pr_warning("%s accesses a variable by symbol name, but that is not supported for user application probe.\n",
tev->args[i].value);
break;
}
+ if (strglobmatch(tev->args[i].value, "[$+-]*")) {
+ pr_warning("Please upgrade your kernel to at least 3.14 to have access to feature %s\n",
+ tev->args[i].value);
+ break;
+ }
+ }
out:
free(buf);
}
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 8ad5b1579f1d..7e3b6c3d1f74 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -137,7 +137,6 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev);
char *synthesize_perf_probe_command(struct perf_probe_event *pev);
char *synthesize_probe_trace_command(struct probe_trace_event *tev);
char *synthesize_perf_probe_arg(struct perf_probe_arg *pa);
-char *synthesize_perf_probe_point(struct perf_probe_point *pp);
int perf_probe_event__copy(struct perf_probe_event *dst,
struct perf_probe_event *src);
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources
index d4c9b4cd35ef..26e1c8d973ea 100644
--- a/tools/perf/util/python-ext-sources
+++ b/tools/perf/util/python-ext-sources
@@ -40,3 +40,12 @@ util/rwsem.c
util/hashmap.c
util/perf_regs.c
util/fncache.c
+util/perf-regs-arch/perf_regs_aarch64.c
+util/perf-regs-arch/perf_regs_arm.c
+util/perf-regs-arch/perf_regs_csky.c
+util/perf-regs-arch/perf_regs_loongarch.c
+util/perf-regs-arch/perf_regs_mips.c
+util/perf-regs-arch/perf_regs_powerpc.c
+util/perf-regs-arch/perf_regs_riscv.c
+util/perf-regs-arch/perf_regs_s390.c
+util/perf-regs-arch/perf_regs_x86.c
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 4eed8ec23994..c29f5f0bb552 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -113,6 +113,11 @@ bool evsel__is_aux_event(const struct evsel *evsel __maybe_unused)
return false;
}
+bool perf_pmus__supports_extended_type(void)
+{
+ return false;
+}
+
/*
* Add this one here not to drag util/metricgroup.c
*/
diff --git a/tools/perf/util/s390-sample-raw.c b/tools/perf/util/s390-sample-raw.c
index c10b891dbad6..115b16edb451 100644
--- a/tools/perf/util/s390-sample-raw.c
+++ b/tools/perf/util/s390-sample-raw.c
@@ -27,7 +27,7 @@
#include "color.h"
#include "sample-raw.h"
#include "s390-cpumcf-kernel.h"
-#include "pmu-events/pmu-events.h"
+#include "util/pmu.h"
#include "util/sample.h"
static size_t ctrset_size(struct cf_ctrset_entry *set)
@@ -132,56 +132,58 @@ static int get_counterset_start(int setnr)
struct get_counter_name_data {
int wanted;
- const char *result;
+ char *result;
};
-static int get_counter_name_callback(const struct pmu_event *evp,
- const struct pmu_events_table *table __maybe_unused,
- void *vdata)
+static int get_counter_name_callback(void *vdata, struct pmu_event_info *info)
{
struct get_counter_name_data *data = vdata;
int rc, event_nr;
+ const char *event_str;
- if (evp->name == NULL || evp->event == NULL)
+ if (info->str == NULL)
return 0;
- rc = sscanf(evp->event, "event=%x", &event_nr);
+
+ event_str = strstr(info->str, "event=");
+ if (!event_str)
+ return 0;
+
+ rc = sscanf(event_str, "event=%x", &event_nr);
if (rc == 1 && event_nr == data->wanted) {
- data->result = evp->name;
+ data->result = strdup(info->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.
+/* Scan the PMU and extract the logical name of a counter from the event. 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_table *table)
+static char *get_counter_name(int set, int nr, struct perf_pmu *pmu)
{
struct get_counter_name_data data = {
.wanted = get_counterset_start(set) + nr,
.result = NULL,
};
- if (!table)
+ if (!pmu)
return NULL;
- pmu_events_table_for_each_event(table, get_counter_name_callback, &data);
+ perf_pmu__for_each_event(pmu, /*skip_duplicate_pmus=*/ true,
+ &data, get_counter_name_callback);
return data.result;
}
-static void s390_cpumcfdg_dump(struct perf_sample *sample)
+static void s390_cpumcfdg_dump(struct perf_pmu *pmu, struct perf_sample *sample)
{
size_t i, len = sample->raw_size, offset = 0;
unsigned char *buf = sample->raw_data;
const char *color = PERF_COLOR_BLUE;
struct cf_ctrset_entry *cep, ce;
- const struct pmu_events_table *table;
u64 *p;
- table = pmu_events_table__find();
while (offset < len) {
cep = (struct cf_ctrset_entry *)(buf + offset);
@@ -199,11 +201,12 @@ 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, table);
+ char *ev_name = get_counter_name(ce.set, i, pmu);
color_fprintf(stdout, color,
"\tCounter:%03d %s Value:%#018lx\n", i,
ev_name ?: "<unknown>", be64_to_cpu(*p));
+ free(ev_name);
}
offset += ctrset_size(&ce);
}
@@ -216,14 +219,14 @@ static void s390_cpumcfdg_dump(struct perf_sample *sample)
*/
void evlist__s390_sample_raw(struct evlist *evlist, union perf_event *event, struct perf_sample *sample)
{
- struct evsel *ev_bc000;
+ struct evsel *evsel;
if (event->header.type != PERF_RECORD_SAMPLE)
return;
- ev_bc000 = evlist__event2evsel(evlist, event);
- if (ev_bc000 == NULL ||
- ev_bc000->core.attr.config != PERF_EVENT_CPUM_CF_DIAG)
+ evsel = evlist__event2evsel(evlist, event);
+ if (evsel == NULL ||
+ evsel->core.attr.config != PERF_EVENT_CPUM_CF_DIAG)
return;
/* Display raw data on screen */
@@ -231,5 +234,5 @@ void evlist__s390_sample_raw(struct evlist *evlist, union perf_event *event, str
pr_err("Invalid counter set data encountered\n");
return;
}
- s390_cpumcfdg_dump(sample);
+ s390_cpumcfdg_dump(evsel->pmu, sample);
}
diff --git a/tools/perf/util/scripting-engines/Build b/tools/perf/util/scripting-engines/Build
index c220fec97032..586b94e90f4e 100644
--- a/tools/perf/util/scripting-engines/Build
+++ b/tools/perf/util/scripting-engines/Build
@@ -5,4 +5,5 @@ perf-$(CONFIG_LIBPYTHON) += trace-event-python.o
CFLAGS_trace-event-perl.o += $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-nested-externs -Wno-undef -Wno-switch-default -Wno-bad-function-cast -Wno-declaration-after-statement -Wno-switch-enum
-CFLAGS_trace-event-python.o += $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-deprecated-declarations -Wno-switch-enum
+# -Wno-declaration-after-statement: The python headers have mixed code with declarations (decls after asserts, for instance)
+CFLAGS_trace-event-python.o += $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-deprecated-declarations -Wno-switch-enum -Wno-declaration-after-statement
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 00d18c74c090..1e9aa8ed15b6 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -833,8 +833,8 @@ static void perf_event__hdr_attr_swap(union perf_event *event,
perf_event__attr_swap(&event->attr.attr);
size = event->header.size;
- size -= (void *)&event->attr.id - (void *)event;
- mem_bswap_64(event->attr.id, size);
+ size -= perf_record_header_attr_id(event) - (void *)event;
+ mem_bswap_64(perf_record_header_attr_id(event), size);
}
static void perf_event__event_update_swap(union perf_event *event,
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py
index 869738fc06c3..79d5e2955f85 100644
--- a/tools/perf/util/setup.py
+++ b/tools/perf/util/setup.py
@@ -66,6 +66,9 @@ if cc_is_clang:
else:
cflags += ['-Wno-cast-function-type' ]
+# The python headers have mixed code with declarations (decls after asserts, for instance)
+cflags += [ "-Wno-declaration-after-statement" ]
+
src_perf = getenv('srctree') + '/tools/perf'
build_lib = getenv('PYTHON_EXTBUILD_LIB')
build_tmp = getenv('PYTHON_EXTBUILD_TMP')
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index d45d5dcb0e2b..afe6db8e7bf4 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -578,7 +578,7 @@ static void print_metric_only_csv(struct perf_stat_config *config __maybe_unused
if (!valid_only_metric(unit))
return;
unit = fixunit(tbuf, os->evsel, unit);
- snprintf(buf, sizeof buf, fmt, val);
+ snprintf(buf, sizeof(buf), fmt ?: "", val);
ends = vals = skip_spaces(buf);
while (isdigit(*ends) || *ends == '.')
ends++;
@@ -600,7 +600,7 @@ static void print_metric_only_json(struct perf_stat_config *config __maybe_unuse
if (!valid_only_metric(unit))
return;
unit = fixunit(tbuf, os->evsel, unit);
- snprintf(buf, sizeof(buf), fmt, val);
+ snprintf(buf, sizeof(buf), fmt ?: "", val);
ends = vals = skip_spaces(buf);
while (isdigit(*ends) || *ends == '.')
ends++;
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index 967e583392c7..ec3506042217 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -729,7 +729,7 @@ size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp)
size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp)
{
- struct perf_stat_config sc;
+ struct perf_stat_config sc = {};
size_t ret;
perf_event__read_stat_config(&sc, &event->stat_config);
diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c
index 5c62d3118c41..0e4dc31c6c9c 100644
--- a/tools/perf/util/svghelper.c
+++ b/tools/perf/util/svghelper.c
@@ -331,7 +331,7 @@ static char *cpu_model(void)
file = fopen("/proc/cpuinfo", "r");
if (file) {
while (fgets(buf, 255, file)) {
- if (strstr(buf, "model name")) {
+ if (strcasestr(buf, "model name")) {
strlcpy(cpu_m, &buf[13], 255);
break;
}
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 8bd466d1c2bd..95e99c332d7e 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -1440,6 +1440,8 @@ static int dso__process_kernel_symbol(struct dso *dso, struct map *map,
curr_dso->kernel = dso->kernel;
curr_dso->long_name = dso->long_name;
curr_dso->long_name_len = dso->long_name_len;
+ curr_dso->binary_type = dso->binary_type;
+ curr_dso->adjust_symbols = dso->adjust_symbols;
curr_map = map__new2(start, curr_dso);
dso__put(curr_dso);
if (curr_map == NULL)
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index f849f9ef68e6..3f36675b7c8f 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -2204,15 +2204,20 @@ int dso__load_vmlinux(struct dso *dso, struct map *map,
if (symsrc__init(&ss, dso, symfs_vmlinux, symtab_type))
return -1;
+ /*
+ * dso__load_sym() may copy 'dso' which will result in the copies having
+ * an incorrect long name unless we set it here first.
+ */
+ dso__set_long_name(dso, vmlinux, vmlinux_allocated);
+ if (dso->kernel == DSO_SPACE__KERNEL_GUEST)
+ dso->binary_type = DSO_BINARY_TYPE__GUEST_VMLINUX;
+ else
+ dso->binary_type = DSO_BINARY_TYPE__VMLINUX;
+
err = dso__load_sym(dso, map, &ss, &ss, 0);
symsrc__destroy(&ss);
if (err > 0) {
- if (dso->kernel == DSO_SPACE__KERNEL_GUEST)
- dso->binary_type = DSO_BINARY_TYPE__GUEST_VMLINUX;
- else
- dso->binary_type = DSO_BINARY_TYPE__VMLINUX;
- dso__set_long_name(dso, vmlinux, vmlinux_allocated);
dso__set_loaded(dso);
pr_debug("Using %s for symbols\n", symfs_vmlinux);
}
diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
index 45714a2785fd..a0579c7d7b9e 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -2145,7 +2145,7 @@ int perf_event__synthesize_attr(struct perf_tool *tool, struct perf_event_attr *
return -ENOMEM;
ev->attr.attr = *attr;
- memcpy(ev->attr.id, id, ids * sizeof(u64));
+ memcpy(perf_record_header_attr_id(ev), id, ids * sizeof(u64));
ev->attr.header.type = PERF_RECORD_HEADER_ATTR;
ev->attr.header.size = (u16)size;
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 0b166404c5c3..fe5e6991ae4b 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -80,6 +80,15 @@ err_thread:
return NULL;
}
+static void (*thread__priv_destructor)(void *priv);
+
+void thread__set_priv_destructor(void (*destructor)(void *priv))
+{
+ assert(thread__priv_destructor == NULL);
+
+ thread__priv_destructor = destructor;
+}
+
void thread__delete(struct thread *thread)
{
struct namespaces *namespaces, *tmp_namespaces;
@@ -112,6 +121,10 @@ void thread__delete(struct thread *thread)
exit_rwsem(thread__namespaces_lock(thread));
exit_rwsem(thread__comm_lock(thread));
thread__free_stitch_list(thread);
+
+ if (thread__priv_destructor)
+ thread__priv_destructor(thread__priv(thread));
+
RC_CHK_FREE(thread);
}
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 9068a21ce0fa..e79225a0ea46 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -71,6 +71,8 @@ struct thread *thread__new(pid_t pid, pid_t tid);
int thread__init_maps(struct thread *thread, struct machine *machine);
void thread__delete(struct thread *thread);
+void thread__set_priv_destructor(void (*destructor)(void *priv));
+
struct thread *thread__get(struct thread *thread);
void thread__put(struct thread *thread);
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 2a96df4c8d42..8554db3fc0d7 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -17,6 +17,7 @@
#include "event.h"
#include "perf_regs.h"
#include "callchain.h"
+#include "util/env.h"
static char *debuginfo_path;
@@ -170,12 +171,14 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *
void *arg)
{
struct unwind_info *ui = arg;
+ const char *arch = perf_env__arch(ui->machine->env);
struct stack_dump *stack = &ui->sample->user_stack;
u64 start, end;
int offset;
int ret;
- ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP);
+ ret = perf_reg_value(&start, &ui->sample->user_regs,
+ perf_arch_reg_sp(arch));
if (ret)
return false;
@@ -253,6 +256,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
.max_stack = max_stack,
.best_effort = best_effort
};
+ const char *arch = perf_env__arch(ui_buf.machine->env);
Dwarf_Word ip;
int err = -EINVAL, i;
@@ -269,7 +273,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
if (!ui->dwfl)
goto out;
- err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP);
+ err = perf_reg_value(&ip, &data->user_regs, perf_arch_reg_ip(arch));
if (err)
goto out;
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index ebfde537b99b..c0641882fd2f 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -553,6 +553,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as,
int __write, void *arg)
{
struct unwind_info *ui = arg;
+ const char *arch = perf_env__arch(ui->machine->env);
struct stack_dump *stack = &ui->sample->user_stack;
u64 start, end;
int offset;
@@ -565,7 +566,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as,
}
ret = perf_reg_value(&start, &ui->sample->user_regs,
- LIBUNWIND__ARCH_REG_SP);
+ perf_arch_reg_sp(arch));
if (ret)
return ret;
@@ -714,6 +715,7 @@ static void _unwind__finish_access(struct maps *maps)
static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
void *arg, int max_stack)
{
+ const char *arch = perf_env__arch(ui->machine->env);
u64 val;
unw_word_t ips[max_stack];
unw_addr_space_t addr_space;
@@ -721,7 +723,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
int ret, i = 0;
ret = perf_reg_value(&val, &ui->sample->user_regs,
- LIBUNWIND__ARCH_REG_IP);
+ perf_arch_reg_ip(arch));
if (ret)
return ret;
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
index b2a03fa5289b..9f7164c6d9aa 100644
--- a/tools/perf/util/unwind.h
+++ b/tools/perf/util/unwind.h
@@ -42,14 +42,6 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
#define LIBUNWIND__ARCH_REG_ID(regnum) libunwind__arch_reg_id(regnum)
#endif
-#ifndef LIBUNWIND__ARCH_REG_SP
-#define LIBUNWIND__ARCH_REG_SP PERF_REG_SP
-#endif
-
-#ifndef LIBUNWIND__ARCH_REG_IP
-#define LIBUNWIND__ARCH_REG_IP PERF_REG_IP
-#endif
-
int LIBUNWIND__ARCH_REG_ID(int regnum);
int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized);
void unwind__flush_access(struct maps *maps);