Skip to content
Merged
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
4 changes: 3 additions & 1 deletion docs/platforms/android/configuration/using-ndk.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ The NDK integration can detect when a native or game thread stops responding, ev

<Alert title="Experimental">

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.

</Alert>

Expand Down Expand Up @@ -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:
Expand Down
43 changes: 41 additions & 2 deletions docs/platforms/native/app-hangs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -49,7 +49,7 @@ The minimum `app_hang_timeout` is `1000` ms. Setting a lower value (including `0

</Alert>

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

Expand All @@ -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 <duration> 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.
4 changes: 2 additions & 2 deletions docs/platforms/native/common/configuration/options.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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`).

</SdkOption>

<SdkOption name="app_hang_timeout" type="uint64_t" defaultValue="5000" defaultNote="milliseconds" availableSince="0.15.2">

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.

</SdkOption>

Expand Down
43 changes: 35 additions & 8 deletions docs/platforms/unity/app-hangs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,59 @@ An app hang occurs when the Unity main thread stops making progress. On Android,

## App Hangs

### Native App Hangs
<Alert level="warning">

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
</Alert>

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 <duration> 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 <PlatformLink to="/configuration/options/#AndroidReportHistoricalAnrs">options</PlatformLink>.

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.

Expand All @@ -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/).
Binary file not shown.
6 changes: 2 additions & 4 deletions docs/platforms/unity/configuration/options/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -269,15 +269,13 @@ Use these options to configure app hang and ANR detection. For supported platfor

<SdkOption name="EnableAppHangTracking" type="bool" defaultValue="false">

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 <PlatformIdentifier name="NdkIntegrationEnabled" />.

When the tracked thread is unresponsive for longer than <PlatformIdentifier name="AppHangTimeout" />, 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 <PlatformIdentifier name="AppHangTimeout" />, the SDK captures an `AppHang` event with a native stack trace.

</SdkOption>

<SdkOption name="AppHangTimeout" type="TimeSpan" defaultValue="TimeSpan.FromSeconds(5)">

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.

</SdkOption>

Expand Down
Loading
Loading