forked from premiere/premiere-libtorrent
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:
parent
b701fb252a
commit
daa453ef3b
|
@ -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
|
||||
|
|
148
src/assert.cpp
148
src/assert.cpp
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue