Merge pull request #168 from andschwa/windows-stacktrace
Port stack tracing to Windows
This commit is contained in:
commit
5cfa9a28f5
1
AUTHORS
1
AUTHORS
@ -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
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
|
|
||||||
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 @@
|
|||||||
// 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) {
|
|||||||
}
|
}
|
||||||
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) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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(
|
|||||||
# 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 @@
|
|||||||
|
|
||||||
_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 {
|
|||||||
{ 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() {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
|
|
||||||
// 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;
|
|||||||
|
|
||||||
// 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,
|
|||||||
// 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,
|
|||||||
|
|
||||||
} // 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() {
|
|||||||
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 @@
|
|||||||
|
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
#if defined(HAVE_PTHREAD)
|
||||||
# include <pthread.h>
|
# 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) {
|
|||||||
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");
|
||||||
|
|||||||
51
src/stacktrace_windows-inl.h
Normal file
51
src/stacktrace_windows-inl.h
Normal file
@ -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_
|
||||||
@ -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;
|
|||||||
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;
|
|||||||
# 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() {
|
|||||||
#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) {
|
|||||||
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() {
|
|||||||
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 @@
|
|||||||
// 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 @@
|
|||||||
# 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 @@
|
|||||||
#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_ {
|
|||||||
#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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user