summaryrefslogtreecommitdiff
path: root/internal/tui/tui.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-09 10:53:18 +0300
committerPaul Buetow <paul@buetow.org>2026-05-09 10:53:18 +0300
commit8da473aed2c3e901615294df398b26db5aea6032 (patch)
treea62807c29441c56776910558ece56361202f7bb2 /internal/tui/tui.go
parentf3aed5203b309f1d452a5dc3f05c1ecba8e1134b (diff)
add auto-reset timer for dashboard aggregates
Live flamegraph trie and stats engine grow unboundedly during long traces. Add a periodic auto-reset (same effect as the 'r' key) so they stay bounded. - New CLI flag -resetTimer=30s (default 30s, 0 disables). - Hotkey I cycles the cadence: off -> 15s -> 30s -> 60s -> 5m -> off. Custom intervals (e.g. -resetTimer=47s) advance to the first preset greater than the current value, then wrap to off. - autoResetTickMsg carries a generation counter so changing the cadence drops in-flight ticks scheduled under the previous interval. - Dashboard chrome shows 'auto-reset: 30s' or 'auto-reset: off'.
Diffstat (limited to 'internal/tui/tui.go')
-rw-r--r--internal/tui/tui.go53
1 files changed, 52 insertions, 1 deletions
diff --git a/internal/tui/tui.go b/internal/tui/tui.go
index a0a033b..e1d5581 100644
--- a/internal/tui/tui.go
+++ b/internal/tui/tui.go
@@ -284,6 +284,7 @@ func TraceFiltersFromContext(ctx context.Context) (globalfilter.Filter, bool) {
// RunWithTraceStarterConfig starts the TUI with explicit runtime flags.
func RunWithTraceStarterConfig(cfg flags.Config, starter TraceStarter) error {
model := newModelWithRuntimeConfig(cfg.PidFilter, filterFromConfig(cfg), cfg.PidFilter, cfg.TidFilter, cfg.TUIExportEnable, starter)
+ model.dashboard.SetAutoResetInterval(cfg.ResetTimer)
program := tea.NewProgram(model)
_, err := program.Run()
return err
@@ -292,6 +293,7 @@ func RunWithTraceStarterConfig(cfg flags.Config, starter TraceStarter) error {
// RunTestFlamesWithTraceStarterConfig starts test-flames mode with explicit runtime flags.
func RunTestFlamesWithTraceStarterConfig(cfg flags.Config, starter TraceStarter) error {
model := newModelWithRuntimeConfig(1, filterFromConfig(cfg), 1, -1, cfg.TUIExportEnable, starter)
+ model.dashboard.SetAutoResetInterval(cfg.ResetTimer)
program := tea.NewProgram(model)
_, err := program.Run()
return err
@@ -376,7 +378,12 @@ func NewModel(initialPID int, startTrace TraceStarter) Model {
// NewModelWithConfig creates the top-level TUI model with explicit runtime flags.
func NewModelWithConfig(cfg flags.Config, initialPID int, startTrace TraceStarter) Model {
- return newModelWithRuntimeConfig(initialPID, filterFromConfig(cfg), cfg.PidFilter, cfg.TidFilter, cfg.TUIExportEnable, startTrace)
+ model := newModelWithRuntimeConfig(initialPID, filterFromConfig(cfg), cfg.PidFilter, cfg.TidFilter, cfg.TUIExportEnable, startTrace)
+ // Seed the dashboard's auto-reset cadence from the parsed CLI flag
+ // (default DefaultResetTimer; 0 disables). Init() will arm the
+ // underlying tea.Tick when the dashboard becomes active.
+ model.dashboard.SetAutoResetInterval(cfg.ResetTimer)
+ return model
}
func newModelWithRuntimeConfig(initialPID int, startupFilter globalfilter.Filter, startupPidFilter, startupTidFilter int, exportEnabled bool, startTrace TraceStarter) Model {
@@ -649,9 +656,53 @@ func (m Model) handleGlobalKeyPress(msg tea.KeyPressMsg) (tea.Model, tea.Cmd, bo
next, cmd := m.reselectTID()
return next, cmd, true
}
+ if m.canHandleDashboardShortcut(msg) && key.Matches(msg, m.keys.AutoReset) {
+ next, cmd := m.cycleAutoResetInterval()
+ return next, cmd, true
+ }
return m, nil, false
}
+// autoResetCycle is the ordered set of cadences exposed via the `I`
+// hotkey. The first entry (0) disables the timer; the rest are
+// progressively longer to give users a quick way to slow auto-resets
+// down on long traces or turn them off entirely.
+var autoResetCycle = []time.Duration{
+ 0,
+ 15 * time.Second,
+ 30 * time.Second,
+ 60 * time.Second,
+ 5 * time.Minute,
+}
+
+// nextAutoResetInterval returns the next entry in autoResetCycle after
+// `current`. If `current` is not in the cycle (e.g. the user passed a
+// custom -resetTimer like 47s), the next entry is the first cycle value
+// strictly greater than current; if there is none, we wrap to 0 (off).
+func nextAutoResetInterval(current time.Duration) time.Duration {
+ for i, d := range autoResetCycle {
+ if d == current {
+ return autoResetCycle[(i+1)%len(autoResetCycle)]
+ }
+ }
+ for _, d := range autoResetCycle {
+ if d > current {
+ return d
+ }
+ }
+ return autoResetCycle[0]
+}
+
+// cycleAutoResetInterval advances the dashboard's auto-reset cadence to
+// the next preset and re-arms the timer. The new cadence takes effect
+// on the next tick; any in-flight tick from the previous cadence is
+// dropped via the dashboard model's generation counter.
+func (m Model) cycleAutoResetInterval() (tea.Model, tea.Cmd) {
+ next := nextAutoResetInterval(m.dashboard.AutoResetInterval())
+ cmd := m.dashboard.SetAutoResetInterval(next)
+ return m, cmd
+}
+
func (m Model) updateDashboardForModal(msg tea.Msg) (Model, tea.Cmd) {
if _, isKey := msg.(tea.KeyPressMsg); isKey || m.screen != ScreenDashboard {
return m, nil