Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows inproc backend #287

Merged
merged 2 commits into from
Jun 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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