From 4b3a56d9652338fa103c9996b129457268961a7e Mon Sep 17 00:00:00 2001 From: "Raziel K. Crowe" <84860158+CWDSYSTEMS@users.noreply.github.com> Date: Sat, 2 Apr 2022 17:07:24 +0500 Subject: [PATCH] update and merge --- tools/Makefile | 27 +- tools/arch/arm64/include/uapi/asm/kvm.h | 5 + .../arch/powerpc/include/uapi/asm/perf_regs.h | 28 +- tools/arch/x86/include/asm/cpufeatures.h | 7 +- tools/arch/x86/include/asm/msr-index.h | 20 + .../arch/x86/include/asm/required-features.h | 4 - tools/arch/x86/include/uapi/asm/kvm.h | 23 +- tools/arch/x86/include/uapi/asm/prctl.h | 24 +- tools/arch/x86/lib/memcpy_64.S | 12 +- tools/perf/.gitignore | 1 + tools/perf/MANIFEST | 4 + tools/perf/Makefile.config | 31 +- tools/perf/Makefile.perf | 62 +- tools/perf/arch/arm/include/arch-tests.h | 2 +- tools/perf/arch/arm/include/perf_regs.h | 42 - tools/perf/arch/arm/tests/arch-tests.c | 16 +- tools/perf/arch/arm/tests/vectors-page.c | 5 +- tools/perf/arch/arm/util/cs-etm.c | 54 +- tools/perf/arch/arm64/include/arch-tests.h | 2 +- tools/perf/arch/arm64/include/perf_regs.h | 78 +- tools/perf/arch/arm64/tests/arch-tests.c | 11 +- tools/perf/arch/arm64/util/arm-spe.c | 283 +++- tools/perf/arch/arm64/util/machine.c | 7 + tools/perf/arch/arm64/util/pmu.c | 4 +- tools/perf/arch/csky/include/perf_regs.h | 82 - .../arch/mips/entry/syscalls/syscall_n64.tbl | 2 + tools/perf/arch/mips/include/perf_regs.h | 69 - .../arch/powerpc/entry/syscalls/syscall.tbl | 2 + tools/perf/arch/powerpc/include/arch-tests.h | 2 +- tools/perf/arch/powerpc/include/perf_regs.h | 64 - tools/perf/arch/powerpc/tests/arch-tests.c | 12 +- tools/perf/arch/powerpc/util/event.c | 8 +- tools/perf/arch/powerpc/util/header.c | 2 +- tools/perf/arch/powerpc/util/kvm-stat.c | 5 +- tools/perf/arch/powerpc/util/perf_regs.c | 2 + tools/perf/arch/riscv/include/perf_regs.h | 74 - .../perf/arch/s390/entry/syscalls/syscall.tbl | 2 + tools/perf/arch/s390/include/perf_regs.h | 78 - tools/perf/arch/x86/annotate/instructions.c | 28 +- .../arch/x86/entry/syscalls/syscall_64.tbl | 2 + tools/perf/arch/x86/include/arch-tests.h | 14 +- tools/perf/arch/x86/include/perf_regs.h | 82 - tools/perf/arch/x86/tests/arch-tests.c | 49 +- tools/perf/arch/x86/tests/bp-modify.c | 2 +- tools/perf/arch/x86/tests/insn-x86.c | 2 +- tools/perf/arch/x86/tests/intel-cqm.c | 2 +- .../x86/tests/intel-pt-pkt-decoder-test.c | 2 +- tools/perf/arch/x86/tests/rdpmc.c | 2 +- tools/perf/arch/x86/tests/sample-parsing.c | 2 +- tools/perf/arch/x86/util/evlist.c | 17 + tools/perf/arch/x86/util/evsel.c | 23 + tools/perf/builtin-annotate.c | 4 + tools/perf/builtin-bench.c | 5 +- tools/perf/builtin-buildid-cache.c | 25 +- tools/perf/builtin-c2c.c | 19 +- tools/perf/builtin-daemon.c | 15 +- tools/perf/builtin-ftrace.c | 450 ++++- tools/perf/builtin-inject.c | 16 +- tools/perf/builtin-kmem.c | 2 +- tools/perf/builtin-kvm.c | 2 +- tools/perf/builtin-list.c | 42 +- tools/perf/builtin-probe.c | 5 + tools/perf/builtin-record.c | 75 +- tools/perf/builtin-report.c | 17 +- tools/perf/builtin-sched.c | 75 +- tools/perf/builtin-script.c | 62 +- tools/perf/builtin-stat.c | 615 +++---- tools/perf/builtin-top.c | 6 +- tools/perf/builtin-trace.c | 68 +- tools/perf/check-headers.sh | 2 + tools/perf/design.txt | 3 + tools/perf/util/affinity.c | 10 +- tools/perf/util/auxtrace.h | 11 +- tools/perf/util/bpf-event.c | 68 +- tools/perf/util/bpf-loader.c | 18 +- tools/perf/util/bpf_counter.h | 4 +- tools/perf/util/bpf_counter_cgroup.c | 20 +- tools/perf/util/callchain.h | 4 +- tools/perf/util/counts.h | 14 +- tools/perf/util/cpumap.c | 257 ++- tools/perf/util/cputopo.c | 89 +- tools/perf/util/cputopo.h | 33 +- tools/perf/util/data-convert-bt.c | 4 +- tools/perf/util/dso.c | 1 + tools/perf/util/dso.h | 2 +- tools/perf/util/env.h | 3 +- tools/perf/util/evlist.c | 184 +- tools/perf/util/evsel.h | 54 +- tools/perf/util/expr.c | 241 ++- tools/perf/util/expr.l | 31 +- tools/perf/util/expr.y | 334 +++- tools/perf/util/genelf.h | 2 +- tools/perf/util/intel-bts.c | 2 +- tools/perf/util/machine.c | 65 +- tools/perf/util/machine.h | 3 + tools/perf/util/map_symbol.h | 1 + tools/perf/util/mem-events.c | 45 +- tools/perf/util/metricgroup.c | 1506 ++++++++++------- tools/perf/util/metricgroup.h | 37 +- tools/perf/util/mmap.h | 6 +- tools/perf/util/parse-events-hybrid.c | 43 +- tools/perf/util/parse-events.c | 482 ++++-- tools/perf/util/parse-events.h | 28 +- tools/perf/util/parse-events.l | 21 +- tools/perf/util/perf_event_attr_fprintf.c | 2 +- tools/perf/util/pfm.c | 3 +- tools/perf/util/pmu.h | 16 +- tools/perf/util/python-ext-sources | 1 + tools/perf/util/python.c | 26 +- tools/perf/util/record.h | 1 + tools/perf/util/s390-cpumsf.c | 8 +- tools/perf/util/s390-sample-raw.c | 6 +- tools/perf/util/sort.h | 3 +- tools/perf/util/srcline.c | 354 ++-- tools/perf/util/stat.h | 9 +- tools/perf/util/symbol.h | 21 +- tools/perf/util/top.c | 6 +- tools/perf/util/util.c | 15 + tools/perf/util/util.h | 11 +- tools/vm/page-types.c | 38 +- tools/vm/page_owner_sort.c | 96 +- 121 files changed, 4401 insertions(+), 2729 deletions(-) diff --git a/tools/Makefile b/tools/Makefile index 7e9d34ddd7..db2f7b8ebe 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -12,6 +12,7 @@ help: @echo ' acpi - ACPI tools' @echo ' bpf - misc BPF tools' @echo ' cgroup - cgroup tools' + @echo ' counter - counter tools' @echo ' cpupower - a tool for all things x86 CPU power' @echo ' debugging - tools for debugging' @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer' @@ -23,7 +24,6 @@ help: @echo ' intel-speed-select - Intel Speed Select tool' @echo ' kvm_stat - top-like utility for displaying kvm statistics' @echo ' leds - LEDs tools' - @echo ' liblockdep - user-space wrapper for kernel locking-validator' @echo ' objtool - an ELF object analysis tool' @echo ' pci - PCI tools' @echo ' perf - Linux performance measurement and analysis tool' @@ -65,15 +65,12 @@ acpi: FORCE cpupower: FORCE $(call descend,power/$@) -cgroup firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE +cgroup counter firewire hv guest bootconfig spi usb virtio vm bpf iio gpio objtool leds wmi pci firmware debugging tracing: FORCE $(call descend,$@) bpf/%: FORCE $(call descend,$@) -liblockdep: FORCE - $(call descend,lib/lockdep) - libapi: FORCE $(call descend,lib/api) @@ -100,7 +97,7 @@ freefall: FORCE kvm_stat: FORCE $(call descend,kvm/$@) -all: acpi cgroup cpupower gpio hv firewire liblockdep \ +all: acpi cgroup counter cpupower gpio hv firewire \ perf selftests bootconfig spi turbostat usb \ virtio vm bpf x86_energy_perf_policy \ tmon freefall iio objtool kvm_stat wmi \ @@ -112,12 +109,9 @@ acpi_install: cpupower_install: $(call descend,power/$(@:_install=),install) -cgroup_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install: +cgroup_install counter_install firewire_install gpio_install hv_install iio_install perf_install bootconfig_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install pci_install debugging_install tracing_install: $(call descend,$(@:_install=),install) -liblockdep_install: - $(call descend,lib/lockdep,install) - selftests_install: $(call descend,testing/$(@:_install=),install) @@ -133,8 +127,8 @@ freefall_install: kvm_stat_install: $(call descend,kvm/$(@:_install=),install) -install: acpi_install cgroup_install cpupower_install gpio_install \ - hv_install firewire_install iio_install liblockdep_install \ +install: acpi_install cgroup_install counter_install cpupower_install gpio_install \ + hv_install firewire_install iio_install \ perf_install selftests_install turbostat_install usb_install \ virtio_install vm_install bpf_install x86_energy_perf_policy_install \ tmon_install freefall_install objtool_install kvm_stat_install \ @@ -147,12 +141,9 @@ acpi_clean: cpupower_clean: $(call descend,power/cpupower,clean) -cgroup_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean: +cgroup_clean counter_clean hv_clean firewire_clean bootconfig_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean pci_clean firmware_clean debugging_clean tracing_clean: $(call descend,$(@:_clean=),clean) -liblockdep_clean: - $(call descend,lib/lockdep,clean) - libapi_clean: $(call descend,lib/api,clean) @@ -181,10 +172,10 @@ freefall_clean: build_clean: $(call descend,build,clean) -clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \ +clean: acpi_clean cgroup_clean counter_clean cpupower_clean hv_clean firewire_clean \ perf_clean selftests_clean turbostat_clean bootconfig_clean spi_clean usb_clean virtio_clean \ vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ - freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \ + freefall_clean build_clean libbpf_clean libsubcmd_clean \ gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean \ intel-speed-select_clean tracing_clean diff --git a/tools/arch/arm64/include/uapi/asm/kvm.h b/tools/arch/arm64/include/uapi/asm/kvm.h index b3edde68bc..323e251ed3 100644 --- a/tools/arch/arm64/include/uapi/asm/kvm.h +++ b/tools/arch/arm64/include/uapi/asm/kvm.h @@ -281,6 +281,11 @@ struct kvm_arm_copy_mte_tags { #define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED 3 #define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED (1U << 4) +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3 KVM_REG_ARM_FW_REG(3) +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_AVAIL 0 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_AVAIL 1 +#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_3_NOT_REQUIRED 2 + /* SVE registers */ #define KVM_REG_ARM64_SVE (0x15 << KVM_REG_ARM_COPROC_SHIFT) diff --git a/tools/arch/powerpc/include/uapi/asm/perf_regs.h b/tools/arch/powerpc/include/uapi/asm/perf_regs.h index 578b3ee861..749a2e3af8 100644 --- a/tools/arch/powerpc/include/uapi/asm/perf_regs.h +++ b/tools/arch/powerpc/include/uapi/asm/perf_regs.h @@ -61,27 +61,35 @@ enum perf_event_powerpc_regs { PERF_REG_POWERPC_PMC4, PERF_REG_POWERPC_PMC5, PERF_REG_POWERPC_PMC6, - /* Max regs without the extended regs */ + PERF_REG_POWERPC_SDAR, + PERF_REG_POWERPC_SIAR, + /* Max mask value for interrupt regs w/o extended regs */ PERF_REG_POWERPC_MAX = PERF_REG_POWERPC_MMCRA + 1, + /* Max mask value for interrupt regs including extended regs */ + PERF_REG_EXTENDED_MAX = PERF_REG_POWERPC_SIAR + 1, }; #define PERF_REG_PMU_MASK ((1ULL << PERF_REG_POWERPC_MAX) - 1) -/* Exclude MMCR3, SIER2, SIER3 for CPU_FTR_ARCH_300 */ -#define PERF_EXCLUDE_REG_EXT_300 (7ULL << PERF_REG_POWERPC_MMCR3) - /* * PERF_REG_EXTENDED_MASK value for CPU_FTR_ARCH_300 - * includes 9 SPRS from MMCR0 to PMC6 excluding the - * unsupported SPRS in PERF_EXCLUDE_REG_EXT_300. + * includes 11 SPRS from MMCR0 to SIAR excluding the + * unsupported SPRS MMCR3, SIER2 and SIER3. */ -#define PERF_REG_PMU_MASK_300 ((0xfffULL << PERF_REG_POWERPC_MMCR0) - PERF_EXCLUDE_REG_EXT_300) +#define PERF_REG_PMU_MASK_300 \ + ((1ULL << PERF_REG_POWERPC_MMCR0) | (1ULL << PERF_REG_POWERPC_MMCR1) | \ + (1ULL << PERF_REG_POWERPC_MMCR2) | (1ULL << PERF_REG_POWERPC_PMC1) | \ + (1ULL << PERF_REG_POWERPC_PMC2) | (1ULL << PERF_REG_POWERPC_PMC3) | \ + (1ULL << PERF_REG_POWERPC_PMC4) | (1ULL << PERF_REG_POWERPC_PMC5) | \ + (1ULL << PERF_REG_POWERPC_PMC6) | (1ULL << PERF_REG_POWERPC_SDAR) | \ + (1ULL << PERF_REG_POWERPC_SIAR)) /* * PERF_REG_EXTENDED_MASK value for CPU_FTR_ARCH_31 - * includes 12 SPRs from MMCR0 to PMC6. + * includes 14 SPRs from MMCR0 to SIAR. */ -#define PERF_REG_PMU_MASK_31 (0xfffULL << PERF_REG_POWERPC_MMCR0) +#define PERF_REG_PMU_MASK_31 \ + (PERF_REG_PMU_MASK_300 | (1ULL << PERF_REG_POWERPC_MMCR3) | \ + (1ULL << PERF_REG_POWERPC_SIER2) | (1ULL << PERF_REG_POWERPC_SIER3)) -#define PERF_REG_EXTENDED_MAX (PERF_REG_POWERPC_PMC6 + 1) #endif /* _UAPI_ASM_POWERPC_PERF_REGS_H */ diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h index 63d30dde20..65d147974f 100644 --- a/tools/arch/x86/include/asm/cpufeatures.h +++ b/tools/arch/x86/include/asm/cpufeatures.h @@ -204,7 +204,7 @@ /* FREE! ( 7*32+10) */ #define X86_FEATURE_PTI ( 7*32+11) /* Kernel Page Table Isolation enabled */ #define X86_FEATURE_RETPOLINE ( 7*32+12) /* "" Generic Retpoline mitigation for Spectre variant 2 */ -#define X86_FEATURE_RETPOLINE_LFENCE ( 7*32+13) /* "" Use LFENCEs for Spectre variant 2 */ +#define X86_FEATURE_RETPOLINE_LFENCE ( 7*32+13) /* "" Use LFENCE for Spectre variant 2 */ #define X86_FEATURE_INTEL_PPIN ( 7*32+14) /* Intel Processor Inventory Number */ #define X86_FEATURE_CDP_L2 ( 7*32+15) /* Code and Data Prioritization L2 */ #define X86_FEATURE_MSR_SPEC_CTRL ( 7*32+16) /* "" MSR SPEC_CTRL is implemented */ @@ -277,6 +277,7 @@ #define X86_FEATURE_XSAVEC (10*32+ 1) /* XSAVEC instruction */ #define X86_FEATURE_XGETBV1 (10*32+ 2) /* XGETBV with ECX = 1 instruction */ #define X86_FEATURE_XSAVES (10*32+ 3) /* XSAVES/XRSTORS instructions */ +#define X86_FEATURE_XFD (10*32+ 4) /* "" eXtended Feature Disabling */ /* * Extended auxiliary flags: Linux defined - for features scattered in various @@ -298,6 +299,9 @@ /* Intel-defined CPU features, CPUID level 0x00000007:1 (EAX), word 12 */ #define X86_FEATURE_AVX_VNNI (12*32+ 4) /* AVX VNNI instructions */ #define X86_FEATURE_AVX512_BF16 (12*32+ 5) /* AVX512 BFLOAT16 instructions */ +#define X86_FEATURE_AMX_BF16 (18*32+22) /* AMX bf16 Support */ +#define X86_FEATURE_AMX_TILE (18*32+24) /* AMX tile Support */ +#define X86_FEATURE_AMX_INT8 (18*32+25) /* AMX int8 Support */ /* AMD-defined CPU features, CPUID level 0x80000008 (EBX), word 13 */ #define X86_FEATURE_CLZERO (13*32+ 0) /* CLZERO instruction */ @@ -313,6 +317,7 @@ #define X86_FEATURE_AMD_SSBD (13*32+24) /* "" Speculative Store Bypass Disable */ #define X86_FEATURE_VIRT_SSBD (13*32+25) /* Virtualized Speculative Store Bypass Disable */ #define X86_FEATURE_AMD_SSB_NO (13*32+26) /* "" Speculative Store Bypass is fixed in hardware. */ +#define X86_FEATURE_CPPC (13*32+27) /* Collaborative Processor Performance Control */ /* Thermal and Power Management Leaf, CPUID level 0x00000006 (EAX), word 14 */ #define X86_FEATURE_DTHERM (14*32+ 0) /* Digital Thermal Sensor */ diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h index a7c413432b..a4a39c3e0f 100644 --- a/tools/arch/x86/include/asm/msr-index.h +++ b/tools/arch/x86/include/asm/msr-index.h @@ -476,6 +476,7 @@ #define MSR_AMD64_ICIBSEXTDCTL 0xc001103c #define MSR_AMD64_IBSOPDATA4 0xc001103d #define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */ +#define MSR_AMD64_SVM_AVIC_DOORBELL 0xc001011b #define MSR_AMD64_VM_PAGE_FLUSH 0xc001011e #define MSR_AMD64_SEV_ES_GHCB 0xc0010130 #define MSR_AMD64_SEV 0xc0010131 @@ -486,6 +487,23 @@ #define MSR_AMD64_VIRT_SPEC_CTRL 0xc001011f +/* AMD Collaborative Processor Performance Control MSRs */ +#define MSR_AMD_CPPC_CAP1 0xc00102b0 +#define MSR_AMD_CPPC_ENABLE 0xc00102b1 +#define MSR_AMD_CPPC_CAP2 0xc00102b2 +#define MSR_AMD_CPPC_REQ 0xc00102b3 +#define MSR_AMD_CPPC_STATUS 0xc00102b4 + +#define AMD_CPPC_LOWEST_PERF(x) (((x) >> 0) & 0xff) +#define AMD_CPPC_LOWNONLIN_PERF(x) (((x) >> 8) & 0xff) +#define AMD_CPPC_NOMINAL_PERF(x) (((x) >> 16) & 0xff) +#define AMD_CPPC_HIGHEST_PERF(x) (((x) >> 24) & 0xff) + +#define AMD_CPPC_MAX_PERF(x) (((x) & 0xff) << 0) +#define AMD_CPPC_MIN_PERF(x) (((x) & 0xff) << 8) +#define AMD_CPPC_DES_PERF(x) (((x) & 0xff) << 16) +#define AMD_CPPC_ENERGY_PERF_PREF(x) (((x) & 0xff) << 24) + /* Fam 17h MSRs */ #define MSR_F17H_IRPERF 0xc00000e9 @@ -625,6 +643,8 @@ #define MSR_IA32_BNDCFGS_RSVD 0x00000ffc +#define MSR_IA32_XFD 0x000001c4 +#define MSR_IA32_XFD_ERR 0x000001c5 #define MSR_IA32_XSS 0x00000da0 #define MSR_IA32_APICBASE 0x0000001b diff --git a/tools/arch/x86/include/asm/required-features.h b/tools/arch/x86/include/asm/required-features.h index b2d504f119..aff774775c 100644 --- a/tools/arch/x86/include/asm/required-features.h +++ b/tools/arch/x86/include/asm/required-features.h @@ -35,11 +35,7 @@ # define NEED_CMOV 0 #endif -#ifdef CONFIG_X86_USE_3DNOW -# define NEED_3DNOW (1<<(X86_FEATURE_3DNOW & 31)) -#else # define NEED_3DNOW 0 -#endif #if defined(CONFIG_X86_P6_NOP) || defined(CONFIG_X86_64) # define NEED_NOPL (1<<(X86_FEATURE_NOPL & 31)) diff --git a/tools/arch/x86/include/uapi/asm/kvm.h b/tools/arch/x86/include/uapi/asm/kvm.h index 2ef1f6513c..bf6e96011d 100644 --- a/tools/arch/x86/include/uapi/asm/kvm.h +++ b/tools/arch/x86/include/uapi/asm/kvm.h @@ -373,9 +373,23 @@ struct kvm_debugregs { __u64 reserved[9]; }; -/* for KVM_CAP_XSAVE */ +/* for KVM_CAP_XSAVE and KVM_CAP_XSAVE2 */ struct kvm_xsave { + /* + * KVM_GET_XSAVE2 and KVM_SET_XSAVE write and read as many bytes + * as are returned by KVM_CHECK_EXTENSION(KVM_CAP_XSAVE2) + * respectively, when invoked on the vm file descriptor. + * + * The size value returned by KVM_CHECK_EXTENSION(KVM_CAP_XSAVE2) + * will always be at least 4096. Currently, it is only greater + * than 4096 if a dynamic feature has been enabled with + * ``arch_prctl()``, but this may change in the future. + * + * The offsets of the state save areas in struct kvm_xsave follow + * the contents of CPUID leaf 0xD on the host. + */ __u32 region[1024]; + __u32 extra[0]; }; #define KVM_MAX_XCRS 16 @@ -438,6 +452,9 @@ struct kvm_sync_regs { #define KVM_STATE_VMX_PREEMPTION_TIMER_DEADLINE 0x00000001 +/* attributes for system fd (group 0) */ +#define KVM_X86_XCOMP_GUEST_SUPP 0 + struct kvm_vmx_nested_state_data { __u8 vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; __u8 shadow_vmcs12[KVM_STATE_NESTED_VMX_VMCS_SIZE]; @@ -504,4 +521,8 @@ struct kvm_pmu_event_filter { #define KVM_PMU_EVENT_ALLOW 0 #define KVM_PMU_EVENT_DENY 1 +/* for KVM_{GET,SET,HAS}_DEVICE_ATTR */ +#define KVM_VCPU_TSC_CTRL 0 /* control group for the timestamp counter (TSC) */ +#define KVM_VCPU_TSC_OFFSET 0 /* attribute for the TSC offset */ + #endif /* _ASM_X86_KVM_H */ diff --git a/tools/arch/x86/include/uapi/asm/prctl.h b/tools/arch/x86/include/uapi/asm/prctl.h index 5a6aac9fa4..500b96e71f 100644 --- a/tools/arch/x86/include/uapi/asm/prctl.h +++ b/tools/arch/x86/include/uapi/asm/prctl.h @@ -2,16 +2,22 @@ #ifndef _ASM_X86_PRCTL_H #define _ASM_X86_PRCTL_H -#define ARCH_SET_GS 0x1001 -#define ARCH_SET_FS 0x1002 -#define ARCH_GET_FS 0x1003 -#define ARCH_GET_GS 0x1004 +#define ARCH_SET_GS 0x1001 +#define ARCH_SET_FS 0x1002 +#define ARCH_GET_FS 0x1003 +#define ARCH_GET_GS 0x1004 -#define ARCH_GET_CPUID 0x1011 -#define ARCH_SET_CPUID 0x1012 +#define ARCH_GET_CPUID 0x1011 +#define ARCH_SET_CPUID 0x1012 -#define ARCH_MAP_VDSO_X32 0x2001 -#define ARCH_MAP_VDSO_32 0x2002 -#define ARCH_MAP_VDSO_64 0x2003 +#define ARCH_GET_XCOMP_SUPP 0x1021 +#define ARCH_GET_XCOMP_PERM 0x1022 +#define ARCH_REQ_XCOMP_PERM 0x1023 +#define ARCH_GET_XCOMP_GUEST_PERM 0x1024 +#define ARCH_REQ_XCOMP_GUEST_PERM 0x1025 + +#define ARCH_MAP_VDSO_X32 0x2001 +#define ARCH_MAP_VDSO_32 0x2002 +#define ARCH_MAP_VDSO_64 0x2003 #endif /* _ASM_X86_PRCTL_H */ diff --git a/tools/arch/x86/lib/memcpy_64.S b/tools/arch/x86/lib/memcpy_64.S index 1cc9da6e29..59cf2343f3 100644 --- a/tools/arch/x86/lib/memcpy_64.S +++ b/tools/arch/x86/lib/memcpy_64.S @@ -39,7 +39,7 @@ SYM_FUNC_START_WEAK(memcpy) rep movsq movl %edx, %ecx rep movsb - ret + RET SYM_FUNC_END(memcpy) SYM_FUNC_END_ALIAS(__memcpy) EXPORT_SYMBOL(memcpy) @@ -53,7 +53,7 @@ SYM_FUNC_START_LOCAL(memcpy_erms) movq %rdi, %rax movq %rdx, %rcx rep movsb - ret + RET SYM_FUNC_END(memcpy_erms) SYM_FUNC_START_LOCAL(memcpy_orig) @@ -137,7 +137,7 @@ SYM_FUNC_START_LOCAL(memcpy_orig) movq %r9, 1*8(%rdi) movq %r10, -2*8(%rdi, %rdx) movq %r11, -1*8(%rdi, %rdx) - retq + RET .p2align 4 .Lless_16bytes: cmpl $8, %edx @@ -149,7 +149,7 @@ SYM_FUNC_START_LOCAL(memcpy_orig) movq -1*8(%rsi, %rdx), %r9 movq %r8, 0*8(%rdi) movq %r9, -1*8(%rdi, %rdx) - retq + RET .p2align 4 .Lless_8bytes: cmpl $4, %edx @@ -162,7 +162,7 @@ SYM_FUNC_START_LOCAL(memcpy_orig) movl -4(%rsi, %rdx), %r8d movl %ecx, (%rdi) movl %r8d, -4(%rdi, %rdx) - retq + RET .p2align 4 .Lless_3bytes: subl $1, %edx @@ -180,7 +180,7 @@ SYM_FUNC_START_LOCAL(memcpy_orig) movb %cl, (%rdi) .Lend: - retq + RET SYM_FUNC_END(memcpy_orig) .popsection diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index 8e0163b7ef..20b8ab984d 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore @@ -37,6 +37,7 @@ trace/beauty/generated/ pmu-events/pmu-events.c pmu-events/jevents feature/ +libbpf/ fixdep libtraceevent-dynamic-list Documentation/doc.dep diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index f05c4d48fd..f5d72f936a 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -17,7 +17,11 @@ tools/lib/symbol/kallsyms.c tools/lib/symbol/kallsyms.h tools/lib/find_bit.c tools/lib/bitmap.c +tools/lib/list_sort.c tools/lib/str_error_r.c tools/lib/vsprintf.c tools/lib/zalloc.c scripts/bpf_doc.py +tools/bpf/bpftool +kernel/bpf/disasm.c +kernel/bpf/disasm.h diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 71772b20ea..96ad944ca6 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -17,6 +17,7 @@ detected = $(shell echo "$(1)=y" >> $(OUTPUT).config-detected) detected_var = $(shell echo "$(1)=$($(1))" >> $(OUTPUT).config-detected) CFLAGS := $(EXTRA_CFLAGS) $(filter-out -Wnested-externs,$(EXTRA_WARNINGS)) +HOSTCFLAGS := $(filter-out -Wnested-externs,$(EXTRA_WARNINGS)) include $(srctree)/tools/scripts/Makefile.arch @@ -214,6 +215,7 @@ endif ifneq ($(WERROR),0) CORE_CFLAGS += -Werror CXXFLAGS += -Werror + HOSTCFLAGS += -Werror endif ifndef DEBUG @@ -286,13 +288,16 @@ CORE_CFLAGS += -Wall CORE_CFLAGS += -Wextra CORE_CFLAGS += -std=gnu99 -CXXFLAGS += -std=gnu++11 -fno-exceptions -fno-rtti +CXXFLAGS += -std=gnu++14 -fno-exceptions -fno-rtti CXXFLAGS += -Wall CXXFLAGS += -fno-omit-frame-pointer CXXFLAGS += -ggdb3 CXXFLAGS += -funwind-tables CXXFLAGS += -Wno-strict-aliasing +HOSTCFLAGS += -Wall +HOSTCFLAGS += -Wextra + # Enforce a non-executable stack, as we may regress (again) in the future by # adding assembler files missing the .GNU-stack linker note. LDFLAGS += -Wl,-z,noexecstack @@ -1011,6 +1016,9 @@ ifndef NO_AUXTRACE ifndef NO_AUXTRACE $(call detected,CONFIG_AUXTRACE) CFLAGS += -DHAVE_AUXTRACE_SUPPORT + ifeq ($(feature-reallocarray), 0) + CFLAGS += -DCOMPAT_NEED_REALLOCARRAY + endif endif endif @@ -1094,11 +1102,32 @@ ifdef LIBTRACEEVENT_DYNAMIC $(call feature_check,libtraceevent) ifeq ($(feature-libtraceevent), 1) EXTLIBS += -ltraceevent + LIBTRACEEVENT_VERSION := $(shell $(PKG_CONFIG) --modversion libtraceevent) + LIBTRACEEVENT_VERSION_1 := $(word 1, $(subst ., ,$(LIBTRACEEVENT_VERSION))) + LIBTRACEEVENT_VERSION_2 := $(word 2, $(subst ., ,$(LIBTRACEEVENT_VERSION))) + LIBTRACEEVENT_VERSION_3 := $(word 3, $(subst ., ,$(LIBTRACEEVENT_VERSION))) + LIBTRACEEVENT_VERSION_CPP := $(shell expr $(LIBTRACEEVENT_VERSION_1) \* 255 \* 255 + $(LIBTRACEEVENT_VERSION_2) \* 255 + $(LIBTRACEEVENT_VERSION_3)) + CFLAGS += -DLIBTRACEEVENT_VERSION=$(LIBTRACEEVENT_VERSION_CPP) else dummy := $(error Error: No libtraceevent devel library found, please install libtraceevent-devel); endif endif +ifdef LIBTRACEFS_DYNAMIC + $(call feature_check,libtracefs) + ifeq ($(feature-libtracefs), 1) + EXTLIBS += -ltracefs + LIBTRACEFS_VERSION := $(shell $(PKG_CONFIG) --modversion libtracefs) + LIBTRACEFS_VERSION_1 := $(word 1, $(subst ., ,$(LIBTRACEFS_VERSION))) + LIBTRACEFS_VERSION_2 := $(word 2, $(subst ., ,$(LIBTRACEFS_VERSION))) + LIBTRACEFS_VERSION_3 := $(word 3, $(subst ., ,$(LIBTRACEFS_VERSION))) + LIBTRACEFS_VERSION_CPP := $(shell expr $(LIBTRACEFS_VERSION_1) \* 255 \* 255 + $(LIBTRACEFS_VERSION_2) \* 255 + $(LIBTRACEFS_VERSION_3)) + CFLAGS += -DLIBTRACEFS_VERSION=$(LIBTRACEFS_VERSION_CPP) + else + dummy := $(error Error: No libtracefs devel library found, please install libtracefs-dev); + endif +endif + # Among the variables below, these: # perfexecdir # perf_include_dir diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index b856afa6eb..ac861e42c8 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -130,6 +130,8 @@ include ../scripts/utilities.mak # # Define LIBTRACEEVENT_DYNAMIC to enable libtraceevent dynamic linking # +# Define LIBTRACEFS_DYNAMIC to enable libtracefs dynamic linking +# # As per kernel Makefile, avoid funny character set dependencies unexport LC_ALL @@ -224,7 +226,7 @@ else endif export srctree OUTPUT RM CC CXX LD AR CFLAGS CXXFLAGS V BISON FLEX AWK -export HOSTCC HOSTLD HOSTAR +export HOSTCC HOSTLD HOSTAR HOSTCFLAGS include $(srctree)/tools/build/Makefile.include @@ -241,7 +243,7 @@ else # force_fixdep LIB_DIR = $(srctree)/tools/lib/api/ TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/ -BPF_DIR = $(srctree)/tools/lib/bpf/ +LIBBPF_DIR = $(srctree)/tools/lib/bpf/ SUBCMD_DIR = $(srctree)/tools/lib/subcmd/ LIBPERF_DIR = $(srctree)/tools/lib/perf/ DOC_DIR = $(srctree)/tools/perf/Documentation/ @@ -293,7 +295,6 @@ strip-libs = $(filter-out -l%,$(1)) ifneq ($(OUTPUT),) TE_PATH=$(OUTPUT) PLUGINS_PATH=$(OUTPUT) - BPF_PATH=$(OUTPUT) SUBCMD_PATH=$(OUTPUT) LIBPERF_PATH=$(OUTPUT) ifneq ($(subdir),) @@ -305,7 +306,6 @@ else TE_PATH=$(TRACE_EVENT_DIR) PLUGINS_PATH=$(TRACE_EVENT_DIR)plugins/ API_PATH=$(LIB_DIR) - BPF_PATH=$(BPF_DIR) SUBCMD_PATH=$(SUBCMD_DIR) LIBPERF_PATH=$(LIBPERF_DIR) endif @@ -324,7 +324,14 @@ LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS = $(if $(findstring -static,$(LDFLAGS)),,$(DY LIBAPI = $(API_PATH)libapi.a export LIBAPI -LIBBPF = $(BPF_PATH)libbpf.a +ifneq ($(OUTPUT),) + LIBBPF_OUTPUT = $(abspath $(OUTPUT))/libbpf +else + LIBBPF_OUTPUT = $(CURDIR)/libbpf +endif +LIBBPF_DESTDIR = $(LIBBPF_OUTPUT) +LIBBPF_INCLUDE = $(LIBBPF_DESTDIR)/include +LIBBPF = $(LIBBPF_OUTPUT)/libbpf.a LIBSUBCMD = $(SUBCMD_PATH)libsubcmd.a @@ -360,7 +367,7 @@ ifndef NO_JVMTI PROGRAMS += $(OUTPUT)$(LIBJVMTI) endif -DLFILTERS := dlfilter-test-api-v0.so +DLFILTERS := dlfilter-test-api-v0.so dlfilter-show-cycles.so DLFILTERS := $(patsubst %,$(OUTPUT)dlfilters/%,$(DLFILTERS)) # what 'all' will build and 'install' will install, in perfexecdir @@ -509,17 +516,17 @@ kvm_ioctl_tbl := $(srctree)/tools/perf/trace/beauty/kvm_ioctl.sh $(kvm_ioctl_array): $(kvm_hdr_dir)/kvm.h $(kvm_ioctl_tbl) $(Q)$(SHELL) '$(kvm_ioctl_tbl)' $(kvm_hdr_dir) > $@ -socket_ipproto_array := $(beauty_outdir)/socket_ipproto_array.c -socket_ipproto_tbl := $(srctree)/tools/perf/trace/beauty/socket_ipproto.sh - -$(socket_ipproto_array): $(linux_uapi_dir)/in.h $(socket_ipproto_tbl) - $(Q)$(SHELL) '$(socket_ipproto_tbl)' $(linux_uapi_dir) > $@ - -socket_arrays := $(beauty_outdir)/socket_arrays.c +socket_arrays := $(beauty_outdir)/socket.c socket_tbl := $(srctree)/tools/perf/trace/beauty/socket.sh -$(socket_arrays): $(beauty_linux_dir)/socket.h $(socket_tbl) - $(Q)$(SHELL) '$(socket_tbl)' $(beauty_linux_dir) > $@ +$(socket_arrays): $(linux_uapi_dir)/in.h $(beauty_linux_dir)/socket.h $(socket_tbl) + $(Q)$(SHELL) '$(socket_tbl)' $(linux_uapi_dir) $(beauty_linux_dir) > $@ + +sockaddr_arrays := $(beauty_outdir)/sockaddr.c +sockaddr_tbl := $(srctree)/tools/perf/trace/beauty/sockaddr.sh + +$(sockaddr_arrays): $(beauty_linux_dir)/socket.h $(sockaddr_tbl) + $(Q)$(SHELL) '$(sockaddr_tbl)' $(beauty_linux_dir) > $@ vhost_virtio_ioctl_array := $(beauty_ioctl_outdir)/vhost_virtio_ioctl_array.c vhost_virtio_hdr_dir := $(srctree)/tools/include/uapi/linux @@ -729,8 +736,8 @@ prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h archheaders $(drm_ioc $(sndrv_ctl_ioctl_array) \ $(kcmp_type_array) \ $(kvm_ioctl_array) \ - $(socket_ipproto_array) \ $(socket_arrays) \ + $(sockaddr_arrays) \ $(vhost_virtio_ioctl_array) \ $(madvise_behavior_array) \ $(mmap_flags_array) \ @@ -829,12 +836,14 @@ $(LIBAPI)-clean: $(call QUIET_CLEAN, libapi) $(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null -$(LIBBPF): FORCE - $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) $(OUTPUT)libbpf.a FEATURES_DUMP=$(FEATURE_DUMP_EXPORT) +$(LIBBPF): FORCE | $(LIBBPF_OUTPUT) + $(Q)$(MAKE) -C $(LIBBPF_DIR) FEATURES_DUMP=$(FEATURE_DUMP_EXPORT) \ + O= OUTPUT=$(LIBBPF_OUTPUT)/ DESTDIR=$(LIBBPF_DESTDIR) prefix= \ + $@ install_headers $(LIBBPF)-clean: $(call QUIET_CLEAN, libbpf) - $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) clean >/dev/null + $(Q)$(RM) -r -- $(LIBBPF_OUTPUT) $(LIBPERF): FORCE $(Q)$(MAKE) -C $(LIBPERF_DIR) EXTRA_CFLAGS="$(LIBPERF_CFLAGS)" O=$(OUTPUT) $(OUTPUT)libperf.a @@ -1032,18 +1041,17 @@ SKEL_OUT := $(abspath $(OUTPUT)util/bpf_skel) SKEL_TMP_OUT := $(abspath $(SKEL_OUT)/.tmp) SKELETONS := $(SKEL_OUT)/bpf_prog_profiler.skel.h SKELETONS += $(SKEL_OUT)/bperf_leader.skel.h $(SKEL_OUT)/bperf_follower.skel.h -SKELETONS += $(SKEL_OUT)/bperf_cgroup.skel.h +SKELETONS += $(SKEL_OUT)/bperf_cgroup.skel.h $(SKEL_OUT)/func_latency.skel.h + +$(SKEL_TMP_OUT) $(LIBBPF_OUTPUT): + $(Q)$(MKDIR) -p $@ ifdef BUILD_BPF_SKEL BPFTOOL := $(SKEL_TMP_OUT)/bootstrap/bpftool -LIBBPF_SRC := $(abspath ../lib/bpf) -BPF_INCLUDE := -I$(SKEL_TMP_OUT)/.. -I$(BPF_PATH) -I$(LIBBPF_SRC)/.. - -$(SKEL_TMP_OUT): - $(Q)$(MKDIR) -p $@ +BPF_INCLUDE := -I$(SKEL_TMP_OUT)/.. -I$(LIBBPF_INCLUDE) $(BPFTOOL): | $(SKEL_TMP_OUT) - CFLAGS= $(MAKE) -C ../bpf/bpftool \ + $(Q)CFLAGS= $(MAKE) -C ../bpf/bpftool \ OUTPUT=$(SKEL_TMP_OUT)/ bootstrap VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux) \ @@ -1105,8 +1113,8 @@ clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clea $(OUTPUT)$(sndrv_pcm_ioctl_array) \ $(OUTPUT)$(kvm_ioctl_array) \ $(OUTPUT)$(kcmp_type_array) \ - $(OUTPUT)$(socket_ipproto_array) \ $(OUTPUT)$(socket_arrays) \ + $(OUTPUT)$(sockaddr_arrays) \ $(OUTPUT)$(vhost_virtio_ioctl_array) \ $(OUTPUT)$(perf_ioctl_array) \ $(OUTPUT)$(prctl_option_array) \ diff --git a/tools/perf/arch/arm/include/arch-tests.h b/tools/perf/arch/arm/include/arch-tests.h index c625380524..452b3d9045 100644 --- a/tools/perf/arch/arm/include/arch-tests.h +++ b/tools/perf/arch/arm/include/arch-tests.h @@ -2,6 +2,6 @@ #ifndef ARCH_TESTS_H #define ARCH_TESTS_H -extern struct test arch_tests[]; +extern struct test_suite *arch_tests[]; #endif diff --git a/tools/perf/arch/arm/include/perf_regs.h b/tools/perf/arch/arm/include/perf_regs.h index 4085419283..99a06550e2 100644 --- a/tools/perf/arch/arm/include/perf_regs.h +++ b/tools/perf/arch/arm/include/perf_regs.h @@ -15,46 +15,4 @@ void perf_regs_load(u64 *regs); #define PERF_REG_IP PERF_REG_ARM_PC #define PERF_REG_SP PERF_REG_ARM_SP -static inline const char *__perf_reg_name(int id) -{ - switch (id) { - case PERF_REG_ARM_R0: - return "r0"; - case PERF_REG_ARM_R1: - return "r1"; - case PERF_REG_ARM_R2: - return "r2"; - case PERF_REG_ARM_R3: - return "r3"; - case PERF_REG_ARM_R4: - return "r4"; - case PERF_REG_ARM_R5: - return "r5"; - case PERF_REG_ARM_R6: - return "r6"; - case PERF_REG_ARM_R7: - return "r7"; - case PERF_REG_ARM_R8: - return "r8"; - case PERF_REG_ARM_R9: - return "r9"; - case PERF_REG_ARM_R10: - return "r10"; - case PERF_REG_ARM_FP: - return "fp"; - case PERF_REG_ARM_IP: - return "ip"; - case PERF_REG_ARM_SP: - return "sp"; - case PERF_REG_ARM_LR: - return "lr"; - case PERF_REG_ARM_PC: - return "pc"; - default: - return NULL; - } - - return NULL; -} - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/arm/tests/arch-tests.c b/tools/perf/arch/arm/tests/arch-tests.c index 6848101a85..69561111cc 100644 --- a/tools/perf/arch/arm/tests/arch-tests.c +++ b/tools/perf/arch/arm/tests/arch-tests.c @@ -3,18 +3,10 @@ #include "tests/tests.h" #include "arch-tests.h" -struct test arch_tests[] = { +struct test_suite *arch_tests[] = { #ifdef HAVE_DWARF_UNWIND_SUPPORT - { - .desc = "DWARF unwind", - .func = test__dwarf_unwind, - }, + &suite__dwarf_unwind, #endif - { - .desc = "Vectors page", - .func = test__vectors_page, - }, - { - .func = NULL, - }, + &suite__vectors_page, + NULL, }; diff --git a/tools/perf/arch/arm/tests/vectors-page.c b/tools/perf/arch/arm/tests/vectors-page.c index 7ffdd79971..55a8358374 100644 --- a/tools/perf/arch/arm/tests/vectors-page.c +++ b/tools/perf/arch/arm/tests/vectors-page.c @@ -9,8 +9,7 @@ #define VECTORS__MAP_NAME "[vectors]" -int test__vectors_page(struct test *test __maybe_unused, - int subtest __maybe_unused) +static int test__vectors_page(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { void *start, *end; @@ -22,3 +21,5 @@ int test__vectors_page(struct test *test __maybe_unused, return TEST_OK; } + +DEFINE_SUITE("Vectors page", vectors_page); diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c index 293a23bf8b..2e8b2c4365 100644 --- a/tools/perf/arch/arm/util/cs-etm.c +++ b/tools/perf/arch/arm/util/cs-etm.c @@ -203,9 +203,11 @@ static int cs_etm_set_option(struct auxtrace_record *itr, struct perf_cpu_map *online_cpus = perf_cpu_map__new(NULL); /* Set option of each CPU we have */ - for (i = 0; i < cpu__max_cpu(); i++) { - if (!cpu_map__has(event_cpus, i) || - !cpu_map__has(online_cpus, i)) + for (i = 0; i < cpu__max_cpu().cpu; i++) { + struct perf_cpu cpu = { .cpu = i, }; + + if (!perf_cpu_map__has(event_cpus, cpu) || + !perf_cpu_map__has(online_cpus, cpu)) continue; if (option & BIT(ETM_OPT_CTXTID)) { @@ -407,25 +409,6 @@ static int cs_etm_recording_options(struct auxtrace_record *itr, } - /* Validate auxtrace_mmap_pages provided by user */ - if (opts->auxtrace_mmap_pages) { - unsigned int max_page = (KiB(128) / page_size); - size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size; - - if (!privileged && - opts->auxtrace_mmap_pages > max_page) { - opts->auxtrace_mmap_pages = max_page; - pr_err("auxtrace too big, truncating to %d\n", - max_page); - } - - if (!is_power_of_2(sz)) { - pr_err("Invalid mmap size for %s: must be a power of 2\n", - CORESIGHT_ETM_PMU_NAME); - return -EINVAL; - } - } - if (opts->auxtrace_snapshot_mode) pr_debug2("%s snapshot size: %zu\n", CORESIGHT_ETM_PMU_NAME, opts->auxtrace_snapshot_size); @@ -541,9 +524,11 @@ cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused, /* cpu map is not empty, we have specific CPUs to work with */ if (!perf_cpu_map__empty(event_cpus)) { - for (i = 0; i < cpu__max_cpu(); i++) { - if (!cpu_map__has(event_cpus, i) || - !cpu_map__has(online_cpus, i)) + for (i = 0; i < cpu__max_cpu().cpu; i++) { + struct perf_cpu cpu = { .cpu = i, }; + + if (!perf_cpu_map__has(event_cpus, cpu) || + !perf_cpu_map__has(online_cpus, cpu)) continue; if (cs_etm_is_ete(itr, i)) @@ -555,8 +540,10 @@ cs_etm_info_priv_size(struct auxtrace_record *itr __maybe_unused, } } else { /* get configuration for all CPUs in the system */ - for (i = 0; i < cpu__max_cpu(); i++) { - if (!cpu_map__has(online_cpus, i)) + for (i = 0; i < cpu__max_cpu().cpu; i++) { + struct perf_cpu cpu = { .cpu = i, }; + + if (!perf_cpu_map__has(online_cpus, cpu)) continue; if (cs_etm_is_ete(itr, i)) @@ -741,8 +728,10 @@ static int cs_etm_info_fill(struct auxtrace_record *itr, } else { /* Make sure all specified CPUs are online */ for (i = 0; i < perf_cpu_map__nr(event_cpus); i++) { - if (cpu_map__has(event_cpus, i) && - !cpu_map__has(online_cpus, i)) + struct perf_cpu cpu = { .cpu = i, }; + + if (perf_cpu_map__has(event_cpus, cpu) && + !perf_cpu_map__has(online_cpus, cpu)) return -EINVAL; } @@ -762,9 +751,12 @@ static int cs_etm_info_fill(struct auxtrace_record *itr, offset = CS_ETM_SNAPSHOT + 1; - for (i = 0; i < cpu__max_cpu() && offset < priv_size; i++) - if (cpu_map__has(cpu_map, i)) + for (i = 0; i < cpu__max_cpu().cpu && offset < priv_size; i++) { + struct perf_cpu cpu = { .cpu = i, }; + + if (perf_cpu_map__has(cpu_map, cpu)) cs_etm_get_metadata(i, &offset, itr, info); + } perf_cpu_map__put(online_cpus); diff --git a/tools/perf/arch/arm64/include/arch-tests.h b/tools/perf/arch/arm64/include/arch-tests.h index c625380524..452b3d9045 100644 --- a/tools/perf/arch/arm64/include/arch-tests.h +++ b/tools/perf/arch/arm64/include/arch-tests.h @@ -2,6 +2,6 @@ #ifndef ARCH_TESTS_H #define ARCH_TESTS_H -extern struct test arch_tests[]; +extern struct test_suite *arch_tests[]; #endif diff --git a/tools/perf/arch/arm64/include/perf_regs.h b/tools/perf/arch/arm64/include/perf_regs.h index fa3e07459f..35a3cc775b 100644 --- a/tools/perf/arch/arm64/include/perf_regs.h +++ b/tools/perf/arch/arm64/include/perf_regs.h @@ -4,7 +4,9 @@ #include #include +#define perf_event_arm_regs perf_event_arm64_regs #include +#undef perf_event_arm_regs void perf_regs_load(u64 *regs); @@ -15,80 +17,4 @@ void perf_regs_load(u64 *regs); #define PERF_REG_IP PERF_REG_ARM64_PC #define PERF_REG_SP PERF_REG_ARM64_SP -static inline const char *__perf_reg_name(int id) -{ - switch (id) { - case PERF_REG_ARM64_X0: - return "x0"; - case PERF_REG_ARM64_X1: - return "x1"; - case PERF_REG_ARM64_X2: - return "x2"; - case PERF_REG_ARM64_X3: - return "x3"; - case PERF_REG_ARM64_X4: - return "x4"; - case PERF_REG_ARM64_X5: - return "x5"; - case PERF_REG_ARM64_X6: - return "x6"; - case PERF_REG_ARM64_X7: - return "x7"; - case PERF_REG_ARM64_X8: - return "x8"; - case PERF_REG_ARM64_X9: - return "x9"; - case PERF_REG_ARM64_X10: - return "x10"; - case PERF_REG_ARM64_X11: - return "x11"; - case PERF_REG_ARM64_X12: - return "x12"; - case PERF_REG_ARM64_X13: - return "x13"; - case PERF_REG_ARM64_X14: - return "x14"; - case PERF_REG_ARM64_X15: - return "x15"; - case PERF_REG_ARM64_X16: - return "x16"; - case PERF_REG_ARM64_X17: - return "x17"; - case PERF_REG_ARM64_X18: - return "x18"; - case PERF_REG_ARM64_X19: - return "x19"; - case PERF_REG_ARM64_X20: - return "x20"; - case PERF_REG_ARM64_X21: - return "x21"; - case PERF_REG_ARM64_X22: - return "x22"; - case PERF_REG_ARM64_X23: - return "x23"; - case PERF_REG_ARM64_X24: - return "x24"; - case PERF_REG_ARM64_X25: - return "x25"; - case PERF_REG_ARM64_X26: - return "x26"; - case PERF_REG_ARM64_X27: - return "x27"; - case PERF_REG_ARM64_X28: - return "x28"; - case PERF_REG_ARM64_X29: - return "x29"; - case PERF_REG_ARM64_SP: - return "sp"; - case PERF_REG_ARM64_LR: - return "lr"; - case PERF_REG_ARM64_PC: - return "pc"; - default: - return NULL; - } - - return NULL; -} - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/arm64/tests/arch-tests.c b/tools/perf/arch/arm64/tests/arch-tests.c index 5b1543c980..ad16b4f8f6 100644 --- a/tools/perf/arch/arm64/tests/arch-tests.c +++ b/tools/perf/arch/arm64/tests/arch-tests.c @@ -3,14 +3,9 @@ #include "tests/tests.h" #include "arch-tests.h" -struct test arch_tests[] = { +struct test_suite *arch_tests[] = { #ifdef HAVE_DWARF_UNWIND_SUPPORT - { - .desc = "DWARF unwind", - .func = test__dwarf_unwind, - }, + &suite__dwarf_unwind, #endif - { - .func = NULL, - }, + NULL, }; diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c index a4420d4df5..2100d46ccf 100644 --- a/tools/perf/arch/arm64/util/arm-spe.c +++ b/tools/perf/arch/arm64/util/arm-spe.c @@ -23,6 +23,7 @@ #include "../../../util/auxtrace.h" #include "../../../util/record.h" #include "../../../util/arm-spe.h" +#include // reallocarray #define KiB(x) ((x) * 1024) #define MiB(x) ((x) * 1024 * 1024) @@ -31,6 +32,8 @@ struct arm_spe_recording { struct auxtrace_record itr; struct perf_pmu *arm_spe_pmu; struct evlist *evlist; + int wrapped_cnt; + bool *wrapped; }; static void arm_spe_set_timestamp(struct auxtrace_record *itr, @@ -84,6 +87,55 @@ static int arm_spe_info_fill(struct auxtrace_record *itr, return 0; } +static void +arm_spe_snapshot_resolve_auxtrace_defaults(struct record_opts *opts, + bool privileged) +{ + /* + * The default snapshot size is the auxtrace mmap size. If neither auxtrace mmap size nor + * snapshot size is specified, then the default is 4MiB for privileged users, 128KiB for + * unprivileged users. + * + * The default auxtrace mmap size is 4MiB/page_size for privileged users, 128KiB for + * unprivileged users. If an unprivileged user does not specify mmap pages, the mmap pages + * will be reduced from the default 512KiB/page_size to 256KiB/page_size, otherwise the + * user is likely to get an error as they exceed their mlock limmit. + */ + + /* + * No size were given to '-S' or '-m,', so go with the default + */ + if (!opts->auxtrace_snapshot_size && !opts->auxtrace_mmap_pages) { + if (privileged) { + opts->auxtrace_mmap_pages = MiB(4) / page_size; + } else { + opts->auxtrace_mmap_pages = KiB(128) / page_size; + if (opts->mmap_pages == UINT_MAX) + opts->mmap_pages = KiB(256) / page_size; + } + } else if (!opts->auxtrace_mmap_pages && !privileged && opts->mmap_pages == UINT_MAX) { + opts->mmap_pages = KiB(256) / page_size; + } + + /* + * '-m,xyz' was specified but no snapshot size, so make the snapshot size as big as the + * auxtrace mmap area. + */ + if (!opts->auxtrace_snapshot_size) + opts->auxtrace_snapshot_size = opts->auxtrace_mmap_pages * (size_t)page_size; + + /* + * '-Sxyz' was specified but no auxtrace mmap area, so make the auxtrace mmap area big + * enough to fit the requested snapshot size. + */ + if (!opts->auxtrace_mmap_pages) { + size_t sz = opts->auxtrace_snapshot_size; + + sz = round_up(sz, page_size) / page_size; + opts->auxtrace_mmap_pages = roundup_pow_of_two(sz); + } +} + static int arm_spe_recording_options(struct auxtrace_record *itr, struct evlist *evlist, struct record_opts *opts) @@ -115,6 +167,36 @@ static int arm_spe_recording_options(struct auxtrace_record *itr, if (!opts->full_auxtrace) return 0; + /* + * we are in snapshot mode. + */ + if (opts->auxtrace_snapshot_mode) { + /* + * Command arguments '-Sxyz' and/or '-m,xyz' are missing, so fill those in with + * default values. + */ + if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) + arm_spe_snapshot_resolve_auxtrace_defaults(opts, privileged); + + /* + * Snapshot size can't be bigger than the auxtrace area. + */ + if (opts->auxtrace_snapshot_size > opts->auxtrace_mmap_pages * (size_t)page_size) { + pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n", + opts->auxtrace_snapshot_size, + opts->auxtrace_mmap_pages * (size_t)page_size); + return -EINVAL; + } + + /* + * Something went wrong somewhere - this shouldn't happen. + */ + if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) { + pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n"); + return -EINVAL; + } + } + /* We are in full trace mode but '-m,xyz' wasn't specified */ if (!opts->auxtrace_mmap_pages) { if (privileged) { @@ -138,6 +220,9 @@ static int arm_spe_recording_options(struct auxtrace_record *itr, } } + if (opts->auxtrace_snapshot_mode) + pr_debug2("%sx snapshot size: %zu\n", ARM_SPE_PMU_NAME, + opts->auxtrace_snapshot_size); /* * To obtain the auxtrace buffer file descriptor, the auxtrace event @@ -166,8 +251,199 @@ static int arm_spe_recording_options(struct auxtrace_record *itr, tracking_evsel->core.attr.sample_period = 1; /* In per-cpu case, always need the time of mmap events etc */ - if (!perf_cpu_map__empty(cpus)) + if (!perf_cpu_map__empty(cpus)) { evsel__set_sample_bit(tracking_evsel, TIME); + evsel__set_sample_bit(tracking_evsel, CPU); + + /* also track task context switch */ + if (!record_opts__no_switch_events(opts)) + tracking_evsel->core.attr.context_switch = 1; + } + + return 0; +} + +static int arm_spe_parse_snapshot_options(struct auxtrace_record *itr __maybe_unused, + struct record_opts *opts, + const char *str) +{ + unsigned long long snapshot_size = 0; + char *endptr; + + if (str) { + snapshot_size = strtoull(str, &endptr, 0); + if (*endptr || snapshot_size > SIZE_MAX) + return -1; + } + + opts->auxtrace_snapshot_mode = true; + opts->auxtrace_snapshot_size = snapshot_size; + + return 0; +} + +static int arm_spe_snapshot_start(struct auxtrace_record *itr) +{ + struct arm_spe_recording *ptr = + container_of(itr, struct arm_spe_recording, itr); + struct evsel *evsel; + + evlist__for_each_entry(ptr->evlist, evsel) { + if (evsel->core.attr.type == ptr->arm_spe_pmu->type) + return evsel__disable(evsel); + } + return -EINVAL; +} + +static int arm_spe_snapshot_finish(struct auxtrace_record *itr) +{ + struct arm_spe_recording *ptr = + container_of(itr, struct arm_spe_recording, itr); + struct evsel *evsel; + + evlist__for_each_entry(ptr->evlist, evsel) { + if (evsel->core.attr.type == ptr->arm_spe_pmu->type) + return evsel__enable(evsel); + } + return -EINVAL; +} + +static int arm_spe_alloc_wrapped_array(struct arm_spe_recording *ptr, int idx) +{ + bool *wrapped; + int cnt = ptr->wrapped_cnt, new_cnt, i; + + /* + * No need to allocate, so return early. + */ + if (idx < cnt) + return 0; + + /* + * Make ptr->wrapped as big as idx. + */ + new_cnt = idx + 1; + + /* + * Free'ed in arm_spe_recording_free(). + */ + wrapped = reallocarray(ptr->wrapped, new_cnt, sizeof(bool)); + if (!wrapped) + return -ENOMEM; + + /* + * init new allocated values. + */ + for (i = cnt; i < new_cnt; i++) + wrapped[i] = false; + + ptr->wrapped_cnt = new_cnt; + ptr->wrapped = wrapped; + + return 0; +} + +static bool arm_spe_buffer_has_wrapped(unsigned char *buffer, + size_t buffer_size, u64 head) +{ + u64 i, watermark; + u64 *buf = (u64 *)buffer; + size_t buf_size = buffer_size; + + /* + * Defensively handle the case where head might be continually increasing - if its value is + * equal or greater than the size of the ring buffer, then we can safely determine it has + * wrapped around. Otherwise, continue to detect if head might have wrapped. + */ + if (head >= buffer_size) + return true; + + /* + * We want to look the very last 512 byte (chosen arbitrarily) in the ring buffer. + */ + watermark = buf_size - 512; + + /* + * The value of head is somewhere within the size of the ring buffer. This can be that there + * hasn't been enough data to fill the ring buffer yet or the trace time was so long that + * head has numerically wrapped around. To find we need to check if we have data at the + * very end of the ring buffer. We can reliably do this because mmap'ed pages are zeroed + * out and there is a fresh mapping with every new session. + */ + + /* + * head is less than 512 byte from the end of the ring buffer. + */ + if (head > watermark) + watermark = head; + + /* + * Speed things up by using 64 bit transactions (see "u64 *buf" above) + */ + watermark /= sizeof(u64); + buf_size /= sizeof(u64); + + /* + * If we find trace data at the end of the ring buffer, head has been there and has + * numerically wrapped around at least once. + */ + for (i = watermark; i < buf_size; i++) + if (buf[i]) + return true; + + return false; +} + +static int arm_spe_find_snapshot(struct auxtrace_record *itr, int idx, + struct auxtrace_mmap *mm, unsigned char *data, + u64 *head, u64 *old) +{ + int err; + bool wrapped; + struct arm_spe_recording *ptr = + container_of(itr, struct arm_spe_recording, itr); + + /* + * Allocate memory to keep track of wrapping if this is the first + * time we deal with this *mm. + */ + if (idx >= ptr->wrapped_cnt) { + err = arm_spe_alloc_wrapped_array(ptr, idx); + if (err) + return err; + } + + /* + * Check to see if *head has wrapped around. If it hasn't only the + * amount of data between *head and *old is snapshot'ed to avoid + * bloating the perf.data file with zeros. But as soon as *head has + * wrapped around the entire size of the AUX ring buffer it taken. + */ + wrapped = ptr->wrapped[idx]; + if (!wrapped && arm_spe_buffer_has_wrapped(data, mm->len, *head)) { + wrapped = true; + ptr->wrapped[idx] = true; + } + + pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n", + __func__, idx, (size_t)*old, (size_t)*head, mm->len); + + /* + * No wrap has occurred, we can just use *head and *old. + */ + if (!wrapped) + return 0; + + /* + * *head has wrapped around - adjust *head and *old to pickup the + * entire content of the AUX buffer. + */ + if (*head >= mm->len) { + *old = *head - mm->len; + } else { + *head += mm->len; + *old = *head - mm->len; + } return 0; } @@ -186,6 +462,7 @@ static void arm_spe_recording_free(struct auxtrace_record *itr) struct arm_spe_recording *sper = container_of(itr, struct arm_spe_recording, itr); + free(sper->wrapped); free(sper); } @@ -207,6 +484,10 @@ struct auxtrace_record *arm_spe_recording_init(int *err, sper->arm_spe_pmu = arm_spe_pmu; sper->itr.pmu = arm_spe_pmu; + sper->itr.snapshot_start = arm_spe_snapshot_start; + sper->itr.snapshot_finish = arm_spe_snapshot_finish; + sper->itr.find_snapshot = arm_spe_find_snapshot; + sper->itr.parse_snapshot_options = arm_spe_parse_snapshot_options; sper->itr.recording_options = arm_spe_recording_options; sper->itr.info_priv_size = arm_spe_info_priv_size; sper->itr.info_fill = arm_spe_info_fill; diff --git a/tools/perf/arch/arm64/util/machine.c b/tools/perf/arch/arm64/util/machine.c index 7e7714290a..d2ce31e28c 100644 --- a/tools/perf/arch/arm64/util/machine.c +++ b/tools/perf/arch/arm64/util/machine.c @@ -5,6 +5,8 @@ #include #include "debug.h" #include "symbol.h" +#include "callchain.h" +#include "record.h" /* On arm64, kernel text segment starts at high memory address, * for example 0xffff 0000 8xxx xxxx. Modules start at a low memory @@ -26,3 +28,8 @@ void arch__symbols__fixup_end(struct symbol *p, struct symbol *c) p->end = c->start; pr_debug4("%s sym:%s end:%#" PRIx64 "\n", __func__, p->name, p->end); } + +void arch__add_leaf_frame_record_opts(struct record_opts *opts) +{ + opts->sample_user_regs |= sample_reg_masks[PERF_REG_ARM64_LR].mask; +} diff --git a/tools/perf/arch/arm64/util/pmu.c b/tools/perf/arch/arm64/util/pmu.c index 2234fbd0a9..79124bba71 100644 --- a/tools/perf/arch/arm64/util/pmu.c +++ b/tools/perf/arch/arm64/util/pmu.c @@ -3,7 +3,7 @@ #include "../../../util/cpumap.h" #include "../../../util/pmu.h" -struct pmu_events_map *pmu_events_map__find(void) +const struct pmu_events_map *pmu_events_map__find(void) { struct perf_pmu *pmu = NULL; @@ -15,7 +15,7 @@ struct pmu_events_map *pmu_events_map__find(void) * The cpumap should cover all CPUs. Otherwise, some CPUs may * not support some events or have different event IDs. */ - if (pmu->cpus->nr != cpu__max_cpu()) + if (pmu->cpus->nr != cpu__max_cpu().cpu) return NULL; return perf_pmu__find_map(pmu); diff --git a/tools/perf/arch/csky/include/perf_regs.h b/tools/perf/arch/csky/include/perf_regs.h index 25ac3bdcb9..1afcc0e916 100644 --- a/tools/perf/arch/csky/include/perf_regs.h +++ b/tools/perf/arch/csky/include/perf_regs.h @@ -15,86 +15,4 @@ #define PERF_REG_IP PERF_REG_CSKY_PC #define PERF_REG_SP PERF_REG_CSKY_SP -static inline const char *__perf_reg_name(int id) -{ - switch (id) { - case PERF_REG_CSKY_A0: - return "a0"; - case PERF_REG_CSKY_A1: - return "a1"; - case PERF_REG_CSKY_A2: - return "a2"; - case PERF_REG_CSKY_A3: - return "a3"; - case PERF_REG_CSKY_REGS0: - return "regs0"; - case PERF_REG_CSKY_REGS1: - return "regs1"; - case PERF_REG_CSKY_REGS2: - return "regs2"; - case PERF_REG_CSKY_REGS3: - return "regs3"; - case PERF_REG_CSKY_REGS4: - return "regs4"; - case PERF_REG_CSKY_REGS5: - return "regs5"; - case PERF_REG_CSKY_REGS6: - return "regs6"; - case PERF_REG_CSKY_REGS7: - return "regs7"; - case PERF_REG_CSKY_REGS8: - return "regs8"; - case PERF_REG_CSKY_REGS9: - return "regs9"; - case PERF_REG_CSKY_SP: - return "sp"; - case PERF_REG_CSKY_LR: - return "lr"; - case PERF_REG_CSKY_PC: - return "pc"; -#if defined(__CSKYABIV2__) - case PERF_REG_CSKY_EXREGS0: - return "exregs0"; - case PERF_REG_CSKY_EXREGS1: - return "exregs1"; - case PERF_REG_CSKY_EXREGS2: - return "exregs2"; - case PERF_REG_CSKY_EXREGS3: - return "exregs3"; - case PERF_REG_CSKY_EXREGS4: - return "exregs4"; - case PERF_REG_CSKY_EXREGS5: - return "exregs5"; - case PERF_REG_CSKY_EXREGS6: - return "exregs6"; - case PERF_REG_CSKY_EXREGS7: - return "exregs7"; - case PERF_REG_CSKY_EXREGS8: - return "exregs8"; - case PERF_REG_CSKY_EXREGS9: - return "exregs9"; - case PERF_REG_CSKY_EXREGS10: - return "exregs10"; - case PERF_REG_CSKY_EXREGS11: - return "exregs11"; - case PERF_REG_CSKY_EXREGS12: - return "exregs12"; - case PERF_REG_CSKY_EXREGS13: - return "exregs13"; - case PERF_REG_CSKY_EXREGS14: - return "exregs14"; - case PERF_REG_CSKY_TLS: - return "tls"; - case PERF_REG_CSKY_HI: - return "hi"; - case PERF_REG_CSKY_LO: - return "lo"; -#endif - default: - return NULL; - } - - return NULL; -} - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl b/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl index 1ca7bc3379..3f1886ad9d 100644 --- a/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl +++ b/tools/perf/arch/mips/entry/syscalls/syscall_n64.tbl @@ -363,3 +363,5 @@ 446 n64 landlock_restrict_self sys_landlock_restrict_self # 447 reserved for memfd_secret 448 n64 process_mrelease sys_process_mrelease +449 n64 futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node diff --git a/tools/perf/arch/mips/include/perf_regs.h b/tools/perf/arch/mips/include/perf_regs.h index ee73b36a14..b8cd8bbb37 100644 --- a/tools/perf/arch/mips/include/perf_regs.h +++ b/tools/perf/arch/mips/include/perf_regs.h @@ -12,73 +12,4 @@ #define PERF_REGS_MASK ((1ULL << PERF_REG_MIPS_MAX) - 1) -static inline const char *__perf_reg_name(int id) -{ - switch (id) { - case PERF_REG_MIPS_PC: - return "PC"; - case PERF_REG_MIPS_R1: - return "$1"; - case PERF_REG_MIPS_R2: - return "$2"; - case PERF_REG_MIPS_R3: - return "$3"; - case PERF_REG_MIPS_R4: - return "$4"; - case PERF_REG_MIPS_R5: - return "$5"; - case PERF_REG_MIPS_R6: - return "$6"; - case PERF_REG_MIPS_R7: - return "$7"; - case PERF_REG_MIPS_R8: - return "$8"; - case PERF_REG_MIPS_R9: - return "$9"; - case PERF_REG_MIPS_R10: - return "$10"; - case PERF_REG_MIPS_R11: - return "$11"; - case PERF_REG_MIPS_R12: - return "$12"; - case PERF_REG_MIPS_R13: - return "$13"; - case PERF_REG_MIPS_R14: - return "$14"; - case PERF_REG_MIPS_R15: - return "$15"; - case PERF_REG_MIPS_R16: - return "$16"; - case PERF_REG_MIPS_R17: - return "$17"; - case PERF_REG_MIPS_R18: - return "$18"; - case PERF_REG_MIPS_R19: - return "$19"; - case PERF_REG_MIPS_R20: - return "$20"; - case PERF_REG_MIPS_R21: - return "$21"; - case PERF_REG_MIPS_R22: - return "$22"; - case PERF_REG_MIPS_R23: - return "$23"; - case PERF_REG_MIPS_R24: - return "$24"; - case PERF_REG_MIPS_R25: - return "$25"; - case PERF_REG_MIPS_R28: - return "$28"; - case PERF_REG_MIPS_R29: - return "$29"; - case PERF_REG_MIPS_R30: - return "$30"; - case PERF_REG_MIPS_R31: - return "$31"; - default: - break; - } - return NULL; -} - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl index 7bef917cc8..2600b42372 100644 --- a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl @@ -528,3 +528,5 @@ 446 common landlock_restrict_self sys_landlock_restrict_self # 447 reserved for memfd_secret 448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 nospu set_mempolicy_home_node sys_set_mempolicy_home_node diff --git a/tools/perf/arch/powerpc/include/arch-tests.h b/tools/perf/arch/powerpc/include/arch-tests.h index c625380524..452b3d9045 100644 --- a/tools/perf/arch/powerpc/include/arch-tests.h +++ b/tools/perf/arch/powerpc/include/arch-tests.h @@ -2,6 +2,6 @@ #ifndef ARCH_TESTS_H #define ARCH_TESTS_H -extern struct test arch_tests[]; +extern struct test_suite *arch_tests[]; #endif diff --git a/tools/perf/arch/powerpc/include/perf_regs.h b/tools/perf/arch/powerpc/include/perf_regs.h index 04e5dc07e9..9bb17c3f37 100644 --- a/tools/perf/arch/powerpc/include/perf_regs.h +++ b/tools/perf/arch/powerpc/include/perf_regs.h @@ -19,68 +19,4 @@ void perf_regs_load(u64 *regs); #define PERF_REG_IP PERF_REG_POWERPC_NIP #define PERF_REG_SP PERF_REG_POWERPC_R1 -static const char *reg_names[] = { - [PERF_REG_POWERPC_R0] = "r0", - [PERF_REG_POWERPC_R1] = "r1", - [PERF_REG_POWERPC_R2] = "r2", - [PERF_REG_POWERPC_R3] = "r3", - [PERF_REG_POWERPC_R4] = "r4", - [PERF_REG_POWERPC_R5] = "r5", - [PERF_REG_POWERPC_R6] = "r6", - [PERF_REG_POWERPC_R7] = "r7", - [PERF_REG_POWERPC_R8] = "r8", - [PERF_REG_POWERPC_R9] = "r9", - [PERF_REG_POWERPC_R10] = "r10", - [PERF_REG_POWERPC_R11] = "r11", - [PERF_REG_POWERPC_R12] = "r12", - [PERF_REG_POWERPC_R13] = "r13", - [PERF_REG_POWERPC_R14] = "r14", - [PERF_REG_POWERPC_R15] = "r15", - [PERF_REG_POWERPC_R16] = "r16", - [PERF_REG_POWERPC_R17] = "r17", - [PERF_REG_POWERPC_R18] = "r18", - [PERF_REG_POWERPC_R19] = "r19", - [PERF_REG_POWERPC_R20] = "r20", - [PERF_REG_POWERPC_R21] = "r21", - [PERF_REG_POWERPC_R22] = "r22", - [PERF_REG_POWERPC_R23] = "r23", - [PERF_REG_POWERPC_R24] = "r24", - [PERF_REG_POWERPC_R25] = "r25", - [PERF_REG_POWERPC_R26] = "r26", - [PERF_REG_POWERPC_R27] = "r27", - [PERF_REG_POWERPC_R28] = "r28", - [PERF_REG_POWERPC_R29] = "r29", - [PERF_REG_POWERPC_R30] = "r30", - [PERF_REG_POWERPC_R31] = "r31", - [PERF_REG_POWERPC_NIP] = "nip", - [PERF_REG_POWERPC_MSR] = "msr", - [PERF_REG_POWERPC_ORIG_R3] = "orig_r3", - [PERF_REG_POWERPC_CTR] = "ctr", - [PERF_REG_POWERPC_LINK] = "link", - [PERF_REG_POWERPC_XER] = "xer", - [PERF_REG_POWERPC_CCR] = "ccr", - [PERF_REG_POWERPC_SOFTE] = "softe", - [PERF_REG_POWERPC_TRAP] = "trap", - [PERF_REG_POWERPC_DAR] = "dar", - [PERF_REG_POWERPC_DSISR] = "dsisr", - [PERF_REG_POWERPC_SIER] = "sier", - [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", - [PERF_REG_POWERPC_PMC1] = "pmc1", - [PERF_REG_POWERPC_PMC2] = "pmc2", - [PERF_REG_POWERPC_PMC3] = "pmc3", - [PERF_REG_POWERPC_PMC4] = "pmc4", - [PERF_REG_POWERPC_PMC5] = "pmc5", - [PERF_REG_POWERPC_PMC6] = "pmc6", -}; - -static inline const char *__perf_reg_name(int id) -{ - return reg_names[id]; -} #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/powerpc/tests/arch-tests.c b/tools/perf/arch/powerpc/tests/arch-tests.c index 8c3fbd4af8..eb98c57b5a 100644 --- a/tools/perf/arch/powerpc/tests/arch-tests.c +++ b/tools/perf/arch/powerpc/tests/arch-tests.c @@ -3,14 +3,10 @@ #include "tests/tests.h" #include "arch-tests.h" -struct test arch_tests[] = { + +struct test_suite *arch_tests[] = { #ifdef HAVE_DWARF_UNWIND_SUPPORT - { - .desc = "Test dwarf unwind", - .func = test__dwarf_unwind, - }, + &suite__dwarf_unwind, #endif - { - .func = NULL, - }, + NULL, }; diff --git a/tools/perf/arch/powerpc/util/event.c b/tools/perf/arch/powerpc/util/event.c index 3bf4412574..cf430a4c55 100644 --- a/tools/perf/arch/powerpc/util/event.c +++ b/tools/perf/arch/powerpc/util/event.c @@ -40,8 +40,12 @@ const char *arch_perf_header_entry(const char *se_header) { if (!strcmp(se_header, "Local INSTR Latency")) return "Finish Cyc"; - else if (!strcmp(se_header, "Pipeline Stage Cycle")) + else if (!strcmp(se_header, "INSTR Latency")) + return "Global Finish_cyc"; + else if (!strcmp(se_header, "Local Pipeline Stage Cycle")) return "Dispatch Cyc"; + else if (!strcmp(se_header, "Pipeline Stage Cycle")) + return "Global Dispatch_cyc"; return se_header; } @@ -49,5 +53,7 @@ int arch_support_sort_key(const char *sort_key) { if (!strcmp(sort_key, "p_stage_cyc")) return 1; + if (!strcmp(sort_key, "local_p_stage_cyc")) + return 1; return 0; } diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c index 58b2d610aa..e8fe36b10d 100644 --- a/tools/perf/arch/powerpc/util/header.c +++ b/tools/perf/arch/powerpc/util/header.c @@ -40,7 +40,7 @@ get_cpuid_str(struct perf_pmu *pmu __maybe_unused) return bufp; } -int arch_get_runtimeparam(struct pmu_event *pe) +int arch_get_runtimeparam(const struct pmu_event *pe) { int count; char path[PATH_MAX] = "/devices/hv_24x7/interface/"; diff --git a/tools/perf/arch/powerpc/util/kvm-stat.c b/tools/perf/arch/powerpc/util/kvm-stat.c index 16510686c1..1a9b40ea92 100644 --- a/tools/perf/arch/powerpc/util/kvm-stat.c +++ b/tools/perf/arch/powerpc/util/kvm-stat.c @@ -113,10 +113,11 @@ static int is_tracepoint_available(const char *str, struct evlist *evlist) struct parse_events_error err; int ret; - bzero(&err, sizeof(err)); + parse_events_error__init(&err); ret = parse_events(evlist, str, &err); if (err.str) - parse_events_print_error(&err, "tracepoint"); + parse_events_error__print(&err, "tracepoint"); + parse_events_error__exit(&err); return ret; } diff --git a/tools/perf/arch/powerpc/util/perf_regs.c b/tools/perf/arch/powerpc/util/perf_regs.c index 8116a253f9..8d07a78e74 100644 --- a/tools/perf/arch/powerpc/util/perf_regs.c +++ b/tools/perf/arch/powerpc/util/perf_regs.c @@ -74,6 +74,8 @@ const struct sample_reg sample_reg_masks[] = { SMPL_REG(pmc4, PERF_REG_POWERPC_PMC4), SMPL_REG(pmc5, PERF_REG_POWERPC_PMC5), SMPL_REG(pmc6, PERF_REG_POWERPC_PMC6), + SMPL_REG(sdar, PERF_REG_POWERPC_SDAR), + SMPL_REG(siar, PERF_REG_POWERPC_SIAR), SMPL_REG_END }; diff --git a/tools/perf/arch/riscv/include/perf_regs.h b/tools/perf/arch/riscv/include/perf_regs.h index 6b02a767c9..6944bf0de5 100644 --- a/tools/perf/arch/riscv/include/perf_regs.h +++ b/tools/perf/arch/riscv/include/perf_regs.h @@ -19,78 +19,4 @@ #define PERF_REG_IP PERF_REG_RISCV_PC #define PERF_REG_SP PERF_REG_RISCV_SP -static inline const char *__perf_reg_name(int id) -{ - switch (id) { - case PERF_REG_RISCV_PC: - return "pc"; - case PERF_REG_RISCV_RA: - return "ra"; - case PERF_REG_RISCV_SP: - return "sp"; - case PERF_REG_RISCV_GP: - return "gp"; - case PERF_REG_RISCV_TP: - return "tp"; - case PERF_REG_RISCV_T0: - return "t0"; - case PERF_REG_RISCV_T1: - return "t1"; - case PERF_REG_RISCV_T2: - return "t2"; - case PERF_REG_RISCV_S0: - return "s0"; - case PERF_REG_RISCV_S1: - return "s1"; - case PERF_REG_RISCV_A0: - return "a0"; - case PERF_REG_RISCV_A1: - return "a1"; - case PERF_REG_RISCV_A2: - return "a2"; - case PERF_REG_RISCV_A3: - return "a3"; - case PERF_REG_RISCV_A4: - return "a4"; - case PERF_REG_RISCV_A5: - return "a5"; - case PERF_REG_RISCV_A6: - return "a6"; - case PERF_REG_RISCV_A7: - return "a7"; - case PERF_REG_RISCV_S2: - return "s2"; - case PERF_REG_RISCV_S3: - return "s3"; - case PERF_REG_RISCV_S4: - return "s4"; - case PERF_REG_RISCV_S5: - return "s5"; - case PERF_REG_RISCV_S6: - return "s6"; - case PERF_REG_RISCV_S7: - return "s7"; - case PERF_REG_RISCV_S8: - return "s8"; - case PERF_REG_RISCV_S9: - return "s9"; - case PERF_REG_RISCV_S10: - return "s10"; - case PERF_REG_RISCV_S11: - return "s11"; - case PERF_REG_RISCV_T3: - return "t3"; - case PERF_REG_RISCV_T4: - return "t4"; - case PERF_REG_RISCV_T5: - return "t5"; - case PERF_REG_RISCV_T6: - return "t6"; - default: - return NULL; - } - - return NULL; -} - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/s390/entry/syscalls/syscall.tbl b/tools/perf/arch/s390/entry/syscalls/syscall.tbl index df5261e5cf..799147658d 100644 --- a/tools/perf/arch/s390/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/s390/entry/syscalls/syscall.tbl @@ -451,3 +451,5 @@ 446 common landlock_restrict_self sys_landlock_restrict_self sys_landlock_restrict_self # 447 reserved for memfd_secret 448 common process_mrelease sys_process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node sys_set_mempolicy_home_node diff --git a/tools/perf/arch/s390/include/perf_regs.h b/tools/perf/arch/s390/include/perf_regs.h index ce30315266..52fcc0891d 100644 --- a/tools/perf/arch/s390/include/perf_regs.h +++ b/tools/perf/arch/s390/include/perf_regs.h @@ -14,82 +14,4 @@ void perf_regs_load(u64 *regs); #define PERF_REG_IP PERF_REG_S390_PC #define PERF_REG_SP PERF_REG_S390_R15 -static inline const char *__perf_reg_name(int id) -{ - switch (id) { - case PERF_REG_S390_R0: - return "R0"; - case PERF_REG_S390_R1: - return "R1"; - case PERF_REG_S390_R2: - return "R2"; - case PERF_REG_S390_R3: - return "R3"; - case PERF_REG_S390_R4: - return "R4"; - case PERF_REG_S390_R5: - return "R5"; - case PERF_REG_S390_R6: - return "R6"; - case PERF_REG_S390_R7: - return "R7"; - case PERF_REG_S390_R8: - return "R8"; - case PERF_REG_S390_R9: - return "R9"; - case PERF_REG_S390_R10: - return "R10"; - case PERF_REG_S390_R11: - return "R11"; - case PERF_REG_S390_R12: - return "R12"; - case PERF_REG_S390_R13: - return "R13"; - case PERF_REG_S390_R14: - return "R14"; - case PERF_REG_S390_R15: - return "R15"; - case PERF_REG_S390_FP0: - return "FP0"; - case PERF_REG_S390_FP1: - return "FP1"; - case PERF_REG_S390_FP2: - return "FP2"; - case PERF_REG_S390_FP3: - return "FP3"; - case PERF_REG_S390_FP4: - return "FP4"; - case PERF_REG_S390_FP5: - return "FP5"; - case PERF_REG_S390_FP6: - return "FP6"; - case PERF_REG_S390_FP7: - return "FP7"; - case PERF_REG_S390_FP8: - return "FP8"; - case PERF_REG_S390_FP9: - return "FP9"; - case PERF_REG_S390_FP10: - return "FP10"; - case PERF_REG_S390_FP11: - return "FP11"; - case PERF_REG_S390_FP12: - return "FP12"; - case PERF_REG_S390_FP13: - return "FP13"; - case PERF_REG_S390_FP14: - return "FP14"; - case PERF_REG_S390_FP15: - return "FP15"; - case PERF_REG_S390_MASK: - return "MASK"; - case PERF_REG_S390_PC: - return "PC"; - default: - return NULL; - } - - return NULL; -} - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/x86/annotate/instructions.c b/tools/perf/arch/x86/annotate/instructions.c index 24ea12ec7e..305872692b 100644 --- a/tools/perf/arch/x86/annotate/instructions.c +++ b/tools/perf/arch/x86/annotate/instructions.c @@ -144,8 +144,31 @@ static struct ins x86__instructions[] = { { .name = "xorps", .ops = &mov_ops, }, }; -static bool x86__ins_is_fused(struct arch *arch, const char *ins1, +static bool amd__ins_is_fused(struct arch *arch, const char *ins1, const char *ins2) +{ + if (strstr(ins2, "jmp")) + return false; + + /* Family >= 15h supports cmp/test + branch fusion */ + if (arch->family >= 0x15 && (strstarts(ins1, "test") || + (strstarts(ins1, "cmp") && !strstr(ins1, "xchg")))) { + return true; + } + + /* Family >= 19h supports some ALU + branch fusion */ + if (arch->family >= 0x19 && (strstarts(ins1, "add") || + strstarts(ins1, "sub") || strstarts(ins1, "and") || + strstarts(ins1, "inc") || strstarts(ins1, "dec") || + strstarts(ins1, "or") || strstarts(ins1, "xor"))) { + return true; + } + + return false; +} + +static bool intel__ins_is_fused(struct arch *arch, const char *ins1, + const char *ins2) { if (arch->family != 6 || arch->model < 0x1e || strstr(ins2, "jmp")) return false; @@ -184,6 +207,9 @@ static int x86__cpuid_parse(struct arch *arch, char *cpuid) if (ret == 3) { arch->family = family; arch->model = model; + arch->ins_is_fused = strstarts(cpuid, "AuthenticAMD") ? + amd__ins_is_fused : + intel__ins_is_fused; return 0; } diff --git a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl index 18b5500ea8..c84d12608c 100644 --- a/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl +++ b/tools/perf/arch/x86/entry/syscalls/syscall_64.tbl @@ -370,6 +370,8 @@ 446 common landlock_restrict_self sys_landlock_restrict_self 447 common memfd_secret sys_memfd_secret 448 common process_mrelease sys_process_mrelease +449 common futex_waitv sys_futex_waitv +450 common set_mempolicy_home_node sys_set_mempolicy_home_node # # Due to a historical design error, certain syscalls are numbered differently diff --git a/tools/perf/arch/x86/include/arch-tests.h b/tools/perf/arch/x86/include/arch-tests.h index 9599e7a3f1..6a1a1b3c08 100644 --- a/tools/perf/arch/x86/include/arch-tests.h +++ b/tools/perf/arch/x86/include/arch-tests.h @@ -2,15 +2,15 @@ #ifndef ARCH_TESTS_H #define ARCH_TESTS_H -struct test; +struct test_suite; /* Tests */ -int test__rdpmc(struct test *test, int subtest); -int test__insn_x86(struct test *test, int subtest); -int test__intel_pt_pkt_decoder(struct test *test, int subtest); -int test__bp_modify(struct test *test, int subtest); -int test__x86_sample_parsing(struct test *test, int subtest); +int test__rdpmc(struct test_suite *test, int subtest); +int test__insn_x86(struct test_suite *test, int subtest); +int test__intel_pt_pkt_decoder(struct test_suite *test, int subtest); +int test__bp_modify(struct test_suite *test, int subtest); +int test__x86_sample_parsing(struct test_suite *test, int subtest); -extern struct test arch_tests[]; +extern struct test_suite *arch_tests[]; #endif diff --git a/tools/perf/arch/x86/include/perf_regs.h b/tools/perf/arch/x86/include/perf_regs.h index cddc4cdc0d..16e23b7220 100644 --- a/tools/perf/arch/x86/include/perf_regs.h +++ b/tools/perf/arch/x86/include/perf_regs.h @@ -23,86 +23,4 @@ void perf_regs_load(u64 *regs); #define PERF_REG_IP PERF_REG_X86_IP #define PERF_REG_SP PERF_REG_X86_SP -static inline const char *__perf_reg_name(int id) -{ - switch (id) { - case PERF_REG_X86_AX: - return "AX"; - case PERF_REG_X86_BX: - return "BX"; - case PERF_REG_X86_CX: - return "CX"; - case PERF_REG_X86_DX: - return "DX"; - case PERF_REG_X86_SI: - return "SI"; - case PERF_REG_X86_DI: - return "DI"; - case PERF_REG_X86_BP: - return "BP"; - case PERF_REG_X86_SP: - return "SP"; - case PERF_REG_X86_IP: - return "IP"; - case PERF_REG_X86_FLAGS: - return "FLAGS"; - case PERF_REG_X86_CS: - return "CS"; - case PERF_REG_X86_SS: - return "SS"; - case PERF_REG_X86_DS: - return "DS"; - case PERF_REG_X86_ES: - return "ES"; - case PERF_REG_X86_FS: - return "FS"; - case PERF_REG_X86_GS: - return "GS"; -#ifdef HAVE_ARCH_X86_64_SUPPORT - case PERF_REG_X86_R8: - return "R8"; - case PERF_REG_X86_R9: - return "R9"; - case PERF_REG_X86_R10: - return "R10"; - case PERF_REG_X86_R11: - return "R11"; - case PERF_REG_X86_R12: - return "R12"; - case PERF_REG_X86_R13: - return "R13"; - case PERF_REG_X86_R14: - return "R14"; - case PERF_REG_X86_R15: - return "R15"; -#endif /* HAVE_ARCH_X86_64_SUPPORT */ - -#define XMM(x) \ - case PERF_REG_X86_XMM ## x: \ - case PERF_REG_X86_XMM ## x + 1: \ - return "XMM" #x; - XMM(0) - XMM(1) - XMM(2) - XMM(3) - XMM(4) - XMM(5) - XMM(6) - XMM(7) - XMM(8) - XMM(9) - XMM(10) - XMM(11) - XMM(12) - XMM(13) - XMM(14) - XMM(15) -#undef XMM - default: - return NULL; - } - - return NULL; -} - #endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/x86/tests/arch-tests.c b/tools/perf/arch/x86/tests/arch-tests.c index 71aa67367a..64fb73d14d 100644 --- a/tools/perf/arch/x86/tests/arch-tests.c +++ b/tools/perf/arch/x86/tests/arch-tests.c @@ -3,39 +3,28 @@ #include "tests/tests.h" #include "arch-tests.h" -struct test arch_tests[] = { - { - .desc = "x86 rdpmc", - .func = test__rdpmc, - }, -#ifdef HAVE_DWARF_UNWIND_SUPPORT - { - .desc = "DWARF unwind", - .func = test__dwarf_unwind, - }, -#endif +DEFINE_SUITE("x86 rdpmc", rdpmc); #ifdef HAVE_AUXTRACE_SUPPORT - { - .desc = "x86 instruction decoder - new instructions", - .func = test__insn_x86, - }, - { - .desc = "Intel PT packet decoder", - .func = test__intel_pt_pkt_decoder, - }, +DEFINE_SUITE("x86 instruction decoder - new instructions", insn_x86); +DEFINE_SUITE("Intel PT packet decoder", intel_pt_pkt_decoder); #endif #if defined(__x86_64__) - { - .desc = "x86 bp modify", - .func = test__bp_modify, - }, +DEFINE_SUITE("x86 bp modify", bp_modify); #endif - { - .desc = "x86 Sample parsing", - .func = test__x86_sample_parsing, - }, - { - .func = NULL, - }, +DEFINE_SUITE("x86 Sample parsing", x86_sample_parsing); +struct test_suite *arch_tests[] = { + &suite__rdpmc, +#ifdef HAVE_DWARF_UNWIND_SUPPORT + &suite__dwarf_unwind, +#endif +#ifdef HAVE_AUXTRACE_SUPPORT + &suite__insn_x86, + &suite__intel_pt_pkt_decoder, +#endif +#if defined(__x86_64__) + &suite__bp_modify, +#endif + &suite__x86_sample_parsing, + NULL, }; diff --git a/tools/perf/arch/x86/tests/bp-modify.c b/tools/perf/arch/x86/tests/bp-modify.c index dffcf9b521..0924ccd9e3 100644 --- a/tools/perf/arch/x86/tests/bp-modify.c +++ b/tools/perf/arch/x86/tests/bp-modify.c @@ -204,7 +204,7 @@ static int bp_modify2(void) return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL; } -int test__bp_modify(struct test *test __maybe_unused, +int test__bp_modify(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1()); diff --git a/tools/perf/arch/x86/tests/insn-x86.c b/tools/perf/arch/x86/tests/insn-x86.c index 0262b0d8cc..94b490c434 100644 --- a/tools/perf/arch/x86/tests/insn-x86.c +++ b/tools/perf/arch/x86/tests/insn-x86.c @@ -173,7 +173,7 @@ static int test_data_set(struct test_data *dat_set, int x86_64) * verbose (-v) option to see all the instructions and whether or not they * decoded successfully. */ -int test__insn_x86(struct test *test __maybe_unused, int subtest __maybe_unused) +int test__insn_x86(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { int ret = 0; diff --git a/tools/perf/arch/x86/tests/intel-cqm.c b/tools/perf/arch/x86/tests/intel-cqm.c index 27dd8cf9e0..cb5b2c6c3b 100644 --- a/tools/perf/arch/x86/tests/intel-cqm.c +++ b/tools/perf/arch/x86/tests/intel-cqm.c @@ -37,7 +37,7 @@ static pid_t spawn(void) * the last read counter value to avoid triggering a WARN_ON_ONCE() in * smp_call_function_many() caused by sending IPIs from NMI context. */ -int test__intel_cqm_count_nmi_context(struct test *test __maybe_unused, int subtest __maybe_unused) +int test__intel_cqm_count_nmi_context(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { struct evlist *evlist = NULL; struct evsel *evsel = NULL; diff --git a/tools/perf/arch/x86/tests/intel-pt-pkt-decoder-test.c b/tools/perf/arch/x86/tests/intel-pt-pkt-decoder-test.c index c933e3dcd0..2fc882ab24 100644 --- a/tools/perf/arch/x86/tests/intel-pt-pkt-decoder-test.c +++ b/tools/perf/arch/x86/tests/intel-pt-pkt-decoder-test.c @@ -289,7 +289,7 @@ static int test_one(struct test_data *d) * This test feeds byte sequences to the Intel PT packet decoder and checks the * results. Changes to the packet context are also checked. */ -int test__intel_pt_pkt_decoder(struct test *test __maybe_unused, int subtest __maybe_unused) +int test__intel_pt_pkt_decoder(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { struct test_data *d = data; int ret; diff --git a/tools/perf/arch/x86/tests/rdpmc.c b/tools/perf/arch/x86/tests/rdpmc.c index 1ea916656a..498413ad9c 100644 --- a/tools/perf/arch/x86/tests/rdpmc.c +++ b/tools/perf/arch/x86/tests/rdpmc.c @@ -157,7 +157,7 @@ static int __test__rdpmc(void) return 0; } -int test__rdpmc(struct test *test __maybe_unused, int subtest __maybe_unused) +int test__rdpmc(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { int status = 0; int wret = 0; diff --git a/tools/perf/arch/x86/tests/sample-parsing.c b/tools/perf/arch/x86/tests/sample-parsing.c index c92db87e44..bfbd3662b6 100644 --- a/tools/perf/arch/x86/tests/sample-parsing.c +++ b/tools/perf/arch/x86/tests/sample-parsing.c @@ -115,7 +115,7 @@ static int do_test(u64 sample_type) * For now, the PERF_SAMPLE_WEIGHT_STRUCT is the only X86 specific sample type. * The test only checks the PERF_SAMPLE_WEIGHT_STRUCT type. */ -int test__x86_sample_parsing(struct test *test __maybe_unused, int subtest __maybe_unused) +int test__x86_sample_parsing(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { return do_test(PERF_SAMPLE_WEIGHT_STRUCT); } diff --git a/tools/perf/arch/x86/util/evlist.c b/tools/perf/arch/x86/util/evlist.c index 0b0951030a..8d9b559592 100644 --- a/tools/perf/arch/x86/util/evlist.c +++ b/tools/perf/arch/x86/util/evlist.c @@ -17,3 +17,20 @@ int arch_evlist__add_default_attrs(struct evlist *evlist) else return parse_events(evlist, TOPDOWN_L1_EVENTS, NULL); } + +struct evsel *arch_evlist__leader(struct list_head *list) +{ + struct evsel *evsel, *first; + + first = list_first_entry(list, struct evsel, core.node); + + if (!pmu_have_event("cpu", "slots")) + return first; + + __evlist__for_each_entry(list, evsel) { + if (evsel->pmu_name && !strcmp(evsel->pmu_name, "cpu") && + evsel->name && strcasestr(evsel->name, "slots")) + return evsel; + } + return first; +} diff --git a/tools/perf/arch/x86/util/evsel.c b/tools/perf/arch/x86/util/evsel.c index 2f733cdc8d..ac2899a25b 100644 --- a/tools/perf/arch/x86/util/evsel.c +++ b/tools/perf/arch/x86/util/evsel.c @@ -1,8 +1,31 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include "util/evsel.h" +#include "util/env.h" +#include "linux/string.h" void arch_evsel__set_sample_weight(struct evsel *evsel) { evsel__set_sample_bit(evsel, WEIGHT_STRUCT); } + +void arch_evsel__fixup_new_cycles(struct perf_event_attr *attr) +{ + struct perf_env env = { .total_mem = 0, } ; + + if (!perf_env__cpuid(&env)) + return; + + /* + * On AMD, precise cycles event sampling internally uses IBS pmu. + * But IBS does not have filtering capabilities and perf by default + * sets exclude_guest = 1. This makes IBS pmu event init fail and + * thus perf ends up doing non-precise sampling. Avoid it by clearing + * exclude_guest. + */ + if (env.cpuid && strstarts(env.cpuid, "AuthenticAMD")) + attr->exclude_guest = 0; + + free(env.cpuid); +} diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 05eb098cb0..490bb9b8cf 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -591,6 +591,10 @@ int cmd_annotate(int argc, const char **argv) return ret; } + ret = symbol__validate_sym_arguments(); + if (ret) + return ret; + if (quiet) perf_quiet_option(); diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index d0895162c2..d291f3a8af 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -226,7 +226,6 @@ static void run_collection(struct collection *coll) if (!bench->fn) break; printf("# Running %s/%s benchmark...\n", coll->name, bench->name); - fflush(stdout); argv[1] = bench->name; run_bench(coll->name, bench->name, bench->fn, 1, argv); @@ -247,6 +246,9 @@ int cmd_bench(int argc, const char **argv) struct collection *coll; int ret = 0; + /* Unbuffered output */ + setvbuf(stdout, NULL, _IONBF, 0); + if (argc < 2) { /* No collection specified. */ print_usage(); @@ -300,7 +302,6 @@ int cmd_bench(int argc, const char **argv) if (bench_format == BENCH_FORMAT_DEFAULT) printf("# Running '%s/%s' benchmark:\n", coll->name, bench->name); - fflush(stdout); ret = run_bench(coll->name, bench->name, bench->fn, argc-1, argv+1); goto end; } diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index 0db3cfc04c..cd38169365 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -351,10 +351,14 @@ static int build_id_cache__show_all(void) static int perf_buildid_cache_config(const char *var, const char *value, void *cb) { - const char **debuginfod = cb; + struct perf_debuginfod *di = cb; - if (!strcmp(var, "buildid-cache.debuginfod")) - *debuginfod = strdup(value); + if (!strcmp(var, "buildid-cache.debuginfod")) { + di->urls = strdup(value); + if (!di->urls) + return -ENOMEM; + di->set = true; + } return 0; } @@ -373,8 +377,8 @@ int cmd_buildid_cache(int argc, const char **argv) *purge_name_list_str = NULL, *missing_filename = NULL, *update_name_list_str = NULL, - *kcore_filename = NULL, - *debuginfod = NULL; + *kcore_filename = NULL; + struct perf_debuginfod debuginfod = { }; char sbuf[STRERR_BUFSIZE]; struct perf_data data = { @@ -399,8 +403,10 @@ int cmd_buildid_cache(int argc, const char **argv) OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), OPT_STRING('u', "update", &update_name_list_str, "file list", "file(s) to update"), - OPT_STRING(0, "debuginfod", &debuginfod, "debuginfod url", - "set debuginfod url"), + OPT_STRING_OPTARG_SET(0, "debuginfod", &debuginfod.urls, + &debuginfod.set, "debuginfod urls", + "Enable debuginfod data retrieval from DEBUGINFOD_URLS or specified urls", + "system"), OPT_INCR('v', "verbose", &verbose, "be more verbose"), OPT_INTEGER(0, "target-ns", &ns_id, "target pid for namespace context"), OPT_END() @@ -425,10 +431,7 @@ int cmd_buildid_cache(int argc, const char **argv) if (argc || !(list_files || opts_flag)) usage_with_options(buildid_cache_usage, buildid_cache_options); - if (debuginfod) { - pr_debug("DEBUGINFOD_URLS=%s\n", debuginfod); - setenv("DEBUGINFOD_URLS", debuginfod, 1); - } + perf_debuginfod_setup(&debuginfod); /* -l is exclusive. It can not be used with other options. */ if (list_files && opts_flag) { diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index a192014fa5..77dd4afacc 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -2015,7 +2015,8 @@ static int setup_nodes(struct perf_session *session) { struct numa_node *n; unsigned long **nodes; - int node, cpu; + int node, idx; + struct perf_cpu cpu; int *cpu2node; if (c2c.node_info > 2) @@ -2038,8 +2039,8 @@ static int setup_nodes(struct perf_session *session) if (!cpu2node) return -ENOMEM; - for (cpu = 0; cpu < c2c.cpus_cnt; cpu++) - cpu2node[cpu] = -1; + for (idx = 0; idx < c2c.cpus_cnt; idx++) + cpu2node[idx] = -1; c2c.cpu2node = cpu2node; @@ -2057,13 +2058,13 @@ static int setup_nodes(struct perf_session *session) if (perf_cpu_map__empty(map)) continue; - for (cpu = 0; cpu < map->nr; cpu++) { - set_bit(map->map[cpu], set); + perf_cpu_map__for_each_cpu(cpu, idx, map) { + set_bit(cpu.cpu, set); - if (WARN_ONCE(cpu2node[map->map[cpu]] != -1, "node/cpu topology bug")) + if (WARN_ONCE(cpu2node[cpu.cpu] != -1, "node/cpu topology bug")) return -EINVAL; - cpu2node[map->map[cpu]] = node; + cpu2node[cpu.cpu] = node; } } @@ -2768,6 +2769,10 @@ static int perf_c2c__report(int argc, const char **argv) if (c2c.stats_only) c2c.use_stdio = true; + err = symbol__validate_sym_arguments(); + if (err) + goto out; + if (!input_name || !strlen(input_name)) input_name = "perf.data"; diff --git a/tools/perf/builtin-daemon.c b/tools/perf/builtin-daemon.c index 61929f63a0..6cb3f6cc36 100644 --- a/tools/perf/builtin-daemon.c +++ b/tools/perf/builtin-daemon.c @@ -1121,8 +1121,6 @@ static int setup_config(struct daemon *daemon) #ifndef F_TLOCK #define F_TLOCK 2 -#include - static int lockf(int fd, int cmd, off_t len) { if (cmd != F_TLOCK || len != 0) @@ -1403,8 +1401,10 @@ static int send_cmd(struct daemon *daemon, union cmd *cmd) static int send_cmd_list(struct daemon *daemon) { - union cmd cmd = { .cmd = CMD_LIST, }; + union cmd cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.list.cmd = CMD_LIST; cmd.list.verbose = verbose; cmd.list.csv_sep = daemon->csv_sep ? *daemon->csv_sep : 0; @@ -1432,6 +1432,7 @@ static int __cmd_signal(struct daemon *daemon, struct option parent_options[], return -1; } + memset(&cmd, 0, sizeof(cmd)); cmd.signal.cmd = CMD_SIGNAL, cmd.signal.sig = SIGUSR2; strncpy(cmd.signal.name, name, sizeof(cmd.signal.name) - 1); @@ -1446,7 +1447,7 @@ static int __cmd_stop(struct daemon *daemon, struct option parent_options[], OPT_PARENT(parent_options), OPT_END() }; - union cmd cmd = { .cmd = CMD_STOP, }; + union cmd cmd; argc = parse_options(argc, argv, start_options, daemon_usage, 0); if (argc) @@ -1457,6 +1458,8 @@ static int __cmd_stop(struct daemon *daemon, struct option parent_options[], return -1; } + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd = CMD_STOP; return send_cmd(daemon, &cmd); } @@ -1470,7 +1473,7 @@ static int __cmd_ping(struct daemon *daemon, struct option parent_options[], OPT_PARENT(parent_options), OPT_END() }; - union cmd cmd = { .cmd = CMD_PING, }; + union cmd cmd; argc = parse_options(argc, argv, ping_options, daemon_usage, 0); if (argc) @@ -1481,6 +1484,8 @@ static int __cmd_ping(struct daemon *daemon, struct option parent_options[], return -1; } + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd = CMD_PING; scnprintf(cmd.ping.name, sizeof(cmd.ping.name), "%s", name); return send_cmd(daemon, &cmd); } diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index 87cb11a7a3..a8785dec5c 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include #include #include @@ -28,36 +30,12 @@ #include "strfilter.h" #include "util/cap.h" #include "util/config.h" +#include "util/ftrace.h" #include "util/units.h" #include "util/parse-sublevel-options.h" #define DEFAULT_TRACER "function_graph" -struct perf_ftrace { - struct evlist *evlist; - struct target target; - const char *tracer; - struct list_head filters; - struct list_head notrace; - struct list_head graph_funcs; - struct list_head nograph_funcs; - int graph_depth; - unsigned long percpu_buffer_size; - bool inherit; - int func_stack_trace; - int func_irq_info; - int graph_nosleep_time; - int graph_noirqs; - int graph_verbose; - int graph_thresh; - unsigned int initial_delay; -}; - -struct filter_entry { - struct list_head list; - char name[]; -}; - static volatile int workload_exec_errno; static bool done; @@ -303,7 +281,7 @@ static int set_tracing_cpumask(struct perf_cpu_map *cpumap) int ret; int last_cpu; - last_cpu = cpu_map__cpu(cpumap, cpumap->nr - 1); + last_cpu = perf_cpu_map__cpu(cpumap, perf_cpu_map__nr(cpumap) - 1).cpu; mask_size = last_cpu / 4 + 2; /* one more byte for EOS */ mask_size += last_cpu / 32; /* ',' is needed for every 32th cpus */ @@ -565,7 +543,24 @@ static int set_tracing_options(struct perf_ftrace *ftrace) return 0; } -static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv) +static void select_tracer(struct perf_ftrace *ftrace) +{ + bool graph = !list_empty(&ftrace->graph_funcs) || + !list_empty(&ftrace->nograph_funcs); + bool func = !list_empty(&ftrace->filters) || + !list_empty(&ftrace->notrace); + + /* The function_graph has priority over function tracer. */ + if (graph) + ftrace->tracer = "function_graph"; + else if (func) + ftrace->tracer = "function"; + /* Otherwise, the default tracer is used. */ + + pr_debug("%s tracer is used\n", ftrace->tracer); +} + +static int __cmd_ftrace(struct perf_ftrace *ftrace) { char *trace_file; int trace_fd; @@ -586,10 +581,7 @@ static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv) return -1; } - signal(SIGINT, sig_handler); - signal(SIGUSR1, sig_handler); - signal(SIGCHLD, sig_handler); - signal(SIGPIPE, sig_handler); + select_tracer(ftrace); if (reset_tracing_files(ftrace) < 0) { pr_err("failed to reset ftrace\n"); @@ -600,11 +592,6 @@ static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv) if (write_tracing_file("trace", "0") < 0) goto out; - if (argc && evlist__prepare_workload(ftrace->evlist, &ftrace->target, argv, false, - ftrace__workload_exec_failed_signal) < 0) { - goto out; - } - if (set_tracing_options(ftrace) < 0) goto out_reset; @@ -693,6 +680,270 @@ static int __cmd_ftrace(struct perf_ftrace *ftrace, int argc, const char **argv) return (done && !workload_exec_errno) ? 0 : -1; } +static void make_histogram(int buckets[], char *buf, size_t len, char *linebuf) +{ + char *p, *q; + char *unit; + double num; + int i; + + /* ensure NUL termination */ + buf[len] = '\0'; + + /* handle data line by line */ + for (p = buf; (q = strchr(p, '\n')) != NULL; p = q + 1) { + *q = '\0'; + /* move it to the line buffer */ + strcat(linebuf, p); + + /* + * parse trace output to get function duration like in + * + * # tracer: function_graph + * # + * # CPU DURATION FUNCTION CALLS + * # | | | | | | | + * 1) + 10.291 us | do_filp_open(); + * 1) 4.889 us | do_filp_open(); + * 1) 6.086 us | do_filp_open(); + * + */ + if (linebuf[0] == '#') + goto next; + + /* ignore CPU */ + p = strchr(linebuf, ')'); + if (p == NULL) + p = linebuf; + + while (*p && !isdigit(*p) && (*p != '|')) + p++; + + /* no duration */ + if (*p == '\0' || *p == '|') + goto next; + + num = strtod(p, &unit); + if (!unit || strncmp(unit, " us", 3)) + goto next; + + i = log2(num); + if (i < 0) + i = 0; + if (i >= NUM_BUCKET) + i = NUM_BUCKET - 1; + + buckets[i]++; + +next: + /* empty the line buffer for the next output */ + linebuf[0] = '\0'; + } + + /* preserve any remaining output (before newline) */ + strcat(linebuf, p); +} + +static void display_histogram(int buckets[]) +{ + int i; + int total = 0; + int bar_total = 46; /* to fit in 80 column */ + char bar[] = "###############################################"; + int bar_len; + + for (i = 0; i < NUM_BUCKET; i++) + total += buckets[i]; + + if (total == 0) { + printf("No data found\n"); + return; + } + + printf("# %14s | %10s | %-*s |\n", + " DURATION ", "COUNT", bar_total, "GRAPH"); + + bar_len = buckets[0] * bar_total / total; + printf(" %4d - %-4d %s | %10d | %.*s%*s |\n", + 0, 1, "us", buckets[0], bar_len, bar, bar_total - bar_len, ""); + + for (i = 1; i < NUM_BUCKET - 1; i++) { + int start = (1 << (i - 1)); + int stop = 1 << i; + const char *unit = "us"; + + if (start >= 1024) { + start >>= 10; + stop >>= 10; + unit = "ms"; + } + bar_len = buckets[i] * bar_total / total; + printf(" %4d - %-4d %s | %10d | %.*s%*s |\n", + start, stop, unit, buckets[i], bar_len, bar, + bar_total - bar_len, ""); + } + + bar_len = buckets[NUM_BUCKET - 1] * bar_total / total; + printf(" %4d - %-4s %s | %10d | %.*s%*s |\n", + 1, "...", " s", buckets[NUM_BUCKET - 1], bar_len, bar, + bar_total - bar_len, ""); + +} + +static int prepare_func_latency(struct perf_ftrace *ftrace) +{ + char *trace_file; + int fd; + + if (ftrace->target.use_bpf) + return perf_ftrace__latency_prepare_bpf(ftrace); + + if (reset_tracing_files(ftrace) < 0) { + pr_err("failed to reset ftrace\n"); + return -1; + } + + /* reset ftrace buffer */ + if (write_tracing_file("trace", "0") < 0) + return -1; + + if (set_tracing_options(ftrace) < 0) + return -1; + + /* force to use the function_graph tracer to track duration */ + if (write_tracing_file("current_tracer", "function_graph") < 0) { + pr_err("failed to set current_tracer to function_graph\n"); + return -1; + } + + trace_file = get_tracing_file("trace_pipe"); + if (!trace_file) { + pr_err("failed to open trace_pipe\n"); + return -1; + } + + fd = open(trace_file, O_RDONLY); + if (fd < 0) + pr_err("failed to open trace_pipe\n"); + + put_tracing_file(trace_file); + return fd; +} + +static int start_func_latency(struct perf_ftrace *ftrace) +{ + if (ftrace->target.use_bpf) + return perf_ftrace__latency_start_bpf(ftrace); + + if (write_tracing_file("tracing_on", "1") < 0) { + pr_err("can't enable tracing\n"); + return -1; + } + + return 0; +} + +static int stop_func_latency(struct perf_ftrace *ftrace) +{ + if (ftrace->target.use_bpf) + return perf_ftrace__latency_stop_bpf(ftrace); + + write_tracing_file("tracing_on", "0"); + return 0; +} + +static int read_func_latency(struct perf_ftrace *ftrace, int buckets[]) +{ + if (ftrace->target.use_bpf) + return perf_ftrace__latency_read_bpf(ftrace, buckets); + + return 0; +} + +static int cleanup_func_latency(struct perf_ftrace *ftrace) +{ + if (ftrace->target.use_bpf) + return perf_ftrace__latency_cleanup_bpf(ftrace); + + reset_tracing_files(ftrace); + return 0; +} + +static int __cmd_latency(struct perf_ftrace *ftrace) +{ + int trace_fd; + char buf[4096]; + char line[256]; + struct pollfd pollfd = { + .events = POLLIN, + }; + int buckets[NUM_BUCKET] = { }; + + if (!(perf_cap__capable(CAP_PERFMON) || + perf_cap__capable(CAP_SYS_ADMIN))) { + pr_err("ftrace only works for %s!\n", +#ifdef HAVE_LIBCAP_SUPPORT + "users with the CAP_PERFMON or CAP_SYS_ADMIN capability" +#else + "root" +#endif + ); + return -1; + } + + trace_fd = prepare_func_latency(ftrace); + if (trace_fd < 0) + goto out; + + fcntl(trace_fd, F_SETFL, O_NONBLOCK); + pollfd.fd = trace_fd; + + if (start_func_latency(ftrace) < 0) + goto out; + + evlist__start_workload(ftrace->evlist); + + line[0] = '\0'; + while (!done) { + if (poll(&pollfd, 1, -1) < 0) + break; + + if (pollfd.revents & POLLIN) { + int n = read(trace_fd, buf, sizeof(buf) - 1); + if (n < 0) + break; + + make_histogram(buckets, buf, n, line); + } + } + + stop_func_latency(ftrace); + + if (workload_exec_errno) { + const char *emsg = str_error_r(workload_exec_errno, buf, sizeof(buf)); + pr_err("workload failed: %s\n", emsg); + goto out; + } + + /* read remaining buffer contents */ + while (!ftrace->target.use_bpf) { + int n = read(trace_fd, buf, sizeof(buf) - 1); + if (n <= 0) + break; + make_histogram(buckets, buf, n, line); + } + + read_func_latency(ftrace, buckets); + + display_histogram(buckets); + +out: + close(trace_fd); + cleanup_func_latency(ftrace); + + return (done && !workload_exec_errno) ? 0 : -1; +} + static int perf_ftrace_config(const char *var, const char *value, void *cb) { struct perf_ftrace *ftrace = cb; @@ -855,41 +1106,21 @@ static int parse_graph_tracer_opts(const struct option *opt, return 0; } -static void select_tracer(struct perf_ftrace *ftrace) -{ - bool graph = !list_empty(&ftrace->graph_funcs) || - !list_empty(&ftrace->nograph_funcs); - bool func = !list_empty(&ftrace->filters) || - !list_empty(&ftrace->notrace); - - /* The function_graph has priority over function tracer. */ - if (graph) - ftrace->tracer = "function_graph"; - else if (func) - ftrace->tracer = "function"; - /* Otherwise, the default tracer is used. */ - - pr_debug("%s tracer is used\n", ftrace->tracer); -} +enum perf_ftrace_subcommand { + PERF_FTRACE_NONE, + PERF_FTRACE_TRACE, + PERF_FTRACE_LATENCY, +}; int cmd_ftrace(int argc, const char **argv) { int ret; + int (*cmd_func)(struct perf_ftrace *) = NULL; struct perf_ftrace ftrace = { .tracer = DEFAULT_TRACER, .target = { .uid = UINT_MAX, }, }; - const char * const ftrace_usage[] = { - "perf ftrace [] []", - "perf ftrace [] -- []", - NULL - }; - const struct option ftrace_options[] = { - OPT_STRING('t', "tracer", &ftrace.tracer, "tracer", - "Tracer to use: function_graph(default) or function"), - OPT_CALLBACK_DEFAULT('F', "funcs", NULL, "[FILTER]", - "Show available functions to filter", - opt_list_avail_functions, "*"), + const struct option common_options[] = { OPT_STRING('p', "pid", &ftrace.target.pid, "pid", "Trace on existing process id"), /* TODO: Add short option -t after -t/--tracer can be removed. */ @@ -901,6 +1132,14 @@ int cmd_ftrace(int argc, const char **argv) "System-wide collection from all CPUs"), OPT_STRING('C', "cpu", &ftrace.target.cpu_list, "cpu", "List of cpus to monitor"), + OPT_END() + }; + const struct option ftrace_options[] = { + OPT_STRING('t', "tracer", &ftrace.tracer, "tracer", + "Tracer to use: function_graph(default) or function"), + OPT_CALLBACK_DEFAULT('F', "funcs", NULL, "[FILTER]", + "Show available functions to filter", + opt_list_avail_functions, "*"), OPT_CALLBACK('T', "trace-funcs", &ftrace.filters, "func", "Trace given functions using function tracer", parse_filter_func), @@ -923,24 +1162,87 @@ int cmd_ftrace(int argc, const char **argv) "Trace children processes"), OPT_UINTEGER('D', "delay", &ftrace.initial_delay, "Number of milliseconds to wait before starting tracing after program start"), - OPT_END() + OPT_PARENT(common_options), }; + const struct option latency_options[] = { + OPT_CALLBACK('T', "trace-funcs", &ftrace.filters, "func", + "Show latency of given function", parse_filter_func), +#ifdef HAVE_BPF_SKEL + OPT_BOOLEAN('b', "use-bpf", &ftrace.target.use_bpf, + "Use BPF to measure function latency"), +#endif + OPT_PARENT(common_options), + }; + const struct option *options = ftrace_options; + + const char * const ftrace_usage[] = { + "perf ftrace [] []", + "perf ftrace [] -- [] []", + "perf ftrace {trace|latency} [] []", + "perf ftrace {trace|latency} [] -- [] []", + NULL + }; + enum perf_ftrace_subcommand subcmd = PERF_FTRACE_NONE; INIT_LIST_HEAD(&ftrace.filters); INIT_LIST_HEAD(&ftrace.notrace); INIT_LIST_HEAD(&ftrace.graph_funcs); INIT_LIST_HEAD(&ftrace.nograph_funcs); + signal(SIGINT, sig_handler); + signal(SIGUSR1, sig_handler); + signal(SIGCHLD, sig_handler); + signal(SIGPIPE, sig_handler); + ret = perf_config(perf_ftrace_config, &ftrace); if (ret < 0) return -1; - argc = parse_options(argc, argv, ftrace_options, ftrace_usage, - PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc && target__none(&ftrace.target)) - ftrace.target.system_wide = true; + if (argc > 1) { + if (!strcmp(argv[1], "trace")) { + subcmd = PERF_FTRACE_TRACE; + } else if (!strcmp(argv[1], "latency")) { + subcmd = PERF_FTRACE_LATENCY; + options = latency_options; + } - select_tracer(&ftrace); + if (subcmd != PERF_FTRACE_NONE) { + argc--; + argv++; + } + } + /* for backward compatibility */ + if (subcmd == PERF_FTRACE_NONE) + subcmd = PERF_FTRACE_TRACE; + + argc = parse_options(argc, argv, options, ftrace_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + if (argc < 0) { + ret = -EINVAL; + goto out_delete_filters; + } + + switch (subcmd) { + case PERF_FTRACE_TRACE: + if (!argc && target__none(&ftrace.target)) + ftrace.target.system_wide = true; + cmd_func = __cmd_ftrace; + break; + case PERF_FTRACE_LATENCY: + if (list_empty(&ftrace.filters)) { + pr_err("Should provide a function to measure\n"); + parse_options_usage(ftrace_usage, options, "T", 1); + ret = -EINVAL; + goto out_delete_filters; + } + cmd_func = __cmd_latency; + break; + case PERF_FTRACE_NONE: + default: + pr_err("Invalid subcommand\n"); + ret = -EINVAL; + goto out_delete_filters; + } ret = target__validate(&ftrace.target); if (ret) { @@ -961,7 +1263,15 @@ int cmd_ftrace(int argc, const char **argv) if (ret < 0) goto out_delete_evlist; - ret = __cmd_ftrace(&ftrace, argc, argv); + if (argc) { + ret = evlist__prepare_workload(ftrace.evlist, &ftrace.target, + argv, false, + ftrace__workload_exec_failed_signal); + if (ret < 0) + goto out_delete_evlist; + } + + ret = cmd_func(&ftrace); out_delete_evlist: evlist__delete(ftrace.evlist); diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 50c2e6892b..fbf43a454c 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -535,12 +535,9 @@ static int perf_event__repipe_exit(struct perf_tool *tool, static int perf_event__repipe_tracing_data(struct perf_session *session, union perf_event *event) { - int err; - perf_event__repipe_synth(session->tool, event); - err = perf_event__process_tracing_data(session, event); - return err; + return perf_event__process_tracing_data(session, event); } static int dso__read_build_id(struct dso *dso) @@ -819,7 +816,8 @@ static int __cmd_inject(struct perf_inject *inject) inject->tool.auxtrace_info = perf_event__process_auxtrace_info; inject->tool.auxtrace = perf_event__process_auxtrace; inject->tool.aux = perf_event__drop_aux; - inject->tool.itrace_start = perf_event__drop_aux, + inject->tool.itrace_start = perf_event__drop_aux; + inject->tool.aux_output_hw_id = perf_event__drop_aux; inject->tool.ordered_events = true; inject->tool.ordering_requires_timestamps = true; /* Allow space in the header for new attributes */ @@ -886,6 +884,7 @@ int cmd_inject(int argc, const char **argv) .lost_samples = perf_event__repipe, .aux = perf_event__repipe, .itrace_start = perf_event__repipe, + .aux_output_hw_id = perf_event__repipe, .context_switch = perf_event__repipe, .throttle = perf_event__repipe, .unthrottle = perf_event__repipe, @@ -942,6 +941,10 @@ int cmd_inject(int argc, const char **argv) #endif OPT_INCR('v', "verbose", &verbose, "be more verbose (show build ids, etc)"), + OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, + "file", "vmlinux pathname"), + OPT_BOOLEAN(0, "ignore-vmlinux", &symbol_conf.ignore_vmlinux, + "don't load vmlinux even if found"), OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, "file", "kallsyms pathname"), OPT_BOOLEAN('f', "force", &data.force, "don't complain, do it"), @@ -976,6 +979,9 @@ int cmd_inject(int argc, const char **argv) return -1; } + if (symbol__validate_sym_arguments()) + return -1; + if (inject.in_place_update) { if (!strcmp(inject.input_name, "-")) { pr_err("Input file name required for in-place updating\n"); diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index da03a341c6..99d7ff9a8e 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -192,7 +192,7 @@ static int evsel__process_alloc_node_event(struct evsel *evsel, struct perf_samp int ret = evsel__process_alloc_event(evsel, sample); if (!ret) { - int node1 = cpu__get_node(sample->cpu), + int node1 = cpu__get_node((struct perf_cpu){.cpu = sample->cpu}), node2 = evsel__intval(evsel, sample, "node"); if (node1 != node2) diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index aa1b127ffb..c6f352ee57 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -1456,7 +1456,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, perf_session__set_id_hdr_size(kvm->session); ordered_events__set_copy_on_queue(&kvm->session->ordered_events, true); machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.target, - kvm->evlist->core.threads, false, 1); + kvm->evlist->core.threads, true, false, 1); err = kvm_live_open_events(kvm); if (err) goto out; diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index 10ab5e40a3..468958154e 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -12,6 +12,7 @@ #include "util/parse-events.h" #include "util/pmu.h" +#include "util/pmu-hybrid.h" #include "util/debug.h" #include "util/metricgroup.h" #include @@ -20,13 +21,15 @@ static bool desc_flag = true; static bool details_flag; +static const char *hybrid_type; int cmd_list(int argc, const char **argv) { - int i; + int i, ret = 0; bool raw_dump = false; bool long_desc_flag = false; bool deprecated = false; + char *pmu_name = NULL; struct option list_options[] = { OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"), OPT_BOOLEAN('d', "desc", &desc_flag, @@ -37,6 +40,9 @@ int cmd_list(int argc, const char **argv) "Print information on the perf event names and expressions used internally by events."), OPT_BOOLEAN(0, "deprecated", &deprecated, "Print deprecated events."), + OPT_STRING(0, "cputype", &hybrid_type, "hybrid cpu type", + "Print events applying cpu with this type for hybrid platform " + "(e.g. core or atom)"), OPT_INCR(0, "debug", &verbose, "Enable debugging output"), OPT_END() @@ -56,10 +62,16 @@ int cmd_list(int argc, const char **argv) if (!raw_dump && pager_in_use()) printf("\nList of pre-defined events (to be used in -e):\n\n"); + if (hybrid_type) { + pmu_name = perf_pmu__hybrid_type_to_pmu(hybrid_type); + if (!pmu_name) + pr_warning("WARNING: hybrid cputype is not supported!\n"); + } + if (argc == 0) { print_events(NULL, raw_dump, !desc_flag, long_desc_flag, - details_flag, deprecated); - return 0; + details_flag, deprecated, pmu_name); + goto out; } for (i = 0; i < argc; ++i) { @@ -82,25 +94,27 @@ int cmd_list(int argc, const char **argv) else if (strcmp(argv[i], "pmu") == 0) print_pmu_events(NULL, raw_dump, !desc_flag, long_desc_flag, details_flag, - deprecated); + deprecated, pmu_name); else if (strcmp(argv[i], "sdt") == 0) print_sdt_events(NULL, NULL, raw_dump); else if (strcmp(argv[i], "metric") == 0 || strcmp(argv[i], "metrics") == 0) - metricgroup__print(true, false, NULL, raw_dump, details_flag); + metricgroup__print(true, false, NULL, raw_dump, details_flag, pmu_name); else if (strcmp(argv[i], "metricgroup") == 0 || strcmp(argv[i], "metricgroups") == 0) - metricgroup__print(false, true, NULL, raw_dump, details_flag); + metricgroup__print(false, true, NULL, raw_dump, details_flag, pmu_name); else if ((sep = strchr(argv[i], ':')) != NULL) { int sep_idx; sep_idx = sep - argv[i]; s = strdup(argv[i]); - if (s == NULL) - return -1; + if (s == NULL) { + ret = -1; + goto out; + } s[sep_idx] = '\0'; print_tracepoint_events(s, s + sep_idx + 1, raw_dump); print_sdt_events(s, s + sep_idx + 1, raw_dump); - metricgroup__print(true, true, s, raw_dump, details_flag); + metricgroup__print(true, true, s, raw_dump, details_flag, pmu_name); free(s); } else { if (asprintf(&s, "*%s*", argv[i]) < 0) { @@ -116,12 +130,16 @@ int cmd_list(int argc, const char **argv) print_pmu_events(s, raw_dump, !desc_flag, long_desc_flag, details_flag, - deprecated); + deprecated, + pmu_name); print_tracepoint_events(NULL, s, raw_dump); print_sdt_events(NULL, s, raw_dump); - metricgroup__print(true, true, s, raw_dump, details_flag); + metricgroup__print(true, true, s, raw_dump, details_flag, pmu_name); free(s); } } - return 0; + +out: + free(pmu_name); + return ret; } diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index e1dd51f287..c31627af75 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -21,6 +21,7 @@ #include "util/build-id.h" #include "util/strlist.h" #include "util/strfilter.h" +#include "util/symbol.h" #include "util/symbol_conf.h" #include "util/debug.h" #include @@ -629,6 +630,10 @@ __cmd_probe(int argc, const char **argv) params.command = 'a'; } + ret = symbol__validate_sym_arguments(); + if (ret) + return ret; + if (params.quiet) { if (verbose != 0) { pr_err(" Error: -v and -q are exclusive.\n"); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b3509d9d20..bb716c953d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -111,6 +111,7 @@ struct record { unsigned long long samples; struct mmap_cpu_mask affinity_mask; unsigned long output_max_size; /* = 0: unlimited */ + struct perf_debuginfod debuginfod; }; static volatile int done; @@ -1255,6 +1256,7 @@ static int record__synthesize_workload(struct record *rec, bool tail) { int err; struct perf_thread_map *thread_map; + bool needs_mmap = rec->opts.synth & PERF_SYNTH_MMAP; if (rec->opts.tail_synthesize != tail) return 0; @@ -1266,6 +1268,7 @@ static int record__synthesize_workload(struct record *rec, bool tail) err = perf_event__synthesize_thread_map(&rec->tool, thread_map, process_synthesized_event, &rec->session->machines.host, + needs_mmap, rec->opts.sample_address); perf_thread_map__put(thread_map); return err; @@ -1409,7 +1412,7 @@ static int record__synthesize(struct record *rec, bool tail) goto out; /* Synthesize id_index before auxtrace_info */ - if (rec->opts.auxtrace_sample_mode) { + if (rec->opts.auxtrace_sample_mode || rec->opts.full_auxtrace) { err = perf_event__synthesize_id_index(tool, process_synthesized_event, session->evlist, machine); @@ -1470,19 +1473,26 @@ static int record__synthesize(struct record *rec, bool tail) if (err < 0) pr_warning("Couldn't synthesize bpf events.\n"); - err = perf_event__synthesize_cgroups(tool, process_synthesized_event, - machine); - if (err < 0) - pr_warning("Couldn't synthesize cgroup events.\n"); + if (rec->opts.synth & PERF_SYNTH_CGROUP) { + err = perf_event__synthesize_cgroups(tool, process_synthesized_event, + machine); + if (err < 0) + pr_warning("Couldn't synthesize cgroup events.\n"); + } if (rec->opts.nr_threads_synthesize > 1) { perf_set_multithreaded(); f = process_locked_synthesized_event; } - err = __machine__synthesize_threads(machine, tool, &opts->target, rec->evlist->core.threads, - f, opts->sample_address, - rec->opts.nr_threads_synthesize); + if (rec->opts.synth & PERF_SYNTH_TASK) { + bool needs_mmap = rec->opts.synth & PERF_SYNTH_MMAP; + + err = __machine__synthesize_threads(machine, tool, &opts->target, + rec->evlist->core.threads, + f, needs_mmap, opts->sample_address, + rec->opts.nr_threads_synthesize); + } if (rec->opts.nr_threads_synthesize > 1) perf_set_singlethreaded(); @@ -2168,6 +2178,12 @@ static int perf_record_config(const char *var, const char *value, void *cb) rec->opts.nr_cblocks = nr_cblocks_default; } #endif + if (!strcmp(var, "record.debuginfod")) { + rec->debuginfod.urls = strdup(value); + if (!rec->debuginfod.urls) + return -ENOMEM; + rec->debuginfod.set = true; + } return 0; } @@ -2258,6 +2274,10 @@ static int record__parse_mmap_pages(const struct option *opt, return ret; } +void __weak arch__add_leaf_frame_record_opts(struct record_opts *opts __maybe_unused) +{ +} + static int parse_control_option(const struct option *opt, const char *str, int unset __maybe_unused) @@ -2391,6 +2411,26 @@ static int process_timestamp_boundary(struct perf_tool *tool, return 0; } +static int parse_record_synth_option(const struct option *opt, + const char *str, + int unset __maybe_unused) +{ + struct record_opts *opts = opt->value; + char *p = strdup(str); + + if (p == NULL) + return -1; + + opts->synth = parse_synth_opt(p); + free(p); + + if (opts->synth < 0) { + pr_err("Invalid synth option: %s\n", str); + return -1; + } + return 0; +} + /* * XXX Ideally would be local to cmd_record() and passed to a record__new * because we need to have access to it in record__exit, that is called @@ -2416,6 +2456,7 @@ static struct record record = { .nr_threads_synthesize = 1, .ctl_fd = -1, .ctl_fd_ack = -1, + .synth = PERF_SYNTH_ALL, }, .tool = { .sample = process_sample_event, @@ -2631,6 +2672,12 @@ static struct option __record_options[] = { "\t\t\t Optionally send control command completion ('ack\\n') to ack-fd descriptor.\n" "\t\t\t Alternatively, ctl-fifo / ack-fifo will be opened and used as ctl-fd / ack-fd.", parse_control_option), + OPT_CALLBACK(0, "synth", &record.opts, "no|all|task|mmap|cgroup", + "Fine-tune event synthesis: default=all", parse_record_synth_option), + OPT_STRING_OPTARG_SET(0, "debuginfod", &record.debuginfod.urls, + &record.debuginfod.set, "debuginfod urls", + "Enable debuginfod data retrieval from DEBUGINFOD_URLS or specified urls", + "system"), OPT_END() }; @@ -2680,6 +2727,12 @@ int cmd_record(int argc, const char **argv) if (quiet) perf_quiet_option(); + err = symbol__validate_sym_arguments(); + if (err) + return err; + + perf_debuginfod_setup(&record.debuginfod); + /* Make system wide (-a) the default target. */ if (!argc && target__none(&rec->opts.target)) rec->opts.target.system_wide = true; @@ -2756,7 +2809,7 @@ int cmd_record(int argc, const char **argv) symbol__init(NULL); if (rec->opts.affinity != PERF_AFFINITY_SYS) { - rec->affinity_mask.nbits = cpu__max_cpu(); + rec->affinity_mask.nbits = cpu__max_cpu().cpu; rec->affinity_mask.bits = bitmap_zalloc(rec->affinity_mask.nbits); if (!rec->affinity_mask.bits) { pr_err("Failed to allocate thread mask for %zd cpus\n", rec->affinity_mask.nbits); @@ -2862,6 +2915,10 @@ int cmd_record(int argc, const char **argv) } rec->opts.target.hybrid = perf_pmu__has_hybrid(); + + if (callchain_param.enabled && callchain_param.record_mode == CALLCHAIN_FP) + arch__add_leaf_frame_record_opts(&rec->opts); + err = -ENOMEM; if (evlist__create_maps(rec->evlist, &rec->opts.target) < 0) usage_with_options(record_usage, record_options); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 997e0a4b09..1dd92d8c92 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -410,7 +410,7 @@ static int report__setup_sample_type(struct report *rep) } } - callchain_param_setup(sample_type); + callchain_param_setup(sample_type, perf_env__arch(&rep->session->header.env)); if (rep->stitch_lbr && (callchain_param.record_mode != CALLCHAIN_LBR)) { ui__warning("Can't find LBR callchain. Switch off --stitch-lbr.\n" @@ -1127,7 +1127,7 @@ static int process_attr(struct perf_tool *tool __maybe_unused, * on events sample_type. */ sample_type = evlist__combined_sample_type(*pevlist); - callchain_param_setup(sample_type); + callchain_param_setup(sample_type, perf_env__arch((*pevlist)->env)); return 0; } @@ -1381,18 +1381,9 @@ int cmd_report(int argc, const char **argv) if (quiet) perf_quiet_option(); - if (symbol_conf.vmlinux_name && - access(symbol_conf.vmlinux_name, R_OK)) { - pr_err("Invalid file: %s\n", symbol_conf.vmlinux_name); - ret = -EINVAL; + ret = symbol__validate_sym_arguments(); + if (ret) goto exit; - } - if (symbol_conf.kallsyms_name && - access(symbol_conf.kallsyms_name, R_OK)) { - pr_err("Invalid file: %s\n", symbol_conf.kallsyms_name); - ret = -EINVAL; - goto exit; - } if (report.inverted_callchain) callchain_param.order = ORDER_CALLER; diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 635a6b5a9e..72d446de9c 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -167,7 +167,7 @@ struct trace_sched_handler { struct perf_sched_map { DECLARE_BITMAP(comp_cpus_mask, MAX_CPUS); - int *comp_cpus; + struct perf_cpu *comp_cpus; bool comp; struct perf_thread_map *color_pids; const char *color_pids_str; @@ -191,7 +191,7 @@ struct perf_sched { * Track the current task - that way we can know whether there's any * weird events, such as a task being switched away that is not current. */ - int max_cpu; + struct perf_cpu max_cpu; u32 curr_pid[MAX_CPUS]; struct thread *curr_thread[MAX_CPUS]; char next_shortname1; @@ -1535,28 +1535,31 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, int new_shortname; u64 timestamp0, timestamp = sample->time; s64 delta; - int i, this_cpu = sample->cpu; + int i; + struct perf_cpu this_cpu = { + .cpu = sample->cpu, + }; int cpus_nr; bool new_cpu = false; const char *color = PERF_COLOR_NORMAL; char stimestamp[32]; - BUG_ON(this_cpu >= MAX_CPUS || this_cpu < 0); + BUG_ON(this_cpu.cpu >= MAX_CPUS || this_cpu.cpu < 0); - if (this_cpu > sched->max_cpu) + if (this_cpu.cpu > sched->max_cpu.cpu) sched->max_cpu = this_cpu; if (sched->map.comp) { cpus_nr = bitmap_weight(sched->map.comp_cpus_mask, MAX_CPUS); - if (!test_and_set_bit(this_cpu, sched->map.comp_cpus_mask)) { + if (!test_and_set_bit(this_cpu.cpu, sched->map.comp_cpus_mask)) { sched->map.comp_cpus[cpus_nr++] = this_cpu; new_cpu = true; } } else - cpus_nr = sched->max_cpu; + cpus_nr = sched->max_cpu.cpu; - timestamp0 = sched->cpu_last_switched[this_cpu]; - sched->cpu_last_switched[this_cpu] = timestamp; + timestamp0 = sched->cpu_last_switched[this_cpu.cpu]; + sched->cpu_last_switched[this_cpu.cpu] = timestamp; if (timestamp0) delta = timestamp - timestamp0; else @@ -1577,7 +1580,7 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, return -1; } - sched->curr_thread[this_cpu] = thread__get(sched_in); + sched->curr_thread[this_cpu.cpu] = thread__get(sched_in); printf(" "); @@ -1608,8 +1611,10 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, } for (i = 0; i < cpus_nr; i++) { - int cpu = sched->map.comp ? sched->map.comp_cpus[i] : i; - struct thread *curr_thread = sched->curr_thread[cpu]; + struct perf_cpu cpu = { + .cpu = sched->map.comp ? sched->map.comp_cpus[i].cpu : i, + }; + struct thread *curr_thread = sched->curr_thread[cpu.cpu]; struct thread_runtime *curr_tr; const char *pid_color = color; const char *cpu_color = color; @@ -1617,19 +1622,19 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, if (curr_thread && thread__has_color(curr_thread)) pid_color = COLOR_PIDS; - if (sched->map.cpus && !cpu_map__has(sched->map.cpus, cpu)) + if (sched->map.cpus && !perf_cpu_map__has(sched->map.cpus, cpu)) continue; - if (sched->map.color_cpus && cpu_map__has(sched->map.color_cpus, cpu)) + if (sched->map.color_cpus && perf_cpu_map__has(sched->map.color_cpus, cpu)) cpu_color = COLOR_CPUS; - if (cpu != this_cpu) + if (cpu.cpu != this_cpu.cpu) color_fprintf(stdout, color, " "); else color_fprintf(stdout, cpu_color, "*"); - if (sched->curr_thread[cpu]) { - curr_tr = thread__get_runtime(sched->curr_thread[cpu]); + if (sched->curr_thread[cpu.cpu]) { + curr_tr = thread__get_runtime(sched->curr_thread[cpu.cpu]); if (curr_tr == NULL) { thread__put(sched_in); return -1; @@ -1639,7 +1644,7 @@ static int map_switch_event(struct perf_sched *sched, struct evsel *evsel, color_fprintf(stdout, color, " "); } - if (sched->map.cpus && !cpu_map__has(sched->map.cpus, this_cpu)) + if (sched->map.cpus && !perf_cpu_map__has(sched->map.cpus, this_cpu)) goto out; timestamp__scnprintf_usec(timestamp, stimestamp, sizeof(stimestamp)); @@ -1929,7 +1934,7 @@ static char *timehist_get_commstr(struct thread *thread) static void timehist_header(struct perf_sched *sched) { - u32 ncpus = sched->max_cpu + 1; + u32 ncpus = sched->max_cpu.cpu + 1; u32 i, j; printf("%15s %6s ", "time", "cpu"); @@ -2008,7 +2013,7 @@ static void timehist_print_sample(struct perf_sched *sched, struct thread_runtime *tr = thread__priv(thread); const char *next_comm = evsel__strval(evsel, sample, "next_comm"); const u32 next_pid = evsel__intval(evsel, sample, "next_pid"); - u32 max_cpus = sched->max_cpu + 1; + u32 max_cpus = sched->max_cpu.cpu + 1; char tstr[64]; char nstr[30]; u64 wait_time; @@ -2389,7 +2394,7 @@ static void timehist_print_wakeup_event(struct perf_sched *sched, timestamp__scnprintf_usec(sample->time, tstr, sizeof(tstr)); printf("%15s [%04d] ", tstr, sample->cpu); if (sched->show_cpu_visual) - printf(" %*s ", sched->max_cpu + 1, ""); + printf(" %*s ", sched->max_cpu.cpu + 1, ""); printf(" %-*s ", comm_width, timehist_get_commstr(thread)); @@ -2449,13 +2454,13 @@ static void timehist_print_migration_event(struct perf_sched *sched, { struct thread *thread; char tstr[64]; - u32 max_cpus = sched->max_cpu + 1; + u32 max_cpus; u32 ocpu, dcpu; if (sched->summary_only) return; - max_cpus = sched->max_cpu + 1; + max_cpus = sched->max_cpu.cpu + 1; ocpu = evsel__intval(evsel, sample, "orig_cpu"); dcpu = evsel__intval(evsel, sample, "dest_cpu"); @@ -2918,7 +2923,7 @@ static void timehist_print_summary(struct perf_sched *sched, printf(" Total scheduling time (msec): "); print_sched_time(hist_time, 2); - printf(" (x %d)\n", sched->max_cpu); + printf(" (x %d)\n", sched->max_cpu.cpu); } typedef int (*sched_handler)(struct perf_tool *tool, @@ -2935,9 +2940,11 @@ static int perf_timehist__process_sample(struct perf_tool *tool, { struct perf_sched *sched = container_of(tool, struct perf_sched, tool); int err = 0; - int this_cpu = sample->cpu; + struct perf_cpu this_cpu = { + .cpu = sample->cpu, + }; - if (this_cpu > sched->max_cpu) + if (this_cpu.cpu > sched->max_cpu.cpu) sched->max_cpu = this_cpu; if (evsel->handler != NULL) { @@ -3054,10 +3061,10 @@ static int perf_sched__timehist(struct perf_sched *sched) goto out; /* pre-allocate struct for per-CPU idle stats */ - sched->max_cpu = session->header.env.nr_cpus_online; - if (sched->max_cpu == 0) - sched->max_cpu = 4; - if (init_idle_threads(sched->max_cpu)) + sched->max_cpu.cpu = session->header.env.nr_cpus_online; + if (sched->max_cpu.cpu == 0) + sched->max_cpu.cpu = 4; + if (init_idle_threads(sched->max_cpu.cpu)) goto out; /* summary_only implies summary option, but don't overwrite summary if set */ @@ -3209,10 +3216,10 @@ static int setup_map_cpus(struct perf_sched *sched) { struct perf_cpu_map *map; - sched->max_cpu = sysconf(_SC_NPROCESSORS_CONF); + sched->max_cpu.cpu = sysconf(_SC_NPROCESSORS_CONF); if (sched->map.comp) { - sched->map.comp_cpus = zalloc(sched->max_cpu * sizeof(int)); + sched->map.comp_cpus = zalloc(sched->max_cpu.cpu * sizeof(int)); if (!sched->map.comp_cpus) return -1; } @@ -3538,6 +3545,7 @@ int cmd_sched(int argc, const char **argv) .fork_event = replay_fork_event, }; unsigned int i; + int ret; for (i = 0; i < ARRAY_SIZE(sched.curr_pid); i++) sched.curr_pid[i] = -1; @@ -3598,6 +3606,9 @@ int cmd_sched(int argc, const char **argv) parse_options_usage(NULL, timehist_options, "n", true); return -EINVAL; } + ret = symbol__validate_sym_arguments(); + if (ret) + return ret; return perf_sched__timehist(&sched); } else { diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 18b56256bb..fa478ddcd1 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -15,6 +15,7 @@ #include "util/symbol.h" #include "util/thread.h" #include "util/trace-event.h" +#include "util/env.h" #include "util/evlist.h" #include "util/evsel.h" #include "util/evsel_fprintf.h" @@ -122,6 +123,7 @@ enum perf_output_field { PERF_OUTPUT_TOD = 1ULL << 32, PERF_OUTPUT_DATA_PAGE_SIZE = 1ULL << 33, PERF_OUTPUT_CODE_PAGE_SIZE = 1ULL << 34, + PERF_OUTPUT_INS_LAT = 1ULL << 35, }; struct perf_script { @@ -188,6 +190,7 @@ struct output_option { {.str = "tod", .field = PERF_OUTPUT_TOD}, {.str = "data_page_size", .field = PERF_OUTPUT_DATA_PAGE_SIZE}, {.str = "code_page_size", .field = PERF_OUTPUT_CODE_PAGE_SIZE}, + {.str = "ins_lat", .field = PERF_OUTPUT_INS_LAT}, }; enum { @@ -262,7 +265,8 @@ static struct { PERF_OUTPUT_DSO | PERF_OUTPUT_PERIOD | PERF_OUTPUT_ADDR | PERF_OUTPUT_DATA_SRC | PERF_OUTPUT_WEIGHT | PERF_OUTPUT_PHYS_ADDR | - PERF_OUTPUT_DATA_PAGE_SIZE | PERF_OUTPUT_CODE_PAGE_SIZE, + PERF_OUTPUT_DATA_PAGE_SIZE | PERF_OUTPUT_CODE_PAGE_SIZE | + PERF_OUTPUT_INS_LAT, .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, }, @@ -459,7 +463,7 @@ static int evsel__check_attr(struct evsel *evsel, struct perf_session *session) return -EINVAL; if (PRINT_FIELD(WEIGHT) && - evsel__check_stype(evsel, PERF_SAMPLE_WEIGHT_TYPE, "WEIGHT", PERF_OUTPUT_WEIGHT)) + evsel__do_check_stype(evsel, PERF_SAMPLE_WEIGHT_TYPE, "WEIGHT", PERF_OUTPUT_WEIGHT, allow_user_set)) return -EINVAL; if (PRINT_FIELD(SYM) && @@ -511,7 +515,7 @@ static int evsel__check_attr(struct evsel *evsel, struct perf_session *session) return -EINVAL; if (PRINT_FIELD(PHYS_ADDR) && - evsel__check_stype(evsel, PERF_SAMPLE_PHYS_ADDR, "PHYS_ADDR", PERF_OUTPUT_PHYS_ADDR)) + evsel__do_check_stype(evsel, PERF_SAMPLE_PHYS_ADDR, "PHYS_ADDR", PERF_OUTPUT_PHYS_ADDR, allow_user_set)) return -EINVAL; if (PRINT_FIELD(DATA_PAGE_SIZE) && @@ -522,6 +526,10 @@ static int evsel__check_attr(struct evsel *evsel, struct perf_session *session) evsel__check_stype(evsel, PERF_SAMPLE_CODE_PAGE_SIZE, "CODE_PAGE_SIZE", PERF_OUTPUT_CODE_PAGE_SIZE)) return -EINVAL; + if (PRINT_FIELD(INS_LAT) && + evsel__check_stype(evsel, PERF_SAMPLE_WEIGHT_STRUCT, "WEIGHT_STRUCT", PERF_OUTPUT_INS_LAT)) + return -EINVAL; + return 0; } @@ -641,7 +649,7 @@ static int perf_session__check_output_opt(struct perf_session *session) return 0; } -static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, +static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, const char *arch, FILE *fp) { unsigned i = 0, r; @@ -654,7 +662,7 @@ static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, for_each_set_bit(r, (unsigned long *) &mask, sizeof(mask) * 8) { u64 val = regs->regs[i++]; - printed += fprintf(fp, "%5s:0x%"PRIx64" ", perf_reg_name(r), val); + printed += fprintf(fp, "%5s:0x%"PRIx64" ", perf_reg_name(r, arch), val); } return printed; @@ -711,17 +719,17 @@ tod_scnprintf(struct perf_script *script, char *buf, int buflen, } static int perf_sample__fprintf_iregs(struct perf_sample *sample, - struct perf_event_attr *attr, FILE *fp) + struct perf_event_attr *attr, const char *arch, FILE *fp) { return perf_sample__fprintf_regs(&sample->intr_regs, - attr->sample_regs_intr, fp); + attr->sample_regs_intr, arch, fp); } static int perf_sample__fprintf_uregs(struct perf_sample *sample, - struct perf_event_attr *attr, FILE *fp) + struct perf_event_attr *attr, const char *arch, FILE *fp) { return perf_sample__fprintf_regs(&sample->user_regs, - attr->sample_regs_user, fp); + attr->sample_regs_user, arch, fp); } static int perf_sample__fprintf_start(struct perf_script *script, @@ -1993,6 +2001,7 @@ static void process_event(struct perf_script *script, struct evsel_script *es = evsel->priv; FILE *fp = es->fp; char str[PAGE_SIZE_NAME_LEN]; + const char *arch = perf_env__arch(machine->env); if (output[type].fields == 0) return; @@ -2039,6 +2048,9 @@ static void process_event(struct perf_script *script, if (PRINT_FIELD(WEIGHT)) fprintf(fp, "%16" PRIu64, sample->weight); + if (PRINT_FIELD(INS_LAT)) + fprintf(fp, "%16" PRIu16, sample->ins_lat); + if (PRINT_FIELD(IP)) { struct callchain_cursor *cursor = NULL; @@ -2056,10 +2068,10 @@ static void process_event(struct perf_script *script, } if (PRINT_FIELD(IREGS)) - perf_sample__fprintf_iregs(sample, attr, fp); + perf_sample__fprintf_iregs(sample, attr, arch, fp); if (PRINT_FIELD(UREGS)) - perf_sample__fprintf_uregs(sample, attr, fp); + perf_sample__fprintf_uregs(sample, attr, arch, fp); if (PRINT_FIELD(BRSTACK)) perf_sample__fprintf_brstack(sample, thread, attr, fp); @@ -2103,8 +2115,8 @@ static struct scripting_ops *scripting_ops; static void __process_stat(struct evsel *counter, u64 tstamp) { int nthreads = perf_thread_map__nr(counter->core.threads); - int ncpus = evsel__nr_cpus(counter); - int cpu, thread; + int idx, thread; + struct perf_cpu cpu; static int header_printed; if (counter->core.system_wide) @@ -2117,13 +2129,13 @@ static void __process_stat(struct evsel *counter, u64 tstamp) } for (thread = 0; thread < nthreads; thread++) { - for (cpu = 0; cpu < ncpus; cpu++) { + perf_cpu_map__for_each_cpu(cpu, idx, evsel__cpus(counter)) { struct perf_counts_values *counts; - counts = perf_counts(counter->counts, cpu, thread); + counts = perf_counts(counter->counts, idx, thread); printf("%3d %8d %15" PRIu64 " %15" PRIu64 " %15" PRIu64 " %15" PRIu64 " %s\n", - counter->core.cpus->map[cpu], + cpu.cpu, perf_thread_map__pid(counter->core.threads, thread), counts->val, counts->ena, @@ -2306,7 +2318,7 @@ static int process_attr(struct perf_tool *tool, union perf_event *event, * on events sample_type. */ sample_type = evlist__combined_sample_type(evlist); - callchain_param_setup(sample_type); + callchain_param_setup(sample_type, perf_env__arch((*pevlist)->env)); /* Enable fields for callchain entries */ if (symbol_conf.use_callchain && @@ -3456,16 +3468,7 @@ static void script__setup_sample_type(struct perf_script *script) struct perf_session *session = script->session; 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) && - (sample_type & PERF_SAMPLE_STACK_USER)) { - callchain_param.record_mode = CALLCHAIN_DWARF; - dwarf_callchain_users = true; - } else if (sample_type & PERF_SAMPLE_BRANCH_STACK) - callchain_param.record_mode = CALLCHAIN_LBR; - else - callchain_param.record_mode = CALLCHAIN_FP; - } + callchain_param_setup(sample_type, perf_env__arch(session->machines.host.env)); if (script->stitch_lbr && (callchain_param.record_mode != CALLCHAIN_LBR)) { pr_warning("Can't find LBR callchain. Switch off --stitch-lbr.\n" @@ -3715,7 +3718,7 @@ int cmd_script(int argc, const char **argv) "addr,symoff,srcline,period,iregs,uregs,brstack," "brstacksym,flags,bpf-output,brstackinsn,brstackoff," "callindent,insn,insnlen,synth,phys_addr,metric,misc,ipc,tod," - "data_page_size,code_page_size", + "data_page_size,code_page_size,ins_lat", parse_output_fields), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), @@ -3836,6 +3839,9 @@ int cmd_script(int argc, const char **argv) data.path = input_name; data.force = symbol_conf.force; + if (symbol__validate_sym_arguments()) + return -1; + if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) { rec_script_path = get_script_path(argv[1], RECORD_SUFFIX); if (!rec_script_path) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index f0ecfda34e..3f98689dd6 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -230,11 +230,12 @@ static bool cpus_map_matched(struct evsel *a, struct evsel *b) if (!a->core.cpus || !b->core.cpus) return false; - if (a->core.cpus->nr != b->core.cpus->nr) + if (perf_cpu_map__nr(a->core.cpus) != perf_cpu_map__nr(b->core.cpus)) return false; - for (int i = 0; i < a->core.cpus->nr; i++) { - if (a->core.cpus->map[i] != b->core.cpus->map[i]) + for (int i = 0; i < perf_cpu_map__nr(a->core.cpus); i++) { + if (perf_cpu_map__cpu(a->core.cpus, i).cpu != + perf_cpu_map__cpu(b->core.cpus, i).cpu) return false; } @@ -327,34 +328,35 @@ static int write_stat_round_event(u64 tm, u64 type) #define SID(e, x, y) xyarray__entry(e->core.sample_id, x, y) -static int evsel__write_stat_event(struct evsel *counter, u32 cpu, u32 thread, +static int evsel__write_stat_event(struct evsel *counter, int cpu_map_idx, u32 thread, struct perf_counts_values *count) { - struct perf_sample_id *sid = SID(counter, cpu, thread); + struct perf_sample_id *sid = SID(counter, cpu_map_idx, thread); + struct perf_cpu cpu = perf_cpu_map__cpu(evsel__cpus(counter), cpu_map_idx); return perf_event__synthesize_stat(NULL, cpu, thread, sid->id, count, process_synthesized_event, NULL); } -static int read_single_counter(struct evsel *counter, int cpu, +static int read_single_counter(struct evsel *counter, int cpu_map_idx, int thread, struct timespec *rs) { if (counter->tool_event == PERF_TOOL_DURATION_TIME) { u64 val = rs->tv_nsec + rs->tv_sec*1000000000ULL; struct perf_counts_values *count = - perf_counts(counter->counts, cpu, thread); + perf_counts(counter->counts, cpu_map_idx, thread); count->ena = count->run = val; count->val = val; return 0; } - return evsel__read_counter(counter, cpu, thread); + return evsel__read_counter(counter, cpu_map_idx, thread); } /* * Read out the results of a single counter: * do not aggregate counts across CPUs in system-wide mode */ -static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu) +static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu_map_idx) { int nthreads = perf_thread_map__nr(evsel_list->core.threads); int thread; @@ -368,24 +370,24 @@ static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu) for (thread = 0; thread < nthreads; thread++) { struct perf_counts_values *count; - count = perf_counts(counter->counts, cpu, thread); + count = perf_counts(counter->counts, cpu_map_idx, thread); /* * The leader's group read loads data into its group members * (via evsel__read_counter()) and sets their count->loaded. */ - if (!perf_counts__is_loaded(counter->counts, cpu, thread) && - read_single_counter(counter, cpu, thread, rs)) { + if (!perf_counts__is_loaded(counter->counts, cpu_map_idx, thread) && + read_single_counter(counter, cpu_map_idx, thread, rs)) { counter->counts->scaled = -1; - perf_counts(counter->counts, cpu, thread)->ena = 0; - perf_counts(counter->counts, cpu, thread)->run = 0; + perf_counts(counter->counts, cpu_map_idx, thread)->ena = 0; + perf_counts(counter->counts, cpu_map_idx, thread)->run = 0; return -1; } - perf_counts__set_loaded(counter->counts, cpu, thread, false); + perf_counts__set_loaded(counter->counts, cpu_map_idx, thread, false); if (STAT_RECORD) { - if (evsel__write_stat_event(counter, cpu, thread, count)) { + if (evsel__write_stat_event(counter, cpu_map_idx, thread, count)) { pr_err("failed to write stat event\n"); return -1; } @@ -395,7 +397,8 @@ static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu) fprintf(stat_config.output, "%s: %d: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", evsel__name(counter), - cpu, + perf_cpu_map__cpu(evsel__cpus(counter), + cpu_map_idx).cpu, count->val, count->ena, count->run); } } @@ -405,36 +408,33 @@ static int read_counter_cpu(struct evsel *counter, struct timespec *rs, int cpu) static int read_affinity_counters(struct timespec *rs) { - struct evsel *counter; - struct affinity affinity; - int i, ncpus, cpu; + struct evlist_cpu_iterator evlist_cpu_itr; + struct affinity saved_affinity, *affinity; if (all_counters_use_bpf) return 0; - if (affinity__setup(&affinity) < 0) - return -1; - - ncpus = perf_cpu_map__nr(evsel_list->core.all_cpus); if (!target__has_cpu(&target) || target__has_per_thread(&target)) - ncpus = 1; - evlist__for_each_cpu(evsel_list, i, cpu) { - if (i >= ncpus) - break; - affinity__set(&affinity, cpu); + affinity = NULL; + else if (affinity__setup(&saved_affinity) < 0) + return -1; + else + affinity = &saved_affinity; - evlist__for_each_entry(evsel_list, counter) { - if (evsel__cpu_iter_skip(counter, cpu)) - continue; - if (evsel__is_bpf(counter)) - continue; - if (!counter->err) { - counter->err = read_counter_cpu(counter, rs, - counter->cpu_iter - 1); - } + evlist__for_each_cpu(evlist_cpu_itr, evsel_list, affinity) { + struct evsel *counter = evlist_cpu_itr.evsel; + + if (evsel__is_bpf(counter)) + continue; + + if (!counter->err) { + counter->err = read_counter_cpu(counter, rs, + evlist_cpu_itr.cpu_map_idx); } } - affinity__cleanup(&affinity); + if (affinity) + affinity__cleanup(&saved_affinity); + return 0; } @@ -788,8 +788,9 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) int status = 0; const bool forks = (argc > 0); bool is_pipe = STAT_RECORD ? perf_stat.data.is_pipe : false; - struct affinity affinity; - int i, cpu, err; + struct evlist_cpu_iterator evlist_cpu_itr; + struct affinity saved_affinity, *affinity = NULL; + int err; bool second_pass = false; if (forks) { @@ -803,8 +804,11 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) if (group) evlist__set_leader(evsel_list); - if (affinity__setup(&affinity) < 0) - return -1; + if (!cpu_map__is_dummy(evsel_list->core.cpus)) { + if (affinity__setup(&saved_affinity) < 0) + return -1; + affinity = &saved_affinity; + } evlist__for_each_entry(evsel_list, counter) { if (bpf_counter__load(counter, &target)) @@ -813,56 +817,53 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) all_counters_use_bpf = false; } - evlist__for_each_cpu (evsel_list, i, cpu) { + evlist__for_each_cpu(evlist_cpu_itr, evsel_list, affinity) { + counter = evlist_cpu_itr.evsel; + /* * bperf calls evsel__open_per_cpu() in bperf__load(), so * no need to call it again here. */ if (target.use_bpf) break; - affinity__set(&affinity, cpu); - evlist__for_each_entry(evsel_list, counter) { - if (evsel__cpu_iter_skip(counter, cpu)) - continue; - if (counter->reset_group || counter->errored) - continue; - if (evsel__is_bpf(counter)) - continue; + if (counter->reset_group || counter->errored) + continue; + if (evsel__is_bpf(counter)) + continue; try_again: - if (create_perf_stat_counter(counter, &stat_config, &target, - counter->cpu_iter - 1) < 0) { - - /* - * Weak group failed. We cannot just undo this here - * because earlier CPUs might be in group mode, and the kernel - * doesn't support mixing group and non group reads. Defer - * it to later. - * Don't close here because we're in the wrong affinity. - */ - if ((errno == EINVAL || errno == EBADF) && - evsel__leader(counter) != counter && - counter->weak_group) { - evlist__reset_weak_group(evsel_list, counter, false); - assert(counter->reset_group); - second_pass = true; - continue; - } - - switch (stat_handle_error(counter)) { - case COUNTER_FATAL: - return -1; - case COUNTER_RETRY: - goto try_again; - case COUNTER_SKIP: - continue; - default: - break; - } + if (create_perf_stat_counter(counter, &stat_config, &target, + evlist_cpu_itr.cpu_map_idx) < 0) { + /* + * Weak group failed. We cannot just undo this here + * because earlier CPUs might be in group mode, and the kernel + * doesn't support mixing group and non group reads. Defer + * it to later. + * Don't close here because we're in the wrong affinity. + */ + if ((errno == EINVAL || errno == EBADF) && + evsel__leader(counter) != counter && + counter->weak_group) { + evlist__reset_weak_group(evsel_list, counter, false); + assert(counter->reset_group); + second_pass = true; + continue; } - counter->supported = true; + + switch (stat_handle_error(counter)) { + case COUNTER_FATAL: + return -1; + case COUNTER_RETRY: + goto try_again; + case COUNTER_SKIP: + continue; + default: + break; + } + } + counter->supported = true; } if (second_pass) { @@ -871,45 +872,43 @@ static int __run_perf_stat(int argc, const char **argv, int run_idx) * and also close errored counters. */ - evlist__for_each_cpu(evsel_list, i, cpu) { - affinity__set(&affinity, cpu); - /* First close errored or weak retry */ - evlist__for_each_entry(evsel_list, counter) { - if (!counter->reset_group && !counter->errored) - continue; - if (evsel__cpu_iter_skip_no_inc(counter, cpu)) - continue; - perf_evsel__close_cpu(&counter->core, counter->cpu_iter); - } - /* Now reopen weak */ - evlist__for_each_entry(evsel_list, counter) { - if (!counter->reset_group && !counter->errored) - continue; - if (evsel__cpu_iter_skip(counter, cpu)) - continue; - if (!counter->reset_group) - continue; -try_again_reset: - pr_debug2("reopening weak %s\n", evsel__name(counter)); - if (create_perf_stat_counter(counter, &stat_config, &target, - counter->cpu_iter - 1) < 0) { + /* First close errored or weak retry */ + evlist__for_each_cpu(evlist_cpu_itr, evsel_list, affinity) { + counter = evlist_cpu_itr.evsel; - switch (stat_handle_error(counter)) { - case COUNTER_FATAL: - return -1; - case COUNTER_RETRY: - goto try_again_reset; - case COUNTER_SKIP: - continue; - default: - break; - } + if (!counter->reset_group && !counter->errored) + continue; + + perf_evsel__close_cpu(&counter->core, evlist_cpu_itr.cpu_map_idx); + } + /* Now reopen weak */ + evlist__for_each_cpu(evlist_cpu_itr, evsel_list, affinity) { + counter = evlist_cpu_itr.evsel; + + if (!counter->reset_group && !counter->errored) + continue; + if (!counter->reset_group) + continue; +try_again_reset: + pr_debug2("reopening weak %s\n", evsel__name(counter)); + if (create_perf_stat_counter(counter, &stat_config, &target, + evlist_cpu_itr.cpu_map_idx) < 0) { + + switch (stat_handle_error(counter)) { + case COUNTER_FATAL: + return -1; + case COUNTER_RETRY: + goto try_again_reset; + case COUNTER_SKIP: + continue; + default: + break; } - counter->supported = true; } + counter->supported = true; } } - affinity__cleanup(&affinity); + affinity__cleanup(affinity); evlist__for_each_entry(evsel_list, counter) { if (!counter->supported) { @@ -1168,6 +1167,26 @@ static int parse_stat_cgroups(const struct option *opt, return parse_cgroups(opt, str, unset); } +static int parse_hybrid_type(const struct option *opt, + const char *str, + int unset __maybe_unused) +{ + struct evlist *evlist = *(struct evlist **)opt->value; + + if (!list_empty(&evlist->core.entries)) { + fprintf(stderr, "Must define cputype before events/metrics\n"); + return -1; + } + + evlist->hybrid_pmu_name = perf_pmu__hybrid_type_to_pmu(str); + if (!evlist->hybrid_pmu_name) { + fprintf(stderr, "--cputype %s is not supported!\n", str); + return -1; + } + + return 0; +} + static struct option stat_options[] = { OPT_BOOLEAN('T', "transaction", &transaction_run, "hardware transaction statistics"), @@ -1282,6 +1301,10 @@ static struct option stat_options[] = { "don't print 'summary' for CSV summary output"), OPT_BOOLEAN(0, "quiet", &stat_config.quiet, "don't print output (useful with record)"), + OPT_CALLBACK(0, "cputype", &evsel_list, "hybrid cpu type", + "Only enable events on applying cpu with this type " + "for hybrid platform (e.g. core or atom)", + parse_hybrid_type), #ifdef HAVE_LIBPFM OPT_CALLBACK(0, "pfm-events", &evsel_list, "event", "libpfm4 event selector. use 'perf list' to list available events", @@ -1298,70 +1321,75 @@ static struct option stat_options[] = { OPT_END() }; +static const char *const aggr_mode__string[] = { + [AGGR_CORE] = "core", + [AGGR_DIE] = "die", + [AGGR_GLOBAL] = "global", + [AGGR_NODE] = "node", + [AGGR_NONE] = "none", + [AGGR_SOCKET] = "socket", + [AGGR_THREAD] = "thread", + [AGGR_UNSET] = "unset", +}; + static struct aggr_cpu_id perf_stat__get_socket(struct perf_stat_config *config __maybe_unused, - struct perf_cpu_map *map, int cpu) + struct perf_cpu cpu) { - return cpu_map__get_socket(map, cpu, NULL); + return aggr_cpu_id__socket(cpu, /*data=*/NULL); } static struct aggr_cpu_id perf_stat__get_die(struct perf_stat_config *config __maybe_unused, - struct perf_cpu_map *map, int cpu) + struct perf_cpu cpu) { - return cpu_map__get_die(map, cpu, NULL); + return aggr_cpu_id__die(cpu, /*data=*/NULL); } static struct aggr_cpu_id perf_stat__get_core(struct perf_stat_config *config __maybe_unused, - struct perf_cpu_map *map, int cpu) + struct perf_cpu cpu) { - return cpu_map__get_core(map, cpu, NULL); + return aggr_cpu_id__core(cpu, /*data=*/NULL); } static struct aggr_cpu_id perf_stat__get_node(struct perf_stat_config *config __maybe_unused, - struct perf_cpu_map *map, int cpu) + struct perf_cpu cpu) { - return cpu_map__get_node(map, cpu, NULL); + return aggr_cpu_id__node(cpu, /*data=*/NULL); } static struct aggr_cpu_id perf_stat__get_aggr(struct perf_stat_config *config, - aggr_get_id_t get_id, struct perf_cpu_map *map, int idx) + aggr_get_id_t get_id, struct perf_cpu cpu) { - int cpu; - struct aggr_cpu_id id = cpu_map__empty_aggr_cpu_id(); + struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (idx >= map->nr) - return id; + if (aggr_cpu_id__is_empty(&config->cpus_aggr_map->map[cpu.cpu])) + config->cpus_aggr_map->map[cpu.cpu] = get_id(config, cpu); - cpu = map->map[idx]; - - if (cpu_map__aggr_cpu_id_is_empty(config->cpus_aggr_map->map[cpu])) - config->cpus_aggr_map->map[cpu] = get_id(config, map, idx); - - id = config->cpus_aggr_map->map[cpu]; + id = config->cpus_aggr_map->map[cpu.cpu]; return id; } static struct aggr_cpu_id perf_stat__get_socket_cached(struct perf_stat_config *config, - struct perf_cpu_map *map, int idx) + struct perf_cpu cpu) { - return perf_stat__get_aggr(config, perf_stat__get_socket, map, idx); + return perf_stat__get_aggr(config, perf_stat__get_socket, cpu); } static struct aggr_cpu_id perf_stat__get_die_cached(struct perf_stat_config *config, - struct perf_cpu_map *map, int idx) + struct perf_cpu cpu) { - return perf_stat__get_aggr(config, perf_stat__get_die, map, idx); + return perf_stat__get_aggr(config, perf_stat__get_die, cpu); } static struct aggr_cpu_id perf_stat__get_core_cached(struct perf_stat_config *config, - struct perf_cpu_map *map, int idx) + struct perf_cpu cpu) { - return perf_stat__get_aggr(config, perf_stat__get_core, map, idx); + return perf_stat__get_aggr(config, perf_stat__get_core, cpu); } static struct aggr_cpu_id perf_stat__get_node_cached(struct perf_stat_config *config, - struct perf_cpu_map *map, int idx) + struct perf_cpu cpu) { - return perf_stat__get_aggr(config, perf_stat__get_node, map, idx); + return perf_stat__get_aggr(config, perf_stat__get_node, cpu); } static bool term_percore_set(void) @@ -1376,54 +1404,67 @@ static bool term_percore_set(void) return false; } -static int perf_stat_init_aggr_mode(void) +static aggr_cpu_id_get_t aggr_mode__get_aggr(enum aggr_mode aggr_mode) { - int nr; - - switch (stat_config.aggr_mode) { + switch (aggr_mode) { case AGGR_SOCKET: - if (cpu_map__build_socket_map(evsel_list->core.cpus, &stat_config.aggr_map)) { - perror("cannot build socket map"); - return -1; - } - stat_config.aggr_get_id = perf_stat__get_socket_cached; - break; + return aggr_cpu_id__socket; case AGGR_DIE: - if (cpu_map__build_die_map(evsel_list->core.cpus, &stat_config.aggr_map)) { - perror("cannot build die map"); - return -1; - } - stat_config.aggr_get_id = perf_stat__get_die_cached; - break; + return aggr_cpu_id__die; case AGGR_CORE: - if (cpu_map__build_core_map(evsel_list->core.cpus, &stat_config.aggr_map)) { - perror("cannot build core map"); - return -1; - } - stat_config.aggr_get_id = perf_stat__get_core_cached; - break; + return aggr_cpu_id__core; case AGGR_NODE: - if (cpu_map__build_node_map(evsel_list->core.cpus, &stat_config.aggr_map)) { - perror("cannot build core map"); - return -1; - } - stat_config.aggr_get_id = perf_stat__get_node_cached; - break; + return aggr_cpu_id__node; case AGGR_NONE: - if (term_percore_set()) { - if (cpu_map__build_core_map(evsel_list->core.cpus, - &stat_config.aggr_map)) { - perror("cannot build core map"); - return -1; - } - stat_config.aggr_get_id = perf_stat__get_core_cached; - } - break; + if (term_percore_set()) + return aggr_cpu_id__core; + + return NULL; case AGGR_GLOBAL: case AGGR_THREAD: case AGGR_UNSET: default: - break; + return NULL; + } +} + +static aggr_get_id_t aggr_mode__get_id(enum aggr_mode aggr_mode) +{ + switch (aggr_mode) { + case AGGR_SOCKET: + return perf_stat__get_socket_cached; + case AGGR_DIE: + return perf_stat__get_die_cached; + case AGGR_CORE: + return perf_stat__get_core_cached; + case AGGR_NODE: + return perf_stat__get_node_cached; + case AGGR_NONE: + if (term_percore_set()) { + return perf_stat__get_core_cached; + } + return NULL; + case AGGR_GLOBAL: + case AGGR_THREAD: + case AGGR_UNSET: + default: + return NULL; + } +} + +static int perf_stat_init_aggr_mode(void) +{ + int nr; + aggr_cpu_id_get_t get_id = aggr_mode__get_aggr(stat_config.aggr_mode); + + if (get_id) { + stat_config.aggr_map = cpu_aggr_map__new(evsel_list->core.cpus, + get_id, /*data=*/NULL); + if (!stat_config.aggr_map) { + pr_err("cannot build %s map", aggr_mode__string[stat_config.aggr_mode]); + return -1; + } + stat_config.aggr_get_id = aggr_mode__get_id(stat_config.aggr_mode); } /* @@ -1431,7 +1472,7 @@ static int perf_stat_init_aggr_mode(void) * taking the highest cpu number to be the size of * the aggregation translate cpumap. */ - nr = perf_cpu_map__max(evsel_list->core.cpus); + nr = perf_cpu_map__max(evsel_list->core.cpus).cpu; stat_config.cpus_aggr_map = cpu_aggr_map__empty_new(nr + 1); return stat_config.cpus_aggr_map ? 0 : -ENOMEM; } @@ -1459,169 +1500,139 @@ static void perf_stat__exit_aggr_mode(void) stat_config.cpus_aggr_map = NULL; } -static inline int perf_env__get_cpu(struct perf_env *env, struct perf_cpu_map *map, int idx) -{ - int cpu; - - if (idx > map->nr) - return -1; - - cpu = map->map[idx]; - - if (cpu >= env->nr_cpus_avail) - return -1; - - return cpu; -} - -static struct aggr_cpu_id perf_env__get_socket(struct perf_cpu_map *map, int idx, void *data) +static struct aggr_cpu_id perf_env__get_socket_aggr_by_cpu(struct perf_cpu cpu, void *data) { struct perf_env *env = data; - int cpu = perf_env__get_cpu(env, map, idx); - struct aggr_cpu_id id = cpu_map__empty_aggr_cpu_id(); + struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu != -1) - id.socket = env->cpu[cpu].socket_id; + if (cpu.cpu != -1) + id.socket = env->cpu[cpu.cpu].socket_id; return id; } -static struct aggr_cpu_id perf_env__get_die(struct perf_cpu_map *map, int idx, void *data) +static struct aggr_cpu_id perf_env__get_die_aggr_by_cpu(struct perf_cpu cpu, void *data) { struct perf_env *env = data; - struct aggr_cpu_id id = cpu_map__empty_aggr_cpu_id(); - int cpu = perf_env__get_cpu(env, map, idx); + struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu != -1) { + if (cpu.cpu != -1) { /* * die_id is relative to socket, so start * with the socket ID and then add die to * make a unique ID. */ - id.socket = env->cpu[cpu].socket_id; - id.die = env->cpu[cpu].die_id; + id.socket = env->cpu[cpu.cpu].socket_id; + id.die = env->cpu[cpu.cpu].die_id; } return id; } -static struct aggr_cpu_id perf_env__get_core(struct perf_cpu_map *map, int idx, void *data) +static struct aggr_cpu_id perf_env__get_core_aggr_by_cpu(struct perf_cpu cpu, void *data) { struct perf_env *env = data; - struct aggr_cpu_id id = cpu_map__empty_aggr_cpu_id(); - int cpu = perf_env__get_cpu(env, map, idx); + struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (cpu != -1) { + if (cpu.cpu != -1) { /* * core_id is relative to socket and die, * we need a global id. So we set * socket, die id and core id */ - id.socket = env->cpu[cpu].socket_id; - id.die = env->cpu[cpu].die_id; - id.core = env->cpu[cpu].core_id; + id.socket = env->cpu[cpu.cpu].socket_id; + id.die = env->cpu[cpu.cpu].die_id; + id.core = env->cpu[cpu.cpu].core_id; } return id; } -static struct aggr_cpu_id perf_env__get_node(struct perf_cpu_map *map, int idx, void *data) +static struct aggr_cpu_id perf_env__get_node_aggr_by_cpu(struct perf_cpu cpu, void *data) { - int cpu = perf_env__get_cpu(data, map, idx); - struct aggr_cpu_id id = cpu_map__empty_aggr_cpu_id(); + struct aggr_cpu_id id = aggr_cpu_id__empty(); id.node = perf_env__numa_node(data, cpu); return id; } -static int perf_env__build_socket_map(struct perf_env *env, struct perf_cpu_map *cpus, - struct cpu_aggr_map **sockp) -{ - return cpu_map__build_map(cpus, sockp, perf_env__get_socket, env); -} - -static int perf_env__build_die_map(struct perf_env *env, struct perf_cpu_map *cpus, - struct cpu_aggr_map **diep) -{ - return cpu_map__build_map(cpus, diep, perf_env__get_die, env); -} - -static int perf_env__build_core_map(struct perf_env *env, struct perf_cpu_map *cpus, - struct cpu_aggr_map **corep) -{ - return cpu_map__build_map(cpus, corep, perf_env__get_core, env); -} - -static int perf_env__build_node_map(struct perf_env *env, struct perf_cpu_map *cpus, - struct cpu_aggr_map **nodep) -{ - return cpu_map__build_map(cpus, nodep, perf_env__get_node, env); -} - static struct aggr_cpu_id perf_stat__get_socket_file(struct perf_stat_config *config __maybe_unused, - struct perf_cpu_map *map, int idx) + struct perf_cpu cpu) { - return perf_env__get_socket(map, idx, &perf_stat.session->header.env); + return perf_env__get_socket_aggr_by_cpu(cpu, &perf_stat.session->header.env); } static struct aggr_cpu_id perf_stat__get_die_file(struct perf_stat_config *config __maybe_unused, - struct perf_cpu_map *map, int idx) + struct perf_cpu cpu) { - return perf_env__get_die(map, idx, &perf_stat.session->header.env); + return perf_env__get_die_aggr_by_cpu(cpu, &perf_stat.session->header.env); } static struct aggr_cpu_id perf_stat__get_core_file(struct perf_stat_config *config __maybe_unused, - struct perf_cpu_map *map, int idx) + struct perf_cpu cpu) { - return perf_env__get_core(map, idx, &perf_stat.session->header.env); + return perf_env__get_core_aggr_by_cpu(cpu, &perf_stat.session->header.env); } static struct aggr_cpu_id perf_stat__get_node_file(struct perf_stat_config *config __maybe_unused, - struct perf_cpu_map *map, int idx) + struct perf_cpu cpu) { - return perf_env__get_node(map, idx, &perf_stat.session->header.env); + return perf_env__get_node_aggr_by_cpu(cpu, &perf_stat.session->header.env); } -static int perf_stat_init_aggr_mode_file(struct perf_stat *st) +static aggr_cpu_id_get_t aggr_mode__get_aggr_file(enum aggr_mode aggr_mode) { - struct perf_env *env = &st->session->header.env; - - switch (stat_config.aggr_mode) { + switch (aggr_mode) { case AGGR_SOCKET: - if (perf_env__build_socket_map(env, evsel_list->core.cpus, &stat_config.aggr_map)) { - perror("cannot build socket map"); - return -1; - } - stat_config.aggr_get_id = perf_stat__get_socket_file; - break; + return perf_env__get_socket_aggr_by_cpu; case AGGR_DIE: - if (perf_env__build_die_map(env, evsel_list->core.cpus, &stat_config.aggr_map)) { - perror("cannot build die map"); - return -1; - } - stat_config.aggr_get_id = perf_stat__get_die_file; - break; + return perf_env__get_die_aggr_by_cpu; case AGGR_CORE: - if (perf_env__build_core_map(env, evsel_list->core.cpus, &stat_config.aggr_map)) { - perror("cannot build core map"); - return -1; - } - stat_config.aggr_get_id = perf_stat__get_core_file; - break; + return perf_env__get_core_aggr_by_cpu; case AGGR_NODE: - if (perf_env__build_node_map(env, evsel_list->core.cpus, &stat_config.aggr_map)) { - perror("cannot build core map"); - return -1; - } - stat_config.aggr_get_id = perf_stat__get_node_file; - break; + return perf_env__get_node_aggr_by_cpu; case AGGR_NONE: case AGGR_GLOBAL: case AGGR_THREAD: case AGGR_UNSET: default: - break; + return NULL; } +} +static aggr_get_id_t aggr_mode__get_id_file(enum aggr_mode aggr_mode) +{ + switch (aggr_mode) { + case AGGR_SOCKET: + return perf_stat__get_socket_file; + case AGGR_DIE: + return perf_stat__get_die_file; + case AGGR_CORE: + return perf_stat__get_core_file; + case AGGR_NODE: + return perf_stat__get_node_file; + case AGGR_NONE: + case AGGR_GLOBAL: + case AGGR_THREAD: + case AGGR_UNSET: + default: + return NULL; + } +} + +static int perf_stat_init_aggr_mode_file(struct perf_stat *st) +{ + struct perf_env *env = &st->session->header.env; + aggr_cpu_id_get_t get_id = aggr_mode__get_aggr_file(stat_config.aggr_mode); + + if (!get_id) + return 0; + + stat_config.aggr_map = cpu_aggr_map__new(evsel_list->core.cpus, get_id, env); + if (!stat_config.aggr_map) { + pr_err("cannot build %s map", aggr_mode__string[stat_config.aggr_mode]); + return -1; + } + stat_config.aggr_get_id = aggr_mode__get_id_file(stat_config.aggr_mode); return 0; } @@ -1750,14 +1761,12 @@ static int add_default_attributes(void) (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, }; - struct parse_events_error errinfo; - /* Set attrs if no event is selected and !null_run: */ if (stat_config.null_run) return 0; - bzero(&errinfo, sizeof(errinfo)); if (transaction_run) { + struct parse_events_error errinfo; /* Handle -T as -M transaction. Once platform specific metrics * support has been added to the json files, all architectures * will use this approach. To determine transaction support @@ -1772,6 +1781,7 @@ static int add_default_attributes(void) &stat_config.metric_events); } + parse_events_error__init(&errinfo); if (pmu_have_event("cpu", "cycles-ct") && pmu_have_event("cpu", "el-start")) err = parse_events(evsel_list, transaction_attrs, @@ -1782,13 +1792,14 @@ static int add_default_attributes(void) &errinfo); if (err) { fprintf(stderr, "Cannot set up transaction events\n"); - parse_events_print_error(&errinfo, transaction_attrs); - return -1; + parse_events_error__print(&errinfo, transaction_attrs); } - return 0; + parse_events_error__exit(&errinfo); + return err ? -1 : 0; } if (smi_cost) { + struct parse_events_error errinfo; int smi; if (sysfs__read_int(FREEZE_ON_SMI_PATH, &smi) < 0) { @@ -1804,23 +1815,23 @@ static int add_default_attributes(void) smi_reset = true; } - if (pmu_have_event("msr", "aperf") && - pmu_have_event("msr", "smi")) { - if (!force_metric_only) - stat_config.metric_only = true; - err = parse_events(evsel_list, smi_cost_attrs, &errinfo); - } else { + if (!pmu_have_event("msr", "aperf") || + !pmu_have_event("msr", "smi")) { fprintf(stderr, "To measure SMI cost, it needs " "msr/aperf/, msr/smi/ and cpu/cycles/ support\n"); - parse_events_print_error(&errinfo, smi_cost_attrs); return -1; } + if (!force_metric_only) + stat_config.metric_only = true; + + parse_events_error__init(&errinfo); + err = parse_events(evsel_list, smi_cost_attrs, &errinfo); if (err) { - parse_events_print_error(&errinfo, smi_cost_attrs); + parse_events_error__print(&errinfo, smi_cost_attrs); fprintf(stderr, "Cannot set up SMI cost events\n"); - return -1; } - return 0; + parse_events_error__exit(&errinfo); + return err ? -1 : 0; } if (topdown_run) { @@ -1875,18 +1886,22 @@ static int add_default_attributes(void) return -1; } if (topdown_attrs[0] && str) { + struct parse_events_error errinfo; if (warn) arch_topdown_group_warn(); setup_metrics: + parse_events_error__init(&errinfo); err = parse_events(evsel_list, str, &errinfo); if (err) { fprintf(stderr, "Cannot set up top down events %s: %d\n", str, err); - parse_events_print_error(&errinfo, str); + parse_events_error__print(&errinfo, str); + parse_events_error__exit(&errinfo); free(str); return -1; } + parse_events_error__exit(&errinfo); } else { fprintf(stderr, "System does not support topdown\n"); return -1; @@ -1896,6 +1911,7 @@ static int add_default_attributes(void) if (!evsel_list->core.nr_entries) { if (perf_pmu__has_hybrid()) { + struct parse_events_error errinfo; const char *hybrid_str = "cycles,instructions,branches,branch-misses"; if (target__has_cpu(&target)) @@ -1906,15 +1922,16 @@ static int add_default_attributes(void) return -1; } + parse_events_error__init(&errinfo); err = parse_events(evsel_list, hybrid_str, &errinfo); if (err) { fprintf(stderr, "Cannot set up hybrid events %s: %d\n", hybrid_str, err); - parse_events_print_error(&errinfo, hybrid_str); - return -1; + parse_events_error__print(&errinfo, hybrid_str); } - return err; + parse_events_error__exit(&errinfo); + return err ? -1 : 0; } if (target__has_cpu(&target)) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index a3ae9176a8..1fc390f136 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1271,7 +1271,7 @@ static int __cmd_top(struct perf_top *top) pr_debug("Couldn't synthesize cgroup events.\n"); machine__synthesize_threads(&top->session->machines.host, &opts->target, - top->evlist->core.threads, false, + top->evlist->core.threads, true, false, top->nr_threads_synthesize); if (top->nr_threads_synthesize > 1) @@ -1618,6 +1618,10 @@ int cmd_top(int argc, const char **argv) if (argc) usage_with_options(top_usage, options); + status = symbol__validate_sym_arguments(); + if (status) + goto out_delete_evlist; + if (annotate_check_args(&top.annotation_opts) < 0) goto out_delete_evlist; diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 2bf21194c7..52b137a184 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -979,6 +979,8 @@ static struct syscall_fmt syscall_fmts[] = { .arg = { [2] = { .scnprintf = SCA_GETRANDOM_FLAGS, /* flags */ }, }, }, { .name = "getrlimit", .arg = { [0] = STRARRAY(resource, rlimit_resources), }, }, + { .name = "getsockopt", + .arg = { [1] = STRARRAY(level, socket_level), }, }, { .name = "gettid", .errpid = true, }, { .name = "ioctl", .arg = { @@ -1121,6 +1123,8 @@ static struct syscall_fmt syscall_fmts[] = { .arg = { [0] = STRARRAY(which, itimers), }, }, { .name = "setrlimit", .arg = { [0] = STRARRAY(resource, rlimit_resources), }, }, + { .name = "setsockopt", + .arg = { [1] = STRARRAY(level, socket_level), }, }, { .name = "socket", .arg = { [0] = STRARRAY(family, socket_families), [1] = { .scnprintf = SCA_SK_TYPE, /* type */ }, @@ -1532,13 +1536,20 @@ static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp) return fprintf(fp, " ? "); } +static pid_t workload_pid = -1; static bool done = false; static bool interrupted = false; -static void sig_handler(int sig) +static void sighandler_interrupt(int sig __maybe_unused) { - done = true; - interrupted = sig == SIGINT; + done = interrupted = true; +} + +static void sighandler_chld(int sig __maybe_unused, siginfo_t *info, + void *context __maybe_unused) +{ + if (info->si_pid == workload_pid) + done = true; } static size_t trace__fprintf_comm_tid(struct trace *trace, struct thread *thread, FILE *fp) @@ -1628,8 +1639,8 @@ static int trace__symbols_init(struct trace *trace, struct evlist *evlist) goto out; err = __machine__synthesize_threads(trace->host, &trace->tool, &trace->opts.target, - evlist->core.threads, trace__tool_process, false, - 1); + evlist->core.threads, trace__tool_process, + true, false, 1); out: if (err) symbol__exit(); @@ -2722,6 +2733,8 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel, offset = format_field__intval(field, sample, evsel->needs_swap); syscall_arg.len = offset >> 16; offset &= 0xffff; + if (field->flags & TEP_FIELD_IS_RELATIVE) + offset += field->offset + field->size; } val = (uintptr_t)(sample->raw_data + offset); @@ -3063,15 +3076,11 @@ static bool evlist__add_vfs_getname(struct evlist *evlist) struct parse_events_error err; int ret; - bzero(&err, sizeof(err)); + parse_events_error__init(&err); ret = parse_events(evlist, "probe:vfs_getname*", &err); - if (ret) { - free(err.str); - free(err.help); - free(err.first_str); - free(err.first_help); + parse_events_error__exit(&err); + if (ret) return false; - } evlist__for_each_entry_safe(evlist, evsel, tmp) { if (!strstarts(evsel__name(evsel), "probe:vfs_getname")) @@ -3257,10 +3266,21 @@ static void trace__set_bpf_map_syscalls(struct trace *trace) static struct bpf_program *trace__find_bpf_program_by_title(struct trace *trace, const char *name) { + struct bpf_program *pos, *prog = NULL; + const char *sec_name; + if (trace->bpf_obj == NULL) return NULL; - return bpf_object__find_program_by_title(trace->bpf_obj, name); + bpf_object__for_each_program(pos, trace->bpf_obj) { + sec_name = bpf_program__section_name(pos); + if (sec_name && !strcmp(sec_name, name)) { + prog = pos; + break; + } + } + + return prog; } static struct bpf_program *trace__find_syscall_bpf_prog(struct trace *trace, struct syscall *sc, @@ -3950,6 +3970,9 @@ static int trace__run(struct trace *trace, int argc, const char **argv) evlist__add(evlist, pgfault_min); } + /* Enable ignoring missing threads when -u/-p option is defined. */ + trace->opts.ignore_missing_thread = trace->opts.target.uid != UINT_MAX || trace->opts.target.pid; + if (trace->sched && evlist__add_newtp(evlist, "sched", "sched_stat_runtime", trace__sched_stat_runtime)) goto out_error_sched_stat_runtime; @@ -4001,6 +4024,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) fprintf(trace->output, "Couldn't run the workload!\n"); goto out_delete_evlist; } + workload_pid = evlist->workload.pid; } err = evlist__open(evlist); @@ -4870,11 +4894,16 @@ int cmd_trace(int argc, const char **argv) const char * const trace_subcommands[] = { "record", NULL }; int err = -1; char bf[BUFSIZ]; + struct sigaction sigchld_act; signal(SIGSEGV, sighandler_dump_stack); signal(SIGFPE, sighandler_dump_stack); - signal(SIGCHLD, sig_handler); - signal(SIGINT, sig_handler); + signal(SIGINT, sighandler_interrupt); + + memset(&sigchld_act, 0, sizeof(sigchld_act)); + sigchld_act.sa_flags = SA_SIGINFO; + sigchld_act.sa_sigaction = sighandler_chld; + sigaction(SIGCHLD, &sigchld_act, NULL); trace.evlist = evlist__new(); trace.sctbl = syscalltbl__new(); @@ -4925,12 +4954,13 @@ int cmd_trace(int argc, const char **argv) if (trace.perfconfig_events != NULL) { struct parse_events_error parse_err; - bzero(&parse_err, sizeof(parse_err)); + parse_events_error__init(&parse_err); err = parse_events(trace.evlist, trace.perfconfig_events, &parse_err); - if (err) { - parse_events_print_error(&parse_err, trace.perfconfig_events); + if (err) + parse_events_error__print(&parse_err, trace.perfconfig_events); + parse_events_error__exit(&parse_err); + if (err) goto out; - } } if ((nr_cgroups || trace.cgroup) && !trace.opts.target.system_wide) { diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh index f1e46277e8..30ecf3a0f6 100644 --- a/tools/perf/check-headers.sh +++ b/tools/perf/check-headers.sh @@ -26,6 +26,7 @@ include/vdso/bits.h include/linux/const.h include/vdso/const.h include/linux/hash.h +include/linux/list-sort.h include/uapi/linux/hw_breakpoint.h arch/x86/include/asm/disabled-features.h arch/x86/include/asm/required-features.h @@ -150,6 +151,7 @@ check include/uapi/linux/mman.h '-I "^#include <\(uapi/\)*asm/mman.h>"' check include/linux/build_bug.h '-I "^#\(ifndef\|endif\)\( \/\/\)* static_assert$"' check include/linux/ctype.h '-I "isdigit("' check lib/ctype.c '-I "^EXPORT_SYMBOL" -I "^#include " -B' +check lib/list_sort.c '-I "^#include "' # diff non-symmetric files check_2 tools/perf/arch/x86/entry/syscalls/syscall_64.tbl arch/x86/entry/syscalls/syscall_64.tbl diff --git a/tools/perf/design.txt b/tools/perf/design.txt index a42fab308f..aa8cfeabb7 100644 --- a/tools/perf/design.txt +++ b/tools/perf/design.txt @@ -106,6 +106,9 @@ enum perf_hw_id { PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, PERF_COUNT_HW_BRANCH_MISSES = 5, PERF_COUNT_HW_BUS_CYCLES = 6, + PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7, + PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8, + PERF_COUNT_HW_REF_CPU_CYCLES = 9, }; These are standardized types of events that work relatively uniformly diff --git a/tools/perf/util/affinity.c b/tools/perf/util/affinity.c index 7b12bd7a30..4d216c0dc4 100644 --- a/tools/perf/util/affinity.c +++ b/tools/perf/util/affinity.c @@ -11,7 +11,7 @@ static int get_cpu_set_size(void) { - int sz = cpu__max_cpu() + 8 - 1; + int sz = cpu__max_cpu().cpu + 8 - 1; /* * sched_getaffinity doesn't like masks smaller than the kernel. * Hopefully that's big enough. @@ -62,7 +62,7 @@ void affinity__set(struct affinity *a, int cpu) clear_bit(cpu, a->sched_cpus); } -void affinity__cleanup(struct affinity *a) +static void __affinity__cleanup(struct affinity *a) { int cpu_set_size = get_cpu_set_size(); @@ -71,3 +71,9 @@ void affinity__cleanup(struct affinity *a) zfree(&a->sched_cpus); zfree(&a->orig_cpus); } + +void affinity__cleanup(struct affinity *a) +{ + if (a != NULL) + __affinity__cleanup(a); +} diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index 5f383908ca..19910b9011 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,7 @@ enum itrace_period_type { #define AUXTRACE_ERR_FLG_DATA_LOST (1 << ('l' - 'a')) #define AUXTRACE_LOG_FLG_ALL_PERF_EVTS (1 << ('a' - 'a')) +#define AUXTRACE_LOG_FLG_USE_STDOUT (1 << ('o' - 'a')) /** * struct itrace_synth_opts - AUX area tracing synthesis options. @@ -84,6 +86,7 @@ enum itrace_period_type { * @thread_stack: feed branches to the thread_stack * @last_branch: add branch context to 'instruction' events * @add_last_branch: add branch context to existing event records + * @approx_ipc: approximate IPC * @flc: whether to synthesize first level cache events * @llc: whether to synthesize last level cache events * @tlb: whether to synthesize TLB events @@ -127,6 +130,7 @@ struct itrace_synth_opts { bool thread_stack; bool last_branch; bool add_last_branch; + bool approx_ipc; bool flc; bool llc; bool tlb; @@ -237,7 +241,7 @@ struct auxtrace_buffer { size_t size; pid_t pid; pid_t tid; - int cpu; + struct perf_cpu cpu; void *data; off_t data_offset; void *mmap_addr; @@ -347,7 +351,7 @@ struct auxtrace_mmap_params { int prot; int idx; pid_t tid; - int cpu; + struct perf_cpu cpu; }; /** @@ -639,6 +643,7 @@ bool auxtrace__evsel_is_auxtrace(struct perf_session *session, " d[flags]: create a debug log\n" \ " each flag must be preceded by + or -\n" \ " log flags are: a (all perf events)\n" \ +" o (output to stdout)\n" \ " f: synthesize first level cache events\n" \ " m: synthesize last level cache events\n" \ " t: synthesize TLB events\n" \ @@ -649,6 +654,8 @@ bool auxtrace__evsel_is_auxtrace(struct perf_session *session, " L[len]: synthesize last branch entries on existing event records\n" \ " sNUMBER: skip initial number of events\n" \ " q: quicker (less detailed) decoding\n" \ +" A: approximate IPC\n" \ +" Z: prefer to ignore timestamps (so-called \"timeless\" decoding)\n" \ " PERIOD[ns|us|ms|i|t]: specify period to sample stream\n" \ " concatenate multiple options. Default is ibxwpe or cewp\n" diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 16ad0e6e9e..a517eaa51e 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -10,6 +10,7 @@ #include #include #include "bpf-event.h" +#include "bpf-utils.h" #include "debug.h" #include "dso.h" #include "symbol.h" @@ -32,7 +33,32 @@ struct btf * __weak btf__load_from_kernel_by_id(__u32 id) return err ? ERR_PTR(err) : btf; } -#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr)) +struct bpf_program * __weak +bpf_object__next_program(const struct bpf_object *obj, struct bpf_program *prev) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + return bpf_program__next(prev, obj); +#pragma GCC diagnostic pop +} + +struct bpf_map * __weak +bpf_object__next_map(const struct bpf_object *obj, const struct bpf_map *prev) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + return bpf_map__next(prev, obj); +#pragma GCC diagnostic pop +} + +const void * __weak +btf__raw_data(const struct btf *btf_ro, __u32 *size) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + return btf__get_raw_data(btf_ro, size); +#pragma GCC diagnostic pop +} static int snprintf_hex(char *buf, size_t size, unsigned char *data, size_t len) { @@ -48,9 +74,9 @@ static int machine__process_bpf_event_load(struct machine *machine, union perf_event *event, struct perf_sample *sample __maybe_unused) { - struct bpf_prog_info_linear *info_linear; struct bpf_prog_info_node *info_node; struct perf_env *env = machine->env; + struct perf_bpil *info_linear; int id = event->bpf.id; unsigned int i; @@ -110,7 +136,7 @@ static int perf_env__fetch_btf(struct perf_env *env, u32 data_size; const void *data; - data = btf__get_raw_data(btf, &data_size); + data = btf__raw_data(btf, &data_size); node = malloc(data_size + sizeof(struct btf_node)); if (!node) @@ -179,9 +205,9 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session, { struct perf_record_ksymbol *ksymbol_event = &event->ksymbol; struct perf_record_bpf_event *bpf_event = &event->bpf; - struct bpf_prog_info_linear *info_linear; struct perf_tool *tool = session->tool; struct bpf_prog_info_node *info_node; + struct perf_bpil *info_linear; struct bpf_prog_info *info; struct btf *btf = NULL; struct perf_env *env; @@ -195,15 +221,15 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session, */ env = session->data ? &session->header.env : &perf_env; - arrays = 1UL << BPF_PROG_INFO_JITED_KSYMS; - arrays |= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS; - arrays |= 1UL << BPF_PROG_INFO_FUNC_INFO; - arrays |= 1UL << BPF_PROG_INFO_PROG_TAGS; - arrays |= 1UL << BPF_PROG_INFO_JITED_INSNS; - arrays |= 1UL << BPF_PROG_INFO_LINE_INFO; - arrays |= 1UL << BPF_PROG_INFO_JITED_LINE_INFO; + arrays = 1UL << PERF_BPIL_JITED_KSYMS; + arrays |= 1UL << PERF_BPIL_JITED_FUNC_LENS; + arrays |= 1UL << PERF_BPIL_FUNC_INFO; + arrays |= 1UL << PERF_BPIL_PROG_TAGS; + arrays |= 1UL << PERF_BPIL_JITED_INSNS; + arrays |= 1UL << PERF_BPIL_LINE_INFO; + arrays |= 1UL << PERF_BPIL_JITED_LINE_INFO; - info_linear = bpf_program__get_prog_info_linear(fd, arrays); + info_linear = get_bpf_prog_info_linear(fd, arrays); if (IS_ERR_OR_NULL(info_linear)) { info_linear = NULL; pr_debug("%s: failed to get BPF program info. aborting\n", __func__); @@ -456,8 +482,8 @@ int perf_event__synthesize_bpf_events(struct perf_session *session, static void perf_env__add_bpf_info(struct perf_env *env, u32 id) { - struct bpf_prog_info_linear *info_linear; struct bpf_prog_info_node *info_node; + struct perf_bpil *info_linear; struct btf *btf = NULL; u64 arrays; u32 btf_id; @@ -467,15 +493,15 @@ static void perf_env__add_bpf_info(struct perf_env *env, u32 id) if (fd < 0) return; - arrays = 1UL << BPF_PROG_INFO_JITED_KSYMS; - arrays |= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS; - arrays |= 1UL << BPF_PROG_INFO_FUNC_INFO; - arrays |= 1UL << BPF_PROG_INFO_PROG_TAGS; - arrays |= 1UL << BPF_PROG_INFO_JITED_INSNS; - arrays |= 1UL << BPF_PROG_INFO_LINE_INFO; - arrays |= 1UL << BPF_PROG_INFO_JITED_LINE_INFO; + arrays = 1UL << PERF_BPIL_JITED_KSYMS; + arrays |= 1UL << PERF_BPIL_JITED_FUNC_LENS; + arrays |= 1UL << PERF_BPIL_FUNC_INFO; + arrays |= 1UL << PERF_BPIL_PROG_TAGS; + arrays |= 1UL << PERF_BPIL_JITED_INSNS; + arrays |= 1UL << PERF_BPIL_LINE_INFO; + arrays |= 1UL << PERF_BPIL_JITED_LINE_INFO; - info_linear = bpf_program__get_prog_info_linear(fd, arrays); + info_linear = get_bpf_prog_info_linear(fd, arrays); if (IS_ERR_OR_NULL(info_linear)) { pr_debug("%s: failed to get BPF program info. aborting\n", __func__); goto out; diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 71710a1da4..16ec605a9f 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -29,6 +29,9 @@ #include +/* temporarily disable libbpf deprecation warnings */ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + static int libbpf_perf_print(enum libbpf_print_level level __attribute__((unused)), const char *fmt, va_list args) { @@ -421,7 +424,7 @@ preproc_gen_prologue(struct bpf_program *prog, int n, size_t prologue_cnt = 0; int i, err; - if (IS_ERR(priv) || !priv || priv->is_tp) + if (IS_ERR_OR_NULL(priv) || priv->is_tp) goto errout; pev = &priv->pev; @@ -570,7 +573,7 @@ static int hook_load_preprocessor(struct bpf_program *prog) bool need_prologue = false; int err, i; - if (IS_ERR(priv) || !priv) { + if (IS_ERR_OR_NULL(priv)) { pr_debug("Internal error when hook preprocessor\n"); return -BPF_LOADER_ERRNO__INTERNAL; } @@ -642,8 +645,11 @@ int bpf__probe(struct bpf_object *obj) goto out; priv = bpf_program__priv(prog); - if (IS_ERR(priv) || !priv) { - err = PTR_ERR(priv); + if (IS_ERR_OR_NULL(priv)) { + if (!priv) + err = -BPF_LOADER_ERRNO__INTERNAL; + else + err = PTR_ERR(priv); goto out; } @@ -693,7 +699,7 @@ int bpf__unprobe(struct bpf_object *obj) struct bpf_prog_priv *priv = bpf_program__priv(prog); int i; - if (IS_ERR(priv) || !priv || priv->is_tp) + if (IS_ERR_OR_NULL(priv) || priv->is_tp) continue; for (i = 0; i < priv->pev.ntevs; i++) { @@ -751,7 +757,7 @@ int bpf__foreach_event(struct bpf_object *obj, struct perf_probe_event *pev; int i, fd; - if (IS_ERR(priv) || !priv) { + if (IS_ERR_OR_NULL(priv)) { pr_debug("bpf: failed to get private field\n"); return -BPF_LOADER_ERRNO__INTERNAL; } diff --git a/tools/perf/util/bpf_counter.h b/tools/perf/util/bpf_counter.h index 65ebaa6694..4dbf26408b 100644 --- a/tools/perf/util/bpf_counter.h +++ b/tools/perf/util/bpf_counter.h @@ -16,7 +16,7 @@ typedef int (*bpf_counter_evsel_op)(struct evsel *evsel); typedef int (*bpf_counter_evsel_target_op)(struct evsel *evsel, struct target *target); typedef int (*bpf_counter_evsel_install_pe_op)(struct evsel *evsel, - int cpu, + int cpu_map_idx, int fd); struct bpf_counter_ops { @@ -40,7 +40,7 @@ int bpf_counter__enable(struct evsel *evsel); int bpf_counter__disable(struct evsel *evsel); int bpf_counter__read(struct evsel *evsel); void bpf_counter__destroy(struct evsel *evsel); -int bpf_counter__install_pe(struct evsel *evsel, int cpu, int fd); +int bpf_counter__install_pe(struct evsel *evsel, int cpu_map_idx, int fd); #else /* HAVE_BPF_SKEL */ diff --git a/tools/perf/util/bpf_counter_cgroup.c b/tools/perf/util/bpf_counter_cgroup.c index 89aa5e71db..ac60c08e8e 100644 --- a/tools/perf/util/bpf_counter_cgroup.c +++ b/tools/perf/util/bpf_counter_cgroup.c @@ -48,7 +48,7 @@ static int bperf_load_program(struct evlist *evlist) struct cgroup *cgrp, *leader_cgrp; __u32 i, cpu; __u32 nr_cpus = evlist->core.all_cpus->nr; - int total_cpus = cpu__max_cpu(); + int total_cpus = cpu__max_cpu().cpu; int map_size, map_fd; int prog_fd, err; @@ -65,14 +65,14 @@ static int bperf_load_program(struct evlist *evlist) /* we need one copy of events per cpu for reading */ map_size = total_cpus * evlist->core.nr_entries / nr_cgroups; - bpf_map__resize(skel->maps.events, map_size); - bpf_map__resize(skel->maps.cgrp_idx, nr_cgroups); + bpf_map__set_max_entries(skel->maps.events, map_size); + bpf_map__set_max_entries(skel->maps.cgrp_idx, nr_cgroups); /* previous result is saved in a per-cpu array */ map_size = evlist->core.nr_entries / nr_cgroups; - bpf_map__resize(skel->maps.prev_readings, map_size); + bpf_map__set_max_entries(skel->maps.prev_readings, map_size); /* cgroup result needs all events (per-cpu) */ map_size = evlist->core.nr_entries; - bpf_map__resize(skel->maps.cgrp_readings, map_size); + bpf_map__set_max_entries(skel->maps.cgrp_readings, map_size); set_max_rlimit(); @@ -125,7 +125,7 @@ static int bperf_load_program(struct evlist *evlist) for (cpu = 0; cpu < nr_cpus; cpu++) { int fd = FD(evsel, cpu); __u32 idx = evsel->core.idx * total_cpus + - evlist->core.all_cpus->map[cpu]; + evlist->core.all_cpus->map[cpu].cpu; err = bpf_map_update_elem(map_fd, &idx, &fd, BPF_ANY); @@ -212,7 +212,7 @@ static int bperf_cgrp__sync_counters(struct evlist *evlist) int prog_fd = bpf_program__fd(skel->progs.trigger_read); for (i = 0; i < nr_cpus; i++) { - cpu = evlist->core.all_cpus->map[i]; + cpu = evlist->core.all_cpus->map[i].cpu; bperf_trigger_reading(prog_fd, cpu); } @@ -245,7 +245,7 @@ static int bperf_cgrp__read(struct evsel *evsel) { struct evlist *evlist = evsel->evlist; int i, cpu, nr_cpus = evlist->core.all_cpus->nr; - int total_cpus = cpu__max_cpu(); + int total_cpus = cpu__max_cpu().cpu; struct perf_counts_values *counts; struct bpf_perf_event_value *values; int reading_map_fd, err = 0; @@ -266,13 +266,13 @@ static int bperf_cgrp__read(struct evsel *evsel) idx = evsel->core.idx; err = bpf_map_lookup_elem(reading_map_fd, &idx, values); if (err) { - pr_err("bpf map lookup falied: idx=%u, event=%s, cgrp=%s\n", + pr_err("bpf map lookup failed: idx=%u, event=%s, cgrp=%s\n", idx, evsel__name(evsel), evsel->cgrp->name); goto out; } for (i = 0; i < nr_cpus; i++) { - cpu = evlist->core.all_cpus->map[i]; + cpu = evlist->core.all_cpus->map[i].cpu; counts = perf_counts(evsel->counts, i, 0); counts->val = values[cpu].counter; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 5824134f98..d95615daed 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -280,6 +280,8 @@ static inline int arch_skip_callchain_idx(struct thread *thread __maybe_unused, } #endif +void arch__add_leaf_frame_record_opts(struct record_opts *opts); + char *callchain_list__sym_name(struct callchain_list *cl, char *bf, size_t bfsize, bool show_dso); char *callchain_node__scnprintf_value(struct callchain_node *node, @@ -298,7 +300,7 @@ int callchain_branch_counts(struct callchain_root *root, u64 *branch_count, u64 *predicted_count, u64 *abort_count, u64 *cycles_count); -void callchain_param_setup(u64 sample_type); +void callchain_param_setup(u64 sample_type, const char *arch); bool callchain_cnode_matched(struct callchain_node *base_cnode, struct callchain_node *pair_cnode); diff --git a/tools/perf/util/counts.h b/tools/perf/util/counts.h index 7ff36bf6d6..5de275194f 100644 --- a/tools/perf/util/counts.h +++ b/tools/perf/util/counts.h @@ -18,21 +18,21 @@ struct perf_counts { static inline struct perf_counts_values* -perf_counts(struct perf_counts *counts, int cpu, int thread) +perf_counts(struct perf_counts *counts, int cpu_map_idx, int thread) { - return xyarray__entry(counts->values, cpu, thread); + return xyarray__entry(counts->values, cpu_map_idx, thread); } static inline bool -perf_counts__is_loaded(struct perf_counts *counts, int cpu, int thread) +perf_counts__is_loaded(struct perf_counts *counts, int cpu_map_idx, int thread) { - return *((bool *) xyarray__entry(counts->loaded, cpu, thread)); + return *((bool *) xyarray__entry(counts->loaded, cpu_map_idx, thread)); } static inline void -perf_counts__set_loaded(struct perf_counts *counts, int cpu, int thread, bool loaded) +perf_counts__set_loaded(struct perf_counts *counts, int cpu_map_idx, int thread, bool loaded) { - *((bool *) xyarray__entry(counts->loaded, cpu, thread)) = loaded; + *((bool *) xyarray__entry(counts->loaded, cpu_map_idx, thread)) = loaded; } struct perf_counts *perf_counts__new(int ncpus, int nthreads); @@ -40,7 +40,7 @@ void perf_counts__delete(struct perf_counts *counts); void perf_counts__reset(struct perf_counts *counts); void evsel__reset_counts(struct evsel *evsel); -int evsel__alloc_counts(struct evsel *evsel, int ncpus, int nthreads); +int evsel__alloc_counts(struct evsel *evsel); void evsel__free_counts(struct evsel *evsel); #endif /* __PERF_COUNTS_H */ diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 87d3eca9b8..12b2243222 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -13,9 +13,13 @@ #include #include -static int max_cpu_num; -static int max_present_cpu_num; +static struct perf_cpu max_cpu_num; +static struct perf_cpu max_present_cpu_num; static int max_node_num; +/** + * The numa node X as read from /sys/devices/system/node/nodeX indexed by the + * CPU number. + */ static int *cpunode_map; static struct perf_cpu_map *cpu_map__from_entries(struct cpu_map_entries *cpus) @@ -33,9 +37,9 @@ static struct perf_cpu_map *cpu_map__from_entries(struct cpu_map_entries *cpus) * otherwise it would become 65535. */ if (cpus->cpu[i] == (u16) -1) - map->map[i] = -1; + map->map[i].cpu = -1; else - map->map[i] = (int) cpus->cpu[i]; + map->map[i].cpu = (int) cpus->cpu[i]; } } @@ -54,7 +58,7 @@ static struct perf_cpu_map *cpu_map__from_mask(struct perf_record_record_cpu_map int cpu, i = 0; for_each_set_bit(cpu, mask->mask, nbits) - map->map[i++] = cpu; + map->map[i++].cpu = cpu; } return map; @@ -87,7 +91,7 @@ struct perf_cpu_map *perf_cpu_map__empty_new(int nr) cpus->nr = nr; for (i = 0; i < nr; i++) - cpus->map[i] = -1; + cpus->map[i].cpu = -1; refcount_set(&cpus->refcnt, 1); } @@ -104,7 +108,7 @@ struct cpu_aggr_map *cpu_aggr_map__empty_new(int nr) cpus->nr = nr; for (i = 0; i < nr; i++) - cpus->map[i] = cpu_map__empty_aggr_cpu_id(); + cpus->map[i] = aggr_cpu_id__empty(); refcount_set(&cpus->refcnt, 1); } @@ -122,28 +126,21 @@ static int cpu__get_topology_int(int cpu, const char *name, int *value) return sysfs__read_int(path, value); } -int cpu_map__get_socket_id(int cpu) +int cpu__get_socket_id(struct perf_cpu cpu) { - int value, ret = cpu__get_topology_int(cpu, "physical_package_id", &value); + int value, ret = cpu__get_topology_int(cpu.cpu, "physical_package_id", &value); return ret ?: value; } -struct aggr_cpu_id cpu_map__get_socket(struct perf_cpu_map *map, int idx, - void *data __maybe_unused) +struct aggr_cpu_id aggr_cpu_id__socket(struct perf_cpu cpu, void *data __maybe_unused) { - int cpu; - struct aggr_cpu_id id = cpu_map__empty_aggr_cpu_id(); + struct aggr_cpu_id id = aggr_cpu_id__empty(); - if (idx > map->nr) - return id; - - cpu = map->map[idx]; - - id.socket = cpu_map__get_socket_id(cpu); + id.socket = cpu__get_socket_id(cpu); return id; } -static int cmp_aggr_cpu_id(const void *a_pointer, const void *b_pointer) +static int aggr_cpu_id__cmp(const void *a_pointer, const void *b_pointer) { struct aggr_cpu_id *a = (struct aggr_cpu_id *)a_pointer; struct aggr_cpu_id *b = (struct aggr_cpu_id *)b_pointer; @@ -160,57 +157,64 @@ static int cmp_aggr_cpu_id(const void *a_pointer, const void *b_pointer) return a->thread - b->thread; } -int cpu_map__build_map(struct perf_cpu_map *cpus, struct cpu_aggr_map **res, - struct aggr_cpu_id (*f)(struct perf_cpu_map *map, int cpu, void *data), - void *data) +struct cpu_aggr_map *cpu_aggr_map__new(const struct perf_cpu_map *cpus, + aggr_cpu_id_get_t get_id, + void *data) { - int nr = cpus->nr; - struct cpu_aggr_map *c = cpu_aggr_map__empty_new(nr); - int cpu, s2; - struct aggr_cpu_id s1; + int idx; + struct perf_cpu cpu; + struct cpu_aggr_map *c = cpu_aggr_map__empty_new(cpus->nr); if (!c) - return -1; + return NULL; /* Reset size as it may only be partially filled */ c->nr = 0; - for (cpu = 0; cpu < nr; cpu++) { - s1 = f(cpus, cpu, data); - for (s2 = 0; s2 < c->nr; s2++) { - if (cpu_map__compare_aggr_cpu_id(s1, c->map[s2])) + perf_cpu_map__for_each_cpu(cpu, idx, cpus) { + bool duplicate = false; + struct aggr_cpu_id cpu_id = get_id(cpu, data); + + for (int j = 0; j < c->nr; j++) { + if (aggr_cpu_id__equal(&cpu_id, &c->map[j])) { + duplicate = true; break; + } } - if (s2 == c->nr) { - c->map[c->nr] = s1; + if (!duplicate) { + c->map[c->nr] = cpu_id; c->nr++; } } - /* ensure we process id in increasing order */ - qsort(c->map, c->nr, sizeof(struct aggr_cpu_id), cmp_aggr_cpu_id); + /* Trim. */ + if (c->nr != cpus->nr) { + struct cpu_aggr_map *trimmed_c = + realloc(c, + sizeof(struct cpu_aggr_map) + sizeof(struct aggr_cpu_id) * c->nr); + + if (trimmed_c) + c = trimmed_c; + } + /* ensure we process id in increasing order */ + qsort(c->map, c->nr, sizeof(struct aggr_cpu_id), aggr_cpu_id__cmp); + + return c; - *res = c; - return 0; } -int cpu_map__get_die_id(int cpu) +int cpu__get_die_id(struct perf_cpu cpu) { - int value, ret = cpu__get_topology_int(cpu, "die_id", &value); + int value, ret = cpu__get_topology_int(cpu.cpu, "die_id", &value); return ret ?: value; } -struct aggr_cpu_id cpu_map__get_die(struct perf_cpu_map *map, int idx, void *data) +struct aggr_cpu_id aggr_cpu_id__die(struct perf_cpu cpu, void *data) { - int cpu, die; - struct aggr_cpu_id id = cpu_map__empty_aggr_cpu_id(); + struct aggr_cpu_id id; + int die; - if (idx > map->nr) - return id; - - cpu = map->map[idx]; - - die = cpu_map__get_die_id(cpu); + die = cpu__get_die_id(cpu); /* There is no die_id on legacy system. */ if (die == -1) die = 0; @@ -220,79 +224,59 @@ struct aggr_cpu_id cpu_map__get_die(struct perf_cpu_map *map, int idx, void *dat * with the socket ID and then add die to * make a unique ID. */ - id = cpu_map__get_socket(map, idx, data); - if (cpu_map__aggr_cpu_id_is_empty(id)) + id = aggr_cpu_id__socket(cpu, data); + if (aggr_cpu_id__is_empty(&id)) return id; id.die = die; return id; } -int cpu_map__get_core_id(int cpu) +int cpu__get_core_id(struct perf_cpu cpu) { - int value, ret = cpu__get_topology_int(cpu, "core_id", &value); + int value, ret = cpu__get_topology_int(cpu.cpu, "core_id", &value); return ret ?: value; } -int cpu_map__get_node_id(int cpu) +struct aggr_cpu_id aggr_cpu_id__core(struct perf_cpu cpu, void *data) { - return cpu__get_node(cpu); -} + struct aggr_cpu_id id; + int core = cpu__get_core_id(cpu); -struct aggr_cpu_id cpu_map__get_core(struct perf_cpu_map *map, int idx, void *data) -{ - int cpu; - struct aggr_cpu_id id = cpu_map__empty_aggr_cpu_id(); - - if (idx > map->nr) - return id; - - cpu = map->map[idx]; - - cpu = cpu_map__get_core_id(cpu); - - /* cpu_map__get_die returns a struct with socket and die set*/ - id = cpu_map__get_die(map, idx, data); - if (cpu_map__aggr_cpu_id_is_empty(id)) + /* aggr_cpu_id__die returns a struct with socket and die set. */ + id = aggr_cpu_id__die(cpu, data); + if (aggr_cpu_id__is_empty(&id)) return id; /* * core_id is relative to socket and die, we need a global id. * So we combine the result from cpu_map__get_die with the core id */ - id.core = cpu; + id.core = core; return id; + } -struct aggr_cpu_id cpu_map__get_node(struct perf_cpu_map *map, int idx, void *data __maybe_unused) +struct aggr_cpu_id aggr_cpu_id__cpu(struct perf_cpu cpu, void *data) { - struct aggr_cpu_id id = cpu_map__empty_aggr_cpu_id(); + struct aggr_cpu_id id; - if (idx < 0 || idx >= map->nr) + /* aggr_cpu_id__core returns a struct with socket, die and core set. */ + id = aggr_cpu_id__core(cpu, data); + if (aggr_cpu_id__is_empty(&id)) return id; - id.node = cpu_map__get_node_id(map->map[idx]); + id.cpu = cpu; return id; + } -int cpu_map__build_socket_map(struct perf_cpu_map *cpus, struct cpu_aggr_map **sockp) +struct aggr_cpu_id aggr_cpu_id__node(struct perf_cpu cpu, void *data __maybe_unused) { - return cpu_map__build_map(cpus, sockp, cpu_map__get_socket, NULL); -} + struct aggr_cpu_id id = aggr_cpu_id__empty(); -int cpu_map__build_die_map(struct perf_cpu_map *cpus, struct cpu_aggr_map **diep) -{ - return cpu_map__build_map(cpus, diep, cpu_map__get_die, NULL); -} - -int cpu_map__build_core_map(struct perf_cpu_map *cpus, struct cpu_aggr_map **corep) -{ - return cpu_map__build_map(cpus, corep, cpu_map__get_core, NULL); -} - -int cpu_map__build_node_map(struct perf_cpu_map *cpus, struct cpu_aggr_map **numap) -{ - return cpu_map__build_map(cpus, numap, cpu_map__get_node, NULL); + id.node = cpu__get_node(cpu); + return id; } /* setup simple routines to easily access node numbers given a cpu number */ @@ -335,8 +319,8 @@ static void set_max_cpu_num(void) int ret = -1; /* set up default */ - max_cpu_num = 4096; - max_present_cpu_num = 4096; + max_cpu_num.cpu = 4096; + max_present_cpu_num.cpu = 4096; mnt = sysfs__mountpoint(); if (!mnt) @@ -349,7 +333,7 @@ static void set_max_cpu_num(void) goto out; } - ret = get_max_num(path, &max_cpu_num); + ret = get_max_num(path, &max_cpu_num.cpu); if (ret) goto out; @@ -360,11 +344,11 @@ static void set_max_cpu_num(void) goto out; } - ret = get_max_num(path, &max_present_cpu_num); + ret = get_max_num(path, &max_present_cpu_num.cpu); out: if (ret) - pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num); + pr_err("Failed to read max cpus, using default of %d\n", max_cpu_num.cpu); } /* Determine highest possible node in the system for sparse allocation */ @@ -403,31 +387,31 @@ int cpu__max_node(void) return max_node_num; } -int cpu__max_cpu(void) +struct perf_cpu cpu__max_cpu(void) { - if (unlikely(!max_cpu_num)) + if (unlikely(!max_cpu_num.cpu)) set_max_cpu_num(); return max_cpu_num; } -int cpu__max_present_cpu(void) +struct perf_cpu cpu__max_present_cpu(void) { - if (unlikely(!max_present_cpu_num)) + if (unlikely(!max_present_cpu_num.cpu)) set_max_cpu_num(); return max_present_cpu_num; } -int cpu__get_node(int cpu) +int cpu__get_node(struct perf_cpu cpu) { if (unlikely(cpunode_map == NULL)) { pr_debug("cpu_map not initialized\n"); return -1; } - return cpunode_map[cpu]; + return cpunode_map[cpu.cpu]; } static int init_cpunode_map(void) @@ -437,13 +421,13 @@ static int init_cpunode_map(void) set_max_cpu_num(); set_max_node_num(); - cpunode_map = calloc(max_cpu_num, sizeof(int)); + cpunode_map = calloc(max_cpu_num.cpu, sizeof(int)); if (!cpunode_map) { pr_err("%s: calloc failed\n", __func__); return -1; } - for (i = 0; i < max_cpu_num; i++) + for (i = 0; i < max_cpu_num.cpu; i++) cpunode_map[i] = -1; return 0; @@ -502,47 +486,39 @@ int cpu__setup_cpunode_map(void) return 0; } -bool cpu_map__has(struct perf_cpu_map *cpus, int cpu) -{ - return perf_cpu_map__idx(cpus, cpu) != -1; -} - -int cpu_map__cpu(struct perf_cpu_map *cpus, int idx) -{ - return cpus->map[idx]; -} - size_t cpu_map__snprint(struct perf_cpu_map *map, char *buf, size_t size) { - int i, cpu, start = -1; + int i, start = -1; bool first = true; size_t ret = 0; #define COMMA first ? "" : "," for (i = 0; i < map->nr + 1; i++) { + struct perf_cpu cpu = { .cpu = INT_MAX }; bool last = i == map->nr; - cpu = last ? INT_MAX : map->map[i]; + if (!last) + cpu = map->map[i]; if (start == -1) { start = i; if (last) { ret += snprintf(buf + ret, size - ret, "%s%d", COMMA, - map->map[i]); + map->map[i].cpu); } - } else if (((i - start) != (cpu - map->map[start])) || last) { + } else if (((i - start) != (cpu.cpu - map->map[start].cpu)) || last) { int end = i - 1; if (start == end) { ret += snprintf(buf + ret, size - ret, "%s%d", COMMA, - map->map[start]); + map->map[start].cpu); } else { ret += snprintf(buf + ret, size - ret, "%s%d-%d", COMMA, - map->map[start], map->map[end]); + map->map[start].cpu, map->map[end].cpu); } first = false; start = i; @@ -569,23 +545,23 @@ size_t cpu_map__snprint_mask(struct perf_cpu_map *map, char *buf, size_t size) int i, cpu; char *ptr = buf; unsigned char *bitmap; - int last_cpu = cpu_map__cpu(map, map->nr - 1); + struct perf_cpu last_cpu = perf_cpu_map__cpu(map, map->nr - 1); if (buf == NULL) return 0; - bitmap = zalloc(last_cpu / 8 + 1); + bitmap = zalloc(last_cpu.cpu / 8 + 1); if (bitmap == NULL) { buf[0] = '\0'; return 0; } for (i = 0; i < map->nr; i++) { - cpu = cpu_map__cpu(map, i); + cpu = perf_cpu_map__cpu(map, i).cpu; bitmap[cpu / 8] |= 1 << (cpu % 8); } - for (cpu = last_cpu / 4 * 4; cpu >= 0; cpu -= 4) { + for (cpu = last_cpu.cpu / 4 * 4; cpu >= 0; cpu -= 4) { unsigned char bits = bitmap[cpu / 8]; if (cpu % 8) @@ -614,32 +590,35 @@ const struct perf_cpu_map *cpu_map__online(void) /* thread unsafe */ return online; } -bool cpu_map__compare_aggr_cpu_id(struct aggr_cpu_id a, struct aggr_cpu_id b) +bool aggr_cpu_id__equal(const struct aggr_cpu_id *a, const struct aggr_cpu_id *b) { - return a.thread == b.thread && - a.node == b.node && - a.socket == b.socket && - a.die == b.die && - a.core == b.core; + return a->thread == b->thread && + a->node == b->node && + a->socket == b->socket && + a->die == b->die && + a->core == b->core && + a->cpu.cpu == b->cpu.cpu; } -bool cpu_map__aggr_cpu_id_is_empty(struct aggr_cpu_id a) +bool aggr_cpu_id__is_empty(const struct aggr_cpu_id *a) { - return a.thread == -1 && - a.node == -1 && - a.socket == -1 && - a.die == -1 && - a.core == -1; + return a->thread == -1 && + a->node == -1 && + a->socket == -1 && + a->die == -1 && + a->core == -1 && + a->cpu.cpu == -1; } -struct aggr_cpu_id cpu_map__empty_aggr_cpu_id(void) +struct aggr_cpu_id aggr_cpu_id__empty(void) { struct aggr_cpu_id ret = { .thread = -1, .node = -1, .socket = -1, .die = -1, - .core = -1 + .core = -1, + .cpu = (struct perf_cpu){ .cpu = -1 }, }; return ret; } diff --git a/tools/perf/util/cputopo.c b/tools/perf/util/cputopo.c index ec77e2a7b3..d275d843c1 100644 --- a/tools/perf/util/cputopo.c +++ b/tools/perf/util/cputopo.c @@ -14,14 +14,16 @@ #include "env.h" #include "pmu-hybrid.h" -#define CORE_SIB_FMT \ +#define PACKAGE_CPUS_FMT \ + "%s/devices/system/cpu/cpu%d/topology/package_cpus_list" +#define PACKAGE_CPUS_FMT_OLD \ "%s/devices/system/cpu/cpu%d/topology/core_siblings_list" -#define DIE_SIB_FMT \ +#define DIE_CPUS_FMT \ "%s/devices/system/cpu/cpu%d/topology/die_cpus_list" -#define THRD_SIB_FMT \ - "%s/devices/system/cpu/cpu%d/topology/thread_siblings_list" -#define THRD_SIB_FMT_NEW \ +#define CORE_CPUS_FMT \ "%s/devices/system/cpu/cpu%d/topology/core_cpus_list" +#define CORE_CPUS_FMT_OLD \ + "%s/devices/system/cpu/cpu%d/topology/thread_siblings_list" #define NODE_ONLINE_FMT \ "%s/devices/system/node/online" #define NODE_MEMINFO_FMT \ @@ -39,8 +41,12 @@ static int build_cpu_topology(struct cpu_topology *tp, int cpu) u32 i = 0; int ret = -1; - scnprintf(filename, MAXPATHLEN, CORE_SIB_FMT, + scnprintf(filename, MAXPATHLEN, PACKAGE_CPUS_FMT, sysfs__mountpoint(), cpu); + if (access(filename, F_OK) == -1) { + scnprintf(filename, MAXPATHLEN, PACKAGE_CPUS_FMT_OLD, + sysfs__mountpoint(), cpu); + } fp = fopen(filename, "r"); if (!fp) goto try_dies; @@ -54,23 +60,23 @@ static int build_cpu_topology(struct cpu_topology *tp, int cpu) if (p) *p = '\0'; - for (i = 0; i < tp->core_sib; i++) { - if (!strcmp(buf, tp->core_siblings[i])) + for (i = 0; i < tp->package_cpus_lists; i++) { + if (!strcmp(buf, tp->package_cpus_list[i])) break; } - if (i == tp->core_sib) { - tp->core_siblings[i] = buf; - tp->core_sib++; + if (i == tp->package_cpus_lists) { + tp->package_cpus_list[i] = buf; + tp->package_cpus_lists++; buf = NULL; len = 0; } ret = 0; try_dies: - if (!tp->die_siblings) + if (!tp->die_cpus_list) goto try_threads; - scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT, + scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT, sysfs__mountpoint(), cpu); fp = fopen(filename, "r"); if (!fp) @@ -85,23 +91,23 @@ static int build_cpu_topology(struct cpu_topology *tp, int cpu) if (p) *p = '\0'; - for (i = 0; i < tp->die_sib; i++) { - if (!strcmp(buf, tp->die_siblings[i])) + for (i = 0; i < tp->die_cpus_lists; i++) { + if (!strcmp(buf, tp->die_cpus_list[i])) break; } - if (i == tp->die_sib) { - tp->die_siblings[i] = buf; - tp->die_sib++; + if (i == tp->die_cpus_lists) { + tp->die_cpus_list[i] = buf; + tp->die_cpus_lists++; buf = NULL; len = 0; } ret = 0; try_threads: - scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT_NEW, + scnprintf(filename, MAXPATHLEN, CORE_CPUS_FMT, sysfs__mountpoint(), cpu); if (access(filename, F_OK) == -1) { - scnprintf(filename, MAXPATHLEN, THRD_SIB_FMT, + scnprintf(filename, MAXPATHLEN, CORE_CPUS_FMT_OLD, sysfs__mountpoint(), cpu); } fp = fopen(filename, "r"); @@ -115,13 +121,13 @@ static int build_cpu_topology(struct cpu_topology *tp, int cpu) if (p) *p = '\0'; - for (i = 0; i < tp->thread_sib; i++) { - if (!strcmp(buf, tp->thread_siblings[i])) + for (i = 0; i < tp->core_cpus_lists; i++) { + if (!strcmp(buf, tp->core_cpus_list[i])) break; } - if (i == tp->thread_sib) { - tp->thread_siblings[i] = buf; - tp->thread_sib++; + if (i == tp->core_cpus_lists) { + tp->core_cpus_list[i] = buf; + tp->core_cpus_lists++; buf = NULL; } ret = 0; @@ -139,16 +145,14 @@ void cpu_topology__delete(struct cpu_topology *tp) if (!tp) return; - for (i = 0 ; i < tp->core_sib; i++) - zfree(&tp->core_siblings[i]); + for (i = 0 ; i < tp->package_cpus_lists; i++) + zfree(&tp->package_cpus_list[i]); - if (tp->die_sib) { - for (i = 0 ; i < tp->die_sib; i++) - zfree(&tp->die_siblings[i]); - } + for (i = 0 ; i < tp->die_cpus_lists; i++) + zfree(&tp->die_cpus_list[i]); - for (i = 0 ; i < tp->thread_sib; i++) - zfree(&tp->thread_siblings[i]); + for (i = 0 ; i < tp->core_cpus_lists; i++) + zfree(&tp->core_cpus_list[i]); free(tp); } @@ -161,10 +165,11 @@ static bool has_die_topology(void) if (uname(&uts) < 0) return false; - if (strncmp(uts.machine, "x86_64", 6)) + if (strncmp(uts.machine, "x86_64", 6) && + strncmp(uts.machine, "s390x", 5)) return false; - scnprintf(filename, MAXPATHLEN, DIE_SIB_FMT, + scnprintf(filename, MAXPATHLEN, DIE_CPUS_FMT, sysfs__mountpoint(), 0); if (access(filename, F_OK) == -1) return false; @@ -183,7 +188,7 @@ struct cpu_topology *cpu_topology__new(void) struct perf_cpu_map *map; bool has_die = has_die_topology(); - ncpus = cpu__max_present_cpu(); + ncpus = cpu__max_present_cpu().cpu; /* build online CPU map */ map = perf_cpu_map__new(NULL); @@ -205,16 +210,16 @@ struct cpu_topology *cpu_topology__new(void) tp = addr; addr += sizeof(*tp); - tp->core_siblings = addr; + tp->package_cpus_list = addr; addr += sz; if (has_die) { - tp->die_siblings = addr; + tp->die_cpus_list = addr; addr += sz; } - tp->thread_siblings = addr; + tp->core_cpus_list = addr; for (i = 0; i < nr; i++) { - if (!cpu_map__has(map, i)) + if (!perf_cpu_map__has(map, (struct perf_cpu){ .cpu = i })) continue; ret = build_cpu_topology(tp, i); @@ -320,7 +325,7 @@ struct numa_topology *numa_topology__new(void) if (!node_map) goto out; - nr = (u32) node_map->nr; + nr = (u32) perf_cpu_map__nr(node_map); tp = zalloc(sizeof(*tp) + sizeof(tp->nodes[0])*nr); if (!tp) @@ -329,7 +334,7 @@ struct numa_topology *numa_topology__new(void) tp->nr = nr; for (i = 0; i < nr; i++) { - if (load_numa_node(&tp->nodes[i], node_map->map[i])) { + if (load_numa_node(&tp->nodes[i], perf_cpu_map__cpu(node_map, i).cpu)) { numa_topology__delete(tp); tp = NULL; break; diff --git a/tools/perf/util/cputopo.h b/tools/perf/util/cputopo.h index d9af971770..854e18f904 100644 --- a/tools/perf/util/cputopo.h +++ b/tools/perf/util/cputopo.h @@ -5,12 +5,33 @@ #include struct cpu_topology { - u32 core_sib; - u32 die_sib; - u32 thread_sib; - char **core_siblings; - char **die_siblings; - char **thread_siblings; + /* The number of unique package_cpus_lists below. */ + u32 package_cpus_lists; + /* The number of unique die_cpu_lists below. */ + u32 die_cpus_lists; + /* The number of unique core_cpu_lists below. */ + u32 core_cpus_lists; + /* + * An array of strings where each string is unique and read from + * /sys/devices/system/cpu/cpuX/topology/package_cpus_list. From the ABI + * each of these is a human-readable list of CPUs sharing the same + * physical_package_id. The format is like 0-3, 8-11, 14,17. + */ + const char **package_cpus_list; + /* + * An array of string where each string is unique and from + * /sys/devices/system/cpu/cpuX/topology/die_cpus_list. From the ABI + * each of these is a human-readable list of CPUs within the same die. + * The format is like 0-3, 8-11, 14,17. + */ + const char **die_cpus_list; + /* + * An array of string where each string is unique and from + * /sys/devices/system/cpu/cpuX/topology/core_cpus_list. From the ABI + * each of these is a human-readable list of CPUs within the same + * core. The format is like 0-3, 8-11, 14,17. + */ + const char **core_cpus_list; }; struct numa_topology_node { diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index aa862a26d9..9e0aee276d 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -318,6 +318,8 @@ static int add_tracepoint_field_value(struct ctf_writer *cw, offset = tmp_val; len = offset >> 16; offset &= 0xffff; + if (flags & TEP_FIELD_IS_RELATIVE) + offset += fmtf->offset + fmtf->size; } if (flags & TEP_FIELD_IS_ARRAY) { @@ -1437,7 +1439,7 @@ static struct bt_ctf_field_type *create_int_type(int size, bool sign, bool hex) bt_ctf_field_type_integer_set_base(type, BT_CTF_INTEGER_BASE_HEXADECIMAL)) goto err; -#if __BYTE_ORDER == __BIG_ENDIAN +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ bt_ctf_field_type_set_byte_order(type, BT_CTF_BYTE_ORDER_BIG_ENDIAN); #else bt_ctf_field_type_set_byte_order(type, BT_CTF_BYTE_ORDER_LITTLE_ENDIAN); diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 9ed9a5676d..9cc8a1772b 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -14,6 +14,7 @@ #ifdef HAVE_LIBBPF_SUPPORT #include #include "bpf-event.h" +#include "bpf-utils.h" #endif #include "compress.h" #include "env.h" diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 83723ba11d..011da3924f 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -193,7 +193,7 @@ struct dso { int fd; int status; u32 status_seen; - size_t file_size; + u64 file_size; struct list_head open_entry; u64 debug_frame_offset; u64 eh_frame_hdr_offset; diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 163e5ec503..a3541f98e1 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -4,6 +4,7 @@ #include #include +#include "cpumap.h" #include "rwsem.h" struct perf_cpu_map; @@ -170,5 +171,5 @@ struct bpf_prog_info_node *perf_env__find_bpf_prog_info(struct perf_env *env, bool perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node); struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id); -int perf_env__numa_node(struct perf_env *env, int cpu); +int perf_env__numa_node(struct perf_env *env, struct perf_cpu cpu); #endif /* __PERF_ENV_H */ diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 5f92319ce2..41a66a48cb 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -342,36 +342,71 @@ static int evlist__nr_threads(struct evlist *evlist, struct evsel *evsel) return perf_thread_map__nr(evlist->core.threads); } -void evlist__cpu_iter_start(struct evlist *evlist) +struct evlist_cpu_iterator evlist__cpu_begin(struct evlist *evlist, struct affinity *affinity) { - struct evsel *pos; + struct evlist_cpu_iterator itr = { + .container = evlist, + .evsel = NULL, + .cpu_map_idx = 0, + .evlist_cpu_map_idx = 0, + .evlist_cpu_map_nr = perf_cpu_map__nr(evlist->core.all_cpus), + .cpu = (struct perf_cpu){ .cpu = -1}, + .affinity = affinity, + }; - /* - * Reset the per evsel cpu_iter. This is needed because - * each evsel's cpumap may have a different index space, - * and some operations need the index to modify - * the FD xyarray (e.g. open, close) - */ - evlist__for_each_entry(evlist, pos) - pos->cpu_iter = 0; -} - -bool evsel__cpu_iter_skip_no_inc(struct evsel *ev, int cpu) -{ - if (ev->cpu_iter >= ev->core.cpus->nr) - return true; - if (cpu >= 0 && ev->core.cpus->map[ev->cpu_iter] != cpu) - return true; - return false; -} - -bool evsel__cpu_iter_skip(struct evsel *ev, int cpu) -{ - if (!evsel__cpu_iter_skip_no_inc(ev, cpu)) { - ev->cpu_iter++; - return false; + if (evlist__empty(evlist)) { + /* Ensure the empty list doesn't iterate. */ + itr.evlist_cpu_map_idx = itr.evlist_cpu_map_nr; + } else { + itr.evsel = evlist__first(evlist); + if (itr.affinity) { + itr.cpu = perf_cpu_map__cpu(evlist->core.all_cpus, 0); + affinity__set(itr.affinity, itr.cpu.cpu); + itr.cpu_map_idx = perf_cpu_map__idx(itr.evsel->core.cpus, itr.cpu); + /* + * If this CPU isn't in the evsel's cpu map then advance + * through the list. + */ + if (itr.cpu_map_idx == -1) + evlist_cpu_iterator__next(&itr); + } } - return true; + return itr; +} + +void evlist_cpu_iterator__next(struct evlist_cpu_iterator *evlist_cpu_itr) +{ + while (evlist_cpu_itr->evsel != evlist__last(evlist_cpu_itr->container)) { + evlist_cpu_itr->evsel = evsel__next(evlist_cpu_itr->evsel); + evlist_cpu_itr->cpu_map_idx = + perf_cpu_map__idx(evlist_cpu_itr->evsel->core.cpus, + evlist_cpu_itr->cpu); + if (evlist_cpu_itr->cpu_map_idx != -1) + return; + } + evlist_cpu_itr->evlist_cpu_map_idx++; + if (evlist_cpu_itr->evlist_cpu_map_idx < evlist_cpu_itr->evlist_cpu_map_nr) { + evlist_cpu_itr->evsel = evlist__first(evlist_cpu_itr->container); + evlist_cpu_itr->cpu = + perf_cpu_map__cpu(evlist_cpu_itr->container->core.all_cpus, + evlist_cpu_itr->evlist_cpu_map_idx); + if (evlist_cpu_itr->affinity) + affinity__set(evlist_cpu_itr->affinity, evlist_cpu_itr->cpu.cpu); + evlist_cpu_itr->cpu_map_idx = + perf_cpu_map__idx(evlist_cpu_itr->evsel->core.cpus, + evlist_cpu_itr->cpu); + /* + * If this CPU isn't in the evsel's cpu map then advance through + * the list. + */ + if (evlist_cpu_itr->cpu_map_idx == -1) + evlist_cpu_iterator__next(evlist_cpu_itr); + } +} + +bool evlist_cpu_iterator__end(const struct evlist_cpu_iterator *evlist_cpu_itr) +{ + return evlist_cpu_itr->evlist_cpu_map_idx >= evlist_cpu_itr->evlist_cpu_map_nr; } static int evsel__strcmp(struct evsel *pos, char *evsel_name) @@ -400,37 +435,36 @@ static int evlist__is_enabled(struct evlist *evlist) static void __evlist__disable(struct evlist *evlist, char *evsel_name) { struct evsel *pos; - struct affinity affinity; - int cpu, i, imm = 0; + struct evlist_cpu_iterator evlist_cpu_itr; + struct affinity saved_affinity, *affinity = NULL; bool has_imm = false; - if (affinity__setup(&affinity) < 0) - return; + // See explanation in evlist__close() + if (!cpu_map__is_dummy(evlist->core.cpus)) { + if (affinity__setup(&saved_affinity) < 0) + return; + affinity = &saved_affinity; + } /* Disable 'immediate' events last */ - for (imm = 0; imm <= 1; imm++) { - evlist__for_each_cpu(evlist, i, cpu) { - affinity__set(&affinity, cpu); - - evlist__for_each_entry(evlist, pos) { - if (evsel__strcmp(pos, evsel_name)) - continue; - if (evsel__cpu_iter_skip(pos, cpu)) - continue; - if (pos->disabled || !evsel__is_group_leader(pos) || !pos->core.fd) - continue; - if (pos->immediate) - has_imm = true; - if (pos->immediate != imm) - continue; - evsel__disable_cpu(pos, pos->cpu_iter - 1); - } + for (int imm = 0; imm <= 1; imm++) { + evlist__for_each_cpu(evlist_cpu_itr, evlist, affinity) { + pos = evlist_cpu_itr.evsel; + if (evsel__strcmp(pos, evsel_name)) + continue; + if (pos->disabled || !evsel__is_group_leader(pos) || !pos->core.fd) + continue; + if (pos->immediate) + has_imm = true; + if (pos->immediate != imm) + continue; + evsel__disable_cpu(pos, evlist_cpu_itr.cpu_map_idx); } if (!has_imm) break; } - affinity__cleanup(&affinity); + affinity__cleanup(affinity); evlist__for_each_entry(evlist, pos) { if (evsel__strcmp(pos, evsel_name)) continue; @@ -462,26 +496,25 @@ void evlist__disable_evsel(struct evlist *evlist, char *evsel_name) static void __evlist__enable(struct evlist *evlist, char *evsel_name) { struct evsel *pos; - struct affinity affinity; - int cpu, i; + struct evlist_cpu_iterator evlist_cpu_itr; + struct affinity saved_affinity, *affinity = NULL; - if (affinity__setup(&affinity) < 0) - return; - - evlist__for_each_cpu(evlist, i, cpu) { - affinity__set(&affinity, cpu); - - evlist__for_each_entry(evlist, pos) { - if (evsel__strcmp(pos, evsel_name)) - continue; - if (evsel__cpu_iter_skip(pos, cpu)) - continue; - if (!evsel__is_group_leader(pos) || !pos->core.fd) - continue; - evsel__enable_cpu(pos, pos->cpu_iter - 1); - } + // See explanation in evlist__close() + if (!cpu_map__is_dummy(evlist->core.cpus)) { + if (affinity__setup(&saved_affinity) < 0) + return; + affinity = &saved_affinity; } - affinity__cleanup(&affinity); + + evlist__for_each_cpu(evlist_cpu_itr, evlist, affinity) { + pos = evlist_cpu_itr.evsel; + if (evsel__strcmp(pos, evsel_name)) + continue; + if (!evsel__is_group_leader(pos) || !pos->core.fd) + continue; + evsel__enable_cpu(pos, evlist_cpu_itr.cpu_map_idx); + } + affinity__cleanup(affinity); evlist__for_each_entry(evlist, pos) { if (evsel__strcmp(pos, evsel_name)) continue; @@ -800,7 +833,7 @@ perf_evlist__mmap_cb_get(struct perf_evlist *_evlist, bool overwrite, int idx) static int perf_evlist__mmap_cb_mmap(struct perf_mmap *_map, struct perf_mmap_param *_mp, - int output, int cpu) + int output, struct perf_cpu cpu) { struct mmap *map = container_of(_map, struct mmap, core); struct mmap_params *mp = container_of(_mp, struct mmap_params, core); @@ -1264,14 +1297,14 @@ void evlist__set_selected(struct evlist *evlist, struct evsel *evsel) void evlist__close(struct evlist *evlist) { struct evsel *evsel; + struct evlist_cpu_iterator evlist_cpu_itr; struct affinity affinity; - int cpu, i; /* * With perf record core.cpus is usually NULL. * Use the old method to handle this for now. */ - if (!evlist->core.cpus) { + if (!evlist->core.cpus || cpu_map__is_dummy(evlist->core.cpus)) { evlist__for_each_entry_reverse(evlist, evsel) evsel__close(evsel); return; @@ -1279,15 +1312,12 @@ void evlist__close(struct evlist *evlist) if (affinity__setup(&affinity) < 0) return; - evlist__for_each_cpu(evlist, i, cpu) { - affinity__set(&affinity, cpu); - evlist__for_each_entry_reverse(evlist, evsel) { - if (evsel__cpu_iter_skip(evsel, cpu)) - continue; - perf_evsel__close_cpu(&evsel->core, evsel->cpu_iter - 1); - } + evlist__for_each_cpu(evlist_cpu_itr, evlist, &affinity) { + perf_evsel__close_cpu(&evlist_cpu_itr.evsel->core, + evlist_cpu_itr.cpu_map_idx); } + affinity__cleanup(&affinity); evlist__for_each_entry_reverse(evlist, evsel) { perf_evsel__free_fd(&evsel->core); diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 1f7edfa856..041b42d33b 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -11,6 +11,7 @@ #include #include "symbol_conf.h" #include +#include struct bpf_object; struct cgroup; @@ -22,6 +23,7 @@ struct target; struct hashmap; struct bperf_leader_bpf; struct bperf_follower_bpf; +struct perf_pmu; typedef int (evsel__sb_cb_t)(union perf_event *event, void *data); @@ -68,6 +70,7 @@ struct evsel { double scale; const char *unit; struct cgroup *cgrp; + const char *metric_id; enum perf_tool_event tool_event; /* parse modifier helper */ int exclude_GH; @@ -119,7 +122,6 @@ struct evsel { bool errored; struct hashmap *per_pkg_mask; int err; - int cpu_iter; struct { evsel__sb_cb_t *cb; void *data; @@ -152,6 +154,9 @@ struct evsel { }; unsigned long open_flags; int precise_ip_original; + + /* for missing_features */ + struct perf_pmu *pmu; }; struct perf_missing_features { @@ -187,12 +192,9 @@ static inline struct perf_cpu_map *evsel__cpus(struct evsel *evsel) static inline int evsel__nr_cpus(struct evsel *evsel) { - return evsel__cpus(evsel)->nr; + return perf_cpu_map__nr(evsel__cpus(evsel)); } -void perf_counts_values__scale(struct perf_counts_values *count, - bool scale, s8 *pscaled); - void evsel__compute_deltas(struct evsel *evsel, int cpu, int thread, struct perf_counts_values *count); @@ -261,6 +263,7 @@ bool evsel__match_bpf_counter_events(const char *name); int __evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, char *bf, size_t size); const char *evsel__name(struct evsel *evsel); +const char *evsel__metric_id(const struct evsel *evsel); const char *evsel__group_name(struct evsel *evsel); int evsel__group_desc(struct evsel *evsel, char *buf, size_t size); @@ -277,16 +280,17 @@ void __evsel__reset_sample_bit(struct evsel *evsel, enum perf_event_sample_forma void evsel__set_sample_id(struct evsel *evsel, bool use_sample_identifier); void arch_evsel__set_sample_weight(struct evsel *evsel); +void arch_evsel__fixup_new_cycles(struct perf_event_attr *attr); int evsel__set_filter(struct evsel *evsel, const char *filter); int evsel__append_tp_filter(struct evsel *evsel, const char *filter); int evsel__append_addr_filter(struct evsel *evsel, const char *filter); -int evsel__enable_cpu(struct evsel *evsel, int cpu); +int evsel__enable_cpu(struct evsel *evsel, int cpu_map_idx); int evsel__enable(struct evsel *evsel); int evsel__disable(struct evsel *evsel); -int evsel__disable_cpu(struct evsel *evsel, int cpu); +int evsel__disable_cpu(struct evsel *evsel, int cpu_map_idx); -int evsel__open_per_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, int cpu); +int evsel__open_per_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, int cpu_map_idx); int evsel__open_per_thread(struct evsel *evsel, struct perf_thread_map *threads); int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus, struct perf_thread_map *threads); @@ -298,10 +302,6 @@ bool evsel__detect_missing_features(struct evsel *evsel); enum rlimit_action { NO_CHANGE, SET_TO_MAX, INCREASED_MAX }; bool evsel__increase_rlimit(enum rlimit_action *set_rlimit); -bool evsel__ignore_missing_thread(struct evsel *evsel, - int nr_cpus, int cpu, - struct perf_thread_map *threads, - int thread, int err); bool evsel__precise_ip_fallback(struct evsel *evsel); struct perf_sample; @@ -330,32 +330,32 @@ static inline bool evsel__match2(struct evsel *e1, struct evsel *e2) (e1->core.attr.config == e2->core.attr.config); } -int evsel__read_counter(struct evsel *evsel, int cpu, int thread); +int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread); -int __evsel__read_on_cpu(struct evsel *evsel, int cpu, int thread, bool scale); +int __evsel__read_on_cpu(struct evsel *evsel, int cpu_map_idx, int thread, bool scale); /** * evsel__read_on_cpu - Read out the results on a CPU and thread * * @evsel - event selector to read value - * @cpu - CPU of interest + * @cpu_map_idx - CPU of interest * @thread - thread of interest */ -static inline int evsel__read_on_cpu(struct evsel *evsel, int cpu, int thread) +static inline int evsel__read_on_cpu(struct evsel *evsel, int cpu_map_idx, int thread) { - return __evsel__read_on_cpu(evsel, cpu, thread, false); + return __evsel__read_on_cpu(evsel, cpu_map_idx, thread, false); } /** * evsel__read_on_cpu_scaled - Read out the results on a CPU and thread, scaled * * @evsel - event selector to read value - * @cpu - CPU of interest + * @cpu_map_idx - CPU of interest * @thread - thread of interest */ -static inline int evsel__read_on_cpu_scaled(struct evsel *evsel, int cpu, int thread) +static inline int evsel__read_on_cpu_scaled(struct evsel *evsel, int cpu_map_idx, int thread) { - return __evsel__read_on_cpu(evsel, cpu, thread, true); + return __evsel__read_on_cpu(evsel, cpu_map_idx, thread, true); } int evsel__parse_sample(struct evsel *evsel, union perf_event *event, @@ -482,4 +482,18 @@ struct evsel *evsel__leader(struct evsel *evsel); bool evsel__has_leader(struct evsel *evsel, struct evsel *leader); bool evsel__is_leader(struct evsel *evsel); void evsel__set_leader(struct evsel *evsel, struct evsel *leader); +int evsel__source_count(const struct evsel *evsel); + +/* + * Macro to swap the bit-field postition and size. + * Used when, + * - dont need to swap the entire u64 && + * - when u64 has variable bit-field sizes && + * - when presented in a host endian which is different + * than the source endian of the perf.data file + */ +#define bitfield_swap(src, pos, size) \ + ((((src) >> (pos)) & ((1ull << (size)) - 1)) << (63 - ((pos) + (size) - 1))) + +u64 evsel__bitfield_swap_branch_flags(u64 value); #endif /* __PERF_EVSEL_H */ diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c index a850fd0be3..675f318ce7 100644 --- a/tools/perf/util/expr.c +++ b/tools/perf/util/expr.c @@ -5,13 +5,18 @@ #include #include #include "metricgroup.h" +#include "cpumap.h" +#include "cputopo.h" #include "debug.h" #include "expr.h" #include "expr-bison.h" #include "expr-flex.h" +#include "smt.h" +#include #include #include #include +#include #ifdef PARSER_DEBUG extern int expr_debug; @@ -19,13 +24,15 @@ extern int expr_debug; struct expr_id_data { union { - double val; + struct { + double val; + int source_count; + } val; struct { double val; const char *metric_name; const char *metric_expr; } ref; - struct expr_id *parent; }; enum { @@ -35,8 +42,6 @@ struct expr_id_data { EXPR_ID_DATA__REF, /* A reference but the value has been computed. */ EXPR_ID_DATA__REF_VALUE, - /* A parent is remembered for the recursion check. */ - EXPR_ID_DATA__PARENT, } kind; }; @@ -59,21 +64,39 @@ static bool key_equal(const void *key1, const void *key2, return !strcmp((const char *)key1, (const char *)key2); } -/* Caller must make sure id is allocated */ -int expr__add_id(struct expr_parse_ctx *ctx, const char *id) +struct hashmap *ids__new(void) +{ + struct hashmap *hash; + + hash = hashmap__new(key_hash, key_equal, NULL); + if (IS_ERR(hash)) + return NULL; + return hash; +} + +void ids__free(struct hashmap *ids) +{ + struct hashmap_entry *cur; + size_t bkt; + + if (ids == NULL) + return; + + hashmap__for_each_entry(ids, cur, bkt) { + free((char *)cur->key); + free(cur->value); + } + + hashmap__free(ids); +} + +int ids__insert(struct hashmap *ids, const char *id) { struct expr_id_data *data_ptr = NULL, *old_data = NULL; char *old_key = NULL; int ret; - data_ptr = malloc(sizeof(*data_ptr)); - if (!data_ptr) - return -ENOMEM; - - data_ptr->parent = ctx->parent; - data_ptr->kind = EXPR_ID_DATA__PARENT; - - ret = hashmap__set(&ctx->ids, id, data_ptr, + ret = hashmap__set(ids, id, data_ptr, (const void **)&old_key, (void **)&old_data); if (ret) free(data_ptr); @@ -82,8 +105,57 @@ int expr__add_id(struct expr_parse_ctx *ctx, const char *id) return ret; } +struct hashmap *ids__union(struct hashmap *ids1, struct hashmap *ids2) +{ + size_t bkt; + struct hashmap_entry *cur; + int ret; + struct expr_id_data *old_data = NULL; + char *old_key = NULL; + + if (!ids1) + return ids2; + + if (!ids2) + return ids1; + + if (hashmap__size(ids1) < hashmap__size(ids2)) { + struct hashmap *tmp = ids1; + + ids1 = ids2; + ids2 = tmp; + } + hashmap__for_each_entry(ids2, cur, bkt) { + ret = hashmap__set(ids1, cur->key, cur->value, + (const void **)&old_key, (void **)&old_data); + free(old_key); + free(old_data); + + if (ret) { + hashmap__free(ids1); + hashmap__free(ids2); + return NULL; + } + } + hashmap__free(ids2); + return ids1; +} + +/* Caller must make sure id is allocated */ +int expr__add_id(struct expr_parse_ctx *ctx, const char *id) +{ + return ids__insert(ctx->ids, id); +} + /* Caller must make sure id is allocated */ int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val) +{ + return expr__add_id_val_source_count(ctx, id, val, /*source_count=*/1); +} + +/* Caller must make sure id is allocated */ +int expr__add_id_val_source_count(struct expr_parse_ctx *ctx, const char *id, + double val, int source_count) { struct expr_id_data *data_ptr = NULL, *old_data = NULL; char *old_key = NULL; @@ -92,10 +164,11 @@ int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val) data_ptr = malloc(sizeof(*data_ptr)); if (!data_ptr) return -ENOMEM; - data_ptr->val = val; + data_ptr->val.val = val; + data_ptr->val.source_count = source_count; data_ptr->kind = EXPR_ID_DATA__VALUE; - ret = hashmap__set(&ctx->ids, id, data_ptr, + ret = hashmap__set(ctx->ids, id, data_ptr, (const void **)&old_key, (void **)&old_data); if (ret) free(data_ptr); @@ -140,7 +213,7 @@ int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref) data_ptr->ref.metric_expr = ref->metric_expr; data_ptr->kind = EXPR_ID_DATA__REF; - ret = hashmap__set(&ctx->ids, name, data_ptr, + ret = hashmap__set(ctx->ids, name, data_ptr, (const void **)&old_key, (void **)&old_data); if (ret) free(data_ptr); @@ -156,9 +229,24 @@ 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) { - return hashmap__find(&ctx->ids, id, (void **)data) ? 0 : -1; + return hashmap__find(ctx->ids, id, (void **)data) ? 0 : -1; } +bool expr__subset_of_ids(struct expr_parse_ctx *haystack, + struct expr_parse_ctx *needles) +{ + struct hashmap_entry *cur; + size_t bkt; + struct expr_id_data *data; + + hashmap__for_each_entry(needles->ids, cur, bkt) { + if (expr__get_id(haystack, cur->key, &data)) + return false; + } + return true; +} + + int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id, struct expr_id_data **datap) { @@ -173,21 +261,18 @@ int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id, switch (data->kind) { case EXPR_ID_DATA__VALUE: - pr_debug2("lookup(%s): val %f\n", id, data->val); - break; - case EXPR_ID_DATA__PARENT: - pr_debug2("lookup(%s): parent %s\n", id, data->parent->id); + pr_debug2("lookup(%s): val %f\n", id, data->val.val); break; case EXPR_ID_DATA__REF: pr_debug2("lookup(%s): ref metric name %s\n", id, data->ref.metric_name); pr_debug("processing metric: %s ENTRY\n", id); data->kind = EXPR_ID_DATA__REF_VALUE; - if (expr__parse(&data->ref.val, ctx, data->ref.metric_expr, 1)) { + if (expr__parse(&data->ref.val, ctx, data->ref.metric_expr)) { pr_debug("%s failed to count\n", id); return -1; } - pr_debug("processing metric: %s EXIT: %f\n", id, data->val); + pr_debug("processing metric: %s EXIT: %f\n", id, data->ref.val); break; case EXPR_ID_DATA__REF_VALUE: pr_debug2("lookup(%s): ref val %f metric name %s\n", id, @@ -205,15 +290,28 @@ 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, + 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) +struct expr_parse_ctx *expr__ctx_new(void) { - hashmap__init(&ctx->ids, key_hash, key_equal, NULL); + struct expr_parse_ctx *ctx; + + ctx = malloc(sizeof(struct expr_parse_ctx)); + if (!ctx) + return NULL; + + ctx->ids = hashmap__new(key_hash, key_equal, NULL); + if (IS_ERR(ctx->ids)) { + free(ctx); + return NULL; + } + ctx->runtime = 0; + + return ctx; } void expr__ctx_clear(struct expr_parse_ctx *ctx) @@ -221,20 +319,32 @@ void expr__ctx_clear(struct expr_parse_ctx *ctx) struct hashmap_entry *cur; size_t bkt; - hashmap__for_each_entry((&ctx->ids), cur, bkt) { + hashmap__for_each_entry(ctx->ids, cur, bkt) { free((char *)cur->key); free(cur->value); } - hashmap__clear(&ctx->ids); + hashmap__clear(ctx->ids); +} + +void expr__ctx_free(struct expr_parse_ctx *ctx) +{ + struct hashmap_entry *cur; + size_t bkt; + + hashmap__for_each_entry(ctx->ids, cur, bkt) { + free((char *)cur->key); + free(cur->value); + } + hashmap__free(ctx->ids); + free(ctx); } static int __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr, - int start, int runtime) + bool compute_ids) { struct expr_scanner_ctx scanner_ctx = { - .start_token = start, - .runtime = runtime, + .runtime = ctx->runtime, }; YY_BUFFER_STATE buffer; void *scanner; @@ -253,7 +363,7 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr, expr_set_debug(1, scanner); #endif - ret = expr_parse(val, ctx, scanner); + ret = expr_parse(val, ctx, compute_ids, scanner); expr__flush_buffer(buffer, scanner); expr__delete_buffer(buffer, scanner); @@ -262,15 +372,15 @@ __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr, } int expr__parse(double *final_val, struct expr_parse_ctx *ctx, - const char *expr, int runtime) + const char *expr) { - return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 0; + return __expr__parse(final_val, ctx, expr, /*compute_ids=*/false) ? -1 : 0; } -int expr__find_other(const char *expr, const char *one, - struct expr_parse_ctx *ctx, int runtime) +int expr__find_ids(const char *expr, const char *one, + struct expr_parse_ctx *ctx) { - int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHER, runtime); + int ret = __expr__parse(NULL, ctx, expr, /*compute_ids=*/true); if (one) expr__del_id(ctx, one); @@ -281,13 +391,60 @@ int expr__find_other(const char *expr, const char *one, double expr_id_data__value(const struct expr_id_data *data) { if (data->kind == EXPR_ID_DATA__VALUE) - return data->val; + return data->val.val; assert(data->kind == EXPR_ID_DATA__REF_VALUE); return data->ref.val; } -struct expr_id *expr_id_data__parent(struct expr_id_data *data) +double expr_id_data__source_count(const struct expr_id_data *data) { - assert(data->kind == EXPR_ID_DATA__PARENT); - return data->parent; + assert(data->kind == EXPR_ID_DATA__VALUE); + return data->val.source_count; +} + +double expr__get_literal(const char *literal) +{ + static struct cpu_topology *topology; + double result = NAN; + + if (!strcasecmp("#smt_on", literal)) { + result = smt_on() > 0 ? 1.0 : 0.0; + goto out; + } + + if (!strcmp("#num_cpus", literal)) { + result = cpu__max_present_cpu().cpu; + goto out; + } + + /* + * Assume that topology strings are consistent, such as CPUs "0-1" + * wouldn't be listed as "0,1", and so after deduplication the number of + * these strings gives an indication of the number of packages, dies, + * etc. + */ + if (!topology) { + topology = cpu_topology__new(); + if (!topology) { + pr_err("Error creating CPU topology"); + goto out; + } + } + if (!strcmp("#num_packages", literal)) { + result = topology->package_cpus_lists; + goto out; + } + if (!strcmp("#num_dies", literal)) { + result = topology->die_cpus_lists; + goto out; + } + if (!strcmp("#num_cores", literal)) { + result = topology->core_cpus_lists; + goto out; + } + + pr_err("Unrecognized literal '%s'", literal); +out: + pr_debug2("literal: %s = %f\n", literal, result); + return result; } diff --git a/tools/perf/util/expr.l b/tools/perf/util/expr.l index 13e5e3c75f..0a13eb20c8 100644 --- a/tools/perf/util/expr.l +++ b/tools/perf/util/expr.l @@ -6,6 +6,7 @@ #include #include "expr.h" #include "expr-bison.h" +#include char *expr_get_text(yyscan_t yyscanner); YYSTYPE *expr_get_lval(yyscan_t yyscanner); @@ -41,11 +42,9 @@ static char *normalize(char *str, int runtime) char *dst = str; while (*str) { - if (*str == '@') - *dst++ = '/'; - else if (*str == '\\') + if (*str == '\\') *dst++ = *++str; - else if (*str == '?') { + else if (*str == '?') { char *paramval; int i = 0; int size = asprintf(¶mval, "%d", runtime); @@ -79,6 +78,17 @@ static int str(yyscan_t scanner, int token, int runtime) yylval->str = normalize(yylval->str, runtime); return token; } + +static int literal(yyscan_t scanner) +{ + YYSTYPE *yylval = expr_get_lval(scanner); + + yylval->num = expr__get_literal(expr_get_text(scanner)); + if (isnan(yylval->num)) + return EXPR_ERROR; + + return LITERAL; +} %} number ([0-9]+\.?[0-9]*|[0-9]*\.?[0-9]+) @@ -87,25 +97,18 @@ sch [-,=] spec \\{sch} sym [0-9a-zA-Z_\.:@?]+ symbol ({spec}|{sym})+ +literal #[0-9a-zA-Z_\.\-]+ %% struct expr_scanner_ctx *sctx = expr_get_extra(yyscanner); - { - int start_token = sctx->start_token; - - if (sctx->start_token) { - sctx->start_token = 0; - return start_token; - } - } - d_ratio { return D_RATIO; } max { return MAX; } min { return MIN; } if { return IF; } else { return ELSE; } -#smt_on { return SMT_ON; } +source_count { return SOURCE_COUNT; } +{literal} { return literal(yyscanner); } {number} { return value(yyscanner); } {symbol} { return str(yyscanner, ID, sctx->runtime); } "|" { return '|'; } diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y index b2ada8f830..a30b825adb 100644 --- a/tools/perf/util/expr.y +++ b/tools/perf/util/expr.y @@ -1,42 +1,43 @@ /* Simple expression parser */ %{ #define YYDEBUG 1 -#include -#include "util.h" +#include +#include +#include #include "util/debug.h" -#include // strtod() #define IN_EXPR_Y 1 #include "expr.h" -#include "smt.h" -#include - -static double d_ratio(double val0, double val1) -{ - if (val1 == 0) { - return 0; - } - return val0 / val1; -} - %} %define api.pure full %parse-param { double *final_val } %parse-param { struct expr_parse_ctx *ctx } +%parse-param { bool compute_ids } %parse-param {void *scanner} %lex-param {void* scanner} %union { double num; char *str; + struct ids { + /* + * When creating ids, holds the working set of event ids. NULL + * implies the set is empty. + */ + struct hashmap *ids; + /* + * The metric value. When not creating ids this is the value + * read from a counter, a constant or some computed value. When + * creating ids the value is either a constant or BOTTOM. NAN is + * used as the special BOTTOM value, representing a "set of all + * values" case. + */ + double val; + } ids; } -%token EXPR_PARSE EXPR_OTHER EXPR_ERROR -%token NUMBER -%token ID -%destructor { free ($$); } -%token MIN MAX IF ELSE SMT_ON D_RATIO +%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT EXPR_ERROR %left MIN MAX IF %left '|' %left '^' @@ -45,83 +46,256 @@ static double d_ratio(double val0, double val1) %left '-' '+' %left '*' '/' '%' %left NEG NOT -%type expr if_expr +%type NUMBER LITERAL +%type ID +%destructor { free ($$); } +%type expr if_expr +%destructor { ids__free($$.ids); } %{ static void expr_error(double *final_val __maybe_unused, struct expr_parse_ctx *ctx __maybe_unused, + bool compute_ids __maybe_unused, void *scanner, const char *s) { pr_debug("%s\n", s); } +/* + * During compute ids, the special "bottom" value uses NAN to represent the set + * of all values. NAN is selected as it isn't a useful constant value. + */ +#define BOTTOM NAN + +/* During computing ids, does val represent a constant (non-BOTTOM) value? */ +static bool is_const(double val) +{ + return isfinite(val); +} + +static struct ids union_expr(struct ids ids1, struct ids ids2) +{ + struct ids result = { + .val = BOTTOM, + .ids = ids__union(ids1.ids, ids2.ids), + }; + return result; +} + +static struct ids handle_id(struct expr_parse_ctx *ctx, char *id, + bool compute_ids, bool source_count) +{ + struct ids result; + + if (!compute_ids) { + /* + * Compute the event's value from ID. If the ID isn't known then + * it isn't used to compute the formula so set to NAN. + */ + struct expr_id_data *data; + + result.val = NAN; + if (expr__resolve_id(ctx, id, &data) == 0) { + result.val = source_count + ? expr_id_data__source_count(data) + : expr_id_data__value(data); + } + result.ids = NULL; + free(id); + } else { + /* + * Set the value to BOTTOM to show that any value is possible + * when the event is computed. Create a set of just the ID. + */ + result.val = BOTTOM; + result.ids = ids__new(); + if (!result.ids || ids__insert(result.ids, id)) { + pr_err("Error creating IDs for '%s'", id); + free(id); + } + } + return result; +} + +/* + * If we're not computing ids or $1 and $3 are constants, compute the new + * constant value using OP. Its invariant that there are no ids. If computing + * ids for non-constants union the set of IDs that must be computed. + */ +#define BINARY_LONG_OP(RESULT, OP, LHS, RHS) \ + if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \ + assert(LHS.ids == NULL); \ + assert(RHS.ids == NULL); \ + RESULT.val = (long)LHS.val OP (long)RHS.val; \ + RESULT.ids = NULL; \ + } else { \ + RESULT = union_expr(LHS, RHS); \ + } + +#define BINARY_OP(RESULT, OP, LHS, RHS) \ + if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \ + assert(LHS.ids == NULL); \ + assert(RHS.ids == NULL); \ + RESULT.val = LHS.val OP RHS.val; \ + RESULT.ids = NULL; \ + } else { \ + RESULT = union_expr(LHS, RHS); \ + } + %} %% -start: -EXPR_PARSE all_expr -| -EXPR_OTHER all_other - -all_other: all_other other -| - -other: ID +start: if_expr { - expr__add_id(ctx, $1); + if (compute_ids) + ctx->ids = ids__union($1.ids, ctx->ids); + + if (final_val) + *final_val = $1.val; } -| -MIN | MAX | IF | ELSE | SMT_ON | NUMBER | '|' | '^' | '&' | '-' | '+' | '*' | '/' | '%' | '(' | ')' | ',' -| -'<' | '>' | D_RATIO +; -all_expr: if_expr { *final_val = $1; } - ; +if_expr: expr IF expr ELSE expr +{ + if (fpclassify($3.val) == FP_ZERO) { + /* + * The IF expression evaluated to 0 so treat as false, take the + * ELSE and discard everything else. + */ + $$.val = $5.val; + $$.ids = $5.ids; + ids__free($1.ids); + ids__free($3.ids); + } else if (!compute_ids || is_const($3.val)) { + /* + * If ids aren't computed then treat the expression as true. If + * ids are being computed and the IF expr is a non-zero + * constant, then also evaluate the true case. + */ + $$.val = $1.val; + $$.ids = $1.ids; + ids__free($3.ids); + ids__free($5.ids); + } else if ($1.val == $5.val) { + /* + * LHS == RHS, so both are an identical constant. No need to + * evaluate any events. + */ + $$.val = $1.val; + $$.ids = NULL; + ids__free($1.ids); + ids__free($3.ids); + ids__free($5.ids); + } else { + /* + * Value is either the LHS or RHS and we need the IF expression + * to compute it. + */ + $$ = union_expr($1, union_expr($3, $5)); + } +} +| expr +; -if_expr: - expr IF expr ELSE expr { $$ = $3 ? $1 : $5; } - | expr - ; - -expr: NUMBER - | ID { - struct expr_id_data *data; - - if (expr__resolve_id(ctx, $1, &data)) { - free($1); - YYABORT; - } - - $$ = expr_id_data__value(data); - 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; } - | expr '/' expr { if ($3 == 0) { - pr_debug("division by zero\n"); - YYABORT; - } - $$ = $1 / $3; - } - | expr '%' expr { if ((long)$3 == 0) { - pr_debug("division by zero\n"); - YYABORT; - } - $$ = (long)$1 % (long)$3; - } - | '-' expr %prec NEG { $$ = -$2; } - | '(' if_expr ')' { $$ = $2; } - | 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); } - ; +expr: NUMBER +{ + $$.val = $1; + $$.ids = NULL; +} +| ID { $$ = handle_id(ctx, $1, compute_ids, /*source_count=*/false); } +| SOURCE_COUNT '(' ID ')' { $$ = handle_id(ctx, $3, compute_ids, /*source_count=*/true); } +| expr '|' expr { BINARY_LONG_OP($$, |, $1, $3); } +| expr '&' expr { BINARY_LONG_OP($$, &, $1, $3); } +| expr '^' expr { BINARY_LONG_OP($$, ^, $1, $3); } +| expr '<' expr { BINARY_OP($$, <, $1, $3); } +| expr '>' expr { BINARY_OP($$, >, $1, $3); } +| expr '+' expr { BINARY_OP($$, +, $1, $3); } +| expr '-' expr { BINARY_OP($$, -, $1, $3); } +| expr '*' expr { BINARY_OP($$, *, $1, $3); } +| expr '/' expr +{ + if (fpclassify($3.val) == FP_ZERO) { + pr_debug("division by zero\n"); + YYABORT; + } else if (!compute_ids || (is_const($1.val) && is_const($3.val))) { + assert($1.ids == NULL); + assert($3.ids == NULL); + $$.val = $1.val / $3.val; + $$.ids = NULL; + } else { + /* LHS and/or RHS need computing from event IDs so union. */ + $$ = union_expr($1, $3); + } +} +| expr '%' expr +{ + if (fpclassify($3.val) == FP_ZERO) { + pr_debug("division by zero\n"); + YYABORT; + } else if (!compute_ids || (is_const($1.val) && is_const($3.val))) { + assert($1.ids == NULL); + assert($3.ids == NULL); + $$.val = (long)$1.val % (long)$3.val; + $$.ids = NULL; + } else { + /* LHS and/or RHS need computing from event IDs so union. */ + $$ = union_expr($1, $3); + } +} +| D_RATIO '(' expr ',' expr ')' +{ + if (fpclassify($5.val) == FP_ZERO) { + /* + * Division by constant zero always yields zero and no events + * are necessary. + */ + assert($5.ids == NULL); + $$.val = 0.0; + $$.ids = NULL; + ids__free($3.ids); + } else if (!compute_ids || (is_const($3.val) && is_const($5.val))) { + assert($3.ids == NULL); + assert($5.ids == NULL); + $$.val = $3.val / $5.val; + $$.ids = NULL; + } else { + /* LHS and/or RHS need computing from event IDs so union. */ + $$ = union_expr($3, $5); + } +} +| '-' expr %prec NEG +{ + $$.val = -$2.val; + $$.ids = $2.ids; +} +| '(' if_expr ')' +{ + $$ = $2; +} +| MIN '(' expr ',' expr ')' +{ + if (!compute_ids) { + $$.val = $3.val < $5.val ? $3.val : $5.val; + $$.ids = NULL; + } else { + $$ = union_expr($3, $5); + } +} +| MAX '(' expr ',' expr ')' +{ + if (!compute_ids) { + $$.val = $3.val > $5.val ? $3.val : $5.val; + $$.ids = NULL; + } else { + $$ = union_expr($3, $5); + } +} +| LITERAL +{ + $$.val = $1; + $$.ids = NULL; +} +; %% diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h index d4137559be..3db3293213 100644 --- a/tools/perf/util/genelf.h +++ b/tools/perf/util/genelf.h @@ -42,7 +42,7 @@ int jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_ent #error "unsupported architecture" #endif -#if __BYTE_ORDER == __BIG_ENDIAN +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define GEN_ELF_ENDIAN ELFDATA2MSB #else #define GEN_ELF_ENDIAN ELFDATA2LSB diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index af1e78d762..2c8147a622 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c @@ -35,7 +35,7 @@ #define INTEL_BTS_ERR_NOINSN 5 #define INTEL_BTS_ERR_LOST 9 -#if __BYTE_ORDER == __BIG_ENDIAN +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define le64_to_cpu bswap_64 #else #define le64_to_cpu diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 44e40bad0e..3945500036 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -16,6 +16,7 @@ #include "map_symbol.h" #include "branch.h" #include "mem-events.h" +#include "path.h" #include "srcline.h" #include "symbol.h" #include "sort.h" @@ -34,6 +35,7 @@ #include "bpf-event.h" #include // page_size #include "cgroup.h" +#include "arm64-frame-pointer-unwind-support.h" #include #include @@ -755,6 +757,14 @@ int machine__process_itrace_start_event(struct machine *machine __maybe_unused, return 0; } +int machine__process_aux_output_hw_id_event(struct machine *machine __maybe_unused, + union perf_event *event) +{ + if (dump_trace) + perf_event__fprintf_aux_output_hw_id(event, stdout); + return 0; +} + int machine__process_switch_event(struct machine *machine __maybe_unused, union perf_event *event) { @@ -1407,7 +1417,7 @@ static int maps__set_modules_path_dir(struct maps *maps, const char *dir_name, i struct stat st; /*sshfs might return bad dent->d_type, so we have to stat*/ - snprintf(path, sizeof(path), "%s/%s", dir_name, dent->d_name); + path__join(path, sizeof(path), dir_name, dent->d_name); if (stat(path, &st)) continue; @@ -2028,6 +2038,8 @@ int machine__process_event(struct machine *machine, union perf_event *event, ret = machine__process_bpf(machine, event, sample); break; case PERF_RECORD_TEXT_POKE: ret = machine__process_text_poke(machine, event, sample); break; + case PERF_RECORD_AUX_OUTPUT_HW_ID: + ret = machine__process_aux_output_hw_id_event(machine, event); break; default: ret = -1; break; @@ -2061,6 +2073,7 @@ static void ip__resolve_ams(struct thread *thread, ams->addr = ip; ams->al_addr = al.addr; + ams->al_level = al.level; ams->ms.maps = al.maps; ams->ms.sym = al.sym; ams->ms.map = al.map; @@ -2080,6 +2093,7 @@ static void ip__resolve_data(struct thread *thread, ams->addr = addr; ams->al_addr = al.addr; + ams->al_level = al.level; ams->ms.maps = al.maps; ams->ms.sym = al.sym; ams->ms.map = al.map; @@ -2700,6 +2714,15 @@ static int find_prev_cpumode(struct ip_callchain *chain, struct thread *thread, return err; } +static u64 get_leaf_frame_caller(struct perf_sample *sample, + struct thread *thread, int usr_idx) +{ + if (machine__normalized_is(thread->maps->machine, "arm64")) + return get_leaf_frame_caller_aarch64(sample, thread, usr_idx); + else + return 0; +} + static int thread__resolve_callchain_sample(struct thread *thread, struct callchain_cursor *cursor, struct evsel *evsel, @@ -2713,9 +2736,10 @@ static int thread__resolve_callchain_sample(struct thread *thread, struct ip_callchain *chain = sample->callchain; int chain_nr = 0; u8 cpumode = PERF_RECORD_MISC_USER; - int i, j, err, nr_entries; + int i, j, err, nr_entries, usr_idx; int skip_idx = -1; int first_call = 0; + u64 leaf_frame_caller; if (chain) chain_nr = chain->nr; @@ -2840,6 +2864,34 @@ static int thread__resolve_callchain_sample(struct thread *thread, continue; } + /* + * PERF_CONTEXT_USER allows us to locate where the user stack ends. + * Depending on callchain_param.order and the position of PERF_CONTEXT_USER, + * the index will be different in order to add the missing frame + * at the right place. + */ + + usr_idx = callchain_param.order == ORDER_CALLEE ? j-2 : j-1; + + if (usr_idx >= 0 && chain->ips[usr_idx] == PERF_CONTEXT_USER) { + + leaf_frame_caller = get_leaf_frame_caller(sample, thread, usr_idx); + + /* + * check if leaf_frame_Caller != ip to not add the same + * value twice. + */ + + if (leaf_frame_caller && leaf_frame_caller != ip) { + + err = add_callchain_ip(thread, cursor, parent, + root_al, &cpumode, leaf_frame_caller, + false, NULL, NULL, 0); + if (err) + return (err < 0) ? err : 0; + } + } + err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip, false, NULL, NULL, 0); @@ -3069,14 +3121,19 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid, } /* - * Compares the raw arch string. N.B. see instead perf_env__arch() if a - * normalized arch is needed. + * Compares the raw arch string. N.B. see instead perf_env__arch() or + * machine__normalized_is() if a normalized arch is needed. */ bool machine__is(struct machine *machine, const char *arch) { return machine && !strcmp(perf_env__raw_arch(machine->env), arch); } +bool machine__normalized_is(struct machine *machine, const char *arch) +{ + return machine && !strcmp(perf_env__arch(machine->env), arch); +} + int machine__nr_cpus_avail(struct machine *machine) { return machine ? perf_env__nr_cpus_avail(machine->env) : 0; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 7377ed6efd..c5a45dc8df 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -124,6 +124,8 @@ int machine__process_aux_event(struct machine *machine, union perf_event *event); int machine__process_itrace_start_event(struct machine *machine, union perf_event *event); +int machine__process_aux_output_hw_id_event(struct machine *machine, + union perf_event *event); int machine__process_switch_event(struct machine *machine, union perf_event *event); int machine__process_namespaces_event(struct machine *machine, @@ -206,6 +208,7 @@ static inline bool machine__is_host(struct machine *machine) } bool machine__is(struct machine *machine, const char *arch); +bool machine__normalized_is(struct machine *machine, const char *arch); int machine__nr_cpus_avail(struct machine *machine); struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid); diff --git a/tools/perf/util/map_symbol.h b/tools/perf/util/map_symbol.h index 7d22ade082..e08817b0c3 100644 --- a/tools/perf/util/map_symbol.h +++ b/tools/perf/util/map_symbol.h @@ -18,6 +18,7 @@ struct addr_map_symbol { struct map_symbol ms; u64 addr; u64 al_addr; + char al_level; u64 phys_addr; u64 data_page_size; }; diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c index f0e75df72b..ed0ab838bc 100644 --- a/tools/perf/util/mem-events.c +++ b/tools/perf/util/mem-events.c @@ -301,12 +301,25 @@ static const char * const mem_lvlnum[] = { [PERF_MEM_LVLNUM_NA] = "N/A", }; +static const char * const mem_hops[] = { + "N/A", + /* + * While printing, 'Remote' will be added to represent + * 'Remote core, same node' accesses as remote field need + * to be set with mem_hops field. + */ + "core, same node", + "node, same socket", + "socket, same board", + "board", +}; + int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info) { size_t i, l = 0; u64 m = PERF_MEM_LVL_NA; u64 hit, miss; - int printed; + int printed = 0; if (mem_info) m = mem_info->data_src.mem_lvl; @@ -320,21 +333,27 @@ int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info) /* already taken care of */ m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS); - if (mem_info && mem_info->data_src.mem_remote) { strcat(out, "Remote "); l += 7; } - printed = 0; - for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) { - if (!(m & 0x1)) - continue; - if (printed++) { - strcat(out, " or "); - l += 4; + /* + * Incase mem_hops field is set, we can skip printing data source via + * PERF_MEM_LVL namespace. + */ + if (mem_info && mem_info->data_src.mem_hops) { + l += scnprintf(out + l, sz - l, "%s ", mem_hops[mem_info->data_src.mem_hops]); + } else { + for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) { + if (!(m & 0x1)) + continue; + if (printed++) { + strcat(out, " or "); + l += 4; + } + l += scnprintf(out + l, sz - l, mem_lvl[i]); } - l += scnprintf(out + l, sz - l, mem_lvl[i]); } if (mem_info && mem_info->data_src.mem_lvl_num) { @@ -472,8 +491,12 @@ int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi) /* * Skylake might report unknown remote level via this * bit, consider it when evaluating remote HITMs. + * + * Incase of power, remote field can also be used to denote cache + * accesses from the another core of same node. Hence, setting + * mrem only when HOPS is zero along with set remote field. */ - bool mrem = data_src->mem_remote; + bool mrem = (data_src->mem_remote && !data_src->mem_hops); int err = 0; #define HITM_INC(__f) \ diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 29b747ac31..d8492e3395 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -11,6 +11,7 @@ #include "evsel.h" #include "strbuf.h" #include "pmu.h" +#include "pmu-hybrid.h" #include "expr.h" #include "rblist.h" #include @@ -18,6 +19,7 @@ #include "strlist.h" #include #include +#include #include #include #include @@ -84,6 +86,7 @@ static void metric_event_delete(struct rblist *rblist __maybe_unused, struct metric_expr *expr, *tmp; list_for_each_entry_safe(expr, tmp, &me->head, nd) { + free((char *)expr->metric_name); free(expr->metric_refs); free(expr->metric_events); free(expr); @@ -116,289 +119,207 @@ struct metric_ref_node { struct list_head list; }; +/** + * The metric under construction. The data held here will be placed in a + * metric_expr. + */ struct metric { struct list_head nd; - struct expr_parse_ctx pctx; + /** + * The expression parse context importantly holding the IDs contained + * within the expression. + */ + struct expr_parse_ctx *pctx; + /** The name of the metric such as "IPC". */ const char *metric_name; + /** Modifier on the metric such as "u" or NULL for none. */ + const char *modifier; + /** The expression to parse, for example, "instructions/cycles". */ const char *metric_expr; + /** + * The "ScaleUnit" that scales and adds a unit to the metric during + * output. + */ const char *metric_unit; - struct list_head metric_refs; - int metric_refs_cnt; - int runtime; + /** Optional null terminated array of referenced metrics. */ + struct metric_ref *metric_refs; + /** + * Is there a constraint on the group of events? In which case the + * events won't be grouped. + */ bool has_constraint; + /** + * Parsed events for the metric. Optional as events may be taken from a + * different metric whose group contains all the IDs necessary for this + * one. + */ + struct evlist *evlist; }; -#define RECURSION_ID_MAX 1000 - -struct expr_ids { - struct expr_id id[RECURSION_ID_MAX]; - int cnt; -}; - -static struct expr_id *expr_ids__alloc(struct expr_ids *ids) +static void metricgroup___watchdog_constraint_hint(const char *name, bool foot) { - if (ids->cnt >= RECURSION_ID_MAX) + static bool violate_nmi_constraint; + + if (!foot) { + pr_warning("Splitting metric group %s into standalone metrics.\n", name); + violate_nmi_constraint = true; + return; + } + + if (!violate_nmi_constraint) + return; + + pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n" + " echo 0 > /proc/sys/kernel/nmi_watchdog\n" + " perf stat ...\n" + " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); +} + +static bool metricgroup__has_constraint(const struct pmu_event *pe) +{ + if (!pe->metric_constraint) + return false; + + if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") && + sysctl__nmi_watchdog_enabled()) { + metricgroup___watchdog_constraint_hint(pe->metric_name, false); + return true; + } + + return false; +} + +static struct metric *metric__new(const struct pmu_event *pe, + const char *modifier, + bool metric_no_group, + int runtime) +{ + struct metric *m; + + m = zalloc(sizeof(*m)); + if (!m) return NULL; - return &ids->id[ids->cnt++]; + + m->pctx = expr__ctx_new(); + if (!m->pctx) { + free(m); + return NULL; + } + + m->metric_name = pe->metric_name; + m->modifier = modifier ? strdup(modifier) : NULL; + if (modifier && !m->modifier) { + expr__ctx_free(m->pctx); + free(m); + return NULL; + } + m->metric_expr = pe->metric_expr; + m->metric_unit = pe->unit; + m->pctx->runtime = runtime; + m->has_constraint = metric_no_group || metricgroup__has_constraint(pe); + m->metric_refs = NULL; + m->evlist = NULL; + + return m; } -static void expr_ids__exit(struct expr_ids *ids) +static void metric__free(struct metric *m) { - int i; - - for (i = 0; i < ids->cnt; i++) - free(ids->id[i].id); + free(m->metric_refs); + expr__ctx_free(m->pctx); + free((char *)m->modifier); + evlist__delete(m->evlist); + free(m); } -static bool contains_event(struct evsel **metric_events, int num_events, - const char *event_name) +static bool contains_metric_id(struct evsel **metric_events, int num_events, + const char *metric_id) { int i; for (i = 0; i < num_events; i++) { - if (!strcmp(metric_events[i]->name, event_name)) + if (!strcmp(evsel__metric_id(metric_events[i]), metric_id)) return true; } return false; } -static bool evsel_same_pmu_or_none(struct evsel *ev1, struct evsel *ev2) -{ - if (!ev1->pmu_name || !ev2->pmu_name) - return true; - - return !strcmp(ev1->pmu_name, ev2->pmu_name); -} - /** - * Find a group of events in perf_evlist that correspond to those from a parsed - * metric expression. Note, as find_evsel_group is called in the same order as - * perf_evlist was constructed, metric_no_merge doesn't need to test for - * underfilling a group. - * @perf_evlist: a list of events something like: {metric1 leader, metric1 - * sibling, metric1 sibling}:W,duration_time,{metric2 leader, metric2 sibling, - * metric2 sibling}:W,duration_time - * @pctx: the parse context for the metric expression. - * @metric_no_merge: don't attempt to share events for the metric with other - * metrics. - * @has_constraint: is there a constraint on the group of events? In which case - * the events won't be grouped. - * @metric_events: out argument, null terminated array of evsel's associated - * with the metric. - * @evlist_used: in/out argument, bitmap tracking which evlist events are used. - * @return the first metric event or NULL on failure. + * setup_metric_events - Find a group of events in metric_evlist that correspond + * to the IDs from a parsed metric expression. + * @ids: the metric IDs to match. + * @metric_evlist: the list of perf events. + * @out_metric_events: holds the created metric events array. */ -static struct evsel *find_evsel_group(struct evlist *perf_evlist, - struct expr_parse_ctx *pctx, - bool metric_no_merge, - bool has_constraint, - struct evsel **metric_events, - unsigned long *evlist_used) +static int setup_metric_events(struct hashmap *ids, + struct evlist *metric_evlist, + struct evsel ***out_metric_events) { - struct evsel *ev, *current_leader = NULL; - struct expr_id_data *val_ptr; - int i = 0, matched_events = 0, events_to_match; - const int idnum = (int)hashmap__size(&pctx->ids); + struct evsel **metric_events; + const char *metric_id; + struct evsel *ev; + size_t ids_size, matched_events, i; - /* - * duration_time is always grouped separately, when events are grouped - * (ie has_constraint is false) then ignore it in the matching loop and - * add it to metric_events at the end. - */ - if (!has_constraint && - hashmap__find(&pctx->ids, "duration_time", (void **)&val_ptr)) - events_to_match = idnum - 1; - else - events_to_match = idnum; + *out_metric_events = NULL; + ids_size = hashmap__size(ids); + + metric_events = calloc(sizeof(void *), ids_size + 1); + if (!metric_events) + return -ENOMEM; + + matched_events = 0; + evlist__for_each_entry(metric_evlist, ev) { + struct expr_id_data *val_ptr; - evlist__for_each_entry (perf_evlist, ev) { /* - * Events with a constraint aren't grouped and match the first - * events available. + * Check for duplicate events with the same name. For + * example, uncore_imc/cas_count_read/ will turn into 6 + * events per socket on skylakex. Only the first such + * event is placed in metric_events. */ - if (has_constraint && ev->weak_group) + metric_id = evsel__metric_id(ev); + if (contains_metric_id(metric_events, matched_events, metric_id)) continue; - /* Ignore event if already used and merging is disabled. */ - if (metric_no_merge && test_bit(ev->core.idx, evlist_used)) - continue; - if (!has_constraint && !evsel__has_leader(ev, current_leader)) { - /* - * Start of a new group, discard the whole match and - * start again. - */ - matched_events = 0; - memset(metric_events, 0, - sizeof(struct evsel *) * idnum); - current_leader = evsel__leader(ev); - } /* - * Check for duplicate events with the same name. For example, - * uncore_imc/cas_count_read/ will turn into 6 events per socket - * on skylakex. Only the first such event is placed in - * metric_events. If events aren't grouped then this also - * ensures that the same event in different sibling groups - * aren't both added to metric_events. + * Does this event belong to the parse context? For + * combined or shared groups, this metric may not care + * about this event. */ - if (contains_event(metric_events, matched_events, ev->name)) - continue; - /* Does this event belong to the parse context? */ - if (hashmap__find(&pctx->ids, ev->name, (void **)&val_ptr)) + if (hashmap__find(ids, metric_id, (void **)&val_ptr)) { metric_events[matched_events++] = ev; - if (matched_events == events_to_match) - break; - } - - if (events_to_match != idnum) { - /* Add the first duration_time. */ - evlist__for_each_entry(perf_evlist, ev) { - if (!strcmp(ev->name, "duration_time")) { - metric_events[matched_events++] = ev; + if (matched_events >= ids_size) break; - } } } - - if (matched_events != idnum) { - /* Not a whole match */ - return NULL; + if (matched_events < ids_size) { + free(metric_events); + return -EINVAL; } - - metric_events[idnum] = NULL; - - for (i = 0; i < idnum; i++) { + for (i = 0; i < ids_size; i++) { ev = metric_events[i]; - /* Don't free the used events. */ - set_bit(ev->core.idx, evlist_used); + ev->collect_stat = true; + /* - * The metric leader points to the identically named event in - * metric_events. + * The metric leader points to the identically named + * event in metric_events. */ ev->metric_leader = ev; /* - * Mark two events with identical names in the same group (or - * globally) as being in use as uncore events may be duplicated - * for each pmu. Set the metric leader of such events to be the - * event that appears in metric_events. + * Mark two events with identical names in the same + * group (or globally) as being in use as uncore events + * may be duplicated for each pmu. Set the metric leader + * of such events to be the event that appears in + * metric_events. */ - evlist__for_each_entry_continue(perf_evlist, ev) { - /* - * If events are grouped then the search can terminate - * when then group is left. - */ - if (!has_constraint && - ev->core.leader != metric_events[i]->core.leader && - evsel_same_pmu_or_none(evsel__leader(ev), evsel__leader(metric_events[i]))) - break; - if (!strcmp(metric_events[i]->name, ev->name)) { - set_bit(ev->core.idx, evlist_used); + metric_id = evsel__metric_id(ev); + evlist__for_each_entry_continue(metric_evlist, ev) { + if (!strcmp(evsel__metric_id(ev), metric_id)) ev->metric_leader = metric_events[i]; - } } } - - return metric_events[0]; -} - -static int metricgroup__setup_events(struct list_head *groups, - bool metric_no_merge, - struct evlist *perf_evlist, - struct rblist *metric_events_list) -{ - struct metric_event *me; - struct metric_expr *expr; - int i = 0; - int ret = 0; - struct metric *m; - struct evsel *evsel, *tmp; - unsigned long *evlist_used; - - evlist_used = bitmap_zalloc(perf_evlist->core.nr_entries); - if (!evlist_used) - return -ENOMEM; - - list_for_each_entry (m, groups, nd) { - struct evsel **metric_events; - struct metric_ref *metric_refs = NULL; - - metric_events = calloc(sizeof(void *), - hashmap__size(&m->pctx.ids) + 1); - if (!metric_events) { - ret = -ENOMEM; - break; - } - evsel = find_evsel_group(perf_evlist, &m->pctx, - metric_no_merge, - m->has_constraint, metric_events, - evlist_used); - if (!evsel) { - pr_debug("Cannot resolve %s: %s\n", - m->metric_name, m->metric_expr); - free(metric_events); - continue; - } - for (i = 0; metric_events[i]; i++) - metric_events[i]->collect_stat = true; - me = metricgroup__lookup(metric_events_list, evsel, true); - if (!me) { - ret = -ENOMEM; - free(metric_events); - break; - } - expr = malloc(sizeof(struct metric_expr)); - if (!expr) { - ret = -ENOMEM; - free(metric_events); - break; - } - - /* - * Collect and store collected nested expressions - * for metric processing. - */ - if (m->metric_refs_cnt) { - struct metric_ref_node *ref; - - metric_refs = zalloc(sizeof(struct metric_ref) * (m->metric_refs_cnt + 1)); - if (!metric_refs) { - ret = -ENOMEM; - free(metric_events); - free(expr); - break; - } - - i = 0; - list_for_each_entry(ref, &m->metric_refs, list) { - /* - * 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. - */ - metric_refs[i].metric_name = ref->metric_name; - metric_refs[i].metric_expr = ref->metric_expr; - i++; - } - } - - expr->metric_refs = metric_refs; - expr->metric_expr = m->metric_expr; - expr->metric_name = m->metric_name; - expr->metric_unit = m->metric_unit; - expr->metric_events = metric_events; - expr->runtime = m->runtime; - list_add(&expr->nd, &me->head); - } - - evlist__for_each_entry_safe(perf_evlist, tmp, evsel) { - if (!test_bit(evsel->core.idx, evlist_used)) { - evlist__remove(perf_evlist, evsel); - evsel__delete(evsel); - } - } - bitmap_free(evlist_used); - - return ret; + *out_metric_events = metric_events; + return 0; } static bool match_metric(const char *n, const char *list) @@ -422,7 +343,7 @@ static bool match_metric(const char *n, const char *list) return false; } -static bool match_pe_metric(struct pmu_event *pe, const char *metric) +static bool match_pe_metric(const struct pmu_event *pe, const char *metric) { return match_metric(pe->metric_group, metric) || match_metric(pe->metric_name, metric); @@ -506,7 +427,7 @@ static void metricgroup__print_strlist(struct strlist *metrics, bool raw) putchar('\n'); } -static int metricgroup__print_pmu_event(struct pmu_event *pe, +static int metricgroup__print_pmu_event(const struct pmu_event *pe, bool metricgroups, char *filter, bool raw, bool details, struct rblist *groups, @@ -581,14 +502,14 @@ struct metricgroup_print_sys_idata { bool details; }; -typedef int (*metricgroup_sys_event_iter_fn)(struct pmu_event *pe, void *); +typedef int (*metricgroup_sys_event_iter_fn)(const struct pmu_event *pe, void *); struct metricgroup_iter_data { metricgroup_sys_event_iter_fn fn; void *data; }; -static int metricgroup__sys_event_iter(struct pmu_event *pe, void *data) +static int metricgroup__sys_event_iter(const struct pmu_event *pe, void *data) { struct metricgroup_iter_data *d = data; struct perf_pmu *pmu = NULL; @@ -607,7 +528,7 @@ static int metricgroup__sys_event_iter(struct pmu_event *pe, void *data) return 0; } -static int metricgroup__print_sys_event_iter(struct pmu_event *pe, void *data) +static int metricgroup__print_sys_event_iter(const struct pmu_event *pe, void *data) { struct metricgroup_print_sys_idata *d = data; @@ -616,10 +537,10 @@ static int metricgroup__print_sys_event_iter(struct pmu_event *pe, void *data) } void metricgroup__print(bool metrics, bool metricgroups, char *filter, - bool raw, bool details) + bool raw, bool details, const char *pmu_name) { - struct pmu_events_map *map = pmu_events_map__find(); - struct pmu_event *pe; + const struct pmu_events_map *map = pmu_events_map__find(); + const struct pmu_event *pe; int i; struct rblist groups; struct rb_node *node, *next; @@ -642,6 +563,10 @@ void metricgroup__print(bool metrics, bool metricgroups, char *filter, break; if (!pe->metric_expr) continue; + if (pmu_name && perf_pmu__is_hybrid(pe->pmu) && + strcmp(pmu_name, pe->pmu)) { + continue; + } if (metricgroup__print_pmu_event(pe, metricgroups, filter, raw, details, &groups, metriclist) < 0) @@ -686,150 +611,391 @@ void metricgroup__print(bool metrics, bool metricgroups, char *filter, strlist__delete(metriclist); } -static void metricgroup__add_metric_weak_group(struct strbuf *events, - struct expr_parse_ctx *ctx) +static const char *code_characters = ",-=@"; + +static int encode_metric_id(struct strbuf *sb, const char *x) +{ + char *c; + int ret = 0; + + for (; *x; x++) { + c = strchr(code_characters, *x); + if (c) { + ret = strbuf_addch(sb, '!'); + if (ret) + break; + + ret = strbuf_addch(sb, '0' + (c - code_characters)); + if (ret) + break; + } else { + ret = strbuf_addch(sb, *x); + if (ret) + break; + } + } + return ret; +} + +static int decode_metric_id(struct strbuf *sb, const char *x) +{ + const char *orig = x; + size_t i; + char c; + int ret; + + for (; *x; x++) { + c = *x; + if (*x == '!') { + x++; + i = *x - '0'; + if (i > strlen(code_characters)) { + pr_err("Bad metric-id encoding in: '%s'", orig); + return -1; + } + c = code_characters[i]; + } + ret = strbuf_addch(sb, c); + if (ret) + return ret; + } + return 0; +} + +static int decode_all_metric_ids(struct evlist *perf_evlist, const char *modifier) +{ + struct evsel *ev; + struct strbuf sb = STRBUF_INIT; + char *cur; + int ret = 0; + + evlist__for_each_entry(perf_evlist, ev) { + if (!ev->metric_id) + continue; + + ret = strbuf_setlen(&sb, 0); + if (ret) + break; + + ret = decode_metric_id(&sb, ev->metric_id); + if (ret) + break; + + free((char *)ev->metric_id); + ev->metric_id = strdup(sb.buf); + if (!ev->metric_id) { + ret = -ENOMEM; + break; + } + /* + * If the name is just the parsed event, use the metric-id to + * give a more friendly display version. + */ + if (strstr(ev->name, "metric-id=")) { + bool has_slash = false; + + free(ev->name); + for (cur = strchr(sb.buf, '@') ; cur; cur = strchr(++cur, '@')) { + *cur = '/'; + has_slash = true; + } + + if (modifier) { + if (!has_slash && !strchr(sb.buf, ':')) { + ret = strbuf_addch(&sb, ':'); + if (ret) + break; + } + ret = strbuf_addstr(&sb, modifier); + if (ret) + break; + } + ev->name = strdup(sb.buf); + if (!ev->name) { + ret = -ENOMEM; + break; + } + } + } + strbuf_release(&sb); + return ret; +} + +static int metricgroup__build_event_string(struct strbuf *events, + const struct expr_parse_ctx *ctx, + const char *modifier, + bool has_constraint) { struct hashmap_entry *cur; size_t bkt; bool no_group = true, has_duration = false; + int ret = 0; - hashmap__for_each_entry((&ctx->ids), cur, bkt) { - pr_debug("found event %s\n", (const char *)cur->key); +#define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0) + + hashmap__for_each_entry(ctx->ids, cur, bkt) { + const char *sep, *rsep, *id = cur->key; + + pr_debug("found event %s\n", id); /* * Duration time maps to a software event and can make * groups not count. Always use it outside a * group. */ - if (!strcmp(cur->key, "duration_time")) { + if (!strcmp(id, "duration_time")) { has_duration = true; continue; } - strbuf_addf(events, "%s%s", - no_group ? "{" : ",", - (const char *)cur->key); - no_group = false; + /* Separate events with commas and open the group if necessary. */ + if (no_group) { + if (!has_constraint) { + ret = strbuf_addch(events, '{'); + RETURN_IF_NON_ZERO(ret); + } + + no_group = false; + } else { + ret = strbuf_addch(events, ','); + RETURN_IF_NON_ZERO(ret); + } + /* + * Encode the ID as an event string. Add a qualifier for + * metric_id that is the original name except with characters + * that parse-events can't parse replaced. For example, + * 'msr@tsc@' gets added as msr/tsc,metric-id=msr!3tsc!3/ + */ + sep = strchr(id, '@'); + if (sep != NULL) { + ret = strbuf_add(events, id, sep - id); + RETURN_IF_NON_ZERO(ret); + ret = strbuf_addch(events, '/'); + RETURN_IF_NON_ZERO(ret); + rsep = strrchr(sep, '@'); + ret = strbuf_add(events, sep + 1, rsep - sep - 1); + RETURN_IF_NON_ZERO(ret); + ret = strbuf_addstr(events, ",metric-id="); + RETURN_IF_NON_ZERO(ret); + sep = rsep; + } else { + sep = strchr(id, ':'); + if (sep != NULL) { + ret = strbuf_add(events, id, sep - id); + RETURN_IF_NON_ZERO(ret); + } else { + ret = strbuf_addstr(events, id); + RETURN_IF_NON_ZERO(ret); + } + ret = strbuf_addstr(events, "/metric-id="); + RETURN_IF_NON_ZERO(ret); + } + ret = encode_metric_id(events, id); + RETURN_IF_NON_ZERO(ret); + ret = strbuf_addstr(events, "/"); + RETURN_IF_NON_ZERO(ret); + + if (sep != NULL) { + ret = strbuf_addstr(events, sep + 1); + RETURN_IF_NON_ZERO(ret); + } + if (modifier) { + ret = strbuf_addstr(events, modifier); + RETURN_IF_NON_ZERO(ret); + } } - if (!no_group) { - strbuf_addf(events, "}:W"); - if (has_duration) - strbuf_addf(events, ",duration_time"); - } else if (has_duration) - strbuf_addf(events, "duration_time"); + if (has_duration) { + if (no_group) { + /* Strange case of a metric of just duration_time. */ + ret = strbuf_addf(events, "duration_time"); + } else if (!has_constraint) + ret = strbuf_addf(events, "}:W,duration_time"); + else + ret = strbuf_addf(events, ",duration_time"); + } else if (!no_group && !has_constraint) + ret = strbuf_addf(events, "}:W"); + + return ret; +#undef RETURN_IF_NON_ZERO } -static void metricgroup__add_metric_non_group(struct strbuf *events, - struct expr_parse_ctx *ctx) -{ - struct hashmap_entry *cur; - size_t bkt; - bool first = true; - - hashmap__for_each_entry((&ctx->ids), cur, bkt) { - if (!first) - strbuf_addf(events, ","); - strbuf_addf(events, "%s", (const char *)cur->key); - first = false; - } -} - -static void metricgroup___watchdog_constraint_hint(const char *name, bool foot) -{ - static bool violate_nmi_constraint; - - if (!foot) { - pr_warning("Splitting metric group %s into standalone metrics.\n", name); - violate_nmi_constraint = true; - return; - } - - if (!violate_nmi_constraint) - return; - - pr_warning("Try disabling the NMI watchdog to comply NO_NMI_WATCHDOG metric constraint:\n" - " echo 0 > /proc/sys/kernel/nmi_watchdog\n" - " perf stat ...\n" - " echo 1 > /proc/sys/kernel/nmi_watchdog\n"); -} - -static bool metricgroup__has_constraint(struct pmu_event *pe) -{ - if (!pe->metric_constraint) - return false; - - if (!strcmp(pe->metric_constraint, "NO_NMI_WATCHDOG") && - sysctl__nmi_watchdog_enabled()) { - metricgroup___watchdog_constraint_hint(pe->metric_name, false); - return true; - } - - return false; -} - -int __weak arch_get_runtimeparam(struct pmu_event *pe __maybe_unused) +int __weak arch_get_runtimeparam(const struct pmu_event *pe __maybe_unused) { return 1; } +/* + * A singly linked list on the stack of the names of metrics being + * processed. Used to identify recursion. + */ +struct visited_metric { + const char *name; + const struct visited_metric *parent; +}; + struct metricgroup_add_iter_data { struct list_head *metric_list; - const char *metric; - struct expr_ids *ids; + const char *metric_name; + const char *modifier; int *ret; bool *has_match; bool metric_no_group; + struct metric *root_metric; + const struct visited_metric *visited; + const struct pmu_events_map *map; }; +static int add_metric(struct list_head *metric_list, + const struct pmu_event *pe, + const char *modifier, + bool metric_no_group, + struct metric *root_metric, + const struct visited_metric *visited, + const struct pmu_events_map *map); + +/** + * resolve_metric - Locate metrics within the root metric and recursively add + * references to them. + * @metric_list: The list the metric is added to. + * @modifier: if non-null event modifiers like "u". + * @metric_no_group: Should events written to events be grouped "{}" or + * global. Grouping is the default but due to multiplexing the + * user may override. + * @root_metric: Metrics may reference other metrics to form a tree. In this + * case the root_metric holds all the IDs and a list of referenced + * metrics. When adding a root this argument is NULL. + * @visited: A singly linked list of metric names being added that is used to + * detect recursion. + * @map: The map that is searched for metrics, most commonly the table for the + * architecture perf is running upon. + */ +static int resolve_metric(struct list_head *metric_list, + const char *modifier, + bool metric_no_group, + struct metric *root_metric, + const struct visited_metric *visited, + const struct pmu_events_map *map) +{ + struct hashmap_entry *cur; + size_t bkt; + struct to_resolve { + /* The metric to resolve. */ + const struct pmu_event *pe; + /* + * The key in the IDs map, this may differ from in case, + * etc. from pe->metric_name. + */ + const char *key; + } *pending = NULL; + int i, ret = 0, pending_cnt = 0; + + /* + * Iterate all the parsed IDs and if there's a matching metric and it to + * the pending array. + */ + hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) { + const struct pmu_event *pe; + + pe = metricgroup__find_metric(cur->key, map); + if (pe) { + pending = realloc(pending, + (pending_cnt + 1) * sizeof(struct to_resolve)); + if (!pending) + return -ENOMEM; + + pending[pending_cnt].pe = pe; + pending[pending_cnt].key = cur->key; + pending_cnt++; + } + } + + /* Remove the metric IDs from the context. */ + for (i = 0; i < pending_cnt; i++) + expr__del_id(root_metric->pctx, pending[i].key); + + /* + * Recursively add all the metrics, IDs are added to the root metric's + * context. + */ + for (i = 0; i < pending_cnt; i++) { + ret = add_metric(metric_list, pending[i].pe, modifier, metric_no_group, + root_metric, visited, map); + if (ret) + break; + } + + free(pending); + return ret; +} + +/** + * __add_metric - Add a metric to metric_list. + * @metric_list: The list the metric is added to. + * @pe: The pmu_event containing the metric to be added. + * @modifier: if non-null event modifiers like "u". + * @metric_no_group: Should events written to events be grouped "{}" or + * global. Grouping is the default but due to multiplexing the + * user may override. + * @runtime: A special argument for the parser only known at runtime. + * @root_metric: Metrics may reference other metrics to form a tree. In this + * case the root_metric holds all the IDs and a list of referenced + * metrics. When adding a root this argument is NULL. + * @visited: A singly linked list of metric names being added that is used to + * detect recursion. + * @map: The map that is searched for metrics, most commonly the table for the + * architecture perf is running upon. + */ static int __add_metric(struct list_head *metric_list, - struct pmu_event *pe, + const struct pmu_event *pe, + const char *modifier, bool metric_no_group, int runtime, - struct metric **mp, - struct expr_id *parent, - struct expr_ids *ids) + struct metric *root_metric, + const struct visited_metric *visited, + const struct pmu_events_map *map) { - struct metric_ref_node *ref; - struct metric *m; + const struct visited_metric *vm; + int ret; + bool is_root = !root_metric; + struct visited_metric visited_node = { + .name = pe->metric_name, + .parent = visited, + }; - if (*mp == NULL) { + for (vm = visited; vm; vm = vm->parent) { + if (!strcmp(pe->metric_name, vm->name)) { + pr_err("failed: recursion detected for %s\n", pe->metric_name); + return -1; + } + } + + if (is_root) { /* - * We got in here for the parent group, - * allocate it and put it on the list. + * This metric is the root of a tree and may reference other + * metrics that are added recursively. */ - m = zalloc(sizeof(*m)); - if (!m) + root_metric = metric__new(pe, modifier, metric_no_group, runtime); + if (!root_metric) return -ENOMEM; - expr__ctx_init(&m->pctx); - m->metric_name = pe->metric_name; - m->metric_expr = pe->metric_expr; - m->metric_unit = pe->unit; - m->runtime = runtime; - m->has_constraint = metric_no_group || metricgroup__has_constraint(pe); - INIT_LIST_HEAD(&m->metric_refs); - m->metric_refs_cnt = 0; - - parent = expr_ids__alloc(ids); - if (!parent) { - free(m); - return -EINVAL; - } - - parent->id = strdup(pe->metric_name); - if (!parent->id) { - free(m); - return -ENOMEM; - } - *mp = m; } else { - /* - * We got here for the referenced metric, via the - * recursive metricgroup__add_metric call, add - * it to the parent group. - */ - m = *mp; + int cnt = 0; - ref = malloc(sizeof(*ref)); - if (!ref) + /* + * This metric was referenced in a metric higher in the + * tree. Check if the same metric is already resolved in the + * metric_refs list. + */ + if (root_metric->metric_refs) { + for (; root_metric->metric_refs[cnt].metric_name; cnt++) { + if (!strcmp(pe->metric_name, + root_metric->metric_refs[cnt].metric_name)) + return 0; + } + } + + /* Create reference. Need space for the entry and the terminator. */ + root_metric->metric_refs = realloc(root_metric->metric_refs, + (cnt + 2) * sizeof(struct metric_ref)); + if (!root_metric->metric_refs) return -ENOMEM; /* @@ -838,54 +1004,35 @@ static int __add_metric(struct list_head *metric_list, * need to change them, so there's no need to create * our own copy. */ - ref->metric_name = pe->metric_name; - ref->metric_expr = pe->metric_expr; + root_metric->metric_refs[cnt].metric_name = pe->metric_name; + root_metric->metric_refs[cnt].metric_expr = pe->metric_expr; - list_add(&ref->list, &m->metric_refs); - m->metric_refs_cnt++; + /* Null terminate array. */ + root_metric->metric_refs[cnt+1].metric_name = NULL; + root_metric->metric_refs[cnt+1].metric_expr = NULL; } - /* Force all found IDs in metric to have us as parent ID. */ - WARN_ON_ONCE(!parent); - m->pctx.parent = parent; - /* * For both the parent and referenced metrics, we parse - * all the metric's IDs and add it to the parent context. + * all the metric's IDs and add it to the root context. */ - if (expr__find_other(pe->metric_expr, NULL, &m->pctx, runtime) < 0) { - if (m->metric_refs_cnt == 0) { - expr__ctx_clear(&m->pctx); - free(m); - *mp = NULL; - } - return -EINVAL; + if (expr__find_ids(pe->metric_expr, NULL, root_metric->pctx) < 0) { + /* Broken metric. */ + ret = -EINVAL; + } else { + /* Resolve referenced metrics. */ + ret = resolve_metric(metric_list, modifier, metric_no_group, root_metric, + &visited_node, map); } - /* - * We add new group only in the 'parent' call, - * so bail out for referenced metric case. - */ - if (m->metric_refs_cnt) - return 0; + if (ret) { + if (is_root) + metric__free(root_metric); - if (list_empty(metric_list)) - list_add(&m->nd, metric_list); - else { - struct list_head *pos; + } else if (is_root) + list_add(&root_metric->nd, metric_list); - /* Place the largest groups at the front. */ - list_for_each_prev(pos, metric_list) { - struct metric *old = list_entry(pos, struct metric, nd); - - if (hashmap__size(&m->pctx.ids) <= - hashmap__size(&old->pctx.ids)) - break; - } - list_add(&m->nd, pos); - } - - return 0; + return ret; } #define map_for_each_event(__pe, __idx, __map) \ @@ -900,10 +1047,10 @@ static int __add_metric(struct list_head *metric_list, (match_metric(__pe->metric_group, __metric) || \ match_metric(__pe->metric_name, __metric))) -struct pmu_event *metricgroup__find_metric(const char *metric, - struct pmu_events_map *map) +const struct pmu_event *metricgroup__find_metric(const char *metric, + const struct pmu_events_map *map) { - struct pmu_event *pe; + const struct pmu_event *pe; int i; map_for_each_event(pe, i, map) { @@ -914,136 +1061,21 @@ struct pmu_event *metricgroup__find_metric(const char *metric, return NULL; } -static int recursion_check(struct metric *m, const char *id, struct expr_id **parent, - struct expr_ids *ids) -{ - struct expr_id_data *data; - struct expr_id *p; - int ret; - - /* - * We get the parent referenced by 'id' argument and - * traverse through all the parent object IDs to check - * if we already processed 'id', if we did, it's recursion - * and we fail. - */ - ret = expr__get_id(&m->pctx, id, &data); - if (ret) - return ret; - - p = expr_id_data__parent(data); - - while (p->parent) { - if (!strcmp(p->id, id)) { - pr_err("failed: recursion detected for %s\n", id); - return -1; - } - p = p->parent; - } - - /* - * If we are over the limit of static entris, the metric - * is too difficult/nested to process, fail as well. - */ - p = expr_ids__alloc(ids); - if (!p) { - pr_err("failed: too many nested metrics\n"); - return -EINVAL; - } - - p->id = strdup(id); - p->parent = expr_id_data__parent(data); - *parent = p; - - return p->id ? 0 : -ENOMEM; -} - static int add_metric(struct list_head *metric_list, - struct pmu_event *pe, + const struct pmu_event *pe, + const char *modifier, bool metric_no_group, - struct metric **mp, - struct expr_id *parent, - struct expr_ids *ids); - -static int __resolve_metric(struct metric *m, - bool metric_no_group, - struct list_head *metric_list, - struct pmu_events_map *map, - struct expr_ids *ids) + struct metric *root_metric, + const struct visited_metric *visited, + const struct pmu_events_map *map) { - struct hashmap_entry *cur; - size_t bkt; - bool all; - int ret; - - /* - * Iterate all the parsed IDs and if there's metric, - * add it to the context. - */ - do { - all = true; - hashmap__for_each_entry((&m->pctx.ids), cur, bkt) { - struct expr_id *parent; - struct pmu_event *pe; - - pe = metricgroup__find_metric(cur->key, map); - if (!pe) - continue; - - ret = recursion_check(m, cur->key, &parent, ids); - if (ret) - return ret; - - all = false; - /* The metric key itself needs to go out.. */ - expr__del_id(&m->pctx, cur->key); - - /* ... and it gets resolved to the parent context. */ - ret = add_metric(metric_list, pe, metric_no_group, &m, parent, ids); - if (ret) - return ret; - - /* - * We added new metric to hashmap, so we need - * to break the iteration and start over. - */ - break; - } - } while (!all); - - return 0; -} - -static int resolve_metric(bool metric_no_group, - struct list_head *metric_list, - struct pmu_events_map *map, - struct expr_ids *ids) -{ - struct metric *m; - int err; - - list_for_each_entry(m, metric_list, nd) { - err = __resolve_metric(m, metric_no_group, metric_list, map, ids); - if (err) - return err; - } - return 0; -} - -static int add_metric(struct list_head *metric_list, - struct pmu_event *pe, - bool metric_no_group, - struct metric **m, - struct expr_id *parent, - struct expr_ids *ids) -{ - struct metric *orig = *m; int ret = 0; pr_debug("metric expr %s for %s\n", pe->metric_expr, pe->metric_name); if (!strstr(pe->metric_expr, "?")) { - ret = __add_metric(metric_list, pe, metric_no_group, 1, m, parent, ids); + ret = __add_metric(metric_list, pe, modifier, metric_no_group, 0, + root_metric, visited, map); } else { int j, count; @@ -1054,29 +1086,25 @@ static int add_metric(struct list_head *metric_list, * those events to metric_list. */ - for (j = 0; j < count && !ret; j++, *m = orig) - ret = __add_metric(metric_list, pe, metric_no_group, j, m, parent, ids); + for (j = 0; j < count && !ret; j++) + ret = __add_metric(metric_list, pe, modifier, metric_no_group, j, + root_metric, visited, map); } return ret; } -static int metricgroup__add_metric_sys_event_iter(struct pmu_event *pe, +static int metricgroup__add_metric_sys_event_iter(const struct pmu_event *pe, void *data) { struct metricgroup_add_iter_data *d = data; - struct metric *m = NULL; int ret; - if (!match_pe_metric(pe, d->metric)) + if (!match_pe_metric(pe, d->metric_name)) return 0; - ret = add_metric(d->metric_list, pe, d->metric_no_group, &m, NULL, d->ids); - if (ret) - goto out; - - ret = resolve_metric(d->metric_no_group, - d->metric_list, NULL, d->ids); + ret = add_metric(d->metric_list, pe, d->modifier, d->metric_no_group, + d->root_metric, d->visited, d->map); if (ret) goto out; @@ -1087,32 +1115,61 @@ static int metricgroup__add_metric_sys_event_iter(struct pmu_event *pe, return ret; } -static int metricgroup__add_metric(const char *metric, bool metric_no_group, - struct strbuf *events, - struct list_head *metric_list, - struct pmu_events_map *map) +/** + * metric_list_cmp - list_sort comparator that sorts metrics with more events to + * the front. duration_time is excluded from the count. + */ +static int metric_list_cmp(void *priv __maybe_unused, const struct list_head *l, + const struct list_head *r) { - struct expr_ids ids = { .cnt = 0, }; - struct pmu_event *pe; - struct metric *m; + const struct metric *left = container_of(l, struct metric, nd); + const struct metric *right = container_of(r, struct metric, nd); + struct expr_id_data *data; + int left_count, right_count; + + left_count = hashmap__size(left->pctx->ids); + if (!expr__get_id(left->pctx, "duration_time", &data)) + left_count--; + + right_count = hashmap__size(right->pctx->ids); + if (!expr__get_id(right->pctx, "duration_time", &data)) + right_count--; + + return right_count - left_count; +} + +/** + * metricgroup__add_metric - Find and add a metric, or a metric group. + * @metric_name: The name of the metric or metric group. For example, "IPC" + * could be the name of a metric and "TopDownL1" the name of a + * metric group. + * @modifier: if non-null event modifiers like "u". + * @metric_no_group: Should events written to events be grouped "{}" or + * global. Grouping is the default but due to multiplexing the + * user may override. + * @metric_list: The list that the metric or metric group are added to. + * @map: The map that is searched for metrics, most commonly the table for the + * architecture perf is running upon. + */ +static int metricgroup__add_metric(const char *metric_name, const char *modifier, + bool metric_no_group, + struct list_head *metric_list, + const struct pmu_events_map *map) +{ + const struct pmu_event *pe; LIST_HEAD(list); int i, ret; bool has_match = false; - map_for_each_metric(pe, i, map, metric) { + /* + * Iterate over all metrics seeing if metric matches either the name or + * group. When it does add the metric to the list. + */ + map_for_each_metric(pe, i, map, metric_name) { has_match = true; - m = NULL; - - ret = add_metric(&list, pe, metric_no_group, &m, NULL, &ids); - if (ret) - goto out; - - /* - * Process any possible referenced metrics - * included in the expression. - */ - ret = resolve_metric(metric_no_group, - &list, map, &ids); + ret = add_metric(&list, pe, modifier, metric_no_group, + /*root_metric=*/NULL, + /*visited_metrics=*/NULL, map); if (ret) goto out; } @@ -1122,34 +1179,20 @@ static int metricgroup__add_metric(const char *metric, bool metric_no_group, .fn = metricgroup__add_metric_sys_event_iter, .data = (void *) &(struct metricgroup_add_iter_data) { .metric_list = &list, - .metric = metric, + .metric_name = metric_name, + .modifier = modifier, .metric_no_group = metric_no_group, - .ids = &ids, .has_match = &has_match, .ret = &ret, + .map = map, }, }; pmu_for_each_sys_event(metricgroup__sys_event_iter, &data); } /* End of pmu events. */ - if (!has_match) { + if (!has_match) ret = -EINVAL; - goto out; - } - - list_for_each_entry(m, &list, nd) { - if (events->len > 0) - strbuf_addf(events, ","); - - if (m->has_constraint) { - metricgroup__add_metric_non_group(events, - &m->pctx); - } else { - metricgroup__add_metric_weak_group(events, - &m->pctx); - } - } out: /* @@ -1157,51 +1200,63 @@ static int metricgroup__add_metric(const char *metric, bool metric_no_group, * even if it's failed */ list_splice(&list, metric_list); - expr_ids__exit(&ids); return ret; } +/** + * metricgroup__add_metric_list - Find and add metrics, or metric groups, + * specified in a list. + * @list: the list of metrics or metric groups. For example, "IPC,CPI,TopDownL1" + * would match the IPC and CPI metrics, and TopDownL1 would match all + * the metrics in the TopDownL1 group. + * @metric_no_group: Should events written to events be grouped "{}" or + * global. Grouping is the default but due to multiplexing the + * user may override. + * @metric_list: The list that metrics are added to. + * @map: The map that is searched for metrics, most commonly the table for the + * architecture perf is running upon. + */ static int metricgroup__add_metric_list(const char *list, bool metric_no_group, - struct strbuf *events, struct list_head *metric_list, - struct pmu_events_map *map) + const struct pmu_events_map *map) { - char *llist, *nlist, *p; - int ret = -EINVAL; + char *list_itr, *list_copy, *metric_name, *modifier; + int ret, count = 0; - nlist = strdup(list); - if (!nlist) + list_copy = strdup(list); + if (!list_copy) return -ENOMEM; - llist = nlist; + list_itr = list_copy; - strbuf_init(events, 100); - strbuf_addf(events, "%s", ""); + while ((metric_name = strsep(&list_itr, ",")) != NULL) { + modifier = strchr(metric_name, ':'); + if (modifier) + *modifier++ = '\0'; - while ((p = strsep(&llist, ",")) != NULL) { - ret = metricgroup__add_metric(p, metric_no_group, events, - metric_list, map); - if (ret == -EINVAL) { - fprintf(stderr, "Cannot find metric or group `%s'\n", - p); + ret = metricgroup__add_metric(metric_name, modifier, + metric_no_group, metric_list, + map); + if (ret == -EINVAL) + pr_err("Cannot find metric or group `%s'\n", metric_name); + + if (ret) break; - } - } - free(nlist); - if (!ret) + count++; + } + free(list_copy); + + if (!ret) { + /* + * Warn about nmi_watchdog if any parsed metrics had the + * NO_NMI_WATCHDOG constraint. + */ metricgroup___watchdog_constraint_hint(NULL, true); - - return ret; -} - -static void metric__free_refs(struct metric *metric) -{ - struct metric_ref_node *ref, *tmp; - - list_for_each_entry_safe(ref, tmp, &metric->metric_refs, list) { - list_del(&ref->list); - free(ref); + /* No metrics. */ + if (count == 0) + return -EINVAL; } + return ret; } static void metricgroup__free_metrics(struct list_head *metric_list) @@ -1209,43 +1264,261 @@ static void metricgroup__free_metrics(struct list_head *metric_list) struct metric *m, *tmp; list_for_each_entry_safe (m, tmp, metric_list, nd) { - metric__free_refs(m); - expr__ctx_clear(&m->pctx); list_del_init(&m->nd); - free(m); + metric__free(m); } } +/** + * build_combined_expr_ctx - Make an expr_parse_ctx with all has_constraint + * metric IDs, as the IDs are held in a set, + * duplicates will be removed. + * @metric_list: List to take metrics from. + * @combined: Out argument for result. + */ +static int build_combined_expr_ctx(const struct list_head *metric_list, + struct expr_parse_ctx **combined) +{ + struct hashmap_entry *cur; + size_t bkt; + struct metric *m; + char *dup; + int ret; + + *combined = expr__ctx_new(); + if (!*combined) + return -ENOMEM; + + list_for_each_entry(m, metric_list, nd) { + if (m->has_constraint && !m->modifier) { + hashmap__for_each_entry(m->pctx->ids, cur, bkt) { + dup = strdup(cur->key); + if (!dup) { + ret = -ENOMEM; + goto err_out; + } + ret = expr__add_id(*combined, dup); + if (ret) + goto err_out; + } + } + } + return 0; +err_out: + expr__ctx_free(*combined); + *combined = NULL; + return ret; +} + +/** + * parse_ids - Build the event string for the ids and parse them creating an + * evlist. The encoded metric_ids are decoded. + * @metric_no_merge: is metric sharing explicitly disabled. + * @fake_pmu: used when testing metrics not supported by the current CPU. + * @ids: the event identifiers parsed from a metric. + * @modifier: any modifiers added to the events. + * @has_constraint: false if events should be placed in a weak group. + * @out_evlist: the created list of events. + */ +static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu, + struct expr_parse_ctx *ids, const char *modifier, + bool has_constraint, struct evlist **out_evlist) +{ + struct parse_events_error parse_error; + struct evlist *parsed_evlist; + struct strbuf events = STRBUF_INIT; + int ret; + + *out_evlist = NULL; + if (!metric_no_merge || hashmap__size(ids->ids) == 0) { + char *tmp; + /* + * We may fail to share events between metrics because + * duration_time isn't present in one metric. For example, a + * ratio of cache misses doesn't need duration_time but the same + * events may be used for a misses per second. Events without + * sharing implies multiplexing, that is best avoided, so place + * duration_time in every group. + * + * Also, there may be no ids/events in the expression parsing + * context because of constant evaluation, e.g.: + * event1 if #smt_on else 0 + * Add a duration_time event to avoid a parse error on an empty + * string. + */ + tmp = strdup("duration_time"); + if (!tmp) + return -ENOMEM; + + ids__insert(ids->ids, tmp); + } + ret = metricgroup__build_event_string(&events, ids, modifier, + has_constraint); + if (ret) + return ret; + + parsed_evlist = evlist__new(); + if (!parsed_evlist) { + ret = -ENOMEM; + goto err_out; + } + pr_debug("Parsing metric events '%s'\n", events.buf); + parse_events_error__init(&parse_error); + ret = __parse_events(parsed_evlist, events.buf, &parse_error, fake_pmu); + if (ret) { + parse_events_error__print(&parse_error, events.buf); + goto err_out; + } + ret = decode_all_metric_ids(parsed_evlist, modifier); + if (ret) + goto err_out; + + *out_evlist = parsed_evlist; + parsed_evlist = NULL; +err_out: + parse_events_error__exit(&parse_error); + evlist__delete(parsed_evlist); + strbuf_release(&events); + return ret; +} + static int parse_groups(struct evlist *perf_evlist, const char *str, bool metric_no_group, bool metric_no_merge, struct perf_pmu *fake_pmu, - struct rblist *metric_events, - struct pmu_events_map *map) + struct rblist *metric_events_list, + const struct pmu_events_map *map) { - struct parse_events_error parse_error; - struct strbuf extra_events; + struct evlist *combined_evlist = NULL; LIST_HEAD(metric_list); + struct metric *m; int ret; - if (metric_events->nr_entries == 0) - metricgroup__rblist_init(metric_events); + if (metric_events_list->nr_entries == 0) + metricgroup__rblist_init(metric_events_list); ret = metricgroup__add_metric_list(str, metric_no_group, - &extra_events, &metric_list, map); + &metric_list, map); if (ret) goto out; - pr_debug("adding %s\n", extra_events.buf); - bzero(&parse_error, sizeof(parse_error)); - ret = __parse_events(perf_evlist, extra_events.buf, &parse_error, fake_pmu); - if (ret) { - parse_events_print_error(&parse_error, extra_events.buf); - goto out; + + /* Sort metrics from largest to smallest. */ + list_sort(NULL, &metric_list, metric_list_cmp); + + if (!metric_no_merge) { + struct expr_parse_ctx *combined = NULL; + + ret = build_combined_expr_ctx(&metric_list, &combined); + + if (!ret && combined && hashmap__size(combined->ids)) { + ret = parse_ids(metric_no_merge, fake_pmu, combined, + /*modifier=*/NULL, + /*has_constraint=*/true, + &combined_evlist); + } + if (combined) + expr__ctx_free(combined); + + if (ret) + goto out; } - ret = metricgroup__setup_events(&metric_list, metric_no_merge, - perf_evlist, metric_events); + + list_for_each_entry(m, &metric_list, nd) { + struct metric_event *me; + struct evsel **metric_events; + struct evlist *metric_evlist = NULL; + struct metric *n; + struct metric_expr *expr; + + if (combined_evlist && m->has_constraint) { + metric_evlist = combined_evlist; + } else if (!metric_no_merge) { + /* + * See if the IDs for this metric are a subset of an + * earlier metric. + */ + list_for_each_entry(n, &metric_list, nd) { + if (m == n) + break; + + if (n->evlist == NULL) + continue; + + if ((!m->modifier && n->modifier) || + (m->modifier && !n->modifier) || + (m->modifier && n->modifier && + strcmp(m->modifier, n->modifier))) + continue; + + if (expr__subset_of_ids(n->pctx, m->pctx)) { + pr_debug("Events in '%s' fully contained within '%s'\n", + m->metric_name, n->metric_name); + metric_evlist = n->evlist; + break; + } + + } + } + if (!metric_evlist) { + ret = parse_ids(metric_no_merge, fake_pmu, m->pctx, m->modifier, + m->has_constraint, &m->evlist); + if (ret) + goto out; + + metric_evlist = m->evlist; + } + ret = setup_metric_events(m->pctx->ids, metric_evlist, &metric_events); + if (ret) { + pr_debug("Cannot resolve IDs for %s: %s\n", + m->metric_name, m->metric_expr); + goto out; + } + + me = metricgroup__lookup(metric_events_list, metric_events[0], true); + + expr = malloc(sizeof(struct metric_expr)); + if (!expr) { + ret = -ENOMEM; + free(metric_events); + goto out; + } + + expr->metric_refs = m->metric_refs; + m->metric_refs = NULL; + expr->metric_expr = m->metric_expr; + if (m->modifier) { + char *tmp; + + if (asprintf(&tmp, "%s:%s", m->metric_name, m->modifier) < 0) + expr->metric_name = NULL; + else + expr->metric_name = tmp; + } else + expr->metric_name = strdup(m->metric_name); + + if (!expr->metric_name) { + ret = -ENOMEM; + free(metric_events); + goto out; + } + expr->metric_unit = m->metric_unit; + expr->metric_events = metric_events; + expr->runtime = m->pctx->runtime; + list_add(&expr->nd, &me->head); + } + + + if (combined_evlist) { + evlist__splice_list_tail(perf_evlist, &combined_evlist->core.entries); + evlist__delete(combined_evlist); + } + + list_for_each_entry(m, &metric_list, nd) { + if (m->evlist) + evlist__splice_list_tail(perf_evlist, &m->evlist->core.entries); + } + out: metricgroup__free_metrics(&metric_list); - strbuf_release(&extra_events); return ret; } @@ -1256,14 +1529,14 @@ int metricgroup__parse_groups(const struct option *opt, struct rblist *metric_events) { struct evlist *perf_evlist = *(struct evlist **)opt->value; - struct pmu_events_map *map = pmu_events_map__find(); + const struct pmu_events_map *map = pmu_events_map__find(); return parse_groups(perf_evlist, str, metric_no_group, metric_no_merge, NULL, metric_events, map); } int metricgroup__parse_groups_test(struct evlist *evlist, - struct pmu_events_map *map, + const struct pmu_events_map *map, const char *str, bool metric_no_group, bool metric_no_merge, @@ -1275,8 +1548,8 @@ int metricgroup__parse_groups_test(struct evlist *evlist, bool metricgroup__has_metric(const char *metric) { - struct pmu_events_map *map = pmu_events_map__find(); - struct pmu_event *pe; + const struct pmu_events_map *map = pmu_events_map__find(); + const struct pmu_event *pe; int i; if (!map) @@ -1328,7 +1601,10 @@ int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp, return -ENOMEM; new_expr->metric_expr = old_expr->metric_expr; - new_expr->metric_name = old_expr->metric_name; + new_expr->metric_name = strdup(old_expr->metric_name); + if (!new_expr->metric_name) + return -ENOMEM; + new_expr->metric_unit = old_expr->metric_unit; new_expr->runtime = old_expr->runtime; diff --git a/tools/perf/util/metricgroup.h b/tools/perf/util/metricgroup.h index cc4a92492a..2b42b778d1 100644 --- a/tools/perf/util/metricgroup.h +++ b/tools/perf/util/metricgroup.h @@ -14,24 +14,51 @@ struct rblist; struct pmu_events_map; struct cgroup; +/** + * A node in a rblist keyed by the evsel. The global rblist of metric events + * generally exists in perf_stat_config. The evsel is looked up in the rblist + * yielding a list of metric_expr. + */ struct metric_event { struct rb_node nd; struct evsel *evsel; struct list_head head; /* list of metric_expr */ }; +/** + * A metric referenced by a metric_expr. When parsing a metric expression IDs + * will be looked up, matching either a value (from metric_events) or a + * metric_ref. A metric_ref will then be parsed recursively. The metric_refs and + * metric_events need to be known before parsing so that their values may be + * placed in the parse context for lookup. + */ struct metric_ref { const char *metric_name; const char *metric_expr; }; +/** + * One in a list of metric_expr associated with an evsel. The data is used to + * generate a metric value during stat output. + */ struct metric_expr { struct list_head nd; + /** The expression to parse, for example, "instructions/cycles". */ const char *metric_expr; + /** The name of the meric such as "IPC". */ const char *metric_name; + /** + * The "ScaleUnit" that scales and adds a unit to the metric during + * output. For example, "6.4e-05MiB" means to scale the resulting metric + * by 6.4e-05 (typically converting a unit like cache lines to something + * more human intelligible) and then add "MiB" afterward when displayed. + */ const char *metric_unit; + /** Null terminated array of events used by the metric. */ struct evsel **metric_events; + /** Null terminated array of referenced metrics. */ struct metric_ref *metric_refs; + /** A value substituted for '?' during parsing. */ int runtime; }; @@ -43,19 +70,19 @@ int metricgroup__parse_groups(const struct option *opt, bool metric_no_group, bool metric_no_merge, struct rblist *metric_events); -struct pmu_event *metricgroup__find_metric(const char *metric, - struct pmu_events_map *map); +const struct pmu_event *metricgroup__find_metric(const char *metric, + const struct pmu_events_map *map); int metricgroup__parse_groups_test(struct evlist *evlist, - struct pmu_events_map *map, + const struct pmu_events_map *map, const char *str, bool metric_no_group, bool metric_no_merge, struct rblist *metric_events); void metricgroup__print(bool metrics, bool groups, char *filter, - bool raw, bool details); + bool raw, bool details, const char *pmu_name); bool metricgroup__has_metric(const char *metric); -int arch_get_runtimeparam(struct pmu_event *pe __maybe_unused); +int arch_get_runtimeparam(const struct pmu_event *pe __maybe_unused); void metricgroup__rblist_exit(struct rblist *metric_events); int metricgroup__copy_metric_events(struct evlist *evlist, struct cgroup *cgrp, diff --git a/tools/perf/util/mmap.h b/tools/perf/util/mmap.h index af33118354..83f6bd4d40 100644 --- a/tools/perf/util/mmap.h +++ b/tools/perf/util/mmap.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include // for cpu_set_t #ifdef HAVE_AIO_SUPPORT @@ -52,7 +53,7 @@ struct mmap_params { struct auxtrace_mmap_params auxtrace_mp; }; -int mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, int cpu); +int mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, struct perf_cpu cpu); void mmap__munmap(struct mmap *map); union perf_event *perf_mmap__read_forward(struct mmap *map); @@ -64,4 +65,7 @@ size_t mmap__mmap_len(struct mmap *map); void mmap_cpu_mask__scnprintf(struct mmap_cpu_mask *mask, const char *tag); +int mmap_cpu_mask__duplicate(struct mmap_cpu_mask *original, + struct mmap_cpu_mask *clone); + #endif /*__PERF_MMAP_H */ diff --git a/tools/perf/util/parse-events-hybrid.c b/tools/perf/util/parse-events-hybrid.c index b234d95fb1..284f8eabd3 100644 --- a/tools/perf/util/parse-events-hybrid.c +++ b/tools/perf/util/parse-events-hybrid.c @@ -38,7 +38,8 @@ static void config_hybrid_attr(struct perf_event_attr *attr, static int create_event_hybrid(__u32 config_type, int *idx, struct list_head *list, - struct perf_event_attr *attr, char *name, + struct perf_event_attr *attr, const char *name, + const char *metric_id, struct list_head *config_terms, struct perf_pmu *pmu) { @@ -47,7 +48,7 @@ static int create_event_hybrid(__u32 config_type, int *idx, __u64 config = attr->config; config_hybrid_attr(attr, config_type, pmu->type); - evsel = parse_events__add_event_hybrid(list, idx, attr, name, + evsel = parse_events__add_event_hybrid(list, idx, attr, name, metric_id, pmu, config_terms); if (evsel) evsel->pmu_name = strdup(pmu->name); @@ -62,15 +63,19 @@ static int create_event_hybrid(__u32 config_type, int *idx, static int pmu_cmp(struct parse_events_state *parse_state, struct perf_pmu *pmu) { - if (!parse_state->hybrid_pmu_name) - return 0; + if (parse_state->evlist && parse_state->evlist->hybrid_pmu_name) + return strcmp(parse_state->evlist->hybrid_pmu_name, pmu->name); - return strcmp(parse_state->hybrid_pmu_name, pmu->name); + if (parse_state->hybrid_pmu_name) + return strcmp(parse_state->hybrid_pmu_name, pmu->name); + + return 0; } static int add_hw_hybrid(struct parse_events_state *parse_state, struct list_head *list, struct perf_event_attr *attr, - char *name, struct list_head *config_terms) + const char *name, const char *metric_id, + struct list_head *config_terms) { struct perf_pmu *pmu; int ret; @@ -84,7 +89,7 @@ static int add_hw_hybrid(struct parse_events_state *parse_state, copy_config_terms(&terms, config_terms); ret = create_event_hybrid(PERF_TYPE_HARDWARE, &parse_state->idx, list, attr, name, - &terms, pmu); + metric_id, &terms, pmu); free_config_terms(&terms); if (ret) return ret; @@ -94,14 +99,16 @@ static int add_hw_hybrid(struct parse_events_state *parse_state, } static int create_raw_event_hybrid(int *idx, struct list_head *list, - struct perf_event_attr *attr, char *name, + struct perf_event_attr *attr, + const char *name, + const char *metric_id, struct list_head *config_terms, struct perf_pmu *pmu) { struct evsel *evsel; attr->type = pmu->type; - evsel = parse_events__add_event_hybrid(list, idx, attr, name, + evsel = parse_events__add_event_hybrid(list, idx, attr, name, metric_id, pmu, config_terms); if (evsel) evsel->pmu_name = strdup(pmu->name); @@ -113,7 +120,8 @@ static int create_raw_event_hybrid(int *idx, struct list_head *list, static int add_raw_hybrid(struct parse_events_state *parse_state, struct list_head *list, struct perf_event_attr *attr, - char *name, struct list_head *config_terms) + const char *name, const char *metric_id, + struct list_head *config_terms) { struct perf_pmu *pmu; int ret; @@ -126,7 +134,7 @@ static int add_raw_hybrid(struct parse_events_state *parse_state, copy_config_terms(&terms, config_terms); ret = create_raw_event_hybrid(&parse_state->idx, list, attr, - name, &terms, pmu); + name, metric_id, &terms, pmu); free_config_terms(&terms); if (ret) return ret; @@ -138,7 +146,8 @@ static int add_raw_hybrid(struct parse_events_state *parse_state, int parse_events__add_numeric_hybrid(struct parse_events_state *parse_state, struct list_head *list, struct perf_event_attr *attr, - char *name, struct list_head *config_terms, + const char *name, const char *metric_id, + struct list_head *config_terms, bool *hybrid) { *hybrid = false; @@ -150,16 +159,18 @@ int parse_events__add_numeric_hybrid(struct parse_events_state *parse_state, *hybrid = true; if (attr->type != PERF_TYPE_RAW) { - return add_hw_hybrid(parse_state, list, attr, name, + return add_hw_hybrid(parse_state, list, attr, name, metric_id, config_terms); } - return add_raw_hybrid(parse_state, list, attr, name, + return add_raw_hybrid(parse_state, list, attr, name, metric_id, config_terms); } int parse_events__add_cache_hybrid(struct list_head *list, int *idx, - struct perf_event_attr *attr, char *name, + struct perf_event_attr *attr, + const char *name, + const char *metric_id, struct list_head *config_terms, bool *hybrid, struct parse_events_state *parse_state) @@ -180,7 +191,7 @@ int parse_events__add_cache_hybrid(struct list_head *list, int *idx, copy_config_terms(&terms, config_terms); ret = create_event_hybrid(PERF_TYPE_HW_CACHE, idx, list, - attr, name, &terms, pmu); + attr, name, metric_id, &terms, pmu); free_config_terms(&terms); if (ret) return ret; diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 51a2219df6..24997925ae 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -19,8 +19,6 @@ #include #include "string2.h" #include "strlist.h" -#include "symbol.h" -#include "header.h" #include "bpf-loader.h" #include "debug.h" #include @@ -193,39 +191,6 @@ static int tp_event_has_id(const char *dir_path, struct dirent *evt_dir) #define MAX_EVENT_LENGTH 512 -void parse_events__handle_error(struct parse_events_error *err, int idx, - char *str, char *help) -{ - if (WARN(!str, "WARNING: failed to provide error string\n")) { - free(help); - return; - } - switch (err->num_errors) { - case 0: - err->idx = idx; - err->str = str; - err->help = help; - break; - case 1: - err->first_idx = err->idx; - err->idx = idx; - err->first_str = err->str; - err->str = str; - err->first_help = err->help; - err->help = help; - break; - default: - pr_debug("Multiple errors dropping message: %s (%s)\n", - err->str, err->help); - free(err->str); - err->str = str; - free(err->help); - err->help = help; - break; - } - err->num_errors++; -} - struct tracepoint_path *tracepoint_id_to_path(u64 config) { struct tracepoint_path *path = NULL; @@ -334,12 +299,7 @@ const char *event_type(int type) return "unknown"; } -static int parse_events__is_name_term(struct parse_events_term *term) -{ - return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME; -} - -static char *get_config_name(struct list_head *head_terms) +static char *get_config_str(struct list_head *head_terms, int type_term) { struct parse_events_term *term; @@ -347,17 +307,27 @@ static char *get_config_name(struct list_head *head_terms) return NULL; list_for_each_entry(term, head_terms, list) - if (parse_events__is_name_term(term)) + if (term->type_term == type_term) return term->val.str; return NULL; } +static char *get_config_metric_id(struct list_head *head_terms) +{ + return get_config_str(head_terms, PARSE_EVENTS__TERM_TYPE_METRIC_ID); +} + +static char *get_config_name(struct list_head *head_terms) +{ + return get_config_str(head_terms, PARSE_EVENTS__TERM_TYPE_NAME); +} + static struct evsel * __add_event(struct list_head *list, int *idx, struct perf_event_attr *attr, bool init_attr, - char *name, struct perf_pmu *pmu, + const char *name, const char *metric_id, struct perf_pmu *pmu, struct list_head *config_terms, bool auto_merge_stats, const char *cpu_list) { @@ -386,6 +356,9 @@ __add_event(struct list_head *list, int *idx, if (name) evsel->name = strdup(name); + if (metric_id) + evsel->metric_id = strdup(metric_id); + if (config_terms) list_splice_init(config_terms, &evsel->config_terms); @@ -396,18 +369,21 @@ __add_event(struct list_head *list, int *idx, } struct evsel *parse_events__add_event(int idx, struct perf_event_attr *attr, - char *name, struct perf_pmu *pmu) + const char *name, const char *metric_id, + struct perf_pmu *pmu) { - return __add_event(NULL, &idx, attr, false, name, pmu, NULL, false, - NULL); + return __add_event(/*list=*/NULL, &idx, attr, /*init_attr=*/false, name, + metric_id, pmu, /*config_terms=*/NULL, + /*auto_merge_stats=*/false, /*cpu_list=*/NULL); } static int add_event(struct list_head *list, int *idx, - struct perf_event_attr *attr, char *name, - struct list_head *config_terms) + struct perf_event_attr *attr, const char *name, + const char *metric_id, struct list_head *config_terms) { - return __add_event(list, idx, attr, true, name, NULL, config_terms, - false, NULL) ? 0 : -ENOMEM; + return __add_event(list, idx, attr, /*init_attr*/true, name, metric_id, + /*pmu=*/NULL, config_terms, + /*auto_merge_stats=*/false, /*cpu_list=*/NULL) ? 0 : -ENOMEM; } static int add_event_tool(struct list_head *list, int *idx, @@ -419,13 +395,17 @@ static int add_event_tool(struct list_head *list, int *idx, .config = PERF_COUNT_SW_DUMMY, }; - evsel = __add_event(list, idx, &attr, true, NULL, NULL, NULL, false, - "0"); + evsel = __add_event(list, idx, &attr, /*init_attr=*/true, /*name=*/NULL, + /*metric_id=*/NULL, /*pmu=*/NULL, + /*config_terms=*/NULL, /*auto_merge_stats=*/false, + /*cpu_list=*/"0"); if (!evsel) return -ENOMEM; evsel->tool_event = tool_event; - if (tool_event == PERF_TOOL_DURATION_TIME) - evsel->unit = "ns"; + if (tool_event == PERF_TOOL_DURATION_TIME) { + free((char *)evsel->unit); + evsel->unit = strdup("ns"); + } return 0; } @@ -466,7 +446,8 @@ int parse_events_add_cache(struct list_head *list, int *idx, { struct perf_event_attr attr; LIST_HEAD(config_terms); - char name[MAX_NAME_LEN], *config_name; + char name[MAX_NAME_LEN]; + const char *config_name, *metric_id; int cache_type = -1, cache_op = -1, cache_result = -1; char *op_result[2] = { op_result1, op_result2 }; int i, n, ret; @@ -531,13 +512,17 @@ int parse_events_add_cache(struct list_head *list, int *idx, return -ENOMEM; } + metric_id = get_config_metric_id(head_config); ret = parse_events__add_cache_hybrid(list, idx, &attr, - config_name ? : name, &config_terms, + config_name ? : name, + metric_id, + &config_terms, &hybrid, parse_state); if (hybrid) goto out_free_terms; - ret = add_event(list, idx, &attr, config_name ? : name, &config_terms); + ret = add_event(list, idx, &attr, config_name ? : name, metric_id, + &config_terms); out_free_terms: free_config_terms(&config_terms); return ret; @@ -571,7 +556,7 @@ static void tracepoint_error(struct parse_events_error *e, int err, } tracing_path__strerror_open_tp(err, help, sizeof(help), sys, name); - parse_events__handle_error(e, 0, strdup(str), strdup(help)); + parse_events_error__handle(e, 0, strdup(str), strdup(help)); } static int add_tracepoint(struct list_head *list, int *idx, @@ -795,7 +780,7 @@ int parse_events_load_bpf_obj(struct parse_events_state *parse_state, return 0; errout: - parse_events__handle_error(parse_state->error, 0, + parse_events_error__handle(parse_state->error, 0, strdup(errbuf), strdup("(add -v to see detail)")); return err; } @@ -815,7 +800,7 @@ parse_events_config_bpf(struct parse_events_state *parse_state, int err; if (term->type_term != PARSE_EVENTS__TERM_TYPE_USER) { - parse_events__handle_error(parse_state->error, term->err_term, + parse_events_error__handle(parse_state->error, term->err_term, strdup("Invalid config term for BPF object"), NULL); return -EINVAL; @@ -835,7 +820,7 @@ parse_events_config_bpf(struct parse_events_state *parse_state, else idx = term->err_term + error_pos; - parse_events__handle_error(parse_state->error, idx, + parse_events_error__handle(parse_state->error, idx, strdup(errbuf), strdup( "Hint:\tValid config terms:\n" @@ -907,7 +892,7 @@ int parse_events_load_bpf(struct parse_events_state *parse_state, -err, errbuf, sizeof(errbuf)); - parse_events__handle_error(parse_state->error, 0, + parse_events_error__handle(parse_state->error, 0, strdup(errbuf), strdup("(add -v to see detail)")); return err; } @@ -931,7 +916,7 @@ int parse_events_load_bpf_obj(struct parse_events_state *parse_state, struct bpf_object *obj __maybe_unused, struct list_head *head_config __maybe_unused) { - parse_events__handle_error(parse_state->error, 0, + parse_events_error__handle(parse_state->error, 0, strdup("BPF support is not compiled"), strdup("Make sure libbpf-devel is available at build time.")); return -ENOTSUP; @@ -943,7 +928,7 @@ int parse_events_load_bpf(struct parse_events_state *parse_state, bool source __maybe_unused, struct list_head *head_config __maybe_unused) { - parse_events__handle_error(parse_state->error, 0, + parse_events_error__handle(parse_state->error, 0, strdup("BPF support is not compiled"), strdup("Make sure libbpf-devel is available at build time.")); return -ENOTSUP; @@ -1014,7 +999,8 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, attr.type = PERF_TYPE_BREAKPOINT; attr.sample_period = 1; - return add_event(list, idx, &attr, NULL, NULL); + return add_event(list, idx, &attr, /*name=*/NULL, /*mertic_id=*/NULL, + /*config_terms=*/NULL); } static int check_type_val(struct parse_events_term *term, @@ -1025,7 +1011,7 @@ static int check_type_val(struct parse_events_term *term, return 0; if (err) { - parse_events__handle_error(err, term->err_val, + parse_events_error__handle(err, term->err_val, type == PARSE_EVENTS__TERM_TYPE_NUM ? strdup("expected numeric value") : strdup("expected string value"), @@ -1059,6 +1045,7 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = { [PARSE_EVENTS__TERM_TYPE_PERCORE] = "percore", [PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT] = "aux-output", [PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE] = "aux-sample-size", + [PARSE_EVENTS__TERM_TYPE_METRIC_ID] = "metric-id", }; static bool config_term_shrinked; @@ -1069,7 +1056,7 @@ config_term_avail(int term_type, struct parse_events_error *err) char *err_str; if (term_type < 0 || term_type >= __PARSE_EVENTS__TERM_TYPE_NR) { - parse_events__handle_error(err, -1, + parse_events_error__handle(err, -1, strdup("Invalid term_type"), NULL); return false; } @@ -1081,6 +1068,7 @@ config_term_avail(int term_type, struct parse_events_error *err) case PARSE_EVENTS__TERM_TYPE_CONFIG1: case PARSE_EVENTS__TERM_TYPE_CONFIG2: case PARSE_EVENTS__TERM_TYPE_NAME: + case PARSE_EVENTS__TERM_TYPE_METRIC_ID: case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: case PARSE_EVENTS__TERM_TYPE_PERCORE: return true; @@ -1091,7 +1079,7 @@ config_term_avail(int term_type, struct parse_events_error *err) /* term_type is validated so indexing is safe */ if (asprintf(&err_str, "'%s' is not usable in 'perf stat'", config_term_names[term_type]) >= 0) - parse_events__handle_error(err, -1, err_str, NULL); + parse_events_error__handle(err, -1, err_str, NULL); return false; } } @@ -1135,7 +1123,7 @@ do { \ if (strcmp(term->val.str, "no") && parse_branch_str(term->val.str, &attr->branch_sample_type)) { - parse_events__handle_error(err, term->err_val, + parse_events_error__handle(err, term->err_val, strdup("invalid branch sample type"), NULL); return -EINVAL; @@ -1144,7 +1132,7 @@ do { \ case PARSE_EVENTS__TERM_TYPE_TIME: CHECK_TYPE_VAL(NUM); if (term->val.num > 1) { - parse_events__handle_error(err, term->err_val, + parse_events_error__handle(err, term->err_val, strdup("expected 0 or 1"), NULL); return -EINVAL; @@ -1171,6 +1159,9 @@ do { \ case PARSE_EVENTS__TERM_TYPE_NAME: CHECK_TYPE_VAL(STR); break; + case PARSE_EVENTS__TERM_TYPE_METRIC_ID: + CHECK_TYPE_VAL(STR); + break; case PARSE_EVENTS__TERM_TYPE_MAX_STACK: CHECK_TYPE_VAL(NUM); break; @@ -1180,7 +1171,7 @@ do { \ case PARSE_EVENTS__TERM_TYPE_PERCORE: CHECK_TYPE_VAL(NUM); if ((unsigned int)term->val.num > 1) { - parse_events__handle_error(err, term->err_val, + parse_events_error__handle(err, term->err_val, strdup("expected 0 or 1"), NULL); return -EINVAL; @@ -1192,14 +1183,14 @@ do { \ case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE: CHECK_TYPE_VAL(NUM); if (term->val.num > UINT_MAX) { - parse_events__handle_error(err, term->err_val, + parse_events_error__handle(err, term->err_val, strdup("too big"), NULL); return -EINVAL; } break; default: - parse_events__handle_error(err, term->err_term, + parse_events_error__handle(err, term->err_term, strdup("unknown term"), parse_events_formats_error_string(NULL)); return -EINVAL; @@ -1253,7 +1244,7 @@ static int config_term_tracepoint(struct perf_event_attr *attr, return config_term_common(attr, term, err); default: if (err) { - parse_events__handle_error(err, term->err_term, + parse_events_error__handle(err, term->err_term, strdup("unknown term"), strdup("valid terms: call-graph,stack-size\n")); } @@ -1440,6 +1431,7 @@ int parse_events_add_numeric(struct parse_events_state *parse_state, { struct perf_event_attr attr; LIST_HEAD(config_terms); + const char *name, *metric_id; bool hybrid; int ret; @@ -1456,14 +1448,16 @@ int parse_events_add_numeric(struct parse_events_state *parse_state, return -ENOMEM; } + name = get_config_name(head_config); + metric_id = get_config_metric_id(head_config); ret = parse_events__add_numeric_hybrid(parse_state, list, &attr, - get_config_name(head_config), + name, metric_id, &config_terms, &hybrid); if (hybrid) goto out_free_terms; - ret = add_event(list, &parse_state->idx, &attr, - get_config_name(head_config), &config_terms); + ret = add_event(list, &parse_state->idx, &attr, name, metric_id, + &config_terms); out_free_terms: free_config_terms(&config_terms); return ret; @@ -1471,7 +1465,7 @@ int parse_events_add_numeric(struct parse_events_state *parse_state, int parse_events_add_tool(struct parse_events_state *parse_state, struct list_head *list, - enum perf_tool_event tool_event) + int tool_event) { return add_event_tool(list, &parse_state->idx, tool_event); } @@ -1549,7 +1543,7 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, if (asprintf(&err_str, "Cannot find PMU `%s'. Missing kernel support?", name) >= 0) - parse_events__handle_error(err, 0, err_str, NULL); + parse_events_error__handle(err, 0, err_str, NULL); return -EINVAL; } @@ -1564,8 +1558,11 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, if (!head_config) { attr.type = pmu->type; - evsel = __add_event(list, &parse_state->idx, &attr, true, NULL, - pmu, NULL, auto_merge_stats, NULL); + evsel = __add_event(list, &parse_state->idx, &attr, + /*init_attr=*/true, /*name=*/NULL, + /*metric_id=*/NULL, pmu, + /*config_terms=*/NULL, auto_merge_stats, + /*cpu_list=*/NULL); if (evsel) { evsel->pmu_name = name ? strdup(name) : NULL; evsel->use_uncore_alias = use_uncore_alias; @@ -1618,9 +1615,10 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, return -EINVAL; } - evsel = __add_event(list, &parse_state->idx, &attr, true, - get_config_name(head_config), pmu, - &config_terms, auto_merge_stats, NULL); + evsel = __add_event(list, &parse_state->idx, &attr, /*init_attr=*/true, + get_config_name(head_config), + get_config_metric_id(head_config), pmu, + &config_terms, auto_merge_stats, /*cpu_list=*/NULL); if (!evsel) return -ENOMEM; @@ -1634,7 +1632,8 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, if (parse_state->fake_pmu) return 0; - evsel->unit = info.unit; + free((char *)evsel->unit); + evsel->unit = strdup(info.unit); evsel->scale = info.scale; evsel->per_pkg = info.per_pkg; evsel->snapshot = info.snapshot; @@ -1644,62 +1643,79 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, } int parse_events_multi_pmu_add(struct parse_events_state *parse_state, - char *str, struct list_head **listp) + char *str, struct list_head *head, + struct list_head **listp) { struct parse_events_term *term; - struct list_head *list; + struct list_head *list = NULL; + struct list_head *orig_head = NULL; struct perf_pmu *pmu = NULL; int ok = 0; + char *config; *listp = NULL; + + if (!head) { + head = malloc(sizeof(struct list_head)); + if (!head) + goto out_err; + + INIT_LIST_HEAD(head); + } + config = strdup(str); + if (!config) + goto out_err; + + if (parse_events_term__num(&term, + PARSE_EVENTS__TERM_TYPE_USER, + config, 1, false, &config, + NULL) < 0) { + free(config); + goto out_err; + } + list_add_tail(&term->list, head); + /* Add it for all PMUs that support the alias */ list = malloc(sizeof(struct list_head)); if (!list) - return -1; + goto out_err; + INIT_LIST_HEAD(list); + while ((pmu = perf_pmu__scan(pmu)) != NULL) { struct perf_pmu_alias *alias; list_for_each_entry(alias, &pmu->aliases, list) { if (!strcasecmp(alias->name, str)) { - struct list_head *head; - char *config; - - head = malloc(sizeof(struct list_head)); - if (!head) - return -1; - INIT_LIST_HEAD(head); - config = strdup(str); - if (!config) - return -1; - if (parse_events_term__num(&term, - PARSE_EVENTS__TERM_TYPE_USER, - config, 1, false, &config, - NULL) < 0) { - free(list); - free(config); - return -1; - } - list_add_tail(&term->list, head); - + parse_events_copy_term_list(head, &orig_head); if (!parse_events_add_pmu(parse_state, list, - pmu->name, head, + pmu->name, orig_head, true, true)) { pr_debug("%s -> %s/%s/\n", str, pmu->name, alias->str); ok++; } - - parse_events_terms__delete(head); + parse_events_terms__delete(orig_head); } } } - if (!ok) { - free(list); - return -1; + + if (parse_state->fake_pmu) { + if (!parse_events_add_pmu(parse_state, list, str, head, + true, true)) { + pr_debug("%s -> %s/%s/\n", str, "fake_pmu", str); + ok++; + } } - *listp = list; - return 0; + +out_err: + if (ok) + *listp = list; + else + free(list); + + parse_events_terms__delete(head); + return ok ? 0 : -1; } int parse_events__modifier_group(struct list_head *list, @@ -1819,6 +1835,11 @@ parse_events__set_leader_for_uncore_aliase(char *name, struct list_head *list, return ret; } +__weak struct evsel *arch_evlist__leader(struct list_head *list) +{ + return list_first_entry(list, struct evsel, core.node); +} + void parse_events__set_leader(char *name, struct list_head *list, struct parse_events_state *parse_state) { @@ -1832,9 +1853,10 @@ void parse_events__set_leader(char *name, struct list_head *list, if (parse_events__set_leader_for_uncore_aliase(name, list, parse_state)) return; - __perf_evlist__set_leader(list); - leader = list_entry(list->next, struct evsel, core.node); + leader = arch_evlist__leader(list); + __perf_evlist__set_leader(list, &leader->core); leader->group_name = name ? strdup(name) : NULL; + list_move(&leader->core.node, list); } /* list_event is assumed to point to malloc'ed memory */ @@ -2029,7 +2051,7 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add) return 0; } -int parse_events_name(struct list_head *list, char *name) +int parse_events_name(struct list_head *list, const char *name) { struct evsel *evsel; @@ -2087,8 +2109,17 @@ static void perf_pmu__parse_init(void) pmu = NULL; while ((pmu = perf_pmu__scan(pmu)) != NULL) { list_for_each_entry(alias, &pmu->aliases, list) { - if (strchr(alias->name, '-')) + char *tmp = strchr(alias->name, '-'); + + if (tmp) { + char *tmp2 = NULL; + + tmp2 = strchr(tmp + 1, '-'); len++; + if (tmp2) + len++; + } + len++; } } @@ -2108,8 +2139,20 @@ static void perf_pmu__parse_init(void) list_for_each_entry(alias, &pmu->aliases, list) { struct perf_pmu_event_symbol *p = perf_pmu_events_list + len; char *tmp = strchr(alias->name, '-'); + char *tmp2 = NULL; - if (tmp != NULL) { + if (tmp) + tmp2 = strchr(tmp + 1, '-'); + if (tmp2) { + SET_SYMBOL(strndup(alias->name, tmp - alias->name), + PMU_EVENT_SYMBOL_PREFIX); + p++; + tmp++; + SET_SYMBOL(strndup(tmp, tmp2 - tmp), PMU_EVENT_SYMBOL_SUFFIX); + p++; + SET_SYMBOL(strdup(++tmp2), PMU_EVENT_SYMBOL_SUFFIX2); + len += 3; + } else if (tmp) { SET_SYMBOL(strndup(alias->name, tmp - alias->name), PMU_EVENT_SYMBOL_PREFIX); p++; @@ -2136,23 +2179,38 @@ static void perf_pmu__parse_init(void) */ int perf_pmu__test_parse_init(void) { - struct perf_pmu_event_symbol *list; + struct perf_pmu_event_symbol *list, *tmp, symbols[] = { + {(char *)"read", PMU_EVENT_SYMBOL}, + {(char *)"event", PMU_EVENT_SYMBOL_PREFIX}, + {(char *)"two", PMU_EVENT_SYMBOL_SUFFIX}, + {(char *)"hyphen", PMU_EVENT_SYMBOL_SUFFIX}, + {(char *)"hyph", PMU_EVENT_SYMBOL_SUFFIX2}, + }; + unsigned long i, j; - list = malloc(sizeof(*list) * 1); + tmp = list = malloc(sizeof(*list) * ARRAY_SIZE(symbols)); if (!list) return -ENOMEM; - list->type = PMU_EVENT_SYMBOL; - list->symbol = strdup("read"); - - if (!list->symbol) { - free(list); - return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(symbols); i++, tmp++) { + tmp->type = symbols[i].type; + tmp->symbol = strdup(symbols[i].symbol); + if (!tmp->symbol) + goto err_free; } perf_pmu_events_list = list; - perf_pmu_events_list_num = 1; + perf_pmu_events_list_num = ARRAY_SIZE(symbols); + + qsort(perf_pmu_events_list, ARRAY_SIZE(symbols), + sizeof(struct perf_pmu_event_symbol), comp_pmu); return 0; + +err_free: + for (j = 0, tmp = list; j < i; j++, tmp++) + free(tmp->symbol); + free(list); + return -ENOMEM; } enum perf_pmu_event_symbol_type @@ -2299,6 +2357,52 @@ int __parse_events(struct evlist *evlist, const char *str, return ret; } +void parse_events_error__init(struct parse_events_error *err) +{ + bzero(err, sizeof(*err)); +} + +void parse_events_error__exit(struct parse_events_error *err) +{ + zfree(&err->str); + zfree(&err->help); + zfree(&err->first_str); + zfree(&err->first_help); +} + +void parse_events_error__handle(struct parse_events_error *err, int idx, + char *str, char *help) +{ + if (WARN(!str, "WARNING: failed to provide error string\n")) { + free(help); + return; + } + switch (err->num_errors) { + case 0: + err->idx = idx; + err->str = str; + err->help = help; + break; + case 1: + err->first_idx = err->idx; + err->idx = idx; + err->first_str = err->str; + err->str = str; + err->first_help = err->help; + err->help = help; + break; + default: + pr_debug("Multiple errors dropping message: %s (%s)\n", + err->str, err->help); + free(err->str); + err->str = str; + free(err->help); + err->help = help; + break; + } + err->num_errors++; +} + #define MAX_WIDTH 1000 static int get_term_width(void) { @@ -2308,8 +2412,8 @@ static int get_term_width(void) return ws.ws_col > MAX_WIDTH ? MAX_WIDTH : ws.ws_col; } -static void __parse_events_print_error(int err_idx, const char *err_str, - const char *err_help, const char *event) +static void __parse_events_error__print(int err_idx, const char *err_str, + const char *err_help, const char *event) { const char *str = "invalid or unsupported event: "; char _buf[MAX_WIDTH]; @@ -2363,22 +2467,18 @@ static void __parse_events_print_error(int err_idx, const char *err_str, } } -void parse_events_print_error(struct parse_events_error *err, - const char *event) +void parse_events_error__print(struct parse_events_error *err, + const char *event) { if (!err->num_errors) return; - __parse_events_print_error(err->idx, err->str, err->help, event); - zfree(&err->str); - zfree(&err->help); + __parse_events_error__print(err->idx, err->str, err->help, event); if (err->num_errors > 1) { fputs("\nInitial error:\n", stderr); - __parse_events_print_error(err->first_idx, err->first_str, + __parse_events_error__print(err->first_idx, err->first_str, err->first_help, event); - zfree(&err->first_str); - zfree(&err->first_help); } } @@ -2391,13 +2491,14 @@ int parse_events_option(const struct option *opt, const char *str, struct parse_events_error err; int ret; - bzero(&err, sizeof(err)); + parse_events_error__init(&err); ret = parse_events(evlist, str, &err); if (ret) { - parse_events_print_error(&err, str); + parse_events_error__print(&err, str); fprintf(stderr, "Run 'perf list' for a list of valid events\n"); } + parse_events_error__exit(&err); return ret; } @@ -2703,7 +2804,7 @@ int is_valid_tracepoint(const char *event_string) return 0; } -static bool is_event_supported(u8 type, unsigned config) +static bool is_event_supported(u8 type, u64 config) { bool ret = true; int open_return; @@ -2823,10 +2924,18 @@ void print_sdt_events(const char *subsys_glob, const char *event_glob, int print_hwcache_events(const char *event_glob, bool name_only) { - unsigned int type, op, i, evt_i = 0, evt_num = 0; - char name[64]; - char **evt_list = NULL; + unsigned int type, op, i, evt_i = 0, evt_num = 0, npmus = 0; + char name[64], new_name[128]; + char **evt_list = NULL, **evt_pmus = NULL; bool evt_num_known = false; + struct perf_pmu *pmu = NULL; + + if (perf_pmu__has_hybrid()) { + npmus = perf_pmu__hybrid_pmu_num(); + evt_pmus = zalloc(sizeof(char *) * npmus); + if (!evt_pmus) + goto out_enomem; + } restart: if (evt_num_known) { @@ -2842,20 +2951,61 @@ int print_hwcache_events(const char *event_glob, bool name_only) continue; for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { + unsigned int hybrid_supported = 0, j; + bool supported; + __evsel__hw_cache_type_op_res_name(type, op, i, name, sizeof(name)); if (event_glob != NULL && !strglobmatch(name, event_glob)) continue; - if (!is_event_supported(PERF_TYPE_HW_CACHE, - type | (op << 8) | (i << 16))) - continue; + if (!perf_pmu__has_hybrid()) { + if (!is_event_supported(PERF_TYPE_HW_CACHE, + type | (op << 8) | (i << 16))) { + continue; + } + } else { + perf_pmu__for_each_hybrid_pmu(pmu) { + if (!evt_num_known) { + evt_num++; + continue; + } + + supported = is_event_supported( + PERF_TYPE_HW_CACHE, + type | (op << 8) | (i << 16) | + ((__u64)pmu->type << PERF_PMU_TYPE_SHIFT)); + if (supported) { + snprintf(new_name, sizeof(new_name), "%s/%s/", + pmu->name, name); + evt_pmus[hybrid_supported] = strdup(new_name); + hybrid_supported++; + } + } + + if (hybrid_supported == 0) + continue; + } if (!evt_num_known) { evt_num++; continue; } - evt_list[evt_i] = strdup(name); + if ((hybrid_supported == 0) || + (hybrid_supported == npmus)) { + evt_list[evt_i] = strdup(name); + if (npmus > 0) { + for (j = 0; j < npmus; j++) + zfree(&evt_pmus[j]); + } + } else { + for (j = 0; j < hybrid_supported; j++) { + evt_list[evt_i++] = evt_pmus[j]; + evt_pmus[j] = NULL; + } + continue; + } + if (evt_list[evt_i] == NULL) goto out_enomem; evt_i++; @@ -2867,6 +3017,13 @@ int print_hwcache_events(const char *event_glob, bool name_only) evt_num_known = true; goto restart; } + + for (evt_i = 0; evt_i < evt_num; evt_i++) { + if (!evt_list[evt_i]) + break; + } + + evt_num = evt_i; qsort(evt_list, evt_num, sizeof(char *), cmp_string); evt_i = 0; while (evt_i < evt_num) { @@ -2885,6 +3042,10 @@ int print_hwcache_events(const char *event_glob, bool name_only) for (evt_i = 0; evt_i < evt_num; evt_i++) zfree(&evt_list[evt_i]); zfree(&evt_list); + + for (evt_i = 0; evt_i < npmus; evt_i++) + zfree(&evt_pmus[evt_i]); + zfree(&evt_pmus); return evt_num; out_enomem: @@ -2994,7 +3155,8 @@ void print_symbol_events(const char *event_glob, unsigned type, * Print the help text for the event symbols: */ void print_events(const char *event_glob, bool name_only, bool quiet_flag, - bool long_desc, bool details_flag, bool deprecated) + bool long_desc, bool details_flag, bool deprecated, + const char *pmu_name) { print_symbol_events(event_glob, PERF_TYPE_HARDWARE, event_symbols_hw, PERF_COUNT_HW_MAX, name_only); @@ -3006,7 +3168,7 @@ void print_events(const char *event_glob, bool name_only, bool quiet_flag, print_hwcache_events(event_glob, name_only); print_pmu_events(event_glob, name_only, quiet_flag, long_desc, - details_flag, deprecated); + details_flag, deprecated, pmu_name); if (event_glob != NULL) return; @@ -3032,7 +3194,8 @@ void print_events(const char *event_glob, bool name_only, bool quiet_flag, print_sdt_events(NULL, NULL, name_only); - metricgroup__print(true, true, NULL, name_only, details_flag); + metricgroup__print(true, true, NULL, name_only, details_flag, + pmu_name); print_libpfm_events(name_only, long_desc); } @@ -3083,7 +3246,7 @@ int parse_events_term__num(struct parse_events_term **term, struct parse_events_term temp = { .type_val = PARSE_EVENTS__TERM_TYPE_NUM, .type_term = type_term, - .config = config, + .config = config ? : strdup(config_term_names[type_term]), .no_value = no_value, .err_term = loc_term ? loc_term->first_column : 0, .err_val = loc_val ? loc_val->first_column : 0, @@ -3227,7 +3390,7 @@ void parse_events_evlist_error(struct parse_events_state *parse_state, if (!parse_state->error) return; - parse_events__handle_error(parse_state->error, idx, strdup(str), NULL); + parse_events_error__handle(parse_state->error, idx, strdup(str), NULL); } static void config_terms_list(char *buf, size_t buf_sz) @@ -3286,9 +3449,12 @@ char *parse_events_formats_error_string(char *additional_terms) struct evsel *parse_events__add_event_hybrid(struct list_head *list, int *idx, struct perf_event_attr *attr, - char *name, struct perf_pmu *pmu, + const char *name, + const char *metric_id, + struct perf_pmu *pmu, struct list_head *config_terms) { - return __add_event(list, idx, attr, true, name, pmu, - config_terms, false, NULL); + return __add_event(list, idx, attr, /*init_attr=*/true, name, metric_id, + pmu, config_terms, /*auto_merge_stats=*/false, + /*cpu_list=*/NULL); } diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index bf6e41aa9b..a38b8b160e 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -53,6 +53,7 @@ enum perf_pmu_event_symbol_type { PMU_EVENT_SYMBOL, /* normal style PMU event */ PMU_EVENT_SYMBOL_PREFIX, /* prefix of pre-suf style event */ PMU_EVENT_SYMBOL_SUFFIX, /* suffix of pre-suf style event */ + PMU_EVENT_SYMBOL_SUFFIX2, /* suffix of pre-suf2 style event */ }; struct perf_pmu_event_symbol { @@ -87,6 +88,7 @@ enum { PARSE_EVENTS__TERM_TYPE_PERCORE, PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT, PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE, + PARSE_EVENTS__TERM_TYPE_METRIC_ID, __PARSE_EVENTS__TERM_TYPE_NR, }; @@ -141,8 +143,6 @@ struct parse_events_state { char *hybrid_pmu_name; }; -void parse_events__handle_error(struct parse_events_error *err, int idx, - char *str, char *help); void parse_events__shrink_config_terms(void); int parse_events__is_hardcoded_term(struct parse_events_term *term); int parse_events_term__num(struct parse_events_term **term, @@ -162,7 +162,7 @@ void parse_events_terms__purge(struct list_head *terms); void parse_events__clear_array(struct parse_events_array *a); int parse_events__modifier_event(struct list_head *list, char *str, bool add); int parse_events__modifier_group(struct list_head *list, char *event_mod); -int parse_events_name(struct list_head *list, char *name); +int parse_events_name(struct list_head *list, const char *name); int parse_events_add_tracepoint(struct list_head *list, int *idx, const char *sys, const char *event, struct parse_events_error *error, @@ -182,10 +182,9 @@ int parse_events_add_numeric(struct parse_events_state *parse_state, struct list_head *list, u32 type, u64 config, struct list_head *head_config); -enum perf_tool_event; int parse_events_add_tool(struct parse_events_state *parse_state, struct list_head *list, - enum perf_tool_event tool_event); + int tool_event); int parse_events_add_cache(struct list_head *list, int *idx, char *type, char *op_result1, char *op_result2, struct parse_events_error *error, @@ -200,10 +199,12 @@ int parse_events_add_pmu(struct parse_events_state *parse_state, bool use_alias); struct evsel *parse_events__add_event(int idx, struct perf_event_attr *attr, - char *name, struct perf_pmu *pmu); + const char *name, const char *metric_id, + struct perf_pmu *pmu); int parse_events_multi_pmu_add(struct parse_events_state *parse_state, char *str, + struct list_head *head_config, struct list_head **listp); int parse_events_copy_term_list(struct list_head *old, @@ -219,7 +220,8 @@ void parse_events_evlist_error(struct parse_events_state *parse_state, int idx, const char *str); void print_events(const char *event_glob, bool name_only, bool quiet, - bool long_desc, bool details_flag, bool deprecated); + bool long_desc, bool details_flag, bool deprecated, + const char *pmu_name); struct event_symbol { const char *symbol; @@ -241,8 +243,12 @@ int is_valid_tracepoint(const char *event_string); int valid_event_mount(const char *eventfs); char *parse_events_formats_error_string(char *additional_terms); -void parse_events_print_error(struct parse_events_error *err, - const char *event); +void parse_events_error__init(struct parse_events_error *err); +void parse_events_error__exit(struct parse_events_error *err); +void parse_events_error__handle(struct parse_events_error *err, int idx, + char *str, char *help); +void parse_events_error__print(struct parse_events_error *err, + const char *event); #ifdef HAVE_LIBELF_SUPPORT /* @@ -267,7 +273,9 @@ int perf_pmu__test_parse_init(void); struct evsel *parse_events__add_event_hybrid(struct list_head *list, int *idx, struct perf_event_attr *attr, - char *name, struct perf_pmu *pmu, + const char *name, + const char *metric_id, + struct perf_pmu *pmu, struct list_head *config_terms); #endif /* __PERF_PARSE_EVENTS_H */ diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 923849024b..5b6e4b5249 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -12,7 +12,6 @@ #include #include #include -#include "../perf.h" #include "parse-events.h" #include "parse-events-bison.h" #include "evsel.h" @@ -139,18 +138,25 @@ static int pmu_str_check(yyscan_t scanner, struct parse_events_state *parse_stat yylval->str = strdup(text); - if (parse_state->fake_pmu) - return PE_PMU_EVENT_FAKE; - + /* + * If we're not testing then parse check determines the PMU event type + * which if it isn't a PMU returns PE_NAME. When testing the result of + * parse check can't be trusted so we return PE_PMU_EVENT_FAKE unless + * an '!' is present in which case the text can't be a PMU name. + */ switch (perf_pmu__parse_check(text)) { case PMU_EVENT_SYMBOL_PREFIX: return PE_PMU_EVENT_PRE; case PMU_EVENT_SYMBOL_SUFFIX: return PE_PMU_EVENT_SUF; + case PMU_EVENT_SYMBOL_SUFFIX2: + return PE_PMU_EVENT_SUF2; case PMU_EVENT_SYMBOL: - return PE_KERNEL_PMU_EVENT; + return parse_state->fake_pmu + ? PE_PMU_EVENT_FAKE : PE_KERNEL_PMU_EVENT; default: - return PE_NAME; + return parse_state->fake_pmu && !strchr(text,'!') + ? PE_PMU_EVENT_FAKE : PE_NAME; } } @@ -205,7 +211,7 @@ bpf_source [^,{}]+\.c[a-zA-Z0-9._]* num_dec [0-9]+ num_hex 0x[a-fA-F0-9]+ num_raw_hex [a-fA-F0-9]+ -name [a-zA-Z_*?\[\]][a-zA-Z0-9_*?.\[\]]* +name [a-zA-Z_*?\[\]][a-zA-Z0-9_*?.\[\]!]* name_tag [\'][a-zA-Z_*?\[\]][a-zA-Z0-9_*?\-,\.\[\]:=]*[\'] name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.:]* drv_cfg_term [a-zA-Z0-9_\.]+(=[a-zA-Z0-9_*?\.:]+)? @@ -295,6 +301,7 @@ no-overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOOVERWRITE); } percore { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_PERCORE); } aux-output { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT); } aux-sample-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE); } +metric-id { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_METRIC_ID); } r{num_raw_hex} { return raw(yyscanner); } r0x{num_raw_hex} { return raw(yyscanner); } , { return ','; } diff --git a/tools/perf/util/perf_event_attr_fprintf.c b/tools/perf/util/perf_event_attr_fprintf.c index 47b7531f51..98af3fa4ea 100644 --- a/tools/perf/util/perf_event_attr_fprintf.c +++ b/tools/perf/util/perf_event_attr_fprintf.c @@ -52,7 +52,7 @@ static void __p_branch_sample_type(char *buf, size_t size, u64 value) bit_name(ABORT_TX), bit_name(IN_TX), bit_name(NO_TX), bit_name(COND), bit_name(CALL_STACK), bit_name(IND_JUMP), bit_name(CALL), bit_name(NO_FLAGS), bit_name(NO_CYCLES), - bit_name(HW_INDEX), + bit_name(TYPE_SAVE), bit_name(HW_INDEX), { .name = NULL, } }; #undef bit_name diff --git a/tools/perf/util/pfm.c b/tools/perf/util/pfm.c index 756295dedc..f0bcfcab1a 100644 --- a/tools/perf/util/pfm.c +++ b/tools/perf/util/pfm.c @@ -87,7 +87,8 @@ int parse_libpfm_events_option(const struct option *opt, const char *str, pmu = perf_pmu__find_by_type((unsigned int)attr.type); evsel = parse_events__add_event(evlist->core.nr_entries, - &attr, q, pmu); + &attr, q, /*metric_id=*/NULL, + pmu); if (evsel == NULL) goto error; diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 394898b07f..541889fa9f 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -49,6 +49,10 @@ struct perf_pmu { struct list_head caps; /* HEAD struct perf_pmu_caps -> list */ struct list_head list; /* ELEM */ struct list_head hybrid_list; + + struct { + bool exclude_guest; + } missing_features; }; extern struct perf_pmu perf_pmu__fake; @@ -111,7 +115,7 @@ struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); bool is_pmu_core(const char *name); void print_pmu_events(const char *event_glob, bool name_only, bool quiet, bool long_desc, bool details_flag, - bool deprecated); + bool deprecated, const char *pmu_name); bool pmu_have_event(const char *pname, const char *name); int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, ...) __scanf(3, 4); @@ -120,21 +124,21 @@ int perf_pmu__test(void); struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu); void pmu_add_cpu_aliases_map(struct list_head *head, struct perf_pmu *pmu, - struct pmu_events_map *map); + const struct pmu_events_map *map); -struct pmu_events_map *perf_pmu__find_map(struct perf_pmu *pmu); -struct pmu_events_map *pmu_events_map__find(void); +const struct pmu_events_map *perf_pmu__find_map(struct perf_pmu *pmu); +const struct pmu_events_map *pmu_events_map__find(void); bool pmu_uncore_alias_match(const char *pmu_name, const char *name); void perf_pmu_free_alias(struct perf_pmu_alias *alias); -typedef int (*pmu_sys_event_iter_fn)(struct pmu_event *pe, void *data); +typedef int (*pmu_sys_event_iter_fn)(const struct pmu_event *pe, void *data); void pmu_for_each_sys_event(pmu_sys_event_iter_fn fn, void *data); int perf_pmu__convert_scale(const char *scale, char **end, double *sval); int perf_pmu__caps_parse(struct perf_pmu *pmu); void perf_pmu__warn_invalid_config(struct perf_pmu *pmu, __u64 config, - char *name); + const char *name); bool perf_pmu__has_hybrid(void); int perf_pmu__match(char *pattern, char *name, char *tok); diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index d7c976671e..a685d20165 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -18,6 +18,7 @@ util/mmap.c util/namespaces.c ../lib/bitmap.c ../lib/find_bit.c +../lib/list_sort.c ../lib/hweight.c ../lib/string.c ../lib/vsprintf.c diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 8feef3a05a..52d8995cfd 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -69,6 +69,18 @@ void perf_stat__collect_metric_expr(struct evlist *evsel_list) { } +/* + * This one is needed not to drag the PMU bandwagon, jevents generated + * pmu_sys_event_tables, etc and evsel__find_pmu() is used so far just for + * doing per PMU perf_event_attr.exclude_guest handling, not really needed, so + * far, for the perf python binding known usecases, revisit if this become + * necessary. + */ +struct perf_pmu *evsel__find_pmu(struct evsel *evsel __maybe_unused) +{ + return NULL; +} + /* * Add this one here not to drag util/metricgroup.c */ @@ -416,6 +428,8 @@ tracepoint_field(struct pyrf_event *pe, struct tep_format_field *field) offset = val; len = offset >> 16; offset &= 0xffff; + if (field->flags & TEP_FIELD_IS_RELATIVE) + offset += field->offset + field->size; } if (field->flags & TEP_FIELD_IS_STRING && is_printable_array(data + offset, len)) { @@ -449,7 +463,7 @@ get_tracepoint_field(struct pyrf_event *pevent, PyObject *attr_name) struct tep_event *tp_format; tp_format = trace_event__tp_format_id(evsel->core.attr.config); - if (!tp_format) + if (IS_ERR_OR_NULL(tp_format)) return NULL; evsel->tp_format = tp_format; @@ -624,17 +638,17 @@ static Py_ssize_t pyrf_cpu_map__length(PyObject *obj) { struct pyrf_cpu_map *pcpus = (void *)obj; - return pcpus->cpus->nr; + return perf_cpu_map__nr(pcpus->cpus); } static PyObject *pyrf_cpu_map__item(PyObject *obj, Py_ssize_t i) { struct pyrf_cpu_map *pcpus = (void *)obj; - if (i >= pcpus->cpus->nr) + if (i >= perf_cpu_map__nr(pcpus->cpus)) return NULL; - return Py_BuildValue("i", pcpus->cpus->map[i]); + return Py_BuildValue("i", perf_cpu_map__cpu(pcpus->cpus, i).cpu); } static PySequenceMethods pyrf_cpu_map__sequence_methods = { @@ -1045,7 +1059,7 @@ static struct mmap *get_md(struct evlist *evlist, int cpu) for (i = 0; i < evlist->core.nr_mmaps; i++) { struct mmap *md = &evlist->mmap[i]; - if (md->core.cpu == cpu) + if (md->core.cpu.cpu == cpu) return md; } @@ -1431,7 +1445,7 @@ PyMODINIT_FUNC PyInit_perf(void) * Dummy, to avoid dragging all the test_attr infrastructure in the python * binding. */ -void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, +void test_attr__open(struct perf_event_attr *attr, pid_t pid, struct perf_cpu cpu, int fd, int group_fd, unsigned long flags) { } diff --git a/tools/perf/util/record.h b/tools/perf/util/record.h index 68f471d9a8..ef6c2715fd 100644 --- a/tools/perf/util/record.h +++ b/tools/perf/util/record.h @@ -77,6 +77,7 @@ struct record_opts { int ctl_fd; int ctl_fd_ack; bool ctl_fd_close; + int synth; }; extern const char * const *record_usage; diff --git a/tools/perf/util/s390-cpumsf.c b/tools/perf/util/s390-cpumsf.c index 8130b56aa0..f3fdad28a8 100644 --- a/tools/perf/util/s390-cpumsf.c +++ b/tools/perf/util/s390-cpumsf.c @@ -244,7 +244,7 @@ static bool s390_cpumsf_basic_show(const char *color, size_t pos, struct hws_basic_entry *basicp) { struct hws_basic_entry *basic = basicp; -#if __BYTE_ORDER == __LITTLE_ENDIAN +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ struct hws_basic_entry local; unsigned long long word = be64toh(*(unsigned long long *)basicp); @@ -288,7 +288,7 @@ static bool s390_cpumsf_diag_show(const char *color, size_t pos, struct hws_diag_entry *diagp) { struct hws_diag_entry *diag = diagp; -#if __BYTE_ORDER == __LITTLE_ENDIAN +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ struct hws_diag_entry local; unsigned long long word = be64toh(*(unsigned long long *)diagp); @@ -322,7 +322,7 @@ static unsigned long long trailer_timestamp(struct hws_trailer_entry *te, static bool s390_cpumsf_trailer_show(const char *color, size_t pos, struct hws_trailer_entry *te) { -#if __BYTE_ORDER == __LITTLE_ENDIAN +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ struct hws_trailer_entry local; const unsigned long long flags = be64toh(te->flags); @@ -552,7 +552,7 @@ static unsigned long long get_trailer_time(const unsigned char *buf) te = (struct hws_trailer_entry *)(buf + S390_CPUMSF_PAGESZ - sizeof(*te)); -#if __BYTE_ORDER == __LITTLE_ENDIAN +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ clock_base = be64toh(te->progusage[0]) >> 63 & 0x1; progusage2 = be64toh(te->progusage[1]); #else diff --git a/tools/perf/util/s390-sample-raw.c b/tools/perf/util/s390-sample-raw.c index 08ec3c3ae0..cd3a348403 100644 --- a/tools/perf/util/s390-sample-raw.c +++ b/tools/perf/util/s390-sample-raw.c @@ -135,12 +135,12 @@ static int get_counterset_start(int setnr) * the name of this counter. * If no match is found a NULL pointer is returned. */ -static const char *get_counter_name(int set, int nr, struct pmu_events_map *map) +static const char *get_counter_name(int set, int nr, const struct pmu_events_map *map) { int rc, event_nr, wanted = get_counterset_start(set) + nr; if (map) { - struct pmu_event *evp = map->table; + const struct pmu_event *evp = map->table; for (; evp->name || evp->event || evp->desc; ++evp) { if (evp->name == NULL || evp->event == NULL) @@ -159,7 +159,7 @@ static void s390_cpumcfdg_dump(struct perf_sample *sample) unsigned char *buf = sample->raw_data; const char *color = PERF_COLOR_BLUE; struct cf_ctrset_entry *cep, ce; - struct pmu_events_map *map; + const struct pmu_events_map *map; u64 *p; map = pmu_events_map__find(); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 7b71455019..f994261888 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -235,7 +235,8 @@ enum sort_type { SORT_CODE_PAGE_SIZE, SORT_LOCAL_INS_LAT, SORT_GLOBAL_INS_LAT, - SORT_PIPELINE_STAGE_CYC, + SORT_LOCAL_PIPELINE_STAGE_CYC, + SORT_GLOBAL_PIPELINE_STAGE_CYC, /* branch stack specific sort keys */ __SORT_BRANCH_STACK, diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 5b7d6c16d3..af468e3bb6 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include #include #include +#include #include #include @@ -15,6 +17,7 @@ #include "srcline.h" #include "string2.h" #include "symbol.h" +#include "subcmd/run-command.h" bool srcline_full_filename; @@ -119,6 +122,8 @@ static struct symbol *new_inline_sym(struct dso *dso, return inline_sym; } +#define MAX_INLINE_NEST 1024 + #ifdef HAVE_LIBBFD_SUPPORT /* @@ -273,8 +278,6 @@ static void addr2line_cleanup(struct a2l_data *a2l) free(a2l); } -#define MAX_INLINE_NEST 1024 - static int inline_list__append_dso_a2l(struct dso *dso, struct inline_node *node, struct symbol *sym) @@ -361,26 +364,14 @@ void dso__free_a2l(struct dso *dso) dso->a2l = NULL; } -static struct inline_node *addr2inlines(const char *dso_name, u64 addr, - struct dso *dso, struct symbol *sym) -{ - struct inline_node *node; - - node = zalloc(sizeof(*node)); - if (node == NULL) { - perror("not enough memory for the inline node"); - return NULL; - } - - INIT_LIST_HEAD(&node->val); - node->addr = addr; - - addr2line(dso_name, addr, NULL, NULL, dso, true, node, sym); - return node; -} - #else /* HAVE_LIBBFD_SUPPORT */ +struct a2l_subprocess { + struct child_process addr2line; + FILE *to_child; + FILE *from_child; +}; + static int filename_split(char *filename, unsigned int *line_nr) { char *sep; @@ -402,114 +393,285 @@ static int filename_split(char *filename, unsigned int *line_nr) return 0; } -static int addr2line(const char *dso_name, u64 addr, - char **file, unsigned int *line_nr, - struct dso *dso __maybe_unused, - bool unwind_inlines __maybe_unused, - struct inline_node *node __maybe_unused, - struct symbol *sym __maybe_unused) +static void addr2line_subprocess_cleanup(struct a2l_subprocess *a2l) { - FILE *fp; - char cmd[PATH_MAX]; - char *filename = NULL; - size_t len; - int ret = 0; - - scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64, - dso_name, addr); - - fp = popen(cmd, "r"); - if (fp == NULL) { - pr_warning("popen failed for %s\n", dso_name); - return 0; + if (a2l->addr2line.pid != -1) { + kill(a2l->addr2line.pid, SIGKILL); + finish_command(&a2l->addr2line); /* ignore result, we don't care */ + a2l->addr2line.pid = -1; } - if (getline(&filename, &len, fp) < 0 || !len) { - pr_warning("addr2line has no output for %s\n", dso_name); + if (a2l->to_child != NULL) { + fclose(a2l->to_child); + a2l->to_child = NULL; + } + + if (a2l->from_child != NULL) { + fclose(a2l->from_child); + a2l->from_child = NULL; + } + + free(a2l); +} + +static struct a2l_subprocess *addr2line_subprocess_init(const char *path) +{ + const char *argv[] = { "addr2line", "-e", path, "-i", "-f", NULL }; + struct a2l_subprocess *a2l = zalloc(sizeof(*a2l)); + int start_command_status = 0; + + if (a2l == NULL) + goto out; + + a2l->to_child = NULL; + a2l->from_child = NULL; + + a2l->addr2line.pid = -1; + a2l->addr2line.in = -1; + a2l->addr2line.out = -1; + a2l->addr2line.no_stderr = 1; + + a2l->addr2line.argv = argv; + start_command_status = start_command(&a2l->addr2line); + a2l->addr2line.argv = NULL; /* it's not used after start_command; avoid dangling pointers */ + + if (start_command_status != 0) { + pr_warning("could not start addr2line for %s: start_command return code %d\n", + path, + start_command_status); goto out; } - ret = filename_split(filename, line_nr); - if (ret != 1) { - free(filename); + a2l->to_child = fdopen(a2l->addr2line.in, "w"); + if (a2l->to_child == NULL) { + pr_warning("could not open write-stream to addr2line of %s\n", path); goto out; } - *file = filename; + a2l->from_child = fdopen(a2l->addr2line.out, "r"); + if (a2l->from_child == NULL) { + pr_warning("could not open read-stream from addr2line of %s\n", path); + goto out; + } + + return a2l; out: - pclose(fp); + if (a2l) + addr2line_subprocess_cleanup(a2l); + + return NULL; +} + +static int read_addr2line_record(struct a2l_subprocess *a2l, + char **function, + char **filename, + unsigned int *line_nr) +{ + /* + * Returns: + * -1 ==> error + * 0 ==> sentinel (or other ill-formed) record read + * 1 ==> a genuine record read + */ + char *line = NULL; + size_t line_len = 0; + unsigned int dummy_line_nr = 0; + int ret = -1; + + if (function != NULL) + zfree(function); + + if (filename != NULL) + zfree(filename); + + if (line_nr != NULL) + *line_nr = 0; + + if (getline(&line, &line_len, a2l->from_child) < 0 || !line_len) + goto error; + + if (function != NULL) + *function = strdup(strim(line)); + + zfree(&line); + line_len = 0; + + if (getline(&line, &line_len, a2l->from_child) < 0 || !line_len) + goto error; + + if (filename_split(line, line_nr == NULL ? &dummy_line_nr : line_nr) == 0) { + ret = 0; + goto error; + } + + if (filename != NULL) + *filename = strdup(line); + + zfree(&line); + line_len = 0; + + return 1; + +error: + free(line); + if (function != NULL) + zfree(function); + if (filename != NULL) + zfree(filename); return ret; } -void dso__free_a2l(struct dso *dso __maybe_unused) +static int inline_list__append_record(struct dso *dso, + struct inline_node *node, + struct symbol *sym, + const char *function, + const char *filename, + unsigned int line_nr) { + struct symbol *inline_sym = new_inline_sym(dso, sym, function); + + return inline_list__append(inline_sym, srcline_from_fileline(filename, line_nr), node); } -static struct inline_node *addr2inlines(const char *dso_name, u64 addr, - struct dso *dso __maybe_unused, - struct symbol *sym) +static int addr2line(const char *dso_name, u64 addr, + char **file, unsigned int *line_nr, + struct dso *dso, + bool unwind_inlines, + struct inline_node *node, + struct symbol *sym __maybe_unused) { - FILE *fp; - char cmd[PATH_MAX]; - struct inline_node *node; - char *filename = NULL; - char *funcname = NULL; - size_t filelen, funclen; - unsigned int line_nr = 0; + struct a2l_subprocess *a2l = dso->a2l; + char *record_function = NULL; + char *record_filename = NULL; + unsigned int record_line_nr = 0; + int record_status = -1; + int ret = 0; + size_t inline_count = 0; - scnprintf(cmd, sizeof(cmd), "addr2line -e %s -i -f %016"PRIx64, - dso_name, addr); - - fp = popen(cmd, "r"); - if (fp == NULL) { - pr_err("popen failed for %s\n", dso_name); - return NULL; + if (!a2l) { + dso->a2l = addr2line_subprocess_init(dso_name); + a2l = dso->a2l; } + if (a2l == NULL) { + if (!symbol_conf.disable_add2line_warn) + pr_warning("%s %s: addr2line_subprocess_init failed\n", __func__, dso_name); + goto out; + } + + /* + * Send our request and then *deliberately* send something that can't be interpreted as + * a valid address to ask addr2line about (namely, ","). This causes addr2line to first + * write out the answer to our request, in an unbounded/unknown number of records, and + * then to write out the lines "??" and "??:0", so that we can detect when it has + * finished giving us anything useful. We have to be careful about the first record, + * though, because it may be genuinely unknown, in which case we'll get two sets of + * "??"/"??:0" lines. + */ + if (fprintf(a2l->to_child, "%016"PRIx64"\n,\n", addr) < 0 || fflush(a2l->to_child) != 0) { + pr_warning("%s %s: could not send request\n", __func__, dso_name); + goto out; + } + + switch (read_addr2line_record(a2l, &record_function, &record_filename, &record_line_nr)) { + case -1: + pr_warning("%s %s: could not read first record\n", __func__, dso_name); + goto out; + case 0: + /* + * The first record was invalid, so return failure, but first read another + * record, since we asked a junk question and have to clear the answer out. + */ + switch (read_addr2line_record(a2l, NULL, NULL, NULL)) { + case -1: + pr_warning("%s %s: could not read delimiter record\n", __func__, dso_name); + break; + case 0: + /* As expected. */ + break; + default: + pr_warning("%s %s: unexpected record instead of sentinel", + __func__, dso_name); + break; + } + goto out; + default: + break; + } + + if (file) { + *file = strdup(record_filename); + ret = 1; + } + if (line_nr) + *line_nr = record_line_nr; + + if (unwind_inlines) { + if (node && inline_list__append_record(dso, node, sym, + record_function, + record_filename, + record_line_nr)) { + ret = 0; + goto out; + } + } + + /* We have to read the records even if we don't care about the inline info. */ + while ((record_status = read_addr2line_record(a2l, + &record_function, + &record_filename, + &record_line_nr)) == 1) { + if (unwind_inlines && node && inline_count++ < MAX_INLINE_NEST) { + if (inline_list__append_record(dso, node, sym, + record_function, + record_filename, + record_line_nr)) { + ret = 0; + goto out; + } + ret = 1; /* found at least one inline frame */ + } + } + +out: + free(record_function); + free(record_filename); + return ret; +} + +void dso__free_a2l(struct dso *dso) +{ + struct a2l_subprocess *a2l = dso->a2l; + + if (!a2l) + return; + + addr2line_subprocess_cleanup(a2l); + + dso->a2l = NULL; +} + +#endif /* HAVE_LIBBFD_SUPPORT */ + +static struct inline_node *addr2inlines(const char *dso_name, u64 addr, + struct dso *dso, struct symbol *sym) +{ + struct inline_node *node; + node = zalloc(sizeof(*node)); if (node == NULL) { perror("not enough memory for the inline node"); - goto out; + return NULL; } INIT_LIST_HEAD(&node->val); node->addr = addr; - /* addr2line -f generates two lines for each inlined functions */ - while (getline(&funcname, &funclen, fp) != -1) { - char *srcline; - struct symbol *inline_sym; - - strim(funcname); - - if (getline(&filename, &filelen, fp) == -1) - goto out; - - if (filename_split(filename, &line_nr) != 1) - goto out; - - srcline = srcline_from_fileline(filename, line_nr); - inline_sym = new_inline_sym(dso, sym, funcname); - - if (inline_list__append(inline_sym, srcline, node) != 0) { - free(srcline); - if (inline_sym && inline_sym->inlined) - symbol__delete(inline_sym); - goto out; - } - } - -out: - pclose(fp); - free(filename); - free(funcname); - + addr2line(dso_name, addr, NULL, NULL, dso, true, node, sym); return node; } -#endif /* HAVE_LIBBFD_SUPPORT */ - /* * Number of addr2line failures (without success) before disabling it for that * dso. diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index 32c8527de3..335d19cc30 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -108,8 +108,7 @@ struct runtime_stat { struct rblist value_list; }; -typedef struct aggr_cpu_id (*aggr_get_id_t)(struct perf_stat_config *config, - struct perf_cpu_map *m, int cpu); +typedef struct aggr_cpu_id (*aggr_get_id_t)(struct perf_stat_config *config, struct perf_cpu cpu); struct perf_stat_config { enum aggr_mode aggr_mode; @@ -209,7 +208,7 @@ void perf_stat__init_shadow_stats(void); void perf_stat__reset_shadow_stats(void); void perf_stat__reset_shadow_per_stat(struct runtime_stat *st); void perf_stat__update_shadow_stats(struct evsel *counter, u64 count, - int cpu, struct runtime_stat *st); + int cpu_map_idx, struct runtime_stat *st); struct perf_stat_output_ctx { void *ctx; print_metric_t print_metric; @@ -249,10 +248,10 @@ size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp); int create_perf_stat_counter(struct evsel *evsel, struct perf_stat_config *config, struct target *target, - int cpu); + int cpu_map_idx); void evlist__print_counters(struct evlist *evlist, struct perf_stat_config *config, struct target *_target, struct timespec *ts, int argc, const char **argv); struct metric_expr; -double test_generic_metric(struct metric_expr *mexp, int cpu, struct runtime_stat *st); +double test_generic_metric(struct metric_expr *mexp, int cpu_map_idx, struct runtime_stat *st); #endif diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 954d6a049e..fbf866d82d 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -40,22 +40,33 @@ Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, GElf_Shdr *shp, const char *name, size_t *idx); #endif -/** struct symbol - symtab entry - * - * @ignore - resolvable but tools ignore it (e.g. idle routines) +/** + * A symtab entry. When allocated this may be preceded by an annotation (see + * symbol__annotation), a browser_index (see symbol__browser_index) and rb_node + * to sort by name (see struct symbol_name_rb_node). */ struct symbol { struct rb_node rb_node; + /** Range of symbol [start, end). */ u64 start; u64 end; + /** Length of the string name. */ u16 namelen; + /** ELF symbol type as defined for st_info. E.g STT_OBJECT or STT_FUNC. */ u8 type:4; + /** ELF binding type as defined for st_info. E.g. STB_WEAK or STB_GLOBAL. */ u8 binding:4; + /** Set true for kernel symbols of idle routines. */ u8 idle:1; + /** Resolvable but tools ignore it (e.g. idle routines). */ u8 ignore:1; + /** Symbol for an inlined function. */ u8 inlined:1; + /** Has symbol__annotate2 been performed. */ + u8 annotate2:1; + /** Architecture specific. Unused except on PPC where it holds st_other. */ u8 arch_sym; - bool annotate2; + /** The name of length namelen associated with the symbol. */ char name[]; }; @@ -286,4 +297,6 @@ static inline void __mem_info__zput(struct mem_info **mi) #define mem_info__zput(mi) __mem_info__zput(&mi) +int symbol__validate_sym_arguments(void); + #endif /* __PERF_SYMBOL */ diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index 27945eeb0c..c1ebfc5d2e 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -95,15 +95,15 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) if (target->cpu_list) ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", - top->evlist->core.cpus->nr > 1 ? "s" : "", + perf_cpu_map__nr(top->evlist->core.cpus) > 1 ? "s" : "", target->cpu_list); else { if (target->tid) ret += SNPRINTF(bf + ret, size - ret, ")"); else ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", - top->evlist->core.cpus->nr, - top->evlist->core.cpus->nr > 1 ? "s" : ""); + perf_cpu_map__nr(top->evlist->core.cpus), + perf_cpu_map__nr(top->evlist->core.cpus) > 1 ? "s" : ""); } perf_top__reset_sample_counters(top); diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index df3c4671be..fb4f6616b5 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -416,3 +416,18 @@ char *perf_exe(char *buf, int len) } return strcpy(buf, "perf"); } + +void perf_debuginfod_setup(struct perf_debuginfod *di) +{ + /* + * By default '!di->set' we clear DEBUGINFOD_URLS, so debuginfod + * processing is not triggered, otherwise we set it to 'di->urls' + * value. If 'di->urls' is "system" we keep DEBUGINFOD_URLS value. + */ + if (!di->set) + setenv("DEBUGINFOD_URLS", "", 1); + else if (di->urls && strcmp(di->urls, "system")) + setenv("DEBUGINFOD_URLS", di->urls, 1); + + pr_debug("DEBUGINFOD_URLS=%s\n", getenv("DEBUGINFOD_URLS")); +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 9f0d36ba77..7b625cbd2d 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -11,6 +11,9 @@ #include #include #include +#ifndef __cplusplus +#include +#endif /* General helper functions */ void usage(const char *err) __noreturn; @@ -66,6 +69,12 @@ extern bool test_attr__enabled; void test_attr__ready(void); void test_attr__init(void); struct perf_event_attr; -void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, +void test_attr__open(struct perf_event_attr *attr, pid_t pid, struct perf_cpu cpu, int fd, int group_fd, unsigned long flags); + +struct perf_debuginfod { + const char *urls; + bool set; +}; +void perf_debuginfod_setup(struct perf_debuginfod *di); #endif /* GIT_COMPAT_UTIL_H */ diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c index f62f10c988..b1ed76d9a9 100644 --- a/tools/vm/page-types.c +++ b/tools/vm/page-types.c @@ -390,7 +390,7 @@ static void show_page_range(unsigned long voffset, unsigned long offset, if (opt_pid) printf("%lx\t", voff); if (opt_file) - printf("%lu\t", voff); + printf("%lx\t", voff); if (opt_list_cgroup) printf("@%llu\t", (unsigned long long)cgroup0); if (opt_list_mapcnt) @@ -418,7 +418,7 @@ static void show_page(unsigned long voffset, unsigned long offset, if (opt_pid) printf("%lx\t", voffset); if (opt_file) - printf("%lu\t", voffset); + printf("%lx\t", voffset); if (opt_list_cgroup) printf("@%llu\t", (unsigned long long)cgroup); if (opt_list_mapcnt) @@ -967,22 +967,19 @@ static struct sigaction sigbus_action = { .sa_flags = SA_SIGINFO, }; -static void walk_file(const char *name, const struct stat *st) +static void walk_file_range(const char *name, int fd, + unsigned long off, unsigned long end) { uint8_t vec[PAGEMAP_BATCH]; uint64_t buf[PAGEMAP_BATCH], flags; uint64_t cgroup = 0; uint64_t mapcnt = 0; unsigned long nr_pages, pfn, i; - off_t off, end = st->st_size; - int fd; ssize_t len; void *ptr; int first = 1; - fd = checked_open(name, O_RDONLY|O_NOATIME|O_NOFOLLOW); - - for (off = 0; off < end; off += len) { + for (; off < end; off += len) { nr_pages = (end - off + page_size - 1) / page_size; if (nr_pages > PAGEMAP_BATCH) nr_pages = PAGEMAP_BATCH; @@ -1037,12 +1034,26 @@ static void walk_file(const char *name, const struct stat *st) if (first && opt_list) { first = 0; flush_page_range(); - show_file(name, st); } add_page(off / page_size + i, pfn, flags, cgroup, mapcnt, buf[i]); } } +} + +static void walk_file(const char *name, const struct stat *st) +{ + int i; + int fd; + + fd = checked_open(name, O_RDONLY|O_NOATIME|O_NOFOLLOW); + + if (!nr_addr_ranges) + add_addr_range(0, st->st_size / page_size); + + for (i = 0; i < nr_addr_ranges; i++) + walk_file_range(name, fd, opt_offset[i] * page_size, + (opt_offset[i] + opt_size[i]) * page_size); close(fd); } @@ -1062,10 +1073,10 @@ int walk_tree(const char *name, const struct stat *st, int type, struct FTW *f) return 0; } +struct stat st; + static void walk_page_cache(void) { - struct stat st; - kpageflags_fd = checked_open(opt_kpageflags, O_RDONLY); pagemap_fd = checked_open("/proc/self/pagemap", O_RDONLY); sigaction(SIGBUS, &sigbus_action, NULL); @@ -1362,6 +1373,11 @@ int main(int argc, char *argv[]) if (opt_list) printf("\n\n"); + if (opt_file) { + show_file(opt_file, &st); + printf("\n"); + } + show_summary(); if (opt_list_mapcnt) diff --git a/tools/vm/page_owner_sort.c b/tools/vm/page_owner_sort.c index 0e75f22c94..9ebb84a9c7 100644 --- a/tools/vm/page_owner_sort.c +++ b/tools/vm/page_owner_sort.c @@ -5,6 +5,8 @@ * Example use: * cat /sys/kernel/debug/page_owner > page_owner_full.txt * ./page_owner_sort page_owner_full.txt sorted_page_owner.txt + * Or sort by total memory: + * ./page_owner_sort -m page_owner_full.txt sorted_page_owner.txt * * See Documentation/vm/page_owner.rst */ @@ -16,14 +18,18 @@ #include #include #include +#include +#include struct block_list { char *txt; int len; int num; + int page_num; }; - +static int sort_by_memory; +static regex_t order_pattern; static struct block_list *list; static int list_size; static int max_size; @@ -59,12 +65,50 @@ static int compare_num(const void *p1, const void *p2) return l2->num - l1->num; } +static int compare_page_num(const void *p1, const void *p2) +{ + const struct block_list *l1 = p1, *l2 = p2; + + return l2->page_num - l1->page_num; +} + +static int get_page_num(char *buf) +{ + int err, val_len, order_val; + char order_str[4] = {0}; + char *endptr; + regmatch_t pmatch[2]; + + err = regexec(&order_pattern, buf, 2, pmatch, REG_NOTBOL); + if (err != 0 || pmatch[1].rm_so == -1) { + printf("no order pattern in %s\n", buf); + return 0; + } + val_len = pmatch[1].rm_eo - pmatch[1].rm_so; + if (val_len > 2) /* max_order should not exceed 2 digits */ + goto wrong_order; + + memcpy(order_str, buf + pmatch[1].rm_so, val_len); + + errno = 0; + order_val = strtol(order_str, &endptr, 10); + if (errno != 0 || endptr == order_str || *endptr != '\0') + goto wrong_order; + + return 1 << order_val; + +wrong_order: + printf("wrong order in follow buf:\n%s\n", buf); + return 0; +} + static void add_list(char *buf, int len) { if (list_size != 0 && len == list[list_size-1].len && memcmp(buf, list[list_size-1].txt, len) == 0) { list[list_size-1].num++; + list[list_size-1].page_num += get_page_num(buf); return; } if (list_size == max_size) { @@ -74,6 +118,7 @@ static void add_list(char *buf, int len) list[list_size].txt = malloc(len+1); list[list_size].len = len; list[list_size].num = 1; + list[list_size].page_num = get_page_num(buf); memcpy(list[list_size].txt, buf, len); list[list_size].txt[len] = 0; list_size++; @@ -85,6 +130,13 @@ static void add_list(char *buf, int len) #define BUF_SIZE (128 * 1024) +static void usage(void) +{ + printf("Usage: ./page_owner_sort [-m] \n" + "-m Sort by total memory. If this option is unset, sort by times\n" + ); +} + int main(int argc, char **argv) { FILE *fin, *fout; @@ -92,18 +144,36 @@ int main(int argc, char **argv) int ret, i, count; struct block_list *list2; struct stat st; + int err; + int opt; - if (argc < 3) { - printf("Usage: ./program \n"); + while ((opt = getopt(argc, argv, "m")) != -1) + switch (opt) { + case 'm': + sort_by_memory = 1; + break; + default: + usage(); + exit(1); + } + + if (optind >= (argc - 1)) { + usage(); + exit(1); + } + + fin = fopen(argv[optind], "r"); + fout = fopen(argv[optind + 1], "w"); + if (!fin || !fout) { + usage(); perror("open: "); exit(1); } - fin = fopen(argv[1], "r"); - fout = fopen(argv[2], "w"); - if (!fin || !fout) { - printf("Usage: ./program \n"); - perror("open: "); + err = regcomp(&order_pattern, "order\\s*([0-9]*),", REG_EXTENDED|REG_NEWLINE); + if (err != 0 || order_pattern.re_nsub != 1) { + printf("%s: Invalid pattern 'order\\s*([0-9]*),' code %d\n", + argv[0], err); exit(1); } @@ -145,13 +215,19 @@ int main(int argc, char **argv) list2[count++] = list[i]; } else { list2[count-1].num += list[i].num; + list2[count-1].page_num += list[i].page_num; } } - qsort(list2, count, sizeof(list[0]), compare_num); + if (sort_by_memory) + qsort(list2, count, sizeof(list[0]), compare_page_num); + else + qsort(list2, count, sizeof(list[0]), compare_num); for (i = 0; i < count; i++) - fprintf(fout, "%d times:\n%s\n", list2[i].num, list2[i].txt); + fprintf(fout, "%d times, %d pages:\n%s\n", + list2[i].num, list2[i].page_num, list2[i].txt); + regfree(&order_pattern); return 0; }