Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

### Features

- Added `AndroidNativeAnrEnabled` (default `true`) to enable ANR detection through `sentry-java` SDK. The native ANR integration monitors the Android UI thread. On API ≥ 30 this uses [ANR v2](https://docs.sentry.io/platforms/android/configuration/app-not-respond/) via `ApplicationExitInfo` to report OS-detected ANRs from prior runs; on API < 30 it falls back to an in-process watchdog. This is complementary to the Unity SDK's C# watchdog, which monitors the Unity player loop. ([#2671](https://github.com/getsentry/sentry-unity/pull/2671))
- Added `EnableAppHangTracking` (default `true`) and `AppHangTimeout` (default `5s`) to enable app hang detection via the native SDK. Currently effective on iOS through `sentry-cocoa`, which monitors the main thread and produces a stack trace for the hang event. On other platforms this is a no-op until each platform's native hang detection lands. When enabled on iOS, the Unity SDK's C# watchdog is skipped to avoid duplicate reports ([#2679](https://github.com/getsentry/sentry-unity/pull/2679))
- Added `AndroidNativeAnrEnabled` (default `true`) to enable ANR detection through the `sentry-java` SDK. The native ANR integration monitors the Android UI thread. On API ≥ 30 this uses [ANR v2](https://docs.sentry.io/platforms/android/configuration/app-not-respond/) via `ApplicationExitInfo` to report OS-detected ANRs from prior runs; on API < 30 it falls back to an in-process watchdog. This is complementary to the Unity SDK's C# watchdog, which monitors the Unity player loop. ([#2671](https://github.com/getsentry/sentry-unity/pull/2671))

### Dependencies

Expand Down
6 changes: 6 additions & 0 deletions package-dev/Plugins/iOS/SentryNativeBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ void SentryNativeBridgeOptionsSetInt(const void *options, const char *name, int3
dictOptions[[NSString stringWithUTF8String:name]] = [NSNumber numberWithInt:value];
}

void SentryNativeBridgeOptionsSetDouble(const void *options, const char *name, double value)
{
NSMutableDictionary *dictOptions = (__bridge NSMutableDictionary *)options;
dictOptions[[NSString stringWithUTF8String:name]] = [NSNumber numberWithDouble:value];
}

void SentryNativeBridgeOptionsAddFailedRequestStatusCodeRange(const void *options, int32_t min, int32_t max)
{
NSMutableDictionary *dictOptions = (__bridge NSMutableDictionary *)options;
Expand Down
1 change: 1 addition & 0 deletions package-dev/Plugins/iOS/SentryNativeBridgeNoOp.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
void *_Nullable SentryNativeBridgeOptionsNew() { return nil; }
void SentryNativeBridgeOptionsSetString(void *options, const char *name, const char *value) { }
void SentryNativeBridgeOptionsSetInt(void *options, const char *name, int32_t value) { }
void SentryNativeBridgeOptionsSetDouble(void *options, const char *name, double value) { }
void SentryNativeBridgeOptionsAddFailedRequestStatusCodeRange(void *options, int32_t min, int32_t max) { }
int SentryNativeBridgeStartWithOptions(void *options) { return 0; }

Expand Down
6 changes: 6 additions & 0 deletions package-dev/Plugins/macOS/SentryNativeBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ void SentryNativeBridgeOptionsSetInt(const void *options, const char *name, int3
dictOptions[[NSString stringWithUTF8String:name]] = [NSNumber numberWithInt:value];
}

void SentryNativeBridgeOptionsSetDouble(const void *options, const char *name, double value)
{
NSMutableDictionary *dictOptions = (__bridge NSMutableDictionary *)options;
dictOptions[[NSString stringWithUTF8String:name]] = [NSNumber numberWithDouble:value];
}

void SentryNativeBridgeOptionsAddFailedRequestStatusCodeRange(const void *options, int32_t min, int32_t max)
{
NSMutableDictionary *dictOptions = (__bridge NSMutableDictionary *)options;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ MonoBehaviour:
m_EditorHideFlags: 0
m_Script: {fileID: -668357930, guid: 43ec428a58422470fa764bdba9d9bc19, type: 3}
m_Name: SentryOptions
m_EditorClassIdentifier:
m_EditorClassIdentifier:
<Enabled>k__BackingField: 1
<Dsn>k__BackingField: https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.us.sentry.io/4504604988538880
<CaptureInEditor>k__BackingField: 1
Expand All @@ -27,8 +27,8 @@ MonoBehaviour:
<AutoAwakeTraces>k__BackingField: 0
<AutoSessionTracking>k__BackingField: 1
<AutoSessionTrackingInterval>k__BackingField: 30000
<ReleaseOverride>k__BackingField:
<EnvironmentOverride>k__BackingField:
<ReleaseOverride>k__BackingField:
<EnvironmentOverride>k__BackingField:
<AttachStacktrace>k__BackingField: 1
<AttachScreenshot>k__BackingField: 1
<ScreenshotQuality>k__BackingField: 1
Expand Down Expand Up @@ -61,6 +61,8 @@ MonoBehaviour:
<MaxQueueItems>k__BackingField: 30
<AnrDetectionEnabled>k__BackingField: 0
<AnrTimeout>k__BackingField: 5000
<EnableAppHangTracking>k__BackingField: 1
<AppHangTimeout>k__BackingField: 5000
<CaptureFailedRequests>k__BackingField: 1
<FailedRequestStatusCodes>k__BackingField: f401000057020000
<FilterBadGatewayExceptions>k__BackingField: 1
Expand Down
3 changes: 2 additions & 1 deletion src/Sentry.Unity.Editor.iOS/NativeOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ internal static string Generate(SentryUnityOptions options)
@""maxBreadcrumbs"": @{options.MaxBreadcrumbs},
@""maxCacheItems"": @{options.MaxCacheItems},
@""enableAutoSessionTracking"": @NO,
@""enableAppHangTracking"": @NO,
@""enableAppHangTracking"": @{ToObjCString(options.EnableAppHangTracking)},
@""appHangTimeoutInterval"": @{options.AppHangTimeout.TotalSeconds.ToString(System.Globalization.CultureInfo.InvariantCulture)},
@""enableCaptureFailedRequests"": @{ToObjCString(options.CaptureFailedRequests)},
@""failedRequestStatusCodes"" : @[{failedRequestStatusCodesArray}],
@""sendDefaultPii"" : @{ToObjCString(options.SendDefaultPii)},
Expand Down
35 changes: 27 additions & 8 deletions src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,40 @@ internal static void Display(ScriptableSentryUnityOptions options, SentryCliOpti
EditorGUILayout.Space();

{
options.AnrDetectionEnabled = EditorGUILayout.BeginToggleGroup(
new GUIContent("ANR Detection", "Whether the SDK should report 'Application Not " +
"Responding' events."),
GUILayout.Label("C# Watchdog", EditorStyles.boldLabel);

options.AnrDetectionEnabled = EditorGUILayout.Toggle(
new GUIContent("Enable", "Whether the SDK should run the C# main-thread watchdog " +
"to report 'Application Not Responding' events."),
options.AnrDetectionEnabled);
EditorGUI.indentLevel++;

options.AnrTimeout = EditorGUILayout.IntField(
new GUIContent("Timeout [ms]",
new GUIContent("Watchdog Timeout [ms]",
"The duration in [ms] for how long the game has to be unresponsive " +
"before an ANR event is reported.\nDefault: 5000ms"),
"before the C# watchdog reports an ANR event.\nDefault: 5000ms"),
options.AnrTimeout);
options.AnrTimeout = Math.Max(0, options.AnrTimeout);
}

EditorGUI.indentLevel--;
EditorGUILayout.EndToggleGroup();
EditorGUILayout.Space();
EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray);
EditorGUILayout.Space();

{
GUILayout.Label("App Hang Tracking", EditorStyles.boldLabel);

options.EnableAppHangTracking = EditorGUILayout.Toggle(
new GUIContent("Enable",
"Enables app hang detection via the native SDK. Currently effective on iOS only; " +
"no-op on other platforms until each platform's native hang detection lands."),
options.EnableAppHangTracking);

options.AppHangTimeout = EditorGUILayout.IntField(
new GUIContent("App Hang Timeout [ms]",
"The duration in [ms] for how long the main thread has to be blocked " +
"before an app hang is reported.\nDefault: 5000ms"),
options.AppHangTimeout);
options.AppHangTimeout = Math.Max(0, options.AppHangTimeout);
}

EditorGUILayout.Space();
Expand Down
10 changes: 7 additions & 3 deletions src/Sentry.Unity.Editor/ScriptableSentryUnityOptionsEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,13 @@ public override void OnInspectorGUI()
EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray);
EditorGUILayout.Space();

EditorGUILayout.LabelField("Application Not Responding", EditorStyles.boldLabel);
EditorGUILayout.Toggle("Enable ANR Detection", options.AnrDetectionEnabled);
EditorGUILayout.IntField("ANR Timeout [ms]", options.AnrTimeout);
EditorGUILayout.LabelField("C# Watchdog", EditorStyles.boldLabel);
EditorGUILayout.Toggle("Enable", options.AnrDetectionEnabled);
EditorGUILayout.IntField("Watchdog Timeout [ms]", options.AnrTimeout);

EditorGUILayout.LabelField("App Hang Tracking", EditorStyles.boldLabel);
EditorGUILayout.Toggle("Enable", options.EnableAppHangTracking);
EditorGUILayout.IntField("App Hang Timeout [ms]", options.AppHangTimeout);

EditorGUILayout.Space();
EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray);
Expand Down
9 changes: 9 additions & 0 deletions src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ public static bool Init(SentryUnityOptions options)
// See https://github.com/getsentry/sentry-unity/issues/1658
OptionsSetInt(cOptions, "enableNetworkBreadcrumbs", 0);

Logger?.LogDebug("Setting EnableAppHangTracking: {0}", options.EnableAppHangTracking);
OptionsSetInt(cOptions, "enableAppHangTracking", options.EnableAppHangTracking ? 1 : 0);

Logger?.LogDebug("Setting AppHangTimeoutInterval: {0}s", options.AppHangTimeout.TotalSeconds);
OptionsSetDouble(cOptions, "appHangTimeoutInterval", options.AppHangTimeout.TotalSeconds);

Logger?.LogDebug("Setting EnableWatchdogTerminationTracking: {0}", options.IosWatchdogTerminationIntegrationEnabled);
OptionsSetInt(cOptions, "enableWatchdogTerminationTracking", options.IosWatchdogTerminationIntegrationEnabled ? 1 : 0);

Expand Down Expand Up @@ -103,6 +109,9 @@ public static bool Init(SentryUnityOptions options)
[DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsSetInt")]
private static extern void OptionsSetInt(IntPtr options, string name, int value);

[DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsSetDouble")]
private static extern void OptionsSetDouble(IntPtr options, string name, double value);
Comment thread
bitsandfoxes marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.

[DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsAddFailedRequestStatusCodeRange")]
private static extern void OptionsAddFailedRequestStatusCodeRange(IntPtr options, int min, int max);

Expand Down
7 changes: 7 additions & 0 deletions src/Sentry.Unity.iOS/SentryNativeCocoa.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ internal static void Configure(SentryUnityOptions options, RuntimePlatform platf
}

options.ScopeObserver = new NativeScopeObserver("iOS", options);

if (options.EnableAppHangTracking)
{
Logger?.LogDebug("Disabling the C# ANR watchdog - sentry-cocoa handles app hang detection.");
options.DisableAnrIntegration();
}
Comment thread
bitsandfoxes marked this conversation as resolved.

Comment thread
bitsandfoxes marked this conversation as resolved.
}
else
{
Expand Down
4 changes: 4 additions & 0 deletions src/Sentry.Unity/ScriptableSentryUnityOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ public static string GetConfigPath(string? notDefaultConfigName = null)

[field: SerializeField] public bool AnrDetectionEnabled { get; set; } = true;
[field: SerializeField] public int AnrTimeout { get; set; } = (int)TimeSpan.FromSeconds(5).TotalMilliseconds;
[field: SerializeField] public bool EnableAppHangTracking { get; set; } = true;
[field: SerializeField] public int AppHangTimeout { get; set; } = (int)TimeSpan.FromSeconds(5).TotalMilliseconds;

[field: SerializeField] public bool CaptureFailedRequests { get; set; } = true;

Expand Down Expand Up @@ -201,6 +203,8 @@ internal SentryUnityOptions ToSentryUnityOptions(
DiagnosticLevel = DiagnosticLevel,
CaptureLogErrorEvents = CaptureLogErrorEvents,
AnrTimeout = TimeSpan.FromMilliseconds(AnrTimeout),
EnableAppHangTracking = EnableAppHangTracking,
AppHangTimeout = TimeSpan.FromMilliseconds(AppHangTimeout),
CaptureFailedRequests = CaptureFailedRequests,
FilterBadGatewayExceptions = FilterBadGatewayExceptions,
IosNativeSupportEnabled = IosNativeSupportEnabled,
Expand Down
13 changes: 13 additions & 0 deletions src/Sentry.Unity/SentryUnityOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,19 @@ public sealed class SentryUnityOptions : SentryOptions
/// </summary>
public bool IosWatchdogTerminationIntegrationEnabled { get; set; } = false;

/// <summary>
/// Enables app hang detection on platforms whose native SDK can deliver Unity-thread hang
/// coverage. Currently effective on iOS only; on other platforms this is a no-op until each
/// platform's native hang detection lands.
/// </summary>
public bool EnableAppHangTracking { get; set; } = true;

/// <summary>
/// The minimum duration for which the main thread must be blocked before <see cref="EnableAppHangTracking"/>
/// reports an app hang.
/// </summary>
public TimeSpan AppHangTimeout { get; set; } = TimeSpan.FromSeconds(5);

/// <summary>
/// Whether the SDK should initialize the native SDK before the game starts. This bakes the options at build-time into
/// the generated Xcode project. Modifying the options at runtime will not affect the options used to initialize
Expand Down
40 changes: 40 additions & 0 deletions test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,46 @@ public void CreateOptionsFile_NewSentryOptions_ContainsSdkNameSetting()
File.Delete(testOptionsFileName);
}

[Test]
public void CreateOptionsFile_EnableAppHangTracking_SetsYes()
{
const string testOptionsFileName = "testOptions.m";

NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions { EnableAppHangTracking = true });

var nativeOptions = File.ReadAllText(testOptionsFileName);
StringAssert.Contains("@\"enableAppHangTracking\": @YES", nativeOptions);

File.Delete(testOptionsFileName);
}

[Test]
public void CreateOptionsFile_AppHangTrackingDisabled_SetsNo()
{
const string testOptionsFileName = "testOptions.m";

NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions { EnableAppHangTracking = false });

var nativeOptions = File.ReadAllText(testOptionsFileName);
StringAssert.Contains("@\"enableAppHangTracking\": @NO", nativeOptions);

File.Delete(testOptionsFileName);
}

[Test]
public void CreateOptionsFile_AppHangTimeout_WrittenAsSeconds()
{
const string testOptionsFileName = "testOptions.m";

NativeOptions.CreateFile(testOptionsFileName,
new SentryUnityOptions { AppHangTimeout = System.TimeSpan.FromMilliseconds(7500) });

var nativeOptions = File.ReadAllText(testOptionsFileName);
StringAssert.Contains("@\"appHangTimeoutInterval\": @7.5", nativeOptions);

File.Delete(testOptionsFileName);
}

[Test]
public void CreateOptionsFile_FilterBadGatewayEnabled_AddsFiltering()
{
Expand Down
Loading