【譯】了解和分析iOS應(yīng)用崩潰報(bào)告

當(dāng)一個(gè)應(yīng)用發(fā)生崩潰時(shí)會(huì)產(chǎn)生一份崩潰報(bào)告(Crash Report)饲齐,該報(bào)告可以幫助我們了解崩潰的產(chǎn)生原因钾怔。該文檔講述了關(guān)于怎么樣符號(hào)化鳍刷、理解和分析崩潰報(bào)告的相關(guān)內(nèi)容租漂。

  • 介紹
  • 獲取崩潰和低內(nèi)存報(bào)告
  • 分析崩潰報(bào)告
  • 頭部信息
  • 異常代碼
  • 應(yīng)用具體信息
  • 回溯
  • 線程狀態(tài)
  • 二進(jìn)制映像
  • 了解低內(nèi)存報(bào)告
  • 相關(guān)文檔
  • 文檔修改記錄

介紹

當(dāng)iOS設(shè)備上的應(yīng)用崩潰時(shí)喇伯,設(shè)備上會(huì)為其生成和保存一份崩潰報(bào)告喊儡。崩潰報(bào)告描述了應(yīng)用程序在什么情況下結(jié)束運(yùn)行的。在大多數(shù)情況下報(bào)告會(huì)為每個(gè)執(zhí)行的線程包含一個(gè)完整的回溯(Backtrace)稻据,這對(duì)于調(diào)試應(yīng)用崩潰問題時(shí)非常有用艾猜。如果你是iOS開發(fā)者,你應(yīng)該查看這些崩潰報(bào)告來了解你的應(yīng)用存在哪些崩潰捻悯,并且應(yīng)該對(duì)這些崩潰進(jìn)行修復(fù)匆赃。

崩潰報(bào)告中帶有的回溯必須要先進(jìn)行符號(hào)化(Symbolicated)才可以進(jìn)行分析。符號(hào)化(Symbolication)指的是使用人能夠讀懂的方法名稱和行號(hào)來替換回溯里面的內(nèi)存地址信息今缚。如果你通過XCode的Devices窗口獲得一臺(tái)設(shè)備的崩潰日志算柳,那么它會(huì)在幾秒鐘內(nèi)自動(dòng)地將日志進(jìn)行符號(hào)化。否則你需要手動(dòng)將.crash文件導(dǎo)入到XCode的Devices窗口中進(jìn)行符號(hào)化姓言〔壕樱可以參考符號(hào)化(Symbolication)章節(jié)來了解詳細(xì)的內(nèi)容

低內(nèi)存報(bào)告與其它崩潰報(bào)告不同的地方在于它沒有回溯信息查牌。當(dāng)一個(gè)低內(nèi)存崩潰發(fā)生時(shí),你必須檢查你的內(nèi)存使用圖表(在Debug面板的Memory中查看)并且對(duì)低內(nèi)存警告做出應(yīng)對(duì)處理(如UIViewController中的didReceiveMemoryWarning)滥壕。本篇文檔會(huì)提供給你一些內(nèi)存管理方面的參考資料纸颜,你應(yīng)該能夠在里面學(xué)習(xí)到一些有用的信息。

獲取崩潰和低內(nèi)存報(bào)告

調(diào)試部署iOS應(yīng)用》一文中講述了怎樣直接從一臺(tái)iOS設(shè)備中查找崩潰和低內(nèi)存報(bào)告绎橘。

應(yīng)用分發(fā)向?qū)В治霰罎?bào)告》一文中講述了怎么查看從TestFlight的beta測(cè)試用戶和從AppStore下載你的App的用戶那里所收集到的崩潰日志信息胁孙。

分析崩潰報(bào)告

本章節(jié)主要討論一份標(biāo)準(zhǔn)的崩潰報(bào)告中出現(xiàn)的每個(gè)小節(jié)。

頭部信息(Header)

每份崩潰報(bào)告的開頭都帶有一個(gè)頭部信息称鳞。

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

清單1.從某份崩潰報(bào)告中摘錄下來的頭部信息

大多數(shù)字段的意思都很容易理解涮较,有少數(shù)字段需要特殊說明一下:

  • Incident Identifier:報(bào)告的唯一標(biāo)識(shí)。不同的報(bào)告該值不相同冈止。
  • CrashReporter Key:每臺(tái)設(shè)備的匿名標(biāo)識(shí)(并非真正的設(shè)備標(biāo)識(shí))狂票。如果不同的報(bào)告出自同一設(shè)備,則該值相同熙暴。
  • Process:崩潰進(jìn)程的名稱闺属。該值對(duì)應(yīng)應(yīng)用信息屬性列表(Info.plist)中的CFBundleExecutable所指定的值。
  • Version:崩潰進(jìn)程的版本號(hào)周霉。該值對(duì)應(yīng)應(yīng)用信息屬性列表(Info.plist)中的CFBundleVersion和CFBundleVersionString拼接起來的值掂器。
  • Code Type:崩潰進(jìn)程的目標(biāo)體系結(jié)構(gòu)。該值是ARM-64或ARM中的一個(gè)俱箱。
  • OS Version:發(fā)生崩潰的操作系統(tǒng)版本號(hào)(包括構(gòu)建版本號(hào))国瓮。

