github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/link/uprobe_multi_test.go (about) 1 package link 2 3 import ( 4 "errors" 5 "math" 6 "os" 7 "os/exec" 8 "testing" 9 10 "github.com/cilium/ebpf" 11 "github.com/cilium/ebpf/internal/testutils" 12 "github.com/go-quicktest/qt" 13 ) 14 15 func TestUprobeMulti(t *testing.T) { 16 testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti()) 17 18 prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "") 19 20 // uprobe 21 um, err := bashEx.UprobeMulti(bashSyms, prog, nil) 22 if err != nil { 23 t.Fatal(err) 24 } 25 26 testLink(t, um, prog) 27 _ = um.Close() 28 29 // uretprobe 30 um, err = bashEx.UretprobeMulti(bashSyms, prog, nil) 31 if err != nil { 32 t.Fatal(err) 33 } 34 35 testLink(t, um, prog) 36 _ = um.Close() 37 } 38 39 func TestUprobeMultiInput(t *testing.T) { 40 testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti()) 41 42 prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "") 43 44 // Always doing same test for both uprobe and uretprobe 45 46 // One of symbols or offsets must be given. 47 _, err := bashEx.UprobeMulti([]string{}, prog, nil) 48 qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) 49 50 _, err = bashEx.UretprobeMulti([]string{}, prog, nil) 51 qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) 52 53 // One address, two cookies. 54 _, err = bashEx.UprobeMulti([]string{}, prog, &UprobeMultiOptions{ 55 Addresses: []uint64{1}, 56 Cookies: []uint64{2, 3}, 57 }) 58 qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) 59 60 _, err = bashEx.UretprobeMulti([]string{}, prog, &UprobeMultiOptions{ 61 Addresses: []uint64{1}, 62 Cookies: []uint64{2, 3}, 63 }) 64 qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) 65 66 // Two addresses, one refctr offset. 67 _, err = bashEx.UprobeMulti([]string{}, prog, &UprobeMultiOptions{ 68 Addresses: []uint64{1, 2}, 69 RefCtrOffsets: []uint64{4}, 70 }) 71 qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) 72 73 _, err = bashEx.UretprobeMulti([]string{}, prog, &UprobeMultiOptions{ 74 Addresses: []uint64{1, 2}, 75 RefCtrOffsets: []uint64{4}, 76 }) 77 qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) 78 79 // It's either symbols or addresses. 80 _, err = bashEx.UprobeMulti(bashSyms, prog, &UprobeMultiOptions{ 81 Addresses: []uint64{1}, 82 }) 83 qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) 84 85 _, err = bashEx.UretprobeMulti(bashSyms, prog, &UprobeMultiOptions{ 86 Addresses: []uint64{1}, 87 }) 88 qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) 89 90 // No addresses and no symbols 91 _, err = bashEx.UprobeMulti([]string{}, prog, nil) 92 qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) 93 94 _, err = bashEx.UretprobeMulti([]string{}, prog, nil) 95 qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) 96 97 // PID not found 98 _, err = bashEx.UprobeMulti(bashSyms, prog, &UprobeMultiOptions{ 99 PID: math.MaxUint32, 100 }) 101 qt.Assert(t, qt.ErrorIs(err, os.ErrNotExist)) 102 103 _, err = bashEx.UretprobeMulti(bashSyms, prog, &UprobeMultiOptions{ 104 PID: math.MaxUint32, 105 }) 106 qt.Assert(t, qt.ErrorIs(err, os.ErrNotExist)) 107 } 108 109 func TestUprobeMultiResolveOk(t *testing.T) { 110 addrSym1, err := bashEx.address(bashSyms[0], 0, 0) 111 qt.Assert(t, qt.IsNil(err)) 112 addrSym2, err := bashEx.address(bashSyms[1], 0, 0) 113 qt.Assert(t, qt.IsNil(err)) 114 addrSym3, err := bashEx.address(bashSyms[2], 0, 0) 115 qt.Assert(t, qt.IsNil(err)) 116 117 addrs, err := bashEx.addresses(bashSyms, nil, nil) 118 qt.Assert(t, qt.IsNil(err)) 119 qt.Assert(t, qt.DeepEquals(addrs, []uint64{addrSym1, addrSym2, addrSym3})) 120 121 addrs, err = bashEx.addresses(bashSyms, nil, []uint64{5, 10, 11}) 122 qt.Assert(t, qt.IsNil(err)) 123 qt.Assert(t, qt.DeepEquals(addrs, []uint64{addrSym1 + 5, addrSym2 + 10, addrSym3 + 11})) 124 125 addrs, err = bashEx.addresses(bashSyms, []uint64{1, 2, 3}, nil) 126 qt.Assert(t, qt.IsNil(err)) 127 128 qt.Assert(t, qt.DeepEquals(addrs, []uint64{1, 2, 3})) 129 } 130 131 func TestUprobeMultiResolveFail(t *testing.T) { 132 // No input 133 _, err := bashEx.addresses(nil, nil, nil) 134 qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) 135 136 // Different dimensions for Addresses and Offsets 137 _, err = bashEx.addresses(nil, []uint64{100, 200}, []uint64{5, 10, 11}) 138 qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) 139 140 // Different dimensions for symbols and Offsets 141 _, err = bashEx.addresses(bashSyms, nil, []uint64{5, 10}) 142 qt.Assert(t, qt.ErrorIs(err, errInvalidInput)) 143 } 144 145 func TestUprobeMultiCookie(t *testing.T) { 146 testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti()) 147 148 prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "") 149 150 // uprobe 151 um, err := bashEx.UprobeMulti(bashSyms, prog, 152 &UprobeMultiOptions{ 153 Cookies: []uint64{1, 2, 3}, 154 }) 155 if err != nil { 156 t.Fatal(err) 157 } 158 _ = um.Close() 159 160 // uretprobe 161 um, err = bashEx.UretprobeMulti(bashSyms, prog, 162 &UprobeMultiOptions{ 163 Cookies: []uint64{3, 2, 1}, 164 }) 165 if err != nil { 166 t.Fatal(err) 167 } 168 _ = um.Close() 169 } 170 171 func TestUprobeMultiProgramCall(t *testing.T) { 172 testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti()) 173 174 // We execute 'bash --help' 175 args := []string{"--help"} 176 elf := "/bin/bash" 177 178 test := func(retprobe bool, expected uint32) { 179 m, p := newUpdaterMapProg(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti) 180 181 var err error 182 183 // Load the executable. 184 ex, err := OpenExecutable(elf) 185 if err != nil { 186 t.Fatal(err) 187 } 188 189 var um Link 190 191 // Open UprobeMulti on the executable for the given symbol 192 // and attach it to the ebpf program created above. 193 if retprobe { 194 um, err = ex.UretprobeMulti(bashSyms, p, nil) 195 } else { 196 um, err = ex.UprobeMulti(bashSyms, p, nil) 197 } 198 if errors.Is(err, ErrNoSymbol) { 199 // Assume bash_Syms symbols always exist and skip the test 200 // if the symbol can't be found as certain OS (eg. Debian) 201 // strip binaries. 202 t.Skipf("executable %s appear to be stripped, skipping", elf) 203 } 204 if err != nil { 205 t.Fatal(err) 206 } 207 208 // Trigger ebpf program call. 209 trigger := func(t *testing.T) { 210 if err := exec.Command(elf, args...).Run(); err != nil { 211 t.Fatal(err) 212 } 213 } 214 trigger(t) 215 216 // Detach link. 217 if err := um.Close(); err != nil { 218 t.Fatal(err) 219 } 220 221 assertMapValueGE(t, m, 0, expected) 222 223 // Reset map value to 0 at index 0. 224 if err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil { 225 t.Fatal(err) 226 } 227 228 // Retrigger the ebpf program call. 229 trigger(t) 230 231 // Assert that this time the value has not been updated. 232 assertMapValue(t, m, 0, 0) 233 } 234 235 // all 3 uprobes should trigger for entry uprobes 236 test(false, 3) 237 238 // We have return uprobe installed on main, _start and check_dev_tty 239 // functions, but only check_dev_tty is triggered, because 'bash --help' 240 // calls exit(0). 241 test(true, 1) 242 } 243 244 func TestHaveBPFLinkUprobeMulti(t *testing.T) { 245 testutils.CheckFeatureTest(t, haveBPFLinkUprobeMulti) 246 }