# iOS Crashes 分析

3831603266272_.pic.jpg

iOS 崩潰原因

  1. CPU resource Limit CPU 占用率過高
  2. 觸發(fā)watch dog 主線程超過20s未響應(yīng)
  3. Memory limit exceeded內(nèi)存超出上限
  4. Memory pressure exit : JatSam
  5. background task timeout

崩潰原因解析

  1. CPU resource limit
    應(yīng)用程序處理任務(wù)時間過長,系統(tǒng)可能會終止程序執(zhí)行,建議使用 iOS 13 BGProcessingTask

  2. watch dog
    出現(xiàn)場景: 應(yīng)用啟動或者重新進入前臺
    可能原因:

  • dead lock
  • 無限循環(huán)
  • 主線程無法完成的任務(wù)
    注意:處于調(diào)試下是不會出現(xiàn) Watchdog 的
  1. Memory limit exceeded
  2. JatSam

在 Linux 系統(tǒng)中,交換空間(swap space)可以用來存放內(nèi)存中不常用的臨時數(shù)據(jù)。而 iOS 系統(tǒng)因為閃存容量和讀寫壽命的原因,并沒有引入交換空間,取而代之的是 Compressed memory 技術(shù),即當內(nèi)存緊張時,壓縮一些內(nèi)存內(nèi)容,并在需要時解壓。但副作用是會造成較高的 CPU 占用甚至卡頓,手機耗電量也會隨之增加。

為了解決上面的問題,蘋果設(shè)計了 Jetsam 機制。其工作方式是當內(nèi)存不足時,系統(tǒng)會通知前臺應(yīng)用去釋放內(nèi)存(通過 applicationDidReceiveMemoryWarning 方法和 UIApplicationDidReceiveMemoryWarningNotification 通知),如果內(nèi)存壓力依然存在,將會終止一些后臺應(yīng)用。最終內(nèi)存還不夠的話,就會終止當前應(yīng)用(FOOM),并且上報日志。

iOS dsym和link-map文件分析

/Users/sunxx/Library/Developer/Xcode/Archives/2020-10-20/xxxPM.xcarchive/dSYMs/AFNetworking.framework.dSYM/Contents/Resources/DWARF/AFNetworking

使用atos解析crash 文件

atos -arch arm64 -o /Users/sun/Library/Developer/Xcode/Archives/xx.xcarchive/dSYMs/AFNetworking.framework.dSYM/Contents/Resources/DWARF/AFNetworking -l 0x1000e4000 0x00000001000effdc

-[xxxxviewController runContenxt:]

  • -arch:指令集,現(xiàn)在release的都包含arm64指令集架構(gòu)。可以通過atool -hv appname查看相關(guān)的指令集;
  • -o :目標文件:可執(zhí)行文件。
  • -l : load address,及發(fā)生crash對應(yīng)的鏡像起始地址;后面一個地址是symblicate address,及符號地址。

使用linkmap 查看二進制文件信息

理解Link Map File,可以幫助我們:

  • 理解鏈接過程
  • 理解Crash時,通過Symbols還原出源碼的機制
  • 理解內(nèi)存分段、分區(qū)
  • 分析可執(zhí)行文件中哪個類或庫占用比較大,進行安裝包瘦身
生成link-map 文件

Xcode 開啟編譯選項:設(shè)置 Write Link Map fileYES

截屏2020-10-21 上午9.51.46.png

查看路徑:~/Library/Developer/Xcode/DerivedData/XXX-

  • __text表示編譯后的程序執(zhí)行語句
  • __data表示已初始化的全局變量和局部靜態(tài)變量
  • __bss表示未初始化的全局變量和局部靜態(tài)變量
  • __cstring表示代碼里的字符串常量

其中一些常用的信息如下:

