前言
崩潰問題修復(fù)是每一個(gè)客戶端開發(fā)者治理的重難點(diǎn),一般崩潰有三道程序監(jiān)防:1、程序員代碼編程規(guī)范化及Codereview ;2、程序員自測及測試部門同事測試;3、線上檢測工具bugly、Firebase監(jiān)測。崩潰修復(fù)是一個(gè)長期復(fù)雜的過程,程序員治理優(yōu)化程序的過程中,同時(shí)也是提高自身能力和了解更廣泛知識(shí)的過程。要想解決崩潰,首先就要學(xué)會(huì)定位線上崩潰問題,本文就總結(jié)分享了開發(fā)中定位崩潰問題的幾種方法和大家做個(gè)分享。
崩潰問題定位
最重要的三個(gè)文件
- dSYM
- .crash / .ips 文件
- .app
并將三文件整理在一個(gè)文件夾中
流程:準(zhǔn)備三文件 - > 符號化 -> 生成可直觀的文件
dSYM文件獲取
dSYM 是保存 16 進(jìn)制函數(shù)地址映射信息的中轉(zhuǎn)文件,我們調(diào)試的 symbols 都會(huì)包含在這個(gè)文件中,并且每次編譯項(xiàng)目的時(shí)候都會(huì)生成一個(gè)新的 dSYM 文件,位于 /Users/<用戶名>/Library/Developer/Xcode/Archives 目錄下
一般開發(fā)者在完成一個(gè)版本后,就要遷出一個(gè)版本出來。選擇真機(jī)或選擇generic iOS Device, Product -> Archive,隨后
XCode -> Window ->Organizer -> XXArchive文件 -> Show in Finder -> 顯示包內(nèi)容 -> dSYMs -> ***.app.dSYM
一般使用bugly線上監(jiān)測,還應(yīng)把該文件上傳到bugly上幫助定位問題,上傳腳本如下:
dsym文件和bugly文件放在同一路徑下
java -jar buglyqq-upload-symbol.jar -appid *** -appkey **** -bundleid com...* -version 3.. -platform IOS -inputSymbol ..dSYM
若想上傳app內(nèi)引用的其他庫的符號表,以方便查找是不是三方庫的原因,可把..dSYM 替換成對應(yīng)的三方庫dsym,如:***.framework.dSYM 即可</pre>
|
.app文件獲取
XCode -> Window ->Organizer -> XXArchive文件 -> Show in Finder -> 顯示包內(nèi)容 ->Products -> Applications -> *.app
.crash文件獲取
1)手機(jī)連接Mac獲取
XCode -> Window ->Devices and simulators -> view Device Logs
搜索你app的名稱,即可找到真機(jī)產(chǎn)生的crash。
2)參與TestFight與上線共享crash
崩潰時(shí)可以直觀的在XCode查看crash的代碼行。
設(shè)備設(shè)置: 設(shè)置 -> 隱私 -> 與應(yīng)用開發(fā)者共享。
獲取報(bào)告:iTunes Connect ->Manage Your Applications -> View Details -> Crash Reports。導(dǎo)出即可。
定位方法
1、通過.dSYM符號表手動(dòng)分析定位
下面是報(bào)錯(cuò)的堆棧信息:
10 **** 0x00000001052119b4 0x0000000104cd4000 + 5495220
如何分析出第7行的地址指向哪個(gè)方法呢?
終端執(zhí)行如下指令
|
atos -arch arm64 -o ***.app.dSYM/Contents/Resources/DWARF/*** -l 0x0000000104cd40000x0000000105212c48
|
arm64:cpu架構(gòu),發(fā)生報(bào)錯(cuò)的APP的手機(jī)的cpu架構(gòu),可以通過手機(jī)型號去查詢確認(rèn)。
.app.dSYM/Contents/Resources/DWARF/:其中是APP的名字,導(dǎo)出符號表默認(rèn)的,后面的路徑是固定的,只要將替換成你的APP名字即可。(需要 cd 到符號表的文件目錄下)
0x0000000104cd4000 + 5499976:0x0000000104cd4000表示默認(rèn)Slide Address,5499976表示偏移量。
0x0000000105212c48:表示錯(cuò)誤信息內(nèi)存地址。
注意這兩個(gè)地址的先后順序。
執(zhí)行結(jié)果如下:
|
MacBook`` test % atos -arch arm64 -o ***.app.dSYM/Contents/Resources/DWARF/*** -l 0x0000000104cd4000 0x0000000105212c48
-[take hold:] (socket:686)
|
得出-[socket onSocket:didReadData:withTag:]代碼位置。
分析輔助工具
dSYM分析工具可以幫助定位
https://github.com/answer-huang/dSYMTools
[圖片上傳失敗...(image-2f1457-1684396669921)]
2、通過hooper 反匯編定位
當(dāng)發(fā)生Unix 信號的崩潰時(shí)不好定位問題,可借助hooper反編譯來定位
10 *** 0x00000001052119b4 0x0000000104cd4000 + 5495220
由于是線上的崩潰,在拿到 xxx.xcarchive 包之后,把包里面的二進(jìn)制文件拖進(jìn) hopper ,選擇 Arm64 架構(gòu)。接下來我們就可以分析跳轉(zhuǎn)到具體的崩潰位置了,剛好崩潰的線程有包含我們的項(xiàng)目代碼;
0x0000000104cd4000 是項(xiàng)目的基地址,5499976 是十進(jìn)制的偏移量,而這個(gè) 0x0000000105212c48 則是崩潰地址,剛好是 0x0000000104cd4000 + 5499976 的結(jié)果。
所以,我們下面要做的就是跳轉(zhuǎn)到 0x0000000105212c48 這個(gè)崩潰地址看看響應(yīng)的匯編代碼。
hopper -> Modify -> Change File Base Address... ,然后把基地址 0x0000000104cd4000 輸入,重新 Rebase 一下。
hopper -> Navigate -> Go to Address or Symbol... ,輸入 0x0000000105212c48 這個(gè)地址,然后 go 到對應(yīng)的崩潰地址
通過上述步驟可以定位大概代碼位置,然后根據(jù)業(yè)務(wù)推斷問題出錯(cuò)點(diǎn),再結(jié)合修改即可了。
常見崩潰問題
1)Mach 異常
最底層的內(nèi)核級異常。用戶態(tài)的開發(fā)者可以直接通過Mach API設(shè)置thread,task,host的異常端口,來捕獲Mach異常。
新建一個(gè)監(jiān)控線程,在監(jiān)控線程中監(jiān)聽 Mach 異常并處理異常信息。主要的步奏如下圖:
2)Unix信號
又稱BSD 信號,如果開發(fā)者沒有捕獲Mach異常,則會(huì)被host層的方法ux_exception()將異常轉(zhuǎn)換為對應(yīng)的UNIX信號,并通過方法threadsignal()將信號投遞到出錯(cuò)線程。可以通過方法signal(x, SignalHandler)來捕獲signal。
Unix Signal 其實(shí)是由 Mach port 拋出的信號轉(zhuǎn)化的,大致信號:
SIGHUP
本信號在用戶終端連接(正?;蚍钦?結(jié)束時(shí)發(fā)出, 通常是在終端的控制進(jìn)程結(jié)束時(shí), 通知同一session內(nèi)的各個(gè)作業(yè), 這時(shí)它們與控制終端不再關(guān)聯(lián)。SIGINT
程序終止(interrupt)信號, 在用戶鍵入INTR字符(通常是Ctrl-C)時(shí)發(fā)出,用于通知前臺(tái)進(jìn)程組終止進(jìn)程。SIGQUIT
和SIGINT類似, 但由QUIT字符(通常是Ctrl-)來控制. 進(jìn)程在因收到SIGQUIT退出時(shí)會(huì)產(chǎn)生core文件, 在這個(gè)意義上類似于一個(gè)程序錯(cuò)誤信號。SIGABRT
調(diào)用abort函數(shù)生成的信號。
SIGABRT is a BSD signal sent by an application to itself when an NSException or obj_exception_throw is not caught.SIGBUS
非法地址, 包括內(nèi)存地址對齊(alignment)出錯(cuò)。比如訪問一個(gè)四個(gè)字長的整數(shù), 但其地址不是4的倍數(shù)。它與SIGSEGV的區(qū)別在于后者是由于對合法存儲(chǔ)地址的非法訪問觸發(fā)的(如訪問不屬于自己存儲(chǔ)空間或只讀存儲(chǔ)空間)。SIGFPE
在發(fā)生致命的算術(shù)運(yùn)算錯(cuò)誤時(shí)發(fā)出. 不僅包括浮點(diǎn)運(yùn)算錯(cuò)誤, 還包括溢出及除數(shù)為0等其它所有的算術(shù)的錯(cuò)誤。SIGKILL
用來立即結(jié)束程序的運(yùn)行. 本信號不能被阻塞、處理和忽略。如果管理員發(fā)現(xiàn)某個(gè)進(jìn)程終止不了,可嘗試發(fā)送這個(gè)信號。SIGSEGV
試圖訪問未分配給自己的內(nèi)存, 或試圖往沒有寫權(quán)限的內(nèi)存地址寫數(shù)據(jù).SIGPIPE
管道破裂。這個(gè)信號通常在進(jìn)程間通信產(chǎn)生,比如采用FIFO(管道)通信的兩個(gè)進(jìn)程,讀管道沒打開或者意外終止就往管道寫,寫進(jìn)程會(huì)收到SIGPIPE信號。-
SIGSYS
非法的系統(tǒng)調(diào)用。
-
SIGTRAP
由斷點(diǎn)指令或其它 trap 指令產(chǎn)生. 由d ebugger 使用。
-
SIGILL
執(zhí)行了非法指令. 通常是因?yàn)榭蓤?zhí)行文件本身出現(xiàn)錯(cuò)誤, 或者試圖執(zhí)行數(shù)據(jù)段. 堆棧溢出時(shí)也有可能產(chǎn)生這個(gè)信號。
3)應(yīng)用級NSException
應(yīng)用級異常,它是未被捕獲的Objective-C異常,導(dǎo)致程序向自身發(fā)送了SIGABRT信號而崩潰,是app自己可控的,對于未捕獲的Objective-C異常,是可以通過try catch來捕獲的,或者通過NSSetUncaughtExceptionHandler()機(jī)制來捕獲。
非主線程刷新UI
NSInvalidArgumentException
非法參數(shù)異常(NSInvalidArgumentException)是 Objective – C 代碼最常出現(xiàn)的錯(cuò)誤,所以平時(shí)在寫代碼的時(shí)候,需要多加注意,加強(qiáng)對參數(shù)的檢查,避免傳入非法參數(shù)導(dǎo)致異常,其中尤以nil參數(shù)為甚。NSRangeException
越界異常(NSRangeException)也是比較常出現(xiàn)的異常。NSGenericException
NSGenericException這個(gè)異常最容易出現(xiàn)在foreach操作中,在for in循環(huán)中如果修改所遍歷的數(shù)組,無論你是add或remove,都會(huì)出錯(cuò) “for in”,它的內(nèi)部遍歷使用了類似 Iterator進(jìn)行迭代遍歷,一旦元素變動(dòng),之前的元素全部被失效,所以在foreach的循環(huán)當(dāng)中,最好不要去進(jìn)行元素的修改動(dòng)作,若需要修改,循環(huán)改為for遍歷,由于內(nèi)部機(jī)制不同,不會(huì)產(chǎn)生修改后結(jié)果失效的問題。NSInternalInconsistencyException
不一致導(dǎo)致出現(xiàn)的異常
比如NSDictionary當(dāng)做NSMutableDictionary來使用,從他們內(nèi)部的機(jī)理來說,就會(huì)產(chǎn)生一些錯(cuò)誤
NSMutableDictionary *info = method return to NSDictionary type;
[info setObject:@“sxm” forKey:@”name”];
比如xib界面使用或者約束設(shè)置不當(dāng)NSFileHandleOperationException
處理文件時(shí)的一些異常,最常見的還是存儲(chǔ)空間不足的問題,比如應(yīng)用頻繁的保存文檔,緩存資料或者處理比較大的數(shù)據(jù):
所以在文件處理里,需要考慮到手機(jī)存儲(chǔ)空間的問題。NSMallocException
這也是內(nèi)存不足的問題,無法分配足夠的內(nèi)存空間
此外還有KVO Crash
移除未注冊的觀察者
重復(fù)移除觀察者
添加了觀察者但是沒有實(shí)現(xiàn)-observeValueForKeyPath:ofObject:change:context:方法
添加移除keypath=nil
添加移除observer=nilunrecognized selector send to instance
總結(jié)
本文也只是對崩潰問題做了淺顯的總結(jié)和解決經(jīng)驗(yàn)的分享,崩潰治理是開發(fā)者長期處理的過程,不僅要解決現(xiàn)有問題,還要防范崩潰問題出現(xiàn)并且做好兜底策略;在治理過程中對個(gè)人底層知識(shí)理解也有較大幫助。
參考:
https://juejin.cn/post/6937619147505270820
http://m.itdecent.cn/p/3f6775c02257