diff --git a/CHANGELOG.md b/CHANGELOG.md index 505668476..4d0089175 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ **Features**: +- Windows: report WINE runtime and host metadata in a separate `wine` context. ([#1004](https://github.com/getsentry/sentry-native/issues/1004)) - Native/Windows: capture WER report ID and expose as `contexts.wer.report_id` in crash events when the WER integration is enabled. ([#1970](https://github.com/getsentry/sentry-native/pull/1970)) - Add `sentry_set_tags` and `sentry_scope_set_tags` for updating multiple tags with a single scope flush, improving bulk-update performance. ([#1993](https://github.com/getsentry/sentry-native/pull/1993)) - Add `sentry_get_last_event_id` and `sentry_scope_get_last_event_id` for retrieving the last event ID captured with the global or given scope, respectively. ([#1992](https://github.com/getsentry/sentry-native/pull/1992)) diff --git a/src/sentry_os.c b/src/sentry_os.c index e1bb18cb9..f45300ca3 100644 --- a/src/sentry_os.c +++ b/src/sentry_os.c @@ -178,6 +178,71 @@ sentry__get_windows_version(windows_version_t *win_ver) return 1; } +static void +set_wine_context_string( + sentry_value_t context, const char *key, const char *value) +{ + if (value && value[0]) { + sentry_value_set_by_key(context, key, sentry_value_new_string(value)); + } +} + +sentry_value_t +sentry__make_wine_context(sentry__wine_get_version_t wine_get_version, + sentry__wine_get_build_id_t wine_get_build_id, + sentry__wine_get_host_version_t wine_get_host_version) +{ + if (!wine_get_version) { + return sentry_value_new_null(); + } + + const char *version = wine_get_version(); + if (!version || !version[0]) { + return sentry_value_new_null(); + } + + sentry_value_t context = sentry_value_new_object(); + if (sentry_value_is_null(context)) { + return context; + } + + set_wine_context_string(context, "version", version); + if (wine_get_build_id) { + set_wine_context_string(context, "build", wine_get_build_id()); + } + if (wine_get_host_version) { + const char *sysname = NULL; + const char *release = NULL; + wine_get_host_version(&sysname, &release); + set_wine_context_string(context, "sysname", sysname); + set_wine_context_string(context, "release", release); + } + + sentry_value_freeze(context); + return context; +} + +sentry_value_t +sentry__get_wine_context(void) +{ + const HMODULE ntdll = GetModuleHandleW(L"ntdll.dll"); + if (!ntdll) { + return sentry_value_new_null(); + } + + const sentry__wine_get_version_t wine_get_version + = (sentry__wine_get_version_t)GetProcAddress(ntdll, "wine_get_version"); + const sentry__wine_get_build_id_t wine_get_build_id + = (sentry__wine_get_build_id_t)GetProcAddress( + ntdll, "wine_get_build_id"); + const sentry__wine_get_host_version_t wine_get_host_version + = (sentry__wine_get_host_version_t)GetProcAddress( + ntdll, "wine_get_host_version"); + + return sentry__make_wine_context( + wine_get_version, wine_get_build_id, wine_get_host_version); +} + # endif // !defined(SENTRY_PLATFORM_XBOX) sentry_value_t diff --git a/src/sentry_os.h b/src/sentry_os.h index 41d1b6b14..976e5d8fc 100644 --- a/src/sentry_os.h +++ b/src/sentry_os.h @@ -52,6 +52,20 @@ typedef struct { int sentry__get_kernel_version(windows_version_t *win_ver); int sentry__get_windows_version(windows_version_t *win_ver); + +# if !defined(SENTRY_PLATFORM_XBOX) +typedef const char *(CDECL *sentry__wine_get_version_t)(void); +typedef const char *(CDECL *sentry__wine_get_build_id_t)(void); +typedef void(CDECL *sentry__wine_get_host_version_t)( + const char **sysname, const char **release); + +sentry_value_t sentry__make_wine_context( + sentry__wine_get_version_t wine_get_version, + sentry__wine_get_build_id_t wine_get_build_id, + sentry__wine_get_host_version_t wine_get_host_version); +sentry_value_t sentry__get_wine_context(void); +# endif + void sentry__set_default_thread_stack_guarantee(void); void sentry__init_cached_kernel32_functions(void); void sentry__get_system_time(LPFILETIME filetime); diff --git a/src/sentry_scope.c b/src/sentry_scope.c index 0bb7057e1..35a0cc654 100644 --- a/src/sentry_scope.c +++ b/src/sentry_scope.c @@ -109,6 +109,14 @@ get_scope(void) init_scope(&g_scope); g_scope.user = sentry_value_new_object(); sentry_value_set_by_key(g_scope.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); + } else { + sentry_value_decref(wine_context); + } +#endif g_scope.client_sdk = get_client_sdk(); g_scope_initialized = true; diff --git a/tests/unit/test_os.c b/tests/unit/test_os.c index 5a4d3337c..0db5576d7 100644 --- a/tests/unit/test_os.c +++ b/tests/unit/test_os.c @@ -200,6 +200,33 @@ extern void(WINAPI *g_kernel32_GetCurrentThreadStackLimits)( PULONG_PTR, PULONG_PTR); static size_t g_kernel32_SetThreadStackGuaranteeCalled = 0; +# if !defined(SENTRY_PLATFORM_XBOX) +static const char *CDECL +wine_get_version(void) +{ + return "9.0"; +} + +static const char *CDECL +wine_get_empty_version(void) +{ + return ""; +} + +static const char *CDECL +wine_get_build_id(void) +{ + return "wine-9.0"; +} + +static void CDECL +wine_get_host_version(const char **sysname, const char **release) +{ + *sysname = "Linux"; + *release = "6.8.0"; +} +# endif + static BOOL WINAPI no_previous_guarantee(PULONG guarantee) { @@ -295,6 +322,48 @@ stack_reserve_exact_factor_minus_one(PULONG_PTR low, PULONG_PTR high) } #endif +SENTRY_TEST(wine_context) +{ +#if !defined(SENTRY_PLATFORM_WINDOWS) || defined(SENTRY_PLATFORM_XBOX) + SKIP_TEST(); +#else + sentry_value_t context = sentry__make_wine_context( + wine_get_version, wine_get_build_id, wine_get_host_version); + TEST_CHECK(!sentry_value_is_null(context)); + TEST_CHECK(sentry_value_is_frozen(context)); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(context, "version")), + "9.0"); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(context, "build")), + "wine-9.0"); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(context, "sysname")), + "Linux"); + TEST_CHECK_STRING_EQUAL( + sentry_value_as_string(sentry_value_get_by_key(context, "release")), + "6.8.0"); + sentry_value_decref(context); + + context = sentry__make_wine_context(wine_get_version, NULL, NULL); + TEST_CHECK(!sentry_value_is_null(context)); + TEST_CHECK(sentry_value_is_null(sentry_value_get_by_key(context, "build"))); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(context, "sysname"))); + TEST_CHECK( + sentry_value_is_null(sentry_value_get_by_key(context, "release"))); + sentry_value_decref(context); + + context = sentry__make_wine_context(NULL, NULL, NULL); + TEST_CHECK(sentry_value_is_null(context)); + sentry_value_decref(context); + + context = sentry__make_wine_context(wine_get_empty_version, NULL, NULL); + TEST_CHECK(sentry_value_is_null(context)); + sentry_value_decref(context); +#endif +} + SENTRY_TEST(stack_guarantee) { #if !defined(SENTRY_PLATFORM_WINDOWS) diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index 98a99e31c..615da711f 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -507,6 +507,7 @@ XX(value_uint64) XX(value_unicode) XX(value_user) XX(value_wrong_type) +XX(wine_context) XX(write_envelope_partial_write_fails) XX(write_raw_envelope_to_file) XX(writer_byte_count_stops_after_failure)