diff --git a/docs/platforms/android/configuration/using-ndk.mdx b/docs/platforms/android/configuration/using-ndk.mdx index 9e5fa65714f39b..a3b851fc57013c 100644 --- a/docs/platforms/android/configuration/using-ndk.mdx +++ b/docs/platforms/android/configuration/using-ndk.mdx @@ -89,7 +89,7 @@ The NDK integration can detect when a native or game thread stops responding, ev -NDK app hang detection is experimental. It requires Sentry Android SDK version `8.48.0` or later and the NDK integration. +NDK app hang detection is experimental. It requires Sentry Android SDK version `8.49.0` or later and the NDK integration. @@ -121,6 +121,8 @@ void game_loop(void) { The first heartbeat chooses the monitored thread. If it misses heartbeats for the configured timeout, the NDK integration captures an app hang event with that thread's stack trace. The timeout defaults to `5000` ms and has a minimum of `1000` ms. +To pause detection, for example while your app is in the background, call `sentry_app_hang_pause()` from native code. The next heartbeat from the monitored thread resumes it. `sentry_app_hang_pause()` requires Sentry Android SDK version `8.52.0` or later. + Android ANR detection remains independent. If the same freeze blocks both the Android UI thread and your monitored native thread, Sentry can report both an ANR and an app hang. You can also configure these options in `AndroidManifest.xml`, which is useful when the SDK initializes automatically: diff --git a/docs/platforms/native/app-hangs.mdx b/docs/platforms/native/app-hangs.mdx index d8caeed50f033a..077d439e2dd12f 100644 --- a/docs/platforms/native/app-hangs.mdx +++ b/docs/platforms/native/app-hangs.mdx @@ -13,7 +13,7 @@ In-process app-hang detection is experimental and may change in a future release An unresponsive application is one of the most frustrating experiences for your users. A long-running computation, a blocking I/O call, or an infinite loop on a critical thread can freeze your app without ever crashing, so it never surfaces as an error. App-hang detection catches these cases and reports them to Sentry as app-hang events. -On macOS, Windows, Linux, and Android, the Native SDK detects hangs entirely in-process: a background watchdog thread monitors a single thread of your choosing and captures an event when that thread stops making progress. Because the SDK can't know which of your threads represents responsiveness, you tell it by sending heartbeats from the thread you want watched (typically your UI or main loop). +On macOS, Windows, Linux, and Android, the Native SDK detects hangs entirely in-process: a background watchdog thread monitors a single thread of your choosing and captures an event when that thread stops making progress. On other platforms, `sentry_app_hang_heartbeat()` and `sentry_app_hang_pause()` do nothing. Because the SDK can't know which of your threads represents responsiveness, you tell it by sending heartbeats from the thread you want watched (typically your UI or main loop). ## Enabling App-Hang Detection @@ -49,7 +49,7 @@ The minimum `app_hang_timeout` is `1000` ms. Setting a lower value (including `0 -If the watched thread doesn't send a heartbeat within the configured [`app_hang_timeout`](/platforms/native/configuration/options/#app_hang_timeout), the watchdog samples the hung thread's stack trace and captures an app-hang event. +If the watched thread doesn't send a heartbeat within the configured [`app_hang_timeout`](/platforms/native/configuration/options/#app_hang_timeout), the watchdog samples the hung thread's stack trace and captures an app-hang event. The watchdog checks every 500 ms, so the event arrives up to 500 ms after the timeout. ## How Heartbeats Work @@ -58,5 +58,44 @@ The SDK monitors exactly one thread, chosen at runtime by your heartbeats: - **The first call to `sentry_app_hang_heartbeat()` latches the calling thread** as the monitored thread. Make sure the first heartbeat comes from the thread most representative of responsiveness, such as your UI or main loop. - **Heartbeats from any other thread are ignored.** Only the latched thread is watched. - **Calls are no-ops unless app-hang detection is enabled** via `sentry_options_set_enable_app_hang_tracking()`, so you can leave the heartbeat calls in place even when the feature is turned off. +- **Detection starts with the first heartbeat.** The SDK doesn't report hangs that happen before it, for example during startup. +- **The monitored thread stays the same until `sentry_close()`.** After you initialize the SDK again, the next first heartbeat chooses the thread anew. +- **Each freeze is reported once.** The watched thread has to send a heartbeat again before the SDK reports another hang. The SDK doesn't send an event when the hang ends. Because detection relies on heartbeats, place them where they reflect your application's responsiveness, for example, at the top of each iteration of your main loop. A thread that's busy but still ticking the loop will keep sending heartbeats; a thread genuinely stuck past the timeout will not. + +## Pausing Detection + +When the watched thread stops sending heartbeats on purpose, for example while your app is in the background or its main loop is suspended, call `sentry_app_hang_pause()` so the SDK doesn't report it as a hang. You can call it from any thread: + +```c +void on_enter_background(void) { + sentry_app_hang_pause(); +} +``` + +The next regular heartbeat from the watched thread resumes detection. Only call `sentry_app_hang_heartbeat()` from the watched thread, not from lifecycle callbacks that run on other threads. If such a callback sends the first heartbeat, the SDK watches the callback's thread instead. + +`sentry_app_hang_pause()` is available since version `0.16.1`. + +## App-Hang Events + +App-hang events have: + +- the level `error` and the message `App hung for at least ms.`, where the duration is the time since the last heartbeat, not the length of the whole freeze +- an exception of type `AppHang` with the watched thread's stack trace +- the mechanism `AppHang`, marked as handled + +Upload your [debug information files](/platforms/native/data-management/debug-files/) to symbolicate the stack trace. The SDK captures these events on its watchdog thread, so `before_send` and sampling apply and run on that thread. + +To find app-hang events, search for: + +``` +error.mechanism.type:AppHang +``` + +## Limitations + +- If the SDK can't capture the watched thread's stack trace, it doesn't send an event. +- On Linux and Android, the SDK captures the stack trace through the real-time signal `SIGRTMIN+4`. If the watched thread blocks this signal, or another library handles it, the SDK can't capture the stack trace. +- On Windows, stack traces are validated on x64. On x86 and ARM64, they might be incomplete. diff --git a/docs/platforms/native/common/configuration/options.mdx b/docs/platforms/native/common/configuration/options.mdx index 5220c6c43ce335..384cda807feed7 100644 --- a/docs/platforms/native/common/configuration/options.mdx +++ b/docs/platforms/native/common/configuration/options.mdx @@ -174,13 +174,13 @@ These options control in-process app-hang detection. See the [App Hangs page](/p Enables or disables in-process app-hang detection. When enabled, a background watchdog thread monitors heartbeats from the watched thread. If no heartbeat is received within `app_hang_timeout`, an app-hang event is captured and sent to Sentry. -Must be combined with regular calls to `sentry_app_hang_heartbeat()` from the thread you want monitored. The first heartbeat latches the calling thread as the monitored thread; heartbeats from any other thread are ignored. +Must be combined with regular calls to `sentry_app_hang_heartbeat()` from the thread you want monitored. The first heartbeat latches the calling thread as the monitored thread; heartbeats from any other thread are ignored. To pause detection while the thread stops sending heartbeats on purpose, call `sentry_app_hang_pause()` (available since `0.16.1`). -Sets the app-hang detection timeout, in milliseconds. If `enable_app_hang_tracking` is enabled and no heartbeat is received from the monitored thread within this window, an app-hang event is captured. +Sets the app-hang detection timeout, in milliseconds. If `enable_app_hang_tracking` is enabled and no heartbeat is received from the monitored thread within this window, an app-hang event is captured. The minimum is `1000` ms; the SDK raises lower values, including `0`, to `1000` ms. diff --git a/docs/platforms/unity/app-hangs.mdx b/docs/platforms/unity/app-hangs.mdx index e93871ef75c22f..587c7afff7d020 100644 --- a/docs/platforms/unity/app-hangs.mdx +++ b/docs/platforms/unity/app-hangs.mdx @@ -9,32 +9,59 @@ An app hang occurs when the Unity main thread stops making progress. On Android, ## App Hangs -### Native App Hangs + -On Android, Windows, Linux, and macOS with the native backend, the SDK provides app hang tracking through the `EnableAppHangTracking` option. It uses sentry-native and is disabled by default. On Android, it also requires `NdkIntegrationEnabled`. The timeout is `AppHangTimeout`, which defaults to 5 seconds. +App hang tracking is experimental. Its behavior may change between releases. -These options are available on the Editor Configuration Window in `Advanced` -> `Automatic Behaviour` or programmatically + + +App hang tracking is disabled by default. Enable it on the Editor Configuration Window in **Advanced** > **Automatic Behaviour** > **App Hang Tracking**, or programmatically: ```csharp options.EnableAppHangTracking = true; options.AppHangTimeout = TimeSpan.FromSeconds(5); ``` -On Android with the NDK integration and on sentry-native desktop platforms, the SDK adds a background watchdog and automatically emits heartbeats from the Unity main thread. Cocoa tracks app hangs directly. When app hang tracking is configured, the SDK removes the managed watchdog to avoid duplicate events. +`AppHangTimeout` is how long the main thread must be blocked before the SDK reports an app hang. It defaults to 5 seconds. + +The detector depends on the platform: + +| Platform | Detected by | Requirements | +| -------------- | -------------------------------------------------------- | ---------------------------------------------------------- | +| Android | sentry-native | IL2CPP and `NdkIntegrationEnabled`, which is on by default | +| iOS | sentry-cocoa | | +| macOS | sentry-cocoa, or sentry-native with the `Native` backend | IL2CPP for the default `Cocoa` backend | +| Windows, Linux | sentry-native | | + +App hangs aren't detected in the Editor or on WebGL. + +You can find the macOS backend in **Advanced** > **Experimental** > **macOS Backend** and `NdkIntegrationEnabled` in **Advanced** > **Native Support**. + +### How Detection Works With sentry-native + +On Android, Windows, Linux, and macOS with the `Native` backend, a coroutine on the Unity main thread sends a heartbeat to sentry-native once per second. If no heartbeat arrives within `AppHangTimeout`, sentry-native's background watchdog captures an event with the main thread's stack trace. Each freeze is reported once. + +- The first heartbeat is sent after the first frame, so the splash screen and loading the first scene aren't reported as app hangs. Blocking scene loads later on are. +- Detection pauses while the game is in the background or loses focus, and resumes with the next heartbeat. A desktop game that keeps running in an unfocused window isn't watched. +- Keep `AppHangTimeout` at 2 seconds or more. With heartbeats one second apart, a shorter timeout reports ordinary frame hitches as app hangs. + +These events have the message `App hung for at least ms.`, where the duration is the time since the last heartbeat. ### App Hangs on iOS and macOS -On iOS and macOS, `EnableAppHangTracking` enables app hang tracking through sentry-cocoa. It is disabled by default. The timeout is `AppHangTimeout`, which defaults to 5 seconds. When targeting macOS you can select the native SDK used to `Native` to utilize sentry-native as well. +With sentry-cocoa, `EnableAppHangTracking` and `AppHangTimeout` configure sentry-cocoa's own app hang tracking. To learn how it detects and reports app hangs, see [Apple app hangs](/platforms/apple/guides/ios/configuration/app-hangs/). ## ANRs ### Android JVM ANRs -`AndroidNativeAnrEnabled` controls JVM ANR detection and defaults to `true`. Below Android 11, it uses an in-process watchdog. On Android 11 and later, it reads the operating system's ANR exit information on the next app start. The app hang detection runs alongside JVM ANR detection, so a freeze that blocks both threads can create an ANR and an `AppHang` event. +`AndroidNativeAnrEnabled` controls JVM ANR detection and defaults to `true`. Below Android 11, it uses an in-process watchdog. On Android 11 and later, it reads the operating system's ANR exit information on the next app start. To report historical ANRs or attach thread dumps, see `AndroidReportHistoricalAnrs` and `AndroidAttachAnrThreadDump` in the options. + +App hang detection runs alongside JVM ANR detection, so a freeze that blocks both threads can create an ANR and an `AppHang` event. ### Unity Watchdog -The managed Unity watchdog is deprecated and disabled by default. Configure its timeout with `AnrTimeout`, which defaults to 5 seconds. Disable it in code with `DisableAnrIntegration()` or through the editor's `AnrDetectionEnabled` setting. +The managed Unity watchdog is deprecated and disabled by default. Use app hang tracking instead. The watchdog can only be enabled in the Editor Configuration Window in **Advanced** > **Automatic Behaviour** > **C# Watchdog (Deprecated)**, where you can also configure its timeout. When app hang tracking is enabled, the SDK removes the watchdog to avoid duplicate events. The watchdog uses a coroutine and a background thread. It detects a stalled Unity main thread, but cannot capture that thread's stack trace. Its events use the `MainThreadWatchdog` mechanism. @@ -46,6 +73,6 @@ To find app hang events, search for: error.mechanism.type:AppHang ``` -Android JVM ANRs remain independent. Filter them with `ANR`, `AppExitInfo`, or `HistoricalAppExitInfo`. +Android JVM ANRs are separate events. Filter them with `ANR`, `AppExitInfo`, or `HistoricalAppExitInfo`. Events from the deprecated Unity watchdog use `MainThreadWatchdog`. For Android NDK implementation details, see [Android NDK app hang detection](/platforms/android/configuration/using-ndk/#app-hang-detection). For Apple behavior, see [Apple app hangs](/platforms/apple/guides/ios/configuration/app-hangs/). diff --git a/docs/platforms/unity/configuration/app-not-responding/img/unity-anr.png b/docs/platforms/unity/configuration/app-not-responding/img/unity-anr.png deleted file mode 100644 index 5ad731aad2e479..00000000000000 Binary files a/docs/platforms/unity/configuration/app-not-responding/img/unity-anr.png and /dev/null differ diff --git a/docs/platforms/unity/configuration/options/index.mdx b/docs/platforms/unity/configuration/options/index.mdx index 23b124b9107659..4510bfcc5677b6 100644 --- a/docs/platforms/unity/configuration/options/index.mdx +++ b/docs/platforms/unity/configuration/options/index.mdx @@ -269,15 +269,13 @@ Use these options to configure app hang and ANR detection. For supported platfor -Enables app hang detection. On iOS and macOS with the default backend, the SDK uses sentry-cocoa. On Android, Windows, Linux, and macOS with the native backend, it uses sentry-native. On Android, this also requires . - -When the tracked thread is unresponsive for longer than , the SDK captures an `AppHang` event with a native stack trace. +Enables experimental app hang detection. When the main thread is unresponsive for longer than , the SDK captures an `AppHang` event with a native stack trace. -Sets how long the main thread must be unresponsive before the SDK captures an app hang. This timeout is also used by native app hang tracking. +Sets how long the main thread must be unresponsive before the SDK captures an app hang. This timeout is also used by native app hang tracking. With sentry-native, keep it at 2 seconds or more, since the SDK sends a heartbeat once per second. diff --git a/docs/platforms/unreal/app-hangs.mdx b/docs/platforms/unreal/app-hangs.mdx index 5c59c9a82a1219..1a274f0693761b 100644 --- a/docs/platforms/unreal/app-hangs.mdx +++ b/docs/platforms/unreal/app-hangs.mdx @@ -9,17 +9,17 @@ Unreal Engine uses **App Not Responding** for mobile UI-thread detection and **A ## App Hangs -Game-thread app hang tracking is disabled by default: +Game-thread app hang tracking is disabled by default. You can find these options in **Project Settings > Plugins > Sentry > App Hangs**, labeled **Enable hang tracking**, **Hang timeout (seconds)**, and **Use native hang tracking**: | Option | Default | Platforms | | ----------------------- | --------- | --------------------------------------------------------------------------- | | `EnableHangTracking` | Disabled | Windows, Linux; required for the native watchdog on all supported platforms | -| `HangTimeoutDuration` | 5 seconds | Windows, Linux, macOS, Android | -| `UseNativeHangTracking` | Disabled | Windows, Linux, macOS, Android | +| `HangTimeoutDuration` | 5 seconds | Windows, Linux, macOS with native backend, Android | +| `UseNativeHangTracking` | Disabled | Windows, Linux, macOS with native backend, Android | ### Engine Watcher -On Windows and Linux, `EnableHangTracking` uses Unreal Engine's `FThreadHeartBeat` watcher. It requires a packaged build and `HangDuration` greater than `0`: +On Windows and Linux, `EnableHangTracking` uses Unreal Engine's `FThreadHeartBeat` watcher. It requires `HangDuration` greater than `0`: ```ini {filename:DefaultEngine.ini} [/Script/Sentry.SentrySettings] @@ -29,7 +29,15 @@ EnableHangTracking=True HangDuration=25 ``` -The engine watcher waits for the greater of `HangTimeoutDuration` and Unreal Engine's `StuckDuration` before capturing a stuck thread's stack trace. It uses Unreal Engine's existing heartbeat system plus a Sentry watchdog thread. +The engine watcher waits for the greater of `HangTimeoutDuration` and Unreal Engine's `StuckDuration` before capturing a stuck thread's stack trace. It uses Unreal Engine's existing heartbeat system plus a Sentry watchdog thread, so it reports any thread that sends engine heartbeats, such as the game thread or the async loading thread. Each stuck thread is reported once. + +Since it relies on Unreal Engine's hang detection, the engine watcher doesn't run: + +- in editor builds, including Standalone Game or `-game` runs of the editor binary, or in Debug builds +- while a debugger is attached, unless you pass `-debughangdetection` +- when you pass `-nothreadtimeout` + +Unreal Engine also reports its own hang when a thread is stuck for `HangDuration`, so keep `HangDuration` above `HangTimeoutDuration`. ### Native Watchdog @@ -42,7 +50,14 @@ UseNativeHangTracking=True HangTimeoutDuration=5.0 ``` -The native watchdog sends an `OnEndFrame` game-thread heartbeat while the app is active and foregrounded, and runs a background watchdog thread. It does not require Unreal Engine's heartbeat configuration. On macOS, it requires the [native backend](/platforms/unreal/configuration/native-backend/). On Android, it runs alongside JVM ANR detection, so a freeze that blocks both threads can create an ANR and an `AppHang` event. +The native watchdog sends a game-thread heartbeat at the end of every frame and runs a background watchdog thread. It does not require Unreal Engine's heartbeat configuration. + +- Detection starts with the first frame, so the SDK doesn't report hangs during startup. After that, blocking loads longer than `HangTimeoutDuration` are reported. +- On Android, detection pauses while the app is in the background. On desktop platforms, it keeps running while the window is in the background or minimized. +- Each freeze is reported once. +- The native watchdog also runs in the Editor, so pausing at a breakpoint for longer than `HangTimeoutDuration` reports an app hang. + +On macOS, the native watchdog requires the [native backend](/platforms/unreal/configuration/native-backend/). On Android, it runs alongside JVM ANR detection, so a freeze that blocks both threads can create an ANR and an `AppHang` event. ## App Not Responding @@ -53,9 +68,11 @@ App Not Responding detection is enabled by default on mobile platforms: | `EnableAppNotRespondingTracking` | Enabled | Android, iOS, macOS with Cocoa backend | | `AppNotRespondingTimeout` | 5 seconds | iOS, macOS with Cocoa backend, Android below API 30 | -On iOS and macOS with the default Cocoa backend, these options configure Cocoa app hang detection. On macOS with the native backend, use native hang tracking instead. On Android, they configure JVM ANR detection. Android 11 and later reads ANRs from `ApplicationExitInfo` during a subsequent SDK initialization, so the operating system controls the timeout. +In **Project Settings > Plugins > Sentry > App Hangs**, these options are under the advanced settings, labeled **Enable main thread hang tracking** and **Main thread hang timeout (seconds)**. + +On iOS and macOS with the default Cocoa backend, they configure Cocoa app hang detection. On macOS with the native backend, use native hang tracking instead. On Android, they configure JVM ANR detection. Android 11 and later reads ANRs from `ApplicationExitInfo` during a subsequent SDK initialization, so the operating system controls the timeout. -To preserve the behavior from before SDK version `1.13.0`, explicitly disable App Not Responding tracking: +To disable App Not Responding tracking: ```ini {filename:DefaultEngine.ini} [/Script/Sentry.SentrySettings] @@ -64,12 +81,14 @@ EnableAppNotRespondingTracking=False ## Queries -App hang events use the `AppHang` mechanism. Search for: +App hang events use the `AppHang` mechanism and the tag `CrashType:Hang`. Search for: ``` error.mechanism.type:AppHang ``` +The two detectors name their events differently, so their issues group separately: the engine watcher reports `App Hanging: Application not responding`, and the native watchdog reports `AppHang: App hung for at least ms.` + Android JVM ANRs use `ANR`, `AppExitInfo`, or `HistoricalAppExitInfo` depending on the Android version and event source. For native watchdog behavior, see [Native SDK app hangs](/platforms/native/app-hangs/). For mobile platform details, see [Android NDK app hang detection](/platforms/android/configuration/using-ndk/#app-hang-detection) and [Apple app hangs](/platforms/apple/guides/ios/configuration/app-hangs/). diff --git a/docs/platforms/unreal/configuration/options.mdx b/docs/platforms/unreal/configuration/options.mdx index 0ccac8c7c15d81..8e267733aeeff3 100644 --- a/docs/platforms/unreal/configuration/options.mdx +++ b/docs/platforms/unreal/configuration/options.mdx @@ -302,7 +302,7 @@ This option is turned off by default. -The default engine-based detection is available on Windows and Linux only, and only works in packaged builds. See [App Hangs and ANRs](/platforms/unreal/app-hangs/) for setup details and prerequisites. +The default engine-based detection is available on Windows and Linux only. It doesn't run in editor builds, including Standalone Game, in Debug builds, or while a debugger is attached. See [App Hangs and ANRs](/platforms/unreal/app-hangs/) for setup details and prerequisites. @@ -334,8 +334,6 @@ This feature is available on Windows, Linux, macOS, and Android. On macOS it req Enables mobile app-not-responding detection. On iOS and macOS with the default Cocoa backend, this configures Cocoa app hang detection. On macOS with the native backend, use and instead. On Android, it configures JVM ANR detection. -This option changed from disabled to enabled by default in version `1.13.0`. Set it to `false` to keep the previous behavior. - diff --git a/middleware.ts b/middleware.ts index dffe4db410274a..7208b5391ef369 100644 --- a/middleware.ts +++ b/middleware.ts @@ -553,6 +553,10 @@ const USER_DOCS_REDIRECTS: Redirect[] = [ from: '/platforms/react-native/install/', to: '/platforms/react-native/manual-setup/manual-setup/', }, + { + from: '/platforms/unity/configuration/app-not-responding/', + to: '/platforms/unity/app-hangs/', + }, { from: '/platforms/unity/data-management/event-grouping/sdk-fingerprinting/', to: '/platforms/unity/usage/sdk-fingerprinting/',