使用動態輸出列印核心的DEBUG資訊

2023-01-16 06:00:56

簡介

printk()是很多嵌入式開發者喜歡用的偵錯手段之一,但是,使用printk()每次都要重新編譯核心,很不方便。使用動態輸出在不需要重新編譯核心的情況下,方便的列印出核心的debug資訊。

要開啟動態輸出,核心需要新增CONFIG_DYNAMIC_DEBUG。開啟宏之後,pr_debug(),dev_dbg()print_hex_dump_debug()print_hex_dump_bytes()`所有資訊都可以被動態列印出來。

動態輸出支援的特性

動態輸出在debugfs檔案系統中對應的是control檔案節點。control檔案節點記錄了系統中所有使用動態輸出技術的檔名路徑,輸出語句所在的行號、模組名和將要輸出的語句等。

你可以通過以下命令檢視目前所有偵錯狀態的行為設定:

cat /sys/kernel/debug/dynamic_debug/control

你也可以應用標準的Unix文字過濾命令來過濾這些資料, 例如:

grep -i rdma /sys/kernel/debug/dynamic_debug/control  | wc -l

在第三列顯示了偵錯狀態位的啟用標誌。如果無額外行為被激話, 為 "=_"。 因此你可以通過下面的命令檢視任何不是預設標誌的狀態位:

awk '$3 != "=_"' <debugfs>/dynamic_debug/control

命令列使用方法

在語法層面上,一個命令由一系列的規格匹配組成,最後由一個標記來改變這規格。

command ::= match-spec* flags-spec

match-spec常用來選擇一個已知的dprintk()呼叫點的子集來套用flags-spec。把他們當做彼此之間的每對做隱式查詢。注意,一個空的match_specs列表是有可能的,但不是非常有用,因為它不會匹配任何呼叫點的偵錯子句。

一個匹配規範由一個關鍵字組成,關鍵字控制被比較的呼叫點的屬性和要比較的值。可能關鍵字是:

match-spec ::= 'func' string |
           'file' string |
           'module' string |
           'format' string |
           'line' line-range
line-range ::= lineno |
           '-'lineno |
           lineno'-' |
           lineno'-'lineno

注意:line-range不能包含空格,例如,「1-30」是有效的範圍,但「1 - 30」就是無效的

每個關鍵字的含義如下:

  • func:給定的字串會和每個呼叫點的函數名比較。例如: func svc_tcp_accept

  • file: 給定的字串會和每個呼叫點的原始檔的全路徑名或者相對名比較。例如: file svcsock.cfile /usr/src/packages/BUILD/sgi-enhancednfs-1.4/default/net/sunrpc/svcsock.c

  • module: 給定的字串會和每個呼叫點的模組名進行比較。模組名是和在ls mod 裡看到的字串一樣。例如,module sunrpc

  • format:給定的字串會在動態偵錯格式字串裡查詢。注意這字串不需要匹配這個格式。空格和其他特殊字元能夠用八進位制字元語法來跳脫,例如空字元是\040。作為選擇,這個字串可以附上雙引號(")或者是單引號(‘)。例如:

  format svcrdma:     // NFS/RDMA 伺服器的dprintks
  format readahead     // 一些在預載入快取裡的dprintks
  format nfsd:\040SETATTR // 一個使用空格來匹配格式的方式
  format "nfsd: SETATTR" // 一個整齊的方法來用空格匹配格式
  format 'nfsd: SETATTR' // 同樣是一個用空格來匹配格式的方法和
  • line: 給定的行號或者是行號範圍會和每個dprintk()呼叫點的行號進行比較。例如:
  line 1603     // 準確定位到1603行 
  line 1600-1605 //1600行到1605行之間的6行
  line -1605     // 從第一行到1605行之間的1605行
  line 1600-     // 從1600行到結尾的全部行

標記規範包含了一個由一個或多個標記字元跟隨的變化操作。這變化操作如下所示:

- //移除給定的標記

+ //加入給定的標記

= //設定標記到給定的標記上 

f //包含已列印訊息的函數名

l //包含已在列印訊息的行號

m //包含已列印訊息的模組名

p //產生一個printk()訊息到顯示系統啟動紀錄檔

t //包含了不在中斷上下文中產生的訊息裡的執行緒ID

傳遞啟動引數給核心

在偵錯系統啟動是時,像USB核心初始化等,這些程式碼在系統進入shell前已經初始化完畢,因此無法及時開啟動態輸出語句。這時可以在核心啟動時傳遞引數給核心,在系統初始化時就開啟它們。

例如,在核心命令列中新增 usbnet.dyndbg=+plft ,就可以在啟動時開啟 usbnet的動態輸出。

在核心啟動後,通過 dmesg | grep "usbnet" 即可看到輸出的偵錯資訊。

舉例

開啟檔案svcsock.c 1603行動態輸出語句

echo -n 'file svcsock.c line 1603 +p' > /sys/kernel/debug/dynamic_debug/control

開啟檔案svcsock.c所有動態輸出語句

echo -n 'file svcsock.c +p' > /sys/kernel/debug/dynamic_debug/control

開啟NFS服務模組所有動態輸出語句

echo -n 'module nfsd +p' > /sys/kernel/debug/dynamic_debug/control

開啟函數svc_process()的所有動態輸出語句

echo -n 'func svc_process +p' > /sys/kernel/debug/dynamic_debug/control

關閉函數svc_process()的所有動態輸出語句

echo -n 'func svc_process -p' > /sys/kernel/debug/dynamic_debug/control

開啟NFS呼叫的所有以READ開始的資訊.

echo -n 'format "nfsd: READ" +p' > /sys/kernel/debug/dynamic_debug/control

檢視輸出的資訊可以使用 dmesg | grep XXX 。也可以使用 tail -f /var/log/dmesg來實時監控dmesg的紀錄檔輸出。

本文參考

dynamic-debug-howto.txt

《奔跑吧Linux核心》