異常代碼(Exception Codes)

這里不要和Objective-C/C++的異常混淆了(雖然兩者都有可能是崩潰的原因)狞谱。本章節(jié)列出了Mach的異常類型乃摹、子異常類型、處理器專用異常代碼和其它的一些字段來為崩潰的根本原因提供更多的信息跟衅。最后一個(gè)字段列出了觸發(fā)崩潰的線程的索引孵睬。并不是所有字段都會(huì)在每份崩潰報(bào)告中出現(xiàn)。

Exception Type: EXC_CRASH (SIGABRT)

Exception Codes: 0x0000000000000000, 0x0000000000000000

Triggered by Thread: 0

清單2.從某份崩潰報(bào)告中摘錄的異常代碼

下面的章節(jié)將對(duì)一些公共的異常類型進(jìn)行說明与斤。

壞的內(nèi)存訪問 [EXC_BAD_ACCESS // SIGSEGV // SIGBUS]

進(jìn)程試圖訪問無效的內(nèi)存地址. 子異常類型字段包含一個(gè)kern_return_t的錯(cuò)誤描述. 子異常代碼(該值跟在子異常類型的后面) 列出被訪問的壞內(nèi)存地址肪康。

如果objc_msgSend或者objc_release出現(xiàn)接近崩潰線程的回溯頂部, 進(jìn)程可能試圖向已經(jīng)釋放的對(duì)象發(fā)送消息. 你應(yīng)該通過Instrument工具的Zombies來分析你的應(yīng)用,以便更好地了解觸發(fā)該崩潰的條件撩穿。

不正常退出[EXC_CRASH // SIGABRT]

進(jìn)程不正常退出. 導(dǎo)致該異常的大多數(shù)情況是因?yàn)闆]有捕獲Objective - C/C++所產(chǎn)生的異常磷支。

如果應(yīng)用擴(kuò)展(App Extensions)在初始化時(shí)花費(fèi)太多時(shí)間將會(huì)被結(jié)束掉(watchdog結(jié)束,watchdog結(jié)束指的是watchdog超時(shí)后強(qiáng)行終止應(yīng)用)食寡。如果擴(kuò)展在啟動(dòng)時(shí)由于掛起而被殺掉進(jìn)程雾狈,那么崩潰報(bào)告的子異常類型將會(huì)是LAUNCH_HANG。因?yàn)閿U(kuò)展沒有主函數(shù)抵皱,因此初始化花費(fèi)的時(shí)間都體現(xiàn)在擴(kuò)展和依賴庫的靜態(tài)構(gòu)造函數(shù)和+load方法中善榛。你應(yīng)該盡可能地避免在靜態(tài)構(gòu)造函數(shù)和+load方法中處理過多的工作(也就是文檔中說的延遲大部分的工作處理)辩蛋。

