Printk 索引¶
有許多方法可以監控系統狀態。一個重要的資訊來源是系統日誌。它提供了大量資訊,包括或多或少重要的警告和錯誤訊息。
存在可根據記錄的訊息進行過濾和採取行動的監控工具。
核心訊息與程式碼一起演進。因此,特定的核心訊息不是 KABI,也永遠不會是!
這對於維護系統日誌監控器來說是一個巨大挑戰。它需要知道特定核心版本中哪些訊息被更新了以及原因。在原始碼中找到這些更改需要複雜的解析器。此外,它還需要將原始碼與二進位制核心進行匹配,這並非總是輕而易舉。各種更改可能會被回溯移植。不同的受監控系統上可能使用不同的核心版本。
這就是 printk 索引功能可能變得有用的地方。它提供了執行時系統中核心和模組所使用的原始碼中所有 printk 格式的轉儲。它可以透過 debugfs 在執行時訪問。
printk 索引有助於查詢訊息格式中的更改。它還有助於將字串追溯到核心原始碼和相關的提交。
使用者介面¶
printk 格式索引被分成單獨的檔案。這些檔案根據內建 printk 格式的二進位制檔案命名。始終存在“vmlinux”檔案,並且可選地還有模組檔案,例如
/sys/kernel/debug/printk/index/vmlinux
/sys/kernel/debug/printk/index/ext4
/sys/kernel/debug/printk/index/scsi_mod
請注意,只顯示已載入的模組。此外,當模組是內建時,來自模組的 printk 格式可能會出現在“vmlinux”中。
內容受動態除錯介面啟發,格式如下
$> head -1 /sys/kernel/debug/printk/index/vmlinux; shuf -n 5 vmlinux
# <level[,flags]> filename:line function "format"
<5> block/blk-settings.c:661 disk_stack_limits "%s: Warning: Device %s is misaligned\n"
<4> kernel/trace/trace.c:8296 trace_create_file "Could not create tracefs '%s' entry\n"
<6> arch/x86/kernel/hpet.c:144 _hpet_print_config "hpet: %s(%d):\n"
<6> init/do_mounts.c:605 prepare_namespace "Waiting for root device %s...\n"
<6> drivers/acpi/osl.c:1410 acpi_no_auto_serialize_setup "ACPI: auto-serialization disabled\n"
,其中含義是
這些額外資訊使得查詢不同核心之間的差異變得有些困難。特別是行號可能會經常變化。另一方面,它極大地有助於確認字串是否相同,或者找到負責最終更改的提交。
printk() 不是穩定的 KABI¶
一些開發者擔心,將所有這些實現細節匯出到使用者空間會把特定的 printk() 呼叫轉換為 KABI。
但事實恰恰相反。printk() 呼叫_不_能是 KABI。而 printk 索引有助於使用者空間工具處理這種情況。
特定子系統的 printk 封裝函式¶
printk 索引是使用儲存在專用的 .elf 節“.printk_index”中的額外元資料生成的。它是透過使用宏封裝函式 __printk_index_emit() 以及真正的 printk() 呼叫實現的。動態除錯功能使用的元資料也採用了相同的技術。
元資料僅在使用這些特殊封裝函式列印特定訊息時才儲存。它已為常用的 printk() 呼叫實現,例如,pr_warn() 或 pr_once()。
對於透過公共輔助函式呼叫原始 printk() 的各種特定子系統封裝函式,需要進行額外的更改。這些函式需要新增 __printk_index_emit() 的自己的封裝函式。
到目前為止,只有少數特定子系統的封裝函式得到了更新,例如 dev_printk()。因此,某些子系統的 printk 格式可能在 printk 索引中缺失。
特定子系統字首¶
宏 pr_fmt() 允許定義一個字首,該字首會在相關的 printk() 呼叫生成的字串之前列印。
特定子系統的封裝函式通常會新增更復雜的字首。
這些字首可以透過 __printk_index_emit() 的可選引數儲存到 printk 索引元資料中。debugfs 介面隨後可能會顯示包含這些字首的 printk 格式。例如,drivers/acpi/osl.c 包含
#define pr_fmt(fmt) "ACPI: OSL: " fmt
static int __init acpi_no_auto_serialize_setup(char *str)
{
acpi_gbl_auto_serialize_methods = FALSE;
pr_info("Auto-serialization disabled\n");
return 1;
}
這會產生以下 printk 索引條目
<6> drivers/acpi/osl.c:1410 acpi_no_auto_serialize_setup "ACPI: auto-serialization disabled\n"
這有助於將實際日誌中的訊息與 printk 索引進行匹配。然後,可以使用原始檔名、行號和函式名將字串與原始碼匹配。