Support signal handler on Windows
authorAndrew Schwartzmeyer <andrew@schwartzmeyer.com>
Tue, 27 Jun 2017 00:13:21 +0000 (17:13 -0700)
committerAndrew Schwartzmeyer <andrew@schwartzmeyer.com>
Tue, 27 Jun 2017 17:50:20 +0000 (10:50 -0700)
CMakeLists.txt
src/logging.cc
src/signalhandler.cc
src/signalhandler_unittest.cc
src/utilities.cc

index af4680d..7f28295 100644 (file)
@@ -364,9 +364,9 @@ set (GLOG_SRCS
   src/vlog_is_on.cc
 )
 
-if (HAVE_PTHREAD)
+if (HAVE_PTHREAD OR WIN32)
   list (APPEND GLOG_SRCS src/signalhandler.cc)
-endif (HAVE_PTHREAD)
+endif (HAVE_PTHREAD OR WIN32)
 
 if (WIN32)
   list (APPEND GLOG_SRCS
index 0b5e6ee..32a5791 100644 (file)
@@ -1463,16 +1463,13 @@ void LogMessage::RecordCrashReason(
 # define ATTRIBUTE_NORETURN
 #endif
 
+#if defined(OS_WINDOWS)
+__declspec(noreturn)
+#endif
 static void logging_fail() ATTRIBUTE_NORETURN;
 
 static void logging_fail() {
-#if defined(_DEBUG) && defined(_MSC_VER)
-  // When debugging on windows, avoid the obnoxious dialog and make
-  // it possible to continue past a LOG(FATAL) in the debugger
-  __debugbreak();
-#else
   abort();
-#endif
 }
 
 typedef void (*logging_fail_func_t)() ATTRIBUTE_NORETURN;
index a7aef8b..72fd92b 100644 (file)
@@ -48,9 +48,6 @@
 
 _START_GOOGLE_NAMESPACE_
 
-// TOOD(hamaji): Use signal instead of sigaction?
-#ifdef HAVE_SIGACTION
-
 namespace {
 
 // We'll install the failure signal handler for these signals.  We could
@@ -66,10 +63,14 @@ const struct {
   { SIGILL, "SIGILL" },
   { SIGFPE, "SIGFPE" },
   { SIGABRT, "SIGABRT" },
+#if !defined(OS_WINDOWS)
   { SIGBUS, "SIGBUS" },
+#endif
   { SIGTERM, "SIGTERM" },
 };
 
+static bool kFailureSignalHandlerInstalled = false;
+
 // Returns the program counter from signal context, NULL if unknown.
 void* GetPC(void* ucontext_in_void) {
 #if (defined(HAVE_UCONTEXT_H) || defined(HAVE_SYS_UCONTEXT_H)) && defined(PC_FROM_UCONTEXT)
@@ -168,6 +169,9 @@ void DumpTimeInfo() {
   g_failure_writer(buf, formatter.num_bytes_written());
 }
 
+// TOOD(hamaji): Use signal instead of sigaction?
+#ifdef HAVE_SIGACTION
+
 // Dumps information about the signal to STDERR.
 void DumpSignalInfo(int signal_number, siginfo_t *siginfo) {
   // Get the signal name.
@@ -213,6 +217,8 @@ void DumpSignalInfo(int signal_number, siginfo_t *siginfo) {
   g_failure_writer(buf, formatter.num_bytes_written());
 }
 
+#endif  // HAVE_SIGACTION
+
 // Dumps information about the stack frame to STDERR.
 void DumpStackFrameInfo(const char* prefix, void* pc) {
   // Get the symbol name.
@@ -240,12 +246,17 @@ void DumpStackFrameInfo(const char* prefix, void* pc) {
 
 // Invoke the default signal handler.
 void InvokeDefaultSignalHandler(int signal_number) {
+#ifdef HAVE_SIGACTION
   struct sigaction sig_action;
   memset(&sig_action, 0, sizeof(sig_action));
   sigemptyset(&sig_action.sa_mask);
   sig_action.sa_handler = SIG_DFL;
   sigaction(signal_number, &sig_action, NULL);
   kill(getpid(), signal_number);
+#elif defined(OS_WINDOWS)
+  signal(signal_number, SIG_DFL);
+  raise(signal_number);
+#endif
 }
 
 // This variable is used for protecting FailureSignalHandler() from
@@ -256,9 +267,14 @@ static pthread_t* g_entered_thread_id_pointer = NULL;
 
 // Dumps signal and stack frame information, and invokes the default
 // signal handler once our job is done.
+#if defined(OS_WINDOWS)
+void FailureSignalHandler(int signal_number)
+#else
 void FailureSignalHandler(int signal_number,
                           siginfo_t *signal_info,
-                          void *ucontext) {
+                          void *ucontext)
+#endif
+{
   // First check if we've already entered the function.  We use an atomic
   // compare and swap operation for platforms that support it.  For other
   // platforms, we use a naive method that could lead to a subtle race.
@@ -298,16 +314,20 @@ void FailureSignalHandler(int signal_number,
   // First dump time info.
   DumpTimeInfo();
 
+#if !defined(OS_WINDOWS)
   // Get the program counter from ucontext.
   void *pc = GetPC(ucontext);
   DumpStackFrameInfo("PC: ", pc);
+#endif
 
 #ifdef HAVE_STACKTRACE
   // Get the stack traces.
   void *stack[32];
   // +1 to exclude this function.
   const int depth = GetStackTrace(stack, ARRAYSIZE(stack), 1);
+# ifdef HAVE_SIGACTION
   DumpSignalInfo(signal_number, signal_info);
+# endif
   // Dump the stack traces.
   for (int i = 0; i < depth; ++i) {
     DumpStackFrameInfo("    ", stack[i]);
@@ -333,18 +353,19 @@ void FailureSignalHandler(int signal_number,
 
 }  // namespace
 
-#endif  // HAVE_SIGACTION
-
 namespace glog_internal_namespace_ {
 
 bool IsFailureSignalHandlerInstalled() {
 #ifdef HAVE_SIGACTION
+  // TODO(andschwa): Return kFailureSignalHandlerInstalled?
   struct sigaction sig_action;
   memset(&sig_action, 0, sizeof(sig_action));
   sigemptyset(&sig_action.sa_mask);
   sigaction(SIGABRT, NULL, &sig_action);
   if (sig_action.sa_sigaction == &FailureSignalHandler)
     return true;
+#elif defined(OS_WINDOWS)
+  return kFailureSignalHandlerInstalled;
 #endif  // HAVE_SIGACTION
   return false;
 }
@@ -363,11 +384,18 @@ void InstallFailureSignalHandler() {
   for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) {
     CHECK_ERR(sigaction(kFailureSignals[i].number, &sig_action, NULL));
   }
+  kFailureSignalHandlerInstalled = true;
+#elif defined(OS_WINDOWS)
+  for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) {
+    CHECK_NE(signal(kFailureSignals[i].number, &FailureSignalHandler),
+             SIG_ERR);
+  }
+  kFailureSignalHandlerInstalled = true;
 #endif  // HAVE_SIGACTION
 }
 
 void InstallFailureWriter(void (*writer)(const char* data, int size)) {
-#ifdef HAVE_SIGACTION
+#if defined(HAVE_SIGACTION) || defined(OS_WINDOWS)
   g_failure_writer = writer;
 #endif  // HAVE_SIGACTION
 }
index 59be231..e85f523 100644 (file)
@@ -34,7 +34,9 @@
 
 #include "utilities.h"
 
-#include <pthread.h>
+#if defined(HAVE_PTHREAD)
+# include <pthread.h>
+#endif
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -87,12 +89,20 @@ int main(int argc, char **argv) {
     fprintf(stderr, "looping\n");
     while (true);
   } else if (command == "die_in_thread") {
+#if defined(HAVE_PTHREAD)
     pthread_t thread;
     pthread_create(&thread, NULL, &DieInThread, NULL);
     pthread_join(thread, NULL);
+#else
+    fprintf(stderr, "no pthread\n");
+    return 1;
+#endif
   } else if (command == "dump_to_stdout") {
     InstallFailureWriter(WriteToStdout);
     abort();
+  } else if (command == "installed") {
+    fprintf(stderr, "signal handler installed: %s\n",
+        IsFailureSignalHandlerInstalled() ? "true" : "false");
   } else {
     // Tell the shell script
     puts("OK");
index 0d686eb..e73cee0 100644 (file)
@@ -137,17 +137,19 @@ static void DumpStackTraceAndExit() {
   DumpStackTrace(1, DebugWriteToStderr, NULL);
 
   // TOOD(hamaji): Use signal instead of sigaction?
-#ifdef HAVE_SIGACTION
   if (IsFailureSignalHandlerInstalled()) {
     // Set the default signal handler for SIGABRT, to avoid invoking our
     // own signal handler installed by InstallFailureSignalHandler().
+#ifdef HAVE_SIGACTION
     struct sigaction sig_action;
     memset(&sig_action, 0, sizeof(sig_action));
     sigemptyset(&sig_action.sa_mask);
     sig_action.sa_handler = SIG_DFL;
     sigaction(SIGABRT, &sig_action, NULL);
-  }
+#elif defined(OS_WINDOWS)
+    signal(SIGABRT, SIG_DFL);
 #endif  // HAVE_SIGACTION
+  }
 
   abort();
 }