diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-24 12:21:42 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-24 12:21:42 +0200 |
| commit | d93d286b6b214f12e65214360a41f8668123f174 (patch) | |
| tree | 1113fbb7c7dbd08373d7db14bc791bb85eaafd80 /internal | |
| parent | bd3c53086a3fe3ac177f4c656d1e521a2f0595b1 (diff) | |
statsengine: add top-n snapshot helpers for overview
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/statsengine/snapshot.go | 48 | ||||
| -rw-r--r-- | internal/statsengine/snapshot_test.go | 23 | ||||
| -rw-r--r-- | internal/tui/dashboard/overview.go | 36 |
3 files changed, 80 insertions, 27 deletions
diff --git a/internal/statsengine/snapshot.go b/internal/statsengine/snapshot.go index 6e38739..1cc2118 100644 --- a/internal/statsengine/snapshot.go +++ b/internal/statsengine/snapshot.go @@ -183,6 +183,12 @@ func (s Snapshot) SyscallsCount() int { return len(s.syscalls) } +// 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 { + return topNSyscalls(s.syscalls, n) +} + // Files returns per-file snapshot rows. // Callers must treat returned data as read-only. func (s Snapshot) Files() []FileSnapshot { @@ -194,6 +200,12 @@ func (s Snapshot) FilesCount() int { return len(s.files) } +// TopNFiles returns at most n file rows in ranking order. +// Callers must treat returned data as read-only. +func (s Snapshot) TopNFiles(n int) []FileSnapshot { + return topNFiles(s.files, n) +} + // Processes returns per-process snapshot rows. // Callers must treat returned data as read-only. func (s Snapshot) Processes() []ProcessSnapshot { @@ -205,8 +217,44 @@ func (s Snapshot) ProcessesCount() int { return len(s.processes) } +// TopNProcesses returns at most n process rows in ranking order. +// Callers must treat returned data as read-only. +func (s Snapshot) TopNProcesses(n int) []ProcessSnapshot { + return topNProcesses(s.processes, n) +} + // Buckets returns histogram buckets. // Callers must treat returned data as read-only. func (h HistogramSnapshot) Buckets() []HistogramBucketSnapshot { return h.buckets } + +func topNSyscalls(rows []SyscallSnapshot, n int) []SyscallSnapshot { + if n <= 0 || len(rows) == 0 { + return nil + } + if n > len(rows) { + n = len(rows) + } + return rows[:n:n] +} + +func topNFiles(rows []FileSnapshot, n int) []FileSnapshot { + if n <= 0 || len(rows) == 0 { + return nil + } + if n > len(rows) { + n = len(rows) + } + return rows[:n:n] +} + +func topNProcesses(rows []ProcessSnapshot, n int) []ProcessSnapshot { + if n <= 0 || len(rows) == 0 { + return nil + } + if n > len(rows) { + n = len(rows) + } + return rows[:n:n] +} diff --git a/internal/statsengine/snapshot_test.go b/internal/statsengine/snapshot_test.go index e5c3caa..277fa0d 100644 --- a/internal/statsengine/snapshot_test.go +++ b/internal/statsengine/snapshot_test.go @@ -103,3 +103,26 @@ func TestNilAccessorsRemainNil(t *testing.T) { t.Fatalf("expected nil buckets, got %#v", got) } } + +func TestTopNAccessors(t *testing.T) { + s := NewSnapshot( + nil, + nil, + nil, + []SyscallSnapshot{{Name: "a"}, {Name: "b"}, {Name: "c"}}, + []FileSnapshot{{Path: "/a"}, {Path: "/b"}}, + []ProcessSnapshot{{PID: 1}, {PID: 2}, {PID: 3}}, + HistogramSnapshot{}, + HistogramSnapshot{}, + ) + + if got := s.TopNSyscalls(2); len(got) != 2 { + t.Fatalf("expected 2 top syscalls, got %d", len(got)) + } + if got := s.TopNFiles(10); len(got) != 2 { + t.Fatalf("expected all files when n exceeds len, got %d", len(got)) + } + if got := s.TopNProcesses(0); got != nil { + t.Fatalf("expected nil when n<=0, got %#v", got) + } +} diff --git a/internal/tui/dashboard/overview.go b/internal/tui/dashboard/overview.go index 6d705da..1bdb64f 100644 --- a/internal/tui/dashboard/overview.go +++ b/internal/tui/dashboard/overview.go @@ -108,54 +108,36 @@ func trendWithArrow(trend statsengine.Trend) string { } func summarizeTopSyscalls(snap *statsengine.Snapshot) string { - syscalls := snap.Syscalls() + syscalls := snap.TopNSyscalls(3) if len(syscalls) == 0 { return "none" } - - limit := 3 - if len(syscalls) < limit { - limit = len(syscalls) - } - - parts := make([]string, 0, limit) - for _, syscall := range syscalls[:limit] { + parts := make([]string, 0, len(syscalls)) + for _, syscall := range syscalls { parts = append(parts, fmt.Sprintf("%s(%d)", syscall.Name, syscall.Count)) } return strings.Join(parts, ", ") } func summarizeTopFiles(snap *statsengine.Snapshot) string { - files := snap.Files() + files := snap.TopNFiles(3) if len(files) == 0 { return "none" } - - limit := 3 - if len(files) < limit { - limit = len(files) - } - - parts := make([]string, 0, limit) - for _, f := range files[:limit] { + parts := make([]string, 0, len(files)) + for _, f := range files { parts = append(parts, fmt.Sprintf("%s(%d)", trimPathTail(f.Path, 24), f.Accesses)) } return strings.Join(parts, ", ") } func summarizeTopProcesses(snap *statsengine.Snapshot) string { - processes := snap.Processes() + processes := snap.TopNProcesses(3) if len(processes) == 0 { return "none" } - - limit := 3 - if len(processes) < limit { - limit = len(processes) - } - - parts := make([]string, 0, limit) - for _, p := range processes[:limit] { + parts := make([]string, 0, len(processes)) + for _, p := range processes { parts = append(parts, fmt.Sprintf("%s/%d(%d)", p.Comm, p.PID, p.Syscalls)) } return strings.Join(parts, ", ") |
