崩潰日誌的產生
iOS中運行App過程中如果發生程序崩潰,會生成一個崩潰日誌文件。這個文件會保存的特定系統目錄下,擴展名是crash。當手機連接到iTunes時,會將該文件同步到電腦上。
在Mac系統中這些文件會同步到“~/Library/Logs/CrashReporter/MobileDevice”下。
而在Windows系統中會同步到“C:\Users\\AppData\Roaming\Apple computer\Logs\CrashReporter/MobileDevice”(Vista或以上)或“C:\Documents and Settings\\Application Data\Apple computer\Logs\CrashReporter”(XP)。
崩潰日誌的上傳
iOS中崩潰日誌是可以上傳的App Store的服務器,並由開發者查看的。用戶可以通過系統設置中的“通用-診斷與用量”來設定是否上傳崩潰日誌。同時開發者也可以通過捕獲異常信號自己定製異常上報。
崩潰日誌的格式
一般崩潰日誌頭部有如下字段
1
2
3
|
Exception
Type: EXC_BAD_ACCESS (SIGSEGV) Exception
Codes: KERN_INVALID_ADDRESS at 0x20000008 Crashed
Thread: 0 |
其中Exception Type是異常類型,Exception Codes是異常代碼。Crashed Thread指示異常的線程編號。上面表示Crash的線程編號是0(主線程的線程編號是0)。
看崩潰日誌時一般要分兩種情況,一種是內存、CPU的系統錯誤,如內存訪問錯誤、除零異常等。另外一種是程序通過寫代碼拋出的異常,即代碼中通過throw關鍵字拋出的異常。程序拋出的異常是可以捕獲並處理的,如果沒有捕獲,就會Crash,並生成崩潰日誌。這兩種在日誌中有一些細微的差別。
一般內存等系統異常Crash線程信息如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
Thread
0 name: Dispatch queue: com.apple.main-thread Thread
0 Crashed: 0
libobjc.A.dylib 0x3624cf78 objc_msgSend + 16 1
QQYanChu 0x0000d92a -[QYServerInterface connectionDidFinishLoading:] (QYServerInterface.m:328) 2
Foundation 0x30d03c22 __65-[NSURLConnectionInternal _withConnectionAndDelegate:onlyActive:]_block_invoke_0 + 10 3
Foundation 0x30c5b6d2 -[NSURLConnectionInternalConnection invokeForDelegate:] + 22 4
Foundation 0x30c5b69c -[NSURLConnectionInternal _withConnectionAndDelegate:onlyActive:] + 192 5
Foundation 0x30c5b5be -[NSURLConnectionInternal _withActiveConnectionAndDelegate:] + 54 6
CFNetwork 0x338077ee URLConnectionClient::_clientDidFinishLoading(URLConnectionClient::ClientConnectionEventQueue*) + 186 7
CFNetwork 0x337fc49e URLConnectionClient::ClientConnectionEventQueue::processAllEventsAndConsumePayload(XConnectionEventInfo<XClientEvent, XClientEventParams>*, long) + 418 8
CFNetwork 0x337fc592 URLConnectionClient::ClientConnectionEventQueue::processAllEventsAndConsumePayload(XConnectionEventInfo<XClientEvent, XClientEventParams>*, long) + 662 9
CFNetwork 0x337fc19c URLConnectionClient::processEvents() + 100 10
CFNetwork 0x337fc0d2 MultiplexerSource::perform() + 150 11
CoreFoundation 0x34dffacc __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 8 12
CoreFoundation 0x34dff298 __CFRunLoopDoSources0 + 208 13
CoreFoundation 0x34dfe03e __CFRunLoopRun + 646 14
CoreFoundation 0x34d8149e CFRunLoopRunSpecific + 294 15
CoreFoundation 0x34d81366 CFRunLoopRunInMode + 98 16
GraphicsServices 0x3607d432 GSEventRunModal + 130 17
UIKit 0x31e77e76 UIApplicationMain + 1074 18
QQYanChu 0x0000feea main (main.m:39) 19
QQYanChu 0x00007ed4 start + 32 |
第一列是編號,表示堆棧中函數調用的嵌套順序。最上面就是崩潰發生的函數,往下面看就可以看到我們的代碼。第二列是堆棧中函數定義的位置,有的是在動態庫或Framework中定義的,有的是我們的App定義的,如QQYanChu就是我們的App可執行文件,另外你還可以看到其他的動態庫等,一般我們關注自己的QQYanChu的就行了。 如上面的例子就是在-[QYServerInterface connectionDidFinishLoading:] 這個調用中掛的。一般這個信息還會包含代碼文件和說對應的行號,如上面紅色部分就是說崩潰在QYServerInterface.m的328行。這就可以方便我們快速定位問題。
下面是程序拋出的異常的日誌。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
Last
Exception Backtrace: 0
CoreFoundation 0x3425929e __exceptionPreprocess + 158 1
libobjc.A.dylib 0x33a2b97a objc_exception_throw + 26 2
CoreFoundation 0x3425ce02 -[NSObject(NSObject) doesNotRecognizeSelector:] + 166 3
CoreFoundation 0x3425b52c ___forwarding___ + 388 4
CoreFoundation 0x341b2f64 _CF_forwarding_prep_0 + 20 5
QQYanChu 0x000ef372 -[QYUser staticDataFetcher:fetchedData:] (QYUser.m:168) 6
QQYanChu 0x001ee9f2 -[QStaticDataFetcher successWithData:] + 94 7
QQYanChu 0x001efb44 -[QStaticDataFetcher sendDelegateMessageWithLocalData] + 220 8
Foundation 0x39256a6a __NSFireDelayedPerform + 446 9
CoreFoundation 0x3422e5da __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 10 10
CoreFoundation 0x3422e28c __CFRunLoopDoTimer + 268 11
CoreFoundation 0x3422cefc __CFRunLoopRun + 1228 12
CoreFoundation 0x3419feb8 CFRunLoopRunSpecific + 352 13
CoreFoundation 0x3419fd44 CFRunLoopRunInMode + 100 14
GraphicsServices 0x34f082e6 GSEventRunModal + 70 15
UIKit 0x36de52fc UIApplicationMain + 1116 16
QQYanChu 0x000fcfc0 main (main.m:39) 17
QQYanChu 0x000ee4d4 start + 36 Thread
0 name: Dispatch queue: com.apple.main-thread Thread
0 Crashed: 0
libsystem_kernel.dylib 0x32f28350 __pthread_kill + 8 1
libsystem_c.dylib 0x3830311e pthread_kill + 54 2
libsystem_c.dylib 0x3833f96e abort + 90 3
libc++abi.dylib 0x38e35d4a abort_message + 70 4
libc++abi.dylib 0x38e32ff4 default_terminate() + 20 5
libobjc.A.dylib 0x33a2ba74 _objc_terminate() + 144 6
libc++abi.dylib 0x38e33078 safe_handler_caller(void (*)()) + 76 7
libc++abi.dylib 0x38e33110 std::terminate() + 16 8
libc++abi.dylib 0x38e34594 __cxa_rethrow + 84 9
libobjc.A.dylib 0x33a2b9cc objc_exception_rethrow + 8 10
CoreFoundation 0x3419ff1c CFRunLoopRunSpecific + 452 11
CoreFoundation 0x3419fd44 CFRunLoopRunInMode + 100 12
GraphicsServices 0x34f082e6 GSEventRunModal + 70 13
UIKit 0x36de52fc UIApplicationMain + 1116 14
QQYanChu 0x000fcfc0 main (main.m:39) 15
QQYanChu 0x000ee4d4 start + 36 |
可以看出相比系統異常日誌中多出了Last Exception Backtrace字段。上面是線程0崩潰的,而線程0只調用的abort。這是我們要看Last Exception Backtrace這裏的堆棧,這個堆棧是拋出異常時的線程當時的堆棧情況,這個信息纔是有意義的。在我們程序拋出異常後,系統會執行一系列相關邏輯後調用abort異常結束程序。