Support symbolizer and demangler on Windows
This commit is contained in:
parent
e5d36443c6
commit
f1d64f7deb
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user