//  __TEXT段中的一些Section
1. __text: 代碼節(jié),存放機器編譯后的代碼
2. __stubs: 用于輔助做動態(tài)鏈接代碼(dyld).
3. __stub_helper:用于輔助做動態(tài)鏈接(dyld).
4. __objc_methname:objc的方法名稱
5. __cstring:代碼運行中包含的字符串常量,比如代碼中定義`#define kGeTuiPushAESKey        @"DWE2#@e2!"`,那DWE2#@e2!會存在這個區(qū)里。
6. __objc_classname:objc類名
7. __objc_methtype:objc方法類型
8. __ustring:
9. __gcc_except_tab:
10. __const:存儲const修飾的常量
11. __dof_RACSignal:
12. __dof_RACCompou:
13. __unwind_info:
// __DATA段中的一些Section
1. __got:存儲引用符號的實際地址,類似于動態(tài)符號表
2. __la_symbol_ptr:lazy symbol pointers。懶加載的函數(shù)指針地址。和__stubs和stub_helper配合使用。具體原理暫留。
3. __mod_init_func:模塊初始化的方法。
4. __const:存儲constant常量的數(shù)據(jù)。比如使用extern導(dǎo)出的const修飾的常量。
5. __cfstring:使用Core Foundation字符串
6. __objc_classlist:objc類列表,保存類信息,映射了__objc_data的地址
7. __objc_nlclslist:Objective-C 的 +load 函數(shù)列表,比 __mod_init_func 更早執(zhí)行。
8. __objc_catlist: categories
9. __objc_nlcatlist:Objective-C 的categories的 +load函數(shù)列表。
10. __objc_protolist:objc協(xié)議列表
11. __objc_imageinfo:objc鏡像信息
12. __objc_const:objc常量。保存objc_classdata結(jié)構(gòu)體數(shù)據(jù)。用于映射類相關(guān)數(shù)據(jù)的地址,比如類名,方法名等。
13. __objc_selrefs:引用到的objc方法
14. __objc_protorefs:引用到的objc協(xié)議
15. __objc_classrefs:引用到的objc類
16. __objc_superrefs:objc超類引用
17. __objc_ivar:objc ivar指針,存儲屬性。
18. __objc_data:objc的數(shù)據(jù)。用于保存類需要的數(shù)據(jù)。最主要的內(nèi)容是映射__objc_const地址,用于找到類的相關(guān)數(shù)據(jù)。
19. __data:暫時沒理解,從日志看存放了協(xié)議和一些固定了地址(已經(jīng)初始化)的靜態(tài)量。
20. __bss:存儲未初始化的靜態(tài)量。比如:`static NSThread *_networkRequestThread = nil;`其中這里面的size表示應(yīng)用運行占用的內(nèi)存,不是實際的占用空間。所以計算大小的時候應(yīng)該去掉這部分數(shù)據(jù)。
21. __common:存儲導(dǎo)出的全局的數(shù)據(jù)。類似于static,但是沒有用static修飾。比如KSCrash里面`NSDictionary* g_registerOrders;`, g_registerOrders就存儲在__common里面
0x101AE43A0 0x00000018  [699] __OBJC_$_PROTOCOL_REFS_PLPlayerDelegate
0x101AE43B8 0x00000128  [699] __OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_PLPlayerDelegate
0x101AE44E0 0x00000060  [699] __OBJC_$_PROTOCOL_METHOD_TYPES_PLPlayerDelegate

再看一段編譯的代碼

0x10181373D 0x0000008D  [699] literal string: ^{AudioBufferList=I[1{AudioBuffer=II^v}]}88@0:8@16^{AudioBufferList=I[1{AudioBuffer=II^v}]}24{AudioStreamBasicDescription=dIIIIIIII}32q72q80
0x1018137CA 0x00000011  [699] literal string: v32@0:8@16i24i28
0x1018137DB 0x00000015  [699] literal string: v24@0:8@"PLPlayer"16
0x1018137F0 0x00000018  [699] literal string: v32@0:8@"PLPlayer"16q24
0x101813808 0x00000021  [699] literal string: v32@0:8@"PLPlayer"16@"NSError"24
0x101813829 0x0000001F  [699] literal string: v48@0:8@"PLPlayer"16{?=qiIq}24
0x101813848 0x0000002E  [699] literal string: v48@0:8@"PLPlayer"16^{__CVBuffer=}24q32i40i44
0x101813876 0x00000097  [699] literal string: ^{AudioBufferList=I[1{AudioBuffer=II^v}]}88@0:8@"PLPlayer"16^{AudioBufferList=I[1{AudioBuffer=II^v}]}24{AudioStreamBasicDescription=dIIIIIIII}32q72q80
0x10181390D 0x00000020  [699] literal string: v32@0:8@"PLPlayer"16@"NSData"24
0x10181392D 0x00000023  [699] literal string: v40@0:8@"PLPlayer"16@"NSData"24q32
0x101813950 0x0000001B  [699] literal string: v32@0:8@"PLPlayer"16i24i28
0x10181396B 0x00000018  [699] literal string: v28@0:8@"PLPlayer"16B24
0x101813983 0x00000011  [699] literal string: v36@0:8@16i24@28
0x101813994 0x0000001C  [699] literal string: v24@0:8@"SuperPlayerView"16
0x1018139B0 0x0000002C  [699] literal string: v36@0:8@"SuperPlayerView"16i24@"NSString"28
0x1018139DC 0x0000000C  [699] literal string: @"PLPlayer"

由此可以推斷,Xcode 在鏈接動態(tài)庫的時候只是在使用動態(tài)庫文件的位置添加了一個描述符而已

lipo 使用

  1. 查看庫信息lipo -info xxx.framework/xxx
  2. 合并靜態(tài)庫
    lipo -create xxx1.a xxx2.a -output xxxnew
lipo -create xxx1.framework/xxx1  xxx2.framework/xxx2 -output xxxnew.framework/xxxnew
  1. 移除指定架構(gòu)
lipo 靜態(tài)庫路徑 -remove 架構(gòu)名 -output 存放路徑
  1. 靜態(tài)庫拆分
lipo 靜態(tài)庫源路徑 -thin 架構(gòu)名 -output 存放路徑
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容