diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/eventloop.go | 9 | ||||
| -rw-r--r-- | internal/eventloop_error_handling_test.go | 24 | ||||
| -rw-r--r-- | internal/eventloop_exit.go | 198 | ||||
| -rw-r--r-- | internal/eventloop_kinds.go | 141 | ||||
| -rw-r--r-- | internal/eventloop_kinds_test.go | 158 | ||||
| -rw-r--r-- | internal/eventloop_runtime.go | 296 |
6 files changed, 390 insertions, 436 deletions
diff --git a/internal/eventloop.go b/internal/eventloop.go index 71c7982..a29d99f 100644 --- a/internal/eventloop.go +++ b/internal/eventloop.go @@ -62,6 +62,7 @@ type eventLoop struct { commResolver *commResolver outputFormatter // pair-emission and warning-notification callbacks (embedded collaborator) rawHandlers map[types.EventType]rawEventHandler + exitHandlers map[types.EventType]runtimeExitHandler cfg eventLoopConfig aggregateSink syscallAggregateSink aggregateSrc syscallAggregateSource @@ -110,15 +111,17 @@ func newEventLoop(cfg eventLoopConfig) (*eventLoop, error) { outputFormatter: outputFormatter{ printCb: func(ep *event.Pair) { fmt.Println(ep); ep.Recycle() }, }, - rawHandlers: make(map[types.EventType]rawEventHandler), - cfg: cfg, - done: make(chan struct{}), + rawHandlers: make(map[types.EventType]rawEventHandler), + exitHandlers: make(map[types.EventType]runtimeExitHandler), + cfg: cfg, + done: make(chan struct{}), } if el.cfg.aggregateDrainEvery <= 0 { el.cfg.aggregateDrainEvery = defaultAggregateDrainEvery } el.SetFilter(cfg.filter) el.initRawHandlers() + el.initRuntimeEventKinds() el.configureOutputCallback() el.seedTrackedPidComm() return el, nil diff --git a/internal/eventloop_error_handling_test.go b/internal/eventloop_error_handling_test.go index 0851ff6..4ac5525 100644 --- a/internal/eventloop_error_handling_test.go +++ b/internal/eventloop_error_handling_test.go @@ -146,28 +146,8 @@ func TestProcessRawEventMalformedKnownTypeDoesNotPanicAndNotifies(t *testing.T) } } -func TestDecodeRawEventMalformedNotifies(t *testing.T) { - el := mustNewEventLoop(t, eventLoopConfig{}) - warnings := make(chan string, 1) - el.warningCb = func(message string) { warnings <- message } - - decoded, ok := decodeRawEvent(el, types.ENTER_OPEN_EVENT, []byte{byte(types.ENTER_OPEN_EVENT)}, types.NewOpenEventFast) - if ok || decoded != nil { - t.Fatalf("expected malformed raw event decode to fail, got ok=%v decoded=%v", ok, decoded) - } - - select { - case msg := <-warnings: - if msg == "" { - t.Fatalf("expected non-empty warning message") - } - default: - t.Fatalf("expected warning notification") - } -} - -// unknownEvent is a minimal event.Event stub used to exercise the default -// branch of handleTracepointExit (an enter-event type not covered by any case). +// unknownEvent is a minimal event.Event stub used to exercise the missing +// runtime-kind path in handleTracepointExit. type unknownEvent struct{} func (unknownEvent) String() string { return "unknownEvent" } diff --git a/internal/eventloop_exit.go b/internal/eventloop_exit.go index 1212de6..cb11074 100644 --- a/internal/eventloop_exit.go +++ b/internal/eventloop_exit.go @@ -10,58 +10,41 @@ import ( "ior/internal/types" ) -// handleTracepointExit routes a completed enter/exit pair to the appropriate -// handler using a type switch, avoiding reflection on the hot event path. +func (e *eventLoop) initRuntimeEventKinds() { + if e.exitHandlers == nil { + e.exitHandlers = make(map[types.EventType]runtimeExitHandler) + } + if len(e.exitHandlers) != 0 { + return + } + for _, kind := range runtimeEventKinds() { + e.exitHandlers[kind.enterEventType] = kind.exit + } +} + +// handleTracepointExit routes a completed enter/exit pair to the runtime +// handler registered for the enter event kind. func (e *eventLoop) handleTracepointExit(ep *event.Pair) bool { - switch ev := ep.EnterEv.(type) { - case *types.OpenEvent: - return e.handleOpenExit(ep, ev) - case *types.ExecEvent: - return e.handleExecExit(ep, ev) - case *types.NameEvent: - return e.handleNameExit(ep, ev) - case *types.PathEvent: - return e.handlePathExit(ep, ev) - case *types.FdEvent: - return e.handleFdExit(ep, ev) - case *types.Dup3Event: - 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.AcceptEvent: - return e.handleAcceptExit(ep, ev) - case *types.PipeEvent: - return e.handlePipeExit(ep, ev) - case *types.EventfdEvent: - return e.handleEventfdExit(ep, ev) - case *types.EpollCtlEvent: - return e.handleEpollCtlExit(ep, ev) - case *types.PollEvent: - return e.handlePollExit(ep, ev) - case *types.TwoFdEvent: - return e.handleTwoFdExit(ep, ev) - case *types.MemEvent: - return e.handleMemExit(ep, ev) - case *types.SleepEvent: - return e.handleSleepExit(ep, ev) - case *types.KeyctlEvent: - return e.handleKeyctlExit(ep, ev) - case *types.PtraceEvent: - return e.handlePtraceExit(ep, ev) - case *types.PerfOpenEvent: - return e.handlePerfOpenExit(ep, ev) - case *types.NullEvent: - return e.handleNullExit(ep, ev) - case *types.FcntlEvent: - return e.handleFcntlExit(ep, ev) - default: + e.initRuntimeEventKinds() + eventType, ok := eventTypeForRuntimeEvent(ep.EnterEv) + if !ok { + e.recyclePair(ep, "Dropped malformed enter event") + return false + } + handler, ok := e.exitHandlers[eventType] + if !ok { e.recyclePair(ep, "Dropped malformed enter event") return false } + return handler(e, ep) +} + +func eventTypeForRuntimeEvent(ev event.Event) (types.EventType, bool) { + typed, ok := ev.(interface{ GetEventType() types.EventType }) + if !ok { + return 0, false + } + return typed.GetEventType(), true } func (e *eventLoop) handleOpenExit(ep *event.Pair, openEv *types.OpenEvent) bool { @@ -94,11 +77,7 @@ func (e *eventLoop) handleExecExit(ep *event.Pair, execEv *types.ExecEvent) bool ep.Comm = comm ep.File = file.NewPathname(execEv.Filename[:]) e.setCachedComm(execEv.Tid, comm) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPair(ep) } func (e *eventLoop) handleNameExit(ep *event.Pair, nameEv *types.NameEvent) bool { @@ -146,8 +125,7 @@ func (e *eventLoop) handleFdExit(ep *event.Pair, fdEv *types.FdEvent) bool { ep.File = e.fdState().resolve(fd, fdEv.Pid) e.applyFdCloseState(ep, fd, fdEv.Pid) ep.Comm = e.comm(fdEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() + if !e.finishPair(ep) { return false } if ok := e.applyFdTransferOp(ep, fdEv); !ok { @@ -209,8 +187,7 @@ func (e *eventLoop) handleDup3Exit(ep *event.Pair, dup3Ev *types.Dup3Event) bool fd := int32(dup3Ev.Fd) ep.File = e.fdState().resolve(fd, dup3Ev.Pid) ep.Comm = e.comm(dup3Ev.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() + if !e.finishPair(ep) { return false } @@ -273,11 +250,7 @@ func (e *eventLoop) handleSocketExit(ep *event.Pair, socketEv *types.SocketEvent ep.File = fdFile } ep.Comm = e.comm(socketEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPair(ep) } func (e *eventLoop) handleSocketpairExit(ep *event.Pair, socketpairEv *types.SocketpairEvent) bool { @@ -315,11 +288,7 @@ func (e *eventLoop) handleSocketpairExit(ep *event.Pair, socketpairEv *types.Soc } } ep.Comm = e.comm(socketpairEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPair(ep) } func (e *eventLoop) handleAcceptExit(ep *event.Pair, acceptEv *types.AcceptEvent) bool { @@ -338,11 +307,7 @@ func (e *eventLoop) handleAcceptExit(ep *event.Pair, acceptEv *types.AcceptEvent ep.File = listening } ep.Comm = e.comm(acceptEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPair(ep) } func socketDescriptorName(family, typ, protocol int32) string { @@ -386,11 +351,7 @@ func (e *eventLoop) handlePipeExit(ep *event.Pair, pipeEv *types.PipeEvent) bool } } ep.Comm = e.comm(pipeEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPair(ep) } func (e *eventLoop) handleEventfdExit(ep *event.Pair, eventfdEv *types.EventfdEvent) bool { @@ -410,76 +371,37 @@ func (e *eventLoop) handleEventfdExit(ep *event.Pair, eventfdEv *types.EventfdEv ep.File = fdFile } ep.Comm = e.comm(eventfdEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPair(ep) } func (e *eventLoop) handleEpollCtlExit(ep *event.Pair, epollCtlEv *types.EpollCtlEvent) bool { ep.File = e.fdState().resolve(epollCtlEv.Epfd, epollCtlEv.Pid) - ep.Comm = e.comm(epollCtlEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPairForTid(ep, epollCtlEv.GetTid()) } func (e *eventLoop) handlePollExit(ep *event.Pair, pollEv *types.PollEvent) bool { - ep.Comm = e.comm(pollEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPairForTid(ep, pollEv.GetTid()) } func (e *eventLoop) handleTwoFdExit(ep *event.Pair, twoFdEv *types.TwoFdEvent) bool { ep.File = e.fdState().resolve(twoFdEv.FdA, twoFdEv.Pid) - ep.Comm = e.comm(twoFdEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPairForTid(ep, twoFdEv.GetTid()) } func (e *eventLoop) handleMemExit(ep *event.Pair, memEv *types.MemEvent) bool { - ep.Comm = e.comm(memEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPairForTid(ep, memEv.GetTid()) } func (e *eventLoop) handleSleepExit(ep *event.Pair, sleepEv *types.SleepEvent) bool { - ep.Comm = e.comm(sleepEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPairForTid(ep, sleepEv.GetTid()) } func (e *eventLoop) handleKeyctlExit(ep *event.Pair, keyctlEv *types.KeyctlEvent) bool { - ep.Comm = e.comm(keyctlEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPairForTid(ep, keyctlEv.GetTid()) } func (e *eventLoop) handlePtraceExit(ep *event.Pair, ptraceEv *types.PtraceEvent) bool { - ep.Comm = e.comm(ptraceEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPairForTid(ep, ptraceEv.GetTid()) } func (e *eventLoop) handlePerfOpenExit(ep *event.Pair, perfOpenEv *types.PerfOpenEvent) bool { @@ -495,11 +417,7 @@ func (e *eventLoop) handlePerfOpenExit(ep *event.Pair, perfOpenEv *types.PerfOpe ep.File = fdFile } ep.Comm = e.comm(perfOpenEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPair(ep) } func pipeDescriptorName(flags, fd0, fd1 int32) string { @@ -576,19 +494,14 @@ func (e *eventLoop) handleNullExit(ep *event.Pair, nullEv *types.NullEvent) bool } } ep.Comm = e.comm(nullEv.GetTid()) - if !e.Filter().MatchPair(ep) { - ep.Recycle() - return false - } - return true + return e.finishPair(ep) } func (e *eventLoop) handleFcntlExit(ep *event.Pair, fcntlEv *types.FcntlEvent) bool { ep.Comm = e.comm(fcntlEv.GetTid()) fd := int32(fcntlEv.Fd) ep.File = e.fdState().resolve(fd, fcntlEv.Pid) - if !e.Filter().MatchPair(ep) { - ep.Recycle() + if !e.finishPair(ep) { return false } @@ -634,6 +547,19 @@ func (e *eventLoop) registerDup(fdFile *file.FdFile, newFd int32, extraFlags int e.fdState().set(newFd, duppedFdFile) } +func (e *eventLoop) finishPairForTid(ep *event.Pair, tid uint32) bool { + ep.Comm = e.comm(tid) + return e.finishPair(ep) +} + +func (e *eventLoop) finishPair(ep *event.Pair) bool { + if e.Filter().MatchPair(ep) { + return true + } + ep.Recycle() + return false +} + // recyclePair notifies about the problem described by warning, then returns ep // to the pool. It is a convenience helper used throughout the exit handlers to // keep the error path concise. diff --git a/internal/eventloop_kinds.go b/internal/eventloop_kinds.go new file mode 100644 index 0000000..e7d9f4a --- /dev/null +++ b/internal/eventloop_kinds.go @@ -0,0 +1,141 @@ +package internal + +import ( + "ior/internal/event" + "ior/internal/globalfilter" + "ior/internal/types" +) + +type runtimeEventDecoder func(raw []byte) event.Event +type runtimeExitHandler func(e *eventLoop, ep *event.Pair) bool +type runtimeEnterFilter func(filter globalfilter.Filter, ev event.Event) bool + +type runtimeEventKind struct { + enterEventType types.EventType + exit runtimeExitHandler +} + +type rawEventDirection uint8 + +const ( + rawEnterEvent rawEventDirection = iota + rawExitEvent +) + +type rawRuntimeEvent struct { + eventType types.EventType + direction rawEventDirection + decode runtimeEventDecoder + filter runtimeEnterFilter +} + +type eventPtr[T any] interface { + *T + event.Event +} + +func rawDecoder[T any, P eventPtr[T]](decode func([]byte) P) runtimeEventDecoder { + return func(raw []byte) event.Event { + ev := decode(raw) + if ev == nil { + return nil + } + return ev + } +} + +func typedRuntimeExit[T any, P eventPtr[T]](handle func(*eventLoop, *event.Pair, P) bool) runtimeExitHandler { + return func(e *eventLoop, ep *event.Pair) bool { + ev, ok := ep.EnterEv.(P) + if !ok { + e.recyclePair(ep, "Dropped malformed enter event") + return false + } + return handle(e, ep, ev) + } +} + +func runtimeEventKinds() []runtimeEventKind { + return []runtimeEventKind{ + {enterEventType: types.ENTER_OPEN_EVENT, exit: typedRuntimeExit((*eventLoop).handleOpenExit)}, + {enterEventType: types.ENTER_EXEC_EVENT, exit: typedRuntimeExit((*eventLoop).handleExecExit)}, + {enterEventType: types.ENTER_NAME_EVENT, exit: typedRuntimeExit((*eventLoop).handleNameExit)}, + {enterEventType: types.ENTER_PATH_EVENT, exit: typedRuntimeExit((*eventLoop).handlePathExit)}, + {enterEventType: types.ENTER_FD_EVENT, exit: typedRuntimeExit((*eventLoop).handleFdExit)}, + {enterEventType: types.ENTER_DUP3_EVENT, exit: typedRuntimeExit((*eventLoop).handleDup3Exit)}, + {enterEventType: types.ENTER_OPEN_BY_HANDLE_AT_EVENT, exit: typedRuntimeExit((*eventLoop).handleOpenByHandleAtExit)}, + {enterEventType: types.ENTER_SOCKET_EVENT, exit: typedRuntimeExit((*eventLoop).handleSocketExit)}, + {enterEventType: types.ENTER_SOCKETPAIR_EVENT, exit: typedRuntimeExit((*eventLoop).handleSocketpairExit)}, + {enterEventType: types.ENTER_ACCEPT_EVENT, exit: typedRuntimeExit((*eventLoop).handleAcceptExit)}, + {enterEventType: types.ENTER_PIPE_EVENT, exit: typedRuntimeExit((*eventLoop).handlePipeExit)}, + {enterEventType: types.ENTER_EVENTFD_EVENT, exit: typedRuntimeExit((*eventLoop).handleEventfdExit)}, + {enterEventType: types.ENTER_EPOLL_CTL_EVENT, exit: typedRuntimeExit((*eventLoop).handleEpollCtlExit)}, + {enterEventType: types.ENTER_POLL_EVENT, exit: typedRuntimeExit((*eventLoop).handlePollExit)}, + {enterEventType: types.ENTER_TWO_FD_EVENT, exit: typedRuntimeExit((*eventLoop).handleTwoFdExit)}, + {enterEventType: types.ENTER_MEM_EVENT, exit: typedRuntimeExit((*eventLoop).handleMemExit)}, + {enterEventType: types.ENTER_SLEEP_EVENT, exit: typedRuntimeExit((*eventLoop).handleSleepExit)}, + {enterEventType: types.ENTER_KEYCTL_EVENT, exit: typedRuntimeExit((*eventLoop).handleKeyctlExit)}, + {enterEventType: types.ENTER_PTRACE_EVENT, exit: typedRuntimeExit((*eventLoop).handlePtraceExit)}, + {enterEventType: types.ENTER_PERF_OPEN_EVENT, exit: typedRuntimeExit((*eventLoop).handlePerfOpenExit)}, + {enterEventType: types.ENTER_NULL_EVENT, exit: typedRuntimeExit((*eventLoop).handleNullExit)}, + {enterEventType: types.ENTER_FCNTL_EVENT, exit: typedRuntimeExit((*eventLoop).handleFcntlExit)}, + } +} + +func rawRuntimeEvents() []rawRuntimeEvent { + return []rawRuntimeEvent{ + enterRaw(types.ENTER_OPEN_EVENT, rawDecoder[types.OpenEvent](types.NewOpenEventFast), matchRawOpenEvent), + exitRaw(types.EXIT_OPEN_EVENT, rawDecoder[types.RetEvent](types.NewRetEventFast)), + enterRaw(types.ENTER_FD_EVENT, rawDecoder[types.FdEvent](types.NewFdEventFast), nil), + exitRaw(types.EXIT_FD_EVENT, rawDecoder[types.FdEvent](types.NewFdEventFast)), + enterRaw(types.ENTER_NULL_EVENT, rawDecoder[types.NullEvent](types.NewNullEventFast), nil), + exitRaw(types.EXIT_NULL_EVENT, rawDecoder[types.NullEvent](types.NewNullEventFast)), + exitRaw(types.EXIT_RET_EVENT, rawDecoder[types.RetEvent](types.NewRetEventFast)), + enterRaw(types.ENTER_NAME_EVENT, rawDecoder[types.NameEvent](types.NewNameEventFast), matchRawNameEvent), + enterRaw(types.ENTER_PATH_EVENT, rawDecoder[types.PathEvent](types.NewPathEventFast), matchRawPathEvent), + enterRaw(types.ENTER_FCNTL_EVENT, rawDecoder[types.FcntlEvent](types.NewFcntlEventFast), nil), + enterRaw(types.ENTER_OPEN_BY_HANDLE_AT_EVENT, rawDecoder[types.OpenByHandleAtEvent](types.NewOpenByHandleAtEventFast), nil), + enterRaw(types.ENTER_DUP3_EVENT, rawDecoder[types.Dup3Event](types.NewDup3EventFast), nil), + enterRaw(types.ENTER_SOCKET_EVENT, rawDecoder[types.SocketEvent](types.NewSocketEventFast), nil), + enterRaw(types.ENTER_SOCKETPAIR_EVENT, rawDecoder[types.SocketpairEvent](types.NewSocketpairEventFast), nil), + exitRaw(types.EXIT_SOCKETPAIR_EVENT, rawDecoder[types.SocketpairEvent](types.NewSocketpairEventFast)), + enterRaw(types.ENTER_ACCEPT_EVENT, rawDecoder[types.AcceptEvent](types.NewAcceptEventFast), nil), + exitRaw(types.EXIT_ACCEPT_EVENT, rawDecoder[types.AcceptEvent](types.NewAcceptEventFast)), + enterRaw(types.ENTER_PIPE_EVENT, rawDecoder[types.PipeEvent](types.NewPipeEventFast), nil), + exitRaw(types.EXIT_PIPE_EVENT, rawDecoder[types.PipeEvent](types.NewPipeEventFast)), + enterRaw(types.ENTER_EVENTFD_EVENT, rawDecoder[types.EventfdEvent](types.NewEventfdEventFast), nil), + exitRaw(types.EXIT_EVENTFD_EVENT, rawDecoder[types.EventfdEvent](types.NewEventfdEventFast)), + enterRaw(types.ENTER_EPOLL_CTL_EVENT, rawDecoder[types.EpollCtlEvent](types.NewEpollCtlEventFast), nil), + enterRaw(types.ENTER_POLL_EVENT, rawDecoder[types.PollEvent](types.NewPollEventFast), nil), + enterRaw(types.ENTER_TWO_FD_EVENT, rawDecoder[types.TwoFdEvent](types.NewTwoFdEventFast), nil), + enterRaw(types.ENTER_MEM_EVENT, rawDecoder[types.MemEvent](types.NewMemEventFast), nil), + enterRaw(types.ENTER_SLEEP_EVENT, rawDecoder[types.SleepEvent](types.NewSleepEventFast), nil), + enterRaw(types.ENTER_EXEC_EVENT, rawDecoder[types.ExecEvent](types.NewExecEventFast), nil), + enterRaw(types.ENTER_KEYCTL_EVENT, rawDecoder[types.KeyctlEvent](types.NewKeyctlEventFast), nil), + enterRaw(types.ENTER_PTRACE_EVENT, rawDecoder[types.PtraceEvent](types.NewPtraceEventFast), nil), + enterRaw(types.ENTER_PERF_OPEN_EVENT, rawDecoder[types.PerfOpenEvent](types.NewPerfOpenEventFast), nil), + } +} + +func enterRaw(eventType types.EventType, decode runtimeEventDecoder, filter runtimeEnterFilter) rawRuntimeEvent { + return rawRuntimeEvent{eventType: eventType, direction: rawEnterEvent, decode: decode, filter: filter} +} + +func exitRaw(eventType types.EventType, decode runtimeEventDecoder) rawRuntimeEvent { + return rawRuntimeEvent{eventType: eventType, direction: rawExitEvent, decode: decode} +} + +func matchRawOpenEvent(filter globalfilter.Filter, ev event.Event) bool { + openEv, ok := ev.(*types.OpenEvent) + return ok && filter.MatchOpenEvent(openEv) +} + +func matchRawNameEvent(filter globalfilter.Filter, ev event.Event) bool { + nameEv, ok := ev.(*types.NameEvent) + return ok && filter.MatchNameEvent(nameEv) +} + +func matchRawPathEvent(filter globalfilter.Filter, ev event.Event) bool { + pathEv, ok := ev.(*types.PathEvent) + return ok && filter.MatchPathEvent(pathEv) +} diff --git a/internal/eventloop_kinds_test.go b/internal/eventloop_kinds_test.go new file mode 100644 index 0000000..4207233 --- /dev/null +++ b/internal/eventloop_kinds_test.go @@ -0,0 +1,158 @@ +package internal + +import ( + "testing" + + "ior/internal/event" + "ior/internal/file" + "ior/internal/globalfilter" + "ior/internal/types" +) + +func TestHandleTracepointExitDispatchesViaRuntimeKindRegistry(t *testing.T) { + el := mustNewEventLoop(t, eventLoopConfig{}) + called := false + el.exitHandlers = map[types.EventType]runtimeExitHandler{ + types.ENTER_FD_EVENT: func(_ *eventLoop, ep *event.Pair) bool { + called = true + ep.Comm = "registry" + return true + }, + } + + ep := &event.Pair{ + EnterEv: &types.FdEvent{EventType: types.ENTER_FD_EVENT, TraceId: types.SYS_ENTER_READ, Tid: defaultTid}, + ExitEv: &types.RetEvent{TraceId: types.SYS_EXIT_READ, Tid: defaultTid}, + } + + if ok := el.handleTracepointExit(ep); !ok { + t.Fatal("handleTracepointExit returned false") + } + if !called { + t.Fatal("registered runtime kind handler was not called") + } + if ep.Comm != "registry" { + t.Fatalf("ep.Comm = %q, want registry", ep.Comm) + } +} + +func TestHandleTracepointExitRejectsMismatchedRuntimeKind(t *testing.T) { + el := mustNewEventLoop(t, eventLoopConfig{}) + warnings := make(chan string, 1) + el.warningCb = func(message string) { warnings <- message } + + ep := &event.Pair{ + EnterEv: &types.NullEvent{EventType: types.ENTER_FD_EVENT, TraceId: types.SYS_ENTER_READ, Tid: defaultTid}, + ExitEv: &types.RetEvent{TraceId: types.SYS_EXIT_READ, Tid: defaultTid}, + } + + if ok := el.handleTracepointExit(ep); ok { + t.Fatal("expected mismatched runtime kind to be rejected") + } + + select { + case msg := <-warnings: + if msg != "Dropped malformed enter event" { + t.Fatalf("unexpected warning %q", msg) + } + default: + t.Fatal("expected warning for mismatched runtime kind") + } +} + +func TestTracepointExitedFinalizesRuntimeKindPair(t *testing.T) { + el := mustNewEventLoop(t, eventLoopConfig{}) + fd := int32(42) + tracked := file.NewFd(fd, "/tmp/read-source", 0) + el.fdState().set(fd, tracked) + + enter := &types.FdEvent{ + EventType: types.ENTER_FD_EVENT, + TraceId: types.SYS_ENTER_READ, + Time: 1000, + Pid: defaultPid, + Tid: defaultTid, + Fd: fd, + } + exit := &types.RetEvent{ + EventType: types.EXIT_RET_EVENT, + TraceId: types.SYS_EXIT_READ, + Time: 1256, + Pid: defaultPid, + Tid: defaultTid, + Ret: 128, + RetType: types.READ_CLASSIFIED, + } + + el.tracepointEntered(enter) + out := make(chan *event.Pair, 1) + el.tracepointExited(exit, out) + + select { + case ep := <-out: + if ep.Bytes != 128 { + t.Fatalf("ep.Bytes = %d, want 128", ep.Bytes) + } + if ep.Duration != 256 { + t.Fatalf("ep.Duration = %d, want 256", ep.Duration) + } + if ep.File == tracked { + t.Fatal("expected emitted fd file to be frozen as a duplicate") + } + if got := ep.FileName(); got != "/tmp/read-source" { + t.Fatalf("ep.FileName() = %q, want /tmp/read-source", got) + } + default: + t.Fatal("expected finalized pair to be emitted") + } +} + +func TestRawRuntimeEventHandlerAppliesEnterFilter(t *testing.T) { + el := mustNewEventLoop(t, eventLoopConfig{ + filter: globalfilter.Filter{ + File: &globalfilter.StringFilter{Pattern: "keep"}, + }, + }) + + _, raw := makeEnterPathEvent(t, defaulTime, defaultPid, defaultTid, "/tmp/drop", types.SYS_ENTER_NEWSTAT) + el.processRawEvent(raw, make(chan *event.Pair, 1)) + + if _, ok := el.pairs.enters[defaultTid]; ok { + t.Fatalf("path enter event should have been rejected by the raw enter filter") + } +} + +func TestRuntimeEventKindRegistriesHaveUniqueCoverage(t *testing.T) { + exitHandlers := make(map[types.EventType]struct{}) + for _, kind := range runtimeEventKinds() { + if _, exists := exitHandlers[kind.enterEventType]; exists { + t.Fatalf("duplicate runtime event kind for %v", kind.enterEventType) + } + exitHandlers[kind.enterEventType] = struct{}{} + if kind.exit == nil { + t.Fatalf("nil runtime exit handler for %v", kind.enterEventType) + } + } + + rawHandlers := make(map[types.EventType]rawEventDirection) + for _, rawEvent := range rawRuntimeEvents() { + if _, exists := rawHandlers[rawEvent.eventType]; exists { + t.Fatalf("duplicate raw runtime event for %v", rawEvent.eventType) + } + rawHandlers[rawEvent.eventType] = rawEvent.direction + if rawEvent.decode == nil { + t.Fatalf("nil raw decoder for %v", rawEvent.eventType) + } + if rawEvent.direction == rawEnterEvent { + if _, exists := exitHandlers[rawEvent.eventType]; !exists { + t.Fatalf("enter raw event %v has no runtime exit handler", rawEvent.eventType) + } + } + } + + for enterEventType := range exitHandlers { + if direction, exists := rawHandlers[enterEventType]; !exists || direction != rawEnterEvent { + t.Fatalf("runtime event kind %v has no enter raw handler", enterEventType) + } + } +} diff --git a/internal/eventloop_runtime.go b/internal/eventloop_runtime.go index e8971d4..24214ca 100644 --- a/internal/eventloop_runtime.go +++ b/internal/eventloop_runtime.go @@ -219,10 +219,9 @@ func (e *eventLoop) processRawEvent(raw []byte, ch chan<- *event.Pair) { handler(raw, ch) } -// initRawHandlers registers all BPF event-type dispatch callbacks. It is -// idempotent: a second call after the map is populated is a no-op. Handlers -// are grouped by event class (open, fd, null, ret, name/path, misc) so that -// each helper stays under 30 lines. +// initRawHandlers registers all BPF event-type dispatch callbacks from the +// runtime event-kind table. It is idempotent: a second call after the map is +// populated is a no-op. func (e *eventLoop) initRawHandlers() { if e.rawHandlers == nil { e.rawHandlers = make(map[types.EventType]rawEventHandler) @@ -230,290 +229,33 @@ func (e *eventLoop) initRawHandlers() { if len(e.rawHandlers) != 0 { return } - e.registerOpenHandlers() - e.registerFdHandlers() - e.registerNullHandlers() - e.registerRetHandlers() - e.registerNamePathHandlers() - e.registerMiscHandlers() - e.registerSocketHandlers() - e.registerIPCHandlers() - e.registerPollingHandlers() - e.registerTwoFdHandlers() - e.registerMemoryHandlers() - e.registerSleepHandlers() - e.registerProcessHandlers() - e.registerSecurityHandlers() -} - -// registerOpenHandlers wires enter/exit handlers for open-family events. -func (e *eventLoop) registerOpenHandlers() { - e.rawHandlers[types.ENTER_OPEN_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - openEv, ok := decodeRawEvent(e, types.ENTER_OPEN_EVENT, raw, types.NewOpenEventFast) - if !ok { - return - } - if e.Filter().MatchOpenEvent(openEv) { - e.tracepointEntered(openEv) - } - } - e.rawHandlers[types.EXIT_OPEN_EVENT] = func(raw []byte, ch chan<- *event.Pair) { - retEv, ok := decodeRawEvent(e, types.EXIT_OPEN_EVENT, raw, types.NewRetEventFast) - if !ok { - return - } - e.tracepointExited(retEv, ch) - } -} - -// registerFdHandlers wires enter/exit handlers for fd-family events (read/write/close…). -func (e *eventLoop) registerFdHandlers() { - e.rawHandlers[types.ENTER_FD_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - fdEv, ok := decodeRawEvent(e, types.ENTER_FD_EVENT, raw, types.NewFdEventFast) - if !ok { - return - } - e.tracepointEntered(fdEv) - } - e.rawHandlers[types.EXIT_FD_EVENT] = func(raw []byte, ch chan<- *event.Pair) { - fdEv, ok := decodeRawEvent(e, types.EXIT_FD_EVENT, raw, types.NewFdEventFast) - if !ok { - return - } - e.tracepointExited(fdEv, ch) - } -} - -// registerNullHandlers wires enter/exit handlers for syscalls with no interesting arguments. -func (e *eventLoop) registerNullHandlers() { - e.rawHandlers[types.ENTER_NULL_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - nullEv, ok := decodeRawEvent(e, types.ENTER_NULL_EVENT, raw, types.NewNullEventFast) - if !ok { - return - } - e.tracepointEntered(nullEv) - } - e.rawHandlers[types.EXIT_NULL_EVENT] = func(raw []byte, ch chan<- *event.Pair) { - nullEv, ok := decodeRawEvent(e, types.EXIT_NULL_EVENT, raw, types.NewNullEventFast) - if !ok { - return - } - e.tracepointExited(nullEv, ch) - } -} - -// registerRetHandlers wires the exit handler for generic return-value events. -func (e *eventLoop) registerRetHandlers() { - e.rawHandlers[types.EXIT_RET_EVENT] = func(raw []byte, ch chan<- *event.Pair) { - retEv, ok := decodeRawEvent(e, types.EXIT_RET_EVENT, raw, types.NewRetEventFast) - if !ok { - return - } - e.tracepointExited(retEv, ch) - } -} - -// registerNamePathHandlers wires enter handlers for name- and path-carrying events. -func (e *eventLoop) registerNamePathHandlers() { - e.rawHandlers[types.ENTER_NAME_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - nameEv, ok := decodeRawEvent(e, types.ENTER_NAME_EVENT, raw, types.NewNameEventFast) - if !ok { - return - } - if e.Filter().MatchNameEvent(nameEv) { - e.tracepointEntered(nameEv) - } - } - e.rawHandlers[types.ENTER_PATH_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - pathEv, ok := decodeRawEvent(e, types.ENTER_PATH_EVENT, raw, types.NewPathEventFast) - if !ok { - return - } - if e.Filter().MatchPathEvent(pathEv) { - e.tracepointEntered(pathEv) - } - } -} - -// registerMiscHandlers wires enter handlers for fcntl, open_by_handle_at, and dup3. -func (e *eventLoop) registerMiscHandlers() { - e.rawHandlers[types.ENTER_FCNTL_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - fcntlEv, ok := decodeRawEvent(e, types.ENTER_FCNTL_EVENT, raw, types.NewFcntlEventFast) - if !ok { - return - } - e.tracepointEntered(fcntlEv) - } - e.rawHandlers[types.ENTER_OPEN_BY_HANDLE_AT_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - openByHandleEv, ok := decodeRawEvent(e, types.ENTER_OPEN_BY_HANDLE_AT_EVENT, raw, types.NewOpenByHandleAtEventFast) - if !ok { - return - } - e.tracepointEntered(openByHandleEv) - } - e.rawHandlers[types.ENTER_DUP3_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - dup3Ev, ok := decodeRawEvent(e, types.ENTER_DUP3_EVENT, raw, types.NewDup3EventFast) - if !ok { - return - } - e.tracepointEntered(dup3Ev) - } -} - -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) - } - e.rawHandlers[types.EXIT_SOCKETPAIR_EVENT] = func(raw []byte, ch chan<- *event.Pair) { - socketpairEv, ok := decodeRawEvent(e, types.EXIT_SOCKETPAIR_EVENT, raw, types.NewSocketpairEventFast) - if !ok { - return - } - e.tracepointExited(socketpairEv, ch) - } - e.rawHandlers[types.ENTER_ACCEPT_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - acceptEv, ok := decodeRawEvent(e, types.ENTER_ACCEPT_EVENT, raw, types.NewAcceptEventFast) - if !ok { - return - } - e.tracepointEntered(acceptEv) - } - e.rawHandlers[types.EXIT_ACCEPT_EVENT] = func(raw []byte, ch chan<- *event.Pair) { - acceptEv, ok := decodeRawEvent(e, types.EXIT_ACCEPT_EVENT, raw, types.NewAcceptEventFast) - if !ok { - return - } - e.tracepointExited(acceptEv, ch) - } -} - -func (e *eventLoop) registerIPCHandlers() { - e.rawHandlers[types.ENTER_PIPE_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - pipeEv, ok := decodeRawEvent(e, types.ENTER_PIPE_EVENT, raw, types.NewPipeEventFast) - if !ok { - return - } - e.tracepointEntered(pipeEv) - } - e.rawHandlers[types.EXIT_PIPE_EVENT] = func(raw []byte, ch chan<- *event.Pair) { - pipeEv, ok := decodeRawEvent(e, types.EXIT_PIPE_EVENT, raw, types.NewPipeEventFast) - if !ok { - return - } - e.tracepointExited(pipeEv, ch) - } - e.rawHandlers[types.ENTER_EVENTFD_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - eventfdEv, ok := decodeRawEvent(e, types.ENTER_EVENTFD_EVENT, raw, types.NewEventfdEventFast) - if !ok { - return - } - e.tracepointEntered(eventfdEv) - } - e.rawHandlers[types.EXIT_EVENTFD_EVENT] = func(raw []byte, ch chan<- *event.Pair) { - eventfdEv, ok := decodeRawEvent(e, types.EXIT_EVENTFD_EVENT, raw, types.NewEventfdEventFast) - if !ok { - return - } - e.tracepointExited(eventfdEv, ch) - } -} - -func (e *eventLoop) registerPollingHandlers() { - e.rawHandlers[types.ENTER_EPOLL_CTL_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - epollCtlEv, ok := decodeRawEvent(e, types.ENTER_EPOLL_CTL_EVENT, raw, types.NewEpollCtlEventFast) - if !ok { - return - } - e.tracepointEntered(epollCtlEv) - } - e.rawHandlers[types.ENTER_POLL_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - pollEv, ok := decodeRawEvent(e, types.ENTER_POLL_EVENT, raw, types.NewPollEventFast) - if !ok { - return - } - e.tracepointEntered(pollEv) - } -} - -func (e *eventLoop) registerTwoFdHandlers() { - e.rawHandlers[types.ENTER_TWO_FD_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - twoFdEv, ok := decodeRawEvent(e, types.ENTER_TWO_FD_EVENT, raw, types.NewTwoFdEventFast) - if !ok { - return - } - e.tracepointEntered(twoFdEv) - } -} - -func (e *eventLoop) registerMemoryHandlers() { - e.rawHandlers[types.ENTER_MEM_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - memEv, ok := decodeRawEvent(e, types.ENTER_MEM_EVENT, raw, types.NewMemEventFast) - if !ok { - return - } - e.tracepointEntered(memEv) - } -} - -func (e *eventLoop) registerSleepHandlers() { - e.rawHandlers[types.ENTER_SLEEP_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - sleepEv, ok := decodeRawEvent(e, types.ENTER_SLEEP_EVENT, raw, types.NewSleepEventFast) - if !ok { - return - } - e.tracepointEntered(sleepEv) - } -} - -func (e *eventLoop) registerProcessHandlers() { - e.rawHandlers[types.ENTER_EXEC_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - execEv, ok := decodeRawEvent(e, types.ENTER_EXEC_EVENT, raw, types.NewExecEventFast) - if !ok { - return - } - e.tracepointEntered(execEv) + for _, rawEvent := range rawRuntimeEvents() { + e.rawHandlers[rawEvent.eventType] = e.rawRuntimeEventHandler(rawEvent) } } -func (e *eventLoop) registerSecurityHandlers() { - e.rawHandlers[types.ENTER_KEYCTL_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - keyctlEv, ok := decodeRawEvent(e, types.ENTER_KEYCTL_EVENT, raw, types.NewKeyctlEventFast) +func (e *eventLoop) rawRuntimeEventHandler(rawEvent rawRuntimeEvent) rawEventHandler { + return func(raw []byte, ch chan<- *event.Pair) { + ev, ok := e.decodeRuntimeEvent(rawEvent, raw) if !ok { return } - e.tracepointEntered(keyctlEv) - } - e.rawHandlers[types.ENTER_PTRACE_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - ptraceEv, ok := decodeRawEvent(e, types.ENTER_PTRACE_EVENT, raw, types.NewPtraceEventFast) - if !ok { + if rawEvent.direction == rawExitEvent { + e.tracepointExited(ev, ch) return } - e.tracepointEntered(ptraceEv) - } - e.rawHandlers[types.ENTER_PERF_OPEN_EVENT] = func(raw []byte, _ chan<- *event.Pair) { - perfOpenEv, ok := decodeRawEvent(e, types.ENTER_PERF_OPEN_EVENT, raw, types.NewPerfOpenEventFast) - if !ok { + if rawEvent.filter != nil && !rawEvent.filter(e.Filter(), ev) { + ev.Recycle() return } - e.tracepointEntered(perfOpenEv) + e.tracepointEntered(ev) } } -func decodeRawEvent[T any](e *eventLoop, eventType types.EventType, raw []byte, decode func([]byte) *T) (*T, bool) { - decoded := decode(raw) +func (e *eventLoop) decodeRuntimeEvent(rawEvent rawRuntimeEvent, raw []byte) (event.Event, bool) { + decoded := rawEvent.decode(raw) if decoded == nil { - e.dropMalformedRawEvent(eventType, raw) + e.dropMalformedRawEvent(rawEvent.eventType, raw) return nil, false } return decoded, true @@ -564,6 +306,11 @@ func (e *eventLoop) tracepointExited(exitEv event.Event, ch chan<- *event.Pair) if !e.handleTracepointExit(ep) { return } + e.finalizeTracepointPair(ep) + ch <- ep +} + +func (e *eventLoop) finalizeTracepointPair(ep *event.Pair) { applyRetBytes(ep) applyAddressSpaceBytes(ep) applyRequestedSleepNs(ep) @@ -571,7 +318,6 @@ func (e *eventLoop) tracepointExited(exitEv event.Event, ch chan<- *event.Pair) ep.CalculateDurations(e.pairs.prevTime(tid)) e.pairs.setPrevTime(tid, ep.ExitEv.GetTime()) e.freezePairForEmission(ep) - ch <- ep } func (e *eventLoop) freezePairForEmission(ep *event.Pair) { |