跟蹤陷阱 [EXC_BREAKPOINT // SIGTRAP]

跟非正常退出相似。該異常是由于打算給一個(gè)附加的調(diào)試器在執(zhí)行特定的斷點(diǎn)來中斷進(jìn)程時(shí)觸發(fā)移盆。你可以在自己的代碼中使用__builtin_trap()方法來觸發(fā)此異常悼院。如果沒有被調(diào)試器所附加,那么進(jìn)程將會(huì)結(jié)束并且生成一份崩潰報(bào)告咒循。

如果Swift代碼在運(yùn)行時(shí)發(fā)現(xiàn)一個(gè)意外的情況時(shí)据途,也會(huì)以該異常類型結(jié)束程序。例如:

  • 為一個(gè)非可選(non-optional)類型被賦值nil
  • 一個(gè)有問題的強(qiáng)制類型轉(zhuǎn)換

通過看崩潰線程的回溯來確定是在什么位置出現(xiàn)異常的情況叙甸。

守護(hù)資源的侵害 [EXC_GUARD]

進(jìn)程侵害了一個(gè)被守護(hù)的資源颖医。系統(tǒng)庫可能會(huì)標(biāo)記守護(hù)某些文件描述符,在此之后如果對(duì)這些文件描述符作正常的操作將觸發(fā)EXC_GUARD異常(當(dāng)系統(tǒng)想操作這些文件描述符時(shí)裆蒸,會(huì)使用特殊的'guarded'私有API)熔萧。該異常類型可以幫助你快速地跟蹤例如關(guān)閉一個(gè)由系統(tǒng)庫打開的文件描述符這類的問題。舉個(gè)例子僚祷,如果應(yīng)用關(guān)閉了一個(gè)基于SQLite存儲(chǔ)的Core Data的SQLite文件描述符佛致,那么Core Data隨后也會(huì)離奇地崩潰。守護(hù)異常越早發(fā)現(xiàn)越容易調(diào)試久妆。

相關(guān)的子異常類型是一個(gè)位字段(bitfield)晌杰,分解如下:

  • [63:61] ‐ 守護(hù)類型: 守護(hù)資源的類型. 0x2表示一個(gè)文件描述符跷睦。
  • [60:32] ‐ 特點(diǎn)(Flavor): 違規(guī)行為被觸發(fā)的條件筷弦。
    • 如果第一位(1 << 0)被設(shè)置,則表示進(jìn)程試圖在被守護(hù)的文件描述符上調(diào)用close()
    • 如果第二位(1 << 1)被設(shè)置抑诸,則表示進(jìn)程試圖在一個(gè)被守護(hù)的文件描述符上調(diào)用dup()烂琴、dup2() 、帶有F_DUPFD或者F_DUPFD_CLOEXEC命令的fcntl()方法蜕乡。
    • 如果第三位(1 << 2)被設(shè)置奸绷,則表示進(jìn)程試圖通過套接字(Socket)來發(fā)送一個(gè)被守護(hù)的文件描述符。
    • 如果第五位(1 << 4)被設(shè)置层玲,則表示進(jìn)程試圖對(duì)一個(gè)被守護(hù)的文件描述進(jìn)行寫操作号醉。
  • [31:0] ‐文件描述符:進(jìn)程試圖修改的被守護(hù)的文件描述符。

資源限制 [EXC_RESOURCE]

進(jìn)程觸及了消耗資源的限額辛块。這不是一個(gè)崩潰畔派,但系統(tǒng)會(huì)派發(fā)通知告訴進(jìn)程使用了過多的資源。在子異常類型中會(huì)描述確切的資源信息:

  • 子異常類型WAKEUPS表示一個(gè)線程每秒中喚醒的次數(shù)太多润绵,這迫使CPU喚醒太頻繁并且會(huì)損耗電池的壽命线椰。

  • 子異常類型MEMORY表示進(jìn)程已經(jīng)越過了系統(tǒng)強(qiáng)制的內(nèi)存限制。這可能是過多占用內(nèi)存而導(dǎo)致崩潰的前兆尘盼。

其它異常類型

一些崩潰報(bào)告可能包含一個(gè)未命名的異常類型憨愉,是一個(gè)十六進(jìn)制數(shù)(如:00000020)烦绳。如果你收到這樣的崩潰報(bào)告,直接查看下面更多的異常代碼信息:

  • 0xbaaaaaad表示日志是整個(gè)系統(tǒng)的一個(gè)堆椗渥希快照(stackshot)而不是一個(gè)崩潰報(bào)告径密。通過按下Home鍵和任意音量鍵可以獲得堆棧快照(stackshot)躺孝。通常這些日志是被用戶意外創(chuàng)建的(不小心同時(shí)按下了Home鍵+音量鍵)睹晒,而不是一個(gè)錯(cuò)誤。

  • 0xbad22222表示一個(gè)VoIP應(yīng)用由于恢復(fù)過于頻繁而被iOS結(jié)束進(jìn)程括细。

  • 0x8badf00d表示一個(gè)應(yīng)用由于watchdog發(fā)生超時(shí)而被iOS結(jié)束進(jìn)程伪很。這表明該應(yīng)用程序在啟動(dòng)、結(jié)束或者響應(yīng)系統(tǒng)事件時(shí)花費(fèi)太長時(shí)間。一個(gè)常見的例子是在主線程上實(shí)現(xiàn)同步的網(wǎng)絡(luò)操作。任何在Thread 0(主線程)上的操作都應(yīng)該放到后臺(tái)線程上執(zhí)行倒慧,或者使用不阻塞主線程的方式進(jìn)行處理缝驳。

  • 0xc00010ff表示一個(gè)應(yīng)用由于響應(yīng)一個(gè)熱事件(thermal event)而被操作系統(tǒng)殺掉進(jìn)程。這可能是由于在特定的設(shè)備上發(fā)生崩潰呢灶,或者是操作環(huán)境的問題。為了讓你的程序獲得更多有效的提示,請(qǐng)觀看《使用Instruments來提升iOS性能和電量優(yōu)化的WWDC會(huì)議》应又。

  • 0xdead10cc表示一個(gè)應(yīng)用由于在后臺(tái)運(yùn)行時(shí)還保留著系統(tǒng)的資源(如通訊錄數(shù)據(jù)庫)而被iOS結(jié)束進(jìn)程。

  • 0xdeadfa11表示應(yīng)用程序被用戶強(qiáng)制退出乏苦。當(dāng)用戶先按住開機(jī)鍵直到出現(xiàn)“滑動(dòng)來關(guān)機(jī)”界面后再按住Home一段時(shí)間后就會(huì)出現(xiàn)強(qiáng)制退出的情況株扛。這可能由于應(yīng)用程序無法響應(yīng)才使用這種方法來進(jìn)行強(qiáng)制退出,但是不能保證可以退出所有應(yīng)用程序汇荐。

注意:從多任務(wù)列表中直接結(jié)束一個(gè)掛起的應(yīng)用是不會(huì)產(chǎn)生崩潰報(bào)告的洞就。一旦應(yīng)用掛起,則iOS可以在任何時(shí)候?qū)⑵浣Y(jié)束掀淘,而不產(chǎn)生崩潰報(bào)告旬蟋。

