Skip to content

Commit

Permalink
Merge pull request google#168 from andschwa/windows-stacktrace
Browse files Browse the repository at this point in the history
Port stack tracing to Windows
  • Loading branch information
shinh authored Jun 28, 2017
2 parents bae9719 + 4c14eb1 commit c4b329e
Show file tree
Hide file tree
Showing 13 changed files with 299 additions and 50 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

Abhishek Dasgupta <[email protected]>
Abhishek Parmar <[email protected]>
Andrew Schwartzmeyer <[email protected]>
Andy Ying <[email protected]>
Brian Silverman <[email protected]>
Google Inc.
Expand Down
9 changes: 7 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -455,6 +455,11 @@ if (HAVE_EXECINFO_H)
set (HAVE_STACKTRACE 1)
endif (HAVE_EXECINFO_H)

if (WIN32)
set (HAVE_STACKTRACE 1)
set (HAVE_SYMBOLIZE 1)
endif (WIN32)

if (UNIX OR (APPLE AND HAVE_DLADDR))
set (HAVE_SYMBOLIZE 1)
endif (UNIX OR (APPLE AND HAVE_DLADDR))
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

Abhishek Dasgupta <[email protected]>
Abhishek Parmar <[email protected]>
Andrew Schwartzmeyer <[email protected]>
Andy Ying <[email protected]>
Brian Silverman <[email protected]>
Fumitoshi Ukai <[email protected]>
Expand Down
32 changes: 32 additions & 0 deletions src/demangle.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,17 @@
// 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>
#pragma comment(lib, "DbgHelp")
#endif

_START_GOOGLE_NAMESPACE_

#if !defined(OS_WINDOWS)
typedef struct {
const char *abbrev;
const char *real_name;
Expand Down Expand Up @@ -1293,12 +1300,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_
41 changes: 31 additions & 10 deletions src/demangle_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -123,6 +142,8 @@ TEST(Demangle, FromFile) {
}
}

#endif

int main(int argc, char **argv) {
#ifdef HAVE_LIB_GFLAGS
ParseCommandLineFlags(&argc, &argv, true);
Expand Down
9 changes: 3 additions & 6 deletions src/logging.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1463,16 +1463,13 @@ void LogMessage::RecordCrashReason(
# define ATTRIBUTE_NORETURN
#endif

#if defined(OS_WINDOWS)
__declspec(noreturn)
#endif
static void logging_fail() ATTRIBUTE_NORETURN;

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();
#endif
}

typedef void (*logging_fail_func_t)() ATTRIBUTE_NORETURN;
Expand Down
42 changes: 35 additions & 7 deletions src/signalhandler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@

_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
Expand All @@ -66,10 +63,14 @@ const struct {
{ SIGILL, "SIGILL" },
{ SIGFPE, "SIGFPE" },
{ SIGABRT, "SIGABRT" },
#if !defined(OS_WINDOWS)
{ SIGBUS, "SIGBUS" },
#endif
{ SIGTERM, "SIGTERM" },
};

static bool kFailureSignalHandlerInstalled = false;

// 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)
Expand Down Expand Up @@ -168,6 +169,9 @@ void DumpTimeInfo() {
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.
void DumpSignalInfo(int signal_number, siginfo_t *siginfo) {
// Get the signal name.
Expand Down Expand Up @@ -213,6 +217,8 @@ void DumpSignalInfo(int signal_number, siginfo_t *siginfo) {
g_failure_writer(buf, formatter.num_bytes_written());
}

#endif // HAVE_SIGACTION

// Dumps information about the stack frame to STDERR.
void DumpStackFrameInfo(const char* prefix, void* pc) {
// Get the symbol name.
Expand Down Expand Up @@ -240,12 +246,17 @@ void DumpStackFrameInfo(const char* prefix, void* pc) {

// Invoke the default signal handler.
void InvokeDefaultSignalHandler(int signal_number) {
#ifdef HAVE_SIGACTION
struct sigaction sig_action;
memset(&sig_action, 0, sizeof(sig_action));
sigemptyset(&sig_action.sa_mask);
sig_action.sa_handler = SIG_DFL;
sigaction(signal_number, &sig_action, NULL);
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
Expand All @@ -256,9 +267,14 @@ static pthread_t* g_entered_thread_id_pointer = NULL;

// Dumps signal and stack frame information, and invokes the default
// signal handler once our job is done.
#if defined(OS_WINDOWS)
void FailureSignalHandler(int signal_number)
#else
void FailureSignalHandler(int signal_number,
siginfo_t *signal_info,
void *ucontext) {
void *ucontext)
#endif
{
// First check if we've already entered the function. We use an atomic
// compare and swap operation for platforms that support it. For other
// platforms, we use a naive method that could lead to a subtle race.
Expand Down Expand Up @@ -298,16 +314,20 @@ void FailureSignalHandler(int signal_number,
// First dump time info.
DumpTimeInfo();

#if !defined(OS_WINDOWS)
// Get the program counter from ucontext.
void *pc = GetPC(ucontext);
DumpStackFrameInfo("PC: ", pc);
#endif

#ifdef HAVE_STACKTRACE
// Get the stack traces.
void *stack[32];
// +1 to exclude this function.
const int depth = GetStackTrace(stack, ARRAYSIZE(stack), 1);
# ifdef HAVE_SIGACTION
DumpSignalInfo(signal_number, signal_info);
# endif
// Dump the stack traces.
for (int i = 0; i < depth; ++i) {
DumpStackFrameInfo(" ", stack[i]);
Expand All @@ -333,18 +353,19 @@ void FailureSignalHandler(int signal_number,

} // namespace

#endif // HAVE_SIGACTION

namespace glog_internal_namespace_ {

bool IsFailureSignalHandlerInstalled() {
#ifdef HAVE_SIGACTION
// TODO(andschwa): Return kFailureSignalHandlerInstalled?
struct sigaction sig_action;
memset(&sig_action, 0, sizeof(sig_action));
sigemptyset(&sig_action.sa_mask);
sigaction(SIGABRT, NULL, &sig_action);
if (sig_action.sa_sigaction == &FailureSignalHandler)
return true;
#elif defined(OS_WINDOWS)
return kFailureSignalHandlerInstalled;
#endif // HAVE_SIGACTION
return false;
}
Expand All @@ -363,11 +384,18 @@ void InstallFailureSignalHandler() {
for (size_t i = 0; i < ARRAYSIZE(kFailureSignals); ++i) {
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
}

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
}
Expand Down
12 changes: 11 additions & 1 deletion src/signalhandler_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@

#include "utilities.h"

#include <pthread.h>
#if defined(HAVE_PTHREAD)
# include <pthread.h>
#endif
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
Expand Down Expand Up @@ -87,12 +89,20 @@ int main(int argc, char **argv) {
fprintf(stderr, "looping\n");
while (true);
} else if (command == "die_in_thread") {
#if defined(HAVE_PTHREAD)
pthread_t thread;
pthread_create(&thread, NULL, &DieInThread, NULL);
pthread_join(thread, NULL);
#else
fprintf(stderr, "no pthread\n");
return 1;
#endif
} else if (command == "dump_to_stdout") {
InstallFailureWriter(WriteToStdout);
abort();
} else if (command == "installed") {
fprintf(stderr, "signal handler installed: %s\n",
IsFailureSignalHandlerInstalled() ? "true" : "false");
} else {
// Tell the shell script
puts("OK");
Expand Down
51 changes: 51 additions & 0 deletions src/stacktrace_windows-inl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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_
Loading

0 comments on commit c4b329e

Please sign in to comment.