From 8dcc1847a421ec3ff91563a8ccb8736ba0f92388 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 6 Feb 2025 17:43:26 +0100 Subject: [PATCH 1/2] feat: add `sentry_trigger_dump()`. --- CHANGELOG.md | 6 ++++ examples/example.c | 3 ++ external/crashpad | 2 +- include/sentry.h | 12 +++++++ src/backends/sentry_backend_crashpad.cpp | 41 ++++++++++++++++++++---- src/sentry_backend.h | 1 + src/sentry_core.c | 11 +++++++ 7 files changed, 69 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba5eda662..bf37ab3bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +**Features**: + +- Add `sentry_trigger_dump()` a function allowing application to trigger non-fatal minidumps when using the `crashpad` backend. ([#1144](https://github.com/getsentry/sentry-native/pull/1144)) + ## 0.7.20 **Features**: diff --git a/examples/example.c b/examples/example.c index 6e48ed8a5..47f406644 100644 --- a/examples/example.c +++ b/examples/example.c @@ -367,6 +367,9 @@ main(int argc, char **argv) sleep_s(10); } + if (has_arg(argc, argv, "trigger-dump")) { + sentry_trigger_dump(); + } if (has_arg(argc, argv, "crash")) { trigger_crash(); } diff --git a/external/crashpad b/external/crashpad index 4cc763c2d..bf88e47be 160000 --- a/external/crashpad +++ b/external/crashpad @@ -1 +1 @@ -Subproject commit 4cc763c2d292c5581f065f88f1d5eebeb581f4ac +Subproject commit bf88e47be186a694ba4521a29882bb6d5573f9ab diff --git a/include/sentry.h b/include/sentry.h index 39f4b2cc7..878be04fa 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -1424,6 +1424,18 @@ SENTRY_API void sentry_capture_minidump_n(const char *path, size_t path_len); SENTRY_EXPERIMENTAL_API void sentry_handle_exception( const sentry_ucontext_t *uctx); +/** + * Captures a minidump for non-fatal errors. The program can continue after + * this call safely. It will produce a minidump context on the spot and crash- + * events will appear with log-level `error`. + * + * This trigger will also invoke the crash-handler, meaning it will call + * the `before_send` or `on_crash` hooks. + * + * Note: currently only works with the `crashpad` backend on Windows and Linux. + */ +SENTRY_EXPERIMENTAL_API void sentry_trigger_dump(void); + /** * Adds the breadcrumb to be sent in case of an event. */ diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 08f60d290..9611e60f6 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -260,11 +260,19 @@ sentry__crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) SENTRY_INFO("flushing session and queue before crashpad handler"); bool should_dump = true; +# if defined(SENTRY_PLATFORM_WINDOWS) + bool is_fatal = ExceptionInfo->ExceptionRecord->ExceptionCode != 0x517a7ed; +# elif defined(SENTRY_PLATFORM_LINUX) + bool is_fatal = signum != -1; +# else +bool is_fatal = true; +# endif SENTRY_WITH_OPTIONS (options) { sentry_value_t crash_event = sentry_value_new_event(); - sentry_value_set_by_key( - crash_event, "level", sentry__value_new_level(SENTRY_LEVEL_FATAL)); + sentry_value_t level = sentry__value_new_level( + is_fatal ? SENTRY_LEVEL_FATAL : SENTRY_LEVEL_ERROR); + sentry_value_set_by_key(crash_event, "level", level); if (options->on_crash_func) { sentry_ucontext_t uctx; @@ -332,11 +340,18 @@ sentry__crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) // (currently returning `true`) // // TODO(supervacuus): - // * we need integration tests for more signal/exception types not only - // for unmapped memory access (which is the current crash in example.c). // * we should adapt the SetFirstChanceExceptionHandler interface in // crashpad - if (!should_dump) { + // + // We now also use this handler for non-fatal dumps in which case we don't + // want to terminate here: + // is_fatal | should_dump | expected behavior + // ------------------------------------------ + // false | false | return true + // false | true | return false + // true | false | terminate + // true | true | return false + if (is_fatal && !should_dump) { # ifdef SENTRY_PLATFORM_WINDOWS TerminateProcess(GetCurrentProcess(), crashpad::TerminationCodes::kTerminationCodeCrashNoDump); @@ -346,7 +361,7 @@ sentry__crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) } // we did not "handle" the signal, so crashpad should do that. - return false; + return !is_fatal && !should_dump; } #endif @@ -622,6 +637,19 @@ crashpad_backend_prune_database(sentry_backend_t *backend) crashpad::PruneCrashReportDatabase(data->db, &condition); } +static void +crashpad_backend_trigger_dump(sentry_backend_t *backend) +{ + (void)backend; + crashpad::NativeCPUContext cpu_context; + crashpad::CaptureContext(&cpu_context); +#if defined(SENTRY_PLATFORM_LINUX) + crashpad::CrashpadClient::DumpWithoutCrash(&cpu_context); +#elif defined(SENTRY_PLATFORM_WINDOWS) + crashpad::CrashpadClient::DumpWithoutCrash(cpu_context); +#endif +} + sentry_backend_t * sentry__backend_new(void) { @@ -643,6 +671,7 @@ sentry__backend_new(void) backend->startup_func = crashpad_backend_startup; backend->shutdown_func = crashpad_backend_shutdown; backend->except_func = crashpad_backend_except; + backend->trigger_dump_func = crashpad_backend_trigger_dump; backend->free_func = crashpad_backend_free; backend->flush_scope_func = crashpad_backend_flush_scope; backend->add_breadcrumb_func = crashpad_backend_add_breadcrumb; diff --git a/src/sentry_backend.h b/src/sentry_backend.h index 0c6969278..ef007f56f 100644 --- a/src/sentry_backend.h +++ b/src/sentry_backend.h @@ -17,6 +17,7 @@ struct sentry_backend_s { void (*shutdown_func)(sentry_backend_t *); void (*free_func)(sentry_backend_t *); void (*except_func)(sentry_backend_t *, const struct sentry_ucontext_s *); + void (*trigger_dump_func)(sentry_backend_t *); void (*flush_scope_func)( sentry_backend_t *, const sentry_options_t *options); // NOTE: The breadcrumb is not moved into the hook and does not need to be diff --git a/src/sentry_core.c b/src/sentry_core.c index 5582d27b8..c136962c4 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -609,6 +609,17 @@ sentry_handle_exception(const sentry_ucontext_t *uctx) } } +void +sentry_trigger_dump(void) +{ + SENTRY_WITH_OPTIONS (options) { + SENTRY_INFO("triggering dump"); + if (options->backend && options->backend->except_func) { + options->backend->trigger_dump_func(options->backend); + } + } +} + sentry_uuid_t sentry__new_event_id(void) { From 3792c055315ded0f7bfea5dd2d11456282571bf9 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 6 Feb 2025 18:00:51 +0100 Subject: [PATCH 2/2] add macOS --- src/backends/sentry_backend_crashpad.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 9611e60f6..869b1a19c 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -41,6 +41,9 @@ extern "C" { #if defined(_WIN32) # include "util/win/termination_codes.h" #endif +#if defined(SENTRY_PLATFORM_MACOS) +# include "client/simulate_crash_mac.h" +#endif #if defined(__GNUC__) # pragma GCC diagnostic pop @@ -647,6 +650,8 @@ crashpad_backend_trigger_dump(sentry_backend_t *backend) crashpad::CrashpadClient::DumpWithoutCrash(&cpu_context); #elif defined(SENTRY_PLATFORM_WINDOWS) crashpad::CrashpadClient::DumpWithoutCrash(cpu_context); +#elif defined(SENTRY_PLATFORM_MACOS) + crashpad::SimulateCrash(cpu_context); #endif }