Skip to content

Commit 5cc61fc

Browse files
committed
feat: support for uprobe multi info
Signed-off-by: Max Altgelt <max.altgelt@nextron-systems.com>
1 parent bec5dfb commit 5cc61fc

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed

link/link_other.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,63 @@ func (kpm *KprobeMultiInfo) Cookies() ([]uint64, bool) {
226226
return kpm.cookies, kpm.cookies != nil
227227
}
228228

229+
type UprobeMultiInfo struct {
230+
count uint32
231+
flags uint32
232+
missed uint64
233+
offsets []uint64
234+
cookies []uint64
235+
refCtrOffsets []uint64
236+
path string
237+
pid uint32
238+
}
239+
240+
// AddressCount is the number of addresses hooked by the kprobe.
241+
func (umi *UprobeMultiInfo) AddressCount() (uint32, bool) {
242+
return umi.count, umi.count > 0
243+
}
244+
245+
func (umi *UprobeMultiInfo) Flags() (uint32, bool) {
246+
return umi.flags, umi.flags > 0
247+
}
248+
249+
func (umi *UprobeMultiInfo) Missed() (uint64, bool) {
250+
return umi.missed, umi.missed > 0
251+
}
252+
253+
type UprobeMultiOffset struct {
254+
Offset uint64
255+
Cookie uint64
256+
RefCtr uint64
257+
}
258+
259+
// Offsets returns the offsets that the uprobe was attached to along with the related cookies and ref counters.
260+
func (umi *UprobeMultiInfo) Offsets() ([]UprobeMultiOffset, bool) {
261+
if umi.offsets == nil || len(umi.cookies) != len(umi.offsets) || len(umi.refCtrOffsets) != len(umi.offsets) {
262+
return nil, false
263+
}
264+
var adresses = make([]UprobeMultiOffset, len(umi.offsets))
265+
for i := range umi.offsets {
266+
adresses[i] = UprobeMultiOffset{
267+
Offset: umi.offsets[i],
268+
Cookie: umi.cookies[i],
269+
RefCtr: umi.refCtrOffsets[i],
270+
}
271+
}
272+
return adresses, true
273+
}
274+
275+
func (umi *UprobeMultiInfo) Path() string {
276+
return umi.path
277+
}
278+
279+
// Pid returns the process ID that this uprobe is attached to.
280+
//
281+
// If it does not exist, the uprobe will trigger for all processes.
282+
func (umi *UprobeMultiInfo) Pid() (uint32, bool) {
283+
return umi.pid, umi.pid > 0
284+
}
285+
229286
const (
230287
PerfEventUnspecified = sys.BPF_PERF_EVENT_UNSPEC
231288
PerfEventUprobe = sys.BPF_PERF_EVENT_UPROBE
@@ -358,6 +415,14 @@ func (r Info) KprobeMulti() *KprobeMultiInfo {
358415
return e
359416
}
360417

418+
// UprobeMulti returns uprobe-multi type-specific link info.
419+
//
420+
// Returns nil if the type-specific link info isn't available.
421+
func (r Info) UprobeMulti() *UprobeMultiInfo {
422+
e, _ := r.extra.(*UprobeMultiInfo)
423+
return e
424+
}
425+
361426
// PerfEvent returns perf-event type-specific link info.
362427
//
363428
// Returns nil if the type-specific link info isn't available.

link/uprobe_multi.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,58 @@ func (kml *uprobeMultiLink) Update(_ *ebpf.Program) error {
176176
return fmt.Errorf("update uprobe_multi: %w", ErrNotSupported)
177177
}
178178

179+
func (kml *uprobeMultiLink) Info() (*Info, error) {
180+
var info sys.UprobeMultiLinkInfo
181+
if err := sys.ObjInfo(kml.fd, &info); err != nil {
182+
return nil, fmt.Errorf("uprobe multi link info: %s", err)
183+
}
184+
var (
185+
path = make([]byte, info.PathSize)
186+
refCtrOffsets = make([]uint64, info.Count)
187+
addrs = make([]uint64, info.Count)
188+
cookies = make([]uint64, info.Count)
189+
)
190+
info = sys.UprobeMultiLinkInfo{
191+
Path: sys.SlicePointer(path),
192+
PathSize: uint32(len(path)),
193+
Offsets: sys.SlicePointer(addrs),
194+
RefCtrOffsets: sys.SlicePointer(refCtrOffsets),
195+
Cookies: sys.SlicePointer(cookies),
196+
Count: uint32(len(addrs)),
197+
}
198+
if err := sys.ObjInfo(kml.fd, &info); err != nil {
199+
return nil, fmt.Errorf("uprobe multi link info: %s", err)
200+
}
201+
if info.Path.IsNil() {
202+
path = nil
203+
}
204+
if info.Cookies.IsNil() {
205+
cookies = nil
206+
}
207+
if info.Offsets.IsNil() {
208+
addrs = nil
209+
}
210+
if info.RefCtrOffsets.IsNil() {
211+
refCtrOffsets = nil
212+
}
213+
extra := &UprobeMultiInfo{
214+
count: info.Count,
215+
flags: info.Flags,
216+
pid: info.Pid,
217+
offsets: addrs,
218+
cookies: cookies,
219+
refCtrOffsets: refCtrOffsets,
220+
path: unix.ByteSliceToString(path),
221+
}
222+
223+
return &Info{
224+
info.Type,
225+
info.Id,
226+
ebpf.ProgramID(info.ProgId),
227+
extra,
228+
}, nil
229+
}
230+
179231
var haveBPFLinkUprobeMulti = internal.NewFeatureTest("bpf_link_uprobe_multi", func() error {
180232
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
181233
Name: "probe_upm_link",

link/uprobe_multi_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,31 @@ func TestUprobeMulti(t *testing.T) {
3939
_ = um.Close()
4040
}
4141

42+
func TestUprobeMultiInfo(t *testing.T) {
43+
testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti())
44+
testutils.SkipOnOldKernel(t, "6.8", "bpf_link_info_uprobe_multi")
45+
46+
prog := mustLoadProgram(t, ebpf.Kprobe, ebpf.AttachTraceUprobeMulti, "")
47+
48+
// uprobe
49+
um, err := bashEx.UprobeMulti(bashSyms, prog, nil)
50+
if err != nil {
51+
t.Fatal(err)
52+
}
53+
defer um.Close()
54+
55+
linkInfo, err := um.Info()
56+
if err != nil {
57+
t.Fatal(err)
58+
}
59+
qt.Assert(t, qt.Equals(linkInfo.Type, UprobeMultiType))
60+
uprobeDetails := linkInfo.UprobeMulti()
61+
qt.Assert(t, qt.StringContains(uprobeDetails.Path(), bashEx.path)) // On some platforms, /bin/bash may point to /usr/bin/bash, thus only a contains and no equals check
62+
uprobeOffsets, ok := uprobeDetails.Offsets()
63+
qt.Assert(t, qt.IsTrue(ok))
64+
qt.Assert(t, qt.HasLen(uprobeOffsets, len(bashSyms)))
65+
}
66+
4267
func TestUprobeMultiInput(t *testing.T) {
4368
testutils.SkipIfNotSupported(t, haveBPFLinkUprobeMulti())
4469

0 commit comments

Comments
 (0)