From: Yunseong Kim <ysk@kzalloc.com>
To: Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@redhat.com>,
Arnaldo Carvalho de Melo <acme@kernel.org>,
Namhyung Kim <namhyung@kernel.org>,
Mark Rutland <mark.rutland@arm.com>,
Alexander Shishkin <alexander.shishkin@linux.intel.com>,
Jiri Olsa <jolsa@kernel.org>, Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>,
Liang Kan <kan.liang@linux.intel.com>,
James Clark <james.clark@linaro.org>,
Collin Funk <collin.funk1@gmail.com>,
Ravi Bangoria <ravi.bangoria@amd.com>,
linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org,
Yunseong Kim <ysk@kzalloc.com>
Subject: [PATCH] perf test: Add test case for event group throttling with inactive events
Date: Wed, 13 Aug 2025 16:32:52 +0000 [thread overview]
Message-ID: <20250813163251.490451-2-ysk@kzalloc.com> (raw)
A recent UBSAN shift-out-of-bounds report was identified when throttling
event groups that included inactive (PERF_EVENT_STATE_OFF) events.
This occurred because pmu->start()/stop() could be called on these events,
potentially leaving event->hw.idx at -1. This leads to undefined behavior
when PMU code later uses this negative index as a shift exponent.
The issue need to ensuring perf_event_throttle() and
perf_event_unthrottle() skip inactive events entirely.
Introduce a new perf test suite, "event group throttle", to verify this
fix and prevent regressions.
The test sets up a scenario designed to trigger frequent throttling:
1. A parent event (leader) is created with sample_period = 1.
2. A child event is created in the same group but initialized with
disabled = 1 (inactive).
A process opens these events and runs in a tight loop. The frequent
sampling of the leader causes the entire group, including the inactive
child event, to be rapidly throttled and unthrottled by the kernel.
The test monitors /dev/kmsg during execution, looking for "UBSAN",
"Invalid PMEV" "WARNING:", or "BUG:" messages.
To ensure robustness and avoid false positives from unrelated prior kernel
messages, the test opens /dev/kmsg and uses lseek(SEEK_END) to skip all
existing log entries before starting the test loop. If /dev/kmsg cannot be
accessed or seeked (e.g., lack of CAP_SYSLOG), the test handles it
appropriately by skipping or failing.
Related Reproducer by Mark Rutland
Link: https://lore.kernel.org/lkml/aIEePonPatjNrJVk@J2N7QTR9R3/
Related Kernel Fix
Link: https://lore.kernel.org/lkml/20250812012722.127646-1-ysk@kzalloc.com/
Cc: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Yunseong Kim <ysk@kzalloc.com>
---
tools/perf/tests/Build | 1 +
tools/perf/tests/builtin-test.c | 1 +
tools/perf/tests/event_group_throttle.c | 132 ++++++++++++++++++++++++
tools/perf/tests/tests.h | 1 +
4 files changed, 135 insertions(+)
create mode 100644 tools/perf/tests/event_group_throttle.c
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 3e8394be15ae..e22e2f285500 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -70,6 +70,7 @@ perf-test-y += util.o
perf-test-y += hwmon_pmu.o
perf-test-y += tool_pmu.o
perf-test-y += subcmd-help.o
+perf-test-y += event_group_throttle.o
ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc))
perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 85142dfb3e01..d302bf9d1535 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -141,6 +141,7 @@ static struct test_suite *generic_tests[] = {
&suite__symbols,
&suite__util,
&suite__subcmd_help,
+ &suite__event_group_throttle,
NULL,
};
diff --git a/tools/perf/tests/event_group_throttle.c b/tools/perf/tests/event_group_throttle.c
new file mode 100644
index 000000000000..7d5191d7e812
--- /dev/null
+++ b/tools/perf/tests/event_group_throttle.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <linux/perf_event.h>
+#include "perf-sys.h"
+#include "tests.h"
+#include "debug.h"
+
+static struct perf_event_attr attr_parent = {
+ .type = PERF_TYPE_HARDWARE,
+ .size = sizeof(attr_parent),
+ .config = PERF_COUNT_HW_CPU_CYCLES,
+ .sample_period = 1,
+ .exclude_kernel = 1,
+};
+
+static struct perf_event_attr attr_child = {
+ .type = PERF_TYPE_HARDWARE,
+ .size = sizeof(attr_child),
+ .config = PERF_COUNT_HW_CPU_CYCLES,
+ .exclude_kernel = 1,
+ .disabled = 1,
+};
+
+static pid_t run_event_group_throttle(void)
+{
+ pid_t pid = fork();
+
+ if (pid == 0) {
+ int parent, child;
+
+ parent = sys_perf_event_open(&attr_parent, 0, -1, -1, 0);
+ if (parent < 0) {
+ pr_debug("Unable to create event: %d\n", parent);
+ exit(-1);
+ }
+
+ child = sys_perf_event_open(&attr_child, 0, -1, parent, 0);
+ if (child < 0) {
+ pr_debug("Unable to create event: %d\n", child);
+ exit(-1);
+ }
+
+ for (;;)
+ asm("" ::: "memory");
+
+ _exit(0);
+ }
+ return pid;
+}
+
+static bool is_kmsg_err(int fd)
+{
+ char buf[1024];
+ ssize_t len;
+
+ while ((len = read(fd, buf, sizeof(buf) - 1)) > 0) {
+ buf[len] = '\0';
+
+ if (strstr(buf, "UBSAN") || strstr(buf, "WARNING:") ||
+ strstr(buf, "BUG:") || strstr(buf, "Invalid PMEV")) {
+ pr_debug("Kernel log error detected: %s", buf);
+ return true;
+ }
+ }
+
+ if (len < 0 && errno != EAGAIN) {
+ pr_debug("Error reading /dev/kmsg: %s\n", strerror(errno));
+ return true;
+ }
+
+ return false;
+}
+
+static int test__event_group_throttle(struct test_suite *test __maybe_unused,
+ int subtest __maybe_unused)
+{
+ time_t start;
+ pid_t pid;
+ int fd;
+
+ fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ /*
+ * If /dev/kmsg cannot be opened (e.g. permission denied), skip the test
+ * as we cannot verify the absence of kernel errors.
+ */
+ pr_debug("Failed to open /dev/kmsg: %s. Skipping test.\n", strerror(errno));
+ return TEST_SKIP;
+ }
+
+ /*
+ * Seek to the end to ignore past events (like EFI boot warnings).
+ * This typically requires CAP_SYSLOG.
+ */
+ if (lseek(fd, 0, SEEK_END) < 0) {
+ pr_debug("Failed to seek to end of /dev/kmsg: %s\n", strerror(errno));
+ return TEST_FAIL;
+ }
+
+ start = time(NULL);
+ do {
+ pr_debug("Starting event group throttling...\n");
+ pid = run_event_group_throttle();
+
+ sleep(8);
+
+ pr_debug("event group throttler(PID=%d)\n", pid);
+ kill(pid, SIGKILL);
+ waitpid(pid, NULL, 0);
+
+ /* Check for errors during the run */
+ if (is_kmsg_err(fd)) {
+ close(fd);
+ return TEST_FAIL;
+ }
+ } while (time(NULL) - start < 10);
+
+ return TEST_OK;
+}
+
+DEFINE_SUITE("event group throttle", event_group_throttle);
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 97e62db8764a..031856a710b2 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -179,6 +179,7 @@ DECLARE_SUITE(event_groups);
DECLARE_SUITE(symbols);
DECLARE_SUITE(util);
DECLARE_SUITE(subcmd_help);
+DECLARE_SUITE(event_group_throttle);
/*
* PowerPC and S390 do not support creation of instruction breakpoints using the
--
2.50.0
next reply other threads:[~2025-08-13 16:33 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-13 16:32 Yunseong Kim [this message]
2025-08-14 17:10 ` [PATCH] perf test: Add test case for event group throttling with inactive events Liang, Kan
2025-08-14 20:17 ` Yunseong Kim
2025-08-14 20:06 ` Namhyung Kim
2025-08-14 20:23 ` Yunseong Kim
2025-08-15 20:07 ` Namhyung Kim
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250813163251.490451-2-ysk@kzalloc.com \
--to=ysk@kzalloc.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=alexander.shishkin@linux.intel.com \
--cc=collin.funk1@gmail.com \
--cc=irogers@google.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=kan.liang@linux.intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
--cc=ravi.bangoria@amd.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.