Commit f1d64f7d authored by Andrew Schwartzmeyer's avatar Andrew Schwartzmeyer

Support symbolizer and demangler on Windows

parent e5d36443
......@@ -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)
......
......@@ -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_
......@@ -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);
......
......@@ -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_
......@@ -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
......
......@@ -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
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment