【除錯】SystemTap除錯網路卡狀態一例
除錯其實不僅僅是針對核心或者程式崩潰的情況,很多時候我們需要跟蹤的問題並不是通過分析一個core dump能夠解決的,比如類似一些狀態資訊輸出不對,或者核心或程式行為不符合預期。此時我們經常需要依賴於日誌,尤其是核心層面的問題。但是日誌往往並不不如我們期望的那樣包羅永珍,常常要面臨的窘境是日誌中空空如也。原因也很容易理解,列印日誌需要程式碼中實現的,而發生問題這部分程式碼邏輯中沒有相關實現,自然也就沒有任何日誌了。此時我們也可以考慮gdb,但是在雲上做gdb kernel除錯代價極大,基本我們不會考慮。
那麼今天我們就來了解一下SystemTap這樣一個輕量的除錯工具,該工具堪稱Linux上核心除錯的神器,筆者之前有多年的Windows除錯經驗,在開始使用SystemTap之後也不得不感嘆其強大。他的優點在於自由度高,並且可以在live的系統上執行,因此相當方便和高效。
首先我們簡單瞭解一下SystemTap的原理:
SystemTap的基本工作原理是將指令碼編譯成核心模組,核心模組載入以後用於檢查執行的核心的兩種方法是Kprobes和Kretprobe,兩種服務都整合在Linux核心中,Kprobes的原理相對簡單,他在需要探測的執行指令處加上特定指令,這部分原理其實和偵錯程式是類似的,因此一旦執行被探測的函式就會轉入SystemTap的指令碼邏輯中。Kretprobe相對複雜,需要理解堆疊機制的工作原理,簡單來講它通過修改堆疊上函式返回地址來達到嵌入指令的目的。
為了更快了解SystemTap的使用方法,我們還是利用一個例項來逐步講解。
問題現象:
這也是一個比較有趣的問題,使用者在雲上例項中使用ip link命令後發現他的eth0狀態顯示為Unknown,如下圖:
但是如果我們建立一個相同規格並且在同一個可用區的例項是無法復現的。升級核心後問題依然存在。
研究步驟:
研究疑難問題的時候思路往往大同小異,依然是不斷對自己提問的過程,很顯然第一個問題自然就是這個state是從哪裡獲取的,或者說資料來源是什麼。
1. 資料來源在哪裡?
我自己是從ip link的程式碼出發來尋找資料來源:
顯然是來自核心網路裝置物件中的operstate:
事實上源資料是可以從如下檔案獲得:
/sys/devices/pci0000:00/0000:00:03.0/virtio0/net/eth0/operstate
2. 除錯什麼?
有了資料來源,接下去一個問題是,我們雖然知道錯誤的狀態是從哪裡來的,可是這只是一個靜態的資料,對我們似乎沒有意義。可以繼續我們研究的關鍵在於 – 這個資料是什麼時候被設上的。知道了這一點我們至少可以知道我們去除錯哪個過程。這個部分和除錯技術本身關聯就不大了,我們完全可以充分發散思路。我自己最後是挑選了這樣一個過程作為我的除錯物件:
rmmod virtio_net
modprobe virtio_net
重新載入虛擬網路卡驅動,驅動被重新載入了,自然所有的網路裝置的狀態也會重新設,那麼我們就可以重點研究這個過程中為什麼把operstate設成了unknown。
3. 閱讀程式碼:
閱讀程式碼永遠是除錯的核心步驟,我們現在尋找一下核心中哪裡會設定operstate:
我們看到在上面這部分程式碼是總是會設定operstate,無論是IF_OPER_LOWERLAYERDOWN,IF_OPER_DOWN或者IF_OPER_UP,至少不會是IF_OPER_UNKNOWN。也就是很有可能在非正常情況並沒有呼叫到default_operstate()。那麼如果確認呢?那就該輪到SystemTap登場了。
SystemTap登場:
SystemTap安裝比較簡單:
yum install kernel-devel
yum install systemtap
接下去是安裝符號檔案,centos的話可以從debuginfo.centos.org下載到對應的符號檔案,rpm安裝即可。
建立一個stp指令碼如:
probe begin
{
prinf(“stap begin
“);
}
probe kernel.function(“default_operstate”)
{
printf(“calling default_operstate
“)
}
執行stap -g setlink.stp即可。探測開始會列印“stap begin”,然後我們就可以開始執行rmmod virtio_net;modprobe virtio_net,觀察是否有輸出default_operstate,當然最好的方法是準備一臺正常的機器進行對比。對比結果當然是正如預期,正常的情況下能夠輸出“calling default_operstate”,而非正常情況卻沒有輸出。
4. 體力活:
真正的體力活開始了,接下去的思路非常簡單:
- 閱讀程式碼,看每一層的呼叫情況。
- 一旦有不確認的情況,使用systemtap確認呼叫路徑。
目的只有一個找到程式碼源頭上的區別。舉一個例子,確認呼叫路徑如下:
rfc2863_policy->default_operstate
但是有兩處程式碼會呼叫rfc2863_policy,linkwatch_do_dev和linkwatch_init_dev,於是我們不想動腦的分析的話,直接修改stp指令碼如:
probe begin
{
prinf(“stap begin
“);
}
probe kernel.function(“linkwatch_do_dev“)
{
printf(“calling linkwatch_do_dev
“)
}
probe kernel.function(“linkwatch_init_dev“)
{
printf(“calling linkwatch_init_dev
“)
}
在正常和非正常的機器執行stap對比輸出即可知道我們下一步的方向了。那麼中間的步驟我們就不贅述了,直奔主題:
正常機器:首先呼叫netif_carrier_off然後再call netif_carrier_on->linkwatch_fire_event->linkwatch_do_dev->rfc2863_policy->default_operstate
非正常機器:直接call netif_carrier_on,因此以下邏輯導致無法觸發event:
if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
if (dev->reg_state == NETREG_UNINITIALIZED)
return;
atomic_inc(&dev->carrier_changes);
linkwatch_fire_event(dev);
上面的邏輯簡單理解為,如果先呼叫netif_carrier_off,那麼裝置會被標記為__LINK_STATE_NOCARRIER,之後核心網路棧監測到網路鏈路是通的,就會呼叫netif_carrier_on,此時會判斷__LINK_STATE_NOCARRIER是否已經標記上了,如果是說明之前的鏈路是不通的,那麼需要改變狀態就會傳送event觸發operstate的改變。但是如果直接呼叫netif_carrier_on,裝置並沒有被標記上__LINK_STATE_NOCARRIER,也就是鏈路直接就是通,不沒有必要傳送event觸發後面關於operstate的邏輯了,自然operstate就停留在unknown的狀態了。
netif_carrier_off是在virtio_net驅動中呼叫的。
這裡有一個邏輯判斷後端有無設上VIRTIO_NET_F_STATUS,如果是那麼我們會呼叫netif_carrier_off,如果不是那麼直接呼叫netif_carrier_on,導致問題。如果想進一步確認在這個邏輯裡的問題,很簡單,修改stap探測響應的程式碼行就可以了,仔細研究還會發現stap很多功能,比如列印引數,探測程式碼行,列印堆疊,都可以根據具體情況靈活應用。
問題結論:
VIRTIO_NET_F_STATUS是在後端qemu中設定的,於是我們據此就可以區分問題是否是前端還是後端產生的。
相關文章
- IE console.log 除錯狀態除錯
- 除錯篇——除錯物件與除錯事件除錯物件事件
- Xcode動態除錯原理XCode除錯
- Windows windbg kernel debug 雙機核心除錯 - USB3.0 除錯 USB除錯 除錯線Windows除錯
- phpstorm進行動態除錯PHPORM除錯
- Apk_動態除錯方案APK除錯
- Python 程式碼除錯—使用 pdb 除錯Python除錯
- IsDebuggerPresent的反除錯與反反除錯除錯
- Android adb 網路除錯Android除錯
- nginx 錯誤除錯Nginx除錯
- 動態除錯及LLDB技巧集合除錯LLDB
- iOS 常用除錯方法:靜態分析iOS除錯
- 前端除錯前端除錯
- python 除錯Python除錯
- LLDB除錯LLDB除錯
- postman除錯Postman除錯
- Linux除錯Linux除錯
- 除錯toybox除錯
- gdb除錯除錯
- 移動端網頁除錯網頁除錯
- 網頁外部注入vConsole除錯網頁除錯
- react-query手把手教程④-快取狀態與除錯工具React快取除錯
- 反除錯&反反除錯 -- 利用sysctl檢測偵錯程式是否存在除錯
- vsstudio中靜態庫lib的除錯除錯
- IDA動態除錯解RC4除錯
- .NET高階除錯系列-Windbg除錯入門篇高階除錯
- win10 如何除錯串列埠_win10串列埠除錯怎麼除錯Win10除錯串列埠
- vscode使用chrome除錯報錯VSCodeChrome除錯
- oracle關閉狀態刪除活動日誌報錯恢復(一)Oracle
- console 除錯技巧除錯
- Express 文件(除錯)Express除錯
- Chrome 除錯技巧Chrome除錯
- console除錯命令除錯
- css除錯技巧CSS除錯
- Sanic 除錯模式除錯模式
- nodejs除錯指南NodeJS除錯
- AS之Debug除錯除錯
- webstrom除錯vueWeb除錯Vue