iOS開發技巧 - 崩潰分析

該文章屬於<簡書 — 劉小壯>原創,轉載請註明:

<簡書 — 劉小壯> http://www.jianshu.com/p/77660e626874objective-c


在iOS開發調試過程當中以及上線以後,程序常常會出現崩潰的問題。簡單的崩潰還好說,複雜的崩潰就須要咱們經過解析Crash文件來分析了,解析Crash文件在iOS開發中是比較常見的。

如今網上有不少關於解析崩潰信息的博客,可是大多質量良莠不齊,或者有些細節沒有注意到。今天寫一篇博客總結一下我對崩潰調試的使用和技巧,若是有哪些錯誤或遺漏,還請指點,謝謝!😁數組


博客配圖

獲取崩潰信息

獲取方式

iOS中獲取崩潰信息的方式有不少,比較常見的是使用友盟、Bugly等第三方分析工具,或者本身收集崩潰信息並上傳公司服務器。下面列舉一些咱們經常使用的崩潰分析方式:xcode

  • 使用友盟、Bugly等第三方崩潰統計工具。
  • 本身實現應用內崩潰收集,並上傳服務器。
  • Xcode-Devices中直接查看某個設備的崩潰信息。
  • 使用蘋果提供的Crash崩潰收集服務。
收集崩潰信息

蘋果給咱們提供了異常處理的類-NSException。這個類能夠建立一個異常對象,也能夠經過這個類獲取一個異常對象。服務器

這個類中最經常使用的是一個獲取崩潰信息的C函數,能夠經過這個函數在程序發生異常的時候收集這個異常。微信

// 將系統提供的獲取崩潰信息函數寫在這個方法中,以保證在程序開始運行就具備獲取崩潰信息的功能
  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     // 將下面C函數的函數地址當作參數
     NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
     return YES;
  }
  // 設置一個C函數,用來接收崩潰信息
  void UncaughtExceptionHandler(NSException *exception){
      // 能夠經過exception對象獲取一些崩潰信息,咱們就是經過這些崩潰信息來進行解析的,例以下面的symbols數組就是咱們的崩潰堆棧。
      NSArray *symbols = [exception callStackSymbols];
      NSString *reason = [exception reason];
      NSString *name = [exception name];
  }

咱們也能夠經過下面方法獲取崩潰統計的函數指針:app

NSUncaughtExceptionHandler *handler = NSGetUncaughtExceptionHandler();

崩潰分析

dSYM 符號集

進行崩潰分析,首先要弄懂一個概念,就是符號集。函數

  • 符號集是咱們對ipa文件進行打包以後,和.app文件同級的後綴名爲.dSYM的文件,這個文件必須使用Xcode進行打包纔有。
  • 每個.dSYM文件都有一個UUID,和.app文件中的UUID對應,表明着是一個應用。而.dSYM文件中每一條崩潰信息也有一個單獨的UUID,用來和程序的UUID進行校對。
  • 咱們若是不使用.dSYM文件解析出的崩潰信息都不能保證準確。
  • 符號集中存儲着文件名、方法名、行號的信息,是和可執行文件的16進制函數地址對應的,經過分析崩潰的.Crash文件能夠準確知道具體的崩潰信息。

咱們每次Archive一個包以後,都會隨之生成一個dSYM文件。每次發佈一個版本,咱們都須要備份這個文件,以方便之後的調試。進行崩潰信息符號化的時候,必須使用當前應用打包的電腦所生成的dSYM文件,其餘電腦生成的文件可能會致使分析不許確的問題。工具

Archive

當程序崩潰的時候,能夠得到到崩潰的錯誤堆棧,錯誤堆棧都是0x開頭的16進制地址,須要使用Xcode自帶的symbolicatecrash工具來將.Crash.dSYM文件進行符號化,就能夠獲得詳細崩潰的信息。優化

系統符號化文件

在崩潰分析時,dSYM文件是解析App堆棧的,若是是系統庫則須要對應的符號化文件。不少解析不出來系統堆棧的問題,就是由於沒有系統的符號化文件。符號化文件就在Xcode的資源庫裏,能夠從下面的目錄找到符號化文件。網站

/Users/liuxiaozhuang/Library/Developer/Xcode/iOS DeviceSupport

符號化文件對版本和Architectures都有要求,例如崩潰的系統是8.4.1系統 arm64的指令集,就須要對應的系統符號化文件8.4.1 (12H321)。不然仍是不能解析出系統崩潰信息,或者解析出來也是錯的。若是在iOS DeviceSupport文件中沒有找到對應的符號化文件,須要找一個對應的才能夠解析。

符號化文件的指令集通常都是兼容低版本的,例如8.4.1 (12H321)的指令集會有arm64armv7sarmv7三個版本,若是蘋果沒有明確說明某個iOS版本不兼容32位處理器,那麼指令集都會兼容的。

