现在的位置: 首页 > 综合 > 正文

CrashReporter

2013年01月15日 ⁄ 综合 ⁄ 共 13302字 ⁄ 字号 评论关闭

 

CrashReporter is a debugging facility in Mac OS X that logs information about all programs that crash. This technote describes CrashReporter in detail. It includes a description of the crash logs generated by CrashReporter, and how you can use these logs to
debug your program.

This technote is useful for anyone who develops Mac OS X user space software.

Introduction

Mac OS X's CrashReporter is a useful facility for learning about problems your application is experiencing in the field. CrashReporter performs two useful actions:

  • When a program crashes, CrashReporter will record a crash log (typically into ~/Library/Logs/CrashReporter/),
    and inform the user by logging a message to the system logging facility.

  • In addition, if the program that crashed is running as a logged in GUI user, CrashReporter will present the user with a dialog asking them whether they want to submit a bug report to Apple (see Figure
    1
    ). If the user clicks the Report button, CrashReporter displays another dialog that shows the details of the report (see Figure 2) and allows
    them to comment it before submission.

Figure 1: First CrashReporter dialog

Figure 1, First CrashReporter dialog

Figure 2: Second CrashReporter dialog

Figure 2, Second CrashReporter dialog

In this technote I explain how to interpret crash logs that you have obtained from end users. In the first section I explain each part of the crash log in
detail. Following that I show you how you can get useful information from a crash log even if your program ships without debugging symbols. Then I explain
how to use CrashReporterPrefs to customize CrashReporter's behavior. Finally, I explain some limitations of
the current implementation.

IMPORTANT: This technote describes CrashReporter as it's implemented in Mac OS X 10.5. CrashReporter has evolved over time, and there are numerous differences between the current version and earlier ones. I've called out these
changes where they are significant.

Note: CrashReporter has limited support for some deprecated technologies, most notably the PEF binary image format. This technote does not describe that support.

Crash Log Placement

CrashReporter usually places the crash log in the user's home directory, as explained above. However, under some circumstances it will put the crash log in /Library/Logs/CrashReporter/.
These include:

  • if it can't determine the ownership of the crashed process

  • if the crashed process was owned by root

  • if the user's home directory is not available or not writable

Each crash log is written to a separate file. The file name is of the form PPP_YYYY-MM-DD-HHMMSS_NNN.crash, where PPP is the process name; YYYY, MM, DD,
HH, MM, and SS are the date and time of the crash; and NNN is the host name. For example, TextEdit_2008-01-29-143702_guy-smiley.crash is a
TextEdit crash from 29 Jan 2008 at 14:37:02 on the machine "guy-smiley". To prevent unbounded disk use, CrashReporter limits the number of crash log files for any given combination of user, process name and host name. The current limit is 20.

IMPORTANT: CrashReporter also creates other files within the CrashReporter directory. Specifically, it creates files of the form .PPP_NNN_CrashHistory.plist (for
example, .TextEdit_guy-smiley_CrashHistory.plist). These files are invisible in the Finder. You must not rely on the presence or format of these files.

Note: Prior to Mac OS X 10.5 CrashReporter created files of the form PPP.crash.log, where PPP is the process name. All crash
logs for a given process name were appended to that file. For example, all TextEdit crashes were logged in TextEdit.crash.log. There was nothing to prevent
these files from growing without bound.

Back to Top 

CrashReporter Logging

CrashReporter logs via Apple System Log. All CrashReporter log entries have the Facility set to "Crash Reporter". You can display all such message with the command shown in Listing
1
.

Listing 1: Displaying all CrashReporter log entries

$ syslog -k Facility eq "Crash Reporter"
[…]
[…] Formulating crash report for process TextEdit[9803]
[…] Saved crashreport to /Users/quinn/Library/Logs/CrashReporter/\
TextEdit_2008-01-29-204411_guy-smiley.crash using uid: 2000 gid: 2000, \
euid: 2000 egid: 2000

You can display recent log entries and wait for more to show up (much like tail -f), with the command shown in Listing
2
.

Listing 2: Waiting for new CrashReporter log entries

$ syslog -w -k Facility eq "Crash Reporter"
[…]

You can do the same thing from the Console application by choosing New Log Database Query from the File menu and then configuring the query to search for log messages where the Facility is "Crash Reporter".

Note: Prior to Mac OS X 10.5 CrashReporter logged to both the system log (/var/log/system.log) and its own specific log file (/var/log/crashreporter.log).

Back to Top 

Crash Log Versions

Crash logs include a version number that you can use to interpret the information contained in the log. This version number is loosely
related to the system version. Table 1 shows that relationship.

