Linux 死機復位(oops、panic)問題定位指南

一個計算機系統和一個人類社會其實是差不多的,系統在運行中碰到的各種bug相當於人類社會中的各種案件:user space發生的bug危害性一般,可能就相當於一般的民事案件;kernel層面發生bug引起系統死機復位,屬於性質特別惡劣後果特別嚴重的刑事案件。

既然bug相當於案件,那麼我們定位bug的過程和破案是差不多的。一般過程如下:

1. 首先我們要保留案發現場。

只要bug發生的時候cpu還能執行,大部分的軟件bug最後都會落入到cpu的陷阱之中。arm準備了3大陷阱來捕獲最後的案發現場:undefine instruction、prefetch abort、data abort。不管程序前面發生了什麼錯誤,最後的出錯都會掉到這3大陷阱中去。類似人類社會中最後看到了刑事案件,大家會撥打110報警。

在這裏插入圖片描述

內核態的代碼,只要掉入到了這3大陷阱之中,最後的結果都會觸發oops然後panic()復位重啓系統。在oops信息中,會打出bug的pc指針、cpu r0-r15寄存器和堆棧信息等最後的案發現場信息。

1.1 oops信息詳解:

這裏使用“echo “1 2” > /proc/aed/generate-oops”在mtk平臺上生成一個kernel NULL pointer類型的oops,來闡釋oops信息。
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述

從上圖的解析,通過oops的信息我們可以得到幾個重要數據:最後出錯的PC指針、函數調用關係、r0-r15寄存器的值、r0-r15指向內存的值、一些重要標誌。

當然因爲一些軟件錯誤(踩堆棧)、硬件錯誤(內存跳變),我們最後得到的oops信息其中一項或者幾項是已經出錯沒有了參考價值的,我們需要利用信息之間的相互關係來排插出更多的線索。

1.2 從oops信息反查源代碼:

我們從oops信息中得到最後的pc值和符號信息,最終的目的還是需要和源代碼聯繫起來。有以下方法:

(1)、addr2line:

arm-linux-androideabi-addr2line -e vmlinux addr,會打印出addr對應的源代碼行號。

在這裏插入圖片描述
在這裏插入圖片描述

上圖通過addr2line工具,找到最後的異常PC指針0xc04e6ffc對應源代碼ade-debug.c文件的252行。

(2)、gdb:

在這裏插入圖片描述

上圖通過gdb的命令“l *0xc04e6ffc”實現addr2line命令同樣的功能,找到對應源代碼的位置ade-debug.c文件的252行。

在這裏插入圖片描述

有時我們需要研究彙編代碼,可以使用gdb的命令“disassemble /m access_null_pointer”來查看pc所在函數access_null_pointer()的彙編代碼,/m 選項是同時打印出源碼行號。

(3)、objdump:

查看彙編源代碼,也可以使用objdump命令來實現。

在這裏插入圖片描述
在這裏插入圖片描述

上圖通過“objdump -S vmlinux > kernel.s ”命令生成kernel對應彙編文件,然後再彙編文件中根據pc地址0xc04e6ffc找到相應的位置。

1.3 Backtrace堆棧回溯原理:

在oops信息中,我們可以看到內核調用dump_backtrace()函數打印出堆棧調用關係。這其中的原理是什麼樣的呢?

在這裏插入圖片描述

上圖可以看到arm c語言到彙編語言轉換的實際過程,可以看到在每一層函數調用的過程中,arm使用了fp指針來記錄這一層的堆棧頂部,而且堆棧中固定位置都有保存上一級的fp指針。具體的堆棧示意圖如下:

在這裏插入圖片描述

所以我們只需要找到這一層次的fp指針,就能一層層的找到函數回調的關係。對應上個oops例子,我們可以手工的來回溯。

在這裏插入圖片描述

可以看到,我們手工計算的調用關係和調用dump_backtarce()打印出來的能一一對應上:

在這裏插入圖片描述

當然,在堆棧被破壞的情況下,這種方法就不適用了,這時候我們只能查看stack殘存的數據,看看哪些是code信息,來推斷猜測回調關係。

1.4 陷阱異常定位思路:

oops信息可以初步定位一些陷阱bug,比如我們檢視代碼在最後bug pc附近就發現了軟件邏輯錯誤,那麼我們很快就找到了bug的根因。

