github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/perf/perf_event.go (about) 1 package perf 2 3 import ( 4 "fmt" 5 "syscall" 6 7 "github.com/dylandreimerink/gobpfld/bpfsys" 8 bpfSyscall "github.com/dylandreimerink/gobpfld/internal/syscall" 9 "golang.org/x/sys/unix" 10 ) 11 12 // Event represents an linux perf event in userspace. 13 type Event struct { 14 Type bpfSyscall.PerfType 15 16 fd FD 17 attachedProgram bpfsys.BPFfd 18 kprobe *KProbe 19 uprobe *UProbe 20 } 21 22 // AttachBPFProgram attach a loaded BPF program to the perf event. 23 func (e *Event) AttachBPFProgram(programFD bpfsys.BPFfd) error { 24 // TODO check that fd is set 25 26 // Attach BPF program 27 err := bpfSyscall.IOCtl(int(e.fd), unix.PERF_EVENT_IOC_SET_BPF, uintptr(programFD)) 28 if err != nil { 29 return err 30 } 31 32 // Enable the perf event 33 err = bpfSyscall.IOCtl(int(e.fd), unix.PERF_EVENT_IOC_ENABLE, 0) 34 if err != nil { 35 return err 36 } 37 38 e.attachedProgram = programFD 39 40 return nil 41 } 42 43 func (e *Event) DetachBPFProgram() error { 44 // disable the perf event 45 err := bpfSyscall.IOCtl(int(e.fd), unix.PERF_EVENT_IOC_DISABLE, 0) 46 if err != nil { 47 return fmt.Errorf("ioctl disable perf event: %w", err) 48 } 49 50 // close the fd of the perf event 51 err = syscall.Close(int(e.fd)) 52 if err != nil { 53 return fmt.Errorf("close perf event: %w", err) 54 } 55 56 if e.kprobe != nil { 57 // ignore error 58 err = e.kprobe.Clear() 59 if err != nil { 60 return fmt.Errorf("clear kprobe: %w", err) 61 } 62 } 63 if e.uprobe != nil { 64 // ignore error 65 err = e.uprobe.Clear() 66 if err != nil { 67 return fmt.Errorf("clear uprobe: %w", err) 68 } 69 } 70 71 return nil 72 } 73 74 type FD uint32 75 76 // Close closes a file descriptor 77 func (fd FD) Close() error { 78 err := unix.Close(int(fd)) 79 if err != nil { 80 return err 81 } 82 83 return nil 84 } 85 86 // OpenTracepointEvent opens a perf event for an existing tracepoint. Tracepoint perf events can be used to to attach 87 // BPF_PROG_TYPE_TRACEPOINT applications to. 88 func OpenTracepointEvent(category, name string) (*Event, error) { 89 id, err := getTracepointID(category, name) 90 if err != nil { 91 return nil, fmt.Errorf("getTracepointID: %w", err) 92 } 93 94 return perfEventOpen(bpfSyscall.PerfEventAttr{ 95 Type: bpfSyscall.PERF_TYPE_TRACEPOINT, 96 Size: bpfSyscall.AttrSize, 97 Config: uint64(id), 98 }, -1, 0, -1, bpfSyscall.PerfEventOpenFDCloseOnExit) 99 } 100 101 // TODO add open event buffer function 102 103 func OpenKProbeEvent(kprobeOpts KProbeOpts) (*Event, error) { 104 kprobe, err := newKProbe(kprobeOpts) 105 if err != nil { 106 return nil, fmt.Errorf("kprobe: %w", err) 107 } 108 109 // TODO using the debugfs and tracepoint type is apparently legacy, the new way to do it is using 110 // dynamic PMU's. Couldn't get this to work, so in future figure it out and add as preferred method 111 // and keep this one as fallback for older kernels 112 113 event, err := perfEventOpen(bpfSyscall.PerfEventAttr{ 114 Type: bpfSyscall.PERF_TYPE_TRACEPOINT, 115 Size: bpfSyscall.AttrSize, 116 Config: uint64(kprobe.ID), 117 }, -1, 0, -1, bpfSyscall.PerfEventOpenFDCloseOnExit) 118 if err != nil { 119 return nil, fmt.Errorf("open perf event: %w", err) 120 } 121 122 event.kprobe = kprobe 123 124 return event, nil 125 } 126 127 func OpenUProbeEvent(uprobeOpts UProbeOpts) (*Event, error) { 128 uprobe, err := newUProbe(uprobeOpts) 129 if err != nil { 130 return nil, fmt.Errorf("uprobe: %w", err) 131 } 132 133 // TODO add CPU and PID options since they are allowed for uprobes to trace specific programs 134 135 // TODO using the debugfs and tracepoint type is apparently legacy, the new way to do it is using 136 // dynamic PMU's. Couldn't get this to work, so in future figure it out and add as preferred method 137 // and keep this one as fallback for older kernels 138 139 event, err := perfEventOpen(bpfSyscall.PerfEventAttr{ 140 Type: bpfSyscall.PERF_TYPE_TRACEPOINT, 141 Size: bpfSyscall.AttrSize, 142 Config: uint64(uprobe.ID), 143 }, -1, 0, -1, bpfSyscall.PerfEventOpenFDCloseOnExit) 144 if err != nil { 145 return nil, fmt.Errorf("open perf event: %w", err) 146 } 147 148 event.uprobe = uprobe 149 150 return event, nil 151 } 152 153 // perfEventOpen is a wrapper around the perf_event_open syscall. 154 func perfEventOpen( 155 attr bpfSyscall.PerfEventAttr, 156 pid, 157 cpu, 158 groupFD int, 159 flags bpfSyscall.PerfEventOpenFlags, 160 ) (*Event, error) { 161 fd, err := bpfSyscall.PerfEventOpen(attr, pid, cpu, groupFD, flags) 162 if err != nil { 163 return nil, err 164 } 165 166 return &Event{ 167 Type: attr.Type, 168 fd: FD(fd), 169 }, nil 170 }