From 96778b1ae5ce991c44b7c73f7ff32c2ec7a6216e Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 10 Sep 2026 13:27:13 +0200 Subject: [PATCH 1/2] ref: prevent direct access to scope data Move scope data out of the header to prevent direct access, and provide internal API tailored for protecting the data with RW-locks in #2024. --- src/backends/sentry_backend_crashpad.cpp | 2 +- src/backends/sentry_backend_native.c | 5 +- src/integrations/sentry_integration_wer.c | 38 +- src/sentry_core.c | 173 ++-- src/sentry_envelope.c | 11 +- src/sentry_scope.c | 925 ++++++++++++++++------ src/sentry_scope.h | 104 ++- src/sentry_session.c | 17 +- src/sentry_tracing.c | 38 +- src/sentry_tracing.h | 4 +- tests/unit/test_attachments.c | 16 +- tests/unit/test_basic.c | 4 +- tests/unit/test_logs.c | 9 +- tests/unit/test_scope.c | 385 ++++++--- tests/unit/test_tracing.c | 83 +- 15 files changed, 1206 insertions(+), 608 deletions(-) diff --git a/src/backends/sentry_backend_crashpad.cpp b/src/backends/sentry_backend_crashpad.cpp index 83d99961bd..eecf80e1a2 100644 --- a/src/backends/sentry_backend_crashpad.cpp +++ b/src/backends/sentry_backend_crashpad.cpp @@ -517,7 +517,7 @@ crashpad_handler(int signum, siginfo_t *info, ucontext_t *user_context) // written above and stays breadcrumb-free SENTRY_WITH_SCOPE (scope) { sentry_value_set_by_key(crash_event, "breadcrumbs", - sentry__ringbuffer_to_list(scope->breadcrumbs)); + sentry__scope_breadcrumbs_to_list(scope)); } sentry__session_replay_flush_pending( diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 19b6fb6eb9..6906af85ca 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -1111,12 +1111,14 @@ native_backend_write_attachments(const sentry_path_t *event_path) return; } SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = scope->attachments; + sentry_value_t attachments = sentry__scope_load_attachments(scope); if (sentry_value_get_length(attachments) == 0) { + sentry_value_decref(attachments); continue; } sentry_path_t *run_path = sentry__path_dir(event_path); if (!run_path) { + sentry_value_decref(attachments); continue; } sentry_path_t *attach_list_path @@ -1172,6 +1174,7 @@ native_backend_write_attachments(const sentry_path_t *event_path) sentry__path_free(attach_list_path); } sentry__path_free(run_path); + sentry_value_decref(attachments); } } diff --git a/src/integrations/sentry_integration_wer.c b/src/integrations/sentry_integration_wer.c index 23f9af3166..33d3423064 100644 --- a/src/integrations/sentry_integration_wer.c +++ b/src/integrations/sentry_integration_wer.c @@ -191,6 +191,18 @@ wer_remove_attachment(void *UNUSED(data), sentry_value_t attachment) } } +static void +wer_for_each_attachment( + sentry_scope_t *scope, void *data, void (*callback)(void *, sentry_value_t)) +{ + sentry_value_t attachments = sentry__scope_load_attachments(scope); + size_t len = sentry_value_get_length(attachments); + for (size_t i = 0; i < len; i++) { + callback(data, sentry_value_get_by_index(attachments, i)); + } + sentry_value_decref(attachments); +} + static int wer_cleanup_tag(const char *key, sentry_value_t UNUSED(value), void *data) { @@ -208,13 +220,11 @@ wer_clear(void *data) return; } - sentry_value_foreach_key_value(scope->tags, wer_cleanup_tag, wer_data); + sentry_value_t tags = sentry__scope_load_tags(scope); + sentry_value_foreach_key_value(tags, wer_cleanup_tag, wer_data); + sentry_value_decref(tags); - size_t len = sentry_value_get_length(scope->attachments); - for (size_t i = 0; i < len; i++) { - wer_remove_attachment( - wer_data, sentry_value_get_by_index(scope->attachments, i)); - } + wer_for_each_attachment(scope, wer_data, wer_remove_attachment); } static void @@ -239,11 +249,7 @@ register_wer( if (sentry__scope_add_observer(scope, observer)) { wer_data->scope = scope; wer_data->observer = observer; - size_t len = sentry_value_get_length(scope->attachments); - for (size_t i = 0; i < len; i++) { - wer_add_attachment( - wer_data, sentry_value_get_by_index(scope->attachments, i)); - } + wer_for_each_attachment(scope, wer_data, wer_add_attachment); } } @@ -257,13 +263,11 @@ unregister_wer( return; } - sentry_value_foreach_key_value(scope->tags, wer_cleanup_tag, wer_data); + sentry_value_t tags = sentry__scope_load_tags(scope); + sentry_value_foreach_key_value(tags, wer_cleanup_tag, wer_data); + sentry_value_decref(tags); - size_t len = sentry_value_get_length(scope->attachments); - for (size_t i = 0; i < len; i++) { - wer_remove_attachment( - wer_data, sentry_value_get_by_index(scope->attachments, i)); - } + wer_for_each_attachment(scope, wer_data, wer_remove_attachment); sentry__scope_remove_observer(scope, wer_data->observer); wer_data->scope = NULL; diff --git a/src/sentry_core.c b/src/sentry_core.c index ab9f3f2564..e93b269eb1 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -80,23 +80,6 @@ sentry__should_skip_upload(void) return skip; } -static void -generate_propagation_context(sentry_value_t propagation_context) -{ - sentry_value_set_by_key( - propagation_context, "trace", sentry_value_new_object()); - sentry_uuid_t trace_id = sentry_uuid_new_v4(); - sentry_uuid_t span_id = sentry_uuid_new_v4(); - sentry_value_set_by_key( - sentry_value_get_by_key(propagation_context, "trace"), "trace_id", - sentry__value_new_internal_uuid(&trace_id)); - sentry_value_set_by_key( - sentry_value_get_by_key(propagation_context, "trace"), "span_id", - sentry__value_new_span_uuid(&span_id)); - sentry__generate_sample_rand( - sentry_value_get_by_key(propagation_context, "trace")); -} - static void register_integrations(sentry_scope_t *scope, const sentry_options_t *options) { @@ -272,38 +255,7 @@ sentry_init(sentry_options_t *options) // `client_sdk` in the `scope` because some downstream SDKs want to override // it at runtime via the options interface. SENTRY_WITH_SCOPE_MUT (scope) { - if (options->sdk_name) { - sentry_value_t sdk_name - = sentry_value_new_string(options->sdk_name); - sentry_value_set_by_key(scope->client_sdk, "name", sdk_name); - } - sentry_value_t integrations - = sentry_value_get_by_key(scope->client_sdk, "integrations"); - for (size_t i = 0; i < options->num_integrations; i++) { - const char *name = options->integrations[i]->name; - if (!name) { - continue; - } - if (sentry_value_is_null(integrations)) { - integrations = sentry_value_new_list(); - sentry_value_set_by_key( - scope->client_sdk, "integrations", integrations); - } - sentry_value_append(integrations, sentry_value_new_string(name)); - } - sentry_value_freeze(scope->client_sdk); - generate_propagation_context(scope->propagation_context); - scope->release = sentry__string_clone(options->release); - scope->environment = sentry__string_clone(options->environment); - sentry_value_decref(scope->attachments); - scope->attachments = options->attachments; - options->attachments = sentry_value_new_null(); - - sentry__ringbuffer_set_max_size( - scope->breadcrumbs, options->max_breadcrumbs); - - sentry__scope_update_dsc(scope, options); - + sentry__scope_apply_options(scope, options); register_integrations(scope, options); } if (backend && backend->user_consent_changed_func) { @@ -559,7 +511,7 @@ sentry__capture_envelope(sentry_transport_t *transport, sentry_uuid_t event_id = sentry__envelope_get_event_id(envelope); if (!sentry_uuid_is_nil(&event_id)) { SENTRY_WITH_SCOPE_MUT_NO_FLUSH (scope) { - scope->last_event_id = event_id; + sentry__scope_set_last_event_id(scope, event_id); } } @@ -794,14 +746,20 @@ sentry__prepare_event(const sentry_options_t *options, sentry_value_t event, } SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = scope->attachments; - if (local_scope - && sentry_value_get_length(local_scope->attachments) > 0) { - // all attachments merged from multiple scopes - sentry__attachments_extend( - &all_attachments, local_scope->attachments); - sentry__attachments_extend(&all_attachments, scope->attachments); - attachments = all_attachments; + sentry_value_t global_attachments + = sentry__scope_load_attachments(scope); + sentry_value_t attachments = global_attachments; + if (local_scope) { + sentry_value_t local_attachments + = sentry__scope_load_attachments(local_scope); + if (sentry_value_get_length(local_attachments) > 0) { + // all attachments merged from multiple scopes + sentry__attachments_extend(&all_attachments, local_attachments); + sentry__attachments_extend( + &all_attachments, global_attachments); + attachments = all_attachments; + } + sentry_value_decref(local_attachments); } // otherwise only global scope has attachments sentry__envelope_add_attachments(envelope, attachments, options); @@ -809,6 +767,7 @@ sentry__prepare_event(const sentry_options_t *options, sentry_value_t event, sentry__cache_attachment_refs(envelope, attachments, options, options->run->cache_path, options->run->run_path); } + sentry_value_decref(global_attachments); } sentry_value_decref(all_attachments); @@ -933,13 +892,18 @@ prepare_user_feedback(const sentry_options_t *options, sentry__attachments_extend(&all_attachments, hint->attachments); } if (local_scope) { - sentry__attachments_extend(&all_attachments, local_scope->attachments); + sentry_value_t local_attachments + = sentry__scope_load_attachments(local_scope); + sentry__attachments_extend(&all_attachments, local_attachments); + sentry_value_decref(local_attachments); } SENTRY_WITH_SCOPE (scope) { - sentry_value_t attachments = scope->attachments; + sentry_value_t global_attachments + = sentry__scope_load_attachments(scope); + sentry_value_t attachments = global_attachments; if (sentry_value_get_length(all_attachments) > 0) { - sentry__attachments_extend(&all_attachments, scope->attachments); + sentry__attachments_extend(&all_attachments, global_attachments); attachments = all_attachments; } sentry__envelope_add_attachments(envelope, attachments, options); @@ -947,6 +911,7 @@ prepare_user_feedback(const sentry_options_t *options, sentry__cache_attachment_refs(envelope, attachments, options, options->run->cache_path, options->run->run_path); } + sentry_value_decref(global_attachments); } sentry_value_decref(all_attachments); @@ -1287,7 +1252,7 @@ void sentry__set_propagation_context(const char *key, sentry_value_t value) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_value_set_by_key(scope->propagation_context, key, value); + sentry__scope_set_propagation_context(scope, key, value); } } @@ -1391,7 +1356,7 @@ sentry_set_trace_n(const char *trace_id, size_t trace_id_len, sentry_uuid_t span_id = sentry_uuid_new_v4(); sentry_value_set_by_key( context, "span_id", sentry__value_new_span_uuid(&span_id)); - scope->trace_managed = false; + sentry__scope_set_trace_managed(scope, false); } if (!sentry_value_is_null(context)) { @@ -1412,8 +1377,8 @@ sentry_regenerate_trace(void) { SENTRY_WITH_OPTIONS (options) { SENTRY_WITH_SCOPE_MUT (scope) { - generate_propagation_context(scope->propagation_context); - scope->trace_managed = false; + sentry__scope_regenerate_propagation_context(scope); + sentry__scope_set_trace_managed(scope, false); sentry__scope_update_dsc(scope, options); } } @@ -1492,12 +1457,12 @@ sentry_transaction_start_ts(sentry_transaction_context_t *opaque_tx_ctx, // Regenerate the scope's propagation context so events // captured outside this transaction also carry the new // trace_id, and align the tx's trace_id with it. - generate_propagation_context(scope->propagation_context); - sentry_value_t scope_trace_id = sentry_value_get_by_key( - sentry_value_get_by_key( - scope->propagation_context, "trace"), - "trace_id"); - sentry_value_incref(scope_trace_id); + sentry__scope_regenerate_propagation_context(scope); + sentry_value_t trace_context + = sentry__scope_load_trace_context(scope); + sentry_value_t scope_trace_id = sentry_value_incref( + sentry_value_get_by_key(trace_context, "trace_id")); + sentry_value_decref(trace_context); sentry_value_set_by_key(tx, "trace_id", scope_trace_id); sentry_value_remove_by_key(tx, "parent_span_id"); sentry_value_remove_by_key(tx, "sampled"); @@ -1510,9 +1475,10 @@ sentry_transaction_start_ts(sentry_transaction_context_t *opaque_tx_ctx, double sample_rand = 1.0; SENTRY_WITH_SCOPE (scope) { - sample_rand = sentry_value_as_double(sentry_value_get_by_key( - sentry_value_get_by_key(scope->propagation_context, "trace"), - "sample_rand")); + sentry_value_t trace_context = sentry__scope_load_trace_context(scope); + sample_rand = sentry_value_as_double( + sentry_value_get_by_key(trace_context, "sample_rand")); + sentry_value_decref(trace_context); } sentry_sampling_context_t sampling_ctx = { opaque_tx_ctx, custom_sampling_ctx, NULL, sample_rand }; @@ -1558,10 +1524,7 @@ sentry_transaction_discard(sentry_transaction_t *opaque_tx) } SENTRY_WITH_SCOPE_MUT (scope) { - if (scope->transaction_object == opaque_tx) { - sentry__transaction_decref(scope->transaction_object); - scope->transaction_object = NULL; - } + sentry__scope_remove_transaction_object(scope, opaque_tx); } sentry__transaction_decref(opaque_tx); @@ -1579,28 +1542,15 @@ sentry__transaction_finish_value( sentry_value_t tx = sentry__value_clone(opaque_tx->inner); SENTRY_WITH_SCOPE_MUT (scope) { - if (scope->transaction_object) { - sentry_value_t scope_tx = scope->transaction_object->inner; - - const char *tx_id = sentry_value_as_string( - sentry_value_get_by_key(tx, "span_id")); - const char *scope_tx_id = sentry_value_as_string( - sentry_value_get_by_key(scope_tx, "span_id")); - if (sentry__string_eq(tx_id, scope_tx_id)) { - sentry__transaction_decref(scope->transaction_object); - scope->transaction_object = NULL; - } - } + sentry__scope_remove_transaction_value(scope, tx); // if the SDK manages the trace (rather than the user or a downstream // SDK) we break propagation context traces at transaction boundaries. - if (scope->trace_managed) { + if (sentry__scope_is_trace_managed(scope)) { sentry_value_t txn_trace_id = sentry_value_get_by_key(tx, "trace_id"); sentry_value_incref(txn_trace_id); - sentry_value_set_by_key( - sentry_value_get_by_key(scope->propagation_context, "trace"), - "trace_id", txn_trace_id); + sentry__scope_set_trace_context(scope, "trace_id", txn_trace_id); } } // The sampling decision should already be made for transactions @@ -1660,7 +1610,7 @@ void sentry_set_transaction_object(sentry_transaction_t *tx) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_scope_set_transaction_object(scope, tx); + sentry__scope_set_transaction_object(scope, tx); } } @@ -1668,7 +1618,7 @@ void sentry_set_span(sentry_span_t *span) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_scope_set_span(scope, span); + sentry__scope_set_span(scope, span); } } @@ -1820,18 +1770,7 @@ sentry_span_finish_ts(sentry_span_t *opaque_span, uint64_t timestamp) sentry_value_t span = sentry__value_clone(opaque_span->inner); SENTRY_WITH_SCOPE_MUT (scope) { - if (scope->span) { - sentry_value_t scope_span = scope->span->inner; - - const char *span_id = sentry_value_as_string( - sentry_value_get_by_key(span, "span_id")); - const char *scope_span_id = sentry_value_as_string( - sentry_value_get_by_key(scope_span, "span_id")); - if (sentry__string_eq(span_id, scope_span_id)) { - sentry__span_decref(scope->span); - scope->span = NULL; - } - } + sentry__scope_remove_span_value(scope, span); } // Note that the current API makes it impossible to set a sampled value @@ -1890,10 +1829,7 @@ sentry_span_discard(sentry_span_t *opaque_span) sentry__transaction_remove_child(opaque_span->transaction, opaque_span); SENTRY_WITH_SCOPE_MUT (scope) { - if (scope->span == opaque_span) { - sentry__span_decref(scope->span); - scope->span = NULL; - } + sentry__scope_remove_span(scope, opaque_span); } sentry__span_decref(opaque_span); @@ -2165,7 +2101,8 @@ sentry_add_attachment(sentry_value_t attachment) sentry_value_t added = sentry_value_new_null(); SENTRY_WITH_SCOPE_MUT (scope) { - added = sentry__attachments_find(scope->attachments, attachment); + sentry_value_t attachments = sentry__scope_load_attachments(scope); + added = sentry__attachments_find(attachments, attachment); if (sentry_value_is_null(added)) { if (options->backend && options->backend->add_attachment_func) { options->backend->add_attachment_func( @@ -2175,6 +2112,7 @@ sentry_add_attachment(sentry_value_t attachment) } else { sentry_value_decref(attachment); } + sentry_value_decref(attachments); } sentry_options_free((sentry_options_t *)options); sentry_uuid_t uuid = sentry__attachment_get_id(added); @@ -2214,8 +2152,7 @@ sentry_clear_attachments(void) { SENTRY_WITH_OPTIONS (options) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_value_t attachments = scope->attachments; - scope->attachments = sentry_value_new_list(); + sentry_value_t attachments = sentry__scope_take_attachments(scope); size_t len = sentry_value_get_length(attachments); for (size_t i = 0; i < len; i++) { sentry_value_t attachment @@ -2241,8 +2178,9 @@ sentry_remove_attachment(sentry_uuid_t attachment_id) SENTRY_WITH_OPTIONS (options) { SENTRY_WITH_SCOPE_MUT (scope) { - sentry_value_t removed = sentry__attachments_remove( - scope->attachments, &attachment_id); + sentry_value_t attachments = sentry__scope_load_attachments(scope); + sentry_value_t removed + = sentry__attachments_remove(attachments, &attachment_id); if (!sentry_value_is_null(removed)) { if (options->backend && options->backend->remove_attachment_func) { @@ -2252,6 +2190,7 @@ sentry_remove_attachment(sentry_uuid_t attachment_id) SENTRY_SCOPE_NOTIFY(scope, remove_attachment, removed); } sentry_value_decref(removed); + sentry_value_decref(attachments); } } } diff --git a/src/sentry_envelope.c b/src/sentry_envelope.c index 289f2cd445..e7ee37f0cd 100644 --- a/src/sentry_envelope.c +++ b/src/sentry_envelope.c @@ -415,10 +415,11 @@ sentry__envelope_add_event(sentry_envelope_t *envelope, sentry_value_t event) sentry_value_t dsc = sentry_value_new_null(); double sample_rand = (double)NAN; SENTRY_WITH_SCOPE (scope) { - dsc = sentry__value_clone(scope->dynamic_sampling_context); - sample_rand = sentry_value_as_double(sentry_value_get_by_key( - sentry_value_get_by_key(scope->propagation_context, "trace"), - "sample_rand")); + dsc = sentry__scope_load_dsc(scope); + sentry_value_t trace_context = sentry__scope_load_trace_context(scope); + sample_rand = sentry_value_as_double( + sentry_value_get_by_key(trace_context, "sample_rand")); + sentry_value_decref(trace_context); } if (!sentry_value_is_null(dsc)) { sentry_value_t trace_id = sentry_value_get_by_key( @@ -496,7 +497,7 @@ sentry__envelope_add_transaction( sentry_value_t dsc = sentry_value_new_null(); SENTRY_WITH_SCOPE (scope) { - dsc = sentry__value_clone(scope->dynamic_sampling_context); + dsc = sentry__scope_load_dsc(scope); } if (!sentry_value_is_null(dsc)) { diff --git a/src/sentry_scope.c b/src/sentry_scope.c index dd52a7e78e..bbcba14e1d 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -13,6 +13,7 @@ #include "sentry_sync.h" #include "sentry_tracing.h" #include "sentry_transport.h" +#include "sentry_uuid.h" #include "sentry_value.h" #include @@ -27,8 +28,40 @@ # define SENTRY_BACKEND "native" #endif +struct sentry_scope_data_s { + char *release; + char *environment; + char *transaction; + sentry_value_t fingerprint; + sentry_value_t user; + sentry_value_t tags; + sentry_value_t extra; + sentry_value_t attributes; + sentry_value_t contexts; + sentry_value_t propagation_context; + sentry_ringbuffer_t *breadcrumbs; + sentry_value_t dynamic_sampling_context; + sentry_level_t level; + sentry_uuid_t last_event_id; + sentry_value_t client_sdk; + sentry_value_t attachments; + + // The span attached to this scope, if any. + // + // Conceptually, every transaction is a span, so it should be possible to + // attach spans or transactions to a scope. But sentry_span_t and + // sentry_transaction_t are unrelated types in the native SDK, so we need + // two distinct pointers. At most one of them should ever be non-null. + // Whenever possible, `transaction` should pull its value from the + // `name` property nested in transaction_object or span. + sentry_transaction_t *transaction_object; + sentry_span_t *span; + bool trace_managed; +}; + static bool g_scope_initialized = false; static sentry_scope_t g_scope = { 0 }; +static sentry_scope_data_t g_scope_data = { 0 }; #ifdef SENTRY__MUTEX_INIT_DYN SENTRY__MUTEX_INIT_DYN(g_lock) #else @@ -70,32 +103,193 @@ get_client_sdk(void) } static void -init_scope(sentry_scope_t *scope) -{ - scope->release = NULL; - scope->environment = NULL; - scope->transaction = NULL; - scope->fingerprint = sentry_value_new_null(); - scope->user = sentry_value_new_null(); - scope->tags = sentry_value_new_object(); - scope->extra = sentry_value_new_object(); - scope->attributes = sentry_value_new_object(); - scope->contexts = sentry_value_new_object(); - scope->propagation_context = sentry_value_new_object(); - scope->breadcrumbs = sentry__ringbuffer_new(SENTRY_BREADCRUMBS_MAX); - scope->dynamic_sampling_context = sentry_value_new_object(); - scope->level = SENTRY_LEVEL_ERROR; - scope->last_event_id = sentry_uuid_nil(); - scope->client_sdk = sentry_value_new_null(); - scope->attachments = sentry_value_new_list(); - scope->transaction_object = NULL; - scope->span = NULL; - scope->trace_managed = true; +init_scope_data(sentry_scope_data_t *data) +{ + data->release = NULL; + data->environment = NULL; + data->transaction = NULL; + data->fingerprint = sentry_value_new_null(); + data->user = sentry_value_new_null(); + data->tags = sentry_value_new_object(); + data->extra = sentry_value_new_object(); + data->attributes = sentry_value_new_object(); + data->contexts = sentry_value_new_object(); + data->propagation_context = sentry_value_new_object(); + data->breadcrumbs = sentry__ringbuffer_new(SENTRY_BREADCRUMBS_MAX); + data->dynamic_sampling_context = sentry_value_new_object(); + data->level = SENTRY_LEVEL_ERROR; + data->last_event_id = sentry_uuid_nil(); + data->client_sdk = sentry_value_new_null(); + data->attachments = sentry_value_new_list(); + data->transaction_object = NULL; + data->span = NULL; + data->trace_managed = true; +} + +static void +cleanup_scope_data(sentry_scope_data_t *data) +{ + sentry_free(data->release); + sentry_free(data->environment); + sentry_free(data->transaction); + sentry_value_decref(data->fingerprint); + sentry_value_decref(data->user); + sentry_value_decref(data->tags); + sentry_value_decref(data->extra); + sentry_value_decref(data->attributes); + sentry_value_decref(data->contexts); + sentry_value_decref(data->propagation_context); + sentry__ringbuffer_free(data->breadcrumbs); + sentry_value_decref(data->dynamic_sampling_context); + sentry_value_decref(data->client_sdk); + sentry_value_decref(data->attachments); + sentry__transaction_decref(data->transaction_object); + sentry__span_decref(data->span); +} + +static sentry_scope_data_t * +new_scope_data(void) +{ + sentry_scope_data_t *data = SENTRY_MAKE(sentry_scope_data_t); + if (data) { + init_scope_data(data); + } + return data; +} + +static void +free_scope_data(sentry_scope_data_t *data) +{ + if (!data) { + return; + } + cleanup_scope_data(data); + sentry_free(data); +} + +static void +clear_scope_data(sentry_scope_data_t *data) +{ + // Keep the propagation and dynamic sampling contexts across clears so + // telemetry captured afterwards continues on the same trace. + bool trace_managed = data->trace_managed; + sentry_value_t propagation_context = data->propagation_context; + sentry_value_t dynamic_sampling_context = data->dynamic_sampling_context; + sentry_value_incref(propagation_context); + sentry_value_incref(dynamic_sampling_context); + + cleanup_scope_data(data); + init_scope_data(data); + + sentry_value_decref(data->propagation_context); + sentry_value_decref(data->dynamic_sampling_context); + data->propagation_context = propagation_context; + data->dynamic_sampling_context = dynamic_sampling_context; + data->trace_managed = trace_managed; +} + +static sentry_scope_data_t * +clone_scope_data(const sentry_scope_data_t *source) +{ + sentry_scope_data_t *clone = SENTRY_MAKE(sentry_scope_data_t); + if (!clone) { + return NULL; + } + + clone->release = sentry__string_clone(source->release); + clone->environment = sentry__string_clone(source->environment); + clone->transaction = sentry__string_clone(source->transaction); + clone->fingerprint = sentry__value_clone(source->fingerprint); + clone->user = sentry__value_clone(source->user); + clone->tags = sentry__value_clone(source->tags); + clone->extra = sentry__value_clone(source->extra); + clone->attributes = sentry__value_clone(source->attributes); + clone->contexts = sentry__value_clone(source->contexts); + clone->propagation_context + = sentry__value_clone(source->propagation_context); + clone->breadcrumbs = sentry__ringbuffer_clone(source->breadcrumbs); + clone->dynamic_sampling_context + = sentry__value_clone(source->dynamic_sampling_context); + if (sentry_value_is_frozen(source->dynamic_sampling_context)) { + sentry_value_freeze(clone->dynamic_sampling_context); + } + clone->level = source->level; + clone->last_event_id = source->last_event_id; + clone->client_sdk = sentry__value_clone(source->client_sdk); + clone->attachments = sentry__attachments_clone(source->attachments); + + clone->transaction_object = source->transaction_object; + sentry__transaction_incref(clone->transaction_object); + clone->span = source->span; + sentry__span_incref(clone->span); + clone->trace_managed = source->trace_managed; + + return clone; +} + +static void +generate_propagation_context(sentry_value_t propagation_context) +{ + sentry_value_set_by_key( + propagation_context, "trace", sentry_value_new_object()); + sentry_uuid_t trace_id = sentry_uuid_new_v4(); + sentry_uuid_t span_id = sentry_uuid_new_v4(); + sentry_value_set_by_key( + sentry_value_get_by_key(propagation_context, "trace"), "trace_id", + sentry__value_new_internal_uuid(&trace_id)); + sentry_value_set_by_key( + sentry_value_get_by_key(propagation_context, "trace"), "span_id", + sentry__value_new_span_uuid(&span_id)); + sentry__generate_sample_rand( + sentry_value_get_by_key(propagation_context, "trace")); +} + +void +sentry__scope_update_dsc(sentry_scope_t *scope, const sentry_options_t *options) +{ + sentry_value_decref(scope->data->dynamic_sampling_context); + sentry_value_t dsc = sentry_value_new_object(); + + if (options->dsn) { + sentry_value_set_by_key(dsc, "public_key", + sentry_value_new_string(options->dsn->public_key)); + } + const char *org_id = sentry__options_get_org_id(options); + if (org_id) { + sentry_value_set_by_key(dsc, "org_id", sentry_value_new_string(org_id)); + } + sentry_value_set_by_key(dsc, "sample_rate", + sentry_value_new_double(options->traces_sample_rate)); + if (options->traces_sampler) { + sentry_value_set_by_key( + dsc, "sample_rate", sentry_value_new_double(1.0)); + } + sentry_value_t sample_rand = sentry_value_get_by_key( + sentry_value_get_by_key(scope->data->propagation_context, "trace"), + "sample_rand"); + sentry_value_set_by_key(dsc, "sample_rand", sample_rand); + sentry_value_incref(sample_rand); + sentry_value_set_by_key( + dsc, "release", sentry_value_new_string(scope->data->release)); + sentry_value_set_by_key( + dsc, "environment", sentry_value_new_string(scope->data->environment)); + + scope->data->dynamic_sampling_context = dsc; +} + +static bool +init_scope(sentry_scope_t *scope, sentry_scope_data_t *data) +{ + scope->data = data ? data : new_scope_data(); + if (!scope->data) { + return false; + } scope->observers = NULL; scope->num_observers = 0; scope->is_notifying = 0; scope->pending_flush = false; scope->one_shot = false; + return true; } static sentry_scope_t * @@ -106,18 +300,21 @@ get_scope(void) } memset(&g_scope, 0, sizeof(sentry_scope_t)); - init_scope(&g_scope); - g_scope.user = sentry_value_new_object(); - sentry_value_set_by_key(g_scope.contexts, "os", sentry__get_os_context()); + memset(&g_scope_data, 0, sizeof(sentry_scope_data_t)); + init_scope_data(&g_scope_data); + init_scope(&g_scope, &g_scope_data); + g_scope.data->user = sentry_value_new_object(); + sentry_value_set_by_key( + g_scope.data->contexts, "os", sentry__get_os_context()); #if defined(SENTRY_PLATFORM_WINDOWS) && !defined(SENTRY_PLATFORM_XBOX) sentry_value_t wine_context = sentry__get_wine_context(); if (!sentry_value_is_null(wine_context)) { - sentry_value_set_by_key(g_scope.contexts, "wine", wine_context); + sentry_value_set_by_key(g_scope.data->contexts, "wine", wine_context); } else { sentry_value_decref(wine_context); } #endif - g_scope.client_sdk = get_client_sdk(); + g_scope.data->client_sdk = get_client_sdk(); g_scope_initialized = true; @@ -125,24 +322,8 @@ get_scope(void) } static void -cleanup_scope(sentry_scope_t *scope) +cleanup_observers(sentry_scope_t *scope) { - sentry_free(scope->release); - sentry_free(scope->environment); - sentry_free(scope->transaction); - sentry_value_decref(scope->fingerprint); - sentry_value_decref(scope->user); - sentry_value_decref(scope->tags); - sentry_value_decref(scope->extra); - sentry_value_decref(scope->attributes); - sentry_value_decref(scope->contexts); - sentry_value_decref(scope->propagation_context); - sentry__ringbuffer_free(scope->breadcrumbs); - sentry_value_decref(scope->dynamic_sampling_context); - sentry_value_decref(scope->client_sdk); - sentry_value_decref(scope->attachments); - sentry__transaction_decref(scope->transaction_object); - sentry__span_decref(scope->span); for (size_t i = 0; i < scope->num_observers; i++) { sentry_free(scope->observers[i]); } @@ -152,6 +333,14 @@ cleanup_scope(sentry_scope_t *scope) scope->pending_flush = false; } +static void +cleanup_scope(sentry_scope_t *scope) +{ + free_scope_data(scope->data); + scope->data = NULL; + cleanup_observers(scope); +} + void sentry__scope_cleanup(void) { @@ -159,7 +348,9 @@ sentry__scope_cleanup(void) sentry__mutex_lock(&g_lock); if (g_scope_initialized) { g_scope_initialized = false; - cleanup_scope(&g_scope); + cleanup_scope_data(g_scope.data); + g_scope.data = NULL; + cleanup_observers(&g_scope); } sentry__mutex_unlock(&g_lock); } @@ -314,7 +505,10 @@ sentry_scope_new(void) return NULL; } - init_scope(scope); + if (!init_scope(scope, NULL)) { + sentry_free(scope); + return NULL; + } return scope; } @@ -347,6 +541,41 @@ sentry_local_scope_new(void) return scope; } +void +sentry__scope_apply_options(sentry_scope_t *scope, sentry_options_t *options) +{ + if (options->sdk_name) { + sentry_value_t sdk_name = sentry_value_new_string(options->sdk_name); + sentry_value_set_by_key(scope->data->client_sdk, "name", sdk_name); + } + sentry_value_t integrations + = sentry_value_get_by_key(scope->data->client_sdk, "integrations"); + for (size_t i = 0; i < options->num_integrations; i++) { + const char *name = options->integrations[i]->name; + if (!name) { + continue; + } + if (sentry_value_is_null(integrations)) { + integrations = sentry_value_new_list(); + sentry_value_set_by_key( + scope->data->client_sdk, "integrations", integrations); + } + sentry_value_append(integrations, sentry_value_new_string(name)); + } + sentry_value_freeze(scope->data->client_sdk); + generate_propagation_context(scope->data->propagation_context); + scope->data->release = sentry__string_clone(options->release); + scope->data->environment = sentry__string_clone(options->environment); + sentry_value_decref(scope->data->attachments); + scope->data->attachments = options->attachments; + options->attachments = sentry_value_new_null(); + + sentry__ringbuffer_set_max_size( + scope->data->breadcrumbs, options->max_breadcrumbs); + + sentry__scope_update_dsc(scope, options); +} + void sentry_scope_clear(sentry_scope_t *scope) { @@ -363,35 +592,7 @@ sentry_scope_clear(sentry_scope_t *scope) } sentry__scope_end_notify(scope); - sentry_scope_observer_t **observers = scope->observers; - size_t num_observers = scope->num_observers; - size_t is_notifying = scope->is_notifying; - bool pending_flush = scope->pending_flush; - scope->observers = NULL; - scope->num_observers = 0; - - // Keep the propagation and dynamic sampling contexts across clears so - // telemetry captured afterwards continues on the same trace. - bool trace_managed = scope->trace_managed; - sentry_value_t propagation_context = scope->propagation_context; - sentry_value_t dynamic_sampling_context = scope->dynamic_sampling_context; - sentry_value_incref(propagation_context); - sentry_value_incref(dynamic_sampling_context); - bool one_shot = scope->one_shot; - - cleanup_scope(scope); - init_scope(scope); - - sentry_value_decref(scope->propagation_context); - sentry_value_decref(scope->dynamic_sampling_context); - scope->propagation_context = propagation_context; - scope->dynamic_sampling_context = dynamic_sampling_context; - scope->trace_managed = trace_managed; - scope->one_shot = one_shot; - scope->observers = observers; - scope->num_observers = num_observers; - scope->is_notifying = is_notifying; - scope->pending_flush = pending_flush; + clear_scope_data(scope->data); } sentry_scope_t * @@ -406,78 +607,88 @@ sentry_scope_clone(const sentry_scope_t *scope) return NULL; } - clone->release = sentry__string_clone(scope->release); - clone->environment = sentry__string_clone(scope->environment); - clone->transaction = sentry__string_clone(scope->transaction); - clone->fingerprint = sentry__value_clone(scope->fingerprint); - clone->user = sentry__value_clone(scope->user); - clone->tags = sentry__value_clone(scope->tags); - clone->extra = sentry__value_clone(scope->extra); - clone->attributes = sentry__value_clone(scope->attributes); - clone->contexts = sentry__value_clone(scope->contexts); - clone->propagation_context - = sentry__value_clone(scope->propagation_context); - clone->breadcrumbs = sentry__ringbuffer_clone(scope->breadcrumbs); - clone->dynamic_sampling_context - = sentry__value_clone(scope->dynamic_sampling_context); - if (sentry_value_is_frozen(scope->dynamic_sampling_context)) { - sentry_value_freeze(clone->dynamic_sampling_context); + sentry_scope_data_t *data = clone_scope_data(scope->data); + if (!data) { + sentry_free(clone); + return NULL; + } + if (!init_scope(clone, data)) { + free_scope_data(data); + sentry_free(clone); + return NULL; } - clone->level = scope->level; - clone->last_event_id = scope->last_event_id; - clone->client_sdk = sentry__value_clone(scope->client_sdk); - clone->attachments = sentry__attachments_clone(scope->attachments); - - clone->transaction_object = scope->transaction_object; - sentry__transaction_incref(clone->transaction_object); - clone->span = scope->span; - sentry__span_incref(clone->span); - clone->trace_managed = scope->trace_managed; - return clone; } +sentry_value_t +sentry__scope_load_propagation_context(const sentry_scope_t *scope) +{ + return sentry__value_clone(scope->data->propagation_context); +} + void -sentry__scope_freeze_dsc(sentry_scope_t *scope, sentry_value_t incoming) +sentry__scope_set_propagation_context( + sentry_scope_t *scope, const char *key, sentry_value_t value) { - sentry_value_decref(scope->dynamic_sampling_context); - sentry_value_t dsc = sentry_value_new_object(); - sentry__value_merge_objects(dsc, incoming); - sentry_value_freeze(dsc); - scope->dynamic_sampling_context = dsc; + sentry_value_set_by_key(scope->data->propagation_context, key, value); } void -sentry__scope_update_dsc(sentry_scope_t *scope, const sentry_options_t *options) +sentry__scope_regenerate_propagation_context(sentry_scope_t *scope) { - sentry_value_decref(scope->dynamic_sampling_context); - sentry_value_t dsc = sentry_value_new_object(); + generate_propagation_context(scope->data->propagation_context); +} - if (options->dsn) { - sentry_value_set_by_key(dsc, "public_key", - sentry_value_new_string(options->dsn->public_key)); - } - const char *org_id = sentry__options_get_org_id(options); - if (org_id) { - sentry_value_set_by_key(dsc, "org_id", sentry_value_new_string(org_id)); - } - sentry_value_set_by_key(dsc, "sample_rate", - sentry_value_new_double(options->traces_sample_rate)); - if (options->traces_sampler) { - sentry_value_set_by_key( - dsc, "sample_rate", sentry_value_new_double(1.0)); - } - sentry_value_t sample_rand = sentry_value_get_by_key( - sentry_value_get_by_key(scope->propagation_context, "trace"), - "sample_rand"); - sentry_value_set_by_key(dsc, "sample_rand", sample_rand); - sentry_value_incref(sample_rand); - sentry_value_set_by_key( - dsc, "release", sentry_value_new_string(scope->release)); +sentry_value_t +sentry__scope_load_trace_context(const sentry_scope_t *scope) +{ + return sentry__value_clone( + sentry_value_get_by_key(scope->data->propagation_context, "trace")); +} + +void +sentry__scope_set_trace_context( + sentry_scope_t *scope, const char *key, sentry_value_t value) +{ sentry_value_set_by_key( - dsc, "environment", sentry_value_new_string(scope->environment)); + sentry_value_get_by_key(scope->data->propagation_context, "trace"), key, + value); +} + +bool +sentry__scope_is_trace_managed(const sentry_scope_t *scope) +{ + return scope->data->trace_managed; +} + +void +sentry__scope_set_trace_managed(sentry_scope_t *scope, bool managed) +{ + scope->data->trace_managed = managed; +} + +sentry_value_t +sentry__scope_load_dsc(const sentry_scope_t *scope) +{ + return sentry__value_clone(scope->data->dynamic_sampling_context); +} - scope->dynamic_sampling_context = dsc; +void +sentry__scope_foreach_dsc(const sentry_scope_t *scope, + sentry_value_foreach_key_value_function_t callback, void *userdata) +{ + sentry_value_foreach_key_value( + scope->data->dynamic_sampling_context, callback, userdata); +} + +void +sentry__scope_freeze_dsc(sentry_scope_t *scope, sentry_value_t incoming) +{ + sentry_value_decref(scope->data->dynamic_sampling_context); + sentry_value_t dsc = sentry_value_new_object(); + sentry__value_merge_objects(dsc, incoming); + sentry_value_freeze(dsc); + scope->data->dynamic_sampling_context = dsc; } #if !defined(SENTRY_PLATFORM_NX) @@ -586,27 +797,15 @@ sentry__symbolize_stacktrace(sentry_value_t stacktrace) static sentry_value_t get_span_or_transaction(const sentry_scope_t *scope) { - if (scope->span) { - return scope->span->inner; - } else if (scope->transaction_object) { - return scope->transaction_object->inner; + if (scope->data->span) { + return scope->data->span->inner; + } else if (scope->data->transaction_object) { + return scope->data->transaction_object->inner; } else { return sentry_value_new_null(); } } -#ifdef SENTRY_UNITTEST -sentry_value_t -sentry__scope_get_span_or_transaction(void) -{ - sentry_value_t result = sentry_value_new_null(); - SENTRY_WITH_SCOPE (scope) { - result = get_span_or_transaction(scope); - } - return result; -} -#endif - void sentry__scope_apply_to_event(const sentry_scope_t *scope, const sentry_options_t *options, sentry_value_t event, @@ -636,20 +835,20 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, PLACE_STRING("platform", "native"); - PLACE_STRING("release", scope->release); + PLACE_STRING("release", scope->data->release); PLACE_STRING("dist", options->dist); - PLACE_STRING("environment", scope->environment); + PLACE_STRING("environment", scope->data->environment); // is not transaction and has no level if (IS_NULL("type") && IS_NULL("level")) { - SET("level", sentry__value_new_level(scope->level)); + SET("level", sentry__value_new_level(scope->data->level)); } - if (sentry_value_get_type(scope->user) == SENTRY_VALUE_TYPE_OBJECT) { + if (sentry_value_get_type(scope->data->user) == SENTRY_VALUE_TYPE_OBJECT) { if (options->run && options->run->installation_id) { // ensure event has a user object if (IS_NULL("user")) { - SET("user", sentry__value_clone(scope->user)); + SET("user", sentry__value_clone(scope->data->user)); } // patch missing user ID with installation ID sentry_value_t user = sentry_value_get_by_key(event, "user"); @@ -658,33 +857,33 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, sentry_value_set_by_key(user, "id", sentry_value_new_string(options->run->installation_id)); } - } else if (sentry_value_get_length(scope->user) > 0) { - PLACE_CLONED_VALUE("user", scope->user); + } else if (sentry_value_get_length(scope->data->user) > 0) { + PLACE_CLONED_VALUE("user", scope->data->user); } } - PLACE_CLONED_VALUE("fingerprint", scope->fingerprint); - PLACE_STRING("transaction", scope->transaction); - PLACE_VALUE("sdk", scope->client_sdk); + PLACE_CLONED_VALUE("fingerprint", scope->data->fingerprint); + PLACE_STRING("transaction", scope->data->transaction); + PLACE_VALUE("sdk", scope->data->client_sdk); sentry_value_t event_tags = sentry_value_get_by_key(event, "tags"); if (sentry_value_is_null(event_tags)) { - if (!sentry_value_is_null(scope->tags)) { - PLACE_CLONED_VALUE("tags", scope->tags); + if (!sentry_value_is_null(scope->data->tags)) { + PLACE_CLONED_VALUE("tags", scope->data->tags); } } else { - sentry__value_merge_objects(event_tags, scope->tags); + sentry__value_merge_objects(event_tags, scope->data->tags); } sentry_value_t event_extra = sentry_value_get_by_key(event, "extra"); if (sentry_value_is_null(event_extra)) { - if (!sentry_value_is_null(scope->extra)) { - PLACE_CLONED_VALUE("extra", scope->extra); + if (!sentry_value_is_null(scope->data->extra)) { + PLACE_CLONED_VALUE("extra", scope->data->extra); } } else { - sentry__value_merge_objects(event_extra, scope->extra); + sentry__value_merge_objects(event_extra, scope->data->extra); } bool is_transaction = sentry__event_is_transaction(event); - sentry_value_t contexts = sentry__value_clone(scope->contexts); + sentry_value_t contexts = sentry__value_clone(scope->data->contexts); if (is_transaction && !sentry_value_is_null(contexts)) { sentry_value_remove_by_key(contexts, "trace"); } @@ -717,7 +916,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, if (!is_transaction && sentry_value_is_null(scope_trace) && sentry_value_is_null( sentry_value_get_by_key(event_contexts, "trace"))) { - sentry__value_merge_objects(contexts, scope->propagation_context); + sentry__value_merge_objects(contexts, scope->data->propagation_context); } if (sentry_value_is_null(event_contexts)) { PLACE_VALUE("contexts", contexts); @@ -730,7 +929,7 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, sentry_value_t event_breadcrumbs = sentry_value_get_by_key(event, "breadcrumbs"); sentry_value_t scope_breadcrumbs - = sentry__ringbuffer_to_list(scope->breadcrumbs); + = sentry__ringbuffer_to_list(scope->data->breadcrumbs); sentry_value_set_by_key(event, "breadcrumbs", sentry__value_merge_breadcrumbs(event_breadcrumbs, scope_breadcrumbs, options->max_breadcrumbs)); @@ -762,24 +961,42 @@ sentry__scope_apply_to_event(const sentry_scope_t *scope, void sentry_scope_add_breadcrumb(sentry_scope_t *scope, sentry_value_t breadcrumb) { - if (sentry__ringbuffer_append(scope->breadcrumbs, breadcrumb) == 0) { + if (sentry__ringbuffer_append(scope->data->breadcrumbs, breadcrumb) == 0) { SENTRY_SCOPE_NOTIFY(scope, add_breadcrumb, breadcrumb); } } +sentry_value_t +sentry__scope_breadcrumbs_to_list(const sentry_scope_t *scope) +{ + return sentry__ringbuffer_to_list(scope->data->breadcrumbs); +} + +sentry_value_t +sentry__scope_ref_user(const sentry_scope_t *scope) +{ + return sentry_value_incref(scope->data->user); +} + void sentry_scope_set_user(sentry_scope_t *scope, sentry_value_t user) { - sentry_value_decref(scope->user); - scope->user = user; + sentry_value_decref(scope->data->user); + scope->data->user = user; SENTRY_SCOPE_NOTIFY(scope, set_user, user); } +sentry_value_t +sentry__scope_load_tags(const sentry_scope_t *scope) +{ + return sentry__value_clone(scope->data->tags); +} + void sentry_scope_set_tag(sentry_scope_t *scope, const char *key, const char *value) { if (sentry_value_set_by_key( - scope->tags, key, sentry_value_new_string(value)) + scope->data->tags, key, sentry_value_new_string(value)) == 0) { SENTRY_SCOPE_NOTIFY(scope, set_tag, key, value); } @@ -791,7 +1008,7 @@ sentry_scope_set_tag_n(sentry_scope_t *scope, const char *key, size_t key_len, { char *k = sentry__string_clone_n(key, key_len); sentry_value_t v = sentry_value_new_string_n(value, value_len); - if (sentry__value_set_by_key_owned(scope->tags, k, key_len, v) == 0) { + if (sentry__value_set_by_key_owned(scope->data->tags, k, key_len, v) == 0) { SENTRY_SCOPE_NOTIFY(scope, set_tag, k, sentry_value_as_string(v)); } } @@ -818,7 +1035,7 @@ sentry_scope_set_tags(sentry_scope_t *scope, sentry_value_t tags) void sentry_scope_remove_tag(sentry_scope_t *scope, const char *key) { - if (sentry_value_remove_by_key(scope->tags, key) == 0) { + if (sentry_value_remove_by_key(scope->data->tags, key) == 0) { SENTRY_SCOPE_NOTIFY(scope, remove_tag, key); } } @@ -827,18 +1044,25 @@ void sentry_scope_remove_tag_n( sentry_scope_t *scope, const char *key, size_t key_len) { - char *k = sentry__value_remove_and_take_key_n(scope->tags, key, key_len); + char *k + = sentry__value_remove_and_take_key_n(scope->data->tags, key, key_len); if (k) { SENTRY_SCOPE_NOTIFY(scope, remove_tag, k); } sentry_free(k); } +sentry_value_t +sentry__scope_load_extra(const sentry_scope_t *scope) +{ + return sentry__value_clone(scope->data->extra); +} + void sentry_scope_set_extra( sentry_scope_t *scope, const char *key, sentry_value_t value) { - if (sentry_value_set_by_key(scope->extra, key, value) == 0) { + if (sentry_value_set_by_key(scope->data->extra, key, value) == 0) { SENTRY_SCOPE_NOTIFY(scope, set_extra, key, value); } } @@ -848,7 +1072,8 @@ sentry_scope_set_extra_n(sentry_scope_t *scope, const char *key, size_t key_len, sentry_value_t value) { char *k = sentry__string_clone_n(key, key_len); - if (sentry__value_set_by_key_owned(scope->extra, k, key_len, value) == 0) { + if (sentry__value_set_by_key_owned(scope->data->extra, k, key_len, value) + == 0) { SENTRY_SCOPE_NOTIFY(scope, set_extra, k, value); } } @@ -856,7 +1081,7 @@ sentry_scope_set_extra_n(sentry_scope_t *scope, const char *key, size_t key_len, void sentry_scope_remove_extra(sentry_scope_t *scope, const char *key) { - if (sentry_value_remove_by_key(scope->extra, key) == 0) { + if (sentry_value_remove_by_key(scope->data->extra, key) == 0) { SENTRY_SCOPE_NOTIFY(scope, remove_extra, key); } } @@ -865,7 +1090,8 @@ void sentry_scope_remove_extra_n( sentry_scope_t *scope, const char *key, size_t key_len) { - char *k = sentry__value_remove_and_take_key_n(scope->extra, key, key_len); + char *k + = sentry__value_remove_and_take_key_n(scope->data->extra, key, key_len); if (k) { SENTRY_SCOPE_NOTIFY(scope, remove_extra, k); } @@ -890,27 +1116,39 @@ sentry_scope_set_attribute_n(sentry_scope_t *scope, const char *key, sentry_value_decref(attribute); return; } - sentry_value_set_by_key_n(scope->attributes, key, key_len, attribute); + sentry_value_set_by_key_n(scope->data->attributes, key, key_len, attribute); +} + +sentry_value_t +sentry__scope_load_attributes(const sentry_scope_t *scope) +{ + return sentry__value_clone(scope->data->attributes); } void sentry_scope_remove_attribute(sentry_scope_t *scope, const char *key) { - sentry_value_remove_by_key(scope->attributes, key); + sentry_value_remove_by_key(scope->data->attributes, key); } void sentry_scope_remove_attribute_n( sentry_scope_t *scope, const char *key, size_t key_len) { - sentry_value_remove_by_key_n(scope->attributes, key, key_len); + sentry_value_remove_by_key_n(scope->data->attributes, key, key_len); +} + +sentry_value_t +sentry__scope_load_contexts(const sentry_scope_t *scope) +{ + return sentry__value_clone(scope->data->contexts); } void sentry_scope_set_context( sentry_scope_t *scope, const char *key, sentry_value_t value) { - if (sentry_value_set_by_key(scope->contexts, key, value) == 0) { + if (sentry_value_set_by_key(scope->data->contexts, key, value) == 0) { SENTRY_SCOPE_NOTIFY(scope, set_context, key, value); } } @@ -920,7 +1158,7 @@ sentry_scope_set_context_n(sentry_scope_t *scope, const char *key, size_t key_len, sentry_value_t value) { char *k = sentry__string_clone_n(key, key_len); - if (sentry__value_set_by_key_owned(scope->contexts, k, key_len, value) + if (sentry__value_set_by_key_owned(scope->data->contexts, k, key_len, value) == 0) { SENTRY_SCOPE_NOTIFY(scope, set_context, k, value); } @@ -929,7 +1167,7 @@ sentry_scope_set_context_n(sentry_scope_t *scope, const char *key, void sentry_scope_remove_context(sentry_scope_t *scope, const char *key) { - if (sentry_value_remove_by_key(scope->contexts, key) == 0) { + if (sentry_value_remove_by_key(scope->data->contexts, key) == 0) { SENTRY_SCOPE_NOTIFY(scope, remove_context, key); } } @@ -938,8 +1176,8 @@ void sentry_scope_remove_context_n( sentry_scope_t *scope, const char *key, size_t key_len) { - char *k - = sentry__value_remove_and_take_key_n(scope->contexts, key, key_len); + char *k = sentry__value_remove_and_take_key_n( + scope->data->contexts, key, key_len); if (k) { SENTRY_SCOPE_NOTIFY(scope, remove_context, k); } @@ -959,16 +1197,18 @@ sentry_scope_update_context_n(sentry_scope_t *scope, const char *key, size_t key_len, sentry_value_t value) { sentry_value_t context - = sentry_value_get_by_key_n(scope->contexts, key, key_len); + = sentry_value_get_by_key_n(scope->data->contexts, key, key_len); char *k = sentry__string_clone_n(key, key_len); if (sentry_value_is_null(context)) { - if (sentry__value_set_by_key_owned(scope->contexts, k, key_len, value) + if (sentry__value_set_by_key_owned( + scope->data->contexts, k, key_len, value) != 0) { return; } } else { sentry__value_merge_objects(value, context); - if (sentry__value_set_by_key_owned(scope->contexts, k, key_len, value) + if (sentry__value_set_by_key_owned( + scope->data->contexts, k, key_len, value) != 0) { return; } @@ -980,11 +1220,11 @@ void sentry_scope_set_release_n( sentry_scope_t *scope, const char *release, size_t release_len) { - sentry_free(scope->release); - scope->release = sentry__string_clone_n(release, release_len); - sentry_value_set_by_key(scope->dynamic_sampling_context, "release", - sentry_value_new_string(scope->release)); - SENTRY_SCOPE_NOTIFY(scope, set_release, scope->release); + sentry_free(scope->data->release); + scope->data->release = sentry__string_clone_n(release, release_len); + sentry_value_set_by_key(scope->data->dynamic_sampling_context, "release", + sentry_value_new_string(scope->data->release)); + SENTRY_SCOPE_NOTIFY(scope, set_release, scope->data->release); } void @@ -997,11 +1237,12 @@ void sentry_scope_set_environment_n( sentry_scope_t *scope, const char *environment, size_t environment_len) { - sentry_free(scope->environment); - scope->environment = sentry__string_clone_n(environment, environment_len); - sentry_value_set_by_key(scope->dynamic_sampling_context, "environment", - sentry_value_new_string(scope->environment)); - SENTRY_SCOPE_NOTIFY(scope, set_environment, scope->environment); + sentry_free(scope->data->environment); + scope->data->environment + = sentry__string_clone_n(environment, environment_len); + sentry_value_set_by_key(scope->data->dynamic_sampling_context, + "environment", sentry_value_new_string(scope->data->environment)); + SENTRY_SCOPE_NOTIFY(scope, set_environment, scope->data->environment); } void @@ -1015,14 +1256,15 @@ void sentry_scope_set_transaction_n( sentry_scope_t *scope, const char *transaction, size_t transaction_len) { - sentry_free(scope->transaction); - scope->transaction = sentry__string_clone_n(transaction, transaction_len); + sentry_free(scope->data->transaction); + scope->data->transaction + = sentry__string_clone_n(transaction, transaction_len); - if (scope->transaction_object) { + if (scope->data->transaction_object) { sentry_transaction_set_name_n( - scope->transaction_object, transaction, transaction_len); + scope->data->transaction_object, transaction, transaction_len); } - SENTRY_SCOPE_NOTIFY(scope, set_transaction, scope->transaction); + SENTRY_SCOPE_NOTIFY(scope, set_transaction, scope->data->transaction); } void @@ -1032,6 +1274,12 @@ sentry_scope_set_transaction(sentry_scope_t *scope, const char *transaction) scope, transaction, sentry__guarded_strlen(transaction)); } +sentry_value_t +sentry__scope_ref_fingerprint(const sentry_scope_t *scope) +{ + return sentry_value_incref(scope->data->fingerprint); +} + void sentry__scope_set_fingerprint_va( sentry_scope_t *scope, const char *fingerprint, va_list va) @@ -1042,8 +1290,8 @@ sentry__scope_set_fingerprint_va( fingerprint_value, sentry_value_new_string(fingerprint)); } - sentry_value_decref(scope->fingerprint); - scope->fingerprint = fingerprint_value; + sentry_value_decref(scope->data->fingerprint); + scope->data->fingerprint = fingerprint_value; SENTRY_SCOPE_NOTIFY(scope, set_fingerprint, fingerprint_value); } @@ -1097,23 +1345,35 @@ sentry_scope_set_fingerprints( return; } - sentry_value_decref(scope->fingerprint); - scope->fingerprint = fingerprints; + sentry_value_decref(scope->data->fingerprint); + scope->data->fingerprint = fingerprints; SENTRY_SCOPE_NOTIFY(scope, set_fingerprint, fingerprints); } void sentry_scope_remove_fingerprint(sentry_scope_t *scope) { - sentry_value_decref(scope->fingerprint); - scope->fingerprint = sentry_value_new_null(); - SENTRY_SCOPE_NOTIFY(scope, set_fingerprint, scope->fingerprint); + sentry_value_decref(scope->data->fingerprint); + scope->data->fingerprint = sentry_value_new_null(); + SENTRY_SCOPE_NOTIFY(scope, set_fingerprint, scope->data->fingerprint); +} + +sentry_level_t +sentry__scope_get_level(const sentry_scope_t *scope) +{ + return scope->data->level; +} + +sentry_value_t +sentry__scope_ref_client_sdk(const sentry_scope_t *scope) +{ + return sentry_value_incref(scope->data->client_sdk); } void sentry_scope_set_level(sentry_scope_t *scope, sentry_level_t level) { - scope->level = level; + scope->data->level = level; SENTRY_SCOPE_NOTIFY(scope, set_level, level); } @@ -1121,38 +1381,42 @@ void sentry_scope_set_transaction_object( sentry_scope_t *scope, sentry_transaction_t *tx) { - sentry__span_decref(scope->span); - scope->span = NULL; - // incref before decref, so rebinding the same object cannot free it - sentry__transaction_incref(tx); - sentry__transaction_decref(scope->transaction_object); - scope->transaction_object = tx; + sentry__scope_set_transaction_object(scope, tx); } void sentry_scope_set_span(sentry_scope_t *scope, sentry_span_t *span) { - sentry__transaction_decref(scope->transaction_object); - scope->transaction_object = NULL; - // incref before decref, so rebinding the same object cannot free it - sentry__span_incref(span); - sentry__span_decref(scope->span); - scope->span = span; + sentry__scope_set_span(scope, span); +} + +sentry_value_t +sentry__scope_load_attachments(const sentry_scope_t *scope) +{ + return sentry_value_incref(scope->data->attachments); } sentry_value_t sentry__scope_add_attachment(sentry_scope_t *scope, sentry_value_t attachment) { - size_t len = sentry_value_get_length(scope->attachments); + size_t len = sentry_value_get_length(scope->data->attachments); sentry_value_t added - = sentry__attachments_add(&scope->attachments, attachment); + = sentry__attachments_add(&scope->data->attachments, attachment); if (!sentry_value_is_null(added) - && sentry_value_get_length(scope->attachments) > len) { + && sentry_value_get_length(scope->data->attachments) > len) { SENTRY_SCOPE_NOTIFY(scope, add_attachment, added); } return added; } +sentry_value_t +sentry__scope_take_attachments(sentry_scope_t *scope) +{ + sentry_value_t attachments = scope->data->attachments; + scope->data->attachments = sentry_value_new_list(); + return attachments; +} + void sentry_scope_remove_attachment( sentry_scope_t *scope, sentry_uuid_t attachment_id) @@ -1162,7 +1426,7 @@ sentry_scope_remove_attachment( } sentry_value_t removed - = sentry__attachments_remove(scope->attachments, &attachment_id); + = sentry__attachments_remove(scope->data->attachments, &attachment_id); if (!sentry_value_is_null(removed)) { SENTRY_SCOPE_NOTIFY(scope, remove_attachment, removed); } @@ -1183,6 +1447,158 @@ sentry_scope_add_attachment(sentry_scope_t *scope, sentry_value_t attachment) return attachment_id; } +sentry_transaction_t * +sentry__scope_ref_transaction_object(const sentry_scope_t *scope) +{ + sentry_transaction_t *transaction = scope->data->transaction_object; + sentry__transaction_incref(transaction); + return transaction; +} + +void +sentry__scope_set_transaction_object( + sentry_scope_t *scope, sentry_transaction_t *tx) +{ + sentry__span_decref(scope->data->span); + scope->data->span = NULL; + // incref before decref, so rebinding the same object cannot free it + sentry__transaction_incref(tx); + sentry__transaction_decref(scope->data->transaction_object); + scope->data->transaction_object = tx; +} + +bool +sentry__scope_remove_transaction_object( + sentry_scope_t *scope, sentry_transaction_t *transaction) +{ + if (scope->data->transaction_object == transaction) { + sentry__transaction_decref(scope->data->transaction_object); + scope->data->transaction_object = NULL; + return true; + } + return false; +} + +bool +sentry__scope_remove_transaction_value(sentry_scope_t *scope, sentry_value_t tx) +{ + if (scope->data->transaction_object) { + sentry_value_t scope_tx = scope->data->transaction_object->inner; + + const char *tx_id + = sentry_value_as_string(sentry_value_get_by_key(tx, "span_id")); + const char *scope_tx_id = sentry_value_as_string( + sentry_value_get_by_key(scope_tx, "span_id")); + if (sentry__string_eq(tx_id, scope_tx_id)) { + sentry__transaction_decref(scope->data->transaction_object); + scope->data->transaction_object = NULL; + return true; + } + } + return false; +} + +bool +sentry__scope_restore_transaction_object( + sentry_scope_t *scope, sentry_transaction_t *transaction) +{ + bool restored = false; + if (!scope->data->transaction_object && transaction) { + scope->data->transaction_object = transaction; + restored = true; + } + return restored; +} + +sentry_span_t * +sentry__scope_ref_span(const sentry_scope_t *scope) +{ + sentry_span_t *span = scope->data->span; + sentry__span_incref(span); + return span; +} + +sentry_value_t +sentry__scope_ref_span_or_transaction(const sentry_scope_t *scope) +{ + return sentry_value_incref(get_span_or_transaction(scope)); +} + +void +sentry__scope_set_span(sentry_scope_t *scope, sentry_span_t *span) +{ + sentry__transaction_decref(scope->data->transaction_object); + scope->data->transaction_object = NULL; + // incref before decref, so rebinding the same object cannot free it + sentry__span_incref(span); + sentry__span_decref(scope->data->span); + scope->data->span = span; +} + +bool +sentry__scope_remove_span(sentry_scope_t *scope, sentry_span_t *span) +{ + if (scope->data->span == span) { + sentry__span_decref(scope->data->span); + scope->data->span = NULL; + return true; + } + return false; +} + +bool +sentry__scope_remove_span_value(sentry_scope_t *scope, sentry_value_t span) +{ + if (scope->data->span) { + sentry_value_t scope_span = scope->data->span->inner; + + const char *span_id + = sentry_value_as_string(sentry_value_get_by_key(span, "span_id")); + const char *scope_span_id = sentry_value_as_string( + sentry_value_get_by_key(scope_span, "span_id")); + if (sentry__string_eq(span_id, scope_span_id)) { + sentry__span_decref(scope->data->span); + scope->data->span = NULL; + return true; + } + } + return false; +} + +bool +sentry__scope_restore_span(sentry_scope_t *scope, sentry_span_t *span) +{ + bool restored = false; + if (!scope->data->span && span) { + scope->data->span = span; + restored = true; + } + return restored; +} + +sentry_value_t +sentry__scope_ref_release(const sentry_scope_t *scope) +{ + return scope->data->release ? sentry_value_new_string(scope->data->release) + : sentry_value_new_null(); +} + +sentry_value_t +sentry__scope_ref_environment(const sentry_scope_t *scope) +{ + return scope->data->environment + ? sentry_value_new_string(scope->data->environment) + : sentry_value_new_null(); +} + +sentry_value_t +sentry__scope_ref_transaction(const sentry_scope_t *scope) +{ + return scope->data->transaction + ? sentry_value_new_string(scope->data->transaction) + : sentry_value_new_null(); +} + sentry_uuid_t sentry_scope_attach_file(sentry_scope_t *scope, const char *path) { @@ -1252,31 +1668,32 @@ void sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, sentry_value_t telemetry, sentry_value_t attributes) { - sentry__value_merge_objects_shallow(attributes, scope->attributes); + sentry__value_merge_objects_shallow(attributes, scope->data->attributes); // a span on the scope MUST take precedence over the propagation context sentry_value_t trace_id = sentry_value_get_by_key( - sentry_value_get_by_key(scope->propagation_context, "trace"), + sentry_value_get_by_key(scope->data->propagation_context, "trace"), "trace_id"); sentry_value_t parent_span_id = sentry_value_new_object(); - if (scope->transaction_object) { + if (scope->data->transaction_object) { sentry_value_t span_id = sentry_value_get_by_key( - scope->transaction_object->inner, "span_id"); + scope->data->transaction_object->inner, "span_id"); sentry_value_incref(span_id); sentry_value_set_by_key(parent_span_id, "value", span_id); trace_id = sentry_value_get_by_key( - scope->transaction_object->inner, "trace_id"); - } else if (scope->span) { + scope->data->transaction_object->inner, "trace_id"); + } else if (scope->data->span) { sentry_value_t span_id - = sentry_value_get_by_key(scope->span->inner, "span_id"); + = sentry_value_get_by_key(scope->data->span->inner, "span_id"); sentry_value_incref(span_id); sentry_value_set_by_key(parent_span_id, "value", span_id); - trace_id = sentry_value_get_by_key(scope->span->inner, "trace_id"); + trace_id + = sentry_value_get_by_key(scope->data->span->inner, "trace_id"); } sentry_value_set_by_key( parent_span_id, "type", sentry_value_new_string("string")); - if ((scope->transaction_object || scope->span) + if ((scope->data->transaction_object || scope->data->span) && sentry_value_is_null(sentry_value_get_by_key( attributes, "sentry.trace.parent_span_id"))) { sentry_value_set_by_key( @@ -1291,8 +1708,9 @@ sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, sentry_value_set_by_key(telemetry, "trace_id", trace_id); } - if (!sentry_value_is_null(scope->user)) { - sentry_value_t user_id = sentry_value_get_by_key(scope->user, "id"); + if (!sentry_value_is_null(scope->data->user)) { + sentry_value_t user_id + = sentry_value_get_by_key(scope->data->user, "id"); if (!sentry_value_is_null(user_id)) { sentry_value_incref(user_id); sentry__value_add_attribute( @@ -1300,7 +1718,7 @@ sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, } sentry_value_t user_username - = sentry_value_get_by_key(scope->user, "username"); + = sentry_value_get_by_key(scope->data->user, "username"); if (!sentry_value_is_null(user_username)) { sentry_value_incref(user_username); sentry__value_add_attribute( @@ -1308,14 +1726,15 @@ sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, } sentry_value_t user_email - = sentry_value_get_by_key(scope->user, "email"); + = sentry_value_get_by_key(scope->data->user, "email"); if (!sentry_value_is_null(user_email)) { sentry_value_incref(user_email); sentry__value_add_attribute( attributes, user_email, "string", "user.email"); } } - sentry_value_t os_context = sentry_value_get_by_key(scope->contexts, "os"); + sentry_value_t os_context + = sentry_value_get_by_key(scope->data->contexts, "os"); if (!sentry_value_is_null(os_context)) { sentry_value_t os_name = sentry_value_get_by_key(os_context, "name"); sentry_value_t os_version @@ -1331,14 +1750,14 @@ sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, attributes, os_version, "string", "os.version"); } } - if (scope->environment) { + if (scope->data->environment) { sentry__value_add_attribute(attributes, - sentry_value_new_string(scope->environment), "string", + sentry_value_new_string(scope->data->environment), "string", "sentry.environment"); } - if (scope->release) { + if (scope->data->release) { sentry__value_add_attribute(attributes, - sentry_value_new_string(scope->release), "string", + sentry_value_new_string(scope->data->release), "string", "sentry.release"); } } @@ -1346,7 +1765,15 @@ sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, sentry_uuid_t sentry_scope_get_last_event_id(const sentry_scope_t *scope) { - return scope ? scope->last_event_id : sentry_uuid_nil(); + return scope ? scope->data->last_event_id : sentry_uuid_nil(); +} + +void +sentry__scope_set_last_event_id(sentry_scope_t *scope, sentry_uuid_t event_id) +{ + if (scope) { + scope->data->last_event_id = event_id; + } } void @@ -1356,7 +1783,7 @@ sentry__scope_capture_envelope(sentry_scope_t *scope, { sentry_uuid_t event_id = sentry__envelope_get_event_id(envelope); if (!sentry_uuid_is_nil(&event_id)) { - scope->last_event_id = event_id; + scope->data->last_event_id = event_id; } sentry__submit_envelope(transport, envelope, options); diff --git a/src/sentry_scope.h b/src/sentry_scope.h index aa758a824b..6552da326f 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -49,38 +49,13 @@ typedef struct sentry_scope_observer_s { void (*remove_attachment)(void *data, sentry_value_t attachment); } sentry_scope_observer_t; +typedef struct sentry_scope_data_s sentry_scope_data_t; + /** * This represents the current scope. */ struct sentry_scope_s { - char *release; - char *environment; - char *transaction; - sentry_value_t fingerprint; - sentry_value_t user; - sentry_value_t tags; - sentry_value_t extra; - sentry_value_t attributes; - sentry_value_t contexts; - sentry_value_t propagation_context; - sentry_ringbuffer_t *breadcrumbs; - sentry_value_t dynamic_sampling_context; - sentry_level_t level; - sentry_uuid_t last_event_id; - sentry_value_t client_sdk; - sentry_value_t attachments; - - // The span attached to this scope, if any. - // - // Conceptually, every transaction is a span, so it should be possible to - // attach spans or transactions to a scope. But sentry_span_t and - // sentry_transaction_t are unrelated types in the native SDK, so we need - // two distinct pointers. At most one of them should ever be non-null. - // Whenever possible, `transaction` should pull its value from the - // `name` property nested in transaction_object or span. - sentry_transaction_t *transaction_object; - sentry_span_t *span; - bool trace_managed; + sentry_scope_data_t *data; sentry_scope_observer_t **observers; size_t num_observers; @@ -123,6 +98,9 @@ void sentry__scope_unlock(void); */ void sentry__scope_cleanup(void); +void sentry__scope_apply_options( + sentry_scope_t *scope, sentry_options_t *options); + /** * Frees the scope if it is a one-shot local scope. */ @@ -145,13 +123,71 @@ void sentry__scope_apply_to_event(const sentry_scope_t *scope, const sentry_options_t *options, sentry_value_t event, sentry_scope_mode_t mode); +sentry_value_t sentry__scope_ref_release(const sentry_scope_t *scope); + +sentry_value_t sentry__scope_ref_environment(const sentry_scope_t *scope); + +sentry_value_t sentry__scope_ref_transaction(const sentry_scope_t *scope); + +sentry_value_t sentry__scope_ref_fingerprint(const sentry_scope_t *scope); void sentry__scope_set_fingerprint_va( sentry_scope_t *scope, const char *fingerprint, va_list va); void sentry__scope_set_fingerprint_nva(sentry_scope_t *scope, const char *fingerprint, size_t fingerprint_len, va_list va); +sentry_value_t sentry__scope_ref_user(const sentry_scope_t *scope); +sentry_level_t sentry__scope_get_level(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_client_sdk(const sentry_scope_t *scope); + +/** + * Returns an owned reference to the scope attachment list. + * The caller must release it with `sentry_value_decref`. + */ +sentry_value_t sentry__scope_load_attachments(const sentry_scope_t *scope); sentry_value_t sentry__scope_add_attachment( sentry_scope_t *scope, sentry_value_t attachment); +sentry_value_t sentry__scope_take_attachments(sentry_scope_t *scope); +sentry_value_t sentry__scope_load_tags(const sentry_scope_t *scope); + +sentry_value_t sentry__scope_load_extra(const sentry_scope_t *scope); + +sentry_value_t sentry__scope_load_attributes(const sentry_scope_t *scope); + +sentry_value_t sentry__scope_load_contexts(const sentry_scope_t *scope); + +sentry_value_t sentry__scope_load_propagation_context( + const sentry_scope_t *scope); +void sentry__scope_set_propagation_context( + sentry_scope_t *scope, const char *key, sentry_value_t value); +void sentry__scope_regenerate_propagation_context(sentry_scope_t *scope); +sentry_value_t sentry__scope_load_trace_context(const sentry_scope_t *scope); +void sentry__scope_set_trace_context( + sentry_scope_t *scope, const char *key, sentry_value_t value); + +sentry_value_t sentry__scope_breadcrumbs_to_list(const sentry_scope_t *scope); + +sentry_transaction_t *sentry__scope_ref_transaction_object( + const sentry_scope_t *scope); +void sentry__scope_set_transaction_object( + sentry_scope_t *scope, sentry_transaction_t *transaction); +bool sentry__scope_remove_transaction_object( + sentry_scope_t *scope, sentry_transaction_t *transaction); +bool sentry__scope_remove_transaction_value( + sentry_scope_t *scope, sentry_value_t transaction); +bool sentry__scope_restore_transaction_object( + sentry_scope_t *scope, sentry_transaction_t *transaction); + +sentry_span_t *sentry__scope_ref_span(const sentry_scope_t *scope); +sentry_value_t sentry__scope_ref_span_or_transaction( + const sentry_scope_t *scope); +void sentry__scope_set_span(sentry_scope_t *scope, sentry_span_t *span); +bool sentry__scope_remove_span(sentry_scope_t *scope, sentry_span_t *span); +bool sentry__scope_remove_span_value( + sentry_scope_t *scope, sentry_value_t span); +bool sentry__scope_restore_span(sentry_scope_t *scope, sentry_span_t *span); + +bool sentry__scope_is_trace_managed(const sentry_scope_t *scope); +void sentry__scope_set_trace_managed(sentry_scope_t *scope, bool managed); /** * These are convenience macros to automatically lock/unlock the global scope @@ -211,6 +247,10 @@ void sentry__scope_end_notify(sentry_scope_t *scope); sentry__scope_end_notify(scope); \ } while (0) +sentry_value_t sentry__scope_load_dsc(const sentry_scope_t *scope); +void sentry__scope_foreach_dsc(const sentry_scope_t *scope, + sentry_value_foreach_key_value_function_t callback, void *userdata); + /** * Rebuilds the scope's dynamic sampling context (DSC) from the SDK options * and the current propagation context. The previous DSC is discarded. @@ -234,6 +274,9 @@ void sentry__scope_freeze_dsc(sentry_scope_t *scope, sentry_value_t incoming); void sentry__scope_apply_to_telemetry(const sentry_scope_t *scope, sentry_value_t telemetry, sentry_value_t attributes); +void sentry__scope_set_last_event_id( + sentry_scope_t *scope, sentry_uuid_t event_id); + /** * Captures the `envelope` on `scope`, recording the last sent event ID. */ @@ -242,8 +285,3 @@ void sentry__scope_capture_envelope(sentry_scope_t *scope, const sentry_options_t *options); #endif - -// this is only used in unit tests -#ifdef SENTRY_UNITTEST -sentry_value_t sentry__scope_get_span_or_transaction(void); -#endif diff --git a/src/sentry_session.c b/src/sentry_session.c index 182dfa92c5..267365f949 100644 --- a/src/sentry_session.c +++ b/src/sentry_session.c @@ -50,8 +50,17 @@ status_from_string(const char *status) sentry_session_t * sentry__session_new(const sentry_scope_t *scope) { - char *release = sentry__string_clone(scope->release); - char *environment = sentry__string_clone(scope->environment); + sentry_value_t release_value = sentry__scope_ref_release(scope); + char *release = sentry_value_is_null(release_value) + ? NULL + : sentry__string_clone(sentry_value_as_string(release_value)); + sentry_value_decref(release_value); + + sentry_value_t environment_value = sentry__scope_ref_environment(scope); + char *environment = sentry_value_is_null(environment_value) + ? NULL + : sentry__string_clone(sentry_value_as_string(environment_value)); + sentry_value_decref(environment_value); if (!release) { sentry_free(environment); @@ -219,8 +228,10 @@ sentry_start_session(void) if (options) { options->session = sentry__session_new(scope); if (options->session) { - sentry__session_sync_user(options->session, scope->user, + sentry_value_t user = sentry__scope_ref_user(scope); + sentry__session_sync_user(options->session, user, options->run ? options->run->installation_id : NULL); + sentry_value_decref(user); sentry__run_write_session(options->run, options->session); } } diff --git a/src/sentry_tracing.c b/src/sentry_tracing.c index cd0618fa3e..b891caae18 100644 --- a/src/sentry_tracing.c +++ b/src/sentry_tracing.c @@ -97,23 +97,20 @@ transaction_context_new_n(sentry_slice_t name, sentry_slice_t operation) sentry_value_new_string_n(name.ptr, name.len)); SENTRY_WITH_SCOPE_MUT (scope) { - if (!scope->trace_managed - && !sentry_value_is_null( - sentry_value_get_by_key(scope->propagation_context, "trace"))) { + sentry_value_t trace_context = sentry__scope_load_trace_context(scope); + if (!sentry__scope_is_trace_managed(scope) + && !sentry_value_is_null(trace_context)) { // The trace is managed from outside, so we use the propagation // context as the trace source for this transaction. This means that // either a downstream SDK or the user manages trace life-cycles. sentry_value_set_by_key(transaction_context, "trace_id", - sentry__value_clone(sentry_value_get_by_key( - sentry_value_get_by_key( - scope->propagation_context, "trace"), - "trace_id"))); + sentry__value_clone( + sentry_value_get_by_key(trace_context, "trace_id"))); sentry_value_set_by_key(transaction_context, "parent_span_id", - sentry__value_clone(sentry_value_get_by_key( - sentry_value_get_by_key( - scope->propagation_context, "trace"), - "parent_span_id"))); + sentry__value_clone( + sentry_value_get_by_key(trace_context, "parent_span_id"))); } + sentry_value_decref(trace_context); } return transaction_context; @@ -965,8 +962,7 @@ sentry__span_iter_headers(sentry_value_t span, sentry__stringbuilder_append(&sb, sentry_value_as_string(trace_id)); SENTRY_WITH_SCOPE (scope) { - sentry_value_foreach_key_value( - scope->dynamic_sampling_context, append_baggage_member, &sb); + sentry__scope_foreach_dsc(scope, append_baggage_member, &sb); } char *baggage = sentry__stringbuilder_into_string(&sb); @@ -1020,14 +1016,8 @@ save_active_trace(void) { saved_trace_t s = { 0 }; SENTRY_WITH_SCOPE (scope) { - if (scope->span) { - sentry__span_incref(scope->span); - s.saved_span = scope->span; - } - if (scope->transaction_object) { - sentry__transaction_incref(scope->transaction_object); - s.saved_tx_obj = scope->transaction_object; - } + s.saved_span = sentry__scope_ref_span(scope); + s.saved_tx_obj = sentry__scope_ref_transaction_object(scope); } s.active_tx = s.saved_span && s.saved_span->transaction ? s.saved_span->transaction @@ -1042,12 +1032,10 @@ static void restore_active_trace(saved_trace_t *s) { SENTRY_WITH_SCOPE_MUT (scope) { - if (!scope->span && s->saved_span) { - scope->span = s->saved_span; + if (sentry__scope_restore_span(scope, s->saved_span)) { s->saved_span = NULL; } - if (!scope->transaction_object && s->saved_tx_obj) { - scope->transaction_object = s->saved_tx_obj; + if (sentry__scope_restore_transaction_object(scope, s->saved_tx_obj)) { s->saved_tx_obj = NULL; } } diff --git a/src/sentry_tracing.h b/src/sentry_tracing.h index 5ba423d170..2c9907794a 100644 --- a/src/sentry_tracing.h +++ b/src/sentry_tracing.h @@ -59,8 +59,8 @@ void sentry__transaction_remove_child( /** * Finishes the active transaction (if any) with `status`, closing out every * in-flight child span in leaf-first order and returning the tx payload. - * `scope->span` / `scope->transaction_object` are preserved so a - * subsequently-captured crash event still inherits the active trace context. + * The scope span / transaction object are preserved so a subsequently-captured + * crash event still inherits the active trace context. * Returns null if nothing is active. */ sentry_value_t sentry__trace_finish(sentry_span_status_t status); diff --git a/tests/unit/test_attachments.c b/tests/unit/test_attachments.c index 8ae9e040d8..bd75f1b1c2 100644 --- a/tests/unit/test_attachments.c +++ b/tests/unit/test_attachments.c @@ -17,7 +17,9 @@ static void add_scope_attachments(sentry_envelope_t *envelope) { SENTRY_WITH_SCOPE (scope) { - sentry__envelope_add_attachments(envelope, scope->attachments, NULL); + sentry_value_t attachments = sentry__scope_load_attachments(scope); + sentry__envelope_add_attachments(envelope, attachments, NULL); + sentry_value_decref(attachments); } } @@ -210,9 +212,13 @@ SENTRY_TEST(attachments_add_remove) sentry_uuid_t scoped_attachment = sentry_scope_attach_bytes(scope, "payload", 7, "file.bin"); TEST_CHECK(!sentry_uuid_is_nil(&scoped_attachment)); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope->attachments), 1); + sentry_value_t scoped_attachments = sentry__scope_load_attachments(scope); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(scoped_attachments), 1); + sentry_value_decref(scoped_attachments); sentry_scope_remove_attachment(scope, scoped_attachment); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope->attachments), 0); + scoped_attachments = sentry__scope_load_attachments(scope); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(scoped_attachments), 0); + sentry_value_decref(scoped_attachments); sentry_scope_remove_attachment(scope, sentry_uuid_nil()); sentry_scope_free(scope); @@ -398,7 +404,9 @@ SENTRY_TEST(attachment_properties) sentry_init(options); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope->attachments), 0); + sentry_value_t attachments = sentry__scope_load_attachments(scope); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(attachments), 0); + sentry_value_decref(attachments); } sentry_value_t invalid = sentry_attachment_from_file(NULL); diff --git a/tests/unit/test_basic.c b/tests/unit/test_basic.c index bcdaed9d20..676fd0e223 100644 --- a/tests/unit/test_basic.c +++ b/tests/unit/test_basic.c @@ -442,8 +442,9 @@ SENTRY_TEST(client_sdk_integrations) sentry_init(options); SENTRY_WITH_SCOPE (scope) { + sentry_value_t client_sdk = sentry__scope_ref_client_sdk(scope); sentry_value_t integrations - = sentry_value_get_by_key(scope->client_sdk, "integrations"); + = sentry_value_get_by_key(client_sdk, "integrations"); size_t integration_count = sentry_value_get_length(integrations); TEST_CHECK(integration_count > 0); TEST_CHECK_STRING_EQUAL( @@ -467,6 +468,7 @@ SENTRY_TEST(client_sdk_integrations) sentry_value_get_by_index(integrations, integration_index)), "qt"); #endif + sentry_value_decref(client_sdk); } sentry_close(); diff --git a/tests/unit/test_logs.c b/tests/unit/test_logs.c index 24710b63cc..b75f72930e 100644 --- a/tests/unit/test_logs.c +++ b/tests/unit/test_logs.c @@ -732,12 +732,13 @@ SENTRY_TEST(scope_capture_log_trace_id) SENTRY_LOG_RETURN_SUCCESS); SENTRY_WITH_SCOPE (global_scope) { + sentry_value_t trace_context + = sentry__scope_load_trace_context(global_scope); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( captured_log, "trace_id")), - sentry_value_as_string(sentry_value_get_by_key( - sentry_value_get_by_key( - global_scope->propagation_context, "trace"), - "trace_id"))); + sentry_value_as_string( + sentry_value_get_by_key(trace_context, "trace_id"))); + sentry_value_decref(trace_context); } sentry_scope_free(scope); diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index 3d171d3c94..9998a9e76e 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -13,6 +13,28 @@ #define TEST_CHECK_UUID_EQUAL(Actual, Expected) \ TEST_CHECK(memcmp(&(Actual), &(Expected), sizeof(sentry_uuid_t)) == 0) +typedef sentry_value_t (*scope_value_getter_t)(const sentry_scope_t *scope); + +static sentry_value_t +scope_value_get_by_key( + scope_value_getter_t get, const sentry_scope_t *scope, const char *key) +{ + sentry_value_t values = get(scope); + sentry_value_t value + = sentry_value_incref(sentry_value_get_by_key(values, key)); + sentry_value_decref(values); + return value; +} + +static size_t +scope_value_get_length(scope_value_getter_t get, const sentry_scope_t *scope) +{ + sentry_value_t value = get(scope); + size_t length = sentry_value_get_length(value); + sentry_value_decref(value); + return length; +} + SENTRY_TEST(scope_contexts) { SENTRY_TEST_OPTIONS_NEW(options); @@ -73,10 +95,14 @@ SENTRY_TEST(scope_contexts) local_scope, "n-removed", sentry_value_new_string("removed")); sentry_scope_remove_context(local_scope, "removed"); sentry_scope_remove_context_n(local_scope, "n-removed-trailing", 9); - TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(local_scope->contexts, "removed"))); - TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(local_scope->contexts, "n-removed"))); + sentry_value_t removed = scope_value_get_by_key( + sentry__scope_load_contexts, local_scope, "removed"); + TEST_CHECK(sentry_value_is_null(removed)); + sentry_value_decref(removed); + removed = scope_value_get_by_key( + sentry__scope_load_contexts, local_scope, "n-removed"); + TEST_CHECK(sentry_value_is_null(removed)); + sentry_value_decref(removed); // event: // {"all":"event","event":"event"} @@ -133,14 +159,15 @@ SENTRY_TEST(scope_update_context) sentry_update_context("device", device); SENTRY_WITH_SCOPE (scope) { - sentry_value_t ctx - = sentry_value_get_by_key(scope->contexts, "device"); + sentry_value_t ctx = scope_value_get_by_key( + sentry__scope_load_contexts, scope, "device"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "model")), "Xbox Series X"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "family")), "Xbox"); + sentry_value_decref(ctx); } } @@ -153,8 +180,8 @@ SENTRY_TEST(scope_update_context) sentry_update_context("device", extra); SENTRY_WITH_SCOPE (scope) { - sentry_value_t ctx - = sentry_value_get_by_key(scope->contexts, "device"); + sentry_value_t ctx = scope_value_get_by_key( + sentry__scope_load_contexts, scope, "device"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "model")), "PC"); @@ -165,6 +192,7 @@ SENTRY_TEST(scope_update_context) sentry_value_as_string( sentry_value_get_by_key(ctx, "cpu_description")), "some cpu"); + sentry_value_decref(ctx); } } @@ -176,11 +204,12 @@ SENTRY_TEST(scope_update_context) sentry_value_set_by_key(os, "name", sentry_value_new_string("SteamOS")); sentry_scope_update_context(local_scope, "os", os); - sentry_value_t ctx - = sentry_value_get_by_key(local_scope->contexts, "os"); + sentry_value_t ctx = scope_value_get_by_key( + sentry__scope_load_contexts, local_scope, "os"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "name")), "SteamOS"); + sentry_value_decref(ctx); // scoped update overwrites existing keys sentry_value_t os2 = sentry_value_new_object(); @@ -188,13 +217,15 @@ SENTRY_TEST(scope_update_context) sentry_value_set_by_key(os2, "version", sentry_value_new_string("6.1")); sentry_scope_update_context(local_scope, "os", os2); - ctx = sentry_value_get_by_key(local_scope->contexts, "os"); + ctx = scope_value_get_by_key( + sentry__scope_load_contexts, local_scope, "os"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "name")), "Linux"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(ctx, "version")), "6.1"); + sentry_value_decref(ctx); sentry_scope_free(local_scope); } @@ -343,10 +374,14 @@ SENTRY_TEST(scope_extra) local_scope, "n-removed", sentry_value_new_string("removed")); sentry_scope_remove_extra(local_scope, "removed"); sentry_scope_remove_extra_n(local_scope, "n-removed-trailing", 9); - TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(local_scope->extra, "removed"))); - TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(local_scope->extra, "n-removed"))); + sentry_value_t removed = scope_value_get_by_key( + sentry__scope_load_extra, local_scope, "removed"); + TEST_CHECK(sentry_value_is_null(removed)); + sentry_value_decref(removed); + removed = scope_value_get_by_key( + sentry__scope_load_extra, local_scope, "n-removed"); + TEST_CHECK(sentry_value_is_null(removed)); + sentry_value_decref(removed); // event: // {"all":"event","event":"event"} @@ -597,10 +632,14 @@ SENTRY_TEST(scope_tags) sentry_scope_set_tag(local_scope, "n-removed", "removed"); sentry_scope_remove_tag(local_scope, "removed"); sentry_scope_remove_tag_n(local_scope, "n-removed-trailing", 9); - TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(local_scope->tags, "removed"))); - TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(local_scope->tags, "n-removed"))); + sentry_value_t removed = scope_value_get_by_key( + sentry__scope_load_tags, local_scope, "removed"); + TEST_CHECK(sentry_value_is_null(removed)); + sentry_value_decref(removed); + removed = scope_value_get_by_key( + sentry__scope_load_tags, local_scope, "n-removed"); + TEST_CHECK(sentry_value_is_null(removed)); + sentry_value_decref(removed); // event: // {"all":"event","event":"event"} @@ -1123,28 +1162,34 @@ SENTRY_TEST(scope_clone) // scope values must not be corrupted by before_send modifications SENTRY_WITH_SCOPE (scope) { sentry_value_t scope_gpu - = sentry_value_get_by_key(scope->contexts, "gpu"); + = scope_value_get_by_key(sentry__scope_load_contexts, scope, "gpu"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(scope_gpu, "name")), "original"); TEST_CHECK(sentry_value_is_null( sentry_value_get_by_key(scope_gpu, "injected"))); + sentry_value_decref(scope_gpu); sentry_value_t scope_data - = sentry_value_get_by_key(scope->extra, "data"); + = scope_value_get_by_key(sentry__scope_load_extra, scope, "data"); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(scope_data, "key")), "original"); TEST_CHECK(sentry_value_is_null( sentry_value_get_by_key(scope_data, "injected"))); + sentry_value_decref(scope_data); + sentry_value_t scope_user = sentry__scope_ref_user(scope); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - scope->user, "username")), + scope_user, "username")), "original"); TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(scope->user, "injected"))); + sentry_value_get_by_key(scope_user, "injected"))); + sentry_value_decref(scope_user); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope->fingerprint), 2); + sentry_value_t scope_fingerprint = sentry__scope_ref_fingerprint(scope); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope_fingerprint), 2); + sentry_value_decref(scope_fingerprint); } sentry_close(); @@ -1161,7 +1206,7 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute("valid_key", valid_attr); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = scope->attributes; + sentry_value_t attributes = sentry__scope_load_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "valid_key"); @@ -1173,6 +1218,7 @@ SENTRY_TEST(scope_global_attributes) TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( retrieved_attr, "value")), "test_value"); + sentry_value_decref(attributes); } // Test that invalid attributes (missing 'value' or 'type') are not set @@ -1183,12 +1229,13 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute("invalid_no_value", invalid_attr_no_value); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = scope->attributes; + sentry_value_t attributes = sentry__scope_load_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "invalid_no_value"); // Check that the attribute was NOT set TEST_CHECK(sentry_value_is_null(retrieved_attr)); + sentry_value_decref(attributes); } // Test invalid attribute missing 'type' @@ -1199,24 +1246,26 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute("invalid_no_type", invalid_attr_no_type); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = scope->attributes; + sentry_value_t attributes = sentry__scope_load_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "invalid_no_type"); // Check that the attribute was NOT set TEST_CHECK(sentry_value_is_null(retrieved_attr)); + sentry_value_decref(attributes); } // Test removing an attribute sentry_remove_attribute("valid_key"); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = scope->attributes; + sentry_value_t attributes = sentry__scope_load_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "valid_key"); // Check that the attribute was removed TEST_CHECK(sentry_value_is_null(retrieved_attr)); + sentry_value_decref(attributes); } // Test setting attribute with _n variant @@ -1225,7 +1274,7 @@ SENTRY_TEST(scope_global_attributes) sentry_set_attribute_n("key_n", 5, attr_n); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = scope->attributes; + sentry_value_t attributes = sentry__scope_load_attributes(scope); sentry_value_t retrieved_attr = sentry_value_get_by_key(attributes, "key_n"); @@ -1240,6 +1289,7 @@ SENTRY_TEST(scope_global_attributes) TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( retrieved_attr, "unit")), "percent"); + sentry_value_decref(attributes); } sentry_close(); @@ -1260,7 +1310,7 @@ SENTRY_TEST(scope_local_attributes) sentry_value_new_attribute(sentry_value_new_string("global"), NULL)); SENTRY_WITH_SCOPE (global_scope) { - sentry_value_t attributes = global_scope->attributes; + sentry_value_t attributes = sentry__scope_load_attributes(global_scope); // Verify global attributes are set TEST_CHECK_STRING_EQUAL( @@ -1275,6 +1325,7 @@ SENTRY_TEST(scope_local_attributes) sentry_value_as_string(sentry_value_get_by_key( sentry_value_get_by_key(attributes, "scope"), "value")), "global"); + sentry_value_decref(attributes); } SENTRY_WITH_SCOPE (global_scope) { @@ -1288,7 +1339,8 @@ SENTRY_TEST(scope_local_attributes) sentry_scope_set_attribute(local_scope, "scope", sentry_value_new_attribute(sentry_value_new_string("local"), NULL)); - sentry_value_t local_attributes = local_scope->attributes; + sentry_value_t local_attributes + = sentry__scope_load_attributes(local_scope); // Verify local attributes are set TEST_CHECK_STRING_EQUAL( @@ -1305,7 +1357,8 @@ SENTRY_TEST(scope_local_attributes) "local"); // Verify global scope still has its own attributes - sentry_value_t global_attributes = global_scope->attributes; + sentry_value_t global_attributes + = sentry__scope_load_attributes(global_scope); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key( sentry_value_get_by_key(global_attributes, "all"), "value")), @@ -1314,6 +1367,8 @@ SENTRY_TEST(scope_local_attributes) sentry_value_as_string(sentry_value_get_by_key( sentry_value_get_by_key(global_attributes, "global"), "value")), "global"); + sentry_value_decref(global_attributes); + sentry_value_decref(local_attributes); sentry_scope_free(local_scope); } @@ -1322,7 +1377,7 @@ SENTRY_TEST(scope_local_attributes) sentry_remove_attribute("all"); SENTRY_WITH_SCOPE (scope) { - sentry_value_t attributes = scope->attributes; + sentry_value_t attributes = sentry__scope_load_attributes(scope); TEST_CHECK( sentry_value_is_null(sentry_value_get_by_key(attributes, "all"))); // Other attributes should still exist @@ -1330,6 +1385,7 @@ SENTRY_TEST(scope_local_attributes) sentry_value_get_by_key(attributes, "global"))); TEST_CHECK(!sentry_value_is_null( sentry_value_get_by_key(attributes, "scope"))); + sentry_value_decref(attributes); } // Test _n variants with local scope @@ -1338,7 +1394,8 @@ SENTRY_TEST(scope_local_attributes) sentry_scope_set_attribute_n(local_scope, "test_key", 8, sentry_value_new_attribute(sentry_value_new_int32(100), "percent")); - sentry_value_t local_attributes = local_scope->attributes; + sentry_value_t local_attributes + = sentry__scope_load_attributes(local_scope); sentry_value_t attr = sentry_value_get_by_key(local_attributes, "test_key"); @@ -1352,10 +1409,14 @@ SENTRY_TEST(scope_local_attributes) sentry_value_as_string(sentry_value_get_by_key(attr, "unit")), "percent"); + sentry_value_decref(local_attributes); + // Remove using _n variant sentry_scope_remove_attribute_n(local_scope, "test_key", 8); + local_attributes = sentry__scope_load_attributes(local_scope); TEST_CHECK(sentry_value_is_null( sentry_value_get_by_key(local_attributes, "test_key"))); + sentry_value_decref(local_attributes); sentry_scope_free(local_scope); } @@ -1370,9 +1431,11 @@ SENTRY_TEST(scope_local_attributes) invalid_attr, "type", sentry_value_new_string("string")); sentry_scope_set_attribute(local_scope, "invalid", invalid_attr); - sentry_value_t local_attributes = local_scope->attributes; + sentry_value_t local_attributes + = sentry__scope_load_attributes(local_scope); TEST_CHECK(sentry_value_is_null( sentry_value_get_by_key(local_attributes, "invalid"))); + sentry_value_decref(local_attributes); sentry_scope_free(local_scope); } @@ -1387,11 +1450,14 @@ SENTRY_TEST(scope_release) SENTRY_WITH_SCOPE_MUT (scope) { sentry_scope_set_release(scope, "my-release"); - TEST_CHECK_STRING_EQUAL(scope->release, "my-release"); + sentry_value_t release = sentry__scope_ref_release(scope); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(release), "my-release"); + sentry_value_decref(release); + sentry_value_t dsc = sentry__scope_load_dsc(scope); TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key( - scope->dynamic_sampling_context, "release")), + sentry_value_as_string(sentry_value_get_by_key(dsc, "release")), "my-release"); + sentry_value_decref(dsc); } sentry_close(); @@ -1404,11 +1470,15 @@ SENTRY_TEST(scope_environment) SENTRY_WITH_SCOPE_MUT (scope) { sentry_scope_set_environment(scope, "my-environment"); - TEST_CHECK_STRING_EQUAL(scope->environment, "my-environment"); + sentry_value_t environment = sentry__scope_ref_environment(scope); TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key( - scope->dynamic_sampling_context, "environment")), + sentry_value_as_string(environment), "my-environment"); + sentry_value_decref(environment); + sentry_value_t dsc = sentry__scope_load_dsc(scope); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(dsc, "environment")), "my-environment"); + sentry_value_decref(dsc); } sentry_close(); @@ -1421,7 +1491,10 @@ SENTRY_TEST(scope_transaction) SENTRY_WITH_SCOPE_MUT (scope) { sentry_scope_set_transaction(scope, "my-transaction"); - TEST_CHECK_STRING_EQUAL(scope->transaction, "my-transaction"); + sentry_value_t transaction = sentry__scope_ref_transaction(scope); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(transaction), "my-transaction"); + sentry_value_decref(transaction); } sentry_close(); @@ -1722,7 +1795,7 @@ SENTRY_TEST(scope_observer_clear) sentry_scope_clear(scope); TEST_CHECK(d.was_cleared); TEST_CHECK_INT_EQUAL(scope->num_observers, 1); - TEST_CHECK(sentry_value_get_length(scope->tags) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_load_tags, scope) == 0); d.was_called = false; sentry_scope_set_tag(scope, "after", "clear"); @@ -1750,7 +1823,7 @@ SENTRY_TEST(scope_observer_clear) TEST_CHECK(observer_data.was_cleared); TEST_CHECK_INT_EQUAL(scope->is_notifying, 0); TEST_CHECK_INT_EQUAL(scope->num_observers, 1); - TEST_CHECK(sentry_value_get_length(scope->tags) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_load_tags, scope) == 0); sentry_value_decref(observer_data.tags); sentry_scope_free(scope); @@ -2478,7 +2551,8 @@ SENTRY_TEST(scope_set_attribute_invalid_decref_value) TEST_CHECK_INT_EQUAL(sentry_value_refcount(no_type), 1); sentry_value_decref(no_type); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope->attributes), 0); + TEST_CHECK_INT_EQUAL( + scope_value_get_length(sentry__scope_load_attributes, scope), 0); sentry_scope_free(scope); } @@ -2495,7 +2569,8 @@ SENTRY_TEST(scope_set_attribute_null_key_decref_value) TEST_CHECK_INT_EQUAL(sentry_value_refcount(v), 1); sentry_value_decref(v); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope->attributes), 0); + TEST_CHECK_INT_EQUAL( + scope_value_get_length(sentry__scope_load_attributes, scope), 0); sentry_scope_free(scope); } @@ -2516,7 +2591,7 @@ SENTRY_TEST(scope_ownership) static size_t scope_breadcrumb_count(const sentry_scope_t *scope) { - sentry_value_t breadcrumbs = sentry__ringbuffer_to_list(scope->breadcrumbs); + sentry_value_t breadcrumbs = sentry__scope_breadcrumbs_to_list(scope); size_t count = sentry_value_get_length(breadcrumbs); sentry_value_decref(breadcrumbs); return count; @@ -2538,10 +2613,11 @@ SENTRY_TEST(scope_clone_independence) TEST_CHECK(!clone->one_shot); // The clone carries over the source's data. - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key(clone->tags, "shared")), - "original"); - TEST_CHECK(clone->level == SENTRY_LEVEL_WARNING); + sentry_value_t clone_tag + = scope_value_get_by_key(sentry__scope_load_tags, clone, "shared"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(clone_tag), "original"); + sentry_value_decref(clone_tag); + TEST_CHECK(sentry__scope_get_level(clone) == SENTRY_LEVEL_WARNING); TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(clone), 1); // Mutating the clone does not affect the source, and vice versa. @@ -2550,11 +2626,14 @@ SENTRY_TEST(scope_clone_independence) sentry_scope_add_breadcrumb( clone, sentry_value_new_breadcrumb(NULL, "second")); - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key(scope->tags, "shared")), - "original"); - TEST_CHECK(sentry_value_is_null( - sentry_value_get_by_key(scope->tags, "clone_only"))); + sentry_value_t scope_tag + = scope_value_get_by_key(sentry__scope_load_tags, scope, "shared"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(scope_tag), "original"); + sentry_value_decref(scope_tag); + sentry_value_t clone_only + = scope_value_get_by_key(sentry__scope_load_tags, scope, "clone_only"); + TEST_CHECK(sentry_value_is_null(clone_only)); + sentry_value_decref(clone_only); TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(scope), 1); TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(clone), 2); @@ -2586,39 +2665,51 @@ SENTRY_TEST(scope_clone_preserves_data) TEST_CHECK(!sentry_uuid_is_nil(&attachment_id)); sentry_scope_t *clone = sentry_scope_clone(scope); - sentry_value_decref( - sentry__attachments_remove(scope->attachments, &attachment_id)); - + sentry_scope_remove_attachment(scope, attachment_id); + + sentry_value_t clone_tag + = scope_value_get_by_key(sentry__scope_load_tags, clone, "tag_key"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(clone_tag), "tag_value"); + sentry_value_decref(clone_tag); + sentry_value_t clone_context + = scope_value_get_by_key(sentry__scope_load_contexts, clone, "device"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(clone_context), "Xbox"); + sentry_value_decref(clone_context); + sentry_value_t clone_user = sentry__scope_ref_user(clone); TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key(clone->tags, "tag_key")), - "tag_value"); - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - clone->contexts, "device")), - "Xbox"); - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - clone->user, "username")), + sentry_value_as_string(sentry_value_get_by_key(clone_user, "username")), "alice"); + sentry_value_decref(clone_user); + sentry_value_t clone_extra + = scope_value_get_by_key(sentry__scope_load_extra, clone, "extra_key"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(clone_extra), "extra_value"); + sentry_value_decref(clone_extra); + sentry_value_t clone_fingerprint = sentry__scope_ref_fingerprint(clone); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(clone_fingerprint), 2); + sentry_value_decref(clone_fingerprint); + TEST_CHECK(sentry__scope_get_level(clone) == SENTRY_LEVEL_WARNING); + sentry_value_t clone_attribute = scope_value_get_by_key( + sentry__scope_load_attributes, clone, "attr_key"); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - clone->extra, "extra_key")), - "extra_value"); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(clone->fingerprint), 2); - TEST_CHECK(clone->level == SENTRY_LEVEL_WARNING); - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key( - sentry_value_get_by_key(clone->attributes, "attr_key"), "value")), + clone_attribute, "value")), "attr_value"); + sentry_value_decref(clone_attribute); TEST_CHECK_INT_EQUAL(scope_breadcrumb_count(clone), 1); // Attachments are deep-copied into an independent list. - TEST_CHECK_INT_EQUAL(sentry_value_get_length(clone->attachments), 1); - TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope->attachments), 0); - TEST_CHECK(clone->attachments._bits != scope->attachments._bits); + sentry_value_t clone_attachments = sentry__scope_load_attachments(clone); + sentry_value_t scope_attachments = sentry__scope_load_attachments(scope); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(clone_attachments), 1); + TEST_CHECK_INT_EQUAL(sentry_value_get_length(scope_attachments), 0); + TEST_CHECK(clone_attachments._bits != scope_attachments._bits); sentry_value_t clone_attachment - = sentry_value_get_by_index(clone->attachments, 0); + = sentry_value_get_by_index(clone_attachments, 0); TEST_CHECK(sentry_value_is_frozen(clone_attachment)); TEST_CHECK_STRING_EQUAL( sentry__attachment_get_filename(clone_attachment), "file.bin"); TEST_CHECK_INT_EQUAL(sentry__attachment_get_size(clone_attachment), 7); + sentry_value_decref(scope_attachments); + sentry_value_decref(clone_attachments); sentry_scope_free(clone); sentry_scope_free(scope); @@ -2640,13 +2731,17 @@ SENTRY_TEST(scope_clone_shares_span) sentry_scope_t *clone = NULL; sentry_transaction_t *scope_txn = NULL; SENTRY_WITH_SCOPE (scope) { - scope_txn = scope->transaction_object; + scope_txn = sentry__scope_ref_transaction_object(scope); clone = sentry_scope_clone(scope); } // The active transaction is shared by reference, not dropped or duplicated. TEST_CHECK(scope_txn != NULL); - TEST_CHECK(clone->transaction_object == scope_txn); + sentry_transaction_t *clone_txn + = sentry__scope_ref_transaction_object(clone); + TEST_CHECK(clone_txn == scope_txn); + sentry__transaction_decref(clone_txn); + sentry__transaction_decref(scope_txn); // The shared reference keeps the transaction alive for the original: the // clone can be freed and the transaction still finished safely. @@ -2665,10 +2760,12 @@ SENTRY_TEST(scope_clear) // Clearing a scope must keep trace propagation data intact, including the // dynamic sampling context. - sentry_value_set_by_key( - scope->propagation_context, "marker", sentry_value_new_string("keep")); - sentry_value_set_by_key(scope->dynamic_sampling_context, "marker", - sentry_value_new_string("keep")); + sentry__scope_set_propagation_context( + scope, "marker", sentry_value_new_string("keep")); + sentry_value_t dsc = sentry_value_new_object(); + sentry_value_set_by_key(dsc, "marker", sentry_value_new_string("keep")); + sentry__scope_freeze_dsc(scope, dsc); + sentry_value_decref(dsc); sentry_scope_set_tag(scope, "tag", "value"); sentry_scope_set_extra(scope, "extra", sentry_value_new_string("value")); @@ -2685,35 +2782,49 @@ SENTRY_TEST(scope_clear) sentry_scope_add_breadcrumb( scope, sentry_value_new_breadcrumb(NULL, "crumb")); - TEST_CHECK(sentry_value_get_length(scope->tags) == 1); - TEST_CHECK(sentry_value_get_length(scope->attributes) == 1); - TEST_CHECK(!sentry_value_is_null(scope->user)); + TEST_CHECK(scope_value_get_length(sentry__scope_load_tags, scope) == 1); + TEST_CHECK( + scope_value_get_length(sentry__scope_load_attributes, scope) == 1); + sentry_value_t scope_user = sentry__scope_ref_user(scope); + TEST_CHECK(!sentry_value_is_null(scope_user)); + sentry_value_decref(scope_user); sentry_scope_clear(scope); // Everything is reset to the state of a fresh scope. - TEST_CHECK(sentry_value_get_length(scope->tags) == 0); - TEST_CHECK(sentry_value_get_length(scope->extra) == 0); - TEST_CHECK(sentry_value_get_length(scope->contexts) == 0); - TEST_CHECK(sentry_value_get_length(scope->attributes) == 0); - TEST_CHECK(sentry_value_is_null(scope->user)); - TEST_CHECK(sentry_value_is_null(scope->fingerprint)); - TEST_CHECK_INT_EQUAL(scope->level, SENTRY_LEVEL_ERROR); - sentry_value_t crumbs = sentry__ringbuffer_to_list(scope->breadcrumbs); + TEST_CHECK(scope_value_get_length(sentry__scope_load_tags, scope) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_load_extra, scope) == 0); + TEST_CHECK(scope_value_get_length(sentry__scope_load_contexts, scope) == 0); + TEST_CHECK( + scope_value_get_length(sentry__scope_load_attributes, scope) == 0); + scope_user = sentry__scope_ref_user(scope); + TEST_CHECK(sentry_value_is_null(scope_user)); + sentry_value_decref(scope_user); + + sentry_value_t scope_fingerprint = sentry__scope_ref_fingerprint(scope); + TEST_CHECK(sentry_value_is_null(scope_fingerprint)); + sentry_value_decref(scope_fingerprint); + TEST_CHECK_INT_EQUAL(sentry__scope_get_level(scope), SENTRY_LEVEL_ERROR); + sentry_value_t crumbs = sentry__scope_breadcrumbs_to_list(scope); TEST_CHECK(sentry_value_get_length(crumbs) == 0); sentry_value_decref(crumbs); // ... except the trace, which is preserved. + sentry_value_t propagation_context + = sentry__scope_load_propagation_context(scope); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - scope->propagation_context, "marker")), + propagation_context, "marker")), "keep"); - TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - scope->dynamic_sampling_context, "marker")), + sentry_value_decref(propagation_context); + sentry_value_t scope_dsc = sentry__scope_load_dsc(scope); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(scope_dsc, "marker")), "keep"); + sentry_value_decref(scope_dsc); // The cleared scope is still usable. sentry_scope_set_tag(scope, "after", "clear"); - TEST_CHECK(sentry_value_get_length(scope->tags) == 1); + TEST_CHECK(scope_value_get_length(sentry__scope_load_tags, scope) == 1); sentry_scope_free(scope); sentry_close(); @@ -2906,9 +3017,10 @@ SENTRY_TEST(scope_capture_user_owned) // The scope was applied but not freed, so reading and reusing it is safe // (a use-after-free here would trip the sanitizers). - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key(scope->tags, "run")), - "first"); + sentry_value_t tag + = scope_value_get_by_key(sentry__scope_load_tags, scope, "run"); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(tag), "first"); + sentry_value_decref(tag); sentry_scope_set_tag(scope, "run", "second"); sentry_scope_capture_event(scope, @@ -2965,8 +3077,8 @@ SENTRY_TEST(scope_bind_transaction_object) // After unbinding, event falls back to the propagation context. TEST_ASSERT(!sentry_value_is_null(trace)); SENTRY_WITH_SCOPE (global_scope) { - sentry_value_t propagation_trace = sentry_value_get_by_key( - global_scope->propagation_context, "trace"); + sentry_value_t propagation_trace + = sentry__scope_load_trace_context(global_scope); TEST_CHECK_STRING_EQUAL( sentry_value_as_string(sentry_value_get_by_key(trace, "trace_id")), sentry_value_as_string( @@ -2975,6 +3087,7 @@ SENTRY_TEST(scope_bind_transaction_object) sentry_value_as_string(sentry_value_get_by_key(trace, "span_id")), sentry_value_as_string( sentry_value_get_by_key(propagation_trace, "span_id"))); + sentry_value_decref(propagation_trace); } sentry_value_decref(trace); @@ -3004,8 +3117,13 @@ SENTRY_TEST(scope_bind_span) // Binding a scope of our own leaves the global scope alone. SENTRY_WITH_SCOPE (global_scope) { - TEST_CHECK(global_scope->span == NULL); - TEST_CHECK(global_scope->transaction_object == NULL); + sentry_span_t *global_span = sentry__scope_ref_span(global_scope); + sentry_transaction_t *global_tx + = sentry__scope_ref_transaction_object(global_scope); + TEST_CHECK(global_span == NULL); + TEST_CHECK(global_tx == NULL); + sentry__span_decref(global_span); + sentry__transaction_decref(global_tx); } sentry_scope_capture_event(scope, @@ -3026,7 +3144,9 @@ SENTRY_TEST(scope_bind_span) // TODO: Finishing a span releases the caller's reference and only clears // the global scope. A user-owned scope still stamps that finished span onto // later events; this acknowledges the current behavior until it changes. - TEST_CHECK_PTR_EQUAL(scope->span, span); + sentry_span_t *bound_span = sentry__scope_ref_span(scope); + TEST_CHECK_PTR_EQUAL(bound_span, span); + sentry__span_decref(bound_span); sentry_value_decref(trace); sentry_scope_free(scope); @@ -3051,17 +3171,30 @@ SENTRY_TEST(scope_bind_span_or_transaction_not_both) sentry_scope_t *scope = sentry_scope_new(); sentry_scope_set_span(scope, span); sentry_scope_set_transaction_object(scope, tx); - TEST_CHECK(scope->span == NULL); - TEST_CHECK_PTR_EQUAL(scope->transaction_object, tx); + sentry_span_t *bound_span = sentry__scope_ref_span(scope); + sentry_transaction_t *bound_tx + = sentry__scope_ref_transaction_object(scope); + TEST_CHECK(bound_span == NULL); + TEST_CHECK_PTR_EQUAL(bound_tx, tx); + sentry__span_decref(bound_span); + sentry__transaction_decref(bound_tx); sentry_scope_set_span(scope, span); - TEST_CHECK(scope->transaction_object == NULL); - TEST_CHECK_PTR_EQUAL(scope->span, span); + bound_tx = sentry__scope_ref_transaction_object(scope); + bound_span = sentry__scope_ref_span(scope); + TEST_CHECK(bound_tx == NULL); + TEST_CHECK_PTR_EQUAL(bound_span, span); + sentry__transaction_decref(bound_tx); + sentry__span_decref(bound_span); // Passing null unbinds both. sentry_scope_set_transaction_object(scope, NULL); - TEST_CHECK(scope->span == NULL); - TEST_CHECK(scope->transaction_object == NULL); + bound_span = sentry__scope_ref_span(scope); + bound_tx = sentry__scope_ref_transaction_object(scope); + TEST_CHECK(bound_span == NULL); + TEST_CHECK(bound_tx == NULL); + sentry__span_decref(bound_span); + sentry__transaction_decref(bound_tx); sentry_scope_free(scope); sentry_span_finish(span); @@ -3090,21 +3223,25 @@ SENTRY_TEST(scope_rebind_same_object) // Rebinding what is already bound must not drop that last reference (a // use-after-free here would trip the sanitizers). - sentry_scope_set_span(scope, scope->span); - TEST_CHECK_PTR_EQUAL(scope->span, span); + sentry_scope_set_span(scope, span); + sentry_span_t *bound_span = sentry__scope_ref_span(scope); + TEST_CHECK_PTR_EQUAL(bound_span, span); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - scope->span->inner, "description")), + bound_span->inner, "description")), "select"); + sentry__span_decref(bound_span); sentry_scope_set_transaction_object(scope, tx); sentry_transaction_finish(tx); - sentry_scope_set_transaction_object(scope, scope->transaction_object); - TEST_CHECK_PTR_EQUAL(scope->transaction_object, tx); - TEST_CHECK_STRING_EQUAL( - sentry_value_as_string(sentry_value_get_by_key( - scope->transaction_object->inner, "transaction")), + sentry_scope_set_transaction_object(scope, tx); + sentry_transaction_t *bound_tx + = sentry__scope_ref_transaction_object(scope); + TEST_CHECK_PTR_EQUAL(bound_tx, tx); + TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( + bound_tx->inner, "transaction")), "txn"); + sentry__transaction_decref(bound_tx); sentry_scope_free(scope); @@ -3126,15 +3263,19 @@ SENTRY_TEST(scope_clone_keeps_bound_span) sentry_scope_t *scope = sentry_scope_new(); sentry_scope_set_span(scope, span); sentry_scope_t *clone = sentry_scope_clone(scope); - TEST_CHECK_PTR_EQUAL(clone->span, span); + sentry_span_t *clone_span = sentry__scope_ref_span(clone); + TEST_CHECK_PTR_EQUAL(clone_span, span); + sentry__span_decref(clone_span); // The clone owns its binding, so it outlives the original scope and caller // reference. sentry_scope_free(scope); sentry_span_finish(span); + clone_span = sentry__scope_ref_span(clone); TEST_CHECK_STRING_EQUAL(sentry_value_as_string(sentry_value_get_by_key( - clone->span->inner, "description")), + clone_span->inner, "description")), "select"); + sentry__span_decref(clone_span); sentry_scope_free(clone); sentry_transaction_finish(tx); diff --git a/tests/unit/test_tracing.c b/tests/unit/test_tracing.c index 9ddb3672ca..55973f1c15 100644 --- a/tests/unit/test_tracing.c +++ b/tests/unit/test_tracing.c @@ -398,6 +398,16 @@ before_transport(sentry_envelope_t *envelope, void *data) sentry_envelope_free(envelope); } +static sentry_value_t +ref_scope_span_or_transaction(void) +{ + sentry_value_t value = sentry_value_new_null(); + SENTRY_WITH_SCOPE (scope) { + value = sentry__scope_ref_span_or_transaction(scope); + } + return value; +} + SENTRY_TEST(multiple_transactions) { uint64_t called_transport = 0; @@ -420,12 +430,14 @@ SENTRY_TEST(multiple_transactions) = sentry_transaction_start(tx_ctx, sentry_value_new_null()); sentry_set_transaction_object(tx); - sentry_value_t scope_tx = sentry__scope_get_span_or_transaction(); + sentry_value_t scope_tx = ref_scope_span_or_transaction(); CHECK_STRING_PROPERTY(scope_tx, "transaction", "wow!"); + sentry_value_decref(scope_tx); sentry_uuid_t event_id = sentry_transaction_finish(tx); - scope_tx = sentry__scope_get_span_or_transaction(); + scope_tx = ref_scope_span_or_transaction(); TEST_CHECK(sentry_value_is_null(scope_tx)); + sentry_value_decref(scope_tx); TEST_CHECK(!sentry_uuid_is_nil(&event_id)); // Set transaction on scope twice, back-to-back without finishing the first @@ -437,8 +449,9 @@ SENTRY_TEST(multiple_transactions) tx_ctx = sentry_transaction_context_new("wowee!", NULL); tx = sentry_transaction_start(tx_ctx, sentry_value_new_null()); sentry_set_transaction_object(tx); - scope_tx = sentry__scope_get_span_or_transaction(); + scope_tx = ref_scope_span_or_transaction(); CHECK_STRING_PROPERTY(scope_tx, "transaction", "wowee!"); + sentry_value_decref(scope_tx); event_id = sentry_transaction_finish(tx); TEST_CHECK(!sentry_uuid_is_nil(&event_id)); @@ -520,20 +533,21 @@ SENTRY_TEST(spans_on_scope) // Peek into the transaction's span list and make sure everything is // good - sentry_value_t scope_tx = sentry__scope_get_span_or_transaction(); - const char *trace_id - = sentry_value_as_string(sentry_value_get_by_key(scope_tx, "trace_id")); - const char *parent_span_id - = sentry_value_as_string(sentry_value_get_by_key(scope_tx, "span_id")); + sentry_value_t scope_tx = ref_scope_span_or_transaction(); + char *trace_id = sentry__string_clone( + sentry_value_as_string(sentry_value_get_by_key(scope_tx, "trace_id"))); + char *parent_span_id = sentry__string_clone( + sentry_value_as_string(sentry_value_get_by_key(scope_tx, "span_id"))); // Don't track the span yet TEST_CHECK(IS_NULL(scope_tx, "spans")); + sentry_value_decref(scope_tx); // Sanity check that child isn't finished yet TEST_CHECK(IS_NULL(child, "timestamp")); sentry_span_finish(opaque_child); - scope_tx = sentry__scope_get_span_or_transaction(); + scope_tx = ref_scope_span_or_transaction(); TEST_CHECK(!IS_NULL(scope_tx, "spans")); sentry_value_t spans = sentry_value_get_by_key(scope_tx, "spans"); TEST_CHECK_INT_EQUAL(sentry_value_get_length(spans), 1); @@ -546,6 +560,9 @@ SENTRY_TEST(spans_on_scope) CHECK_STRING_PROPERTY(stored_child, "description", "goose"); // Should be finished TEST_CHECK(!IS_NULL(stored_child, "timestamp")); + sentry_value_decref(scope_tx); + sentry_free(trace_id); + sentry_free(parent_span_id); sentry__transaction_decref(opaque_tx); @@ -852,7 +869,9 @@ SENTRY_TEST(trace_finish) // Scope still points at the (finished) span so a subsequent crash event // inherits its trace context. SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(scope->span != NULL); + sentry_span_t *scope_span = sentry__scope_ref_span(scope); + TEST_CHECK(scope_span != NULL); + sentry__span_decref(scope_span); } sentry__span_decref(grand); @@ -934,13 +953,19 @@ SENTRY_TEST(discard_transaction) sentry_set_transaction_object(tx); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(scope->transaction_object == tx); + sentry_transaction_t *scope_tx + = sentry__scope_ref_transaction_object(scope); + TEST_CHECK(scope_tx == tx); + sentry__transaction_decref(scope_tx); } sentry_transaction_discard(tx); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(scope->transaction_object == NULL); + sentry_transaction_t *scope_tx + = sentry__scope_ref_transaction_object(scope); + TEST_CHECK(scope_tx == NULL); + sentry__transaction_decref(scope_tx); } sentry_close(); @@ -970,13 +995,17 @@ SENTRY_TEST(discard_span) sentry_set_span(span); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(scope->span == span); + sentry_span_t *scope_span = sentry__scope_ref_span(scope); + TEST_CHECK(scope_span == span); + sentry__span_decref(scope_span); } sentry_span_discard(span); SENTRY_WITH_SCOPE (scope) { - TEST_CHECK(scope->span == NULL); + sentry_span_t *scope_span = sentry__scope_ref_span(scope); + TEST_CHECK(scope_span == NULL); + sentry__span_decref(scope_span); } TEST_CHECK_INT_EQUAL( sentry_value_get_length(sentry_value_get_by_key(tx->inner, "spans")), @@ -1566,7 +1595,7 @@ SENTRY_TEST(set_trace) SENTRY_WITH_SCOPE (scope) { sentry_value_t propagation_trace_context - = sentry_value_get_by_key(scope->propagation_context, "trace"); + = sentry__scope_load_trace_context(scope); TEST_CHECK(!sentry_value_is_null(propagation_trace_context)); CHECK_STRING_PROPERTY(propagation_trace_context, "type", "trace"); @@ -1579,6 +1608,7 @@ SENTRY_TEST(set_trace) sentry_value_get_by_key(propagation_trace_context, "span_id")); TEST_ASSERT(!!span_id); TEST_CHECK(strlen(span_id) > 0); + sentry_value_decref(propagation_trace_context); } sentry_close(); @@ -1597,13 +1627,14 @@ check_trace_omits_parent(const char *parent_span_id, size_t parent_span_id_len) SENTRY_WITH_SCOPE (scope) { sentry_value_t propagation_trace_context - = sentry_value_get_by_key(scope->propagation_context, "trace"); + = sentry__scope_load_trace_context(scope); CHECK_STRING_PROPERTY(propagation_trace_context, "trace_id", trace_id); char *json = sentry_value_to_json(propagation_trace_context); TEST_ASSERT(!!json); TEST_CHECK(strstr(json, "parent_span_id") == NULL); sentry_free(json); + sentry_value_decref(propagation_trace_context); } sentry_close(); @@ -2459,12 +2490,12 @@ SENTRY_TEST(strict_continuation_no_baggage_forks) // Scope propagation follows the fork: no lingering upstream trace_id. SENTRY_WITH_SCOPE (scope) { - const char *scope_trace_id - = sentry_value_as_string(sentry_value_get_by_key( - sentry_value_get_by_key(scope->propagation_context, "trace"), - "trace_id")); + sentry_value_t trace_context = sentry__scope_load_trace_context(scope); + const char *scope_trace_id = sentry_value_as_string( + sentry_value_get_by_key(trace_context, "trace_id")); TEST_CHECK(strcmp(scope_trace_id, UPSTREAM_TRACE_ID) != 0); TEST_CHECK_STRING_EQUAL(scope_trace_id, trace_id); + sentry_value_decref(trace_context); } sentry_transaction_finish(tx); @@ -2509,16 +2540,20 @@ SENTRY_TEST(set_trace_rebuilds_dsc_sample_rand) double init_sample_rand = 0.0; SENTRY_WITH_SCOPE (scope) { - init_sample_rand = sentry_value_as_double(sentry_value_get_by_key( - scope->dynamic_sampling_context, "sample_rand")); + sentry_value_t dsc = sentry__scope_load_dsc(scope); + init_sample_rand = sentry_value_as_double( + sentry_value_get_by_key(dsc, "sample_rand")); + sentry_value_decref(dsc); } sentry_set_trace("11112222333344445555666677778888", "1234567812345678"); double new_sample_rand = -1.0; SENTRY_WITH_SCOPE (scope) { - new_sample_rand = sentry_value_as_double(sentry_value_get_by_key( - scope->dynamic_sampling_context, "sample_rand")); + sentry_value_t dsc = sentry__scope_load_dsc(scope); + new_sample_rand = sentry_value_as_double( + sentry_value_get_by_key(dsc, "sample_rand")); + sentry_value_decref(dsc); } // sample_rand is regenerated for the new trace, so the DSC must reflect // the fresh value, not the init-time one. From 981087c3ce7a70feca8eeab2c91dce5fe4c3eb7b Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 11 Sep 2026 15:05:05 +0200 Subject: [PATCH 2/2] fix(scope): Return independent tracing snapshots Clone the active span or transaction value instead of retaining its inner reference. This lets the returned value outlive finish without bypassing wrapper cleanup. Rename the accessor to load_span_or_transaction and cover both lifetimes with sanitizer regression tests. --- src/sentry_scope.c | 4 ++-- src/sentry_scope.h | 2 +- tests/unit/test_tracing.c | 21 ++++++++++++--------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/sentry_scope.c b/src/sentry_scope.c index bbcba14e1d..a22065be9b 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -1519,9 +1519,9 @@ sentry__scope_ref_span(const sentry_scope_t *scope) } sentry_value_t -sentry__scope_ref_span_or_transaction(const sentry_scope_t *scope) +sentry__scope_load_span_or_transaction(const sentry_scope_t *scope) { - return sentry_value_incref(get_span_or_transaction(scope)); + return sentry__value_clone(get_span_or_transaction(scope)); } void diff --git a/src/sentry_scope.h b/src/sentry_scope.h index 6552da326f..ff44cda6c4 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -178,7 +178,7 @@ bool sentry__scope_restore_transaction_object( sentry_scope_t *scope, sentry_transaction_t *transaction); sentry_span_t *sentry__scope_ref_span(const sentry_scope_t *scope); -sentry_value_t sentry__scope_ref_span_or_transaction( +sentry_value_t sentry__scope_load_span_or_transaction( const sentry_scope_t *scope); void sentry__scope_set_span(sentry_scope_t *scope, sentry_span_t *span); bool sentry__scope_remove_span(sentry_scope_t *scope, sentry_span_t *span); diff --git a/tests/unit/test_tracing.c b/tests/unit/test_tracing.c index 55973f1c15..fced784ed9 100644 --- a/tests/unit/test_tracing.c +++ b/tests/unit/test_tracing.c @@ -399,11 +399,11 @@ before_transport(sentry_envelope_t *envelope, void *data) } static sentry_value_t -ref_scope_span_or_transaction(void) +load_scope_span_or_transaction(void) { sentry_value_t value = sentry_value_new_null(); SENTRY_WITH_SCOPE (scope) { - value = sentry__scope_ref_span_or_transaction(scope); + value = sentry__scope_load_span_or_transaction(scope); } return value; } @@ -430,12 +430,12 @@ SENTRY_TEST(multiple_transactions) = sentry_transaction_start(tx_ctx, sentry_value_new_null()); sentry_set_transaction_object(tx); - sentry_value_t scope_tx = ref_scope_span_or_transaction(); + sentry_value_t scope_tx = load_scope_span_or_transaction(); CHECK_STRING_PROPERTY(scope_tx, "transaction", "wow!"); sentry_value_decref(scope_tx); sentry_uuid_t event_id = sentry_transaction_finish(tx); - scope_tx = ref_scope_span_or_transaction(); + scope_tx = load_scope_span_or_transaction(); TEST_CHECK(sentry_value_is_null(scope_tx)); sentry_value_decref(scope_tx); TEST_CHECK(!sentry_uuid_is_nil(&event_id)); @@ -449,14 +449,14 @@ SENTRY_TEST(multiple_transactions) tx_ctx = sentry_transaction_context_new("wowee!", NULL); tx = sentry_transaction_start(tx_ctx, sentry_value_new_null()); sentry_set_transaction_object(tx); - scope_tx = ref_scope_span_or_transaction(); - CHECK_STRING_PROPERTY(scope_tx, "transaction", "wowee!"); - sentry_value_decref(scope_tx); + scope_tx = load_scope_span_or_transaction(); event_id = sentry_transaction_finish(tx); TEST_CHECK(!sentry_uuid_is_nil(&event_id)); sentry_close(); + CHECK_STRING_PROPERTY(scope_tx, "transaction", "wowee!"); + sentry_value_decref(scope_tx); TEST_CHECK_INT_EQUAL(called_transport, 2); } @@ -533,7 +533,7 @@ SENTRY_TEST(spans_on_scope) // Peek into the transaction's span list and make sure everything is // good - sentry_value_t scope_tx = ref_scope_span_or_transaction(); + sentry_value_t scope_tx = load_scope_span_or_transaction(); char *trace_id = sentry__string_clone( sentry_value_as_string(sentry_value_get_by_key(scope_tx, "trace_id"))); char *parent_span_id = sentry__string_clone( @@ -547,7 +547,7 @@ SENTRY_TEST(spans_on_scope) sentry_span_finish(opaque_child); - scope_tx = ref_scope_span_or_transaction(); + scope_tx = load_scope_span_or_transaction(); TEST_CHECK(!IS_NULL(scope_tx, "spans")); sentry_value_t spans = sentry_value_get_by_key(scope_tx, "spans"); TEST_CHECK_INT_EQUAL(sentry_value_get_length(spans), 1); @@ -993,6 +993,7 @@ SENTRY_TEST(discard_span) = sentry_transaction_start(tx_ctx, sentry_value_new_null()); sentry_span_t *span = sentry_transaction_start_child(tx, "dropped", "span"); sentry_set_span(span); + sentry_value_t value = load_scope_span_or_transaction(); SENTRY_WITH_SCOPE (scope) { sentry_span_t *scope_span = sentry__scope_ref_span(scope); @@ -1015,6 +1016,8 @@ SENTRY_TEST(discard_span) TEST_CHECK(!sentry_uuid_is_nil(&event_id)); sentry_close(); + CHECK_STRING_PROPERTY(value, "description", "span"); + sentry_value_decref(value); TEST_CHECK_INT_EQUAL(called, 1); }