diff --git a/CMakeLists.txt b/CMakeLists.txt index e955145be..0c3d3ffaa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,6 +115,9 @@ option(SENTRY_BUILD_TESTS "Build sentry-native tests" "${SENTRY_MAIN_PROJECT}") option(SENTRY_BUILD_EXAMPLES "Build sentry-native example(s)" "${SENTRY_MAIN_PROJECT}") option(SENTRY_BUILD_BENCHMARKS "Build sentry-native benchmarks" OFF) +option(SENTRY_INTEGRATION_PLATFORM + "Enable the downstream-provided platform integration" OFF) + # Platform version embedding options option(SENTRY_EMBED_INFO "Embed version information in binary" OFF) set(SENTRY_BUILD_PLATFORM "${CMAKE_SYSTEM_NAME}" CACHE STRING "Platform name for embedded version (e.g., switch, playstation, xbox)") @@ -298,6 +301,7 @@ message(STATUS "SENTRY_TRANSPORT=${SENTRY_TRANSPORT}") message(STATUS "SENTRY_BACKEND=${SENTRY_BACKEND}") message(STATUS "SENTRY_LIBRARY_TYPE=${SENTRY_LIBRARY_TYPE}") message(STATUS "SENTRY_SDK_NAME=${SENTRY_SDK_NAME}") +message(STATUS "SENTRY_INTEGRATION_PLATFORM=${SENTRY_INTEGRATION_PLATFORM}") message(STATUS "SENTRY_HANDLER_STACK_SIZE=${SENTRY_HANDLER_STACK_SIZE}") message(STATUS "SENTRY_BATCHER_BUFFER_COUNT=${SENTRY_BATCHER_BUFFER_COUNT}") if (WIN32) @@ -419,6 +423,10 @@ endif() add_subdirectory(src) +if(SENTRY_INTEGRATION_PLATFORM) + target_compile_definitions(sentry PRIVATE SENTRY_INTEGRATION_PLATFORM) +endif() + target_compile_definitions(sentry PRIVATE SENTRY_HANDLER_STACK_SIZE=${SENTRY_HANDLER_STACK_SIZE}) target_compile_definitions(sentry PRIVATE SENTRY_BATCHER_BUFFER_COUNT=${SENTRY_BATCHER_BUFFER_COUNT}) if(WIN32) @@ -1030,6 +1038,9 @@ if(SENTRY_BUILD_TESTS) add_subdirectory(tests/unit) add_subdirectory(tests/fixtures/crash_reporter) add_subdirectory(tests/fixtures/early_init) + if(SENTRY_INTEGRATION_PLATFORM AND SENTRY_MAIN_PROJECT) + add_subdirectory(tests/fixtures/test_platform) + endif() add_subdirectory(tests/fixtures/screenshot) if(WIN32 AND NOT XBOX) add_subdirectory(tests/fixtures/appx) diff --git a/src/sentry_core.c b/src/sentry_core.c index b87e36c59..04b0ffa32 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -117,8 +117,9 @@ unregister_integrations(sentry_scope_t *scope, const sentry_options_t *options) } } -#if defined(SENTRY_PLATFORM_NX) || defined(SENTRY_PLATFORM_PS) \ - || defined(SENTRY_PLATFORM_XBOX) +#if (defined(SENTRY_PLATFORM_NX) || defined(SENTRY_PLATFORM_PS) \ + || defined(SENTRY_PLATFORM_XBOX)) \ + && !defined(SENTRY_INTEGRATION_PLATFORM) int sentry__native_init(sentry_options_t *options) #else @@ -983,11 +984,7 @@ void sentry_set_release_n(const char *release, size_t release_len) { SENTRY_WITH_SCOPE_MUT (scope) { - 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__scope_set_release_n(scope, release, release_len); } } diff --git a/src/sentry_core.h b/src/sentry_core.h index b2ac8ae07..1e45c37e0 100644 --- a/src/sentry_core.h +++ b/src/sentry_core.h @@ -162,8 +162,9 @@ bool sentry__should_send_transaction( sentry_value_t tx_ctx, sentry_sampling_context_t *sampling_ctx); #endif -#if defined(SENTRY_PLATFORM_NX) || defined(SENTRY_PLATFORM_PS) \ - || defined(SENTRY_PLATFORM_XBOX) +#if (defined(SENTRY_PLATFORM_NX) || defined(SENTRY_PLATFORM_PS) \ + || defined(SENTRY_PLATFORM_XBOX)) \ + && !defined(SENTRY_INTEGRATION_PLATFORM) int sentry__native_init(sentry_options_t *options); #endif diff --git a/src/sentry_integration.h b/src/sentry_integration.h index b02e74d9b..2bcfd52f2 100644 --- a/src/sentry_integration.h +++ b/src/sentry_integration.h @@ -14,4 +14,16 @@ typedef struct sentry_integration_s { void (*free_func)(void *data); } sentry_integration_t; +#ifdef SENTRY_INTEGRATION_PLATFORM +# ifdef __cplusplus +extern "C" { +# endif + +sentry_integration_t *sentry_integration_platform_new(void); + +# ifdef __cplusplus +} +# endif +#endif + #endif diff --git a/src/sentry_options.c b/src/sentry_options.c index 1710ca6e1..6da6966ef 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -136,6 +136,13 @@ sentry_options_new(void) #ifdef SENTRY_INTEGRATION_WER sentry__options_add_integration(opts, sentry_integration_wer_new()); #endif +#ifdef SENTRY_INTEGRATION_PLATFORM + if (!sentry__options_add_integration( + opts, sentry_integration_platform_new())) { + sentry_options_free(opts); + return NULL; + } +#endif return opts; } @@ -980,12 +987,12 @@ sentry_options_set_backend(sentry_options_t *opts, sentry_backend_t *backend) opts->backend = backend; } -void +bool sentry__options_add_integration( sentry_options_t *opts, sentry_integration_t *integration) { if (!integration) { - return; + return false; } size_t new_count = opts->num_integrations + 1; @@ -993,7 +1000,7 @@ sentry__options_add_integration( = sentry__calloc(new_count, sizeof(sentry_integration_t *)); if (!integrations) { free_integration(integration); - return; + return false; } for (size_t i = 0; i < opts->num_integrations; i++) { @@ -1003,6 +1010,7 @@ sentry__options_add_integration( sentry_free(opts->integrations); opts->integrations = integrations; opts->num_integrations = new_count; + return true; } bool diff --git a/src/sentry_options.h b/src/sentry_options.h index dcdfeec72..280016a44 100644 --- a/src/sentry_options.h +++ b/src/sentry_options.h @@ -137,8 +137,10 @@ const char *sentry__options_get_org_id(const sentry_options_t *options); * * Takes ownership of `integration`. If the integration owns `data`, it must * provide `free_func`. + * + * Returns true if the integration was added. */ -void sentry__options_add_integration( +bool sentry__options_add_integration( sentry_options_t *opts, sentry_integration_t *integration); /** diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 0bb7057e1..7fe5c2058 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -909,6 +909,24 @@ sentry_scope_update_context_n(sentry_scope_t *scope, const char *key, SENTRY_SCOPE_NOTIFY(scope, set_context, k, value); } +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); +} + +void +sentry__scope_set_release(sentry_scope_t *scope, const char *release) +{ + sentry__scope_set_release_n( + scope, release, sentry__guarded_strlen(release)); +} + void sentry__scope_set_fingerprint_va( sentry_scope_t *scope, const char *fingerprint, va_list va) diff --git a/src/sentry_scope.h b/src/sentry_scope.h index f2133bbda..8cf1a3ebe 100644 --- a/src/sentry_scope.h +++ b/src/sentry_scope.h @@ -145,6 +145,10 @@ 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); +void sentry__scope_set_release(sentry_scope_t *scope, const char *release); +void sentry__scope_set_release_n( + sentry_scope_t *scope, const char *release, size_t release_len); + 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, diff --git a/tests/cmake.py b/tests/cmake.py index 305d784b5..8413b820f 100644 --- a/tests/cmake.py +++ b/tests/cmake.py @@ -106,6 +106,7 @@ def lib_name(name): lib_name("sentry"), exe_name("sentry-crash"), exe_name("sentry_early_init"), + exe_name("sentry_test_platform"), ] cmd = [ os.environ.get("LLVM_COV", "llvm-cov"), diff --git a/tests/fixtures/test_platform/CMakeLists.txt b/tests/fixtures/test_platform/CMakeLists.txt new file mode 100644 index 000000000..89d94d443 --- /dev/null +++ b/tests/fixtures/test_platform/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.10) +project(sentry_test_platform LANGUAGES C) + +target_compile_definitions(sentry PRIVATE + SENTRY_SDK_NAME="sentry.native.test") + +add_executable(sentry_test_platform test_platform.c) +target_include_directories(sentry_test_platform PRIVATE ${SENTRY_SOURCE_DIR}/src) +target_link_libraries(sentry_test_platform PRIVATE sentry) diff --git a/tests/fixtures/test_platform/test_platform.c b/tests/fixtures/test_platform/test_platform.c new file mode 100644 index 000000000..f9da671fc --- /dev/null +++ b/tests/fixtures/test_platform/test_platform.c @@ -0,0 +1,46 @@ +#include "sentry_alloc.h" +#include "sentry_integration.h" +#include "sentry_scope.h" + +static void +register_platform( + void *data, sentry_scope_t *scope, const sentry_options_t *options) +{ + (void)data; + (void)options; + + sentry_value_t device = sentry_value_new_object(); + sentry_value_set_by_key(device, "name", sentry_value_new_string("Test")); + sentry_value_set_by_key( + device, "model", sentry_value_new_string("test-model")); + sentry_value_set_by_key( + device, "arch", sentry_value_new_string("test-arch")); + sentry_scope_set_context(scope, "device", device); +} + +sentry_integration_t * +sentry_integration_platform_new(void) +{ + sentry_integration_t *integration = SENTRY_MAKE(sentry_integration_t); + integration->name = "test"; + integration->register_func = register_platform; + return integration; +} + +int +main(void) +{ + sentry_options_t *options = sentry_options_new(); + sentry_options_set_auto_session_tracking(options, 0); + sentry_options_set_debug(options, true); + sentry_init(options); + + sentry_set_tag("my-tag", "my-value"); + sentry_set_user(sentry_value_new_user("123", "my-user", NULL, NULL)); + + sentry_value_t event = sentry_value_new_message_event( + SENTRY_LEVEL_INFO, "my-logger", "Hello World!"); + sentry_capture_event(event); + + sentry_close(); +} diff --git a/tests/test_integration_platform.py b/tests/test_integration_platform.py new file mode 100644 index 000000000..ae4860e50 --- /dev/null +++ b/tests/test_integration_platform.py @@ -0,0 +1,63 @@ +import os + +import pytest + +from . import Envelope, SENTRY_VERSION, make_dsn, run +from .conditions import has_http + +pytestmark = pytest.mark.skipif(not has_http, reason="tests need http transport") + + +def test_platform_integration(cmake, httpserver): + cwd = cmake( + ["sentry_test_platform"], + { + "SENTRY_BACKEND": "none", + "SENTRY_BUILD_SHARED_LIBS": "OFF", + "SENTRY_INTEGRATION_PLATFORM": "ON", + }, + ) + + httpserver.expect_oneshot_request("/api/123456/envelope/").respond_with_data("OK") + + run( + cwd, + "sentry_test_platform", + [], + env=dict(os.environ, SENTRY_DSN=make_dsn(httpserver)), + ) + + assert len(httpserver.log) == 1 + envelope = Envelope.deserialize(httpserver.log[0][0].get_data()) + + (item,) = envelope.items + assert item.headers["type"] == "event" + event = item.payload.json + + # SDK + assert event["platform"] == "native" + assert event["environment"] == "production" + assert event["event_id"] + assert event["contexts"]["os"]["name"] + assert len(event["contexts"]["trace"]["trace_id"]) == 32 + assert len(event["contexts"]["trace"]["span_id"]) == 16 + assert event["sdk"]["version"] == SENTRY_VERSION + assert event["sdk"]["packages"] == [ + { + "name": "github:getsentry/sentry-native", + "version": SENTRY_VERSION, + } + ] + + # platform integration + assert event["contexts"]["device"] == { + "name": "Test", + "model": "test-model", + "arch": "test-arch", + } + assert event["sdk"]["name"] == "sentry.native.test" + assert event["sdk"]["integrations"].count("test") == 1 + + # app + assert event["tags"] == {"my-tag": "my-value"} + assert event["user"] == {"id": "123", "username": "my-user"} diff --git a/tests/unit/test_basic.c b/tests/unit/test_basic.c index b08f2551a..0c2fd735b 100644 --- a/tests/unit/test_basic.c +++ b/tests/unit/test_basic.c @@ -419,7 +419,7 @@ SENTRY_TEST(client_sdk_integrations) sentry_integration_t *integration = SENTRY_MAKE(sentry_integration_t); TEST_ASSERT(!!integration); integration->name = "custom"; - sentry__options_add_integration(options, integration); + TEST_ASSERT(sentry__options_add_integration(options, integration)); sentry_init(options); diff --git a/tests/unit/test_scope.c b/tests/unit/test_scope.c index e21e79e87..6d99d43bb 100644 --- a/tests/unit/test_scope.c +++ b/tests/unit/test_scope.c @@ -1352,6 +1352,23 @@ SENTRY_TEST(scope_local_attributes) sentry_close(); } +SENTRY_TEST(scope_release) +{ + SENTRY_TEST_OPTIONS_NEW(options); + sentry_init(options); + + SENTRY_WITH_SCOPE_MUT (scope) { + sentry__scope_set_release(scope, "my-release"); + TEST_CHECK_STRING_EQUAL(scope->release, "my-release"); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key( + scope->dynamic_sampling_context, "release")), + "my-release"); + } + + sentry_close(); +} + typedef struct { sentry_value_t release; sentry_value_t environment; diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index f3af596d0..fd41d0dc0 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -371,6 +371,7 @@ XX(scope_observer_user) XX(scope_ownership) XX(scope_propagation_context) XX(scope_rebind_same_object) +XX(scope_release) XX(scope_remove_fingerprint_capture) XX(scope_set_attribute_invalid_decref_value) XX(scope_set_attribute_null_key_decref_value)