blob: 2f0fae3c46511fb8dee243419663cf3d49b91fbd [file] [log] [blame]
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