但是綜上,陷阱異常提供了oops信息,信息只是線索不是定罪的證據。那麼我們拿到這些線索以後,有可能能找到問題的元兇,有可能還需要要更多的分析手段。

  • 陷阱異常定位思路 :
步驟 信息 行動 next
step1 PC指針信息OK,根據PC指針找到源代碼對應的位置 觀察源代碼位置前後的邏輯,推導是否有符合最後錯誤的情況發生。 如果這一步的信息不能定位到bug根因,繼續往下一步走。
step1 PC指針信息無效 根據出錯的場景步驟,嘗試復現bug,並往硬件錯誤方向嘗試定位
step2 堆棧回調信息ok 分析堆棧調用鏈上的邏輯,分析可能產生的bug符合最後的錯誤情況。 如果這一步的信息不能定位到bug根因,繼續往下一步走。
step2 堆棧回調信息無效
step3 反彙編代碼 反彙編出錯位置的代碼,分析彙編代碼的前後關係,來嘗試推斷,故障是軟件造成的,還是硬件造成的 如果這一步的信息不能定位到bug根因,繼續往下一步走。
step4 分析bug的前後log 分析bug發生前的kernel log,重點關注err、fail等關鍵字打印,分析推導是否有這些出錯引起了後面的重啓bug 如果這一步的信息不能定位到bug根因,繼續往下一步走。
step5 更多的定位手段1 - 專項監測手段
step6 更多的定位手段2 – 更多的現場信息。
step7 硬件traceponit來監控被破壞內存地址的修改 追蹤是哪個模塊修改了內存
step8 最後的定位大法:復現和排除 先復現, 再來一個一個因素的排除,直到故障不出現,那麼bug的相關因素就會被排除出來。

2. 更多的定位手段1 - 專項監測手段。

bug都會落入到arm的3大陷阱中去,我們能得到最後的故障現象信息。但是僅僅只是一份案發現場,我們其實很難分析出起始的案發原因,因爲第一步可能只是一個小小的錯誤,經過多層次的反覆累加,最後才釀成了刑事案件。

bug是有幫助的,比如我們檢視代碼在最後bug pc附近就發現了軟件邏輯錯誤,那麼我們很快就找到了bug的根因。

bug,最後現場的信息室遠遠不夠的,我們需要更多的監測手段,在故障到達最後的陷阱之前,就能監控到它已經出錯,至少能縮小定位的範圍。比如我們人類公安對社會的監控:在主要街道設置了監控頭,在汽車站、火車站、高速站、飛機場都有監控,在銀行、互聯網、電話都有監控,對於不對的苗頭在發展成刑事案件之前都能監控捕捉到。

2.1 專項檢測手段:

系統也是一樣,我們設置了很多的專項監測措施來監控各個子系統,力圖早點發現bug,縮小定位範圍。例如:

  • (1)、我們在代碼運行之前做代碼靜態掃描,相當於人類社會的基因掃描,在運行之前先剔除有問題的基因。

  • (2)、在運行中用”lockdep”檢測可能的死鎖問題,報出死鎖方面的詳細信息。

  • (3)、在運行中用”stack protect”檢測可能的堆棧溢出問題,報出溢出方面的詳細信息。

  • (4)、在運行中用”huang task detector”檢測可能的任務hung死問題,報出hung死詳細信息。

  • (5)、在運行中用”kmem leak detector”檢測可能的內存泄露問題,報出kmem泄露的詳細信息。

  • (6)、在運行中還有smp_process_id、schedule、sleep、irq、spinlock、mutex、rt-mutex、workqueue等都有各自模塊的檢測手段,這些檢測手段檢測到問題都會及時報出,提前了問題保留的時間、縮小了問題定位範圍。

2.2 BUG()宏的實現原理:

除了代碼邏輯錯誤在出錯的時候會掉入arm的陷阱異常,動態檢測手段檢測到邏輯出錯後,會調用BUG()這個宏來主動觸發陷阱異常,以此來報告錯誤。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-spQBjxVI-1592915928346)(images/stability/BUG.png)]

3. 更多的定位手段2 – 更多的現場信息。

oops打印出來的現場信息過少,有一種思路是抓取更多的現場信息,例如coredump。

產生kernel coredump的方法:

