From 8d7a4fc8441880f8c8ac62ca5d53123bf21c4ee4 Mon Sep 17 00:00:00 2001 From: Chris Rudd Date: Tue, 9 Jun 2020 14:06:13 -0500 Subject: [PATCH] Add support for windows inproc backend --- CMakeLists.txt | 3 - src/backends/sentry_backend_inproc.c | 96 ++++++++++++++++++++++++++-- tests/conditions.py | 2 +- 3 files changed, 92 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 290a6551e..dce671bfb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,9 +113,6 @@ endif() if(SENTRY_BACKEND_BREAKPAD AND NOT LINUX) message(FATAL_ERROR "The Breakpad backend is currently only supported on Linux") endif() -if(SENTRY_BACKEND_INPROC AND WIN32) - message(FATAL_ERROR "The in-process backend is not supported on Windows") -endif() message(STATUS "SENTRY_TRANSPORT=${SENTRY_TRANSPORT}") message(STATUS "SENTRY_BACKEND=${SENTRY_BACKEND}") diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index a2494ec60..593dbfae8 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -17,17 +17,18 @@ Sig, #Sig, Desc \ } +#define MAX_FRAMES 128 + +#ifdef SENTRY_PLATFORM_UNIX struct signal_slot { int signum; const char *signame; const char *sigdesc; }; -#define MAX_FRAMES 128 - // we need quite a bit of space for backtrace generation -#define SIGNAL_COUNT 6 -#define SIGNAL_STACK_SIZE 65536 +# define SIGNAL_COUNT 6 +# define SIGNAL_STACK_SIZE 65536 static struct sigaction g_sigaction; static struct sigaction g_previous_handlers[SIGNAL_COUNT]; static stack_t g_signal_stack; @@ -41,6 +42,8 @@ static const struct signal_slot SIGNAL_DEFINITIONS[SIGNAL_COUNT] = { SIGNAL_DEF(SIGSEGV, "Segfault"), }; +static void handle_signal(int signum, siginfo_t *info, void *user_context); + static void reset_signal_handlers(void) { @@ -102,6 +105,60 @@ shutdown_inproc_backend(sentry_backend_t *UNUSED(backend)) sentry_free(g_signal_stack.ss_sp); } +#elif defined SENTRY_PLATFORM_WINDOWS +struct signal_slot { + DWORD signum; + const char *signame; + const char *sigdesc; +}; + +# define SIGNAL_COUNT 20 + +static LPTOP_LEVEL_EXCEPTION_FILTER g_previous_handler = NULL; + +static const struct signal_slot SIGNAL_DEFINITIONS[SIGNAL_COUNT] = { + SIGNAL_DEF(EXCEPTION_ACCESS_VIOLATION, "AccessViolation"), + SIGNAL_DEF(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "ArrayBoundsExceeded"), + SIGNAL_DEF(EXCEPTION_BREAKPOINT, "BreakPoint"), + SIGNAL_DEF(EXCEPTION_DATATYPE_MISALIGNMENT, "DatatypeMisalignment"), + SIGNAL_DEF(EXCEPTION_FLT_DENORMAL_OPERAND, "FloatDenormalOperand"), + SIGNAL_DEF(EXCEPTION_FLT_DIVIDE_BY_ZERO, "FloatDivideByZero"), + SIGNAL_DEF(EXCEPTION_FLT_INEXACT_RESULT, "FloatInexactResult"), + SIGNAL_DEF(EXCEPTION_FLT_INVALID_OPERATION, "FloatInvalidOperation"), + SIGNAL_DEF(EXCEPTION_FLT_OVERFLOW, "FloatOverflow"), + SIGNAL_DEF(EXCEPTION_FLT_STACK_CHECK, "FloatStackCheck"), + SIGNAL_DEF(EXCEPTION_FLT_UNDERFLOW, "FloatUnderflow"), + SIGNAL_DEF(EXCEPTION_ILLEGAL_INSTRUCTION, "IllegalInstruction"), + SIGNAL_DEF(EXCEPTION_IN_PAGE_ERROR, "InPageError"), + SIGNAL_DEF(EXCEPTION_INT_DIVIDE_BY_ZERO, "IntegerDivideByZero"), + SIGNAL_DEF(EXCEPTION_INT_OVERFLOW, "IntegerOverflow"), + SIGNAL_DEF(EXCEPTION_INVALID_DISPOSITION, "InvalidDisposition"), + SIGNAL_DEF(EXCEPTION_NONCONTINUABLE_EXCEPTION, "NonContinuableException"), + SIGNAL_DEF(EXCEPTION_PRIV_INSTRUCTION, "PrivilgedInstruction"), + SIGNAL_DEF(EXCEPTION_SINGLE_STEP, "SingleStep"), + SIGNAL_DEF(EXCEPTION_STACK_OVERFLOW, "StackOverflow") +}; + +static LONG WINAPI handle_exception(EXCEPTION_POINTERS *); + +static void +startup_inproc_backend(sentry_backend_t *UNUSED(backend)) +{ + g_previous_handler = SetUnhandledExceptionFilter(&handle_exception); + SetErrorMode(SEM_FAILCRITICALERRORS); +} + +static void +shutdown_inproc_backend(sentry_backend_t *UNUSED(backend)) +{ + LPTOP_LEVEL_EXCEPTION_FILTER current_handler + = SetUnhandledExceptionFilter(g_previous_handler); + if (current_handler != &handle_exception) { + SetUnhandledExceptionFilter(current_handler); + } +} +#endif + static sentry_value_t make_signal_event( const struct signal_slot *sig_slot, const sentry_ucontext_t *uctx) @@ -140,7 +197,7 @@ make_signal_event( void *backtrace[MAX_FRAMES]; size_t frame_count = sentry_unwind_stack_from_ucontext(uctx, &backtrace[0], MAX_FRAMES); - SENTRY_TRACEF("captured backtrace with %zu frames", frame_count); + SENTRY_TRACEF("captured backtrace with %lu frames", frame_count); sentry_value_t frames = sentry__value_new_list_with_size(frame_count); for (size_t i = 0; i < frame_count; i++) { @@ -169,11 +226,19 @@ handle_ucontext(const sentry_ucontext_t *uctx) { const struct signal_slot *sig_slot = NULL; for (int i = 0; i < SIGNAL_COUNT; ++i) { +#ifdef SENTRY_PLATFORM_UNIX if (SIGNAL_DEFINITIONS[i].signum == uctx->signum) { +#elif defined SENTRY_PLATFORM_WINDOWS + if (SIGNAL_DEFINITIONS[i].signum + == uctx->exception_ptrs.ExceptionRecord->ExceptionCode) { +#else +# error Unsupported platform +#endif sig_slot = &SIGNAL_DEFINITIONS[i]; } } +#ifdef SENTRY_PLATFORM_UNIX // give us an allocator we can use safely in signals before we tear down. sentry__page_allocator_enable(); @@ -181,6 +246,7 @@ handle_ucontext(const sentry_ucontext_t *uctx) // make mutexes spin on a spinlock instead as it's no longer safe to use a // pthread mutex. sentry__enter_signal_handler(); +#endif const sentry_options_t *opts = sentry_get_options(); sentry__write_crash_marker(opts); @@ -203,6 +269,7 @@ handle_ucontext(const sentry_ucontext_t *uctx) } SENTRY_DEBUG("crash has been captured"); +#ifdef SENTRY_PLATFORM_UNIX // reset signal handlers and invoke the original ones. This will then tear // down the process. In theory someone might have some other handler here // which recovers the process but this will cause a memory leak going @@ -211,8 +278,10 @@ handle_ucontext(const sentry_ucontext_t *uctx) sentry__leave_signal_handler(); invoke_signal_handler( uctx->signum, uctx->siginfo, (void *)uctx->user_context); +#endif } +#ifdef SENTRY_PLATFORM_UNIX static void handle_signal(int signum, siginfo_t *info, void *user_context) { @@ -222,6 +291,23 @@ handle_signal(int signum, siginfo_t *info, void *user_context) uctx.user_context = (ucontext_t *)user_context; handle_ucontext(&uctx); } +#elif defined SENTRY_PLATFORM_WINDOWS +static LONG WINAPI +handle_exception(EXCEPTION_POINTERS *ExceptionInfo) +{ + if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT + || ExceptionInfo->ExceptionRecord->ExceptionCode + == EXCEPTION_SINGLE_STEP) { + return EXCEPTION_CONTINUE_SEARCH; + } + + sentry_ucontext_t uctx; + memset(&uctx, 0, sizeof(uctx)); + uctx.exception_ptrs = *ExceptionInfo; + handle_ucontext(&uctx); + return EXCEPTION_CONTINUE_SEARCH; +} +#endif static void handle_except(sentry_backend_t *UNUSED(backend), const sentry_ucontext_t *uctx) diff --git a/tests/conditions.py b/tests/conditions.py index b26e60e71..0fc3e2fae 100644 --- a/tests/conditions.py +++ b/tests/conditions.py @@ -7,7 +7,7 @@ has_crashpad = sys.platform != "linux" and not is_android # 32-bit linux has no proper curl support has_http = not is_android and not is_x86 -has_inproc = sys.platform != "win32" +has_inproc = True has_breakpad = sys.platform == "linux" # android has no local filesystem has_files = not is_android