KeyMob
All articles

How to actually read an iOS crash log

3 min readCrashesDebugging

A crash log looks unreadable the first few times. It is not — it is just dense, and most of it is irrelevant to your bug. Here is the order to read one in, and the handful of things that tell you almost everything.

Get the log from the right device

Crash reports are written to the device that crashed. Not a similar device, not the one on your desk. Whoever hit the crash has it in their pocket.

There are three ways to it: the device’s own Settings → Privacy & Security → Analytics & Improvements → Analytics Data, which is a long list of cryptically-named files a non-developer will not navigate; a crash-reporting SDK, which reports what it was built to report and nothing else; or a desktop tool that lists them off the device directly. Whichever you use, get the actual file — a screenshot of the alert is not a crash log.

Symbolication first

An unsymbolicated report is addresses:

0 MyApp 0x00000001045a3b2c 0x1044f4000 + 719148

Symbolicated, the same line is a function and a line number. This only works if the symbols match the exact binary that crashed — same version, same build number. Keep the dSYM for every build you ship; without it, your frames stay hexadecimal forever, and no tool can fix that after the fact.

System frames symbolicate from the OS symbols for that iOS version, which is why a report from an iOS version nobody on the team has installed sometimes comes back half-symbolicated.

Read it in this order

1. Exception type

The single most informative line in the file.

Type What happened
EXC_BAD_ACCESS (SIGSEGV) Touched memory that was not there — released object, bad pointer
EXC_CRASH (SIGABRT) Uncaught exception, failed assertion, or a deliberate abort()
EXC_BREAKPOINT (SIGTRAP) Swift runtime trap — force-unwrapped nil, index out of range, failed as!
EXC_RESOURCE Blew a resource limit, usually memory or sustained CPU
0x8badf00d Watchdog: too slow to launch or respond, killed by iOS
0xdead10cc Held a file lock or shared resource while suspended

Those last two are hex jokes — “ate bad food” and “dead lock” — and they are the two most commonly misdiagnosed entries in the list, because neither is a crash in your code.

2. Termination description

When present, this line names the assertion or the exception outright. Read it before you look at any stack. A surprising number of “impossible” crashes are explained in full by this one line.

3. The crashed thread

Marked as such. Ignore the other twenty; they were parked.

4. Your frames

Scan down the crashed thread for your own binary. The system frames above it are usually just the route in — UIApplicationMain, run loop, the delegate call. The topmost frame in your own code is where to start reading.

The two entries that are not crashes

Jetsam reports. The process was killed for memory. The stack shows where it happened to be, not what caused it. There is no bug at the top of that trace; the bug is the memory trend over the preceding minutes. Cross-check with a memory capture — a staircase that never comes back down, ending in a kill, is the signature.

Watchdog terminations. The main thread was blocked long enough that iOS gave up. The backtrace shows the block — usually a synchronous file read, a lock, or a network call on the main thread — but not always why it took so long. 0x8badf00d at launch almost always means work in application(_:didFinishLaunchingWithOptions:).

When the log does not explain it

Three follow-ups, in order:

  1. The log just before. The app’s last few lines are often more useful than the stack. What was it doing?
  2. What else was happening. Usage history around that timestamp: was something else hammering the device?
  3. The device itself. iOS version, free storage, battery health. “Only on that phone” is frequently “only when the disk is nearly full”.

Habits that make this easier

  • Keep every dSYM. Archive them with the build. This is the one thing you cannot fix retroactively.
  • Log a breadcrumb per screen. Cheap, and it turns an unreadable stack into a story.
  • Do not catch and swallow. A crash with a report beats a silent corrupt state.
  • Read jetsam reports as memory bugs, not as crashes. Different investigation entirely.