標籤:
以下內容主要學習自debug hacks一書。
有關核心調試方面的技巧,我更多的是將其映射為應用程式的調試技巧。我覺得大部分都是線程間同步的問題。
1.對於NULL指標的訪問。
這個問題一般來講是比較容易確定的,一般來講就是未申請空間,或者申請空間失敗了但是沒有檢查錯誤。這個可以通過backtrace和源碼確定發生錯誤的位置。
這裡介紹兩個gdb的新命令。info symbol和maintenance translate-address,這兩個命令可以把某個地址值轉化為符號表中的資訊,或者和符號表中某個符號的差值,這個功能在調試時可以大大地提高效率。
2.鏈表破壞。
這個是由於線程間同步問題導致的錯誤。核心中採用的確定方法是,將被刪除過的節點或者鏈表項的前後指標指向兩個非法的地址,但不是null,所以當出現段錯誤時,發現非法訪問時由這兩個非法地址所引起的,那麼就可以確定是由於訪問已經被刪除的鏈表項或者空鏈表導致的問題。解決方案是不管訪問還是修改,都需要先獲得該資料結構的鎖。
3.從代碼發現可能的錯誤
書中講了一個有關釋放inode的例子,inode是快取中的檔案節點,如果記憶體不夠時,就會釋放某些不使用的inode。由於線程間同步問題,在一個函數中,對某個資料結構的操作過程中拿到鎖之後,中途釋放了該鎖,其他等待線程對該資料結構操作之後,原線程又拿鎖再一次對這個資料結構進行操作。這時候就可能會導致一定的問題。
這種錯誤在應用程式中,應該是出現了段錯誤,對於這個資料結構的訪問出現了問題,那麼使用gdb對這個地址進行監視,或者前後有函數的地方設定斷點,列印堆棧,確定多線程之間的順序。
解決這種問題,中途不釋放鎖,那麼就不會出現差錯。
4.核心停止回應(死迴圈)
映射為應用程式的死迴圈技巧的話,在上一篇文章中我已經講過了,定位加複現加單步調試。而在核心中更多的是查看核心轉儲,因為此時不可能使用crash來調試,當核心轉儲檔案沒什麼效果時,就該使用大招,加列印。其實加列印是最簡單直接,但是繁瑣的調試方法。
重點:
認真詢問故障發生時的情況
在自己的環境中複現
實驗各種各樣的條件。
5.核心停止回應(自旋鎖)
類似於應用程式中的死結,進程A需要擷取進程B的鎖,進程B想要擷取進程A的鎖,導致了死結的發生。
停止回應時,首先應該擷取到當前的dump資訊,然後分析dump資訊,當前進程正在做什麼。確定是在拿鎖,然後在拿鎖這個函數上設定斷點,列印堆棧,確定多線程的流程。
6.核心停止回應(自旋鎖2)
還是查看堆棧,發現有兩個相同的函數調用,這兩個函數中都存在拿鎖的操作。解決過程一般結合源碼和dump檔案進行分析。根據堆棧追蹤源碼。
這裡的技巧是根據堆棧查看源碼,發現問題的原因。
7.核心停止回應(訊號量)
其實這個問題還是由於線程間同步的問題,這個問題是由於讀寫訊號量的問題導致的。可以查看堆棧資訊,如果在堆棧中可以發現兩個互斥的操作,那麼問題也就解決了,但是如果不夠詳細,那麼需要重新運行,設定斷點,列印bt資訊。
8.即時進程停止回應
這裡擷取到的經驗還是通過堆棧查看問題原因所在,還有一個是通過查看進程佔用的時間。ps -t。
9.運行緩慢的故障
當一個進程運行需要很長時間時,可以通過strace進行跟蹤,查看在哪個函數中調用的時間最長,當然strace僅限於系統調用的時間。
10.cpu負載過高
這一條和前兩條,8,9,10這三條的重點我覺得在於查看一個進程在各個函數鎖停留的時間。這裡學習了一個很重要的工具oprofile,可以解析每個函數的被調用次數,運行總時間,調用關係等等。如果僅僅是關心應用程式層函數,也就是自己所寫的函數,可以使用gprof來檢測。
總結:
上面10條中,大部分都是先查看堆棧,然後複現問題,加斷點,列印堆棧,查看衝突所在。
有6條是屬於線程間同步的問題,234567這六條。
第1條屬於簡單地查看堆棧即可確定錯誤。
最後的三條需要使用oprofile來確定函數或者進程的已耗用時間各方面的情況。
所以在解決問題時,一般流程,收集資訊,分析dump檔案,複現bug,查明原因,修複bug,測試。
這裡學到的手段,查看堆棧,查看運行情況。
讀核心調試總結