diff --git a/MAINTAINERS b/MAINTAINERS
index adbe6d9c5658..a9fe96417547 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21907,7 +21907,7 @@ F: tools/lib/perf/
F: tools/perf/
PERFORMANCE EVENTS TOOLING ARM64
-R: John Garry
+R: John Garry
R: Will Deacon
R: James Clark
R: Mike Leach
diff --git a/tools/arch/arm64/include/uapi/asm/kvm.h b/tools/arch/arm64/include/uapi/asm/kvm.h
index 9998252ea8ec..65ee09bd1463 100644
--- a/tools/arch/arm64/include/uapi/asm/kvm.h
+++ b/tools/arch/arm64/include/uapi/asm/kvm.h
@@ -111,6 +111,7 @@ struct kvm_regs {
#define KVM_ARM_VCPU_PTRAUTH_GENERIC 6 /* VCPU uses generic authentication */
#define KVM_ARM_VCPU_HAS_EL2 7 /* Support nested virtualization */
#define KVM_ARM_VCPU_HAS_EL2_E2H0 8 /* Limit NV support to E2H RES0 */
+#define KVM_ARM_VCPU_PMU_V3_STRICT 9 /* No default PMU creation */
struct kvm_vcpu_init {
__u32 target;
diff --git a/tools/arch/powerpc/include/uapi/asm/kvm.h b/tools/arch/powerpc/include/uapi/asm/kvm.h
index 077c5437f521..913a64b901a3 100644
--- a/tools/arch/powerpc/include/uapi/asm/kvm.h
+++ b/tools/arch/powerpc/include/uapi/asm/kvm.h
@@ -437,6 +437,24 @@ struct kvm_ppc_cpu_char {
__u64 behaviour_mask; /* valid bits in behaviour */
};
+/* For KVM_PPC_GET_COMPAT_CAPS */
+struct kvm_ppc_compat_caps {
+ __u64 size; /* Size of this structure */
+ __u64 flags; /* Reserved for future use */
+ __u64 compat_capabilities; /* Capabilities supported by the host */
+};
+#define KVM_PPC_COMPAT_CAPS_SIZE_VER0 24 /* sizeof first published struct */
+
+/*
+ * Capability bits for compat_capabilities field in kvm_ppc_compat_caps.
+ * These bits indicate which processor compatibility modes are supported.
+ */
+#define KVM_PPC_COMPAT_CAP_POWER9 (1ULL << 62)
+#define KVM_PPC_COMPAT_CAP_POWER10 (1ULL << 61)
+#define KVM_PPC_COMPAT_CAP_POWER11 (1ULL << 60)
+#define KVM_PPC_COMPAT_BITMASK (KVM_PPC_COMPAT_CAP_POWER9 | \
+ KVM_PPC_COMPAT_CAP_POWER10 | \
+ KVM_PPC_COMPAT_CAP_POWER11)
/*
* Values for character and character_mask.
* These are identical to the values used by H_GET_CPU_CHARACTERISTICS.
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h
index 6547c0aee45b..fe84cf4e97d3 100644
--- a/tools/arch/x86/include/asm/cpufeatures.h
+++ b/tools/arch/x86/include/asm/cpufeatures.h
@@ -136,8 +136,12 @@
#define X86_FEATURE_HYPERVISOR ( 4*32+31) /* "hypervisor" Running on a hypervisor */
/* VIA/Cyrix/Centaur-defined CPU features, CPUID level 0xC0000001, word 5 */
+#define X86_FEATURE_SM2 ( 5*32+ 0) /* "sm2" SM2 algorithm */
+#define X86_FEATURE_SM2_EN ( 5*32+ 1) /* "sm2_en" SM2 enabled */
#define X86_FEATURE_XSTORE ( 5*32+ 2) /* "rng" RNG present (xstore) */
#define X86_FEATURE_XSTORE_EN ( 5*32+ 3) /* "rng_en" RNG enabled */
+#define X86_FEATURE_CCS ( 5*32+ 4) /* "ccs" SM3 + SM4 instructions */
+#define X86_FEATURE_CCS_EN ( 5*32+ 5) /* "ccs_en" CCS enabled */
#define X86_FEATURE_XCRYPT ( 5*32+ 6) /* "ace" on-CPU crypto (xcrypt) */
#define X86_FEATURE_XCRYPT_EN ( 5*32+ 7) /* "ace_en" on-CPU crypto enabled */
#define X86_FEATURE_ACE2 ( 5*32+ 8) /* "ace2" Advanced Cryptography Engine v2 */
@@ -146,6 +150,12 @@
#define X86_FEATURE_PHE_EN ( 5*32+11) /* "phe_en" PHE enabled */
#define X86_FEATURE_PMM ( 5*32+12) /* "pmm" PadLock Montgomery Multiplier */
#define X86_FEATURE_PMM_EN ( 5*32+13) /* "pmm_en" PMM enabled */
+#define X86_FEATURE_RNG2 ( 5*32+22) /* "rng2" RNG v2 */
+#define X86_FEATURE_RNG2_EN ( 5*32+23) /* "rng2_en" RNG2 enabled */
+#define X86_FEATURE_PHE2 ( 5*32+25) /* "phe2" PadLock Hash Engine v2 */
+#define X86_FEATURE_PHE2_EN ( 5*32+26) /* "phe2_en" PHE2 enabled */
+#define X86_FEATURE_RSA ( 5*32+27) /* "rsa" Big-number arithmetic */
+#define X86_FEATURE_RSA_EN ( 5*32+28) /* "rsa_en" RSA enabled */
/* More extended AMD flags: CPUID level 0x80000001, ECX, word 6 */
#define X86_FEATURE_LAHF_LM ( 6*32+ 0) /* "lahf_lm" LAHF/SAHF in long mode */
@@ -365,6 +375,8 @@
#define X86_FEATURE_HWP_HIGHEST_PERF_CHANGE (14*32+15) /* HWP Highest perf change */
#define X86_FEATURE_HFI (14*32+19) /* "hfi" Hardware Feedback Interface */
+#define X86_FEATURE_DPTI (14*32+24) /* Intel Directed Package Thermal Interrupt */
+
/* AMD SVM Feature Identification, CPUID level 0x8000000a (EDX), word 15 */
#define X86_FEATURE_NPT (15*32+ 0) /* "npt" Nested Page Table support */
#define X86_FEATURE_LBRV (15*32+ 1) /* "lbrv" LBR Virtualization support */
@@ -465,6 +477,8 @@
#define X86_FEATURE_NO_NESTED_DATA_BP (20*32+ 0) /* No Nested Data Breakpoints */
#define X86_FEATURE_WRMSR_XX_BASE_NS (20*32+ 1) /* WRMSR to {FS,GS,KERNEL_GS}_BASE is non-serializing */
#define X86_FEATURE_LFENCE_RDTSC (20*32+ 2) /* LFENCE always serializing / synchronizes RDTSC */
+
+#define X86_FEATURE_TLBSYNC_SFW_NO (20*32+ 4) /* TLBI Erratum #1718 fixed in microcode */
#define X86_FEATURE_VERW_CLEAR (20*32+ 5) /* The memory form of VERW mitigates TSA */
#define X86_FEATURE_NULL_SEL_CLR_BASE (20*32+ 6) /* Null Selector Clears Base */
@@ -575,4 +589,5 @@
#define X86_BUG_TSA X86_BUG( 1*32+ 9) /* "tsa" CPU is affected by Transient Scheduler Attacks */
#define X86_BUG_VMSCAPE X86_BUG( 1*32+10) /* "vmscape" CPU is affected by VMSCAPE attacks from guests */
#define X86_BUG_SEAMRET_INVD_VMCS X86_BUG( 1*32+11) /* "seamret_invd_vmcs" SEAMRET from P-SEAMLDR clears the current VMCS */
+#define X86_BUG_TLBI X86_BUG( 1*32+12) /* "tlbi" AMD TLBI Erratum #1718 */
#endif /* _ASM_X86_CPUFEATURES_H */
diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h
index 18c4be75e927..9c26d4075c5f 100644
--- a/tools/arch/x86/include/asm/msr-index.h
+++ b/tools/arch/x86/include/asm/msr-index.h
@@ -350,6 +350,13 @@
#define ARCH_PEBS_LBR_SHIFT 40
#define ARCH_PEBS_LBR (0x3ull << ARCH_PEBS_LBR_SHIFT)
#define ARCH_PEBS_VECR_XMM BIT_ULL(49)
+#define ARCH_PEBS_VECR_YMMH BIT_ULL(50)
+#define ARCH_PEBS_VECR_EGPRS BIT_ULL(51)
+#define ARCH_PEBS_VECR_OPMASK BIT_ULL(53)
+#define ARCH_PEBS_VECR_ZMMH BIT_ULL(54)
+#define ARCH_PEBS_VECR_H16ZMM BIT_ULL(55)
+#define ARCH_PEBS_VECR_EXT_SHIFT 49
+#define ARCH_PEBS_VECR_EXT (0x7full << ARCH_PEBS_VECR_EXT_SHIFT)
#define ARCH_PEBS_GPR BIT_ULL(61)
#define ARCH_PEBS_AUX BIT_ULL(62)
#define ARCH_PEBS_EN BIT_ULL(63)
diff --git a/tools/arch/x86/include/uapi/asm/perf_regs.h b/tools/arch/x86/include/uapi/asm/perf_regs.h
index 7c9d2bb3833b..faaa82df688d 100644
--- a/tools/arch/x86/include/uapi/asm/perf_regs.h
+++ b/tools/arch/x86/include/uapi/asm/perf_regs.h
@@ -2,6 +2,8 @@
#ifndef _ASM_X86_PERF_REGS_H
#define _ASM_X86_PERF_REGS_H
+#include
+
enum perf_event_x86_regs {
PERF_REG_X86_AX,
PERF_REG_X86_BX,
@@ -27,9 +29,35 @@ enum perf_event_x86_regs {
PERF_REG_X86_R13,
PERF_REG_X86_R14,
PERF_REG_X86_R15,
+ /*
+ * The eGPRs/SSP and XMM have overlaps. Only one can be used
+ * at a time. The ABI PERF_SAMPLE_REGS_ABI_SIMD is used to
+ * distinguish which one is used. If PERF_SAMPLE_REGS_ABI_SIMD
+ * is set, then eGPRs/SSP is used, otherwise, XMM is used.
+ *
+ * Extended GPRs (eGPRs)
+ */
+ PERF_REG_X86_R16,
+ PERF_REG_X86_R17,
+ PERF_REG_X86_R18,
+ PERF_REG_X86_R19,
+ PERF_REG_X86_R20,
+ PERF_REG_X86_R21,
+ PERF_REG_X86_R22,
+ PERF_REG_X86_R23,
+ PERF_REG_X86_R24,
+ PERF_REG_X86_R25,
+ PERF_REG_X86_R26,
+ PERF_REG_X86_R27,
+ PERF_REG_X86_R28,
+ PERF_REG_X86_R29,
+ PERF_REG_X86_R30,
+ PERF_REG_X86_R31,
+ PERF_REG_X86_SSP,
/* These are the limits for the GPRs. */
PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1,
PERF_REG_X86_64_MAX = PERF_REG_X86_R15 + 1,
+ PERF_REG_MISC_MAX = PERF_REG_X86_SSP + 1,
/* These all need two bits set because they are 128bit */
PERF_REG_X86_XMM0 = 32,
@@ -54,5 +82,30 @@ enum perf_event_x86_regs {
};
#define PERF_REG_EXTENDED_MASK (~((1ULL << PERF_REG_X86_XMM0) - 1))
+#define PERF_X86_EGPRS_MASK __GENMASK_ULL(PERF_REG_X86_R31, PERF_REG_X86_R16)
+
+enum {
+ PERF_X86_SIMD_XMM_REGS = 16,
+ PERF_X86_SIMD_YMM_REGS = 16,
+ PERF_X86_SIMD_ZMM_REGS = 32,
+ PERF_X86_SIMD_VEC_REGS_MAX = PERF_X86_SIMD_ZMM_REGS,
+
+ PERF_X86_SIMD_OPMASK_REGS = 8,
+ PERF_X86_SIMD_PRED_REGS_MAX = PERF_X86_SIMD_OPMASK_REGS,
+};
+
+#define PERF_X86_SIMD_PRED_MASK __GENMASK(PERF_X86_SIMD_PRED_REGS_MAX - 1, 0)
+#define PERF_X86_SIMD_VEC_MASK __GENMASK_ULL(PERF_X86_SIMD_VEC_REGS_MAX - 1, 0)
+
+#define PERF_X86_H16ZMM_BASE 16
+
+enum {
+ /* 1 qword = 8 bytes */
+ PERF_X86_OPMASK_QWORDS = 1,
+ PERF_X86_XMM_QWORDS = 2,
+ PERF_X86_YMM_QWORDS = 4,
+ PERF_X86_ZMM_QWORDS = 8,
+ PERF_X86_SIMD_QWORDS_MAX = PERF_X86_ZMM_QWORDS,
+};
#endif /* _ASM_X86_PERF_REGS_H */
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 99eb0ea09537..4d37d11f674c 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -79,12 +79,13 @@ FEATURE_TESTS_BASIC := \
libelf-zstd \
libnuma \
numa_num_possible_cpus \
- libpython \
+ python-module \
libslang \
libtraceevent \
libcpupower \
pthread-attr-setaffinity-np \
pthread-barrier \
+ pthread-mutex-robust \
reallocarray \
stackprotector-all \
timerfd \
@@ -113,8 +114,7 @@ FEATURE_TESTS_EXTRA := \
compile-x32 \
cplus-demangle \
cxa-demangle \
- gtk2 \
- gtk2-infobar \
+ gtk4 \
hello \
babeltrace2-ctf-writer \
libcapstone \
@@ -122,7 +122,6 @@ FEATURE_TESTS_EXTRA := \
libbfd-liberty \
libbfd-liberty-z \
libopencsd \
- libperl \
llvm \
libbpf \
libpfm4 \
@@ -146,7 +145,7 @@ FEATURE_DISPLAY ?= \
libelf \
libnuma \
numa_num_possible_cpus \
- libpython \
+ python-module \
libcapstone \
llvm-perf \
zlib \
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 7d165018116a..10340a537224 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -11,8 +11,7 @@ FILES= \
test-eventfd.bin \
test-fortify-source.bin \
test-glibc.bin \
- test-gtk2.bin \
- test-gtk2-infobar.bin \
+ test-gtk4.bin \
test-hello.bin \
test-libbfd.bin \
test-libbfd-threadsafe.bin \
@@ -32,8 +31,7 @@ FILES= \
test-libdebuginfod.bin \
test-libnuma.bin \
test-numa_num_possible_cpus.bin \
- test-libperl.bin \
- test-libpython.bin \
+ test-python-module.bin \
test-libslang.bin \
test-libtraceevent.bin \
test-libcpupower.bin \
@@ -43,6 +41,7 @@ FILES= \
$(foreach arch,$(LIBUNWIND_ARCHS),test-libunwind-$(arch).bin test-libunwind-debug-frame-$(arch).bin) \
test-pthread-attr-setaffinity-np.bin \
test-pthread-barrier.bin \
+ test-pthread-mutex-robust.bin \
test-stackprotector-all.bin \
test-timerfd.bin \
test-babeltrace2-ctf-writer.bin \
@@ -130,6 +129,9 @@ $(OUTPUT)test-pthread-attr-setaffinity-np.bin:
$(OUTPUT)test-pthread-barrier.bin:
$(BUILD) -lpthread
+$(OUTPUT)test-pthread-mutex-robust.bin:
+ $(BUILD) -lpthread
+
$(OUTPUT)test-stackprotector-all.bin:
$(BUILD) -fstack-protector-all
@@ -240,46 +242,24 @@ $(OUTPUT)test-libcpupower.bin:
$(OUTPUT)test-libtracefs.bin:
$(BUILD) $(shell $(PKG_CONFIG) --cflags libtracefs 2>/dev/null) -ltracefs
-$(OUTPUT)test-gtk2.bin:
- $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) -Wno-deprecated-declarations
+$(OUTPUT)test-gtk4.bin:
+ $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk4 2>/dev/null)
-$(OUTPUT)test-gtk2-infobar.bin:
- $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null)
-
-grep-libs = $(filter -l%,$(1))
-strip-libs = $(filter-out -l%,$(1))
-
-PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null)
-PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS))
-PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS))
-PERL_EMBED_CCOPTS = $(shell perl -MExtUtils::Embed -e ccopts 2>/dev/null)
-FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS)
-
-ifeq ($(CC_NO_CLANG), 0)
- PERL_EMBED_LDOPTS := $(filter-out -specs=%,$(PERL_EMBED_LDOPTS))
- PERL_EMBED_CCOPTS := $(filter-out -flto=auto -ffat-lto-objects, $(PERL_EMBED_CCOPTS))
- PERL_EMBED_CCOPTS := $(filter-out -specs=%,$(PERL_EMBED_CCOPTS))
- FLAGS_PERL_EMBED += -Wno-compound-token-split-by-macro
-endif
-
-$(OUTPUT)test-libperl.bin:
- $(BUILD) $(FLAGS_PERL_EMBED)
-
-$(OUTPUT)test-libpython.bin:
+$(OUTPUT)test-python-module.bin:
$(BUILD) $(FLAGS_PYTHON_EMBED)
$(OUTPUT)test-libbfd.bin:
- $(BUILD_BFD)
+ $(BUILD_BFD) || $(BUILD_BFD) -lzstd
$(OUTPUT)test-libbfd-threadsafe.bin:
- $(BUILD_BFD) || $(BUILD_BFD) -liberty || $(BUILD_BFD) -liberty -lz
+ $(BUILD_BFD) || $(BUILD_BFD) -lzstd || $(BUILD_BFD) -liberty || $(BUILD_BFD) -liberty -lz
$(OUTPUT)test-disassembler-four-args.bin:
- $(BUILD_BFD) -lopcodes || $(BUILD_BFD) -lopcodes -liberty || \
+ $(BUILD_BFD) -lopcodes || $(BUILD_BFD) -lopcodes -lzstd || $(BUILD_BFD) -lopcodes -liberty || \
$(BUILD_BFD) -lopcodes -liberty -lz
$(OUTPUT)test-disassembler-init-styled.bin:
- $(BUILD_BFD) -lopcodes || $(BUILD_BFD) -lopcodes -liberty || \
+ $(BUILD_BFD) -lopcodes || $(BUILD_BFD) -lopcodes -liberty || \
$(BUILD_BFD) -lopcodes -liberty -lz
$(OUTPUT)test-reallocarray.bin:
diff --git a/tools/build/feature/test-all.c b/tools/build/feature/test-all.c
index 544563d62950..4a6890290f74 100644
--- a/tools/build/feature/test-all.c
+++ b/tools/build/feature/test-all.c
@@ -10,8 +10,8 @@
* Quirk: Python headers cannot be in arbitrary places, so keep this testcase at
* the top:
*/
-#define main main_test_libpython
-# include "test-libpython.c"
+#define main main_test_python_module
+# include "test-python-module.c"
#undef main
#define main main_test_hello
@@ -90,6 +90,10 @@
# include "test-pthread-barrier.c"
#undef main
+#define main main_test_pthread_mutex_robust
+# include "test-pthread-mutex-robust.c"
+#undef main
+
#define main main_test_scandirat
# include "test-scandirat.c"
#undef main
@@ -148,7 +152,7 @@
int main(int argc, char *argv[])
{
- main_test_libpython();
+ main_test_python_module();
main_test_hello();
main_test_libelf();
main_test_gettid();
@@ -167,6 +171,7 @@ int main(int argc, char *argv[])
main_test_zlib();
main_test_pthread_attr_setaffinity_np();
main_test_pthread_barrier();
+ main_test_pthread_mutex_robust();
main_test_lzma();
main_test_bpf();
main_test_scandirat();
diff --git a/tools/build/feature/test-gettid.c b/tools/build/feature/test-gettid.c
index ef24e42d3f1b..a8fdb2619669 100644
--- a/tools/build/feature/test-gettid.c
+++ b/tools/build/feature/test-gettid.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2019, Red Hat Inc, Arnaldo Carvalho de Melo
+#undef _GNU_SOURCE
#define _GNU_SOURCE
#include
diff --git a/tools/build/feature/test-gtk2-infobar.c b/tools/build/feature/test-gtk2-infobar.c
deleted file mode 100644
index b1b716dd5733..000000000000
--- a/tools/build/feature/test-gtk2-infobar.c
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#pragma GCC diagnostic ignored "-Wstrict-prototypes"
-#include
-#pragma GCC diagnostic error "-Wstrict-prototypes"
-
-int main(int argc, char *argv[])
-{
- gtk_init(&argc, &argv);
- gtk_info_bar_new();
-
- return 0;
-}
diff --git a/tools/build/feature/test-gtk2.c b/tools/build/feature/test-gtk4.c
similarity index 76%
rename from tools/build/feature/test-gtk2.c
rename to tools/build/feature/test-gtk4.c
index 2aaf4bfc2055..b9520e7408b9 100644
--- a/tools/build/feature/test-gtk2.c
+++ b/tools/build/feature/test-gtk4.c
@@ -3,9 +3,9 @@
#include
#pragma GCC diagnostic error "-Wstrict-prototypes"
-int main(int argc, char *argv[])
+int main(void)
{
- gtk_init(&argc, &argv);
+ gtk_init();
return 0;
}
diff --git a/tools/build/feature/test-libdw.c b/tools/build/feature/test-libdw.c
index aabd63ca76b4..46f75cde5947 100644
--- a/tools/build/feature/test-libdw.c
+++ b/tools/build/feature/test-libdw.c
@@ -49,8 +49,16 @@ int test_elfutils(void)
return 0;
}
+/* Needs elfutils 0.160: taking its address fails the probe, not the link. */
+int test_libdw_cu_getdwarf(void)
+{
+ void *sym = (void *)dwarf_cu_getdwarf;
+
+ return sym == NULL;
+}
+
int main(void)
{
return test_libdw() + test_libdw_unwind() + test_libdw_getlocations() +
- test_libdw_getcfi() + test_elfutils();
+ test_libdw_getcfi() + test_libdw_cu_getdwarf() + test_elfutils();
}
diff --git a/tools/build/feature/test-libperl.c b/tools/build/feature/test-libperl.c
deleted file mode 100644
index 0415f437eb31..000000000000
--- a/tools/build/feature/test-libperl.c
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include
-#include
-
-int main(void)
-{
- perl_alloc();
-
- return 0;
-}
diff --git a/tools/build/feature/test-libpython.c b/tools/build/feature/test-libpython.c
deleted file mode 100644
index 371c9113e49d..000000000000
--- a/tools/build/feature/test-libpython.c
+++ /dev/null
@@ -1,10 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include
-
-int main(void)
-{
- Py_Initialize();
-
- return 0;
-}
-#undef _GNU_SOURCE
diff --git a/tools/build/feature/test-pthread-mutex-robust.c b/tools/build/feature/test-pthread-mutex-robust.c
new file mode 100644
index 000000000000..d8409074713d
--- /dev/null
+++ b/tools/build/feature/test-pthread-mutex-robust.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+#include
+
+int main(void)
+{
+ pthread_mutexattr_t attr;
+ pthread_mutex_t mtx;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
+ pthread_mutex_init(&mtx, &attr);
+ pthread_mutex_consistent(&mtx);
+ pthread_mutex_destroy(&mtx);
+ pthread_mutexattr_destroy(&attr);
+
+ return 0;
+}
diff --git a/tools/build/feature/test-python-module.c b/tools/build/feature/test-python-module.c
new file mode 100644
index 000000000000..50e9e5062feb
--- /dev/null
+++ b/tools/build/feature/test-python-module.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+#include
+
+int main(void)
+{
+ static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "test",
+ };
+ PyObject *module = PyModule_Create(&moduledef);
+
+ return module ? 0 : -1;
+}
diff --git a/tools/build/feature/test-timerfd.c b/tools/build/feature/test-timerfd.c
index 9c72c697a9df..f88e8cc63ef3 100644
--- a/tools/build/feature/test-timerfd.c
+++ b/tools/build/feature/test-timerfd.c
@@ -6,7 +6,7 @@
int main(void)
{
- struct itimerspec new_value;
+ struct itimerspec new_value = {};
int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
if (fd < 0)
diff --git a/tools/include/tools/dis-asm-compat.h b/tools/include/tools/dis-asm-compat.h
index 70f331e23ed3..1e0586d8451b 100644
--- a/tools/include/tools/dis-asm-compat.h
+++ b/tools/include/tools/dis-asm-compat.h
@@ -2,6 +2,7 @@
#ifndef _TOOLS_DIS_ASM_COMPAT_H
#define _TOOLS_DIS_ASM_COMPAT_H
+#include
#include
#include
diff --git a/tools/include/uapi/linux/if_addr.h b/tools/include/uapi/linux/if_addr.h
index aa7958b4e41d..7fb630b7fe31 100644
--- a/tools/include/uapi/linux/if_addr.h
+++ b/tools/include/uapi/linux/if_addr.h
@@ -36,6 +36,7 @@ enum {
IFA_RT_PRIORITY, /* u32, priority/metric for prefix route */
IFA_TARGET_NETNSID,
IFA_PROTO, /* u8, address protocol */
+ IFA_MC_USERS, /* u32, multicast group users */
__IFA_MAX,
};
diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h
index 419011097fa8..ac2d77d14963 100644
--- a/tools/include/uapi/linux/kvm.h
+++ b/tools/include/uapi/linux/kvm.h
@@ -997,6 +997,8 @@ struct kvm_enable_cap {
#define KVM_CAP_S390_KEYOP 247
#define KVM_CAP_S390_VSIE_ESAMODE 248
#define KVM_CAP_S390_HPAGE_2G 249
+#define KVM_CAP_PPC_COMPAT_CAPS 250
+#define KVM_CAP_ARM_PMU_V3_STRICT 251
struct kvm_irq_routing_irqchip {
__u32 irqchip;
@@ -1341,6 +1343,8 @@ struct kvm_s390_keyop {
/* Available with KVM_CAP_COUNTER_OFFSET */
#define KVM_ARM_SET_COUNTER_OFFSET _IOW(KVMIO, 0xb5, struct kvm_arm_counter_offset)
#define KVM_ARM_GET_REG_WRITABLE_MASKS _IOR(KVMIO, 0xb6, struct reg_mask_range)
+/* Available with KVM_CAP_PPC_COMPAT_CAPS */
+#define KVM_PPC_GET_COMPAT_CAPS _IO(KVMIO, 0xb8)
/* ioctl for vm fd */
#define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device)
diff --git a/tools/include/uapi/linux/perf_event.h b/tools/include/uapi/linux/perf_event.h
index fd10aa8d697f..c49fc76292f7 100644
--- a/tools/include/uapi/linux/perf_event.h
+++ b/tools/include/uapi/linux/perf_event.h
@@ -314,8 +314,9 @@ enum {
*/
enum perf_sample_regs_abi {
PERF_SAMPLE_REGS_ABI_NONE = 0,
- PERF_SAMPLE_REGS_ABI_32 = 1,
- PERF_SAMPLE_REGS_ABI_64 = 2,
+ PERF_SAMPLE_REGS_ABI_32 = (1 << 0),
+ PERF_SAMPLE_REGS_ABI_64 = (1 << 1),
+ PERF_SAMPLE_REGS_ABI_SIMD = (1 << 2),
};
/*
@@ -383,6 +384,7 @@ enum perf_event_read_format {
#define PERF_ATTR_SIZE_VER7 128 /* Add: sig_data */
#define PERF_ATTR_SIZE_VER8 136 /* Add: config3 */
#define PERF_ATTR_SIZE_VER9 144 /* add: config4 */
+#define PERF_ATTR_SIZE_VER10 176 /* Add: sample_simd_{vec|pred}_reg_* */
/*
* 'struct perf_event_attr' contains various attributes that define
@@ -547,6 +549,29 @@ struct perf_event_attr {
__u64 config3; /* extension of config2 */
__u64 config4; /* extension of config3 */
+
+ /*
+ * Defines the sampling SIMD/PRED(predicate) registers bitmap and
+ * qwords (8 bytes) length.
+ *
+ * sample_simd_regs_enabled != 0 indicates there are SIMD/PRED
+ * registers to be sampled, the SIMD/PRED registers bitmap and
+ * qwords length are represented in
+ * sample_simd_{vec|pred}_reg_{intr|user} and
+ * sample_simd_{vec|pred}_reg_qwords fields separately.
+ *
+ * sample_simd_regs_enabled == 0 indicates no SIMD/PRED registers
+ * are sampled.
+ */
+ __u16 sample_simd_regs_enabled;
+ __u16 sample_simd_pred_reg_qwords;
+ __u16 sample_simd_vec_reg_qwords;
+ __u16 __reserved_4;
+
+ __u32 sample_simd_pred_reg_intr;
+ __u32 sample_simd_pred_reg_user;
+ __u64 sample_simd_vec_reg_intr;
+ __u64 sample_simd_vec_reg_user;
};
/*
@@ -1020,7 +1045,15 @@ enum perf_event_type {
* } && PERF_SAMPLE_BRANCH_STACK
*
* { u64 abi; # enum perf_sample_regs_abi
- * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
+ * u64 regs[weight(mask)];
+ * struct {
+ * u64 nr_vectors; # 0 ... weight(sample_simd_vec_reg_user)
+ * u64 vector_qwords; # 0 ... sample_simd_vec_reg_qwords
+ * u64 nr_pred; # 0 ... weight(sample_simd_pred_reg_user)
+ * u64 pred_qwords; # 0 ... sample_simd_pred_reg_qwords
+ * u64 data[nr_vectors * vector_qwords + nr_pred * pred_qwords];
+ * } && (abi & PERF_SAMPLE_REGS_ABI_SIMD)
+ * } && PERF_SAMPLE_REGS_USER
*
* { u64 size;
* char data[size];
@@ -1047,7 +1080,15 @@ enum perf_event_type {
* { u64 data_src; } && PERF_SAMPLE_DATA_SRC
* { u64 transaction; } && PERF_SAMPLE_TRANSACTION
* { u64 abi; # enum perf_sample_regs_abi
- * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR
+ * u64 regs[weight(mask)];
+ * struct {
+ * u64 nr_vectors; # 0 ... weight(sample_simd_vec_reg_intr)
+ * u64 vector_qwords; # 0 ... sample_simd_vec_reg_qwords
+ * u64 nr_pred; # 0 ... weight(sample_simd_pred_reg_intr)
+ * u64 pred_qwords; # 0 ... sample_simd_pred_reg_qwords
+ * u64 data[nr_vectors * vector_qwords + nr_pred * pred_qwords];
+ * } && (abi & PERF_SAMPLE_REGS_ABI_SIMD)
+ * } && PERF_SAMPLE_REGS_INTR
* { u64 phys_addr;} && PERF_SAMPLE_PHYS_ADDR
* { u64 cgroup;} && PERF_SAMPLE_CGROUP
* { u64 data_page_size;} && PERF_SAMPLE_DATA_PAGE_SIZE
diff --git a/tools/include/uapi/linux/rtnetlink.h b/tools/include/uapi/linux/rtnetlink.h
index 27265fd31e5f..3988b51db539 100644
--- a/tools/include/uapi/linux/rtnetlink.h
+++ b/tools/include/uapi/linux/rtnetlink.h
@@ -399,6 +399,7 @@ enum rtattr_type_t {
RTA_DPORT,
RTA_NH_ID,
RTA_FLOWLABEL,
+ RTA_DEL_REASON,
__RTA_MAX
};
@@ -407,6 +408,30 @@ enum rtattr_type_t {
#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg))))
#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg))
+/* RTA_DEL_REASON: why the kernel deleted the route. u32.
+ * Emitted only on RTM_DELROUTE notifications, and only when the deletion
+ * path records a cause. Absence means either an older kernel or a
+ * deletion path that does not (yet) record its cause - consumers must
+ * treat "absent" and "unspec" identically. New causes may be appended.
+ * Currently only IPv6 deletion paths record a cause.
+ *
+ * The attribute is notification-only: the kernel rejects it in
+ * requests, so a notification must not be echoed back verbatim.
+ *
+ * The value space is family-agnostic: a value must never be
+ * reinterpreted per address family. A cause that only one family can
+ * produce still gets its own value rather than reusing another
+ * family's.
+ */
+enum rt_del_reason {
+ RT_DEL_REASON_UNSPEC, /* cause not recorded */
+ RT_DEL_REASON_EXPIRED, /* RTF_EXPIRES lifetime ran out (GC) */
+ RT_DEL_REASON_RA_WITHDRAWN, /* zero-lifetime RA / PIO / RIO */
+ __RT_DEL_REASON_MAX
+};
+
+#define RT_DEL_REASON_MAX (__RT_DEL_REASON_MAX - 1)
+
/* RTM_MULTIPATH --- array of struct rtnexthop.
*
* "struct rtnexthop" describes all necessary nexthop information,
diff --git a/tools/lib/api/fd/array.c b/tools/lib/api/fd/array.c
index 67b73481df27..3200746d1657 100644
--- a/tools/lib/api/fd/array.c
+++ b/tools/lib/api/fd/array.c
@@ -116,6 +116,10 @@ int fdarray__filter(struct fdarray *fda, short revents,
return 0;
for (fd = 0; fd < fda->nr; ++fd) {
+ /*
+ * Explicitly bypass nonfilterable items (e.g. control descriptors).
+ * This ensures their fd, events, and revents remain completely untouched.
+ */
if (fda->priv[fd].flags & fdarray_flag__nonfilterable)
continue;
diff --git a/tools/lib/perf/evlist.c b/tools/lib/perf/evlist.c
index 1f210dadd666..c1c5b0684eed 100644
--- a/tools/lib/perf/evlist.c
+++ b/tools/lib/perf/evlist.c
@@ -79,8 +79,9 @@ static void __perf_evlist__propagate_maps(struct perf_evlist *evlist,
}
}
- /* Ensure cpus only references valid PMU CPUs. */
- if (!perf_cpu_map__has_any_cpu(evsel->cpus) &&
+ /* Ensure cpus only references valid PMU CPUs, except for uncore PMUs. */
+ if (!evsel->is_pmu_uncore &&
+ !perf_cpu_map__has_any_cpu(evsel->cpus) &&
!perf_cpu_map__is_subset(evsel->pmu_cpus, evsel->cpus)) {
struct perf_cpu_map *tmp = perf_cpu_map__intersect(evsel->pmu_cpus, evsel->cpus);
diff --git a/tools/lib/perf/include/internal/evsel.h b/tools/lib/perf/include/internal/evsel.h
index b988034f1371..35447bed0f95 100644
--- a/tools/lib/perf/include/internal/evsel.h
+++ b/tools/lib/perf/include/internal/evsel.h
@@ -128,6 +128,8 @@ struct perf_evsel {
bool requires_cpu;
/** Is the PMU for the event a core one? Effects the handling of own_cpus. */
bool is_pmu_core;
+ /** Is the PMU for the event an uncore one? */
+ bool is_pmu_uncore;
/** Does the evsel on read on the first CPU index such as tool time events? */
bool reads_only_on_cpu_idx0;
int idx;
diff --git a/tools/lib/perf/mmap.c b/tools/lib/perf/mmap.c
index ec124eb0ec0a..ab3adf1d8baf 100644
--- a/tools/lib/perf/mmap.c
+++ b/tools/lib/perf/mmap.c
@@ -200,7 +200,7 @@ void perf_mmap__read_done(struct perf_mmap *map)
/*
* Check if event was unmapped due to a POLLHUP/POLLERR.
*/
- if (!refcount_read(&map->refcnt))
+ if (!refcount_read(&map->refcnt) || !map->overwrite)
return;
map->prev = perf_mmap__read_head(map);
diff --git a/tools/perf/.clang-format b/tools/perf/.clang-format
index e4cff363a603..7cc11f84da88 100644
--- a/tools/perf/.clang-format
+++ b/tools/perf/.clang-format
@@ -21,3 +21,4 @@ IncludeCategories:
# Your Project's Other Headers
- Regex: '^".*"'
Priority: 4
+AlignArrayOfStructures: Left
diff --git a/tools/perf/Build b/tools/perf/Build
index e18c80a5c1bc..dcba5ba55206 100644
--- a/tools/perf/Build
+++ b/tools/perf/Build
@@ -61,7 +61,6 @@ perf-util-y += arch/
perf-y += arch/
perf-test-y += arch/
perf-ui-y += ui/
-perf-util-y += scripts/
gtk-y += ui/gtk/
@@ -77,7 +76,9 @@ $(OUTPUT)%.shellcheck_log: %
$(call rule_mkdir)
$(Q)$(call echo-cmd,test)$(SHELLCHECK) "$<" > $@ || (cat $@ && rm $@ && false)
-perf-y += $(SHELL_TEST_LOGS)
+.PHONY: shellcheck
+shellcheck: $(addprefix $(OUTPUT),$(SHELL_TEST_LOGS))
+ @:
ifdef MYPY
PY_TESTS := $(shell find python -type f -name '*.py')
@@ -88,9 +89,11 @@ endif
$(OUTPUT)%.mypy_log: %
$(call rule_mkdir)
- $(Q)$(call echo-cmd,test)mypy "$<" > $@ || (cat $@ && rm $@ && false)
+ $(Q)$(call echo-cmd,test)$(MYPY) "$<" > $@ || (cat $@ && rm $@ && false)
-perf-y += $(MYPY_TEST_LOGS)
+.PHONY: mypy
+mypy: $(addprefix $(OUTPUT),$(MYPY_TEST_LOGS))
+ @:
ifdef PYLINT
PY_TESTS := $(shell find python -type f -name '*.py')
@@ -101,6 +104,8 @@ endif
$(OUTPUT)%.pylint_log: %
$(call rule_mkdir)
- $(Q)$(call echo-cmd,test)pylint "$<" > $@ || (cat $@ && rm $@ && false)
+ $(Q)$(call echo-cmd,test)$(PYLINT) "$<" > $@ || (cat $@ && rm $@ && false)
-perf-y += $(PYLINT_TEST_LOGS)
+.PHONY: pylint
+pylint: $(addprefix $(OUTPUT),$(PYLINT_TEST_LOGS))
+ @:
diff --git a/tools/perf/Documentation/build-docdep.perl b/tools/perf/Documentation/build-docdep.perl
index ba4205e0302a..f19f5107fe0f 100755
--- a/tools/perf/Documentation/build-docdep.perl
+++ b/tools/perf/Documentation/build-docdep.perl
@@ -41,6 +41,6 @@ while ($changed) {
while (my ($text, $included) = each %include) {
if (! exists $included{$text} &&
(my $base = $text) =~ s/\.txt$//) {
- print "$base.html $base.xml : ", join(" ", keys %$included), "\n";
+ print "\$(OUTPUT)$base.html \$(OUTPUT)$base.xml : ", join(" ", keys %$included), "\n";
}
}
diff --git a/tools/perf/Documentation/db-export.txt b/tools/perf/Documentation/db-export.txt
index 52ffccb02d55..a736be3b6e84 100644
--- a/tools/perf/Documentation/db-export.txt
+++ b/tools/perf/Documentation/db-export.txt
@@ -1,14 +1,14 @@
Database Export
===============
-perf tool's python scripting engine:
+perf tool's python module:
- tools/perf/util/scripting-engines/trace-event-python.c
+ tools/perf/util/python.c
-supports scripts:
+supports standalone scripts:
- tools/perf/scripts/python/export-to-sqlite.py
- tools/perf/scripts/python/export-to-postgresql.py
+ tools/perf/python/export-to-sqlite.py
+ tools/perf/python/export-to-postgresql.py
which export data to a SQLite3 or PostgreSQL database.
@@ -31,11 +31,5 @@ backward compatibility by testing for the presence of new tables and columns
before using them. e.g. function IsSelectable() in exported-sql-viewer.py
4. The export scripts themselves maintain forward compatibility (i.e. an existing
-script will continue to work with new versions of perf) by accepting a variable
-number of arguments (e.g. def call_return_table(*x)) i.e. perf can pass more
-arguments which old scripts will ignore.
-
-5. The scripting engine tests for the existence of script handler functions
-before calling them. The scripting engine can also test for the support of new
-or optional features by checking for the existence and value of script global
-variables.
+script will continue to work with new versions of perf) by querying event and
+sample attributes dynamically from the perf Python module.
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt
index a688738809c4..1a90b09a12d5 100644
--- a/tools/perf/Documentation/perf-annotate.txt
+++ b/tools/perf/Documentation/perf-annotate.txt
@@ -95,6 +95,9 @@ include::itrace.txt[]
--gtk:: Use the GTK interface.
+--weights:: Show or hide (with `--no-weights`) weight columns in annotation output.
+ By default, weight columns are shown when samples contain non-zero weights.
+
-C::
--cpu=:: Only report samples for the list of CPUs provided. Multiple CPUs can
be provided as a comma-separated list with no space: 0,1. Ranges of
diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt
index c5913cf59c98..7f820f69740f 100644
--- a/tools/perf/Documentation/perf-bench.txt
+++ b/tools/perf/Documentation/perf-bench.txt
@@ -58,6 +58,9 @@ SUBSYSTEM
'numa'::
NUMA scheduling and MM benchmarks.
+'atomic'::
+ Atomic operation benchmarks.
+
'futex'::
Futex stressing benchmarks.
@@ -283,6 +286,41 @@ SUITES FOR 'numa'
*mem*::
Suite for evaluating NUMA workloads.
+SUITES FOR 'atomic'
+~~~~~~~~~~~~~~~~~~~
+*cas*::
+Suite for evaluating compare-and-swap (CAS) atomic operations under
+multi-threaded contention.
+
+*inc*::
+Suite for evaluating atomic fetch-add (increment) operations under
+multi-threaded contention.
+
+Options of *cas* and *inc*
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+-T::
+--threads=::
+Number of threads contending for the shared counter (default: 2, or
+1 on a single-CPU machine).
+
+-R::
+--runtime=::
+Runtime of each repeat in seconds (default: 2).
+
+The per-thread fastest/slowest/avg ops summary is only printed when
+running with more than one thread.
+
+The average throughput is annotated with its relative deviation
+("+- x%"), i.e. the standard error of the mean over the measured
+repeats. A small value means the average is reproducible; it does not
+reflect per-thread fairness, which the per-thread ops summary shows.
+
+Note that the cas benchmark counts successful compare-and-swap
+operations only; retries and the loads between them are additional
+atomic instructions not included in the ops/sec count, so cas and
+inc throughputs are not directly comparable at the instruction
+level.
+
SUITES FOR 'futex'
~~~~~~~~~~~~~~~~~~
*hash*::
diff --git a/tools/perf/Documentation/perf-c2c.txt b/tools/perf/Documentation/perf-c2c.txt
index 8775889bc0a3..7a0cf31be7ed 100644
--- a/tools/perf/Documentation/perf-c2c.txt
+++ b/tools/perf/Documentation/perf-c2c.txt
@@ -107,6 +107,10 @@ REPORT OPTIONS
--stats::
Display only statistic tables and force stdio mode.
+--function::
+ Display the function view and force stdio mode. This requires `iaddr`
+ in the cacheline coalescing fields and cannot be used with `--stats`.
+
--full-symbols::
Display full length of symbols.
@@ -341,7 +345,7 @@ output fields set for cacheline offsets output:
Code address, Code symbol, Shared Object, Source line
dso - coalesced by shared object
-By default the coalescing is setup with 'pid,iaddr'.
+By default, coalescing uses `iaddr`.
STDIO OUTPUT
------------
@@ -360,6 +364,10 @@ Following tables are displayed:
Shared Cache Line Distribution Pareto
- list of all accessed offsets for each cacheline
+With `--function`, the cacheline and Pareto tables are replaced by a fully
+expanded Shared Data Functions Table. Its three levels are the read-side
+function, contending writer, and shared cacheline, as detailed below.
+
TUI OUTPUT
----------
The TUI output provides interactive interface to navigate
@@ -374,8 +382,8 @@ Verbose mode also includes code addresses in function rows, and code addresses
remain available in the per-cacheline detail view ('d').
The function view requires `iaddr` in the cacheline coalescing fields. If
-`--coalesce` omits it, TAB reports that the view is unavailable rather than
-attributing already-coalesced samples to an arbitrary function.
+`--coalesce` omits it, TAB or `--function` reports that the view is unavailable
+rather than attributing already-coalesced samples to an arbitrary function.
Level 1: the read-side function, sorted by Cycles % (estimated load
cycles: HITM, peer-snoop and other-load cycles)
diff --git a/tools/perf/Documentation/perf-check.txt b/tools/perf/Documentation/perf-check.txt
index 09e1d35677f5..1406a41c5d09 100644
--- a/tools/perf/Documentation/perf-check.txt
+++ b/tools/perf/Documentation/perf-check.txt
@@ -58,14 +58,13 @@ feature::
libLLVM / HAVE_LIBLLVM_SUPPORT
libnuma / HAVE_LIBNUMA_SUPPORT
libopencsd / HAVE_CSTRACE_SUPPORT
- libperl / HAVE_LIBPERL_SUPPORT
libpfm4 / HAVE_LIBPFM
- libpython / HAVE_LIBPYTHON_SUPPORT
libslang / HAVE_SLANG_SUPPORT
libtraceevent / HAVE_LIBTRACEEVENT
libunwind / HAVE_LIBUNWIND_SUPPORT
lzma / HAVE_LZMA_SUPPORT
numa_num_possible_cpus / HAVE_LIBNUMA_SUPPORT
+ python-module / HAVE_PYTHON_MODULE_SUPPORT
zlib / HAVE_ZLIB_SUPPORT
zstd / HAVE_ZSTD_SUPPORT
diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt
index 9b223f892829..6cff6f26af8d 100644
--- a/tools/perf/Documentation/perf-config.txt
+++ b/tools/perf/Documentation/perf-config.txt
@@ -217,6 +217,16 @@ core.*::
Sets a timeout (in milliseconds) for parsing 'addr2line'
output. The default timeout is 5s.
+ hybrid-merge::
+ Merge matching events from all hybrid core PMUs into one display
+ by default. For example, if a wildcard expands to run on both p-cores and
+ e-cores, this aggregates them into a single view. This applies to
+ 'perf report' and 'perf top', it is ignored with '--hierarchy' and
+ doesn't alter 'perf stat' where '--hybrid-merge' must be given
+ explicitly. As a default it is silently ignored when there is
+ nothing to merge, such as on a machine with a single core PMU,
+ whereas an explicit '--hybrid-merge' warns.
+
tui.*, gtk.*::
Subcommands that can be configured here are 'top', 'report' and 'annotate'.
These values are booleans, for example:
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt
index a4378a0cd914..17e582998474 100644
--- a/tools/perf/Documentation/perf-list.txt
+++ b/tools/perf/Documentation/perf-list.txt
@@ -10,6 +10,7 @@ SYNOPSIS
[verse]
'perf list' []
[hw|sw|cache|tracepoint|pmu|sdt|metric|metricgroup|event_glob]
+'perf list' --tui [-- ]
DESCRIPTION
-----------
@@ -51,6 +52,15 @@ Output in JSON format.
--output=::
Output file name. By default output is written to stdout.
+--tui::
+Rather than printing the events, interactively browse the PMUs, events and
+metrics in the terminal using the 'ilist' python script (see
+linkperf:perf-script[1]). Selecting an event or metric shows its description
+and counts it on each CPU. The script requires the perf python module and the
+python 'textual' library. Other options are ignored, arguments after '--' are
+passed to the script, for example 'perf list --tui -- -I 0.5' to update the
+counts every 0.5 seconds.
+
[[EVENT_MODIFIERS]]
EVENT MODIFIERS
---------------
diff --git a/tools/perf/Documentation/perf-mem.txt b/tools/perf/Documentation/perf-mem.txt
index 4d164836d094..fe51c5e3333d 100644
--- a/tools/perf/Documentation/perf-mem.txt
+++ b/tools/perf/Documentation/perf-mem.txt
@@ -14,6 +14,10 @@ DESCRIPTION
-----------
"perf mem record" runs a command and gathers memory operation data
from it, into perf.data. Perf record options are accepted and are passed through.
+It also requests the address (-d), the weight (-W, where supported) and the
+CPU id (--sample-cpu) of every sampled access by default; the CPU id is what
+lets per-sample analysis tell reads and writes to the same data from
+different cores apart from same-CPU traffic.
"perf mem report" displays the result. It invokes perf report with the
right set of options to display a memory access profile. By default, loads
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index 178f483140ed..2351f7b4f5fc 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -212,7 +212,7 @@ OPTIONS
The can be one of:
(for any term)
na, load, store, pfetch, exec (for mem_op)
- l1, l2, l3, l4, cxl, io, any_cache, lfb, ram, pmem (for mem_lvl)
+ l0, l1, l2, l3, l4, cxl, io, any_cache, lfb, ram, pmem (for mem_lvl)
na, none, hit, miss, hitm, fwd, peer (for mem_snoop)
remote (for mem_remote)
na, locked (for mem_locked)
@@ -293,7 +293,7 @@ OPTIONS
--call-graph::
Setup and enable call-graph (stack chain/backtrace) recording,
- implies -g. Default is "fp" (for user space).
+ implies -g. Default is "fp" for user space (s390 uses "dwarf").
The unwinding method used for kernel space is dependent on the
unwinder used by the active kernel configuration, i.e
@@ -329,6 +329,11 @@ OPTIONS
enable deferred user callchain which will collect user-space callchains
when the thread returns to the user space.
+ On s390, the "fp" method uses back chain instead of frame pointers,
+ which requires user space to maintain a back chain (e.g. compiler
+ option -mbackchain). Without a maintained back chain this produces
+ bogus call graphs. In that case use "dwarf" instead.
+
-q::
--quiet::
Don't print any warnings or messages, useful for scripting.
@@ -480,9 +485,11 @@ Note that this feature may not be available on all processors.
-W::
--weight::
-Enable weightened sampling. An additional weight is recorded per sample and can be
-displayed with the weight and local_weight sort keys. This currently works for TSX
-abort events and some memory events in precise mode on modern Intel CPUs.
+Enable weighted sampling. One or more additional weights can be recorded per sample
+and can be displayed with the weight, weight2, weight3 and local_weight sort keys.
+Possible uses for weights are memory or instruction retirement latencies.
+Support depends on the PMU and the event, as well as its configuration. Typically
+it requires use of 'p'.
--namespaces::
Record events of type PERF_RECORD_NAMESPACES. This enables 'cgroup_id' sort key.
@@ -513,12 +520,16 @@ Capture machine state (registers) at interrupt, i.e., on counter overflows for
each sample. List of captured registers depends on the architecture. This option
is off by default. It is possible to select the registers to sample using their
symbolic names, e.g. on x86, ax, si. To list the available registers use
---intr-regs=\?. To name registers, pass a comma separated list such as
---intr-regs=ax,bx. The list of register is architecture dependent.
+--intr-regs=\?. On supported architectures, SIMD registers are displayed as
+groups (e.g., on x86: XMM0-15,YMM0-15,ZMM0-31). To name registers, pass a comma
+separated list such as --intr-regs=ax,bx,zmm. Please notice SIMD registers must
+be assigned as a complete set, sampling individual SIMD registers (e.g., zmm0)
+is not supported. The list of register is architecture dependent.
--user-regs::
Similar to -I, but capture user registers at sample time. To list the available
-user registers use --user-regs=\?.
+user registers use --user-regs=\?. For SIMD registers, only complete register
+sets are allowed like -I.
--running-time::
Record running and enabled time for read events (:S)
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 22f87eaa3279..e145124d6c9a 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -29,6 +29,25 @@ OPTIONS
--quiet::
Do not show any warnings or messages. (Suppress -v)
+--progress::
+ Show progress for each of the processing phases, printing the
+ percentage done and the current/total counts: the first phase
+ counts the bytes of the perf.data file processed so far, the
+ merge and sort phases count hist entries, e.g.:
+
+ Processing events... [ 42.3%] 4G / 10G
+ Merging related events... [ 7.1%] 1024 / 14387
+ Sorting events for output... [ 98.2%] 14132 / 14387
+
+ It is a no-op when using the TUI or GTK browsers, that already
+ present progress information, or when --quiet is used, that asks
+ for no messages at all.
+
+--no-progress::
+ Do not show progress while processing the perf.data file. It
+ also turns off the progress the TUI and GTK browsers present,
+ which is their own.
+
-n::
--show-nr-samples::
Show the number of samples for each symbol
@@ -64,6 +83,9 @@ OPTIONS
--symbol-filter=::
Only show symbols that match (partially) with this filter.
+--weights:: Show or hide (with `--no-weights`) weight columns in annotation output.
+ By default, weight columns are shown when samples contain non-zero weights.
+
-U::
--hide-unresolved::
Only display entries resolved to a symbol.
@@ -351,7 +373,10 @@ OPTIONS
requires a tty, if one is not present, as when piping to other
commands, the stdio interface is used.
---gtk:: Use the GTK2 interface.
+--gtk:: Use the GTK4 interface.
+
+--weights:: Show or hide (with `--no-weights`) weight columns in annotation output.
+ By default, weight columns are shown when samples contain non-zero weights.
-k::
--vmlinux=::
@@ -572,6 +597,11 @@ include::itrace.txt[]
--raw-trace::
When displaying traceevent output, do not use print fmt or plugins.
+--hybrid-merge::
+ Merge matching events from all hybrid core PMUs into one
+ display. For example, if a wildcard expands to run on both p-cores and
+ e-cores, this aggregates them into a single view.
+
-H::
--hierarchy::
Enable hierarchical output. In the hierarchy mode, each sort key groups
diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt
index 4da06215163a..fe0abef2bd13 100644
--- a/tools/perf/Documentation/perf-sched.txt
+++ b/tools/perf/Documentation/perf-sched.txt
@@ -95,8 +95,10 @@ There are several variants of 'perf sched':
events, ``try_to_wakeup()`` call among others. This is useful in understanding the
scheduler behavior for the workload.
- Note: The tool will not give correct results if there is topological reordering or
- online/offline of cpus in between capturing snapshots of `/proc/schedstat`.
+ Note: Reports reject incomplete snapshots and mismatched CPU or domain IDs.
+ Topology changes that retain the same IDs, including CPUs going offline
+ and returning online between snapshots, cannot be detected and may still
+ produce incorrect results.
Example usage:
perf sched stats record -- sleep 1
diff --git a/tools/perf/Documentation/perf-script-perl.txt b/tools/perf/Documentation/perf-script-perl.txt
deleted file mode 100644
index 5b479f5e62ff..000000000000
--- a/tools/perf/Documentation/perf-script-perl.txt
+++ /dev/null
@@ -1,216 +0,0 @@
-perf-script-perl(1)
-===================
-
-NAME
-----
-perf-script-perl - Process trace data with a Perl script
-
-SYNOPSIS
---------
-[verse]
-'perf script' [-s [Perl]:script[.pl] ]
-
-DESCRIPTION
------------
-
-This perf script option is used to process perf script data using perf's
-built-in Perl interpreter. It reads and processes the input file and
-displays the results of the trace analysis implemented in the given
-Perl script, if any.
-
-STARTER SCRIPTS
----------------
-
-You can avoid reading the rest of this document by running 'perf script
--g perl' in the same directory as an existing perf.data trace file.
-That will generate a starter script containing a handler for each of
-the event types in the trace file; it simply prints every available
-field for each event in the trace file.
-
-You can also look at the existing scripts in
-~/libexec/perf-core/scripts/perl for typical examples showing how to
-do basic things like aggregate event data, print results, etc. Also,
-the check-perf-script.pl script, while not interesting for its results,
-attempts to exercise all of the main scripting features.
-
-EVENT HANDLERS
---------------
-
-When perf script is invoked using a trace script, a user-defined
-'handler function' is called for each event in the trace. If there's
-no handler function defined for a given event type, the event is
-ignored (or passed to a 'trace_unhandled' function, see below) and the
-next event is processed.
-
-Most of the event's field values are passed as arguments to the
-handler function; some of the less common ones aren't - those are
-available as calls back into the perf executable (see below).
-
-As an example, the following perf record command can be used to record
-all sched_wakeup events in the system:
-
- # perf record -a -e sched:sched_wakeup
-
-Traces meant to be processed using a script should be recorded with
-the above option: -a to enable system-wide collection.
-
-The format file for the sched_wakeup event defines the following fields
-(see /sys/kernel/tracing/events/sched/sched_wakeup/format):
-
-----
- format:
- field:unsigned short common_type;
- field:unsigned char common_flags;
- field:unsigned char common_preempt_count;
- field:int common_pid;
-
- field:char comm[TASK_COMM_LEN];
- field:pid_t pid;
- field:int prio;
- field:int success;
- field:int target_cpu;
-----
-
-The handler function for this event would be defined as:
-
-----
-sub sched::sched_wakeup
-{
- my ($event_name, $context, $common_cpu, $common_secs,
- $common_nsecs, $common_pid, $common_comm,
- $comm, $pid, $prio, $success, $target_cpu) = @_;
-}
-----
-
-The handler function takes the form subsystem::event_name.
-
-The $common_* arguments in the handler's argument list are the set of
-arguments passed to all event handlers; some of the fields correspond
-to the common_* fields in the format file, but some are synthesized,
-and some of the common_* fields aren't common enough to to be passed
-to every event as arguments but are available as library functions.
-
-Here's a brief description of each of the invariant event args:
-
- $event_name the name of the event as text
- $context an opaque 'cookie' used in calls back into perf
- $common_cpu the cpu the event occurred on
- $common_secs the secs portion of the event timestamp
- $common_nsecs the nsecs portion of the event timestamp
- $common_pid the pid of the current task
- $common_comm the name of the current process
-
-All of the remaining fields in the event's format file have
-counterparts as handler function arguments of the same name, as can be
-seen in the example above.
-
-The above provides the basics needed to directly access every field of
-every event in a trace, which covers 90% of what you need to know to
-write a useful trace script. The sections below cover the rest.
-
-SCRIPT LAYOUT
--------------
-
-Every perf script Perl script should start by setting up a Perl module
-search path and 'use'ing a few support modules (see module
-descriptions below):
-
-----
- use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
- use lib "./Perf-Trace-Util/lib";
- use Perf::Trace::Core;
- use Perf::Trace::Context;
- use Perf::Trace::Util;
-----
-
-The rest of the script can contain handler functions and support
-functions in any order.
-
-Aside from the event handler functions discussed above, every script
-can implement a set of optional functions:
-
-*trace_begin*, if defined, is called before any event is processed and
-gives scripts a chance to do setup tasks:
-
-----
- sub trace_begin
- {
- }
-----
-
-*trace_end*, if defined, is called after all events have been
- processed and gives scripts a chance to do end-of-script tasks, such
- as display results:
-
-----
-sub trace_end
-{
-}
-----
-
-*trace_unhandled*, if defined, is called after for any event that
- doesn't have a handler explicitly defined for it. The standard set
- of common arguments are passed into it:
-
-----
-sub trace_unhandled
-{
- my ($event_name, $context, $common_cpu, $common_secs,
- $common_nsecs, $common_pid, $common_comm) = @_;
-}
-----
-
-The remaining sections provide descriptions of each of the available
-built-in perf script Perl modules and their associated functions.
-
-AVAILABLE MODULES AND FUNCTIONS
--------------------------------
-
-The following sections describe the functions and variables available
-via the various Perf::Trace::* Perl modules. To use the functions and
-variables from the given module, add the corresponding 'use
-Perf::Trace::XXX' line to your perf script script.
-
-Perf::Trace::Core Module
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-These functions provide some essential functions to user scripts.
-
-The *flag_str* and *symbol_str* functions provide human-readable
-strings for flag and symbolic fields. These correspond to the strings
-and values parsed from the 'print fmt' fields of the event format
-files:
-
- flag_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the flag field $field_name of event $event_name
- symbol_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the symbolic field $field_name of event $event_name
-
-Perf::Trace::Context Module
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Some of the 'common' fields in the event format file aren't all that
-common, but need to be made accessible to user scripts nonetheless.
-
-Perf::Trace::Context defines a set of functions that can be used to
-access this data in the context of the current event. Each of these
-functions expects a $context variable, which is the same as the
-$context variable passed into every event handler as the second
-argument.
-
- common_pc($context) - returns common_preempt count for the current event
- common_flags($context) - returns common_flags for the current event
- common_lock_depth($context) - returns common_lock_depth for the current event
-
-Perf::Trace::Util Module
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Various utility functions for use with perf script:
-
- nsecs($secs, $nsecs) - returns total nsecs given secs/nsecs pair
- nsecs_secs($nsecs) - returns whole secs portion given nsecs
- nsecs_nsecs($nsecs) - returns nsecs remainder given nsecs
- nsecs_str($nsecs) - returns printable string in the form secs.nsecs
- avg($total, $n) - returns average given a sum and a total number of values
-
-SEE ALSO
---------
-linkperf:perf-script[1]
diff --git a/tools/perf/Documentation/perf-script-python.txt b/tools/perf/Documentation/perf-script-python.txt
index 27a1cac6fe76..cbf50b3e5f06 100644
--- a/tools/perf/Documentation/perf-script-python.txt
+++ b/tools/perf/Documentation/perf-script-python.txt
@@ -3,676 +3,182 @@ perf-script-python(1)
NAME
----
-perf-script-python - Process trace data with a Python script
+perf-script-python - Process trace data with a Python script using perf module
SYNOPSIS
--------
[verse]
-'perf script' [-s [Python]:script[.py] ]
+'perf script'
+
+
+
Redirecting to Firefox Profiler...
+"""
+
+class Node:
+ """A node in the flame graph tree."""
+ def __init__(self, name: str, libtype: str):
+ self.name = name
+ self.libtype = libtype
+ self.value: int = 0
+ self.children: dict[str, Node] = {}
+
+ def to_json(self) -> Dict[str, Union[str, int, list[Dict]]]:
+ """Convert the node to a JSON-serializable dictionary."""
+ return {
+ "n": self.name,
+ "l": self.libtype,
+ "v": self.value,
+ "c": [x.to_json() for x in self.children.values()]
+ }
+
+
+class FlameGraphCLI:
+ """Command-line interface for generating flame graphs."""
+ def __init__(self, args):
+ self.args = args
+ self.stack = Node("all", "root")
+ self.session = None
+
+ @staticmethod
+ def get_libtype_from_dso(dso: Optional[str]) -> str:
+ """Determine the library type from the DSO name."""
+ if dso and (dso == "[kernel.kallsyms]" or dso.endswith("/vmlinux") or dso == "[kernel]"):
+ return "kernel"
+ return ""
+
+ @staticmethod
+ def find_or_create_node(node: Node, name: str, libtype: str) -> Node:
+ """Find a child node with the given name or create a new one."""
+ if name in node.children:
+ return node.children[name]
+ child = Node(name, libtype)
+ node.children[name] = child
+ return child
+
+ def process_event(self, sample) -> None:
+ """Process a single perf sample event."""
+ if self.args.event_name and self.args.event_name not in str(sample.evsel):
+ return
+
+ pid = sample.sample_pid
+ dso_type = ""
+ try:
+ thread = self.session.find_thread(sample.sample_pid, sample.sample_tid)
+ comm = (thread.comm() if thread else None) or "[unknown]"
+ except (OSError, ValueError, KeyError, RuntimeError, TypeError, AttributeError):
+ comm = "[unknown]"
+
+ if pid == 0:
+ comm = comm if comm != "[unknown]" else "swapper"
+ dso_type = "kernel"
+ else:
+ comm = f"{comm} ({pid})"
+
+ node = self.find_or_create_node(self.stack, comm, dso_type)
+
+ callchain = sample.callchain
+ if callchain:
+ # We want to traverse from root to leaf.
+ # perf callchain iterator gives leaf to root.
+ # We collect them and reverse.
+ frames = list(callchain)
+ for entry in reversed(frames):
+ name = entry.symbol or "[unknown]"
+ libtype = self.get_libtype_from_dso(entry.dso)
+ node = self.find_or_create_node(node, name, libtype)
+ else:
+ # Fallback if no callchain
+ name = (sample.symbol or '[unknown]')
+ libtype = self.get_libtype_from_dso((sample.dso or '[unknown]'))
+ node = self.find_or_create_node(node, name, libtype)
+
+ node.value += 1
+
+ def get_report_header(self) -> str:
+ """Get the header from the perf report."""
+ try:
+ input_file = self.args.input or "perf.data"
+ if input_file == "-":
+ return ""
+ mode = os.stat(input_file).st_mode
+ import stat
+ if stat.S_ISFIFO(mode) or stat.S_ISCHR(mode):
+ return ""
+ output = subprocess.check_output(["perf", "report", "--header-only", "-i", input_file])
+ result = output.decode("utf-8")
+ if self.args.event_name:
+ result += "\nFocused event: " + self.args.event_name
+ return result
+ except (OSError, ValueError, KeyError, RuntimeError, TypeError, AttributeError,
+ subprocess.CalledProcessError):
+ return ""
+
+ def run(self) -> None:
+ """Run the flame graph generation."""
+ input_file = self.args.input or "perf.data"
+ if input_file != "-" and not os.path.exists(input_file):
+ print(f"Error: {input_file} not found. (try 'perf record' first)", file=sys.stderr)
+ sys.exit(1)
+
+ try:
+ self.session = perf.session(perf.data(input_file),
+ sample=self.process_event)
+ except (OSError, ValueError, KeyError, RuntimeError, TypeError, AttributeError) as e:
+ print(f"Error opening session: {e}", file=sys.stderr)
+ sys.exit(1)
+
+ try:
+ self.session.process_events()
+ finally:
+ # perf.session is a C extension type that holds a strong reference to the
+ # bound method self.process_event (which references self) and does not
+ # implement cyclic GC (tp_traverse). Explicitly clear self.session to break
+ # the reference cycle and free the underlying session resources deterministically.
+ self.session = None
+
+ stacks_json = json.dumps(self.stack, default=lambda x: x.to_json())
+ # Escape HTML special characters to prevent XSS
+ stacks_json = stacks_json.replace("<", "\\u003c") \
+ .replace(">", "\\u003e").replace("&", "\\u0026")
+
+ if self.args.format == "html":
+ report_header = self.get_report_header()
+ options = {
+ "colorscheme": self.args.colorscheme,
+ "context": report_header
+ }
+ options_json = json.dumps(options)
+ options_json = options_json.replace("<", "\\u003c") \
+ .replace(">", "\\u003e").replace("&", "\\u0026")
+
+ template = self.args.template
+ template_sha256sum = None
+ output_str = None
+
+ if not os.path.isfile(template):
+ if template.startswith("http://") or template.startswith("https://"):
+ if not self.args.allow_download:
+ print("Warning: Downloading templates is disabled. "
+ "Use --allow-download.", file=sys.stderr)
+ template = None
+ else:
+ print(f"Warning: Template file '{template}' not found.", file=sys.stderr)
+ if self.args.allow_download:
+ print("Using default CDN template.", file=sys.stderr)
+ template = (
+ "https://cdn.jsdelivr.net/npm/d3-flame-graph@4.1.3/dist/templates/"
+ "d3-flamegraph-base.html"
+ )
+ template_sha256sum = (
+ "f6a4aa7edffda4fb9bd71eb0eb75bc44d0bb34cd9efbd053f6095bc5c28d702b"
+ )
+ else:
+ template = None
+
+ use_minimal = False
+ try:
+ if not template:
+ use_minimal = True
+ elif template.startswith(("http://", "https://")):
+ with urllib.request.urlopen(template) as url_template:
+ output_str = "".join([l.decode("utf-8") for l in url_template.readlines()])
+ else:
+ with open(template, "r", encoding="utf-8") as f:
+ output_str = f.read()
+ except (OSError, ValueError, KeyError, RuntimeError, TypeError, AttributeError) as err:
+ print(f"Error reading template {template}: {err}\n", file=sys.stderr)
+ use_minimal = True
+
+ if use_minimal:
+ print("Using internal minimal HTML that refers to d3's web site. JavaScript " +
+ "loaded this way from a local file may be blocked unless your " +
+ "browser has relaxed permissions. Run with '--allow-download' to fetch " +
+ "the full D3 HTML template.", file=sys.stderr)
+ output_str = MINIMAL_HTML
+
+ elif template_sha256sum:
+ assert output_str is not None
+ download_sha256sum = hashlib.sha256(
+ output_str.encode("utf-8")
+ ).hexdigest()
+ if download_sha256sum != template_sha256sum:
+ s = None
+ while s not in ["y", "n"]:
+ try:
+ s = input(f"""Unexpected template sha256sum.
+{download_sha256sum} != {template_sha256sum}, for:
+{template}
+continue?[yn] """).lower()
+ except EOFError:
+ s = "n"
+ if s == "n":
+ sys.exit(1)
+
+ assert output_str is not None
+ replacements = {
+ "/** @options_json **/": options_json,
+ "/** @flamegraph_json **/": stacks_json,
+ }
+ # Use a lambda replacement so re.sub does not interpret backslashes
+ # in the JSON strings (such as \n or \u003c) as regex escape sequences.
+ output_str = re.sub(
+ r"/\*\* @(?:options_json|flamegraph_json) \*\*/",
+ lambda m: replacements[m.group(0)],
+ output_str,
+ )
+ output_fn = self.args.output or "flamegraph.html"
+ else:
+ output_str = stacks_json
+ output_fn = self.args.output or "stacks.json"
+
+ if output_fn == "-":
+ with open(sys.stdout.fileno(), "w", encoding="utf-8", closefd=False) as out:
+ out.write(output_str)
+ else:
+ print(f"dumping data to {output_fn}")
+ with open(output_fn, "w", encoding="utf-8") as out:
+ out.write(output_str)
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="Create flame graphs using perf python module.")
+ parser.add_argument("-f", "--format", default="html", choices=["json", "html"],
+ help="output file format")
+ parser.add_argument("-o", "--output", help="output file name")
+ parser.add_argument("--template",
+ default="/usr/share/d3-flame-graph/d3-flamegraph-base.html",
+ help="path to flame graph HTML template")
+ parser.add_argument("--colorscheme", default="blue-green",
+ help="flame graph color scheme", choices=["blue-green", "orange"])
+ parser.add_argument("-i", "--input", help="input perf.data file")
+ parser.add_argument("--allow-download", default=False, action="store_true",
+ help="allow unprompted downloading of HTML template")
+ parser.add_argument("-e", "--event", default="", dest="event_name", type=str,
+ help="specify the event to generate flamegraph for")
+
+ cli_args = parser.parse_args()
+ cli = FlameGraphCLI(cli_args)
+ cli.run()
diff --git a/tools/perf/python/futex-contention.py b/tools/perf/python/futex-contention.py
new file mode 100755
index 000000000000..097663d929b7
--- /dev/null
+++ b/tools/perf/python/futex-contention.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+"""Measures futex contention."""
+from __future__ import annotations
+
+import argparse
+from collections import defaultdict
+from typing import Dict, Tuple
+import perf
+
+class LockStats:
+ """Aggregate lock contention information."""
+ def __init__(self) -> None:
+ self.count = 0
+ self.total_time = 0
+ self.min_time = 0
+ self.max_time = 0
+
+ def add(self, duration: int) -> None:
+ """Add a new duration measurement."""
+ self.count += 1
+ self.total_time += duration
+ if self.count == 1:
+ self.min_time = duration
+ self.max_time = duration
+ else:
+ self.min_time = min(self.min_time, duration)
+ self.max_time = max(self.max_time, duration)
+
+ def avg(self) -> float:
+ """Return average duration."""
+ return self.total_time / self.count if self.count > 0 else 0.0
+
+process_names: Dict[int, str] = {}
+start_times: Dict[int, Tuple[int, int]] = {}
+session = None
+durations: Dict[Tuple[int, int], LockStats] = defaultdict(LockStats)
+
+FUTEX_WAIT = 0
+FUTEX_PRIVATE_FLAG = 128
+FUTEX_CLOCK_REALTIME = 256
+# Mask out FUTEX_PRIVATE_FLAG and FUTEX_CLOCK_REALTIME so variants such as
+# FUTEX_WAIT_PRIVATE (0 | 128) match the base FUTEX_WAIT command.
+FUTEX_CMD_MASK = ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME)
+
+
+def handle_start(pid: int, tid: int, uaddr: int, op: int, start_time: int) -> None:
+ """Handle a futex sys_enter event."""
+ if (op & FUTEX_CMD_MASK) != FUTEX_WAIT:
+ return
+ try:
+ if session:
+ process = session.find_thread(pid, tid)
+ if process:
+ process_names[tid] = process.comm() or "unknown"
+ except (TypeError, AttributeError):
+ pass
+ if tid not in process_names:
+ process_names[tid] = "unknown"
+
+ start_times[tid] = (uaddr, start_time)
+
+def handle_end(tid: int, end_time: int) -> None:
+ """Handle a futex sys_exit event."""
+ if tid not in start_times:
+ return
+ (uaddr, start_time) = start_times[tid]
+ del start_times[tid]
+ durations[(tid, uaddr)].add(end_time - start_time)
+
+def process_event(sample: perf.sample_event) -> None:
+ """Process a single sample event."""
+ event_name = str(sample.evsel)
+ if event_name.startswith("evsel(") and event_name.endswith(")"):
+ event_name = event_name[6:-1]
+ if event_name.startswith("syscalls:sys_enter_futex"):
+ uaddr = getattr(sample, "uaddr", None)
+ op = getattr(sample, "op", None)
+ if uaddr is None or op is None:
+ return # Tracepoint fields missing, skip silent attribution to 0
+ handle_start(getattr(sample, "sample_pid", sample.sample_tid),
+ sample.sample_tid, uaddr, op, sample.sample_time)
+ elif event_name.startswith("syscalls:sys_exit_futex"):
+ handle_end(sample.sample_tid, sample.sample_time)
+
+
+if __name__ == "__main__":
+ ap = argparse.ArgumentParser(description="Measure futex contention")
+ ap.add_argument("-i", "--input", default="perf.data", help="Input file name")
+ args = ap.parse_args()
+
+ try:
+ session = perf.session(perf.data(args.input), sample=process_event)
+ try:
+ session.process_events()
+ except KeyboardInterrupt:
+ pass
+
+ for ((t, u), stats) in sorted(durations.items()):
+ avg_ns = stats.avg()
+ print(f"{process_names.get(t, 'unknown')}[{t}] lock {u:x} contended "
+ f"{stats.count} times, {avg_ns:.0f} avg ns "
+ f"[max: {stats.max_time} ns, min {stats.min_time} ns]")
+ finally:
+ # Break the reference cycle between the global session and the
+ # process_event callback so the C perf.session object is freed.
+ session = None
diff --git a/tools/perf/python/gecko.py b/tools/perf/python/gecko.py
new file mode 100755
index 000000000000..3427e0770156
--- /dev/null
+++ b/tools/perf/python/gecko.py
@@ -0,0 +1,411 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+"""
+gecko.py - Convert perf record output to Firefox's gecko profile format
+"""
+from __future__ import annotations
+
+import argparse
+import functools
+import json
+import os
+import sys
+import tempfile
+import threading
+import urllib.parse
+import uuid
+import webbrowser
+from dataclasses import dataclass, field
+from http.server import HTTPServer, SimpleHTTPRequestHandler
+from typing import Dict, List, NamedTuple, Optional, Tuple
+
+import perf
+
+
+# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L156
+class Frame(NamedTuple):
+ """A single stack frame in the gecko profile format."""
+ string_id: int
+ relevantForJS: bool
+ innerWindowID: int
+ implementation: None
+ optimizations: None
+ line: None
+ column: None
+ category: int
+ subcategory: Optional[int]
+
+
+# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L216
+class Stack(NamedTuple):
+ """A single stack in the gecko profile format."""
+ prefix_id: Optional[int]
+ frame_id: int
+
+
+# https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L90
+class Sample(NamedTuple):
+ """A single sample in the gecko profile format."""
+ stack_id: Optional[int]
+ time_ms: float
+ responsiveness: int
+
+
+@dataclass
+class Tables:
+ """Interned tables for the gecko profile format."""
+ frame_table: List[Frame] = field(default_factory=list)
+ string_table: List[str] = field(default_factory=list)
+ string_map: Dict[str, int] = field(default_factory=dict)
+ stack_table: List[Stack] = field(default_factory=list)
+ stack_map: Dict[Tuple[Optional[int], int], int] = field(default_factory=dict)
+ frame_map: Dict[str, int] = field(default_factory=dict)
+
+
+@dataclass
+class Thread:
+ """A builder for a profile of the thread."""
+ comm: str
+ pid: int
+ tid: int
+ user_category: int
+ kernel_category: int
+ samples: List[Sample] = field(default_factory=list)
+ tables: Tables = field(default_factory=Tables)
+
+ def _intern_stack(self, frame_id: int, prefix_id: Optional[int]) -> int:
+ """Gets a matching stack, or saves the new stack. Returns a Stack ID."""
+ key = (prefix_id, frame_id)
+ stack_id = self.tables.stack_map.get(key)
+ if stack_id is None:
+ stack_id = len(self.tables.stack_table)
+ self.tables.stack_table.append(Stack(prefix_id=prefix_id, frame_id=frame_id))
+ self.tables.stack_map[key] = stack_id
+ return stack_id
+
+ def _intern_string(self, string: str) -> int:
+ """Gets a matching string, or saves the new string. Returns a String ID."""
+ string_id = self.tables.string_map.get(string)
+ if string_id is not None:
+ return string_id
+ string_id = len(self.tables.string_table)
+ self.tables.string_table.append(string)
+ self.tables.string_map[string] = string_id
+ return string_id
+
+ def _intern_frame(self, frame_str: str) -> int:
+ """Gets a matching stack frame, or saves the new frame. Returns a Frame ID."""
+ frame_id = self.tables.frame_map.get(frame_str)
+ if frame_id is not None:
+ return frame_id
+ frame_id = len(self.tables.frame_table)
+ self.tables.frame_map[frame_str] = frame_id
+ string_id = self._intern_string(frame_str)
+
+ category = self.user_category
+ if (frame_str.find('kallsyms') != -1 or
+ frame_str.find('/vmlinux') != -1 or
+ frame_str.endswith('.ko)')):
+ category = self.kernel_category
+
+ self.tables.frame_table.append(Frame(
+ string_id=string_id,
+ relevantForJS=False,
+ innerWindowID=0,
+ implementation=None,
+ optimizations=None,
+ line=None,
+ column=None,
+ category=category,
+ subcategory=None,
+ ))
+ return frame_id
+
+ def add_sample(self, comm: str, stack: List[str], time_ms: float) -> None:
+ """Add a timestamped stack trace sample to the thread builder."""
+ if self.comm != comm:
+ self.comm = comm
+
+ prefix_stack_id: Optional[int] = None
+ for frame in stack:
+ frame_id = self._intern_frame(frame)
+ prefix_stack_id = self._intern_stack(frame_id, prefix_stack_id)
+
+ if prefix_stack_id is not None:
+ self.samples.append(Sample(stack_id=prefix_stack_id,
+ time_ms=time_ms,
+ responsiveness=0))
+
+ def to_json_dict(self) -> Dict:
+ """Converts current Thread to GeckoThread JSON format."""
+ return {
+ "tid": self.tid,
+ "pid": self.pid,
+ "name": self.comm,
+ "markers": {
+ "schema": {
+ "name": 0,
+ "startTime": 1,
+ "endTime": 2,
+ "phase": 3,
+ "category": 4,
+ "data": 5,
+ },
+ "data": [],
+ },
+ "samples": {
+ "schema": {
+ "stack": 0,
+ "time": 1,
+ "responsiveness": 2,
+ },
+ "data": self.samples
+ },
+ "frameTable": {
+ "schema": {
+ "location": 0,
+ "relevantForJS": 1,
+ "innerWindowID": 2,
+ "implementation": 3,
+ "optimizations": 4,
+ "line": 5,
+ "column": 6,
+ "category": 7,
+ "subcategory": 8,
+ },
+ "data": self.tables.frame_table,
+ },
+ "stackTable": {
+ "schema": {
+ "prefix": 0,
+ "frame": 1,
+ },
+ "data": self.tables.stack_table,
+ },
+ "stringTable": self.tables.string_table,
+ "registerTime": 0,
+ "unregisterTime": None,
+ "processType": "default",
+ }
+
+
+class CORSRequestHandler(SimpleHTTPRequestHandler):
+ """Enable CORS for requests from profiler.firefox.com."""
+ def __init__(self, *args, allowed_file: Optional[str] = None, **kwargs):
+ self.allowed_file = allowed_file
+ super().__init__(*args, **kwargs)
+
+ def end_headers(self):
+ self.send_header('Access-Control-Allow-Origin', 'https://profiler.firefox.com')
+ super().end_headers()
+
+ def do_GET(self):
+ if self.allowed_file and self.path.split('?')[0] != f'/{self.allowed_file}':
+ self.send_error(403, "Access denied")
+ return
+ super().do_GET()
+
+ def list_directory(self, path):
+ self.send_error(403, "Directory listing forbidden")
+ return None
+
+
+@dataclass
+class CategoryData:
+ """Category configuration for the gecko profile."""
+ user_index: int = 0
+ kernel_index: int = 1
+ categories: List[Dict] = field(default_factory=list)
+
+
+class GeckoCLI:
+ """Command-line interface for converting perf data to Gecko format."""
+ def __init__(self, args: argparse.Namespace) -> None:
+ self.args = args
+ self.tid_to_thread: Dict[int, Thread] = {}
+ self.start_time_ms: Optional[float] = None
+ self.session: Optional[perf.session] = None
+ self.product = args.product
+ self.cat_data = CategoryData(
+ categories=[
+ {
+ "name": 'User',
+ "color": args.user_color,
+ "subcategories": ['Other']
+ },
+ {
+ "name": 'Kernel',
+ "color": args.kernel_color,
+ "subcategories": ['Other']
+ },
+ ]
+ )
+
+ def process_event(self, sample) -> None:
+ """Process a single perf sample event."""
+ if self.args.event_name and self.args.event_name not in str(sample.evsel):
+ return
+
+ # sample_time is in nanoseconds. Gecko wants milliseconds.
+ time_ms = sample.sample_time / 1000000.0
+ pid = sample.sample_pid
+ tid = sample.sample_tid
+
+ if self.start_time_ms is None:
+ self.start_time_ms = time_ms
+
+ try:
+ thread_info = self.session.find_thread(sample.sample_pid, sample.sample_tid) if self.session else None
+ comm = (thread_info.comm() if thread_info is not None else None) or "[unknown]"
+ except AttributeError:
+ comm = "[unknown]"
+
+ stack = []
+ callchain = sample.callchain
+ if callchain:
+ for entry in callchain:
+ symbol = entry.symbol or "[unknown]"
+ dso = entry.dso or "[unknown]"
+ stack.append(f"{symbol} (in {dso})")
+ # Reverse because Gecko wants root first.
+ stack.reverse()
+ else:
+ symbol = sample.symbol or "[unknown]"
+ dso = sample.dso or "[unknown]"
+ stack.append(f"{symbol} (in {dso})")
+
+ thread = self.tid_to_thread.get(tid)
+ if thread is None:
+ thread = Thread(comm=comm, pid=pid, tid=tid,
+ user_category=self.cat_data.user_index,
+ kernel_category=self.cat_data.kernel_index)
+ self.tid_to_thread[tid] = thread
+ thread.add_sample(comm=comm, stack=stack, time_ms=time_ms)
+
+ def run(self) -> None:
+ """Run the conversion process."""
+ input_file = self.args.input or "perf.data"
+ if input_file != "-" and not os.path.exists(input_file):
+ print(f"Error: {input_file} not found.", file=sys.stderr)
+ sys.exit(1)
+
+ try:
+ self.session = perf.session(perf.data(input_file), sample=self.process_event)
+ except (OSError, RuntimeError, ValueError) as e:
+ print(f"Error opening session: {e}", file=sys.stderr)
+ sys.exit(1)
+
+ if self.session:
+ try:
+ self.session.process_events()
+ finally:
+ # Break the reference cycle between perf.session and self.process_event
+ # because perf.session lacks cyclic GC support (tp_traverse).
+ self.session = None
+
+ threads = [t.to_json_dict() for t in self.tid_to_thread.values()]
+
+ gecko_profile = {
+ "meta": {
+ "interval": 1,
+ "processType": 0,
+ "product": self.product,
+ "stackwalk": 1,
+ "debug": 0,
+ "gcpoison": 0,
+ "asyncstack": 1,
+ "startTime": self.start_time_ms or 0,
+ "shutdownTime": None,
+ "version": 24,
+ "presymbolicated": True,
+ "categories": self.cat_data.categories,
+ "markerSchema": [],
+ },
+ "libs": [],
+ "threads": threads,
+ "processes": [],
+ "pausedRanges": [],
+ }
+
+ output_file = self.args.save_only
+ if output_file is None:
+ self._write_and_launch(gecko_profile)
+ else:
+ print(f'[ perf gecko: Captured and wrote into {output_file} ]')
+ with open(output_file, 'w', encoding='utf-8') as f:
+ json.dump(gecko_profile, f, indent=2)
+
+ def _write_and_launch(self, profile: Dict) -> None:
+ """Write the profile to a file and launch the Firefox profiler."""
+ print("Starting Firefox Profiler on your default browser...")
+
+ with tempfile.TemporaryDirectory() as tmp_dir_name:
+ profile_name = f'gecko_profile_{uuid.uuid4().hex}.json'
+ filename = os.path.join(tmp_dir_name, profile_name)
+
+ with open(filename, 'w', encoding='utf-8') as f:
+ json.dump(profile, f, indent=2)
+
+ handler = functools.partial(
+ CORSRequestHandler, directory=tmp_dir_name, allowed_file=profile_name
+ )
+ try:
+ httpd = HTTPServer(('127.0.0.1', 0), handler)
+ except OSError as e:
+ print(f"Error starting HTTP server: {e}", file=sys.stderr)
+ sys.exit(1)
+
+ port = httpd.server_port
+
+ def start_server():
+ httpd.serve_forever()
+
+ thread = threading.Thread(target=start_server, daemon=True)
+ thread.start()
+
+ safe_string = urllib.parse.quote_plus(f'http://127.0.0.1:{port}/{profile_name}')
+ url = f'https://profiler.firefox.com/from-url/{safe_string}'
+ print(f"Please open the following URL in your browser to view the profile:\n\n{url}\n")
+ launch_html = os.path.join(tmp_dir_name, 'launch.html')
+ with open(launch_html, 'w', encoding='utf-8') as f:
+ f.write(f'
'
+ f''
+ f''
+ f'