diff --git a/CHANGELOG.md b/CHANGELOG.md index b85251c3a..841192a8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ **Features**: - Auto-populate `event.user.id` with a persistent per-installation UUID when no explicit user ID is set. ([#1661](https://github.com/getsentry/sentry-native/pull/1661)) +- Add `cache_dir` envelope header for external crash reporters. ([#1698](https://github.com/getsentry/sentry-native/pull/1698)) ## 0.14.0 diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index ec8b052ea..0c1a8f314 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 efa36c671..145802e9f 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 881448739..ccefa2816 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -1704,6 +1704,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 c91724ee6..e303f9ef0 100644 --- a/tests/test_integration_crashpad.py +++ b/tests/test_integration_crashpad.py @@ -768,6 +768,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") @@ -777,7 +778,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, ) @@ -791,6 +792,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 0267d294c..3d54dd829 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -402,6 +402,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") @@ -412,7 +413,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 @@ -427,6 +428,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);