蒐集系統符號化文件很是困難,我在國外也沒找到蒐集全的網站。可是國內有一個很是敬業的iOS同行,蒐集總結了iOS7 - iOS10的不少符號化文件,並且做者對文件作了優化,下載下來的文件也不會很大,很是感謝他!

網盤連接: https://pan.baidu.com/s/1nvfi4g5
密碼: 79m8

這裏的符號化文件也並不全,若是遇到沒有找到的符號化文件,到崩潰日誌中找到OS Version:iOS 8.4.1 (12H321),把後面的系統build版本放在Google搜一下試試,若是搜不到就不太好解決了。

symbolicatecrash工具

經過Mac自帶的命令行工具解析Crash文件須要具有四個文件

  • symbolicatecrashXcode自帶的崩潰分析工具,使用這個工具能夠更精確的定位崩潰所在的位置,將0x開頭的地址替換爲響應的代碼和具體行數。
  • 打包時產生的dSYM文件。
  • 崩潰時產生的Crash文件。
  • 對應iOS系統和指令集的符號化文件

我在解析崩潰信息的時候,通常會在桌面上單獨創建一個Crash文件夾,而後將.Crash.dSYMsymbolicatecrash放在這個文件夾中,這樣進入這個文件夾下,直接一行命令就解決了。下次再作崩潰分析的時候,換一下對應的文件名就能夠解析。

symbolicatecrashXcode8及以後是下面的路徑。

/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash

Xcode8以前symbolicatecrash在下面的路徑中。

/Applications/Xcode.app/Contents/SharedFrameworks/DTDeviceKitBase.framework/Versions/A/Resources/symbolicatecrash

而後Window -> Organizer -> Archives中,選中archive的版本右擊,選擇Show in Finder就能夠獲取dSYM文件了。

dSYM文件

.Crash.dSYMsymbolicatecrash三個文件都放在咱們在桌面創建的Crash文件夾中。

Crash文件夾

開啓命令行工具,進入崩潰文件夾中

cd /Users/username/Desktop/崩潰文件夾

使用命令解析Crash文件

