[RFC PATCH bpf-next 0/6] bpf, printk: add BTF-based type printing

From: Alan Maguire
Date: Fri Apr 17 2020 - 06:45:20 EST


The printk family of functions support printing specific pointer types
using %p format specifiers (MAC addresses, IP addresses, etc). For
full details see Documentation/core-api/printk-formats.rst.

This RFC patchset proposes introducing a "print typed pointer" format
specifier "%pT<type>"; the type specified is then looked up in the BPF
Type Format (BTF) information provided for vmlinux to support display.

There is already support in kernel/bpf/btf.c for "show" functionality;
the changes here generalize that support from seq-file specific
verifier display to the more generic case and add another specific
use case; snprintf()-style rendering of type information to a
provided buffer. This support is then used to support printk
rendering of types, but the intent is to provide a function
that might be useful in other in-kernel scenarios; for example:

- ftrace could possibly utilize the function to support typed
display of function arguments by cross-referencing BTF function
information to derive the types of arguments
- oops/panic messaging could extend the information displayed to
dig into data structures associated with failing functions

The above potential use cases hint at a potential reply to
a reasonable objection that such typed display should be
solved by tracing programs, where the in kernel tracing records
data and the userspace program prints it out. While this
is certainly the recommended approach for most cases, I
believe having an in-kernel mechanism would be valuable
also.

The function the printk() family of functions rely on
could potentially be used directly for other use cases
like ftrace where we might have the BTF ids of the
pointers we wish to display; its signature is as follows:

int btf_type_snprintf_show(const struct btf *btf, u32 type_id, void *obj,
char *buf, int len, u64 flags);

So if ftrace say had the BTF ids of the types of arguments,
we see that the above would allow us to convert the
pointer data into displayable form.

To give a flavour for what the printed-out data looks like,
here we use pr_info() to display a struct sk_buff *. Note
we specify the 'N' modifier to show type field names:

struct sk_buff *skb = alloc_skb(64, GFP_KERNEL);

pr_info("%pTN<struct sk_buff>", skb);

...gives us:

{{{.next=00000000c7916e9c,.prev=00000000c7916e9c,{.dev=00000000c7916e9c|.dev_scratch=0}}|.rbnode={.__rb_parent_color=0,.rb_right=00000000c7916e9c,.rb_left=00000000c7916e9c}|.list={.next=00000000c7916e9c,.prev=00000000c7916e9c}},{.sk=00000000c7916e9c|.ip_defrag_offset=0},{.tstamp=0|.skb_mstamp_ns=0},.cb=['\0'],{{._skb_refdst=0,.destructor=00000000c7916e9c}|.tcp_tsorted_anchor={.next=00000000c7916e9c,.prev=00000000c7916e9c}},._nfct=0,.len=0,.data_len=0,.mac_len=0,.hdr_len=0,.queue_mapping=0,.__cloned_offset=[],.cloned=0x0,.nohdr=0x0,.fclone=0x0,.peeked=0x0,.head_frag=0x0,.pfmemalloc=0x0,.active_extensions=0,.headers_start=[],.__pkt_type_offset=[],.pkt_type=0x0,.ignore_df=0x0,.nf_trace=0x0,.ip_summed=0x0,.ooo_okay=0x0,.l4_hash=0x0,.sw_hash=0x0,.wifi_acked_valid=0x0,.wifi_acked=0x0,.no_fcs=0x0,.encapsulation=0x0,.encap_hdr_csum=0x0,.csum_valid=0x0,.__pkt_vlan_present_offset=[],.vlan_present=0x0,.csum_complete_sw=0x0,.csum_level=0x0,.csum_not_inet=0x0,.dst_pending_co

printk output is truncated at 1024 bytes. For such cases, the compact
display mode (minus the field info 'N') may be used at the expense of
readability. "|" differentiates between different union members, but
aside from that the format is intended to minic a valid C initiailizer
for the given type.

The hope is that this functionality will be useful for debugging,
and possibly help facilitate the cases mentioned above in the future.

The patches are marked RFC for several reasons

- There's already an RFC patchset in flight dealing with BTF dumping;

https://www.spinics.net/lists/netdev/msg644412.html

The reason I'm posting this is the approach is a bit different
and there may be ways of synthesizing the approaches.
- The mechanism of vmlinux BTF initialization is not fit for purpose
in a printk() setting as I understand it (it uses mutex locking
to prevent multiple initializations of the BTF info). A simple
approach to support printk might be to simply initialize the
BTF vmlinux case early in boot; it only needs to happen once.
Any suggestions here would be great.
- BTF-based rendering is more complex than other printk() format
specifier-driven methods; that said, because of its generality it
does provide significant value I think
- More tests are needed.

Alan Maguire (6):
bpf: provide function to get vmlinux BTF information
bpf: btf->resolved_[ids,sizes] should not be used for vmlinux BTF
bpf: move to generic BTF show support, apply it to seq files/strings
checkpatch: add new BTF pointer format specifier
printk: add type-printing %pT<type> format specifier which uses BTF
printk: extend test_printf to test %pT BTF-based format specifier

Documentation/core-api/printk-formats.rst | 8 +
include/linux/bpf.h | 2 +
include/linux/btf.h | 35 ++-
kernel/bpf/btf.c | 466 ++++++++++++++++++++++++------
kernel/bpf/verifier.c | 18 +-
lib/Kconfig | 16 +
lib/test_printf.c | 118 ++++++++
lib/vsprintf.c | 145 +++++++++-
scripts/checkpatch.pl | 2 +-
9 files changed, 704 insertions(+), 106 deletions(-)

--
1.8.3.1