Last active
December 11, 2021 22:47
-
-
Save kostikbel/0055f980c8b3f3f03b79939e4764b459 to your computer and use it in GitHub Desktop.
Test AVX context save/restore on context switches and signals
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $Id: avx_sig.c,v 1.12 2021/12/11 22:47:09 kostik Exp $ */ | |
/* | |
* FreeBSD: | |
* ${CC} -Wall -Wextra -O -g -o avx_sig avx_sig.c -lpthread | |
* Linux | |
* ${CC} -D_GNU_SOURCE -Wall -Wextra -O -g -o avx_sig avx_sig.c -lbsd -lpthread | |
*/ | |
#include <sys/param.h> | |
#include <sys/time.h> | |
#include <sys/resource.h> | |
#include <sys/syscall.h> | |
#include <errno.h> | |
#include <pthread.h> | |
#ifdef __FreeBSD__ | |
#include <pthread_np.h> | |
#endif | |
#ifdef __linux__ | |
#include <bsd/stdlib.h> | |
#endif | |
#include <signal.h> | |
#include <stdatomic.h> | |
#include <stdbool.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#ifndef __unused | |
#define __unused __attribute__((__unused__)) | |
#endif | |
#ifndef nitems | |
#define nitems(x) (sizeof((x)) / sizeof((x)[0])) | |
#endif | |
struct xmmreg { | |
uint8_t xmm_bytes[16]; | |
}; | |
struct xmm { | |
struct xmmreg xmmreg[16]; | |
}; | |
#define X2C(r) asm("movdqu %0, %%xmm" #r : "=m" (xmm->xmmreg[r])) | |
#define C2X(r) asm("movdqu %%xmm" #r ", %0" : : "m" (xmm->xmmreg[r]) : "xmm" #r) | |
static void | |
cpu_to_xmm(struct xmm *xmm) | |
{ | |
C2X(0); C2X(1); C2X(2); C2X(3); C2X(4); C2X(5); C2X(6); C2X(7); | |
C2X(8); C2X(9); C2X(10); C2X(11); C2X(12); C2X(13); C2X(14); C2X(15); | |
} | |
static void | |
xmm_to_cpu(struct xmm *xmm) | |
{ | |
X2C(0); X2C(1); X2C(2); X2C(3); X2C(4); X2C(5); X2C(6); X2C(7); | |
X2C(8); X2C(9); X2C(10); X2C(11); X2C(12); X2C(13); X2C(14); X2C(15); | |
} | |
#undef C2X | |
#undef X2C | |
static atomic_uint sigs; | |
static void | |
sigusr1_handler(int sig __unused, siginfo_t *si __unused, void *m __unused) | |
{ | |
atomic_fetch_add_explicit(&sigs, 1, memory_order_relaxed); | |
} | |
#ifdef SIGINFO | |
static void | |
siginfo_handler(int sig __unused) | |
{ | |
struct rusage r; | |
if (getrusage(RUSAGE_SELF, &r) == 0) { | |
printf("%lu vctx %lu nvctx %lu nsigs ", | |
r.ru_nvcsw, r.ru_nivcsw, r.ru_nsignals); | |
} | |
printf("%u SIGUSR1\n", sigs); | |
} | |
#endif | |
static struct xmm zero_xmm = {}; | |
static void | |
fill_xmm(struct xmm *xmm) | |
{ | |
arc4random_buf(xmm, sizeof(*xmm)); | |
} | |
static void | |
dump_xmm(const struct xmmreg *r) | |
{ | |
unsigned k; | |
for (k = 0; k < nitems(r->xmm_bytes); k++) { | |
if (k != 0) | |
printf(" "); | |
printf("%02x", r->xmm_bytes[k]); | |
} | |
printf("\n"); | |
} | |
static pthread_mutex_t show_lock; | |
static void | |
show_diff(const struct xmm *xmm1, const struct xmm *xmm2) | |
{ | |
const struct xmmreg *r1, *r2; | |
unsigned i, j; | |
#if defined(__FreeBSD__) | |
printf("thr %d\n", pthread_getthreadid_np()); | |
#elif defined(__linux__) | |
printf("thr %ld\n", syscall(SYS_gettid)); | |
#endif | |
for (i = 0; i < nitems(xmm1->xmmreg); i++) { | |
r1 = &xmm1->xmmreg[i]; | |
r2 = &xmm2->xmmreg[i]; | |
for (j = 0; j < nitems(r1->xmm_bytes); j++) { | |
if (r1->xmm_bytes[j] != r2->xmm_bytes[j]) { | |
printf("xmm%u\n", i); | |
dump_xmm(r1); | |
dump_xmm(r2); | |
break; | |
} | |
} | |
} | |
} | |
static void | |
my_pause(void) | |
{ | |
usleep(0); | |
} | |
static void * | |
worker_thread(void *arg __unused) | |
{ | |
struct xmm xmm, xmm_cpu; | |
fill_xmm(&xmm); | |
for (;;) { | |
xmm_to_cpu(&xmm); | |
my_pause(); | |
cpu_to_xmm(&xmm_cpu); | |
if (memcmp(&xmm, &xmm_cpu, sizeof(struct xmm)) != 0) { | |
pthread_mutex_lock(&show_lock); | |
show_diff(&xmm, &xmm_cpu); | |
abort(); | |
pthread_mutex_unlock(&show_lock); | |
} | |
xmm_to_cpu(&zero_xmm); | |
my_pause(); | |
cpu_to_xmm(&xmm_cpu); | |
if (memcmp(&zero_xmm, &xmm_cpu, sizeof(struct xmm)) != 0) { | |
pthread_mutex_lock(&show_lock); | |
show_diff(&zero_xmm, &xmm_cpu); | |
abort(); | |
pthread_mutex_unlock(&show_lock); | |
} | |
} | |
return (NULL); | |
} | |
int | |
main(void) | |
{ | |
struct sigaction sa; | |
int error, i, ncpu; | |
#ifdef SIGINFO | |
bzero(&sa, sizeof(sa)); | |
sa.sa_handler = siginfo_handler; | |
if (sigaction(SIGINFO, &sa, NULL) == -1) { | |
fprintf(stderr, "sigaction SIGINFO %s\n", strerror(errno)); | |
exit(1); | |
} | |
#endif | |
bzero(&sa, sizeof(sa)); | |
sa.sa_sigaction = sigusr1_handler; | |
sa.sa_flags = SA_SIGINFO; | |
if (sigaction(SIGUSR1, &sa, NULL) == -1) { | |
fprintf(stderr, "sigaction SIGUSR1 %s\n", strerror(errno)); | |
exit(1); | |
} | |
error = pthread_mutex_init(&show_lock, NULL); | |
if (error != 0) { | |
fprintf(stderr, "pthread_mutex_init %s\n", strerror(error)); | |
exit(1); | |
} | |
ncpu = sysconf(_SC_NPROCESSORS_ONLN); | |
ncpu *= 2; | |
pthread_t wt[ncpu]; | |
for (i = 0; i < ncpu; i++) { | |
error = pthread_create(&wt[i], NULL, worker_thread, NULL); | |
if (error != 0) { | |
fprintf(stderr, "pthread_create %s\n", strerror(error)); | |
} | |
} | |
for (;;) { | |
for (i = 0; i < ncpu; i++) { | |
my_pause(); | |
pthread_kill(wt[i], SIGUSR1); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment