From c228503ee706358328f3fd4b2bed45d142801488 Mon Sep 17 00:00:00 2001 From: Mike Rumpler Date: Mon, 5 Jul 2021 11:51:43 +0200 Subject: [PATCH] added concurrency tests --- src/sentry_core.c | 35 ++++------ tests/unit/CMakeLists.txt | 1 + tests/unit/test_concurrency.c | 116 ++++++++++++++++++++++++++++++++++ tests/unit/test_session.c | 3 + tests/unit/tests.inc | 2 + 5 files changed, 134 insertions(+), 23 deletions(-) create mode 100644 tests/unit/test_concurrency.c diff --git a/src/sentry_core.c b/src/sentry_core.c index 4b099e795..29847dd5d 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -73,18 +73,13 @@ sentry__should_skip_upload(void) int sentry_init(sentry_options_t *options) { - // this function is to be called only once, so we do not allow more than - // one call to be active. If the function is called twice, create an error - // and return fail (1) + // this function is to be called only once, so we do not allow more than one + // caller sentry__mutex_lock(&g_initclose_lock); - // pre-init here, so we can consistently use bailing out to :fail sentry_transport_t *transport = NULL; - if (g_options != NULL) { - SENTRY_ERROR("sentry_init() may only be called once"); - goto fail; - } + sentry_close(); sentry_logger_t logger = { NULL, NULL }; if (options->debug) { @@ -150,9 +145,7 @@ sentry_init(sentry_options_t *options) last_crash = backend->get_last_crash_func(backend); } - sentry__mutex_lock(&g_options_lock); g_options = options; - sentry__mutex_unlock(&g_options_lock); // *after* setting the global options, trigger a scope and consent flush, // since at least crashpad needs that. @@ -194,21 +187,20 @@ sentry_init(sentry_options_t *options) int sentry_close(void) { + // this function is to be called only once, so we do not allow more than one + // caller sentry__mutex_lock(&g_initclose_lock); - if (g_options == NULL) { - SENTRY_DEBUG("sentry_close() called, but options was empty"); - } - // keep the global options until end of function if other calls need the - // data while we still clean up to avoid crashes sentry__mutex_lock(&g_options_lock); sentry_options_t *options = g_options; + if (options) { + sentry_end_session(); + } + g_options = NULL; sentry__mutex_unlock(&g_options_lock); size_t dumped_envelopes = 0; if (options) { - sentry_end_session(); - if (options->backend && options->backend->shutdown_func) { SENTRY_TRACE("shutting down backend"); options->backend->shutdown_func(options->backend); @@ -227,21 +219,18 @@ sentry_close(void) if (!dumped_envelopes && (!options->backend || !options->backend->can_capture_after_shutdown)) { + SENTRY_DEBUGF("# of dumped envelopes is %i", (int)dumped_envelopes); sentry__run_clean(options->run); } - sentry_options_free(options); + } else { + SENTRY_DEBUG("sentry_close() called, but options was empty"); } sentry__scope_cleanup(); sentry_clear_modulecache(); - sentry__mutex_lock(&g_options_lock); - g_options = NULL; - sentry__mutex_unlock(&g_options_lock); - sentry__mutex_unlock(&g_initclose_lock); - return (int)dumped_envelopes; } diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index adecacdfa..91a7e9faf 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -20,6 +20,7 @@ add_executable(sentry_test_unit test_attachments.c test_basic.c test_consent.c + test_concurrency.c test_envelopes.c test_failures.c test_fuzzfailures.c diff --git a/tests/unit/test_concurrency.c b/tests/unit/test_concurrency.c new file mode 100644 index 000000000..db9cb3859 --- /dev/null +++ b/tests/unit/test_concurrency.c @@ -0,0 +1,116 @@ +#include "sentry_core.h" +#include "sentry_testsupport.h" +#include +#include +#include + +static sentry_mutex_t func_lock = SENTRY__MUTEX_INIT; + +static void +send_envelope_test_concurrent(const sentry_envelope_t *envelope, void *data) +{ + sentry__mutex_lock(&func_lock); + uint64_t *called = data; + *called += 1; + + sentry_value_t event = sentry_envelope_get_event(envelope); + if (sentry_value_is_null(event)) { + sentry__mutex_unlock(&func_lock); + return; + } + TEST_CHECK(!sentry_value_is_null(event)); + const char *event_id + = sentry_value_as_string(sentry_value_get_by_key(event, "event_id")); + TEST_CHECK_STRING_EQUAL(event_id, "4c035723-8638-4c3a-923f-2ab9d08b4018"); + + if (*called == 1) { + const char *msg = sentry_value_as_string(sentry_value_get_by_key( + sentry_value_get_by_key(event, "message"), "formatted")); + TEST_CHECK_STRING_EQUAL(msg, "Hello World!"); + const char *release + = sentry_value_as_string(sentry_value_get_by_key(event, "release")); + TEST_CHECK_STRING_EQUAL(release, "prod"); + const char *trans = sentry_value_as_string( + sentry_value_get_by_key(event, "transaction")); + TEST_CHECK_STRING_EQUAL(trans, "demo-trans"); + } + sentry__mutex_unlock(&func_lock); +} + +static void +init_framework(uint64_t *called) +{ + sentry_options_t *options = sentry_options_new(); + sentry_options_set_dsn(options, "https://foo@sentry.invalid/42"); + sentry_options_set_transport(options, + sentry_new_function_transport(send_envelope_test_concurrent, called)); + sentry_options_set_release(options, "prod"); + sentry_options_set_require_user_consent(options, false); + sentry_options_set_auto_session_tracking(options, true); + sentry_options_set_debug(options, true); + sentry_init(options); +} + +SENTRY_TEST(multiple_inits) +{ + uint64_t called = 0; + + init_framework(&called); + init_framework(&called); + + sentry_set_transaction("demo-trans"); + + sentry_capture_event(sentry_value_new_message_event( + SENTRY_LEVEL_INFO, "root", "Hello World!")); + + sentry_value_t obj = sentry_value_new_object(); + // something that is not a uuid, as this will be forcibly changed + sentry_value_set_by_key(obj, "event_id", sentry_value_new_int32(1234)); + sentry_capture_event(obj); + + sentry_close(); + sentry_close(); + + TEST_CHECK_INT_EQUAL(called, 4); +} + +static void * +thread_worker(void *vargp) +{ + sentry_set_transaction("demo-trans"); + + sentry_capture_event(sentry_value_new_message_event( + SENTRY_LEVEL_INFO, "root", "Hello World!")); + + sentry_value_t obj = sentry_value_new_object(); + // something that is not a uuid, as this will be forcibly changed + sentry_value_set_by_key(obj, "event_id", sentry_value_new_int32(1234)); + sentry_capture_event(obj); + + return NULL; +} + +SENTRY_TEST(concurrent_init) +{ + uint64_t called_m = 0; + sentry_threadid_t thread_id1, thread_id2, thread_id3; + + init_framework(&called_m); + + sentry__thread_init(&thread_id1); + sentry__thread_init(&thread_id2); + sentry__thread_init(&thread_id3); + sentry__thread_spawn(&thread_id1, &thread_worker, NULL); + sentry__thread_spawn(&thread_id2, &thread_worker, NULL); + sentry__thread_spawn(&thread_id3, &thread_worker, NULL); + sentry__thread_join(thread_id1); + sentry__thread_join(thread_id2); + sentry__thread_join(thread_id3); + sentry__thread_free(&thread_id1); + sentry__thread_free(&thread_id2); + sentry__thread_free(&thread_id3); + + sentry_close(); + + TEST_CHECK_INT_EQUAL(called_m, 7); +} \ No newline at end of file diff --git a/tests/unit/test_session.c b/tests/unit/test_session.c index e79cc266b..5864526f1 100644 --- a/tests/unit/test_session.c +++ b/tests/unit/test_session.c @@ -93,9 +93,12 @@ send_sampled_envelope(const sentry_envelope_t *envelope, void *data) { session_assertion_t *assertion = data; + SENTRY_DEBUG("send_sampled_envelope"); if (assertion->assert_session) { assertion->called += 1; + SENTRY_DEBUG("assertion + 1"); + TEST_CHECK_INT_EQUAL(sentry__envelope_get_item_count(envelope), 1); const sentry_envelope_item_t *item diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 08908530a..eb1626160 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -5,6 +5,7 @@ XX(basic_http_request_preparation_for_event) XX(basic_http_request_preparation_for_event_with_attachment) XX(basic_http_request_preparation_for_minidump) XX(buildid_fallback) +XX(concurrent_init) XX(count_sampled_events) XX(custom_logger) XX(dsn_parsing_complete) @@ -22,6 +23,7 @@ XX(module_addr) XX(module_finder) XX(mpack_newlines) XX(mpack_removed_tags) +XX(multiple_inits) XX(os) XX(page_allocator) XX(path_basics)