diff options
| -rw-r--r-- | cmd/ioworkload/scenario_socket.go | 31 | ||||
| -rw-r--r-- | cmd/ioworkload/scenarios.go | 2 | ||||
| -rw-r--r-- | integrationtests/socket_test.go | 21 | ||||
| -rw-r--r-- | internal/bench_components_test.go | 2 | ||||
| -rw-r--r-- | internal/c/generated_tracepoints.c | 25 | ||||
| -rw-r--r-- | internal/c/generated_tracepoints_result.txt | 4 | ||||
| -rw-r--r-- | internal/c/types.h | 28 | ||||
| -rw-r--r-- | internal/event/interface_assertions.go | 6 | ||||
| -rw-r--r-- | internal/eventloop_exit.go | 53 | ||||
| -rw-r--r-- | internal/eventloop_runtime.go | 18 | ||||
| -rw-r--r-- | internal/eventloop_socket_test.go | 79 | ||||
| -rw-r--r-- | internal/generate/bpfhandler.go | 12 | ||||
| -rw-r--r-- | internal/generate/classify.go | 6 | ||||
| -rw-r--r-- | internal/generate/classify_test.go | 17 | ||||
| -rw-r--r-- | internal/generate/codegen_test.go | 28 | ||||
| -rw-r--r-- | internal/generate/kindregistry.go | 2 | ||||
| -rw-r--r-- | internal/generate/testdata.go | 31 | ||||
| -rw-r--r-- | internal/types/fastdecode.go | 42 | ||||
| -rw-r--r-- | internal/types/fastdecode_test.go | 28 | ||||
| -rw-r--r-- | internal/types/generated_types.go | 146 | ||||
| -rw-r--r-- | internal/types/types_test.go | 58 |
21 files changed, 626 insertions, 13 deletions
diff --git a/cmd/ioworkload/scenario_socket.go b/cmd/ioworkload/scenario_socket.go new file mode 100644 index 0000000..1ffc784 --- /dev/null +++ b/cmd/ioworkload/scenario_socket.go @@ -0,0 +1,31 @@ +package main + +import ( + "fmt" + "syscall" +) + +func socketBasic() error { + fd, err := syscall.Socket(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) + if err != nil { + return fmt.Errorf("socket: %w", err) + } + if err := syscall.Close(fd); err != nil { + return fmt.Errorf("close socket fd: %w", err) + } + return nil +} + +func socketpairBasic() error { + fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0) + if err != nil { + return fmt.Errorf("socketpair: %w", err) + } + if err := syscall.Close(fds[0]); err != nil { + return fmt.Errorf("close socketpair fd0: %w", err) + } + if err := syscall.Close(fds[1]); err != nil { + return fmt.Errorf("close socketpair fd1: %w", err) + } + return nil +} diff --git a/cmd/ioworkload/scenarios.go b/cmd/ioworkload/scenarios.go index 326c954..aedbffa 100644 --- a/cmd/ioworkload/scenarios.go +++ b/cmd/ioworkload/scenarios.go @@ -25,6 +25,8 @@ var scenarios = map[string]func() error{ "readwrite-pread-invalid": readwritePreadInvalid, "readwrite-pwrite-invalid": readwritePwriteInvalid, "retbytes-phase-a": retbytesPhaseA, + "socket-basic": socketBasic, + "socketpair-basic": socketpairBasic, "family-mixed": familyMixed, "close-basic": closeBasic, "close-range": closeRange, diff --git a/integrationtests/socket_test.go b/integrationtests/socket_test.go new file mode 100644 index 0000000..a48d628 --- /dev/null +++ b/integrationtests/socket_test.go @@ -0,0 +1,21 @@ +package integrationtests + +import "testing" + +func TestSocketBasic(t *testing.T) { + runScenario(t, "socket-basic", []ExpectedEvent{ + { + Tracepoint: "enter_socket", + MinCount: 1, + }, + }) +} + +func TestSocketpairBasic(t *testing.T) { + runScenario(t, "socketpair-basic", []ExpectedEvent{ + { + Tracepoint: "enter_socketpair", + MinCount: 1, + }, + }) +} diff --git a/internal/bench_components_test.go b/internal/bench_components_test.go index c7b724e..64438b9 100644 --- a/internal/bench_components_test.go +++ b/internal/bench_components_test.go @@ -108,6 +108,8 @@ func BenchmarkRawHandlerLookup(b *testing.B) { types.ENTER_PATH_EVENT, types.ENTER_FCNTL_EVENT, types.ENTER_DUP3_EVENT, + types.ENTER_SOCKET_EVENT, + types.ENTER_SOCKETPAIR_EVENT, } b.ResetTimer() diff --git a/internal/c/generated_tracepoints.c b/internal/c/generated_tracepoints.c index 980f91d..0974d77 100644 --- a/internal/c/generated_tracepoints.c +++ b/internal/c/generated_tracepoints.c @@ -736,22 +736,25 @@ #define SYS_ENTER_RT_SIGRETURN 57 #define SYS_EXIT_RT_SIGRETURN 56 -/// sys_enter_socket is a struct null_event +/// sys_enter_socket is a struct socket_event SEC("tracepoint/syscalls/sys_enter_socket") int handle_sys_enter_socket(struct syscall_trace_enter *ctx) { __u32 pid, tid; if (filter(&pid, &tid)) return 0; - struct null_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct null_event), 0); + struct socket_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct socket_event), 0); if (!ev) return 0; - ev->event_type = ENTER_NULL_EVENT; + ev->event_type = ENTER_SOCKET_EVENT; ev->trace_id = SYS_ENTER_SOCKET; ev->pid = pid; ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); + ev->family = (__s32)ctx->args[0]; + ev->type = (__s32)ctx->args[1]; + ev->protocol = (__s32)ctx->args[2]; bpf_ringbuf_submit(ev, 0); return 0; @@ -780,22 +783,32 @@ int handle_sys_exit_socket(struct syscall_trace_exit *ctx) { return 0; } -/// sys_enter_socketpair is a struct null_event +/// sys_enter_socketpair is a struct socketpair_event SEC("tracepoint/syscalls/sys_enter_socketpair") int handle_sys_enter_socketpair(struct syscall_trace_enter *ctx) { __u32 pid, tid; if (filter(&pid, &tid)) return 0; - struct null_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct null_event), 0); + struct socketpair_event *ev = bpf_ringbuf_reserve(&event_map, sizeof(struct socketpair_event), 0); if (!ev) return 0; - ev->event_type = ENTER_NULL_EVENT; + ev->event_type = ENTER_SOCKETPAIR_EVENT; ev->trace_id = SYS_ENTER_SOCKETPAIR; ev->pid = pid; ev->tid = tid; ev->time = bpf_ktime_get_boot_ns(); + int sv[2]; + __builtin_memset(&sv, 0xff, sizeof(sv)); + if (ctx->args[3] != 0) { + bpf_probe_read_user(&sv, sizeof(sv), (void *)ctx->args[3]); + } + ev->family = (__s32)ctx->args[0]; + ev->type = (__s32)ctx->args[1]; + ev->protocol = (__s32)ctx->args[2]; + ev->sv0 = (__s32)sv[0]; + ev->sv1 = (__s32)sv[1]; bpf_ringbuf_submit(ev, 0); return 0; diff --git a/internal/c/generated_tracepoints_result.txt b/internal/c/generated_tracepoints_result.txt index ea4f2d1..2d6e54c 100644 --- a/internal/c/generated_tracepoints_result.txt +++ b/internal/c/generated_tracepoints_result.txt @@ -316,8 +316,8 @@ sys_enter_shutdown is a struct fd_event sys_enter_sigaltstack is a struct null_event sys_enter_signalfd is a struct null_event sys_enter_signalfd4 is a struct null_event -sys_enter_socket is a struct null_event -sys_enter_socketpair is a struct null_event +sys_enter_socket is a struct socket_event +sys_enter_socketpair is a struct socketpair_event sys_enter_splice is a struct null_event sys_enter_statfs is a struct path_event sys_enter_statmount is a struct null_event diff --git a/internal/c/types.h b/internal/c/types.h index 18ffe6a..ddb2dca 100644 --- a/internal/c/types.h +++ b/internal/c/types.h @@ -21,6 +21,10 @@ #define EXIT_DUP3_EVENT 16 #define ENTER_OPEN_BY_HANDLE_AT_EVENT 17 #define EXIT_OPEN_BY_HANDLE_AT_EVENT 18 +#define ENTER_SOCKET_EVENT 19 +#define EXIT_SOCKET_EVENT 20 +#define ENTER_SOCKETPAIR_EVENT 21 +#define EXIT_SOCKETPAIR_EVENT 22 #define UNCLASSIFIED 0 #define READ_CLASSIFIED 1 @@ -114,3 +118,27 @@ struct open_by_handle_at_event { __u32 tid; __s32 flags; }; + +struct socket_event { + __u32 event_type; + __u32 trace_id; + __u64 time; + __u32 pid; + __u32 tid; + __s32 family; + __s32 type; + __s32 protocol; +}; + +struct socketpair_event { + __u32 event_type; + __u32 trace_id; + __u64 time; + __u32 pid; + __u32 tid; + __s32 family; + __s32 type; + __s32 protocol; + __s32 sv0; + __s32 sv1; +}; diff --git a/internal/event/interface_assertions.go b/internal/event/interface_assertions.go index 77a43ea..3dce64d 100644 --- a/internal/event/interface_assertions.go +++ b/internal/event/interface_assertions.go @@ -41,4 +41,10 @@ var ( // *types.OpenByHandleAtEvent carries the mount-fd and flags for // open_by_handle_at syscalls. _ Event = (*types.OpenByHandleAtEvent)(nil) + + // *types.SocketEvent carries socket domain/type/protocol metadata. + _ Event = (*types.SocketEvent)(nil) + + // *types.SocketpairEvent carries socketpair domain/type/protocol metadata. + _ Event = (*types.SocketpairEvent)(nil) ) diff --git a/internal/eventloop_exit.go b/internal/eventloop_exit.go index a0cc675..e13d3c5 100644 --- a/internal/eventloop_exit.go +++ b/internal/eventloop_exit.go @@ -26,6 +26,10 @@ func (e *eventLoop) handleTracepointExit(ep *event.Pair) bool { return e.handleDup3Exit(ep, ev) case *types.OpenByHandleAtEvent: return e.handleOpenByHandleAtExit(ep, ev) + case *types.SocketEvent: + return e.handleSocketExit(ep, ev) + case *types.SocketpairEvent: + return e.handleSocketpairExit(ep, ev) case *types.NullEvent: return e.handleNullExit(ep, ev) case *types.FcntlEvent: @@ -216,6 +220,55 @@ func (e *eventLoop) handleOpenByHandleAtExit(ep *event.Pair, openByHandleEv *typ return true } +func (e *eventLoop) handleSocketExit(ep *event.Pair, socketEv *types.SocketEvent) bool { + retEvent, ok := ep.ExitEv.(*types.RetEvent) + if !ok { + e.recyclePair(ep, "Dropped malformed socket exit event") + return false + } + + if fd := int32(retEvent.Ret); fd >= 0 { + fdFile := file.NewFd(fd, socketDescriptorName(socketEv.Family, socketEv.Type, socketEv.Protocol), -1) + e.fdState().set(fd, fdFile) + ep.File = fdFile + } + ep.Comm = e.comm(socketEv.GetTid()) + return true +} + +func (e *eventLoop) handleSocketpairExit(ep *event.Pair, socketpairEv *types.SocketpairEvent) bool { + retEvent, ok := ep.ExitEv.(*types.RetEvent) + if !ok { + e.recyclePair(ep, "Dropped malformed socketpair exit event") + return false + } + + if retEvent.Ret == 0 { + if socketpairEv.Sv0 >= 0 { + fdFile := file.NewFd(socketpairEv.Sv0, socketDescriptorName(socketpairEv.Family, socketpairEv.Type, socketpairEv.Protocol), -1) + e.fdState().set(socketpairEv.Sv0, fdFile) + ep.File = fdFile + } + if socketpairEv.Sv1 >= 0 { + fdFile := file.NewFd(socketpairEv.Sv1, socketDescriptorName(socketpairEv.Family, socketpairEv.Type, socketpairEv.Protocol), -1) + e.fdState().set(socketpairEv.Sv1, fdFile) + if ep.File == nil { + ep.File = fdFile + } + } + } + ep.Comm = e.comm(socketpairEv.GetTid()) + if !e.Filter().MatchPair(ep) { + ep.Recycle() + return false + } + return true +} + +func socketDescriptorName(family, typ, protocol int32) string { + return fmt.Sprintf("socket:%d:%d:%d", family, typ, protocol) +} + func (e *eventLoop) handleNullExit(ep *event.Pair, nullEv *types.NullEvent) bool { if ep.Is(types.SYS_ENTER_IO_URING_SETUP) { retEvent, ok := ep.ExitEv.(*types.RetEvent) diff --git a/internal/eventloop_runtime.go b/internal/eventloop_runtime.go index c285507..d9acb23 100644 --- a/internal/eventloop_runtime.go +++ b/internal/eventloop_runtime.go @@ -146,6 +146,7 @@ func (e *eventLoop) initRawHandlers() { e.registerRetHandlers() e.registerNamePathHandlers() e.registerMiscHandlers() + e.registerSocketHandlers() } // registerOpenHandlers wires enter/exit handlers for open-family events. @@ -262,6 +263,23 @@ func (e *eventLoop) registerMiscHandlers() { } } +func (e *eventLoop) registerSocketHandlers() { + e.rawHandlers[types.ENTER_SOCKET_EVENT] = func(raw []byte, _ chan<- *event.Pair) { + socketEv, ok := decodeRawEvent(e, types.ENTER_SOCKET_EVENT, raw, types.NewSocketEventFast) + if !ok { + return + } + e.tracepointEntered(socketEv) + } + e.rawHandlers[types.ENTER_SOCKETPAIR_EVENT] = func(raw []byte, _ chan<- *event.Pair) { + socketpairEv, ok := decodeRawEvent(e, types.ENTER_SOCKETPAIR_EVENT, raw, types.NewSocketpairEventFast) + if !ok { + return + } + e.tracepointEntered(socketpairEv) + } +} + func decodeRawEvent[T any](e *eventLoop, eventType types.EventType, raw []byte, decode func([]byte) *T) (*T, bool) { decoded := decode(raw) if decoded == nil { diff --git a/internal/eventloop_socket_test.go b/internal/eventloop_socket_test.go new file mode 100644 index 0000000..6b76813 --- /dev/null +++ b/internal/eventloop_socket_test.go @@ -0,0 +1,79 @@ +package internal + +import ( + "testing" + + "ior/internal/event" + "ior/internal/types" +) + +func TestHandleSocketExitTracksReturnedFd(t *testing.T) { + el := mustNewEventLoop(t, eventLoopConfig{}) + + enter := &types.SocketEvent{ + EventType: types.ENTER_SOCKET_EVENT, + TraceId: types.SYS_ENTER_SOCKET, + Time: 100, + Pid: 42, + Tid: 43, + Family: 1, + Type: 2, + Protocol: 0, + } + exit := &types.RetEvent{ + EventType: types.EXIT_SOCKET_EVENT, + TraceId: types.SYS_EXIT_SOCKET, + Time: 200, + Ret: 55, + Pid: 42, + Tid: 43, + } + ep := &event.Pair{EnterEv: enter, ExitEv: exit} + + if ok := el.handleSocketExit(ep, enter); !ok { + t.Fatal("handleSocketExit returned false") + } + verifyFileDescriptor(t, el, 55, "socket:1:2:0") +} + +func TestHandleSocketpairExitTracksReturnedFds(t *testing.T) { + el := mustNewEventLoop(t, eventLoopConfig{}) + + enter := &types.SocketpairEvent{ + EventType: types.ENTER_SOCKETPAIR_EVENT, + TraceId: types.SYS_ENTER_SOCKETPAIR, + Time: 100, + Pid: 77, + Tid: 78, + Family: 1, + Type: 1, + Protocol: 0, + Sv0: 61, + Sv1: 62, + } + exit := &types.RetEvent{ + EventType: types.EXIT_SOCKETPAIR_EVENT, + TraceId: types.SYS_EXIT_SOCKETPAIR, + Time: 200, + Ret: 0, + Pid: 77, + Tid: 78, + } + ep := &event.Pair{EnterEv: enter, ExitEv: exit} + + if ok := el.handleSocketpairExit(ep, enter); !ok { + t.Fatal("handleSocketpairExit returned false") + } + verifyFileDescriptor(t, el, 61, "socket:1:1:0") + verifyFileDescriptor(t, el, 62, "socket:1:1:0") +} + +func TestInitRawHandlersRegistersSocketEvents(t *testing.T) { + el := mustNewEventLoop(t, eventLoopConfig{}) + if _, ok := el.rawHandlers[types.ENTER_SOCKET_EVENT]; !ok { + t.Fatal("ENTER_SOCKET_EVENT handler is not registered") + } + if _, ok := el.rawHandlers[types.ENTER_SOCKETPAIR_EVENT]; !ok { + t.Fatal("ENTER_SOCKETPAIR_EVENT handler is not registered") + } +} diff --git a/internal/generate/bpfhandler.go b/internal/generate/bpfhandler.go index 549e80b..6c9c314 100644 --- a/internal/generate/bpfhandler.go +++ b/internal/generate/bpfhandler.go @@ -73,6 +73,10 @@ func generateExtra(tp GeneratedTracepoint, isEnter bool) string { return " ev->fd = (__s32)ctx->args[0];\n ev->flags = (__s32)ctx->args[2];\n" case KindOpenByHandleAt: return " ev->flags = (__s32)ctx->args[2];\n" + case KindSocket: + return generateExtraSocket() + case KindSocketpair: + return generateExtraSocketpair() case KindOpen: return generateExtraOpen(f) case KindPathname: @@ -149,6 +153,14 @@ func generateExtraFcntl(f *Format) string { ) } +func generateExtraSocket() string { + return " ev->family = (__s32)ctx->args[0];\n ev->type = (__s32)ctx->args[1];\n ev->protocol = (__s32)ctx->args[2];\n" +} + +func generateExtraSocketpair() string { + return " int sv[2];\n __builtin_memset(&sv, 0xff, sizeof(sv));\n if (ctx->args[3] != 0) {\n bpf_probe_read_user(&sv, sizeof(sv), (void *)ctx->args[3]);\n }\n ev->family = (__s32)ctx->args[0];\n ev->type = (__s32)ctx->args[1];\n ev->protocol = (__s32)ctx->args[2];\n ev->sv0 = (__s32)sv[0];\n ev->sv1 = (__s32)sv[1];\n" +} + // eventStructName returns the C struct name for a TracepointKind. The mapping // is driven by kindRegistry so adding a new kind only requires a registry entry. func eventStructName(kind TracepointKind) string { diff --git a/internal/generate/classify.go b/internal/generate/classify.go index 7768ea7..68cd722 100644 --- a/internal/generate/classify.go +++ b/internal/generate/classify.go @@ -15,6 +15,8 @@ const ( KindNull KindDup3 KindOpenByHandleAt + KindSocket + KindSocketpair ) type RetClassification string @@ -79,6 +81,10 @@ func classifyNameOnly(name string) (ClassificationResult, bool) { return ClassificationResult{Kind: KindNull}, true case "sys_enter_getcwd": return ClassificationResult{Kind: KindNull}, true + case "sys_enter_socket": + return ClassificationResult{Kind: KindSocket}, true + case "sys_enter_socketpair": + return ClassificationResult{Kind: KindSocketpair}, true } if strings.HasPrefix(name, "sys_enter_io_") { return ClassificationResult{Kind: KindNull}, true diff --git a/internal/generate/classify_test.go b/internal/generate/classify_test.go index ea7d662..ce6eff1 100644 --- a/internal/generate/classify_test.go +++ b/internal/generate/classify_test.go @@ -244,10 +244,17 @@ func TestClassifyFdAccept(t *testing.T) { } } -func TestClassifySocketRequiresGenerationFallback(t *testing.T) { +func TestClassifySocket(t *testing.T) { r := classifyFromData(t, FormatSocket) - if r.Kind != KindNone { - t.Errorf("socket: got kind %d, want KindNone before generation fallback", r.Kind) + if r.Kind != KindSocket { + t.Errorf("socket: got kind %d, want KindSocket", r.Kind) + } +} + +func TestClassifySocketpair(t *testing.T) { + r := classifyFromData(t, FormatSocketpair) + if r.Kind != KindSocketpair { + t.Errorf("socketpair: got kind %d, want KindSocketpair", r.Kind) } } @@ -288,7 +295,8 @@ func TestClassifySyscallPairAccepted(t *testing.T) { {"mknod", FormatMknod, FormatExitMknod, KindPathname}, {"execve", FormatExecve, FormatExitExecve, KindPathname}, {"accept", FormatAccept, FormatExitAccept, KindFd}, - {"socket", FormatSocket, FormatExitSocket, KindNull}, + {"socket", FormatSocket, FormatExitSocket, KindSocket}, + {"socketpair", FormatSocketpair, FormatExitSocketpair, KindSocketpair}, {"kill", FormatKill, FormatExitKill, KindNull}, } @@ -314,6 +322,7 @@ func TestClassifySyscallPairEmitsAllFamilies(t *testing.T) { {"execve", FormatExecve, FormatExitExecve, FamilyProcess}, {"accept", FormatAccept, FormatExitAccept, FamilyNetwork}, {"socket", FormatSocket, FormatExitSocket, FamilyNetwork}, + {"socketpair", FormatSocketpair, FormatExitSocketpair, FamilyNetwork}, {"kill", FormatKill, FormatExitKill, FamilySignals}, } diff --git a/internal/generate/codegen_test.go b/internal/generate/codegen_test.go index a448162..f87a6dc 100644 --- a/internal/generate/codegen_test.go +++ b/internal/generate/codegen_test.go @@ -185,6 +185,28 @@ func TestGenerateOpenByHandleAtHandler(t *testing.T) { requireContains(t, output, "ev->flags = (__s32)ctx->args[2];") } +func TestGenerateSocketHandler(t *testing.T) { + output := generateFromPair(t, FormatSocket, FormatExitSocket) + + requireContains(t, output, "struct socket_event *ev") + requireContains(t, output, "ev->event_type = ENTER_SOCKET_EVENT;") + requireContains(t, output, "ev->family = (__s32)ctx->args[0];") + requireContains(t, output, "ev->type = (__s32)ctx->args[1];") + requireContains(t, output, "ev->protocol = (__s32)ctx->args[2];") +} + +func TestGenerateSocketpairHandler(t *testing.T) { + output := generateFromPair(t, FormatSocketpair, FormatExitSocketpair) + + requireContains(t, output, "struct socketpair_event *ev") + requireContains(t, output, "ev->event_type = ENTER_SOCKETPAIR_EVENT;") + requireContains(t, output, "int sv[2];") + requireContains(t, output, "bpf_probe_read_user(&sv, sizeof(sv), (void *)ctx->args[3]);") + requireContains(t, output, "ev->family = (__s32)ctx->args[0];") + requireContains(t, output, "ev->sv0 = (__s32)sv[0];") + requireContains(t, output, "ev->sv1 = (__s32)sv[1];") +} + func TestGenerateNameToHandleAtHandler(t *testing.T) { output := generateFromPair(t, FormatNameToHandleAt, FormatExitNameToHandleAt) @@ -292,6 +314,8 @@ func TestGenerateAllEventTypes(t *testing.T) { {KindNull, "ENTER_NULL_EVENT", "EXIT_NULL_EVENT"}, {KindDup3, "ENTER_DUP3_EVENT", "EXIT_DUP3_EVENT"}, {KindOpenByHandleAt, "ENTER_OPEN_BY_HANDLE_AT_EVENT", "EXIT_OPEN_BY_HANDLE_AT_EVENT"}, + {KindSocket, "ENTER_SOCKET_EVENT", "EXIT_SOCKET_EVENT"}, + {KindSocketpair, "ENTER_SOCKETPAIR_EVENT", "EXIT_SOCKETPAIR_EVENT"}, } for _, tt := range tests { @@ -318,6 +342,8 @@ func TestEventStructNames(t *testing.T) { {KindNull, "null_event"}, {KindDup3, "dup3_event"}, {KindOpenByHandleAt, "open_by_handle_at_event"}, + {KindSocket, "socket_event"}, + {KindSocketpair, "socketpair_event"}, } for _, tt := range tests { @@ -336,7 +362,7 @@ func TestEnterReject(t *testing.T) { t.Error("KindNone should be enter-rejected") } - accepted := []TracepointKind{KindFd, KindOpen, KindPathname, KindName, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt} + accepted := []TracepointKind{KindFd, KindOpen, KindPathname, KindName, KindFcntl, KindNull, KindDup3, KindOpenByHandleAt, KindSocket, KindSocketpair} for _, k := range accepted { if isEnterRejected(k) { t.Errorf("kind %d should NOT be enter-rejected", k) diff --git a/internal/generate/kindregistry.go b/internal/generate/kindregistry.go index 86b77aa..f716489 100644 --- a/internal/generate/kindregistry.go +++ b/internal/generate/kindregistry.go @@ -25,6 +25,8 @@ var kindRegistry = map[TracepointKind]kindMeta{ KindNull: {structName: "null_event", enterAccepted: true}, KindDup3: {structName: "dup3_event", enterAccepted: true}, KindOpenByHandleAt: {structName: "open_by_handle_at_event", enterAccepted: true}, + KindSocket: {structName: "socket_event", enterAccepted: true}, + KindSocketpair: {structName: "socketpair_event", enterAccepted: true}, // KindNone is intentionally absent: it represents "unclassified" and is // never enter-accepted. lookupKind returns the zero kindMeta (enterAccepted=false) // for any unregistered kind, so KindNone is implicitly rejected. diff --git a/internal/generate/testdata.go b/internal/generate/testdata.go index a9bedc6..e363a68 100644 --- a/internal/generate/testdata.go +++ b/internal/generate/testdata.go @@ -823,6 +823,37 @@ format: print fmt: "0x%lx", REC->ret ` +const FormatSocketpair = `name: sys_enter_socketpair +ID: 1816 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:int family; offset:16; size:8; signed:0; + field:int type; offset:24; size:8; signed:0; + field:int protocol; offset:32; size:8; signed:0; + field:int * usockvec; offset:40; size:8; signed:0; + +print fmt: "family: 0x%08lx, type: 0x%08lx, protocol: 0x%08lx, usockvec: 0x%08lx", ((unsigned long)(REC->family)), ((unsigned long)(REC->type)), ((unsigned long)(REC->protocol)), ((unsigned long)(REC->usockvec)) +` + +const FormatExitSocketpair = `name: sys_exit_socketpair +ID: 1815 +format: + field:unsigned short common_type; offset:0; size:2; signed:0; + field:unsigned char common_flags; offset:2; size:1; signed:0; + field:unsigned char common_preempt_count; offset:3; size:1; signed:0; + field:int common_pid; offset:4; size:4; signed:1; + + field:int __syscall_nr; offset:8; size:4; signed:1; + field:long ret; offset:16; size:8; signed:1; + +print fmt: "0x%lx", REC->ret +` + const FormatPread64 = `name: sys_enter_pread64 ID: 840 format: diff --git a/internal/types/fastdecode.go b/internal/types/fastdecode.go index a0ce5f1..b464013 100644 --- a/internal/types/fastdecode.go +++ b/internal/types/fastdecode.go @@ -12,6 +12,8 @@ const ( fcntlEventSize = 40 dup3EventSize = 32 openByHandleAtEventSize = 28 + socketEventSize = 36 + socketpairEventSize = 44 ) func NewOpenEventFast(raw []byte) *OpenEvent { @@ -172,3 +174,43 @@ func NewOpenByHandleAtEventFast(raw []byte) *OpenByHandleAtEvent { o.Flags = int32(binary.LittleEndian.Uint32(raw[24:28])) return o } + +func NewSocketEventFast(raw []byte) *SocketEvent { + if len(raw) < socketEventSize { + return nil + } + if len(raw) != socketEventSize { + return NewSocketEvent(raw) + } + s := poolOfSocketEvents.Get().(*SocketEvent) + s.EventType = EventType(binary.LittleEndian.Uint32(raw[0:4])) + s.TraceId = TraceId(binary.LittleEndian.Uint32(raw[4:8])) + s.Time = binary.LittleEndian.Uint64(raw[8:16]) + s.Pid = binary.LittleEndian.Uint32(raw[16:20]) + s.Tid = binary.LittleEndian.Uint32(raw[20:24]) + s.Family = int32(binary.LittleEndian.Uint32(raw[24:28])) + s.Type = int32(binary.LittleEndian.Uint32(raw[28:32])) + s.Protocol = int32(binary.LittleEndian.Uint32(raw[32:36])) + return s +} + +func NewSocketpairEventFast(raw []byte) *SocketpairEvent { + if len(raw) < socketpairEventSize { + return nil + } + if len(raw) != socketpairEventSize { + return NewSocketpairEvent(raw) + } + s := poolOfSocketpairEvents.Get().(*SocketpairEvent) + s.EventType = EventType(binary.LittleEndian.Uint32(raw[0:4])) + s.TraceId = TraceId(binary.LittleEndian.Uint32(raw[4:8])) + s.Time = binary.LittleEndian.Uint64(raw[8:16]) + s.Pid = binary.LittleEndian.Uint32(raw[16:20]) + s.Tid = binary.LittleEndian.Uint32(raw[20:24]) + s.Family = int32(binary.LittleEndian.Uint32(raw[24:28])) + s.Type = int32(binary.LittleEndian.Uint32(raw[28:32])) + s.Protocol = int32(binary.LittleEndian.Uint32(raw[32:36])) + s.Sv0 = int32(binary.LittleEndian.Uint32(raw[36:40])) + s.Sv1 = int32(binary.LittleEndian.Uint32(raw[40:44])) + return s +} diff --git a/internal/types/fastdecode_test.go b/internal/types/fastdecode_test.go index f40faeb..5279130 100644 --- a/internal/types/fastdecode_test.go +++ b/internal/types/fastdecode_test.go @@ -124,6 +124,32 @@ func TestFastDecodersMatchGeneratedDecoders(t *testing.T) { t.Fatalf("open_by_handle_at decode mismatch") } }) + + t.Run("SocketEvent", func(t *testing.T) { + ev := &SocketEvent{EventType: ENTER_SOCKET_EVENT, TraceId: SYS_ENTER_SOCKET, Time: 1, Pid: 2, Tid: 3, Family: 1, Type: 2, Protocol: 3} + raw, _ := ev.Bytes() + + slow := NewSocketEvent(raw) + fast := NewSocketEventFast(raw) + defer slow.Recycle() + defer fast.Recycle() + if !slow.Equals(fast) { + t.Fatalf("socket decode mismatch") + } + }) + + t.Run("SocketpairEvent", func(t *testing.T) { + ev := &SocketpairEvent{EventType: ENTER_SOCKETPAIR_EVENT, TraceId: SYS_ENTER_SOCKETPAIR, Time: 1, Pid: 2, Tid: 3, Family: 1, Type: 2, Protocol: 0, Sv0: 10, Sv1: 11} + raw, _ := ev.Bytes() + + slow := NewSocketpairEvent(raw) + fast := NewSocketpairEventFast(raw) + defer slow.Recycle() + defer fast.Recycle() + if !slow.Equals(fast) { + t.Fatalf("socketpair decode mismatch") + } + }) } func TestFastDecodersReturnNilOnShortPayload(t *testing.T) { @@ -140,6 +166,8 @@ func TestFastDecodersReturnNilOnShortPayload(t *testing.T) { {name: "FcntlEvent", decode: func(raw []byte) bool { return NewFcntlEventFast(raw) == nil }}, {name: "Dup3Event", decode: func(raw []byte) bool { return NewDup3EventFast(raw) == nil }}, {name: "OpenByHandleAtEvent", decode: func(raw []byte) bool { return NewOpenByHandleAtEventFast(raw) == nil }}, + {name: "SocketEvent", decode: func(raw []byte) bool { return NewSocketEventFast(raw) == nil }}, + {name: "SocketpairEvent", decode: func(raw []byte) bool { return NewSocketpairEventFast(raw) == nil }}, } for _, tc := range cases { diff --git a/internal/types/generated_types.go b/internal/types/generated_types.go index 7eeac21..fb4dd36 100644 --- a/internal/types/generated_types.go +++ b/internal/types/generated_types.go @@ -86,6 +86,10 @@ const ENTER_DUP3_EVENT = 15 const EXIT_DUP3_EVENT = 16 const ENTER_OPEN_BY_HANDLE_AT_EVENT = 17 const EXIT_OPEN_BY_HANDLE_AT_EVENT = 18 +const ENTER_SOCKET_EVENT = 19 +const EXIT_SOCKET_EVENT = 20 +const ENTER_SOCKETPAIR_EVENT = 21 +const EXIT_SOCKETPAIR_EVENT = 22 const UNCLASSIFIED = 0 const READ_CLASSIFIED = 1 const WRITE_CLASSIFIED = 2 @@ -1442,3 +1446,145 @@ func (o *OpenByHandleAtEvent) Bytes() ([]byte, error) { func (o *OpenByHandleAtEvent) Recycle() { poolOfOpenByHandleAtEvents.Put(o) } + +type SocketEvent struct { + EventType EventType + TraceId TraceId + Time uint64 + Pid uint32 + Tid uint32 + Family int32 + Type int32 + Protocol int32 +} + +func (s SocketEvent) String() string { + return fmt.Sprintf("EventType:%v TraceId:%v Time:%v Pid:%v Tid:%v Family:%v Type:%v Protocol:%v", s.EventType, s.TraceId, s.Time, s.Pid, s.Tid, s.Family, s.Type, s.Protocol) +} + +func (s SocketEvent) Equals(other any) bool { + otherConcrete, ok := other.(*SocketEvent) + if !ok { + return false + } + return s.EventType == otherConcrete.EventType && s.TraceId == otherConcrete.TraceId && s.Time == otherConcrete.Time && s.Pid == otherConcrete.Pid && s.Tid == otherConcrete.Tid && s.Family == otherConcrete.Family && s.Type == otherConcrete.Type && s.Protocol == otherConcrete.Protocol +} + +func (s *SocketEvent) GetEventType() EventType { + return s.EventType +} + +func (s *SocketEvent) GetTraceId() TraceId { + return s.TraceId +} + +func (s *SocketEvent) GetPid() uint32 { + return s.Pid +} + +func (s *SocketEvent) GetTid() uint32 { + return s.Tid +} + +func (s *SocketEvent) GetTime() uint64 { + return s.Time +} + +var poolOfSocketEvents = sync.Pool{ + New: func() any { return &SocketEvent{} }, +} + +func NewSocketEvent(raw []byte) *SocketEvent { + s := poolOfSocketEvents.Get().(*SocketEvent) + if err := binary.Read(bytes.NewReader(raw), binary.LittleEndian, s); err != nil { + *s = SocketEvent{} + poolOfSocketEvents.Put(s) + return nil + } + return s +} + +func (s *SocketEvent) Bytes() ([]byte, error) { + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.LittleEndian, s) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func (s *SocketEvent) Recycle() { + poolOfSocketEvents.Put(s) +} + +type SocketpairEvent struct { + EventType EventType + TraceId TraceId + Time uint64 + Pid uint32 + Tid uint32 + Family int32 + Type int32 + Protocol int32 + Sv0 int32 + Sv1 int32 +} + +func (s SocketpairEvent) String() string { + return fmt.Sprintf("EventType:%v TraceId:%v Time:%v Pid:%v Tid:%v Family:%v Type:%v Protocol:%v Sv0:%v Sv1:%v", s.EventType, s.TraceId, s.Time, s.Pid, s.Tid, s.Family, s.Type, s.Protocol, s.Sv0, s.Sv1) +} + +func (s SocketpairEvent) Equals(other any) bool { + otherConcrete, ok := other.(*SocketpairEvent) + if !ok { + return false + } + return s.EventType == otherConcrete.EventType && s.TraceId == otherConcrete.TraceId && s.Time == otherConcrete.Time && s.Pid == otherConcrete.Pid && s.Tid == otherConcrete.Tid && s.Family == otherConcrete.Family && s.Type == otherConcrete.Type && s.Protocol == otherConcrete.Protocol && s.Sv0 == otherConcrete.Sv0 && s.Sv1 == otherConcrete.Sv1 +} + +func (s *SocketpairEvent) GetEventType() EventType { + return s.EventType +} + +func (s *SocketpairEvent) GetTraceId() TraceId { + return s.TraceId +} + +func (s *SocketpairEvent) GetPid() uint32 { + return s.Pid +} + +func (s *SocketpairEvent) GetTid() uint32 { + return s.Tid +} + +func (s *SocketpairEvent) GetTime() uint64 { + return s.Time +} + +var poolOfSocketpairEvents = sync.Pool{ + New: func() any { return &SocketpairEvent{} }, +} + +func NewSocketpairEvent(raw []byte) *SocketpairEvent { + s := poolOfSocketpairEvents.Get().(*SocketpairEvent) + if err := binary.Read(bytes.NewReader(raw), binary.LittleEndian, s); err != nil { + *s = SocketpairEvent{} + poolOfSocketpairEvents.Put(s) + return nil + } + return s +} + +func (s *SocketpairEvent) Bytes() ([]byte, error) { + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.LittleEndian, s) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func (s *SocketpairEvent) Recycle() { + poolOfSocketpairEvents.Put(s) +} diff --git a/internal/types/types_test.go b/internal/types/types_test.go index 8ba7367..9e11baa 100644 --- a/internal/types/types_test.go +++ b/internal/types/types_test.go @@ -130,6 +130,64 @@ func TestNullEventSerialization(t *testing.T) { t.Log("NullEvent could be serialized correctly") } +func TestSocketEventSerialization(t *testing.T) { + socketEv1 := SocketEvent{ + EventType: ENTER_SOCKET_EVENT, + TraceId: SYS_ENTER_SOCKET, + Time: 1234, + Pid: 30, + Tid: 31, + Family: 1, + Type: 2, + Protocol: 0, + } + bytes, err := socketEv1.Bytes() + if err != nil { + t.Error(err) + } + socketEv2 := NewSocketEvent(bytes) + + assertEquals(t, socketEv1.EventType, socketEv2.EventType) + assertEquals(t, socketEv1.TraceId, socketEv2.TraceId) + assertEquals(t, socketEv1.Time, socketEv2.Time) + assertEquals(t, socketEv1.Pid, socketEv2.Pid) + assertEquals(t, socketEv1.Tid, socketEv2.Tid) + assertEquals(t, socketEv1.Family, socketEv2.Family) + assertEquals(t, socketEv1.Type, socketEv2.Type) + assertEquals(t, socketEv1.Protocol, socketEv2.Protocol) +} + +func TestSocketpairEventSerialization(t *testing.T) { + socketpairEv1 := SocketpairEvent{ + EventType: ENTER_SOCKETPAIR_EVENT, + TraceId: SYS_ENTER_SOCKETPAIR, + Time: 2345, + Pid: 32, + Tid: 33, + Family: 1, + Type: 1, + Protocol: 0, + Sv0: 42, + Sv1: 43, + } + bytes, err := socketpairEv1.Bytes() + if err != nil { + t.Error(err) + } + socketpairEv2 := NewSocketpairEvent(bytes) + + assertEquals(t, socketpairEv1.EventType, socketpairEv2.EventType) + assertEquals(t, socketpairEv1.TraceId, socketpairEv2.TraceId) + assertEquals(t, socketpairEv1.Time, socketpairEv2.Time) + assertEquals(t, socketpairEv1.Pid, socketpairEv2.Pid) + assertEquals(t, socketpairEv1.Tid, socketpairEv2.Tid) + assertEquals(t, socketpairEv1.Family, socketpairEv2.Family) + assertEquals(t, socketpairEv1.Type, socketpairEv2.Type) + assertEquals(t, socketpairEv1.Protocol, socketpairEv2.Protocol) + assertEquals(t, socketpairEv1.Sv0, socketpairEv2.Sv0) + assertEquals(t, socketpairEv1.Sv1, socketpairEv2.Sv1) +} + func TestEqualsDifferentTypes(t *testing.T) { openEv := OpenEvent{EventType: ENTER_OPEN_EVENT, TraceId: SYS_ENTER_OPENAT, Time: 1, Pid: 1, Tid: 1} nullEv := NullEvent{EventType: ENTER_NULL_EVENT, TraceId: SYS_ENTER_SYNC, Time: 1, Pid: 1, Tid: 1} |
