From f71600f2ca9e2bd671900c753decb9923c364c58 Mon Sep 17 00:00:00 2001 From: Viet Anh Duong Date: Thu, 23 Nov 2023 16:24:39 +0000 Subject: [PATCH] Refactor: Remove epoll, migrate to `perf_reader_poll` Signed-off-by: Viet Anh Duong --- bcc/module.go | 6 +-- bcc/perf.go | 105 ++++++++++++-------------------------- examples/bcc/perf/perf.go | 6 +-- 3 files changed, 38 insertions(+), 79 deletions(-) diff --git a/bcc/module.go b/bcc/module.go index 1302fa6..70de659 100644 --- a/bcc/module.go +++ b/bcc/module.go @@ -72,7 +72,7 @@ type Module struct { tracepoints map[string]int rawTracepoints map[string]int perfEvents map[string][]int - perfBuffers map[string]*PerfBuffer + perfBuffers map[string]*PerfEvent symCacheMu sync.Mutex symCaches map[int]*SymbolCache @@ -126,7 +126,7 @@ func newModule(code string, opts *ModuleOptions) *Module { tracepoints: make(map[string]int), rawTracepoints: make(map[string]int), perfEvents: make(map[string][]int), - perfBuffers: make(map[string]*PerfBuffer), + perfBuffers: make(map[string]*PerfEvent), symCaches: make(map[int]*SymbolCache), } } @@ -681,7 +681,7 @@ func (bpf *Module) ClosePerfBuffer(name string) error { return perfBuf.CloseAllCpu() } -func (bpf *Module) GetPerfBuffer(name string) *PerfBuffer { +func (bpf *Module) GetPerfBuffer(name string) *PerfEvent { return bpf.perfBuffers[name] } diff --git a/bcc/perf.go b/bcc/perf.go index 63605c9..a0bfff3 100644 --- a/bcc/perf.go +++ b/bcc/perf.go @@ -2,7 +2,6 @@ package bcc import ( "fmt" - "log" "runtime" "runtime/cgo" "time" @@ -27,42 +26,28 @@ import ( extern void rawCallback(void*, void*, int); // typedef void (*perf_reader_lost_cb)(void *cb_cookie, uint64_t lost); extern void lostCallback(void*, uint64_t); - -struct epoll_event create_ptr_event(int event_type, void* ptr) { - struct epoll_event event = { .events = event_type }; - event.data.ptr = ptr; - return event; -} - -void* get_event_data_ptr(struct epoll_event event) { return event.data.ptr; } */ import "C" -type PerfBuffer struct { - table *Table - epfd C.int - readers map[int]*C.struct_perf_reader - handler cgo.Handle - epEvents []C.struct_epoll_event +// TODO(vietanhduong): Implement open perf event +type PerfEvent struct { + table *Table + readers map[int]*C.struct_perf_reader + handler cgo.Handle } -func CreatePerfBuffer(table *Table) *PerfBuffer { - return &PerfBuffer{ +func CreatePerfBuffer(table *Table) *PerfEvent { + return &PerfEvent{ table: table, - epfd: -1, readers: make(map[int]*C.struct_perf_reader), } } -func (perf *PerfBuffer) Close() error { +func (perf *PerfEvent) Close() error { return perf.CloseAllCpu() } -func (perf *PerfBuffer) OpenAllCpu(cb Callback, pageCnt int) error { - if len(perf.readers) != 0 || perf.epfd != -1 { - return fmt.Errorf("perviously opened perf buffer not cleaned") - } - +func (perf *PerfEvent) OpenAllCpu(cb Callback, pageCnt int) error { cpus, err := cpuonline.Get() if err != nil { return fmt.Errorf("get online cpu: %v", err) @@ -72,23 +57,11 @@ func (perf *PerfBuffer) OpenAllCpu(cb Callback, pageCnt int) error { cb = &emptyCallback{} } - perf.epEvents = make([]C.struct_epoll_event, len(cpus)) - perf.epfd, err = C.epoll_create1(C.EPOLL_CLOEXEC) - if err != nil { - return fmt.Errorf("failed to create epoll: %v", err) - } - perf.handler = cgo.NewHandle(cb) - runtime.SetFinalizer(perf, (*PerfBuffer).Close) + runtime.SetFinalizer(perf, (*PerfEvent).Close) for _, cpu := range cpus { - opts := &C.struct_bcc_perf_buffer_opts{ - pid: -1, - cpu: C.int(cpu), - wakeup_events: 1, - } - - if err := perf.openOnCpu(pageCnt, opts); err != nil { + if err := perf.openOnCpu(int(cpu), pageCnt, 1); err != nil { _ = perf.CloseAllCpu() return err } @@ -96,19 +69,10 @@ func (perf *PerfBuffer) OpenAllCpu(cb Callback, pageCnt int) error { return nil } -func (perf *PerfBuffer) CloseAllCpu() error { +func (perf *PerfEvent) CloseAllCpu() error { var errStr string perf.handler.Delete() - if int(perf.epfd) >= 0 { - _, err := C.close(perf.epfd) - perf.epfd = -1 - perf.epEvents = perf.epEvents[:0] - if err != nil { - errStr += fmt.Sprintf("close epoll: %v\n", err) - } - } - for cpu := range perf.readers { if err := perf.closeOnCpu(cpu); err != nil { errStr += fmt.Sprintf("cpu %d: %v\n", cpu, err) @@ -121,31 +85,31 @@ func (perf *PerfBuffer) CloseAllCpu() error { return nil } -func (perf *PerfBuffer) Poll(timeout time.Duration) int { - if perf.epfd < 0 { - return -1 - } - - timeoutMs := C.int(timeout.Milliseconds()) - cnt, err := C.epoll_wait(perf.epfd, &perf.epEvents[0], C.int(len(perf.readers)), timeoutMs) - if err != nil { - log.Printf("epoll_wait: %v", err) - } +func (perf *PerfEvent) Poll(timeout time.Duration) int { + ctimeout := C.int(timeout.Milliseconds()) - for i := 0; i < int(cnt); i++ { - C.perf_reader_event_read((*C.struct_perf_reader)(unsafe.Pointer(C.get_event_data_ptr(perf.epEvents[i])))) + var readers []*C.struct_perf_reader + for _, reader := range perf.readers { + readers = append(readers, reader) } - return int(cnt) + res := C.perf_reader_poll(C.int(len(readers)), &readers[0], ctimeout) + return int(res) } -func (perf *PerfBuffer) openOnCpu(pageCnt int, opts *C.struct_bcc_perf_buffer_opts) error { - if _, ok := perf.readers[int(opts.cpu)]; ok { - return fmt.Errorf("perf buffer already open on CPU %d", opts.cpu) +func (perf *PerfEvent) openOnCpu(cpu, pageCnt, weakupEvents int) error { + if _, ok := perf.readers[cpu]; ok { + return fmt.Errorf("perf buffer already open on CPU %d", cpu) } if (pageCnt & (pageCnt - 1)) != 0 { return fmt.Errorf("pageCnt must be a power of 2: %d", pageCnt) } + opts := &C.struct_bcc_perf_buffer_opts{ + pid: -1, + cpu: C.int(cpu), + wakeup_events: C.int(weakupEvents), + } + reader, err := C.bpf_open_perf_buffer_opts( // Raw callback (C.perf_reader_raw_cb)(unsafe.Pointer(C.rawCallback)), @@ -153,8 +117,10 @@ func (perf *PerfBuffer) openOnCpu(pageCnt int, opts *C.struct_bcc_perf_buffer_op (C.perf_reader_lost_cb)(unsafe.Pointer(C.lostCallback)), // Callback Cookie unsafe.Pointer(&perf.handler), - C.int(pageCnt), opts, + C.int(pageCnt), + opts, ) + if reader == nil { return fmt.Errorf("unable to open perf buffer: %v", err) } @@ -165,17 +131,11 @@ func (perf *PerfBuffer) openOnCpu(pageCnt int, opts *C.struct_bcc_perf_buffer_op return fmt.Errorf("unable to open perf buffer on CPU %d: %v", opts.cpu, err) } - event := C.create_ptr_event(C.EPOLLIN, unsafe.Pointer(reader)) - if _, err = C.epoll_ctl(perf.epfd, C.EPOLL_CTL_ADD, readerFd, &event); err != nil { - C.perf_reader_free(unsafe.Pointer(reader)) - return fmt.Errorf("unable to add perf buffer FD to epoll: %v", err) - } - perf.readers[int(opts.cpu)] = ((*C.struct_perf_reader)(reader)) return nil } -func (perf *PerfBuffer) closeOnCpu(cpu int) error { +func (perf *PerfEvent) closeOnCpu(cpu int) error { reader := perf.readers[cpu] if reader == nil { return nil @@ -185,7 +145,6 @@ func (perf *PerfBuffer) closeOnCpu(cpu int) error { if err := perf.table.Remove(unsafe.Pointer(&cpuC)); err != nil { return fmt.Errorf("unable to close perf buffer on CPU: %d, %v", cpu, err) } - delete(perf.readers, cpu) return nil } diff --git a/examples/bcc/perf/perf.go b/examples/bcc/perf/perf.go index 63d37d1..0e835dd 100644 --- a/examples/bcc/perf/perf.go +++ b/examples/bcc/perf/perf.go @@ -134,7 +134,7 @@ func main() { return default: } - time.Sleep(500 * time.Millisecond) + time.Sleep(5 * time.Second) m.PollPerfBuffer("chown_events", 0) } } @@ -145,11 +145,11 @@ func (cb *callback) RawSample(raw []byte, size int32) { var event chownEvent err := binary.Read(bytes.NewBuffer(raw), binary.LittleEndian, &event) if err != nil { - fmt.Printf("failed to decode received data: %s\n", err) + log.Printf("failed to decode received data: %s\n", err) return } filename := (*C.char)(unsafe.Pointer(&event.Filename)) - fmt.Printf("uid %d gid %d pid %d called fchownat(2) on %s (return value: %d)\n", + log.Printf("uid %d gid %d pid %d called fchownat(2) on %s (return value: %d)\n", event.Uid, event.Gid, event.Pid, C.GoString(filename), event.ReturnValue) }