diff --git a/src/backends/sentry_backend_breakpad.cpp b/src/backends/sentry_backend_breakpad.cpp index 9d397b042..f180259e3 100644 --- a/src/backends/sentry_backend_breakpad.cpp +++ b/src/backends/sentry_backend_breakpad.cpp @@ -23,7 +23,6 @@ extern "C" { #include "sentry_tracing.h" #include "sentry_transport.h" #include "sentry_unix_pageallocator.h" -#include "transports/sentry_disk_transport.h" } #ifdef __GNUC__ @@ -167,6 +166,8 @@ breakpad_backend_callback(const google_breakpad::MinidumpDescriptor &descriptor, should_handle = !sentry_value_is_null(result); } + sentry__transport_suspend(options->transport); + // Flush logs and metrics in a crash-safe manner before crash handling if (options->enable_logs) { sentry__logs_flush_crash_safe(); @@ -246,33 +247,21 @@ breakpad_backend_callback(const google_breakpad::MinidumpDescriptor &descriptor, if (envelope && sentry__session_replay_has_pending(options)) { sentry_value_t crash_event = sentry_envelope_get_event(envelope); - sentry_transport_t *replay_transport - = sentry_new_disk_transport(options->run); - if (replay_transport) { - sentry__session_replay_flush_pending( - options, replay_transport, crash_event); - sentry__transport_dump_queue( - replay_transport, options->run); - sentry_transport_free(replay_transport); - } + sentry__session_replay_flush_pending( + options, options->transport, crash_event); } if (!sentry__launch_external_crash_reporter(options, envelope)) { - // capture the envelopes with the disk transport - sentry_transport_t *disk_transport - = sentry_new_disk_transport(options->run); if (!sentry_value_is_null(transaction)) { sentry_envelope_t *tx_envelope = sentry__prepare_transaction( options, transaction, nullptr); if (tx_envelope) { sentry__capture_envelope( - disk_transport, tx_envelope, options); + options->transport, tx_envelope, options); } } - sentry__capture_envelope(disk_transport, envelope, options); - sentry__transport_dump_queue(disk_transport, options->run); - sentry_transport_free(disk_transport); + sentry__capture_envelope(options->transport, envelope, options); } else { sentry_value_decref(transaction); } diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 71fabc3d7..a4d6eca9c 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -26,7 +26,6 @@ extern "C" { #endif #include "sentry_utils.h" #include "sentry_uuid.h" -#include "transports/sentry_disk_transport.h" } #include @@ -477,6 +476,8 @@ crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) crash_event, nullptr, options->before_send_data); } + sentry__transport_suspend(options->transport); + // Flush logs and metrics in a crash-safe manner before crash handling if (options->enable_logs) { sentry__logs_flush_crash_safe(); @@ -498,13 +499,7 @@ crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) if (session) { sentry_envelope_t *envelope = sentry__envelope_new(); sentry__envelope_add_session(envelope, session); - - // capture the envelope with the disk transport - sentry_transport_t *disk_transport - = sentry_new_disk_transport(options->run); - sentry__capture_envelope(disk_transport, envelope, options); - sentry__transport_dump_queue(disk_transport, options->run); - sentry_transport_free(disk_transport); + sentry__capture_envelope(options->transport, envelope, options); } if (sentry__session_replay_has_pending(options)) { @@ -517,15 +512,8 @@ crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) sentry__ringbuffer_to_list(scope->breadcrumbs)); } - sentry_transport_t *replay_transport - = sentry_new_disk_transport(options->run); - if (replay_transport) { - sentry__session_replay_flush_pending( - options, replay_transport, crash_event); - sentry__transport_dump_queue( - replay_transport, options->run); - sentry_transport_free(replay_transport); - } + sentry__session_replay_flush_pending( + options, options->transport, crash_event); } sentry_value_decref(crash_event); } else { diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index 05cf02e1b..2b1db8996 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -19,7 +19,6 @@ #include "sentry_tracing.h" #include "sentry_transport.h" #include "sentry_unix_pageallocator.h" -#include "transports/sentry_disk_transport.h" #include #include #ifdef SENTRY_PLATFORM_UNIX @@ -1086,6 +1085,8 @@ process_ucontext_deferred(const sentry_ucontext_t *uctx, SENTRY_DEBUG("skipping `on_crash` hook due to recursive crash"); } + sentry__transport_suspend(options->transport); + // Flush logs in a crash-safe manner before crash handling if (options->enable_logs) { sentry__logs_flush_crash_safe(); @@ -1135,33 +1136,21 @@ process_ucontext_deferred(const sentry_ucontext_t *uctx, if (envelope && sentry__session_replay_has_pending(options)) { sentry_value_t crash_event = sentry_envelope_get_event(envelope); - sentry_transport_t *replay_transport - = sentry_new_disk_transport(options->run); - if (replay_transport) { - sentry__session_replay_flush_pending( - options, replay_transport, crash_event); - sentry__transport_dump_queue( - replay_transport, options->run); - sentry_transport_free(replay_transport); - } + sentry__session_replay_flush_pending( + options, options->transport, crash_event); } if (!sentry__launch_external_crash_reporter(options, envelope)) { - // capture the envelopes with the disk transport - sentry_transport_t *disk_transport - = sentry_new_disk_transport(options->run); if (!sentry_value_is_null(transaction)) { sentry_envelope_t *tx_envelope = sentry__prepare_transaction( options, transaction, NULL); if (tx_envelope) { sentry__capture_envelope( - disk_transport, tx_envelope, options); + options->transport, tx_envelope, options); } } - sentry__capture_envelope(disk_transport, envelope, options); - sentry__transport_dump_queue(disk_transport, options->run); - sentry_transport_free(disk_transport); + sentry__capture_envelope(options->transport, envelope, options); } else { sentry_value_decref(transaction); } diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 3a5627975..bf8c898fe 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -40,7 +40,6 @@ #include "sentry_tracing.h" #include "sentry_transport.h" #include "sentry_value.h" -#include "transports/sentry_disk_transport.h" // Global process-wide synchronization for IPC and shared memory access // This lives for the entire backend lifetime and is shared across all threads @@ -989,6 +988,8 @@ native_backend_except(sentry_backend_t *backend, const sentry_ucontext_t *uctx) SENTRY_DEBUG("handling native backend exception"); + sentry__transport_suspend(options->transport); + // Flush logs and metrics in a crash-safe manner before crash handling if (options->enable_logs) { sentry__logs_flush_crash_safe(); @@ -1087,32 +1088,22 @@ native_backend_except(sentry_backend_t *backend, const sentry_ucontext_t *uctx) SENTRY_SESSION_STATUS_CRASHED); if (session || !sentry_value_is_null(transaction)) { - sentry_transport_t *disk_transport - = sentry_new_disk_transport(options->run); - if (disk_transport) { - if (!sentry_value_is_null(transaction)) { - sentry_envelope_t *tx_envelope - = sentry__prepare_transaction( - options, transaction, NULL); - if (tx_envelope) { - sentry__capture_envelope( - disk_transport, tx_envelope, options); - } + if (!sentry_value_is_null(transaction)) { + sentry_envelope_t *tx_envelope + = sentry__prepare_transaction( + options, transaction, NULL); + if (tx_envelope) { + sentry__capture_envelope( + options->transport, tx_envelope, options); } - if (session) { - sentry_envelope_t *envelope - = sentry__envelope_new(); - if (envelope) { - sentry__envelope_add_session(envelope, session); - sentry__capture_envelope( - disk_transport, envelope, options); - } + } + if (session) { + sentry_envelope_t *envelope = sentry__envelope_new(); + if (envelope) { + sentry__envelope_add_session(envelope, session); + sentry__capture_envelope( + options->transport, envelope, options); } - sentry__transport_dump_queue( - disk_transport, options->run); - sentry_transport_free(disk_transport); - } else { - sentry_value_decref(transaction); } } diff --git a/src/sentry_core.c b/src/sentry_core.c index 63704ac22..ba074d0c2 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -161,6 +161,10 @@ sentry_init(sentry_options_t *options) } transport = options->transport; + if (!transport) { + transport = sentry__transport_new_null(); + options->transport = transport; + } sentry_path_t *database_path = options->database_path; options->database_path = sentry__path_absolute(database_path); if (options->database_path) { @@ -199,7 +203,12 @@ sentry_init(sentry_options_t *options) // Also, we want to continue - crash capture doesn't need transport. sentry__transport_shutdown(transport, 0); sentry_options_set_transport(options, NULL); - transport = NULL; + transport = options->transport; + if (!transport + || sentry__transport_startup(transport, options) != 0) { + SENTRY_WARN("failed to initialize fallback transport"); + goto fail; + } #else SENTRY_WARN("failed to initialize transport"); goto fail; diff --git a/src/sentry_options.c b/src/sentry_options.c index e7c780750..26cd3e9d1 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -197,7 +197,7 @@ sentry_options_set_transport( sentry_options_t *opts, sentry_transport_t *transport) { sentry_transport_free(opts->transport); - opts->transport = transport; + opts->transport = transport ? transport : sentry__transport_new_null(); } #ifdef SENTRY_PLATFORM_NX diff --git a/src/sentry_transport.c b/src/sentry_transport.c index 03c6a5fbf..09d292755 100644 --- a/src/sentry_transport.c +++ b/src/sentry_transport.c @@ -2,6 +2,7 @@ #include "sentry_alloc.h" #include "sentry_envelope.h" #include "sentry_options.h" +#include "sentry_sync.h" struct sentry_transport_s { void (*send_envelope_func)(sentry_envelope_t *envelope, void *state); @@ -13,9 +14,18 @@ struct sentry_transport_s { void (*retry_func)(void *state); void (*cleanup_func)(const sentry_options_t *options, void *state); void *state; + sentry_run_t *run; + long suspended; bool running; }; +static void +send_null_envelope(sentry_envelope_t *envelope, void *state) +{ + (void)state; + sentry_envelope_free(envelope); +} + sentry_transport_t * sentry_transport_new( void (*send_func)(sentry_envelope_t *envelope, void *state)) @@ -28,6 +38,12 @@ sentry_transport_new( return transport; } + +sentry_transport_t * +sentry__transport_new_null(void) +{ + return sentry_transport_new(send_null_envelope); +} void sentry_transport_set_state(sentry_transport_t *transport, void *state) { @@ -73,14 +89,24 @@ sentry__transport_send_envelope( sentry_envelope_free(envelope); return; } + if (sentry__atomic_fetch(&transport->suspended)) { + sentry__run_write_envelope(transport->run, envelope); + sentry_envelope_free(envelope); + return; + } SENTRY_DEBUG("sending envelope"); transport->send_envelope_func(envelope, transport->state); + if (sentry__atomic_fetch(&transport->suspended)) { + sentry__transport_dump_queue(transport, transport->run); + } } int sentry__transport_startup( sentry_transport_t *transport, const sentry_options_t *options) { + sentry__run_free(transport->run); + transport->run = sentry__run_incref(options->run); if (transport->startup_func) { SENTRY_DEBUG("starting transport"); int rv = transport->startup_func(options, transport->state); @@ -131,6 +157,15 @@ sentry__transport_dump_queue(sentry_transport_t *transport, sentry_run_t *run) return dumped; } +void +sentry__transport_suspend(sentry_transport_t *transport) +{ + if (!transport) { + return; + } + sentry__atomic_store(&transport->suspended, 1); +} + void sentry_transport_free(sentry_transport_t *transport) { @@ -140,6 +175,7 @@ sentry_transport_free(sentry_transport_t *transport) if (transport->free_func) { transport->free_func(transport->state); } + sentry__run_free(transport->run); sentry_free(transport); } diff --git a/src/sentry_transport.h b/src/sentry_transport.h index a4ce8197e..4c4dd0ef0 100644 --- a/src/sentry_transport.h +++ b/src/sentry_transport.h @@ -55,6 +55,12 @@ sentry_transport_t *sentry__transport_new_default(void); size_t sentry__transport_dump_queue( sentry_transport_t *transport, sentry_run_t *run); +/** Creates an internal transport that discards envelopes. */ +sentry_transport_t *sentry__transport_new_null(void); + +/** Sets the run used for automatic queue dumps. */ +void sentry__transport_suspend(sentry_transport_t *transport); + void *sentry__transport_get_state(sentry_transport_t *transport); void sentry__transport_set_retry_func( diff --git a/tests/unit/test_concurrency.c b/tests/unit/test_concurrency.c index 1129ff7f5..cdc3c739a 100644 --- a/tests/unit/test_concurrency.c +++ b/tests/unit/test_concurrency.c @@ -1,5 +1,10 @@ #include "sentry_core.h" +#include "sentry_database.h" +#include "sentry_envelope.h" +#include "sentry_options.h" +#include "sentry_path.h" #include "sentry_testsupport.h" +#include "sentry_transport.h" #include @@ -76,6 +81,92 @@ SENTRY_TEST(multiple_inits) TEST_CHECK_INT_EQUAL(called, 4); } +typedef struct { + sentry_envelope_t *queued; + long send_started; + long release_send; + long send_count; + long dump_count; +} suspend_transport_state_t; + +static void +suspend_send(sentry_envelope_t *envelope, void *data) +{ + suspend_transport_state_t *state = data; + sentry__atomic_store(&state->send_started, 1); + while (!sentry__atomic_fetch(&state->release_send)) { + sentry__thread_yield(); + } + state->queued = envelope; + sentry__atomic_fetch_and_add(&state->send_count, 1); +} + +static size_t +suspend_dump(sentry_run_t *run, void *data) +{ + (void)run; + suspend_transport_state_t *state = data; + sentry__atomic_fetch_and_add(&state->dump_count, 1); + if (!state->queued) { + return 0; + } + sentry_envelope_free(state->queued); + state->queued = NULL; + return 1; +} + +SENTRY_THREAD_FN +suspend_send_thread(void *data) +{ + sentry__transport_send_envelope(data, sentry__envelope_new()); + return 0; +} + +SENTRY_TEST(transport_suspend) +{ + sentry_path_t *database_path = sentry__path_from_str( + SENTRY_TEST_PATH_PREFIX ".transport-crash-mode"); + TEST_ASSERT(!!database_path); + sentry__path_remove_all(database_path); + TEST_ASSERT(!sentry__path_create_dir_all(database_path)); + sentry_run_t *run = sentry__run_new(database_path); + TEST_ASSERT(!!run); + + suspend_transport_state_t state = { 0 }; + sentry_transport_t *transport = sentry_transport_new(suspend_send); + TEST_ASSERT(!!transport); + sentry_transport_set_state(transport, &state); + sentry__transport_set_dump_func(transport, suspend_dump); + sentry_options_t startup_options = { 0 }; + startup_options.run = run; + TEST_CHECK(!sentry__transport_startup(transport, &startup_options)); + + sentry_threadid_t thread; + sentry__thread_init(&thread); + TEST_ASSERT(!sentry__thread_spawn(&thread, suspend_send_thread, transport)); + while (!sentry__atomic_fetch(&state.send_started)) { + sentry__thread_yield(); + } + + sentry__transport_suspend(transport); + sentry__atomic_store(&state.release_send, 1); + sentry__thread_join(thread); + + TEST_CHECK_INT_EQUAL(sentry__atomic_fetch(&state.send_count), 1); + TEST_CHECK_INT_EQUAL(sentry__atomic_fetch(&state.dump_count), 1); + TEST_CHECK(!state.queued); + + sentry__transport_send_envelope(transport, sentry__envelope_new()); + TEST_CHECK_INT_EQUAL(sentry__atomic_fetch(&state.send_count), 1); + TEST_CHECK(sentry__atomic_fetch(&run->retain)); + + sentry_transport_free(transport); + sentry__run_clean(run, true); + sentry__run_free(run); + sentry__path_remove_all(database_path); + sentry__path_free(database_path); +} + SENTRY_THREAD_FN thread_worker(void *called) { diff --git a/tests/unit/test_failures.c b/tests/unit/test_failures.c index 6e8cb1081..169d0fb24 100644 --- a/tests/unit/test_failures.c +++ b/tests/unit/test_failures.c @@ -30,7 +30,7 @@ SENTRY_TEST(init_failure) // On NX a failing transport must not fail initialization. TEST_CHECK(rv == 0); SENTRY_WITH_OPTIONS (runtime_options) { - TEST_CHECK(runtime_options->transport == NULL); + TEST_CHECK(runtime_options->transport != NULL); } #else TEST_CHECK(rv != 0); diff --git a/tests/unit/test_uninit.c b/tests/unit/test_uninit.c index 03ff3209d..478096f2c 100644 --- a/tests/unit/test_uninit.c +++ b/tests/unit/test_uninit.c @@ -1,4 +1,9 @@ +#include "sentry_core.h" +#include "sentry_envelope.h" +#include "sentry_options.h" +#include "sentry_sync.h" #include "sentry_testsupport.h" +#include "sentry_transport.h" SENTRY_TEST(uninitialized) { @@ -45,6 +50,14 @@ SENTRY_TEST(empty_transport) sentry_uuid_t id = sentry_capture_event(event); TEST_CHECK(!sentry_uuid_is_nil(&id)); + SENTRY_WITH_OPTIONS (runtime_options) { + TEST_ASSERT(!!runtime_options->transport); + sentry__transport_suspend(runtime_options->transport); + sentry__transport_send_envelope( + runtime_options->transport, sentry__envelope_new()); + TEST_CHECK(sentry__atomic_fetch(&runtime_options->run->retain)); + } + sentry_close(); } diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index c16c09b7a..1237f487a 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -393,6 +393,7 @@ XX(transactions_skip_before_send) XX(transport_retry) XX(transport_sampling_transactions) XX(transport_sampling_transactions_set_trace) +XX(transport_suspend) XX(tus_file_attachment_preserves_original) XX(tus_placeholder_uses_raw_location) XX(tus_request_preparation)