應(yīng)用具體信息(Application Specific Information)

某些崩潰會(huì)寫入一些額外的信息到崩潰報(bào)告中。不同的結(jié)束類型對(duì)應(yīng)該部分的內(nèi)容也不相同革娄。你可以通過閱讀本章內(nèi)容來更好地了解一下應(yīng)用程序結(jié)束的情形倾贰。

Application Specific Information:

MyApp[134] was suspended with locked system files: 

/private/var/mobile/Library/AddressBook/AddressBook.sqlitedb

清單3.從某份崩潰報(bào)告中摘錄的應(yīng)用具體信息

回溯(Backtrace)

奔潰報(bào)告中最有趣的部分就是每個(gè)進(jìn)程當(dāng)時(shí)已經(jīng)中止執(zhí)行的線程回溯。這些回溯的內(nèi)容就和你使用調(diào)試器停止執(zhí)行應(yīng)用的時(shí)候看到的內(nèi)容類似拦惋。

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

...

清單4. 從某份已完成符號(hào)化的崩潰報(bào)告中摘錄的回溯內(nèi)容

符號(hào)化(Symbolication)

從iOS設(shè)備中檢索到的崩潰日志只有可執(zhí)行代碼在加載的二進(jìn)制映像(Binary Images)中的十六進(jìn)制地址匆浙,是沒有包含方法或函數(shù)名稱的,而這些方法和函數(shù)的名稱被稱為符號(hào)架忌,如清單5所示吞彤,你需要將這些地址與符號(hào)進(jìn)行映射。

將回溯的地址解析為源碼的方法和行號(hào)被稱為符號(hào)化 ,這過程需要上傳到AppStore的應(yīng)用的二進(jìn)制文件和編譯二進(jìn)制文件時(shí)生成的.dSYM文件饰恕。二進(jìn)制文件和.dSYM文件是一一對(duì)應(yīng)的挠羔,否則崩潰報(bào)告可能只會(huì)顯示部分內(nèi)容被符號(hào)化,如清單6所示埋嵌。因此破加,保留每個(gè)分發(fā)給用戶的應(yīng)用包(不管如何分發(fā))和對(duì)應(yīng)的.dSYM文件就非常有必要了。

Thread 0 name: Dispatch queue: com.apple.main-thread

Thread 0 Crashed:

0 TheElements 0x00000001000effdc0x1000e4000+49116

1 UIKit 0x000000018ca5c2ec 0x18ca14000 + 295660

2 UIKit 0x000000018ca5c1f4 0x18ca14000 + 295412

3 QuartzCore 0x000000018c380f600x18c36c000 + 85856

4 libdispatch.dylib 0x0000000198fb93680x198fb8000 + 4968

5 libdispatch.dylib 0x0000000198fbd97c0x198fb8000 + 22908

6 CoreFoundation 0x000000018822dfa0 0x188150000 + 909216

7 CoreFoundation 0x000000018822c048 0x188150000 + 901192

8 CoreFoundation 0x00000001881590a00x188150000 + 37024

9 GraphicsServices 0x00000001912fb5a00x1912f0000 + 46496

10 UIKit 0x000000018ca8aaa0 0x18ca14000 + 486048

11 TheElements 0x00000001000e98000x1000e4000 + 22528

12 libdyld.dylib 0x0000000198fe2a040x198fe0000 + 10756

清單5.從某份未符號(hào)化的崩潰報(bào)告中摘錄的回溯部分

