From a625f2c2a4f2598600ccdce7c4381d52f3ad83ba Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 11 May 2026 15:31:50 +0200 Subject: [PATCH 1/7] native anr samples --- .../Assets/Scenes/3_AdditionalSamples.unity | 275 +++++++++++++++--- .../Assets/Scripts/AdditionalSampleButtons.cs | 25 ++ .../Scripts/NativeSupport/KotlinPlugin.kt | 5 + .../Scripts/NativeSupport/ObjectiveCPlugin.m | 7 + 4 files changed, 275 insertions(+), 37 deletions(-) diff --git a/samples/unity-of-bugs/Assets/Scenes/3_AdditionalSamples.unity b/samples/unity-of-bugs/Assets/Scenes/3_AdditionalSamples.unity index 85086bcb6..6d7db894a 100644 --- a/samples/unity-of-bugs/Assets/Scenes/3_AdditionalSamples.unity +++ b/samples/unity-of-bugs/Assets/Scenes/3_AdditionalSamples.unity @@ -13,7 +13,7 @@ OcclusionCullingSettings: --- !u!104 &2 RenderSettings: m_ObjectHideFlags: 0 - serializedVersion: 9 + serializedVersion: 10 m_Fog: 0 m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} m_FogMode: 3 @@ -42,8 +42,8 @@ RenderSettings: --- !u!157 &3 LightmapSettings: m_ObjectHideFlags: 0 - serializedVersion: 12 - m_GIWorkflowMode: 1 + serializedVersion: 13 + m_BakeOnSceneLoad: 0 m_GISettings: serializedVersion: 2 m_BounceScale: 1 @@ -66,9 +66,6 @@ LightmapSettings: m_LightmapParameters: {fileID: 0} m_LightmapsBakeMode: 1 m_TextureCompression: 1 - m_FinalGather: 0 - m_FinalGatherFiltering: 1 - m_FinalGatherRayCount: 256 m_ReflectionCompression: 2 m_MixedBakeMode: 2 m_BakeBackend: 0 @@ -103,7 +100,7 @@ NavMeshSettings: serializedVersion: 2 m_ObjectHideFlags: 0 m_BuildSettings: - serializedVersion: 2 + serializedVersion: 3 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 @@ -116,12 +113,145 @@ NavMeshSettings: cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 - accuratePlacement: 0 + buildHeightMesh: 0 maxJobWorkers: 0 preserveTilesOutsideBounds: 0 debug: m_Flags: 0 m_NavMeshData: {fileID: 0} +--- !u!1 &191506112 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 191506113} + - component: {fileID: 191506116} + - component: {fileID: 191506115} + - component: {fileID: 191506114} + m_Layer: 5 + m_Name: Application-Not-Responding (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &191506113 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 191506112} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1603559786} + m_Father: {fileID: 253040315} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 200, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &191506114 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 191506112} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 191506115} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 253040317} + m_TargetAssemblyTypeName: AdditionalSampleButtons, Assembly-CSharp + m_MethodName: ApplicationNotRespondingNative + m_Mode: 1 + m_Arguments: + m_ObjectArgument: {fileID: 0} + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine + m_IntArgument: 0 + m_FloatArgument: 0 + m_StringArgument: + m_BoolArgument: 0 + m_CallState: 1 +--- !u!114 &191506115 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 191506112} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &191506116 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 191506112} + m_CullTransparentMesh: 0 --- !u!1 &253040314 GameObject: m_ObjectHideFlags: 0 @@ -156,9 +286,9 @@ RectTransform: - {fileID: 1326160953} - {fileID: 1983589452} - {fileID: 978406552} + - {fileID: 191506113} - {fileID: 2066465601} m_Father: {fileID: 1665572489} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 1, y: 1} @@ -234,9 +364,17 @@ Camera: m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} - m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 x: 0 @@ -270,13 +408,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 519420028} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -10} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &582054325 GameObject: @@ -318,13 +456,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 582054325} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &582054329 MonoBehaviour: @@ -367,7 +505,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5216638424148094703} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -405,7 +542,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 978406552} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -445,7 +581,7 @@ MonoBehaviour: m_HorizontalOverflow: 1 m_VerticalOverflow: 0 m_LineSpacing: 1 - m_Text: Application-Not-Responding + m_Text: App Freeze in C# --- !u!222 &765642148 CanvasRenderer: m_ObjectHideFlags: 0 @@ -487,7 +623,6 @@ RectTransform: m_Children: - {fileID: 834374186} m_Father: {fileID: 253040315} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -619,7 +754,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 802360430} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -699,7 +833,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1983589452} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -781,7 +914,6 @@ RectTransform: m_Children: - {fileID: 765642146} m_Father: {fileID: 253040315} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -915,7 +1047,6 @@ RectTransform: m_Children: - {fileID: 1857152829} m_Father: {fileID: 253040315} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -1047,7 +1178,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 2066465601} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -1096,6 +1226,85 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1583546086} m_CullTransparentMesh: 0 +--- !u!1 &1603559785 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1603559786} + - component: {fileID: 1603559788} + - component: {fileID: 1603559787} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1603559786 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1603559785} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 191506113} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1603559787 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1603559785} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: App Freeze in Native +--- !u!222 &1603559788 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1603559785} + m_CullTransparentMesh: 0 --- !u!1 &1665572488 GameObject: m_ObjectHideFlags: 0 @@ -1131,7 +1340,6 @@ RectTransform: - {fileID: 253040315} - {fileID: 5216638424148094703} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -1197,6 +1405,7 @@ Canvas: m_SortingBucketNormalizedSize: 0 m_VertexColorAlwaysGammaSpace: 0 m_AdditionalShaderChannelsFlag: 0 + m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 0 m_TargetDisplay: 0 @@ -1231,7 +1440,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1326160953} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -1313,7 +1521,6 @@ RectTransform: m_Children: - {fileID: 908640125} m_Father: {fileID: 253040315} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -1447,7 +1654,6 @@ RectTransform: m_Children: - {fileID: 1583546087} m_Father: {fileID: 253040315} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -1623,7 +1829,6 @@ RectTransform: - {fileID: 5216638425317031111} - {fileID: 735359052} m_Father: {fileID: 1665572489} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0} m_AnchorMax: {x: 0.5, y: 0} @@ -1703,7 +1908,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5216638425317031111} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -1765,7 +1969,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5216638425882040144} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -1837,7 +2040,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5216638424148094703} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -1997,7 +2199,6 @@ RectTransform: m_Children: - {fileID: 5216638424536828319} m_Father: {fileID: 5216638424148094703} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -2112,7 +2313,6 @@ RectTransform: m_Children: - {fileID: 5216638426142319615} m_Father: {fileID: 5216638424148094703} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -2152,7 +2352,6 @@ RectTransform: m_Children: - {fileID: 5216638425035397373} m_Father: {fileID: 5216638424148094703} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -2301,7 +2500,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5216638424148094703} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -2381,7 +2579,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5216638425805414493} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -2453,7 +2650,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7152012675849339084} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -2483,7 +2679,6 @@ RectTransform: - {fileID: 7152012675148913643} - {fileID: 7152012676968016302} m_Father: {fileID: 1665572489} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 1, y: 1} @@ -2561,7 +2756,6 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7152012675849339084} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -2586,3 +2780,10 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 1665572489} + - {fileID: 582054328} + - {fileID: 519420032} diff --git a/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs b/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs index 0bfac1503..1f621e2d5 100644 --- a/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs +++ b/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs @@ -1,5 +1,8 @@ using System; using System.Threading; +#if UNITY_IOS +using System.Runtime.InteropServices; +#endif using Sentry; using Sentry.Unity; using Unity.Burst; @@ -52,6 +55,28 @@ public void ApplicationNotResponding() Debug.Log("Thread.Sleep() finished."); } + public void ApplicationNotRespondingNative() + { +#if UNITY_ANDROID && !UNITY_EDITOR + Debug.Log("Stalling the main thread via Kotlin to trigger a native ANR event."); + using (var jo = new AndroidJavaObject("unity.of.bugs.KotlinPlugin")) + { + jo.CallStatic("applicationNotResponding"); + } +#elif UNITY_IOS && !UNITY_EDITOR + Debug.Log("Stalling the main thread via Objective-C to trigger a native ANR event."); + applicationNotResponding(); +#else + Debug.LogWarning("Native ANR sample requires running on Android or iOS."); +#endif + } + +#if UNITY_IOS && !UNITY_EDITOR + // ObjectiveCPlugin.m + [DllImport("__Internal")] + private static extern void applicationNotResponding(); +#endif + public void Assert() => UnityEngine.Assertions.Assert.IsTrue(false); diff --git a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt index 4f684b432..ada8ebd38 100644 --- a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt +++ b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt @@ -18,4 +18,9 @@ object KotlinPlugin { throw Exception("Kotlin 🐛 from a background thread.") } } + @JvmStatic fun applicationNotResponding() { + Log.i("test", "Stalling the main thread from Kotlin to trigger a native ANR.") + Thread.sleep(6 * 1000) // ANR detection currently defaults to 5 seconds + Log.i("test", "Kotlin main thread stall finished.") + } } diff --git a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m index e092f42eb..b0f36280e 100644 --- a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m +++ b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m @@ -16,4 +16,11 @@ void throwObjectiveC() #endif } +void applicationNotResponding() +{ + NSLog(@"Stalling the main thread from Objective-C to trigger a native ANR."); + [NSThread sleepForTimeInterval:6.0]; // ANR detection currently defaults to 5 seconds + NSLog(@"Objective-C main thread stall finished."); +} + NS_ASSUME_NONNULL_END From a33b5141f96c81cd9cf220b95cb8c9f8d82f8afd Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 13 May 2026 15:55:38 +0200 Subject: [PATCH 2/7] . --- samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs | 2 +- .../unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt | 2 +- .../Assets/Scripts/NativeSupport/ObjectiveCPlugin.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs b/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs index 1f621e2d5..80e6ab199 100644 --- a/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs +++ b/samples/unity-of-bugs/Assets/Scripts/AdditionalSampleButtons.cs @@ -51,7 +51,7 @@ public void CaptureMessageWithContext() public void ApplicationNotResponding() { Debug.Log("Running Thread.Sleep() on the UI thread to trigger an ANR event."); - Thread.Sleep(6 * 1000); // ANR detection currently defaults to 5 seconds + Thread.Sleep(10 * 1000); // ANR detection currently defaults to 5 seconds Debug.Log("Thread.Sleep() finished."); } diff --git a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt index ada8ebd38..b975abfa7 100644 --- a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt +++ b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/KotlinPlugin.kt @@ -20,7 +20,7 @@ object KotlinPlugin { } @JvmStatic fun applicationNotResponding() { Log.i("test", "Stalling the main thread from Kotlin to trigger a native ANR.") - Thread.sleep(6 * 1000) // ANR detection currently defaults to 5 seconds + Thread.sleep(10 * 1000) // ANR detection currently defaults to 5 seconds Log.i("test", "Kotlin main thread stall finished.") } } diff --git a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m index b0f36280e..deaa19b51 100644 --- a/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m +++ b/samples/unity-of-bugs/Assets/Scripts/NativeSupport/ObjectiveCPlugin.m @@ -19,7 +19,7 @@ void throwObjectiveC() void applicationNotResponding() { NSLog(@"Stalling the main thread from Objective-C to trigger a native ANR."); - [NSThread sleepForTimeInterval:6.0]; // ANR detection currently defaults to 5 seconds + [NSThread sleepForTimeInterval:10.0]; // ANR detection currently defaults to 5 seconds NSLog(@"Objective-C main thread stall finished."); } From c3b5f3b18578454bda513213d8f7ab98d3526e9a Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 13 May 2026 15:59:18 +0200 Subject: [PATCH 3/7] ios native anr --- src/Sentry.Unity.Editor.iOS/NativeOptions.cs | 2 +- .../SentryCocoaBridgeProxy.cs | 3 +++ src/Sentry.Unity.iOS/SentryNativeCocoa.cs | 6 +++++ src/Sentry.Unity/SentryUnityOptions.cs | 12 +++++++++ .../NativeOptionsTests.cs | 26 +++++++++++++++++++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/Sentry.Unity.Editor.iOS/NativeOptions.cs b/src/Sentry.Unity.Editor.iOS/NativeOptions.cs index 6a0752d60..7ce213173 100644 --- a/src/Sentry.Unity.Editor.iOS/NativeOptions.cs +++ b/src/Sentry.Unity.Editor.iOS/NativeOptions.cs @@ -30,7 +30,7 @@ internal static string Generate(SentryUnityOptions options) @""maxBreadcrumbs"": @{options.MaxBreadcrumbs}, @""maxCacheItems"": @{options.MaxCacheItems}, @""enableAutoSessionTracking"": @NO, - @""enableAppHangTracking"": @NO, + @""enableAppHangTracking"": @{ToObjCString(options.IosNativeAnrEnabled)}, @""enableCaptureFailedRequests"": @{ToObjCString(options.CaptureFailedRequests)}, @""failedRequestStatusCodes"" : @[{failedRequestStatusCodesArray}], @""sendDefaultPii"" : @{ToObjCString(options.SendDefaultPii)}, diff --git a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs index 9d36d8d6a..3d06f69a3 100644 --- a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs +++ b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs @@ -66,6 +66,9 @@ 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.IosNativeAnrEnabled); + OptionsSetInt(cOptions, "enableAppHangTracking", options.IosNativeAnrEnabled ? 1 : 0); + Logger?.LogDebug("Setting EnableWatchdogTerminationTracking: {0}", options.IosWatchdogTerminationIntegrationEnabled); OptionsSetInt(cOptions, "enableWatchdogTerminationTracking", options.IosWatchdogTerminationIntegrationEnabled ? 1 : 0); diff --git a/src/Sentry.Unity.iOS/SentryNativeCocoa.cs b/src/Sentry.Unity.iOS/SentryNativeCocoa.cs index 89aa52f04..4f5657ed2 100644 --- a/src/Sentry.Unity.iOS/SentryNativeCocoa.cs +++ b/src/Sentry.Unity.iOS/SentryNativeCocoa.cs @@ -43,6 +43,12 @@ internal static void Configure(SentryUnityOptions options, RuntimePlatform platf } options.ScopeObserver = new NativeScopeObserver("iOS", options); + + if (options.IosNativeAnrEnabled) + { + Logger?.LogDebug("Disabling the C# ANR watchdog on iOS - sentry-cocoa handles app hang detection."); + options.DisableAnrIntegration(); + } } else { diff --git a/src/Sentry.Unity/SentryUnityOptions.cs b/src/Sentry.Unity/SentryUnityOptions.cs index 20da43cb0..faa8a6dcd 100644 --- a/src/Sentry.Unity/SentryUnityOptions.cs +++ b/src/Sentry.Unity/SentryUnityOptions.cs @@ -185,6 +185,18 @@ public sealed class SentryUnityOptions : SentryOptions /// public bool IosWatchdogTerminationIntegrationEnabled { get; set; } = false; + /// + /// Enables ANR (app hang) detection on iOS through the native (sentry-cocoa) SDK. + /// When enabled, sentry-cocoa monitors the main thread for hangs and the Unity SDK's + /// C# ANR watchdog is skipped on iOS to avoid duplicate reports. + /// + /// + /// sentry-cocoa observes the iOS run loop directly, which yields more accurate + /// app-hang detection than the Unity-side watchdog. Disable this only if you want + /// to fall back to the C# watchdog. + /// + public bool IosNativeAnrEnabled { get; set; } = true; + /// /// 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 diff --git a/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs b/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs index 71aecac29..b2b5057ef 100644 --- a/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs +++ b/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs @@ -58,6 +58,32 @@ public void CreateOptionsFile_NewSentryOptions_ContainsSdkNameSetting() File.Delete(testOptionsFileName); } + [Test] + public void CreateOptionsFile_IosNativeAnrEnabled_SetsYes() + { + const string testOptionsFileName = "testOptions.m"; + + NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions { IosNativeAnrEnabled = true }); + + var nativeOptions = File.ReadAllText(testOptionsFileName); + StringAssert.Contains("@\"enableAppHangTracking\": @YES", nativeOptions); + + File.Delete(testOptionsFileName); + } + + [Test] + public void CreateOptionsFile_IosNativeAnrDisabled_SetsNo() + { + const string testOptionsFileName = "testOptions.m"; + + NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions { IosNativeAnrEnabled = false }); + + var nativeOptions = File.ReadAllText(testOptionsFileName); + StringAssert.Contains("@\"enableAppHangTracking\": @NO", nativeOptions); + + File.Delete(testOptionsFileName); + } + [Test] public void CreateOptionsFile_FilterBadGatewayEnabled_AddsFiltering() { From 727155d71e42a4dde6cb5a910ad9a12c51e6dc86 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 18 May 2026 13:01:18 +0200 Subject: [PATCH 4/7] ios only --- src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs index 3d06f69a3..d1ea487ff 100644 --- a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs +++ b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs @@ -1,6 +1,8 @@ using System; using System.Runtime.InteropServices; using Sentry.Extensibility; +using Sentry.Unity.Integrations; +using UnityEngine; namespace Sentry.Unity.iOS; @@ -66,8 +68,11 @@ 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.IosNativeAnrEnabled); - OptionsSetInt(cOptions, "enableAppHangTracking", options.IosNativeAnrEnabled ? 1 : 0); + if (ApplicationAdapter.Instance.Platform == RuntimePlatform.IPhonePlayer) + { + Logger?.LogDebug("Setting EnableAppHangTracking: {0}", options.IosNativeAnrEnabled); + OptionsSetInt(cOptions, "enableAppHangTracking", options.IosNativeAnrEnabled ? 1 : 0); + } Logger?.LogDebug("Setting EnableWatchdogTerminationTracking: {0}", options.IosWatchdogTerminationIntegrationEnabled); OptionsSetInt(cOptions, "enableWatchdogTerminationTracking", options.IosWatchdogTerminationIntegrationEnabled ? 1 : 0); From 0023a45f97881a6e26ff8e8746dfc6256518d5af Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 18 May 2026 13:04:45 +0200 Subject: [PATCH 5/7] updated changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21b3440c3..35f951e49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,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 `IosNativeAnrEnabled` (default `true`) to enable ANR detection through the `sentry-cocoa` SDK. This replaces the Unity SDK's C# watchdog, monitors the Unity player loop and allows Sentry to show a stack trace for the ANR event ([#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 From 791e74382a7035ac2fed07496477d0b6a700c378 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 18 May 2026 17:26:36 +0200 Subject: [PATCH 6/7] AppHang is the name. And timeouts must be passed down --- src/Sentry.Unity.Editor.iOS/NativeOptions.cs | 3 ++- .../ConfigurationWindow/AdvancedTab.cs | 19 +++++++++------- .../SentryCocoaBridgeProxy.cs | 15 +++++++------ src/Sentry.Unity.iOS/SentryNativeCocoa.cs | 12 +++++----- .../ScriptableSentryUnityOptions.cs | 2 ++ src/Sentry.Unity/SentryUnityOptions.cs | 8 +++---- .../NativeOptionsTests.cs | 22 +++++++++++++++---- 7 files changed, 51 insertions(+), 30 deletions(-) diff --git a/src/Sentry.Unity.Editor.iOS/NativeOptions.cs b/src/Sentry.Unity.Editor.iOS/NativeOptions.cs index 7ce213173..3ad945c29 100644 --- a/src/Sentry.Unity.Editor.iOS/NativeOptions.cs +++ b/src/Sentry.Unity.Editor.iOS/NativeOptions.cs @@ -30,7 +30,8 @@ internal static string Generate(SentryUnityOptions options) @""maxBreadcrumbs"": @{options.MaxBreadcrumbs}, @""maxCacheItems"": @{options.MaxCacheItems}, @""enableAutoSessionTracking"": @NO, - @""enableAppHangTracking"": @{ToObjCString(options.IosNativeAnrEnabled)}, + @""enableAppHangTracking"": @{ToObjCString(options.EnableAppHangTracking)}, + @""appHangTimeoutInterval"": @{options.AnrTimeout.TotalSeconds.ToString(System.Globalization.CultureInfo.InvariantCulture)}, @""enableCaptureFailedRequests"": @{ToObjCString(options.CaptureFailedRequests)}, @""failedRequestStatusCodes"" : @[{failedRequestStatusCodesArray}], @""sendDefaultPii"" : @{ToObjCString(options.SendDefaultPii)}, diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs index 56d0c2e9e..028b4ef72 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs @@ -39,21 +39,24 @@ 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."), + options.AnrDetectionEnabled = EditorGUILayout.Toggle( + new GUIContent("C# Watchdog", "Whether the SDK should run the C# main-thread watchdog " + + "to report 'Application Not Responding' events."), options.AnrDetectionEnabled); - EditorGUI.indentLevel++; + + options.EnableAppHangTracking = EditorGUILayout.Toggle( + new GUIContent("App Hang Tracking", + "Enables app hang (ANR) detection via the native SDK. " + + "When enabled, the native SDK monitors the main thread and the Unity SDK's C# ANR " + + "watchdog is skipped to avoid duplicate reports."), + options.EnableAppHangTracking); options.AnrTimeout = EditorGUILayout.IntField( - new GUIContent("Timeout [ms]", + new GUIContent("App Hang Timeout [ms]", "The duration in [ms] for how long the game has to be unresponsive " + "before an ANR event is reported.\nDefault: 5000ms"), options.AnrTimeout); options.AnrTimeout = Math.Max(0, options.AnrTimeout); - - EditorGUI.indentLevel--; - EditorGUILayout.EndToggleGroup(); } EditorGUILayout.Space(); diff --git a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs index d1ea487ff..a3d6c1080 100644 --- a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs +++ b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs @@ -1,8 +1,6 @@ using System; using System.Runtime.InteropServices; using Sentry.Extensibility; -using Sentry.Unity.Integrations; -using UnityEngine; namespace Sentry.Unity.iOS; @@ -68,11 +66,11 @@ public static bool Init(SentryUnityOptions options) // See https://github.com/getsentry/sentry-unity/issues/1658 OptionsSetInt(cOptions, "enableNetworkBreadcrumbs", 0); - if (ApplicationAdapter.Instance.Platform == RuntimePlatform.IPhonePlayer) - { - Logger?.LogDebug("Setting EnableAppHangTracking: {0}", options.IosNativeAnrEnabled); - OptionsSetInt(cOptions, "enableAppHangTracking", options.IosNativeAnrEnabled ? 1 : 0); - } + Logger?.LogDebug("Setting EnableAppHangTracking: {0}", options.EnableAppHangTracking); + OptionsSetInt(cOptions, "enableAppHangTracking", options.EnableAppHangTracking ? 1 : 0); + + Logger?.LogDebug("Setting AppHangTimeoutInterval: {0}s", options.AnrTimeout.TotalSeconds); + OptionsSetDouble(cOptions, "appHangTimeoutInterval", options.AnrTimeout.TotalSeconds); Logger?.LogDebug("Setting EnableWatchdogTerminationTracking: {0}", options.IosWatchdogTerminationIntegrationEnabled); OptionsSetInt(cOptions, "enableWatchdogTerminationTracking", options.IosWatchdogTerminationIntegrationEnabled ? 1 : 0); @@ -111,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); + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsAddFailedRequestStatusCodeRange")] private static extern void OptionsAddFailedRequestStatusCodeRange(IntPtr options, int min, int max); diff --git a/src/Sentry.Unity.iOS/SentryNativeCocoa.cs b/src/Sentry.Unity.iOS/SentryNativeCocoa.cs index 4f5657ed2..98d29de0c 100644 --- a/src/Sentry.Unity.iOS/SentryNativeCocoa.cs +++ b/src/Sentry.Unity.iOS/SentryNativeCocoa.cs @@ -43,12 +43,6 @@ internal static void Configure(SentryUnityOptions options, RuntimePlatform platf } options.ScopeObserver = new NativeScopeObserver("iOS", options); - - if (options.IosNativeAnrEnabled) - { - Logger?.LogDebug("Disabling the C# ANR watchdog on iOS - sentry-cocoa handles app hang detection."); - options.DisableAnrIntegration(); - } } else { @@ -60,6 +54,12 @@ internal static void Configure(SentryUnityOptions options, RuntimePlatform platf options.ScopeObserver = new NativeScopeObserver("macOS", options); } + if (options.EnableAppHangTracking) + { + Logger?.LogDebug("Disabling the C# ANR watchdog - sentry-cocoa handles app hang detection."); + options.DisableAnrIntegration(); + } + SentryCocoaBridgeProxy.SetSdkName(); // Since we're not building the SDK we have to overwrite the name here options.NativeContextWriter = new NativeContextWriter(); diff --git a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs index e357b6724..5bb07e44f 100644 --- a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs +++ b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs @@ -108,6 +108,7 @@ 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 bool CaptureFailedRequests { get; set; } = true; @@ -201,6 +202,7 @@ internal SentryUnityOptions ToSentryUnityOptions( DiagnosticLevel = DiagnosticLevel, CaptureLogErrorEvents = CaptureLogErrorEvents, AnrTimeout = TimeSpan.FromMilliseconds(AnrTimeout), + EnableAppHangTracking = EnableAppHangTracking, CaptureFailedRequests = CaptureFailedRequests, FilterBadGatewayExceptions = FilterBadGatewayExceptions, IosNativeSupportEnabled = IosNativeSupportEnabled, diff --git a/src/Sentry.Unity/SentryUnityOptions.cs b/src/Sentry.Unity/SentryUnityOptions.cs index faa8a6dcd..23870254c 100644 --- a/src/Sentry.Unity/SentryUnityOptions.cs +++ b/src/Sentry.Unity/SentryUnityOptions.cs @@ -186,16 +186,16 @@ public sealed class SentryUnityOptions : SentryOptions public bool IosWatchdogTerminationIntegrationEnabled { get; set; } = false; /// - /// Enables ANR (app hang) detection on iOS through the native (sentry-cocoa) SDK. + /// Enables app hang (ANR) detection on iOS and macOS through the native (sentry-cocoa) SDK. /// When enabled, sentry-cocoa monitors the main thread for hangs and the Unity SDK's - /// C# ANR watchdog is skipped on iOS to avoid duplicate reports. + /// C# ANR watchdog is skipped to avoid duplicate reports. /// /// - /// sentry-cocoa observes the iOS run loop directly, which yields more accurate + /// sentry-cocoa observes the platform run loop directly, which yields more accurate /// app-hang detection than the Unity-side watchdog. Disable this only if you want /// to fall back to the C# watchdog. /// - public bool IosNativeAnrEnabled { get; set; } = true; + public bool EnableAppHangTracking { get; set; } = true; /// /// Whether the SDK should initialize the native SDK before the game starts. This bakes the options at build-time into diff --git a/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs b/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs index b2b5057ef..87ad6e0c7 100644 --- a/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs +++ b/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs @@ -59,11 +59,11 @@ public void CreateOptionsFile_NewSentryOptions_ContainsSdkNameSetting() } [Test] - public void CreateOptionsFile_IosNativeAnrEnabled_SetsYes() + public void CreateOptionsFile_EnableAppHangTracking_SetsYes() { const string testOptionsFileName = "testOptions.m"; - NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions { IosNativeAnrEnabled = true }); + NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions { EnableAppHangTracking = true }); var nativeOptions = File.ReadAllText(testOptionsFileName); StringAssert.Contains("@\"enableAppHangTracking\": @YES", nativeOptions); @@ -72,11 +72,11 @@ public void CreateOptionsFile_IosNativeAnrEnabled_SetsYes() } [Test] - public void CreateOptionsFile_IosNativeAnrDisabled_SetsNo() + public void CreateOptionsFile_AppHangTrackingDisabled_SetsNo() { const string testOptionsFileName = "testOptions.m"; - NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions { IosNativeAnrEnabled = false }); + NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions { EnableAppHangTracking = false }); var nativeOptions = File.ReadAllText(testOptionsFileName); StringAssert.Contains("@\"enableAppHangTracking\": @NO", nativeOptions); @@ -84,6 +84,20 @@ public void CreateOptionsFile_IosNativeAnrDisabled_SetsNo() File.Delete(testOptionsFileName); } + [Test] + public void CreateOptionsFile_AnrTimeout_WrittenAsSeconds() + { + const string testOptionsFileName = "testOptions.m"; + + NativeOptions.CreateFile(testOptionsFileName, + new SentryUnityOptions { AnrTimeout = System.TimeSpan.FromMilliseconds(7500) }); + + var nativeOptions = File.ReadAllText(testOptionsFileName); + StringAssert.Contains("@\"appHangTimeoutInterval\": @7.5", nativeOptions); + + File.Delete(testOptionsFileName); + } + [Test] public void CreateOptionsFile_FilterBadGatewayEnabled_AddsFiltering() { From 3f4c269ecf817b76a026505f6f052345d2e9b3df Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Mon, 18 May 2026 17:26:44 +0200 Subject: [PATCH 7/7] updated changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa901fbaa..96ef13050 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ ### Features -- Added `IosNativeAnrEnabled` (default `true`) to enable ANR detection through the `sentry-cocoa` SDK. This replaces the Unity SDK's C# watchdog, monitors the Unity player loop and allows Sentry to show a stack trace for the ANR event ([#2679](https://github.com/getsentry/sentry-unity/pull/2679)) +- Added `EnableAppHangTracking` (default `true`) to enable app hang (ANR) detection through the `sentry-cocoa` SDK on iOS and macOS. When enabled, sentry-cocoa monitors the main thread and replaces the Unity SDK's C# watchdog on these platforms, allowing Sentry to show a stack trace for the hang event ([#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