github.com/cilium/ebpf@v0.10.0/internal/errors_test.go (about)

     1  package internal
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"syscall"
     8  	"testing"
     9  
    10  	"github.com/cilium/ebpf/internal/unix"
    11  	qt "github.com/frankban/quicktest"
    12  )
    13  
    14  func TestVerifierErrorWhitespace(t *testing.T) {
    15  	b := []byte("unreachable insn 28")
    16  	b = append(b,
    17  		0xa,  // \n
    18  		0xd,  // \r
    19  		0x9,  // \t
    20  		0x20, // space
    21  		0, 0, // trailing NUL bytes
    22  	)
    23  
    24  	err := ErrorWithLog("frob", errors.New("test"), b, false)
    25  	qt.Assert(t, err.Error(), qt.Equals, "frob: test: unreachable insn 28")
    26  
    27  	for _, log := range [][]byte{
    28  		nil,
    29  		[]byte("\x00"),
    30  		[]byte(" "),
    31  	} {
    32  		err = ErrorWithLog("frob", errors.New("test"), log, false)
    33  		qt.Assert(t, err.Error(), qt.Equals, "frob: test", qt.Commentf("empty log %q has incorrect format", log))
    34  	}
    35  }
    36  
    37  func TestVerifierErrorWrapping(t *testing.T) {
    38  	ve := ErrorWithLog("frob", unix.ENOENT, nil, false)
    39  	qt.Assert(t, ve, qt.ErrorIs, unix.ENOENT, qt.Commentf("should wrap provided error"))
    40  	qt.Assert(t, ve.Truncated, qt.IsFalse, qt.Commentf("verifier log should not be marked as truncated"))
    41  
    42  	ve = ErrorWithLog("frob", unix.EINVAL, nil, true)
    43  	qt.Assert(t, ve, qt.ErrorIs, unix.EINVAL, qt.Commentf("should wrap provided error"))
    44  	qt.Assert(t, ve.Truncated, qt.IsTrue, qt.Commentf("verifier log should be marked as truncated"))
    45  
    46  	ve = ErrorWithLog("frob", unix.EINVAL, []byte("foo"), false)
    47  	qt.Assert(t, ve, qt.ErrorIs, unix.EINVAL, qt.Commentf("should wrap provided error"))
    48  	qt.Assert(t, ve.Error(), qt.Contains, "foo", qt.Commentf("verifier log should appear in error string"))
    49  
    50  	ve = ErrorWithLog("frob", unix.ENOSPC, []byte("foo"), true)
    51  	qt.Assert(t, ve, qt.ErrorIs, unix.ENOSPC, qt.Commentf("should wrap provided error"))
    52  	qt.Assert(t, ve.Error(), qt.Contains, "foo", qt.Commentf("verifier log should appear in error string"))
    53  	qt.Assert(t, ve.Truncated, qt.IsTrue, qt.Commentf("verifier log should be marked truncated"))
    54  }
    55  
    56  func TestVerifierErrorSummary(t *testing.T) {
    57  	// Suppress the last line containing 'processed ... insns'.
    58  	errno524 := readErrorFromFile(t, "testdata/errno524.log")
    59  	qt.Assert(t, errno524.Error(), qt.Contains, "JIT doesn't support bpf-to-bpf calls")
    60  	qt.Assert(t, errno524.Error(), qt.Not(qt.Contains), "processed 39 insns")
    61  
    62  	// Include the previous line if the current one starts with a tab.
    63  	invalidMember := readErrorFromFile(t, "testdata/invalid-member.log")
    64  	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")
    65  
    66  	// Only include the last line.
    67  	issue43 := readErrorFromFile(t, "testdata/issue-43.log")
    68  	qt.Assert(t, issue43.Error(), qt.Contains, "[11] FUNC helper_func2 type_id=10 vlen != 0")
    69  	qt.Assert(t, issue43.Error(), qt.Not(qt.Contains), "[10] FUNC_PROTO (anon) return=3 args=(3 arg)")
    70  
    71  	// Include instruction that caused invalid register access.
    72  	invalidR0 := readErrorFromFile(t, "testdata/invalid-R0.log")
    73  	qt.Assert(t, invalidR0.Error(), qt.Contains, "0: (95) exit: R0 !read_ok")
    74  
    75  	// Include symbol that doesn't match context type.
    76  	invalidCtx := readErrorFromFile(t, "testdata/invalid-ctx-access.log")
    77  	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")
    78  }
    79  
    80  func ExampleVerifierError() {
    81  	err := &VerifierError{
    82  		source:    "catastrophe",
    83  		Cause:     syscall.ENOSPC,
    84  		Log:       []string{"first", "second", "third"},
    85  		Truncated: false,
    86  	}
    87  
    88  	fmt.Printf("With %%s: %s\n", err)
    89  	err.Truncated = true
    90  	fmt.Printf("With %%v and a truncated log: %v\n", err)
    91  	fmt.Printf("All log lines: %+v\n", err)
    92  	fmt.Printf("First line: %+1v\n", err)
    93  	fmt.Printf("Last two lines: %-2v\n", err)
    94  
    95  	// Output: With %s: catastrophe: no space left on device: third (2 line(s) omitted)
    96  	// With %v and a truncated log: catastrophe: no space left on device: second: third (truncated, 1 line(s) omitted)
    97  	// All log lines: catastrophe: no space left on device:
    98  	// 	first
    99  	// 	second
   100  	// 	third
   101  	// 	(truncated)
   102  	// First line: catastrophe: no space left on device:
   103  	// 	first
   104  	// 	(2 line(s) omitted)
   105  	// 	(truncated)
   106  	// Last two lines: catastrophe: no space left on device:
   107  	// 	(1 line(s) omitted)
   108  	// 	second
   109  	// 	third
   110  	// 	(truncated)
   111  }
   112  
   113  func readErrorFromFile(tb testing.TB, file string) *VerifierError {
   114  	tb.Helper()
   115  
   116  	contents, err := os.ReadFile(file)
   117  	if err != nil {
   118  		tb.Fatal("Read file:", err)
   119  	}
   120  
   121  	return ErrorWithLog("file", unix.EINVAL, contents, false)
   122  }