diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..671df269 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,9 @@ +# Bolt's Journal + +## 2025-02-23 - [eBPF Event Deserialization Strategy] +**Learning:** The `tcpEvent` struct has a size mismatch (102 bytes raw vs 104 bytes Go struct) preventing `unsafe.Pointer` casting. `binary.Read` must be used for this specific struct. +**Action:** When optimizing `ebpftracer` events, check struct alignment carefully. For `l7Event`, `procEvent`, and `fileEvent`, padding is added to match C layout, allowing zero-copy parsing. + +## 2025-02-23 - [Large Event Deserialization] +**Learning:** `l7Event` is large (>8KB). `binary.Read` is extremely slow (~100k ns/op) compared to unsafe cast (~1.2 ns/op). +**Action:** Always prefer `unsafe.Pointer` cast for large structs where memory layout matches. diff --git a/ebpftracer/tracer.go b/ebpftracer/tracer.go index 160aecb8..a68f2787 100644 --- a/ebpftracer/tracer.go +++ b/ebpftracer/tracer.go @@ -14,6 +14,7 @@ import ( "strconv" "strings" "time" + "unsafe" "github.com/cilium/ebpf" "github.com/cilium/ebpf/link" @@ -432,13 +433,11 @@ func runEventsReader(name string, r *perf.Reader, ch chan<- Event, typ perfMapTy switch typ { case perfMapTypeL7Events: - v := &l7Event{} - data := rec.RawSample - - if err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, v); err != nil { - klog.Warningln("failed to read l7 event:", err) + if len(rec.RawSample) < int(unsafe.Sizeof(l7Event{})) { + klog.Warningln("l7 event too short:", len(rec.RawSample)) continue } + v := (*l7Event)(unsafe.Pointer(&rec.RawSample[0])) // Extract payload data directly from the struct arrays payloadSize := min(int(v.PayloadSize), len(v.Payload)) @@ -471,18 +470,18 @@ func runEventsReader(name string, r *perf.Reader, ch chan<- Event, typ perfMapTy L7Request: req, } case perfMapTypeFileEvents: - v := &fileEvent{} - if err := binary.Read(bytes.NewBuffer(rec.RawSample), binary.LittleEndian, v); err != nil { - klog.Warningln("failed to read file event:", err) + if len(rec.RawSample) < int(unsafe.Sizeof(fileEvent{})) { + klog.Warningln("file event too short:", len(rec.RawSample)) continue } + v := (*fileEvent)(unsafe.Pointer(&rec.RawSample[0])) event = Event{Type: v.Type, Pid: v.Pid, Fd: v.Fd, Mnt: v.Mnt, Log: v.Log > 0} case perfMapTypeProcEvents: - v := &procEvent{} - if err := binary.Read(bytes.NewBuffer(rec.RawSample), binary.LittleEndian, v); err != nil { - klog.Warningln("failed to read proc event:", err) + if len(rec.RawSample) < int(unsafe.Sizeof(procEvent{})) { + klog.Warningln("proc event too short:", len(rec.RawSample)) continue } + v := (*procEvent)(unsafe.Pointer(&rec.RawSample[0])) event = Event{Type: v.Type, Reason: EventReason(v.Reason), Pid: v.Pid} case perfMapTypeTCPEvents: v := &tcpEvent{}