Thread 0 name: Dispatch queue: com.apple.main-thread

Thread 0 Crashed:

0 TheElements 0x00000001000effdc0x1000e4000 + 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 0x00000001881590a0CFRunLoopRunSpecific + 392

9 GraphicsServices 0x00000001912fb5a0GSEventRunModal + 164

10 UIKit 0x000000018ca8aaa0UIApplicationMain + 1484

11 TheElements 0x00000001000e98000x1000e4000 + 22528

12 libdyld.dylib 0x0000000198fe2a04start + 0

清單6.從某份部分符號(hào)化崩潰報(bào)告中摘錄的回溯部分(系統(tǒng)的棧幀已符號(hào)化雹嗦,但沒有符號(hào)化應(yīng)用程序棧幀)

要點(diǎn):你必須保留應(yīng)用程序二進(jìn)制和它對(duì)應(yīng)的.dSYM文件才能夠完整地進(jìn)行符號(hào)化范舀。你每次提交這些編譯文件到iTunes Connect的時(shí)候必須要將它們歸檔。.dSYM文件和應(yīng)用程序的二進(jìn)制文件在每次編譯中是對(duì)應(yīng)捆綁的了罪。即使是往后使用的是相同的源文件進(jìn)行編譯锭环,那產(chǎn)生的.dSYM文件和應(yīng)用程序的二進(jìn)制文件也是跟之前的沒有任何關(guān)系的。如果你使用XCode的Build和Archive命令進(jìn)行編譯泊藕,那么.dSYM 文件和二進(jìn)制文件將會(huì)自動(dòng)放到一個(gè)合適的路徑辅辩。不然也可以是一個(gè)通過Spotlight可搜索到的位置(如你的Home目錄)。

使用XCode的Archive命令可以很容易地使二進(jìn)制文件和.dSYM文件配對(duì)娃圆。當(dāng)你使用Archive命令(通過選擇Product菜單中的Archive)時(shí)玫锋,XCode會(huì)一起生成應(yīng)用的二進(jìn)制文件和包含符號(hào)信息的.dSYM文件并將它們保存到你的Home目錄下。你可以通過XCode的Organizer在Archived欄目下找到所有你歸檔過的應(yīng)用讼呢。當(dāng)符號(hào)化崩潰報(bào)告時(shí)撩鹿,XCode會(huì)自動(dòng)從這里查找對(duì)應(yīng)的歸檔應(yīng)用;并且可以通過這里直接提交歸檔應(yīng)用到iTunes Connect來確保你release的應(yīng)用程序二進(jìn)制文件和.dSYM文件相匹配悦屏。

XCode會(huì)自動(dòng)符號(hào)化所有能夠匹配程序二進(jìn)制文件和.dSYM文件的崩潰報(bào)告节沦。因此,你需要將所有要符號(hào)化的崩潰報(bào)告添加到XCode的Organizer中窜管。其步驟如下:

  1. 將iOS設(shè)備連接到你的Mac中散劫。
  2. 選擇XCode的Window菜單中的Devices稚机。
  3. 在左側(cè)的DEVICES欄目下選擇連接的設(shè)備幕帆。
  4. 在右側(cè)的Device Information欄目下點(diǎn)擊“View Device Logs”按鈕。
  5. 將你的崩潰報(bào)告拖拽到彈出面板的左側(cè)赖条。
  6. XCode會(huì)自動(dòng)符號(hào)化崩潰報(bào)告并顯示符號(hào)化后的結(jié)果失乾。

異常(Exceptions)

異常在Objective-C中用來說明編程中或者運(yùn)行時(shí)意外發(fā)生的錯(cuò)誤。例如:在集合中的超出范圍訪問纬乍、試圖改變一個(gè)不可變的對(duì)象碱茁、沒有實(shí)現(xiàn)協(xié)議中必須實(shí)現(xiàn)的方法或者給對(duì)象發(fā)送一個(gè)不存在的消息等。

注意:給一個(gè)已經(jīng)釋放的對(duì)象發(fā)送消息會(huì)拋出NSInvalidArgumentException異常而不是立即崩潰仿贬;當(dāng)一個(gè)新對(duì)象分配的內(nèi)存剛好在已釋放對(duì)象的內(nèi)存地址上時(shí)會(huì)發(fā)生這樣的情況纽竣。如果你的應(yīng)用崩潰是由于未捕獲的NSInvalidArgumentException異常(在異常的回溯中看到有-[NSObject(NSObject) doesNotRecognizeSelector:]這樣的信息),可以考慮使用Instrument的Zombies分析你的應(yīng)用程序來盡可能排出一些不合理的內(nèi)存管理情況。

