|
| 1 | +--- |
| 2 | +title: App Hangs |
| 3 | +sidebar_order: 11 |
| 4 | +description: "Learn how to detect and report application hangs in your Unreal Engine game." |
| 5 | +--- |
| 6 | + |
| 7 | +Application hang errors are triggered when a thread monitored by Unreal Engine's heartbeat system becomes unresponsive for longer than a configurable timeout. The Unreal SDK reports hang errors as Sentry events. |
| 8 | + |
| 9 | +Trying to play an unresponsive game is extremely frustrating for users. There are many reasons why an app may become unresponsive, such as long-running computations on the game thread, infinite loops, deadlocks, and so on. With app hang tracking you can detect and fix them. |
| 10 | + |
| 11 | +<Alert> |
| 12 | + |
| 13 | +This feature is available on Windows and Linux only. On macOS and iOS, the Cocoa SDK provides its own <PlatformLink to="/configuration/options/#enableAppNotRespondingTracking">app hang detection</PlatformLink>. On Android, the Java SDK provides its own <PlatformLink to="/configuration/options/#enableAppNotRespondingTracking">ANR detection</PlatformLink>. |
| 14 | + |
| 15 | +</Alert> |
| 16 | + |
| 17 | +## How It Works |
| 18 | + |
| 19 | +The SDK hooks into Unreal Engine's built-in `FThreadHeartBeat` system, which monitors threads that participate in the engine's heartbeat (game thread, render thread, audio thread, etc.): |
| 20 | + |
| 21 | +1. The engine runs a background thread that periodically checks whether monitored threads are still sending heartbeats. |
| 22 | +2. When a thread misses heartbeats for longer than the engine's `StuckDuration` (default 1 second), the engine fires an `OnThreadStuck` delegate. |
| 23 | +3. The SDK's watchdog thread then waits for the remaining time up to the configured hang timeout. |
| 24 | +4. If the thread is still unresponsive when the timeout expires, the SDK captures a hang event with the stuck thread's stack trace and sends it to Sentry. |
| 25 | +5. If the thread recovers before the timeout, the hang is not reported. |
| 26 | + |
| 27 | +The hang event includes: |
| 28 | +- Event level: `error` |
| 29 | +- Exception type: `App Hanging` |
| 30 | +- Exception value: `Application not responding` |
| 31 | +- Mechanism type: `AppHang` |
| 32 | +- Stack trace of the hung thread |
| 33 | + |
| 34 | +## Prerequisites |
| 35 | + |
| 36 | +The engine's heartbeat monitor thread must be enabled for hang tracking to work. By default, `HangDuration` in `[Core.System]` is set to `0`, which disables the heartbeat monitor. Set it to a value greater than `0` in your project's `DefaultEngine.ini`: |
| 37 | + |
| 38 | +```ini {filename:DefaultEngine.ini} |
| 39 | +[Core.System] |
| 40 | +HangDuration=25 |
| 41 | +``` |
| 42 | + |
| 43 | +`HangDuration` is the threshold (in seconds) after which the engine reports a hang to the [Crash Reporter Client](/platforms/unreal/configuration/setup-crashreporter/). The Sentry SDK's hang tracking coexists with the engine's mechanism — the SDK hooks into the heartbeat system independently and captures its own events based on the <PlatformLink to="/configuration/app-hangs/#hang-timeout">hang timeout</PlatformLink> you configure. |
| 44 | + |
| 45 | +## Configuration |
| 46 | + |
| 47 | +App hang tracking is disabled by default. To enable it, navigate to **Project Settings > Plugins > Sentry > General > Native** and toggle **Enable hang tracking**. |
| 48 | + |
| 49 | +Alternatively, add the following to your project's configuration file: |
| 50 | + |
| 51 | +```ini {filename:DefaultEngine.ini} |
| 52 | +[/Script/Sentry.SentrySettings] |
| 53 | +EnableHangTracking=True |
| 54 | +``` |
| 55 | + |
| 56 | +### Hang Timeout |
| 57 | + |
| 58 | +The hang timeout controls how long a thread must be unresponsive before a hang event is captured. The default is 5 seconds, with a minimum of 1 second. |
| 59 | + |
| 60 | +You can adjust it in **Project Settings > Plugins > Sentry > General > Native > Hang timeout (seconds)**, or via the configuration file: |
| 61 | + |
| 62 | +```ini {filename:DefaultEngine.ini} |
| 63 | +[/Script/Sentry.SentrySettings] |
| 64 | +HangTimeoutDuration=5.0 |
| 65 | +``` |
| 66 | + |
| 67 | +If the configured timeout is shorter than the engine's `StuckDuration`, it will be automatically adjusted upward to match, since the SDK can only start tracking after the engine reports a thread as stuck. |
| 68 | + |
| 69 | +## Filtering Hang Events |
| 70 | + |
| 71 | +You can filter or modify hang events using the `BeforeSend` callback. The `USentryEvent` class provides an `IsAnr()` method that returns `true` for hang events (exception type `App Hanging`): |
| 72 | + |
| 73 | +```cpp |
| 74 | +UCLASS() |
| 75 | +class UHangFilterHandler : public USentryBeforeSendHandler |
| 76 | +{ |
| 77 | + GENERATED_BODY() |
| 78 | + |
| 79 | +public: |
| 80 | + virtual USentryEvent* HandleBeforeSend_Implementation(USentryEvent* Event, USentryHint* Hint) override |
| 81 | + { |
| 82 | + if (Event->IsAnr()) |
| 83 | + { |
| 84 | + // Modify or return nullptr to discard the event |
| 85 | + } |
| 86 | + return Event; |
| 87 | + } |
| 88 | +}; |
| 89 | +``` |
| 90 | + |
| 91 | +## Limitations |
| 92 | + |
| 93 | +- **Packaged builds only**: Hang tracking does not work in the editor or in debug build configurations. The engine's `USE_HANG_DETECTION` macro must be enabled, which is only the case in packaged non-debug builds. |
| 94 | +- **No early-startup detection**: Hangs that occur before `FEngineLoop::Tick()` starts (e.g. during `GameInstance::Init()`) are not detected, because threads must have sent at least one heartbeat before they can be monitored. |
| 95 | +- **Requires engine configuration**: The `HangDuration` setting in `[Core.System]` must be set to a value greater than `0`. Without it, the engine's heartbeat monitor thread doesn't run. |
| 96 | +- **One thread at a time**: If multiple threads become stuck simultaneously, only one hang is reported per episode. The engine's `FThreadHeartBeat` reports one stuck thread at a time, so additional stuck threads are only detected after the first one recovers. |
0 commit comments