shithub: dav1d

Download patch

ref: 0b6ee30eab2400e4f85b735ad29a68a842c34e21
parent: 02312cae6c45a58d1b275ad80eb6c41271415c3b
author: Henrik Gramner <[email protected]>
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;