你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> 手把手教你如何分析 iOS 系統棧 crash

手把手教你如何分析 iOS 系統棧 crash

編輯:IOS開發基礎

作者:李偉
先上棧,這個 crash 是我們目前開發產品的 top5 crash

012.png

第一步

對於死在 ojbc _ msgSend 的函數(不僅僅是 msgSend, objc_retain 等一切沒有創建棧幀的都需要注意),請先檢查 crash 上報的寄存器信息。

013.png

一般來說,lr 肯定不等於第一個棧。 目前的 crash上報功能,丟失了最頂層的棧。因為 objc_msgSend 並沒有創建棧幀。

這樣,我們就得根據 lr,來計算真實的最後一個棧了。

棧幀介紹

014.png

棧幀,保存當前函數返回地址,以及上級棧幀地址。 這樣,通過枚舉棧幀,即可得到函數的調用棧。

第二步

在模塊列表查找,lr 是那個模塊的

015.png

找到了,計算絕對偏移,找出對應函數地址。(函數絕對偏移 = lr - 模塊基址)同樣,反過來就可以在本機或者 IDA 查找函數了)

libAVFAudio.dylib`AVAudioSessionPropertyListener(void, unsigned int, unsigned int, void const) + 1796

好了,終於找到調用 objc_msgSend 的點了。

第三步

運行程序,找到野掉的對象到底是個什麼

AVAudioSessionRouteDescription (這裡需要根據 selector 再次核實上一步驟的調用點是否正確。crash 時 selector 存放在 x1 寄存器, 有時候上報平台會打印出 x-selector detect, 對比下 selector 是否一致,一致則說明上一步得到的地址沒有問題)

第四步

這個對象是哪裡來的

需要調試神器 lzmalloc 命令(話說,這個命令實在是太好用了。)

lzmalloc 為我們自己開發的調試器輔助命令,用於打印對象分配以及釋放點的堆棧信息。

下面為 lzmalloc 結果

016.png

第五步

到這裡為止,首先排查了自己代碼內部對於 AVAudioSessionRouteDescription 確定不存在過度釋放的問題,不得已,只有逆向了。(最蛋疼的步驟了)

首先確定野掉的 AVAudioSessionRouteDescription 來源於

 libAVFAudio.dylib`AVAudioSessionPropertyListener(void, unsigned int, unsigned int, void const) + 1768

而此行是調用函數

 -[AVAudioSession privateConfigureRouteDescription:]

而 privateConfigureRouteDescription 從lzmalloc 結論來看,內部是調用

 +[AVAudioSessionRouteDescription privateCreateOrConfigure:withRawDescription:]

第六步

首先逆向

+[AVAudioSessionRouteDescription privateCreateOrConfigure:withRawDescription:]

函數邏輯大概如下

test config

if(!change) return orgDes;

else  release(orgDes); alloc newDes。 newDes retain autorelease

從這個邏輯,可以看出來,如果是 new 出來的對象,那是絕對不可能野的。

所以,對象只可能是返回了 orgDes。

第七步

逆向
 -[AVAudioSession privateConfigureRouteDescription:]

函數邏輯大概如下

lock
{

   get orgDes
   newdes = call privateCreateOrConfigure:withRawDescription:
   return newdes
}

發現問題了嗎? 如果 newdes = orgdes 呢。 而函數返回後,剛好另一個線程執行了 privateCreateOrConfigure:withRawDescription: 而這個時候,config 又恰好變動呢。 orgDes  會被釋放!! 哎,這個鎖算是白加了。

第八步

問題原因可能猜到了。但是如何修改呢?

hook 

-[AVAudioSession privateConfigureRouteDescription:]  內部調用原函數之後加上 retain autorelease? 似乎挺理想,但是仔細想想,還是沒什麼用啊,照樣阻止不了其他線程 privateCreateOrConfigure:withRawDescription:的調用。當然這個可以很大降低概率,因為間隔代碼很少。

so,換種思路,根據之前動態調試的結果 privateCreateOrConfigure:withRawDescription: 觸發時機,有兩個,一個是系統耳機插拔通知的時候,另一個就是我們自己調用  audiosession.currentroute 的時候。 而系統通知只在 audio 線程調用。所以呢,既然如此,那我們自己干脆不調用了,在系統通知的時候,在回調裡面保存最新的。 當需要訪問 audiosession.currentroute 直接返回我們保存的值。 這樣,沖突不就沒了

第九步

修改外發

很幸運,已經消滅了這個問題。

第十步

總結下

最近發現不少蘋果的內存問題。 不知道為什麼蘋果自己代碼很多都不使用 arc,也許這樣做很 cool!!

不過,連蘋果這麼牛這麼自信的開發,都弄出了這麼多難纏的問題。我們還是不要向他學習,老老實實的用好 ARC 吧。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved