요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
=========================================
How to get printk format specifiers right
=========================================
.. _printk-specifiers:
:Author: Randy Dunlap <[email protected]>
:Author: Andrew Murray <[email protected]>
Integer types
=============
::
If variable is of Type, use printk format specifier:
------------------------------------------------------------
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
If <type> is architecture-dependent for its size (e.g., cycles_t, tcflag_t) or
is dependent on a config option for its size (e.g., blk_status_t), use a format
specifier of its largest possible type and explicitly cast to it.
Example::
printk("test: latency: %llu cycles\n", (unsigned long long)time);
Reminder: sizeof() returns type size_t.
The kernel's printf does not support %n. Floating point formats (%e, %f,
%g, %a) are also not recognized, for obvious reasons. Use of any
unsupported specifier or length qualifier results in a WARN and early
return from vsnprintf().
Pointer types
=============
A raw pointer value may be printed with %p which will hash the address
before printing. The kernel also supports extended specifiers for printing
pointers of different types.
Some of the extended specifiers print the data on the given address instead
of printing the address itself. In this case, the following error messages
might be printed instead of the unreachable information::
(null) data on plain NULL address
(efault) data on invalid address
(einval) invalid data on a valid address
Plain Pointers
--------------
::
%p abcdef12 or 00000000abcdef12
Pointers printed without a specifier extension (i.e unadorned %p) are
hashed to prevent leaking information about the kernel memory layout. This
has the added benefit of providing a unique identifier. On 64-bit machines
the first 32 bits are zeroed. The kernel will print ``(ptrval)`` until it
gathers enough entropy.
When possible, use specialised modifiers such as %pS or %pB (described below)
to avoid the need of providing an unhashed address that has to be interpreted
post-hoc. If not possible, and the aim of printing the address is to provide
more information for debugging, use %p and boot the kernel with the
``no_hash_pointers`` parameter during debugging, which will print all %p
addresses unmodified. If you *really* always want the unmodified address, see
%px below.
If (and only if) you are printing addresses as a content of a virtual file in
e.g. procfs or sysfs (using e.g. seq_printf(), not printk()) read by a
userspace process, use the %pK modifier described below instead of %p or %px.
Error Pointers
--------------
::
%pe -ENOSPC
For printing error pointers (i.e. a pointer for which IS_ERR() is true)
as a symbolic error name. Error values for which no symbolic name is
known are printed in decimal, while a non-ERR_PTR passed as the
argument to %pe gets treated as ordinary %p.
Symbols/Function Pointers
-------------------------
::
%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
The ``S`` and ``s`` specifiers are used for printing a pointer in symbolic
format. They result in the symbol name with (S) or without (s)
offsets. If KALLSYMS are disabled then the symbol address is printed instead.
The ``B`` specifier results in the symbol name with offsets and should be
used when printing stack backtraces. The specifier takes into
consideration the effect of compiler optimisations which may occur
when tail-calls are used and marked with the noreturn GCC attribute.
If the pointer is within a module, the module name and optionally build ID is
printed after the symbol name with an extra ``b`` appended to the end of the
specifier.
::
%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]
Probed Pointers from BPF / tracing
----------------------------------
::
%pks kernel string
%pus user string
The ``k`` and ``u`` specifiers are used for printing prior probed memory from
either kernel memory (k) or user memory (u). The subsequent ``s`` specifier
results in printing a string. For direct use in regular vsnprintf() the (k)
and (u) annotation is ignored, however, when used out of BPF's bpf_trace_printk(),
for example, it reads the memory it is pointing to without faulting.
Kernel Pointers
---------------
::
%pK 01234567 or 0123456789abcdef
For printing kernel pointers which should be hidden from unprivileged
users. The behaviour of %pK depends on the kptr_restrict sysctl - see
Documentation/admin-guide/sysctl/kernel.rst for more details.
This modifier is *only* intended when producing content of a file read by
userspace from e.g. procfs or sysfs, not for dmesg. Please refer to the
section about %p above for discussion about how to manage hashing pointers
in printk().
Unmodified Addresses
--------------------
::
%px 01234567 or 0123456789abcdef
For printing pointers when you *really* want to print the address. Please
consider whether or not you are leaking sensitive information about the
kernel memory layout before printing pointers with %px. %px is functionally
equivalent to %lx (or %lu). %px is preferred because it is more uniquely
grep'able. If in the future we need to modify the way the kernel handles
printing pointers we will be better equipped to find the call sites.
Before using %px, consider if using %p is sufficient together with enabling the
``no_hash_pointers`` kernel parameter during debugging sessions (see the %p
description above). One valid scenario for %px might be printing information
immediately before a panic, which prevents any sensitive information to be
exploited anyway, and with %px there would be no need to reproduce the panic
with no_hash_pointers.
Pointer Differences
-------------------
::
%td 2560
%tx a00
For printing the pointer differences, use the %t modifier for ptrdiff_t.
Example::
printk("test: difference between pointers: %td\n", ptr2 - ptr1);
Struct Resources
----------------
::
%pr [mem 0x60000000-0x6fffffff flags 0x2200] or
[mem 0x60000000 flags 0x2200] or
[mem 0x0000000060000000-0x000000006fffffff flags 0x2200]
[mem 0x0000000060000000 flags 0x2200]
%pR [mem 0x60000000-0x6fffffff pref] or
[mem 0x60000000 pref] or
[mem 0x0000000060000000-0x000000006fffffff pref]
[mem 0x0000000060000000 pref]
For printing struct resources. The ``R`` and ``r`` specifiers result in a
printed resource with (R) or without (r) a decoded flags member. If start is
equal to end only print the start value.
Passed by reference.
Physical address types phys_addr_t
----------------------------------
::
%pa[p] 0x01234567 or 0x0123456789abcdef
For printing a phys_addr_t type (and its derivatives, such as
resource_size_t) which can vary based on build options, regardless of the
width of the CPU data path.
Passed by reference.
Struct Range
------------
::
%pra [range 0x0000000060000000-0x000000006fffffff] or
[range 0x0000000060000000]
For printing struct range. struct range holds an arbitrary range of u64
values. If start is equal to end only print the start value.
Passed by reference.
DMA address types dma_addr_t
----------------------------
::
%pad 0x01234567 or 0x0123456789abcdef
For printing a dma_addr_t type which can vary based on build options,
regardless of the width of the CPU data path.
Passed by reference.
Raw buffer as an escaped string
-------------------------------
::
%*pE[achnops]
For printing raw buffer as an escaped string. For the following buffer::
1b 62 20 5c 43 07 22 90 0d 5d
A few examples show how the conversion would be done (excluding surrounding
quotes)::
%*pE "\eb \C\a"\220\r]"
%*pEhp "\x1bb \C\x07"\x90\x0d]"
%*pEa "\e\142\040\\\103\a\042\220\r\135"
The conversion rules are applied according to an optional combination
of flags (see :c:func:`string_escape_mem` kernel documentation for the
details):
- a - ESCAPE_ANY
- c - ESCAPE_SPECIAL
- h - ESCAPE_HEX
- n - ESCAPE_NULL
- o - ESCAPE_OCTAL
- p - ESCAPE_NP
- s - ESCAPE_SPACE
By default ESCAPE_ANY_NP is used.
ESCAPE_ANY_NP is the sane choice for many cases, in particularly for
printing SSIDs.
If field width is omitted then 1 byte only will be escaped.
Raw buffer as a hex string
--------------------------
::
%*ph 00 01 02 ... 3f
%*phC 00:01:02: ... :3f
%*phD 00-01-02- ... -3f
%*phN 000102 ... 3f
For printing small buffers (up to 64 bytes long) as a hex string with a
certain separator. For larger buffers consider using
:c:func:`print_hex_dump`.
MAC/FDDI addresses
------------------
::
%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
For printing 6-byte MAC/FDDI addresses in hex notation. The ``M`` and ``m``
specifiers result in a printed address with (M) or without (m) byte
separators. The default byte separator is the colon (:).
Where FDDI addresses are concerned the ``F`` specifier can be used after
the ``M`` specifier to use dash (-) separators instead of the default
separator.
For Bluetooth addresses the ``R`` specifier shall be used after the ``M``
specifier to use reversed byte order suitable for visual interpretation
of Bluetooth addresses which are in the little endian order.
Passed by reference.
IPv4 addresses
--------------
::
%pI4 1.2.3.4
%pi4 001.002.003.004
%p[Ii]4[hnbl]
For printing IPv4 dot-separated decimal addresses. The ``I4`` and ``i4``
specifiers result in a printed address with (i4) or without (I4) leading
zeros.
The additional ``h``, ``n``, ``b``, and ``l`` specifiers are used to specify
host, network, big or little endian order addresses respectively. Where
no specifier is provided the default network/big endian order is used.
Passed by reference.
IPv6 addresses
--------------
::
%pI6 0001:0002:0003:0004:0005:0006:0007:0008
%pi6 00010002000300040005000600070008
%pI6c 1:2:3:4:5:6:7:8
For printing IPv6 network-order 16-bit hex addresses. The ``I6`` and ``i6``
specifiers result in a printed address with (I6) or without (i6)
colon-separators. Leading zeros are always used.
The additional ``c`` specifier can be used with the ``I`` specifier to
print a compressed IPv6 address as described by
https://tools.ietf.org/html/rfc5952
Passed by reference.
IPv4/IPv6 addresses (generic, with port, flowinfo, scope)
---------------------------------------------------------
::
%pIS 1.2.3.4 or 0001:0002:0003:0004:0005:0006:0007:0008
%piS 001.002.003.004 or 00010002000300040005000600070008
%pISc 1.2.3.4 or 1:2:3:4:5:6:7:8
%pISpc 1.2.3.4:12345 or [1:2:3:4:5:6:7:8]:12345
%p[Ii]S[pfschnbl]
For printing an IP address without the need to distinguish whether it's of
type AF_INET or AF_INET6. A pointer to a valid struct sockaddr,
specified through ``IS`` or ``iS``, can be passed to this format specifier.
The additional ``p``, ``f``, and ``s`` specifiers are used to specify port
(IPv4, IPv6), flowinfo (IPv6) and scope (IPv6). Ports have a ``:`` prefix,
flowinfo a ``/`` and scope a ``%``, each followed by the actual value.
In case of an IPv6 address the compressed IPv6 address as described by
https://tools.ietf.org/html/rfc5952 is being used if the additional
specifier ``c`` is given. The IPv6 address is surrounded by ``[``, ``]`` in
case of additional specifiers ``p``, ``f`` or ``s`` as suggested by
https://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-07
In case of IPv4 addresses, the additional ``h``, ``n``, ``b``, and ``l``
specifiers can be used as well and are ignored in case of an IPv6
address.
Passed by reference.
Further examples::
%pISfc 1.2.3.4 or [1:2:3:4:5:6:7:8]/123456789
%pISsc 1.2.3.4 or [1:2:3:4:5:6:7:8]%1234567890
%pISpfc 1.2.3.4:12345 or [1:2:3:4:5:6:7:8]:12345/123456789
UUID/GUID addresses
-------------------
::
%pUb 00010203-0405-0607-0809-0a0b0c0d0e0f
%pUB 00010203-0405-0607-0809-0A0B0C0D0E0F
%pUl 03020100-0504-0706-0809-0a0b0c0e0e0f
%pUL 03020100-0504-0706-0809-0A0B0C0E0E0F
For printing 16-byte UUID/GUIDs addresses. The additional ``l``, ``L``,
``b`` and ``B`` specifiers are used to specify a little endian order in
lower (l) or upper case (L) hex notation - and big endian order in lower (b)
or upper case (B) hex notation.
Where no additional specifiers are used the default big endian
order with lower case hex notation will be printed.
Passed by reference.
dentry names
------------
::
%pd{,2,3,4}
%pD{,2,3,4}
For printing dentry name; if we race with :c:func:`d_move`, the name might
be a mix of old and new ones, but it won't oops. %pd dentry is a safer
equivalent of %s dentry->d_name.name we used to use, %pd<n> prints ``n``
last components. %pD does the same thing for struct file.
Passed by reference.
block_device names
------------------
::
%pg sda, sda1 or loop0p1
For printing name of block_device pointers.
struct va_format
----------------
::
%pV
For printing struct va_format structures. These contain a format string
and va_list as follows::
struct va_format {
const char *fmt;
va_list *va;
};
Implements a "recursive vsnprintf".
Do not use this feature without some mechanism to verify the
correctness of the format string and va_list arguments.
Passed by reference.
Device tree nodes
-----------------
::
%pOF[fnpPcCF]
For printing device tree node structures. Default behaviour is
equivalent to %pOFf.
- f - device node full_name
- n - device node name
- p - device node phandle
- P - device node path spec (name + @unit)
- F - device node flags
- c - major compatible string
- C - full compatible string
The separator when using multiple arguments is ':'
Examples::
%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
D - dynamic
d - detached
P - Populated
B - Populated bus
Passed by reference.
Fwnode handles
--------------
::
%pfw[fP]
For printing information on an fwnode_handle. The default is to print the full
node name, including the path. The modifiers are functionally equivalent to
%pOF above.
- f - full name of the node, including the path
- P - the name of the node including an address (if there is one)
Examples (ACPI)::
%pfwf \[email protected]@0 - Full node name
%pfwP endpoint@0 - Node name
Examples (OF)::
%pfwf /ocp@68000000/i2c@48072000/camera@10/port/endpoint - Full name
%pfwP endpoint - Node name
Time and date
-------------
::
%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]
For printing date and time as represented by::
R struct rtc_time structure
T time64_t type
in human readable format.
By default year will be incremented by 1900 and month by 1.
Use %pt[RT]r (raw) to suppress this behaviour.
The %pt[RT]s (space) will override ISO 8601 separator by using ' ' (space)
instead of 'T' (Capital T) between date and time. It won't have any effect
when date or time is omitted.
Passed by reference.
struct clk
----------
::
%pC pll1
For printing struct clk structures. %pC prints the name of the clock
(Common Clock Framework) or a unique 32-bit ID (legacy clock framework).
Passed by reference.
bitmap and its derivatives such as cpumask and nodemask
-------------------------------------------------------
::
%*pb 0779
%*pbl 0,3-6,8-10
For printing bitmap and its derivatives such as cpumask and nodemask,
%*pb outputs the bitmap with field width as the number of bits and %*pbl
output the bitmap as range list with field width as the number of bits.
The field width is passed by value, the bitmap is passed by reference.
Helper macros cpumask_pr_args() and nodemask_pr_args() are available to ease
printing cpumask and nodemask.
Flags bitfields such as page flags and gfp_flags
--------------------------------------------------------
::
%pGp 0x17ffffc0002036(referenced|uptodate|lru|active|private|node=0|zone=2|lastcpupid=0x1fffff)
%pGg GFP_USER|GFP_DMA32|GFP_NOWARN
%pGv read|exec|mayread|maywrite|mayexec|denywrite
For printing flags bitfields as a collection of symbolic constants that
would construct the value. The type of flags is given by the third
character. Currently supported are:
- p - [p]age flags, expects value of type (``unsigned long *``)
- v - [v]ma_flags, expects value of type (``unsigned long *``)
- g - [g]fp_flags, expects value of type (``gfp_t *``)
The flag names and print order depends on the particular type.
Note that this format should not be used directly in the
:c:func:`TP_printk()` part of a tracepoint. Instead, use the show_*_flags()
functions from <trace/events/mmflags.h>.
Passed by reference.
Network device features
-----------------------
::
%pNF 0x000000000000c000
For printing netdev_features_t.
Passed by reference.
V4L2 and DRM FourCC code (pixel format)
---------------------------------------
::
%p4cc
Print a FourCC code used by V4L2 or DRM, including format endianness and
its numerical value as hexadecimal.
Passed by reference.
Examples::
%p4cc BG12 little-endian (0x32314742)
%p4cc Y10 little-endian (0x20303159)
%p4cc NV12 big-endian (0xb231564e)
Generic FourCC code
-------------------
::
%p4c[h[R]lb] gP00 (0x67503030)
Print a generic FourCC code, as both ASCII characters and its numerical
value as hexadecimal.
The generic FourCC code is always printed in the big-endian format,
the most significant byte first. This is the opposite of V4L/DRM FourCCs.
The additional ``h``, ``hR``, ``l``, and ``b`` specifiers define what
endianness is used to load the stored bytes. The data might be interpreted
using the host, reversed host byte order, little-endian, or big-endian.
Passed by reference.
Examples for a little-endian machine, given &(u32)0x67503030::
%p4ch gP00 (0x67503030)
%p4chR 00Pg (0x30305067)
%p4cl gP00 (0x67503030)
%p4cb 00Pg (0x30305067)
Examples for a big-endian machine, given &(u32)0x67503030::
%p4ch gP00 (0x67503030)
%p4chR 00Pg (0x30305067)
%p4cl 00Pg (0x30305067)
%p4cb gP00 (0x67503030)
Rust
----
::
%pA
Only intended to be used from Rust code to format ``core::fmt::Arguments``.
Do *not* use it from C.
Thanks
======
If you add other %p extensions, please extend <lib/tests/printf_kunit.c>
with one or more test cases, if at all feasible.
Thank you for your cooperation and attention.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
printk 형식 지정자를 올바르게 사용하는 방법
1-10printk 형식 지정자를 올바르게 사용하는 방법
문서 내부 참조 이름은 `printk-specifiers`입니다.
저자: Randy Dunlap <[email protected]>
저자: Andrew Murray <[email protected]>
정수 형식
11-55정수 형식 (Integer types)
| 변수 형식 | printk 형식 지정자 |
|---|---|
| signed char | %d 또는 %hhx |
| unsigned char | %u 또는 %x |
| char | %u 또는 %x |
| short int | %d 또는 %hx |
| unsigned short int | %u 또는 %x |
| int | %d 또는 %x |
| unsigned int | %u 또는 %x |
| long | %ld 또는 %lx |
| unsigned long | %lu 또는 %lx |
| long long | %lld 또는 %llx |
| unsigned long long | %llu 또는 %llx |
| size_t | %zu 또는 %zx |
| ssize_t | %zd 또는 %zx |
| s8 | %d 또는 %hhx |
| u8 | %u 또는 %x |
| s16 | %d 또는 %hx |
| u16 | %u 또는 %x |
| s32 | %d 또는 %x |
| u32 | %u 또는 %x |
| s64 | %lld 또는 %llx |
| u64 | %llu 또는 %llx |
크기가 아키텍처에 의존하는 형식, 예를 들어 `cycles_t`와 `tcflag_t`, 또는 `blk_status_t`처럼 구성 옵션에 따라 크기가 달라지는 형식은 가능한 가장 큰 형식의 지정자를 사용하고 그 형식으로 명시적으로 cast해야 합니다.
예:
printk("test: latency: %llu cycles\n", (unsigned long long)time);
`sizeof()`는 `size_t` 형식을 반환한다는 점을 기억하십시오.
커널의 printf는 `%n`을 지원하지 않습니다. 명백한 이유로 부동소수점 형식 `%e`, `%f`, `%g`, `%a`도 인식하지 않습니다. 지원하지 않는 지정자나 길이 한정자를 사용하면 WARN이 발생하고 `vsnprintf()`가 일찍 반환합니다.
포인터 형식과 접근 오류
56-70포인터 형식 (Pointer types)
원시 포인터 값은 `%p`로 출력할 수 있으며, 주소는 출력 전에 hash됩니다. 커널은 서로 다른 포인터 형식을 출력하기 위한 확장 지정자도 지원합니다.
일부 확장 지정자는 주소 자체가 아니라 해당 주소의 데이터를 출력합니다. 이때 정보에 접근할 수 없으면 다음 오류 메시지가 대신 출력될 수 있습니다.
(null) data on plain NULL address
(efault) data on invalid address
(einval) invalid data on a valid address
일반 포인터
71-95일반 포인터 (Plain Pointers)
%p abcdef12 or 00000000abcdef12
확장 없는 `%p`로 출력하는 포인터는 커널 메모리 배치 정보가 노출되지 않도록 hash됩니다. 동시에 고유 식별자로 쓸 수 있다는 장점도 있습니다. 64비트 시스템에서는 첫 32비트를 0으로 만듭니다. 커널은 충분한 entropy를 모을 때까지 `(ptrval)`을 출력합니다.
가능하면 나중에 해석해야 하는 원시 주소를 노출하지 않도록 아래에 설명한 `%pS` 또는 `%pB` 같은 전용 modifier를 사용하십시오. 불가능하고 디버깅 정보를 늘리는 것이 목적이라면 `%p`를 사용하고 디버깅 중 `no_hash_pointers` 커널 매개변수로 부팅하여 모든 `%p` 주소를 수정 없이 출력할 수 있습니다. 언제나 수정되지 않은 주소가 정말 필요하다면 아래의 `%px`를 참고하십시오.
주소를 procfs 또는 sysfs 같은 가상 파일의 내용으로 출력하여 사용자 공간 프로세스가 읽게 하는 경우에만, 예를 들어 `printk()`가 아니라 `seq_printf()`를 사용할 때는 `%p`나 `%px` 대신 아래의 `%pK` modifier를 사용하십시오.
오류 포인터
96-107오류 포인터 (Error Pointers)
%pe -ENOSPC
`IS_ERR()`가 참인 오류 포인터를 기호 오류 이름으로 출력할 때 `%pe`를 사용합니다. 알려진 기호 이름이 없는 오류 값은 10진수로 출력하며, `ERR_PTR`이 아닌 값을 `%pe` 인자로 전달하면 일반 `%p`처럼 처리합니다.
심볼과 함수 포인터
108-140심볼/함수 포인터 (Symbols/Function Pointers)
%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
`S`와 `s` 지정자는 포인터를 기호 형식으로 출력합니다. `S`는 offset을 포함한 symbol 이름을, `s`는 offset 없는 이름을 출력합니다. `KALLSYMS`가 비활성화되어 있으면 symbol 주소를 대신 출력합니다.
`B` 지정자는 offset을 포함한 symbol 이름을 출력하며 stack backtrace에 사용해야 합니다. Tail call을 사용하고 `noreturn` GCC attribute로 표시했을 때 생길 수 있는 compiler optimization 효과도 고려합니다.
포인터가 모듈 안에 있으면 지정자 끝에 `b`를 추가하여 symbol 이름 뒤에 모듈 이름과 선택적으로 build 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]
BPF와 tracing에서 조사한 포인터
141-154BPF/tracing에서 사전에 조사한 포인터
%pks kernel string
%pus user string
`k`와 `u` 지정자는 각각 커널 메모리와 사용자 메모리에서 미리 조사한 메모리를 출력합니다. 뒤따르는 `s`는 문자열 출력을 뜻합니다. 일반 `vsnprintf()`에서 직접 사용할 때 `(k)`와 `(u)` 표시는 무시되지만, 예를 들어 BPF의 `bpf_trace_printk()`에서 사용하면 fault 없이 포인터가 가리키는 메모리를 읽습니다.
커널 포인터
155-170커널 포인터 (Kernel Pointers)
%pK 01234567 or 0123456789abcdef
비특권 사용자에게 숨겨야 하는 커널 포인터를 출력할 때 `%pK`를 사용합니다. `%pK`의 동작은 `kptr_restrict` sysctl에 따라 달라지며 자세한 내용은 `Documentation/admin-guide/sysctl/kernel.rst`를 참조하십시오.
이 modifier는 procfs나 sysfs처럼 사용자 공간이 읽는 파일의 내용을 만들 때만 사용하며 dmesg용이 아닙니다. `printk()` 포인터 hash 관리 방법은 앞의 `%p` 절을 참고하십시오.
수정되지 않은 주소
171-191수정되지 않은 주소 (Unmodified Addresses)
%px 01234567 or 0123456789abcdef
주소 자체를 정말 출력해야 할 때 `%px`를 사용합니다. 사용하기 전에 커널 메모리 배치에 관한 민감한 정보가 노출되지 않는지 검토해야 합니다. `%px`는 기능상 `%lx` 또는 `%lu`와 같지만 검색으로 호출 지점을 찾기 쉽기 때문에 선호됩니다.
`%px`를 사용하기 전에 디버깅 세션에서 `no_hash_pointers`를 활성화한 `%p`로 충분한지 고려하십시오. `%px`가 타당한 예는 panic 직전에 정보를 출력하는 경우입니다. 이미 panic이 발생하므로 민감한 정보를 악용하기 어렵고, `no_hash_pointers`를 켜서 panic을 재현할 필요도 없습니다.
포인터 차이
192-205포인터 차이 (Pointer Differences)
%td 2560
%tx a00
포인터 차이를 출력할 때는 `ptrdiff_t`용 `%t` modifier를 사용합니다.
예:
printk("test: difference between pointers: %td\n", ptr2 - ptr1);
struct resource
206-225`struct resource` 출력
%pr [mem 0x60000000-0x6fffffff flags 0x2200] or
[mem 0x60000000 flags 0x2200] or
[mem 0x0000000060000000-0x000000006fffffff flags 0x2200]
[mem 0x0000000060000000 flags 0x2200]
%pR [mem 0x60000000-0x6fffffff pref] or
[mem 0x60000000 pref] or
[mem 0x0000000060000000-0x000000006fffffff pref]
[mem 0x0000000060000000 pref]
`R`과 `r` 지정자는 각각 flags 멤버를 해석한 형태와 해석하지 않은 형태로 resource를 출력합니다. `start`와 `end`가 같으면 시작 값만 출력합니다.
인자는 참조로 전달합니다.
물리 주소 형식
226-238물리 주소 형식 `phys_addr_t`
%pa[p] 0x01234567 or 0x0123456789abcdef
CPU data path 폭과 관계없이 빌드 옵션에 따라 크기가 달라질 수 있는 `phys_addr_t` 및 `resource_size_t` 같은 파생 형식을 출력합니다.
인자는 참조로 전달합니다.
struct range
239-251`struct range`
%pra [range 0x0000000060000000-0x000000006fffffff] or
[range 0x0000000060000000]
`struct range`는 임의의 `u64` 값 범위를 담습니다. `start`와 `end`가 같으면 시작 값만 출력합니다.
인자는 참조로 전달합니다.
DMA 주소 형식
252-263DMA 주소 형식 `dma_addr_t`
%pad 0x01234567 or 0x0123456789abcdef
CPU data path 폭과 관계없이 빌드 옵션에 따라 크기가 달라질 수 있는 `dma_addr_t` 형식을 출력합니다.
인자는 참조로 전달합니다.
Escape된 문자열로 출력하는 raw buffer
264-300Raw buffer를 escape된 문자열로 출력
%*pE[achnops]
다음 buffer를 출력한다고 가정합니다.
1b 62 20 5c 43 07 22 90 0d 5d
주변 따옴표를 제외한 변환 예는 다음과 같습니다.
%*pE "\eb \C\a"\220\r]"
%*pEhp "\x1bb \C\x07"\x90\x0d]"
%*pEa "\e\142\040\\\103\a\042\220\r\135"
변환 규칙은 선택적인 플래그 조합에 따라 적용됩니다. 자세한 내용은 `string_escape_mem` 커널 문서를 참조하십시오.
- `a`: `ESCAPE_ANY`
- `c`: `ESCAPE_SPECIAL`
- `h`: `ESCAPE_HEX`
- `n`: `ESCAPE_NULL`
- `o`: `ESCAPE_OCTAL`
- `p`: `ESCAPE_NP`
- `s`: `ESCAPE_SPACE`
기본값은 `ESCAPE_ANY_NP`입니다. 이는 특히 SSID 출력 등 많은 경우에 합리적인 선택입니다. Field width를 생략하면 1바이트만 escape됩니다.
16진 문자열로 출력하는 raw buffer
301-314Raw buffer를 16진 문자열로 출력
%*ph 00 01 02 ... 3f
%*phC 00:01:02: ... :3f
%*phD 00-01-02- ... -3f
%*phN 000102 ... 3f
최대 64바이트의 작은 buffer를 지정한 separator가 있는 16진 문자열로 출력합니다. 더 큰 buffer에는 `print_hex_dump`를 사용하는 편을 고려하십시오.
MAC/FDDI 주소
315-339MAC/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 주소를 16진 표기로 출력합니다. `M`은 byte separator를 포함하고 `m`은 포함하지 않습니다. 기본 separator는 colon(`:`)입니다.
FDDI 주소에는 `M` 뒤의 `F` 지정자로 기본 separator 대신 dash(`-`)를 사용할 수 있습니다.
Bluetooth 주소에는 `M` 뒤의 `R` 지정자를 사용하여 little-endian 주소를 사람이 보기 좋은 역순 byte order로 출력합니다.
인자는 참조로 전달합니다.
IPv4 주소
340-358IPv4 주소
%pI4 1.2.3.4
%pi4 001.002.003.004
%p[Ii]4[hnbl]
IPv4 주소를 점으로 구분한 10진수로 출력합니다. `I4`는 leading zero 없이, `i4`는 leading zero를 포함해 출력합니다.
추가 지정자 `h`, `n`, `b`, `l`은 각각 host, network, big-endian, little-endian 주소 순서를 뜻합니다. 지정자가 없으면 network/big-endian 순서가 기본입니다.
인자는 참조로 전달합니다.
IPv6 주소
359-377IPv6 주소
%pI6 0001:0002:0003:0004:0005:0006:0007:0008
%pi6 00010002000300040005000600070008
%pI6c 1:2:3:4:5:6:7:8
Network order의 16비트 16진 IPv6 주소를 출력합니다. `I6`은 colon separator를 포함하고 `i6`은 포함하지 않으며 leading zero는 항상 사용합니다.
`I` 뒤에 `c`를 추가하면 RFC 5952에서 설명하는 압축 IPv6 주소를 출력합니다.
인자는 참조로 전달합니다.
일반 IPv4/IPv6 주소
378-414IPv4/IPv6 주소: 일반 형식과 port, flowinfo, scope
%pIS 1.2.3.4 or 0001:0002:0003:0004:0005:0006:0007:0008
%piS 001.002.003.004 or 00010002000300040005000600070008
%pISc 1.2.3.4 or 1:2:3:4:5:6:7:8
%pISpc 1.2.3.4:12345 or [1:2:3:4:5:6:7:8]:12345
%p[Ii]S[pfschnbl]
주소가 `AF_INET`인지 `AF_INET6`인지 구별하지 않고 IP 주소를 출력합니다. `IS` 또는 `iS`로 유효한 `struct sockaddr` 포인터를 전달할 수 있습니다.
추가 지정자 `p`, `f`, `s`는 각각 port(IPv4와 IPv6), flowinfo(IPv6), scope(IPv6)를 지정합니다. Port에는 `:`, flowinfo에는 `/`, scope에는 `%` 접두사가 붙고 그 뒤에 실제 값이 옵니다.
IPv6 주소에 `c`를 추가하면 RFC 5952 압축 형식을 사용합니다. `p`, `f`, `s` 중 하나가 추가되면 관련 주소 표현 초안의 권고대로 IPv6 주소를 `[`와 `]`로 감쌉니다.
- RFC 5952
https://tools.ietf.org/html/rfc5952 - IPv6 text representation draft
https://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-07
IPv4 주소에는 `h`, `n`, `b`, `l`도 사용할 수 있으며 IPv6 주소에서는 무시됩니다. 인자는 참조로 전달합니다.
추가 예:
%pISfc 1.2.3.4 or [1:2:3:4:5:6:7:8]/123456789
%pISsc 1.2.3.4 or [1:2:3:4:5:6:7:8]%1234567890
%pISpfc 1.2.3.4:12345 or [1:2:3:4:5:6:7:8]:12345/123456789
UUID/GUID 주소
415-434UUID/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/GUID를 출력합니다. `l`과 `L`은 little-endian 순서의 소문자 및 대문자 16진 표기, `b`와 `B`는 big-endian 순서의 소문자 및 대문자 16진 표기를 뜻합니다.
추가 지정자가 없으면 big-endian 순서와 소문자 16진 표기가 기본입니다. 인자는 참조로 전달합니다.
dentry 이름
435-449Dentry 이름
%pd{,2,3,4}
%pD{,2,3,4}
Dentry 이름을 출력합니다. `d_move`와 race가 발생하면 이전 이름과 새 이름이 섞일 수 있지만 oops는 발생하지 않습니다. `%pd`는 과거의 `%s dentry->d_name.name`보다 안전하며 `%pd<n>`은 마지막 `n`개 path component를 출력합니다. `%pD`는 `struct file`에 같은 동작을 적용합니다.
인자는 참조로 전달합니다.
block_device 이름
450-458`block_device` 이름
%pg sda, sda1 or loop0p1
`block_device` 포인터의 이름을 출력합니다.
struct va_format
459-480`struct va_format`
%pV
`struct va_format`은 다음과 같이 format string과 `va_list`를 담습니다.
struct va_format {
const char *fmt;
va_list *va;
};
이는 재귀적인 `vsnprintf`를 구현합니다. Format string과 `va_list` 인자의 정확성을 검증하는 장치 없이는 이 기능을 사용하지 마십시오. 인자는 참조로 전달합니다.
Device tree node
481-516Device tree node
%pOF[fnpPcCF]
Device tree node 구조체 정보를 출력합니다. 기본 동작은 `%pOFf`와 같습니다.
- `f`: device node `full_name`
- `n`: device node 이름
- `p`: device node phandle
- `P`: device node path 사양, 즉 이름과 `@unit`
- `F`: device node flags
- `c`: 주요 compatible string
- `C`: 전체 compatible string
여러 인자를 사용할 때 separator는 colon(`:`)입니다.
예:
%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
D - dynamic
d - detached
P - Populated
B - Populated bus
인자는 참조로 전달합니다.
Fwnode handle
517-540Fwnode handle
%pfw[fP]
`fwnode_handle` 정보를 출력합니다. 기본값은 path를 포함한 전체 node 이름이며 modifier는 앞의 `%pOF`와 기능상 같습니다.
- `f`: path를 포함한 node 전체 이름
- `P`: 주소가 있으면 이를 포함한 node 이름
ACPI 예:
%pfwf \[email protected]@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
시간과 날짜
541-567시간과 날짜
%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 구조체 |
| T | time64_t 형식 |
기본적으로 연도에는 1900을, 월에는 1을 더합니다. 이 동작을 억제하려면 raw 형식 `%pt[RT]r`을 사용하십시오.
`%pt[RT]s`의 `s`는 날짜와 시간 사이의 ISO 8601 separator `T` 대신 space를 사용합니다. 날짜 또는 시간이 생략된 경우에는 영향이 없습니다.
인자는 참조로 전달합니다.
struct clk
568-579`struct clk`
%pC pll1
`struct clk`를 출력합니다. `%pC`는 Common Clock Framework에서는 clock 이름을, legacy clock framework에서는 고유한 32비트 ID를 출력합니다. 인자는 참조로 전달합니다.
Bitmap과 cpumask/nodemask
580-595Bitmap 및 `cpumask`, `nodemask` 같은 파생 형식
%*pb 0779
%*pbl 0,3-6,8-10
`%*pb`는 field width를 bit 수로 사용하여 bitmap을 출력하고, `%*pbl`은 같은 field width를 사용해 bitmap을 범위 목록으로 출력합니다.
Field width는 값으로, bitmap은 참조로 전달합니다. `cpumask`와 `nodemask` 출력을 돕는 `cpumask_pr_args()` 및 `nodemask_pr_args()` helper macro도 제공됩니다.
Page flag와 gfp_flags bitfield
596-620Page flag와 `gfp_flags` 같은 flags bitfield
%pGp 0x17ffffc0002036(referenced|uptodate|lru|active|private|node=0|zone=2|lastcpupid=0x1fffff)
%pGg GFP_USER|GFP_DMA32|GFP_NOWARN
%pGv read|exec|mayread|maywrite|mayexec|denywrite
값을 구성하는 기호 상수의 집합으로 flags bitfield를 출력합니다. 세 번째 문자가 flags 형식을 지정합니다.
- `p`: page flags, `unsigned long *` 형식 값을 기대합니다.
- `v`: `vma_flags`, `unsigned long *` 형식 값을 기대합니다.
- `g`: `gfp_flags`, `gfp_t *` 형식 값을 기대합니다.
Flag 이름과 출력 순서는 구체적인 형식에 따라 달라집니다.
이 형식은 tracepoint의 `TP_printk()` 부분에서 직접 사용하면 안 됩니다. 대신 `<trace/events/mmflags.h>`의 `show_*_flags()` 함수를 사용하십시오. 인자는 참조로 전달합니다.
네트워크 장치 기능
621-631네트워크 장치 기능
%pNF 0x000000000000c000
`netdev_features_t`를 출력합니다. 인자는 참조로 전달합니다.
V4L2와 DRM FourCC
632-649V4L2 및 DRM FourCC code(pixel format)
%p4cc
V4L2 또는 DRM이 사용하는 FourCC code를 형식의 endianness와 16진 숫자값까지 포함하여 출력합니다. 인자는 참조로 전달합니다.
예:
%p4cc BG12 little-endian (0x32314742)
%p4cc Y10 little-endian (0x20303159)
%p4cc NV12 big-endian (0xb231564e)
일반 FourCC
650-681일반 FourCC code
%p4c[h[R]lb] gP00 (0x67503030)
일반 FourCC code를 ASCII 문자와 16진 숫자값으로 함께 출력합니다. 일반 FourCC는 항상 최상위 byte부터 시작하는 big-endian 형식으로 출력되며, 이는 V4L/DRM FourCC와 반대입니다.
추가 지정자 `h`, `hR`, `l`, `b`는 저장된 byte를 읽을 때 사용할 endianness를 정의합니다. 각각 host, reversed host byte order, little-endian, big-endian으로 해석할 수 있습니다. 인자는 참조로 전달합니다.
Little-endian 시스템에서 `&(u32)0x67503030`을 전달한 예:
%p4ch gP00 (0x67503030)
%p4chR 00Pg (0x30305067)
%p4cl gP00 (0x67503030)
%p4cb 00Pg (0x30305067)
Big-endian 시스템에서 같은 값을 전달한 예:
%p4ch gP00 (0x67503030)
%p4chR 00Pg (0x30305067)
%p4cl 00Pg (0x30305067)
%p4cb gP00 (0x67503030)
Rust 형식
682-691Rust
%pA
`%pA`는 Rust 코드에서 `core::fmt::Arguments`를 형식화하는 용도로만 사용합니다. C에서는 사용하지 마십시오.
마무리
692-698감사의 말 (Thanks)
다른 `%p` 확장을 추가한다면 가능한 경우 `<lib/tests/printf_kunit.c>`에도 하나 이상의 test case를 추가하십시오.
협조와 관심에 감사드립니다.
요약과 해설
printk-formats.rst:1-698커널의 `printk()` 형식은 C printf와 비슷하지만 `%n`과 부동소수점 변환을 지원하지 않으며, 커널 객체를 안전하고 의미 있게 출력하기 위한 다양한 `%p` 확장을 제공합니다.
일반 `%p`는 메모리 배치 노출을 막기 위해 주소를 hash합니다. 원시 주소가 꼭 필요할 때만 `%px`, 사용자 공간이 읽는 가상 파일에는 `%pK`, symbol에는 `%pS`·`%ps`·`%pB`처럼 목적에 맞는 전용 modifier를 사용해야 합니다.
문서는 resource, 물리·DMA 주소, escaped/hex buffer, MAC·IP·UUID, dentry, device tree, fwnode, 날짜와 시간, bitmap과 flags, FourCC 및 Rust 인자를 위한 형식을 예제와 함께 설명합니다. 참조 전달 여부와 endianness 규칙을 각 항목에서 확인해야 합니다.