1
2
3
4
5
6
7
8#define _GNU_SOURCE
9
10
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
39static struct {
40 int tids_want_signal;
41 int signal_count;
42 volatile int iterate_on;
43 siginfo_t first_siginfo;
44} ctx;
45
46
47#define TEST_SIG_DATA(addr) (~(unsigned long)(addr))
48
49static struct perf_event_attr make_event_attr(bool enabled, volatile void *addr)
50{
51 struct perf_event_attr attr = {
52 .type = PERF_TYPE_BREAKPOINT,
53 .size = sizeof(attr),
54 .sample_period = 1,
55 .disabled = !enabled,
56 .bp_addr = (unsigned long)addr,
57 .bp_type = HW_BREAKPOINT_RW,
58 .bp_len = HW_BREAKPOINT_LEN_1,
59 .inherit = 1,
60 .inherit_thread = 1,
61 .remove_on_exec = 1,
62 .sigtrap = 1,
63 .sig_data = TEST_SIG_DATA(addr),
64 };
65 return attr;
66}
67
68static void sigtrap_handler(int signum, siginfo_t *info, void *ucontext)
69{
70 if (info->si_code != TRAP_PERF) {
71 fprintf(stderr, "%s: unexpected si_code %d\n", __func__, info->si_code);
72 return;
73 }
74
75
76
77
78
79 if (!__atomic_fetch_add(&ctx.signal_count, 1, __ATOMIC_RELAXED))
80 ctx.first_siginfo = *info;
81 __atomic_fetch_sub(&ctx.tids_want_signal, syscall(__NR_gettid), __ATOMIC_RELAXED);
82}
83
84static void *test_thread(void *arg)
85{
86 pthread_barrier_t *barrier = (pthread_barrier_t *)arg;
87 pid_t tid = syscall(__NR_gettid);
88 int iter;
89 int i;
90
91 pthread_barrier_wait(barrier);
92
93 __atomic_fetch_add(&ctx.tids_want_signal, tid, __ATOMIC_RELAXED);
94 iter = ctx.iterate_on;
95 for (i = 0; i < iter - 1; i++) {
96 __atomic_fetch_add(&ctx.tids_want_signal, tid, __ATOMIC_RELAXED);
97 ctx.iterate_on = iter;
98 }
99
100 return NULL;
101}
102
103FIXTURE(sigtrap_threads)
104{
105 struct sigaction oldact;
106 pthread_t threads[NUM_THREADS];
107 pthread_barrier_t barrier;
108 int fd;
109};
110
111FIXTURE_SETUP(sigtrap_threads)
112{
113 struct perf_event_attr attr = make_event_attr(false, &ctx.iterate_on);
114 struct sigaction action = {};
115 int i;
116
117 memset(&ctx, 0, sizeof(ctx));
118
119
120 action.sa_flags = SA_SIGINFO | SA_NODEFER;
121 action.sa_sigaction = sigtrap_handler;
122 sigemptyset(&action.sa_mask);
123 ASSERT_EQ(sigaction(SIGTRAP, &action, &self->oldact), 0);
124
125
126 self->fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, PERF_FLAG_FD_CLOEXEC);
127 ASSERT_NE(self->fd, -1);
128
129
130 pthread_barrier_init(&self->barrier, NULL, NUM_THREADS + 1);
131 for (i = 0; i < NUM_THREADS; i++)
132 ASSERT_EQ(pthread_create(&self->threads[i], NULL, test_thread, &self->barrier), 0);
133}
134
135FIXTURE_TEARDOWN(sigtrap_threads)
136{
137 pthread_barrier_destroy(&self->barrier);
138 close(self->fd);
139 sigaction(SIGTRAP, &self->oldact, NULL);
140}
141
142static void run_test_threads(struct __test_metadata *_metadata,
143 FIXTURE_DATA(sigtrap_threads) *self)
144{
145 int i;
146
147 pthread_barrier_wait(&self->barrier);
148 for (i = 0; i < NUM_THREADS; i++)
149 ASSERT_EQ(pthread_join(self->threads[i], NULL), 0);
150}
151
152TEST_F(sigtrap_threads, remain_disabled)
153{
154 run_test_threads(_metadata, self);
155 EXPECT_EQ(ctx.signal_count, 0);
156 EXPECT_NE(ctx.tids_want_signal, 0);
157}
158
159TEST_F(sigtrap_threads, enable_event)
160{
161 EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0);
162 run_test_threads(_metadata, self);
163
164 EXPECT_EQ(ctx.signal_count, NUM_THREADS);
165 EXPECT_EQ(ctx.tids_want_signal, 0);
166 EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
167 EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
168 EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on));
169
170
171 ctx.iterate_on = 0;
172 EXPECT_EQ(ctx.signal_count, NUM_THREADS + 1);
173}
174
175
176TEST_F(sigtrap_threads, modify_and_enable_event)
177{
178 struct perf_event_attr new_attr = make_event_attr(true, &ctx.iterate_on);
179
180 EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &new_attr), 0);
181 run_test_threads(_metadata, self);
182
183 EXPECT_EQ(ctx.signal_count, NUM_THREADS);
184 EXPECT_EQ(ctx.tids_want_signal, 0);
185 EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
186 EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
187 EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on));
188
189
190 ctx.iterate_on = 0;
191 EXPECT_EQ(ctx.signal_count, NUM_THREADS + 1);
192}
193
194
195TEST_F(sigtrap_threads, signal_stress)
196{
197 ctx.iterate_on = 3000;
198
199 EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_ENABLE, 0), 0);
200 run_test_threads(_metadata, self);
201 EXPECT_EQ(ioctl(self->fd, PERF_EVENT_IOC_DISABLE, 0), 0);
202
203 EXPECT_EQ(ctx.signal_count, NUM_THREADS * ctx.iterate_on);
204 EXPECT_EQ(ctx.tids_want_signal, 0);
205 EXPECT_EQ(ctx.first_siginfo.si_addr, &ctx.iterate_on);
206 EXPECT_EQ(ctx.first_siginfo.si_perf_type, PERF_TYPE_BREAKPOINT);
207 EXPECT_EQ(ctx.first_siginfo.si_perf_data, TEST_SIG_DATA(&ctx.iterate_on));
208}
209
210TEST_HARNESS_MAIN
211