summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-18 08:51:33 +0200
committerPaul Buetow <paul@buetow.org>2026-03-18 08:51:33 +0200
commit6ab80599c8f8ba688a0415ecbeb03e494ef31f04 (patch)
treec0c831f29bddb1754629eb337f5ec8bc9efae983
parentd8ef4ac43d3039c3d583c1ac20c7dca6b69a6c92 (diff)
refactor: replace reflect.TypeOf dispatch in exit handlers with type switch (task 432)
The exitHandlers map keyed by reflect.Type was a reflection-based dispatch table that incurred allocation and reflection overhead on every event pair in the hot processing path. Replace it with a plain type switch in handleTracepointExit, which the compiler resolves statically. Removes: initExitHandlers, typeKey, mustBeType, newTypedExitHandler, exitHandlerRegistry, the exitHandlers struct field, and the tracepointExitHandler type alias — all dead after the switch. Fixes a pre-existing uint→uint64 mismatch in stats(). Updates tests to target the new default branch directly. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
-rw-r--r--internal/eventloop.go26
-rw-r--r--internal/eventloop_error_handling_test.go46
-rw-r--r--internal/eventloop_exit.go69
3 files changed, 56 insertions, 85 deletions
diff --git a/internal/eventloop.go b/internal/eventloop.go
index f15c344..b7fe230 100644
--- a/internal/eventloop.go
+++ b/internal/eventloop.go
@@ -1,10 +1,7 @@
package internal
-import "C"
-
import (
"fmt"
- "reflect"
"time"
"ior/internal/event"
@@ -38,7 +35,6 @@ type eventLoopConfig struct {
}
type rawEventHandler func(raw []byte, ch chan<- *event.Pair)
-type tracepointExitHandler func(ep *event.Pair) bool
type eventLoop struct {
filter globalfilter.Filter
@@ -51,7 +47,6 @@ type eventLoop struct {
commResolver *commResolver
prevPairTimes map[uint32]uint64 // Previous event's time (to calculate time differences between two events)
rawHandlers map[types.EventType]rawEventHandler
- exitHandlers map[reflect.Type]tracepointExitHandler
printCb func(ep *event.Pair) // Callback to print the event
warningCb func(message string) // Optional callback for non-fatal event processing warnings
cfg eventLoopConfig
@@ -86,13 +81,11 @@ func newEventLoop(cfg eventLoopConfig) (*eventLoop, error) {
commResolver: commState,
prevPairTimes: make(map[uint32]uint64),
rawHandlers: make(map[types.EventType]rawEventHandler),
- exitHandlers: make(map[reflect.Type]tracepointExitHandler),
printCb: func(ep *event.Pair) { fmt.Println(ep); ep.Recycle() },
cfg: cfg,
done: make(chan struct{}),
}
el.initRawHandlers()
- el.initExitHandlers()
el.configureOutputCallback()
el.seedTrackedPidComm()
return el, nil
@@ -167,6 +160,19 @@ func (e *eventLoop) stats() string {
<-e.done
duration := time.Since(e.startTime)
+ secs := duration.Seconds()
+ // Guard against division by zero when called immediately after start.
+ rate := func(n uint64) float64 {
+ if secs <= 0 {
+ return 0
+ }
+ return float64(n) / secs
+ }
+ mismatchPct := 0.0
+ if e.numTracepoints > 0 {
+ mismatchPct = (float64(e.numTracepointMismatches) / float64(e.numTracepoints)) * 100
+ }
+
stats := fmt.Sprintf(
"Statistics:\n"+
"\tduration: %v\n"+
@@ -174,9 +180,9 @@ func (e *eventLoop) stats() string {
"\tsyscalls: %d (%.2f/s)\n"+
"\tsyscalls after filter: %d (%.2f/s)\n",
duration,
- e.numTracepoints, float64(e.numTracepoints)/duration.Seconds(), e.numTracepointMismatches, (float64(e.numTracepointMismatches)/float64(e.numTracepoints))*100,
- e.numSyscalls, float64(e.numSyscalls)/duration.Seconds(),
- e.numSyscallsAfterFilter, float64(e.numSyscallsAfterFilter)/duration.Seconds(),
+ e.numTracepoints, rate(uint64(e.numTracepoints)), e.numTracepointMismatches, mismatchPct,
+ e.numSyscalls, rate(uint64(e.numSyscalls)),
+ e.numSyscallsAfterFilter, rate(uint64(e.numSyscallsAfterFilter)),
)
return stats
diff --git a/internal/eventloop_error_handling_test.go b/internal/eventloop_error_handling_test.go
index b7dd282..5867417 100644
--- a/internal/eventloop_error_handling_test.go
+++ b/internal/eventloop_error_handling_test.go
@@ -166,44 +166,38 @@ func TestDecodeRawEventMalformedNotifies(t *testing.T) {
}
}
-func TestMustBeTypeReturnsTypedEnterEvent(t *testing.T) {
- el := mustNewEventLoop(t, eventLoopConfig{})
- enterEv, enterRaw := makeEnterOpenEvent(t, defaulTime, defaultPid, defaultTid)
- ep := event.NewPair(types.NewOpenEvent(enterRaw))
- defer ep.Recycle()
-
- typed, ok := mustBeType[*types.OpenEvent](el, ep, "ignored")
- if !ok {
- t.Fatal("expected mustBeType to return the typed enter event")
- }
- if typed.GetTid() != enterEv.Tid {
- t.Fatalf("mustBeType() returned tid %d, want %d", typed.GetTid(), enterEv.Tid)
- }
-}
-
-func TestMustBeTypeRecyclesMalformedPairAndNotifies(t *testing.T) {
+// unknownEvent is a minimal event.Event stub used to exercise the default
+// branch of handleTracepointExit (an enter-event type not covered by any case).
+type unknownEvent struct{}
+
+func (unknownEvent) String() string { return "unknownEvent" }
+func (unknownEvent) GetTraceId() types.TraceId { return 0 }
+func (unknownEvent) GetPid() uint32 { return 0 }
+func (unknownEvent) GetTid() uint32 { return 0 }
+func (unknownEvent) GetTime() uint64 { return 0 }
+func (unknownEvent) Equals(other any) bool { return false }
+func (unknownEvent) Recycle() {}
+
+func TestHandleTracepointExitUnknownTypeDropsMalformedEvent(t *testing.T) {
el := mustNewEventLoop(t, eventLoopConfig{})
warnings := make(chan string, 1)
el.warningCb = func(message string) { warnings <- message }
- _, enterRaw := makeEnterNullEvent(t, defaulTime, defaultPid, defaultTid, types.SYS_ENTER_SYNC)
- ep := event.NewPair(types.NewNullEvent(enterRaw))
+ // Wrap an unknown enter-event type to hit the default branch of the type switch.
+ ep := event.NewPair(unknownEvent{})
- typed, ok := mustBeType[*types.OpenEvent](el, ep, "Dropped malformed open enter event")
- if ok || typed != nil {
- t.Fatalf("expected mustBeType mismatch to fail, got ok=%v typed=%v", ok, typed)
- }
- if ep.EnterEv != nil || ep.ExitEv != nil {
- t.Fatalf("expected malformed pair to be recycled")
+ ok := el.handleTracepointExit(ep)
+ if ok {
+ t.Fatal("expected handleTracepointExit to return false for unknown enter-event type")
}
select {
case msg := <-warnings:
- if msg != "Dropped malformed open enter event" {
+ if msg != "Dropped malformed enter event" {
t.Fatalf("unexpected warning %q", msg)
}
default:
- t.Fatalf("expected warning notification")
+ t.Fatal("expected warning notification for unknown enter-event type")
}
}
diff --git a/internal/eventloop_exit.go b/internal/eventloop_exit.go
index b0c0256..e40a3fd 100644
--- a/internal/eventloop_exit.go
+++ b/internal/eventloop_exit.go
@@ -3,7 +3,6 @@ package internal
import (
"fmt"
"os"
- "reflect"
"syscall"
"ior/internal/event"
@@ -11,58 +10,30 @@ import (
"ior/internal/types"
)
-func (e *eventLoop) initExitHandlers() {
- e.exitHandlers = map[reflect.Type]tracepointExitHandler{
- typeKey[*types.OpenEvent](): newTypedExitHandler(e, "Dropped malformed open enter event", e.handleOpenExit),
- typeKey[*types.NameEvent](): newTypedExitHandler(e, "Dropped malformed name enter event", e.handleNameExit),
- typeKey[*types.PathEvent](): newTypedExitHandler(e, "Dropped malformed path enter event", e.handlePathExit),
- typeKey[*types.FdEvent](): newTypedExitHandler(e, "Dropped malformed fd enter event", e.handleFdExit),
- typeKey[*types.Dup3Event](): newTypedExitHandler(e, "Dropped malformed dup3 enter event", e.handleDup3Exit),
- typeKey[*types.OpenByHandleAtEvent](): newTypedExitHandler(e, "Dropped malformed open_by_handle_at enter event", e.handleOpenByHandleAtExit),
- typeKey[*types.NullEvent](): newTypedExitHandler(e, "Dropped malformed null enter event", e.handleNullExit),
- typeKey[*types.FcntlEvent](): newTypedExitHandler(e, "Dropped malformed fcntl enter event", e.handleFcntlExit),
- }
-}
-
-func mustBeType[T event.Event](e *eventLoop, ep *event.Pair, message string) (T, bool) {
- enterEv, ok := ep.EnterEv.(T)
- if !ok {
- e.recyclePair(ep, message)
- var zero T
- return zero, false
- }
- return enterEv, true
-}
-
-func newTypedExitHandler[T event.Event](e *eventLoop, message string, handle func(*event.Pair, T) bool) tracepointExitHandler {
- return func(ep *event.Pair) bool {
- enterEv, ok := mustBeType[T](e, ep, message)
- if !ok {
- return false
- }
- return handle(ep, enterEv)
- }
-}
-
-func typeKey[T any]() reflect.Type {
- var zero T
- return reflect.TypeOf(zero)
-}
-
-func (e *eventLoop) exitHandlerRegistry() map[reflect.Type]tracepointExitHandler {
- if e.exitHandlers == nil {
- e.initExitHandlers()
- }
- return e.exitHandlers
-}
-
+// 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) handleTracepointExit(ep *event.Pair) bool {
- handler, ok := e.exitHandlerRegistry()[reflect.TypeOf(ep.EnterEv)]
- if !ok {
+ switch ev := ep.EnterEv.(type) {
+ case *types.OpenEvent:
+ return e.handleOpenExit(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.NullEvent:
+ return e.handleNullExit(ep, ev)
+ case *types.FcntlEvent:
+ return e.handleFcntlExit(ep, ev)
+ default:
e.recyclePair(ep, "Dropped malformed enter event")
return false
}
- return handler(ep)
}
func (e *eventLoop) handleOpenExit(ep *event.Pair, openEv *types.OpenEvent) bool {