From e4e703e453b2c090b9c68bdd70c62c8a5b335a6f Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 2 Jun 2026 15:31:21 +0200 Subject: [PATCH 01/28] feat(native): WER report --- CMakeLists.txt | 3 + src/backends/native/sentry_crash_context.h | 9 ++- src/backends/native/sentry_crash_daemon.c | 90 +++++++++++++++++++++- src/backends/native/sentry_crash_handler.c | 11 +-- src/backends/native/sentry_wer.c | 62 +++++++++++---- src/backends/sentry_backend_native.c | 13 ++-- tests/test_integration_native.py | 37 ++++++++- 7 files changed, 190 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bacef22283..b2911a50ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -912,6 +912,9 @@ elseif(SENTRY_BACKEND_NATIVE) add_dependencies(sentry sentry-crash) if(WIN32 AND NOT XBOX) add_dependencies(sentry sentry-wer) + # TODO: resolve symbols at run-time + target_link_libraries(sentry PRIVATE wer) + target_link_libraries(sentry-crash PRIVATE wer) endif() # Install daemon diff --git a/src/backends/native/sentry_crash_context.h b/src/backends/native/sentry_crash_context.h index 40f43b4f18..0d76e93f4a 100644 --- a/src/backends/native/sentry_crash_context.h +++ b/src/backends/native/sentry_crash_context.h @@ -122,8 +122,10 @@ typedef enum { SENTRY_CRASH_STATE_READY = 0, SENTRY_CRASH_STATE_CRASHED = 1, SENTRY_CRASH_STATE_PROCESSING = 2, - SENTRY_CRASH_STATE_CAPTURED = 3, - SENTRY_CRASH_STATE_DONE = 4 + SENTRY_CRASH_STATE_POSTPROCESSING = 3, + SENTRY_CRASH_STATE_POSTPROCESSED = 4, + SENTRY_CRASH_STATE_CAPTURED = 5, + SENTRY_CRASH_STATE_DONE = 6 } sentry_crash_state_t; /** @@ -242,6 +244,9 @@ typedef struct { // Additional thread contexts DWORD num_threads; sentry_thread_context_windows_t threads[SENTRY_CRASH_MAX_THREADS]; + + bool wer_enabled; + char wer_report_id[64]; } sentry_crash_platform_windows_t; typedef struct { diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 93397233f8..2e84da361b 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -56,6 +56,7 @@ # include # include # include +# include # include // Forward declaration for StackWalk64-based stack unwinding (defined later) @@ -2211,6 +2212,43 @@ enumerate_threads_from_process(sentry_crash_context_t *ctx) ctx->crashed_pid); # endif // SENTRY_PLATFORM_XBOX } + +static sentry_path_t * +find_wer_report(sentry_uuid_t *report_id) +{ + HREPORTSTORE report_store; + if (WerStoreOpen(E_STORE_MACHINE_ARCHIVE, &report_store) != S_OK) { + return NULL; + } + + PCWSTR report_key = NULL; + sentry_path_t *report_path = NULL; + WER_REPORT_METADATA_V3 report_data = { 0 }; + + HRESULT hr = WerStoreGetFirstReportKey(report_store, &report_key); + while (SUCCEEDED(hr) && report_key && !report_path) { + if (WerStoreQueryReportMetadataV3( + report_store, report_key, &report_data) + == S_OK) { + sentry_uuid_t integrator_id + = sentry__uuid_from_native(&report_data.ReportIntegratorId); + if (memcmp(&integrator_id, report_id, sizeof(sentry_uuid_t)) == 0) { + sentry_path_t *report_dir = sentry__path_from_wstr(report_key); + if (report_dir) { + report_path + = sentry__path_join_str(report_dir, "Report.wer"); + sentry__path_free(report_dir); + } + } + } + WerFreeString(report_key); + hr = WerStoreGetNextReportKey(report_store, &report_key); + } + + WerStoreClose(report_store); + + return report_path; +} #endif // SENTRY_PLATFORM_WINDOWS /** @@ -2819,6 +2857,16 @@ write_envelope_with_native_stacktrace(const sentry_options_t *options, } } + if (/*ctx->attach_wer_report &&*/ run_folder) { + sentry_path_t *report_path + = sentry__path_join_str(run_folder, "Report.wer"); + if (report_path) { + write_attachment_to_envelope(fd, report_path->path, "Report.wer", + NULL, "application/octet-stream"); + sentry__path_free(report_path); + } + } + #if defined(SENTRY_PLATFORM_UNIX) close(fd); #elif defined(SENTRY_PLATFORM_WINDOWS) @@ -3288,7 +3336,8 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) } #endif - // On Windows, capture modules and threads from the crashed process + // On Windows, capture modules, threads, and WER report for the crashed + // process #if defined(SENTRY_PLATFORM_WINDOWS) if (use_native_mode) { if (ctx->module_count == 0) { @@ -3300,6 +3349,45 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) enumerate_threads_from_process(ctx); } } + + if (ctx->platform.wer_enabled) { + SENTRY_DEBUG("Waiting for WER"); + sentry__atomic_store(&ctx->state, SENTRY_CRASH_STATE_POSTPROCESSING); + + int elapsed_ms = 0; + while (elapsed_ms < SENTRY_CRASH_HANDLER_WAIT_TIMEOUT_MS) { + long state = sentry__atomic_fetch(&ctx->state); + if (state == SENTRY_CRASH_STATE_POSTPROCESSED) { + break; + } + Sleep(SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS); + elapsed_ms += SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS; + } + SENTRY_DEBUGF("WER report ID: %s", ctx->platform.wer_report_id); + + sentry_uuid_t report_id + = sentry_uuid_from_string(ctx->platform.wer_report_id); + + elapsed_ms = 0; + while (elapsed_ms < SENTRY_CRASH_HANDLER_WAIT_TIMEOUT_MS) { + sentry_path_t *wer_report_path = find_wer_report(&report_id); + if (wer_report_path) { + SENTRY_DEBUGF("Found WER report: %s", wer_report_path->path); + sentry_path_t *run_report_path + = sentry__path_join_str(run_folder, "Report.wer"); + if (!run_report_path + || !sentry__path_copy(wer_report_path, run_report_path)) { + SENTRY_WARN("Failed to copy WER report"); + } + sentry__path_free(run_report_path); + sentry__path_free(wer_report_path); + break; + } + + Sleep(SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS); + elapsed_ms += SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS; + } + } #endif // Write envelope based on mode diff --git a/src/backends/native/sentry_crash_handler.c b/src/backends/native/sentry_crash_handler.c index 3d6658a7c9..1fc9093a1f 100644 --- a/src/backends/native/sentry_crash_handler.c +++ b/src/backends/native/sentry_crash_handler.c @@ -1026,17 +1026,12 @@ crash_exception_filter(EXCEPTION_POINTERS *exception_info) // Wait for daemon to finish processing (keep process alive for // minidump) - bool processing_started = false; int elapsed_ms = 0; while (elapsed_ms < SENTRY_CRASH_HANDLER_WAIT_TIMEOUT_MS) { long state = sentry__atomic_fetch(&ctx->state); - if (state == SENTRY_CRASH_STATE_PROCESSING && !processing_started) { - // Daemon started processing (no logging - exception filter - // context) - processing_started = true; - } else if (state >= SENTRY_CRASH_STATE_CAPTURED) { - // Daemon captured crash data (no logging - exception filter - // context) + if (state >= SENTRY_CRASH_STATE_POSTPROCESSING) { + // Either WER is post-processing, or daemon already captured + // crash data (no logging - exception filter context) break; } Sleep(SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS); diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index 37542528c3..617faba1b2 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -14,21 +14,21 @@ # define STATUS_STACK_BUFFER_OVERRUN ((DWORD)0xC0000409) #endif +typedef struct { + DWORD dwSize; + HANDLE hProcess; + HANDLE hThread; + EXCEPTION_RECORD exceptionRecord; + CONTEXT context; + PCWSTR pwszReportId; + BOOL bIsFatal; + DWORD dwReserved; +} WER_RUNTIME_EXCEPTION_INFORMATION_19041; + static BOOL is_fatal_wer_exception(const WER_RUNTIME_EXCEPTION_INFORMATION *info) { // bIsFatal is missing in older SDKs; guard access with dwSize. - typedef struct { - DWORD dwSize; - HANDLE hProcess; - HANDLE hThread; - EXCEPTION_RECORD exceptionRecord; - CONTEXT context; - PCWSTR pwszReportId; - BOOL bIsFatal; - DWORD dwReserved; - } WER_RUNTIME_EXCEPTION_INFORMATION_19041; - if (!info || info->dwSize <= offsetof(WER_RUNTIME_EXCEPTION_INFORMATION_19041, bIsFatal)) { @@ -38,6 +38,20 @@ is_fatal_wer_exception(const WER_RUNTIME_EXCEPTION_INFORMATION *info) return ((const WER_RUNTIME_EXCEPTION_INFORMATION_19041 *)info)->bIsFatal; } +static PCWSTR +get_report_id(const WER_RUNTIME_EXCEPTION_INFORMATION *info) +{ + // pwszReportId is missing in older SDKs; guard access with dwSize. + if (!info + || info->dwSize <= offsetof( + WER_RUNTIME_EXCEPTION_INFORMATION_19041, pwszReportId)) { + return NULL; + } + + return ((const WER_RUNTIME_EXCEPTION_INFORMATION_19041 *)info) + ->pwszReportId; +} + static BOOL is_native_wer_exception(DWORD code) { @@ -113,12 +127,6 @@ static BOOL process_wer_exception( PVOID context, const WER_RUNTIME_EXCEPTION_INFORMATION *exception_info) { - if (!exception_info || !is_fatal_wer_exception(exception_info) - || !is_native_wer_exception( - exception_info->exceptionRecord.ExceptionCode)) { - return FALSE; - } - sentry_wer_registration_t registration = { 0 }; if (!read_registration(exception_info->hProcess, context, ®istration)) { return FALSE; @@ -131,6 +139,26 @@ process_wer_exception( return FALSE; } + PCWSTR report_id = get_report_id(exception_info); + if (report_id) { + WideCharToMultiByte(CP_UTF8, 0, report_id, -1, + ctx->platform.wer_report_id, + (int)sizeof(ctx->platform.wer_report_id), NULL, NULL); + } + + // advance POSTPROCESSING -> POSTPROCESSED + if (InterlockedCompareExchange(&ctx->state, + SENTRY_CRASH_STATE_POSTPROCESSED, SENTRY_CRASH_STATE_POSTPROCESSING) + == SENTRY_CRASH_STATE_POSTPROCESSING) { + return FALSE; + } + + if (!exception_info || !is_fatal_wer_exception(exception_info) + || !is_native_wer_exception( + exception_info->exceptionRecord.ExceptionCode)) { + return FALSE; + } + BOOL claimed = FALSE; if (InterlockedCompareExchange(&ctx->state, SENTRY_CRASH_STATE_PROCESSING, SENTRY_CRASH_STATE_READY) diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 3c3e508434..9b53692eda 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -124,21 +124,21 @@ wer_unregister_module(void) memset(&g_wer_registration, 0, sizeof(g_wer_registration)); } -static void +static bool wer_register_module(uint64_t app_tid) { windows_version_t win_ver; if (!sentry__get_windows_version(&win_ver) || win_ver.build < 19041) { SENTRY_WARN("Native WER module not registered, because Windows " "doesn't meet version requirements (build >= 19041)."); - return; + return false; } sentry_path_t *wer_path = wer_default_path(); if (!wer_path || !sentry__path_is_file(wer_path)) { SENTRY_WARN("Native WER module not found"); sentry__path_free(wer_path); - return; + return false; } const DWORD one = 1; @@ -146,7 +146,7 @@ wer_register_module(uint64_t app_tid) if (reg_res != ERROR_SUCCESS) { SENTRY_WARN("registering native WER module in registry failed"); sentry__path_free(wer_path); - return; + return false; } g_wer_registration.version = 1; @@ -160,11 +160,12 @@ wer_register_module(uint64_t app_tid) wer_delete_registry_value(wer_path); sentry__path_free(wer_path); memset(&g_wer_registration, 0, sizeof(g_wer_registration)); - return; + return false; } SENTRY_DEBUGF("registered native WER module \"%s\"", wer_path->path); g_wer_path = wer_path; + return true; } #endif @@ -526,7 +527,7 @@ native_backend_startup( } # if defined(SENTRY_PLATFORM_WINDOWS) && !defined(SENTRY_PLATFORM_XBOX) - wer_register_module(tid); + state->ipc->shmem->platform.wer_enabled = wer_register_module(tid); # endif if (sentry__crash_handler_init(state->ipc) < 0) { diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index 3132aa4f52..e2979e3673 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -89,7 +89,7 @@ def test_native_capture_crash(cmake, httpserver): ) @pytest.mark.with_wer @pytest.mark.parametrize("crash_arg", ["fastfail", "stack-buffer-overrun"]) -def test_native_wer(cmake, httpserver, crash_arg): +def test_native_wer_crash(cmake, httpserver, crash_arg): """Test WER crash capture with native backend""" tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "native"}) @@ -108,6 +108,41 @@ def test_native_wer(cmake, httpserver, crash_arg): envelope = Envelope.deserialize(httpserver.log[0][0].get_data()) assert_native_crash(envelope, exception_code=0xC0000409) + has_wer_report = any( + item.headers.get("type") == "attachment" + and item.headers.get("filename") == "Report.wer" + for item in envelope.items + ) + assert has_wer_report, "Should include WER report" + + +@pytest.mark.with_wer +def test_native_wer_report(cmake, httpserver): + """Test WER report capture with native backend""" + tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "native"}) + + httpserver.expect_oneshot_request("/api/123456/envelope/").respond_with_data("OK") + + with httpserver.wait(timeout=10) as waiting: + run_crash( + tmp_path, + "sentry_example", + ["log", "stdout", "crash"], + env=dict(os.environ, SENTRY_DSN=make_dsn(httpserver)), + ) + assert waiting.result + + assert len(httpserver.log) >= 1 + envelope = Envelope.deserialize(httpserver.log[0][0].get_data()) + assert_native_crash(envelope) + + has_wer_report = any( + item.headers.get("type") == "attachment" + and item.headers.get("filename") == "Report.wer" + for item in envelope.items + ) + assert has_wer_report, "Should include WER report" + @pytest.mark.skipif(not has_oom, reason="OOM test unreliable in this environment") def test_native_oom(cmake, httpserver): From 641bf368de150c96fac63b46e1ac43ce263e847c Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 07:54:28 +0200 Subject: [PATCH 02/28] ReportId vs. ReportIntegratorId --- src/backends/native/sentry_crash_daemon.c | 6 +++--- src/sentry_uuid.c | 13 +++++++++++++ src/sentry_uuid.h | 10 ++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 2e84da361b..5e97f454a7 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -2230,9 +2230,9 @@ find_wer_report(sentry_uuid_t *report_id) if (WerStoreQueryReportMetadataV3( report_store, report_key, &report_data) == S_OK) { - sentry_uuid_t integrator_id - = sentry__uuid_from_native(&report_data.ReportIntegratorId); - if (memcmp(&integrator_id, report_id, sizeof(sentry_uuid_t)) == 0) { + if (sentry__uuid_equal_native(report_id, &report_data.ReportId) + || sentry__uuid_equal_native( + report_id, &report_data.ReportIntegratorId)) { sentry_path_t *report_dir = sentry__path_from_wstr(report_key); if (report_dir) { report_path diff --git a/src/sentry_uuid.c b/src/sentry_uuid.c index 1a81ca20e4..f73bf5b023 100644 --- a/src/sentry_uuid.c +++ b/src/sentry_uuid.c @@ -148,6 +148,12 @@ sentry__uuid_as_filename(const sentry_uuid_t *uuid, const char *suffix) return buf; } +bool +sentry__uuid_equal(const sentry_uuid_t *a, const sentry_uuid_t *b) +{ + return memcmp(a->bytes, b->bytes, 16) == 0; +} + #ifdef SENTRY_PLATFORM_WINDOWS sentry_uuid_t sentry__uuid_from_native(const GUID *guid) @@ -171,4 +177,11 @@ sentry__uuid_from_native(const GUID *guid) rv.bytes[15] = (char)guid->Data4[7]; return rv; } + +bool +sentry__uuid_equal_native(const sentry_uuid_t *uuid, const GUID *guid) +{ + sentry_uuid_t guid_uuid = sentry__uuid_from_native(guid); + return sentry__uuid_equal(uuid, &guid_uuid); +} #endif diff --git a/src/sentry_uuid.h b/src/sentry_uuid.h index 8476e81727..ef9ae6644d 100644 --- a/src/sentry_uuid.h +++ b/src/sentry_uuid.h @@ -20,11 +20,21 @@ void sentry__span_uuid_as_string(const sentry_uuid_t *uuid, char str[17]); */ char *sentry__uuid_as_filename(const sentry_uuid_t *uuid, const char *suffix); +/** + * Compares two sentry UUIDs for equality. + */ +bool sentry__uuid_equal(const sentry_uuid_t *a, const sentry_uuid_t *b); + #ifdef SENTRY_PLATFORM_WINDOWS /** * Create a new UUID from the windows-native GUID type. */ sentry_uuid_t sentry__uuid_from_native(const GUID *guid); + +/** + * Compares a sentry UUID with a windows-native GUID for equality. + */ +bool sentry__uuid_equal_native(const sentry_uuid_t *uuid, const GUID *guid); #endif #endif From 9c3e82f553975a1f36e5f4127644b7fed5ceec62 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 08:06:51 +0200 Subject: [PATCH 03/28] close handles --- src/backends/native/sentry_wer.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index 617faba1b2..11bd6488ab 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -132,6 +132,7 @@ process_wer_exception( return FALSE; } + BOOL claimed = FALSE; HANDLE mapping = NULL; HANDLE event = NULL; sentry_crash_context_t *ctx = NULL; @@ -150,16 +151,15 @@ process_wer_exception( if (InterlockedCompareExchange(&ctx->state, SENTRY_CRASH_STATE_POSTPROCESSED, SENTRY_CRASH_STATE_POSTPROCESSING) == SENTRY_CRASH_STATE_POSTPROCESSING) { - return FALSE; + goto done; } if (!exception_info || !is_fatal_wer_exception(exception_info) || !is_native_wer_exception( exception_info->exceptionRecord.ExceptionCode)) { - return FALSE; + goto done; } - BOOL claimed = FALSE; if (InterlockedCompareExchange(&ctx->state, SENTRY_CRASH_STATE_PROCESSING, SENTRY_CRASH_STATE_READY) == SENTRY_CRASH_STATE_READY) { @@ -194,6 +194,7 @@ process_wer_exception( } } +done: CloseHandle(event); UnmapViewOfFile(ctx); CloseHandle(mapping); From 25861f60a0ed88991ce61ee2227714ac400c8cd6 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 08:40:55 +0200 Subject: [PATCH 04/28] attach_wer_report --- examples/example.c | 7 +++++-- include/sentry.h | 12 ++++++++++++ src/backends/native/sentry_crash_context.h | 1 + src/backends/native/sentry_crash_daemon.c | 7 ++++--- src/backends/sentry_backend_native.c | 1 + src/sentry_options.c | 7 +++++++ src/sentry_options.h | 1 + tests/test_integration_native.py | 4 ++-- 8 files changed, 33 insertions(+), 7 deletions(-) diff --git a/examples/example.c b/examples/example.c index d3a0832800..eadd33f781 100644 --- a/examples/example.c +++ b/examples/example.c @@ -878,6 +878,9 @@ main(int argc, char **argv) sentry_options_set_crash_upload_mode( options, SENTRY_CRASH_UPLOAD_MODE_ASYNC); } + if (has_arg(argc, argv, "attach-wer-report")) { + sentry_options_set_attach_wer_report(options, true); + } // E2E test mode: generate unique test ID for event correlation char e2e_test_id[37] = { 0 }; @@ -1102,8 +1105,8 @@ main(int argc, char **argv) sentry_scope_add_breadcrumb(scope, debug_crumb); if (has_arg(argc, argv, "attach-to-scope")) { - // assuming the example / test is run directly from the cmake build - // directory + // assuming the example / test is run directly from the cmake + // build directory sentry_scope_attach_file(scope, "./CMakeCache.txt"); sentry_attachment_t *bytes = sentry_scope_attach_bytes( scope, "\xc0\xff\xee", 3, "bytes.bin"); diff --git a/include/sentry.h b/include/sentry.h index 25416813e1..fb89e26597 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -1922,6 +1922,18 @@ SENTRY_API void sentry_options_set_crash_upload_mode( SENTRY_API sentry_crash_upload_mode_t sentry_options_get_crash_upload_mode( const sentry_options_t *opts); +/** + * Enables or disables attaching WER (Windows Error Reporting) reports. This is + * disabled by default. + * + * When enabled, a `Report.wer` file generated by WER is attached to crash + * events. + * + * This setting only has an effect when using the `native` backend on Windows. + */ +SENTRY_EXPERIMENTAL_API void sentry_options_set_attach_wer_report( + sentry_options_t *opts, int val); + /** * Enables a wait for the crash report upload to be finished before shutting * down. This is disabled by default. diff --git a/src/backends/native/sentry_crash_context.h b/src/backends/native/sentry_crash_context.h index 0d76e93f4a..fa5df30f94 100644 --- a/src/backends/native/sentry_crash_context.h +++ b/src/backends/native/sentry_crash_context.h @@ -286,6 +286,7 @@ typedef struct { bool attach_screenshot; // Screenshot attachment enabled in parent process bool attach_session_replay; // Session replay attachment enabled in parent // process + bool attach_wer_report; // WER report attachment enabled in parent process uint32_t session_replay_duration; // Requested session replay duration in // ms int cache_keep; // sentry_cache_keep_t diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 5e97f454a7..c1cfdba655 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -2857,12 +2857,13 @@ write_envelope_with_native_stacktrace(const sentry_options_t *options, } } - if (/*ctx->attach_wer_report &&*/ run_folder) { + // Add Report.wer if captured by WER + if (ctx->attach_wer_report && run_folder) { sentry_path_t *report_path = sentry__path_join_str(run_folder, "Report.wer"); if (report_path) { - write_attachment_to_envelope(fd, report_path->path, "Report.wer", - NULL, "application/octet-stream"); + write_attachment_to_envelope( + fd, report_path->path, "Report.wer", NULL, "text/plain"); sentry__path_free(report_path); } } diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 9b53692eda..3a6f531dfc 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -298,6 +298,7 @@ native_backend_startup( ctx->crash_reporting_mode = options->crash_reporting_mode; ctx->system_crash_reporter_enabled = options->system_crash_reporter_enabled; ctx->crash_upload_mode = options->crash_upload_mode; + ctx->attach_wer_report = options->attach_wer_report; // Pass debug logging setting to daemon ctx->debug_enabled = options->debug; diff --git a/src/sentry_options.c b/src/sentry_options.c index cb5bb936a6..e280dfa909 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -99,6 +99,7 @@ sentry_options_new(void) = SENTRY_CRASH_REPORTING_MODE_NATIVE_WITH_MINIDUMP; // Default: best of // both worlds opts->crash_upload_mode = SENTRY_CRASH_UPLOAD_MODE_SYNC; + opts->attach_wer_report = false; opts->http_retry = false; opts->send_client_reports = true; opts->enable_large_attachments = false; @@ -636,6 +637,12 @@ sentry_options_get_crash_upload_mode(const sentry_options_t *opts) return (sentry_crash_upload_mode_t)opts->crash_upload_mode; } +void +sentry_options_set_attach_wer_report(sentry_options_t *opts, int val) +{ + opts->attach_wer_report = !!val; +} + void sentry_options_set_crashpad_wait_for_upload( sentry_options_t *opts, int wait_for_upload) diff --git a/src/sentry_options.h b/src/sentry_options.h index 6f64bba436..c2ad889b3b 100644 --- a/src/sentry_options.h +++ b/src/sentry_options.h @@ -103,6 +103,7 @@ struct sentry_options_s { int crash_reporting_mode; // 0=minidump, 1=native, 2=native_with_minidump // (see sentry_crash_reporting_mode_t) int crash_upload_mode; // 0=sync, 1=async (see sentry_crash_upload_mode_t) + bool attach_wer_report; #ifdef SENTRY_PLATFORM_NX void (*network_connect_func)(void); diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index e2979e3673..e7dfabfa84 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -99,7 +99,7 @@ def test_native_wer_crash(cmake, httpserver, crash_arg): run_crash( tmp_path, "sentry_example", - ["log", "stdout", crash_arg], + ["log", "stdout", crash_arg, "attach-wer-report"], env=dict(os.environ, SENTRY_DSN=make_dsn(httpserver)), ) assert waiting.result @@ -127,7 +127,7 @@ def test_native_wer_report(cmake, httpserver): run_crash( tmp_path, "sentry_example", - ["log", "stdout", "crash"], + ["log", "stdout", "crash", "attach-wer-report"], env=dict(os.environ, SENTRY_DSN=make_dsn(httpserver)), ) assert waiting.result From 617239e6a4d3f8e27920c2ff69d4a5cdc57fcd54 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 08:50:35 +0200 Subject: [PATCH 05/28] !exception_info --- src/backends/native/sentry_wer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index 11bd6488ab..beccafc878 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -128,7 +128,9 @@ process_wer_exception( PVOID context, const WER_RUNTIME_EXCEPTION_INFORMATION *exception_info) { sentry_wer_registration_t registration = { 0 }; - if (!read_registration(exception_info->hProcess, context, ®istration)) { + if (!exception_info + || !read_registration( + exception_info->hProcess, context, ®istration)) { return FALSE; } @@ -154,7 +156,7 @@ process_wer_exception( goto done; } - if (!exception_info || !is_fatal_wer_exception(exception_info) + if (!is_fatal_wer_exception(exception_info) || !is_native_wer_exception( exception_info->exceptionRecord.ExceptionCode)) { goto done; From 7e04976ada438929c1a21b19f1552190a816e8db Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 08:54:55 +0200 Subject: [PATCH 06/28] respect attach_wer_report for the wait --- src/backends/native/sentry_crash_daemon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index c1cfdba655..a2d3b750d2 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -3351,7 +3351,7 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) } } - if (ctx->platform.wer_enabled) { + if (ctx->platform.wer_enabled && ctx->attach_wer_report && run_folder) { SENTRY_DEBUG("Waiting for WER"); sentry__atomic_store(&ctx->state, SENTRY_CRASH_STATE_POSTPROCESSING); From 57d308647abe83319006f26c484da93d00a9ee27 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 08:58:36 +0200 Subject: [PATCH 07/28] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33c7493bcf..936d37fb82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Add a `transfer_timeout` option for SDK-managed HTTP transports. ([#1741](https://github.com/getsentry/sentry-native/pull/1741)) - Apple: use `os_sync_wait_on_address` for the level-triggered waitable flag in the batcher on modern macOS(14.4+) and iOS(17.4+). ([#1765](https://github.com/getsentry/sentry-native/pull/1765)) - Native/macOS: add thread names. ([#1766](https://github.com/getsentry/sentry-native/pull/1766)) +- Native/Windows: add `attach_wer_report` option for attaching `Report.wer` generated by WER. ([#1777](https://github.com/getsentry/sentry-native/pull/1777)) **Fixes**: From bcac0cbf5c8859d156b25f99ff24b4a5c4c07925 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 09:01:35 +0200 Subject: [PATCH 08/28] no report --- src/backends/native/sentry_crash_daemon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index a2d3b750d2..099e3b8014 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -3351,7 +3351,8 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) } } - if (ctx->platform.wer_enabled && ctx->attach_wer_report && run_folder) { + if (ctx->platform.wer_enabled && ctx->attach_wer_report + && !sentry__string_empty(ctx->platform.wer_report_id) && run_folder) { SENTRY_DEBUG("Waiting for WER"); sentry__atomic_store(&ctx->state, SENTRY_CRASH_STATE_POSTPROCESSING); From eaf18c6e39f7ceae168d37ae749145b2e44e6677 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 09:37:50 +0200 Subject: [PATCH 09/28] fix minidump & report id --- src/backends/native/sentry_crash_daemon.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 099e3b8014..cd8f779f92 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -3120,6 +3120,17 @@ write_envelope_with_minidump(const sentry_options_t *options, } } + // Add Report.wer if captured by WER + if (ctx->attach_wer_report && run_folder) { + sentry_path_t *report_path + = sentry__path_join_str(run_folder, "Report.wer"); + if (report_path) { + write_attachment_to_envelope( + fd, report_path->path, "Report.wer", NULL, "text/plain"); + sentry__path_free(report_path); + } + } + #if defined(SENTRY_PLATFORM_UNIX) close(fd); #elif defined(SENTRY_PLATFORM_WINDOWS) @@ -3351,8 +3362,7 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) } } - if (ctx->platform.wer_enabled && ctx->attach_wer_report - && !sentry__string_empty(ctx->platform.wer_report_id) && run_folder) { + if (ctx->platform.wer_enabled && ctx->attach_wer_report && run_folder) { SENTRY_DEBUG("Waiting for WER"); sentry__atomic_store(&ctx->state, SENTRY_CRASH_STATE_POSTPROCESSING); From e8a360901f70e77faac906ed2f8f07c227ce1521 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 09:38:00 +0200 Subject: [PATCH 10/28] test all crash modes --- tests/test_integration_native.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index e7dfabfa84..6b61691299 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -117,7 +117,8 @@ def test_native_wer_crash(cmake, httpserver, crash_arg): @pytest.mark.with_wer -def test_native_wer_report(cmake, httpserver): +@pytest.mark.parametrize("crash_mode", ["native", "minidump", "native-with-minidump"]) +def test_native_wer_report(cmake, httpserver, crash_mode): """Test WER report capture with native backend""" tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "native"}) @@ -127,14 +128,15 @@ def test_native_wer_report(cmake, httpserver): run_crash( tmp_path, "sentry_example", - ["log", "stdout", "crash", "attach-wer-report"], + ["log", "stdout", "crash", "crash-mode", crash_mode, "attach-wer-report"], env=dict(os.environ, SENTRY_DSN=make_dsn(httpserver)), ) assert waiting.result assert len(httpserver.log) >= 1 envelope = Envelope.deserialize(httpserver.log[0][0].get_data()) - assert_native_crash(envelope) + if "native" in crash_mode: + assert_native_crash(envelope) has_wer_report = any( item.headers.get("type") == "attachment" From 26eb82195ac9f70133ccba318adf151c20b9fae1 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 09:39:13 +0200 Subject: [PATCH 11/28] fix sentry__path_copy check --- src/backends/native/sentry_crash_daemon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index cd8f779f92..84a4e807cc 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -3388,7 +3388,7 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) sentry_path_t *run_report_path = sentry__path_join_str(run_folder, "Report.wer"); if (!run_report_path - || !sentry__path_copy(wer_report_path, run_report_path)) { + || sentry__path_copy(wer_report_path, run_report_path)) { SENTRY_WARN("Failed to copy WER report"); } sentry__path_free(run_report_path); From b01fcd13a2c1cb3ce712a2f540a69a5e8f2662ac Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 10:23:38 +0200 Subject: [PATCH 12/28] simplify state machine --- src/backends/native/sentry_crash_context.h | 7 +-- src/backends/native/sentry_crash_daemon.c | 46 +++++++--------- src/backends/native/sentry_crash_handler.c | 6 +- src/backends/native/sentry_wer.c | 64 ++++------------------ 4 files changed, 37 insertions(+), 86 deletions(-) diff --git a/src/backends/native/sentry_crash_context.h b/src/backends/native/sentry_crash_context.h index fa5df30f94..7260107395 100644 --- a/src/backends/native/sentry_crash_context.h +++ b/src/backends/native/sentry_crash_context.h @@ -122,10 +122,9 @@ typedef enum { SENTRY_CRASH_STATE_READY = 0, SENTRY_CRASH_STATE_CRASHED = 1, SENTRY_CRASH_STATE_PROCESSING = 2, - SENTRY_CRASH_STATE_POSTPROCESSING = 3, - SENTRY_CRASH_STATE_POSTPROCESSED = 4, - SENTRY_CRASH_STATE_CAPTURED = 5, - SENTRY_CRASH_STATE_DONE = 6 + SENTRY_CRASH_STATE_PROCESSED = 3, + SENTRY_CRASH_STATE_CAPTURED = 4, + SENTRY_CRASH_STATE_DONE = 5 } sentry_crash_state_t; /** diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 84a4e807cc..72c4ce9b0c 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -3363,37 +3363,29 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) } if (ctx->platform.wer_enabled && ctx->attach_wer_report && run_folder) { - SENTRY_DEBUG("Waiting for WER"); - sentry__atomic_store(&ctx->state, SENTRY_CRASH_STATE_POSTPROCESSING); + SENTRY_DEBUG("Waiting for WER, allowing app process to exit"); + sentry__atomic_store(&ctx->state, SENTRY_CRASH_STATE_PROCESSED); int elapsed_ms = 0; while (elapsed_ms < SENTRY_CRASH_HANDLER_WAIT_TIMEOUT_MS) { - long state = sentry__atomic_fetch(&ctx->state); - if (state == SENTRY_CRASH_STATE_POSTPROCESSED) { - break; - } - Sleep(SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS); - elapsed_ms += SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS; - } - SENTRY_DEBUGF("WER report ID: %s", ctx->platform.wer_report_id); - - sentry_uuid_t report_id - = sentry_uuid_from_string(ctx->platform.wer_report_id); - - elapsed_ms = 0; - while (elapsed_ms < SENTRY_CRASH_HANDLER_WAIT_TIMEOUT_MS) { - sentry_path_t *wer_report_path = find_wer_report(&report_id); - if (wer_report_path) { - SENTRY_DEBUGF("Found WER report: %s", wer_report_path->path); - sentry_path_t *run_report_path - = sentry__path_join_str(run_folder, "Report.wer"); - if (!run_report_path - || sentry__path_copy(wer_report_path, run_report_path)) { - SENTRY_WARN("Failed to copy WER report"); + if (!sentry__string_empty(ctx->platform.wer_report_id)) { + sentry_uuid_t report_id + = sentry_uuid_from_string(ctx->platform.wer_report_id); + sentry_path_t *wer_report_path = find_wer_report(&report_id); + if (wer_report_path) { + SENTRY_DEBUGF("Found WER report %s: %s", + ctx->platform.wer_report_id, wer_report_path->path); + sentry_path_t *run_report_path + = sentry__path_join_str(run_folder, "Report.wer"); + if (!run_report_path + || sentry__path_copy( + wer_report_path, run_report_path)) { + SENTRY_WARN("Failed to copy WER report"); + } + sentry__path_free(run_report_path); + sentry__path_free(wer_report_path); + break; } - sentry__path_free(run_report_path); - sentry__path_free(wer_report_path); - break; } Sleep(SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS); diff --git a/src/backends/native/sentry_crash_handler.c b/src/backends/native/sentry_crash_handler.c index 1fc9093a1f..1d72721994 100644 --- a/src/backends/native/sentry_crash_handler.c +++ b/src/backends/native/sentry_crash_handler.c @@ -1029,9 +1029,9 @@ crash_exception_filter(EXCEPTION_POINTERS *exception_info) int elapsed_ms = 0; while (elapsed_ms < SENTRY_CRASH_HANDLER_WAIT_TIMEOUT_MS) { long state = sentry__atomic_fetch(&ctx->state); - if (state >= SENTRY_CRASH_STATE_POSTPROCESSING) { - // Either WER is post-processing, or daemon already captured - // crash data (no logging - exception filter context) + if (state >= SENTRY_CRASH_STATE_PROCESSED) { + // Daemon already processed crash data (no logging - exception + // filter context) break; } Sleep(SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS); diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index beccafc878..83b8ffb7d0 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -6,41 +6,20 @@ #include #include -#ifndef STATUS_FAIL_FAST_EXCEPTION -# define STATUS_FAIL_FAST_EXCEPTION ((DWORD)0xC0000602) -#endif - -#ifndef STATUS_STACK_BUFFER_OVERRUN -# define STATUS_STACK_BUFFER_OVERRUN ((DWORD)0xC0000409) -#endif - -typedef struct { - DWORD dwSize; - HANDLE hProcess; - HANDLE hThread; - EXCEPTION_RECORD exceptionRecord; - CONTEXT context; - PCWSTR pwszReportId; - BOOL bIsFatal; - DWORD dwReserved; -} WER_RUNTIME_EXCEPTION_INFORMATION_19041; - -static BOOL -is_fatal_wer_exception(const WER_RUNTIME_EXCEPTION_INFORMATION *info) -{ - // bIsFatal is missing in older SDKs; guard access with dwSize. - if (!info - || info->dwSize - <= offsetof(WER_RUNTIME_EXCEPTION_INFORMATION_19041, bIsFatal)) { - return FALSE; - } - - return ((const WER_RUNTIME_EXCEPTION_INFORMATION_19041 *)info)->bIsFatal; -} - static PCWSTR get_report_id(const WER_RUNTIME_EXCEPTION_INFORMATION *info) { + typedef struct { + DWORD dwSize; + HANDLE hProcess; + HANDLE hThread; + EXCEPTION_RECORD exceptionRecord; + CONTEXT context; + PCWSTR pwszReportId; + BOOL bIsFatal; + DWORD dwReserved; + } WER_RUNTIME_EXCEPTION_INFORMATION_19041; + // pwszReportId is missing in older SDKs; guard access with dwSize. if (!info || info->dwSize <= offsetof( @@ -52,13 +31,6 @@ get_report_id(const WER_RUNTIME_EXCEPTION_INFORMATION *info) ->pwszReportId; } -static BOOL -is_native_wer_exception(DWORD code) -{ - return code == STATUS_FAIL_FAST_EXCEPTION - || code == STATUS_STACK_BUFFER_OVERRUN; -} - static BOOL read_registration( HANDLE process, PVOID context, sentry_wer_registration_t *registration) @@ -149,19 +121,7 @@ process_wer_exception( (int)sizeof(ctx->platform.wer_report_id), NULL, NULL); } - // advance POSTPROCESSING -> POSTPROCESSED - if (InterlockedCompareExchange(&ctx->state, - SENTRY_CRASH_STATE_POSTPROCESSED, SENTRY_CRASH_STATE_POSTPROCESSING) - == SENTRY_CRASH_STATE_POSTPROCESSING) { - goto done; - } - - if (!is_fatal_wer_exception(exception_info) - || !is_native_wer_exception( - exception_info->exceptionRecord.ExceptionCode)) { - goto done; - } - + // SENTRY_CRASH_STATE_READY: hard WER crash that bypassed the crash handler if (InterlockedCompareExchange(&ctx->state, SENTRY_CRASH_STATE_PROCESSING, SENTRY_CRASH_STATE_READY) == SENTRY_CRASH_STATE_READY) { From 5511bb3669902bc5c73eb674dbb939b8a7afdfcf Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 14:15:25 +0200 Subject: [PATCH 13/28] resolve WER symbols & bump down from V3 to V2 WER_REPORT_METADATA_V2 is enough for the ID's we need: - ReportId - ReportIntegratorId https://learn.microsoft.com/en-us/windows/win32/api/werapi/ns-werapi-wer_report_metadata_v2 --- CMakeLists.txt | 3 - src/backends/native/sentry_crash_daemon.c | 77 ++++++++++++++++++++--- 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b2911a50ed..bacef22283 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -912,9 +912,6 @@ elseif(SENTRY_BACKEND_NATIVE) add_dependencies(sentry sentry-crash) if(WIN32 AND NOT XBOX) add_dependencies(sentry sentry-wer) - # TODO: resolve symbols at run-time - target_link_libraries(sentry PRIVATE wer) - target_link_libraries(sentry-crash PRIVATE wer) endif() # Install daemon diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 72c4ce9b0c..67d4481294 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -2213,21 +2213,84 @@ enumerate_threads_from_process(sentry_crash_context_t *ctx) # endif // SENTRY_PLATFORM_XBOX } +typedef HRESULT(WINAPI *WerStoreOpen_t)(REPORT_STORE_TYPES, HREPORTSTORE *); +typedef void(WINAPI *WerStoreClose_t)(HREPORTSTORE); +typedef HRESULT(WINAPI *WerStoreGetFirstReportKey_t)(HREPORTSTORE, PCWSTR *); +typedef HRESULT(WINAPI *WerStoreGetNextReportKey_t)(HREPORTSTORE, PCWSTR *); +typedef HRESULT(WINAPI *WerStoreQueryReportMetadataV2_t)( + HREPORTSTORE, PCWSTR, PWER_REPORT_METADATA_V2); +typedef void(WINAPI *WerFreeString_t)(PCWSTR); + +static struct { + HMODULE module; + WerStoreOpen_t WerStoreOpen; + WerStoreClose_t WerStoreClose; + WerStoreGetFirstReportKey_t WerStoreGetFirstReportKey; + WerStoreGetNextReportKey_t WerStoreGetNextReportKey; + WerStoreQueryReportMetadataV2_t WerStoreQueryReportMetadataV2; + WerFreeString_t WerFreeString; +} g_wer = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + +# define WER_FAILED ((HMODULE)(intptr_t)-1) + +static bool +resolve_wer(void) +{ + if (g_wer.module) { + return g_wer.module != WER_FAILED; + } + + g_wer.module = LoadLibraryW(L"wer.dll"); + if (!g_wer.module) { + g_wer.module = WER_FAILED; + return false; + } + + g_wer.WerStoreOpen + = (WerStoreOpen_t)GetProcAddress(g_wer.module, "WerStoreOpen"); + g_wer.WerStoreClose + = (WerStoreClose_t)GetProcAddress(g_wer.module, "WerStoreClose"); + g_wer.WerStoreGetFirstReportKey + = (WerStoreGetFirstReportKey_t)GetProcAddress( + g_wer.module, "WerStoreGetFirstReportKey"); + g_wer.WerStoreGetNextReportKey = (WerStoreGetNextReportKey_t)GetProcAddress( + g_wer.module, "WerStoreGetNextReportKey"); + g_wer.WerStoreQueryReportMetadataV2 + = (WerStoreQueryReportMetadataV2_t)GetProcAddress( + g_wer.module, "WerStoreQueryReportMetadataV2"); + g_wer.WerFreeString + = (WerFreeString_t)GetProcAddress(g_wer.module, "WerFreeString"); + + if (!g_wer.WerStoreOpen || !g_wer.WerStoreClose + || !g_wer.WerStoreGetFirstReportKey || !g_wer.WerStoreGetNextReportKey + || !g_wer.WerStoreQueryReportMetadataV2 || !g_wer.WerFreeString) { + FreeLibrary(g_wer.module); + g_wer.module = WER_FAILED; + return false; + } + + return true; +} + static sentry_path_t * find_wer_report(sentry_uuid_t *report_id) { + if (!resolve_wer()) { + return NULL; + } + HREPORTSTORE report_store; - if (WerStoreOpen(E_STORE_MACHINE_ARCHIVE, &report_store) != S_OK) { + if (g_wer.WerStoreOpen(E_STORE_MACHINE_ARCHIVE, &report_store) != S_OK) { return NULL; } PCWSTR report_key = NULL; sentry_path_t *report_path = NULL; - WER_REPORT_METADATA_V3 report_data = { 0 }; + WER_REPORT_METADATA_V2 report_data = { 0 }; - HRESULT hr = WerStoreGetFirstReportKey(report_store, &report_key); + HRESULT hr = g_wer.WerStoreGetFirstReportKey(report_store, &report_key); while (SUCCEEDED(hr) && report_key && !report_path) { - if (WerStoreQueryReportMetadataV3( + if (g_wer.WerStoreQueryReportMetadataV2( report_store, report_key, &report_data) == S_OK) { if (sentry__uuid_equal_native(report_id, &report_data.ReportId) @@ -2241,11 +2304,11 @@ find_wer_report(sentry_uuid_t *report_id) } } } - WerFreeString(report_key); - hr = WerStoreGetNextReportKey(report_store, &report_key); + g_wer.WerFreeString(report_key); + hr = g_wer.WerStoreGetNextReportKey(report_store, &report_key); } - WerStoreClose(report_store); + g_wer.WerStoreClose(report_store); return report_path; } From 128601398d70bb92caa94cdde9cd79e8e36c8c61 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 14:42:50 +0200 Subject: [PATCH 14/28] revert accidental reformatting --- examples/example.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/example.c b/examples/example.c index eadd33f781..4a36d14be9 100644 --- a/examples/example.c +++ b/examples/example.c @@ -1105,8 +1105,8 @@ main(int argc, char **argv) sentry_scope_add_breadcrumb(scope, debug_crumb); if (has_arg(argc, argv, "attach-to-scope")) { - // assuming the example / test is run directly from the cmake - // build directory + // assuming the example / test is run directly from the cmake build + // directory sentry_scope_attach_file(scope, "./CMakeCache.txt"); sentry_attachment_t *bytes = sentry_scope_attach_bytes( scope, "\xc0\xff\xee", 3, "bytes.bin"); From e165e94101668b1adc0f3dce1375bbf1aa52e663 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 14:42:57 +0200 Subject: [PATCH 15/28] restore is_fatal_exception --- src/backends/native/sentry_wer.c | 43 +++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index 83b8ffb7d0..37c9212316 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -6,20 +6,33 @@ #include #include +typedef struct { + DWORD dwSize; + HANDLE hProcess; + HANDLE hThread; + EXCEPTION_RECORD exceptionRecord; + CONTEXT context; + PCWSTR pwszReportId; + BOOL bIsFatal; + DWORD dwReserved; +} WER_RUNTIME_EXCEPTION_INFORMATION_19041; + +static BOOL +is_fatal_wer_exception(const WER_RUNTIME_EXCEPTION_INFORMATION *info) +{ + // bIsFatal is missing in older SDKs; guard access with dwSize. + if (!info + || info->dwSize + <= offsetof(WER_RUNTIME_EXCEPTION_INFORMATION_19041, bIsFatal)) { + return FALSE; + } + + return ((const WER_RUNTIME_EXCEPTION_INFORMATION_19041 *)info)->bIsFatal; +} + static PCWSTR get_report_id(const WER_RUNTIME_EXCEPTION_INFORMATION *info) { - typedef struct { - DWORD dwSize; - HANDLE hProcess; - HANDLE hThread; - EXCEPTION_RECORD exceptionRecord; - CONTEXT context; - PCWSTR pwszReportId; - BOOL bIsFatal; - DWORD dwReserved; - } WER_RUNTIME_EXCEPTION_INFORMATION_19041; - // pwszReportId is missing in older SDKs; guard access with dwSize. if (!info || info->dwSize <= offsetof( @@ -99,10 +112,12 @@ static BOOL process_wer_exception( PVOID context, const WER_RUNTIME_EXCEPTION_INFORMATION *exception_info) { + if (!exception_info || !is_fatal_wer_exception(exception_info)) { + return FALSE; + } + sentry_wer_registration_t registration = { 0 }; - if (!exception_info - || !read_registration( - exception_info->hProcess, context, ®istration)) { + if (!read_registration(exception_info->hProcess, context, ®istration)) { return FALSE; } From d8c4e7b7ce9a25eaa087bd3ac809ee0ea380358d Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 14:44:24 +0200 Subject: [PATCH 16/28] minimize diff --- src/backends/native/sentry_wer.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index 37c9212316..2a54495017 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -121,7 +121,6 @@ process_wer_exception( return FALSE; } - BOOL claimed = FALSE; HANDLE mapping = NULL; HANDLE event = NULL; sentry_crash_context_t *ctx = NULL; @@ -129,6 +128,7 @@ process_wer_exception( return FALSE; } + BOOL claimed = FALSE; PCWSTR report_id = get_report_id(exception_info); if (report_id) { WideCharToMultiByte(CP_UTF8, 0, report_id, -1, @@ -171,7 +171,6 @@ process_wer_exception( } } -done: CloseHandle(event); UnmapViewOfFile(ctx); CloseHandle(mapping); From 10b8a7f44f3c28301fcba07b381d30f8deb1e620 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 15:06:38 +0200 Subject: [PATCH 17/28] fix sentry__path_copy rv handling --- src/backends/native/sentry_crash_daemon.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 67d4481294..db41a76999 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -3440,14 +3440,16 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) ctx->platform.wer_report_id, wer_report_path->path); sentry_path_t *run_report_path = sentry__path_join_str(run_folder, "Report.wer"); - if (!run_report_path - || sentry__path_copy( - wer_report_path, run_report_path)) { - SENTRY_WARN("Failed to copy WER report"); - } + int rv = run_report_path + ? sentry__path_copy(wer_report_path, run_report_path) + : -1; sentry__path_free(run_report_path); sentry__path_free(wer_report_path); - break; + if (rv != 0) { + SENTRY_WARN("Failed to copy WER report"); + } else { + break; + } } } From 155f698a1d2f58afaf505fe47a0bafdae356dcd6 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 15:14:18 +0200 Subject: [PATCH 18/28] E_STORE_USER_ARCHIVE vs. E_STORE_MACHINE_ARCHIVE --- src/backends/native/sentry_crash_daemon.c | 56 +++++++++++++---------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index db41a76999..9a62461071 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -2279,36 +2279,42 @@ find_wer_report(sentry_uuid_t *report_id) return NULL; } - HREPORTSTORE report_store; - if (g_wer.WerStoreOpen(E_STORE_MACHINE_ARCHIVE, &report_store) != S_OK) { - return NULL; - } - - PCWSTR report_key = NULL; sentry_path_t *report_path = NULL; - WER_REPORT_METADATA_V2 report_data = { 0 }; - - HRESULT hr = g_wer.WerStoreGetFirstReportKey(report_store, &report_key); - while (SUCCEEDED(hr) && report_key && !report_path) { - if (g_wer.WerStoreQueryReportMetadataV2( - report_store, report_key, &report_data) - == S_OK) { - if (sentry__uuid_equal_native(report_id, &report_data.ReportId) - || sentry__uuid_equal_native( - report_id, &report_data.ReportIntegratorId)) { - sentry_path_t *report_dir = sentry__path_from_wstr(report_key); - if (report_dir) { - report_path - = sentry__path_join_str(report_dir, "Report.wer"); - sentry__path_free(report_dir); + REPORT_STORE_TYPES store_types[] + = { E_STORE_USER_ARCHIVE, E_STORE_MACHINE_ARCHIVE }; + for (size_t i = 0; + !report_path && i < sizeof(store_types) / sizeof(store_types[0]); i++) { + HREPORTSTORE report_store; + if (g_wer.WerStoreOpen(store_types[i], &report_store) != S_OK) { + continue; + } + + PCWSTR report_key = NULL; + WER_REPORT_METADATA_V2 report_data = { 0 }; + + HRESULT hr = g_wer.WerStoreGetFirstReportKey(report_store, &report_key); + while (SUCCEEDED(hr) && report_key && !report_path) { + if (g_wer.WerStoreQueryReportMetadataV2( + report_store, report_key, &report_data) + == S_OK) { + if (sentry__uuid_equal_native(report_id, &report_data.ReportId) + || sentry__uuid_equal_native( + report_id, &report_data.ReportIntegratorId)) { + sentry_path_t *report_dir + = sentry__path_from_wstr(report_key); + if (report_dir) { + report_path + = sentry__path_join_str(report_dir, "Report.wer"); + sentry__path_free(report_dir); + } } } + g_wer.WerFreeString(report_key); + hr = g_wer.WerStoreGetNextReportKey(report_store, &report_key); } - g_wer.WerFreeString(report_key); - hr = g_wer.WerStoreGetNextReportKey(report_store, &report_key); - } - g_wer.WerStoreClose(report_store); + g_wer.WerStoreClose(report_store); + } return report_path; } From 4c9229a4dc8b5eb88fa4a666ac28d2e002f7a0e3 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 15:20:58 +0200 Subject: [PATCH 19/28] pytest skip != win32 --- tests/test_integration_native.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index 6b61691299..33395d39d5 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -116,6 +116,10 @@ def test_native_wer_crash(cmake, httpserver, crash_arg): assert has_wer_report, "Should include WER report" +@pytest.mark.skipif( + sys.platform != "win32", + reason="WER reports are only available on Windows", +) @pytest.mark.with_wer @pytest.mark.parametrize("crash_mode", ["native", "minidump", "native-with-minidump"]) def test_native_wer_report(cmake, httpserver, crash_mode): From e72acc25d6945c977fcf48f26758501b82b3c55d Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 15:26:27 +0200 Subject: [PATCH 20/28] check WideCharToMultiByte rv --- src/backends/native/sentry_wer.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/backends/native/sentry_wer.c b/src/backends/native/sentry_wer.c index 2a54495017..665841ac0f 100644 --- a/src/backends/native/sentry_wer.c +++ b/src/backends/native/sentry_wer.c @@ -130,10 +130,12 @@ process_wer_exception( BOOL claimed = FALSE; PCWSTR report_id = get_report_id(exception_info); - if (report_id) { - WideCharToMultiByte(CP_UTF8, 0, report_id, -1, - ctx->platform.wer_report_id, - (int)sizeof(ctx->platform.wer_report_id), NULL, NULL); + if (report_id + && WideCharToMultiByte(CP_UTF8, 0, report_id, -1, + ctx->platform.wer_report_id, + (int)sizeof(ctx->platform.wer_report_id), NULL, NULL) + <= 0) { + ctx->platform.wer_report_id[0] = '\0'; } // SENTRY_CRASH_STATE_READY: hard WER crash that bypassed the crash handler From 0ee38c10115e62c7cf39eb7c9babed590ff84eb5 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 15:29:33 +0200 Subject: [PATCH 21/28] Revert "fix sentry__path_copy rv handling" This reverts commit 10b8a7f44f3c28301fcba07b381d30f8deb1e620. --- src/backends/native/sentry_crash_daemon.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 9a62461071..401789665f 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -3446,16 +3446,14 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) ctx->platform.wer_report_id, wer_report_path->path); sentry_path_t *run_report_path = sentry__path_join_str(run_folder, "Report.wer"); - int rv = run_report_path - ? sentry__path_copy(wer_report_path, run_report_path) - : -1; - sentry__path_free(run_report_path); - sentry__path_free(wer_report_path); - if (rv != 0) { + if (!run_report_path + || sentry__path_copy( + wer_report_path, run_report_path)) { SENTRY_WARN("Failed to copy WER report"); - } else { - break; } + sentry__path_free(run_report_path); + sentry__path_free(wer_report_path); + break; } } From 8799ebe73e07c87c968d964636b2505a78a93f0f Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 15:34:36 +0200 Subject: [PATCH 22/28] fix leak --- src/backends/native/sentry_crash_daemon.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 401789665f..5f335a6248 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -2310,7 +2310,9 @@ find_wer_report(sentry_uuid_t *report_id) } } g_wer.WerFreeString(report_key); - hr = g_wer.WerStoreGetNextReportKey(report_store, &report_key); + if (!report_path) { + hr = g_wer.WerStoreGetNextReportKey(report_store, &report_key); + } } g_wer.WerStoreClose(report_store); From 5c812aaa49e32a2492e1824e8855dfdec8ead198 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 16:06:53 +0200 Subject: [PATCH 23/28] utf8 --- src/backends/native/sentry_crash_daemon.c | 52 +++++++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 5f335a6248..6c05602541 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -2320,6 +2320,47 @@ find_wer_report(sentry_uuid_t *report_id) return report_path; } + +/** + * Reads a WER report, converts it to UTF-16LE -> UTF-8, and writes to the .run + * directory. + * + * TODO: use sentry__path_copy: https://github.com/getsentry/sentry/issues/91336 + */ +static bool +write_wer_report(sentry_path_t *report_path, sentry_path_t *run_folder) +{ + sentry_path_t *run_path = sentry__path_join_str(run_folder, "Report.wer"); + if (!run_path) { + return false; + } + + size_t utf16_size = 0; + char *utf16 = sentry__path_read_to_buffer(report_path, &utf16_size); + if (!utf16) { + SENTRY_WARN("Failed to read WER report"); + return false; + } + + char *utf8 = sentry__string_from_wstr_n( + (const wchar_t *)utf16, utf16_size / sizeof(wchar_t)); + if (!utf8) { + SENTRY_WARN("Failed to convert WER report to UTF-8"); + return false; + } + + int rv = sentry__path_write_buffer(run_path, utf8, strlen(utf8)); + + sentry_free(utf8); + sentry_free(utf16); + sentry__path_free(run_path); + + if (rv != 0) { + SENTRY_WARN("Failed to write WER report"); + return false; + } + return true; +} #endif // SENTRY_PLATFORM_WINDOWS /** @@ -3446,16 +3487,9 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) if (wer_report_path) { SENTRY_DEBUGF("Found WER report %s: %s", ctx->platform.wer_report_id, wer_report_path->path); - sentry_path_t *run_report_path - = sentry__path_join_str(run_folder, "Report.wer"); - if (!run_report_path - || sentry__path_copy( - wer_report_path, run_report_path)) { - SENTRY_WARN("Failed to copy WER report"); + if (write_wer_report(wer_report_path, run_folder)) { + break; } - sentry__path_free(run_report_path); - sentry__path_free(wer_report_path); - break; } } From 8f61b96e312a5e6cefe8565ab647f9bc2ed6fc45 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 16:15:50 +0200 Subject: [PATCH 24/28] fix leaks --- src/backends/native/sentry_crash_daemon.c | 31 +++++++++++++---------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 6c05602541..87f46a8393 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -2330,36 +2330,39 @@ find_wer_report(sentry_uuid_t *report_id) static bool write_wer_report(sentry_path_t *report_path, sentry_path_t *run_folder) { + int rv = -1; + char *utf8 = NULL; + char *utf16 = NULL; + size_t utf16_size = 0; + sentry_path_t *run_path = sentry__path_join_str(run_folder, "Report.wer"); if (!run_path) { - return false; + goto cleanup; } - size_t utf16_size = 0; - char *utf16 = sentry__path_read_to_buffer(report_path, &utf16_size); + utf16 = sentry__path_read_to_buffer(report_path, &utf16_size); if (!utf16) { SENTRY_WARN("Failed to read WER report"); - return false; + goto cleanup; } - char *utf8 = sentry__string_from_wstr_n( + utf8 = sentry__string_from_wstr_n( (const wchar_t *)utf16, utf16_size / sizeof(wchar_t)); if (!utf8) { SENTRY_WARN("Failed to convert WER report to UTF-8"); - return false; + goto cleanup; } - int rv = sentry__path_write_buffer(run_path, utf8, strlen(utf8)); + if ((rv = sentry__path_write_buffer(run_path, utf8, strlen(utf8))) != 0) { + SENTRY_WARN("Failed to write WER report"); + goto cleanup; + } +cleanup: sentry_free(utf8); sentry_free(utf16); sentry__path_free(run_path); - - if (rv != 0) { - SENTRY_WARN("Failed to write WER report"); - return false; - } - return true; + return rv == 0; } #endif // SENTRY_PLATFORM_WINDOWS @@ -3488,8 +3491,10 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) SENTRY_DEBUGF("Found WER report %s: %s", ctx->platform.wer_report_id, wer_report_path->path); if (write_wer_report(wer_report_path, run_folder)) { + sentry_path_free(wer_report_path); break; } + sentry_path_free(wer_report_path); } } From 84a45681874475535060c1f9d32b7e1c0815ce85 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 16:16:37 +0200 Subject: [PATCH 25/28] sys32 --- src/backends/native/sentry_crash_daemon.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 87f46a8393..fd1b84ed6f 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -2240,7 +2240,8 @@ resolve_wer(void) return g_wer.module != WER_FAILED; } - g_wer.module = LoadLibraryW(L"wer.dll"); + g_wer.module + = LoadLibraryExW(L"wer.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); if (!g_wer.module) { g_wer.module = WER_FAILED; return false; @@ -3491,10 +3492,10 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) SENTRY_DEBUGF("Found WER report %s: %s", ctx->platform.wer_report_id, wer_report_path->path); if (write_wer_report(wer_report_path, run_folder)) { - sentry_path_free(wer_report_path); + sentry__path_free(wer_report_path); break; } - sentry_path_free(wer_report_path); + sentry__path_free(wer_report_path); } } From e5699a4baaf5196ecd763076fd479e52127ac943 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 16:36:26 +0200 Subject: [PATCH 26/28] !SENTRY_PLATFORM_XBOX --- src/backends/native/sentry_crash_daemon.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index fd1b84ed6f..5ba0d07b43 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -2213,6 +2213,7 @@ enumerate_threads_from_process(sentry_crash_context_t *ctx) # endif // SENTRY_PLATFORM_XBOX } +# if !defined(SENTRY_PLATFORM_XBOX) typedef HRESULT(WINAPI *WerStoreOpen_t)(REPORT_STORE_TYPES, HREPORTSTORE *); typedef void(WINAPI *WerStoreClose_t)(HREPORTSTORE); typedef HRESULT(WINAPI *WerStoreGetFirstReportKey_t)(HREPORTSTORE, PCWSTR *); @@ -2231,7 +2232,7 @@ static struct { WerFreeString_t WerFreeString; } g_wer = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -# define WER_FAILED ((HMODULE)(intptr_t)-1) +# define WER_FAILED ((HMODULE)(intptr_t)-1) static bool resolve_wer(void) @@ -2365,6 +2366,7 @@ write_wer_report(sentry_path_t *report_path, sentry_path_t *run_folder) sentry__path_free(run_path); return rv == 0; } +# endif // !SENTRY_PLATFORM_XBOX #endif // SENTRY_PLATFORM_WINDOWS /** @@ -3478,6 +3480,7 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) } } +# if !defined(SENTRY_PLATFORM_XBOX) if (ctx->platform.wer_enabled && ctx->attach_wer_report && run_folder) { SENTRY_DEBUG("Waiting for WER, allowing app process to exit"); sentry__atomic_store(&ctx->state, SENTRY_CRASH_STATE_PROCESSED); @@ -3503,6 +3506,7 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) elapsed_ms += SENTRY_CRASH_HANDLER_POLL_INTERVAL_MS; } } +# endif #endif // Write envelope based on mode From d701cad71c7ba47c40765eca93cb267b3ce767a2 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 3 Jun 2026 17:17:10 +0200 Subject: [PATCH 27/28] void* --- src/backends/native/sentry_crash_daemon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 5ba0d07b43..b5d8ee4c32 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -2334,7 +2334,7 @@ write_wer_report(sentry_path_t *report_path, sentry_path_t *run_folder) { int rv = -1; char *utf8 = NULL; - char *utf16 = NULL; + void *utf16 = NULL; size_t utf16_size = 0; sentry_path_t *run_path = sentry__path_join_str(run_folder, "Report.wer"); From e77d570583d4243c1459ba6954682d29eb61c6eb Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 4 Jun 2026 07:38:14 +0200 Subject: [PATCH 28/28] uuid: braces --- src/sentry_uuid.c | 7 ++++++- tests/unit/test_uuid.c | 10 ++++++++++ tests/unit/tests.inc | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/sentry_uuid.c b/src/sentry_uuid.c index f73bf5b023..2359f1ba04 100644 --- a/src/sentry_uuid.c +++ b/src/sentry_uuid.c @@ -37,7 +37,12 @@ sentry_uuid_from_string_n(const char *str, size_t str_len) bool is_nibble = true; char nibble = 0; - for (i = 0; i < len && pos < 16; i++) { + if (len > 1 && str[0] == '{' && str[len - 1] == '}') { + i = 1; + len--; + } + + for (; i < len && pos < 16; i++) { char c = str[i]; if (!c || c == '-') { continue; diff --git a/tests/unit/test_uuid.c b/tests/unit/test_uuid.c index 2ff38ebd38..1c04efd884 100644 --- a/tests/unit/test_uuid.c +++ b/tests/unit/test_uuid.c @@ -46,3 +46,13 @@ SENTRY_TEST(internal_uuid_api) sentry__span_uuid_as_string(&span_id, sbuf); TEST_CHECK_STRING_EQUAL(sbuf, "f391fdc0bb2743b1"); } + +SENTRY_TEST(uuid_braces) +{ + sentry_uuid_t uuid + = sentry_uuid_from_string("{f391fdc0-bb27-43b1-8c0c-183bc217d42b}"); + TEST_CHECK(!sentry_uuid_is_nil(&uuid)); + char buf[37]; + sentry_uuid_as_string(&uuid, buf); + TEST_CHECK_STRING_EQUAL(buf, "f391fdc0-bb27-43b1-8c0c-183bc217d42b"); +} diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index e7ad60ab96..f26889d055 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -353,6 +353,7 @@ XX(user_feedback_is_valid) XX(user_feedback_with_null_args) XX(user_report_is_valid) XX(uuid_api) +XX(uuid_braces) XX(uuid_v4) XX(value_attribute) XX(value_bool)