ANR Monitoring in JavaScript #9027
Replies: 6 comments
-
I've created a Proof of Concept showing ANR detection as an integration for nodejs: This uses a worker thread which is periodically polled from the main thread. It has two thresholds:
I'm now working on attempting to get stack traces from the main thread from the worker. Excessive event loop blocking in the Electron main process is an important metric since this can block communication between the GPU thread and renderer threads and cause jankiness. |
Beta Was this translation helpful? Give feedback.
-
@krystofwoldrich if you have feedback about the React Native side of things would be great to get some insights! |
Beta Was this translation helpful? Give feedback.
-
My testing suggests that when the It is possible to capture stack traces via a forked nodejs process with a couple of caveats:
|
Beta Was this translation helpful? Give feedback.
-
I've been working on a branch that captures ANR events from a forked child process: The Watchdog integration starts a forked node process with the same entry point as the current process. To stop the background ANR process from running the main app code, we use a promise that only resolves in the main process: import { init, anrWatchdog } from '@sentry/node';
init({ dsn: "__DSN__" });
// for esm with top-level await
await anrWatchdog({ captureStackTrace: true });
runApp();
// or for cjs
anrWatchdog({ captureStackTrace: true }).then(() => {
runApp();
}) The background ANR process connects to the main process debugger interface via WebSockets and pauses when the event loop threshold is exceeded and captures a stacktrace: |
Beta Was this translation helpful? Give feedback.
-
ElectronOne downside of using the debugging interface with Electron apps is that this opens debug ports for the main process and this probably has security implications for some apps Renderer processesThe renderer |
Beta Was this translation helpful? Give feedback.
-
For RN we want to explore the engine native APIs to attach stack trace when the JS loop is not responding. At the moment we already use the Hermes engine APIs for profiling. |
Beta Was this translation helpful? Give feedback.
-
getsentry/sentry-electron#729 raised the idea of doing Application Not Responding (ANR) detection in the Electron SDK. ANR's are a mobile concept typically (we also use the phrasing App Hangs for iOS), but are also valuable for desktop applications that need a smooth UX.
In JavaScript this is especially valuable as there is only one thread. The event loop hanging on the main thread effectively means no work can get done, which is a huge performance problem for browser, react native, electron, and server runtimes (node, bun).
We should figure out a way to automatically detect ANRs in JavaScript and report them to users. If we can, we should also attempt to attach a stacktrace or similar so users can figure out the source of the ANR and fix it directly.
Walking through our different platforms:
For Electron renderer instances (the UI part of an Electron app), we can use Electron APIs to detect if they are unresponsive. Right now we actually have detection for this, but only generate breadcrumbs.
For the main thread, there is no approach. The main thread in Electron is just a node process, so it makes sense to first build a general solution for node and then apply that back to
To get this working on Node, we can start a watchdog thread that will inspect the main thread to see if it has hanged. By leveraging the Inspector API, we can also generate stacktraces to pinpoint what is causing the hang.
For browser JavaScript, we might want to explore a timeout based approach that ensures the event loop is not being stalled. This has overhead problems though since we are introducing event loop pressure ourselves with it.
In React Native we have stalltracking that is used to set app stall measurements. https://github.com/getsentry/sentry-react-native/blob/main/src/js/tracing/stalltracking.ts. Maybe we can use a similar approach that is more generic than just performance monitoring.
We can use watchdog threads in runtimes that support the necessary APIs, but stacktraces might not be possible because of no inspector API like in Node.
See Browser JS and https://github.com/getsentry/sentry-react-native/blob/main/src/js/tracing/stalltracking.ts.
Plan of Action
Beta Was this translation helpful? Give feedback.
All reactions