Skip to content

Android TombstoneMerged event loses app native debug image symbolication after enableTombstone #5710

Description

@zourw

Description

When enableTombstone is enabled in a Flutter Android app, a native SIGSEGV crash is reported as a TombstoneMerged event. Compared with the regular native signalhandler event for the same crash, the merged tombstone event no longer contains the app native library debug image in debugmeta.images, so Sentry cannot symbolicate the app frame to the source file and line.

The regular native crash resolves to native_crash.cpp, but the TombstoneMerged event only shows the JNI symbol/address.

Expected behavior

The merged tombstone event should preserve enough native debug image information from the original native SDK event, or include the app native library image parsed from the tombstone, so the app frame can still be symbolicated to source file and line.

Actual behavior

Regular native crash event (mechanism=signalhandler) is symbolicated:

  • Event ID: a0fc8e59e5a84631746cfb62af3cf072
  • SDK: sentry.native.android.flutter 0.14.2
  • App native debug image is present and found:
    • debug id: 2387c138-d607-9c92-9829-9c0adb12b9fd
    • status: found
    • has_debug_info=true, has_sources=true, has_symbols=true, has_unwind_info=true
  • Top app frame resolves to:
    • function: Java_com_example_sentry_1demo_MainActivity_triggerNativeSegfault
    • file: native_crash.cpp
    • path: android/app/src/main/cpp/native_crash.cpp

Tombstone merged event is not symbolicated to source file/line:

  • Event ID: ba490da4525e4ec519f0fbf27c1966be
  • SDK: sentry.native.android.flutter 0.15.2
  • mechanism: TombstoneMerged
  • Crashing app frame is shown as:
    • base.apk!libsentry_demo_native.so 0x79e0279618 Java_com_example_sentry_1demo_MainActivity_triggerNativeSegfault
  • The event does not include the app native debug image 2387c138-d607-9c92-9829-9c0adb12b9fd in debugmeta.images.
  • Missing images are mostly Android system/ART/OAT files, for example boot.oat, boot-framework.oat, libc.so, libandroid_runtime.so.

Relevant debugmeta comparison

Regular signalhandler event includes the app native image and Sentry finds uploaded symbols:

{
  "type": "elf",
  "code_file": "/data/app/.../base.apk",
  "debug_id": "2387c138-d607-9c92-9829-9c0adb12b9fd",
  "debug_status": "found",
  "features": {
    "has_debug_info": true,
    "has_sources": true,
    "has_symbols": true,
    "has_unwind_info": true
  }
}

TombstoneMerged event has no image with that debug id. Its missing images are system/ART/OAT images, for example:

{
  "code_file": "/system/framework/arm64/boot-framework.oat",
  "debug_status": "missing"
}

Minimal reproduction

Flutter init:

await SentryFlutter.init(
  (options) {
    options.dsn = '<dsn>';
    options.release = 'sentry_demo@1.0.0+1';
    options.dist = '1';
    options.enableTombstone = true;
    options.attachThreads = true;
  },
  appRunner: () => runApp(const App()),
);

Dart trigger:

static const _nativeChannel = MethodChannel('sentry_demo/native');

Future<void> triggerNativeCrash() async {
  await _nativeChannel.invokeMethod('triggerRealNativeCrash');
}

Android MainActivity.kt:

class MainActivity : FlutterActivity() {
    external fun triggerNativeSegfault()

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "sentry_demo/native")
            .setMethodCallHandler { call, result ->
                if (call.method == "triggerRealNativeCrash") {
                    triggerNativeSegfault()
                } else {
                    result.notImplemented()
                }
            }
    }

    companion object {
        init {
            System.loadLibrary("sentry_demo_native")
        }
    }
}

Native crash source:

#include <jni.h>

extern "C" JNIEXPORT void JNICALL
Java_com_example_sentry_1demo_MainActivity_triggerNativeSegfault(JNIEnv *, jobject) {
  volatile int *crash_address = nullptr;
  *crash_address = 1;
}

Release build setup:

  • Flutter app using sentry_flutter: ^9.22.0
  • Android release build with R8 enabled
  • Native debug symbols uploaded to Sentry
  • Android 12 device
  • Native SIGSEGV triggered from JNI/C++

Why this looks related to the tombstone merge path

From PR #5037, the merge strategy appears to replace debugMeta, exception, and threads with data from the tombstone parser. In this case, the original native SDK event had a found app debug image, but the TombstoneMerged event does not. It looks like the merge/parsing path drops or fails to reconstruct the app native image required for symbolication.

Additional note

The ProGuard UUID is present in the tombstone merged event, but Java/OAT frames such as P.a.onMethodCall also remain obfuscated in the stack. The main blocker here is that the app native frame no longer resolves to source file/line after the event becomes TombstoneMerged.

Metadata

Metadata

Assignees

No one assigned

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Waiting for: Product Owner

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions