iOS 應用程式 Crash檔案的理解與分析
Understanding and Analyzing iOS Application Crash Reports
https://developer.apple.com/library/ios/technotes/tn2151/_index.html
Understanding and Analyzing iOS Application Crash Reports
When an application crashes, a crash report is created which is very useful for understanding what caused the crash. This document contains essential information about how to symbolicate, understand, and interpret crash reports.
Introduction
When an application crashes on an iOS device, a crash report is created and stored on the device. Crash reports describe the conditions under which the application terminated, in most cases including a complete backtrace for each executing thread, and are typically very useful for debugging issues in the application. If you are an iOS developer, you should look at these crash reports to understand what crashes your application is having, and then try to fix them.
Crash reports with backtraces need to be symbolicated before they can be analyzed. Symbolication replaces memory addresses with human-readable function names and line numbers.
If you get crash logs off a device through Xcode's Devices window, then they will be symbolicated for you automatically after a few seconds. Otherwise you will need to symbolicate the .crash
A Low Memory report differs from other crash reports in that there are no backtraces in this type of report. When a low memory crash happens, you must investigate your memory usage patterns and your responses to low memory warnings. This document points to you several memory management references that you might find useful.
Acquiring Crash and Low Memory Reports
discusses how to retrieve crash and low memory reports directly from an iOS device.
in the discusses how to view aggregate crash reports collected from TestFlight beta testers and users who have downloaded your app from the App Store.
Analyzing Crash Reports
This section discusses each of the sections found within a standard crash report.
Header
Every crash report begins with a header.
Excerpt of the header from a crash report.
Incident Identifier: E6EBC860-0222-4B82-BF7A-2B1C26BE1E85 |
CrashReporter Key: 6196484647b3431a9bc2833c19422539549f3dbe |
Hardware Model: iPhone6,1 |
Process: TheElements [4637] |
Path: /private/var/mobile/Containers/Bundle/Application/5A9E4FC2-D03B-4E19-9A91-104A0D0C1D44/TheElements.app/TheElements |
Identifier: com.example.apple-samplecode.TheElements |
Version: 1.12 |
Code Type: ARM (Native) |
Parent Process: launchd [1] |
Date/Time: 2015-04-06 09:14:08.775 -0700 |
Launch Time: 2015-04-06 09:14:08.597 -0700 |
OS Version: iOS 8.1.3 (12B466) |
Report Version: 105 |
Most of the fields are self-explanatory but a few deserve special note:
-
Incident Identifier: A unique identifier for the report. Two reports will never share the same Incident Identifier.
-
CrashReporter Key: An anonymized per-device identifier. Two reports from the same device will contain identical values.
-
Process: The executable name for the process that crashed. This matches the value for the
CFBundleExecutable
key in the application's information property list. -
Version: The version of the process that crashed. The value for this key is a concatenation of the values for the
CFBundleVersion
andCFBundleVersionString
keys in the application's information property list. -
Code Type: The target architecture of the process that crashed. This will be one of
ARM-64
orARM
. -
OS Version: The OS version, including the build number, on which the crash occurred.
Exception Codes
Not to be confused with Objective-C/C++ exceptions (though one of those may be the cause of the crash). This section lists the Mach Exception Type, Exception Subtype, processor-specificException Codes, and other fields which may provide more information about the nature of the crash. The final field lists the index of the thread which triggered the crash. Not all fields will be present in every crash report.
Excerpt of the Exception Codes section from a crash report.
Exception Type: EXC_CRASH (SIGABRT) |
Exception Codes: 0x0000000000000000, 0x0000000000000000 |
Triggered by Thread: 0 |
The following sections explain some of the most common exception types.
Bad Memory Access [EXC_BAD_ACCESS // SIGSEGV // SIGBUS]
The process attempted to access invalid memory. The Exception Subtype field contains a kern_return_t
describing
error. The Exception Sub-code (the value following the exception subtype) lists the bad memory address that was accessed.
If objc_msgSend
or objc_release
is
near the top of the for
the crashed thread, the process may have attempted to message a deallocated object. You should profile the application with the to better understand the conditions of this crash.
Abnormal Exit [EXC_CRASH // SIGABRT]
The process exited abnormally. The most common cause of crashes with this exception type are uncaught Objective-C/C++ exceptions.
App Extensions will be terminated with this exception type if they take too much time to initialize (a watchdog termination). If an extension is killed due to a hang at launch, the Exception
Subtype of the generated crash report will be LAUNCH_HANG
. Because extensions do not have a main
function,
any time spent initializing occurs within static constructors and +load
methods present in your extension and dependent libraries. You should defer as much
of this work as possible.
Trace Trap [EXC_BREAKPOINT // SIGTRAP]
Similar to an Abnormal Exit, this exception is intended to give an attached debugger the chance to interrupt
the process at a specific point in its execution. You can trigger this exception from your own code using the __builtin_trap()
function. If no debugger is
attached, the process is terminated and a crash report is generated.
Swift code will terminate the program with this exception type if it detects an unexpected condition at runtime such as:
-
a non-optional type with a nil value
-
a failed forced type conversion
Look at the of the crashed thread to determine where the unexpected condition was encountered. Additional information may have also been logged to the device's console.
Guarded Resource Violation [EXC_GUARD]
The process violated a guarded resource protection. System libraries may mark certain file descriptors as guarded, after which normal operations on those descriptors will
trigger an EXC_GUARD
exception (when it wants to operate on these file descriptors, the system uses special 'guarded' private APIs). This helps you quickly
track down issues such as closing a file descriptor that had been opened by a system library. For example, if an app closes the file descriptor used to access the SQLite file backing a Core Data store, Core Data would then crash mysteriously much later on.
The guard exception gets these problems noticed sooner, and thus makes them easier to debug.
The associated Exception Subtype is a bitfield which breaks down as follows:
-
[63:61] - Guard Type: The type of the guarded resource. A value of
0x2
indicates the resource is a file descriptor. -
[60:32] - Flavor: The conditions under which the violation was triggered.
-
If the first
(1 << 0)
bit is set, the process attempted to invokeclose()
on a guarded file descriptor. -
If the second
(1 << 1)
bit is set, the process attempted to invokedup()
,dup2()
, orfcntl()
with theF_DUPFD
orF_DUPFD_CLOEXEC
commands on a guarded file descriptor. -
If the third
(1 << 2)
bit is set, the process attempted to send a guarded file descriptor via a socket. -
If the fifth
(1 << 4)
bit is set, the process attempted to write to a guarded file descriptor.
-
-
[31:0] - File Descriptor: The guarded file descriptor that the process attempted to modify.
Resource Limit [EXC_RESOURCE]
The process hit a resource consumption limit. This is not a crash, but a notification from the OS that the process is using too many resources. The exact resource is in the Exception Subtype field.
-
The exception subtype
WAKEUPS
indicates that a thread was waking up too many times per second, which forces the CPU to wake up very often and consumes battery life. You should investigate why this is happening and avoid it if possible. -
The exception subtype
MEMORY
indicates that the process has crossed a memory limit imposed by the system. This may be a precursor to termination for excess memory usage.
Other Exception Types
Some crash reports may contain an un-named Exception Type, which will be printed as a hexadecimal value (e.g. 00000020). If you receive one of these crash reports, look directly to the Exception Codes field for more information.
-
The exception code
0xbaaaaaad
indicates that the log is a stackshot of the entire system, not a crash report. To take a stackshot, push the Home button and any volume button. Often these logs are accidentally created by users, and do not indicate an error. -
The exception code
0xbad22222
indicates that a VoIP application has been terminated by iOS because it resumed too frequently. -
The exception code
0x8badf00d
indicates that an application has been terminated by iOS because a watchdog timeout occurred. The application took too long to launch, terminate, or respond to system events. One common cause of this is doing . Whatever operation is onThread 0
needs to be moved to a background thread, or processed differently, so that it does not block the main thread. -
The exception code
0xc00010ff
indicates the app was killed by the operating system in response to a thermal event. This may be due to an issue with the particular device that this crash occurred on, or the environment it was operated in. For tips on making your app run more efficiently, see WWDC session. -
The exception code
0xdead10cc
indicates that an application has been terminated by iOS because it held on to a system resource (like the address book database) while running in the background. -
The exception code
0xdeadfa11
indicated that an application has been force quit by the user. Force quits occur when the user first holds down the On/Off button until "slide to power off" appears, then holds down the Home button. It's reasonable to assume that the user has done this because the application has become unresponsive, but it's not guaranteed - force quit will work on any application.Note: Terminating a suspended app by removing it from the multitasking tray does not generate a crash report. Once an app has suspended, it is eligible for termination by iOS at any time, so no crash report will be generated.
Application Specific Information
Certain crashes write extra information out to the generated crash report. The contents of this section vary depending upon the type of termination. You should read this section to better understand the circumstances under which the application was terminated.
Excerpt of the Application Specific Information section from a crash report.
Application Specific Information: |
MyApp[134] was suspended with locked system files: |
/private/var/mobile/Library/AddressBook/AddressBook.sqlitedb |
Backtrace
The most interesting part of a crash report is the backtrace for each of the process's threads at the time execution halted. Each of these traces is similar to what you would see when stopping execution in the debugger.
Excerpt of the Backtrace section from a fully symbolicated crash report.
Thread 0 name: Dispatch queue: com.apple.main-thread |
Thread 0 Crashed: |
0 TheElements 0x0000000100063fdc -[AtomicElementViewController myTransitionDidStop:finished:context:] (AtomicElementViewController.m:201) |
1 UIKit 0x000000018ca5c2ec -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 184 |
2 UIKit 0x000000018ca5c1f4 -[UIViewAnimationState animationDidStop:finished:] + 100 |
3 QuartzCore 0x000000018c380f60 CA::Layer::run_animation_callbacks(void*) + 292 |
4 libdispatch.dylib 0x0000000198fb9368 _dispatch_client_callout + 12 |
5 libdispatch.dylib 0x0000000198fbd97c _dispatch_main_queue_callback_4CF + 928 |
6 CoreFoundation 0x000000018822dfa0 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 8 |
7 CoreFoundation 0x000000018822c048 __CFRunLoopRun + 1488 |
8 CoreFoundation 0x00000001881590a0 CFRunLoopRunSpecific + 392 |
9 GraphicsServices 0x00000001912fb5a0 GSEventRunModal + 164 |
10 UIKit 0x000000018ca8aaa0 UIApplicationMain + 1484 |
11 TheElements 0x000000010005d800 main (main.m:55) |
12 libdyld.dylib 0x0000000198fe2a04 start + 0 |
Thread 1 name: Dispatch queue: com.apple.libdispatch-manager |
Thread 1: |
0 libsystem_kernel.dylib 0x00000001990e0c94 kevent64 + 8 |
1 libdispatch.dylib 0x0000000198fc897c _dispatch_mgr_invoke + 272 |
2 libdispatch.dylib 0x0000000198fbb3b0 _dispatch_mgr_thread + 48 |
... |
Symbolication
Crash logs retrieved from an iOS device will not contain the method or function names, known as symbols. Instead, you have hexadecimal addresses of executable code within the loaded binary images, as shown in Listing 5. You need to map these addresses to symbols.
Symbolication - resolving backtrace addresses to source code methods and lines - requires the application binary that was uploaded to the App Store and the .dSYM
file
that was generated when that binary was built. This must be an exact match - otherwise, you might get a partially symbolicated crash report, as shown in Listing
6. It is essential that you keep each build distributed to users (regardless of the details of that distribution) with its .dSYM
file.
Excerpt of a backtrace section from an unsymbolicated crash report.
Thread 0 name: Dispatch queue: com.apple.main-thread |
Thread 0 Crashed: |
0 TheElements 0x00000001000effdc 0x1000e4000 + 49116 |
1 UIKit 0x000000018ca5c2ec 0x18ca14000 + 295660 |
2 UIKit 0x000000018ca5c1f4 0x18ca14000 + 295412 |
3 QuartzCore 0x000000018c380f60 0x18c36c000 + 85856 |
4 libdispatch.dylib 0x0000000198fb9368 0x198fb8000 + 4968 |
5 libdispatch.dylib 0x0000000198fbd97c 0x198fb8000 + 22908 |
6 CoreFoundation 0x000000018822dfa0 0x188150000 + 909216 |
7 CoreFoundation 0x000000018822c048 0x188150000 + 901192 |
8 CoreFoundation 0x00000001881590a0 0x188150000 + 37024 |
9 GraphicsServices 0x00000001912fb5a0 0x1912f0000 + 46496 |
10 UIKit 0x000000018ca8aaa0 0x18ca14000 + 486048 |
11 TheElements 0x00000001000e9800 0x1000e4000 + 22528 |
12 libdyld.dylib 0x0000000198fe2a04 0x198fe0000 + 10756 |
Excerpt of a backtrace section from a partially symbolicated crash report (The system stack frames are symbolicated, but the app's stack frames are not).
Thread 0 name: Dispatch queue: com.apple.main-thread |
Thread 0 Crashed: |
0 TheElements 0x00000001000effdc 0x1000e4000 + 49116 |
1 UIKit 0x000000018ca5c2ec -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 184 |
2 UIKit 0x000000018ca5c1f4 -[UIViewAnimationState animationDidStop:finished:] + 100 |
3 QuartzCore 0x000000018c380f60 CA::Layer::run_animation_callbacks(void*) + 292 |
4 libdispatch.dylib 0x0000000198fb9368 _dispatch_client_callout + 12 |
5 libdispatch.dylib 0x0000000198fbd97c _dispatch_main_queue_callback_4CF + 928 |
6 CoreFoundation 0x000000018822dfa0 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 8 |
7 CoreFoundation 0x000000018822c048 __CFRunLoopRun + 1488 |
8 CoreFoundation 0x00000001881590a0 CFRunLoopRunSpecific + 392 |
9 GraphicsServices 0x00000001912fb5a0 GSEventRunModal + 164 |
10 UIKit 0x000000018ca8aaa0 UIApplicationMain + 1484 |
11 TheElements 0x00000001000e9800 0x1000e4000 + 22528 |
12 libdyld.dylib 0x0000000198fe2a04 start + 0 |
Important: You must keep both the application binary and the .dSYM
file
in order to be able to fully symbolicate crash reports. You should archive these files for every build that you submit to iTunes Connect. The .dSYM
and application
binary are specifically tied together on a per-build-basis, and subsequent builds, even from the same source files, will not interoperate with files from other builds. If you use Xcode's Build and Archive command then they will be placed in a suitable location
automatically. Otherwise any location searchable by Spotlight (such as your home directory) is fine.
Xcode's Archive command makes it easy keeping the matching binary and the .dSYM
. When you use the Archive
command (by choosing "Archive" from the "Product" menu), Xcode will gather the application binary and the .dSYM
containing symbol information together and
store them in a location in your home folder. You can find all of your archived applications in the Xcode Organizer under the "Archived" section. Xcode will automatically search archived applications when symbolicating crash reports, and you can submit archived
applications directly to iTunes Connect ensuring that the application and .dSYM
that you have archived match what you release.
Xcode will automatically symbolicate all crash reports that it encounters, if it has the .dSYM
and application
binary that produced the crash report. Given a crash report, the matching binary, and its.dSYM
file, all you need to do for symbolication is to add the crash
report to the Xcode Organizer.
-
Connect an iOS device to your Mac
-
Choose "Devices" from the "Window" menu
-
Under the "DEVICES" section in the left column, choose a device
-
Click the "View Device Logs" button under the "Device Information" section on the right hand panel
-
Drag your crash report onto the left column of the presented panel
-
Xcode will automatically symbolicate the crash report and display the results
Exceptions
Exceptions in Objective-C are used to indicate programming or unexpected runtime errors such as out-of-bounds collection access, attempts to mutate immutable objects, not implementing a required method of a protocol, or sending an invalid message.
Note: Messaging a previously deallocated object may raise an NSInvalidArgumentException
instead
of immediately crashing the program. This occurs when a new object is allocated in the memory previously occupied by the deallocated object. If your application is crashing due to an uncaught NSInvalidArgumentException
(look
for -[NSObject(NSObject) doesNotRecognizeSelector:]
in the exception backtrace), consider profiling your application with the to eliminate the possibility that improper memory management is the cause.
If an exception is not caught, it is intercepted by a function called the uncaught exception handler. The default uncaught exception handler on iOS logs the exception information and backtrace to the device's console then terminates the program. Only the exception backtrace of the last uncaught exception is written to the generated crash report under the Last Exception Backtrace section, as shown in Listing 7. The exception message is omitted from the crash report. If you receive a crash report with a Last Exception Backtrace section you should acquire the console logs from the originating device to better understand the conditions leading to the exception being raised.
Excerpt of the Last Exception Backtrace section from an unsymbolicated crash report.
Last Exception Backtrace: |
(0x18632c2d8 0x197af80e4 0x18632bf5c 0x187165480 0x186257520 0x18b18c7a0 0x18b088384 0x18ad6ca28 0x18ad6c994 0x18af0f25c 0x18ae21ef0 0x18ae21cbc 0x18ae21c3c 0x18ad69760 0x18a6b1e1c 0x18a6ac884 0x18a6ac728 0x18a6abebc 0x18a6abc3c 0x18a6a5364 0x1862e42a4 0x1862e1230 0x1862e1610 0x18620d2d4 0x18fa2b6fc 0x18add2fac 0x1000fd2f4 0x198176a08) |
A crash log with a Last Exception Backtrace containing only hexadecimal addresses must be symbolicated to produce a usable backtrace as shown in Listing 8.
Excerpt of the Last Exception Backtrace section from a symbolicated crash report. This exception was raised when loading a scene in the app's storyboard. The corresponding IBOutlet for a connection to an element in the scene was missing.
Last Exception Backtrace: |
0 CoreFoundation 0x18632c2d8 __exceptionPreprocess + 132 |
1 libobjc.A.dylib 0x197af80e4 objc_exception_throw + 60 |
2 CoreFoundation 0x18632bf5c -[NSException raise] + 12 |
3 Foundation 0x187165480 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 248 |
4 CoreFoundation 0x186257520 -[NSArray makeObjectsPerformSelector:] + 248 |
5 UIKit 0x18b18c7a0 -[UINib instantiateWithOwner:options:] + 1604 |
6 UIKit 0x18b088384 -[UIViewController _loadViewFromNibNamed:bundle:] + 284 |
7 UIKit 0x18ad6ca28 -[UIViewController loadViewIfRequired] + 88 |
8 UIKit 0x18ad6c994 -[UIViewController view] + 32 |
9 UIKit 0x18af0f25c -[UINavigationController _startCustomTransition:] + 712 |
10 UIKit 0x18ae21ef0 -[UINavigationController _startDeferredTransitionIfNeeded:] + 468 |
11 UIKit 0x18ae21cbc -[UINavigationController __viewWillLayoutSubviews] + 56 |
12 UIKit 0x18ae21c3c -[UILayoutContainerView layoutSubviews] + 200 |
13 UIKit 0x18ad69760 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 580 |
14 QuartzCore 0x18a6b1e1c -[CALayer layoutSublayers] + 152 |
15 QuartzCore 0x18a6ac884 CA::Layer::layout_if_needed(CA::Transaction*) + 320 |
16 QuartzCore 0x18a6ac728 CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 32 |
17 QuartzCore 0x18a6abebc CA::Context::commit_transaction(CA::Transaction*) + 276 |
18 QuartzCore 0x18a6abc3c CA::Transaction::commit() + 528 |
19 QuartzCore 0x18a6a5364 CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 80 |
20 CoreFoundation 0x1862e42a4 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32 |
21 CoreFoundation 0x1862e1230 __CFRunLoopDoObservers + 360 |
22 CoreFoundation 0x1862e1610 __CFRunLoopRun + 836 |
23 CoreFoundation 0x18620d2d4 CFRunLoopRunSpecific + 396 |
24 GraphicsServices 0x18fa2b6fc GSEventRunModal + 168 |
25 UIKit 0x18add2fac UIApplicationMain + 1488 |
26 TheElements 0x1000fd2f4 main (main.m:55) |
27 libdyld.dylib 0x198176a08 start + 4 |
Note: If you find that exceptions thrown within an exception handling domain setup by your application are not being caught, verify that you are not specifying
the -no_compact_unwind
flag when building your application or libraries.
64-bit iOS uses a "zero-cost" exception implementation. In a "zero-cost" system, every function has additional data that describes how to unwind the stack if an exception is thrown across the function. If an
exception is thrown across a stack frame that has no unwind data then exception handling cannot proceed and the process halts. There might be an exception handler farther up the stack, but if there is no unwind data for a frame then there is no way to get
there from the stack frame where the exception was thrown. Specifying the -no_compact_unwind
flag means you get no unwind tables for that code, so you can
not throw exceptions across those functions.
Additionally, if you are including plain C code in your application or a library, you may need to specify the -funwind-tables
flag
to include unwind tables for all functions in that code.
Thread State
This section lists the ARM thread state of the crashed thread. This is a list of registers and their values at the time of the crash. Understanding the thread state is not necessary when reading a crash report but you may be able to use this information to better understand the conditions of the crash.
Excerpt of the Thread State section from a crash report.
Thread 0 crashed with ARM Thread State (64-bit): |
x0: 0x0000000000000000 x1: 0x0000000000000000 x2: 0x0000000000000000 x3: 0x00000001995f8020 |
x4: 0x0000000000000000 x5: 0x0000000000000001 x6: 0x0000000000000000 x7: 0x0000000000000000 |
x8: 0x0000000000000000 x9: 0x0000000000000015 x10: 0x0000000199601df0 x11: 0x0000000b0000000f |
x12: 0x00000001741e8700 x13: 0x000001a5995f5779 x14: 0x0000000000000000 x15: 0x0000000044000000 |