xref: /freebsd/tools/test/xregs_sig/xregs_sig.c (revision 911f0260390e18cf85f3dbf2c719b593efdc1e3c)
1 /* $Id: avx_sig.c,v 1.12 2021/12/11 22:47:09 kostik Exp $ */
2 /*
3  * Naive test to check that context switches and signal delivery do
4  * not corrupt AVX registers file (%xmm).  Run until some
5  * inconsistency detected, then aborts.
6  *
7  * FreeBSD:
8  * ${CC} -Wall -Wextra -O -g -o avx_sig avx_sig.c -lpthread
9  * Linux
10  * ${CC} -D_GNU_SOURCE -Wall -Wextra -O -g -o avx_sig avx_sig.c -lbsd -lpthread
11  */
12 
13 #include <sys/param.h>
14 #include <sys/time.h>
15 #include <sys/resource.h>
16 #include <sys/syscall.h>
17 #include <errno.h>
18 #include <pthread.h>
19 #ifdef __FreeBSD__
20 #include <pthread_np.h>
21 #endif
22 #ifdef __linux__
23 #ifdef __GLIBC__
24 #include <gnu/libc-version.h>
25 #endif
26 #if !defined(__GLIBC__) || (__GLIBC__ * 100 + __GLIBC_MINOR__) < 236
27 #include <bsd/stdlib.h>
28 #endif
29 #endif
30 #include <signal.h>
31 #include <stdatomic.h>
32 #include <stdbool.h>
33 #include <stdint.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 /* SIGALRM interval in seconds. */
40 #ifndef TIMO
41 #define	TIMO		5
42 #endif
43 
44 #ifndef __unused
45 #define	__unused	__attribute__((__unused__))
46 #endif
47 
48 struct xregs_bank {
49 	const char	*b_name;
50 	const char	*r_name;
51 	uint32_t	regs;
52 	uint32_t	bytes;
53 	void		(*x2c)(uint8_t *);
54 	void		(*c2x)(uint8_t *);
55 };
56 
57 int xregs_banks_max(void);
58 
59 #if defined(__amd64__)
60 void cpu_to_xmm(uint8_t *);
61 void xmm_to_cpu(uint8_t *);
62 void cpu_to_avx(uint8_t *);
63 void avx_to_cpu(uint8_t *);
64 
65 static const struct xregs_bank xregs_banks[] = {
66 	{
67 		.b_name	= "SSE",
68 		.r_name	= "xmm",
69 		.regs	= 16,
70 		.bytes	= 16,
71 		.x2c	= xmm_to_cpu,
72 		.c2x	= cpu_to_xmm,
73 	},
74 	{
75 		.b_name	= "AVX",
76 		.r_name	= "ymm",
77 		.regs	= 16,
78 		.bytes	= 32,
79 		.x2c	= avx_to_cpu,
80 		.c2x	= cpu_to_avx,
81 	},
82 };
83 #elif defined(__aarch64__)
84 void cpu_to_vfp(uint8_t *);
85 void vfp_to_cpu(uint8_t *);
86 
87 static const struct xregs_bank xregs_banks[] = {
88 	{
89 		.b_name	= "VFP",
90 		.r_name	= "q",
91 		.regs	= 32,
92 		.bytes	= 16,
93 		.x2c	= vfp_to_cpu,
94 		.c2x	= cpu_to_vfp,
95 	},
96 };
97 #endif
98 
99 static atomic_uint sigs;
100 static int max_bank_idx;
101 
102 
103 static void
104 sigusr1_handler(int sig __unused, siginfo_t *si __unused, void *m __unused)
105 {
106 	atomic_fetch_add_explicit(&sigs, 1, memory_order_relaxed);
107 }
108 
109 static void
110 sigalrm_handler(int sig __unused)
111 {
112 	struct rusage r;
113 
114 	if (getrusage(RUSAGE_SELF, &r) == 0) {
115 		printf("%lu vctx %lu nvctx %lu nsigs %u SIGUSR1\n",
116 		    r.ru_nvcsw, r.ru_nivcsw, r.ru_nsignals, sigs);
117 	}
118 	alarm(TIMO);
119 }
120 
121 
122 static void
123 fill_xregs(uint8_t *xregs, int bank)
124 {
125 	arc4random_buf(xregs, xregs_banks[bank].regs * xregs_banks[bank].bytes);
126 }
127 
128 static void
129 dump_xregs(const uint8_t *r, int bank)
130 {
131 	unsigned k;
132 
133 	for (k = 0; k < xregs_banks[bank].bytes; k++) {
134 		if (k != 0)
135 			printf(" ");
136 		printf("%02x", r[k]);
137 	}
138 	printf("\n");
139 }
140 
141 static pthread_mutex_t show_lock;
142 
143 static void
144 show_diff(const uint8_t *xregs1, const uint8_t *xregs2, int bank)
145 {
146 	const uint8_t *r1, *r2;
147 	unsigned i, j;
148 
149 #if defined(__FreeBSD__)
150 	printf("thr %d\n", pthread_getthreadid_np());
151 #elif defined(__linux__)
152 	printf("thr %ld\n", syscall(SYS_gettid));
153 #endif
154 	for (i = 0; i < xregs_banks[bank].regs; i++) {
155 		r1 = xregs1 + i * xregs_banks[bank].bytes;
156 		r2 = xregs2 + i * xregs_banks[bank].bytes;
157 		for (j = 0; j < xregs_banks[bank].bytes; j++) {
158 			if (r1[j] != r2[j]) {
159 				printf("%%%s%u\n", xregs_banks[bank].r_name, i);
160 				dump_xregs(r1, bank);
161 				dump_xregs(r2, bank);
162 				break;
163 			}
164 		}
165 	}
166 }
167 
168 static void
169 my_pause(void)
170 {
171 	usleep(0);
172 }
173 
174 static void *
175 worker_thread(void *arg)
176 {
177 	int bank = (uintptr_t)arg;
178 	int sz = xregs_banks[bank].regs * xregs_banks[bank].bytes;
179 	uint8_t xregs[sz], xregs_cpu[sz], zero_xregs[sz];
180 
181 	memset(zero_xregs, 0, sz);
182 
183 	fill_xregs(xregs, bank);
184 	for (;;) {
185 		xregs_banks[bank].x2c(xregs);
186 		my_pause();
187 		xregs_banks[bank].c2x(xregs_cpu);
188 		if (memcmp(xregs, xregs_cpu, sz) != 0) {
189 			pthread_mutex_lock(&show_lock);
190 			show_diff(xregs, xregs_cpu, bank);
191 			abort();
192 			pthread_mutex_unlock(&show_lock);
193 		}
194 
195 		xregs_banks[bank].x2c(zero_xregs);
196 		my_pause();
197 		xregs_banks[bank].c2x(xregs_cpu);
198 		if (memcmp(zero_xregs, xregs_cpu, sz) != 0) {
199 			pthread_mutex_lock(&show_lock);
200 			show_diff(zero_xregs, xregs_cpu, bank);
201 			abort();
202 			pthread_mutex_unlock(&show_lock);
203 		}
204 	}
205 	return (NULL);
206 }
207 
208 int
209 main(void)
210 {
211 	struct sigaction sa;
212 	int error, i, ncpu, bank;
213 
214 	max_bank_idx = xregs_banks_max();
215 
216 	bzero(&sa, sizeof(sa));
217 	sa.sa_handler = sigalrm_handler;
218 	if (sigaction(SIGALRM, &sa, NULL) == -1) {
219 		fprintf(stderr, "sigaction SIGALRM %s\n", strerror(errno));
220 		exit(1);
221 	}
222 
223 	bzero(&sa, sizeof(sa));
224 	sa.sa_sigaction = sigusr1_handler;
225 	sa.sa_flags = SA_SIGINFO;
226 	if (sigaction(SIGUSR1, &sa, NULL) == -1) {
227 		fprintf(stderr, "sigaction SIGUSR1 %s\n", strerror(errno));
228 		exit(1);
229 	}
230 
231 	error = pthread_mutex_init(&show_lock, NULL);
232 	if (error != 0) {
233 		fprintf(stderr, "pthread_mutex_init %s\n", strerror(error));
234 		exit(1);
235 	}
236 
237 	ncpu = sysconf(_SC_NPROCESSORS_ONLN);
238 	if (max_bank_idx == 0)
239 		ncpu *= 2;
240 	bank = 0;
241 	pthread_t wt[ncpu];
242 nextbank:
243 	printf("Starting %d threads for registers bank %s sized [%d][%d]\n", ncpu,
244 	    xregs_banks[bank].b_name, xregs_banks[bank].regs, xregs_banks[bank].bytes);
245 	for (i = 0; i < ncpu; i++) {
246 		error = pthread_create(&wt[i], NULL, worker_thread,
247 		    (void *)(uintptr_t)bank);
248 		if (error != 0) {
249 			fprintf(stderr, "pthread_create %s\n", strerror(error));
250 		}
251 	}
252 	if (++bank <= max_bank_idx)
253 		goto nextbank;
254 
255 	alarm(TIMO);
256 	for (;;) {
257 		for (i = 0; i < ncpu; i++) {
258 			my_pause();
259 			pthread_kill(wt[i], SIGUSR1);
260 		}
261 	}
262 }
263