使用dSYM分析App崩潰日志


前言

我們在開發App過程中,因為連接到控制台,所以遇到問題會很容易找到問題代碼。但是對於線上的App出現Crash的時候,我們不可能通過這種方式,也不現實,所以我們只能通過收集Crash信息,來解決Bug。而這種收集Crash信息並且分析定位到具體代碼的第三方SDK很多。但是今天我們來自己實現一下。

 

收集 Crash 信息

Apple提供了NSException類來幫助我們收集異常信息。

NSException is used to implement exception handling and contains information about an exception — Apple Documentation.

點擊這里來查看官方文檔具體內容。

我們的確可以通過NSException來收集信息,但是,我們怎么把這個信息保存下來,並且上傳到我們后台服務器,收集起來呢。這就需要用到另一個函數:NSUncaughtExceptionHandler

Sets the top-level error-handling function where you can perform last-minute logging before the program terminates.http://www.90168.org/

意思就是我們可以在App異常退出的之前有一分鍾的時間來處理異常信息,利用這段時間,我們可以把Crash信息寫入本地,也可以上傳到服務器,但是考慮到網絡阻塞原因,我們可能在這一分鍾不能操作完畢,所以我們把上傳放到下一次App啟動時執行。

具體的代碼如下:

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
- (void)lyCarshLog {  [self uploadExceptionLog];  NSSetUncaughtExceptionHandler(&catchExceptionLog); } - (void)uploadExceptionLog {  if (log != nil) {  // 在這里上傳 Crash 信息,上傳完畢后要記得清空。  } } void catchExceptionLog(NSException *exception) {  // 獲取 Crash 信息  NSArray *symbols = [exception callStackSymbols];  NSString *reason = [exception reason];  NSString *name = [exception name];  NSDictionary *userInfo = [exception userInfo];  //...   /*另外,我們可能需要一些別的信息,比如說發生 Crash 的設備的系統版本,設備型號,App的版本號*/  struct utsname systemInfo; // 需要導入`sys/utsname.h`頭文件。  uname(&systemInfo);  NSString *deviceString = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];  NSDictionary *appInfo = [[NSBundle mainBundle] infoDictionary];  NSString *appVersion = [appInfo objectForKey:@"CFBundleShortVersionString"];  NSString *result = [NSString stringWithFormat:@"CarshReason = %@ \n name = %@ \n userInfo = %@ \n log = %@ \n systemVersion = %f \n deviceInfo = %@ \n appVersion = %@ ",reason,name,userInfo,symbols,[UIDevice currentDevice].systemVersion.floatValue,deviceString,appVersion];  // 把 result 寫入本地。 }

Crash信息至此已經收集完畢,等待下次App啟動的時候,我們把本地的Crash信息上傳到服務器就OK了。

處理 Crash 信息 - 符號化(Fully Symbolicated)

我們得到的信息可能如下(Partially Symbolicated): carshLog

Tips: 堆棧跟蹤是自下而上展示的,也就是最先調用的方法在最下面。

每行信息中包含的信息: carshInfo

其中:

  1. Binary name 表明代碼所在App或者Framework的位置。比如:line 0 是在CoreFoundation中,line 3 在CrashDemo中…
  2. Address 方法的內存地址。
  3. Class name 當前的類名。
  4. Method name 當前調用的方法名。
  5. Offset 相對加載地址/基地址(load address)的偏移量。

我們得到這個半符號化(Partially Symbolicated)的日志對我們分析Crash原因的幫助很有限,因為我們可能只能知道__NSArrayI objectAtIndex:調用出現了問題,但是不能定位到具體代碼。所以我們要把它完全符號化(Fully Symbolicated)。

dSYM

我們需要借助dSYM來幫助我們完成符號化,對於dSYM文件的獲取,我們可以通過多種方法,我這里只說一種:

先打開XcodeWindows->Organize->找到對應的app包,然后右鍵->Show in finder,找到appName. xcarchive->顯示包內容->把dSYMs拷貝出來(或者就在里面操作)

atos

The atos command converts numeric addresses to their symbolic equivalents

我們使用atos命令來完成符號化,具體命令如下: $ atos -arch <Binary Architecture> -o <Path to dSYM file>/Contents/Resources/DWARF/<binary image name> -l <load address> <address to symbolicate> 其中:

  1. Binary Architecture: arm64armv6armv7 armv7s 根據自己的情況來寫。
  2. Path to dSYM file: dSYM文件的路徑。
  3. binary image name: 你工程的名字。
  4. load address: 基地址,如果我們的崩潰日志中沒有這個信息(比如上面的Crash信息中就沒有包含),就需要我們手動去計算這個load address:laod address = address to symbolicate - offset,比如:0x0000000102838119轉化為十進制為4337139993,再減去偏移量265,為4337139728,在轉化為十六進制0x0000000102838010
  5. address to symbolicate:當前方法的內存地址。

所以,上圖為例:

1 2
$ cd CrashDemo/dSYMs $ atos -arch arm64 -o CrashDemo.app.dSYM/Contents/Resources/DWARF/CrashDemo -l 0x0000000102838010 0x0000000102838119

這時命令就會輸出已經符號化過的信息: -[ViewController viewDidLoad] (in CrashDemo) (ViewController.m:45)

其中45就是問題代碼在ViewController.m中的具體位置。

其實符號化的過程有多種方式,你可以參考Apple文檔,對於其中UUID,只是為了我們找到App對應版本的dSYM文件,所以如果你能確定兩者的對應,不需要我們再去獲取。而且,使用上面方法,我們可以找到每一個版本對應的dSYM文件(假如你沒有刪除的話)。 allversion

最后

愉快的改Bug吧😳


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM