back-port windows stack trace function to 1.1 (#1017)

back port exception handling and stack trace printing from master -> RC_1_1
This commit is contained in:
Arvid Norberg 2016-08-21 01:07:02 -04:00 committed by GitHub
parent b701fb252a
commit daa453ef3b
3 changed files with 171 additions and 73 deletions

View File

@ -42,7 +42,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include <string>
std::string demangle(char const* name);
TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth = 0);
TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth = 0
, void* ctx = NULL);
#endif
// this is to disable the warning of conditional expressions

View File

@ -39,10 +39,9 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/atomic.hpp>
#endif
#if (defined TORRENT_DEBUG && TORRENT_USE_ASSERTS) \
#if TORRENT_USE_ASSERTS \
|| defined TORRENT_ASIO_DEBUGGING \
|| defined TORRENT_PROFILE_CALLS \
|| defined TORRENT_RELEASE_ASSERTS \
|| defined TORRENT_DEBUG_BUFFERS
#ifdef __APPLE__
@ -51,8 +50,10 @@ POSSIBILITY OF SUCH DAMAGE.
#include <string>
#include <cstring>
#include <stdlib.h>
#include <stdarg.h>
#include <cstdlib>
#include <cstdarg>
#include <cstdio> // for snprintf
#include <boost/array.hpp>
#include "libtorrent/aux_/disable_warnings_pop.hpp"
@ -67,7 +68,7 @@ std::string demangle(char const* name)
// in case this string comes
// this is needed on linux
char const* start = strchr(name, '(');
if (start != 0)
if (start != NULL)
{
++start;
}
@ -75,10 +76,10 @@ std::string demangle(char const* name)
{
// this is needed on macos x
start = strstr(name, "0x");
if (start != 0)
if (start != NULL)
{
start = strchr(start, ' ');
if (start != 0) ++start;
if (start != NULL) ++start;
else start = name;
}
else start = name;
@ -88,13 +89,13 @@ std::string demangle(char const* name)
if (end) while (*(end-1) == ' ') --end;
std::string in;
if (end == 0) in.assign(start);
if (end == NULL) in.assign(start);
else in.assign(start, end);
size_t len;
int status;
char* unmangled = ::abi::__cxa_demangle(in.c_str(), 0, &len, &status);
if (unmangled == 0) return in;
char* unmangled = ::abi::__cxa_demangle(in.c_str(), NULL, &len, &status);
if (unmangled == NULL) return in;
std::string ret(unmangled);
free(unmangled);
return ret;
@ -116,15 +117,15 @@ std::string demangle(char const* name)
std::string demangle(char const* name) { return name; }
#endif
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <cstdlib>
#include <cstdio>
#include <csignal>
#include "libtorrent/version.hpp"
#if TORRENT_USE_EXECINFO
#include <execinfo.h>
TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth)
TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth, void*)
{
void* stack[50];
int size = backtrace(stack, 50);
@ -141,8 +142,7 @@ TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth)
free(symbols);
}
// visual studio 9 and up appears to support this
#elif defined _WIN32 && _MSC_VER >= 1500
#elif defined _WIN32
#include "windows.h"
#include "libtorrent/utf8.hpp"
@ -151,41 +151,61 @@ TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth)
#include "winbase.h"
#include "dbghelp.h"
static libtorrent::mutex dbghlp_mutex;
TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth)
TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth
, void* ctx)
{
// all calls to DbgHlp.dll are thread-unsafe. i.e. they all need to be
// synchronized and not called concurrently. This mutex serializes access
static libtorrent::mutex dbghlp_mutex;
libtorrent::mutex::scoped_lock l(dbghlp_mutex);
typedef USHORT (WINAPI *RtlCaptureStackBackTrace_t)(
__in ULONG FramesToSkip,
__in ULONG FramesToCapture,
__out PVOID *BackTrace,
__out_opt PULONG BackTraceHash);
static RtlCaptureStackBackTrace_t RtlCaptureStackBackTrace = 0;
if (RtlCaptureStackBackTrace == 0)
CONTEXT context_record;
if (ctx)
{
// we don't actually have to free this library, everyone has it loaded
HMODULE lib = LoadLibrary(TEXT("kernel32.dll"));
RtlCaptureStackBackTrace = (RtlCaptureStackBackTrace_t)GetProcAddress(lib, "RtlCaptureStackBackTrace");
if (RtlCaptureStackBackTrace == 0)
{
out[0] = 0;
return;
}
context_record = *static_cast<CONTEXT*>(ctx);
}
else
{
// use the current thread's context
RtlCaptureContext(&context_record);
}
int i;
void* stack[50];
int size = CaptureStackBackTrace(0, 50, stack, 0);
int size = 0;
boost::array<void*, 50> stack;
SYMBOL_INFO* symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR), 1);
symbol->MaxNameLen = MAX_SYM_NAME;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
STACKFRAME64 stack_frame;
memset(&stack_frame, 0, sizeof(stack_frame));
#if defined(_WIN64)
int const machine_type = IMAGE_FILE_MACHINE_AMD64;
stack_frame.AddrPC.Offset = context_record.Rip;
stack_frame.AddrFrame.Offset = context_record.Rbp;
stack_frame.AddrStack.Offset = context_record.Rsp;
#else
int const machine_type = IMAGE_FILE_MACHINE_I386;
stack_frame.AddrPC.Offset = context_record.Eip;
stack_frame.AddrFrame.Offset = context_record.Ebp;
stack_frame.AddrStack.Offset = context_record.Esp;
#endif
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrFrame.Mode = AddrModeFlat;
stack_frame.AddrStack.Mode = AddrModeFlat;
while (StackWalk64(machine_type,
GetCurrentProcess(),
GetCurrentThread(),
&stack_frame,
&context_record,
NULL,
&SymFunctionTableAccess64,
&SymGetModuleBase64,
NULL) && size < stack.size())
{
stack[size++] = reinterpret_cast<void*>(stack_frame.AddrPC.Offset);
}
struct symbol_bundle : SYMBOL_INFO
{
wchar_t name[MAX_SYM_NAME];
};
HANDLE p = GetCurrentProcess();
static bool sym_initialized = false;
@ -194,24 +214,52 @@ TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth)
sym_initialized = true;
SymInitialize(p, NULL, true);
}
for (i = 0; i < size && len > 0; ++i)
SymRefreshModuleList(p);
for (int i = 0; i < size && len > 0; ++i)
{
int ret;
if (SymFromAddr(p, uintptr_t(stack[i]), 0, symbol))
ret = snprintf(out, len, "%d: %s\n", i, symbol->Name);
else
ret = snprintf(out, len, "%d: <unknown>\n", i);
DWORD_PTR frame_ptr = reinterpret_cast<DWORD_PTR>(stack[i]);
DWORD64 displacement = 0;
symbol_bundle symbol;
symbol.MaxNameLen = MAX_SYM_NAME;
symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
BOOL const has_symbol = SymFromAddr(p, frame_ptr, &displacement, &symbol);
DWORD line_displacement = 0;
IMAGEHLP_LINE64 line = {};
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
BOOL const has_line = SymGetLineFromAddr64(GetCurrentProcess(), frame_ptr,
&line_displacement, &line);
int ret = snprintf(out, len, "%2d: %p", i, stack[i]);
out += ret; len -= ret; if (len <= 0) break;
if (has_symbol)
{
ret = snprintf(out, len, " %s +%-4" PRId64
, demangle(symbol.Name).c_str(), displacement);
out += ret; len -= ret; if (len <= 0) break;
}
if (has_line)
{
ret = snprintf(out, len, " %s:%d"
, line.FileName, line.LineNumber);
out += ret; len -= ret; if (len <= 0) break;
}
ret = snprintf(out, len, "\n");
out += ret;
len -= ret;
if (i == max_depth && max_depth > 0) break;
}
free(symbol);
}
#else
TORRENT_EXPORT void print_backtrace(char* out, int len, int max_depth)
TORRENT_EXPORT void print_backtrace(char* out, int len, int /*max_depth*/, void* /* ctx */)
{
out[0] = 0;
strncat(out, "<not supported>", len);

View File

@ -97,6 +97,62 @@ void output_test_log_to_terminal()
} while (size > 0);
}
#ifdef _WIN32
LONG WINAPI seh_exception_handler(LPEXCEPTION_POINTERS p)
{
char stack_text[10000];
#if TORRENT_USE_ASSERTS \
|| defined TORRENT_ASIO_DEBUGGING \
|| defined TORRENT_PROFILE_CALLS \
|| defined TORRENT_DEBUG_BUFFERS
print_backtrace(stack_text, sizeof(stack_text), 30
, p->ContextRecord);
#elif defined __FUNCTION__
strcat(stack_text, __FUNCTION__);
#else
stack_text[0] = 0;
strcat(stack_text, "<stack traces disabled>");
#endif
int const code = p->ExceptionRecord->ExceptionCode;
char const* name = "<unknown exception>";
switch (code)
{
#define EXC(x) case x: name = #x; break
EXC(EXCEPTION_ACCESS_VIOLATION);
EXC(EXCEPTION_ARRAY_BOUNDS_EXCEEDED);
EXC(EXCEPTION_BREAKPOINT);
EXC(EXCEPTION_DATATYPE_MISALIGNMENT);
EXC(EXCEPTION_FLT_DENORMAL_OPERAND);
EXC(EXCEPTION_FLT_DIVIDE_BY_ZERO);
EXC(EXCEPTION_FLT_INEXACT_RESULT);
EXC(EXCEPTION_FLT_INVALID_OPERATION);
EXC(EXCEPTION_FLT_OVERFLOW);
EXC(EXCEPTION_FLT_STACK_CHECK);
EXC(EXCEPTION_FLT_UNDERFLOW);
EXC(EXCEPTION_ILLEGAL_INSTRUCTION);
EXC(EXCEPTION_IN_PAGE_ERROR);
EXC(EXCEPTION_INT_DIVIDE_BY_ZERO);
EXC(EXCEPTION_INT_OVERFLOW);
EXC(EXCEPTION_INVALID_DISPOSITION);
EXC(EXCEPTION_NONCONTINUABLE_EXCEPTION);
EXC(EXCEPTION_PRIV_INSTRUCTION);
EXC(EXCEPTION_SINGLE_STEP);
EXC(EXCEPTION_STACK_OVERFLOW);
#undef EXC
};
std::fprintf(stderr, "exception: (0x%x) %s caught:\n%s\n"
, code, name, stack_text);
output_test_log_to_terminal();
exit(code);
}
#else
void sig_handler(int sig)
{
char stack_text[10000];
@ -142,6 +198,8 @@ void sig_handler(int sig)
#endif
}
#endif // _WIN32
void print_usage(char const* executable)
{
printf("%s [options] [tests...]\n"
@ -159,17 +217,6 @@ void print_usage(char const* executable)
"by -l. If no test is specified, all tests are run\n", executable);
}
#ifdef WIN32
LONG WINAPI seh_exception_handler(LPEXCEPTION_POINTERS p)
{
int sig = p->ExceptionRecord->ExceptionCode;
fprintf(stderr, "SEH exception: %u\n"
, p->ExceptionRecord->ExceptionCode);
sig_handler(sig);
exit(sig);
}
#endif
EXPORT int main(int argc, char const* argv[])
{
char const* executable = argv[0];
@ -218,6 +265,16 @@ EXPORT int main(int argc, char const* argv[])
filter = true;
}
#ifdef O_NONBLOCK
// on darwin, stdout is set to non-blocking mode by default
// which sometimes causes tests to fail with EAGAIN just
// by printing logs
int flags = fcntl(fileno(stdout), F_GETFL, 0);
fcntl(fileno(stdout), F_SETFL, flags & ~O_NONBLOCK);
flags = fcntl(fileno(stderr), F_GETFL, 0);
fcntl(fileno(stderr), F_SETFL, flags & ~O_NONBLOCK);
#endif
#ifdef WIN32
// try to suppress hanging the process by windows displaying
// modal dialogs.
@ -231,17 +288,7 @@ EXPORT int main(int argc, char const* argv[])
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
#endif
#endif
#ifdef O_NONBLOCK
// on darwin, stdout is set to non-blocking mode by default
// which sometimes causes tests to fail with EAGAIN just
// by printing logs
int flags = fcntl(fileno(stdout), F_GETFL, 0);
fcntl(fileno(stdout), F_SETFL, flags & ~O_NONBLOCK);
flags = fcntl(fileno(stderr), F_GETFL, 0);
fcntl(fileno(stderr), F_SETFL, flags & ~O_NONBLOCK);
#endif
#else
signal(SIGSEGV, &sig_handler);
#ifdef SIGBUS
@ -255,6 +302,8 @@ EXPORT int main(int argc, char const* argv[])
signal(SIGSYS, &sig_handler);
#endif
#endif // WIN32
int process_id = -1;
#ifdef _WIN32
process_id = _getpid();