Catch exceptions on Windows, to stop it popping up dialog boxes
authorIan Lynagh <igloo@earth.li>
Fri, 3 Aug 2007 18:32:29 +0000 (18:32 +0000)
committerIan Lynagh <igloo@earth.li>
Fri, 3 Aug 2007 18:32:29 +0000 (18:32 +0000)
Adaptated from code from Sigbjorn Finne

rts/Main.c
rts/seh_excn.c [new file with mode: 0644]
rts/seh_excn.h [new file with mode: 0644]

index 745d775..5f16fa3 100644 (file)
@@ -16,6 +16,7 @@
 #include "RtsUtils.h"
 #include "Prelude.h"
 #include "Task.h"
+#include "seh_excn.h"
 #include <stdlib.h>
 
 #ifdef DEBUG
@@ -48,6 +49,7 @@ int main(int argc, char *argv[])
     SchedulerStatus status;
     /* all GranSim/GUM init is done in startupHaskell; sets IAmMainThread! */
 
+    BEGIN_CATCH
     startupHaskell(argc,argv,__stginit_ZCMain);
 
     /* kick off the computation by creating the main thread with a pointer
@@ -133,6 +135,8 @@ int main(int argc, char *argv[])
       barf("main thread completed with invalid status");
     }
     shutdownHaskellAndExit(exit_status);
-    return 0; /* never reached, keep gcc -Wall happy */
+    END_CATCH
+    return 0; /* not reached unless a Windows exception happens,
+                 also keeps gcc -Wall happy */
 }
 # endif /* BATCH_MODE */
diff --git a/rts/seh_excn.c b/rts/seh_excn.c
new file mode 100644 (file)
index 0000000..5da7579
--- /dev/null
@@ -0,0 +1,42 @@
+#include "seh_excn.h"
+
+/*
+ * Exception / signal handlers.
+ */
+#if defined(__MINGW32__)
+jmp_buf seh_unwind_to;
+unsigned long seh_excn_code; /* variable used to communicate what kind of exception we've caught;nice. */
+
+EXCEPTION_DISPOSITION
+catchDivZero(struct _EXCEPTION_RECORD* rec,
+         void* arg1 __attribute__((unused)),
+         struct _CONTEXT* ctxt __attribute__((unused)),
+         void* arg2 __attribute__((unused)))
+{
+     if ((rec->ExceptionFlags & EH_UNWINDING) != 0) {
+     // When the system unwinds the SEH stack after having handled an excn,
+     // return immediately.
+         return ExceptionContinueSearch;
+     }
+     switch (rec->ExceptionCode) {
+     case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+     case EXCEPTION_INT_DIVIDE_BY_ZERO:
+     seh_excn_code = 0;
+     longjmp(seh_unwind_to, rec->ExceptionCode);
+     return ExceptionContinueExecution;
+     case EXCEPTION_STACK_OVERFLOW:
+     seh_excn_code = 1;
+     longjmp(seh_unwind_to, rec->ExceptionCode);
+     return ExceptionContinueExecution;
+     case EXCEPTION_ACCESS_VIOLATION:
+     seh_excn_code = 2;
+     longjmp(seh_unwind_to, rec->ExceptionCode);
+     return ExceptionContinueExecution;
+     longjmp(seh_unwind_to, rec->ExceptionCode);
+     return ExceptionContinueExecution;
+     default: ;
+     }
+     return ExceptionContinueSearch;
+}
+#endif
+
diff --git a/rts/seh_excn.h b/rts/seh_excn.h
new file mode 100644 (file)
index 0000000..311675d
--- /dev/null
@@ -0,0 +1,92 @@
+#ifndef __SEH_EXCN_H__
+#define __SEH_EXCN_H__
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(__MINGW32__)
+/* Stuff needed to install and use SEH exception handlers */
+#include <excpt.h>
+#include <setjmp.h>
+#include <windows.h>
+#elif defined(_MSC_VER)
+#include <windows.h>
+#else
+#include <signal.h>
+#endif
+
+/* Exception handling.
+ *
+ * On Win32, the default action for things like division by zero and
+ * segfaults is to pop up an annoying little dialog box.
+ *
+ * This is a pain when we are SSHed into a Windows machine, or when we
+ * want to debug a problem with gdb.
+ *
+ * seh_excn provides two macros, BEGIN_CATCH and END_CATCH, which
+ * will catch such exceptions in the code they bracket and die by
+ * printing a message and calling exit(1).
+ */
+#define ON_DIV_ZERO fprintf(stdout,"divide by zero\n"); fflush(stdout);exit(1)
+#define ON_STACK_OVERFLOW fprintf(stdout,"C stack overflow in generated code\n"); fflush(stdout); exit(1)
+#define ON_SIGSEGV fprintf(stdout,"Segmentation fault/access violation in generated code\n"); fflush(stdout); exit(1)
+
+#if defined(__MINGW32__)
+extern jmp_buf seh_unwind_to;
+extern unsigned long seh_excn_code;
+/*
+ * install an exception handler 'exHandler' which longjmp()s (via 'jumpBuf')
+ * to the code 'onExnCaught' when successfully catching an exception.
+ *
+ * Macro based on Andrew Begel's SEH support code posted to the mingw-users
+ * mailing list.
+ */
+#define TRY_BEGIN(jumpBuf, exHandler, onExcnCaught)  \
+    do { \
+      int signal; \
+      if ((signal = setjmp(jumpBuf)) != 0) { \
+        onExcnCaught; \
+      } else { \
+        __try1(exHandler); \
+      } \
+    } while (0);
+
+#define TRY_END()  __except1
+
+extern
+EXCEPTION_DISPOSITION
+catchDivZero(struct _EXCEPTION_RECORD*,
+         void*,
+         struct _CONTEXT*,
+         void*);
+
+#define ON_EXCN \
+   if (seh_excn_code == 1) { \
+      ON_STACK_OVERFLOW; \
+   } else if ( seh_excn_code == 2 ) { \
+      ON_SIGSEGV; \
+   } else { \
+      ON_DIV_ZERO; \
+   }
+
+#define BEGIN_CATCH TRY_BEGIN(seh_unwind_to, catchDivZero, ON_EXCN)
+#define END_CATCH TRY_END()
+#elif defined(_MSC_VER)
+#define BEGIN_CATCH __try {
+#define END_CATCH   } __except ( ( ((GetExceptionCode() == EXCEPTION_FLT_DIVIDE_BY_ZERO) || (GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) || (GetExceptionCode() == EXCEPTION_STACK_OVERFLOW) || (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) ) { \
+   switch ( (GetExceptionCode()) ) { \
+    case EXCEPTION_FLT_DIVIDE_BY_ZERO: \
+    case EXCEPTION_INT_DIVIDE_BY_ZERO:
+          ON_DIV_ZERO; break; \
+    case EXCEPTION_STACK_OVERFLOW: \
+        ON_STACK_OVERFLOW; break; \
+    case EXCEPTION_ACCESS_VIOLATION: \
+        ON_SIGSEGV; break; \
+   } \
+  }
+#else
+#define BEGIN_CATCH /* nothing */
+#define END_CATCH   /* nothing */
+#endif
+
+#endif /* __SEH_EXCN_H__ */
+