Table 1: Crash Log Versions

Crash Log Version Mac OS X Versions
1 prior to 10.3.2
2 10.3.2 through 10.3.9
3 10.4.x on PowerPC
4 10.4.x on Intel
5 none
6 10.5 and later

Back to Top 

Anatomy of a Crash Log

A crash log has a number of different parts; in the following sections I describe each part in detail.

Process Information

The first part of the crash log contains information about the process that crashed, as illustrated in Listing 3.

Listing 3: Process information

Process:         TextEdit [8752]
Path:            /Applications/TextEdit.app/Contents/MacOS/TextEdit
Identifier:      com.apple.TextEdit
Version:         1.5 (244)
Build Info:      TextEdit-2440000~2
Code Type:       X86 (Native)
Parent Process:  launchd [241]

The most important thing to note here is the name of the process that crashed. In some cases the actual process that died is not what you think. For example, if your application uses a helper tool to do some work, and that helper tool dies, you want to focus
on the helper tool's code and not waste time debugging the application code.

The "Process" field includes the name and PID (in square brackets) of the crashed process. The "Path" field is the path to the process's executable.

The "Identifier" field contains the bundle identifier, if any, of the crashed process.

CrashReporter gets the "Version" field from the process's executable. If the process is a packaged application, the version is composed of the CFBundleShortVersionString and CFBundleVersion properties
from itsInfo.plist file. If the process is single file application, the version is derived from its 'vers' ID=1
resource.

The "Build Info" field shows information extracted from the version.plist file in the application's bundle, if any. This file, and hence this
field, is typically only present for Apple applications.

The "Code Type" field shows the type of code that was executing. If your program is a universal binary, you should check this field to see which architecture was actually being run (for example, the user might have accidentally run your program using Rosetta).

The "Parent Process" field includes the name and the PID (in square brackets) of the parent process. It's worth checking that this is what you'd expect it to be.

Note: In crash logs prior to version 6 the "Process" field was known as the "Command" field, and the process ID was included in a separate "PID" field.

Note: The "Path" and "Version" fields were introduced with version 2 crash logs.

Note: The "Identifier" field was introduced with version 6 crash logs.

Note: In crash logs prior to version 6 the information in the "Build Info" field was split across three separate fields: "Build Version", "Project Name" and "Source Version". These fields were introduced with version 3 crash logs.

Note: The "Code Type" field was introduced with version 6 crash logs.

Note: In crash logs prior to version 6 the "Parent Process" field was named "Parent". It was first included in version 3 crash logs.

Note: In crash logs prior to version 6 the process information part of the crash log was placed after the basic information part.

Back to Top 

Basic Information

The next part of the crash log contains information about the crash log itself. You can see an example in Listing 4.

Listing 4: Basic information

Date/Time:       2008-01-29 12:32:46.239 +0000
OS Version:      Mac OS X 10.5.1 (9B18)
Report Version:  6

The most important piece of information here is the OS version. You should pay particular attention to the build number; each user-visible version of Mac OS X can have multiple variants distinguished only by their build numbers (this typically happens for hardware-specific
system releases). In addition, make sure to look at the time and date to see if there are any suspicious patterns: if you get lots of crash logs that all occur at 12:00, you probably need to investigate your time handling code.

The "Report Version" field is discussed in Crash Log Versions.

Note: Version 2 crash logs included a "Host Name" field in this section; this is not present in later versions.

Back to Top 

Exception Information

The third part of the crash log shows information about the processor exception that was the immediate cause of the crash. Listing 5 shows
a typical example.

Listing 5: Exception information

Exception Type:  EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x0000000000000000
Crashed Thread:  0

The "Crashed Thread" field denotes the thread that crashed; it is redundant because the backtrace section highlights the crashing
thread.

Note: The "Crashed Thread" field was introduced in version 2 crash logs. Prior to version 6 crash logs it was called "Thread" and it appeared in the process information section.

The most common forms of exception are:

  • EXC_BAD_ACCESS/KERN_INVALID_ADDRESS — This is caused by the thread
    accessing unmapped memory. It may be triggered by either a data access or an instruction fetch; the Thread State section describes
    how to tell the difference.

  • EXC_BAD_ACCESS/KERN_PROTECTION_FAILURE — This is caused by the
    thread trying to write to read-only memory. This is always caused by a data access.

  • EXC_BAD_INSTRUCTION — This is caused by the thread executing an illegal instruction.

  • EXC_ARITHMETIC/EXC_I386_DIV — This is caused by the thread doing
    an integer divide by zero on an Intel-based computer.

