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 594a234f122bbb5ed569445cd8dbb9bc75a0b3ab..cc789efc7f43c4e273e3909cfb71b9e9bc555e93 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 7a3fa2ff567b1ce1487f7e964cf49d523c6522bd..90a98e23be619da6aae628ae53307e938f840227 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"))