package internal import ( "errors" "fmt" "os" "syscall" "testing" "github.com/cilium/ebpf/internal/unix" qt "github.com/frankban/quicktest" ) func TestVerifierErrorWhitespace(t *testing.T) { b := []byte("unreachable insn 28") b = append(b, 0xa, // \n 0xd, // \r 0x9, // \t 0x20, // space 0, 0, // trailing NUL bytes ) err := ErrorWithLog(errors.New("test"), b) want := "test: unreachable insn 28" got := err.Error() t.Log(got) if want != got { t.Fatalf("\nwant: %s\ngot: %s", want, got) } } func TestVerifierError(t *testing.T) { for _, test := range []struct { name string log string }{ {"missing null", "foo"}, {"missing newline before null", "foo\x00"}, } { t.Run("truncate "+test.name, func(t *testing.T) { ve := ErrorWithLog(syscall.ENOENT, []byte(test.log)) qt.Assert(t, ve, qt.IsNotNil, qt.Commentf("should return error")) qt.Assert(t, ve.Truncated, qt.IsTrue, qt.Commentf("should be truncated")) }) } ve := ErrorWithLog(syscall.ENOENT, nil) qt.Assert(t, ve, qt.IsNotNil, qt.Commentf("should return error without log or logErr")) errno524 := readErrorFromFile(t, "testdata/errno524.log") t.Log(errno524) qt.Assert(t, errno524.Error(), qt.Contains, "JIT doesn't support bpf-to-bpf calls") qt.Assert(t, errno524.Error(), qt.Not(qt.Contains), "processed 39 insns") invalidMember := readErrorFromFile(t, "testdata/invalid-member.log") t.Log(invalidMember) qt.Assert(t, invalidMember.Error(), qt.Contains, "STRUCT task_struct size=7744 vlen=218: cpus_mask type_id=109 bitfield_size=0 bits_offset=7744 Invalid member") issue43 := readErrorFromFile(t, "testdata/issue-43.log") t.Log(issue43) qt.Assert(t, issue43.Error(), qt.Contains, "[11] FUNC helper_func2 type_id=10 vlen != 0") qt.Assert(t, issue43.Error(), qt.Not(qt.Contains), "[10] FUNC_PROTO (anon) return=3 args=(3 arg)") truncated := readErrorFromFile(t, "testdata/truncated.log") t.Log(truncated) qt.Assert(t, truncated.Truncated, qt.IsTrue) qt.Assert(t, truncated.Error(), qt.Contains, "str_off: 3166088: str_len: 228") invalidR0 := readErrorFromFile(t, "testdata/invalid-R0.log") t.Log(invalidR0) qt.Assert(t, invalidR0.Error(), qt.Contains, "0: (95) exit: R0 !read_ok") invalidCtx := readErrorFromFile(t, "testdata/invalid-ctx-access.log") t.Log(invalidCtx) qt.Assert(t, invalidCtx.Error(), qt.Contains, "func '__x64_sys_recvfrom' arg0 type FWD is not a struct: invalid bpf_context access off=0 size=8") } func ExampleVerifierError() { err := &VerifierError{ syscall.ENOSPC, []string{"first", "second", "third"}, false, } fmt.Printf("With %%s: %s\n", err) err.Truncated = true fmt.Printf("With %%v and a truncated log: %v\n", err) fmt.Printf("All log lines: %+v\n", err) fmt.Printf("First line: %+1v\n", err) fmt.Printf("Last two lines: %-2v\n", err) // Output: With %s: no space left on device: third (2 line(s) omitted) // With %v and a truncated log: no space left on device: second: third (truncated, 1 line(s) omitted) // All log lines: no space left on device: // first // second // third // (truncated) // First line: no space left on device: // first // (2 line(s) omitted) // (truncated) // Last two lines: no space left on device: // (1 line(s) omitted) // second // third // (truncated) } func readErrorFromFile(tb testing.TB, file string) *VerifierError { tb.Helper() contents, err := os.ReadFile(file) if err != nil { tb.Fatal("Read file:", err) } return ErrorWithLog(unix.EINVAL, contents) }