From 7514cdfe9abfd3cdea2c56f7f18d0a9b938760e7 Mon Sep 17 00:00:00 2001 From: Timo Beckers Date: Wed, 4 Dec 2024 10:42:25 +0100 Subject: [PATCH] info: add tests for recent ProgramInfo API additions Added tests for: - FuncInfos() - LineInfos() - JitedKsymAddrs() - JitedInsns() - JitedLineInfos() - JitedFuncLens() The existing test for JitedKsymAddrs was removed because it's not strictly necessary to load a CollectionSpec to exercise it. It obscured the fact that it seems like kernels before at least 5.4 need to have a subprog for jited addrs to be returned in prog info. TestProgInfoFuncInfos was removed for a similar reason. It's possible to craft a program using the asm package that meets the criteria for exercising FuncInfos() and LineInfos() in a deterministic way, no need to load a Collection. Signed-off-by: Timo Beckers --- info_test.go | 135 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 52 deletions(-) diff --git a/info_test.go b/info_test.go index 224fe2ceb..1134b69c5 100644 --- a/info_test.go +++ b/info_test.go @@ -17,6 +17,30 @@ import ( "github.com/cilium/ebpf/internal/testutils" ) +var btfFn = &btf.Func{ + Name: "_", + Type: &btf.FuncProto{ + Return: &btf.Int{Size: 16}, + Params: []btf.FuncParam{}, + }, + Linkage: btf.StaticFunc, +} + +var multiprogSpec = &ProgramSpec{ + Name: "test", + Type: SocketFilter, + Instructions: asm.Instructions{ + btf.WithFuncMetadata(asm.LoadImm(asm.R0, 0, asm.DWord), btfFn). + WithSource(asm.Comment("line info")), + asm.Call.Label("fn"), + asm.Return(), + btf.WithFuncMetadata(asm.LoadImm(asm.R0, 0, asm.DWord), btfFn). + WithSource(asm.Comment("line info")).WithSymbol("fn"), + asm.Return(), + }, + License: "MIT", +} + func TestMapInfoFromProc(t *testing.T) { hash, err := NewMap(&MapSpec{ Type: Hash, @@ -130,6 +154,13 @@ func TestProgramInfo(t *testing.T) { qt.Assert(t, qt.IsTrue(ok)) qt.Assert(t, qt.IsTrue(verifiedInsns > 0)) } + + if insns, ok := info.JitedInsns(); testutils.IsKernelLessThan(t, "4.13") { + qt.Assert(t, qt.IsFalse(ok)) + } else { + qt.Assert(t, qt.IsTrue(ok)) + qt.Assert(t, qt.IsTrue(len(insns) > 0)) + } } func TestProgramInfoProc(t *testing.T) { @@ -142,6 +173,58 @@ func TestProgramInfoProc(t *testing.T) { validateProgInfo(t, info) } +func TestProgramInfoBTF(t *testing.T) { + prog, err := NewProgram(multiprogSpec) + testutils.SkipIfNotSupported(t, err) + qt.Assert(t, qt.IsNil(err)) + t.Cleanup(func() { prog.Close() }) + + info, err := prog.Info() + testutils.SkipIfNotSupported(t, err) + qt.Assert(t, qt.IsNil(err)) + + // On kernels before 5.x, nr_jited_ksyms is not set for programs without subprogs. + // It's included here since this test uses a bpf program with subprogs. + if addrs, ok := info.JitedKsymAddrs(); testutils.IsKernelLessThan(t, "4.18") { + qt.Assert(t, qt.IsFalse(ok)) + } else { + qt.Assert(t, qt.IsTrue(ok)) + qt.Assert(t, qt.IsTrue(len(addrs) > 0)) + } + + if lens, ok := info.JitedFuncLens(); testutils.IsKernelLessThan(t, "4.18") { + qt.Assert(t, qt.IsFalse(ok)) + } else { + qt.Assert(t, qt.IsTrue(ok)) + qt.Assert(t, qt.IsTrue(len(lens) > 0)) + } + + if infos, ok := info.JitedLineInfos(); testutils.IsKernelLessThan(t, "5.0") { + qt.Assert(t, qt.IsFalse(ok)) + } else { + qt.Assert(t, qt.IsTrue(ok)) + qt.Assert(t, qt.IsTrue(len(infos) > 0)) + } + + if funcs, err := info.FuncInfos(); testutils.IsKernelLessThan(t, "5.0") { + qt.Assert(t, qt.IsNotNil(err)) + } else { + qt.Assert(t, qt.IsNil(err)) + qt.Assert(t, qt.HasLen(funcs, 2)) + qt.Assert(t, qt.ContentEquals(funcs[0].Func, btfFn)) + qt.Assert(t, qt.ContentEquals(funcs[1].Func, btfFn)) + } + + if lines, err := info.LineInfos(); testutils.IsKernelLessThan(t, "5.0") { + qt.Assert(t, qt.IsNotNil(err)) + } else { + qt.Assert(t, qt.IsNil(err)) + qt.Assert(t, qt.HasLen(lines, 2)) + qt.Assert(t, qt.Equals(lines[0].Line.Line(), "line info")) + qt.Assert(t, qt.Equals(lines[1].Line.Line(), "line info")) + } +} + func TestProgramInfoMapIDs(t *testing.T) { arr, err := NewMap(&MapSpec{ Type: Array, @@ -495,55 +578,3 @@ func TestZero(t *testing.T) { qt.Assert(t, qt.IsTrue(zero(&inul))) qt.Assert(t, qt.IsFalse(zero(&ione))) } - -func TestProgInfoKsym(t *testing.T) { - testutils.SkipOnOldKernel(t, "4.18", "Program ksym addresses") - - spec, err := LoadCollectionSpec(testutils.NativeFile(t, "testdata/loader-%s.elf")) - qt.Assert(t, qt.IsNil(err)) - - var obj struct { - Prog *Program `ebpf:"xdp_prog"` - } - err = spec.LoadAndAssign(&obj, nil) - testutils.SkipIfNotSupported(t, err) - qt.Assert(t, qt.IsNil(err)) - defer obj.Prog.Close() - - info, err := obj.Prog.Info() - qt.Assert(t, qt.IsNil(err)) - - addrs, ok := info.JitedKsymAddrs() - qt.Assert(t, qt.IsTrue(ok)) - qt.Assert(t, qt.HasLen(addrs, 5)) - for _, addr := range addrs { - qt.Assert(t, qt.Not(qt.Equals(addr, 0))) - } -} - -func TestProgInfoFuncInfos(t *testing.T) { - testutils.SkipOnOldKernel(t, "5.0", "Program func info") - - spec, err := LoadCollectionSpec(testutils.NativeFile(t, "testdata/loader-%s.elf")) - qt.Assert(t, qt.IsNil(err)) - - var obj struct { - Prog *Program `ebpf:"xdp_prog"` - } - err = spec.LoadAndAssign(&obj, nil) - testutils.SkipIfNotSupported(t, err) - qt.Assert(t, qt.IsNil(err)) - defer obj.Prog.Close() - - info, err := obj.Prog.Info() - qt.Assert(t, qt.IsNil(err)) - - funcs, err := info.FuncInfos() - qt.Assert(t, qt.IsNil(err)) - - qt.Assert(t, qt.HasLen(funcs, 5)) - for _, fo := range funcs { - qt.Assert(t, qt.IsNotNil(fo.Func)) - qt.Assert(t, qt.Not(qt.Equals(fo.Func.Name, ""))) - } -}