Commit 5cfa9a28 authored by Shinichiro Hamaji's avatar Shinichiro Hamaji Committed by GitHub

Merge pull request #168 from andschwa/windows-stacktrace

Port stack tracing to Windows
parents 77a0e36b d1f49ba5
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
Abhishek Dasgupta <abhi2743@gmail.com> Abhishek Dasgupta <abhi2743@gmail.com>
Abhishek Parmar <abhishek@orng.net> Abhishek Parmar <abhishek@orng.net>
Andrew Schwartzmeyer <andrew@schwartzmeyer.com>
Andy Ying <andy@trailofbits.com> Andy Ying <andy@trailofbits.com>
Brian Silverman <bsilver16384@gmail.com> Brian Silverman <bsilver16384@gmail.com>
Google Inc. Google Inc.
......
...@@ -364,9 +364,9 @@ set (GLOG_SRCS ...@@ -364,9 +364,9 @@ set (GLOG_SRCS
src/vlog_is_on.cc src/vlog_is_on.cc
) )
if (HAVE_PTHREAD) if (HAVE_PTHREAD OR WIN32)
list (APPEND GLOG_SRCS src/signalhandler.cc) list (APPEND GLOG_SRCS src/signalhandler.cc)
endif (HAVE_PTHREAD) endif (HAVE_PTHREAD OR WIN32)
if (WIN32) if (WIN32)
list (APPEND GLOG_SRCS list (APPEND GLOG_SRCS
...@@ -455,6 +455,11 @@ if (HAVE_EXECINFO_H) ...@@ -455,6 +455,11 @@ if (HAVE_EXECINFO_H)
set (HAVE_STACKTRACE 1) set (HAVE_STACKTRACE 1)
endif (HAVE_EXECINFO_H) endif (HAVE_EXECINFO_H)
if (WIN32)
set (HAVE_STACKTRACE 1)
set (HAVE_SYMBOLIZE 1)
endif (WIN32)
if (UNIX OR (APPLE AND HAVE_DLADDR)) if (UNIX OR (APPLE AND HAVE_DLADDR))
set (HAVE_SYMBOLIZE 1) set (HAVE_SYMBOLIZE 1)
endif (UNIX OR (APPLE AND HAVE_DLADDR)) endif (UNIX OR (APPLE AND HAVE_DLADDR))
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
Abhishek Dasgupta <abhi2743@gmail.com> Abhishek Dasgupta <abhi2743@gmail.com>
Abhishek Parmar <abhishek@orng.net> Abhishek Parmar <abhishek@orng.net>
Andrew Schwartzmeyer <andrew@schwartzmeyer.com>
Andy Ying <andy@trailofbits.com> Andy Ying <andy@trailofbits.com>
Brian Silverman <bsilver16384@gmail.com> Brian Silverman <bsilver16384@gmail.com>
Fumitoshi Ukai <ukai@google.com> Fumitoshi Ukai <ukai@google.com>
......
...@@ -35,10 +35,17 @@ ...@@ -35,10 +35,17 @@
// Note that we only have partial C++0x support yet. // Note that we only have partial C++0x support yet.
#include <stdio.h> // for NULL #include <stdio.h> // for NULL
#include "utilities.h"
#include "demangle.h" #include "demangle.h"
#if defined(OS_WINDOWS)
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp")
#endif
_START_GOOGLE_NAMESPACE_ _START_GOOGLE_NAMESPACE_
#if !defined(OS_WINDOWS)
typedef struct { typedef struct {
const char *abbrev; const char *abbrev;
const char *real_name; const char *real_name;
...@@ -1293,12 +1300,37 @@ static bool ParseTopLevelMangledName(State *state) { ...@@ -1293,12 +1300,37 @@ static bool ParseTopLevelMangledName(State *state) {
} }
return false; return false;
} }
#endif
// The demangler entry point. // The demangler entry point.
bool Demangle(const char *mangled, char *out, int out_size) { 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; State state;
InitState(&state, mangled, out, out_size); InitState(&state, mangled, out, out_size);
return ParseTopLevelMangledName(&state) && !state.overflowed; return ParseTopLevelMangledName(&state) && !state.overflowed;
#endif
} }
_END_GOOGLE_NAMESPACE_ _END_GOOGLE_NAMESPACE_
...@@ -62,18 +62,37 @@ static const char *DemangleIt(const char * const mangled) { ...@@ -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 corner cases of bounary conditions.
TEST(Demangle, CornerCases) { TEST(Demangle, CornerCases) {
char tmp[10]; const size_t size = 10;
EXPECT_TRUE(Demangle("_Z6foobarv", tmp, sizeof(tmp))); char tmp[size] = { 0 };
// sizeof("foobar()") == 9 const char *demangled = "foobar()";
EXPECT_STREQ("foobar()", tmp); const char *mangled = "_Z6foobarv";
EXPECT_TRUE(Demangle("_Z6foobarv", tmp, 9)); EXPECT_TRUE(Demangle(mangled, tmp, sizeof(tmp)));
EXPECT_STREQ("foobar()", tmp); // sizeof("foobar()") == size - 1
EXPECT_FALSE(Demangle("_Z6foobarv", tmp, 8)); // Not enough. EXPECT_STREQ(demangled, tmp);
EXPECT_FALSE(Demangle("_Z6foobarv", tmp, 1)); EXPECT_TRUE(Demangle(mangled, tmp, size - 1));
EXPECT_FALSE(Demangle("_Z6foobarv", tmp, 0)); EXPECT_STREQ(demangled, tmp);
EXPECT_FALSE(Demangle("_Z6foobarv", NULL, 0)); // Should not cause SEGV. 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 // Test handling of functions suffixed with .clone.N, which is used by GCC
...@@ -123,6 +142,8 @@ TEST(Demangle, FromFile) { ...@@ -123,6 +142,8 @@ TEST(Demangle, FromFile) {
} }
} }
#endif
int main(int argc, char **argv) { int main(int argc, char **argv) {
#ifdef HAVE_LIB_GFLAGS #ifdef HAVE_LIB_GFLAGS
ParseCommandLineFlags(&argc, &argv, true); ParseCommandLineFlags(&argc, &argv, true);
......
...@@ -1463,16 +1463,13 @@ void LogMessage::RecordCrashReason( ...@@ -1463,16 +1463,13 @@ void LogMessage::RecordCrashReason(
# define ATTRIBUTE_NORETURN # define ATTRIBUTE_NORETURN
#endif #endif
#if defined(OS_WINDOWS)
__declspec(noreturn)
#endif
static void logging_fail() ATTRIBUTE_NORETURN; static void logging_fail() ATTRIBUTE_NORETURN;
static void logging_fail() { static void logging_fail() {
#if defined(_DEBUG) && defined(_MSC_VER)
// When debugging on windows, avoid the obnoxious dialog and make
// it possible to continue past a LOG(FATAL) in the debugger
__debugbreak();
#else
abort(); abort();
#endif
} }
typedef void (*logging_fail_func_t)() ATTRIBUTE_NORETURN; typedef void (*logging_fail_func_t)() ATTRIBUTE_NORETURN;
......
...@@ -48,9 +48,6 @@ ...@@ -48,9 +48,6 @@
_START_GOOGLE_NAMESPACE_ _START_GOOGLE_NAMESPACE_
// TOOD(hamaji): Use signal instead of sigaction?
#ifdef HAVE_SIGACTION
namespace { namespace {
// We'll install the failure signal handler for these signals. We could // We'll install the failure signal handler for these signals. We could
...@@ -66,10 +63,14 @@ const struct { ...@@ -66,10 +63,14 @@ const struct {
{ SIGILL, "SIGILL" }, { SIGILL, "SIGILL" },
{ SIGFPE, "SIGFPE" }, { SIGFPE, "SIGFPE" },
{ SIGABRT, "SIGABRT" }, { SIGABRT, "SIGABRT" },
#if !defined(OS_WINDOWS)
{ SIGBUS, "SIGBUS" }, { SIGBUS, "SIGBUS" },
#endif
{ SIGTERM, "SIGTERM" }, { SIGTERM, "SIGTERM" },
}; };
static bool kFailureSignalHandlerInstalled = false;
// Returns the program counter from signal context, NULL if unknown. // Returns the program counter from signal context, NULL if unknown.
void* GetPC(void* ucontext_in_void) { void* GetPC(void* ucontext_in_void) {
#if (defined(HAVE_UCONTEXT_H) || defined(HAVE_SYS_UCONTEXT_H)) && defined(PC_FROM_UCONTEXT) #if (defined(HAVE_UCONTEXT_H) || defined(HAVE_SYS_UCONTEXT_H)) && defined(PC_FROM_UCONTEXT)
...@@ -168,6 +169,9 @@ void DumpTimeInfo() { ...@@ -168,6 +169,9 @@ void DumpTimeInfo() {
g_failure_writer(buf, formatter.num_bytes_written()); g_failure_writer(buf, formatter.num_bytes_written());
} }
// TOOD(hamaji): Use signal instead of sigaction?
#ifdef HAVE_SIGACTION
// Dumps information about the signal to STDERR. // Dumps information about the signal to STDERR.
void DumpSignalInfo(int signal_number, siginfo_t *siginfo) { void DumpSignalInfo(int signal_number, siginfo_t *siginfo) {
// Get the signal name. // Get the signal name.
...@@ -213,6 +217,8 @@ void DumpSignalInfo(int signal_number, siginfo_t *siginfo) { ...@@ -213,6 +217,8 @@ void DumpSignalInfo(int signal_number, siginfo_t *siginfo) {
g_failure_writer(buf, formatter.num_bytes_written()); g_failure_writer(buf, formatter.num_bytes_written());
} }
#endif // HAVE_SIGACTION
// Dumps information about the stack frame to STDERR. // Dumps information about the stack frame to STDERR.
void DumpStackFrameInfo(const char* prefix, void* pc) { void DumpStackFrameInfo(const char* prefix, void* pc) {
// Get the symbol name. // Get the symbol name.
...@@ -240,12 +246,17 @@ void DumpStackFrameInfo(const char* prefix, void* pc) { ...@@ -240,12 +246,17 @@ void DumpStackFrameInfo(const char* prefix, void* pc) {
// Invoke the default signal handler. // Invoke the default signal handler.
void InvokeDefaultSignalHandler(int signal_number) { void InvokeDefaultSignalHandler(int signal_number) {
#ifdef HAVE_SIGACTION
struct sigaction sig_action; struct sigaction sig_action;
memset(&sig_action, 0, sizeof(sig_action)); memset(&sig_action, 0, sizeof(sig_action));
sigemptyset(&sig_action.sa_mask); sigemptyset(&sig_action.sa_mask);
sig_action.sa_handler = SIG_DFL; sig_action.sa_handler = SIG_DFL;
sigaction(signal_number, &sig_action, NULL); sigaction(signal_number, &sig_action, NULL);
kill(getpid(), signal_number); kill(getpid(), signal_number);
#elif defined(OS_WINDOWS)
signal(signal_number, SIG_DFL);
raise(signal_number);
#endif
} }
// This variable is used for protecting FailureSignalHandler() from // This variable is used for protecting FailureSignalHandler() from
...@@ -256,9 +267,14 @@ static pthread_t* g_entered_thread_id_pointer = NULL; ...@@ -256,9 +267,14 @@ static pthread_t* g_entered_thread_id_pointer = NULL;
// Dumps signal and stack frame information, and invokes the default // Dumps signal and stack frame information, and invokes the default
// signal handler once our job is done. // signal handler once our job is done.
#if defined(OS_WINDOWS)
void FailureSignalHandler(int signal_number)
#else
void FailureSignalHandler(int signal_number, void FailureSignalHandler(int signal_number,
siginfo_t *signal_info, siginfo_t *signal_info,
void *ucontext) { void *ucontext)
#endif
{
// First check if we've already entered the function. We use an atomic // First check if we've already entered the function. We use an atomic
// compare and swap operation for platforms that support it. For other // compare and swap operation for platforms that support it. For other
// platforms, we use a naive method that could lead to a subtle race. // platforms, we use a naive method that could lead to a subtle race.
...@@ -298,16 +314,20 @@ void FailureSignalHandler(int signal_number, ...@@ -298,16 +314,20 @@ void FailureSignalHandler(int signal_number,
// First dump time info. // First dump time info.
DumpTimeInfo(); DumpTimeInfo();
#if !defined(OS_WINDOWS)
// Get the program counter from ucontext. // Get the program counter from ucontext.
void *pc = GetPC(ucontext); void *pc = GetPC(ucontext);
DumpStackFrameInfo("PC: ", pc); DumpStackFrameInfo("PC: ", pc);
#endif
#ifdef HAVE_STACKTRACE #ifdef HAVE_STACKTRACE
// Get the stack traces. // Get the stack traces.
void *stack[32]; void *stack[32];
// +1 to exclude this function. // +1 to exclude this function.
const int depth = GetStackTrace(stack, ARRAYSIZE(stack), 1); const int depth = GetStackTrace(stack, ARRAYSIZE(stack), 1);
# ifdef HAVE_SIGACTION
DumpSignalInfo(signal_number, signal_info); DumpSignalInfo(signal_number, signal_info);
# endif
// Dump the stack traces. // Dump the stack traces.
for (int i = 0; i < depth; ++i) { for (int i = 0; i < depth; ++i) {
DumpStackFrameInfo(" ", stack[i]); DumpStackFrameInfo(" ", stack[i]);
...@@ -333,18 +353,19 @@ void FailureSignalHandler(int signal_number, ...@@ -333,18 +353,19 @@ void FailureSignalHandler(int signal_number,
} // namespace } // namespace
#endif // HAVE_SIGACTION
namespace glog_internal_namespace_ { namespace glog_internal_namespace_ {
bool IsFailureSignalHandlerInstalled() { bool IsFailureSignalHandlerInstalled() {
#ifdef HAVE_SIGACTION #ifdef HAVE_SIGACTION
// TODO(andschwa): Return kFailureSignalHandlerInstalled?
struct sigaction sig_action; struct sigaction sig_action;
memset(&sig_action, 0, sizeof(sig_action)); memset(&sig_action, 0, sizeof(sig_action));
sigemptyset(&sig_action.sa_mask); sigemptyset(&sig_action.sa_mask);
sigaction(SIGABRT, NULL, &sig_action); sigaction(SIGABRT, NULL, &sig_action);
if (sig_action.sa_sigaction == &FailureSignalHandler) if (sig_action.sa_sigaction == &FailureSignalHandler)
return true; return true;
#elif defined(OS_WINDOWS)
return kFailureSignalHandlerInstalled;
#endif // HAVE_SIGACTION #endif // HAVE_SIGACTION
return false; return false;
} }
...@@ -363,11 +384,18 @@ void InstallFailureSignalHandler() { ...@@ -363,11 +384,18 @@ void InstallFailureSignalHandler() {
for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) { for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) {
CHECK_ERR(sigaction(kFailureSignals[i].number, &sig_action, NULL)); CHECK_ERR(sigaction(kFailureSignals[i].number, &sig_action, NULL));
} }
kFailureSignalHandlerInstalled = true;
#elif defined(OS_WINDOWS)
for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) {
CHECK_NE(signal(kFailureSignals[i].number, &FailureSignalHandler),
SIG_ERR);
}
kFailureSignalHandlerInstalled = true;
#endif // HAVE_SIGACTION #endif // HAVE_SIGACTION
} }
void InstallFailureWriter(void (*writer)(const char* data, int size)) { void InstallFailureWriter(void (*writer)(const char* data, int size)) {
#ifdef HAVE_SIGACTION #if defined(HAVE_SIGACTION) || defined(OS_WINDOWS)
g_failure_writer = writer; g_failure_writer = writer;
#endif // HAVE_SIGACTION #endif // HAVE_SIGACTION
} }
......
...@@ -34,7 +34,9 @@ ...@@ -34,7 +34,9 @@
#include "utilities.h" #include "utilities.h"
#include <pthread.h> #if defined(HAVE_PTHREAD)
# include <pthread.h>
#endif
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -87,12 +89,20 @@ int main(int argc, char **argv) { ...@@ -87,12 +89,20 @@ int main(int argc, char **argv) {
fprintf(stderr, "looping\n"); fprintf(stderr, "looping\n");
while (true); while (true);
} else if (command == "die_in_thread") { } else if (command == "die_in_thread") {
#if defined(HAVE_PTHREAD)
pthread_t thread; pthread_t thread;
pthread_create(&thread, NULL, &DieInThread, NULL); pthread_create(&thread, NULL, &DieInThread, NULL);
pthread_join(thread, NULL); pthread_join(thread, NULL);
#else
fprintf(stderr, "no pthread\n");
return 1;
#endif
} else if (command == "dump_to_stdout") { } else if (command == "dump_to_stdout") {
InstallFailureWriter(WriteToStdout); InstallFailureWriter(WriteToStdout);
abort(); abort();
} else if (command == "installed") {
fprintf(stderr, "signal handler installed: %s\n",
IsFailureSignalHandlerInstalled() ? "true" : "false");
} else { } else {
// Tell the shell script // Tell the shell script
puts("OK"); puts("OK");
......
// 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 "config.h"
#include "port.h"
#include "stacktrace.h"
#include <DbgHelp.h>
_START_GOOGLE_NAMESPACE_
GOOGLE_GLOG_DLL_DECL
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_
...@@ -837,6 +837,67 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, ...@@ -837,6 +837,67 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
_END_GOOGLE_NAMESPACE_ _END_GOOGLE_NAMESPACE_
#elif defined(OS_WINDOWS)
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp")
_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 #else
# error BUG: HAVE_SYMBOLIZE was wrongly set # error BUG: HAVE_SYMBOLIZE was wrongly set
#endif #endif
......
...@@ -49,10 +49,22 @@ using namespace GFLAGS_NAMESPACE; ...@@ -49,10 +49,22 @@ using namespace GFLAGS_NAMESPACE;
using namespace std; using namespace std;
using namespace GOOGLE_NAMESPACE; using namespace GOOGLE_NAMESPACE;
#if defined(HAVE_STACKTRACE) && defined(__ELF__) #if defined(HAVE_STACKTRACE)
#define always_inline #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. // This unit tests make sense only with GCC.
// Uses lots of GCC specific features. // Uses lots of GCC specific features.
#if defined(__GNUC__) && !defined(__OPENCC__) #if defined(__GNUC__) && !defined(__OPENCC__)
...@@ -70,16 +82,6 @@ using namespace GOOGLE_NAMESPACE; ...@@ -70,16 +82,6 @@ using namespace GOOGLE_NAMESPACE;
# endif // defined(__i386__) || defined(__x86_64__) # endif // defined(__i386__) || defined(__x86_64__)
#endif #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. // Make them C linkage to avoid mangled names.
extern "C" { extern "C" {
void nonstatic_func() { void nonstatic_func() {
...@@ -355,11 +357,42 @@ void ATTRIBUTE_NOINLINE TestWithReturnAddress() { ...@@ -355,11 +357,42 @@ void ATTRIBUTE_NOINLINE TestWithReturnAddress() {
#endif #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) { int main(int argc, char **argv) {
FLAGS_logtostderr = true; FLAGS_logtostderr = true;
InitGoogleLogging(argv[0]); InitGoogleLogging(argv[0]);
InitGoogleTest(&argc, argv); 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 // We don't want to get affected by the callback interface, that may be
// used to install some callback function at InitGoogle() time. // used to install some callback function at InitGoogle() time.
InstallSymbolizeCallback(NULL); InstallSymbolizeCallback(NULL);
...@@ -368,18 +401,15 @@ int main(int argc, char **argv) { ...@@ -368,18 +401,15 @@ int main(int argc, char **argv) {
TestWithPCInsideNonInlineFunction(); TestWithPCInsideNonInlineFunction();
TestWithReturnAddress(); TestWithReturnAddress();
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
#else # elif defined(OS_WINDOWS)
return 0; TestWithReturnAddress();
#endif return RUN_ALL_TESTS();
} # else // OS_WINDOWS
#else
int main() {
#ifdef HAVE_SYMBOLIZE
printf("PASS (no symbolize_unittest support)\n"); printf("PASS (no symbolize_unittest support)\n");
return 0;
# endif // __ELF__
#else #else
printf("PASS (no symbolize support)\n"); printf("PASS (no symbolize support)\n");
#endif
return 0; return 0;
#endif // HAVE_SYMBOLIZE
} }
#endif // HAVE_STACKTRACE
...@@ -137,17 +137,19 @@ static void DumpStackTraceAndExit() { ...@@ -137,17 +137,19 @@ static void DumpStackTraceAndExit() {
DumpStackTrace(1, DebugWriteToStderr, NULL); DumpStackTrace(1, DebugWriteToStderr, NULL);
// TOOD(hamaji): Use signal instead of sigaction? // TOOD(hamaji): Use signal instead of sigaction?
#ifdef HAVE_SIGACTION
if (IsFailureSignalHandlerInstalled()) { if (IsFailureSignalHandlerInstalled()) {
// Set the default signal handler for SIGABRT, to avoid invoking our // Set the default signal handler for SIGABRT, to avoid invoking our
// own signal handler installed by InstallFailureSignalHandler(). // own signal handler installed by InstallFailureSignalHandler().
#ifdef HAVE_SIGACTION
struct sigaction sig_action; struct sigaction sig_action;
memset(&sig_action, 0, sizeof(sig_action)); memset(&sig_action, 0, sizeof(sig_action));
sigemptyset(&sig_action.sa_mask); sigemptyset(&sig_action.sa_mask);
sig_action.sa_handler = SIG_DFL; sig_action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &sig_action, NULL); sigaction(SIGABRT, &sig_action, NULL);
} #elif defined(OS_WINDOWS)
signal(SIGABRT, SIG_DFL);
#endif // HAVE_SIGACTION #endif // HAVE_SIGACTION
}
abort(); abort();
} }
......
...@@ -99,6 +99,8 @@ ...@@ -99,6 +99,8 @@
// malloc() from the unwinder. This is a problem because we're // malloc() from the unwinder. This is a problem because we're
// trying to use the unwinder to instrument malloc(). // 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 // Note: if you add a new implementation here, make sure it works
// correctly when GetStackTrace() is called with max_depth == 0. // correctly when GetStackTrace() is called with max_depth == 0.
// Some code may do that. // Some code may do that.
...@@ -112,6 +114,8 @@ ...@@ -112,6 +114,8 @@
# define STACKTRACE_H "stacktrace_x86_64-inl.h" # define STACKTRACE_H "stacktrace_x86_64-inl.h"
# elif (defined(__ppc__) || defined(__PPC__)) && __GNUC__ >= 2 # elif (defined(__ppc__) || defined(__PPC__)) && __GNUC__ >= 2
# define STACKTRACE_H "stacktrace_powerpc-inl.h" # define STACKTRACE_H "stacktrace_powerpc-inl.h"
# elif defined(OS_WINDOWS)
# define STACKTRACE_H "stacktrace_windows-inl.h"
# endif # endif
#endif #endif
...@@ -129,6 +133,9 @@ ...@@ -129,6 +133,9 @@
#elif defined(OS_MACOSX) && defined(HAVE_DLADDR) #elif defined(OS_MACOSX) && defined(HAVE_DLADDR)
// Use dladdr to symbolize. // Use dladdr to symbolize.
# define HAVE_SYMBOLIZE # define HAVE_SYMBOLIZE
#elif defined(OS_WINDOWS)
// Use DbgHelp to symbolize
# define HAVE_SYMBOLIZE
#endif #endif
#ifndef ARRAYSIZE #ifndef ARRAYSIZE
...@@ -143,6 +150,9 @@ namespace glog_internal_namespace_ { ...@@ -143,6 +150,9 @@ namespace glog_internal_namespace_ {
#ifdef HAVE___ATTRIBUTE__ #ifdef HAVE___ATTRIBUTE__
# define ATTRIBUTE_NOINLINE __attribute__ ((noinline)) # define ATTRIBUTE_NOINLINE __attribute__ ((noinline))
# define HAVE_ATTRIBUTE_NOINLINE # define HAVE_ATTRIBUTE_NOINLINE
#elif defined(OS_WINDOWS)
# define ATTRIBUTE_NOINLINE __declspec(noinline)
# define HAVE_ATTRIBUTE_NOINLINE
#else #else
# define ATTRIBUTE_NOINLINE # define ATTRIBUTE_NOINLINE
#endif #endif
......
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