如果一個(gè)異常未被捕獲蜓氨,那么它會(huì)被一個(gè)叫未捕獲異常處理器(uncaught exception handler)的方法所攔截聋袋。iOS默認(rèn)的未捕獲異常處理器會(huì)將異常信息和回溯打印到設(shè)備的控制臺(tái)后結(jié)束掉程序。只有最后一個(gè)未捕獲異常會(huì)寫入到崩潰報(bào)告的Last Exception Backtrace小節(jié)下穴吹,如清單7所示幽勒。崩潰報(bào)告中省略了異常消息。如果你收到一份帶有Last Exception Backtrace小節(jié)的崩潰報(bào)告港令,你應(yīng)該從原來的設(shè)備獲得控制臺(tái)日志來更好地了解導(dǎo)致拋出這次異常的情況啥容。

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)

條目7.從某份未符號(hào)化的異常報(bào)告中摘錄的Last Exception Backtrace小節(jié)

帶有Last Exception Backtrace的崩潰日志僅包含了16進(jìn)制的地址信息,必須對(duì)其進(jìn)行符號(hào)化處理顷霹,使它變成可被理解的回溯咪惠。如條目8所示:

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

清單8.從某份已符號(hào)化的崩潰報(bào)告中摘錄的Last Exception Backtrace小節(jié).這是一個(gè)在應(yīng)用的故事板中加載一個(gè)場(chǎng)景時(shí)拋出的異常。因?yàn)橐粋€(gè)與IBOutlet相關(guān)聯(lián)的場(chǎng)景元素缺失導(dǎo)致淋淀。

注意:如果發(fā)現(xiàn)你的應(yīng)用程序所指定的異常處理域中的異常在拋出時(shí)沒有被捕獲硝逢,請(qǐng)檢查你的應(yīng)用或者庫在編譯時(shí)是否指定了-no_compact_unwind標(biāo)識(shí),如果指定了請(qǐng)去掉绅喉。

64位的iOS使用了一個(gè)“零成本(zero-cost)”的異常實(shí)現(xiàn)渠鸽。在一個(gè)“零成本”系統(tǒng)中,每個(gè)可能拋出異常的方法都有描述怎么unwind堆棧的附加信息柴罐。如果一個(gè)異常在不具有unwind數(shù)據(jù)的棧幀中拋出徽缚,那么異常處理將無法繼續(xù)并且進(jìn)程被停止執(zhí)行。這有可能是一個(gè)更上層堆棧異常處理革屠,但是如果有一幀沒有unwind數(shù)據(jù)那么將沒有方法知道異常從哪一個(gè)棧幀拋出凿试。指定-no_compact_unwind標(biāo)識(shí)意味著你的方法沒有unwind table的代碼,所以你不能從這些方法中拋出異常似芝。

此外那婉,如果你的應(yīng)用程序或庫包含純C代碼,你可能需要指定-funwind-tables標(biāo)識(shí)來讓你的代碼中的所有方法包含unwind table党瓮。

線程狀態(tài)(Thread State)

該小節(jié)列出了崩潰線程的ARM狀態(tài)详炬。這是一份在崩潰時(shí)寄存器及其值的列表。當(dāng)你看一份崩潰報(bào)告的時(shí)候了解線程狀態(tài)不是必須的寞奸,但你可以利用這些信息來更好的了解當(dāng)時(shí)崩潰的情況呛谜。

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

x16: 0x00000001989724d8  x17: 0x0000000188176370  x18: 0x0000000000000000  x19: 0x00000001701dda60

x20: 0x0000000000000001  x21: 0x0000000136606e20  x22: 0x00000001000f6238  x23: 0x0000000000000000

x24: 0x000000019cc640a8  x25: 0x0000000000000020  x26: 0x0000000000000000  x27: 0x0000000000000000

x28: 0x000000019cc577c0  fp: 0x000000016fd1a8d0   lr: 0x00000001000effcc

sp: 0x000000016fd1a860   pc: 0x00000001000effdc cpsr: 0x60000000

清單9.從某份崩潰報(bào)告中摘錄的線程狀態(tài)小節(jié)

二進(jìn)制映像(Binary Images)

該小節(jié)列出了崩潰時(shí)加載到進(jìn)程中的二進(jìn)制映像。

Binary Images:

0x100058000 - 0x10006bfff TheElements arm64 <77b672e2b9f53b0f95adbc4f68cb80d6>
/var/mobile/Containers/Bundle/Application/CB86658C-F349-4C7A-B73B-CE3B4502D5A4/TheElements.app/TheElements

...

清單10.從某份崩潰報(bào)告中摘錄的二進(jìn)制映像小節(jié)中的程序入口

