[Devel] [PATCH] selftests/perf_events: fix sigtrap_threads tests fail with -O2

Eva Kurchatova eva.kurchatova at virtuozzo.com
Fri May 8 15:39:31 MSK 2026


The enable_event and modify_and_enable_event tests fail when compiled
with -O2:

    Expected ctx.signal_count (5) == NUM_THREADS + 1 (6)

The test sequence is:

    EXPECT_EQ(ctx.signal_count, NUM_THREADS);  /* compiler loads into reg */
    ctx.iterate_on = 0;                        /* triggers SIGTRAP, handler
                                                increments signal_count */
    EXPECT_EQ(ctx.signal_count, NUM_THREADS + 1);  /* stale cached value */

As SIGTRAP handler used atomics for signal_count and tids_want_signal,
test now correctly uses atomic access everywhere for those variables.

Additionally, use READ_ONCE/WRITE_ONCE macros for idempotent access to
iterate_on watchpoint and prevent reordering / misoptimization

https://virtuozzo.atlassian.net/browse/VSTOR-127669
Feature: fix selftests

Signed-off-by: Konstantin Khorenko <khorenko at virtuozzo.com>
Signed-off-by: Eva Kurchatova <eva.kurchatova at virtuozzo.com>
---
 .../selftests/perf_events/sigtrap_threads.c   | 42 ++++++++++---------
 1 file changed, 23 insertions(+), 19 deletions(-)

diff --git a/tools/testing/selftests/perf_events/sigtrap_threads.c b/tools/testing/selftests/perf_events/sigtrap_threads.c
index d1d8483ac628..ffc65d6f1c2b 100644
--- a/tools/testing/selftests/perf_events/sigtrap_threads.c
+++ b/tools/testing/selftests/perf_events/sigtrap_threads.c
@@ -35,11 +35,15 @@
 
 #define NUM_THREADS 5
 
+/* Force watchpoint access to actually occur */
+#define READ_ONCE(x)       (*(volatile typeof(x) *)&(x))
+#define WRITE_ONCE(x, val) (*(volatile typeof(x) *)&(x) = (val))
+
 /* Data shared between test body, threads, and signal handler. */
 static struct {
 	int tids_want_signal;		/* Which threads still want a signal. */
 	int signal_count;		/* Sanity check number of signals received. */
-	volatile int iterate_on;	/* Variable to set breakpoint on. */
+	int iterate_on;			/* Variable to set breakpoint on. */
 	siginfo_t first_siginfo;	/* First observed siginfo_t. */
 } ctx;
 
@@ -94,14 +98,14 @@ static void *test_thread(void *arg)
 	pthread_barrier_wait(barrier);
 
 	__atomic_fetch_add(&ctx.tids_want_signal, tid, __ATOMIC_RELAXED);
-	iter = ctx.iterate_on; /* read */
+	iter = READ_ONCE(ctx.iterate_on);
 	if (iter >= 0) {
 		for (i = 0; i < iter - 1; i++) {
 			__atomic_fetch_add(&ctx.tids_want_signal, tid, __ATOMIC_RELAXED);
-			ctx.iterate_on = iter; /* idempotent write */
+			WRITE_ONCE(ctx.iterate_on, iter); /* idempotent write */
 		}
 	} else {
-		while (ctx.iterate_on);
+		while (READ_ONCE(ctx.iterate_on));
 	}
 
 	return NULL;
@@ -159,8 +163,8 @@ static void run_test_threads(struct __test_metadata *_metadata,
 TEST_F(sigtrap_threads, remain_disabled)
 {
 	run_test_threads(_metadata, self);
-	EXPECT_EQ(ctx.signal_count, 0);
-	EXPECT_NE(ctx.tids_want_signal, 0);
+	EXPECT_EQ(__atomic_load_n(&ctx.signal_count, __ATOMIC_RELAXED), 0);
+	EXPECT_NE(__atomic_load_n(&ctx.tids_want_signal, __ATOMIC_RELAXED), 0);
 }
 
 TEST_F(sigtrap_threads, enable_event)
@@ -168,15 +172,15 @@ TEST_F(sigtrap_threads, enable_event)
 	EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0);
 	run_test_threads(_metadata, self);
 
