| 1 | |
| 2 | |
| 3 | * Basic test for sigtrap support.
|
| 4 | *
|
| 5 | * Copyright (C) 2021, Google LLC.
|
| 6 |
|
| 7 |
|
| 8 | #include <errno.h>
|
| 9 | #include <stdint.h>
|
| 10 | #include <stdlib.h>
|
| 11 | #include <linux/hw_breakpoint.h>
|
| 12 | #include <linux/string.h>
|
| 13 | #include <pthread.h>
|
| 14 | #include <signal.h>
|
| 15 | #include <sys/ioctl.h>
|
| 16 | #include <sys/syscall.h>
|
| 17 | #include <unistd.h>
|
| 18 |
|
| 19 | #include "cloexec.h"
|
| 20 | #include "debug.h"
|
| 21 | #include "event.h"
|
| 22 | #include "tests.h"
|
| 23 | #include "../perf-sys.h"
|
| 24 |
|
| 25 | #define NUM_THREADS 5
|
| 26 |
|
| 27 | static struct {
|
| 28 | int tids_want_signal;
|
| 29 | int signal_count;
|
| 30 | volatile int iterate_on;
|
| 31 | siginfo_t first_siginfo;
|
| 32 | } ctx;
|
| 33 |
|
| 34 | #define TEST_SIG_DATA (~(unsigned long)(&ctx.iterate_on))
|
| 35 |
|
| 36 | static struct perf_event_attr make_event_attr(void)
|
| 37 | {
|
| 38 | struct perf_event_attr attr = {
|
| 39 | .type = PERF_TYPE_BREAKPOINT,
|
| 40 | .size = sizeof(attr),
|
| 41 | .sample_period = 1,
|
| 42 | .disabled = 1,
|
| 43 | .bp_addr = (unsigned long)&ctx.iterate_on,
|
| 44 | .bp_type = HW_BREAKPOINT_RW,
|
| 45 | .bp_len = HW_BREAKPOINT_LEN_1,
|
| 46 | .inherit = 1,
|
| 47 | .inherit_thread = 1,
|
| 48 | .remove_on_exec = 1,
|
| 49 | .sigtrap = 1,
|
| 50 | .sig_data = TEST_SIG_DATA,
|
| 51 | .exclude_kernel = 1,
|
| 52 | .exclude_hv = 1,
|
| 53 | };
|
| 54 | return attr;
|
| 55 | }
|
| 56 |
|
| 57 | #ifdef HAVE_BPF_SKEL
|
| 58 | #include <bpf/btf.h>
|
| 59 | #include <util/btf.h>
|
| 60 |
|
| 61 | static struct btf *btf;
|
| 62 |
|
| 63 | static bool btf__available(void)
|
| 64 | {
|
| 65 | if (btf == NULL)
|
| 66 | btf = btf__load_vmlinux_btf();
|
| 67 |
|
| 68 | return btf != NULL;
|
| 69 | }
|
| 70 |
|
| 71 | static void btf__exit(void)
|
| 72 | {
|
| 73 | btf__free(btf);
|
| 74 | btf = NULL;
|
| 75 | }
|
| 76 |
|
| 77 | static bool attr_has_sigtrap(void)
|
| 78 | {
|
| 79 | int id;
|
| 80 |
|
| 81 | if (!btf__available()) {
|
| 82 |
|
| 83 | return false;
|
| 84 | }
|
| 85 |
|
| 86 | id = btf__find_by_name_kind(btf, "perf_event_attr", BTF_KIND_STRUCT);
|
| 87 | if (id < 0)
|
| 88 | return false;
|
| 89 |
|
| 90 | return __btf_type__find_member_by_name(btf, id, "sigtrap") != NULL;
|
| 91 | }
|
| 92 |
|
| 93 | static bool kernel_with_sleepable_spinlocks(void)
|
| 94 | {
|
| 95 | const struct btf_member *member;
|
| 96 | const struct btf_type *type;
|
| 97 | const char *type_name;
|
| 98 | int id;
|
| 99 |
|
| 100 | if (!btf__available())
|
| 101 | return false;
|
| 102 |
|
| 103 | id = btf__find_by_name_kind(btf, "spinlock", BTF_KIND_STRUCT);
|
| 104 | if (id < 0)
|
| 105 | return false;
|
| 106 |
|
| 107 | |
| 108 | member = __btf_type__find_member_by_name(btf, id, "lock");
|
| 109 | if (member == NULL)
|
| 110 | return false;
|
| 111 |
|
| 112 | |
| 113 | type = btf__type_by_id(btf, member->type);
|
| 114 | if (!type || !btf_is_struct(type))
|
| 115 | return false;
|
| 116 |
|
| 117 | type_name = btf__name_by_offset(btf, type->name_off);
|
| 118 | return type_name && !strcmp(type_name, "rt_mutex_base");
|
| 119 | }
|
| 120 | #else
|
| 121 | static bool attr_has_sigtrap(void)
|
| 122 | {
|
| 123 | struct perf_event_attr attr = {
|
| 124 | .type = PERF_TYPE_SOFTWARE,
|
| 125 | .config = PERF_COUNT_SW_DUMMY,
|
| 126 | .size = sizeof(attr),
|
| 127 | .remove_on_exec = 1,
|
| 128 | .sigtrap = 1,
|
| 129 | };
|
| 130 | int fd;
|
| 131 | bool ret = false;
|
| 132 |
|
| 133 | fd = sys_perf_event_open(&attr, 0, -1, -1, perf_event_open_cloexec_flag());
|
| 134 | if (fd >= 0) {
|
| 135 | ret = true;
|
| 136 | close(fd);
|
| 137 | }
|
| 138 |
|
| 139 | return ret;
|
| 140 | }
|
| 141 |
|
| 142 | static bool kernel_with_sleepable_spinlocks(void)
|
| 143 | {
|
| 144 | return false;
|
| 145 | }
|
| 146 |
|
| 147 | static void btf__exit(void)
|
| 148 | {
|
| 149 | }
|
| 150 | #endif
|
| 151 |
|
| 152 | static void
|
| 153 | sigtrap_handler(int signum __maybe_unused, siginfo_t *info, void *ucontext __maybe_unused)
|
| 154 | {
|
| 155 | if (!__atomic_fetch_add(&ctx.signal_count, 1, __ATOMIC_RELAXED))
|
| 156 | ctx.first_siginfo = *info;
|
| 157 | __atomic_fetch_sub(&ctx.tids_want_signal, syscall(SYS_gettid), __ATOMIC_RELAXED);
|
| 158 | }
|
| 159 |
|
| 160 | static void *test_thread(void *arg)
|
| 161 | {
|
| 162 | pthread_barrier_t *barrier = (pthread_barrier_t *)arg;
|
| 163 | pid_t tid = syscall(SYS_gettid);
|
| 164 | int i;
|
| 165 |
|
| 166 | pthread_barrier_wait(barrier);
|
| 167 |
|
| 168 | __atomic_fetch_add(&ctx.tids_want_signal, tid, __ATOMIC_RELAXED);
|
| 169 | for (i = 0; i < ctx.iterate_on - 1; i++)
|
| 170 | __atomic_fetch_add(&ctx.tids_want_signal, tid, __ATOMIC_RELAXED);
|
| 171 |
|
| 172 | return NULL;
|
| 173 | }
|
| 174 |
|
| 175 | static int run_test_threads(pthread_t *threads, pthread_barrier_t *barrier)
|
| 176 | {
|
| 177 | int i;
|
| 178 |
|
| 179 | pthread_barrier_wait(barrier);
|
| 180 | for (i = 0; i < NUM_THREADS; i++)
|
| 181 | TEST_ASSERT_EQUAL("pthread_join() failed", pthread_join(threads[i], NULL), 0);
|
| 182 |
|
| 183 | return TEST_OK;
|
| 184 | }
|
| 185 |
|
| 186 | static int run_stress_test(int fd, pthread_t *threads, pthread_barrier_t *barrier)
|
| 187 | {
|
| 188 | int ret, expected_sigtraps;
|
| 189 |
|
| 190 | ctx.iterate_on = 3000;
|
| 191 |
|
| 192 | TEST_ASSERT_EQUAL("misfired signal?", ctx.signal_count, 0);
|
| 193 | TEST_ASSERT_EQUAL("enable failed", ioctl(fd, PERF_EVENT_IOC_ENABLE, 0), 0);
|
| 194 | ret = run_test_threads(threads, barrier);
|
| 195 | TEST_ASSERT_EQUAL("disable failed", ioctl(fd, PERF_EVENT_IOC_DISABLE, 0), 0);
|
| 196 |
|
| 197 | expected_sigtraps = NUM_THREADS * ctx.iterate_on;
|
| 198 |
|
| 199 | if (ctx.signal_count < expected_sigtraps && kernel_with_sleepable_spinlocks()) {
|
| 200 | pr_debug("Expected %d sigtraps, got %d, running on a kernel with sleepable spinlocks.\n",
|
| 201 | expected_sigtraps, ctx.signal_count);
|
| 202 | pr_debug("See https://lore.kernel.org/all/[email protected]/\n");
|
| 203 | return TEST_SKIP;
|
| 204 | } else
|
| 205 | TEST_ASSERT_EQUAL("unexpected sigtraps", ctx.signal_count, expected_sigtraps);
|
| 206 |
|
| 207 | TEST_ASSERT_EQUAL("missing signals or incorrectly delivered", ctx.tids_want_signal, 0);
|
| 208 | TEST_ASSERT_VAL("unexpected si_addr", ctx.first_siginfo.si_addr == &ctx.iterate_on);
|
| 209 | #if 0 |
| 210 | TEST_ASSERT_EQUAL("unexpected si_perf_type", ctx.first_siginfo.si_perf_type,
|
| 211 | PERF_TYPE_BREAKPOINT);
|
| 212 | TEST_ASSERT_EQUAL("unexpected si_perf_data", ctx.first_siginfo.si_perf_data,
|
| 213 | TEST_SIG_DATA);
|
| 214 | #endif
|
| 215 |
|
| 216 | return ret;
|
| 217 | }
|
| 218 |
|
| 219 | static int test__sigtrap(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
|
| 220 | {
|
| 221 | struct perf_event_attr attr = make_event_attr();
|
| 222 | struct sigaction action = {};
|
| 223 | struct sigaction oldact;
|
| 224 | pthread_t threads[NUM_THREADS];
|
| 225 | pthread_barrier_t barrier;
|
| 226 | char sbuf[STRERR_BUFSIZE];
|
| 227 | int i, fd, ret = TEST_FAIL;
|
| 228 |
|
| 229 | if (!BP_SIGNAL_IS_SUPPORTED) {
|
| 230 | pr_debug("Test not supported on this architecture");
|
| 231 | return TEST_SKIP;
|
| 232 | }
|
| 233 |
|
| 234 | pthread_barrier_init(&barrier, NULL, NUM_THREADS + 1);
|
| 235 |
|
| 236 | action.sa_flags = SA_SIGINFO | SA_NODEFER;
|
| 237 | action.sa_sigaction = sigtrap_handler;
|
| 238 | sigemptyset(&action.sa_mask);
|
| 239 | if (sigaction(SIGTRAP, &action, &oldact)) {
|
| 240 | pr_debug("FAILED sigaction(): %s\n", str_error_r(errno, sbuf, sizeof(sbuf)));
|
| 241 | goto out;
|
| 242 | }
|
| 243 |
|
| 244 | fd = sys_perf_event_open(&attr, 0, -1, -1, perf_event_open_cloexec_flag());
|
| 245 | if (fd < 0) {
|
| 246 | if (attr_has_sigtrap()) {
|
| 247 | pr_debug("FAILED sys_perf_event_open(): %s\n",
|
| 248 | str_error_r(errno, sbuf, sizeof(sbuf)));
|
| 249 | } else {
|
| 250 | pr_debug("perf_event_attr doesn't have sigtrap\n");
|
| 251 | ret = TEST_SKIP;
|
| 252 | }
|
| 253 | goto out_restore_sigaction;
|
| 254 | }
|
| 255 |
|
| 256 | for (i = 0; i < NUM_THREADS; i++) {
|
| 257 | if (pthread_create(&threads[i], NULL, test_thread, &barrier)) {
|
| 258 | pr_debug("FAILED pthread_create(): %s\n", str_error_r(errno, sbuf, sizeof(sbuf)));
|
| 259 | goto out_close_perf_event;
|
| 260 | }
|
| 261 | }
|
| 262 |
|
| 263 | ret = run_stress_test(fd, threads, &barrier);
|
| 264 |
|
| 265 | out_close_perf_event:
|
| 266 | close(fd);
|
| 267 | out_restore_sigaction:
|
| 268 | sigaction(SIGTRAP, &oldact, NULL);
|
| 269 | out:
|
| 270 | pthread_barrier_destroy(&barrier);
|
| 271 | btf__exit();
|
| 272 | return ret;
|
| 273 | }
|
| 274 |
|
| 275 | DEFINE_SUITE("Sigtrap", sigtrap);
|