| diff --git c/CMakeLists.txt w/CMakeLists.txt |
| index 7ede6e7ad..1b05456e4 100644 |
| --- c/CMakeLists.txt |
| +++ w/CMakeLists.txt |
| @@ -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 |
| @@ -455,6 +455,12 @@ if (HAVE_EXECINFO_H) |
| set (HAVE_STACKTRACE 1) |
| endif (HAVE_EXECINFO_H) |
| |
| +if (WIN32) |
| + set (HAVE_STACKTRACE 1) |
| + set (HAVE_SYMBOLIZE 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)) |
| @@ -527,13 +533,13 @@ if (BUILD_TESTING) |
| |
| target_link_libraries (utilities_unittest PRIVATE glog) |
| |
| - if (HAVE_STACKTRACE AND HAVE_SYMBOLIZE) |
| + if (HAVE_STACKTRACE AND HAVE_SYMBOLIZE AND NOT WIN32) |
| add_executable (signalhandler_unittest |
| src/signalhandler_unittest.cc |
| ) |
| |
| target_link_libraries (signalhandler_unittest PRIVATE glog) |
| - endif (HAVE_STACKTRACE AND HAVE_SYMBOLIZE) |
| + endif (HAVE_STACKTRACE AND HAVE_SYMBOLIZE AND NOT WIN32) |
| |
| add_test (NAME demangle COMMAND demangle_unittest) |
| add_test (NAME logging COMMAND logging_unittest) |
| diff --git c/src/demangle.cc w/src/demangle.cc |
| index e858181a6..cf897457a 100644 |
| --- c/src/demangle.cc |
| +++ w/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,32 @@ 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 `)`. |
| + std::string symbol(mangled); |
| + size_t mark = symbol.find('('); |
| + if (mark != std::string::npos) { |
| + // Extract the string `(?...)` |
| + std::string temp = symbol.substr(mark); |
| + // Remove the surrounding parentheses |
| + symbol = temp.substr(1, temp.size() - 2); |
| + } // Else the symbol wasn't inside a set of parentheses |
| + return UnDecorateSymbolName(symbol.c_str(), 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 c/src/demangle_unittest.cc w/src/demangle_unittest.cc |
| index 32f322101..be483411f 100644 |
| --- c/src/demangle_unittest.cc |
| +++ w/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 c/src/logging.cc w/src/logging.cc |
| index 0b5e6ee97..aa8a95eda 100644 |
| --- c/src/logging.cc |
| +++ w/src/logging.cc |
| @@ -92,6 +92,15 @@ using std::fdopen; |
| #define fdopen _fdopen |
| #endif |
| |
| +#if defined(OS_WINDOWS) |
| +_START_GOOGLE_NAMESPACE_ |
| +namespace glog_internal_namespace_ { |
| + void DumpTimeInfo(); |
| + void DumpStackInfo(int); |
| +} |
| +_END_GOOGLE_NAMESPACE_ |
| +#endif |
| + |
| // There is no thread annotation support. |
| #define EXCLUSIVE_LOCKS_REQUIRED(mu) |
| |
| @@ -1466,8 +1475,17 @@ void LogMessage::RecordCrashReason( |
| static void logging_fail() ATTRIBUTE_NORETURN; |
| |
| static void logging_fail() { |
| +#if defined(OS_WINDOWS) |
| + // On Windows,the stack trace info needs to be dumped here instead |
| + // of in the `SIGABRT` signal handler to avoid thread switching. |
| + // The signal handler runs on the thread on which it was installed, |
| + // making the stack trace irrelevant. |
| + glog_internal_namespace_::DumpTimeInfo(); |
| + glog_internal_namespace_::DumpStackInfo(5); |
| + FlushLogFilesUnsafe(0); |
| +#endif |
| #if defined(_DEBUG) && defined(_MSC_VER) |
| - // When debugging on windows, avoid the obnoxious dialog and make |
| + // When debugging on Windows, avoid the obnoxious dialog and make |
| // it possible to continue past a LOG(FATAL) in the debugger |
| __debugbreak(); |
| #else |
| diff --git c/src/signalhandler.cc w/src/signalhandler.cc |
| index a7aef8b99..a28b8b7c6 100644 |
| --- c/src/signalhandler.cc |
| +++ w/src/signalhandler.cc |
| @@ -48,38 +48,11 @@ |
| |
| _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 |
| -// use strsignal() to get signal names, but we don't use it to avoid |
| -// introducing yet another #ifdef complication. |
| -// |
| -// The list should be synced with the comment in signalhandler.h. |
| -const struct { |
| - int number; |
| - const char *name; |
| -} kFailureSignals[] = { |
| - { SIGSEGV, "SIGSEGV" }, |
| - { SIGILL, "SIGILL" }, |
| - { SIGFPE, "SIGFPE" }, |
| - { SIGABRT, "SIGABRT" }, |
| - { SIGBUS, "SIGBUS" }, |
| - { SIGTERM, "SIGTERM" }, |
| -}; |
| +namespace glog_internal_namespace_ { |
| |
| -// 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) |
| - if (ucontext_in_void != NULL) { |
| - ucontext_t *context = reinterpret_cast<ucontext_t *>(ucontext_in_void); |
| - return (void*)context->PC_FROM_UCONTEXT; |
| - } |
| +#ifdef HAVE_SIGACTION |
| + void DumpSignalInfo(int signal_number, siginfo_t *siginfo); |
| #endif |
| - return NULL; |
| -} |
| |
| // The class is used for formatting error messages. We don't use printf() |
| // as it's not async signal safe. |
| @@ -168,6 +141,76 @@ void DumpTimeInfo() { |
| g_failure_writer(buf, formatter.num_bytes_written()); |
| } |
| |
| +// Dumps information about the stack frame to STDERR. |
| +void DumpStackFrameInfo(const char* prefix, void* pc) { |
| + // Get the symbol name. |
| + const char *symbol = "(unknown)"; |
| + char symbolized[1024] = {}; // Big enough for a sane symbol. |
| + // Symbolizes the previous address of pc because pc may be in the |
| + // next function. |
| + if (Symbolize(reinterpret_cast<char *>(pc) - 1, |
| + symbolized, sizeof(symbolized))) { |
| + symbol = symbolized; |
| + } |
| + |
| + char buf[1024]; // Big enough for stack frame info. |
| + MinimalFormatter formatter(buf, sizeof(buf)); |
| + |
| + formatter.AppendString(prefix); |
| + formatter.AppendString("@ "); |
| + const int width = 2 * sizeof(void*) + 2; // + 2 for "0x". |
| + formatter.AppendHexWithPadding(reinterpret_cast<uintptr_t>(pc), width); |
| + formatter.AppendString(" "); |
| + formatter.AppendString(symbol); |
| + formatter.AppendString("\n"); |
| + g_failure_writer(buf, formatter.num_bytes_written()); |
| +} |
| + |
| +// Argument is the number of frames to skip, excluding this function. |
| +void DumpStackInfo(int frames) { |
| +#ifdef HAVE_STACKTRACE |
| + // Get the stack traces. |
| + void *stack[32]; |
| + // +1 to exclude this function. |
| + const int depth = GetStackTrace(stack, ARRAYSIZE(stack), frames + 1); |
| + // Dump the stack traces. |
| + for (int i = 0; i < depth; ++i) { |
| + DumpStackFrameInfo(" ", stack[i]); |
| + } |
| +#endif |
| +} |
| + |
| +// TOOD(hamaji): Use signal instead of sigaction? |
| +#ifdef HAVE_SIGACTION |
| + |
| +// We'll install the failure signal handler for these signals. We could |
| +// use strsignal() to get signal names, but we don't use it to avoid |
| +// introducing yet another #ifdef complication. |
| +// |
| +// The list should be synced with the comment in signalhandler.h. |
| +const struct { |
| + int number; |
| + const char *name; |
| +} kFailureSignals[] = { |
| + { SIGSEGV, "SIGSEGV" }, |
| + { SIGILL, "SIGILL" }, |
| + { SIGFPE, "SIGFPE" }, |
| + { SIGABRT, "SIGABRT" }, |
| + { SIGBUS, "SIGBUS" }, |
| + { SIGTERM, "SIGTERM" }, |
| +}; |
| + |
| +// 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) |
| + if (ucontext_in_void != NULL) { |
| + ucontext_t *context = reinterpret_cast<ucontext_t *>(ucontext_in_void); |
| + return (void*)context->PC_FROM_UCONTEXT; |
| + } |
| +#endif |
| + return NULL; |
| +} |
| + |
| // Dumps information about the signal to STDERR. |
| void DumpSignalInfo(int signal_number, siginfo_t *siginfo) { |
| // Get the signal name. |
| @@ -213,31 +256,6 @@ void DumpSignalInfo(int signal_number, siginfo_t *siginfo) { |
| g_failure_writer(buf, formatter.num_bytes_written()); |
| } |
| |
| -// Dumps information about the stack frame to STDERR. |
| -void DumpStackFrameInfo(const char* prefix, void* pc) { |
| - // Get the symbol name. |
| - const char *symbol = "(unknown)"; |
| - char symbolized[1024]; // Big enough for a sane symbol. |
| - // Symbolizes the previous address of pc because pc may be in the |
| - // next function. |
| - if (Symbolize(reinterpret_cast<char *>(pc) - 1, |
| - symbolized, sizeof(symbolized))) { |
| - symbol = symbolized; |
| - } |
| - |
| - char buf[1024]; // Big enough for stack frame info. |
| - MinimalFormatter formatter(buf, sizeof(buf)); |
| - |
| - formatter.AppendString(prefix); |
| - formatter.AppendString("@ "); |
| - const int width = 2 * sizeof(void*) + 2; // + 2 for "0x". |
| - formatter.AppendHexWithPadding(reinterpret_cast<uintptr_t>(pc), width); |
| - formatter.AppendString(" "); |
| - formatter.AppendString(symbol); |
| - formatter.AppendString("\n"); |
| - g_failure_writer(buf, formatter.num_bytes_written()); |
| -} |
| - |
| // Invoke the default signal handler. |
| void InvokeDefaultSignalHandler(int signal_number) { |
| struct sigaction sig_action; |
| @@ -302,18 +320,14 @@ void FailureSignalHandler(int signal_number, |
| void *pc = GetPC(ucontext); |
| DumpStackFrameInfo("PC: ", pc); |
| |
| -#ifdef HAVE_STACKTRACE |
| - // Get the stack traces. |
| - void *stack[32]; |
| - // +1 to exclude this function. |
| - const int depth = GetStackTrace(stack, ARRAYSIZE(stack), 1); |
| + // Now dumb the signal info. |
| +#ifdef HAVE_SIGACTION |
| DumpSignalInfo(signal_number, signal_info); |
| - // Dump the stack traces. |
| - for (int i = 0; i < depth; ++i) { |
| - DumpStackFrameInfo(" ", stack[i]); |
| - } |
| #endif |
| |
| + // Now dump the stack trace. |
| + DumpStackInfo(0); |
| + |
| // *** TRANSITION *** |
| // |
| // BEFORE this point, all code must be async-termination-safe! |
| @@ -330,12 +344,15 @@ void FailureSignalHandler(int signal_number, |
| // Kill ourself by the default signal handler. |
| InvokeDefaultSignalHandler(signal_number); |
| } |
| - |
| -} // namespace |
| - |
| +#elif defined(OS_WINDOWS) |
| +void FailureSignalHandler(int signal) { |
| + if (signal != SIGABRT) { |
| + return; |
| + } |
| + exit(1); |
| +} |
| #endif // HAVE_SIGACTION |
| |
| -namespace glog_internal_namespace_ { |
| |
| bool IsFailureSignalHandlerInstalled() { |
| #ifdef HAVE_SIGACTION |
| @@ -345,6 +362,9 @@ bool IsFailureSignalHandlerInstalled() { |
| sigaction(SIGABRT, NULL, &sig_action); |
| if (sig_action.sa_sigaction == &FailureSignalHandler) |
| return true; |
| +#elif defined(OS_WINDOWS) |
| + // TODO: check if we installed it |
| + return true; |
| #endif // HAVE_SIGACTION |
| return false; |
| } |
| @@ -363,11 +383,15 @@ void InstallFailureSignalHandler() { |
| for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) { |
| CHECK_ERR(sigaction(kFailureSignals[i].number, &sig_action, NULL)); |
| } |
| +#elif defined(OS_WINDOWS) |
| + typedef void (*SignalHandlerPointer)(int); |
| + SignalHandlerPointer previousHandler; |
| + previousHandler = signal(SIGABRT, &FailureSignalHandler); |
| #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 |
| } |
| diff --git c/src/stacktrace_unittest.cc w/src/stacktrace_unittest.cc |
| index c1b3b36ff..ed95f8945 100644 |
| --- c/src/stacktrace_unittest.cc |
| +++ w/src/stacktrace_unittest.cc |
| @@ -90,6 +90,32 @@ AddressRange expected_range[BACKTRACE_STEPS]; |
| (prange)->end = ra; \ |
| } \ |
| } while (0) |
| +#elif defined(OS_WINDOWS) |
| +// Compiler Intrinsic _ReturnAddress documentation: |
| +// https://msdn.microsoft.com/en-us/library/64ez38eh(v=vs.140).aspx |
| +// This is equivalent to __builtin_return_address. |
| +#include <intrin.h> |
| +#pragma intrinsic(_ReturnAddress) |
| +#define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange) \ |
| + do { \ |
| + (prange)->start = &fn; \ |
| + (prange)->end = _ReturnAddress(); \ |
| + CHECK_LT((prange)->start, (prange)->end); \ |
| + } while (0) |
| +#define DECLARE_ADDRESS_LABEL(a_label) do { } while (0) |
| +// MSVC may do the same thing as GCC (as noted above). |
| +// Adjust function range from _ReturnAddress. |
| +#define ADJUST_ADDRESS_RANGE_FROM_RA(prange) \ |
| + do { \ |
| + void *ra = _ReturnAddress(); \ |
| + CHECK_LT((prange)->start, ra); \ |
| + if (ra > (prange)->end) { \ |
| + printf("Adjusting range from %p..%p to %p..%p\n", \ |
| + (prange)->start, (prange)->end, \ |
| + (prange)->start, ra); \ |
| + (prange)->end = ra; \ |
| + } \ |
| + } while (0) |
| #else |
| // Assume the Check* functions below are not longer than 256 bytes. |
| #define INIT_ADDRESS_RANGE(fn, start_label, end_label, prange) \ |
| diff --git c/src/stacktrace_windows-inl.h w/src/stacktrace_windows-inl.h |
| new file mode 100644 |
| index 000000000..dc23d109d |
| --- /dev/null |
| +++ w/src/stacktrace_windows-inl.h |
| @@ -0,0 +1,49 @@ |
| +// Copyright (c) 2000 - 2007, Google Inc. |
| +// All rights reserved. |
| +// |
| +// Redistribution and use in source and binary forms, with or without |
| +// modification, are permitted provided that the following conditions are |
| +// met: |
| +// |
| +// * Redistributions of source code must retain the above copyright |
| +// notice, this list of conditions and the following disclaimer. |
| +// * Redistributions in binary form must reproduce the above |
| +// copyright notice, this list of conditions and the following disclaimer |
| +// in the documentation and/or other materials provided with the |
| +// distribution. |
| +// * Neither the name of Google Inc. nor the names of its |
| +// contributors may be used to endorse or promote products derived from |
| +// this software without specific prior written permission. |
| +// |
| +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| +// |
| +// Author: Andrew Schwartzmeyer |
| +// |
| +// Windows implementation - just use CaptureStackBackTrace |
| + |
| +#include "port.h" |
| +#include "stacktrace.h" |
| +#include "Dbghelp.h" |
| + |
| +_START_GOOGLE_NAMESPACE_ |
| + |
| +int GetStackTrace(void** result, int max_depth, int skip_count) { |
| + if (max_depth > 64) { |
| + max_depth = 64; |
| + } |
| + skip_count++; // we want to skip the current frame as well |
| + // This API is thread-safe (moreover it walks only the current thread). |
| + return CaptureStackBackTrace(skip_count, max_depth, result, NULL); |
| +} |
| + |
| +_END_GOOGLE_NAMESPACE_ |
| diff --git c/src/symbolize.cc w/src/symbolize.cc |
| index f83c30973..0f10a86af 100644 |
| --- c/src/symbolize.cc |
| +++ w/src/symbolize.cc |
| @@ -837,6 +837,57 @@ 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); |
| + } |
| +}; |
| + |
| +__declspec(noinline) static bool SymbolizeAndDemangle(void *pc, char *out, |
| + int out_size) { |
| + const 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 * sizeof(TCHAR)]; |
| + SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO*>(buf); |
| + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); |
| + symbol->MaxNameLen = MAX_SYM_NAME; |
| + bool ret = SymFromAddr(symInitializer.process, reinterpret_cast<DWORD64>(pc), 0, symbol); |
| + if (ret && static_cast<int>(symbol->NameLen) < out_size) { |
| + strncpy(out, symbol->Name, symbol->NameLen + 1); |
| + // 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 c/src/symbolize_unittest.cc w/src/symbolize_unittest.cc |
| index 05cb8a11c..348a71b43 100644 |
| --- c/src/symbolize_unittest.cc |
| +++ w/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() { |
| @@ -340,11 +342,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); |
| @@ -353,18 +386,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 |
| printf("PASS (no symbolize_unittest support)\n"); |
| + return 0; |
| +# endif // __ELF__ |
| #else |
| printf("PASS (no symbolize support)\n"); |
| -#endif |
| return 0; |
| +#endif |
| } |
| -#endif // HAVE_STACKTRACE |
| diff --git c/src/utilities.h w/src/utilities.h |
| index 5f79968ef..10f98b24a 100644 |
| --- c/src/utilities.h |
| +++ w/src/utilities.h |
| @@ -97,6 +97,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. |
| @@ -110,6 +112,8 @@ |
| # 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 |
| |
| @@ -127,6 +131,9 @@ |
| #elif defined(OS_MACOSX) && defined(HAVE_DLADDR) |
| // Use dladdr to symbolize. |
| # define HAVE_SYMBOLIZE |
| +#elif defined(OS_WINDOWS) |
| +// Use Dbghelp.dll to symbolize |
| +# define HAVE_SYMBOLIZE |
| #endif |
| |
| #ifndef ARRAYSIZE |