./symbolicatecrash ./*.crash ./*.app.dSYM > symbol.crash

若是上面命令不成功,使用命令檢查一下環境變量

xcode-select -print-path

返回結果:

/Applications/Xcode.app/Contents/Developer/

若是不是上面的結果,須要使用下面命令設置一下導出的環境變量,而後重複上面解析的操做。

export DEVELOPER_DIR=/Applications/XCode.app/Contents/Developer

解析完成後會生成一個新的.Crash文件,這個文件中就是崩潰詳細信息。圖中紅色標註的部分就是咱們代碼崩潰的部分。

解析完成的結果

注意,如下狀況不會有崩潰信息產生:

  • 內存訪問錯誤(不是野指針錯誤,bad memory錯誤)
  • 低內存,當程序內存使用過多會形成系統低內存的問題,系統會將程序內存回收
  • 由於某種緣由觸發看門狗機制
atos命令

有時候經過symbolicatecrash並不能解析出來崩潰信息,或者App自身的堆棧能解析出來,可是系統堆棧解析不出來,這在解析過程當中是常常遇到的。

能夠經過atos命令進行逐行解析,經過這個命令能夠解析指定的某一行堆棧。而且這個命令是不須要dSYM的,能夠在沒有dSYM的狀況下使用。

在講atos以前先對一些基本知識瞭解一下。

architecture是指令集類型,例如arm64armv7s之類的。能夠在XcodeBuild Settings中查看,也能夠執行下面命令查看二進制文件支持的指令集。

lipo -info 二進制名

會輸出下面信息,根據崩潰設備機型選擇對應的指令集類型。

Architectures in the fat file: 二進制名 are: armv7 arm64

loadAddressBinary Images中對應的二進制首地址,每一個動態庫和二進制都有本身的首地址, 對應崩潰堆棧的首地址。

Binary Images

在每條崩潰堆棧中,都有對應的首地址和地址偏移。例如Zeus這個二進制對應的就是Binary ImagesZeus arm64的首地址,根據這個首地址加上後面的地址偏移,就是函數所在的函數地址。

崩潰堆棧

以上面崩潰堆棧爲例,atos命令格式

atos -arch arm64 -o Zeus -l 0x10063000 0x00000001009795d4

經過這個命令能夠解析出具體的崩潰信息

-[FocusView updateImageViews] (in Zeus) (FocusView.m:119)

若是是系統堆棧,則須要把二進制名Zeus換成對應的frameworkdylib名。拿Foundation舉例來講,爲了命令看起來簡單點,我先把8.4.1 (12H321)中的Foundation.framework拷貝到和Zeus統計目錄下,而後執行下面命令便可,dylib同理。

atos -arch arm64 Foundation.framework/Foundation -l 0x1830bc000 0x0000000183109f94

而後就輸出了Foundation內部的堆棧信息。

-[NSURL(NSURLPathUtilities) URLByAppendingPathComponent:] (in Foundation) + 144
注意點

1.須要注意的是,文中提到的dSYM符號化文件App二進制,都須要和當前崩潰設備的App版本、iOS系統版本、Architectures指令集對應,地址偏移也不要寫錯,不然會致使解析出來的堆棧不正確。

atos -arch arm64 -o Zeus -l 0x10063000 0x000000010092b52a

例如上面命令解析的堆棧以下

-[DetailHeaderView layoutSubviews] (in Zeus) (DetailHeaderView.m:34)

可是我用其餘版本的二進制解析,一樣的命令,解析結果可能就不同。

-[ShareManager manager] (in Zeus) (ShareManager.m:194)

因此崩潰信息中最上面的一些參數就很重要,包含了咱們須要的iOS版本、App build號、指令集類型等信息。

2.不管是App的堆棧仍是系統堆棧,若是使用系統自帶的symbolicatecrashatos兩種方式不能解析,都是由於沒有對應的dSYM或二進制、符號化文件。

若是手裏有對應的設備,第一次插入電腦後鏈接Xcode,會顯示Processing symbol files。這個過程就是拷貝符號化文件到iOS DeviceSupport目錄的過程,拷貝完成後就能夠拿到符號化文件了。

崩潰統計工具

經過Xcode查看設備崩潰信息

除了上面的系統分析工具來進行分析,也能夠將發生崩潰的設備鏈接Xcode,選擇window-> devices -> 選擇本身的手機 -> view device logs 就能夠查看手機上全部的崩潰信息了。

view device logs

只要手機上的應用是這臺電腦安裝打包的,這樣的崩潰信息系統已經爲咱們符號化好了,只須要進去以後等一會就行(不要相信這裏面的進度刷新,並不許確,親測….)。若是仍是沒有符號化完畢 ,能夠選擇文件,而後右擊選擇Re-Sysbomlicate就能夠。

若是是使用其餘電腦進行的打包,能夠在這裏面將Crash文件導出,本身經過命令行的方式進行解析。

第三方崩潰分析工具

如今有不少第三方工具均可以進行崩潰統計分析,使用比較多的是友盟崩潰統計,友盟崩潰統計被集成在友盟SDK中,具體用法能夠直接看官方文檔,這裏很少作敘述。

可是我並不推薦友盟,而是推薦一個很是強大的崩潰統計工具—Bugly,我公司項目也在使用Bugly。它最大的優點在於,能夠直接將崩潰信息分析出來而且作好分類和彙總,並且一些咱們本身分析不出來或很難分析出來的崩潰,Bugly都能分析出來。固然須要提早上傳dSYMBugly後臺,不然可能會致使一些崩潰分析不出來。

崩潰統計

這是Bugly統計的崩潰分佈,能夠選擇版本、時間之類的。並且Bugly不只限於崩潰統計,還有卡頓分析和錯誤分析,我感受這兩個也比較實用。相對友盟或者其餘崩潰分析工具,Bugly是一個輕量級的崩潰統計工具。

頁面追蹤

出錯堆棧Bugly會幫咱們解析好,而且會根據不一樣狀況給一些解決建議,這個我就不截圖了。

Bugly有一個頁面追蹤功能,這是我認爲很是有用的一個功能。這個功能會將用戶在不一樣頁面之間跳轉的流程記錄下來。這樣對於復現bug是頗有用的,能夠根據用戶頁面跳轉推測出用戶大概操做流程,根據這個流程復現bug

日報功能

Bugly還有日報功能,能夠天天彙總一篇日報,而且發到團隊每一個人的郵箱和微信號上。我認爲這個功能很是實用,天天大概八九點上班路上,掏出手機看一下微信上Bugly日報,想一下項目哪可能有問題,到公司正好解決bug

蘋果自帶崩潰統計工具

蘋果在Xcode中爲咱們集成了崩潰統計功能,在Window -> Organizer -> Crashes中能夠看到。

Xcode Crashes

蘋果自帶的崩潰統計工具並不推薦用,若是想要使用這個功能,須要用戶在iPhone中進行設置設置 -> 隱私 -> 診斷與用量 -> 診斷與用量數據(iOS8如下在通用中設置)選擇自動發送,並與開發者共享。

然而不少人並不想和開發者共享數據,或者不設置這個選項,那這樣就收集不到這部分的崩潰。

第三方工具惡意覆蓋

崩潰收集統計函數應該只進行一次調用,若是用第三方的話也最好只用一個第三方,這樣獲取崩潰統計信息的途徑也是惟一的。第三方統計工具並非用的越多越好,使用多個崩潰收集第三方會致使NSSetUncaughtExceptionHandler()函數指針的惡意覆蓋,致使有些第三方不能收到崩潰信息。

如今不少第三方崩潰收集工具爲了確保本身能最大可能的收集到崩潰信息,會對NSSetUncaughtExceptionHandler()函數指針的惡意覆蓋。由於這個函數是將函數地址當作參數傳遞,因此只要重複調用就會被覆蓋,這樣就不能保證崩潰收集的穩定性。

相關文章
相關標籤/搜索