Support stacktrace on Windows
authorAndrew Schwartzmeyer <andrew@schwartzmeyer.com>
Wed, 21 Dec 2016 21:48:58 +0000 (13:48 -0800)
committerAndrew Schwartzmeyer <andrew@schwartzmeyer.com>
Tue, 27 Jun 2017 01:47:34 +0000 (18:47 -0700)
CMakeLists.txt
src/stacktrace_windows-inl.h
src/utilities.h

index 7415eab..d353c00 100644 (file)
@@ -455,6 +455,11 @@ if (HAVE_EXECINFO_H)
   set (HAVE_STACKTRACE 1)
 endif (HAVE_EXECINFO_H)
 
+if (WIN32)
+  set (HAVE_STACKTRACE 1)
+  target_link_libraries (glog PUBLIC Dbghelp.lib)
+endif (WIN32)
+
 if (UNIX OR (APPLE AND HAVE_DLADDR))
   set (HAVE_SYMBOLIZE 1)
 endif (UNIX OR (APPLE AND HAVE_DLADDR))
index fad81d3..5606408 100644 (file)
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
-// Portable implementation - just use glibc
+// Author: Andrew Schwartzmeyer
 //
-// Note:  The glibc implementation may cause a call to malloc.
-// This can cause a deadlock in HeapProfiler.
-#include <execinfo.h>
-#include <string.h>
+// Windows implementation - just use CaptureStackBackTrace
+
+#include "port.h"
 #include "stacktrace.h"
+#include "Dbghelp.h"
+#include <vector>
 
 _START_GOOGLE_NAMESPACE_
 
+static bool ready_to_run = false;
+class StackTraceInit {
+public:
+  HANDLE hProcess;
+  StackTraceInit() {
+    // Initialize the symbol handler
+    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680344(v=vs.85).aspx
+    hProcess = GetCurrentProcess();
+    SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
+    SymInitialize(hProcess, NULL, true);
+    ready_to_run = true;
+  }
+  ~StackTraceInit() {
+    SymCleanup(hProcess);
+    ready_to_run = false;
+  }
+};
+
+static const StackTraceInit module_initializer;  // Force initialization
+
 // If you change this function, also change GetStackFrames below.
 int GetStackTrace(void** result, int max_depth, int skip_count) {
-  static const int kStackLength = 64;
-  void * stack[kStackLength];
-  int size;
-
-  size = backtrace(stack, kStackLength);
+  if (!ready_to_run) {
+    return 0;
+  }
   skip_count++;  // we want to skip the current frame as well
-  int result_count = size - skip_count;
-  if (result_count < 0)
-    result_count = 0;
-  if (result_count > max_depth)
-    result_count = max_depth;
-  for (int i = 0; i < result_count; i++)
-    result[i] = stack[i + skip_count];
+  if (max_depth > 64) {
+    max_depth = 64;
+  }
+  std::vector<void*> stack(max_depth);
+  // This API is thread-safe (moreover it walks only the current thread).
+  int size = CaptureStackBackTrace(skip_count, max_depth, &stack[0], NULL);
+  for (int i = 0; i < size; ++i) {
+    // Resolve symbol information from address.
+    char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
+    SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(buffer);
+    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+    symbol->MaxNameLen = MAX_SYM_NAME;
+    SymFromAddr(module_initializer.hProcess, reinterpret_cast<DWORD64>(stack[i]), 0, symbol);
+    result[i] = stack[i];
+  }
 
-  return result_count;
+  return size;
 }
 
 _END_GOOGLE_NAMESPACE_
index 101ca64..a10735b 100644 (file)
@@ -99,6 +99,8 @@
 //    malloc() from the unwinder.  This is a problem because we're
 //    trying to use the unwinder to instrument malloc().
 //
+// 4) The Windows API CaptureStackTrace.
+//
 // Note: if you add a new implementation here, make sure it works
 // correctly when GetStackTrace() is called with max_depth == 0.
 // Some code may do that.
 #  define STACKTRACE_H "stacktrace_x86_64-inl.h"
 # elif (defined(__ppc__) || defined(__PPC__)) && __GNUC__ >= 2
 #  define STACKTRACE_H "stacktrace_powerpc-inl.h"
+# elif defined(OS_WINDOWS)
+#  define STACKTRACE_H "stacktrace_windows-inl.h"
 # endif
 #endif
 
@@ -143,6 +147,9 @@ namespace glog_internal_namespace_ {
 #ifdef HAVE___ATTRIBUTE__
 # define ATTRIBUTE_NOINLINE __attribute__ ((noinline))
 # define HAVE_ATTRIBUTE_NOINLINE
+#elif defined(OS_WINDOWS)
+# define ATTRIBUTE_NOINLINE __declspec(noinline)
+# define HAVE_ATTRIBUTE_NOINLINE
 #else
 # define ATTRIBUTE_NOINLINE
 #endif