perf tools for v5.8:
- Further Intel PT call-trace fixes - Improve SELinux docs and tool warnings - Fix race at exit in 'perf record' using eventfd. - Add missing build tests to the default set of 'make -C tools/perf build-test' - Sync msr-index.h getting new AMD MSRs to decode and filter in 'perf trace'. - Fix fallback to libaudit in 'perf trace' for arches not using per-arch *.tbl files. - Fixes for 'perf ftrace'. - Fixes and improvements for the 'perf stat' metrics. - Use dummy event to get PERF_RECORD_{FORK,MMAP,etc} while synthesizing those metadata events for pre-existing threads. - Fix leaks detected using clang tooling. - Improvements to PMU event metric testing. - Report summary for 'perf stat' interval mode at the end, summing up all the intervals. - Improve pipe mode, i.e. this now works as expected, continuously dumping samples: # perf record -g -e raw_syscalls:sys_enter | perf --no-pager script - Fixes for event grouping, detecting incompatible groups such as: # perf stat -e '{cycles,power/energy-cores/}' -v WARNING: group events cpu maps do not match, disabling group: anon group { power/energy-cores/, cycles } power/energy-cores/: 0 cycles: 0-7 - Fixes for 'perf probe': blacklist address checking, number of kretprobe instances, etc. - JIT processing improvements and fixes plus the addition of a 'perf test' entry for the java demangler. - Add support for synthesizing first/last level cache, TLB and remove access events from HW tracing in the auxtrace code, first to use is ARM SPE. - Vendor events updates and fixes, including for POWER9 and Intel. - Allow using ~/.perfconfig for removing the ',' separators in 'perf stat' output. - Opt-in support for libpfm4. ================================================================================= Adrian Hunter (8): perf intel-pt: Use allocated branch stack for PEBS sample perf symbols: Fix debuginfo search for Ubuntu perf kcore_copy: Fix module map when there are no modules loaded perf evlist: Disable 'immediate' events last perf script: Fix --call-trace for Intel PT perf record: Respect --no-switch-events perf intel-pt: Refine kernel decoding only warning message perf symbols: Fix kernel maps for kcore and eBPF Alexey Budankov (3): perf docs: Extend CAP_SYS_ADMIN with CAP_PERFMON where needed perf tool: Make perf tool aware of SELinux access control perf docs: Introduce security.txt file to document related issues Anand K Mistry (1): perf record: Use an eventfd to wakeup when done Andi Kleen (1): perf script: Don't force less for non tty output with --xed Arnaldo Carvalho de Melo (21): perf evsel: Rename perf_evsel__object_config() to evsel__object_config() perf evsel: Rename perf_evsel__resort*() to evsel__resort*() perf evsel: Rename perf_evsel__fprintf() to evsel__fprintf() perf evsel: Rename *perf_evsel__get_config_term() & friends to evsel__env() perf evsel: Rename perf_evsel__new*() to evsel__new*() perf evsel: Rename perf_evsel__[hs]w_cache* to evsel__[hs]w_cache* perf counts: Rename perf_evsel__*counts() to evsel__*counts() perf parse-events: Fix incorrect conversion of 'if () free()' to 'zfree()' perf evsel: Initialize evsel->per_pkg_mask to NULL in evsel__init() tools feature: Rename HAVE_EVENTFD to HAVE_EVENTFD_SUPPORT perf build: Group the NO_SYSCALL_TABLE logic perf build: Allow explicitely disabling the NO_SYSCALL_TABLE variable perf trace: Remove union from syscalltbl, all the fields are needed perf trace: Use zalloc() to make sure all fields are zeroed in the syscalltbl constructor perf trace: Grow the syscall table as needed when using libaudit perf build: Remove libaudit from the default feature checks perf build: Add NO_SYSCALL_TABLE=1 to the build tests perf build: Add NO_LIBCRYPTO=1 to the default set of build tests perf build: Add NO_SDT=1 to the default set of build tests perf build: Add a LIBPFM4=1 build test entry tools arch x86: Sync the msr-index.h copy with the kernel sources Changbin Du (2): perf ftrace: Trace system wide if no target is given perf ftrace: Detect workload failure Ed Maste (1): perf tools: Correct license on jsmn JSON parser Gustavo A. R. Silva (2): perf tools: Replace zero-length array with flexible-array perf branch: Replace zero-length array with flexible-array Ian Rogers (38): perf expr: Allow for unlimited escaped characters in a symbol perf metrics: Fix parse errors in cascade lake metrics perf metrics: Fix parse errors in skylake metrics perf expr: Allow ',' to be an other token perf expr: Increase max other perf expr: Parse numbers as doubles perf expr: Debug lex if debugging yacc perf metrics: Fix parse errors in power8 metrics perf metrics: Fix parse errors in power9 metrics perf expr: Print a debug message for division by zero perf evsel: Dummy events never triggers, no need to ask for PERF_SAMPLE_BRANCH_STACK perf record: Add dummy event during system wide synthesis perf c2c: Fix 'perf c2c record -e list' to show the default events used perf evsel: Fix 2 memory leaks perf expr: Test parsing of floating point numbers perf expr: Fix memory leaks in metric bison perf parse-events: Make add PMU verbose output clearer perf test: Provide a subtest callback to ask for the reason for skipping a subtest perf test: Improve pmu event metric testing perf trace: Fix the selection for architectures to generate the errno name tables perf beauty: Allow the CC used in the arch errno names script to acccept CFLAGS perf tools: Grab a copy of libbpf's hashmap perf expr: Migrate expr ids table to a hashmap perf metricgroup: Make 'evlist_used' variable a bitmap instead of array of bools perf expr: Allow numbers to be followed by a dot perf metricgroup: Free metric_events on error perf metricgroup: Always place duration_time last perf metricgroup: Use early return in add_metric perf metricgroup: Delay events string creation perf metricgroup: Order event groups by size perf metricgroup: Remove duped metric group events perf metricgroup: Add options to not group or merge perf metricgroup: Remove unnecessary ',' from events perf list: Add metrics to command line usage tools compiler.h: Add attribute to disable tail calls perf tests: Don't tail call optimize in unwind test perf test: Initialize memory in dwarf-unwind perf libdw: Fix off-by 1 relative directory includes Jin Yao (6): perf parse-events: Use strcmp() to compare the PMU name perf stat: Fix wrong per-thread runtime stat for interval mode perf counts: Reset prev_raw_counts counts perf stat: Copy counts from prev_raw_counts to evsel->counts perf stat: Save aggr value to first member of prev_raw_counts perf stat: Report summary for interval mode Jiri Olsa (13): perf tools: Do not display extra info when there is nothing to build perf tools: Do not seek in pipe fd during tracing data processing perf session: Try to read pipe data from file perf callchain: Setup callchain properly in pipe mode perf script: Enable IP fields for callchains perf tools: Fix is_bpf_image function logic perf trace: Fix compilation error for make NO_LIBBPF=1 DEBUG=1 perf stat: Fix duration_time value for higher intervals perf stat: Fail on extra comma while parsing events perf tests: Consider subtests when searching for user specified tests perf stat: Do not pass avg to generic_metric perf parse: Add 'struct parse_events_state' pointer to scanner perf stat: Ensure group is defined on top of the same cpu mask Li Bin (1): perf util: Fix potential SEGFAULT in put_tracepoints_path error path Masami Hiramatsu (4): perf probe: Accept the instance number of kretprobe event perf probe: Fix to check blacklist address correctly perf probe: Check address correctness by map instead of _etext perf probe: Do not show the skipped events Nick Gasson (6): perf jvmti: Fix jitdump for methods without debug info perf jvmti: Do not report error when missing debug information perf tests: Add test for the java demangler perf jvmti: Fix demangling Java symbols perf jvmti: Remove redundant jitdump line table entries perf jit: Fix inaccurate DWARF line table Paul A. Clarke (5): perf stat: Increase perf metric output resolution perf vendor events power9: Add missing metrics to POWER9 'cpi_breakdown' perf stat: POWER9 metrics: expand "ICT" acronym perf script: Better align register values in dump perf config: Add stat.big-num support Ravi Bangoria (1): perf powerpc: Don't ignore sym-handling.c file Stephane Eranian (1): perf tools: Add optional support for libpfm4 Tan Xiaojun (3): perf tools: Move arm-spe-pkt-decoder.h/c to the new dir perf auxtrace: Add four itrace options perf arm-spe: Support synthetic events Tiezhu Yang (1): perf tools: Remove some duplicated includes Wang ShaoBo (1): perf bpf-loader: Add missing '*' for key_scan_pos Xie XiuQi (1): perf util: Fix memory leak of prefix_if_not_in ================================================================================= Test results: The first ones are container based builds of tools/perf with and without libelf support. Where clang is available, it is also used to build perf with/without libelf, and building with LIBCLANGLLVM=1 (built-in clang) with gcc and clang when clang and its devel libraries are installed. The objtool and samples/bpf/ builds are disabled now that I'm switching from using the sources in a local volume to fetching them from a http server to build it inside the container, to make it easier to build in a container cluster. Those will come back later. Several are cross builds, the ones with -x-ARCH and the android one, and those may not have all the features built, due to lack of multi-arch devel packages, available and being used so far on just a few, like debian:experimental-x-{arm64,mipsel}. The 'perf test' one will perform a variety of tests exercising tools/perf/util/, tools/lib/{bpf,traceevent,etc}, as well as run perf commands with a variety of command line event specifications to then intercept the sys_perf_event syscall to check that the perf_event_attr fields are set up as expected, among a variety of other unit tests. Then there is the 'make -C tools/perf build-test' ones, that build tools/perf/ with a variety of feature sets, exercising the build with an incomplete set of features as well as with a complete one. It is planned to have it run on each of the containers mentioned above, using some container orchestration infrastructure. Get in contact if interested in helping having this in place. Ubuntu 19.10 is failing when linking against libllvm, which isn't the default, needs to be investigated, haven't tested with CC=gcc, but should be the same problem: + make ARCH= CROSS_COMPILE= EXTRA_CFLAGS= LIBCLANGLLVM=1 -C /git/linux/tools/perf O=/tmp/build/perf CC=clang ... /usr/bin/ld: /usr/lib/llvm-9/lib/libclangAnalysis.a(ExprMutationAnalyzer.cpp.o): in function `clang::ast_matchers::internal::matcher_ignoringImpCasts0Matcher::matches(clang::Expr const&, clang::ast_matchers::internal::ASTMatchFinder*, clang::ast_matchers::internal::BoundNodesTreeBuilder*) const': (.text._ZNK5clang12ast_matchers8internal32matcher_ignoringImpCasts0Matcher7matchesERKNS_4ExprEPNS1_14ASTMatchFinderEPNS1_21BoundNodesTreeBuilderE[_ZNK5clang12ast_matchers8internal32matcher_ignoringImpCasts0Matcher7matchesERKNS_4ExprEPNS1_14ASTMatchFinderEPNS1_21BoundNodesTreeBuilderE]+0x43): undefined reference to `clang::ast_matchers::internal::DynTypedMatcher::matches(clang::ast_type_traits::DynTypedNode const&, clang::ast_matchers::internal::ASTMatchFinder*, clang::ast_matchers::internal::BoundNodesTreeBuilder*) const' /usr/bin/ld: /usr/lib/llvm-9/lib/libclangAnalysis.a(ExprMutationAnalyzer.cpp.o): in function `clang::ast_matchers::internal::matcher_hasLoopVariable0Matcher::matches(clang::CXXForRangeStmt const&, clang::ast_matchers::internal::ASTMatchFinder*, clang::ast_matchers::internal::BoundNodesTreeBuilder*) const': (.text._ZNK5clang12ast_matchers8internal31matcher_hasLoopVariable0Matcher7matchesERKNS_15CXXForRangeStmtEPNS1_14ASTMatchFinderEPNS1_21BoundNodesTreeBuilderE[_ZNK5clang12ast_matchers8internal31matcher_hasLoopVariable0Matcher7matchesERKNS_15CXXForRangeStmtEPNS1_14ASTMatchFinderEPNS1_21BoundNodesTreeBuilderE]+0x48): undefined reference to `clang::ast_matchers::internal::DynTypedMatcher::matches(clang::ast_type_traits::DynTypedNode const&, clang::ast_matchers::internal::ASTMatchFinder*, clang::ast_matchers::internal::BoundNodesTreeBuilder*) const' ... # export PERF_TARBALL=http://192.168.124.1/perf/perf-5.7.0-rc7.tar.xz # time dm 1 alpine:3.4 : Ok gcc (Alpine 5.3.0) 5.3.0, clang version 3.8.0 (tags/RELEASE_380/final) 2 alpine:3.5 : Ok gcc (Alpine 6.2.1) 6.2.1 20160822, clang version 3.8.1 (tags/RELEASE_381/final) 3 alpine:3.6 : Ok gcc (Alpine 6.3.0) 6.3.0, clang version 4.0.0 (tags/RELEASE_400/final) 4 alpine:3.7 : Ok gcc (Alpine 6.4.0) 6.4.0, Alpine clang version 5.0.0 (tags/RELEASE_500/final) (based on LLVM 5.0.0) 5 alpine:3.8 : Ok gcc (Alpine 6.4.0) 6.4.0, Alpine clang version 5.0.1 (tags/RELEASE_501/final) (based on LLVM 5.0.1) 6 alpine:3.9 : Ok gcc (Alpine 8.3.0) 8.3.0, Alpine clang version 5.0.1 (tags/RELEASE_502/final) (based on LLVM 5.0.1) 7 alpine:3.10 : Ok gcc (Alpine 8.3.0) 8.3.0, Alpine clang version 8.0.0 (tags/RELEASE_800/final) (based on LLVM 8.0.0) 8 alpine:3.11 : Ok gcc (Alpine 9.2.0) 9.2.0, Alpine clang version 9.0.0 (https://git.alpinelinux.org/aports f7f0d2c2b8bcd6a5843401a9a702029556492689) (based on LLVM 9.0.0) 9 alpine:3.12 : Ok gcc (Alpine 9.3.0) 9.3.0, Alpine clang version 10.0.0 (https://gitlab.alpinelinux.org/alpine/aports.git 7445adce501f8473efdb93b17b5eaf2f1445ed4c) 10 alpine:edge : Ok gcc (Alpine 9.3.0) 9.3.0, Alpine clang version 10.0.0 (git://git.alpinelinux.org/aports 7445adce501f8473efdb93b17b5eaf2f1445ed4c) 11 alt:p8 : Ok x86_64-alt-linux-gcc (GCC) 5.3.1 20151207 (ALT p8 5.3.1-alt3.M80P.1), clang version 3.8.0 (tags/RELEASE_380/final) 12 alt:p9 : Ok x86_64-alt-linux-gcc (GCC) 8.4.1 20200305 (ALT p9 8.4.1-alt0.p9.1), clang version 7.0.1 13 alt:sisyphus : Ok x86_64-alt-linux-gcc (GCC) 9.2.1 20200123 (ALT Sisyphus 9.2.1-alt3), clang version 10.0.0 14 amazonlinux:1 : Ok gcc (GCC) 7.2.1 20170915 (Red Hat 7.2.1-2), clang version 3.6.2 (tags/RELEASE_362/final) 15 amazonlinux:2 : Ok gcc (GCC) 7.3.1 20180712 (Red Hat 7.3.1-6), clang version 7.0.1 (Amazon Linux 2 7.0.1-1.amzn2.0.2) 16 android-ndk:r12b-arm : Ok arm-linux-androideabi-gcc (GCC) 4.9.x 20150123 (prerelease) 17 android-ndk:r15c-arm : Ok arm-linux-androideabi-gcc (GCC) 4.9.x 20150123 (prerelease) 18 centos:5 : Ok gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-55) 19 centos:6 : Ok gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-23) 20 centos:7 : Ok gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39) 21 centos:8 : Ok gcc (GCC) 8.3.1 20190507 (Red Hat 8.3.1-4), clang version 8.0.1 (Red Hat 8.0.1-1.module_el8.1.0+215+a01033fb) 22 clearlinux:latest : Ok gcc (Clear Linux OS for Intel Architecture) 9.3.1 20200501 releases/gcc-9.3.0-196-gcb2c76c8b1, clang version 10.0.0 23 debian:8 : Ok gcc (Debian 4.9.2-10+deb8u2) 4.9.2, Debian clang version 3.5.0-10 (tags/RELEASE_350/final) (based on LLVM 3.5.0) 24 debian:9 : Ok gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516, clang version 3.8.1-24 (tags/RELEASE_381/final) 25 debian:10 : Ok gcc (Debian 8.3.0-6) 8.3.0, clang version 7.0.1-8 (tags/RELEASE_701/final) 26 debian:experimental : FAIL gcc (Debian 9.3.0-13) 9.3.0, clang version 9.0.1-12 27 debian:experimental-x-arm64 : Ok aarch64-linux-gnu-gcc (Debian 9.3.0-8) 9.3.0 28 debian:experimental-x-mips : Ok mips-linux-gnu-gcc (Debian 8.3.0-19) 8.3.0 29 debian:experimental-x-mips64 : Ok mips64-linux-gnuabi64-gcc (Debian 9.3.0-8) 9.3.0 30 debian:experimental-x-mipsel : Ok mipsel-linux-gnu-gcc (Debian 9.2.1-8) 9.2.1 20190909 31 fedora:20 : Ok gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-7) 32 fedora:22 : Ok gcc (GCC) 5.3.1 20160406 (Red Hat 5.3.1-6), clang version 3.5.0 (tags/RELEASE_350/final) 33 fedora:23 : Ok gcc (GCC) 5.3.1 20160406 (Red Hat 5.3.1-6), clang version 3.7.0 (tags/RELEASE_370/final) 34 fedora:24 : Ok gcc (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1), clang version 3.8.1 (tags/RELEASE_381/final) 35 fedora:24-x-ARC-uClibc : Ok arc-linux-gcc (ARCompact ISA Linux uClibc toolchain 2017.09-rc2) 7.1.1 20170710 36 fedora:25 : Ok gcc (GCC) 6.4.1 20170727 (Red Hat 6.4.1-1), clang version 3.9.1 (tags/RELEASE_391/final) 37 fedora:26 : Ok gcc (GCC) 7.3.1 20180130 (Red Hat 7.3.1-2), clang version 4.0.1 (tags/RELEASE_401/final) 38 fedora:27 : Ok gcc (GCC) 7.3.1 20180712 (Red Hat 7.3.1-6), clang version 5.0.2 (tags/RELEASE_502/final) 39 fedora:28 : Ok gcc (GCC) 8.3.1 20190223 (Red Hat 8.3.1-2), clang version 6.0.1 (tags/RELEASE_601/final) 40 fedora:29 : Ok gcc (GCC) 8.3.1 20190223 (Red Hat 8.3.1-2), clang version 7.0.1 (Fedora 7.0.1-6.fc29) 41 fedora:30 : Ok gcc (GCC) 9.2.1 20190827 (Red Hat 9.2.1-1), clang version 8.0.0 (Fedora 8.0.0-3.fc30) 42 fedora:30-x-ARC-glibc : Ok arc-linux-gcc (ARC HS GNU/Linux glibc toolchain 2019.03-rc1) 8.3.1 20190225 43 fedora:30-x-ARC-uClibc : Ok arc-linux-gcc (ARCv2 ISA Linux uClibc toolchain 2019.03-rc1) 8.3.1 20190225 44 fedora:31 : Ok gcc (GCC) 9.3.1 20200408 (Red Hat 9.3.1-2), clang version 9.0.1 (Fedora 9.0.1-2.fc31) 45 fedora:32 : Ok gcc (GCC) 10.1.1 20200507 (Red Hat 10.1.1-1), clang version 10.0.0 (Fedora 10.0.0-1.fc32) 46 fedora:rawhide : Ok gcc (GCC) 10.0.1 20200216 (Red Hat 10.0.1-0.8), clang version 10.0.0 (Fedora 10.0.0-0.3.rc2.fc33) 47 gentoo-stage3-amd64:latest : Ok gcc (Gentoo 9.2.0-r2 p3) 9.2.0 48 mageia:5 : Ok gcc (GCC) 4.9.2, clang version 3.5.2 (tags/RELEASE_352/final) 49 mageia:6 : Ok gcc (Mageia 5.5.0-1.mga6) 5.5.0, clang version 3.9.1 (tags/RELEASE_391/final) 50 mageia:7 : Ok gcc (Mageia 8.3.1-0.20190524.1.mga7) 8.3.1 20190524, clang version 8.0.0 (Mageia 8.0.0-1.mga7) 51 manjaro:latest : Ok gcc (GCC) 9.2.0, clang version 9.0.0 (tags/RELEASE_900/final) 52 openmandriva:cooker : Ok gcc (GCC) 10.0.0 20200502 (OpenMandriva), clang version 10.0.1 53 opensuse:15.0 : Ok gcc (SUSE Linux) 7.4.1 20190424 [gcc-7-branch revision 270538], clang version 5.0.1 (tags/RELEASE_501/final 312548) 54 opensuse:15.1 : Ok gcc (SUSE Linux) 7.5.0, clang version 7.0.1 (tags/RELEASE_701/final 349238) 55 opensuse:15.2 : Ok gcc (SUSE Linux) 7.5.0, clang version 7.0.1 (tags/RELEASE_701/final 349238) 56 opensuse:42.3 : Ok gcc (SUSE Linux) 4.8.5, clang version 3.8.0 (tags/RELEASE_380/final 262553) 57 opensuse:tumbleweed : Ok gcc (SUSE Linux) 9.3.1 20200406 [revision 6db837a5288ee3ca5ec504fbd5a765817e556ac2], clang version 10.0.0 58 oraclelinux:6 : Ok gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-23.0.1) 59 oraclelinux:7 : Ok gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39.0.3) 60 oraclelinux:8 : Ok gcc (GCC) 8.3.1 20191121 (Red Hat 8.3.1-5.0.3), clang version 9.0.1 (Red Hat 9.0.1-2.0.1.module+el8.2.0+5599+9ed9ef6d) 61 ubuntu:12.04 : Ok gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3, Ubuntu clang version 3.0-6ubuntu3 (tags/RELEASE_30/final) (based on LLVM 3.0) 62 ubuntu:14.04 : Ok gcc (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4 63 ubuntu:16.04 : Ok gcc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609, clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final) 64 ubuntu:16.04-x-arm : Ok arm-linux-gnueabihf-gcc (Ubuntu/Linaro 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 65 ubuntu:16.04-x-arm64 : Ok aarch64-linux-gnu-gcc (Ubuntu/Linaro 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 66 ubuntu:16.04-x-powerpc : Ok powerpc-linux-gnu-gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 67 ubuntu:16.04-x-powerpc64 : Ok powerpc64-linux-gnu-gcc (Ubuntu/IBM 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 68 ubuntu:16.04-x-powerpc64el : Ok powerpc64le-linux-gnu-gcc (Ubuntu/IBM 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 69 ubuntu:16.04-x-s390 : Ok s390x-linux-gnu-gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 70 ubuntu:18.04 : Ok gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0, clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final) 71 ubuntu:18.04-x-arm : Ok arm-linux-gnueabihf-gcc (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0 72 ubuntu:18.04-x-arm64 : Ok aarch64-linux-gnu-gcc (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0 73 ubuntu:18.04-x-m68k : Ok m68k-linux-gnu-gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 74 ubuntu:18.04-x-powerpc : Ok powerpc-linux-gnu-gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0 75 ubuntu:18.04-x-powerpc64 : Ok powerpc64-linux-gnu-gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0 76 ubuntu:18.04-x-powerpc64el : Ok powerpc64le-linux-gnu-gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 77 ubuntu:18.04-x-riscv64 : Ok riscv64-linux-gnu-gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 78 ubuntu:18.04-x-s390 : Ok s390x-linux-gnu-gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 79 ubuntu:18.04-x-sh4 : Ok sh4-linux-gnu-gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0 80 ubuntu:18.04-x-sparc64 : Ok sparc64-linux-gnu-gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 81 ubuntu:18.10 : Ok gcc (Ubuntu 8.3.0-6ubuntu1~18.10.1) 8.3.0, clang version 7.0.0-3 (tags/RELEASE_700/final) 82 ubuntu:19.04 : Ok gcc (Ubuntu 8.3.0-6ubuntu1) 8.3.0, clang version 8.0.0-3 (tags/RELEASE_800/final) 83 ubuntu:19.04-x-alpha : Ok alpha-linux-gnu-gcc (Ubuntu 8.3.0-6ubuntu1) 8.3.0 84 ubuntu:19.04-x-arm64 : Ok aarch64-linux-gnu-gcc (Ubuntu/Linaro 8.3.0-6ubuntu1) 8.3.0 85 ubuntu:19.04-x-hppa : Ok hppa-linux-gnu-gcc (Ubuntu 8.3.0-6ubuntu1) 8.3.0 86 ubuntu:19.10 : FAIL gcc (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008, clang version 9.0.0-2 (tags/RELEASE_900/final) 87 ubuntu:20.04 : Ok gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0, clang version 10.0.0-4ubuntu1 # It builds ok with the default set of options. The "7: Simple expression parser" entry is failing due to a bug in the hashmap in libbpf that will hit upstream via the bpf tree. # uname -a Linux five 5.5.17-200.fc31.x86_64 #1 SMP Mon Apr 13 15:29:42 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux # git log --oneline -13e9b26dc22
perf tools: Remove some duplicated includes # perf version --build-options perf version 5.7.rc7.g0affd0e5262b dwarf: [ on ] # HAVE_DWARF_SUPPORT dwarf_getlocations: [ on ] # HAVE_DWARF_GETLOCATIONS_SUPPORT glibc: [ on ] # HAVE_GLIBC_SUPPORT gtk2: [ on ] # HAVE_GTK2_SUPPORT syscall_table: [ on ] # HAVE_SYSCALL_TABLE_SUPPORT libbfd: [ on ] # HAVE_LIBBFD_SUPPORT libelf: [ on ] # HAVE_LIBELF_SUPPORT libnuma: [ on ] # HAVE_LIBNUMA_SUPPORT numa_num_possible_cpus: [ on ] # HAVE_LIBNUMA_SUPPORT libperl: [ on ] # HAVE_LIBPERL_SUPPORT libpython: [ on ] # HAVE_LIBPYTHON_SUPPORT libslang: [ on ] # HAVE_SLANG_SUPPORT libcrypto: [ on ] # HAVE_LIBCRYPTO_SUPPORT libunwind: [ on ] # HAVE_LIBUNWIND_SUPPORT libdw-dwarf-unwind: [ on ] # HAVE_DWARF_SUPPORT zlib: [ on ] # HAVE_ZLIB_SUPPORT lzma: [ on ] # HAVE_LZMA_SUPPORT get_cpuid: [ on ] # HAVE_AUXTRACE_SUPPORT bpf: [ on ] # HAVE_LIBBPF_SUPPORT aio: [ on ] # HAVE_AIO_SUPPORT zstd: [ on ] # HAVE_ZSTD_SUPPORT # perf test 1: vmlinux symtab matches kallsyms : Ok 2: Detect openat syscall event : Ok 3: Detect openat syscall event on all cpus : Ok 4: Read samples using the mmap interface : Ok 5: Test data source output : Ok 6: Parse event definition strings : Ok 7: Simple expression parser : FAILED! 8: PERF_RECORD_* events & perf_sample fields : Ok 9: Parse perf pmu format : Ok 10: PMU events : 10.1: PMU event table sanity : Ok 10.2: PMU event map aliases : Ok 10.3: Parsing of PMU event table metrics : Ok 11: DSO data read : Ok 12: DSO data cache : Ok 13: DSO data reopen : Ok 14: Roundtrip evsel->name : Ok 15: Parse sched tracepoints fields : Ok 16: syscalls:sys_enter_openat event fields : Ok 17: Setup struct perf_event_attr : Ok 18: Match and link multiple hists : Ok 19: 'import perf' in python : Ok 20: Breakpoint overflow signal handler : Ok 21: Breakpoint overflow sampling : Ok 22: Breakpoint accounting : Ok 23: Watchpoint : 23.1: Read Only Watchpoint : Skip 23.2: Write Only Watchpoint : Ok 23.3: Read / Write Watchpoint : Ok 23.4: Modify Watchpoint : Ok 24: Number of exit events of a simple workload : Ok 25: Software clock events period values : Ok 26: Object code reading : Ok 27: Sample parsing : Ok 28: Use a dummy software event to keep tracking : Ok 29: Parse with no sample_id_all bit set : Ok 30: Filter hist entries : Ok 31: Lookup mmap thread : Ok 32: Share thread maps : Ok 33: Sort output of hist entries : Ok 34: Cumulate child hist entries : Ok 35: Track with sched_switch : Ok 36: Filter fds with revents mask in a fdarray : Ok 37: Add fd to a fdarray, making it autogrow : Ok 38: kmod_path__parse : Ok 39: Thread map : Ok 40: LLVM search and compile : 40.1: Basic BPF llvm compile : Ok 40.2: kbuild searching : Ok 40.3: Compile source for BPF prologue generation : Ok 40.4: Compile source for BPF relocation : Ok 41: Session topology : Ok 42: BPF filter : 42.1: Basic BPF filtering : Ok 42.2: BPF pinning : Ok 42.3: BPF prologue generation : Ok 42.4: BPF relocation checker : Ok 43: Synthesize thread map : Ok 44: Remove thread map : Ok 45: Synthesize cpu map : Ok 46: Synthesize stat config : Ok 47: Synthesize stat : Ok 48: Synthesize stat round : Ok 49: Synthesize attr update : Ok 50: Event times : Ok 51: Read backward ring buffer : Ok 52: Print cpu map : Ok 53: Merge cpu map : Ok 54: Probe SDT events : Ok 55: is_printable_array : Ok 56: Print bitmap : Ok 57: perf hooks : Ok 58: builtin clang support : Skip (not compiled in) 59: unit_number__scnprintf : Ok 60: mem2node : Ok 61: time utils : Ok 62: Test jit_write_elf : Ok 63: Test libpfm4 support : Skip (not compiled in) 64: Test api io : Ok 65: maps__merge_in : Ok 66: Demangle Java : Ok 67: x86 rdpmc : Ok 68: Convert perf time to TSC : Ok 69: DWARF unwind : Ok 70: x86 instruction decoder - new instructions : Ok 71: Intel PT packet decoder : Ok 72: x86 bp modify : Ok 73: probe libc's inet_pton & backtrace it with ping : Ok 74: Use vfs_getname probe to get syscall args filenames : Ok 75: Check open filename arg using perf trace + vfs_getname: Ok 76: Zstd perf.data compression/decompression : Ok 77: Add vfs_getname probe to get syscall args filenames : Ok # [acme@five perf]$ git log --oneline -1 ; time make -C tools/perf build-test3e9b26dc22
(HEAD -> perf/core, seventh/perf/core, quaco/perf/core) perf tools: Remove some duplicated includes make: Entering directory '/home/acme/git/perf/tools/perf' - tarpkg: ./tests/perf-targz-src-pkg . make_no_libbpf_DEBUG_O: make NO_LIBBPF=1 DEBUG=1 make_with_clangllvm_O: make LIBCLANGLLVM=1 make_no_auxtrace_O: make NO_AUXTRACE=1 make_no_demangle_O: make NO_DEMANGLE=1 make_clean_all_O: make clean all make_util_pmu_bison_o_O: make util/pmu-bison.o make_no_newt_O: make NO_NEWT=1 make_no_gtk2_O: make NO_GTK2=1 make_no_libbpf_O: make NO_LIBBPF=1 make_no_libaudit_O: make NO_LIBAUDIT=1 make_perf_o_O: make perf.o make_no_libelf_O: make NO_LIBELF=1 make_static_O: make LDFLAGS=-static NO_PERF_READ_VDSO32=1 NO_PERF_READ_VDSOX32=1 NO_JVMTI=1 make_install_O: make install make_no_libpython_O: make NO_LIBPYTHON=1 make_no_libunwind_O: make NO_LIBUNWIND=1 make_no_libdw_dwarf_unwind_O: make NO_LIBDW_DWARF_UNWIND=1 make_no_scripts_O: make NO_LIBPYTHON=1 NO_LIBPERL=1 make_with_libpfm4_O: make LIBPFM4=1 make_pure_O: make make_doc_O: make doc make_no_sdt_O: make NO_SDT=1 make_no_slang_O: make NO_SLANG=1 make_no_syscall_tbl_O: make NO_SYSCALL_TABLE=1 make_install_bin_O: make install-bin make_no_libperl_O: make NO_LIBPERL=1 make_install_prefix_slash_O: make install prefix=/tmp/krava/ make_install_prefix_O: make install prefix=/tmp/krava make_no_ui_O: make NO_NEWT=1 NO_SLANG=1 NO_GTK2=1 make_no_libnuma_O: make NO_LIBNUMA=1 make_minimal_O: make NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1 NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1 NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1 NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1 NO_LIBCRYPTO=1 NO_SDT=1 NO_JVMTI=1 NO_LIBZSTD=1 NO_LIBCAP=1 NO_SYSCALL_TABLE=1 make_with_babeltrace_O: make LIBBABELTRACE=1 make_help_O: make help make_no_libcrypto_O: make NO_LIBCRYPTO=1 make_debug_O: make DEBUG=1 make_no_libbionic_O: make NO_LIBBIONIC=1 make_no_backtrace_O: make NO_BACKTRACE=1 make_tags_O: make tags make_util_map_o_O: make util/map.o OK make: Leaving directory '/home/acme/git/perf/tools/perf' [acme@five perf]$ -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQR2GiIUctdOfX2qHhGyPKLppCJ+JwUCXtaWmgAKCRCyPKLppCJ+ J4mdAQD9wzSFCI96jZkWvCx61AkoZkcG8fyaWBpH+7Wuum3J1QD/Q9c37NGepK3T /sMFKufuBF7Z0Uy2toMz9i9P/KLaEQI= =dNn2 -----END PGP SIGNATURE----- Merge tag 'perf-tools-2020-06-02' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux Pull perf tooling updates from Arnaldo Carvalho de Melo: "These are additional changes to the perf tools, on top of what Ingo already submitted. - Further Intel PT call-trace fixes - Improve SELinux docs and tool warnings - Fix race at exit in 'perf record' using eventfd. - Add missing build tests to the default set of 'make -C tools/perf build-test' - Sync msr-index.h getting new AMD MSRs to decode and filter in 'perf trace'. - Fix fallback to libaudit in 'perf trace' for arches not using per-arch *.tbl files. - Fixes for 'perf ftrace'. - Fixes and improvements for the 'perf stat' metrics. - Use dummy event to get PERF_RECORD_{FORK,MMAP,etc} while synthesizing those metadata events for pre-existing threads. - Fix leaks detected using clang tooling. - Improvements to PMU event metric testing. - Report summary for 'perf stat' interval mode at the end, summing up all the intervals. - Improve pipe mode, i.e. this now works as expected, continuously dumping samples: # perf record -g -e raw_syscalls:sys_enter | perf --no-pager script - Fixes for event grouping, detecting incompatible groups such as: # perf stat -e '{cycles,power/energy-cores/}' -v WARNING: group events cpu maps do not match, disabling group: anon group { power/energy-cores/, cycles } power/energy-cores/: 0 cycles: 0-7 - Fixes for 'perf probe': blacklist address checking, number of kretprobe instances, etc. - JIT processing improvements and fixes plus the addition of a 'perf test' entry for the java demangler. - Add support for synthesizing first/last level cache, TLB and remove access events from HW tracing in the auxtrace code, first to use is ARM SPE. - Vendor events updates and fixes, including for POWER9 and Intel. - Allow using ~/.perfconfig for removing the ',' separators in 'perf stat' output. - Opt-in support for libpfm4" * tag 'perf-tools-2020-06-02' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux: (120 commits) perf tools: Remove some duplicated includes perf symbols: Fix kernel maps for kcore and eBPF tools arch x86: Sync the msr-index.h copy with the kernel sources perf stat: Ensure group is defined on top of the same cpu mask perf libdw: Fix off-by 1 relative directory includes perf arm-spe: Support synthetic events perf auxtrace: Add four itrace options perf tools: Move arm-spe-pkt-decoder.h/c to the new dir perf test: Initialize memory in dwarf-unwind perf tests: Don't tail call optimize in unwind test tools compiler.h: Add attribute to disable tail calls perf build: Add a LIBPFM4=1 build test entry perf tools: Add optional support for libpfm4 perf tools: Correct license on jsmn JSON parser perf jit: Fix inaccurate DWARF line table perf jvmti: Remove redundant jitdump line table entries perf build: Add NO_SDT=1 to the default set of build tests perf build: Add NO_LIBCRYPTO=1 to the default set of build tests perf build: Add NO_SYSCALL_TABLE=1 to the build tests perf build: Remove libaudit from the default feature checks ...
This commit is contained in:
commit
38b3a5aaf2
|
@ -301,6 +301,9 @@
|
|||
#define MSR_PP1_ENERGY_STATUS 0x00000641
|
||||
#define MSR_PP1_POLICY 0x00000642
|
||||
|
||||
#define MSR_AMD_PKG_ENERGY_STATUS 0xc001029b
|
||||
#define MSR_AMD_RAPL_POWER_UNIT 0xc0010299
|
||||
|
||||
/* Config TDP MSRs */
|
||||
#define MSR_CONFIG_TDP_NOMINAL 0x00000648
|
||||
#define MSR_CONFIG_TDP_LEVEL_1 0x00000649
|
||||
|
|
|
@ -40,7 +40,6 @@ FEATURE_TESTS_BASIC := \
|
|||
glibc \
|
||||
gtk2 \
|
||||
gtk2-infobar \
|
||||
libaudit \
|
||||
libbfd \
|
||||
libcap \
|
||||
libelf \
|
||||
|
@ -112,7 +111,6 @@ FEATURE_DISPLAY ?= \
|
|||
dwarf_getlocations \
|
||||
glibc \
|
||||
gtk2 \
|
||||
libaudit \
|
||||
libbfd \
|
||||
libcap \
|
||||
libelf \
|
||||
|
|
|
@ -91,7 +91,7 @@ __BUILDXX = $(CXX) $(CXXFLAGS) -MD -Wall -Werror -o $@ $(patsubst %.bin,%.cpp,$(
|
|||
###############################
|
||||
|
||||
$(OUTPUT)test-all.bin:
|
||||
$(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz -llzma
|
||||
$(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz -llzma
|
||||
|
||||
$(OUTPUT)test-hello.bin:
|
||||
$(BUILD)
|
||||
|
|
|
@ -74,10 +74,6 @@
|
|||
# include "test-libunwind.c"
|
||||
#undef main
|
||||
|
||||
#define main main_test_libaudit
|
||||
# include "test-libaudit.c"
|
||||
#undef main
|
||||
|
||||
#define main main_test_libslang
|
||||
# include "test-libslang.c"
|
||||
#undef main
|
||||
|
@ -208,7 +204,6 @@ int main(int argc, char *argv[])
|
|||
main_test_libelf_gelf_getnote();
|
||||
main_test_libelf_getshdrstrndx();
|
||||
main_test_libunwind();
|
||||
main_test_libaudit();
|
||||
main_test_libslang();
|
||||
main_test_gtk2(argc, argv);
|
||||
main_test_gtk2_infobar(argc, argv);
|
||||
|
|
|
@ -27,6 +27,18 @@
|
|||
#define __pure __attribute__((pure))
|
||||
#endif
|
||||
#define noinline __attribute__((noinline))
|
||||
#ifdef __has_attribute
|
||||
#if __has_attribute(disable_tail_calls)
|
||||
#define __no_tail_call __attribute__((disable_tail_calls))
|
||||
#endif
|
||||
#endif
|
||||
#ifndef __no_tail_call
|
||||
#if GCC_VERSION > 40201
|
||||
#define __no_tail_call __attribute__((optimize("no-optimize-sibling-calls")))
|
||||
#else
|
||||
#define __no_tail_call
|
||||
#endif
|
||||
#endif
|
||||
#ifndef __packed
|
||||
#define __packed __attribute__((packed))
|
||||
#endif
|
||||
|
|
|
@ -47,6 +47,9 @@
|
|||
#ifndef noinline
|
||||
#define noinline
|
||||
#endif
|
||||
#ifndef __no_tail_call
|
||||
#define __no_tail_call
|
||||
#endif
|
||||
|
||||
/* Are two types/vars the same type (ignoring qualifiers)? */
|
||||
#ifndef __same_type
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
i synthesize instructions events
|
||||
b synthesize branches events
|
||||
b synthesize branches events (branch misses for Arm SPE)
|
||||
c synthesize branches events (calls only)
|
||||
r synthesize branches events (returns only)
|
||||
x synthesize transactions events
|
||||
|
@ -9,6 +9,10 @@
|
|||
of aux-output (refer to perf record)
|
||||
e synthesize error events
|
||||
d create a debug log
|
||||
f synthesize first level cache events
|
||||
m synthesize last level cache events
|
||||
t synthesize TLB events
|
||||
a synthesize remote access events
|
||||
g synthesize a call chain (use with i or x)
|
||||
G synthesize a call chain on existing event records
|
||||
l synthesize last branch entries (use with i or x)
|
||||
|
|
|
@ -40,7 +40,7 @@ RECORD OPTIONS
|
|||
--------------
|
||||
-e::
|
||||
--event=::
|
||||
Select the PMU event. Use 'perf mem record -e list'
|
||||
Select the PMU event. Use 'perf c2c record -e list'
|
||||
to list available events.
|
||||
|
||||
-v::
|
||||
|
|
|
@ -667,6 +667,11 @@ convert.*::
|
|||
Limit the size of ordered_events queue, so we could control
|
||||
allocation size of perf data files without proper finished
|
||||
round events.
|
||||
stat.*::
|
||||
|
||||
stat.big-num::
|
||||
(boolean) Change the default for "--big-num". To make
|
||||
"--no-big-num" the default, set "stat.big-num=false".
|
||||
|
||||
intel-pt.*::
|
||||
|
||||
|
|
|
@ -687,7 +687,7 @@ The v4.2 kernel introduced support for a context switch metadata event,
|
|||
PERF_RECORD_SWITCH, which allows unprivileged users to see when their processes
|
||||
are scheduled out and in, just not by whom, which is left for the
|
||||
PERF_RECORD_SWITCH_CPU_WIDE, that is only accessible in system wide context,
|
||||
which in turn requires CAP_SYS_ADMIN.
|
||||
which in turn requires CAP_PERFMON or CAP_SYS_ADMIN.
|
||||
|
||||
Please see the 45ac1403f564 ("perf: Add PERF_RECORD_SWITCH to indicate context
|
||||
switches") commit, that introduces these metadata events for further info.
|
||||
|
|
|
@ -458,7 +458,9 @@ This option sets the time out limit. The default value is 500 ms.
|
|||
|
||||
--switch-events::
|
||||
Record context switch events i.e. events of type PERF_RECORD_SWITCH or
|
||||
PERF_RECORD_SWITCH_CPU_WIDE.
|
||||
PERF_RECORD_SWITCH_CPU_WIDE. In some cases (e.g. Intel PT or CoreSight)
|
||||
switch events will be enabled automatically, which can be suppressed by
|
||||
by the option --no-switch-events.
|
||||
|
||||
--clang-path=PATH::
|
||||
Path to clang binary to use for compiling BPF scriptlets.
|
||||
|
@ -613,6 +615,17 @@ appended unit character - B/K/M/G
|
|||
The number of threads to run when synthesizing events for existing processes.
|
||||
By default, the number of threads equals 1.
|
||||
|
||||
ifdef::HAVE_LIBPFM[]
|
||||
--pfm-events events::
|
||||
Select a PMU event using libpfm4 syntax (see http://perfmon2.sf.net)
|
||||
including support for event filters. For example '--pfm-events
|
||||
inst_retired:any_p:u:c=1:i'. More than one event can be passed to the
|
||||
option using the comma separator. Hardware events and generic hardware
|
||||
events cannot be mixed together. The latter must be used with the -e
|
||||
option. The -e option and this one can be mixed and matched. Events
|
||||
can be grouped using the {} notation.
|
||||
endif::HAVE_LIBPFM[]
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-intel-pt[1]
|
||||
|
|
|
@ -71,6 +71,16 @@ report::
|
|||
--tid=<tid>::
|
||||
stat events on existing thread id (comma separated list)
|
||||
|
||||
ifdef::HAVE_LIBPFM[]
|
||||
--pfm-events events::
|
||||
Select a PMU event using libpfm4 syntax (see http://perfmon2.sf.net)
|
||||
including support for event filters. For example '--pfm-events
|
||||
inst_retired:any_p:u:c=1:i'. More than one event can be passed to the
|
||||
option using the comma separator. Hardware events and generic hardware
|
||||
events cannot be mixed together. The latter must be used with the -e
|
||||
option. The -e option and this one can be mixed and matched. Events
|
||||
can be grouped using the {} notation.
|
||||
endif::HAVE_LIBPFM[]
|
||||
|
||||
-a::
|
||||
--all-cpus::
|
||||
|
@ -93,7 +103,9 @@ report::
|
|||
|
||||
-B::
|
||||
--big-num::
|
||||
print large numbers with thousands' separators according to locale
|
||||
print large numbers with thousands' separators according to locale.
|
||||
Enabled by default. Use "--no-big-num" to disable.
|
||||
Default setting can be changed with "perf config stat.big-num=false".
|
||||
|
||||
-C::
|
||||
--cpu=::
|
||||
|
@ -234,6 +246,25 @@ filter out the startup phase of the program, which is often very different.
|
|||
|
||||
Print statistics of transactional execution if supported.
|
||||
|
||||
--metric-no-group::
|
||||
By default, events to compute a metric are placed in weak groups. The
|
||||
group tries to enforce scheduling all or none of the events. The
|
||||
--metric-no-group option places events outside of groups and may
|
||||
increase the chance of the event being scheduled - leading to more
|
||||
accuracy. However, as events may not be scheduled together accuracy
|
||||
for metrics like instructions per cycle can be lower - as both metrics
|
||||
may no longer be being measured at the same time.
|
||||
|
||||
--metric-no-merge::
|
||||
By default metric events in different weak groups can be shared if one
|
||||
group contains all the events needed by another. In such cases one
|
||||
group will be eliminated reducing event multiplexing and making it so
|
||||
that certain groups of metrics sum to 100%. A downside to sharing a
|
||||
group is that the group may require multiplexing and so accuracy for a
|
||||
small group that need not have multiplexing is lowered. This option
|
||||
forbids the event merging logic from sharing events between groups and
|
||||
may be used to increase accuracy in this case.
|
||||
|
||||
STAT RECORD
|
||||
-----------
|
||||
Stores stat data into perf data file.
|
||||
|
|
|
@ -329,6 +329,17 @@ Default is to monitor all CPUS.
|
|||
The known limitations include exception handing such as
|
||||
setjmp/longjmp will have calls/returns not match.
|
||||
|
||||
ifdef::HAVE_LIBPFM[]
|
||||
--pfm-events events::
|
||||
Select a PMU event using libpfm4 syntax (see http://perfmon2.sf.net)
|
||||
including support for event filters. For example '--pfm-events
|
||||
inst_retired:any_p:u:c=1:i'. More than one event can be passed to the
|
||||
option using the comma separator. Hardware events and generic hardware
|
||||
events cannot be mixed together. The latter must be used with the -e
|
||||
option. The -e option and this one can be mixed and matched. Events
|
||||
can be grouped using the {} notation.
|
||||
endif::HAVE_LIBPFM[]
|
||||
|
||||
INTERACTIVE PROMPTING KEYS
|
||||
--------------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
Overview
|
||||
========
|
||||
|
||||
For general security related questions of perf_event_open() syscall usage,
|
||||
performance monitoring and observability operations by Perf see here:
|
||||
https://www.kernel.org/doc/html/latest/admin-guide/perf-security.html
|
||||
|
||||
Enabling LSM based mandatory access control (MAC) to perf_event_open() syscall
|
||||
==============================================================================
|
||||
|
||||
LSM hooks for mandatory access control for perf_event_open() syscall can be
|
||||
used starting from Linux v5.3. Below are the steps to extend Fedora (v31) with
|
||||
Targeted policy with perf_event_open() access control capabilities:
|
||||
|
||||
1. Download selinux-policy SRPM package (e.g. selinux-policy-3.14.4-48.fc31.src.rpm on FC31)
|
||||
and install it so rpmbuild directory would exist in the current working directory:
|
||||
|
||||
# rpm -Uhv selinux-policy-3.14.4-48.fc31.src.rpm
|
||||
|
||||
2. Get into rpmbuild/SPECS directory and unpack the source code:
|
||||
|
||||
# rpmbuild -bp selinux-policy.spec
|
||||
|
||||
3. Place patch below at rpmbuild/BUILD/selinux-policy-b86eaaf4dbcf2d51dd4432df7185c0eaf3cbcc02
|
||||
directory and apply it:
|
||||
|
||||
# patch -p1 < selinux-policy-perf-events-perfmon.patch
|
||||
patching file policy/flask/access_vectors
|
||||
patching file policy/flask/security_classes
|
||||
# cat selinux-policy-perf-events-perfmon.patch
|
||||
diff -Nura a/policy/flask/access_vectors b/policy/flask/access_vectors
|
||||
--- a/policy/flask/access_vectors 2020-02-04 18:19:53.000000000 +0300
|
||||
+++ b/policy/flask/access_vectors 2020-02-28 23:37:25.000000000 +0300
|
||||
@@ -174,6 +174,7 @@
|
||||
wake_alarm
|
||||
block_suspend
|
||||
audit_read
|
||||
+ perfmon
|
||||
}
|
||||
|
||||
#
|
||||
@@ -1099,3 +1100,15 @@
|
||||
|
||||
class xdp_socket
|
||||
inherits socket
|
||||
+
|
||||
+class perf_event
|
||||
+{
|
||||
+ open
|
||||
+ cpu
|
||||
+ kernel
|
||||
+ tracepoint
|
||||
+ read
|
||||
+ write
|
||||
+}
|
||||
+
|
||||
+
|
||||
diff -Nura a/policy/flask/security_classes b/policy/flask/security_classes
|
||||
--- a/policy/flask/security_classes 2020-02-04 18:19:53.000000000 +0300
|
||||
+++ b/policy/flask/security_classes 2020-02-28 21:35:17.000000000 +0300
|
||||
@@ -200,4 +200,6 @@
|
||||
|
||||
class xdp_socket
|
||||
|
||||
+class perf_event
|
||||
+
|
||||
# FLASK
|
||||
|
||||
4. Get into rpmbuild/SPECS directory and build policy packages from patched sources:
|
||||
|
||||
# rpmbuild --noclean --noprep -ba selinux-policy.spec
|
||||
|
||||
so you have this:
|
||||
|
||||
# ls -alh rpmbuild/RPMS/noarch/
|
||||
total 33M
|
||||
drwxr-xr-x. 2 root root 4.0K Mar 20 12:16 .
|
||||
drwxr-xr-x. 3 root root 4.0K Mar 20 12:16 ..
|
||||
-rw-r--r--. 1 root root 112K Mar 20 12:16 selinux-policy-3.14.4-48.fc31.noarch.rpm
|
||||
-rw-r--r--. 1 root root 1.2M Mar 20 12:17 selinux-policy-devel-3.14.4-48.fc31.noarch.rpm
|
||||
-rw-r--r--. 1 root root 2.3M Mar 20 12:17 selinux-policy-doc-3.14.4-48.fc31.noarch.rpm
|
||||
-rw-r--r--. 1 root root 12M Mar 20 12:17 selinux-policy-minimum-3.14.4-48.fc31.noarch.rpm
|
||||
-rw-r--r--. 1 root root 4.5M Mar 20 12:16 selinux-policy-mls-3.14.4-48.fc31.noarch.rpm
|
||||
-rw-r--r--. 1 root root 111K Mar 20 12:16 selinux-policy-sandbox-3.14.4-48.fc31.noarch.rpm
|
||||
-rw-r--r--. 1 root root 14M Mar 20 12:17 selinux-policy-targeted-3.14.4-48.fc31.noarch.rpm
|
||||
|
||||
5. Install SELinux packages from Fedora repo, if not already done so, and
|
||||
update with the patched rpms above:
|
||||
|
||||
# rpm -Uhv rpmbuild/RPMS/noarch/selinux-policy-*
|
||||
|
||||
6. Enable SELinux Permissive mode for Targeted policy, if not already done so:
|
||||
|
||||
# cat /etc/selinux/config
|
||||
|
||||
# This file controls the state of SELinux on the system.
|
||||
# SELINUX= can take one of these three values:
|
||||
# enforcing - SELinux security policy is enforced.
|
||||
# permissive - SELinux prints warnings instead of enforcing.
|
||||
# disabled - No SELinux policy is loaded.
|
||||
SELINUX=permissive
|
||||
# SELINUXTYPE= can take one of these three values:
|
||||
# targeted - Targeted processes are protected,
|
||||
# minimum - Modification of targeted policy. Only selected processes are protected.
|
||||
# mls - Multi Level Security protection.
|
||||
SELINUXTYPE=targeted
|
||||
|
||||
7. Enable filesystem SELinux labeling at the next reboot:
|
||||
|
||||
# touch /.autorelabel
|
||||
|
||||
8. Reboot machine and it will label filesystems and load Targeted policy into the kernel;
|
||||
|
||||
9. Login and check that dmesg output doesn't mention that perf_event class is unknown to SELinux subsystem;
|
||||
|
||||
10. Check that SELinux is enabled and in Permissive mode
|
||||
|
||||
# getenforce
|
||||
Permissive
|
||||
|
||||
11. Turn SELinux into Enforcing mode:
|
||||
|
||||
# setenforce 1
|
||||
# getenforce
|
||||
Enforcing
|
||||
|
||||
Opening access to perf_event_open() syscall on Fedora with SELinux
|
||||
==================================================================
|
||||
|
||||
Access to performance monitoring and observability operations by Perf
|
||||
can be limited for superuser or CAP_PERFMON or CAP_SYS_ADMIN privileged
|
||||
processes. MAC policy settings (e.g. SELinux) can be loaded into the kernel
|
||||
and prevent unauthorized access to perf_event_open() syscall. In such case
|
||||
Perf tool provides a message similar to the one below:
|
||||
|
||||
# perf stat
|
||||
Error:
|
||||
Access to performance monitoring and observability operations is limited.
|
||||
Enforced MAC policy settings (SELinux) can limit access to performance
|
||||
monitoring and observability operations. Inspect system audit records for
|
||||
more perf_event access control information and adjusting the policy.
|
||||
Consider adjusting /proc/sys/kernel/perf_event_paranoid setting to open
|
||||
access to performance monitoring and observability operations for users
|
||||
without CAP_PERFMON or CAP_SYS_ADMIN Linux capability.
|
||||
perf_event_paranoid setting is -1:
|
||||
-1: Allow use of (almost) all events by all users
|
||||
Ignore mlock limit after perf_event_mlock_kb without CAP_IPC_LOCK
|
||||
>= 0: Disallow raw and ftrace function tracepoint access
|
||||
>= 1: Disallow CPU event access
|
||||
>= 2: Disallow kernel profiling
|
||||
To make the adjusted perf_event_paranoid setting permanent preserve it
|
||||
in /etc/sysctl.conf (e.g. kernel.perf_event_paranoid = <setting>)
|
||||
|
||||
To make sure that access is limited by MAC policy settings inspect system
|
||||
audit records using journalctl command or /var/log/audit/audit.log so the
|
||||
output would contain AVC denied records related to perf_event:
|
||||
|
||||
# journalctl --reverse --no-pager | grep perf_event
|
||||
|
||||
python3[1318099]: SELinux is preventing perf from open access on the perf_event labeled unconfined_t.
|
||||
If you believe that perf should be allowed open access on perf_event labeled unconfined_t by default.
|
||||
setroubleshoot[1318099]: SELinux is preventing perf from open access on the perf_event labeled unconfined_t. For complete SELinux messages run: sealert -l 4595ce5b-e58f-462c-9d86-3bc2074935de
|
||||
audit[1318098]: AVC avc: denied { open } for pid=1318098 comm="perf" scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tclass=perf_event permissive=0
|
||||
|
||||
In order to open access to perf_event_open() syscall MAC policy settings can
|
||||
require to be extended. On SELinux system this can be done by loading a special
|
||||
policy module extending base policy settings. Perf related policy module can
|
||||
be generated using the system audit records about blocking perf_event access.
|
||||
Run the command below to generate my-perf.te policy extension file with
|
||||
perf_event related rules:
|
||||
|
||||
# ausearch -c 'perf' --raw | audit2allow -M my-perf && cat my-perf.te
|
||||
|
||||
module my-perf 1.0;
|
||||
|
||||
require {
|
||||
type unconfined_t;
|
||||
class perf_event { cpu kernel open read tracepoint write };
|
||||
}
|
||||
|
||||
#============= unconfined_t ==============
|
||||
allow unconfined_t self:perf_event { cpu kernel open read tracepoint write };
|
||||
|
||||
Now compile, pack and load my-perf.pp extension module into the kernel:
|
||||
|
||||
# checkmodule -M -m -o my-perf.mod my-perf.te
|
||||
# semodule_package -o my-perf.pp -m my-perf.mod
|
||||
# semodule -X 300 -i my-perf.pp
|
||||
|
||||
After all those taken steps above access to perf_event_open() syscall should
|
||||
now be allowed by the policy settings. Check access running Perf like this:
|
||||
|
||||
# perf stat
|
||||
^C
|
||||
Performance counter stats for 'system wide':
|
||||
|
||||
36,387.41 msec cpu-clock # 7.999 CPUs utilized
|
||||
2,629 context-switches # 0.072 K/sec
|
||||
57 cpu-migrations # 0.002 K/sec
|
||||
1 page-faults # 0.000 K/sec
|
||||
263,721,559 cycles # 0.007 GHz
|
||||
175,746,713 instructions # 0.67 insn per cycle
|
||||
19,628,798 branches # 0.539 M/sec
|
||||
1,259,201 branch-misses # 6.42% of all branches
|
||||
|
||||
4.549061439 seconds time elapsed
|
||||
|
||||
The generated perf-event.pp related policy extension module can be removed
|
||||
from the kernel using this command:
|
||||
|
||||
# semodule -X 300 -r my-perf
|
||||
|
||||
Alternatively the module can be temporarily disabled and enabled back using
|
||||
these two commands:
|
||||
|
||||
# semodule -d my-perf
|
||||
# semodule -e my-perf
|
||||
|
||||
If something went wrong
|
||||
=======================
|
||||
|
||||
To turn SELinux into Permissive mode:
|
||||
# setenforce 0
|
||||
|
||||
To fully disable SELinux during kernel boot [3] set kernel command line parameter selinux=0
|
||||
|
||||
To remove SELinux labeling from local filesystems:
|
||||
# find / -mount -print0 | xargs -0 setfattr -h -x security.selinux
|
||||
|
||||
To fully turn SELinux off a machine set SELINUX=disabled at /etc/selinux/config file and reboot;
|
||||
|
||||
Links
|
||||
=====
|
||||
|
||||
[1] https://download-ib01.fedoraproject.org/pub/fedora/linux/updates/31/Everything/SRPMS/Packages/s/selinux-policy-3.14.4-49.fc31.src.rpm
|
||||
[2] https://docs.fedoraproject.org/en-US/Fedora/11/html/Security-Enhanced_Linux/sect-Security-Enhanced_Linux-Working_with_SELinux-Enabling_and_Disabling_SELinux.html
|
||||
[3] https://danwalsh.livejournal.com/10972.html
|
|
@ -23,12 +23,28 @@ include $(srctree)/tools/scripts/Makefile.arch
|
|||
$(call detected_var,SRCARCH)
|
||||
|
||||
NO_PERF_REGS := 1
|
||||
NO_SYSCALL_TABLE := 1
|
||||
|
||||
ifneq ($(NO_SYSCALL_TABLE),1)
|
||||
NO_SYSCALL_TABLE := 1
|
||||
|
||||
ifeq ($(SRCARCH),x86)
|
||||
ifeq (${IS_64_BIT}, 1)
|
||||
NO_SYSCALL_TABLE := 0
|
||||
endif
|
||||
else
|
||||
ifeq ($(SRCARCH),$(filter $(SRCARCH),powerpc arm64 s390))
|
||||
NO_SYSCALL_TABLE := 0
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(NO_SYSCALL_TABLE),1)
|
||||
CFLAGS += -DHAVE_SYSCALL_TABLE_SUPPORT
|
||||
endif
|
||||
endif
|
||||
|
||||
# Additional ARCH settings for ppc
|
||||
ifeq ($(SRCARCH),powerpc)
|
||||
NO_PERF_REGS := 0
|
||||
NO_SYSCALL_TABLE := 0
|
||||
CFLAGS += -I$(OUTPUT)arch/powerpc/include/generated
|
||||
LIBUNWIND_LIBS := -lunwind -lunwind-ppc64
|
||||
endif
|
||||
|
@ -37,7 +53,6 @@ endif
|
|||
ifeq ($(SRCARCH),x86)
|
||||
$(call detected,CONFIG_X86)
|
||||
ifeq (${IS_64_BIT}, 1)
|
||||
NO_SYSCALL_TABLE := 0
|
||||
CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT -I$(OUTPUT)arch/x86/include/generated
|
||||
ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S
|
||||
LIBUNWIND_LIBS = -lunwind-x86_64 -lunwind -llzma
|
||||
|
@ -55,7 +70,6 @@ endif
|
|||
|
||||
ifeq ($(SRCARCH),arm64)
|
||||
NO_PERF_REGS := 0
|
||||
NO_SYSCALL_TABLE := 0
|
||||
CFLAGS += -I$(OUTPUT)arch/arm64/include/generated
|
||||
LIBUNWIND_LIBS = -lunwind -lunwind-aarch64
|
||||
endif
|
||||
|
@ -70,7 +84,6 @@ endif
|
|||
|
||||
ifeq ($(ARCH),s390)
|
||||
NO_PERF_REGS := 0
|
||||
NO_SYSCALL_TABLE := 0
|
||||
CFLAGS += -fPIC -I$(OUTPUT)arch/s390/include/generated
|
||||
endif
|
||||
|
||||
|
@ -78,10 +91,6 @@ ifeq ($(NO_PERF_REGS),0)
|
|||
$(call detected,CONFIG_PERF_REGS)
|
||||
endif
|
||||
|
||||
ifneq ($(NO_SYSCALL_TABLE),1)
|
||||
CFLAGS += -DHAVE_SYSCALL_TABLE_SUPPORT
|
||||
endif
|
||||
|
||||
# So far there's only x86 and arm libdw unwind support merged in perf.
|
||||
# Disable it on all other architectures in case libdw unwind
|
||||
# support is detected in system. Add supported architectures
|
||||
|
@ -346,7 +355,7 @@ ifndef NO_BIONIC
|
|||
endif
|
||||
|
||||
ifeq ($(feature-eventfd), 1)
|
||||
CFLAGS += -DHAVE_EVENTFD
|
||||
CFLAGS += -DHAVE_EVENTFD_SUPPORT
|
||||
endif
|
||||
|
||||
ifeq ($(feature-get_current_dir_name), 1)
|
||||
|
@ -651,6 +660,7 @@ ifeq ($(NO_SYSCALL_TABLE),0)
|
|||
$(call detected,CONFIG_TRACE)
|
||||
else
|
||||
ifndef NO_LIBAUDIT
|
||||
$(call feature_check,libaudit)
|
||||
ifneq ($(feature-libaudit), 1)
|
||||
msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev);
|
||||
NO_LIBAUDIT := 1
|
||||
|
@ -1012,6 +1022,19 @@ ifdef LIBCLANGLLVM
|
|||
endif
|
||||
endif
|
||||
|
||||
ifdef LIBPFM4
|
||||
$(call feature_check,libpfm4)
|
||||
ifeq ($(feature-libpfm4), 1)
|
||||
CFLAGS += -DHAVE_LIBPFM
|
||||
EXTLIBS += -lpfm
|
||||
ASCIIDOC_EXTRA = -aHAVE_LIBPFM=1
|
||||
$(call detected,CONFIG_LIBPFM4)
|
||||
else
|
||||
msg := $(warning libpfm4 not found, disables libpfm4 support. Please install libpfm4-dev);
|
||||
NO_LIBPFM4 := 1
|
||||
endif
|
||||
endif
|
||||
|
||||
# Among the variables below, these:
|
||||
# perfexecdir
|
||||
# perf_include_dir
|
||||
|
|
|
@ -118,6 +118,12 @@ include ../scripts/utilities.mak
|
|||
#
|
||||
# Define LIBBPF_DYNAMIC to enable libbpf dynamic linking.
|
||||
#
|
||||
# Define NO_SYSCALL_TABLE=1 to disable the use of syscall id to/from name tables
|
||||
# generated from the kernel .tbl or unistd.h files and use, if available, libaudit
|
||||
# for doing the conversions to/from strings/id.
|
||||
#
|
||||
# Define LIBPFM4 to enable libpfm4 events extension.
|
||||
#
|
||||
|
||||
# As per kernel Makefile, avoid funny character set dependencies
|
||||
unexport LC_ALL
|
||||
|
@ -278,6 +284,7 @@ strip-libs = $(filter-out -l%,$(1))
|
|||
|
||||
ifneq ($(OUTPUT),)
|
||||
TE_PATH=$(OUTPUT)
|
||||
PLUGINS_PATH=$(OUTPUT)
|
||||
BPF_PATH=$(OUTPUT)
|
||||
SUBCMD_PATH=$(OUTPUT)
|
||||
LIBPERF_PATH=$(OUTPUT)
|
||||
|
@ -288,6 +295,7 @@ else
|
|||
endif
|
||||
else
|
||||
TE_PATH=$(TRACE_EVENT_DIR)
|
||||
PLUGINS_PATH=$(TRACE_EVENT_DIR)plugins/
|
||||
API_PATH=$(LIB_DIR)
|
||||
BPF_PATH=$(BPF_DIR)
|
||||
SUBCMD_PATH=$(SUBCMD_DIR)
|
||||
|
@ -297,7 +305,7 @@ endif
|
|||
LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
|
||||
export LIBTRACEEVENT
|
||||
|
||||
LIBTRACEEVENT_DYNAMIC_LIST = $(TE_PATH)plugins/libtraceevent-dynamic-list
|
||||
LIBTRACEEVENT_DYNAMIC_LIST = $(PLUGINS_PATH)libtraceevent-dynamic-list
|
||||
|
||||
#
|
||||
# The static build has no dynsym table, so this does not work for
|
||||
|
@ -756,10 +764,10 @@ $(LIBTRACEEVENT): FORCE
|
|||
$(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) $(OUTPUT)libtraceevent.a
|
||||
|
||||
libtraceevent_plugins: FORCE
|
||||
$(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) plugins
|
||||
$(Q)$(MAKE) -C $(TRACE_EVENT_DIR)plugins $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) plugins
|
||||
|
||||
$(LIBTRACEEVENT_DYNAMIC_LIST): libtraceevent_plugins
|
||||
$(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) $(OUTPUT)plugins/libtraceevent-dynamic-list
|
||||
$(Q)$(MAKE) -C $(TRACE_EVENT_DIR)plugins $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) $(OUTPUT)libtraceevent-dynamic-list
|
||||
|
||||
$(LIBTRACEEVENT)-clean:
|
||||
$(call QUIET_CLEAN, libtraceevent)
|
||||
|
|
|
@ -216,7 +216,7 @@ static int cs_etm_set_sink_attr(struct perf_pmu *pmu,
|
|||
struct evsel *evsel)
|
||||
{
|
||||
char msg[BUFSIZ], path[PATH_MAX], *sink;
|
||||
struct perf_evsel_config_term *term;
|
||||
struct evsel_config_term *term;
|
||||
int ret = -EINVAL;
|
||||
u32 hash;
|
||||
|
||||
|
@ -224,7 +224,7 @@ static int cs_etm_set_sink_attr(struct perf_pmu *pmu,
|
|||
return 0;
|
||||
|
||||
list_for_each_entry(term, &evsel->config_terms, list) {
|
||||
if (term->type != PERF_EVSEL__CONFIG_TERM_DRV_CFG)
|
||||
if (term->type != EVSEL__CONFIG_TERM_DRV_CFG)
|
||||
continue;
|
||||
|
||||
sink = term->val.str;
|
||||
|
@ -265,7 +265,8 @@ static int cs_etm_recording_options(struct auxtrace_record *itr,
|
|||
ptr->evlist = evlist;
|
||||
ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
|
||||
|
||||
if (perf_can_record_switch_events())
|
||||
if (!record_opts__no_switch_events(opts) &&
|
||||
perf_can_record_switch_events())
|
||||
opts->record_switch_events = true;
|
||||
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include "../../util/unwind-libdw.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
#include "../../util/event.h"
|
||||
#include "../../../util/unwind-libdw.h"
|
||||
#include "../../../util/perf_regs.h"
|
||||
#include "../../../util/event.h"
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@ perf-y += header.o
|
|||
perf-y += kvm-stat.o
|
||||
perf-y += perf_regs.o
|
||||
perf-y += mem-events.o
|
||||
perf-y += sym-handling.o
|
||||
|
||||
perf-$(CONFIG_DWARF) += dwarf-regs.o
|
||||
perf-$(CONFIG_DWARF) += skip-callchain-idx.o
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include <linux/kernel.h>
|
||||
#include "../../util/unwind-libdw.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
#include "../../util/event.h"
|
||||
#include "../../../util/unwind-libdw.h"
|
||||
#include "../../../util/perf_regs.h"
|
||||
#include "../../../util/event.h"
|
||||
|
||||
/* See backends/ppc_initreg.c and backends/ppc_regs.c in elfutils. */
|
||||
static const int special_regs[3][2] = {
|
||||
|
|
|
@ -55,6 +55,14 @@ int test__arch_unwind_sample(struct perf_sample *sample,
|
|||
return -1;
|
||||
}
|
||||
|
||||
#ifdef MEMORY_SANITIZER
|
||||
/*
|
||||
* Assignments to buf in the assembly function perf_regs_load aren't
|
||||
* seen by memory sanitizer. Zero the memory to convince memory
|
||||
* sanitizer the memory is initialized.
|
||||
*/
|
||||
memset(buf, 0, sizeof(u64) * PERF_REGS_MAX);
|
||||
#endif
|
||||
perf_regs_load(buf);
|
||||
regs->abi = PERF_SAMPLE_REGS_ABI;
|
||||
regs->regs = buf;
|
||||
|
|
|
@ -59,7 +59,8 @@ struct intel_pt_recording {
|
|||
size_t priv_size;
|
||||
};
|
||||
|
||||
static int intel_pt_parse_terms_with_default(struct list_head *formats,
|
||||
static int intel_pt_parse_terms_with_default(const char *pmu_name,
|
||||
struct list_head *formats,
|
||||
const char *str,
|
||||
u64 *config)
|
||||
{
|
||||
|
@ -78,7 +79,8 @@ static int intel_pt_parse_terms_with_default(struct list_head *formats,
|
|||
goto out_free;
|
||||
|
||||
attr.config = *config;
|
||||
err = perf_pmu__config_terms(formats, &attr, terms, true, NULL);
|
||||
err = perf_pmu__config_terms(pmu_name, formats, &attr, terms, true,
|
||||
NULL);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
|
@ -88,11 +90,12 @@ out_free:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int intel_pt_parse_terms(struct list_head *formats, const char *str,
|
||||
u64 *config)
|
||||
static int intel_pt_parse_terms(const char *pmu_name, struct list_head *formats,
|
||||
const char *str, u64 *config)
|
||||
{
|
||||
*config = 0;
|
||||
return intel_pt_parse_terms_with_default(formats, str, config);
|
||||
return intel_pt_parse_terms_with_default(pmu_name, formats, str,
|
||||
config);
|
||||
}
|
||||
|
||||
static u64 intel_pt_masked_bits(u64 mask, u64 bits)
|
||||
|
@ -229,7 +232,8 @@ static u64 intel_pt_default_config(struct perf_pmu *intel_pt_pmu)
|
|||
|
||||
pr_debug2("%s default config: %s\n", intel_pt_pmu->name, buf);
|
||||
|
||||
intel_pt_parse_terms(&intel_pt_pmu->format, buf, &config);
|
||||
intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, buf,
|
||||
&config);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
@ -337,13 +341,16 @@ static int intel_pt_info_fill(struct auxtrace_record *itr,
|
|||
if (priv_size != ptr->priv_size)
|
||||
return -EINVAL;
|
||||
|
||||
intel_pt_parse_terms(&intel_pt_pmu->format, "tsc", &tsc_bit);
|
||||
intel_pt_parse_terms(&intel_pt_pmu->format, "noretcomp",
|
||||
&noretcomp_bit);
|
||||
intel_pt_parse_terms(&intel_pt_pmu->format, "mtc", &mtc_bit);
|
||||
intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format,
|
||||
"tsc", &tsc_bit);
|
||||
intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format,
|
||||
"noretcomp", &noretcomp_bit);
|
||||
intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format,
|
||||
"mtc", &mtc_bit);
|
||||
mtc_freq_bits = perf_pmu__format_bits(&intel_pt_pmu->format,
|
||||
"mtc_period");
|
||||
intel_pt_parse_terms(&intel_pt_pmu->format, "cyc", &cyc_bit);
|
||||
intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format,
|
||||
"cyc", &cyc_bit);
|
||||
|
||||
intel_pt_tsc_ctc_ratio(&tsc_ctc_ratio_n, &tsc_ctc_ratio_d);
|
||||
|
||||
|
@ -556,10 +563,9 @@ static int intel_pt_validate_config(struct perf_pmu *intel_pt_pmu,
|
|||
static void intel_pt_config_sample_mode(struct perf_pmu *intel_pt_pmu,
|
||||
struct evsel *evsel)
|
||||
{
|
||||
struct perf_evsel_config_term *term;
|
||||
u64 user_bits = 0, bits;
|
||||
struct evsel_config_term *term = evsel__get_config_term(evsel, CFG_CHG);
|
||||
|
||||
term = perf_evsel__get_config_term(evsel, CFG_CHG);
|
||||
if (term)
|
||||
user_bits = term->val.cfg_chg;
|
||||
|
||||
|
@ -769,7 +775,8 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
|
|||
}
|
||||
}
|
||||
|
||||
intel_pt_parse_terms(&intel_pt_pmu->format, "tsc", &tsc_bit);
|
||||
intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format,
|
||||
"tsc", &tsc_bit);
|
||||
|
||||
if (opts->full_auxtrace && (intel_pt_evsel->core.attr.config & tsc_bit))
|
||||
have_timing_info = true;
|
||||
|
@ -780,7 +787,8 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
|
|||
* Per-cpu recording needs sched_switch events to distinguish different
|
||||
* threads.
|
||||
*/
|
||||
if (have_timing_info && !perf_cpu_map__empty(cpus)) {
|
||||
if (have_timing_info && !perf_cpu_map__empty(cpus) &&
|
||||
!record_opts__no_switch_events(opts)) {
|
||||
if (perf_can_record_switch_events()) {
|
||||
bool cpu_wide = !target__none(&opts->target) &&
|
||||
!target__has_task(&opts->target);
|
||||
|
@ -875,7 +883,8 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
|
|||
* per-cpu with no sched_switch (except workload-only).
|
||||
*/
|
||||
if (!ptr->have_sched_switch && !perf_cpu_map__empty(cpus) &&
|
||||
!target__none(&opts->target))
|
||||
!target__none(&opts->target) &&
|
||||
!intel_pt_evsel->core.attr.exclude_user)
|
||||
ui__warning("Intel Processor Trace decoding will not be possible except for kernel tracing!\n");
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include "../../util/unwind-libdw.h"
|
||||
#include "../../util/perf_regs.h"
|
||||
#include "../../util/event.h"
|
||||
#include "../../../util/unwind-libdw.h"
|
||||
#include "../../../util/perf_regs.h"
|
||||
#include "../../../util/event.h"
|
||||
|
||||
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Benchmark the various operations allowed for epoll_ctl(2).
|
||||
* The idea is to concurrently stress a single epoll instance
|
||||
*/
|
||||
#ifdef HAVE_EVENTFD
|
||||
#ifdef HAVE_EVENTFD_SUPPORT
|
||||
/* For the CLR_() macros */
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
@ -412,4 +412,4 @@ int bench_epoll_ctl(int argc, const char **argv)
|
|||
errmem:
|
||||
err(EXIT_FAILURE, "calloc");
|
||||
}
|
||||
#endif // HAVE_EVENTFD
|
||||
#endif // HAVE_EVENTFD_SUPPORT
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#ifdef HAVE_EVENTFD
|
||||
#ifdef HAVE_EVENTFD_SUPPORT
|
||||
/*
|
||||
* Copyright (C) 2018 Davidlohr Bueso.
|
||||
*
|
||||
|
@ -540,4 +540,4 @@ int bench_epoll_wait(int argc, const char **argv)
|
|||
errmem:
|
||||
err(EXIT_FAILURE, "calloc");
|
||||
}
|
||||
#endif // HAVE_EVENTFD
|
||||
#endif // HAVE_EVENTFD_SUPPORT
|
||||
|
|
|
@ -40,7 +40,7 @@ struct sender_context {
|
|||
unsigned int num_fds;
|
||||
int ready_out;
|
||||
int wakefd;
|
||||
int out_fds[0];
|
||||
int out_fds[];
|
||||
};
|
||||
|
||||
struct receiver_context {
|
||||
|
|
|
@ -432,7 +432,7 @@ static int __cmd_annotate(struct perf_annotate *ann)
|
|||
hists__collapse_resort(hists, NULL);
|
||||
/* Don't sort callchain */
|
||||
evsel__reset_sample_bit(pos, CALLCHAIN);
|
||||
perf_evsel__output_resort(pos, NULL);
|
||||
evsel__output_resort(pos, NULL);
|
||||
|
||||
if (symbol_conf.event_group && !evsel__is_group_leader(pos))
|
||||
continue;
|
||||
|
|
|
@ -67,14 +67,14 @@ static struct bench futex_benchmarks[] = {
|
|||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
#ifdef HAVE_EVENTFD
|
||||
#ifdef HAVE_EVENTFD_SUPPORT
|
||||
static struct bench epoll_benchmarks[] = {
|
||||
{ "wait", "Benchmark epoll concurrent epoll_waits", bench_epoll_wait },
|
||||
{ "ctl", "Benchmark epoll concurrent epoll_ctls", bench_epoll_ctl },
|
||||
{ "all", "Run all futex benchmarks", NULL },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
#endif // HAVE_EVENTFD
|
||||
#endif // HAVE_EVENTFD_SUPPORT
|
||||
|
||||
static struct bench internals_benchmarks[] = {
|
||||
{ "synthesize", "Benchmark perf event synthesis", bench_synthesize },
|
||||
|
@ -95,7 +95,7 @@ static struct collection collections[] = {
|
|||
{ "numa", "NUMA scheduling and MM benchmarks", numa_benchmarks },
|
||||
#endif
|
||||
{"futex", "Futex stressing benchmarks", futex_benchmarks },
|
||||
#ifdef HAVE_EVENTFD
|
||||
#ifdef HAVE_EVENTFD_SUPPORT
|
||||
{"epoll", "Epoll stressing benchmarks", epoll_benchmarks },
|
||||
#endif
|
||||
{ "internals", "Perf-internals benchmarks", internals_benchmarks },
|
||||
|
|
|
@ -2887,8 +2887,15 @@ static int parse_record_events(const struct option *opt,
|
|||
{
|
||||
bool *event_set = (bool *) opt->value;
|
||||
|
||||
if (!strcmp(str, "list")) {
|
||||
perf_mem_events__list();
|
||||
exit(0);
|
||||
}
|
||||
if (perf_mem_events__parse(str))
|
||||
exit(-1);
|
||||
|
||||
*event_set = true;
|
||||
return perf_mem_events__parse(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details
|
|||
return PTR_ERR(session);
|
||||
|
||||
evlist__for_each_entry(session->evlist, pos) {
|
||||
perf_evsel__fprintf(pos, details, stdout);
|
||||
evsel__fprintf(pos, details, stdout);
|
||||
|
||||
if (pos->core.attr.type == PERF_TYPE_TRACEPOINT)
|
||||
has_tracepoint = true;
|
||||
|
|
|
@ -45,6 +45,7 @@ struct filter_entry {
|
|||
char name[];
|
||||
};
|
||||
|
||||
static volatile int workload_exec_errno;
|
||||
static bool done;
|
||||
|
||||
static void sig_handler(int sig __maybe_unused)
|
||||
|
@ -63,7 +64,7 @@ static void ftrace__workload_exec_failed_signal(int signo __maybe_unused,
|
|||
siginfo_t *info __maybe_unused,
|
||||
void *ucontext __maybe_unused)
|
||||
{
|
||||
/* workload_exec_errno = info->si_value.sival_int; */
|
||||
workload_exec_errno = info->si_value.sival_int;
|
||||
done = true;
|
||||
}
|
||||
|
||||
|
@ -383,6 +384,14 @@ static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv)
|
|||
|
||||
write_tracing_file("tracing_on", "0");
|
||||
|
||||
if (workload_exec_errno) {
|
||||
const char *emsg = str_error_r(workload_exec_errno, buf, sizeof(buf));
|
||||
/* flush stdout first so below error msg appears at the end. */
|
||||
fflush(stdout);
|
||||
pr_err("workload failed: %s\n", emsg);
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
/* read remaining buffer contents */
|
||||
while (true) {
|
||||
int n = read(trace_fd, buf, sizeof(buf));
|
||||
|
@ -397,7 +406,7 @@ out_close_fd:
|
|||
out_reset:
|
||||
reset_tracing_files(ftrace);
|
||||
out:
|
||||
return done ? 0 : -1;
|
||||
return (done && !workload_exec_errno) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int perf_ftrace_config(const char *var, const char *value, void *cb)
|
||||
|
@ -494,7 +503,7 @@ int cmd_ftrace(int argc, const char **argv)
|
|||
argc = parse_options(argc, argv, ftrace_options, ftrace_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
if (!argc && target__none(&ftrace.target))
|
||||
usage_with_options(ftrace_usage, ftrace_options);
|
||||
ftrace.target.system_wide = true;
|
||||
|
||||
ret = target__validate(&ftrace.target);
|
||||
if (ret) {
|
||||
|
|
|
@ -51,7 +51,7 @@ struct perf_inject {
|
|||
struct event_entry {
|
||||
struct list_head node;
|
||||
u32 tid;
|
||||
union perf_event event[0];
|
||||
union perf_event event[];
|
||||
};
|
||||
|
||||
static int output_bytes(struct perf_inject *inject, void *buf, size_t sz)
|
||||
|
|
|
@ -42,7 +42,7 @@ int cmd_list(int argc, const char **argv)
|
|||
OPT_END()
|
||||
};
|
||||
const char * const list_usage[] = {
|
||||
"perf list [<options>] [hw|sw|cache|tracepoint|pmu|sdt|event_glob]",
|
||||
"perf list [<options>] [hw|sw|cache|tracepoint|pmu|sdt|metric|metricgroup|event_glob]",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
@ -38,26 +38,16 @@ static int parse_record_events(const struct option *opt,
|
|||
const char *str, int unset __maybe_unused)
|
||||
{
|
||||
struct perf_mem *mem = *(struct perf_mem **)opt->value;
|
||||
int j;
|
||||
|
||||
if (strcmp(str, "list")) {
|
||||
if (!perf_mem_events__parse(str)) {
|
||||
mem->operation = 0;
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(str, "list")) {
|
||||
perf_mem_events__list();
|
||||
exit(0);
|
||||
}
|
||||
if (perf_mem_events__parse(str))
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
|
||||
struct perf_mem_event *e = &perf_mem_events[j];
|
||||
|
||||
fprintf(stderr, "%-13s%-*s%s\n",
|
||||
e->tag,
|
||||
verbose > 0 ? 25 : 0,
|
||||
verbose > 0 ? perf_mem_events__name(j) : "",
|
||||
e->supported ? ": available" : "");
|
||||
}
|
||||
exit(0);
|
||||
mem->operation = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const __usage[] = {
|
||||
|
|
|
@ -364,6 +364,9 @@ static int perf_add_probe_events(struct perf_probe_event *pevs, int npevs)
|
|||
|
||||
for (k = 0; k < pev->ntevs; k++) {
|
||||
struct probe_trace_event *tev = &pev->tevs[k];
|
||||
/* Skipped events have no event name */
|
||||
if (!tev->event)
|
||||
continue;
|
||||
|
||||
/* We use tev's name for showing new events */
|
||||
show_perf_probe_event(tev->group, tev->event, pev,
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "util/units.h"
|
||||
#include "util/bpf-event.h"
|
||||
#include "util/util.h"
|
||||
#include "util/pfm.h"
|
||||
#include "asm/bug.h"
|
||||
#include "perf.h"
|
||||
|
||||
|
@ -56,6 +57,9 @@
|
|||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_EVENTFD_SUPPORT
|
||||
#include <sys/eventfd.h>
|
||||
#endif
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -538,6 +542,9 @@ static int record__pushfn(struct mmap *map, void *to, void *bf, size_t size)
|
|||
|
||||
static volatile int signr = -1;
|
||||
static volatile int child_finished;
|
||||
#ifdef HAVE_EVENTFD_SUPPORT
|
||||
static int done_fd = -1;
|
||||
#endif
|
||||
|
||||
static void sig_handler(int sig)
|
||||
{
|
||||
|
@ -547,6 +554,21 @@ static void sig_handler(int sig)
|
|||
signr = sig;
|
||||
|
||||
done = 1;
|
||||
#ifdef HAVE_EVENTFD_SUPPORT
|
||||
{
|
||||
u64 tmp = 1;
|
||||
/*
|
||||
* It is possible for this signal handler to run after done is checked
|
||||
* in the main loop, but before the perf counter fds are polled. If this
|
||||
* happens, the poll() will continue to wait even though done is set,
|
||||
* and will only break out if either another signal is received, or the
|
||||
* counters are ready for read. To ensure the poll() doesn't sleep when
|
||||
* done is set, use an eventfd (done_fd) to wake up the poll().
|
||||
*/
|
||||
if (write(done_fd, &tmp, sizeof(tmp)) < 0)
|
||||
pr_err("failed to signal wakeup fd, error: %m\n");
|
||||
}
|
||||
#endif // HAVE_EVENTFD_SUPPORT
|
||||
}
|
||||
|
||||
static void sigsegv_handler(int sig)
|
||||
|
@ -825,19 +847,28 @@ static int record__open(struct record *rec)
|
|||
int rc = 0;
|
||||
|
||||
/*
|
||||
* For initial_delay we need to add a dummy event so that we can track
|
||||
* PERF_RECORD_MMAP while we wait for the initial delay to enable the
|
||||
* real events, the ones asked by the user.
|
||||
* For initial_delay or system wide, we need to add a dummy event so
|
||||
* that we can track PERF_RECORD_MMAP to cover the delay of waiting or
|
||||
* event synthesis.
|
||||
*/
|
||||
if (opts->initial_delay) {
|
||||
if (opts->initial_delay || target__has_cpu(&opts->target)) {
|
||||
if (perf_evlist__add_dummy(evlist))
|
||||
return -ENOMEM;
|
||||
|
||||
/* Disable tracking of mmaps on lead event. */
|
||||
pos = evlist__first(evlist);
|
||||
pos->tracking = 0;
|
||||
/* Set up dummy event. */
|
||||
pos = evlist__last(evlist);
|
||||
pos->tracking = 1;
|
||||
pos->core.attr.enable_on_exec = 1;
|
||||
/*
|
||||
* Enable the dummy event when the process is forked for
|
||||
* initial_delay, immediately for system wide.
|
||||
*/
|
||||
if (opts->initial_delay)
|
||||
pos->core.attr.enable_on_exec = 1;
|
||||
else
|
||||
pos->immediate = 1;
|
||||
}
|
||||
|
||||
perf_evlist__config(evlist, opts, &callchain_param);
|
||||
|
@ -1538,6 +1569,20 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
|||
pr_err("Compression initialization failed.\n");
|
||||
return -1;
|
||||
}
|
||||
#ifdef HAVE_EVENTFD_SUPPORT
|
||||
done_fd = eventfd(0, EFD_NONBLOCK);
|
||||
if (done_fd < 0) {
|
||||
pr_err("Failed to create wakeup eventfd, error: %m\n");
|
||||
status = -1;
|
||||
goto out_delete_session;
|
||||
}
|
||||
err = evlist__add_pollfd(rec->evlist, done_fd);
|
||||
if (err < 0) {
|
||||
pr_err("Failed to add wakeup eventfd to poll list\n");
|
||||
status = err;
|
||||
goto out_delete_session;
|
||||
}
|
||||
#endif // HAVE_EVENTFD_SUPPORT
|
||||
|
||||
session->header.env.comp_type = PERF_COMP_ZSTD;
|
||||
session->header.env.comp_level = rec->opts.comp_level;
|
||||
|
@ -1896,6 +1941,10 @@ out_child:
|
|||
}
|
||||
|
||||
out_delete_session:
|
||||
#ifdef HAVE_EVENTFD_SUPPORT
|
||||
if (done_fd >= 0)
|
||||
close(done_fd);
|
||||
#endif
|
||||
zstd_fini(&session->zstd_data);
|
||||
perf_session__delete(session);
|
||||
|
||||
|
@ -2453,8 +2502,9 @@ static struct option __record_options[] = {
|
|||
"Record namespaces events"),
|
||||
OPT_BOOLEAN(0, "all-cgroups", &record.opts.record_cgroup,
|
||||
"Record cgroup events"),
|
||||
OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events,
|
||||
"Record context switch events"),
|
||||
OPT_BOOLEAN_SET(0, "switch-events", &record.opts.record_switch_events,
|
||||
&record.opts.record_switch_events_set,
|
||||
"Record context switch events"),
|
||||
OPT_BOOLEAN_FLAG(0, "all-kernel", &record.opts.all_kernel,
|
||||
"Configure all used events to run in kernel space.",
|
||||
PARSE_OPT_EXCLUSIVE),
|
||||
|
@ -2506,6 +2556,11 @@ static struct option __record_options[] = {
|
|||
OPT_UINTEGER(0, "num-thread-synthesize",
|
||||
&record.opts.nr_threads_synthesize,
|
||||
"number of threads to run for event synthesis"),
|
||||
#ifdef HAVE_LIBPFM
|
||||
OPT_CALLBACK(0, "pfm-events", &record.evlist, "event",
|
||||
"libpfm4 event selector. use 'perf list' to list available events",
|
||||
parse_libpfm_events_option),
|
||||
#endif
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
#include "util/time-utils.h"
|
||||
#include "util/auxtrace.h"
|
||||
#include "util/units.h"
|
||||
#include "util/branch.h"
|
||||
#include "util/util.h" // perf_tip()
|
||||
#include "ui/ui.h"
|
||||
#include "ui/progress.h"
|
||||
|
@ -402,16 +401,7 @@ static int report__setup_sample_type(struct report *rep)
|
|||
}
|
||||
}
|
||||
|
||||
if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) {
|
||||
if ((sample_type & PERF_SAMPLE_REGS_USER) &&
|
||||
(sample_type & PERF_SAMPLE_STACK_USER)) {
|
||||
callchain_param.record_mode = CALLCHAIN_DWARF;
|
||||
dwarf_callchain_users = true;
|
||||
} else if (sample_type & PERF_SAMPLE_BRANCH_STACK)
|
||||
callchain_param.record_mode = CALLCHAIN_LBR;
|
||||
else
|
||||
callchain_param.record_mode = CALLCHAIN_FP;
|
||||
}
|
||||
callchain_param_setup(sample_type);
|
||||
|
||||
if (rep->stitch_lbr && (callchain_param.record_mode != CALLCHAIN_LBR)) {
|
||||
ui__warning("Can't find LBR callchain. Switch off --stitch-lbr.\n"
|
||||
|
@ -716,8 +706,7 @@ static void report__output_resort(struct report *rep)
|
|||
ui_progress__init(&prog, rep->nr_entries, "Sorting events for output...");
|
||||
|
||||
evlist__for_each_entry(rep->session->evlist, pos) {
|
||||
perf_evsel__output_resort_cb(pos, &prog,
|
||||
hists__resort_cb, rep);
|
||||
evsel__output_resort_cb(pos, &prog, hists__resort_cb, rep);
|
||||
}
|
||||
|
||||
ui_progress__finish();
|
||||
|
@ -1090,6 +1079,26 @@ parse_percent_limit(const struct option *opt, const char *str,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int process_attr(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct evlist **pevlist)
|
||||
{
|
||||
u64 sample_type;
|
||||
int err;
|
||||
|
||||
err = perf_event__process_attr(tool, event, pevlist);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Check if we need to enable callchains based
|
||||
* on events sample_type.
|
||||
*/
|
||||
sample_type = perf_evlist__combined_sample_type(*pevlist);
|
||||
callchain_param_setup(sample_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_report(int argc, const char **argv)
|
||||
{
|
||||
struct perf_session *session;
|
||||
|
@ -1120,7 +1129,7 @@ int cmd_report(int argc, const char **argv)
|
|||
.fork = perf_event__process_fork,
|
||||
.lost = perf_event__process_lost,
|
||||
.read = process_read_event,
|
||||
.attr = perf_event__process_attr,
|
||||
.attr = process_attr,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.id_index = perf_event__process_id_index,
|
||||
|
|
|
@ -167,6 +167,7 @@ static struct {
|
|||
u64 fields;
|
||||
u64 invalid_fields;
|
||||
u64 user_set_fields;
|
||||
u64 user_unset_fields;
|
||||
} output[OUTPUT_TYPE_MAX] = {
|
||||
|
||||
[PERF_TYPE_HARDWARE] = {
|
||||
|
@ -2085,6 +2086,7 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
|
|||
struct perf_script *scr = container_of(tool, struct perf_script, tool);
|
||||
struct evlist *evlist;
|
||||
struct evsel *evsel, *pos;
|
||||
u64 sample_type;
|
||||
int err;
|
||||
static struct evsel_script *es;
|
||||
|
||||
|
@ -2117,12 +2119,34 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
|
|||
return 0;
|
||||
}
|
||||
|
||||
set_print_ip_opts(&evsel->core.attr);
|
||||
|
||||
if (evsel->core.attr.sample_type)
|
||||
if (evsel->core.attr.sample_type) {
|
||||
err = perf_evsel__check_attr(evsel, scr->session);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
/*
|
||||
* Check if we need to enable callchains based
|
||||
* on events sample_type.
|
||||
*/
|
||||
sample_type = perf_evlist__combined_sample_type(evlist);
|
||||
callchain_param_setup(sample_type);
|
||||
|
||||
/* Enable fields for callchain entries */
|
||||
if (symbol_conf.use_callchain &&
|
||||
(sample_type & PERF_SAMPLE_CALLCHAIN ||
|
||||
sample_type & PERF_SAMPLE_BRANCH_STACK ||
|
||||
(sample_type & PERF_SAMPLE_REGS_USER &&
|
||||
sample_type & PERF_SAMPLE_STACK_USER))) {
|
||||
int type = output_type(evsel->core.attr.type);
|
||||
|
||||
if (!(output[type].user_unset_fields & PERF_OUTPUT_IP))
|
||||
output[type].fields |= PERF_OUTPUT_IP;
|
||||
if (!(output[type].user_unset_fields & PERF_OUTPUT_SYM))
|
||||
output[type].fields |= PERF_OUTPUT_SYM;
|
||||
}
|
||||
set_print_ip_opts(&evsel->core.attr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_event_with_time(struct perf_tool *tool,
|
||||
|
@ -2434,7 +2458,7 @@ static int __cmd_script(struct perf_script *script)
|
|||
struct script_spec {
|
||||
struct list_head node;
|
||||
struct scripting_ops *ops;
|
||||
char spec[0];
|
||||
char spec[];
|
||||
};
|
||||
|
||||
static LIST_HEAD(script_specs);
|
||||
|
@ -2672,9 +2696,11 @@ parse:
|
|||
if (change == REMOVE) {
|
||||
output[j].fields &= ~all_output_options[i].field;
|
||||
output[j].user_set_fields &= ~all_output_options[i].field;
|
||||
output[j].user_unset_fields |= all_output_options[i].field;
|
||||
} else {
|
||||
output[j].fields |= all_output_options[i].field;
|
||||
output[j].user_set_fields |= all_output_options[i].field;
|
||||
output[j].user_unset_fields &= ~all_output_options[i].field;
|
||||
}
|
||||
output[j].user_set = true;
|
||||
output[j].wildcard_set = true;
|
||||
|
@ -3286,7 +3312,10 @@ static int parse_xed(const struct option *opt __maybe_unused,
|
|||
const char *str __maybe_unused,
|
||||
int unset __maybe_unused)
|
||||
{
|
||||
force_pager("xed -F insn: -A -64 | less");
|
||||
if (isatty(1))
|
||||
force_pager("xed -F insn: -A -64 | less");
|
||||
else
|
||||
force_pager("xed -F insn: -A -64");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
#include "util/time-utils.h"
|
||||
#include "util/top.h"
|
||||
#include "util/affinity.h"
|
||||
#include "util/pfm.h"
|
||||
#include "asm/bug.h"
|
||||
|
||||
#include <linux/time64.h>
|
||||
|
@ -189,6 +190,59 @@ static struct perf_stat_config stat_config = {
|
|||
.big_num = true,
|
||||
};
|
||||
|
||||
static bool cpus_map_matched(struct evsel *a, struct evsel *b)
|
||||
{
|
||||
if (!a->core.cpus && !b->core.cpus)
|
||||
return true;
|
||||
|
||||
if (!a->core.cpus || !b->core.cpus)
|
||||
return false;
|
||||
|
||||
if (a->core.cpus->nr != b->core.cpus->nr)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < a->core.cpus->nr; i++) {
|
||||
if (a->core.cpus->map[i] != b->core.cpus->map[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void evlist__check_cpu_maps(struct evlist *evlist)
|
||||
{
|
||||
struct evsel *evsel, *pos, *leader;
|
||||
char buf[1024];
|
||||
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
leader = evsel->leader;
|
||||
|
||||
/* Check that leader matches cpus with each member. */
|
||||
if (leader == evsel)
|
||||
continue;
|
||||
if (cpus_map_matched(leader, evsel))
|
||||
continue;
|
||||
|
||||
/* If there's mismatch disable the group and warn user. */
|
||||
WARN_ONCE(1, "WARNING: grouped events cpus do not match, disabling group:\n");
|
||||
evsel__group_desc(leader, buf, sizeof(buf));
|
||||
pr_warning(" %s\n", buf);
|
||||
|
||||
if (verbose) {
|
||||
cpu_map__snprint(leader->core.cpus, buf, sizeof(buf));
|
||||
pr_warning(" %s: %s\n", leader->name, buf);
|
||||
cpu_map__snprint(evsel->core.cpus, buf, sizeof(buf));
|
||||
pr_warning(" %s: %s\n", evsel->name, buf);
|
||||
}
|
||||
|
||||
for_each_group_evsel(pos, leader) {
|
||||
pos->leader = pos;
|
||||
pos->core.nr_members = 0;
|
||||
}
|
||||
evsel->leader->core.nr_members = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void diff_timespec(struct timespec *r, struct timespec *a,
|
||||
struct timespec *b)
|
||||
{
|
||||
|
@ -314,14 +368,14 @@ static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void read_counters(struct timespec *rs)
|
||||
static int read_affinity_counters(struct timespec *rs)
|
||||
{
|
||||
struct evsel *counter;
|
||||
struct affinity affinity;
|
||||
int i, ncpus, cpu;
|
||||
|
||||
if (affinity__setup(&affinity) < 0)
|
||||
return;
|
||||
return -1;
|
||||
|
||||
ncpus = perf_cpu_map__nr(evsel_list->core.all_cpus);
|
||||
if (!target__has_cpu(&target) || target__has_per_thread(&target))
|
||||
|
@ -341,6 +395,15 @@ static void read_counters(struct timespec *rs)
|
|||
}
|
||||
}
|
||||
affinity__cleanup(&affinity);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_counters(struct timespec *rs)
|
||||
{
|
||||
struct evsel *counter;
|
||||
|
||||
if (!stat_config.summary && (read_affinity_counters(rs) < 0))
|
||||
return;
|
||||
|
||||
evlist__for_each_entry(evsel_list, counter) {
|
||||
if (counter->err)
|
||||
|
@ -351,6 +414,46 @@ static void read_counters(struct timespec *rs)
|
|||
}
|
||||
}
|
||||
|
||||
static int runtime_stat_new(struct perf_stat_config *config, int nthreads)
|
||||
{
|
||||
int i;
|
||||
|
||||
config->stats = calloc(nthreads, sizeof(struct runtime_stat));
|
||||
if (!config->stats)
|
||||
return -1;
|
||||
|
||||
config->stats_num = nthreads;
|
||||
|
||||
for (i = 0; i < nthreads; i++)
|
||||
runtime_stat__init(&config->stats[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void runtime_stat_delete(struct perf_stat_config *config)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!config->stats)
|
||||
return;
|
||||
|
||||
for (i = 0; i < config->stats_num; i++)
|
||||
runtime_stat__exit(&config->stats[i]);
|
||||
|
||||
zfree(&config->stats);
|
||||
}
|
||||
|
||||
static void runtime_stat_reset(struct perf_stat_config *config)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!config->stats)
|
||||
return;
|
||||
|
||||
for (i = 0; i < config->stats_num; i++)
|
||||
perf_stat__reset_shadow_per_stat(&config->stats[i]);
|
||||
}
|
||||
|
||||
static void process_interval(void)
|
||||
{
|
||||
struct timespec ts, rs;
|
||||
|
@ -359,6 +462,7 @@ static void process_interval(void)
|
|||
diff_timespec(&rs, &ts, &ref_time);
|
||||
|
||||
perf_stat__reset_shadow_per_stat(&rt_stat);
|
||||
runtime_stat_reset(&stat_config);
|
||||
read_counters(&rs);
|
||||
|
||||
if (STAT_RECORD) {
|
||||
|
@ -367,7 +471,7 @@ static void process_interval(void)
|
|||
}
|
||||
|
||||
init_stats(&walltime_nsecs_stats);
|
||||
update_stats(&walltime_nsecs_stats, stat_config.interval * 1000000);
|
||||
update_stats(&walltime_nsecs_stats, stat_config.interval * 1000000ULL);
|
||||
print_counters(&rs, 0, NULL);
|
||||
}
|
||||
|
||||
|
@ -722,7 +826,21 @@ try_again_reset:
|
|||
if (stat_config.walltime_run_table)
|
||||
stat_config.walltime_run[run_idx] = t1 - t0;
|
||||
|
||||
update_stats(&walltime_nsecs_stats, t1 - t0);
|
||||
if (interval) {
|
||||
stat_config.interval = 0;
|
||||
stat_config.summary = true;
|
||||
init_stats(&walltime_nsecs_stats);
|
||||
update_stats(&walltime_nsecs_stats, t1 - t0);
|
||||
|
||||
if (stat_config.aggr_mode == AGGR_GLOBAL)
|
||||
perf_evlist__save_aggr_prev_raw_counts(evsel_list);
|
||||
|
||||
perf_evlist__copy_prev_raw_counts(evsel_list);
|
||||
perf_evlist__reset_prev_raw_counts(evsel_list);
|
||||
runtime_stat_reset(&stat_config);
|
||||
perf_stat__reset_shadow_per_stat(&rt_stat);
|
||||
} else
|
||||
update_stats(&walltime_nsecs_stats, t1 - t0);
|
||||
|
||||
/*
|
||||
* Closing a group leader splits the group, and as we only disable
|
||||
|
@ -821,10 +939,16 @@ static void sig_atexit(void)
|
|||
kill(getpid(), signr);
|
||||
}
|
||||
|
||||
void perf_stat__set_big_num(int set)
|
||||
{
|
||||
stat_config.big_num = (set != 0);
|
||||
}
|
||||
|
||||
static int stat__set_big_num(const struct option *opt __maybe_unused,
|
||||
const char *s __maybe_unused, int unset)
|
||||
{
|
||||
big_num_opt = unset ? 0 : 1;
|
||||
perf_stat__set_big_num(!unset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -840,7 +964,10 @@ static int parse_metric_groups(const struct option *opt,
|
|||
const char *str,
|
||||
int unset __maybe_unused)
|
||||
{
|
||||
return metricgroup__parse_groups(opt, str, &stat_config.metric_events);
|
||||
return metricgroup__parse_groups(opt, str,
|
||||
stat_config.metric_no_group,
|
||||
stat_config.metric_no_merge,
|
||||
&stat_config.metric_events);
|
||||
}
|
||||
|
||||
static struct option stat_options[] = {
|
||||
|
@ -918,6 +1045,10 @@ static struct option stat_options[] = {
|
|||
"ms to wait before starting measurement after program start"),
|
||||
OPT_CALLBACK_NOOPT(0, "metric-only", &stat_config.metric_only, NULL,
|
||||
"Only print computed metrics. No raw values", enable_metric_only),
|
||||
OPT_BOOLEAN(0, "metric-no-group", &stat_config.metric_no_group,
|
||||
"don't group metric events, impacts multiplexing"),
|
||||
OPT_BOOLEAN(0, "metric-no-merge", &stat_config.metric_no_merge,
|
||||
"don't try to share events between metrics in a group"),
|
||||
OPT_BOOLEAN(0, "topdown", &topdown_run,
|
||||
"measure topdown level 1 statistics"),
|
||||
OPT_BOOLEAN(0, "smi-cost", &smi_cost,
|
||||
|
@ -935,6 +1066,11 @@ static struct option stat_options[] = {
|
|||
"Use with 'percore' event qualifier to show the event "
|
||||
"counts of one hardware thread by sum up total hardware "
|
||||
"threads of same physical core"),
|
||||
#ifdef HAVE_LIBPFM
|
||||
OPT_CALLBACK(0, "pfm-events", &evsel_list, "event",
|
||||
"libpfm4 event selector. use 'perf list' to list available events",
|
||||
parse_libpfm_events_option),
|
||||
#endif
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
@ -1442,6 +1578,8 @@ static int add_default_attributes(void)
|
|||
struct option opt = { .value = &evsel_list };
|
||||
|
||||
return metricgroup__parse_groups(&opt, "transaction",
|
||||
stat_config.metric_no_group,
|
||||
stat_config.metric_no_merge,
|
||||
&stat_config.metric_events);
|
||||
}
|
||||
|
||||
|
@ -1737,35 +1875,6 @@ int process_cpu_map_event(struct perf_session *session,
|
|||
return set_maps(st);
|
||||
}
|
||||
|
||||
static int runtime_stat_new(struct perf_stat_config *config, int nthreads)
|
||||
{
|
||||
int i;
|
||||
|
||||
config->stats = calloc(nthreads, sizeof(struct runtime_stat));
|
||||
if (!config->stats)
|
||||
return -1;
|
||||
|
||||
config->stats_num = nthreads;
|
||||
|
||||
for (i = 0; i < nthreads; i++)
|
||||
runtime_stat__init(&config->stats[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void runtime_stat_delete(struct perf_stat_config *config)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!config->stats)
|
||||
return;
|
||||
|
||||
for (i = 0; i < config->stats_num; i++)
|
||||
runtime_stat__exit(&config->stats[i]);
|
||||
|
||||
zfree(&config->stats);
|
||||
}
|
||||
|
||||
static const char * const stat_report_usage[] = {
|
||||
"perf stat report [<options>]",
|
||||
NULL,
|
||||
|
@ -2057,6 +2166,8 @@ int cmd_stat(int argc, const char **argv)
|
|||
goto out;
|
||||
}
|
||||
|
||||
evlist__check_cpu_maps(evsel_list);
|
||||
|
||||
/*
|
||||
* Initialize thread_map with comm names,
|
||||
* so we could print it out on output.
|
||||
|
@ -2147,7 +2258,7 @@ int cmd_stat(int argc, const char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
if (!forever && status != -1 && !interval)
|
||||
if (!forever && status != -1 && (!interval || stat_config.summary))
|
||||
print_counters(NULL, argc, argv);
|
||||
|
||||
if (STAT_RECORD) {
|
||||
|
|
|
@ -128,7 +128,7 @@ struct sample_wrapper {
|
|||
struct sample_wrapper *next;
|
||||
|
||||
u64 timestamp;
|
||||
unsigned char data[0];
|
||||
unsigned char data[];
|
||||
};
|
||||
|
||||
#define TYPE_NONE 0
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
|
||||
#include "util/debug.h"
|
||||
#include "util/ordered-events.h"
|
||||
#include "util/pfm.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
|
@ -307,7 +308,7 @@ static void perf_top__resort_hists(struct perf_top *t)
|
|||
}
|
||||
|
||||
evlist__for_each_entry(evlist, pos) {
|
||||
perf_evsel__output_resort(pos, NULL);
|
||||
evsel__output_resort(pos, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -949,7 +950,7 @@ static int perf_top__overwrite_check(struct perf_top *top)
|
|||
{
|
||||
struct record_opts *opts = &top->record_opts;
|
||||
struct evlist *evlist = top->evlist;
|
||||
struct perf_evsel_config_term *term;
|
||||
struct evsel_config_term *term;
|
||||
struct list_head *config_terms;
|
||||
struct evsel *evsel;
|
||||
int set, overwrite = -1;
|
||||
|
@ -958,7 +959,7 @@ static int perf_top__overwrite_check(struct perf_top *top)
|
|||
set = -1;
|
||||
config_terms = &evsel->config_terms;
|
||||
list_for_each_entry(term, config_terms, list) {
|
||||
if (term->type == PERF_EVSEL__CONFIG_TERM_OVERWRITE)
|
||||
if (term->type == EVSEL__CONFIG_TERM_OVERWRITE)
|
||||
set = term->val.overwrite ? 1 : 0;
|
||||
}
|
||||
|
||||
|
@ -1575,6 +1576,11 @@ int cmd_top(int argc, const char **argv)
|
|||
"WARNING: should be used on grouped events."),
|
||||
OPT_BOOLEAN(0, "stitch-lbr", &top.stitch_lbr,
|
||||
"Enable LBR callgraph stitching approach"),
|
||||
#ifdef HAVE_LIBPFM
|
||||
OPT_CALLBACK(0, "pfm-events", &top.evlist, "event",
|
||||
"libpfm4 event selector. use 'perf list' to list available events",
|
||||
parse_libpfm_events_option),
|
||||
#endif
|
||||
OPTS_EVSWITCH(&top.evswitch),
|
||||
OPT_END()
|
||||
};
|
||||
|
|
|
@ -461,11 +461,11 @@ static int evsel__init_raw_syscall_tp(struct evsel *evsel, void *handler)
|
|||
|
||||
static struct evsel *perf_evsel__raw_syscall_newtp(const char *direction, void *handler)
|
||||
{
|
||||
struct evsel *evsel = perf_evsel__newtp("raw_syscalls", direction);
|
||||
struct evsel *evsel = evsel__newtp("raw_syscalls", direction);
|
||||
|
||||
/* older kernel (e.g., RHEL6) use syscalls:{enter,exit} */
|
||||
if (IS_ERR(evsel))
|
||||
evsel = perf_evsel__newtp("syscalls", direction);
|
||||
evsel = evsel__newtp("syscalls", direction);
|
||||
|
||||
if (IS_ERR(evsel))
|
||||
return NULL;
|
||||
|
@ -1748,12 +1748,26 @@ static int trace__read_syscall_info(struct trace *trace, int id)
|
|||
struct syscall *sc;
|
||||
const char *name = syscalltbl__name(trace->sctbl, id);
|
||||
|
||||
#ifdef HAVE_SYSCALL_TABLE_SUPPORT
|
||||
if (trace->syscalls.table == NULL) {
|
||||
trace->syscalls.table = calloc(trace->sctbl->syscalls.max_id + 1, sizeof(*sc));
|
||||
if (trace->syscalls.table == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
#else
|
||||
if (id > trace->sctbl->syscalls.max_id || (id == 0 && trace->syscalls.table == NULL)) {
|
||||
// When using libaudit we don't know beforehand what is the max syscall id
|
||||
struct syscall *table = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc));
|
||||
|
||||
if (table == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(table + trace->sctbl->syscalls.max_id, 0, (id - trace->sctbl->syscalls.max_id) * sizeof(*sc));
|
||||
|
||||
trace->syscalls.table = table;
|
||||
trace->sctbl->syscalls.max_id = id;
|
||||
}
|
||||
#endif
|
||||
sc = trace->syscalls.table + id;
|
||||
if (sc->nonexistent)
|
||||
return 0;
|
||||
|
@ -2077,8 +2091,20 @@ static struct syscall *trace__syscall_info(struct trace *trace,
|
|||
|
||||
err = -EINVAL;
|
||||
|
||||
if (id > trace->sctbl->syscalls.max_id)
|
||||
#ifdef HAVE_SYSCALL_TABLE_SUPPORT
|
||||
if (id > trace->sctbl->syscalls.max_id) {
|
||||
#else
|
||||
if (id >= trace->sctbl->syscalls.max_id) {
|
||||
/*
|
||||
* With libaudit we don't know beforehand what is the max_id,
|
||||
* so we let trace__read_syscall_info() figure that out as we
|
||||
* go on reading syscalls.
|
||||
*/
|
||||
err = trace__read_syscall_info(trace, id);
|
||||
if (err)
|
||||
#endif
|
||||
goto out_cant_read;
|
||||
}
|
||||
|
||||
if ((trace->syscalls.table == NULL || trace->syscalls.table[id].name == NULL) &&
|
||||
(err = trace__read_syscall_info(trace, id)) != 0)
|
||||
|
@ -3045,7 +3071,7 @@ static bool evlist__add_vfs_getname(struct evlist *evlist)
|
|||
return found;
|
||||
}
|
||||
|
||||
static struct evsel *perf_evsel__new_pgfault(u64 config)
|
||||
static struct evsel *evsel__new_pgfault(u64 config)
|
||||
{
|
||||
struct evsel *evsel;
|
||||
struct perf_event_attr attr = {
|
||||
|
@ -3174,6 +3200,26 @@ out_enomem:
|
|||
}
|
||||
|
||||
#ifdef HAVE_LIBBPF_SUPPORT
|
||||
static struct bpf_map *trace__find_bpf_map_by_name(struct trace *trace, const char *name)
|
||||
{
|
||||
if (trace->bpf_obj == NULL)
|
||||
return NULL;
|
||||
|
||||
return bpf_object__find_map_by_name(trace->bpf_obj, name);
|
||||
}
|
||||
|
||||
static void trace__set_bpf_map_filtered_pids(struct trace *trace)
|
||||
{
|
||||
trace->filter_pids.map = trace__find_bpf_map_by_name(trace, "pids_filtered");
|
||||
}
|
||||
|
||||
static void trace__set_bpf_map_syscalls(struct trace *trace)
|
||||
{
|
||||
trace->syscalls.map = trace__find_bpf_map_by_name(trace, "syscalls");
|
||||
trace->syscalls.prog_array.sys_enter = trace__find_bpf_map_by_name(trace, "syscalls_sys_enter");
|
||||
trace->syscalls.prog_array.sys_exit = trace__find_bpf_map_by_name(trace, "syscalls_sys_exit");
|
||||
}
|
||||
|
||||
static struct bpf_program *trace__find_bpf_program_by_title(struct trace *trace, const char *name)
|
||||
{
|
||||
if (trace->bpf_obj == NULL)
|
||||
|
@ -3512,6 +3558,20 @@ static void trace__delete_augmented_syscalls(struct trace *trace)
|
|||
trace->bpf_obj = NULL;
|
||||
}
|
||||
#else // HAVE_LIBBPF_SUPPORT
|
||||
static struct bpf_map *trace__find_bpf_map_by_name(struct trace *trace __maybe_unused,
|
||||
const char *name __maybe_unused)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void trace__set_bpf_map_filtered_pids(struct trace *trace __maybe_unused)
|
||||
{
|
||||
}
|
||||
|
||||
static void trace__set_bpf_map_syscalls(struct trace *trace __maybe_unused)
|
||||
{
|
||||
}
|
||||
|
||||
static int trace__set_ev_qualifier_bpf_filter(struct trace *trace __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
|
@ -3841,7 +3901,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
|||
}
|
||||
|
||||
if ((trace->trace_pgfaults & TRACE_PFMAJ)) {
|
||||
pgfault_maj = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MAJ);
|
||||
pgfault_maj = evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MAJ);
|
||||
if (pgfault_maj == NULL)
|
||||
goto out_error_mem;
|
||||
evsel__config_callchain(pgfault_maj, &trace->opts, &callchain_param);
|
||||
|
@ -3849,7 +3909,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
|||
}
|
||||
|
||||
if ((trace->trace_pgfaults & TRACE_PFMIN)) {
|
||||
pgfault_min = perf_evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MIN);
|
||||
pgfault_min = evsel__new_pgfault(PERF_COUNT_SW_PAGE_FAULTS_MIN);
|
||||
if (pgfault_min == NULL)
|
||||
goto out_error_mem;
|
||||
evsel__config_callchain(pgfault_min, &trace->opts, &callchain_param);
|
||||
|
@ -4600,26 +4660,6 @@ static int trace__parse_cgroups(const struct option *opt, const char *str, int u
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct bpf_map *trace__find_bpf_map_by_name(struct trace *trace, const char *name)
|
||||
{
|
||||
if (trace->bpf_obj == NULL)
|
||||
return NULL;
|
||||
|
||||
return bpf_object__find_map_by_name(trace->bpf_obj, name);
|
||||
}
|
||||
|
||||
static void trace__set_bpf_map_filtered_pids(struct trace *trace)
|
||||
{
|
||||
trace->filter_pids.map = trace__find_bpf_map_by_name(trace, "pids_filtered");
|
||||
}
|
||||
|
||||
static void trace__set_bpf_map_syscalls(struct trace *trace)
|
||||
{
|
||||
trace->syscalls.map = trace__find_bpf_map_by_name(trace, "syscalls");
|
||||
trace->syscalls.prog_array.sys_enter = trace__find_bpf_map_by_name(trace, "syscalls_sys_enter");
|
||||
trace->syscalls.prog_array.sys_exit = trace__find_bpf_map_by_name(trace, "syscalls_sys_exit");
|
||||
}
|
||||
|
||||
static int trace__config(const char *var, const char *value, void *arg)
|
||||
{
|
||||
struct trace *trace = arg;
|
||||
|
|
|
@ -128,4 +128,8 @@ check arch/x86/lib/insn.c '-I "^#include [\"<]\(../include/\)*asm/in
|
|||
# diff non-symmetric files
|
||||
check_2 tools/perf/arch/x86/entry/syscalls/syscall_64.tbl arch/x86/entry/syscalls/syscall_64.tbl
|
||||
|
||||
# check duplicated library files
|
||||
check_2 tools/perf/util/hashmap.h tools/lib/bpf/hashmap.h
|
||||
check_2 tools/perf/util/hashmap.c tools/lib/bpf/hashmap.c
|
||||
|
||||
cd tools/perf
|
||||
|
|
|
@ -32,34 +32,41 @@ static void print_error(jvmtiEnv *jvmti, const char *msg, jvmtiError ret)
|
|||
|
||||
#ifdef HAVE_JVMTI_CMLR
|
||||
static jvmtiError
|
||||
do_get_line_numbers(jvmtiEnv *jvmti, void *pc, jmethodID m, jint bci,
|
||||
jvmti_line_info_t *tab, jint *nr)
|
||||
do_get_line_number(jvmtiEnv *jvmti, void *pc, jmethodID m, jint bci,
|
||||
jvmti_line_info_t *tab)
|
||||
{
|
||||
jint i, lines = 0;
|
||||
jint nr_lines = 0;
|
||||
jint i, nr_lines = 0;
|
||||
jvmtiLineNumberEntry *loc_tab = NULL;
|
||||
jvmtiError ret;
|
||||
jint src_line = -1;
|
||||
|
||||
ret = (*jvmti)->GetLineNumberTable(jvmti, m, &nr_lines, &loc_tab);
|
||||
if (ret != JVMTI_ERROR_NONE) {
|
||||
if (ret == JVMTI_ERROR_ABSENT_INFORMATION || ret == JVMTI_ERROR_NATIVE_METHOD) {
|
||||
/* No debug information for this method */
|
||||
return ret;
|
||||
} else if (ret != JVMTI_ERROR_NONE) {
|
||||
print_error(jvmti, "GetLineNumberTable", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_lines; i++) {
|
||||
if (loc_tab[i].start_location < bci) {
|
||||
tab[lines].pc = (unsigned long)pc;
|
||||
tab[lines].line_number = loc_tab[i].line_number;
|
||||
tab[lines].discrim = 0; /* not yet used */
|
||||
tab[lines].methodID = m;
|
||||
lines++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < nr_lines && loc_tab[i].start_location <= bci; i++) {
|
||||
src_line = i;
|
||||
}
|
||||
|
||||
if (src_line != -1) {
|
||||
tab->pc = (unsigned long)pc;
|
||||
tab->line_number = loc_tab[src_line].line_number;
|
||||
tab->discrim = 0; /* not yet used */
|
||||
tab->methodID = m;
|
||||
|
||||
ret = JVMTI_ERROR_NONE;
|
||||
} else {
|
||||
ret = JVMTI_ERROR_ABSENT_INFORMATION;
|
||||
}
|
||||
|
||||
(*jvmti)->Deallocate(jvmti, (unsigned char *)loc_tab);
|
||||
*nr = lines;
|
||||
return JVMTI_ERROR_NONE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static jvmtiError
|
||||
|
@ -67,9 +74,8 @@ get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t **
|
|||
{
|
||||
const jvmtiCompiledMethodLoadRecordHeader *hdr;
|
||||
jvmtiCompiledMethodLoadInlineRecord *rec;
|
||||
jvmtiLineNumberEntry *lne = NULL;
|
||||
PCStackInfo *c;
|
||||
jint nr, ret;
|
||||
jint ret;
|
||||
int nr_total = 0;
|
||||
int i, lines_total = 0;
|
||||
|
||||
|
@ -82,21 +88,7 @@ get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t **
|
|||
for (hdr = compile_info; hdr != NULL; hdr = hdr->next) {
|
||||
if (hdr->kind == JVMTI_CMLR_INLINE_INFO) {
|
||||
rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr;
|
||||
for (i = 0; i < rec->numpcs; i++) {
|
||||
c = rec->pcinfo + i;
|
||||
nr = 0;
|
||||
/*
|
||||
* unfortunately, need a tab to get the number of lines!
|
||||
*/
|
||||
ret = (*jvmti)->GetLineNumberTable(jvmti, c->methods[0], &nr, &lne);
|
||||
if (ret == JVMTI_ERROR_NONE) {
|
||||
/* free what was allocated for nothing */
|
||||
(*jvmti)->Deallocate(jvmti, (unsigned char *)lne);
|
||||
nr_total += (int)nr;
|
||||
} else {
|
||||
print_error(jvmti, "GetLineNumberTable", ret);
|
||||
}
|
||||
}
|
||||
nr_total += rec->numpcs;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,14 +107,17 @@ get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t **
|
|||
rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr;
|
||||
for (i = 0; i < rec->numpcs; i++) {
|
||||
c = rec->pcinfo + i;
|
||||
nr = 0;
|
||||
ret = do_get_line_numbers(jvmti, c->pc,
|
||||
c->methods[0],
|
||||
c->bcis[0],
|
||||
*tab + lines_total,
|
||||
&nr);
|
||||
/*
|
||||
* c->methods is the stack of inlined method calls
|
||||
* at c->pc. [0] is the leaf method. Caller frames
|
||||
* are ignored at the moment.
|
||||
*/
|
||||
ret = do_get_line_number(jvmti, c->pc,
|
||||
c->methods[0],
|
||||
c->bcis[0],
|
||||
*tab + lines_total);
|
||||
if (ret == JVMTI_ERROR_NONE)
|
||||
lines_total += nr;
|
||||
lines_total++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -246,8 +241,6 @@ compiled_method_load_cb(jvmtiEnv *jvmti,
|
|||
char *class_sign = NULL;
|
||||
char *func_name = NULL;
|
||||
char *func_sign = NULL;
|
||||
char *file_name = NULL;
|
||||
char fn[PATH_MAX];
|
||||
uint64_t addr = (uint64_t)(uintptr_t)code_addr;
|
||||
jvmtiError ret;
|
||||
int nr_lines = 0; /* in line_tab[] */
|
||||
|
@ -264,7 +257,9 @@ compiled_method_load_cb(jvmtiEnv *jvmti,
|
|||
if (has_line_numbers && map && map_length) {
|
||||
ret = get_line_numbers(jvmti, compile_info, &line_tab, &nr_lines);
|
||||
if (ret != JVMTI_ERROR_NONE) {
|
||||
warnx("jvmti: cannot get line table for method");
|
||||
if (ret != JVMTI_ERROR_NOT_FOUND) {
|
||||
warnx("jvmti: cannot get line table for method");
|
||||
}
|
||||
nr_lines = 0;
|
||||
} else if (nr_lines > 0) {
|
||||
line_file_names = malloc(sizeof(char*) * nr_lines);
|
||||
|
@ -282,12 +277,6 @@ compiled_method_load_cb(jvmtiEnv *jvmti,
|
|||
}
|
||||
}
|
||||
|
||||
ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name);
|
||||
if (ret != JVMTI_ERROR_NONE) {
|
||||
print_error(jvmti, "GetSourceFileName", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = (*jvmti)->GetClassSignature(jvmti, decl_class,
|
||||
&class_sign, NULL);
|
||||
if (ret != JVMTI_ERROR_NONE) {
|
||||
|
@ -302,8 +291,6 @@ compiled_method_load_cb(jvmtiEnv *jvmti,
|
|||
goto error;
|
||||
}
|
||||
|
||||
copy_class_filename(class_sign, file_name, fn, PATH_MAX);
|
||||
|
||||
/*
|
||||
* write source line info record if we have it
|
||||
*/
|
||||
|
@ -323,7 +310,6 @@ error:
|
|||
(*jvmti)->Deallocate(jvmti, (unsigned char *)func_name);
|
||||
(*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign);
|
||||
(*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign);
|
||||
(*jvmti)->Deallocate(jvmti, (unsigned char *)file_name);
|
||||
free(line_tab);
|
||||
while (line_file_names && (nr_lines > 0)) {
|
||||
if (line_file_names[nr_lines - 1]) {
|
||||
|
|
|
@ -169,7 +169,7 @@
|
|||
},
|
||||
{
|
||||
"BriefDescription": "Cycles GCT empty where dispatch was held",
|
||||
"MetricExpr": "(PM_GCT_NOSLOT_DISP_HELD_MAP + PM_GCT_NOSLOT_DISP_HELD_SRQ + PM_GCT_NOSLOT_DISP_HELD_ISSQ + PM_GCT_NOSLOT_DISP_HELD_OTHER) / PM_RUN_INST_CMPL)",
|
||||
"MetricExpr": "(PM_GCT_NOSLOT_DISP_HELD_MAP + PM_GCT_NOSLOT_DISP_HELD_SRQ + PM_GCT_NOSLOT_DISP_HELD_ISSQ + PM_GCT_NOSLOT_DISP_HELD_OTHER) / PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "gct_empty_disp_held_cpi"
|
||||
},
|
||||
|
|
|
@ -207,6 +207,84 @@
|
|||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "fxu_stall_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Instruction Completion Table empty for this thread due to branch mispred",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_BR_MPRED/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "ict_noslot_br_mpred_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Instruction Completion Table empty for this thread due to Icache Miss and branch mispred",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_BR_MPRED_ICMISS/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "ict_noslot_br_mpred_icmiss_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Instruction Completion Table other stalls",
|
||||
"MetricExpr": "(PM_ICT_NOSLOT_CYC - PM_ICT_NOSLOT_IC_MISS - PM_ICT_NOSLOT_BR_MPRED_ICMISS - PM_ICT_NOSLOT_BR_MPRED - PM_ICT_NOSLOT_DISP_HELD)/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "ict_noslot_cyc_other_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Cycles in which the NTC instruciton is held at dispatch for any reason",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_DISP_HELD/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "ict_noslot_disp_held_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Instruction Completion Table empty for this thread due to dispatch holds because the History Buffer was full. Could be GPR/VSR/VMR/FPR/CR/XVF",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_DISP_HELD_HB_FULL/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "ict_noslot_disp_held_hb_full_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Instruction Completion Table empty for this thread due to dispatch hold on this thread due to Issue q full, BRQ full, XVCF Full, Count cache, Link, Tar full",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_DISP_HELD_ISSQ/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "ict_noslot_disp_held_issq_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "ICT_NOSLOT_DISP_HELD_OTHER_CPI",
|
||||
"MetricExpr": "(PM_ICT_NOSLOT_DISP_HELD - PM_ICT_NOSLOT_DISP_HELD_HB_FULL - PM_ICT_NOSLOT_DISP_HELD_SYNC - PM_ICT_NOSLOT_DISP_HELD_TBEGIN - PM_ICT_NOSLOT_DISP_HELD_ISSQ)/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "ict_noslot_disp_held_other_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Dispatch held due to a synchronizing instruction at dispatch",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_DISP_HELD_SYNC/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "ict_noslot_disp_held_sync_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "the NTC instruction is being held at dispatch because it is a tbegin instruction and there is an older tbegin in the pipeline that must complete before the younger tbegin can dispatch",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_DISP_HELD_TBEGIN/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "ict_noslot_disp_held_tbegin_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "ICT_NOSLOT_IC_L2_CPI",
|
||||
"MetricExpr": "(PM_ICT_NOSLOT_IC_MISS - PM_ICT_NOSLOT_IC_L3 - PM_ICT_NOSLOT_IC_L3MISS)/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "ict_noslot_ic_l2_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Instruction Completion Table empty for this thread due to icache misses that were sourced from the local L3",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_IC_L3/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "ict_noslot_ic_l3_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Instruction Completion Table empty for this thread due to icache misses that were sourced from beyond the local L3. The source could be local/remote/distant memory or another core's cache",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_IC_L3MISS/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "ict_noslot_ic_l3miss_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Instruction Completion Table empty for this thread due to Icache Miss",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_IC_MISS/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "ict_noslot_ic_miss_cpi"
|
||||
},
|
||||
{
|
||||
"MetricExpr": "(PM_NTC_ISSUE_HELD_DARQ_FULL + PM_NTC_ISSUE_HELD_ARB + PM_NTC_ISSUE_HELD_OTHER)/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
|
@ -313,7 +391,7 @@
|
|||
"MetricName": "nested_tend_stall_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Number of cycles the ICT has no itags assigned to this thread",
|
||||
"BriefDescription": "Number of cycles the Instruction Completion Table has no itags assigned to this thread",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_CYC/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "nothing_dispatched_cpi"
|
||||
|
@ -362,7 +440,7 @@
|
|||
},
|
||||
{
|
||||
"BriefDescription": "Completion stall for other reasons",
|
||||
"MetricExpr": "PM_CMPLU_STALL - PM_CMPLU_STALL_NTC_DISP_FIN - PM_CMPLU_STALL_NTC_FLUSH - PM_CMPLU_STALL_LSU - PM_CMPLU_STALL_EXEC_UNIT - PM_CMPLU_STALL_BRU)/PM_RUN_INST_CMPL",
|
||||
"MetricExpr": "(PM_CMPLU_STALL - PM_CMPLU_STALL_NTC_DISP_FIN - PM_CMPLU_STALL_NTC_FLUSH - PM_CMPLU_STALL_LSU - PM_CMPLU_STALL_EXEC_UNIT - PM_CMPLU_STALL_BRU)/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "other_stall_cpi"
|
||||
},
|
||||
|
@ -425,7 +503,7 @@
|
|||
"MetricName": "st_fwd_stall_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Nothing completed and ICT not empty",
|
||||
"BriefDescription": "Nothing completed and Instruction Completion Table not empty",
|
||||
"MetricExpr": "PM_CMPLU_STALL/PM_RUN_INST_CMPL",
|
||||
"MetricGroup": "cpi_breakdown",
|
||||
"MetricName": "stall_cpi"
|
||||
|
@ -1819,71 +1897,6 @@
|
|||
"MetricExpr": "PM_FXU_IDLE / PM_CYC",
|
||||
"MetricName": "fxu_all_idle"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Ict empty for this thread due to branch mispred",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_BR_MPRED/PM_RUN_INST_CMPL",
|
||||
"MetricName": "ict_noslot_br_mpred_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Ict empty for this thread due to Icache Miss and branch mispred",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_BR_MPRED_ICMISS/PM_RUN_INST_CMPL",
|
||||
"MetricName": "ict_noslot_br_mpred_icmiss_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "ICT other stalls",
|
||||
"MetricExpr": "(PM_ICT_NOSLOT_CYC - PM_ICT_NOSLOT_IC_MISS - PM_ICT_NOSLOT_BR_MPRED_ICMISS - PM_ICT_NOSLOT_BR_MPRED - PM_ICT_NOSLOT_DISP_HELD)/PM_RUN_INST_CMPL",
|
||||
"MetricName": "ict_noslot_cyc_other_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Cycles in which the NTC instruciton is held at dispatch for any reason",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_DISP_HELD/PM_RUN_INST_CMPL",
|
||||
"MetricName": "ict_noslot_disp_held_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Ict empty for this thread due to dispatch holds because the History Buffer was full. Could be GPR/VSR/VMR/FPR/CR/XVF",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_DISP_HELD_HB_FULL/PM_RUN_INST_CMPL",
|
||||
"MetricName": "ict_noslot_disp_held_hb_full_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Ict empty for this thread due to dispatch hold on this thread due to Issue q full, BRQ full, XVCF Full, Count cache, Link, Tar full",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_DISP_HELD_ISSQ/PM_RUN_INST_CMPL",
|
||||
"MetricName": "ict_noslot_disp_held_issq_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "ICT_NOSLOT_DISP_HELD_OTHER_CPI",
|
||||
"MetricExpr": "(PM_ICT_NOSLOT_DISP_HELD - PM_ICT_NOSLOT_DISP_HELD_HB_FULL - PM_ICT_NOSLOT_DISP_HELD_SYNC - PM_ICT_NOSLOT_DISP_HELD_TBEGIN - PM_ICT_NOSLOT_DISP_HELD_ISSQ)/PM_RUN_INST_CMPL",
|
||||
"MetricName": "ict_noslot_disp_held_other_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Dispatch held due to a synchronizing instruction at dispatch",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_DISP_HELD_SYNC/PM_RUN_INST_CMPL",
|
||||
"MetricName": "ict_noslot_disp_held_sync_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "the NTC instruction is being held at dispatch because it is a tbegin instruction and there is an older tbegin in the pipeline that must complete before the younger tbegin can dispatch",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_DISP_HELD_TBEGIN/PM_RUN_INST_CMPL",
|
||||
"MetricName": "ict_noslot_disp_held_tbegin_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "ICT_NOSLOT_IC_L2_CPI",
|
||||
"MetricExpr": "(PM_ICT_NOSLOT_IC_MISS - PM_ICT_NOSLOT_IC_L3 - PM_ICT_NOSLOT_IC_L3MISS)/PM_RUN_INST_CMPL",
|
||||
"MetricName": "ict_noslot_ic_l2_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Ict empty for this thread due to icache misses that were sourced from the local L3",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_IC_L3/PM_RUN_INST_CMPL",
|
||||
"MetricName": "ict_noslot_ic_l3_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Ict empty for this thread due to icache misses that were sourced from beyond the local L3. The source could be local/remote/distant memory or another core's cache",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_IC_L3MISS/PM_RUN_INST_CMPL",
|
||||
"MetricName": "ict_noslot_ic_l3miss_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Ict empty for this thread due to Icache Miss",
|
||||
"MetricExpr": "PM_ICT_NOSLOT_IC_MISS/PM_RUN_INST_CMPL",
|
||||
"MetricName": "ict_noslot_ic_miss_cpi"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Rate of IERAT reloads from L2",
|
||||
"MetricExpr": "PM_IPTEG_FROM_L2 * 100 / PM_RUN_INST_CMPL",
|
||||
|
|
|
@ -328,31 +328,31 @@
|
|||
},
|
||||
{
|
||||
"BriefDescription": "Average latency of data read request to external memory (in nanoseconds). Accounts for demand loads and L1/L2 prefetches",
|
||||
"MetricExpr": "1000000000 * ( cha@event\\=0x36\\\\\\,umask\\=0x21@ / cha@event\\=0x35\\\\\\,umask\\=0x21@ ) / ( cha_0@event\\=0x0@ / duration_time )",
|
||||
"MetricExpr": "1000000000 * ( cha@event\\=0x36\\,umask\\=0x21@ / cha@event\\=0x35\\,umask\\=0x21@ ) / ( cha_0@event\\=0x0@ / duration_time )",
|
||||
"MetricGroup": "Memory_Lat",
|
||||
"MetricName": "DRAM_Read_Latency"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Average number of parallel data read requests to external memory. Accounts for demand loads and L1/L2 prefetches",
|
||||
"MetricExpr": "cha@event\\=0x36\\\\\\,umask\\=0x21@ / cha@event\\=0x36\\\\\\,umask\\=0x21\\\\\\,thresh\\=1@",
|
||||
"MetricExpr": "cha@event\\=0x36\\,umask\\=0x21@ / cha@event\\=0x36\\,umask\\=0x21\\,thresh\\=1@",
|
||||
"MetricGroup": "Memory_BW",
|
||||
"MetricName": "DRAM_Parallel_Reads"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Average latency of data read request to external 3D X-Point memory [in nanoseconds]. Accounts for demand loads and L1/L2 data-read prefetches",
|
||||
"MetricExpr": "( 1000000000 * ( imc@event\\=0xe0\\\\\\,umask\\=0x1@ / imc@event\\=0xe3@ ) / imc_0@event\\=0x0@ ) if 1 if 0 == 1 else 0 else 0",
|
||||
"MetricExpr": "( 1000000000 * ( imc@event\\=0xe0\\,umask\\=0x1@ / imc@event\\=0xe3@ ) / imc_0@event\\=0x0@ )",
|
||||
"MetricGroup": "Memory_Lat",
|
||||
"MetricName": "MEM_PMM_Read_Latency"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Average 3DXP Memory Bandwidth Use for reads [GB / sec]",
|
||||
"MetricExpr": "( ( 64 * imc@event\\=0xe3@ / 1000000000 ) / duration_time ) if 1 if 0 == 1 else 0 else 0",
|
||||
"MetricExpr": "( ( 64 * imc@event\\=0xe3@ / 1000000000 ) / duration_time )",
|
||||
"MetricGroup": "Memory_BW",
|
||||
"MetricName": "PMM_Read_BW"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Average 3DXP Memory Bandwidth Use for Writes [GB / sec]",
|
||||
"MetricExpr": "( ( 64 * imc@event\\=0xe7@ / 1000000000 ) / duration_time ) if 1 if 0 == 1 else 0 else 0",
|
||||
"MetricExpr": "( ( 64 * imc@event\\=0xe7@ / 1000000000 ) / duration_time )",
|
||||
"MetricGroup": "Memory_BW",
|
||||
"MetricName": "PMM_Write_BW"
|
||||
},
|
||||
|
|
|
@ -328,13 +328,13 @@
|
|||
},
|
||||
{
|
||||
"BriefDescription": "Average latency of data read request to external memory (in nanoseconds). Accounts for demand loads and L1/L2 prefetches",
|
||||
"MetricExpr": "1000000000 * ( cha@event\\=0x36\\\\\\,umask\\=0x21@ / cha@event\\=0x35\\\\\\,umask\\=0x21@ ) / ( cha_0@event\\=0x0@ / duration_time )",
|
||||
"MetricExpr": "1000000000 * ( cha@event\\=0x36\\,umask\\=0x21@ / cha@event\\=0x35\\,umask\\=0x21@ ) / ( cha_0@event\\=0x0@ / duration_time )",
|
||||
"MetricGroup": "Memory_Lat",
|
||||
"MetricName": "DRAM_Read_Latency"
|
||||
},
|
||||
{
|
||||
"BriefDescription": "Average number of parallel data read requests to external memory. Accounts for demand loads and L1/L2 prefetches",
|
||||
"MetricExpr": "cha@event\\=0x36\\\\\\,umask\\=0x21@ / cha@event\\=0x36\\\\\\,umask\\=0x21\\\\\\,thresh\\=1@",
|
||||
"MetricExpr": "cha@event\\=0x36\\,umask\\=0x21@ / cha@event\\=0x36\\,umask\\=0x21\\,thresh\\=1@",
|
||||
"MetricGroup": "Memory_BW",
|
||||
"MetricName": "DRAM_Parallel_Reads"
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
#ifndef __JSMN_H_
|
||||
#define __JSMN_H_
|
||||
|
||||
|
|
|
@ -57,6 +57,8 @@ perf-y += maps.o
|
|||
perf-y += time-utils-test.o
|
||||
perf-y += genelf.o
|
||||
perf-y += api-io.o
|
||||
perf-y += demangle-java-test.o
|
||||
perf-y += pfm.o
|
||||
|
||||
$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
|
||||
$(call rule_mkdir)
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
# Event added by system-wide or CPU perf-record to handle the race of
|
||||
# processes starting while /proc is processed.
|
||||
[event]
|
||||
fd=1
|
||||
group_fd=-1
|
||||
cpu=*
|
||||
pid=-1
|
||||
flags=8
|
||||
type=1
|
||||
size=120
|
||||
config=9
|
||||
sample_period=4000
|
||||
sample_type=455
|
||||
read_format=4
|
||||
# Event will be enabled right away.
|
||||
disabled=0
|
||||
inherit=1
|
||||
pinned=0
|
||||
exclusive=0
|
||||
exclude_user=0
|
||||
exclude_kernel=0
|
||||
exclude_hv=0
|
||||
exclude_idle=0
|
||||
mmap=1
|
||||
comm=1
|
||||
freq=1
|
||||
inherit_stat=0
|
||||
enable_on_exec=0
|
||||
task=1
|
||||
watermark=0
|
||||
precise_ip=0
|
||||
mmap_data=0
|
||||
sample_id_all=1
|
||||
exclude_host=0
|
||||
exclude_guest=0
|
||||
exclude_callchain_kernel=0
|
||||
exclude_callchain_user=0
|
||||
mmap2=1
|
||||
comm_exec=1
|
||||
context_switch=0
|
||||
write_backward=0
|
||||
namespaces=0
|
||||
use_clockid=0
|
||||
wakeup_events=0
|
||||
bp_type=0
|
||||
config1=0
|
||||
config2=0
|
||||
branch_sample_type=0
|
||||
sample_regs_user=0
|
||||
sample_stack_user=0
|
|
@ -9,6 +9,14 @@ cpu=0
|
|||
# no enable on exec for CPU attached
|
||||
enable_on_exec=0
|
||||
|
||||
# PERF_SAMPLE_IP | PERF_SAMPLE_TID PERF_SAMPLE_TIME | # PERF_SAMPLE_PERIOD
|
||||
# PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME |
|
||||
# PERF_SAMPLE_ID | PERF_SAMPLE_PERIOD
|
||||
# + PERF_SAMPLE_CPU added by -C 0
|
||||
sample_type=391
|
||||
sample_type=455
|
||||
|
||||
# Dummy event handles mmaps, comm and task.
|
||||
mmap=0
|
||||
comm=0
|
||||
task=0
|
||||
|
||||
[event:system-wide-dummy]
|
||||
|
|
|
@ -75,6 +75,13 @@ static struct test generic_tests[] = {
|
|||
{
|
||||
.desc = "PMU events",
|
||||
.func = test__pmu_events,
|
||||
.subtest = {
|
||||
.skip_if_fail = false,
|
||||
.get_nr = test__pmu_events_subtest_get_nr,
|
||||
.get_desc = test__pmu_events_subtest_get_desc,
|
||||
.skip_reason = test__pmu_events_subtest_skip_reason,
|
||||
},
|
||||
|
||||
},
|
||||
{
|
||||
.desc = "DSO data read",
|
||||
|
@ -309,6 +316,15 @@ static struct test generic_tests[] = {
|
|||
.desc = "Test jit_write_elf",
|
||||
.func = test__jit_write_elf,
|
||||
},
|
||||
{
|
||||
.desc = "Test libpfm4 support",
|
||||
.func = test__pfm,
|
||||
.subtest = {
|
||||
.skip_if_fail = true,
|
||||
.get_nr = test__pfm_subtest_get_nr,
|
||||
.get_desc = test__pfm_subtest_get_desc,
|
||||
}
|
||||
},
|
||||
{
|
||||
.desc = "Test api io",
|
||||
.func = test__api_io,
|
||||
|
@ -317,6 +333,10 @@ static struct test generic_tests[] = {
|
|||
.desc = "maps__merge_in",
|
||||
.func = test__maps__merge_in,
|
||||
},
|
||||
{
|
||||
.desc = "Demangle Java",
|
||||
.func = test__demangle_java,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
|
@ -327,7 +347,7 @@ static struct test *tests[] = {
|
|||
arch_tests,
|
||||
};
|
||||
|
||||
static bool perf_test__matches(struct test *test, int curr, int argc, const char *argv[])
|
||||
static bool perf_test__matches(const char *desc, int curr, int argc, const char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -344,7 +364,7 @@ static bool perf_test__matches(struct test *test, int curr, int argc, const char
|
|||
continue;
|
||||
}
|
||||
|
||||
if (strcasestr(test->desc, argv[i]))
|
||||
if (strcasestr(desc, argv[i]))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -429,8 +449,15 @@ static int test_and_print(struct test *t, bool force_skip, int subtest)
|
|||
case TEST_OK:
|
||||
pr_info(" Ok\n");
|
||||
break;
|
||||
case TEST_SKIP:
|
||||
color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n");
|
||||
case TEST_SKIP: {
|
||||
const char *skip_reason = NULL;
|
||||
if (t->subtest.skip_reason)
|
||||
skip_reason = t->subtest.skip_reason(subtest);
|
||||
if (skip_reason)
|
||||
color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (%s)\n", skip_reason);
|
||||
else
|
||||
color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n");
|
||||
}
|
||||
break;
|
||||
case TEST_FAIL:
|
||||
default:
|
||||
|
@ -566,7 +593,7 @@ static int run_shell_tests(int argc, const char *argv[], int i, int width)
|
|||
.priv = &st,
|
||||
};
|
||||
|
||||
if (!perf_test__matches(&test, curr, argc, argv))
|
||||
if (!perf_test__matches(test.desc, curr, argc, argv))
|
||||
continue;
|
||||
|
||||
st.file = ent->d_name;
|
||||
|
@ -594,9 +621,25 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
|
|||
|
||||
for_each_test(j, t) {
|
||||
int curr = i++, err;
|
||||
int subi;
|
||||
|
||||
if (!perf_test__matches(t, curr, argc, argv))
|
||||
continue;
|
||||
if (!perf_test__matches(t->desc, curr, argc, argv)) {
|
||||
bool skip = true;
|
||||
int subn;
|
||||
|
||||
if (!t->subtest.get_nr)
|
||||
continue;
|
||||
|
||||
subn = t->subtest.get_nr();
|
||||
|
||||
for (subi = 0; subi < subn; subi++) {
|
||||
if (perf_test__matches(t->subtest.get_desc(subi), curr, argc, argv))
|
||||
skip = false;
|
||||
}
|
||||
|
||||
if (skip)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t->is_supported && !t->is_supported()) {
|
||||
pr_debug("%2d: %-*s: Disabled\n", i, width, t->desc);
|
||||
|
@ -624,7 +667,6 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
|
|||
*/
|
||||
int subw = width > 2 ? width - 2 : width;
|
||||
bool skip = false;
|
||||
int subi;
|
||||
|
||||
if (subn <= 0) {
|
||||
color_fprintf(stderr, PERF_COLOR_YELLOW,
|
||||
|
@ -641,6 +683,9 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
|
|||
}
|
||||
|
||||
for (subi = 0; subi < subn; subi++) {
|
||||
if (!perf_test__matches(t->subtest.get_desc(subi), curr, argc, argv))
|
||||
continue;
|
||||
|
||||
pr_info("%2d.%1d: %-*s:", i, subi + 1, subw,
|
||||
t->subtest.get_desc(subi));
|
||||
err = test_and_print(t, skip, subi);
|
||||
|
@ -674,7 +719,7 @@ static int perf_test__list_shell(int argc, const char **argv, int i)
|
|||
.desc = shell_test__description(bf, sizeof(bf), path, ent->d_name),
|
||||
};
|
||||
|
||||
if (!perf_test__matches(&t, curr, argc, argv))
|
||||
if (!perf_test__matches(t.desc, curr, argc, argv))
|
||||
continue;
|
||||
|
||||
pr_info("%2d: %s\n", i, t.desc);
|
||||
|
@ -693,7 +738,7 @@ static int perf_test__list(int argc, const char **argv)
|
|||
for_each_test(j, t) {
|
||||
int curr = i++;
|
||||
|
||||
if (!perf_test__matches(t, curr, argc, argv) ||
|
||||
if (!perf_test__matches(t->desc, curr, argc, argv) ||
|
||||
(t->is_supported && !t->is_supported()))
|
||||
continue;
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "tests.h"
|
||||
#include "session.h"
|
||||
#include "debug.h"
|
||||
#include "demangle-java.h"
|
||||
|
||||
int test__demangle_java(struct test *test __maybe_unused, int subtest __maybe_unused)
|
||||
{
|
||||
int ret = TEST_OK;
|
||||
char *buf = NULL;
|
||||
size_t i;
|
||||
|
||||
struct {
|
||||
const char *mangled, *demangled;
|
||||
} test_cases[] = {
|
||||
{ "Ljava/lang/StringLatin1;equals([B[B)Z",
|
||||
"boolean java.lang.StringLatin1.equals(byte[], byte[])" },
|
||||
{ "Ljava/util/zip/ZipUtils;CENSIZ([BI)J",
|
||||
"long java.util.zip.ZipUtils.CENSIZ(byte[], int)" },
|
||||
{ "Ljava/util/regex/Pattern$BmpCharProperty;match(Ljava/util/regex/Matcher;ILjava/lang/CharSequence;)Z",
|
||||
"boolean java.util.regex.Pattern$BmpCharProperty.match(java.util.regex.Matcher, int, java.lang.CharSequence)" },
|
||||
{ "Ljava/lang/AbstractStringBuilder;appendChars(Ljava/lang/String;II)V",
|
||||
"void java.lang.AbstractStringBuilder.appendChars(java.lang.String, int, int)" },
|
||||
{ "Ljava/lang/Object;<init>()V",
|
||||
"void java.lang.Object<init>()" },
|
||||
};
|
||||
|
||||
for (i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
|
||||
buf = java_demangle_sym(test_cases[i].mangled, 0);
|
||||
if (strcmp(buf, test_cases[i].demangled)) {
|
||||
pr_debug("FAILED: %s: %s != %s\n", test_cases[i].mangled,
|
||||
buf, test_cases[i].demangled);
|
||||
ret = TEST_FAIL;
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -37,6 +37,7 @@ static int init_live_machine(struct machine *machine)
|
|||
union perf_event event;
|
||||
pid_t pid = getpid();
|
||||
|
||||
memset(&event, 0, sizeof(event));
|
||||
return perf_event__synthesize_mmap_events(NULL, &event, pid, pid,
|
||||
mmap_handler, machine, true);
|
||||
}
|
||||
|
@ -94,7 +95,7 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
|
|||
return strcmp((const char *) symbol, funcs[idx]);
|
||||
}
|
||||
|
||||
noinline int test_dwarf_unwind__thread(struct thread *thread)
|
||||
__no_tail_call noinline int test_dwarf_unwind__thread(struct thread *thread)
|
||||
{
|
||||
struct perf_sample sample;
|
||||
unsigned long cnt = 0;
|
||||
|
@ -125,7 +126,7 @@ noinline int test_dwarf_unwind__thread(struct thread *thread)
|
|||
|
||||
static int global_unwind_retval = -INT_MAX;
|
||||
|
||||
noinline int test_dwarf_unwind__compare(void *p1, void *p2)
|
||||
__no_tail_call noinline int test_dwarf_unwind__compare(void *p1, void *p2)
|
||||
{
|
||||
/* Any possible value should be 'thread' */
|
||||
struct thread *thread = *(struct thread **)p1;
|
||||
|
@ -144,7 +145,7 @@ noinline int test_dwarf_unwind__compare(void *p1, void *p2)
|
|||
return p1 - p2;
|
||||
}
|
||||
|
||||
noinline int test_dwarf_unwind__krava_3(struct thread *thread)
|
||||
__no_tail_call noinline int test_dwarf_unwind__krava_3(struct thread *thread)
|
||||
{
|
||||
struct thread *array[2] = {thread, thread};
|
||||
void *fp = &bsearch;
|
||||
|
@ -163,12 +164,12 @@ noinline int test_dwarf_unwind__krava_3(struct thread *thread)
|
|||
return global_unwind_retval;
|
||||
}
|
||||
|
||||
noinline int test_dwarf_unwind__krava_2(struct thread *thread)
|
||||
__no_tail_call noinline int test_dwarf_unwind__krava_2(struct thread *thread)
|
||||
{
|
||||
return test_dwarf_unwind__krava_3(thread);
|
||||
}
|
||||
|
||||
noinline int test_dwarf_unwind__krava_1(struct thread *thread)
|
||||
__no_tail_call noinline int test_dwarf_unwind__krava_1(struct thread *thread)
|
||||
{
|
||||
return test_dwarf_unwind__krava_2(thread);
|
||||
}
|
||||
|
|
|
@ -100,12 +100,11 @@ int test__perf_evsel__roundtrip_name_test(struct test *test __maybe_unused, int
|
|||
{
|
||||
int err = 0, ret = 0;
|
||||
|
||||
err = perf_evsel__name_array_test(perf_evsel__hw_names);
|
||||
err = perf_evsel__name_array_test(evsel__hw_names);
|
||||
if (err)
|
||||
ret = err;
|
||||
|
||||
err = __perf_evsel__name_array_test(perf_evsel__sw_names,
|
||||
PERF_COUNT_SW_DUMMY + 1);
|
||||
err = __perf_evsel__name_array_test(evsel__sw_names, PERF_COUNT_SW_DUMMY + 1);
|
||||
if (err)
|
||||
ret = err;
|
||||
|
||||
|
|
|
@ -35,11 +35,11 @@ static int perf_evsel__test_field(struct evsel *evsel, const char *name,
|
|||
|
||||
int test__perf_evsel__tp_sched_test(struct test *test __maybe_unused, int subtest __maybe_unused)
|
||||
{
|
||||
struct evsel *evsel = perf_evsel__newtp("sched", "sched_switch");
|
||||
struct evsel *evsel = evsel__newtp("sched", "sched_switch");
|
||||
int ret = 0;
|
||||
|
||||
if (IS_ERR(evsel)) {
|
||||
pr_debug("perf_evsel__newtp failed with %ld\n", PTR_ERR(evsel));
|
||||
pr_debug("evsel__newtp failed with %ld\n", PTR_ERR(evsel));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -66,10 +66,10 @@ int test__perf_evsel__tp_sched_test(struct test *test __maybe_unused, int subtes
|
|||
|
||||
evsel__delete(evsel);
|
||||
|
||||
evsel = perf_evsel__newtp("sched", "sched_wakeup");
|
||||
evsel = evsel__newtp("sched", "sched_wakeup");
|
||||
|
||||
if (IS_ERR(evsel)) {
|
||||
pr_debug("perf_evsel__newtp failed with %ld\n", PTR_ERR(evsel));
|
||||
pr_debug("evsel__newtp failed with %ld\n", PTR_ERR(evsel));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,15 +19,13 @@ static int test(struct expr_parse_ctx *ctx, const char *e, double val2)
|
|||
int test__expr(struct test *t __maybe_unused, int subtest __maybe_unused)
|
||||
{
|
||||
const char *p;
|
||||
const char **other;
|
||||
double val;
|
||||
int i, ret;
|
||||
double val, *val_ptr;
|
||||
int ret;
|
||||
struct expr_parse_ctx ctx;
|
||||
int num_other;
|
||||
|
||||
expr__ctx_init(&ctx);
|
||||
expr__add_id(&ctx, "FOO", 1);
|
||||
expr__add_id(&ctx, "BAR", 2);
|
||||
expr__add_id(&ctx, strdup("FOO"), 1);
|
||||
expr__add_id(&ctx, strdup("BAR"), 2);
|
||||
|
||||
ret = test(&ctx, "1+1", 2);
|
||||
ret |= test(&ctx, "FOO+BAR", 3);
|
||||
|
@ -39,6 +37,8 @@ int test__expr(struct test *t __maybe_unused, int subtest __maybe_unused)
|
|||
ret |= test(&ctx, "min(1,2) + 1", 2);
|
||||
ret |= test(&ctx, "max(1,2) + 1", 3);
|
||||
ret |= test(&ctx, "1+1 if 3*4 else 0", 2);
|
||||
ret |= test(&ctx, "1.1 + 2.1", 3.2);
|
||||
ret |= test(&ctx, ".1 + 2.", 2.1);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -51,25 +51,29 @@ int test__expr(struct test *t __maybe_unused, int subtest __maybe_unused)
|
|||
ret = expr__parse(&val, &ctx, p, 1);
|
||||
TEST_ASSERT_VAL("missing operand", ret == -1);
|
||||
|
||||
expr__ctx_clear(&ctx);
|
||||
TEST_ASSERT_VAL("find other",
|
||||
expr__find_other("FOO + BAR + BAZ + BOZO", "FOO", &other, &num_other, 1) == 0);
|
||||
TEST_ASSERT_VAL("find other", num_other == 3);
|
||||
TEST_ASSERT_VAL("find other", !strcmp(other[0], "BAR"));
|
||||
TEST_ASSERT_VAL("find other", !strcmp(other[1], "BAZ"));
|
||||
TEST_ASSERT_VAL("find other", !strcmp(other[2], "BOZO"));
|
||||
TEST_ASSERT_VAL("find other", other[3] == NULL);
|
||||
expr__find_other("FOO + BAR + BAZ + BOZO", "FOO",
|
||||
&ctx, 1) == 0);
|
||||
TEST_ASSERT_VAL("find other", hashmap__size(&ctx.ids) == 3);
|
||||
TEST_ASSERT_VAL("find other", hashmap__find(&ctx.ids, "BAR",
|
||||
(void **)&val_ptr));
|
||||
TEST_ASSERT_VAL("find other", hashmap__find(&ctx.ids, "BAZ",
|
||||
(void **)&val_ptr));
|
||||
TEST_ASSERT_VAL("find other", hashmap__find(&ctx.ids, "BOZO",
|
||||
(void **)&val_ptr));
|
||||
|
||||
expr__ctx_clear(&ctx);
|
||||
TEST_ASSERT_VAL("find other",
|
||||
expr__find_other("EVENT1\\,param\\=?@ + EVENT2\\,param\\=?@", NULL,
|
||||
&other, &num_other, 3) == 0);
|
||||
TEST_ASSERT_VAL("find other", num_other == 2);
|
||||
TEST_ASSERT_VAL("find other", !strcmp(other[0], "EVENT1,param=3/"));
|
||||
TEST_ASSERT_VAL("find other", !strcmp(other[1], "EVENT2,param=3/"));
|
||||
TEST_ASSERT_VAL("find other", other[2] == NULL);
|
||||
expr__find_other("EVENT1\\,param\\=?@ + EVENT2\\,param\\=?@",
|
||||
NULL, &ctx, 3) == 0);
|
||||
TEST_ASSERT_VAL("find other", hashmap__size(&ctx.ids) == 2);
|
||||
TEST_ASSERT_VAL("find other", hashmap__find(&ctx.ids, "EVENT1,param=3/",
|
||||
(void **)&val_ptr));
|
||||
TEST_ASSERT_VAL("find other", hashmap__find(&ctx.ids, "EVENT2,param=3/",
|
||||
(void **)&val_ptr));
|
||||
|
||||
for (i = 0; i < num_other; i++)
|
||||
zfree(&other[i]);
|
||||
free((void *)other);
|
||||
expr__ctx_clear(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -190,7 +190,7 @@ static int do_test(struct hists *hists, struct result *expected, size_t nr_expec
|
|||
* function since TEST_ASSERT_VAL() returns in case of failure.
|
||||
*/
|
||||
hists__collapse_resort(hists, NULL);
|
||||
perf_evsel__output_resort(hists_to_evsel(hists), NULL);
|
||||
evsel__output_resort(hists_to_evsel(hists), NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("use callchain: %d, cumulate callchain: %d\n",
|
||||
|
|
|
@ -142,7 +142,7 @@ int test__hists_filter(struct test *test __maybe_unused, int subtest __maybe_unu
|
|||
struct hists *hists = evsel__hists(evsel);
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
evsel__output_resort(evsel, NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("Normal histogram\n");
|
||||
|
|
|
@ -155,7 +155,7 @@ static int test1(struct evsel *evsel, struct machine *machine)
|
|||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
evsel__output_resort(evsel, NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
|
@ -255,7 +255,7 @@ static int test2(struct evsel *evsel, struct machine *machine)
|
|||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
evsel__output_resort(evsel, NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
|
@ -309,7 +309,7 @@ static int test3(struct evsel *evsel, struct machine *machine)
|
|||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
evsel__output_resort(evsel, NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
|
@ -387,7 +387,7 @@ static int test4(struct evsel *evsel, struct machine *machine)
|
|||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
evsel__output_resort(evsel, NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
|
@ -490,7 +490,7 @@ static int test5(struct evsel *evsel, struct machine *machine)
|
|||
goto out;
|
||||
|
||||
hists__collapse_resort(hists, NULL);
|
||||
perf_evsel__output_resort(evsel, NULL);
|
||||
evsel__output_resort(evsel, NULL);
|
||||
|
||||
if (verbose > 2) {
|
||||
pr_info("[fields = %s, sort = %s]\n", field_order, sort_order);
|
||||
|
|
|
@ -84,10 +84,13 @@ make_no_libaudit := NO_LIBAUDIT=1
|
|||
make_no_libbionic := NO_LIBBIONIC=1
|
||||
make_no_auxtrace := NO_AUXTRACE=1
|
||||
make_no_libbpf := NO_LIBBPF=1
|
||||
make_no_libbpf_DEBUG := NO_LIBBPF=1 DEBUG=1
|
||||
make_no_libcrypto := NO_LIBCRYPTO=1
|
||||
make_with_babeltrace:= LIBBABELTRACE=1
|
||||
make_no_sdt := NO_SDT=1
|
||||
make_no_syscall_tbl := NO_SYSCALL_TABLE=1
|
||||
make_with_clangllvm := LIBCLANGLLVM=1
|
||||
make_with_libpfm4 := LIBPFM4=1
|
||||
make_tags := tags
|
||||
make_cscope := cscope
|
||||
make_help := help
|
||||
|
@ -112,7 +115,7 @@ make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1
|
|||
make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1
|
||||
make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1
|
||||
make_minimal += NO_LIBCRYPTO=1 NO_SDT=1 NO_JVMTI=1 NO_LIBZSTD=1
|
||||
make_minimal += NO_LIBCAP=1
|
||||
make_minimal += NO_LIBCAP=1 NO_SYSCALL_TABLE=1
|
||||
|
||||
# $(run) contains all available tests
|
||||
run := make_pure
|
||||
|
@ -144,8 +147,13 @@ run += make_no_libaudit
|
|||
run += make_no_libbionic
|
||||
run += make_no_auxtrace
|
||||
run += make_no_libbpf
|
||||
run += make_no_libbpf_DEBUG
|
||||
run += make_no_libcrypto
|
||||
run += make_no_sdt
|
||||
run += make_no_syscall_tbl
|
||||
run += make_with_babeltrace
|
||||
run += make_with_clangllvm
|
||||
run += make_with_libpfm4
|
||||
run += make_help
|
||||
run += make_doc
|
||||
run += make_perf_o
|
||||
|
|
|
@ -79,9 +79,9 @@ int test__basic_mmap(struct test *test __maybe_unused, int subtest __maybe_unuse
|
|||
char name[64];
|
||||
|
||||
snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]);
|
||||
evsels[i] = perf_evsel__newtp("syscalls", name);
|
||||
evsels[i] = evsel__newtp("syscalls", name);
|
||||
if (IS_ERR(evsels[i])) {
|
||||
pr_debug("perf_evsel__new(%s)\n", name);
|
||||
pr_debug("evsel__new(%s)\n", name);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ int test__openat_syscall_event_on_all_cpus(struct test *test __maybe_unused, int
|
|||
|
||||
CPU_ZERO(&cpu_set);
|
||||
|
||||
evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
|
||||
evsel = evsel__newtp("syscalls", "sys_enter_openat");
|
||||
if (IS_ERR(evsel)) {
|
||||
tracing_path__strerror_open_tp(errno, errbuf, sizeof(errbuf), "syscalls", "sys_enter_openat");
|
||||
pr_debug("%s\n", errbuf);
|
||||
|
@ -90,8 +90,8 @@ int test__openat_syscall_event_on_all_cpus(struct test *test __maybe_unused, int
|
|||
* we use the auto allocation it will allocate just for 1 cpu,
|
||||
* as we start by cpu 0.
|
||||
*/
|
||||
if (perf_evsel__alloc_counts(evsel, cpus->nr, 1) < 0) {
|
||||
pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr);
|
||||
if (evsel__alloc_counts(evsel, cpus->nr, 1) < 0) {
|
||||
pr_debug("evsel__alloc_counts(ncpus=%d)\n", cpus->nr);
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ int test__openat_syscall_event_on_all_cpus(struct test *test __maybe_unused, int
|
|||
}
|
||||
}
|
||||
|
||||
perf_evsel__free_counts(evsel);
|
||||
evsel__free_counts(evsel);
|
||||
out_close_fd:
|
||||
perf_evsel__close_fd(&evsel->core);
|
||||
out_evsel_delete:
|
||||
|
|
|
@ -46,9 +46,9 @@ int test__syscall_openat_tp_fields(struct test *test __maybe_unused, int subtest
|
|||
goto out;
|
||||
}
|
||||
|
||||
evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
|
||||
evsel = evsel__newtp("syscalls", "sys_enter_openat");
|
||||
if (IS_ERR(evsel)) {
|
||||
pr_debug("%s: perf_evsel__newtp\n", __func__);
|
||||
pr_debug("%s: evsel__newtp\n", __func__);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ int test__openat_syscall_event(struct test *test __maybe_unused, int subtest __m
|
|||
return -1;
|
||||
}
|
||||
|
||||
evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
|
||||
evsel = evsel__newtp("syscalls", "sys_enter_openat");
|
||||
if (IS_ERR(evsel)) {
|
||||
tracing_path__strerror_open_tp(errno, errbuf, sizeof(errbuf), "syscalls", "sys_enter_openat");
|
||||
pr_debug("%s\n", errbuf);
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Test support for libpfm4 event encodings.
|
||||
*
|
||||
* Copyright 2020 Google LLC.
|
||||
*/
|
||||
#include "tests.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/pfm.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#ifdef HAVE_LIBPFM
|
||||
static int test__pfm_events(void);
|
||||
static int test__pfm_group(void);
|
||||
#endif
|
||||
|
||||
static const struct {
|
||||
int (*func)(void);
|
||||
const char *desc;
|
||||
} pfm_testcase_table[] = {
|
||||
#ifdef HAVE_LIBPFM
|
||||
{
|
||||
.func = test__pfm_events,
|
||||
.desc = "test of individual --pfm-events",
|
||||
},
|
||||
{
|
||||
.func = test__pfm_group,
|
||||
.desc = "test groups of --pfm-events",
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef HAVE_LIBPFM
|
||||
static int count_pfm_events(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
int count = 0;
|
||||
|
||||
perf_evlist__for_each_entry(evlist, evsel) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int test__pfm_events(void)
|
||||
{
|
||||
struct evlist *evlist;
|
||||
struct option opt;
|
||||
size_t i;
|
||||
const struct {
|
||||
const char *events;
|
||||
int nr_events;
|
||||
} table[] = {
|
||||
{
|
||||
.events = "",
|
||||
.nr_events = 0,
|
||||
},
|
||||
{
|
||||
.events = "instructions",
|
||||
.nr_events = 1,
|
||||
},
|
||||
{
|
||||
.events = "instructions,cycles",
|
||||
.nr_events = 2,
|
||||
},
|
||||
{
|
||||
.events = "stereolab",
|
||||
.nr_events = 0,
|
||||
},
|
||||
{
|
||||
.events = "instructions,instructions",
|
||||
.nr_events = 2,
|
||||
},
|
||||
{
|
||||
.events = "stereolab,instructions",
|
||||
.nr_events = 0,
|
||||
},
|
||||
{
|
||||
.events = "instructions,stereolab",
|
||||
.nr_events = 1,
|
||||
},
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(table); i++) {
|
||||
evlist = evlist__new();
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
opt.value = evlist;
|
||||
parse_libpfm_events_option(&opt,
|
||||
table[i].events,
|
||||
0);
|
||||
TEST_ASSERT_EQUAL(table[i].events,
|
||||
count_pfm_events(&evlist->core),
|
||||
table[i].nr_events);
|
||||
TEST_ASSERT_EQUAL(table[i].events,
|
||||
evlist->nr_groups,
|
||||
0);
|
||||
|
||||
evlist__delete(evlist);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test__pfm_group(void)
|
||||
{
|
||||
struct evlist *evlist;
|
||||
struct option opt;
|
||||
size_t i;
|
||||
const struct {
|
||||
const char *events;
|
||||
int nr_events;
|
||||
int nr_groups;
|
||||
} table[] = {
|
||||
{
|
||||
.events = "{},",
|
||||
.nr_events = 0,
|
||||
.nr_groups = 0,
|
||||
},
|
||||
{
|
||||
.events = "{instructions}",
|
||||
.nr_events = 1,
|
||||
.nr_groups = 1,
|
||||
},
|
||||
{
|
||||
.events = "{instructions},{}",
|
||||
.nr_events = 1,
|
||||
.nr_groups = 1,
|
||||
},
|
||||
{
|
||||
.events = "{},{instructions}",
|
||||
.nr_events = 0,
|
||||
.nr_groups = 0,
|
||||
},
|
||||
{
|
||||
.events = "{instructions},{instructions}",
|
||||
.nr_events = 2,
|
||||
.nr_groups = 2,
|
||||
},
|
||||
{
|
||||
.events = "{instructions,cycles},{instructions,cycles}",
|
||||
.nr_events = 4,
|
||||
.nr_groups = 2,
|
||||
},
|
||||
{
|
||||
.events = "{stereolab}",
|
||||
.nr_events = 0,
|
||||
.nr_groups = 0,
|
||||
},
|
||||
{
|
||||
.events =
|
||||
"{instructions,cycles},{instructions,stereolab}",
|
||||
.nr_events = 3,
|
||||
.nr_groups = 1,
|
||||
},
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(table); i++) {
|
||||
evlist = evlist__new();
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
opt.value = evlist;
|
||||
parse_libpfm_events_option(&opt,
|
||||
table[i].events,
|
||||
0);
|
||||
TEST_ASSERT_EQUAL(table[i].events,
|
||||
count_pfm_events(&evlist->core),
|
||||
table[i].nr_events);
|
||||
TEST_ASSERT_EQUAL(table[i].events,
|
||||
evlist->nr_groups,
|
||||
table[i].nr_groups);
|
||||
|
||||
evlist__delete(evlist);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *test__pfm_subtest_get_desc(int i)
|
||||
{
|
||||
if (i < 0 || i >= (int)ARRAY_SIZE(pfm_testcase_table))
|
||||
return NULL;
|
||||
return pfm_testcase_table[i].desc;
|
||||
}
|
||||
|
||||
int test__pfm_subtest_get_nr(void)
|
||||
{
|
||||
return (int)ARRAY_SIZE(pfm_testcase_table);
|
||||
}
|
||||
|
||||
int test__pfm(struct test *test __maybe_unused, int i __maybe_unused)
|
||||
{
|
||||
#ifdef HAVE_LIBPFM
|
||||
if (i < 0 || i >= (int)ARRAY_SIZE(pfm_testcase_table))
|
||||
return TEST_FAIL;
|
||||
return pfm_testcase_table[i].func();
|
||||
#else
|
||||
return TEST_SKIP;
|
||||
#endif
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "math.h"
|
||||
#include "parse-events.h"
|
||||
#include "pmu.h"
|
||||
#include "tests.h"
|
||||
|
@ -8,6 +9,9 @@
|
|||
#include <linux/zalloc.h>
|
||||
#include "debug.h"
|
||||
#include "../pmu-events/pmu-events.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/expr.h"
|
||||
#include "util/parse-events.h"
|
||||
|
||||
struct perf_pmu_test_event {
|
||||
struct pmu_event event;
|
||||
|
@ -144,7 +148,7 @@ static struct pmu_events_map *__test_pmu_get_events_map(void)
|
|||
}
|
||||
|
||||
/* Verify generated events from pmu-events.c is as expected */
|
||||
static int __test_pmu_event_table(void)
|
||||
static int test_pmu_event_table(void)
|
||||
{
|
||||
struct pmu_events_map *map = __test_pmu_get_events_map();
|
||||
struct pmu_event *table;
|
||||
|
@ -347,14 +351,11 @@ static int __test__pmu_event_aliases(char *pmu_name, int *count)
|
|||
return res;
|
||||
}
|
||||
|
||||
int test__pmu_events(struct test *test __maybe_unused,
|
||||
int subtest __maybe_unused)
|
||||
|
||||
static int test_aliases(void)
|
||||
{
|
||||
struct perf_pmu *pmu = NULL;
|
||||
|
||||
if (__test_pmu_event_table())
|
||||
return -1;
|
||||
|
||||
while ((pmu = perf_pmu__scan(pmu)) != NULL) {
|
||||
int count = 0;
|
||||
|
||||
|
@ -377,3 +378,163 @@ int test__pmu_events(struct test *test __maybe_unused,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_number(const char *str)
|
||||
{
|
||||
char *end_ptr;
|
||||
double v;
|
||||
|
||||
errno = 0;
|
||||
v = strtod(str, &end_ptr);
|
||||
(void)v; // We're not interested in this value, only if it is valid
|
||||
return errno == 0 && end_ptr != str;
|
||||
}
|
||||
|
||||
static int check_parse_id(const char *id, bool same_cpu, struct pmu_event *pe)
|
||||
{
|
||||
struct parse_events_error error;
|
||||
struct evlist *evlist;
|
||||
int ret;
|
||||
|
||||
/* Numbers are always valid. */
|
||||
if (is_number(id))
|
||||
return 0;
|
||||
|
||||
evlist = evlist__new();
|
||||
memset(&error, 0, sizeof(error));
|
||||
ret = parse_events(evlist, id, &error);
|
||||
if (ret && same_cpu) {
|
||||
pr_warning("Parse event failed metric '%s' id '%s' expr '%s'\n",
|
||||
pe->metric_name, id, pe->metric_expr);
|
||||
pr_warning("Error string '%s' help '%s'\n", error.str,
|
||||
error.help);
|
||||
} else if (ret) {
|
||||
pr_debug3("Parse event failed, but for an event that may not be supported by this CPU.\nid '%s' metric '%s' expr '%s'\n",
|
||||
id, pe->metric_name, pe->metric_expr);
|
||||
ret = 0;
|
||||
}
|
||||
evlist__delete(evlist);
|
||||
free(error.str);
|
||||
free(error.help);
|
||||
free(error.first_str);
|
||||
free(error.first_help);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void expr_failure(const char *msg,
|
||||
const struct pmu_events_map *map,
|
||||
const struct pmu_event *pe)
|
||||
{
|
||||
pr_debug("%s for map %s %s %s\n",
|
||||
msg, map->cpuid, map->version, map->type);
|
||||
pr_debug("On metric %s\n", pe->metric_name);
|
||||
pr_debug("On expression %s\n", pe->metric_expr);
|
||||
}
|
||||
|
||||
static int test_parsing(void)
|
||||
{
|
||||
struct pmu_events_map *cpus_map = perf_pmu__find_map(NULL);
|
||||
struct pmu_events_map *map;
|
||||
struct pmu_event *pe;
|
||||
int i, j, k;
|
||||
int ret = 0;
|
||||
struct expr_parse_ctx ctx;
|
||||
double result;
|
||||
|
||||
i = 0;
|
||||
for (;;) {
|
||||
map = &pmu_events_map[i++];
|
||||
if (!map->table)
|
||||
break;
|
||||
j = 0;
|
||||
for (;;) {
|
||||
struct hashmap_entry *cur;
|
||||
size_t bkt;
|
||||
|
||||
pe = &map->table[j++];
|
||||
if (!pe->name && !pe->metric_group && !pe->metric_name)
|
||||
break;
|
||||
if (!pe->metric_expr)
|
||||
continue;
|
||||
expr__ctx_init(&ctx);
|
||||
if (expr__find_other(pe->metric_expr, NULL, &ctx, 0)
|
||||
< 0) {
|
||||
expr_failure("Parse other failed", map, pe);
|
||||
ret++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add all ids with a made up value. The value may
|
||||
* trigger divide by zero when subtracted and so try to
|
||||
* make them unique.
|
||||
*/
|
||||
k = 1;
|
||||
hashmap__for_each_entry((&ctx.ids), cur, bkt)
|
||||
expr__add_id(&ctx, strdup(cur->key), k++);
|
||||
|
||||
hashmap__for_each_entry((&ctx.ids), cur, bkt) {
|
||||
if (check_parse_id(cur->key, map == cpus_map,
|
||||
pe))
|
||||
ret++;
|
||||
}
|
||||
|
||||
if (expr__parse(&result, &ctx, pe->metric_expr, 0)) {
|
||||
expr_failure("Parse failed", map, pe);
|
||||
ret++;
|
||||
}
|
||||
expr__ctx_clear(&ctx);
|
||||
}
|
||||
}
|
||||
/* TODO: fail when not ok */
|
||||
return ret == 0 ? TEST_OK : TEST_SKIP;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
int (*func)(void);
|
||||
const char *desc;
|
||||
} pmu_events_testcase_table[] = {
|
||||
{
|
||||
.func = test_pmu_event_table,
|
||||
.desc = "PMU event table sanity",
|
||||
},
|
||||
{
|
||||
.func = test_aliases,
|
||||
.desc = "PMU event map aliases",
|
||||
},
|
||||
{
|
||||
.func = test_parsing,
|
||||
.desc = "Parsing of PMU event table metrics",
|
||||
},
|
||||
};
|
||||
|
||||
const char *test__pmu_events_subtest_get_desc(int subtest)
|
||||
{
|
||||
if (subtest < 0 ||
|
||||
subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
|
||||
return NULL;
|
||||
return pmu_events_testcase_table[subtest].desc;
|
||||
}
|
||||
|
||||
const char *test__pmu_events_subtest_skip_reason(int subtest)
|
||||
{
|
||||
if (subtest < 0 ||
|
||||
subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
|
||||
return NULL;
|
||||
if (pmu_events_testcase_table[subtest].func != test_parsing)
|
||||
return NULL;
|
||||
return "some metrics failed";
|
||||
}
|
||||
|
||||
int test__pmu_events_subtest_get_nr(void)
|
||||
{
|
||||
return (int)ARRAY_SIZE(pmu_events_testcase_table);
|
||||
}
|
||||
|
||||
int test__pmu_events(struct test *test __maybe_unused, int subtest)
|
||||
{
|
||||
if (subtest < 0 ||
|
||||
subtest >= (int)ARRAY_SIZE(pmu_events_testcase_table))
|
||||
return TEST_FAIL;
|
||||
return pmu_events_testcase_table[subtest].func();
|
||||
}
|
||||
|
|
|
@ -156,8 +156,8 @@ int test__pmu(struct test *test __maybe_unused, int subtest __maybe_unused)
|
|||
if (ret)
|
||||
break;
|
||||
|
||||
ret = perf_pmu__config_terms(&formats, &attr, terms,
|
||||
false, NULL);
|
||||
ret = perf_pmu__config_terms("perf-pmu-test", &formats, &attr,
|
||||
terms, false, NULL);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id)
|
|||
|
||||
evsel = evsel__new(&attr);
|
||||
if (evsel == NULL) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
pr_debug("evsel__new\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
evlist__add(evlist, evsel);
|
||||
|
|
|
@ -34,6 +34,7 @@ struct test {
|
|||
bool skip_if_fail;
|
||||
int (*get_nr)(void);
|
||||
const char *(*get_desc)(int subtest);
|
||||
const char *(*skip_reason)(int subtest);
|
||||
} subtest;
|
||||
bool (*is_supported)(void);
|
||||
void *priv;
|
||||
|
@ -50,6 +51,9 @@ int test__perf_evsel__tp_sched_test(struct test *test, int subtest);
|
|||
int test__syscall_openat_tp_fields(struct test *test, int subtest);
|
||||
int test__pmu(struct test *test, int subtest);
|
||||
int test__pmu_events(struct test *test, int subtest);
|
||||
const char *test__pmu_events_subtest_get_desc(int subtest);
|
||||
const char *test__pmu_events_subtest_skip_reason(int subtest);
|
||||
int test__pmu_events_subtest_get_nr(void);
|
||||
int test__attr(struct test *test, int subtest);
|
||||
int test__dso_data(struct test *test, int subtest);
|
||||
int test__dso_data_cache(struct test *test, int subtest);
|
||||
|
@ -113,6 +117,10 @@ int test__maps__merge_in(struct test *t, int subtest);
|
|||
int test__time_utils(struct test *t, int subtest);
|
||||
int test__jit_write_elf(struct test *test, int subtest);
|
||||
int test__api_io(struct test *test, int subtest);
|
||||
int test__demangle_java(struct test *test, int subtest);
|
||||
int test__pfm(struct test *test, int subtest);
|
||||
const char *test__pfm_subtest_get_desc(int subtest);
|
||||
int test__pfm_subtest_get_nr(void);
|
||||
|
||||
bool test__bp_signal_is_supported(void);
|
||||
bool test__bp_account_is_supported(void);
|
||||
|
|
|
@ -57,7 +57,7 @@ process_arch()
|
|||
local arch="$1"
|
||||
local asm_errno=$(asm_errno_file "$arch")
|
||||
|
||||
$gcc $include_path -E -dM -x c $asm_errno \
|
||||
$gcc $CFLAGS $include_path -E -dM -x c $asm_errno \
|
||||
|grep -hE '^#define[[:blank:]]+(E[^[:blank:]]+)[[:blank:]]+([[:digit:]]+).*' \
|
||||
|awk '{ print $2","$3; }' \
|
||||
|sort -t, -k2 -nu \
|
||||
|
@ -91,7 +91,7 @@ EoHEADER
|
|||
# in tools/perf/arch
|
||||
archlist=""
|
||||
for arch in $(find $toolsdir/arch -maxdepth 1 -mindepth 1 -type d -printf "%f\n" | grep -v x86 | sort); do
|
||||
test -d arch/$arch && archlist="$archlist $arch"
|
||||
test -d $toolsdir/perf/arch/$arch && archlist="$archlist $arch"
|
||||
done
|
||||
|
||||
for arch in x86 $archlist generic; do
|
||||
|
|
|
@ -106,7 +106,7 @@ perf-$(CONFIG_AUXTRACE) += intel-pt-decoder/
|
|||
perf-$(CONFIG_AUXTRACE) += intel-pt.o
|
||||
perf-$(CONFIG_AUXTRACE) += intel-bts.o
|
||||
perf-$(CONFIG_AUXTRACE) += arm-spe.o
|
||||
perf-$(CONFIG_AUXTRACE) += arm-spe-pkt-decoder.o
|
||||
perf-$(CONFIG_AUXTRACE) += arm-spe-decoder/
|
||||
perf-$(CONFIG_AUXTRACE) += s390-cpumsf.o
|
||||
|
||||
ifdef CONFIG_LIBOPENCSD
|
||||
|
@ -136,6 +136,10 @@ perf-$(CONFIG_LIBELF) += symbol-elf.o
|
|||
perf-$(CONFIG_LIBELF) += probe-file.o
|
||||
perf-$(CONFIG_LIBELF) += probe-event.o
|
||||
|
||||
ifndef CONFIG_LIBBPF
|
||||
perf-y += hashmap.o
|
||||
endif
|
||||
|
||||
ifndef CONFIG_LIBELF
|
||||
perf-y += symbol-minimal.o
|
||||
endif
|
||||
|
@ -179,6 +183,8 @@ perf-$(CONFIG_LIBBPF) += bpf-event.o
|
|||
|
||||
perf-$(CONFIG_CXX) += c++/
|
||||
|
||||
perf-$(CONFIG_LIBPFM4) += pfm.o
|
||||
|
||||
CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
|
||||
CFLAGS_llvm-utils.o += -DPERF_INCLUDE_DIR="BUILD_STR($(perf_include_dir_SQ))"
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#include <linux/bitops.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <subcmd/parse-options.h>
|
||||
#include <subcmd/run-command.h>
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ struct annotation_line {
|
|||
u32 idx;
|
||||
int idx_asm;
|
||||
int data_nr;
|
||||
struct annotation_data data[0];
|
||||
struct annotation_data data[];
|
||||
};
|
||||
|
||||
struct disasm_line {
|
||||
|
@ -227,7 +227,7 @@ void symbol__calc_percent(struct symbol *sym, struct evsel *evsel);
|
|||
struct sym_hist {
|
||||
u64 nr_samples;
|
||||
u64 period;
|
||||
struct sym_hist_entry addr[0];
|
||||
struct sym_hist_entry addr[];
|
||||
};
|
||||
|
||||
struct cyc_hist {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
perf-$(CONFIG_AUXTRACE) += arm-spe-pkt-decoder.o arm-spe-decoder.o
|
|
@ -0,0 +1,219 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* arm_spe_decoder.c: ARM SPE support
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/zalloc.h>
|
||||
|
||||
#include "../auxtrace.h"
|
||||
#include "../debug.h"
|
||||
#include "../util.h"
|
||||
|
||||
#include "arm-spe-decoder.h"
|
||||
|
||||
#ifndef BIT
|
||||
#define BIT(n) (1UL << (n))
|
||||
#endif
|
||||
|
||||
static u64 arm_spe_calc_ip(int index, u64 payload)
|
||||
{
|
||||
u8 *addr = (u8 *)&payload;
|
||||
int ns, el;
|
||||
|
||||
/* Instruction virtual address or Branch target address */
|
||||
if (index == SPE_ADDR_PKT_HDR_INDEX_INS ||
|
||||
index == SPE_ADDR_PKT_HDR_INDEX_BRANCH) {
|
||||
ns = addr[7] & SPE_ADDR_PKT_NS;
|
||||
el = (addr[7] & SPE_ADDR_PKT_EL_MASK) >> SPE_ADDR_PKT_EL_OFFSET;
|
||||
|
||||
/* Fill highest byte for EL1 or EL2 (VHE) mode */
|
||||
if (ns && (el == SPE_ADDR_PKT_EL1 || el == SPE_ADDR_PKT_EL2))
|
||||
addr[7] = 0xff;
|
||||
/* Clean highest byte for other cases */
|
||||
else
|
||||
addr[7] = 0x0;
|
||||
|
||||
/* Data access virtual address */
|
||||
} else if (index == SPE_ADDR_PKT_HDR_INDEX_DATA_VIRT) {
|
||||
|
||||
/* Fill highest byte if bits [48..55] is 0xff */
|
||||
if (addr[6] == 0xff)
|
||||
addr[7] = 0xff;
|
||||
/* Otherwise, cleanup tags */
|
||||
else
|
||||
addr[7] = 0x0;
|
||||
|
||||
/* Data access physical address */
|
||||
} else if (index == SPE_ADDR_PKT_HDR_INDEX_DATA_PHYS) {
|
||||
/* Cleanup byte 7 */
|
||||
addr[7] = 0x0;
|
||||
} else {
|
||||
pr_err("unsupported address packet index: 0x%x\n", index);
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
struct arm_spe_decoder *arm_spe_decoder_new(struct arm_spe_params *params)
|
||||
{
|
||||
struct arm_spe_decoder *decoder;
|
||||
|
||||
if (!params->get_trace)
|
||||
return NULL;
|
||||
|
||||
decoder = zalloc(sizeof(struct arm_spe_decoder));
|
||||
if (!decoder)
|
||||
return NULL;
|
||||
|
||||
decoder->get_trace = params->get_trace;
|
||||
decoder->data = params->data;
|
||||
|
||||
return decoder;
|
||||
}
|
||||
|
||||
void arm_spe_decoder_free(struct arm_spe_decoder *decoder)
|
||||
{
|
||||
free(decoder);
|
||||
}
|
||||
|
||||
static int arm_spe_get_data(struct arm_spe_decoder *decoder)
|
||||
{
|
||||
struct arm_spe_buffer buffer = { .buf = 0, };
|
||||
int ret;
|
||||
|
||||
pr_debug("Getting more data\n");
|
||||
ret = decoder->get_trace(&buffer, decoder->data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
decoder->buf = buffer.buf;
|
||||
decoder->len = buffer.len;
|
||||
|
||||
if (!decoder->len)
|
||||
pr_debug("No more data\n");
|
||||
|
||||
return decoder->len;
|
||||
}
|
||||
|
||||
static int arm_spe_get_next_packet(struct arm_spe_decoder *decoder)
|
||||
{
|
||||
int ret;
|
||||
|
||||
do {
|
||||
if (!decoder->len) {
|
||||
ret = arm_spe_get_data(decoder);
|
||||
|
||||
/* Failed to read out trace data */
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = arm_spe_get_packet(decoder->buf, decoder->len,
|
||||
&decoder->packet);
|
||||
if (ret <= 0) {
|
||||
/* Move forward for 1 byte */
|
||||
decoder->buf += 1;
|
||||
decoder->len -= 1;
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
decoder->buf += ret;
|
||||
decoder->len -= ret;
|
||||
} while (decoder->packet.type == ARM_SPE_PAD);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int arm_spe_read_record(struct arm_spe_decoder *decoder)
|
||||
{
|
||||
int err;
|
||||
int idx;
|
||||
u64 payload, ip;
|
||||
|
||||
memset(&decoder->record, 0x0, sizeof(decoder->record));
|
||||
|
||||
while (1) {
|
||||
err = arm_spe_get_next_packet(decoder);
|
||||
if (err <= 0)
|
||||
return err;
|
||||
|
||||
idx = decoder->packet.index;
|
||||
payload = decoder->packet.payload;
|
||||
|
||||
switch (decoder->packet.type) {
|
||||
case ARM_SPE_TIMESTAMP:
|
||||
decoder->record.timestamp = payload;
|
||||
return 1;
|
||||
case ARM_SPE_END:
|
||||
return 1;
|
||||
case ARM_SPE_ADDRESS:
|
||||
ip = arm_spe_calc_ip(idx, payload);
|
||||
if (idx == SPE_ADDR_PKT_HDR_INDEX_INS)
|
||||
decoder->record.from_ip = ip;
|
||||
else if (idx == SPE_ADDR_PKT_HDR_INDEX_BRANCH)
|
||||
decoder->record.to_ip = ip;
|
||||
break;
|
||||
case ARM_SPE_COUNTER:
|
||||
break;
|
||||
case ARM_SPE_CONTEXT:
|
||||
break;
|
||||
case ARM_SPE_OP_TYPE:
|
||||
break;
|
||||
case ARM_SPE_EVENTS:
|
||||
if (payload & BIT(EV_L1D_REFILL))
|
||||
decoder->record.type |= ARM_SPE_L1D_MISS;
|
||||
|
||||
if (payload & BIT(EV_L1D_ACCESS))
|
||||
decoder->record.type |= ARM_SPE_L1D_ACCESS;
|
||||
|
||||
if (payload & BIT(EV_TLB_WALK))
|
||||
decoder->record.type |= ARM_SPE_TLB_MISS;
|
||||
|
||||
if (payload & BIT(EV_TLB_ACCESS))
|
||||
decoder->record.type |= ARM_SPE_TLB_ACCESS;
|
||||
|
||||
if ((idx == 1 || idx == 2 || idx == 3) &&
|
||||
(payload & BIT(EV_LLC_MISS)))
|
||||
decoder->record.type |= ARM_SPE_LLC_MISS;
|
||||
|
||||
if ((idx == 1 || idx == 2 || idx == 3) &&
|
||||
(payload & BIT(EV_LLC_ACCESS)))
|
||||
decoder->record.type |= ARM_SPE_LLC_ACCESS;
|
||||
|
||||
if ((idx == 1 || idx == 2 || idx == 3) &&
|
||||
(payload & BIT(EV_REMOTE_ACCESS)))
|
||||
decoder->record.type |= ARM_SPE_REMOTE_ACCESS;
|
||||
|
||||
if (payload & BIT(EV_MISPRED))
|
||||
decoder->record.type |= ARM_SPE_BRANCH_MISS;
|
||||
|
||||
break;
|
||||
case ARM_SPE_DATA_SOURCE:
|
||||
break;
|
||||
case ARM_SPE_BAD:
|
||||
break;
|
||||
case ARM_SPE_PAD:
|
||||
break;
|
||||
default:
|
||||
pr_err("Get packet error!\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arm_spe_decode(struct arm_spe_decoder *decoder)
|
||||
{
|
||||
return arm_spe_read_record(decoder);
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* arm_spe_decoder.h: Arm Statistical Profiling Extensions support
|
||||
* Copyright (c) 2019-2020, Arm Ltd.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE__ARM_SPE_DECODER_H__
|
||||
#define INCLUDE__ARM_SPE_DECODER_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "arm-spe-pkt-decoder.h"
|
||||
|
||||
enum arm_spe_events {
|
||||
EV_EXCEPTION_GEN = 0,
|
||||
EV_RETIRED = 1,
|
||||
EV_L1D_ACCESS = 2,
|
||||
EV_L1D_REFILL = 3,
|
||||
EV_TLB_ACCESS = 4,
|
||||
EV_TLB_WALK = 5,
|
||||
EV_NOT_TAKEN = 6,
|
||||
EV_MISPRED = 7,
|
||||
EV_LLC_ACCESS = 8,
|
||||
EV_LLC_MISS = 9,
|
||||
EV_REMOTE_ACCESS = 10,
|
||||
EV_ALIGNMENT = 11,
|
||||
EV_PARTIAL_PREDICATE = 17,
|
||||
EV_EMPTY_PREDICATE = 18,
|
||||
};
|
||||
|
||||
enum arm_spe_sample_type {
|
||||
ARM_SPE_L1D_ACCESS = 1 << 0,
|
||||
ARM_SPE_L1D_MISS = 1 << 1,
|
||||
ARM_SPE_LLC_ACCESS = 1 << 2,
|
||||
ARM_SPE_LLC_MISS = 1 << 3,
|
||||
ARM_SPE_TLB_ACCESS = 1 << 4,
|
||||
ARM_SPE_TLB_MISS = 1 << 5,
|
||||
ARM_SPE_BRANCH_MISS = 1 << 6,
|
||||
ARM_SPE_REMOTE_ACCESS = 1 << 7,
|
||||
};
|
||||
|
||||
struct arm_spe_record {
|
||||
enum arm_spe_sample_type type;
|
||||
int err;
|
||||
u64 from_ip;
|
||||
u64 to_ip;
|
||||
u64 timestamp;
|
||||
};
|
||||
|
||||
struct arm_spe_insn;
|
||||
|
||||
struct arm_spe_buffer {
|
||||
const unsigned char *buf;
|
||||
size_t len;
|
||||
u64 offset;
|
||||
u64 trace_nr;
|
||||
};
|
||||
|
||||
struct arm_spe_params {
|
||||
int (*get_trace)(struct arm_spe_buffer *buffer, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct arm_spe_decoder {
|
||||
int (*get_trace)(struct arm_spe_buffer *buffer, void *data);
|
||||
void *data;
|
||||
struct arm_spe_record record;
|
||||
|
||||
const unsigned char *buf;
|
||||
size_t len;
|
||||
|
||||
struct arm_spe_pkt packet;
|
||||
};
|
||||
|
||||
struct arm_spe_decoder *arm_spe_decoder_new(struct arm_spe_params *params);
|
||||
void arm_spe_decoder_free(struct arm_spe_decoder *decoder);
|
||||
|
||||
int arm_spe_decode(struct arm_spe_decoder *decoder);
|
||||
|
||||
#endif
|
|
@ -15,6 +15,8 @@
|
|||
#define ARM_SPE_NEED_MORE_BYTES -1
|
||||
#define ARM_SPE_BAD_PACKET -2
|
||||
|
||||
#define ARM_SPE_PKT_MAX_SZ 16
|
||||
|
||||
enum arm_spe_pkt_type {
|
||||
ARM_SPE_BAD,
|
||||
ARM_SPE_PAD,
|
||||
|
@ -34,6 +36,20 @@ struct arm_spe_pkt {
|
|||
uint64_t payload;
|
||||
};
|
||||
|
||||
#define SPE_ADDR_PKT_HDR_INDEX_INS (0x0)
|
||||
#define SPE_ADDR_PKT_HDR_INDEX_BRANCH (0x1)
|
||||
#define SPE_ADDR_PKT_HDR_INDEX_DATA_VIRT (0x2)
|
||||
#define SPE_ADDR_PKT_HDR_INDEX_DATA_PHYS (0x3)
|
||||
|
||||
#define SPE_ADDR_PKT_NS BIT(7)
|
||||
#define SPE_ADDR_PKT_CH BIT(6)
|
||||
#define SPE_ADDR_PKT_EL_OFFSET (5)
|
||||
#define SPE_ADDR_PKT_EL_MASK (0x3 << SPE_ADDR_PKT_EL_OFFSET)
|
||||
#define SPE_ADDR_PKT_EL0 (0)
|
||||
#define SPE_ADDR_PKT_EL1 (1)
|
||||
#define SPE_ADDR_PKT_EL2 (2)
|
||||
#define SPE_ADDR_PKT_EL3 (3)
|
||||
|
||||
const char *arm_spe_pkt_name(enum arm_spe_pkt_type);
|
||||
|
||||
int arm_spe_get_packet(const unsigned char *buf, size_t len,
|
|
@ -4,46 +4,85 @@
|
|||
* Copyright (c) 2017-2018, Arm Ltd.
|
||||
*/
|
||||
|
||||
#include <byteswap.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <byteswap.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/zalloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "auxtrace.h"
|
||||
#include "color.h"
|
||||
#include "debug.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "machine.h"
|
||||
#include "session.h"
|
||||
#include "debug.h"
|
||||
#include "auxtrace.h"
|
||||
#include "symbol.h"
|
||||
#include "thread.h"
|
||||
#include "thread-stack.h"
|
||||
#include "tool.h"
|
||||
#include "util/synthetic-events.h"
|
||||
|
||||
#include "arm-spe.h"
|
||||
#include "arm-spe-pkt-decoder.h"
|
||||
#include "arm-spe-decoder/arm-spe-decoder.h"
|
||||
#include "arm-spe-decoder/arm-spe-pkt-decoder.h"
|
||||
|
||||
#define MAX_TIMESTAMP (~0ULL)
|
||||
|
||||
struct arm_spe {
|
||||
struct auxtrace auxtrace;
|
||||
struct auxtrace_queues queues;
|
||||
struct auxtrace_heap heap;
|
||||
struct itrace_synth_opts synth_opts;
|
||||
u32 auxtrace_type;
|
||||
struct perf_session *session;
|
||||
struct machine *machine;
|
||||
u32 pmu_type;
|
||||
|
||||
u8 timeless_decoding;
|
||||
u8 data_queued;
|
||||
|
||||
u8 sample_flc;
|
||||
u8 sample_llc;
|
||||
u8 sample_tlb;
|
||||
u8 sample_branch;
|
||||
u8 sample_remote_access;
|
||||
|
||||
u64 l1d_miss_id;
|
||||
u64 l1d_access_id;
|
||||
u64 llc_miss_id;
|
||||
u64 llc_access_id;
|
||||
u64 tlb_miss_id;
|
||||
u64 tlb_access_id;
|
||||
u64 branch_miss_id;
|
||||
u64 remote_access_id;
|
||||
|
||||
u64 kernel_start;
|
||||
|
||||
unsigned long num_events;
|
||||
};
|
||||
|
||||
struct arm_spe_queue {
|
||||
struct arm_spe *spe;
|
||||
unsigned int queue_nr;
|
||||
struct auxtrace_buffer *buffer;
|
||||
bool on_heap;
|
||||
bool done;
|
||||
pid_t pid;
|
||||
pid_t tid;
|
||||
int cpu;
|
||||
struct arm_spe *spe;
|
||||
unsigned int queue_nr;
|
||||
struct auxtrace_buffer *buffer;
|
||||
struct auxtrace_buffer *old_buffer;
|
||||
union perf_event *event_buf;
|
||||
bool on_heap;
|
||||
bool done;
|
||||
pid_t pid;
|
||||
pid_t tid;
|
||||
int cpu;
|
||||
struct arm_spe_decoder *decoder;
|
||||
u64 time;
|
||||
u64 timestamp;
|
||||
struct thread *thread;
|
||||
};
|
||||
|
||||
static void arm_spe_dump(struct arm_spe *spe __maybe_unused,
|
||||
|
@ -92,44 +131,520 @@ static void arm_spe_dump_event(struct arm_spe *spe, unsigned char *buf,
|
|||
arm_spe_dump(spe, buf, len);
|
||||
}
|
||||
|
||||
static int arm_spe_process_event(struct perf_session *session __maybe_unused,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct perf_tool *tool __maybe_unused)
|
||||
static int arm_spe_get_trace(struct arm_spe_buffer *b, void *data)
|
||||
{
|
||||
struct arm_spe_queue *speq = data;
|
||||
struct auxtrace_buffer *buffer = speq->buffer;
|
||||
struct auxtrace_buffer *old_buffer = speq->old_buffer;
|
||||
struct auxtrace_queue *queue;
|
||||
|
||||
queue = &speq->spe->queues.queue_array[speq->queue_nr];
|
||||
|
||||
buffer = auxtrace_buffer__next(queue, buffer);
|
||||
/* If no more data, drop the previous auxtrace_buffer and return */
|
||||
if (!buffer) {
|
||||
if (old_buffer)
|
||||
auxtrace_buffer__drop_data(old_buffer);
|
||||
b->len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
speq->buffer = buffer;
|
||||
|
||||
/* If the aux_buffer doesn't have data associated, try to load it */
|
||||
if (!buffer->data) {
|
||||
/* get the file desc associated with the perf data file */
|
||||
int fd = perf_data__fd(speq->spe->session->data);
|
||||
|
||||
buffer->data = auxtrace_buffer__get_data(buffer, fd);
|
||||
if (!buffer->data)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
b->len = buffer->size;
|
||||
b->buf = buffer->data;
|
||||
|
||||
if (b->len) {
|
||||
if (old_buffer)
|
||||
auxtrace_buffer__drop_data(old_buffer);
|
||||
speq->old_buffer = buffer;
|
||||
} else {
|
||||
auxtrace_buffer__drop_data(buffer);
|
||||
return arm_spe_get_trace(b, data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct arm_spe_queue *arm_spe__alloc_queue(struct arm_spe *spe,
|
||||
unsigned int queue_nr)
|
||||
{
|
||||
struct arm_spe_params params = { .get_trace = 0, };
|
||||
struct arm_spe_queue *speq;
|
||||
|
||||
speq = zalloc(sizeof(*speq));
|
||||
if (!speq)
|
||||
return NULL;
|
||||
|
||||
speq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE);
|
||||
if (!speq->event_buf)
|
||||
goto out_free;
|
||||
|
||||
speq->spe = spe;
|
||||
speq->queue_nr = queue_nr;
|
||||
speq->pid = -1;
|
||||
speq->tid = -1;
|
||||
speq->cpu = -1;
|
||||
|
||||
/* params set */
|
||||
params.get_trace = arm_spe_get_trace;
|
||||
params.data = speq;
|
||||
|
||||
/* create new decoder */
|
||||
speq->decoder = arm_spe_decoder_new(¶ms);
|
||||
if (!speq->decoder)
|
||||
goto out_free;
|
||||
|
||||
return speq;
|
||||
|
||||
out_free:
|
||||
zfree(&speq->event_buf);
|
||||
free(speq);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline u8 arm_spe_cpumode(struct arm_spe *spe, u64 ip)
|
||||
{
|
||||
return ip >= spe->kernel_start ?
|
||||
PERF_RECORD_MISC_KERNEL :
|
||||
PERF_RECORD_MISC_USER;
|
||||
}
|
||||
|
||||
static void arm_spe_prep_sample(struct arm_spe *spe,
|
||||
struct arm_spe_queue *speq,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct arm_spe_record *record = &speq->decoder->record;
|
||||
|
||||
if (!spe->timeless_decoding)
|
||||
sample->time = speq->timestamp;
|
||||
|
||||
sample->ip = record->from_ip;
|
||||
sample->cpumode = arm_spe_cpumode(spe, sample->ip);
|
||||
sample->pid = speq->pid;
|
||||
sample->tid = speq->tid;
|
||||
sample->addr = record->to_ip;
|
||||
sample->period = 1;
|
||||
sample->cpu = speq->cpu;
|
||||
|
||||
event->sample.header.type = PERF_RECORD_SAMPLE;
|
||||
event->sample.header.misc = sample->cpumode;
|
||||
event->sample.header.size = sizeof(struct perf_event_header);
|
||||
}
|
||||
|
||||
static inline int
|
||||
arm_spe_deliver_synth_event(struct arm_spe *spe,
|
||||
struct arm_spe_queue *speq __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = perf_session__deliver_synth_event(spe->session, event, sample);
|
||||
if (ret)
|
||||
pr_err("ARM SPE: failed to deliver event, error %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
arm_spe_synth_spe_events_sample(struct arm_spe_queue *speq,
|
||||
u64 spe_events_id)
|
||||
{
|
||||
struct arm_spe *spe = speq->spe;
|
||||
union perf_event *event = speq->event_buf;
|
||||
struct perf_sample sample = { .ip = 0, };
|
||||
|
||||
arm_spe_prep_sample(spe, speq, event, &sample);
|
||||
|
||||
sample.id = spe_events_id;
|
||||
sample.stream_id = spe_events_id;
|
||||
|
||||
return arm_spe_deliver_synth_event(spe, speq, event, &sample);
|
||||
}
|
||||
|
||||
static int arm_spe_sample(struct arm_spe_queue *speq)
|
||||
{
|
||||
const struct arm_spe_record *record = &speq->decoder->record;
|
||||
struct arm_spe *spe = speq->spe;
|
||||
int err;
|
||||
|
||||
if (spe->sample_flc) {
|
||||
if (record->type & ARM_SPE_L1D_MISS) {
|
||||
err = arm_spe_synth_spe_events_sample(
|
||||
speq, spe->l1d_miss_id);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (record->type & ARM_SPE_L1D_ACCESS) {
|
||||
err = arm_spe_synth_spe_events_sample(
|
||||
speq, spe->l1d_access_id);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (spe->sample_llc) {
|
||||
if (record->type & ARM_SPE_LLC_MISS) {
|
||||
err = arm_spe_synth_spe_events_sample(
|
||||
speq, spe->llc_miss_id);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (record->type & ARM_SPE_LLC_ACCESS) {
|
||||
err = arm_spe_synth_spe_events_sample(
|
||||
speq, spe->llc_access_id);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (spe->sample_tlb) {
|
||||
if (record->type & ARM_SPE_TLB_MISS) {
|
||||
err = arm_spe_synth_spe_events_sample(
|
||||
speq, spe->tlb_miss_id);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (record->type & ARM_SPE_TLB_ACCESS) {
|
||||
err = arm_spe_synth_spe_events_sample(
|
||||
speq, spe->tlb_access_id);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (spe->sample_branch && (record->type & ARM_SPE_BRANCH_MISS)) {
|
||||
err = arm_spe_synth_spe_events_sample(speq,
|
||||
spe->branch_miss_id);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (spe->sample_remote_access &&
|
||||
(record->type & ARM_SPE_REMOTE_ACCESS)) {
|
||||
err = arm_spe_synth_spe_events_sample(speq,
|
||||
spe->remote_access_id);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arm_spe_run_decoder(struct arm_spe_queue *speq, u64 *timestamp)
|
||||
{
|
||||
struct arm_spe *spe = speq->spe;
|
||||
int ret;
|
||||
|
||||
if (!spe->kernel_start)
|
||||
spe->kernel_start = machine__kernel_start(spe->machine);
|
||||
|
||||
while (1) {
|
||||
ret = arm_spe_decode(speq->decoder);
|
||||
if (!ret) {
|
||||
pr_debug("No data or all data has been processed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Error is detected when decode SPE trace data, continue to
|
||||
* the next trace data and find out more records.
|
||||
*/
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
ret = arm_spe_sample(speq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!spe->timeless_decoding && speq->timestamp >= *timestamp) {
|
||||
*timestamp = speq->timestamp;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arm_spe__setup_queue(struct arm_spe *spe,
|
||||
struct auxtrace_queue *queue,
|
||||
unsigned int queue_nr)
|
||||
{
|
||||
struct arm_spe_queue *speq = queue->priv;
|
||||
struct arm_spe_record *record;
|
||||
|
||||
if (list_empty(&queue->head) || speq)
|
||||
return 0;
|
||||
|
||||
speq = arm_spe__alloc_queue(spe, queue_nr);
|
||||
|
||||
if (!speq)
|
||||
return -ENOMEM;
|
||||
|
||||
queue->priv = speq;
|
||||
|
||||
if (queue->cpu != -1)
|
||||
speq->cpu = queue->cpu;
|
||||
|
||||
if (!speq->on_heap) {
|
||||
int ret;
|
||||
|
||||
if (spe->timeless_decoding)
|
||||
return 0;
|
||||
|
||||
retry:
|
||||
ret = arm_spe_decode(speq->decoder);
|
||||
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
if (ret < 0)
|
||||
goto retry;
|
||||
|
||||
record = &speq->decoder->record;
|
||||
|
||||
speq->timestamp = record->timestamp;
|
||||
ret = auxtrace_heap__add(&spe->heap, queue_nr, speq->timestamp);
|
||||
if (ret)
|
||||
return ret;
|
||||
speq->on_heap = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arm_spe__setup_queues(struct arm_spe *spe)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < spe->queues.nr_queues; i++) {
|
||||
ret = arm_spe__setup_queue(spe, &spe->queues.queue_array[i], i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arm_spe__update_queues(struct arm_spe *spe)
|
||||
{
|
||||
if (spe->queues.new_data) {
|
||||
spe->queues.new_data = false;
|
||||
return arm_spe__setup_queues(spe);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool arm_spe__is_timeless_decoding(struct arm_spe *spe)
|
||||
{
|
||||
struct evsel *evsel;
|
||||
struct evlist *evlist = spe->session->evlist;
|
||||
bool timeless_decoding = true;
|
||||
|
||||
/*
|
||||
* Circle through the list of event and complain if we find one
|
||||
* with the time bit set.
|
||||
*/
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
if ((evsel->core.attr.sample_type & PERF_SAMPLE_TIME))
|
||||
timeless_decoding = false;
|
||||
}
|
||||
|
||||
return timeless_decoding;
|
||||
}
|
||||
|
||||
static void arm_spe_set_pid_tid_cpu(struct arm_spe *spe,
|
||||
struct auxtrace_queue *queue)
|
||||
{
|
||||
struct arm_spe_queue *speq = queue->priv;
|
||||
pid_t tid;
|
||||
|
||||
tid = machine__get_current_tid(spe->machine, speq->cpu);
|
||||
if (tid != -1) {
|
||||
speq->tid = tid;
|
||||
thread__zput(speq->thread);
|
||||
} else
|
||||
speq->tid = queue->tid;
|
||||
|
||||
if ((!speq->thread) && (speq->tid != -1)) {
|
||||
speq->thread = machine__find_thread(spe->machine, -1,
|
||||
speq->tid);
|
||||
}
|
||||
|
||||
if (speq->thread) {
|
||||
speq->pid = speq->thread->pid_;
|
||||
if (queue->cpu == -1)
|
||||
speq->cpu = speq->thread->cpu;
|
||||
}
|
||||
}
|
||||
|
||||
static int arm_spe_process_queues(struct arm_spe *spe, u64 timestamp)
|
||||
{
|
||||
unsigned int queue_nr;
|
||||
u64 ts;
|
||||
int ret;
|
||||
|
||||
while (1) {
|
||||
struct auxtrace_queue *queue;
|
||||
struct arm_spe_queue *speq;
|
||||
|
||||
if (!spe->heap.heap_cnt)
|
||||
return 0;
|
||||
|
||||
if (spe->heap.heap_array[0].ordinal >= timestamp)
|
||||
return 0;
|
||||
|
||||
queue_nr = spe->heap.heap_array[0].queue_nr;
|
||||
queue = &spe->queues.queue_array[queue_nr];
|
||||
speq = queue->priv;
|
||||
|
||||
auxtrace_heap__pop(&spe->heap);
|
||||
|
||||
if (spe->heap.heap_cnt) {
|
||||
ts = spe->heap.heap_array[0].ordinal + 1;
|
||||
if (ts > timestamp)
|
||||
ts = timestamp;
|
||||
} else {
|
||||
ts = timestamp;
|
||||
}
|
||||
|
||||
arm_spe_set_pid_tid_cpu(spe, queue);
|
||||
|
||||
ret = arm_spe_run_decoder(speq, &ts);
|
||||
if (ret < 0) {
|
||||
auxtrace_heap__add(&spe->heap, queue_nr, ts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
ret = auxtrace_heap__add(&spe->heap, queue_nr, ts);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
speq->on_heap = false;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arm_spe_process_timeless_queues(struct arm_spe *spe, pid_t tid,
|
||||
u64 time_)
|
||||
{
|
||||
struct auxtrace_queues *queues = &spe->queues;
|
||||
unsigned int i;
|
||||
u64 ts = 0;
|
||||
|
||||
for (i = 0; i < queues->nr_queues; i++) {
|
||||
struct auxtrace_queue *queue = &spe->queues.queue_array[i];
|
||||
struct arm_spe_queue *speq = queue->priv;
|
||||
|
||||
if (speq && (tid == -1 || speq->tid == tid)) {
|
||||
speq->time = time_;
|
||||
arm_spe_set_pid_tid_cpu(spe, queue);
|
||||
arm_spe_run_decoder(speq, &ts);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arm_spe_process_event(struct perf_session *session,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_tool *tool)
|
||||
{
|
||||
int err = 0;
|
||||
u64 timestamp;
|
||||
struct arm_spe *spe = container_of(session->auxtrace,
|
||||
struct arm_spe, auxtrace);
|
||||
|
||||
if (dump_trace)
|
||||
return 0;
|
||||
|
||||
if (!tool->ordered_events) {
|
||||
pr_err("SPE trace requires ordered events\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (sample->time && (sample->time != (u64) -1))
|
||||
timestamp = sample->time;
|
||||
else
|
||||
timestamp = 0;
|
||||
|
||||
if (timestamp || spe->timeless_decoding) {
|
||||
err = arm_spe__update_queues(spe);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (spe->timeless_decoding) {
|
||||
if (event->header.type == PERF_RECORD_EXIT) {
|
||||
err = arm_spe_process_timeless_queues(spe,
|
||||
event->fork.tid,
|
||||
sample->time);
|
||||
}
|
||||
} else if (timestamp) {
|
||||
if (event->header.type == PERF_RECORD_EXIT) {
|
||||
err = arm_spe_process_queues(spe, timestamp);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int arm_spe_process_auxtrace_event(struct perf_session *session,
|
||||
union perf_event *event,
|
||||
struct perf_tool *tool __maybe_unused)
|
||||
{
|
||||
struct arm_spe *spe = container_of(session->auxtrace, struct arm_spe,
|
||||
auxtrace);
|
||||
struct auxtrace_buffer *buffer;
|
||||
off_t data_offset;
|
||||
int fd = perf_data__fd(session->data);
|
||||
int err;
|
||||
|
||||
if (perf_data__is_pipe(session->data)) {
|
||||
data_offset = 0;
|
||||
} else {
|
||||
data_offset = lseek(fd, 0, SEEK_CUR);
|
||||
if (data_offset == -1)
|
||||
return -errno;
|
||||
}
|
||||
if (!spe->data_queued) {
|
||||
struct auxtrace_buffer *buffer;
|
||||
off_t data_offset;
|
||||
int fd = perf_data__fd(session->data);
|
||||
int err;
|
||||
|
||||
err = auxtrace_queues__add_event(&spe->queues, session, event,
|
||||
data_offset, &buffer);
|
||||
if (err)
|
||||
return err;
|
||||
if (perf_data__is_pipe(session->data)) {
|
||||
data_offset = 0;
|
||||
} else {
|
||||
data_offset = lseek(fd, 0, SEEK_CUR);
|
||||
if (data_offset == -1)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* Dump here now we have copied a piped trace out of the pipe */
|
||||
if (dump_trace) {
|
||||
if (auxtrace_buffer__get_data(buffer, fd)) {
|
||||
arm_spe_dump_event(spe, buffer->data,
|
||||
buffer->size);
|
||||
auxtrace_buffer__put_data(buffer);
|
||||
err = auxtrace_queues__add_event(&spe->queues, session, event,
|
||||
data_offset, &buffer);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Dump here now we have copied a piped trace out of the pipe */
|
||||
if (dump_trace) {
|
||||
if (auxtrace_buffer__get_data(buffer, fd)) {
|
||||
arm_spe_dump_event(spe, buffer->data,
|
||||
buffer->size);
|
||||
auxtrace_buffer__put_data(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,7 +654,25 @@ static int arm_spe_process_auxtrace_event(struct perf_session *session,
|
|||
static int arm_spe_flush(struct perf_session *session __maybe_unused,
|
||||
struct perf_tool *tool __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
struct arm_spe *spe = container_of(session->auxtrace, struct arm_spe,
|
||||
auxtrace);
|
||||
int ret;
|
||||
|
||||
if (dump_trace)
|
||||
return 0;
|
||||
|
||||
if (!tool->ordered_events)
|
||||
return -EINVAL;
|
||||
|
||||
ret = arm_spe__update_queues(spe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (spe->timeless_decoding)
|
||||
return arm_spe_process_timeless_queues(spe, -1,
|
||||
MAX_TIMESTAMP - 1);
|
||||
|
||||
return arm_spe_process_queues(spe, MAX_TIMESTAMP);
|
||||
}
|
||||
|
||||
static void arm_spe_free_queue(void *priv)
|
||||
|
@ -148,6 +681,9 @@ static void arm_spe_free_queue(void *priv)
|
|||
|
||||
if (!speq)
|
||||
return;
|
||||
thread__zput(speq->thread);
|
||||
arm_spe_decoder_free(speq->decoder);
|
||||
zfree(&speq->event_buf);
|
||||
free(speq);
|
||||
}
|
||||
|
||||
|
@ -196,11 +732,189 @@ static void arm_spe_print_info(__u64 *arr)
|
|||
fprintf(stdout, arm_spe_info_fmts[ARM_SPE_PMU_TYPE], arr[ARM_SPE_PMU_TYPE]);
|
||||
}
|
||||
|
||||
struct arm_spe_synth {
|
||||
struct perf_tool dummy_tool;
|
||||
struct perf_session *session;
|
||||
};
|
||||
|
||||
static int arm_spe_event_synth(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
{
|
||||
struct arm_spe_synth *arm_spe_synth =
|
||||
container_of(tool, struct arm_spe_synth, dummy_tool);
|
||||
|
||||
return perf_session__deliver_synth_event(arm_spe_synth->session,
|
||||
event, NULL);
|
||||
}
|
||||
|
||||
static int arm_spe_synth_event(struct perf_session *session,
|
||||
struct perf_event_attr *attr, u64 id)
|
||||
{
|
||||
struct arm_spe_synth arm_spe_synth;
|
||||
|
||||
memset(&arm_spe_synth, 0, sizeof(struct arm_spe_synth));
|
||||
arm_spe_synth.session = session;
|
||||
|
||||
return perf_event__synthesize_attr(&arm_spe_synth.dummy_tool, attr, 1,
|
||||
&id, arm_spe_event_synth);
|
||||
}
|
||||
|
||||
static void arm_spe_set_event_name(struct evlist *evlist, u64 id,
|
||||
const char *name)
|
||||
{
|
||||
struct evsel *evsel;
|
||||
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
if (evsel->core.id && evsel->core.id[0] == id) {
|
||||
if (evsel->name)
|
||||
zfree(&evsel->name);
|
||||
evsel->name = strdup(name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session)
|
||||
{
|
||||
struct evlist *evlist = session->evlist;
|
||||
struct evsel *evsel;
|
||||
struct perf_event_attr attr;
|
||||
bool found = false;
|
||||
u64 id;
|
||||
int err;
|
||||
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
if (evsel->core.attr.type == spe->pmu_type) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
pr_debug("No selected events with SPE trace data\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&attr, 0, sizeof(struct perf_event_attr));
|
||||
attr.size = sizeof(struct perf_event_attr);
|
||||
attr.type = PERF_TYPE_HARDWARE;
|
||||
attr.sample_type = evsel->core.attr.sample_type & PERF_SAMPLE_MASK;
|
||||
attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID |
|
||||
PERF_SAMPLE_PERIOD;
|
||||
if (spe->timeless_decoding)
|
||||
attr.sample_type &= ~(u64)PERF_SAMPLE_TIME;
|
||||
else
|
||||
attr.sample_type |= PERF_SAMPLE_TIME;
|
||||
|
||||
attr.exclude_user = evsel->core.attr.exclude_user;
|
||||
attr.exclude_kernel = evsel->core.attr.exclude_kernel;
|
||||
attr.exclude_hv = evsel->core.attr.exclude_hv;
|
||||
attr.exclude_host = evsel->core.attr.exclude_host;
|
||||
attr.exclude_guest = evsel->core.attr.exclude_guest;
|
||||
attr.sample_id_all = evsel->core.attr.sample_id_all;
|
||||
attr.read_format = evsel->core.attr.read_format;
|
||||
|
||||
/* create new id val to be a fixed offset from evsel id */
|
||||
id = evsel->core.id[0] + 1000000000;
|
||||
|
||||
if (!id)
|
||||
id = 1;
|
||||
|
||||
if (spe->synth_opts.flc) {
|
||||
spe->sample_flc = true;
|
||||
|
||||
/* Level 1 data cache miss */
|
||||
err = arm_spe_synth_event(session, &attr, id);
|
||||
if (err)
|
||||
return err;
|
||||
spe->l1d_miss_id = id;
|
||||
arm_spe_set_event_name(evlist, id, "l1d-miss");
|
||||
id += 1;
|
||||
|
||||
/* Level 1 data cache access */
|
||||
err = arm_spe_synth_event(session, &attr, id);
|
||||
if (err)
|
||||
return err;
|
||||
spe->l1d_access_id = id;
|
||||
arm_spe_set_event_name(evlist, id, "l1d-access");
|
||||
id += 1;
|
||||
}
|
||||
|
||||
if (spe->synth_opts.llc) {
|
||||
spe->sample_llc = true;
|
||||
|
||||
/* Last level cache miss */
|
||||
err = arm_spe_synth_event(session, &attr, id);
|
||||
if (err)
|
||||
return err;
|
||||
spe->llc_miss_id = id;
|
||||
arm_spe_set_event_name(evlist, id, "llc-miss");
|
||||
id += 1;
|
||||
|
||||
/* Last level cache access */
|
||||
err = arm_spe_synth_event(session, &attr, id);
|
||||
if (err)
|
||||
return err;
|
||||
spe->llc_access_id = id;
|
||||
arm_spe_set_event_name(evlist, id, "llc-access");
|
||||
id += 1;
|
||||
}
|
||||
|
||||
if (spe->synth_opts.tlb) {
|
||||
spe->sample_tlb = true;
|
||||
|
||||
/* TLB miss */
|
||||
err = arm_spe_synth_event(session, &attr, id);
|
||||
if (err)
|
||||
return err;
|
||||
spe->tlb_miss_id = id;
|
||||
arm_spe_set_event_name(evlist, id, "tlb-miss");
|
||||
id += 1;
|
||||
|
||||
/* TLB access */
|
||||
err = arm_spe_synth_event(session, &attr, id);
|
||||
if (err)
|
||||
return err;
|
||||
spe->tlb_access_id = id;
|
||||
arm_spe_set_event_name(evlist, id, "tlb-access");
|
||||
id += 1;
|
||||
}
|
||||
|
||||
if (spe->synth_opts.branches) {
|
||||
spe->sample_branch = true;
|
||||
|
||||
/* Branch miss */
|
||||
err = arm_spe_synth_event(session, &attr, id);
|
||||
if (err)
|
||||
return err;
|
||||
spe->branch_miss_id = id;
|
||||
arm_spe_set_event_name(evlist, id, "branch-miss");
|
||||
id += 1;
|
||||
}
|
||||
|
||||
if (spe->synth_opts.remote_access) {
|
||||
spe->sample_remote_access = true;
|
||||
|
||||
/* Remote access */
|
||||
err = arm_spe_synth_event(session, &attr, id);
|
||||
if (err)
|
||||
return err;
|
||||
spe->remote_access_id = id;
|
||||
arm_spe_set_event_name(evlist, id, "remote-access");
|
||||
id += 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arm_spe_process_auxtrace_info(union perf_event *event,
|
||||
struct perf_session *session)
|
||||
{
|
||||
struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
|
||||
size_t min_sz = sizeof(u64) * ARM_SPE_PMU_TYPE;
|
||||
size_t min_sz = sizeof(u64) * ARM_SPE_AUXTRACE_PRIV_MAX;
|
||||
struct arm_spe *spe;
|
||||
int err;
|
||||
|
||||
|
@ -221,6 +935,7 @@ int arm_spe_process_auxtrace_info(union perf_event *event,
|
|||
spe->auxtrace_type = auxtrace_info->type;
|
||||
spe->pmu_type = auxtrace_info->priv[ARM_SPE_PMU_TYPE];
|
||||
|
||||
spe->timeless_decoding = arm_spe__is_timeless_decoding(spe);
|
||||
spe->auxtrace.process_event = arm_spe_process_event;
|
||||
spe->auxtrace.process_auxtrace_event = arm_spe_process_auxtrace_event;
|
||||
spe->auxtrace.flush_events = arm_spe_flush;
|
||||
|
@ -231,8 +946,30 @@ int arm_spe_process_auxtrace_info(union perf_event *event,
|
|||
|
||||
arm_spe_print_info(&auxtrace_info->priv[0]);
|
||||
|
||||
if (dump_trace)
|
||||
return 0;
|
||||
|
||||
if (session->itrace_synth_opts && session->itrace_synth_opts->set)
|
||||
spe->synth_opts = *session->itrace_synth_opts;
|
||||
else
|
||||
itrace_synth_opts__set_default(&spe->synth_opts, false);
|
||||
|
||||
err = arm_spe_synth_events(spe, session);
|
||||
if (err)
|
||||
goto err_free_queues;
|
||||
|
||||
err = auxtrace_queues__process_index(&spe->queues, session);
|
||||
if (err)
|
||||
goto err_free_queues;
|
||||
|
||||
if (spe->queues.populated)
|
||||
spe->data_queued = true;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_queues:
|
||||
auxtrace_queues__free(&spe->queues);
|
||||
session->auxtrace = NULL;
|
||||
err_free:
|
||||
free(spe);
|
||||
return err;
|
||||
|
|
|
@ -55,7 +55,6 @@
|
|||
#include "util/mmap.h"
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/kernel.h>
|
||||
#include "symbol/kallsyms.h"
|
||||
#include <internal/lib.h>
|
||||
|
||||
|
@ -729,7 +728,7 @@ int auxtrace_parse_sample_options(struct auxtrace_record *itr,
|
|||
struct evlist *evlist,
|
||||
struct record_opts *opts, const char *str)
|
||||
{
|
||||
struct perf_evsel_config_term *term;
|
||||
struct evsel_config_term *term;
|
||||
struct evsel *aux_evsel;
|
||||
bool has_aux_sample_size = false;
|
||||
bool has_aux_leader = false;
|
||||
|
@ -771,7 +770,7 @@ no_opt:
|
|||
evlist__for_each_entry(evlist, evsel) {
|
||||
if (evsel__is_aux_event(evsel))
|
||||
aux_evsel = evsel;
|
||||
term = perf_evsel__get_config_term(evsel, AUX_SAMPLE_SIZE);
|
||||
term = evsel__get_config_term(evsel, AUX_SAMPLE_SIZE);
|
||||
if (term) {
|
||||
has_aux_sample_size = true;
|
||||
evsel->core.attr.aux_sample_size = term->val.aux_sample_size;
|
||||
|
@ -1331,6 +1330,11 @@ void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts,
|
|||
synth_opts->pwr_events = true;
|
||||
synth_opts->other_events = true;
|
||||
synth_opts->errors = true;
|
||||
synth_opts->flc = true;
|
||||
synth_opts->llc = true;
|
||||
synth_opts->tlb = true;
|
||||
synth_opts->remote_access = true;
|
||||
|
||||
if (no_sample) {
|
||||
synth_opts->period_type = PERF_ITRACE_PERIOD_INSTRUCTIONS;
|
||||
synth_opts->period = 1;
|
||||
|
@ -1491,6 +1495,18 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
|
|||
goto out_err;
|
||||
p = endptr;
|
||||
break;
|
||||
case 'f':
|
||||
synth_opts->flc = true;
|
||||
break;
|
||||
case 'm':
|
||||
synth_opts->llc = true;
|
||||
break;
|
||||
case 't':
|
||||
synth_opts->tlb = true;
|
||||
break;
|
||||
case 'a':
|
||||
synth_opts->remote_access = true;
|
||||
break;
|
||||
case ' ':
|
||||
case ',':
|
||||
break;
|
||||
|
|
|
@ -63,6 +63,7 @@ enum itrace_period_type {
|
|||
* because 'perf inject' will write it out
|
||||
* @instructions: whether to synthesize 'instructions' events
|
||||
* @branches: whether to synthesize 'branches' events
|
||||
* (branch misses only for Arm SPE)
|
||||
* @transactions: whether to synthesize events for transactions
|
||||
* @ptwrites: whether to synthesize events for ptwrites
|
||||
* @pwr_events: whether to synthesize power events
|
||||
|
@ -78,6 +79,10 @@ enum itrace_period_type {
|
|||
* @thread_stack: feed branches to the thread_stack
|
||||
* @last_branch: add branch context to 'instruction' events
|
||||
* @add_last_branch: add branch context to existing event records
|
||||
* @flc: whether to synthesize first level cache events
|
||||
* @llc: whether to synthesize last level cache events
|
||||
* @tlb: whether to synthesize TLB events
|
||||
* @remote_access: whether to synthesize remote access events
|
||||
* @callchain_sz: maximum callchain size
|
||||
* @last_branch_sz: branch context size
|
||||
* @period: 'instructions' events period
|
||||
|
@ -107,6 +112,10 @@ struct itrace_synth_opts {
|
|||
bool thread_stack;
|
||||
bool last_branch;
|
||||
bool add_last_branch;
|
||||
bool flc;
|
||||
bool llc;
|
||||
bool tlb;
|
||||
bool remote_access;
|
||||
unsigned int callchain_sz;
|
||||
unsigned int last_branch_sz;
|
||||
unsigned long long period;
|
||||
|
@ -596,7 +605,7 @@ bool auxtrace__evsel_is_auxtrace(struct perf_session *session,
|
|||
|
||||
#define ITRACE_HELP \
|
||||
" i: synthesize instructions events\n" \
|
||||
" b: synthesize branches events\n" \
|
||||
" b: synthesize branches events (branch misses for Arm SPE)\n" \
|
||||
" c: synthesize branches events (calls only)\n" \
|
||||
" r: synthesize branches events (returns only)\n" \
|
||||
" x: synthesize transactions events\n" \
|
||||
|
@ -604,6 +613,10 @@ bool auxtrace__evsel_is_auxtrace(struct perf_session *session,
|
|||
" p: synthesize power events\n" \
|
||||
" e: synthesize error events\n" \
|
||||
" d: create a debug log\n" \
|
||||
" f: synthesize first level cache events\n" \
|
||||
" m: synthesize last level cache events\n" \
|
||||
" t: synthesize TLB events\n" \
|
||||
" a: synthesize remote access events\n" \
|
||||
" g[len]: synthesize a call chain (use with i or x)\n" \
|
||||
" l[len]: synthesize last branch entries (use with i or x)\n" \
|
||||
" sNUMBER: skip initial number of events\n" \
|
||||
|
|
|
@ -1225,7 +1225,7 @@ bpf__obj_config_map(struct bpf_object *obj,
|
|||
out:
|
||||
free(map_name);
|
||||
if (!err)
|
||||
key_scan_pos += strlen(map_opt);
|
||||
*key_scan_pos += strlen(map_opt);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ struct branch_entry {
|
|||
struct branch_stack {
|
||||
u64 nr;
|
||||
u64 hw_idx;
|
||||
struct branch_entry entries[0];
|
||||
struct branch_entry entries[];
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -1599,3 +1599,17 @@ void callchain_cursor_reset(struct callchain_cursor *cursor)
|
|||
for (node = cursor->first; node != NULL; node = node->next)
|
||||
map__zput(node->ms.map);
|
||||
}
|
||||
|
||||
void callchain_param_setup(u64 sample_type)
|
||||
{
|
||||
if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) {
|
||||
if ((sample_type & PERF_SAMPLE_REGS_USER) &&
|
||||
(sample_type & PERF_SAMPLE_STACK_USER)) {
|
||||
callchain_param.record_mode = CALLCHAIN_DWARF;
|
||||
dwarf_callchain_users = true;
|
||||
} else if (sample_type & PERF_SAMPLE_BRANCH_STACK)
|
||||
callchain_param.record_mode = CALLCHAIN_LBR;
|
||||
else
|
||||
callchain_param.record_mode = CALLCHAIN_FP;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -297,4 +297,5 @@ int callchain_branch_counts(struct callchain_root *root,
|
|||
u64 *branch_count, u64 *predicted_count,
|
||||
u64 *abort_count, u64 *cycles_count);
|
||||
|
||||
void callchain_param_setup(u64 sample_type);
|
||||
#endif /* __PERF_CALLCHAIN_H */
|
||||
|
|
|
@ -65,7 +65,7 @@ static int perf_flag_probe(void)
|
|||
return 1;
|
||||
}
|
||||
|
||||
WARN_ONCE(err != EINVAL && err != EBUSY,
|
||||
WARN_ONCE(err != EINVAL && err != EBUSY && err != EACCES,
|
||||
"perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n",
|
||||
err, str_error_r(err, sbuf, sizeof(sbuf)));
|
||||
|
||||
|
@ -83,7 +83,7 @@ static int perf_flag_probe(void)
|
|||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
if (WARN_ONCE(fd < 0 && err != EBUSY,
|
||||
if (WARN_ONCE(fd < 0 && err != EBUSY && err != EACCES,
|
||||
"perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n",
|
||||
err, str_error_r(err, sbuf, sizeof(sbuf))))
|
||||
return -1;
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
#include "util/event.h" /* proc_map_timeout */
|
||||
#include "util/hist.h" /* perf_hist_config */
|
||||
#include "util/llvm-utils.h" /* perf_llvm_config */
|
||||
#include "util/stat.h" /* perf_stat__set_big_num */
|
||||
#include "build-id.h"
|
||||
#include "debug.h"
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -452,6 +452,15 @@ static int perf_ui_config(const char *var, const char *value)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int perf_stat_config(const char *var, const char *value)
|
||||
{
|
||||
if (!strcmp(var, "stat.big-num"))
|
||||
perf_stat__set_big_num(perf_config_bool(var, value));
|
||||
|
||||
/* Add other config variables here. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_default_config(const char *var, const char *value,
|
||||
void *dummy __maybe_unused)
|
||||
{
|
||||
|
@ -473,6 +482,9 @@ int perf_default_config(const char *var, const char *value,
|
|||
if (strstarts(var, "buildid."))
|
||||
return perf_buildid_config(var, value);
|
||||
|
||||
if (strstarts(var, "stat."))
|
||||
return perf_stat_config(var, value);
|
||||
|
||||
/* Add other config variables here. */
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "evsel.h"
|
||||
#include "counts.h"
|
||||
#include <linux/zalloc.h>
|
||||
|
@ -42,24 +43,25 @@ void perf_counts__delete(struct perf_counts *counts)
|
|||
}
|
||||
}
|
||||
|
||||
static void perf_counts__reset(struct perf_counts *counts)
|
||||
void perf_counts__reset(struct perf_counts *counts)
|
||||
{
|
||||
xyarray__reset(counts->loaded);
|
||||
xyarray__reset(counts->values);
|
||||
memset(&counts->aggr, 0, sizeof(struct perf_counts_values));
|
||||
}
|
||||
|
||||
void perf_evsel__reset_counts(struct evsel *evsel)
|
||||
void evsel__reset_counts(struct evsel *evsel)
|
||||
{
|
||||
perf_counts__reset(evsel->counts);
|
||||
}
|
||||
|
||||
int perf_evsel__alloc_counts(struct evsel *evsel, int ncpus, int nthreads)
|
||||
int evsel__alloc_counts(struct evsel *evsel, int ncpus, int nthreads)
|
||||
{
|
||||
evsel->counts = perf_counts__new(ncpus, nthreads);
|
||||
return evsel->counts != NULL ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
void perf_evsel__free_counts(struct evsel *evsel)
|
||||
void evsel__free_counts(struct evsel *evsel)
|
||||
{
|
||||
perf_counts__delete(evsel->counts);
|
||||
evsel->counts = NULL;
|
||||
|
|
|
@ -37,9 +37,10 @@ perf_counts__set_loaded(struct perf_counts *counts, int cpu, int thread, bool lo
|
|||
|
||||
struct perf_counts *perf_counts__new(int ncpus, int nthreads);
|
||||
void perf_counts__delete(struct perf_counts *counts);
|
||||
void perf_counts__reset(struct perf_counts *counts);
|
||||
|
||||
void perf_evsel__reset_counts(struct evsel *evsel);
|
||||
int perf_evsel__alloc_counts(struct evsel *evsel, int ncpus, int nthreads);
|
||||
void perf_evsel__free_counts(struct evsel *evsel);
|
||||
void evsel__reset_counts(struct evsel *evsel);
|
||||
int evsel__alloc_counts(struct evsel *evsel, int ncpus, int nthreads);
|
||||
void evsel__free_counts(struct evsel *evsel);
|
||||
|
||||
#endif /* __PERF_COUNTS_H */
|
||||
|
|
|
@ -22,7 +22,7 @@ struct numa_topology_node {
|
|||
|
||||
struct numa_topology {
|
||||
u32 nr;
|
||||
struct numa_topology_node nodes[0];
|
||||
struct numa_topology_node nodes[];
|
||||
};
|
||||
|
||||
struct cpu_topology *cpu_topology__new(void);
|
||||
|
|
|
@ -15,7 +15,7 @@ enum {
|
|||
MODE_CLASS = 1,
|
||||
MODE_FUNC = 2,
|
||||
MODE_TYPE = 3,
|
||||
MODE_CTYPE = 3, /* class arg */
|
||||
MODE_CTYPE = 4, /* class arg */
|
||||
};
|
||||
|
||||
#define BASE_ENT(c, n) [c - 'A']=n
|
||||
|
@ -27,7 +27,7 @@ static const char *base_types['Z' - 'A' + 1] = {
|
|||
BASE_ENT('I', "int" ),
|
||||
BASE_ENT('J', "long" ),
|
||||
BASE_ENT('S', "short" ),
|
||||
BASE_ENT('Z', "bool" ),
|
||||
BASE_ENT('Z', "boolean" ),
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -59,15 +59,16 @@ __demangle_java_sym(const char *str, const char *end, char *buf, int maxlen, int
|
|||
|
||||
switch (*q) {
|
||||
case 'L':
|
||||
if (mode == MODE_PREFIX || mode == MODE_CTYPE) {
|
||||
if (mode == MODE_CTYPE) {
|
||||
if (mode == MODE_PREFIX || mode == MODE_TYPE) {
|
||||
if (mode == MODE_TYPE) {
|
||||
if (narg)
|
||||
rlen += scnprintf(buf + rlen, maxlen - rlen, ", ");
|
||||
narg++;
|
||||
}
|
||||
rlen += scnprintf(buf + rlen, maxlen - rlen, "class ");
|
||||
if (mode == MODE_PREFIX)
|
||||
mode = MODE_CLASS;
|
||||
else
|
||||
mode = MODE_CTYPE;
|
||||
} else
|
||||
buf[rlen++] = *q;
|
||||
break;
|
||||
|
@ -120,7 +121,7 @@ __demangle_java_sym(const char *str, const char *end, char *buf, int maxlen, int
|
|||
if (mode != MODE_CLASS && mode != MODE_CTYPE)
|
||||
goto error;
|
||||
/* safe because at least one other char to process */
|
||||
if (isalpha(*(q + 1)))
|
||||
if (isalpha(*(q + 1)) && mode == MODE_CLASS)
|
||||
rlen += scnprintf(buf + rlen, maxlen - rlen, ".");
|
||||
if (mode == MODE_CLASS)
|
||||
mode = MODE_FUNC;
|
||||
|
|
|
@ -47,6 +47,7 @@ char dso__symtab_origin(const struct dso *dso)
|
|||
[DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO] = 'D',
|
||||
[DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f',
|
||||
[DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u',
|
||||
[DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO] = 'x',
|
||||
[DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO] = 'o',
|
||||
[DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b',
|
||||
[DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd',
|
||||
|
@ -129,6 +130,21 @@ int dso__read_binary_type_filename(const struct dso *dso,
|
|||
snprintf(filename + len, size - len, "%s", dso->long_name);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO:
|
||||
/*
|
||||
* Ubuntu can mixup /usr/lib with /lib, putting debuginfo in
|
||||
* /usr/lib/debug/lib when it is expected to be in
|
||||
* /usr/lib/debug/usr/lib
|
||||
*/
|
||||
if (strlen(dso->long_name) < 9 ||
|
||||
strncmp(dso->long_name, "/usr/lib/", 9)) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
len = __symbol__join_symfs(filename, size, "/usr/lib/debug");
|
||||
snprintf(filename + len, size - len, "%s", dso->long_name + 4);
|
||||
break;
|
||||
|
||||
case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO:
|
||||
{
|
||||
const char *last_slash;
|
||||
|
|
|
@ -30,6 +30,7 @@ enum dso_binary_type {
|
|||
DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO,
|
||||
DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
|
||||
DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
|
||||
DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO,
|
||||
DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
|
||||
DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
|
||||
DSO_BINARY_TYPE__GUEST_KMODULE,
|
||||
|
@ -137,7 +138,7 @@ struct dso_cache {
|
|||
struct rb_node rb_node;
|
||||
u64 offset;
|
||||
u64 size;
|
||||
char data[0];
|
||||
char data[];
|
||||
};
|
||||
|
||||
struct auxtrace_cache;
|
||||
|
@ -209,7 +210,7 @@ struct dso {
|
|||
struct nsinfo *nsinfo;
|
||||
struct dso_id id;
|
||||
refcount_t refcnt;
|
||||
char name[0];
|
||||
char name[];
|
||||
};
|
||||
|
||||
/* dso__for_each_symbol - iterate over the symbols of given type
|
||||
|
|
|
@ -79,7 +79,7 @@ struct sample_read {
|
|||
|
||||
struct ip_callchain {
|
||||
u64 nr;
|
||||
u64 ips[0];
|
||||
u64 ips[];
|
||||
};
|
||||
|
||||
struct branch_stack;
|
||||
|
|
|
@ -233,7 +233,7 @@ void perf_evlist__set_leader(struct evlist *evlist)
|
|||
|
||||
int __perf_evlist__add_default(struct evlist *evlist, bool precise)
|
||||
{
|
||||
struct evsel *evsel = perf_evsel__new_cycles(precise);
|
||||
struct evsel *evsel = evsel__new_cycles(precise);
|
||||
|
||||
if (evsel == NULL)
|
||||
return -ENOMEM;
|
||||
|
@ -249,7 +249,7 @@ int perf_evlist__add_dummy(struct evlist *evlist)
|
|||
.config = PERF_COUNT_SW_DUMMY,
|
||||
.size = sizeof(attr), /* to capture ABI version */
|
||||
};
|
||||
struct evsel *evsel = perf_evsel__new_idx(&attr, evlist->core.nr_entries);
|
||||
struct evsel *evsel = evsel__new_idx(&attr, evlist->core.nr_entries);
|
||||
|
||||
if (evsel == NULL)
|
||||
return -ENOMEM;
|
||||
|
@ -266,7 +266,7 @@ static int evlist__add_attrs(struct evlist *evlist,
|
|||
size_t i;
|
||||
|
||||
for (i = 0; i < nr_attrs; i++) {
|
||||
evsel = perf_evsel__new_idx(attrs + i, evlist->core.nr_entries + i);
|
||||
evsel = evsel__new_idx(attrs + i, evlist->core.nr_entries + i);
|
||||
if (evsel == NULL)
|
||||
goto out_delete_partial_list;
|
||||
list_add_tail(&evsel->core.node, &head);
|
||||
|
@ -325,7 +325,7 @@ perf_evlist__find_tracepoint_by_name(struct evlist *evlist,
|
|||
int perf_evlist__add_newtp(struct evlist *evlist,
|
||||
const char *sys, const char *name, void *handler)
|
||||
{
|
||||
struct evsel *evsel = perf_evsel__newtp(sys, name);
|
||||
struct evsel *evsel = evsel__newtp(sys, name);
|
||||
|
||||
if (IS_ERR(evsel))
|
||||
return -1;
|
||||
|
@ -380,22 +380,33 @@ void evlist__disable(struct evlist *evlist)
|
|||
{
|
||||
struct evsel *pos;
|
||||
struct affinity affinity;
|
||||
int cpu, i;
|
||||
int cpu, i, imm = 0;
|
||||
bool has_imm = false;
|
||||
|
||||
if (affinity__setup(&affinity) < 0)
|
||||
return;
|
||||
|
||||
evlist__for_each_cpu(evlist, i, cpu) {
|
||||
affinity__set(&affinity, cpu);
|
||||
/* Disable 'immediate' events last */
|
||||
for (imm = 0; imm <= 1; imm++) {
|
||||
evlist__for_each_cpu(evlist, i, cpu) {
|
||||
affinity__set(&affinity, cpu);
|
||||
|
||||
evlist__for_each_entry(evlist, pos) {
|
||||
if (evsel__cpu_iter_skip(pos, cpu))
|
||||
continue;
|
||||
if (pos->disabled || !evsel__is_group_leader(pos) || !pos->core.fd)
|
||||
continue;
|
||||
evsel__disable_cpu(pos, pos->cpu_iter - 1);
|
||||
evlist__for_each_entry(evlist, pos) {
|
||||
if (evsel__cpu_iter_skip(pos, cpu))
|
||||
continue;
|
||||
if (pos->disabled || !evsel__is_group_leader(pos) || !pos->core.fd)
|
||||
continue;
|
||||
if (pos->immediate)
|
||||
has_imm = true;
|
||||
if (pos->immediate != imm)
|
||||
continue;
|
||||
evsel__disable_cpu(pos, pos->cpu_iter - 1);
|
||||
}
|
||||
}
|
||||
if (!has_imm)
|
||||
break;
|
||||
}
|
||||
|
||||
affinity__cleanup(&affinity);
|
||||
evlist__for_each_entry(evlist, pos) {
|
||||
if (!evsel__is_group_leader(pos) || !pos->core.fd)
|
||||
|
|
|
@ -56,14 +56,14 @@ struct perf_missing_features perf_missing_features;
|
|||
|
||||
static clockid_t clockid;
|
||||
|
||||
static int perf_evsel__no_extra_init(struct evsel *evsel __maybe_unused)
|
||||
static int evsel__no_extra_init(struct evsel *evsel __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __weak test_attr__ready(void) { }
|
||||
|
||||
static void perf_evsel__no_extra_fini(struct evsel *evsel __maybe_unused)
|
||||
static void evsel__no_extra_fini(struct evsel *evsel __maybe_unused)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -73,13 +73,12 @@ static struct {
|
|||
void (*fini)(struct evsel *evsel);
|
||||
} perf_evsel__object = {
|
||||
.size = sizeof(struct evsel),
|
||||
.init = perf_evsel__no_extra_init,
|
||||
.fini = perf_evsel__no_extra_fini,
|
||||
.init = evsel__no_extra_init,
|
||||
.fini = evsel__no_extra_fini,
|
||||
};
|
||||
|
||||
int perf_evsel__object_config(size_t object_size,
|
||||
int (*init)(struct evsel *evsel),
|
||||
void (*fini)(struct evsel *evsel))
|
||||
int evsel__object_config(size_t object_size, int (*init)(struct evsel *evsel),
|
||||
void (*fini)(struct evsel *evsel))
|
||||
{
|
||||
|
||||
if (object_size == 0)
|
||||
|
@ -255,11 +254,12 @@ void evsel__init(struct evsel *evsel,
|
|||
evsel->metric_expr = NULL;
|
||||
evsel->metric_name = NULL;
|
||||
evsel->metric_events = NULL;
|
||||
evsel->per_pkg_mask = NULL;
|
||||
evsel->collect_stat = false;
|
||||
evsel->pmu_name = NULL;
|
||||
}
|
||||
|
||||
struct evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
|
||||
struct evsel *evsel__new_idx(struct perf_event_attr *attr, int idx)
|
||||
{
|
||||
struct evsel *evsel = zalloc(perf_evsel__object.size);
|
||||
|
||||
|
@ -292,7 +292,7 @@ static bool perf_event_can_profile_kernel(void)
|
|||
return perf_event_paranoid_check(1);
|
||||
}
|
||||
|
||||
struct evsel *perf_evsel__new_cycles(bool precise)
|
||||
struct evsel *evsel__new_cycles(bool precise)
|
||||
{
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
|
@ -334,7 +334,7 @@ error_free:
|
|||
/*
|
||||
* Returns pointer with encoded error via <linux/err.h> interface.
|
||||
*/
|
||||
struct evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx)
|
||||
struct evsel *evsel__newtp_idx(const char *sys, const char *name, int idx)
|
||||
{
|
||||
struct evsel *evsel = zalloc(perf_evsel__object.size);
|
||||
int err = -ENOMEM;
|
||||
|
@ -372,7 +372,7 @@ out_err:
|
|||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {
|
||||
const char *evsel__hw_names[PERF_COUNT_HW_MAX] = {
|
||||
"cycles",
|
||||
"instructions",
|
||||
"cache-references",
|
||||
|
@ -387,8 +387,8 @@ const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {
|
|||
|
||||
static const char *__evsel__hw_name(u64 config)
|
||||
{
|
||||
if (config < PERF_COUNT_HW_MAX && perf_evsel__hw_names[config])
|
||||
return perf_evsel__hw_names[config];
|
||||
if (config < PERF_COUNT_HW_MAX && evsel__hw_names[config])
|
||||
return evsel__hw_names[config];
|
||||
|
||||
return "unknown-hardware";
|
||||
}
|
||||
|
@ -435,7 +435,7 @@ static int evsel__hw_name(struct evsel *evsel, char *bf, size_t size)
|
|||
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
|
||||
}
|
||||
|
||||
const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = {
|
||||
const char *evsel__sw_names[PERF_COUNT_SW_MAX] = {
|
||||
"cpu-clock",
|
||||
"task-clock",
|
||||
"page-faults",
|
||||
|
@ -450,8 +450,8 @@ const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = {
|
|||
|
||||
static const char *__evsel__sw_name(u64 config)
|
||||
{
|
||||
if (config < PERF_COUNT_SW_MAX && perf_evsel__sw_names[config])
|
||||
return perf_evsel__sw_names[config];
|
||||
if (config < PERF_COUNT_SW_MAX && evsel__sw_names[config])
|
||||
return evsel__sw_names[config];
|
||||
return "unknown-software";
|
||||
}
|
||||
|
||||
|
@ -486,8 +486,7 @@ static int evsel__bp_name(struct evsel *evsel, char *bf, size_t size)
|
|||
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
|
||||
}
|
||||
|
||||
const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES] = {
|
||||
const char *evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX][EVSEL__MAX_ALIASES] = {
|
||||
{ "L1-dcache", "l1-d", "l1d", "L1-data", },
|
||||
{ "L1-icache", "l1-i", "l1i", "L1-instruction", },
|
||||
{ "LLC", "L2", },
|
||||
|
@ -497,15 +496,13 @@ const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX]
|
|||
{ "node", },
|
||||
};
|
||||
|
||||
const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES] = {
|
||||
const char *evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX][EVSEL__MAX_ALIASES] = {
|
||||
{ "load", "loads", "read", },
|
||||
{ "store", "stores", "write", },
|
||||
{ "prefetch", "prefetches", "speculative-read", "speculative-load", },
|
||||
};
|
||||
|
||||
const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES] = {
|
||||
const char *evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX][EVSEL__MAX_ALIASES] = {
|
||||
{ "refs", "Reference", "ops", "access", },
|
||||
{ "misses", "miss", },
|
||||
};
|
||||
|
@ -521,7 +518,7 @@ const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
|
|||
* L1I : Read and prefetch only
|
||||
* ITLB and BPU : Read-only
|
||||
*/
|
||||
static unsigned long perf_evsel__hw_cache_stat[C(MAX)] = {
|
||||
static unsigned long evsel__hw_cache_stat[C(MAX)] = {
|
||||
[C(L1D)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
||||
[C(L1I)] = (CACHE_READ | CACHE_PREFETCH),
|
||||
[C(LL)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH),
|
||||
|
@ -533,7 +530,7 @@ static unsigned long perf_evsel__hw_cache_stat[C(MAX)] = {
|
|||
|
||||
bool evsel__is_cache_op_valid(u8 type, u8 op)
|
||||
{
|
||||
if (perf_evsel__hw_cache_stat[type] & COP(op))
|
||||
if (evsel__hw_cache_stat[type] & COP(op))
|
||||
return true; /* valid */
|
||||
else
|
||||
return false; /* invalid */
|
||||
|
@ -542,13 +539,13 @@ bool evsel__is_cache_op_valid(u8 type, u8 op)
|
|||
int __evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, char *bf, size_t size)
|
||||
{
|
||||
if (result) {
|
||||
return scnprintf(bf, size, "%s-%s-%s", perf_evsel__hw_cache[type][0],
|
||||
perf_evsel__hw_cache_op[op][0],
|
||||
perf_evsel__hw_cache_result[result][0]);
|
||||
return scnprintf(bf, size, "%s-%s-%s", evsel__hw_cache[type][0],
|
||||
evsel__hw_cache_op[op][0],
|
||||
evsel__hw_cache_result[result][0]);
|
||||
}
|
||||
|
||||
return scnprintf(bf, size, "%s-%s", perf_evsel__hw_cache[type][0],
|
||||
perf_evsel__hw_cache_op[op][1]);
|
||||
return scnprintf(bf, size, "%s-%s", evsel__hw_cache[type][0],
|
||||
evsel__hw_cache_op[op][1]);
|
||||
}
|
||||
|
||||
static int __evsel__hw_cache_name(u64 config, char *bf, size_t size)
|
||||
|
@ -768,10 +765,10 @@ perf_evsel__reset_callgraph(struct evsel *evsel,
|
|||
}
|
||||
}
|
||||
|
||||
static void apply_config_terms(struct evsel *evsel,
|
||||
struct record_opts *opts, bool track)
|
||||
static void evsel__apply_config_terms(struct evsel *evsel,
|
||||
struct record_opts *opts, bool track)
|
||||
{
|
||||
struct perf_evsel_config_term *term;
|
||||
struct evsel_config_term *term;
|
||||
struct list_head *config_terms = &evsel->config_terms;
|
||||
struct perf_event_attr *attr = &evsel->core.attr;
|
||||
/* callgraph default */
|
||||
|
@ -784,30 +781,30 @@ static void apply_config_terms(struct evsel *evsel,
|
|||
|
||||
list_for_each_entry(term, config_terms, list) {
|
||||
switch (term->type) {
|
||||
case PERF_EVSEL__CONFIG_TERM_PERIOD:
|
||||
case EVSEL__CONFIG_TERM_PERIOD:
|
||||
if (!(term->weak && opts->user_interval != ULLONG_MAX)) {
|
||||
attr->sample_period = term->val.period;
|
||||
attr->freq = 0;
|
||||
evsel__reset_sample_bit(evsel, PERIOD);
|
||||
}
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_FREQ:
|
||||
case EVSEL__CONFIG_TERM_FREQ:
|
||||
if (!(term->weak && opts->user_freq != UINT_MAX)) {
|
||||
attr->sample_freq = term->val.freq;
|
||||
attr->freq = 1;
|
||||
evsel__set_sample_bit(evsel, PERIOD);
|
||||
}
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_TIME:
|
||||
case EVSEL__CONFIG_TERM_TIME:
|
||||
if (term->val.time)
|
||||
evsel__set_sample_bit(evsel, TIME);
|
||||
else
|
||||
evsel__reset_sample_bit(evsel, TIME);
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_CALLGRAPH:
|
||||
case EVSEL__CONFIG_TERM_CALLGRAPH:
|
||||
callgraph_buf = term->val.str;
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_BRANCH:
|
||||
case EVSEL__CONFIG_TERM_BRANCH:
|
||||
if (term->val.str && strcmp(term->val.str, "no")) {
|
||||
evsel__set_sample_bit(evsel, BRANCH_STACK);
|
||||
parse_branch_str(term->val.str,
|
||||
|
@ -815,16 +812,16 @@ static void apply_config_terms(struct evsel *evsel,
|
|||
} else
|
||||
evsel__reset_sample_bit(evsel, BRANCH_STACK);
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_STACK_USER:
|
||||
case EVSEL__CONFIG_TERM_STACK_USER:
|
||||
dump_size = term->val.stack_user;
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_MAX_STACK:
|
||||
case EVSEL__CONFIG_TERM_MAX_STACK:
|
||||
max_stack = term->val.max_stack;
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_MAX_EVENTS:
|
||||
case EVSEL__CONFIG_TERM_MAX_EVENTS:
|
||||
evsel->max_events = term->val.max_events;
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_INHERIT:
|
||||
case EVSEL__CONFIG_TERM_INHERIT:
|
||||
/*
|
||||
* attr->inherit should has already been set by
|
||||
* evsel__config. If user explicitly set
|
||||
|
@ -833,20 +830,20 @@ static void apply_config_terms(struct evsel *evsel,
|
|||
*/
|
||||
attr->inherit = term->val.inherit ? 1 : 0;
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_OVERWRITE:
|
||||
case EVSEL__CONFIG_TERM_OVERWRITE:
|
||||
attr->write_backward = term->val.overwrite ? 1 : 0;
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_DRV_CFG:
|
||||
case EVSEL__CONFIG_TERM_DRV_CFG:
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_PERCORE:
|
||||
case EVSEL__CONFIG_TERM_PERCORE:
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_AUX_OUTPUT:
|
||||
case EVSEL__CONFIG_TERM_AUX_OUTPUT:
|
||||
attr->aux_output = term->val.aux_output ? 1 : 0;
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_AUX_SAMPLE_SIZE:
|
||||
case EVSEL__CONFIG_TERM_AUX_SAMPLE_SIZE:
|
||||
/* Already applied by auxtrace */
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_CFG_CHG:
|
||||
case EVSEL__CONFIG_TERM_CFG_CHG:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -907,10 +904,9 @@ static bool is_dummy_event(struct evsel *evsel)
|
|||
(evsel->core.attr.config == PERF_COUNT_SW_DUMMY);
|
||||
}
|
||||
|
||||
struct perf_evsel_config_term *__perf_evsel__get_config_term(struct evsel *evsel,
|
||||
enum evsel_term_type type)
|
||||
struct evsel_config_term *__evsel__get_config_term(struct evsel *evsel, enum evsel_term_type type)
|
||||
{
|
||||
struct perf_evsel_config_term *term, *found_term = NULL;
|
||||
struct evsel_config_term *term, *found_term = NULL;
|
||||
|
||||
list_for_each_entry(term, &evsel->config_terms, list) {
|
||||
if (term->type == type)
|
||||
|
@ -1145,7 +1141,7 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts,
|
|||
* Apply event specific term settings,
|
||||
* it overloads any global configuration.
|
||||
*/
|
||||
apply_config_terms(evsel, opts, track);
|
||||
evsel__apply_config_terms(evsel, opts, track);
|
||||
|
||||
evsel->ignore_missing_thread = opts->ignore_missing_thread;
|
||||
|
||||
|
@ -1158,11 +1154,14 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts,
|
|||
}
|
||||
|
||||
/*
|
||||
* A dummy event never triggers any actual counter and therefore
|
||||
* cannot be used with branch_stack.
|
||||
*
|
||||
* For initial_delay, a dummy event is added implicitly.
|
||||
* The software event will trigger -EOPNOTSUPP error out,
|
||||
* if BRANCH_STACK bit is set.
|
||||
*/
|
||||
if (opts->initial_delay && is_dummy_event(evsel))
|
||||
if (is_dummy_event(evsel))
|
||||
evsel__reset_sample_bit(evsel, BRANCH_STACK);
|
||||
}
|
||||
|
||||
|
@ -1241,9 +1240,9 @@ int evsel__disable(struct evsel *evsel)
|
|||
return err;
|
||||
}
|
||||
|
||||
static void perf_evsel__free_config_terms(struct evsel *evsel)
|
||||
static void evsel__free_config_terms(struct evsel *evsel)
|
||||
{
|
||||
struct perf_evsel_config_term *term, *h;
|
||||
struct evsel_config_term *term, *h;
|
||||
|
||||
list_for_each_entry_safe(term, h, &evsel->config_terms, list) {
|
||||
list_del_init(&term->list);
|
||||
|
@ -1257,10 +1256,10 @@ void evsel__exit(struct evsel *evsel)
|
|||
{
|
||||
assert(list_empty(&evsel->core.node));
|
||||
assert(evsel->evlist == NULL);
|
||||
perf_evsel__free_counts(evsel);
|
||||
evsel__free_counts(evsel);
|
||||
perf_evsel__free_fd(&evsel->core);
|
||||
perf_evsel__free_id(&evsel->core);
|
||||
perf_evsel__free_config_terms(evsel);
|
||||
evsel__free_config_terms(evsel);
|
||||
cgroup__put(evsel->cgrp);
|
||||
perf_cpu_map__put(evsel->core.cpus);
|
||||
perf_cpu_map__put(evsel->core.own_cpus);
|
||||
|
@ -1268,6 +1267,8 @@ void evsel__exit(struct evsel *evsel)
|
|||
zfree(&evsel->group_name);
|
||||
zfree(&evsel->name);
|
||||
zfree(&evsel->pmu_name);
|
||||
zfree(&evsel->per_pkg_mask);
|
||||
zfree(&evsel->metric_events);
|
||||
perf_evsel__object.fini(evsel);
|
||||
}
|
||||
|
||||
|
@ -1425,7 +1426,7 @@ int __evsel__read_on_cpu(struct evsel *evsel, int cpu, int thread, bool scale)
|
|||
if (FD(evsel, cpu, thread) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1, thread + 1) < 0)
|
||||
if (evsel->counts == NULL && evsel__alloc_counts(evsel, cpu + 1, thread + 1) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) <= 0)
|
||||
|
@ -2416,7 +2417,7 @@ bool evsel__fallback(struct evsel *evsel, int err, char *msg, size_t msgsize)
|
|||
|
||||
/* Is there already the separator in the name. */
|
||||
if (strchr(name, '/') ||
|
||||
strchr(name, ':'))
|
||||
(strchr(name, ':') && !evsel->is_libpfm_event))
|
||||
sep = "";
|
||||
|
||||
if (asprintf(&new_name, "%s%su", name, sep) < 0)
|
||||
|
@ -2477,31 +2478,40 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target,
|
|||
int err, char *msg, size_t size)
|
||||
{
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
int printed = 0;
|
||||
int printed = 0, enforced = 0;
|
||||
|
||||
switch (err) {
|
||||
case EPERM:
|
||||
case EACCES:
|
||||
printed += scnprintf(msg + printed, size - printed,
|
||||
"Access to performance monitoring and observability operations is limited.\n");
|
||||
|
||||
if (!sysfs__read_int("fs/selinux/enforce", &enforced)) {
|
||||
if (enforced) {
|
||||
printed += scnprintf(msg + printed, size - printed,
|
||||
"Enforced MAC policy settings (SELinux) can limit access to performance\n"
|
||||
"monitoring and observability operations. Inspect system audit records for\n"
|
||||
"more perf_event access control information and adjusting the policy.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (err == EPERM)
|
||||
printed = scnprintf(msg, size,
|
||||
printed += scnprintf(msg, size,
|
||||
"No permission to enable %s event.\n\n", evsel__name(evsel));
|
||||
|
||||
return scnprintf(msg + printed, size - printed,
|
||||
"You may not have permission to collect %sstats.\n\n"
|
||||
"Consider tweaking /proc/sys/kernel/perf_event_paranoid,\n"
|
||||
"which controls use of the performance events system by\n"
|
||||
"unprivileged users (without CAP_PERFMON or CAP_SYS_ADMIN).\n\n"
|
||||
"The current value is %d:\n\n"
|
||||
"Consider adjusting /proc/sys/kernel/perf_event_paranoid setting to open\n"
|
||||
"access to performance monitoring and observability operations for users\n"
|
||||
"without CAP_PERFMON or CAP_SYS_ADMIN Linux capability.\n"
|
||||
"perf_event_paranoid setting is %d:\n"
|
||||
" -1: Allow use of (almost) all events by all users\n"
|
||||
" Ignore mlock limit after perf_event_mlock_kb without CAP_IPC_LOCK\n"
|
||||
">= 0: Disallow ftrace function tracepoint by users without CAP_PERFMON or CAP_SYS_ADMIN\n"
|
||||
" Disallow raw tracepoint access by users without CAP_SYS_PERFMON or CAP_SYS_ADMIN\n"
|
||||
">= 1: Disallow CPU event access by users without CAP_PERFMON or CAP_SYS_ADMIN\n"
|
||||
">= 2: Disallow kernel profiling by users without CAP_PERFMON or CAP_SYS_ADMIN\n\n"
|
||||
"To make this setting permanent, edit /etc/sysctl.conf too, e.g.:\n\n"
|
||||
" kernel.perf_event_paranoid = -1\n" ,
|
||||
target->system_wide ? "system-wide " : "",
|
||||
perf_event_paranoid());
|
||||
">= 0: Disallow raw and ftrace function tracepoint access\n"
|
||||
">= 1: Disallow CPU event access\n"
|
||||
">= 2: Disallow kernel profiling\n"
|
||||
"To make the adjusted perf_event_paranoid setting permanent preserve it\n"
|
||||
"in /etc/sysctl.conf (e.g. kernel.perf_event_paranoid = <setting>)",
|
||||
perf_event_paranoid());
|
||||
case ENOENT:
|
||||
return scnprintf(msg, size, "The %s event is not supported.", evsel__name(evsel));
|
||||
case EMFILE:
|
||||
|
|
|
@ -76,6 +76,7 @@ struct evsel {
|
|||
bool ignore_missing_thread;
|
||||
bool forced_leader;
|
||||
bool use_uncore_alias;
|
||||
bool is_libpfm_event;
|
||||
/* parse modifier helper */
|
||||
int exclude_GH;
|
||||
int sample_read;
|
||||
|
@ -154,31 +155,31 @@ void perf_counts_values__scale(struct perf_counts_values *count,
|
|||
void evsel__compute_deltas(struct evsel *evsel, int cpu, int thread,
|
||||
struct perf_counts_values *count);
|
||||
|
||||
int perf_evsel__object_config(size_t object_size,
|
||||
int (*init)(struct evsel *evsel),
|
||||
void (*fini)(struct evsel *evsel));
|
||||
int evsel__object_config(size_t object_size,
|
||||
int (*init)(struct evsel *evsel),
|
||||
void (*fini)(struct evsel *evsel));
|
||||
|
||||
struct perf_pmu *evsel__find_pmu(struct evsel *evsel);
|
||||
bool evsel__is_aux_event(struct evsel *evsel);
|
||||
|
||||
struct evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx);
|
||||
struct evsel *evsel__new_idx(struct perf_event_attr *attr, int idx);
|
||||
|
||||
static inline struct evsel *evsel__new(struct perf_event_attr *attr)
|
||||
{
|
||||
return perf_evsel__new_idx(attr, 0);
|
||||
return evsel__new_idx(attr, 0);
|
||||
}
|
||||
|
||||
struct evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx);
|
||||
struct evsel *evsel__newtp_idx(const char *sys, const char *name, int idx);
|
||||
|
||||
/*
|
||||
* Returns pointer with encoded error via <linux/err.h> interface.
|
||||
*/
|
||||
static inline struct evsel *perf_evsel__newtp(const char *sys, const char *name)
|
||||
static inline struct evsel *evsel__newtp(const char *sys, const char *name)
|
||||
{
|
||||
return perf_evsel__newtp_idx(sys, name, 0);
|
||||
return evsel__newtp_idx(sys, name, 0);
|
||||
}
|
||||
|
||||
struct evsel *perf_evsel__new_cycles(bool precise);
|
||||
struct evsel *evsel__new_cycles(bool precise);
|
||||
|
||||
struct tep_event *event_format__new(const char *sys, const char *name);
|
||||
|
||||
|
@ -198,16 +199,13 @@ void evsel__calc_id_pos(struct evsel *evsel);
|
|||
|
||||
bool evsel__is_cache_op_valid(u8 type, u8 op);
|
||||
|
||||
#define PERF_EVSEL__MAX_ALIASES 8
|
||||
#define EVSEL__MAX_ALIASES 8
|
||||
|
||||
extern const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES];
|
||||
extern const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES];
|
||||
extern const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
|
||||
[PERF_EVSEL__MAX_ALIASES];
|
||||
extern const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX];
|
||||
extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX];
|
||||
extern const char *evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX][EVSEL__MAX_ALIASES];
|
||||
extern const char *evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX][EVSEL__MAX_ALIASES];
|
||||
extern const char *evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX][EVSEL__MAX_ALIASES];
|
||||
extern const char *evsel__hw_names[PERF_COUNT_HW_MAX];
|
||||
extern const char *evsel__sw_names[PERF_COUNT_SW_MAX];
|
||||
int __evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, char *bf, size_t size);
|
||||
const char *evsel__name(struct evsel *evsel);
|
||||
|
||||
|
|
|
@ -6,30 +6,30 @@
|
|||
#include <stdbool.h>
|
||||
|
||||
/*
|
||||
* The 'struct perf_evsel_config_term' is used to pass event
|
||||
* The 'struct evsel_config_term' is used to pass event
|
||||
* specific configuration data to evsel__config routine.
|
||||
* It is allocated within event parsing and attached to
|
||||
* perf_evsel::config_terms list head.
|
||||
* evsel::config_terms list head.
|
||||
*/
|
||||
enum evsel_term_type {
|
||||
PERF_EVSEL__CONFIG_TERM_PERIOD,
|
||||
PERF_EVSEL__CONFIG_TERM_FREQ,
|
||||
PERF_EVSEL__CONFIG_TERM_TIME,
|
||||
PERF_EVSEL__CONFIG_TERM_CALLGRAPH,
|
||||
PERF_EVSEL__CONFIG_TERM_STACK_USER,
|
||||
PERF_EVSEL__CONFIG_TERM_INHERIT,
|
||||
PERF_EVSEL__CONFIG_TERM_MAX_STACK,
|
||||
PERF_EVSEL__CONFIG_TERM_MAX_EVENTS,
|
||||
PERF_EVSEL__CONFIG_TERM_OVERWRITE,
|
||||
PERF_EVSEL__CONFIG_TERM_DRV_CFG,
|
||||
PERF_EVSEL__CONFIG_TERM_BRANCH,
|
||||
PERF_EVSEL__CONFIG_TERM_PERCORE,
|
||||
PERF_EVSEL__CONFIG_TERM_AUX_OUTPUT,
|
||||
PERF_EVSEL__CONFIG_TERM_AUX_SAMPLE_SIZE,
|
||||
PERF_EVSEL__CONFIG_TERM_CFG_CHG,
|
||||
EVSEL__CONFIG_TERM_PERIOD,
|
||||
EVSEL__CONFIG_TERM_FREQ,
|
||||
EVSEL__CONFIG_TERM_TIME,
|
||||
EVSEL__CONFIG_TERM_CALLGRAPH,
|
||||
EVSEL__CONFIG_TERM_STACK_USER,
|
||||
EVSEL__CONFIG_TERM_INHERIT,
|
||||
EVSEL__CONFIG_TERM_MAX_STACK,
|
||||
EVSEL__CONFIG_TERM_MAX_EVENTS,
|
||||
EVSEL__CONFIG_TERM_OVERWRITE,
|
||||
EVSEL__CONFIG_TERM_DRV_CFG,
|
||||
EVSEL__CONFIG_TERM_BRANCH,
|
||||
EVSEL__CONFIG_TERM_PERCORE,
|
||||
EVSEL__CONFIG_TERM_AUX_OUTPUT,
|
||||
EVSEL__CONFIG_TERM_AUX_SAMPLE_SIZE,
|
||||
EVSEL__CONFIG_TERM_CFG_CHG,
|
||||
};
|
||||
|
||||
struct perf_evsel_config_term {
|
||||
struct evsel_config_term {
|
||||
struct list_head list;
|
||||
enum evsel_term_type type;
|
||||
bool free_str;
|
||||
|
@ -53,10 +53,9 @@ struct perf_evsel_config_term {
|
|||
|
||||
struct evsel;
|
||||
|
||||
struct perf_evsel_config_term *__perf_evsel__get_config_term(struct evsel *evsel,
|
||||
enum evsel_term_type type);
|
||||
struct evsel_config_term *__evsel__get_config_term(struct evsel *evsel, enum evsel_term_type type);
|
||||
|
||||
#define perf_evsel__get_config_term(evsel, type) \
|
||||
__perf_evsel__get_config_term(evsel, PERF_EVSEL__CONFIG_TERM_ ## type)
|
||||
#define evsel__get_config_term(evsel, type) \
|
||||
__evsel__get_config_term(evsel, EVSEL__CONFIG_TERM_ ## type)
|
||||
|
||||
#endif // __PERF_EVSEL_CONFIG_H
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue