From 8bd0e0525ff3fb654d4c6b9d723dd3e19212f2bc Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 19 May 2025 22:22:42 +0200 Subject: [PATCH 01/17] mainthread --- src/Sentry.Unity.Android/SentryJava.cs | 4 +- .../SentryNativeAndroid.cs | 2 +- .../Integrations/UnityScopeIntegration.cs | 76 ++++++++-------- src/Sentry.Unity/ScreenshotEventProcessor.cs | 14 ++- ...nThreadData.cs => SentryMainThreadData.cs} | 88 +++++++++++-------- src/Sentry.Unity/SentryUnitySDK.cs | 2 - src/Sentry.Unity/UnityEventProcessor.cs | 6 +- .../ViewHierarchyEventProcessor.cs | 2 +- test/Sentry.Unity.Tests/ContextWriterTests.cs | 2 +- .../UnityEventScopeTests.cs | 36 ++++---- 10 files changed, 129 insertions(+), 103 deletions(-) rename src/Sentry.Unity/{MainThreadData.cs => SentryMainThreadData.cs} (51%) diff --git a/src/Sentry.Unity.Android/SentryJava.cs b/src/Sentry.Unity.Android/SentryJava.cs index 55378909b..26058d061 100644 --- a/src/Sentry.Unity.Android/SentryJava.cs +++ b/src/Sentry.Unity.Android/SentryJava.cs @@ -432,7 +432,7 @@ public void SetTrace(SentryId traceId, SpanId spanId) internal void HandleJniThreadAttachment(bool? isMainThread = null) { - isMainThread ??= MainThreadData.IsMainThread(); + isMainThread ??= SentryMainThreadData.IsMainThread(); if (isMainThread is false) { _androidJNI.AttachCurrentThread(); @@ -441,7 +441,7 @@ internal void HandleJniThreadAttachment(bool? isMainThread = null) internal void HandleJniThreadDetachment(bool? isMainThread = null) { - isMainThread ??= MainThreadData.IsMainThread(); + isMainThread ??= SentryMainThreadData.IsMainThread(); if (isMainThread is false) { _androidJNI.DetachCurrentThread(); diff --git a/src/Sentry.Unity.Android/SentryNativeAndroid.cs b/src/Sentry.Unity.Android/SentryNativeAndroid.cs index 76ee1f7ae..6f39d8c87 100644 --- a/src/Sentry.Unity.Android/SentryNativeAndroid.cs +++ b/src/Sentry.Unity.Android/SentryNativeAndroid.cs @@ -21,7 +21,7 @@ public static class SentryNativeAndroid /// The Sentry Unity options to use. public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) { - MainThreadData.CollectData(); + SentryMainThreadData.Collect(); options.DiagnosticLogger?.LogInfo("Attempting to configure native support via the Android SDK"); diff --git a/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs b/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs index 23e7b9814..c522309ac 100644 --- a/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs +++ b/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs @@ -69,57 +69,57 @@ private static void PopulateSdk(SdkVersion sdk) private void PopulateApp(App app) { app.Name = _application.ProductName; - app.StartTime = MainThreadData.StartTime; - var isDebugBuild = MainThreadData.IsDebugBuild; + app.StartTime = SentryMainThreadData.StartTime; + var isDebugBuild = SentryMainThreadData.IsDebugBuild; app.BuildType = isDebugBuild is null ? null : (isDebugBuild.Value ? "debug" : "release"); } private void PopulateOperatingSystem(OperatingSystem operatingSystem) { - operatingSystem.RawDescription = MainThreadData.OperatingSystem; + operatingSystem.RawDescription = SentryMainThreadData.OperatingSystem; } private void PopulateDevice(Device device) { - device.ProcessorCount = MainThreadData.ProcessorCount; - device.CpuDescription = MainThreadData.CpuDescription; + device.ProcessorCount = SentryMainThreadData.ProcessorCount; + device.CpuDescription = SentryMainThreadData.CpuDescription; device.Timezone = TimeZoneInfo.Local; - device.SupportsVibration = MainThreadData.SupportsVibration; - device.Name = MainThreadData.DeviceName; + device.SupportsVibration = SentryMainThreadData.SupportsVibration; + device.Name = SentryMainThreadData.DeviceName; // The app can be run in an iOS or Android emulator. We can't safely set a value for simulator. device.Simulator = _application.IsEditor ? true : null; device.DeviceUniqueIdentifier = _options.SendDefaultPii - ? MainThreadData.DeviceUniqueIdentifier + ? SentryMainThreadData.DeviceUniqueIdentifier : null; - device.DeviceType = MainThreadData.DeviceType; - device.Model = MainThreadData.DeviceModel; + device.DeviceType = SentryMainThreadData.DeviceType; + device.Model = SentryMainThreadData.DeviceModel; // This is the approximate amount of system memory in megabytes. // This function is not supported on Windows Store Apps and will always return 0. - if (MainThreadData.SystemMemorySize > 0) + if (SentryMainThreadData.SystemMemorySize > 0) { - device.MemorySize = MainThreadData.SystemMemorySize * 1048576L; // Sentry device mem is in Bytes + device.MemorySize = SentryMainThreadData.SystemMemorySize * 1048576L; // Sentry device mem is in Bytes } } private void PopulateGpu(Gpu gpu) { - gpu.Id = MainThreadData.GraphicsDeviceId; - gpu.Name = MainThreadData.GraphicsDeviceName; - gpu.VendorName = MainThreadData.GraphicsDeviceVendor; - gpu.MemorySize = MainThreadData.GraphicsMemorySize; - gpu.NpotSupport = MainThreadData.NpotSupport; - gpu.Version = MainThreadData.GraphicsDeviceVersion; - gpu.ApiType = MainThreadData.GraphicsDeviceType; - gpu.MaxTextureSize = MainThreadData.MaxTextureSize; - gpu.SupportsDrawCallInstancing = MainThreadData.SupportsDrawCallInstancing; - gpu.SupportsRayTracing = MainThreadData.SupportsRayTracing; - gpu.SupportsComputeShaders = MainThreadData.SupportsComputeShaders; - gpu.SupportsGeometryShaders = MainThreadData.SupportsGeometryShaders; - gpu.VendorId = MainThreadData.GraphicsDeviceVendorId; - gpu.MultiThreadedRendering = MainThreadData.GraphicsMultiThreaded; - gpu.GraphicsShaderLevel = MainThreadData.GraphicsShaderLevel switch + gpu.Id = SentryMainThreadData.GraphicsDeviceId; + gpu.Name = SentryMainThreadData.GraphicsDeviceName; + gpu.VendorName = SentryMainThreadData.GraphicsDeviceVendor; + gpu.MemorySize = SentryMainThreadData.GraphicsMemorySize; + gpu.NpotSupport = SentryMainThreadData.NpotSupport; + gpu.Version = SentryMainThreadData.GraphicsDeviceVersion; + gpu.ApiType = SentryMainThreadData.GraphicsDeviceType; + gpu.MaxTextureSize = SentryMainThreadData.MaxTextureSize; + gpu.SupportsDrawCallInstancing = SentryMainThreadData.SupportsDrawCallInstancing; + gpu.SupportsRayTracing = SentryMainThreadData.SupportsRayTracing; + gpu.SupportsComputeShaders = SentryMainThreadData.SupportsComputeShaders; + gpu.SupportsGeometryShaders = SentryMainThreadData.SupportsGeometryShaders; + gpu.VendorId = SentryMainThreadData.GraphicsDeviceVendorId; + gpu.MultiThreadedRendering = SentryMainThreadData.GraphicsMultiThreaded; + gpu.GraphicsShaderLevel = SentryMainThreadData.GraphicsShaderLevel switch { null => null, -1 => null, @@ -131,39 +131,39 @@ private void PopulateGpu(Gpu gpu) 45 => "Metal / OpenGL ES 3.1", 46 => "OpenGL 4.1", 50 => "Shader Model 5.0", - _ => MainThreadData.GraphicsShaderLevel.ToString() + _ => SentryMainThreadData.GraphicsShaderLevel.ToString() }; } private void PopulateUnity(Protocol.Unity unity) { - unity.EditorVersion = MainThreadData.EditorVersion; - unity.InstallMode = MainThreadData.InstallMode; - unity.TargetFrameRate = MainThreadData.TargetFrameRate; - unity.CopyTextureSupport = MainThreadData.CopyTextureSupport; - unity.RenderingThreadingMode = MainThreadData.RenderingThreadingMode; + unity.EditorVersion = SentryMainThreadData.EditorVersion; + unity.InstallMode = SentryMainThreadData.InstallMode; + unity.TargetFrameRate = SentryMainThreadData.TargetFrameRate; + unity.CopyTextureSupport = SentryMainThreadData.CopyTextureSupport; + unity.RenderingThreadingMode = SentryMainThreadData.RenderingThreadingMode; unity.ActiveSceneName = _sceneManager.GetActiveScene().Name; } private void PopulateTags(Action setTag) { // TODO revisit which tags we should be adding by default - if (MainThreadData.InstallMode is { } installMode) + if (SentryMainThreadData.InstallMode is { } installMode) { setTag("unity.install_mode", installMode); } - if (MainThreadData.SupportsDrawCallInstancing.HasValue) + if (SentryMainThreadData.SupportsDrawCallInstancing.HasValue) { - setTag("unity.gpu.supports_instancing", MainThreadData.SupportsDrawCallInstancing.Value.ToTagValue()); + setTag("unity.gpu.supports_instancing", SentryMainThreadData.SupportsDrawCallInstancing.Value.ToTagValue()); } - if (MainThreadData.DeviceType is { } deviceType) + if (SentryMainThreadData.DeviceType is { } deviceType) { setTag("unity.device.device_type", deviceType); } - if (_options.SendDefaultPii && MainThreadData.DeviceUniqueIdentifier is { } deviceUniqueIdentifier) + if (_options.SendDefaultPii && SentryMainThreadData.DeviceUniqueIdentifier is { } deviceUniqueIdentifier) { setTag("unity.device.unique_identifier", deviceUniqueIdentifier); } diff --git a/src/Sentry.Unity/ScreenshotEventProcessor.cs b/src/Sentry.Unity/ScreenshotEventProcessor.cs index 46ea04c27..a57e429e9 100644 --- a/src/Sentry.Unity/ScreenshotEventProcessor.cs +++ b/src/Sentry.Unity/ScreenshotEventProcessor.cs @@ -1,4 +1,5 @@ using Sentry.Extensibility; +using Sentry.Unity.Integrations; using UnityEngine; namespace Sentry.Unity; @@ -6,10 +7,13 @@ namespace Sentry.Unity; public class ScreenshotEventProcessor : ISentryEventProcessorWithHint { private readonly SentryUnityOptions _options; + private readonly IApplication _application; + public ScreenshotEventProcessor(SentryUnityOptions sentryOptions) : this(sentryOptions, null) { } - public ScreenshotEventProcessor(SentryUnityOptions sentryOptions) + internal ScreenshotEventProcessor(SentryUnityOptions sentryOptions, IApplication? application) { _options = sentryOptions; + _application = application ?? ApplicationAdapter.Instance; } public SentryEvent? Process(SentryEvent @event) @@ -19,13 +23,19 @@ public ScreenshotEventProcessor(SentryUnityOptions sentryOptions) public SentryEvent? Process(SentryEvent @event, SentryHint hint) { - if (!MainThreadData.IsMainThread()) + if (SentryMainThreadData.IsMainThread() is not true) { return @event; } if (_options.BeforeCaptureScreenshotInternal?.Invoke() is not false) { + if (_application.IsEditor) + { + _options.DiagnosticLogger?.LogInfo("Screenshot attachment skipped. Capturing screenshots it not supported in the Editor"); + return @event; + } + if (Screen.width == 0 || Screen.height == 0) { _options.DiagnosticLogger?.LogWarning("Can't capture screenshots on a screen with a resolution of '{0}x{1}'.", Screen.width, Screen.height); diff --git a/src/Sentry.Unity/MainThreadData.cs b/src/Sentry.Unity/SentryMainThreadData.cs similarity index 51% rename from src/Sentry.Unity/MainThreadData.cs rename to src/Sentry.Unity/SentryMainThreadData.cs index 69a9451eb..fd397ee1b 100644 --- a/src/Sentry.Unity/MainThreadData.cs +++ b/src/Sentry.Unity/SentryMainThreadData.cs @@ -1,82 +1,96 @@ using System; using System.Threading; -using UnityEngine; namespace Sentry.Unity; -internal static class MainThreadData +public static class SentryMainThreadData { internal static int? MainThreadId { get; set; } - public static string? OperatingSystem { get; set; } + internal static string? OperatingSystem { get; set; } - public static int? ProcessorCount { get; set; } + internal static int? ProcessorCount { get; set; } - public static bool? SupportsVibration { get; set; } + internal static bool? SupportsVibration { get; set; } - public static string? DeviceType { get; set; } + internal static string? DeviceType { get; set; } - public static string? CpuDescription { get; set; } + internal static string? CpuDescription { get; set; } - public static string? DeviceName { get; set; } + internal static string? DeviceName { get; set; } - public static string? DeviceUniqueIdentifier { get; set; } + internal static string? DeviceUniqueIdentifier { get; set; } - public static string? DeviceModel { get; set; } + internal static string? DeviceModel { get; set; } - public static int? SystemMemorySize { get; set; } + internal static int? SystemMemorySize { get; set; } - public static int? GraphicsDeviceId { get; set; } + internal static int? GraphicsDeviceId { get; set; } - public static string? GraphicsDeviceName { get; set; } + internal static string? GraphicsDeviceName { get; set; } - public static string? GraphicsDeviceVendorId { get; set; } + internal static string? GraphicsDeviceVendorId { get; set; } - public static string? GraphicsDeviceVendor { get; set; } + internal static string? GraphicsDeviceVendor { get; set; } - public static int? GraphicsMemorySize { get; set; } + internal static int? GraphicsMemorySize { get; set; } - public static bool? GraphicsMultiThreaded { get; set; } + internal static bool? GraphicsMultiThreaded { get; set; } - public static string? NpotSupport { get; set; } + internal static string? NpotSupport { get; set; } - public static string? GraphicsDeviceVersion { get; set; } + internal static string? GraphicsDeviceVersion { get; set; } - public static string? GraphicsDeviceType { get; set; } + internal static string? GraphicsDeviceType { get; set; } - public static int? MaxTextureSize { get; set; } + internal static int? MaxTextureSize { get; set; } - public static bool? SupportsDrawCallInstancing { get; set; } + internal static bool? SupportsDrawCallInstancing { get; set; } - public static bool? SupportsRayTracing { get; set; } + internal static bool? SupportsRayTracing { get; set; } - public static bool? SupportsComputeShaders { get; set; } + internal static bool? SupportsComputeShaders { get; set; } - public static bool? SupportsGeometryShaders { get; set; } + internal static bool? SupportsGeometryShaders { get; set; } - public static int? GraphicsShaderLevel { get; set; } + internal static int? GraphicsShaderLevel { get; set; } - public static bool? IsDebugBuild { get; set; } + internal static bool? IsDebugBuild { get; set; } - public static string? EditorVersion { get; set; } - public static string? InstallMode { get; set; } + internal static string? EditorVersion { get; set; } + internal static string? InstallMode { get; set; } - public static string? TargetFrameRate { get; set; } + internal static string? TargetFrameRate { get; set; } - public static string? CopyTextureSupport { get; set; } + internal static string? CopyTextureSupport { get; set; } - public static string? RenderingThreadingMode { get; set; } + internal static string? RenderingThreadingMode { get; set; } - public static DateTimeOffset? StartTime { get; set; } + internal static DateTimeOffset? StartTime { get; set; } - public static bool IsMainThread() - => MainThreadId.HasValue && Thread.CurrentThread.ManagedThreadId == MainThreadId; + internal static bool Collected = false; + + public static bool? IsMainThread() + { + if (MainThreadId.HasValue) + { + return MainThreadId.Equals(Thread.CurrentThread.ManagedThreadId); + } + + // We don't know whether this is the main thread or not + return null; + } // For testing internal static ISentrySystemInfo? SentrySystemInfo { get; set; } - public static void CollectData() + public static void Collect() { + if (Collected) + { + return; + } + var sentrySystemInfo = SentrySystemInfo ?? SentrySystemInfoAdapter.Instance; MainThreadId = sentrySystemInfo.MainThreadId; @@ -111,5 +125,7 @@ public static void CollectData() CopyTextureSupport = sentrySystemInfo.CopyTextureSupport?.Value; RenderingThreadingMode = sentrySystemInfo.RenderingThreadingMode?.Value; StartTime = sentrySystemInfo.StartTime?.Value; + + Collected = true; } } diff --git a/src/Sentry.Unity/SentryUnitySDK.cs b/src/Sentry.Unity/SentryUnitySDK.cs index 029e34194..9724351da 100644 --- a/src/Sentry.Unity/SentryUnitySDK.cs +++ b/src/Sentry.Unity/SentryUnitySDK.cs @@ -28,8 +28,6 @@ private SentryUnitySdk(SentryUnityOptions options) return null; } - MainThreadData.CollectData(); - // On Standalone, we disable cache dir in case multiple app instances run over the same path. // Note: we cannot use a named Mutex, because Unit doesn't support it. Instead, we create a file with `FileShare.None`. // https://forum.unity.com/threads/unsupported-internal-call-for-il2cpp-mutex-createmutex_internal-named-mutexes-are-not-supported.387334/ diff --git a/src/Sentry.Unity/UnityEventProcessor.cs b/src/Sentry.Unity/UnityEventProcessor.cs index bcb525d62..78412e8f0 100644 --- a/src/Sentry.Unity/UnityEventProcessor.cs +++ b/src/Sentry.Unity/UnityEventProcessor.cs @@ -41,7 +41,7 @@ private void SetEventContext(IEventLike sentryEvent) // that it got added last or that there was not an integration added at a later point PopulateSdkIntegrations(sentryEvent.Sdk); // TODO revisit which tags we should be adding by default - sentryEvent.SetTag("unity.is_main_thread", MainThreadData.IsMainThread().ToTagValue()); + sentryEvent.SetTag("unity.is_main_thread", SentryMainThreadData.IsMainThread().ToTagValue()); } catch (Exception exception) { @@ -51,7 +51,7 @@ private void SetEventContext(IEventLike sentryEvent) private void PopulateDevice(Device device) { - if (!MainThreadData.IsMainThread()) + if (SentryMainThreadData.IsMainThread() is not true) { return; } @@ -93,4 +93,6 @@ private void PopulateSdkIntegrations(SdkVersion sdkVersion) internal static class TagValueNormalizer { internal static string ToTagValue(this bool value) => value ? "true" : "false"; + + internal static string ToTagValue(this bool? value) => value.HasValue ? value.ToTagValue() : "unknown"; } diff --git a/src/Sentry.Unity/ViewHierarchyEventProcessor.cs b/src/Sentry.Unity/ViewHierarchyEventProcessor.cs index 88424f185..993527342 100644 --- a/src/Sentry.Unity/ViewHierarchyEventProcessor.cs +++ b/src/Sentry.Unity/ViewHierarchyEventProcessor.cs @@ -24,7 +24,7 @@ public ViewHierarchyEventProcessor(SentryUnityOptions sentryOptions) public SentryEvent? Process(SentryEvent @event, SentryHint hint) { - if (!MainThreadData.IsMainThread()) + if (SentryMainThreadData.IsMainThread() is not true) { _options.DiagnosticLogger?.LogDebug("Can't capture view hierarchy on other than main (UI) thread."); return @event; diff --git a/test/Sentry.Unity.Tests/ContextWriterTests.cs b/test/Sentry.Unity.Tests/ContextWriterTests.cs index ca9d40c11..5ae2baa08 100644 --- a/test/Sentry.Unity.Tests/ContextWriterTests.cs +++ b/test/Sentry.Unity.Tests/ContextWriterTests.cs @@ -78,7 +78,7 @@ public void Arguments() }; // act - MainThreadData.SentrySystemInfo = sysInfo; + SentryMainThreadData.SentrySystemInfo = sysInfo; SentryUnity.Init(options); Assert.IsTrue(context.SyncFinished.WaitOne(TimeSpan.FromSeconds(10))); diff --git a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs index 5be314652..55844eab5 100644 --- a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs +++ b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs @@ -120,8 +120,8 @@ public void SentrySdkCaptureEvent(bool captureOnUiThread) }; // In an actual build, the collection is automatically triggered before the SDK initializes - MainThreadData.SentrySystemInfo = systemInfo; - MainThreadData.CollectData(); + SentryMainThreadData.SentrySystemInfo = systemInfo; + SentryMainThreadData.Collect(); SentryUnity.Init(options); @@ -236,11 +236,11 @@ public void EventDeviceSimulator_SetCorrectly(bool isEditor, bool? isSimulator) public void DeviceUniqueIdentifierWithSendDefaultPii_IsNotNull() { // arrange - MainThreadData.SentrySystemInfo = new TestSentrySystemInfo + SentryMainThreadData.SentrySystemInfo = new TestSentrySystemInfo { DeviceUniqueIdentifier = new Lazy(() => "83fdd6d4-50b1-4735-a4d1-d4f7de64aff0") }; - MainThreadData.CollectData(); + SentryMainThreadData.Collect(); var sentryOptions = new SentryUnityOptions { SendDefaultPii = true }; var sut = new UnityScopeUpdater(sentryOptions, _testApplication); @@ -329,8 +329,8 @@ public void Tags_Set() DeviceUniqueIdentifier = new(() => "f810306c-68db-4ebe-89ba-13c457449339"), InstallMode = ApplicationInstallMode.Store.ToString() }; - MainThreadData.SentrySystemInfo = systemInfo; - MainThreadData.CollectData(); + SentryMainThreadData.SentrySystemInfo = systemInfo; + SentryMainThreadData.Collect(); var sentryOptions = new SentryUnityOptions { SendDefaultPii = true }; var scopeUpdater = new UnityScopeUpdater(sentryOptions, _testApplication); @@ -382,8 +382,8 @@ public void OperatingSystemProtocol_Assigned() { // arrange var systemInfo = new TestSentrySystemInfo { OperatingSystem = "Windows" }; - MainThreadData.SentrySystemInfo = systemInfo; - MainThreadData.CollectData(); + SentryMainThreadData.SentrySystemInfo = systemInfo; + SentryMainThreadData.Collect(); var sut = new UnityScopeUpdater(_sentryOptions, _testApplication); var scope = new Scope(_sentryOptions); @@ -409,8 +409,8 @@ public void DeviceProtocol_Assigned() DeviceModel = new Lazy(() => "Samsung Galaxy S3"), SystemMemorySize = 16000 }; - MainThreadData.SentrySystemInfo = systemInfo; - MainThreadData.CollectData(); + SentryMainThreadData.SentrySystemInfo = systemInfo; + SentryMainThreadData.Collect(); var sut = new UnityScopeUpdater(_sentryOptions, _testApplication); var scope = new Scope(_sentryOptions); @@ -441,8 +441,8 @@ public void UnityProtocol_Assigned() CopyTextureSupport = new Lazy(() => "Basic, Copy3D, DifferentTypes, TextureToRT, RTToTexture"), RenderingThreadingMode = new Lazy(() => "MultiThreaded") }; - MainThreadData.SentrySystemInfo = systemInfo; - MainThreadData.CollectData(); + SentryMainThreadData.SentrySystemInfo = systemInfo; + SentryMainThreadData.Collect(); var sut = new UnityScopeUpdater(_sentryOptions, _testApplication, sceneManager); var scope = new Scope(_sentryOptions); @@ -482,8 +482,8 @@ public void GpuProtocol_Assigned() SupportsComputeShaders = true, SupportsGeometryShaders = true }; - MainThreadData.SentrySystemInfo = systemInfo; - MainThreadData.CollectData(); + SentryMainThreadData.SentrySystemInfo = systemInfo; + SentryMainThreadData.Collect(); var sut = new UnityScopeUpdater(_sentryOptions, _testApplication); var scope = new Scope(_sentryOptions); @@ -514,8 +514,8 @@ public void GpuProtocolGraphicsShaderLevel_Assigned( [ValueSource(nameof(ShaderLevels))] (int, string) shaderValue) { var (shaderLevel, shaderDescription) = shaderValue; - MainThreadData.SentrySystemInfo = new TestSentrySystemInfo { GraphicsShaderLevel = shaderLevel }; - MainThreadData.CollectData(); + SentryMainThreadData.SentrySystemInfo = new TestSentrySystemInfo { GraphicsShaderLevel = shaderLevel }; + SentryMainThreadData.Collect(); var sut = new UnityScopeUpdater(_sentryOptions, _testApplication); var scope = new Scope(_sentryOptions); @@ -552,8 +552,8 @@ public void GpuProtocolGraphicsShaderLevelMinusOne_Ignored() var scope = new Scope(_sentryOptions); // act - MainThreadData.SentrySystemInfo = systemInfo; - MainThreadData.CollectData(); + SentryMainThreadData.SentrySystemInfo = systemInfo; + SentryMainThreadData.Collect(); sut.ConfigureScope(scope); // assert From 35fc6800aec6a8c6c797d0f8862d23ccea2961e3 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 19 May 2025 22:22:57 +0200 Subject: [PATCH 02/17] collecting right at the beginning --- package-dev/Runtime/SentryInitialization.cs | 25 ++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/package-dev/Runtime/SentryInitialization.cs b/package-dev/Runtime/SentryInitialization.cs index 918cc2165..bdbe7cbb4 100644 --- a/package-dev/Runtime/SentryInitialization.cs +++ b/package-dev/Runtime/SentryInitialization.cs @@ -15,6 +15,7 @@ #endif using System; +using System.Threading; using Sentry.Extensibility; #if UNITY_2020_3_OR_NEWER using System.Buffers; @@ -55,6 +56,7 @@ public static class SentryInitialization #endif public static void Init() { + SentryMainThreadData.Collect(); var unityInfo = new SentryUnityInfo(); // Loading the options invokes the ScriptableOption`Configure` callback. Users can disable the SDK via code. var options = ScriptableSentryUnityOptions.LoadSentryUnityOptions(unityInfo); @@ -92,7 +94,7 @@ private static void SetupNativeSdk(SentryUnityOptions options, SentryUnityInfo u #elif SENTRY_NATIVE SentryNative.Configure(options, unityInfo); #elif SENTRY_WEBGL - SentryWebGL.Configure(options); + SentryWebGL.Configure(options); #endif } catch (DllNotFoundException e) @@ -130,6 +132,27 @@ private static void SetupStartupTracing(SentryUnityOptions options) public class SentryUnityInfo : ISentryUnityInfo { + public SentryUnityInfo() + { + // We're only ever going to create an instance of `SentryUnityInfo` when initializing via + // `RuntimeInitializeOnLoadMethod`. This guarantees, that this is running on the main thread. + MainThreadId = Thread.CurrentThread.ManagedThreadId; + } + + private static int? MainThreadId; + + public bool? IsMainThread() + { + if (MainThreadId.HasValue) + { + return MainThreadId.Equals(Thread.CurrentThread.ManagedThreadId); + } + + // Fallback so we don't make assumptinos. We don't know whether this is the main thread or not. Operations + // such as detaching from the JNI on the main thread would cause a crash. + return null; + } + public bool IL2CPP { get => From 17357992a7ca8c22bffa485f56f0f1d948e62be4 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 19 May 2025 22:39:55 +0200 Subject: [PATCH 03/17] revert changes to info --- package-dev/Runtime/SentryInitialization.cs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/package-dev/Runtime/SentryInitialization.cs b/package-dev/Runtime/SentryInitialization.cs index bdbe7cbb4..0d022828d 100644 --- a/package-dev/Runtime/SentryInitialization.cs +++ b/package-dev/Runtime/SentryInitialization.cs @@ -132,27 +132,6 @@ private static void SetupStartupTracing(SentryUnityOptions options) public class SentryUnityInfo : ISentryUnityInfo { - public SentryUnityInfo() - { - // We're only ever going to create an instance of `SentryUnityInfo` when initializing via - // `RuntimeInitializeOnLoadMethod`. This guarantees, that this is running on the main thread. - MainThreadId = Thread.CurrentThread.ManagedThreadId; - } - - private static int? MainThreadId; - - public bool? IsMainThread() - { - if (MainThreadId.HasValue) - { - return MainThreadId.Equals(Thread.CurrentThread.ManagedThreadId); - } - - // Fallback so we don't make assumptinos. We don't know whether this is the main thread or not. Operations - // such as detaching from the JNI on the main thread would cause a crash. - return null; - } - public bool IL2CPP { get => From a230aefbeacd11904252e5909f8d7917b18c4d05 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 19 May 2025 22:40:28 +0200 Subject: [PATCH 04/17] . --- package-dev/Runtime/SentryInitialization.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/package-dev/Runtime/SentryInitialization.cs b/package-dev/Runtime/SentryInitialization.cs index 0d022828d..39ceb436b 100644 --- a/package-dev/Runtime/SentryInitialization.cs +++ b/package-dev/Runtime/SentryInitialization.cs @@ -15,7 +15,6 @@ #endif using System; -using System.Threading; using Sentry.Extensibility; #if UNITY_2020_3_OR_NEWER using System.Buffers; From e4c76996d75e69f2df31d970e9639804672f49cf Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 19 May 2025 22:41:13 +0200 Subject: [PATCH 05/17] collect only once --- src/Sentry.Unity.Android/SentryNativeAndroid.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Sentry.Unity.Android/SentryNativeAndroid.cs b/src/Sentry.Unity.Android/SentryNativeAndroid.cs index 6f39d8c87..c0566227d 100644 --- a/src/Sentry.Unity.Android/SentryNativeAndroid.cs +++ b/src/Sentry.Unity.Android/SentryNativeAndroid.cs @@ -21,8 +21,6 @@ public static class SentryNativeAndroid /// The Sentry Unity options to use. public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) { - SentryMainThreadData.Collect(); - options.DiagnosticLogger?.LogInfo("Attempting to configure native support via the Android SDK"); if (!options.AndroidNativeSupportEnabled) From 3097916121338d731d7a1b3bea35127abded84f8 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 19 May 2025 23:47:17 +0200 Subject: [PATCH 06/17] recursions are bad --- src/Sentry.Unity/UnityEventProcessor.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Sentry.Unity/UnityEventProcessor.cs b/src/Sentry.Unity/UnityEventProcessor.cs index 78412e8f0..cc739de15 100644 --- a/src/Sentry.Unity/UnityEventProcessor.cs +++ b/src/Sentry.Unity/UnityEventProcessor.cs @@ -94,5 +94,13 @@ internal static class TagValueNormalizer { internal static string ToTagValue(this bool value) => value ? "true" : "false"; - internal static string ToTagValue(this bool? value) => value.HasValue ? value.ToTagValue() : "unknown"; + internal static string ToTagValue(this bool? value) + { + if (value.HasValue) + { + return value.Value ? "true" : "false"; + } + + return "unknown"; + } } From b1809a69e35cdeaf0c5cd0486d124fe7f05d2b78 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 19 May 2025 23:47:42 +0200 Subject: [PATCH 07/17] collect --- src/Sentry.Unity/SentryMainThreadData.cs | 2 +- test/Sentry.Unity.Tests/UnityEventScopeTests.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Sentry.Unity/SentryMainThreadData.cs b/src/Sentry.Unity/SentryMainThreadData.cs index fd397ee1b..f2e1089cc 100644 --- a/src/Sentry.Unity/SentryMainThreadData.cs +++ b/src/Sentry.Unity/SentryMainThreadData.cs @@ -88,7 +88,7 @@ public static void Collect() { if (Collected) { - return; + // return; } var sentrySystemInfo = SentrySystemInfo ?? SentrySystemInfoAdapter.Instance; diff --git a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs index 55844eab5..2f0827e78 100644 --- a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs +++ b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs @@ -262,6 +262,7 @@ public void AppProtocol_Assigned() _testApplication = new TestApplication(productName: "TestGame"); var sut = new UnityScopeUpdater(_sentryOptions, _testApplication); var scope = new Scope(_sentryOptions); + SentryMainThreadData.Collect(); // act sut.ConfigureScope(scope); From 3fae73b3da8e8b2a0a90dacc7952667ee01c9722 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 19 May 2025 23:48:04 +0200 Subject: [PATCH 08/17] collect only if running --- package-dev/Runtime/SentryInitialization.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package-dev/Runtime/SentryInitialization.cs b/package-dev/Runtime/SentryInitialization.cs index 39ceb436b..436fd506b 100644 --- a/package-dev/Runtime/SentryInitialization.cs +++ b/package-dev/Runtime/SentryInitialization.cs @@ -55,12 +55,13 @@ public static class SentryInitialization #endif public static void Init() { - SentryMainThreadData.Collect(); var unityInfo = new SentryUnityInfo(); // Loading the options invokes the ScriptableOption`Configure` callback. Users can disable the SDK via code. var options = ScriptableSentryUnityOptions.LoadSentryUnityOptions(unityInfo); if (options != null && options.ShouldInitializeSdk()) { + SentryMainThreadData.Collect(); + // Certain integrations require access to preprocessor directives so we provide them as `.cs` and // compile them with the game instead of precompiling them with the rest of the SDK. // i.e. SceneManagerAPI requires UNITY_2020_3_OR_NEWER From 5780727457178b35f0e4234c91040380f2e5682b Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 20 May 2025 12:24:56 +0200 Subject: [PATCH 09/17] updated tests --- test/Sentry.Unity.Tests/ContextWriterTests.cs | 6 ++++-- test/Sentry.Unity.Tests/IntegrationTests.cs | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/Sentry.Unity.Tests/ContextWriterTests.cs b/test/Sentry.Unity.Tests/ContextWriterTests.cs index 5ae2baa08..ec6a2009f 100644 --- a/test/Sentry.Unity.Tests/ContextWriterTests.cs +++ b/test/Sentry.Unity.Tests/ContextWriterTests.cs @@ -77,12 +77,14 @@ public void Arguments() NativeContextWriter = context, }; + // In an actual build, it's getting collected before initialization via the RuntimeInitializeOnLoadMethod + SentryMainThreadData.Collect(); + // act - SentryMainThreadData.SentrySystemInfo = sysInfo; SentryUnity.Init(options); - Assert.IsTrue(context.SyncFinished.WaitOne(TimeSpan.FromSeconds(10))); // assert + Assert.IsTrue(context.SyncFinished.WaitOne(TimeSpan.FromSeconds(10))); Assert.AreEqual(sysInfo.StartTime?.Value.ToString("o"), context.AppStartTime); Assert.AreEqual("debug", context.AppBuildType); Assert.AreEqual(sysInfo.OperatingSystem, context.OperatingSystemRawDescription); diff --git a/test/Sentry.Unity.Tests/IntegrationTests.cs b/test/Sentry.Unity.Tests/IntegrationTests.cs index 10e7e4924..35354a750 100644 --- a/test/Sentry.Unity.Tests/IntegrationTests.cs +++ b/test/Sentry.Unity.Tests/IntegrationTests.cs @@ -15,6 +15,7 @@ namespace Sentry.Unity.Tests; public sealed class IntegrationTests { + private TestSentrySystemInfo? _testSystemInfo = null; private TestHttpClientHandler _testHttpClientHandler = null!; // Set in Setup private readonly TimeSpan _eventReceiveTimeout = TimeSpan.FromSeconds(1); @@ -338,6 +339,10 @@ internal static IEnumerator SetupSceneCoroutine(string sceneName, [CallerMemberN internal IDisposable InitSentrySdk(Action? configure = null) { + // In an actual build, it's getting collected before initialization via the RuntimeInitializeOnLoadMethod + SentryMainThreadData.SentrySystemInfo = _testSystemInfo; + SentryMainThreadData.Collect(); + SentryUnity.Init(options => { options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; From 7946207c498c6f9219becae23d14fa222134f7cd Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 20 May 2025 13:54:24 +0200 Subject: [PATCH 10/17] collect mainthread ID only once --- src/Sentry.Unity/SentryMainThreadData.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Sentry.Unity/SentryMainThreadData.cs b/src/Sentry.Unity/SentryMainThreadData.cs index f2e1089cc..948908d91 100644 --- a/src/Sentry.Unity/SentryMainThreadData.cs +++ b/src/Sentry.Unity/SentryMainThreadData.cs @@ -68,7 +68,7 @@ public static class SentryMainThreadData internal static DateTimeOffset? StartTime { get; set; } - internal static bool Collected = false; + internal static bool MainThreadICollected = false; public static bool? IsMainThread() { @@ -86,14 +86,14 @@ public static class SentryMainThreadData public static void Collect() { - if (Collected) + var sentrySystemInfo = SentrySystemInfo ?? SentrySystemInfoAdapter.Instance; + + if (!MainThreadICollected) { - // return; + MainThreadId = sentrySystemInfo.MainThreadId; + MainThreadICollected = true; } - var sentrySystemInfo = SentrySystemInfo ?? SentrySystemInfoAdapter.Instance; - - MainThreadId = sentrySystemInfo.MainThreadId; ProcessorCount = sentrySystemInfo.ProcessorCount; OperatingSystem = sentrySystemInfo.OperatingSystem; CpuDescription = sentrySystemInfo.CpuDescription; @@ -125,7 +125,5 @@ public static void Collect() CopyTextureSupport = sentrySystemInfo.CopyTextureSupport?.Value; RenderingThreadingMode = sentrySystemInfo.RenderingThreadingMode?.Value; StartTime = sentrySystemInfo.StartTime?.Value; - - Collected = true; } } From 570c83d34a7829a8fb1707b7fb9817bcf347796d Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 20 May 2025 13:54:37 +0200 Subject: [PATCH 11/17] fixed context tests --- test/Sentry.Unity.Tests/ContextWriterTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Sentry.Unity.Tests/ContextWriterTests.cs b/test/Sentry.Unity.Tests/ContextWriterTests.cs index ec6a2009f..554d08f70 100644 --- a/test/Sentry.Unity.Tests/ContextWriterTests.cs +++ b/test/Sentry.Unity.Tests/ContextWriterTests.cs @@ -78,6 +78,7 @@ public void Arguments() }; // In an actual build, it's getting collected before initialization via the RuntimeInitializeOnLoadMethod + SentryMainThreadData.SentrySystemInfo = sysInfo; SentryMainThreadData.Collect(); // act From a491feaa23d12bbccb53ecacc0677c3a2961e7f0 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 20 May 2025 14:56:16 +0200 Subject: [PATCH 12/17] added log from task to bugfarm start --- samples/unity-of-bugs/Assets/Scripts/BugFarmButtons.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/samples/unity-of-bugs/Assets/Scripts/BugFarmButtons.cs b/samples/unity-of-bugs/Assets/Scripts/BugFarmButtons.cs index 674fc54c3..24e2abd3e 100644 --- a/samples/unity-of-bugs/Assets/Scripts/BugFarmButtons.cs +++ b/samples/unity-of-bugs/Assets/Scripts/BugFarmButtons.cs @@ -1,6 +1,8 @@ using System; using System.Globalization; using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; using Sentry; using UnityEngine; using UnityEngine.Assertions; @@ -15,7 +17,9 @@ private void Awake() private void Start() { Debug.Log("Sample Start 🦋"); - Debug.LogWarning("Here come the bugs 🐞🦋🐛🐜🕷!"); + Debug.LogWarning("Here come the bugs 🐞🦋🐛🐜!"); + + Task.Run(() => { Debug.Log("The spider snuck up from a task! 🕷"); }); } public void AssertFalse() => Assert.AreEqual(true, false); From 13d8f43186b25743ea101db860fe040fee303164 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 20 May 2025 16:31:32 +0200 Subject: [PATCH 13/17] collect --- src/Sentry.Unity/SentryMainThreadData.cs | 9 ++------- src/Sentry.Unity/SentryUnitySDK.cs | 2 ++ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Sentry.Unity/SentryMainThreadData.cs b/src/Sentry.Unity/SentryMainThreadData.cs index 948908d91..5447f72b0 100644 --- a/src/Sentry.Unity/SentryMainThreadData.cs +++ b/src/Sentry.Unity/SentryMainThreadData.cs @@ -68,8 +68,6 @@ public static class SentryMainThreadData internal static DateTimeOffset? StartTime { get; set; } - internal static bool MainThreadICollected = false; - public static bool? IsMainThread() { if (MainThreadId.HasValue) @@ -88,11 +86,8 @@ public static void Collect() { var sentrySystemInfo = SentrySystemInfo ?? SentrySystemInfoAdapter.Instance; - if (!MainThreadICollected) - { - MainThreadId = sentrySystemInfo.MainThreadId; - MainThreadICollected = true; - } + // Don't overwrite the MainThreadId if it's already been set + MainThreadId ??= sentrySystemInfo.MainThreadId; ProcessorCount = sentrySystemInfo.ProcessorCount; OperatingSystem = sentrySystemInfo.OperatingSystem; diff --git a/src/Sentry.Unity/SentryUnitySDK.cs b/src/Sentry.Unity/SentryUnitySDK.cs index 9724351da..02b6e5153 100644 --- a/src/Sentry.Unity/SentryUnitySDK.cs +++ b/src/Sentry.Unity/SentryUnitySDK.cs @@ -47,6 +47,8 @@ private SentryUnitySdk(SentryUnityOptions options) } } + SentryMainThreadData.Collect(); + unitySdk._dotnetSdk = SentrySdk.Init(options); if (options.NativeContextWriter is { } contextWriter) From 9a35b067d463fc2025c6c741cfc66889e7ba306b Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 20 May 2025 17:13:33 +0200 Subject: [PATCH 14/17] keep MainThreadData internal --- src/Sentry.Unity.Android/SentryJava.cs | 4 +- .../SentryNativeAndroid.cs | 2 + .../Integrations/UnityScopeIntegration.cs | 76 +++++++++---------- ...tryMainThreadData.cs => MainThreadData.cs} | 4 +- src/Sentry.Unity/ScreenshotEventProcessor.cs | 2 +- src/Sentry.Unity/SentryUnitySDK.cs | 2 +- src/Sentry.Unity/UnityEventProcessor.cs | 4 +- .../ViewHierarchyEventProcessor.cs | 2 +- test/Sentry.Unity.Tests/ContextWriterTests.cs | 7 +- test/Sentry.Unity.Tests/IntegrationTests.cs | 5 -- .../UnityEventScopeTests.cs | 38 +++++----- 11 files changed, 70 insertions(+), 76 deletions(-) rename src/Sentry.Unity/{SentryMainThreadData.cs => MainThreadData.cs} (98%) diff --git a/src/Sentry.Unity.Android/SentryJava.cs b/src/Sentry.Unity.Android/SentryJava.cs index 26058d061..55378909b 100644 --- a/src/Sentry.Unity.Android/SentryJava.cs +++ b/src/Sentry.Unity.Android/SentryJava.cs @@ -432,7 +432,7 @@ public void SetTrace(SentryId traceId, SpanId spanId) internal void HandleJniThreadAttachment(bool? isMainThread = null) { - isMainThread ??= SentryMainThreadData.IsMainThread(); + isMainThread ??= MainThreadData.IsMainThread(); if (isMainThread is false) { _androidJNI.AttachCurrentThread(); @@ -441,7 +441,7 @@ internal void HandleJniThreadAttachment(bool? isMainThread = null) internal void HandleJniThreadDetachment(bool? isMainThread = null) { - isMainThread ??= SentryMainThreadData.IsMainThread(); + isMainThread ??= MainThreadData.IsMainThread(); if (isMainThread is false) { _androidJNI.DetachCurrentThread(); diff --git a/src/Sentry.Unity.Android/SentryNativeAndroid.cs b/src/Sentry.Unity.Android/SentryNativeAndroid.cs index c0566227d..76ee1f7ae 100644 --- a/src/Sentry.Unity.Android/SentryNativeAndroid.cs +++ b/src/Sentry.Unity.Android/SentryNativeAndroid.cs @@ -21,6 +21,8 @@ public static class SentryNativeAndroid /// The Sentry Unity options to use. public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) { + MainThreadData.CollectData(); + options.DiagnosticLogger?.LogInfo("Attempting to configure native support via the Android SDK"); if (!options.AndroidNativeSupportEnabled) diff --git a/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs b/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs index c522309ac..23e7b9814 100644 --- a/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs +++ b/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs @@ -69,57 +69,57 @@ private static void PopulateSdk(SdkVersion sdk) private void PopulateApp(App app) { app.Name = _application.ProductName; - app.StartTime = SentryMainThreadData.StartTime; - var isDebugBuild = SentryMainThreadData.IsDebugBuild; + app.StartTime = MainThreadData.StartTime; + var isDebugBuild = MainThreadData.IsDebugBuild; app.BuildType = isDebugBuild is null ? null : (isDebugBuild.Value ? "debug" : "release"); } private void PopulateOperatingSystem(OperatingSystem operatingSystem) { - operatingSystem.RawDescription = SentryMainThreadData.OperatingSystem; + operatingSystem.RawDescription = MainThreadData.OperatingSystem; } private void PopulateDevice(Device device) { - device.ProcessorCount = SentryMainThreadData.ProcessorCount; - device.CpuDescription = SentryMainThreadData.CpuDescription; + device.ProcessorCount = MainThreadData.ProcessorCount; + device.CpuDescription = MainThreadData.CpuDescription; device.Timezone = TimeZoneInfo.Local; - device.SupportsVibration = SentryMainThreadData.SupportsVibration; - device.Name = SentryMainThreadData.DeviceName; + device.SupportsVibration = MainThreadData.SupportsVibration; + device.Name = MainThreadData.DeviceName; // The app can be run in an iOS or Android emulator. We can't safely set a value for simulator. device.Simulator = _application.IsEditor ? true : null; device.DeviceUniqueIdentifier = _options.SendDefaultPii - ? SentryMainThreadData.DeviceUniqueIdentifier + ? MainThreadData.DeviceUniqueIdentifier : null; - device.DeviceType = SentryMainThreadData.DeviceType; - device.Model = SentryMainThreadData.DeviceModel; + device.DeviceType = MainThreadData.DeviceType; + device.Model = MainThreadData.DeviceModel; // This is the approximate amount of system memory in megabytes. // This function is not supported on Windows Store Apps and will always return 0. - if (SentryMainThreadData.SystemMemorySize > 0) + if (MainThreadData.SystemMemorySize > 0) { - device.MemorySize = SentryMainThreadData.SystemMemorySize * 1048576L; // Sentry device mem is in Bytes + device.MemorySize = MainThreadData.SystemMemorySize * 1048576L; // Sentry device mem is in Bytes } } private void PopulateGpu(Gpu gpu) { - gpu.Id = SentryMainThreadData.GraphicsDeviceId; - gpu.Name = SentryMainThreadData.GraphicsDeviceName; - gpu.VendorName = SentryMainThreadData.GraphicsDeviceVendor; - gpu.MemorySize = SentryMainThreadData.GraphicsMemorySize; - gpu.NpotSupport = SentryMainThreadData.NpotSupport; - gpu.Version = SentryMainThreadData.GraphicsDeviceVersion; - gpu.ApiType = SentryMainThreadData.GraphicsDeviceType; - gpu.MaxTextureSize = SentryMainThreadData.MaxTextureSize; - gpu.SupportsDrawCallInstancing = SentryMainThreadData.SupportsDrawCallInstancing; - gpu.SupportsRayTracing = SentryMainThreadData.SupportsRayTracing; - gpu.SupportsComputeShaders = SentryMainThreadData.SupportsComputeShaders; - gpu.SupportsGeometryShaders = SentryMainThreadData.SupportsGeometryShaders; - gpu.VendorId = SentryMainThreadData.GraphicsDeviceVendorId; - gpu.MultiThreadedRendering = SentryMainThreadData.GraphicsMultiThreaded; - gpu.GraphicsShaderLevel = SentryMainThreadData.GraphicsShaderLevel switch + gpu.Id = MainThreadData.GraphicsDeviceId; + gpu.Name = MainThreadData.GraphicsDeviceName; + gpu.VendorName = MainThreadData.GraphicsDeviceVendor; + gpu.MemorySize = MainThreadData.GraphicsMemorySize; + gpu.NpotSupport = MainThreadData.NpotSupport; + gpu.Version = MainThreadData.GraphicsDeviceVersion; + gpu.ApiType = MainThreadData.GraphicsDeviceType; + gpu.MaxTextureSize = MainThreadData.MaxTextureSize; + gpu.SupportsDrawCallInstancing = MainThreadData.SupportsDrawCallInstancing; + gpu.SupportsRayTracing = MainThreadData.SupportsRayTracing; + gpu.SupportsComputeShaders = MainThreadData.SupportsComputeShaders; + gpu.SupportsGeometryShaders = MainThreadData.SupportsGeometryShaders; + gpu.VendorId = MainThreadData.GraphicsDeviceVendorId; + gpu.MultiThreadedRendering = MainThreadData.GraphicsMultiThreaded; + gpu.GraphicsShaderLevel = MainThreadData.GraphicsShaderLevel switch { null => null, -1 => null, @@ -131,39 +131,39 @@ private void PopulateGpu(Gpu gpu) 45 => "Metal / OpenGL ES 3.1", 46 => "OpenGL 4.1", 50 => "Shader Model 5.0", - _ => SentryMainThreadData.GraphicsShaderLevel.ToString() + _ => MainThreadData.GraphicsShaderLevel.ToString() }; } private void PopulateUnity(Protocol.Unity unity) { - unity.EditorVersion = SentryMainThreadData.EditorVersion; - unity.InstallMode = SentryMainThreadData.InstallMode; - unity.TargetFrameRate = SentryMainThreadData.TargetFrameRate; - unity.CopyTextureSupport = SentryMainThreadData.CopyTextureSupport; - unity.RenderingThreadingMode = SentryMainThreadData.RenderingThreadingMode; + unity.EditorVersion = MainThreadData.EditorVersion; + unity.InstallMode = MainThreadData.InstallMode; + unity.TargetFrameRate = MainThreadData.TargetFrameRate; + unity.CopyTextureSupport = MainThreadData.CopyTextureSupport; + unity.RenderingThreadingMode = MainThreadData.RenderingThreadingMode; unity.ActiveSceneName = _sceneManager.GetActiveScene().Name; } private void PopulateTags(Action setTag) { // TODO revisit which tags we should be adding by default - if (SentryMainThreadData.InstallMode is { } installMode) + if (MainThreadData.InstallMode is { } installMode) { setTag("unity.install_mode", installMode); } - if (SentryMainThreadData.SupportsDrawCallInstancing.HasValue) + if (MainThreadData.SupportsDrawCallInstancing.HasValue) { - setTag("unity.gpu.supports_instancing", SentryMainThreadData.SupportsDrawCallInstancing.Value.ToTagValue()); + setTag("unity.gpu.supports_instancing", MainThreadData.SupportsDrawCallInstancing.Value.ToTagValue()); } - if (SentryMainThreadData.DeviceType is { } deviceType) + if (MainThreadData.DeviceType is { } deviceType) { setTag("unity.device.device_type", deviceType); } - if (_options.SendDefaultPii && SentryMainThreadData.DeviceUniqueIdentifier is { } deviceUniqueIdentifier) + if (_options.SendDefaultPii && MainThreadData.DeviceUniqueIdentifier is { } deviceUniqueIdentifier) { setTag("unity.device.unique_identifier", deviceUniqueIdentifier); } diff --git a/src/Sentry.Unity/SentryMainThreadData.cs b/src/Sentry.Unity/MainThreadData.cs similarity index 98% rename from src/Sentry.Unity/SentryMainThreadData.cs rename to src/Sentry.Unity/MainThreadData.cs index 5447f72b0..64b0ba05e 100644 --- a/src/Sentry.Unity/SentryMainThreadData.cs +++ b/src/Sentry.Unity/MainThreadData.cs @@ -3,7 +3,7 @@ namespace Sentry.Unity; -public static class SentryMainThreadData +internal static class MainThreadData { internal static int? MainThreadId { get; set; } @@ -82,7 +82,7 @@ public static class SentryMainThreadData // For testing internal static ISentrySystemInfo? SentrySystemInfo { get; set; } - public static void Collect() + public static void CollectData() { var sentrySystemInfo = SentrySystemInfo ?? SentrySystemInfoAdapter.Instance; diff --git a/src/Sentry.Unity/ScreenshotEventProcessor.cs b/src/Sentry.Unity/ScreenshotEventProcessor.cs index 0c01f627b..9f26d66bf 100644 --- a/src/Sentry.Unity/ScreenshotEventProcessor.cs +++ b/src/Sentry.Unity/ScreenshotEventProcessor.cs @@ -23,7 +23,7 @@ internal ScreenshotEventProcessor(SentryUnityOptions sentryOptions, IApplication public SentryEvent? Process(SentryEvent @event, SentryHint hint) { - if (SentryMainThreadData.IsMainThread() is not true) + if (MainThreadData.IsMainThread() is not true) { _options.DiagnosticLogger?.LogDebug("Screenshot capture skipped. Can't capture screenshots on other than the main thread."); return @event; diff --git a/src/Sentry.Unity/SentryUnitySDK.cs b/src/Sentry.Unity/SentryUnitySDK.cs index 02b6e5153..481756359 100644 --- a/src/Sentry.Unity/SentryUnitySDK.cs +++ b/src/Sentry.Unity/SentryUnitySDK.cs @@ -47,7 +47,7 @@ private SentryUnitySdk(SentryUnityOptions options) } } - SentryMainThreadData.Collect(); + MainThreadData.CollectData(); unitySdk._dotnetSdk = SentrySdk.Init(options); diff --git a/src/Sentry.Unity/UnityEventProcessor.cs b/src/Sentry.Unity/UnityEventProcessor.cs index cc739de15..b7938feab 100644 --- a/src/Sentry.Unity/UnityEventProcessor.cs +++ b/src/Sentry.Unity/UnityEventProcessor.cs @@ -41,7 +41,7 @@ private void SetEventContext(IEventLike sentryEvent) // that it got added last or that there was not an integration added at a later point PopulateSdkIntegrations(sentryEvent.Sdk); // TODO revisit which tags we should be adding by default - sentryEvent.SetTag("unity.is_main_thread", SentryMainThreadData.IsMainThread().ToTagValue()); + sentryEvent.SetTag("unity.is_main_thread", MainThreadData.IsMainThread().ToTagValue()); } catch (Exception exception) { @@ -51,7 +51,7 @@ private void SetEventContext(IEventLike sentryEvent) private void PopulateDevice(Device device) { - if (SentryMainThreadData.IsMainThread() is not true) + if (MainThreadData.IsMainThread() is not true) { return; } diff --git a/src/Sentry.Unity/ViewHierarchyEventProcessor.cs b/src/Sentry.Unity/ViewHierarchyEventProcessor.cs index c89efeefe..3efe01ace 100644 --- a/src/Sentry.Unity/ViewHierarchyEventProcessor.cs +++ b/src/Sentry.Unity/ViewHierarchyEventProcessor.cs @@ -24,7 +24,7 @@ public ViewHierarchyEventProcessor(SentryUnityOptions sentryOptions) public SentryEvent? Process(SentryEvent @event, SentryHint hint) { - if (SentryMainThreadData.IsMainThread() is not true) + if (MainThreadData.IsMainThread() is not true) { _options.DiagnosticLogger?.LogDebug("Hierarchy capture skipped. Can't capture hierarchy on other than the main thread."); return @event; diff --git a/test/Sentry.Unity.Tests/ContextWriterTests.cs b/test/Sentry.Unity.Tests/ContextWriterTests.cs index 554d08f70..ca9d40c11 100644 --- a/test/Sentry.Unity.Tests/ContextWriterTests.cs +++ b/test/Sentry.Unity.Tests/ContextWriterTests.cs @@ -77,15 +77,12 @@ public void Arguments() NativeContextWriter = context, }; - // In an actual build, it's getting collected before initialization via the RuntimeInitializeOnLoadMethod - SentryMainThreadData.SentrySystemInfo = sysInfo; - SentryMainThreadData.Collect(); - // act + MainThreadData.SentrySystemInfo = sysInfo; SentryUnity.Init(options); + Assert.IsTrue(context.SyncFinished.WaitOne(TimeSpan.FromSeconds(10))); // assert - Assert.IsTrue(context.SyncFinished.WaitOne(TimeSpan.FromSeconds(10))); Assert.AreEqual(sysInfo.StartTime?.Value.ToString("o"), context.AppStartTime); Assert.AreEqual("debug", context.AppBuildType); Assert.AreEqual(sysInfo.OperatingSystem, context.OperatingSystemRawDescription); diff --git a/test/Sentry.Unity.Tests/IntegrationTests.cs b/test/Sentry.Unity.Tests/IntegrationTests.cs index 35354a750..10e7e4924 100644 --- a/test/Sentry.Unity.Tests/IntegrationTests.cs +++ b/test/Sentry.Unity.Tests/IntegrationTests.cs @@ -15,7 +15,6 @@ namespace Sentry.Unity.Tests; public sealed class IntegrationTests { - private TestSentrySystemInfo? _testSystemInfo = null; private TestHttpClientHandler _testHttpClientHandler = null!; // Set in Setup private readonly TimeSpan _eventReceiveTimeout = TimeSpan.FromSeconds(1); @@ -339,10 +338,6 @@ internal static IEnumerator SetupSceneCoroutine(string sceneName, [CallerMemberN internal IDisposable InitSentrySdk(Action? configure = null) { - // In an actual build, it's getting collected before initialization via the RuntimeInitializeOnLoadMethod - SentryMainThreadData.SentrySystemInfo = _testSystemInfo; - SentryMainThreadData.Collect(); - SentryUnity.Init(options => { options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; diff --git a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs index 2f0827e78..d7a0a7ee8 100644 --- a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs +++ b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs @@ -120,8 +120,8 @@ public void SentrySdkCaptureEvent(bool captureOnUiThread) }; // In an actual build, the collection is automatically triggered before the SDK initializes - SentryMainThreadData.SentrySystemInfo = systemInfo; - SentryMainThreadData.Collect(); + MainThreadData.SentrySystemInfo = systemInfo; + MainThreadData.CollectData(); SentryUnity.Init(options); @@ -236,11 +236,11 @@ public void EventDeviceSimulator_SetCorrectly(bool isEditor, bool? isSimulator) public void DeviceUniqueIdentifierWithSendDefaultPii_IsNotNull() { // arrange - SentryMainThreadData.SentrySystemInfo = new TestSentrySystemInfo + MainThreadData.SentrySystemInfo = new TestSentrySystemInfo { DeviceUniqueIdentifier = new Lazy(() => "83fdd6d4-50b1-4735-a4d1-d4f7de64aff0") }; - SentryMainThreadData.Collect(); + MainThreadData.CollectData(); var sentryOptions = new SentryUnityOptions { SendDefaultPii = true }; var sut = new UnityScopeUpdater(sentryOptions, _testApplication); @@ -262,7 +262,7 @@ public void AppProtocol_Assigned() _testApplication = new TestApplication(productName: "TestGame"); var sut = new UnityScopeUpdater(_sentryOptions, _testApplication); var scope = new Scope(_sentryOptions); - SentryMainThreadData.Collect(); + MainThreadData.CollectData(); // act sut.ConfigureScope(scope); @@ -330,8 +330,8 @@ public void Tags_Set() DeviceUniqueIdentifier = new(() => "f810306c-68db-4ebe-89ba-13c457449339"), InstallMode = ApplicationInstallMode.Store.ToString() }; - SentryMainThreadData.SentrySystemInfo = systemInfo; - SentryMainThreadData.Collect(); + MainThreadData.SentrySystemInfo = systemInfo; + MainThreadData.CollectData(); var sentryOptions = new SentryUnityOptions { SendDefaultPii = true }; var scopeUpdater = new UnityScopeUpdater(sentryOptions, _testApplication); @@ -383,8 +383,8 @@ public void OperatingSystemProtocol_Assigned() { // arrange var systemInfo = new TestSentrySystemInfo { OperatingSystem = "Windows" }; - SentryMainThreadData.SentrySystemInfo = systemInfo; - SentryMainThreadData.Collect(); + MainThreadData.SentrySystemInfo = systemInfo; + MainThreadData.CollectData(); var sut = new UnityScopeUpdater(_sentryOptions, _testApplication); var scope = new Scope(_sentryOptions); @@ -410,8 +410,8 @@ public void DeviceProtocol_Assigned() DeviceModel = new Lazy(() => "Samsung Galaxy S3"), SystemMemorySize = 16000 }; - SentryMainThreadData.SentrySystemInfo = systemInfo; - SentryMainThreadData.Collect(); + MainThreadData.SentrySystemInfo = systemInfo; + MainThreadData.CollectData(); var sut = new UnityScopeUpdater(_sentryOptions, _testApplication); var scope = new Scope(_sentryOptions); @@ -442,8 +442,8 @@ public void UnityProtocol_Assigned() CopyTextureSupport = new Lazy(() => "Basic, Copy3D, DifferentTypes, TextureToRT, RTToTexture"), RenderingThreadingMode = new Lazy(() => "MultiThreaded") }; - SentryMainThreadData.SentrySystemInfo = systemInfo; - SentryMainThreadData.Collect(); + MainThreadData.SentrySystemInfo = systemInfo; + MainThreadData.CollectData(); var sut = new UnityScopeUpdater(_sentryOptions, _testApplication, sceneManager); var scope = new Scope(_sentryOptions); @@ -483,8 +483,8 @@ public void GpuProtocol_Assigned() SupportsComputeShaders = true, SupportsGeometryShaders = true }; - SentryMainThreadData.SentrySystemInfo = systemInfo; - SentryMainThreadData.Collect(); + MainThreadData.SentrySystemInfo = systemInfo; + MainThreadData.CollectData(); var sut = new UnityScopeUpdater(_sentryOptions, _testApplication); var scope = new Scope(_sentryOptions); @@ -515,8 +515,8 @@ public void GpuProtocolGraphicsShaderLevel_Assigned( [ValueSource(nameof(ShaderLevels))] (int, string) shaderValue) { var (shaderLevel, shaderDescription) = shaderValue; - SentryMainThreadData.SentrySystemInfo = new TestSentrySystemInfo { GraphicsShaderLevel = shaderLevel }; - SentryMainThreadData.Collect(); + MainThreadData.SentrySystemInfo = new TestSentrySystemInfo { GraphicsShaderLevel = shaderLevel }; + MainThreadData.CollectData(); var sut = new UnityScopeUpdater(_sentryOptions, _testApplication); var scope = new Scope(_sentryOptions); @@ -553,8 +553,8 @@ public void GpuProtocolGraphicsShaderLevelMinusOne_Ignored() var scope = new Scope(_sentryOptions); // act - SentryMainThreadData.SentrySystemInfo = systemInfo; - SentryMainThreadData.Collect(); + MainThreadData.SentrySystemInfo = systemInfo; + MainThreadData.CollectData(); sut.ConfigureScope(scope); // assert From 9b983ba878a38dbf0bd8bfd4096b77b8ebce5bec Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 20 May 2025 17:18:56 +0200 Subject: [PATCH 15/17] revert internal/public switch of main thread properties --- src/Sentry.Unity/MainThreadData.cs | 62 +++++++++++++++--------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/Sentry.Unity/MainThreadData.cs b/src/Sentry.Unity/MainThreadData.cs index 64b0ba05e..f763c421f 100644 --- a/src/Sentry.Unity/MainThreadData.cs +++ b/src/Sentry.Unity/MainThreadData.cs @@ -7,66 +7,66 @@ internal static class MainThreadData { internal static int? MainThreadId { get; set; } - internal static string? OperatingSystem { get; set; } + public static string? OperatingSystem { get; set; } - internal static int? ProcessorCount { get; set; } + public static int? ProcessorCount { get; set; } - internal static bool? SupportsVibration { get; set; } + public static bool? SupportsVibration { get; set; } - internal static string? DeviceType { get; set; } + public static string? DeviceType { get; set; } - internal static string? CpuDescription { get; set; } + public static string? CpuDescription { get; set; } - internal static string? DeviceName { get; set; } + public static string? DeviceName { get; set; } - internal static string? DeviceUniqueIdentifier { get; set; } + public static string? DeviceUniqueIdentifier { get; set; } - internal static string? DeviceModel { get; set; } + public static string? DeviceModel { get; set; } - internal static int? SystemMemorySize { get; set; } + public static int? SystemMemorySize { get; set; } - internal static int? GraphicsDeviceId { get; set; } + public static int? GraphicsDeviceId { get; set; } - internal static string? GraphicsDeviceName { get; set; } + public static string? GraphicsDeviceName { get; set; } - internal static string? GraphicsDeviceVendorId { get; set; } + public static string? GraphicsDeviceVendorId { get; set; } - internal static string? GraphicsDeviceVendor { get; set; } + public static string? GraphicsDeviceVendor { get; set; } - internal static int? GraphicsMemorySize { get; set; } + public static int? GraphicsMemorySize { get; set; } - internal static bool? GraphicsMultiThreaded { get; set; } + public static bool? GraphicsMultiThreaded { get; set; } - internal static string? NpotSupport { get; set; } + public static string? NpotSupport { get; set; } - internal static string? GraphicsDeviceVersion { get; set; } + public static string? GraphicsDeviceVersion { get; set; } - internal static string? GraphicsDeviceType { get; set; } + public static string? GraphicsDeviceType { get; set; } - internal static int? MaxTextureSize { get; set; } + public static int? MaxTextureSize { get; set; } - internal static bool? SupportsDrawCallInstancing { get; set; } + public static bool? SupportsDrawCallInstancing { get; set; } - internal static bool? SupportsRayTracing { get; set; } + public static bool? SupportsRayTracing { get; set; } - internal static bool? SupportsComputeShaders { get; set; } + public static bool? SupportsComputeShaders { get; set; } - internal static bool? SupportsGeometryShaders { get; set; } + public static bool? SupportsGeometryShaders { get; set; } - internal static int? GraphicsShaderLevel { get; set; } + public static int? GraphicsShaderLevel { get; set; } - internal static bool? IsDebugBuild { get; set; } + public static bool? IsDebugBuild { get; set; } - internal static string? EditorVersion { get; set; } - internal static string? InstallMode { get; set; } + public static string? EditorVersion { get; set; } + public static string? InstallMode { get; set; } - internal static string? TargetFrameRate { get; set; } + public static string? TargetFrameRate { get; set; } - internal static string? CopyTextureSupport { get; set; } + public static string? CopyTextureSupport { get; set; } - internal static string? RenderingThreadingMode { get; set; } + public static string? RenderingThreadingMode { get; set; } - internal static DateTimeOffset? StartTime { get; set; } + public static DateTimeOffset? StartTime { get; set; } public static bool? IsMainThread() { From 07908720d042dd16adfba533d967a85b8a8c3cfa Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 20 May 2025 17:31:04 +0200 Subject: [PATCH 16/17] removed collect from initialization --- package-dev/Runtime/SentryInitialization.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/package-dev/Runtime/SentryInitialization.cs b/package-dev/Runtime/SentryInitialization.cs index 436fd506b..a0881afb1 100644 --- a/package-dev/Runtime/SentryInitialization.cs +++ b/package-dev/Runtime/SentryInitialization.cs @@ -60,8 +60,6 @@ public static void Init() var options = ScriptableSentryUnityOptions.LoadSentryUnityOptions(unityInfo); if (options != null && options.ShouldInitializeSdk()) { - SentryMainThreadData.Collect(); - // Certain integrations require access to preprocessor directives so we provide them as `.cs` and // compile them with the game instead of precompiling them with the rest of the SDK. // i.e. SceneManagerAPI requires UNITY_2020_3_OR_NEWER From 36c4527e42711202039b83318cd8c66e701e0827 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 20 May 2025 17:35:43 +0200 Subject: [PATCH 17/17] Updated CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8ffd2fc2..494756a2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - The SDK no longer attaches screenshots when capturing errors in the Unity Editor. ([#2163](https://github.com/getsentry/sentry-unity/pull/2163)) +## Fixes + +- Fixed a potential race condition that when targeting Android could cause 'attempting to detach while still running code' crashes ([#2165](https://github.com/getsentry/sentry-unity/pull/2165)) + ### Dependencies - Bump Java SDK from v8.11.1 to v8.12.0 ([#2155](https://github.com/getsentry/sentry-unity/pull/2155))