perf tools changes for v5.9
New features: - Introduce controlling how 'perf stat' and 'perf record' works via a control file descriptor, allowing starting with events configured but disabled until commands are received via the control file descriptor. This allows, for instance for tools such as Intel VTune to make further use of perf as its Linux platform driver. - Improve 'perf record' to to register in a perf.data file header the clockid used to help later correlate things like syslog files and perf events recorded. - Add basic syscall and find_next_bit benchmarks to 'perf bench'. - Allow using computed metrics in calculating other metrics. For instance: { .metric_expr = "l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit", .metric_name = "DCache_L2_All_Hits", }, { .metric_expr = "max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss", .metric_name = "DCache_L2_All_Miss", }, { .metric_expr = "dcache_l2_all_hits + dcache_l2_all_miss", .metric_name = "DCache_L2_All", } - Add suport for 'd_ratio', '>' and '<' operators to the expression resolver used in calculating metrics in 'perf stat'. Support for new kernel features: - Support TEXT_POKE and KSYMBOL_TYPE_OOL perf metadata events to cope with things like ftrace, trampolines, i.e. changes in the kernel text that gets in the way of properly decoding Intel PT hardware traces, for instance. Intel PT: - Add various knobs to reduce the volume of Intel PT traces by reducing the level of details such as decoding just some types of packets (e.g., FUP/TIP, PSB+), also filtering by time range. - Add new itrace options (log flags to the 'd' option, error flags to the 'e' one, etc), controlling how Intel PT is transformed into perf events, document some missing options (e.g., how to synthesize callchains). BPF: - Properly report BPF errors when parsing events. - Do not setup side-band events if LIBBPF is not linked, fixing a segfault. Libraries: - Improvements on the libtraceevent plugin mechanism. - Improve libtracevent support for KVM trace events SVM exit reasons. - Add a libtracevent plugins for decoding syscalls/sys_enter_futex and for tlb_flush. - Ensure sample_period is set libpfm4 events in 'perf test'. - Fixup libperf namespacing, to make sure what is in libperf has the perf_ namespace while what is now only in tools/perf/ doesn't use that prefix. Arch specific: - Improve the testing of vendor events and metrics in 'perf test'. - Allow no ARM CoreSight hardware tracer sink to be specified on command line. - Fix arm_spe_x recording when mixed with other perf events. - Add s390 idle functions 'psw_idle' and 'psw_idle_exit' to list of idle symbols. - List kernel supplied event aliases for arm64 in 'perf list'. - Add support for extended register capability in PowerPC 9 and 10. - Added nest IMC power9 metric events. Miscellaneous: - No need to setup sample_regs_intr/sample_regs_user for dummy events. - Update various copies of kernel headers, some causing perf to handle new syscalls, MSRs, etc. - Improve usage of flex and yacc, enabling warnings and addressing the fallout. - Add missing '--output' option to 'perf kmem' so that it can pass it along to 'perf record'. - 'perf probe' fixes related to adding multiple probes on the same address for the same event. - Make 'perf probe' warn if the target function is a GNU indirect function. - Remove //anon mmap events from 'perf inject jit' to fix supporting both using ELF files for generated functions and the perf-PID.map approaches. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> 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. fedora:rawhide with python3 and gcc 10.1.1-2 is failing (10.1.1-1 on fedora:32 works), fixes will be provided soon. clearlinux:latest is failing on libbpf, there is a fix already in the bpf tree. The ones failing when linking with libllvm, not the default build, were restricted to clang-9/llvm-9, working with anything before or after, e.g., using clang-8 on ubuntu:19.10 and clang-11 on debian:experimental fixed the build in those environments. # export PERF_TARBALL=http://192.168.124.1/perf/perf-5.8.0.tar.xz # 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:6 : Ok gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-23) 19 centos:7 : Ok gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39) 20 centos:8 : Ok gcc (GCC) 8.3.1 20191121 (Red Hat 8.3.1-5), clang version 9.0.1 (Red Hat 9.0.1-2.module_el8.2.0+309+0c7b6b03) 21 clearlinux:latest : FAIL gcc (Clear Linux OS for Intel Architecture) 10.2.1 20200723 releases/gcc-10.2.0-3-g677b80db41, clang version 10.0.1 gcc (Clear Linux OS for Intel Architecture) 10.2.1 20200723 releases/gcc-10.2.0-3-g677b80db41 btf.c: In function 'btf__parse_raw': btf.c:625:28: error: 'btf' may be used uninitialized in this function [-Werror=maybe-uninitialized] 625 | return err ? ERR_PTR(err) : btf; | ~~~~~~~~~~~~~~~~~~~^~~~~ 22 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) 23 debian:9 : Ok gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516, clang version 3.8.1-24 (tags/RELEASE_381/final) 24 debian:10 : Ok gcc (Debian 8.3.0-6) 8.3.0, clang version 7.0.1-8 (tags/RELEASE_701/final) 25 debian:experimental : Ok gcc (Debian 10.2.0-3) 10.2.0, Debian clang version 11.0.0-+rc1-1 26 debian:experimental-x-arm64 : Ok aarch64-linux-gnu-gcc (Debian 9.3.0-8) 9.3.0 27 debian:experimental-x-mips : Ok mips-linux-gnu-gcc (Debian 8.3.0-19) 8.3.0 28 debian:experimental-x-mips64 : Ok mips64-linux-gnuabi64-gcc (Debian 9.3.0-8) 9.3.0 29 debian:experimental-x-mipsel : Ok mipsel-linux-gnu-gcc (Debian 9.2.1-8) 9.2.1 20190909 30 fedora:20 : Ok gcc (GCC) 4.8.3 20140911 (Red Hat 4.8.3-7) 31 fedora:22 : Ok gcc (GCC) 5.3.1 20160406 (Red Hat 5.3.1-6), clang version 3.5.0 (tags/RELEASE_350/final) 32 fedora:23 : Ok gcc (GCC) 5.3.1 20160406 (Red Hat 5.3.1-6), clang version 3.7.0 (tags/RELEASE_370/final) 33 fedora:24 : Ok gcc (GCC) 6.3.1 20161221 (Red Hat 6.3.1-1), clang version 3.8.1 (tags/RELEASE_381/final) 34 fedora:24-x-ARC-uClibc : Ok arc-linux-gcc (ARCompact ISA Linux uClibc toolchain 2017.09-rc2) 7.1.1 20170710 35 fedora:25 : Ok gcc (GCC) 6.4.1 20170727 (Red Hat 6.4.1-1), clang version 3.9.1 (tags/RELEASE_391/final) 36 fedora:26 : Ok gcc (GCC) 7.3.1 20180130 (Red Hat 7.3.1-2), clang version 4.0.1 (tags/RELEASE_401/final) 37 fedora:27 : Ok gcc (GCC) 7.3.1 20180712 (Red Hat 7.3.1-6), clang version 5.0.2 (tags/RELEASE_502/final) 38 fedora:28 : Ok gcc (GCC) 8.3.1 20190223 (Red Hat 8.3.1-2), clang version 6.0.1 (tags/RELEASE_601/final) 39 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) 40 fedora:30 : Ok gcc (GCC) 9.3.1 20200408 (Red Hat 9.3.1-2), clang version 8.0.0 (Fedora 8.0.0-3.fc30) 41 fedora:30-x-ARC-glibc : Ok arc-linux-gcc (ARC HS GNU/Linux glibc toolchain 2019.03-rc1) 8.3.1 20190225 42 fedora:30-x-ARC-uClibc : Ok arc-linux-gcc (ARCv2 ISA Linux uClibc toolchain 2019.03-rc1) 8.3.1 20190225 43 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) 44 fedora:32 : Ok gcc (GCC) 10.1.1 20200507 (Red Hat 10.1.1-1), clang version 10.0.0 (Fedora 10.0.0-2.fc32) 45 fedora:rawhide : FAIL gcc (GCC) 10.2.1 20200723 (Red Hat 10.2.1-1), clang version 10.0.0 (Fedora 10.0.0-10.fc33) gcc (GCC) 10.2.1 20200723 (Red Hat 10.2.1-1) util/scripting-engines/trace-event-python.c: In function 'python_start_script': util/scripting-engines/trace-event-python.c:1595:2: error: 'visibility' attribute ignored [-Werror=attributes] 1595 | PyMODINIT_FUNC (*initfunc)(void); | ^~~~~~~~~~~~~~ 46 gentoo-stage3-amd64:latest : Ok gcc (Gentoo 9.3.0-r1 p3) 9.3.0 47 mageia:5 : Ok gcc (GCC) 4.9.2, clang version 3.5.2 (tags/RELEASE_352/final) 48 mageia:6 : Ok gcc (Mageia 5.5.0-1.mga6) 5.5.0, clang version 3.9.1 (tags/RELEASE_391/final) 49 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) 50 manjaro:latest : Ok gcc (GCC) 9.2.0, clang version 9.0.0 (tags/RELEASE_900/final) 51 openmandriva:cooker : Ok gcc (GCC) 10.0.0 20200502 (OpenMandriva), clang version 10.0.1 52 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) 53 opensuse:15.1 : Ok gcc (SUSE Linux) 7.5.0, clang version 7.0.1 (tags/RELEASE_701/final 349238) 54 opensuse:15.2 : Ok gcc (SUSE Linux) 7.5.0, clang version 9.0.1 55 opensuse:42.3 : Ok gcc (SUSE Linux) 4.8.5, clang version 3.8.0 (tags/RELEASE_380/final 262553) 56 opensuse:tumbleweed : Ok gcc (SUSE Linux) 10.2.1 20200728 [revision c0438ced53bcf57e4ebb1c38c226e41571aca892], clang version 10.0.1 57 oraclelinux:6 : Ok gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-23.0.1) 58 oraclelinux:7 : Ok gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39.0.5) 59 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) 60 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) 61 ubuntu:14.04 : Ok gcc (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4 62 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) 63 ubuntu:16.04-x-arm : Ok arm-linux-gnueabihf-gcc (Ubuntu/Linaro 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 64 ubuntu:16.04-x-arm64 : Ok aarch64-linux-gnu-gcc (Ubuntu/Linaro 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 65 ubuntu:16.04-x-powerpc : Ok powerpc-linux-gnu-gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 66 ubuntu:16.04-x-powerpc64 : Ok powerpc64-linux-gnu-gcc (Ubuntu/IBM 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 67 ubuntu:16.04-x-powerpc64el : Ok powerpc64le-linux-gnu-gcc (Ubuntu/IBM 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 68 ubuntu:16.04-x-s390 : Ok s390x-linux-gnu-gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 69 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) 70 ubuntu:18.04-x-arm : Ok arm-linux-gnueabihf-gcc (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0 71 ubuntu:18.04-x-arm64 : Ok aarch64-linux-gnu-gcc (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0 72 ubuntu:18.04-x-m68k : Ok m68k-linux-gnu-gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 73 ubuntu:18.04-x-powerpc : Ok powerpc-linux-gnu-gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0 74 ubuntu:18.04-x-powerpc64 : Ok powerpc64-linux-gnu-gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0 75 ubuntu:18.04-x-powerpc64el : Ok powerpc64le-linux-gnu-gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 76 ubuntu:18.04-x-riscv64 : Ok riscv64-linux-gnu-gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 77 ubuntu:18.04-x-s390 : Ok s390x-linux-gnu-gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 78 ubuntu:18.04-x-sh4 : Ok sh4-linux-gnu-gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0 79 ubuntu:18.04-x-sparc64 : Ok sparc64-linux-gnu-gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 80 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) 81 ubuntu:19.04 : Ok gcc (Ubuntu 8.3.0-6ubuntu1) 8.3.0, clang version 8.0.0-3 (tags/RELEASE_800/final) 82 ubuntu:19.04-x-alpha : Ok alpha-linux-gnu-gcc (Ubuntu 8.3.0-6ubuntu1) 8.3.0 83 ubuntu:19.04-x-arm64 : Ok aarch64-linux-gnu-gcc (Ubuntu/Linaro 8.3.0-6ubuntu1) 8.3.0 84 ubuntu:19.04-x-hppa : Ok hppa-linux-gnu-gcc (Ubuntu 8.3.0-6ubuntu1) 8.3.0 85 ubuntu:19.10 : Ok gcc (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008, clang version 8.0.1-3build1 (tags/RELEASE_801/final) 86 219.74 ubuntu:20.04 : Ok gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0, clang version 10.0.0-4ubuntu1 # # uname -a Linux quaco 5.7.12-200.fc32.x86_64 #1 SMP Sat Aug 1 16:13:38 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux # git log --oneline -11101c872c8
perf record: Skip side-band event setup if HAVE_LIBBPF_SUPPORT is not set # perf version --build-options perf version 5.8.g1101c872c8c7 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 : Ok 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 : Skip (some metrics failed) 10.4: Parsing of PMU event table metrics with fake PMUs : 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 : FAILED! Fix being evaluated 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: Parse and process metrics : Ok 68: x86 rdpmc : Ok 69: Convert perf time to TSC : Ok 70: DWARF unwind : Ok 71: x86 instruction decoder - new instructions : Ok 72: Intel PT packet decoder : Ok 73: x86 bp modify : Ok 74: probe libc's inet_pton & backtrace it with ping : Ok 75: Use vfs_getname probe to get syscall args filenames : Ok 76: Add vfs_getname probe to get syscall args filenames : Ok 77: Check open filename arg using perf trace + vfs_getname: Ok 78: Zstd perf.data compression/decompression : Ok # $ cd ~acme/git/perf ; git log --oneline -1; time make -C tools/perf build-test1101c872c8
(HEAD -> perf/core, quaco/perf/core) perf record: Skip side-band event setup if HAVE_LIBBPF_SUPPORT is not set make: Entering directory '/home/acme/git/perf/tools/perf' - tarpkg: ./tests/perf-targz-src-pkg . make_no_libcrypto_O: make NO_LIBCRYPTO=1 make_no_sdt_O: make NO_SDT=1 make_no_libnuma_O: make NO_LIBNUMA=1 make_no_libaudit_O: make NO_LIBAUDIT=1 make_no_syscall_tbl_O: make NO_SYSCALL_TABLE=1 make_no_newt_O: make NO_NEWT=1 make_no_auxtrace_O: make NO_AUXTRACE=1 make_install_prefix_slash_O: make install prefix=/tmp/krava/ make_no_libbpf_DEBUG_O: make NO_LIBBPF=1 DEBUG=1 make_static_O: make LDFLAGS=-static NO_PERF_READ_VDSO32=1 NO_PERF_READ_VDSOX32=1 NO_JVMTI=1 make_pure_O: make make_install_bin_O: make install-bin make_no_libelf_O: make NO_LIBELF=1 make_util_pmu_bison_o_O: make util/pmu-bison.o make_with_babeltrace_O: make LIBBABELTRACE=1 make_debug_O: make DEBUG=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_clangllvm_O: make LIBCLANGLLVM=1 make_no_libbionic_O: make NO_LIBBIONIC=1 make_tags_O: make tags make_doc_O: make doc make_no_gtk2_O: make NO_GTK2=1 make_no_libbpf_O: make NO_LIBBPF=1 make_no_backtrace_O: make NO_BACKTRACE=1 make_install_prefix_O: make install prefix=/tmp/krava make_no_slang_O: make NO_SLANG=1 make_no_demangle_O: make NO_DEMANGLE=1 make_no_libpython_O: make NO_LIBPYTHON=1 make_no_libperl_O: make NO_LIBPERL=1 make_clean_all_O: make clean all make_no_libdw_dwarf_unwind_O: make NO_LIBDW_DWARF_UNWIND=1 make_with_libpfm4_O: make LIBPFM4=1 make_help_O: make help make_no_ui_O: make NO_NEWT=1 NO_SLANG=1 NO_GTK2=1 make_no_libunwind_O: make NO_LIBUNWIND=1 make_util_map_o_O: make util/map.o make_install_O: make install make_no_scripts_O: make NO_LIBPYTHON=1 NO_LIBPERL=1 make_perf_o_O: make perf.o OK make: Leaving directory '/home/acme/git/perf/tools/perf' $ -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQR2GiIUctdOfX2qHhGyPKLppCJ+JwUCXzFq5QAKCRCyPKLppCJ+ J46OAP40WV9uE1L+3NznUF5D+zh7++SquzEBoABZiYNAXNhrGQEA2QZqAspkbLoo hCM/yo7lO1XixiTGlp533b14OvE5oQk= =n4VQ -----END PGP SIGNATURE----- Merge tag 'perf-tools-2020-08-10' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux Pull perf tools updates from Arnaldo Carvalho de Melo: "New features: - Introduce controlling how 'perf stat' and 'perf record' works via a control file descriptor, allowing starting with events configured but disabled until commands are received via the control file descriptor. This allows, for instance for tools such as Intel VTune to make further use of perf as its Linux platform driver. - Improve 'perf record' to to register in a perf.data file header the clockid used to help later correlate things like syslog files and perf events recorded. - Add basic syscall and find_next_bit benchmarks to 'perf bench'. - Allow using computed metrics in calculating other metrics. For instance: { .metric_expr = "l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit", .metric_name = "DCache_L2_All_Hits", }, { .metric_expr = "max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss", .metric_name = "DCache_L2_All_Miss", }, { .metric_expr = "dcache_l2_all_hits + dcache_l2_all_miss", .metric_name = "DCache_L2_All", } - Add suport for 'd_ratio', '>' and '<' operators to the expression resolver used in calculating metrics in 'perf stat'. Support for new kernel features: - Support TEXT_POKE and KSYMBOL_TYPE_OOL perf metadata events to cope with things like ftrace, trampolines, i.e. changes in the kernel text that gets in the way of properly decoding Intel PT hardware traces, for instance. Intel PT: - Add various knobs to reduce the volume of Intel PT traces by reducing the level of details such as decoding just some types of packets (e.g., FUP/TIP, PSB+), also filtering by time range. - Add new itrace options (log flags to the 'd' option, error flags to the 'e' one, etc), controlling how Intel PT is transformed into perf events, document some missing options (e.g., how to synthesize callchains). BPF: - Properly report BPF errors when parsing events. - Do not setup side-band events if LIBBPF is not linked, fixing a segfault. Libraries: - Improvements to the libtraceevent plugin mechanism. - Improve libtracevent support for KVM trace events SVM exit reasons. - Add a libtracevent plugins for decoding syscalls/sys_enter_futex and for tlb_flush. - Ensure sample_period is set libpfm4 events in 'perf test'. - Fixup libperf namespacing, to make sure what is in libperf has the perf_ namespace while what is now only in tools/perf/ doesn't use that prefix. Arch specific: - Improve the testing of vendor events and metrics in 'perf test'. - Allow no ARM CoreSight hardware tracer sink to be specified on command line. - Fix arm_spe_x recording when mixed with other perf events. - Add s390 idle functions 'psw_idle' and 'psw_idle_exit' to list of idle symbols. - List kernel supplied event aliases for arm64 in 'perf list'. - Add support for extended register capability in PowerPC 9 and 10. - Added nest IMC power9 metric events. Miscellaneous: - No need to setup sample_regs_intr/sample_regs_user for dummy events. - Update various copies of kernel headers, some causing perf to handle new syscalls, MSRs, etc. - Improve usage of flex and yacc, enabling warnings and addressing the fallout. - Add missing '--output' option to 'perf kmem' so that it can pass it along to 'perf record'. - 'perf probe' fixes related to adding multiple probes on the same address for the same event. - Make 'perf probe' warn if the target function is a GNU indirect function. - Remove //anon mmap events from 'perf inject jit' to fix supporting both using ELF files for generated functions and the perf-PID.map approaches" * tag 'perf-tools-2020-08-10' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux: (144 commits) perf record: Skip side-band event setup if HAVE_LIBBPF_SUPPORT is not set perf tools powerpc: Add support for extended regs in power10 perf tools powerpc: Add support for extended register capability tools headers UAPI: Sync drm/i915_drm.h with the kernel sources tools arch x86: Sync asm/cpufeatures.h with the kernel sources tools arch x86: Sync the msr-index.h copy with the kernel sources tools headers UAPI: update linux/in.h copy tools headers API: Update close_range affected files perf script: Add 'tod' field to display time of day perf script: Change the 'enum perf_output_field' enumerators to be 64 bits perf data: Add support to store time of day in CTF data conversion perf tools: Move clockid_res_ns under clock struct perf header: Store clock references for -k/--clockid option perf tools: Add clockid_name function perf clockid: Move parse_clockid() to new clockid object tools lib traceevent: Handle possible strdup() error in tep_add_plugin_path() API libtraceevent: Fixed description of tep_add_plugin_path() API libtraceevent: Fixed type in PRINT_FMT_STING libtraceevent: Fixed broken indentation in parse_ip4_print_args() libtraceevent: Improve error handling of tep_plugin_add_option() API ...
This commit is contained in:
Коммит
00e4db5125
|
@ -48,6 +48,24 @@ enum perf_event_powerpc_regs {
|
|||
PERF_REG_POWERPC_DSISR,
|
||||
PERF_REG_POWERPC_SIER,
|
||||
PERF_REG_POWERPC_MMCRA,
|
||||
PERF_REG_POWERPC_MAX,
|
||||
/* Extended registers */
|
||||
PERF_REG_POWERPC_MMCR0,
|
||||
PERF_REG_POWERPC_MMCR1,
|
||||
PERF_REG_POWERPC_MMCR2,
|
||||
PERF_REG_POWERPC_MMCR3,
|
||||
PERF_REG_POWERPC_SIER2,
|
||||
PERF_REG_POWERPC_SIER3,
|
||||
/* Max regs without the extended regs */
|
||||
PERF_REG_POWERPC_MAX = PERF_REG_POWERPC_MMCRA + 1,
|
||||
};
|
||||
|
||||
#define PERF_REG_PMU_MASK ((1ULL << PERF_REG_POWERPC_MAX) - 1)
|
||||
|
||||
/* PERF_REG_EXTENDED_MASK value for CPU_FTR_ARCH_300 */
|
||||
#define PERF_REG_PMU_MASK_300 (((1ULL << (PERF_REG_POWERPC_MMCR2 + 1)) - 1) - PERF_REG_PMU_MASK)
|
||||
/* PERF_REG_EXTENDED_MASK value for CPU_FTR_ARCH_31 */
|
||||
#define PERF_REG_PMU_MASK_31 (((1ULL << (PERF_REG_POWERPC_SIER3 + 1)) - 1) - PERF_REG_PMU_MASK)
|
||||
|
||||
#define PERF_REG_MAX_ISA_300 (PERF_REG_POWERPC_MMCR2 + 1)
|
||||
#define PERF_REG_MAX_ISA_31 (PERF_REG_POWERPC_SIER3 + 1)
|
||||
#endif /* _UAPI_ASM_POWERPC_PERF_REGS_H */
|
||||
|
|
|
@ -96,6 +96,7 @@
|
|||
#define X86_FEATURE_SYSCALL32 ( 3*32+14) /* "" syscall in IA32 userspace */
|
||||
#define X86_FEATURE_SYSENTER32 ( 3*32+15) /* "" sysenter in IA32 userspace */
|
||||
#define X86_FEATURE_REP_GOOD ( 3*32+16) /* REP microcode works well */
|
||||
/* free ( 3*32+17) */
|
||||
#define X86_FEATURE_LFENCE_RDTSC ( 3*32+18) /* "" LFENCE synchronizes RDTSC */
|
||||
#define X86_FEATURE_ACC_POWER ( 3*32+19) /* AMD Accumulated Power Mechanism */
|
||||
#define X86_FEATURE_NOPL ( 3*32+20) /* The NOPL (0F 1F) instructions */
|
||||
|
@ -107,6 +108,7 @@
|
|||
#define X86_FEATURE_EXTD_APICID ( 3*32+26) /* Extended APICID (8 bits) */
|
||||
#define X86_FEATURE_AMD_DCM ( 3*32+27) /* AMD multi-node processor */
|
||||
#define X86_FEATURE_APERFMPERF ( 3*32+28) /* P-State hardware coordination feedback capability (APERF/MPERF MSRs) */
|
||||
/* free ( 3*32+29) */
|
||||
#define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */
|
||||
#define X86_FEATURE_TSC_KNOWN_FREQ ( 3*32+31) /* TSC has known frequency */
|
||||
|
||||
|
@ -365,7 +367,9 @@
|
|||
#define X86_FEATURE_SRBDS_CTRL (18*32+ 9) /* "" SRBDS mitigation MSR available */
|
||||
#define X86_FEATURE_MD_CLEAR (18*32+10) /* VERW clears CPU buffers */
|
||||
#define X86_FEATURE_TSX_FORCE_ABORT (18*32+13) /* "" TSX_FORCE_ABORT */
|
||||
#define X86_FEATURE_SERIALIZE (18*32+14) /* SERIALIZE instruction */
|
||||
#define X86_FEATURE_PCONFIG (18*32+18) /* Intel PCONFIG */
|
||||
#define X86_FEATURE_ARCH_LBR (18*32+19) /* Intel ARCH LBR */
|
||||
#define X86_FEATURE_SPEC_CTRL (18*32+26) /* "" Speculation Control (IBRS + IBPB) */
|
||||
#define X86_FEATURE_INTEL_STIBP (18*32+27) /* "" Single Thread Indirect Branch Predictors */
|
||||
#define X86_FEATURE_FLUSH_L1D (18*32+28) /* Flush L1D cache */
|
||||
|
|
|
@ -149,6 +149,10 @@
|
|||
|
||||
#define MSR_LBR_SELECT 0x000001c8
|
||||
#define MSR_LBR_TOS 0x000001c9
|
||||
|
||||
#define MSR_IA32_POWER_CTL 0x000001fc
|
||||
#define MSR_IA32_POWER_CTL_BIT_EE 19
|
||||
|
||||
#define MSR_LBR_NHM_FROM 0x00000680
|
||||
#define MSR_LBR_NHM_TO 0x000006c0
|
||||
#define MSR_LBR_CORE_FROM 0x00000040
|
||||
|
@ -158,7 +162,23 @@
|
|||
#define LBR_INFO_MISPRED BIT_ULL(63)
|
||||
#define LBR_INFO_IN_TX BIT_ULL(62)
|
||||
#define LBR_INFO_ABORT BIT_ULL(61)
|
||||
#define LBR_INFO_CYC_CNT_VALID BIT_ULL(60)
|
||||
#define LBR_INFO_CYCLES 0xffff
|
||||
#define LBR_INFO_BR_TYPE_OFFSET 56
|
||||
#define LBR_INFO_BR_TYPE (0xfull << LBR_INFO_BR_TYPE_OFFSET)
|
||||
|
||||
#define MSR_ARCH_LBR_CTL 0x000014ce
|
||||
#define ARCH_LBR_CTL_LBREN BIT(0)
|
||||
#define ARCH_LBR_CTL_CPL_OFFSET 1
|
||||
#define ARCH_LBR_CTL_CPL (0x3ull << ARCH_LBR_CTL_CPL_OFFSET)
|
||||
#define ARCH_LBR_CTL_STACK_OFFSET 3
|
||||
#define ARCH_LBR_CTL_STACK (0x1ull << ARCH_LBR_CTL_STACK_OFFSET)
|
||||
#define ARCH_LBR_CTL_FILTER_OFFSET 16
|
||||
#define ARCH_LBR_CTL_FILTER (0x7full << ARCH_LBR_CTL_FILTER_OFFSET)
|
||||
#define MSR_ARCH_LBR_DEPTH 0x000014cf
|
||||
#define MSR_ARCH_LBR_FROM_0 0x00001500
|
||||
#define MSR_ARCH_LBR_TO_0 0x00001600
|
||||
#define MSR_ARCH_LBR_INFO_0 0x00001200
|
||||
|
||||
#define MSR_IA32_PEBS_ENABLE 0x000003f1
|
||||
#define MSR_PEBS_DATA_CFG 0x000003f2
|
||||
|
@ -253,8 +273,6 @@
|
|||
|
||||
#define MSR_PEBS_FRONTEND 0x000003f7
|
||||
|
||||
#define MSR_IA32_POWER_CTL 0x000001fc
|
||||
|
||||
#define MSR_IA32_MC0_CTL 0x00000400
|
||||
#define MSR_IA32_MC0_STATUS 0x00000401
|
||||
#define MSR_IA32_MC0_ADDR 0x00000402
|
||||
|
@ -418,7 +436,6 @@
|
|||
#define MSR_AMD64_PATCH_LEVEL 0x0000008b
|
||||
#define MSR_AMD64_TSC_RATIO 0xc0000104
|
||||
#define MSR_AMD64_NB_CFG 0xc001001f
|
||||
#define MSR_AMD64_CPUID_FN_1 0xc0011004
|
||||
#define MSR_AMD64_PATCH_LOADER 0xc0010020
|
||||
#define MSR_AMD_PERF_CTL 0xc0010062
|
||||
#define MSR_AMD_PERF_STATUS 0xc0010063
|
||||
|
@ -427,6 +444,7 @@
|
|||
#define MSR_AMD64_OSVW_STATUS 0xc0010141
|
||||
#define MSR_AMD_PPIN_CTL 0xc00102f0
|
||||
#define MSR_AMD_PPIN 0xc00102f1
|
||||
#define MSR_AMD64_CPUID_FN_1 0xc0011004
|
||||
#define MSR_AMD64_LS_CFG 0xc0011020
|
||||
#define MSR_AMD64_DC_CFG 0xc0011022
|
||||
#define MSR_AMD64_BU_CFG2 0xc001102a
|
||||
|
@ -466,6 +484,8 @@
|
|||
#define MSR_F16H_DR0_ADDR_MASK 0xc0011027
|
||||
|
||||
/* Fam 15h MSRs */
|
||||
#define MSR_F15H_CU_PWR_ACCUMULATOR 0xc001007a
|
||||
#define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b
|
||||
#define MSR_F15H_PERF_CTL 0xc0010200
|
||||
#define MSR_F15H_PERF_CTL0 MSR_F15H_PERF_CTL
|
||||
#define MSR_F15H_PERF_CTL1 (MSR_F15H_PERF_CTL + 2)
|
||||
|
|
|
@ -8,7 +8,7 @@ endif
|
|||
|
||||
feature_check = $(eval $(feature_check_code))
|
||||
define feature_check_code
|
||||
feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" CXXFLAGS="$(EXTRA_CXXFLAGS) $(FEATURE_CHECK_CXXFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) $(OUTPUT_FEATURES)test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0)
|
||||
feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CC=$(CC) CXX=$(CXX) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" CXXFLAGS="$(EXTRA_CXXFLAGS) $(FEATURE_CHECK_CXXFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) $(OUTPUT_FEATURES)test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0)
|
||||
endef
|
||||
|
||||
feature_set = $(eval $(feature_set_code))
|
||||
|
|
|
@ -74,8 +74,6 @@ FILES= \
|
|||
|
||||
FILES := $(addprefix $(OUTPUT),$(FILES))
|
||||
|
||||
CC ?= $(CROSS_COMPILE)gcc
|
||||
CXX ?= $(CROSS_COMPILE)g++
|
||||
PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config
|
||||
LLVM_CONFIG ?= llvm-config
|
||||
CLANG ?= clang
|
||||
|
|
|
@ -850,6 +850,8 @@ __SYSCALL(__NR_pidfd_open, sys_pidfd_open)
|
|||
#define __NR_clone3 435
|
||||
__SYSCALL(__NR_clone3, sys_clone3)
|
||||
#endif
|
||||
#define __NR_close_range 436
|
||||
__SYSCALL(__NR_close_range, sys_close_range)
|
||||
|
||||
#define __NR_openat2 437
|
||||
__SYSCALL(__NR_openat2, sys_openat2)
|
||||
|
|
|
@ -55,7 +55,7 @@ extern "C" {
|
|||
* cause the related events to not be seen.
|
||||
*
|
||||
* I915_RESET_UEVENT - Event is generated just before an attempt to reset the
|
||||
* the GPU. The value supplied with the event is always 1. NOTE: Disable
|
||||
* GPU. The value supplied with the event is always 1. NOTE: Disable
|
||||
* reset via module parameter will cause this event to not be seen.
|
||||
*/
|
||||
#define I915_L3_PARITY_UEVENT "L3_PARITY_ERROR"
|
||||
|
@ -1934,7 +1934,7 @@ enum drm_i915_perf_property_id {
|
|||
|
||||
/**
|
||||
* The value specifies which set of OA unit metrics should be
|
||||
* be configured, defining the contents of any OA unit reports.
|
||||
* configured, defining the contents of any OA unit reports.
|
||||
*
|
||||
* This property is available in perf revision 1.
|
||||
*/
|
||||
|
|
|
@ -123,6 +123,7 @@ struct in_addr {
|
|||
#define IP_CHECKSUM 23
|
||||
#define IP_BIND_ADDRESS_NO_PORT 24
|
||||
#define IP_RECVFRAGSIZE 25
|
||||
#define IP_RECVERR_RFC4884 26
|
||||
|
||||
/* IP_MTU_DISCOVER values */
|
||||
#define IP_PMTUDISC_DONT 0 /* Never send DF frames */
|
||||
|
|
|
@ -383,7 +383,8 @@ struct perf_event_attr {
|
|||
bpf_event : 1, /* include bpf events */
|
||||
aux_output : 1, /* generate AUX records instead of events */
|
||||
cgroup : 1, /* include cgroup events */
|
||||
__reserved_1 : 31;
|
||||
text_poke : 1, /* include text poke events */
|
||||
__reserved_1 : 30;
|
||||
|
||||
union {
|
||||
__u32 wakeup_events; /* wakeup every n events */
|
||||
|
@ -1041,12 +1042,35 @@ enum perf_event_type {
|
|||
*/
|
||||
PERF_RECORD_CGROUP = 19,
|
||||
|
||||
/*
|
||||
* Records changes to kernel text i.e. self-modified code. 'old_len' is
|
||||
* the number of old bytes, 'new_len' is the number of new bytes. Either
|
||||
* 'old_len' or 'new_len' may be zero to indicate, for example, the
|
||||
* addition or removal of a trampoline. 'bytes' contains the old bytes
|
||||
* followed immediately by the new bytes.
|
||||
*
|
||||
* struct {
|
||||
* struct perf_event_header header;
|
||||
* u64 addr;
|
||||
* u16 old_len;
|
||||
* u16 new_len;
|
||||
* u8 bytes[];
|
||||
* struct sample_id sample_id;
|
||||
* };
|
||||
*/
|
||||
PERF_RECORD_TEXT_POKE = 20,
|
||||
|
||||
PERF_RECORD_MAX, /* non-ABI */
|
||||
};
|
||||
|
||||
enum perf_record_ksymbol_type {
|
||||
PERF_RECORD_KSYMBOL_TYPE_UNKNOWN = 0,
|
||||
PERF_RECORD_KSYMBOL_TYPE_BPF = 1,
|
||||
/*
|
||||
* Out of line code such as kprobe-replaced instructions or optimized
|
||||
* kprobes or ftrace trampolines.
|
||||
*/
|
||||
PERF_RECORD_KSYMBOL_TYPE_OOL = 2,
|
||||
PERF_RECORD_KSYMBOL_TYPE_MAX /* non-ABI */
|
||||
};
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <poll.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
void fdarray__init(struct fdarray *fda, int nr_autogrow)
|
||||
{
|
||||
|
@ -19,7 +20,7 @@ void fdarray__init(struct fdarray *fda, int nr_autogrow)
|
|||
|
||||
int fdarray__grow(struct fdarray *fda, int nr)
|
||||
{
|
||||
void *priv;
|
||||
struct priv *priv;
|
||||
int nr_alloc = fda->nr_alloc + nr;
|
||||
size_t psize = sizeof(fda->priv[0]) * nr_alloc;
|
||||
size_t size = sizeof(struct pollfd) * nr_alloc;
|
||||
|
@ -34,6 +35,9 @@ int fdarray__grow(struct fdarray *fda, int nr)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(&entries[fda->nr_alloc], 0, sizeof(struct pollfd) * nr);
|
||||
memset(&priv[fda->nr_alloc], 0, sizeof(fda->priv[0]) * nr);
|
||||
|
||||
fda->nr_alloc = nr_alloc;
|
||||
fda->entries = entries;
|
||||
fda->priv = priv;
|
||||
|
@ -69,7 +73,7 @@ void fdarray__delete(struct fdarray *fda)
|
|||
free(fda);
|
||||
}
|
||||
|
||||
int fdarray__add(struct fdarray *fda, int fd, short revents)
|
||||
int fdarray__add(struct fdarray *fda, int fd, short revents, enum fdarray_flags flags)
|
||||
{
|
||||
int pos = fda->nr;
|
||||
|
||||
|
@ -79,6 +83,7 @@ int fdarray__add(struct fdarray *fda, int fd, short revents)
|
|||
|
||||
fda->entries[fda->nr].fd = fd;
|
||||
fda->entries[fda->nr].events = revents;
|
||||
fda->priv[fda->nr].flags = flags;
|
||||
fda->nr++;
|
||||
return pos;
|
||||
}
|
||||
|
@ -93,22 +98,22 @@ int fdarray__filter(struct fdarray *fda, short revents,
|
|||
return 0;
|
||||
|
||||
for (fd = 0; fd < fda->nr; ++fd) {
|
||||
if (!fda->entries[fd].events)
|
||||
continue;
|
||||
|
||||
if (fda->entries[fd].revents & revents) {
|
||||
if (entry_destructor)
|
||||
entry_destructor(fda, fd, arg);
|
||||
|
||||
fda->entries[fd].revents = fda->entries[fd].events = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fd != nr) {
|
||||
fda->entries[nr] = fda->entries[fd];
|
||||
fda->priv[nr] = fda->priv[fd];
|
||||
}
|
||||
|
||||
++nr;
|
||||
if (!(fda->priv[fd].flags & fdarray_flag__nonfilterable))
|
||||
++nr;
|
||||
}
|
||||
|
||||
return fda->nr = nr;
|
||||
return nr;
|
||||
}
|
||||
|
||||
int fdarray__poll(struct fdarray *fda, int timeout)
|
||||
|
|
|
@ -21,19 +21,27 @@ struct fdarray {
|
|||
int nr_alloc;
|
||||
int nr_autogrow;
|
||||
struct pollfd *entries;
|
||||
union {
|
||||
int idx;
|
||||
void *ptr;
|
||||
struct priv {
|
||||
union {
|
||||
int idx;
|
||||
void *ptr;
|
||||
};
|
||||
unsigned int flags;
|
||||
} *priv;
|
||||
};
|
||||
|
||||
enum fdarray_flags {
|
||||
fdarray_flag__default = 0x00000000,
|
||||
fdarray_flag__nonfilterable = 0x00000001
|
||||
};
|
||||
|
||||
void fdarray__init(struct fdarray *fda, int nr_autogrow);
|
||||
void fdarray__exit(struct fdarray *fda);
|
||||
|
||||
struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow);
|
||||
void fdarray__delete(struct fdarray *fda);
|
||||
|
||||
int fdarray__add(struct fdarray *fda, int fd, short revents);
|
||||
int fdarray__add(struct fdarray *fda, int fd, short revents, enum fdarray_flags flags);
|
||||
int fdarray__poll(struct fdarray *fda, int timeout);
|
||||
int fdarray__filter(struct fdarray *fda, short revents,
|
||||
void (*entry_destructor)(struct fdarray *fda, int fd, void *arg),
|
||||
|
|
|
@ -305,9 +305,9 @@ int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
|
|||
}
|
||||
|
||||
int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,
|
||||
void *ptr, short revent)
|
||||
void *ptr, short revent, enum fdarray_flags flags)
|
||||
{
|
||||
int pos = fdarray__add(&evlist->pollfd, fd, revent | POLLERR | POLLHUP);
|
||||
int pos = fdarray__add(&evlist->pollfd, fd, revent | POLLERR | POLLHUP, flags);
|
||||
|
||||
if (pos >= 0) {
|
||||
evlist->pollfd.priv[pos].ptr = ptr;
|
||||
|
@ -488,7 +488,7 @@ mmap_per_evsel(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops,
|
|||
revent = !overwrite ? POLLIN : 0;
|
||||
|
||||
if (!evsel->system_wide &&
|
||||
perf_evlist__add_pollfd(evlist, fd, map, revent) < 0) {
|
||||
perf_evlist__add_pollfd(evlist, fd, map, revent, fdarray_flag__default) < 0) {
|
||||
perf_mmap__put(map);
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ struct perf_evlist_mmap_ops {
|
|||
|
||||
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist);
|
||||
int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd,
|
||||
void *ptr, short revent);
|
||||
void *ptr, short revent, enum fdarray_flags flags);
|
||||
|
||||
int perf_evlist__mmap_ops(struct perf_evlist *evlist,
|
||||
struct perf_evlist_mmap_ops *ops,
|
||||
|
|
|
@ -111,6 +111,14 @@ struct perf_record_cgroup {
|
|||
char path[PATH_MAX];
|
||||
};
|
||||
|
||||
struct perf_record_text_poke_event {
|
||||
struct perf_event_header header;
|
||||
__u64 addr;
|
||||
__u16 old_len;
|
||||
__u16 new_len;
|
||||
__u8 bytes[];
|
||||
};
|
||||
|
||||
struct perf_record_sample {
|
||||
struct perf_event_header header;
|
||||
__u64 array[];
|
||||
|
@ -367,6 +375,7 @@ union perf_event {
|
|||
struct perf_record_sample sample;
|
||||
struct perf_record_bpf_event bpf;
|
||||
struct perf_record_ksymbol ksymbol;
|
||||
struct perf_record_text_poke_event text_poke;
|
||||
struct perf_record_header_attr attr;
|
||||
struct perf_record_event_update event_update;
|
||||
struct perf_record_header_event_type event_type;
|
||||
|
|
|
@ -3,7 +3,7 @@ libtraceevent(3)
|
|||
|
||||
NAME
|
||||
----
|
||||
tep_load_plugins, tep_unload_plugins - Load / unload traceevent plugins.
|
||||
tep_load_plugins, tep_unload_plugins, tep_load_plugins_hook - Load / unload traceevent plugins.
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
@ -13,6 +13,12 @@ SYNOPSIS
|
|||
|
||||
struct tep_plugin_list pass:[*]*tep_load_plugins*(struct tep_handle pass:[*]_tep_);
|
||||
void *tep_unload_plugins*(struct tep_plugin_list pass:[*]_plugin_list_, struct tep_handle pass:[*]_tep_);
|
||||
void *tep_load_plugins_hook*(struct tep_handle pass:[*]_tep_, const char pass:[*]_suffix_,
|
||||
void (pass:[*]_load_plugin_)(struct tep_handle pass:[*]tep,
|
||||
const char pass:[*]path,
|
||||
const char pass:[*]name,
|
||||
void pass:[*]data),
|
||||
void pass:[*]_data_);
|
||||
--
|
||||
|
||||
DESCRIPTION
|
||||
|
@ -22,11 +28,13 @@ directories. The _tep_ argument is trace event parser context.
|
|||
The plugin directories are :
|
||||
[verse]
|
||||
--
|
||||
- Directories, specified in _tep_->plugins_dir with priority TEP_PLUGIN_FIRST
|
||||
- System's plugin directory, defined at the library compile time. It
|
||||
depends on the library installation prefix and usually is
|
||||
_(install_preffix)/lib/traceevent/plugins_
|
||||
- Directory, defined by the environment variable _TRACEEVENT_PLUGIN_DIR_
|
||||
- User's plugin directory, located at _~/.local/lib/traceevent/plugins_
|
||||
- Directories, specified in _tep_->plugins_dir with priority TEP_PLUGIN_LAST
|
||||
--
|
||||
Loading of plugins can be controlled by the _tep_flags_, using the
|
||||
_tep_set_flag()_ API:
|
||||
|
@ -44,6 +52,12 @@ _tep_load_plugins()_. The _tep_ argument is trace event parser context. The
|
|||
_plugin_list_ is the list of loaded plugins, returned by
|
||||
the _tep_load_plugins()_ function.
|
||||
|
||||
The _tep_load_plugins_hook_ function walks through all directories with plugins
|
||||
and calls user specified _load_plugin()_ hook for each plugin file. Only files
|
||||
with given _suffix_ are considered to be plugins. The _data_ is a user specified
|
||||
context, passed to _load_plugin()_. Directories and the walk order are the same
|
||||
as in _tep_load_plugins()_ API.
|
||||
|
||||
RETURN VALUE
|
||||
------------
|
||||
The _tep_load_plugins()_ function returns a list of successfully loaded plugins,
|
||||
|
@ -63,6 +77,15 @@ if (plugins == NULL) {
|
|||
}
|
||||
...
|
||||
tep_unload_plugins(plugins, tep);
|
||||
...
|
||||
void print_plugin(struct tep_handle *tep, const char *path,
|
||||
const char *name, void *data)
|
||||
{
|
||||
pritnf("Found libtraceevent plugin %s/%s\n", path, name);
|
||||
}
|
||||
...
|
||||
tep_load_plugins_hook(tep, ".so", print_plugin, NULL);
|
||||
...
|
||||
--
|
||||
|
||||
FILES
|
||||
|
|
|
@ -13,6 +13,7 @@ struct func_map;
|
|||
struct func_list;
|
||||
struct event_handler;
|
||||
struct func_resolver;
|
||||
struct tep_plugins_dir;
|
||||
|
||||
struct tep_handle {
|
||||
int ref_count;
|
||||
|
@ -47,7 +48,6 @@ struct tep_handle {
|
|||
struct printk_list *printklist;
|
||||
unsigned int printk_count;
|
||||
|
||||
|
||||
struct tep_event **events;
|
||||
int nr_events;
|
||||
struct tep_event **sort_events;
|
||||
|
@ -81,10 +81,30 @@ struct tep_handle {
|
|||
|
||||
/* cache */
|
||||
struct tep_event *last_event;
|
||||
|
||||
struct tep_plugins_dir *plugins_dir;
|
||||
};
|
||||
|
||||
enum tep_print_parse_type {
|
||||
PRINT_FMT_STRING,
|
||||
PRINT_FMT_ARG_DIGIT,
|
||||
PRINT_FMT_ARG_POINTER,
|
||||
PRINT_FMT_ARG_STRING,
|
||||
};
|
||||
|
||||
struct tep_print_parse {
|
||||
struct tep_print_parse *next;
|
||||
|
||||
char *format;
|
||||
int ls;
|
||||
enum tep_print_parse_type type;
|
||||
struct tep_print_arg *arg;
|
||||
struct tep_print_arg *len_as_arg;
|
||||
};
|
||||
|
||||
void tep_free_event(struct tep_event *event);
|
||||
void tep_free_format_field(struct tep_format_field *field);
|
||||
void tep_free_plugin_paths(struct tep_handle *tep);
|
||||
|
||||
unsigned short tep_data2host2(struct tep_handle *tep, unsigned short data);
|
||||
unsigned int tep_data2host4(struct tep_handle *tep, unsigned int data);
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,21 +1,7 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1 */
|
||||
/*
|
||||
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#ifndef _PARSE_EVENTS_H
|
||||
#define _PARSE_EVENTS_H
|
||||
|
@ -272,9 +258,12 @@ struct tep_print_arg {
|
|||
};
|
||||
};
|
||||
|
||||
struct tep_print_parse;
|
||||
|
||||
struct tep_print_fmt {
|
||||
char *format;
|
||||
struct tep_print_arg *args;
|
||||
struct tep_print_parse *print_cache;
|
||||
};
|
||||
|
||||
struct tep_event {
|
||||
|
@ -393,14 +382,29 @@ struct tep_plugin_list;
|
|||
|
||||
#define INVALID_PLUGIN_LIST_OPTION ((char **)((unsigned long)-1))
|
||||
|
||||
enum tep_plugin_load_priority {
|
||||
TEP_PLUGIN_FIRST,
|
||||
TEP_PLUGIN_LAST,
|
||||
};
|
||||
|
||||
int tep_add_plugin_path(struct tep_handle *tep, char *path,
|
||||
enum tep_plugin_load_priority prio);
|
||||
struct tep_plugin_list *tep_load_plugins(struct tep_handle *tep);
|
||||
void tep_unload_plugins(struct tep_plugin_list *plugin_list,
|
||||
struct tep_handle *tep);
|
||||
void tep_load_plugins_hook(struct tep_handle *tep, const char *suffix,
|
||||
void (*load_plugin)(struct tep_handle *tep,
|
||||
const char *path,
|
||||
const char *name,
|
||||
void *data),
|
||||
void *data);
|
||||
char **tep_plugin_list_options(void);
|
||||
void tep_plugin_free_options_list(char **list);
|
||||
int tep_plugin_add_options(const char *name,
|
||||
struct tep_plugin_option *options);
|
||||
int tep_plugin_add_option(const char *name, const char *val);
|
||||
void tep_plugin_remove_options(struct tep_plugin_option *options);
|
||||
void tep_plugin_print_options(struct trace_seq *s);
|
||||
void tep_print_plugins(struct trace_seq *s,
|
||||
const char *prefix, const char *suffix,
|
||||
const struct tep_plugin_list *list);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include "event-parse.h"
|
||||
#include "event-parse-local.h"
|
||||
#include "event-utils.h"
|
||||
|
@ -38,6 +39,12 @@ struct tep_plugin_list {
|
|||
void *handle;
|
||||
};
|
||||
|
||||
struct tep_plugins_dir {
|
||||
struct tep_plugins_dir *next;
|
||||
char *path;
|
||||
enum tep_plugin_load_priority prio;
|
||||
};
|
||||
|
||||
static void lower_case(char *str)
|
||||
{
|
||||
if (!str)
|
||||
|
@ -247,6 +254,170 @@ void tep_plugin_remove_options(struct tep_plugin_option *options)
|
|||
}
|
||||
}
|
||||
|
||||
static int parse_option_name(char **option, char **plugin)
|
||||
{
|
||||
char *p;
|
||||
|
||||
*plugin = NULL;
|
||||
|
||||
if ((p = strstr(*option, ":"))) {
|
||||
*plugin = *option;
|
||||
*p = '\0';
|
||||
*option = strdup(p + 1);
|
||||
if (!*option)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct tep_plugin_option *
|
||||
find_registered_option(const char *plugin, const char *option)
|
||||
{
|
||||
struct registered_plugin_options *reg;
|
||||
struct tep_plugin_option *op;
|
||||
const char *op_plugin;
|
||||
|
||||
for (reg = registered_options; reg; reg = reg->next) {
|
||||
for (op = reg->options; op->name; op++) {
|
||||
if (op->plugin_alias)
|
||||
op_plugin = op->plugin_alias;
|
||||
else
|
||||
op_plugin = op->file;
|
||||
|
||||
if (plugin && strcmp(plugin, op_plugin) != 0)
|
||||
continue;
|
||||
if (strcmp(option, op->name) != 0)
|
||||
continue;
|
||||
|
||||
return op;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int process_option(const char *plugin, const char *option, const char *val)
|
||||
{
|
||||
struct tep_plugin_option *op;
|
||||
|
||||
op = find_registered_option(plugin, option);
|
||||
if (!op)
|
||||
return 0;
|
||||
|
||||
return update_option_value(op, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* tep_plugin_add_option - add an option/val pair to set plugin options
|
||||
* @name: The name of the option (format: <plugin>:<option> or just <option>)
|
||||
* @val: (optional) the value for the option
|
||||
*
|
||||
* Modify a plugin option. If @val is given than the value of the option
|
||||
* is set (note, some options just take a boolean, so @val must be either
|
||||
* "1" or "0" or "true" or "false").
|
||||
*/
|
||||
int tep_plugin_add_option(const char *name, const char *val)
|
||||
{
|
||||
struct trace_plugin_options *op;
|
||||
char *option_str;
|
||||
char *plugin;
|
||||
|
||||
option_str = strdup(name);
|
||||
if (!option_str)
|
||||
return -ENOMEM;
|
||||
|
||||
if (parse_option_name(&option_str, &plugin) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
/* If the option exists, update the val */
|
||||
for (op = trace_plugin_options; op; op = op->next) {
|
||||
/* Both must be NULL or not NULL */
|
||||
if ((!plugin || !op->plugin) && plugin != op->plugin)
|
||||
continue;
|
||||
if (plugin && strcmp(plugin, op->plugin) != 0)
|
||||
continue;
|
||||
if (strcmp(op->option, option_str) != 0)
|
||||
continue;
|
||||
|
||||
/* update option */
|
||||
free(op->value);
|
||||
if (val) {
|
||||
op->value = strdup(val);
|
||||
if (!op->value)
|
||||
goto out_free;
|
||||
} else
|
||||
op->value = NULL;
|
||||
|
||||
/* plugin and option_str don't get freed at the end */
|
||||
free(plugin);
|
||||
free(option_str);
|
||||
|
||||
plugin = op->plugin;
|
||||
option_str = op->option;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If not found, create */
|
||||
if (!op) {
|
||||
op = malloc(sizeof(*op));
|
||||
if (!op)
|
||||
goto out_free;
|
||||
memset(op, 0, sizeof(*op));
|
||||
op->plugin = plugin;
|
||||
op->option = option_str;
|
||||
if (val) {
|
||||
op->value = strdup(val);
|
||||
if (!op->value) {
|
||||
free(op);
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
op->next = trace_plugin_options;
|
||||
trace_plugin_options = op;
|
||||
}
|
||||
|
||||
return process_option(plugin, option_str, val);
|
||||
|
||||
out_free:
|
||||
free(plugin);
|
||||
free(option_str);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void print_op_data(struct trace_seq *s, const char *name,
|
||||
const char *op)
|
||||
{
|
||||
if (op)
|
||||
trace_seq_printf(s, "%8s:\t%s\n", name, op);
|
||||
}
|
||||
|
||||
/**
|
||||
* tep_plugin_print_options - print out the registered plugin options
|
||||
* @s: The trace_seq descriptor to write the plugin options into
|
||||
*
|
||||
* Writes a list of options into trace_seq @s.
|
||||
*/
|
||||
void tep_plugin_print_options(struct trace_seq *s)
|
||||
{
|
||||
struct registered_plugin_options *reg;
|
||||
struct tep_plugin_option *op;
|
||||
|
||||
for (reg = registered_options; reg; reg = reg->next) {
|
||||
if (reg != registered_options)
|
||||
trace_seq_printf(s, "============\n");
|
||||
for (op = reg->options; op->name; op++) {
|
||||
if (op != reg->options)
|
||||
trace_seq_printf(s, "------------\n");
|
||||
print_op_data(s, "file", op->file);
|
||||
print_op_data(s, "plugin", op->plugin_alias);
|
||||
print_op_data(s, "option", op->name);
|
||||
print_op_data(s, "desc", op->description);
|
||||
print_op_data(s, "value", op->value);
|
||||
trace_seq_printf(s, "%8s:\t%d\n", "set", op->set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tep_print_plugins - print out the list of plugins loaded
|
||||
* @s: the trace_seq descripter to write to
|
||||
|
@ -273,6 +444,7 @@ load_plugin(struct tep_handle *tep, const char *path,
|
|||
const char *file, void *data)
|
||||
{
|
||||
struct tep_plugin_list **plugin_list = data;
|
||||
struct tep_plugin_option *options;
|
||||
tep_plugin_load_func func;
|
||||
struct tep_plugin_list *list;
|
||||
const char *alias;
|
||||
|
@ -297,6 +469,16 @@ load_plugin(struct tep_handle *tep, const char *path,
|
|||
if (!alias)
|
||||
alias = file;
|
||||
|
||||
options = dlsym(handle, TEP_PLUGIN_OPTIONS_NAME);
|
||||
if (options) {
|
||||
while (options->name) {
|
||||
ret = update_option(alias, options);
|
||||
if (ret < 0)
|
||||
goto out_free;
|
||||
options++;
|
||||
}
|
||||
}
|
||||
|
||||
func = dlsym(handle, TEP_PLUGIN_LOADER_NAME);
|
||||
if (!func) {
|
||||
warning("could not find func '%s' in plugin '%s'\n%s\n",
|
||||
|
@ -365,28 +547,53 @@ load_plugins_dir(struct tep_handle *tep, const char *suffix,
|
|||
closedir(dir);
|
||||
}
|
||||
|
||||
static void
|
||||
load_plugins(struct tep_handle *tep, const char *suffix,
|
||||
void (*load_plugin)(struct tep_handle *tep,
|
||||
const char *path,
|
||||
const char *name,
|
||||
void *data),
|
||||
void *data)
|
||||
/**
|
||||
* tep_load_plugins_hook - call a user specified callback to load a plugin
|
||||
* @tep: handler to traceevent context
|
||||
* @suffix: filter only plugin files with given suffix
|
||||
* @load_plugin: user specified callback, called for each plugin file
|
||||
* @data: custom context, passed to @load_plugin
|
||||
*
|
||||
* Searches for traceevent plugin files and calls @load_plugin for each
|
||||
* The order of plugins search is:
|
||||
* - Directories, specified in @tep->plugins_dir and priority TEP_PLUGIN_FIRST
|
||||
* - Directory, specified at compile time with PLUGIN_TRACEEVENT_DIR
|
||||
* - Directory, specified by environment variable TRACEEVENT_PLUGIN_DIR
|
||||
* - In user's home: ~/.local/lib/traceevent/plugins/
|
||||
* - Directories, specified in @tep->plugins_dir and priority TEP_PLUGIN_LAST
|
||||
*
|
||||
*/
|
||||
void tep_load_plugins_hook(struct tep_handle *tep, const char *suffix,
|
||||
void (*load_plugin)(struct tep_handle *tep,
|
||||
const char *path,
|
||||
const char *name,
|
||||
void *data),
|
||||
void *data)
|
||||
{
|
||||
struct tep_plugins_dir *dir = NULL;
|
||||
char *home;
|
||||
char *path;
|
||||
char *envdir;
|
||||
int ret;
|
||||
|
||||
if (tep->flags & TEP_DISABLE_PLUGINS)
|
||||
if (tep && tep->flags & TEP_DISABLE_PLUGINS)
|
||||
return;
|
||||
|
||||
if (tep)
|
||||
dir = tep->plugins_dir;
|
||||
while (dir) {
|
||||
if (dir->prio == TEP_PLUGIN_FIRST)
|
||||
load_plugins_dir(tep, suffix, dir->path,
|
||||
load_plugin, data);
|
||||
dir = dir->next;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a system plugin directory was defined,
|
||||
* check that first.
|
||||
*/
|
||||
#ifdef PLUGIN_DIR
|
||||
if (!(tep->flags & TEP_DISABLE_SYS_PLUGINS))
|
||||
if (!tep || !(tep->flags & TEP_DISABLE_SYS_PLUGINS))
|
||||
load_plugins_dir(tep, suffix, PLUGIN_DIR,
|
||||
load_plugin, data);
|
||||
#endif
|
||||
|
@ -415,6 +622,15 @@ load_plugins(struct tep_handle *tep, const char *suffix,
|
|||
|
||||
load_plugins_dir(tep, suffix, path, load_plugin, data);
|
||||
|
||||
if (tep)
|
||||
dir = tep->plugins_dir;
|
||||
while (dir) {
|
||||
if (dir->prio == TEP_PLUGIN_LAST)
|
||||
load_plugins_dir(tep, suffix, dir->path,
|
||||
load_plugin, data);
|
||||
dir = dir->next;
|
||||
}
|
||||
|
||||
free(path);
|
||||
}
|
||||
|
||||
|
@ -423,10 +639,59 @@ tep_load_plugins(struct tep_handle *tep)
|
|||
{
|
||||
struct tep_plugin_list *list = NULL;
|
||||
|
||||
load_plugins(tep, ".so", load_plugin, &list);
|
||||
tep_load_plugins_hook(tep, ".so", load_plugin, &list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* tep_add_plugin_path - Add a new plugin directory.
|
||||
* @tep: Trace event handler.
|
||||
* @path: Path to a directory. All plugin files in that
|
||||
* directory will be loaded.
|
||||
*@prio: Load priority of the plugins in that directory.
|
||||
*
|
||||
* Returns -1 in case of an error, 0 otherwise.
|
||||
*/
|
||||
int tep_add_plugin_path(struct tep_handle *tep, char *path,
|
||||
enum tep_plugin_load_priority prio)
|
||||
{
|
||||
struct tep_plugins_dir *dir;
|
||||
|
||||
if (!tep || !path)
|
||||
return -1;
|
||||
|
||||
dir = calloc(1, sizeof(*dir));
|
||||
if (!dir)
|
||||
return -1;
|
||||
|
||||
dir->path = strdup(path);
|
||||
if (!dir->path) {
|
||||
free(dir);
|
||||
return -1;
|
||||
}
|
||||
dir->prio = prio;
|
||||
dir->next = tep->plugins_dir;
|
||||
tep->plugins_dir = dir;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tep_free_plugin_paths(struct tep_handle *tep)
|
||||
{
|
||||
struct tep_plugins_dir *dir;
|
||||
|
||||
if (!tep)
|
||||
return;
|
||||
|
||||
dir = tep->plugins_dir;
|
||||
while (dir) {
|
||||
tep->plugins_dir = tep->plugins_dir->next;
|
||||
free(dir->path);
|
||||
free(dir);
|
||||
dir = tep->plugins_dir;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tep_unload_plugins(struct tep_plugin_list *plugin_list, struct tep_handle *tep)
|
||||
{
|
||||
|
|
|
@ -1,22 +1,7 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1 */
|
||||
/*
|
||||
* Copyright (C) 2012 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#ifndef _KBUFFER_H
|
||||
#define _KBUFFER_H
|
||||
|
|
|
@ -5,6 +5,8 @@ plugin_kvm-y += plugin_kvm.o
|
|||
plugin_mac80211-y += plugin_mac80211.o
|
||||
plugin_sched_switch-y += plugin_sched_switch.o
|
||||
plugin_function-y += plugin_function.o
|
||||
plugin_futex-y += plugin_futex.o
|
||||
plugin_xen-y += plugin_xen.o
|
||||
plugin_scsi-y += plugin_scsi.o
|
||||
plugin_cfg80211-y += plugin_cfg80211.o
|
||||
plugin_tlb-y += plugin_tlb.o
|
|
@ -134,9 +134,11 @@ PLUGINS += plugin_kvm.so
|
|||
PLUGINS += plugin_mac80211.so
|
||||
PLUGINS += plugin_sched_switch.so
|
||||
PLUGINS += plugin_function.so
|
||||
PLUGINS += plugin_futex.so
|
||||
PLUGINS += plugin_xen.so
|
||||
PLUGINS += plugin_scsi.so
|
||||
PLUGINS += plugin_cfg80211.so
|
||||
PLUGINS += plugin_tlb.so
|
||||
|
||||
PLUGINS := $(addprefix $(OUTPUT),$(PLUGINS))
|
||||
PLUGINS_IN := $(PLUGINS:.so=-in.o)
|
||||
|
|
|
@ -1,21 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1
|
||||
/*
|
||||
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -49,6 +34,13 @@ struct tep_plugin_option plugin_options[] =
|
|||
"Try to show function call indents, based on parents",
|
||||
.set = 1,
|
||||
},
|
||||
{
|
||||
.name = "offset",
|
||||
.plugin_alias = "ftrace",
|
||||
.description =
|
||||
"Show function names as well as their offsets",
|
||||
.set = 0,
|
||||
},
|
||||
{
|
||||
.name = NULL,
|
||||
}
|
||||
|
@ -56,6 +48,7 @@ struct tep_plugin_option plugin_options[] =
|
|||
|
||||
static struct tep_plugin_option *ftrace_parent = &plugin_options[0];
|
||||
static struct tep_plugin_option *ftrace_indent = &plugin_options[1];
|
||||
static struct tep_plugin_option *ftrace_offset = &plugin_options[2];
|
||||
|
||||
static void add_child(struct func_stack *stack, const char *child, int pos)
|
||||
{
|
||||
|
@ -123,6 +116,18 @@ static int add_and_get_index(const char *parent, const char *child, int cpu)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void show_function(struct trace_seq *s, struct tep_handle *tep,
|
||||
const char *func, unsigned long long function)
|
||||
{
|
||||
unsigned long long offset;
|
||||
|
||||
trace_seq_printf(s, "%s", func);
|
||||
if (ftrace_offset->set) {
|
||||
offset = tep_find_function_address(tep, function);
|
||||
trace_seq_printf(s, "+0x%x ", (int)(function - offset));
|
||||
}
|
||||
}
|
||||
|
||||
static int function_handler(struct trace_seq *s, struct tep_record *record,
|
||||
struct tep_event *event, void *context)
|
||||
{
|
||||
|
@ -149,14 +154,14 @@ static int function_handler(struct trace_seq *s, struct tep_record *record,
|
|||
trace_seq_printf(s, "%*s", index*3, "");
|
||||
|
||||
if (func)
|
||||
trace_seq_printf(s, "%s", func);
|
||||
show_function(s, tep, func, function);
|
||||
else
|
||||
trace_seq_printf(s, "0x%llx", function);
|
||||
|
||||
if (ftrace_parent->set) {
|
||||
trace_seq_printf(s, " <-- ");
|
||||
if (parent)
|
||||
trace_seq_printf(s, "%s", parent);
|
||||
show_function(s, tep, parent, pfunction);
|
||||
else
|
||||
trace_seq_printf(s, "0x%llx", pfunction);
|
||||
}
|
||||
|
@ -164,11 +169,93 @@ static int function_handler(struct trace_seq *s, struct tep_record *record,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
trace_stack_handler(struct trace_seq *s, struct tep_record *record,
|
||||
struct tep_event *event, void *context)
|
||||
{
|
||||
struct tep_format_field *field;
|
||||
unsigned long long addr;
|
||||
const char *func;
|
||||
int long_size;
|
||||
void *data = record->data;
|
||||
|
||||
field = tep_find_any_field(event, "caller");
|
||||
if (!field) {
|
||||
trace_seq_printf(s, "<CANT FIND FIELD %s>", "caller");
|
||||
return 0;
|
||||
}
|
||||
|
||||
trace_seq_puts(s, "<stack trace >\n");
|
||||
|
||||
long_size = tep_get_long_size(event->tep);
|
||||
|
||||
for (data += field->offset; data < record->data + record->size;
|
||||
data += long_size) {
|
||||
addr = tep_read_number(event->tep, data, long_size);
|
||||
|
||||
if ((long_size == 8 && addr == (unsigned long long)-1) ||
|
||||
((int)addr == -1))
|
||||
break;
|
||||
|
||||
func = tep_find_function(event->tep, addr);
|
||||
if (func)
|
||||
trace_seq_printf(s, "=> %s (%llx)\n", func, addr);
|
||||
else
|
||||
trace_seq_printf(s, "=> %llx\n", addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
trace_raw_data_handler(struct trace_seq *s, struct tep_record *record,
|
||||
struct tep_event *event, void *context)
|
||||
{
|
||||
struct tep_format_field *field;
|
||||
unsigned long long id;
|
||||
int long_size;
|
||||
void *data = record->data;
|
||||
|
||||
if (tep_get_field_val(s, event, "id", record, &id, 1))
|
||||
return trace_seq_putc(s, '!');
|
||||
|
||||
trace_seq_printf(s, "# %llx", id);
|
||||
|
||||
field = tep_find_any_field(event, "buf");
|
||||
if (!field) {
|
||||
trace_seq_printf(s, "<CANT FIND FIELD %s>", "buf");
|
||||
return 0;
|
||||
}
|
||||
|
||||
long_size = tep_get_long_size(event->tep);
|
||||
|
||||
for (data += field->offset; data < record->data + record->size;
|
||||
data += long_size) {
|
||||
int size = sizeof(long);
|
||||
int left = (record->data + record->size) - data;
|
||||
int i;
|
||||
|
||||
if (size > left)
|
||||
size = left;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
trace_seq_printf(s, " %02x", *(unsigned char *)(data + i));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TEP_PLUGIN_LOADER(struct tep_handle *tep)
|
||||
{
|
||||
tep_register_event_handler(tep, -1, "ftrace", "function",
|
||||
function_handler, NULL);
|
||||
|
||||
tep_register_event_handler(tep, -1, "ftrace", "kernel_stack",
|
||||
trace_stack_handler, NULL);
|
||||
|
||||
tep_register_event_handler(tep, -1, "ftrace", "raw_data",
|
||||
trace_raw_data_handler, NULL);
|
||||
|
||||
tep_plugin_add_options("ftrace", plugin_options);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1
|
||||
/*
|
||||
* Copyright (C) 2017 National Instruments Corp.
|
||||
*
|
||||
* Author: Julia Cartwright <julia@ni.com>
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <linux/futex.h>
|
||||
|
||||
#include "event-parse.h"
|
||||
|
||||
#define ARRAY_SIZE(_a) (sizeof(_a) / sizeof((_a)[0]))
|
||||
|
||||
struct futex_args {
|
||||
unsigned long long uaddr;
|
||||
unsigned long long op;
|
||||
unsigned long long val;
|
||||
unsigned long long utime; /* or val2 */
|
||||
unsigned long long uaddr2;
|
||||
unsigned long long val3;
|
||||
};
|
||||
|
||||
struct futex_op {
|
||||
const char *name;
|
||||
const char *fmt_val;
|
||||
const char *fmt_utime;
|
||||
const char *fmt_uaddr2;
|
||||
const char *fmt_val3;
|
||||
};
|
||||
|
||||
static const struct futex_op futex_op_tbl[] = {
|
||||
{ "FUTEX_WAIT", " val=0x%08llx", " utime=0x%08llx", NULL, NULL },
|
||||
{ "FUTEX_WAKE", " val=%llu", NULL, NULL, NULL },
|
||||
{ "FUTEX_FD", " val=%llu", NULL, NULL, NULL },
|
||||
{ "FUTEX_REQUEUE", " val=%llu", " val2=%llu", " uaddr2=0x%08llx", NULL },
|
||||
{ "FUTEX_CMP_REQUEUE", " val=%llu", " val2=%llu", " uaddr2=0x%08llx", " val3=0x%08llx" },
|
||||
{ "FUTEX_WAKE_OP", " val=%llu", " val2=%llu", " uaddr2=0x%08llx", " val3=0x%08llx" },
|
||||
{ "FUTEX_LOCK_PI", NULL, " utime=0x%08llx", NULL, NULL },
|
||||
{ "FUTEX_UNLOCK_PI", NULL, NULL, NULL, NULL },
|
||||
{ "FUTEX_TRYLOCK_PI", NULL, NULL, NULL, NULL },
|
||||
{ "FUTEX_WAIT_BITSET", " val=0x%08llx", " utime=0x%08llx", NULL, " val3=0x%08llx" },
|
||||
{ "FUTEX_WAKE_BITSET", " val=%llu", NULL, NULL, " val3=0x%08llx" },
|
||||
{ "FUTEX_WAIT_REQUEUE_PI", " val=0x%08llx", " utime=0x%08llx", " uaddr2=0x%08llx", " val3=0x%08llx" },
|
||||
{ "FUTEX_CMP_REQUEUE_PI", " val=%llu", " val2=%llu", " uaddr2=0x%08llx", " val3=0x%08llx" },
|
||||
};
|
||||
|
||||
|
||||
static void futex_print(struct trace_seq *s, const struct futex_args *args,
|
||||
const struct futex_op *fop)
|
||||
{
|
||||
trace_seq_printf(s, " uaddr=0x%08llx", args->uaddr);
|
||||
|
||||
if (fop->fmt_val)
|
||||
trace_seq_printf(s, fop->fmt_val, args->val);
|
||||
|
||||
if (fop->fmt_utime)
|
||||
trace_seq_printf(s,fop->fmt_utime, args->utime);
|
||||
|
||||
if (fop->fmt_uaddr2)
|
||||
trace_seq_printf(s, fop->fmt_uaddr2, args->uaddr2);
|
||||
|
||||
if (fop->fmt_val3)
|
||||
trace_seq_printf(s, fop->fmt_val3, args->val3);
|
||||
}
|
||||
|
||||
static int futex_handler(struct trace_seq *s, struct tep_record *record,
|
||||
struct tep_event *event, void *context)
|
||||
{
|
||||
const struct futex_op *fop;
|
||||
struct futex_args args;
|
||||
unsigned long long cmd;
|
||||
|
||||
if (tep_get_field_val(s, event, "uaddr", record, &args.uaddr, 1))
|
||||
return 1;
|
||||
|
||||
if (tep_get_field_val(s, event, "op", record, &args.op, 1))
|
||||
return 1;
|
||||
|
||||
if (tep_get_field_val(s, event, "val", record, &args.val, 1))
|
||||
return 1;
|
||||
|
||||
if (tep_get_field_val(s, event, "utime", record, &args.utime, 1))
|
||||
return 1;
|
||||
|
||||
if (tep_get_field_val(s, event, "uaddr2", record, &args.uaddr2, 1))
|
||||
return 1;
|
||||
|
||||
if (tep_get_field_val(s, event, "val3", record, &args.val3, 1))
|
||||
return 1;
|
||||
|
||||
cmd = args.op & FUTEX_CMD_MASK;
|
||||
if (cmd >= ARRAY_SIZE(futex_op_tbl))
|
||||
return 1;
|
||||
|
||||
fop = &futex_op_tbl[cmd];
|
||||
|
||||
trace_seq_printf(s, "op=%s", fop->name);
|
||||
|
||||
if (args.op & FUTEX_PRIVATE_FLAG)
|
||||
trace_seq_puts(s, "|FUTEX_PRIVATE_FLAG");
|
||||
|
||||
if (args.op & FUTEX_CLOCK_REALTIME)
|
||||
trace_seq_puts(s, "|FUTEX_CLOCK_REALTIME");
|
||||
|
||||
futex_print(s, &args, fop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TEP_PLUGIN_LOADER(struct tep_handle *tep)
|
||||
{
|
||||
tep_register_event_handler(tep, -1, "syscalls", "sys_enter_futex",
|
||||
futex_handler, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TEP_PLUGIN_UNLOADER(struct tep_handle *tep)
|
||||
{
|
||||
tep_unregister_event_handler(tep, -1, "syscalls", "sys_enter_futex",
|
||||
futex_handler, NULL);
|
||||
}
|
|
@ -1,22 +1,7 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1
|
||||
/*
|
||||
* Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
||||
* Copyright (C) 2009 Johannes Berg <johannes@sipsolutions.net>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
@ -1,21 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1
|
||||
/*
|
||||
* Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
@ -1,21 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1
|
||||
/*
|
||||
* Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
@ -1,21 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1
|
||||
/*
|
||||
* Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -155,7 +140,23 @@ static const char *disassemble(unsigned char *insn, int len, uint64_t rip,
|
|||
_ER(EXIT_WRITE_DR5, 0x035) \
|
||||
_ER(EXIT_WRITE_DR6, 0x036) \
|
||||
_ER(EXIT_WRITE_DR7, 0x037) \
|
||||
_ER(EXIT_EXCP_BASE, 0x040) \
|
||||
_ER(EXIT_EXCP_DE, 0x040) \
|
||||
_ER(EXIT_EXCP_DB, 0x041) \
|
||||
_ER(EXIT_EXCP_BP, 0x043) \
|
||||
_ER(EXIT_EXCP_OF, 0x044) \
|
||||
_ER(EXIT_EXCP_BR, 0x045) \
|
||||
_ER(EXIT_EXCP_UD, 0x046) \
|
||||
_ER(EXIT_EXCP_NM, 0x047) \
|
||||
_ER(EXIT_EXCP_DF, 0x048) \
|
||||
_ER(EXIT_EXCP_TS, 0x04a) \
|
||||
_ER(EXIT_EXCP_NP, 0x04b) \
|
||||
_ER(EXIT_EXCP_SS, 0x04c) \
|
||||
_ER(EXIT_EXCP_GP, 0x04d) \
|
||||
_ER(EXIT_EXCP_PF, 0x04e) \
|
||||
_ER(EXIT_EXCP_MF, 0x050) \
|
||||
_ER(EXIT_EXCP_AC, 0x051) \
|
||||
_ER(EXIT_EXCP_MC, 0x052) \
|
||||
_ER(EXIT_EXCP_XF, 0x053) \
|
||||
_ER(EXIT_INTR, 0x060) \
|
||||
_ER(EXIT_NMI, 0x061) \
|
||||
_ER(EXIT_SMI, 0x062) \
|
||||
|
@ -201,7 +202,10 @@ static const char *disassemble(unsigned char *insn, int len, uint64_t rip,
|
|||
_ER(EXIT_MONITOR, 0x08a) \
|
||||
_ER(EXIT_MWAIT, 0x08b) \
|
||||
_ER(EXIT_MWAIT_COND, 0x08c) \
|
||||
_ER(EXIT_NPF, 0x400) \
|
||||
_ER(EXIT_XSETBV, 0x08d) \
|
||||
_ER(EXIT_NPF, 0x400) \
|
||||
_ER(EXIT_AVIC_INCOMPLETE_IPI, 0x401) \
|
||||
_ER(EXIT_AVIC_UNACCELERATED_ACCESS, 0x402) \
|
||||
_ER(EXIT_ERR, -1)
|
||||
|
||||
#define _ER(reason, val) { #reason, val },
|
||||
|
@ -241,7 +245,7 @@ static const char *find_exit_reason(unsigned isa, int val)
|
|||
}
|
||||
if (!strings)
|
||||
return "UNKNOWN-ISA";
|
||||
for (i = 0; strings[i].val >= 0; i++)
|
||||
for (i = 0; strings[i].str; i++)
|
||||
if (strings[i].val == val)
|
||||
break;
|
||||
|
||||
|
|
|
@ -1,21 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1
|
||||
/*
|
||||
* Copyright (C) 2009 Johannes Berg <johannes@sipsolutions.net>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
@ -1,21 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1
|
||||
/*
|
||||
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// SPDX-License-Identifier: LGPL-2.1
|
||||
/*
|
||||
* Copyright (C) 2015 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "event-parse.h"
|
||||
|
||||
enum tlb_flush_reason {
|
||||
TLB_FLUSH_ON_TASK_SWITCH,
|
||||
TLB_REMOTE_SHOOTDOWN,
|
||||
TLB_LOCAL_SHOOTDOWN,
|
||||
TLB_LOCAL_MM_SHOOTDOWN,
|
||||
NR_TLB_FLUSH_REASONS,
|
||||
};
|
||||
|
||||
static int tlb_flush_handler(struct trace_seq *s, struct tep_record *record,
|
||||
struct tep_event *event, void *context)
|
||||
{
|
||||
unsigned long long val;
|
||||
|
||||
trace_seq_printf(s, "pages=");
|
||||
|
||||
tep_print_num_field(s, "%ld", event, "pages", record, 1);
|
||||
|
||||
if (tep_get_field_val(s, event, "reason", record, &val, 1) < 0)
|
||||
return -1;
|
||||
|
||||
trace_seq_puts(s, " reason=");
|
||||
|
||||
switch (val) {
|
||||
case TLB_FLUSH_ON_TASK_SWITCH:
|
||||
trace_seq_puts(s, "flush on task switch");
|
||||
break;
|
||||
case TLB_REMOTE_SHOOTDOWN:
|
||||
trace_seq_puts(s, "remote shootdown");
|
||||
break;
|
||||
case TLB_LOCAL_SHOOTDOWN:
|
||||
trace_seq_puts(s, "local shootdown");
|
||||
break;
|
||||
case TLB_LOCAL_MM_SHOOTDOWN:
|
||||
trace_seq_puts(s, "local mm shootdown");
|
||||
break;
|
||||
}
|
||||
|
||||
trace_seq_printf(s, " (%lld)", val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TEP_PLUGIN_LOADER(struct tep_handle *tep)
|
||||
{
|
||||
tep_register_event_handler(tep, -1, "tlb", "tlb_flush",
|
||||
tlb_flush_handler, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TEP_PLUGIN_UNLOADER(struct tep_handle *tep)
|
||||
{
|
||||
tep_unregister_event_handler(tep, -1,
|
||||
"tlb", "tlb_flush",
|
||||
tlb_flush_handler, NULL);
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
l synthesize last branch entries (use with i or x)
|
||||
L synthesize last branch entries on existing event records
|
||||
s skip initial number of events
|
||||
q quicker (less detailed) decoding
|
||||
|
||||
The default is all events i.e. the same as --itrace=ibxwpe,
|
||||
except for perf script where it is --itrace=ce
|
||||
|
@ -47,3 +48,16 @@
|
|||
--itrace=i0nss1000000
|
||||
|
||||
skips the first million instructions.
|
||||
|
||||
The 'e' option may be followed by flags which affect what errors will or
|
||||
will not be reported. Each flag must be preceded by either '+' or '-'.
|
||||
The flags are:
|
||||
o overflow
|
||||
l trace data lost
|
||||
|
||||
If supported, the 'd' option may be followed by flags which affect what
|
||||
debug messages will or will not be logged. Each flag must be preceded
|
||||
by either '+' or '-'. The flags are:
|
||||
a all perf events
|
||||
|
||||
If supported, the 'q' option may be repeated to increase the effect.
|
||||
|
|
|
@ -49,6 +49,9 @@ SUBSYSTEM
|
|||
'sched'::
|
||||
Scheduler and IPC mechanisms.
|
||||
|
||||
'syscall'::
|
||||
System call performance (throughput).
|
||||
|
||||
'mem'::
|
||||
Memory access performance.
|
||||
|
||||
|
@ -137,6 +140,14 @@ Example of *pipe*
|
|||
59004 ops/sec
|
||||
---------------------
|
||||
|
||||
SUITES FOR 'syscall'
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
*basic*::
|
||||
Suite for evaluating performance of core system call throughput (both usecs/op and ops/sec metrics).
|
||||
This uses a single thread simply doing getppid(2), which is a simple syscall where the result is not
|
||||
cached by glibc.
|
||||
|
||||
|
||||
SUITES FOR 'mem'
|
||||
~~~~~~~~~~~~~~~~
|
||||
*memcpy*::
|
||||
|
|
|
@ -27,6 +27,9 @@ OPTIONS for 'convert'
|
|||
--to-ctf::
|
||||
Triggers the CTF conversion, specify the path of CTF data directory.
|
||||
|
||||
--tod::
|
||||
Convert time to wall clock time.
|
||||
|
||||
-i::
|
||||
Specify input perf data file path.
|
||||
|
||||
|
|
|
@ -825,6 +825,7 @@ The letters are:
|
|||
l synthesize last branch entries (use with i or x)
|
||||
L synthesize last branch entries on existing event records
|
||||
s skip initial number of events
|
||||
q quicker (less detailed) decoding
|
||||
|
||||
"Instructions" events look like they were recorded by "perf record -e
|
||||
instructions".
|
||||
|
@ -871,11 +872,24 @@ Developer Manuals.
|
|||
|
||||
Error events show where the decoder lost the trace. Error events
|
||||
are quite important. Users must know if what they are seeing is a complete
|
||||
picture or not.
|
||||
picture or not. The "e" option may be followed by flags which affect what errors
|
||||
will or will not be reported. Each flag must be preceded by either '+' or '-'.
|
||||
The flags supported by Intel PT are:
|
||||
-o Suppress overflow errors
|
||||
-l Suppress trace data lost errors
|
||||
For example, for errors but not overflow or data lost errors:
|
||||
|
||||
--itrace=e-o-l
|
||||
|
||||
The "d" option will cause the creation of a file "intel_pt.log" containing all
|
||||
decoded packets and instructions. Note that this option slows down the decoder
|
||||
and that the resulting file may be very large.
|
||||
and that the resulting file may be very large. The "d" option may be followed
|
||||
by flags which affect what debug messages will or will not be logged. Each flag
|
||||
must be preceded by either '+' or '-'. The flags support by Intel PT are:
|
||||
-a Suppress logging of perf events
|
||||
+a Log all perf events
|
||||
By default, logged perf events are filtered by any specified time ranges, but
|
||||
flag +a overrides that.
|
||||
|
||||
In addition, the period of the "instructions" event can be specified. e.g.
|
||||
|
||||
|
@ -956,6 +970,51 @@ at the beginning. This is useful to ignore initialization code.
|
|||
|
||||
skips the first million instructions.
|
||||
|
||||
The q option changes the way the trace is decoded. The decoding is much faster
|
||||
but much less detailed. Specifically, with the q option, the decoder does not
|
||||
decode TNT packets, and does not walk object code, but gets the ip from FUP and
|
||||
TIP packets. The q option can be used with the b and i options but the period
|
||||
is not used. The q option decodes more quickly, but is useful only if the
|
||||
control flow of interest is represented or indicated by FUP, TIP, TIP.PGE, or
|
||||
TIP.PGD packets (refer below). However the q option could be used to find time
|
||||
ranges that could then be decoded fully using the --time option.
|
||||
|
||||
What will *not* be decoded with the (single) q option:
|
||||
|
||||
- direct calls and jmps
|
||||
- conditional branches
|
||||
- non-branch instructions
|
||||
|
||||
What *will* be decoded with the (single) q option:
|
||||
|
||||
- asynchronous branches such as interrupts
|
||||
- indirect branches
|
||||
- function return target address *if* the noretcomp config term (refer
|
||||
config terms section) was used
|
||||
- start of (control-flow) tracing
|
||||
- end of (control-flow) tracing, if it is not out of context
|
||||
- power events, ptwrite, transaction start and abort
|
||||
- instruction pointer associated with PSB packets
|
||||
|
||||
Note the q option does not specify what events will be synthesized e.g. the p
|
||||
option must be used also to show power events.
|
||||
|
||||
Repeating the q option (double-q i.e. qq) results in even faster decoding and even
|
||||
less detail. The decoder decodes only extended PSB (PSB+) packets, getting the
|
||||
instruction pointer if there is a FUP packet within PSB+ (i.e. between PSB and
|
||||
PSBEND). Note PSB packets occur regularly in the trace based on the psb_period
|
||||
config term (refer config terms section). There will be a FUP packet if the
|
||||
PSB+ occurs while control flow is being traced.
|
||||
|
||||
What will *not* be decoded with the qq option:
|
||||
|
||||
- everything except instruction pointer associated with PSB packets
|
||||
|
||||
What *will* be decoded with the qq option:
|
||||
|
||||
- instruction pointer associated with PSB packets
|
||||
|
||||
|
||||
dump option
|
||||
~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -119,6 +119,7 @@ It's also possible to use pmu syntax:
|
|||
|
||||
perf record -e r1a8 -a sleep 1
|
||||
perf record -e cpu/r1a8/ ...
|
||||
perf record -e cpu/r0x1a8/ ...
|
||||
|
||||
You should refer to the processor specific documentation for getting these
|
||||
details. Some of them are referenced in the SEE ALSO section below.
|
||||
|
|
|
@ -407,8 +407,9 @@ if combined with -a or -C options.
|
|||
|
||||
-D::
|
||||
--delay=::
|
||||
After starting the program, wait msecs before measuring. This is useful to
|
||||
filter out the startup phase of the program, which is often very different.
|
||||
After starting the program, wait msecs before measuring (-1: start with events
|
||||
disabled). This is useful to filter out the startup phase of the program, which
|
||||
is often very different.
|
||||
|
||||
-I::
|
||||
--intr-regs::
|
||||
|
@ -626,6 +627,45 @@ option. The -e option and this one can be mixed and matched. Events
|
|||
can be grouped using the {} notation.
|
||||
endif::HAVE_LIBPFM[]
|
||||
|
||||
--control fd:ctl-fd[,ack-fd]
|
||||
Listen on ctl-fd descriptor for command to control measurement ('enable': enable events,
|
||||
'disable': disable events). Measurements can be started with events disabled using
|
||||
--delay=-1 option. Optionally send control command completion ('ack\n') to ack-fd descriptor
|
||||
to synchronize with the controlling process. Example of bash shell script to enable and
|
||||
disable events during measurements:
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
ctl_dir=/tmp/
|
||||
|
||||
ctl_fifo=${ctl_dir}perf_ctl.fifo
|
||||
test -p ${ctl_fifo} && unlink ${ctl_fifo}
|
||||
mkfifo ${ctl_fifo}
|
||||
exec {ctl_fd}<>${ctl_fifo}
|
||||
|
||||
ctl_ack_fifo=${ctl_dir}perf_ctl_ack.fifo
|
||||
test -p ${ctl_ack_fifo} && unlink ${ctl_ack_fifo}
|
||||
mkfifo ${ctl_ack_fifo}
|
||||
exec {ctl_fd_ack}<>${ctl_ack_fifo}
|
||||
|
||||
perf record -D -1 -e cpu-cycles -a \
|
||||
--control fd:${ctl_fd},${ctl_fd_ack} \
|
||||
-- sleep 30 &
|
||||
perf_pid=$!
|
||||
|
||||
sleep 5 && echo 'enable' >&${ctl_fd} && read -u ${ctl_fd_ack} e1 && echo "enabled(${e1})"
|
||||
sleep 10 && echo 'disable' >&${ctl_fd} && read -u ${ctl_fd_ack} d1 && echo "disabled(${d1})"
|
||||
|
||||
exec {ctl_fd_ack}>&-
|
||||
unlink ${ctl_ack_fifo}
|
||||
|
||||
exec {ctl_fd}>&-
|
||||
unlink ${ctl_fifo}
|
||||
|
||||
wait -n ${perf_pid}
|
||||
exit $?
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-list[1], linkperf:perf-intel-pt[1]
|
||||
|
|
|
@ -322,6 +322,10 @@ OPTIONS
|
|||
--show-cgroup-events
|
||||
Display cgroup events i.e. events of type PERF_RECORD_CGROUP.
|
||||
|
||||
--show-text-poke-events
|
||||
Display text poke events i.e. events of type PERF_RECORD_TEXT_POKE and
|
||||
PERF_RECORD_KSYMBOL.
|
||||
|
||||
--demangle::
|
||||
Demangle symbol names to human readable form. It's enabled by default,
|
||||
disable with --no-demangle.
|
||||
|
|
|
@ -176,6 +176,45 @@ with it. --append may be used here. Examples:
|
|||
3>results perf stat --log-fd 3 -- $cmd
|
||||
3>>results perf stat --log-fd 3 --append -- $cmd
|
||||
|
||||
--control fd:ctl-fd[,ack-fd]
|
||||
Listen on ctl-fd descriptor for command to control measurement ('enable': enable events,
|
||||
'disable': disable events). Measurements can be started with events disabled using
|
||||
--delay=-1 option. Optionally send control command completion ('ack\n') to ack-fd descriptor
|
||||
to synchronize with the controlling process. Example of bash shell script to enable and
|
||||
disable events during measurements:
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
ctl_dir=/tmp/
|
||||
|
||||
ctl_fifo=${ctl_dir}perf_ctl.fifo
|
||||
test -p ${ctl_fifo} && unlink ${ctl_fifo}
|
||||
mkfifo ${ctl_fifo}
|
||||
exec {ctl_fd}<>${ctl_fifo}
|
||||
|
||||
ctl_ack_fifo=${ctl_dir}perf_ctl_ack.fifo
|
||||
test -p ${ctl_ack_fifo} && unlink ${ctl_ack_fifo}
|
||||
mkfifo ${ctl_ack_fifo}
|
||||
exec {ctl_fd_ack}<>${ctl_ack_fifo}
|
||||
|
||||
perf stat -D -1 -e cpu-cycles -a -I 1000 \
|
||||
--control fd:${ctl_fd},${ctl_fd_ack} \
|
||||
-- sleep 30 &
|
||||
perf_pid=$!
|
||||
|
||||
sleep 5 && echo 'enable' >&${ctl_fd} && read -u ${ctl_fd_ack} e1 && echo "enabled(${e1})"
|
||||
sleep 10 && echo 'disable' >&${ctl_fd} && read -u ${ctl_fd_ack} d1 && echo "disabled(${d1})"
|
||||
|
||||
exec {ctl_fd_ack}>&-
|
||||
unlink ${ctl_ack_fifo}
|
||||
|
||||
exec {ctl_fd}>&-
|
||||
unlink ${ctl_fifo}
|
||||
|
||||
wait -n ${perf_pid}
|
||||
exit $?
|
||||
|
||||
|
||||
--pre::
|
||||
--post::
|
||||
Pre and post measurement hooks, e.g.:
|
||||
|
@ -238,8 +277,9 @@ mode, use --per-node in addition to -a. (system-wide).
|
|||
|
||||
-D msecs::
|
||||
--delay msecs::
|
||||
After starting the program, wait msecs before measuring. This is useful to
|
||||
filter out the startup phase of the program, which is often very different.
|
||||
After starting the program, wait msecs before measuring (-1: start with events
|
||||
disabled). This is useful to filter out the startup phase of the program,
|
||||
which is often very different.
|
||||
|
||||
-T::
|
||||
--transaction::
|
||||
|
|
|
@ -389,6 +389,19 @@ struct {
|
|||
Example:
|
||||
cpu pmu capabilities: branches=32, max_precise=3, pmu_name=icelake
|
||||
|
||||
HEADER_CLOCK_DATA = 29,
|
||||
|
||||
Contains clock id and its reference time together with wall clock
|
||||
time taken at the 'same time', both values are in nanoseconds.
|
||||
The format of data is as below.
|
||||
|
||||
struct {
|
||||
u32 version; /* version = 1 */
|
||||
u32 clockid;
|
||||
u64 wall_clock_ns;
|
||||
u64 clockid_time_ns;
|
||||
};
|
||||
|
||||
other bits are reserved and should ignored for now
|
||||
HEADER_FEAT_BITS = 256,
|
||||
|
||||
|
|
|
@ -57,17 +57,15 @@ struct auxtrace_record
|
|||
struct evsel *evsel;
|
||||
bool found_etm = false;
|
||||
struct perf_pmu *found_spe = NULL;
|
||||
static struct perf_pmu **arm_spe_pmus = NULL;
|
||||
static int nr_spes = 0;
|
||||
struct perf_pmu **arm_spe_pmus = NULL;
|
||||
int nr_spes = 0;
|
||||
int i = 0;
|
||||
|
||||
if (!evlist)
|
||||
return NULL;
|
||||
|
||||
cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
|
||||
|
||||
if (!arm_spe_pmus)
|
||||
arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err);
|
||||
arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err);
|
||||
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
if (cs_etm_pmu &&
|
||||
|
@ -84,6 +82,7 @@ struct auxtrace_record
|
|||
}
|
||||
}
|
||||
}
|
||||
free(arm_spe_pmus);
|
||||
|
||||
if (found_etm && found_spe) {
|
||||
pr_err("Concurrent ARM Coresight ETM and SPE operation not currently supported\n");
|
||||
|
|
|
@ -243,10 +243,10 @@ static int cs_etm_set_sink_attr(struct perf_pmu *pmu,
|
|||
}
|
||||
|
||||
/*
|
||||
* No sink was provided on the command line - for _now_ treat
|
||||
* this as an error.
|
||||
* No sink was provided on the command line - allow the CoreSight
|
||||
* system to look for a default
|
||||
*/
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs_etm_recording_options(struct auxtrace_record *itr,
|
||||
|
|
|
@ -64,7 +64,13 @@ static const char *reg_names[] = {
|
|||
[PERF_REG_POWERPC_DAR] = "dar",
|
||||
[PERF_REG_POWERPC_DSISR] = "dsisr",
|
||||
[PERF_REG_POWERPC_SIER] = "sier",
|
||||
[PERF_REG_POWERPC_MMCRA] = "mmcra"
|
||||
[PERF_REG_POWERPC_MMCRA] = "mmcra",
|
||||
[PERF_REG_POWERPC_MMCR0] = "mmcr0",
|
||||
[PERF_REG_POWERPC_MMCR1] = "mmcr1",
|
||||
[PERF_REG_POWERPC_MMCR2] = "mmcr2",
|
||||
[PERF_REG_POWERPC_MMCR3] = "mmcr3",
|
||||
[PERF_REG_POWERPC_SIER2] = "sier2",
|
||||
[PERF_REG_POWERPC_SIER3] = "sier3",
|
||||
};
|
||||
|
||||
static inline const char *perf_reg_name(int id)
|
||||
|
|
|
@ -7,17 +7,10 @@
|
|||
#include <string.h>
|
||||
#include <linux/stringify.h>
|
||||
#include "header.h"
|
||||
#include "utils_header.h"
|
||||
#include "metricgroup.h"
|
||||
#include <api/fs/fs.h>
|
||||
|
||||
#define mfspr(rn) ({unsigned long rval; \
|
||||
asm volatile("mfspr %0," __stringify(rn) \
|
||||
: "=r" (rval)); rval; })
|
||||
|
||||
#define SPRN_PVR 0x11F /* Processor Version Register */
|
||||
#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */
|
||||
#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revison field */
|
||||
|
||||
int
|
||||
get_cpuid(char *buffer, size_t sz)
|
||||
{
|
||||
|
|
|
@ -6,9 +6,16 @@
|
|||
|
||||
#include "../../../util/perf_regs.h"
|
||||
#include "../../../util/debug.h"
|
||||
#include "../../../util/event.h"
|
||||
#include "../../../util/header.h"
|
||||
#include "../../../perf-sys.h"
|
||||
#include "utils_header.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define PVR_POWER9 0x004E
|
||||
#define PVR_POWER10 0x0080
|
||||
|
||||
const struct sample_reg sample_reg_masks[] = {
|
||||
SMPL_REG(r0, PERF_REG_POWERPC_R0),
|
||||
SMPL_REG(r1, PERF_REG_POWERPC_R1),
|
||||
|
@ -55,6 +62,12 @@ const struct sample_reg sample_reg_masks[] = {
|
|||
SMPL_REG(dsisr, PERF_REG_POWERPC_DSISR),
|
||||
SMPL_REG(sier, PERF_REG_POWERPC_SIER),
|
||||
SMPL_REG(mmcra, PERF_REG_POWERPC_MMCRA),
|
||||
SMPL_REG(mmcr0, PERF_REG_POWERPC_MMCR0),
|
||||
SMPL_REG(mmcr1, PERF_REG_POWERPC_MMCR1),
|
||||
SMPL_REG(mmcr2, PERF_REG_POWERPC_MMCR2),
|
||||
SMPL_REG(mmcr3, PERF_REG_POWERPC_MMCR3),
|
||||
SMPL_REG(sier2, PERF_REG_POWERPC_SIER2),
|
||||
SMPL_REG(sier3, PERF_REG_POWERPC_SIER3),
|
||||
SMPL_REG_END
|
||||
};
|
||||
|
||||
|
@ -163,3 +176,45 @@ int arch_sdt_arg_parse_op(char *old_op, char **new_op)
|
|||
|
||||
return SDT_ARG_VALID;
|
||||
}
|
||||
|
||||
uint64_t arch__intr_reg_mask(void)
|
||||
{
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.config = PERF_COUNT_HW_CPU_CYCLES,
|
||||
.sample_type = PERF_SAMPLE_REGS_INTR,
|
||||
.precise_ip = 1,
|
||||
.disabled = 1,
|
||||
.exclude_kernel = 1,
|
||||
};
|
||||
int fd;
|
||||
u32 version;
|
||||
u64 extended_mask = 0, mask = PERF_REGS_MASK;
|
||||
|
||||
/*
|
||||
* Get the PVR value to set the extended
|
||||
* mask specific to platform.
|
||||
*/
|
||||
version = (((mfspr(SPRN_PVR)) >> 16) & 0xFFFF);
|
||||
if (version == PVR_POWER9)
|
||||
extended_mask = PERF_REG_PMU_MASK_300;
|
||||
else if (version == PVR_POWER10)
|
||||
extended_mask = PERF_REG_PMU_MASK_31;
|
||||
else
|
||||
return mask;
|
||||
|
||||
attr.sample_regs_intr = extended_mask;
|
||||
attr.sample_period = 1;
|
||||
event_attr_init(&attr);
|
||||
|
||||
/*
|
||||
* check if the pmu supports perf extended regs, before
|
||||
* returning the register mask to sample.
|
||||
*/
|
||||
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
mask |= extended_mask;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __PERF_UTIL_HEADER_H
|
||||
#define __PERF_UTIL_HEADER_H
|
||||
|
||||
#include <linux/stringify.h>
|
||||
|
||||
#define mfspr(rn) ({unsigned long rval; \
|
||||
asm volatile("mfspr %0," __stringify(rn) \
|
||||
: "=r" (rval)); rval; })
|
||||
|
||||
#define SPRN_PVR 0x11F /* Processor Version Register */
|
||||
#define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */
|
||||
#define PVR_REV(pvr) (((pvr) >> 0) & 0xFFFF) /* Revison field */
|
||||
|
||||
#endif /* __PERF_UTIL_HEADER_H */
|
|
@ -357,6 +357,7 @@
|
|||
433 common fspick sys_fspick
|
||||
434 common pidfd_open sys_pidfd_open
|
||||
435 common clone3 sys_clone3
|
||||
436 common close_range sys_close_range
|
||||
437 common openat2 sys_openat2
|
||||
438 common pidfd_getfd sys_pidfd_getfd
|
||||
439 common faccessat2 sys_faccessat2
|
||||
|
|
|
@ -837,6 +837,10 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
|
|||
}
|
||||
}
|
||||
|
||||
if (have_timing_info && !intel_pt_evsel->core.attr.exclude_kernel &&
|
||||
perf_can_record_text_poke_events() && perf_can_record_cpu_wide())
|
||||
opts->text_poke = true;
|
||||
|
||||
if (intel_pt_evsel) {
|
||||
/*
|
||||
* To obtain the auxtrace buffer file descriptor, the auxtrace
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
perf-y += sched-messaging.o
|
||||
perf-y += sched-pipe.o
|
||||
perf-y += syscall.o
|
||||
perf-y += mem-functions.o
|
||||
perf-y += futex-hash.o
|
||||
perf-y += futex-wake.o
|
||||
|
@ -10,6 +11,7 @@ perf-y += epoll-wait.o
|
|||
perf-y += epoll-ctl.o
|
||||
perf-y += synthesize.o
|
||||
perf-y += kallsyms-parse.o
|
||||
perf-y += find-bit-bench.o
|
||||
|
||||
perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-lib.o
|
||||
perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o
|
||||
|
|
|
@ -33,8 +33,10 @@ extern struct timeval bench__start, bench__end, bench__runtime;
|
|||
int bench_numa(int argc, const char **argv);
|
||||
int bench_sched_messaging(int argc, const char **argv);
|
||||
int bench_sched_pipe(int argc, const char **argv);
|
||||
int bench_syscall_basic(int argc, const char **argv);
|
||||
int bench_mem_memcpy(int argc, const char **argv);
|
||||
int bench_mem_memset(int argc, const char **argv);
|
||||
int bench_mem_find_bit(int argc, const char **argv);
|
||||
int bench_futex_hash(int argc, const char **argv);
|
||||
int bench_futex_wake(int argc, const char **argv);
|
||||
int bench_futex_wake_parallel(int argc, const char **argv);
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Benchmark find_next_bit and related bit operations.
|
||||
*
|
||||
* Copyright 2020 Google LLC.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include "bench.h"
|
||||
#include "../util/stat.h"
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/time64.h>
|
||||
#include <subcmd/parse-options.h>
|
||||
|
||||
static unsigned int outer_iterations = 5;
|
||||
static unsigned int inner_iterations = 100000;
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_UINTEGER('i', "outer-iterations", &outer_iterations,
|
||||
"Number of outerer iterations used"),
|
||||
OPT_UINTEGER('j', "inner-iterations", &inner_iterations,
|
||||
"Number of outerer iterations used"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static const char *const bench_usage[] = {
|
||||
"perf bench mem find_bit <options>",
|
||||
NULL
|
||||
};
|
||||
|
||||
static unsigned int accumulator;
|
||||
static unsigned int use_of_val;
|
||||
|
||||
static noinline void workload(int val)
|
||||
{
|
||||
use_of_val += val;
|
||||
accumulator++;
|
||||
}
|
||||
|
||||
#if (defined(__i386__) || defined(__x86_64__)) && defined(__GCC_ASM_FLAG_OUTPUTS__)
|
||||
static bool asm_test_bit(long nr, const unsigned long *addr)
|
||||
{
|
||||
bool oldbit;
|
||||
|
||||
asm volatile("bt %2,%1"
|
||||
: "=@ccc" (oldbit)
|
||||
: "m" (*(unsigned long *)addr), "Ir" (nr) : "memory");
|
||||
|
||||
return oldbit;
|
||||
}
|
||||
#else
|
||||
#define asm_test_bit test_bit
|
||||
#endif
|
||||
|
||||
static int do_for_each_set_bit(unsigned int num_bits)
|
||||
{
|
||||
unsigned long *to_test = bitmap_alloc(num_bits);
|
||||
struct timeval start, end, diff;
|
||||
u64 runtime_us;
|
||||
struct stats fb_time_stats, tb_time_stats;
|
||||
double time_average, time_stddev;
|
||||
unsigned int bit, i, j;
|
||||
unsigned int set_bits, skip;
|
||||
unsigned int old;
|
||||
|
||||
init_stats(&fb_time_stats);
|
||||
init_stats(&tb_time_stats);
|
||||
|
||||
for (set_bits = 1; set_bits <= num_bits; set_bits <<= 1) {
|
||||
bitmap_zero(to_test, num_bits);
|
||||
skip = num_bits / set_bits;
|
||||
for (i = 0; i < num_bits; i += skip)
|
||||
set_bit(i, to_test);
|
||||
|
||||
for (i = 0; i < outer_iterations; i++) {
|
||||
old = accumulator;
|
||||
gettimeofday(&start, NULL);
|
||||
for (j = 0; j < inner_iterations; j++) {
|
||||
for_each_set_bit(bit, to_test, num_bits)
|
||||
workload(bit);
|
||||
}
|
||||
gettimeofday(&end, NULL);
|
||||
assert(old + (inner_iterations * set_bits) == accumulator);
|
||||
timersub(&end, &start, &diff);
|
||||
runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
|
||||
update_stats(&fb_time_stats, runtime_us);
|
||||
|
||||
old = accumulator;
|
||||
gettimeofday(&start, NULL);
|
||||
for (j = 0; j < inner_iterations; j++) {
|
||||
for (bit = 0; bit < num_bits; bit++) {
|
||||
if (asm_test_bit(bit, to_test))
|
||||
workload(bit);
|
||||
}
|
||||
}
|
||||
gettimeofday(&end, NULL);
|
||||
assert(old + (inner_iterations * set_bits) == accumulator);
|
||||
timersub(&end, &start, &diff);
|
||||
runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
|
||||
update_stats(&tb_time_stats, runtime_us);
|
||||
}
|
||||
|
||||
printf("%d operations %d bits set of %d bits\n",
|
||||
inner_iterations, set_bits, num_bits);
|
||||
time_average = avg_stats(&fb_time_stats);
|
||||
time_stddev = stddev_stats(&fb_time_stats);
|
||||
printf(" Average for_each_set_bit took: %.3f usec (+- %.3f usec)\n",
|
||||
time_average, time_stddev);
|
||||
time_average = avg_stats(&tb_time_stats);
|
||||
time_stddev = stddev_stats(&tb_time_stats);
|
||||
printf(" Average test_bit loop took: %.3f usec (+- %.3f usec)\n",
|
||||
time_average, time_stddev);
|
||||
|
||||
if (use_of_val == accumulator) /* Try to avoid compiler tricks. */
|
||||
printf("\n");
|
||||
}
|
||||
bitmap_free(to_test);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bench_mem_find_bit(int argc, const char **argv)
|
||||
{
|
||||
int err = 0, i;
|
||||
|
||||
argc = parse_options(argc, argv, options, bench_usage, 0);
|
||||
if (argc) {
|
||||
usage_with_options(bench_usage, options);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (i = 1; i <= 2048; i <<= 1)
|
||||
do_for_each_set_bit(i);
|
||||
|
||||
return err;
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
*
|
||||
* syscall.c
|
||||
*
|
||||
* syscall: Benchmark for system call performance
|
||||
*/
|
||||
#include "../perf.h"
|
||||
#include "../util/util.h"
|
||||
#include <subcmd/parse-options.h>
|
||||
#include "../builtin.h"
|
||||
#include "bench.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define LOOPS_DEFAULT 10000000
|
||||
static int loops = LOOPS_DEFAULT;
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_INTEGER('l', "loop", &loops, "Specify number of loops"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static const char * const bench_syscall_usage[] = {
|
||||
"perf bench syscall <options>",
|
||||
NULL
|
||||
};
|
||||
|
||||
int bench_syscall_basic(int argc, const char **argv)
|
||||
{
|
||||
struct timeval start, stop, diff;
|
||||
unsigned long long result_usec = 0;
|
||||
int i;
|
||||
|
||||
argc = parse_options(argc, argv, options, bench_syscall_usage, 0);
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
|
||||
for (i = 0; i < loops; i++)
|
||||
getppid();
|
||||
|
||||
gettimeofday(&stop, NULL);
|
||||
timersub(&stop, &start, &diff);
|
||||
|
||||
switch (bench_format) {
|
||||
case BENCH_FORMAT_DEFAULT:
|
||||
printf("# Executed %'d getppid() calls\n", loops);
|
||||
|
||||
result_usec = diff.tv_sec * 1000000;
|
||||
result_usec += diff.tv_usec;
|
||||
|
||||
printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
|
||||
diff.tv_sec,
|
||||
(unsigned long) (diff.tv_usec/1000));
|
||||
|
||||
printf(" %14lf usecs/op\n",
|
||||
(double)result_usec / (double)loops);
|
||||
printf(" %'14d ops/sec\n",
|
||||
(int)((double)loops /
|
||||
((double)result_usec / (double)1000000)));
|
||||
break;
|
||||
|
||||
case BENCH_FORMAT_SIMPLE:
|
||||
printf("%lu.%03lu\n",
|
||||
diff.tv_sec,
|
||||
(unsigned long) (diff.tv_usec / 1000));
|
||||
break;
|
||||
|
||||
default:
|
||||
/* reaching here is something disaster */
|
||||
fprintf(stderr, "Unknown format:%d\n", bench_format);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
* Available benchmark collection list:
|
||||
*
|
||||
* sched ... scheduler and IPC performance
|
||||
* syscall ... System call performance
|
||||
* mem ... memory access performance
|
||||
* numa ... NUMA scheduling and MM performance
|
||||
* futex ... Futex performance
|
||||
|
@ -49,9 +50,16 @@ static struct bench sched_benchmarks[] = {
|
|||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static struct bench syscall_benchmarks[] = {
|
||||
{ "basic", "Benchmark for basic getppid(2) calls", bench_syscall_basic },
|
||||
{ "all", "Run all syscall benchmarks", NULL },
|
||||
{ NULL, NULL, NULL },
|
||||
};
|
||||
|
||||
static struct bench mem_benchmarks[] = {
|
||||
{ "memcpy", "Benchmark for memcpy() functions", bench_mem_memcpy },
|
||||
{ "memset", "Benchmark for memset() functions", bench_mem_memset },
|
||||
{ "find_bit", "Benchmark for find_bit() functions", bench_mem_find_bit },
|
||||
{ "all", "Run all memory access benchmarks", NULL },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
@ -90,6 +98,7 @@ struct collection {
|
|||
|
||||
static struct collection collections[] = {
|
||||
{ "sched", "Scheduler and IPC benchmarks", sched_benchmarks },
|
||||
{ "syscall", "System call benchmarks", syscall_benchmarks },
|
||||
{ "mem", "Memory access benchmarks", mem_benchmarks },
|
||||
#ifdef HAVE_LIBNUMA_SUPPORT
|
||||
{ "numa", "NUMA scheduling and MM benchmarks", numa_benchmarks },
|
||||
|
|
|
@ -2582,7 +2582,7 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset)
|
|||
|
||||
static int setup_callchain(struct evlist *evlist)
|
||||
{
|
||||
u64 sample_type = perf_evlist__combined_sample_type(evlist);
|
||||
u64 sample_type = evlist__combined_sample_type(evlist);
|
||||
enum perf_call_graph_mode mode = CALLCHAIN_NONE;
|
||||
|
||||
if ((sample_type & PERF_SAMPLE_REGS_USER) &&
|
||||
|
|
|
@ -65,6 +65,7 @@ static int cmd_data_convert(int argc, const char **argv)
|
|||
OPT_STRING('i', "input", &input_name, "file", "input file name"),
|
||||
#ifdef HAVE_LIBBABELTRACE_SUPPORT
|
||||
OPT_STRING(0, "to-ctf", &to_ctf, NULL, "Convert to CTF format"),
|
||||
OPT_BOOLEAN(0, "tod", &opts.tod, "Convert time to wall clock time"),
|
||||
#endif
|
||||
OPT_BOOLEAN('f', "force", &opts.force, "don't complain, do it"),
|
||||
OPT_BOOLEAN(0, "all", &opts.all, "Convert all events"),
|
||||
|
|
|
@ -292,7 +292,7 @@ static int perf_event__jit_repipe_mmap(struct perf_tool *tool,
|
|||
* if jit marker, then inject jit mmaps and generate ELF images
|
||||
*/
|
||||
ret = jit_process(inject->session, &inject->output, machine,
|
||||
event->mmap.filename, sample->pid, &n);
|
||||
event->mmap.filename, event->mmap.pid, &n);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret) {
|
||||
|
@ -330,7 +330,7 @@ static int perf_event__jit_repipe_mmap2(struct perf_tool *tool,
|
|||
* if jit marker, then inject jit mmaps and generate ELF images
|
||||
*/
|
||||
ret = jit_process(inject->session, &inject->output, machine,
|
||||
event->mmap2.filename, sample->pid, &n);
|
||||
event->mmap2.filename, event->mmap2.pid, &n);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret) {
|
||||
|
|
|
@ -1933,7 +1933,8 @@ int cmd_kmem(int argc, const char **argv)
|
|||
return ret;
|
||||
|
||||
argc = parse_options_subcommand(argc, argv, kmem_options,
|
||||
kmem_subcommands, kmem_usage, 0);
|
||||
kmem_subcommands, kmem_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
if (!argc)
|
||||
usage_with_options(kmem_usage, kmem_options);
|
||||
|
|
|
@ -1319,7 +1319,7 @@ static struct evlist *kvm_live_event_list(void)
|
|||
*name = '\0';
|
||||
name++;
|
||||
|
||||
if (perf_evlist__add_newtp(evlist, sys, name, NULL)) {
|
||||
if (evlist__add_newtp(evlist, sys, name, NULL)) {
|
||||
pr_err("Failed to add %s tracepoint to the list\n", *events_tp);
|
||||
free(tp);
|
||||
goto out;
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "util/bpf-event.h"
|
||||
#include "util/util.h"
|
||||
#include "util/pfm.h"
|
||||
#include "util/clockid.h"
|
||||
#include "asm/bug.h"
|
||||
#include "perf.h"
|
||||
|
||||
|
@ -70,6 +71,7 @@
|
|||
#include <linux/time64.h>
|
||||
#include <linux/zalloc.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
struct switch_output {
|
||||
bool enabled;
|
||||
|
@ -765,6 +767,43 @@ static int record__auxtrace_init(struct record *rec __maybe_unused)
|
|||
|
||||
#endif
|
||||
|
||||
static int record__config_text_poke(struct evlist *evlist)
|
||||
{
|
||||
struct evsel *evsel;
|
||||
int err;
|
||||
|
||||
/* Nothing to do if text poke is already configured */
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
if (evsel->core.attr.text_poke)
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = parse_events(evlist, "dummy:u", NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
evsel = evlist__last(evlist);
|
||||
|
||||
evsel->core.attr.freq = 0;
|
||||
evsel->core.attr.sample_period = 1;
|
||||
evsel->core.attr.text_poke = 1;
|
||||
evsel->core.attr.ksymbol = 1;
|
||||
|
||||
evsel->core.system_wide = true;
|
||||
evsel->no_aux_samples = true;
|
||||
evsel->immediate = true;
|
||||
|
||||
/* Text poke must be collected on all CPUs */
|
||||
perf_cpu_map__put(evsel->core.own_cpus);
|
||||
evsel->core.own_cpus = perf_cpu_map__new(NULL);
|
||||
perf_cpu_map__put(evsel->core.cpus);
|
||||
evsel->core.cpus = perf_cpu_map__get(evsel->core.own_cpus);
|
||||
|
||||
evsel__set_sample_bit(evsel, TIME);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool record__kcore_readable(struct machine *machine)
|
||||
{
|
||||
char kcore[PATH_MAX];
|
||||
|
@ -855,7 +894,7 @@ static int record__open(struct record *rec)
|
|||
pos = perf_evlist__get_tracking_event(evlist);
|
||||
if (!evsel__is_dummy_event(pos)) {
|
||||
/* Set up dummy event. */
|
||||
if (perf_evlist__add_dummy(evlist))
|
||||
if (evlist__add_dummy(evlist))
|
||||
return -ENOMEM;
|
||||
pos = evlist__last(evlist);
|
||||
perf_evlist__set_tracking_event(evlist, pos);
|
||||
|
@ -1166,6 +1205,9 @@ static void record__init_features(struct record *rec)
|
|||
if (!(rec->opts.use_clockid && rec->opts.clockid_res_ns))
|
||||
perf_header__clear_feat(&session->header, HEADER_CLOCKID);
|
||||
|
||||
if (!rec->opts.use_clockid)
|
||||
perf_header__clear_feat(&session->header, HEADER_CLOCK_DATA);
|
||||
|
||||
perf_header__clear_feat(&session->header, HEADER_DIR_FORMAT);
|
||||
if (!record__comp_enabled(rec))
|
||||
perf_header__clear_feat(&session->header, HEADER_COMPRESSED);
|
||||
|
@ -1489,7 +1531,7 @@ static int record__setup_sb_evlist(struct record *rec)
|
|||
evlist__set_cb(rec->sb_evlist, record__process_signal_event, rec);
|
||||
rec->thread_id = pthread_self();
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBBPF_SUPPORT
|
||||
if (!opts->no_bpf_event) {
|
||||
if (rec->sb_evlist == NULL) {
|
||||
rec->sb_evlist = evlist__new();
|
||||
|
@ -1505,7 +1547,7 @@ static int record__setup_sb_evlist(struct record *rec)
|
|||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
if (perf_evlist__start_sb_thread(rec->sb_evlist, &rec->opts.target)) {
|
||||
pr_debug("Couldn't start the BPF side band thread:\nBPF programs starting from now on won't be annotatable\n");
|
||||
opts->no_bpf_event = true;
|
||||
|
@ -1514,6 +1556,43 @@ static int record__setup_sb_evlist(struct record *rec)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int record__init_clock(struct record *rec)
|
||||
{
|
||||
struct perf_session *session = rec->session;
|
||||
struct timespec ref_clockid;
|
||||
struct timeval ref_tod;
|
||||
u64 ref;
|
||||
|
||||
if (!rec->opts.use_clockid)
|
||||
return 0;
|
||||
|
||||
if (rec->opts.use_clockid && rec->opts.clockid_res_ns)
|
||||
session->header.env.clock.clockid_res_ns = rec->opts.clockid_res_ns;
|
||||
|
||||
session->header.env.clock.clockid = rec->opts.clockid;
|
||||
|
||||
if (gettimeofday(&ref_tod, NULL) != 0) {
|
||||
pr_err("gettimeofday failed, cannot set reference time.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (clock_gettime(rec->opts.clockid, &ref_clockid)) {
|
||||
pr_err("clock_gettime failed, cannot set reference time.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ref = (u64) ref_tod.tv_sec * NSEC_PER_SEC +
|
||||
(u64) ref_tod.tv_usec * NSEC_PER_USEC;
|
||||
|
||||
session->header.env.clock.tod_ns = ref;
|
||||
|
||||
ref = (u64) ref_clockid.tv_sec * NSEC_PER_SEC +
|
||||
(u64) ref_clockid.tv_nsec;
|
||||
|
||||
session->header.env.clock.clockid_ns = ref;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||
{
|
||||
int err;
|
||||
|
@ -1527,6 +1606,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
|||
bool disabled = false, draining = false;
|
||||
int fd;
|
||||
float ratio = 0;
|
||||
enum evlist_ctl_cmd cmd = EVLIST_CTL_CMD_UNSUPPORTED;
|
||||
|
||||
atexit(record__sig_exit);
|
||||
signal(SIGCHLD, sig_handler);
|
||||
|
@ -1593,10 +1673,10 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
|||
return -1;
|
||||
}
|
||||
|
||||
record__init_features(rec);
|
||||
if (record__init_clock(rec))
|
||||
return -1;
|
||||
|
||||
if (rec->opts.use_clockid && rec->opts.clockid_res_ns)
|
||||
session->header.env.clockid_res_ns = rec->opts.clockid_res_ns;
|
||||
record__init_features(rec);
|
||||
|
||||
if (forks) {
|
||||
err = perf_evlist__prepare_workload(rec->evlist, &opts->target,
|
||||
|
@ -1646,7 +1726,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
|||
* Normally perf_session__new would do this, but it doesn't have the
|
||||
* evlist.
|
||||
*/
|
||||
if (rec->tool.ordered_events && !perf_evlist__sample_id_all(rec->evlist)) {
|
||||
if (rec->tool.ordered_events && !evlist__sample_id_all(rec->evlist)) {
|
||||
pr_warning("WARNING: No sample_id_all support, falling back to unordered processing\n");
|
||||
rec->tool.ordered_events = false;
|
||||
}
|
||||
|
@ -1748,9 +1828,16 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
|||
perf_evlist__start_workload(rec->evlist);
|
||||
}
|
||||
|
||||
if (evlist__initialize_ctlfd(rec->evlist, opts->ctl_fd, opts->ctl_fd_ack))
|
||||
goto out_child;
|
||||
|
||||
if (opts->initial_delay) {
|
||||
usleep(opts->initial_delay * USEC_PER_MSEC);
|
||||
evlist__enable(rec->evlist);
|
||||
pr_info(EVLIST_DISABLED_MSG);
|
||||
if (opts->initial_delay > 0) {
|
||||
usleep(opts->initial_delay * USEC_PER_MSEC);
|
||||
evlist__enable(rec->evlist);
|
||||
pr_info(EVLIST_ENABLED_MSG);
|
||||
}
|
||||
}
|
||||
|
||||
trigger_ready(&auxtrace_snapshot_trigger);
|
||||
|
@ -1842,6 +1929,21 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
|||
draining = true;
|
||||
}
|
||||
|
||||
if (evlist__ctlfd_process(rec->evlist, &cmd) > 0) {
|
||||
switch (cmd) {
|
||||
case EVLIST_CTL_CMD_ENABLE:
|
||||
pr_info(EVLIST_ENABLED_MSG);
|
||||
break;
|
||||
case EVLIST_CTL_CMD_DISABLE:
|
||||
pr_info(EVLIST_DISABLED_MSG);
|
||||
break;
|
||||
case EVLIST_CTL_CMD_ACK:
|
||||
case EVLIST_CTL_CMD_UNSUPPORTED:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When perf is starting the traced process, at the end events
|
||||
* die with the process and we wait for that. Thus no need to
|
||||
|
@ -1875,6 +1977,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
|||
record__synthesize_workload(rec, true);
|
||||
|
||||
out_child:
|
||||
evlist__finalize_ctlfd(rec->evlist);
|
||||
record__mmap_read_all(rec, true);
|
||||
record__aio_mmap_read_sync(rec);
|
||||
|
||||
|
@ -2041,103 +2144,6 @@ static int perf_record_config(const char *var, const char *value, void *cb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct clockid_map {
|
||||
const char *name;
|
||||
int clockid;
|
||||
};
|
||||
|
||||
#define CLOCKID_MAP(n, c) \
|
||||
{ .name = n, .clockid = (c), }
|
||||
|
||||
#define CLOCKID_END { .name = NULL, }
|
||||
|
||||
|
||||
/*
|
||||
* Add the missing ones, we need to build on many distros...
|
||||
*/
|
||||
#ifndef CLOCK_MONOTONIC_RAW
|
||||
#define CLOCK_MONOTONIC_RAW 4
|
||||
#endif
|
||||
#ifndef CLOCK_BOOTTIME
|
||||
#define CLOCK_BOOTTIME 7
|
||||
#endif
|
||||
#ifndef CLOCK_TAI
|
||||
#define CLOCK_TAI 11
|
||||
#endif
|
||||
|
||||
static const struct clockid_map clockids[] = {
|
||||
/* available for all events, NMI safe */
|
||||
CLOCKID_MAP("monotonic", CLOCK_MONOTONIC),
|
||||
CLOCKID_MAP("monotonic_raw", CLOCK_MONOTONIC_RAW),
|
||||
|
||||
/* available for some events */
|
||||
CLOCKID_MAP("realtime", CLOCK_REALTIME),
|
||||
CLOCKID_MAP("boottime", CLOCK_BOOTTIME),
|
||||
CLOCKID_MAP("tai", CLOCK_TAI),
|
||||
|
||||
/* available for the lazy */
|
||||
CLOCKID_MAP("mono", CLOCK_MONOTONIC),
|
||||
CLOCKID_MAP("raw", CLOCK_MONOTONIC_RAW),
|
||||
CLOCKID_MAP("real", CLOCK_REALTIME),
|
||||
CLOCKID_MAP("boot", CLOCK_BOOTTIME),
|
||||
|
||||
CLOCKID_END,
|
||||
};
|
||||
|
||||
static int get_clockid_res(clockid_t clk_id, u64 *res_ns)
|
||||
{
|
||||
struct timespec res;
|
||||
|
||||
*res_ns = 0;
|
||||
if (!clock_getres(clk_id, &res))
|
||||
*res_ns = res.tv_nsec + res.tv_sec * NSEC_PER_SEC;
|
||||
else
|
||||
pr_warning("WARNING: Failed to determine specified clock resolution.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_clockid(const struct option *opt, const char *str, int unset)
|
||||
{
|
||||
struct record_opts *opts = (struct record_opts *)opt->value;
|
||||
const struct clockid_map *cm;
|
||||
const char *ostr = str;
|
||||
|
||||
if (unset) {
|
||||
opts->use_clockid = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* no arg passed */
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
/* no setting it twice */
|
||||
if (opts->use_clockid)
|
||||
return -1;
|
||||
|
||||
opts->use_clockid = true;
|
||||
|
||||
/* if its a number, we're done */
|
||||
if (sscanf(str, "%d", &opts->clockid) == 1)
|
||||
return get_clockid_res(opts->clockid, &opts->clockid_res_ns);
|
||||
|
||||
/* allow a "CLOCK_" prefix to the name */
|
||||
if (!strncasecmp(str, "CLOCK_", 6))
|
||||
str += 6;
|
||||
|
||||
for (cm = clockids; cm->name; cm++) {
|
||||
if (!strcasecmp(str, cm->name)) {
|
||||
opts->clockid = cm->clockid;
|
||||
return get_clockid_res(opts->clockid,
|
||||
&opts->clockid_res_ns);
|
||||
}
|
||||
}
|
||||
|
||||
opts->use_clockid = false;
|
||||
ui__warning("unknown clockid %s, check man page\n", ostr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int record__parse_affinity(const struct option *opt, const char *str, int unset)
|
||||
{
|
||||
|
@ -2224,6 +2230,33 @@ out_free:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int parse_control_option(const struct option *opt,
|
||||
const char *str,
|
||||
int unset __maybe_unused)
|
||||
{
|
||||
char *comma = NULL, *endptr = NULL;
|
||||
struct record_opts *config = (struct record_opts *)opt->value;
|
||||
|
||||
if (strncmp(str, "fd:", 3))
|
||||
return -EINVAL;
|
||||
|
||||
config->ctl_fd = strtoul(&str[3], &endptr, 0);
|
||||
if (endptr == &str[3])
|
||||
return -EINVAL;
|
||||
|
||||
comma = strchr(str, ',');
|
||||
if (comma) {
|
||||
if (endptr != comma)
|
||||
return -EINVAL;
|
||||
|
||||
config->ctl_fd_ack = strtoul(comma + 1, &endptr, 0);
|
||||
if (endptr == comma + 1 || *endptr != '\0')
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void switch_output_size_warn(struct record *rec)
|
||||
{
|
||||
u64 wakeup_size = evlist__mmap_size(rec->opts.mmap_pages);
|
||||
|
@ -2360,6 +2393,8 @@ static struct record record = {
|
|||
},
|
||||
.mmap_flush = MMAP_FLUSH_DEFAULT,
|
||||
.nr_threads_synthesize = 1,
|
||||
.ctl_fd = -1,
|
||||
.ctl_fd_ack = -1,
|
||||
},
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
|
@ -2462,8 +2497,8 @@ static struct option __record_options[] = {
|
|||
OPT_CALLBACK('G', "cgroup", &record.evlist, "name",
|
||||
"monitor event in cgroup name only",
|
||||
parse_cgroups),
|
||||
OPT_UINTEGER('D', "delay", &record.opts.initial_delay,
|
||||
"ms to wait before starting measurement after program start"),
|
||||
OPT_INTEGER('D', "delay", &record.opts.initial_delay,
|
||||
"ms to wait before starting measurement after program start (-1: start with events disabled)"),
|
||||
OPT_BOOLEAN(0, "kcore", &record.opts.kcore, "copy /proc/kcore"),
|
||||
OPT_STRING('u', "uid", &record.opts.target.uid_str, "user",
|
||||
"user to profile"),
|
||||
|
@ -2561,6 +2596,10 @@ static struct option __record_options[] = {
|
|||
"libpfm4 event selector. use 'perf list' to list available events",
|
||||
parse_libpfm_events_option),
|
||||
#endif
|
||||
OPT_CALLBACK(0, "control", &record.opts, "fd:ctl-fd[,ack-fd]",
|
||||
"Listen on ctl-fd descriptor for command to control measurement ('enable': enable events, 'disable': disable events).\n"
|
||||
"\t\t\t Optionally send control command completion ('ack\\n') to ack-fd descriptor.",
|
||||
parse_control_option),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
@ -2722,7 +2761,7 @@ int cmd_record(int argc, const char **argv)
|
|||
record.opts.tail_synthesize = true;
|
||||
|
||||
if (rec->evlist->core.nr_entries == 0 &&
|
||||
__perf_evlist__add_default(rec->evlist, !record.opts.no_samples) < 0) {
|
||||
__evlist__add_default(rec->evlist, !record.opts.no_samples) < 0) {
|
||||
pr_err("Not enough memory for event selector list\n");
|
||||
goto out;
|
||||
}
|
||||
|
@ -2766,6 +2805,14 @@ int cmd_record(int argc, const char **argv)
|
|||
if (rec->opts.full_auxtrace)
|
||||
rec->buildid_all = true;
|
||||
|
||||
if (rec->opts.text_poke) {
|
||||
err = record__config_text_poke(rec->evlist);
|
||||
if (err) {
|
||||
pr_err("record__config_text_poke failed, error %d\n", err);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (record_opts__config(&rec->opts)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
|
|
|
@ -338,7 +338,7 @@ static int process_read_event(struct perf_tool *tool,
|
|||
static int report__setup_sample_type(struct report *rep)
|
||||
{
|
||||
struct perf_session *session = rep->session;
|
||||
u64 sample_type = perf_evlist__combined_sample_type(session->evlist);
|
||||
u64 sample_type = evlist__combined_sample_type(session->evlist);
|
||||
bool is_pipe = perf_data__is_pipe(session->data);
|
||||
|
||||
if (session->itrace_synth_opts->callchain ||
|
||||
|
@ -410,8 +410,7 @@ static int report__setup_sample_type(struct report *rep)
|
|||
}
|
||||
|
||||
/* ??? handle more cases than just ANY? */
|
||||
if (!(perf_evlist__combined_branch_type(session->evlist) &
|
||||
PERF_SAMPLE_BRANCH_ANY))
|
||||
if (!(evlist__combined_branch_type(session->evlist) & PERF_SAMPLE_BRANCH_ANY))
|
||||
rep->nonany_branch_mode = true;
|
||||
|
||||
#if !defined(HAVE_LIBUNWIND_SUPPORT) && !defined(HAVE_DWARF_SUPPORT)
|
||||
|
@ -1093,7 +1092,7 @@ static int process_attr(struct perf_tool *tool __maybe_unused,
|
|||
* Check if we need to enable callchains based
|
||||
* on events sample_type.
|
||||
*/
|
||||
sample_type = perf_evlist__combined_sample_type(*pevlist);
|
||||
sample_type = evlist__combined_sample_type(*pevlist);
|
||||
callchain_param_setup(sample_type);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1389,7 +1388,7 @@ repeat:
|
|||
|
||||
has_br_stack = perf_header__has_feat(&session->header,
|
||||
HEADER_BRANCH_STACK);
|
||||
if (perf_evlist__combined_sample_type(session->evlist) & PERF_SAMPLE_STACK_USER)
|
||||
if (evlist__combined_sample_type(session->evlist) & PERF_SAMPLE_STACK_USER)
|
||||
has_br_stack = false;
|
||||
|
||||
setup_forced_leader(&report, session->evlist);
|
||||
|
|
|
@ -82,38 +82,64 @@ static bool native_arch;
|
|||
unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH;
|
||||
|
||||
enum perf_output_field {
|
||||
PERF_OUTPUT_COMM = 1U << 0,
|
||||
PERF_OUTPUT_TID = 1U << 1,
|
||||
PERF_OUTPUT_PID = 1U << 2,
|
||||
PERF_OUTPUT_TIME = 1U << 3,
|
||||
PERF_OUTPUT_CPU = 1U << 4,
|
||||
PERF_OUTPUT_EVNAME = 1U << 5,
|
||||
PERF_OUTPUT_TRACE = 1U << 6,
|
||||
PERF_OUTPUT_IP = 1U << 7,
|
||||
PERF_OUTPUT_SYM = 1U << 8,
|
||||
PERF_OUTPUT_DSO = 1U << 9,
|
||||
PERF_OUTPUT_ADDR = 1U << 10,
|
||||
PERF_OUTPUT_SYMOFFSET = 1U << 11,
|
||||
PERF_OUTPUT_SRCLINE = 1U << 12,
|
||||
PERF_OUTPUT_PERIOD = 1U << 13,
|
||||
PERF_OUTPUT_IREGS = 1U << 14,
|
||||
PERF_OUTPUT_BRSTACK = 1U << 15,
|
||||
PERF_OUTPUT_BRSTACKSYM = 1U << 16,
|
||||
PERF_OUTPUT_DATA_SRC = 1U << 17,
|
||||
PERF_OUTPUT_WEIGHT = 1U << 18,
|
||||
PERF_OUTPUT_BPF_OUTPUT = 1U << 19,
|
||||
PERF_OUTPUT_CALLINDENT = 1U << 20,
|
||||
PERF_OUTPUT_INSN = 1U << 21,
|
||||
PERF_OUTPUT_INSNLEN = 1U << 22,
|
||||
PERF_OUTPUT_BRSTACKINSN = 1U << 23,
|
||||
PERF_OUTPUT_BRSTACKOFF = 1U << 24,
|
||||
PERF_OUTPUT_SYNTH = 1U << 25,
|
||||
PERF_OUTPUT_PHYS_ADDR = 1U << 26,
|
||||
PERF_OUTPUT_UREGS = 1U << 27,
|
||||
PERF_OUTPUT_METRIC = 1U << 28,
|
||||
PERF_OUTPUT_MISC = 1U << 29,
|
||||
PERF_OUTPUT_SRCCODE = 1U << 30,
|
||||
PERF_OUTPUT_IPC = 1U << 31,
|
||||
PERF_OUTPUT_COMM = 1ULL << 0,
|
||||
PERF_OUTPUT_TID = 1ULL << 1,
|
||||
PERF_OUTPUT_PID = 1ULL << 2,
|
||||
PERF_OUTPUT_TIME = 1ULL << 3,
|
||||
PERF_OUTPUT_CPU = 1ULL << 4,
|
||||
PERF_OUTPUT_EVNAME = 1ULL << 5,
|
||||
PERF_OUTPUT_TRACE = 1ULL << 6,
|
||||
PERF_OUTPUT_IP = 1ULL << 7,
|
||||
PERF_OUTPUT_SYM = 1ULL << 8,
|
||||
PERF_OUTPUT_DSO = 1ULL << 9,
|
||||
PERF_OUTPUT_ADDR = 1ULL << 10,
|
||||
PERF_OUTPUT_SYMOFFSET = 1ULL << 11,
|
||||
PERF_OUTPUT_SRCLINE = 1ULL << 12,
|
||||
PERF_OUTPUT_PERIOD = 1ULL << 13,
|
||||
PERF_OUTPUT_IREGS = 1ULL << 14,
|
||||
PERF_OUTPUT_BRSTACK = 1ULL << 15,
|
||||
PERF_OUTPUT_BRSTACKSYM = 1ULL << 16,
|
||||
PERF_OUTPUT_DATA_SRC = 1ULL << 17,
|
||||
PERF_OUTPUT_WEIGHT = 1ULL << 18,
|
||||
PERF_OUTPUT_BPF_OUTPUT = 1ULL << 19,
|
||||
PERF_OUTPUT_CALLINDENT = 1ULL << 20,
|
||||
PERF_OUTPUT_INSN = 1ULL << 21,
|
||||
PERF_OUTPUT_INSNLEN = 1ULL << 22,
|
||||
PERF_OUTPUT_BRSTACKINSN = 1ULL << 23,
|
||||
PERF_OUTPUT_BRSTACKOFF = 1ULL << 24,
|
||||
PERF_OUTPUT_SYNTH = 1ULL << 25,
|
||||
PERF_OUTPUT_PHYS_ADDR = 1ULL << 26,
|
||||
PERF_OUTPUT_UREGS = 1ULL << 27,
|
||||
PERF_OUTPUT_METRIC = 1ULL << 28,
|
||||
PERF_OUTPUT_MISC = 1ULL << 29,
|
||||
PERF_OUTPUT_SRCCODE = 1ULL << 30,
|
||||
PERF_OUTPUT_IPC = 1ULL << 31,
|
||||
PERF_OUTPUT_TOD = 1ULL << 32,
|
||||
};
|
||||
|
||||
struct perf_script {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
bool show_task_events;
|
||||
bool show_mmap_events;
|
||||
bool show_switch_events;
|
||||
bool show_namespace_events;
|
||||
bool show_lost_events;
|
||||
bool show_round_events;
|
||||
bool show_bpf_events;
|
||||
bool show_cgroup_events;
|
||||
bool show_text_poke_events;
|
||||
bool allocated;
|
||||
bool per_event_dump;
|
||||
bool stitch_lbr;
|
||||
struct evswitch evswitch;
|
||||
struct perf_cpu_map *cpus;
|
||||
struct perf_thread_map *threads;
|
||||
int name_width;
|
||||
const char *time_str;
|
||||
struct perf_time_interval *ptime_range;
|
||||
int range_size;
|
||||
int range_num;
|
||||
};
|
||||
|
||||
struct output_option {
|
||||
|
@ -152,6 +178,7 @@ struct output_option {
|
|||
{.str = "misc", .field = PERF_OUTPUT_MISC},
|
||||
{.str = "srccode", .field = PERF_OUTPUT_SRCCODE},
|
||||
{.str = "ipc", .field = PERF_OUTPUT_IPC},
|
||||
{.str = "tod", .field = PERF_OUTPUT_TOD},
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -388,7 +415,7 @@ static int evsel__check_stype(struct evsel *evsel, u64 sample_type, const char *
|
|||
return evsel__do_check_stype(evsel, sample_type, sample_msg, field, false);
|
||||
}
|
||||
|
||||
static int perf_evsel__check_attr(struct evsel *evsel, struct perf_session *session)
|
||||
static int evsel__check_attr(struct evsel *evsel, struct perf_session *session)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->core.attr;
|
||||
bool allow_user_set;
|
||||
|
@ -443,8 +470,7 @@ static int perf_evsel__check_attr(struct evsel *evsel, struct perf_session *sess
|
|||
return -EINVAL;
|
||||
}
|
||||
if (PRINT_FIELD(BRSTACKINSN) && !allow_user_set &&
|
||||
!(perf_evlist__combined_branch_type(session->evlist) &
|
||||
PERF_SAMPLE_BRANCH_ANY)) {
|
||||
!(evlist__combined_branch_type(session->evlist) & PERF_SAMPLE_BRANCH_ANY)) {
|
||||
pr_err("Display of branch stack assembler requested, but non all-branch filter set\n"
|
||||
"Hint: run 'perf record -b ...'\n");
|
||||
return -EINVAL;
|
||||
|
@ -503,6 +529,7 @@ static void set_print_ip_opts(struct perf_event_attr *attr)
|
|||
*/
|
||||
static int perf_session__check_output_opt(struct perf_session *session)
|
||||
{
|
||||
bool tod = false;
|
||||
unsigned int j;
|
||||
struct evsel *evsel;
|
||||
|
||||
|
@ -522,13 +549,14 @@ static int perf_session__check_output_opt(struct perf_session *session)
|
|||
}
|
||||
|
||||
if (evsel && output[j].fields &&
|
||||
perf_evsel__check_attr(evsel, session))
|
||||
evsel__check_attr(evsel, session))
|
||||
return -1;
|
||||
|
||||
if (evsel == NULL)
|
||||
continue;
|
||||
|
||||
set_print_ip_opts(&evsel->core.attr);
|
||||
tod |= output[j].fields & PERF_OUTPUT_TOD;
|
||||
}
|
||||
|
||||
if (!no_callchain) {
|
||||
|
@ -569,13 +597,17 @@ static int perf_session__check_output_opt(struct perf_session *session)
|
|||
}
|
||||
}
|
||||
|
||||
if (tod && !session->header.env.clock.enabled) {
|
||||
pr_err("Can't provide 'tod' time, missing clock data. "
|
||||
"Please record with -k/--clockid option.\n");
|
||||
return -1;
|
||||
}
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask,
|
||||
FILE *fp
|
||||
)
|
||||
FILE *fp)
|
||||
{
|
||||
unsigned i = 0, r;
|
||||
int printed = 0;
|
||||
|
@ -593,6 +625,56 @@ static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask,
|
|||
return printed;
|
||||
}
|
||||
|
||||
#define DEFAULT_TOD_FMT "%F %H:%M:%S"
|
||||
|
||||
static char*
|
||||
tod_scnprintf(struct perf_script *script, char *buf, int buflen,
|
||||
u64 timestamp)
|
||||
{
|
||||
u64 tod_ns, clockid_ns;
|
||||
struct perf_env *env;
|
||||
unsigned long nsec;
|
||||
struct tm ltime;
|
||||
char date[64];
|
||||
time_t sec;
|
||||
|
||||
buf[0] = '\0';
|
||||
if (buflen < 64 || !script)
|
||||
return buf;
|
||||
|
||||
env = &script->session->header.env;
|
||||
if (!env->clock.enabled) {
|
||||
scnprintf(buf, buflen, "disabled");
|
||||
return buf;
|
||||
}
|
||||
|
||||
clockid_ns = env->clock.clockid_ns;
|
||||
tod_ns = env->clock.tod_ns;
|
||||
|
||||
if (timestamp > clockid_ns)
|
||||
tod_ns += timestamp - clockid_ns;
|
||||
else
|
||||
tod_ns -= clockid_ns - timestamp;
|
||||
|
||||
sec = (time_t) (tod_ns / NSEC_PER_SEC);
|
||||
nsec = tod_ns - sec * NSEC_PER_SEC;
|
||||
|
||||
if (localtime_r(&sec, <ime) == NULL) {
|
||||
scnprintf(buf, buflen, "failed");
|
||||
} else {
|
||||
strftime(date, sizeof(date), DEFAULT_TOD_FMT, <ime);
|
||||
|
||||
if (symbol_conf.nanosecs) {
|
||||
snprintf(buf, buflen, "%s.%09lu", date, nsec);
|
||||
} else {
|
||||
snprintf(buf, buflen, "%s.%06lu",
|
||||
date, nsec / NSEC_PER_USEC);
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int perf_sample__fprintf_iregs(struct perf_sample *sample,
|
||||
struct perf_event_attr *attr, FILE *fp)
|
||||
{
|
||||
|
@ -607,7 +689,8 @@ static int perf_sample__fprintf_uregs(struct perf_sample *sample,
|
|||
attr->sample_regs_user, fp);
|
||||
}
|
||||
|
||||
static int perf_sample__fprintf_start(struct perf_sample *sample,
|
||||
static int perf_sample__fprintf_start(struct perf_script *script,
|
||||
struct perf_sample *sample,
|
||||
struct thread *thread,
|
||||
struct evsel *evsel,
|
||||
u32 type, FILE *fp)
|
||||
|
@ -616,6 +699,7 @@ static int perf_sample__fprintf_start(struct perf_sample *sample,
|
|||
unsigned long secs;
|
||||
unsigned long long nsecs;
|
||||
int printed = 0;
|
||||
char tstr[128];
|
||||
|
||||
if (PRINT_FIELD(COMM)) {
|
||||
if (latency_format)
|
||||
|
@ -684,6 +768,11 @@ static int perf_sample__fprintf_start(struct perf_sample *sample,
|
|||
printed += ret;
|
||||
}
|
||||
|
||||
if (PRINT_FIELD(TOD)) {
|
||||
tod_scnprintf(script, tstr, sizeof(tstr), sample->time);
|
||||
printed += fprintf(fp, "%s ", tstr);
|
||||
}
|
||||
|
||||
if (PRINT_FIELD(TIME)) {
|
||||
u64 t = sample->time;
|
||||
if (reltime) {
|
||||
|
@ -1668,31 +1757,7 @@ static int perf_sample__fprintf_synth(struct perf_sample *sample,
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct perf_script {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
bool show_task_events;
|
||||
bool show_mmap_events;
|
||||
bool show_switch_events;
|
||||
bool show_namespace_events;
|
||||
bool show_lost_events;
|
||||
bool show_round_events;
|
||||
bool show_bpf_events;
|
||||
bool show_cgroup_events;
|
||||
bool allocated;
|
||||
bool per_event_dump;
|
||||
bool stitch_lbr;
|
||||
struct evswitch evswitch;
|
||||
struct perf_cpu_map *cpus;
|
||||
struct perf_thread_map *threads;
|
||||
int name_width;
|
||||
const char *time_str;
|
||||
struct perf_time_interval *ptime_range;
|
||||
int range_size;
|
||||
int range_num;
|
||||
};
|
||||
|
||||
static int perf_evlist__max_name_len(struct evlist *evlist)
|
||||
static int evlist__max_name_len(struct evlist *evlist)
|
||||
{
|
||||
struct evsel *evsel;
|
||||
int max = 0;
|
||||
|
@ -1739,7 +1804,7 @@ static void script_print_metric(struct perf_stat_config *config __maybe_unused,
|
|||
|
||||
if (!fmt)
|
||||
return;
|
||||
perf_sample__fprintf_start(mctx->sample, mctx->thread, mctx->evsel,
|
||||
perf_sample__fprintf_start(NULL, mctx->sample, mctx->thread, mctx->evsel,
|
||||
PERF_RECORD_SAMPLE, mctx->fp);
|
||||
fputs("\tmetric: ", mctx->fp);
|
||||
if (color)
|
||||
|
@ -1754,7 +1819,7 @@ static void script_new_line(struct perf_stat_config *config __maybe_unused,
|
|||
{
|
||||
struct metric_ctx *mctx = ctx;
|
||||
|
||||
perf_sample__fprintf_start(mctx->sample, mctx->thread, mctx->evsel,
|
||||
perf_sample__fprintf_start(NULL, mctx->sample, mctx->thread, mctx->evsel,
|
||||
PERF_RECORD_SAMPLE, mctx->fp);
|
||||
fputs("\tmetric: ", mctx->fp);
|
||||
}
|
||||
|
@ -1865,7 +1930,7 @@ static void process_event(struct perf_script *script,
|
|||
|
||||
++es->samples;
|
||||
|
||||
perf_sample__fprintf_start(sample, thread, evsel,
|
||||
perf_sample__fprintf_start(script, sample, thread, evsel,
|
||||
PERF_RECORD_SAMPLE, fp);
|
||||
|
||||
if (PRINT_FIELD(PERIOD))
|
||||
|
@ -1875,7 +1940,7 @@ static void process_event(struct perf_script *script,
|
|||
const char *evname = evsel__name(evsel);
|
||||
|
||||
if (!script->name_width)
|
||||
script->name_width = perf_evlist__max_name_len(script->session->evlist);
|
||||
script->name_width = evlist__max_name_len(script->session->evlist);
|
||||
|
||||
fprintf(fp, "%*s: ", script->name_width, evname ?: "[unknown]");
|
||||
}
|
||||
|
@ -2120,7 +2185,7 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
|
|||
}
|
||||
|
||||
if (evsel->core.attr.sample_type) {
|
||||
err = perf_evsel__check_attr(evsel, scr->session);
|
||||
err = evsel__check_attr(evsel, scr->session);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
@ -2129,7 +2194,7 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
|
|||
* Check if we need to enable callchains based
|
||||
* on events sample_type.
|
||||
*/
|
||||
sample_type = perf_evlist__combined_sample_type(evlist);
|
||||
sample_type = evlist__combined_sample_type(evlist);
|
||||
callchain_param_setup(sample_type);
|
||||
|
||||
/* Enable fields for callchain entries */
|
||||
|
@ -2174,11 +2239,11 @@ static int print_event_with_time(struct perf_tool *tool,
|
|||
thread = machine__findnew_thread(machine, pid, tid);
|
||||
|
||||
if (thread && evsel) {
|
||||
perf_sample__fprintf_start(sample, thread, evsel,
|
||||
perf_sample__fprintf_start(script, sample, thread, evsel,
|
||||
event->header.type, stdout);
|
||||
}
|
||||
|
||||
perf_event__fprintf(event, stdout);
|
||||
perf_event__fprintf(event, machine, stdout);
|
||||
|
||||
thread__put(thread);
|
||||
|
||||
|
@ -2313,7 +2378,7 @@ process_finished_round_event(struct perf_tool *tool __maybe_unused,
|
|||
struct ordered_events *oe __maybe_unused)
|
||||
|
||||
{
|
||||
perf_event__fprintf(event, stdout);
|
||||
perf_event__fprintf(event, NULL, stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2330,6 +2395,18 @@ process_bpf_events(struct perf_tool *tool __maybe_unused,
|
|||
sample->tid);
|
||||
}
|
||||
|
||||
static int process_text_poke_events(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
if (perf_event__process_text_poke(tool, event, sample, machine) < 0)
|
||||
return -1;
|
||||
|
||||
return print_event(tool, event, sample, machine, sample->pid,
|
||||
sample->tid);
|
||||
}
|
||||
|
||||
static void sig_handler(int sig __maybe_unused)
|
||||
{
|
||||
session_done = 1;
|
||||
|
@ -2438,6 +2515,10 @@ static int __cmd_script(struct perf_script *script)
|
|||
script->tool.ksymbol = process_bpf_events;
|
||||
script->tool.bpf = process_bpf_events;
|
||||
}
|
||||
if (script->show_text_poke_events) {
|
||||
script->tool.ksymbol = process_bpf_events;
|
||||
script->tool.text_poke = process_text_poke_events;
|
||||
}
|
||||
|
||||
if (perf_script__setup_per_event_dump(script)) {
|
||||
pr_err("Couldn't create the per event dump files\n");
|
||||
|
@ -3171,7 +3252,7 @@ static int have_cmd(int argc, const char **argv)
|
|||
static void script__setup_sample_type(struct perf_script *script)
|
||||
{
|
||||
struct perf_session *session = script->session;
|
||||
u64 sample_type = perf_evlist__combined_sample_type(session->evlist);
|
||||
u64 sample_type = evlist__combined_sample_type(session->evlist);
|
||||
|
||||
if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) {
|
||||
if ((sample_type & PERF_SAMPLE_REGS_USER) &&
|
||||
|
@ -3423,7 +3504,7 @@ int cmd_script(int argc, const char **argv)
|
|||
"Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,"
|
||||
"addr,symoff,srcline,period,iregs,uregs,brstack,"
|
||||
"brstacksym,flags,bpf-output,brstackinsn,brstackoff,"
|
||||
"callindent,insn,insnlen,synth,phys_addr,metric,misc,ipc",
|
||||
"callindent,insn,insnlen,synth,phys_addr,metric,misc,ipc,tod",
|
||||
parse_output_fields),
|
||||
OPT_BOOLEAN('a', "all-cpus", &system_wide,
|
||||
"system-wide collection from all CPUs"),
|
||||
|
@ -3474,6 +3555,8 @@ int cmd_script(int argc, const char **argv)
|
|||
"Show round events (if recorded)"),
|
||||
OPT_BOOLEAN('\0', "show-bpf-events", &script.show_bpf_events,
|
||||
"Show bpf related events (if recorded)"),
|
||||
OPT_BOOLEAN('\0', "show-text-poke-events", &script.show_text_poke_events,
|
||||
"Show text poke related events (if recorded)"),
|
||||
OPT_BOOLEAN('\0', "per-event-dump", &script.per_event_dump,
|
||||
"Dump trace output to files named by the monitored events"),
|
||||
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
|
||||
|
|
|
@ -188,6 +188,8 @@ static struct perf_stat_config stat_config = {
|
|||
.metric_only_len = METRIC_ONLY_LEN,
|
||||
.walltime_nsecs_stats = &walltime_nsecs_stats,
|
||||
.big_num = true,
|
||||
.ctl_fd = -1,
|
||||
.ctl_fd_ack = -1
|
||||
};
|
||||
|
||||
static bool cpus_map_matched(struct evsel *a, struct evsel *b)
|
||||
|
@ -475,18 +477,38 @@ static void process_interval(void)
|
|||
print_counters(&rs, 0, NULL);
|
||||
}
|
||||
|
||||
static bool handle_interval(unsigned int interval, int *times)
|
||||
{
|
||||
if (interval) {
|
||||
process_interval();
|
||||
if (interval_count && !(--(*times)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void enable_counters(void)
|
||||
{
|
||||
if (stat_config.initial_delay)
|
||||
if (stat_config.initial_delay < 0) {
|
||||
pr_info(EVLIST_DISABLED_MSG);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stat_config.initial_delay > 0) {
|
||||
pr_info(EVLIST_DISABLED_MSG);
|
||||
usleep(stat_config.initial_delay * USEC_PER_MSEC);
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to enable counters only if:
|
||||
* - we don't have tracee (attaching to task or cpu)
|
||||
* - we have initial delay configured
|
||||
*/
|
||||
if (!target__none(&target) || stat_config.initial_delay)
|
||||
if (!target__none(&target) || stat_config.initial_delay) {
|
||||
evlist__enable(evsel_list);
|
||||
if (stat_config.initial_delay > 0)
|
||||
pr_info(EVLIST_ENABLED_MSG);
|
||||
}
|
||||
}
|
||||
|
||||
static void disable_counters(void)
|
||||
|
@ -540,6 +562,86 @@ static bool is_target_alive(struct target *_target,
|
|||
return false;
|
||||
}
|
||||
|
||||
static void process_evlist(struct evlist *evlist, unsigned int interval)
|
||||
{
|
||||
enum evlist_ctl_cmd cmd = EVLIST_CTL_CMD_UNSUPPORTED;
|
||||
|
||||
if (evlist__ctlfd_process(evlist, &cmd) > 0) {
|
||||
switch (cmd) {
|
||||
case EVLIST_CTL_CMD_ENABLE:
|
||||
pr_info(EVLIST_ENABLED_MSG);
|
||||
if (interval)
|
||||
process_interval();
|
||||
break;
|
||||
case EVLIST_CTL_CMD_DISABLE:
|
||||
if (interval)
|
||||
process_interval();
|
||||
pr_info(EVLIST_DISABLED_MSG);
|
||||
break;
|
||||
case EVLIST_CTL_CMD_ACK:
|
||||
case EVLIST_CTL_CMD_UNSUPPORTED:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void compute_tts(struct timespec *time_start, struct timespec *time_stop,
|
||||
int *time_to_sleep)
|
||||
{
|
||||
int tts = *time_to_sleep;
|
||||
struct timespec time_diff;
|
||||
|
||||
diff_timespec(&time_diff, time_stop, time_start);
|
||||
|
||||
tts -= time_diff.tv_sec * MSEC_PER_SEC +
|
||||
time_diff.tv_nsec / NSEC_PER_MSEC;
|
||||
|
||||
if (tts < 0)
|
||||
tts = 0;
|
||||
|
||||
*time_to_sleep = tts;
|
||||
}
|
||||
|
||||
static int dispatch_events(bool forks, int timeout, int interval, int *times)
|
||||
{
|
||||
int child_exited = 0, status = 0;
|
||||
int time_to_sleep, sleep_time;
|
||||
struct timespec time_start, time_stop;
|
||||
|
||||
if (interval)
|
||||
sleep_time = interval;
|
||||
else if (timeout)
|
||||
sleep_time = timeout;
|
||||
else
|
||||
sleep_time = 1000;
|
||||
|
||||
time_to_sleep = sleep_time;
|
||||
|
||||
while (!done) {
|
||||
if (forks)
|
||||
child_exited = waitpid(child_pid, &status, WNOHANG);
|
||||
else
|
||||
child_exited = !is_target_alive(&target, evsel_list->core.threads) ? 1 : 0;
|
||||
|
||||
if (child_exited)
|
||||
break;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &time_start);
|
||||
if (!(evlist__poll(evsel_list, time_to_sleep) > 0)) { /* poll timeout or EINTR */
|
||||
if (timeout || handle_interval(interval, times))
|
||||
break;
|
||||
time_to_sleep = sleep_time;
|
||||
} else { /* fd revent */
|
||||
process_evlist(evsel_list, interval);
|
||||
clock_gettime(CLOCK_MONOTONIC, &time_stop);
|
||||
compute_tts(&time_start, &time_stop, &time_to_sleep);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
enum counter_recovery {
|
||||
COUNTER_SKIP,
|
||||
COUNTER_RETRY,
|
||||
|
@ -603,7 +705,6 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
|
|||
char msg[BUFSIZ];
|
||||
unsigned long long t0, t1;
|
||||
struct evsel *counter;
|
||||
struct timespec ts;
|
||||
size_t l;
|
||||
int status = 0;
|
||||
const bool forks = (argc > 0);
|
||||
|
@ -612,17 +713,6 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx)
|
|||
int i, cpu;
|
||||
bool second_pass = false;
|
||||
|
||||
if (interval) {
|
||||
ts.tv_sec = interval / USEC_PER_MSEC;
|
||||
ts.tv_nsec = (interval % USEC_PER_MSEC) * NSEC_PER_MSEC;
|
||||
} else if (timeout) {
|
||||
ts.tv_sec = timeout / USEC_PER_MSEC;
|
||||
ts.tv_nsec = (timeout % USEC_PER_MSEC) * NSEC_PER_MSEC;
|
||||
} else {
|
||||
ts.tv_sec = 1;
|
||||
ts.tv_nsec = 0;
|
||||
}
|
||||
|
||||
if (forks) {
|
||||
if (perf_evlist__prepare_workload(evsel_list, &target, argv, is_pipe,
|
||||
workload_exec_failed_signal) < 0) {
|
||||
|
@ -779,16 +869,8 @@ try_again_reset:
|
|||
perf_evlist__start_workload(evsel_list);
|
||||
enable_counters();
|
||||
|
||||
if (interval || timeout) {
|
||||
while (!waitpid(child_pid, &status, WNOHANG)) {
|
||||
nanosleep(&ts, NULL);
|
||||
if (timeout)
|
||||
break;
|
||||
process_interval();
|
||||
if (interval_count && !(--times))
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (interval || timeout || evlist__ctlfd_initialized(evsel_list))
|
||||
status = dispatch_events(forks, timeout, interval, ×);
|
||||
if (child_pid != -1) {
|
||||
if (timeout)
|
||||
kill(child_pid, SIGTERM);
|
||||
|
@ -805,18 +887,7 @@ try_again_reset:
|
|||
psignal(WTERMSIG(status), argv[0]);
|
||||
} else {
|
||||
enable_counters();
|
||||
while (!done) {
|
||||
nanosleep(&ts, NULL);
|
||||
if (!is_target_alive(&target, evsel_list->core.threads))
|
||||
break;
|
||||
if (timeout)
|
||||
break;
|
||||
if (interval) {
|
||||
process_interval();
|
||||
if (interval_count && !(--times))
|
||||
break;
|
||||
}
|
||||
}
|
||||
status = dispatch_events(forks, timeout, interval, ×);
|
||||
}
|
||||
|
||||
disable_counters();
|
||||
|
@ -970,6 +1041,33 @@ static int parse_metric_groups(const struct option *opt,
|
|||
&stat_config.metric_events);
|
||||
}
|
||||
|
||||
static int parse_control_option(const struct option *opt,
|
||||
const char *str,
|
||||
int unset __maybe_unused)
|
||||
{
|
||||
char *comma = NULL, *endptr = NULL;
|
||||
struct perf_stat_config *config = (struct perf_stat_config *)opt->value;
|
||||
|
||||
if (strncmp(str, "fd:", 3))
|
||||
return -EINVAL;
|
||||
|
||||
config->ctl_fd = strtoul(&str[3], &endptr, 0);
|
||||
if (endptr == &str[3])
|
||||
return -EINVAL;
|
||||
|
||||
comma = strchr(str, ',');
|
||||
if (comma) {
|
||||
if (endptr != comma)
|
||||
return -EINVAL;
|
||||
|
||||
config->ctl_fd_ack = strtoul(comma + 1, &endptr, 0);
|
||||
if (endptr == comma + 1 || *endptr != '\0')
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct option stat_options[] = {
|
||||
OPT_BOOLEAN('T', "transaction", &transaction_run,
|
||||
"hardware transaction statistics"),
|
||||
|
@ -1041,8 +1139,8 @@ static struct option stat_options[] = {
|
|||
"aggregate counts per thread", AGGR_THREAD),
|
||||
OPT_SET_UINT(0, "per-node", &stat_config.aggr_mode,
|
||||
"aggregate counts per numa node", AGGR_NODE),
|
||||
OPT_UINTEGER('D', "delay", &stat_config.initial_delay,
|
||||
"ms to wait before starting measurement after program start"),
|
||||
OPT_INTEGER('D', "delay", &stat_config.initial_delay,
|
||||
"ms to wait before starting measurement after program start (-1: start with events disabled)"),
|
||||
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,
|
||||
|
@ -1071,6 +1169,10 @@ static struct option stat_options[] = {
|
|||
"libpfm4 event selector. use 'perf list' to list available events",
|
||||
parse_libpfm_events_option),
|
||||
#endif
|
||||
OPT_CALLBACK(0, "control", &stat_config, "fd:ctl-fd[,ack-fd]",
|
||||
"Listen on ctl-fd descriptor for command to control measurement ('enable': enable events, 'disable': disable events).\n"
|
||||
"\t\t\t Optionally send control command completion ('ack\\n') to ack-fd descriptor.",
|
||||
parse_control_option),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
@ -1679,19 +1781,17 @@ static int add_default_attributes(void)
|
|||
if (target__has_cpu(&target))
|
||||
default_attrs0[0].config = PERF_COUNT_SW_CPU_CLOCK;
|
||||
|
||||
if (perf_evlist__add_default_attrs(evsel_list, default_attrs0) < 0)
|
||||
if (evlist__add_default_attrs(evsel_list, default_attrs0) < 0)
|
||||
return -1;
|
||||
if (pmu_have_event("cpu", "stalled-cycles-frontend")) {
|
||||
if (perf_evlist__add_default_attrs(evsel_list,
|
||||
frontend_attrs) < 0)
|
||||
if (evlist__add_default_attrs(evsel_list, frontend_attrs) < 0)
|
||||
return -1;
|
||||
}
|
||||
if (pmu_have_event("cpu", "stalled-cycles-backend")) {
|
||||
if (perf_evlist__add_default_attrs(evsel_list,
|
||||
backend_attrs) < 0)
|
||||
if (evlist__add_default_attrs(evsel_list, backend_attrs) < 0)
|
||||
return -1;
|
||||
}
|
||||
if (perf_evlist__add_default_attrs(evsel_list, default_attrs1) < 0)
|
||||
if (evlist__add_default_attrs(evsel_list, default_attrs1) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1701,21 +1801,21 @@ static int add_default_attributes(void)
|
|||
return 0;
|
||||
|
||||
/* Append detailed run extra attributes: */
|
||||
if (perf_evlist__add_default_attrs(evsel_list, detailed_attrs) < 0)
|
||||
if (evlist__add_default_attrs(evsel_list, detailed_attrs) < 0)
|
||||
return -1;
|
||||
|
||||
if (detailed_run < 2)
|
||||
return 0;
|
||||
|
||||
/* Append very detailed run extra attributes: */
|
||||
if (perf_evlist__add_default_attrs(evsel_list, very_detailed_attrs) < 0)
|
||||
if (evlist__add_default_attrs(evsel_list, very_detailed_attrs) < 0)
|
||||
return -1;
|
||||
|
||||
if (detailed_run < 3)
|
||||
return 0;
|
||||
|
||||
/* Append very, very detailed run extra attributes: */
|
||||
return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs);
|
||||
return evlist__add_default_attrs(evsel_list, very_very_detailed_attrs);
|
||||
}
|
||||
|
||||
static const char * const stat_record_usage[] = {
|
||||
|
@ -2242,6 +2342,9 @@ int cmd_stat(int argc, const char **argv)
|
|||
signal(SIGALRM, skip_signal);
|
||||
signal(SIGABRT, skip_signal);
|
||||
|
||||
if (evlist__initialize_ctlfd(evsel_list, stat_config.ctl_fd, stat_config.ctl_fd_ack))
|
||||
goto out;
|
||||
|
||||
status = 0;
|
||||
for (run_idx = 0; forever || run_idx < stat_config.run_count; run_idx++) {
|
||||
if (stat_config.run_count != 1 && verbose > 0)
|
||||
|
@ -2261,6 +2364,8 @@ int cmd_stat(int argc, const char **argv)
|
|||
if (!forever && status != -1 && (!interval || stat_config.summary))
|
||||
print_counters(NULL, argc, argv);
|
||||
|
||||
evlist__finalize_ctlfd(evsel_list);
|
||||
|
||||
if (STAT_RECORD) {
|
||||
/*
|
||||
* We synthesize the kernel mmap record just so that older tools
|
||||
|
@ -2307,6 +2412,7 @@ out:
|
|||
|
||||
evlist__delete(evsel_list);
|
||||
|
||||
metricgroup__rblist_exit(&stat_config.metric_events);
|
||||
runtime_stat_delete(&stat_config);
|
||||
|
||||
return status;
|
||||
|
|
|
@ -1627,7 +1627,7 @@ int cmd_top(int argc, const char **argv)
|
|||
goto out_delete_evlist;
|
||||
|
||||
if (!top.evlist->core.nr_entries &&
|
||||
perf_evlist__add_default(top.evlist) < 0) {
|
||||
evlist__add_default(top.evlist) < 0) {
|
||||
pr_err("Not enough memory for event selector list\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
|
|
@ -3917,8 +3917,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
|||
}
|
||||
|
||||
if (trace->sched &&
|
||||
perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime",
|
||||
trace__sched_stat_runtime))
|
||||
evlist__add_newtp(evlist, "sched", "sched_stat_runtime", trace__sched_stat_runtime))
|
||||
goto out_error_sched_stat_runtime;
|
||||
/*
|
||||
* If a global cgroup was set, apply it to all the events without an
|
||||
|
@ -4150,11 +4149,11 @@ out_error_raw_syscalls:
|
|||
goto out_error;
|
||||
|
||||
out_error_mmap:
|
||||
perf_evlist__strerror_mmap(evlist, errno, errbuf, sizeof(errbuf));
|
||||
evlist__strerror_mmap(evlist, errno, errbuf, sizeof(errbuf));
|
||||
goto out_error;
|
||||
|
||||
out_error_open:
|
||||
perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf));
|
||||
evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf));
|
||||
|
||||
out_error:
|
||||
fprintf(trace->output, "%s\n", errbuf);
|
||||
|
@ -4813,7 +4812,7 @@ int cmd_trace(int argc, const char **argv)
|
|||
"per thread proc mmap processing timeout in ms"),
|
||||
OPT_CALLBACK('G', "cgroup", &trace, "name", "monitor event in cgroup name only",
|
||||
trace__parse_cgroups),
|
||||
OPT_UINTEGER('D', "delay", &trace.opts.initial_delay,
|
||||
OPT_INTEGER('D', "delay", &trace.opts.initial_delay,
|
||||
"ms to wait before starting measurement after program "
|
||||
"start"),
|
||||
OPTS_EVSWITCH(&trace.evswitch),
|
||||
|
|
|
@ -15,5 +15,40 @@
|
|||
"MetricExpr": "(hv_24x7@PM_PB_CYC\\,chip\\=?@ )",
|
||||
"MetricName": "PowerBUS_Frequency",
|
||||
"ScaleUnit": "2.5e-7GHz"
|
||||
},
|
||||
{
|
||||
"MetricExpr" : "nest_mcs01_imc@PM_MCS01_128B_RD_DISP_PORT01@ + nest_mcs01_imc@PM_MCS01_128B_RD_DISP_PORT23@",
|
||||
"MetricName" : "mcs01-read",
|
||||
"MetricGroup" : "memory_bw",
|
||||
"ScaleUnit": "6.1e-5MB"
|
||||
},
|
||||
{
|
||||
"MetricExpr" : "nest_mcs23_imc@PM_MCS23_128B_RD_DISP_PORT01@ + nest_mcs23_imc@PM_MCS23_128B_RD_DISP_PORT23@",
|
||||
"MetricName" : "mcs23-read",
|
||||
"MetricGroup" : "memory_bw",
|
||||
"ScaleUnit": "6.1e-5MB"
|
||||
},
|
||||
{
|
||||
"MetricExpr" : "nest_mcs01_imc@PM_MCS01_128B_WR_DISP_PORT01@ + nest_mcs01_imc@PM_MCS01_128B_WR_DISP_PORT23@",
|
||||
"MetricName" : "mcs01-write",
|
||||
"MetricGroup" : "memory_bw",
|
||||
"ScaleUnit": "6.1e-5MB"
|
||||
},
|
||||
{
|
||||
"MetricExpr" : "nest_mcs23_imc@PM_MCS23_128B_WR_DISP_PORT01@ + nest_mcs23_imc@PM_MCS23_128B_WR_DISP_PORT23@",
|
||||
"MetricName" : "mcs23-write",
|
||||
"MetricGroup" : "memory-bandwidth",
|
||||
"ScaleUnit": "6.1e-5MB"
|
||||
},
|
||||
{
|
||||
"MetricExpr" : "nest_powerbus0_imc@PM_PB_CYC@",
|
||||
"MetricName" : "powerbus_freq",
|
||||
"ScaleUnit": "1e-9GHz"
|
||||
},
|
||||
{
|
||||
"MetricExpr" : "(nest_mcs01_imc@PM_MCS01_128B_RD_DISP_PORT01@ + nest_mcs01_imc@PM_MCS01_128B_RD_DISP_PORT23@ + nest_mcs23_imc@PM_MCS23_128B_RD_DISP_PORT01@ + nest_mcs23_imc@PM_MCS23_128B_RD_DISP_PORT23@ + nest_mcs01_imc@PM_MCS01_128B_WR_DISP_PORT01@ + nest_mcs01_imc@PM_MCS01_128B_WR_DISP_PORT23@ + nest_mcs23_imc@PM_MCS23_128B_WR_DISP_PORT01@ + nest_mcs23_imc@PM_MCS23_128B_WR_DISP_PORT23@)",
|
||||
"MetricName" : "Memory-bandwidth-MCS",
|
||||
"MetricGroup" : "memory_bw",
|
||||
"ScaleUnit": "6.1e-5MB"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -59,6 +59,7 @@ perf-y += genelf.o
|
|||
perf-y += api-io.o
|
||||
perf-y += demangle-java-test.o
|
||||
perf-y += pfm.o
|
||||
perf-y += parse-metric.o
|
||||
|
||||
$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
|
||||
$(call rule_mkdir)
|
||||
|
|
|
@ -53,6 +53,7 @@ Following tests are defined (with perf commands):
|
|||
perf record -i kill (test-record-no-inherit)
|
||||
perf record -n kill (test-record-no-samples)
|
||||
perf record -c 100 -P kill (test-record-period)
|
||||
perf record -c 1 --pfm-events=cycles:period=2 (test-record-pfm-period)
|
||||
perf record -R kill (test-record-raw)
|
||||
perf stat -e cycles kill (test-stat-basic)
|
||||
perf stat kill (test-stat-default)
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
[config]
|
||||
command = record
|
||||
args = --no-bpf-event -c 10000 --pfm-events=cycles:period=77777 kill >/dev/null 2>&1
|
||||
ret = 1
|
||||
|
||||
[event:base-record]
|
||||
sample_period=77777
|
||||
sample_type=7
|
||||
freq=0
|
|
@ -337,6 +337,10 @@ static struct test generic_tests[] = {
|
|||
.desc = "Demangle Java",
|
||||
.func = test__demangle_java,
|
||||
},
|
||||
{
|
||||
.desc = "Parse and process metrics",
|
||||
.func = test__parse_metric,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
|
|
|
@ -678,7 +678,7 @@ static int do_test_code_reading(bool try_kcore)
|
|||
|
||||
if (verbose > 0) {
|
||||
char errbuf[512];
|
||||
perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf));
|
||||
evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf));
|
||||
pr_debug("perf_evlist__open() failed!\n%s\n", errbuf);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,14 +18,15 @@ 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)
|
||||
{
|
||||
struct expr_id_data *val_ptr;
|
||||
const char *p;
|
||||
double val, *val_ptr;
|
||||
double val;
|
||||
int ret;
|
||||
struct expr_parse_ctx ctx;
|
||||
|
||||
expr__ctx_init(&ctx);
|
||||
expr__add_id(&ctx, strdup("FOO"), 1);
|
||||
expr__add_id(&ctx, strdup("BAR"), 2);
|
||||
expr__add_id_val(&ctx, strdup("FOO"), 1);
|
||||
expr__add_id_val(&ctx, strdup("BAR"), 2);
|
||||
|
||||
ret = test(&ctx, "1+1", 2);
|
||||
ret |= test(&ctx, "FOO+BAR", 3);
|
||||
|
@ -39,6 +40,14 @@ int test__expr(struct test *t __maybe_unused, int subtest __maybe_unused)
|
|||
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);
|
||||
ret |= test(&ctx, "d_ratio(1, 2)", 0.5);
|
||||
ret |= test(&ctx, "d_ratio(2.5, 0)", 0);
|
||||
ret |= test(&ctx, "1.1 < 2.2", 1);
|
||||
ret |= test(&ctx, "2.2 > 1.1", 1);
|
||||
ret |= test(&ctx, "1.1 < 1.1", 0);
|
||||
ret |= test(&ctx, "2.2 > 2.2", 0);
|
||||
ret |= test(&ctx, "2.2 < 1.1", 0);
|
||||
ret |= test(&ctx, "1.1 > 2.2", 0);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -12,6 +12,7 @@ static void fdarray__init_revents(struct fdarray *fda, short revents)
|
|||
|
||||
for (fd = 0; fd < fda->nr; ++fd) {
|
||||
fda->entries[fd].fd = fda->nr - fd;
|
||||
fda->entries[fd].events = revents;
|
||||
fda->entries[fd].revents = revents;
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +30,7 @@ static int fdarray__fprintf_prefix(struct fdarray *fda, const char *prefix, FILE
|
|||
|
||||
int test__fdarray__filter(struct test *test __maybe_unused, int subtest __maybe_unused)
|
||||
{
|
||||
int nr_fds, expected_fd[2], fd, err = TEST_FAIL;
|
||||
int nr_fds, err = TEST_FAIL;
|
||||
struct fdarray *fda = fdarray__new(5, 5);
|
||||
|
||||
if (fda == NULL) {
|
||||
|
@ -55,7 +56,6 @@ int test__fdarray__filter(struct test *test __maybe_unused, int subtest __maybe_
|
|||
|
||||
fdarray__init_revents(fda, POLLHUP);
|
||||
fda->entries[2].revents = POLLIN;
|
||||
expected_fd[0] = fda->entries[2].fd;
|
||||
|
||||
pr_debug("\nfiltering all but fda->entries[2]:");
|
||||
fdarray__fprintf_prefix(fda, "before", stderr);
|
||||
|
@ -66,17 +66,9 @@ int test__fdarray__filter(struct test *test __maybe_unused, int subtest __maybe_
|
|||
goto out_delete;
|
||||
}
|
||||
|
||||
if (fda->entries[0].fd != expected_fd[0]) {
|
||||
pr_debug("\nfda->entries[0].fd=%d != %d\n",
|
||||
fda->entries[0].fd, expected_fd[0]);
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
fdarray__init_revents(fda, POLLHUP);
|
||||
fda->entries[0].revents = POLLIN;
|
||||
expected_fd[0] = fda->entries[0].fd;
|
||||
fda->entries[3].revents = POLLIN;
|
||||
expected_fd[1] = fda->entries[3].fd;
|
||||
|
||||
pr_debug("\nfiltering all but (fda->entries[0], fda->entries[3]):");
|
||||
fdarray__fprintf_prefix(fda, "before", stderr);
|
||||
|
@ -88,14 +80,6 @@ int test__fdarray__filter(struct test *test __maybe_unused, int subtest __maybe_
|
|||
goto out_delete;
|
||||
}
|
||||
|
||||
for (fd = 0; fd < 2; ++fd) {
|
||||
if (fda->entries[fd].fd != expected_fd[fd]) {
|
||||
pr_debug("\nfda->entries[%d].fd=%d != %d\n", fd,
|
||||
fda->entries[fd].fd, expected_fd[fd]);
|
||||
goto out_delete;
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("\n");
|
||||
|
||||
err = 0;
|
||||
|
@ -128,7 +112,7 @@ int test__fdarray__add(struct test *test __maybe_unused, int subtest __maybe_unu
|
|||
}
|
||||
|
||||
#define FDA_ADD(_idx, _fd, _revents, _nr) \
|
||||
if (fdarray__add(fda, _fd, _revents) < 0) { \
|
||||
if (fdarray__add(fda, _fd, _revents, fdarray_flag__default) < 0) { \
|
||||
pr_debug("\n%d: fdarray__add(fda, %d, %d) failed!", \
|
||||
__LINE__,_fd, _revents); \
|
||||
goto out_delete; \
|
||||
|
|
|
@ -631,6 +631,34 @@ static int test__checkterms_simple(struct list_head *terms)
|
|||
TEST_ASSERT_VAL("wrong val", term->val.num == 1);
|
||||
TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "umask"));
|
||||
|
||||
/*
|
||||
* read
|
||||
*
|
||||
* The perf_pmu__test_parse_init injects 'read' term into
|
||||
* perf_pmu_events_list, so 'read' is evaluated as read term
|
||||
* and not as raw event with 'ead' hex value.
|
||||
*/
|
||||
term = list_entry(term->list.next, struct parse_events_term, list);
|
||||
TEST_ASSERT_VAL("wrong type term",
|
||||
term->type_term == PARSE_EVENTS__TERM_TYPE_USER);
|
||||
TEST_ASSERT_VAL("wrong type val",
|
||||
term->type_val == PARSE_EVENTS__TERM_TYPE_NUM);
|
||||
TEST_ASSERT_VAL("wrong val", term->val.num == 1);
|
||||
TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "read"));
|
||||
|
||||
/*
|
||||
* r0xead
|
||||
*
|
||||
* To be still able to pass 'ead' value with 'r' syntax,
|
||||
* we added support to parse 'r0xHEX' event.
|
||||
*/
|
||||
term = list_entry(term->list.next, struct parse_events_term, list);
|
||||
TEST_ASSERT_VAL("wrong type term",
|
||||
term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG);
|
||||
TEST_ASSERT_VAL("wrong type val",
|
||||
term->type_val == PARSE_EVENTS__TERM_TYPE_NUM);
|
||||
TEST_ASSERT_VAL("wrong val", term->val.num == 0xead);
|
||||
TEST_ASSERT_VAL("wrong config", !term->config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1766,6 +1794,11 @@ static struct evlist_test test__events_pmu[] = {
|
|||
.check = test__checkevent_raw_pmu,
|
||||
.id = 4,
|
||||
},
|
||||
{
|
||||
.name = "software/r0x1a/",
|
||||
.check = test__checkevent_raw_pmu,
|
||||
.id = 4,
|
||||
},
|
||||
};
|
||||
|
||||
struct terms_test {
|
||||
|
@ -1776,7 +1809,7 @@ struct terms_test {
|
|||
|
||||
static struct terms_test test__terms[] = {
|
||||
[0] = {
|
||||
.str = "config=10,config1,config2=3,umask=1",
|
||||
.str = "config=10,config1,config2=3,umask=1,read,r0xead",
|
||||
.check = test__checkterms_simple,
|
||||
},
|
||||
};
|
||||
|
@ -1836,6 +1869,13 @@ static int test_term(struct terms_test *t)
|
|||
|
||||
INIT_LIST_HEAD(&terms);
|
||||
|
||||
/*
|
||||
* The perf_pmu__test_parse_init prepares perf_pmu_events_list
|
||||
* which gets freed in parse_events_terms.
|
||||
*/
|
||||
if (perf_pmu__test_parse_init())
|
||||
return -1;
|
||||
|
||||
ret = parse_events_terms(&terms, t->str);
|
||||
if (ret) {
|
||||
pr_debug("failed to parse terms '%s', err %d\n",
|
||||
|
|
|
@ -0,0 +1,352 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/compiler.h>
|
||||
#include <string.h>
|
||||
#include <perf/cpumap.h>
|
||||
#include <perf/evlist.h>
|
||||
#include "metricgroup.h"
|
||||
#include "tests.h"
|
||||
#include "pmu-events/pmu-events.h"
|
||||
#include "evlist.h"
|
||||
#include "rblist.h"
|
||||
#include "debug.h"
|
||||
#include "expr.h"
|
||||
#include "stat.h"
|
||||
#include <perf/cpumap.h>
|
||||
#include <perf/evlist.h>
|
||||
|
||||
static struct pmu_event pme_test[] = {
|
||||
{
|
||||
.metric_expr = "inst_retired.any / cpu_clk_unhalted.thread",
|
||||
.metric_name = "IPC",
|
||||
.metric_group = "group1",
|
||||
},
|
||||
{
|
||||
.metric_expr = "idq_uops_not_delivered.core / (4 * (( ( cpu_clk_unhalted.thread / 2 ) * "
|
||||
"( 1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk ) )))",
|
||||
.metric_name = "Frontend_Bound_SMT",
|
||||
},
|
||||
{
|
||||
.metric_expr = "l1d\\-loads\\-misses / inst_retired.any",
|
||||
.metric_name = "dcache_miss_cpi",
|
||||
},
|
||||
{
|
||||
.metric_expr = "l1i\\-loads\\-misses / inst_retired.any",
|
||||
.metric_name = "icache_miss_cycles",
|
||||
},
|
||||
{
|
||||
.metric_expr = "(dcache_miss_cpi + icache_miss_cycles)",
|
||||
.metric_name = "cache_miss_cycles",
|
||||
.metric_group = "group1",
|
||||
},
|
||||
{
|
||||
.metric_expr = "l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit",
|
||||
.metric_name = "DCache_L2_All_Hits",
|
||||
},
|
||||
{
|
||||
.metric_expr = "max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + "
|
||||
"l2_rqsts.pf_miss + l2_rqsts.rfo_miss",
|
||||
.metric_name = "DCache_L2_All_Miss",
|
||||
},
|
||||
{
|
||||
.metric_expr = "dcache_l2_all_hits + dcache_l2_all_miss",
|
||||
.metric_name = "DCache_L2_All",
|
||||
},
|
||||
{
|
||||
.metric_expr = "d_ratio(dcache_l2_all_hits, dcache_l2_all)",
|
||||
.metric_name = "DCache_L2_Hits",
|
||||
},
|
||||
{
|
||||
.metric_expr = "d_ratio(dcache_l2_all_miss, dcache_l2_all)",
|
||||
.metric_name = "DCache_L2_Misses",
|
||||
},
|
||||
{
|
||||
.metric_expr = "ipc + m2",
|
||||
.metric_name = "M1",
|
||||
},
|
||||
{
|
||||
.metric_expr = "ipc + m1",
|
||||
.metric_name = "M2",
|
||||
},
|
||||
{
|
||||
.metric_expr = "1/m3",
|
||||
.metric_name = "M3",
|
||||
}
|
||||
};
|
||||
|
||||
static struct pmu_events_map map = {
|
||||
.cpuid = "test",
|
||||
.version = "1",
|
||||
.type = "core",
|
||||
.table = pme_test,
|
||||
};
|
||||
|
||||
struct value {
|
||||
const char *event;
|
||||
u64 val;
|
||||
};
|
||||
|
||||
static u64 find_value(const char *name, struct value *values)
|
||||
{
|
||||
struct value *v = values;
|
||||
|
||||
while (v->event) {
|
||||
if (!strcmp(name, v->event))
|
||||
return v->val;
|
||||
v++;
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void load_runtime_stat(struct runtime_stat *st, struct evlist *evlist,
|
||||
struct value *vals)
|
||||
{
|
||||
struct evsel *evsel;
|
||||
u64 count;
|
||||
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
count = find_value(evsel->name, vals);
|
||||
perf_stat__update_shadow_stats(evsel, count, 0, st);
|
||||
}
|
||||
}
|
||||
|
||||
static double compute_single(struct rblist *metric_events, struct evlist *evlist,
|
||||
struct runtime_stat *st, const char *name)
|
||||
{
|
||||
struct metric_expr *mexp;
|
||||
struct metric_event *me;
|
||||
struct evsel *evsel;
|
||||
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
me = metricgroup__lookup(metric_events, evsel, false);
|
||||
if (me != NULL) {
|
||||
list_for_each_entry (mexp, &me->head, nd) {
|
||||
if (strcmp(mexp->metric_name, name))
|
||||
continue;
|
||||
return test_generic_metric(mexp, 0, st);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0.;
|
||||
}
|
||||
|
||||
static int __compute_metric(const char *name, struct value *vals,
|
||||
const char *name1, double *ratio1,
|
||||
const char *name2, double *ratio2)
|
||||
{
|
||||
struct rblist metric_events = {
|
||||
.nr_entries = 0,
|
||||
};
|
||||
struct perf_cpu_map *cpus;
|
||||
struct runtime_stat st;
|
||||
struct evlist *evlist;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* We need to prepare evlist for stat mode running on CPU 0
|
||||
* because that's where all the stats are going to be created.
|
||||
*/
|
||||
evlist = evlist__new();
|
||||
if (!evlist)
|
||||
return -ENOMEM;
|
||||
|
||||
cpus = perf_cpu_map__new("0");
|
||||
if (!cpus)
|
||||
return -ENOMEM;
|
||||
|
||||
perf_evlist__set_maps(&evlist->core, cpus, NULL);
|
||||
|
||||
/* Parse the metric into metric_events list. */
|
||||
err = metricgroup__parse_groups_test(evlist, &map, name,
|
||||
false, false,
|
||||
&metric_events);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (perf_evlist__alloc_stats(evlist, false))
|
||||
return -1;
|
||||
|
||||
/* Load the runtime stats with given numbers for events. */
|
||||
runtime_stat__init(&st);
|
||||
load_runtime_stat(&st, evlist, vals);
|
||||
|
||||
/* And execute the metric */
|
||||
if (name1 && ratio1)
|
||||
*ratio1 = compute_single(&metric_events, evlist, &st, name1);
|
||||
if (name2 && ratio2)
|
||||
*ratio2 = compute_single(&metric_events, evlist, &st, name2);
|
||||
|
||||
/* ... clenup. */
|
||||
metricgroup__rblist_exit(&metric_events);
|
||||
runtime_stat__exit(&st);
|
||||
perf_evlist__free_stats(evlist);
|
||||
perf_cpu_map__put(cpus);
|
||||
evlist__delete(evlist);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int compute_metric(const char *name, struct value *vals, double *ratio)
|
||||
{
|
||||
return __compute_metric(name, vals, name, ratio, NULL, NULL);
|
||||
}
|
||||
|
||||
static int compute_metric_group(const char *name, struct value *vals,
|
||||
const char *name1, double *ratio1,
|
||||
const char *name2, double *ratio2)
|
||||
{
|
||||
return __compute_metric(name, vals, name1, ratio1, name2, ratio2);
|
||||
}
|
||||
|
||||
static int test_ipc(void)
|
||||
{
|
||||
double ratio;
|
||||
struct value vals[] = {
|
||||
{ .event = "inst_retired.any", .val = 300 },
|
||||
{ .event = "cpu_clk_unhalted.thread", .val = 200 },
|
||||
{ .event = NULL, },
|
||||
};
|
||||
|
||||
TEST_ASSERT_VAL("failed to compute metric",
|
||||
compute_metric("IPC", vals, &ratio) == 0);
|
||||
|
||||
TEST_ASSERT_VAL("IPC failed, wrong ratio",
|
||||
ratio == 1.5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_frontend(void)
|
||||
{
|
||||
double ratio;
|
||||
struct value vals[] = {
|
||||
{ .event = "idq_uops_not_delivered.core", .val = 300 },
|
||||
{ .event = "cpu_clk_unhalted.thread", .val = 200 },
|
||||
{ .event = "cpu_clk_unhalted.one_thread_active", .val = 400 },
|
||||
{ .event = "cpu_clk_unhalted.ref_xclk", .val = 600 },
|
||||
{ .event = NULL, },
|
||||
};
|
||||
|
||||
TEST_ASSERT_VAL("failed to compute metric",
|
||||
compute_metric("Frontend_Bound_SMT", vals, &ratio) == 0);
|
||||
|
||||
TEST_ASSERT_VAL("Frontend_Bound_SMT failed, wrong ratio",
|
||||
ratio == 0.45);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_cache_miss_cycles(void)
|
||||
{
|
||||
double ratio;
|
||||
struct value vals[] = {
|
||||
{ .event = "l1d-loads-misses", .val = 300 },
|
||||
{ .event = "l1i-loads-misses", .val = 200 },
|
||||
{ .event = "inst_retired.any", .val = 400 },
|
||||
{ .event = NULL, },
|
||||
};
|
||||
|
||||
TEST_ASSERT_VAL("failed to compute metric",
|
||||
compute_metric("cache_miss_cycles", vals, &ratio) == 0);
|
||||
|
||||
TEST_ASSERT_VAL("cache_miss_cycles failed, wrong ratio",
|
||||
ratio == 1.25);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DCache_L2_All_Hits = l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hi
|
||||
* DCache_L2_All_Miss = max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) +
|
||||
* l2_rqsts.pf_miss + l2_rqsts.rfo_miss
|
||||
* DCache_L2_All = dcache_l2_all_hits + dcache_l2_all_miss
|
||||
* DCache_L2_Hits = d_ratio(dcache_l2_all_hits, dcache_l2_all)
|
||||
* DCache_L2_Misses = d_ratio(dcache_l2_all_miss, dcache_l2_all)
|
||||
*
|
||||
* l2_rqsts.demand_data_rd_hit = 100
|
||||
* l2_rqsts.pf_hit = 200
|
||||
* l2_rqsts.rfo_hi = 300
|
||||
* l2_rqsts.all_demand_data_rd = 400
|
||||
* l2_rqsts.pf_miss = 500
|
||||
* l2_rqsts.rfo_miss = 600
|
||||
*
|
||||
* DCache_L2_All_Hits = 600
|
||||
* DCache_L2_All_Miss = MAX(400 - 100, 0) + 500 + 600 = 1400
|
||||
* DCache_L2_All = 600 + 1400 = 2000
|
||||
* DCache_L2_Hits = 600 / 2000 = 0.3
|
||||
* DCache_L2_Misses = 1400 / 2000 = 0.7
|
||||
*/
|
||||
static int test_dcache_l2(void)
|
||||
{
|
||||
double ratio;
|
||||
struct value vals[] = {
|
||||
{ .event = "l2_rqsts.demand_data_rd_hit", .val = 100 },
|
||||
{ .event = "l2_rqsts.pf_hit", .val = 200 },
|
||||
{ .event = "l2_rqsts.rfo_hit", .val = 300 },
|
||||
{ .event = "l2_rqsts.all_demand_data_rd", .val = 400 },
|
||||
{ .event = "l2_rqsts.pf_miss", .val = 500 },
|
||||
{ .event = "l2_rqsts.rfo_miss", .val = 600 },
|
||||
{ .event = NULL, },
|
||||
};
|
||||
|
||||
TEST_ASSERT_VAL("failed to compute metric",
|
||||
compute_metric("DCache_L2_Hits", vals, &ratio) == 0);
|
||||
|
||||
TEST_ASSERT_VAL("DCache_L2_Hits failed, wrong ratio",
|
||||
ratio == 0.3);
|
||||
|
||||
TEST_ASSERT_VAL("failed to compute metric",
|
||||
compute_metric("DCache_L2_Misses", vals, &ratio) == 0);
|
||||
|
||||
TEST_ASSERT_VAL("DCache_L2_Misses failed, wrong ratio",
|
||||
ratio == 0.7);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_recursion_fail(void)
|
||||
{
|
||||
double ratio;
|
||||
struct value vals[] = {
|
||||
{ .event = "inst_retired.any", .val = 300 },
|
||||
{ .event = "cpu_clk_unhalted.thread", .val = 200 },
|
||||
{ .event = NULL, },
|
||||
};
|
||||
|
||||
TEST_ASSERT_VAL("failed to find recursion",
|
||||
compute_metric("M1", vals, &ratio) == -1);
|
||||
|
||||
TEST_ASSERT_VAL("failed to find recursion",
|
||||
compute_metric("M3", vals, &ratio) == -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_metric_group(void)
|
||||
{
|
||||
double ratio1, ratio2;
|
||||
struct value vals[] = {
|
||||
{ .event = "cpu_clk_unhalted.thread", .val = 200 },
|
||||
{ .event = "l1d-loads-misses", .val = 300 },
|
||||
{ .event = "l1i-loads-misses", .val = 200 },
|
||||
{ .event = "inst_retired.any", .val = 400 },
|
||||
{ .event = NULL, },
|
||||
};
|
||||
|
||||
TEST_ASSERT_VAL("failed to find recursion",
|
||||
compute_metric_group("group1", vals,
|
||||
"IPC", &ratio1,
|
||||
"cache_miss_cycles", &ratio2) == 0);
|
||||
|
||||
TEST_ASSERT_VAL("group IPC failed, wrong ratio",
|
||||
ratio1 == 2.0);
|
||||
|
||||
TEST_ASSERT_VAL("group cache_miss_cycles failed, wrong ratio",
|
||||
ratio2 == 1.25);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test__parse_metric(struct test *test __maybe_unused, int subtest __maybe_unused)
|
||||
{
|
||||
TEST_ASSERT_VAL("IPC failed", test_ipc() == 0);
|
||||
TEST_ASSERT_VAL("frontend failed", test_frontend() == 0);
|
||||
TEST_ASSERT_VAL("cache_miss_cycles failed", test_cache_miss_cycles() == 0);
|
||||
TEST_ASSERT_VAL("DCache_L2 failed", test_dcache_l2() == 0);
|
||||
TEST_ASSERT_VAL("recursion fail failed", test_recursion_fail() == 0);
|
||||
TEST_ASSERT_VAL("test metric group", test_metric_group() == 0);
|
||||
return 0;
|
||||
}
|
|
@ -185,14 +185,14 @@ int test__PERF_RECORD(struct test *test __maybe_unused, int subtest __maybe_unus
|
|||
err = perf_evlist__parse_sample(evlist, event, &sample);
|
||||
if (err < 0) {
|
||||
if (verbose > 0)
|
||||
perf_event__fprintf(event, stderr);
|
||||
perf_event__fprintf(event, NULL, stderr);
|
||||
pr_debug("Couldn't parse sample\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
if (verbose > 0) {
|
||||
pr_info("%" PRIu64" %d ", sample.time, sample.cpu);
|
||||
perf_event__fprintf(event, stderr);
|
||||
perf_event__fprintf(event, NULL, stderr);
|
||||
}
|
||||
|
||||
if (prev_time > sample.time) {
|
||||
|
|
|
@ -390,9 +390,9 @@ static bool is_number(const char *str)
|
|||
return errno == 0 && end_ptr != str;
|
||||
}
|
||||
|
||||
static int check_parse_id(const char *id, bool same_cpu, struct pmu_event *pe)
|
||||
static int check_parse_id(const char *id, struct parse_events_error *error,
|
||||
struct perf_pmu *fake_pmu)
|
||||
{
|
||||
struct parse_events_error error;
|
||||
struct evlist *evlist;
|
||||
int ret;
|
||||
|
||||
|
@ -401,8 +401,18 @@ static int check_parse_id(const char *id, bool same_cpu, struct pmu_event *pe)
|
|||
return 0;
|
||||
|
||||
evlist = evlist__new();
|
||||
memset(&error, 0, sizeof(error));
|
||||
ret = parse_events(evlist, id, &error);
|
||||
if (!evlist)
|
||||
return -ENOMEM;
|
||||
ret = __parse_events(evlist, id, error, fake_pmu);
|
||||
evlist__delete(evlist);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_parse_cpu(const char *id, bool same_cpu, struct pmu_event *pe)
|
||||
{
|
||||
struct parse_events_error error = { .idx = 0, };
|
||||
|
||||
int ret = check_parse_id(id, &error, NULL);
|
||||
if (ret && same_cpu) {
|
||||
pr_warning("Parse event failed metric '%s' id '%s' expr '%s'\n",
|
||||
pe->metric_name, id, pe->metric_expr);
|
||||
|
@ -413,7 +423,18 @@ static int check_parse_id(const char *id, bool same_cpu, struct pmu_event *pe)
|
|||
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 int check_parse_fake(const char *id)
|
||||
{
|
||||
struct parse_events_error error = { .idx = 0, };
|
||||
int ret = check_parse_id(id, &error, &perf_pmu__fake);
|
||||
|
||||
free(error.str);
|
||||
free(error.help);
|
||||
free(error.first_str);
|
||||
|
@ -471,10 +492,10 @@ static int test_parsing(void)
|
|||
*/
|
||||
k = 1;
|
||||
hashmap__for_each_entry((&ctx.ids), cur, bkt)
|
||||
expr__add_id(&ctx, strdup(cur->key), k++);
|
||||
expr__add_id_val(&ctx, strdup(cur->key), k++);
|
||||
|
||||
hashmap__for_each_entry((&ctx.ids), cur, bkt) {
|
||||
if (check_parse_id(cur->key, map == cpus_map,
|
||||
if (check_parse_cpu(cur->key, map == cpus_map,
|
||||
pe))
|
||||
ret++;
|
||||
}
|
||||
|
@ -490,6 +511,100 @@ static int test_parsing(void)
|
|||
return ret == 0 ? TEST_OK : TEST_SKIP;
|
||||
}
|
||||
|
||||
struct test_metric {
|
||||
const char *str;
|
||||
};
|
||||
|
||||
static struct test_metric metrics[] = {
|
||||
{ "(unc_p_power_state_occupancy.cores_c0 / unc_p_clockticks) * 100." },
|
||||
{ "imx8_ddr0@read\\-cycles@ * 4 * 4", },
|
||||
{ "imx8_ddr0@axid\\-read\\,axi_mask\\=0xffff\\,axi_id\\=0x0000@ * 4", },
|
||||
{ "(cstate_pkg@c2\\-residency@ / msr@tsc@) * 100", },
|
||||
{ "(imx8_ddr0@read\\-cycles@ + imx8_ddr0@write\\-cycles@)", },
|
||||
};
|
||||
|
||||
static int metric_parse_fake(const char *str)
|
||||
{
|
||||
struct expr_parse_ctx ctx;
|
||||
struct hashmap_entry *cur;
|
||||
double result;
|
||||
int ret = -1;
|
||||
size_t bkt;
|
||||
int i;
|
||||
|
||||
pr_debug("parsing '%s'\n", str);
|
||||
|
||||
expr__ctx_init(&ctx);
|
||||
if (expr__find_other(str, NULL, &ctx, 0) < 0) {
|
||||
pr_err("expr__find_other failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add all ids with a made up value. The value may
|
||||
* trigger divide by zero when subtracted and so try to
|
||||
* make them unique.
|
||||
*/
|
||||
i = 1;
|
||||
hashmap__for_each_entry((&ctx.ids), cur, bkt)
|
||||
expr__add_id_val(&ctx, strdup(cur->key), i++);
|
||||
|
||||
hashmap__for_each_entry((&ctx.ids), cur, bkt) {
|
||||
if (check_parse_fake(cur->key)) {
|
||||
pr_err("check_parse_fake failed\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (expr__parse(&result, &ctx, str, 1))
|
||||
pr_err("expr__parse failed\n");
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
expr__ctx_clear(&ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse all the metrics for current architecture,
|
||||
* or all defined cpus via the 'fake_pmu'
|
||||
* in parse_events.
|
||||
*/
|
||||
static int test_parsing_fake(void)
|
||||
{
|
||||
struct pmu_events_map *map;
|
||||
struct pmu_event *pe;
|
||||
unsigned int i, j;
|
||||
int err = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(metrics); i++) {
|
||||
err = metric_parse_fake(metrics[i].str);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (;;) {
|
||||
map = &pmu_events_map[i++];
|
||||
if (!map->table)
|
||||
break;
|
||||
j = 0;
|
||||
for (;;) {
|
||||
pe = &map->table[j++];
|
||||
if (!pe->name && !pe->metric_group && !pe->metric_name)
|
||||
break;
|
||||
if (!pe->metric_expr)
|
||||
continue;
|
||||
err = metric_parse_fake(pe->metric_expr);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
int (*func)(void);
|
||||
const char *desc;
|
||||
|
@ -506,6 +621,10 @@ static const struct {
|
|||
.func = test_parsing,
|
||||
.desc = "Parsing of PMU event table metrics",
|
||||
},
|
||||
{
|
||||
.func = test_parsing_fake,
|
||||
.desc = "Parsing of PMU event table metrics with fake PMUs",
|
||||
},
|
||||
};
|
||||
|
||||
const char *test__pmu_events_subtest_get_desc(int subtest)
|
||||
|
|
|
@ -121,6 +121,7 @@ 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);
|
||||
int test__parse_metric(struct test *test, int subtest);
|
||||
|
||||
bool test__bp_signal_is_supported(void);
|
||||
bool test__bp_account_is_supported(void);
|
||||
|
|
|
@ -209,7 +209,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser)
|
|||
ui_browser__mark_fused(browser,
|
||||
pcnt_width + 3 + notes->widths.addr + width,
|
||||
from - 1,
|
||||
to > from ? true : false);
|
||||
to > from);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -128,6 +128,7 @@ perf-y += expr-bison.o
|
|||
perf-y += expr.o
|
||||
perf-y += branch.o
|
||||
perf-y += mem2node.o
|
||||
perf-y += clockid.o
|
||||
|
||||
perf-$(CONFIG_LIBBPF) += bpf-loader.o
|
||||
perf-$(CONFIG_LIBBPF) += bpf_map.o
|
||||
|
@ -191,36 +192,60 @@ CFLAGS_llvm-utils.o += -DPERF_INCLUDE_DIR="BUILD_STR($(perf_include_dir_SQ))"
|
|||
# avoid compiler warnings in 32-bit mode
|
||||
CFLAGS_genelf_debug.o += -Wno-packed
|
||||
|
||||
$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c
|
||||
$(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-flex.h: util/parse-events.l $(OUTPUT)util/parse-events-bison.c
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) util/parse-events.l
|
||||
$(Q)$(call echo-cmd,flex)$(FLEX) -o $(OUTPUT)util/parse-events-flex.c \
|
||||
--header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) $<
|
||||
|
||||
$(OUTPUT)util/parse-events-bison.c: util/parse-events.y
|
||||
$(OUTPUT)util/parse-events-bison.c $(OUTPUT)util/parse-events-bison.h: util/parse-events.y
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,bison)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $@ -p parse_events_
|
||||
$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) \
|
||||
-o $(OUTPUT)util/parse-events-bison.c -p parse_events_
|
||||
|
||||
$(OUTPUT)util/expr-flex.c: util/expr.l $(OUTPUT)util/expr-bison.c
|
||||
$(OUTPUT)util/expr-flex.c $(OUTPUT)util/expr-flex.h: util/expr.l $(OUTPUT)util/expr-bison.c
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/expr-flex.h $(PARSER_DEBUG_FLEX) util/expr.l
|
||||
$(Q)$(call echo-cmd,flex)$(FLEX) -o $(OUTPUT)util/expr-flex.c \
|
||||
--header-file=$(OUTPUT)util/expr-flex.h $(PARSER_DEBUG_FLEX) $<
|
||||
|
||||
$(OUTPUT)util/expr-bison.c: util/expr.y
|
||||
$(OUTPUT)util/expr-bison.c $(OUTPUT)util/expr-bison.h: util/expr.y
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,bison)$(BISON) -v util/expr.y -d $(PARSER_DEBUG_BISON) -o $@ -p expr_
|
||||
$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) \
|
||||
-o $(OUTPUT)util/expr-bison.c -p expr_
|
||||
|
||||
$(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c
|
||||
$(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-flex.h: util/pmu.l $(OUTPUT)util/pmu-bison.c
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/pmu-flex.h util/pmu.l
|
||||
$(Q)$(call echo-cmd,flex)$(FLEX) -o $(OUTPUT)util/pmu-flex.c \
|
||||
--header-file=$(OUTPUT)util/pmu-flex.h $(PARSER_DEBUG_FLEX) $<
|
||||
|
||||
$(OUTPUT)util/pmu-bison.c: util/pmu.y
|
||||
$(OUTPUT)util/pmu-bison.c $(OUTPUT)util/pmu-bison.h: util/pmu.y
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,bison)$(BISON) -v util/pmu.y -d -o $@ -p perf_pmu_
|
||||
$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) \
|
||||
-o $(OUTPUT)util/pmu-bison.c -p perf_pmu_
|
||||
|
||||
CFLAGS_parse-events-flex.o += -w
|
||||
CFLAGS_pmu-flex.o += -w
|
||||
CFLAGS_expr-flex.o += -w
|
||||
CFLAGS_parse-events-bison.o += -DYYENABLE_NLS=0 -w
|
||||
CFLAGS_pmu-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w
|
||||
CFLAGS_expr-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w
|
||||
FLEX_GE_26 := $(shell expr $(shell $(FLEX) --version | sed -e 's/flex \([0-9]\+\).\([0-9]\+\)/\1\2/g') \>\= 26)
|
||||
ifeq ($(FLEX_GE_26),1)
|
||||
flex_flags := -Wno-switch-enum -Wno-switch-default -Wno-unused-function -Wno-redundant-decls -Wno-sign-compare -Wno-unused-parameter -Wno-missing-prototypes -Wno-missing-declarations
|
||||
CC_HASNT_MISLEADING_INDENTATION := $(shell echo "int main(void) { return 0 }" | $(CC) -Werror -Wno-misleading-indentation -o /dev/null -xc - 2>&1 | grep -q -- -Wno-misleading-indentation ; echo $$?)
|
||||
ifeq ($(CC_HASNT_MISLEADING_INDENTATION), 1)
|
||||
flex_flags += -Wno-misleading-indentation
|
||||
endif
|
||||
else
|
||||
flex_flags := -w
|
||||
endif
|
||||
CFLAGS_parse-events-flex.o += $(flex_flags)
|
||||
CFLAGS_pmu-flex.o += $(flex_flags)
|
||||
CFLAGS_expr-flex.o += $(flex_flags)
|
||||
|
||||
bison_flags := -DYYENABLE_NLS=0
|
||||
BISON_GE_35 := $(shell expr $(shell $(BISON) --version | grep bison | sed -e 's/.\+ \([0-9]\+\).\([0-9]\+\)/\1\2/g') \>\= 35)
|
||||
ifeq ($(BISON_GE_35),1)
|
||||
bison_flags += -Wno-unused-parameter -Wno-nested-externs -Wno-implicit-function-declaration -Wno-switch-enum
|
||||
else
|
||||
bison_flags += -w
|
||||
endif
|
||||
CFLAGS_parse-events-bison.o += $(bison_flags)
|
||||
CFLAGS_pmu-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
|
||||
CFLAGS_expr-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
|
||||
|
||||
$(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c
|
||||
$(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c
|
||||
|
|
|
@ -1621,6 +1621,7 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
|
|||
char *build_id_filename;
|
||||
char *build_id_path = NULL;
|
||||
char *pos;
|
||||
int len;
|
||||
|
||||
if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS &&
|
||||
!dso__is_kcore(dso))
|
||||
|
@ -1649,10 +1650,16 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
|
|||
if (pos && strlen(pos) < SBUILD_ID_SIZE - 2)
|
||||
dirname(build_id_path);
|
||||
|
||||
if (dso__is_kcore(dso) ||
|
||||
readlink(build_id_path, linkname, sizeof(linkname)) < 0 ||
|
||||
strstr(linkname, DSO__NAME_KALLSYMS) ||
|
||||
access(filename, R_OK)) {
|
||||
if (dso__is_kcore(dso))
|
||||
goto fallback;
|
||||
|
||||
len = readlink(build_id_path, linkname, sizeof(linkname) - 1);
|
||||
if (len < 0)
|
||||
goto fallback;
|
||||
|
||||
linkname[len] = '\0';
|
||||
if (strstr(linkname, DSO__NAME_KALLSYMS) ||
|
||||
access(filename, R_OK)) {
|
||||
fallback:
|
||||
/*
|
||||
* If we don't have build-ids or the build-id file isn't in the
|
||||
|
|
|
@ -1349,6 +1349,47 @@ void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts,
|
|||
synth_opts->initial_skip = 0;
|
||||
}
|
||||
|
||||
static int get_flag(const char **ptr, unsigned int *flags)
|
||||
{
|
||||
while (1) {
|
||||
char c = **ptr;
|
||||
|
||||
if (c >= 'a' && c <= 'z') {
|
||||
*flags |= 1 << (c - 'a');
|
||||
++*ptr;
|
||||
return 0;
|
||||
} else if (c == ' ') {
|
||||
++*ptr;
|
||||
continue;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int get_flags(const char **ptr, unsigned int *plus_flags, unsigned int *minus_flags)
|
||||
{
|
||||
while (1) {
|
||||
switch (**ptr) {
|
||||
case '+':
|
||||
++*ptr;
|
||||
if (get_flag(ptr, plus_flags))
|
||||
return -1;
|
||||
break;
|
||||
case '-':
|
||||
++*ptr;
|
||||
if (get_flag(ptr, minus_flags))
|
||||
return -1;
|
||||
break;
|
||||
case ' ':
|
||||
++*ptr;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Please check tools/perf/Documentation/perf-script.txt for information
|
||||
* about the options parsed here, which is introduced after this cset,
|
||||
|
@ -1436,9 +1477,15 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
|
|||
break;
|
||||
case 'e':
|
||||
synth_opts->errors = true;
|
||||
if (get_flags(&p, &synth_opts->error_plus_flags,
|
||||
&synth_opts->error_minus_flags))
|
||||
goto out_err;
|
||||
break;
|
||||
case 'd':
|
||||
synth_opts->log = true;
|
||||
if (get_flags(&p, &synth_opts->log_plus_flags,
|
||||
&synth_opts->log_minus_flags))
|
||||
goto out_err;
|
||||
break;
|
||||
case 'c':
|
||||
synth_opts->branches = true;
|
||||
|
@ -1507,6 +1554,9 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
|
|||
case 'a':
|
||||
synth_opts->remote_access = true;
|
||||
break;
|
||||
case 'q':
|
||||
synth_opts->quick += 1;
|
||||
break;
|
||||
case ' ':
|
||||
case ',':
|
||||
break;
|
||||
|
|
|
@ -55,6 +55,11 @@ enum itrace_period_type {
|
|||
PERF_ITRACE_PERIOD_NANOSECS,
|
||||
};
|
||||
|
||||
#define AUXTRACE_ERR_FLG_OVERFLOW (1 << ('o' - 'a'))
|
||||
#define AUXTRACE_ERR_FLG_DATA_LOST (1 << ('l' - 'a'))
|
||||
|
||||
#define AUXTRACE_LOG_FLG_ALL_PERF_EVTS (1 << ('a' - 'a'))
|
||||
|
||||
/**
|
||||
* struct itrace_synth_opts - AUX area tracing synthesis options.
|
||||
* @set: indicates whether or not options have been set
|
||||
|
@ -91,6 +96,11 @@ enum itrace_period_type {
|
|||
* @cpu_bitmap: CPUs for which to synthesize events, or NULL for all
|
||||
* @ptime_range: time intervals to trace or NULL
|
||||
* @range_num: number of time intervals to trace
|
||||
* @error_plus_flags: flags to affect what errors are reported
|
||||
* @error_minus_flags: flags to affect what errors are reported
|
||||
* @log_plus_flags: flags to affect what is logged
|
||||
* @log_minus_flags: flags to affect what is logged
|
||||
* @quick: quicker (less detailed) decoding
|
||||
*/
|
||||
struct itrace_synth_opts {
|
||||
bool set;
|
||||
|
@ -124,6 +134,11 @@ struct itrace_synth_opts {
|
|||
unsigned long *cpu_bitmap;
|
||||
struct perf_time_interval *ptime_range;
|
||||
int range_num;
|
||||
unsigned int error_plus_flags;
|
||||
unsigned int error_minus_flags;
|
||||
unsigned int log_plus_flags;
|
||||
unsigned int log_minus_flags;
|
||||
unsigned int quick;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -604,22 +619,32 @@ bool auxtrace__evsel_is_auxtrace(struct perf_session *session,
|
|||
struct evsel *evsel);
|
||||
|
||||
#define ITRACE_HELP \
|
||||
" i: synthesize instructions events\n" \
|
||||
" i[period]: synthesize instructions 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" \
|
||||
" w: synthesize ptwrite events\n" \
|
||||
" p: synthesize power events\n" \
|
||||
" e: synthesize error events\n" \
|
||||
" d: create a debug log\n" \
|
||||
" o: synthesize other events recorded due to the use\n" \
|
||||
" of aux-output (refer to perf record)\n" \
|
||||
" e[flags]: synthesize error events\n" \
|
||||
" each flag must be preceded by + or -\n" \
|
||||
" error flags are: o (overflow)\n" \
|
||||
" l (data lost)\n" \
|
||||
" d[flags]: create a debug log\n" \
|
||||
" each flag must be preceded by + or -\n" \
|
||||
" log flags are: a (all perf events)\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" \
|
||||
" G[len]: synthesize a call chain on existing event records\n" \
|
||||
" l[len]: synthesize last branch entries (use with i or x)\n" \
|
||||
" L[len]: synthesize last branch entries on existing event records\n" \
|
||||
" sNUMBER: skip initial number of events\n" \
|
||||
" q: quicker (less detailed) decoding\n" \
|
||||
" PERIOD[ns|us|ms|i|t]: specify period to sample stream\n" \
|
||||
" concatenate multiple options. Default is ibxwpe or cewp\n"
|
||||
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <subcmd/parse-options.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <strings.h>
|
||||
#include <linux/time64.h>
|
||||
#include "debug.h"
|
||||
#include "clockid.h"
|
||||
#include "record.h"
|
||||
|
||||
struct clockid_map {
|
||||
const char *name;
|
||||
int clockid;
|
||||
};
|
||||
|
||||
#define CLOCKID_MAP(n, c) \
|
||||
{ .name = n, .clockid = (c), }
|
||||
|
||||
#define CLOCKID_END { .name = NULL, }
|
||||
|
||||
|
||||
/*
|
||||
* Add the missing ones, we need to build on many distros...
|
||||
*/
|
||||
#ifndef CLOCK_MONOTONIC_RAW
|
||||
#define CLOCK_MONOTONIC_RAW 4
|
||||
#endif
|
||||
#ifndef CLOCK_BOOTTIME
|
||||
#define CLOCK_BOOTTIME 7
|
||||
#endif
|
||||
#ifndef CLOCK_TAI
|
||||
#define CLOCK_TAI 11
|
||||
#endif
|
||||
|
||||
static const struct clockid_map clockids[] = {
|
||||
/* available for all events, NMI safe */
|
||||
CLOCKID_MAP("monotonic", CLOCK_MONOTONIC),
|
||||
CLOCKID_MAP("monotonic_raw", CLOCK_MONOTONIC_RAW),
|
||||
|
||||
/* available for some events */
|
||||
CLOCKID_MAP("realtime", CLOCK_REALTIME),
|
||||
CLOCKID_MAP("boottime", CLOCK_BOOTTIME),
|
||||
CLOCKID_MAP("tai", CLOCK_TAI),
|
||||
|
||||
/* available for the lazy */
|
||||
CLOCKID_MAP("mono", CLOCK_MONOTONIC),
|
||||
CLOCKID_MAP("raw", CLOCK_MONOTONIC_RAW),
|
||||
CLOCKID_MAP("real", CLOCK_REALTIME),
|
||||
CLOCKID_MAP("boot", CLOCK_BOOTTIME),
|
||||
|
||||
CLOCKID_END,
|
||||
};
|
||||
|
||||
static int get_clockid_res(clockid_t clk_id, u64 *res_ns)
|
||||
{
|
||||
struct timespec res;
|
||||
|
||||
*res_ns = 0;
|
||||
if (!clock_getres(clk_id, &res))
|
||||
*res_ns = res.tv_nsec + res.tv_sec * NSEC_PER_SEC;
|
||||
else
|
||||
pr_warning("WARNING: Failed to determine specified clock resolution.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_clockid(const struct option *opt, const char *str, int unset)
|
||||
{
|
||||
struct record_opts *opts = (struct record_opts *)opt->value;
|
||||
const struct clockid_map *cm;
|
||||
const char *ostr = str;
|
||||
|
||||
if (unset) {
|
||||
opts->use_clockid = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* no arg passed */
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
/* no setting it twice */
|
||||
if (opts->use_clockid)
|
||||
return -1;
|
||||
|
||||
opts->use_clockid = true;
|
||||
|
||||
/* if its a number, we're done */
|
||||
if (sscanf(str, "%d", &opts->clockid) == 1)
|
||||
return get_clockid_res(opts->clockid, &opts->clockid_res_ns);
|
||||
|
||||
/* allow a "CLOCK_" prefix to the name */
|
||||
if (!strncasecmp(str, "CLOCK_", 6))
|
||||
str += 6;
|
||||
|
||||
for (cm = clockids; cm->name; cm++) {
|
||||
if (!strcasecmp(str, cm->name)) {
|
||||
opts->clockid = cm->clockid;
|
||||
return get_clockid_res(opts->clockid,
|
||||
&opts->clockid_res_ns);
|
||||
}
|
||||
}
|
||||
|
||||
opts->use_clockid = false;
|
||||
ui__warning("unknown clockid %s, check man page\n", ostr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *clockid_name(clockid_t clk_id)
|
||||
{
|
||||
const struct clockid_map *cm;
|
||||
|
||||
for (cm = clockids; cm->name; cm++) {
|
||||
if (cm->clockid == clk_id)
|
||||
return cm->name;
|
||||
}
|
||||
return "(not found)";
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __PERF_CLOCKID_H
|
||||
#define __PERF_CLOCKID_H
|
||||
|
||||
struct option;
|
||||
int parse_clockid(const struct option *opt, const char *str, int unset);
|
||||
|
||||
const char *clockid_name(clockid_t clk_id);
|
||||
|
||||
#endif
|
|
@ -31,6 +31,9 @@
|
|||
#include "config.h"
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/time64.h>
|
||||
#include "util.h"
|
||||
#include "clockid.h"
|
||||
|
||||
#define pr_N(n, fmt, ...) \
|
||||
eprintf(n, debug_data_convert, fmt, ##__VA_ARGS__)
|
||||
|
@ -1381,11 +1384,26 @@ do { \
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ctf_writer__setup_clock(struct ctf_writer *cw)
|
||||
static int ctf_writer__setup_clock(struct ctf_writer *cw,
|
||||
struct perf_session *session,
|
||||
bool tod)
|
||||
{
|
||||
struct bt_ctf_clock *clock = cw->clock;
|
||||
const char *desc = "perf clock";
|
||||
int64_t offset = 0;
|
||||
|
||||
bt_ctf_clock_set_description(clock, "perf clock");
|
||||
if (tod) {
|
||||
struct perf_env *env = &session->header.env;
|
||||
|
||||
if (!env->clock.enabled) {
|
||||
pr_err("Can't provide --tod time, missing clock data. "
|
||||
"Please record with -k/--clockid option.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
desc = clockid_name(env->clock.clockid);
|
||||
offset = env->clock.tod_ns - env->clock.clockid_ns;
|
||||
}
|
||||
|
||||
#define SET(__n, __v) \
|
||||
do { \
|
||||
|
@ -1394,8 +1412,8 @@ do { \
|
|||
} while (0)
|
||||
|
||||
SET(frequency, 1000000000);
|
||||
SET(offset_s, 0);
|
||||
SET(offset, 0);
|
||||
SET(offset, offset);
|
||||
SET(description, desc);
|
||||
SET(precision, 10);
|
||||
SET(is_absolute, 0);
|
||||
|
||||
|
@ -1481,7 +1499,8 @@ static void ctf_writer__cleanup(struct ctf_writer *cw)
|
|||
memset(cw, 0, sizeof(*cw));
|
||||
}
|
||||
|
||||
static int ctf_writer__init(struct ctf_writer *cw, const char *path)
|
||||
static int ctf_writer__init(struct ctf_writer *cw, const char *path,
|
||||
struct perf_session *session, bool tod)
|
||||
{
|
||||
struct bt_ctf_writer *writer;
|
||||
struct bt_ctf_stream_class *stream_class;
|
||||
|
@ -1505,7 +1524,7 @@ static int ctf_writer__init(struct ctf_writer *cw, const char *path)
|
|||
|
||||
cw->clock = clock;
|
||||
|
||||
if (ctf_writer__setup_clock(cw)) {
|
||||
if (ctf_writer__setup_clock(cw, session, tod)) {
|
||||
pr("Failed to setup CTF clock.\n");
|
||||
goto err_cleanup;
|
||||
}
|
||||
|
@ -1613,17 +1632,15 @@ int bt_convert__perf2ctf(const char *input, const char *path,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
/* CTF writer */
|
||||
if (ctf_writer__init(cw, path))
|
||||
return -1;
|
||||
|
||||
err = -1;
|
||||
/* perf.data session */
|
||||
session = perf_session__new(&data, 0, &c.tool);
|
||||
if (IS_ERR(session)) {
|
||||
err = PTR_ERR(session);
|
||||
goto free_writer;
|
||||
}
|
||||
if (IS_ERR(session))
|
||||
return PTR_ERR(session);
|
||||
|
||||
/* CTF writer */
|
||||
if (ctf_writer__init(cw, path, session, opts->tod))
|
||||
goto free_session;
|
||||
|
||||
if (c.queue_size) {
|
||||
ordered_events__set_alloc_size(&session->ordered_events,
|
||||
|
@ -1632,17 +1649,17 @@ int bt_convert__perf2ctf(const char *input, const char *path,
|
|||
|
||||
/* CTF writer env/clock setup */
|
||||
if (ctf_writer__setup_env(cw, session))
|
||||
goto free_session;
|
||||
goto free_writer;
|
||||
|
||||
/* CTF events setup */
|
||||
if (setup_events(cw, session))
|
||||
goto free_session;
|
||||
goto free_writer;
|
||||
|
||||
if (opts->all && setup_non_sample_events(cw, session))
|
||||
goto free_session;
|
||||
goto free_writer;
|
||||
|
||||
if (setup_streams(cw, session))
|
||||
goto free_session;
|
||||
goto free_writer;
|
||||
|
||||
err = perf_session__process_events(session);
|
||||
if (!err)
|
||||
|
@ -1670,10 +1687,10 @@ int bt_convert__perf2ctf(const char *input, const char *path,
|
|||
|
||||
return err;
|
||||
|
||||
free_session:
|
||||
perf_session__delete(session);
|
||||
free_writer:
|
||||
ctf_writer__cleanup(cw);
|
||||
free_session:
|
||||
perf_session__delete(session);
|
||||
pr_err("Error during conversion setup.\n");
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
struct perf_data_convert_opts {
|
||||
bool force;
|
||||
bool all;
|
||||
bool tod;
|
||||
};
|
||||
|
||||
#endif /* __DATA_CONVERT_H */
|
||||
|
|
|
@ -208,6 +208,7 @@ int dso__read_binary_type_filename(const struct dso *dso,
|
|||
case DSO_BINARY_TYPE__JAVA_JIT:
|
||||
case DSO_BINARY_TYPE__BPF_PROG_INFO:
|
||||
case DSO_BINARY_TYPE__BPF_IMAGE:
|
||||
case DSO_BINARY_TYPE__OOL:
|
||||
case DSO_BINARY_TYPE__NOT_FOUND:
|
||||
ret = -1;
|
||||
break;
|
||||
|
@ -898,6 +899,8 @@ static struct dso_cache *dso_cache__populate(struct dso *dso,
|
|||
|
||||
if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO)
|
||||
*ret = bpf_read(dso, cache_offset, cache->data);
|
||||
else if (dso->binary_type == DSO_BINARY_TYPE__OOL)
|
||||
*ret = DSO__DATA_CACHE_SIZE;
|
||||
else
|
||||
*ret = file_read(dso, machine, cache_offset, cache->data);
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ enum dso_binary_type {
|
|||
DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
|
||||
DSO_BINARY_TYPE__BPF_PROG_INFO,
|
||||
DSO_BINARY_TYPE__BPF_IMAGE,
|
||||
DSO_BINARY_TYPE__OOL,
|
||||
DSO_BINARY_TYPE__NOT_FOUND,
|
||||
};
|
||||
|
||||
|
|
|
@ -77,7 +77,6 @@ struct perf_env {
|
|||
struct numa_node *numa_nodes;
|
||||
struct memory_node *memory_nodes;
|
||||
unsigned long long memory_bsize;
|
||||
u64 clockid_res_ns;
|
||||
|
||||
/*
|
||||
* bpf_info_lock protects bpf rbtrees. This is needed because the
|
||||
|
@ -100,6 +99,19 @@ struct perf_env {
|
|||
/* For fast cpu to numa node lookup via perf_env__numa_node */
|
||||
int *numa_map;
|
||||
int nr_numa_map;
|
||||
|
||||
/* For real clock time reference. */
|
||||
struct {
|
||||
u64 tod_ns;
|
||||
u64 clockid_ns;
|
||||
u64 clockid_res_ns;
|
||||
int clockid;
|
||||
/*
|
||||
* enabled is valid for report mode, and is true if above
|
||||
* values are set, it's set in process_clock_data
|
||||
*/
|
||||
bool enabled;
|
||||
} clock;
|
||||
};
|
||||
|
||||
enum perf_compress_type {
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "stat.h"
|
||||
#include "session.h"
|
||||
#include "bpf-event.h"
|
||||
#include "print_binary.h"
|
||||
#include "tool.h"
|
||||
#include "../perf.h"
|
||||
|
||||
|
@ -55,6 +56,7 @@ static const char *perf_event__names[] = {
|
|||
[PERF_RECORD_KSYMBOL] = "KSYMBOL",
|
||||
[PERF_RECORD_BPF_EVENT] = "BPF_EVENT",
|
||||
[PERF_RECORD_CGROUP] = "CGROUP",
|
||||
[PERF_RECORD_TEXT_POKE] = "TEXT_POKE",
|
||||
[PERF_RECORD_HEADER_ATTR] = "ATTR",
|
||||
[PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
|
||||
[PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
|
||||
|
@ -267,6 +269,14 @@ int perf_event__process_bpf(struct perf_tool *tool __maybe_unused,
|
|||
return machine__process_bpf(machine, event, sample);
|
||||
}
|
||||
|
||||
int perf_event__process_text_poke(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
return machine__process_text_poke(machine, event, sample);
|
||||
}
|
||||
|
||||
size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
|
||||
{
|
||||
return fprintf(fp, " %d/%d: [%#" PRI_lx64 "(%#" PRI_lx64 ") @ %#" PRI_lx64 "]: %c %s\n",
|
||||
|
@ -413,7 +423,52 @@ size_t perf_event__fprintf_bpf(union perf_event *event, FILE *fp)
|
|||
event->bpf.type, event->bpf.flags, event->bpf.id);
|
||||
}
|
||||
|
||||
size_t perf_event__fprintf(union perf_event *event, FILE *fp)
|
||||
static int text_poke_printer(enum binary_printer_ops op, unsigned int val,
|
||||
void *extra, FILE *fp)
|
||||
{
|
||||
bool old = *(bool *)extra;
|
||||
|
||||
switch ((int)op) {
|
||||
case BINARY_PRINT_LINE_BEGIN:
|
||||
return fprintf(fp, " %s bytes:", old ? "Old" : "New");
|
||||
case BINARY_PRINT_NUM_DATA:
|
||||
return fprintf(fp, " %02x", val);
|
||||
case BINARY_PRINT_LINE_END:
|
||||
return fprintf(fp, "\n");
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t perf_event__fprintf_text_poke(union perf_event *event, struct machine *machine, FILE *fp)
|
||||
{
|
||||
struct perf_record_text_poke_event *tp = &event->text_poke;
|
||||
size_t ret;
|
||||
bool old;
|
||||
|
||||
ret = fprintf(fp, " %" PRI_lx64 " ", tp->addr);
|
||||
if (machine) {
|
||||
struct addr_location al;
|
||||
|
||||
al.map = maps__find(&machine->kmaps, tp->addr);
|
||||
if (al.map && map__load(al.map) >= 0) {
|
||||
al.addr = al.map->map_ip(al.map, tp->addr);
|
||||
al.sym = map__find_symbol(al.map, al.addr);
|
||||
if (al.sym)
|
||||
ret += symbol__fprintf_symname_offs(al.sym, &al, fp);
|
||||
}
|
||||
}
|
||||
ret += fprintf(fp, " old len %u new len %u\n", tp->old_len, tp->new_len);
|
||||
old = true;
|
||||
ret += binary__fprintf(tp->bytes, tp->old_len, 16, text_poke_printer,
|
||||
&old, fp);
|
||||
old = false;
|
||||
ret += binary__fprintf(tp->bytes + tp->old_len, tp->new_len, 16,
|
||||
text_poke_printer, &old, fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t perf_event__fprintf(union perf_event *event, struct machine *machine, FILE *fp)
|
||||
{
|
||||
size_t ret = fprintf(fp, "PERF_RECORD_%s",
|
||||
perf_event__name(event->header.type));
|
||||
|
@ -457,6 +512,9 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)
|
|||
case PERF_RECORD_BPF_EVENT:
|
||||
ret += perf_event__fprintf_bpf(event, fp);
|
||||
break;
|
||||
case PERF_RECORD_TEXT_POKE:
|
||||
ret += perf_event__fprintf_text_poke(event, machine, fp);
|
||||
break;
|
||||
default:
|
||||
ret += fprintf(fp, "\n");
|
||||
}
|
||||
|
|
|
@ -351,6 +351,10 @@ int perf_event__process_bpf(struct perf_tool *tool,
|
|||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
int perf_event__process_text_poke(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
int perf_event__process(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
|
@ -385,7 +389,8 @@ size_t perf_event__fprintf_namespaces(union perf_event *event, FILE *fp);
|
|||
size_t perf_event__fprintf_cgroup(union perf_event *event, FILE *fp);
|
||||
size_t perf_event__fprintf_ksymbol(union perf_event *event, FILE *fp);
|
||||
size_t perf_event__fprintf_bpf(union perf_event *event, FILE *fp);
|
||||
size_t perf_event__fprintf(union perf_event *event, FILE *fp);
|
||||
size_t perf_event__fprintf_text_poke(union perf_event *event, struct machine *machine,FILE *fp);
|
||||
size_t perf_event__fprintf(union perf_event *event, struct machine *machine, FILE *fp);
|
||||
|
||||
int kallsyms__get_function_start(const char *kallsyms_filename,
|
||||
const char *symbol_name, u64 *addr);
|
||||
|
|
|
@ -63,6 +63,9 @@ void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus,
|
|||
perf_evlist__set_maps(&evlist->core, cpus, threads);
|
||||
evlist->workload.pid = -1;
|
||||
evlist->bkw_mmap_state = BKW_MMAP_NOTREADY;
|
||||
evlist->ctl_fd.fd = -1;
|
||||
evlist->ctl_fd.ack = -1;
|
||||
evlist->ctl_fd.pos = -1;
|
||||
}
|
||||
|
||||
struct evlist *evlist__new(void)
|
||||
|
@ -79,7 +82,7 @@ struct evlist *perf_evlist__new_default(void)
|
|||
{
|
||||
struct evlist *evlist = evlist__new();
|
||||
|
||||
if (evlist && perf_evlist__add_default(evlist)) {
|
||||
if (evlist && evlist__add_default(evlist)) {
|
||||
evlist__delete(evlist);
|
||||
evlist = NULL;
|
||||
}
|
||||
|
@ -91,7 +94,7 @@ struct evlist *perf_evlist__new_dummy(void)
|
|||
{
|
||||
struct evlist *evlist = evlist__new();
|
||||
|
||||
if (evlist && perf_evlist__add_dummy(evlist)) {
|
||||
if (evlist && evlist__add_dummy(evlist)) {
|
||||
evlist__delete(evlist);
|
||||
evlist = NULL;
|
||||
}
|
||||
|
@ -231,7 +234,7 @@ void perf_evlist__set_leader(struct evlist *evlist)
|
|||
}
|
||||
}
|
||||
|
||||
int __perf_evlist__add_default(struct evlist *evlist, bool precise)
|
||||
int __evlist__add_default(struct evlist *evlist, bool precise)
|
||||
{
|
||||
struct evsel *evsel = evsel__new_cycles(precise);
|
||||
|
||||
|
@ -242,7 +245,7 @@ int __perf_evlist__add_default(struct evlist *evlist, bool precise)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int perf_evlist__add_dummy(struct evlist *evlist)
|
||||
int evlist__add_dummy(struct evlist *evlist)
|
||||
{
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_SOFTWARE,
|
||||
|
@ -258,8 +261,7 @@ int perf_evlist__add_dummy(struct evlist *evlist)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int evlist__add_attrs(struct evlist *evlist,
|
||||
struct perf_event_attr *attrs, size_t nr_attrs)
|
||||
static int evlist__add_attrs(struct evlist *evlist, struct perf_event_attr *attrs, size_t nr_attrs)
|
||||
{
|
||||
struct evsel *evsel, *n;
|
||||
LIST_HEAD(head);
|
||||
|
@ -282,8 +284,7 @@ out_delete_partial_list:
|
|||
return -1;
|
||||
}
|
||||
|
||||
int __perf_evlist__add_default_attrs(struct evlist *evlist,
|
||||
struct perf_event_attr *attrs, size_t nr_attrs)
|
||||
int __evlist__add_default_attrs(struct evlist *evlist, struct perf_event_attr *attrs, size_t nr_attrs)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
|
@ -322,8 +323,7 @@ perf_evlist__find_tracepoint_by_name(struct evlist *evlist,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int perf_evlist__add_newtp(struct evlist *evlist,
|
||||
const char *sys, const char *name, void *handler)
|
||||
int evlist__add_newtp(struct evlist *evlist, const char *sys, const char *name, void *handler)
|
||||
{
|
||||
struct evsel *evsel = evsel__newtp(sys, name);
|
||||
|
||||
|
@ -500,7 +500,7 @@ int perf_evlist__enable_event_idx(struct evlist *evlist,
|
|||
|
||||
int evlist__add_pollfd(struct evlist *evlist, int fd)
|
||||
{
|
||||
return perf_evlist__add_pollfd(&evlist->core, fd, NULL, POLLIN);
|
||||
return perf_evlist__add_pollfd(&evlist->core, fd, NULL, POLLIN, fdarray_flag__default);
|
||||
}
|
||||
|
||||
int evlist__filter_pollfd(struct evlist *evlist, short revents_and_mask)
|
||||
|
@ -540,7 +540,7 @@ struct evsel *perf_evlist__id2evsel(struct evlist *evlist, u64 id)
|
|||
if (sid)
|
||||
return container_of(sid->evsel, struct evsel, core);
|
||||
|
||||
if (!perf_evlist__sample_id_all(evlist))
|
||||
if (!evlist__sample_id_all(evlist))
|
||||
return evlist__first(evlist);
|
||||
|
||||
return NULL;
|
||||
|
@ -1088,7 +1088,7 @@ int perf_evlist__append_tp_filter_pid(struct evlist *evlist, pid_t pid)
|
|||
return perf_evlist__append_tp_filter_pids(evlist, 1, &pid);
|
||||
}
|
||||
|
||||
bool perf_evlist__valid_sample_type(struct evlist *evlist)
|
||||
bool evlist__valid_sample_type(struct evlist *evlist)
|
||||
{
|
||||
struct evsel *pos;
|
||||
|
||||
|
@ -1107,7 +1107,7 @@ bool perf_evlist__valid_sample_type(struct evlist *evlist)
|
|||
return true;
|
||||
}
|
||||
|
||||
u64 __perf_evlist__combined_sample_type(struct evlist *evlist)
|
||||
u64 __evlist__combined_sample_type(struct evlist *evlist)
|
||||
{
|
||||
struct evsel *evsel;
|
||||
|
||||
|
@ -1120,13 +1120,13 @@ u64 __perf_evlist__combined_sample_type(struct evlist *evlist)
|
|||
return evlist->combined_sample_type;
|
||||
}
|
||||
|
||||
u64 perf_evlist__combined_sample_type(struct evlist *evlist)
|
||||
u64 evlist__combined_sample_type(struct evlist *evlist)
|
||||
{
|
||||
evlist->combined_sample_type = 0;
|
||||
return __perf_evlist__combined_sample_type(evlist);
|
||||
return __evlist__combined_sample_type(evlist);
|
||||
}
|
||||
|
||||
u64 perf_evlist__combined_branch_type(struct evlist *evlist)
|
||||
u64 evlist__combined_branch_type(struct evlist *evlist)
|
||||
{
|
||||
struct evsel *evsel;
|
||||
u64 branch_type = 0;
|
||||
|
@ -1191,7 +1191,7 @@ out:
|
|||
return size;
|
||||
}
|
||||
|
||||
bool perf_evlist__valid_sample_id_all(struct evlist *evlist)
|
||||
bool evlist__valid_sample_id_all(struct evlist *evlist)
|
||||
{
|
||||
struct evsel *first = evlist__first(evlist), *pos = first;
|
||||
|
||||
|
@ -1203,7 +1203,7 @@ bool perf_evlist__valid_sample_id_all(struct evlist *evlist)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool perf_evlist__sample_id_all(struct evlist *evlist)
|
||||
bool evlist__sample_id_all(struct evlist *evlist)
|
||||
{
|
||||
struct evsel *first = evlist__first(evlist);
|
||||
return first->core.attr.sample_id_all;
|
||||
|
@ -1464,8 +1464,7 @@ int perf_evlist__parse_sample_timestamp(struct evlist *evlist,
|
|||
return evsel__parse_sample_timestamp(evsel, event, timestamp);
|
||||
}
|
||||
|
||||
int perf_evlist__strerror_open(struct evlist *evlist,
|
||||
int err, char *buf, size_t size)
|
||||
int evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_t size)
|
||||
{
|
||||
int printed, value;
|
||||
char sbuf[STRERR_BUFSIZE], *emsg = str_error_r(err, sbuf, sizeof(sbuf));
|
||||
|
@ -1518,7 +1517,7 @@ out_default:
|
|||
return 0;
|
||||
}
|
||||
|
||||
int perf_evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size)
|
||||
int evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size)
|
||||
{
|
||||
char sbuf[STRERR_BUFSIZE], *emsg = str_error_r(err, sbuf, sizeof(sbuf));
|
||||
int pages_attempted = evlist->core.mmap_len / 1024, pages_max_per_user, printed = 0;
|
||||
|
@ -1727,3 +1726,143 @@ struct evsel *perf_evlist__reset_weak_group(struct evlist *evsel_list,
|
|||
}
|
||||
return leader;
|
||||
}
|
||||
|
||||
int evlist__initialize_ctlfd(struct evlist *evlist, int fd, int ack)
|
||||
{
|
||||
if (fd == -1) {
|
||||
pr_debug("Control descriptor is not initialized\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
evlist->ctl_fd.pos = perf_evlist__add_pollfd(&evlist->core, fd, NULL, POLLIN,
|
||||
fdarray_flag__nonfilterable);
|
||||
if (evlist->ctl_fd.pos < 0) {
|
||||
evlist->ctl_fd.pos = -1;
|
||||
pr_err("Failed to add ctl fd entry: %m\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
evlist->ctl_fd.fd = fd;
|
||||
evlist->ctl_fd.ack = ack;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool evlist__ctlfd_initialized(struct evlist *evlist)
|
||||
{
|
||||
return evlist->ctl_fd.pos >= 0;
|
||||
}
|
||||
|
||||
int evlist__finalize_ctlfd(struct evlist *evlist)
|
||||
{
|
||||
struct pollfd *entries = evlist->core.pollfd.entries;
|
||||
|
||||
if (!evlist__ctlfd_initialized(evlist))
|
||||
return 0;
|
||||
|
||||
entries[evlist->ctl_fd.pos].fd = -1;
|
||||
entries[evlist->ctl_fd.pos].events = 0;
|
||||
entries[evlist->ctl_fd.pos].revents = 0;
|
||||
|
||||
evlist->ctl_fd.pos = -1;
|
||||
evlist->ctl_fd.ack = -1;
|
||||
evlist->ctl_fd.fd = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int evlist__ctlfd_recv(struct evlist *evlist, enum evlist_ctl_cmd *cmd,
|
||||
char *cmd_data, size_t data_size)
|
||||
{
|
||||
int err;
|
||||
char c;
|
||||
size_t bytes_read = 0;
|
||||
|
||||
memset(cmd_data, 0, data_size);
|
||||
data_size--;
|
||||
|
||||
do {
|
||||
err = read(evlist->ctl_fd.fd, &c, 1);
|
||||
if (err > 0) {
|
||||
if (c == '\n' || c == '\0')
|
||||
break;
|
||||
cmd_data[bytes_read++] = c;
|
||||
if (bytes_read == data_size)
|
||||
break;
|
||||
} else {
|
||||
if (err == -1)
|
||||
pr_err("Failed to read from ctlfd %d: %m\n", evlist->ctl_fd.fd);
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
pr_debug("Message from ctl_fd: \"%s%s\"\n", cmd_data,
|
||||
bytes_read == data_size ? "" : c == '\n' ? "\\n" : "\\0");
|
||||
|
||||
if (err > 0) {
|
||||
if (!strncmp(cmd_data, EVLIST_CTL_CMD_ENABLE_TAG,
|
||||
(sizeof(EVLIST_CTL_CMD_ENABLE_TAG)-1))) {
|
||||
*cmd = EVLIST_CTL_CMD_ENABLE;
|
||||
} else if (!strncmp(cmd_data, EVLIST_CTL_CMD_DISABLE_TAG,
|
||||
(sizeof(EVLIST_CTL_CMD_DISABLE_TAG)-1))) {
|
||||
*cmd = EVLIST_CTL_CMD_DISABLE;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int evlist__ctlfd_ack(struct evlist *evlist)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (evlist->ctl_fd.ack == -1)
|
||||
return 0;
|
||||
|
||||
err = write(evlist->ctl_fd.ack, EVLIST_CTL_CMD_ACK_TAG,
|
||||
sizeof(EVLIST_CTL_CMD_ACK_TAG));
|
||||
if (err == -1)
|
||||
pr_err("failed to write to ctl_ack_fd %d: %m\n", evlist->ctl_fd.ack);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int evlist__ctlfd_process(struct evlist *evlist, enum evlist_ctl_cmd *cmd)
|
||||
{
|
||||
int err = 0;
|
||||
char cmd_data[EVLIST_CTL_CMD_MAX_LEN];
|
||||
int ctlfd_pos = evlist->ctl_fd.pos;
|
||||
struct pollfd *entries = evlist->core.pollfd.entries;
|
||||
|
||||
if (!evlist__ctlfd_initialized(evlist) || !entries[ctlfd_pos].revents)
|
||||
return 0;
|
||||
|
||||
if (entries[ctlfd_pos].revents & POLLIN) {
|
||||
err = evlist__ctlfd_recv(evlist, cmd, cmd_data,
|
||||
EVLIST_CTL_CMD_MAX_LEN);
|
||||
if (err > 0) {
|
||||
switch (*cmd) {
|
||||
case EVLIST_CTL_CMD_ENABLE:
|
||||
evlist__enable(evlist);
|
||||
break;
|
||||
case EVLIST_CTL_CMD_DISABLE:
|
||||
evlist__disable(evlist);
|
||||
break;
|
||||
case EVLIST_CTL_CMD_ACK:
|
||||
case EVLIST_CTL_CMD_UNSUPPORTED:
|
||||
default:
|
||||
pr_debug("ctlfd: unsupported %d\n", *cmd);
|
||||
break;
|
||||
}
|
||||
if (!(*cmd == EVLIST_CTL_CMD_ACK || *cmd == EVLIST_CTL_CMD_UNSUPPORTED))
|
||||
evlist__ctlfd_ack(evlist);
|
||||
}
|
||||
}
|
||||
|
||||
if (entries[ctlfd_pos].revents & (POLLHUP | POLLERR))
|
||||
evlist__finalize_ctlfd(evlist);
|
||||
else
|
||||
entries[ctlfd_pos].revents = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -74,6 +74,11 @@ struct evlist {
|
|||
pthread_t th;
|
||||
volatile int done;
|
||||
} thread;
|
||||
struct {
|
||||
int fd; /* control file descriptor */
|
||||
int ack; /* ack file descriptor for control commands */
|
||||
int pos; /* index at evlist core object to check signals */
|
||||
} ctl_fd;
|
||||
};
|
||||
|
||||
struct evsel_str_handler {
|
||||
|
@ -92,20 +97,20 @@ void evlist__delete(struct evlist *evlist);
|
|||
void evlist__add(struct evlist *evlist, struct evsel *entry);
|
||||
void evlist__remove(struct evlist *evlist, struct evsel *evsel);
|
||||
|
||||
int __perf_evlist__add_default(struct evlist *evlist, bool precise);
|
||||
int __evlist__add_default(struct evlist *evlist, bool precise);
|
||||
|
||||
static inline int perf_evlist__add_default(struct evlist *evlist)
|
||||
static inline int evlist__add_default(struct evlist *evlist)
|
||||
{
|
||||
return __perf_evlist__add_default(evlist, true);
|
||||
return __evlist__add_default(evlist, true);
|
||||
}
|
||||
|
||||
int __perf_evlist__add_default_attrs(struct evlist *evlist,
|
||||
int __evlist__add_default_attrs(struct evlist *evlist,
|
||||
struct perf_event_attr *attrs, size_t nr_attrs);
|
||||
|
||||
#define perf_evlist__add_default_attrs(evlist, array) \
|
||||
__perf_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array))
|
||||
#define evlist__add_default_attrs(evlist, array) \
|
||||
__evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array))
|
||||
|
||||
int perf_evlist__add_dummy(struct evlist *evlist);
|
||||
int evlist__add_dummy(struct evlist *evlist);
|
||||
|
||||
int perf_evlist__add_sb_event(struct evlist *evlist,
|
||||
struct perf_event_attr *attr,
|
||||
|
@ -116,8 +121,7 @@ int perf_evlist__start_sb_thread(struct evlist *evlist,
|
|||
struct target *target);
|
||||
void perf_evlist__stop_sb_thread(struct evlist *evlist);
|
||||
|
||||
int perf_evlist__add_newtp(struct evlist *evlist,
|
||||
const char *sys, const char *name, void *handler);
|
||||
int evlist__add_newtp(struct evlist *evlist, const char *sys, const char *name, void *handler);
|
||||
|
||||
int __evlist__set_tracepoints_handlers(struct evlist *evlist,
|
||||
const struct evsel_str_handler *assocs,
|
||||
|
@ -219,10 +223,10 @@ int perf_evlist__apply_filters(struct evlist *evlist, struct evsel **err_evsel);
|
|||
void __perf_evlist__set_leader(struct list_head *list);
|
||||
void perf_evlist__set_leader(struct evlist *evlist);
|
||||
|
||||
u64 __perf_evlist__combined_sample_type(struct evlist *evlist);
|
||||
u64 perf_evlist__combined_sample_type(struct evlist *evlist);
|
||||
u64 perf_evlist__combined_branch_type(struct evlist *evlist);
|
||||
bool perf_evlist__sample_id_all(struct evlist *evlist);
|
||||
u64 __evlist__combined_sample_type(struct evlist *evlist);
|
||||
u64 evlist__combined_sample_type(struct evlist *evlist);
|
||||
u64 evlist__combined_branch_type(struct evlist *evlist);
|
||||
bool evlist__sample_id_all(struct evlist *evlist);
|
||||
u16 perf_evlist__id_hdr_size(struct evlist *evlist);
|
||||
|
||||
int perf_evlist__parse_sample(struct evlist *evlist, union perf_event *event,
|
||||
|
@ -232,8 +236,8 @@ int perf_evlist__parse_sample_timestamp(struct evlist *evlist,
|
|||
union perf_event *event,
|
||||
u64 *timestamp);
|
||||
|
||||
bool perf_evlist__valid_sample_type(struct evlist *evlist);
|
||||
bool perf_evlist__valid_sample_id_all(struct evlist *evlist);
|
||||
bool evlist__valid_sample_type(struct evlist *evlist);
|
||||
bool evlist__valid_sample_id_all(struct evlist *evlist);
|
||||
bool perf_evlist__valid_read_format(struct evlist *evlist);
|
||||
|
||||
void perf_evlist__splice_list_tail(struct evlist *evlist,
|
||||
|
@ -258,8 +262,8 @@ static inline struct evsel *evlist__last(struct evlist *evlist)
|
|||
return container_of(evsel, struct evsel, core);
|
||||
}
|
||||
|
||||
int perf_evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_t size);
|
||||
int perf_evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size);
|
||||
int evlist__strerror_open(struct evlist *evlist, int err, char *buf, size_t size);
|
||||
int evlist__strerror_mmap(struct evlist *evlist, int err, char *buf, size_t size);
|
||||
|
||||
bool perf_evlist__can_select_event(struct evlist *evlist, const char *str);
|
||||
void perf_evlist__to_front(struct evlist *evlist,
|
||||
|
@ -356,4 +360,25 @@ void perf_evlist__force_leader(struct evlist *evlist);
|
|||
struct evsel *perf_evlist__reset_weak_group(struct evlist *evlist,
|
||||
struct evsel *evsel,
|
||||
bool close);
|
||||
#define EVLIST_CTL_CMD_ENABLE_TAG "enable"
|
||||
#define EVLIST_CTL_CMD_DISABLE_TAG "disable"
|
||||
#define EVLIST_CTL_CMD_ACK_TAG "ack\n"
|
||||
|
||||
#define EVLIST_CTL_CMD_MAX_LEN 64
|
||||
|
||||
enum evlist_ctl_cmd {
|
||||
EVLIST_CTL_CMD_UNSUPPORTED = 0,
|
||||
EVLIST_CTL_CMD_ENABLE,
|
||||
EVLIST_CTL_CMD_DISABLE,
|
||||
EVLIST_CTL_CMD_ACK
|
||||
};
|
||||
|
||||
int evlist__initialize_ctlfd(struct evlist *evlist, int ctl_fd, int ctl_fd_ack);
|
||||
int evlist__finalize_ctlfd(struct evlist *evlist);
|
||||
bool evlist__ctlfd_initialized(struct evlist *evlist);
|
||||
int evlist__ctlfd_process(struct evlist *evlist, enum evlist_ctl_cmd *cmd);
|
||||
|
||||
#define EVLIST_ENABLED_MSG "Events enabled\n"
|
||||
#define EVLIST_DISABLED_MSG "Events disabled\n"
|
||||
|
||||
#endif /* __PERF_EVLIST_H */
|
||||
|
|
|
@ -1014,12 +1014,14 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts,
|
|||
if (callchain && callchain->enabled && !evsel->no_aux_samples)
|
||||
evsel__config_callchain(evsel, opts, callchain);
|
||||
|
||||
if (opts->sample_intr_regs && !evsel->no_aux_samples) {
|
||||
if (opts->sample_intr_regs && !evsel->no_aux_samples &&
|
||||
!evsel__is_dummy_event(evsel)) {
|
||||
attr->sample_regs_intr = opts->sample_intr_regs;
|
||||
evsel__set_sample_bit(evsel, REGS_INTR);
|
||||
}
|
||||
|
||||
if (opts->sample_user_regs && !evsel->no_aux_samples) {
|
||||
if (opts->sample_user_regs && !evsel->no_aux_samples &&
|
||||
!evsel__is_dummy_event(evsel)) {
|
||||
attr->sample_regs_user |= opts->sample_user_regs;
|
||||
evsel__set_sample_bit(evsel, REGS_USER);
|
||||
}
|
||||
|
@ -1064,7 +1066,12 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts,
|
|||
attr->mmap = track;
|
||||
attr->mmap2 = track && !perf_missing_features.mmap2;
|
||||
attr->comm = track;
|
||||
attr->ksymbol = track && !perf_missing_features.ksymbol;
|
||||
/*
|
||||
* ksymbol is tracked separately with text poke because it needs to be
|
||||
* system wide and enabled immediately.
|
||||
*/
|
||||
if (!opts->text_poke)
|
||||
attr->ksymbol = track && !perf_missing_features.ksymbol;
|
||||
attr->bpf_event = track && !opts->no_bpf_event && !perf_missing_features.bpf;
|
||||
|
||||
if (opts->record_namespaces)
|
||||
|
@ -2495,8 +2502,10 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target,
|
|||
|
||||
return scnprintf(msg + printed, size - printed,
|
||||
"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"
|
||||
"access to performance monitoring and observability operations for processes\n"
|
||||
"without CAP_PERFMON, CAP_SYS_PTRACE or CAP_SYS_ADMIN Linux capability.\n"
|
||||
"More information can be found at 'Perf events and tool security' document:\n"
|
||||
"https://www.kernel.org/doc/html/latest/admin-guide/perf-security.html\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"
|
||||
|
@ -2528,6 +2537,10 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target,
|
|||
"No such device - did you specify an out-of-range profile CPU?");
|
||||
break;
|
||||
case EOPNOTSUPP:
|
||||
if (evsel->core.attr.aux_output)
|
||||
return scnprintf(msg, size,
|
||||
"%s: PMU Hardware doesn't support 'aux_output' feature",
|
||||
evsel__name(evsel));
|
||||
if (evsel->core.attr.sample_period != 0)
|
||||
return scnprintf(msg, size,
|
||||
"%s: PMU Hardware doesn't support sampling/overflow-interrupts. Try 'perf stat'",
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "metricgroup.h"
|
||||
#include "debug.h"
|
||||
#include "expr.h"
|
||||
#include "expr-bison.h"
|
||||
#include "expr-flex.h"
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/zalloc.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef PARSER_DEBUG
|
||||
extern int expr_debug;
|
||||
|
@ -30,35 +37,144 @@ static bool key_equal(const void *key1, const void *key2,
|
|||
}
|
||||
|
||||
/* Caller must make sure id is allocated */
|
||||
int expr__add_id(struct expr_parse_ctx *ctx, const char *name, double val)
|
||||
int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
|
||||
{
|
||||
double *val_ptr = NULL, *old_val = NULL;
|
||||
struct expr_id_data *data_ptr = NULL, *old_data = NULL;
|
||||
char *old_key = NULL;
|
||||
int ret;
|
||||
|
||||
if (val != 0.0) {
|
||||
val_ptr = malloc(sizeof(double));
|
||||
if (!val_ptr)
|
||||
return -ENOMEM;
|
||||
*val_ptr = val;
|
||||
}
|
||||
ret = hashmap__set(&ctx->ids, name, val_ptr,
|
||||
(const void **)&old_key, (void **)&old_val);
|
||||
data_ptr = malloc(sizeof(*data_ptr));
|
||||
if (!data_ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
data_ptr->parent = ctx->parent;
|
||||
|
||||
ret = hashmap__set(&ctx->ids, id, data_ptr,
|
||||
(const void **)&old_key, (void **)&old_data);
|
||||
if (ret)
|
||||
free(data_ptr);
|
||||
free(old_key);
|
||||
free(old_val);
|
||||
free(old_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int expr__get_id(struct expr_parse_ctx *ctx, const char *id, double *val_ptr)
|
||||
/* Caller must make sure id is allocated */
|
||||
int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val)
|
||||
{
|
||||
double *data;
|
||||
struct expr_id_data *data_ptr = NULL, *old_data = NULL;
|
||||
char *old_key = NULL;
|
||||
int ret;
|
||||
|
||||
if (!hashmap__find(&ctx->ids, id, (void **)&data))
|
||||
data_ptr = malloc(sizeof(*data_ptr));
|
||||
if (!data_ptr)
|
||||
return -ENOMEM;
|
||||
data_ptr->val = val;
|
||||
data_ptr->is_ref = false;
|
||||
|
||||
ret = hashmap__set(&ctx->ids, id, data_ptr,
|
||||
(const void **)&old_key, (void **)&old_data);
|
||||
if (ret)
|
||||
free(data_ptr);
|
||||
free(old_key);
|
||||
free(old_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref)
|
||||
{
|
||||
struct expr_id_data *data_ptr = NULL, *old_data = NULL;
|
||||
char *old_key = NULL;
|
||||
char *name, *p;
|
||||
int ret;
|
||||
|
||||
data_ptr = zalloc(sizeof(*data_ptr));
|
||||
if (!data_ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
name = strdup(ref->metric_name);
|
||||
if (!name) {
|
||||
free(data_ptr);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* The jevents tool converts all metric expressions
|
||||
* to lowercase, including metric references, hence
|
||||
* we need to add lowercase name for metric, so it's
|
||||
* properly found.
|
||||
*/
|
||||
for (p = name; *p; p++)
|
||||
*p = tolower(*p);
|
||||
|
||||
/*
|
||||
* Intentionally passing just const char pointers,
|
||||
* originally from 'struct pmu_event' object.
|
||||
* We don't need to change them, so there's no
|
||||
* need to create our own copy.
|
||||
*/
|
||||
data_ptr->ref.metric_name = ref->metric_name;
|
||||
data_ptr->ref.metric_expr = ref->metric_expr;
|
||||
data_ptr->ref.counted = false;
|
||||
data_ptr->is_ref = true;
|
||||
|
||||
ret = hashmap__set(&ctx->ids, name, data_ptr,
|
||||
(const void **)&old_key, (void **)&old_data);
|
||||
if (ret)
|
||||
free(data_ptr);
|
||||
|
||||
pr_debug2("adding ref metric %s: %s\n",
|
||||
ref->metric_name, ref->metric_expr);
|
||||
|
||||
free(old_key);
|
||||
free(old_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int expr__get_id(struct expr_parse_ctx *ctx, const char *id,
|
||||
struct expr_id_data **data)
|
||||
{
|
||||
return hashmap__find(&ctx->ids, id, (void **)data) ? 0 : -1;
|
||||
}
|
||||
|
||||
int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id,
|
||||
struct expr_id_data **datap)
|
||||
{
|
||||
struct expr_id_data *data;
|
||||
|
||||
if (expr__get_id(ctx, id, datap) || !*datap) {
|
||||
pr_debug("%s not found\n", id);
|
||||
return -1;
|
||||
*val_ptr = (data == NULL) ? 0.0 : *data;
|
||||
}
|
||||
|
||||
data = *datap;
|
||||
|
||||
pr_debug2("lookup: is_ref %d, counted %d, val %f: %s\n",
|
||||
data->is_ref, data->ref.counted, data->val, id);
|
||||
|
||||
if (data->is_ref && !data->ref.counted) {
|
||||
data->ref.counted = true;
|
||||
pr_debug("processing metric: %s ENTRY\n", id);
|
||||
if (expr__parse(&data->val, ctx, data->ref.metric_expr, 1)) {
|
||||
pr_debug("%s failed to count\n", id);
|
||||
return -1;
|
||||
}
|
||||
pr_debug("processing metric: %s EXIT: %f\n", id, data->val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void expr__del_id(struct expr_parse_ctx *ctx, const char *id)
|
||||
{
|
||||
struct expr_id_data *old_val = NULL;
|
||||
char *old_key = NULL;
|
||||
|
||||
hashmap__delete(&ctx->ids, id,
|
||||
(const void **)&old_key, (void **)&old_val);
|
||||
free(old_key);
|
||||
free(old_val);
|
||||
}
|
||||
|
||||
void expr__ctx_init(struct expr_parse_ctx *ctx)
|
||||
{
|
||||
hashmap__init(&ctx->ids, key_hash, key_equal, NULL);
|
||||
|
@ -88,6 +204,8 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
|
|||
void *scanner;
|
||||
int ret;
|
||||
|
||||
pr_debug2("parsing metric: %s\n", expr);
|
||||
|
||||
ret = expr_lex_init_extra(&scanner_ctx, &scanner);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -116,16 +234,10 @@ int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
|
|||
int expr__find_other(const char *expr, const char *one,
|
||||
struct expr_parse_ctx *ctx, int runtime)
|
||||
{
|
||||
double *old_val = NULL;
|
||||
char *old_key = NULL;
|
||||
int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHER, runtime);
|
||||
|
||||
if (one) {
|
||||
hashmap__delete(&ctx->ids, one,
|
||||
(const void **)&old_key, (void **)&old_val);
|
||||
free(old_key);
|
||||
free(old_val);
|
||||
}
|
||||
if (one)
|
||||
expr__del_id(ctx, one);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,30 @@
|
|||
#include "util/hashmap.h"
|
||||
//#endif
|
||||
|
||||
struct metric_ref;
|
||||
|
||||
struct expr_id {
|
||||
char *id;
|
||||
struct expr_id *parent;
|
||||
};
|
||||
|
||||
struct expr_parse_ctx {
|
||||
struct hashmap ids;
|
||||
struct hashmap ids;
|
||||
struct expr_id *parent;
|
||||
};
|
||||
|
||||
struct expr_id_data {
|
||||
union {
|
||||
double val;
|
||||
struct {
|
||||
const char *metric_name;
|
||||
const char *metric_expr;
|
||||
bool counted;
|
||||
} ref;
|
||||
struct expr_id *parent;
|
||||
};
|
||||
|
||||
bool is_ref;
|
||||
};
|
||||
|
||||
struct expr_scanner_ctx {
|
||||
|
@ -22,8 +44,14 @@ struct expr_scanner_ctx {
|
|||
|
||||
void expr__ctx_init(struct expr_parse_ctx *ctx);
|
||||
void expr__ctx_clear(struct expr_parse_ctx *ctx);
|
||||
int expr__add_id(struct expr_parse_ctx *ctx, const char *id, double val);
|
||||
int expr__get_id(struct expr_parse_ctx *ctx, const char *id, double *val_ptr);
|
||||
void expr__del_id(struct expr_parse_ctx *ctx, const char *id);
|
||||
int expr__add_id(struct expr_parse_ctx *ctx, const char *id);
|
||||
int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val);
|
||||
int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref);
|
||||
int expr__get_id(struct expr_parse_ctx *ctx, const char *id,
|
||||
struct expr_id_data **data);
|
||||
int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id,
|
||||
struct expr_id_data **datap);
|
||||
int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
|
||||
const char *expr, int runtime);
|
||||
int expr__find_other(const char *expr, const char *one,
|
||||
|
|
|
@ -100,6 +100,7 @@ symbol ({spec}|{sym})+
|
|||
}
|
||||
}
|
||||
|
||||
d_ratio { return D_RATIO; }
|
||||
max { return MAX; }
|
||||
min { return MIN; }
|
||||
if { return IF; }
|
||||
|
@ -110,6 +111,8 @@ else { return ELSE; }
|
|||
"|" { return '|'; }
|
||||
"^" { return '^'; }
|
||||
"&" { return '&'; }
|
||||
"<" { return '<'; }
|
||||
">" { return '>'; }
|
||||
"-" { return '-'; }
|
||||
"+" { return '+'; }
|
||||
"*" { return '*'; }
|
||||
|
|
|
@ -10,6 +10,14 @@
|
|||
#include "smt.h"
|
||||
#include <string.h>
|
||||
|
||||
static double d_ratio(double val0, double val1)
|
||||
{
|
||||
if (val1 == 0) {
|
||||
return 0;
|
||||
}
|
||||
return val0 / val1;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%define api.pure full
|
||||
|
@ -28,11 +36,12 @@
|
|||
%token <num> NUMBER
|
||||
%token <str> ID
|
||||
%destructor { free ($$); } <str>
|
||||
%token MIN MAX IF ELSE SMT_ON
|
||||
%token MIN MAX IF ELSE SMT_ON D_RATIO
|
||||
%left MIN MAX IF
|
||||
%left '|'
|
||||
%left '^'
|
||||
%left '&'
|
||||
%left '<' '>'
|
||||
%left '-' '+'
|
||||
%left '*' '/' '%'
|
||||
%left NEG NOT
|
||||
|
@ -60,11 +69,12 @@ all_other: all_other other
|
|||
|
||||
other: ID
|
||||
{
|
||||
expr__add_id(ctx, $1, 0.0);
|
||||
expr__add_id(ctx, $1);
|
||||
}
|
||||
|
|
||||
MIN | MAX | IF | ELSE | SMT_ON | NUMBER | '|' | '^' | '&' | '-' | '+' | '*' | '/' | '%' | '(' | ')' | ','
|
||||
|
||||
|
|
||||
'<' | '>' | D_RATIO
|
||||
|
||||
all_expr: if_expr { *final_val = $1; }
|
||||
;
|
||||
|
@ -75,16 +85,22 @@ if_expr:
|
|||
;
|
||||
|
||||
expr: NUMBER
|
||||
| ID { if (expr__get_id(ctx, $1, &$$)) {
|
||||
pr_debug("%s not found\n", $1);
|
||||
| ID {
|
||||
struct expr_id_data *data;
|
||||
|
||||
if (expr__resolve_id(ctx, $1, &data)) {
|
||||
free($1);
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
$$ = data->val;
|
||||
free($1);
|
||||
YYABORT;
|
||||
}
|
||||
free($1);
|
||||
}
|
||||
| expr '|' expr { $$ = (long)$1 | (long)$3; }
|
||||
| expr '&' expr { $$ = (long)$1 & (long)$3; }
|
||||
| expr '^' expr { $$ = (long)$1 ^ (long)$3; }
|
||||
| expr '<' expr { $$ = $1 < $3; }
|
||||
| expr '>' expr { $$ = $1 > $3; }
|
||||
| expr '+' expr { $$ = $1 + $3; }
|
||||
| expr '-' expr { $$ = $1 - $3; }
|
||||
| expr '*' expr { $$ = $1 * $3; }
|
||||
|
@ -105,6 +121,7 @@ expr: NUMBER
|
|||
| MIN '(' expr ',' expr ')' { $$ = $3 < $5 ? $3 : $5; }
|
||||
| MAX '(' expr ',' expr ')' { $$ = $3 > $5 ? $3 : $5; }
|
||||
| SMT_ON { $$ = smt_on() > 0; }
|
||||
| D_RATIO '(' expr ',' expr ')' { $$ = d_ratio($3,$5); }
|
||||
;
|
||||
|
||||
%%
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "util/util.h" // perf_exe()
|
||||
#include "cputopo.h"
|
||||
#include "bpf-event.h"
|
||||
#include "clockid.h"
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <internal/lib.h>
|
||||
|
@ -891,8 +892,42 @@ static int write_auxtrace(struct feat_fd *ff,
|
|||
static int write_clockid(struct feat_fd *ff,
|
||||
struct evlist *evlist __maybe_unused)
|
||||
{
|
||||
return do_write(ff, &ff->ph->env.clockid_res_ns,
|
||||
sizeof(ff->ph->env.clockid_res_ns));
|
||||
return do_write(ff, &ff->ph->env.clock.clockid_res_ns,
|
||||
sizeof(ff->ph->env.clock.clockid_res_ns));
|
||||
}
|
||||
|
||||
static int write_clock_data(struct feat_fd *ff,
|
||||
struct evlist *evlist __maybe_unused)
|
||||
{
|
||||
u64 *data64;
|
||||
u32 data32;
|
||||
int ret;
|
||||
|
||||
/* version */
|
||||
data32 = 1;
|
||||
|
||||
ret = do_write(ff, &data32, sizeof(data32));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* clockid */
|
||||
data32 = ff->ph->env.clock.clockid;
|
||||
|
||||
ret = do_write(ff, &data32, sizeof(data32));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* TOD ref time */
|
||||
data64 = &ff->ph->env.clock.tod_ns;
|
||||
|
||||
ret = do_write(ff, data64, sizeof(*data64));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* clockid ref time */
|
||||
data64 = &ff->ph->env.clock.clockid_ns;
|
||||
|
||||
return do_write(ff, data64, sizeof(*data64));
|
||||
}
|
||||
|
||||
static int write_dir_format(struct feat_fd *ff,
|
||||
|
@ -1546,7 +1581,50 @@ static void print_cpu_topology(struct feat_fd *ff, FILE *fp)
|
|||
static void print_clockid(struct feat_fd *ff, FILE *fp)
|
||||
{
|
||||
fprintf(fp, "# clockid frequency: %"PRIu64" MHz\n",
|
||||
ff->ph->env.clockid_res_ns * 1000);
|
||||
ff->ph->env.clock.clockid_res_ns * 1000);
|
||||
}
|
||||
|
||||
static void print_clock_data(struct feat_fd *ff, FILE *fp)
|
||||
{
|
||||
struct timespec clockid_ns;
|
||||
char tstr[64], date[64];
|
||||
struct timeval tod_ns;
|
||||
clockid_t clockid;
|
||||
struct tm ltime;
|
||||
u64 ref;
|
||||
|
||||
if (!ff->ph->env.clock.enabled) {
|
||||
fprintf(fp, "# reference time disabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compute TOD time. */
|
||||
ref = ff->ph->env.clock.tod_ns;
|
||||
tod_ns.tv_sec = ref / NSEC_PER_SEC;
|
||||
ref -= tod_ns.tv_sec * NSEC_PER_SEC;
|
||||
tod_ns.tv_usec = ref / NSEC_PER_USEC;
|
||||
|
||||
/* Compute clockid time. */
|
||||
ref = ff->ph->env.clock.clockid_ns;
|
||||
clockid_ns.tv_sec = ref / NSEC_PER_SEC;
|
||||
ref -= clockid_ns.tv_sec * NSEC_PER_SEC;
|
||||
clockid_ns.tv_nsec = ref;
|
||||
|
||||
clockid = ff->ph->env.clock.clockid;
|
||||
|
||||
if (localtime_r(&tod_ns.tv_sec, <ime) == NULL)
|
||||
snprintf(tstr, sizeof(tstr), "<error>");
|
||||
else {
|
||||
strftime(date, sizeof(date), "%F %T", <ime);
|
||||
scnprintf(tstr, sizeof(tstr), "%s.%06d",
|
||||
date, (int) tod_ns.tv_usec);
|
||||
}
|
||||
|
||||
fprintf(fp, "# clockid: %s (%u)\n", clockid_name(clockid), clockid);
|
||||
fprintf(fp, "# reference time: %s = %ld.%06d (TOD) = %ld.%09ld (%s)\n",
|
||||
tstr, tod_ns.tv_sec, (int) tod_ns.tv_usec,
|
||||
clockid_ns.tv_sec, clockid_ns.tv_nsec,
|
||||
clockid_name(clockid));
|
||||
}
|
||||
|
||||
static void print_dir_format(struct feat_fd *ff, FILE *fp)
|
||||
|
@ -2732,12 +2810,46 @@ out:
|
|||
static int process_clockid(struct feat_fd *ff,
|
||||
void *data __maybe_unused)
|
||||
{
|
||||
if (do_read_u64(ff, &ff->ph->env.clockid_res_ns))
|
||||
if (do_read_u64(ff, &ff->ph->env.clock.clockid_res_ns))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_clock_data(struct feat_fd *ff,
|
||||
void *_data __maybe_unused)
|
||||
{
|
||||
u32 data32;
|
||||
u64 data64;
|
||||
|
||||
/* version */
|
||||
if (do_read_u32(ff, &data32))
|
||||
return -1;
|
||||
|
||||
if (data32 != 1)
|
||||
return -1;
|
||||
|
||||
/* clockid */
|
||||
if (do_read_u32(ff, &data32))
|
||||
return -1;
|
||||
|
||||
ff->ph->env.clock.clockid = data32;
|
||||
|
||||
/* TOD ref time */
|
||||
if (do_read_u64(ff, &data64))
|
||||
return -1;
|
||||
|
||||
ff->ph->env.clock.tod_ns = data64;
|
||||
|
||||
/* clockid ref time */
|
||||
if (do_read_u64(ff, &data64))
|
||||
return -1;
|
||||
|
||||
ff->ph->env.clock.clockid_ns = data64;
|
||||
ff->ph->env.clock.enabled = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_dir_format(struct feat_fd *ff,
|
||||
void *_data __maybe_unused)
|
||||
{
|
||||
|
@ -3008,6 +3120,7 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = {
|
|||
FEAT_OPR(BPF_BTF, bpf_btf, false),
|
||||
FEAT_OPR(COMPRESSED, compressed, false),
|
||||
FEAT_OPR(CPU_PMU_CAPS, cpu_pmu_caps, false),
|
||||
FEAT_OPR(CLOCK_DATA, clock_data, false),
|
||||
};
|
||||
|
||||
struct header_print_data {
|
||||
|
|
|
@ -44,6 +44,7 @@ enum {
|
|||
HEADER_BPF_BTF,
|
||||
HEADER_COMPRESSED,
|
||||
HEADER_CPU_PMU_CAPS,
|
||||
HEADER_CLOCK_DATA,
|
||||
HEADER_LAST_FEATURE,
|
||||
HEADER_FEAT_BITS = 256,
|
||||
};
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче