summaryrefslogtreecommitdiff
path: root/internal/tui/tui.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-24 10:35:13 +0200
committerPaul Buetow <paul@buetow.org>2026-02-24 10:35:13 +0200
commita403ca152b6268eacf2804c2d857ead16af37ef3 (patch)
tree6df1ebaa0e2a68f2dfc6c17b9987ae8fbff3129c /internal/tui/tui.go
parent791c7aa9e573e80e90ba37e07c8791f280e74d9a (diff)
tui: address review feedback for dashboard and export
Diffstat (limited to 'internal/tui/tui.go')
-rw-r--r--internal/tui/tui.go38
1 files changed, 30 insertions, 8 deletions
diff --git a/internal/tui/tui.go b/internal/tui/tui.go
index 5bc7bf9..d988fa1 100644
--- a/internal/tui/tui.go
+++ b/internal/tui/tui.go
@@ -299,12 +299,6 @@ func runExportCmd(option tuiexport.Option, snap *statsengine.Snapshot) tea.Cmd {
return tuiexport.FailedMsg{Err: err}
}
return tuiexport.CompletedMsg{Path: path}
- case tuiexport.OptionFlamegraph:
- path, err := exportFlamegraph()
- if err != nil {
- return tuiexport.FailedMsg{Err: err}
- }
- return tuiexport.CompletedMsg{Path: path}
default:
return tuiexport.FailedMsg{Err: errors.New("unknown export option")}
}
@@ -335,6 +329,8 @@ func exportSnapshotCSV(snap *statsengine.Snapshot) (string, error) {
{"section", "name", "value1", "value2", "value3"},
{"summary", "totals", fmt.Sprint(snapValue(snap, func(s *statsengine.Snapshot) uint64 { return s.TotalSyscalls })), fmt.Sprint(snapValue(snap, func(s *statsengine.Snapshot) uint64 { return s.TotalErrors })), fmt.Sprint(snapValue(snap, func(s *statsengine.Snapshot) uint64 { return s.TotalBytes }))},
{"summary", "rates_per_sec", fmt.Sprintf("%.2f", snapValueF(snap, func(s *statsengine.Snapshot) float64 { return s.SyscallRatePerSec })), fmt.Sprintf("%.2f", snapValueF(snap, func(s *statsengine.Snapshot) float64 { return s.ReadBytesPerSec })), fmt.Sprintf("%.2f", snapValueF(snap, func(s *statsengine.Snapshot) float64 { return s.WriteBytesPerSec }))},
+ {"summary", "latency_gap_mean_ns", fmt.Sprintf("%.2f", snapValueF(snap, func(s *statsengine.Snapshot) float64 { return s.LatencyMeanNs })), fmt.Sprintf("%.2f", snapValueF(snap, func(s *statsengine.Snapshot) float64 { return s.GapMeanNs })), ""},
+ {"summary", "trend", trendSummary(snap, func(s *statsengine.Snapshot) statsengine.Trend { return s.LatencyTrend }), trendSummary(snap, func(s *statsengine.Snapshot) statsengine.Trend { return s.GapTrend }), trendSummary(snap, func(s *statsengine.Snapshot) statsengine.Trend { return s.ThroughputTrend })},
}
for _, row := range rows {
if err := w.Write(row); err != nil {
@@ -347,16 +343,38 @@ func exportSnapshotCSV(snap *statsengine.Snapshot) (string, error) {
if err := w.Write([]string{"syscall", s.Name, fmt.Sprint(s.Count), fmt.Sprintf("%.2f", s.RatePerSec), fmt.Sprint(s.Bytes)}); err != nil {
return "", err
}
+ if err := w.Write([]string{"syscall_latency_ns", s.Name, fmt.Sprintf("%.2f", s.LatencyMeanNs), fmt.Sprint(s.LatencyMinNs), fmt.Sprint(s.LatencyMaxNs)}); err != nil {
+ return "", err
+ }
+ if err := w.Write([]string{"syscall_percentiles_ns", s.Name, fmt.Sprint(s.LatencyP50Ns), fmt.Sprint(s.LatencyP95Ns), fmt.Sprint(s.LatencyP99Ns)}); err != nil {
+ return "", err
+ }
}
for _, r := range snap.Files() {
if err := w.Write([]string{"file", r.Path, fmt.Sprint(r.Accesses), fmt.Sprint(r.BytesRead), fmt.Sprint(r.BytesWritten)}); err != nil {
return "", err
}
+ if err := w.Write([]string{"file_latency_ns", r.Path, fmt.Sprintf("%.2f", r.AvgLatencyNs), fmt.Sprint(r.MaxLatencyNs), ""}); err != nil {
+ return "", err
+ }
}
for _, p := range snap.Processes() {
if err := w.Write([]string{"process", fmt.Sprint(p.PID), fmt.Sprint(p.Syscalls), fmt.Sprintf("%.2f", p.RatePerSec), fmt.Sprint(p.Bytes)}); err != nil {
return "", err
}
+ if err := w.Write([]string{"process_latency_ns", fmt.Sprint(p.PID), fmt.Sprintf("%.2f", p.AvgLatencyNs), "", ""}); err != nil {
+ return "", err
+ }
+ }
+ for _, b := range snap.LatencyHistogram.Buckets() {
+ if err := w.Write([]string{"latency_hist", b.Label, fmt.Sprint(b.Count), fmt.Sprint(b.LowerNs), fmt.Sprint(b.UpperNs)}); err != nil {
+ return "", err
+ }
+ }
+ for _, b := range snap.GapHistogram.Buckets() {
+ if err := w.Write([]string{"gap_hist", b.Label, fmt.Sprint(b.Count), fmt.Sprint(b.LowerNs), fmt.Sprint(b.UpperNs)}); err != nil {
+ return "", err
+ }
}
}
@@ -381,8 +399,12 @@ func snapValueF(snap *statsengine.Snapshot, get func(*statsengine.Snapshot) floa
return get(snap)
}
-func exportFlamegraph() (string, error) {
- return "", errors.New("flamegraph export is not yet available in TUI mode")
+func trendSummary(snap *statsengine.Snapshot, get func(*statsengine.Snapshot) statsengine.Trend) string {
+ if snap == nil {
+ return "stable:0.00"
+ }
+ trend := get(snap)
+ return fmt.Sprintf("%s:%.2f", trend.Direction, trend.DeltaPercent)
}
func renderHelpOverlay(width, height int, groups [][]key.Binding) string {