每行列出一個(gè)二進(jìn)制映像的以下細(xì)節(jié):

  • 二進(jìn)制映像在進(jìn)程中的地址空間枪萄。
  • 二進(jìn)制映像的可執(zhí)行程序名稱隐岛。
  • 二進(jìn)制映像的體系結(jié)構(gòu)。一個(gè)可執(zhí)行文件可以包含多個(gè)不同體系結(jié)構(gòu)的“切片”瓷翻,但僅有一個(gè)“切片”將會(huì)加載到進(jìn)程中聚凹。
  • 二進(jìn)制映像的唯一標(biāo)識(shí)UUID割坠。每個(gè)版本的應(yīng)用程序/框架和與其對(duì)應(yīng)的符號(hào)化.dSYM文件影響此標(biāo)識(shí)的變化。
  • 可執(zhí)行文件在磁盤上的路徑妒牙。

了解低內(nèi)存報(bào)告

當(dāng)發(fā)現(xiàn)低內(nèi)存情況時(shí)韭脊,iOS中的虛擬內(nèi)存系統(tǒng)會(huì)依靠協(xié)作的應(yīng)用程序去釋放內(nèi)存。低內(nèi)存警告會(huì)通知所有運(yùn)行中的程序和進(jìn)程來請(qǐng)求釋放內(nèi)存单旁,希望減少內(nèi)存的使用量沪羔。如果內(nèi)存壓力得不到釋放,系統(tǒng)可能會(huì)終止后臺(tái)的進(jìn)程來緩解內(nèi)存壓力象浑。如果有足夠的內(nèi)存被釋放蔫饰,那么你的程序可以繼續(xù)運(yùn)行,否則你的程序會(huì)由于沒有足夠的內(nèi)存來滿足需要而被iOS結(jié)束掉愉豺,并且會(huì)在設(shè)備上生成和保存一份低內(nèi)存報(bào)告篓吁。

低內(nèi)存報(bào)告的格式和其它的崩潰報(bào)告不一樣,它沒有應(yīng)用的線程回溯蚪拦;其開始帶有一個(gè)類似于崩潰報(bào)告的頭部信息杖剪,接下來就是整個(gè)系統(tǒng)的內(nèi)存統(tǒng)計(jì)字段的集合。值得關(guān)注的是一個(gè)叫Page SIze字段的值驰贷,其記錄了關(guān)于每個(gè)進(jìn)程在低內(nèi)存報(bào)告中的使用內(nèi)存分頁數(shù)量方面的情況盛嘿。

低內(nèi)存報(bào)告中最重要的部分就是進(jìn)程列表(table of processes)。該表列出了在低內(nèi)存報(bào)告生成時(shí)所有正在運(yùn)行的進(jìn)程括袒,包括系統(tǒng)的守護(hù)進(jìn)程次兆。如果一個(gè)進(jìn)程被“拋棄(jettisoned)”,其原因?qū)?huì)記錄在[reason]列中锹锰。進(jìn)程被“拋棄”可能由于下面的原因?qū)е拢?/p>

  • [per‐process‐limit]:該進(jìn)程跨越了系統(tǒng)施加的內(nèi)存限制芥炭。系統(tǒng)為所有應(yīng)用建立的常駐內(nèi)存進(jìn)行了限制∈鸦郏跨越該限制的進(jìn)程將會(huì)被終止园蝠。

注意:擴(kuò)展的進(jìn)程內(nèi)存限制很低,某些技術(shù)痢士,如地圖視圖和SpriteKit都需要較高的內(nèi)存成本彪薛,不適合在擴(kuò)展中使用。

  • [vm‐pageshortage]/[vm‐thrashing]/[vm]:進(jìn)程由于內(nèi)存壓力而被殺死良瞧。
  • [vnode‐limit]:打開了太多的文件陪汽。

注意:最前面的應(yīng)用即使耗盡所有虛擬節(jié)點(diǎn),系統(tǒng)也不會(huì)將其殺死褥蚯。這意味著你的應(yīng)用在后臺(tái)時(shí),即使不是過量使用虛擬節(jié)點(diǎn)的源也有可能會(huì)被結(jié)束况增。

  • [highwater]: 一個(gè)系統(tǒng)守護(hù)進(jìn)程跨越了它的內(nèi)存使用的最大標(biāo)記值赞庶。
  • [jettisoned]: 其它原因?qū)е逻M(jìn)程被拋棄。

如果你沒有看到原因中列出你的應(yīng)用/擴(kuò)展的進(jìn)程,那么可能不是因?yàn)閮?nèi)存壓力引起的崩潰歧强。查看.crash文件(上一節(jié)講述的)了解更多信息澜薄。

