summaryrefslogtreecommitdiff
path: root/internal/flags
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/flags
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/flags')
-rw-r--r--internal/flags/flags.go16
-rw-r--r--internal/flags/flags_test.go40
2 files changed, 56 insertions, 0 deletions
diff --git a/internal/flags/flags.go b/internal/flags/flags.go
index 5d00b74..bbdf4b8 100644
--- a/internal/flags/flags.go
+++ b/internal/flags/flags.go
@@ -42,6 +42,7 @@ type Config struct {
CollapsedFields []string
CountField string
GlobalFilter globalfilter.Filter
+ ResetTimer time.Duration
// ShowVersion prints the banner plus version and exits without running.
ShowVersion bool
@@ -58,6 +59,12 @@ func init() {
current.Store(&defaults)
}
+// DefaultResetTimer is the default cadence for the dashboard's auto-reset
+// timer. It periodically clears aggregate state (live flamegraph trie and
+// stats engine) — the same effect as pressing `r` — to prevent unbounded
+// growth during long traces. A value of 0 disables auto-reset entirely.
+const DefaultResetTimer = 30 * time.Second
+
// NewFlags returns a configuration instance initialized with project defaults.
func NewFlags() Config {
return Config{
@@ -69,6 +76,7 @@ func NewFlags() Config {
TUIExportEnable: true,
CollapsedFields: []string{"comm", "tracepoint", "path"},
CountField: "count",
+ ResetTimer: DefaultResetTimer,
}
}
@@ -180,6 +188,8 @@ func parse() error {
flag.BoolVar(&cfg.TestLiveFlames, "testliveflames", false, "Run TUI with continuously-updating synthetic flamegraph data for live keyboard-navigation testing")
flag.DurationVar(&cfg.LiveInterval, "live-interval", cfg.LiveInterval, "Synthetic live flamegraph refresh interval for --testliveflames")
flag.BoolVar(&cfg.TUIExportEnable, "tuiExport", cfg.TUIExportEnable, "Enable TUI CSV snapshot export files (separate from Parquet recording)")
+ flag.DurationVar(&cfg.ResetTimer, "resetTimer", cfg.ResetTimer,
+ "Auto-reset interval for aggregate dashboard state (flamegraph trie + stats engine); set to 0 to disable")
flag.BoolVar(&cfg.ShowVersion, "version", false, "Print version banner and exit")
fields := flag.String("fields", "",
fmt.Sprintf("Comma separated list of fields to collapse, valid are: %v", validFields))
@@ -220,6 +230,12 @@ func parse() error {
return fmt.Errorf("invalid count field: %s", cfg.CountField)
}
+ // A negative reset timer would imply auto-resets in the past, which is
+ // nonsensical. 0 disables, anything positive enables.
+ if cfg.ResetTimer < 0 {
+ return fmt.Errorf("invalid resetTimer: %s (must be >= 0; 0 disables)", cfg.ResetTimer)
+ }
+
setCurrent(cfg)
return nil
}
diff --git a/internal/flags/flags_test.go b/internal/flags/flags_test.go
index f5274d8..77c167c 100644
--- a/internal/flags/flags_test.go
+++ b/internal/flags/flags_test.go
@@ -171,3 +171,43 @@ func TestParseInvalidTracepointRegexReturnsError(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
}
+
+func TestParseResetTimerDefault(t *testing.T) {
+ cfg, err := parseForTest(t)
+ if err != nil {
+ t.Fatalf("parse returned error: %v", err)
+ }
+ if cfg.ResetTimer != DefaultResetTimer {
+ t.Fatalf("default reset timer = %v, want %v", cfg.ResetTimer, DefaultResetTimer)
+ }
+}
+
+func TestParseResetTimerOverride(t *testing.T) {
+ cfg, err := parseForTest(t, "-resetTimer", "45s")
+ if err != nil {
+ t.Fatalf("parse returned error: %v", err)
+ }
+ if cfg.ResetTimer != 45*time.Second {
+ t.Fatalf("reset timer = %v, want 45s", cfg.ResetTimer)
+ }
+}
+
+func TestParseResetTimerZeroDisables(t *testing.T) {
+ cfg, err := parseForTest(t, "-resetTimer", "0")
+ if err != nil {
+ t.Fatalf("parse returned error: %v", err)
+ }
+ if cfg.ResetTimer != 0 {
+ t.Fatalf("reset timer = %v, want 0 (disabled)", cfg.ResetTimer)
+ }
+}
+
+func TestParseResetTimerNegativeReturnsError(t *testing.T) {
+ _, err := parseForTest(t, "-resetTimer", "-5s")
+ if err == nil {
+ t.Fatalf("expected parse error for negative reset timer")
+ }
+ if !strings.Contains(err.Error(), "invalid resetTimer") {
+ t.Fatalf("unexpected error: %v", err)
+ }
+}