背景

最近在调试个异常卡死的问题时,需要打印某个pcie link的,故学习了下内核里的打印,特此记录。

printk(“acpi_pci_link_set–>%pfwf, irq: %d\n”, &link->device->fwnode, irq);

内核中的格式化打印主要分为整型和指针。

整型

signed char		%d or %hhx
unsigned char		%u or %x
char			%u or %x
short int		%d or %hx
unsigned short int	%u or %x
int			%d or %x
unsigned int		%u or %x
long			%ld or %lx
unsigned long		%lu or %lx
long long		%lld or %llx
unsigned long long	%llu or %llx
size_t			%zu or %zx
ssize_t			%zd or %zx
s8			%d or %hhx
u8			%u or %x
s16			%d or %hx
u16			%u or %x
s32			%d or %x
u32			%u or %x
s64			%lld or %llx
u64			%llu or %llx

内核的printf不支持%n,浮点型(%e, %f, %g, %a)等。
有些类型的大小依赖配置选项如(如sector_t, blkcnt_t)或架构(如 tcflag_t),这时使用其可能的最大类型的格式占位符并显式强制转换为它。

指针类型

内核支持扩展占位符来打印不同类型的指针,有些不止能够打印指针地址,还能打印给定地址上的数据,名称或者相关提示等。

目前支持的指针类型如下:

  • 普通指针
  • 错误指针
  • 符号/函数指针
  • 来自BPF / tracing追踪的探查指针
  • 内核指针
  • 未修改的地址
  • 指针差异
  • 结构体资源
  • 物理地址类型phys_addr_t
  • DMA地址类型dma_addr_t
  • 原始缓冲区为转义字符串
  • 原始缓冲区为hex字符串
  • MAC/FDDI 地址
  • IPv4 地址
  • IPv6 地址
  • IPv4/IPv6 地址 (generic, with port, flowinfo, scope)
  • UUID/GUID 地址
  • 目录项(dentry)的名称
  • 块设备(block_device)名称
  • va_format结构体
  • 设备树节点
  • Fwnode handles
  • 时间和日期
  • clk结构体
  • 位图及其扩展,如cpumask和nodemask
  • 标志位字段,如页标志、gfp_flags
  • 网络设备特性
  • V4L2和DRM FourCC代码(像素格式)
  • Rust

下面展开几个可能比较常用的

符号/函数指针

%pS	versatile_init+0x0/0x110
%ps	versatile_init
%pSR	versatile_init+0x9/0x110
	(with __builtin_extract_return_addr() translation)
%pB	prev_fn_of_versatile_init+0x88/0x88

Ss 用于打印符号格式的指针。(S)带有偏移量,(s)不带。如果禁用KALLSYMS,则打印符号地址。

如果指针在一个模块内,模块名称将被打印在符号名称之后,如果再添加一个额外的 b,后面还会打印构建ID。
例如:

%pS	versatile_init+0x0/0x110 [module_name]
%pSb	versatile_init+0x0/0x110 [module_name ed5019fdf5e53be37cb1ba7899292d7e143b259e]
%pSRb	versatile_init+0x9/0x110 [module_name ed5019fdf5e53be37cb1ba7899292d7e143b259e]
	(with __builtin_extract_return_addr() translation)
%pBb	prev_fn_of_versatile_init+0x88/0x88 [module_name ed5019fdf5e53be37cb1ba7899292d7e143b259e]

MAC/FDDI地址

%pM	00:01:02:03:04:05
%pMR	05:04:03:02:01:00
%pMF	00-01-02-03-04-05
%pm	000102030405
%pmR	050403020100

用于打印以十六进制表示的6字节MAC/FDDI地址。 M带有分隔符, m则不带 。默认的字节分隔符是冒号(:)。

对于FDDI地址,可以在 M 占位符之后使用 F ,以使用破折号(——)分隔符代替默认的分隔符。

对于蓝牙地址, R 占位符应用在 M 占位符之后,以使用反转的字节顺序,适合于以小尾端顺序的蓝牙地址。

IPv4 地址

