On 4/4/22 7:41 PM, Andrii Nakryiko wrote: > Add BPF-side implementation of libbpf-provided USDT support. This > consists of single header library, usdt.bpf.h, which is meant to be used > from user's BPF-side source code. This header is added to the list of > installed libbpf header, along bpf_helpers.h and others. > > BPF-side implementation consists of two BPF maps: > - spec map, which contains "a USDT spec" which encodes information > necessary to be able to fetch USDT arguments and other information > (argument count, user-provided cookie value, etc) at runtime; > - IP-to-spec-ID map, which is only used on kernels that don't support > BPF cookie feature. It allows to lookup spec ID based on the place > in user application that triggers USDT program. > > These maps have default sizes, 256 and 1024, which are chosen > conservatively to not waste a lot of space, but handling a lot of common > cases. But there could be cases when user application needs to either > trace a lot of different USDTs, or USDTs are heavily inlined and their > arguments are located in a lot of differing locations. For such cases it > might be necessary to size those maps up, which libbpf allows to do by > overriding BPF_USDT_MAX_SPEC_CNT and BPF_USDT_MAX_IP_CNT macros. > > It is an important aspect to keep in mind. Single USDT (user-space > equivalent of kernel tracepoint) can have multiple USDT "call sites". > That is, single logical USDT is triggered from multiple places in user > application. This can happen due to function inlining. Each such inlined > instance of USDT invocation can have its own unique USDT argument > specification (instructions about the location of the value of each of > USDT arguments). So while USDT looks very similar to usual uprobe or > kernel tracepoint, under the hood it's actually a collection of uprobes, > each potentially needing different spec to know how to fetch arguments. > > User-visible API consists of three helper functions: > - bpf_usdt_arg_cnt(), which returns number of arguments of current USDT; > - bpf_usdt_arg(), which reads value of specified USDT argument (by > it's zero-indexed position) and returns it as 64-bit value; > - bpf_usdt_cookie(), which functions like BPF cookie for USDT > programs; this is necessary as libbpf doesn't allow specifying actual > BPF cookie and utilizes it internally for USDT support implementation. > > Each bpf_usdt_xxx() APIs expect struct pt_regs * context, passed into > BPF program. On kernels that don't support BPF cookie it is used to > fetch absolute IP address of the underlying uprobe. > > usdt.bpf.h also provides BPF_USDT() macro, which functions like > BPF_PROG() and BPF_KPROBE() and allows much more user-friendly way to > get access to USDT arguments, if USDT definition is static and known to > the user. It is expected that majority of use cases won't have to use > bpf_usdt_arg_cnt() and bpf_usdt_arg() directly and BPF_USDT() will cover > all their needs. > > Last, usdt.bpf.h is utilizing BPF CO-RE for one single purpose: to > detect kernel support for BPF cookie. If BPF CO-RE dependency is > undesirable, user application can redefine BPF_USDT_HAS_BPF_COOKIE to > either a boolean constant (or equivalently zero and non-zero), or even > point it to its own .rodata variable that can be specified from user's > application user-space code. It is important that > BPF_USDT_HAS_BPF_COOKIE is known to BPF verifier as static value (thus > .rodata and not just .data), as otherwise BPF code will still contain > bpf_get_attach_cookie() BPF helper call and will fail validation at > runtime, if not dead-code eliminated. > > Reviewed-by: Alan Maguire <alan.maguire@xxxxxxxxxx> > Signed-off-by: Andrii Nakryiko <andrii@xxxxxxxxxx> > --- Reviewed-by: Dave Marchevsky <davemarchevsky@xxxxxx>