Skip to content

Commit

Permalink
feat: Add an inproc backend on Windows (#287)
Browse files Browse the repository at this point in the history
  • Loading branch information
eakoli authored Jun 17, 2020
1 parent feee796 commit b972a24
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 27 deletions.
3 changes: 0 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,6 @@ endif()
if(SENTRY_BACKEND_BREAKPAD AND NOT (LINUX OR WIN32))
message(FATAL_ERROR "The Breakpad backend is currently only supported on Linux and Windows")
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}")
Expand Down
132 changes: 109 additions & 23 deletions src/backends/sentry_backend_inproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
{
Expand Down Expand Up @@ -71,6 +74,14 @@ invoke_signal_handler(int signum, siginfo_t *info, void *user_context)
static void
startup_inproc_backend(sentry_backend_t *UNUSED(backend))
{
g_signal_stack.ss_sp = sentry_malloc(SIGNAL_STACK_SIZE);
g_signal_stack.ss_size = SIGNAL_STACK_SIZE;
g_signal_stack.ss_flags = 0;
memset(g_previous_handlers, 0, sizeof(g_previous_handlers));
sigemptyset(&g_sigaction.sa_mask);
g_sigaction.sa_sigaction = handle_signal;
g_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK;

sigaltstack(&g_signal_stack, 0);

for (size_t i = 0; i < SIGNAL_COUNT; ++i) {
Expand All @@ -86,6 +97,68 @@ startup_inproc_backend(sentry_backend_t *UNUSED(backend))
}
}

static void
shutdown_inproc_backend(sentry_backend_t *UNUSED(backend))
{
g_signal_stack.ss_flags = SS_DISABLE;
sigaltstack(&g_signal_stack, 0);
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)
Expand Down Expand Up @@ -124,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++) {
Expand Down Expand Up @@ -153,18 +226,27 @@ 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();

// inform the sentry_sync system that we're in a signal handler. This will
// 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);
Expand All @@ -187,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
Expand All @@ -195,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)
{
Expand All @@ -206,19 +291,28 @@ handle_signal(int signum, siginfo_t *info, void *user_context)
uctx.user_context = (ucontext_t *)user_context;
handle_ucontext(&uctx);
}

static void
handle_except(sentry_backend_t *UNUSED(backend), const sentry_ucontext_t *uctx)
#elif defined SENTRY_PLATFORM_WINDOWS
static LONG WINAPI
handle_exception(EXCEPTION_POINTERS *ExceptionInfo)
{
handle_ucontext(uctx);
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
free_backend(sentry_backend_t *UNUSED(backend))
handle_except(sentry_backend_t *UNUSED(backend), const sentry_ucontext_t *uctx)
{
g_signal_stack.ss_flags = SS_DISABLE;
sigaltstack(&g_signal_stack, 0);
sentry_free(g_signal_stack.ss_sp);
handle_ucontext(uctx);
}

sentry_backend_t *
Expand All @@ -229,18 +323,10 @@ sentry__backend_new(void)
return NULL;
}

g_signal_stack.ss_sp = sentry_malloc(SIGNAL_STACK_SIZE);
g_signal_stack.ss_size = SIGNAL_STACK_SIZE;
g_signal_stack.ss_flags = 0;
memset(g_previous_handlers, 0, sizeof(g_previous_handlers));
sigemptyset(&g_sigaction.sa_mask);
g_sigaction.sa_sigaction = handle_signal;
g_sigaction.sa_flags = SA_SIGINFO | SA_ONSTACK;

backend->startup_func = startup_inproc_backend;
backend->shutdown_func = NULL;
backend->shutdown_func = shutdown_inproc_backend;
backend->except_func = handle_except;
backend->free_func = free_backend;
backend->free_func = NULL;
backend->flush_scope_func = NULL;
backend->add_breadcrumb_func = NULL;
backend->user_consent_changed_func = NULL;
Expand Down
2 changes: 1 addition & 1 deletion tests/conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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" or sys.platform == "win32"
# android has no local filesystem
has_files = not is_android

0 comments on commit b972a24

Please sign in to comment.