Why did that app crash?

It happens to the best of apps at times, and in this case it looks like macOS 12.4 broke something for one of my favourite tools. So what can you do when an app crashes frequently or reproducibly? Can you discover what went wrong and work around it? Armed with nothing more than my free log browser Ulbow, this is what I tried.

Save the crash log

There was a time when crashing apps shouted out loud that they had suddenly and unexpectedly quit. These days, you’re lucky if you even see the notification of their passing as they simply vanish from the display. Our first task is to recover the crash log report, without which we’re left totally in the dark.

At the very least, a crashing app should leave a notification to give you access to its crash log. Click on the item at the far right end of the menu bar, normally the clock, to view notifications, and open the stack from Problem Reporter. Click on one of those to open that crash log.

logsb01

If the details aren’t shown, click on the Show Details button. Then click in the text of the report, select all, copy and paste the whole report into TextEdit or a plain text editor, where you can save it. When you report this problem to the app’s developer be sure to enclose that text file, together with a detailed description of what led up to the crash, how it occurred, and all other potentially useful information you can think of.

logsb02

Developers normally try to reproduce the crash as a first step to discover what has gone wrong. What to you may appear quite ordinary about your Mac and its configuration could be very unusual, and provide the clue which enables the developer to make a diagnosis. While they don’t want to know what you ate for breakfast that morning, if your Mac has a custom or unusual configuration, or you’ve installed half a dozen kernel extensions, please let them know.

Check the Unified log

The most important information now is the exact time of the crash, listed as Date/Time near the top. Next, open Ulbow and set its Period up to inspect the log for the five seconds or so immediately before that datestamp. An easy way to do this is to set the time to the round second immediately after that given in the crash log, then set the Period to -5 seconds. With the predicate set at none and your favourite style selected, click on Get log.

logsb03

Scroll to the end of the log excerpt, assuming that the entries go past the time that the crash log recorded. You may find that those 5 seconds generated so many log entries that they’re clipped. In that case, you can shorten the period and untick Limit entries shown in the View menu, so you can scroll past the moment the crash log was generated.

To help you gauge whether you’re going to be overwhelmed with tens of thousands of log entries, two numbers are shown to the right of the Now button. The first of these, in parentheses, tells you how many log entries are in the whole time period, while the second shows the number of lines actually displayed.

You may find that scrolling back from the end enables you to find the crash, and entries leading up to it. If you’re having difficulties, use Ulbow’s Find command. First click somewhere among the log entries, then select the Find… command in the Edit menu. This brings up a new search window which you can use to search for the app’s name. It can be very helpful for the developer to see this log extract; to do that, use the Save… command from the File menu, ensuring that you save it as rich (RTF) rather than plain text.

In this case, the app crash was precipitated by a click or tap, and the log reported normal event dispatching for that, almost 0.1 second before AMFI denied a core dump for the crashing app. Apart from the click that triggered the crash, nothing else appears relevant. Our only hope of improving our insight into what went wrong is to read the crash log.

Check the crash log

Crash logs are notoriously difficult to read, even for developers. Sometimes they contain information to help you discover a workaround; more often than not they’re opaque. Their most pervasive problem is that they contain lots of hex memory addresses which are meaningless until those are converted into readable function names. Only the developer can perform what’s called symbolification, as you need the source code and Xcode. As Apple puts it, “an unsymbolicated crash report is rarely useful.” Its full account of what a developer can do with a crash report is here.

Basic information on the cause of a crash is revealed by details of the Exception that occurred:

  • EXC_BREAKPOINT (SIGTRAP) on ARM and EXC_BAD_INSTRUCTION (SIGILL) on Intel usually indicates an unrecoverable runtime error, these days normally one trapped by the Swift runtime. This means that something went wrong but the code wasn’t able to handle and recover from it.
  • EXC_CRASH (SIGABRT) is normally the result on an abort() call made as a result of an unrecoverable runtime error in Objective-C or C++ code.
  • EXC_CRASH (SIGKILL) occurs when macOS terminates the process, and should be accompanied by a termination reason explained below.
  • EXC_CRASH (SIGQUIT) is seldom seen in macOS, but indicates the process was terminated by another process which is managing its lifetime.
  • EXC_GUARD is also unusual in macOS, and indicates the process violated a guarded system resource.
  • EXC_RESOURCE is unusual in macOS, showing that the process exceeded the limit on a resource, such as CPU, memory, I/O or wakeups.

Useful codes you may see in the Termination Reason include:

  • 0x8badf00d – system watchdog;
  • 0xc00010ff – thermal event;
  • 0xdead10cc – held onto a file lock or SQLite database lock during suspension;
  • 0xbaddd15c – app terminated so that macOS could delete caches to free up disk space;
  • 0xbaadca11 – failed to report a CallKit call in response to PushKit notification (iOS);
  • 0xbad22222 – VoIP app resumed too frequently;
  • 0xc51bad01 – too much CPU used when performing a background task (watchOS);
  • 0xc51bad02 – failed to complete background task in the allocated time (watchOS);
  • 0xc51bad03 – failed to complete background task in the allocated time (watchOS).

With Swift being increasingly used to code apps, errors resulting from its memory and type safety protection are becoming common. These typically follow a standard pattern. On Intel Macs, you’ll normally see:
Exception Type: EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes: 0x0000000000000001, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Termination Reason: Namespace SIGNAL, Code 4 Illegal instruction: 4

and on Apple silicon Macs the equivalent is:
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x0000000100d86f80
Termination Reason: Namespace SIGNAL, Code 5 Trace/BPT trap: 5

where the Exception Codes could be completely different.

Sadly, understanding why an app crashed seldom provides any workaround, but at least you’re better informed, and can let its developer know of the problem.