summaryrefslogtreecommitdiff
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/bpf_skel/.gitignore1
-rw-r--r--tools/perf/util/bpf_skel/lock_contention.bpf.c2
-rw-r--r--tools/perf/util/bpf_skel/vmlinux/vmlinux.h (renamed from tools/perf/util/bpf_skel/vmlinux.h)10
-rw-r--r--tools/perf/util/dso.c7
-rw-r--r--tools/perf/util/dso.h3
-rw-r--r--tools/perf/util/event.c7
-rw-r--r--tools/perf/util/evsel.c30
-rw-r--r--tools/perf/util/evsel.h1
-rw-r--r--tools/perf/util/expr.c21
-rw-r--r--tools/perf/util/expr.h1
-rw-r--r--tools/perf/util/expr.l1
-rw-r--r--tools/perf/util/expr.y8
-rw-r--r--tools/perf/util/llvm-utils.c4
-rw-r--r--tools/perf/util/map.c14
-rw-r--r--tools/perf/util/map.h12
-rw-r--r--tools/perf/util/perf_event_attr_fprintf.c186
-rw-r--r--tools/perf/util/pmu.c27
-rw-r--r--tools/perf/util/pmu.h1
-rw-r--r--tools/perf/util/pmus.c7
-rw-r--r--tools/perf/util/probe-event.c16
-rw-r--r--tools/perf/util/python.c8
-rw-r--r--tools/perf/util/symbol.c136
-rw-r--r--tools/perf/util/symbol.h12
-rw-r--r--tools/perf/util/symbol_conf.h1
-rw-r--r--tools/perf/util/symbol_fprintf.c10
-rw-r--r--tools/perf/util/trace-event-info.c12
-rw-r--r--tools/perf/util/trace-event.h6
-rw-r--r--tools/perf/util/unwind-libdw.c8
28 files changed, 407 insertions, 145 deletions
diff --git a/tools/perf/util/bpf_skel/.gitignore b/tools/perf/util/bpf_skel/.gitignore
index 7a1c832825de..cd01455e1b53 100644
--- a/tools/perf/util/bpf_skel/.gitignore
+++ b/tools/perf/util/bpf_skel/.gitignore
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
.tmp
*.skel.h
+vmlinux.h
diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/util/bpf_skel/lock_contention.bpf.c
index 1d48226ae75d..8d3cfbb3cc65 100644
--- a/tools/perf/util/bpf_skel/lock_contention.bpf.c
+++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c
@@ -416,8 +416,6 @@ int contention_end(u64 *ctx)
return 0;
}
-struct rq {};
-
extern struct rq runqueues __ksym;
struct rq___old {
diff --git a/tools/perf/util/bpf_skel/vmlinux.h b/tools/perf/util/bpf_skel/vmlinux/vmlinux.h
index c7ed51b0c1ef..ab84a6e1da5e 100644
--- a/tools/perf/util/bpf_skel/vmlinux.h
+++ b/tools/perf/util/bpf_skel/vmlinux/vmlinux.h
@@ -171,4 +171,14 @@ struct bpf_perf_event_data_kern {
struct perf_sample_data *data;
struct perf_event *event;
} __attribute__((preserve_access_index));
+
+/*
+ * If 'struct rq' isn't defined for lock_contention.bpf.c, for the sake of
+ * rq___old and rq___new, then the type for the 'runqueue' variable ends up
+ * being a forward declaration (BTF_KIND_FWD) while the kernel has it defined
+ * (BTF_KIND_STRUCT). The definition appears in vmlinux.h rather than
+ * lock_contention.bpf.c for consistency with a generated vmlinux.h.
+ */
+struct rq {};
+
#endif // __VMLINUX_H
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 046fbfcfdaab..bdfead36b83a 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1320,7 +1320,9 @@ struct dso *dso__new_id(const char *name, struct dso_id *id)
dso->id = *id;
dso__set_long_name_id(dso, dso->name, id, false);
dso__set_short_name(dso, dso->name, false);
- dso->symbols = dso->symbol_names = RB_ROOT_CACHED;
+ dso->symbols = RB_ROOT_CACHED;
+ dso->symbol_names = NULL;
+ dso->symbol_names_len = 0;
dso->data.cache = RB_ROOT;
dso->inlined_nodes = RB_ROOT_CACHED;
dso->srclines = RB_ROOT_CACHED;
@@ -1364,7 +1366,8 @@ void dso__delete(struct dso *dso)
inlines__tree_delete(&dso->inlined_nodes);
srcline__tree_delete(&dso->srclines);
symbols__delete(&dso->symbols);
-
+ dso->symbol_names_len = 0;
+ zfree(&dso->symbol_names);
if (dso->short_name_allocated) {
zfree((char **)&dso->short_name);
dso->short_name_allocated = false;
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index b23a157c914d..b41c9782c754 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -150,7 +150,8 @@ struct dso {
struct rb_node rb_node; /* rbtree node sorted by long name */
struct rb_root *root; /* root of rbtree that rb_node is in */
struct rb_root_cached symbols;
- struct rb_root_cached symbol_names;
+ struct symbol **symbol_names;
+ size_t symbol_names_len;
struct rb_root_cached inlined_nodes;
struct rb_root_cached srclines;
struct {
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 3860b0c74829..4cbb092e0684 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -581,15 +581,14 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr,
maps__zput(al->maps);
map__zput(al->map);
thread__zput(al->thread);
+ al->thread = thread__get(thread);
al->addr = addr;
al->cpumode = cpumode;
al->filtered = 0;
- if (machine == NULL) {
- al->map = NULL;
+ if (machine == NULL)
return NULL;
- }
if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) {
al->level = 'k';
@@ -605,7 +604,6 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr,
al->level = 'u';
} else {
al->level = 'H';
- al->map = NULL;
if ((cpumode == PERF_RECORD_MISC_GUEST_USER ||
cpumode == PERF_RECORD_MISC_GUEST_KERNEL) &&
@@ -619,7 +617,6 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr,
return NULL;
}
al->maps = maps__get(maps);
- al->thread = thread__get(thread);
al->map = map__get(maps__find(maps, al->addr));
if (al->map != NULL) {
/*
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index f607b5bddc76..762e2b2634a5 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -2924,25 +2924,19 @@ static bool find_process(const char *name)
return ret ? false : true;
}
-static bool is_amd(const char *arch, const char *cpuid)
+int __weak arch_evsel__open_strerror(struct evsel *evsel __maybe_unused,
+ char *msg __maybe_unused,
+ size_t size __maybe_unused)
{
- return arch && !strcmp("x86", arch) && cpuid && strstarts(cpuid, "AuthenticAMD");
-}
-
-static bool is_amd_ibs(struct evsel *evsel)
-{
- return evsel->core.attr.precise_ip
- || (evsel->pmu_name && !strncmp(evsel->pmu_name, "ibs", 3));
+ return 0;
}
int evsel__open_strerror(struct evsel *evsel, struct target *target,
int err, char *msg, size_t size)
{
- struct perf_env *env = evsel__env(evsel);
- const char *arch = perf_env__arch(env);
- const char *cpuid = perf_env__cpuid(env);
char sbuf[STRERR_BUFSIZE];
int printed = 0, enforced = 0;
+ int ret;
switch (err) {
case EPERM:
@@ -3044,16 +3038,6 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target,
return scnprintf(msg, size,
"Invalid event (%s) in per-thread mode, enable system wide with '-a'.",
evsel__name(evsel));
- if (is_amd(arch, cpuid)) {
- if (is_amd_ibs(evsel)) {
- if (evsel->core.attr.exclude_kernel)
- return scnprintf(msg, size,
- "AMD IBS can't exclude kernel events. Try running at a higher privilege level.");
- if (!evsel->core.system_wide)
- return scnprintf(msg, size,
- "AMD IBS may only be available in system-wide/per-cpu mode. Try using -a, or -C and workload affinity");
- }
- }
break;
case ENODATA:
@@ -3063,6 +3047,10 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target,
break;
}
+ ret = arch_evsel__open_strerror(evsel, msg, size);
+ if (ret)
+ return ret;
+
return scnprintf(msg, size,
"The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n"
"/bin/dmesg | grep -i perf may provide additional information.\n",
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 9f06d6cd5379..848534ec74fa 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -311,6 +311,7 @@ void evsel__set_sample_id(struct evsel *evsel, bool use_sample_identifier);
void arch_evsel__set_sample_weight(struct evsel *evsel);
void arch__post_evsel_config(struct evsel *evsel, struct perf_event_attr *attr);
+int arch_evsel__open_strerror(struct evsel *evsel, char *msg, size_t size);
int evsel__set_filter(struct evsel *evsel, const char *filter);
int evsel__append_tp_filter(struct evsel *evsel, const char *filter);
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index f4e52919324e..4814262e3805 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -8,6 +8,7 @@
#include "cpumap.h"
#include "cputopo.h"
#include "debug.h"
+#include "evlist.h"
#include "expr.h"
#include "expr-bison.h"
#include "expr-flex.h"
@@ -474,3 +475,23 @@ out:
pr_debug2("literal: %s = %f\n", literal, result);
return result;
}
+
+/* Does the event 'id' parse? Determine via ctx->ids if possible. */
+double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const char *id)
+{
+ struct evlist *tmp;
+ double ret;
+
+ if (hashmap__find(ctx->ids, id, /*value=*/NULL))
+ return 1.0;
+
+ if (!compute_ids)
+ return 0.0;
+
+ tmp = evlist__new();
+ if (!tmp)
+ return NAN;
+ ret = parse_event(tmp, id) ? 0 : 1;
+ evlist__delete(tmp);
+ return ret;
+}
diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h
index eaa44b24c555..3c1e49b3e35d 100644
--- a/tools/perf/util/expr.h
+++ b/tools/perf/util/expr.h
@@ -54,5 +54,6 @@ int expr__find_ids(const char *expr, const char *one,
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);
#endif
diff --git a/tools/perf/util/expr.l b/tools/perf/util/expr.l
index 4fbf353e78e7..dbb117414710 100644
--- a/tools/perf/util/expr.l
+++ b/tools/perf/util/expr.l
@@ -113,6 +113,7 @@ min { return MIN; }
if { return IF; }
else { return ELSE; }
source_count { return SOURCE_COUNT; }
+has_event { return HAS_EVENT; }
{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 f04963eb6be0..dd504afd8f36 100644
--- a/tools/perf/util/expr.y
+++ b/tools/perf/util/expr.y
@@ -37,7 +37,7 @@
} ids;
}
-%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT EXPR_ERROR
+%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT EXPR_ERROR
%left MIN MAX IF
%left '|'
%left '^'
@@ -199,6 +199,12 @@ expr: NUMBER
}
| ID { $$ = handle_id(ctx, $1, compute_ids, /*source_count=*/false); }
| SOURCE_COUNT '(' ID ')' { $$ = handle_id(ctx, $3, compute_ids, /*source_count=*/true); }
+| HAS_EVENT '(' ID ')'
+{
+ $$.val = expr__has_event(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/llvm-utils.c b/tools/perf/util/llvm-utils.c
index 4e8e243a6e4b..c6c9c2228578 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -25,7 +25,7 @@
"$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"
+ "-c \"$CLANG_SOURCE\" --target=bpf $CLANG_EMIT_LLVM -g -O2 -o - $LLVM_OPTIONS_PIPE"
struct llvm_param llvm_param = {
.clang_path = "clang",
@@ -569,7 +569,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
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(" \t\tclang --target=bpf -O2 -c %s\n", path);
pr_err(" \twith proper -I and -D options.\n");
goto errout;
}
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index f30d34903aa4..f64b83004421 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -390,7 +390,7 @@ struct symbol *map__find_symbol(struct map *map, u64 addr)
return dso__find_symbol(map__dso(map), addr);
}
-struct symbol *map__find_symbol_by_name(struct map *map, const char *name)
+struct symbol *map__find_symbol_by_name_idx(struct map *map, const char *name, size_t *idx)
{
struct dso *dso;
@@ -398,10 +398,16 @@ struct symbol *map__find_symbol_by_name(struct map *map, const char *name)
return NULL;
dso = map__dso(map);
- if (!dso__sorted_by_name(dso))
- dso__sort_by_name(dso);
+ dso__sort_by_name(dso);
+
+ return dso__find_symbol_by_name(dso, name, idx);
+}
+
+struct symbol *map__find_symbol_by_name(struct map *map, const char *name)
+{
+ size_t idx;
- return dso__find_symbol_by_name(dso, name);
+ return map__find_symbol_by_name_idx(map, name, &idx);
}
struct map *map__clone(struct map *from)
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 66a87b3d9965..1b53d53adc86 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -148,16 +148,17 @@ struct thread;
* @map: the 'struct map *' in which symbols are iterated
* @sym_name: the symbol name
* @pos: the 'struct symbol *' to use as a loop cursor
+ * @idx: the cursor index in the symbol names array
*/
-#define __map__for_each_symbol_by_name(map, sym_name, pos) \
- for (pos = map__find_symbol_by_name(map, sym_name); \
+#define __map__for_each_symbol_by_name(map, sym_name, pos, idx) \
+ for (pos = map__find_symbol_by_name_idx(map, sym_name, &idx); \
pos && \
!symbol__match_symbol_name(pos->name, sym_name, \
SYMBOL_TAG_INCLUDE__DEFAULT_ONLY); \
- pos = symbol__next_by_name(pos))
+ pos = dso__next_symbol_by_name(map__dso(map), &idx))
-#define map__for_each_symbol_by_name(map, sym_name, pos) \
- __map__for_each_symbol_by_name(map, sym_name, (pos))
+#define map__for_each_symbol_by_name(map, sym_name, pos, idx) \
+ __map__for_each_symbol_by_name(map, sym_name, (pos), idx)
void map__init(struct map *map,
u64 start, u64 end, u64 pgoff, struct dso *dso);
@@ -202,6 +203,7 @@ int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix,
int map__load(struct map *map);
struct symbol *map__find_symbol(struct map *map, u64 addr);
struct symbol *map__find_symbol_by_name(struct map *map, const char *name);
+struct symbol *map__find_symbol_by_name_idx(struct map *map, const char *name, size_t *idx);
void map__fixup_start(struct map *map);
void map__fixup_end(struct map *map);
diff --git a/tools/perf/util/perf_event_attr_fprintf.c b/tools/perf/util/perf_event_attr_fprintf.c
index 7e5e7b30510d..2247991451f3 100644
--- a/tools/perf/util/perf_event_attr_fprintf.c
+++ b/tools/perf/util/perf_event_attr_fprintf.c
@@ -1,11 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
#include <inttypes.h>
#include <stdio.h>
+#include <stdlib.h>
#include <stdbool.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/perf_event.h>
#include "util/evsel_fprintf.h"
+#include "trace-event.h"
struct bit_names {
int bit;
@@ -71,6 +73,170 @@ static void __p_read_format(char *buf, size_t size, u64 value)
__p_bits(buf, size, value, bits);
}
+#define ENUM_ID_TO_STR_CASE(x) case x: return (#x);
+static const char *stringify_perf_type_id(u64 value)
+{
+ switch (value) {
+ ENUM_ID_TO_STR_CASE(PERF_TYPE_HARDWARE)
+ ENUM_ID_TO_STR_CASE(PERF_TYPE_SOFTWARE)
+ ENUM_ID_TO_STR_CASE(PERF_TYPE_TRACEPOINT)
+ ENUM_ID_TO_STR_CASE(PERF_TYPE_HW_CACHE)
+ ENUM_ID_TO_STR_CASE(PERF_TYPE_RAW)
+ ENUM_ID_TO_STR_CASE(PERF_TYPE_BREAKPOINT)
+ default:
+ return NULL;
+ }
+}
+
+static const char *stringify_perf_hw_id(u64 value)
+{
+ switch (value) {
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CPU_CYCLES)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_INSTRUCTIONS)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_REFERENCES)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_MISSES)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_BRANCH_INSTRUCTIONS)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_BRANCH_MISSES)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_BUS_CYCLES)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_STALLED_CYCLES_FRONTEND)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_STALLED_CYCLES_BACKEND)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_REF_CPU_CYCLES)
+ default:
+ return NULL;
+ }
+}
+
+static const char *stringify_perf_hw_cache_id(u64 value)
+{
+ switch (value) {
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_L1D)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_L1I)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_LL)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_DTLB)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_ITLB)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_BPU)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_NODE)
+ default:
+ return NULL;
+ }
+}
+
+static const char *stringify_perf_hw_cache_op_id(u64 value)
+{
+ switch (value) {
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_OP_READ)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_OP_WRITE)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_OP_PREFETCH)
+ default:
+ return NULL;
+ }
+}
+
+static const char *stringify_perf_hw_cache_op_result_id(u64 value)
+{
+ switch (value) {
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_RESULT_ACCESS)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_RESULT_MISS)
+ default:
+ return NULL;
+ }
+}
+
+static const char *stringify_perf_sw_id(u64 value)
+{
+ switch (value) {
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_CPU_CLOCK)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_TASK_CLOCK)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_PAGE_FAULTS)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_CONTEXT_SWITCHES)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_CPU_MIGRATIONS)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_PAGE_FAULTS_MIN)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_PAGE_FAULTS_MAJ)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_ALIGNMENT_FAULTS)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_EMULATION_FAULTS)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_DUMMY)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_BPF_OUTPUT)
+ ENUM_ID_TO_STR_CASE(PERF_COUNT_SW_CGROUP_SWITCHES)
+ default:
+ return NULL;
+ }
+}
+#undef ENUM_ID_TO_STR_CASE
+
+#define PRINT_ID(_s, _f) \
+do { \
+ const char *__s = _s; \
+ if (__s == NULL) \
+ snprintf(buf, size, _f, value); \
+ else \
+ snprintf(buf, size, _f" (%s)", value, __s); \
+} while (0)
+#define print_id_unsigned(_s) PRINT_ID(_s, "%"PRIu64)
+#define print_id_hex(_s) PRINT_ID(_s, "%#"PRIx64)
+
+static void __p_type_id(char *buf, size_t size, u64 value)
+{
+ print_id_unsigned(stringify_perf_type_id(value));
+}
+
+static void __p_config_hw_id(char *buf, size_t size, u64 value)
+{
+ print_id_hex(stringify_perf_hw_id(value));
+}
+
+static void __p_config_sw_id(char *buf, size_t size, u64 value)
+{
+ print_id_hex(stringify_perf_sw_id(value));
+}
+
+static void __p_config_hw_cache_id(char *buf, size_t size, u64 value)
+{
+ const char *hw_cache_str = stringify_perf_hw_cache_id(value & 0xff);
+ const char *hw_cache_op_str =
+ stringify_perf_hw_cache_op_id((value & 0xff00) >> 8);
+ const char *hw_cache_op_result_str =
+ stringify_perf_hw_cache_op_result_id((value & 0xff0000) >> 16);
+
+ if (hw_cache_str == NULL || hw_cache_op_str == NULL ||
+ hw_cache_op_result_str == NULL) {
+ snprintf(buf, size, "%#"PRIx64, value);
+ } else {
+ snprintf(buf, size, "%#"PRIx64" (%s | %s | %s)", value,
+ hw_cache_op_result_str, hw_cache_op_str, hw_cache_str);
+ }
+}
+
+#ifdef HAVE_LIBTRACEEVENT
+static void __p_config_tracepoint_id(char *buf, size_t size, u64 value)
+{
+ char *str = tracepoint_id_to_name(value);
+
+ print_id_hex(str);
+ free(str);
+}
+#endif
+
+static void __p_config_id(char *buf, size_t size, u32 type, u64 value)
+{
+ switch (type) {
+ case PERF_TYPE_HARDWARE:
+ return __p_config_hw_id(buf, size, value);
+ case PERF_TYPE_SOFTWARE:
+ return __p_config_sw_id(buf, size, value);
+ case PERF_TYPE_HW_CACHE:
+ return __p_config_hw_cache_id(buf, size, value);
+ case PERF_TYPE_TRACEPOINT:
+#ifdef HAVE_LIBTRACEEVENT
+ return __p_config_tracepoint_id(buf, size, value);
+#endif
+ case PERF_TYPE_RAW:
+ case PERF_TYPE_BREAKPOINT:
+ default:
+ snprintf(buf, size, "%#"PRIx64, value);
+ return;
+ }
+}
+
#define BUF_SIZE 1024
#define p_hex(val) snprintf(buf, BUF_SIZE, "%#"PRIx64, (uint64_t)(val))
@@ -79,16 +245,18 @@ static void __p_read_format(char *buf, size_t size, u64 value)
#define p_sample_type(val) __p_sample_type(buf, BUF_SIZE, val)
#define p_branch_sample_type(val) __p_branch_sample_type(buf, BUF_SIZE, val)
#define p_read_format(val) __p_read_format(buf, BUF_SIZE, val)
+#define p_type_id(val) __p_type_id(buf, BUF_SIZE, val)
+#define p_config_id(val) __p_config_id(buf, BUF_SIZE, attr->type, val)
-#define PRINT_ATTRn(_n, _f, _p) \
+#define PRINT_ATTRn(_n, _f, _p, _a) \
do { \
- if (attr->_f) { \
+ if (_a || attr->_f) { \
_p(attr->_f); \
ret += attr__fprintf(fp, _n, buf, priv);\
} \
} while (0)
-#define PRINT_ATTRf(_f, _p) PRINT_ATTRn(#_f, _f, _p)
+#define PRINT_ATTRf(_f, _p) PRINT_ATTRn(#_f, _f, _p, false)
int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
attr__fprintf_f attr__fprintf, void *priv)
@@ -96,10 +264,10 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
char buf[BUF_SIZE];
int ret = 0;
- PRINT_ATTRf(type, p_unsigned);
+ PRINT_ATTRn("type", type, p_type_id, true);
PRINT_ATTRf(size, p_unsigned);
- PRINT_ATTRf(config, p_hex);
- PRINT_ATTRn("{ sample_period, sample_freq }", sample_period, p_unsigned);
+ PRINT_ATTRn("config", config, p_config_id, true);
+ PRINT_ATTRn("{ sample_period, sample_freq }", sample_period, p_unsigned, false);
PRINT_ATTRf(sample_type, p_sample_type);
PRINT_ATTRf(read_format, p_read_format);
@@ -141,10 +309,10 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
PRINT_ATTRf(remove_on_exec, p_unsigned);
PRINT_ATTRf(sigtrap, p_unsigned);
- PRINT_ATTRn("{ wakeup_events, wakeup_watermark }", wakeup_events, p_unsigned);
+ PRINT_ATTRn("{ wakeup_events, wakeup_watermark }", wakeup_events, p_unsigned, false);
PRINT_ATTRf(bp_type, p_unsigned);
- PRINT_ATTRn("{ bp_addr, config1 }", bp_addr, p_hex);
- PRINT_ATTRn("{ bp_len, config2 }", bp_len, p_hex);
+ PRINT_ATTRn("{ bp_addr, config1 }", bp_addr, p_hex, false);
+ PRINT_ATTRn("{ bp_len, config2 }", bp_len, p_hex, false);
PRINT_ATTRf(branch_sample_type, p_branch_sample_type);
PRINT_ATTRf(sample_regs_user, p_hex);
PRINT_ATTRf(sample_stack_user, p_unsigned);
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 64fa568a5426..7f984a7f16ca 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -928,6 +928,31 @@ err:
return NULL;
}
+/* Creates the PMU when sysfs scanning fails. */
+struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pmus)
+{
+ struct perf_pmu *pmu = zalloc(sizeof(*pmu));
+
+ if (!pmu)
+ return NULL;
+
+ pmu->name = strdup("cpu");
+ if (!pmu->name) {
+ free(pmu);
+ return NULL;
+ }
+
+ pmu->is_core = true;
+ pmu->type = PERF_TYPE_RAW;
+ pmu->cpus = cpu_map__online();
+
+ INIT_LIST_HEAD(&pmu->format);
+ INIT_LIST_HEAD(&pmu->aliases);
+ INIT_LIST_HEAD(&pmu->caps);
+ list_add_tail(&pmu->list, core_pmus);
+ return pmu;
+}
+
void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu)
{
struct perf_pmu_format *format;
@@ -1427,7 +1452,7 @@ bool perf_pmu__supports_legacy_cache(const struct perf_pmu *pmu)
bool perf_pmu__auto_merge_stats(const struct perf_pmu *pmu)
{
- return pmu->is_core && perf_pmus__num_core_pmus() == 1;
+ return !pmu->is_core || perf_pmus__num_core_pmus() == 1;
}
bool perf_pmu__have_event(const struct perf_pmu *pmu, const char *name)
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 8807a624e918..203b92860e3c 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -286,6 +286,7 @@ int perf_pmu__event_source_devices_fd(void);
int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename, int flags);
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);
#endif /* __PMU_H */
diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c
index 0866dee3fc62..3cd9de42139e 100644
--- a/tools/perf/util/pmus.c
+++ b/tools/perf/util/pmus.c
@@ -153,7 +153,12 @@ static void pmu_read_sysfs(bool core_only)
closedir(dir);
if (core_only) {
- read_sysfs_core_pmus = true;
+ if (!list_empty(&core_pmus))
+ read_sysfs_core_pmus = true;
+ else {
+ if (perf_pmu__create_placeholder_core_pmu(&core_pmus))
+ read_sysfs_core_pmus = true;
+ }
} else {
read_sysfs_core_pmus = true;
read_sysfs_all_pmus = true;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 6e2110d605fb..16822a8a540f 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -74,7 +74,6 @@ int init_probe_symbol_maps(bool user_only)
{
int ret;
- symbol_conf.sort_by_name = true;
symbol_conf.allow_aliases = true;
ret = symbol__init(NULL);
if (ret < 0) {
@@ -382,6 +381,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
struct symbol *sym;
u64 address = 0;
int ret = -ENOENT;
+ size_t idx;
/* This can work only for function-name based one */
if (!pp->function || pp->file)
@@ -392,7 +392,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
return -EINVAL;
/* Find the address of given function */
- map__for_each_symbol_by_name(map, pp->function, sym) {
+ map__for_each_symbol_by_name(map, pp->function, sym, idx) {
if (uprobes) {
address = sym->start;
if (sym->type == STT_GNU_IFUNC)
@@ -3738,7 +3738,6 @@ out:
int show_available_funcs(const char *target, struct nsinfo *nsi,
struct strfilter *_filter, bool user)
{
- struct rb_node *nd;
struct map *map;
struct dso *dso;
int ret;
@@ -3767,17 +3766,16 @@ int show_available_funcs(const char *target, struct nsinfo *nsi,
goto end;
}
dso = map__dso(map);
- if (!dso__sorted_by_name(dso))
- dso__sort_by_name(dso);
+ dso__sort_by_name(dso);
/* Show all (filtered) symbols */
setup_pager();
- for (nd = rb_first_cached(&dso->symbol_names); nd; nd = rb_next(nd)) {
- struct symbol_name_rb_node *pos = rb_entry(nd, struct symbol_name_rb_node, rb_node);
+ for (size_t i = 0; i < dso->symbol_names_len; i++) {
+ struct symbol *pos = dso->symbol_names[i];
- if (strfilter__compare(_filter, pos->sym.name))
- printf("%s\n", pos->sym.name);
+ if (strfilter__compare(_filter, pos->name))
+ printf("%s\n", pos->name);
}
end:
map__put(map);
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index a7b2cb05dc86..4eed8ec23994 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -124,6 +124,14 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
}
/*
+ * Add this one here not to drag util/trace-event-info.c
+ */
+char *tracepoint_id_to_name(u64 config)
+{
+ return NULL;
+}
+
+/*
* XXX: All these evsel destructors need some better mechanism, like a linked
* list of destructors registered when the relevant code indeed is used instead
* of having more and more calls in perf_evsel__delete(). -- acme
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index d275d3bef7d5..f849f9ef68e6 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -440,38 +440,35 @@ static struct symbol *symbols__next(struct symbol *sym)
return NULL;
}
-static void symbols__insert_by_name(struct rb_root_cached *symbols, struct symbol *sym)
+static int symbols__sort_name_cmp(const void *vlhs, const void *vrhs)
{
- struct rb_node **p = &symbols->rb_root.rb_node;
- struct rb_node *parent = NULL;
- struct symbol_name_rb_node *symn, *s;
- bool leftmost = true;
+ const struct symbol *lhs = *((const struct symbol **)vlhs);
+ const struct symbol *rhs = *((const struct symbol **)vrhs);
- symn = container_of(sym, struct symbol_name_rb_node, sym);
-
- while (*p != NULL) {
- parent = *p;
- s = rb_entry(parent, struct symbol_name_rb_node, rb_node);
- if (strcmp(sym->name, s->sym.name) < 0)
- p = &(*p)->rb_left;
- else {
- p = &(*p)->rb_right;
- leftmost = false;
- }
- }
- rb_link_node(&symn->rb_node, parent, p);
- rb_insert_color_cached(&symn->rb_node, symbols, leftmost);
+ return strcmp(lhs->name, rhs->name);
}
-static void symbols__sort_by_name(struct rb_root_cached *symbols,
- struct rb_root_cached *source)
+static struct symbol **symbols__sort_by_name(struct rb_root_cached *source, size_t *len)
{
struct rb_node *nd;
+ struct symbol **result;
+ size_t i = 0, size = 0;
+
+ for (nd = rb_first_cached(source); nd; nd = rb_next(nd))
+ size++;
+
+ result = malloc(sizeof(*result) * size);
+ if (!result)
+ return NULL;
for (nd = rb_first_cached(source); nd; nd = rb_next(nd)) {
struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
- symbols__insert_by_name(symbols, pos);
+
+ result[i++] = pos;
}
+ qsort(result, size, sizeof(*result), symbols__sort_name_cmp);
+ *len = size;
+ return result;
}
int symbol__match_symbol_name(const char *name, const char *str,
@@ -491,48 +488,53 @@ int symbol__match_symbol_name(const char *name, const char *str,
return arch__compare_symbol_names(name, str);
}
-static struct symbol *symbols__find_by_name(struct rb_root_cached *symbols,
+static struct symbol *symbols__find_by_name(struct symbol *symbols[],
+ size_t symbols_len,
const char *name,
- enum symbol_tag_include includes)
+ enum symbol_tag_include includes,
+ size_t *found_idx)
{
- struct rb_node *n;
- struct symbol_name_rb_node *s = NULL;
+ size_t i, lower = 0, upper = symbols_len;
+ struct symbol *s = NULL;
- if (symbols == NULL)
- return NULL;
+ if (found_idx)
+ *found_idx = SIZE_MAX;
- n = symbols->rb_root.rb_node;
+ if (!symbols_len)
+ return NULL;
- while (n) {
+ while (lower < upper) {
int cmp;
- s = rb_entry(n, struct symbol_name_rb_node, rb_node);
- cmp = symbol__match_symbol_name(s->sym.name, name, includes);
+ i = (lower + upper) / 2;
+ cmp = symbol__match_symbol_name(symbols[i]->name, name, includes);
if (cmp > 0)
- n = n->rb_left;
+ upper = i;
else if (cmp < 0)
- n = n->rb_right;
- else
+ lower = i + 1;
+ else {
+ if (found_idx)
+ *found_idx = i;
+ s = symbols[i];
break;
+ }
}
-
- if (n == NULL)
- return NULL;
-
- if (includes != SYMBOL_TAG_INCLUDE__DEFAULT_ONLY)
+ if (s && includes != SYMBOL_TAG_INCLUDE__DEFAULT_ONLY) {
/* return first symbol that has same name (if any) */
- for (n = rb_prev(n); n; n = rb_prev(n)) {
- struct symbol_name_rb_node *tmp;
-
- tmp = rb_entry(n, struct symbol_name_rb_node, rb_node);
- if (arch__compare_symbol_names(tmp->sym.name, s->sym.name))
+ for (; i > 0; i--) {
+ struct symbol *tmp = symbols[i - 1];
+
+ if (!arch__compare_symbol_names(tmp->name, s->name)) {
+ if (found_idx)
+ *found_idx = i - 1;
+ s = tmp;
+ } else
break;
-
- s = tmp;
}
-
- return &s->sym;
+ }
+ assert(!found_idx || !s || s == symbols[*found_idx]);
+ return s;
}
void dso__reset_find_symbol_cache(struct dso *dso)
@@ -590,31 +592,41 @@ struct symbol *dso__next_symbol(struct symbol *sym)
return symbols__next(sym);
}
-struct symbol *symbol__next_by_name(struct symbol *sym)
+struct symbol *dso__next_symbol_by_name(struct dso *dso, size_t *idx)
{
- struct symbol_name_rb_node *s = container_of(sym, struct symbol_name_rb_node, sym);
- struct rb_node *n = rb_next(&s->rb_node);
+ if (*idx + 1 >= dso->symbol_names_len)
+ return NULL;
- return n ? &rb_entry(n, struct symbol_name_rb_node, rb_node)->sym : NULL;
+ ++*idx;
+ return dso->symbol_names[*idx];
}
/*
* Returns first symbol that matched with @name.
*/
-struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name)
+struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name, size_t *idx)
{
- struct symbol *s = symbols__find_by_name(&dso->symbol_names, name,
- SYMBOL_TAG_INCLUDE__NONE);
+ struct symbol *s = symbols__find_by_name(dso->symbol_names, dso->symbol_names_len,
+ name, SYMBOL_TAG_INCLUDE__NONE, idx);
if (!s)
- s = symbols__find_by_name(&dso->symbol_names, name,
- SYMBOL_TAG_INCLUDE__DEFAULT_ONLY);
+ s = symbols__find_by_name(dso->symbol_names, dso->symbol_names_len,
+ name, SYMBOL_TAG_INCLUDE__DEFAULT_ONLY, idx);
return s;
}
void dso__sort_by_name(struct dso *dso)
{
- dso__set_sorted_by_name(dso);
- return symbols__sort_by_name(&dso->symbol_names, &dso->symbols);
+ mutex_lock(&dso->lock);
+ if (!dso__sorted_by_name(dso)) {
+ size_t len;
+
+ dso->symbol_names = symbols__sort_by_name(&dso->symbols, &len);
+ if (dso->symbol_names) {
+ dso->symbol_names_len = len;
+ dso__set_sorted_by_name(dso);
+ }
+ }
+ mutex_unlock(&dso->lock);
}
/*
@@ -2656,10 +2668,6 @@ int symbol__init(struct perf_env *env)
symbol__elf_init();
- if (symbol_conf.sort_by_name)
- symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) -
- sizeof(struct symbol));
-
if (symbol_conf.try_vmlinux_path && vmlinux_path__init(env) < 0)
return -1;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 5ca8665dd2c1..af87c46b3f89 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -43,8 +43,7 @@ Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
/**
* A symtab entry. When allocated this may be preceded by an annotation (see
- * symbol__annotation), a browser_index (see symbol__browser_index) and rb_node
- * to sort by name (see struct symbol_name_rb_node).
+ * symbol__annotation) and/or a browser_index (see symbol__browser_index).
*/
struct symbol {
struct rb_node rb_node;
@@ -95,11 +94,6 @@ static inline size_t symbol__size(const struct symbol *sym)
struct strlist;
struct intlist;
-struct symbol_name_rb_node {
- struct rb_node rb_node;
- struct symbol sym;
-};
-
static inline int __symbol__join_symfs(char *bf, size_t size, const char *path)
{
return path__join(bf, size, symbol_conf.symfs, path);
@@ -136,9 +130,9 @@ void dso__delete_symbol(struct dso *dso,
struct symbol *dso__find_symbol(struct dso *dso, u64 addr);
struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr);
-struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name);
-struct symbol *symbol__next_by_name(struct symbol *sym);
+struct symbol *dso__next_symbol_by_name(struct dso *dso, size_t *idx);
+struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name, size_t *idx);
struct symbol *dso__first_symbol(struct dso *dso);
struct symbol *dso__last_symbol(struct dso *dso);
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h
index f26f81eb8252..0b589570d1d0 100644
--- a/tools/perf/util/symbol_conf.h
+++ b/tools/perf/util/symbol_conf.h
@@ -18,7 +18,6 @@ struct symbol_conf {
show_kernel_path,
use_modules,
allow_aliases,
- sort_by_name,
show_nr_samples,
show_total_period,
use_callchain,
diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c
index d9e5ad040b6a..088f4abf230f 100644
--- a/tools/perf/util/symbol_fprintf.c
+++ b/tools/perf/util/symbol_fprintf.c
@@ -63,13 +63,11 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso,
FILE *fp)
{
size_t ret = 0;
- struct rb_node *nd;
- struct symbol_name_rb_node *pos;
- for (nd = rb_first_cached(&dso->symbol_names); nd; nd = rb_next(nd)) {
- pos = rb_entry(nd, struct symbol_name_rb_node, rb_node);
- ret += fprintf(fp, "%s\n", pos->sym.name);
- }
+ for (size_t i = 0; i < dso->symbol_names_len; i++) {
+ struct symbol *pos = dso->symbol_names[i];
+ ret += fprintf(fp, "%s\n", pos->name);
+ }
return ret;
}
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index c24b3a15e319..319ccf09a435 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -466,6 +466,18 @@ next:
return NULL;
}
+char *tracepoint_id_to_name(u64 config)
+{
+ struct tracepoint_path *path = tracepoint_id_to_path(config);
+ char *buf = NULL;
+
+ if (path && asprintf(&buf, "%s:%s", path->system, path->name) < 0)
+ buf = NULL;
+
+ put_tracepoints_path(path);
+ return buf;
+}
+
static struct tracepoint_path *tracepoint_name_to_path(const char *name)
{
struct tracepoint_path *path = zalloc(sizeof(*path));
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index a0cff184b1cd..a69ee29419f3 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -62,6 +62,12 @@ unsigned long long eval_flag(const char *flag);
int read_tracing_data(int fd, struct list_head *pattrs);
+/*
+ * Return the tracepoint name in the format "subsystem:event_name",
+ * callers should free the returned string.
+ */
+char *tracepoint_id_to_name(u64 config);
+
struct tracing_data {
/* size is only valid if temp is 'true' */
ssize_t size;
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 83eea968482e..2a96df4c8d42 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -66,9 +66,13 @@ static int __report_module(struct addr_location *al, u64 ip,
mod = 0;
}
- if (!mod)
- mod = dwfl_report_elf(ui->dwfl, dso->short_name, dso->long_name, -1,
+ if (!mod) {
+ char filename[PATH_MAX];
+
+ __symbol__join_symfs(filename, sizeof(filename), dso->long_name);
+ mod = dwfl_report_elf(ui->dwfl, dso->short_name, filename, -1,
map__start(al->map) - map__pgoff(al->map), false);
+ }
if (!mod) {
char filename[PATH_MAX];