Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'perf-tools-for-v6.3-1-2023-02-22' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux

Pull perf tools updates from Arnaldo Carvalho de Melo:
"Miscellaneous:

- Add Ian Rogers to MAINTAINERS as a perf tools reviewer.

- Add support for retire latency feature (pipeline stall of a
instruction compared to the previous one, in cycles) present on
some Intel processors.

- Add 'perf c2c' report option to show false sharing with adjacent
cachelines, to be used in machines with cacheline prefetching,
where accesses to a cacheline brings the next one too.

- Skip 'perf test bpf' when the required kernel-debuginfo package
isn't installed.

- Avoid d3-flame-graph package dependency in 'perf script flamegraph',
making this feature more generally available.

- Add JSON metric events to present CPI stall cycles in Power10.

- Assorted improvements/refactorings on the JSON metrics parsing
code.

perf lock contention:

- Add -o/--lock-owner option:

$ sudo ./perf lock contention -abo -- ./perf bench sched pipe
# Running 'sched/pipe' benchmark:
# Executed 1000000 pipe operations between two processes

Total time: 4.766 [sec]

4.766540 usecs/op
209795 ops/sec
contended total wait max wait avg wait pid owner

403 565.32 us 26.81 us 1.40 us -1 Unknown
4 27.99 us 8.57 us 7.00 us 1583145 sched-pipe
1 8.25 us 8.25 us 8.25 us 1583144 sched-pipe
1 2.03 us 2.03 us 2.03 us 5068 chrome

The owner is unknown in most cases. Filtering only for the
mutex locks, it will more likely get the owners.

- -S/--callstack-filter is to limit display entries having the given
string in the callstack:

$ sudo ./perf lock contention -abv -S net sleep 1
...
contended total wait max wait avg wait type caller

5 70.20 us 16.13 us 14.04 us spinlock __dev_queue_xmit+0xb6d
0xffffffffa5dd1c60 _raw_spin_lock+0x30
0xffffffffa5b8f6ed __dev_queue_xmit+0xb6d
0xffffffffa5cd8267 ip6_finish_output2+0x2c7
0xffffffffa5cdac14 ip6_finish_output+0x1d4
0xffffffffa5cdb477 ip6_xmit+0x457
0xffffffffa5d1fd17 inet6_csk_xmit+0xd7
0xffffffffa5c5f4aa __tcp_transmit_skb+0x54a
0xffffffffa5c6467d tcp_keepalive_timer+0x2fd

Please note that to have the -b option (BPF) working above one has
to build with BUILD_BPF_SKEL=1.

- Add more 'perf test' entries to test these new features.

perf script:

- Add 'cgroup' field for 'perf script' output:

$ perf record --all-cgroups -- true
$ perf script -F comm,pid,cgroup
true 337112 /user.slice/user-657345.slice/user@657345.service/...
true 337112 /user.slice/user-657345.slice/user@657345.service/...
true 337112 /user.slice/user-657345.slice/user@657345.service/...
true 337112 /user.slice/user-657345.slice/user@657345.service/...

- Add support for showing branch speculation information in 'perf
script' and in the 'perf report' raw dump (-D).

perf record:

- Fix 'perf record' segfault with --overwrite and --max-size.

perf test/bench:

- Switch basic BPF filtering test to use syscall tracepoint to avoid
the variable number of probes inserted when using the previous
probe point (do_epoll_wait) that happens on different CPU
architectures.

- Fix DWARF unwind test by adding non-inline to expected function in
a backtrace.

- Use 'grep -c' where the longer form 'grep | wc -l' was being used.

- Add getpid and execve benchmarks to 'perf bench syscall'.

Intel PT:

- Add support for synthesizing "cycle" events from Intel PT traces as
we support "instruction" events when Intel PT CYC packets are
available. This enables much more accurate profiles than when using
the regular 'perf record -e cycles' (the default) when the workload
lasts for very short periods (<10ms).

- .plt symbol handling improvements, better handling IBT (in the past
MPX) done in the context of decoding Intel PT processor traces,
IFUNC symbols on x86_64, static executables, understanding .plt.got
symbols on x86_64.

- Add a 'perf test' to test symbol resolution, part of the .plt
improvements series, this tests things like symbol size in contexts
where only the symbol start is available (kallsyms), etc.

- Better handle auxtrace/Intel PT data when using pipe mode (perf
record sleep 1|perf report).

- Fix symbol lookup with kcore with multiple segments match stext,
getting the symbol resolution to just show DSOs as unknown.

ARM:

- Timestamp improvements for ARM64 systems with ETMv4 (Embedded Trace
Macrocell v4).

- Ensure ARM64 CoreSight timestamps don't go backwards.

- Document that ARM64 SPE (Statistical Profiling Extension) is used
with 'perf c2c/mem'.

- Add raw decoding for ARM64 SPEv1.2 previous branch address.

- Update neoverse-n2-v2 ARM vendor events (JSON tables): topdown L1,
TLB, cache, branch, PE utilization and instruction mix metrics.

- Update decoder code for OpenCSD version 1.4, on ARM64 systems.

- Fix command line auto-complete of CPU events on aarch64.

Build:

- Fix 'perf probe' and 'perf test' when libtraceevent isn't linked,
as several tests use tracepoints, those should be skipped.

- More fallout fixes for the removal of tools/lib/traceevent/.

- Fix build error when linking with libpfm"

* tag 'perf-tools-for-v6.3-1-2023-02-22' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux: (114 commits)
perf tests stat_all_metrics: Change true workload to sleep workload for system wide check
perf vendor events power10: Add JSON metric events to present CPI stall cycles in powerpc
perf intel-pt: Synthesize cycle events
perf c2c: Add report option to show false sharing in adjacent cachelines
perf record: Fix segfault with --overwrite and --max-size
perf stat: Avoid merging/aggregating metric counts twice
perf tools: Fix perf tool build error in util/pfm.c
perf tools: Fix auto-complete on aarch64
perf lock contention: Support old rw_semaphore type
perf lock contention: Add -o/--lock-owner option
perf lock contention: Fix to save callstack for the default modified
perf test bpf: Skip test if kernel-debuginfo is not present
perf probe: Update the exit error codes in function try_to_find_probe_trace_event
perf script: Fix missing Retire Latency fields option documentation
perf event x86: Add retire_lat when synthesizing PERF_SAMPLE_WEIGHT_STRUCT
perf test x86: Support the retire_lat (Retire Latency) sample_type check
perf test bpf: Check for libtraceevent support
perf script: Support Retire Latency
perf report: Support Retire Latency
perf lock contention: Support filters for different aggregation
...

+3211 -1093
+1
MAINTAINERS
··· 16323 16323 R: Alexander Shishkin <alexander.shishkin@linux.intel.com> 16324 16324 R: Jiri Olsa <jolsa@kernel.org> 16325 16325 R: Namhyung Kim <namhyung@kernel.org> 16326 + R: Ian Rogers <irogers@google.com> 16326 16327 L: linux-perf-users@vger.kernel.org 16327 16328 L: linux-kernel@vger.kernel.org 16328 16329 S: Supported
+16 -7
tools/arch/x86/include/uapi/asm/unistd_32.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 - #ifndef __NR_perf_event_open 3 - # define __NR_perf_event_open 336 2 + #ifndef __NR_execve 3 + #define __NR_execve 11 4 4 #endif 5 - #ifndef __NR_futex 6 - # define __NR_futex 240 5 + #ifndef __NR_getppid 6 + #define __NR_getppid 64 7 + #endif 8 + #ifndef __NR_getpgid 9 + #define __NR_getpgid 132 7 10 #endif 8 11 #ifndef __NR_gettid 9 - # define __NR_gettid 224 12 + #define __NR_gettid 224 13 + #endif 14 + #ifndef __NR_futex 15 + #define __NR_futex 240 10 16 #endif 11 17 #ifndef __NR_getcpu 12 - # define __NR_getcpu 318 18 + #define __NR_getcpu 318 19 + #endif 20 + #ifndef __NR_perf_event_open 21 + #define __NR_perf_event_open 336 13 22 #endif 14 23 #ifndef __NR_setns 15 - # define __NR_setns 346 24 + #define __NR_setns 346 16 25 #endif
+16 -7
tools/arch/x86/include/uapi/asm/unistd_64.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 - #ifndef __NR_perf_event_open 3 - # define __NR_perf_event_open 298 2 + #ifndef __NR_execve 3 + #define __NR_execve 59 4 4 #endif 5 - #ifndef __NR_futex 6 - # define __NR_futex 202 5 + #ifndef __NR_getppid 6 + #define __NR_getppid 110 7 + #endif 8 + #ifndef __NR_getpgid 9 + #define __NR_getpgid 121 7 10 #endif 8 11 #ifndef __NR_gettid 9 - # define __NR_gettid 186 12 + #define __NR_gettid 186 10 13 #endif 11 - #ifndef __NR_getcpu 12 - # define __NR_getcpu 309 14 + #ifndef __NR_futex 15 + #define __NR_futex 202 16 + #endif 17 + #ifndef __NR_perf_event_open 18 + #define __NR_perf_event_open 298 13 19 #endif 14 20 #ifndef __NR_setns 15 21 #define __NR_setns 308 22 + #endif 23 + #ifndef __NR_getcpu 24 + #define __NR_getcpu 309 16 25 #endif
+1
tools/build/Makefile.build
··· 53 53 54 54 quiet_cmd_flex = FLEX $@ 55 55 quiet_cmd_bison = BISON $@ 56 + quiet_cmd_test = TEST $@ 56 57 57 58 # Create directory unless it exists 58 59 quiet_cmd_mkdir = MKDIR $(dir $@)
+1
tools/perf/.gitignore
··· 38 38 trace/beauty/generated/ 39 39 pmu-events/pmu-events.c 40 40 pmu-events/jevents 41 + pmu-events/metric_test.log 41 42 feature/ 42 43 libapi/ 43 44 libbpf/
+2 -1
tools/perf/Documentation/itrace.txt
··· 1 1 i synthesize instructions events 2 + y synthesize cycles events 2 3 b synthesize branches events (branch misses for Arm SPE) 3 4 c synthesize branches events (calls only) 4 5 r synthesize branches events (returns only) ··· 26 25 A approximate IPC 27 26 Z prefer to ignore timestamps (so-called "timeless" decoding) 28 27 29 - The default is all events i.e. the same as --itrace=ibxwpe, 28 + The default is all events i.e. the same as --itrace=iybxwpe, 30 29 except for perf script where it is --itrace=ce 31 30 32 31 In addition, the period (default 100000, except for perf script where it is 1)
+1 -1
tools/perf/Documentation/perf-bench.txt
··· 18 18 -------------- 19 19 -r:: 20 20 --repeat=:: 21 - Specify amount of times to repeat the run (default 10). 21 + Specify number of times to repeat the run (default 10). 22 22 23 23 -f:: 24 24 --format=::
+13 -3
tools/perf/Documentation/perf-c2c.txt
··· 22 22 On Intel, the tool is based on load latency and precise store facility events 23 23 provided by Intel CPUs. On PowerPC, the tool uses random instruction sampling 24 24 with thresholding feature. On AMD, the tool uses IBS op pmu (due to hardware 25 - limitations, perf c2c is not supported on Zen3 cpus). 25 + limitations, perf c2c is not supported on Zen3 cpus). On Arm64 it uses SPE to 26 + sample load and store operations, therefore hardware and kernel support is 27 + required. See linkperf:perf-arm-spe[1] for a setup guide. Due to the 28 + statistical nature of Arm SPE sampling, not every memory operation will be 29 + sampled. 26 30 27 31 These events provide: 28 32 - memory address of the access ··· 125 121 perf c2c record --call-graph lbr. 126 122 Disabled by default. In common cases with call stack overflows, 127 123 it can recreate better call stacks than the default lbr call stack 128 - output. But this approach is not full proof. There can be cases 124 + output. But this approach is not foolproof. There can be cases 129 125 where it creates incorrect call stacks from incorrect matches. 130 126 The known limitations include exception handing such as 131 127 setjmp/longjmp will have calls/returns not match. 128 + 129 + --double-cl:: 130 + Group the detection of shared cacheline events into double cacheline 131 + granularity. Some architectures have an Adjacent Cacheline Prefetch 132 + feature, which causes cacheline sharing to behave like the cacheline 133 + size is doubled. 132 134 133 135 C2C RECORD 134 136 ---------- ··· 343 333 344 334 SEE ALSO 345 335 -------- 346 - linkperf:perf-record[1], linkperf:perf-mem[1] 336 + linkperf:perf-record[1], linkperf:perf-mem[1], linkperf:perf-arm-spe[1]
+54 -12
tools/perf/Documentation/perf-intel-pt.txt
··· 101 101 options, which will list all the samples. 102 102 103 103 perf record -e intel_pt//u ls 104 - perf script --itrace=ibxwpe 104 + perf script --itrace=iybxwpe 105 105 106 106 An interesting field that is not printed by default is 'flags' which can be 107 107 displayed as follows: 108 108 109 - perf script --itrace=ibxwpe -F+flags 109 + perf script --itrace=iybxwpe -F+flags 110 110 111 111 The flags are "bcrosyiABExghDt" which stand for branch, call, return, conditional, 112 112 system, asynchronous, interrupt, transaction abort, trace begin, trace end, ··· 147 147 There are two ways that instructions-per-cycle (IPC) can be calculated depending 148 148 on the recording. 149 149 150 - If the 'cyc' config term (see config terms section below) was used, then IPC is 151 - calculated using the cycle count from CYC packets, otherwise MTC packets are 152 - used - refer to the 'mtc' config term. When MTC is used, however, the values 153 - are less accurate because the timing is less accurate. 150 + If the 'cyc' config term (see config terms section below) was used, then IPC 151 + and cycle events are calculated using the cycle count from CYC packets, otherwise 152 + MTC packets are used - refer to the 'mtc' config term. When MTC is used, however, 153 + the values are less accurate because the timing is less accurate. 154 154 155 155 Because Intel PT does not update the cycle count on every branch or instruction, 156 156 the values will often be zero. When there are values, they will be the number 157 157 of instructions and number of cycles since the last update, and thus represent 158 - the average IPC since the last IPC for that event type. Note IPC for "branches" 159 - events is calculated separately from IPC for "instructions" events. 158 + the average IPC cycle count since the last IPC for that event type. 159 + Note IPC for "branches" events is calculated separately from IPC for "instructions" 160 + events. 160 161 161 162 Even with the 'cyc' config term, it is possible to produce IPC information for 162 163 every change of timestamp, but at the expense of accuracy. That is selected by ··· 901 900 902 901 which, in turn, is the same as 903 902 904 - --itrace=cepwx 903 + --itrace=cepwxy 905 904 906 905 The letters are: 907 906 908 907 i synthesize "instructions" events 908 + y synthesize "cycles" events 909 909 b synthesize "branches" events 910 910 x synthesize "transactions" events 911 911 w synthesize "ptwrite" events ··· 929 927 "Instructions" events look like they were recorded by "perf record -e 930 928 instructions". 931 929 930 + "Cycles" events look like they were recorded by "perf record -e cycles" 931 + (ie., the default). Note that even with CYC packets enabled and no sampling, 932 + these are not fully accurate, since CYC packets are not emitted for each 933 + instruction, only when some other event (like an indirect branch, or a 934 + TNT packet representing multiple branches) happens causes a packet to 935 + be emitted. Thus, it is more effective for attributing cycles to functions 936 + (and possibly basic blocks) than to individual instructions, although it 937 + is not even perfect for functions (although it becomes better if the noretcomp 938 + option is active). 939 + 932 940 "Branches" events look like they were recorded by "perf record -e branches". "c" 933 941 and "r" can be combined to get calls and returns. 934 942 ··· 946 934 'flags' field can be used in perf script to determine whether the event is a 947 935 transaction start, commit or abort. 948 936 949 - Note that "instructions", "branches" and "transactions" events depend on code 950 - flow packets which can be disabled by using the config term "branch=0". Refer 951 - to the config terms section above. 937 + Note that "instructions", "cycles", "branches" and "transactions" events 938 + depend on code flow packets which can be disabled by using the config term 939 + "branch=0". Refer to the config terms section above. 952 940 953 941 "ptwrite" events record the payload of the ptwrite instruction and whether 954 942 "fup_on_ptw" was used. "ptwrite" events depend on PTWRITE packets which are ··· 1831 1819 $ perf script --itrace=ew 1832 1820 eg_ptw 19875 [007] 8061.235912: ptwrite: IP: 0 payload: 0x1234567890abcdef 55701249a196 perf_emulate_ptwrite+0x16 (/home/user/eg_ptw) 1833 1821 $ 1822 + 1823 + 1824 + Pipe mode 1825 + --------- 1826 + Pipe mode is a problem for Intel PT and possibly other auxtrace users. 1827 + It's not recommended to use a pipe as data output with Intel PT because 1828 + of the following reason. 1829 + 1830 + Essentially the auxtrace buffers do not behave like the regular perf 1831 + event buffers. That is because the head and tail are updated by 1832 + software, but in the auxtrace case the data is written by hardware. 1833 + So the head and tail do not get updated as data is written. 1834 + 1835 + In the Intel PT case, the head and tail are updated only when the trace 1836 + is disabled by software, for example: 1837 + - full-trace, system wide : when buffer passes watermark 1838 + - full-trace, not system-wide : when buffer passes watermark or 1839 + context switches 1840 + - snapshot mode : as above but also when a snapshot is made 1841 + - sample mode : as above but also when a sample is made 1842 + 1843 + That means finished-round ordering doesn't work. An auxtrace buffer 1844 + can turn up that has data that extends back in time, possibly to the 1845 + very beginning of tracing. 1846 + 1847 + For a perf.data file, that problem is solved by going through the trace 1848 + and queuing up the auxtrace buffers in advance. 1849 + 1850 + For pipe mode, the order of events and timestamps can presumably 1851 + be messed up. 1834 1852 1835 1853 1836 1854 EXAMPLE
+1 -1
tools/perf/Documentation/perf-list.txt
··· 232 232 sysctl to -1, which allows non root to use these events. 233 233 234 234 For accessing trace point events perf needs to have read access to 235 - /sys/kernel/debug/tracing, even when perf_event_paranoid is in a relaxed 235 + /sys/kernel/tracing, even when perf_event_paranoid is in a relaxed 236 236 setting. 237 237 238 238 TRACING
+11
tools/perf/Documentation/perf-lock.txt
··· 172 172 --lock-addr:: 173 173 Show lock contention stat by address 174 174 175 + -o:: 176 + --lock-owner:: 177 + Show lock contention stat by owners. Implies --threads and 178 + requires --use-bpf. 179 + 175 180 -Y:: 176 181 --type-filter=<value>:: 177 182 Show lock contention only for given lock types (comma separated list). ··· 191 186 -L:: 192 187 --lock-filter=<value>:: 193 188 Show lock contention only for given lock addresses or names (comma separated list). 189 + 190 + -S:: 191 + --callstack-filter=<value>:: 192 + Show lock contention only if the callstack contains the given string. 193 + Note that it matches the substring so 'rq' would match both 'raw_spin_rq_lock' 194 + and 'irq_enter_rcu'. 194 195 195 196 196 197 SEE ALSO
+6 -1
tools/perf/Documentation/perf-mem.txt
··· 23 23 not the pure load (or store latency). Use latency includes any pipeline 24 24 queueing delays in addition to the memory subsystem latency. 25 25 26 + On Arm64 this uses SPE to sample load and store operations, therefore hardware 27 + and kernel support is required. See linkperf:perf-arm-spe[1] for a setup guide. 28 + Due to the statistical nature of SPE sampling, not every memory operation will 29 + be sampled. 30 + 26 31 OPTIONS 27 32 ------- 28 33 <command>...:: ··· 98 93 99 94 SEE ALSO 100 95 -------- 101 - linkperf:perf-record[1], linkperf:perf-report[1] 96 + linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-arm-spe[1]
+1 -1
tools/perf/Documentation/perf-probe.txt
··· 222 222 and 'ALN2' is end line number in the file. It is also possible to specify how 223 223 many lines to show by using 'NUM'. Moreover, 'FUNC@SRC' combination is good 224 224 for searching a specific function when several functions share same name. 225 - So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function. 225 + So, "source.c:100-120" shows lines between 100th to 120th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function. 226 226 227 227 LAZY MATCHING 228 228 -------------
+3 -1
tools/perf/Documentation/perf-report.txt
··· 115 115 - p_stage_cyc: On powerpc, this presents the number of cycles spent in a 116 116 pipeline stage. And currently supported only on powerpc. 117 117 - addr: (Full) virtual address of the sampled instruction 118 + - retire_lat: On X86, this reports pipeline stall of this instruction compared 119 + to the previous instruction in cycles. And currently supported only on X86 118 120 119 121 By default, comm, dso and symbol keys are used. 120 122 (i.e. --sort comm,dso,symbol) ··· 509 507 perf record --call-graph lbr. 510 508 Disabled by default. In common cases with call stack overflows, 511 509 it can recreate better call stacks than the default lbr call stack 512 - output. But this approach is not full proof. There can be cases 510 + output. But this approach is not foolproof. There can be cases 513 511 where it creates incorrect call stacks from incorrect matches. 514 512 The known limitations include exception handing such as 515 513 setjmp/longjmp will have calls/returns not match.
+1 -1
tools/perf/Documentation/perf-script-perl.txt
··· 55 55 the above option: -a to enable system-wide collection. 56 56 57 57 The format file for the sched_wakeup event defines the following fields 58 - (see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format): 58 + (see /sys/kernel/tracing/events/sched/sched_wakeup/format): 59 59 60 60 ---- 61 61 format:
+2 -2
tools/perf/Documentation/perf-script-python.txt
··· 319 319 process can be generalized to any tracepoint or set of tracepoints 320 320 you're interested in - basically find the tracepoint(s) you're 321 321 interested in by looking at the list of available events shown by 322 - 'perf list' and/or look in /sys/kernel/debug/tracing/events/ for 322 + 'perf list' and/or look in /sys/kernel/tracing/events/ for 323 323 detailed event and field info, record the corresponding trace data 324 324 using 'perf record', passing it the list of interesting events, 325 325 generate a skeleton script using 'perf script -g python' and modify the ··· 449 449 the above option: -a to enable system-wide collection. 450 450 451 451 The format file for the sched_wakeup event defines the following fields 452 - (see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format): 452 + (see /sys/kernel/tracing/events/sched/sched_wakeup/format): 453 453 454 454 ---- 455 455 format:
+5 -2
tools/perf/Documentation/perf-script.txt
··· 134 134 srcline, period, iregs, uregs, brstack, brstacksym, flags, bpf-output, 135 135 brstackinsn, brstackinsnlen, brstackoff, callindent, insn, insnlen, synth, 136 136 phys_addr, metric, misc, srccode, ipc, data_page_size, code_page_size, ins_lat, 137 - machine_pid, vcpu. 137 + machine_pid, vcpu, cgroup, retire_lat. 138 138 Field list can be prepended with the type, trace, sw or hw, 139 139 to indicate to which event type the field list applies. 140 140 e.g., -F sw:comm,tid,time,ip,sym and -F trace:time,cpu,trace ··· 230 230 The machine_pid and vcpu fields are derived from data resulting from using 231 231 perf inject to insert a perf.data file recorded inside a virtual machine into 232 232 a perf.data file recorded on the host at the same time. 233 + 234 + The cgroup fields requires sample having the cgroup id which is saved 235 + when "--all-cgroups" option is passed to 'perf record'. 233 236 234 237 Finally, a user may not set fields to none for all event types. 235 238 i.e., -F "" is not allowed. ··· 505 502 perf record --call-graph lbr. 506 503 Disabled by default. In common cases with call stack overflows, 507 504 it can recreate better call stacks than the default lbr call stack 508 - output. But this approach is not full proof. There can be cases 505 + output. But this approach is not foolproof. There can be cases 509 506 where it creates incorrect call stacks from incorrect matches. 510 507 The known limitations include exception handing such as 511 508 setjmp/longjmp will have calls/returns not match.
+3
tools/perf/Documentation/perf-test.txt
··· 34 34 -F:: 35 35 --dont-fork:: 36 36 Do not fork child for each test, run all tests within single process. 37 + 38 + --dso:: 39 + Specify a DSO for the "Symbols" test.
+1 -1
tools/perf/Documentation/perf-top.txt
··· 334 334 callgraph. The option must be used with --call-graph lbr recording. 335 335 Disabled by default. In common cases with call stack overflows, 336 336 it can recreate better call stacks than the default lbr call stack 337 - output. But this approach is not full proof. There can be cases 337 + output. But this approach is not foolproof. There can be cases 338 338 where it creates incorrect call stacks from incorrect matches. 339 339 The known limitations include exception handing such as 340 340 setjmp/longjmp will have calls/returns not match.
-4
tools/perf/Makefile.config
··· 1208 1208 LIBTRACEEVENT_VERSION_CPP := $(shell expr $(LIBTRACEEVENT_VERSION_1) \* 255 \* 255 + $(LIBTRACEEVENT_VERSION_2) \* 255 + $(LIBTRACEEVENT_VERSION_3)) 1209 1209 CFLAGS += -DLIBTRACEEVENT_VERSION=$(LIBTRACEEVENT_VERSION_CPP) 1210 1210 $(call detected,CONFIG_LIBTRACEEVENT) 1211 - LIBTRACEEVENT_VERSION_WITH_TEP_FIELD_IS_RELATIVE := $(shell expr 1 \* 255 \* 255 + 5 \* 255 + 0) # 1.5.0 1212 - ifeq ($(shell test $(LIBTRACEEVENT_VERSION_CPP) -gt $(LIBTRACEEVENT_VERSION_WITH_TEP_FIELD_IS_RELATIVE); echo $$?),0) 1213 - CFLAGS += -DHAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE 1214 - endif 1215 1211 else 1216 1212 dummy := $(warning Warning: libtraceevent is missing limiting functionality, please install libtraceevent-dev/libtraceevent-devel) 1217 1213 endif
+1
tools/perf/Makefile.perf
··· 1103 1103 $(OUTPUT)util/intel-pt-decoder/inat-tables.c \ 1104 1104 $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \ 1105 1105 $(OUTPUT)pmu-events/pmu-events.c \ 1106 + $(OUTPUT)pmu-events/metric_test.log \ 1106 1107 $(OUTPUT)$(fadvise_advice_array) \ 1107 1108 $(OUTPUT)$(fsconfig_arrays) \ 1108 1109 $(OUTPUT)$(fsmount_arrays) \
+2 -3
tools/perf/arch/arm/util/auxtrace.c
··· 55 55 56 56 static struct perf_pmu **find_all_hisi_ptt_pmus(int *nr_ptts, int *err) 57 57 { 58 - const char *sysfs = sysfs__mountpoint(); 59 58 struct perf_pmu **hisi_ptt_pmus = NULL; 60 59 struct dirent *dent; 61 60 char path[PATH_MAX]; 62 61 DIR *dir = NULL; 63 62 int idx = 0; 64 63 65 - snprintf(path, PATH_MAX, "%s" EVENT_SOURCE_DEVICE_PATH, sysfs); 64 + perf_pmu__event_source_devices_scnprintf(path, sizeof(path)); 66 65 dir = opendir(path); 67 66 if (!dir) { 68 - pr_err("can't read directory '%s'\n", EVENT_SOURCE_DEVICE_PATH); 67 + pr_err("can't read directory '%s'\n", path); 69 68 *err = -EINVAL; 70 69 return NULL; 71 70 }
+93 -10
tools/perf/arch/arm/util/cs-etm.c
··· 53 53 [CS_ETMV4_TRCIDR2] = "trcidr/trcidr2", 54 54 [CS_ETMV4_TRCIDR8] = "trcidr/trcidr8", 55 55 [CS_ETMV4_TRCAUTHSTATUS] = "mgmt/trcauthstatus", 56 - [CS_ETE_TRCDEVARCH] = "mgmt/trcdevarch" 56 + [CS_ETMV4_TS_SOURCE] = "ts_source", 57 + }; 58 + 59 + static const char * const metadata_ete_ro[] = { 60 + [CS_ETE_TRCIDR0] = "trcidr/trcidr0", 61 + [CS_ETE_TRCIDR1] = "trcidr/trcidr1", 62 + [CS_ETE_TRCIDR2] = "trcidr/trcidr2", 63 + [CS_ETE_TRCIDR8] = "trcidr/trcidr8", 64 + [CS_ETE_TRCAUTHSTATUS] = "mgmt/trcauthstatus", 65 + [CS_ETE_TRCDEVARCH] = "mgmt/trcdevarch", 66 + [CS_ETE_TS_SOURCE] = "ts_source", 57 67 }; 58 68 59 69 static bool cs_etm_is_etmv4(struct auxtrace_record *itr, int cpu); ··· 283 273 284 274 ret = perf_pmu__scan_file(pmu, path, "%x", &hash); 285 275 if (ret != 1) { 286 - pr_err("failed to set sink \"%s\" on event %s with %d (%s)\n", 287 - sink, evsel__name(evsel), errno, 288 - str_error_r(errno, msg, sizeof(msg))); 276 + if (errno == ENOENT) 277 + pr_err("Couldn't find sink \"%s\" on event %s\n" 278 + "Missing kernel or device support?\n\n" 279 + "Hint: An appropriate sink will be picked automatically if one isn't specified.\n", 280 + sink, evsel__name(evsel)); 281 + else 282 + pr_err("Failed to set sink \"%s\" on event %s with %d (%s)\n", 283 + sink, evsel__name(evsel), errno, 284 + str_error_r(errno, msg, sizeof(msg))); 289 285 return ret; 290 286 } 291 287 ··· 621 605 return val; 622 606 } 623 607 608 + static int cs_etm_get_ro_signed(struct perf_pmu *pmu, int cpu, const char *path) 609 + { 610 + char pmu_path[PATH_MAX]; 611 + int scan; 612 + int val = 0; 613 + 614 + /* Get RO metadata from sysfs */ 615 + snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu, path); 616 + 617 + scan = perf_pmu__scan_file(pmu, pmu_path, "%d", &val); 618 + if (scan != 1) 619 + pr_err("%s: error reading: %s\n", __func__, pmu_path); 620 + 621 + return val; 622 + } 623 + 624 + static bool cs_etm_pmu_path_exists(struct perf_pmu *pmu, int cpu, const char *path) 625 + { 626 + char pmu_path[PATH_MAX]; 627 + 628 + /* Get RO metadata from sysfs */ 629 + snprintf(pmu_path, PATH_MAX, "cpu%d/%s", cpu, path); 630 + 631 + return perf_pmu__file_exists(pmu, pmu_path); 632 + } 633 + 624 634 #define TRCDEVARCH_ARCHPART_SHIFT 0 625 635 #define TRCDEVARCH_ARCHPART_MASK GENMASK(11, 0) 626 636 #define TRCDEVARCH_ARCHPART(x) (((x) & TRCDEVARCH_ARCHPART_MASK) >> TRCDEVARCH_ARCHPART_SHIFT) ··· 659 617 { 660 618 struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr); 661 619 struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 662 - int trcdevarch = cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETE_TRCDEVARCH]); 620 + int trcdevarch = cs_etm_get_ro(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCDEVARCH]); 663 621 664 622 /* 665 623 * ETE if ARCHVER is 5 (ARCHVER is 4 for ETM) and ARCHPART is 0xA13. ··· 688 646 metadata_etmv4_ro[CS_ETMV4_TRCIDR8]); 689 647 data[CS_ETMV4_TRCAUTHSTATUS] = cs_etm_get_ro(cs_etm_pmu, cpu, 690 648 metadata_etmv4_ro[CS_ETMV4_TRCAUTHSTATUS]); 649 + 650 + /* Kernels older than 5.19 may not expose ts_source */ 651 + if (cs_etm_pmu_path_exists(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TS_SOURCE])) 652 + data[CS_ETMV4_TS_SOURCE] = (__u64) cs_etm_get_ro_signed(cs_etm_pmu, cpu, 653 + metadata_etmv4_ro[CS_ETMV4_TS_SOURCE]); 654 + else { 655 + pr_warning("[%03d] pmu file 'ts_source' not found. Fallback to safe value (-1)\n", 656 + cpu); 657 + data[CS_ETMV4_TS_SOURCE] = (__u64) -1; 658 + } 659 + } 660 + 661 + static void cs_etm_save_ete_header(__u64 data[], struct auxtrace_record *itr, int cpu) 662 + { 663 + struct cs_etm_recording *ptr = container_of(itr, struct cs_etm_recording, itr); 664 + struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu; 665 + 666 + /* Get trace configuration register */ 667 + data[CS_ETE_TRCCONFIGR] = cs_etmv4_get_config(itr); 668 + /* Get traceID from the framework */ 669 + data[CS_ETE_TRCTRACEIDR] = coresight_get_trace_id(cpu); 670 + /* Get read-only information from sysFS */ 671 + data[CS_ETE_TRCIDR0] = cs_etm_get_ro(cs_etm_pmu, cpu, 672 + metadata_ete_ro[CS_ETE_TRCIDR0]); 673 + data[CS_ETE_TRCIDR1] = cs_etm_get_ro(cs_etm_pmu, cpu, 674 + metadata_ete_ro[CS_ETE_TRCIDR1]); 675 + data[CS_ETE_TRCIDR2] = cs_etm_get_ro(cs_etm_pmu, cpu, 676 + metadata_ete_ro[CS_ETE_TRCIDR2]); 677 + data[CS_ETE_TRCIDR8] = cs_etm_get_ro(cs_etm_pmu, cpu, 678 + metadata_ete_ro[CS_ETE_TRCIDR8]); 679 + data[CS_ETE_TRCAUTHSTATUS] = cs_etm_get_ro(cs_etm_pmu, cpu, 680 + metadata_ete_ro[CS_ETE_TRCAUTHSTATUS]); 681 + /* ETE uses the same registers as ETMv4 plus TRCDEVARCH */ 682 + data[CS_ETE_TRCDEVARCH] = cs_etm_get_ro(cs_etm_pmu, cpu, 683 + metadata_ete_ro[CS_ETE_TRCDEVARCH]); 684 + 685 + /* Kernels older than 5.19 may not expose ts_source */ 686 + if (cs_etm_pmu_path_exists(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TS_SOURCE])) 687 + data[CS_ETE_TS_SOURCE] = (__u64) cs_etm_get_ro_signed(cs_etm_pmu, cpu, 688 + metadata_ete_ro[CS_ETE_TS_SOURCE]); 689 + else { 690 + pr_warning("[%03d] pmu file 'ts_source' not found. Fallback to safe value (-1)\n", 691 + cpu); 692 + data[CS_ETE_TS_SOURCE] = (__u64) -1; 693 + } 691 694 } 692 695 693 696 static void cs_etm_get_metadata(int cpu, u32 *offset, ··· 748 661 /* first see what kind of tracer this cpu is affined to */ 749 662 if (cs_etm_is_ete(itr, cpu)) { 750 663 magic = __perf_cs_ete_magic; 751 - /* ETE uses the same registers as ETMv4 plus TRCDEVARCH */ 752 - cs_etm_save_etmv4_header(&info->priv[*offset], itr, cpu); 753 - info->priv[*offset + CS_ETE_TRCDEVARCH] = 754 - cs_etm_get_ro(cs_etm_pmu, cpu, 755 - metadata_etmv4_ro[CS_ETE_TRCDEVARCH]); 664 + cs_etm_save_ete_header(&info->priv[*offset], itr, cpu); 756 665 757 666 /* How much space was used */ 758 667 increment = CS_ETE_PRIV_MAX;
+42 -2
tools/perf/arch/arm64/util/pmu.c
··· 3 3 #include <internal/cpumap.h> 4 4 #include "../../../util/cpumap.h" 5 5 #include "../../../util/pmu.h" 6 + #include <api/fs/fs.h> 7 + #include <math.h> 6 8 7 - const struct pmu_events_table *pmu_events_table__find(void) 9 + static struct perf_pmu *pmu__find_core_pmu(void) 8 10 { 9 11 struct perf_pmu *pmu = NULL; 10 12 ··· 21 19 if (pmu->cpus->nr != cpu__max_cpu().cpu) 22 20 return NULL; 23 21 24 - return perf_pmu__find_table(pmu); 22 + return pmu; 25 23 } 24 + return NULL; 25 + } 26 + 27 + const struct pmu_metrics_table *pmu_metrics_table__find(void) 28 + { 29 + struct perf_pmu *pmu = pmu__find_core_pmu(); 30 + 31 + if (pmu) 32 + return perf_pmu__find_metrics_table(pmu); 26 33 27 34 return NULL; 35 + } 36 + 37 + const struct pmu_events_table *pmu_events_table__find(void) 38 + { 39 + struct perf_pmu *pmu = pmu__find_core_pmu(); 40 + 41 + if (pmu) 42 + return perf_pmu__find_events_table(pmu); 43 + 44 + return NULL; 45 + } 46 + 47 + double perf_pmu__cpu_slots_per_cycle(void) 48 + { 49 + char path[PATH_MAX]; 50 + unsigned long long slots = 0; 51 + struct perf_pmu *pmu = pmu__find_core_pmu(); 52 + 53 + if (pmu) { 54 + perf_pmu__pathname_scnprintf(path, sizeof(path), 55 + pmu->name, "caps/slots"); 56 + /* 57 + * The value of slots is not greater than 32 bits, but sysfs__read_int 58 + * can't read value with 0x prefix, so use sysfs__read_ull instead. 59 + */ 60 + sysfs__read_ull(path, &slots); 61 + } 62 + 63 + return slots ? (double)slots : NAN; 28 64 }
+2 -2
tools/perf/arch/powerpc/util/header.c
··· 40 40 return bufp; 41 41 } 42 42 43 - int arch_get_runtimeparam(const struct pmu_event *pe) 43 + int arch_get_runtimeparam(const struct pmu_metric *pm) 44 44 { 45 45 int count; 46 46 char path[PATH_MAX] = "/devices/hv_24x7/interface/"; 47 47 48 - atoi(pe->aggr_mode) == PerChip ? strcat(path, "sockets") : strcat(path, "coresperchip"); 48 + atoi(pm->aggr_mode) == PerChip ? strcat(path, "sockets") : strcat(path, "coresperchip"); 49 49 return sysfs__read_int(path, &count) < 0 ? 1 : count; 50 50 }
+4 -1
tools/perf/arch/x86/tests/sample-parsing.c
··· 27 27 const struct perf_sample *s2, 28 28 u64 type) 29 29 { 30 - if (type & PERF_SAMPLE_WEIGHT_STRUCT) 30 + if (type & PERF_SAMPLE_WEIGHT_STRUCT) { 31 31 COMP(ins_lat); 32 + COMP(retire_lat); 33 + } 32 34 33 35 return true; 34 36 } ··· 50 48 struct perf_sample sample = { 51 49 .weight = 101, 52 50 .ins_lat = 102, 51 + .retire_lat = 103, 53 52 }; 54 53 struct perf_sample sample_out; 55 54 size_t i, sz, bufsz;
+21
tools/perf/arch/x86/util/event.c
··· 89 89 else { 90 90 data->weight = weight.var1_dw; 91 91 data->ins_lat = weight.var2_w; 92 + data->retire_lat = weight.var3_w; 92 93 } 93 94 } 94 95 ··· 101 100 if (type & PERF_SAMPLE_WEIGHT_STRUCT) { 102 101 *array &= 0xffffffff; 103 102 *array |= ((u64)data->ins_lat << 32); 103 + *array |= ((u64)data->retire_lat << 48); 104 104 } 105 + } 106 + 107 + const char *arch_perf_header_entry(const char *se_header) 108 + { 109 + if (!strcmp(se_header, "Local Pipeline Stage Cycle")) 110 + return "Local Retire Latency"; 111 + else if (!strcmp(se_header, "Pipeline Stage Cycle")) 112 + return "Retire Latency"; 113 + 114 + return se_header; 115 + } 116 + 117 + int arch_support_sort_key(const char *sort_key) 118 + { 119 + if (!strcmp(sort_key, "p_stage_cyc")) 120 + return 1; 121 + if (!strcmp(sort_key, "local_p_stage_cyc")) 122 + return 1; 123 + return 0; 105 124 }
+2 -10
tools/perf/arch/x86/util/pmu.c
··· 15 15 #include "../../../util/pmu.h" 16 16 #include "../../../util/fncache.h" 17 17 18 - #define TEMPLATE_ALIAS "%s/bus/event_source/devices/%s/alias" 19 - 20 18 struct pmu_alias { 21 19 char *name; 22 20 char *alias; ··· 70 72 char path[PATH_MAX]; 71 73 DIR *dir; 72 74 struct dirent *dent; 73 - const char *sysfs = sysfs__mountpoint(); 74 75 struct pmu_alias *pmu_alias; 75 76 char buf[MAX_PMU_NAME_LEN]; 76 77 FILE *file; 77 78 int ret = -ENOMEM; 78 79 79 - if (!sysfs) 80 + if (!perf_pmu__event_source_devices_scnprintf(path, sizeof(path))) 80 81 return -1; 81 - 82 - snprintf(path, PATH_MAX, 83 - "%s" EVENT_SOURCE_DEVICE_PATH, sysfs); 84 82 85 83 dir = opendir(path); 86 84 if (!dir) ··· 87 93 !strcmp(dent->d_name, "..")) 88 94 continue; 89 95 90 - snprintf(path, PATH_MAX, 91 - TEMPLATE_ALIAS, sysfs, dent->d_name); 92 - 96 + perf_pmu__pathname_scnprintf(path, sizeof(path), dent->d_name, "alias"); 93 97 if (!file_available(path)) 94 98 continue; 95 99
+2
tools/perf/bench/bench.h
··· 22 22 int bench_sched_messaging(int argc, const char **argv); 23 23 int bench_sched_pipe(int argc, const char **argv); 24 24 int bench_syscall_basic(int argc, const char **argv); 25 + int bench_syscall_getpgid(int argc, const char **argv); 26 + int bench_syscall_execve(int argc, const char **argv); 25 27 int bench_mem_memcpy(int argc, const char **argv); 26 28 int bench_mem_memset(int argc, const char **argv); 27 29 int bench_mem_find_bit(int argc, const char **argv);
+72 -4
tools/perf/bench/syscall.c
··· 14 14 #include <sys/time.h> 15 15 #include <sys/syscall.h> 16 16 #include <sys/types.h> 17 + #include <sys/wait.h> 17 18 #include <unistd.h> 18 19 #include <stdlib.h> 19 20 ··· 31 30 NULL 32 31 }; 33 32 34 - int bench_syscall_basic(int argc, const char **argv) 33 + static void test_execve(void) 34 + { 35 + const char *pathname = "/bin/true"; 36 + char *const argv[] = { (char *)pathname, NULL }; 37 + pid_t pid = fork(); 38 + 39 + if (pid < 0) { 40 + fprintf(stderr, "fork failed\n"); 41 + exit(1); 42 + } else if (pid == 0) { 43 + execve(pathname, argv, NULL); 44 + fprintf(stderr, "execve /bin/true failed\n"); 45 + exit(1); 46 + } else { 47 + if (waitpid(pid, NULL, 0) < 0) { 48 + fprintf(stderr, "waitpid failed\n"); 49 + exit(1); 50 + } 51 + } 52 + } 53 + 54 + static int bench_syscall_common(int argc, const char **argv, int syscall) 35 55 { 36 56 struct timeval start, stop, diff; 37 57 unsigned long long result_usec = 0; 58 + const char *name = NULL; 38 59 int i; 39 60 40 61 argc = parse_options(argc, argv, options, bench_syscall_usage, 0); 41 62 42 63 gettimeofday(&start, NULL); 43 64 44 - for (i = 0; i < loops; i++) 45 - getppid(); 65 + for (i = 0; i < loops; i++) { 66 + switch (syscall) { 67 + case __NR_getppid: 68 + getppid(); 69 + break; 70 + case __NR_getpgid: 71 + getpgid(0); 72 + break; 73 + case __NR_execve: 74 + test_execve(); 75 + /* Only loop 10000 times to save time */ 76 + if (i == 10000) 77 + loops = 10000; 78 + break; 79 + default: 80 + break; 81 + } 82 + } 46 83 47 84 gettimeofday(&stop, NULL); 48 85 timersub(&stop, &start, &diff); 49 86 87 + switch (syscall) { 88 + case __NR_getppid: 89 + name = "getppid()"; 90 + break; 91 + case __NR_getpgid: 92 + name = "getpgid()"; 93 + break; 94 + case __NR_execve: 95 + name = "execve()"; 96 + break; 97 + default: 98 + break; 99 + } 100 + 50 101 switch (bench_format) { 51 102 case BENCH_FORMAT_DEFAULT: 52 - printf("# Executed %'d getppid() calls\n", loops); 103 + printf("# Executed %'d %s calls\n", loops, name); 53 104 54 105 result_usec = diff.tv_sec * 1000000; 55 106 result_usec += diff.tv_usec; ··· 131 78 } 132 79 133 80 return 0; 81 + } 82 + 83 + int bench_syscall_basic(int argc, const char **argv) 84 + { 85 + return bench_syscall_common(argc, argv, __NR_getppid); 86 + } 87 + 88 + int bench_syscall_getpgid(int argc, const char **argv) 89 + { 90 + return bench_syscall_common(argc, argv, __NR_getpgid); 91 + } 92 + 93 + int bench_syscall_execve(int argc, const char **argv) 94 + { 95 + return bench_syscall_common(argc, argv, __NR_execve); 134 96 }
+3 -1
tools/perf/builtin-bench.c
··· 52 52 53 53 static struct bench syscall_benchmarks[] = { 54 54 { "basic", "Benchmark for basic getppid(2) calls", bench_syscall_basic }, 55 + { "getpgid", "Benchmark for getpgid(2) calls", bench_syscall_getpgid }, 56 + { "execve", "Benchmark for execve(2) calls", bench_syscall_execve }, 55 57 { "all", "Run all syscall benchmarks", NULL }, 56 58 { NULL, NULL, NULL }, 57 59 }; ··· 152 150 153 151 static const struct option bench_options[] = { 154 152 OPT_STRING('f', "format", &bench_format_str, "default|simple", "Specify the output formatting style"), 155 - OPT_UINTEGER('r', "repeat", &bench_repeat, "Specify amount of times to repeat the run"), 153 + OPT_UINTEGER('r', "repeat", &bench_repeat, "Specify number of times to repeat the run"), 156 154 OPT_END() 157 155 }; 158 156
+12 -9
tools/perf/builtin-c2c.c
··· 524 524 char buf[20]; 525 525 526 526 if (he->mem_info) 527 - addr = cl_address(he->mem_info->daddr.addr); 527 + addr = cl_address(he->mem_info->daddr.addr, chk_double_cl); 528 528 529 529 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr)); 530 530 } ··· 562 562 char buf[20]; 563 563 564 564 if (he->mem_info) 565 - addr = cl_offset(he->mem_info->daddr.al_addr); 565 + addr = cl_offset(he->mem_info->daddr.al_addr, chk_double_cl); 566 566 567 567 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr)); 568 568 } ··· 574 574 uint64_t l = 0, r = 0; 575 575 576 576 if (left->mem_info) 577 - l = cl_offset(left->mem_info->daddr.addr); 577 + l = cl_offset(left->mem_info->daddr.addr, chk_double_cl); 578 + 578 579 if (right->mem_info) 579 - r = cl_offset(right->mem_info->daddr.addr); 580 + r = cl_offset(right->mem_info->daddr.addr, chk_double_cl); 580 581 581 582 return (int64_t)(r - l); 582 583 } ··· 2591 2590 he = cl_browser->he; 2592 2591 2593 2592 if (he->mem_info) 2594 - addr = cl_address(he->mem_info->daddr.addr); 2593 + addr = cl_address(he->mem_info->daddr.addr, chk_double_cl); 2595 2594 2596 2595 scnprintf(bf, size, "Cacheline 0x%lx", addr); 2597 2596 return 0; ··· 2789 2788 if (!c2c.use_stdio) { 2790 2789 dim_offset.width = 5; 2791 2790 dim_offset.header = header_offset_tui; 2792 - nodestr = "CL"; 2791 + nodestr = chk_double_cl ? "Double-CL" : "CL"; 2793 2792 } 2794 2793 2795 2794 dim_percent_costly_snoop.header = percent_costly_snoop_header[c2c.display]; 2796 2795 2797 2796 /* Fix the zero line for dcacheline column. */ 2798 - buf = fill_line("Cacheline", dim_dcacheline.width + 2799 - dim_dcacheline_node.width + 2800 - dim_dcacheline_count.width + 4); 2797 + buf = fill_line(chk_double_cl ? "Double-Cacheline" : "Cacheline", 2798 + dim_dcacheline.width + 2799 + dim_dcacheline_node.width + 2800 + dim_dcacheline_count.width + 4); 2801 2801 if (!buf) 2802 2802 return -ENOMEM; 2803 2803 ··· 3039 3037 OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"), 3040 3038 OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr, 3041 3039 "Enable LBR callgraph stitching approach"), 3040 + OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"), 3042 3041 OPT_PARENT(c2c_options), 3043 3042 OPT_END() 3044 3043 };
+3 -3
tools/perf/builtin-inject.c
··· 215 215 216 216 #ifdef HAVE_AUXTRACE_SUPPORT 217 217 218 - static int copy_bytes(struct perf_inject *inject, int fd, off_t size) 218 + static int copy_bytes(struct perf_inject *inject, struct perf_data *data, off_t size) 219 219 { 220 220 char buf[4096]; 221 221 ssize_t ssz; 222 222 int ret; 223 223 224 224 while (size > 0) { 225 - ssz = read(fd, buf, min(size, (off_t)sizeof(buf))); 225 + ssz = perf_data__read(data, buf, min(size, (off_t)sizeof(buf))); 226 226 if (ssz < 0) 227 227 return -errno; 228 228 ret = output_bytes(inject, buf, ssz); ··· 260 260 ret = output_bytes(inject, event, event->header.size); 261 261 if (ret < 0) 262 262 return ret; 263 - ret = copy_bytes(inject, perf_data__fd(session->data), 263 + ret = copy_bytes(inject, session->data, 264 264 event->auxtrace.size); 265 265 } else { 266 266 ret = output_bytes(inject, event,
+2 -18
tools/perf/builtin-list.c
··· 99 99 const char *scale_unit __maybe_unused, 100 100 bool deprecated, const char *event_type_desc, 101 101 const char *desc, const char *long_desc, 102 - const char *encoding_desc, 103 - const char *metric_name, const char *metric_expr) 102 + const char *encoding_desc) 104 103 { 105 104 struct print_state *print_state = ps; 106 105 int pos; ··· 158 159 if (print_state->detailed && encoding_desc) { 159 160 printf("%*s", 8, ""); 160 161 wordwrap(encoding_desc, 8, pager_get_columns(), 0); 161 - if (metric_name) 162 - printf(" MetricName: %s", metric_name); 163 - if (metric_expr) 164 - printf(" MetricExpr: %s", metric_expr); 165 162 putchar('\n'); 166 163 } 167 164 } ··· 303 308 const char *scale_unit, 304 309 bool deprecated, const char *event_type_desc, 305 310 const char *desc, const char *long_desc, 306 - const char *encoding_desc, 307 - const char *metric_name, const char *metric_expr) 311 + const char *encoding_desc) 308 312 { 309 313 struct json_print_state *print_state = ps; 310 314 bool need_sep = false; ··· 358 364 if (encoding_desc) { 359 365 fix_escape_printf(&buf, "%s\t\"Encoding\": \"%S\"", need_sep ? ",\n" : "", 360 366 encoding_desc); 361 - need_sep = true; 362 - } 363 - if (metric_name) { 364 - fix_escape_printf(&buf, "%s\t\"MetricName\": \"%S\"", need_sep ? ",\n" : "", 365 - metric_name); 366 - need_sep = true; 367 - } 368 - if (metric_expr) { 369 - fix_escape_printf(&buf, "%s\t\"MetricExpr\": \"%S\"", need_sep ? ",\n" : "", 370 - metric_expr); 371 367 need_sep = true; 372 368 } 373 369 printf("%s}", need_sep ? "\n" : "");
+130 -17
tools/perf/builtin-lock.c
··· 58 58 static bool combine_locks; 59 59 static bool show_thread_stats; 60 60 static bool show_lock_addrs; 61 + static bool show_lock_owner; 61 62 static bool use_bpf; 62 63 static unsigned long bpf_map_entries = 10240; 63 64 static int max_stack_depth = CONTENTION_STACK_DEPTH; 64 65 static int stack_skip = CONTENTION_STACK_SKIP; 65 66 static int print_nr_entries = INT_MAX / 2; 67 + static LIST_HEAD(callstack_filters); 68 + 69 + struct callstack_filter { 70 + struct list_head list; 71 + char name[]; 72 + }; 66 73 67 74 static struct lock_filter filters; 68 75 69 76 static enum lock_aggr_mode aggr_mode = LOCK_AGGR_ADDR; 77 + 78 + static bool needs_callstack(void) 79 + { 80 + return verbose > 0 || !list_empty(&callstack_filters); 81 + } 70 82 71 83 static struct thread_stat *thread_stat_find(u32 tid) 72 84 { ··· 466 454 return container_of(node, struct lock_stat, rb); 467 455 } 468 456 469 - static struct lock_stat *lock_stat_find(u64 addr) 457 + struct lock_stat *lock_stat_find(u64 addr) 470 458 { 471 459 struct hlist_head *entry = lockhashentry(addr); 472 460 struct lock_stat *ret; ··· 478 466 return NULL; 479 467 } 480 468 481 - static struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags) 469 + struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags) 482 470 { 483 471 struct hlist_head *entry = lockhashentry(addr); 484 472 struct lock_stat *ret, *new; ··· 508 496 alloc_failed: 509 497 pr_err("memory allocation failed\n"); 510 498 return NULL; 499 + } 500 + 501 + bool match_callstack_filter(struct machine *machine, u64 *callstack) 502 + { 503 + struct map *kmap; 504 + struct symbol *sym; 505 + u64 ip; 506 + 507 + if (list_empty(&callstack_filters)) 508 + return true; 509 + 510 + for (int i = 0; i < max_stack_depth; i++) { 511 + struct callstack_filter *filter; 512 + 513 + if (!callstack || !callstack[i]) 514 + break; 515 + 516 + ip = callstack[i]; 517 + sym = machine__find_kernel_symbol(machine, ip, &kmap); 518 + if (sym == NULL) 519 + continue; 520 + 521 + list_for_each_entry(filter, &callstack_filters, list) { 522 + if (strstr(sym->name, filter->name)) 523 + return true; 524 + } 525 + } 526 + return false; 511 527 } 512 528 513 529 struct trace_lock_handler { ··· 1099 1059 ls = lock_stat_findnew(key, name, flags); 1100 1060 if (!ls) 1101 1061 return -ENOMEM; 1102 - 1103 - if (aggr_mode == LOCK_AGGR_CALLER && verbose > 0) { 1104 - ls->callstack = get_callstack(sample, max_stack_depth); 1105 - if (ls->callstack == NULL) 1106 - return -ENOMEM; 1107 - } 1108 1062 } 1109 1063 1110 1064 if (filters.nr_types) { ··· 1127 1093 1128 1094 if (!found) 1129 1095 return 0; 1096 + } 1097 + 1098 + if (needs_callstack()) { 1099 + u64 *callstack = get_callstack(sample, max_stack_depth); 1100 + if (callstack == NULL) 1101 + return -ENOMEM; 1102 + 1103 + if (!match_callstack_filter(machine, callstack)) { 1104 + free(callstack); 1105 + return 0; 1106 + } 1107 + 1108 + if (ls->callstack == NULL) 1109 + ls->callstack = callstack; 1110 + else 1111 + free(callstack); 1130 1112 } 1131 1113 1132 1114 ts = thread_stat_findnew(sample->tid); ··· 1617 1567 1618 1568 switch (aggr_mode) { 1619 1569 case LOCK_AGGR_TASK: 1620 - pr_info(" %10s %s\n\n", "pid", "comm"); 1570 + pr_info(" %10s %s\n\n", "pid", 1571 + show_lock_owner ? "owner" : "comm"); 1621 1572 break; 1622 1573 case LOCK_AGGR_CALLER: 1623 1574 pr_info(" %10s %s\n\n", "type", "caller"); ··· 1658 1607 case LOCK_AGGR_TASK: 1659 1608 pid = st->addr; 1660 1609 t = perf_session__findnew(session, pid); 1661 - pr_info(" %10d %s\n", pid, thread__comm_str(t)); 1610 + pr_info(" %10d %s\n", 1611 + pid, pid == -1 ? "Unknown" : thread__comm_str(t)); 1662 1612 break; 1663 1613 case LOCK_AGGR_ADDR: 1664 1614 pr_info(" %016llx %s\n", (unsigned long long)st->addr, ··· 1771 1719 { 1772 1720 } 1773 1721 1722 + static int check_lock_contention_options(const struct option *options, 1723 + const char * const *usage) 1724 + 1725 + { 1726 + if (show_thread_stats && show_lock_addrs) { 1727 + pr_err("Cannot use thread and addr mode together\n"); 1728 + parse_options_usage(usage, options, "threads", 0); 1729 + parse_options_usage(NULL, options, "lock-addr", 0); 1730 + return -1; 1731 + } 1732 + 1733 + if (show_lock_owner && !use_bpf) { 1734 + pr_err("Lock owners are available only with BPF\n"); 1735 + parse_options_usage(usage, options, "lock-owner", 0); 1736 + parse_options_usage(NULL, options, "use-bpf", 0); 1737 + return -1; 1738 + } 1739 + 1740 + if (show_lock_owner && show_lock_addrs) { 1741 + pr_err("Cannot use owner and addr mode together\n"); 1742 + parse_options_usage(usage, options, "lock-owner", 0); 1743 + parse_options_usage(NULL, options, "lock-addr", 0); 1744 + return -1; 1745 + } 1746 + 1747 + if (show_lock_owner) 1748 + show_thread_stats = true; 1749 + 1750 + return 0; 1751 + } 1752 + 1774 1753 static int __cmd_contention(int argc, const char **argv) 1775 1754 { 1776 1755 int err = -EINVAL; ··· 1826 1743 .max_stack = max_stack_depth, 1827 1744 .stack_skip = stack_skip, 1828 1745 .filters = &filters, 1746 + .save_callstack = needs_callstack(), 1747 + .owner = show_lock_owner, 1829 1748 }; 1830 1749 1831 1750 session = perf_session__new(use_bpf ? NULL : &data, &eops); ··· 1840 1755 1841 1756 con.aggr_mode = aggr_mode = show_thread_stats ? LOCK_AGGR_TASK : 1842 1757 show_lock_addrs ? LOCK_AGGR_ADDR : LOCK_AGGR_CALLER; 1758 + 1759 + if (con.aggr_mode == LOCK_AGGR_CALLER) 1760 + con.save_callstack = true; 1843 1761 1844 1762 /* for lock function check */ 1845 1763 symbol_conf.sort_by_name = true; ··· 2211 2123 return ret; 2212 2124 } 2213 2125 2126 + static int parse_call_stack(const struct option *opt __maybe_unused, const char *str, 2127 + int unset __maybe_unused) 2128 + { 2129 + char *s, *tmp, *tok; 2130 + int ret = 0; 2131 + 2132 + s = strdup(str); 2133 + if (s == NULL) 2134 + return -1; 2135 + 2136 + for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) { 2137 + struct callstack_filter *entry; 2138 + 2139 + entry = malloc(sizeof(*entry) + strlen(tok) + 1); 2140 + if (entry == NULL) { 2141 + pr_err("Memory allocation failure\n"); 2142 + return -1; 2143 + } 2144 + 2145 + strcpy(entry->name, tok); 2146 + list_add_tail(&entry->list, &callstack_filters); 2147 + } 2148 + 2149 + free(s); 2150 + return ret; 2151 + } 2152 + 2214 2153 int cmd_lock(int argc, const char **argv) 2215 2154 { 2216 2155 const struct option lock_options[] = { ··· 2305 2190 "Filter specific type of locks", parse_lock_type), 2306 2191 OPT_CALLBACK('L', "lock-filter", NULL, "ADDRS/NAMES", 2307 2192 "Filter specific address/symbol of locks", parse_lock_addr), 2193 + OPT_CALLBACK('S', "callstack-filter", NULL, "NAMES", 2194 + "Filter specific function in the callstack", parse_call_stack), 2195 + OPT_BOOLEAN('o', "lock-owner", &show_lock_owner, "show lock owners instead of waiters"), 2308 2196 OPT_PARENT(lock_options) 2309 2197 }; 2310 2198 ··· 2378 2260 contention_usage, 0); 2379 2261 } 2380 2262 2381 - if (show_thread_stats && show_lock_addrs) { 2382 - pr_err("Cannot use thread and addr mode together\n"); 2383 - parse_options_usage(contention_usage, contention_options, 2384 - "threads", 0); 2385 - parse_options_usage(NULL, contention_options, 2386 - "lock-addr", 0); 2263 + if (check_lock_contention_options(contention_options, 2264 + contention_usage) < 0) 2387 2265 return -1; 2388 - } 2389 2266 2390 2267 rc = __cmd_contention(argc, argv); 2391 2268 } else {
+9
tools/perf/builtin-probe.c
··· 383 383 384 384 /* Note that it is possible to skip all events because of blacklist */ 385 385 if (event) { 386 + #ifndef HAVE_LIBTRACEEVENT 387 + pr_info("\nperf is not linked with libtraceevent, to use the new probe you can use tracefs:\n\n"); 388 + pr_info("\tcd /sys/kernel/tracing/\n"); 389 + pr_info("\techo 1 > events/%s/%s/enable\n", group, event); 390 + pr_info("\techo 1 > tracing_on\n"); 391 + pr_info("\tcat trace_pipe\n"); 392 + pr_info("\tBefore removing the probe, echo 0 > events/%s/%s/enable\n", group, event); 393 + #else 386 394 /* Show how to use the event. */ 387 395 pr_info("\nYou can now use it in all perf tools, such as:\n\n"); 388 396 pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", group, event); 397 + #endif 389 398 } 390 399 391 400 out_cleanup:
+6 -10
tools/perf/builtin-record.c
··· 154 154 struct perf_tool tool; 155 155 struct record_opts opts; 156 156 u64 bytes_written; 157 + u64 thread_bytes_written; 157 158 struct perf_data data; 158 159 struct auxtrace_record *itr; 159 160 struct evlist *evlist; ··· 227 226 228 227 static u64 record__bytes_written(struct record *rec) 229 228 { 230 - int t; 231 - u64 bytes_written = rec->bytes_written; 232 - struct record_thread *thread_data = rec->thread_data; 233 - 234 - for (t = 0; t < rec->nr_threads; t++) 235 - bytes_written += thread_data[t].bytes_written; 236 - 237 - return bytes_written; 229 + return rec->bytes_written + rec->thread_bytes_written; 238 230 } 239 231 240 232 static bool record__output_max_size_exceeded(struct record *rec) ··· 249 255 return -1; 250 256 } 251 257 252 - if (map && map->file) 258 + if (map && map->file) { 253 259 thread->bytes_written += size; 254 - else 260 + rec->thread_bytes_written += size; 261 + } else { 255 262 rec->bytes_written += size; 263 + } 256 264 257 265 if (record__output_max_size_exceeded(rec) && !done) { 258 266 fprintf(stderr, "[ perf record: perf size limit reached (%" PRIu64 " KB),"
+35 -5
tools/perf/builtin-script.c
··· 59 59 #include "util/dlfilter.h" 60 60 #include "util/record.h" 61 61 #include "util/util.h" 62 + #include "util/cgroup.h" 62 63 #include "perf.h" 63 64 64 65 #include <linux/ctype.h> ··· 131 130 PERF_OUTPUT_BRSTACKINSNLEN = 1ULL << 36, 132 131 PERF_OUTPUT_MACHINE_PID = 1ULL << 37, 133 132 PERF_OUTPUT_VCPU = 1ULL << 38, 133 + PERF_OUTPUT_CGROUP = 1ULL << 39, 134 + PERF_OUTPUT_RETIRE_LAT = 1ULL << 40, 134 135 }; 135 136 136 137 struct perf_script { ··· 203 200 {.str = "brstackinsnlen", .field = PERF_OUTPUT_BRSTACKINSNLEN}, 204 201 {.str = "machine_pid", .field = PERF_OUTPUT_MACHINE_PID}, 205 202 {.str = "vcpu", .field = PERF_OUTPUT_VCPU}, 203 + {.str = "cgroup", .field = PERF_OUTPUT_CGROUP}, 204 + {.str = "retire_lat", .field = PERF_OUTPUT_RETIRE_LAT}, 206 205 }; 207 206 208 207 enum { ··· 280 275 PERF_OUTPUT_ADDR | PERF_OUTPUT_DATA_SRC | 281 276 PERF_OUTPUT_WEIGHT | PERF_OUTPUT_PHYS_ADDR | 282 277 PERF_OUTPUT_DATA_PAGE_SIZE | PERF_OUTPUT_CODE_PAGE_SIZE | 283 - PERF_OUTPUT_INS_LAT, 278 + PERF_OUTPUT_INS_LAT | PERF_OUTPUT_RETIRE_LAT, 284 279 285 280 .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, 286 281 }, ··· 545 540 546 541 if (PRINT_FIELD(INS_LAT) && 547 542 evsel__check_stype(evsel, PERF_SAMPLE_WEIGHT_STRUCT, "WEIGHT_STRUCT", PERF_OUTPUT_INS_LAT)) 543 + return -EINVAL; 544 + 545 + if (PRINT_FIELD(CGROUP) && 546 + evsel__check_stype(evsel, PERF_SAMPLE_CGROUP, "CGROUP", PERF_OUTPUT_CGROUP)) { 547 + pr_err("Hint: run 'perf record --all-cgroups ...'\n"); 548 + return -EINVAL; 549 + } 550 + 551 + if (PRINT_FIELD(RETIRE_LAT) && 552 + evsel__check_stype(evsel, PERF_SAMPLE_WEIGHT_STRUCT, "WEIGHT_STRUCT", PERF_OUTPUT_RETIRE_LAT)) 548 553 return -EINVAL; 549 554 550 555 return 0; ··· 895 880 896 881 static int print_bstack_flags(FILE *fp, struct branch_entry *br) 897 882 { 898 - return fprintf(fp, "/%c/%c/%c/%d/%s ", 883 + return fprintf(fp, "/%c/%c/%c/%d/%s/%s ", 899 884 mispred_str(br), 900 885 br->flags.in_tx ? 'X' : '-', 901 886 br->flags.abort ? 'A' : '-', 902 887 br->flags.cycles, 903 - get_branch_type(br)); 888 + get_branch_type(br), 889 + br->flags.spec ? branch_spec_desc(br->flags.spec) : "-"); 904 890 } 905 891 906 892 static int perf_sample__fprintf_brstack(struct perf_sample *sample, ··· 1317 1301 goto out; 1318 1302 1319 1303 /* 1320 - * Print final block upto sample 1304 + * Print final block up to sample 1321 1305 * 1322 1306 * Due to pipeline delays the LBRs might be missing a branch 1323 1307 * or two, which can result in very large or negative blocks ··· 2194 2178 if (PRINT_FIELD(INS_LAT)) 2195 2179 fprintf(fp, "%16" PRIu16, sample->ins_lat); 2196 2180 2181 + if (PRINT_FIELD(RETIRE_LAT)) 2182 + fprintf(fp, "%16" PRIu16, sample->retire_lat); 2183 + 2197 2184 if (PRINT_FIELD(IP)) { 2198 2185 struct callchain_cursor *cursor = NULL; 2199 2186 ··· 2238 2219 2239 2220 if (PRINT_FIELD(CODE_PAGE_SIZE)) 2240 2221 fprintf(fp, " %s", get_page_size_name(sample->code_page_size, str)); 2222 + 2223 + if (PRINT_FIELD(CGROUP)) { 2224 + const char *cgrp_name; 2225 + struct cgroup *cgrp = cgroup__find(machine->env, 2226 + sample->cgroup); 2227 + if (cgrp != NULL) 2228 + cgrp_name = cgrp->name; 2229 + else 2230 + cgrp_name = "unknown"; 2231 + fprintf(fp, " %s", cgrp_name); 2232 + } 2241 2233 2242 2234 perf_sample__fprintf_ipc(sample, attr, fp); 2243 2235 ··· 3886 3856 "brstacksym,flags,data_src,weight,bpf-output,brstackinsn," 3887 3857 "brstackinsnlen,brstackoff,callindent,insn,insnlen,synth," 3888 3858 "phys_addr,metric,misc,srccode,ipc,tod,data_page_size," 3889 - "code_page_size,ins_lat", 3859 + "code_page_size,ins_lat,machine_pid,vcpu,cgroup,retire_lat", 3890 3860 parse_output_fields), 3891 3861 OPT_BOOLEAN('a', "all-cpus", &system_wide, 3892 3862 "system-wide collection from all CPUs"),
-1
tools/perf/builtin-stat.c
··· 2524 2524 &stat_config.metric_events); 2525 2525 zfree(&metrics); 2526 2526 } 2527 - perf_stat__collect_metric_expr(evsel_list); 2528 2527 perf_stat__init_shadow_stats(); 2529 2528 2530 2529 if (add_default_attributes())
+1 -3
tools/perf/builtin-trace.c
··· 2731 2731 offset = format_field__intval(field, sample, evsel->needs_swap); 2732 2732 syscall_arg.len = offset >> 16; 2733 2733 offset &= 0xffff; 2734 - #ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE 2735 - if (field->flags & TEP_FIELD_IS_RELATIVE) 2734 + if (tep_field_is_relative(field->flags)) 2736 2735 offset += field->offset + field->size; 2737 - #endif 2738 2736 } 2739 2737 2740 2738 val = (uintptr_t)(sample->raw_data + offset);
+8 -3
tools/perf/perf-completion.sh
··· 165 165 166 166 local cur1=${COMP_WORDS[COMP_CWORD]} 167 167 local raw_evts=$($cmd list --raw-dump) 168 - local arr s tmp result 168 + local arr s tmp result cpu_evts 169 + 170 + # aarch64 doesn't have /sys/bus/event_source/devices/cpu/events 171 + if [[ `uname -m` != aarch64 ]]; then 172 + cpu_evts=$(ls /sys/bus/event_source/devices/cpu/events) 173 + fi 169 174 170 175 if [[ "$cur1" == */* && ${cur1#*/} =~ ^[A-Z] ]]; then 171 176 OLD_IFS="$IFS" ··· 188 183 fi 189 184 done 190 185 191 - evts=${result}" "$(ls /sys/bus/event_source/devices/cpu/events) 186 + evts=${result}" "${cpu_evts} 192 187 else 193 - evts=${raw_evts}" "$(ls /sys/bus/event_source/devices/cpu/events) 188 + evts=${raw_evts}" "${cpu_evts} 194 189 fi 195 190 196 191 if [[ "$cur1" == , ]]; then
+13 -3
tools/perf/pmu-events/Build
··· 6 6 JSON_TEST = $(shell [ -d $(JDIR_TEST) ] && \ 7 7 find $(JDIR_TEST) -name '*.json') 8 8 JEVENTS_PY = pmu-events/jevents.py 9 + METRIC_PY = pmu-events/metric.py 10 + METRIC_TEST_PY = pmu-events/metric_test.py 11 + EMPTY_PMU_EVENTS_C = pmu-events/empty-pmu-events.c 12 + PMU_EVENTS_C = $(OUTPUT)pmu-events/pmu-events.c 13 + METRIC_TEST_LOG = $(OUTPUT)pmu-events/metric_test.log 9 14 10 15 ifeq ($(JEVENTS_ARCH),) 11 16 JEVENTS_ARCH=$(SRCARCH) 12 17 endif 18 + JEVENTS_MODEL ?= all 13 19 14 20 # 15 21 # Locate/process JSON files in pmu-events/arch/ ··· 23 17 # 24 18 25 19 ifeq ($(NO_JEVENTS),1) 26 - $(OUTPUT)pmu-events/pmu-events.c: pmu-events/empty-pmu-events.c 20 + $(PMU_EVENTS_C): $(EMPTY_PMU_EVENTS_C) 27 21 $(call rule_mkdir) 28 22 $(Q)$(call echo-cmd,gen)cp $< $@ 29 23 else 30 - $(OUTPUT)pmu-events/pmu-events.c: $(JSON) $(JSON_TEST) $(JEVENTS_PY) pmu-events/metric.py 24 + $(METRIC_TEST_LOG): $(METRIC_TEST_PY) $(METRIC_PY) 31 25 $(call rule_mkdir) 32 - $(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) $(JEVENTS_ARCH) pmu-events/arch $@ 26 + $(Q)$(call echo-cmd,test)$(PYTHON) $< 2> $@ || (cat $@ && false) 27 + 28 + $(PMU_EVENTS_C): $(JSON) $(JSON_TEST) $(JEVENTS_PY) $(METRIC_PY) $(METRIC_TEST_LOG) 29 + $(call rule_mkdir) 30 + $(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) $(JEVENTS_ARCH) $(JEVENTS_MODEL) pmu-events/arch $@ 33 31 endif
+273
tools/perf/pmu-events/arch/arm64/arm/neoverse-n2-v2/metrics.json
··· 1 + [ 2 + { 3 + "ArchStdEvent": "FRONTEND_BOUND", 4 + "MetricExpr": "((stall_slot_frontend) if (#slots - 5) else (stall_slot_frontend - cpu_cycles)) / (#slots * cpu_cycles)" 5 + }, 6 + { 7 + "ArchStdEvent": "BAD_SPECULATION", 8 + "MetricExpr": "(1 - op_retired / op_spec) * (1 - (stall_slot if (#slots - 5) else (stall_slot - cpu_cycles)) / (#slots * cpu_cycles))" 9 + }, 10 + { 11 + "ArchStdEvent": "RETIRING", 12 + "MetricExpr": "(op_retired / op_spec) * (1 - (stall_slot if (#slots - 5) else (stall_slot - cpu_cycles)) / (#slots * cpu_cycles))" 13 + }, 14 + { 15 + "ArchStdEvent": "BACKEND_BOUND" 16 + }, 17 + { 18 + "MetricExpr": "L1D_TLB_REFILL / L1D_TLB", 19 + "BriefDescription": "The rate of L1D TLB refill to the overall L1D TLB lookups", 20 + "MetricGroup": "TLB", 21 + "MetricName": "l1d_tlb_miss_rate", 22 + "ScaleUnit": "100%" 23 + }, 24 + { 25 + "MetricExpr": "L1I_TLB_REFILL / L1I_TLB", 26 + "BriefDescription": "The rate of L1I TLB refill to the overall L1I TLB lookups", 27 + "MetricGroup": "TLB", 28 + "MetricName": "l1i_tlb_miss_rate", 29 + "ScaleUnit": "100%" 30 + }, 31 + { 32 + "MetricExpr": "L2D_TLB_REFILL / L2D_TLB", 33 + "BriefDescription": "The rate of L2D TLB refill to the overall L2D TLB lookups", 34 + "MetricGroup": "TLB", 35 + "MetricName": "l2_tlb_miss_rate", 36 + "ScaleUnit": "100%" 37 + }, 38 + { 39 + "MetricExpr": "DTLB_WALK / INST_RETIRED * 1000", 40 + "BriefDescription": "The rate of TLB Walks per kilo instructions for data accesses", 41 + "MetricGroup": "TLB", 42 + "MetricName": "dtlb_mpki", 43 + "ScaleUnit": "1MPKI" 44 + }, 45 + { 46 + "MetricExpr": "DTLB_WALK / L1D_TLB", 47 + "BriefDescription": "The rate of DTLB Walks to the overall L1D TLB lookups", 48 + "MetricGroup": "TLB", 49 + "MetricName": "dtlb_walk_rate", 50 + "ScaleUnit": "100%" 51 + }, 52 + { 53 + "MetricExpr": "ITLB_WALK / INST_RETIRED * 1000", 54 + "BriefDescription": "The rate of TLB Walks per kilo instructions for instruction accesses", 55 + "MetricGroup": "TLB", 56 + "MetricName": "itlb_mpki", 57 + "ScaleUnit": "1MPKI" 58 + }, 59 + { 60 + "MetricExpr": "ITLB_WALK / L1I_TLB", 61 + "BriefDescription": "The rate of ITLB Walks to the overall L1I TLB lookups", 62 + "MetricGroup": "TLB", 63 + "MetricName": "itlb_walk_rate", 64 + "ScaleUnit": "100%" 65 + }, 66 + { 67 + "MetricExpr": "L1I_CACHE_REFILL / INST_RETIRED * 1000", 68 + "BriefDescription": "The rate of L1 I-Cache misses per kilo instructions", 69 + "MetricGroup": "Cache", 70 + "MetricName": "l1i_cache_mpki", 71 + "ScaleUnit": "1MPKI" 72 + }, 73 + { 74 + "MetricExpr": "L1I_CACHE_REFILL / L1I_CACHE", 75 + "BriefDescription": "The rate of L1 I-Cache misses to the overall L1 I-Cache", 76 + "MetricGroup": "Cache", 77 + "MetricName": "l1i_cache_miss_rate", 78 + "ScaleUnit": "100%" 79 + }, 80 + { 81 + "MetricExpr": "L1D_CACHE_REFILL / INST_RETIRED * 1000", 82 + "BriefDescription": "The rate of L1 D-Cache misses per kilo instructions", 83 + "MetricGroup": "Cache", 84 + "MetricName": "l1d_cache_mpki", 85 + "ScaleUnit": "1MPKI" 86 + }, 87 + { 88 + "MetricExpr": "L1D_CACHE_REFILL / L1D_CACHE", 89 + "BriefDescription": "The rate of L1 D-Cache misses to the overall L1 D-Cache", 90 + "MetricGroup": "Cache", 91 + "MetricName": "l1d_cache_miss_rate", 92 + "ScaleUnit": "100%" 93 + }, 94 + { 95 + "MetricExpr": "L2D_CACHE_REFILL / INST_RETIRED * 1000", 96 + "BriefDescription": "The rate of L2 D-Cache misses per kilo instructions", 97 + "MetricGroup": "Cache", 98 + "MetricName": "l2d_cache_mpki", 99 + "ScaleUnit": "1MPKI" 100 + }, 101 + { 102 + "MetricExpr": "L2D_CACHE_REFILL / L2D_CACHE", 103 + "BriefDescription": "The rate of L2 D-Cache misses to the overall L2 D-Cache", 104 + "MetricGroup": "Cache", 105 + "MetricName": "l2d_cache_miss_rate", 106 + "ScaleUnit": "100%" 107 + }, 108 + { 109 + "MetricExpr": "L3D_CACHE_REFILL / INST_RETIRED * 1000", 110 + "BriefDescription": "The rate of L3 D-Cache misses per kilo instructions", 111 + "MetricGroup": "Cache", 112 + "MetricName": "l3d_cache_mpki", 113 + "ScaleUnit": "1MPKI" 114 + }, 115 + { 116 + "MetricExpr": "L3D_CACHE_REFILL / L3D_CACHE", 117 + "BriefDescription": "The rate of L3 D-Cache misses to the overall L3 D-Cache", 118 + "MetricGroup": "Cache", 119 + "MetricName": "l3d_cache_miss_rate", 120 + "ScaleUnit": "100%" 121 + }, 122 + { 123 + "MetricExpr": "LL_CACHE_MISS_RD / INST_RETIRED * 1000", 124 + "BriefDescription": "The rate of LL Cache read misses per kilo instructions", 125 + "MetricGroup": "Cache", 126 + "MetricName": "ll_cache_read_mpki", 127 + "ScaleUnit": "1MPKI" 128 + }, 129 + { 130 + "MetricExpr": "LL_CACHE_MISS_RD / LL_CACHE_RD", 131 + "BriefDescription": "The rate of LL Cache read misses to the overall LL Cache read", 132 + "MetricGroup": "Cache", 133 + "MetricName": "ll_cache_read_miss_rate", 134 + "ScaleUnit": "100%" 135 + }, 136 + { 137 + "MetricExpr": "(LL_CACHE_RD - LL_CACHE_MISS_RD) / LL_CACHE_RD", 138 + "BriefDescription": "The rate of LL Cache read hit to the overall LL Cache read", 139 + "MetricGroup": "Cache", 140 + "MetricName": "ll_cache_read_hit_rate", 141 + "ScaleUnit": "100%" 142 + }, 143 + { 144 + "MetricExpr": "BR_MIS_PRED_RETIRED / INST_RETIRED * 1000", 145 + "BriefDescription": "The rate of branches mis-predicted per kilo instructions", 146 + "MetricGroup": "Branch", 147 + "MetricName": "branch_mpki", 148 + "ScaleUnit": "1MPKI" 149 + }, 150 + { 151 + "MetricExpr": "BR_RETIRED / INST_RETIRED * 1000", 152 + "BriefDescription": "The rate of branches retired per kilo instructions", 153 + "MetricGroup": "Branch", 154 + "MetricName": "branch_pki", 155 + "ScaleUnit": "1PKI" 156 + }, 157 + { 158 + "MetricExpr": "BR_MIS_PRED_RETIRED / BR_RETIRED", 159 + "BriefDescription": "The rate of branches mis-predited to the overall branches", 160 + "MetricGroup": "Branch", 161 + "MetricName": "branch_miss_pred_rate", 162 + "ScaleUnit": "100%" 163 + }, 164 + { 165 + "MetricExpr": "instructions / CPU_CYCLES", 166 + "BriefDescription": "The average number of instructions executed for each cycle.", 167 + "MetricGroup": "PEutilization", 168 + "MetricName": "ipc" 169 + }, 170 + { 171 + "MetricExpr": "ipc / 5", 172 + "BriefDescription": "IPC percentage of peak. The peak of IPC is 5.", 173 + "MetricGroup": "PEutilization", 174 + "MetricName": "ipc_rate", 175 + "ScaleUnit": "100%" 176 + }, 177 + { 178 + "MetricExpr": "INST_RETIRED / CPU_CYCLES", 179 + "BriefDescription": "Architecturally executed Instructions Per Cycle (IPC)", 180 + "MetricGroup": "PEutilization", 181 + "MetricName": "retired_ipc" 182 + }, 183 + { 184 + "MetricExpr": "INST_SPEC / CPU_CYCLES", 185 + "BriefDescription": "Speculatively executed Instructions Per Cycle (IPC)", 186 + "MetricGroup": "PEutilization", 187 + "MetricName": "spec_ipc" 188 + }, 189 + { 190 + "MetricExpr": "OP_RETIRED / OP_SPEC", 191 + "BriefDescription": "Of all the micro-operations issued, what percentage are retired(committed)", 192 + "MetricGroup": "PEutilization", 193 + "MetricName": "retired_rate", 194 + "ScaleUnit": "100%" 195 + }, 196 + { 197 + "MetricExpr": "1 - OP_RETIRED / OP_SPEC", 198 + "BriefDescription": "Of all the micro-operations issued, what percentage are not retired(committed)", 199 + "MetricGroup": "PEutilization", 200 + "MetricName": "wasted_rate", 201 + "ScaleUnit": "100%" 202 + }, 203 + { 204 + "MetricExpr": "OP_RETIRED / OP_SPEC * (1 - (STALL_SLOT if (#slots - 5) else (STALL_SLOT - CPU_CYCLES)) / (#slots * CPU_CYCLES))", 205 + "BriefDescription": "The truly effective ratio of micro-operations executed by the CPU, which means that misprediction and stall are not included", 206 + "MetricGroup": "PEutilization", 207 + "MetricName": "cpu_utilization", 208 + "ScaleUnit": "100%" 209 + }, 210 + { 211 + "MetricExpr": "LD_SPEC / INST_SPEC", 212 + "BriefDescription": "The rate of load instructions speculatively executed to overall instructions speclatively executed", 213 + "MetricGroup": "InstructionMix", 214 + "MetricName": "load_spec_rate", 215 + "ScaleUnit": "100%" 216 + }, 217 + { 218 + "MetricExpr": "ST_SPEC / INST_SPEC", 219 + "BriefDescription": "The rate of store instructions speculatively executed to overall instructions speclatively executed", 220 + "MetricGroup": "InstructionMix", 221 + "MetricName": "store_spec_rate", 222 + "ScaleUnit": "100%" 223 + }, 224 + { 225 + "MetricExpr": "DP_SPEC / INST_SPEC", 226 + "BriefDescription": "The rate of integer data-processing instructions speculatively executed to overall instructions speclatively executed", 227 + "MetricGroup": "InstructionMix", 228 + "MetricName": "data_process_spec_rate", 229 + "ScaleUnit": "100%" 230 + }, 231 + { 232 + "MetricExpr": "ASE_SPEC / INST_SPEC", 233 + "BriefDescription": "The rate of advanced SIMD instructions speculatively executed to overall instructions speclatively executed", 234 + "MetricGroup": "InstructionMix", 235 + "MetricName": "advanced_simd_spec_rate", 236 + "ScaleUnit": "100%" 237 + }, 238 + { 239 + "MetricExpr": "VFP_SPEC / INST_SPEC", 240 + "BriefDescription": "The rate of floating point instructions speculatively executed to overall instructions speclatively executed", 241 + "MetricGroup": "InstructionMix", 242 + "MetricName": "float_point_spec_rate", 243 + "ScaleUnit": "100%" 244 + }, 245 + { 246 + "MetricExpr": "CRYPTO_SPEC / INST_SPEC", 247 + "BriefDescription": "The rate of crypto instructions speculatively executed to overall instructions speclatively executed", 248 + "MetricGroup": "InstructionMix", 249 + "MetricName": "crypto_spec_rate", 250 + "ScaleUnit": "100%" 251 + }, 252 + { 253 + "MetricExpr": "BR_IMMED_SPEC / INST_SPEC", 254 + "BriefDescription": "The rate of branch immediate instructions speculatively executed to overall instructions speclatively executed", 255 + "MetricGroup": "InstructionMix", 256 + "MetricName": "branch_immed_spec_rate", 257 + "ScaleUnit": "100%" 258 + }, 259 + { 260 + "MetricExpr": "BR_RETURN_SPEC / INST_SPEC", 261 + "BriefDescription": "The rate of procedure return instructions speculatively executed to overall instructions speclatively executed", 262 + "MetricGroup": "InstructionMix", 263 + "MetricName": "branch_return_spec_rate", 264 + "ScaleUnit": "100%" 265 + }, 266 + { 267 + "MetricExpr": "BR_INDIRECT_SPEC / INST_SPEC", 268 + "BriefDescription": "The rate of indirect branch instructions speculatively executed to overall instructions speclatively executed", 269 + "MetricGroup": "InstructionMix", 270 + "MetricName": "branch_indirect_spec_rate", 271 + "ScaleUnit": "100%" 272 + } 273 + ]
+30
tools/perf/pmu-events/arch/arm64/sbsa.json
··· 1 + [ 2 + { 3 + "MetricExpr": "stall_slot_frontend / (#slots * cpu_cycles)", 4 + "BriefDescription": "Frontend bound L1 topdown metric", 5 + "MetricGroup": "TopdownL1", 6 + "MetricName": "frontend_bound", 7 + "ScaleUnit": "100%" 8 + }, 9 + { 10 + "MetricExpr": "(1 - op_retired / op_spec) * (1 - stall_slot / (#slots * cpu_cycles))", 11 + "BriefDescription": "Bad speculation L1 topdown metric", 12 + "MetricGroup": "TopdownL1", 13 + "MetricName": "bad_speculation", 14 + "ScaleUnit": "100%" 15 + }, 16 + { 17 + "MetricExpr": "(op_retired / op_spec) * (1 - stall_slot / (#slots * cpu_cycles))", 18 + "BriefDescription": "Retiring L1 topdown metric", 19 + "MetricGroup": "TopdownL1", 20 + "MetricName": "retiring", 21 + "ScaleUnit": "100%" 22 + }, 23 + { 24 + "MetricExpr": "stall_slot_backend / (#slots * cpu_cycles)", 25 + "BriefDescription": "Backend Bound L1 topdown metric", 26 + "MetricGroup": "TopdownL1", 27 + "MetricName": "backend_bound", 28 + "ScaleUnit": "100%" 29 + } 30 + ]
+4 -4
tools/perf/pmu-events/arch/powerpc/power10/metrics.json
··· 15 15 { 16 16 "BriefDescription": "Average cycles per completed instruction when dispatch was stalled for any reason", 17 17 "MetricExpr": "PM_DISP_STALL_CYC / PM_RUN_INST_CMPL", 18 - "MetricGroup": "CPI", 18 + "MetricGroup": "CPI;CPI_STALL_RATIO", 19 19 "MetricName": "DISPATCHED_CPI" 20 20 }, 21 21 { ··· 147 147 { 148 148 "BriefDescription": "Average cycles per completed instruction when the NTC instruction has been dispatched but not issued for any reason", 149 149 "MetricExpr": "PM_ISSUE_STALL / PM_RUN_INST_CMPL", 150 - "MetricGroup": "CPI", 150 + "MetricGroup": "CPI;CPI_STALL_RATIO", 151 151 "MetricName": "ISSUE_STALL_CPI" 152 152 }, 153 153 { 154 154 "BriefDescription": "Average cycles per completed instruction when the NTC instruction is waiting to be finished in one of the execution units", 155 155 "MetricExpr": "PM_EXEC_STALL / PM_RUN_INST_CMPL", 156 - "MetricGroup": "CPI", 156 + "MetricGroup": "CPI;CPI_STALL_RATIO", 157 157 "MetricName": "EXECUTION_STALL_CPI" 158 158 }, 159 159 { ··· 309 309 { 310 310 "BriefDescription": "Average cycles per completed instruction when the NTC instruction cannot complete because the thread was blocked", 311 311 "MetricExpr": "PM_CMPL_STALL / PM_RUN_INST_CMPL", 312 - "MetricGroup": "CPI", 312 + "MetricGroup": "CPI;CPI_STALL_RATIO", 313 313 "MetricName": "COMPLETION_STALL_CPI" 314 314 }, 315 315 {
+1 -1
tools/perf/pmu-events/arch/powerpc/power10/others.json
··· 265 265 "BriefDescription": "Load Missed L1, counted at finish time." 266 266 }, 267 267 { 268 - "EventCode": "0x400FA", 268 + "EventCode": "0x500FA", 269 269 "EventName": "PM_RUN_INST_CMPL", 270 270 "BriefDescription": "Completed PowerPC instructions gated by the run latch." 271 271 }
+1 -1
tools/perf/pmu-events/arch/x86/mapfile.csv
··· 21 21 GenuineIntel-6-1[AEF],v3,nehalemep,core 22 22 GenuineIntel-6-2E,v3,nehalemex,core 23 23 GenuineIntel-6-2A,v17,sandybridge,core 24 - GenuineIntel-6-8F,v1.09,sapphirerapids,core 24 + GenuineIntel-6-(8F|CF),v1.09,sapphirerapids,core 25 25 GenuineIntel-6-(37|4A|4C|4D|5A),v14,silvermont,core 26 26 GenuineIntel-6-(4E|5E|8E|9E|A5|A6),v53,skylake,core 27 27 GenuineIntel-6-55-[01234],v1.28,skylakex,core
+94 -14
tools/perf/pmu-events/empty-pmu-events.c
··· 11 11 #include <string.h> 12 12 #include <stddef.h> 13 13 14 - static const struct pmu_event pme_test_soc_cpu[] = { 14 + static const struct pmu_event pmu_events__test_soc_cpu[] = { 15 15 { 16 16 .name = "l3_cache_rd", 17 17 .event = "event=0x40", ··· 106 106 .topic = "branch", 107 107 }, 108 108 { 109 + .name = 0, 110 + .event = 0, 111 + .desc = 0, 112 + }, 113 + }; 114 + 115 + static const struct pmu_metric pmu_metrics__test_soc_cpu[] = { 116 + { 109 117 .metric_expr = "1 / IPC", 110 118 .metric_name = "CPI", 111 119 }, ··· 178 170 .metric_name = "L1D_Cache_Fill_BW", 179 171 }, 180 172 { 181 - .name = 0, 182 - .event = 0, 183 - .desc = 0, 173 + .metric_expr = 0, 174 + .metric_name = 0, 184 175 }, 185 176 }; 186 177 187 178 /* Struct used to make the PMU event table implementation opaque to callers. */ 188 179 struct pmu_events_table { 189 180 const struct pmu_event *entries; 181 + }; 182 + 183 + /* Struct used to make the PMU metric table implementation opaque to callers. */ 184 + struct pmu_metrics_table { 185 + const struct pmu_metric *entries; 190 186 }; 191 187 192 188 /* ··· 204 192 struct pmu_events_map { 205 193 const char *arch; 206 194 const char *cpuid; 207 - const struct pmu_events_table table; 195 + const struct pmu_events_table event_table; 196 + const struct pmu_metrics_table metric_table; 208 197 }; 209 198 210 199 /* ··· 216 203 { 217 204 .arch = "testarch", 218 205 .cpuid = "testcpu", 219 - .table = { pme_test_soc_cpu }, 206 + .event_table = { pmu_events__test_soc_cpu }, 207 + .metric_table = { pmu_metrics__test_soc_cpu }, 220 208 }, 221 209 { 222 210 .arch = 0, 223 211 .cpuid = 0, 224 - .table = { 0 }, 212 + .event_table = { 0 }, 213 + .metric_table = { 0 }, 225 214 }, 226 215 }; 227 216 ··· 269 254 int pmu_events_table_for_each_event(const struct pmu_events_table *table, pmu_event_iter_fn fn, 270 255 void *data) 271 256 { 272 - for (const struct pmu_event *pe = &table->entries[0]; 273 - pe->name || pe->metric_group || pe->metric_name; 274 - pe++) { 257 + for (const struct pmu_event *pe = &table->entries[0]; pe->name; pe++) { 275 258 int ret = fn(pe, table, data); 276 259 277 260 if (ret) ··· 278 265 return 0; 279 266 } 280 267 281 - const struct pmu_events_table *perf_pmu__find_table(struct perf_pmu *pmu) 268 + int pmu_metrics_table_for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn, 269 + void *data) 270 + { 271 + for (const struct pmu_metric *pm = &table->entries[0]; pm->metric_expr; pm++) { 272 + int ret = fn(pm, table, data); 273 + 274 + if (ret) 275 + return ret; 276 + } 277 + return 0; 278 + } 279 + 280 + const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu) 282 281 { 283 282 const struct pmu_events_table *table = NULL; 284 283 char *cpuid = perf_pmu__getcpuid(pmu); ··· 310 285 break; 311 286 312 287 if (!strcmp_cpuid_str(map->cpuid, cpuid)) { 313 - table = &map->table; 288 + table = &map->event_table; 289 + break; 290 + } 291 + } 292 + free(cpuid); 293 + return table; 294 + } 295 + 296 + const struct pmu_metrics_table *perf_pmu__find_metrics_table(struct perf_pmu *pmu) 297 + { 298 + const struct pmu_metrics_table *table = NULL; 299 + char *cpuid = perf_pmu__getcpuid(pmu); 300 + int i; 301 + 302 + /* on some platforms which uses cpus map, cpuid can be NULL for 303 + * PMUs other than CORE PMUs. 304 + */ 305 + if (!cpuid) 306 + return NULL; 307 + 308 + i = 0; 309 + for (;;) { 310 + const struct pmu_events_map *map = &pmu_events_map[i++]; 311 + 312 + if (!map->cpuid) 313 + break; 314 + 315 + if (!strcmp_cpuid_str(map->cpuid, cpuid)) { 316 + table = &map->metric_table; 314 317 break; 315 318 } 316 319 } ··· 352 299 tables->arch; 353 300 tables++) { 354 301 if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) 355 - return &tables->table; 302 + return &tables->event_table; 303 + } 304 + return NULL; 305 + } 306 + 307 + const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const char *cpuid) 308 + { 309 + for (const struct pmu_events_map *tables = &pmu_events_map[0]; 310 + tables->arch; 311 + tables++) { 312 + if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) 313 + return &tables->metric_table; 356 314 } 357 315 return NULL; 358 316 } 359 317 360 318 int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data) 361 319 { 320 + for (const struct pmu_events_map *tables = &pmu_events_map[0]; tables->arch; tables++) { 321 + int ret = pmu_events_table_for_each_event(&tables->event_table, fn, data); 322 + 323 + if (ret) 324 + return ret; 325 + } 326 + return 0; 327 + } 328 + 329 + int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data) 330 + { 362 331 for (const struct pmu_events_map *tables = &pmu_events_map[0]; 363 332 tables->arch; 364 333 tables++) { 365 - int ret = pmu_events_table_for_each_event(&tables->table, fn, data); 334 + int ret = pmu_metrics_table_for_each_metric(&tables->metric_table, fn, data); 366 335 367 336 if (ret) 368 337 return ret; ··· 413 338 if (ret) 414 339 return ret; 415 340 } 341 + return 0; 342 + } 343 + 344 + int pmu_for_each_sys_metric(pmu_metric_iter_fn fn __maybe_unused, void *data __maybe_unused) 345 + { 416 346 return 0; 417 347 }
+296 -63
tools/perf/pmu-events/jevents.py
··· 3 3 """Convert directories of JSON events to C code.""" 4 4 import argparse 5 5 import csv 6 + from functools import lru_cache 6 7 import json 7 8 import metric 8 9 import os ··· 13 12 14 13 # Global command line arguments. 15 14 _args = None 15 + # List of regular event tables. 16 + _event_tables = [] 16 17 # List of event tables generated from "/sys" directories. 17 18 _sys_event_tables = [] 19 + # List of regular metric tables. 20 + _metric_tables = [] 21 + # List of metric tables generated from "/sys" directories. 22 + _sys_metric_tables = [] 23 + # Mapping between sys event table names and sys metric table names. 24 + _sys_event_table_to_metric_table_mapping = {} 18 25 # Map from an event name to an architecture standard 19 26 # JsonEvent. Architecture standard events are in json files in the top 20 27 # f'{_args.starting_dir}/{_args.arch}' directory. 21 28 _arch_std_events = {} 22 - # Track whether an events table is currently being defined and needs closing. 23 - _close_table = False 24 29 # Events to write out when the table is closed 25 30 _pending_events = [] 31 + # Name of events table to be written out 32 + _pending_events_tblname = None 33 + # Metrics to write out when the table is closed 34 + _pending_metrics = [] 35 + # Name of metrics table to be written out 36 + _pending_metrics_tblname = None 26 37 # Global BigCString shared by all structures. 27 38 _bcs = None 28 39 # Order specific JsonEvent attributes will be visited. 29 40 _json_event_attributes = [ 30 41 # cmp_sevent related attributes. 31 - 'name', 'pmu', 'topic', 'desc', 'metric_name', 'metric_group', 42 + 'name', 'pmu', 'topic', 'desc', 32 43 # Seems useful, put it early. 33 44 'event', 34 45 # Short things in alphabetical order. 35 46 'aggr_mode', 'compat', 'deprecated', 'perpkg', 'unit', 36 47 # Longer things (the last won't be iterated over during decompress). 37 - 'metric_constraint', 'metric_expr', 'long_desc' 48 + 'long_desc' 38 49 ] 39 50 51 + # Attributes that are in pmu_metric rather than pmu_event. 52 + _json_metric_attributes = [ 53 + 'metric_name', 'metric_group', 'metric_constraint', 'metric_expr', 'desc', 54 + 'long_desc', 'unit', 'compat', 'aggr_mode' 55 + ] 40 56 41 57 def removesuffix(s: str, suffix: str) -> str: 42 58 """Remove the suffix from a string ··· 64 46 return s[0:-len(suffix)] if s.endswith(suffix) else s 65 47 66 48 67 - def file_name_to_table_name(parents: Sequence[str], dirname: str) -> str: 49 + def file_name_to_table_name(prefix: str, parents: Sequence[str], 50 + dirname: str) -> str: 68 51 """Generate a C table name from directory names.""" 69 - tblname = 'pme' 52 + tblname = prefix 70 53 for p in parents: 71 54 tblname += '_' + p 72 55 tblname += '_' + dirname 73 56 return tblname.replace('-', '_') 57 + 74 58 75 59 def c_len(s: str) -> int: 76 60 """Return the length of s a C string ··· 291 271 self.metric_constraint = jd.get('MetricConstraint') 292 272 self.metric_expr = None 293 273 if 'MetricExpr' in jd: 294 - self.metric_expr = metric.ParsePerfJson(jd['MetricExpr']).Simplify() 274 + self.metric_expr = metric.ParsePerfJson(jd['MetricExpr']).Simplify() 295 275 296 276 arch_std = jd.get('ArchStdEvent') 297 277 if precise and self.desc and '(Precise Event)' not in self.desc: ··· 340 320 s += f'\t{attr} = {value},\n' 341 321 return s + '}' 342 322 343 - def build_c_string(self) -> str: 323 + def build_c_string(self, metric: bool) -> str: 344 324 s = '' 345 - for attr in _json_event_attributes: 325 + for attr in _json_metric_attributes if metric else _json_event_attributes: 346 326 x = getattr(self, attr) 347 - if x and attr == 'metric_expr': 327 + if metric and x and attr == 'metric_expr': 348 328 # Convert parsed metric expressions into a string. Slashes 349 329 # must be doubled in the file. 350 330 x = x.ToPerfJson().replace('\\', '\\\\') 351 331 s += f'{x}\\000' if x else '\\000' 352 332 return s 353 333 354 - def to_c_string(self) -> str: 334 + def to_c_string(self, metric: bool) -> str: 355 335 """Representation of the event as a C struct initializer.""" 356 336 357 - s = self.build_c_string() 337 + s = self.build_c_string(metric) 358 338 return f'{{ { _bcs.offsets[s] } }}, /* {s} */\n' 359 339 360 340 341 + @lru_cache(maxsize=None) 361 342 def read_json_events(path: str, topic: str) -> Sequence[JsonEvent]: 362 343 """Read json events from the specified file.""" 363 - 364 344 try: 365 - result = json.load(open(path), object_hook=JsonEvent) 345 + events = json.load(open(path), object_hook=JsonEvent) 366 346 except BaseException as err: 367 347 print(f"Exception processing {path}") 368 348 raise 369 - for event in result: 349 + metrics: list[Tuple[str, metric.Expression]] = [] 350 + for event in events: 370 351 event.topic = topic 371 - return result 352 + if event.metric_name and '-' not in event.metric_name: 353 + metrics.append((event.metric_name, event.metric_expr)) 354 + updates = metric.RewriteMetricsInTermsOfOthers(metrics) 355 + if updates: 356 + for event in events: 357 + if event.metric_name in updates: 358 + # print(f'Updated {event.metric_name} from\n"{event.metric_expr}"\n' 359 + # f'to\n"{updates[event.metric_name]}"') 360 + event.metric_expr = updates[event.metric_name] 361 + 362 + return events 372 363 373 364 def preprocess_arch_std_files(archpath: str) -> None: 374 365 """Read in all architecture standard events.""" ··· 389 358 for event in read_json_events(item.path, topic=''): 390 359 if event.name: 391 360 _arch_std_events[event.name.lower()] = event 392 - 393 - 394 - def print_events_table_prefix(tblname: str) -> None: 395 - """Called when a new events table is started.""" 396 - global _close_table 397 - if _close_table: 398 - raise IOError('Printing table prefix but last table has no suffix') 399 - _args.output_file.write(f'static const struct compact_pmu_event {tblname}[] = {{\n') 400 - _close_table = True 361 + if event.metric_name: 362 + _arch_std_events[event.metric_name.lower()] = event 401 363 402 364 403 365 def add_events_table_entries(item: os.DirEntry, topic: str) -> None: 404 366 """Add contents of file to _pending_events table.""" 405 - if not _close_table: 406 - raise IOError('Table entries missing prefix') 407 367 for e in read_json_events(item.path, topic): 408 - _pending_events.append(e) 368 + if e.name: 369 + _pending_events.append(e) 370 + if e.metric_name: 371 + _pending_metrics.append(e) 409 372 410 373 411 - def print_events_table_suffix() -> None: 374 + def print_pending_events() -> None: 412 375 """Optionally close events table.""" 413 376 414 377 def event_cmp_key(j: JsonEvent) -> Tuple[bool, str, str, str, str]: ··· 414 389 return (j.desc is not None, fix_none(j.topic), fix_none(j.name), fix_none(j.pmu), 415 390 fix_none(j.metric_name)) 416 391 417 - global _close_table 418 - if not _close_table: 392 + global _pending_events 393 + if not _pending_events: 419 394 return 420 395 421 - global _pending_events 396 + global _pending_events_tblname 397 + if _pending_events_tblname.endswith('_sys'): 398 + global _sys_event_tables 399 + _sys_event_tables.append(_pending_events_tblname) 400 + else: 401 + global event_tables 402 + _event_tables.append(_pending_events_tblname) 403 + 404 + _args.output_file.write( 405 + f'static const struct compact_pmu_event {_pending_events_tblname}[] = {{\n') 406 + 422 407 for event in sorted(_pending_events, key=event_cmp_key): 423 - _args.output_file.write(event.to_c_string()) 424 - _pending_events = [] 408 + _args.output_file.write(event.to_c_string(metric=False)) 409 + _pending_events = [] 425 410 426 411 _args.output_file.write('};\n\n') 427 - _close_table = False 412 + 413 + def print_pending_metrics() -> None: 414 + """Optionally close metrics table.""" 415 + 416 + def metric_cmp_key(j: JsonEvent) -> Tuple[bool, str, str]: 417 + def fix_none(s: Optional[str]) -> str: 418 + if s is None: 419 + return '' 420 + return s 421 + 422 + return (j.desc is not None, fix_none(j.pmu), fix_none(j.metric_name)) 423 + 424 + global _pending_metrics 425 + if not _pending_metrics: 426 + return 427 + 428 + global _pending_metrics_tblname 429 + if _pending_metrics_tblname.endswith('_sys'): 430 + global _sys_metric_tables 431 + _sys_metric_tables.append(_pending_metrics_tblname) 432 + else: 433 + global metric_tables 434 + _metric_tables.append(_pending_metrics_tblname) 435 + 436 + _args.output_file.write( 437 + f'static const struct compact_pmu_event {_pending_metrics_tblname}[] = {{\n') 438 + 439 + for metric in sorted(_pending_metrics, key=metric_cmp_key): 440 + _args.output_file.write(metric.to_c_string(metric=True)) 441 + _pending_metrics = [] 442 + 443 + _args.output_file.write('};\n\n') 428 444 429 445 def get_topic(topic: str) -> str: 430 446 if topic.endswith('metrics.json'): ··· 489 423 490 424 topic = get_topic(item.name) 491 425 for event in read_json_events(item.path, topic): 492 - _bcs.add(event.build_c_string()) 426 + if event.name: 427 + _bcs.add(event.build_c_string(metric=False)) 428 + if event.metric_name: 429 + _bcs.add(event.build_c_string(metric=True)) 493 430 494 431 def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None: 495 432 """Process a JSON file during the main walk.""" 496 - global _sys_event_tables 497 - 498 433 def is_leaf_dir(path: str) -> bool: 499 434 for item in os.scandir(path): 500 435 if item.is_dir(): ··· 504 437 505 438 # model directory, reset topic 506 439 if item.is_dir() and is_leaf_dir(item.path): 507 - print_events_table_suffix() 440 + print_pending_events() 441 + print_pending_metrics() 508 442 509 - tblname = file_name_to_table_name(parents, item.name) 443 + global _pending_events_tblname 444 + _pending_events_tblname = file_name_to_table_name('pmu_events_', parents, item.name) 445 + global _pending_metrics_tblname 446 + _pending_metrics_tblname = file_name_to_table_name('pmu_metrics_', parents, item.name) 447 + 510 448 if item.name == 'sys': 511 - _sys_event_tables.append(tblname) 512 - print_events_table_prefix(tblname) 449 + _sys_event_table_to_metric_table_mapping[_pending_events_tblname] = _pending_metrics_tblname 513 450 return 514 451 515 452 # base dir or too deep ··· 538 467 size_t length; 539 468 }; 540 469 470 + /* Struct used to make the PMU metric table implementation opaque to callers. */ 471 + struct pmu_metrics_table { 472 + const struct compact_pmu_event *entries; 473 + size_t length; 474 + }; 475 + 541 476 /* 542 477 * Map a CPU to its table of PMU events. The CPU is identified by the 543 478 * cpuid field, which is an arch-specific identifier for the CPU. ··· 555 478 struct pmu_events_map { 556 479 const char *arch; 557 480 const char *cpuid; 558 - struct pmu_events_table table; 481 + struct pmu_events_table event_table; 482 + struct pmu_metrics_table metric_table; 559 483 }; 560 484 561 485 /* ··· 570 492 _args.output_file.write("""{ 571 493 \t.arch = "testarch", 572 494 \t.cpuid = "testcpu", 573 - \t.table = { 574 - \t.entries = pme_test_soc_cpu, 575 - \t.length = ARRAY_SIZE(pme_test_soc_cpu), 495 + \t.event_table = { 496 + \t\t.entries = pmu_events__test_soc_cpu, 497 + \t\t.length = ARRAY_SIZE(pmu_events__test_soc_cpu), 498 + \t}, 499 + \t.metric_table = { 500 + \t\t.entries = pmu_metrics__test_soc_cpu, 501 + \t\t.length = ARRAY_SIZE(pmu_metrics__test_soc_cpu), 576 502 \t} 577 503 }, 578 504 """) ··· 587 505 for row in table: 588 506 # Skip the first row or any row beginning with #. 589 507 if not first and len(row) > 0 and not row[0].startswith('#'): 590 - tblname = file_name_to_table_name([], row[2].replace('/', '_')) 508 + event_tblname = file_name_to_table_name('pmu_events_', [], row[2].replace('/', '_')) 509 + if event_tblname in _event_tables: 510 + event_size = f'ARRAY_SIZE({event_tblname})' 511 + else: 512 + event_tblname = 'NULL' 513 + event_size = '0' 514 + metric_tblname = file_name_to_table_name('pmu_metrics_', [], row[2].replace('/', '_')) 515 + if metric_tblname in _metric_tables: 516 + metric_size = f'ARRAY_SIZE({metric_tblname})' 517 + else: 518 + metric_tblname = 'NULL' 519 + metric_size = '0' 520 + if event_size == '0' and metric_size == '0': 521 + continue 591 522 cpuid = row[0].replace('\\', '\\\\') 592 523 _args.output_file.write(f"""{{ 593 524 \t.arch = "{arch}", 594 525 \t.cpuid = "{cpuid}", 595 - \t.table = {{ 596 - \t\t.entries = {tblname}, 597 - \t\t.length = ARRAY_SIZE({tblname}) 526 + \t.event_table = {{ 527 + \t\t.entries = {event_tblname}, 528 + \t\t.length = {event_size} 529 + \t}}, 530 + \t.metric_table = {{ 531 + \t\t.entries = {metric_tblname}, 532 + \t\t.length = {metric_size} 598 533 \t}} 599 534 }}, 600 535 """) ··· 620 521 _args.output_file.write("""{ 621 522 \t.arch = 0, 622 523 \t.cpuid = 0, 623 - \t.table = { 0, 0 }, 524 + \t.event_table = { 0, 0 }, 525 + \t.metric_table = { 0, 0 }, 624 526 } 625 527 }; 626 528 """) ··· 632 532 _args.output_file.write(""" 633 533 struct pmu_sys_events { 634 534 \tconst char *name; 635 - \tstruct pmu_events_table table; 535 + \tstruct pmu_events_table event_table; 536 + \tstruct pmu_metrics_table metric_table; 636 537 }; 637 538 638 539 static const struct pmu_sys_events pmu_sys_event_tables[] = { 639 540 """) 541 + printed_metric_tables = [] 640 542 for tblname in _sys_event_tables: 641 543 _args.output_file.write(f"""\t{{ 642 - \t\t.table = {{ 544 + \t\t.event_table = {{ 545 + \t\t\t.entries = {tblname}, 546 + \t\t\t.length = ARRAY_SIZE({tblname}) 547 + \t\t}},""") 548 + metric_tblname = _sys_event_table_to_metric_table_mapping[tblname] 549 + if metric_tblname in _sys_metric_tables: 550 + _args.output_file.write(f""" 551 + \t\t.metric_table = {{ 552 + \t\t\t.entries = {metric_tblname}, 553 + \t\t\t.length = ARRAY_SIZE({metric_tblname}) 554 + \t\t}},""") 555 + printed_metric_tables.append(metric_tblname) 556 + _args.output_file.write(f""" 557 + \t\t.name = \"{tblname}\", 558 + \t}}, 559 + """) 560 + for tblname in _sys_metric_tables: 561 + if tblname in printed_metric_tables: 562 + continue 563 + _args.output_file.write(f"""\t{{ 564 + \t\t.metric_table = {{ 643 565 \t\t\t.entries = {tblname}, 644 566 \t\t\t.length = ARRAY_SIZE({tblname}) 645 567 \t\t}}, ··· 669 547 \t}}, 670 548 """) 671 549 _args.output_file.write("""\t{ 672 - \t\t.table = { 0, 0 } 550 + \t\t.event_table = { 0, 0 }, 551 + \t\t.metric_table = { 0, 0 }, 673 552 \t}, 674 553 }; 675 554 676 - static void decompress(int offset, struct pmu_event *pe) 555 + static void decompress_event(int offset, struct pmu_event *pe) 677 556 { 678 557 \tconst char *p = &big_c_string[offset]; 679 558 """) ··· 687 564 _args.output_file.write('\twhile (*p++);') 688 565 _args.output_file.write("""} 689 566 567 + static void decompress_metric(int offset, struct pmu_metric *pm) 568 + { 569 + \tconst char *p = &big_c_string[offset]; 570 + """) 571 + for attr in _json_metric_attributes: 572 + _args.output_file.write(f""" 573 + \tpm->{attr} = (*p == '\\0' ? NULL : p); 574 + """) 575 + if attr == _json_metric_attributes[-1]: 576 + continue 577 + _args.output_file.write('\twhile (*p++);') 578 + _args.output_file.write("""} 579 + 690 580 int pmu_events_table_for_each_event(const struct pmu_events_table *table, 691 581 pmu_event_iter_fn fn, 692 582 void *data) ··· 708 572 struct pmu_event pe; 709 573 int ret; 710 574 711 - decompress(table->entries[i].offset, &pe); 575 + decompress_event(table->entries[i].offset, &pe); 576 + if (!pe.name) 577 + continue; 712 578 ret = fn(&pe, table, data); 713 579 if (ret) 714 580 return ret; ··· 718 580 return 0; 719 581 } 720 582 721 - const struct pmu_events_table *perf_pmu__find_table(struct perf_pmu *pmu) 583 + int pmu_metrics_table_for_each_metric(const struct pmu_metrics_table *table, 584 + pmu_metric_iter_fn fn, 585 + void *data) 586 + { 587 + for (size_t i = 0; i < table->length; i++) { 588 + struct pmu_metric pm; 589 + int ret; 590 + 591 + decompress_metric(table->entries[i].offset, &pm); 592 + if (!pm.metric_expr) 593 + continue; 594 + ret = fn(&pm, table, data); 595 + if (ret) 596 + return ret; 597 + } 598 + return 0; 599 + } 600 + 601 + const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu) 722 602 { 723 603 const struct pmu_events_table *table = NULL; 724 604 char *cpuid = perf_pmu__getcpuid(pmu); ··· 755 599 break; 756 600 757 601 if (!strcmp_cpuid_str(map->cpuid, cpuid)) { 758 - table = &map->table; 602 + table = &map->event_table; 603 + break; 604 + } 605 + } 606 + free(cpuid); 607 + return table; 608 + } 609 + 610 + const struct pmu_metrics_table *perf_pmu__find_metrics_table(struct perf_pmu *pmu) 611 + { 612 + const struct pmu_metrics_table *table = NULL; 613 + char *cpuid = perf_pmu__getcpuid(pmu); 614 + int i; 615 + 616 + /* on some platforms which uses cpus map, cpuid can be NULL for 617 + * PMUs other than CORE PMUs. 618 + */ 619 + if (!cpuid) 620 + return NULL; 621 + 622 + i = 0; 623 + for (;;) { 624 + const struct pmu_events_map *map = &pmu_events_map[i++]; 625 + if (!map->arch) 626 + break; 627 + 628 + if (!strcmp_cpuid_str(map->cpuid, cpuid)) { 629 + table = &map->metric_table; 759 630 break; 760 631 } 761 632 } ··· 796 613 tables->arch; 797 614 tables++) { 798 615 if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) 799 - return &tables->table; 616 + return &tables->event_table; 617 + } 618 + return NULL; 619 + } 620 + 621 + const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const char *cpuid) 622 + { 623 + for (const struct pmu_events_map *tables = &pmu_events_map[0]; 624 + tables->arch; 625 + tables++) { 626 + if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) 627 + return &tables->metric_table; 800 628 } 801 629 return NULL; 802 630 } ··· 817 623 for (const struct pmu_events_map *tables = &pmu_events_map[0]; 818 624 tables->arch; 819 625 tables++) { 820 - int ret = pmu_events_table_for_each_event(&tables->table, fn, data); 626 + int ret = pmu_events_table_for_each_event(&tables->event_table, fn, data); 627 + 628 + if (ret) 629 + return ret; 630 + } 631 + return 0; 632 + } 633 + 634 + int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data) 635 + { 636 + for (const struct pmu_events_map *tables = &pmu_events_map[0]; 637 + tables->arch; 638 + tables++) { 639 + int ret = pmu_metrics_table_for_each_metric(&tables->metric_table, fn, data); 821 640 822 641 if (ret) 823 642 return ret; ··· 844 637 tables->name; 845 638 tables++) { 846 639 if (!strcmp(tables->name, name)) 847 - return &tables->table; 640 + return &tables->event_table; 848 641 } 849 642 return NULL; 850 643 } ··· 854 647 for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 855 648 tables->name; 856 649 tables++) { 857 - int ret = pmu_events_table_for_each_event(&tables->table, fn, data); 650 + int ret = pmu_events_table_for_each_event(&tables->event_table, fn, data); 651 + 652 + if (ret) 653 + return ret; 654 + } 655 + return 0; 656 + } 657 + 658 + int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data) 659 + { 660 + for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; 661 + tables->name; 662 + tables++) { 663 + int ret = pmu_metrics_table_for_each_metric(&tables->metric_table, fn, data); 858 664 859 665 if (ret) 860 666 return ret; ··· 890 670 action: Callable[[Sequence[str], os.DirEntry], None]) -> None: 891 671 """Replicate the directory/file walking behavior of C's file tree walk.""" 892 672 for item in os.scandir(path): 673 + if _args.model != 'all' and item.is_dir(): 674 + # Check if the model matches one in _args.model. 675 + if len(parents) == _args.model.split(',')[0].count('/'): 676 + # We're testing the correct directory. 677 + item_path = '/'.join(parents) + ('/' if len(parents) > 0 else '') + item.name 678 + if 'test' not in item_path and item_path not in _args.model.split(','): 679 + continue 893 680 action(parents, item) 894 681 if item.is_dir(): 895 682 ftw(item.path, parents + [item.name], action) 896 683 897 684 ap = argparse.ArgumentParser() 898 685 ap.add_argument('arch', help='Architecture name like x86') 686 + ap.add_argument('model', help='''Select a model such as skylake to 687 + reduce the code size. Normally set to "all". For architectures like 688 + ARM64 with an implementor/model, the model must include the implementor 689 + such as "arm/cortex-a34".''', 690 + default='all') 899 691 ap.add_argument( 900 692 'starting_dir', 901 693 type=dir_path, ··· 953 721 for arch in archs: 954 722 arch_path = f'{_args.starting_dir}/{arch}' 955 723 ftw(arch_path, [], process_one_file) 956 - print_events_table_suffix() 724 + print_pending_events() 725 + print_pending_metrics() 957 726 958 727 print_mapping_table(archs) 959 728 print_system_mapping_table()
+75 -4
tools/perf/pmu-events/metric.py
··· 4 4 import decimal 5 5 import json 6 6 import re 7 - from typing import Dict, List, Optional, Set, Union 7 + from typing import Dict, List, Optional, Set, Tuple, Union 8 8 9 9 10 10 class Expression: ··· 24 24 25 25 def Equals(self, other) -> bool: 26 26 """Returns true when two expressions are the same.""" 27 + raise NotImplementedError() 28 + 29 + def Substitute(self, name: str, expression: 'Expression') -> 'Expression': 27 30 raise NotImplementedError() 28 31 29 32 def __str__(self) -> str: ··· 189 186 other.lhs) and self.rhs.Equals(other.rhs) 190 187 return False 191 188 189 + def Substitute(self, name: str, expression: Expression) -> Expression: 190 + if self.Equals(expression): 191 + return Event(name) 192 + lhs = self.lhs.Substitute(name, expression) 193 + rhs = None 194 + if self.rhs: 195 + rhs = self.rhs.Substitute(name, expression) 196 + return Operator(self.operator, lhs, rhs) 197 + 192 198 193 199 class Select(Expression): 194 200 """Represents a select ternary in the parse tree.""" ··· 237 225 other.false_val) and self.true_val.Equals(other.true_val) 238 226 return False 239 227 228 + def Substitute(self, name: str, expression: Expression) -> Expression: 229 + if self.Equals(expression): 230 + return Event(name) 231 + true_val = self.true_val.Substitute(name, expression) 232 + cond = self.cond.Substitute(name, expression) 233 + false_val = self.false_val.Substitute(name, expression) 234 + return Select(true_val, cond, false_val) 235 + 240 236 241 237 class Function(Expression): 242 238 """A function in an expression like min, max, d_ratio.""" ··· 281 261 282 262 def Equals(self, other: Expression) -> bool: 283 263 if isinstance(other, Function): 284 - return self.fn == other.fn and self.lhs.Equals( 285 - other.lhs) and self.rhs.Equals(other.rhs) 264 + result = self.fn == other.fn and self.lhs.Equals(other.lhs) 265 + if self.rhs: 266 + result = result and self.rhs.Equals(other.rhs) 267 + return result 286 268 return False 269 + 270 + def Substitute(self, name: str, expression: Expression) -> Expression: 271 + if self.Equals(expression): 272 + return Event(name) 273 + lhs = self.lhs.Substitute(name, expression) 274 + rhs = None 275 + if self.rhs: 276 + rhs = self.rhs.Substitute(name, expression) 277 + return Function(self.fn, lhs, rhs) 287 278 288 279 289 280 def _FixEscapes(s: str) -> str: ··· 322 291 def Equals(self, other: Expression) -> bool: 323 292 return isinstance(other, Event) and self.name == other.name 324 293 294 + def Substitute(self, name: str, expression: Expression) -> Expression: 295 + return self 296 + 325 297 326 298 class Constant(Expression): 327 299 """A constant within the expression tree.""" ··· 349 315 def Equals(self, other: Expression) -> bool: 350 316 return isinstance(other, Constant) and self.value == other.value 351 317 318 + def Substitute(self, name: str, expression: Expression) -> Expression: 319 + return self 320 + 352 321 353 322 class Literal(Expression): 354 323 """A runtime literal within the expression tree.""" ··· 370 333 371 334 def Equals(self, other: Expression) -> bool: 372 335 return isinstance(other, Literal) and self.value == other.value 336 + 337 + def Substitute(self, name: str, expression: Expression) -> Expression: 338 + return self 373 339 374 340 375 341 def min(lhs: Union[int, float, Expression], rhs: Union[int, float, ··· 499 459 500 460 501 461 class _RewriteIfExpToSelect(ast.NodeTransformer): 462 + """Transformer to convert if-else nodes to Select expressions.""" 502 463 503 464 def visit_IfExp(self, node): 504 465 # pylint: disable=invalid-name ··· 537 496 for kw in keywords: 538 497 py = re.sub(rf'Event\(r"{kw}"\)', kw, py) 539 498 540 - parsed = ast.parse(py, mode='eval') 499 + try: 500 + parsed = ast.parse(py, mode='eval') 501 + except SyntaxError as e: 502 + raise SyntaxError(f'Parsing expression:\n{orig}') from e 541 503 _RewriteIfExpToSelect().visit(parsed) 542 504 parsed = ast.fix_missing_locations(parsed) 543 505 return _Constify(eval(compile(parsed, orig, 'eval'))) 506 + 507 + 508 + def RewriteMetricsInTermsOfOthers(metrics: List[Tuple[str, Expression]] 509 + )-> Dict[str, Expression]: 510 + """Shorten metrics by rewriting in terms of others. 511 + 512 + Args: 513 + metrics (list): pairs of metric names and their expressions. 514 + Returns: 515 + Dict: mapping from a metric name to a shortened expression. 516 + """ 517 + updates: Dict[str, Expression] = dict() 518 + for outer_name, outer_expression in metrics: 519 + updated = outer_expression 520 + while True: 521 + for inner_name, inner_expression in metrics: 522 + if inner_name.lower() == outer_name.lower(): 523 + continue 524 + if inner_name in updates: 525 + inner_expression = updates[inner_name] 526 + updated = updated.Substitute(inner_name, inner_expression) 527 + if updated.Equals(outer_expression): 528 + break 529 + if outer_name in updates and updated.Equals(updates[outer_name]): 530 + break 531 + updates[outer_name] = updated 532 + return updates
+13 -2
tools/perf/pmu-events/metric_test.py
··· 1 + #!/usr/bin/env python3 1 2 # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 3 import unittest 3 4 from metric import Constant 4 5 from metric import Event 6 + from metric import Expression 5 7 from metric import ParsePerfJson 8 + from metric import RewriteMetricsInTermsOfOthers 6 9 7 10 8 11 class TestMetricExpressions(unittest.TestCase): ··· 90 87 after = r'min((a + b if c > 1 else c + d), e + f)' 91 88 self.assertEqual(ParsePerfJson(before).ToPerfJson(), after) 92 89 93 - before =3D r'a if b else c if d else e' 94 - after =3D r'(a if b else (c if d else e))' 90 + before = r'a if b else c if d else e' 91 + after = r'(a if b else (c if d else e))' 95 92 self.assertEqual(ParsePerfJson(before).ToPerfJson(), after) 96 93 97 94 def test_ToPython(self): ··· 155 152 before = '0 * SLOTS' 156 153 after = '0 * SLOTS' 157 154 self.assertEqual(ParsePerfJson(before).Simplify().ToPerfJson(), after) 155 + 156 + def test_RewriteMetricsInTermsOfOthers(self): 157 + Expression.__eq__ = lambda e1, e2: e1.Equals(e2) 158 + before = [('m1', ParsePerfJson('a + b + c + d')), 159 + ('m2', ParsePerfJson('a + b + c'))] 160 + after = {'m1': ParsePerfJson('m2 + d')} 161 + self.assertEqual(RewriteMetricsInTermsOfOthers(before), after) 162 + Expression.__eq__ = None 158 163 159 164 if __name__ == '__main__': 160 165 unittest.main()
+23 -3
tools/perf/pmu-events/pmu-events.h
··· 23 23 const char *unit; 24 24 const char *perpkg; 25 25 const char *aggr_mode; 26 - const char *metric_expr; 26 + const char *deprecated; 27 + }; 28 + 29 + struct pmu_metric { 27 30 const char *metric_name; 28 31 const char *metric_group; 29 - const char *deprecated; 32 + const char *metric_expr; 33 + const char *unit; 34 + const char *compat; 35 + const char *aggr_mode; 30 36 const char *metric_constraint; 37 + const char *desc; 38 + const char *long_desc; 31 39 }; 32 40 33 41 struct pmu_events_table; 42 + struct pmu_metrics_table; 34 43 35 44 typedef int (*pmu_event_iter_fn)(const struct pmu_event *pe, 36 45 const struct pmu_events_table *table, 37 46 void *data); 38 47 48 + typedef int (*pmu_metric_iter_fn)(const struct pmu_metric *pm, 49 + const struct pmu_metrics_table *table, 50 + void *data); 51 + 39 52 int pmu_events_table_for_each_event(const struct pmu_events_table *table, pmu_event_iter_fn fn, 40 53 void *data); 54 + int pmu_metrics_table_for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn, 55 + void *data); 41 56 42 - const struct pmu_events_table *perf_pmu__find_table(struct perf_pmu *pmu); 57 + const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu); 58 + const struct pmu_metrics_table *perf_pmu__find_metrics_table(struct perf_pmu *pmu); 43 59 const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid); 60 + const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const char *cpuid); 44 61 int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data); 62 + int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data); 45 63 46 64 const struct pmu_events_table *find_sys_events_table(const char *name); 65 + const struct pmu_metrics_table *find_sys_metrics_table(const char *name); 47 66 int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data); 67 + int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data); 48 68 49 69 #endif
+85 -22
tools/perf/scripts/python/flamegraph.py
··· 19 19 # pylint: disable=missing-function-docstring 20 20 21 21 from __future__ import print_function 22 - import sys 23 - import os 24 - import io 25 22 import argparse 23 + import hashlib 24 + import io 26 25 import json 26 + import os 27 27 import subprocess 28 + import sys 29 + import urllib.request 30 + 31 + minimal_html = """<head> 32 + <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/d3-flamegraph.css"> 33 + </head> 34 + <body> 35 + <div id="chart"></div> 36 + <script type="text/javascript" src="https://d3js.org/d3.v7.js"></script> 37 + <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/d3-flamegraph.min.js"></script> 38 + <script type="text/javascript"> 39 + const stacks = [/** @flamegraph_json **/]; 40 + // Note, options is unused. 41 + const options = [/** @options_json **/]; 42 + 43 + var chart = flamegraph(); 44 + d3.select("#chart") 45 + .datum(stacks[0]) 46 + .call(chart); 47 + </script> 48 + </body> 49 + """ 28 50 29 51 # pylint: disable=too-few-public-methods 30 52 class Node: ··· 71 49 def __init__(self, args): 72 50 self.args = args 73 51 self.stack = Node("all", "root") 74 - 75 - if self.args.format == "html" and \ 76 - not os.path.isfile(self.args.template): 77 - print("Flame Graph template {} does not exist. Please install " 78 - "the js-d3-flame-graph (RPM) or libjs-d3-flame-graph (deb) " 79 - "package, specify an existing flame graph template " 80 - "(--template PATH) or another output format " 81 - "(--format FORMAT).".format(self.args.template), 82 - file=sys.stderr) 83 - sys.exit(1) 84 52 85 53 @staticmethod 86 54 def get_libtype_from_dso(dso): ··· 140 128 } 141 129 options_json = json.dumps(options) 142 130 131 + template_md5sum = None 132 + if self.args.format == "html": 133 + if os.path.isfile(self.args.template): 134 + template = f"file://{self.args.template}" 135 + else: 136 + if not self.args.allow_download: 137 + print(f"""Warning: Flame Graph template '{self.args.template}' 138 + does not exist. To avoid this please install a package such as the 139 + js-d3-flame-graph or libjs-d3-flame-graph, specify an existing flame 140 + graph template (--template PATH) or use another output format (--format 141 + FORMAT).""", 142 + file=sys.stderr) 143 + if self.args.input == "-": 144 + print("""Not attempting to download Flame Graph template as script command line 145 + input is disabled due to using live mode. If you want to download the 146 + template retry without live mode. For example, use 'perf record -a -g 147 + -F 99 sleep 60' and 'perf script report flamegraph'. Alternatively, 148 + download the template from: 149 + https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flamegraph-base.html 150 + and place it at: 151 + /usr/share/d3-flame-graph/d3-flamegraph-base.html""", 152 + file=sys.stderr) 153 + quit() 154 + s = None 155 + while s != "y" and s != "n": 156 + s = input("Do you wish to download a template from cdn.jsdelivr.net? (this warning can be suppressed with --allow-download) [yn] ").lower() 157 + if s == "n": 158 + quit() 159 + template = "https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/d3-flamegraph-base.html" 160 + template_md5sum = "143e0d06ba69b8370b9848dcd6ae3f36" 161 + 143 162 try: 144 - with io.open(self.args.template, encoding="utf-8") as template: 145 - output_str = ( 146 - template.read() 147 - .replace("/** @options_json **/", options_json) 148 - .replace("/** @flamegraph_json **/", stacks_json) 149 - ) 150 - except IOError as err: 151 - print("Error reading template file: {}".format(err), file=sys.stderr) 152 - sys.exit(1) 163 + with urllib.request.urlopen(template) as template: 164 + output_str = "".join([ 165 + l.decode("utf-8") for l in template.readlines() 166 + ]) 167 + except Exception as err: 168 + print(f"Error reading template {template}: {err}\n" 169 + "a minimal flame graph will be generated", file=sys.stderr) 170 + output_str = minimal_html 171 + template_md5sum = None 172 + 173 + if template_md5sum: 174 + download_md5sum = hashlib.md5(output_str.encode("utf-8")).hexdigest() 175 + if download_md5sum != template_md5sum: 176 + s = None 177 + while s != "y" and s != "n": 178 + s = input(f"""Unexpected template md5sum. 179 + {download_md5sum} != {template_md5sum}, for: 180 + {output_str} 181 + continue?[yn] """).lower() 182 + if s == "n": 183 + quit() 184 + 185 + output_str = output_str.replace("/** @options_json **/", options_json) 186 + output_str = output_str.replace("/** @flamegraph_json **/", stacks_json) 187 + 153 188 output_fn = self.args.output or "flamegraph.html" 154 189 else: 155 190 output_str = stacks_json ··· 231 172 choices=["blue-green", "orange"]) 232 173 parser.add_argument("-i", "--input", 233 174 help=argparse.SUPPRESS) 175 + parser.add_argument("--allow-download", 176 + default=False, 177 + action="store_true", 178 + help="allow unprompted downloading of HTML template") 234 179 235 180 cli_args = parser.parse_args() 236 181 cli = FlameGraphCLI(cli_args)
+1
tools/perf/tests/Build
··· 68 68 perf-y += dlfilter-test.o 69 69 perf-y += sigtrap.o 70 70 perf-y += event_groups.o 71 + perf-y += symbols.o 71 72 72 73 $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build 73 74 $(call rule_mkdir)
+1 -1
tools/perf/tests/bpf-script-example.c
··· 43 43 __type(value, int); 44 44 } flip_table SEC(".maps"); 45 45 46 - SEC("func=do_epoll_wait") 46 + SEC("syscalls:sys_enter_epoll_pwait") 47 47 int bpf_func__SyS_epoll_pwait(void *ctx) 48 48 { 49 49 int ind =0;
+16 -12
tools/perf/tests/bpf.c
··· 23 23 #define NR_ITERS 111 24 24 #define PERF_TEST_BPF_PATH "/sys/fs/bpf/perf_test" 25 25 26 - #ifdef HAVE_LIBBPF_SUPPORT 26 + #if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_LIBTRACEEVENT) 27 27 #include <linux/bpf.h> 28 28 #include <bpf/bpf.h> 29 29 ··· 126 126 127 127 err = parse_events_load_bpf_obj(&parse_state, &parse_state.list, obj, NULL); 128 128 parse_events_error__exit(&parse_error); 129 + if (err == -ENODATA) { 130 + pr_debug("Failed to add events selected by BPF, debuginfo package not installed\n"); 131 + return TEST_SKIP; 132 + } 129 133 if (err || list_empty(&parse_state.list)) { 130 134 pr_debug("Failed to add events selected by BPF\n"); 131 135 return TEST_FAIL; ··· 334 330 static int test__basic_bpf_test(struct test_suite *test __maybe_unused, 335 331 int subtest __maybe_unused) 336 332 { 337 - #ifdef HAVE_LIBBPF_SUPPORT 333 + #if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_LIBTRACEEVENT) 338 334 return test__bpf(0); 339 335 #else 340 - pr_debug("Skip BPF test because BPF support is not compiled\n"); 336 + pr_debug("Skip BPF test because BPF or libtraceevent support is not compiled\n"); 341 337 return TEST_SKIP; 342 338 #endif 343 339 } ··· 345 341 static int test__bpf_pinning(struct test_suite *test __maybe_unused, 346 342 int subtest __maybe_unused) 347 343 { 348 - #ifdef HAVE_LIBBPF_SUPPORT 344 + #if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_LIBTRACEEVENT) 349 345 return test__bpf(1); 350 346 #else 351 - pr_debug("Skip BPF test because BPF support is not compiled\n"); 347 + pr_debug("Skip BPF test because BPF or libtraceevent support is not compiled\n"); 352 348 return TEST_SKIP; 353 349 #endif 354 350 } ··· 356 352 static int test__bpf_prologue_test(struct test_suite *test __maybe_unused, 357 353 int subtest __maybe_unused) 358 354 { 359 - #if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_BPF_PROLOGUE) 355 + #if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_BPF_PROLOGUE) && defined(HAVE_LIBTRACEEVENT) 360 356 return test__bpf(2); 361 357 #else 362 - pr_debug("Skip BPF test because BPF support is not compiled\n"); 358 + pr_debug("Skip BPF test because BPF or libtraceevent support is not compiled\n"); 363 359 return TEST_SKIP; 364 360 #endif 365 361 } 366 362 367 363 368 364 static struct test_case bpf_tests[] = { 369 - #ifdef HAVE_LIBBPF_SUPPORT 365 + #if defined(HAVE_LIBBPF_SUPPORT) && defined(HAVE_LIBTRACEEVENT) 370 366 TEST_CASE("Basic BPF filtering", basic_bpf_test), 371 367 TEST_CASE_REASON("BPF pinning", bpf_pinning, 372 368 "clang isn't installed or environment missing BPF support"), 373 369 #ifdef HAVE_BPF_PROLOGUE 374 370 TEST_CASE_REASON("BPF prologue generation", bpf_prologue_test, 375 - "clang isn't installed or environment missing BPF support"), 371 + "clang/debuginfo isn't installed or environment missing BPF support"), 376 372 #else 377 373 TEST_CASE_REASON("BPF prologue generation", bpf_prologue_test, "not compiled in"), 378 374 #endif 379 375 #else 380 - TEST_CASE_REASON("Basic BPF filtering", basic_bpf_test, "not compiled in"), 381 - TEST_CASE_REASON("BPF pinning", bpf_pinning, "not compiled in"), 382 - TEST_CASE_REASON("BPF prologue generation", bpf_prologue_test, "not compiled in"), 376 + TEST_CASE_REASON("Basic BPF filtering", basic_bpf_test, "not compiled in or missing libtraceevent support"), 377 + TEST_CASE_REASON("BPF pinning", bpf_pinning, "not compiled in or missing libtraceevent support"), 378 + TEST_CASE_REASON("BPF prologue generation", bpf_prologue_test, "not compiled in or missing libtraceevent support"), 383 379 #endif 384 380 { .name = NULL, } 385 381 };
+3
tools/perf/tests/builtin-test.c
··· 31 31 #include "builtin-test-list.h" 32 32 33 33 static bool dont_fork; 34 + const char *dso_to_test; 34 35 35 36 struct test_suite *__weak arch_tests[] = { 36 37 NULL, ··· 118 117 &suite__dlfilter, 119 118 &suite__sigtrap, 120 119 &suite__event_groups, 120 + &suite__symbols, 121 121 NULL, 122 122 }; 123 123 ··· 523 521 OPT_BOOLEAN('F', "dont-fork", &dont_fork, 524 522 "Do not fork for testcase"), 525 523 OPT_STRING('w', "workload", &workload, "work", "workload to run for testing"), 524 + OPT_STRING(0, "dso", &dso_to_test, "dso", "dso to test"), 526 525 OPT_END() 527 526 }; 528 527 const char * const test_subcommands[] = { "list", NULL };
+3 -2
tools/perf/tests/dwarf-unwind.c
··· 67 67 int test_dwarf_unwind__krava_3(struct thread *thread); 68 68 int test_dwarf_unwind__krava_2(struct thread *thread); 69 69 int test_dwarf_unwind__krava_1(struct thread *thread); 70 + int test__dwarf_unwind(struct test_suite *test, int subtest); 70 71 71 72 #define MAX_STACK 8 72 73 ··· 196 195 return ret; 197 196 } 198 197 199 - static int test__dwarf_unwind(struct test_suite *test __maybe_unused, 200 - int subtest __maybe_unused) 198 + noinline int test__dwarf_unwind(struct test_suite *test __maybe_unused, 199 + int subtest __maybe_unused) 201 200 { 202 201 struct machine *machine; 203 202 struct thread *thread;
+2 -2
tools/perf/tests/expand-cgroup.c
··· 180 180 struct evlist *evlist; 181 181 struct rblist metric_events; 182 182 const char metric_str[] = "CPI"; 183 - const struct pmu_events_table *pme_test; 183 + const struct pmu_metrics_table *pme_test; 184 184 185 185 evlist = evlist__new(); 186 186 TEST_ASSERT_VAL("failed to get evlist", evlist); 187 187 188 188 rblist__init(&metric_events); 189 - pme_test = find_core_events_table("testarch", "testcpu"); 189 + pme_test = find_core_metrics_table("testarch", "testcpu"); 190 190 ret = metricgroup__parse_groups_test(evlist, pme_test, metric_str, 191 191 false, false, &metric_events); 192 192 if (ret < 0) {
+2 -2
tools/perf/tests/parse-metric.c
··· 72 72 struct rblist metric_events = { 73 73 .nr_entries = 0, 74 74 }; 75 - const struct pmu_events_table *pme_test; 75 + const struct pmu_metrics_table *pme_test; 76 76 struct perf_cpu_map *cpus; 77 77 struct runtime_stat st; 78 78 struct evlist *evlist; ··· 96 96 runtime_stat__init(&st); 97 97 98 98 /* Parse the metric into metric_events list. */ 99 - pme_test = find_core_events_table("testarch", "testcpu"); 99 + pme_test = find_core_metrics_table("testarch", "testcpu"); 100 100 err = metricgroup__parse_groups_test(evlist, pme_test, name, 101 101 false, false, 102 102 &metric_events);
+21 -48
tools/perf/tests/pmu-events.c
··· 337 337 return -1; 338 338 } 339 339 340 - if (!is_same(e1->metric_expr, e2->metric_expr)) { 341 - pr_debug2("testing event e1 %s: mismatched metric_expr, %s vs %s\n", 342 - e1->name, e1->metric_expr, e2->metric_expr); 343 - return -1; 344 - } 345 - 346 - if (!is_same(e1->metric_name, e2->metric_name)) { 347 - pr_debug2("testing event e1 %s: mismatched metric_name, %s vs %s\n", 348 - e1->name, e1->metric_name, e2->metric_name); 349 - return -1; 350 - } 351 - 352 - if (!is_same(e1->metric_group, e2->metric_group)) { 353 - pr_debug2("testing event e1 %s: mismatched metric_group, %s vs %s\n", 354 - e1->name, e1->metric_group, e2->metric_group); 355 - return -1; 356 - } 357 - 358 340 if (!is_same(e1->deprecated, e2->deprecated)) { 359 341 pr_debug2("testing event e1 %s: mismatched deprecated, %s vs %s\n", 360 342 e1->name, e1->deprecated, e2->deprecated); 361 - return -1; 362 - } 363 - 364 - if (!is_same(e1->metric_constraint, e2->metric_constraint)) { 365 - pr_debug2("testing event e1 %s: mismatched metric_constant, %s vs %s\n", 366 - e1->name, e1->metric_constraint, e2->metric_constraint); 367 343 return -1; 368 344 } 369 345 ··· 408 432 struct perf_pmu_test_event const **test_event_table; 409 433 bool found = false; 410 434 411 - if (!pe->name) 412 - return 0; 413 - 414 435 if (pe->pmu) 415 436 test_event_table = &uncore_events[0]; 416 437 else ··· 469 496 static int test__pmu_event_table(struct test_suite *test __maybe_unused, 470 497 int subtest __maybe_unused) 471 498 { 472 - const struct pmu_events_table *sys_event_table = find_sys_events_table("pme_test_soc_sys"); 499 + const struct pmu_events_table *sys_event_table = 500 + find_sys_events_table("pmu_events__test_soc_sys"); 473 501 const struct pmu_events_table *table = find_core_events_table("testarch", "testcpu"); 474 502 int map_events = 0, expected_events, err; 475 503 ··· 814 840 struct metric_ref metric_ref; 815 841 }; 816 842 817 - static int test__parsing_callback(const struct pmu_event *pe, const struct pmu_events_table *table, 843 + static int test__parsing_callback(const struct pmu_metric *pm, 844 + const struct pmu_metrics_table *table, 818 845 void *data) 819 846 { 820 847 int *failures = data; ··· 829 854 }; 830 855 int err = 0; 831 856 832 - if (!pe->metric_expr) 857 + if (!pm->metric_expr) 833 858 return 0; 834 859 835 - pr_debug("Found metric '%s'\n", pe->metric_name); 860 + pr_debug("Found metric '%s'\n", pm->metric_name); 836 861 (*failures)++; 837 862 838 863 /* ··· 852 877 perf_evlist__set_maps(&evlist->core, cpus, NULL); 853 878 runtime_stat__init(&st); 854 879 855 - err = metricgroup__parse_groups_test(evlist, table, pe->metric_name, 880 + err = metricgroup__parse_groups_test(evlist, table, pm->metric_name, 856 881 false, false, 857 882 &metric_events); 858 883 if (err) { 859 - if (!strcmp(pe->metric_name, "M1") || !strcmp(pe->metric_name, "M2") || 860 - !strcmp(pe->metric_name, "M3")) { 884 + if (!strcmp(pm->metric_name, "M1") || !strcmp(pm->metric_name, "M2") || 885 + !strcmp(pm->metric_name, "M3")) { 861 886 (*failures)--; 862 - pr_debug("Expected broken metric %s skipping\n", pe->metric_name); 887 + pr_debug("Expected broken metric %s skipping\n", pm->metric_name); 863 888 err = 0; 864 889 } 865 890 goto out_err; ··· 887 912 struct metric_expr *mexp; 888 913 889 914 list_for_each_entry (mexp, &me->head, nd) { 890 - if (strcmp(mexp->metric_name, pe->metric_name)) 915 + if (strcmp(mexp->metric_name, pm->metric_name)) 891 916 continue; 892 917 pr_debug("Result %f\n", test_generic_metric(mexp, 0, &st)); 893 918 err = 0; ··· 896 921 } 897 922 } 898 923 } 899 - pr_debug("Didn't find parsed metric %s", pe->metric_name); 924 + pr_debug("Didn't find parsed metric %s", pm->metric_name); 900 925 err = 1; 901 926 out_err: 902 927 if (err) 903 - pr_debug("Broken metric %s\n", pe->metric_name); 928 + pr_debug("Broken metric %s\n", pm->metric_name); 904 929 905 930 /* ... cleanup. */ 906 931 metricgroup__rblist_exit(&metric_events); ··· 916 941 { 917 942 int failures = 0; 918 943 919 - pmu_for_each_core_event(test__parsing_callback, &failures); 920 - pmu_for_each_sys_event(test__parsing_callback, &failures); 944 + pmu_for_each_core_metric(test__parsing_callback, &failures); 945 + pmu_for_each_sys_metric(test__parsing_callback, &failures); 921 946 922 947 return failures == 0 ? TEST_OK : TEST_FAIL; 923 948 } ··· 950 975 pr_debug("expr__ctx_new failed"); 951 976 return TEST_FAIL; 952 977 } 978 + ctx->sctx.is_test = true; 953 979 if (expr__find_ids(str, NULL, ctx) < 0) { 954 980 pr_err("expr__find_ids failed\n"); 955 981 return -1; ··· 997 1021 return ret; 998 1022 } 999 1023 1000 - static int test__parsing_fake_callback(const struct pmu_event *pe, 1001 - const struct pmu_events_table *table __maybe_unused, 1024 + static int test__parsing_fake_callback(const struct pmu_metric *pm, 1025 + const struct pmu_metrics_table *table __maybe_unused, 1002 1026 void *data __maybe_unused) 1003 1027 { 1004 - if (!pe->metric_expr) 1005 - return 0; 1006 - 1007 - return metric_parse_fake(pe->metric_name, pe->metric_expr); 1028 + return metric_parse_fake(pm->metric_name, pm->metric_expr); 1008 1029 } 1009 1030 1010 1031 /* ··· 1020 1047 return err; 1021 1048 } 1022 1049 1023 - err = pmu_for_each_core_event(test__parsing_fake_callback, NULL); 1050 + err = pmu_for_each_core_metric(test__parsing_fake_callback, NULL); 1024 1051 if (err) 1025 1052 return err; 1026 1053 1027 - return pmu_for_each_sys_event(test__parsing_fake_callback, NULL); 1054 + return pmu_for_each_sys_metric(test__parsing_fake_callback, NULL); 1028 1055 } 1029 1056 1030 1057 static struct test_case pmu_events_tests[] = {
+1 -1
tools/perf/tests/sample-parsing.c
··· 37 37 * in branch_stack variable. 38 38 */ 39 39 #define BS_EXPECTED_BE 0xa000d00000000000 40 - #define BS_EXPECTED_LE 0xd5000000 40 + #define BS_EXPECTED_LE 0x1aa00000000 41 41 #define FLAG(s) s->branch_stack->entries[i].flags 42 42 43 43 static bool samples_same(const struct perf_sample *s1,
+11 -7
tools/perf/tests/shell/buildid.sh
··· 66 66 esac 67 67 echo "build id: ${id}" 68 68 69 - link=${build_id_dir}/.build-id/${id:0:2}/${id:2} 69 + id_file=${id#??} 70 + id_dir=${id%$id_file} 71 + link=$build_id_dir/.build-id/$id_dir/$id_file 70 72 echo "link: ${link}" 71 73 72 74 if [ ! -h $link ]; then ··· 76 74 exit 1 77 75 fi 78 76 79 - file=${build_id_dir}/.build-id/${id:0:2}/`readlink ${link}`/elf 77 + file=${build_id_dir}/.build-id/$id_dir/`readlink ${link}`/elf 80 78 echo "file: ${file}" 81 79 82 80 # Check for file permission of original file ··· 132 130 { 133 131 data=$(mktemp /tmp/perf.data.XXX) 134 132 build_id_dir=$(mktemp -d /tmp/perf.debug.XXX) 135 - log=$(mktemp /tmp/perf.log.XXX) 133 + log_out=$(mktemp /tmp/perf.log.out.XXX) 134 + log_err=$(mktemp /tmp/perf.log.err.XXX) 136 135 perf="perf --buildid-dir ${build_id_dir}" 137 136 138 137 echo "running: perf record $@" 139 - ${perf} record --buildid-all -o ${data} $@ &> ${log} 138 + ${perf} record --buildid-all -o ${data} $@ 1>${log_out} 2>${log_err} 140 139 if [ $? -ne 0 ]; then 141 140 echo "failed: record $@" 142 - echo "see log: ${log}" 141 + echo "see log: ${log_err}" 143 142 exit 1 144 143 fi 145 144 146 - check ${@: -1} 145 + args="$*" 146 + check ${args##* } 147 147 148 - rm -f ${log} 148 + rm -f ${log_out} ${log_err} 149 149 rm -rf ${build_id_dir} 150 150 rm -rf ${data} 151 151 }
+9 -9
tools/perf/tests/shell/lib/coresight.sh
··· 58 58 # compiler may produce different code depending on the compiler and 59 59 # optimization options, so this is rough just to see if we're 60 60 # either missing almost all the data or all of it 61 - ATOM_FX_NUM=`grep I_ATOM_F "$DUMP" | wc -l` 62 - ASYNC_NUM=`grep I_ASYNC "$DUMP" | wc -l` 63 - TRACE_INFO_NUM=`grep I_TRACE_INFO "$DUMP" | wc -l` 61 + ATOM_FX_NUM=$(grep -c I_ATOM_F "$DUMP") 62 + ASYNC_NUM=$(grep -c I_ASYNC "$DUMP") 63 + TRACE_INFO_NUM=$(grep -c I_TRACE_INFO "$DUMP") 64 64 rm -f "$DUMP" 65 65 66 66 # Arguments provide minimums for a pass ··· 96 96 97 97 # The TID test tools will print a TID per stdout line that are being 98 98 # tested 99 - TIDS=`cat "$2"` 99 + TIDS=$(cat "$2") 100 100 # Scan the perf report to find the TIDs that are actually CID in hex 101 101 # and build a list of the ones found 102 - FOUND_TIDS=`perf report --stdio --dump -i "$1" | \ 102 + FOUND_TIDS=$(perf report --stdio --dump -i "$1" | \ 103 103 grep -o "CID=0x[0-9a-z]\+" | sed 's/CID=//g' | \ 104 - uniq | sort | uniq` 104 + uniq | sort | uniq) 105 105 # No CID=xxx found - maybe your kernel is reporting these as 106 106 # VMID=xxx so look there 107 107 if test -z "$FOUND_TIDS"; then 108 - FOUND_TIDS=`perf report --stdio --dump -i "$1" | \ 108 + FOUND_TIDS=$(perf report --stdio --dump -i "$1" | \ 109 109 grep -o "VMID=0x[0-9a-z]\+" | sed 's/VMID=//g' | \ 110 - uniq | sort | uniq` 110 + uniq | sort | uniq) 111 111 fi 112 112 113 113 # Iterate over the list of TIDs that the test says it has and find ··· 116 116 for TID2 in $TIDS; do 117 117 FOUND="" 118 118 for TIDHEX in $FOUND_TIDS; do 119 - TID=`printf "%i" $TIDHEX` 119 + TID=$(printf "%i" $TIDHEX) 120 120 if test "$TID" -eq "$TID2"; then 121 121 FOUND="y" 122 122 break
+8
tools/perf/tests/shell/lib/probe_vfs_getname.sh
··· 22 22 add_probe_vfs_getname -v 2>&1 | grep -E -q "^(Failed to find the path for the kernel|Debuginfo-analysis is not supported)|(file has no debug information)" && return 2 23 23 return 1 24 24 } 25 + 26 + # check if perf is compiled with libtraceevent support 27 + skip_no_probe_record_support() { 28 + if [ $had_vfs_getname -eq 1 ] ; then 29 + perf record --dry-run -e $1 2>&1 | grep "libtraceevent is necessary for tracepoint support" && return 2 30 + return 1 31 + fi 32 + }
+62 -4
tools/perf/tests/shell/lock_contention.sh
··· 128 128 echo "Testing perf lock contention --type-filter (w/ spinlock)" 129 129 perf lock contention -i ${perfdata} -Y spinlock -q 2> ${result} 130 130 if [ $(grep -c -v spinlock "${result}") != "0" ]; then 131 - echo "[Fail] Recorded should not have non-spinlocks:" $(cat "${result}") 131 + echo "[Fail] Recorded result should not have non-spinlocks:" $(cat "${result}") 132 132 err=1 133 133 exit 134 134 fi ··· 139 139 140 140 perf lock con -a -b -Y spinlock -q -- perf bench sched messaging > /dev/null 2> ${result} 141 141 if [ $(grep -c -v spinlock "${result}") != "0" ]; then 142 - echo "[Fail] Recorded should not have non-spinlocks:" $(cat "${result}") 142 + echo "[Fail] BPF result should not have non-spinlocks:" $(cat "${result}") 143 143 err=1 144 144 exit 145 145 fi ··· 160 160 local type=$(head -1 "${result}" | awk '{ print $8 }' | sed -e 's/:.*//') 161 161 162 162 if [ $(grep -c -v "${type}" "${result}") != "0" ]; then 163 - echo "[Fail] Recorded should not have non-${type} locks:" $(cat "${result}") 163 + echo "[Fail] Recorded result should not have non-${type} locks:" $(cat "${result}") 164 164 err=1 165 165 exit 166 166 fi ··· 171 171 172 172 perf lock con -a -b -L tasklist_lock -q -- perf bench sched messaging > /dev/null 2> ${result} 173 173 if [ $(grep -c -v "${type}" "${result}") != "0" ]; then 174 - echo "[Fail] Recorded should not have non-${type} locks:" $(cat "${result}") 174 + echo "[Fail] BPF result should not have non-${type} locks:" $(cat "${result}") 175 + err=1 176 + exit 177 + fi 178 + } 179 + 180 + test_stack_filter() 181 + { 182 + echo "Testing perf lock contention --callstack-filter (w/ unix_stream)" 183 + perf lock contention -i ${perfdata} -v -q 2> ${result} 184 + if [ $(grep -c unix_stream "${result}") == "0" ]; then 185 + echo "[Skip] Could not find 'unix_stream'" 186 + return 187 + fi 188 + 189 + perf lock contention -i ${perfdata} -E 1 -S unix_stream -q 2> ${result} 190 + if [ $(cat "${result}" | wc -l) != "1" ]; then 191 + echo "[Fail] Recorded result should have a lock from unix_stream:" $(cat "${result}") 192 + err=1 193 + exit 194 + fi 195 + 196 + if ! perf lock con -b true > /dev/null 2>&1 ; then 197 + return 198 + fi 199 + 200 + perf lock con -a -b -S unix_stream -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result} 201 + if [ $(cat "${result}" | wc -l) != "1" ]; then 202 + echo "[Fail] BPF result should have a lock from unix_stream:" $(cat "${result}") 203 + err=1 204 + exit 205 + fi 206 + } 207 + 208 + test_aggr_task_stack_filter() 209 + { 210 + echo "Testing perf lock contention --callstack-filter with task aggregation" 211 + perf lock contention -i ${perfdata} -v -q 2> ${result} 212 + if [ $(grep -c unix_stream "${result}") == "0" ]; then 213 + echo "[Skip] Could not find 'unix_stream'" 214 + return 215 + fi 216 + 217 + perf lock contention -i ${perfdata} -t -E 1 -S unix_stream -q 2> ${result} 218 + if [ $(cat "${result}" | wc -l) != "1" ]; then 219 + echo "[Fail] Recorded result should have a task from unix_stream:" $(cat "${result}") 220 + err=1 221 + exit 222 + fi 223 + 224 + if ! perf lock con -b true > /dev/null 2>&1 ; then 225 + return 226 + fi 227 + 228 + perf lock con -a -b -t -S unix_stream -E 1 -q -- perf bench sched messaging > /dev/null 2> ${result} 229 + if [ $(cat "${result}" | wc -l) != "1" ]; then 230 + echo "[Fail] BPF result should have a task from unix_stream:" $(cat "${result}") 175 231 err=1 176 232 exit 177 233 fi ··· 242 186 test_aggr_addr 243 187 test_type_filter 244 188 test_lock_filter 189 + test_stack_filter 190 + test_aggr_task_stack_filter 245 191 246 192 exit ${err}
+11
tools/perf/tests/shell/record+probe_libc_inet_pton.sh
··· 11 11 # Arnaldo Carvalho de Melo <acme@kernel.org>, 2017 12 12 13 13 . $(dirname $0)/lib/probe.sh 14 + . $(dirname $0)/lib/probe_vfs_getname.sh 14 15 15 16 libc=$(grep -w libc /proc/self/maps | head -1 | sed -r 's/.*[[:space:]](\/.*)/\1/g') 16 17 nm -Dg $libc 2>/dev/null | fgrep -q inet_pton || exit 254 ··· 58 57 59 58 perf_data=`mktemp -u /tmp/perf.data.XXX` 60 59 perf_script=`mktemp -u /tmp/perf.script.XXX` 60 + 61 + # Check presence of libtraceevent support to run perf record 62 + skip_no_probe_record_support "$event_name/$eventattr/" 63 + [ $? -eq 2 ] && return 2 64 + 61 65 perf record -e $event_name/$eventattr/ -o $perf_data ping -6 -c 1 ::1 > /dev/null 2>&1 66 + # check if perf data file got created in above step. 67 + if [ ! -e $perf_data ]; then 68 + printf "FAIL: perf record failed to create \"%s\" \n" "$perf_data" 69 + return 1 70 + fi 62 71 perf script -i $perf_data | tac | grep -m1 ^ping -B9 | tac > $perf_script 63 72 64 73 exec 3<$perf_script
+3
tools/perf/tests/shell/record+script_probe_vfs_getname.sh
··· 17 17 18 18 record_open_file() { 19 19 echo "Recording open file:" 20 + # Check presence of libtraceevent support to run perf record 21 + skip_no_probe_record_support "probe:vfs_getname*" 22 + [ $? -eq 2 ] && return 2 20 23 perf record -o ${perfdata} -e probe:vfs_getname\* touch $file 21 24 } 22 25
+1 -1
tools/perf/tests/shell/stat_all_metrics.sh
··· 11 11 continue 12 12 fi 13 13 # Failed so try system wide. 14 - result=$(perf stat -M "$m" -a true 2>&1) 14 + result=$(perf stat -M "$m" -a sleep 0.01 2>&1) 15 15 if [[ "$result" =~ "${m:0:50}" ]] 16 16 then 17 17 continue
+9 -9
tools/perf/tests/shell/test_brstack.sh
··· 30 30 # brstack_foo+0x14/brstack_bar+0x40/P/-/-/0/CALL 31 31 32 32 set -x 33 - grep -E -m1 "^brstack_bench\+[^ ]*/brstack_foo\+[^ ]*/IND_CALL$" $TMPDIR/perf.script 34 - grep -E -m1 "^brstack_foo\+[^ ]*/brstack_bar\+[^ ]*/CALL$" $TMPDIR/perf.script 35 - grep -E -m1 "^brstack_bench\+[^ ]*/brstack_foo\+[^ ]*/CALL$" $TMPDIR/perf.script 36 - grep -E -m1 "^brstack_bench\+[^ ]*/brstack_bar\+[^ ]*/CALL$" $TMPDIR/perf.script 37 - grep -E -m1 "^brstack_bar\+[^ ]*/brstack_foo\+[^ ]*/RET$" $TMPDIR/perf.script 38 - grep -E -m1 "^brstack_foo\+[^ ]*/brstack_bench\+[^ ]*/RET$" $TMPDIR/perf.script 39 - grep -E -m1 "^brstack_bench\+[^ ]*/brstack_bench\+[^ ]*/COND$" $TMPDIR/perf.script 40 - grep -E -m1 "^brstack\+[^ ]*/brstack\+[^ ]*/UNCOND$" $TMPDIR/perf.script 33 + grep -E -m1 "^brstack_bench\+[^ ]*/brstack_foo\+[^ ]*/IND_CALL/.*$" $TMPDIR/perf.script 34 + grep -E -m1 "^brstack_foo\+[^ ]*/brstack_bar\+[^ ]*/CALL/.*$" $TMPDIR/perf.script 35 + grep -E -m1 "^brstack_bench\+[^ ]*/brstack_foo\+[^ ]*/CALL/.*$" $TMPDIR/perf.script 36 + grep -E -m1 "^brstack_bench\+[^ ]*/brstack_bar\+[^ ]*/CALL/.*$" $TMPDIR/perf.script 37 + grep -E -m1 "^brstack_bar\+[^ ]*/brstack_foo\+[^ ]*/RET/.*$" $TMPDIR/perf.script 38 + grep -E -m1 "^brstack_foo\+[^ ]*/brstack_bench\+[^ ]*/RET/.*$" $TMPDIR/perf.script 39 + grep -E -m1 "^brstack_bench\+[^ ]*/brstack_bench\+[^ ]*/COND/.*$" $TMPDIR/perf.script 40 + grep -E -m1 "^brstack\+[^ ]*/brstack\+[^ ]*/UNCOND/.*$" $TMPDIR/perf.script 41 41 set +x 42 42 43 43 # some branch types are still not being tested: ··· 57 57 58 58 # fail if we find any branch type that doesn't match any of the expected ones 59 59 # also consider UNKNOWN branch types (-) 60 - if grep -E -vm1 "^[^ ]*/($expect|-|( *))$" $TMPDIR/perf.script; then 60 + if grep -E -vm1 "^[^ ]*/($expect|-|( *))/.*$" $TMPDIR/perf.script; then 61 61 return 1 62 62 fi 63 63 }
+17
tools/perf/tests/shell/test_intel_pt.sh
··· 620 620 return 0 621 621 } 622 622 623 + test_pipe() 624 + { 625 + echo "--- Test with pipe mode ---" 626 + # Check if it works with pipe 627 + if ! perf_record_no_bpf -o- -e intel_pt//u uname | perf report -q -i- --itrace=i10000 ; then 628 + echo "perf record + report failed with pipe mode" 629 + return 1 630 + fi 631 + if ! perf_record_no_bpf -o- -e intel_pt//u uname | perf inject -b > /dev/null ; then 632 + echo "perf record + inject failed with pipe mode" 633 + return 1 634 + fi 635 + echo OK 636 + return 0 637 + } 638 + 623 639 count_result() 624 640 { 625 641 if [ "$1" -eq 2 ] ; then ··· 663 647 test_power_event || ret=$? ; count_result $ret ; ret=0 664 648 test_no_tnt || ret=$? ; count_result $ret ; ret=0 665 649 test_event_trace || ret=$? ; count_result $ret ; ret=0 650 + test_pipe || ret=$? ; count_result $ret ; ret=0 666 651 667 652 cleanup 668 653
+150
tools/perf/tests/symbols.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/compiler.h> 3 + #include <linux/string.h> 4 + #include <sys/mman.h> 5 + #include <limits.h> 6 + #include "debug.h" 7 + #include "dso.h" 8 + #include "machine.h" 9 + #include "thread.h" 10 + #include "symbol.h" 11 + #include "map.h" 12 + #include "util.h" 13 + #include "tests.h" 14 + 15 + struct test_info { 16 + struct machine *machine; 17 + struct thread *thread; 18 + }; 19 + 20 + static int init_test_info(struct test_info *ti) 21 + { 22 + ti->machine = machine__new_host(); 23 + if (!ti->machine) { 24 + pr_debug("machine__new_host() failed!\n"); 25 + return TEST_FAIL; 26 + } 27 + 28 + /* Create a dummy thread */ 29 + ti->thread = machine__findnew_thread(ti->machine, 100, 100); 30 + if (!ti->thread) { 31 + pr_debug("machine__findnew_thread() failed!\n"); 32 + return TEST_FAIL; 33 + } 34 + 35 + return TEST_OK; 36 + } 37 + 38 + static void exit_test_info(struct test_info *ti) 39 + { 40 + thread__put(ti->thread); 41 + machine__delete(ti->machine); 42 + } 43 + 44 + static void get_test_dso_filename(char *filename, size_t max_sz) 45 + { 46 + if (dso_to_test) 47 + strlcpy(filename, dso_to_test, max_sz); 48 + else 49 + perf_exe(filename, max_sz); 50 + } 51 + 52 + static int create_map(struct test_info *ti, char *filename, struct map **map_p) 53 + { 54 + /* Create a dummy map at 0x100000 */ 55 + *map_p = map__new(ti->machine, 0x100000, 0xffffffff, 0, NULL, 56 + PROT_EXEC, 0, NULL, filename, ti->thread); 57 + if (!*map_p) { 58 + pr_debug("Failed to create map!"); 59 + return TEST_FAIL; 60 + } 61 + 62 + return TEST_OK; 63 + } 64 + 65 + static int test_dso(struct dso *dso) 66 + { 67 + struct symbol *last_sym = NULL; 68 + struct rb_node *nd; 69 + int ret = TEST_OK; 70 + 71 + /* dso__fprintf() prints all the symbols */ 72 + if (verbose > 1) 73 + dso__fprintf(dso, stderr); 74 + 75 + for (nd = rb_first_cached(&dso->symbols); nd; nd = rb_next(nd)) { 76 + struct symbol *sym = rb_entry(nd, struct symbol, rb_node); 77 + 78 + if (sym->type != STT_FUNC && sym->type != STT_GNU_IFUNC) 79 + continue; 80 + 81 + /* Check for overlapping function symbols */ 82 + if (last_sym && sym->start < last_sym->end) { 83 + pr_debug("Overlapping symbols:\n"); 84 + symbol__fprintf(last_sym, stderr); 85 + symbol__fprintf(sym, stderr); 86 + ret = TEST_FAIL; 87 + } 88 + /* Check for zero-length function symbol */ 89 + if (sym->start == sym->end) { 90 + pr_debug("Zero-length symbol:\n"); 91 + symbol__fprintf(sym, stderr); 92 + ret = TEST_FAIL; 93 + } 94 + last_sym = sym; 95 + } 96 + 97 + return ret; 98 + } 99 + 100 + static int test_file(struct test_info *ti, char *filename) 101 + { 102 + struct map *map = NULL; 103 + int ret, nr; 104 + 105 + pr_debug("Testing %s\n", filename); 106 + 107 + ret = create_map(ti, filename, &map); 108 + if (ret != TEST_OK) 109 + return ret; 110 + 111 + nr = dso__load(map->dso, map); 112 + if (nr < 0) { 113 + pr_debug("dso__load() failed!\n"); 114 + ret = TEST_FAIL; 115 + goto out_put; 116 + } 117 + 118 + if (nr == 0) { 119 + pr_debug("DSO has no symbols!\n"); 120 + ret = TEST_SKIP; 121 + goto out_put; 122 + } 123 + 124 + ret = test_dso(map->dso); 125 + out_put: 126 + map__put(map); 127 + 128 + return ret; 129 + } 130 + 131 + static int test__symbols(struct test_suite *test __maybe_unused, int subtest __maybe_unused) 132 + { 133 + char filename[PATH_MAX]; 134 + struct test_info ti; 135 + int ret; 136 + 137 + ret = init_test_info(&ti); 138 + if (ret != TEST_OK) 139 + return ret; 140 + 141 + get_test_dso_filename(filename, sizeof(filename)); 142 + 143 + ret = test_file(&ti, filename); 144 + 145 + exit_test_info(&ti); 146 + 147 + return ret; 148 + } 149 + 150 + DEFINE_SUITE("Symbols", symbols);
+3
tools/perf/tests/tests.h
··· 148 148 DECLARE_SUITE(dlfilter); 149 149 DECLARE_SUITE(sigtrap); 150 150 DECLARE_SUITE(event_groups); 151 + DECLARE_SUITE(symbols); 151 152 152 153 /* 153 154 * PowerPC and S390 do not support creation of instruction breakpoints using the ··· 208 207 DECLARE_WORKLOAD(sqrtloop); 209 208 DECLARE_WORKLOAD(brstack); 210 209 DECLARE_WORKLOAD(datasym); 210 + 211 + extern const char *dso_to_test; 211 212 212 213 #endif /* TESTS_H */
+1 -1
tools/perf/tests/workloads/thloop.c
··· 20 20 noinline void test_loop(void) 21 21 { 22 22 while (!done) 23 - count++; 23 + __atomic_fetch_add(&count, 1, __ATOMIC_RELAXED); 24 24 } 25 25 26 26 static void *thfunc(void *arg)
+4 -1
tools/perf/util/Build
··· 154 154 perf-$(CONFIG_PERF_BPF_SKEL) += bpf_counter_cgroup.o 155 155 perf-$(CONFIG_PERF_BPF_SKEL) += bpf_ftrace.o 156 156 perf-$(CONFIG_PERF_BPF_SKEL) += bpf_off_cpu.o 157 - perf-$(CONFIG_PERF_BPF_SKEL) += bpf_lock_contention.o 157 + 158 + ifeq ($(CONFIG_LIBTRACEEVENT),y) 159 + perf-$(CONFIG_PERF_BPF_SKEL) += bpf_lock_contention.o 160 + endif 158 161 159 162 ifeq ($(CONFIG_LIBTRACEEVENT),y) 160 163 perf-$(CONFIG_PERF_BPF_SKEL) += bpf_kwork.o
+5 -1
tools/perf/util/arm-spe-decoder/arm-spe-decoder.c
··· 68 68 /* Clean highest byte */ 69 69 payload = SPE_ADDR_PKT_ADDR_GET_BYTES_0_6(payload); 70 70 } else { 71 - pr_err("unsupported address packet index: 0x%x\n", index); 71 + static u32 seen_idx = 0; 72 + if (!(seen_idx & BIT(index))) { 73 + seen_idx |= BIT(index); 74 + pr_warning("ignoring unsupported address packet index: 0x%x\n", index); 75 + } 72 76 } 73 77 74 78 return payload;
+3 -1
tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.c
··· 422 422 int ch, pat; 423 423 u64 payload = packet->payload; 424 424 int err = 0; 425 + static const char *idx_name[] = {"PC", "TGT", "VA", "PA", "PBT"}; 425 426 426 427 switch (idx) { 427 428 case SPE_ADDR_PKT_HDR_INDEX_INS: 428 429 case SPE_ADDR_PKT_HDR_INDEX_BRANCH: 430 + case SPE_ADDR_PKT_HDR_INDEX_PREV_BRANCH: 429 431 ns = !!SPE_ADDR_PKT_GET_NS(payload); 430 432 el = SPE_ADDR_PKT_GET_EL(payload); 431 433 payload = SPE_ADDR_PKT_ADDR_GET_BYTES_0_6(payload); 432 434 arm_spe_pkt_out_string(&err, &buf, &buf_len, 433 435 "%s 0x%llx el%d ns=%d", 434 - (idx == 1) ? "TGT" : "PC", payload, el, ns); 436 + idx_name[idx], payload, el, ns); 435 437 break; 436 438 case SPE_ADDR_PKT_HDR_INDEX_DATA_VIRT: 437 439 arm_spe_pkt_out_string(&err, &buf, &buf_len,
+1
tools/perf/util/arm-spe-decoder/arm-spe-pkt-decoder.h
··· 65 65 #define SPE_ADDR_PKT_HDR_INDEX_BRANCH 0x1 66 66 #define SPE_ADDR_PKT_HDR_INDEX_DATA_VIRT 0x2 67 67 #define SPE_ADDR_PKT_HDR_INDEX_DATA_PHYS 0x3 68 + #define SPE_ADDR_PKT_HDR_INDEX_PREV_BRANCH 0x4 68 69 69 70 /* Address packet payload */ 70 71 #define SPE_ADDR_PKT_ADDR_BYTE7_SHIFT 56
+10 -2
tools/perf/util/auxtrace.c
··· 1133 1133 if (auxtrace__dont_decode(session)) 1134 1134 return 0; 1135 1135 1136 + if (perf_data__is_pipe(session->data)) 1137 + return 0; 1138 + 1136 1139 if (!session->auxtrace || !session->auxtrace->queue_data) 1137 1140 return -EINVAL; 1138 1141 ··· 1394 1391 synth_opts->calls = true; 1395 1392 } else { 1396 1393 synth_opts->instructions = true; 1394 + synth_opts->cycles = true; 1397 1395 synth_opts->period_type = PERF_ITRACE_DEFAULT_PERIOD_TYPE; 1398 1396 synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD; 1399 1397 } ··· 1483 1479 for (p = str; *p;) { 1484 1480 switch (*p++) { 1485 1481 case 'i': 1486 - synth_opts->instructions = true; 1482 + case 'y': 1483 + if (p[-1] == 'y') 1484 + synth_opts->cycles = true; 1485 + else 1486 + synth_opts->instructions = true; 1487 1487 while (*p == ' ' || *p == ',') 1488 1488 p += 1; 1489 1489 if (isdigit(*p)) { ··· 1646 1638 } 1647 1639 } 1648 1640 out: 1649 - if (synth_opts->instructions) { 1641 + if (synth_opts->instructions || synth_opts->cycles) { 1650 1642 if (!period_type_set) 1651 1643 synth_opts->period_type = 1652 1644 PERF_ITRACE_DEFAULT_PERIOD_TYPE;
+6 -1
tools/perf/util/auxtrace.h
··· 71 71 * @inject: indicates the event (not just the sample) must be fully synthesized 72 72 * because 'perf inject' will write it out 73 73 * @instructions: whether to synthesize 'instructions' events 74 + * @cycles: whether to synthesize 'cycles' events 75 + * (not fully accurate, since CYC packets are only emitted 76 + * together with other events, such as branches) 74 77 * @branches: whether to synthesize 'branches' events 75 78 * (branch misses only for Arm SPE) 76 79 * @transactions: whether to synthesize events for transactions ··· 122 119 bool default_no_sample; 123 120 bool inject; 124 121 bool instructions; 122 + bool cycles; 125 123 bool branches; 126 124 bool transactions; 127 125 bool ptwrites; ··· 647 643 648 644 #define ITRACE_HELP \ 649 645 " i[period]: synthesize instructions events\n" \ 646 + " y[period]: synthesize cycles events (same period as i)\n" \ 650 647 " b: synthesize branches events (branch misses for Arm SPE)\n" \ 651 648 " c: synthesize branches events (calls only)\n" \ 652 649 " r: synthesize branches events (returns only)\n" \ ··· 679 674 " A: approximate IPC\n" \ 680 675 " Z: prefer to ignore timestamps (so-called \"timeless\" decoding)\n" \ 681 676 " PERIOD[ns|us|ms|i|t]: specify period to sample stream\n" \ 682 - " concatenate multiple options. Default is ibxwpe or cewp\n" 677 + " concatenate multiple options. Default is iybxwpe or cewp\n" 683 678 684 679 static inline 685 680 void itrace_synth_opts__set_time_range(struct itrace_synth_opts *opts,
+112 -71
tools/perf/util/bpf_lock_contention.c
··· 34 34 bpf_map__set_max_entries(skel->maps.lock_stat, con->map_nr_entries); 35 35 bpf_map__set_max_entries(skel->maps.tstamp, con->map_nr_entries); 36 36 37 - if (con->aggr_mode == LOCK_AGGR_TASK) { 37 + if (con->aggr_mode == LOCK_AGGR_TASK) 38 38 bpf_map__set_max_entries(skel->maps.task_data, con->map_nr_entries); 39 - bpf_map__set_max_entries(skel->maps.stacks, 1); 40 - } else { 39 + else 41 40 bpf_map__set_max_entries(skel->maps.task_data, 1); 41 + 42 + if (con->save_callstack) 42 43 bpf_map__set_max_entries(skel->maps.stacks, con->map_nr_entries); 43 - } 44 + else 45 + bpf_map__set_max_entries(skel->maps.stacks, 1); 44 46 45 47 if (target__has_cpu(target)) 46 48 ncpus = perf_cpu_map__nr(evlist->core.user_requested_cpus); ··· 148 146 /* these don't work well if in the rodata section */ 149 147 skel->bss->stack_skip = con->stack_skip; 150 148 skel->bss->aggr_mode = con->aggr_mode; 149 + skel->bss->needs_callstack = con->save_callstack; 150 + skel->bss->lock_owner = con->owner; 151 151 152 152 lock_contention_bpf__attach(skel); 153 153 return 0; ··· 167 163 return 0; 168 164 } 169 165 166 + static const char *lock_contention_get_name(struct lock_contention *con, 167 + struct contention_key *key, 168 + u64 *stack_trace) 169 + { 170 + int idx = 0; 171 + u64 addr; 172 + const char *name = ""; 173 + static char name_buf[KSYM_NAME_LEN]; 174 + struct symbol *sym; 175 + struct map *kmap; 176 + struct machine *machine = con->machine; 177 + 178 + if (con->aggr_mode == LOCK_AGGR_TASK) { 179 + struct contention_task_data task; 180 + int pid = key->pid; 181 + int task_fd = bpf_map__fd(skel->maps.task_data); 182 + 183 + /* do not update idle comm which contains CPU number */ 184 + if (pid) { 185 + struct thread *t = __machine__findnew_thread(machine, /*pid=*/-1, pid); 186 + 187 + if (t == NULL) 188 + return name; 189 + if (!bpf_map_lookup_elem(task_fd, &pid, &task) && 190 + thread__set_comm(t, task.comm, /*timestamp=*/0)) 191 + name = task.comm; 192 + } 193 + return name; 194 + } 195 + 196 + if (con->aggr_mode == LOCK_AGGR_ADDR) { 197 + sym = machine__find_kernel_symbol(machine, key->lock_addr, &kmap); 198 + if (sym) 199 + name = sym->name; 200 + return name; 201 + } 202 + 203 + /* LOCK_AGGR_CALLER: skip lock internal functions */ 204 + while (machine__is_lock_function(machine, stack_trace[idx]) && 205 + idx < con->max_stack - 1) 206 + idx++; 207 + 208 + addr = stack_trace[idx]; 209 + sym = machine__find_kernel_symbol(machine, addr, &kmap); 210 + 211 + if (sym) { 212 + unsigned long offset; 213 + 214 + offset = kmap->map_ip(kmap, addr) - sym->start; 215 + 216 + if (offset == 0) 217 + return sym->name; 218 + 219 + snprintf(name_buf, sizeof(name_buf), "%s+%#lx", sym->name, offset); 220 + } else { 221 + snprintf(name_buf, sizeof(name_buf), "%#lx", (unsigned long)addr); 222 + } 223 + 224 + return name_buf; 225 + } 226 + 170 227 int lock_contention_read(struct lock_contention *con) 171 228 { 172 - int fd, stack, task_fd, err = 0; 229 + int fd, stack, err = 0; 173 230 struct contention_key *prev_key, key; 174 231 struct contention_data data = {}; 175 232 struct lock_stat *st = NULL; ··· 240 175 241 176 fd = bpf_map__fd(skel->maps.lock_stat); 242 177 stack = bpf_map__fd(skel->maps.stacks); 243 - task_fd = bpf_map__fd(skel->maps.task_data); 244 178 245 179 con->lost = skel->bss->lost; 246 180 ··· 259 195 260 196 prev_key = NULL; 261 197 while (!bpf_map_get_next_key(fd, prev_key, &key)) { 262 - struct map *kmap; 263 - struct symbol *sym; 264 - int idx = 0; 265 - s32 stack_id; 198 + s64 ls_key; 199 + const char *name; 266 200 267 201 /* to handle errors in the loop body */ 268 202 err = -1; 269 203 270 204 bpf_map_lookup_elem(fd, &key, &data); 271 - st = zalloc(sizeof(*st)); 205 + if (con->save_callstack) { 206 + bpf_map_lookup_elem(stack, &key.stack_id, stack_trace); 207 + 208 + if (!match_callstack_filter(machine, stack_trace)) 209 + goto next; 210 + } 211 + 212 + switch (con->aggr_mode) { 213 + case LOCK_AGGR_CALLER: 214 + ls_key = key.stack_id; 215 + break; 216 + case LOCK_AGGR_TASK: 217 + ls_key = key.pid; 218 + break; 219 + case LOCK_AGGR_ADDR: 220 + ls_key = key.lock_addr; 221 + break; 222 + default: 223 + goto next; 224 + } 225 + 226 + st = lock_stat_find(ls_key); 227 + if (st != NULL) { 228 + st->wait_time_total += data.total_time; 229 + if (st->wait_time_max < data.max_time) 230 + st->wait_time_max = data.max_time; 231 + if (st->wait_time_min > data.min_time) 232 + st->wait_time_min = data.min_time; 233 + 234 + st->nr_contended += data.count; 235 + if (st->nr_contended) 236 + st->avg_wait_time = st->wait_time_total / st->nr_contended; 237 + goto next; 238 + } 239 + 240 + name = lock_contention_get_name(con, &key, stack_trace); 241 + st = lock_stat_findnew(ls_key, name, data.flags); 272 242 if (st == NULL) 273 243 break; 274 244 ··· 314 216 if (data.count) 315 217 st->avg_wait_time = data.total_time / data.count; 316 218 317 - st->flags = data.flags; 318 - st->addr = key.aggr_key; 319 - 320 - if (con->aggr_mode == LOCK_AGGR_TASK) { 321 - struct contention_task_data task; 322 - struct thread *t; 323 - int pid = key.aggr_key; 324 - 325 - /* do not update idle comm which contains CPU number */ 326 - if (st->addr) { 327 - bpf_map_lookup_elem(task_fd, &pid, &task); 328 - t = __machine__findnew_thread(machine, /*pid=*/-1, pid); 329 - thread__set_comm(t, task.comm, /*timestamp=*/0); 330 - } 331 - goto next; 332 - } 333 - 334 - if (con->aggr_mode == LOCK_AGGR_ADDR) { 335 - sym = machine__find_kernel_symbol(machine, st->addr, &kmap); 336 - if (sym) 337 - st->name = strdup(sym->name); 338 - goto next; 339 - } 340 - 341 - stack_id = key.aggr_key; 342 - bpf_map_lookup_elem(stack, &stack_id, stack_trace); 343 - 344 - /* skip lock internal functions */ 345 - while (machine__is_lock_function(machine, stack_trace[idx]) && 346 - idx < con->max_stack - 1) 347 - idx++; 348 - 349 - st->addr = stack_trace[idx]; 350 - sym = machine__find_kernel_symbol(machine, st->addr, &kmap); 351 - 352 - if (sym) { 353 - unsigned long offset; 354 - int ret = 0; 355 - 356 - offset = kmap->map_ip(kmap, st->addr) - sym->start; 357 - 358 - if (offset) 359 - ret = asprintf(&st->name, "%s+%#lx", sym->name, offset); 360 - else 361 - st->name = strdup(sym->name); 362 - 363 - if (ret < 0 || st->name == NULL) 364 - break; 365 - } else if (asprintf(&st->name, "%#lx", (unsigned long)st->addr) < 0) { 366 - break; 367 - } 368 - 369 - if (verbose > 0) { 219 + if (con->save_callstack) { 370 220 st->callstack = memdup(stack_trace, stack_size); 371 221 if (st->callstack == NULL) 372 222 break; 373 223 } 224 + 374 225 next: 375 - hlist_add_head(&st->hash_entry, con->result); 376 226 prev_key = &key; 377 227 378 - /* we're fine now, reset the values */ 379 - st = NULL; 228 + /* we're fine now, reset the error */ 380 229 err = 0; 381 230 } 382 231 383 232 free(stack_trace); 384 - if (st) { 385 - free(st->name); 386 - free(st); 387 - } 388 233 389 234 return err; 390 235 }
+95 -9
tools/perf/util/bpf_skel/lock_contention.bpf.c
··· 10 10 /* default buffer size */ 11 11 #define MAX_ENTRIES 10240 12 12 13 + /* lock contention flags from include/trace/events/lock.h */ 14 + #define LCB_F_SPIN (1U << 0) 15 + #define LCB_F_READ (1U << 1) 16 + #define LCB_F_WRITE (1U << 2) 17 + #define LCB_F_RT (1U << 3) 18 + #define LCB_F_PERCPU (1U << 4) 19 + #define LCB_F_MUTEX (1U << 5) 20 + 13 21 struct tstamp_data { 14 22 __u64 timestamp; 15 23 __u64 lock; ··· 84 76 __uint(max_entries, 1); 85 77 } addr_filter SEC(".maps"); 86 78 79 + struct rw_semaphore___old { 80 + struct task_struct *owner; 81 + } __attribute__((preserve_access_index)); 82 + 83 + struct rw_semaphore___new { 84 + atomic_long_t owner; 85 + } __attribute__((preserve_access_index)); 86 + 87 87 /* control flags */ 88 88 int enabled; 89 89 int has_cpu; 90 90 int has_task; 91 91 int has_type; 92 92 int has_addr; 93 + int needs_callstack; 93 94 int stack_skip; 95 + int lock_owner; 94 96 95 97 /* determine the key of lock stat */ 96 98 int aggr_mode; ··· 149 131 return 1; 150 132 } 151 133 152 - static inline void update_task_data(__u32 pid) 134 + static inline int update_task_data(struct task_struct *task) 153 135 { 154 136 struct contention_task_data *p; 137 + int pid, err; 138 + 139 + err = bpf_core_read(&pid, sizeof(pid), &task->pid); 140 + if (err) 141 + return -1; 155 142 156 143 p = bpf_map_lookup_elem(&task_data, &pid); 157 144 if (p == NULL) { 158 - struct contention_task_data data; 145 + struct contention_task_data data = {}; 159 146 160 - bpf_get_current_comm(data.comm, sizeof(data.comm)); 147 + BPF_CORE_READ_STR_INTO(&data.comm, task, comm); 161 148 bpf_map_update_elem(&task_data, &pid, &data, BPF_NOEXIST); 162 149 } 150 + 151 + return 0; 152 + } 153 + 154 + #ifndef __has_builtin 155 + # define __has_builtin(x) 0 156 + #endif 157 + 158 + static inline struct task_struct *get_lock_owner(__u64 lock, __u32 flags) 159 + { 160 + struct task_struct *task; 161 + __u64 owner = 0; 162 + 163 + if (flags & LCB_F_MUTEX) { 164 + struct mutex *mutex = (void *)lock; 165 + owner = BPF_CORE_READ(mutex, owner.counter); 166 + } else if (flags == LCB_F_READ || flags == LCB_F_WRITE) { 167 + #if __has_builtin(bpf_core_type_matches) 168 + if (bpf_core_type_matches(struct rw_semaphore___old)) { 169 + struct rw_semaphore___old *rwsem = (void *)lock; 170 + owner = (unsigned long)BPF_CORE_READ(rwsem, owner); 171 + } else if (bpf_core_type_matches(struct rw_semaphore___new)) { 172 + struct rw_semaphore___new *rwsem = (void *)lock; 173 + owner = BPF_CORE_READ(rwsem, owner.counter); 174 + } 175 + #else 176 + /* assume new struct */ 177 + struct rw_semaphore *rwsem = (void *)lock; 178 + owner = BPF_CORE_READ(rwsem, owner.counter); 179 + #endif 180 + } 181 + 182 + if (!owner) 183 + return NULL; 184 + 185 + task = (void *)(owner & ~7UL); 186 + return task; 163 187 } 164 188 165 189 SEC("tp_btf/contention_begin") ··· 233 173 pelem->lock = (__u64)ctx[0]; 234 174 pelem->flags = (__u32)ctx[1]; 235 175 236 - if (aggr_mode == LOCK_AGGR_CALLER) { 176 + if (needs_callstack) { 237 177 pelem->stack_id = bpf_get_stackid(ctx, &stacks, 238 178 BPF_F_FAST_STACK_CMP | stack_skip); 239 179 if (pelem->stack_id < 0) 240 180 lost++; 181 + } else if (aggr_mode == LOCK_AGGR_TASK) { 182 + struct task_struct *task; 183 + 184 + if (lock_owner) { 185 + task = get_lock_owner(pelem->lock, pelem->flags); 186 + 187 + /* The flags is not used anymore. Pass the owner pid. */ 188 + if (task) 189 + pelem->flags = BPF_CORE_READ(task, pid); 190 + else 191 + pelem->flags = -1U; 192 + 193 + } else { 194 + task = bpf_get_current_task_btf(); 195 + } 196 + 197 + if (task) { 198 + if (update_task_data(task) < 0 && lock_owner) 199 + pelem->flags = -1U; 200 + } 241 201 } 242 202 243 203 return 0; ··· 268 188 { 269 189 __u32 pid; 270 190 struct tstamp_data *pelem; 271 - struct contention_key key; 191 + struct contention_key key = {}; 272 192 struct contention_data *data; 273 193 __u64 duration; 274 194 ··· 284 204 285 205 switch (aggr_mode) { 286 206 case LOCK_AGGR_CALLER: 287 - key.aggr_key = pelem->stack_id; 207 + key.stack_id = pelem->stack_id; 288 208 break; 289 209 case LOCK_AGGR_TASK: 290 - key.aggr_key = pid; 291 - update_task_data(pid); 210 + if (lock_owner) 211 + key.pid = pelem->flags; 212 + else 213 + key.pid = pid; 214 + if (needs_callstack) 215 + key.stack_id = pelem->stack_id; 292 216 break; 293 217 case LOCK_AGGR_ADDR: 294 - key.aggr_key = pelem->lock; 218 + key.lock_addr = pelem->lock; 219 + if (needs_callstack) 220 + key.stack_id = pelem->stack_id; 295 221 break; 296 222 default: 297 223 /* should not happen */
+3 -1
tools/perf/util/bpf_skel/lock_data.h
··· 4 4 #define UTIL_BPF_SKEL_LOCK_DATA_H 5 5 6 6 struct contention_key { 7 - u64 aggr_key; /* can be stack_id, pid or lock addr */ 7 + u32 stack_id; 8 + u32 pid; 9 + u64 lock_addr; 8 10 }; 9 11 10 12 #define TASK_COMM_LEN 16
+15
tools/perf/util/branch.c
··· 212 212 213 213 return printed; 214 214 } 215 + 216 + const char *branch_spec_desc(int spec) 217 + { 218 + const char *branch_spec_outcomes[PERF_BR_SPEC_MAX] = { 219 + "N/A", 220 + "SPEC_WRONG_PATH", 221 + "NON_SPEC_CORRECT_PATH", 222 + "SPEC_CORRECT_PATH", 223 + }; 224 + 225 + if (spec >= 0 && spec < PERF_BR_SPEC_MAX) 226 + return branch_spec_outcomes[spec]; 227 + 228 + return NULL; 229 + }
+2
tools/perf/util/branch.h
··· 89 89 void branch_type_stat_display(FILE *fp, struct branch_type_stat *st); 90 90 int branch_type_str(struct branch_type_stat *st, char *bf, int bfsize); 91 91 92 + const char *branch_spec_desc(int spec); 93 + 92 94 #endif /* _PERF_BRANCH_H */
+20 -5
tools/perf/util/cacheline.h
··· 6 6 7 7 int __pure cacheline_size(void); 8 8 9 - static inline u64 cl_address(u64 address) 9 + 10 + /* 11 + * Some architectures have 'Adjacent Cacheline Prefetch' feature, 12 + * which performs like the cacheline size being doubled. 13 + */ 14 + static inline u64 cl_address(u64 address, bool double_cl) 10 15 { 16 + u64 size = cacheline_size(); 17 + 18 + if (double_cl) 19 + size *= 2; 20 + 11 21 /* return the cacheline of the address */ 12 - return (address & ~(cacheline_size() - 1)); 22 + return (address & ~(size - 1)); 13 23 } 14 24 15 - static inline u64 cl_offset(u64 address) 25 + static inline u64 cl_offset(u64 address, bool double_cl) 16 26 { 17 - /* return the cacheline of the address */ 18 - return (address & (cacheline_size() - 1)); 27 + u64 size = cacheline_size(); 28 + 29 + if (double_cl) 30 + size *= 2; 31 + 32 + /* return the offset inside cacheline */ 33 + return (address & (size - 1)); 19 34 } 20 35 21 36 #endif // PERF_CACHELINE_H
-1
tools/perf/util/cgroup.c
··· 481 481 nr_cgroups++; 482 482 483 483 if (metric_events) { 484 - perf_stat__collect_metric_expr(tmp_list); 485 484 if (metricgroup__copy_metric_events(tmp_list, cgrp, 486 485 metric_events, 487 486 &orig_metric_events) < 0)
+1 -8
tools/perf/util/cputopo.c
··· 422 422 static int load_hybrid_node(struct hybrid_topology_node *node, 423 423 struct perf_pmu *pmu) 424 424 { 425 - const char *sysfs; 426 - char path[PATH_MAX]; 427 425 char *buf = NULL, *p; 428 426 FILE *fp; 429 427 size_t len = 0; ··· 430 432 if (!node->pmu_name) 431 433 return -1; 432 434 433 - sysfs = sysfs__mountpoint(); 434 - if (!sysfs) 435 - goto err; 436 - 437 - snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, pmu->name); 438 - fp = fopen(path, "r"); 435 + fp = perf_pmu__open_file(pmu, "cpus"); 439 436 if (!fp) 440 437 goto err; 441 438
+26 -8
tools/perf/util/cs-etm-base.c
··· 36 36 [CS_ETMV4_TRCIDR2] = " TRCIDR2 %llx\n", 37 37 [CS_ETMV4_TRCIDR8] = " TRCIDR8 %llx\n", 38 38 [CS_ETMV4_TRCAUTHSTATUS] = " TRCAUTHSTATUS %llx\n", 39 - [CS_ETE_TRCDEVARCH] = " TRCDEVARCH %llx\n" 39 + [CS_ETMV4_TS_SOURCE] = " TS_SOURCE %lld\n", 40 + }; 41 + 42 + static const char * const cs_ete_priv_fmts[] = { 43 + [CS_ETM_MAGIC] = " Magic number %llx\n", 44 + [CS_ETM_CPU] = " CPU %lld\n", 45 + [CS_ETM_NR_TRC_PARAMS] = " NR_TRC_PARAMS %llx\n", 46 + [CS_ETE_TRCCONFIGR] = " TRCCONFIGR %llx\n", 47 + [CS_ETE_TRCTRACEIDR] = " TRCTRACEIDR %llx\n", 48 + [CS_ETE_TRCIDR0] = " TRCIDR0 %llx\n", 49 + [CS_ETE_TRCIDR1] = " TRCIDR1 %llx\n", 50 + [CS_ETE_TRCIDR2] = " TRCIDR2 %llx\n", 51 + [CS_ETE_TRCIDR8] = " TRCIDR8 %llx\n", 52 + [CS_ETE_TRCAUTHSTATUS] = " TRCAUTHSTATUS %llx\n", 53 + [CS_ETE_TRCDEVARCH] = " TRCDEVARCH %llx\n", 54 + [CS_ETE_TS_SOURCE] = " TS_SOURCE %lld\n", 40 55 }; 41 56 42 57 static const char * const param_unk_fmt = ··· 111 96 else 112 97 fprintf(stdout, cs_etm_priv_fmts[j], val[i]); 113 98 } 114 - } else if (magic == __perf_cs_etmv4_magic || magic == __perf_cs_ete_magic) { 115 - /* 116 - * ETE and ETMv4 can be printed in the same block because the number of parameters 117 - * is saved and they share the list of parameter names. ETE is also only supported 118 - * in V1 files. 119 - */ 99 + } else if (magic == __perf_cs_etmv4_magic) { 100 + for (j = 0; j < total_params; j++, i++) { 101 + /* if newer record - could be excess params */ 102 + if (j >= CS_ETMV4_PRIV_MAX) 103 + fprintf(stdout, param_unk_fmt, j, val[i]); 104 + else 105 + fprintf(stdout, cs_etmv4_priv_fmts[j], val[i]); 106 + } 107 + } else if (magic == __perf_cs_ete_magic) { 120 108 for (j = 0; j < total_params; j++, i++) { 121 109 /* if newer record - could be excess params */ 122 110 if (j >= CS_ETE_PRIV_MAX) 123 111 fprintf(stdout, param_unk_fmt, j, val[i]); 124 112 else 125 - fprintf(stdout, cs_etmv4_priv_fmts[j], val[i]); 113 + fprintf(stdout, cs_ete_priv_fmts[j], val[i]); 126 114 } 127 115 } else { 128 116 /* failure - note bad magic value and error out */
+57 -14
tools/perf/util/cs-etm-decoder/cs-etm-decoder.c
··· 30 30 #endif 31 31 #endif 32 32 33 + /* 34 + * Assume a maximum of 0.1ns elapsed per instruction. This would be the 35 + * case with a theoretical 10GHz core executing 1 instruction per cycle. 36 + * Used to estimate the sample time for synthesized instructions because 37 + * Coresight only emits a timestamp for a range of instructions rather 38 + * than per instruction. 39 + */ 40 + const u32 INSTR_PER_NS = 10; 41 + 33 42 struct cs_etm_decoder { 34 43 void *data; 35 44 void (*packet_printer)(const char *msg); ··· 119 110 packet_queue->packet_count--; 120 111 121 112 return 1; 113 + } 114 + 115 + /* 116 + * Calculate the number of nanoseconds elapsed. 117 + * 118 + * instr_count is updated in place with the remainder of the instructions 119 + * which didn't make up a whole nanosecond. 120 + */ 121 + static u32 cs_etm_decoder__dec_instr_count_to_ns(u32 *instr_count) 122 + { 123 + const u32 instr_copy = *instr_count; 124 + 125 + *instr_count %= INSTR_PER_NS; 126 + return instr_copy / INSTR_PER_NS; 122 127 } 123 128 124 129 static int cs_etm_decoder__gen_etmv3_config(struct cs_etm_trace_params *params, ··· 283 260 struct cs_etm_packet_queue *packet_queue, 284 261 const uint8_t trace_chan_id) 285 262 { 263 + u64 estimated_ts; 264 + 286 265 /* No timestamp packet has been received, nothing to do */ 287 - if (!packet_queue->cs_timestamp) 266 + if (!packet_queue->next_cs_timestamp) 288 267 return OCSD_RESP_CONT; 289 268 290 - packet_queue->cs_timestamp = packet_queue->next_cs_timestamp; 269 + estimated_ts = packet_queue->cs_timestamp + 270 + cs_etm_decoder__dec_instr_count_to_ns(&packet_queue->instr_count); 291 271 292 - /* Estimate the timestamp for the next range packet */ 293 - packet_queue->next_cs_timestamp += packet_queue->instr_count; 294 - packet_queue->instr_count = 0; 272 + /* Estimated TS can never be higher than the next real one in the trace */ 273 + packet_queue->cs_timestamp = min(packet_queue->next_cs_timestamp, estimated_ts); 295 274 296 275 /* Tell the front end which traceid_queue needs attention */ 297 276 cs_etm__etmq_set_traceid_queue_timestamp(etmq, trace_chan_id); ··· 308 283 const ocsd_trc_index_t indx) 309 284 { 310 285 struct cs_etm_packet_queue *packet_queue; 286 + u64 converted_timestamp; 287 + u64 estimated_first_ts; 311 288 312 289 /* First get the packet queue for this traceID */ 313 290 packet_queue = cs_etm__etmq_get_packet_queue(etmq, trace_chan_id); ··· 317 290 return OCSD_RESP_FATAL_SYS_ERR; 318 291 319 292 /* 293 + * Coresight timestamps are raw timer values which need to be scaled to ns. Assume 294 + * 0 is a bad value so don't try to convert it. 295 + */ 296 + converted_timestamp = elem->timestamp ? 297 + cs_etm__convert_sample_time(etmq, elem->timestamp) : 0; 298 + 299 + /* 320 300 * We've seen a timestamp packet before - simply record the new value. 321 301 * Function do_soft_timestamp() will report the value to the front end, 322 302 * hence asking the decoder to keep decoding rather than stopping. 323 303 */ 324 - if (packet_queue->cs_timestamp) { 325 - packet_queue->next_cs_timestamp = elem->timestamp; 304 + if (packet_queue->next_cs_timestamp) { 305 + /* 306 + * What was next is now where new ranges start from, overwriting 307 + * any previous estimate in cs_timestamp 308 + */ 309 + packet_queue->cs_timestamp = packet_queue->next_cs_timestamp; 310 + packet_queue->next_cs_timestamp = converted_timestamp; 326 311 return OCSD_RESP_CONT; 327 312 } 328 313 329 - 330 - if (!elem->timestamp) { 314 + if (!converted_timestamp) { 331 315 /* 332 316 * Zero timestamps can be seen due to misconfiguration or hardware bugs. 333 317 * Warn once, and don't try to subtract instr_count as it would result in an ··· 350 312 ". Decoding may be improved by prepending 'Z' to your current --itrace arguments.\n", 351 313 indx); 352 314 353 - } else if (packet_queue->instr_count > elem->timestamp) { 315 + } else if (packet_queue->instr_count / INSTR_PER_NS > converted_timestamp) { 354 316 /* 355 317 * Sanity check that the elem->timestamp - packet_queue->instr_count would not 356 318 * result in an underflow. Warn and clamp at 0 if it would. ··· 363 325 * or a discontinuity. Since timestamps packets are generated *after* 364 326 * range packets have been generated, we need to estimate the time at 365 327 * which instructions started by subtracting the number of instructions 366 - * executed to the timestamp. 328 + * executed to the timestamp. Don't estimate earlier than the last used 329 + * timestamp though. 367 330 */ 368 - packet_queue->cs_timestamp = elem->timestamp - packet_queue->instr_count; 331 + estimated_first_ts = converted_timestamp - 332 + (packet_queue->instr_count / INSTR_PER_NS); 333 + packet_queue->cs_timestamp = max(packet_queue->cs_timestamp, estimated_first_ts); 369 334 } 370 - packet_queue->next_cs_timestamp = elem->timestamp; 335 + packet_queue->next_cs_timestamp = converted_timestamp; 371 336 packet_queue->instr_count = 0; 372 337 373 338 /* Tell the front end which traceid_queue needs attention */ ··· 383 342 static void 384 343 cs_etm_decoder__reset_timestamp(struct cs_etm_packet_queue *packet_queue) 385 344 { 386 - packet_queue->cs_timestamp = 0; 387 345 packet_queue->next_cs_timestamp = 0; 388 346 packet_queue->instr_count = 0; 389 347 } ··· 644 604 case OCSD_GEN_TRC_ELEM_CUSTOM: 645 605 case OCSD_GEN_TRC_ELEM_SYNC_MARKER: 646 606 case OCSD_GEN_TRC_ELEM_MEMTRANS: 607 + #if (OCSD_VER_NUM >= 0x010400) 608 + case OCSD_GEN_TRC_ELEM_INSTRUMENTATION: 609 + #endif 647 610 default: 648 611 break; 649 612 }
+85 -10
tools/perf/util/cs-etm.c
··· 35 35 #include "tool.h" 36 36 #include "thread.h" 37 37 #include "thread-stack.h" 38 + #include "tsc.h" 38 39 #include <tools/libc_compat.h> 39 40 #include "util/synthetic-events.h" 40 41 ··· 47 46 struct perf_session *session; 48 47 struct machine *machine; 49 48 struct thread *unknown_thread; 49 + struct perf_tsc_conversion tc; 50 50 51 51 u8 timeless_decoding; 52 52 u8 snapshot_mode; 53 53 u8 data_queued; 54 + u8 has_virtual_ts; /* Virtual/Kernel timestamps in the trace. */ 54 55 55 56 int num_cpu; 56 57 u64 latest_kernel_timestamp; ··· 467 464 u64 **metadata = etm->metadata; 468 465 469 466 t_params[idx].protocol = CS_ETM_PROTO_ETE; 470 - t_params[idx].ete.reg_idr0 = metadata[idx][CS_ETMV4_TRCIDR0]; 471 - t_params[idx].ete.reg_idr1 = metadata[idx][CS_ETMV4_TRCIDR1]; 472 - t_params[idx].ete.reg_idr2 = metadata[idx][CS_ETMV4_TRCIDR2]; 473 - t_params[idx].ete.reg_idr8 = metadata[idx][CS_ETMV4_TRCIDR8]; 474 - t_params[idx].ete.reg_configr = metadata[idx][CS_ETMV4_TRCCONFIGR]; 475 - t_params[idx].ete.reg_traceidr = metadata[idx][CS_ETMV4_TRCTRACEIDR]; 467 + t_params[idx].ete.reg_idr0 = metadata[idx][CS_ETE_TRCIDR0]; 468 + t_params[idx].ete.reg_idr1 = metadata[idx][CS_ETE_TRCIDR1]; 469 + t_params[idx].ete.reg_idr2 = metadata[idx][CS_ETE_TRCIDR2]; 470 + t_params[idx].ete.reg_idr8 = metadata[idx][CS_ETE_TRCIDR8]; 471 + t_params[idx].ete.reg_configr = metadata[idx][CS_ETE_TRCCONFIGR]; 472 + t_params[idx].ete.reg_traceidr = metadata[idx][CS_ETE_TRCTRACEIDR]; 476 473 t_params[idx].ete.reg_devarch = metadata[idx][CS_ETE_TRCDEVARCH]; 477 474 } 478 475 ··· 1164 1161 sample->insn_len, (void *)sample->insn); 1165 1162 } 1166 1163 1164 + u64 cs_etm__convert_sample_time(struct cs_etm_queue *etmq, u64 cs_timestamp) 1165 + { 1166 + struct cs_etm_auxtrace *etm = etmq->etm; 1167 + 1168 + if (etm->has_virtual_ts) 1169 + return tsc_to_perf_time(cs_timestamp, &etm->tc); 1170 + else 1171 + return cs_timestamp; 1172 + } 1173 + 1174 + static inline u64 cs_etm__resolve_sample_time(struct cs_etm_queue *etmq, 1175 + struct cs_etm_traceid_queue *tidq) 1176 + { 1177 + struct cs_etm_auxtrace *etm = etmq->etm; 1178 + struct cs_etm_packet_queue *packet_queue = &tidq->packet_queue; 1179 + 1180 + if (etm->timeless_decoding) 1181 + return 0; 1182 + else if (etm->has_virtual_ts) 1183 + return packet_queue->cs_timestamp; 1184 + else 1185 + return etm->latest_kernel_timestamp; 1186 + } 1187 + 1167 1188 static int cs_etm__synth_instruction_sample(struct cs_etm_queue *etmq, 1168 1189 struct cs_etm_traceid_queue *tidq, 1169 1190 u64 addr, u64 period) ··· 1201 1174 event->sample.header.misc = cs_etm__cpu_mode(etmq, addr); 1202 1175 event->sample.header.size = sizeof(struct perf_event_header); 1203 1176 1204 - if (!etm->timeless_decoding) 1205 - sample.time = etm->latest_kernel_timestamp; 1177 + /* Set time field based on etm auxtrace config. */ 1178 + sample.time = cs_etm__resolve_sample_time(etmq, tidq); 1179 + 1206 1180 sample.ip = addr; 1207 1181 sample.pid = tidq->pid; 1208 1182 sample.tid = tidq->tid; ··· 1260 1232 event->sample.header.misc = cs_etm__cpu_mode(etmq, ip); 1261 1233 event->sample.header.size = sizeof(struct perf_event_header); 1262 1234 1263 - if (!etm->timeless_decoding) 1264 - sample.time = etm->latest_kernel_timestamp; 1235 + /* Set time field based on etm auxtrace config. */ 1236 + sample.time = cs_etm__resolve_sample_time(etmq, tidq); 1237 + 1265 1238 sample.ip = ip; 1266 1239 sample.pid = tidq->pid; 1267 1240 sample.tid = tidq->tid; ··· 2775 2746 return 0; 2776 2747 } 2777 2748 2749 + #define HAS_PARAM(j, type, param) (metadata[(j)][CS_ETM_NR_TRC_PARAMS] <= \ 2750 + (CS_##type##_##param - CS_ETM_COMMON_BLK_MAX_V1)) 2751 + 2752 + /* 2753 + * Loop through the ETMs and complain if we find at least one where ts_source != 1 (virtual 2754 + * timestamps). 2755 + */ 2756 + static bool cs_etm__has_virtual_ts(u64 **metadata, int num_cpu) 2757 + { 2758 + int j; 2759 + 2760 + for (j = 0; j < num_cpu; j++) { 2761 + switch (metadata[j][CS_ETM_MAGIC]) { 2762 + case __perf_cs_etmv4_magic: 2763 + if (HAS_PARAM(j, ETMV4, TS_SOURCE) || metadata[j][CS_ETMV4_TS_SOURCE] != 1) 2764 + return false; 2765 + break; 2766 + case __perf_cs_ete_magic: 2767 + if (HAS_PARAM(j, ETE, TS_SOURCE) || metadata[j][CS_ETE_TS_SOURCE] != 1) 2768 + return false; 2769 + break; 2770 + default: 2771 + /* Unknown / unsupported magic number. */ 2772 + return false; 2773 + } 2774 + } 2775 + return true; 2776 + } 2777 + 2778 2778 int cs_etm__process_auxtrace_info_full(union perf_event *event, 2779 2779 struct perf_session *session) 2780 2780 { 2781 2781 struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info; 2782 2782 struct cs_etm_auxtrace *etm = NULL; 2783 2783 struct int_node *inode; 2784 + struct perf_record_time_conv *tc = &session->time_conv; 2784 2785 int event_header_size = sizeof(struct perf_event_header); 2785 2786 int total_size = auxtrace_info->header.size; 2786 2787 int priv_size = 0; ··· 2945 2886 etm->auxtrace_type = auxtrace_info->type; 2946 2887 etm->timeless_decoding = cs_etm__is_timeless_decoding(etm); 2947 2888 2889 + /* Use virtual timestamps if all ETMs report ts_source = 1 */ 2890 + etm->has_virtual_ts = cs_etm__has_virtual_ts(metadata, num_cpu); 2891 + 2892 + if (!etm->has_virtual_ts) 2893 + ui__warning("Virtual timestamps are not enabled, or not supported by the traced system.\n" 2894 + "The time field of the samples will not be set accurately.\n\n"); 2895 + 2948 2896 etm->auxtrace.process_event = cs_etm__process_event; 2949 2897 etm->auxtrace.process_auxtrace_event = cs_etm__process_auxtrace_event; 2950 2898 etm->auxtrace.flush_events = cs_etm__flush_events; ··· 2981 2915 goto err_delete_thread; 2982 2916 } 2983 2917 2918 + etm->tc.time_shift = tc->time_shift; 2919 + etm->tc.time_mult = tc->time_mult; 2920 + etm->tc.time_zero = tc->time_zero; 2921 + if (event_contains(*tc, time_cycles)) { 2922 + etm->tc.time_cycles = tc->time_cycles; 2923 + etm->tc.time_mask = tc->time_mask; 2924 + etm->tc.cap_user_time_zero = tc->cap_user_time_zero; 2925 + etm->tc.cap_user_time_short = tc->cap_user_time_short; 2926 + } 2984 2927 err = cs_etm__synth_events(etm, session); 2985 2928 if (err) 2986 2929 goto err_delete_thread;
+14 -2
tools/perf/util/cs-etm.h
··· 71 71 CS_ETMV4_TRCIDR2, 72 72 CS_ETMV4_TRCIDR8, 73 73 CS_ETMV4_TRCAUTHSTATUS, 74 + CS_ETMV4_TS_SOURCE, 74 75 CS_ETMV4_PRIV_MAX, 75 76 }; 76 77 ··· 83 82 * added in header V1 84 83 */ 85 84 enum { 86 - CS_ETE_TRCDEVARCH = CS_ETMV4_PRIV_MAX, 85 + /* Dynamic, configurable parameters */ 86 + CS_ETE_TRCCONFIGR = CS_ETM_COMMON_BLK_MAX_V1, 87 + CS_ETE_TRCTRACEIDR, 88 + /* RO, taken from sysFS */ 89 + CS_ETE_TRCIDR0, 90 + CS_ETE_TRCIDR1, 91 + CS_ETE_TRCIDR2, 92 + CS_ETE_TRCIDR8, 93 + CS_ETE_TRCAUTHSTATUS, 94 + CS_ETE_TRCDEVARCH, 95 + CS_ETE_TS_SOURCE, 87 96 CS_ETE_PRIV_MAX 88 97 }; 89 98 ··· 192 181 u32 head; 193 182 u32 tail; 194 183 u32 instr_count; 195 - u64 cs_timestamp; 184 + u64 cs_timestamp; /* Timestamp from trace data, converted to ns if possible */ 196 185 u64 next_cs_timestamp; 197 186 struct cs_etm_packet packet_buffer[CS_ETM_PACKET_MAX_BUFFER]; 198 187 }; ··· 231 220 *cs_etm__etmq_get_packet_queue(struct cs_etm_queue *etmq, u8 trace_chan_id); 232 221 int cs_etm__process_auxtrace_info_full(union perf_event *event __maybe_unused, 233 222 struct perf_session *session __maybe_unused); 223 + u64 cs_etm__convert_sample_time(struct cs_etm_queue *etmq, u64 cs_timestamp); 234 224 #else 235 225 static inline int 236 226 cs_etm__process_auxtrace_info_full(union perf_event *event __maybe_unused,
+1 -3
tools/perf/util/data-convert-bt.c
··· 322 322 offset = tmp_val; 323 323 len = offset >> 16; 324 324 offset &= 0xffff; 325 - #ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE 326 - if (flags & TEP_FIELD_IS_RELATIVE) 325 + if (tep_field_is_relative(flags)) 327 326 offset += fmtf->offset + fmtf->size; 328 - #endif 329 327 } 330 328 331 329 if (flags & TEP_FIELD_IS_ARRAY) {
+15
tools/perf/util/debug.c
··· 19 19 #include "debug.h" 20 20 #include "print_binary.h" 21 21 #include "target.h" 22 + #include "trace-event.h" 22 23 #include "ui/helpline.h" 23 24 #include "ui/ui.h" 24 25 #include "util/parse-sublevel-options.h" 25 26 26 27 #include <linux/ctype.h> 28 + 29 + #ifdef HAVE_LIBTRACEEVENT 30 + #include <traceevent/event-parse.h> 31 + #else 32 + #define LIBTRACEEVENT_VERSION 0 33 + #endif 27 34 28 35 int verbose; 29 36 int debug_peo_args; ··· 235 228 /* Allow only verbose value in range (0, 10), otherwise set 0. */ 236 229 verbose = (verbose < 0) || (verbose > 10) ? 0 : verbose; 237 230 231 + #if LIBTRACEEVENT_VERSION >= MAKE_LIBTRACEEVENT_VERSION(1, 3, 0) 232 + if (verbose == 1) 233 + tep_set_loglevel(TEP_LOG_INFO); 234 + else if (verbose == 2) 235 + tep_set_loglevel(TEP_LOG_DEBUG); 236 + else if (verbose >= 3) 237 + tep_set_loglevel(TEP_LOG_ALL); 238 + #endif 238 239 return 0; 239 240 } 240 241
+13 -8
tools/perf/util/evsel.c
··· 285 285 evsel->sample_size = __evsel__sample_size(attr->sample_type); 286 286 evsel__calc_id_pos(evsel); 287 287 evsel->cmdline_group_boundary = false; 288 - evsel->metric_expr = NULL; 289 - evsel->metric_name = NULL; 290 288 evsel->metric_events = NULL; 291 289 evsel->per_pkg_mask = NULL; 292 290 evsel->collect_stat = false; ··· 2317 2319 * abort:1 //transaction abort 2318 2320 * cycles:16 //cycle count to last branch 2319 2321 * type:4 //branch type 2320 - * reserved:40 2322 + * spec:2 //branch speculation info 2323 + * new_type:4 //additional branch type 2324 + * priv:3 //privilege level 2325 + * reserved:31 2321 2326 * } 2322 2327 * } 2323 2328 * ··· 2336 2335 new_val |= bitfield_swap(value, 3, 1); 2337 2336 new_val |= bitfield_swap(value, 4, 16); 2338 2337 new_val |= bitfield_swap(value, 20, 4); 2339 - new_val |= bitfield_swap(value, 24, 40); 2338 + new_val |= bitfield_swap(value, 24, 2); 2339 + new_val |= bitfield_swap(value, 26, 4); 2340 + new_val |= bitfield_swap(value, 30, 3); 2341 + new_val |= bitfield_swap(value, 33, 31); 2340 2342 } else { 2341 2343 new_val = bitfield_swap(value, 63, 1); 2342 2344 new_val |= bitfield_swap(value, 62, 1); ··· 2347 2343 new_val |= bitfield_swap(value, 60, 1); 2348 2344 new_val |= bitfield_swap(value, 44, 16); 2349 2345 new_val |= bitfield_swap(value, 40, 4); 2350 - new_val |= bitfield_swap(value, 0, 40); 2346 + new_val |= bitfield_swap(value, 38, 2); 2347 + new_val |= bitfield_swap(value, 34, 4); 2348 + new_val |= bitfield_swap(value, 31, 3); 2349 + new_val |= bitfield_swap(value, 0, 31); 2351 2350 } 2352 2351 2353 2352 return new_val; ··· 2791 2784 if (field->flags & TEP_FIELD_IS_DYNAMIC) { 2792 2785 offset = *(int *)(sample->raw_data + field->offset); 2793 2786 offset &= 0xffff; 2794 - #ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE 2795 - if (field->flags & TEP_FIELD_IS_RELATIVE) 2787 + if (tep_field_is_relative(field->flags)) 2796 2788 offset += field->offset + field->size; 2797 - #endif 2798 2789 } 2799 2790 2800 2791 return sample->raw_data + offset;
-2
tools/perf/util/evsel.h
··· 105 105 * metric fields are similar, but needs more care as they can have 106 106 * references to other metric (evsel). 107 107 */ 108 - const char * metric_expr; 109 - const char * metric_name; 110 108 struct evsel **metric_events; 111 109 struct evsel *metric_leader; 112 110
+2 -2
tools/perf/util/evswitch.h
··· 22 22 23 23 #define OPTS_EVSWITCH(evswitch) \ 24 24 OPT_STRING(0, "switch-on", &(evswitch)->on_name, \ 25 - "event", "Consider events after the ocurrence of this event"), \ 25 + "event", "Consider events after the occurrence of this event"), \ 26 26 OPT_STRING(0, "switch-off", &(evswitch)->off_name, \ 27 - "event", "Stop considering events after the ocurrence of this event"), \ 27 + "event", "Stop considering events after the occurrence of this event"), \ 28 28 OPT_BOOLEAN(0, "show-on-off-events", &(evswitch)->show_on_off_events, \ 29 29 "Show the on/off switch events, used with --switch-on and --switch-off") 30 30
+5
tools/perf/util/expr.c
··· 19 19 #include <linux/zalloc.h> 20 20 #include <ctype.h> 21 21 #include <math.h> 22 + #include "pmu.h" 22 23 23 24 #ifdef PARSER_DEBUG 24 25 extern int expr_debug; ··· 447 446 } 448 447 if (!strcmp("#num_cores", literal)) { 449 448 result = topology->core_cpus_lists; 449 + goto out; 450 + } 451 + if (!strcmp("#slots", literal)) { 452 + result = perf_pmu__cpu_slots_per_cycle(); 450 453 goto out; 451 454 } 452 455
+1
tools/perf/util/expr.h
··· 9 9 char *user_requested_cpu_list; 10 10 int runtime; 11 11 bool system_wide; 12 + bool is_test; 12 13 }; 13 14 14 15 struct expr_parse_ctx {
+5 -3
tools/perf/util/expr.l
··· 87 87 YYSTYPE *yylval = expr_get_lval(scanner); 88 88 89 89 yylval->num = expr__get_literal(expr_get_text(scanner), sctx); 90 - if (isnan(yylval->num)) 91 - return EXPR_ERROR; 92 - 90 + if (isnan(yylval->num)) { 91 + if (!sctx->is_test) 92 + return EXPR_ERROR; 93 + yylval->num = 1; 94 + } 93 95 return LITERAL; 94 96 } 95 97 %}
+68 -5
tools/perf/util/intel-pt.c
··· 5 5 */ 6 6 7 7 #include <inttypes.h> 8 + #include <linux/perf_event.h> 8 9 #include <stdio.h> 9 10 #include <stdbool.h> 10 11 #include <errno.h> ··· 98 97 bool sample_instructions; 99 98 u64 instructions_sample_type; 100 99 u64 instructions_id; 100 + 101 + bool sample_cycles; 102 + u64 cycles_sample_type; 103 + u64 cycles_id; 101 104 102 105 bool sample_branches; 103 106 u32 branches_filter; ··· 219 214 u64 ipc_cyc_cnt; 220 215 u64 last_in_insn_cnt; 221 216 u64 last_in_cyc_cnt; 217 + u64 last_cy_insn_cnt; 218 + u64 last_cy_cyc_cnt; 222 219 u64 last_br_insn_cnt; 223 220 u64 last_br_cyc_cnt; 224 221 unsigned int cbr_seen; ··· 1326 1319 if (pt->filts.cnt > 0) 1327 1320 params.pgd_ip = intel_pt_pgd_ip; 1328 1321 1329 - if (pt->synth_opts.instructions) { 1322 + if (pt->synth_opts.instructions || pt->synth_opts.cycles) { 1330 1323 if (pt->synth_opts.period) { 1331 1324 switch (pt->synth_opts.period_type) { 1332 1325 case PERF_ITRACE_PERIOD_INSTRUCTIONS: ··· 1835 1828 1836 1829 return intel_pt_deliver_synth_event(pt, event, &sample, 1837 1830 pt->instructions_sample_type); 1831 + } 1832 + 1833 + static int intel_pt_synth_cycle_sample(struct intel_pt_queue *ptq) 1834 + { 1835 + struct intel_pt *pt = ptq->pt; 1836 + union perf_event *event = ptq->event_buf; 1837 + struct perf_sample sample = { .ip = 0, }; 1838 + u64 period = 0; 1839 + 1840 + if (ptq->sample_ipc) 1841 + period = ptq->ipc_cyc_cnt - ptq->last_cy_cyc_cnt; 1842 + 1843 + if (!period || intel_pt_skip_event(pt)) 1844 + return 0; 1845 + 1846 + intel_pt_prep_sample(pt, ptq, event, &sample); 1847 + 1848 + sample.id = ptq->pt->cycles_id; 1849 + sample.stream_id = ptq->pt->cycles_id; 1850 + sample.period = period; 1851 + 1852 + sample.cyc_cnt = period; 1853 + sample.insn_cnt = ptq->ipc_insn_cnt - ptq->last_cy_insn_cnt; 1854 + ptq->last_cy_insn_cnt = ptq->ipc_insn_cnt; 1855 + ptq->last_cy_cyc_cnt = ptq->ipc_cyc_cnt; 1856 + 1857 + return intel_pt_deliver_synth_event(pt, event, &sample, pt->cycles_sample_type); 1838 1858 } 1839 1859 1840 1860 static int intel_pt_synth_transaction_sample(struct intel_pt_queue *ptq) ··· 2632 2598 } 2633 2599 } 2634 2600 2635 - if (pt->sample_instructions && (state->type & INTEL_PT_INSTRUCTION)) { 2636 - err = intel_pt_synth_instruction_sample(ptq); 2637 - if (err) 2638 - return err; 2601 + if (state->type & INTEL_PT_INSTRUCTION) { 2602 + if (pt->sample_instructions) { 2603 + err = intel_pt_synth_instruction_sample(ptq); 2604 + if (err) 2605 + return err; 2606 + } 2607 + if (pt->sample_cycles) { 2608 + err = intel_pt_synth_cycle_sample(ptq); 2609 + if (err) 2610 + return err; 2611 + } 2639 2612 } 2640 2613 2641 2614 if (pt->sample_transactions && (state->type & INTEL_PT_TRANSACTION)) { ··· 3772 3731 id += 1; 3773 3732 } 3774 3733 3734 + if (pt->synth_opts.cycles) { 3735 + attr.config = PERF_COUNT_HW_CPU_CYCLES; 3736 + if (pt->synth_opts.period_type == PERF_ITRACE_PERIOD_NANOSECS) 3737 + attr.sample_period = 3738 + intel_pt_ns_to_ticks(pt, pt->synth_opts.period); 3739 + else 3740 + attr.sample_period = pt->synth_opts.period; 3741 + err = intel_pt_synth_event(session, "cycles", &attr, id); 3742 + if (err) 3743 + return err; 3744 + pt->sample_cycles = true; 3745 + pt->cycles_sample_type = attr.sample_type; 3746 + pt->cycles_id = id; 3747 + id += 1; 3748 + } 3749 + 3775 3750 attr.sample_type &= ~(u64)PERF_SAMPLE_PERIOD; 3776 3751 attr.sample_period = 1; 3777 3752 ··· 4435 4378 goto err_delete_thread; 4436 4379 4437 4380 intel_pt_setup_pebs_events(pt); 4381 + 4382 + if (perf_data__is_pipe(session->data)) { 4383 + pr_warning("WARNING: Intel PT with pipe mode is not recommended.\n" 4384 + " The output cannot relied upon. In particular,\n" 4385 + " timestamps and the order of events may be incorrect.\n"); 4386 + } 4438 4387 4439 4388 if (pt->sampling_mode || list_empty(&session->auxtrace_index)) 4440 4389 err = auxtrace_queue_data(session, true, true);
+24 -1
tools/perf/util/llvm-utils.c
··· 531 531 532 532 pr_debug("llvm compiling command template: %s\n", template); 533 533 534 + /* 535 + * Below, substitute control characters for values that can cause the 536 + * echo to misbehave, then substitute the values back. 537 + */ 534 538 err = -ENOMEM; 535 - if (asprintf(&command_echo, "echo -n \"%s\"", template) < 0) 539 + if (asprintf(&command_echo, "echo -n \a%s\a", template) < 0) 536 540 goto errout; 537 541 542 + #define SWAP_CHAR(a, b) do { if (*p == a) *p = b; } while (0) 543 + for (char *p = command_echo; *p; p++) { 544 + SWAP_CHAR('<', '\001'); 545 + SWAP_CHAR('>', '\002'); 546 + SWAP_CHAR('"', '\003'); 547 + SWAP_CHAR('\'', '\004'); 548 + SWAP_CHAR('|', '\005'); 549 + SWAP_CHAR('&', '\006'); 550 + SWAP_CHAR('\a', '"'); 551 + } 538 552 err = read_from_pipe(command_echo, (void **) &command_out, NULL); 539 553 if (err) 540 554 goto errout; 541 555 556 + for (char *p = command_out; *p; p++) { 557 + SWAP_CHAR('\001', '<'); 558 + SWAP_CHAR('\002', '>'); 559 + SWAP_CHAR('\003', '"'); 560 + SWAP_CHAR('\004', '\''); 561 + SWAP_CHAR('\005', '|'); 562 + SWAP_CHAR('\006', '&'); 563 + } 564 + #undef SWAP_CHAR 542 565 pr_debug("llvm compiling command : %s\n", command_out); 543 566 544 567 err = read_from_pipe(template, &obj_buf, &obj_buf_sz);
+7
tools/perf/util/lock-contention.h
··· 65 65 */ 66 66 #define MAX_LOCK_DEPTH 48 67 67 68 + struct lock_stat *lock_stat_find(u64 addr); 69 + struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags); 70 + 71 + bool match_callstack_filter(struct machine *machine, u64 *callstack); 72 + 68 73 /* 69 74 * struct lock_seq_stat: 70 75 * Place to put on state of one lock sequence ··· 133 128 int max_stack; 134 129 int stack_skip; 135 130 int aggr_mode; 131 + int owner; 132 + bool save_callstack; 136 133 }; 137 134 138 135 #ifdef HAVE_BPF_SKEL
+102 -105
tools/perf/util/metricgroup.c
··· 167 167 " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); 168 168 } 169 169 170 - static bool metricgroup__has_constraint(const struct pmu_event *pe) 170 + static bool metricgroup__has_constraint(const struct pmu_metric *pm) 171 171 { 172 - if (!pe->metric_constraint) 172 + if (!pm->metric_constraint) 173 173 return false; 174 174 175 - if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") && 175 + if (!strcmp(pm->metric_constraint, "NO_NMI_WATCHDOG") && 176 176 sysctl__nmi_watchdog_enabled()) { 177 - metricgroup___watchdog_constraint_hint(pe->metric_name, false); 177 + metricgroup___watchdog_constraint_hint(pm->metric_name, false); 178 178 return true; 179 179 } 180 180 ··· 193 193 free(m); 194 194 } 195 195 196 - static struct metric *metric__new(const struct pmu_event *pe, 196 + static struct metric *metric__new(const struct pmu_metric *pm, 197 197 const char *modifier, 198 198 bool metric_no_group, 199 199 int runtime, ··· 210 210 if (!m->pctx) 211 211 goto out_err; 212 212 213 - m->metric_name = pe->metric_name; 213 + m->metric_name = pm->metric_name; 214 214 m->modifier = NULL; 215 215 if (modifier) { 216 216 m->modifier = strdup(modifier); 217 217 if (!m->modifier) 218 218 goto out_err; 219 219 } 220 - m->metric_expr = pe->metric_expr; 221 - m->metric_unit = pe->unit; 220 + m->metric_expr = pm->metric_expr; 221 + m->metric_unit = pm->unit; 222 222 m->pctx->sctx.user_requested_cpu_list = NULL; 223 223 if (user_requested_cpu_list) { 224 224 m->pctx->sctx.user_requested_cpu_list = strdup(user_requested_cpu_list); ··· 227 227 } 228 228 m->pctx->sctx.runtime = runtime; 229 229 m->pctx->sctx.system_wide = system_wide; 230 - m->has_constraint = metric_no_group || metricgroup__has_constraint(pe); 230 + m->has_constraint = metric_no_group || metricgroup__has_constraint(pm); 231 231 m->metric_refs = NULL; 232 232 m->evlist = NULL; 233 233 ··· 348 348 return false; 349 349 } 350 350 351 - static bool match_pe_metric(const struct pmu_event *pe, const char *metric) 351 + static bool match_pm_metric(const struct pmu_metric *pm, const char *metric) 352 352 { 353 - return match_metric(pe->metric_group, metric) || 354 - match_metric(pe->metric_name, metric); 353 + return match_metric(pm->metric_group, metric) || 354 + match_metric(pm->metric_name, metric); 355 355 } 356 356 357 357 /** struct mep - RB-tree node for building printing information. */ ··· 420 420 return NULL; 421 421 } 422 422 423 - static int metricgroup__add_to_mep_groups(const struct pmu_event *pe, 423 + static int metricgroup__add_to_mep_groups(const struct pmu_metric *pm, 424 424 struct rblist *groups) 425 425 { 426 426 const char *g; 427 427 char *omg, *mg; 428 428 429 - mg = strdup(pe->metric_group ?: "No_group"); 429 + mg = strdup(pm->metric_group ?: "No_group"); 430 430 if (!mg) 431 431 return -ENOMEM; 432 432 omg = mg; ··· 435 435 436 436 g = skip_spaces(g); 437 437 if (strlen(g)) 438 - me = mep_lookup(groups, g, pe->metric_name); 438 + me = mep_lookup(groups, g, pm->metric_name); 439 439 else 440 - me = mep_lookup(groups, "No_group", pe->metric_name); 440 + me = mep_lookup(groups, "No_group", pm->metric_name); 441 441 442 442 if (me) { 443 - me->metric_desc = pe->desc; 444 - me->metric_long_desc = pe->long_desc; 445 - me->metric_expr = pe->metric_expr; 446 - me->metric_unit = pe->unit; 443 + me->metric_desc = pm->desc; 444 + me->metric_long_desc = pm->long_desc; 445 + me->metric_expr = pm->metric_expr; 446 + me->metric_unit = pm->unit; 447 447 } 448 448 } 449 449 free(omg); ··· 452 452 } 453 453 454 454 struct metricgroup_iter_data { 455 - pmu_event_iter_fn fn; 455 + pmu_metric_iter_fn fn; 456 456 void *data; 457 457 }; 458 458 459 - static int metricgroup__sys_event_iter(const struct pmu_event *pe, 460 - const struct pmu_events_table *table, 459 + static int metricgroup__sys_event_iter(const struct pmu_metric *pm, 460 + const struct pmu_metrics_table *table, 461 461 void *data) 462 462 { 463 463 struct metricgroup_iter_data *d = data; 464 464 struct perf_pmu *pmu = NULL; 465 465 466 - if (!pe->metric_expr || !pe->compat) 466 + if (!pm->metric_expr || !pm->compat) 467 467 return 0; 468 468 469 469 while ((pmu = perf_pmu__scan(pmu))) { 470 470 471 - if (!pmu->id || strcmp(pmu->id, pe->compat)) 471 + if (!pmu->id || strcmp(pmu->id, pm->compat)) 472 472 continue; 473 473 474 - return d->fn(pe, table, d->data); 474 + return d->fn(pm, table, d->data); 475 475 } 476 476 return 0; 477 477 } 478 478 479 - static int metricgroup__add_to_mep_groups_callback(const struct pmu_event *pe, 480 - const struct pmu_events_table *table __maybe_unused, 481 - void *vdata) 479 + static int metricgroup__add_to_mep_groups_callback(const struct pmu_metric *pm, 480 + const struct pmu_metrics_table *table __maybe_unused, 481 + void *vdata) 482 482 { 483 483 struct rblist *groups = vdata; 484 484 485 - if (!pe->metric_name) 486 - return 0; 487 - 488 - return metricgroup__add_to_mep_groups(pe, groups); 485 + return metricgroup__add_to_mep_groups(pm, groups); 489 486 } 490 487 491 488 void metricgroup__print(const struct print_callbacks *print_cb, void *print_state) 492 489 { 493 490 struct rblist groups; 494 - const struct pmu_events_table *table; 491 + const struct pmu_metrics_table *table; 495 492 struct rb_node *node, *next; 496 493 497 494 rblist__init(&groups); 498 495 groups.node_new = mep_new; 499 496 groups.node_cmp = mep_cmp; 500 497 groups.node_delete = mep_delete; 501 - table = pmu_events_table__find(); 498 + table = pmu_metrics_table__find(); 502 499 if (table) { 503 - pmu_events_table_for_each_event(table, 504 - metricgroup__add_to_mep_groups_callback, 505 - &groups); 500 + pmu_metrics_table_for_each_metric(table, 501 + metricgroup__add_to_mep_groups_callback, 502 + &groups); 506 503 } 507 504 { 508 505 struct metricgroup_iter_data data = { 509 506 .fn = metricgroup__add_to_mep_groups_callback, 510 507 .data = &groups, 511 508 }; 512 - pmu_for_each_sys_event(metricgroup__sys_event_iter, &data); 509 + pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data); 513 510 } 514 511 515 512 for (node = rb_first_cached(&groups.entries); node; node = next) { ··· 740 743 #undef RETURN_IF_NON_ZERO 741 744 } 742 745 743 - int __weak arch_get_runtimeparam(const struct pmu_event *pe __maybe_unused) 746 + int __weak arch_get_runtimeparam(const struct pmu_metric *pm __maybe_unused) 744 747 { 745 748 return 1; 746 749 } ··· 765 768 bool system_wide; 766 769 struct metric *root_metric; 767 770 const struct visited_metric *visited; 768 - const struct pmu_events_table *table; 771 + const struct pmu_metrics_table *table; 769 772 }; 770 773 771 774 static bool metricgroup__find_metric(const char *metric, 772 - const struct pmu_events_table *table, 773 - struct pmu_event *pe); 775 + const struct pmu_metrics_table *table, 776 + struct pmu_metric *pm); 774 777 775 778 static int add_metric(struct list_head *metric_list, 776 - const struct pmu_event *pe, 779 + const struct pmu_metric *pm, 777 780 const char *modifier, 778 781 bool metric_no_group, 779 782 const char *user_requested_cpu_list, 780 783 bool system_wide, 781 784 struct metric *root_metric, 782 785 const struct visited_metric *visited, 783 - const struct pmu_events_table *table); 786 + const struct pmu_metrics_table *table); 784 787 785 788 /** 786 789 * resolve_metric - Locate metrics within the root metric and recursively add ··· 807 810 bool system_wide, 808 811 struct metric *root_metric, 809 812 const struct visited_metric *visited, 810 - const struct pmu_events_table *table) 813 + const struct pmu_metrics_table *table) 811 814 { 812 815 struct hashmap_entry *cur; 813 816 size_t bkt; 814 817 struct to_resolve { 815 818 /* The metric to resolve. */ 816 - struct pmu_event pe; 819 + struct pmu_metric pm; 817 820 /* 818 821 * The key in the IDs map, this may differ from in case, 819 - * etc. from pe->metric_name. 822 + * etc. from pm->metric_name. 820 823 */ 821 824 const char *key; 822 825 } *pending = NULL; ··· 827 830 * the pending array. 828 831 */ 829 832 hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) { 830 - struct pmu_event pe; 833 + struct pmu_metric pm; 831 834 832 - if (metricgroup__find_metric(cur->pkey, table, &pe)) { 835 + if (metricgroup__find_metric(cur->pkey, table, &pm)) { 833 836 pending = realloc(pending, 834 837 (pending_cnt + 1) * sizeof(struct to_resolve)); 835 838 if (!pending) 836 839 return -ENOMEM; 837 840 838 - memcpy(&pending[pending_cnt].pe, &pe, sizeof(pe)); 841 + memcpy(&pending[pending_cnt].pm, &pm, sizeof(pm)); 839 842 pending[pending_cnt].key = cur->pkey; 840 843 pending_cnt++; 841 844 } ··· 850 853 * context. 851 854 */ 852 855 for (i = 0; i < pending_cnt; i++) { 853 - ret = add_metric(metric_list, &pending[i].pe, modifier, metric_no_group, 856 + ret = add_metric(metric_list, &pending[i].pm, modifier, metric_no_group, 854 857 user_requested_cpu_list, system_wide, root_metric, visited, 855 858 table); 856 859 if (ret) ··· 864 867 /** 865 868 * __add_metric - Add a metric to metric_list. 866 869 * @metric_list: The list the metric is added to. 867 - * @pe: The pmu_event containing the metric to be added. 870 + * @pm: The pmu_metric containing the metric to be added. 868 871 * @modifier: if non-null event modifiers like "u". 869 872 * @metric_no_group: Should events written to events be grouped "{}" or 870 873 * global. Grouping is the default but due to multiplexing the ··· 881 884 * architecture perf is running upon. 882 885 */ 883 886 static int __add_metric(struct list_head *metric_list, 884 - const struct pmu_event *pe, 887 + const struct pmu_metric *pm, 885 888 const char *modifier, 886 889 bool metric_no_group, 887 890 int runtime, ··· 889 892 bool system_wide, 890 893 struct metric *root_metric, 891 894 const struct visited_metric *visited, 892 - const struct pmu_events_table *table) 895 + const struct pmu_metrics_table *table) 893 896 { 894 897 const struct visited_metric *vm; 895 898 int ret; 896 899 bool is_root = !root_metric; 897 900 struct visited_metric visited_node = { 898 - .name = pe->metric_name, 901 + .name = pm->metric_name, 899 902 .parent = visited, 900 903 }; 901 904 902 905 for (vm = visited; vm; vm = vm->parent) { 903 - if (!strcmp(pe->metric_name, vm->name)) { 904 - pr_err("failed: recursion detected for %s\n", pe->metric_name); 906 + if (!strcmp(pm->metric_name, vm->name)) { 907 + pr_err("failed: recursion detected for %s\n", pm->metric_name); 905 908 return -1; 906 909 } 907 910 } ··· 911 914 * This metric is the root of a tree and may reference other 912 915 * metrics that are added recursively. 913 916 */ 914 - root_metric = metric__new(pe, modifier, metric_no_group, runtime, 917 + root_metric = metric__new(pm, modifier, metric_no_group, runtime, 915 918 user_requested_cpu_list, system_wide); 916 919 if (!root_metric) 917 920 return -ENOMEM; ··· 926 929 */ 927 930 if (root_metric->metric_refs) { 928 931 for (; root_metric->metric_refs[cnt].metric_name; cnt++) { 929 - if (!strcmp(pe->metric_name, 932 + if (!strcmp(pm->metric_name, 930 933 root_metric->metric_refs[cnt].metric_name)) 931 934 return 0; 932 935 } ··· 944 947 * need to change them, so there's no need to create 945 948 * our own copy. 946 949 */ 947 - root_metric->metric_refs[cnt].metric_name = pe->metric_name; 948 - root_metric->metric_refs[cnt].metric_expr = pe->metric_expr; 950 + root_metric->metric_refs[cnt].metric_name = pm->metric_name; 951 + root_metric->metric_refs[cnt].metric_expr = pm->metric_expr; 949 952 950 953 /* Null terminate array. */ 951 954 root_metric->metric_refs[cnt+1].metric_name = NULL; ··· 956 959 * For both the parent and referenced metrics, we parse 957 960 * all the metric's IDs and add it to the root context. 958 961 */ 959 - if (expr__find_ids(pe->metric_expr, NULL, root_metric->pctx) < 0) { 962 + if (expr__find_ids(pm->metric_expr, NULL, root_metric->pctx) < 0) { 960 963 /* Broken metric. */ 961 964 ret = -EINVAL; 962 965 } else { ··· 978 981 979 982 struct metricgroup__find_metric_data { 980 983 const char *metric; 981 - struct pmu_event *pe; 984 + struct pmu_metric *pm; 982 985 }; 983 986 984 - static int metricgroup__find_metric_callback(const struct pmu_event *pe, 985 - const struct pmu_events_table *table __maybe_unused, 987 + static int metricgroup__find_metric_callback(const struct pmu_metric *pm, 988 + const struct pmu_metrics_table *table __maybe_unused, 986 989 void *vdata) 987 990 { 988 991 struct metricgroup__find_metric_data *data = vdata; 989 992 990 - if (!match_metric(pe->metric_name, data->metric)) 993 + if (!match_metric(pm->metric_name, data->metric)) 991 994 return 0; 992 995 993 - memcpy(data->pe, pe, sizeof(*pe)); 996 + memcpy(data->pm, pm, sizeof(*pm)); 994 997 return 1; 995 998 } 996 999 997 1000 static bool metricgroup__find_metric(const char *metric, 998 - const struct pmu_events_table *table, 999 - struct pmu_event *pe) 1001 + const struct pmu_metrics_table *table, 1002 + struct pmu_metric *pm) 1000 1003 { 1001 1004 struct metricgroup__find_metric_data data = { 1002 1005 .metric = metric, 1003 - .pe = pe, 1006 + .pm = pm, 1004 1007 }; 1005 1008 1006 - return pmu_events_table_for_each_event(table, metricgroup__find_metric_callback, &data) 1009 + return pmu_metrics_table_for_each_metric(table, metricgroup__find_metric_callback, &data) 1007 1010 ? true : false; 1008 1011 } 1009 1012 1010 1013 static int add_metric(struct list_head *metric_list, 1011 - const struct pmu_event *pe, 1014 + const struct pmu_metric *pm, 1012 1015 const char *modifier, 1013 1016 bool metric_no_group, 1014 1017 const char *user_requested_cpu_list, 1015 1018 bool system_wide, 1016 1019 struct metric *root_metric, 1017 1020 const struct visited_metric *visited, 1018 - const struct pmu_events_table *table) 1021 + const struct pmu_metrics_table *table) 1019 1022 { 1020 1023 int ret = 0; 1021 1024 1022 - pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name); 1025 + pr_debug("metric expr %s for %s\n", pm->metric_expr, pm->metric_name); 1023 1026 1024 - if (!strstr(pe->metric_expr, "?")) { 1025 - ret = __add_metric(metric_list, pe, modifier, metric_no_group, 0, 1027 + if (!strstr(pm->metric_expr, "?")) { 1028 + ret = __add_metric(metric_list, pm, modifier, metric_no_group, 0, 1026 1029 user_requested_cpu_list, system_wide, root_metric, 1027 1030 visited, table); 1028 1031 } else { 1029 1032 int j, count; 1030 1033 1031 - count = arch_get_runtimeparam(pe); 1034 + count = arch_get_runtimeparam(pm); 1032 1035 1033 1036 /* This loop is added to create multiple 1034 1037 * events depend on count value and add ··· 1036 1039 */ 1037 1040 1038 1041 for (j = 0; j < count && !ret; j++) 1039 - ret = __add_metric(metric_list, pe, modifier, metric_no_group, j, 1042 + ret = __add_metric(metric_list, pm, modifier, metric_no_group, j, 1040 1043 user_requested_cpu_list, system_wide, 1041 1044 root_metric, visited, table); 1042 1045 } ··· 1044 1047 return ret; 1045 1048 } 1046 1049 1047 - static int metricgroup__add_metric_sys_event_iter(const struct pmu_event *pe, 1048 - const struct pmu_events_table *table __maybe_unused, 1049 - void *data) 1050 + static int metricgroup__add_metric_sys_event_iter(const struct pmu_metric *pm, 1051 + const struct pmu_metrics_table *table __maybe_unused, 1052 + void *data) 1050 1053 { 1051 1054 struct metricgroup_add_iter_data *d = data; 1052 1055 int ret; 1053 1056 1054 - if (!match_pe_metric(pe, d->metric_name)) 1057 + if (!match_pm_metric(pm, d->metric_name)) 1055 1058 return 0; 1056 1059 1057 - ret = add_metric(d->metric_list, pe, d->modifier, d->metric_no_group, 1060 + ret = add_metric(d->metric_list, pm, d->modifier, d->metric_no_group, 1058 1061 d->user_requested_cpu_list, d->system_wide, 1059 1062 d->root_metric, d->visited, d->table); 1060 1063 if (ret) ··· 1104 1107 bool has_match; 1105 1108 }; 1106 1109 1107 - static int metricgroup__add_metric_callback(const struct pmu_event *pe, 1108 - const struct pmu_events_table *table, 1110 + static int metricgroup__add_metric_callback(const struct pmu_metric *pm, 1111 + const struct pmu_metrics_table *table, 1109 1112 void *vdata) 1110 1113 { 1111 1114 struct metricgroup__add_metric_data *data = vdata; 1112 1115 int ret = 0; 1113 1116 1114 - if (pe->metric_expr && 1115 - (match_metric(pe->metric_group, data->metric_name) || 1116 - match_metric(pe->metric_name, data->metric_name))) { 1117 + if (pm->metric_expr && 1118 + (match_metric(pm->metric_group, data->metric_name) || 1119 + match_metric(pm->metric_name, data->metric_name))) { 1117 1120 1118 1121 data->has_match = true; 1119 - ret = add_metric(data->list, pe, data->modifier, data->metric_no_group, 1122 + ret = add_metric(data->list, pm, data->modifier, data->metric_no_group, 1120 1123 data->user_requested_cpu_list, data->system_wide, 1121 1124 /*root_metric=*/NULL, /*visited_metrics=*/NULL, table); 1122 1125 } ··· 1143 1146 const char *user_requested_cpu_list, 1144 1147 bool system_wide, 1145 1148 struct list_head *metric_list, 1146 - const struct pmu_events_table *table) 1149 + const struct pmu_metrics_table *table) 1147 1150 { 1148 1151 LIST_HEAD(list); 1149 1152 int ret; ··· 1163 1166 * Iterate over all metrics seeing if metric matches either the 1164 1167 * name or group. When it does add the metric to the list. 1165 1168 */ 1166 - ret = pmu_events_table_for_each_event(table, metricgroup__add_metric_callback, 1167 - &data); 1169 + ret = pmu_metrics_table_for_each_metric(table, metricgroup__add_metric_callback, 1170 + &data); 1168 1171 if (ret) 1169 1172 goto out; 1170 1173 ··· 1186 1189 }, 1187 1190 }; 1188 1191 1189 - pmu_for_each_sys_event(metricgroup__sys_event_iter, &data); 1192 + pmu_for_each_sys_metric(metricgroup__sys_event_iter, &data); 1190 1193 } 1191 1194 /* End of pmu events. */ 1192 1195 if (!has_match) ··· 1219 1222 static int metricgroup__add_metric_list(const char *list, bool metric_no_group, 1220 1223 const char *user_requested_cpu_list, 1221 1224 bool system_wide, struct list_head *metric_list, 1222 - const struct pmu_events_table *table) 1225 + const struct pmu_metrics_table *table) 1223 1226 { 1224 1227 char *list_itr, *list_copy, *metric_name, *modifier; 1225 1228 int ret, count = 0; ··· 1429 1432 bool system_wide, 1430 1433 struct perf_pmu *fake_pmu, 1431 1434 struct rblist *metric_events_list, 1432 - const struct pmu_events_table *table) 1435 + const struct pmu_metrics_table *table) 1433 1436 { 1434 1437 struct evlist *combined_evlist = NULL; 1435 1438 LIST_HEAD(metric_list); ··· 1577 1580 bool system_wide, 1578 1581 struct rblist *metric_events) 1579 1582 { 1580 - const struct pmu_events_table *table = pmu_events_table__find(); 1583 + const struct pmu_metrics_table *table = pmu_metrics_table__find(); 1581 1584 1582 1585 if (!table) 1583 1586 return -EINVAL; ··· 1588 1591 } 1589 1592 1590 1593 int metricgroup__parse_groups_test(struct evlist *evlist, 1591 - const struct pmu_events_table *table, 1594 + const struct pmu_metrics_table *table, 1592 1595 const char *str, 1593 1596 bool metric_no_group, 1594 1597 bool metric_no_merge, ··· 1600 1603 &perf_pmu__fake, metric_events, table); 1601 1604 } 1602 1605 1603 - static int metricgroup__has_metric_callback(const struct pmu_event *pe, 1604 - const struct pmu_events_table *table __maybe_unused, 1606 + static int metricgroup__has_metric_callback(const struct pmu_metric *pm, 1607 + const struct pmu_metrics_table *table __maybe_unused, 1605 1608 void *vdata) 1606 1609 { 1607 1610 const char *metric = vdata; 1608 1611 1609 - if (!pe->metric_expr) 1612 + if (!pm->metric_expr) 1610 1613 return 0; 1611 1614 1612 - if (match_metric(pe->metric_name, metric)) 1615 + if (match_metric(pm->metric_name, metric)) 1613 1616 return 1; 1614 1617 1615 1618 return 0; ··· 1617 1620 1618 1621 bool metricgroup__has_metric(const char *metric) 1619 1622 { 1620 - const struct pmu_events_table *table = pmu_events_table__find(); 1623 + const struct pmu_metrics_table *table = pmu_metrics_table__find(); 1621 1624 1622 1625 if (!table) 1623 1626 return false; 1624 1627 1625 - return pmu_events_table_for_each_event(table, metricgroup__has_metric_callback, 1626 - (void *)metric) ? true : false; 1628 + return pmu_metrics_table_for_each_metric(table, metricgroup__has_metric_callback, 1629 + (void *)metric) ? true : false; 1627 1630 } 1628 1631 1629 1632 int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
+2 -2
tools/perf/util/metricgroup.h
··· 73 73 bool system_wide, 74 74 struct rblist *metric_events); 75 75 int metricgroup__parse_groups_test(struct evlist *evlist, 76 - const struct pmu_events_table *table, 76 + const struct pmu_metrics_table *table, 77 77 const char *str, 78 78 bool metric_no_group, 79 79 bool metric_no_merge, ··· 81 81 82 82 void metricgroup__print(const struct print_callbacks *print_cb, void *print_state); 83 83 bool metricgroup__has_metric(const char *metric); 84 - int arch_get_runtimeparam(const struct pmu_event *pe __maybe_unused); 84 + int arch_get_runtimeparam(const struct pmu_metric *pm); 85 85 void metricgroup__rblist_exit(struct rblist *metric_events); 86 86 87 87 int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp,
-2
tools/perf/util/parse-events.c
··· 1570 1570 evsel->scale = info.scale; 1571 1571 evsel->per_pkg = info.per_pkg; 1572 1572 evsel->snapshot = info.snapshot; 1573 - evsel->metric_expr = info.metric_expr; 1574 - evsel->metric_name = info.metric_name; 1575 1573 return 0; 1576 1574 } 1577 1575
+2 -4
tools/perf/util/pfm.c
··· 193 193 /*scale_unit=*/NULL, 194 194 /*deprecated=*/NULL, "PFM event", 195 195 info->desc, /*long_desc=*/NULL, 196 - /*encoding_desc=*/buf->buf, 197 - /*metric_name=*/NULL, /*metric_expr=*/NULL); 196 + /*encoding_desc=*/buf->buf); 198 197 199 198 pfm_for_each_event_attr(j, info) { 200 199 pfm_event_attr_info_t ainfo; ··· 223 224 /*scale_unit=*/NULL, 224 225 /*deprecated=*/NULL, "PFM event", 225 226 ainfo.desc, /*long_desc=*/NULL, 226 - /*encoding_desc=*/buf->buf, 227 - /*metric_name=*/NULL, /*metric_expr=*/NULL); 227 + /*encoding_desc=*/buf->buf); 228 228 } 229 229 } 230 230 }
+5 -22
tools/perf/util/pmu-hybrid.c
··· 20 20 21 21 bool perf_pmu__hybrid_mounted(const char *name) 22 22 { 23 - char path[PATH_MAX]; 24 - const char *sysfs; 25 - FILE *file; 26 - int n, cpu; 23 + int cpu; 24 + char pmu_name[PATH_MAX]; 25 + struct perf_pmu pmu = {.name = pmu_name}; 27 26 28 27 if (strncmp(name, "cpu_", 4)) 29 28 return false; 30 29 31 - sysfs = sysfs__mountpoint(); 32 - if (!sysfs) 33 - return false; 34 - 35 - snprintf(path, PATH_MAX, CPUS_TEMPLATE_CPU, sysfs, name); 36 - if (!file_available(path)) 37 - return false; 38 - 39 - file = fopen(path, "r"); 40 - if (!file) 41 - return false; 42 - 43 - n = fscanf(file, "%u", &cpu); 44 - fclose(file); 45 - if (n <= 0) 46 - return false; 47 - 48 - return true; 30 + strlcpy(pmu_name, name, sizeof(pmu_name)); 31 + return perf_pmu__scan_file(&pmu, "cpus", "%u", &cpu) > 0; 49 32 } 50 33 51 34 struct perf_pmu *perf_pmu__find_hybrid_pmu(const char *name)
+84 -143
tools/perf/util/pmu.c
··· 19 19 #include <regex.h> 20 20 #include <perf/cpumap.h> 21 21 #include <fnmatch.h> 22 + #include <math.h> 22 23 #include "debug.h" 23 24 #include "evsel.h" 24 25 #include "pmu.h" ··· 108 107 static int pmu_format(const char *name, struct list_head *format) 109 108 { 110 109 char path[PATH_MAX]; 111 - const char *sysfs = sysfs__mountpoint(); 112 110 113 - if (!sysfs) 111 + if (!perf_pmu__pathname_scnprintf(path, sizeof(path), name, "format")) 114 112 return -1; 115 - 116 - snprintf(path, PATH_MAX, 117 - "%s" EVENT_SOURCE_DEVICE_PATH "%s/format", sysfs, name); 118 113 119 114 if (!file_available(path)) 120 115 return 0; ··· 280 283 perf_pmu_assign_str(old->name, "long_desc", &old->long_desc, 281 284 &newalias->long_desc); 282 285 perf_pmu_assign_str(old->name, "topic", &old->topic, &newalias->topic); 283 - perf_pmu_assign_str(old->name, "metric_expr", &old->metric_expr, 284 - &newalias->metric_expr); 285 - perf_pmu_assign_str(old->name, "metric_name", &old->metric_name, 286 - &newalias->metric_name); 287 286 perf_pmu_assign_str(old->name, "value", &old->str, &newalias->str); 288 287 old->scale = newalias->scale; 289 288 old->per_pkg = newalias->per_pkg; ··· 295 302 zfree(&newalias->long_desc); 296 303 zfree(&newalias->topic); 297 304 zfree(&newalias->str); 298 - zfree(&newalias->metric_expr); 299 - zfree(&newalias->metric_name); 300 305 zfree(&newalias->pmu_name); 301 306 parse_events_terms__purge(&newalias->terms); 302 307 free(newalias); ··· 331 340 int num; 332 341 char newval[256]; 333 342 char *long_desc = NULL, *topic = NULL, *unit = NULL, *perpkg = NULL, 334 - *metric_expr = NULL, *metric_name = NULL, *deprecated = NULL, 335 - *pmu_name = NULL; 343 + *deprecated = NULL, *pmu_name = NULL; 336 344 337 345 if (pe) { 338 346 long_desc = (char *)pe->long_desc; 339 347 topic = (char *)pe->topic; 340 348 unit = (char *)pe->unit; 341 349 perpkg = (char *)pe->perpkg; 342 - metric_expr = (char *)pe->metric_expr; 343 - metric_name = (char *)pe->metric_name; 344 350 deprecated = (char *)pe->deprecated; 345 351 pmu_name = (char *)pe->pmu; 346 352 } ··· 392 404 perf_pmu__parse_snapshot(alias, dir, name); 393 405 } 394 406 395 - alias->metric_expr = metric_expr ? strdup(metric_expr) : NULL; 396 - alias->metric_name = metric_name ? strdup(metric_name): NULL; 397 407 alias->desc = desc ? strdup(desc) : NULL; 398 408 alias->long_desc = long_desc ? strdup(long_desc) : 399 409 desc ? strdup(desc) : NULL; ··· 499 513 static int pmu_aliases(const char *name, struct list_head *head) 500 514 { 501 515 char path[PATH_MAX]; 502 - const char *sysfs = sysfs__mountpoint(); 503 516 504 - if (!sysfs) 517 + if (!perf_pmu__pathname_scnprintf(path, sizeof(path), name, "events")) 505 518 return -1; 506 - 507 - snprintf(path, PATH_MAX, 508 - "%s/bus/event_source/devices/%s/events", sysfs, name); 509 519 510 520 if (!file_available(path)) 511 521 return 0; ··· 536 554 return 0; 537 555 } 538 556 539 - /* 540 - * Reading/parsing the default pmu type value, which should be 541 - * located at: 542 - * /sys/bus/event_source/devices/<dev>/type as sysfs attribute. 543 - */ 544 - static int pmu_type(const char *name, __u32 *type) 545 - { 546 - char path[PATH_MAX]; 547 - FILE *file; 548 - int ret = 0; 549 - const char *sysfs = sysfs__mountpoint(); 550 - 551 - if (!sysfs) 552 - return -1; 553 - 554 - snprintf(path, PATH_MAX, 555 - "%s" EVENT_SOURCE_DEVICE_PATH "%s/type", sysfs, name); 556 - 557 - if (access(path, R_OK) < 0) 558 - return -1; 559 - 560 - file = fopen(path, "r"); 561 - if (!file) 562 - return -EINVAL; 563 - 564 - if (1 != fscanf(file, "%u", type)) 565 - ret = -1; 566 - 567 - fclose(file); 568 - return ret; 569 - } 570 - 571 557 /* Add all pmus in sysfs to pmu list: */ 572 558 static void pmu_read_sysfs(void) 573 559 { 574 560 char path[PATH_MAX]; 575 561 DIR *dir; 576 562 struct dirent *dent; 577 - const char *sysfs = sysfs__mountpoint(); 578 563 579 - if (!sysfs) 564 + if (!perf_pmu__event_source_devices_scnprintf(path, sizeof(path))) 580 565 return; 581 - 582 - snprintf(path, PATH_MAX, 583 - "%s" EVENT_SOURCE_DEVICE_PATH, sysfs); 584 566 585 567 dir = opendir(path); 586 568 if (!dir) ··· 560 614 closedir(dir); 561 615 } 562 616 563 - static struct perf_cpu_map *__pmu_cpumask(const char *path) 564 - { 565 - FILE *file; 566 - struct perf_cpu_map *cpus; 567 - 568 - file = fopen(path, "r"); 569 - if (!file) 570 - return NULL; 571 - 572 - cpus = perf_cpu_map__read(file); 573 - fclose(file); 574 - return cpus; 575 - } 576 - 577 617 /* 578 618 * Uncore PMUs have a "cpumask" file under sysfs. CPU PMUs (e.g. on arm/arm64) 579 619 * may have a "cpus" file. 580 620 */ 581 - #define SYS_TEMPLATE_ID "./bus/event_source/devices/%s/identifier" 582 - #define CPUS_TEMPLATE_UNCORE "%s/bus/event_source/devices/%s/cpumask" 583 - 584 621 static struct perf_cpu_map *pmu_cpumask(const char *name) 585 622 { 586 - char path[PATH_MAX]; 587 623 struct perf_cpu_map *cpus; 588 - const char *sysfs = sysfs__mountpoint(); 589 624 const char *templates[] = { 590 - CPUS_TEMPLATE_UNCORE, 591 - CPUS_TEMPLATE_CPU, 625 + "cpumask", 626 + "cpus", 592 627 NULL 593 628 }; 594 629 const char **template; 630 + char pmu_name[PATH_MAX]; 631 + struct perf_pmu pmu = {.name = pmu_name}; 632 + FILE *file; 595 633 596 - if (!sysfs) 597 - return NULL; 598 - 634 + strlcpy(pmu_name, name, sizeof(pmu_name)); 599 635 for (template = templates; *template; template++) { 600 - snprintf(path, PATH_MAX, *template, sysfs, name); 601 - cpus = __pmu_cpumask(path); 636 + file = perf_pmu__open_file(&pmu, *template); 637 + if (!file) 638 + continue; 639 + cpus = perf_cpu_map__read(file); 602 640 if (cpus) 603 641 return cpus; 604 642 } ··· 593 663 static bool pmu_is_uncore(const char *name) 594 664 { 595 665 char path[PATH_MAX]; 596 - const char *sysfs; 597 666 598 667 if (perf_pmu__hybrid_mounted(name)) 599 668 return false; 600 669 601 - sysfs = sysfs__mountpoint(); 602 - snprintf(path, PATH_MAX, CPUS_TEMPLATE_UNCORE, sysfs, name); 670 + perf_pmu__pathname_scnprintf(path, sizeof(path), name, "cpumask"); 603 671 return file_available(path); 604 672 } 605 673 ··· 606 678 char path[PATH_MAX], *str; 607 679 size_t len; 608 680 609 - snprintf(path, PATH_MAX, SYS_TEMPLATE_ID, name); 681 + perf_pmu__pathname_scnprintf(path, sizeof(path), name, "identifier"); 610 682 611 - if (sysfs__read_str(path, &str, &len) < 0) 683 + if (filename__read_str(path, &str, &len) < 0) 612 684 return NULL; 613 685 614 686 str[len - 1] = 0; /* remove line feed */ ··· 624 696 static int is_arm_pmu_core(const char *name) 625 697 { 626 698 char path[PATH_MAX]; 627 - const char *sysfs = sysfs__mountpoint(); 628 699 629 - if (!sysfs) 700 + if (!perf_pmu__pathname_scnprintf(path, sizeof(path), name, "cpus")) 630 701 return 0; 631 - 632 - /* Look for cpu sysfs (specific to arm) */ 633 - scnprintf(path, PATH_MAX, "%s/bus/event_source/devices/%s/cpus", 634 - sysfs, name); 635 702 return file_available(path); 636 703 } 637 704 ··· 652 729 653 730 __weak const struct pmu_events_table *pmu_events_table__find(void) 654 731 { 655 - return perf_pmu__find_table(NULL); 732 + return perf_pmu__find_events_table(NULL); 733 + } 734 + 735 + __weak const struct pmu_metrics_table *pmu_metrics_table__find(void) 736 + { 737 + return perf_pmu__find_metrics_table(NULL); 656 738 } 657 739 658 740 /* ··· 750 822 struct pmu_add_cpu_aliases_map_data *data = vdata; 751 823 const char *pname = pe->pmu ? pe->pmu : data->cpu_name; 752 824 753 - if (!pe->name) 754 - return 0; 755 - 756 825 if (data->pmu->is_uncore && pmu_uncore_alias_match(pname, data->name)) 757 826 goto new_alias; 758 827 ··· 785 860 { 786 861 const struct pmu_events_table *table; 787 862 788 - table = perf_pmu__find_table(pmu); 863 + table = perf_pmu__find_events_table(pmu); 789 864 if (!table) 790 865 return; 791 866 ··· 803 878 { 804 879 struct pmu_sys_event_iter_data *idata = data; 805 880 struct perf_pmu *pmu = idata->pmu; 806 - 807 - if (!pe->name) { 808 - if (pe->metric_group || pe->metric_name) 809 - return 0; 810 - return -EINVAL; 811 - } 812 881 813 882 if (!pe->compat || !pe->pmu) 814 883 return 0; ··· 850 931 return NULL; 851 932 } 852 933 853 - static int pmu_max_precise(const char *name) 934 + static int pmu_max_precise(struct perf_pmu *pmu) 854 935 { 855 - char path[PATH_MAX]; 856 936 int max_precise = -1; 857 937 858 - scnprintf(path, PATH_MAX, 859 - "bus/event_source/devices/%s/caps/max_precise", 860 - name); 861 - 862 - sysfs__read_int(path, &max_precise); 938 + perf_pmu__scan_file(pmu, "caps/max_precise", "%d", &max_precise); 863 939 return max_precise; 864 940 } 865 941 ··· 883 969 return NULL; 884 970 885 971 /* 886 - * Check the type first to avoid unnecessary work. 972 + * Check the aliases first to avoid unnecessary work. 887 973 */ 888 - if (pmu_type(name, &type)) 889 - return NULL; 890 - 891 974 if (pmu_aliases(name, &aliases)) 892 975 return NULL; 893 976 ··· 894 983 895 984 pmu->cpus = pmu_cpumask(name); 896 985 pmu->name = strdup(name); 986 + 897 987 if (!pmu->name) 988 + goto err; 989 + 990 + /* Read type, and ensure that type value is successfully assigned (return 1) */ 991 + if (perf_pmu__scan_file(pmu, "type", "%u", &type) != 1) 898 992 goto err; 899 993 900 994 alias_name = pmu_find_alias_name(name); ··· 913 997 pmu->is_uncore = pmu_is_uncore(name); 914 998 if (pmu->is_uncore) 915 999 pmu->id = pmu_id(name); 916 - pmu->max_precise = pmu_max_precise(name); 1000 + pmu->max_precise = pmu_max_precise(pmu); 917 1001 pmu_add_cpu_aliases(&aliases, pmu); 918 1002 pmu_add_sys_aliases(&aliases, pmu); 919 1003 ··· 1385 1469 info->unit = NULL; 1386 1470 info->scale = 0.0; 1387 1471 info->snapshot = false; 1388 - info->metric_expr = NULL; 1389 - info->metric_name = NULL; 1390 1472 1391 1473 list_for_each_entry_safe(term, h, head_terms, list) { 1392 1474 alias = pmu_find_alias(pmu, term); ··· 1400 1486 1401 1487 if (alias->per_pkg) 1402 1488 info->per_pkg = true; 1403 - info->metric_expr = alias->metric_expr; 1404 - info->metric_name = alias->metric_name; 1405 1489 1406 1490 list_del_init(&term->list); 1407 1491 parse_events_term__delete(term); ··· 1615 1703 for (j = 0; j < len; j++) { 1616 1704 const char *name, *alias = NULL, *scale_unit = NULL, 1617 1705 *desc = NULL, *long_desc = NULL, 1618 - *encoding_desc = NULL, *topic = NULL, 1619 - *metric_name = NULL, *metric_expr = NULL; 1706 + *encoding_desc = NULL, *topic = NULL; 1620 1707 bool deprecated = false; 1621 1708 size_t buf_used; 1622 1709 ··· 1653 1742 buf_used += snprintf(buf + buf_used, sizeof(buf) - buf_used, 1654 1743 "%s/%s/", aliases[j].pmu->name, 1655 1744 aliases[j].event->str) + 1; 1656 - metric_name = aliases[j].event->metric_name; 1657 - metric_expr = aliases[j].event->metric_expr; 1658 1745 deprecated = aliases[j].event->deprecated; 1659 1746 } 1660 1747 print_cb->print_event(print_state, ··· 1665 1756 "Kernel PMU event", 1666 1757 desc, 1667 1758 long_desc, 1668 - encoding_desc, 1669 - metric_name, 1670 - metric_expr); 1759 + encoding_desc); 1671 1760 } 1672 1761 if (printed && pager_in_use()) 1673 1762 printf("\n"); ··· 1690 1783 return false; 1691 1784 } 1692 1785 1693 - static FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name) 1786 + FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name) 1694 1787 { 1695 1788 char path[PATH_MAX]; 1696 - const char *sysfs; 1697 1789 1698 - sysfs = sysfs__mountpoint(); 1699 - if (!sysfs) 1790 + if (!perf_pmu__pathname_scnprintf(path, sizeof(path), pmu->name, name) || 1791 + !file_available(path)) 1700 1792 return NULL; 1701 1793 1702 - snprintf(path, PATH_MAX, 1703 - "%s" EVENT_SOURCE_DEVICE_PATH "%s/%s", sysfs, pmu->name, name); 1704 - if (!file_available(path)) 1705 - return NULL; 1706 1794 return fopen(path, "r"); 1707 1795 } 1708 1796 ··· 1716 1814 } 1717 1815 va_end(args); 1718 1816 return ret; 1817 + } 1818 + 1819 + bool perf_pmu__file_exists(struct perf_pmu *pmu, const char *name) 1820 + { 1821 + char path[PATH_MAX]; 1822 + 1823 + if (!perf_pmu__pathname_scnprintf(path, sizeof(path), pmu->name, name)) 1824 + return false; 1825 + 1826 + return file_available(path); 1719 1827 } 1720 1828 1721 1829 static int perf_pmu__new_caps(struct list_head *list, char *name, char *value) ··· 1761 1849 { 1762 1850 struct stat st; 1763 1851 char caps_path[PATH_MAX]; 1764 - const char *sysfs = sysfs__mountpoint(); 1765 1852 DIR *caps_dir; 1766 1853 struct dirent *evt_ent; 1767 1854 ··· 1769 1858 1770 1859 pmu->nr_caps = 0; 1771 1860 1772 - if (!sysfs) 1861 + if (!perf_pmu__pathname_scnprintf(caps_path, sizeof(caps_path), pmu->name, "caps")) 1773 1862 return -1; 1774 - 1775 - snprintf(caps_path, PATH_MAX, 1776 - "%s" EVENT_SOURCE_DEVICE_PATH "%s/caps", sysfs, pmu->name); 1777 1863 1778 1864 if (stat(caps_path, &st) < 0) { 1779 1865 pmu->caps_initialized = true; ··· 1900 1992 *mcpus_ptr = matched_cpus; 1901 1993 *ucpus_ptr = unmatched_cpus; 1902 1994 return 0; 1995 + } 1996 + 1997 + double __weak perf_pmu__cpu_slots_per_cycle(void) 1998 + { 1999 + return NAN; 2000 + } 2001 + 2002 + int perf_pmu__event_source_devices_scnprintf(char *pathname, size_t size) 2003 + { 2004 + const char *sysfs = sysfs__mountpoint(); 2005 + 2006 + if (!sysfs) 2007 + return 0; 2008 + return scnprintf(pathname, size, "%s/bus/event_source/devices/", sysfs); 2009 + } 2010 + 2011 + /* 2012 + * Fill 'buf' with the path to a file or folder in 'pmu_name' in 2013 + * sysfs. For example if pmu_name = "cs_etm" and 'filename' = "format" 2014 + * then pathname will be filled with 2015 + * "/sys/bus/event_source/devices/cs_etm/format" 2016 + * 2017 + * Return 0 if the sysfs mountpoint couldn't be found or if no 2018 + * characters were written. 2019 + */ 2020 + int perf_pmu__pathname_scnprintf(char *buf, size_t size, 2021 + const char *pmu_name, const char *filename) 2022 + { 2023 + char base_path[PATH_MAX]; 2024 + 2025 + if (!perf_pmu__event_source_devices_scnprintf(base_path, sizeof(base_path))) 2026 + return 0; 2027 + return scnprintf(buf, size, "%s%s/%s", base_path, pmu_name, filename); 1903 2028 }
+10 -11
tools/perf/util/pmu.h
··· 7 7 #include <linux/perf_event.h> 8 8 #include <linux/list.h> 9 9 #include <stdbool.h> 10 + #include <stdio.h> 10 11 #include "parse-events.h" 11 12 #include "pmu-events/pmu-events.h" 12 13 ··· 23 22 }; 24 23 25 24 #define PERF_PMU_FORMAT_BITS 64 26 - #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/" 27 - #define CPUS_TEMPLATE_CPU "%s/bus/event_source/devices/%s/cpus" 28 25 #define MAX_PMU_NAME_LEN 128 29 26 30 27 struct perf_event_attr; ··· 132 133 133 134 struct perf_pmu_info { 134 135 const char *unit; 135 - const char *metric_expr; 136 - const char *metric_name; 137 136 double scale; 138 137 bool per_pkg; 139 138 bool snapshot; ··· 185 188 * default. 186 189 */ 187 190 bool deprecated; 188 - /** 189 - * @metric_expr: A metric expression associated with an event. Doing 190 - * this makes little sense due to scale and unit applying to both. 191 - */ 192 - char *metric_expr; 193 - /** @metric_name: A name for the metric. unit applying to both. */ 194 - char *metric_name; 195 191 /** @pmu_name: The name copied from struct perf_pmu. */ 196 192 char *pmu_name; 197 193 }; ··· 221 231 222 232 int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, ...) __scanf(3, 4); 223 233 234 + bool perf_pmu__file_exists(struct perf_pmu *pmu, const char *name); 235 + 224 236 int perf_pmu__test(void); 225 237 226 238 struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu); ··· 231 239 232 240 char *perf_pmu__getcpuid(struct perf_pmu *pmu); 233 241 const struct pmu_events_table *pmu_events_table__find(void); 242 + const struct pmu_metrics_table *pmu_metrics_table__find(void); 234 243 bool pmu_uncore_alias_match(const char *pmu_name, const char *name); 235 244 void perf_pmu_free_alias(struct perf_pmu_alias *alias); 236 245 ··· 252 259 253 260 char *pmu_find_real_name(const char *name); 254 261 char *pmu_find_alias_name(const char *name); 262 + double perf_pmu__cpu_slots_per_cycle(void); 263 + int perf_pmu__event_source_devices_scnprintf(char *pathname, size_t size); 264 + int perf_pmu__pathname_scnprintf(char *buf, size_t size, 265 + const char *pmu_name, const char *filename); 266 + FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name); 267 + 255 268 #endif /* __PMU_H */
+8 -24
tools/perf/util/print-events.c
··· 101 101 "Tracepoint event", 102 102 /*desc=*/NULL, 103 103 /*long_desc=*/NULL, 104 - /*encoding_desc=*/NULL, 105 - /*metric_name=*/NULL, 106 - /*metric_expr=*/NULL); 104 + /*encoding_desc=*/NULL); 107 105 } 108 106 free(dir_path); 109 107 free(evt_namelist); ··· 193 195 "SDT event", 194 196 /*desc=*/NULL, 195 197 /*long_desc=*/NULL, 196 - /*encoding_desc=*/NULL, 197 - /*metric_name=*/NULL, 198 - /*metric_expr=*/NULL); 198 + /*encoding_desc=*/NULL); 199 199 200 200 free(evt_name); 201 201 } ··· 251 255 event_type_descriptors[PERF_TYPE_HW_CACHE], 252 256 /*desc=*/NULL, 253 257 /*long_desc=*/NULL, 254 - /*encoding_desc=*/NULL, 255 - /*metric_name=*/NULL, 256 - /*metric_expr=*/NULL); 258 + /*encoding_desc=*/NULL); 257 259 } 258 260 strlist__delete(evt_name_list); 259 261 return 0; ··· 271 277 "Tool event", 272 278 /*desc=*/NULL, 273 279 /*long_desc=*/NULL, 274 - /*encoding_desc=*/NULL, 275 - /*metric_name=*/NULL, 276 - /*metric_expr=*/NULL); 280 + /*encoding_desc=*/NULL); 277 281 } 278 282 } 279 283 ··· 323 331 event_type_descriptors[type], 324 332 /*desc=*/NULL, 325 333 /*long_desc=*/NULL, 326 - /*encoding_desc=*/NULL, 327 - /*metric_name=*/NULL, 328 - /*metric_expr=*/NULL); 334 + /*encoding_desc=*/NULL); 329 335 } 330 336 strlist__delete(evt_name_list); 331 337 } ··· 354 364 event_type_descriptors[PERF_TYPE_RAW], 355 365 /*desc=*/NULL, 356 366 /*long_desc=*/NULL, 357 - /*encoding_desc=*/NULL, 358 - /*metric_name=*/NULL, 359 - /*metric_expr=*/NULL); 367 + /*encoding_desc=*/NULL); 360 368 361 369 print_cb->print_event(print_state, 362 370 /*topic=*/NULL, ··· 366 378 event_type_descriptors[PERF_TYPE_RAW], 367 379 "(see 'man perf-list' on how to encode it)", 368 380 /*long_desc=*/NULL, 369 - /*encoding_desc=*/NULL, 370 - /*metric_name=*/NULL, 371 - /*metric_expr=*/NULL); 381 + /*encoding_desc=*/NULL); 372 382 373 383 print_cb->print_event(print_state, 374 384 /*topic=*/NULL, ··· 378 392 event_type_descriptors[PERF_TYPE_BREAKPOINT], 379 393 /*desc=*/NULL, 380 394 /*long_desc=*/NULL, 381 - /*encoding_desc=*/NULL, 382 - /*metric_name=*/NULL, 383 - /*metric_expr=*/NULL); 395 + /*encoding_desc=*/NULL); 384 396 385 397 print_tracepoint_events(print_cb, print_state); 386 398
+1 -2
tools/perf/util/print-events.h
··· 16 16 const char *scale_unit, 17 17 bool deprecated, const char *event_type_desc, 18 18 const char *desc, const char *long_desc, 19 - const char *encoding_desc, 20 - const char *metric_name, const char *metric_expr); 19 + const char *encoding_desc); 21 20 void (*print_metric)(void *print_state, 22 21 const char *group, 23 22 const char *name,
+2 -2
tools/perf/util/probe-event.c
··· 917 917 dinfo = open_debuginfo(pev->target, pev->nsi, !need_dwarf); 918 918 if (!dinfo) { 919 919 if (need_dwarf) 920 - return -ENOENT; 920 + return -ENODATA; 921 921 pr_debug("Could not open debuginfo. Try to use symbols.\n"); 922 922 return 0; 923 923 } ··· 956 956 if (ntevs == 0) { /* No error but failed to find probe point. */ 957 957 pr_warning("Probe point '%s' not found.\n", 958 958 synthesize_perf_probe_point(&pev->point)); 959 - return -ENOENT; 959 + return -ENODEV; 960 960 } else if (ntevs < 0) { 961 961 /* Error path : ntevs < 0 */ 962 962 pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs);
+8 -11
tools/perf/util/python.c
··· 20 20 #include "stat.h" 21 21 #include "metricgroup.h" 22 22 #include "util/env.h" 23 + #include "util/pmu.h" 23 24 #include <internal/lib.h> 24 25 #include "util.h" 25 26 ··· 77 76 } 78 77 79 78 /* 80 - * Add this one here not to drag util/stat-shadow.c 81 - */ 82 - void perf_stat__collect_metric_expr(struct evlist *evsel_list) 83 - { 84 - } 85 - 86 - /* 87 - * This one is needed not to drag the PMU bandwagon, jevents generated 79 + * These ones are needed not to drag the PMU bandwagon, jevents generated 88 80 * pmu_sys_event_tables, etc and evsel__find_pmu() is used so far just for 89 81 * doing per PMU perf_event_attr.exclude_guest handling, not really needed, so 90 82 * far, for the perf python binding known usecases, revisit if this become ··· 86 92 struct perf_pmu *evsel__find_pmu(struct evsel *evsel __maybe_unused) 87 93 { 88 94 return NULL; 95 + } 96 + 97 + int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, ...) 98 + { 99 + return EOF; 89 100 } 90 101 91 102 /* ··· 441 442 offset = val; 442 443 len = offset >> 16; 443 444 offset &= 0xffff; 444 - #ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE 445 - if (field->flags & TEP_FIELD_IS_RELATIVE) 445 + if (tep_field_is_relative(field->flags)) 446 446 offset += field->offset + field->size; 447 - #endif 448 447 } 449 448 if (field->flags & TEP_FIELD_IS_STRING && 450 449 is_printable_array(data + offset, len)) {
+4 -1
tools/perf/util/sample.h
··· 92 92 u8 cpumode; 93 93 u16 misc; 94 94 u16 ins_lat; 95 - u16 p_stage_cyc; 95 + union { 96 + u16 p_stage_cyc; 97 + u16 retire_lat; 98 + }; 96 99 bool no_hw_idx; /* No hw_idx collected in branch_stack */ 97 100 char insn[MAX_INSN]; 98 101 void *raw_data;
+1 -3
tools/perf/util/scripting-engines/trace-event-perl.c
··· 393 393 if (field->flags & TEP_FIELD_IS_DYNAMIC) { 394 394 offset = *(int *)(data + field->offset); 395 395 offset &= 0xffff; 396 - #ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE 397 - if (field->flags & TEP_FIELD_IS_RELATIVE) 396 + if (tep_field_is_relative(field->flags)) 398 397 offset += field->offset + field->size; 399 - #endif 400 398 } else 401 399 offset = field->offset; 402 400 XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0)));
+1 -3
tools/perf/util/scripting-engines/trace-event-python.c
··· 994 994 offset = val; 995 995 len = offset >> 16; 996 996 offset &= 0xffff; 997 - #ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE 998 - if (field->flags & TEP_FIELD_IS_RELATIVE) 997 + if (tep_field_is_relative(field->flags)) 999 998 offset += field->offset + field->size; 1000 - #endif 1001 999 } 1002 1000 if (field->flags & TEP_FIELD_IS_STRING && 1003 1001 is_printable_array(data + offset, len)) {
+10 -4
tools/perf/util/session.c
··· 1180 1180 struct branch_entry *e = &entries[i]; 1181 1181 1182 1182 if (!callstack) { 1183 - printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 " %hu cycles %s%s%s%s %x %s\n", 1183 + printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 " %hu cycles %s%s%s%s %x %s %s\n", 1184 1184 i, e->from, e->to, 1185 1185 (unsigned short)e->flags.cycles, 1186 1186 e->flags.mispred ? "M" : " ", ··· 1188 1188 e->flags.abort ? "A" : " ", 1189 1189 e->flags.in_tx ? "T" : " ", 1190 1190 (unsigned)e->flags.reserved, 1191 - get_branch_type(e)); 1191 + get_branch_type(e), 1192 + e->flags.spec ? branch_spec_desc(e->flags.spec) : ""); 1192 1193 } else { 1193 1194 if (i == 0) { 1194 1195 printf("..... %2"PRIu64": %016" PRIx64 "\n" ··· 1700 1699 case PERF_RECORD_AUXTRACE_INFO: 1701 1700 return tool->auxtrace_info(session, event); 1702 1701 case PERF_RECORD_AUXTRACE: 1703 - /* setup for reading amidst mmap */ 1704 - lseek(fd, file_offset + event->header.size, SEEK_SET); 1702 + /* 1703 + * Setup for reading amidst mmap, but only when we 1704 + * are in 'file' mode. The 'pipe' fd is in proper 1705 + * place already. 1706 + */ 1707 + if (!perf_data__is_pipe(session->data)) 1708 + lseek(fd, file_offset + event->header.size, SEEK_SET); 1705 1709 return tool->auxtrace(session, event); 1706 1710 case PERF_RECORD_AUXTRACE_ERROR: 1707 1711 perf_session__auxtrace_error_inc(session, event);
+14 -6
tools/perf/util/sort.c
··· 28 28 #include "time-utils.h" 29 29 #include "cgroup.h" 30 30 #include "machine.h" 31 + #include "trace-event.h" 31 32 #include <linux/kernel.h> 32 33 #include <linux/string.h> 33 34 ··· 52 51 enum sort_mode sort__mode = SORT_MODE__NORMAL; 53 52 static const char *const dynamic_headers[] = {"local_ins_lat", "ins_lat", "local_p_stage_cyc", "p_stage_cyc"}; 54 53 static const char *const arch_specific_sort_keys[] = {"local_p_stage_cyc", "p_stage_cyc"}; 54 + 55 + /* 56 + * Some architectures have Adjacent Cacheline Prefetch feature, which 57 + * behaves like the cacheline size is doubled. Enable this flag to 58 + * check things in double cacheline granularity. 59 + */ 60 + bool chk_double_cl; 55 61 56 62 /* 57 63 * Replaces all occurrences of a char used with the: ··· 1507 1499 1508 1500 addr: 1509 1501 /* al_addr does all the right addr - start + offset calculations */ 1510 - l = cl_address(left->mem_info->daddr.al_addr); 1511 - r = cl_address(right->mem_info->daddr.al_addr); 1502 + l = cl_address(left->mem_info->daddr.al_addr, chk_double_cl); 1503 + r = cl_address(right->mem_info->daddr.al_addr, chk_double_cl); 1512 1504 1513 1505 if (l > r) return -1; 1514 1506 if (l < r) return 1; ··· 1527 1519 if (he->mem_info) { 1528 1520 struct map *map = he->mem_info->daddr.ms.map; 1529 1521 1530 - addr = cl_address(he->mem_info->daddr.al_addr); 1522 + addr = cl_address(he->mem_info->daddr.al_addr, chk_double_cl); 1531 1523 ms = &he->mem_info->daddr.ms; 1532 1524 1533 1525 /* print [s] for shared data mmaps */ ··· 2140 2132 DIM(SORT_LOCAL_PIPELINE_STAGE_CYC, "local_p_stage_cyc", sort_local_p_stage_cyc), 2141 2133 DIM(SORT_GLOBAL_PIPELINE_STAGE_CYC, "p_stage_cyc", sort_global_p_stage_cyc), 2142 2134 DIM(SORT_ADDR, "addr", sort_addr), 2135 + DIM(SORT_LOCAL_RETIRE_LAT, "local_retire_lat", sort_local_p_stage_cyc), 2136 + DIM(SORT_GLOBAL_RETIRE_LAT, "retire_lat", sort_global_p_stage_cyc), 2143 2137 }; 2144 2138 2145 2139 #undef DIM ··· 2677 2667 tep_read_number_field(field, a->raw_data, &dyn); 2678 2668 offset = dyn & 0xffff; 2679 2669 size = (dyn >> 16) & 0xffff; 2680 - #ifdef HAVE_LIBTRACEEVENT_TEP_FIELD_IS_RELATIVE 2681 - if (field->flags & TEP_FIELD_IS_RELATIVE) 2670 + if (tep_field_is_relative(field->flags)) 2682 2671 offset += field->offset + field->size; 2683 - #endif 2684 2672 /* record max width for output */ 2685 2673 if (size > hde->dynamic_len) 2686 2674 hde->dynamic_len = size;
+3
tools/perf/util/sort.h
··· 35 35 extern struct sort_entry sort_sym_to; 36 36 extern struct sort_entry sort_srcline; 37 37 extern const char default_mem_sort_order[]; 38 + extern bool chk_double_cl; 38 39 39 40 struct res_sample { 40 41 u64 time; ··· 238 237 SORT_LOCAL_PIPELINE_STAGE_CYC, 239 238 SORT_GLOBAL_PIPELINE_STAGE_CYC, 240 239 SORT_ADDR, 240 + SORT_LOCAL_RETIRE_LAT, 241 + SORT_GLOBAL_RETIRE_LAT, 241 242 242 243 /* branch stack specific sort keys */ 243 244 __SORT_BRANCH_STACK,
+46 -5
tools/perf/util/stat-display.c
··· 787 787 uniquify_event_name(counter); 788 788 } 789 789 790 + /** 791 + * should_skip_zero_count() - Check if the event should print 0 values. 792 + * @config: The perf stat configuration (including aggregation mode). 793 + * @counter: The evsel with its associated cpumap. 794 + * @id: The aggregation id that is being queried. 795 + * 796 + * Due to mismatch between the event cpumap or thread-map and the 797 + * aggregation mode, sometimes it'd iterate the counter with the map 798 + * which does not contain any values. 799 + * 800 + * For example, uncore events have dedicated CPUs to manage them, 801 + * result for other CPUs should be zero and skipped. 802 + * 803 + * Return: %true if the value should NOT be printed, %false if the value 804 + * needs to be printed like "<not counted>" or "<not supported>". 805 + */ 806 + static bool should_skip_zero_counter(struct perf_stat_config *config, 807 + struct evsel *counter, 808 + const struct aggr_cpu_id *id) 809 + { 810 + struct perf_cpu cpu; 811 + int idx; 812 + 813 + /* 814 + * Skip value 0 when enabling --per-thread globally, 815 + * otherwise it will have too many 0 output. 816 + */ 817 + if (config->aggr_mode == AGGR_THREAD && config->system_wide) 818 + return true; 819 + /* 820 + * Skip value 0 when it's an uncore event and the given aggr id 821 + * does not belong to the PMU cpumask. 822 + */ 823 + if (!counter->pmu || !counter->pmu->is_uncore) 824 + return false; 825 + 826 + perf_cpu_map__for_each_cpu(cpu, idx, counter->pmu->cpus) { 827 + struct aggr_cpu_id own_id = config->aggr_get_id(config, cpu); 828 + 829 + if (aggr_cpu_id__equal(id, &own_id)) 830 + return false; 831 + } 832 + return true; 833 + } 834 + 790 835 static void print_counter_aggrdata(struct perf_stat_config *config, 791 836 struct evsel *counter, int s, 792 837 struct outstate *os) ··· 859 814 ena = aggr->counts.ena; 860 815 run = aggr->counts.run; 861 816 862 - /* 863 - * Skip value 0 when enabling --per-thread globally, otherwise it will 864 - * have too many 0 output. 865 - */ 866 - if (val == 0 && config->aggr_mode == AGGR_THREAD && config->system_wide) 817 + if (val == 0 && should_skip_zero_counter(config, counter, &id)) 867 818 return; 868 819 869 820 if (!metric_only) {
+1 -113
tools/perf/util/stat-shadow.c
··· 311 311 update_stats(&v->stats, count); 312 312 if (counter->metric_leader) 313 313 v->metric_total += count; 314 - } else if (counter->metric_leader) { 314 + } else if (counter->metric_leader && !counter->merged_stat) { 315 315 v = saved_value_lookup(counter->metric_leader, 316 316 map_idx, true, STAT_NONE, 0, st, rsd.cgrp); 317 317 v->metric_total += count; ··· 344 344 color = PERF_COLOR_YELLOW; 345 345 346 346 return color; 347 - } 348 - 349 - static struct evsel *perf_stat__find_event(struct evlist *evsel_list, 350 - const char *name) 351 - { 352 - struct evsel *c2; 353 - 354 - evlist__for_each_entry (evsel_list, c2) { 355 - if (!strcasecmp(c2->name, name) && !c2->collect_stat) 356 - return c2; 357 - } 358 - return NULL; 359 - } 360 - 361 - /* Mark MetricExpr target events and link events using them to them. */ 362 - void perf_stat__collect_metric_expr(struct evlist *evsel_list) 363 - { 364 - struct evsel *counter, *leader, **metric_events, *oc; 365 - bool found; 366 - struct expr_parse_ctx *ctx; 367 - struct hashmap_entry *cur; 368 - size_t bkt; 369 - int i; 370 - 371 - ctx = expr__ctx_new(); 372 - if (!ctx) { 373 - pr_debug("expr__ctx_new failed"); 374 - return; 375 - } 376 - evlist__for_each_entry(evsel_list, counter) { 377 - bool invalid = false; 378 - 379 - leader = evsel__leader(counter); 380 - if (!counter->metric_expr) 381 - continue; 382 - 383 - expr__ctx_clear(ctx); 384 - metric_events = counter->metric_events; 385 - if (!metric_events) { 386 - if (expr__find_ids(counter->metric_expr, 387 - counter->name, 388 - ctx) < 0) 389 - continue; 390 - 391 - metric_events = calloc(sizeof(struct evsel *), 392 - hashmap__size(ctx->ids) + 1); 393 - if (!metric_events) { 394 - expr__ctx_free(ctx); 395 - return; 396 - } 397 - counter->metric_events = metric_events; 398 - } 399 - 400 - i = 0; 401 - hashmap__for_each_entry(ctx->ids, cur, bkt) { 402 - const char *metric_name = cur->pkey; 403 - 404 - found = false; 405 - if (leader) { 406 - /* Search in group */ 407 - for_each_group_member (oc, leader) { 408 - if (!strcasecmp(oc->name, 409 - metric_name) && 410 - !oc->collect_stat) { 411 - found = true; 412 - break; 413 - } 414 - } 415 - } 416 - if (!found) { 417 - /* Search ignoring groups */ 418 - oc = perf_stat__find_event(evsel_list, 419 - metric_name); 420 - } 421 - if (!oc) { 422 - /* Deduping one is good enough to handle duplicated PMUs. */ 423 - static char *printed; 424 - 425 - /* 426 - * Adding events automatically would be difficult, because 427 - * it would risk creating groups that are not schedulable. 428 - * perf stat doesn't understand all the scheduling constraints 429 - * of events. So we ask the user instead to add the missing 430 - * events. 431 - */ 432 - if (!printed || 433 - strcasecmp(printed, metric_name)) { 434 - fprintf(stderr, 435 - "Add %s event to groups to get metric expression for %s\n", 436 - metric_name, 437 - counter->name); 438 - free(printed); 439 - printed = strdup(metric_name); 440 - } 441 - invalid = true; 442 - continue; 443 - } 444 - metric_events[i++] = oc; 445 - oc->collect_stat = true; 446 - } 447 - metric_events[i] = NULL; 448 - if (invalid) { 449 - free(metric_events); 450 - counter->metric_events = NULL; 451 - counter->metric_expr = NULL; 452 - } 453 - } 454 - expr__ctx_free(ctx); 455 347 } 456 348 457 349 static double runtime_stat_avg(struct runtime_stat *st, ··· 1191 1299 color = NULL; 1192 1300 print_metric(config, ctxp, color, "%8.1f%%", "Core Bound", 1193 1301 core_bound * 100.); 1194 - } else if (evsel->metric_expr) { 1195 - generic_metric(config, evsel->metric_expr, evsel->metric_events, NULL, 1196 - evsel->name, evsel->metric_name, NULL, 1, 1197 - map_idx, out, st); 1198 1302 } else if (runtime_stat_n(st, STAT_NSECS, map_idx, &rsd) != 0) { 1199 1303 char unit = ' '; 1200 1304 char unit_buf[10] = "/sec";
-1
tools/perf/util/stat.h
··· 257 257 struct perf_stat_output_ctx *out, 258 258 struct rblist *metric_events, 259 259 struct runtime_stat *st); 260 - void perf_stat__collect_metric_expr(struct evlist *); 261 260 262 261 int evlist__alloc_stats(struct perf_stat_config *config, 263 262 struct evlist *evlist, bool alloc_raw);
+426 -103
tools/perf/util/symbol-elf.c
··· 323 323 return demangled; 324 324 } 325 325 326 - #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \ 327 - for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \ 328 - idx < nr_entries; \ 329 - ++idx, pos = gelf_getrel(reldata, idx, &pos_mem)) 326 + struct rel_info { 327 + u32 nr_entries; 328 + u32 *sorted; 329 + bool is_rela; 330 + Elf_Data *reldata; 331 + GElf_Rela rela; 332 + GElf_Rel rel; 333 + }; 330 334 331 - #define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \ 332 - for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \ 333 - idx < nr_entries; \ 334 - ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) 335 + static u32 get_rel_symidx(struct rel_info *ri, u32 idx) 336 + { 337 + idx = ri->sorted ? ri->sorted[idx] : idx; 338 + if (ri->is_rela) { 339 + gelf_getrela(ri->reldata, idx, &ri->rela); 340 + return GELF_R_SYM(ri->rela.r_info); 341 + } 342 + gelf_getrel(ri->reldata, idx, &ri->rel); 343 + return GELF_R_SYM(ri->rel.r_info); 344 + } 345 + 346 + static u64 get_rel_offset(struct rel_info *ri, u32 x) 347 + { 348 + if (ri->is_rela) { 349 + GElf_Rela rela; 350 + 351 + gelf_getrela(ri->reldata, x, &rela); 352 + return rela.r_offset; 353 + } else { 354 + GElf_Rel rel; 355 + 356 + gelf_getrel(ri->reldata, x, &rel); 357 + return rel.r_offset; 358 + } 359 + } 360 + 361 + static int rel_cmp(const void *a, const void *b, void *r) 362 + { 363 + struct rel_info *ri = r; 364 + u64 a_offset = get_rel_offset(ri, *(const u32 *)a); 365 + u64 b_offset = get_rel_offset(ri, *(const u32 *)b); 366 + 367 + return a_offset < b_offset ? -1 : (a_offset > b_offset ? 1 : 0); 368 + } 369 + 370 + static int sort_rel(struct rel_info *ri) 371 + { 372 + size_t sz = sizeof(ri->sorted[0]); 373 + u32 i; 374 + 375 + ri->sorted = calloc(ri->nr_entries, sz); 376 + if (!ri->sorted) 377 + return -1; 378 + for (i = 0; i < ri->nr_entries; i++) 379 + ri->sorted[i] = i; 380 + qsort_r(ri->sorted, ri->nr_entries, sz, rel_cmp, ri); 381 + return 0; 382 + } 383 + 384 + /* 385 + * For x86_64, the GNU linker is putting IFUNC information in the relocation 386 + * addend. 387 + */ 388 + static bool addend_may_be_ifunc(GElf_Ehdr *ehdr, struct rel_info *ri) 389 + { 390 + return ehdr->e_machine == EM_X86_64 && ri->is_rela && 391 + GELF_R_TYPE(ri->rela.r_info) == R_X86_64_IRELATIVE; 392 + } 393 + 394 + static bool get_ifunc_name(Elf *elf, struct dso *dso, GElf_Ehdr *ehdr, 395 + struct rel_info *ri, char *buf, size_t buf_sz) 396 + { 397 + u64 addr = ri->rela.r_addend; 398 + struct symbol *sym; 399 + GElf_Phdr phdr; 400 + 401 + if (!addend_may_be_ifunc(ehdr, ri)) 402 + return false; 403 + 404 + if (elf_read_program_header(elf, addr, &phdr)) 405 + return false; 406 + 407 + addr -= phdr.p_vaddr - phdr.p_offset; 408 + 409 + sym = dso__find_symbol_nocache(dso, addr); 410 + 411 + /* Expecting the address to be an IFUNC or IFUNC alias */ 412 + if (!sym || sym->start != addr || (sym->type != STT_GNU_IFUNC && !sym->ifunc_alias)) 413 + return false; 414 + 415 + snprintf(buf, buf_sz, "%s@plt", sym->name); 416 + 417 + return true; 418 + } 419 + 420 + static void exit_rel(struct rel_info *ri) 421 + { 422 + free(ri->sorted); 423 + } 424 + 425 + static bool get_plt_sizes(struct dso *dso, GElf_Ehdr *ehdr, GElf_Shdr *shdr_plt, 426 + u64 *plt_header_size, u64 *plt_entry_size) 427 + { 428 + switch (ehdr->e_machine) { 429 + case EM_ARM: 430 + *plt_header_size = 20; 431 + *plt_entry_size = 12; 432 + return true; 433 + case EM_AARCH64: 434 + *plt_header_size = 32; 435 + *plt_entry_size = 16; 436 + return true; 437 + case EM_SPARC: 438 + *plt_header_size = 48; 439 + *plt_entry_size = 12; 440 + return true; 441 + case EM_SPARCV9: 442 + *plt_header_size = 128; 443 + *plt_entry_size = 32; 444 + return true; 445 + case EM_386: 446 + case EM_X86_64: 447 + *plt_entry_size = shdr_plt->sh_entsize; 448 + /* Size is 8 or 16, if not, assume alignment indicates size */ 449 + if (*plt_entry_size != 8 && *plt_entry_size != 16) 450 + *plt_entry_size = shdr_plt->sh_addralign == 8 ? 8 : 16; 451 + *plt_header_size = *plt_entry_size; 452 + break; 453 + default: /* FIXME: s390/alpha/mips/parisc/poperpc/sh/xtensa need to be checked */ 454 + *plt_header_size = shdr_plt->sh_entsize; 455 + *plt_entry_size = shdr_plt->sh_entsize; 456 + break; 457 + } 458 + if (*plt_entry_size) 459 + return true; 460 + pr_debug("Missing PLT entry size for %s\n", dso->long_name); 461 + return false; 462 + } 463 + 464 + static bool machine_is_x86(GElf_Half e_machine) 465 + { 466 + return e_machine == EM_386 || e_machine == EM_X86_64; 467 + } 468 + 469 + struct rela_dyn { 470 + GElf_Addr offset; 471 + u32 sym_idx; 472 + }; 473 + 474 + struct rela_dyn_info { 475 + struct dso *dso; 476 + Elf_Data *plt_got_data; 477 + u32 nr_entries; 478 + struct rela_dyn *sorted; 479 + Elf_Data *dynsym_data; 480 + Elf_Data *dynstr_data; 481 + Elf_Data *rela_dyn_data; 482 + }; 483 + 484 + static void exit_rela_dyn(struct rela_dyn_info *di) 485 + { 486 + free(di->sorted); 487 + } 488 + 489 + static int cmp_offset(const void *a, const void *b) 490 + { 491 + const struct rela_dyn *va = a; 492 + const struct rela_dyn *vb = b; 493 + 494 + return va->offset < vb->offset ? -1 : (va->offset > vb->offset ? 1 : 0); 495 + } 496 + 497 + static int sort_rela_dyn(struct rela_dyn_info *di) 498 + { 499 + u32 i, n; 500 + 501 + di->sorted = calloc(di->nr_entries, sizeof(di->sorted[0])); 502 + if (!di->sorted) 503 + return -1; 504 + 505 + /* Get data for sorting: the offset and symbol index */ 506 + for (i = 0, n = 0; i < di->nr_entries; i++) { 507 + GElf_Rela rela; 508 + u32 sym_idx; 509 + 510 + gelf_getrela(di->rela_dyn_data, i, &rela); 511 + sym_idx = GELF_R_SYM(rela.r_info); 512 + if (sym_idx) { 513 + di->sorted[n].sym_idx = sym_idx; 514 + di->sorted[n].offset = rela.r_offset; 515 + n += 1; 516 + } 517 + } 518 + 519 + /* Sort by offset */ 520 + di->nr_entries = n; 521 + qsort(di->sorted, n, sizeof(di->sorted[0]), cmp_offset); 522 + 523 + return 0; 524 + } 525 + 526 + static void get_rela_dyn_info(Elf *elf, GElf_Ehdr *ehdr, struct rela_dyn_info *di, Elf_Scn *scn) 527 + { 528 + GElf_Shdr rela_dyn_shdr; 529 + GElf_Shdr shdr; 530 + 531 + di->plt_got_data = elf_getdata(scn, NULL); 532 + 533 + scn = elf_section_by_name(elf, ehdr, &rela_dyn_shdr, ".rela.dyn", NULL); 534 + if (!scn || !rela_dyn_shdr.sh_link || !rela_dyn_shdr.sh_entsize) 535 + return; 536 + 537 + di->nr_entries = rela_dyn_shdr.sh_size / rela_dyn_shdr.sh_entsize; 538 + di->rela_dyn_data = elf_getdata(scn, NULL); 539 + 540 + scn = elf_getscn(elf, rela_dyn_shdr.sh_link); 541 + if (!scn || !gelf_getshdr(scn, &shdr) || !shdr.sh_link) 542 + return; 543 + 544 + di->dynsym_data = elf_getdata(scn, NULL); 545 + di->dynstr_data = elf_getdata(elf_getscn(elf, shdr.sh_link), NULL); 546 + 547 + if (!di->plt_got_data || !di->dynstr_data || !di->dynsym_data || !di->rela_dyn_data) 548 + return; 549 + 550 + /* Sort into offset order */ 551 + sort_rela_dyn(di); 552 + } 553 + 554 + /* Get instruction displacement from a plt entry for x86_64 */ 555 + static u32 get_x86_64_plt_disp(const u8 *p) 556 + { 557 + u8 endbr64[] = {0xf3, 0x0f, 0x1e, 0xfa}; 558 + int n = 0; 559 + 560 + /* Skip endbr64 */ 561 + if (!memcmp(p, endbr64, sizeof(endbr64))) 562 + n += sizeof(endbr64); 563 + /* Skip bnd prefix */ 564 + if (p[n] == 0xf2) 565 + n += 1; 566 + /* jmp with 4-byte displacement */ 567 + if (p[n] == 0xff && p[n + 1] == 0x25) { 568 + n += 2; 569 + /* Also add offset from start of entry to end of instruction */ 570 + return n + 4 + le32toh(*(const u32 *)(p + n)); 571 + } 572 + return 0; 573 + } 574 + 575 + static bool get_plt_got_name(GElf_Shdr *shdr, size_t i, 576 + struct rela_dyn_info *di, 577 + char *buf, size_t buf_sz) 578 + { 579 + struct rela_dyn vi, *vr; 580 + const char *sym_name; 581 + char *demangled; 582 + GElf_Sym sym; 583 + u32 disp; 584 + 585 + if (!di->sorted) 586 + return false; 587 + 588 + disp = get_x86_64_plt_disp(di->plt_got_data->d_buf + i); 589 + if (!disp) 590 + return false; 591 + 592 + /* Compute target offset of the .plt.got entry */ 593 + vi.offset = shdr->sh_offset + di->plt_got_data->d_off + i + disp; 594 + 595 + /* Find that offset in .rela.dyn (sorted by offset) */ 596 + vr = bsearch(&vi, di->sorted, di->nr_entries, sizeof(di->sorted[0]), cmp_offset); 597 + if (!vr) 598 + return false; 599 + 600 + /* Get the associated symbol */ 601 + gelf_getsym(di->dynsym_data, vr->sym_idx, &sym); 602 + sym_name = elf_sym__name(&sym, di->dynstr_data); 603 + demangled = demangle_sym(di->dso, 0, sym_name); 604 + if (demangled != NULL) 605 + sym_name = demangled; 606 + 607 + snprintf(buf, buf_sz, "%s@plt", sym_name); 608 + 609 + free(demangled); 610 + 611 + return *sym_name; 612 + } 613 + 614 + static int dso__synthesize_plt_got_symbols(struct dso *dso, Elf *elf, 615 + GElf_Ehdr *ehdr, 616 + char *buf, size_t buf_sz) 617 + { 618 + struct rela_dyn_info di = { .dso = dso }; 619 + struct symbol *sym; 620 + GElf_Shdr shdr; 621 + Elf_Scn *scn; 622 + int err = -1; 623 + size_t i; 624 + 625 + scn = elf_section_by_name(elf, ehdr, &shdr, ".plt.got", NULL); 626 + if (!scn || !shdr.sh_entsize) 627 + return 0; 628 + 629 + if (ehdr->e_machine == EM_X86_64) 630 + get_rela_dyn_info(elf, ehdr, &di, scn); 631 + 632 + for (i = 0; i < shdr.sh_size; i += shdr.sh_entsize) { 633 + if (!get_plt_got_name(&shdr, i, &di, buf, buf_sz)) 634 + snprintf(buf, buf_sz, "offset_%#" PRIx64 "@plt", (u64)shdr.sh_offset + i); 635 + sym = symbol__new(shdr.sh_offset + i, shdr.sh_entsize, STB_GLOBAL, STT_FUNC, buf); 636 + if (!sym) 637 + goto out; 638 + symbols__insert(&dso->symbols, sym); 639 + } 640 + err = 0; 641 + out: 642 + exit_rela_dyn(&di); 643 + return err; 644 + } 335 645 336 646 /* 337 647 * We need to check if we have a .dynsym, so that we can handle the ··· 652 342 */ 653 343 int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss) 654 344 { 655 - uint32_t nr_rel_entries, idx; 345 + uint32_t idx; 656 346 GElf_Sym sym; 657 347 u64 plt_offset, plt_header_size, plt_entry_size; 658 - GElf_Shdr shdr_plt; 659 - struct symbol *f; 348 + GElf_Shdr shdr_plt, plt_sec_shdr; 349 + struct symbol *f, *plt_sym; 660 350 GElf_Shdr shdr_rel_plt, shdr_dynsym; 661 - Elf_Data *reldata, *syms, *symstrs; 351 + Elf_Data *syms, *symstrs; 662 352 Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; 663 - size_t dynsym_idx; 664 353 GElf_Ehdr ehdr; 665 354 char sympltname[1024]; 666 355 Elf *elf; 667 - int nr = 0, symidx, err = 0; 668 - 669 - if (!ss->dynsym) 670 - return 0; 356 + int nr = 0, err = -1; 357 + struct rel_info ri = { .is_rela = false }; 358 + bool lazy_plt; 671 359 672 360 elf = ss->elf; 673 361 ehdr = ss->ehdr; 674 362 675 - scn_dynsym = ss->dynsym; 676 - shdr_dynsym = ss->dynshdr; 677 - dynsym_idx = ss->dynsym_idx; 363 + if (!elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL)) 364 + return 0; 678 365 679 - if (scn_dynsym == NULL) 366 + /* 367 + * A symbol from a previous section (e.g. .init) can have been expanded 368 + * by symbols__fixup_end() to overlap .plt. Truncate it before adding 369 + * a symbol for .plt header. 370 + */ 371 + f = dso__find_symbol_nocache(dso, shdr_plt.sh_offset); 372 + if (f && f->start < shdr_plt.sh_offset && f->end > shdr_plt.sh_offset) 373 + f->end = shdr_plt.sh_offset; 374 + 375 + if (!get_plt_sizes(dso, &ehdr, &shdr_plt, &plt_header_size, &plt_entry_size)) 376 + return 0; 377 + 378 + /* Add a symbol for .plt header */ 379 + plt_sym = symbol__new(shdr_plt.sh_offset, plt_header_size, STB_GLOBAL, STT_FUNC, ".plt"); 380 + if (!plt_sym) 680 381 goto out_elf_end; 382 + symbols__insert(&dso->symbols, plt_sym); 383 + 384 + /* Only x86 has .plt.got */ 385 + if (machine_is_x86(ehdr.e_machine) && 386 + dso__synthesize_plt_got_symbols(dso, elf, &ehdr, sympltname, sizeof(sympltname))) 387 + goto out_elf_end; 388 + 389 + /* Only x86 has .plt.sec */ 390 + if (machine_is_x86(ehdr.e_machine) && 391 + elf_section_by_name(elf, &ehdr, &plt_sec_shdr, ".plt.sec", NULL)) { 392 + if (!get_plt_sizes(dso, &ehdr, &plt_sec_shdr, &plt_header_size, &plt_entry_size)) 393 + return 0; 394 + /* Extend .plt symbol to entire .plt */ 395 + plt_sym->end = plt_sym->start + shdr_plt.sh_size; 396 + /* Use .plt.sec offset */ 397 + plt_offset = plt_sec_shdr.sh_offset; 398 + lazy_plt = false; 399 + } else { 400 + plt_offset = shdr_plt.sh_offset; 401 + lazy_plt = true; 402 + } 681 403 682 404 scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, 683 405 ".rela.plt", NULL); ··· 717 375 scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, 718 376 ".rel.plt", NULL); 719 377 if (scn_plt_rel == NULL) 720 - goto out_elf_end; 378 + return 0; 721 379 } 722 380 723 - err = -1; 381 + if (shdr_rel_plt.sh_type != SHT_RELA && 382 + shdr_rel_plt.sh_type != SHT_REL) 383 + return 0; 724 384 725 - if (shdr_rel_plt.sh_link != dynsym_idx) 726 - goto out_elf_end; 385 + if (!shdr_rel_plt.sh_link) 386 + return 0; 727 387 728 - if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) 388 + if (shdr_rel_plt.sh_link == ss->dynsym_idx) { 389 + scn_dynsym = ss->dynsym; 390 + shdr_dynsym = ss->dynshdr; 391 + } else if (shdr_rel_plt.sh_link == ss->symtab_idx) { 392 + /* 393 + * A static executable can have a .plt due to IFUNCs, in which 394 + * case .symtab is used not .dynsym. 395 + */ 396 + scn_dynsym = ss->symtab; 397 + shdr_dynsym = ss->symshdr; 398 + } else { 729 399 goto out_elf_end; 400 + } 401 + 402 + if (!scn_dynsym) 403 + return 0; 730 404 731 405 /* 732 406 * Fetch the relocation section to find the idxes to the GOT 733 407 * and the symbols in the .dynsym they refer to. 734 408 */ 735 - reldata = elf_getdata(scn_plt_rel, NULL); 736 - if (reldata == NULL) 409 + ri.reldata = elf_getdata(scn_plt_rel, NULL); 410 + if (!ri.reldata) 737 411 goto out_elf_end; 738 412 739 413 syms = elf_getdata(scn_dynsym, NULL); ··· 767 409 if (symstrs->d_size == 0) 768 410 goto out_elf_end; 769 411 770 - nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; 771 - plt_offset = shdr_plt.sh_offset; 772 - switch (ehdr.e_machine) { 773 - case EM_ARM: 774 - plt_header_size = 20; 775 - plt_entry_size = 12; 776 - break; 412 + ri.nr_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; 777 413 778 - case EM_AARCH64: 779 - plt_header_size = 32; 780 - plt_entry_size = 16; 781 - break; 414 + ri.is_rela = shdr_rel_plt.sh_type == SHT_RELA; 782 415 783 - case EM_SPARC: 784 - plt_header_size = 48; 785 - plt_entry_size = 12; 786 - break; 787 - 788 - case EM_SPARCV9: 789 - plt_header_size = 128; 790 - plt_entry_size = 32; 791 - break; 792 - 793 - default: /* FIXME: s390/alpha/mips/parisc/poperpc/sh/xtensa need to be checked */ 794 - plt_header_size = shdr_plt.sh_entsize; 795 - plt_entry_size = shdr_plt.sh_entsize; 796 - break; 416 + if (lazy_plt) { 417 + /* 418 + * Assume a .plt with the same number of entries as the number 419 + * of relocation entries is not lazy and does not have a header. 420 + */ 421 + if (ri.nr_entries * plt_entry_size == shdr_plt.sh_size) 422 + dso__delete_symbol(dso, plt_sym); 423 + else 424 + plt_offset += plt_header_size; 797 425 } 798 - plt_offset += plt_header_size; 799 426 800 - if (shdr_rel_plt.sh_type == SHT_RELA) { 801 - GElf_Rela pos_mem, *pos; 427 + /* 428 + * x86 doesn't insert IFUNC relocations in .plt order, so sort to get 429 + * back in order. 430 + */ 431 + if (machine_is_x86(ehdr.e_machine) && sort_rel(&ri)) 432 + goto out_elf_end; 802 433 803 - elf_section__for_each_rela(reldata, pos, pos_mem, idx, 804 - nr_rel_entries) { 805 - const char *elf_name = NULL; 806 - char *demangled = NULL; 807 - symidx = GELF_R_SYM(pos->r_info); 808 - gelf_getsym(syms, symidx, &sym); 434 + for (idx = 0; idx < ri.nr_entries; idx++) { 435 + const char *elf_name = NULL; 436 + char *demangled = NULL; 809 437 810 - elf_name = elf_sym__name(&sym, symstrs); 811 - demangled = demangle_sym(dso, 0, elf_name); 812 - if (demangled != NULL) 813 - elf_name = demangled; 438 + gelf_getsym(syms, get_rel_symidx(&ri, idx), &sym); 439 + 440 + elf_name = elf_sym__name(&sym, symstrs); 441 + demangled = demangle_sym(dso, 0, elf_name); 442 + if (demangled) 443 + elf_name = demangled; 444 + if (*elf_name) 445 + snprintf(sympltname, sizeof(sympltname), "%s@plt", elf_name); 446 + else if (!get_ifunc_name(elf, dso, &ehdr, &ri, sympltname, sizeof(sympltname))) 814 447 snprintf(sympltname, sizeof(sympltname), 815 - "%s@plt", elf_name); 816 - free(demangled); 448 + "offset_%#" PRIx64 "@plt", plt_offset); 449 + free(demangled); 817 450 818 - f = symbol__new(plt_offset, plt_entry_size, 819 - STB_GLOBAL, STT_FUNC, sympltname); 820 - if (!f) 821 - goto out_elf_end; 451 + f = symbol__new(plt_offset, plt_entry_size, STB_GLOBAL, STT_FUNC, sympltname); 452 + if (!f) 453 + goto out_elf_end; 822 454 823 - plt_offset += plt_entry_size; 824 - symbols__insert(&dso->symbols, f); 825 - ++nr; 826 - } 827 - } else if (shdr_rel_plt.sh_type == SHT_REL) { 828 - GElf_Rel pos_mem, *pos; 829 - elf_section__for_each_rel(reldata, pos, pos_mem, idx, 830 - nr_rel_entries) { 831 - const char *elf_name = NULL; 832 - char *demangled = NULL; 833 - symidx = GELF_R_SYM(pos->r_info); 834 - gelf_getsym(syms, symidx, &sym); 835 - 836 - elf_name = elf_sym__name(&sym, symstrs); 837 - demangled = demangle_sym(dso, 0, elf_name); 838 - if (demangled != NULL) 839 - elf_name = demangled; 840 - snprintf(sympltname, sizeof(sympltname), 841 - "%s@plt", elf_name); 842 - free(demangled); 843 - 844 - f = symbol__new(plt_offset, plt_entry_size, 845 - STB_GLOBAL, STT_FUNC, sympltname); 846 - if (!f) 847 - goto out_elf_end; 848 - 849 - plt_offset += plt_entry_size; 850 - symbols__insert(&dso->symbols, f); 851 - ++nr; 852 - } 455 + plt_offset += plt_entry_size; 456 + symbols__insert(&dso->symbols, f); 457 + ++nr; 853 458 } 854 459 855 460 err = 0; 856 461 out_elf_end: 462 + exit_rel(&ri); 857 463 if (err == 0) 858 464 return nr; 859 465 pr_debug("%s: problems reading %s PLT info.\n", ··· 1268 946 1269 947 ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64); 1270 948 949 + ss->symtab_idx = 0; 1271 950 ss->symtab = elf_section_by_name(elf, &ehdr, &ss->symshdr, ".symtab", 1272 - NULL); 951 + &ss->symtab_idx); 1273 952 if (ss->symshdr.sh_type != SHT_SYMTAB) 1274 953 ss->symtab = NULL; 1275 954
+24 -2
tools/perf/util/symbol.c
··· 201 201 continue; 202 202 203 203 if (choose_best_symbol(curr, next) == SYMBOL_A) { 204 + if (next->type == STT_GNU_IFUNC) 205 + curr->ifunc_alias = true; 204 206 rb_erase_cached(&next->rb_node, symbols); 205 207 symbol__delete(next); 206 208 goto again; 207 209 } else { 210 + if (curr->type == STT_GNU_IFUNC) 211 + next->ifunc_alias = true; 208 212 nd = rb_next(&curr->rb_node); 209 213 rb_erase_cached(&curr->rb_node, symbols); 210 214 symbol__delete(curr); ··· 558 554 } 559 555 560 556 return dso->last_find_result.symbol; 557 + } 558 + 559 + struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr) 560 + { 561 + return symbols__find(&dso->symbols, addr); 561 562 } 562 563 563 564 struct symbol *dso__first_symbol(struct dso *dso) ··· 1377 1368 1378 1369 /* Find the kernel map using the '_stext' symbol */ 1379 1370 if (!kallsyms__get_function_start(kallsyms_filename, "_stext", &stext)) { 1371 + u64 replacement_size = 0; 1372 + 1380 1373 list_for_each_entry(new_map, &md.maps, node) { 1381 - if (stext >= new_map->start && stext < new_map->end) { 1374 + u64 new_size = new_map->end - new_map->start; 1375 + 1376 + if (!(stext >= new_map->start && stext < new_map->end)) 1377 + continue; 1378 + 1379 + /* 1380 + * On some architectures, ARM64 for example, the kernel 1381 + * text can get allocated inside of the vmalloc segment. 1382 + * Select the smallest matching segment, in case stext 1383 + * falls within more than one in the list. 1384 + */ 1385 + if (!replacement_map || new_size < replacement_size) { 1382 1386 replacement_map = new_map; 1383 - break; 1387 + replacement_size = new_size; 1384 1388 } 1385 1389 } 1386 1390 }
+3
tools/perf/util/symbol.h
··· 64 64 u8 inlined:1; 65 65 /** Has symbol__annotate2 been performed. */ 66 66 u8 annotate2:1; 67 + /** Symbol is an alias of an STT_GNU_IFUNC */ 68 + u8 ifunc_alias:1; 67 69 /** Architecture specific. Unused except on PPC where it holds st_other. */ 68 70 u8 arch_sym; 69 71 /** The name of length namelen associated with the symbol. */ ··· 150 148 struct symbol *sym); 151 149 152 150 struct symbol *dso__find_symbol(struct dso *dso, u64 addr); 151 + struct symbol *dso__find_symbol_nocache(struct dso *dso, u64 addr); 153 152 struct symbol *dso__find_symbol_by_name(struct dso *dso, const char *name); 154 153 155 154 struct symbol *symbol__next_by_name(struct symbol *sym);
+1
tools/perf/util/symsrc.h
··· 26 26 GElf_Shdr opdshdr; 27 27 28 28 Elf_Scn *symtab; 29 + size_t symtab_idx; 29 30 GElf_Shdr symshdr; 30 31 31 32 Elf_Scn *dynsym;
+2 -2
tools/perf/util/synthetic-events.c
··· 2219 2219 2220 2220 len = pos->long_name_len + 1; 2221 2221 len = PERF_ALIGN(len, NAME_ALIGN); 2222 - memcpy(&ev.build_id.build_id, pos->bid.data, sizeof(pos->bid.data)); 2223 - ev.build_id.size = pos->bid.size; 2222 + ev.build_id.size = min(pos->bid.size, sizeof(pos->bid.data)); 2223 + memcpy(&ev.build_id.build_id, pos->bid.data, ev.build_id.size); 2224 2224 ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; 2225 2225 ev.build_id.header.misc = misc | PERF_RECORD_MISC_BUILD_ID_SIZE; 2226 2226 ev.build_id.pid = machine->pid;
+19
tools/perf/util/trace-event.h
··· 21 21 struct tep_plugin_list *plugin_list; 22 22 }; 23 23 24 + /* Computes a version number comparable with LIBTRACEEVENT_VERSION from Makefile.config. */ 25 + #define MAKE_LIBTRACEEVENT_VERSION(a, b, c) ((a)*255*255+(b)*255+(c)) 26 + 24 27 typedef char *(tep_func_resolver_t)(void *priv, 25 28 unsigned long long *addrp, char **modp); 26 29 ··· 139 136 140 137 #define SAMPLE_FLAGS_BUF_SIZE 64 141 138 int perf_sample__sprintf_flags(u32 flags, char *str, size_t sz); 139 + 140 + #if defined(LIBTRACEEVENT_VERSION) && LIBTRACEEVENT_VERSION >= MAKE_LIBTRACEEVENT_VERSION(1, 5, 0) 141 + #include <traceevent/event-parse.h> 142 + 143 + static inline bool tep_field_is_relative(unsigned long flags) 144 + { 145 + return (flags & TEP_FIELD_IS_RELATIVE) != 0; 146 + } 147 + #else 148 + #include <linux/compiler.h> 149 + 150 + static inline bool tep_field_is_relative(unsigned long flags __maybe_unused) 151 + { 152 + return false; 153 + } 154 + #endif 142 155 143 156 #endif /* _PERF_UTIL_TRACE_EVENT_H */