summaryrefslogtreecommitdiff
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/Build19
-rw-r--r--tools/perf/util/amd-sample-raw.c14
-rw-r--r--tools/perf/util/annotate.c85
-rw-r--r--tools/perf/util/annotate.h9
-rw-r--r--tools/perf/util/arm-spe-decoder/arm-spe-decoder.c30
-rw-r--r--tools/perf/util/arm-spe-decoder/arm-spe-decoder.h47
-rw-r--r--tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c9
-rw-r--r--tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h3
-rw-r--r--tools/perf/util/arm-spe.c28
-rw-r--r--tools/perf/util/auxtrace.c7
-rw-r--r--tools/perf/util/block-info.c4
-rw-r--r--tools/perf/util/block-range.c6
-rw-r--r--tools/perf/util/bpf-event.c76
-rw-r--r--tools/perf/util/bpf-filter.c197
-rw-r--r--tools/perf/util/bpf-filter.h49
-rw-r--r--tools/perf/util/bpf-filter.l159
-rw-r--r--tools/perf/util/bpf-filter.y78
-rw-r--r--tools/perf/util/bpf-loader.c18
-rw-r--r--tools/perf/util/bpf_counter.c28
-rw-r--r--tools/perf/util/bpf_lock_contention.c54
-rw-r--r--tools/perf/util/bpf_skel/.gitignore2
-rw-r--r--tools/perf/util/bpf_skel/lock_contention.bpf.c136
-rw-r--r--tools/perf/util/bpf_skel/lock_data.h17
-rw-r--r--tools/perf/util/bpf_skel/sample-filter.h27
-rw-r--r--tools/perf/util/bpf_skel/sample_filter.bpf.c196
-rw-r--r--tools/perf/util/bpf_skel/vmlinux.h173
-rw-r--r--tools/perf/util/build-id.c2
-rw-r--r--tools/perf/util/callchain.c28
-rw-r--r--tools/perf/util/cloexec.c13
-rw-r--r--tools/perf/util/cpumap.c43
-rw-r--r--tools/perf/util/cpumap.h3
-rw-r--r--tools/perf/util/cputopo.c14
-rw-r--r--tools/perf/util/cputopo.h5
-rw-r--r--tools/perf/util/cs-etm-base.c3
-rw-r--r--tools/perf/util/cs-etm-decoder/cs-etm-decoder.c7
-rw-r--r--tools/perf/util/cs-etm-decoder/cs-etm-decoder.h8
-rw-r--r--tools/perf/util/cs-etm.c568
-rw-r--r--tools/perf/util/cs-etm.h20
-rw-r--r--tools/perf/util/data-convert-json.c10
-rw-r--r--tools/perf/util/db-export.c16
-rw-r--r--tools/perf/util/demangle-cxx.cpp49
-rw-r--r--tools/perf/util/demangle-cxx.h16
-rw-r--r--tools/perf/util/dlfilter.c28
-rw-r--r--tools/perf/util/dso.c13
-rw-r--r--tools/perf/util/dso.h2
-rw-r--r--tools/perf/util/dsos.c3
-rw-r--r--tools/perf/util/env.c2
-rw-r--r--tools/perf/util/event.c29
-rw-r--r--tools/perf/util/event.h3
-rw-r--r--tools/perf/util/evlist.c42
-rw-r--r--tools/perf/util/evlist.h8
-rw-r--r--tools/perf/util/evsel.c46
-rw-r--r--tools/perf/util/evsel.h26
-rw-r--r--tools/perf/util/evsel_fprintf.c13
-rw-r--r--tools/perf/util/expr.c49
-rw-r--r--tools/perf/util/expr.y12
-rw-r--r--tools/perf/util/ftrace.h1
-rw-r--r--tools/perf/util/genelf_debug.c46
-rw-r--r--tools/perf/util/header.c3
-rw-r--r--tools/perf/util/header.h2
-rw-r--r--tools/perf/util/hist.c49
-rw-r--r--tools/perf/util/hist.h4
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-decoder.c8
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c18
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h2
-rw-r--r--tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c2
-rw-r--r--tools/perf/util/intel-pt.c63
-rw-r--r--tools/perf/util/jitdump.c7
-rw-r--r--tools/perf/util/kvm-stat.h73
-rw-r--r--tools/perf/util/lock-contention.h10
-rw-r--r--tools/perf/util/machine.c257
-rw-r--r--tools/perf/util/map.c219
-rw-r--r--tools/perf/util/map.h144
-rw-r--r--tools/perf/util/maps.c317
-rw-r--r--tools/perf/util/maps.h72
-rw-r--r--tools/perf/util/mem-events.c90
-rw-r--r--tools/perf/util/metricgroup.c200
-rw-r--r--tools/perf/util/metricgroup.h5
-rw-r--r--tools/perf/util/namespaces.c141
-rw-r--r--tools/perf/util/namespaces.h3
-rw-r--r--tools/perf/util/ordered-events.c2
-rw-r--r--tools/perf/util/parse-events.c295
-rw-r--r--tools/perf/util/parse-events.h15
-rw-r--r--tools/perf/util/parse-events.l1
-rw-r--r--tools/perf/util/parse-events.y28
-rw-r--r--tools/perf/util/pfm.c1
-rw-r--r--tools/perf/util/pmu.c461
-rw-r--r--tools/perf/util/pmu.h23
-rw-r--r--tools/perf/util/pmu.l17
-rw-r--r--tools/perf/util/pmu.y5
-rw-r--r--tools/perf/util/print-events.c50
-rw-r--r--tools/perf/util/print-events.h1
-rw-r--r--tools/perf/util/probe-event.c62
-rw-r--r--tools/perf/util/probe-finder.c2
-rw-r--r--tools/perf/util/python.c21
-rw-r--r--tools/perf/util/record.h1
-rw-r--r--tools/perf/util/sample.h13
-rw-r--r--tools/perf/util/scripting-engines/Build2
-rw-r--r--tools/perf/util/scripting-engines/trace-event-perl.c10
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c101
-rw-r--r--tools/perf/util/session.c5
-rw-r--r--tools/perf/util/smt.c11
-rw-r--r--tools/perf/util/smt.h12
-rw-r--r--tools/perf/util/sort.c126
-rw-r--r--tools/perf/util/sort.h3
-rw-r--r--tools/perf/util/srcline.c183
-rw-r--r--tools/perf/util/stat-display.c119
-rw-r--r--tools/perf/util/stat-shadow.c1287
-rw-r--r--tools/perf/util/stat.c74
-rw-r--r--tools/perf/util/stat.h96
-rw-r--r--tools/perf/util/strfilter.c2
-rw-r--r--tools/perf/util/string.c2
-rw-r--r--tools/perf/util/symbol-elf.c94
-rw-r--r--tools/perf/util/symbol.c314
-rw-r--r--tools/perf/util/symbol_conf.h2
-rw-r--r--tools/perf/util/symbol_fprintf.c2
-rw-r--r--tools/perf/util/synthetic-events.c36
-rw-r--r--tools/perf/util/thread-stack.c4
-rw-r--r--tools/perf/util/thread.c69
-rw-r--r--tools/perf/util/top.c2
-rw-r--r--tools/perf/util/topdown.c68
-rw-r--r--tools/perf/util/topdown.h11
-rw-r--r--tools/perf/util/trace-event-scripting.c9
-rw-r--r--tools/perf/util/tracepoint.c1
-rw-r--r--tools/perf/util/unwind-libdw.c20
-rw-r--r--tools/perf/util/unwind-libunwind-local.c68
-rw-r--r--tools/perf/util/unwind-libunwind.c39
-rw-r--r--tools/perf/util/usage.c6
-rw-r--r--tools/perf/util/util.c21
-rw-r--r--tools/perf/util/util.h8
-rw-r--r--tools/perf/util/vdso.c7
131 files changed, 4981 insertions, 3119 deletions
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 918b501f9bd8..bd18fe5f2719 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -78,7 +78,7 @@ perf-y += pmu-bison.o
perf-y += pmu-hybrid.o
perf-y += svghelper.o
perf-$(CONFIG_LIBTRACEEVENT) += trace-event-info.o
-perf-$(CONFIG_LIBTRACEEVENT) += trace-event-scripting.o
+perf-y += trace-event-scripting.o
perf-$(CONFIG_LIBTRACEEVENT) += trace-event.o
perf-$(CONFIG_LIBTRACEEVENT) += trace-event-parse.o
perf-$(CONFIG_LIBTRACEEVENT) += trace-event-read.o
@@ -154,6 +154,9 @@ perf-$(CONFIG_PERF_BPF_SKEL) += bpf_counter.o
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_counter_cgroup.o
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_ftrace.o
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_off_cpu.o
+perf-$(CONFIG_PERF_BPF_SKEL) += bpf-filter.o
+perf-$(CONFIG_PERF_BPF_SKEL) += bpf-filter-flex.o
+perf-$(CONFIG_PERF_BPF_SKEL) += bpf-filter-bison.o
ifeq ($(CONFIG_LIBTRACEEVENT),y)
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_lock_contention.o
@@ -211,6 +214,7 @@ perf-$(CONFIG_ZSTD) += zstd.o
perf-$(CONFIG_LIBCAP) += cap.o
+perf-y += demangle-cxx.o
perf-y += demangle-ocaml.o
perf-y += demangle-java.o
perf-y += demangle-rust.o
@@ -266,6 +270,16 @@ $(OUTPUT)util/pmu-bison.c $(OUTPUT)util/pmu-bison.h: util/pmu.y
$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) \
-o $(OUTPUT)util/pmu-bison.c -p perf_pmu_
+$(OUTPUT)util/bpf-filter-flex.c $(OUTPUT)util/bpf-filter-flex.h: util/bpf-filter.l $(OUTPUT)util/bpf-filter-bison.c
+ $(call rule_mkdir)
+ $(Q)$(call echo-cmd,flex)$(FLEX) -o $(OUTPUT)util/bpf-filter-flex.c \
+ --header-file=$(OUTPUT)util/bpf-filter-flex.h $(PARSER_DEBUG_FLEX) $<
+
+$(OUTPUT)util/bpf-filter-bison.c $(OUTPUT)util/bpf-filter-bison.h: util/bpf-filter.y
+ $(call rule_mkdir)
+ $(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
@@ -279,6 +293,7 @@ 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)
@@ -290,10 +305,12 @@ endif
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)
+CFLAGS_bpf-filter-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
$(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c
$(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c
$(OUTPUT)util/expr.o: $(OUTPUT)util/expr-flex.c $(OUTPUT)util/expr-bison.c
+$(OUTPUT)util/bpf-filter.o: $(OUTPUT)util/bpf-filter-flex.c $(OUTPUT)util/bpf-filter-bison.c
CFLAGS_bitmap.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
CFLAGS_find_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
diff --git a/tools/perf/util/amd-sample-raw.c b/tools/perf/util/amd-sample-raw.c
index b0e70ce9d87a..6a6ddba76c75 100644
--- a/tools/perf/util/amd-sample-raw.c
+++ b/tools/perf/util/amd-sample-raw.c
@@ -105,17 +105,17 @@ static void pr_ibs_op_data2_extended(union ibs_op_data2 reg)
static const char * const data_src_str[] = {
"",
" DataSrc 1=Local L3 or other L1/L2 in CCX",
- " DataSrc 2=A peer cache in a near CCX",
- " DataSrc 3=Data returned from DRAM",
+ " DataSrc 2=Another CCX cache in the same NUMA node",
+ " DataSrc 3=DRAM",
" DataSrc 4=(reserved)",
- " DataSrc 5=A peer cache in a far CCX",
- " DataSrc 6=DRAM address map with \"long latency\" bit set",
- " DataSrc 7=Data returned from MMIO/Config/PCI/APIC",
- " DataSrc 8=Extension Memory (S-Link, GenZ, etc)",
+ " DataSrc 5=Another CCX cache in a different NUMA node",
+ " DataSrc 6=Long-latency DIMM",
+ " DataSrc 7=MMIO/Config/PCI/APIC",
+ " DataSrc 8=Extension Memory",
" DataSrc 9=(reserved)",
" DataSrc 10=(reserved)",
" DataSrc 11=(reserved)",
- " DataSrc 12=Peer Agent Memory",
+ " DataSrc 12=Coherent Memory of a different processor type",
/* 13 to 31 are reserved. Avoid printing them. */
};
int data_src = (reg.data_src_hi << 3) | reg.data_src_lo;
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 0cc7710f32da..11992cfe271c 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -38,6 +38,7 @@
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/zalloc.h>
#include <subcmd/parse-options.h>
#include <subcmd/run-command.h>
@@ -55,14 +56,6 @@
#include <linux/ctype.h>
-struct annotation_options annotation__default_options = {
- .use_offset = true,
- .jump_arrows = true,
- .annotate_src = true,
- .offset_level = ANNOTATION__OFFSET_JUMP_TARGETS,
- .percent_type = PERCENT_PERIOD_LOCAL,
-};
-
static regex_t file_lineno;
static struct ins_ops *ins__find(struct arch *arch, const char *name);
@@ -288,7 +281,7 @@ find_target:
target.addr = map__objdump_2mem(map, ops->target.addr);
if (maps__find_ams(ms->maps, &target) == 0 &&
- map__rip_2objdump(target.ms.map, map->map_ip(target.ms.map, target.addr)) == ops->target.addr)
+ map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
ops->target.sym = target.ms.sym;
return 0;
@@ -392,8 +385,8 @@ static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_s
}
target.addr = map__objdump_2mem(map, ops->target.addr);
- start = map->unmap_ip(map, sym->start),
- end = map->unmap_ip(map, sym->end);
+ start = map__unmap_ip(map, sym->start);
+ end = map__unmap_ip(map, sym->end);
ops->target.outside = target.addr < start || target.addr > end;
@@ -416,7 +409,7 @@ static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_s
* the symbol searching and disassembly should be done.
*/
if (maps__find_ams(ms->maps, &target) == 0 &&
- map__rip_2objdump(target.ms.map, map->map_ip(target.ms.map, target.addr)) == ops->target.addr)
+ map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
ops->target.sym = target.ms.sym;
if (!ops->target.outside) {
@@ -897,7 +890,7 @@ static int __symbol__inc_addr_samples(struct map_symbol *ms,
unsigned offset;
struct sym_hist *h;
- pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, ms->map->unmap_ip(ms->map, addr));
+ pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map__unmap_ip(ms->map, addr));
if ((addr < sym->start || addr >= sym->end) &&
(addr != sym->end || sym->start != sym->end)) {
@@ -1024,13 +1017,13 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
if (start &&
(start->ms.sym == ams->ms.sym ||
(ams->ms.sym &&
- start->addr == ams->ms.sym->start + ams->ms.map->start)))
+ start->addr == ams->ms.sym->start + map__start(ams->ms.map))))
saddr = start->al_addr;
if (saddr == 0)
pr_debug2("BB with bad start: addr %"PRIx64" start %"PRIx64" sym %"PRIx64" saddr %"PRIx64"\n",
ams->addr,
start ? start->addr : 0,
- ams->ms.sym ? ams->ms.sym->start + ams->ms.map->start : 0,
+ ams->ms.sym ? ams->ms.sym->start + map__start(ams->ms.map) : 0,
saddr);
err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles);
if (err)
@@ -1601,7 +1594,7 @@ static void delete_last_nop(struct symbol *sym)
int symbol__strerror_disassemble(struct map_symbol *ms, int errnum, char *buf, size_t buflen)
{
- struct dso *dso = ms->map->dso;
+ struct dso *dso = map__dso(ms->map);
BUG_ON(buflen == 0);
@@ -1707,8 +1700,7 @@ fallback:
mutex_lock(&dso->lock);
if (access(filename, R_OK) && errno == ENOENT && dso->nsinfo) {
- char *new_name = filename_with_chroot(dso->nsinfo->pid,
- filename);
+ char *new_name = dso__filename_with_chroot(dso, filename);
if (new_name) {
strlcpy(filename, new_name, filename_size);
free(new_name);
@@ -1743,7 +1735,7 @@ static int symbol__disassemble_bpf(struct symbol *sym,
struct map *map = args->ms.map;
struct perf_bpil *info_linear;
struct disassemble_info info;
- struct dso *dso = map->dso;
+ struct dso *dso = map__dso(map);
int pc = 0, count, sub_id;
struct btf *btf = NULL;
char tpath[PATH_MAX];
@@ -1897,7 +1889,7 @@ symbol__disassemble_bpf_image(struct symbol *sym,
if (dl)
annotation_line__add(&dl->al, &notes->src->source);
- free(args->line);
+ zfree(&args->line);
return 0;
}
@@ -1966,7 +1958,7 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
{
struct annotation_options *opts = args->options;
struct map *map = args->ms.map;
- struct dso *dso = map->dso;
+ struct dso *dso = map__dso(map);
char *command;
FILE *file;
char symfs_filename[PATH_MAX];
@@ -1993,8 +1985,8 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
return err;
pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
- symfs_filename, sym->name, map->unmap_ip(map, sym->start),
- map->unmap_ip(map, sym->end));
+ symfs_filename, sym->name, map__unmap_ip(map, sym->start),
+ map__unmap_ip(map, sym->end));
pr_debug("annotating [%p] %30s : [%p] %30s\n",
dso, dso->long_name, sym, sym->name);
@@ -2411,7 +2403,7 @@ int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel,
{
struct map *map = ms->map;
struct symbol *sym = ms->sym;
- struct dso *dso = map->dso;
+ struct dso *dso = map__dso(map);
char *filename;
const char *d_filename;
const char *evsel_name = evsel__name(evsel);
@@ -2594,7 +2586,7 @@ int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel,
}
fprintf(fp, "%s() %s\nEvent: %s\n\n",
- ms->sym->name, ms->map->dso->long_name, ev_name);
+ ms->sym->name, map__dso(ms->map)->long_name, ev_name);
symbol__annotate_fprintf2(ms->sym, fp, opts);
fclose(fp);
@@ -2820,7 +2812,7 @@ static void annotation__calc_lines(struct annotation *notes, struct map *map,
if (percent_max <= 0.5)
continue;
- al->path = get_srcline(map->dso, notes->start + al->offset, NULL,
+ al->path = get_srcline(map__dso(map), notes->start + al->offset, NULL,
false, true, notes->start + al->offset);
insert_source_line(&tmp_root, al, opts);
}
@@ -2839,7 +2831,7 @@ static void symbol__calc_lines(struct map_symbol *ms, struct rb_root *root,
int symbol__tty_annotate2(struct map_symbol *ms, struct evsel *evsel,
struct annotation_options *opts)
{
- struct dso *dso = ms->map->dso;
+ struct dso *dso = map__dso(ms->map);
struct symbol *sym = ms->sym;
struct rb_root source_line = RB_ROOT;
struct hists *hists = evsel__hists(evsel);
@@ -2875,7 +2867,7 @@ int symbol__tty_annotate2(struct map_symbol *ms, struct evsel *evsel,
int symbol__tty_annotate(struct map_symbol *ms, struct evsel *evsel,
struct annotation_options *opts)
{
- struct dso *dso = ms->map->dso;
+ struct dso *dso = map__dso(ms->map);
struct symbol *sym = ms->sym;
struct rb_root source_line = RB_ROOT;
int err;
@@ -3222,7 +3214,23 @@ static int annotation__config(const char *var, const char *value, void *data)
} else if (!strcmp(var, "annotate.use_offset")) {
opt->use_offset = perf_config_bool("use_offset", value);
} else if (!strcmp(var, "annotate.disassembler_style")) {
- opt->disassembler_style = value;
+ opt->disassembler_style = strdup(value);
+ if (!opt->disassembler_style) {
+ pr_err("Not enough memory for annotate.disassembler_style\n");
+ return -1;
+ }
+ } else if (!strcmp(var, "annotate.objdump")) {
+ opt->objdump_path = strdup(value);
+ if (!opt->objdump_path) {
+ pr_err("Not enough memory for annotate.objdump\n");
+ return -1;
+ }
+ } else if (!strcmp(var, "annotate.addr2line")) {
+ symbol_conf.addr2line_path = strdup(value);
+ if (!symbol_conf.addr2line_path) {
+ pr_err("Not enough memory for annotate.addr2line\n");
+ return -1;
+ }
} else if (!strcmp(var, "annotate.demangle")) {
symbol_conf.demangle = perf_config_bool("demangle", value);
} else if (!strcmp(var, "annotate.demangle_kernel")) {
@@ -3234,6 +3242,25 @@ static int annotation__config(const char *var, const char *value, void *data)
return 0;
}
+void annotation_options__init(struct annotation_options *opt)
+{
+ memset(opt, 0, sizeof(*opt));
+
+ /* Default values. */
+ opt->use_offset = true;
+ opt->jump_arrows = true;
+ opt->annotate_src = true;
+ opt->offset_level = ANNOTATION__OFFSET_JUMP_TARGETS;
+ opt->percent_type = PERCENT_PERIOD_LOCAL;
+}
+
+
+void annotation_options__exit(struct annotation_options *opt)
+{
+ zfree(&opt->disassembler_style);
+ zfree(&opt->objdump_path);
+}
+
void annotation_config__init(struct annotation_options *opt)
{
perf_config(annotation__config, opt);
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 8934072c39e6..1c6335b8333a 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -94,8 +94,8 @@ struct annotation_options {
int min_pcnt;
int max_lines;
int context;
- const char *objdump_path;
- const char *disassembler_style;
+ char *objdump_path;
+ char *disassembler_style;
const char *prefix;
const char *prefix_strip;
unsigned int percent_type;
@@ -109,8 +109,6 @@ enum {
#define ANNOTATION__MIN_OFFSET_LEVEL ANNOTATION__OFFSET_JUMP_TARGETS
-extern struct annotation_options annotation__default_options;
-
struct annotation;
struct sym_hist_entry {
@@ -418,6 +416,9 @@ static inline int symbol__tui_annotate(struct map_symbol *ms __maybe_unused,
}
#endif
+void annotation_options__init(struct annotation_options *opt);
+void annotation_options__exit(struct annotation_options *opt);
+
void annotation_config__init(struct annotation_options *opt);
int annotate_parse_percent_type(const struct option *opt, const char *_str,
diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c
index 40dcedfd75cd..f3918f290df5 100644
--- a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c
+++ b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.c
@@ -190,11 +190,27 @@ static int arm_spe_read_record(struct arm_spe_decoder *decoder)
decoder->record.context_id = payload;
break;
case ARM_SPE_OP_TYPE:
- if (idx == SPE_OP_PKT_HDR_CLASS_LD_ST_ATOMIC) {
- if (payload & 0x1)
- decoder->record.op = ARM_SPE_ST;
+ switch (idx) {
+ case SPE_OP_PKT_HDR_CLASS_LD_ST_ATOMIC:
+ decoder->record.op |= ARM_SPE_OP_LDST;
+ if (payload & SPE_OP_PKT_ST)
+ decoder->record.op |= ARM_SPE_OP_ST;
else
- decoder->record.op = ARM_SPE_LD;
+ decoder->record.op |= ARM_SPE_OP_LD;
+ if (SPE_OP_PKT_IS_LDST_SVE(payload))
+ decoder->record.op |= ARM_SPE_OP_SVE_LDST;
+ break;
+ case SPE_OP_PKT_HDR_CLASS_OTHER:
+ decoder->record.op |= ARM_SPE_OP_OTHER;
+ if (SPE_OP_PKT_IS_OTHER_SVE_OP(payload))
+ decoder->record.op |= ARM_SPE_OP_SVE_OTHER;
+ break;
+ case SPE_OP_PKT_HDR_CLASS_BR_ERET:
+ decoder->record.op |= ARM_SPE_OP_BRANCH_ERET;
+ break;
+ default:
+ pr_err("Get packet error!\n");
+ return -1;
}
break;
case ARM_SPE_EVENTS:
@@ -222,6 +238,12 @@ static int arm_spe_read_record(struct arm_spe_decoder *decoder)
if (payload & BIT(EV_MISPRED))
decoder->record.type |= ARM_SPE_BRANCH_MISS;
+ if (payload & BIT(EV_PARTIAL_PREDICATE))
+ decoder->record.type |= ARM_SPE_SVE_PARTIAL_PRED;
+
+ if (payload & BIT(EV_EMPTY_PREDICATE))
+ decoder->record.type |= ARM_SPE_SVE_EMPTY_PRED;
+
break;
case ARM_SPE_DATA_SOURCE:
decoder->record.source = payload;
diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h
index 46a61df1145b..1443c28545a9 100644
--- a/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h
+++ b/tools/perf/util/arm-spe-decoder/arm-spe-decoder.h
@@ -14,19 +14,46 @@
#include "arm-spe-pkt-decoder.h"
enum arm_spe_sample_type {
- ARM_SPE_L1D_ACCESS = 1 << 0,
- ARM_SPE_L1D_MISS = 1 << 1,
- ARM_SPE_LLC_ACCESS = 1 << 2,
- ARM_SPE_LLC_MISS = 1 << 3,
- ARM_SPE_TLB_ACCESS = 1 << 4,
- ARM_SPE_TLB_MISS = 1 << 5,
- ARM_SPE_BRANCH_MISS = 1 << 6,
- ARM_SPE_REMOTE_ACCESS = 1 << 7,
+ ARM_SPE_L1D_ACCESS = 1 << 0,
+ ARM_SPE_L1D_MISS = 1 << 1,
+ ARM_SPE_LLC_ACCESS = 1 << 2,
+ ARM_SPE_LLC_MISS = 1 << 3,
+ ARM_SPE_TLB_ACCESS = 1 << 4,
+ ARM_SPE_TLB_MISS = 1 << 5,
+ ARM_SPE_BRANCH_MISS = 1 << 6,
+ ARM_SPE_REMOTE_ACCESS = 1 << 7,
+ ARM_SPE_SVE_PARTIAL_PRED = 1 << 8,
+ ARM_SPE_SVE_EMPTY_PRED = 1 << 9,
};
enum arm_spe_op_type {
- ARM_SPE_LD = 1 << 0,
- ARM_SPE_ST = 1 << 1,
+ /* First level operation type */
+ ARM_SPE_OP_OTHER = 1 << 0,
+ ARM_SPE_OP_LDST = 1 << 1,
+ ARM_SPE_OP_BRANCH_ERET = 1 << 2,
+
+ /* Second level operation type for OTHER */
+ ARM_SPE_OP_SVE_OTHER = 1 << 16,
+ ARM_SPE_OP_SVE_FP = 1 << 17,
+ ARM_SPE_OP_SVE_PRED_OTHER = 1 << 18,
+
+ /* Second level operation type for LDST */
+ ARM_SPE_OP_LD = 1 << 16,
+ ARM_SPE_OP_ST = 1 << 17,
+ ARM_SPE_OP_ATOMIC = 1 << 18,
+ ARM_SPE_OP_EXCL = 1 << 19,
+ ARM_SPE_OP_AR = 1 << 20,
+ ARM_SPE_OP_SIMD_FP = 1 << 21,
+ ARM_SPE_OP_GP_REG = 1 << 22,
+ ARM_SPE_OP_UNSPEC_REG = 1 << 23,
+ ARM_SPE_OP_NV_SYSREG = 1 << 24,
+ ARM_SPE_OP_SVE_LDST = 1 << 25,
+ ARM_SPE_OP_SVE_PRED_LDST = 1 << 26,
+ ARM_SPE_OP_SVE_SG = 1 << 27,
+
+ /* Second level operation type for BRANCH_ERET */
+ ARM_SPE_OP_BR_COND = 1 << 16,
+ ARM_SPE_OP_BR_INDIRECT = 1 << 17,
};
enum arm_spe_neoverse_data_source {
diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
index fed4741f372e..a454c6737563 100644
--- a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
+++ b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
@@ -381,6 +381,15 @@ static int arm_spe_pkt_desc_op_type(const struct arm_spe_pkt *packet,
case SPE_OP_PKT_LDST_SUBCLASS_NV_SYSREG:
arm_spe_pkt_out_string(&err, &buf, &buf_len, " NV-SYSREG");
break;
+ case SPE_OP_PKT_LDST_SUBCLASS_MTE_TAG:
+ arm_spe_pkt_out_string(&err, &buf, &buf_len, " MTE-TAG");
+ break;
+ case SPE_OP_PKT_LDST_SUBCLASS_MEMCPY:
+ arm_spe_pkt_out_string(&err, &buf, &buf_len, " MEMCPY");
+ break;
+ case SPE_OP_PKT_LDST_SUBCLASS_MEMSET:
+ arm_spe_pkt_out_string(&err, &buf, &buf_len, " MEMSET");
+ break;
default:
break;
}
diff --git a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h
index f75ed3a8a050..464a912b221c 100644
--- a/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h
+++ b/tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h
@@ -123,6 +123,9 @@ enum arm_spe_events {
#define SPE_OP_PKT_LDST_SUBCLASS_SIMD_FP 0x4
#define SPE_OP_PKT_LDST_SUBCLASS_UNSPEC_REG 0x10
#define SPE_OP_PKT_LDST_SUBCLASS_NV_SYSREG 0x30
+#define SPE_OP_PKT_LDST_SUBCLASS_MTE_TAG 0x14
+#define SPE_OP_PKT_LDST_SUBCLASS_MEMCPY 0x20
+#define SPE_OP_PKT_LDST_SUBCLASS_MEMSET 0x25
#define SPE_OP_PKT_IS_LDST_ATOMIC(v) (((v) & (GENMASK_ULL(7, 5) | BIT(1))) == 0x2)
diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c
index 906476a839e1..7b36ba6b4079 100644
--- a/tools/perf/util/arm-spe.c
+++ b/tools/perf/util/arm-spe.c
@@ -273,6 +273,25 @@ static int arm_spe_set_tid(struct arm_spe_queue *speq, pid_t tid)
return 0;
}
+static struct simd_flags arm_spe__synth_simd_flags(const struct arm_spe_record *record)
+{
+ struct simd_flags simd_flags = {};
+
+ if ((record->op & ARM_SPE_OP_LDST) && (record->op & ARM_SPE_OP_SVE_LDST))
+ simd_flags.arch |= SIMD_OP_FLAGS_ARCH_SVE;
+
+ if ((record->op & ARM_SPE_OP_OTHER) && (record->op & ARM_SPE_OP_SVE_OTHER))
+ simd_flags.arch |= SIMD_OP_FLAGS_ARCH_SVE;
+
+ if (record->type & ARM_SPE_SVE_PARTIAL_PRED)
+ simd_flags.pred |= SIMD_OP_FLAGS_PRED_PARTIAL;
+
+ if (record->type & ARM_SPE_SVE_EMPTY_PRED)
+ simd_flags.pred |= SIMD_OP_FLAGS_PRED_EMPTY;
+
+ return simd_flags;
+}
+
static void arm_spe_prep_sample(struct arm_spe *spe,
struct arm_spe_queue *speq,
union perf_event *event,
@@ -289,6 +308,7 @@ static void arm_spe_prep_sample(struct arm_spe *spe,
sample->tid = speq->tid;
sample->period = 1;
sample->cpu = speq->cpu;
+ sample->simd_flags = arm_spe__synth_simd_flags(record);
event->sample.header.type = PERF_RECORD_SAMPLE;
event->sample.header.misc = sample->cpumode;
@@ -411,7 +431,7 @@ static void arm_spe__synth_data_source_neoverse(const struct arm_spe_record *rec
* We have no data on the hit level or data source for stores in the
* Neoverse SPE records.
*/
- if (record->op & ARM_SPE_ST) {
+ if (record->op & ARM_SPE_OP_ST) {
data_src->mem_lvl = PERF_MEM_LVL_NA;
data_src->mem_lvl_num = PERF_MEM_LVLNUM_NA;
data_src->mem_snoop = PERF_MEM_SNOOP_NA;
@@ -497,12 +517,12 @@ static void arm_spe__synth_data_source_generic(const struct arm_spe_record *reco
static u64 arm_spe__synth_data_source(const struct arm_spe_record *record, u64 midr)
{
- union perf_mem_data_src data_src = { 0 };
+ union perf_mem_data_src data_src = { .mem_op = PERF_MEM_OP_NA };
bool is_neoverse = is_midr_in_range_list(midr, neoverse_spe);
- if (record->op == ARM_SPE_LD)
+ if (record->op & ARM_SPE_OP_LD)
data_src.mem_op = PERF_MEM_OP_LOAD;
- else if (record->op == ARM_SPE_ST)
+ else if (record->op & ARM_SPE_OP_ST)
data_src.mem_op = PERF_MEM_OP_STORE;
else
return 0;
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 498ff7f24463..a0368202a746 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -2449,6 +2449,7 @@ static int find_entire_kern_cb(void *arg, const char *name __maybe_unused,
char type, u64 start)
{
struct sym_args *args = arg;
+ u64 size;
if (!kallsyms__is_function(type))
return 0;
@@ -2458,7 +2459,9 @@ static int find_entire_kern_cb(void *arg, const char *name __maybe_unused,
args->start = start;
}
/* Don't know exactly where the kernel ends, so we add a page */
- args->size = round_up(start, page_size) + page_size - args->start;
+ size = round_up(start, page_size) + page_size - args->start;
+ if (size > args->size)
+ args->size = size;
return 0;
}
@@ -2557,7 +2560,7 @@ static struct dso *load_dso(const char *name)
if (map__load(map) < 0)
pr_err("File '%s' not found or has no symbols.\n", name);
- dso = dso__get(map->dso);
+ dso = dso__get(map__dso(map));
map__put(map);
diff --git a/tools/perf/util/block-info.c b/tools/perf/util/block-info.c
index 5ecd4f401f32..16a7b4adcf18 100644
--- a/tools/perf/util/block-info.c
+++ b/tools/perf/util/block-info.c
@@ -317,9 +317,9 @@ static int block_dso_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
struct map *map = he->ms.map;
- if (map && map->dso) {
+ if (map && map__dso(map)) {
return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
- map->dso->short_name);
+ map__dso(map)->short_name);
}
return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
diff --git a/tools/perf/util/block-range.c b/tools/perf/util/block-range.c
index 1be432657501..680e92774d0c 100644
--- a/tools/perf/util/block-range.c
+++ b/tools/perf/util/block-range.c
@@ -11,11 +11,7 @@ struct {
static void block_range__debug(void)
{
- /*
- * XXX still paranoid for now; see if we can make this depend on
- * DEBUG=1 builds.
- */
-#if 1
+#ifndef NDEBUG
struct rb_node *rb;
u64 old = 0; /* NULL isn't executable */
diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c
index cc7c1f90cf62..38fcf3ba5749 100644
--- a/tools/perf/util/bpf-event.c
+++ b/tools/perf/util/bpf-event.c
@@ -22,72 +22,6 @@
#include "record.h"
#include "util/synthetic-events.h"
-#ifndef HAVE_LIBBPF_BTF__LOAD_FROM_KERNEL_BY_ID
-struct btf *btf__load_from_kernel_by_id(__u32 id)
-{
- struct btf *btf;
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- int err = btf__get_from_id(id, &btf);
-#pragma GCC diagnostic pop
-
- return err ? ERR_PTR(err) : btf;
-}
-#endif
-
-#ifndef HAVE_LIBBPF_BPF_PROG_LOAD
-LIBBPF_API int bpf_load_program(enum bpf_prog_type type,
- const struct bpf_insn *insns, size_t insns_cnt,
- const char *license, __u32 kern_version,
- char *log_buf, size_t log_buf_sz);
-
-int bpf_prog_load(enum bpf_prog_type prog_type,
- const char *prog_name __maybe_unused,
- const char *license,
- const struct bpf_insn *insns, size_t insn_cnt,
- const struct bpf_prog_load_opts *opts)
-{
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- return bpf_load_program(prog_type, insns, insn_cnt, license,
- opts->kern_version, opts->log_buf, opts->log_size);
-#pragma GCC diagnostic pop
-}
-#endif
-
-#ifndef HAVE_LIBBPF_BPF_OBJECT__NEXT_PROGRAM
-struct bpf_program *
-bpf_object__next_program(const struct bpf_object *obj, struct bpf_program *prev)
-{
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- return bpf_program__next(prev, obj);
-#pragma GCC diagnostic pop
-}
-#endif
-
-#ifndef HAVE_LIBBPF_BPF_OBJECT__NEXT_MAP
-struct bpf_map *
-bpf_object__next_map(const struct bpf_object *obj, const struct bpf_map *prev)
-{
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- return bpf_map__next(prev, obj);
-#pragma GCC diagnostic pop
-}
-#endif
-
-#ifndef HAVE_LIBBPF_BTF__RAW_DATA
-const void *
-btf__raw_data(const struct btf *btf_ro, __u32 *size)
-{
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- return btf__get_raw_data(btf_ro, size);
-#pragma GCC diagnostic pop
-}
-#endif
-
static int snprintf_hex(char *buf, size_t size, unsigned char *data, size_t len)
{
int ret = 0;
@@ -123,10 +57,12 @@ static int machine__process_bpf_event_load(struct machine *machine,
struct map *map = maps__find(machine__kernel_maps(machine), addr);
if (map) {
- map->dso->binary_type = DSO_BINARY_TYPE__BPF_PROG_INFO;
- map->dso->bpf_prog.id = id;
- map->dso->bpf_prog.sub_id = i;
- map->dso->bpf_prog.env = env;
+ struct dso *dso = map__dso(map);
+
+ dso->binary_type = DSO_BINARY_TYPE__BPF_PROG_INFO;
+ dso->bpf_prog.id = id;
+ dso->bpf_prog.sub_id = i;
+ dso->bpf_prog.env = env;
}
}
return 0;
diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c
new file mode 100644
index 000000000000..0b30688d78a7
--- /dev/null
+++ b/tools/perf/util/bpf-filter.c
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <stdlib.h>
+
+#include <bpf/bpf.h>
+#include <linux/err.h>
+#include <internal/xyarray.h>
+
+#include "util/debug.h"
+#include "util/evsel.h"
+
+#include "util/bpf-filter.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"
+
+#define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y))
+
+#define __PERF_SAMPLE_TYPE(st, opt) { st, #st, opt }
+#define PERF_SAMPLE_TYPE(_st, opt) __PERF_SAMPLE_TYPE(PERF_SAMPLE_##_st, opt)
+
+static const struct perf_sample_info {
+ u64 type;
+ const char *name;
+ const char *option;
+} sample_table[] = {
+ /* default sample flags */
+ PERF_SAMPLE_TYPE(IP, NULL),
+ PERF_SAMPLE_TYPE(TID, NULL),
+ PERF_SAMPLE_TYPE(PERIOD, NULL),
+ /* flags mostly set by default, but still have options */
+ PERF_SAMPLE_TYPE(ID, "--sample-identifier"),
+ PERF_SAMPLE_TYPE(CPU, "--sample-cpu"),
+ PERF_SAMPLE_TYPE(TIME, "-T"),
+ /* optional sample flags */
+ PERF_SAMPLE_TYPE(ADDR, "-d"),
+ PERF_SAMPLE_TYPE(DATA_SRC, "-d"),
+ PERF_SAMPLE_TYPE(PHYS_ADDR, "--phys-data"),
+ PERF_SAMPLE_TYPE(WEIGHT, "-W"),
+ PERF_SAMPLE_TYPE(WEIGHT_STRUCT, "-W"),
+ PERF_SAMPLE_TYPE(TRANSACTION, "--transaction"),
+ PERF_SAMPLE_TYPE(CODE_PAGE_SIZE, "--code-page-size"),
+ PERF_SAMPLE_TYPE(DATA_PAGE_SIZE, "--data-page-size"),
+};
+
+static const struct perf_sample_info *get_sample_info(u64 flags)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(sample_table); i++) {
+ if (sample_table[i].type == flags)
+ return &sample_table[i];
+ }
+ return NULL;
+}
+
+static int check_sample_flags(struct evsel *evsel, struct perf_bpf_filter_expr *expr)
+{
+ const struct perf_sample_info *info;
+
+ if (evsel->core.attr.sample_type & expr->sample_flags)
+ return 0;
+
+ info = get_sample_info(expr->sample_flags);
+ if (info == NULL) {
+ pr_err("Error: %s event does not have sample flags %lx\n",
+ evsel__name(evsel), expr->sample_flags);
+ return -1;
+ }
+
+ pr_err("Error: %s event does not have %s\n", evsel__name(evsel), info->name);
+ if (info->option)
+ pr_err(" Hint: please add %s option to perf record\n", info->option);
+ return -1;
+}
+
+int perf_bpf_filter__prepare(struct evsel *evsel)
+{
+ int i, x, y, fd;
+ struct sample_filter_bpf *skel;
+ struct bpf_program *prog;
+ struct bpf_link *link;
+ struct perf_bpf_filter_expr *expr;
+
+ skel = sample_filter_bpf__open_and_load();
+ if (!skel) {
+ pr_err("Failed to load perf sample-filter BPF skeleton\n");
+ return -1;
+ }
+
+ i = 0;
+ fd = bpf_map__fd(skel->maps.filters);
+ list_for_each_entry(expr, &evsel->bpf_filters, list) {
+ struct perf_bpf_filter_entry entry = {
+ .op = expr->op,
+ .part = expr->part,
+ .flags = expr->sample_flags,
+ .value = expr->val,
+ };
+
+ if (check_sample_flags(evsel, expr) < 0)
+ return -1;
+
+ bpf_map_update_elem(fd, &i, &entry, BPF_ANY);
+ i++;
+
+ if (expr->op == PBF_OP_GROUP_BEGIN) {
+ struct perf_bpf_filter_expr *group;
+
+ list_for_each_entry(group, &expr->groups, list) {
+ struct perf_bpf_filter_entry group_entry = {
+ .op = group->op,
+ .part = group->part,
+ .flags = group->sample_flags,
+ .value = group->val,
+ };
+ bpf_map_update_elem(fd, &i, &group_entry, BPF_ANY);
+ i++;
+ }
+
+ memset(&entry, 0, sizeof(entry));
+ entry.op = PBF_OP_GROUP_END;
+ bpf_map_update_elem(fd, &i, &entry, BPF_ANY);
+ i++;
+ }
+ }
+
+ if (i > MAX_FILTERS) {
+ pr_err("Too many filters: %d (max = %d)\n", i, MAX_FILTERS);
+ return -1;
+ }
+ prog = skel->progs.perf_sample_filter;
+ for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) {
+ for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) {
+ link = bpf_program__attach_perf_event(prog, FD(evsel, x, y));
+ if (IS_ERR(link)) {
+ pr_err("Failed to attach perf sample-filter program\n");
+ return PTR_ERR(link);
+ }
+ }
+ }
+ evsel->bpf_skel = skel;
+ return 0;
+}
+
+int perf_bpf_filter__destroy(struct evsel *evsel)
+{
+ struct perf_bpf_filter_expr *expr, *tmp;
+
+ list_for_each_entry_safe(expr, tmp, &evsel->bpf_filters, list) {
+ list_del(&expr->list);
+ free(expr);
+ }
+ sample_filter_bpf__destroy(evsel->bpf_skel);
+ return 0;
+}
+
+u64 perf_bpf_filter__lost_count(struct evsel *evsel)
+{
+ struct sample_filter_bpf *skel = evsel->bpf_skel;
+
+ return skel ? skel->bss->dropped : 0;
+}
+
+struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flags, int part,
+ enum perf_bpf_filter_op op,
+ unsigned long val)
+{
+ struct perf_bpf_filter_expr *expr;
+
+ expr = malloc(sizeof(*expr));
+ if (expr != NULL) {
+ expr->sample_flags = sample_flags;
+ expr->part = part;
+ expr->op = op;
+ expr->val = val;
+ INIT_LIST_HEAD(&expr->groups);
+ }
+ return expr;
+}
+
+int perf_bpf_filter__parse(struct list_head *expr_head, const char *str)
+{
+ YY_BUFFER_STATE buffer;
+ int ret;
+
+ buffer = perf_bpf_filter__scan_string(str);
+
+ ret = perf_bpf_filter_parse(expr_head);
+
+ perf_bpf_filter__flush_buffer(buffer);
+ perf_bpf_filter__delete_buffer(buffer);
+ perf_bpf_filter_lex_destroy();
+
+ return ret;
+}
diff --git a/tools/perf/util/bpf-filter.h b/tools/perf/util/bpf-filter.h
new file mode 100644
index 000000000000..7afd159411b8
--- /dev/null
+++ b/tools/perf/util/bpf-filter.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef PERF_UTIL_BPF_FILTER_H
+#define PERF_UTIL_BPF_FILTER_H
+
+#include <linux/list.h>
+
+#include "bpf_skel/sample-filter.h"
+
+struct perf_bpf_filter_expr {
+ struct list_head list;
+ struct list_head groups;
+ enum perf_bpf_filter_op op;
+ int part;
+ unsigned long sample_flags;
+ unsigned long val;
+};
+
+struct evsel;
+
+#ifdef HAVE_BPF_SKEL
+struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flags, int part,
+ enum perf_bpf_filter_op op,
+ unsigned long val);
+int perf_bpf_filter__parse(struct list_head *expr_head, const char *str);
+int perf_bpf_filter__prepare(struct evsel *evsel);
+int perf_bpf_filter__destroy(struct evsel *evsel);
+u64 perf_bpf_filter__lost_count(struct evsel *evsel);
+
+#else /* !HAVE_BPF_SKEL */
+
+static inline int perf_bpf_filter__parse(struct list_head *expr_head __maybe_unused,
+ const char *str __maybe_unused)
+{
+ return -EOPNOTSUPP;
+}
+static inline int perf_bpf_filter__prepare(struct evsel *evsel __maybe_unused)
+{
+ return -EOPNOTSUPP;
+}
+static inline int perf_bpf_filter__destroy(struct evsel *evsel __maybe_unused)
+{
+ return -EOPNOTSUPP;
+}
+static inline u64 perf_bpf_filter__lost_count(struct evsel *evsel __maybe_unused)
+{
+ return 0;
+}
+#endif /* HAVE_BPF_SKEL*/
+#endif /* PERF_UTIL_BPF_FILTER_H */
diff --git a/tools/perf/util/bpf-filter.l b/tools/perf/util/bpf-filter.l
new file mode 100644
index 000000000000..d4ff0f1345cd
--- /dev/null
+++ b/tools/perf/util/bpf-filter.l
@@ -0,0 +1,159 @@
+%option prefix="perf_bpf_filter_"
+%option noyywrap
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+#include <linux/perf_event.h>
+
+#include "bpf-filter.h"
+#include "bpf-filter-bison.h"
+
+static int sample(unsigned long sample_flag)
+{
+ perf_bpf_filter_lval.sample.type = sample_flag;
+ perf_bpf_filter_lval.sample.part = 0;
+ return BFT_SAMPLE;
+}
+
+static int sample_part(unsigned long sample_flag, int part)
+{
+ perf_bpf_filter_lval.sample.type = sample_flag;
+ perf_bpf_filter_lval.sample.part = part;
+ return BFT_SAMPLE;
+}
+
+static int operator(enum perf_bpf_filter_op op)
+{
+ perf_bpf_filter_lval.op = op;
+ return BFT_OP;
+}
+
+static int value(int base)
+{
+ long num;
+
+ errno = 0;
+ num = strtoul(perf_bpf_filter_text, NULL, base);
+ if (errno)
+ return BFT_ERROR;
+
+ perf_bpf_filter_lval.num = num;
+ return BFT_NUM;
+}
+
+static int constant(int val)
+{
+ perf_bpf_filter_lval.num = val;
+ return BFT_NUM;
+}
+
+static int error(const char *str)
+{
+ printf("perf_bpf_filter: Unexpected filter %s: %s\n", str, perf_bpf_filter_text);
+ return BFT_ERROR;
+}
+
+%}
+
+num_dec [0-9]+
+num_hex 0[Xx][0-9a-fA-F]+
+space [ \t]+
+ident [_a-zA-Z][_a-zA-Z0-9]+
+
+%%
+
+{num_dec} { return value(10); }
+{num_hex} { return value(16); }
+{space} { }
+
+ip { return sample(PERF_SAMPLE_IP); }
+id { return sample(PERF_SAMPLE_ID); }
+tid { return sample(PERF_SAMPLE_TID); }
+pid { return sample_part(PERF_SAMPLE_TID, 1); }
+cpu { return sample(PERF_SAMPLE_CPU); }
+time { return sample(PERF_SAMPLE_TIME); }
+addr { return sample(PERF_SAMPLE_ADDR); }
+period { return sample(PERF_SAMPLE_PERIOD); }
+txn { return sample(PERF_SAMPLE_TRANSACTION); }
+weight { return sample(PERF_SAMPLE_WEIGHT); }
+weight1 { return sample_part(PERF_SAMPLE_WEIGHT_STRUCT, 1); }
+weight2 { return sample_part(PERF_SAMPLE_WEIGHT_STRUCT, 2); }
+weight3 { return sample_part(PERF_SAMPLE_WEIGHT_STRUCT, 3); }
+ins_lat { return sample_part(PERF_SAMPLE_WEIGHT_STRUCT, 2); } /* alias for weight2 */
+p_stage_cyc { return sample_part(PERF_SAMPLE_WEIGHT_STRUCT, 3); } /* alias for weight3 */
+retire_lat { return sample_part(PERF_SAMPLE_WEIGHT_STRUCT, 3); } /* alias for weight3 */
+phys_addr { return sample(PERF_SAMPLE_PHYS_ADDR); }
+code_pgsz { return sample(PERF_SAMPLE_CODE_PAGE_SIZE); }
+data_pgsz { return sample(PERF_SAMPLE_DATA_PAGE_SIZE); }
+mem_op { return sample_part(PERF_SAMPLE_DATA_SRC, 1); }
+mem_lvlnum { return sample_part(PERF_SAMPLE_DATA_SRC, 2); }
+mem_lvl { return sample_part(PERF_SAMPLE_DATA_SRC, 2); } /* alias for mem_lvlnum */
+mem_snoop { return sample_part(PERF_SAMPLE_DATA_SRC, 3); } /* include snoopx */
+mem_remote { return sample_part(PERF_SAMPLE_DATA_SRC, 4); }
+mem_lock { return sample_part(PERF_SAMPLE_DATA_SRC, 5); }
+mem_dtlb { return sample_part(PERF_SAMPLE_DATA_SRC, 6); }
+mem_blk { return sample_part(PERF_SAMPLE_DATA_SRC, 7); }
+mem_hops { return sample_part(PERF_SAMPLE_DATA_SRC, 8); }
+
+"==" { return operator(PBF_OP_EQ); }
+"!=" { return operator(PBF_OP_NEQ); }
+">" { return operator(PBF_OP_GT); }
+"<" { return operator(PBF_OP_LT); }
+">=" { return operator(PBF_OP_GE); }
+"<=" { return operator(PBF_OP_LE); }
+"&" { return operator(PBF_OP_AND); }
+
+na { return constant(PERF_MEM_OP_NA); }
+load { return constant(PERF_MEM_OP_LOAD); }
+store { return constant(PERF_MEM_OP_STORE); }
+pfetch { return constant(PERF_MEM_OP_PFETCH); }
+exec { return constant(PERF_MEM_OP_EXEC); }
+
+l1 { return constant(PERF_MEM_LVLNUM_L1); }
+l2 { return constant(PERF_MEM_LVLNUM_L2); }
+l3 { return constant(PERF_MEM_LVLNUM_L3); }
+l4 { return constant(PERF_MEM_LVLNUM_L4); }
+cxl { return constant(PERF_MEM_LVLNUM_CXL); }
+io { return constant(PERF_MEM_LVLNUM_IO); }
+any_cache { return constant(PERF_MEM_LVLNUM_ANY_CACHE); }
+lfb { return constant(PERF_MEM_LVLNUM_LFB); }
+ram { return constant(PERF_MEM_LVLNUM_RAM); }
+pmem { return constant(PERF_MEM_LVLNUM_PMEM); }
+
+none { return constant(PERF_MEM_SNOOP_NONE); }
+hit { return constant(PERF_MEM_SNOOP_HIT); }
+miss { return constant(PERF_MEM_SNOOP_MISS); }
+hitm { return constant(PERF_MEM_SNOOP_HITM); }
+fwd { return constant(PERF_MEM_SNOOPX_FWD); }
+peer { return constant(PERF_MEM_SNOOPX_PEER); }
+
+remote { return constant(PERF_MEM_REMOTE_REMOTE); }
+
+locked { return constant(PERF_MEM_LOCK_LOCKED); }
+
+l1_hit { return constant(PERF_MEM_TLB_L1 | PERF_MEM_TLB_HIT); }
+l1_miss { return constant(PERF_MEM_TLB_L1 | PERF_MEM_TLB_MISS); }
+l2_hit { return constant(PERF_MEM_TLB_L2 | PERF_MEM_TLB_HIT); }
+l2_miss { return constant(PERF_MEM_TLB_L2 | PERF_MEM_TLB_MISS); }
+any_hit { return constant(PERF_MEM_TLB_HIT); }
+any_miss { return constant(PERF_MEM_TLB_MISS); }
+walk { return constant(PERF_MEM_TLB_WK); }
+os { return constant(PERF_MEM_TLB_OS); }
+fault { return constant(PERF_MEM_TLB_OS); } /* alias for os */
+
+by_data { return constant(PERF_MEM_BLK_DATA); }
+by_addr { return constant(PERF_MEM_BLK_ADDR); }
+
+hops0 { return constant(PERF_MEM_HOPS_0); }
+hops1 { return constant(PERF_MEM_HOPS_1); }
+hops2 { return constant(PERF_MEM_HOPS_2); }
+hops3 { return constant(PERF_MEM_HOPS_3); }
+
+"," { return ','; }
+"||" { return BFT_LOGICAL_OR; }
+
+{ident} { return error("ident"); }
+. { return error("input"); }
+
+%%
diff --git a/tools/perf/util/bpf-filter.y b/tools/perf/util/bpf-filter.y
new file mode 100644
index 000000000000..07d6c7926c13
--- /dev/null
+++ b/tools/perf/util/bpf-filter.y
@@ -0,0 +1,78 @@
+%parse-param {struct list_head *expr_head}
+%define parse.error verbose
+
+%{
+
+#include <stdio.h>
+#include <string.h>
+#include <linux/compiler.h>
+#include <linux/list.h>
+#include "bpf-filter.h"
+
+static void perf_bpf_filter_error(struct list_head *expr __maybe_unused,
+ char const *msg)
+{
+ printf("perf_bpf_filter: %s\n", msg);
+}
+
+%}
+
+%union
+{
+ unsigned long num;
+ struct {
+ unsigned long type;
+ int part;
+ } sample;
+ enum perf_bpf_filter_op op;
+ struct perf_bpf_filter_expr *expr;
+}
+
+%token BFT_SAMPLE BFT_OP BFT_ERROR BFT_NUM BFT_LOGICAL_OR
+%type <expr> filter_term filter_expr
+%destructor { free ($$); } <expr>
+%type <sample> BFT_SAMPLE
+%type <op> BFT_OP
+%type <num> BFT_NUM
+
+%%
+
+filter:
+filter ',' filter_term
+{
+ list_add_tail(&$3->list, expr_head);
+}
+|
+filter_term
+{
+ list_add_tail(&$1->list, expr_head);
+}
+
+filter_term:
+filter_term BFT_LOGICAL_OR filter_expr
+{
+ struct perf_bpf_filter_expr *expr;
+
+ if ($1->op == PBF_OP_GROUP_BEGIN) {
+ expr = $1;
+ } else {
+ expr = perf_bpf_filter_expr__new(0, 0, PBF_OP_GROUP_BEGIN, 1);
+ list_add_tail(&$1->list, &expr->groups);
+ }
+ expr->val++;
+ list_add_tail(&$3->list, &expr->groups);
+ $$ = expr;
+}
+|
+filter_expr
+{
+ $$ = $1;
+}
+
+filter_expr:
+BFT_SAMPLE BFT_OP BFT_NUM
+{
+ $$ = perf_bpf_filter_expr__new($1.type, $1.part, $2, $3);
+}
+
+%%
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 6e9b06cf06ee..44cde27d6389 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -32,24 +32,6 @@
#include <internal/xyarray.h>
-#ifndef HAVE_LIBBPF_BPF_PROGRAM__SET_INSNS
-int bpf_program__set_insns(struct bpf_program *prog __maybe_unused,
- struct bpf_insn *new_insns __maybe_unused, size_t new_insn_cnt __maybe_unused)
-{
- pr_err("%s: not support, update libbpf\n", __func__);
- return -ENOTSUP;
-}
-
-int libbpf_register_prog_handler(const char *sec __maybe_unused,
- enum bpf_prog_type prog_type __maybe_unused,
- enum bpf_attach_type exp_attach_type __maybe_unused,
- const struct libbpf_prog_handler_opts *opts __maybe_unused)
-{
- pr_err("%s: not support, update libbpf\n", __func__);
- return -ENOTSUP;
-}
-#endif
-
/* temporarily disable libbpf deprecation warnings */
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
diff --git a/tools/perf/util/bpf_counter.c b/tools/perf/util/bpf_counter.c
index eeee899fcf34..6732cbbcf9b3 100644
--- a/tools/perf/util/bpf_counter.c
+++ b/tools/perf/util/bpf_counter.c
@@ -312,24 +312,6 @@ static bool bperf_attr_map_compatible(int attr_map_fd)
(map_info.value_size == sizeof(struct perf_event_attr_map_entry));
}
-#ifndef HAVE_LIBBPF_BPF_MAP_CREATE
-LIBBPF_API int bpf_create_map(enum bpf_map_type map_type, int key_size,
- int value_size, int max_entries, __u32 map_flags);
-int
-bpf_map_create(enum bpf_map_type map_type,
- const char *map_name __maybe_unused,
- __u32 key_size,
- __u32 value_size,
- __u32 max_entries,
- const struct bpf_map_create_opts *opts __maybe_unused)
-{
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- return bpf_create_map(map_type, key_size, value_size, max_entries, 0);
-#pragma GCC diagnostic pop
-}
-#endif
-
static int bperf_lock_attr_map(struct target *target)
{
char path[PATH_MAX];
@@ -563,7 +545,7 @@ static int bperf__load(struct evsel *evsel, struct target *target)
filter_type == BPERF_FILTER_TGID)
key = perf_thread_map__pid(evsel->core.threads, i);
else if (filter_type == BPERF_FILTER_CPU)
- key = evsel->core.cpus->map[i].cpu;
+ key = perf_cpu_map__cpu(evsel->core.cpus, i).cpu;
else
break;
@@ -605,9 +587,9 @@ static int bperf_sync_counters(struct evsel *evsel)
{
int num_cpu, i, cpu;
- num_cpu = all_cpu_map->nr;
+ num_cpu = perf_cpu_map__nr(all_cpu_map);
for (i = 0; i < num_cpu; i++) {
- cpu = all_cpu_map->map[i].cpu;
+ cpu = perf_cpu_map__cpu(all_cpu_map, i).cpu;
bperf_trigger_reading(evsel->bperf_leader_prog_fd, cpu);
}
return 0;
@@ -781,8 +763,7 @@ extern struct bpf_counter_ops bperf_cgrp_ops;
static inline bool bpf_counter_skip(struct evsel *evsel)
{
- return list_empty(&evsel->bpf_counter_list) &&
- evsel->follower_skel == NULL;
+ return evsel->bpf_counter_ops == NULL;
}
int bpf_counter__install_pe(struct evsel *evsel, int cpu_map_idx, int fd)
@@ -834,4 +815,5 @@ void bpf_counter__destroy(struct evsel *evsel)
return;
evsel->bpf_counter_ops->destroy(evsel);
evsel->bpf_counter_ops = NULL;
+ evsel->bpf_skel = NULL;
}
diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c
index fadcacb9d501..e7dddf0127bc 100644
--- a/tools/perf/util/bpf_lock_contention.c
+++ b/tools/perf/util/bpf_lock_contention.c
@@ -74,7 +74,7 @@ int lock_contention_prepare(struct lock_contention *con)
continue;
}
- addrs[con->filters->nr_addrs++] = kmap->unmap_ip(kmap, sym->start);
+ addrs[con->filters->nr_addrs++] = map__unmap_ip(kmap, sym->start);
con->filters->addrs = addrs;
}
naddrs = con->filters->nr_addrs;
@@ -151,6 +151,8 @@ int lock_contention_prepare(struct lock_contention *con)
skel->bss->needs_callstack = con->save_callstack;
skel->bss->lock_owner = con->owner;
+ bpf_program__set_autoload(skel->progs.collect_lock_syms, false);
+
lock_contention_bpf__attach(skel);
return 0;
}
@@ -169,7 +171,7 @@ int lock_contention_stop(void)
static const char *lock_contention_get_name(struct lock_contention *con,
struct contention_key *key,
- u64 *stack_trace)
+ u64 *stack_trace, u32 flags)
{
int idx = 0;
u64 addr;
@@ -198,10 +200,26 @@ static const char *lock_contention_get_name(struct lock_contention *con,
}
if (con->aggr_mode == LOCK_AGGR_ADDR) {
+ int lock_fd = bpf_map__fd(skel->maps.lock_syms);
+
+ /* per-process locks set upper bits of the flags */
+ if (flags & LCD_F_MMAP_LOCK)
+ return "mmap_lock";
+ if (flags & LCD_F_SIGHAND_LOCK)
+ return "siglock";
+
+ /* global locks with symbols */
sym = machine__find_kernel_symbol(machine, key->lock_addr, &kmap);
if (sym)
- name = sym->name;
- return name;
+ return sym->name;
+
+ /* try semi-global locks collected separately */
+ if (!bpf_map_lookup_elem(lock_fd, &key->lock_addr, &flags)) {
+ if (flags == LOCK_CLASS_RQLOCK)
+ return "rq_lock";
+ }
+
+ return "";
}
/* LOCK_AGGR_CALLER: skip lock internal functions */
@@ -215,7 +233,7 @@ static const char *lock_contention_get_name(struct lock_contention *con,
if (sym) {
unsigned long offset;
- offset = kmap->map_ip(kmap, addr) - sym->start;
+ offset = map__map_ip(kmap, addr) - sym->start;
if (offset == 0)
return sym->name;
@@ -231,7 +249,7 @@ static const char *lock_contention_get_name(struct lock_contention *con,
int lock_contention_read(struct lock_contention *con)
{
int fd, stack, err = 0;
- struct contention_key *prev_key, key;
+ struct contention_key *prev_key, key = {};
struct contention_data data = {};
struct lock_stat *st = NULL;
struct machine *machine = con->machine;
@@ -241,7 +259,10 @@ int lock_contention_read(struct lock_contention *con)
fd = bpf_map__fd(skel->maps.lock_stat);
stack = bpf_map__fd(skel->maps.stacks);
- con->lost = skel->bss->lost;
+ con->fails.task = skel->bss->task_fail;
+ con->fails.stack = skel->bss->stack_fail;
+ con->fails.time = skel->bss->time_fail;
+ con->fails.data = skel->bss->data_fail;
stack_trace = zalloc(stack_size);
if (stack_trace == NULL)
@@ -254,8 +275,17 @@ int lock_contention_read(struct lock_contention *con)
thread__set_comm(idle, "swapper", /*timestamp=*/0);
}
+ if (con->aggr_mode == LOCK_AGGR_ADDR) {
+ DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
+ .flags = BPF_F_TEST_RUN_ON_CPU,
+ );
+ int prog_fd = bpf_program__fd(skel->progs.collect_lock_syms);
+
+ bpf_prog_test_run_opts(prog_fd, &opts);
+ }
+
/* make sure it loads the kernel map */
- map__load(maps__first(machine->kmaps));
+ map__load(maps__first(machine->kmaps)->map);
prev_key = NULL;
while (!bpf_map_get_next_key(fd, prev_key, &key)) {
@@ -269,8 +299,10 @@ int lock_contention_read(struct lock_contention *con)
if (con->save_callstack) {
bpf_map_lookup_elem(stack, &key.stack_id, stack_trace);
- if (!match_callstack_filter(machine, stack_trace))
+ if (!match_callstack_filter(machine, stack_trace)) {
+ con->nr_filtered += data.count;
goto next;
+ }
}
switch (con->aggr_mode) {
@@ -301,7 +333,7 @@ int lock_contention_read(struct lock_contention *con)
goto next;
}
- name = lock_contention_get_name(con, &key, stack_trace);
+ name = lock_contention_get_name(con, &key, stack_trace, data.flags);
st = lock_stat_findnew(ls_key, name, data.flags);
if (st == NULL)
break;
@@ -314,7 +346,7 @@ int lock_contention_read(struct lock_contention *con)
if (data.count)
st->avg_wait_time = data.total_time / data.count;
- if (con->save_callstack) {
+ if (con->aggr_mode == LOCK_AGGR_CALLER && verbose > 0) {
st->callstack = memdup(stack_trace, stack_size);
if (st->callstack == NULL)
break;
diff --git a/tools/perf/util/bpf_skel/.gitignore b/tools/perf/util/bpf_skel/.gitignore
index 5263e9e6c5d8..7a1c832825de 100644
--- a/tools/perf/util/bpf_skel/.gitignore
+++ b/tools/perf/util/bpf_skel/.gitignore
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
.tmp
-*.skel.h \ No newline at end of file
+*.skel.h
diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/util/bpf_skel/lock_contention.bpf.c
index e6007eaeda1a..8d3cfbb3cc65 100644
--- a/tools/perf/util/bpf_skel/lock_contention.bpf.c
+++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c
@@ -4,11 +4,12 @@
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
+#include <asm-generic/errno-base.h>
#include "lock_data.h"
-/* default buffer size */
-#define MAX_ENTRIES 10240
+/* for collect_lock_syms(). 4096 was rejected by the verifier */
+#define MAX_CPUS 1024
/* lock contention flags from include/trace/events/lock.h */
#define LCB_F_SPIN (1U << 0)
@@ -58,6 +59,13 @@ struct {
struct {
__uint(type, BPF_MAP_TYPE_HASH);
+ __uint(key_size, sizeof(__u64));
+ __uint(value_size, sizeof(__u32));
+ __uint(max_entries, MAX_ENTRIES);
+} lock_syms SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
__uint(key_size, sizeof(__u32));
__uint(value_size, sizeof(__u8));
__uint(max_entries, 1);
@@ -92,6 +100,14 @@ struct rw_semaphore___new {
atomic_long_t owner;
} __attribute__((preserve_access_index));
+struct mm_struct___old {
+ struct rw_semaphore mmap_sem;
+} __attribute__((preserve_access_index));
+
+struct mm_struct___new {
+ struct rw_semaphore mmap_lock;
+} __attribute__((preserve_access_index));
+
/* control flags */
int enabled;
int has_cpu;
@@ -106,7 +122,13 @@ int lock_owner;
int aggr_mode;
/* error stat */
-int lost;
+int task_fail;
+int stack_fail;
+int time_fail;
+int data_fail;
+
+int task_map_full;
+int data_map_full;
static inline int can_record(u64 *ctx)
{
@@ -159,11 +181,12 @@ static inline int update_task_data(struct task_struct *task)
return -1;
p = bpf_map_lookup_elem(&task_data, &pid);
- if (p == NULL) {
+ if (p == NULL && !task_map_full) {
struct contention_task_data data = {};
BPF_CORE_READ_STR_INTO(&data.comm, task, comm);
- bpf_map_update_elem(&task_data, &pid, &data, BPF_NOEXIST);
+ if (bpf_map_update_elem(&task_data, &pid, &data, BPF_NOEXIST) == -E2BIG)
+ task_map_full = 1;
}
return 0;
@@ -182,7 +205,13 @@ static inline struct task_struct *get_lock_owner(__u64 lock, __u32 flags)
struct mutex *mutex = (void *)lock;
owner = BPF_CORE_READ(mutex, owner.counter);
} else if (flags == LCB_F_READ || flags == LCB_F_WRITE) {
-#if __has_builtin(bpf_core_type_matches)
+ /*
+ * Support for the BPF_TYPE_MATCHES argument to the
+ * __builtin_preserve_type_info builtin was added at some point during
+ * development of clang 15 and it's what is needed for
+ * bpf_core_type_matches.
+ */
+#if __has_builtin(__builtin_preserve_type_info) && __clang_major__ >= 15
if (bpf_core_type_matches(struct rw_semaphore___old)) {
struct rw_semaphore___old *rwsem = (void *)lock;
owner = (unsigned long)BPF_CORE_READ(rwsem, owner);
@@ -204,6 +233,41 @@ static inline struct task_struct *get_lock_owner(__u64 lock, __u32 flags)
return task;
}
+static inline __u32 check_lock_type(__u64 lock, __u32 flags)
+{
+ struct task_struct *curr;
+ struct mm_struct___old *mm_old;
+ struct mm_struct___new *mm_new;
+
+ switch (flags) {
+ case LCB_F_READ: /* rwsem */
+ case LCB_F_WRITE:
+ curr = bpf_get_current_task_btf();
+ if (curr->mm == NULL)
+ break;
+ mm_new = (void *)curr->mm;
+ if (bpf_core_field_exists(mm_new->mmap_lock)) {
+ if (&mm_new->mmap_lock == (void *)lock)
+ return LCD_F_MMAP_LOCK;
+ break;
+ }
+ mm_old = (void *)curr->mm;
+ if (bpf_core_field_exists(mm_old->mmap_sem)) {
+ if (&mm_old->mmap_sem == (void *)lock)
+ return LCD_F_MMAP_LOCK;
+ }
+ break;
+ case LCB_F_SPIN: /* spinlock */
+ curr = bpf_get_current_task_btf();
+ if (&curr->sighand->siglock == (void *)lock)
+ return LCD_F_SIGHAND_LOCK;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
SEC("tp_btf/contention_begin")
int contention_begin(u64 *ctx)
{
@@ -224,7 +288,7 @@ int contention_begin(u64 *ctx)
bpf_map_update_elem(&tstamp, &pid, &zero, BPF_ANY);
pelem = bpf_map_lookup_elem(&tstamp, &pid);
if (pelem == NULL) {
- lost++;
+ __sync_fetch_and_add(&task_fail, 1);
return 0;
}
}
@@ -237,7 +301,7 @@ int contention_begin(u64 *ctx)
pelem->stack_id = bpf_get_stackid(ctx, &stacks,
BPF_F_FAST_STACK_CMP | stack_skip);
if (pelem->stack_id < 0)
- lost++;
+ __sync_fetch_and_add(&stack_fail, 1);
} else if (aggr_mode == LOCK_AGGR_TASK) {
struct task_struct *task;
@@ -281,6 +345,11 @@ int contention_end(u64 *ctx)
return 0;
duration = bpf_ktime_get_ns() - pelem->timestamp;
+ if ((__s64)duration < 0) {
+ bpf_map_delete_elem(&tstamp, &pid);
+ __sync_fetch_and_add(&time_fail, 1);
+ return 0;
+ }
switch (aggr_mode) {
case LOCK_AGGR_CALLER:
@@ -306,6 +375,12 @@ int contention_end(u64 *ctx)
data = bpf_map_lookup_elem(&lock_stat, &key);
if (!data) {
+ if (data_map_full) {
+ bpf_map_delete_elem(&tstamp, &pid);
+ __sync_fetch_and_add(&data_fail, 1);
+ return 0;
+ }
+
struct contention_data first = {
.total_time = duration,
.max_time = duration,
@@ -313,8 +388,17 @@ int contention_end(u64 *ctx)
.count = 1,
.flags = pelem->flags,
};
+ int err;
+
+ if (aggr_mode == LOCK_AGGR_ADDR)
+ first.flags |= check_lock_type(pelem->lock, pelem->flags);
- bpf_map_update_elem(&lock_stat, &key, &first, BPF_NOEXIST);
+ err = bpf_map_update_elem(&lock_stat, &key, &first, BPF_NOEXIST);
+ if (err < 0) {
+ if (err == -E2BIG)
+ data_map_full = 1;
+ __sync_fetch_and_add(&data_fail, 1);
+ }
bpf_map_delete_elem(&tstamp, &pid);
return 0;
}
@@ -332,4 +416,38 @@ int contention_end(u64 *ctx)
return 0;
}
+extern struct rq runqueues __ksym;
+
+struct rq___old {
+ raw_spinlock_t lock;
+} __attribute__((preserve_access_index));
+
+struct rq___new {
+ raw_spinlock_t __lock;
+} __attribute__((preserve_access_index));
+
+SEC("raw_tp/bpf_test_finish")
+int BPF_PROG(collect_lock_syms)
+{
+ __u64 lock_addr, lock_off;
+ __u32 lock_flag;
+
+ if (bpf_core_field_exists(struct rq___new, __lock))
+ lock_off = offsetof(struct rq___new, __lock);
+ else
+ lock_off = offsetof(struct rq___old, lock);
+
+ for (int i = 0; i < MAX_CPUS; i++) {
+ struct rq *rq = bpf_per_cpu_ptr(&runqueues, i);
+
+ if (rq == NULL)
+ break;
+
+ lock_addr = (__u64)(void *)rq + lock_off;
+ lock_flag = LOCK_CLASS_RQLOCK;
+ bpf_map_update_elem(&lock_syms, &lock_addr, &lock_flag, BPF_ANY);
+ }
+ return 0;
+}
+
char LICENSE[] SEC("license") = "Dual BSD/GPL";
diff --git a/tools/perf/util/bpf_skel/lock_data.h b/tools/perf/util/bpf_skel/lock_data.h
index 3d35fd4407ac..260062a9f2ab 100644
--- a/tools/perf/util/bpf_skel/lock_data.h
+++ b/tools/perf/util/bpf_skel/lock_data.h
@@ -15,6 +15,18 @@ struct contention_task_data {
char comm[TASK_COMM_LEN];
};
+/* default buffer size */
+#define MAX_ENTRIES 16384
+
+/*
+ * Upper bits of the flags in the contention_data are used to identify
+ * some well-known locks which do not have symbols (non-global locks).
+ */
+#define LCD_F_MMAP_LOCK (1U << 31)
+#define LCD_F_SIGHAND_LOCK (1U << 30)
+
+#define LCB_F_MAX_FLAGS (1U << 7)
+
struct contention_data {
u64 total_time;
u64 min_time;
@@ -29,4 +41,9 @@ enum lock_aggr_mode {
LOCK_AGGR_CALLER,
};
+enum lock_class_sym {
+ LOCK_CLASS_NONE,
+ LOCK_CLASS_RQLOCK,
+};
+
#endif /* UTIL_BPF_SKEL_LOCK_DATA_H */
diff --git a/tools/perf/util/bpf_skel/sample-filter.h b/tools/perf/util/bpf_skel/sample-filter.h
new file mode 100644
index 000000000000..2e96e1ab084a
--- /dev/null
+++ b/tools/perf/util/bpf_skel/sample-filter.h
@@ -0,0 +1,27 @@
+#ifndef PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H
+#define PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H
+
+#define MAX_FILTERS 64
+
+/* supported filter operations */
+enum perf_bpf_filter_op {
+ PBF_OP_EQ,
+ PBF_OP_NEQ,
+ PBF_OP_GT,
+ PBF_OP_GE,
+ PBF_OP_LT,
+ PBF_OP_LE,
+ PBF_OP_AND,
+ PBF_OP_GROUP_BEGIN,
+ PBF_OP_GROUP_END,
+};
+
+/* BPF map entry for filtering */
+struct perf_bpf_filter_entry {
+ enum perf_bpf_filter_op op;
+ __u32 part; /* sub-sample type info when it has multiple values */
+ __u64 flags; /* perf sample type flags */
+ __u64 value;
+};
+
+#endif /* PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H */ \ No newline at end of file
diff --git a/tools/perf/util/bpf_skel/sample_filter.bpf.c b/tools/perf/util/bpf_skel/sample_filter.bpf.c
new file mode 100644
index 000000000000..cffe493af1ed
--- /dev/null
+++ b/tools/perf/util/bpf_skel/sample_filter.bpf.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+// Copyright (c) 2023 Google
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+
+#include "sample-filter.h"
+
+/* BPF map that will be filled by user space */
+struct filters {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __type(key, int);
+ __type(value, struct perf_bpf_filter_entry);
+ __uint(max_entries, MAX_FILTERS);
+} filters SEC(".maps");
+
+int dropped;
+
+void *bpf_cast_to_kern_ctx(void *) __ksym;
+
+/* new kernel perf_sample_data definition */
+struct perf_sample_data___new {
+ __u64 sample_flags;
+} __attribute__((preserve_access_index));
+
+/* new kernel perf_mem_data_src definition */
+union perf_mem_data_src__new {
+ __u64 val;
+ struct {
+ __u64 mem_op:5, /* type of opcode */
+ mem_lvl:14, /* memory hierarchy level */
+ mem_snoop:5, /* snoop mode */
+ mem_lock:2, /* lock instr */
+ mem_dtlb:7, /* tlb access */
+ mem_lvl_num:4, /* memory hierarchy level number */
+ mem_remote:1, /* remote */
+ mem_snoopx:2, /* snoop mode, ext */
+ mem_blk:3, /* access blocked */
+ mem_hops:3, /* hop level */
+ mem_rsvd:18;
+ };
+};
+
+/* helper function to return the given perf sample data */
+static inline __u64 perf_get_sample(struct bpf_perf_event_data_kern *kctx,
+ struct perf_bpf_filter_entry *entry)
+{
+ struct perf_sample_data___new *data = (void *)kctx->data;
+
+ if (!bpf_core_field_exists(data->sample_flags) ||
+ (data->sample_flags & entry->flags) == 0)
+ return 0;
+
+ switch (entry->flags) {
+ case PERF_SAMPLE_IP:
+ return kctx->data->ip;
+ case PERF_SAMPLE_ID:
+ return kctx->data->id;
+ case PERF_SAMPLE_TID:
+ if (entry->part)
+ return kctx->data->tid_entry.pid;
+ else
+ return kctx->data->tid_entry.tid;
+ case PERF_SAMPLE_CPU:
+ return kctx->data->cpu_entry.cpu;
+ case PERF_SAMPLE_TIME:
+ return kctx->data->time;
+ case PERF_SAMPLE_ADDR:
+ return kctx->data->addr;
+ case PERF_SAMPLE_PERIOD:
+ return kctx->data->period;
+ case PERF_SAMPLE_TRANSACTION:
+ return kctx->data->txn;
+ case PERF_SAMPLE_WEIGHT_STRUCT:
+ if (entry->part == 1)
+ return kctx->data->weight.var1_dw;
+ if (entry->part == 2)
+ return kctx->data->weight.var2_w;
+ if (entry->part == 3)
+ return kctx->data->weight.var3_w;
+ /* fall through */
+ case PERF_SAMPLE_WEIGHT:
+ return kctx->data->weight.full;
+ case PERF_SAMPLE_PHYS_ADDR:
+ return kctx->data->phys_addr;
+ case PERF_SAMPLE_CODE_PAGE_SIZE:
+ return kctx->data->code_page_size;
+ case PERF_SAMPLE_DATA_PAGE_SIZE:
+ return kctx->data->data_page_size;
+ case PERF_SAMPLE_DATA_SRC:
+ if (entry->part == 1)
+ return kctx->data->data_src.mem_op;
+ if (entry->part == 2)
+ return kctx->data->data_src.mem_lvl_num;
+ if (entry->part == 3) {
+ __u32 snoop = kctx->data->data_src.mem_snoop;
+ __u32 snoopx = kctx->data->data_src.mem_snoopx;
+
+ return (snoopx << 5) | snoop;
+ }
+ if (entry->part == 4)
+ return kctx->data->data_src.mem_remote;
+ if (entry->part == 5)
+ return kctx->data->data_src.mem_lock;
+ if (entry->part == 6)
+ return kctx->data->data_src.mem_dtlb;
+ if (entry->part == 7)
+ return kctx->data->data_src.mem_blk;
+ if (entry->part == 8) {
+ union perf_mem_data_src__new *data = (void *)&kctx->data->data_src;
+
+ if (bpf_core_field_exists(data->mem_hops))
+ return data->mem_hops;
+
+ return 0;
+ }
+ /* return the whole word */
+ return kctx->data->data_src.val;
+ default:
+ break;
+ }
+ return 0;
+}
+
+#define CHECK_RESULT(data, op, val) \
+ if (!(data op val)) { \
+ if (!in_group) \
+ goto drop; \
+ } else if (in_group) { \
+ group_result = 1; \
+ }
+
+/* BPF program to be called from perf event overflow handler */
+SEC("perf_event")
+int perf_sample_filter(void *ctx)
+{
+ struct bpf_perf_event_data_kern *kctx;
+ struct perf_bpf_filter_entry *entry;
+ __u64 sample_data;
+ int in_group = 0;
+ int group_result = 0;
+ int i;
+
+ kctx = bpf_cast_to_kern_ctx(ctx);
+
+ for (i = 0; i < MAX_FILTERS; i++) {
+ int key = i; /* needed for verifier :( */
+
+ entry = bpf_map_lookup_elem(&filters, &key);
+ if (entry == NULL)
+ break;
+ sample_data = perf_get_sample(kctx, entry);
+
+ switch (entry->op) {
+ case PBF_OP_EQ:
+ CHECK_RESULT(sample_data, ==, entry->value)
+ break;
+ case PBF_OP_NEQ:
+ CHECK_RESULT(sample_data, !=, entry->value)
+ break;
+ case PBF_OP_GT:
+ CHECK_RESULT(sample_data, >, entry->value)
+ break;
+ case PBF_OP_GE:
+ CHECK_RESULT(sample_data, >=, entry->value)
+ break;
+ case PBF_OP_LT:
+ CHECK_RESULT(sample_data, <, entry->value)
+ break;
+ case PBF_OP_LE:
+ CHECK_RESULT(sample_data, <=, entry->value)
+ break;
+ case PBF_OP_AND:
+ CHECK_RESULT(sample_data, &, entry->value)
+ break;
+ case PBF_OP_GROUP_BEGIN:
+ in_group = 1;
+ group_result = 0;
+ break;
+ case PBF_OP_GROUP_END:
+ if (group_result == 0)
+ goto drop;
+ in_group = 0;
+ break;
+ }
+ }
+ /* generate sample data */
+ return 1;
+
+drop:
+ __sync_fetch_and_add(&dropped, 1);
+ return 0;
+}
+
+char LICENSE[] SEC("license") = "Dual BSD/GPL";
diff --git a/tools/perf/util/bpf_skel/vmlinux.h b/tools/perf/util/bpf_skel/vmlinux.h
new file mode 100644
index 000000000000..449b1ea91fc4
--- /dev/null
+++ b/tools/perf/util/bpf_skel/vmlinux.h
@@ -0,0 +1,173 @@
+#ifndef __VMLINUX_H
+#define __VMLINUX_H
+
+#include <linux/bpf.h>
+#include <linux/types.h>
+#include <linux/perf_event.h>
+#include <stdbool.h>
+
+// non-UAPI kernel data structures, used in the .bpf.c BPF tool component.
+
+// Just the fields used in these tools preserving the access index so that
+// libbpf can fixup offsets with the ones used in the kernel when loading the
+// BPF bytecode, if they differ from what is used here.
+
+typedef __u8 u8;
+typedef __u32 u32;
+typedef __u64 u64;
+typedef __s64 s64;
+
+typedef int pid_t;
+
+enum cgroup_subsys_id {
+ perf_event_cgrp_id = 8,
+};
+
+enum {
+ HI_SOFTIRQ = 0,
+ TIMER_SOFTIRQ,
+ NET_TX_SOFTIRQ,
+ NET_RX_SOFTIRQ,
+ BLOCK_SOFTIRQ,
+ IRQ_POLL_SOFTIRQ,
+ TASKLET_SOFTIRQ,
+ SCHED_SOFTIRQ,
+ HRTIMER_SOFTIRQ,
+ RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
+
+ NR_SOFTIRQS
+};
+
+typedef struct {
+ s64 counter;
+} __attribute__((preserve_access_index)) atomic64_t;
+
+typedef atomic64_t atomic_long_t;
+
+struct raw_spinlock {
+ int rawlock;
+} __attribute__((preserve_access_index));
+
+typedef struct raw_spinlock raw_spinlock_t;
+
+typedef struct {
+ struct raw_spinlock rlock;
+} __attribute__((preserve_access_index)) spinlock_t;
+
+struct sighand_struct {
+ spinlock_t siglock;
+} __attribute__((preserve_access_index));
+
+struct rw_semaphore {
+ atomic_long_t owner;
+} __attribute__((preserve_access_index));
+
+struct mutex {
+ atomic_long_t owner;
+} __attribute__((preserve_access_index));
+
+struct kernfs_node {
+ u64 id;
+} __attribute__((preserve_access_index));
+
+struct cgroup {
+ struct kernfs_node *kn;
+ int level;
+} __attribute__((preserve_access_index));
+
+struct cgroup_subsys_state {
+ struct cgroup *cgroup;
+} __attribute__((preserve_access_index));
+
+struct css_set {
+ struct cgroup_subsys_state *subsys[13];
+ struct cgroup *dfl_cgrp;
+} __attribute__((preserve_access_index));
+
+struct mm_struct {
+ struct rw_semaphore mmap_lock;
+} __attribute__((preserve_access_index));
+
+struct task_struct {
+ unsigned int flags;
+ struct mm_struct *mm;
+ pid_t pid;
+ pid_t tgid;
+ char comm[16];
+ struct sighand_struct *sighand;
+ struct css_set *cgroups;
+} __attribute__((preserve_access_index));
+
+struct trace_entry {
+ short unsigned int type;
+ unsigned char flags;
+ unsigned char preempt_count;
+ int pid;
+} __attribute__((preserve_access_index));
+
+struct trace_event_raw_irq_handler_entry {
+ struct trace_entry ent;
+ int irq;
+ u32 __data_loc_name;
+ char __data[];
+} __attribute__((preserve_access_index));
+
+struct trace_event_raw_irq_handler_exit {
+ struct trace_entry ent;
+ int irq;
+ int ret;
+ char __data[];
+} __attribute__((preserve_access_index));
+
+struct trace_event_raw_softirq {
+ struct trace_entry ent;
+ unsigned int vec;
+ char __data[];
+} __attribute__((preserve_access_index));
+
+struct trace_event_raw_workqueue_execute_start {
+ struct trace_entry ent;
+ void *work;
+ void *function;
+ char __data[];
+} __attribute__((preserve_access_index));
+
+struct trace_event_raw_workqueue_execute_end {
+ struct trace_entry ent;
+ void *work;
+ void *function;
+ char __data[];
+} __attribute__((preserve_access_index));
+
+struct trace_event_raw_workqueue_activate_work {
+ struct trace_entry ent;
+ void *work;
+ char __data[];
+} __attribute__((preserve_access_index));
+
+struct perf_sample_data {
+ u64 addr;
+ u64 period;
+ union perf_sample_weight weight;
+ u64 txn;
+ union perf_mem_data_src data_src;
+ u64 ip;
+ struct {
+ u32 pid;
+ u32 tid;
+ } tid_entry;
+ u64 time;
+ u64 id;
+ struct {
+ u32 cpu;
+ } cpu_entry;
+ u64 phys_addr;
+ u64 data_page_size;
+ u64 code_page_size;
+} __attribute__((__aligned__(64))) __attribute__((preserve_access_index));
+
+struct bpf_perf_event_data_kern {
+ struct perf_sample_data *data;
+ struct perf_event *event;
+} __attribute__((preserve_access_index));
+#endif // __VMLINUX_H
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index ea9c083ab1e3..06a8cd88cbef 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -59,7 +59,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
}
if (thread__find_map(thread, sample->cpumode, sample->ip, &al))
- al.map->dso->hit = 1;
+ map__dso(al.map)->hit = 1;
thread__put(thread);
return 0;
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index a093a15f048f..b0dafc758173 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -589,7 +589,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
}
call->ip = cursor_node->ip;
call->ms = cursor_node->ms;
- map__get(call->ms.map);
+ call->ms.map = map__get(call->ms.map);
call->srcline = cursor_node->srcline;
if (cursor_node->branch) {
@@ -701,8 +701,8 @@ static enum match_result match_chain_strings(const char *left,
static enum match_result match_chain_dso_addresses(struct map *left_map, u64 left_ip,
struct map *right_map, u64 right_ip)
{
- struct dso *left_dso = left_map ? left_map->dso : NULL;
- struct dso *right_dso = right_map ? right_map->dso : NULL;
+ struct dso *left_dso = left_map ? map__dso(left_map) : NULL;
+ struct dso *right_dso = right_map ? map__dso(right_map) : NULL;
if (left_dso != right_dso)
return left_dso < right_dso ? MATCH_LT : MATCH_GT;
@@ -724,7 +724,7 @@ static enum match_result match_chain(struct callchain_cursor_node *node,
if (match != MATCH_ERROR)
break;
/* otherwise fall-back to symbol-based comparison below */
- __fallthrough;
+ fallthrough;
case CCKEY_FUNCTION:
if (node->ms.sym && cnode->ms.sym) {
/*
@@ -745,7 +745,7 @@ static enum match_result match_chain(struct callchain_cursor_node *node,
}
}
/* otherwise fall-back to IP-based comparison below */
- __fallthrough;
+ fallthrough;
case CCKEY_ADDRESS:
default:
match = match_chain_dso_addresses(cnode->ms.map, cnode->ip, node->ms.map, node->ip);
@@ -1067,7 +1067,7 @@ int callchain_cursor_append(struct callchain_cursor *cursor,
node->ip = ip;
map__zput(node->ms.map);
node->ms = *ms;
- map__get(node->ms.map);
+ node->ms.map = map__get(node->ms.map);
node->branch = branch;
node->nr_loop_iter = nr_loop_iter;
node->iter_cycles = iter_cycles;
@@ -1112,8 +1112,11 @@ int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *samp
int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node,
bool hide_unresolved)
{
+ struct machine *machine = maps__machine(node->ms.maps);
+
al->maps = node->ms.maps;
- al->map = node->ms.map;
+ map__put(al->map);
+ al->map = map__get(node->ms.map);
al->sym = node->ms.sym;
al->srcline = node->srcline;
al->addr = node->ip;
@@ -1124,9 +1127,8 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
if (al->map == NULL)
goto out;
}
-
- if (al->maps == machine__kernel_maps(al->maps->machine)) {
- if (machine__is_host(al->maps->machine)) {
+ if (al->maps == machine__kernel_maps(machine)) {
+ if (machine__is_host(machine)) {
al->cpumode = PERF_RECORD_MISC_KERNEL;
al->level = 'k';
} else {
@@ -1134,7 +1136,7 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
al->level = 'g';
}
} else {
- if (machine__is_host(al->maps->machine)) {
+ if (machine__is_host(machine)) {
al->cpumode = PERF_RECORD_MISC_USER;
al->level = '.';
} else if (perf_guest) {
@@ -1173,7 +1175,7 @@ char *callchain_list__sym_name(struct callchain_list *cl,
if (show_dso)
scnprintf(bf + printed, bfsize - printed, " %s",
cl->ms.map ?
- cl->ms.map->dso->short_name :
+ map__dso(cl->ms.map)->short_name :
"unknown");
return bf;
@@ -1527,7 +1529,7 @@ int callchain_node__make_parent_list(struct callchain_node *node)
goto out;
*new = *chain;
new->has_children = false;
- map__get(new->ms.map);
+ new->ms.map = map__get(new->ms.map);
list_add_tail(&new->list, &head);
}
parent = parent->parent;
diff --git a/tools/perf/util/cloexec.c b/tools/perf/util/cloexec.c
index fa8248aadb59..8830604c3a8d 100644
--- a/tools/perf/util/cloexec.c
+++ b/tools/perf/util/cloexec.c
@@ -13,19 +13,6 @@
static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
-int __weak sched_getcpu(void)
-{
-#ifdef __NR_getcpu
- unsigned cpu;
- int err = syscall(__NR_getcpu, &cpu, NULL, NULL);
- if (!err)
- return cpu;
-#else
- errno = ENOSYS;
-#endif
- return -1;
-}
-
static int perf_flag_probe(void)
{
/* use 'safest' configuration as used in evsel__fallback() */
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 5e564974fba4..75d9c73e0184 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -77,9 +77,9 @@ static struct perf_cpu_map *cpu_map__from_entries(const struct perf_record_cpu_m
* otherwise it would become 65535.
*/
if (data->cpus_data.cpu[i] == (u16) -1)
- map->map[i].cpu = -1;
+ RC_CHK_ACCESS(map)->map[i].cpu = -1;
else
- map->map[i].cpu = (int) data->cpus_data.cpu[i];
+ RC_CHK_ACCESS(map)->map[i].cpu = (int) data->cpus_data.cpu[i];
}
}
@@ -107,7 +107,7 @@ static struct perf_cpu_map *cpu_map__from_mask(const struct perf_record_cpu_map_
perf_record_cpu_map_data__read_one_mask(data, i, local_copy);
for_each_set_bit(cpu, local_copy, 64)
- map->map[j++].cpu = cpu + cpus_per_i;
+ RC_CHK_ACCESS(map)->map[j++].cpu = cpu + cpus_per_i;
}
return map;
@@ -124,11 +124,11 @@ static struct perf_cpu_map *cpu_map__from_range(const struct perf_record_cpu_map
return NULL;
if (data->range_cpu_data.any_cpu)
- map->map[i++].cpu = -1;
+ RC_CHK_ACCESS(map)->map[i++].cpu = -1;
for (int cpu = data->range_cpu_data.start_cpu; cpu <= data->range_cpu_data.end_cpu;
i++, cpu++)
- map->map[i].cpu = cpu;
+ RC_CHK_ACCESS(map)->map[i].cpu = cpu;
return map;
}
@@ -160,16 +160,11 @@ size_t cpu_map__fprintf(struct perf_cpu_map *map, FILE *fp)
struct perf_cpu_map *perf_cpu_map__empty_new(int nr)
{
- struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int) * nr);
+ struct perf_cpu_map *cpus = perf_cpu_map__alloc(nr);
if (cpus != NULL) {
- int i;
-
- cpus->nr = nr;
- for (i = 0; i < nr; i++)
- cpus->map[i].cpu = -1;
-
- refcount_set(&cpus->refcnt, 1);
+ for (int i = 0; i < nr; i++)
+ RC_CHK_ACCESS(cpus)->map[i].cpu = -1;
}
return cpus;
@@ -239,7 +234,7 @@ struct cpu_aggr_map *cpu_aggr_map__new(const struct perf_cpu_map *cpus,
{
int idx;
struct perf_cpu cpu;
- struct cpu_aggr_map *c = cpu_aggr_map__empty_new(cpus->nr);
+ struct cpu_aggr_map *c = cpu_aggr_map__empty_new(perf_cpu_map__nr(cpus));
if (!c)
return NULL;
@@ -263,7 +258,7 @@ struct cpu_aggr_map *cpu_aggr_map__new(const struct perf_cpu_map *cpus,
}
}
/* Trim. */
- if (c->nr != cpus->nr) {
+ if (c->nr != perf_cpu_map__nr(cpus)) {
struct cpu_aggr_map *trimmed_c =
realloc(c,
sizeof(struct cpu_aggr_map) + sizeof(struct aggr_cpu_id) * c->nr);
@@ -582,31 +577,31 @@ size_t cpu_map__snprint(struct perf_cpu_map *map, char *buf, size_t size)
#define COMMA first ? "" : ","
- for (i = 0; i < map->nr + 1; i++) {
+ for (i = 0; i < perf_cpu_map__nr(map) + 1; i++) {
struct perf_cpu cpu = { .cpu = INT_MAX };
- bool last = i == map->nr;
+ bool last = i == perf_cpu_map__nr(map);
if (!last)
- cpu = map->map[i];
+ cpu = perf_cpu_map__cpu(map, i);
if (start == -1) {
start = i;
if (last) {
ret += snprintf(buf + ret, size - ret,
"%s%d", COMMA,
- map->map[i].cpu);
+ perf_cpu_map__cpu(map, i).cpu);
}
- } else if (((i - start) != (cpu.cpu - map->map[start].cpu)) || last) {
+ } else if (((i - start) != (cpu.cpu - perf_cpu_map__cpu(map, start).cpu)) || last) {
int end = i - 1;
if (start == end) {
ret += snprintf(buf + ret, size - ret,
"%s%d", COMMA,
- map->map[start].cpu);
+ perf_cpu_map__cpu(map, start).cpu);
} else {
ret += snprintf(buf + ret, size - ret,
"%s%d-%d", COMMA,
- map->map[start].cpu, map->map[end].cpu);
+ perf_cpu_map__cpu(map, start).cpu, perf_cpu_map__cpu(map, end).cpu);
}
first = false;
start = i;
@@ -633,7 +628,7 @@ size_t cpu_map__snprint_mask(struct perf_cpu_map *map, char *buf, size_t size)
int i, cpu;
char *ptr = buf;
unsigned char *bitmap;
- struct perf_cpu last_cpu = perf_cpu_map__cpu(map, map->nr - 1);
+ struct perf_cpu last_cpu = perf_cpu_map__cpu(map, perf_cpu_map__nr(map) - 1);
if (buf == NULL)
return 0;
@@ -644,7 +639,7 @@ size_t cpu_map__snprint_mask(struct perf_cpu_map *map, char *buf, size_t size)
return 0;
}
- for (i = 0; i < map->nr; i++) {
+ for (i = 0; i < perf_cpu_map__nr(map); i++) {
cpu = perf_cpu_map__cpu(map, i).cpu;
bitmap[cpu / 8] |= 1 << (cpu % 8);
}
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index c2f5824a3a22..e3426541e0aa 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -35,6 +35,9 @@ struct cpu_aggr_map {
struct aggr_cpu_id map[];
};
+#define cpu_aggr_map__for_each_idx(idx, aggr_map) \
+ for ((idx) = 0; (idx) < aggr_map->nr; (idx)++)
+
struct perf_record_cpu_map_data;
bool perf_record_cpu_map_data__test_bit(int i, const struct perf_record_cpu_map_data *data);
diff --git a/tools/perf/util/cputopo.c b/tools/perf/util/cputopo.c
index e08797c3cdbc..ca1d833a0c26 100644
--- a/tools/perf/util/cputopo.c
+++ b/tools/perf/util/cputopo.c
@@ -238,6 +238,20 @@ static bool has_die_topology(void)
return true;
}
+const struct cpu_topology *online_topology(void)
+{
+ static const struct cpu_topology *topology;
+
+ if (!topology) {
+ topology = cpu_topology__new();
+ if (!topology) {
+ pr_err("Error creating CPU topology");
+ abort();
+ }
+ }
+ return topology;
+}
+
struct cpu_topology *cpu_topology__new(void)
{
struct cpu_topology *tp = NULL;
diff --git a/tools/perf/util/cputopo.h b/tools/perf/util/cputopo.h
index 969e5920a00e..8d42f6102954 100644
--- a/tools/perf/util/cputopo.h
+++ b/tools/perf/util/cputopo.h
@@ -56,6 +56,11 @@ struct hybrid_topology {
struct hybrid_topology_node nodes[];
};
+/*
+ * The topology for online CPUs, lazily created.
+ */
+const struct cpu_topology *online_topology(void);
+
struct cpu_topology *cpu_topology__new(void);
void cpu_topology__delete(struct cpu_topology *tp);
/* Determine from the core list whether SMT was enabled. */
diff --git a/tools/perf/util/cs-etm-base.c b/tools/perf/util/cs-etm-base.c
index 5f48b756c4cf..4abe416e3feb 100644
--- a/tools/perf/util/cs-etm-base.c
+++ b/tools/perf/util/cs-etm-base.c
@@ -148,7 +148,8 @@ static void cs_etm__print_auxtrace_info(u64 *val, int num)
for (i = CS_HEADER_VERSION_MAX; cpu < num; cpu++) {
if (version == 0)
err = cs_etm__print_cpu_metadata_v0(val, &i);
- else if (version == 1)
+ /* printing same for both, but value bit flags added on v2 */
+ else if ((version == 1) || (version == 2))
err = cs_etm__print_cpu_metadata_v1(val, &i);
if (err)
return;
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
index d0e521dfcf35..82a27ab90c8b 100644
--- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
@@ -668,6 +668,7 @@ cs_etm_decoder__create_etm_decoder(struct cs_etm_decoder_params *d_params,
switch (t_params->protocol) {
case CS_ETM_PROTO_ETMV3:
case CS_ETM_PROTO_PTM:
+ csid = (t_params->etmv3.reg_idr & CORESIGHT_TRACE_ID_VAL_MASK);
cs_etm_decoder__gen_etmv3_config(t_params, &config_etmv3);
decoder->decoder_name = (t_params->protocol == CS_ETM_PROTO_ETMV3) ?
OCSD_BUILTIN_DCD_ETMV3 :
@@ -675,11 +676,13 @@ cs_etm_decoder__create_etm_decoder(struct cs_etm_decoder_params *d_params,
trace_config = &config_etmv3;
break;
case CS_ETM_PROTO_ETMV4i:
+ csid = (t_params->etmv4.reg_traceidr & CORESIGHT_TRACE_ID_VAL_MASK);
cs_etm_decoder__gen_etmv4_config(t_params, &trace_config_etmv4);
decoder->decoder_name = OCSD_BUILTIN_DCD_ETMV4I;
trace_config = &trace_config_etmv4;
break;
case CS_ETM_PROTO_ETE:
+ csid = (t_params->ete.reg_traceidr & CORESIGHT_TRACE_ID_VAL_MASK);
cs_etm_decoder__gen_ete_config(t_params, &trace_config_ete);
decoder->decoder_name = OCSD_BUILTIN_DCD_ETE;
trace_config = &trace_config_ete;
@@ -688,6 +691,10 @@ cs_etm_decoder__create_etm_decoder(struct cs_etm_decoder_params *d_params,
return -1;
}
+ /* if the CPU has no trace ID associated, no decoder needed */
+ if (csid == CORESIGHT_TRACE_ID_UNUSED_VAL)
+ return 0;
+
if (d_params->operation == CS_ETM_OPERATION_DECODE) {
if (ocsd_dt_create_decoder(decoder->dcd_tree,
decoder->decoder_name,
diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
index 92a855fbe5b8..21d403f55d96 100644
--- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
+++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h
@@ -60,10 +60,10 @@ struct cs_etm_decoder_params {
int operation;
void (*packet_printer)(const char *msg);
cs_etm_mem_cb_type mem_acc_cb;
- u8 formatted;
- u8 fsyncs;
- u8 hsyncs;
- u8 frame_aligned;
+ bool formatted;
+ bool fsyncs;
+ bool hsyncs;
+ bool frame_aligned;
void *data;
};
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
index f65bac5ddbdb..91299cc56bf7 100644
--- a/tools/perf/util/cs-etm.c
+++ b/tools/perf/util/cs-etm.c
@@ -38,6 +38,7 @@
#include "tsc.h"
#include <tools/libc_compat.h>
#include "util/synthetic-events.h"
+#include "util/util.h"
struct cs_etm_auxtrace {
struct auxtrace auxtrace;
@@ -49,10 +50,25 @@ struct cs_etm_auxtrace {
struct thread *unknown_thread;
struct perf_tsc_conversion tc;
- u8 timeless_decoding;
- u8 snapshot_mode;
- u8 data_queued;
- u8 has_virtual_ts; /* Virtual/Kernel timestamps in the trace. */
+ /*
+ * Timeless has no timestamps in the trace so overlapping mmap lookups
+ * are less accurate but produces smaller trace data. We use context IDs
+ * in the trace instead of matching timestamps with fork records so
+ * they're not really needed in the general case. Overlapping mmaps
+ * happen in cases like between a fork and an exec.
+ */
+ bool timeless_decoding;
+
+ /*
+ * Per-thread ignores the trace channel ID and instead assumes that
+ * everything in a buffer comes from the same process regardless of
+ * which CPU it ran on. It also implies no context IDs so the TID is
+ * taken from the auxtrace buffer.
+ */
+ bool per_thread_decoding;
+ bool snapshot_mode;
+ bool data_queued;
+ bool has_virtual_ts; /* Virtual/Kernel timestamps in the trace. */
int num_cpu;
u64 latest_kernel_timestamp;
@@ -97,7 +113,7 @@ struct cs_etm_queue {
/* RB tree for quick conversion between traceID and metadata pointers */
static struct intlist *traceid_list;
-static int cs_etm__process_queues(struct cs_etm_auxtrace *etm);
+static int cs_etm__process_timestamped_queues(struct cs_etm_auxtrace *etm);
static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
pid_t tid);
static int cs_etm__get_data_block(struct cs_etm_queue *etmq);
@@ -196,6 +212,189 @@ int cs_etm__get_pid_fmt(u8 trace_chan_id, u64 *pid_fmt)
return 0;
}
+static int cs_etm__map_trace_id(u8 trace_chan_id, u64 *cpu_metadata)
+{
+ struct int_node *inode;
+
+ /* Get an RB node for this CPU */
+ inode = intlist__findnew(traceid_list, trace_chan_id);
+
+ /* Something went wrong, no need to continue */
+ if (!inode)
+ return -ENOMEM;
+
+ /*
+ * The node for that CPU should not be taken.
+ * Back out if that's the case.
+ */
+ if (inode->priv)
+ return -EINVAL;
+
+ /* All good, associate the traceID with the metadata pointer */
+ inode->priv = cpu_metadata;
+
+ return 0;
+}
+
+static int cs_etm__metadata_get_trace_id(u8 *trace_chan_id, u64 *cpu_metadata)
+{
+ u64 cs_etm_magic = cpu_metadata[CS_ETM_MAGIC];
+
+ switch (cs_etm_magic) {
+ case __perf_cs_etmv3_magic:
+ *trace_chan_id = (u8)(cpu_metadata[CS_ETM_ETMTRACEIDR] &
+ CORESIGHT_TRACE_ID_VAL_MASK);
+ break;
+ case __perf_cs_etmv4_magic:
+ case __perf_cs_ete_magic:
+ *trace_chan_id = (u8)(cpu_metadata[CS_ETMV4_TRCTRACEIDR] &
+ CORESIGHT_TRACE_ID_VAL_MASK);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * update metadata trace ID from the value found in the AUX_HW_INFO packet.
+ * This will also clear the CORESIGHT_TRACE_ID_UNUSED_FLAG flag if present.
+ */
+static int cs_etm__metadata_set_trace_id(u8 trace_chan_id, u64 *cpu_metadata)
+{
+ u64 cs_etm_magic = cpu_metadata[CS_ETM_MAGIC];
+
+ switch (cs_etm_magic) {
+ case __perf_cs_etmv3_magic:
+ cpu_metadata[CS_ETM_ETMTRACEIDR] = trace_chan_id;
+ break;
+ case __perf_cs_etmv4_magic:
+ case __perf_cs_ete_magic:
+ cpu_metadata[CS_ETMV4_TRCTRACEIDR] = trace_chan_id;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * 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.
+ *
+ */
+static u64 *get_cpu_data(struct cs_etm_auxtrace *etm, int cpu)
+{
+ int i;
+ u64 *metadata = NULL;
+
+ for (i = 0; i < etm->num_cpu; i++) {
+ if (etm->metadata[i][CS_ETM_CPU] == (u64)cpu) {
+ metadata = etm->metadata[i];
+ break;
+ }
+ }
+
+ return metadata;
+}
+
+/*
+ * Handle the PERF_RECORD_AUX_OUTPUT_HW_ID event.
+ *
+ * The payload associates the Trace ID and the CPU.
+ * The routine is tolerant of seeing multiple packets with the same association,
+ * but a CPU / Trace ID association changing during a session is an error.
+ */
+static int cs_etm__process_aux_output_hw_id(struct perf_session *session,
+ union perf_event *event)
+{
+ struct cs_etm_auxtrace *etm;
+ struct perf_sample sample;
+ struct int_node *inode;
+ struct evsel *evsel;
+ u64 *cpu_data;
+ u64 hw_id;
+ int cpu, version, err;
+ u8 trace_chan_id, curr_chan_id;
+
+ /* extract and parse the HW ID */
+ hw_id = event->aux_output_hw_id.hw_id;
+ version = FIELD_GET(CS_AUX_HW_ID_VERSION_MASK, hw_id);
+ trace_chan_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id);
+
+ /* check that we can handle this version */
+ if (version > CS_AUX_HW_ID_CURR_VERSION)
+ return -EINVAL;
+
+ /* get access to the etm metadata */
+ etm = container_of(session->auxtrace, struct cs_etm_auxtrace, auxtrace);
+ if (!etm || !etm->metadata)
+ return -EINVAL;
+
+ /* parse the sample to get the CPU */
+ evsel = evlist__event2evsel(session->evlist, event);
+ if (!evsel)
+ return -EINVAL;
+ err = evsel__parse_sample(evsel, event, &sample);
+ if (err)
+ return err;
+ cpu = sample.cpu;
+ if (cpu == -1) {
+ /* no CPU in the sample - possibly recorded with an old version of perf */
+ pr_err("CS_ETM: no CPU AUX_OUTPUT_HW_ID sample. Use compatible perf to record.");
+ return -EINVAL;
+ }
+
+ /* See if the ID is mapped to a CPU, and it matches the current CPU */
+ inode = intlist__find(traceid_list, trace_chan_id);
+ if (inode) {
+ cpu_data = inode->priv;
+ if ((int)cpu_data[CS_ETM_CPU] != cpu) {
+ pr_err("CS_ETM: map mismatch between HW_ID packet CPU and Trace ID\n");
+ return -EINVAL;
+ }
+
+ /* check that the mapped ID matches */
+ err = cs_etm__metadata_get_trace_id(&curr_chan_id, cpu_data);
+ if (err)
+ return err;
+ if (curr_chan_id != trace_chan_id) {
+ pr_err("CS_ETM: mismatch between CPU trace ID and HW_ID packet ID\n");
+ return -EINVAL;
+ }
+
+ /* mapped and matched - return OK */
+ return 0;
+ }
+
+ cpu_data = get_cpu_data(etm, cpu);
+ if (cpu_data == NULL)
+ return err;
+
+ /* not one we've seen before - lets map it */
+ err = cs_etm__map_trace_id(trace_chan_id, cpu_data);
+ if (err)
+ return err;
+
+ /*
+ * if we are picking up the association from the packet, need to plug
+ * the correct trace ID into the metadata for setting up decoders later.
+ */
+ err = cs_etm__metadata_set_trace_id(trace_chan_id, cpu_data);
+ return err;
+}
+
void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq,
u8 trace_chan_id)
{
@@ -330,7 +529,7 @@ static struct cs_etm_traceid_queue
struct cs_etm_traceid_queue *tidq, **traceid_queues;
struct cs_etm_auxtrace *etm = etmq->etm;
- if (etm->timeless_decoding)
+ if (etm->per_thread_decoding)
trace_chan_id = CS_ETM_PER_THREAD_TRACEID;
traceid_queues_list = etmq->traceid_queues_list;
@@ -569,10 +768,15 @@ static int cs_etm__flush_events(struct perf_session *session,
if (!tool->ordered_events)
return -EINVAL;
- if (etm->timeless_decoding)
+ if (etm->timeless_decoding) {
+ /*
+ * Pass tid = -1 to process all queues. But likely they will have
+ * already been processed on PERF_RECORD_EXIT anyway.
+ */
return cs_etm__process_timeless_queues(etm, -1);
+ }
- return cs_etm__process_queues(etm);
+ return cs_etm__process_timestamped_queues(etm);
}
static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq)
@@ -704,6 +908,7 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
struct thread *thread;
struct machine *machine;
struct addr_location al;
+ struct dso *dso;
struct cs_etm_traceid_queue *tidq;
if (!etmq)
@@ -722,27 +927,31 @@ static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u8 trace_chan_id,
thread = etmq->etm->unknown_thread;
}
- if (!thread__find_map(thread, cpumode, address, &al) || !al.map->dso)
+ if (!thread__find_map(thread, cpumode, address, &al))
+ return 0;
+
+ dso = map__dso(al.map);
+ if (!dso)
return 0;
- if (al.map->dso->data.status == DSO_DATA_STATUS_ERROR &&
- dso__data_status_seen(al.map->dso, DSO_DATA_STATUS_SEEN_ITRACE))
+ if (dso->data.status == DSO_DATA_STATUS_ERROR &&
+ dso__data_status_seen(dso, DSO_DATA_STATUS_SEEN_ITRACE))
return 0;
- offset = al.map->map_ip(al.map, address);
+ offset = map__map_ip(al.map, address);
map__load(al.map);
- len = dso__data_read_offset(al.map->dso, machine, offset, buffer, size);
+ len = dso__data_read_offset(dso, machine, offset, buffer, size);
if (len <= 0) {
ui__warning_once("CS ETM Trace: Missing DSO. Use 'perf archive' or debuginfod to export data from the traced system.\n"
" Enable CONFIG_PROC_KCORE or use option '-k /path/to/vmlinux' for kernel symbols.\n");
- if (!al.map->dso->auxtrace_warned) {
+ if (!dso->auxtrace_warned) {
pr_err("CS ETM Trace: Debug data not found for address %#"PRIx64" in %s\n",
address,
- al.map->dso->long_name ? al.map->dso->long_name : "Unknown");
- al.map->dso->auxtrace_warned = true;
+ dso->long_name ? dso->long_name : "Unknown");
+ dso->auxtrace_warned = true;
}
return 0;
}
@@ -899,7 +1108,7 @@ static int cs_etm__queue_first_cs_timestamp(struct cs_etm_auxtrace *etm,
* chronological order.
*
* Note that packets decoded above are still in the traceID's packet
- * queue and will be processed in cs_etm__process_queues().
+ * queue and will be processed in cs_etm__process_timestamped_queues().
*/
cs_queue_nr = TO_CS_QUEUE_NR(queue_nr, trace_chan_id);
ret = auxtrace_heap__add(&etm->heap, cs_queue_nr, cs_timestamp);
@@ -1180,9 +1389,7 @@ static inline u64 cs_etm__resolve_sample_time(struct cs_etm_queue *etmq,
struct cs_etm_auxtrace *etm = etmq->etm;
struct cs_etm_packet_queue *packet_queue = &tidq->packet_queue;
- if (etm->timeless_decoding)
- return 0;
- else if (etm->has_virtual_ts)
+ if (!etm->timeless_decoding && etm->has_virtual_ts)
return packet_queue->cs_timestamp;
else
return etm->latest_kernel_timestamp;
@@ -2162,7 +2369,7 @@ static void cs_etm__clear_all_traceid_queues(struct cs_etm_queue *etmq)
}
}
-static int cs_etm__run_decoder(struct cs_etm_queue *etmq)
+static int cs_etm__run_per_thread_timeless_decoder(struct cs_etm_queue *etmq)
{
int err = 0;
struct cs_etm_traceid_queue *tidq;
@@ -2200,6 +2407,51 @@ static int cs_etm__run_decoder(struct cs_etm_queue *etmq)
return err;
}
+static int cs_etm__run_per_cpu_timeless_decoder(struct cs_etm_queue *etmq)
+{
+ int idx, err = 0;
+ struct cs_etm_traceid_queue *tidq;
+ struct int_node *inode;
+
+ /* Go through each buffer in the queue and decode them one by one */
+ while (1) {
+ err = cs_etm__get_data_block(etmq);
+ if (err <= 0)
+ return err;
+
+ /* Run trace decoder until buffer consumed or end of trace */
+ do {
+ err = cs_etm__decode_data_block(etmq);
+ if (err)
+ return err;
+
+ /*
+ * cs_etm__run_per_thread_timeless_decoder() runs on a
+ * single traceID queue because each TID has a separate
+ * buffer. But here in per-cpu mode we need to iterate
+ * over each channel instead.
+ */
+ intlist__for_each_entry(inode,
+ etmq->traceid_queues_list) {
+ idx = (int)(intptr_t)inode->priv;
+ tidq = etmq->traceid_queues[idx];
+ cs_etm__process_traceid_queue(etmq, tidq);
+ }
+ } while (etmq->buf_len);
+
+ intlist__for_each_entry(inode, etmq->traceid_queues_list) {
+ idx = (int)(intptr_t)inode->priv;
+ tidq = etmq->traceid_queues[idx];
+ /* Flush any remaining branch stack entries */
+ err = cs_etm__end_block(etmq, tidq);
+ if (err)
+ return err;
+ }
+ }
+
+ return err;
+}
+
static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
pid_t tid)
{
@@ -2214,22 +2466,30 @@ static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm,
if (!etmq)
continue;
- tidq = cs_etm__etmq_get_traceid_queue(etmq,
- CS_ETM_PER_THREAD_TRACEID);
+ /*
+ * Per-cpu mode has contextIDs in the trace and the decoder
+ * calls cs_etm__set_pid_tid_cpu() automatically so no need
+ * to do this here
+ */
+ if (etm->per_thread_decoding) {
+ tidq = cs_etm__etmq_get_traceid_queue(
+ etmq, CS_ETM_PER_THREAD_TRACEID);
- if (!tidq)
- continue;
+ if (!tidq)
+ continue;
- if ((tid == -1) || (tidq->tid == tid)) {
- cs_etm__set_pid_tid_cpu(etm, tidq);
- cs_etm__run_decoder(etmq);
- }
+ if ((tid == -1) || (tidq->tid == tid)) {
+ cs_etm__set_pid_tid_cpu(etm, tidq);
+ cs_etm__run_per_thread_timeless_decoder(etmq);
+ }
+ } else
+ cs_etm__run_per_cpu_timeless_decoder(etmq);
}
return 0;
}
-static int cs_etm__process_queues(struct cs_etm_auxtrace *etm)
+static int cs_etm__process_timestamped_queues(struct cs_etm_auxtrace *etm)
{
int ret = 0;
unsigned int cs_queue_nr, queue_nr, i;
@@ -2406,7 +2666,6 @@ static int cs_etm__process_event(struct perf_session *session,
struct perf_sample *sample,
struct perf_tool *tool)
{
- u64 sample_kernel_timestamp;
struct cs_etm_auxtrace *etm = container_of(session->auxtrace,
struct cs_etm_auxtrace,
auxtrace);
@@ -2419,33 +2678,39 @@ static int cs_etm__process_event(struct perf_session *session,
return -EINVAL;
}
- if (sample->time && (sample->time != (u64) -1))
- sample_kernel_timestamp = sample->time;
- else
- sample_kernel_timestamp = 0;
-
- /*
- * Don't wait for cs_etm__flush_events() in per-thread/timeless mode to start the decode. We
- * need the tid of the PERF_RECORD_EXIT event to assign to the synthesised samples because
- * ETM_OPT_CTXTID is not enabled.
- */
- if (etm->timeless_decoding &&
- event->header.type == PERF_RECORD_EXIT)
- return cs_etm__process_timeless_queues(etm,
- event->fork.tid);
+ switch (event->header.type) {
+ case PERF_RECORD_EXIT:
+ /*
+ * Don't need to wait for cs_etm__flush_events() in per-thread mode to
+ * start the decode because we know there will be no more trace from
+ * this thread. All this does is emit samples earlier than waiting for
+ * the flush in other modes, but with timestamps it makes sense to wait
+ * for flush so that events from different threads are interleaved
+ * properly.
+ */
+ if (etm->per_thread_decoding && etm->timeless_decoding)
+ return cs_etm__process_timeless_queues(etm,
+ event->fork.tid);
+ break;
- if (event->header.type == PERF_RECORD_ITRACE_START)
+ case PERF_RECORD_ITRACE_START:
return cs_etm__process_itrace_start(etm, event);
- else if (event->header.type == PERF_RECORD_SWITCH_CPU_WIDE)
+
+ case PERF_RECORD_SWITCH_CPU_WIDE:
return cs_etm__process_switch_cpu_wide(etm, event);
- if (!etm->timeless_decoding && event->header.type == PERF_RECORD_AUX) {
+ case PERF_RECORD_AUX:
/*
* Record the latest kernel timestamp available in the header
* for samples so that synthesised samples occur from this point
* onwards.
*/
- etm->latest_kernel_timestamp = sample_kernel_timestamp;
+ if (sample->time && (sample->time != (u64)-1))
+ etm->latest_kernel_timestamp = sample->time;
+ break;
+
+ default:
+ break;
}
return 0;
@@ -2517,26 +2782,29 @@ static int cs_etm__process_auxtrace_event(struct perf_session *session,
return 0;
}
-static bool cs_etm__is_timeless_decoding(struct cs_etm_auxtrace *etm)
+static int cs_etm__setup_timeless_decoding(struct cs_etm_auxtrace *etm)
{
struct evsel *evsel;
struct evlist *evlist = etm->session->evlist;
- bool timeless_decoding = true;
/* Override timeless mode with user input from --itrace=Z */
- if (etm->synth_opts.timeless_decoding)
- return true;
+ if (etm->synth_opts.timeless_decoding) {
+ etm->timeless_decoding = true;
+ return 0;
+ }
/*
- * Circle through the list of event and complain if we find one
- * with the time bit set.
+ * Find the cs_etm evsel and look at what its timestamp setting was
*/
- evlist__for_each_entry(evlist, evsel) {
- if ((evsel->core.attr.sample_type & PERF_SAMPLE_TIME))
- timeless_decoding = false;
- }
+ evlist__for_each_entry(evlist, evsel)
+ if (cs_etm__evsel_is_auxtrace(etm->session, evsel)) {
+ etm->timeless_decoding =
+ !(evsel->core.attr.config & BIT(ETM_OPT_TS));
+ return 0;
+ }
- return timeless_decoding;
+ pr_err("CS ETM: Couldn't find ETM evsel\n");
+ return -EINVAL;
}
/*
@@ -2644,12 +2912,27 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o
}
/*
- * In per-thread mode, CPU is set to -1, but TID will be set instead. See
- * auxtrace_mmap_params__set_idx(). Return 'not found' if neither CPU nor TID match.
+ * In per-thread mode, auxtrace CPU is set to -1, but TID will be set instead. See
+ * auxtrace_mmap_params__set_idx(). However, the sample AUX event will contain a
+ * CPU as we set this always for the AUX_OUTPUT_HW_ID event.
+ * So now compare only TIDs if auxtrace CPU is -1, and CPUs if auxtrace CPU is not -1.
+ * Return 'not found' if mismatch.
*/
- if ((auxtrace_event->cpu == (__u32) -1 && auxtrace_event->tid != sample->tid) ||
- auxtrace_event->cpu != sample->cpu)
+ if (auxtrace_event->cpu == (__u32) -1) {
+ etm->per_thread_decoding = true;
+ if (auxtrace_event->tid != sample->tid)
+ return 1;
+ } else if (auxtrace_event->cpu != sample->cpu) {
+ if (etm->per_thread_decoding) {
+ /*
+ * Found a per-cpu buffer after a per-thread one was
+ * already found
+ */
+ pr_err("CS ETM: Inconsistent per-thread/per-cpu mode.\n");
+ return -EINVAL;
+ }
return 1;
+ }
if (aux_event->flags & PERF_AUX_FLAG_OVERWRITE) {
/*
@@ -2697,6 +2980,17 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o
return 1;
}
+static int cs_etm__process_aux_hw_id_cb(struct perf_session *session, union perf_event *event,
+ u64 offset __maybe_unused, void *data __maybe_unused)
+{
+ /* look to handle PERF_RECORD_AUX_OUTPUT_HW_ID early to ensure decoders can be set up */
+ if (event->header.type == PERF_RECORD_AUX_OUTPUT_HW_ID) {
+ (*(int *)data)++; /* increment found count */
+ return cs_etm__process_aux_output_hw_id(session, event);
+ }
+ return 0;
+}
+
static int cs_etm__queue_aux_records_cb(struct perf_session *session, union perf_event *event,
u64 offset __maybe_unused, void *data __maybe_unused)
{
@@ -2804,18 +3098,77 @@ static bool cs_etm__has_virtual_ts(u64 **metadata, int num_cpu)
return true;
}
+/* map trace ids to correct metadata block, from information in metadata */
+static int cs_etm__map_trace_ids_metadata(int num_cpu, u64 **metadata)
+{
+ u64 cs_etm_magic;
+ u8 trace_chan_id;
+ int i, err;
+
+ for (i = 0; i < num_cpu; i++) {
+ cs_etm_magic = metadata[i][CS_ETM_MAGIC];
+ switch (cs_etm_magic) {
+ case __perf_cs_etmv3_magic:
+ metadata[i][CS_ETM_ETMTRACEIDR] &= CORESIGHT_TRACE_ID_VAL_MASK;
+ trace_chan_id = (u8)(metadata[i][CS_ETM_ETMTRACEIDR]);
+ break;
+ case __perf_cs_etmv4_magic:
+ case __perf_cs_ete_magic:
+ metadata[i][CS_ETMV4_TRCTRACEIDR] &= CORESIGHT_TRACE_ID_VAL_MASK;
+ trace_chan_id = (u8)(metadata[i][CS_ETMV4_TRCTRACEIDR]);
+ break;
+ default:
+ /* unknown magic number */
+ return -EINVAL;
+ }
+ err = cs_etm__map_trace_id(trace_chan_id, metadata[i]);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * If we found AUX_HW_ID packets, then set any metadata marked as unused to the
+ * unused value to reduce the number of unneeded decoders created.
+ */
+static int cs_etm__clear_unused_trace_ids_metadata(int num_cpu, u64 **metadata)
+{
+ u64 cs_etm_magic;
+ int i;
+
+ for (i = 0; i < num_cpu; i++) {
+ cs_etm_magic = metadata[i][CS_ETM_MAGIC];
+ switch (cs_etm_magic) {
+ case __perf_cs_etmv3_magic:
+ if (metadata[i][CS_ETM_ETMTRACEIDR] & CORESIGHT_TRACE_ID_UNUSED_FLAG)
+ metadata[i][CS_ETM_ETMTRACEIDR] = CORESIGHT_TRACE_ID_UNUSED_VAL;
+ break;
+ case __perf_cs_etmv4_magic:
+ case __perf_cs_ete_magic:
+ if (metadata[i][CS_ETMV4_TRCTRACEIDR] & CORESIGHT_TRACE_ID_UNUSED_FLAG)
+ metadata[i][CS_ETMV4_TRCTRACEIDR] = CORESIGHT_TRACE_ID_UNUSED_VAL;
+ break;
+ default:
+ /* unknown magic number */
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
int cs_etm__process_auxtrace_info_full(union perf_event *event,
struct perf_session *session)
{
struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
struct cs_etm_auxtrace *etm = NULL;
- struct int_node *inode;
struct perf_record_time_conv *tc = &session->time_conv;
int event_header_size = sizeof(struct perf_event_header);
int total_size = auxtrace_info->header.size;
int priv_size = 0;
- int num_cpu, trcidr_idx;
+ int num_cpu;
int err = 0;
+ int aux_hw_id_found;
int i, j;
u64 *ptr = NULL;
u64 **metadata = NULL;
@@ -2853,23 +3206,13 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
cs_etm__create_meta_blk(ptr, &i,
CS_ETM_PRIV_MAX,
CS_ETM_NR_TRC_PARAMS_V0);
-
- /* The traceID is our handle */
- trcidr_idx = CS_ETM_ETMTRACEIDR;
-
} else if (ptr[i] == __perf_cs_etmv4_magic) {
metadata[j] =
cs_etm__create_meta_blk(ptr, &i,
CS_ETMV4_PRIV_MAX,
CS_ETMV4_NR_TRC_PARAMS_V0);
-
- /* The traceID is our handle */
- trcidr_idx = CS_ETMV4_TRCTRACEIDR;
} else if (ptr[i] == __perf_cs_ete_magic) {
metadata[j] = cs_etm__create_meta_blk(ptr, &i, CS_ETE_PRIV_MAX, -1);
-
- /* ETE shares first part of metadata with ETMv4 */
- trcidr_idx = CS_ETMV4_TRCTRACEIDR;
} else {
ui__error("CS ETM Trace: Unrecognised magic number %#"PRIx64". File could be from a newer version of perf.\n",
ptr[i]);
@@ -2881,26 +3224,6 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
err = -ENOMEM;
goto err_free_metadata;
}
-
- /* Get an RB node for this CPU */
- inode = intlist__findnew(traceid_list, metadata[j][trcidr_idx]);
-
- /* Something went wrong, no need to continue */
- if (!inode) {
- err = -ENOMEM;
- goto err_free_metadata;
- }
-
- /*
- * The node for that CPU should not be taken.
- * Back out if that's the case.
- */
- if (inode->priv) {
- err = -EINVAL;
- goto err_free_metadata;
- }
- /* All good, associate the traceID with the metadata pointer */
- inode->priv = metadata[j];
}
/*
@@ -2943,7 +3266,6 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
etm->snapshot_mode = (ptr[CS_ETM_SNAPSHOT] != 0);
etm->metadata = metadata;
etm->auxtrace_type = auxtrace_info->type;
- etm->timeless_decoding = cs_etm__is_timeless_decoding(etm);
/* Use virtual timestamps if all ETMs report ts_source = 1 */
etm->has_virtual_ts = cs_etm__has_virtual_ts(metadata, num_cpu);
@@ -2960,6 +3282,10 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
etm->auxtrace.evsel_is_auxtrace = cs_etm__evsel_is_auxtrace;
session->auxtrace = &etm->auxtrace;
+ err = cs_etm__setup_timeless_decoding(etm);
+ if (err)
+ return err;
+
etm->unknown_thread = thread__new(999999999, 999999999);
if (!etm->unknown_thread) {
err = -ENOMEM;
@@ -2994,19 +3320,51 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event,
if (err)
goto err_delete_thread;
+ /*
+ * Map Trace ID values to CPU metadata.
+ *
+ * Trace metadata will always contain Trace ID values from the legacy algorithm. If the
+ * files has been recorded by a "new" perf updated to handle AUX_HW_ID then the metadata
+ * ID value will also have the CORESIGHT_TRACE_ID_UNUSED_FLAG set.
+ *
+ * The updated kernel drivers that use AUX_HW_ID to sent Trace IDs will attempt to use
+ * the same IDs as the old algorithm as far as is possible, unless there are clashes
+ * in which case a different value will be used. This means an older perf may still
+ * be able to record and read files generate on a newer system.
+ *
+ * For a perf able to interpret AUX_HW_ID packets we first check for the presence of
+ * those packets. If they are there then the values will be mapped and plugged into
+ * the metadata. We then set any remaining metadata values with the used flag to a
+ * value CORESIGHT_TRACE_ID_UNUSED_VAL - which indicates no decoder is required.
+ *
+ * If no AUX_HW_ID packets are present - which means a file recorded on an old kernel
+ * then we map Trace ID values to CPU directly from the metadata - clearing any unused
+ * flags if present.
+ */
+
+ /* first scan for AUX_OUTPUT_HW_ID records to map trace ID values to CPU metadata */
+ aux_hw_id_found = 0;
+ err = perf_session__peek_events(session, session->header.data_offset,
+ session->header.data_size,
+ cs_etm__process_aux_hw_id_cb, &aux_hw_id_found);
+ if (err)
+ goto err_delete_thread;
+
+ /* if HW ID found then clear any unused metadata ID values */
+ if (aux_hw_id_found)
+ err = cs_etm__clear_unused_trace_ids_metadata(num_cpu, metadata);
+ /* otherwise, this is a file with metadata values only, map from metadata */
+ else
+ err = cs_etm__map_trace_ids_metadata(num_cpu, metadata);
+
+ if (err)
+ goto err_delete_thread;
+
err = cs_etm__queue_aux_records(session);
if (err)
goto err_delete_thread;
etm->data_queued = etm->queues.populated;
- /*
- * Print warning in pipe mode, see cs_etm__process_auxtrace_event() and
- * cs_etm__queue_aux_fragment() for details relating to limitations.
- */
- if (!etm->data_queued)
- pr_warning("CS ETM warning: Coresight decode and TRBE support requires random file access.\n"
- "Continuing with best effort decoding in piped mode.\n\n");
-
return 0;
err_delete_thread:
diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h
index 98a4f7113d2f..70cac0375b34 100644
--- a/tools/perf/util/cs-etm.h
+++ b/tools/perf/util/cs-etm.h
@@ -12,6 +12,7 @@
#include <linux/bits.h>
struct perf_session;
+struct perf_pmu;
/*
* Versioning header in case things need to change in the future. That way
@@ -29,13 +30,17 @@ enum {
/*
* Update the version for new format.
*
- * New version 1 format adds a param count to the per cpu metadata.
+ * Version 1: format adds a param count to the per cpu metadata.
* This allows easy adding of new metadata parameters.
* Requires that new params always added after current ones.
* Also allows client reader to handle file versions that are different by
* checking the number of params in the file vs the number expected.
+ *
+ * Version 2: Drivers will use PERF_RECORD_AUX_OUTPUT_HW_ID to output
+ * CoreSight Trace ID. ...TRACEIDR metadata will be set to legacy values
+ * but with addition flags.
*/
-#define CS_HEADER_CURRENT_VERSION 1
+#define CS_HEADER_CURRENT_VERSION 2
/* Beginning of header common to both ETMv3 and V4 */
enum {
@@ -98,6 +103,12 @@ enum {
};
/*
+ * Check for valid CoreSight trace ID. If an invalid value is present in the metadata,
+ * then IDs are present in the hardware ID packet in the data file.
+ */
+#define CS_IS_VALID_TRACE_ID(id) ((id > 0) && (id < 0x70))
+
+/*
* ETMv3 exception encoding number:
* See Embedded Trace Macrocell specification (ARM IHI 0014Q)
* table 7-12 Encoding of Exception[3:0] for non-ARMv7-M processors.
@@ -168,8 +179,8 @@ struct cs_etm_packet {
u32 last_instr_subtype;
u32 flags;
u32 exception_number;
- u8 last_instr_cond;
- u8 last_instr_taken_branch;
+ bool last_instr_cond;
+ bool last_instr_taken_branch;
u8 last_instr_size;
u8 trace_chan_id;
int cpu;
@@ -218,6 +229,7 @@ struct cs_etm_packet_queue {
int cs_etm__process_auxtrace_info(union perf_event *event,
struct perf_session *session);
+struct perf_event_attr *cs_etm_get_default_config(struct perf_pmu *pmu);
#ifdef HAVE_CSTRACE_SUPPORT
int cs_etm__get_cpu(u8 trace_chan_id, int *cpu);
diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c
index ba9d93ce9463..653709ab867a 100644
--- a/tools/perf/util/data-convert-json.c
+++ b/tools/perf/util/data-convert-json.c
@@ -128,15 +128,17 @@ static void output_sample_callchain_entry(struct perf_tool *tool,
output_json_key_format(out, false, 5, "ip", "\"0x%" PRIx64 "\"", ip);
if (al && al->sym && al->sym->namelen) {
+ struct dso *dso = al->map ? map__dso(al->map) : NULL;
+
fputc(',', out);
output_json_key_string(out, false, 5, "symbol", al->sym->name);
- if (al->map && al->map->dso) {
- const char *dso = al->map->dso->short_name;
+ if (dso) {
+ const char *dso_name = dso->short_name;
- if (dso && strlen(dso) > 0) {
+ if (dso_name && strlen(dso_name) > 0) {
fputc(',', out);
- output_json_key_string(out, false, 5, "dso", dso);
+ output_json_key_string(out, false, 5, "dso", dso_name);
}
}
}
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index e0d4f08839fb..84c970c11794 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -179,9 +179,9 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
int err;
if (al->map) {
- struct dso *dso = al->map->dso;
+ struct dso *dso = map__dso(al->map);
- err = db_export__dso(dbe, dso, al->maps->machine);
+ err = db_export__dso(dbe, dso, maps__machine(al->maps));
if (err)
return err;
*dso_db_id = dso->db_id;
@@ -255,7 +255,7 @@ static struct call_path *call_path_from_sample(struct db_export *dbe,
al.addr = node->ip;
if (al.map && !al.sym)
- al.sym = dso__find_symbol(al.map->dso, al.addr);
+ al.sym = dso__find_symbol(map__dso(al.map), al.addr);
db_ids_from_al(dbe, &al, &dso_db_id, &sym_db_id, &offset);
@@ -354,19 +354,21 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
};
struct thread *main_thread;
struct comm *comm = NULL;
+ struct machine *machine;
int err;
err = db_export__evsel(dbe, evsel);
if (err)
return err;
- err = db_export__machine(dbe, al->maps->machine);
+ machine = maps__machine(al->maps);
+ err = db_export__machine(dbe, machine);
if (err)
return err;
- main_thread = thread__main_thread(al->maps->machine, thread);
+ main_thread = thread__main_thread(machine, thread);
- err = db_export__threads(dbe, thread, main_thread, al->maps->machine, &comm);
+ err = db_export__threads(dbe, thread, main_thread, machine, &comm);
if (err)
goto out_put;
@@ -380,7 +382,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
goto out_put;
if (dbe->cpr) {
- struct call_path *cp = call_path_from_sample(dbe, al->maps->machine,
+ struct call_path *cp = call_path_from_sample(dbe, machine,
thread, sample,
evsel);
if (cp) {
diff --git a/tools/perf/util/demangle-cxx.cpp b/tools/perf/util/demangle-cxx.cpp
new file mode 100644
index 000000000000..85b706641837
--- /dev/null
+++ b/tools/perf/util/demangle-cxx.cpp
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "demangle-cxx.h"
+#include <stdlib.h>
+#include <string.h>
+#include <linux/compiler.h>
+
+#ifdef HAVE_LIBBFD_SUPPORT
+#define PACKAGE 'perf'
+#include <bfd.h>
+#endif
+
+#ifdef HAVE_CXA_DEMANGLE_SUPPORT
+#include <cxxabi.h>
+#endif
+
+#if defined(HAVE_LIBBFD_SUPPORT) || defined(HAVE_CPLUS_DEMANGLE_SUPPORT)
+#ifndef DMGL_PARAMS
+#define DMGL_PARAMS (1 << 0) /* Include function args */
+#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
+#endif
+#endif
+
+/*
+ * Demangle C++ function signature
+ *
+ * Note: caller is responsible for freeing demangled string
+ */
+extern "C"
+char *cxx_demangle_sym(const char *str, bool params __maybe_unused,
+ bool modifiers __maybe_unused)
+{
+#ifdef HAVE_LIBBFD_SUPPORT
+ int flags = (params ? DMGL_PARAMS : 0) | (modifiers ? DMGL_ANSI : 0);
+
+ return bfd_demangle(NULL, str, flags);
+#elif defined(HAVE_CPLUS_DEMANGLE_SUPPORT)
+ int flags = (params ? DMGL_PARAMS : 0) | (modifiers ? DMGL_ANSI : 0);
+
+ return cplus_demangle(str, flags);
+#elif defined(HAVE_CXA_DEMANGLE_SUPPORT)
+ char *output;
+ int status;
+
+ output = abi::__cxa_demangle(str, /*output_buffer=*/NULL, /*length=*/NULL, &status);
+ return output;
+#else
+ return NULL;
+#endif
+}
diff --git a/tools/perf/util/demangle-cxx.h b/tools/perf/util/demangle-cxx.h
new file mode 100644
index 000000000000..26b5b66c0b4e
--- /dev/null
+++ b/tools/perf/util/demangle-cxx.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_DEMANGLE_CXX
+#define __PERF_DEMANGLE_CXX 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+char *cxx_demangle_sym(const char *str, bool params, bool modifiers);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __PERF_DEMANGLE_CXX */
diff --git a/tools/perf/util/dlfilter.c b/tools/perf/util/dlfilter.c
index 37beb7530288..16238f823a5e 100644
--- a/tools/perf/util/dlfilter.c
+++ b/tools/perf/util/dlfilter.c
@@ -29,7 +29,7 @@ static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al)
d_al->size = sizeof(*d_al);
if (al->map) {
- struct dso *dso = al->map->dso;
+ struct dso *dso = map__dso(al->map);
if (symbol_conf.show_kernel_path && dso->long_name)
d_al->dso = dso->long_name;
@@ -51,7 +51,7 @@ static void al_to_d_al(struct addr_location *al, struct perf_dlfilter_al *d_al)
if (al->addr < sym->end)
d_al->symoff = al->addr - sym->start;
else
- d_al->symoff = al->addr - al->map->start - sym->start;
+ d_al->symoff = al->addr - map__start(al->map) - sym->start;
d_al->sym_binding = sym->binding;
} else {
d_al->sym = NULL;
@@ -197,8 +197,12 @@ static const __u8 *dlfilter__insn(void *ctx, __u32 *len)
if (!al->thread && machine__resolve(d->machine, al, d->sample) < 0)
return NULL;
- if (al->thread->maps && al->thread->maps->machine)
- script_fetch_insn(d->sample, al->thread, al->thread->maps->machine);
+ if (al->thread->maps) {
+ struct machine *machine = maps__machine(al->thread->maps);
+
+ if (machine)
+ script_fetch_insn(d->sample, al->thread, machine);
+ }
}
if (!d->sample->insn_len)
@@ -216,6 +220,7 @@ static const char *dlfilter__srcline(void *ctx, __u32 *line_no)
unsigned int line = 0;
char *srcfile = NULL;
struct map *map;
+ struct dso *dso;
u64 addr;
if (!d->ctx_valid || !line_no)
@@ -227,9 +232,10 @@ static const char *dlfilter__srcline(void *ctx, __u32 *line_no)
map = al->map;
addr = al->addr;
+ dso = map ? map__dso(map) : NULL;
- if (map && map->dso)
- srcfile = get_srcline_split(map->dso, map__rip_2objdump(map, addr), &line);
+ if (dso)
+ srcfile = get_srcline_split(dso, map__rip_2objdump(map, addr), &line);
*line_no = line;
return srcfile;
@@ -262,7 +268,7 @@ static __s32 dlfilter__object_code(void *ctx, __u64 ip, void *buf, __u32 len)
map = al->map;
- if (map && ip >= map->start && ip < map->end &&
+ if (map && ip >= map__start(map) && ip < map__end(map) &&
machine__kernel_ip(d->machine, ip) == machine__kernel_ip(d->machine, d->sample->ip))
goto have_map;
@@ -272,10 +278,10 @@ static __s32 dlfilter__object_code(void *ctx, __u64 ip, void *buf, __u32 len)
map = a.map;
have_map:
- offset = map->map_ip(map, ip);
- if (ip + len >= map->end)
- len = map->end - ip;
- return dso__data_read_offset(map->dso, d->machine, offset, buf, len);
+ offset = map__map_ip(map, ip);
+ if (ip + len >= map__end(map))
+ len = map__end(map) - ip;
+ return dso__data_read_offset(map__dso(map), d->machine, offset, buf, len);
}
static const struct perf_dlfilter_fns perf_dlfilter_fns = {
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index f1a14c0ad26d..a86614599269 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -491,6 +491,11 @@ static int do_open(char *name)
return -1;
}
+char *dso__filename_with_chroot(const struct dso *dso, const char *filename)
+{
+ return filename_with_chroot(nsinfo__pid(dso->nsinfo), filename);
+}
+
static int __open_dso(struct dso *dso, struct machine *machine)
{
int fd = -EINVAL;
@@ -515,7 +520,7 @@ static int __open_dso(struct dso *dso, struct machine *machine)
if (errno != ENOENT || dso->nsinfo == NULL)
goto out;
- new_name = filename_with_chroot(dso->nsinfo->pid, name);
+ new_name = dso__filename_with_chroot(dso, name);
if (!new_name)
goto out;
@@ -1122,7 +1127,8 @@ ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
struct machine *machine, u64 addr,
u8 *data, ssize_t size)
{
- u64 offset = map->map_ip(map, addr);
+ u64 offset = map__map_ip(map, addr);
+
return dso__data_read_offset(dso, machine, offset, data, size);
}
@@ -1162,7 +1168,8 @@ ssize_t dso__data_write_cache_addr(struct dso *dso, struct map *map,
struct machine *machine, u64 addr,
const u8 *data, ssize_t size)
{
- u64 offset = map->map_ip(map, addr);
+ u64 offset = map__map_ip(map, addr);
+
return dso__data_write_cache_offs(dso, machine, offset, data, size);
}
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 58d94175e714..0b7c7633b9f6 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -266,6 +266,8 @@ static inline bool dso__has_symbols(const struct dso *dso)
return !RB_EMPTY_ROOT(&dso->symbols.rb_root);
}
+char *dso__filename_with_chroot(const struct dso *dso, const char *filename);
+
bool dso__sorted_by_name(const struct dso *dso);
void dso__set_sorted_by_name(struct dso *dso);
void dso__sort_by_name(struct dso *dso);
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index 2bd23e4cf19e..cf80aa42dd07 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -91,8 +91,7 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
have_build_id = true;
pos->has_build_id = true;
} else if (errno == ENOENT && pos->nsinfo) {
- char *new_name = filename_with_chroot(pos->nsinfo->pid,
- pos->long_name);
+ char *new_name = dso__filename_with_chroot(pos, pos->long_name);
if (new_name && filename__read_build_id(new_name,
&pos->bid) > 0) {
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 0d5d40cb997b..4a4fdad820d6 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -149,7 +149,7 @@ static void perf_env__purge_bpf(struct perf_env *env)
node = rb_entry(next, struct bpf_prog_info_node, rb_node);
next = rb_next(&node->rb_node);
rb_erase(&node->rb_node, root);
- free(node->info_linear);
+ zfree(&node->info_linear);
free(node);
}
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 1fa14598b916..8ae742e32e3c 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -33,7 +33,7 @@
#include "bpf-event.h"
#include "print_binary.h"
#include "tool.h"
-#include "../perf.h"
+#include "util.h"
static const char *perf_event__names[] = {
[0] = "TOTAL",
@@ -485,13 +485,14 @@ size_t perf_event__fprintf_text_poke(union perf_event *event, struct machine *ma
if (machine) {
struct addr_location al;
- al.map = maps__find(machine__kernel_maps(machine), tp->addr);
+ al.map = map__get(maps__find(machine__kernel_maps(machine), tp->addr));
if (al.map && map__load(al.map) >= 0) {
- al.addr = al.map->map_ip(al.map, tp->addr);
+ al.addr = map__map_ip(al.map, tp->addr);
al.sym = map__find_symbol(al.map, al.addr);
if (al.sym)
ret += symbol__fprintf_symname_offs(al.sym, &al, fp);
}
+ map__put(al.map);
}
ret += fprintf(fp, " old len %u new len %u\n", tp->old_len, tp->new_len);
old = true;
@@ -572,7 +573,7 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr,
struct addr_location *al)
{
struct maps *maps = thread->maps;
- struct machine *machine = maps->machine;
+ struct machine *machine = maps__machine(maps);
bool load_map = false;
al->maps = maps;
@@ -614,7 +615,7 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr,
return NULL;
}
- al->map = maps__find(maps, al->addr);
+ al->map = map__get(maps__find(maps, al->addr));
if (al->map != NULL) {
/*
* Kernel maps might be changed when loading symbols so loading
@@ -622,7 +623,7 @@ struct map *thread__find_map(struct thread *thread, u8 cpumode, u64 addr,
*/
if (load_map)
map__load(al->map);
- al->addr = al->map->map_ip(al->map, al->addr);
+ al->addr = map__map_ip(al->map, al->addr);
}
return al->map;
@@ -637,7 +638,7 @@ struct map *thread__find_map_fb(struct thread *thread, u8 cpumode, u64 addr,
struct addr_location *al)
{
struct map *map = thread__find_map(thread, cpumode, addr, al);
- struct machine *machine = thread->maps->machine;
+ struct machine *machine = maps__machine(thread->maps);
u8 addr_cpumode = machine__addr_cpumode(machine, cpumode, addr);
if (map || addr_cpumode == cpumode)
@@ -685,6 +686,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
struct perf_sample *sample)
{
struct thread *thread;
+ struct dso *dso;
if (symbol_conf.guest_code && !machine__is_host(machine))
thread = machine__findnew_guest_code(machine, sample->pid);
@@ -695,9 +697,11 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);
thread__find_map(thread, sample->cpumode, sample->ip, al);
+ dso = al->map ? map__dso(al->map) : NULL;
dump_printf(" ...... dso: %s\n",
- al->map ? al->map->dso->long_name :
- al->level == 'H' ? "[hypervisor]" : "<not found>");
+ dso
+ ? dso->long_name
+ : (al->level == 'H' ? "[hypervisor]" : "<not found>"));
if (thread__is_filtered(thread))
al->filtered |= (1 << HIST_FILTER__THREAD);
@@ -715,8 +719,6 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
}
if (al->map) {
- struct dso *dso = al->map->dso;
-
if (symbol_conf.dso_list &&
(!dso || !(strlist__has_entry(symbol_conf.dso_list,
dso->short_name) ||
@@ -742,12 +744,12 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
}
if (!ret && al->sym) {
snprintf(al_addr_str, sz, "0x%"PRIx64,
- al->map->unmap_ip(al->map, al->sym->start));
+ map__unmap_ip(al->map, al->sym->start));
ret = strlist__has_entry(symbol_conf.sym_list,
al_addr_str);
}
if (!ret && symbol_conf.addr_list && al->map) {
- unsigned long addr = al->map->unmap_ip(al->map, al->addr);
+ unsigned long addr = map__unmap_ip(al->map, al->addr);
ret = intlist__has_entry(symbol_conf.addr_list, addr);
if (!ret && symbol_conf.addr_range) {
@@ -772,6 +774,7 @@ int machine__resolve(struct machine *machine, struct addr_location *al,
*/
void addr_location__put(struct addr_location *al)
{
+ map__zput(al->map);
thread__zput(al->thread);
}
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 6663a676eadc..de20e01c9d72 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -89,7 +89,8 @@ enum {
PERF_MEM_S(LVL, NA) |\
PERF_MEM_S(SNOOP, NA) |\
PERF_MEM_S(LOCK, NA) |\
- PERF_MEM_S(TLB, NA))
+ PERF_MEM_S(TLB, NA) |\
+ PERF_MEM_S(LVLNUM, NA))
/* Attribute type for custom synthesized events */
#define PERF_TYPE_SYNTH (INT_MAX + 1U)
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 817df2504a1e..a0504316b06f 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -31,6 +31,8 @@
#include "util/evlist-hybrid.h"
#include "util/pmu.h"
#include "util/sample.h"
+#include "util/bpf-filter.h"
+#include "util/util.h"
#include <signal.h>
#include <unistd.h>
#include <sched.h>
@@ -465,7 +467,7 @@ static int evsel__strcmp(struct evsel *pos, char *evsel_name)
return 0;
if (evsel__is_dummy_event(pos))
return 1;
- return strcmp(pos->name, evsel_name);
+ return !evsel__name_is(pos, evsel_name);
}
static int evlist__is_enabled(struct evlist *evlist)
@@ -1086,17 +1088,27 @@ int evlist__apply_filters(struct evlist *evlist, struct evsel **err_evsel)
int err = 0;
evlist__for_each_entry(evlist, evsel) {
- if (evsel->filter == NULL)
- continue;
-
/*
* filters only work for tracepoint event, which doesn't have cpu limit.
* So evlist and evsel should always be same.
*/
- err = perf_evsel__apply_filter(&evsel->core, evsel->filter);
- if (err) {
- *err_evsel = evsel;
- break;
+ if (evsel->filter) {
+ err = perf_evsel__apply_filter(&evsel->core, evsel->filter);
+ if (err) {
+ *err_evsel = evsel;
+ break;
+ }
+ }
+
+ /*
+ * non-tracepoint events can have BPF filters.
+ */
+ if (!list_empty(&evsel->bpf_filters)) {
+ err = perf_bpf_filter__prepare(evsel);
+ if (err) {
+ *err_evsel = evsel;
+ break;
+ }
}
}
@@ -1694,7 +1706,7 @@ struct evsel *evlist__find_evsel_by_str(struct evlist *evlist, const char *str)
evlist__for_each_entry(evlist, evsel) {
if (!evsel->name)
continue;
- if (strcmp(str, evsel->name) == 0)
+ if (evsel__name_is(evsel, str))
return evsel;
}
@@ -1777,7 +1789,7 @@ bool evlist__exclude_kernel(struct evlist *evlist)
*/
void evlist__force_leader(struct evlist *evlist)
{
- if (!evlist->core.nr_groups) {
+ if (evlist__nr_groups(evlist) == 0) {
struct evsel *leader = evlist__first(evlist);
evlist__set_leader(evlist);
@@ -2262,8 +2274,8 @@ int evlist__parse_event_enable_time(struct evlist *evlist, struct record_opts *o
if (unset)
return 0;
- opts->initial_delay = str_to_delay(str);
- if (opts->initial_delay)
+ opts->target.initial_delay = str_to_delay(str);
+ if (opts->target.initial_delay)
return 0;
ret = parse_event_enable_times(str, NULL);
@@ -2306,14 +2318,14 @@ int evlist__parse_event_enable_time(struct evlist *evlist, struct record_opts *o
eet->evlist = evlist;
evlist->eet = eet;
- opts->initial_delay = eet->times[0].start;
+ opts->target.initial_delay = eet->times[0].start;
return 0;
close_timerfd:
close(eet->timerfd);
free_eet_times:
- free(eet->times);
+ zfree(&eet->times);
free_eet:
free(eet);
return err;
@@ -2395,7 +2407,7 @@ void event_enable_timer__exit(struct event_enable_timer **ep)
{
if (!ep || !*ep)
return;
- free((*ep)->times);
+ zfree(&(*ep)->times);
zfree(ep);
}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 01fa9d592c5a..46cf402add93 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -9,6 +9,7 @@
#include <api/fd/array.h>
#include <internal/evlist.h>
#include <internal/evsel.h>
+#include <perf/evlist.h>
#include "events_stats.h"
#include "evsel.h"
#include <pthread.h>
@@ -119,7 +120,7 @@ int arch_evlist__add_default_attrs(struct evlist *evlist,
#define evlist__add_default_attrs(evlist, array) \
arch_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array))
-struct evsel *arch_evlist__leader(struct list_head *list);
+int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs);
int evlist__add_dummy(struct evlist *evlist);
struct evsel *evlist__add_aux_dummy(struct evlist *evlist, bool system_wide);
@@ -255,6 +256,11 @@ static inline struct evsel *evlist__last(struct evlist *evlist)
return container_of(evsel, struct evsel, core);
}
+static inline int evlist__nr_groups(struct evlist *evlist)
+{
+ return perf_evlist__nr_groups(&evlist->core);
+}
+
int evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_t size);
int evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 51e8ce6edddc..356c07f03be6 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -50,6 +50,7 @@
#include "off_cpu.h"
#include "../perf-sys.h"
#include "util/parse-branch-options.h"
+#include "util/bpf-filter.h"
#include <internal/xyarray.h>
#include <internal/lib.h>
#include <internal/threadmap.h>
@@ -458,7 +459,6 @@ struct evsel *evsel__clone(struct evsel *orig)
evsel->per_pkg = orig->per_pkg;
evsel->percore = orig->percore;
evsel->precise_max = orig->precise_max;
- evsel->use_uncore_alias = orig->use_uncore_alias;
evsel->is_libpfm_event = orig->is_libpfm_event;
evsel->exclude_GH = orig->exclude_GH;
@@ -821,6 +821,35 @@ out_unknown:
return "unknown";
}
+bool evsel__name_is(struct evsel *evsel, const char *name)
+{
+ return !strcmp(evsel__name(evsel), name);
+}
+
+const char *evsel__group_pmu_name(const struct evsel *evsel)
+{
+ const struct evsel *leader;
+
+ /* If the pmu_name is set use it. pmu_name isn't set for CPU and software events. */
+ if (evsel->pmu_name)
+ return evsel->pmu_name;
+ /*
+ * Software events may be in a group with other uncore PMU events. Use
+ * the pmu_name of the group leader to avoid breaking the software event
+ * out of the group.
+ *
+ * Aux event leaders, like intel_pt, expect a group with events from
+ * other PMUs, so substitute the AUX event's PMU in this case.
+ */
+ leader = evsel__leader(evsel);
+ if ((evsel->core.attr.type == PERF_TYPE_SOFTWARE || evsel__is_aux_event(leader)) &&
+ leader->pmu_name) {
+ return leader->pmu_name;
+ }
+
+ return "cpu";
+}
+
const char *evsel__metric_id(const struct evsel *evsel)
{
if (evsel->metric_id)
@@ -1122,7 +1151,7 @@ static void evsel__set_default_freq_period(struct record_opts *opts,
static bool evsel__is_offcpu_event(struct evsel *evsel)
{
- return evsel__is_bpf_output(evsel) && !strcmp(evsel->name, OFFCPU_EVENT);
+ return evsel__is_bpf_output(evsel) && evsel__name_is(evsel, OFFCPU_EVENT);
}
/*
@@ -1334,7 +1363,7 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts,
* group leaders for traced executed by perf.
*/
if (target__none(&opts->target) && evsel__is_group_leader(evsel) &&
- !opts->initial_delay)
+ !opts->target.initial_delay)
attr->enable_on_exec = 1;
if (evsel->immediate) {
@@ -1494,6 +1523,7 @@ void evsel__exit(struct evsel *evsel)
assert(list_empty(&evsel->core.node));
assert(evsel->evlist == NULL);
bpf_counter__destroy(evsel);
+ perf_bpf_filter__destroy(evsel);
evsel__free_counts(evsel);
perf_evsel__free_fd(&evsel->core);
perf_evsel__free_id(&evsel->core);
@@ -1516,6 +1546,9 @@ void evsel__exit(struct evsel *evsel)
void evsel__delete(struct evsel *evsel)
{
+ if (!evsel)
+ return;
+
evsel__exit(evsel);
free(evsel);
}
@@ -2889,8 +2922,7 @@ bool evsel__fallback(struct evsel *evsel, int err, char *msg, size_t msgsize)
if (asprintf(&new_name, "%s%su", name, sep) < 0)
return false;
- if (evsel->name)
- free(evsel->name);
+ free(evsel->name);
evsel->name = new_name;
scnprintf(msg, msgsize, "kernel.perf_event_paranoid=%d, trying "
"to fall back to excluding kernel and hypervisor "
@@ -3128,7 +3160,7 @@ void evsel__zero_per_pkg(struct evsel *evsel)
if (evsel->per_pkg_mask) {
hashmap__for_each_entry(evsel->per_pkg_mask, cur, bkt)
- free((void *)cur->pkey);
+ zfree(&cur->pkey);
hashmap__clear(evsel->per_pkg_mask);
}
@@ -3139,7 +3171,7 @@ bool evsel__is_hybrid(const struct evsel *evsel)
return evsel->pmu_name && perf_pmu__is_hybrid(evsel->pmu_name);
}
-struct evsel *evsel__leader(struct evsel *evsel)
+struct evsel *evsel__leader(const struct evsel *evsel)
{
return container_of(evsel->core.leader, struct evsel, core);
}
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 24cb807ef6ce..d575390d80bc 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -89,7 +89,6 @@ struct evsel {
bool per_pkg;
bool percore;
bool precise_max;
- bool use_uncore_alias;
bool is_libpfm_event;
bool auto_merge_stats;
bool collect_stat;
@@ -151,8 +150,10 @@ struct evsel {
*/
struct bpf_counter_ops *bpf_counter_ops;
- /* for perf-stat -b */
- struct list_head bpf_counter_list;
+ union {
+ struct list_head bpf_counter_list; /* for perf-stat -b */
+ struct list_head bpf_filters; /* for perf-record --filter */
+ };
/* for perf-stat --use-bpf */
int bperf_leader_prog_fd;
@@ -160,6 +161,7 @@ struct evsel {
union {
struct bperf_leader_bpf *leader_skel;
struct bperf_follower_bpf *follower_skel;
+ void *bpf_skel;
};
unsigned long open_flags;
int precise_ip_original;
@@ -212,8 +214,8 @@ int evsel__object_config(size_t object_size,
int (*init)(struct evsel *evsel),
void (*fini)(struct evsel *evsel));
-struct perf_pmu *evsel__find_pmu(struct evsel *evsel);
-bool evsel__is_aux_event(struct evsel *evsel);
+struct perf_pmu *evsel__find_pmu(const struct evsel *evsel);
+bool evsel__is_aux_event(const struct evsel *evsel);
struct evsel *evsel__new_idx(struct perf_event_attr *attr, int idx);
@@ -267,6 +269,11 @@ static inline bool evsel__is_bpf(struct evsel *evsel)
return evsel->bpf_counter_ops != NULL;
}
+static inline bool evsel__is_bperf(struct evsel *evsel)
+{
+ return evsel->bpf_counter_ops != NULL && list_empty(&evsel->bpf_counter_list);
+}
+
#define EVSEL__MAX_ALIASES 8
extern const char *const evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX][EVSEL__MAX_ALIASES];
@@ -280,6 +287,8 @@ int arch_evsel__hw_name(struct evsel *evsel, char *bf, size_t size);
int __evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, char *bf, size_t size);
const char *evsel__name(struct evsel *evsel);
+bool evsel__name_is(struct evsel *evsel, const char *name);
+const char *evsel__group_pmu_name(const struct evsel *evsel);
const char *evsel__metric_id(const struct evsel *evsel);
static inline bool evsel__is_tool(const struct evsel *evsel)
@@ -436,7 +445,7 @@ static inline bool evsel__is_bpf_output(struct evsel *evsel)
return evsel__match(evsel, SOFTWARE, SW_BPF_OUTPUT);
}
-static inline bool evsel__is_clock(struct evsel *evsel)
+static inline bool evsel__is_clock(const struct evsel *evsel)
{
return evsel__match(evsel, SOFTWARE, SW_CPU_CLOCK) ||
evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK);
@@ -505,7 +514,7 @@ int evsel__store_ids(struct evsel *evsel, struct evlist *evlist);
void evsel__zero_per_pkg(struct evsel *evsel);
bool evsel__is_hybrid(const struct evsel *evsel);
-struct evsel *evsel__leader(struct evsel *evsel);
+struct evsel *evsel__leader(const struct evsel *evsel);
bool evsel__has_leader(struct evsel *evsel, struct evsel *leader);
bool evsel__is_leader(struct evsel *evsel);
void evsel__set_leader(struct evsel *evsel, struct evsel *leader);
@@ -526,4 +535,7 @@ bool arch_evsel__must_be_in_group(const struct evsel *evsel);
((((src) >> (pos)) & ((1ull << (size)) - 1)) << (63 - ((pos) + (size) - 1)))
u64 evsel__bitfield_swap_branch_flags(u64 value);
+void evsel__set_config_if_unset(struct perf_pmu *pmu, struct evsel *evsel,
+ const char *config_name, u64 val);
+
#endif /* __PERF_EVSEL_H */
diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c
index bd22c4932d10..cc80ec554c0a 100644
--- a/tools/perf/util/evsel_fprintf.c
+++ b/tools/perf/util/evsel_fprintf.c
@@ -151,15 +151,10 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
printed += fprintf(fp, " <-");
if (map)
- addr = map->map_ip(map, node->ip);
-
- if (print_ip) {
- /* Show binary offset for userspace addr */
- if (map && !map->dso->kernel)
- printed += fprintf(fp, "%c%16" PRIx64, s, addr);
- else
- printed += fprintf(fp, "%c%16" PRIx64, s, node->ip);
- }
+ addr = map__map_ip(map, node->ip);
+
+ if (print_ip)
+ printed += fprintf(fp, "%c%16" PRIx64, s, node->ip);
if (print_sym) {
printed += fprintf(fp, " ");
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index c1da20b868db..f4e52919324e 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -14,6 +14,7 @@
#include "util/hashmap.h"
#include "smt.h"
#include "tsc.h"
+#include <api/fs/fs.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/zalloc.h>
@@ -85,8 +86,8 @@ void ids__free(struct hashmap *ids)
return;
hashmap__for_each_entry(ids, cur, bkt) {
- free((void *)cur->pkey);
- free((void *)cur->pvalue);
+ zfree(&cur->pkey);
+ zfree(&cur->pvalue);
}
hashmap__free(ids);
@@ -310,8 +311,8 @@ void expr__ctx_clear(struct expr_parse_ctx *ctx)
size_t bkt;
hashmap__for_each_entry(ctx->ids, cur, bkt) {
- free((void *)cur->pkey);
- free(cur->pvalue);
+ zfree(&cur->pkey);
+ zfree(&cur->pvalue);
}
hashmap__clear(ctx->ids);
}
@@ -324,10 +325,10 @@ void expr__ctx_free(struct expr_parse_ctx *ctx)
if (!ctx)
return;
- free(ctx->sctx.user_requested_cpu_list);
+ zfree(&ctx->sctx.user_requested_cpu_list);
hashmap__for_each_entry(ctx->ids, cur, bkt) {
- free((void *)cur->pkey);
- free(cur->pvalue);
+ zfree(&cur->pkey);
+ zfree(&cur->pvalue);
}
hashmap__free(ctx->ids);
free(ctx);
@@ -400,9 +401,23 @@ double arch_get_tsc_freq(void)
}
#endif
+static double has_pmem(void)
+{
+ static bool has_pmem, cached;
+ const char *sysfs = sysfs__mountpoint();
+ char path[PATH_MAX];
+
+ if (!cached) {
+ snprintf(path, sizeof(path), "%s/firmware/acpi/tables/NFIT", sysfs);
+ has_pmem = access(path, F_OK) == 0;
+ cached = true;
+ }
+ return has_pmem ? 1.0 : 0.0;
+}
+
double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx)
{
- static struct cpu_topology *topology;
+ const struct cpu_topology *topology;
double result = NAN;
if (!strcmp("#num_cpus", literal)) {
@@ -421,31 +436,27 @@ double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx
* these strings gives an indication of the number of packages, dies,
* etc.
*/
- if (!topology) {
- topology = cpu_topology__new();
- if (!topology) {
- pr_err("Error creating CPU topology");
- goto out;
- }
- }
if (!strcasecmp("#smt_on", literal)) {
- result = smt_on(topology) ? 1.0 : 0.0;
+ result = smt_on() ? 1.0 : 0.0;
goto out;
}
if (!strcmp("#core_wide", literal)) {
- result = core_wide(ctx->system_wide, ctx->user_requested_cpu_list, topology)
+ result = core_wide(ctx->system_wide, ctx->user_requested_cpu_list)
? 1.0 : 0.0;
goto out;
}
if (!strcmp("#num_packages", literal)) {
+ topology = online_topology();
result = topology->package_cpus_lists;
goto out;
}
if (!strcmp("#num_dies", literal)) {
+ topology = online_topology();
result = topology->die_cpus_lists;
goto out;
}
if (!strcmp("#num_cores", literal)) {
+ topology = online_topology();
result = topology->core_cpus_lists;
goto out;
}
@@ -453,6 +464,10 @@ double expr__get_literal(const char *literal, const struct expr_scanner_ctx *ctx
result = perf_pmu__cpu_slots_per_cycle();
goto out;
}
+ if (!strcmp("#has_pmem", literal)) {
+ result = has_pmem();
+ goto out;
+ }
pr_err("Unrecognized literal '%s'", literal);
out:
diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y
index 635e562350c5..250e444bf032 100644
--- a/tools/perf/util/expr.y
+++ b/tools/perf/util/expr.y
@@ -127,7 +127,11 @@ static struct ids handle_id(struct expr_parse_ctx *ctx, char *id,
if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \
assert(LHS.ids == NULL); \
assert(RHS.ids == NULL); \
- RESULT.val = (long)LHS.val OP (long)RHS.val; \
+ if (isnan(LHS.val) || isnan(RHS.val)) { \
+ RESULT.val = NAN; \
+ } else { \
+ RESULT.val = (long)LHS.val OP (long)RHS.val; \
+ } \
RESULT.ids = NULL; \
} else { \
RESULT = union_expr(LHS, RHS); \
@@ -137,7 +141,11 @@ static struct ids handle_id(struct expr_parse_ctx *ctx, char *id,
if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \
assert(LHS.ids == NULL); \
assert(RHS.ids == NULL); \
- RESULT.val = LHS.val OP RHS.val; \
+ if (isnan(LHS.val) || isnan(RHS.val)) { \
+ RESULT.val = NAN; \
+ } else { \
+ RESULT.val = LHS.val OP RHS.val; \
+ } \
RESULT.ids = NULL; \
} else { \
RESULT = union_expr(LHS, RHS); \
diff --git a/tools/perf/util/ftrace.h b/tools/perf/util/ftrace.h
index a34cd15733b8..558efcb98d25 100644
--- a/tools/perf/util/ftrace.h
+++ b/tools/perf/util/ftrace.h
@@ -25,7 +25,6 @@ struct perf_ftrace {
int graph_noirqs;
int graph_verbose;
int graph_thresh;
- unsigned int initial_delay;
};
struct filter_entry {
diff --git a/tools/perf/util/genelf_debug.c b/tools/perf/util/genelf_debug.c
index dd40683bd4c0..aa5dcc56b2ac 100644
--- a/tools/perf/util/genelf_debug.c
+++ b/tools/perf/util/genelf_debug.c
@@ -11,6 +11,7 @@
* @author Philippe Elie
*/
#include <linux/compiler.h>
+#include <linux/zalloc.h>
#include <sys/types.h>
#include <stdio.h>
#include <getopt.h>
@@ -87,6 +88,12 @@ buffer_ext_init(struct buffer_ext *be)
be->max_sz = 0;
}
+static void
+buffer_ext_exit(struct buffer_ext *be)
+{
+ zfree(&be->data);
+}
+
static inline size_t
buffer_ext_size(struct buffer_ext *be)
{
@@ -487,28 +494,28 @@ jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries
Elf_Scn *scn;
Elf_Shdr *shdr;
struct buffer_ext dl, di, da;
- int ret;
+ int ret = -1;
buffer_ext_init(&dl);
buffer_ext_init(&di);
buffer_ext_init(&da);
- ret = jit_process_debug_info(code_addr, debug, nr_debug_entries, &dl, &da, &di);
- if (ret)
- return -1;
+ if (jit_process_debug_info(code_addr, debug, nr_debug_entries, &dl, &da, &di))
+ goto out;
+
/*
* setup .debug_line section
*/
scn = elf_newscn(e);
if (!scn) {
warnx("cannot create section");
- return -1;
+ goto out;
}
d = elf_newdata(scn);
if (!d) {
warnx("cannot get new data");
- return -1;
+ goto out;
}
d->d_align = 1;
@@ -521,7 +528,7 @@ jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries
shdr = elf_getshdr(scn);
if (!shdr) {
warnx("cannot get section header");
- return -1;
+ goto out;
}
shdr->sh_name = 52; /* .debug_line */
@@ -536,13 +543,13 @@ jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries
scn = elf_newscn(e);
if (!scn) {
warnx("cannot create section");
- return -1;
+ goto out;
}
d = elf_newdata(scn);
if (!d) {
warnx("cannot get new data");
- return -1;
+ goto out;
}
d->d_align = 1;
@@ -555,7 +562,7 @@ jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries
shdr = elf_getshdr(scn);
if (!shdr) {
warnx("cannot get section header");
- return -1;
+ goto out;
}
shdr->sh_name = 64; /* .debug_info */
@@ -570,13 +577,13 @@ jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries
scn = elf_newscn(e);
if (!scn) {
warnx("cannot create section");
- return -1;
+ goto out;
}
d = elf_newdata(scn);
if (!d) {
warnx("cannot get new data");
- return -1;
+ goto out;
}
d->d_align = 1;
@@ -589,7 +596,7 @@ jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries
shdr = elf_getshdr(scn);
if (!shdr) {
warnx("cannot get section header");
- return -1;
+ goto out;
}
shdr->sh_name = 76; /* .debug_info */
@@ -601,9 +608,14 @@ jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries
/*
* now we update the ELF image with all the sections
*/
- if (elf_update(e, ELF_C_WRITE) < 0) {
+ if (elf_update(e, ELF_C_WRITE) < 0)
warnx("elf_update debug failed");
- return -1;
- }
- return 0;
+ else
+ ret = 0;
+
+out:
+ buffer_ext_exit(&dl);
+ buffer_ext_exit(&di);
+ buffer_ext_exit(&da);
+ return ret;
}
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 404d816ca124..276870221ce0 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -786,7 +786,7 @@ static int write_pmu_mappings(struct feat_fd *ff,
static int write_group_desc(struct feat_fd *ff,
struct evlist *evlist)
{
- u32 nr_groups = evlist->core.nr_groups;
+ u32 nr_groups = evlist__nr_groups(evlist);
struct evsel *evsel;
int ret;
@@ -2807,7 +2807,6 @@ static int process_group_desc(struct feat_fd *ff, void *data __maybe_unused)
* Rebuild group relationship based on the group_desc
*/
session = container_of(ff->ph, struct perf_session, header);
- session->evlist->core.nr_groups = nr_groups;
i = nr = 0;
evlist__for_each_entry(session->evlist, evsel) {
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index e3861ae62172..59eeb4a32ac5 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -115,6 +115,8 @@ struct perf_session;
struct perf_tool;
union perf_event;
+extern const char perf_version_string[];
+
int perf_session__read_header(struct perf_session *session, int repipe_fd);
int perf_session__write_header(struct perf_session *session,
struct evlist *evlist,
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index b6e4b4edde43..3c9301a26dfc 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -4,6 +4,7 @@
#include "dso.h"
#include "build-id.h"
#include "hist.h"
+#include "kvm-stat.h"
#include "map.h"
#include "map_symbol.h"
#include "branch.h"
@@ -105,7 +106,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
hists__set_col_len(hists, HISTC_THREAD, len + 8);
if (h->ms.map) {
- len = dso__name_len(h->ms.map->dso);
+ len = dso__name_len(map__dso(h->ms.map));
hists__new_col_len(hists, HISTC_DSO, len);
}
@@ -119,7 +120,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
symlen += BITS_PER_LONG / 4 + 2 + 3;
hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
- symlen = dso__name_len(h->branch_info->from.ms.map->dso);
+ symlen = dso__name_len(map__dso(h->branch_info->from.ms.map));
hists__new_col_len(hists, HISTC_DSO_FROM, symlen);
} else {
symlen = unresolved_col_width + 4 + 2;
@@ -134,7 +135,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
symlen += BITS_PER_LONG / 4 + 2 + 3;
hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
- symlen = dso__name_len(h->branch_info->to.ms.map->dso);
+ symlen = dso__name_len(map__dso(h->branch_info->to.ms.map));
hists__new_col_len(hists, HISTC_DSO_TO, symlen);
} else {
symlen = unresolved_col_width + 4 + 2;
@@ -179,7 +180,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
}
if (h->mem_info->daddr.ms.map) {
- symlen = dso__name_len(h->mem_info->daddr.ms.map->dso);
+ symlen = dso__name_len(map__dso(h->mem_info->daddr.ms.map));
hists__new_col_len(hists, HISTC_MEM_DADDR_DSO,
symlen);
} else {
@@ -207,7 +208,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
hists__new_col_len(hists, HISTC_MEM_LOCKED, 6);
hists__new_col_len(hists, HISTC_MEM_TLB, 22);
hists__new_col_len(hists, HISTC_MEM_SNOOP, 12);
- hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3);
+ hists__new_col_len(hists, HISTC_MEM_LVL, 36 + 3);
hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12);
hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12);
hists__new_col_len(hists, HISTC_MEM_BLOCKED, 10);
@@ -240,7 +241,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
if (h->cgroup) {
const char *cgrp_name = "unknown";
- struct cgroup *cgrp = cgroup__find(h->ms.maps->machine->env,
+ struct cgroup *cgrp = cgroup__find(maps__machine(h->ms.maps)->env,
h->cgroup);
if (cgrp != NULL)
cgrp_name = cgrp->name;
@@ -449,7 +450,7 @@ static int hist_entry__init(struct hist_entry *he,
memset(&he->stat, 0, sizeof(he->stat));
}
- map__get(he->ms.map);
+ he->ms.map = map__get(he->ms.map);
if (he->branch_info) {
/*
@@ -464,13 +465,13 @@ static int hist_entry__init(struct hist_entry *he,
memcpy(he->branch_info, template->branch_info,
sizeof(*he->branch_info));
- map__get(he->branch_info->from.ms.map);
- map__get(he->branch_info->to.ms.map);
+ he->branch_info->from.ms.map = map__get(he->branch_info->from.ms.map);
+ he->branch_info->to.ms.map = map__get(he->branch_info->to.ms.map);
}
if (he->mem_info) {
- map__get(he->mem_info->iaddr.ms.map);
- map__get(he->mem_info->daddr.ms.map);
+ he->mem_info->iaddr.ms.map = map__get(he->mem_info->iaddr.ms.map);
+ he->mem_info->daddr.ms.map = map__get(he->mem_info->daddr.ms.map);
}
if (hist_entry__has_callchains(he) && symbol_conf.use_callchain)
@@ -627,6 +628,8 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
block_info__zput(entry->block_info);
+ kvm_info__zput(entry->kvm_info);
+
/* If the map of an existing hist_entry has
* become out-of-date due to an exec() or
* similar, update it. Otherwise we will
@@ -698,6 +701,7 @@ __hists__add_entry(struct hists *hists,
struct symbol *sym_parent,
struct branch_info *bi,
struct mem_info *mi,
+ struct kvm_info *ki,
struct block_info *block_info,
struct perf_sample *sample,
bool sample_self,
@@ -733,6 +737,7 @@ __hists__add_entry(struct hists *hists,
.hists = hists,
.branch_info = bi,
.mem_info = mi,
+ .kvm_info = ki,
.block_info = block_info,
.transaction = sample->transaction,
.raw_data = sample->raw_data,
@@ -742,6 +747,7 @@ __hists__add_entry(struct hists *hists,
.weight = sample->weight,
.ins_lat = sample->ins_lat,
.p_stage_cyc = sample->p_stage_cyc,
+ .simd_flags = sample->simd_flags,
}, *he = hists__findnew_entry(hists, &entry, al, sample_self);
if (!hists->has_callchains && he && he->callchain_size != 0)
@@ -756,10 +762,11 @@ struct hist_entry *hists__add_entry(struct hists *hists,
struct symbol *sym_parent,
struct branch_info *bi,
struct mem_info *mi,
+ struct kvm_info *ki,
struct perf_sample *sample,
bool sample_self)
{
- return __hists__add_entry(hists, al, sym_parent, bi, mi, NULL,
+ return __hists__add_entry(hists, al, sym_parent, bi, mi, ki, NULL,
sample, sample_self, NULL);
}
@@ -769,10 +776,11 @@ struct hist_entry *hists__add_entry_ops(struct hists *hists,
struct symbol *sym_parent,
struct branch_info *bi,
struct mem_info *mi,
+ struct kvm_info *ki,
struct perf_sample *sample,
bool sample_self)
{
- return __hists__add_entry(hists, al, sym_parent, bi, mi, NULL,
+ return __hists__add_entry(hists, al, sym_parent, bi, mi, ki, NULL,
sample, sample_self, ops);
}
@@ -846,7 +854,7 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al
*/
sample->period = cost;
- he = hists__add_entry(hists, al, iter->parent, NULL, mi,
+ he = hists__add_entry(hists, al, iter->parent, NULL, mi, NULL,
sample, true);
if (!he)
return -ENOMEM;
@@ -949,7 +957,7 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a
sample->period = 1;
sample->weight = bi->flags.cycles ? bi->flags.cycles : 1;
- he = hists__add_entry(hists, al, iter->parent, &bi[i], NULL,
+ he = hists__add_entry(hists, al, iter->parent, &bi[i], NULL, NULL,
sample, true);
if (he == NULL)
return -ENOMEM;
@@ -987,7 +995,7 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location
struct hist_entry *he;
he = hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
- sample, true);
+ NULL, sample, true);
if (he == NULL)
return -ENOMEM;
@@ -1047,7 +1055,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter,
struct hist_entry *he;
int err = 0;
- he = hists__add_entry(hists, al, iter->parent, NULL, NULL,
+ he = hists__add_entry(hists, al, iter->parent, NULL, NULL, NULL,
sample, true);
if (he == NULL)
return -ENOMEM;
@@ -1148,7 +1156,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
}
he = hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
- sample, false);
+ NULL, sample, false);
if (he == NULL)
return -ENOMEM;
@@ -1318,6 +1326,9 @@ void hist_entry__delete(struct hist_entry *he)
if (he->block_info)
block_info__zput(he->block_info);
+ if (he->kvm_info)
+ kvm_info__zput(he->kvm_info);
+
zfree(&he->res_samples);
zfree(&he->stat_acc);
free_srcline(he->srcline);
@@ -2099,7 +2110,7 @@ static bool hists__filter_entry_by_dso(struct hists *hists,
struct hist_entry *he)
{
if (hists->dso_filter != NULL &&
- (he->ms.map == NULL || he->ms.map->dso != hists->dso_filter)) {
+ (he->ms.map == NULL || map__dso(he->ms.map) != hists->dso_filter)) {
he->filtered |= (1 << HIST_FILTER__DSO);
return true;
}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index d93a4e510dc7..afc9f1c7f4dc 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -14,6 +14,7 @@ struct hist_entry_ops;
struct addr_location;
struct map_symbol;
struct mem_info;
+struct kvm_info;
struct branch_info;
struct branch_stack;
struct block_info;
@@ -80,6 +81,7 @@ enum hist_column {
HISTC_ADDR_FROM,
HISTC_ADDR_TO,
HISTC_ADDR,
+ HISTC_SIMD,
HISTC_NR_COLS, /* Last entry */
};
@@ -150,6 +152,7 @@ struct hist_entry *hists__add_entry(struct hists *hists,
struct symbol *parent,
struct branch_info *bi,
struct mem_info *mi,
+ struct kvm_info *ki,
struct perf_sample *sample,
bool sample_self);
@@ -159,6 +162,7 @@ struct hist_entry *hists__add_entry_ops(struct hists *hists,
struct symbol *sym_parent,
struct branch_info *bi,
struct mem_info *mi,
+ struct kvm_info *ki,
struct perf_sample *sample,
bool sample_self);
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
index 0ac860c8dd2b..b450178e3420 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c
@@ -1998,6 +1998,8 @@ static void intel_pt_calc_cbr(struct intel_pt_decoder *decoder)
decoder->cbr = cbr;
decoder->cbr_cyc_to_tsc = decoder->max_non_turbo_ratio_fp / cbr;
+ decoder->cyc_ref_timestamp = decoder->timestamp;
+ decoder->cycle_cnt = 0;
intel_pt_mtc_cyc_cnt_cbr(decoder);
}
@@ -3544,7 +3546,7 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
switch (decoder->packet.type) {
case INTEL_PT_TIP_PGD:
decoder->continuous_period = false;
- __fallthrough;
+ fallthrough;
case INTEL_PT_TIP_PGE:
case INTEL_PT_TIP:
case INTEL_PT_PTWRITE:
@@ -3614,7 +3616,7 @@ static int intel_pt_walk_psb(struct intel_pt_decoder *decoder)
decoder->pge = false;
decoder->continuous_period = false;
intel_pt_clear_tx_flags(decoder);
- __fallthrough;
+ fallthrough;
case INTEL_PT_TNT:
decoder->have_tma = false;
@@ -3978,7 +3980,7 @@ const struct intel_pt_state *intel_pt_decode(struct intel_pt_decoder *decoder)
decoder->have_last_ip = false;
decoder->last_ip = 0;
decoder->ip = 0;
- __fallthrough;
+ fallthrough;
case INTEL_PT_STATE_ERR_RESYNC:
err = intel_pt_sync_ip(decoder);
break;
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c
index 22308dd93010..c5d57027ec23 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c
@@ -52,6 +52,20 @@ static void intel_pt_insn_decoder(struct insn *insn,
op = INTEL_PT_OP_VMENTRY;
branch = INTEL_PT_BR_INDIRECT;
break;
+ case 0xca:
+ switch (insn->prefixes.bytes[3]) {
+ case 0xf2: /* erets */
+ op = INTEL_PT_OP_ERETS;
+ branch = INTEL_PT_BR_INDIRECT;
+ break;
+ case 0xf3: /* eretu */
+ op = INTEL_PT_OP_ERETU;
+ branch = INTEL_PT_BR_INDIRECT;
+ break;
+ default:
+ break;
+ }
+ break;
default:
break;
}
@@ -230,6 +244,8 @@ const char *branch_name[] = {
[INTEL_PT_OP_SYSCALL] = "Syscall",
[INTEL_PT_OP_SYSRET] = "Sysret",
[INTEL_PT_OP_VMENTRY] = "VMentry",
+ [INTEL_PT_OP_ERETS] = "Erets",
+ [INTEL_PT_OP_ERETU] = "Eretu",
};
const char *intel_pt_insn_name(enum intel_pt_insn_op op)
@@ -273,6 +289,8 @@ int intel_pt_insn_type(enum intel_pt_insn_op op)
case INTEL_PT_OP_LOOP:
return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_CONDITIONAL;
case INTEL_PT_OP_IRET:
+ case INTEL_PT_OP_ERETS:
+ case INTEL_PT_OP_ERETU:
return PERF_IP_FLAG_BRANCH | PERF_IP_FLAG_RETURN |
PERF_IP_FLAG_INTERRUPT;
case INTEL_PT_OP_INT:
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h
index e3338b56a75f..7fb7fe3a1566 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.h
@@ -25,6 +25,8 @@ enum intel_pt_insn_op {
INTEL_PT_OP_SYSCALL,
INTEL_PT_OP_SYSRET,
INTEL_PT_OP_VMENTRY,
+ INTEL_PT_OP_ERETS,
+ INTEL_PT_OP_ERETU,
};
enum intel_pt_insn_branch {
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c
index 18f97f43e01a..af9710622a1f 100644
--- a/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c
+++ b/tools/perf/util/intel-pt-decoder/intel-pt-pkt-decoder.c
@@ -730,7 +730,7 @@ int intel_pt_pkt_desc(const struct intel_pt_pkt *packet, char *buf,
case INTEL_PT_FUP:
if (!(packet->count))
return snprintf(buf, buf_len, "%s no ip", name);
- __fallthrough;
+ fallthrough;
case INTEL_PT_CYC:
case INTEL_PT_VMCS:
case INTEL_PT_MTC:
diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c
index 955c1b9dc6a4..fe893c9bab3f 100644
--- a/tools/perf/util/intel-pt.c
+++ b/tools/perf/util/intel-pt.c
@@ -801,25 +801,27 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn,
}
while (1) {
- if (!thread__find_map(thread, cpumode, *ip, &al) || !al.map->dso) {
+ struct dso *dso;
+
+ if (!thread__find_map(thread, cpumode, *ip, &al) || !map__dso(al.map)) {
if (al.map)
intel_pt_log("ERROR: thread has no dso for %#" PRIx64 "\n", *ip);
else
intel_pt_log("ERROR: thread has no map for %#" PRIx64 "\n", *ip);
return -EINVAL;
}
+ dso = map__dso(al.map);
- if (al.map->dso->data.status == DSO_DATA_STATUS_ERROR &&
- dso__data_status_seen(al.map->dso,
- DSO_DATA_STATUS_SEEN_ITRACE))
+ if (dso->data.status == DSO_DATA_STATUS_ERROR &&
+ dso__data_status_seen(dso, DSO_DATA_STATUS_SEEN_ITRACE))
return -ENOENT;
- offset = al.map->map_ip(al.map, *ip);
+ offset = map__map_ip(al.map, *ip);
if (!to_ip && one_map) {
struct intel_pt_cache_entry *e;
- e = intel_pt_cache_lookup(al.map->dso, machine, offset);
+ e = intel_pt_cache_lookup(dso, machine, offset);
if (e &&
(!max_insn_cnt || e->insn_cnt <= max_insn_cnt)) {
*insn_cnt_ptr = e->insn_cnt;
@@ -829,8 +831,7 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn,
intel_pt_insn->emulated_ptwrite = e->emulated_ptwrite;
intel_pt_insn->length = e->length;
intel_pt_insn->rel = e->rel;
- memcpy(intel_pt_insn->buf, e->insn,
- INTEL_PT_INSN_BUF_SZ);
+ memcpy(intel_pt_insn->buf, e->insn, INTEL_PT_INSN_BUF_SZ);
intel_pt_log_insn_no_data(intel_pt_insn, *ip);
return 0;
}
@@ -842,17 +843,17 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn,
/* Load maps to ensure dso->is_64_bit has been updated */
map__load(al.map);
- x86_64 = al.map->dso->is_64_bit;
+ x86_64 = dso->is_64_bit;
while (1) {
- len = dso__data_read_offset(al.map->dso, machine,
+ len = dso__data_read_offset(dso, machine,
offset, buf,
INTEL_PT_INSN_BUF_SZ);
if (len <= 0) {
intel_pt_log("ERROR: failed to read at offset %#" PRIx64 " ",
offset);
if (intel_pt_enable_logging)
- dso__fprintf(al.map->dso, intel_pt_log_fp());
+ dso__fprintf(dso, intel_pt_log_fp());
return -EINVAL;
}
@@ -871,7 +872,7 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn,
goto out;
/* Check for emulated ptwrite */
offs = offset + intel_pt_insn->length;
- eptw = intel_pt_emulated_ptwrite(al.map->dso, machine, offs);
+ eptw = intel_pt_emulated_ptwrite(dso, machine, offs);
intel_pt_insn->emulated_ptwrite = eptw;
goto out;
}
@@ -886,7 +887,7 @@ static int intel_pt_walk_next_insn(struct intel_pt_insn *intel_pt_insn,
goto out_no_cache;
}
- if (*ip >= al.map->end)
+ if (*ip >= map__end(al.map))
break;
offset += intel_pt_insn->length;
@@ -906,13 +907,13 @@ out:
if (to_ip) {
struct intel_pt_cache_entry *e;
- e = intel_pt_cache_lookup(al.map->dso, machine, start_offset);
+ e = intel_pt_cache_lookup(map__dso(al.map), machine, start_offset);
if (e)
return 0;
}
/* Ignore cache errors */
- intel_pt_cache_add(al.map->dso, machine, start_offset, insn_cnt,
+ intel_pt_cache_add(map__dso(al.map), machine, start_offset, insn_cnt,
*ip - start_ip, intel_pt_insn);
return 0;
@@ -983,13 +984,12 @@ static int __intel_pt_pgd_ip(uint64_t ip, void *data)
if (!thread)
return -EINVAL;
- if (!thread__find_map(thread, cpumode, ip, &al) || !al.map->dso)
+ if (!thread__find_map(thread, cpumode, ip, &al) || !map__dso(al.map))
return -EINVAL;
- offset = al.map->map_ip(al.map, ip);
+ offset = map__map_ip(al.map, ip);
- return intel_pt_match_pgd_ip(ptq->pt, ip, offset,
- al.map->dso->long_name);
+ return intel_pt_match_pgd_ip(ptq->pt, ip, offset, map__dso(al.map)->long_name);
}
static bool intel_pt_pgd_ip(uint64_t ip, void *data)
@@ -2744,13 +2744,13 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
if (map__load(map))
return 0;
- start = dso__first_symbol(map->dso);
+ start = dso__first_symbol(map__dso(map));
for (sym = start; sym; sym = dso__next_symbol(sym)) {
if (sym->binding == STB_GLOBAL &&
!strcmp(sym->name, "__switch_to")) {
- ip = map->unmap_ip(map, sym->start);
- if (ip >= map->start && ip < map->end) {
+ ip = map__unmap_ip(map, sym->start);
+ if (ip >= map__start(map) && ip < map__end(map)) {
switch_ip = ip;
break;
}
@@ -2767,8 +2767,8 @@ static u64 intel_pt_switch_ip(struct intel_pt *pt, u64 *ptss_ip)
for (sym = start; sym; sym = dso__next_symbol(sym)) {
if (!strcmp(sym->name, ptss)) {
- ip = map->unmap_ip(map, sym->start);
- if (ip >= map->start && ip < map->end) {
+ ip = map__unmap_ip(map, sym->start);
+ if (ip >= map__start(map) && ip < map__end(map)) {
*ptss_ip = ip;
break;
}
@@ -3356,7 +3356,7 @@ static int intel_pt_process_aux_output_hw_id(struct intel_pt *pt,
static int intel_pt_find_map(struct thread *thread, u8 cpumode, u64 addr,
struct addr_location *al)
{
- if (!al->map || addr < al->map->start || addr >= al->map->end) {
+ if (!al->map || addr < map__start(al->map) || addr >= map__end(al->map)) {
if (!thread__find_map(thread, cpumode, addr, al))
return -1;
}
@@ -3381,18 +3381,21 @@ static int intel_pt_text_poke(struct intel_pt *pt, union perf_event *event)
return 0;
for (; cnt; cnt--, addr--) {
+ struct dso *dso;
+
if (intel_pt_find_map(thread, cpumode, addr, &al)) {
if (addr < event->text_poke.addr)
return 0;
continue;
}
- if (!al.map->dso || !al.map->dso->auxtrace_cache)
+ dso = map__dso(al.map);
+ if (!dso || !dso->auxtrace_cache)
continue;
- offset = al.map->map_ip(al.map, addr);
+ offset = map__map_ip(al.map, addr);
- e = intel_pt_cache_lookup(al.map->dso, machine, offset);
+ e = intel_pt_cache_lookup(dso, machine, offset);
if (!e)
continue;
@@ -3405,9 +3408,9 @@ static int intel_pt_text_poke(struct intel_pt *pt, union perf_event *event)
if (e->branch != INTEL_PT_BR_NO_BRANCH)
return 0;
} else {
- intel_pt_cache_invalidate(al.map->dso, machine, offset);
+ intel_pt_cache_invalidate(dso, machine, offset);
intel_pt_log("Invalidated instruction cache for %s at %#"PRIx64"\n",
- al.map->dso->long_name, addr);
+ dso->long_name, addr);
}
}
diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c
index 0e033278fa12..28e49502db5e 100644
--- a/tools/perf/util/jitdump.c
+++ b/tools/perf/util/jitdump.c
@@ -235,9 +235,11 @@ jit_open(struct jit_buf_desc *jd, const char *name)
*/
strcpy(jd->dir, name);
dirname(jd->dir);
+ free(buf);
return 0;
error:
+ free(buf);
funlockfile(jd->in);
fclose(jd->in);
return retval;
@@ -523,7 +525,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
ret = perf_event__process_mmap2(tool, event, &sample, jd->machine);
if (ret)
- return ret;
+ goto out;
ret = jit_inject_event(jd, event);
/*
@@ -532,6 +534,8 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
if (!ret)
build_id__mark_dso_hit(tool, event, &sample, NULL, jd->machine);
+out:
+ free(event);
return ret;
}
@@ -874,6 +878,7 @@ jit_process(struct perf_session *session,
}
nsinfo__put(jd.nsi);
+ free(jd.buf);
return ret;
}
diff --git a/tools/perf/util/kvm-stat.h b/tools/perf/util/kvm-stat.h
index 6f0fa05b62b6..3e9ac754c3d1 100644
--- a/tools/perf/util/kvm-stat.h
+++ b/tools/perf/util/kvm-stat.h
@@ -5,9 +5,16 @@
#ifdef HAVE_KVM_STAT_SUPPORT
#include "tool.h"
+#include "sort.h"
#include "stat.h"
+#include "symbol.h"
#include "record.h"
+#include <stdlib.h>
+#include <linux/zalloc.h>
+
+#define KVM_EVENT_NAME_LEN 40
+
struct evsel;
struct evlist;
struct perf_session;
@@ -19,15 +26,22 @@ struct event_key {
struct exit_reasons_table *exit_reasons;
};
+struct kvm_info {
+ char name[KVM_EVENT_NAME_LEN];
+ refcount_t refcnt;
+};
+
struct kvm_event_stats {
u64 time;
struct stats stats;
};
+struct perf_kvm_stat;
+
struct kvm_event {
struct list_head hash_entry;
- struct rb_node rb;
+ struct perf_kvm_stat *perf_kvm;
struct event_key key;
struct kvm_event_stats total;
@@ -35,17 +49,10 @@ struct kvm_event {
#define DEFAULT_VCPU_NUM 8
int max_vcpu;
struct kvm_event_stats *vcpu;
-};
-
-typedef int (*key_cmp_fun)(struct kvm_event*, struct kvm_event*, int);
-struct kvm_event_key {
- const char *name;
- key_cmp_fun key;
+ struct hist_entry he;
};
-struct perf_kvm_stat;
-
struct child_event_ops {
void (*get_key)(struct evsel *evsel,
struct perf_sample *sample,
@@ -70,9 +77,6 @@ struct exit_reasons_table {
const char *reason;
};
-#define EVENTS_BITS 12
-#define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS)
-
struct perf_kvm_stat {
struct perf_tool tool;
struct record_opts opts;
@@ -84,12 +88,13 @@ struct perf_kvm_stat {
const char *sort_key;
int trace_vcpu;
+ /* Used when process events */
+ struct addr_location al;
+
struct exit_reasons_table *exit_reasons;
const char *exit_reasons_isa;
struct kvm_events_ops *events_ops;
- key_cmp_fun compare;
- struct list_head kvm_events_cache[EVENTS_CACHE_SIZE];
u64 total_time;
u64 total_count;
@@ -98,12 +103,11 @@ struct perf_kvm_stat {
struct intlist *pid_list;
- struct rb_root result;
-
int timerfd;
unsigned int display_time;
bool live;
bool force;
+ bool use_stdio;
};
struct kvm_reg_events_ops {
@@ -142,10 +146,45 @@ extern const char *kvm_events_tp[];
extern struct kvm_reg_events_ops kvm_reg_events_ops[];
extern const char * const kvm_skip_events[];
extern const char *vcpu_id_str;
-extern const int decode_str_len;
extern const char *kvm_exit_reason;
extern const char *kvm_entry_trace;
extern const char *kvm_exit_trace;
+
+static inline struct kvm_info *kvm_info__get(struct kvm_info *ki)
+{
+ if (ki)
+ refcount_inc(&ki->refcnt);
+ return ki;
+}
+
+static inline void kvm_info__put(struct kvm_info *ki)
+{
+ if (ki && refcount_dec_and_test(&ki->refcnt))
+ free(ki);
+}
+
+static inline void __kvm_info__zput(struct kvm_info **ki)
+{
+ kvm_info__put(*ki);
+ *ki = NULL;
+}
+
+#define kvm_info__zput(ki) __kvm_info__zput(&ki)
+
+static inline struct kvm_info *kvm_info__new(void)
+{
+ struct kvm_info *ki;
+
+ ki = zalloc(sizeof(*ki));
+ if (ki)
+ refcount_set(&ki->refcnt, 1);
+
+ return ki;
+}
+
+#else /* HAVE_KVM_STAT_SUPPORT */
+// We use this unconditionally in hists__findnew_entry() and hist_entry__delete()
+#define kvm_info__zput(ki) do { } while (0)
#endif /* HAVE_KVM_STAT_SUPPORT */
extern int kvm_add_default_arch_event(int *argc, const char **argv);
diff --git a/tools/perf/util/lock-contention.h b/tools/perf/util/lock-contention.h
index 040b618b2215..fa16532c971c 100644
--- a/tools/perf/util/lock-contention.h
+++ b/tools/perf/util/lock-contention.h
@@ -122,18 +122,26 @@ struct evlist;
struct machine;
struct target;
+struct lock_contention_fails {
+ int task;
+ int stack;
+ int time;
+ int data;
+};
+
struct lock_contention {
struct evlist *evlist;
struct target *target;
struct machine *machine;
struct hlist_head *result;
struct lock_filter *filters;
+ struct lock_contention_fails fails;
unsigned long map_nr_entries;
- int lost;
int max_stack;
int stack_skip;
int aggr_mode;
int owner;
+ int nr_filtered;
bool save_callstack;
};
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 803c9d1803dd..9e02e19c1b7a 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -44,10 +44,11 @@
#include <linux/zalloc.h>
static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock);
+static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms, u64 ip);
static struct dso *machine__kernel_dso(struct machine *machine)
{
- return machine->vmlinux_map->dso;
+ return map__dso(machine->vmlinux_map);
}
static void dsos__init(struct dsos *dsos)
@@ -434,7 +435,7 @@ static struct thread *findnew_guest_code(struct machine *machine,
return NULL;
/* Assume maps are set up if there are any */
- if (thread->maps->nr_maps)
+ if (maps__nr_maps(thread->maps))
return thread;
host_thread = machine__find_thread(host_machine, -1, pid);
@@ -878,46 +879,67 @@ static int machine__process_ksymbol_register(struct machine *machine,
struct perf_sample *sample __maybe_unused)
{
struct symbol *sym;
+ struct dso *dso;
struct map *map = maps__find(machine__kernel_maps(machine), event->ksymbol.addr);
+ bool put_map = false;
+ int err = 0;
if (!map) {
- struct dso *dso = dso__new(event->ksymbol.name);
+ dso = dso__new(event->ksymbol.name);
- if (dso) {
- dso->kernel = DSO_SPACE__KERNEL;
- map = map__new2(0, dso);
- dso__put(dso);
+ if (!dso) {
+ err = -ENOMEM;
+ goto out;
}
-
- if (!dso || !map) {
- return -ENOMEM;
+ dso->kernel = DSO_SPACE__KERNEL;
+ map = map__new2(0, dso);
+ dso__put(dso);
+ if (!map) {
+ err = -ENOMEM;
+ goto out;
}
-
+ /*
+ * The inserted map has a get on it, we need to put to release
+ * the reference count here, but do it after all accesses are
+ * done.
+ */
+ put_map = true;
if (event->ksymbol.ksym_type == PERF_RECORD_KSYMBOL_TYPE_OOL) {
- map->dso->binary_type = DSO_BINARY_TYPE__OOL;
- map->dso->data.file_size = event->ksymbol.len;
- dso__set_loaded(map->dso);
+ dso->binary_type = DSO_BINARY_TYPE__OOL;
+ dso->data.file_size = event->ksymbol.len;
+ dso__set_loaded(dso);
+ }
+
+ map__set_start(map, event->ksymbol.addr);
+ map__set_end(map, map__start(map) + event->ksymbol.len);
+ err = maps__insert(machine__kernel_maps(machine), map);
+ if (err) {
+ err = -ENOMEM;
+ goto out;
}
- map->start = event->ksymbol.addr;
- map->end = map->start + event->ksymbol.len;
- maps__insert(machine__kernel_maps(machine), map);
- map__put(map);
dso__set_loaded(dso);
if (is_bpf_image(event->ksymbol.name)) {
dso->binary_type = DSO_BINARY_TYPE__BPF_IMAGE;
dso__set_long_name(dso, "", false);
}
+ } else {
+ dso = map__dso(map);
}
- sym = symbol__new(map->map_ip(map, map->start),
+ sym = symbol__new(map__map_ip(map, map__start(map)),
event->ksymbol.len,
0, 0, event->ksymbol.name);
- if (!sym)
- return -ENOMEM;
- dso__insert_symbol(map->dso, sym);
- return 0;
+ if (!sym) {
+ err = -ENOMEM;
+ goto out;
+ }
+ dso__insert_symbol(dso, sym);
+out:
+ if (put_map)
+ map__put(map);
+ return err;
}
static int machine__process_ksymbol_unregister(struct machine *machine,
@@ -931,12 +953,14 @@ static int machine__process_ksymbol_unregister(struct machine *machine,
if (!map)
return 0;
- if (map != machine->vmlinux_map)
+ if (RC_CHK_ACCESS(map) != RC_CHK_ACCESS(machine->vmlinux_map))
maps__remove(machine__kernel_maps(machine), map);
else {
- sym = dso__find_symbol(map->dso, map->map_ip(map, map->start));
+ struct dso *dso = map__dso(map);
+
+ sym = dso__find_symbol(dso, map__map_ip(map, map__start(map)));
if (sym)
- dso__delete_symbol(map->dso, sym);
+ dso__delete_symbol(dso, sym);
}
return 0;
@@ -960,6 +984,7 @@ int machine__process_text_poke(struct machine *machine, union perf_event *event,
{
struct map *map = maps__find(machine__kernel_maps(machine), event->text_poke.addr);
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+ struct dso *dso = map ? map__dso(map) : NULL;
if (dump_trace)
perf_event__fprintf_text_poke(event, machine, stdout);
@@ -972,7 +997,7 @@ int machine__process_text_poke(struct machine *machine, union perf_event *event,
return 0;
}
- if (map && map->dso) {
+ if (dso) {
u8 *new_bytes = event->text_poke.bytes + event->text_poke.old_len;
int ret;
@@ -981,7 +1006,7 @@ int machine__process_text_poke(struct machine *machine, union perf_event *event,
* must be done prior to using kernel maps.
*/
map__load(map);
- ret = dso__data_write_cache_addr(map->dso, map, machine,
+ ret = dso__data_write_cache_addr(dso, map, machine,
event->text_poke.addr,
new_bytes,
event->text_poke.new_len);
@@ -1002,6 +1027,7 @@ static struct map *machine__addnew_module_map(struct machine *machine, u64 start
struct map *map = NULL;
struct kmod_path m;
struct dso *dso;
+ int err;
if (kmod_path__parse_name(&m, filename))
return NULL;
@@ -1014,10 +1040,12 @@ static struct map *machine__addnew_module_map(struct machine *machine, u64 start
if (map == NULL)
goto out;
- maps__insert(machine__kernel_maps(machine), map);
-
- /* Put the map here because maps__insert already got it */
- map__put(map);
+ err = maps__insert(machine__kernel_maps(machine), map);
+ /* If maps__insert failed, return NULL. */
+ if (err) {
+ map__put(map);
+ map = NULL;
+ }
out:
/* put the dso here, corresponding to machine__findnew_module_dso */
dso__put(dso);
@@ -1184,26 +1212,29 @@ int machine__create_extra_kernel_map(struct machine *machine,
{
struct kmap *kmap;
struct map *map;
+ int err;
map = map__new2(xm->start, kernel);
if (!map)
- return -1;
+ return -ENOMEM;
- map->end = xm->end;
- map->pgoff = xm->pgoff;
+ map__set_end(map, xm->end);
+ map__set_pgoff(map, xm->pgoff);
kmap = map__kmap(map);
strlcpy(kmap->name, xm->name, KMAP_NAME_LEN);
- maps__insert(machine__kernel_maps(machine), map);
+ err = maps__insert(machine__kernel_maps(machine), map);
- pr_debug2("Added extra kernel map %s %" PRIx64 "-%" PRIx64 "\n",
- kmap->name, map->start, map->end);
+ if (!err) {
+ pr_debug2("Added extra kernel map %s %" PRIx64 "-%" PRIx64 "\n",
+ kmap->name, map__start(map), map__end(map));
+ }
map__put(map);
- return 0;
+ return err;
}
static u64 find_entry_trampoline(struct dso *dso)
@@ -1244,23 +1275,23 @@ int machine__map_x86_64_entry_trampolines(struct machine *machine,
struct maps *kmaps = machine__kernel_maps(machine);
int nr_cpus_avail, cpu;
bool found = false;
- struct map *map;
+ struct map_rb_node *rb_node;
u64 pgoff;
/*
* In the vmlinux case, pgoff is a virtual address which must now be
* mapped to a vmlinux offset.
*/
- maps__for_each_entry(kmaps, map) {
+ maps__for_each_entry(kmaps, rb_node) {
+ struct map *dest_map, *map = rb_node->map;
struct kmap *kmap = __map__kmap(map);
- struct map *dest_map;
if (!kmap || !is_entry_trampoline(kmap->name))
continue;
- dest_map = maps__find(kmaps, map->pgoff);
+ dest_map = maps__find(kmaps, map__pgoff(map));
if (dest_map != map)
- map->pgoff = dest_map->map_ip(dest_map, map->pgoff);
+ map__set_pgoff(map, map__map_ip(dest_map, map__pgoff(map)));
found = true;
}
if (found || machine->trampolines_mapped)
@@ -1306,13 +1337,14 @@ __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)
/* In case of renewal the kernel map, destroy previous one */
machine__destroy_kernel_maps(machine);
+ map__put(machine->vmlinux_map);
machine->vmlinux_map = map__new2(0, kernel);
if (machine->vmlinux_map == NULL)
- return -1;
+ return -ENOMEM;
- machine->vmlinux_map->map_ip = machine->vmlinux_map->unmap_ip = identity__map_ip;
- maps__insert(machine__kernel_maps(machine), machine->vmlinux_map);
- return 0;
+ map__set_map_ip(machine->vmlinux_map, identity__map_ip);
+ map__set_unmap_ip(machine->vmlinux_map, identity__map_ip);
+ return maps__insert(machine__kernel_maps(machine), machine->vmlinux_map);
}
void machine__destroy_kernel_maps(struct machine *machine)
@@ -1410,10 +1442,11 @@ int machines__create_kernel_maps(struct machines *machines, pid_t pid)
int machine__load_kallsyms(struct machine *machine, const char *filename)
{
struct map *map = machine__kernel_map(machine);
- int ret = __dso__load_kallsyms(map->dso, filename, map, true);
+ struct dso *dso = map__dso(map);
+ int ret = __dso__load_kallsyms(dso, filename, map, true);
if (ret > 0) {
- dso__set_loaded(map->dso);
+ dso__set_loaded(dso);
/*
* Since /proc/kallsyms will have multiple sessions for the
* kernel, with modules between them, fixup the end of all
@@ -1428,10 +1461,11 @@ int machine__load_kallsyms(struct machine *machine, const char *filename)
int machine__load_vmlinux_path(struct machine *machine)
{
struct map *map = machine__kernel_map(machine);
- int ret = dso__load_vmlinux_path(map->dso, map);
+ struct dso *dso = map__dso(map);
+ int ret = dso__load_vmlinux_path(dso, map);
if (ret > 0)
- dso__set_loaded(map->dso);
+ dso__set_loaded(dso);
return ret;
}
@@ -1473,6 +1507,7 @@ static bool is_kmod_dso(struct dso *dso)
static int maps__set_module_path(struct maps *maps, const char *path, struct kmod_path *m)
{
char *long_name;
+ struct dso *dso;
struct map *map = maps__find_by_name(maps, m->name);
if (map == NULL)
@@ -1482,16 +1517,17 @@ static int maps__set_module_path(struct maps *maps, const char *path, struct kmo
if (long_name == NULL)
return -ENOMEM;
- dso__set_long_name(map->dso, long_name, true);
- dso__kernel_module_get_build_id(map->dso, "");
+ dso = map__dso(map);
+ dso__set_long_name(dso, long_name, true);
+ dso__kernel_module_get_build_id(dso, "");
/*
* Full name could reveal us kmod compression, so
* we need to update the symtab_type if needed.
*/
- if (m->comp && is_kmod_dso(map->dso)) {
- map->dso->symtab_type++;
- map->dso->comp = m->comp;
+ if (m->comp && is_kmod_dso(dso)) {
+ dso->symtab_type++;
+ dso->comp = m->comp;
}
return 0;
@@ -1588,10 +1624,10 @@ static int machine__create_module(void *arg, const char *name, u64 start,
map = machine__addnew_module_map(machine, start, name);
if (map == NULL)
return -1;
- map->end = start + size;
-
- dso__kernel_module_get_build_id(map->dso, machine->root_dir);
+ map__set_end(map, start + size);
+ dso__kernel_module_get_build_id(map__dso(map), machine->root_dir);
+ map__put(map);
return 0;
}
@@ -1624,35 +1660,38 @@ static int machine__create_modules(struct machine *machine)
static void machine__set_kernel_mmap(struct machine *machine,
u64 start, u64 end)
{
- machine->vmlinux_map->start = start;
- machine->vmlinux_map->end = end;
+ map__set_start(machine->vmlinux_map, start);
+ map__set_end(machine->vmlinux_map, end);
/*
* Be a bit paranoid here, some perf.data file came with
* a zero sized synthesized MMAP event for the kernel.
*/
if (start == 0 && end == 0)
- machine->vmlinux_map->end = ~0ULL;
+ map__set_end(machine->vmlinux_map, ~0ULL);
}
-static void machine__update_kernel_mmap(struct machine *machine,
+static int machine__update_kernel_mmap(struct machine *machine,
u64 start, u64 end)
{
- struct map *map = machine__kernel_map(machine);
+ struct map *orig, *updated;
+ int err;
- map__get(map);
- maps__remove(machine__kernel_maps(machine), map);
+ orig = machine->vmlinux_map;
+ updated = map__get(orig);
+ machine->vmlinux_map = updated;
machine__set_kernel_mmap(machine, start, end);
+ maps__remove(machine__kernel_maps(machine), orig);
+ err = maps__insert(machine__kernel_maps(machine), updated);
+ map__put(orig);
- maps__insert(machine__kernel_maps(machine), map);
- map__put(map);
+ return err;
}
int machine__create_kernel_maps(struct machine *machine)
{
struct dso *kernel = machine__get_kernel(machine);
const char *name = NULL;
- struct map *map;
u64 start = 0, end = ~0ULL;
int ret;
@@ -1684,7 +1723,9 @@ int machine__create_kernel_maps(struct machine *machine)
* we have a real start address now, so re-order the kmaps
* assume it's the last in the kmaps
*/
- machine__update_kernel_mmap(machine, start, end);
+ ret = machine__update_kernel_mmap(machine, start, end);
+ if (ret < 0)
+ goto out_put;
}
if (machine__create_extra_kernel_maps(machine, kernel))
@@ -1692,9 +1733,12 @@ int machine__create_kernel_maps(struct machine *machine)
if (end == ~0ULL) {
/* update end address of the kernel map using adjacent module address */
- map = map__next(machine__kernel_map(machine));
- if (map)
- machine__set_kernel_mmap(machine, start, map->start);
+ struct map_rb_node *rb_node = maps__find_node(machine__kernel_maps(machine),
+ machine__kernel_map(machine));
+ struct map_rb_node *next = map_rb_node__next(rb_node);
+
+ if (next)
+ machine__set_kernel_mmap(machine, start, map__start(next->map));
}
out_put:
@@ -1767,10 +1811,10 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
if (map == NULL)
goto out_problem;
- map->end = map->start + xm->end - xm->start;
+ map__set_end(map, map__start(map) + xm->end - xm->start);
if (build_id__is_defined(bid))
- dso__set_build_id(map->dso, bid);
+ dso__set_build_id(map__dso(map), bid);
} else if (is_kernel_mmap) {
const char *symbol_name = xm->name + strlen(mmap_name);
@@ -1827,7 +1871,10 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
if (strstr(kernel->long_name, "vmlinux"))
dso__set_short_name(kernel, "[kernel.vmlinux]", false);
- machine__update_kernel_mmap(machine, xm->start, xm->end);
+ if (machine__update_kernel_mmap(machine, xm->start, xm->end) < 0) {
+ dso__put(kernel);
+ goto out_problem;
+ }
if (build_id__is_defined(bid))
dso__set_build_id(kernel, bid);
@@ -2227,18 +2274,20 @@ static char *callchain_srcline(struct map_symbol *ms, u64 ip)
{
struct map *map = ms->map;
char *srcline = NULL;
+ struct dso *dso;
if (!map || callchain_param.key == CCKEY_FUNCTION)
return srcline;
- srcline = srcline__tree_find(&map->dso->srclines, ip);
+ dso = map__dso(map);
+ srcline = srcline__tree_find(&dso->srclines, ip);
if (!srcline) {
bool show_sym = false;
bool show_addr = callchain_param.key == CCKEY_ADDRESS;
- srcline = get_srcline(map->dso, map__rip_2objdump(map, ip),
+ srcline = get_srcline(dso, map__rip_2objdump(map, ip),
ms->sym, show_sym, show_addr, ip);
- srcline__tree_insert(&map->dso->srclines, ip, srcline);
+ srcline__tree_insert(&dso->srclines, ip, srcline);
}
return srcline;
@@ -2262,7 +2311,7 @@ static int add_callchain_ip(struct thread *thread,
{
struct map_symbol ms;
struct addr_location al;
- int nr_loop_iter = 0;
+ int nr_loop_iter = 0, err;
u64 iter_cycles = 0;
const char *srcline = NULL;
@@ -2322,10 +2371,16 @@ static int add_callchain_ip(struct thread *thread,
ms.maps = al.maps;
ms.map = al.map;
ms.sym = al.sym;
+
+ if (!branch && append_inlines(cursor, &ms, ip) == 0)
+ return 0;
+
srcline = callchain_srcline(&ms, al.addr);
- return callchain_cursor_append(cursor, ip, &ms,
- branch, flags, nr_loop_iter,
- iter_cycles, branch_from, srcline);
+ err = callchain_cursor_append(cursor, ip, &ms,
+ branch, flags, nr_loop_iter,
+ iter_cycles, branch_from, srcline);
+ map__put(al.map);
+ return err;
}
struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
@@ -2822,7 +2877,7 @@ static int find_prev_cpumode(struct ip_callchain *chain, struct thread *thread,
static u64 get_leaf_frame_caller(struct perf_sample *sample,
struct thread *thread, int usr_idx)
{
- if (machine__normalized_is(thread->maps->machine, "arm64"))
+ if (machine__normalized_is(maps__machine(thread->maps), "arm64"))
return get_leaf_frame_caller_aarch64(sample, thread, usr_idx);
else
return 0;
@@ -3014,21 +3069,23 @@ static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms
struct map *map = ms->map;
struct inline_node *inline_node;
struct inline_list *ilist;
+ struct dso *dso;
u64 addr;
int ret = 1;
if (!symbol_conf.inline_name || !map || !sym)
return ret;
- addr = map__map_ip(map, ip);
+ addr = map__dso_map_ip(map, ip);
addr = map__rip_2objdump(map, addr);
+ dso = map__dso(map);
- inline_node = inlines__tree_find(&map->dso->inlined_nodes, addr);
+ inline_node = inlines__tree_find(&dso->inlined_nodes, addr);
if (!inline_node) {
- inline_node = dso__parse_addr_inlines(map->dso, addr, sym);
+ inline_node = dso__parse_addr_inlines(dso, addr, sym);
if (!inline_node)
return ret;
- inlines__tree_insert(&map->dso->inlined_nodes, inline_node);
+ inlines__tree_insert(&dso->inlined_nodes, inline_node);
}
list_for_each_entry(ilist, &inline_node->val, list) {
@@ -3064,7 +3121,7 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
* its corresponding binary.
*/
if (entry->ms.map)
- addr = map__map_ip(entry->ms.map, entry->ip);
+ addr = map__dso_map_ip(entry->ms.map, entry->ip);
srcline = callchain_srcline(&entry->ms, addr);
return callchain_cursor_append(cursor, entry->ip, &entry->ms,
@@ -3254,7 +3311,7 @@ int machine__get_kernel_start(struct machine *machine)
* kernel_start = 1ULL << 63 for x86_64.
*/
if (!err && !machine__is(machine, "x86_64"))
- machine->kernel_start = map->start;
+ machine->kernel_start = map__start(map);
}
return err;
}
@@ -3305,8 +3362,8 @@ char *machine__resolve_kernel_addr(void *vmachine, unsigned long long *addrp, ch
if (sym == NULL)
return NULL;
- *modp = __map__is_kmodule(map) ? (char *)map->dso->short_name : NULL;
- *addrp = map->unmap_ip(map, sym->start);
+ *modp = __map__is_kmodule(map) ? (char *)map__dso(map)->short_name : NULL;
+ *addrp = map__unmap_ip(map, sym->start);
return sym->name;
}
@@ -3325,11 +3382,11 @@ int machine__for_each_dso(struct machine *machine, machine__dso_t fn, void *priv
int machine__for_each_kernel_map(struct machine *machine, machine__map_t fn, void *priv)
{
struct maps *maps = machine__kernel_maps(machine);
- struct map *map;
+ struct map_rb_node *pos;
int err = 0;
- for (map = maps__first(maps); map != NULL; map = map__next(map)) {
- err = fn(map, priv);
+ maps__for_each_entry(maps, pos) {
+ err = fn(pos->map, priv);
if (err != 0) {
break;
}
@@ -3349,17 +3406,17 @@ bool machine__is_lock_function(struct machine *machine, u64 addr)
return false;
}
- machine->sched.text_start = kmap->unmap_ip(kmap, sym->start);
+ machine->sched.text_start = map__unmap_ip(kmap, sym->start);
/* should not fail from here */
sym = machine__find_kernel_symbol_by_name(machine, "__sched_text_end", &kmap);
- machine->sched.text_end = kmap->unmap_ip(kmap, sym->start);
+ machine->sched.text_end = map__unmap_ip(kmap, sym->start);
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_start", &kmap);
- machine->lock.text_start = kmap->unmap_ip(kmap, sym->start);
+ machine->lock.text_start = map__unmap_ip(kmap, sym->start);
sym = machine__find_kernel_symbol_by_name(machine, "__lock_text_end", &kmap);
- machine->lock.text_end = kmap->unmap_ip(kmap, sym->start);
+ machine->lock.text_end = map__unmap_ip(kmap, sym->start);
}
/* failed to get kernel symbols */
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index f3a3d9b3a40d..b7f890950909 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -104,16 +104,15 @@ static inline bool replace_android_lib(const char *filename, char *newfilename)
void map__init(struct map *map, u64 start, u64 end, u64 pgoff, struct dso *dso)
{
- map->start = start;
- map->end = end;
- map->pgoff = pgoff;
- map->reloc = 0;
- map->dso = dso__get(dso);
- map->map_ip = map__map_ip;
- map->unmap_ip = map__unmap_ip;
- RB_CLEAR_NODE(&map->rb_node);
- map->erange_warned = false;
- refcount_set(&map->refcnt, 1);
+ map__set_start(map, start);
+ map__set_end(map, end);
+ map__set_pgoff(map, pgoff);
+ map__set_reloc(map, 0);
+ map__set_dso(map, dso__get(dso));
+ map__set_map_ip(map, map__dso_map_ip);
+ map__set_unmap_ip(map, map__dso_unmap_ip);
+ map__set_erange_warned(map, false);
+ refcount_set(map__refcnt(map), 1);
}
struct map *map__new(struct machine *machine, u64 start, u64 len,
@@ -121,11 +120,13 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
u32 prot, u32 flags, struct build_id *bid,
char *filename, struct thread *thread)
{
- struct map *map = malloc(sizeof(*map));
+ struct map *result;
+ RC_STRUCT(map) *map;
struct nsinfo *nsi = NULL;
struct nsinfo *nnsi;
- if (map != NULL) {
+ map = malloc(sizeof(*map));
+ if (ADD_RC_CHK(result, map)) {
char newfilename[PATH_MAX];
struct dso *dso, *header_bid_dso;
int anon, no_dso, vdso, android;
@@ -168,7 +169,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
if (dso == NULL)
goto out_delete;
- map__init(map, start, start + len, pgoff, dso);
+ map__init(result, start, start + len, pgoff, dso);
if (anon || no_dso) {
map->map_ip = map->unmap_ip = identity__map_ip;
@@ -205,10 +206,10 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
}
dso__put(dso);
}
- return map;
+ return result;
out_delete:
nsinfo__put(nsi);
- free(map);
+ RC_CHK_FREE(result);
return NULL;
}
@@ -219,23 +220,25 @@ out_delete:
*/
struct map *map__new2(u64 start, struct dso *dso)
{
- struct map *map = calloc(1, (sizeof(*map) +
- (dso->kernel ? sizeof(struct kmap) : 0)));
- if (map != NULL) {
+ struct map *result;
+ RC_STRUCT(map) *map;
+
+ map = calloc(1, sizeof(*map) + (dso->kernel ? sizeof(struct kmap) : 0));
+ if (ADD_RC_CHK(result, map)) {
/*
* ->end will be filled after we load all the symbols
*/
- map__init(map, start, 0, 0, dso);
+ map__init(result, start, 0, 0, dso);
}
- return map;
+ return result;
}
bool __map__is_kernel(const struct map *map)
{
- if (!map->dso->kernel)
+ if (!map__dso(map)->kernel)
return false;
- return machine__kernel_map(map__kmaps((struct map *)map)->machine) == map;
+ return machine__kernel_map(maps__machine(map__kmaps((struct map *)map))) == map;
}
bool __map__is_extra_kernel_map(const struct map *map)
@@ -248,8 +251,9 @@ bool __map__is_extra_kernel_map(const struct map *map)
bool __map__is_bpf_prog(const struct map *map)
{
const char *name;
+ struct dso *dso = map__dso(map);
- if (map->dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO)
+ if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO)
return true;
/*
@@ -257,15 +261,16 @@ bool __map__is_bpf_prog(const struct map *map)
* type of DSO_BINARY_TYPE__BPF_PROG_INFO. In such cases, we can
* guess the type based on name.
*/
- name = map->dso->short_name;
+ name = dso->short_name;
return name && (strstr(name, "bpf_prog_") == name);
}
bool __map__is_bpf_image(const struct map *map)
{
const char *name;
+ struct dso *dso = map__dso(map);
- if (map->dso->binary_type == DSO_BINARY_TYPE__BPF_IMAGE)
+ if (dso->binary_type == DSO_BINARY_TYPE__BPF_IMAGE)
return true;
/*
@@ -273,55 +278,64 @@ bool __map__is_bpf_image(const struct map *map)
* type of DSO_BINARY_TYPE__BPF_IMAGE. In such cases, we can
* guess the type based on name.
*/
- name = map->dso->short_name;
+ name = dso->short_name;
return name && is_bpf_image(name);
}
bool __map__is_ool(const struct map *map)
{
- return map->dso && map->dso->binary_type == DSO_BINARY_TYPE__OOL;
+ const struct dso *dso = map__dso(map);
+
+ return dso && dso->binary_type == DSO_BINARY_TYPE__OOL;
}
bool map__has_symbols(const struct map *map)
{
- return dso__has_symbols(map->dso);
+ return dso__has_symbols(map__dso(map));
}
static void map__exit(struct map *map)
{
- BUG_ON(refcount_read(&map->refcnt) != 0);
- dso__zput(map->dso);
+ BUG_ON(refcount_read(map__refcnt(map)) != 0);
+ dso__zput(RC_CHK_ACCESS(map)->dso);
}
void map__delete(struct map *map)
{
map__exit(map);
- free(map);
+ RC_CHK_FREE(map);
}
void map__put(struct map *map)
{
- if (map && refcount_dec_and_test(&map->refcnt))
+ if (map && refcount_dec_and_test(map__refcnt(map)))
map__delete(map);
+ else
+ RC_CHK_PUT(map);
}
void map__fixup_start(struct map *map)
{
- struct rb_root_cached *symbols = &map->dso->symbols;
+ struct dso *dso = map__dso(map);
+ struct rb_root_cached *symbols = &dso->symbols;
struct rb_node *nd = rb_first_cached(symbols);
+
if (nd != NULL) {
struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
- map->start = sym->start;
+
+ map__set_start(map, sym->start);
}
}
void map__fixup_end(struct map *map)
{
- struct rb_root_cached *symbols = &map->dso->symbols;
+ struct dso *dso = map__dso(map);
+ struct rb_root_cached *symbols = &dso->symbols;
struct rb_node *nd = rb_last(&symbols->rb_root);
+
if (nd != NULL) {
struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
- map->end = sym->end;
+ map__set_end(map, sym->end);
}
}
@@ -329,18 +343,19 @@ void map__fixup_end(struct map *map)
int map__load(struct map *map)
{
- const char *name = map->dso->long_name;
+ struct dso *dso = map__dso(map);
+ const char *name = dso->long_name;
int nr;
- if (dso__loaded(map->dso))
+ if (dso__loaded(dso))
return 0;
- nr = dso__load(map->dso, map);
+ nr = dso__load(dso, map);
if (nr < 0) {
- if (map->dso->has_build_id) {
+ if (dso->has_build_id) {
char sbuild_id[SBUILD_ID_SIZE];
- build_id__sprintf(&map->dso->bid, sbuild_id);
+ build_id__sprintf(&dso->bid, sbuild_id);
pr_debug("%s with build id %s not found", name, sbuild_id);
} else
pr_debug("Failed to open %s", name);
@@ -372,54 +387,61 @@ struct symbol *map__find_symbol(struct map *map, u64 addr)
if (map__load(map) < 0)
return NULL;
- return dso__find_symbol(map->dso, addr);
+ return dso__find_symbol(map__dso(map), addr);
}
struct symbol *map__find_symbol_by_name(struct map *map, const char *name)
{
+ struct dso *dso;
+
if (map__load(map) < 0)
return NULL;
- if (!dso__sorted_by_name(map->dso))
- dso__sort_by_name(map->dso);
+ dso = map__dso(map);
+ if (!dso__sorted_by_name(dso))
+ dso__sort_by_name(dso);
- return dso__find_symbol_by_name(map->dso, name);
+ return dso__find_symbol_by_name(dso, name);
}
struct map *map__clone(struct map *from)
{
- size_t size = sizeof(struct map);
- struct map *map;
+ struct map *result;
+ RC_STRUCT(map) *map;
+ size_t size = sizeof(RC_STRUCT(map));
+ struct dso *dso = map__dso(from);
- if (from->dso && from->dso->kernel)
+ if (dso && dso->kernel)
size += sizeof(struct kmap);
- map = memdup(from, size);
- if (map != NULL) {
+ map = memdup(RC_CHK_ACCESS(from), size);
+ if (ADD_RC_CHK(result, map)) {
refcount_set(&map->refcnt, 1);
- RB_CLEAR_NODE(&map->rb_node);
- dso__get(map->dso);
+ map->dso = dso__get(dso);
}
- return map;
+ return result;
}
size_t map__fprintf(struct map *map, FILE *fp)
{
+ const struct dso *dso = map__dso(map);
+
return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s\n",
- map->start, map->end, map->pgoff, map->dso->name);
+ map__start(map), map__end(map), map__pgoff(map), dso->name);
}
size_t map__fprintf_dsoname(struct map *map, FILE *fp)
{
char buf[symbol_conf.pad_output_len_dso + 1];
const char *dsoname = "[unknown]";
+ const struct dso *dso = map ? map__dso(map) : NULL;
- if (map && map->dso) {
- if (symbol_conf.show_kernel_path && map->dso->long_name)
- dsoname = map->dso->long_name;
+ if (dso) {
+ if (symbol_conf.show_kernel_path && dso->long_name)
+ dsoname = dso->long_name;
else
- dsoname = map->dso->name;
+ dsoname = dso->name;
}
if (symbol_conf.pad_output_len_dso) {
@@ -434,15 +456,17 @@ char *map__srcline(struct map *map, u64 addr, struct symbol *sym)
{
if (map == NULL)
return SRCLINE_UNKNOWN;
- return get_srcline(map->dso, map__rip_2objdump(map, addr), sym, true, true, addr);
+
+ return get_srcline(map__dso(map), map__rip_2objdump(map, addr), sym, true, true, addr);
}
int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix,
FILE *fp)
{
+ const struct dso *dso = map ? map__dso(map) : NULL;
int ret = 0;
- if (map && map->dso) {
+ if (dso) {
char *srcline = map__srcline(map, addr, NULL);
if (strncmp(srcline, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0)
ret = fprintf(fp, "%s%s", prefix, srcline);
@@ -471,33 +495,38 @@ void srccode_state_free(struct srccode_state *state)
u64 map__rip_2objdump(struct map *map, u64 rip)
{
struct kmap *kmap = __map__kmap(map);
+ const struct dso *dso = map__dso(map);
/*
* vmlinux does not have program headers for PTI entry trampolines and
* kcore may not either. However the trampoline object code is on the
* main kernel map, so just use that instead.
*/
- if (kmap && is_entry_trampoline(kmap->name) && kmap->kmaps && kmap->kmaps->machine) {
- struct map *kernel_map = machine__kernel_map(kmap->kmaps->machine);
+ if (kmap && is_entry_trampoline(kmap->name) && kmap->kmaps) {
+ struct machine *machine = maps__machine(kmap->kmaps);
- if (kernel_map)
- map = kernel_map;
+ if (machine) {
+ struct map *kernel_map = machine__kernel_map(machine);
+
+ if (kernel_map)
+ map = kernel_map;
+ }
}
- if (!map->dso->adjust_symbols)
+ if (!dso->adjust_symbols)
return rip;
- if (map->dso->rel)
- return rip - map->pgoff;
+ if (dso->rel)
+ return rip - map__pgoff(map);
/*
* kernel modules also have DSO_TYPE_USER in dso->kernel,
* but all kernel modules are ET_REL, so won't get here.
*/
- if (map->dso->kernel == DSO_SPACE__USER)
- return rip + map->dso->text_offset;
+ if (dso->kernel == DSO_SPACE__USER)
+ return rip + dso->text_offset;
- return map->unmap_ip(map, rip) - map->reloc;
+ return map__unmap_ip(map, rip) - map__reloc(map);
}
/**
@@ -508,54 +537,44 @@ u64 map__rip_2objdump(struct map *map, u64 rip)
* Closely related to map__rip_2objdump(), this function takes an address from
* objdump and converts it to a memory address. Note this assumes that @map
* contains the address. To be sure the result is valid, check it forwards
- * e.g. map__rip_2objdump(map->map_ip(map, map__objdump_2mem(map, ip))) == ip
+ * e.g. map__rip_2objdump(map__map_ip(map, map__objdump_2mem(map, ip))) == ip
*
* Return: Memory address.
*/
u64 map__objdump_2mem(struct map *map, u64 ip)
{
- if (!map->dso->adjust_symbols)
- return map->unmap_ip(map, ip);
+ const struct dso *dso = map__dso(map);
- if (map->dso->rel)
- return map->unmap_ip(map, ip + map->pgoff);
+ if (!dso->adjust_symbols)
+ return map__unmap_ip(map, ip);
+
+ if (dso->rel)
+ return map__unmap_ip(map, ip + map__pgoff(map));
/*
* kernel modules also have DSO_TYPE_USER in dso->kernel,
* but all kernel modules are ET_REL, so won't get here.
*/
- if (map->dso->kernel == DSO_SPACE__USER)
- return map->unmap_ip(map, ip - map->dso->text_offset);
+ if (dso->kernel == DSO_SPACE__USER)
+ return map__unmap_ip(map, ip - dso->text_offset);
- return ip + map->reloc;
+ return ip + map__reloc(map);
}
bool map__contains_symbol(const struct map *map, const struct symbol *sym)
{
- u64 ip = map->unmap_ip(map, sym->start);
-
- return ip >= map->start && ip < map->end;
-}
-
-static struct map *__map__next(struct map *map)
-{
- struct rb_node *next = rb_next(&map->rb_node);
+ u64 ip = map__unmap_ip(map, sym->start);
- if (next)
- return rb_entry(next, struct map, rb_node);
- return NULL;
-}
-
-struct map *map__next(struct map *map)
-{
- return map ? __map__next(map) : NULL;
+ return ip >= map__start(map) && ip < map__end(map);
}
struct kmap *__map__kmap(struct map *map)
{
- if (!map->dso || !map->dso->kernel)
+ const struct dso *dso = map__dso(map);
+
+ if (!dso || !dso->kernel)
return NULL;
- return (struct kmap *)(map + 1);
+ return (struct kmap *)(&RC_CHK_ACCESS(map)[1]);
}
struct kmap *map__kmap(struct map *map)
@@ -578,14 +597,14 @@ struct maps *map__kmaps(struct map *map)
return kmap->kmaps;
}
-u64 map__map_ip(const struct map *map, u64 ip)
+u64 map__dso_map_ip(const struct map *map, u64 ip)
{
- return ip - map->start + map->pgoff;
+ return ip - map__start(map) + map__pgoff(map);
}
-u64 map__unmap_ip(const struct map *map, u64 ip)
+u64 map__dso_unmap_ip(const struct map *map, u64 ip)
{
- return ip + map->start - map->pgoff;
+ return ip + map__start(map) - map__pgoff(map);
}
u64 identity__map_ip(const struct map *map __maybe_unused, u64 ip)
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 3dcfe06db6b3..823ab7fc0acf 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -10,16 +10,13 @@
#include <string.h>
#include <stdbool.h>
#include <linux/types.h>
+#include <internal/rc_check.h>
struct dso;
struct maps;
struct machine;
-struct map {
- union {
- struct rb_node rb_node;
- struct list_head node;
- };
+DECLARE_RC_STRUCT(map) {
u64 start;
u64 end;
bool erange_warned:1;
@@ -45,15 +42,85 @@ struct kmap *map__kmap(struct map *map);
struct maps *map__kmaps(struct map *map);
/* ip -> dso rip */
-u64 map__map_ip(const struct map *map, u64 ip);
+u64 map__dso_map_ip(const struct map *map, u64 ip);
/* dso rip -> ip */
-u64 map__unmap_ip(const struct map *map, u64 ip);
+u64 map__dso_unmap_ip(const struct map *map, u64 ip);
/* Returns ip */
u64 identity__map_ip(const struct map *map __maybe_unused, u64 ip);
+static inline struct dso *map__dso(const struct map *map)
+{
+ return RC_CHK_ACCESS(map)->dso;
+}
+
+static inline u64 map__map_ip(const struct map *map, u64 ip)
+{
+ return RC_CHK_ACCESS(map)->map_ip(map, ip);
+}
+
+static inline u64 map__unmap_ip(const struct map *map, u64 ip)
+{
+ return RC_CHK_ACCESS(map)->unmap_ip(map, ip);
+}
+
+static inline void *map__map_ip_ptr(struct map *map)
+{
+ return RC_CHK_ACCESS(map)->map_ip;
+}
+
+static inline void* map__unmap_ip_ptr(struct map *map)
+{
+ return RC_CHK_ACCESS(map)->unmap_ip;
+}
+
+static inline u64 map__start(const struct map *map)
+{
+ return RC_CHK_ACCESS(map)->start;
+}
+
+static inline u64 map__end(const struct map *map)
+{
+ return RC_CHK_ACCESS(map)->end;
+}
+
+static inline u64 map__pgoff(const struct map *map)
+{
+ return RC_CHK_ACCESS(map)->pgoff;
+}
+
+static inline u64 map__reloc(const struct map *map)
+{
+ return RC_CHK_ACCESS(map)->reloc;
+}
+
+static inline u32 map__flags(const struct map *map)
+{
+ return RC_CHK_ACCESS(map)->flags;
+}
+
+static inline u32 map__prot(const struct map *map)
+{
+ return RC_CHK_ACCESS(map)->prot;
+}
+
+static inline bool map__priv(const struct map *map)
+{
+ return RC_CHK_ACCESS(map)->priv;
+}
+
+static inline refcount_t *map__refcnt(struct map *map)
+{
+ return &RC_CHK_ACCESS(map)->refcnt;
+}
+
+static inline bool map__erange_warned(struct map *map)
+{
+ return RC_CHK_ACCESS(map)->erange_warned;
+}
+
static inline size_t map__size(const struct map *map)
{
- return map->end - map->start;
+ return map__end(map) - map__start(map);
}
/* rip/ip <-> addr suitable for passing to `objdump --start-address=` */
@@ -73,7 +140,7 @@ struct thread;
* Note: caller must ensure map->dso is not NULL (map is loaded).
*/
#define map__for_each_symbol(map, pos, n) \
- dso__for_each_symbol(map->dso, pos, n)
+ dso__for_each_symbol(map__dso(map), pos, n)
/* map__for_each_symbol_with_name - iterate over the symbols in the given map
* that have the given name
@@ -107,9 +174,12 @@ struct map *map__clone(struct map *map);
static inline struct map *map__get(struct map *map)
{
- if (map)
- refcount_inc(&map->refcnt);
- return map;
+ struct map *result;
+
+ if (RC_CHK_GET(result, map))
+ refcount_inc(map__refcnt(map));
+
+ return result;
}
void map__put(struct map *map);
@@ -180,4 +250,54 @@ static inline int is_no_dso_memory(const char *filename)
!strncmp(filename, "/SYSV", 5) ||
!strcmp(filename, "[heap]");
}
+
+static inline void map__set_start(struct map *map, u64 start)
+{
+ RC_CHK_ACCESS(map)->start = start;
+}
+
+static inline void map__set_end(struct map *map, u64 end)
+{
+ RC_CHK_ACCESS(map)->end = end;
+}
+
+static inline void map__set_pgoff(struct map *map, u64 pgoff)
+{
+ RC_CHK_ACCESS(map)->pgoff = pgoff;
+}
+
+static inline void map__add_pgoff(struct map *map, u64 inc)
+{
+ RC_CHK_ACCESS(map)->pgoff += inc;
+}
+
+static inline void map__set_reloc(struct map *map, u64 reloc)
+{
+ RC_CHK_ACCESS(map)->reloc = reloc;
+}
+
+static inline void map__set_priv(struct map *map, int priv)
+{
+ RC_CHK_ACCESS(map)->priv = priv;
+}
+
+static inline void map__set_erange_warned(struct map *map, bool erange_warned)
+{
+ RC_CHK_ACCESS(map)->erange_warned = erange_warned;
+}
+
+static inline void map__set_dso(struct map *map, struct dso *dso)
+{
+ RC_CHK_ACCESS(map)->dso = dso;
+}
+
+static inline void map__set_map_ip(struct map *map, u64 (*map_ip)(const struct map *map, u64 ip))
+{
+ RC_CHK_ACCESS(map)->map_ip = map_ip;
+}
+
+static inline void map__set_unmap_ip(struct map *map, u64 (*unmap_ip)(const struct map *map, u64 rip))
+{
+ RC_CHK_ACCESS(map)->unmap_ip = unmap_ip;
+}
#endif /* __PERF_MAP_H */
diff --git a/tools/perf/util/maps.c b/tools/perf/util/maps.c
index 37bd5b40000d..1aeb1db58fe5 100644
--- a/tools/perf/util/maps.c
+++ b/tools/perf/util/maps.c
@@ -10,17 +10,15 @@
#include "ui/ui.h"
#include "unwind.h"
-static void __maps__insert(struct maps *maps, struct map *map);
-
static void maps__init(struct maps *maps, struct machine *machine)
{
- maps->entries = RB_ROOT;
- init_rwsem(&maps->lock);
- maps->machine = machine;
- maps->last_search_by_name = NULL;
- maps->nr_maps = 0;
- maps->maps_by_name = NULL;
- refcount_set(&maps->refcnt, 1);
+ refcount_set(maps__refcnt(maps), 1);
+ init_rwsem(maps__lock(maps));
+ RC_CHK_ACCESS(maps)->entries = RB_ROOT;
+ RC_CHK_ACCESS(maps)->machine = machine;
+ RC_CHK_ACCESS(maps)->last_search_by_name = NULL;
+ RC_CHK_ACCESS(maps)->nr_maps = 0;
+ RC_CHK_ACCESS(maps)->maps_by_name = NULL;
}
static void __maps__free_maps_by_name(struct maps *maps)
@@ -28,17 +26,54 @@ static void __maps__free_maps_by_name(struct maps *maps)
/*
* Free everything to try to do it from the rbtree in the next search
*/
- zfree(&maps->maps_by_name);
- maps->nr_maps_allocated = 0;
+ for (unsigned int i = 0; i < maps__nr_maps(maps); i++)
+ map__put(maps__maps_by_name(maps)[i]);
+
+ zfree(&RC_CHK_ACCESS(maps)->maps_by_name);
+ RC_CHK_ACCESS(maps)->nr_maps_allocated = 0;
}
-void maps__insert(struct maps *maps, struct map *map)
+static int __maps__insert(struct maps *maps, struct map *map)
{
- down_write(&maps->lock);
- __maps__insert(maps, map);
- ++maps->nr_maps;
+ struct rb_node **p = &maps__entries(maps)->rb_node;
+ struct rb_node *parent = NULL;
+ const u64 ip = map__start(map);
+ struct map_rb_node *m, *new_rb_node;
+
+ new_rb_node = malloc(sizeof(*new_rb_node));
+ if (!new_rb_node)
+ return -ENOMEM;
+
+ RB_CLEAR_NODE(&new_rb_node->rb_node);
+ new_rb_node->map = map__get(map);
+
+ while (*p != NULL) {
+ parent = *p;
+ m = rb_entry(parent, struct map_rb_node, rb_node);
+ if (ip < map__start(m->map))
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
- if (map->dso && map->dso->kernel) {
+ rb_link_node(&new_rb_node->rb_node, parent, p);
+ rb_insert_color(&new_rb_node->rb_node, maps__entries(maps));
+ return 0;
+}
+
+int maps__insert(struct maps *maps, struct map *map)
+{
+ int err;
+ const struct dso *dso = map__dso(map);
+
+ down_write(maps__lock(maps));
+ err = __maps__insert(maps, map);
+ if (err)
+ goto out;
+
+ ++RC_CHK_ACCESS(maps)->nr_maps;
+
+ if (dso && dso->kernel) {
struct kmap *kmap = map__kmap(map);
if (kmap)
@@ -52,60 +87,72 @@ void maps__insert(struct maps *maps, struct map *map)
* If we already performed some search by name, then we need to add the just
* inserted map and resort.
*/
- if (maps->maps_by_name) {
- if (maps->nr_maps > maps->nr_maps_allocated) {
- int nr_allocate = maps->nr_maps * 2;
- struct map **maps_by_name = realloc(maps->maps_by_name, nr_allocate * sizeof(map));
+ if (maps__maps_by_name(maps)) {
+ if (maps__nr_maps(maps) > RC_CHK_ACCESS(maps)->nr_maps_allocated) {
+ int nr_allocate = maps__nr_maps(maps) * 2;
+ struct map **maps_by_name = realloc(maps__maps_by_name(maps),
+ nr_allocate * sizeof(map));
if (maps_by_name == NULL) {
__maps__free_maps_by_name(maps);
- up_write(&maps->lock);
- return;
+ err = -ENOMEM;
+ goto out;
}
- maps->maps_by_name = maps_by_name;
- maps->nr_maps_allocated = nr_allocate;
+ RC_CHK_ACCESS(maps)->maps_by_name = maps_by_name;
+ RC_CHK_ACCESS(maps)->nr_maps_allocated = nr_allocate;
}
- maps->maps_by_name[maps->nr_maps - 1] = map;
+ maps__maps_by_name(maps)[maps__nr_maps(maps) - 1] = map__get(map);
__maps__sort_by_name(maps);
}
- up_write(&maps->lock);
+ out:
+ up_write(maps__lock(maps));
+ return err;
}
-static void __maps__remove(struct maps *maps, struct map *map)
+static void __maps__remove(struct maps *maps, struct map_rb_node *rb_node)
{
- rb_erase_init(&map->rb_node, &maps->entries);
- map__put(map);
+ rb_erase_init(&rb_node->rb_node, maps__entries(maps));
+ map__put(rb_node->map);
+ free(rb_node);
}
void maps__remove(struct maps *maps, struct map *map)
{
- down_write(&maps->lock);
- if (maps->last_search_by_name == map)
- maps->last_search_by_name = NULL;
+ struct map_rb_node *rb_node;
- __maps__remove(maps, map);
- --maps->nr_maps;
- if (maps->maps_by_name)
+ down_write(maps__lock(maps));
+ if (RC_CHK_ACCESS(maps)->last_search_by_name == map)
+ RC_CHK_ACCESS(maps)->last_search_by_name = NULL;
+
+ rb_node = maps__find_node(maps, map);
+ assert(rb_node->RC_CHK_ACCESS(map) == RC_CHK_ACCESS(map));
+ __maps__remove(maps, rb_node);
+ if (maps__maps_by_name(maps))
__maps__free_maps_by_name(maps);
- up_write(&maps->lock);
+ --RC_CHK_ACCESS(maps)->nr_maps;
+ up_write(maps__lock(maps));
}
static void __maps__purge(struct maps *maps)
{
- struct map *pos, *next;
+ struct map_rb_node *pos, *next;
+
+ if (maps__maps_by_name(maps))
+ __maps__free_maps_by_name(maps);
maps__for_each_entry_safe(maps, pos, next) {
- rb_erase_init(&pos->rb_node, &maps->entries);
- map__put(pos);
+ rb_erase_init(&pos->rb_node, maps__entries(maps));
+ map__put(pos->map);
+ free(pos);
}
}
static void maps__exit(struct maps *maps)
{
- down_write(&maps->lock);
+ down_write(maps__lock(maps));
__maps__purge(maps);
- up_write(&maps->lock);
+ up_write(maps__lock(maps));
}
bool maps__empty(struct maps *maps)
@@ -115,25 +162,38 @@ bool maps__empty(struct maps *maps)
struct maps *maps__new(struct machine *machine)
{
- struct maps *maps = zalloc(sizeof(*maps));
+ struct maps *result;
+ RC_STRUCT(maps) *maps = zalloc(sizeof(*maps));
- if (maps != NULL)
- maps__init(maps, machine);
+ if (ADD_RC_CHK(result, maps))
+ maps__init(result, machine);
- return maps;
+ return result;
}
void maps__delete(struct maps *maps)
{
maps__exit(maps);
unwind__finish_access(maps);
- free(maps);
+ RC_CHK_FREE(maps);
+}
+
+struct maps *maps__get(struct maps *maps)
+{
+ struct maps *result;
+
+ if (RC_CHK_GET(result, maps))
+ refcount_inc(maps__refcnt(maps));
+
+ return result;
}
void maps__put(struct maps *maps)
{
- if (maps && refcount_dec_and_test(&maps->refcnt))
+ if (maps && refcount_dec_and_test(maps__refcnt(maps)))
maps__delete(maps);
+ else
+ RC_CHK_PUT(maps);
}
struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp)
@@ -144,7 +204,7 @@ struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp)
if (map != NULL && map__load(map) >= 0) {
if (mapp != NULL)
*mapp = map;
- return map__find_symbol(map, map->map_ip(map, addr));
+ return map__find_symbol(map, map__map_ip(map, addr));
}
return NULL;
@@ -153,33 +213,33 @@ struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp)
struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp)
{
struct symbol *sym;
- struct map *pos;
+ struct map_rb_node *pos;
- down_read(&maps->lock);
+ down_read(maps__lock(maps));
maps__for_each_entry(maps, pos) {
- sym = map__find_symbol_by_name(pos, name);
+ sym = map__find_symbol_by_name(pos->map, name);
if (sym == NULL)
continue;
- if (!map__contains_symbol(pos, sym)) {
+ if (!map__contains_symbol(pos->map, sym)) {
sym = NULL;
continue;
}
if (mapp != NULL)
- *mapp = pos;
+ *mapp = pos->map;
goto out;
}
sym = NULL;
out:
- up_read(&maps->lock);
+ up_read(maps__lock(maps));
return sym;
}
int maps__find_ams(struct maps *maps, struct addr_map_symbol *ams)
{
- if (ams->addr < ams->ms.map->start || ams->addr >= ams->ms.map->end) {
+ if (ams->addr < map__start(ams->ms.map) || ams->addr >= map__end(ams->ms.map)) {
if (maps == NULL)
return -1;
ams->ms.map = maps__find(maps, ams->addr);
@@ -187,7 +247,7 @@ int maps__find_ams(struct maps *maps, struct addr_map_symbol *ams)
return -1;
}
- ams->al_addr = ams->ms.map->map_ip(ams->ms.map, ams->addr);
+ ams->al_addr = map__map_ip(ams->ms.map, ams->addr);
ams->ms.sym = map__find_symbol(ams->ms.map, ams->al_addr);
return ams->ms.sym ? 0 : -1;
@@ -196,20 +256,20 @@ int maps__find_ams(struct maps *maps, struct addr_map_symbol *ams)
size_t maps__fprintf(struct maps *maps, FILE *fp)
{
size_t printed = 0;
- struct map *pos;
+ struct map_rb_node *pos;
- down_read(&maps->lock);
+ down_read(maps__lock(maps));
maps__for_each_entry(maps, pos) {
printed += fprintf(fp, "Map:");
- printed += map__fprintf(pos, fp);
+ printed += map__fprintf(pos->map, fp);
if (verbose > 2) {
- printed += dso__fprintf(pos->dso, fp);
+ printed += dso__fprintf(map__dso(pos->map), fp);
printed += fprintf(fp, "--\n");
}
}
- up_read(&maps->lock);
+ up_read(maps__lock(maps));
return printed;
}
@@ -220,9 +280,9 @@ int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp)
struct rb_node *next, *first;
int err = 0;
- down_write(&maps->lock);
+ down_write(maps__lock(maps));
- root = &maps->entries;
+ root = maps__entries(maps);
/*
* Find first map where end > map->start.
@@ -231,11 +291,11 @@ int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp)
next = root->rb_node;
first = NULL;
while (next) {
- struct map *pos = rb_entry(next, struct map, rb_node);
+ struct map_rb_node *pos = rb_entry(next, struct map_rb_node, rb_node);
- if (pos->end > map->start) {
+ if (map__end(pos->map) > map__start(map)) {
first = next;
- if (pos->start <= map->start)
+ if (map__start(pos->map) <= map__start(map))
break;
next = next->rb_left;
} else
@@ -243,26 +303,26 @@ int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp)
}
next = first;
- while (next) {
- struct map *pos = rb_entry(next, struct map, rb_node);
+ while (next && !err) {
+ struct map_rb_node *pos = rb_entry(next, struct map_rb_node, rb_node);
next = rb_next(&pos->rb_node);
/*
* Stop if current map starts after map->end.
* Maps are ordered by start: next will not overlap for sure.
*/
- if (pos->start >= map->end)
+ if (map__start(pos->map) >= map__end(map))
break;
if (verbose >= 2) {
if (use_browser) {
pr_debug("overlapping maps in %s (disable tui for more info)\n",
- map->dso->name);
+ map__dso(map)->name);
} else {
fputs("overlapping maps:\n", fp);
map__fprintf(map, fp);
- map__fprintf(pos, fp);
+ map__fprintf(pos->map, fp);
}
}
@@ -271,47 +331,51 @@ int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp)
* Now check if we need to create new maps for areas not
* overlapped by the new map:
*/
- if (map->start > pos->start) {
- struct map *before = map__clone(pos);
+ if (map__start(map) > map__start(pos->map)) {
+ struct map *before = map__clone(pos->map);
if (before == NULL) {
err = -ENOMEM;
goto put_map;
}
- before->end = map->start;
- __maps__insert(maps, before);
+ map__set_end(before, map__start(map));
+ err = __maps__insert(maps, before);
+ if (err) {
+ map__put(before);
+ goto put_map;
+ }
+
if (verbose >= 2 && !use_browser)
map__fprintf(before, fp);
map__put(before);
}
- if (map->end < pos->end) {
- struct map *after = map__clone(pos);
+ if (map__end(map) < map__end(pos->map)) {
+ struct map *after = map__clone(pos->map);
if (after == NULL) {
err = -ENOMEM;
goto put_map;
}
- after->start = map->end;
- after->pgoff += map->end - pos->start;
- assert(pos->map_ip(pos, map->end) == after->map_ip(after, map->end));
- __maps__insert(maps, after);
+ map__set_start(after, map__end(map));
+ map__add_pgoff(after, map__end(map) - map__start(pos->map));
+ assert(map__map_ip(pos->map, map__end(map)) ==
+ map__map_ip(after, map__end(map)));
+ err = __maps__insert(maps, after);
+ if (err) {
+ map__put(after);
+ goto put_map;
+ }
if (verbose >= 2 && !use_browser)
map__fprintf(after, fp);
map__put(after);
}
put_map:
- map__put(pos);
-
- if (err)
- goto out;
+ map__put(pos->map);
}
-
- err = 0;
-out:
- up_write(&maps->lock);
+ up_write(maps__lock(maps));
return err;
}
@@ -322,12 +386,12 @@ int maps__clone(struct thread *thread, struct maps *parent)
{
struct maps *maps = thread->maps;
int err;
- struct map *map;
+ struct map_rb_node *rb_node;
- down_read(&parent->lock);
+ down_read(maps__lock(parent));
- maps__for_each_entry(parent, map) {
- struct map *new = map__clone(map);
+ maps__for_each_entry(parent, rb_node) {
+ struct map *new = map__clone(rb_node->map);
if (new == NULL) {
err = -ENOMEM;
@@ -338,50 +402,44 @@ int maps__clone(struct thread *thread, struct maps *parent)
if (err)
goto out_unlock;
- maps__insert(maps, new);
+ err = maps__insert(maps, new);
+ if (err)
+ goto out_unlock;
+
map__put(new);
}
err = 0;
out_unlock:
- up_read(&parent->lock);
+ up_read(maps__lock(parent));
return err;
}
-static void __maps__insert(struct maps *maps, struct map *map)
+struct map_rb_node *maps__find_node(struct maps *maps, struct map *map)
{
- struct rb_node **p = &maps->entries.rb_node;
- struct rb_node *parent = NULL;
- const u64 ip = map->start;
- struct map *m;
+ struct map_rb_node *rb_node;
- while (*p != NULL) {
- parent = *p;
- m = rb_entry(parent, struct map, rb_node);
- if (ip < m->start)
- p = &(*p)->rb_left;
- else
- p = &(*p)->rb_right;
+ maps__for_each_entry(maps, rb_node) {
+ if (rb_node->RC_CHK_ACCESS(map) == RC_CHK_ACCESS(map))
+ return rb_node;
}
-
- rb_link_node(&map->rb_node, parent, p);
- rb_insert_color(&map->rb_node, &maps->entries);
- map__get(map);
+ return NULL;
}
struct map *maps__find(struct maps *maps, u64 ip)
{
struct rb_node *p;
- struct map *m;
+ struct map_rb_node *m;
+
- down_read(&maps->lock);
+ down_read(maps__lock(maps));
- p = maps->entries.rb_node;
+ p = maps__entries(maps)->rb_node;
while (p != NULL) {
- m = rb_entry(p, struct map, rb_node);
- if (ip < m->start)
+ m = rb_entry(p, struct map_rb_node, rb_node);
+ if (ip < map__start(m->map))
p = p->rb_left;
- else if (ip >= m->end)
+ else if (ip >= map__end(m->map))
p = p->rb_right;
else
goto out;
@@ -389,15 +447,30 @@ struct map *maps__find(struct maps *maps, u64 ip)
m = NULL;
out:
- up_read(&maps->lock);
- return m;
+ up_read(maps__lock(maps));
+ return m ? m->map : NULL;
}
-struct map *maps__first(struct maps *maps)
+struct map_rb_node *maps__first(struct maps *maps)
{
- struct rb_node *first = rb_first(&maps->entries);
+ struct rb_node *first = rb_first(maps__entries(maps));
if (first)
- return rb_entry(first, struct map, rb_node);
+ return rb_entry(first, struct map_rb_node, rb_node);
return NULL;
}
+
+struct map_rb_node *map_rb_node__next(struct map_rb_node *node)
+{
+ struct rb_node *next;
+
+ if (!node)
+ return NULL;
+
+ next = rb_next(&node->rb_node);
+
+ if (!next)
+ return NULL;
+
+ return rb_entry(next, struct map_rb_node, rb_node);
+}
diff --git a/tools/perf/util/maps.h b/tools/perf/util/maps.h
index 7e729ff42749..d2963456cfbe 100644
--- a/tools/perf/util/maps.h
+++ b/tools/perf/util/maps.h
@@ -8,6 +8,7 @@
#include <stdbool.h>
#include <linux/types.h>
#include "rwsem.h"
+#include <internal/rc_check.h>
struct ref_reloc_sym;
struct machine;
@@ -15,17 +16,24 @@ struct map;
struct maps;
struct thread;
+struct map_rb_node {
+ struct rb_node rb_node;
+ struct map *map;
+};
+
+struct map_rb_node *maps__first(struct maps *maps);
+struct map_rb_node *map_rb_node__next(struct map_rb_node *node);
+struct map_rb_node *maps__find_node(struct maps *maps, struct map *map);
struct map *maps__find(struct maps *maps, u64 addr);
-struct map *maps__first(struct maps *maps);
-struct map *map__next(struct map *map);
#define maps__for_each_entry(maps, map) \
- for (map = maps__first(maps); map; map = map__next(map))
+ for (map = maps__first(maps); map; map = map_rb_node__next(map))
#define maps__for_each_entry_safe(maps, map, next) \
- for (map = maps__first(maps), next = map__next(map); map; map = next, next = map__next(map))
+ for (map = maps__first(maps), next = map_rb_node__next(map); map; \
+ map = next, next = map_rb_node__next(map))
-struct maps {
+DECLARE_RC_STRUCT(maps) {
struct rb_root entries;
struct rw_semaphore lock;
struct machine *machine;
@@ -36,7 +44,7 @@ struct maps {
unsigned int nr_maps_allocated;
#ifdef HAVE_LIBUNWIND_SUPPORT
void *addr_space;
- struct unwind_libunwind_ops *unwind_libunwind_ops;
+ const struct unwind_libunwind_ops *unwind_libunwind_ops;
#endif
};
@@ -51,20 +59,56 @@ struct kmap {
struct maps *maps__new(struct machine *machine);
void maps__delete(struct maps *maps);
bool maps__empty(struct maps *maps);
+int maps__clone(struct thread *thread, struct maps *parent);
+
+struct maps *maps__get(struct maps *maps);
+void maps__put(struct maps *maps);
-static inline struct maps *maps__get(struct maps *maps)
+static inline struct rb_root *maps__entries(struct maps *maps)
{
- if (maps)
- refcount_inc(&maps->refcnt);
- return maps;
+ return &RC_CHK_ACCESS(maps)->entries;
}
-void maps__put(struct maps *maps);
-int maps__clone(struct thread *thread, struct maps *parent);
-size_t maps__fprintf(struct maps *maps, FILE *fp);
+static inline struct machine *maps__machine(struct maps *maps)
+{
+ return RC_CHK_ACCESS(maps)->machine;
+}
+
+static inline struct rw_semaphore *maps__lock(struct maps *maps)
+{
+ return &RC_CHK_ACCESS(maps)->lock;
+}
+
+static inline struct map **maps__maps_by_name(struct maps *maps)
+{
+ return RC_CHK_ACCESS(maps)->maps_by_name;
+}
-void maps__insert(struct maps *maps, struct map *map);
+static inline unsigned int maps__nr_maps(const struct maps *maps)
+{
+ return RC_CHK_ACCESS(maps)->nr_maps;
+}
+
+static inline refcount_t *maps__refcnt(struct maps *maps)
+{
+ return &RC_CHK_ACCESS(maps)->refcnt;
+}
+
+#ifdef HAVE_LIBUNWIND_SUPPORT
+static inline void *maps__addr_space(struct maps *maps)
+{
+ return RC_CHK_ACCESS(maps)->addr_space;
+}
+
+static inline const struct unwind_libunwind_ops *maps__unwind_libunwind_ops(const struct maps *maps)
+{
+ return RC_CHK_ACCESS(maps)->unwind_libunwind_ops;
+}
+#endif
+
+size_t maps__fprintf(struct maps *maps, FILE *fp);
+int maps__insert(struct maps *maps, struct map *map);
void maps__remove(struct maps *maps, struct map *map);
struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp);
diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c
index b3a91093069a..ed1ee4b05356 100644
--- a/tools/perf/util/mem-events.c
+++ b/tools/perf/util/mem-events.c
@@ -295,6 +295,7 @@ static const char * const mem_lvl[] = {
};
static const char * const mem_lvlnum[] = {
+ [PERF_MEM_LVLNUM_UNC] = "Uncached",
[PERF_MEM_LVLNUM_CXL] = "CXL",
[PERF_MEM_LVLNUM_IO] = "I/O",
[PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
@@ -343,66 +344,71 @@ static int perf_mem__op_scnprintf(char *out, size_t sz, struct mem_info *mem_inf
int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
{
- size_t i, l = 0;
- u64 m = PERF_MEM_LVL_NA;
- u64 hit, miss;
+ union perf_mem_data_src data_src;
int printed = 0;
-
- if (mem_info)
- m = mem_info->data_src.mem_lvl;
+ size_t l = 0;
+ size_t i;
+ int lvl;
+ char hit_miss[5] = {0};
sz -= 1; /* -1 for null termination */
out[0] = '\0';
- hit = m & PERF_MEM_LVL_HIT;
- miss = m & PERF_MEM_LVL_MISS;
+ if (!mem_info)
+ goto na;
- /* already taken care of */
- m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
+ data_src = mem_info->data_src;
- if (mem_info && mem_info->data_src.mem_remote) {
- strcat(out, "Remote ");
- l += 7;
- }
+ if (data_src.mem_lvl & PERF_MEM_LVL_HIT)
+ memcpy(hit_miss, "hit", 3);
+ else if (data_src.mem_lvl & PERF_MEM_LVL_MISS)
+ memcpy(hit_miss, "miss", 4);
- /*
- * Incase mem_hops field is set, we can skip printing data source via
- * PERF_MEM_LVL namespace.
- */
- if (mem_info && mem_info->data_src.mem_hops) {
- l += scnprintf(out + l, sz - l, "%s ", mem_hops[mem_info->data_src.mem_hops]);
- } else {
- for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
- if (!(m & 0x1))
- continue;
- if (printed++) {
- strcat(out, " or ");
- l += 4;
- }
- l += scnprintf(out + l, sz - l, mem_lvl[i]);
+ lvl = data_src.mem_lvl_num;
+ if (lvl && lvl != PERF_MEM_LVLNUM_NA) {
+ if (data_src.mem_remote) {
+ strcat(out, "Remote ");
+ l += 7;
}
+
+ if (data_src.mem_hops)
+ l += scnprintf(out + l, sz - l, "%s ", mem_hops[data_src.mem_hops]);
+
+ if (mem_lvlnum[lvl])
+ l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
+ else
+ l += scnprintf(out + l, sz - l, "L%d", lvl);
+
+ l += scnprintf(out + l, sz - l, " %s", hit_miss);
+ return l;
}
- if (mem_info && mem_info->data_src.mem_lvl_num) {
- int lvl = mem_info->data_src.mem_lvl_num;
+ lvl = data_src.mem_lvl;
+ if (!lvl)
+ goto na;
+
+ lvl &= ~(PERF_MEM_LVL_NA | PERF_MEM_LVL_HIT | PERF_MEM_LVL_MISS);
+ if (!lvl)
+ goto na;
+
+ for (i = 0; lvl && i < ARRAY_SIZE(mem_lvl); i++, lvl >>= 1) {
+ if (!(lvl & 0x1))
+ continue;
if (printed++) {
strcat(out, " or ");
l += 4;
}
- if (mem_lvlnum[lvl])
- l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
- else
- l += scnprintf(out + l, sz - l, "L%d", lvl);
+ l += scnprintf(out + l, sz - l, mem_lvl[i]);
}
- if (l == 0)
- l += scnprintf(out + l, sz - l, "N/A");
- if (hit)
- l += scnprintf(out + l, sz - l, " hit");
- if (miss)
- l += scnprintf(out + l, sz - l, " miss");
+ if (printed) {
+ l += scnprintf(out + l, sz - l, " %s", hit_miss);
+ return l;
+ }
- return l;
+na:
+ strcat(out, "N/A");
+ return 3;
}
static const char * const snoop_access[] = {
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index f3559be95541..c566c6859302 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -13,6 +13,7 @@
#include "pmu.h"
#include "pmu-hybrid.h"
#include "print-events.h"
+#include "smt.h"
#include "expr.h"
#include "rblist.h"
#include <string.h>
@@ -89,9 +90,9 @@ static void metric_event_delete(struct rblist *rblist __maybe_unused,
struct metric_expr *expr, *tmp;
list_for_each_entry_safe(expr, tmp, &me->head, nd) {
- free((char *)expr->metric_name);
- free(expr->metric_refs);
- free(expr->metric_events);
+ zfree(&expr->metric_name);
+ zfree(&expr->metric_refs);
+ zfree(&expr->metric_events);
free(expr);
}
@@ -128,6 +129,8 @@ struct metric {
const char *modifier;
/** The expression to parse, for example, "instructions/cycles". */
const char *metric_expr;
+ /** Optional threshold expression where zero value is green, otherwise red. */
+ const char *metric_threshold;
/**
* The "ScaleUnit" that scales and adds a unit to the metric during
* output.
@@ -136,10 +139,9 @@ struct metric {
/** Optional null terminated array of referenced metrics. */
struct metric_ref *metric_refs;
/**
- * Is there a constraint on the group of events? In which case the
- * events won't be grouped.
+ * Should events of the metric be grouped?
*/
- bool has_constraint;
+ bool group_events;
/**
* Parsed events for the metric. Optional as events may be taken from a
* different metric whose group contains all the IDs necessary for this
@@ -148,12 +150,12 @@ struct metric {
struct evlist *evlist;
};
-static void metricgroup___watchdog_constraint_hint(const char *name, bool foot)
+static void metric__watchdog_constraint_hint(const char *name, bool foot)
{
static bool violate_nmi_constraint;
if (!foot) {
- pr_warning("Splitting metric group %s into standalone metrics.\n", name);
+ pr_warning("Not grouping metric %s's events.\n", name);
violate_nmi_constraint = true;
return;
}
@@ -167,18 +169,22 @@ static void metricgroup___watchdog_constraint_hint(const char *name, bool foot)
" echo 1 > /proc/sys/kernel/nmi_watchdog\n");
}
-static bool metricgroup__has_constraint(const struct pmu_metric *pm)
+static bool metric__group_events(const struct pmu_metric *pm)
{
- if (!pm->metric_constraint)
+ switch (pm->event_grouping) {
+ case MetricNoGroupEvents:
return false;
-
- if (!strcmp(pm->metric_constraint, "NO_NMI_WATCHDOG") &&
- sysctl__nmi_watchdog_enabled()) {
- metricgroup___watchdog_constraint_hint(pm->metric_name, false);
+ case MetricNoGroupEventsNmi:
+ if (!sysctl__nmi_watchdog_enabled())
+ return true;
+ metric__watchdog_constraint_hint(pm->metric_name, /*foot=*/false);
+ return false;
+ case MetricNoGroupEventsSmt:
+ return !smt_on();
+ case MetricGroupEvents:
+ default:
return true;
}
-
- return false;
}
static void metric__free(struct metric *m)
@@ -186,9 +192,9 @@ static void metric__free(struct metric *m)
if (!m)
return;
- free(m->metric_refs);
+ zfree(&m->metric_refs);
expr__ctx_free(m->pctx);
- free((char *)m->modifier);
+ zfree(&m->modifier);
evlist__delete(m->evlist);
free(m);
}
@@ -218,6 +224,7 @@ static struct metric *metric__new(const struct pmu_metric *pm,
goto out_err;
}
m->metric_expr = pm->metric_expr;
+ m->metric_threshold = pm->metric_threshold;
m->metric_unit = pm->unit;
m->pctx->sctx.user_requested_cpu_list = NULL;
if (user_requested_cpu_list) {
@@ -227,7 +234,7 @@ static struct metric *metric__new(const struct pmu_metric *pm,
}
m->pctx->sctx.runtime = runtime;
m->pctx->sctx.system_wide = system_wide;
- m->has_constraint = metric_no_group || metricgroup__has_constraint(pm);
+ m->group_events = !metric_no_group && metric__group_events(pm);
m->metric_refs = NULL;
m->evlist = NULL;
@@ -364,6 +371,7 @@ struct mep {
const char *metric_desc;
const char *metric_long_desc;
const char *metric_expr;
+ const char *metric_threshold;
const char *metric_unit;
};
@@ -443,6 +451,7 @@ static int metricgroup__add_to_mep_groups(const struct pmu_metric *pm,
me->metric_desc = pm->desc;
me->metric_long_desc = pm->long_desc;
me->metric_expr = pm->metric_expr;
+ me->metric_threshold = pm->metric_threshold;
me->metric_unit = pm->unit;
}
}
@@ -518,6 +527,7 @@ void metricgroup__print(const struct print_callbacks *print_cb, void *print_stat
me->metric_desc,
me->metric_long_desc,
me->metric_expr,
+ me->metric_threshold,
me->metric_unit);
next = rb_next(node);
rblist__remove_node(&groups, node);
@@ -607,7 +617,7 @@ static int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifie
if (strstr(ev->name, "metric-id=")) {
bool has_slash = false;
- free(ev->name);
+ zfree(&ev->name);
for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) {
*cur = '/';
has_slash = true;
@@ -637,7 +647,7 @@ static int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifie
static int metricgroup__build_event_string(struct strbuf *events,
const struct expr_parse_ctx *ctx,
const char *modifier,
- bool has_constraint)
+ bool group_events)
{
struct hashmap_entry *cur;
size_t bkt;
@@ -662,7 +672,7 @@ static int metricgroup__build_event_string(struct strbuf *events,
}
/* Separate events with commas and open the group if necessary. */
if (no_group) {
- if (!has_constraint) {
+ if (group_events) {
ret = strbuf_addch(events, '{');
RETURN_IF_NON_ZERO(ret);
}
@@ -716,7 +726,7 @@ static int metricgroup__build_event_string(struct strbuf *events,
RETURN_IF_NON_ZERO(ret);
}
}
- if (!no_group && !has_constraint) {
+ if (!no_group && group_events) {
ret = strbuf_addf(events, "}:W");
RETURN_IF_NON_ZERO(ret);
}
@@ -761,6 +771,7 @@ struct metricgroup_add_iter_data {
int *ret;
bool *has_match;
bool metric_no_group;
+ bool metric_no_threshold;
const char *user_requested_cpu_list;
bool system_wide;
struct metric *root_metric;
@@ -776,6 +787,7 @@ static int add_metric(struct list_head *metric_list,
const struct pmu_metric *pm,
const char *modifier,
bool metric_no_group,
+ bool metric_no_threshold,
const char *user_requested_cpu_list,
bool system_wide,
struct metric *root_metric,
@@ -803,6 +815,7 @@ static int add_metric(struct list_head *metric_list,
static int resolve_metric(struct list_head *metric_list,
const char *modifier,
bool metric_no_group,
+ bool metric_no_threshold,
const char *user_requested_cpu_list,
bool system_wide,
struct metric *root_metric,
@@ -851,8 +864,8 @@ static int resolve_metric(struct list_head *metric_list,
*/
for (i = 0; i < pending_cnt; i++) {
ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group,
- user_requested_cpu_list, system_wide, root_metric, visited,
- table);
+ metric_no_threshold, user_requested_cpu_list, system_wide,
+ root_metric, visited, table);
if (ret)
break;
}
@@ -869,6 +882,7 @@ static int resolve_metric(struct list_head *metric_list,
* @metric_no_group: Should events written to events be grouped "{}" or
* global. Grouping is the default but due to multiplexing the
* user may override.
+ * @metric_no_threshold: Should threshold expressions be ignored?
* @runtime: A special argument for the parser only known at runtime.
* @user_requested_cpu_list: Command line specified CPUs to record on.
* @system_wide: Are events for all processes recorded.
@@ -884,6 +898,7 @@ static int __add_metric(struct list_head *metric_list,
const struct pmu_metric *pm,
const char *modifier,
bool metric_no_group,
+ bool metric_no_threshold,
int runtime,
const char *user_requested_cpu_list,
bool system_wide,
@@ -894,6 +909,7 @@ static int __add_metric(struct list_head *metric_list,
const struct visited_metric *vm;
int ret;
bool is_root = !root_metric;
+ const char *expr;
struct visited_metric visited_node = {
.name = pm->metric_name,
.parent = visited,
@@ -956,16 +972,31 @@ static int __add_metric(struct list_head *metric_list,
* For both the parent and referenced metrics, we parse
* all the metric's IDs and add it to the root context.
*/
- if (expr__find_ids(pm->metric_expr, NULL, root_metric->pctx) < 0) {
+ ret = 0;
+ expr = pm->metric_expr;
+ if (is_root && pm->metric_threshold) {
+ /*
+ * Threshold expressions are built off the actual metric. Switch
+ * to use that in case of additional necessary events. Change
+ * the visited node name to avoid this being flagged as
+ * recursion. If the threshold events are disabled, just use the
+ * metric's name as a reference. This allows metric threshold
+ * computation if there are sufficient events.
+ */
+ assert(strstr(pm->metric_threshold, pm->metric_name));
+ expr = metric_no_threshold ? pm->metric_name : pm->metric_threshold;
+ visited_node.name = "__threshold__";
+ }
+ if (expr__find_ids(expr, NULL, root_metric->pctx) < 0) {
/* Broken metric. */
ret = -EINVAL;
- } else {
+ }
+ if (!ret) {
/* Resolve referenced metrics. */
ret = resolve_metric(metric_list, modifier, metric_no_group,
- user_requested_cpu_list, system_wide,
- root_metric, &visited_node, table);
+ metric_no_threshold, user_requested_cpu_list,
+ system_wide, root_metric, &visited_node, table);
}
-
if (ret) {
if (is_root)
metric__free(root_metric);
@@ -1011,6 +1042,7 @@ static int add_metric(struct list_head *metric_list,
const struct pmu_metric *pm,
const char *modifier,
bool metric_no_group,
+ bool metric_no_threshold,
const char *user_requested_cpu_list,
bool system_wide,
struct metric *root_metric,
@@ -1022,9 +1054,9 @@ static int add_metric(struct list_head *metric_list,
pr_debug("metric expr %s for %s\n", pm->metric_expr, pm->metric_name);
if (!strstr(pm->metric_expr, "?")) {
- ret = __add_metric(metric_list, pm, modifier, metric_no_group, 0,
- user_requested_cpu_list, system_wide, root_metric,
- visited, table);
+ ret = __add_metric(metric_list, pm, modifier, metric_no_group,
+ metric_no_threshold, 0, user_requested_cpu_list,
+ system_wide, root_metric, visited, table);
} else {
int j, count;
@@ -1036,9 +1068,9 @@ static int add_metric(struct list_head *metric_list,
*/
for (j = 0; j < count && !ret; j++)
- ret = __add_metric(metric_list, pm, modifier, metric_no_group, j,
- user_requested_cpu_list, system_wide,
- root_metric, visited, table);
+ ret = __add_metric(metric_list, pm, modifier, metric_no_group,
+ metric_no_threshold, j, user_requested_cpu_list,
+ system_wide, root_metric, visited, table);
}
return ret;
@@ -1055,8 +1087,8 @@ static int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm,
return 0;
ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group,
- d->user_requested_cpu_list, d->system_wide,
- d->root_metric, d->visited, d->table);
+ d->metric_no_threshold, d->user_requested_cpu_list,
+ d->system_wide, d->root_metric, d->visited, d->table);
if (ret)
goto out;
@@ -1100,6 +1132,7 @@ struct metricgroup__add_metric_data {
const char *modifier;
const char *user_requested_cpu_list;
bool metric_no_group;
+ bool metric_no_threshold;
bool system_wide;
bool has_match;
};
@@ -1117,8 +1150,9 @@ static int metricgroup__add_metric_callback(const struct pmu_metric *pm,
data->has_match = true;
ret = add_metric(data->list, pm, data->modifier, data->metric_no_group,
- data->user_requested_cpu_list, data->system_wide,
- /*root_metric=*/NULL, /*visited_metrics=*/NULL, table);
+ data->metric_no_threshold, data->user_requested_cpu_list,
+ data->system_wide, /*root_metric=*/NULL,
+ /*visited_metrics=*/NULL, table);
}
return ret;
}
@@ -1139,7 +1173,7 @@ static int metricgroup__add_metric_callback(const struct pmu_metric *pm,
* architecture perf is running upon.
*/
static int metricgroup__add_metric(const char *metric_name, const char *modifier,
- bool metric_no_group,
+ bool metric_no_group, bool metric_no_threshold,
const char *user_requested_cpu_list,
bool system_wide,
struct list_head *metric_list,
@@ -1155,6 +1189,7 @@ static int metricgroup__add_metric(const char *metric_name, const char *modifier
.metric_name = metric_name,
.modifier = modifier,
.metric_no_group = metric_no_group,
+ .metric_no_threshold = metric_no_threshold,
.user_requested_cpu_list = user_requested_cpu_list,
.system_wide = system_wide,
.has_match = false,
@@ -1217,6 +1252,7 @@ out:
* architecture perf is running upon.
*/
static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
+ bool metric_no_threshold,
const char *user_requested_cpu_list,
bool system_wide, struct list_head *metric_list,
const struct pmu_metrics_table *table)
@@ -1235,7 +1271,8 @@ static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
*modifier++ = '\0';
ret = metricgroup__add_metric(metric_name, modifier,
- metric_no_group, user_requested_cpu_list,
+ metric_no_group, metric_no_threshold,
+ user_requested_cpu_list,
system_wide, metric_list, table);
if (ret == -EINVAL)
pr_err("Cannot find metric or group `%s'\n", metric_name);
@@ -1252,7 +1289,7 @@ static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
* Warn about nmi_watchdog if any parsed metrics had the
* NO_NMI_WATCHDOG constraint.
*/
- metricgroup___watchdog_constraint_hint(NULL, true);
+ metric__watchdog_constraint_hint(NULL, /*foot=*/true);
/* No metrics. */
if (count == 0)
return -EINVAL;
@@ -1295,7 +1332,7 @@ static void find_tool_events(const struct list_head *metric_list,
}
/**
- * build_combined_expr_ctx - Make an expr_parse_ctx with all has_constraint
+ * build_combined_expr_ctx - Make an expr_parse_ctx with all !group_events
* metric IDs, as the IDs are held in a set,
* duplicates will be removed.
* @metric_list: List to take metrics from.
@@ -1315,7 +1352,7 @@ static int build_combined_expr_ctx(const struct list_head *metric_list,
return -ENOMEM;
list_for_each_entry(m, metric_list, nd) {
- if (m->has_constraint && !m->modifier) {
+ if (!m->group_events && !m->modifier) {
hashmap__for_each_entry(m->pctx->ids, cur, bkt) {
dup = strdup(cur->pkey);
if (!dup) {
@@ -1342,14 +1379,14 @@ err_out:
* @fake_pmu: used when testing metrics not supported by the current CPU.
* @ids: the event identifiers parsed from a metric.
* @modifier: any modifiers added to the events.
- * @has_constraint: false if events should be placed in a weak group.
+ * @group_events: should events be placed in a weak group.
* @tool_events: entries set true if the tool event of index could be present in
* the overall list of metrics.
* @out_evlist: the created list of events.
*/
static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu,
struct expr_parse_ctx *ids, const char *modifier,
- bool has_constraint, const bool tool_events[PERF_TOOL_MAX],
+ bool group_events, const bool tool_events[PERF_TOOL_MAX],
struct evlist **out_evlist)
{
struct parse_events_error parse_error;
@@ -1393,7 +1430,7 @@ static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu,
}
}
ret = metricgroup__build_event_string(&events, ids, modifier,
- has_constraint);
+ group_events);
if (ret)
return ret;
@@ -1404,7 +1441,8 @@ static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu,
}
pr_debug("Parsing metric events '%s'\n", events.buf);
parse_events_error__init(&parse_error);
- ret = __parse_events(parsed_evlist, events.buf, &parse_error, fake_pmu);
+ ret = __parse_events(parsed_evlist, events.buf, &parse_error, fake_pmu,
+ /*warn_if_reordered=*/false);
if (ret) {
parse_events_error__print(&parse_error, events.buf);
goto err_out;
@@ -1425,6 +1463,7 @@ err_out:
static int parse_groups(struct evlist *perf_evlist, const char *str,
bool metric_no_group,
bool metric_no_merge,
+ bool metric_no_threshold,
const char *user_requested_cpu_list,
bool system_wide,
struct perf_pmu *fake_pmu,
@@ -1439,7 +1478,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str,
if (metric_events_list->nr_entries == 0)
metricgroup__rblist_init(metric_events_list);
- ret = metricgroup__add_metric_list(str, metric_no_group,
+ ret = metricgroup__add_metric_list(str, metric_no_group, metric_no_threshold,
user_requested_cpu_list,
system_wide, &metric_list, table);
if (ret)
@@ -1458,7 +1497,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str,
if (!ret && combined && hashmap__size(combined->ids)) {
ret = parse_ids(metric_no_merge, fake_pmu, combined,
/*modifier=*/NULL,
- /*has_constraint=*/true,
+ /*group_events=*/false,
tool_events,
&combined_evlist);
}
@@ -1476,7 +1515,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str,
struct metric *n;
struct metric_expr *expr;
- if (combined_evlist && m->has_constraint) {
+ if (combined_evlist && !m->group_events) {
metric_evlist = combined_evlist;
} else if (!metric_no_merge) {
/*
@@ -1507,7 +1546,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str,
}
if (!metric_evlist) {
ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier,
- m->has_constraint, tool_events, &m->evlist);
+ m->group_events, tool_events, &m->evlist);
if (ret)
goto out;
@@ -1547,6 +1586,7 @@ static int parse_groups(struct evlist *perf_evlist, const char *str,
free(metric_events);
goto out;
}
+ expr->metric_threshold = m->metric_threshold;
expr->metric_unit = m->metric_unit;
expr->metric_events = metric_events;
expr->runtime = m->pctx->sctx.runtime;
@@ -1573,6 +1613,7 @@ int metricgroup__parse_groups(struct evlist *perf_evlist,
const char *str,
bool metric_no_group,
bool metric_no_merge,
+ bool metric_no_threshold,
const char *user_requested_cpu_list,
bool system_wide,
struct rblist *metric_events)
@@ -1583,18 +1624,19 @@ int metricgroup__parse_groups(struct evlist *perf_evlist,
return -EINVAL;
return parse_groups(perf_evlist, str, metric_no_group, metric_no_merge,
- user_requested_cpu_list, system_wide,
+ metric_no_threshold, user_requested_cpu_list, system_wide,
/*fake_pmu=*/NULL, metric_events, table);
}
int metricgroup__parse_groups_test(struct evlist *evlist,
const struct pmu_metrics_table *table,
const char *str,
- bool metric_no_group,
- bool metric_no_merge,
struct rblist *metric_events)
{
- return parse_groups(evlist, str, metric_no_group, metric_no_merge,
+ return parse_groups(evlist, str,
+ /*metric_no_group=*/false,
+ /*metric_no_merge=*/false,
+ /*metric_no_threshold=*/false,
/*user_requested_cpu_list=*/NULL,
/*system_wide=*/false,
&perf_pmu__fake, metric_events, table);
@@ -1606,10 +1648,8 @@ static int metricgroup__has_metric_callback(const struct pmu_metric *pm,
{
const char *metric = vdata;
- if (!pm->metric_expr)
- return 0;
-
- if (match_metric(pm->metric_name, metric))
+ if (match_metric(pm->metric_name, metric) ||
+ match_metric(pm->metric_group, metric))
return 1;
return 0;
@@ -1626,6 +1666,37 @@ bool metricgroup__has_metric(const char *metric)
(void *)metric) ? true : false;
}
+static int metricgroup__topdown_max_level_callback(const struct pmu_metric *pm,
+ const struct pmu_metrics_table *table __maybe_unused,
+ void *data)
+{
+ unsigned int *max_level = data;
+ unsigned int level;
+ const char *p = strstr(pm->metric_group, "TopdownL");
+
+ if (!p || p[8] == '\0')
+ return 0;
+
+ level = p[8] - '0';
+ if (level > *max_level)
+ *max_level = level;
+
+ return 0;
+}
+
+unsigned int metricgroups__topdown_max_level(void)
+{
+ unsigned int max_level = 0;
+ const struct pmu_metrics_table *table = pmu_metrics_table__find();
+
+ if (!table)
+ return false;
+
+ pmu_metrics_table_for_each_metric(table, metricgroup__topdown_max_level_callback,
+ &max_level);
+ return max_level;
+}
+
int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
struct rblist *new_metric_events,
struct rblist *old_metric_events)
@@ -1659,6 +1730,7 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
return -ENOMEM;
new_expr->metric_expr = old_expr->metric_expr;
+ new_expr->metric_threshold = old_expr->metric_threshold;
new_expr->metric_name = strdup(old_expr->metric_name);
if (!new_expr->metric_name)
return -ENOMEM;
@@ -1689,7 +1761,7 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
alloc_size = sizeof(*new_expr->metric_events);
new_expr->metric_events = calloc(nr + 1, alloc_size);
if (!new_expr->metric_events) {
- free(new_expr->metric_refs);
+ zfree(&new_expr->metric_refs);
free(new_expr);
return -ENOMEM;
}
@@ -1699,8 +1771,8 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
evsel = old_expr->metric_events[idx];
evsel = evlist__find_evsel(evlist, evsel->core.idx);
if (evsel == NULL) {
- free(new_expr->metric_events);
- free(new_expr->metric_refs);
+ zfree(&new_expr->metric_events);
+ zfree(&new_expr->metric_refs);
free(new_expr);
return -EINVAL;
}
diff --git a/tools/perf/util/metricgroup.h b/tools/perf/util/metricgroup.h
index 84030321a057..77472e35705e 100644
--- a/tools/perf/util/metricgroup.h
+++ b/tools/perf/util/metricgroup.h
@@ -47,6 +47,7 @@ struct metric_expr {
const char *metric_expr;
/** The name of the meric such as "IPC". */
const char *metric_name;
+ const char *metric_threshold;
/**
* The "ScaleUnit" that scales and adds a unit to the metric during
* output. For example, "6.4e-05MiB" means to scale the resulting metric
@@ -69,18 +70,18 @@ int metricgroup__parse_groups(struct evlist *perf_evlist,
const char *str,
bool metric_no_group,
bool metric_no_merge,
+ bool metric_no_threshold,
const char *user_requested_cpu_list,
bool system_wide,
struct rblist *metric_events);
int metricgroup__parse_groups_test(struct evlist *evlist,
const struct pmu_metrics_table *table,
const char *str,
- bool metric_no_group,
- bool metric_no_merge,
struct rblist *metric_events);
void metricgroup__print(const struct print_callbacks *print_cb, void *print_state);
bool metricgroup__has_metric(const char *metric);
+unsigned int metricgroups__topdown_max_level(void);
int arch_get_runtimeparam(const struct pmu_metric *pm);
void metricgroup__rblist_exit(struct rblist *metric_events);
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index dd536220cdb9..cb185c5659d6 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -60,7 +60,7 @@ void namespaces__free(struct namespaces *namespaces)
free(namespaces);
}
-static int nsinfo__get_nspid(struct nsinfo *nsi, const char *path)
+static int nsinfo__get_nspid(pid_t *tgid, pid_t *nstgid, bool *in_pidns, const char *path)
{
FILE *f = NULL;
char *statln = NULL;
@@ -74,19 +74,18 @@ static int nsinfo__get_nspid(struct nsinfo *nsi, const char *path)
while (getline(&statln, &linesz, f) != -1) {
/* Use tgid if CONFIG_PID_NS is not defined. */
if (strstr(statln, "Tgid:") != NULL) {
- nsi->tgid = (pid_t)strtol(strrchr(statln, '\t'),
- NULL, 10);
- nsi->nstgid = nsinfo__tgid(nsi);
+ *tgid = (pid_t)strtol(strrchr(statln, '\t'), NULL, 10);
+ *nstgid = *tgid;
}
if (strstr(statln, "NStgid:") != NULL) {
nspid = strrchr(statln, '\t');
- nsi->nstgid = (pid_t)strtol(nspid, NULL, 10);
+ *nstgid = (pid_t)strtol(nspid, NULL, 10);
/*
* If innermost tgid is not the first, process is in a different
* PID namespace.
*/
- nsi->in_pidns = (statln + sizeof("NStgid:") - 1) != nspid;
+ *in_pidns = (statln + sizeof("NStgid:") - 1) != nspid;
break;
}
}
@@ -121,8 +120,8 @@ int nsinfo__init(struct nsinfo *nsi)
* want to switch as part of looking up dso/map data.
*/
if (old_stat.st_ino != new_stat.st_ino) {
- nsi->need_setns = true;
- nsi->mntns_path = newns;
+ RC_CHK_ACCESS(nsi)->need_setns = true;
+ RC_CHK_ACCESS(nsi)->mntns_path = newns;
newns = NULL;
}
@@ -132,13 +131,26 @@ int nsinfo__init(struct nsinfo *nsi)
if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsinfo__pid(nsi)) >= PATH_MAX)
goto out;
- rv = nsinfo__get_nspid(nsi, spath);
+ rv = nsinfo__get_nspid(&RC_CHK_ACCESS(nsi)->tgid, &RC_CHK_ACCESS(nsi)->nstgid,
+ &RC_CHK_ACCESS(nsi)->in_pidns, spath);
out:
free(newns);
return rv;
}
+static struct nsinfo *nsinfo__alloc(void)
+{
+ struct nsinfo *res;
+ RC_STRUCT(nsinfo) *nsi;
+
+ nsi = calloc(1, sizeof(*nsi));
+ if (ADD_RC_CHK(res, nsi))
+ refcount_set(&nsi->refcnt, 1);
+
+ return res;
+}
+
struct nsinfo *nsinfo__new(pid_t pid)
{
struct nsinfo *nsi;
@@ -146,26 +158,30 @@ struct nsinfo *nsinfo__new(pid_t pid)
if (pid == 0)
return NULL;
- nsi = calloc(1, sizeof(*nsi));
- if (nsi != NULL) {
- nsi->pid = pid;
- nsi->tgid = pid;
- nsi->nstgid = pid;
- nsi->need_setns = false;
- nsi->in_pidns = false;
- /* Init may fail if the process exits while we're trying to look
- * at its proc information. In that case, save the pid but
- * don't try to enter the namespace.
- */
- if (nsinfo__init(nsi) == -1)
- nsi->need_setns = false;
+ nsi = nsinfo__alloc();
+ if (!nsi)
+ return NULL;
- refcount_set(&nsi->refcnt, 1);
- }
+ RC_CHK_ACCESS(nsi)->pid = pid;
+ RC_CHK_ACCESS(nsi)->tgid = pid;
+ RC_CHK_ACCESS(nsi)->nstgid = pid;
+ nsinfo__clear_need_setns(nsi);
+ RC_CHK_ACCESS(nsi)->in_pidns = false;
+ /* Init may fail if the process exits while we're trying to look at its
+ * proc information. In that case, save the pid but don't try to enter
+ * the namespace.
+ */
+ if (nsinfo__init(nsi) == -1)
+ nsinfo__clear_need_setns(nsi);
return nsi;
}
+static const char *nsinfo__mntns_path(const struct nsinfo *nsi)
+{
+ return RC_CHK_ACCESS(nsi)->mntns_path;
+}
+
struct nsinfo *nsinfo__copy(const struct nsinfo *nsi)
{
struct nsinfo *nnsi;
@@ -173,73 +189,86 @@ struct nsinfo *nsinfo__copy(const struct nsinfo *nsi)
if (nsi == NULL)
return NULL;
- nnsi = calloc(1, sizeof(*nnsi));
- if (nnsi != NULL) {
- nnsi->pid = nsinfo__pid(nsi);
- nnsi->tgid = nsinfo__tgid(nsi);
- nnsi->nstgid = nsinfo__nstgid(nsi);
- nnsi->need_setns = nsinfo__need_setns(nsi);
- nnsi->in_pidns = nsinfo__in_pidns(nsi);
- if (nsi->mntns_path) {
- nnsi->mntns_path = strdup(nsi->mntns_path);
- if (!nnsi->mntns_path) {
- free(nnsi);
- return NULL;
- }
+ nnsi = nsinfo__alloc();
+ if (!nnsi)
+ return NULL;
+
+ RC_CHK_ACCESS(nnsi)->pid = nsinfo__pid(nsi);
+ RC_CHK_ACCESS(nnsi)->tgid = nsinfo__tgid(nsi);
+ RC_CHK_ACCESS(nnsi)->nstgid = nsinfo__nstgid(nsi);
+ RC_CHK_ACCESS(nnsi)->need_setns = nsinfo__need_setns(nsi);
+ RC_CHK_ACCESS(nnsi)->in_pidns = nsinfo__in_pidns(nsi);
+ if (nsinfo__mntns_path(nsi)) {
+ RC_CHK_ACCESS(nnsi)->mntns_path = strdup(nsinfo__mntns_path(nsi));
+ if (!RC_CHK_ACCESS(nnsi)->mntns_path) {
+ nsinfo__put(nnsi);
+ return NULL;
}
- refcount_set(&nnsi->refcnt, 1);
}
return nnsi;
}
+static refcount_t *nsinfo__refcnt(struct nsinfo *nsi)
+{
+ return &RC_CHK_ACCESS(nsi)->refcnt;
+}
+
static void nsinfo__delete(struct nsinfo *nsi)
{
- zfree(&nsi->mntns_path);
- free(nsi);
+ if (nsi) {
+ WARN_ONCE(refcount_read(nsinfo__refcnt(nsi)) != 0, "nsinfo refcnt unbalanced\n");
+ zfree(&RC_CHK_ACCESS(nsi)->mntns_path);
+ RC_CHK_FREE(nsi);
+ }
}
struct nsinfo *nsinfo__get(struct nsinfo *nsi)
{
- if (nsi)
- refcount_inc(&nsi->refcnt);
- return nsi;
+ struct nsinfo *result;
+
+ if (RC_CHK_GET(result, nsi))
+ refcount_inc(nsinfo__refcnt(nsi));
+
+ return result;
}
void nsinfo__put(struct nsinfo *nsi)
{
- if (nsi && refcount_dec_and_test(&nsi->refcnt))
+ if (nsi && refcount_dec_and_test(nsinfo__refcnt(nsi)))
nsinfo__delete(nsi);
+ else
+ RC_CHK_PUT(nsi);
}
bool nsinfo__need_setns(const struct nsinfo *nsi)
{
- return nsi->need_setns;
+ return RC_CHK_ACCESS(nsi)->need_setns;
}
void nsinfo__clear_need_setns(struct nsinfo *nsi)
{
- nsi->need_setns = false;
+ RC_CHK_ACCESS(nsi)->need_setns = false;
}
pid_t nsinfo__tgid(const struct nsinfo *nsi)
{
- return nsi->tgid;
+ return RC_CHK_ACCESS(nsi)->tgid;
}
pid_t nsinfo__nstgid(const struct nsinfo *nsi)
{
- return nsi->nstgid;
+ return RC_CHK_ACCESS(nsi)->nstgid;
}
pid_t nsinfo__pid(const struct nsinfo *nsi)
{
- return nsi->pid;
+ return RC_CHK_ACCESS(nsi)->pid;
}
pid_t nsinfo__in_pidns(const struct nsinfo *nsi)
{
- return nsi->in_pidns;
+ return RC_CHK_ACCESS(nsi)->in_pidns;
}
void nsinfo__mountns_enter(struct nsinfo *nsi,
@@ -256,7 +285,7 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
nc->oldns = -1;
nc->newns = -1;
- if (!nsi || !nsi->need_setns)
+ if (!nsi || !nsinfo__need_setns(nsi))
return;
if (snprintf(curpath, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
@@ -270,7 +299,7 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
if (oldns < 0)
goto errout;
- newns = open(nsi->mntns_path, O_RDONLY);
+ newns = open(nsinfo__mntns_path(nsi), O_RDONLY);
if (newns < 0)
goto errout;
@@ -339,9 +368,9 @@ int nsinfo__stat(const char *filename, struct stat *st, struct nsinfo *nsi)
bool nsinfo__is_in_root_namespace(void)
{
- struct nsinfo nsi;
+ pid_t tgid = 0, nstgid = 0;
+ bool in_pidns = false;
- memset(&nsi, 0x0, sizeof(nsi));
- nsinfo__get_nspid(&nsi, "/proc/self/status");
- return !nsi.in_pidns;
+ nsinfo__get_nspid(&tgid, &nstgid, &in_pidns, "/proc/self/status");
+ return !in_pidns;
}
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index 567829262c42..8c0731c6cbb7 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -13,6 +13,7 @@
#include <linux/perf_event.h>
#include <linux/refcount.h>
#include <linux/types.h>
+#include <internal/rc_check.h>
#ifndef HAVE_SETNS_SUPPORT
int setns(int fd, int nstype);
@@ -29,7 +30,7 @@ struct namespaces {
struct namespaces *namespaces__new(struct perf_record_namespaces *event);
void namespaces__free(struct namespaces *namespaces);
-struct nsinfo {
+DECLARE_RC_STRUCT(nsinfo) {
pid_t pid;
pid_t tgid;
pid_t nstgid;
diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c
index b887dfeea673..8c62611f10aa 100644
--- a/tools/perf/util/ordered-events.c
+++ b/tools/perf/util/ordered-events.c
@@ -284,7 +284,7 @@ static int __ordered_events__flush(struct ordered_events *oe, enum oe_flush how,
switch (how) {
case OE_FLUSH__FINAL:
show_progress = true;
- __fallthrough;
+ fallthrough;
case OE_FLUSH__TOP:
oe->next_flush = ULLONG_MAX;
break;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 0336ff27c15f..d71019dcd614 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/hw_breakpoint.h>
#include <linux/err.h>
+#include <linux/list_sort.h>
#include <linux/zalloc.h>
#include <dirent.h>
#include <errno.h>
@@ -24,9 +25,10 @@
#include "util/parse-branch-options.h"
#include "util/evsel_config.h"
#include "util/event.h"
-#include "perf.h"
#include "util/parse-events-hybrid.h"
#include "util/pmu-hybrid.h"
+#include "util/bpf-filter.h"
+#include "util/util.h"
#include "tracepoint.h"
#include "thread_map.h"
@@ -947,6 +949,7 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = {
[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",
@@ -986,6 +989,7 @@ config_term_avail(int term_type, struct parse_events_error *err)
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_SAMPLE_PERIOD:
@@ -1031,6 +1035,10 @@ do { \
CHECK_TYPE_VAL(NUM);
attr->config2 = term->val.num;
break;
+ case PARSE_EVENTS__TERM_TYPE_CONFIG3:
+ CHECK_TYPE_VAL(NUM);
+ attr->config3 = term->val.num;
+ break;
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
CHECK_TYPE_VAL(NUM);
break;
@@ -1444,15 +1452,13 @@ static int parse_events__inside_hybrid_pmu(struct parse_events_state *parse_stat
int parse_events_add_pmu(struct parse_events_state *parse_state,
struct list_head *list, char *name,
struct list_head *head_config,
- bool auto_merge_stats,
- bool use_alias)
+ bool auto_merge_stats)
{
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;
- bool use_uncore_alias;
LIST_HEAD(config_terms);
pmu = parse_state->fake_pmu ?: perf_pmu__find(name);
@@ -1487,8 +1493,6 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
memset(&attr, 0, sizeof(attr));
}
- use_uncore_alias = (pmu->is_uncore && use_alias);
-
if (!head_config) {
attr.type = pmu->type;
evsel = __add_event(list, &parse_state->idx, &attr,
@@ -1498,7 +1502,6 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
/*cpu_list=*/NULL);
if (evsel) {
evsel->pmu_name = name ? strdup(name) : NULL;
- evsel->use_uncore_alias = use_uncore_alias;
return 0;
} else {
return -ENOMEM;
@@ -1559,7 +1562,6 @@ int parse_events_add_pmu(struct parse_events_state *parse_state,
evsel->use_config_name = true;
evsel->pmu_name = name ? strdup(name) : NULL;
- evsel->use_uncore_alias = use_uncore_alias;
evsel->percore = config_term_percore(&evsel->config_terms);
if (parse_state->fake_pmu)
@@ -1599,7 +1601,7 @@ 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, &config,
+ config, 1, false, NULL,
NULL) < 0) {
free(config);
goto out_err;
@@ -1621,7 +1623,7 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
parse_events_copy_term_list(head, &orig_head);
if (!parse_events_add_pmu(parse_state, list,
pmu->name, orig_head,
- true, true)) {
+ /*auto_merge_stats=*/true)) {
pr_debug("%s -> %s/%s/\n", str,
pmu->name, alias->str);
ok++;
@@ -1633,7 +1635,7 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state,
if (parse_state->fake_pmu) {
if (!parse_events_add_pmu(parse_state, list, str, head,
- true, true)) {
+ /*auto_merge_stats=*/true)) {
pr_debug("%s -> %s/%s/\n", str, "fake_pmu", str);
ok++;
}
@@ -1655,124 +1657,7 @@ int parse_events__modifier_group(struct list_head *list,
return parse_events__modifier_event(list, event_mod, true);
}
-/*
- * Check if the two uncore PMUs are from the same uncore block
- * The format of the uncore PMU name is uncore_#blockname_#pmuidx
- */
-static bool is_same_uncore_block(const char *pmu_name_a, const char *pmu_name_b)
-{
- char *end_a, *end_b;
-
- end_a = strrchr(pmu_name_a, '_');
- end_b = strrchr(pmu_name_b, '_');
-
- if (!end_a || !end_b)
- return false;
-
- if ((end_a - pmu_name_a) != (end_b - pmu_name_b))
- return false;
-
- return (strncmp(pmu_name_a, pmu_name_b, end_a - pmu_name_a) == 0);
-}
-
-static int
-parse_events__set_leader_for_uncore_aliase(char *name, struct list_head *list,
- struct parse_events_state *parse_state)
-{
- struct evsel *evsel, *leader;
- uintptr_t *leaders;
- bool is_leader = true;
- int i, nr_pmu = 0, total_members, ret = 0;
-
- leader = list_first_entry(list, struct evsel, core.node);
- evsel = list_last_entry(list, struct evsel, core.node);
- total_members = evsel->core.idx - leader->core.idx + 1;
-
- leaders = calloc(total_members, sizeof(uintptr_t));
- if (WARN_ON(!leaders))
- return 0;
-
- /*
- * Going through the whole group and doing sanity check.
- * All members must use alias, and be from the same uncore block.
- * Also, storing the leader events in an array.
- */
- __evlist__for_each_entry(list, evsel) {
-
- /* Only split the uncore group which members use alias */
- if (!evsel->use_uncore_alias)
- goto out;
-
- /* The events must be from the same uncore block */
- if (!is_same_uncore_block(leader->pmu_name, evsel->pmu_name))
- goto out;
-
- if (!is_leader)
- continue;
- /*
- * If the event's PMU name starts to repeat, it must be a new
- * event. That can be used to distinguish the leader from
- * other members, even they have the same event name.
- */
- if ((leader != evsel) &&
- !strcmp(leader->pmu_name, evsel->pmu_name)) {
- is_leader = false;
- continue;
- }
-
- /* Store the leader event for each PMU */
- leaders[nr_pmu++] = (uintptr_t) evsel;
- }
-
- /* only one event alias */
- if (nr_pmu == total_members) {
- parse_state->nr_groups--;
- goto handled;
- }
-
- /*
- * An uncore event alias is a joint name which means the same event
- * runs on all PMUs of a block.
- * Perf doesn't support mixed events from different PMUs in the same
- * group. The big group has to be split into multiple small groups
- * which only include the events from the same PMU.
- *
- * Here the uncore event aliases must be from the same uncore block.
- * The number of PMUs must be same for each alias. The number of new
- * small groups equals to the number of PMUs.
- * Setting the leader event for corresponding members in each group.
- */
- i = 0;
- __evlist__for_each_entry(list, evsel) {
- if (i >= nr_pmu)
- i = 0;
- evsel__set_leader(evsel, (struct evsel *) leaders[i++]);
- }
-
- /* The number of members and group name are same for each group */
- for (i = 0; i < nr_pmu; i++) {
- evsel = (struct evsel *) leaders[i];
- evsel->core.nr_members = total_members / nr_pmu;
- evsel->group_name = name ? strdup(name) : NULL;
- }
-
- /* Take the new small groups into account */
- parse_state->nr_groups += nr_pmu - 1;
-
-handled:
- ret = 1;
-out:
- free(leaders);
- return ret;
-}
-
-__weak struct evsel *arch_evlist__leader(struct list_head *list)
-{
- return list_first_entry(list, struct evsel, core.node);
-}
-
-void parse_events__set_leader(char *name, struct list_head *list,
- struct parse_events_state *parse_state)
+void parse_events__set_leader(char *name, struct list_head *list)
{
struct evsel *leader;
@@ -1781,13 +1666,9 @@ void parse_events__set_leader(char *name, struct list_head *list,
return;
}
- if (parse_events__set_leader_for_uncore_aliase(name, list, parse_state))
- return;
-
- leader = arch_evlist__leader(list);
+ leader = list_first_entry(list, struct evsel, core.node);
__perf_evlist__set_leader(list, &leader->core);
- leader->group_name = name ? strdup(name) : NULL;
- list_move(&leader->core.node, list);
+ leader->group_name = name;
}
/* list_event is assumed to point to malloc'ed memory */
@@ -2139,7 +2020,7 @@ int perf_pmu__test_parse_init(void)
err_free:
for (j = 0, tmp = list; j < i; j++, tmp++)
- free(tmp->symbol);
+ zfree(&tmp->symbol);
free(list);
return -ENOMEM;
}
@@ -2244,8 +2125,136 @@ static int parse_events__with_hybrid_pmu(struct parse_events_state *parse_state,
return ret;
}
+__weak int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs)
+{
+ /* Order by insertion index. */
+ return lhs->core.idx - rhs->core.idx;
+}
+
+static int evlist__cmp(void *state, const struct list_head *l, const struct list_head *r)
+{
+ const struct perf_evsel *lhs_core = container_of(l, struct perf_evsel, node);
+ const struct evsel *lhs = container_of(lhs_core, struct evsel, core);
+ const struct perf_evsel *rhs_core = container_of(r, struct perf_evsel, node);
+ const struct evsel *rhs = container_of(rhs_core, struct evsel, core);
+ int *leader_idx = state;
+ int lhs_leader_idx = *leader_idx, rhs_leader_idx = *leader_idx, ret;
+ const char *lhs_pmu_name, *rhs_pmu_name;
+
+ /*
+ * First sort by grouping/leader. Read the leader idx only if the evsel
+ * is part of a group, as -1 indicates no group.
+ */
+ if (lhs_core->leader != lhs_core || lhs_core->nr_members > 1)
+ lhs_leader_idx = lhs_core->leader->idx;
+ if (rhs_core->leader != rhs_core || rhs_core->nr_members > 1)
+ rhs_leader_idx = rhs_core->leader->idx;
+
+ if (lhs_leader_idx != rhs_leader_idx)
+ return lhs_leader_idx - rhs_leader_idx;
+
+ /* Group by PMU. Groups can't span PMUs. */
+ lhs_pmu_name = evsel__group_pmu_name(lhs);
+ rhs_pmu_name = evsel__group_pmu_name(rhs);
+ ret = strcmp(lhs_pmu_name, rhs_pmu_name);
+ if (ret)
+ return ret;
+
+ /* Architecture specific sorting. */
+ return arch_evlist__cmp(lhs, rhs);
+}
+
+static bool parse_events__sort_events_and_fix_groups(struct list_head *list)
+{
+ int idx = 0, unsorted_idx = -1;
+ struct evsel *pos, *cur_leader = NULL;
+ struct perf_evsel *cur_leaders_grp = NULL;
+ bool idx_changed = false;
+ int orig_num_leaders = 0, num_leaders = 0;
+
+ /*
+ * Compute index to insert ungrouped events at. Place them where the
+ * first ungrouped event appears.
+ */
+ list_for_each_entry(pos, list, core.node) {
+ const struct evsel *pos_leader = evsel__leader(pos);
+
+ if (pos == pos_leader)
+ orig_num_leaders++;
+
+ /*
+ * Ensure indexes are sequential, in particular for multiple
+ * event lists being merged. The indexes are used to detect when
+ * the user order is modified.
+ */
+ pos->core.idx = idx++;
+
+ if (unsorted_idx == -1 && pos == pos_leader && pos->core.nr_members < 2)
+ unsorted_idx = pos->core.idx;
+ }
+
+ /* Sort events. */
+ list_sort(&unsorted_idx, list, evlist__cmp);
+
+ /*
+ * Recompute groups, splitting for PMUs and adding groups for events
+ * that require them.
+ */
+ idx = 0;
+ list_for_each_entry(pos, list, core.node) {
+ const struct evsel *pos_leader = evsel__leader(pos);
+ const char *pos_pmu_name = evsel__group_pmu_name(pos);
+ const char *cur_leader_pmu_name, *pos_leader_pmu_name;
+ bool force_grouped = arch_evsel__must_be_in_group(pos);
+
+ /* Reset index and nr_members. */
+ if (pos->core.idx != idx)
+ idx_changed = true;
+ pos->core.idx = idx++;
+ pos->core.nr_members = 0;
+
+ /*
+ * Set the group leader respecting the given groupings and that
+ * groups can't span PMUs.
+ */
+ if (!cur_leader)
+ cur_leader = pos;
+
+ cur_leader_pmu_name = evsel__group_pmu_name(cur_leader);
+ if ((cur_leaders_grp != pos->core.leader && !force_grouped) ||
+ strcmp(cur_leader_pmu_name, pos_pmu_name)) {
+ /* Event is for a different group/PMU than last. */
+ cur_leader = pos;
+ /*
+ * Remember the leader's group before it is overwritten,
+ * so that later events match as being in the same
+ * group.
+ */
+ cur_leaders_grp = pos->core.leader;
+ }
+ pos_leader_pmu_name = evsel__group_pmu_name(pos_leader);
+ if (strcmp(pos_leader_pmu_name, pos_pmu_name) || force_grouped) {
+ /*
+ * Event's PMU differs from its leader's. Groups can't
+ * span PMUs, so update leader from the group/PMU
+ * tracker.
+ */
+ evsel__set_leader(pos, cur_leader);
+ }
+ }
+ list_for_each_entry(pos, list, core.node) {
+ struct evsel *pos_leader = evsel__leader(pos);
+
+ if (pos == pos_leader)
+ num_leaders++;
+ pos_leader->core.nr_members++;
+ }
+ return idx_changed || num_leaders != orig_num_leaders;
+}
+
int __parse_events(struct evlist *evlist, const char *str,
- struct parse_events_error *err, struct perf_pmu *fake_pmu)
+ struct parse_events_error *err, struct perf_pmu *fake_pmu,
+ bool warn_if_reordered)
{
struct parse_events_state parse_state = {
.list = LIST_HEAD_INIT(parse_state.list),
@@ -2265,6 +2274,10 @@ int __parse_events(struct evlist *evlist, const char *str,
return -1;
}
+ if (parse_events__sort_events_and_fix_groups(&parse_state.list) &&
+ warn_if_reordered && !parse_state.wild_card_pmus)
+ pr_warning("WARNING: events were regrouped to match PMUs\n");
+
/*
* Add list to the evlist even with errors to allow callers to clean up.
*/
@@ -2273,7 +2286,6 @@ int __parse_events(struct evlist *evlist, const char *str,
if (!ret) {
struct evsel *last;
- evlist->core.nr_groups += parse_state.nr_groups;
last = evlist__last(evlist);
last->cmdline_group_boundary = true;
@@ -2537,11 +2549,8 @@ static int set_filter(struct evsel *evsel, const void *arg)
perf_pmu__scan_file(pmu, "nr_addr_filters",
"%d", &nr_addr_filters);
- if (!nr_addr_filters) {
- fprintf(stderr,
- "This CPU does not support address filtering\n");
- return -1;
- }
+ if (!nr_addr_filters)
+ return perf_bpf_filter__parse(&evsel->bpf_filters, str);
if (evsel__append_addr_filter(evsel, str) < 0) {
fprintf(stderr,
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 428e72eaafcc..86ad4438a2aa 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -26,13 +26,13 @@ int parse_events_option(const struct option *opt, const char *str, int unset);
int parse_events_option_new_evlist(const struct option *opt, const char *str, int unset);
__attribute__((nonnull(1, 2, 3)))
int __parse_events(struct evlist *evlist, const char *str, struct parse_events_error *error,
- struct perf_pmu *fake_pmu);
+ struct perf_pmu *fake_pmu, bool warn_if_reordered);
-__attribute__((nonnull))
+__attribute__((nonnull(1, 2, 3)))
static inline int parse_events(struct evlist *evlist, const char *str,
struct parse_events_error *err)
{
- return __parse_events(evlist, str, err, NULL);
+ return __parse_events(evlist, str, err, /*fake_pmu=*/NULL, /*warn_if_reordered=*/true);
}
int parse_event(struct evlist *evlist, const char *str);
@@ -59,6 +59,7 @@ enum {
PARSE_EVENTS__TERM_TYPE_CONFIG,
PARSE_EVENTS__TERM_TYPE_CONFIG1,
PARSE_EVENTS__TERM_TYPE_CONFIG2,
+ PARSE_EVENTS__TERM_TYPE_CONFIG3,
PARSE_EVENTS__TERM_TYPE_NAME,
PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD,
PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ,
@@ -122,13 +123,13 @@ struct parse_events_error {
struct parse_events_state {
struct list_head list;
int idx;
- int nr_groups;
struct parse_events_error *error;
struct evlist *evlist;
struct list_head *terms;
int stoken;
struct perf_pmu *fake_pmu;
char *hybrid_pmu_name;
+ bool wild_card_pmus;
};
void parse_events__shrink_config_terms(void);
@@ -183,8 +184,7 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx,
int parse_events_add_pmu(struct parse_events_state *parse_state,
struct list_head *list, char *name,
struct list_head *head_config,
- bool auto_merge_stats,
- bool use_alias);
+ bool auto_merge_stats);
struct evsel *parse_events__add_event(int idx, struct perf_event_attr *attr,
const char *name, const char *metric_id,
@@ -200,8 +200,7 @@ int parse_events_copy_term_list(struct list_head *old,
enum perf_pmu_event_symbol_type
perf_pmu__parse_check(const char *name);
-void parse_events__set_leader(char *name, struct list_head *list,
- struct parse_events_state *parse_state);
+void parse_events__set_leader(char *name, struct list_head *list);
void parse_events_update_lists(struct list_head *list_event,
struct list_head *list_all);
void parse_events_evlist_error(struct parse_events_state *parse_state,
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 3a9ce96c8bce..51fe0a9fb3de 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -285,6 +285,7 @@ modifier_bp [rwx]{1,3}
config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); }
config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); }
config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); }
+config3 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG3); }
name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); }
period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
freq { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ); }
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index be8c51770051..4488443e506e 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -49,14 +49,6 @@ static void free_list_evsel(struct list_head* list_evsel)
free(list_evsel);
}
-static void inc_group_count(struct list_head *list,
- struct parse_events_state *parse_state)
-{
- /* Count groups only have more than 1 members */
- if (!list_is_last(list->next, list))
- parse_state->nr_groups++;
-}
-
%}
%token PE_START_EVENTS PE_START_TERMS
@@ -201,9 +193,8 @@ PE_NAME '{' events '}'
{
struct list_head *list = $3;
- inc_group_count(list, _parse_state);
- parse_events__set_leader($1, list, _parse_state);
- free($1);
+ /* Takes ownership of $1. */
+ parse_events__set_leader($1, list);
$$ = list;
}
|
@@ -211,8 +202,7 @@ PE_NAME '{' events '}'
{
struct list_head *list = $2;
- inc_group_count(list, _parse_state);
- parse_events__set_leader(NULL, list, _parse_state);
+ parse_events__set_leader(NULL, list);
$$ = list;
}
@@ -313,7 +303,7 @@ event_pmu_name opt_pmu_config
list = alloc_list();
if (!list)
CLEANUP_YYABORT;
- if (parse_events_add_pmu(_parse_state, list, $1, $2, false, false)) {
+ if (parse_events_add_pmu(_parse_state, list, $1, $2, /*auto_merge_stats=*/false)) {
struct perf_pmu *pmu = NULL;
int ok = 0;
@@ -330,8 +320,11 @@ event_pmu_name opt_pmu_config
!perf_pmu__match(pattern, pmu->alias_name, $1)) {
if (parse_events_copy_term_list(orig_terms, &terms))
CLEANUP_YYABORT;
- if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms, true, false))
+ if (!parse_events_add_pmu(_parse_state, list, pmu->name, terms,
+ /*auto_merge_stats=*/true)) {
ok++;
+ parse_state->wild_card_pmus = true;
+ }
parse_events_terms__delete(terms);
}
}
@@ -407,7 +400,8 @@ PE_PMU_EVENT_FAKE sep_dc
if (!list)
YYABORT;
- err = parse_events_add_pmu(_parse_state, list, $1, NULL, false, false);
+ err = parse_events_add_pmu(_parse_state, list, $1, /*head_config=*/NULL,
+ /*auto_merge_stats=*/false);
free($1);
if (err < 0) {
free(list);
@@ -425,7 +419,7 @@ PE_PMU_EVENT_FAKE opt_pmu_config
if (!list)
YYABORT;
- err = parse_events_add_pmu(_parse_state, list, $1, $2, false, false);
+ err = parse_events_add_pmu(_parse_state, list, $1, $2, /*auto_merge_stats=*/false);
free($1);
parse_events_terms__delete($2);
if (err < 0) {
diff --git a/tools/perf/util/pfm.c b/tools/perf/util/pfm.c
index b59ba825ddc9..6c11914c179f 100644
--- a/tools/perf/util/pfm.c
+++ b/tools/perf/util/pfm.c
@@ -112,7 +112,6 @@ int parse_libpfm_events_option(const struct option *opt, const char *str,
"cannot close a non-existing event group\n");
goto error;
}
- evlist->core.nr_groups++;
grp_leader = NULL;
grp_evt = -1;
}
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index c256b29defad..ad209c88a124 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -24,6 +24,8 @@
#include "evsel.h"
#include "pmu.h"
#include "pmus.h"
+#include "pmu-bison.h"
+#include "pmu-flex.h"
#include "parse-events.h"
#include "print-events.h"
#include "header.h"
@@ -31,6 +33,7 @@
#include "strbuf.h"
#include "fncache.h"
#include "pmu-hybrid.h"
+#include "util/evsel_config.h"
struct perf_pmu perf_pmu__fake;
@@ -57,42 +60,54 @@ struct perf_pmu_format {
struct list_head list;
};
-int perf_pmu_parse(struct list_head *list, char *name);
-extern FILE *perf_pmu_in;
-
static bool hybrid_scanned;
+static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name);
+
/*
* Parse & process all the sysfs attributes located under
* the directory specified in 'dir' parameter.
*/
-int perf_pmu__format_parse(char *dir, struct list_head *head)
+int perf_pmu__format_parse(int dirfd, struct list_head *head)
{
struct dirent *evt_ent;
DIR *format_dir;
int ret = 0;
- format_dir = opendir(dir);
+ format_dir = fdopendir(dirfd);
if (!format_dir)
return -EINVAL;
while (!ret && (evt_ent = readdir(format_dir))) {
- char path[PATH_MAX];
char *name = evt_ent->d_name;
+ int fd;
+ void *scanner;
FILE *file;
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
- snprintf(path, PATH_MAX, "%s/%s", dir, name);
ret = -EINVAL;
- file = fopen(path, "r");
- if (!file)
+ fd = openat(dirfd, name, O_RDONLY);
+ if (fd < 0)
+ break;
+
+ file = fdopen(fd, "r");
+ if (!file) {
+ close(fd);
+ break;
+ }
+
+ ret = perf_pmu_lex_init(&scanner);
+ if (ret) {
+ fclose(file);
break;
+ }
- perf_pmu_in = file;
- ret = perf_pmu_parse(head, name);
+ perf_pmu_set_in(file, scanner);
+ ret = perf_pmu_parse(head, name, scanner);
+ perf_pmu_lex_destroy(scanner);
fclose(file);
}
@@ -105,17 +120,16 @@ int perf_pmu__format_parse(char *dir, struct list_head *head)
* located at:
* /sys/bus/event_source/devices/<dev>/format as sysfs group attributes.
*/
-static int pmu_format(const char *name, struct list_head *format)
+static int pmu_format(int dirfd, const char *name, struct list_head *format)
{
- char path[PATH_MAX];
-
- if (!perf_pmu__pathname_scnprintf(path, sizeof(path), name, "format"))
- return -1;
+ int fd;
- if (!file_available(path))
+ fd = perf_pmu__pathname_fd(dirfd, name, "format", O_DIRECTORY);
+ if (fd < 0)
return 0;
- if (perf_pmu__format_parse(path, format))
+ /* it'll close the fd */
+ if (perf_pmu__format_parse(fd, format))
return -1;
return 0;
@@ -158,7 +172,7 @@ out:
return ret;
}
-static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name)
+static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, int dirfd, char *name)
{
struct stat st;
ssize_t sret;
@@ -166,9 +180,9 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *
int fd, ret = -1;
char path[PATH_MAX];
- scnprintf(path, PATH_MAX, "%s/%s.scale", dir, name);
+ scnprintf(path, PATH_MAX, "%s.scale", name);
- fd = open(path, O_RDONLY);
+ fd = openat(dirfd, path, O_RDONLY);
if (fd == -1)
return -1;
@@ -190,15 +204,15 @@ error:
return ret;
}
-static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *name)
+static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, int dirfd, char *name)
{
char path[PATH_MAX];
ssize_t sret;
int fd;
- scnprintf(path, PATH_MAX, "%s/%s.unit", dir, name);
+ scnprintf(path, PATH_MAX, "%s.unit", name);
- fd = open(path, O_RDONLY);
+ fd = openat(dirfd, path, O_RDONLY);
if (fd == -1)
return -1;
@@ -221,14 +235,14 @@ error:
}
static int
-perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, char *dir, char *name)
+perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, int dirfd, char *name)
{
char path[PATH_MAX];
int fd;
- scnprintf(path, PATH_MAX, "%s/%s.per-pkg", dir, name);
+ scnprintf(path, PATH_MAX, "%s.per-pkg", name);
- fd = open(path, O_RDONLY);
+ fd = openat(dirfd, path, O_RDONLY);
if (fd == -1)
return -1;
@@ -239,14 +253,14 @@ perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, char *dir, char *name)
}
static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias,
- char *dir, char *name)
+ int dirfd, char *name)
{
char path[PATH_MAX];
int fd;
- scnprintf(path, PATH_MAX, "%s/%s.snapshot", dir, name);
+ scnprintf(path, PATH_MAX, "%s.snapshot", name);
- fd = open(path, O_RDONLY);
+ fd = openat(dirfd, path, O_RDONLY);
if (fd == -1)
return -1;
@@ -300,6 +314,16 @@ void perf_pmu_free_alias(struct perf_pmu_alias *newalias)
free(newalias);
}
+static void perf_pmu__del_aliases(struct perf_pmu *pmu)
+{
+ struct perf_pmu_alias *alias, *tmp;
+
+ list_for_each_entry_safe(alias, tmp, &pmu->aliases, list) {
+ list_del(&alias->list);
+ perf_pmu_free_alias(alias);
+ }
+}
+
/* Merge an alias, search in alias list. If this name is already
* present merge both of them to combine all information.
*/
@@ -322,24 +346,23 @@ static bool perf_pmu_merge_alias(struct perf_pmu_alias *newalias,
return false;
}
-static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
+static int __perf_pmu__new_alias(struct list_head *list, int dirfd, char *name,
char *desc, char *val, const struct pmu_event *pe)
{
struct parse_events_term *term;
struct perf_pmu_alias *alias;
int ret;
- int num;
char newval[256];
- char *long_desc = NULL, *topic = NULL, *unit = NULL, *perpkg = NULL,
- *deprecated = NULL, *pmu_name = NULL;
+ const char *long_desc = NULL, *topic = NULL, *unit = NULL, *pmu_name = NULL;
+ bool deprecated = false, perpkg = false;
if (pe) {
- long_desc = (char *)pe->long_desc;
- topic = (char *)pe->topic;
- unit = (char *)pe->unit;
- perpkg = (char *)pe->perpkg;
- deprecated = (char *)pe->deprecated;
- pmu_name = (char *)pe->pmu;
+ long_desc = pe->long_desc;
+ topic = pe->topic;
+ unit = pe->unit;
+ perpkg = pe->perpkg;
+ deprecated = pe->deprecated;
+ pmu_name = pe->pmu;
}
alias = malloc(sizeof(*alias));
@@ -349,9 +372,9 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
INIT_LIST_HEAD(&alias->terms);
alias->scale = 1.0;
alias->unit[0] = '\0';
- alias->per_pkg = false;
+ alias->per_pkg = perpkg;
alias->snapshot = false;
- alias->deprecated = false;
+ alias->deprecated = deprecated;
ret = parse_events_terms(&alias->terms, val);
if (ret) {
@@ -382,14 +405,14 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
}
alias->name = strdup(name);
- if (dir) {
+ if (dirfd >= 0) {
/*
* load unit name and scale if available
*/
- perf_pmu__parse_unit(alias, dir, name);
- perf_pmu__parse_scale(alias, dir, name);
- perf_pmu__parse_per_pkg(alias, dir, name);
- perf_pmu__parse_snapshot(alias, dir, name);
+ 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;
@@ -397,24 +420,20 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name,
desc ? strdup(desc) : NULL;
alias->topic = topic ? strdup(topic) : NULL;
if (unit) {
- if (perf_pmu__convert_scale(unit, &unit, &alias->scale) < 0)
+ if (perf_pmu__convert_scale(unit, (char **)&unit, &alias->scale) < 0)
return -1;
snprintf(alias->unit, sizeof(alias->unit), "%s", unit);
}
- alias->per_pkg = perpkg && sscanf(perpkg, "%d", &num) == 1 && num == 1;
alias->str = strdup(newval);
alias->pmu_name = pmu_name ? strdup(pmu_name) : NULL;
- if (deprecated)
- alias->deprecated = true;
-
if (!perf_pmu_merge_alias(alias, list))
list_add_tail(&alias->list, list);
return 0;
}
-static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file)
+static int perf_pmu__new_alias(struct list_head *list, int dirfd, char *name, FILE *file)
{
char buf[256];
int ret;
@@ -428,7 +447,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI
/* Remove trailing newline from sysfs file */
strim(buf);
- return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL);
+ return __perf_pmu__new_alias(list, dirfd, name, NULL, buf, NULL);
}
static inline bool pmu_alias_info_file(char *name)
@@ -452,17 +471,17 @@ static inline bool pmu_alias_info_file(char *name)
* Process all the sysfs attributes located under the directory
* specified in 'dir' parameter.
*/
-static int pmu_aliases_parse(char *dir, struct list_head *head)
+static int pmu_aliases_parse(int dirfd, struct list_head *head)
{
struct dirent *evt_ent;
DIR *event_dir;
+ int fd;
- event_dir = opendir(dir);
+ event_dir = fdopendir(dirfd);
if (!event_dir)
return -EINVAL;
while ((evt_ent = readdir(event_dir))) {
- char path[PATH_MAX];
char *name = evt_ent->d_name;
FILE *file;
@@ -475,15 +494,18 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
if (pmu_alias_info_file(name))
continue;
- scnprintf(path, PATH_MAX, "%s/%s", dir, name);
-
- file = fopen(path, "r");
+ fd = openat(dirfd, name, O_RDONLY);
+ if (fd == -1) {
+ pr_debug("Cannot open %s\n", name);
+ continue;
+ }
+ file = fdopen(fd, "r");
if (!file) {
- pr_debug("Cannot open %s\n", path);
+ close(fd);
continue;
}
- if (perf_pmu__new_alias(head, dir, name, file) < 0)
+ if (perf_pmu__new_alias(head, dirfd, name, file) < 0)
pr_debug("Cannot set up %s\n", name);
fclose(file);
}
@@ -496,17 +518,16 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
* 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(const char *name, struct list_head *head)
+static int pmu_aliases(int dirfd, const char *name, struct list_head *head)
{
- char path[PATH_MAX];
-
- if (!perf_pmu__pathname_scnprintf(path, sizeof(path), name, "events"))
- return -1;
+ int fd;
- if (!file_available(path))
+ fd = perf_pmu__pathname_fd(dirfd, name, "events", O_DIRECTORY);
+ if (fd < 0)
return 0;
- if (pmu_aliases_parse(path, head))
+ /* it'll close the fd */
+ if (pmu_aliases_parse(fd, head))
return -1;
return 0;
@@ -539,14 +560,15 @@ static int pmu_alias_terms(struct perf_pmu_alias *alias,
/* Add all pmus in sysfs to pmu list: */
static void pmu_read_sysfs(void)
{
- char path[PATH_MAX];
+ int fd;
DIR *dir;
struct dirent *dent;
- if (!perf_pmu__event_source_devices_scnprintf(path, sizeof(path)))
+ fd = perf_pmu__event_source_devices_fd();
+ if (fd < 0)
return;
- dir = opendir(path);
+ dir = fdopendir(fd);
if (!dir)
return;
@@ -554,7 +576,7 @@ static void pmu_read_sysfs(void)
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
continue;
/* add to static LIST_HEAD(pmus): */
- perf_pmu__find(dent->d_name);
+ perf_pmu__find2(fd, dent->d_name);
}
closedir(dir);
@@ -564,7 +586,7 @@ static void pmu_read_sysfs(void)
* Uncore PMUs have a "cpumask" file under sysfs. CPU PMUs (e.g. on arm/arm64)
* may have a "cpus" file.
*/
-static struct perf_cpu_map *pmu_cpumask(const char *name)
+static struct perf_cpu_map *pmu_cpumask(int dirfd, const char *name)
{
struct perf_cpu_map *cpus;
const char *templates[] = {
@@ -579,10 +601,11 @@ static struct perf_cpu_map *pmu_cpumask(const char *name)
strlcpy(pmu_name, name, sizeof(pmu_name));
for (template = templates; *template; template++) {
- file = perf_pmu__open_file(&pmu, *template);
+ file = perf_pmu__open_file_at(&pmu, dirfd, *template);
if (!file)
continue;
cpus = perf_cpu_map__read(file);
+ fclose(file);
if (cpus)
return cpus;
}
@@ -590,15 +613,19 @@ static struct perf_cpu_map *pmu_cpumask(const char *name)
return NULL;
}
-static bool pmu_is_uncore(const char *name)
+static bool pmu_is_uncore(int dirfd, const char *name)
{
- char path[PATH_MAX];
+ int fd;
if (perf_pmu__hybrid_mounted(name))
return false;
- perf_pmu__pathname_scnprintf(path, sizeof(path), name, "cpumask");
- return file_available(path);
+ fd = perf_pmu__pathname_fd(dirfd, name, "cpumask", O_PATH);
+ if (fd < 0)
+ return false;
+
+ close(fd);
+ return true;
}
static char *pmu_id(const char *name)
@@ -660,11 +687,14 @@ __weak const struct pmu_metrics_table *pmu_metrics_table__find(void)
return perf_pmu__find_metrics_table(NULL);
}
-/*
- * Suffix must be in form tok_{digits}, or tok{digits}, or same as pmu_name
- * to be valid.
+/**
+ * perf_pmu__match_ignoring_suffix - Does the pmu_name match tok ignoring any
+ * trailing suffix? The Suffix must be in form
+ * tok_{digits}, or tok{digits}.
+ * @pmu_name: The pmu_name with possible suffix.
+ * @tok: The possible match to pmu_name without suffix.
*/
-static bool perf_pmu__valid_suffix(const char *pmu_name, char *tok)
+static bool perf_pmu__match_ignoring_suffix(const char *pmu_name, const char *tok)
{
const char *p;
@@ -689,11 +719,21 @@ static bool perf_pmu__valid_suffix(const char *pmu_name, char *tok)
return true;
}
-bool pmu_uncore_alias_match(const char *pmu_name, const char *name)
+/**
+ * pmu_uncore_alias_match - does name match the PMU name?
+ * @pmu_name: the json struct pmu_event name. This may lack a suffix (which
+ * matches) or be of the form "socket,pmuname" which will match
+ * "socketX_pmunameY".
+ * @name: a real full PMU name as from sysfs.
+ */
+static bool pmu_uncore_alias_match(const char *pmu_name, const char *name)
{
char *tmp = NULL, *tok, *str;
bool res;
+ if (strchr(pmu_name, ',') == NULL)
+ return perf_pmu__match_ignoring_suffix(name, pmu_name);
+
str = strdup(pmu_name);
if (!str)
return false;
@@ -720,7 +760,7 @@ bool pmu_uncore_alias_match(const char *pmu_name, const char *name)
name = strstr(name, tok);
if (!name ||
- (!next_tok && !perf_pmu__valid_suffix(name, tok))) {
+ (!next_tok && !perf_pmu__match_ignoring_suffix(name, tok))) {
res = false;
goto out;
}
@@ -758,7 +798,7 @@ static int pmu_add_cpu_aliases_map_callback(const struct pmu_event *pe,
new_alias:
/* need type casts to override 'const' */
- __perf_pmu__new_alias(data->head, NULL, (char *)pe->name, (char *)pe->desc,
+ __perf_pmu__new_alias(data->head, -1, (char *)pe->name, (char *)pe->desc,
(char *)pe->event, pe);
return 0;
}
@@ -809,7 +849,7 @@ static int pmu_add_sys_aliases_iter_fn(const struct pmu_event *pe,
if (!strcmp(pmu->id, pe->compat) &&
pmu_uncore_alias_match(pe->pmu, pmu->name)) {
- __perf_pmu__new_alias(idata->head, NULL,
+ __perf_pmu__new_alias(idata->head, -1,
(char *)pe->name,
(char *)pe->desc,
(char *)pe->event,
@@ -850,15 +890,15 @@ pmu_find_alias_name(const char *name __maybe_unused)
return NULL;
}
-static int pmu_max_precise(struct perf_pmu *pmu)
+static int pmu_max_precise(int dirfd, struct perf_pmu *pmu)
{
int max_precise = -1;
- perf_pmu__scan_file(pmu, "caps/max_precise", "%d", &max_precise);
+ perf_pmu__scan_file_at(pmu, dirfd, "caps/max_precise", "%d", &max_precise);
return max_precise;
}
-static struct perf_pmu *pmu_lookup(const char *lookup_name)
+static struct perf_pmu *pmu_lookup(int dirfd, const char *lookup_name)
{
struct perf_pmu *pmu;
LIST_HEAD(format);
@@ -879,27 +919,27 @@ static struct perf_pmu *pmu_lookup(const char *lookup_name)
* type value and format definitions. Load both right
* now.
*/
- if (pmu_format(name, &format))
+ if (pmu_format(dirfd, name, &format))
return NULL;
/*
* Check the aliases first to avoid unnecessary work.
*/
- if (pmu_aliases(name, &aliases))
+ if (pmu_aliases(dirfd, name, &aliases))
return NULL;
pmu = zalloc(sizeof(*pmu));
if (!pmu)
return NULL;
- pmu->cpus = pmu_cpumask(name);
+ pmu->cpus = pmu_cpumask(dirfd, name);
pmu->name = strdup(name);
if (!pmu->name)
goto err;
/* Read type, and ensure that type value is successfully assigned (return 1) */
- if (perf_pmu__scan_file(pmu, "type", "%u", &type) != 1)
+ if (perf_pmu__scan_file_at(pmu, dirfd, "type", "%u", &type) != 1)
goto err;
alias_name = pmu_find_alias_name(name);
@@ -910,10 +950,10 @@ static struct perf_pmu *pmu_lookup(const char *lookup_name)
}
pmu->type = type;
- pmu->is_uncore = pmu_is_uncore(name);
+ pmu->is_uncore = pmu_is_uncore(dirfd, name);
if (pmu->is_uncore)
pmu->id = pmu_id(name);
- pmu->max_precise = pmu_max_precise(pmu);
+ pmu->max_precise = pmu_max_precise(dirfd, pmu);
pmu_add_cpu_aliases(&aliases, pmu);
pmu_add_sys_aliases(&aliases, pmu);
@@ -926,13 +966,14 @@ static struct perf_pmu *pmu_lookup(const char *lookup_name)
if (is_hybrid)
list_add_tail(&pmu->hybrid_list, &perf_pmu__hybrid_pmus);
+ else
+ INIT_LIST_HEAD(&pmu->hybrid_list);
pmu->default_config = perf_pmu__get_default_config(pmu);
return pmu;
err:
- if (pmu->name)
- free(pmu->name);
+ zfree(&pmu->name);
free(pmu);
return NULL;
}
@@ -993,7 +1034,7 @@ struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu)
return NULL;
}
-struct perf_pmu *evsel__find_pmu(struct evsel *evsel)
+struct perf_pmu *evsel__find_pmu(const struct evsel *evsel)
{
struct perf_pmu *pmu = NULL;
@@ -1005,20 +1046,50 @@ struct perf_pmu *evsel__find_pmu(struct evsel *evsel)
break;
}
- evsel->pmu = pmu;
+ ((struct evsel *)evsel)->pmu = pmu;
return pmu;
}
-bool evsel__is_aux_event(struct evsel *evsel)
+bool evsel__is_aux_event(const struct evsel *evsel)
{
struct perf_pmu *pmu = evsel__find_pmu(evsel);
return pmu && pmu->auxtrace;
}
+/*
+ * Set @config_name to @val as long as the user hasn't already set or cleared it
+ * by passing a config term on the command line.
+ *
+ * @val is the value to put into the bits specified by @config_name rather than
+ * the bit pattern. It is shifted into position by this function, so to set
+ * something to true, pass 1 for val rather than a pre shifted value.
+ */
+#define field_prep(_mask, _val) (((_val) << (ffsll(_mask) - 1)) & (_mask))
+void evsel__set_config_if_unset(struct perf_pmu *pmu, struct evsel *evsel,
+ const char *config_name, u64 val)
+{
+ u64 user_bits = 0, bits;
+ struct evsel_config_term *term = evsel__get_config_term(evsel, CFG_CHG);
+
+ if (term)
+ user_bits = term->val.cfg_chg;
+
+ bits = perf_pmu__format_bits(&pmu->format, config_name);
+
+ /* Do nothing if the user changed the value */
+ if (bits & user_bits)
+ return;
+
+ /* Otherwise replace it */
+ evsel->core.attr.config &= ~bits;
+ evsel->core.attr.config |= field_prep(bits, val);
+}
+
struct perf_pmu *perf_pmu__find(const char *name)
{
struct perf_pmu *pmu;
+ int dirfd;
/*
* Once PMU is loaded it stays in the list,
@@ -1029,7 +1100,27 @@ struct perf_pmu *perf_pmu__find(const char *name)
if (pmu)
return pmu;
- return pmu_lookup(name);
+ dirfd = perf_pmu__event_source_devices_fd();
+ pmu = pmu_lookup(dirfd, name);
+ close(dirfd);
+
+ return pmu;
+}
+
+static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)
+{
+ struct perf_pmu *pmu;
+
+ /*
+ * Once PMU is loaded it stays in the list,
+ * so we keep us from multiple reading/parsing
+ * the pmu format definitions.
+ */
+ pmu = pmu_find(name);
+ if (pmu)
+ return pmu;
+
+ return pmu_lookup(dirfd, name);
}
static struct perf_pmu_format *
@@ -1214,6 +1305,9 @@ static int pmu_config_term(const char *pmu_name,
case PERF_PMU_FORMAT_VALUE_CONFIG2:
vp = &attr->config2;
break;
+ case PERF_PMU_FORMAT_VALUE_CONFIG3:
+ vp = &attr->config3;
+ break;
default:
return -EINVAL;
}
@@ -1454,7 +1548,7 @@ void perf_pmu__del_formats(struct list_head *formats)
list_for_each_entry_safe(fmt, tmp, formats, list) {
list_del(&fmt->list);
- free(fmt->name);
+ zfree(&fmt->name);
free(fmt);
}
}
@@ -1509,7 +1603,7 @@ 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, *b_pmu_name;
+ 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 = "";
int ret;
@@ -1518,11 +1612,13 @@ static int cmp_sevent(const void *a, const void *b)
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)
@@ -1538,11 +1634,13 @@ static int cmp_sevent(const void *a, const void *b)
return as->is_cpu ? -1 : 1;
/* Order by PMU name. */
- a_pmu_name = as->pmu->name ?: "";
- b_pmu_name = bs->pmu->name ?: "";
- ret = strcmp(a_pmu_name, b_pmu_name);
- if (ret)
- return ret;
+ 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);
+ if (ret)
+ return ret;
+ }
/* Order by event name. */
return strcmp(a_name, b_name);
@@ -1556,17 +1654,26 @@ bool is_pmu_core(const char *name)
static bool pmu_alias_is_duplicate(struct sevent *alias_a,
struct sevent *alias_b)
{
- const char *a_pmu_name, *b_pmu_name;
- const char *a_name = alias_a->event ? alias_a->event->name : "//";
- const char *b_name = alias_b->event ? alias_b->event->name : "//";
+ 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))
return false;
/* Don't remove duplicates for different PMUs */
- a_pmu_name = alias_a->pmu->name ?: "";
- b_pmu_name = alias_b->pmu->name ?: "";
+ 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;
}
@@ -1615,7 +1722,8 @@ void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
for (j = 0; j < len; j++) {
const char *name, *alias = NULL, *scale_unit = NULL,
*desc = NULL, *long_desc = NULL,
- *encoding_desc = NULL, *topic = NULL;
+ *encoding_desc = NULL, *topic = NULL,
+ *pmu_name = NULL;
bool deprecated = false;
size_t buf_used;
@@ -1625,7 +1733,8 @@ void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
if (!aliases[j].event) {
/* A selectable event. */
- buf_used = snprintf(buf, sizeof(buf), "%s//", aliases[j].pmu->name) + 1;
+ pmu_name = aliases[j].pmu->name;
+ buf_used = snprintf(buf, sizeof(buf), "%s//", pmu_name) + 1;
name = buf;
} else {
if (aliases[j].event->desc) {
@@ -1640,6 +1749,7 @@ void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
}
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,
@@ -1651,12 +1761,11 @@ void print_pmu_events(const struct print_callbacks *print_cb, void *print_state)
topic = aliases[j].event->topic;
encoding_desc = buf + buf_used;
buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used,
- "%s/%s/", aliases[j].pmu->name,
- aliases[j].event->str) + 1;
+ "%s/%s/", pmu_name, aliases[j].event->str) + 1;
deprecated = aliases[j].event->deprecated;
}
print_cb->print_event(print_state,
- aliases[j].pmu->name,
+ pmu_name,
topic,
name,
alias,
@@ -1701,6 +1810,17 @@ FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name)
return fopen(path, "r");
}
+FILE *perf_pmu__open_file_at(struct perf_pmu *pmu, int dirfd, const char *name)
+{
+ int fd;
+
+ fd = perf_pmu__pathname_fd(dirfd, pmu->name, name, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ return fdopen(fd, "r");
+}
+
int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
...)
{
@@ -1718,6 +1838,23 @@ int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
return ret;
}
+int perf_pmu__scan_file_at(struct perf_pmu *pmu, int dirfd, const char *name,
+ const char *fmt, ...)
+{
+ va_list args;
+ FILE *file;
+ int ret = EOF;
+
+ va_start(args, fmt);
+ file = perf_pmu__open_file_at(pmu, dirfd, name);
+ if (file) {
+ ret = vfscanf(file, fmt, args);
+ fclose(file);
+ }
+ va_end(args);
+ return ret;
+}
+
bool perf_pmu__file_exists(struct perf_pmu *pmu, const char *name)
{
char path[PATH_MAX];
@@ -1745,13 +1882,25 @@ static int perf_pmu__new_caps(struct list_head *list, char *name, char *value)
return 0;
free_name:
- zfree(caps->name);
+ zfree(&caps->name);
free_caps:
free(caps);
return -ENOMEM;
}
+static void perf_pmu__del_caps(struct perf_pmu *pmu)
+{
+ struct perf_pmu_caps *caps, *tmp;
+
+ list_for_each_entry_safe(caps, tmp, &pmu->caps, list) {
+ list_del(&caps->list);
+ zfree(&caps->name);
+ zfree(&caps->value);
+ free(caps);
+ }
+}
+
/*
* Reading/parsing the given pmu capabilities, which should be located at:
* /sys/bus/event_source/devices/<dev>/caps as sysfs group attributes.
@@ -1763,6 +1912,7 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu)
char caps_path[PATH_MAX];
DIR *caps_dir;
struct dirent *evt_ent;
+ int caps_fd;
if (pmu->caps_initialized)
return pmu->nr_caps;
@@ -1781,20 +1931,25 @@ int perf_pmu__caps_parse(struct perf_pmu *pmu)
if (!caps_dir)
return -EINVAL;
+ caps_fd = dirfd(caps_dir);
+
while ((evt_ent = readdir(caps_dir)) != NULL) {
- char path[PATH_MAX + NAME_MAX + 1];
char *name = evt_ent->d_name;
char value[128];
FILE *file;
+ int fd;
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
- snprintf(path, sizeof(path), "%s/%s", caps_path, name);
-
- file = fopen(path, "r");
- if (!file)
+ fd = openat(caps_fd, name, O_RDONLY);
+ if (fd == -1)
+ continue;
+ file = fdopen(fd, "r");
+ if (!file) {
+ close(fd);
continue;
+ }
if (!fgets(value, sizeof(value), file) ||
(perf_pmu__new_caps(&pmu->caps, name, value) < 0)) {
@@ -1863,7 +2018,7 @@ int perf_pmu__match(char *pattern, char *name, char *tok)
if (fnmatch(pattern, name, 0))
return -1;
- if (tok && !perf_pmu__valid_suffix(name, tok))
+ if (tok && !perf_pmu__match_ignoring_suffix(name, tok))
return -1;
return 0;
@@ -1890,13 +2045,13 @@ int perf_pmu__cpus_match(struct perf_pmu *pmu, struct perf_cpu_map *cpus,
perf_cpu_map__for_each_cpu(cpu, i, cpus) {
if (!perf_cpu_map__has(pmu_cpus, cpu))
- unmatched_cpus->map[unmatched_nr++] = cpu;
+ RC_CHK_ACCESS(unmatched_cpus)->map[unmatched_nr++] = cpu;
else
- matched_cpus->map[matched_nr++] = cpu;
+ RC_CHK_ACCESS(matched_cpus)->map[matched_nr++] = cpu;
}
- unmatched_cpus->nr = unmatched_nr;
- matched_cpus->nr = matched_nr;
+ perf_cpu_map__set_nr(unmatched_cpus, unmatched_nr);
+ perf_cpu_map__set_nr(matched_cpus, matched_nr);
*mcpus_ptr = matched_cpus;
*ucpus_ptr = unmatched_cpus;
return 0;
@@ -1916,6 +2071,18 @@ int perf_pmu__event_source_devices_scnprintf(char *pathname, size_t size)
return scnprintf(pathname, size, "%s/bus/event_source/devices/", sysfs);
}
+int perf_pmu__event_source_devices_fd(void)
+{
+ char path[PATH_MAX];
+ const char *sysfs = sysfs__mountpoint();
+
+ if (!sysfs)
+ return -1;
+
+ scnprintf(path, sizeof(path), "%s/bus/event_source/devices/", sysfs);
+ return open(path, O_DIRECTORY);
+}
+
/*
* Fill 'buf' with the path to a file or folder in 'pmu_name' in
* sysfs. For example if pmu_name = "cs_etm" and 'filename' = "format"
@@ -1934,3 +2101,37 @@ int perf_pmu__pathname_scnprintf(char *buf, size_t size,
return 0;
return scnprintf(buf, size, "%s%s/%s", base_path, pmu_name, filename);
}
+
+int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename, int flags)
+{
+ char path[PATH_MAX];
+
+ scnprintf(path, sizeof(path), "%s/%s", pmu_name, filename);
+ return openat(dirfd, path, flags);
+}
+
+static void perf_pmu__delete(struct perf_pmu *pmu)
+{
+ perf_pmu__del_formats(&pmu->format);
+ perf_pmu__del_aliases(pmu);
+ perf_pmu__del_caps(pmu);
+
+ perf_cpu_map__put(pmu->cpus);
+
+ zfree(&pmu->default_config);
+ zfree(&pmu->name);
+ zfree(&pmu->alias_name);
+ free(pmu);
+}
+
+void perf_pmu__destroy(void)
+{
+ struct perf_pmu *pmu, *tmp;
+
+ list_for_each_entry_safe(pmu, tmp, &pmus, list) {
+ list_del(&pmu->list);
+ list_del(&pmu->hybrid_list);
+
+ perf_pmu__delete(pmu);
+ }
+}
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 6b770f17eb86..b9a02dedd473 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -19,6 +19,7 @@ enum {
PERF_PMU_FORMAT_VALUE_CONFIG,
PERF_PMU_FORMAT_VALUE_CONFIG1,
PERF_PMU_FORMAT_VALUE_CONFIG2,
+ PERF_PMU_FORMAT_VALUE_CONFIG3,
PERF_PMU_FORMAT_VALUE_CONFIG_END,
};
@@ -34,7 +35,7 @@ struct perf_pmu_caps {
};
/**
- * struct perf_pmu - hi
+ * struct perf_pmu
*/
struct perf_pmu {
/** @name: The name of the PMU such as "cpu". */
@@ -185,7 +186,10 @@ struct perf_pmu_alias {
* default.
*/
bool deprecated;
- /** @pmu_name: The name copied from struct perf_pmu. */
+ /**
+ * @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;
};
@@ -205,12 +209,12 @@ 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, char const *msg);
+void perf_pmu_error(struct list_head *list, char *name, void *scanner, char const *msg);
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(char *dir, struct list_head *head);
+int perf_pmu__format_parse(int dirfd, struct list_head *head);
void perf_pmu__del_formats(struct list_head *formats);
struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);
@@ -219,7 +223,12 @@ bool is_pmu_core(const char *name);
void print_pmu_events(const struct print_callbacks *print_cb, void *print_state);
bool pmu_have_event(const char *pname, const char *name);
+FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name);
+FILE *perf_pmu__open_file_at(struct perf_pmu *pmu, int dirfd, const char *name);
+
int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, ...) __scanf(3, 4);
+int perf_pmu__scan_file_at(struct perf_pmu *pmu, int dirfd, const char *name,
+ const char *fmt, ...) __scanf(4, 5);
bool perf_pmu__file_exists(struct perf_pmu *pmu, const char *name);
@@ -232,7 +241,6 @@ void pmu_add_cpu_aliases_table(struct list_head *head, struct perf_pmu *pmu,
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);
-bool pmu_uncore_alias_match(const char *pmu_name, const char *name);
void perf_pmu_free_alias(struct perf_pmu_alias *alias);
int perf_pmu__convert_scale(const char *scale, char **end, double *sval);
@@ -256,6 +264,9 @@ 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,
const char *pmu_name, const char *filename);
-FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name);
+int perf_pmu__event_source_devices_fd(void);
+int perf_pmu__pathname_fd(int dirfd, const char *pmu_name, const char *filename, int flags);
+
+void perf_pmu__destroy(void);
#endif /* __PMU_H */
diff --git a/tools/perf/util/pmu.l b/tools/perf/util/pmu.l
index 58b4926cfaca..67b247be693b 100644
--- a/tools/perf/util/pmu.l
+++ b/tools/perf/util/pmu.l
@@ -1,4 +1,6 @@
%option prefix="perf_pmu_"
+%option reentrant
+%option bison-bridge
%{
#include <stdlib.h>
@@ -6,16 +8,21 @@
#include "pmu.h"
#include "pmu-bison.h"
-static int value(int base)
+char *perf_pmu_get_text(yyscan_t yyscanner);
+YYSTYPE *perf_pmu_get_lval(yyscan_t yyscanner);
+
+static int value(yyscan_t scanner, int base)
{
+ YYSTYPE *yylval = perf_pmu_get_lval(scanner);
+ char *text = perf_pmu_get_text(scanner);
long num;
errno = 0;
- num = strtoul(perf_pmu_text, NULL, base);
+ num = strtoul(text, NULL, base);
if (errno)
return PP_ERROR;
- perf_pmu_lval.num = num;
+ yylval->num = num;
return PP_VALUE;
}
@@ -25,7 +32,7 @@ num_dec [0-9]+
%%
-{num_dec} { return value(10); }
+{num_dec} { return value(yyscanner, 10); }
config { return PP_CONFIG; }
- { return '-'; }
: { return ':'; }
@@ -35,7 +42,7 @@ config { return PP_CONFIG; }
%%
-int perf_pmu_wrap(void)
+int perf_pmu_wrap(void *scanner __maybe_unused)
{
return 1;
}
diff --git a/tools/perf/util/pmu.y b/tools/perf/util/pmu.y
index e675d79a0274..dff4e892ac4d 100644
--- a/tools/perf/util/pmu.y
+++ b/tools/perf/util/pmu.y
@@ -1,6 +1,8 @@
-
+%define api.pure full
%parse-param {struct list_head *format}
%parse-param {char *name}
+%parse-param {void *scanner}
+%lex-param {void* scanner}
%{
@@ -78,6 +80,7 @@ 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)
{
}
diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c
index 62e9ea7dcf40..ee145cec42c0 100644
--- a/tools/perf/util/print-events.c
+++ b/tools/perf/util/print-events.c
@@ -4,7 +4,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <fcntl.h>
#include <sys/param.h>
+#include <unistd.h>
#include <api/fs/tracing_path.h>
#include <linux/stddef.h>
@@ -56,7 +58,18 @@ static const struct event_symbol event_symbols_tool[PERF_TOOL_MAX] = {
/*
* Print the events from <debugfs_mount_point>/tracing/events
*/
-void print_tracepoint_events(const struct print_callbacks *print_cb, void *print_state)
+void print_tracepoint_events(const struct print_callbacks *print_cb __maybe_unused, void *print_state __maybe_unused)
+{
+ char *events_path = get_tracing_file("events");
+ int events_fd = open(events_path, O_PATH);
+
+ put_tracing_file(events_path);
+ if (events_fd < 0) {
+ printf("Error: failed to open tracing events directory\n");
+ return;
+ }
+
+#ifdef HAVE_SCANDIRAT_SUPPORT
{
struct dirent **sys_namelist = NULL;
int sys_items = tracing_events__scandir_alphasort(&sys_namelist);
@@ -64,30 +77,34 @@ void print_tracepoint_events(const struct print_callbacks *print_cb, void *print
for (int i = 0; i < sys_items; i++) {
struct dirent *sys_dirent = sys_namelist[i];
struct dirent **evt_namelist = NULL;
- char *dir_path;
+ int dir_fd;
int evt_items;
if (sys_dirent->d_type != DT_DIR ||
!strcmp(sys_dirent->d_name, ".") ||
!strcmp(sys_dirent->d_name, ".."))
- continue;
+ goto next_sys;
- dir_path = get_events_file(sys_dirent->d_name);
- if (!dir_path)
- continue;
+ dir_fd = openat(events_fd, sys_dirent->d_name, O_PATH);
+ if (dir_fd < 0)
+ goto next_sys;
- evt_items = scandir(dir_path, &evt_namelist, NULL, alphasort);
+ evt_items = scandirat(events_fd, sys_dirent->d_name, &evt_namelist, NULL, alphasort);
for (int j = 0; j < evt_items; j++) {
struct dirent *evt_dirent = evt_namelist[j];
char evt_path[MAXPATHLEN];
+ int evt_fd;
if (evt_dirent->d_type != DT_DIR ||
!strcmp(evt_dirent->d_name, ".") ||
!strcmp(evt_dirent->d_name, ".."))
- continue;
+ goto next_evt;
- if (tp_event_has_id(dir_path, evt_dirent) != 0)
- continue;
+ snprintf(evt_path, sizeof(evt_path), "%s/id", evt_dirent->d_name);
+ evt_fd = openat(dir_fd, evt_path, O_RDONLY);
+ if (evt_fd < 0)
+ goto next_evt;
+ close(evt_fd);
snprintf(evt_path, MAXPATHLEN, "%s:%s",
sys_dirent->d_name, evt_dirent->d_name);
@@ -102,12 +119,23 @@ void print_tracepoint_events(const struct print_callbacks *print_cb, void *print
/*desc=*/NULL,
/*long_desc=*/NULL,
/*encoding_desc=*/NULL);
+next_evt:
+ free(evt_namelist[j]);
}
- free(dir_path);
+ close(dir_fd);
free(evt_namelist);
+next_sys:
+ free(sys_namelist[i]);
}
+
free(sys_namelist);
}
+#else
+ printf("\nWARNING: Your libc doesn't have the scandirat function, please ask its maintainers to implement it.\n"
+ " As a rough fallback, please do 'ls %s' to see the available tracepoint events.\n", events_path);
+#endif
+ close(events_fd);
+}
void print_sdt_events(const struct print_callbacks *print_cb, void *print_state)
{
diff --git a/tools/perf/util/print-events.h b/tools/perf/util/print-events.h
index 716dcf4b4859..e75a3d7e3fe3 100644
--- a/tools/perf/util/print-events.h
+++ b/tools/perf/util/print-events.h
@@ -23,6 +23,7 @@ struct print_callbacks {
const char *desc,
const char *long_desc,
const char *expr,
+ const char *threshold,
const char *unit);
};
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 881d94f65a6b..6e2110d605fb 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -135,15 +135,15 @@ static int kernel_get_symbol_address_by_name(const char *name, u64 *addr,
/* ref_reloc_sym is just a label. Need a special fix*/
reloc_sym = kernel_get_ref_reloc_sym(&map);
if (reloc_sym && strcmp(name, reloc_sym->name) == 0)
- *addr = (!map->reloc || reloc) ? reloc_sym->addr :
+ *addr = (!map__reloc(map) || reloc) ? reloc_sym->addr :
reloc_sym->unrelocated_addr;
else {
sym = machine__find_kernel_symbol_by_name(host_machine, name, &map);
if (!sym)
return -ENOENT;
- *addr = map->unmap_ip(map, sym->start) -
- ((reloc) ? 0 : map->reloc) -
- ((reladdr) ? map->start : 0);
+ *addr = map__unmap_ip(map, sym->start) -
+ ((reloc) ? 0 : map__reloc(map)) -
+ ((reladdr) ? map__start(map) : 0);
}
return 0;
}
@@ -151,23 +151,28 @@ static int kernel_get_symbol_address_by_name(const char *name, u64 *addr,
static struct map *kernel_get_module_map(const char *module)
{
struct maps *maps = machine__kernel_maps(host_machine);
- struct map *pos;
+ struct map_rb_node *pos;
/* A file path -- this is an offline module */
if (module && strchr(module, '/'))
return dso__new_map(module);
if (!module) {
- pos = machine__kernel_map(host_machine);
- return map__get(pos);
+ struct map *map = machine__kernel_map(host_machine);
+
+ return map__get(map);
}
maps__for_each_entry(maps, pos) {
/* short_name is "[module]" */
- if (strncmp(pos->dso->short_name + 1, module,
- pos->dso->short_name_len - 2) == 0 &&
- module[pos->dso->short_name_len - 2] == '\0') {
- return map__get(pos);
+ struct dso *dso = map__dso(pos->map);
+ const char *short_name = dso->short_name;
+ u16 short_name_len = dso->short_name_len;
+
+ if (strncmp(short_name + 1, module,
+ short_name_len - 2) == 0 &&
+ module[short_name_len - 2] == '\0') {
+ return map__get(pos->map);
}
}
return NULL;
@@ -178,13 +183,15 @@ struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user)
/* Init maps of given executable or kernel */
if (user) {
struct map *map;
+ struct dso *dso;
map = dso__new_map(target);
- if (map && map->dso) {
- mutex_lock(&map->dso->lock);
- nsinfo__put(map->dso->nsinfo);
- map->dso->nsinfo = nsinfo__get(nsi);
- mutex_unlock(&map->dso->lock);
+ dso = map ? map__dso(map) : NULL;
+ if (dso) {
+ mutex_lock(&dso->lock);
+ nsinfo__put(dso->nsinfo);
+ dso->nsinfo = nsinfo__get(nsi);
+ mutex_unlock(&dso->lock);
}
return map;
} else {
@@ -250,7 +257,7 @@ static bool kprobe_warn_out_range(const char *symbol, u64 address)
map = kernel_get_module_map(NULL);
if (map) {
- ret = address <= map->start || map->end < address;
+ ret = address <= map__start(map) || map__end(map) < address;
if (ret)
pr_warning("%s is out of .text, skip it.\n", symbol);
map__put(map);
@@ -337,7 +344,7 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso)
snprintf(module_name, sizeof(module_name), "[%s]", module);
map = maps__find_by_name(machine__kernel_maps(host_machine), module_name);
if (map) {
- dso = map->dso;
+ dso = map__dso(map);
goto found;
}
pr_debug("Failed to find module %s.\n", module);
@@ -345,7 +352,7 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso)
}
map = machine__kernel_map(host_machine);
- dso = map->dso;
+ dso = map__dso(map);
if (!dso->has_build_id)
dso__read_running_kernel_build_id(dso, host_machine);
@@ -393,7 +400,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
"Consider identifying the final function used at run time and set the probe directly on that.\n",
pp->function);
} else
- address = map->unmap_ip(map, sym->start) - map->reloc;
+ address = map__unmap_ip(map, sym->start) - map__reloc(map);
break;
}
if (!address) {
@@ -859,7 +866,7 @@ post_process_kernel_probe_trace_events(struct probe_trace_event *tevs,
free(tevs[i].point.symbol);
tevs[i].point.symbol = tmp;
tevs[i].point.offset = tevs[i].point.address -
- (map->reloc ? reloc_sym->unrelocated_addr :
+ (map__reloc(map) ? reloc_sym->unrelocated_addr :
reloc_sym->addr);
}
return skipped;
@@ -2242,7 +2249,7 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp,
goto out;
pp->retprobe = tp->retprobe;
- pp->offset = addr - map->unmap_ip(map, sym->start);
+ pp->offset = addr - map__unmap_ip(map, sym->start);
pp->function = strdup(sym->name);
ret = pp->function ? 0 : -ENOMEM;
@@ -3116,7 +3123,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
goto err_out;
}
/* Add one probe point */
- tp->address = map->unmap_ip(map, sym->start) + pp->offset;
+ tp->address = map__unmap_ip(map, sym->start) + pp->offset;
/* Check the kprobe (not in module) is within .text */
if (!pev->uprobes && !pev->target &&
@@ -3733,6 +3740,7 @@ int show_available_funcs(const char *target, struct nsinfo *nsi,
{
struct rb_node *nd;
struct map *map;
+ struct dso *dso;
int ret;
ret = init_probe_symbol_maps(user);
@@ -3758,14 +3766,14 @@ int show_available_funcs(const char *target, struct nsinfo *nsi,
(target) ? : "kernel");
goto end;
}
- if (!dso__sorted_by_name(map->dso))
- dso__sort_by_name(map->dso);
+ dso = map__dso(map);
+ if (!dso__sorted_by_name(dso))
+ dso__sort_by_name(dso);
/* Show all (filtered) symbols */
setup_pager();
- for (nd = rb_first_cached(&map->dso->symbol_names); nd;
- nd = rb_next(nd)) {
+ 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);
if (strfilter__compare(_filter, pos->sym.name))
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 54b49ce85c9f..f171360b0ef4 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -1729,7 +1729,7 @@ int debuginfo__find_probe_point(struct debuginfo *dbg, u64 addr,
addr += baseaddr;
/* Find cu die */
if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr, &cudie)) {
- pr_warning("Failed to find debug information for address %" PRIx64 "\n",
+ pr_warning("Failed to find debug information for address %#" PRIx64 "\n",
addr);
ret = -EINVAL;
goto end;
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 42e8b813d010..0faea4c75eed 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -19,6 +19,7 @@
#include "mmap.h"
#include "stat.h"
#include "metricgroup.h"
+#include "util/bpf-filter.h"
#include "util/env.h"
#include "util/pmu.h"
#include <internal/lib.h>
@@ -83,7 +84,7 @@ const char *perf_env__arch(struct perf_env *env __maybe_unused)
* far, for the perf python binding known usecases, revisit if this become
* necessary.
*/
-struct perf_pmu *evsel__find_pmu(struct evsel *evsel __maybe_unused)
+struct perf_pmu *evsel__find_pmu(const struct evsel *evsel __maybe_unused)
{
return NULL;
}
@@ -93,6 +94,11 @@ int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
return EOF;
}
+bool evsel__is_aux_event(const struct evsel *evsel __maybe_unused)
+{
+ return false;
+}
+
/*
* Add this one here not to drag util/metricgroup.c
*/
@@ -130,6 +136,19 @@ int bpf_counter__disable(struct evsel *evsel __maybe_unused)
return 0;
}
+// not to drag util/bpf-filter.c
+#ifdef HAVE_BPF_SKEL
+int perf_bpf_filter__prepare(struct evsel *evsel __maybe_unused)
+{
+ return 0;
+}
+
+int perf_bpf_filter__destroy(struct evsel *evsel __maybe_unused)
+{
+ return 0;
+}
+#endif
+
/*
* Support debug printing even though util/debug.c is not linked. That means
* implementing 'verbose' and 'eprintf'.
diff --git a/tools/perf/util/record.h b/tools/perf/util/record.h
index 46212bf020cf..a6566134e09e 100644
--- a/tools/perf/util/record.h
+++ b/tools/perf/util/record.h
@@ -65,7 +65,6 @@ struct record_opts {
const char *auxtrace_snapshot_opts;
const char *auxtrace_sample_opts;
bool sample_transaction;
- int initial_delay;
bool use_clockid;
clockid_t clockid;
u64 clockid_res_ns;
diff --git a/tools/perf/util/sample.h b/tools/perf/util/sample.h
index 33b08e0ac746..c92ad0f51ecd 100644
--- a/tools/perf/util/sample.h
+++ b/tools/perf/util/sample.h
@@ -66,6 +66,18 @@ struct aux_sample {
void *data;
};
+struct simd_flags {
+ u64 arch:1, /* architecture (isa) */
+ pred:2; /* predication */
+};
+
+/* simd architecture flags */
+#define SIMD_OP_FLAGS_ARCH_SVE 0x01 /* ARM SVE */
+
+/* simd predicate flags */
+#define SIMD_OP_FLAGS_PRED_PARTIAL 0x01 /* partial predicate */
+#define SIMD_OP_FLAGS_PRED_EMPTY 0x02 /* empty predicate */
+
struct perf_sample {
u64 ip;
u32 pid, tid;
@@ -106,6 +118,7 @@ struct perf_sample {
struct stack_dump user_stack;
struct sample_read read;
struct aux_sample aux_sample;
+ struct simd_flags simd_flags;
};
/*
diff --git a/tools/perf/util/scripting-engines/Build b/tools/perf/util/scripting-engines/Build
index 2c96aa3cc1ec..c220fec97032 100644
--- a/tools/perf/util/scripting-engines/Build
+++ b/tools/perf/util/scripting-engines/Build
@@ -1,7 +1,7 @@
ifeq ($(CONFIG_LIBTRACEEVENT),y)
perf-$(CONFIG_LIBPERL) += trace-event-perl.o
- perf-$(CONFIG_LIBPYTHON) += trace-event-python.o
endif
+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
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index 83fd2fd0ba16..039d0365ad41 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -315,12 +315,14 @@ static SV *perl_process_callchain(struct perf_sample *sample,
if (node->ms.map) {
struct map *map = node->ms.map;
+ struct dso *dso = map ? map__dso(map) : NULL;
const char *dsoname = "[unknown]";
- if (map && map->dso) {
- if (symbol_conf.show_kernel_path && map->dso->long_name)
- dsoname = map->dso->long_name;
+
+ if (dso) {
+ if (symbol_conf.show_kernel_path && dso->long_name)
+ dsoname = dso->long_name;
else
- dsoname = map->dso->name;
+ dsoname = dso->name;
}
if (!hv_stores(elem, "dso", newSVpv(dsoname,0))) {
hv_undef(elem);
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 2c2697c5d025..41d4f9e6a8b7 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -30,7 +30,9 @@
#include <linux/bitmap.h>
#include <linux/compiler.h>
#include <linux/time64.h>
+#ifdef HAVE_LIBTRACEEVENT
#include <traceevent/event-parse.h>
+#endif
#include "../build-id.h"
#include "../counts.h"
@@ -87,18 +89,21 @@ PyMODINIT_FUNC initperf_trace_context(void);
PyMODINIT_FUNC PyInit_perf_trace_context(void);
#endif
+#ifdef HAVE_LIBTRACEEVENT
#define TRACE_EVENT_TYPE_MAX \
((1 << (sizeof(unsigned short) * 8)) - 1)
static DECLARE_BITMAP(events_defined, TRACE_EVENT_TYPE_MAX);
-#define MAX_FIELDS 64
#define N_COMMON_FIELDS 7
-extern struct scripting_context *scripting_context;
-
static char *cur_field_name;
static int zero_flag_atom;
+#endif
+
+#define MAX_FIELDS 64
+
+extern struct scripting_context *scripting_context;
static PyObject *main_module, *main_dict;
@@ -153,6 +158,26 @@ static PyObject *get_handler(const char *handler_name)
return handler;
}
+static void call_object(PyObject *handler, PyObject *args, const char *die_msg)
+{
+ PyObject *retval;
+
+ retval = PyObject_CallObject(handler, args);
+ if (retval == NULL)
+ handler_call_die(die_msg);
+ Py_DECREF(retval);
+}
+
+static void try_call_object(const char *handler_name, PyObject *args)
+{
+ PyObject *handler;
+
+ handler = get_handler(handler_name);
+ if (handler)
+ call_object(handler, args, handler_name);
+}
+
+#ifdef HAVE_LIBTRACEEVENT
static int get_argument_count(PyObject *handler)
{
int arg_count = 0;
@@ -181,25 +206,6 @@ static int get_argument_count(PyObject *handler)
return arg_count;
}
-static void call_object(PyObject *handler, PyObject *args, const char *die_msg)
-{
- PyObject *retval;
-
- retval = PyObject_CallObject(handler, args);
- if (retval == NULL)
- handler_call_die(die_msg);
- Py_DECREF(retval);
-}
-
-static void try_call_object(const char *handler_name, PyObject *args)
-{
- PyObject *handler;
-
- handler = get_handler(handler_name);
- if (handler)
- call_object(handler, args, handler_name);
-}
-
static void define_value(enum tep_print_arg_type field_type,
const char *ev_name,
const char *field_name,
@@ -379,16 +385,18 @@ static PyObject *get_field_numeric_entry(struct tep_event *event,
obj = list;
return obj;
}
+#endif
static const char *get_dsoname(struct map *map)
{
const char *dsoname = "[unknown]";
+ struct dso *dso = map ? map__dso(map) : NULL;
- if (map && map->dso) {
- if (symbol_conf.show_kernel_path && map->dso->long_name)
- dsoname = map->dso->long_name;
+ if (dso) {
+ if (symbol_conf.show_kernel_path && dso->long_name)
+ dsoname = dso->long_name;
else
- dsoname = map->dso->name;
+ dsoname = dso->name;
}
return dsoname;
@@ -401,7 +409,7 @@ static unsigned long get_offset(struct symbol *sym, struct addr_location *al)
if (al->addr < sym->end)
offset = al->addr - sym->start;
else
- offset = al->addr - al->map->start - sym->start;
+ offset = al->addr - map__start(al->map) - sym->start;
return offset;
}
@@ -463,7 +471,7 @@ static PyObject *python_process_callchain(struct perf_sample *sample,
struct addr_location node_al;
unsigned long offset;
- node_al.addr = map->map_ip(map, node->ip);
+ node_al.addr = map__map_ip(map, node->ip);
node_al.map = map;
offset = get_offset(node->ms.sym, &node_al);
@@ -773,15 +781,16 @@ static void set_sym_in_dict(PyObject *dict, struct addr_location *al,
char sbuild_id[SBUILD_ID_SIZE];
if (al->map) {
- pydict_set_item_string_decref(dict, dso_field,
- _PyUnicode_FromString(al->map->dso->name));
- build_id__sprintf(&al->map->dso->bid, sbuild_id);
+ struct dso *dso = map__dso(al->map);
+
+ pydict_set_item_string_decref(dict, dso_field, _PyUnicode_FromString(dso->name));
+ build_id__sprintf(&dso->bid, sbuild_id);
pydict_set_item_string_decref(dict, dso_bid_field,
_PyUnicode_FromString(sbuild_id));
pydict_set_item_string_decref(dict, dso_map_start,
- PyLong_FromUnsignedLong(al->map->start));
+ PyLong_FromUnsignedLong(map__start(al->map)));
pydict_set_item_string_decref(dict, dso_map_end,
- PyLong_FromUnsignedLong(al->map->end));
+ PyLong_FromUnsignedLong(map__end(al->map)));
}
if (al->sym) {
pydict_set_item_string_decref(dict, sym_field,
@@ -906,6 +915,7 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample,
return dict;
}
+#ifdef HAVE_LIBTRACEEVENT
static void python_process_tracepoint(struct perf_sample *sample,
struct evsel *evsel,
struct addr_location *al,
@@ -1035,6 +1045,16 @@ static void python_process_tracepoint(struct perf_sample *sample,
Py_DECREF(t);
}
+#else
+static void python_process_tracepoint(struct perf_sample *sample __maybe_unused,
+ struct evsel *evsel __maybe_unused,
+ struct addr_location *al __maybe_unused,
+ struct addr_location *addr_al __maybe_unused)
+{
+ fprintf(stderr, "Tracepoint events are not supported because "
+ "perf is not linked with libtraceevent.\n");
+}
+#endif
static PyObject *tuple_new(unsigned int sz)
{
@@ -1270,7 +1290,7 @@ static void python_export_sample_table(struct db_export *dbe,
tuple_set_d64(t, 0, es->db_id);
tuple_set_d64(t, 1, es->evsel->db_id);
- tuple_set_d64(t, 2, es->al->maps->machine->db_id);
+ tuple_set_d64(t, 2, maps__machine(es->al->maps)->db_id);
tuple_set_d64(t, 3, es->al->thread->db_id);
tuple_set_d64(t, 4, es->comm_db_id);
tuple_set_d64(t, 5, es->dso_db_id);
@@ -1965,6 +1985,7 @@ static int python_stop_script(void)
return 0;
}
+#ifdef HAVE_LIBTRACEEVENT
static int python_generate_script(struct tep_handle *pevent, const char *outfile)
{
int i, not_first, count, nr_events;
@@ -2155,6 +2176,18 @@ static int python_generate_script(struct tep_handle *pevent, const char *outfile
return 0;
}
+#else
+static int python_generate_script(struct tep_handle *pevent __maybe_unused,
+ const char *outfile __maybe_unused)
+{
+ fprintf(stderr, "Generating Python perf-script is not supported."
+ " Install libtraceevent and rebuild perf to enable it.\n"
+ "For example:\n # apt install libtraceevent-dev (ubuntu)"
+ "\n # yum install libtraceevent-devel (Fedora)"
+ "\n etc.\n");
+ return -1;
+}
+#endif
struct scripting_ops python_scripting_ops = {
.name = "Python",
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 749d5b5c135b..e2806791c76a 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -33,7 +33,7 @@
#include "stat.h"
#include "tsc.h"
#include "ui/progress.h"
-#include "../perf.h"
+#include "util.h"
#include "arch/common.h"
#include "units.h"
#include <internal/lib.h>
@@ -1582,7 +1582,8 @@ static int machines__deliver_event(struct machines *machines,
evlist->stats.total_lost += event->lost.lost;
return tool->lost(tool, event, sample, machine);
case PERF_RECORD_LOST_SAMPLES:
- if (tool->lost_samples == perf_event__process_lost_samples)
+ if (tool->lost_samples == perf_event__process_lost_samples &&
+ !(event->header.misc & PERF_RECORD_MISC_LOST_SAMPLES_BPF))
evlist->stats.total_lost_samples += event->lost_samples.lost;
return tool->lost_samples(tool, event, sample, machine);
case PERF_RECORD_READ:
diff --git a/tools/perf/util/smt.c b/tools/perf/util/smt.c
index 994e9e418227..650e804d0adc 100644
--- a/tools/perf/util/smt.c
+++ b/tools/perf/util/smt.c
@@ -4,7 +4,7 @@
#include "cputopo.h"
#include "smt.h"
-bool smt_on(const struct cpu_topology *topology)
+bool smt_on(void)
{
static bool cached;
static bool cached_result;
@@ -16,22 +16,21 @@ bool smt_on(const struct cpu_topology *topology)
if (sysfs__read_int("devices/system/cpu/smt/active", &fs_value) >= 0)
cached_result = (fs_value == 1);
else
- cached_result = cpu_topology__smt_on(topology);
+ cached_result = cpu_topology__smt_on(online_topology());
cached = true;
return cached_result;
}
-bool core_wide(bool system_wide, const char *user_requested_cpu_list,
- const struct cpu_topology *topology)
+bool core_wide(bool system_wide, const char *user_requested_cpu_list)
{
/* If not everything running on a core is being recorded then we can't use core_wide. */
if (!system_wide)
return false;
/* Cheap case that SMT is disabled and therefore we're inherently core_wide. */
- if (!smt_on(topology))
+ if (!smt_on())
return true;
- return cpu_topology__core_wide(topology, user_requested_cpu_list);
+ return cpu_topology__core_wide(online_topology(), user_requested_cpu_list);
}
diff --git a/tools/perf/util/smt.h b/tools/perf/util/smt.h
index ae9095f2c38c..01441fd2c0a2 100644
--- a/tools/perf/util/smt.h
+++ b/tools/perf/util/smt.h
@@ -2,16 +2,16 @@
#ifndef __SMT_H
#define __SMT_H 1
-struct cpu_topology;
-
-/* Returns true if SMT (aka hyperthreading) is enabled. */
-bool smt_on(const struct cpu_topology *topology);
+/*
+ * Returns true if SMT (aka hyperthreading) is enabled. Determined via sysfs or
+ * the online topology.
+ */
+bool smt_on(void);
/*
* Returns true when system wide and all SMT threads for a core are in the
* user_requested_cpus map.
*/
-bool core_wide(bool system_wide, const char *user_requested_cpu_list,
- const struct cpu_topology *topology);
+bool core_wide(bool system_wide, const char *user_requested_cpu_list);
#endif /* __SMT_H */
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 093a0c8b2e3d..650cd8df4041 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -139,6 +139,52 @@ struct sort_entry sort_thread = {
.se_width_idx = HISTC_THREAD,
};
+/* --sort simd */
+
+static int64_t
+sort__simd_cmp(struct hist_entry *left, struct hist_entry *right)
+{
+ if (left->simd_flags.arch != right->simd_flags.arch)
+ return (int64_t) left->simd_flags.arch - right->simd_flags.arch;
+
+ return (int64_t) left->simd_flags.pred - right->simd_flags.pred;
+}
+
+static const char *hist_entry__get_simd_name(struct simd_flags *simd_flags)
+{
+ u64 arch = simd_flags->arch;
+
+ if (arch & SIMD_OP_FLAGS_ARCH_SVE)
+ return "SVE";
+ else
+ return "n/a";
+}
+
+static int hist_entry__simd_snprintf(struct hist_entry *he, char *bf,
+ size_t size, unsigned int width __maybe_unused)
+{
+ const char *name;
+
+ if (!he->simd_flags.arch)
+ return repsep_snprintf(bf, size, "");
+
+ name = hist_entry__get_simd_name(&he->simd_flags);
+
+ if (he->simd_flags.pred & SIMD_OP_FLAGS_PRED_EMPTY)
+ return repsep_snprintf(bf, size, "[e] %s", name);
+ else if (he->simd_flags.pred & SIMD_OP_FLAGS_PRED_PARTIAL)
+ return repsep_snprintf(bf, size, "[p] %s", name);
+
+ return repsep_snprintf(bf, size, "[.] %s", name);
+}
+
+struct sort_entry sort_simd = {
+ .se_header = "Simd ",
+ .se_cmp = sort__simd_cmp,
+ .se_snprintf = hist_entry__simd_snprintf,
+ .se_width_idx = HISTC_SIMD,
+};
+
/* --sort comm */
/*
@@ -184,8 +230,8 @@ struct sort_entry sort_comm = {
static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r)
{
- struct dso *dso_l = map_l ? map_l->dso : NULL;
- struct dso *dso_r = map_r ? map_r->dso : NULL;
+ struct dso *dso_l = map_l ? map__dso(map_l) : NULL;
+ struct dso *dso_r = map_r ? map__dso(map_r) : NULL;
const char *dso_name_l, *dso_name_r;
if (!dso_l || !dso_r)
@@ -211,13 +257,13 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
static int _hist_entry__dso_snprintf(struct map *map, char *bf,
size_t size, unsigned int width)
{
- if (map && map->dso) {
- const char *dso_name = verbose > 0 ? map->dso->long_name :
- map->dso->short_name;
- return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name);
- }
+ const struct dso *dso = map ? map__dso(map) : NULL;
+ const char *dso_name = "[unknown]";
- return repsep_snprintf(bf, size, "%-*.*s", width, width, "[unknown]");
+ if (dso)
+ dso_name = verbose > 0 ? dso->long_name : dso->short_name;
+
+ return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name);
}
static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf,
@@ -233,7 +279,7 @@ static int hist_entry__dso_filter(struct hist_entry *he, int type, const void *a
if (type != HIST_FILTER__DSO)
return -1;
- return dso && (!he->ms.map || he->ms.map->dso != dso);
+ return dso && (!he->ms.map || map__dso(he->ms.map) != dso);
}
struct sort_entry sort_dso = {
@@ -313,12 +359,12 @@ static int _hist_entry__sym_snprintf(struct map_symbol *ms,
size_t ret = 0;
if (verbose > 0) {
- char o = map ? dso__symtab_origin(map->dso) : '!';
+ struct dso *dso = map ? map__dso(map) : NULL;
+ char o = dso ? dso__symtab_origin(dso) : '!';
u64 rip = ip;
- if (map && map->dso && map->dso->kernel
- && map->dso->adjust_symbols)
- rip = map->unmap_ip(map, ip);
+ if (dso && dso->kernel && dso->adjust_symbols)
+ rip = map__unmap_ip(map, ip);
ret += repsep_snprintf(bf, size, "%-#*llx %c ",
BITS_PER_LONG / 4 + 2, rip, o);
@@ -329,7 +375,7 @@ static int _hist_entry__sym_snprintf(struct map_symbol *ms,
if (sym->type == STT_OBJECT) {
ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
- ip - map->unmap_ip(map, sym->start));
+ ip - map__unmap_ip(map, sym->start));
} else {
ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
width - ret,
@@ -595,7 +641,7 @@ static char *hist_entry__get_srcfile(struct hist_entry *e)
if (!map)
return no_srcfile;
- sf = __get_srcline(map->dso, map__rip_2objdump(map, e->ip),
+ sf = __get_srcline(map__dso(map), map__rip_2objdump(map, e->ip),
e->ms.sym, false, true, true, e->ip);
if (!strcmp(sf, SRCLINE_UNKNOWN))
return no_srcfile;
@@ -611,12 +657,7 @@ static char *hist_entry__get_srcfile(struct hist_entry *e)
static int64_t
sort__srcfile_cmp(struct hist_entry *left, struct hist_entry *right)
{
- if (!left->srcfile)
- left->srcfile = hist_entry__get_srcfile(left);
- if (!right->srcfile)
- right->srcfile = hist_entry__get_srcfile(right);
-
- return strcmp(right->srcfile, left->srcfile);
+ return sort__srcline_cmp(left, right);
}
static int64_t
@@ -762,7 +803,7 @@ static int hist_entry__cgroup_snprintf(struct hist_entry *he,
const char *cgrp_name = "N/A";
if (he->cgroup) {
- struct cgroup *cgrp = cgroup__find(he->ms.maps->machine->env,
+ struct cgroup *cgrp = cgroup__find(maps__machine(he->ms.maps)->env,
he->cgroup);
if (cgrp != NULL)
cgrp_name = cgrp->name;
@@ -941,7 +982,7 @@ static int hist_entry__dso_from_filter(struct hist_entry *he, int type,
return -1;
return dso && (!he->branch_info || !he->branch_info->from.ms.map ||
- he->branch_info->from.ms.map->dso != dso);
+ map__dso(he->branch_info->from.ms.map) != dso);
}
static int64_t
@@ -973,14 +1014,13 @@ static int hist_entry__dso_to_filter(struct hist_entry *he, int type,
return -1;
return dso && (!he->branch_info || !he->branch_info->to.ms.map ||
- he->branch_info->to.ms.map->dso != dso);
+ map__dso(he->branch_info->to.ms.map) != dso);
}
static int64_t
sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right)
{
- struct addr_map_symbol *from_l = &left->branch_info->from;
- struct addr_map_symbol *from_r = &right->branch_info->from;
+ struct addr_map_symbol *from_l, *from_r;
if (!left->branch_info || !right->branch_info)
return cmp_null(left->branch_info, right->branch_info);
@@ -1106,7 +1146,7 @@ static int _hist_entry__addr_snprintf(struct map_symbol *ms,
if (sym->type == STT_OBJECT) {
ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
- ip - map->unmap_ip(map, sym->start));
+ ip - map__unmap_ip(map, sym->start));
} else {
ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
width - ret,
@@ -1465,6 +1505,7 @@ sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
{
u64 l, r;
struct map *l_map, *r_map;
+ struct dso *l_dso, *r_dso;
int rc;
if (!left->mem_info) return -1;
@@ -1484,7 +1525,9 @@ sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
if (!l_map) return -1;
if (!r_map) return 1;
- rc = dso__cmp_id(l_map->dso, r_map->dso);
+ l_dso = map__dso(l_map);
+ r_dso = map__dso(r_map);
+ rc = dso__cmp_id(l_dso, r_dso);
if (rc)
return rc;
/*
@@ -1496,9 +1539,8 @@ sort__dcacheline_cmp(struct hist_entry *left, struct hist_entry *right)
*/
if ((left->cpumode != PERF_RECORD_MISC_KERNEL) &&
- (!(l_map->flags & MAP_SHARED)) &&
- !l_map->dso->id.maj && !l_map->dso->id.min &&
- !l_map->dso->id.ino && !l_map->dso->id.ino_generation) {
+ (!(map__flags(l_map) & MAP_SHARED)) && !l_dso->id.maj && !l_dso->id.min &&
+ !l_dso->id.ino && !l_dso->id.ino_generation) {
/* userspace anonymous */
if (left->thread->pid_ > right->thread->pid_) return -1;
@@ -1526,16 +1568,16 @@ static int hist_entry__dcacheline_snprintf(struct hist_entry *he, char *bf,
if (he->mem_info) {
struct map *map = he->mem_info->daddr.ms.map;
+ struct dso *dso = map ? map__dso(map) : NULL;
addr = cl_address(he->mem_info->daddr.al_addr, chk_double_cl);
ms = &he->mem_info->daddr.ms;
/* print [s] for shared data mmaps */
if ((he->cpumode != PERF_RECORD_MISC_KERNEL) &&
- map && !(map->prot & PROT_EXEC) &&
- (map->flags & MAP_SHARED) &&
- (map->dso->id.maj || map->dso->id.min ||
- map->dso->id.ino || map->dso->id.ino_generation))
+ map && !(map__prot(map) & PROT_EXEC) &&
+ (map__flags(map) & MAP_SHARED) &&
+ (dso->id.maj || dso->id.min || dso->id.ino || dso->id.ino_generation))
level = 's';
else if (!map)
level = 'X';
@@ -2031,9 +2073,8 @@ sort__dso_size_cmp(struct hist_entry *left, struct hist_entry *right)
static int _hist_entry__dso_size_snprintf(struct map *map, char *bf,
size_t bf_size, unsigned int width)
{
- if (map && map->dso)
- return repsep_snprintf(bf, bf_size, "%*d", width,
- map__size(map));
+ if (map && map__dso(map))
+ return repsep_snprintf(bf, bf_size, "%*d", width, map__size(map));
return repsep_snprintf(bf, bf_size, "%*s", width, "unknown");
}
@@ -2062,9 +2103,9 @@ sort__addr_cmp(struct hist_entry *left, struct hist_entry *right)
struct map *right_map = right->ms.map;
if (left_map)
- left_ip = left_map->unmap_ip(left_map, left_ip);
+ left_ip = map__unmap_ip(left_map, left_ip);
if (right_map)
- right_ip = right_map->unmap_ip(right_map, right_ip);
+ right_ip = map__unmap_ip(right_map, right_ip);
return _sort__addr_cmp(left_ip, right_ip);
}
@@ -2076,7 +2117,7 @@ static int hist_entry__addr_snprintf(struct hist_entry *he, char *bf,
struct map *map = he->ms.map;
if (map)
- ip = map->unmap_ip(map, ip);
+ ip = map__unmap_ip(map, ip);
return repsep_snprintf(bf, size, "%-#*llx", width, ip);
}
@@ -2142,6 +2183,7 @@ static struct sort_dimension common_sort_dimensions[] = {
DIM(SORT_ADDR, "addr", sort_addr),
DIM(SORT_LOCAL_RETIRE_LAT, "local_retire_lat", sort_local_p_stage_cyc),
DIM(SORT_GLOBAL_RETIRE_LAT, "retire_lat", sort_global_p_stage_cyc),
+ DIM(SORT_SIMD, "simd", sort_simd)
};
#undef DIM
@@ -2851,7 +2893,7 @@ static struct evsel *find_evsel(struct evlist *evlist, char *event_name)
full_name = !!strchr(event_name, ':');
evlist__for_each_entry(evlist, pos) {
/* case 2 */
- if (full_name && !strcmp(pos->name, event_name))
+ if (full_name && evsel__name_is(pos, event_name))
return pos;
/* case 3 */
if (!full_name && strstr(pos->name, event_name)) {
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index d79a100e5999..ecfb7f1359d5 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -111,6 +111,7 @@ struct hist_entry {
u64 p_stage_cyc;
u8 cpumode;
u8 depth;
+ struct simd_flags simd_flags;
/* We are added by hists__add_dummy_entry. */
bool dummy;
@@ -144,6 +145,7 @@ struct hist_entry {
struct hists *hists;
struct mem_info *mem_info;
struct block_info *block_info;
+ struct kvm_info *kvm_info;
void *raw_data;
u32 raw_size;
int num_res;
@@ -240,6 +242,7 @@ enum sort_type {
SORT_ADDR,
SORT_LOCAL_RETIRE_LAT,
SORT_GLOBAL_RETIRE_LAT,
+ SORT_SIMD,
/* branch stack specific sort keys */
__SORT_BRANCH_STACK,
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index 33321867416b..cfca03abd6f8 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -10,6 +10,8 @@
#include <linux/string.h>
#include <linux/zalloc.h>
+#include <api/io.h>
+
#include "util/dso.h"
#include "util/debug.h"
#include "util/callchain.h"
@@ -366,12 +368,6 @@ void dso__free_a2l(struct dso *dso)
#else /* HAVE_LIBBFD_SUPPORT */
-struct a2l_subprocess {
- struct child_process addr2line;
- FILE *to_child;
- FILE *from_child;
-};
-
static int filename_split(char *filename, unsigned int *line_nr)
{
char *sep;
@@ -393,77 +389,98 @@ static int filename_split(char *filename, unsigned int *line_nr)
return 0;
}
-static void addr2line_subprocess_cleanup(struct a2l_subprocess *a2l)
+static void addr2line_subprocess_cleanup(struct child_process *a2l)
{
- if (a2l->addr2line.pid != -1) {
- kill(a2l->addr2line.pid, SIGKILL);
- finish_command(&a2l->addr2line); /* ignore result, we don't care */
- a2l->addr2line.pid = -1;
- }
-
- if (a2l->to_child != NULL) {
- fclose(a2l->to_child);
- a2l->to_child = NULL;
- }
-
- if (a2l->from_child != NULL) {
- fclose(a2l->from_child);
- a2l->from_child = NULL;
+ if (a2l->pid != -1) {
+ kill(a2l->pid, SIGKILL);
+ finish_command(a2l); /* ignore result, we don't care */
+ a2l->pid = -1;
}
free(a2l);
}
-static struct a2l_subprocess *addr2line_subprocess_init(const char *path)
+static struct child_process *addr2line_subprocess_init(const char *addr2line_path,
+ const char *binary_path)
{
- const char *argv[] = { "addr2line", "-e", path, "-i", "-f", NULL };
- struct a2l_subprocess *a2l = zalloc(sizeof(*a2l));
+ const char *argv[] = {
+ addr2line_path ?: "addr2line",
+ "-e", binary_path,
+ "-i", "-f", NULL
+ };
+ struct child_process *a2l = zalloc(sizeof(*a2l));
int start_command_status = 0;
- if (a2l == NULL)
- goto out;
-
- a2l->to_child = NULL;
- a2l->from_child = NULL;
+ if (a2l == NULL) {
+ pr_err("Failed to allocate memory for addr2line");
+ return NULL;
+ }
- a2l->addr2line.pid = -1;
- a2l->addr2line.in = -1;
- a2l->addr2line.out = -1;
- a2l->addr2line.no_stderr = 1;
+ a2l->pid = -1;
+ a2l->in = -1;
+ a2l->out = -1;
+ a2l->no_stderr = 1;
- a2l->addr2line.argv = argv;
- start_command_status = start_command(&a2l->addr2line);
- a2l->addr2line.argv = NULL; /* it's not used after start_command; avoid dangling pointers */
+ a2l->argv = argv;
+ start_command_status = start_command(a2l);
+ a2l->argv = NULL; /* it's not used after start_command; avoid dangling pointers */
if (start_command_status != 0) {
- pr_warning("could not start addr2line for %s: start_command return code %d\n",
- path,
- start_command_status);
- goto out;
- }
-
- a2l->to_child = fdopen(a2l->addr2line.in, "w");
- if (a2l->to_child == NULL) {
- pr_warning("could not open write-stream to addr2line of %s\n", path);
- goto out;
- }
-
- a2l->from_child = fdopen(a2l->addr2line.out, "r");
- if (a2l->from_child == NULL) {
- pr_warning("could not open read-stream from addr2line of %s\n", path);
- goto out;
+ pr_warning("could not start addr2line (%s) for %s: start_command return code %d\n",
+ addr2line_path, binary_path, start_command_status);
+ addr2line_subprocess_cleanup(a2l);
+ return NULL;
}
return a2l;
+}
-out:
- if (a2l)
- addr2line_subprocess_cleanup(a2l);
+enum a2l_style {
+ BROKEN,
+ GNU_BINUTILS,
+ LLVM,
+};
- return NULL;
+static enum a2l_style addr2line_configure(struct child_process *a2l)
+{
+ static bool cached;
+ static enum a2l_style style;
+
+ if (!cached) {
+ char buf[128];
+ struct io io;
+ int ch;
+
+ if (write(a2l->in, ",\n", 2) != 2)
+ return BROKEN;
+
+ io__init(&io, a2l->out, buf, sizeof(buf));
+ ch = io__get_char(&io);
+ if (ch == ',') {
+ style = LLVM;
+ cached = true;
+ } else if (ch == '?') {
+ style = GNU_BINUTILS;
+ cached = true;
+ } else {
+ style = BROKEN;
+ }
+ do {
+ ch = io__get_char(&io);
+ } while (ch > 0 && ch != '\n');
+ if (style == GNU_BINUTILS) {
+ do {
+ ch = io__get_char(&io);
+ } while (ch > 0 && ch != '\n');
+ }
+ /* Ignore SIGPIPE in the event addr2line exits. */
+ signal(SIGPIPE, SIG_IGN);
+ }
+ return style;
}
-static int read_addr2line_record(struct a2l_subprocess *a2l,
+static int read_addr2line_record(struct io *io,
+ enum a2l_style style,
char **function,
char **filename,
unsigned int *line_nr)
@@ -488,19 +505,25 @@ static int read_addr2line_record(struct a2l_subprocess *a2l,
if (line_nr != NULL)
*line_nr = 0;
- if (getline(&line, &line_len, a2l->from_child) < 0 || !line_len)
+ if (io__getline(io, &line, &line_len) < 0 || !line_len)
goto error;
+ if (style == LLVM && line_len == 2 && line[0] == ',') {
+ zfree(&line);
+ return 0;
+ }
+
if (function != NULL)
*function = strdup(strim(line));
zfree(&line);
line_len = 0;
- if (getline(&line, &line_len, a2l->from_child) < 0 || !line_len)
+ if (io__getline(io, &line, &line_len) < 0 || !line_len)
goto error;
- if (filename_split(line, line_nr == NULL ? &dummy_line_nr : line_nr) == 0) {
+ if (filename_split(line, line_nr == NULL ? &dummy_line_nr : line_nr) == 0 &&
+ style == GNU_BINUTILS) {
ret = 0;
goto error;
}
@@ -541,19 +564,25 @@ static int addr2line(const char *dso_name, u64 addr,
struct inline_node *node,
struct symbol *sym __maybe_unused)
{
- struct a2l_subprocess *a2l = dso->a2l;
+ struct child_process *a2l = dso->a2l;
char *record_function = NULL;
char *record_filename = NULL;
unsigned int record_line_nr = 0;
int record_status = -1;
int ret = 0;
size_t inline_count = 0;
+ int len;
+ char buf[128];
+ ssize_t written;
+ struct io io;
+ enum a2l_style a2l_style;
if (!a2l) {
if (!filename__has_section(dso_name, ".debug_line"))
goto out;
- dso->a2l = addr2line_subprocess_init(dso_name);
+ dso->a2l = addr2line_subprocess_init(symbol_conf.addr2line_path,
+ dso_name);
a2l = dso->a2l;
}
@@ -562,23 +591,34 @@ static int addr2line(const char *dso_name, u64 addr,
pr_warning("%s %s: addr2line_subprocess_init failed\n", __func__, dso_name);
goto out;
}
+ a2l_style = addr2line_configure(a2l);
+ if (a2l_style == BROKEN) {
+ if (!symbol_conf.disable_add2line_warn)
+ pr_warning("%s: addr2line configuration failed\n", __func__);
+ goto out;
+ }
/*
* Send our request and then *deliberately* send something that can't be interpreted as
* a valid address to ask addr2line about (namely, ","). This causes addr2line to first
* write out the answer to our request, in an unbounded/unknown number of records, and
- * then to write out the lines "??" and "??:0", so that we can detect when it has
- * finished giving us anything useful. We have to be careful about the first record,
- * though, because it may be genuinely unknown, in which case we'll get two sets of
- * "??"/"??:0" lines.
+ * then to write out the lines "??" and "??:0", for GNU binutils, or "," for
+ * llvm-addr2line, so that we can detect when it has finished giving us anything
+ * useful. With GNU binutils, we have to be careful about the first record, though,
+ * because it may be genuinely unknown, in which case we'll get two sets of "??"/"??:0"
+ * lines.
*/
- if (fprintf(a2l->to_child, "%016"PRIx64"\n,\n", addr) < 0 || fflush(a2l->to_child) != 0) {
+ len = snprintf(buf, sizeof(buf), "%016"PRIx64"\n,\n", addr);
+ written = len > 0 ? write(a2l->in, buf, len) : -1;
+ if (written != len) {
if (!symbol_conf.disable_add2line_warn)
pr_warning("%s %s: could not send request\n", __func__, dso_name);
goto out;
}
+ io__init(&io, a2l->out, buf, sizeof(buf));
- switch (read_addr2line_record(a2l, &record_function, &record_filename, &record_line_nr)) {
+ switch (read_addr2line_record(&io, a2l_style,
+ &record_function, &record_filename, &record_line_nr)) {
case -1:
if (!symbol_conf.disable_add2line_warn)
pr_warning("%s %s: could not read first record\n", __func__, dso_name);
@@ -588,7 +628,7 @@ static int addr2line(const char *dso_name, u64 addr,
* The first record was invalid, so return failure, but first read another
* record, since we asked a junk question and have to clear the answer out.
*/
- switch (read_addr2line_record(a2l, NULL, NULL, NULL)) {
+ switch (read_addr2line_record(&io, a2l_style, NULL, NULL, NULL)) {
case -1:
if (!symbol_conf.disable_add2line_warn)
pr_warning("%s %s: could not read delimiter record\n",
@@ -626,7 +666,8 @@ static int addr2line(const char *dso_name, u64 addr,
}
/* We have to read the records even if we don't care about the inline info. */
- while ((record_status = read_addr2line_record(a2l,
+ while ((record_status = read_addr2line_record(&io,
+ a2l_style,
&record_function,
&record_filename,
&record_line_nr)) == 1) {
@@ -650,7 +691,7 @@ out:
void dso__free_a2l(struct dso *dso)
{
- struct a2l_subprocess *a2l = dso->a2l;
+ struct child_process *a2l = dso->a2l;
if (!a2l)
return;
diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c
index 1b5cb20efd23..73b2ff2ddf29 100644
--- a/tools/perf/util/stat-display.c
+++ b/tools/perf/util/stat-display.c
@@ -183,7 +183,7 @@ static void print_cgroup(struct perf_stat_config *config, struct cgroup *cgrp)
}
static void print_aggr_id_std(struct perf_stat_config *config,
- struct evsel *evsel, struct aggr_cpu_id id, int nr)
+ struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr)
{
FILE *output = config->output;
int idx = config->aggr_mode;
@@ -225,11 +225,11 @@ static void print_aggr_id_std(struct perf_stat_config *config,
return;
}
- fprintf(output, "%-*s %*d ", aggr_header_lens[idx], buf, 4, nr);
+ fprintf(output, "%-*s %*d ", aggr_header_lens[idx], buf, 4, aggr_nr);
}
static void print_aggr_id_csv(struct perf_stat_config *config,
- struct evsel *evsel, struct aggr_cpu_id id, int nr)
+ struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr)
{
FILE *output = config->output;
const char *sep = config->csv_sep;
@@ -237,19 +237,19 @@ static void print_aggr_id_csv(struct perf_stat_config *config,
switch (config->aggr_mode) {
case AGGR_CORE:
fprintf(output, "S%d-D%d-C%d%s%d%s",
- id.socket, id.die, id.core, sep, nr, sep);
+ id.socket, id.die, id.core, sep, aggr_nr, sep);
break;
case AGGR_DIE:
fprintf(output, "S%d-D%d%s%d%s",
- id.socket, id.die, sep, nr, sep);
+ id.socket, id.die, sep, aggr_nr, sep);
break;
case AGGR_SOCKET:
fprintf(output, "S%d%s%d%s",
- id.socket, sep, nr, sep);
+ id.socket, sep, aggr_nr, sep);
break;
case AGGR_NODE:
fprintf(output, "N%d%s%d%s",
- id.node, sep, nr, sep);
+ id.node, sep, aggr_nr, sep);
break;
case AGGR_NONE:
if (evsel->percore && !config->percore_show_thread) {
@@ -275,26 +275,26 @@ static void print_aggr_id_csv(struct perf_stat_config *config,
}
static void print_aggr_id_json(struct perf_stat_config *config,
- struct evsel *evsel, struct aggr_cpu_id id, int nr)
+ struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr)
{
FILE *output = config->output;
switch (config->aggr_mode) {
case AGGR_CORE:
fprintf(output, "\"core\" : \"S%d-D%d-C%d\", \"aggregate-number\" : %d, ",
- id.socket, id.die, id.core, nr);
+ id.socket, id.die, id.core, aggr_nr);
break;
case AGGR_DIE:
fprintf(output, "\"die\" : \"S%d-D%d\", \"aggregate-number\" : %d, ",
- id.socket, id.die, nr);
+ id.socket, id.die, aggr_nr);
break;
case AGGR_SOCKET:
fprintf(output, "\"socket\" : \"S%d\", \"aggregate-number\" : %d, ",
- id.socket, nr);
+ id.socket, aggr_nr);
break;
case AGGR_NODE:
fprintf(output, "\"node\" : \"N%d\", \"aggregate-number\" : %d, ",
- id.node, nr);
+ id.node, aggr_nr);
break;
case AGGR_NONE:
if (evsel->percore && !config->percore_show_thread) {
@@ -319,14 +319,14 @@ static void print_aggr_id_json(struct perf_stat_config *config,
}
static void aggr_printout(struct perf_stat_config *config,
- struct evsel *evsel, struct aggr_cpu_id id, int nr)
+ struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr)
{
if (config->json_output)
- print_aggr_id_json(config, evsel, id, nr);
+ print_aggr_id_json(config, evsel, id, aggr_nr);
else if (config->csv_output)
- print_aggr_id_csv(config, evsel, id, nr);
+ print_aggr_id_csv(config, evsel, id, aggr_nr);
else
- print_aggr_id_std(config, evsel, id, nr);
+ print_aggr_id_std(config, evsel, id, aggr_nr);
}
struct outstate {
@@ -335,7 +335,7 @@ struct outstate {
bool first;
const char *prefix;
int nfields;
- int nr;
+ int aggr_nr;
struct aggr_cpu_id id;
struct evsel *evsel;
struct cgroup *cgrp;
@@ -355,7 +355,7 @@ static void do_new_line_std(struct perf_stat_config *config,
fputc('\n', os->fh);
if (os->prefix)
fputs(os->prefix, os->fh);
- aggr_printout(config, os->evsel, os->id, os->nr);
+ aggr_printout(config, os->evsel, os->id, os->aggr_nr);
if (config->aggr_mode == AGGR_NONE)
fprintf(os->fh, " ");
fprintf(os->fh, " ");
@@ -396,7 +396,7 @@ static void new_line_csv(struct perf_stat_config *config, void *ctx)
fputc('\n', os->fh);
if (os->prefix)
fprintf(os->fh, "%s", os->prefix);
- aggr_printout(config, os->evsel, os->id, os->nr);
+ aggr_printout(config, os->evsel, os->id, os->aggr_nr);
for (i = 0; i < os->nfields; i++)
fputs(config->csv_sep, os->fh);
}
@@ -444,7 +444,7 @@ static void new_line_json(struct perf_stat_config *config, void *ctx)
fputs("\n{", os->fh);
if (os->prefix)
fprintf(os->fh, "%s", os->prefix);
- aggr_printout(config, os->evsel, os->id, os->nr);
+ aggr_printout(config, os->evsel, os->id, os->aggr_nr);
}
/* Filter out some columns that don't work well in metrics only mode */
@@ -645,10 +645,10 @@ static void print_counter_value(struct perf_stat_config *config,
}
static void abs_printout(struct perf_stat_config *config,
- struct aggr_cpu_id id, int nr,
+ struct aggr_cpu_id id, int aggr_nr,
struct evsel *evsel, double avg, bool ok)
{
- aggr_printout(config, evsel, id, nr);
+ aggr_printout(config, evsel, id, aggr_nr);
print_counter_value(config, evsel, avg, ok);
print_cgroup(config, evsel->cgrp);
}
@@ -678,7 +678,7 @@ static bool is_mixed_hw_group(struct evsel *counter)
}
static void printout(struct perf_stat_config *config, struct outstate *os,
- double uval, u64 run, u64 ena, double noise, int map_idx)
+ double uval, u64 run, u64 ena, double noise, int aggr_idx)
{
struct perf_stat_output_ctx out;
print_metric_t pm;
@@ -721,15 +721,15 @@ static void printout(struct perf_stat_config *config, struct outstate *os,
out.force_header = false;
if (!config->metric_only) {
- abs_printout(config, os->id, os->nr, counter, uval, ok);
+ abs_printout(config, os->id, os->aggr_nr, counter, uval, ok);
print_noise(config, counter, noise, /*before_metric=*/true);
print_running(config, run, ena, /*before_metric=*/true);
}
if (ok) {
- perf_stat__print_shadow_stats(config, counter, uval, map_idx,
- &out, &config->metric_events, &rt_stat);
+ perf_stat__print_shadow_stats(config, counter, uval, aggr_idx,
+ &out, &config->metric_events);
} else {
pm(config, os, /*color=*/NULL, /*format=*/NULL, /*unit=*/"", /*val=*/0);
}
@@ -747,7 +747,7 @@ static void uniquify_event_name(struct evsel *counter)
int ret = 0;
if (counter->uniquified_name || counter->use_config_name ||
- !counter->pmu_name || !strncmp(counter->name, counter->pmu_name,
+ !counter->pmu_name || !strncmp(evsel__name(counter), counter->pmu_name,
strlen(counter->pmu_name)))
return;
@@ -833,20 +833,20 @@ static bool should_skip_zero_counter(struct perf_stat_config *config,
}
static void print_counter_aggrdata(struct perf_stat_config *config,
- struct evsel *counter, int s,
+ struct evsel *counter, int aggr_idx,
struct outstate *os)
{
FILE *output = config->output;
u64 ena, run, val;
double uval;
struct perf_stat_evsel *ps = counter->stats;
- struct perf_stat_aggr *aggr = &ps->aggr[s];
- struct aggr_cpu_id id = config->aggr_map->map[s];
+ struct perf_stat_aggr *aggr = &ps->aggr[aggr_idx];
+ struct aggr_cpu_id id = config->aggr_map->map[aggr_idx];
double avg = aggr->counts.val;
bool metric_only = config->metric_only;
os->id = id;
- os->nr = aggr->nr;
+ os->aggr_nr = aggr->nr;
os->evsel = counter;
/* Skip already merged uncore/hybrid events */
@@ -874,7 +874,7 @@ static void print_counter_aggrdata(struct perf_stat_config *config,
uval = val * counter->scale;
- printout(config, os, uval, run, ena, avg, s);
+ printout(config, os, uval, run, ena, avg, aggr_idx);
if (!metric_only)
fputc('\n', output);
@@ -925,7 +925,7 @@ static void print_aggr(struct perf_stat_config *config,
struct outstate *os)
{
struct evsel *counter;
- int s;
+ int aggr_idx;
if (!config->aggr_map || !config->aggr_get_id)
return;
@@ -934,11 +934,11 @@ static void print_aggr(struct perf_stat_config *config,
* With metric_only everything is on a single line.
* Without each counter has its own line.
*/
- for (s = 0; s < config->aggr_map->nr; s++) {
- print_metric_begin(config, evlist, os, s);
+ cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) {
+ print_metric_begin(config, evlist, os, aggr_idx);
evlist__for_each_entry(evlist, counter) {
- print_counter_aggrdata(config, counter, s, os);
+ print_counter_aggrdata(config, counter, aggr_idx, os);
}
print_metric_end(config, os);
}
@@ -949,7 +949,7 @@ static void print_aggr_cgroup(struct perf_stat_config *config,
struct outstate *os)
{
struct evsel *counter, *evsel;
- int s;
+ int aggr_idx;
if (!config->aggr_map || !config->aggr_get_id)
return;
@@ -960,14 +960,14 @@ static void print_aggr_cgroup(struct perf_stat_config *config,
os->cgrp = evsel->cgrp;
- for (s = 0; s < config->aggr_map->nr; s++) {
- print_metric_begin(config, evlist, os, s);
+ cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) {
+ print_metric_begin(config, evlist, os, aggr_idx);
evlist__for_each_entry(evlist, counter) {
if (counter->cgrp != os->cgrp)
continue;
- print_counter_aggrdata(config, counter, s, os);
+ print_counter_aggrdata(config, counter, aggr_idx, os);
}
print_metric_end(config, os);
}
@@ -977,14 +977,14 @@ static void print_aggr_cgroup(struct perf_stat_config *config,
static void print_counter(struct perf_stat_config *config,
struct evsel *counter, struct outstate *os)
{
- int s;
+ int aggr_idx;
/* AGGR_THREAD doesn't have config->aggr_get_id */
if (!config->aggr_map)
return;
- for (s = 0; s < config->aggr_map->nr; s++) {
- print_counter_aggrdata(config, counter, s, os);
+ cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) {
+ print_counter_aggrdata(config, counter, aggr_idx, os);
}
}
@@ -1003,23 +1003,23 @@ static void print_no_aggr_metric(struct perf_stat_config *config,
u64 ena, run, val;
double uval;
struct perf_stat_evsel *ps = counter->stats;
- int counter_idx = perf_cpu_map__idx(evsel__cpus(counter), cpu);
+ int aggr_idx = perf_cpu_map__idx(evsel__cpus(counter), cpu);
- if (counter_idx < 0)
+ if (aggr_idx < 0)
continue;
os->evsel = counter;
os->id = aggr_cpu_id__cpu(cpu, /*data=*/NULL);
if (first) {
- print_metric_begin(config, evlist, os, counter_idx);
+ print_metric_begin(config, evlist, os, aggr_idx);
first = false;
}
- val = ps->aggr[counter_idx].counts.val;
- ena = ps->aggr[counter_idx].counts.ena;
- run = ps->aggr[counter_idx].counts.run;
+ val = ps->aggr[aggr_idx].counts.val;
+ ena = ps->aggr[aggr_idx].counts.ena;
+ run = ps->aggr[aggr_idx].counts.run;
uval = val * counter->scale;
- printout(config, os, uval, run, ena, 1.0, counter_idx);
+ printout(config, os, uval, run, ena, 1.0, aggr_idx);
}
if (!first)
print_metric_end(config, os);
@@ -1089,8 +1089,7 @@ static void print_metric_headers(struct perf_stat_config *config,
perf_stat__print_shadow_stats(config, counter, 0,
0,
&out,
- &config->metric_events,
- &rt_stat);
+ &config->metric_events);
}
if (!config->json_output)
@@ -1339,7 +1338,7 @@ static void print_percore(struct perf_stat_config *config,
bool metric_only = config->metric_only;
FILE *output = config->output;
struct cpu_aggr_map *core_map;
- int s, c, i;
+ int aggr_idx, core_map_len = 0;
if (!config->aggr_map || !config->aggr_get_id)
return;
@@ -1347,18 +1346,22 @@ static void print_percore(struct perf_stat_config *config,
if (config->percore_show_thread)
return print_counter(config, counter, os);
+ /*
+ * core_map will hold the aggr_cpu_id for the cores that have been
+ * printed so that each core is printed just once.
+ */
core_map = cpu_aggr_map__empty_new(config->aggr_map->nr);
if (core_map == NULL) {
fprintf(output, "Cannot allocate per-core aggr map for display\n");
return;
}
- for (s = 0, c = 0; s < config->aggr_map->nr; s++) {
- struct perf_cpu curr_cpu = config->aggr_map->map[s].cpu;
+ cpu_aggr_map__for_each_idx(aggr_idx, config->aggr_map) {
+ struct perf_cpu curr_cpu = config->aggr_map->map[aggr_idx].cpu;
struct aggr_cpu_id core_id = aggr_cpu_id__core(curr_cpu, NULL);
bool found = false;
- for (i = 0; i < c; i++) {
+ for (int i = 0; i < core_map_len; i++) {
if (aggr_cpu_id__equal(&core_map->map[i], &core_id)) {
found = true;
break;
@@ -1367,9 +1370,9 @@ static void print_percore(struct perf_stat_config *config,
if (found)
continue;
- print_counter_aggrdata(config, counter, s, os);
+ print_counter_aggrdata(config, counter, aggr_idx, os);
- core_map->map[c++] = core_id;
+ core_map->map[core_map_len++] = core_id;
}
free(core_map);
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c
index 806b32156459..eeccab6751d7 100644
--- a/tools/perf/util/stat-shadow.c
+++ b/tools/perf/util/stat-shadow.c
@@ -16,142 +16,43 @@
#include "iostat.h"
#include "util/hashmap.h"
-/*
- * AGGR_GLOBAL: Use CPU 0
- * AGGR_SOCKET: Use first CPU of socket
- * AGGR_DIE: Use first CPU of die
- * AGGR_CORE: Use first CPU of core
- * AGGR_NONE: Use matching CPU
- * AGGR_THREAD: Not supported?
- */
-
-struct runtime_stat rt_stat;
struct stats walltime_nsecs_stats;
struct rusage_stats ru_stats;
-struct saved_value {
- struct rb_node rb_node;
- struct evsel *evsel;
- enum stat_type type;
- int ctx;
- int map_idx; /* cpu or thread map index */
- struct cgroup *cgrp;
- struct stats stats;
- u64 metric_total;
- int metric_other;
+enum {
+ CTX_BIT_USER = 1 << 0,
+ CTX_BIT_KERNEL = 1 << 1,
+ CTX_BIT_HV = 1 << 2,
+ CTX_BIT_HOST = 1 << 3,
+ CTX_BIT_IDLE = 1 << 4,
+ CTX_BIT_MAX = 1 << 5,
};
-static int saved_value_cmp(struct rb_node *rb_node, const void *entry)
-{
- struct saved_value *a = container_of(rb_node,
- struct saved_value,
- rb_node);
- const struct saved_value *b = entry;
-
- if (a->map_idx != b->map_idx)
- return a->map_idx - b->map_idx;
-
- /*
- * Previously the rbtree was used to link generic metrics.
- * The keys were evsel/cpu. Now the rbtree is extended to support
- * per-thread shadow stats. For shadow stats case, the keys
- * are cpu/type/ctx/stat (evsel is NULL). For generic metrics
- * case, the keys are still evsel/cpu (type/ctx/stat are 0 or NULL).
- */
- if (a->type != b->type)
- return a->type - b->type;
-
- if (a->ctx != b->ctx)
- return a->ctx - b->ctx;
-
- if (a->cgrp != b->cgrp)
- return (char *)a->cgrp < (char *)b->cgrp ? -1 : +1;
-
- if (a->evsel == b->evsel)
- return 0;
- if ((char *)a->evsel < (char *)b->evsel)
- return -1;
- return +1;
-}
-
-static struct rb_node *saved_value_new(struct rblist *rblist __maybe_unused,
- const void *entry)
-{
- struct saved_value *nd = malloc(sizeof(struct saved_value));
-
- if (!nd)
- return NULL;
- memcpy(nd, entry, sizeof(struct saved_value));
- return &nd->rb_node;
-}
-
-static void saved_value_delete(struct rblist *rblist __maybe_unused,
- struct rb_node *rb_node)
-{
- struct saved_value *v;
-
- BUG_ON(!rb_node);
- v = container_of(rb_node, struct saved_value, rb_node);
- free(v);
-}
-
-static struct saved_value *saved_value_lookup(struct evsel *evsel,
- int map_idx,
- bool create,
- enum stat_type type,
- int ctx,
- struct runtime_stat *st,
- struct cgroup *cgrp)
-{
- struct rblist *rblist;
- struct rb_node *nd;
- struct saved_value dm = {
- .map_idx = map_idx,
- .evsel = evsel,
- .type = type,
- .ctx = ctx,
- .cgrp = cgrp,
- };
-
- rblist = &st->value_list;
-
- /* don't use context info for clock events */
- if (type == STAT_NSECS)
- dm.ctx = 0;
-
- nd = rblist__find(rblist, &dm);
- if (nd)
- return container_of(nd, struct saved_value, rb_node);
- if (create) {
- rblist__add_node(rblist, &dm);
- nd = rblist__find(rblist, &dm);
- if (nd)
- return container_of(nd, struct saved_value, rb_node);
- }
- return NULL;
-}
-
-void runtime_stat__init(struct runtime_stat *st)
-{
- struct rblist *rblist = &st->value_list;
-
- rblist__init(rblist);
- rblist->node_cmp = saved_value_cmp;
- rblist->node_new = saved_value_new;
- rblist->node_delete = saved_value_delete;
-}
-
-void runtime_stat__exit(struct runtime_stat *st)
-{
- rblist__exit(&st->value_list);
-}
-
-void perf_stat__init_shadow_stats(void)
-{
- runtime_stat__init(&rt_stat);
-}
+enum stat_type {
+ STAT_NONE = 0,
+ STAT_NSECS,
+ STAT_CYCLES,
+ STAT_INSTRUCTIONS,
+ STAT_STALLED_CYCLES_FRONT,
+ STAT_STALLED_CYCLES_BACK,
+ STAT_BRANCHES,
+ STAT_BRANCH_MISS,
+ STAT_CACHE_REFS,
+ STAT_CACHE_MISSES,
+ STAT_L1_DCACHE,
+ STAT_L1_ICACHE,
+ STAT_LL_CACHE,
+ STAT_ITLB_CACHE,
+ STAT_DTLB_CACHE,
+ STAT_L1D_MISS,
+ STAT_L1I_MISS,
+ STAT_LL_MISS,
+ STAT_DTLB_MISS,
+ STAT_ITLB_MISS,
+ STAT_MAX
+};
-static int evsel_context(struct evsel *evsel)
+static int evsel_context(const struct evsel *evsel)
{
int ctx = 0;
@@ -169,553 +70,307 @@ static int evsel_context(struct evsel *evsel)
return ctx;
}
-static void reset_stat(struct runtime_stat *st)
-{
- struct rblist *rblist;
- struct rb_node *pos, *next;
-
- rblist = &st->value_list;
- next = rb_first_cached(&rblist->entries);
- while (next) {
- pos = next;
- next = rb_next(pos);
- memset(&container_of(pos, struct saved_value, rb_node)->stats,
- 0,
- sizeof(struct stats));
- }
-}
-
void perf_stat__reset_shadow_stats(void)
{
- reset_stat(&rt_stat);
memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats));
memset(&ru_stats, 0, sizeof(ru_stats));
}
-void perf_stat__reset_shadow_per_stat(struct runtime_stat *st)
+static enum stat_type evsel__stat_type(const struct evsel *evsel)
+{
+ /* Fake perf_hw_cache_op_id values for use with evsel__match. */
+ u64 PERF_COUNT_hw_cache_l1d_miss = PERF_COUNT_HW_CACHE_L1D |
+ ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
+ ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16);
+ u64 PERF_COUNT_hw_cache_l1i_miss = PERF_COUNT_HW_CACHE_L1I |
+ ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
+ ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16);
+ u64 PERF_COUNT_hw_cache_ll_miss = PERF_COUNT_HW_CACHE_LL |
+ ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
+ ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16);
+ u64 PERF_COUNT_hw_cache_dtlb_miss = PERF_COUNT_HW_CACHE_DTLB |
+ ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
+ ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16);
+ u64 PERF_COUNT_hw_cache_itlb_miss = PERF_COUNT_HW_CACHE_ITLB |
+ ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
+ ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16);
+
+ if (evsel__is_clock(evsel))
+ return STAT_NSECS;
+ else if (evsel__match(evsel, HARDWARE, HW_CPU_CYCLES))
+ return STAT_CYCLES;
+ else if (evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS))
+ return STAT_INSTRUCTIONS;
+ else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND))
+ return STAT_STALLED_CYCLES_FRONT;
+ else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND))
+ return STAT_STALLED_CYCLES_BACK;
+ else if (evsel__match(evsel, HARDWARE, HW_BRANCH_INSTRUCTIONS))
+ return STAT_BRANCHES;
+ else if (evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES))
+ return STAT_BRANCH_MISS;
+ else if (evsel__match(evsel, HARDWARE, HW_CACHE_REFERENCES))
+ return STAT_CACHE_REFS;
+ else if (evsel__match(evsel, HARDWARE, HW_CACHE_MISSES))
+ return STAT_CACHE_MISSES;
+ else if (evsel__match(evsel, HW_CACHE, HW_CACHE_L1D))
+ return STAT_L1_DCACHE;
+ else if (evsel__match(evsel, HW_CACHE, HW_CACHE_L1I))
+ return STAT_L1_ICACHE;
+ else if (evsel__match(evsel, HW_CACHE, HW_CACHE_LL))
+ return STAT_LL_CACHE;
+ else if (evsel__match(evsel, HW_CACHE, HW_CACHE_DTLB))
+ return STAT_DTLB_CACHE;
+ else if (evsel__match(evsel, HW_CACHE, HW_CACHE_ITLB))
+ return STAT_ITLB_CACHE;
+ else if (evsel__match(evsel, HW_CACHE, hw_cache_l1d_miss))
+ return STAT_L1D_MISS;
+ else if (evsel__match(evsel, HW_CACHE, hw_cache_l1i_miss))
+ return STAT_L1I_MISS;
+ else if (evsel__match(evsel, HW_CACHE, hw_cache_ll_miss))
+ return STAT_LL_MISS;
+ else if (evsel__match(evsel, HW_CACHE, hw_cache_dtlb_miss))
+ return STAT_DTLB_MISS;
+ else if (evsel__match(evsel, HW_CACHE, hw_cache_itlb_miss))
+ return STAT_ITLB_MISS;
+ return STAT_NONE;
+}
+
+static const char *get_ratio_color(const double ratios[3], double val)
{
- reset_stat(st);
-}
-
-struct runtime_stat_data {
- int ctx;
- struct cgroup *cgrp;
-};
-
-static void update_runtime_stat(struct runtime_stat *st,
- enum stat_type type,
- int map_idx, u64 count,
- struct runtime_stat_data *rsd)
-{
- struct saved_value *v = saved_value_lookup(NULL, map_idx, true, type,
- rsd->ctx, st, rsd->cgrp);
-
- if (v)
- update_stats(&v->stats, count);
-}
-
-/*
- * Update various tracking values we maintain to print
- * more semantic information such as miss/hit ratios,
- * instruction rates, etc:
- */
-void perf_stat__update_shadow_stats(struct evsel *counter, u64 count,
- int map_idx, struct runtime_stat *st)
-{
- u64 count_ns = count;
- struct saved_value *v;
- struct runtime_stat_data rsd = {
- .ctx = evsel_context(counter),
- .cgrp = counter->cgrp,
- };
-
- count *= counter->scale;
-
- if (evsel__is_clock(counter))
- update_runtime_stat(st, STAT_NSECS, map_idx, count_ns, &rsd);
- else if (evsel__match(counter, HARDWARE, HW_CPU_CYCLES))
- update_runtime_stat(st, STAT_CYCLES, map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, CYCLES_IN_TX))
- update_runtime_stat(st, STAT_CYCLES_IN_TX, map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TRANSACTION_START))
- update_runtime_stat(st, STAT_TRANSACTION, map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, ELISION_START))
- update_runtime_stat(st, STAT_ELISION, map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TOPDOWN_TOTAL_SLOTS))
- update_runtime_stat(st, STAT_TOPDOWN_TOTAL_SLOTS,
- map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TOPDOWN_SLOTS_ISSUED))
- update_runtime_stat(st, STAT_TOPDOWN_SLOTS_ISSUED,
- map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TOPDOWN_SLOTS_RETIRED))
- update_runtime_stat(st, STAT_TOPDOWN_SLOTS_RETIRED,
- map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TOPDOWN_FETCH_BUBBLES))
- update_runtime_stat(st, STAT_TOPDOWN_FETCH_BUBBLES,
- map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TOPDOWN_RECOVERY_BUBBLES))
- update_runtime_stat(st, STAT_TOPDOWN_RECOVERY_BUBBLES,
- map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TOPDOWN_RETIRING))
- update_runtime_stat(st, STAT_TOPDOWN_RETIRING,
- map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TOPDOWN_BAD_SPEC))
- update_runtime_stat(st, STAT_TOPDOWN_BAD_SPEC,
- map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TOPDOWN_FE_BOUND))
- update_runtime_stat(st, STAT_TOPDOWN_FE_BOUND,
- map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TOPDOWN_BE_BOUND))
- update_runtime_stat(st, STAT_TOPDOWN_BE_BOUND,
- map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TOPDOWN_HEAVY_OPS))
- update_runtime_stat(st, STAT_TOPDOWN_HEAVY_OPS,
- map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TOPDOWN_BR_MISPREDICT))
- update_runtime_stat(st, STAT_TOPDOWN_BR_MISPREDICT,
- map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TOPDOWN_FETCH_LAT))
- update_runtime_stat(st, STAT_TOPDOWN_FETCH_LAT,
- map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, TOPDOWN_MEM_BOUND))
- update_runtime_stat(st, STAT_TOPDOWN_MEM_BOUND,
- map_idx, count, &rsd);
- else if (evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND))
- update_runtime_stat(st, STAT_STALLED_CYCLES_FRONT,
- map_idx, count, &rsd);
- else if (evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_BACKEND))
- update_runtime_stat(st, STAT_STALLED_CYCLES_BACK,
- map_idx, count, &rsd);
- else if (evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS))
- update_runtime_stat(st, STAT_BRANCHES, map_idx, count, &rsd);
- else if (evsel__match(counter, HARDWARE, HW_CACHE_REFERENCES))
- update_runtime_stat(st, STAT_CACHEREFS, map_idx, count, &rsd);
- else if (evsel__match(counter, HW_CACHE, HW_CACHE_L1D))
- update_runtime_stat(st, STAT_L1_DCACHE, map_idx, count, &rsd);
- else if (evsel__match(counter, HW_CACHE, HW_CACHE_L1I))
- update_runtime_stat(st, STAT_L1_ICACHE, map_idx, count, &rsd);
- else if (evsel__match(counter, HW_CACHE, HW_CACHE_LL))
- update_runtime_stat(st, STAT_LL_CACHE, map_idx, count, &rsd);
- else if (evsel__match(counter, HW_CACHE, HW_CACHE_DTLB))
- update_runtime_stat(st, STAT_DTLB_CACHE, map_idx, count, &rsd);
- else if (evsel__match(counter, HW_CACHE, HW_CACHE_ITLB))
- update_runtime_stat(st, STAT_ITLB_CACHE, map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, SMI_NUM))
- update_runtime_stat(st, STAT_SMI_NUM, map_idx, count, &rsd);
- else if (perf_stat_evsel__is(counter, APERF))
- update_runtime_stat(st, STAT_APERF, map_idx, count, &rsd);
-
- if (counter->collect_stat) {
- v = saved_value_lookup(counter, map_idx, true, STAT_NONE, 0, st,
- rsd.cgrp);
- update_stats(&v->stats, count);
- if (counter->metric_leader)
- v->metric_total += count;
- } else if (counter->metric_leader && !counter->merged_stat) {
- v = saved_value_lookup(counter->metric_leader,
- map_idx, true, STAT_NONE, 0, st, rsd.cgrp);
- v->metric_total += count;
- v->metric_other++;
- }
-}
-
-/* used for get_ratio_color() */
-enum grc_type {
- GRC_STALLED_CYCLES_FE,
- GRC_STALLED_CYCLES_BE,
- GRC_CACHE_MISSES,
- GRC_MAX_NR
-};
-
-static const char *get_ratio_color(enum grc_type type, double ratio)
-{
- static const double grc_table[GRC_MAX_NR][3] = {
- [GRC_STALLED_CYCLES_FE] = { 50.0, 30.0, 10.0 },
- [GRC_STALLED_CYCLES_BE] = { 75.0, 50.0, 20.0 },
- [GRC_CACHE_MISSES] = { 20.0, 10.0, 5.0 },
- };
const char *color = PERF_COLOR_NORMAL;
- if (ratio > grc_table[type][0])
+ if (val > ratios[0])
color = PERF_COLOR_RED;
- else if (ratio > grc_table[type][1])
+ else if (val > ratios[1])
color = PERF_COLOR_MAGENTA;
- else if (ratio > grc_table[type][2])
+ else if (val > ratios[2])
color = PERF_COLOR_YELLOW;
return color;
}
-static double runtime_stat_avg(struct runtime_stat *st,
- enum stat_type type, int map_idx,
- struct runtime_stat_data *rsd)
-{
- struct saved_value *v;
-
- v = saved_value_lookup(NULL, map_idx, false, type, rsd->ctx, st, rsd->cgrp);
- if (!v)
- return 0.0;
-
- return avg_stats(&v->stats);
+static double find_stat(const struct evsel *evsel, int aggr_idx, enum stat_type type)
+{
+ const struct evsel *cur;
+ int evsel_ctx = evsel_context(evsel);
+
+ evlist__for_each_entry(evsel->evlist, cur) {
+ struct perf_stat_aggr *aggr;
+
+ /* Ignore the evsel that is being searched from. */
+ if (evsel == cur)
+ continue;
+
+ /* Ignore evsels that are part of different groups. */
+ if (evsel->core.leader->nr_members > 1 &&
+ evsel->core.leader != cur->core.leader)
+ continue;
+ /* Ignore evsels with mismatched modifiers. */
+ if (evsel_ctx != evsel_context(cur))
+ continue;
+ /* Ignore if not the cgroup we're looking for. */
+ if (evsel->cgrp != cur->cgrp)
+ continue;
+ /* Ignore if not the stat we're looking for. */
+ if (type != evsel__stat_type(cur))
+ continue;
+
+ aggr = &cur->stats->aggr[aggr_idx];
+ if (type == STAT_NSECS)
+ return aggr->counts.val;
+ return aggr->counts.val * cur->scale;
+ }
+ return 0.0;
}
-static double runtime_stat_n(struct runtime_stat *st,
- enum stat_type type, int map_idx,
- struct runtime_stat_data *rsd)
+static void print_ratio(struct perf_stat_config *config,
+ const struct evsel *evsel, int aggr_idx,
+ double numerator, struct perf_stat_output_ctx *out,
+ enum stat_type denominator_type,
+ const double color_ratios[3], const char *unit)
{
- struct saved_value *v;
+ double denominator = find_stat(evsel, aggr_idx, denominator_type);
- v = saved_value_lookup(NULL, map_idx, false, type, rsd->ctx, st, rsd->cgrp);
- if (!v)
- return 0.0;
+ if (numerator && denominator) {
+ double ratio = numerator / denominator * 100.0;
+ const char *color = get_ratio_color(color_ratios, ratio);
- return v->stats.n;
+ out->print_metric(config, out->ctx, color, "%7.2f%%", unit, ratio);
+ } else
+ out->print_metric(config, out->ctx, NULL, NULL, unit, 0);
}
-static void print_stalled_cycles_frontend(struct perf_stat_config *config,
- int map_idx, double avg,
- struct perf_stat_output_ctx *out,
- struct runtime_stat *st,
- struct runtime_stat_data *rsd)
+static void print_stalled_cycles_front(struct perf_stat_config *config,
+ const struct evsel *evsel,
+ int aggr_idx, double stalled,
+ struct perf_stat_output_ctx *out)
{
- double total, ratio = 0.0;
- const char *color;
-
- total = runtime_stat_avg(st, STAT_CYCLES, map_idx, rsd);
-
- if (total)
- ratio = avg / total * 100.0;
-
- color = get_ratio_color(GRC_STALLED_CYCLES_FE, ratio);
+ static const double color_ratios[3] = {50.0, 30.0, 10.0};
- if (ratio)
- out->print_metric(config, out->ctx, color, "%7.2f%%", "frontend cycles idle",
- ratio);
- else
- out->print_metric(config, out->ctx, NULL, NULL, "frontend cycles idle", 0);
+ print_ratio(config, evsel, aggr_idx, stalled, out, STAT_CYCLES, color_ratios,
+ "frontend cycles idle");
}
-static void print_stalled_cycles_backend(struct perf_stat_config *config,
- int map_idx, double avg,
- struct perf_stat_output_ctx *out,
- struct runtime_stat *st,
- struct runtime_stat_data *rsd)
+static void print_stalled_cycles_back(struct perf_stat_config *config,
+ const struct evsel *evsel,
+ int aggr_idx, double stalled,
+ struct perf_stat_output_ctx *out)
{
- double total, ratio = 0.0;
- const char *color;
+ static const double color_ratios[3] = {75.0, 50.0, 20.0};
- total = runtime_stat_avg(st, STAT_CYCLES, map_idx, rsd);
-
- if (total)
- ratio = avg / total * 100.0;
-
- color = get_ratio_color(GRC_STALLED_CYCLES_BE, ratio);
-
- out->print_metric(config, out->ctx, color, "%7.2f%%", "backend cycles idle", ratio);
+ print_ratio(config, evsel, aggr_idx, stalled, out, STAT_CYCLES, color_ratios,
+ "backend cycles idle");
}
-static void print_branch_misses(struct perf_stat_config *config,
- int map_idx, double avg,
- struct perf_stat_output_ctx *out,
- struct runtime_stat *st,
- struct runtime_stat_data *rsd)
+static void print_branch_miss(struct perf_stat_config *config,
+ const struct evsel *evsel,
+ int aggr_idx, double misses,
+ struct perf_stat_output_ctx *out)
{
- double total, ratio = 0.0;
- const char *color;
-
- total = runtime_stat_avg(st, STAT_BRANCHES, map_idx, rsd);
+ static const double color_ratios[3] = {20.0, 10.0, 5.0};
- if (total)
- ratio = avg / total * 100.0;
-
- color = get_ratio_color(GRC_CACHE_MISSES, ratio);
-
- out->print_metric(config, out->ctx, color, "%7.2f%%", "of all branches", ratio);
+ print_ratio(config, evsel, aggr_idx, misses, out, STAT_BRANCHES, color_ratios,
+ "of all branches");
}
-static void print_l1_dcache_misses(struct perf_stat_config *config,
- int map_idx, double avg,
- struct perf_stat_output_ctx *out,
- struct runtime_stat *st,
- struct runtime_stat_data *rsd)
+static void print_l1d_miss(struct perf_stat_config *config,
+ const struct evsel *evsel,
+ int aggr_idx, double misses,
+ struct perf_stat_output_ctx *out)
{
- double total, ratio = 0.0;
- const char *color;
-
- total = runtime_stat_avg(st, STAT_L1_DCACHE, map_idx, rsd);
+ static const double color_ratios[3] = {20.0, 10.0, 5.0};
- if (total)
- ratio = avg / total * 100.0;
-
- color = get_ratio_color(GRC_CACHE_MISSES, ratio);
-
- out->print_metric(config, out->ctx, color, "%7.2f%%", "of all L1-dcache accesses", ratio);
+ print_ratio(config, evsel, aggr_idx, misses, out, STAT_L1_DCACHE, color_ratios,
+ "of all L1-dcache accesses");
}
-static void print_l1_icache_misses(struct perf_stat_config *config,
- int map_idx, double avg,
- struct perf_stat_output_ctx *out,
- struct runtime_stat *st,
- struct runtime_stat_data *rsd)
+static void print_l1i_miss(struct perf_stat_config *config,
+ const struct evsel *evsel,
+ int aggr_idx, double misses,
+ struct perf_stat_output_ctx *out)
{
- double total, ratio = 0.0;
- const char *color;
-
- total = runtime_stat_avg(st, STAT_L1_ICACHE, map_idx, rsd);
-
- if (total)
- ratio = avg / total * 100.0;
+ static const double color_ratios[3] = {20.0, 10.0, 5.0};
- color = get_ratio_color(GRC_CACHE_MISSES, ratio);
- out->print_metric(config, out->ctx, color, "%7.2f%%", "of all L1-icache accesses", ratio);
+ print_ratio(config, evsel, aggr_idx, misses, out, STAT_L1_ICACHE, color_ratios,
+ "of all L1-icache accesses");
}
-static void print_dtlb_cache_misses(struct perf_stat_config *config,
- int map_idx, double avg,
- struct perf_stat_output_ctx *out,
- struct runtime_stat *st,
- struct runtime_stat_data *rsd)
+static void print_ll_miss(struct perf_stat_config *config,
+ const struct evsel *evsel,
+ int aggr_idx, double misses,
+ struct perf_stat_output_ctx *out)
{
- double total, ratio = 0.0;
- const char *color;
+ static const double color_ratios[3] = {20.0, 10.0, 5.0};
- total = runtime_stat_avg(st, STAT_DTLB_CACHE, map_idx, rsd);
-
- if (total)
- ratio = avg / total * 100.0;
-
- color = get_ratio_color(GRC_CACHE_MISSES, ratio);
- out->print_metric(config, out->ctx, color, "%7.2f%%", "of all dTLB cache accesses", ratio);
+ print_ratio(config, evsel, aggr_idx, misses, out, STAT_LL_CACHE, color_ratios,
+ "of all L1-icache accesses");
}
-static void print_itlb_cache_misses(struct perf_stat_config *config,
- int map_idx, double avg,
- struct perf_stat_output_ctx *out,
- struct runtime_stat *st,
- struct runtime_stat_data *rsd)
+static void print_dtlb_miss(struct perf_stat_config *config,
+ const struct evsel *evsel,
+ int aggr_idx, double misses,
+ struct perf_stat_output_ctx *out)
{
- double total, ratio = 0.0;
- const char *color;
-
- total = runtime_stat_avg(st, STAT_ITLB_CACHE, map_idx, rsd);
-
- if (total)
- ratio = avg / total * 100.0;
+ static const double color_ratios[3] = {20.0, 10.0, 5.0};
- color = get_ratio_color(GRC_CACHE_MISSES, ratio);
- out->print_metric(config, out->ctx, color, "%7.2f%%", "of all iTLB cache accesses", ratio);
+ print_ratio(config, evsel, aggr_idx, misses, out, STAT_DTLB_CACHE, color_ratios,
+ "of all dTLB cache accesses");
}
-static void print_ll_cache_misses(struct perf_stat_config *config,
- int map_idx, double avg,
- struct perf_stat_output_ctx *out,
- struct runtime_stat *st,
- struct runtime_stat_data *rsd)
+static void print_itlb_miss(struct perf_stat_config *config,
+ const struct evsel *evsel,
+ int aggr_idx, double misses,
+ struct perf_stat_output_ctx *out)
{
- double total, ratio = 0.0;
- const char *color;
+ static const double color_ratios[3] = {20.0, 10.0, 5.0};
- total = runtime_stat_avg(st, STAT_LL_CACHE, map_idx, rsd);
-
- if (total)
- ratio = avg / total * 100.0;
-
- color = get_ratio_color(GRC_CACHE_MISSES, ratio);
- out->print_metric(config, out->ctx, color, "%7.2f%%", "of all LL-cache accesses", ratio);
-}
-
-/*
- * High level "TopDown" CPU core pipe line bottleneck break down.
- *
- * Basic concept following
- * Yasin, A Top Down Method for Performance analysis and Counter architecture
- * ISPASS14
- *
- * The CPU pipeline is divided into 4 areas that can be bottlenecks:
- *
- * Frontend -> Backend -> Retiring
- * BadSpeculation in addition means out of order execution that is thrown away
- * (for example branch mispredictions)
- * Frontend is instruction decoding.
- * Backend is execution, like computation and accessing data in memory
- * Retiring is good execution that is not directly bottlenecked
- *
- * The formulas are computed in slots.
- * A slot is an entry in the pipeline each for the pipeline width
- * (for example a 4-wide pipeline has 4 slots for each cycle)
- *
- * Formulas:
- * BadSpeculation = ((SlotsIssued - SlotsRetired) + RecoveryBubbles) /
- * TotalSlots
- * Retiring = SlotsRetired / TotalSlots
- * FrontendBound = FetchBubbles / TotalSlots
- * BackendBound = 1.0 - BadSpeculation - Retiring - FrontendBound
- *
- * The kernel provides the mapping to the low level CPU events and any scaling
- * needed for the CPU pipeline width, for example:
- *
- * TotalSlots = Cycles * 4
- *
- * The scaling factor is communicated in the sysfs unit.
- *
- * In some cases the CPU may not be able to measure all the formulas due to
- * missing events. In this case multiple formulas are combined, as possible.
- *
- * Full TopDown supports more levels to sub-divide each area: for example
- * BackendBound into computing bound and memory bound. For now we only
- * support Level 1 TopDown.
- */
-
-static double sanitize_val(double x)
-{
- if (x < 0 && x >= -0.02)
- return 0.0;
- return x;
+ print_ratio(config, evsel, aggr_idx, misses, out, STAT_ITLB_CACHE, color_ratios,
+ "of all iTLB cache accesses");
}
-static double td_total_slots(int map_idx, struct runtime_stat *st,
- struct runtime_stat_data *rsd)
+static void print_cache_miss(struct perf_stat_config *config,
+ const struct evsel *evsel,
+ int aggr_idx, double misses,
+ struct perf_stat_output_ctx *out)
{
- return runtime_stat_avg(st, STAT_TOPDOWN_TOTAL_SLOTS, map_idx, rsd);
-}
+ static const double color_ratios[3] = {20.0, 10.0, 5.0};
-static double td_bad_spec(int map_idx, struct runtime_stat *st,
- struct runtime_stat_data *rsd)
-{
- double bad_spec = 0;
- double total_slots;
- double total;
-
- total = runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_ISSUED, map_idx, rsd) -
- runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_RETIRED, map_idx, rsd) +
- runtime_stat_avg(st, STAT_TOPDOWN_RECOVERY_BUBBLES, map_idx, rsd);
-
- total_slots = td_total_slots(map_idx, st, rsd);
- if (total_slots)
- bad_spec = total / total_slots;
- return sanitize_val(bad_spec);
+ print_ratio(config, evsel, aggr_idx, misses, out, STAT_CACHE_REFS, color_ratios,
+ "of all cache refs");
}
-static double td_retiring(int map_idx, struct runtime_stat *st,
- struct runtime_stat_data *rsd)
+static void print_instructions(struct perf_stat_config *config,
+ const struct evsel *evsel,
+ int aggr_idx, double instructions,
+ struct perf_stat_output_ctx *out)
{
- double retiring = 0;
- double total_slots = td_total_slots(map_idx, st, rsd);
- double ret_slots = runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_RETIRED,
- map_idx, rsd);
-
- if (total_slots)
- retiring = ret_slots / total_slots;
- return retiring;
-}
-
-static double td_fe_bound(int map_idx, struct runtime_stat *st,
- struct runtime_stat_data *rsd)
-{
- double fe_bound = 0;
- double total_slots = td_total_slots(map_idx, st, rsd);
- double fetch_bub = runtime_stat_avg(st, STAT_TOPDOWN_FETCH_BUBBLES,
- map_idx, rsd);
-
- if (total_slots)
- fe_bound = fetch_bub / total_slots;
- return fe_bound;
-}
-
-static double td_be_bound(int map_idx, struct runtime_stat *st,
- struct runtime_stat_data *rsd)
-{
- double sum = (td_fe_bound(map_idx, st, rsd) +
- td_bad_spec(map_idx, st, rsd) +
- td_retiring(map_idx, st, rsd));
- if (sum == 0)
- return 0;
- return sanitize_val(1.0 - sum);
+ print_metric_t print_metric = out->print_metric;
+ void *ctxp = out->ctx;
+ double cycles = find_stat(evsel, aggr_idx, STAT_CYCLES);
+ double max_stalled = max(find_stat(evsel, aggr_idx, STAT_STALLED_CYCLES_FRONT),
+ find_stat(evsel, aggr_idx, STAT_STALLED_CYCLES_BACK));
+
+ if (cycles) {
+ print_metric(config, ctxp, NULL, "%7.2f ", "insn per cycle",
+ instructions / cycles);
+ } else
+ print_metric(config, ctxp, NULL, NULL, "insn per cycle", 0);
+
+ if (max_stalled && instructions) {
+ out->new_line(config, ctxp);
+ print_metric(config, ctxp, NULL, "%7.2f ", "stalled cycles per insn",
+ max_stalled / instructions);
+ }
}
-/*
- * Kernel reports metrics multiplied with slots. To get back
- * the ratios we need to recreate the sum.
- */
-
-static double td_metric_ratio(int map_idx, enum stat_type type,
- struct runtime_stat *stat,
- struct runtime_stat_data *rsd)
+static void print_cycles(struct perf_stat_config *config,
+ const struct evsel *evsel,
+ int aggr_idx, double cycles,
+ struct perf_stat_output_ctx *out)
{
- double sum = runtime_stat_avg(stat, STAT_TOPDOWN_RETIRING, map_idx, rsd) +
- runtime_stat_avg(stat, STAT_TOPDOWN_FE_BOUND, map_idx, rsd) +
- runtime_stat_avg(stat, STAT_TOPDOWN_BE_BOUND, map_idx, rsd) +
- runtime_stat_avg(stat, STAT_TOPDOWN_BAD_SPEC, map_idx, rsd);
- double d = runtime_stat_avg(stat, type, map_idx, rsd);
-
- if (sum)
- return d / sum;
- return 0;
-}
+ double nsecs = find_stat(evsel, aggr_idx, STAT_NSECS);
-/*
- * ... but only if most of the values are actually available.
- * We allow two missing.
- */
+ if (cycles && nsecs) {
+ double ratio = cycles / nsecs;
-static bool full_td(int map_idx, struct runtime_stat *stat,
- struct runtime_stat_data *rsd)
-{
- int c = 0;
-
- if (runtime_stat_avg(stat, STAT_TOPDOWN_RETIRING, map_idx, rsd) > 0)
- c++;
- if (runtime_stat_avg(stat, STAT_TOPDOWN_BE_BOUND, map_idx, rsd) > 0)
- c++;
- if (runtime_stat_avg(stat, STAT_TOPDOWN_FE_BOUND, map_idx, rsd) > 0)
- c++;
- if (runtime_stat_avg(stat, STAT_TOPDOWN_BAD_SPEC, map_idx, rsd) > 0)
- c++;
- return c >= 2;
+ out->print_metric(config, out->ctx, NULL, "%8.3f", "GHz", ratio);
+ } else
+ out->print_metric(config, out->ctx, NULL, NULL, "GHz", 0);
}
-static void print_smi_cost(struct perf_stat_config *config, int map_idx,
- struct perf_stat_output_ctx *out,
- struct runtime_stat *st,
- struct runtime_stat_data *rsd)
+static void print_nsecs(struct perf_stat_config *config,
+ const struct evsel *evsel,
+ int aggr_idx __maybe_unused, double nsecs,
+ struct perf_stat_output_ctx *out)
{
- double smi_num, aperf, cycles, cost = 0.0;
- const char *color = NULL;
-
- smi_num = runtime_stat_avg(st, STAT_SMI_NUM, map_idx, rsd);
- aperf = runtime_stat_avg(st, STAT_APERF, map_idx, rsd);
- cycles = runtime_stat_avg(st, STAT_CYCLES, map_idx, rsd);
-
- if ((cycles == 0) || (aperf == 0))
- return;
-
- if (smi_num)
- cost = (aperf - cycles) / aperf * 100.00;
+ print_metric_t print_metric = out->print_metric;
+ void *ctxp = out->ctx;
+ double wall_time = avg_stats(&walltime_nsecs_stats);
- if (cost > 10)
- color = PERF_COLOR_RED;
- out->print_metric(config, out->ctx, color, "%8.1f%%", "SMI cycles%", cost);
- out->print_metric(config, out->ctx, NULL, "%4.0f", "SMI#", smi_num);
+ if (wall_time) {
+ print_metric(config, ctxp, NULL, "%8.3f", "CPUs utilized",
+ nsecs / (wall_time * evsel->scale));
+ } else
+ print_metric(config, ctxp, NULL, NULL, "CPUs utilized", 0);
}
static int prepare_metric(struct evsel **metric_events,
struct metric_ref *metric_refs,
struct expr_parse_ctx *pctx,
- int map_idx,
- struct runtime_stat *st)
+ int aggr_idx)
{
- double scale;
- char *n;
- int i, j, ret;
+ int i;
for (i = 0; metric_events[i]; i++) {
- struct saved_value *v;
- struct stats *stats;
- u64 metric_total = 0;
- int source_count;
+ char *n;
+ double val;
+ int source_count = 0;
if (evsel__is_tool(metric_events[i])) {
- source_count = 1;
+ struct stats *stats;
+ double scale;
+
switch (metric_events[i]->tool_event) {
case PERF_TOOL_DURATION_TIME:
stats = &walltime_nsecs_stats;
@@ -739,35 +394,32 @@ static int prepare_metric(struct evsel **metric_events,
pr_err("Unknown tool event '%s'", evsel__name(metric_events[i]));
abort();
}
+ val = avg_stats(stats) * scale;
+ source_count = 1;
} else {
- v = saved_value_lookup(metric_events[i], map_idx, false,
- STAT_NONE, 0, st,
- metric_events[i]->cgrp);
- if (!v)
+ struct perf_stat_evsel *ps = metric_events[i]->stats;
+ struct perf_stat_aggr *aggr = &ps->aggr[aggr_idx];
+
+ if (!aggr)
break;
- stats = &v->stats;
+
/*
* If an event was scaled during stat gathering, reverse
* the scale before computing the metric.
*/
- scale = 1.0 / metric_events[i]->scale;
-
+ val = aggr->counts.val * (1.0 / metric_events[i]->scale);
source_count = evsel__source_count(metric_events[i]);
-
- if (v->metric_other)
- metric_total = v->metric_total * scale;
}
n = strdup(evsel__metric_id(metric_events[i]));
if (!n)
return -ENOMEM;
- expr__add_id_val_source_count(pctx, n,
- metric_total ? : avg_stats(stats) * scale,
- source_count);
+ expr__add_id_val_source_count(pctx, n, val, source_count);
}
- for (j = 0; metric_refs && metric_refs[j].metric_name; j++) {
- ret = expr__add_ref(pctx, &metric_refs[j]);
+ for (int j = 0; metric_refs && metric_refs[j].metric_name; j++) {
+ int ret = expr__add_ref(pctx, &metric_refs[j]);
+
if (ret)
return ret;
}
@@ -777,21 +429,22 @@ static int prepare_metric(struct evsel **metric_events,
static void generic_metric(struct perf_stat_config *config,
const char *metric_expr,
+ const char *metric_threshold,
struct evsel **metric_events,
struct metric_ref *metric_refs,
char *name,
const char *metric_name,
const char *metric_unit,
int runtime,
- int map_idx,
- struct perf_stat_output_ctx *out,
- struct runtime_stat *st)
+ int aggr_idx,
+ struct perf_stat_output_ctx *out)
{
print_metric_t print_metric = out->print_metric;
struct expr_parse_ctx *pctx;
- double ratio, scale;
+ double ratio, scale, threshold;
int i;
void *ctxp = out->ctx;
+ const char *color = NULL;
pctx = expr__ctx_new();
if (!pctx)
@@ -801,7 +454,7 @@ static void generic_metric(struct perf_stat_config *config,
pctx->sctx.user_requested_cpu_list = strdup(config->user_requested_cpu_list);
pctx->sctx.runtime = runtime;
pctx->sctx.system_wide = config->system_wide;
- i = prepare_metric(metric_events, metric_refs, pctx, map_idx, st);
+ i = prepare_metric(metric_events, metric_refs, pctx, aggr_idx);
if (i < 0) {
expr__ctx_free(pctx);
return;
@@ -811,6 +464,13 @@ static void generic_metric(struct perf_stat_config *config,
char *unit;
char metric_bf[64];
+ if (metric_threshold &&
+ expr__parse(&threshold, pctx, metric_threshold) == 0 &&
+ !isnan(threshold)) {
+ color = fpclassify(threshold) == FP_ZERO
+ ? PERF_COLOR_GREEN : PERF_COLOR_RED;
+ }
+
if (metric_unit && metric_name) {
if (perf_pmu__convert_scale(metric_unit,
&unit, &scale) >= 0) {
@@ -823,22 +483,22 @@ static void generic_metric(struct perf_stat_config *config,
scnprintf(metric_bf, sizeof(metric_bf),
"%s %s", unit, metric_name);
- print_metric(config, ctxp, NULL, "%8.1f",
+ print_metric(config, ctxp, color, "%8.1f",
metric_bf, ratio);
} else {
- print_metric(config, ctxp, NULL, "%8.2f",
+ print_metric(config, ctxp, color, "%8.2f",
metric_name ?
metric_name :
out->force_header ? name : "",
ratio);
}
} else {
- print_metric(config, ctxp, NULL, NULL,
+ print_metric(config, ctxp, color, /*unit=*/NULL,
out->force_header ?
(metric_name ? metric_name : name) : "", 0);
}
} else {
- print_metric(config, ctxp, NULL, NULL,
+ print_metric(config, ctxp, color, /*unit=*/NULL,
out->force_header ?
(metric_name ? metric_name : name) : "", 0);
}
@@ -846,7 +506,7 @@ static void generic_metric(struct perf_stat_config *config,
expr__ctx_free(pctx);
}
-double test_generic_metric(struct metric_expr *mexp, int map_idx, struct runtime_stat *st)
+double test_generic_metric(struct metric_expr *mexp, int aggr_idx)
{
struct expr_parse_ctx *pctx;
double ratio = 0.0;
@@ -855,7 +515,7 @@ double test_generic_metric(struct metric_expr *mexp, int map_idx, struct runtime
if (!pctx)
return NAN;
- if (prepare_metric(mexp->metric_events, mexp->metric_refs, pctx, map_idx, st) < 0)
+ if (prepare_metric(mexp->metric_events, mexp->metric_refs, pctx, aggr_idx) < 0)
goto out;
if (expr__parse(&ratio, pctx, mexp->metric_expr))
@@ -868,344 +528,55 @@ out:
void perf_stat__print_shadow_stats(struct perf_stat_config *config,
struct evsel *evsel,
- double avg, int map_idx,
+ double avg, int aggr_idx,
struct perf_stat_output_ctx *out,
- struct rblist *metric_events,
- struct runtime_stat *st)
-{
- void *ctxp = out->ctx;
- print_metric_t print_metric = out->print_metric;
- double total, ratio = 0.0, total2;
- const char *color = NULL;
- struct runtime_stat_data rsd = {
- .ctx = evsel_context(evsel),
- .cgrp = evsel->cgrp,
+ struct rblist *metric_events)
+{
+ typedef void (*stat_print_function_t)(struct perf_stat_config *config,
+ const struct evsel *evsel,
+ int aggr_idx, double misses,
+ struct perf_stat_output_ctx *out);
+ static const stat_print_function_t stat_print_function[STAT_MAX] = {
+ [STAT_INSTRUCTIONS] = print_instructions,
+ [STAT_BRANCH_MISS] = print_branch_miss,
+ [STAT_L1D_MISS] = print_l1d_miss,
+ [STAT_L1I_MISS] = print_l1i_miss,
+ [STAT_DTLB_MISS] = print_dtlb_miss,
+ [STAT_ITLB_MISS] = print_itlb_miss,
+ [STAT_LL_MISS] = print_ll_miss,
+ [STAT_CACHE_MISSES] = print_cache_miss,
+ [STAT_STALLED_CYCLES_FRONT] = print_stalled_cycles_front,
+ [STAT_STALLED_CYCLES_BACK] = print_stalled_cycles_back,
+ [STAT_CYCLES] = print_cycles,
+ [STAT_NSECS] = print_nsecs,
};
+ print_metric_t print_metric = out->print_metric;
+ void *ctxp = out->ctx;
struct metric_event *me;
int num = 1;
if (config->iostat_run) {
iostat_print_metric(config, evsel, out);
- } else if (evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) {
- total = runtime_stat_avg(st, STAT_CYCLES, map_idx, &rsd);
-
- if (total) {
- ratio = avg / total;
- print_metric(config, ctxp, NULL, "%7.2f ",
- "insn per cycle", ratio);
- } else {
- print_metric(config, ctxp, NULL, NULL, "insn per cycle", 0);
- }
-
- total = runtime_stat_avg(st, STAT_STALLED_CYCLES_FRONT, map_idx, &rsd);
-
- total = max(total, runtime_stat_avg(st,
- STAT_STALLED_CYCLES_BACK,
- map_idx, &rsd));
-
- if (total && avg) {
- out->new_line(config, ctxp);
- ratio = total / avg;
- print_metric(config, ctxp, NULL, "%7.2f ",
- "stalled cycles per insn",
- ratio);
- }
- } else if (evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) {
- if (runtime_stat_n(st, STAT_BRANCHES, map_idx, &rsd) != 0)
- print_branch_misses(config, map_idx, avg, out, st, &rsd);
- else
- print_metric(config, ctxp, NULL, NULL, "of all branches", 0);
- } else if (
- evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
- evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_L1D |
- ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
- ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
-
- if (runtime_stat_n(st, STAT_L1_DCACHE, map_idx, &rsd) != 0)
- print_l1_dcache_misses(config, map_idx, avg, out, st, &rsd);
- else
- print_metric(config, ctxp, NULL, NULL, "of all L1-dcache accesses", 0);
- } else if (
- evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
- evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_L1I |
- ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
- ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
-
- if (runtime_stat_n(st, STAT_L1_ICACHE, map_idx, &rsd) != 0)
- print_l1_icache_misses(config, map_idx, avg, out, st, &rsd);
- else
- print_metric(config, ctxp, NULL, NULL, "of all L1-icache accesses", 0);
- } else if (
- evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
- evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_DTLB |
- ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
- ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
-
- if (runtime_stat_n(st, STAT_DTLB_CACHE, map_idx, &rsd) != 0)
- print_dtlb_cache_misses(config, map_idx, avg, out, st, &rsd);
- else
- print_metric(config, ctxp, NULL, NULL, "of all dTLB cache accesses", 0);
- } else if (
- evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
- evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_ITLB |
- ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
- ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
-
- if (runtime_stat_n(st, STAT_ITLB_CACHE, map_idx, &rsd) != 0)
- print_itlb_cache_misses(config, map_idx, avg, out, st, &rsd);
- else
- print_metric(config, ctxp, NULL, NULL, "of all iTLB cache accesses", 0);
- } else if (
- evsel->core.attr.type == PERF_TYPE_HW_CACHE &&
- evsel->core.attr.config == ( PERF_COUNT_HW_CACHE_LL |
- ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
- ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
-
- if (runtime_stat_n(st, STAT_LL_CACHE, map_idx, &rsd) != 0)
- print_ll_cache_misses(config, map_idx, avg, out, st, &rsd);
- else
- print_metric(config, ctxp, NULL, NULL, "of all LL-cache accesses", 0);
- } else if (evsel__match(evsel, HARDWARE, HW_CACHE_MISSES)) {
- total = runtime_stat_avg(st, STAT_CACHEREFS, map_idx, &rsd);
-
- if (total)
- ratio = avg * 100 / total;
-
- if (runtime_stat_n(st, STAT_CACHEREFS, map_idx, &rsd) != 0)
- print_metric(config, ctxp, NULL, "%8.3f %%",
- "of all cache refs", ratio);
- else
- print_metric(config, ctxp, NULL, NULL, "of all cache refs", 0);
- } else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) {
- print_stalled_cycles_frontend(config, map_idx, avg, out, st, &rsd);
- } else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) {
- print_stalled_cycles_backend(config, map_idx, avg, out, st, &rsd);
- } else if (evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) {
- total = runtime_stat_avg(st, STAT_NSECS, map_idx, &rsd);
-
- if (total) {
- ratio = avg / total;
- print_metric(config, ctxp, NULL, "%8.3f", "GHz", ratio);
- } else {
- print_metric(config, ctxp, NULL, NULL, "Ghz", 0);
- }
- } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX)) {
- total = runtime_stat_avg(st, STAT_CYCLES, map_idx, &rsd);
-
- if (total)
- print_metric(config, ctxp, NULL,
- "%7.2f%%", "transactional cycles",
- 100.0 * (avg / total));
- else
- print_metric(config, ctxp, NULL, NULL, "transactional cycles",
- 0);
- } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX_CP)) {
- total = runtime_stat_avg(st, STAT_CYCLES, map_idx, &rsd);
- total2 = runtime_stat_avg(st, STAT_CYCLES_IN_TX, map_idx, &rsd);
-
- if (total2 < avg)
- total2 = avg;
- if (total)
- print_metric(config, ctxp, NULL, "%7.2f%%", "aborted cycles",
- 100.0 * ((total2-avg) / total));
- else
- print_metric(config, ctxp, NULL, NULL, "aborted cycles", 0);
- } else if (perf_stat_evsel__is(evsel, TRANSACTION_START)) {
- total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, map_idx, &rsd);
-
- if (avg)
- ratio = total / avg;
-
- if (runtime_stat_n(st, STAT_CYCLES_IN_TX, map_idx, &rsd) != 0)
- print_metric(config, ctxp, NULL, "%8.0f",
- "cycles / transaction", ratio);
- else
- print_metric(config, ctxp, NULL, NULL, "cycles / transaction",
- 0);
- } else if (perf_stat_evsel__is(evsel, ELISION_START)) {
- total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, map_idx, &rsd);
-
- if (avg)
- ratio = total / avg;
-
- print_metric(config, ctxp, NULL, "%8.0f", "cycles / elision", ratio);
- } else if (evsel__is_clock(evsel)) {
- if ((ratio = avg_stats(&walltime_nsecs_stats)) != 0)
- print_metric(config, ctxp, NULL, "%8.3f", "CPUs utilized",
- avg / (ratio * evsel->scale));
- else
- print_metric(config, ctxp, NULL, NULL, "CPUs utilized", 0);
- } else if (perf_stat_evsel__is(evsel, TOPDOWN_FETCH_BUBBLES)) {
- double fe_bound = td_fe_bound(map_idx, st, &rsd);
-
- if (fe_bound > 0.2)
- color = PERF_COLOR_RED;
- print_metric(config, ctxp, color, "%8.1f%%", "frontend bound",
- fe_bound * 100.);
- } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_RETIRED)) {
- double retiring = td_retiring(map_idx, st, &rsd);
-
- if (retiring > 0.7)
- color = PERF_COLOR_GREEN;
- print_metric(config, ctxp, color, "%8.1f%%", "retiring",
- retiring * 100.);
- } else if (perf_stat_evsel__is(evsel, TOPDOWN_RECOVERY_BUBBLES)) {
- double bad_spec = td_bad_spec(map_idx, st, &rsd);
-
- if (bad_spec > 0.1)
- color = PERF_COLOR_RED;
- print_metric(config, ctxp, color, "%8.1f%%", "bad speculation",
- bad_spec * 100.);
- } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_ISSUED)) {
- double be_bound = td_be_bound(map_idx, st, &rsd);
- const char *name = "backend bound";
- static int have_recovery_bubbles = -1;
-
- /* In case the CPU does not support topdown-recovery-bubbles */
- if (have_recovery_bubbles < 0)
- have_recovery_bubbles = pmu_have_event("cpu",
- "topdown-recovery-bubbles");
- if (!have_recovery_bubbles)
- name = "backend bound/bad spec";
-
- if (be_bound > 0.2)
- color = PERF_COLOR_RED;
- if (td_total_slots(map_idx, st, &rsd) > 0)
- print_metric(config, ctxp, color, "%8.1f%%", name,
- be_bound * 100.);
- else
- print_metric(config, ctxp, NULL, NULL, name, 0);
- } else if (perf_stat_evsel__is(evsel, TOPDOWN_RETIRING) &&
- full_td(map_idx, st, &rsd)) {
- double retiring = td_metric_ratio(map_idx,
- STAT_TOPDOWN_RETIRING, st,
- &rsd);
- if (retiring > 0.7)
- color = PERF_COLOR_GREEN;
- print_metric(config, ctxp, color, "%8.1f%%", "Retiring",
- retiring * 100.);
- } else if (perf_stat_evsel__is(evsel, TOPDOWN_FE_BOUND) &&
- full_td(map_idx, st, &rsd)) {
- double fe_bound = td_metric_ratio(map_idx,
- STAT_TOPDOWN_FE_BOUND, st,
- &rsd);
- if (fe_bound > 0.2)
- color = PERF_COLOR_RED;
- print_metric(config, ctxp, color, "%8.1f%%", "Frontend Bound",
- fe_bound * 100.);
- } else if (perf_stat_evsel__is(evsel, TOPDOWN_BE_BOUND) &&
- full_td(map_idx, st, &rsd)) {
- double be_bound = td_metric_ratio(map_idx,
- STAT_TOPDOWN_BE_BOUND, st,
- &rsd);
- if (be_bound > 0.2)
- color = PERF_COLOR_RED;
- print_metric(config, ctxp, color, "%8.1f%%", "Backend Bound",
- be_bound * 100.);
- } else if (perf_stat_evsel__is(evsel, TOPDOWN_BAD_SPEC) &&
- full_td(map_idx, st, &rsd)) {
- double bad_spec = td_metric_ratio(map_idx,
- STAT_TOPDOWN_BAD_SPEC, st,
- &rsd);
- if (bad_spec > 0.1)
- color = PERF_COLOR_RED;
- print_metric(config, ctxp, color, "%8.1f%%", "Bad Speculation",
- bad_spec * 100.);
- } else if (perf_stat_evsel__is(evsel, TOPDOWN_HEAVY_OPS) &&
- full_td(map_idx, st, &rsd) && (config->topdown_level > 1)) {
- double retiring = td_metric_ratio(map_idx,
- STAT_TOPDOWN_RETIRING, st,
- &rsd);
- double heavy_ops = td_metric_ratio(map_idx,
- STAT_TOPDOWN_HEAVY_OPS, st,
- &rsd);
- double light_ops = retiring - heavy_ops;
-
- if (retiring > 0.7 && heavy_ops > 0.1)
- color = PERF_COLOR_GREEN;
- print_metric(config, ctxp, color, "%8.1f%%", "Heavy Operations",
- heavy_ops * 100.);
- if (retiring > 0.7 && light_ops > 0.6)
- color = PERF_COLOR_GREEN;
- else
- color = NULL;
- print_metric(config, ctxp, color, "%8.1f%%", "Light Operations",
- light_ops * 100.);
- } else if (perf_stat_evsel__is(evsel, TOPDOWN_BR_MISPREDICT) &&
- full_td(map_idx, st, &rsd) && (config->topdown_level > 1)) {
- double bad_spec = td_metric_ratio(map_idx,
- STAT_TOPDOWN_BAD_SPEC, st,
- &rsd);
- double br_mis = td_metric_ratio(map_idx,
- STAT_TOPDOWN_BR_MISPREDICT, st,
- &rsd);
- double m_clears = bad_spec - br_mis;
-
- if (bad_spec > 0.1 && br_mis > 0.05)
- color = PERF_COLOR_RED;
- print_metric(config, ctxp, color, "%8.1f%%", "Branch Mispredict",
- br_mis * 100.);
- if (bad_spec > 0.1 && m_clears > 0.05)
- color = PERF_COLOR_RED;
- else
- color = NULL;
- print_metric(config, ctxp, color, "%8.1f%%", "Machine Clears",
- m_clears * 100.);
- } else if (perf_stat_evsel__is(evsel, TOPDOWN_FETCH_LAT) &&
- full_td(map_idx, st, &rsd) && (config->topdown_level > 1)) {
- double fe_bound = td_metric_ratio(map_idx,
- STAT_TOPDOWN_FE_BOUND, st,
- &rsd);
- double fetch_lat = td_metric_ratio(map_idx,
- STAT_TOPDOWN_FETCH_LAT, st,
- &rsd);
- double fetch_bw = fe_bound - fetch_lat;
-
- if (fe_bound > 0.2 && fetch_lat > 0.15)
- color = PERF_COLOR_RED;
- print_metric(config, ctxp, color, "%8.1f%%", "Fetch Latency",
- fetch_lat * 100.);
- if (fe_bound > 0.2 && fetch_bw > 0.1)
- color = PERF_COLOR_RED;
- else
- color = NULL;
- print_metric(config, ctxp, color, "%8.1f%%", "Fetch Bandwidth",
- fetch_bw * 100.);
- } else if (perf_stat_evsel__is(evsel, TOPDOWN_MEM_BOUND) &&
- full_td(map_idx, st, &rsd) && (config->topdown_level > 1)) {
- double be_bound = td_metric_ratio(map_idx,
- STAT_TOPDOWN_BE_BOUND, st,
- &rsd);
- double mem_bound = td_metric_ratio(map_idx,
- STAT_TOPDOWN_MEM_BOUND, st,
- &rsd);
- double core_bound = be_bound - mem_bound;
-
- if (be_bound > 0.2 && mem_bound > 0.2)
- color = PERF_COLOR_RED;
- print_metric(config, ctxp, color, "%8.1f%%", "Memory Bound",
- mem_bound * 100.);
- if (be_bound > 0.2 && core_bound > 0.1)
- color = PERF_COLOR_RED;
- else
- color = NULL;
- print_metric(config, ctxp, color, "%8.1f%%", "Core Bound",
- core_bound * 100.);
- } else if (runtime_stat_n(st, STAT_NSECS, map_idx, &rsd) != 0) {
- char unit = ' ';
- char unit_buf[10] = "/sec";
-
- total = runtime_stat_avg(st, STAT_NSECS, map_idx, &rsd);
- if (total)
- ratio = convert_unit_double(1000000000.0 * avg / total, &unit);
-
- if (unit != ' ')
- snprintf(unit_buf, sizeof(unit_buf), "%c/sec", unit);
- print_metric(config, ctxp, NULL, "%8.3f", unit_buf, ratio);
- } else if (perf_stat_evsel__is(evsel, SMI_NUM)) {
- print_smi_cost(config, map_idx, out, st, &rsd);
} else {
- num = 0;
+ stat_print_function_t fn = stat_print_function[evsel__stat_type(evsel)];
+
+ if (fn)
+ fn(config, evsel, aggr_idx, avg, out);
+ else {
+ double nsecs = find_stat(evsel, aggr_idx, STAT_NSECS);
+
+ if (nsecs) {
+ char unit = ' ';
+ char unit_buf[10] = "/sec";
+ double ratio = convert_unit_double(1000000000.0 * avg / nsecs,
+ &unit);
+
+ if (unit != ' ')
+ snprintf(unit_buf, sizeof(unit_buf), "%c/sec", unit);
+ print_metric(config, ctxp, NULL, "%8.3f", unit_buf, ratio);
+ } else
+ num = 0;
+ }
}
if ((me = metricgroup__lookup(metric_events, evsel, false)) != NULL) {
@@ -1214,10 +585,10 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
list_for_each_entry (mexp, &me->head, nd) {
if (num++ > 0)
out->new_line(config, ctxp);
- generic_metric(config, mexp->metric_expr, mexp->metric_events,
- mexp->metric_refs, evsel->name, mexp->metric_name,
- mexp->metric_unit, mexp->runtime,
- map_idx, out, st);
+ generic_metric(config, mexp->metric_expr, mexp->metric_threshold,
+ mexp->metric_events, mexp->metric_refs, evsel->name,
+ mexp->metric_name, mexp->metric_unit, mexp->runtime,
+ aggr_idx, out);
}
}
if (num == 0)
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index a07473703c6d..0f7b8a8cdea6 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -77,55 +77,6 @@ double rel_stddev_stats(double stddev, double avg)
return pct;
}
-bool __perf_stat_evsel__is(struct evsel *evsel, enum perf_stat_evsel_id id)
-{
- struct perf_stat_evsel *ps = evsel->stats;
-
- return ps->id == id;
-}
-
-#define ID(id, name) [PERF_STAT_EVSEL_ID__##id] = #name
-static const char *id_str[PERF_STAT_EVSEL_ID__MAX] = {
- ID(NONE, x),
- ID(CYCLES_IN_TX, cpu/cycles-t/),
- ID(TRANSACTION_START, cpu/tx-start/),
- ID(ELISION_START, cpu/el-start/),
- ID(CYCLES_IN_TX_CP, cpu/cycles-ct/),
- ID(TOPDOWN_TOTAL_SLOTS, topdown-total-slots),
- ID(TOPDOWN_SLOTS_ISSUED, topdown-slots-issued),
- ID(TOPDOWN_SLOTS_RETIRED, topdown-slots-retired),
- ID(TOPDOWN_FETCH_BUBBLES, topdown-fetch-bubbles),
- ID(TOPDOWN_RECOVERY_BUBBLES, topdown-recovery-bubbles),
- ID(TOPDOWN_RETIRING, topdown-retiring),
- ID(TOPDOWN_BAD_SPEC, topdown-bad-spec),
- ID(TOPDOWN_FE_BOUND, topdown-fe-bound),
- ID(TOPDOWN_BE_BOUND, topdown-be-bound),
- ID(TOPDOWN_HEAVY_OPS, topdown-heavy-ops),
- ID(TOPDOWN_BR_MISPREDICT, topdown-br-mispredict),
- ID(TOPDOWN_FETCH_LAT, topdown-fetch-lat),
- ID(TOPDOWN_MEM_BOUND, topdown-mem-bound),
- ID(SMI_NUM, msr/smi/),
- ID(APERF, msr/aperf/),
-};
-#undef ID
-
-static void perf_stat_evsel_id_init(struct evsel *evsel)
-{
- struct perf_stat_evsel *ps = evsel->stats;
- int i;
-
- /* ps->id is 0 hence PERF_STAT_EVSEL_ID__NONE by default */
-
- for (i = 0; i < PERF_STAT_EVSEL_ID__MAX; i++) {
- if (!strcmp(evsel__name(evsel), id_str[i]) ||
- (strstr(evsel__name(evsel), id_str[i]) && evsel->pmu_name
- && strstr(evsel__name(evsel), evsel->pmu_name))) {
- ps->id = i;
- break;
- }
- }
-}
-
static void evsel__reset_aggr_stats(struct evsel *evsel)
{
struct perf_stat_evsel *ps = evsel->stats;
@@ -185,7 +136,6 @@ static int evsel__alloc_stat_priv(struct evsel *evsel, int nr_aggr)
return -ENOMEM;
}
- perf_stat_evsel_id_init(evsel);
evsel__reset_stat_priv(evsel);
return 0;
}
@@ -698,30 +648,6 @@ void perf_stat_process_percore(struct perf_stat_config *config, struct evlist *e
evsel__process_percore(evsel);
}
-static void evsel__update_shadow_stats(struct evsel *evsel)
-{
- struct perf_stat_evsel *ps = evsel->stats;
- int i;
-
- if (ps->aggr == NULL)
- return;
-
- for (i = 0; i < ps->nr_aggr; i++) {
- struct perf_counts_values *aggr_counts = &ps->aggr[i].counts;
-
- perf_stat__update_shadow_stats(evsel, aggr_counts->val, i, &rt_stat);
- }
-}
-
-void perf_stat_process_shadow_stats(struct perf_stat_config *config __maybe_unused,
- struct evlist *evlist)
-{
- struct evsel *evsel;
-
- evlist__for_each_entry(evlist, evsel)
- evsel__update_shadow_stats(evsel);
-}
-
int perf_event__process_stat_event(struct perf_session *session,
union perf_event *event)
{
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
index bf1794ebc916..e35e188237c8 100644
--- a/tools/perf/util/stat.h
+++ b/tools/perf/util/stat.h
@@ -19,30 +19,6 @@ struct stats {
u64 max, min;
};
-enum perf_stat_evsel_id {
- PERF_STAT_EVSEL_ID__NONE = 0,
- PERF_STAT_EVSEL_ID__CYCLES_IN_TX,
- PERF_STAT_EVSEL_ID__TRANSACTION_START,
- PERF_STAT_EVSEL_ID__ELISION_START,
- PERF_STAT_EVSEL_ID__CYCLES_IN_TX_CP,
- PERF_STAT_EVSEL_ID__TOPDOWN_TOTAL_SLOTS,
- PERF_STAT_EVSEL_ID__TOPDOWN_SLOTS_ISSUED,
- PERF_STAT_EVSEL_ID__TOPDOWN_SLOTS_RETIRED,
- PERF_STAT_EVSEL_ID__TOPDOWN_FETCH_BUBBLES,
- PERF_STAT_EVSEL_ID__TOPDOWN_RECOVERY_BUBBLES,
- PERF_STAT_EVSEL_ID__TOPDOWN_RETIRING,
- PERF_STAT_EVSEL_ID__TOPDOWN_BAD_SPEC,
- PERF_STAT_EVSEL_ID__TOPDOWN_FE_BOUND,
- PERF_STAT_EVSEL_ID__TOPDOWN_BE_BOUND,
- PERF_STAT_EVSEL_ID__TOPDOWN_HEAVY_OPS,
- PERF_STAT_EVSEL_ID__TOPDOWN_BR_MISPREDICT,
- PERF_STAT_EVSEL_ID__TOPDOWN_FETCH_LAT,
- PERF_STAT_EVSEL_ID__TOPDOWN_MEM_BOUND,
- PERF_STAT_EVSEL_ID__SMI_NUM,
- PERF_STAT_EVSEL_ID__APERF,
- PERF_STAT_EVSEL_ID__MAX,
-};
-
/* hold aggregated event info */
struct perf_stat_aggr {
/* aggregated values */
@@ -59,8 +35,6 @@ struct perf_stat_aggr {
struct perf_stat_evsel {
/* used for repeated runs */
struct stats res_stats;
- /* evsel id for quick check */
- enum perf_stat_evsel_id id;
/* number of allocated 'aggr' */
int nr_aggr;
/* aggregated event values */
@@ -81,55 +55,6 @@ enum aggr_mode {
AGGR_MAX
};
-enum {
- CTX_BIT_USER = 1 << 0,
- CTX_BIT_KERNEL = 1 << 1,
- CTX_BIT_HV = 1 << 2,
- CTX_BIT_HOST = 1 << 3,
- CTX_BIT_IDLE = 1 << 4,
- CTX_BIT_MAX = 1 << 5,
-};
-
-#define NUM_CTX CTX_BIT_MAX
-
-enum stat_type {
- STAT_NONE = 0,
- STAT_NSECS,
- STAT_CYCLES,
- STAT_STALLED_CYCLES_FRONT,
- STAT_STALLED_CYCLES_BACK,
- STAT_BRANCHES,
- STAT_CACHEREFS,
- STAT_L1_DCACHE,
- STAT_L1_ICACHE,
- STAT_LL_CACHE,
- STAT_ITLB_CACHE,
- STAT_DTLB_CACHE,
- STAT_CYCLES_IN_TX,
- STAT_TRANSACTION,
- STAT_ELISION,
- STAT_TOPDOWN_TOTAL_SLOTS,
- STAT_TOPDOWN_SLOTS_ISSUED,
- STAT_TOPDOWN_SLOTS_RETIRED,
- STAT_TOPDOWN_FETCH_BUBBLES,
- STAT_TOPDOWN_RECOVERY_BUBBLES,
- STAT_TOPDOWN_RETIRING,
- STAT_TOPDOWN_BAD_SPEC,
- STAT_TOPDOWN_FE_BOUND,
- STAT_TOPDOWN_BE_BOUND,
- STAT_TOPDOWN_HEAVY_OPS,
- STAT_TOPDOWN_BR_MISPREDICT,
- STAT_TOPDOWN_FETCH_LAT,
- STAT_TOPDOWN_MEM_BOUND,
- STAT_SMI_NUM,
- STAT_APERF,
- STAT_MAX
-};
-
-struct runtime_stat {
- struct rblist value_list;
-};
-
struct rusage_stats {
struct stats ru_utime_usec_stat;
struct stats ru_stime_usec_stat;
@@ -159,6 +84,7 @@ struct perf_stat_config {
bool no_csv_summary;
bool metric_no_group;
bool metric_no_merge;
+ bool metric_no_threshold;
bool stop_read_counter;
bool iostat_run;
char *user_requested_cpu_list;
@@ -222,12 +148,6 @@ static inline void update_rusage_stats(struct rusage_stats *ru_stats, struct rus
struct evsel;
struct evlist;
-bool __perf_stat_evsel__is(struct evsel *evsel, enum perf_stat_evsel_id id);
-
-#define perf_stat_evsel__is(evsel, id) \
- __perf_stat_evsel__is(evsel, PERF_STAT_EVSEL_ID__ ## id)
-
-extern struct runtime_stat rt_stat;
extern struct stats walltime_nsecs_stats;
extern struct rusage_stats ru_stats;
@@ -236,13 +156,7 @@ typedef void (*print_metric_t)(struct perf_stat_config *config,
const char *fmt, double val);
typedef void (*new_line_t)(struct perf_stat_config *config, void *ctx);
-void runtime_stat__init(struct runtime_stat *st);
-void runtime_stat__exit(struct runtime_stat *st);
-void perf_stat__init_shadow_stats(void);
void perf_stat__reset_shadow_stats(void);
-void perf_stat__reset_shadow_per_stat(struct runtime_stat *st);
-void perf_stat__update_shadow_stats(struct evsel *counter, u64 count,
- int map_idx, struct runtime_stat *st);
struct perf_stat_output_ctx {
void *ctx;
print_metric_t print_metric;
@@ -252,10 +166,9 @@ struct perf_stat_output_ctx {
void perf_stat__print_shadow_stats(struct perf_stat_config *config,
struct evsel *evsel,
- double avg, int map_idx,
+ double avg, int aggr_idx,
struct perf_stat_output_ctx *out,
- struct rblist *metric_events,
- struct runtime_stat *st);
+ struct rblist *metric_events);
int evlist__alloc_stats(struct perf_stat_config *config,
struct evlist *evlist, bool alloc_raw);
@@ -272,7 +185,6 @@ int perf_stat_process_counter(struct perf_stat_config *config,
struct evsel *counter);
void perf_stat_merge_counters(struct perf_stat_config *config, struct evlist *evlist);
void perf_stat_process_percore(struct perf_stat_config *config, struct evlist *evlist);
-void perf_stat_process_shadow_stats(struct perf_stat_config *config, struct evlist *evlist);
struct perf_tool;
union perf_event;
@@ -294,5 +206,5 @@ void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *conf
struct target *_target, struct timespec *ts, int argc, const char **argv);
struct metric_expr;
-double test_generic_metric(struct metric_expr *mexp, int map_idx, struct runtime_stat *st);
+double test_generic_metric(struct metric_expr *mexp, int aggr_idx);
#endif
diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c
index 78aa4c3b990d..02807b9d4122 100644
--- a/tools/perf/util/strfilter.c
+++ b/tools/perf/util/strfilter.c
@@ -274,7 +274,7 @@ static int strfilter_node__sprint(struct strfilter_node *node, char *buf)
len = strfilter_node__sprint_pt(node->l, buf);
if (len < 0)
return len;
- __fallthrough;
+ fallthrough;
case '!':
if (buf) {
*(buf + len++) = *node->p;
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index 4f12a96f33cc..cf05b0b56c57 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -35,7 +35,7 @@ s64 perf_atoll(const char *str)
if (*p)
goto out_err;
- __fallthrough;
+ fallthrough;
case '\0':
return length;
default:
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 554289fd6df9..b2ed9cc52265 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -12,6 +12,7 @@
#include "maps.h"
#include "symbol.h"
#include "symsrc.h"
+#include "demangle-cxx.h"
#include "demangle-ocaml.h"
#include "demangle-java.h"
#include "demangle-rust.h"
@@ -25,6 +26,11 @@
#include <symbol/kallsyms.h>
#include <internal/lib.h>
+#ifdef HAVE_LIBBFD_SUPPORT
+#define PACKAGE 'perf'
+#include <bfd.h>
+#endif
+
#ifndef EM_AARCH64
#define EM_AARCH64 183 /* ARM 64 bit */
#endif
@@ -45,34 +51,6 @@
typedef Elf64_Nhdr GElf_Nhdr;
-#ifndef DMGL_PARAMS
-#define DMGL_NO_OPTS 0 /* For readability... */
-#define DMGL_PARAMS (1 << 0) /* Include function args */
-#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
-#endif
-
-#ifdef HAVE_LIBBFD_SUPPORT
-#define PACKAGE 'perf'
-#include <bfd.h>
-#else
-#ifdef HAVE_CPLUS_DEMANGLE_SUPPORT
-extern char *cplus_demangle(const char *, int);
-
-static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i)
-{
- return cplus_demangle(c, i);
-}
-#else
-#ifdef NO_DEMANGLE
-static inline char *bfd_demangle(void __maybe_unused *v,
- const char __maybe_unused *c,
- int __maybe_unused i)
-{
- return NULL;
-}
-#endif
-#endif
-#endif
#ifndef HAVE_ELF_GETPHDRNUM_SUPPORT
static int elf_getphdrnum(Elf *elf, size_t *dst)
@@ -295,7 +273,6 @@ static bool want_demangle(bool is_kernel_sym)
static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
{
- int demangle_flags = verbose > 0 ? (DMGL_PARAMS | DMGL_ANSI) : DMGL_NO_OPTS;
char *demangled = NULL;
/*
@@ -306,7 +283,7 @@ static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
if (!want_demangle(dso->kernel || kmodule))
return demangled;
- demangled = bfd_demangle(NULL, elf_name, demangle_flags);
+ demangled = cxx_demangle_sym(elf_name, verbose > 0, verbose > 0);
if (demangled == NULL) {
demangled = ocaml_demangle_sym(elf_name);
if (demangled == NULL) {
@@ -419,7 +396,7 @@ static bool get_ifunc_name(Elf *elf, struct dso *dso, GElf_Ehdr *ehdr,
static void exit_rel(struct rel_info *ri)
{
- free(ri->sorted);
+ zfree(&ri->sorted);
}
static bool get_plt_sizes(struct dso *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr_plt,
@@ -483,7 +460,7 @@ struct rela_dyn_info {
static void exit_rela_dyn(struct rela_dyn_info *di)
{
- free(di->sorted);
+ zfree(&di->sorted);
}
static int cmp_offset(const void *a, const void *b)
@@ -565,9 +542,12 @@ static u32 get_x86_64_plt_disp(const u8 *p)
n += 1;
/* jmp with 4-byte displacement */
if (p[n] == 0xff && p[n + 1] == 0x25) {
+ u32 disp;
+
n += 2;
/* Also add offset from start of entry to end of instruction */
- return n + 4 + le32toh(*(const u32 *)(p + n));
+ memcpy(&disp, p + n, sizeof(disp));
+ return n + 4 + le32toh(disp);
}
return 0;
}
@@ -580,6 +560,7 @@ static bool get_plt_got_name(GElf_Shdr *shdr, size_t i,
const char *sym_name;
char *demangled;
GElf_Sym sym;
+ bool result;
u32 disp;
if (!di->sorted)
@@ -606,9 +587,11 @@ static bool get_plt_got_name(GElf_Shdr *shdr, size_t i,
snprintf(buf, buf_sz, "%s@plt", sym_name);
+ result = *sym_name;
+
free(demangled);
- return *sym_name;
+ return result;
}
static int dso__synthesize_plt_got_symbols(struct dso *dso, Elf *elf,
@@ -903,7 +886,7 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size)
size_t sz = min(size, descsz);
memcpy(bf, ptr, sz);
memset(bf + sz, 0, size - sz);
- err = descsz;
+ err = sz;
break;
}
}
@@ -1371,17 +1354,21 @@ static int dso__process_kernel_symbol(struct dso *dso, struct map *map,
*/
if (*remap_kernel && dso->kernel && !kmodule) {
*remap_kernel = false;
- map->start = shdr->sh_addr + ref_reloc(kmap);
- map->end = map->start + shdr->sh_size;
- map->pgoff = shdr->sh_offset;
- map->map_ip = map__map_ip;
- map->unmap_ip = map__unmap_ip;
+ map__set_start(map, shdr->sh_addr + ref_reloc(kmap));
+ map__set_end(map, map__start(map) + shdr->sh_size);
+ map__set_pgoff(map, shdr->sh_offset);
+ map__set_map_ip(map, map__dso_map_ip);
+ map__set_unmap_ip(map, map__dso_unmap_ip);
/* Ensure maps are correctly ordered */
if (kmaps) {
+ int err;
+
map__get(map);
maps__remove(kmaps, map);
- maps__insert(kmaps, map);
+ err = maps__insert(kmaps, map);
map__put(map);
+ if (err)
+ return err;
}
}
@@ -1392,7 +1379,7 @@ static int dso__process_kernel_symbol(struct dso *dso, struct map *map,
*/
if (*remap_kernel && kmodule) {
*remap_kernel = false;
- map->pgoff = shdr->sh_offset;
+ map__set_pgoff(map, shdr->sh_offset);
}
*curr_mapp = map;
@@ -1410,7 +1397,7 @@ static int dso__process_kernel_symbol(struct dso *dso, struct map *map,
u64 start = sym->st_value;
if (kmodule)
- start += map->start + shdr->sh_offset;
+ start += map__start(map) + shdr->sh_offset;
curr_dso = dso__new(dso_name);
if (curr_dso == NULL)
@@ -1427,27 +1414,29 @@ static int dso__process_kernel_symbol(struct dso *dso, struct map *map,
map__kmap(curr_map)->kmaps = kmaps;
if (adjust_kernel_syms) {
- curr_map->start = shdr->sh_addr + ref_reloc(kmap);
- curr_map->end = curr_map->start + shdr->sh_size;
- curr_map->pgoff = shdr->sh_offset;
+ map__set_start(curr_map, shdr->sh_addr + ref_reloc(kmap));
+ map__set_end(curr_map, map__start(curr_map) + shdr->sh_size);
+ map__set_pgoff(curr_map, shdr->sh_offset);
} else {
- curr_map->map_ip = curr_map->unmap_ip = identity__map_ip;
+ map__set_map_ip(curr_map, identity__map_ip);
+ map__set_unmap_ip(curr_map, identity__map_ip);
}
curr_dso->symtab_type = dso->symtab_type;
- maps__insert(kmaps, curr_map);
+ if (maps__insert(kmaps, curr_map))
+ return -1;
/*
* Add it before we drop the reference to curr_map, i.e. while
* we still are sure to have a reference to this DSO via
* *curr_map->dso.
*/
- dsos__add(&kmaps->machine->dsos, curr_dso);
+ dsos__add(&maps__machine(kmaps)->dsos, curr_dso);
/* kmaps already got it */
map__put(curr_map);
dso__set_loaded(curr_dso);
*curr_mapp = curr_map;
*curr_dsop = curr_dso;
} else
- *curr_dsop = curr_map->dso;
+ *curr_dsop = map__dso(curr_map);
return 0;
}
@@ -1537,8 +1526,7 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
if (strcmp(elf_name, kmap->ref_reloc_sym->name))
continue;
kmap->ref_reloc_sym->unrelocated_addr = sym.st_value;
- map->reloc = kmap->ref_reloc_sym->addr -
- kmap->ref_reloc_sym->unrelocated_addr;
+ map__set_reloc(map, kmap->ref_reloc_sym->addr - kmap->ref_reloc_sym->unrelocated_addr);
break;
}
}
@@ -1548,7 +1536,7 @@ dso__load_sym_internal(struct dso *dso, struct map *map, struct symsrc *syms_ss,
* attempted to prelink vdso to its virtual address.
*/
if (dso__is_vdso(dso))
- map->reloc = map->start - dso->text_offset;
+ map__set_reloc(map, map__start(map) - dso->text_offset);
dso->adjust_symbols = runtime_ss->adjust_symbols || ref_reloc(kmap);
/*
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 317c0706852f..6b9c55784b56 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -48,6 +48,11 @@ static bool symbol__is_idle(const char *name);
int vmlinux_path__nr_entries;
char **vmlinux_path;
+struct map_list_node {
+ struct list_head node;
+ struct map *map;
+};
+
struct symbol_conf symbol_conf = {
.nanosecs = false,
.use_modules = true,
@@ -85,6 +90,11 @@ static enum dso_binary_type binary_type_symtab[] = {
#define DSO_BINARY_TYPE__SYMTAB_CNT ARRAY_SIZE(binary_type_symtab)
+static struct map_list_node *map_list_node__new(void)
+{
+ return malloc(sizeof(struct map_list_node));
+}
+
static bool symbol_type__filter(char symbol_type)
{
symbol_type = toupper(symbol_type);
@@ -263,13 +273,13 @@ void symbols__fixup_end(struct rb_root_cached *symbols, bool is_kallsyms)
void maps__fixup_end(struct maps *maps)
{
- struct map *prev = NULL, *curr;
+ struct map_rb_node *prev = NULL, *curr;
- down_write(&maps->lock);
+ down_write(maps__lock(maps));
maps__for_each_entry(maps, curr) {
- if (prev != NULL && !prev->end)
- prev->end = curr->start;
+ if (prev != NULL && !map__end(prev->map))
+ map__set_end(prev->map, map__start(curr->map));
prev = curr;
}
@@ -278,10 +288,10 @@ void maps__fixup_end(struct maps *maps)
* We still haven't the actual symbols, so guess the
* last map final address.
*/
- if (curr && !curr->end)
- curr->end = ~0ULL;
+ if (curr && !map__end(curr->map))
+ map__set_end(curr->map, ~0ULL);
- up_write(&maps->lock);
+ up_write(maps__lock(maps));
}
struct symbol *symbol__new(u64 start, u64 len, u8 binding, u8 type, const char *name)
@@ -781,6 +791,7 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
*root = RB_ROOT_CACHED;
while (next) {
+ struct dso *curr_map_dso;
char *module;
pos = rb_entry(next, struct symbol, rb_node);
@@ -798,13 +809,13 @@ static int maps__split_kallsyms_for_kcore(struct maps *kmaps, struct dso *dso)
symbol__delete(pos);
continue;
}
-
- pos->start -= curr_map->start - curr_map->pgoff;
- if (pos->end > curr_map->end)
- pos->end = curr_map->end;
+ curr_map_dso = map__dso(curr_map);
+ pos->start -= map__start(curr_map) - map__pgoff(curr_map);
+ if (pos->end > map__end(curr_map))
+ pos->end = map__end(curr_map);
if (pos->end)
- pos->end -= curr_map->start - curr_map->pgoff;
- symbols__insert(&curr_map->dso->symbols, pos);
+ pos->end -= map__start(curr_map) - map__pgoff(curr_map);
+ symbols__insert(&curr_map_dso->symbols, pos);
++count;
}
@@ -834,7 +845,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
if (!kmaps)
return -1;
- machine = kmaps->machine;
+ machine = maps__machine(kmaps);
x86_64 = machine__is(machine, "x86_64");
@@ -846,13 +857,15 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
module = strchr(pos->name, '\t');
if (module) {
+ struct dso *curr_map_dso;
+
if (!symbol_conf.use_modules)
goto discard_symbol;
*module++ = '\0';
-
- if (strcmp(curr_map->dso->short_name, module)) {
- if (curr_map != initial_map &&
+ curr_map_dso = map__dso(curr_map);
+ if (strcmp(curr_map_dso->short_name, module)) {
+ if (RC_CHK_ACCESS(curr_map) != RC_CHK_ACCESS(initial_map) &&
dso->kernel == DSO_SPACE__KERNEL_GUEST &&
machine__is_default_guest(machine)) {
/*
@@ -862,7 +875,7 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
* symbols are in its kmap. Mark it as
* loaded.
*/
- dso__set_loaded(curr_map->dso);
+ dso__set_loaded(curr_map_dso);
}
curr_map = maps__find_by_name(kmaps, module);
@@ -874,8 +887,8 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
curr_map = initial_map;
goto discard_symbol;
}
-
- if (curr_map->dso->loaded &&
+ curr_map_dso = map__dso(curr_map);
+ if (curr_map_dso->loaded &&
!machine__is_default_guest(machine))
goto discard_symbol;
}
@@ -883,8 +896,8 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
* So that we look just like we get from .ko files,
* i.e. not prelinked, relative to initial_map->start.
*/
- pos->start = curr_map->map_ip(curr_map, pos->start);
- pos->end = curr_map->map_ip(curr_map, pos->end);
+ pos->start = map__map_ip(curr_map, pos->start);
+ pos->end = map__map_ip(curr_map, pos->end);
} else if (x86_64 && is_entry_trampoline(pos->name)) {
/*
* These symbols are not needed anymore since the
@@ -931,8 +944,12 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
return -1;
}
- curr_map->map_ip = curr_map->unmap_ip = identity__map_ip;
- maps__insert(kmaps, curr_map);
+ map__set_map_ip(curr_map, identity__map_ip);
+ map__set_unmap_ip(curr_map, identity__map_ip);
+ if (maps__insert(kmaps, curr_map)) {
+ dso__put(ndso);
+ return -1;
+ }
++kernel_range;
} else if (delta) {
/* Kernel was relocated at boot time */
@@ -941,8 +958,10 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
}
add_symbol:
if (curr_map != initial_map) {
+ struct dso *curr_map_dso = map__dso(curr_map);
+
rb_erase_cached(&pos->rb_node, root);
- symbols__insert(&curr_map->dso->symbols, pos);
+ symbols__insert(&curr_map_dso->symbols, pos);
++moved;
} else
++count;
@@ -955,8 +974,8 @@ discard_symbol:
if (curr_map != initial_map &&
dso->kernel == DSO_SPACE__KERNEL_GUEST &&
- machine__is_default_guest(kmaps->machine)) {
- dso__set_loaded(curr_map->dso);
+ machine__is_default_guest(maps__machine(kmaps))) {
+ dso__set_loaded(map__dso(curr_map));
}
return count + moved;
@@ -1120,23 +1139,25 @@ out_delete_from:
static int do_validate_kcore_modules(const char *filename, struct maps *kmaps)
{
struct rb_root modules = RB_ROOT;
- struct map *old_map;
+ struct map_rb_node *old_node;
int err;
err = read_proc_modules(filename, &modules);
if (err)
return err;
- maps__for_each_entry(kmaps, old_map) {
+ maps__for_each_entry(kmaps, old_node) {
+ struct map *old_map = old_node->map;
struct module_info *mi;
+ struct dso *dso;
if (!__map__is_kmodule(old_map)) {
continue;
}
-
+ dso = map__dso(old_map);
/* Module must be in memory at the same address */
- mi = find_module(old_map->dso->short_name, &modules);
- if (!mi || mi->start != old_map->start) {
+ mi = find_module(dso->short_name, &modules);
+ if (!mi || mi->start != map__start(old_map)) {
err = -EINVAL;
goto out;
}
@@ -1219,16 +1240,21 @@ struct kcore_mapfn_data {
static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data)
{
struct kcore_mapfn_data *md = data;
- struct map *map;
+ struct map_list_node *list_node = map_list_node__new();
- map = map__new2(start, md->dso);
- if (map == NULL)
+ if (!list_node)
return -ENOMEM;
- map->end = map->start + len;
- map->pgoff = pgoff;
+ list_node->map = map__new2(start, md->dso);
+ if (!list_node->map) {
+ free(list_node);
+ return -ENOMEM;
+ }
- list_add(&map->node, &md->maps);
+ map__set_end(list_node->map, map__start(list_node->map) + len);
+ map__set_pgoff(list_node->map, pgoff);
+
+ list_add(&list_node->node, &md->maps);
return 0;
}
@@ -1239,47 +1265,59 @@ static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data)
*/
int maps__merge_in(struct maps *kmaps, struct map *new_map)
{
- struct map *old_map;
+ struct map_rb_node *rb_node;
LIST_HEAD(merged);
+ int err = 0;
+
+ maps__for_each_entry(kmaps, rb_node) {
+ struct map *old_map = rb_node->map;
- maps__for_each_entry(kmaps, old_map) {
/* no overload with this one */
- if (new_map->end < old_map->start ||
- new_map->start >= old_map->end)
+ if (map__end(new_map) < map__start(old_map) ||
+ map__start(new_map) >= map__end(old_map))
continue;
- if (new_map->start < old_map->start) {
+ if (map__start(new_map) < map__start(old_map)) {
/*
* |new......
* |old....
*/
- if (new_map->end < old_map->end) {
+ if (map__end(new_map) < map__end(old_map)) {
/*
* |new......| -> |new..|
* |old....| -> |old....|
*/
- new_map->end = old_map->start;
+ map__set_end(new_map, map__start(old_map));
} else {
/*
* |new.............| -> |new..| |new..|
* |old....| -> |old....|
*/
- struct map *m = map__clone(new_map);
+ struct map_list_node *m = map_list_node__new();
- if (!m)
- return -ENOMEM;
+ if (!m) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ m->map = map__clone(new_map);
+ if (!m->map) {
+ free(m);
+ err = -ENOMEM;
+ goto out;
+ }
- m->end = old_map->start;
+ map__set_end(m->map, map__start(old_map));
list_add_tail(&m->node, &merged);
- new_map->pgoff += old_map->end - new_map->start;
- new_map->start = old_map->end;
+ map__add_pgoff(new_map, map__end(old_map) - map__start(new_map));
+ map__set_start(new_map, map__end(old_map));
}
} else {
/*
* |new......
* |old....
*/
- if (new_map->end < old_map->end) {
+ if (map__end(new_map) < map__end(old_map)) {
/*
* |new..| -> x
* |old.........| -> |old.........|
@@ -1292,24 +1330,30 @@ int maps__merge_in(struct maps *kmaps, struct map *new_map)
* |new......| -> |new...|
* |old....| -> |old....|
*/
- new_map->pgoff += old_map->end - new_map->start;
- new_map->start = old_map->end;
+ map__add_pgoff(new_map, map__end(old_map) - map__start(new_map));
+ map__set_start(new_map, map__end(old_map));
}
}
}
+out:
while (!list_empty(&merged)) {
- old_map = list_entry(merged.next, struct map, node);
- list_del_init(&old_map->node);
- maps__insert(kmaps, old_map);
- map__put(old_map);
+ struct map_list_node *old_node;
+
+ old_node = list_entry(merged.next, struct map_list_node, node);
+ list_del_init(&old_node->node);
+ if (!err)
+ err = maps__insert(kmaps, old_node->map);
+ map__put(old_node->map);
+ free(old_node);
}
if (new_map) {
- maps__insert(kmaps, new_map);
+ if (!err)
+ err = maps__insert(kmaps, new_map);
map__put(new_map);
}
- return 0;
+ return err;
}
static int dso__load_kcore(struct dso *dso, struct map *map,
@@ -1317,7 +1361,8 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
{
struct maps *kmaps = map__kmaps(map);
struct kcore_mapfn_data md;
- struct map *old_map, *new_map, *replacement_map = NULL, *next;
+ struct map *replacement_map = NULL;
+ struct map_rb_node *old_node, *next;
struct machine *machine;
bool is_64_bit;
int err, fd;
@@ -1327,7 +1372,7 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
if (!kmaps)
return -EINVAL;
- machine = kmaps->machine;
+ machine = maps__machine(kmaps);
/* This function requires that the map is the kernel map */
if (!__map__is_kernel(map))
@@ -1352,7 +1397,7 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
}
/* Read new maps into temporary lists */
- err = file__read_maps(fd, map->prot & PROT_EXEC, kcore_mapfn, &md,
+ err = file__read_maps(fd, map__prot(map) & PROT_EXEC, kcore_mapfn, &md,
&is_64_bit);
if (err)
goto out_err;
@@ -1364,7 +1409,9 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
}
/* Remove old maps */
- maps__for_each_entry_safe(kmaps, old_map, next) {
+ maps__for_each_entry_safe(kmaps, old_node, next) {
+ struct map *old_map = old_node->map;
+
/*
* We need to preserve eBPF maps even if they are
* covered by kcore, because we need to access
@@ -1378,11 +1425,13 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
/* Find the kernel map using the '_stext' symbol */
if (!kallsyms__get_function_start(kallsyms_filename, "_stext", &stext)) {
u64 replacement_size = 0;
+ struct map_list_node *new_node;
- list_for_each_entry(new_map, &md.maps, node) {
- u64 new_size = new_map->end - new_map->start;
+ list_for_each_entry(new_node, &md.maps, node) {
+ struct map *new_map = new_node->map;
+ u64 new_size = map__size(new_map);
- if (!(stext >= new_map->start && stext < new_map->end))
+ if (!(stext >= map__start(new_map) && stext < map__end(new_map)))
continue;
/*
@@ -1399,33 +1448,41 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
}
if (!replacement_map)
- replacement_map = list_entry(md.maps.next, struct map, node);
+ replacement_map = list_entry(md.maps.next, struct map_list_node, node)->map;
/* Add new maps */
while (!list_empty(&md.maps)) {
- new_map = list_entry(md.maps.next, struct map, node);
- list_del_init(&new_map->node);
- if (new_map == replacement_map) {
- map->start = new_map->start;
- map->end = new_map->end;
- map->pgoff = new_map->pgoff;
- map->map_ip = new_map->map_ip;
- map->unmap_ip = new_map->unmap_ip;
+ struct map_list_node *new_node = list_entry(md.maps.next, struct map_list_node, node);
+ struct map *new_map = new_node->map;
+
+ list_del_init(&new_node->node);
+
+ if (RC_CHK_ACCESS(new_map) == RC_CHK_ACCESS(replacement_map)) {
+ map__set_start(map, map__start(new_map));
+ map__set_end(map, map__end(new_map));
+ map__set_pgoff(map, map__pgoff(new_map));
+ map__set_map_ip(map, map__map_ip_ptr(new_map));
+ map__set_unmap_ip(map, map__unmap_ip_ptr(new_map));
/* Ensure maps are correctly ordered */
map__get(map);
maps__remove(kmaps, map);
- maps__insert(kmaps, map);
+ err = maps__insert(kmaps, map);
map__put(map);
map__put(new_map);
+ if (err)
+ goto out_err;
} else {
/*
* Merge kcore map into existing maps,
* and ensure that current maps (eBPF)
* stay intact.
*/
- if (maps__merge_in(kmaps, new_map))
+ if (maps__merge_in(kmaps, new_map)) {
+ err = -EINVAL;
goto out_err;
+ }
}
+ free(new_node);
}
if (machine__is(machine, "x86_64")) {
@@ -1453,7 +1510,7 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
close(fd);
- if (map->prot & PROT_EXEC)
+ if (map__prot(map) & PROT_EXEC)
pr_debug("Using %s for kernel object code\n", kcore_filename);
else
pr_debug("Using %s for kernel data\n", kcore_filename);
@@ -1462,12 +1519,15 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
out_err:
while (!list_empty(&md.maps)) {
- map = list_entry(md.maps.next, struct map, node);
- list_del_init(&map->node);
- map__put(map);
+ struct map_list_node *list_node;
+
+ list_node = list_entry(md.maps.next, struct map_list_node, node);
+ list_del_init(&list_node->node);
+ map__zput(list_node->map);
+ free(list_node);
}
close(fd);
- return -EINVAL;
+ return err;
}
/*
@@ -1841,7 +1901,7 @@ int dso__load(struct dso *dso, struct map *map)
else if (dso->kernel == DSO_SPACE__KERNEL_GUEST)
ret = dso__load_guest_kernel_sym(dso, map);
- machine = map__kmaps(map)->machine;
+ machine = maps__machine(map__kmaps(map));
if (machine__is(machine, "x86_64"))
machine__map_x86_64_entry_trampolines(machine, dso);
goto out;
@@ -1904,8 +1964,7 @@ int dso__load(struct dso *dso, struct map *map)
is_reg = is_regular_file(name);
if (!is_reg && errno == ENOENT && dso->nsinfo) {
- char *new_name = filename_with_chroot(dso->nsinfo->pid,
- name);
+ char *new_name = dso__filename_with_chroot(dso, name);
if (new_name) {
is_reg = is_regular_file(new_name);
strlcpy(name, new_name, PATH_MAX);
@@ -1994,37 +2053,61 @@ out:
static int map__strcmp(const void *a, const void *b)
{
- const struct map *ma = *(const struct map **)a, *mb = *(const struct map **)b;
- return strcmp(ma->dso->short_name, mb->dso->short_name);
+ const struct map *map_a = *(const struct map **)a;
+ const struct map *map_b = *(const struct map **)b;
+ const struct dso *dso_a = map__dso(map_a);
+ const struct dso *dso_b = map__dso(map_b);
+ int ret = strcmp(dso_a->short_name, dso_b->short_name);
+
+ if (ret == 0 && map_a != map_b) {
+ /*
+ * Ensure distinct but name equal maps have an order in part to
+ * aid reference counting.
+ */
+ ret = (int)map__start(map_a) - (int)map__start(map_b);
+ if (ret == 0)
+ ret = (int)((intptr_t)map_a - (intptr_t)map_b);
+ }
+
+ return ret;
}
static int map__strcmp_name(const void *name, const void *b)
{
- const struct map *map = *(const struct map **)b;
- return strcmp(name, map->dso->short_name);
+ const struct dso *dso = map__dso(*(const struct map **)b);
+
+ return strcmp(name, dso->short_name);
}
void __maps__sort_by_name(struct maps *maps)
{
- qsort(maps->maps_by_name, maps->nr_maps, sizeof(struct map *), map__strcmp);
+ qsort(maps__maps_by_name(maps), maps__nr_maps(maps), sizeof(struct map *), map__strcmp);
}
static int map__groups__sort_by_name_from_rbtree(struct maps *maps)
{
- struct map *map;
- struct map **maps_by_name = realloc(maps->maps_by_name, maps->nr_maps * sizeof(map));
+ struct map_rb_node *rb_node;
+ struct map **maps_by_name = realloc(maps__maps_by_name(maps),
+ maps__nr_maps(maps) * sizeof(struct map *));
int i = 0;
if (maps_by_name == NULL)
return -1;
- maps->maps_by_name = maps_by_name;
- maps->nr_maps_allocated = maps->nr_maps;
+ up_read(maps__lock(maps));
+ down_write(maps__lock(maps));
- maps__for_each_entry(maps, map)
- maps_by_name[i++] = map;
+ RC_CHK_ACCESS(maps)->maps_by_name = maps_by_name;
+ RC_CHK_ACCESS(maps)->nr_maps_allocated = maps__nr_maps(maps);
+
+ maps__for_each_entry(maps, rb_node)
+ maps_by_name[i++] = map__get(rb_node->map);
__maps__sort_by_name(maps);
+
+ up_write(maps__lock(maps));
+ down_read(maps__lock(maps));
+
return 0;
}
@@ -2032,11 +2115,12 @@ static struct map *__maps__find_by_name(struct maps *maps, const char *name)
{
struct map **mapp;
- if (maps->maps_by_name == NULL &&
+ if (maps__maps_by_name(maps) == NULL &&
map__groups__sort_by_name_from_rbtree(maps))
return NULL;
- mapp = bsearch(name, maps->maps_by_name, maps->nr_maps, sizeof(*mapp), map__strcmp_name);
+ mapp = bsearch(name, maps__maps_by_name(maps), maps__nr_maps(maps),
+ sizeof(*mapp), map__strcmp_name);
if (mapp)
return *mapp;
return NULL;
@@ -2044,13 +2128,19 @@ static struct map *__maps__find_by_name(struct maps *maps, const char *name)
struct map *maps__find_by_name(struct maps *maps, const char *name)
{
+ struct map_rb_node *rb_node;
struct map *map;
- down_read(&maps->lock);
+ down_read(maps__lock(maps));
- if (maps->last_search_by_name && strcmp(maps->last_search_by_name->dso->short_name, name) == 0) {
- map = maps->last_search_by_name;
- goto out_unlock;
+
+ if (RC_CHK_ACCESS(maps)->last_search_by_name) {
+ const struct dso *dso = map__dso(RC_CHK_ACCESS(maps)->last_search_by_name);
+
+ if (strcmp(dso->short_name, name) == 0) {
+ map = RC_CHK_ACCESS(maps)->last_search_by_name;
+ goto out_unlock;
+ }
}
/*
* If we have maps->maps_by_name, then the name isn't in the rbtree,
@@ -2058,20 +2148,24 @@ struct map *maps__find_by_name(struct maps *maps, const char *name)
* made.
*/
map = __maps__find_by_name(maps, name);
- if (map || maps->maps_by_name != NULL)
+ if (map || maps__maps_by_name(maps) != NULL)
goto out_unlock;
/* Fallback to traversing the rbtree... */
- maps__for_each_entry(maps, map)
- if (strcmp(map->dso->short_name, name) == 0) {
- maps->last_search_by_name = map;
+ maps__for_each_entry(maps, rb_node) {
+ struct dso *dso;
+
+ map = rb_node->map;
+ dso = map__dso(map);
+ if (strcmp(dso->short_name, name) == 0) {
+ RC_CHK_ACCESS(maps)->last_search_by_name = map;
goto out_unlock;
}
-
+ }
map = NULL;
out_unlock:
- up_read(&maps->lock);
+ up_read(maps__lock(maps));
return map;
}
@@ -2323,7 +2417,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map)
{
int err;
const char *kallsyms_filename;
- struct machine *machine = map__kmaps(map)->machine;
+ struct machine *machine = maps__machine(map__kmaps(map));
char path[PATH_MAX];
if (machine->kallsyms_filename) {
diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h
index bc3d046fbb63..f26f81eb8252 100644
--- a/tools/perf/util/symbol_conf.h
+++ b/tools/perf/util/symbol_conf.h
@@ -33,7 +33,6 @@ struct symbol_conf {
demangle_kernel,
filter_relative,
show_hist_headers,
- branch_callstack,
has_filter,
show_ref_callgraph,
hide_unresolved,
@@ -61,6 +60,7 @@ struct symbol_conf {
*sym_list_str,
*col_width_list_str,
*bt_stop_list_str;
+ char *addr2line_path;
unsigned long time_quantum;
struct strlist *dso_list,
*comm_list,
diff --git a/tools/perf/util/symbol_fprintf.c b/tools/perf/util/symbol_fprintf.c
index 2664fb65e47a..d9e5ad040b6a 100644
--- a/tools/perf/util/symbol_fprintf.c
+++ b/tools/perf/util/symbol_fprintf.c
@@ -30,7 +30,7 @@ size_t __symbol__fprintf_symname_offs(const struct symbol *sym,
if (al->addr < sym->end)
offset = al->addr - sym->start;
else
- offset = al->addr - al->map->start - sym->start;
+ offset = al->addr - map__start(al->map) - sym->start;
length += fprintf(fp, "+0x%lx", offset);
}
return length;
diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c
index 9ab9308ee80c..b2e4afa5efa1 100644
--- a/tools/perf/util/synthetic-events.c
+++ b/tools/perf/util/synthetic-events.c
@@ -669,7 +669,7 @@ int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t
struct machine *machine)
{
int rc = 0;
- struct map *pos;
+ struct map_rb_node *pos;
struct maps *maps = machine__kernel_maps(machine);
union perf_event *event;
size_t size = symbol_conf.buildid_mmap2 ?
@@ -692,37 +692,39 @@ int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t
event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL;
maps__for_each_entry(maps, pos) {
- if (!__map__is_kmodule(pos))
+ struct map *map = pos->map;
+ struct dso *dso;
+
+ if (!__map__is_kmodule(map))
continue;
+ dso = map__dso(map);
if (symbol_conf.buildid_mmap2) {
- size = PERF_ALIGN(pos->dso->long_name_len + 1, sizeof(u64));
+ size = PERF_ALIGN(dso->long_name_len + 1, sizeof(u64));
event->mmap2.header.type = PERF_RECORD_MMAP2;
event->mmap2.header.size = (sizeof(event->mmap2) -
(sizeof(event->mmap2.filename) - size));
memset(event->mmap2.filename + size, 0, machine->id_hdr_size);
event->mmap2.header.size += machine->id_hdr_size;
- event->mmap2.start = pos->start;
- event->mmap2.len = pos->end - pos->start;
+ event->mmap2.start = map__start(map);
+ event->mmap2.len = map__size(map);
event->mmap2.pid = machine->pid;
- memcpy(event->mmap2.filename, pos->dso->long_name,
- pos->dso->long_name_len + 1);
+ memcpy(event->mmap2.filename, dso->long_name, dso->long_name_len + 1);
perf_record_mmap2__read_build_id(&event->mmap2, machine, false);
} else {
- size = PERF_ALIGN(pos->dso->long_name_len + 1, sizeof(u64));
+ size = PERF_ALIGN(dso->long_name_len + 1, sizeof(u64));
event->mmap.header.type = PERF_RECORD_MMAP;
event->mmap.header.size = (sizeof(event->mmap) -
(sizeof(event->mmap.filename) - size));
memset(event->mmap.filename + size, 0, machine->id_hdr_size);
event->mmap.header.size += machine->id_hdr_size;
- event->mmap.start = pos->start;
- event->mmap.len = pos->end - pos->start;
+ event->mmap.start = map__start(map);
+ event->mmap.len = map__size(map);
event->mmap.pid = machine->pid;
- memcpy(event->mmap.filename, pos->dso->long_name,
- pos->dso->long_name_len + 1);
+ memcpy(event->mmap.filename, dso->long_name, dso->long_name_len + 1);
}
if (perf_tool__process_synth_event(tool, event, machine, process) != 0) {
@@ -1141,8 +1143,8 @@ static int __perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
event->mmap2.header.size = (sizeof(event->mmap2) -
(sizeof(event->mmap2.filename) - size) + machine->id_hdr_size);
event->mmap2.pgoff = kmap->ref_reloc_sym->addr;
- event->mmap2.start = map->start;
- event->mmap2.len = map->end - event->mmap.start;
+ event->mmap2.start = map__start(map);
+ event->mmap2.len = map__end(map) - event->mmap.start;
event->mmap2.pid = machine->pid;
perf_record_mmap2__read_build_id(&event->mmap2, machine, true);
@@ -1154,8 +1156,8 @@ static int __perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
event->mmap.header.size = (sizeof(event->mmap) -
(sizeof(event->mmap.filename) - size) + machine->id_hdr_size);
event->mmap.pgoff = kmap->ref_reloc_sym->addr;
- event->mmap.start = map->start;
- event->mmap.len = map->end - event->mmap.start;
+ event->mmap.start = map__start(map);
+ event->mmap.len = map__end(map) - event->mmap.start;
event->mmap.pid = machine->pid;
}
@@ -2004,7 +2006,7 @@ int perf_event__synthesize_event_update_name(struct perf_tool *tool, struct evse
perf_event__handler_t process)
{
struct perf_record_event_update *ev;
- size_t len = strlen(evsel->name);
+ size_t len = strlen(evsel__name(evsel));
int err;
ev = event_update_event__new(len + 1, PERF_EVENT_UPDATE__NAME, evsel->core.id[0]);
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index 1b992bbba4e8..4b85c1728012 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -155,8 +155,8 @@ static int thread_stack__init(struct thread_stack *ts, struct thread *thread,
ts->br_stack_sz = br_stack_sz;
}
- if (thread->maps && thread->maps->machine) {
- struct machine *machine = thread->maps->machine;
+ if (thread->maps && maps__machine(thread->maps)) {
+ struct machine *machine = maps__machine(thread->maps);
const char *arch = perf_env__arch(machine->env);
ts->kernel_start = machine__kernel_start(machine);
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index e3e5427e1c3c..4b5bdc277baa 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -311,17 +311,30 @@ const char *thread__comm_str(struct thread *thread)
return str;
}
+static int __thread__comm_len(struct thread *thread, const char *comm)
+{
+ if (!comm)
+ return 0;
+ thread->comm_len = strlen(comm);
+
+ return thread->comm_len;
+}
+
/* CHECKME: it should probably better return the max comm len from its comm list */
int thread__comm_len(struct thread *thread)
{
- if (!thread->comm_len) {
- const char *comm = thread__comm_str(thread);
- if (!comm)
- return 0;
- thread->comm_len = strlen(comm);
+ int comm_len = thread->comm_len;
+
+ if (!comm_len) {
+ const char *comm;
+
+ down_read(&thread->comm_lock);
+ comm = __thread__comm_str(thread);
+ comm_len = __thread__comm_len(thread, comm);
+ up_read(&thread->comm_lock);
}
- return thread->comm_len;
+ return comm_len;
}
size_t thread__fprintf(struct thread *thread, FILE *fp)
@@ -339,9 +352,7 @@ int thread__insert_map(struct thread *thread, struct map *map)
return ret;
maps__fixup_overlappings(thread->maps, map, stderr);
- maps__insert(thread->maps, map);
-
- return 0;
+ return maps__insert(thread->maps, map);
}
static int __thread__prepare_access(struct thread *thread)
@@ -349,17 +360,17 @@ static int __thread__prepare_access(struct thread *thread)
bool initialized = false;
int err = 0;
struct maps *maps = thread->maps;
- struct map *map;
+ struct map_rb_node *rb_node;
- down_read(&maps->lock);
+ down_read(maps__lock(maps));
- maps__for_each_entry(maps, map) {
- err = unwind__prepare_access(thread->maps, map, &initialized);
+ maps__for_each_entry(maps, rb_node) {
+ err = unwind__prepare_access(thread->maps, rb_node->map, &initialized);
if (err || initialized)
break;
}
- up_read(&maps->lock);
+ up_read(maps__lock(maps));
return err;
}
@@ -437,23 +448,27 @@ struct thread *thread__main_thread(struct machine *machine, struct thread *threa
int thread__memcpy(struct thread *thread, struct machine *machine,
void *buf, u64 ip, int len, bool *is64bit)
{
- u8 cpumode = PERF_RECORD_MISC_USER;
- struct addr_location al;
- long offset;
+ u8 cpumode = PERF_RECORD_MISC_USER;
+ struct addr_location al;
+ struct dso *dso;
+ long offset;
+
+ if (machine__kernel_ip(machine, ip))
+ cpumode = PERF_RECORD_MISC_KERNEL;
+
+ if (!thread__find_map(thread, cpumode, ip, &al))
+ return -1;
- if (machine__kernel_ip(machine, ip))
- cpumode = PERF_RECORD_MISC_KERNEL;
+ dso = map__dso(al.map);
- if (!thread__find_map(thread, cpumode, ip, &al) || !al.map->dso ||
- al.map->dso->data.status == DSO_DATA_STATUS_ERROR ||
- map__load(al.map) < 0)
- return -1;
+ if( !dso || dso->data.status == DSO_DATA_STATUS_ERROR || map__load(al.map) < 0)
+ return -1;
- offset = al.map->map_ip(al.map, ip);
- if (is64bit)
- *is64bit = al.map->dso->is_64_bit;
+ offset = map__map_ip(al.map, ip);
+ if (is64bit)
+ *is64bit = dso->is_64_bit;
- return dso__data_read_offset(al.map->dso, machine, offset, buf, len);
+ return dso__data_read_offset(dso, machine, offset, buf, len);
}
void thread__free_stitch_list(struct thread *thread)
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c
index b8b32431d2f7..be7157de0451 100644
--- a/tools/perf/util/top.c
+++ b/tools/perf/util/top.c
@@ -11,7 +11,7 @@
#include "parse-events.h"
#include "symbol.h"
#include "top.h"
-#include "../perf.h"
+#include "util.h"
#include <inttypes.h>
#define SNPRINTF(buf, size, fmt, args...) \
diff --git a/tools/perf/util/topdown.c b/tools/perf/util/topdown.c
index 1090841550f7..18fd5fed5d1a 100644
--- a/tools/perf/util/topdown.c
+++ b/tools/perf/util/topdown.c
@@ -1,74 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
-#include <stdio.h>
-#include "pmu.h"
-#include "pmu-hybrid.h"
#include "topdown.h"
-
-int topdown_filter_events(const char **attr, char **str, bool use_group,
- const char *pmu_name)
-{
- int off = 0;
- int i;
- int len = 0;
- char *s;
- bool is_hybrid = perf_pmu__is_hybrid(pmu_name);
-
- for (i = 0; attr[i]; i++) {
- if (pmu_have_event(pmu_name, attr[i])) {
- if (is_hybrid)
- len += strlen(attr[i]) + strlen(pmu_name) + 3;
- else
- len += strlen(attr[i]) + 1;
- attr[i - off] = attr[i];
- } else
- off++;
- }
- attr[i - off] = NULL;
-
- *str = malloc(len + 1 + 2);
- if (!*str)
- return -1;
- s = *str;
- if (i - off == 0) {
- *s = 0;
- return 0;
- }
- if (use_group)
- *s++ = '{';
- for (i = 0; attr[i]; i++) {
- if (!is_hybrid)
- strcpy(s, attr[i]);
- else
- sprintf(s, "%s/%s/", pmu_name, attr[i]);
- s += strlen(s);
- *s++ = ',';
- }
- if (use_group) {
- s[-1] = '}';
- *s = 0;
- } else
- s[-1] = 0;
- return 0;
-}
-
-__weak bool arch_topdown_check_group(bool *warn)
-{
- *warn = false;
- return false;
-}
-
-__weak void arch_topdown_group_warn(void)
-{
-}
+#include <linux/kernel.h>
__weak bool arch_topdown_sample_read(struct evsel *leader __maybe_unused)
{
return false;
}
-
-__weak const char *arch_get_topdown_pmu_name(struct evlist *evlist
- __maybe_unused,
- bool warn __maybe_unused)
-{
- return "cpu";
-}
diff --git a/tools/perf/util/topdown.h b/tools/perf/util/topdown.h
index f9531528c559..1996c5fedcd7 100644
--- a/tools/perf/util/topdown.h
+++ b/tools/perf/util/topdown.h
@@ -1,14 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef TOPDOWN_H
#define TOPDOWN_H 1
-#include "evsel.h"
-#include "evlist.h"
-bool arch_topdown_check_group(bool *warn);
-void arch_topdown_group_warn(void);
+#include <stdbool.h>
+
+struct evsel;
+
bool arch_topdown_sample_read(struct evsel *leader);
-const char *arch_get_topdown_pmu_name(struct evlist *evlist, bool warn);
-int topdown_filter_events(const char **attr, char **str, bool use_group,
- const char *pmu_name);
#endif
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index 56175c53f9af..bd0000300c77 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -9,7 +9,9 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#ifdef HAVE_LIBTRACEEVENT
#include <traceevent/event-parse.h>
+#endif
#include "debug.h"
#include "trace-event.h"
@@ -27,10 +29,11 @@ void scripting_context__update(struct scripting_context *c,
struct addr_location *addr_al)
{
c->event_data = sample->raw_data;
+ c->pevent = NULL;
+#ifdef HAVE_LIBTRACEEVENT
if (evsel->tp_format)
c->pevent = evsel->tp_format->tep;
- else
- c->pevent = NULL;
+#endif
c->event = event;
c->sample = sample;
c->evsel = evsel;
@@ -122,6 +125,7 @@ void setup_python_scripting(void)
}
#endif
+#ifdef HAVE_LIBTRACEEVENT
static void print_perl_unsupported_msg(void)
{
fprintf(stderr, "Perl scripting not supported."
@@ -186,3 +190,4 @@ void setup_perl_scripting(void)
register_perl_scripting(&perl_scripting_ops);
}
#endif
+#endif
diff --git a/tools/perf/util/tracepoint.c b/tools/perf/util/tracepoint.c
index 89ef56c43311..92dd8b455b90 100644
--- a/tools/perf/util/tracepoint.c
+++ b/tools/perf/util/tracepoint.c
@@ -50,6 +50,7 @@ int is_valid_tracepoint(const char *event_string)
sys_dirent->d_name, evt_dirent->d_name);
if (!strcmp(evt_path, event_string)) {
closedir(evt_dir);
+ put_events_file(dir_path);
closedir(sys_dir);
return 1;
}
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 94aa40f6e348..bdccfc511b7e 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -52,7 +52,7 @@ static int __report_module(struct addr_location *al, u64 ip,
thread__find_symbol(ui->thread, PERF_RECORD_MISC_USER, ip, al);
if (al->map)
- dso = al->map->dso;
+ dso = map__dso(al->map);
if (!dso)
return 0;
@@ -62,19 +62,19 @@ static int __report_module(struct addr_location *al, u64 ip,
Dwarf_Addr s;
dwfl_module_info(mod, NULL, &s, NULL, NULL, NULL, NULL, NULL);
- if (s != al->map->start - al->map->pgoff)
+ if (s != map__start(al->map) - map__pgoff(al->map))
mod = 0;
}
if (!mod)
mod = dwfl_report_elf(ui->dwfl, dso->short_name, dso->long_name, -1,
- al->map->start - al->map->pgoff, false);
+ map__start(al->map) - map__pgoff(al->map), false);
if (!mod) {
char filename[PATH_MAX];
if (dso__build_id_filename(dso, filename, sizeof(filename), false))
mod = dwfl_report_elf(ui->dwfl, dso->short_name, filename, -1,
- al->map->start - al->map->pgoff, false);
+ map__start(al->map) - map__pgoff(al->map), false);
}
if (mod) {
@@ -115,7 +115,7 @@ static int entry(u64 ip, struct unwind_info *ui)
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
al.sym ? al.sym->name : "''",
ip,
- al.map ? al.map->map_ip(al.map, ip) : (u64) 0);
+ al.map ? map__map_ip(al.map, ip) : (u64) 0);
return 0;
}
@@ -134,17 +134,17 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr,
{
struct addr_location al;
ssize_t size;
+ struct dso *dso;
if (!thread__find_map(ui->thread, PERF_RECORD_MISC_USER, addr, &al)) {
pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
return -1;
}
-
- if (!al.map->dso)
+ dso = map__dso(al.map);
+ if (!dso)
return -1;
- size = dso__data_read_addr(al.map->dso, al.map, ui->machine,
- addr, (u8 *) data, sizeof(*data));
+ size = dso__data_read_addr(dso, al.map, ui->machine, addr, (u8 *) data, sizeof(*data));
return !(size == sizeof(*data));
}
@@ -230,7 +230,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct unwind_info *ui, ui_buf = {
.sample = data,
.thread = thread,
- .machine = thread->maps->machine,
+ .machine = RC_CHK_ACCESS(thread->maps)->machine,
.cb = cb,
.arg = arg,
.max_stack = max_stack,
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c
index 81b6bd6e1536..83dd79dcd597 100644
--- a/tools/perf/util/unwind-libunwind-local.c
+++ b/tools/perf/util/unwind-libunwind-local.c
@@ -306,7 +306,7 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *ui,
u64 *table_data, u64 *segbase,
u64 *fde_count)
{
- struct map *map;
+ struct map_rb_node *map_node;
u64 base_addr = UINT64_MAX;
int ret, fd;
@@ -325,9 +325,12 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *ui,
return -EINVAL;
}
- maps__for_each_entry(ui->thread->maps, map) {
- if (map->dso == dso && map->start < base_addr)
- base_addr = map->start;
+ maps__for_each_entry(ui->thread->maps, map_node) {
+ struct map *map = map_node->map;
+ u64 start = map__start(map);
+
+ if (map__dso(map) == dso && start < base_addr)
+ base_addr = start;
}
base_addr -= dso->data.elf_base_addr;
/* Address of .eh_frame_hdr */
@@ -422,23 +425,27 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
{
struct unwind_info *ui = arg;
struct map *map;
+ struct dso *dso;
unw_dyn_info_t di;
u64 table_data, segbase, fde_count;
int ret = -EINVAL;
map = find_map(ip, ui);
- if (!map || !map->dso)
+ if (!map)
+ return -EINVAL;
+
+ dso = map__dso(map);
+ if (!dso)
return -EINVAL;
- pr_debug("unwind: find_proc_info dso %s\n", map->dso->name);
+ pr_debug("unwind: find_proc_info dso %s\n", dso->name);
/* Check the .eh_frame section for unwinding info */
- if (!read_unwind_spec_eh_frame(map->dso, ui,
- &table_data, &segbase, &fde_count)) {
+ if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count)) {
memset(&di, 0, sizeof(di));
di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
- di.start_ip = map->start;
- di.end_ip = map->end;
+ di.start_ip = map__start(map);
+ di.end_ip = map__end(map);
di.u.rti.segbase = segbase;
di.u.rti.table_data = table_data;
di.u.rti.table_len = fde_count * sizeof(struct table_entry)
@@ -450,20 +457,20 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
#ifndef NO_LIBUNWIND_DEBUG_FRAME
/* Check the .debug_frame section for unwinding info */
if (ret < 0 &&
- !read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) {
- int fd = dso__data_get_fd(map->dso, ui->machine);
- int is_exec = elf_is_exec(fd, map->dso->name);
- unw_word_t base = is_exec ? 0 : map->start;
+ !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) {
+ int fd = dso__data_get_fd(dso, ui->machine);
+ int is_exec = elf_is_exec(fd, dso->name);
+ u64 start = map__start(map);
+ unw_word_t base = is_exec ? 0 : start;
const char *symfile;
if (fd >= 0)
- dso__data_put_fd(map->dso);
+ dso__data_put_fd(dso);
- symfile = map->dso->symsrc_filename ?: map->dso->name;
+ symfile = dso->symsrc_filename ?: dso->name;
memset(&di, 0, sizeof(di));
- if (dwarf_find_debug_frame(0, &di, ip, base, symfile,
- map->start, map->end))
+ if (dwarf_find_debug_frame(0, &di, ip, base, symfile, start, map__end(map)))
return dwarf_search_unwind_table(as, ip, &di, pi,
need_unwind_info, arg);
}
@@ -511,6 +518,7 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
unw_word_t *data)
{
struct map *map;
+ struct dso *dso;
ssize_t size;
map = find_map(addr, ui);
@@ -519,10 +527,12 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
return -1;
}
- if (!map->dso)
+ dso = map__dso(map);
+
+ if (!dso)
return -1;
- size = dso__data_read_addr(map->dso, map, ui->machine,
+ size = dso__data_read_addr(dso, map, ui->machine,
addr, (u8 *) data, sizeof(*data));
return !(size == sizeof(*data));
@@ -630,7 +640,7 @@ static int entry(u64 ip, struct thread *thread,
pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
al.sym ? al.sym->name : "''",
ip,
- al.map ? al.map->map_ip(al.map, ip) : (u64) 0);
+ al.map ? map__map_ip(al.map, ip) : (u64) 0);
return cb(&e, arg);
}
@@ -665,24 +675,26 @@ static unw_accessors_t accessors = {
static int _unwind__prepare_access(struct maps *maps)
{
- maps->addr_space = unw_create_addr_space(&accessors, 0);
- if (!maps->addr_space) {
+ void *addr_space = unw_create_addr_space(&accessors, 0);
+
+ RC_CHK_ACCESS(maps)->addr_space = addr_space;
+ if (!addr_space) {
pr_err("unwind: Can't create unwind address space.\n");
return -ENOMEM;
}
- unw_set_caching_policy(maps->addr_space, UNW_CACHE_GLOBAL);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
return 0;
}
static void _unwind__flush_access(struct maps *maps)
{
- unw_flush_cache(maps->addr_space, 0, 0);
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
}
static void _unwind__finish_access(struct maps *maps)
{
- unw_destroy_addr_space(maps->addr_space);
+ unw_destroy_addr_space(maps__addr_space(maps));
}
static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
@@ -707,7 +719,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
*/
if (max_stack - 1 > 0) {
WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL");
- addr_space = ui->thread->maps->addr_space;
+ addr_space = maps__addr_space(ui->thread->maps);
if (addr_space == NULL)
return -1;
@@ -757,7 +769,7 @@ static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct unwind_info ui = {
.sample = data,
.thread = thread,
- .machine = thread->maps->machine,
+ .machine = maps__machine(thread->maps),
.best_effort = best_effort
};
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c
index 509c287ee762..375d23d9a590 100644
--- a/tools/perf/util/unwind-libunwind.c
+++ b/tools/perf/util/unwind-libunwind.c
@@ -14,7 +14,7 @@ struct unwind_libunwind_ops __weak *arm64_unwind_libunwind_ops;
static void unwind__register_ops(struct maps *maps, struct unwind_libunwind_ops *ops)
{
- maps->unwind_libunwind_ops = ops;
+ RC_CHK_ACCESS(maps)->unwind_libunwind_ops = ops;
}
int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized)
@@ -22,28 +22,30 @@ int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized
const char *arch;
enum dso_type dso_type;
struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
+ struct dso *dso = map__dso(map);
+ struct machine *machine;
int err;
if (!dwarf_callchain_users)
return 0;
- if (maps->addr_space) {
- pr_debug("unwind: thread map already set, dso=%s\n",
- map->dso->name);
+ if (maps__addr_space(maps)) {
+ pr_debug("unwind: thread map already set, dso=%s\n", dso->name);
if (initialized)
*initialized = true;
return 0;
}
+ machine = maps__machine(maps);
/* env->arch is NULL for live-mode (i.e. perf top) */
- if (!maps->machine->env || !maps->machine->env->arch)
+ if (!machine->env || !machine->env->arch)
goto out_register;
- dso_type = dso__type(map->dso, maps->machine);
+ dso_type = dso__type(dso, machine);
if (dso_type == DSO__TYPE_UNKNOWN)
return 0;
- arch = perf_env__arch(maps->machine->env);
+ arch = perf_env__arch(machine->env);
if (!strcmp(arch, "x86")) {
if (dso_type != DSO__TYPE_64BIT)
@@ -54,13 +56,13 @@ int unwind__prepare_access(struct maps *maps, struct map *map, bool *initialized
}
if (!ops) {
- pr_err("unwind: target platform=%s is not supported\n", arch);
+ pr_warning_once("unwind: target platform=%s is not supported\n", arch);
return 0;
}
out_register:
unwind__register_ops(maps, ops);
- err = maps->unwind_libunwind_ops->prepare_access(maps);
+ err = maps__unwind_libunwind_ops(maps)->prepare_access(maps);
if (initialized)
*initialized = err ? false : true;
return err;
@@ -68,14 +70,18 @@ out_register:
void unwind__flush_access(struct maps *maps)
{
- if (maps->unwind_libunwind_ops)
- maps->unwind_libunwind_ops->flush_access(maps);
+ const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps);
+
+ if (ops)
+ ops->flush_access(maps);
}
void unwind__finish_access(struct maps *maps)
{
- if (maps->unwind_libunwind_ops)
- maps->unwind_libunwind_ops->finish_access(maps);
+ const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(maps);
+
+ if (ops)
+ ops->finish_access(maps);
}
int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
@@ -83,8 +89,9 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
struct perf_sample *data, int max_stack,
bool best_effort)
{
- if (thread->maps->unwind_libunwind_ops)
- return thread->maps->unwind_libunwind_ops->get_entries(cb, arg, thread, data,
- max_stack, best_effort);
+ const struct unwind_libunwind_ops *ops = maps__unwind_libunwind_ops(thread->maps);
+
+ if (ops)
+ return ops->get_entries(cb, arg, thread, data, max_stack, best_effort);
return 0;
}
diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c
index 196438ee4c9d..4c8ffbad2323 100644
--- a/tools/perf/util/usage.c
+++ b/tools/perf/util/usage.c
@@ -12,6 +12,12 @@
#include <stdlib.h>
#include <linux/compiler.h>
+const char perf_usage_string[] =
+ "perf [--version] [--help] [OPTIONS] COMMAND [ARGS]";
+
+const char perf_more_info_string[] =
+ "See 'perf help COMMAND' for more information on a specific command.";
+
static __noreturn void usage_builtin(const char *err)
{
fprintf(stderr, "\n Usage: %s\n", err);
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 391c1e928bd7..c1fd9ba6d697 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -28,6 +28,8 @@
* XXX We need to find a better place for these things...
*/
+const char *input_name;
+
bool perf_singlethreaded = true;
void perf_set_singlethreaded(void)
@@ -524,7 +526,8 @@ int do_realloc_array_as_needed(void **arr, size_t *arr_sz, size_t x, size_t msz,
new_arr = calloc(new_sz, msz);
if (!new_arr)
return -ENOMEM;
- memcpy(new_arr, *arr, *arr_sz * msz);
+ if (*arr_sz)
+ memcpy(new_arr, *arr, *arr_sz * msz);
if (init_val) {
for (i = *arr_sz; i < new_sz; i++)
memcpy(new_arr + (i * msz), init_val, msz);
@@ -533,3 +536,19 @@ int do_realloc_array_as_needed(void **arr, size_t *arr_sz, size_t x, size_t msz,
*arr_sz = new_sz;
return 0;
}
+
+#ifndef HAVE_SCHED_GETCPU_SUPPORT
+int sched_getcpu(void)
+{
+#ifdef __NR_getcpu
+ unsigned int cpu;
+ int err = syscall(__NR_getcpu, &cpu, NULL, NULL);
+
+ if (!err)
+ return cpu;
+#else
+ errno = ENOSYS;
+#endif
+ return -1;
+}
+#endif
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 1d3b300af5a1..7c8915d92dca 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -15,6 +15,14 @@
#include <internal/cpumap.h>
#endif
+extern const char perf_usage_string[];
+extern const char perf_more_info_string[];
+
+extern const char *input_name;
+
+extern bool perf_host;
+extern bool perf_guest;
+
/* General helper functions */
void usage(const char *err) __noreturn;
void die(const char *err, ...) __noreturn __printf(1, 2);
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index 43beb169631d..ec777ee11493 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -144,10 +144,11 @@ static enum dso_type machine__thread_dso_type(struct machine *machine,
struct thread *thread)
{
enum dso_type dso_type = DSO__TYPE_UNKNOWN;
- struct map *map;
+ struct map_rb_node *rb_node;
+
+ maps__for_each_entry(thread->maps, rb_node) {
+ struct dso *dso = map__dso(rb_node->map);
- maps__for_each_entry(thread->maps, map) {
- struct dso *dso = map->dso;
if (!dso || dso->long_name[0] != '/')
continue;
dso_type = dso__type(dso, machine);