1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Test for perf events with SIGTRAP across all threads.
4 *
5 * Copyright (C) 2021, Google LLC.
6 */
7
8#define _GNU_SOURCE
9
10/* We need the latest siginfo from the kernel repo. */
11#include <sys/types.h>
12#include <asm/siginfo.h>
13#define __have_siginfo_t 1
14#define __have_sigval_t 1
15#define __have_sigevent_t 1
16#define __siginfo_t_defined
17#define __sigval_t_defined
18#define __sigevent_t_defined
19#define _BITS_SIGINFO_CONSTS_H 1
20#define _BITS_SIGEVENT_CONSTS_H 1
21
22#include <stdbool.h>
23#include <stddef.h>
24#include <stdint.h>
25#include <stdio.h>
26#include <linux/hw_breakpoint.h>
27#include <linux/perf_event.h>
28#include <pthread.h>
29#include <signal.h>
30#include <sys/ioctl.h>
31#include <sys/syscall.h>
32#include <unistd.h>
33
34#include "../kselftest_harness.h"
35
36#define NUM_THREADS 5
37
38/* Data shared between test body, threads, and signal handler. */
39static struct {
40 int tids_want_signal; /* Which threads still want a signal. */
41 int signal_count; /* Sanity check number of signals received. */
42 volatile int iterate_on; /* Variable to set breakpoint on. */
43 siginfo_t first_siginfo; /* First observed siginfo_t. */
44} ctx;
45
46/* Unique value to check si_perf_data is correctly set from perf_event_attr::sig_data. */
47#define TEST_SIG_DATA(addr, id) (~(unsigned long)(addr) + id)
48
49static struct perf_event_attr make_event_attr(bool enabled, volatile void *addr,
50 unsigned long id)
51{
52 struct perf_event_attr attr = {
53 .type = PERF_TYPE_BREAKPOINT,
54 .size = sizeof(attr),
55 .sample_period = 1,
56 .disabled = !enabled,
57 .bp_addr = (unsigned long)addr,
58 .bp_type = HW_BREAKPOINT_RW,
59 .bp_len = HW_BREAKPOINT_LEN_1,
60 .inherit = 1, /* Children inherit events ... */
61 .inherit_thread = 1, /* ... but only cloned with CLONE_THREAD. */
62 .remove_on_exec = 1, /* Required by sigtrap. */
63 .sigtrap = 1, /* Request synchronous SIGTRAP on event. */
64 .sig_data = TEST_SIG_DATA(addr, id),
65 .exclude_kernel = 1, /* To allow */
66 .exclude_hv = 1, /* running as !root */
67 };
68 return attr;
69}
70
71static void sigtrap_handler(int signum, siginfo_t *info, void *ucontext)
72{
73 if (info->si_code != TRAP_PERF) {
74 fprintf(stderr, "%s: unexpected si_code %d\n", __func__, info->si_code);
75 return;
76 }
77
78 /*
79 * The data in siginfo_t we're interested in should all be the same
80 * across threads.
81 */
82 if (!__atomic_fetch_add(&ctx.signal_count, 1, __ATOMIC_RELAXED))
83 ctx.first_siginfo = *info;
84 __atomic_fetch_sub(&ctx.tids_want_signal, syscall(__NR_gettid), __ATOMIC_RELAXED);
85}
86
87static void *test_thread(void *arg)
88{
89 pthread_barrier_t *barrier = (pthread_barrier_t *)arg;
90 pid_t tid = syscall(__NR_gettid);
91 int iter;
92 int i;
93
94 pthread_barrier_wait(barrier);
95
96 __atomic_fetch_add(&ctx.tids_want_signal, tid, __ATOMIC_RELAXED);
97 iter = ctx.iterate_on; /* read */
98 if (iter >= 0) {
99 for (i = 0; i < iter - 1; i++) {
100 __atomic_fetch_add(&ctx.tids_want_signal, tid, __ATOMIC_RELAXED);
101 ctx.iterate_on = iter; /* idempotent write */
102 }
103 } else {
104 while (ctx.iterate_on);
105 }
106
107 return NULL;
108}
109
110FIXTURE(sigtrap_threads)
111{
112 struct sigaction oldact;
113 pthread_t threads[NUM_THREADS];
114 pthread_barrier_t barrier;
115 int fd;
116};
117
118FIXTURE_SETUP(sigtrap_threads)
119{
120 struct perf_event_attr attr = make_event_attr(enabled: false, addr: &ctx.iterate_on, id: 0);
121 struct sigaction action = {};
122 int i;
123
124 memset(&ctx, 0, sizeof(ctx));
125
126 /* Initialize sigtrap handler. */
127 action.sa_flags = SA_SIGINFO | SA_NODEFER;
128 action.sa_sigaction = sigtrap_handler;
129 sigemptyset(set: &action.sa_mask);
130 ASSERT_EQ(sigaction(SIGTRAP, &action, &self->oldact), 0);
131
132 /* Initialize perf event. */
133 self->fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, PERF_FLAG_FD_CLOEXEC);
134 ASSERT_NE(self->fd, -1);
135
136 /* Spawn threads inheriting perf event. */
137 pthread_barrier_init(&self->barrier, NULL, NUM_THREADS + 1);
138 for (i = 0; i < NUM_THREADS; i++)
139 ASSERT_EQ(pthread_create(&self->threads[i], NULL, test_thread, &self->barrier), 0);
140}
141
142FIXTURE_TEARDOWN(sigtrap_threads)
143{
144 pthread_barrier_destroy(&self->barrier);
145 close(self->fd);
146 sigaction(SIGTRAP, &self->oldact, NULL);
147}
148
149static void run_test_threads(struct __test_metadata *_metadata,
150 FIXTURE_DATA(sigtrap_threads) *self)
151{
152 int i;
153
154 pthread_barrier_wait(&self->barrier);
155 for (i = 0; i < NUM_THREADS; i++)
156 ASSERT_EQ(pthread_join(self->threads[i], NULL), 0);
157}
158
159TEST_F(sigtrap_threads, remain_disabled)
160{
161 run_test_threads(_metadata, self);
162 EXPECT_EQ(ctx.signal_count, 0);
163 EXPECT_NE(ctx.tids_want_signal, 0);
164}
165
166TEST_F(sigtrap_threads, enable_event)
167{
168 EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0);
169 run_test_threads(_metadata, self);
170
171 EXPECT_EQ(ctx.signal_count, NUM_THREADS);
172 EXPECT_EQ(ctx.tids_want_signal, 0);
173 EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
174 EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
175 EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on, 0));
176
177 /* Check enabled for parent. */
178 ctx.iterate_on = 0;
179 EXPECT_EQ(ctx.signal_count, NUM_THREADS + 1);
180}
181
182/* Test that modification propagates to all inherited events. */
183TEST_F(sigtrap_threads, modify_and_enable_event)
184{
185 struct perf_event_attr new_attr = make_event_attr(enabled: true, addr: &ctx.iterate_on, id: 42);
186
187 EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &new_attr), 0);
188 run_test_threads(_metadata, self);
189
190 EXPECT_EQ(ctx.signal_count, NUM_THREADS);
191 EXPECT_EQ(ctx.tids_want_signal, 0);
192 EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
193 EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
194 EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on, 42));
195
196 /* Check enabled for parent. */
197 ctx.iterate_on = 0;
198 EXPECT_EQ(ctx.signal_count, NUM_THREADS + 1);
199}
200
201/* Stress test event + signal handling. */
202TEST_F(sigtrap_threads, signal_stress)
203{
204 ctx.iterate_on = 3000;
205
206 EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0);
207 run_test_threads(_metadata, self);
208 EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_DISABLE, 0), 0);
209
210 EXPECT_EQ(ctx.signal_count, NUM_THREADS * ctx.iterate_on);
211 EXPECT_EQ(ctx.tids_want_signal, 0);
212 EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
213 EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
214 EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on, 0));
215}
216
217TEST_F(sigtrap_threads, signal_stress_with_disable)
218{
219 const int target_count = NUM_THREADS * 3000;
220 int i;
221
222 ctx.iterate_on = -1;
223
224 EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0);
225 pthread_barrier_wait(&self->barrier);
226 while (__atomic_load_n(&ctx.signal_count, __ATOMIC_RELAXED) < target_count) {
227 EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_DISABLE, 0), 0);
228 EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0);
229 }
230 ctx.iterate_on = 0;
231 for (i = 0; i < NUM_THREADS; i++)
232 ASSERT_EQ(pthread_join(self->threads[i], NULL), 0);
233 EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_DISABLE, 0), 0);
234
235 EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
236 EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
237 EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on, 0));
238}
239
240TEST_HARNESS_MAIN
241

source code of linux/tools/testing/selftests/perf_events/sigtrap_threads.c