Skip to content

ANR: loadDebugImages runs synchronously on main thread causing ANR on slow I/O devices #3536

Description

@tanchuev

Problem

LoadNativeDebugImagesIntegration causes ANR on Android devices with slow I/O (observed on Realme RMX3830, RMX3834, RMX3940 across Android 13-15).

The root cause is that SentryNativeJava.loadDebugImages() performs a synchronous JNI call to SentryFlutterPlugin.loadDebugImagesAsBytes() on the main thread. The native function sentry_get_modules_list reads /proc/self/maps via syscall, which blocks the UI thread for >5 seconds on affected devices.

Stacktrace

at io.sentry.flutter.SentryFlutterPlugin.loadDebugImagesAsBytes
at io.sentry.flutter.SentryFlutterPlugin$Companion.loadDebugImagesAsBytes
at io.sentry.android.ndk.DebugImagesLoader.loadDebugImagesForAddresses
at io.sentry.android.ndk.DebugImagesLoader.loadDebugImages
at io.sentry.ndk.NativeModuleListLoader.loadModuleList
at io.sentry.ndk.NativeModuleListLoader.nativeLoadModuleList
at sentry_get_modules_list
at syscall

Call chain

EventProcessor.apply() [main isolate]
  → SentryNativeJava.loadDebugImages() [synchronous, main isolate]
    → SentryFlutterPlugin.loadDebugImagesAsBytes() [JNI, main thread]
      → DebugImagesLoader.loadDebugImagesForAddresses() [Java, main thread]
        → NativeModuleListLoader.loadModuleList() [Java, main thread]
          → sentry_get_modules_list() [native, main thread]
            → read /proc/self/maps [syscall, blocking I/O]

Environment

  • sentry_flutter: 9.13.0
  • Flutter: 3.35.0
  • Devices: Realme RMX3830, RMX3834, RMX3940 (budget devices with slow I/O)
  • OS: Android 13, 14, 15
  • Mechanism: AppExitInfo

Previous optimizations (already in 9.13.0)

These optimizations helped, but sentry_get_modules_listsyscall still runs synchronously on the main thread, which is too slow on budget devices.

Expected behavior

loadDebugImages should run on a background thread/isolate to avoid blocking the UI thread.

Workaround

We currently remove LoadNativeDebugImagesIntegration from integrations list to prevent the ANR:

options.integrations.removeWhere(
  (i) => i.runtimeType.toString() == 'LoadNativeDebugImagesIntegration',
);

This sacrifices NDK stacktrace symbolication but eliminates the ANR.

Metadata

Metadata

Assignees

Projects

Status
No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions