summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/globalfilter/pair.go99
-rw-r--r--internal/globalfilter/pair_test.go55
-rw-r--r--internal/ior.go11
-rw-r--r--internal/ior_mode_test.go39
4 files changed, 204 insertions, 0 deletions
diff --git a/internal/globalfilter/pair.go b/internal/globalfilter/pair.go
new file mode 100644
index 0000000..4215120
--- /dev/null
+++ b/internal/globalfilter/pair.go
@@ -0,0 +1,99 @@
+package globalfilter
+
+import (
+ "ior/internal/event"
+ "ior/internal/types"
+)
+
+func MatchPair(filter Filter, pair *event.Pair) bool {
+ if pair == nil {
+ return false
+ }
+ return filter.Matches(pairCandidate{pair: pair})
+}
+
+type pairCandidate struct {
+ pair *event.Pair
+}
+
+func (p pairCandidate) SyscallValue() string {
+ if p.pair == nil || p.pair.EnterEv == nil {
+ return ""
+ }
+ return p.pair.EnterEv.GetTraceId().Name()
+}
+
+func (p pairCandidate) CommValue() string {
+ if p.pair == nil {
+ return ""
+ }
+ return p.pair.Comm
+}
+
+func (p pairCandidate) FileValue() string {
+ if p.pair == nil || p.pair.File == nil {
+ return ""
+ }
+ return p.pair.File.Name()
+}
+
+func (p pairCandidate) PIDValue() uint32 {
+ if p.pair == nil || p.pair.EnterEv == nil {
+ return 0
+ }
+ return p.pair.EnterEv.GetPid()
+}
+
+func (p pairCandidate) TIDValue() uint32 {
+ if p.pair == nil || p.pair.EnterEv == nil {
+ return 0
+ }
+ return p.pair.EnterEv.GetTid()
+}
+
+func (p pairCandidate) FDValue() int32 {
+ if p.pair == nil {
+ return -1
+ }
+ fd, ok := p.pair.FileDescriptor()
+ if !ok {
+ return -1
+ }
+ return fd
+}
+
+func (p pairCandidate) LatencyValue() uint64 {
+ if p.pair == nil {
+ return 0
+ }
+ return p.pair.Duration
+}
+
+func (p pairCandidate) GapValue() uint64 {
+ if p.pair == nil {
+ return 0
+ }
+ return p.pair.DurationToPrev
+}
+
+func (p pairCandidate) BytesValue() uint64 {
+ if p.pair == nil {
+ return 0
+ }
+ return p.pair.Bytes
+}
+
+func (p pairCandidate) ReturnValue() int64 {
+ if p.pair == nil {
+ return 0
+ }
+ retEvent, ok := p.pair.ExitEv.(*types.RetEvent)
+ if !ok {
+ return 0
+ }
+ return retEvent.Ret
+}
+
+func (p pairCandidate) ErrorValue() bool {
+ return p.ReturnValue() < 0
+}
diff --git a/internal/globalfilter/pair_test.go b/internal/globalfilter/pair_test.go
new file mode 100644
index 0000000..4810a0b
--- /dev/null
+++ b/internal/globalfilter/pair_test.go
@@ -0,0 +1,55 @@
+package globalfilter
+
+import (
+ "testing"
+
+ "ior/internal/event"
+ "ior/internal/file"
+ "ior/internal/types"
+)
+
+func samplePair() *event.Pair {
+ return &event.Pair{
+ EnterEv: &types.RetEvent{TraceId: types.SYS_ENTER_READ, Pid: 1234, Tid: 1235},
+ ExitEv: &types.RetEvent{TraceId: types.SYS_EXIT_READ, Pid: 1234, Tid: 1235, Ret: -1},
+ Comm: "nginx",
+ File: file.NewFd(7, "/var/log/access.log", 0),
+ Duration: 1_500_000,
+ DurationToPrev: 12_000,
+ Bytes: 4_096,
+ }
+}
+
+func TestMatchPairMatchesAllSupportedFields(t *testing.T) {
+ filter := Filter{
+ Syscall: &StringFilter{Pattern: "rea"},
+ Comm: &StringFilter{Pattern: "NGI"},
+ File: &StringFilter{Pattern: "access"},
+ PID: &NumericFilter{Op: OpEq, Value: 1234},
+ TID: &NumericFilter{Op: OpEq, Value: 1235},
+ FD: &NumericFilter{Op: OpEq, Value: 7},
+ LatencyNs: &NumericFilter{Op: OpGt, Value: 1_000_000},
+ GapNs: &NumericFilter{Op: OpLte, Value: 12_000},
+ Bytes: &NumericFilter{Op: OpLt, Value: 8_192},
+ RetVal: &NumericFilter{Op: OpEq, Value: -1},
+ ErrorsOnly: true,
+ }
+ if !MatchPair(filter, samplePair()) {
+ t.Fatalf("expected full filter to match pair")
+ }
+}
+
+func TestMatchPairRejectsMismatchesAndMissingFD(t *testing.T) {
+ pair := samplePair()
+ if MatchPair(Filter{Syscall: &StringFilter{Pattern: "write"}}, pair) {
+ t.Fatalf("expected syscall mismatch to reject pair")
+ }
+ if MatchPair(Filter{FD: &NumericFilter{Op: OpEq, Value: 99}}, pair) {
+ t.Fatalf("expected fd mismatch to reject pair")
+ }
+
+ pair.File = nil
+ if MatchPair(Filter{FD: &NumericFilter{Op: OpEq, Value: 7}}, pair) {
+ t.Fatalf("expected missing fd to reject pair")
+ }
+}
diff --git a/internal/ior.go b/internal/ior.go
index ea06baa..d113fff 100644
--- a/internal/ior.go
+++ b/internal/ior.go
@@ -201,6 +201,10 @@ func tuiTraceStarterFromRunTrace(
go func() {
err := startTrace(ctx, cfg, startedCh, func(el *eventLoop) {
el.printCb = func(ep *event.Pair) {
+ if !shouldIngestTracePair(cfg.GlobalFilter, ep) {
+ ep.Recycle()
+ return
+ }
engine.Ingest(ep)
streamEvents <- eventstream.NewStreamEvent(ep.EnterEv.GetTime(), ep)
liveTrie.Ingest(ep)
@@ -230,6 +234,13 @@ func tuiTraceStarterFromRunTrace(
}
}
+func shouldIngestTracePair(filter globalfilter.Filter, pair *event.Pair) bool {
+ if !filter.IsActive() {
+ return true
+ }
+ return globalfilter.MatchPair(filter, pair)
+}
+
func applyTraceFilterConfig(cfg *flags.Config, filter globalfilter.Filter) {
if cfg == nil {
return
diff --git a/internal/ior_mode_test.go b/internal/ior_mode_test.go
index fef3125..4140485 100644
--- a/internal/ior_mode_test.go
+++ b/internal/ior_mode_test.go
@@ -9,9 +9,12 @@ import (
"testing/synctest"
"time"
+ "ior/internal/event"
+ "ior/internal/file"
"ior/internal/flags"
"ior/internal/globalfilter"
"ior/internal/tui"
+ "ior/internal/types"
)
func TestShouldRunTraceMode(t *testing.T) {
@@ -472,6 +475,42 @@ func TestTuiTraceStarterFromRunTraceUsesContextFilters(t *testing.T) {
}
}
+func TestShouldIngestTracePairAppliesFullGlobalFilter(t *testing.T) {
+ pair := &event.Pair{
+ EnterEv: &types.RetEvent{TraceId: types.SYS_ENTER_READ, Pid: 1234, Tid: 1235},
+ ExitEv: &types.RetEvent{TraceId: types.SYS_EXIT_READ, Pid: 1234, Tid: 1235, Ret: -1},
+ Comm: "nginx",
+ File: file.NewFd(7, "/var/log/access.log", 0),
+ Duration: 1_500_000,
+ DurationToPrev: 12_000,
+ Bytes: 4_096,
+ }
+
+ filter := globalfilter.Filter{
+ Syscall: &globalfilter.StringFilter{Pattern: "rea"},
+ Comm: &globalfilter.StringFilter{Pattern: "ngi"},
+ File: &globalfilter.StringFilter{Pattern: "access"},
+ PID: &globalfilter.NumericFilter{Op: globalfilter.OpEq, Value: 1234},
+ TID: &globalfilter.NumericFilter{Op: globalfilter.OpEq, Value: 1235},
+ FD: &globalfilter.NumericFilter{Op: globalfilter.OpEq, Value: 7},
+ LatencyNs: &globalfilter.NumericFilter{Op: globalfilter.OpGt, Value: 1_000_000},
+ GapNs: &globalfilter.NumericFilter{Op: globalfilter.OpLte, Value: 12_000},
+ Bytes: &globalfilter.NumericFilter{Op: globalfilter.OpLt, Value: 8_192},
+ RetVal: &globalfilter.NumericFilter{Op: globalfilter.OpEq, Value: -1},
+ ErrorsOnly: true,
+ }
+
+ if !shouldIngestTracePair(filter, pair) {
+ t.Fatalf("expected full filter to accept matching pair")
+ }
+ if shouldIngestTracePair(globalfilter.Filter{Syscall: &globalfilter.StringFilter{Pattern: "write"}}, pair) {
+ t.Fatalf("expected syscall mismatch to reject pair")
+ }
+ if shouldIngestTracePair(globalfilter.Filter{FD: &globalfilter.NumericFilter{Op: globalfilter.OpEq, Value: 99}}, pair) {
+ t.Fatalf("expected fd mismatch to reject pair")
+ }
+}
+
func TestProfilingFilesForMode(t *testing.T) {
cpu, mem, execTrace, duration := profilingFilesForMode(false)
if cpu != "ior.cpuprofile" || mem != "ior.memprofile" {