1 // SPDX-License-Identifier: GPL-2.0
3 * Basic test for sigtrap support.
5 * Copyright (C) 2021, Google LLC.
11 #include <linux/hw_breakpoint.h>
12 #include <linux/string.h>
15 #include <sys/ioctl.h>
16 #include <sys/syscall.h>
23 #include "../perf-sys.h"
28 int tids_want_signal
; /* Which threads still want a signal. */
29 int signal_count
; /* Sanity check number of signals received. */
30 volatile int iterate_on
; /* Variable to set breakpoint on. */
31 siginfo_t first_siginfo
; /* First observed siginfo_t. */
34 #define TEST_SIG_DATA (~(unsigned long)(&ctx.iterate_on))
36 static struct perf_event_attr
make_event_attr(void)
38 struct perf_event_attr attr
= {
39 .type
= PERF_TYPE_BREAKPOINT
,
43 .bp_addr
= (unsigned long)&ctx
.iterate_on
,
44 .bp_type
= HW_BREAKPOINT_RW
,
45 .bp_len
= HW_BREAKPOINT_LEN_1
,
46 .inherit
= 1, /* Children inherit events ... */
47 .inherit_thread
= 1, /* ... but only cloned with CLONE_THREAD. */
48 .remove_on_exec
= 1, /* Required by sigtrap. */
49 .sigtrap
= 1, /* Request synchronous SIGTRAP on event. */
50 .sig_data
= TEST_SIG_DATA
,
51 .exclude_kernel
= 1, /* To allow */
52 .exclude_hv
= 1, /* running as !root */
60 static struct btf
*btf
;
62 static bool btf__available(void)
65 btf
= btf__load_vmlinux_btf();
70 static void btf__exit(void)
76 static const struct btf_member
*__btf_type__find_member_by_name(int type_id
, const char *member_name
)
78 const struct btf_type
*t
= btf__type_by_id(btf
, type_id
);
79 const struct btf_member
*m
;
82 for (i
= 0, m
= btf_members(t
); i
< btf_vlen(t
); i
++, m
++) {
83 const char *current_member_name
= btf__name_by_offset(btf
, m
->name_off
);
84 if (!strcmp(current_member_name
, member_name
))
91 static bool attr_has_sigtrap(void)
95 if (!btf__available()) {
96 /* should be an old kernel */
100 id
= btf__find_by_name_kind(btf
, "perf_event_attr", BTF_KIND_STRUCT
);
104 return __btf_type__find_member_by_name(id
, "sigtrap") != NULL
;
107 static bool kernel_with_sleepable_spinlocks(void)
109 const struct btf_member
*member
;
110 const struct btf_type
*type
;
111 const char *type_name
;
114 if (!btf__available())
117 id
= btf__find_by_name_kind(btf
, "spinlock", BTF_KIND_STRUCT
);
121 // Only RT has a "lock" member for "struct spinlock"
122 member
= __btf_type__find_member_by_name(id
, "lock");
126 // But check its type as well
127 type
= btf__type_by_id(btf
, member
->type
);
128 if (!type
|| !btf_is_struct(type
))
131 type_name
= btf__name_by_offset(btf
, type
->name_off
);
132 return type_name
&& !strcmp(type_name
, "rt_mutex_base");
134 #else /* !HAVE_BPF_SKEL */
135 static bool attr_has_sigtrap(void)
137 struct perf_event_attr attr
= {
138 .type
= PERF_TYPE_SOFTWARE
,
139 .config
= PERF_COUNT_SW_DUMMY
,
140 .size
= sizeof(attr
),
141 .remove_on_exec
= 1, /* Required by sigtrap. */
142 .sigtrap
= 1, /* Request synchronous SIGTRAP on event. */
147 fd
= sys_perf_event_open(&attr
, 0, -1, -1, perf_event_open_cloexec_flag());
156 static bool kernel_with_sleepable_spinlocks(void)
161 static void btf__exit(void)
164 #endif /* HAVE_BPF_SKEL */
167 sigtrap_handler(int signum __maybe_unused
, siginfo_t
*info
, void *ucontext __maybe_unused
)
169 if (!__atomic_fetch_add(&ctx
.signal_count
, 1, __ATOMIC_RELAXED
))
170 ctx
.first_siginfo
= *info
;
171 __atomic_fetch_sub(&ctx
.tids_want_signal
, syscall(SYS_gettid
), __ATOMIC_RELAXED
);
174 static void *test_thread(void *arg
)
176 pthread_barrier_t
*barrier
= (pthread_barrier_t
*)arg
;
177 pid_t tid
= syscall(SYS_gettid
);
180 pthread_barrier_wait(barrier
);
182 __atomic_fetch_add(&ctx
.tids_want_signal
, tid
, __ATOMIC_RELAXED
);
183 for (i
= 0; i
< ctx
.iterate_on
- 1; i
++)
184 __atomic_fetch_add(&ctx
.tids_want_signal
, tid
, __ATOMIC_RELAXED
);
189 static int run_test_threads(pthread_t
*threads
, pthread_barrier_t
*barrier
)
193 pthread_barrier_wait(barrier
);
194 for (i
= 0; i
< NUM_THREADS
; i
++)
195 TEST_ASSERT_EQUAL("pthread_join() failed", pthread_join(threads
[i
], NULL
), 0);
200 static int run_stress_test(int fd
, pthread_t
*threads
, pthread_barrier_t
*barrier
)
202 int ret
, expected_sigtraps
;
204 ctx
.iterate_on
= 3000;
206 TEST_ASSERT_EQUAL("misfired signal?", ctx
.signal_count
, 0);
207 TEST_ASSERT_EQUAL("enable failed", ioctl(fd
, PERF_EVENT_IOC_ENABLE
, 0), 0);
208 ret
= run_test_threads(threads
, barrier
);
209 TEST_ASSERT_EQUAL("disable failed", ioctl(fd
, PERF_EVENT_IOC_DISABLE
, 0), 0);
211 expected_sigtraps
= NUM_THREADS
* ctx
.iterate_on
;
213 if (ctx
.signal_count
< expected_sigtraps
&& kernel_with_sleepable_spinlocks()) {
214 pr_debug("Expected %d sigtraps, got %d, running on a kernel with sleepable spinlocks.\n",
215 expected_sigtraps
, ctx
.signal_count
);
216 pr_debug("See https://lore.kernel.org/all/e368f2c848d77fbc8d259f44e2055fe469c219cf.camel@gmx.de/\n");
219 TEST_ASSERT_EQUAL("unexpected sigtraps", ctx
.signal_count
, expected_sigtraps
);
221 TEST_ASSERT_EQUAL("missing signals or incorrectly delivered", ctx
.tids_want_signal
, 0);
222 TEST_ASSERT_VAL("unexpected si_addr", ctx
.first_siginfo
.si_addr
== &ctx
.iterate_on
);
223 #if 0 /* FIXME: enable when libc's signal.h has si_perf_{type,data} */
224 TEST_ASSERT_EQUAL("unexpected si_perf_type", ctx
.first_siginfo
.si_perf_type
,
225 PERF_TYPE_BREAKPOINT
);
226 TEST_ASSERT_EQUAL("unexpected si_perf_data", ctx
.first_siginfo
.si_perf_data
,
233 static int test__sigtrap(struct test_suite
*test __maybe_unused
, int subtest __maybe_unused
)
235 struct perf_event_attr attr
= make_event_attr();
236 struct sigaction action
= {};
237 struct sigaction oldact
;
238 pthread_t threads
[NUM_THREADS
];
239 pthread_barrier_t barrier
;
240 char sbuf
[STRERR_BUFSIZE
];
241 int i
, fd
, ret
= TEST_FAIL
;
243 if (!BP_SIGNAL_IS_SUPPORTED
) {
244 pr_debug("Test not supported on this architecture");
248 pthread_barrier_init(&barrier
, NULL
, NUM_THREADS
+ 1);
250 action
.sa_flags
= SA_SIGINFO
| SA_NODEFER
;
251 action
.sa_sigaction
= sigtrap_handler
;
252 sigemptyset(&action
.sa_mask
);
253 if (sigaction(SIGTRAP
, &action
, &oldact
)) {
254 pr_debug("FAILED sigaction(): %s\n", str_error_r(errno
, sbuf
, sizeof(sbuf
)));
258 fd
= sys_perf_event_open(&attr
, 0, -1, -1, perf_event_open_cloexec_flag());
260 if (attr_has_sigtrap()) {
261 pr_debug("FAILED sys_perf_event_open(): %s\n",
262 str_error_r(errno
, sbuf
, sizeof(sbuf
)));
264 pr_debug("perf_event_attr doesn't have sigtrap\n");
267 goto out_restore_sigaction
;
270 for (i
= 0; i
< NUM_THREADS
; i
++) {
271 if (pthread_create(&threads
[i
], NULL
, test_thread
, &barrier
)) {
272 pr_debug("FAILED pthread_create(): %s\n", str_error_r(errno
, sbuf
, sizeof(sbuf
)));
273 goto out_close_perf_event
;
277 ret
= run_stress_test(fd
, threads
, &barrier
);
279 out_close_perf_event
:
281 out_restore_sigaction
:
282 sigaction(SIGTRAP
, &oldact
, NULL
);
284 pthread_barrier_destroy(&barrier
);
289 DEFINE_SUITE("Sigtrap", sigtrap
);