diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-05-07 11:32:18 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-05-07 11:32:18 -0700 |
commit | f085df1be60abf670315c11036261cfaec16b2eb (patch) | |
tree | c02c07ad31578b90c3cc99be6b5ba680d163bca7 /tools/perf/util | |
parent | 17784de648be93b4eef0ef8fe28a16ff04feecc7 (diff) | |
parent | 9a2d5178b9d51e1c5f9e08989ff97fc8d4893f31 (diff) |
Merge tag 'perf-tools-for-v6.4-3-2023-05-06' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux
Pull perf tool updates from Arnaldo Carvalho de Melo:
"Third version of perf tool updates, with the build problems with with
using a 'vmlinux.h' generated from the main build fixed, and the bpf
skeleton build disabled by default.
Build:
- Require libtraceevent to build, one can disable it using
NO_LIBTRACEEVENT=1.
It is required for tools like 'perf sched', 'perf kvm', 'perf
trace', etc.
libtraceevent is available in most distros so installing
'libtraceevent-devel' should be a one-time event to continue
building perf as usual.
Using NO_LIBTRACEEVENT=1 produces tooling that is functional and
sufficient for lots of users not interested in those libtraceevent
dependent features.
- Allow Python support in 'perf script' when libtraceevent isn't
linked, as not all features requires it, for instance Intel PT does
not use tracepoints.
- Error if the python interpreter needed for jevents to work isn't
available and NO_JEVENTS=1 isn't set, preventing a build without
support for JSON vendor events, which is a rare but possible
condition. The two check error messages:
$(error ERROR: No python interpreter needed for jevents generation. Install python or build with NO_JEVENTS=1.)
$(error ERROR: Python interpreter needed for jevents generation too old (older than 3.6). Install a newer python or build with NO_JEVENTS=1.)
- Make libbpf 1.0 the minimum required when building with out of
tree, distro provided libbpf.
- Use libsdtc++'s and LLVM's libcxx's __cxa_demangle, a portable C++
demangler, add 'perf test' entry for it.
- Make binutils libraries opt in, as distros disable building with it
due to licensing, they were used for C++ demangling, for instance.
- Switch libpfm4 to opt-out rather than opt-in, if libpfm-devel (or
equivalent) isn't installed, we'll just have a build warning:
Makefile.config:1144: libpfm4 not found, disables libpfm4 support. Please install libpfm4-dev
- Add a feature test for scandirat(), that is not implemented so far
in musl and uclibc, disabling features that need it, such as
scanning for tracepoints in /sys/kernel/tracing/events.
perf BPF filters:
- New feature where BPF can be used to filter samples, for instance:
$ sudo ./perf record -e cycles --filter 'period > 1000' true
$ sudo ./perf script
perf-exec 2273949 546850.708501: 5029 cycles: ffffffff826f9e25 finish_wait+0x5 ([kernel.kallsyms])
perf-exec 2273949 546850.708508: 32409 cycles: ffffffff826f9e25 finish_wait+0x5 ([kernel.kallsyms])
perf-exec 2273949 546850.708526: 143369 cycles: ffffffff82b4cdbf xas_start+0x5f ([kernel.kallsyms])
perf-exec 2273949 546850.708600: 372650 cycles: ffffffff8286b8f7 __pagevec_lru_add+0x117 ([kernel.kallsyms])
perf-exec 2273949 546850.708791: 482953 cycles: ffffffff829190de __mod_memcg_lruvec_state+0x4e ([kernel.kallsyms])
true 2273949 546850.709036: 501985 cycles: ffffffff828add7c tlb_gather_mmu+0x4c ([kernel.kallsyms])
true 2273949 546850.709292: 503065 cycles: 7f2446d97c03 _dl_map_object_deps+0x973 (/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2)
- In addition to 'period' (PERF_SAMPLE_PERIOD), the other
PERF_SAMPLE_ can be used for filtering, and also some other sample
accessible values, from tools/perf/Documentation/perf-record.txt:
Essentially the BPF filter expression is:
<term> <operator> <value> (("," | "||") <term> <operator> <value>)*
The <term> can be one of:
ip, id, tid, pid, cpu, time, addr, period, txn, weight, phys_addr,
code_pgsz, data_pgsz, weight1, weight2, weight3, ins_lat, retire_lat,
p_stage_cyc, mem_op, mem_lvl, mem_snoop, mem_remote, mem_lock,
mem_dtlb, mem_blk, mem_hops
The <operator> can be one of:
==, !=, >, >=, <, <=, &
The <value> can be one of:
<number> (for any term)
na, load, store, pfetch, exec (for mem_op)
l1, l2, l3, l4, cxl, io, any_cache, lfb, ram, pmem (for mem_lvl)
na, none, hit, miss, hitm, fwd, peer (for mem_snoop)
remote (for mem_remote)
na, locked (for mem_locked)
na, l1_hit, l1_miss, l2_hit, l2_miss, any_hit, any_miss, walk, fault (for mem_dtlb)
na, by_data, by_addr (for mem_blk)
hops0, hops1, hops2, hops3 (for mem_hops)
perf lock contention:
- Show lock type with address.
- Track and show mmap_lock, siglock and per-cpu rq_lock with address.
This is done for mmap_lock by following the current->mm pointer:
$ sudo ./perf lock con -abl -- sleep 10
contended total wait max wait avg wait address symbol
...
16344 312.30 ms 2.22 ms 19.11 us ffff8cc702595640
17686 310.08 ms 1.49 ms 17.53 us ffff8cc7025952c0
3 84.14 ms 45.79 ms 28.05 ms ffff8cc78114c478 mmap_lock
3557 76.80 ms 68.75 us 21.59 us ffff8cc77ca3af58
1 68.27 ms 68.27 ms 68.27 ms ffff8cda745dfd70
9 54.53 ms 7.96 ms 6.06 ms ffff8cc7642a48b8 mmap_lock
14629 44.01 ms 60.00 us 3.01 us ffff8cc7625f9ca0
3481 42.63 ms 140.71 us 12.24 us ffffffff937906ac vmap_area_lock
16194 38.73 ms 42.15 us 2.39 us ffff8cd397cbc560
11 38.44 ms 10.39 ms 3.49 ms ffff8ccd6d12fbb8 mmap_lock
1 5.43 ms 5.43 ms 5.43 ms ffff8cd70018f0d8
1674 5.38 ms 422.93 us 3.21 us ffffffff92e06080 tasklist_lock
581 4.51 ms 130.68 us 7.75 us ffff8cc9b1259058
5 3.52 ms 1.27 ms 703.23 us ffff8cc754510070
112 3.47 ms 56.47 us 31.02 us ffff8ccee38b3120
381 3.31 ms 73.44 us 8.69 us ffffffff93790690 purge_vmap_area_lock
255 3.19 ms 36.35 us 12.49 us ffff8d053ce30c80
- Update default map size to 16384.
- Allocate single letter option -M for --map-nr-entries, as it is
proving being frequently used.
- Fix struct rq lock access for older kernels with BPF's CO-RE
(Compile once, run everywhere).
- Fix problems found with MSAn.
perf report/top:
- Add inline information when using --call-graph=fp or lbr, as was
already done to the --call-graph=dwarf callchain mode.
- Improve the 'srcfile' sort key performance by really using an
optimization introduced in 6.2 for the 'srcline' sort key that
avoids calling addr2line for comparision with each sample.
perf sched:
- Make 'perf sched latency/map/replay' to use "sched:sched_waking"
instead of "sched:sched_waking", consistent with 'perf record'
since d566a9c2d482 ("perf sched: Prefer sched_waking event when it
exists").
perf ftrace:
- Make system wide the default target for latency subcommand, run the
following command then generate some network traffic and press
control+C:
# perf ftrace latency -T __kfree_skb
^C
DURATION | COUNT | GRAPH |
0 - 1 us | 27 | ############# |
1 - 2 us | 22 | ########### |
2 - 4 us | 8 | #### |
4 - 8 us | 5 | ## |
8 - 16 us | 24 | ############ |
16 - 32 us | 2 | # |
32 - 64 us | 1 | |
64 - 128 us | 0 | |
128 - 256 us | 0 | |
256 - 512 us | 0 | |
512 - 1024 us | 0 | |
1 - 2 ms | 0 | |
2 - 4 ms | 0 | |
4 - 8 ms | 0 | |
8 - 16 ms | 0 | |
16 - 32 ms | 0 | |
32 - 64 ms | 0 | |
64 - 128 ms | 0 | |
128 - 256 ms | 0 | |
256 - 512 ms | 0 | |
512 - 1024 ms | 0 | |
1 - ... s | 0 | |
#
perf top:
- Add --branch-history (LBR: Last Branch Record) option, just like
already available for 'perf record'.
- Fix segfault in thread__comm_len() where thread->comm was being
used outside thread->comm_lock.
perf annotate:
- Allow configuring objdump and addr2line in ~/.perfconfig., so that
you can use alternative binaries, such as llvm's.
perf kvm:
- Add TUI mode for 'perf kvm stat report'.
Reference counting:
- Add reference count checking infrastructure to check for use after
free, done to the 'cpumap', 'namespaces', 'maps' and 'map' structs,
more to come.
To build with it use -DREFCNT_CHECKING=1 in the make command line
to build tools/perf. Documented at:
https://perf.wiki.kernel.org/index.php/Reference_Count_Checking
- The above caught, for instance, fix, present in this series:
- Fix maps use after put in 'perf test "Share thread maps"':
'maps' is copied from leader, but the leader is put on line 79
and then 'maps' is used to read the reference count below - so
a use after put, with the put of maps happening within
thread__put.
Fixed by reversing the order of puts so that the leader is put
last.
- Also several fixes were made to places where reference counts were
not being held.
- Make this one of the tests in 'make -C tools/perf build-test' to
regularly build test it and to make sure no direct access to the
reference counted structs are made, doing that via accessors to
check the validity of the struct pointer.
ARM64:
- Fix 'perf report' segfault when filtering coresight traces by
sparse lists of CPUs.
- Add support for 'simd' as a sort field for 'perf report', to show
ARM's NEON SIMD's predicate flags: "partial" and "empty".
arm64 vendor events:
- Add N1 metrics.
Intel vendor events:
- Add graniterapids, grandridge and sierraforrest events.
- Refresh events for: alderlake, aldernaken, broadwell, broadwellde,
broadwellx, cascadelakx, haswell, haswellx, icelake, icelakex,
jaketown, meteorlake, knightslanding, sandybridge, sapphirerapids,
silvermont, skylake, tigerlake and westmereep-dp
- Refresh metrics for alderlake-n, broadwell, broadwellde,
broadwellx, haswell, haswellx, icelakex, ivybridge, ivytown and
skylakex.
perf stat:
- Implement --topdown using JSON metrics.
- Add TopdownL1 JSON metric as a default if present, but disable it
for now for some Intel hybrid architectures, a series of patches
addressing this is being reviewed and will be submitted for v6.5.
- Use metrics for --smi-cost.
- Update topdown documentation.
Vendor events (JSON) infrastructure:
- Add support for computing and printing metric threshold values. For
instance, here is one found in thesapphirerapids json file:
{
"BriefDescription": "Percentage of cycles spent in System Management Interrupts.",
"MetricExpr": "((msr@aperf@ - cycles) / msr@aperf@ if msr@smi@ > 0 else 0)",
"MetricGroup": "smi",
"MetricName": "smi_cycles",
"MetricThreshold": "smi_cycles > 0.1",
"ScaleUnit": "100%"
},
- Test parsing metric thresholds with the fake PMU in 'perf test
pmu-events'.
- Support for printing metric thresholds in 'perf list'.
- Add --metric-no-threshold option to 'perf stat'.
- Add rand (reverse and) and has_pmem (optane memory) support to
metrics.
- Sort list of input files to avoid depending on the order from
readdir() helping in obtaining reproducible builds.
S/390:
- Add common metrics: - CPI (cycles per instruction), prbstate (ratio
of instructions executed in problem state compared to total number
of instructions), l1mp (Level one instruction and data cache misses
per 100 instructions).
- Add cache metrics for z13, z14, z15 and z16.
- Add metric for TLB and cache.
ARM:
- Add raw decoding for SPE (Statistical Profiling Extension) v1.3 MTE
(Memory Tagging Extension) and MOPS (Memory Operations) load/store.
Intel PT hardware tracing:
- Add event type names UINTR (User interrupt delivered) and UIRET
(Exiting from user interrupt routine), documented in table 32-50
"CFE Packet Type and Vector Fields Details" in the Intel Processor
Trace chapter of The Intel SDM Volume 3 version 078.
- Add support for new branch instructions ERETS and ERETU.
- Fix CYC timestamps after standalone CBR
ARM CoreSight hardware tracing:
- Allow user to override timestamp and contextid settings.
- Fix segfault in dso lookup.
- Fix timeless decode mode detection.
- Add separate decode paths for timeless and per-thread modes.
auxtrace:
- Fix address filter entire kernel size.
Miscellaneous:
- Fix use-after-free and unaligned bugs in the PLT handling routines.
- Use zfree() to reduce chances of use after free.
- Add missing 0x prefix for addresses printed in hexadecimal in 'perf
probe'.
- Suppress massive unsupported target platform errors in the unwind
code.
- Fix return incorrect build_id size in elf_read_build_id().
- Fix 'perf scripts intel-pt-events.py' IPC output for Python 2 .
- Add missing new parameter in kfree_skb tracepoint to the python
scripts using it.
- Add 'perf bench syscall fork' benchmark.
- Add support for printing PERF_MEM_LVLNUM_UNC (Uncached access) in
'perf mem'.
- Fix wrong size expectation for perf test 'Setup struct
perf_event_attr' caused by the patch adding
perf_event_attr::config3.
- Fix some spelling mistakes"
* tag 'perf-tools-for-v6.4-3-2023-05-06' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux: (365 commits)
Revert "perf build: Make BUILD_BPF_SKEL default, rename to NO_BPF_SKEL"
Revert "perf build: Warn for BPF skeletons if endian mismatches"
perf metrics: Fix SEGV with --for-each-cgroup
perf bpf skels: Stop using vmlinux.h generated from BTF, use subset of used structs + CO-RE
perf stat: Separate bperf from bpf_profiler
perf test record+probe_libc_inet_pton: Fix call chain match on x86_64
perf test record+probe_libc_inet_pton: Fix call chain match on s390
perf tracepoint: Fix memory leak in is_valid_tracepoint()
perf cs-etm: Add fix for coresight trace for any range of CPUs
perf build: Fix unescaped # in perf build-test
perf unwind: Suppress massive unsupported target platform errors
perf script: Add new parameter in kfree_skb tracepoint to the python scripts using it
perf script: Print raw ip instead of binary offset for callchain
perf symbols: Fix return incorrect build_id size in elf_read_build_id()
perf list: Modify the warning message about scandirat(3)
perf list: Fix memory leaks in print_tracepoint_events()
perf lock contention: Rework offset calculation with BPF CO-RE
perf lock contention: Fix struct rq lock access
perf stat: Disable TopdownL1 on hybrid
perf stat: Avoid SEGV on counter->name
...
Diffstat (limited to 'tools/perf/util')
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, ¬es->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); |