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. */ |
39 | static 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 | |
49 | static 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 | |
71 | static 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 | |
87 | static 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 | |
110 | FIXTURE(sigtrap_threads) |
111 | { |
112 | struct sigaction oldact; |
113 | pthread_t threads[NUM_THREADS]; |
114 | pthread_barrier_t barrier; |
115 | int fd; |
116 | }; |
117 | |
118 | FIXTURE_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 | |
142 | FIXTURE_TEARDOWN(sigtrap_threads) |
143 | { |
144 | pthread_barrier_destroy(&self->barrier); |
145 | close(self->fd); |
146 | sigaction(SIGTRAP, &self->oldact, NULL); |
147 | } |
148 | |
149 | static 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 | |
159 | TEST_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 | |
166 | TEST_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. */ |
183 | TEST_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. */ |
202 | TEST_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 | |
217 | TEST_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 | |
240 | TEST_HARNESS_MAIN |
241 | |