mtk aee系統在panic的時候,會調用ipanic_mrdump_mini()創建一個mini coredump文件,就是我們在解壓開aee目錄後看到的SYS_MINI_RDUMP文件。這個記錄的信息比較少,但是在wdt復位時拿來看每個cpu的堆棧非常方便。

調試coredump的方法:

可以使用gdb來調試coredump文件,命令格式爲:gdb vmlinux coredumpfile

在這裏插入圖片描述

啓動以後是一個多進程的調試環境,每個進程對應一個cpu,LWP1xx是出錯時的CPU,LWP1-LWP8對應CPU0-CPU7。

使用命令info threads查看當前所有進程:
在這裏插入圖片描述

使用thread Id來切換當前調試的進程:

在這裏插入圖片描述

使用bt命令來查看當前進程的堆棧回調:
在這裏插入圖片描述

oops臨終現場一樣,得到更多的最後現場,不一定就能推斷出bug的起始原因。更多信息只能給我們破案提供更多的線索,不能保證找到錯誤的根因。

4. 沒有掉入陷阱的錯誤檢測手段 - WDT。

arm的3大陷阱之中,還有一種不會落入陷阱的情況:cpu在運行,但是系統已經掛死。這種情況叫hang住,通常是代碼中出現了死循環,或者是因爲死鎖系統產生了死循環。還有一種hang住,就是硬件的hang住,訪問硬件出錯造成cpu停止運行掛死。

WDT監控對於MTK 平臺來說, 大體的做法是每一個cpu core 對應一個Watchdog Thread [wdtk-X], 週期性(20s)的去寫RGU register, 而RGU register 的timeout 時間是30s, 即如果出現一個core 的watchdog thread 沒有按時踢狗, 那麼就會觸發一個timeout 的FIQ, 產生一個KE 引發系統完整重啓。

在這裏插入圖片描述

4.1 wdt log信息解析:

wdt的詳細原理可以參考:。

主要的思路就是WDT超時以後,首先在cpu0上會產生一個FIQ,在FIQ中記錄臨終現場類似如oops,並且打印出每個cpu的調用堆棧。最後在cpu0上調用panic()復位整系統,如果FIQ或者panic()沒有響應成功,後面還有一個硬件超時的reset信號發出來。

在這裏插入圖片描述

4.2 wdt復位定位思路:

  • WDT異常定位思路
步驟 信息 行動 next
step1 根據"kick=0x0000000X,check=0x0000000X"的信息找出是哪個cpu沒有餵狗 結合每個cpu打印出的回調信息,推斷出當時的死鎖情景 如果這一步的信息不能定位到bug根因,繼續往下一步走。
step2 分析bug的前後log 分析bug發生前的kernel log,重點關注err、fail等關鍵字打印,分析推導是否有這些出錯引起了後面的重啓bug 如果這一步的信息不能定位到bug根因,繼續往下一步走。
step3 最後的定位大法:復現和排除 先復現,再來一個一個因素的排除,直到故障不出現,那麼bug的相關因素就會被排除出來。

5. Hardware Reboot。

最難搞的復位,既沒有被arm陷阱異常捕獲到,又沒有被WDT監控到,運行過程中硬件出現bug,系統直接復位。

5.1 Hardware Reboot:

HW bug,常見和以下的模塊相關:
Hardware Reboot相關模塊

分類 相關驅動模塊
Power CPU DVFS、CPU IDLE、CPU HotplugGPU DVFSMEM DVFSPMIC
Clock CPU DVFS、CPU IDLE、CPU HotplugGPU DVFSMEM DVFS
Memory & Memory Controller MEM DVFS、MEM失效
Fail IC TP、sensor、connect等所有驅動模塊IC的硬件失效

所以hardware reboot一方面可能是因爲硬件本身的不合造成的,另一方面也可能是以上模塊軟件配置不正確造成的異常。所以我們的穩定性測試需要充分覆蓋以上的所有模塊。

5.2 Hardware Reboot定位思路:

對於hardware reboot故障的定位,可以依照以下的SOP先篩選出是否是haedware相關的問題,按照步驟一步步進行確認。

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

6. 最後的定位大法:復現和排除。

如果上述的所有手段都不能定位到bug根因的話,我們只有祭出最後的大招。先復現,再來一個一個因素的排除,直到故障不出現,那麼bug的相關因素就會被排除出來。

這個方法的好處是針對任何bug都是適用的,但是穩定復現,是一個不小的工作量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章