diff --git a/link/link_test.go b/link/link_test.go index e4203bd07..c1d06e650 100644 --- a/link/link_test.go +++ b/link/link_test.go @@ -229,6 +229,30 @@ func testLink(t *testing.T, link Link, prog *ebpf.Program) { t.Fatalf("Failed to get link KprobeMulti extra info") } qt.Assert(t, qt.IsTrue(len(kmulti.Addrs) == 0)) + case sys.BPF_LINK_TYPE_PERF_EVENT: + pevent := info.PerfEvent() + switch pevent.Type { + case sys.BPF_PERF_EVENT_KPROBE, sys.BPF_PERF_EVENT_KRETPROBE: + kprobe := pevent.Kprobe() + if kprobe.Addr == 0 { + t.Fatalf("Failed to get link Kprobe info") + } + case sys.BPF_PERF_EVENT_UPROBE, sys.BPF_PERF_EVENT_URETPROBE: + uprobe := pevent.Uprobe() + if uprobe.Offset == 0 { + t.Fatalf("Failed to get link Uprobe info") + } + case sys.BPF_PERF_EVENT_TRACEPOINT: + tp := pevent.Tracepoint() + if tp.NameLen == 0 { + t.Fatalf("Failed to get link Tracepoint info") + } + case sys.BPF_PERF_EVENT_EVENT: + event := pevent.Event() + if event.Type == 0 { + t.Fatalf("Failed to get link Event info") + } + } } }) diff --git a/link/perf_event.go b/link/perf_event.go index 534974c6b..996a213aa 100644 --- a/link/perf_event.go +++ b/link/perf_event.go @@ -1,6 +1,8 @@ package link import ( + "bytes" + "encoding/binary" "errors" "fmt" "runtime" @@ -158,6 +160,98 @@ func (pi *perfEventIoctl) InfoOpts(_ *InfoOpts) (*Info, error) { return nil, fmt.Errorf("perf event ioctl info: %w", ErrNotSupported) } +type PerfEventInfo struct { + Type PerfEventInfoType + extra interface{} +} + +type KprobeInfo sys.KprobeLinkInfo +type UprobeInfo sys.UprobeLinkInfo +type TracepointInfo sys.TracepointLinkInfo +type EventInfo sys.EventLinkInfo + +// PerfEvent returns perf event type-specific link info. +// +// Returns nil if the type-specific link info isn't available. +func (r Info) PerfEvent() *PerfEventInfo { + e, _ := r.extra.(*PerfEventInfo) + return e +} + +func (r *PerfEventInfo) Kprobe() *KprobeInfo { + e, _ := r.extra.(*KprobeInfo) + return e +} + +func (r *PerfEventInfo) Uprobe() *UprobeInfo { + e, _ := r.extra.(*UprobeInfo) + return e +} + +func (r *PerfEventInfo) Tracepoint() *TracepointInfo { + e, _ := r.extra.(*TracepointInfo) + return e +} + +func (r *PerfEventInfo) Event() *EventInfo { + e, _ := r.extra.(*EventInfo) + return e +} + +func (pl *perfEventLink) Info() (*Info, error) { + return pl.InfoOpts(&InfoOpts{}) +} + +func (pl *perfEventLink) InfoOpts(opts *InfoOpts) (*Info, error) { + var info sys.LinkInfo + var err error + + if err = sys.ObjInfo(pl.fd, &info); err != nil { + return nil, fmt.Errorf("link info: %s", err) + } + + var buf *bytes.Reader + var pevent sys.PerfEventLinkInfo + + buf = bytes.NewReader(info.Extra[:]) + err = binary.Read(buf, internal.NativeEndian, &pevent) + if err != nil { + return nil, fmt.Errorf("cannot read extra link info: %w", err) + } + + var extra interface{} + switch pevent.Type { + case sys.BPF_PERF_EVENT_KPROBE, sys.BPF_PERF_EVENT_KRETPROBE: + extra = &KprobeInfo{} + case sys.BPF_PERF_EVENT_UPROBE, sys.BPF_PERF_EVENT_URETPROBE: + extra = &UprobeInfo{} + case sys.BPF_PERF_EVENT_TRACEPOINT: + extra = &TracepointInfo{} + case sys.BPF_PERF_EVENT_EVENT: + extra = &EventInfo{} + } + + if extra == nil { + return nil, fmt.Errorf("perf event ioctl info: %w", ErrNotSupported) + } + + buf = bytes.NewReader(pevent.Extra[:]) + err = binary.Read(buf, internal.NativeEndian, extra) + if err != nil { + return nil, fmt.Errorf("cannot read extra link info: %w", err) + } + + return &Info{ + info.Type, + info.Id, + ebpf.ProgramID(info.ProgId), + &PerfEventInfo{ + pevent.Type, + extra, + }, + }, nil +} + // attach the given eBPF prog to the perf event stored in pe. // pe must contain a valid perf event fd. // prog's type must match the program type stored in pe.