ref: 0b6ee30eab2400e4f85b735ad29a68a842c34e21
parent: 02312cae6c45a58d1b275ad80eb6c41271415c3b
author: Henrik Gramner <gramner@twoorioles.com>
date: Wed Dec 5 17:49:25 EST 2018
checkasm: Try to handle crashes more gracefully Attempt to catch SIGFPE, SIGILL, and SIGSEGV and flag the current test as failed instead of aborting abruptly.
--- a/tests/checkasm/checkasm.c
+++ b/tests/checkasm/checkasm.c
@@ -44,6 +44,7 @@
}
#else
#include <unistd.h>
+#include <signal.h>
#include <sys/time.h>
#define COLOR_RED 1
#define COLOR_GREEN 2
@@ -400,6 +401,44 @@
return f;
}
+checkasm_context checkasm_context_buf;
+
+/* Crash handling: attempt to catch crashes and handle them
+ * gracefully instead of just aborting abruptly. */
+#ifdef _WIN32
+static LONG NTAPI signal_handler(EXCEPTION_POINTERS *const e) {
+ switch (e->ExceptionRecord->ExceptionCode) {
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ checkasm_fail_func("fatal arithmetic error");
+ break;
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ case EXCEPTION_PRIV_INSTRUCTION:
+ checkasm_fail_func("illegal instruction");
+ break;
+ case EXCEPTION_ACCESS_VIOLATION:
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ case EXCEPTION_IN_PAGE_ERROR:
+ case EXCEPTION_STACK_OVERFLOW:
+ checkasm_fail_func("segmentation fault");
+ break;
+ default:
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ checkasm_load_context();
+ return EXCEPTION_CONTINUE_EXECUTION; /* never reached, but shuts up gcc */
+}
+#else
+static void signal_handler(const int s) {
+ checkasm_set_signal_handler_state(0);
+ checkasm_fail_func(s == SIGFPE ? "fatal arithmetic error" :
+ s == SIGILL ? "illegal instruction" :
+ "segmentation fault");
+ checkasm_load_context();
+}
+#endif
+
/* Perform tests and benchmarks for the specified
* cpu flag if supported by the host */
static void check_cpu_flag(const char *const name, unsigned flag) {
@@ -604,4 +643,18 @@
if (length > max_length)
max_length = length;
}
+}
+
+void checkasm_set_signal_handler_state(const int enabled) {
+#ifdef _WIN32
+ if (enabled)
+ AddVectoredExceptionHandler(0, signal_handler);
+ else
+ RemoveVectoredExceptionHandler(signal_handler);
+#else
+ void (*const handler)(int) = enabled ? signal_handler : SIG_DFL;
+ signal(SIGFPE, handler);
+ signal(SIGILL, handler);
+ signal(SIGSEGV, handler);
+#endif
}
--- a/tests/checkasm/checkasm.h
+++ b/tests/checkasm/checkasm.h
@@ -33,6 +33,20 @@
#include <stdint.h>
#include <stdlib.h>
+#if ARCH_X86_64 && defined(_WIN32)
+/* setjmp/longjmp on 64-bit Windows will try to use SEH to unwind the stack,
+ * which doesn't work for assembly functions without unwind information. */
+#include <windows.h>
+#define checkasm_context CONTEXT
+#define checkasm_save_context() RtlCaptureContext(&checkasm_context_buf)
+#define checkasm_load_context() RtlRestoreContext(&checkasm_context_buf, NULL)
+#else
+#include <setjmp.h>
+#define checkasm_context jmp_buf
+#define checkasm_save_context() setjmp(checkasm_context_buf)
+#define checkasm_load_context() longjmp(checkasm_context_buf, 1)
+#endif
+
#include "include/common/attributes.h"
#include "include/common/intops.h"
@@ -52,6 +66,8 @@
void checkasm_fail_func(const char *msg, ...);
void checkasm_update_bench(int iterations, uint64_t cycles);
void checkasm_report(const char *name, ...);
+void checkasm_set_signal_handler_state(int enabled);
+extern checkasm_context checkasm_context_buf;
/* float compare utilities */
int float_near_ulp(float a, float b, unsigned max_ulp);
@@ -76,7 +92,9 @@
* the remaining arguments are the function parameters. Naming parameters
* is optional. */
#define declare_func(ret, ...)\
- declare_new(ret, __VA_ARGS__) typedef ret func_type(__VA_ARGS__)
+ declare_new(ret, __VA_ARGS__)\
+ typedef ret func_type(__VA_ARGS__);\
+ checkasm_save_context()
/* Indicate that the current test has failed */
#define fail() checkasm_fail_func("%s:%d", __FILE__, __LINE__)
@@ -85,7 +103,10 @@
#define report checkasm_report
/* Call the reference function */
-#define call_ref(...) ((func_type *)func_ref)(__VA_ARGS__)
+#define call_ref(...)\
+ (checkasm_set_signal_handler_state(1),\
+ ((func_type *)func_ref)(__VA_ARGS__));\
+ checkasm_set_signal_handler_state(0)
#if HAVE_ASM
#if ARCH_X86
@@ -153,14 +174,19 @@
(void *)checkasm_checked_call;
#define CLOB (UINT64_C(0xdeadbeefdeadbeef))
#define call_new(...)\
- (checkasm_stack_clobber(CLOB, CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
+ (checkasm_set_signal_handler_state(1),\
+ checkasm_stack_clobber(CLOB, CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
CLOB, CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
CLOB, CLOB, CLOB, CLOB, CLOB, CLOB, CLOB),\
- checked_call(func_new, 0, 0, 0, 0, 0, __VA_ARGS__))
+ checked_call(func_new, 0, 0, 0, 0, 0, __VA_ARGS__));\
+ checkasm_set_signal_handler_state(0)
#elif ARCH_X86_32
#define declare_new(ret, ...)\
ret (*checked_call)(void *, __VA_ARGS__) = (void *)checkasm_checked_call;
-#define call_new(...) checked_call(func_new, __VA_ARGS__)
+#define call_new(...)\
+ (checkasm_set_signal_handler_state(1),\
+ checked_call(func_new, __VA_ARGS__));\
+ checkasm_set_signal_handler_state(0)
#elif ARCH_ARM
/* Use a dummy argument, to offset the real parameters by 2, not only 1.
* This makes sure that potential 8-byte-alignment of parameters is kept
@@ -169,7 +195,10 @@
#define declare_new(ret, ...)\
ret (*checked_call)(void *, int dummy, __VA_ARGS__) =\
(void *)checkasm_checked_call_vfp;
-#define call_new(...) checked_call(func_new, 0, __VA_ARGS__)
+#define call_new(...)\
+ (checkasm_set_signal_handler_state(1),\
+ checked_call(func_new, 0, __VA_ARGS__));\
+ checkasm_set_signal_handler_state(0)
#elif ARCH_AARCH64 && !defined(__APPLE__)
void checkasm_stack_clobber(uint64_t clobber, ...);
#define declare_new(ret, ...)\
@@ -178,19 +207,27 @@
(void *)checkasm_checked_call;
#define CLOB (UINT64_C(0xdeadbeefdeadbeef))
#define call_new(...)\
- (checkasm_stack_clobber(CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
+ (checkasm_set_signal_handler_state(1),\
+ checkasm_stack_clobber(CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
CLOB, CLOB, CLOB, CLOB, CLOB),\
- checked_call(func_new, 0, 0, 0, 0, 0, 0, 0, __VA_ARGS__))
+ checked_call(func_new, 0, 0, 0, 0, 0, 0, 0, __VA_ARGS__));\
+ checkasm_set_signal_handler_state(0)
#else
#define declare_new(ret, ...)
-#define call_new(...) ((func_type *)func_new)(__VA_ARGS__)
+#define call_new(...)\
+ (checkasm_set_signal_handler_state(1),\
+ ((func_type *)func_new)(__VA_ARGS__));\
+ checkasm_set_signal_handler_state(0)
#endif
#else /* HAVE_ASM */
#define declare_new(ret, ...)
/* Call the function */
-#define call_new(...) ((func_type *)func_new)(__VA_ARGS__)
+#define call_new(...)\
+ (checkasm_set_signal_handler_state(1),\
+ ((func_type *)func_new)(__VA_ARGS__));\
+ checkasm_set_signal_handler_state(0)
#endif /* HAVE_ASM */
/* Benchmark the function */
@@ -198,6 +235,7 @@
#define bench_new(...)\
do {\
if (checkasm_bench_func()) {\
+ checkasm_set_signal_handler_state(1);\
func_type *tfunc = func_new;\
uint64_t tsum = 0;\
int ti, tcount = 0;\
@@ -213,6 +251,7 @@
tcount++;\
}\
}\
+ checkasm_set_signal_handler_state(0);\
checkasm_update_bench(tcount, tsum);\
}\
} while (0)
--- a/tests/checkasm/loopfilter.c
+++ b/tests/checkasm/loopfilter.c
@@ -97,6 +97,11 @@
{
ALIGN_STK_32(pixel, c_dst_mem, 128 * 16,);
ALIGN_STK_32(pixel, a_dst_mem, 128 * 16,);
+
+ declare_func(void, pixel *dst, ptrdiff_t dst_stride, const uint32_t *mask,
+ const uint8_t (*l)[4], ptrdiff_t b4_stride,
+ const Av1FilterLUT *lut, int w HIGHBD_DECL_SUFFIX);
+
pixel *a_dst, *c_dst;
ptrdiff_t stride, b4_stride;
if (dir) {
@@ -110,10 +115,6 @@
stride = 16 * sizeof(pixel);
b4_stride = 2;
}
-
- declare_func(void, pixel *dst, ptrdiff_t dst_stride, const uint32_t *mask,
- const uint8_t (*l)[4], ptrdiff_t b4_stride,
- const Av1FilterLUT *lut, int w HIGHBD_DECL_SUFFIX);
Av1FilterLUT lut;
const int sharp = rand() & 7;