%pI4	1.2.3.4
%pi4	001.002.003.004
%p[Ii]4[hnbl]

用于打印IPv4点分隔的十进制地址。 I4i4 占位符是打印的地址有(i4)或没有(I4)前导零。

附加的 hnbl 占位符分别用于指定主机、网络、大端或小端地址。如果没有提供占位符,则使用默认的网络/大尾端顺序。

IPv6 地址

%pI6	0001:0002:0003:0004:0005:0006:0007:0008
%pi6	00010002000300040005000600070008
%pI6c	1:2:3:4:5:6:7:8

用于打印IPv6网络顺序的16位十六进制地址。 I6i6 占位符是打印的地址有(I6)或没有(i6)分号,始终使用前导零。

额外的 c 占位符可与 I 占位符一起使用,以打印压缩的IPv6地址,如https://tools.ietf.org/html/rfc5952 所述

UUID/GUID地址

%pUb	00010203-0405-0607-0809-0a0b0c0d0e0f
%pUB	00010203-0405-0607-0809-0A0B0C0D0E0F
%pUl	03020100-0504-0706-0809-0a0b0c0e0e0f
%pUL	03020100-0504-0706-0809-0A0B0C0E0E0F

用于打印16字节的UUID/GUIDs地址。附加的 l , L , bB 占位符用于指定小写(l)或大写(L)十六进制的小端顺序,以及小写(b)或大写(B)十六进制的大端顺序。

如果没有使用额外的占位符,则默认打印小写十六进制的大端顺序

块设备(block_device)名称

%pg	sda, sda1 or loop0p1

用于打印block_device指针的名称。

设备树节点

%pOF[fnpPcCF]

用于打印设备树节点结构,默认为%pOFf
其余参数说明:

  • f - 设备节点全称
  • n - 设备节点名
  • p - 设备节点句柄
  • P - 设备节点路径规范(名称+@单位)
  • F - 设备节点标志位
  • c - 主要兼容字符串
  • C - 全兼容字符串

当使用多个参数时,分隔符是’:’,例如:

%pOF	/foo/bar@0			- Node full name
%pOFf	/foo/bar@0			- Same as above
%pOFfp	/foo/bar@0:10			- Node full name + phandle
%pOFfcF	/foo/bar@0:foo,device:--P-	- Node full name + major compatible string + node flags

节点标志位(node flags)说明:

  • D - dynamic
  • d - detached
  • P - Populated
  • B - Populated bus

Fwnode handles

%pfw[fP]

用于打印fwnode_handles的消息。默认情况打印完整的节点名称,包括路径。
其余参数说明:

  • f - 节点的全名,包括路径。
  • P - 节点名称,包括地址(如果有的话)。
  • (ACPI)例子:
    %pfwf	\_SB.PCI0.CIO2.port@1.endpoint@0	- Full node name
    %pfwP	endpoint@0				- Node name
  • (OF)设备树例子:
    %pfwf	/ocp@68000000/i2c@48072000/camera@10/port/endpoint - Full name
    %pfwP	endpoint				- Node name

    时间和日期

    %pt[RT]			YYYY-mm-ddTHH:MM:SS
    %pt[RT]s		YYYY-mm-dd HH:MM:SS
    %pt[RT]d		YYYY-mm-dd
    %pt[RT]t		HH:MM:SS
    %pt[RT][dt][r][s]
    用于以我们(人类)可读的格式打印日期和时间:
    R  struct rtc_time structure
    T  time64_t type
    默认情况下,年将以1900为单位递增,月将以1为单位递增。
    %pt[RT]s(空格)将覆盖ISO 8601的分隔符,在日期和时间之间使用’’(空格)而不是’T’(大写T)。当日期或时间被省略时,它不会有任何影响。

V4L2和DRM FourCC代码(像素格式)

打印V4L2或DRM使用的FourCC代码,包括格式端序及其十六进制的数值

%p4cc	BG12 little-endian (0x32314742)
%p4cc	Y10  little-endian (0x20303159)
%p4cc	NV12 big-endian (0xb231564e)

参考

  • 内核文档:Documentation/core-api/printk-formats.rst