summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/types/fastdecode.go93
-rw-r--r--internal/types/fastdecode_test.go103
2 files changed, 158 insertions, 38 deletions
diff --git a/internal/types/fastdecode.go b/internal/types/fastdecode.go
index 5b0f588..5cb9b64 100644
--- a/internal/types/fastdecode.go
+++ b/internal/types/fastdecode.go
@@ -3,41 +3,58 @@ package types
import "encoding/binary"
const (
- openEventSize = 300
- execEventSize = 304
- nullEventSize = 24
- fdEventSize = 28
- retEventSize = 36
- nameEventSize = 536
- pathEventSize = 280
- fcntlEventSize = 40
- dup3EventSize = 32
- openByHandleAtEventSize = 28
- socketEventSize = 36
- socketpairEventSize = 56
- socketpairEventSizeV1 = 52
- acceptEventSize = 40
- acceptEventSizeV1 = 36
- pipeEventSize = 48
- pipeEventSizeV1 = 44
- eventfdEventSize = 40
- eventfdEventSizeV1 = 36
- epollCtlEventSize = 40
- twoFdEventSize = 40
- pollEventSize = 40
- pollEventSizeV1 = 36
- memEventSize = 56
- sleepEventSize = 32
- keyctlEventSize = 40
- ptraceEventSize = 48
- perfOpenEventSize = 56
+ // Sizes below are the kernel ringbuf payload sizes, i.e. sizeof(struct ...)
+ // from internal/c/types.h compiled for the BPF/LP64 target. The kernel
+ // reserves sizeof(struct) via bpf_ringbuf_reserve, which for structs whose
+ // last field is 32-bit but that also contain a __u64 includes trailing
+ // padding to the struct's 8-byte alignment. The fast path must therefore
+ // gate on sizeof, not on the sum of field widths, or it silently falls back
+ // to the slow binary.Read path on the hot events (open/read/write/ret/...).
+ //
+ // The *V1 constants are the historical, unpadded field-sum sizes still
+ // produced by Go's binary.Write (used by tests and by Bytes()); the fast
+ // decoders accept both so test payloads and kernel payloads both hit the
+ // fast path. The trailing pad bytes carry no data and are ignored.
+ openEventSize = 304
+ openEventSizeV1 = 300
+ execEventSize = 304
+ nullEventSize = 24
+ fdEventSize = 32
+ fdEventSizeV1 = 28
+ retEventSize = 40
+ retEventSizeV1 = 36
+ nameEventSize = 536
+ pathEventSize = 280
+ fcntlEventSize = 40
+ dup3EventSize = 32
+ openByHandleAtEventSize = 32
+ openByHandleAtEventSizeV1 = 28
+ socketEventSize = 40
+ socketEventSizeV1 = 36
+ socketpairEventSize = 56
+ socketpairEventSizeV1 = 52
+ acceptEventSize = 40
+ acceptEventSizeV1 = 36
+ pipeEventSize = 48
+ pipeEventSizeV1 = 44
+ eventfdEventSize = 40
+ eventfdEventSizeV1 = 36
+ epollCtlEventSize = 40
+ twoFdEventSize = 40
+ pollEventSize = 40
+ pollEventSizeV1 = 36
+ memEventSize = 56
+ sleepEventSize = 32
+ keyctlEventSize = 40
+ ptraceEventSize = 48
+ perfOpenEventSize = 56
)
func NewOpenEventFast(raw []byte) *OpenEvent {
- if len(raw) < openEventSize {
+ if len(raw) < openEventSizeV1 {
return nil
}
- if len(raw) != openEventSize {
+ if len(raw) != openEventSize && len(raw) != openEventSizeV1 {
return NewOpenEvent(raw)
}
o := poolOfOpenEvents.Get().(*OpenEvent)
@@ -89,10 +106,10 @@ func NewNullEventFast(raw []byte) *NullEvent {
}
func NewFdEventFast(raw []byte) *FdEvent {
- if len(raw) < fdEventSize {
+ if len(raw) < fdEventSizeV1 {
return nil
}
- if len(raw) != fdEventSize {
+ if len(raw) != fdEventSize && len(raw) != fdEventSizeV1 {
return NewFdEvent(raw)
}
f := poolOfFdEvents.Get().(*FdEvent)
@@ -106,10 +123,10 @@ func NewFdEventFast(raw []byte) *FdEvent {
}
func NewRetEventFast(raw []byte) *RetEvent {
- if len(raw) < retEventSize {
+ if len(raw) < retEventSizeV1 {
return nil
}
- if len(raw) != retEventSize {
+ if len(raw) != retEventSize && len(raw) != retEventSizeV1 {
return NewRetEvent(raw)
}
r := poolOfRetEvents.Get().(*RetEvent)
@@ -196,10 +213,10 @@ func NewDup3EventFast(raw []byte) *Dup3Event {
}
func NewOpenByHandleAtEventFast(raw []byte) *OpenByHandleAtEvent {
- if len(raw) < openByHandleAtEventSize {
+ if len(raw) < openByHandleAtEventSizeV1 {
return nil
}
- if len(raw) != openByHandleAtEventSize {
+ if len(raw) != openByHandleAtEventSize && len(raw) != openByHandleAtEventSizeV1 {
return NewOpenByHandleAtEvent(raw)
}
o := poolOfOpenByHandleAtEvents.Get().(*OpenByHandleAtEvent)
@@ -213,10 +230,10 @@ func NewOpenByHandleAtEventFast(raw []byte) *OpenByHandleAtEvent {
}
func NewSocketEventFast(raw []byte) *SocketEvent {
- if len(raw) < socketEventSize {
+ if len(raw) < socketEventSizeV1 {
return nil
}
- if len(raw) != socketEventSize {
+ if len(raw) != socketEventSize && len(raw) != socketEventSizeV1 {
return NewSocketEvent(raw)
}
s := poolOfSocketEvents.Get().(*SocketEvent)
diff --git a/internal/types/fastdecode_test.go b/internal/types/fastdecode_test.go
index d09f6a9..31ad386 100644
--- a/internal/types/fastdecode_test.go
+++ b/internal/types/fastdecode_test.go
@@ -544,6 +544,109 @@ func TestNewSleepEventFastKernelLayout(t *testing.T) {
}
}
+// fillCommonHeader writes the shared 24-byte event prefix
+// (event_type, trace_id, time, pid, tid) into raw.
+func fillCommonHeader(raw []byte, et EventType, tid TraceId) {
+ binary.LittleEndian.PutUint32(raw[0:4], uint32(et))
+ binary.LittleEndian.PutUint32(raw[4:8], uint32(tid))
+ binary.LittleEndian.PutUint64(raw[8:16], 111)
+ binary.LittleEndian.PutUint32(raw[16:20], 22)
+ binary.LittleEndian.PutUint32(raw[20:24], 33)
+}
+
+// The five tests below feed the *padded* kernel payload (sizeof(struct ...)
+// reserved by bpf_ringbuf_reserve, which Go's binary.Write does NOT emit) and
+// assert the fast path decodes it correctly. They guard against the regression
+// where the fast-size gate used the field-sum size and silently dropped to the
+// slow binary.Read path on the hottest events (open/read/write/ret).
+
+func TestNewOpenEventFastKernelLayout(t *testing.T) {
+ raw := make([]byte, openEventSize) // 304: sizeof(struct open_event)
+ fillCommonHeader(raw, ENTER_OPEN_EVENT, SYS_ENTER_OPENAT)
+ flags := int32(-7)
+ binary.LittleEndian.PutUint32(raw[24:28], uint32(flags))
+ copy(raw[28:284], "hi")
+ copy(raw[284:300], "bash")
+
+ fast := NewOpenEventFast(raw)
+ if fast == nil {
+ t.Fatalf("expected decoded open event for padded kernel payload")
+ }
+ defer fast.Recycle()
+ if fast.Time != 111 || fast.Pid != 22 || fast.Tid != 33 || fast.Flags != -7 ||
+ string(fast.Comm[:4]) != "bash" {
+ t.Fatalf("unexpected open decode: %#v", fast)
+ }
+}
+
+func TestNewFdEventFastKernelLayout(t *testing.T) {
+ raw := make([]byte, fdEventSize) // 32: sizeof(struct fd_event)
+ fillCommonHeader(raw, ENTER_FD_EVENT, SYS_ENTER_READ)
+ binary.LittleEndian.PutUint32(raw[24:28], uint32(int32(9)))
+
+ fast := NewFdEventFast(raw)
+ if fast == nil {
+ t.Fatalf("expected decoded fd event for padded kernel payload")
+ }
+ defer fast.Recycle()
+ if fast.Time != 111 || fast.Pid != 22 || fast.Tid != 33 || fast.Fd != 9 {
+ t.Fatalf("unexpected fd decode: %#v", fast)
+ }
+}
+
+func TestNewRetEventFastKernelLayout(t *testing.T) {
+ raw := make([]byte, retEventSize) // 40: sizeof(struct ret_event)
+ fillCommonHeader(raw, EXIT_RET_EVENT, SYS_EXIT_READ)
+ // ret_event has Ret before Pid/Tid; rewrite pid/tid at their real offsets.
+ ret := int64(-5)
+ binary.LittleEndian.PutUint64(raw[16:24], uint64(ret))
+ binary.LittleEndian.PutUint32(raw[24:28], 22)
+ binary.LittleEndian.PutUint32(raw[28:32], 33)
+ binary.LittleEndian.PutUint32(raw[32:36], READ_CLASSIFIED)
+
+ fast := NewRetEventFast(raw)
+ if fast == nil {
+ t.Fatalf("expected decoded ret event for padded kernel payload")
+ }
+ defer fast.Recycle()
+ if fast.Time != 111 || fast.Ret != -5 || fast.Pid != 22 || fast.Tid != 33 ||
+ fast.RetType != READ_CLASSIFIED {
+ t.Fatalf("unexpected ret decode: %#v", fast)
+ }
+}
+
+func TestNewSocketEventFastKernelLayout(t *testing.T) {
+ raw := make([]byte, socketEventSize) // 40: sizeof(struct socket_event)
+ fillCommonHeader(raw, ENTER_SOCKET_EVENT, SYS_ENTER_SOCKET)
+ binary.LittleEndian.PutUint32(raw[24:28], uint32(int32(2)))
+ binary.LittleEndian.PutUint32(raw[28:32], uint32(int32(1)))
+ binary.LittleEndian.PutUint32(raw[32:36], uint32(int32(6)))
+
+ fast := NewSocketEventFast(raw)
+ if fast == nil {
+ t.Fatalf("expected decoded socket event for padded kernel payload")
+ }
+ defer fast.Recycle()
+ if fast.Time != 111 || fast.Family != 2 || fast.Type != 1 || fast.Protocol != 6 {
+ t.Fatalf("unexpected socket decode: %#v", fast)
+ }
+}
+
+func TestNewOpenByHandleAtEventFastKernelLayout(t *testing.T) {
+ raw := make([]byte, openByHandleAtEventSize) // 32: sizeof(struct open_by_handle_at_event)
+ fillCommonHeader(raw, ENTER_OPEN_BY_HANDLE_AT_EVENT, SYS_ENTER_OPEN_BY_HANDLE_AT)
+ binary.LittleEndian.PutUint32(raw[24:28], uint32(int32(3)))
+
+ fast := NewOpenByHandleAtEventFast(raw)
+ if fast == nil {
+ t.Fatalf("expected decoded open_by_handle_at event for padded kernel payload")
+ }
+ defer fast.Recycle()
+ if fast.Time != 111 || fast.Pid != 22 || fast.Tid != 33 || fast.Flags != 3 {
+ t.Fatalf("unexpected open_by_handle_at decode: %#v", fast)
+ }
+}
+
func TestFastDecodersReturnNilOnShortPayload(t *testing.T) {
cases := []struct {
name string