當(dāng)你看到一個(gè)低內(nèi)存崩潰時(shí),與其關(guān)心那一部分代碼在應(yīng)用終止時(shí)正在執(zhí)行摊册,倒不如檢查你對(duì)內(nèi)存的使用方式和對(duì)低內(nèi)存警告的響應(yīng)處理肤京。《在你的應(yīng)用中查找內(nèi)存問題》一文中詳細(xì)地講述了如何使用Instruments的Leaks分析來發(fā)現(xiàn)內(nèi)存泄露茅特,以及如何使用Allocations分析的Mark Heap功能來防止出現(xiàn)被遺棄的內(nèi)存忘分。《內(nèi)存使用性能指南》論述了一種正確的方法來應(yīng)對(duì)低內(nèi)存通知白修,同時(shí)又提供了很多有效使用內(nèi)存的技巧妒峦。同時(shí)也建議你看看WWDC2010年會(huì)議,使用Instruments進(jìn)行高級(jí)內(nèi)存分析兵睛。

重點(diǎn):Instruments的Leaks和Allocations分析無法跟蹤所有的內(nèi)存使用肯骇。你需要與Instruments的VM Tracker一起來運(yùn)行你的應(yīng)用(包含在Instruments的Allocations的模版中)來查看所有的內(nèi)存使用量。VM Tracker默認(rèn)是不開啟的祖很,要想啟動(dòng)VM Tracker笛丙,可以通過點(diǎn)擊Instruments,選中“Automatic Snapshotting”選項(xiàng)或者手動(dòng)按下“Snapshot Now”按鈕假颇。

相關(guān)文檔

有關(guān)如何使用Instruments的Zombies模版來修復(fù)內(nèi)存過度釋放而崩潰的更多信息若债,請(qǐng)參考《通過Zombies模版來消除僵尸對(duì)象

有關(guān)應(yīng)用歸檔的更多信息,請(qǐng)參考《通過XCode的Archive功能來進(jìn)應(yīng)用分發(fā)與測(cè)試

關(guān)于解析崩潰日志的更多信息拆融,請(qǐng)參考《iPhone OS WWDC 2010會(huì)議上的了解崩潰報(bào)告

文檔修改記錄

日期 備注
2015‐07‐21 XCode6開展的崩潰報(bào)告討論更新
2012‐12‐13 增加更多的異常代碼信息
2012‐03‐28 XCode4更新蠢琳,加入關(guān)于低內(nèi)存崩潰報(bào)告和更多異常代碼信息。
2011‐03‐01 iOS4或更高版本的變化更新
2010‐07‐06 修復(fù)文檔中的Bug
2010‐05‐18 iPhone OS3.2和XCode 3.2.2的變化更新
2009‐06‐01 添加強(qiáng)調(diào)不僅需要保存.dSYM文件镜豹,還需要保存應(yīng)用的二進(jìn)制文件傲须。
2009‐04‐30 iTunes Connect崩潰日志服務(wù)更新
2009‐02‐18 包含一個(gè)防止應(yīng)用程序代碼被符號(hào)化的問題解決方案更新
2009‐01‐29 為開發(fā)人員說明如何符號(hào)化、了解和分析崩潰報(bào)告的新文檔

翻譯的處女作趟脂,如果有不對(duì)的地方請(qǐng)狠狠地指出來_泰讽。

原文鏈接:https://developer.apple.com/library/ios/technotes/tn2151/_index.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市昔期,隨后出現(xiàn)的幾起案子已卸,更是在濱河造成了極大的恐慌,老刑警劉巖硼一,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件累澡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡般贼,警方通過查閱死者的電腦和手機(jī)愧哟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門奥吩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蕊梧,你說我怎么就攤上這事霞赫。” “怎么了肥矢?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵端衰,是天一觀的道長。 經(jīng)常有香客問我甘改,道長旅东,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任楼誓,我火速辦了婚禮玉锌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘疟羹。我一直安慰自己主守,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布榄融。 她就那樣靜靜地躺著参淫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪愧杯。 梳的紋絲不亂的頭發(fā)上涎才,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音力九,去河邊找鬼耍铜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛跌前,可吹牛的內(nèi)容都是我干的棕兼。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼抵乓,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼伴挚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起灾炭,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤茎芋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蜈出,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體田弥,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年掏缎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了皱蹦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片煤杀。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡眷蜈,死狀恐怖沪哺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酌儒,我是刑警寧澤辜妓,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站忌怎,受9級(jí)特大地震影響籍滴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜榴啸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一孽惰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鸥印,春花似錦勋功、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至潜的,卻和暖如春骚揍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背啰挪。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工信不, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亡呵。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓抽活,卻偏偏與公主長得像,于是被迫代替她去往敵國和親政己。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酌壕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容