Journal · 2 May 2026

Reading an Android tombstone without drowning in the unwind

Native crashes arrive as tombstones. The useful part is smaller than the file looks: abort message, fault address, and the first frames that belong to your .so files.

Close view of a smartphone screen held above a marble surface

Start at the abort, not at frame forty

A tombstone is a post-mortem from the Android debuggerd. Engineers new to native crashes often scroll the unwind until their eyes blur. We start at the abort message and the signal. SIGSEGV on a known null is a different conversation from SIGABRT after a failed CHECK in a third-party .so.

If the abort names a codec, a renderer, or a vendor graphics library, the next question is OEM skew, not whether your Kotlin wrapper “looks correct.”

Which frames to keep in the finding pack

The finding pack should name the first frames that belong to libraries you ship, then the vendor frames that actually faulted if the crash is inside a GPU driver. Dumping the entire unwind into a ticket trains nobody.

Missing symbols are not a personality flaw. They mean the .so was stripped and nobody kept the unstripped copy for that version. Say that plainly. A crash stability audit that pretends unknown frames are understood is worse than a short pack that marks them unknown.

JNI as the usual bridge

Many “native” crashes in consumer Android apps are JNI misuse: a local ref released twice, a UTF conversion on a null, a callback into Java after the object was collected. If the tombstone’s backtrace crosses libart into your JNI_OnLoad or a named native method, that is the paragraph the readout should linger on.

Request an audit briefing if this is the cluster on your desk this week.