summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/statsengine/snapshot.go48
-rw-r--r--internal/statsengine/snapshot_test.go23
-rw-r--r--internal/tui/dashboard/overview.go36
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, ", ")