summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AGENTS.md2
-rw-r--r--docs/syscall-tracing-plan.md2
-rw-r--r--internal/statsengine/engine_test.go11
-rw-r--r--internal/statsengine/snapshot.go22
-rw-r--r--internal/statsengine/snapshot_test.go29
-rw-r--r--internal/tui/dashboard/model.go2
-rw-r--r--internal/tui/dashboard/nonio.go37
-rw-r--r--internal/tui/dashboard/nonio_test.go37
-rw-r--r--internal/types/family.go10
9 files changed, 82 insertions, 70 deletions
diff --git a/AGENTS.md b/AGENTS.md
index 08d37a5..0481ee8 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -75,7 +75,7 @@ Generator source code:
- **File output in TUI** is explicit export only (`e`), writing `ior-stream-<timestamp>.csv` in the current directory from the current filtered stream snapshot.
- **Export toggle flag**: `-tuiExport=true|false` (default `true`) enables or disables TUI stream CSV export at runtime.
- **Tab navigation** supports `tab/shift+tab`, numeric keys `1..8`, and directional keys `left/right` and `h/l`.
-- **Non-IO visibility**: dashboard includes a dedicated `Non-IO` tab backed by per-family aggregate rows (`Snapshot.Families` / `Snapshot.NonIOFamilies`).
+- **Non-IO visibility**: dashboard includes a dedicated `Non-IO` tab backed by per-family aggregate rows (`Snapshot.Families`); Non-IO filtering is applied in `internal/tui/dashboard`.
- **When export is disabled**, export key hints are hidden from dashboard help and `e` does not open export modal.
- **Fast-refresh cadence**: `-tui-fast-refresh` (default `250ms`) controls the high-frequency tick interval for the flamegraph and stream tabs; set to `0` to disable high-frequency refresh and fall back to the standard dashboard cadence.
- **Sampling / aggregate-only mode**:
diff --git a/docs/syscall-tracing-plan.md b/docs/syscall-tracing-plan.md
index 0a6e370..25e1af3 100644
--- a/docs/syscall-tracing-plan.md
+++ b/docs/syscall-tracing-plan.md
@@ -102,7 +102,7 @@ Memory extent is tracked separately via address-space metrics.
## Runtime Notes
- Dashboard ships with a dedicated `Non-IO` tab (shortcut `8`) backed by
- per-family aggregates (`Snapshot.Families` / `Snapshot.NonIOFamilies`).
+ per-family aggregates (`Snapshot.Families`); Non-IO filtering is applied in `internal/tui/dashboard`.
- Aggregate-only sampling mode is implemented (`rate=0`) via:
- `-syscall-sampling-families`
- `-syscall-sampling-syscalls`
diff --git a/internal/statsengine/engine_test.go b/internal/statsengine/engine_test.go
index 0500d20..6eb19e8 100644
--- a/internal/statsengine/engine_test.go
+++ b/internal/statsengine/engine_test.go
@@ -115,12 +115,13 @@ func TestEngineAggregatesSyscallFamilies(t *testing.T) {
t.Fatalf("FS family = %+v, want count=1 bytes=4", families["FS"])
}
- nonIO := familyRowsByName(snap.NonIOFamilies())
- if _, ok := nonIO["FS"]; ok {
- t.Fatalf("NonIOFamilies should not include FS: %+v", nonIO["FS"])
+ // Verify that non-FS families exist and FS is present in the full
+ // family list — Non-IO filtering has moved to the dashboard package.
+ if _, ok := families["Polling"]; !ok {
+ t.Fatalf("Families missing Polling row: %+v", families)
}
- if nonIO["Polling"].Count != 2 || nonIO["Process"].Count != 1 {
- t.Fatalf("NonIOFamilies missing expected rows: %+v", nonIO)
+ if _, ok := families["Process"]; !ok {
+ t.Fatalf("Families missing Process row: %+v", families)
}
}
diff --git a/internal/statsengine/snapshot.go b/internal/statsengine/snapshot.go
index bec92fb..fa9c948 100644
--- a/internal/statsengine/snapshot.go
+++ b/internal/statsengine/snapshot.go
@@ -237,28 +237,6 @@ func (s Snapshot) FamiliesCount() int {
return len(s.families)
}
-// NonIOFamilies returns family rows outside the file-system/fd-focused family.
-func (s Snapshot) NonIOFamilies() []FamilySnapshot {
- rows := make([]FamilySnapshot, 0, len(s.families))
- for _, row := range s.families {
- if types.IsNonIOSyscallFamily(row.Family) {
- rows = append(rows, row)
- }
- }
- return rows
-}
-
-// NonIOFamiliesCount returns number of non-FS syscall-family rows.
-func (s Snapshot) NonIOFamiliesCount() int {
- count := 0
- for _, row := range s.families {
- if types.IsNonIOSyscallFamily(row.Family) {
- count++
- }
- }
- return count
-}
-
// TopNSyscalls returns at most n per-syscall rows in ranking order.
// Callers must treat returned data as read-only.
func (s Snapshot) TopNSyscalls(n int) []SyscallSnapshot {
diff --git a/internal/statsengine/snapshot_test.go b/internal/statsengine/snapshot_test.go
index 9b54409..d5cb7aa 100644
--- a/internal/statsengine/snapshot_test.go
+++ b/internal/statsengine/snapshot_test.go
@@ -124,35 +124,6 @@ func TestNilAccessorsRemainNil(t *testing.T) {
}
}
-func TestSnapshotNonIOFamilies(t *testing.T) {
- s := NewSnapshotWithFamilies(
- nil,
- nil,
- nil,
- nil,
- []FamilySnapshot{
- {Family: types.FamilyFS, Name: "FS"},
- {Family: types.FamilyPolling, Name: "Polling"},
- {Family: types.FamilyProcess, Name: "Process"},
- },
- nil,
- nil,
- HistogramSnapshot{},
- HistogramSnapshot{},
- )
-
- rows := s.NonIOFamilies()
- if len(rows) != 2 {
- t.Fatalf("NonIOFamilies len = %d, want 2", len(rows))
- }
- if rows[0].Family == types.FamilyFS || rows[1].Family == types.FamilyFS {
- t.Fatalf("NonIOFamilies included FS: %+v", rows)
- }
- if got := s.NonIOFamiliesCount(); got != 2 {
- t.Fatalf("NonIOFamiliesCount = %d, want 2", got)
- }
-}
-
func TestTopNAccessors(t *testing.T) {
s := NewSnapshot(
nil,
diff --git a/internal/tui/dashboard/model.go b/internal/tui/dashboard/model.go
index 1f479c7..fa36453 100644
--- a/internal/tui/dashboard/model.go
+++ b/internal/tui/dashboard/model.go
@@ -884,7 +884,7 @@ func (m Model) maxSyscallsRows() int {
}
func (m Model) maxNonIORows() int {
- return m.snapshotOrZero().NonIOFamiliesCount()
+ return nonIOFamiliesCount(m.snapshotOrZero().Families())
}
func (m Model) maxFilesRows() int {
diff --git a/internal/tui/dashboard/nonio.go b/internal/tui/dashboard/nonio.go
index aef63f4..42069a2 100644
--- a/internal/tui/dashboard/nonio.go
+++ b/internal/tui/dashboard/nonio.go
@@ -6,6 +6,7 @@ import (
"ior/internal/statsengine"
common "ior/internal/tui/common"
+ "ior/internal/types"
)
func renderNonIO(snap *statsengine.Snapshot, width, height int) string {
@@ -17,7 +18,7 @@ func renderNonIOWithOffset(snap *statsengine.Snapshot, width, height, offset, se
return "Non-IO: waiting for stats..."
}
- rowsData := snap.NonIOFamilies()
+ rowsData := nonIOFamilies(snap.Families())
columns, rows := nonIOTableData(rowsData, width)
if len(rows) == 0 {
return "Non-IO: no data"
@@ -97,3 +98,37 @@ func nonIORowsCompact(families []statsengine.FamilySnapshot) [][]string {
}
return rows
}
+
+// isFileSyscallFamily reports whether family belongs to file-system/fd views.
+// This is a dashboard presentation concept: the Non-IO tab excludes FS families.
+func isFileSyscallFamily(family types.SyscallFamily) bool {
+ return family == types.FamilyFS
+}
+
+// isNonIOSyscallFamily reports whether family should appear in the Non-IO tab.
+// This is a dashboard grouping policy kept out of the core stats/types packages.
+func isNonIOSyscallFamily(family types.SyscallFamily) bool {
+ return family != "" && !isFileSyscallFamily(family)
+}
+
+// nonIOFamilies filters family snapshot rows to those outside the FS family.
+func nonIOFamilies(all []statsengine.FamilySnapshot) []statsengine.FamilySnapshot {
+ rows := make([]statsengine.FamilySnapshot, 0, len(all))
+ for _, row := range all {
+ if isNonIOSyscallFamily(row.Family) {
+ rows = append(rows, row)
+ }
+ }
+ return rows
+}
+
+// nonIOFamiliesCount returns the number of non-FS family rows.
+func nonIOFamiliesCount(all []statsengine.FamilySnapshot) int {
+ count := 0
+ for _, row := range all {
+ if isNonIOSyscallFamily(row.Family) {
+ count++
+ }
+ }
+ return count
+}
diff --git a/internal/tui/dashboard/nonio_test.go b/internal/tui/dashboard/nonio_test.go
index 5fabc76..0b5fc25 100644
--- a/internal/tui/dashboard/nonio_test.go
+++ b/internal/tui/dashboard/nonio_test.go
@@ -35,3 +35,40 @@ func TestRenderNonIOIncludesExpectedFamilyRows(t *testing.T) {
t.Fatalf("non-io table should exclude FS rows:\n%s", out)
}
}
+
+func TestNonIOFamiliesFiltering(t *testing.T) {
+ all := []statsengine.FamilySnapshot{
+ {Family: types.FamilyFS, Name: "FS"},
+ {Family: types.FamilyPolling, Name: "Polling"},
+ {Family: types.FamilyProcess, Name: "Process"},
+ }
+
+ rows := nonIOFamilies(all)
+ if len(rows) != 2 {
+ t.Fatalf("nonIOFamilies len = %d, want 2", len(rows))
+ }
+ if rows[0].Family == types.FamilyFS || rows[1].Family == types.FamilyFS {
+ t.Fatalf("nonIOFamilies included FS: %+v", rows)
+ }
+ if got := nonIOFamiliesCount(all); got != 2 {
+ t.Fatalf("nonIOFamiliesCount = %d, want 2", got)
+ }
+}
+
+func TestIsNonIOSyscallFamily(t *testing.T) {
+ tests := []struct {
+ family types.SyscallFamily
+ want bool
+ }{
+ {types.FamilyFS, false},
+ {types.FamilyPolling, true},
+ {types.FamilyProcess, true},
+ {types.FamilyNetwork, true},
+ {"", false},
+ }
+ for _, tt := range tests {
+ if got := isNonIOSyscallFamily(tt.family); got != tt.want {
+ t.Errorf("isNonIOSyscallFamily(%q) = %v, want %v", tt.family, got, tt.want)
+ }
+ }
+}
diff --git a/internal/types/family.go b/internal/types/family.go
index 048f143..de5e021 100644
--- a/internal/types/family.go
+++ b/internal/types/family.go
@@ -20,16 +20,6 @@ func AllSyscallFamilies() []SyscallFamily {
}
}
-// IsFileSyscallFamily reports whether family belongs to file-system/syscall-fd views.
-func IsFileSyscallFamily(family SyscallFamily) bool {
- return family == FamilyFS
-}
-
-// IsNonIOSyscallFamily reports whether family should appear in the Non-IO tab.
-func IsNonIOSyscallFamily(family SyscallFamily) bool {
- return family != "" && !IsFileSyscallFamily(family)
-}
-
// SyscallFamilyRank returns the stable display rank for a family.
func SyscallFamilyRank(family SyscallFamily) int {
for idx, candidate := range AllSyscallFamilies() {