原文:https://www.kernel.org/doc/html/latest/core-api/printk-index.html
Printk索引
有许多方法可以监视系统的状态。一个重要的信息来源是系统日志。它提供了大量信息,包括更或不那么重要的警告和错误消息。
有一些监控工具可以根据记录的消息进行过滤并采取行动。
内核消息随着代码的演变而不断发展。因此,特定的内核消息不是KABI,也永远不会成为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"
其中的含义是:
-
level
日志级别值:0-7表示特定严重性,-1表示默认值,'c'表示连续行而没有显式的日志级别 -
flags
可选标志:目前只有'c'表示KERN_CONT -
filename:line
相关printk()调用的源文件名和行号。请注意,有许多包装器,例如pr_warn(),pr_warn_once(),dev_warn()。 -
function
使用printk()调用的函数名。 -
format
格式字符串
额外的信息使得在不同的内核版本之间找到差异有点困难。特别是行号可能会经常改变。另一方面,它在确认它是相同的字符串或找到负责任何变化的提交方面有很大帮助。
printk()不是一个稳定的KABI
一些开发人员担心将所有这些实现细节导出到用户空间会将特定的printk()调用转变为KABI。
但事实恰恰相反。printk()调用必须 不 是KABI。而printk索引帮助用户空间工具处理这一点。
子系统特定的printk包装器
printk索引是使用存储在专用.elf节".printk_index"中的额外元数据生成的。它是通过使用宏包装器__printk_index_emit()
与真正的printk()调用来实现的。动态调试功能也使用相同的技术。
当使用这些特殊包装器打印特定消息时,元数据仅为该消息存储。它适用于常用的printk()调用,包括例如pr_warn()
或pr_once()
。
对于调用原始printk()的各种子系统特定包装器,需要进行额外的更改,这些包装器通过一个通用的辅助函数调用原始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索引进行匹配。然后,可以使用源文件名、行号和函数名将字符串与源代码进行匹配。