For memory access exceptions (EXC_BAD_ACCESS) the exception part of the crash log contains the address that triggered the exception
(the exception address). In Listing 5 that address is 0x0000000000000000.

Back to Top 

Backtrace Information

The fourth part of the crash log, which displays a backtrace for all of the threads in the crashed process, is typically the most interesting. Listing
6
 shows an example.

Listing 6: Backtrace information

Thread 0 Crashed:
0   ???                             0000000000 0 + 0
1   com.apple.CoreFoundation        0x942cf0fe CFRunLoopRunSpecific + 18…
2   com.apple.CoreFoundation        0x942cfd38 CFRunLoopRunInMode + 88
3   com.apple.HIToolbox             0x919e58a4 RunCurrentEventLoopInMode…
4   com.apple.HIToolbox             0x919e56bd ReceiveNextEventCommon + …
5   com.apple.HIToolbox             0x919e5531 BlockUntilNextEventMatchi…
6   com.apple.AppKit                0x9390bd5b _DPSNextEvent + 657
7   com.apple.AppKit                0x9390b6a0 -[NSApplication nextEvent…
8   com.apple.AppKit                0x939046d1 -[NSApplication run] + 79…
9   com.apple.AppKit                0x938d19ba NSApplicationMain + 574
10  com.apple.TextEdit              0x00001df6 0x1000 + 3574

In this example there is only one thread, so there's only one backtrace. In a multi-threaded process, there is one backtrace per thread. Thus, it's critical that you identify the thread that crashed. CrashReporter makes this easy by tagging that backtrace with
the text "Thread <ThreadNumber> Crashed:". However, it's easy to overlook this text and erroneously assume that the Thread 0 is the one that crashed.

Note: Your process may be multi-threaded even if you don't explicitly create any threads. Various frameworks can create threads on your behalf. For example, CFSocket creates a thread to integrate sockets with the runloop.

Each line of the backtrace describes a nested function invocation (a frame), with the most recently executed function at the top and the least recently executed at the bottom. For each frame, the columns in the backtrace are as
follows.

  • The first column is the frame number, starting at 0 (indicating the function that crashed) and incrementing for each nested function call.

  • The second column is the name of the binary image containing the code executing in this frame; this is derived by cross referencing the program counter address (from the next column) with the list of loaded
    binary images
    .

  • The third column is the program counter address within the frame. For frame 0 this is typically the address of the instruction that caused the exception. For higher frames this is the return address for that frame. That is, for frame N it points to the next
    instruction that will execute when the function referenced by frame N - 1 returns.

  • The fourth column is the symbolic name for the program counter address given in the third column. If you strip debugging symbols before shipping your application to end users, this column will just contain a hex number. You can work out the corresponding symbolic
    name using the technique described later in this document.

Finally, if your program is multi-threaded, you can often identify which thread is which by looking at the symbolic names deep within the backtrace. For example, in Listing
6
, frame 9 lists NSApplicationMain as its symbolic address, indicating that this thread is the main thread. In contrast, the deepest frame
for a pthread is always the routine _pthread_start.

Back to Top 

Thread State

The next part of the crash log contains a dump of the processor state of the thread that crashed. Listing 7 shows an example of
this for PowerPC.

Listing 7: PowerPC thread state

Thread 0 crashed with PPC Thread State 64:
  srr0: 0x0000000000000000 srr1: 0x000000004000d030                  …
    cr: 0x44022282           …                 lr: 0x000000009000a6bc…
    r0: 0x00000000ffffffe1   r1: 0x00000000bfffeb10   r2: 0x00000000a…
    r4: 0x0000000003000006   r5: 0x0000000000000000   r6: 0x000000000…
    r8: 0x0000000000000000   r9: 0x0000000000000000  r10: 0x000000000…
   r12: 0x000000009000a770  r13: 0x0000000000000000  r14: 0x000000000…
   r16: 0x0000000000000000  r17: 0x0000000000000000  r18: 0x000000000…
   r20: 0x00000000101a7026  r21: 0x00000000be5b19d8  r22: 0x000000000…
   r24: 0x0000000000000450  r25: 0x0000000000001203  r26: 0x000000000…
   r28: 0x0000000000000000  r29: 0x0000000003000006  r30: 0x000000000…

To get the most out of this information, you need a good understanding of the Mac OS X application binary interface (ABI) for the processor. For a detailed description, see Mac
OS X ABI Function Call Guide
. However, there are some simple ways get useful results without a full understanding of the ABI.

PowerPC Architecture

For PowerPC-based computers, you should consider the following points: