diff --git a/examples/example.c b/examples/example.c index 2ed6ae971..1c1275975 100644 --- a/examples/example.c +++ b/examples/example.c @@ -154,6 +154,10 @@ main(int argc, char **argv) } } + if (has_arg(argc, argv, "reinstall")) { + sentry_reinstall_backend(); + } + if (has_arg(argc, argv, "sleep")) { sleep_s(10); } diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 06e028934..f92e62ddc 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -44,6 +44,27 @@ extern "C" { #ifdef SENTRY_PLATFORM_LINUX # define SIGNAL_STACK_SIZE 65536 static stack_t g_signal_stack; + +# include "util/posix/signals.h" + +// This list was taken from crashpad's util/posix/signals.cc file +// and is used to know which signals we need to reset to default +// when shutting down the backend +constexpr int g_CrashSignals[] = { + SIGABRT, + SIGBUS, + SIGFPE, + SIGILL, + SIGQUIT, + SIGSEGV, + SIGSYS, + SIGTRAP, +# if defined(SIGEMT) + SIGEMT, +# endif // defined(SIGEMT) + SIGXCPU, + SIGXFSZ, +}; #endif typedef struct { @@ -281,6 +302,15 @@ sentry__crashpad_backend_startup( static void sentry__crashpad_backend_shutdown(sentry_backend_t *backend) { +#ifdef SENTRY_PLATFORM_LINUX + // restore signal handlers to their default state + for (const auto signal : g_CrashSignals) { + if (crashpad::Signals::IsCrashSignal(signal)) { + crashpad::Signals::InstallDefaultHandler(signal); + } + } +#endif + crashpad_state_t *data = (crashpad_state_t *)backend->data; delete data->db; data->db = nullptr; diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index d3b77fd40..c15493dab 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -111,6 +111,7 @@ shutdown_inproc_backend(sentry_backend_t *UNUSED(backend)) sigaltstack(&g_signal_stack, 0); sentry_free(g_signal_stack.ss_sp); g_signal_stack.ss_sp = NULL; + reset_signal_handlers(); } #elif defined SENTRY_PLATFORM_WINDOWS diff --git a/tests/requirements.txt b/tests/requirements.txt index 892a12bc4..440b71546 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,3 @@ black==19.10b0 pytest==5.4.1 -pytest-httpserver==0.3.4 +pytest-httpserver==1.0.0 diff --git a/tests/test_integration_crashpad.py b/tests/test_integration_crashpad.py index 8358bed7d..ba56b6ee2 100644 --- a/tests/test_integration_crashpad.py +++ b/tests/test_integration_crashpad.py @@ -29,6 +29,23 @@ def test_crashpad_capture(cmake, httpserver): assert len(httpserver.log) == 2 +def test_crashpad_reinstall(cmake, httpserver): + tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "crashpad"}) + + env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) + httpserver.expect_oneshot_request("/api/123456/minidump/").respond_with_data("OK") + + with httpserver.wait(timeout=10) as waiting: + child = run(tmp_path, "sentry_example", ["log", "reinstall", "crash"], env=env) + assert child.returncode # well, its a crash after all + + assert waiting.result + + run(tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env) + + assert len(httpserver.log) == 1 + + def test_crashpad_crash(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "crashpad"}) @@ -67,6 +84,9 @@ def test_crashpad_crash(cmake, httpserver): assert_crashpad_upload(multipart) +@pytest.mark.skipif( + sys.platform == "linux", reason="linux clears the signal handlers on shutdown" +) def test_crashpad_crash_after_shutdown(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "crashpad"}) diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 334f9d8da..3066368c4 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -216,6 +216,24 @@ def test_inproc_crash_http(cmake, httpserver): assert_crash(envelope) +def test_inproc_reinstall(cmake, httpserver): + tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "inproc"}) + + env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) + httpserver.expect_request( + "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + ).respond_with_data("OK") + + child = run(tmp_path, "sentry_example", ["log", "reinstall", "crash"], env=env,) + assert child.returncode # well, its a crash after all + + run( + tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env, + ) + + assert len(httpserver.log) == 1 + + def test_inproc_dump_inflight(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "inproc"}) @@ -268,6 +286,25 @@ def test_breakpad_crash_http(cmake, httpserver): assert_minidump(envelope) +@pytest.mark.skipif(not has_breakpad, reason="test needs breakpad backend") +def test_breakpad_reinstall(cmake, httpserver): + tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "breakpad"}) + + env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) + httpserver.expect_request( + "/api/123456/envelope/", headers={"x-sentry-auth": auth_header}, + ).respond_with_data("OK") + + child = run(tmp_path, "sentry_example", ["log", "reinstall", "crash"], env=env,) + assert child.returncode # well, its a crash after all + + run( + tmp_path, "sentry_example", ["log", "no-setup"], check=True, env=env, + ) + + assert len(httpserver.log) == 1 + + @pytest.mark.skipif(not has_breakpad, reason="test needs breakpad backend") def test_breakpad_dump_inflight(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "breakpad"})