brintos

brintos / linux-shallow public Read only

0
0
Text · 6.9 KiB · d1d8483 Raw
241 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Test for perf events with SIGTRAP across all threads.4 *5 * Copyright (C) 2021, Google LLC.6 */7 8#define _GNU_SOURCE9 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 114#define __have_sigval_t 115#define __have_sigevent_t 116#define __siginfo_t_defined17#define __sigval_t_defined18#define __sigevent_t_defined19#define _BITS_SIGINFO_CONSTS_H 120#define _BITS_SIGEVENT_CONSTS_H 121 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 537 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 same80	 * 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(false, &ctx.iterate_on, 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(&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(true, &ctx.iterate_on, 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_MAIN241