summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Schwartzmeyer <andrew@schwartzmeyer.com>2017-03-03 16:50:08 -0800
committerAndrew Schwartzmeyer <andrew@schwartzmeyer.com>2017-06-27 10:50:20 -0700
commitf1d64f7debcbf236e73b8ccef942ffbd81ec1929 (patch)
tree41224412c7940f7241c82ae6c71dbcb447cfa7c4
parente5d36443c6d03453f985721a6b16b4f1749846c4 (diff)
downloadglog-f1d64f7debcbf236e73b8ccef942ffbd81ec1929.tar.gz
glog-f1d64f7debcbf236e73b8ccef942ffbd81ec1929.tar.bz2
glog-f1d64f7debcbf236e73b8ccef942ffbd81ec1929.zip
Support symbolizer and demangler on Windows
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/demangle.cc31
-rw-r--r--src/demangle_unittest.cc41
-rw-r--r--src/stacktrace_windows-inl.h42
-rw-r--r--src/symbolize.cc60
-rw-r--r--src/symbolize_unittest.cc74
-rw-r--r--src/utilities.h3
7 files changed, 181 insertions, 71 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d353c00..b8f8bd8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -457,6 +457,7 @@ endif (HAVE_EXECINFO_H)
if (WIN32)
set (HAVE_STACKTRACE 1)
+ set (HAVE_SYMBOLIZE 1)
target_link_libraries (glog PUBLIC Dbghelp.lib)
endif (WIN32)
diff --git a/src/demangle.cc b/src/demangle.cc
index e858181..7852043 100644
--- a/src/demangle.cc
+++ b/src/demangle.cc
@@ -35,10 +35,16 @@
// Note that we only have partial C++0x support yet.
#include <stdio.h> // for NULL
+#include "utilities.h"
#include "demangle.h"
+#if defined(OS_WINDOWS)
+#include <DbgHelp.h>
+#endif
+
_START_GOOGLE_NAMESPACE_
+#if !defined(OS_WINDOWS)
typedef struct {
const char *abbrev;
const char *real_name;
@@ -1293,12 +1299,37 @@ static bool ParseTopLevelMangledName(State *state) {
}
return false;
}
+#endif
// The demangler entry point.
bool Demangle(const char *mangled, char *out, int out_size) {
+#if defined(OS_WINDOWS)
+ // When built with incremental linking, the Windows debugger
+ // library provides a more complicated `Symbol->Name` with the
+ // Incremental Linking Table offset, which looks like
+ // `@ILT+1105(?func@Foo@@SAXH@Z)`. However, the demangler expects
+ // only the mangled symbol, `?func@Foo@@SAXH@Z`. Fortunately, the
+ // mangled symbol is guaranteed not to have parentheses,
+ // so we search for `(` and extract up to `)`.
+ //
+ // Since we may be in a signal handler here, we cannot use `std::string`.
+ char buffer[1024]; // Big enough for a sane symbol.
+ const char *lparen = strchr(mangled, '(');
+ if (lparen) {
+ // Extract the string `(?...)`
+ const char *rparen = strchr(lparen, ')');
+ size_t length = rparen - lparen - 1;
+ strncpy(buffer, lparen + 1, length);
+ buffer[length] = '\0';
+ mangled = buffer;
+ } // Else the symbol wasn't inside a set of parentheses
+ // We use the ANSI version to ensure the string type is always `char *`.
+ return UnDecorateSymbolName(mangled, out, out_size, UNDNAME_COMPLETE);
+#else
State state;
InitState(&state, mangled, out, out_size);
return ParseTopLevelMangledName(&state) && !state.overflowed;
+#endif
}
_END_GOOGLE_NAMESPACE_
diff --git a/src/demangle_unittest.cc b/src/demangle_unittest.cc
index 32f3221..be48341 100644
--- a/src/demangle_unittest.cc
+++ b/src/demangle_unittest.cc
@@ -62,18 +62,37 @@ static const char *DemangleIt(const char * const mangled) {
}
}
+#if defined(OS_WINDOWS)
+
+TEST(Demangle, Windows) {
+ EXPECT_STREQ(
+ "public: static void __cdecl Foo::func(int)",
+ DemangleIt("?func@Foo@@SAXH@Z"));
+ EXPECT_STREQ(
+ "public: static void __cdecl Foo::func(int)",
+ DemangleIt("@ILT+1105(?func@Foo@@SAXH@Z)"));
+ EXPECT_STREQ(
+ "int __cdecl foobarArray(int * const)",
+ DemangleIt("?foobarArray@@YAHQAH@Z"));
+}
+
+#else
+
// Test corner cases of bounary conditions.
TEST(Demangle, CornerCases) {
- char tmp[10];
- EXPECT_TRUE(Demangle("_Z6foobarv", tmp, sizeof(tmp)));
- // sizeof("foobar()") == 9
- EXPECT_STREQ("foobar()", tmp);
- EXPECT_TRUE(Demangle("_Z6foobarv", tmp, 9));
- EXPECT_STREQ("foobar()", tmp);
- EXPECT_FALSE(Demangle("_Z6foobarv", tmp, 8)); // Not enough.
- EXPECT_FALSE(Demangle("_Z6foobarv", tmp, 1));
- EXPECT_FALSE(Demangle("_Z6foobarv", tmp, 0));
- EXPECT_FALSE(Demangle("_Z6foobarv", NULL, 0)); // Should not cause SEGV.
+ const size_t size = 10;
+ char tmp[size] = { 0 };
+ const char *demangled = "foobar()";
+ const char *mangled = "_Z6foobarv";
+ EXPECT_TRUE(Demangle(mangled, tmp, sizeof(tmp)));
+ // sizeof("foobar()") == size - 1
+ EXPECT_STREQ(demangled, tmp);
+ EXPECT_TRUE(Demangle(mangled, tmp, size - 1));
+ EXPECT_STREQ(demangled, tmp);
+ EXPECT_FALSE(Demangle(mangled, tmp, size - 2)); // Not enough.
+ EXPECT_FALSE(Demangle(mangled, tmp, 1));
+ EXPECT_FALSE(Demangle(mangled, tmp, 0));
+ EXPECT_FALSE(Demangle(mangled, NULL, 0)); // Should not cause SEGV.
}
// Test handling of functions suffixed with .clone.N, which is used by GCC
@@ -123,6 +142,8 @@ TEST(Demangle, FromFile) {
}
}
+#endif
+
int main(int argc, char **argv) {
#ifdef HAVE_LIB_GFLAGS
ParseCommandLineFlags(&argc, &argv, true);
diff --git a/src/stacktrace_windows-inl.h b/src/stacktrace_windows-inl.h
index 5606408..f329a7c 100644
--- a/src/stacktrace_windows-inl.h
+++ b/src/stacktrace_windows-inl.h
@@ -33,54 +33,18 @@
#include "port.h"
#include "stacktrace.h"
-#include "Dbghelp.h"
-#include <vector>
+#include <DbgHelp.h>
_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) {
- if (!ready_to_run) {
- return 0;
- }
- skip_count++; // we want to skip the current frame as well
if (max_depth > 64) {
max_depth = 64;
}
- std::vector<void*> stack(max_depth);
+ skip_count++; // we want to skip the current frame as well
// 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 size;
+ return CaptureStackBackTrace(skip_count, max_depth, result, NULL);
}
_END_GOOGLE_NAMESPACE_
diff --git a/src/symbolize.cc b/src/symbolize.cc
index f83c309..24dcddc 100644
--- a/src/symbolize.cc
+++ b/src/symbolize.cc
@@ -837,6 +837,66 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
_END_GOOGLE_NAMESPACE_
+#elif defined(OS_WINDOWS)
+
+#include <DbgHelp.h>
+
+_START_GOOGLE_NAMESPACE_
+
+class SymInitializer {
+public:
+ HANDLE process = NULL;
+ bool ready = false;
+ SymInitializer() {
+ // Initialize the symbol handler.
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680344(v=vs.85).aspx
+ process = GetCurrentProcess();
+ // Defer symbol loading.
+ // We do not request undecorated symbols with SYMOPT_UNDNAME
+ // because the mangling library calls UnDecorateSymbolName.
+ SymSetOptions(SYMOPT_DEFERRED_LOADS);
+ if (SymInitialize(process, NULL, true)) {
+ ready = true;
+ }
+ }
+ ~SymInitializer() {
+ SymCleanup(process);
+ // We do not need to close `HANDLE process` because it's a "pseudo handle."
+ }
+private:
+ SymInitializer(const SymInitializer&);
+ SymInitializer& operator=(const SymInitializer&);
+};
+
+static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
+ int out_size) {
+ const static SymInitializer symInitializer;
+ if (!symInitializer.ready) {
+ return false;
+ }
+ // Resolve symbol information from address.
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680578(v=vs.85).aspx
+ char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
+ SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO *>(buf);
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ symbol->MaxNameLen = MAX_SYM_NAME;
+ // We use the ANSI version to ensure the string type is always `char *`.
+ // This could break if a symbol has Unicode in it.
+ BOOL ret = SymFromAddr(symInitializer.process,
+ reinterpret_cast<DWORD64>(pc), 0, symbol);
+ if (ret == 1 && static_cast<int>(symbol->NameLen) < out_size) {
+ // `NameLen` does not include the null terminating character.
+ strncpy(out, symbol->Name, static_cast<size_t>(symbol->NameLen) + 1);
+ out[static_cast<size_t>(symbol->NameLen)] = '\0';
+ // Symbolization succeeded. Now we try to demangle the symbol.
+ DemangleInplace(out, out_size);
+ return true;
+ }
+ return false;
+}
+
+_END_GOOGLE_NAMESPACE_
+
#else
# error BUG: HAVE_SYMBOLIZE was wrongly set
#endif
diff --git a/src/symbolize_unittest.cc b/src/symbolize_unittest.cc
index bdd2f03..a0a9737 100644
--- a/src/symbolize_unittest.cc
+++ b/src/symbolize_unittest.cc
@@ -49,10 +49,22 @@ using namespace GFLAGS_NAMESPACE;
using namespace std;
using namespace GOOGLE_NAMESPACE;
-#if defined(HAVE_STACKTRACE) && defined(__ELF__)
+#if defined(HAVE_STACKTRACE)
#define always_inline
+// A wrapper function for Symbolize() to make the unit test simple.
+static const char *TrySymbolize(void *pc) {
+ static char symbol[4096];
+ if (Symbolize(pc, symbol, sizeof(symbol))) {
+ return symbol;
+ } else {
+ return NULL;
+ }
+}
+
+# if defined(__ELF__)
+
// This unit tests make sense only with GCC.
// Uses lots of GCC specific features.
#if defined(__GNUC__) && !defined(__OPENCC__)
@@ -70,16 +82,6 @@ using namespace GOOGLE_NAMESPACE;
# endif // defined(__i386__) || defined(__x86_64__)
#endif
-// A wrapper function for Symbolize() to make the unit test simple.
-static const char *TrySymbolize(void *pc) {
- static char symbol[4096];
- if (Symbolize(pc, symbol, sizeof(symbol))) {
- return symbol;
- } else {
- return NULL;
- }
-}
-
// Make them C linkage to avoid mangled names.
extern "C" {
void nonstatic_func() {
@@ -355,11 +357,42 @@ void ATTRIBUTE_NOINLINE TestWithReturnAddress() {
#endif
}
+# elif defined(OS_WINDOWS)
+
+#include <intrin.h>
+#pragma intrinsic(_ReturnAddress)
+
+struct Foo {
+ static void func(int x);
+};
+
+__declspec(noinline) void Foo::func(int x) {
+ volatile int a = x;
+ ++a;
+}
+
+TEST(Symbolize, SymbolizeWithDemangling) {
+ Foo::func(100);
+ const char* ret = TrySymbolize((void *)(&Foo::func));
+ EXPECT_STREQ("public: static void __cdecl Foo::func(int)", ret);
+}
+
+__declspec(noinline) void TestWithReturnAddress() {
+ void *return_address = _ReturnAddress();
+ const char *symbol = TrySymbolize(return_address);
+ CHECK(symbol != NULL);
+ CHECK_STREQ(symbol, "main");
+ cout << "Test case TestWithReturnAddress passed." << endl;
+}
+# endif // __ELF__
+#endif // HAVE_STACKTRACE
+
int main(int argc, char **argv) {
FLAGS_logtostderr = true;
InitGoogleLogging(argv[0]);
InitGoogleTest(&argc, argv);
-#ifdef HAVE_SYMBOLIZE
+#if defined(HAVE_SYMBOLIZE)
+# if defined(__ELF__)
// We don't want to get affected by the callback interface, that may be
// used to install some callback function at InitGoogle() time.
InstallSymbolizeCallback(NULL);
@@ -368,18 +401,15 @@ int main(int argc, char **argv) {
TestWithPCInsideNonInlineFunction();
TestWithReturnAddress();
return RUN_ALL_TESTS();
-#else
- return 0;
-#endif
-}
-
-#else
-int main() {
-#ifdef HAVE_SYMBOLIZE
+# elif defined(OS_WINDOWS)
+ TestWithReturnAddress();
+ return RUN_ALL_TESTS();
+# else // OS_WINDOWS
printf("PASS (no symbolize_unittest support)\n");
+ return 0;
+# endif // __ELF__
#else
printf("PASS (no symbolize support)\n");
-#endif
return 0;
+#endif // HAVE_SYMBOLIZE
}
-#endif // HAVE_STACKTRACE
diff --git a/src/utilities.h b/src/utilities.h
index a10735b..be2cff4 100644
--- a/src/utilities.h
+++ b/src/utilities.h
@@ -133,6 +133,9 @@
#elif defined(OS_MACOSX) && defined(HAVE_DLADDR)
// Use dladdr to symbolize.
# define HAVE_SYMBOLIZE
+#elif defined(OS_WINDOWS)
+// Use DbgHelp to symbolize
+# define HAVE_SYMBOLIZE
#endif
#ifndef ARRAYSIZE