diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a708e499..0dba932cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Add cache keep modes, including `SENTRY_CACHE_KEEP_ALWAYS` to cache envelopes regardless of upload result. ([#1707](https://github.com/getsentry/sentry-native/pull/1707)) - Crashpad: add error log for oversized envelopes (HTTP 413 Content Too Large). ([#1706](https://github.com/getsentry/sentry-native/pull/1706), [crashpad#155](https://github.com/getsentry/crashpad/pull/155)) - Crashpad: support modifying attachments after `sentry_init` on macOS. ([#1705](https://github.com/getsentry/sentry-native/pull/1705), [crashpad#153](https://github.com/getsentry/crashpad/pull/153)) +- Add `cache_dir` envelope header for external crash reporters. ([#1698](https://github.com/getsentry/sentry-native/pull/1698)) **Fixes**: diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index 8d4d75026..7e702f17f 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -3036,6 +3036,11 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc) SENTRY_DEBUG("Envelope loaded, capturing"); + if (options->cache_keep && options->external_crash_reporter + && !sentry__envelope_materialize(envelope)) { + SENTRY_WARN("Failed to materialize envelope for external crash report"); + } + // Capture directly, or pass to external crash reporter if (!sentry__launch_external_crash_reporter(options, envelope)) { if (has_attachment_refs && options && options->run) { diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 1d5d9f35f..5e2b9c318 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -253,6 +253,10 @@ flush_external_crash_report( sentry__envelope_add_session(envelope, options->session); } + if (options->cache_keep) { + sentry__envelope_set_header(envelope, "cache_dir", + sentry_value_new_string(options->run->cache_path->path)); + } sentry__run_write_external(options->run, envelope); sentry_envelope_free(envelope); } diff --git a/src/sentry_core.c b/src/sentry_core.c index fdd1aa10d..815aa2b71 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -1709,6 +1709,14 @@ sentry__launch_external_crash_reporter( sentry_free(envelope_filename); return false; } + if (options->cache_keep) { + if (!sentry__envelope_is_raw(envelope)) { + sentry__envelope_set_header(envelope, "cache_dir", + sentry_value_new_string(options->run->cache_path->path)); + } else { + SENTRY_WARN("failed to add cache_dir to external crash report"); + } + } sentry__transport_send_envelope(disk_transport, envelope); sentry__transport_dump_queue(disk_transport, options->run); sentry_transport_free(disk_transport); diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index 69d3603d8..3214ff86a 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -83,6 +83,25 @@ envelope_item_cleanup(sentry_envelope_item_t *item) sentry_free(item->payload); } +static void +envelope_items_cleanup(sentry_envelope_t *envelope) +{ + sentry_value_decref(envelope->contents.items.headers); + + // Free all items in the linked list + sentry_envelope_item_t *item = envelope->contents.items.first_item; + while (item) { + sentry_envelope_item_t *next = item->next; + envelope_item_cleanup(item); + sentry_free(item); + item = next; + } + + envelope->contents.items.first_item = NULL; + envelope->contents.items.last_item = NULL; + envelope->contents.items.item_count = 0; +} + sentry_value_t sentry_envelope_get_header(const sentry_envelope_t *envelope, const char *key) { @@ -200,17 +219,7 @@ sentry_envelope_free(sentry_envelope_t *envelope) sentry_free(envelope); return; } - sentry_value_decref(envelope->contents.items.headers); - - // Free all items in the linked list - sentry_envelope_item_t *item = envelope->contents.items.first_item; - while (item) { - sentry_envelope_item_t *next = item->next; - envelope_item_cleanup(item); - sentry_free(item); - item = next; - } - + envelope_items_cleanup(envelope); sentry_free(envelope); } @@ -1510,8 +1519,16 @@ sentry__envelope_materialize(sentry_envelope_t *envelope) envelope->contents.items.last_item = NULL; envelope->contents.items.item_count = 0; bool ok = deserialize_into(envelope, payload, payload_len); + if (!ok) { + envelope_items_cleanup(envelope); + envelope->is_raw = true; + envelope->contents.raw.payload = payload; + envelope->contents.raw.payload_len = payload_len; + return false; + } + sentry_free(payload); - return ok; + return true; } static bool diff --git a/tests/test_integration_crashpad.py b/tests/test_integration_crashpad.py index 04e847124..6d6a56f57 100644 --- a/tests/test_integration_crashpad.py +++ b/tests/test_integration_crashpad.py @@ -760,6 +760,7 @@ def test_crashpad_external_crash_reporter(cmake, httpserver, run_args): tmp_path = cmake( ["sentry_example", "sentry_crash_reporter"], {"SENTRY_BACKEND": "crashpad"} ) + cache_dir = tmp_path.joinpath(".sentry-native/cache") env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) httpserver.expect_oneshot_request("/api/123456/envelope/").respond_with_data("OK") @@ -769,7 +770,7 @@ def test_crashpad_external_crash_reporter(cmake, httpserver, run_args): run( tmp_path, "sentry_example", - ["log", "crash-reporter"] + run_args, + ["log", "crash-reporter", "cache-keep"] + run_args, expect_failure=True, env=env, ) @@ -783,6 +784,7 @@ def test_crashpad_external_crash_reporter(cmake, httpserver, run_args): crash = crash_request.get_data() envelope = Envelope.deserialize(crash) + assert envelope.headers["cache_dir"] == str(cache_dir) assert_meta(envelope, integration="crashpad") assert_breadcrumb(envelope) diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 7fc1d13dc..0b3352665 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -314,6 +314,7 @@ def test_user_report_http(cmake, httpserver): ) def test_external_crash_reporter_http(cmake, httpserver, build_args): tmp_path = cmake(["sentry_example", "sentry_crash_reporter"], build_args) + cache_dir = tmp_path.joinpath(".sentry-native/cache") httpserver.expect_oneshot_request( "/api/123456/envelope/", @@ -329,7 +330,7 @@ def test_external_crash_reporter_http(cmake, httpserver, build_args): run( tmp_path, "sentry_example", - ["log", "crash-reporter", "crash"], + ["log", "crash-reporter", "cache-keep", "crash"], expect_failure=True, env=env, ) @@ -354,6 +355,7 @@ def test_external_crash_reporter_http(cmake, httpserver, build_args): crash = crash_request.get_data() envelope = Envelope.deserialize(crash) + assert envelope.headers["cache_dir"] == str(cache_dir) assert_meta(envelope, integration=build_args.get("SENTRY_BACKEND", "")) envelope = Envelope.deserialize(feedback) diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index fd4bb37a4..5dcdafbd4 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -418,6 +418,7 @@ def test_native_external_crash_reporter(cmake, httpserver): tmp_path = cmake( ["sentry_example", "sentry_crash_reporter"], {"SENTRY_BACKEND": "native"} ) + cache_dir = tmp_path.joinpath(".sentry-native/cache") env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) httpserver.expect_oneshot_request("/api/123456/envelope/").respond_with_data("OK") @@ -428,7 +429,7 @@ def test_native_external_crash_reporter(cmake, httpserver): run_crash( tmp_path, "sentry_example", - ["log", "crash-reporter", "crash"], + ["log", "crash-reporter", "cache-keep", "crash"], env=env, ) assert waiting.result @@ -443,6 +444,7 @@ def test_native_external_crash_reporter(cmake, httpserver): # Verify it's a minidump crash report and user feedback envelope = Envelope.deserialize(crash) + assert envelope.headers["cache_dir"] == str(cache_dir) assert_meta(envelope) assert_breadcrumb(envelope) diff --git a/tests/unit/test_envelopes.c b/tests/unit/test_envelopes.c index 9cd4017ee..843dfb9d9 100644 --- a/tests/unit/test_envelopes.c +++ b/tests/unit/test_envelopes.c @@ -753,6 +753,13 @@ SENTRY_TEST(envelope_materialize) sentry_envelope_t *bad = sentry__envelope_from_path(path); TEST_ASSERT(!!bad); TEST_CHECK(!sentry__envelope_materialize(bad)); + TEST_CHECK(sentry__envelope_is_raw(bad)); + size_t serialized_len = 0; + char *serialized = sentry_envelope_serialize(bad, &serialized_len); + TEST_ASSERT(!!serialized); + TEST_CHECK_INT_EQUAL(serialized_len, 7); + TEST_CHECK_STRING_EQUAL(serialized, "garbage"); + sentry_free(serialized); sentry_envelope_free(bad); sentry__path_remove(path);