diff --git a/link/uprobe_multi_test.go b/link/uprobe_multi_test.go new file mode 100644 index 000000000..48d205830 --- /dev/null +++ b/link/uprobe_multi_test.go @@ -0,0 +1,159 @@ +package link + +import ( + "errors" + "os/exec" + "testing" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/internal/testutils" + "github.com/go-quicktest/qt" +) + +func TestUprobeMulti(t *testing.T) { + testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti()) + + prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "") + + um, err := bashEx.UprobeMulti([]string{"bash_logout", "main"}, prog, nil) + if err != nil { + t.Fatal(err) + } + defer um.Close() + + testLink(t, um, prog) +} + +func TestUprobeMultiInput(t *testing.T) { + testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti()) + + prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "") + + // One of symbols or offsets must be given. + _, err := bashEx.UprobeMulti([]string{}, prog, nil) + qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) + + // One address, two cookies. + _, err = bashEx.UprobeMulti([]string{}, prog, &UprobeMultiOptions{ + Addresses: []uint64{1}, + Cookies: []uint64{2, 3}, + }) + qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) + + // Two addresses, ont refctr offset. + _, err = bashEx.UprobeMulti([]string{}, prog, &UprobeMultiOptions{ + Addresses: []uint64{1, 2}, + RefCtrOffsets: []uint64{4}, + }) + qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) + + // It's either symbols or addresses. + _, err = bashEx.UprobeMulti([]string{"main"}, prog, &UprobeMultiOptions{ + Addresses: []uint64{1}, + }) + qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) +} + +func TestUprobeMultiAddresses(t *testing.T) { + addrSym1, err := bashEx.address("main") + if err != nil { + t.Fatal(err) + } + addrSym2, err := bashEx.address("_start") + if err != nil { + t.Fatal(err) + } + + addresses, err := bashEx.uprobeMultiAddresses([]string{"main", "_start"}, &UprobeMultiOptions{}) + if err != nil { + t.Fatal(err) + } + + qt.Assert(t, qt.Equals(len(addresses), 2)) + qt.Assert(t, qt.Equals(addresses[0], addrSym1)) + qt.Assert(t, qt.Equals(addresses[1], addrSym2)) + + addresses, err = bashEx.uprobeMultiAddresses([]string{"main", "_start"}, + &UprobeMultiOptions{Offsets: []uint64{5, 10}}) + if err != nil { + t.Fatal(err) + } + + qt.Assert(t, qt.Equals(len(addresses), 2)) + qt.Assert(t, qt.Equals(addresses[0], addrSym1+5)) + qt.Assert(t, qt.Equals(addresses[1], addrSym2+10)) +} + +func TestUprobeMultiCookie(t *testing.T) { + testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti()) + + prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "") + + um, err := bashEx.UprobeMulti([]string{"bash_logout", "main"}, prog, + &UprobeMultiOptions{ + Cookies: []uint64{1, 2}, + }) + if err != nil { + t.Fatal(err) + } + _ = um.Close() +} + +func TestUprobeMultiProgramCall(t *testing.T) { + testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti()) + + args := []string{"--help"} + elf := "/bin/bash" + + m, p := newUpdaterMapProg(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti) + + // Load the executable. + ex, err := OpenExecutable(elf) + if err != nil { + t.Fatal(err) + } + + // Open UprobeMulti on the executable for the given symbol + // and attach it to the ebpf program created above. + um, err := ex.UprobeMulti([]string{"main", "_start"}, p, nil) + if errors.Is(err, ErrNoSymbol) { + // Assume bash::main and go::main.main always exists + // and skip the test if the symbol can't be found as + // certain OS (eg. Debian) strip binaries. + t.Skipf("executable %s appear to be stripped, skipping", elf) + } + if err != nil { + t.Fatal(err) + } + + // Trigger ebpf program call. + trigger := func(t *testing.T) { + if err := exec.Command(elf, args...).Run(); err != nil { + t.Fatal(err) + } + } + trigger(t) + + // Assert that the value at index 0 has been updated to 2. + assertMapValueGE(t, m, 0, 2) + + // Detach the Uprobe. + if err := um.Close(); err != nil { + t.Fatal(err) + } + + // Reset map value to 0 at index 0. + if err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil { + t.Fatal(err) + } + + // Retrigger the ebpf program call. + trigger(t) + + // Assert that this time the value has not been updated. + assertMapValue(t, m, 0, 0) +} + +func TestHaveBPFLinkUprobeMulti(t *testing.T) { + testutils.CheckFeatureTest(t, haveBPFLinkUprobeMulti) +}