bpf: emit source code file name and line number in verifier log
authorAndrii Nakryiko <andrii@kernel.org>
Mon, 12 Feb 2024 23:59:44 +0000 (15:59 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 14 Feb 2024 02:51:32 +0000 (18:51 -0800)
As BPF applications grow in size and complexity and are separated into
multiple .bpf.c files that are statically linked together, it becomes
harder and harder to match verifier's BPF assembly level output to
original C code. While often annotated C source code is unique enough to
be able to identify the file it belongs to, quite often this is actually
problematic as parts of source code can be quite generic.

Long story short, it is very useful to see source code file name and
line number information along with the original C code. Verifier already
knows this information, we just need to output it.

This patch extends verifier log with file name and line number
information, emitted next to original (presumably C) source code,
annotating BPF assembly output, like so:

  ; <original C code> @ <filename>.bpf.c:<line>

If file name has directory names in it, they are stripped away. This
should be fine in practice as file names tend to be pretty unique with
C code anyways, and keeping log size smaller is always good.

In practice this might look something like below, where some code is
coming from application files, while others are from libbpf's usdt.bpf.h
header file:

  ; if (STROBEMETA_READ( @ strobemeta_probe.bpf.c:534
  5592: (79) r1 = *(u64 *)(r10 -56)     ; R1_w=mem_or_null(id=1589,sz=7680) R10=fp0
  5593: (7b) *(u64 *)(r10 -56) = r1     ; R1_w=mem_or_null(id=1589,sz=7680) R10=fp0
  5594: (79) r3 = *(u64 *)(r10 -8)      ; R3_w=scalar() R10=fp0 fp-8=mmmmmmmm

  ...

  170: (71) r1 = *(u8 *)(r8 +15)        ; frame1: R1_w=scalar(...) R8_w=map_value(map=__bpf_usdt_spec,ks=4,vs=208)
  171: (67) r1 <<= 56                   ; frame1: R1_w=scalar(...)
  172: (c7) r1 s>>= 56                  ; frame1: R1_w=scalar(smin=smin32=-128,smax=smax32=127)
  ; val <<= arg_spec->arg_bitshift; @ usdt.bpf.h:183
  173: (67) r1 <<= 32                   ; frame1: R1_w=scalar(...)
  174: (77) r1 >>= 32                   ; frame1: R1_w=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff))
  175: (79) r2 = *(u64 *)(r10 -8)       ; frame1: R2_w=scalar() R10=fp0 fp-8=mmmmmmmm
  176: (6f) r2 <<= r1                   ; frame1: R1_w=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff)) R2_w=scalar()
  177: (7b) *(u64 *)(r10 -8) = r2       ; frame1: R2_w=scalar(id=61) R10=fp0 fp-8_w=scalar(id=61)
  ; if (arg_spec->arg_signed) @ usdt.bpf.h:184
  178: (bf) r3 = r2                     ; frame1: R2_w=scalar(id=61) R3_w=scalar(id=61)
  179: (7f) r3 >>= r1                   ; frame1: R1_w=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff)) R3_w=scalar()
  ; if (arg_spec->arg_signed) @ usdt.bpf.h:184
  180: (71) r4 = *(u8 *)(r8 +14)
  181: safe

log_fixup tests needed a minor adjustment as verifier log output
increased a bit and that test is quite sensitive to such changes.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20240212235944.2816107-1-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
kernel/bpf/log.c
tools/testing/selftests/bpf/prog_tests/log_fixup.c

index 594a234..cc789ef 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/bpf.h>
 #include <linux/bpf_verifier.h>
 #include <linux/math64.h>
+#include <linux/string.h>
 
 #define verbose(env, fmt, args...) bpf_verifier_log_write(env, fmt, ##args)
 
@@ -362,6 +363,8 @@ __printf(3, 4) void verbose_linfo(struct bpf_verifier_env *env,
                                  const char *prefix_fmt, ...)
 {
        const struct bpf_line_info *linfo;
+       const struct btf *btf;
+       const char *s, *fname;
 
        if (!bpf_verifier_log_needed(&env->log))
                return;
@@ -378,9 +381,15 @@ __printf(3, 4) void verbose_linfo(struct bpf_verifier_env *env,
                va_end(args);
        }
 
-       verbose(env, "%s\n",
-               ltrim(btf_name_by_offset(env->prog->aux->btf,
-                                        linfo->line_off)));
+       btf = env->prog->aux->btf;
+       s = ltrim(btf_name_by_offset(btf, linfo->line_off));
+       verbose(env, "%s", s); /* source code line */
+
+       s = btf_name_by_offset(btf, linfo->file_name_off);
+       /* leave only file name */
+       fname = strrchr(s, '/');
+       fname = fname ? fname + 1 : s;
+       verbose(env, " @ %s:%u\n", fname, BPF_LINE_INFO_LINE_NUM(linfo->line_col));
 
        env->prev_linfo = linfo;
 }
index 7a3fa2f..90a98e2 100644 (file)
@@ -169,9 +169,9 @@ void test_log_fixup(void)
        if (test__start_subtest("bad_core_relo_trunc_none"))
                bad_core_relo(0, TRUNC_NONE /* full buf */);
        if (test__start_subtest("bad_core_relo_trunc_partial"))
-               bad_core_relo(280, TRUNC_PARTIAL /* truncate original log a bit */);
+               bad_core_relo(300, TRUNC_PARTIAL /* truncate original log a bit */);
        if (test__start_subtest("bad_core_relo_trunc_full"))
-               bad_core_relo(220, TRUNC_FULL  /* truncate also libbpf's message patch */);
+               bad_core_relo(240, TRUNC_FULL  /* truncate also libbpf's message patch */);
        if (test__start_subtest("bad_core_relo_subprog"))
                bad_core_relo_subprog();
        if (test__start_subtest("missing_map"))