一、背景
內核代碼裏頻繁能看到一些用於檢查一些條件的宏,如BUG_ON,WARN_ON等,這些宏除了直觀的説明條件應該或者不應該被滿足以外,還能方便我們進行規範的打印,讓代碼更加簡潔,也同時滿足的一些基本的要求,如在內核代碼裏進行打印時不建議直接使用printk,需要啓用dev_printk或者至少得用pr_xx這樣的打印宏。你若使用printk來進行打印輸出的時候,如果用./scripts/checkpatch.pl來掃描你的改動的patch的話,假設發現是直接使用printk的話,checkpatch.pl的檢查腳本會報出一個警告的。
這篇博客裏,大家一次介紹一下這些基本的宏。
二、BUG/BUG_ON宏
我們搜索BUG_ON宏可能搜到,它是分為可以是ARCH代碼來實現,也可以不用ARCH代碼用公共的代碼來實現:
通過如上圖能夠看到BUG_ON就是判斷入參的條件,要是入參的條件是true,就調用BUG宏來觸發panic。
CONFIG_BUG默認情況下就是打開的:
在沒有定義arch的BUG_ON函數時,就用公共代碼的BUG_ON構建。
許可看到是有有些arch下構建自己的BUG_ON的,搜索HAVE_ARCH_BUG_ON就可以搜到:
如mips平台的BUG_ON實現:
三、WARN_ON,WARN_ON_ONCE,WARN_ONCE宏
3.1 WARN_ON的實現
被包在一般都開的CONFIG_BUG宏裏的:就是WARN_ON的下面的定義也
用的上圖裏的定義。就是常見的arm64平台和x86平台也都
大家進一步看__WARN宏的實現,__WARN宏根據有沒有定義__WARN_FLAGS的情況,
來進行的定義:
而__WARN_FLAGS在x86和arm64平台都有相關定義:
我們追一下arm64平台的也就是上圖裏的__BUG_FLAGS是如何實現的,如上圖看到__BUG_FLAGS使用了ASM_BUG_FLAGS,ASM_BUG_FLAGS定義如下:
BUG_BRK_IMM,定義及如下:就是可以看到上面圖裏紅色框出的部分,也就是用了brk彙編指令,傳入的
3.2 WARN_ON_ONCE和WARN_ONCE的區別
WARN_ON_ONCE和WARN_ONCE在內核代碼裏大量利用,它們倆是有區別的,哪怕區別並不複雜,但是卻很有用,且容易被忽視。
WARN_ON_ONCE的定義如下:
它是無法輸入自定義的字符串的。
而WARN_ONCE則不同,可以輸入自定義的字符串:
這個區別很關鍵,有時候就必須定義自己的日誌內容。
3.3 WARN_ONCE如何確保只打印一次
如何實現的:就是接着上面分析的WARN_ONCE宏來看一下WARN_ONCE宏所調用的DO_ONCE_LITE_IF宏
如上圖行看到DO_ONCE_LITE_IF宏利用了__ONCE_LITE_IF宏來判斷是否已經打印了一次。
而__ONCE_LITE_IF宏聲明瞭一個static的bool類型的變量,這樣就可以保留之前是否已經打印一次的狀態了。
3.4 如何清楚打印了一次的標記
__section(".data.once")的段,這個為什麼要用這個段呢,源於這個段可以讓環境針對這些WARN_ONCE的標記的變量統一進行一些批處理。就是可以從上面 3.3 的分析裏可以看到,在聲明用於判斷是否打印一次的變量時,用得
對該.data.once段的所有標記位進行清除:就是下圖裏的clear_warn_once_set函數就
而clear_warn_once_set被集成進clear_warn_once_fops後,
最後暴露出debugfs的用户態可以進行控制的節點:
通過有關的能夠清楚WARN_ONCE標記的命令如下:
echo 1 > /sys/kernel/debug/clear_warn_once