Skip to content

Commit

Permalink
link: add test for uprobe multi link
Browse files Browse the repository at this point in the history
This commit adds test for uprobe multi link.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
  • Loading branch information
olsajiri committed Dec 13, 2023
1 parent 92f8d87 commit f86b1fd
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 0 deletions.
65 changes: 65 additions & 0 deletions link/kprobe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,71 @@ func newUpdaterMapProg(t *testing.T, typ ebpf.ProgramType, attach ebpf.AttachTyp
return m, p
}

func newUpdaterMapIncProg(t *testing.T, typ ebpf.ProgramType, attach ebpf.AttachType) (*ebpf.Map, *ebpf.Program) {
// Create ebpf map. Will contain only one key with initial value 0.
m, err := ebpf.NewMap(&ebpf.MapSpec{
Type: ebpf.Array,
KeySize: 4,
ValueSize: 4,
MaxEntries: 1,
})
if err != nil {
t.Fatal(err)
}

// Create ebpf program. When called, will increase the value of key 0 by 1
// in the map created above
p, err := ebpf.NewProgram(&ebpf.ProgramSpec{
Type: typ,
Instructions: asm.Instructions{
// R1 map
asm.LoadMapPtr(asm.R1, m.FD()),
// R2 key
asm.Mov.Reg(asm.R2, asm.R10),
asm.Add.Imm(asm.R2, -4),
asm.StoreImm(asm.R2, 0, 0, asm.Word),
// Lookup map[0]
asm.FnMapLookupElem.Call(),
asm.JEq.Imm(asm.R0, 0, "ret"),

// u32 val = R0++
asm.LoadMem(asm.R1, asm.R0, 0, asm.Word),
asm.Add.Imm(asm.R1, 1),
asm.StoreMem(asm.RFP, -8, asm.R1, asm.Word),

// u32 key = 0
asm.Mov.Imm(asm.R1, 0),
asm.StoreMem(asm.RFP, -4, asm.R1, asm.Word),

// bpf_map_update_elem(...)
asm.Mov.Reg(asm.R2, asm.RFP),
asm.Add.Imm(asm.R2, -4),
asm.Mov.Reg(asm.R3, asm.RFP),
asm.Add.Imm(asm.R3, -8),
asm.LoadMapPtr(asm.R1, m.FD()),
asm.Mov.Imm(asm.R4, 0),
asm.FnMapUpdateElem.Call(),

// exit 0
asm.Mov.Imm(asm.R0, 0),
asm.Return().WithSymbol("ret"),
},
AttachType: attach,
License: "Dual MIT/GPL",
})
if err != nil {
t.Fatal(err)
}

// Close the program and map on test teardown.
t.Cleanup(func() {
m.Close()
p.Close()
})

return m, p
}

func assertMapValue(t *testing.T, m *ebpf.Map, k, v uint32) {
var val uint32
if err := m.Lookup(k, &val); err != nil {
Expand Down
133 changes: 133 additions & 0 deletions link/uprobe_multi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package link

import (
"errors"
"os/exec"
"testing"

"github.com/cilium/ebpf"
"github.com/cilium/ebpf/internal/testutils"
"github.com/cilium/ebpf/internal/unix"
)

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 Addresses must be given.
_, err := bashEx.UprobeMulti([]string{}, prog, nil)
if !errors.Is(err, errInvalidInput) {
t.Fatalf("expected errInvalidInput, got: %v", err)
}

// One Symbol, two cookies..
_, err = bashEx.UprobeMulti([]string{}, prog, &UprobeMultiOptions{
Offsets: []uint64{1},
Cookies: []uint64{2, 3},
})
if !errors.Is(err, errInvalidInput) {
t.Fatalf("expected errInvalidInput, got: %v", err)
}
}

func TestUprobeMultiErrors(t *testing.T) {
testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti())

prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "")

// Wrong offset
um, err := bashEx.UprobeMulti([]string{}, prog, &UprobeMultiOptions{Offsets: []uint64{1<<64 - 1}})
if !errors.Is(err, unix.EINVAL) {
um.Close()
t.Skipf("need kernel change, skipping for now, expected EINVAL, got: %s", err)
}
}

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 := newUpdaterMapIncProg(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.
assertMapValue(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)
}

0 comments on commit f86b1fd

Please sign in to comment.