-	EXPECT_EQ(ctx.signal_count, NUM_THREADS);
-	EXPECT_EQ(ctx.tids_want_signal, 0);
+	EXPECT_EQ(__atomic_load_n(&ctx.signal_count, __ATOMIC_RELAXED), NUM_THREADS);
+	EXPECT_EQ(__atomic_load_n(&ctx.tids_want_signal, __ATOMIC_RELAXED), 0);
 	EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
 	EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
 	EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on, 0));
 
 	/* Check enabled for parent. */
-	ctx.iterate_on = 0;
-	EXPECT_EQ(ctx.signal_count, NUM_THREADS + 1);
+	WRITE_ONCE(ctx.iterate_on, 0);
+	EXPECT_EQ(__atomic_load_n(&ctx.signal_count, __ATOMIC_RELAXED), NUM_THREADS + 1);
 }
 
 /* Test that modification propagates to all inherited events. */
@@ -187,28 +191,28 @@ TEST_F(sigtrap_threads, modify_and_enable_event)
 	EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &new_attr), 0);
 	run_test_threads(_metadata, self);
 
-	EXPECT_EQ(ctx.signal_count, NUM_THREADS);
-	EXPECT_EQ(ctx.tids_want_signal, 0);
+	EXPECT_EQ(__atomic_load_n(&ctx.signal_count, __ATOMIC_RELAXED), NUM_THREADS);
+	EXPECT_EQ(__atomic_load_n(&ctx.tids_want_signal, __ATOMIC_RELAXED), 0);
 	EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
 	EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
 	EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on, 42));
 
 	/* Check enabled for parent. */
-	ctx.iterate_on = 0;
-	EXPECT_EQ(ctx.signal_count, NUM_THREADS + 1);
+	WRITE_ONCE(ctx.iterate_on, 0);
+	EXPECT_EQ(__atomic_load_n(&ctx.signal_count, __ATOMIC_RELAXED), NUM_THREADS + 1);
 }
 
 /* Stress test event + signal handling. */
 TEST_F(sigtrap_threads, signal_stress)
 {
-	ctx.iterate_on = 3000;
+	WRITE_ONCE(ctx.iterate_on, 3000);
 
 	EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0);
 	run_test_threads(_metadata, self);
 	EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_DISABLE, 0), 0);
 
-	EXPECT_EQ(ctx.signal_count, NUM_THREADS * ctx.iterate_on);
-	EXPECT_EQ(ctx.tids_want_signal, 0);
+	EXPECT_EQ(__atomic_load_n(&ctx.signal_count, __ATOMIC_RELAXED), NUM_THREADS * READ_ONCE(ctx.iterate_on));
+	EXPECT_EQ(__atomic_load_n(&ctx.tids_want_signal, __ATOMIC_RELAXED), 0);
 	EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
 	EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
 	EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on, 0));
@@ -219,7 +223,7 @@ TEST_F(sigtrap_threads, signal_stress_with_disable)
 	const int target_count = NUM_THREADS * 3000;
 	int i;
 
-	ctx.iterate_on = -1;
+	WRITE_ONCE(ctx.iterate_on, -1);
 
 	EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0);
 	pthread_barrier_wait(&self->barrier);
@@ -227,7 +231,7 @@ TEST_F(sigtrap_threads, signal_stress_with_disable)
 		EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_DISABLE, 0), 0);
 		EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0);
 	}
-	ctx.iterate_on = 0;
+	WRITE_ONCE(ctx.iterate_on, 0);
 	for (i = 0; i < NUM_THREADS; i++)
 		ASSERT_EQ(pthread_join(self->threads[i], NULL), 0);
 	EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_DISABLE, 0), 0);
-- 
2.54.0



More information about the Devel mailing list