summaryrefslogtreecommitdiff
path: root/internal/tui
AgeCommit message (Collapse)Author
2026-05-14use configurable fastRefreshEvery for InitCmd on flame and stream tabsPaul Buetow
Change tabDescriptor.InitCmd from func() tea.Cmd to func(*Model) tea.Cmd so the initial tick for the flame and stream tabs uses the model's fastRefreshEvery interval rather than hardcoded 200 ms constants. Both call sites in Init() and postKeyTransitionCmd() now pass the model pointer. The now-unused streamTickCmdFn/flameTickCmdFn package-level adapters are removed; the TabFlame and TabStream registry entries use inline closures that delegate to m.flameTickCmd()/m.streamTickCmd(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14wire TUIFastRefreshInterval into dashboard model and update testsPaul Buetow
Add fastRefreshMs parameter to NewModelWithConfig so callers can supply the high-frequency tick cadence for stream and flame tabs. Convert the streamTickCmd/flameTickCmd package-level functions to model methods that honour fastRefreshEvery (falling back to the 200 ms constants when zero for backward-compatibility). Add SetFastRefreshInterval setter so RunWithTraceStarterConfig can apply cfg.TUIFastRefreshInterval after construction. Update all 68 test call sites to pass fastRefreshMs=200 and add three new tests covering zero-fallback, stored value, and setter behaviour. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13refactor: break down functions exceeding 50 lines into smaller helpersPaul Buetow
Split 22 production files across the codebase — event loop, TUI models, probe manager, dashboard, export, flag parsing, code generation, and ioworkload scenarios — so that no function body exceeds 50 lines. Each extracted helper carries its own comment explaining its role. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13keep View() pure by moving state transitions into Update() handlersPaul Buetow
The dashboard model's View() was mutating sub-model state on every render: it called streamModel.SetFooterVisible() and flameModel.SetViewport() on local copies instead of keeping those fields in sync through Update(). Moved the sync points to the Update() handlers that trigger each change: - handleWindowSize: syncs stream footer visibility alongside SetViewport - handleHelpToggleKey: already updated flame viewport; now also updates stream footer visibility when the help bar is toggled - handleKey: syncs flame viewport when switching to the flame tab, covering the case that window-resize and help-toggle do not Aligned the stream model's initial showFooter value with the dashboard's default showHelp=false in NewModelWithConfig so there is no mismatch on the first render. Also extracted numeric tab shortcuts into the tab registry via ShortcutKey fields so handleShortcutKey no longer needs updating when tabs are added. Updated two tests that bypassed Update() to set dimensions directly; they now use WindowSizeMsg so sub-model viewports are initialised correctly under the new pure-View contract. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13introduce Accumulator interface in statsengine to separate ingestion from ↵Paul Buetow
snapshot-building Define statsengine.Accumulator (Ingest + Reset) to represent the event-accumulation responsibility separately from runtime.SnapshotSource (Snapshot), which handles the read side. This reduces the SRP violation in Engine: callers that only push events now hold an Accumulator; callers that only read statistics hold a SnapshotSource. - Add Accumulator interface and compile-time assertion in statsengine/engine.go - Add EventIngester type alias (= statsengine.Accumulator) in runtime/runtime.go with a compile-time assertion, so callers in the runtime layer can reference the ingestion contract without importing statsengine directly - Split tuiRuntime.engine field into accumulator + snapSource so the event-loop callback holds Accumulator and wireRuntimeBindings passes SnapshotSource to SetDashboardSnapshotSource — making each consumer's dependency explicit - Simplify resetDashboardSnapshotSource in tui.go to cast for interface{ Reset() } independently of Snapshot(), removing the combined ad-hoc interface check Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13refactor: move TraceFilter and tracepoint selector logic out of flags.ConfigPaul Buetow
- Add tracepoints.Selector type with ShouldAttach method and ParseSelector constructor, replacing the raw TracepointsToAttach/TracepointsToExclude regex slices on flags.Config. - Add flags.BuildTraceFilter as a standalone function replacing the Config.TraceFilter() method, keeping filter-building logic out of the config struct. - Remove stale ShouldIAttachTracepoint noise-filter entry from Magefile. - Add selector_test.go with full coverage of ParseSelector and ShouldAttach. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13extract generic sortedWithState helper to deduplicate tab sort logicPaul Buetow
Four near-identical sorted-rows functions (sortedFileSnapshots, sortedDirSnapshots, sortedSyscallSnapshots, sortedProcessTableRows) each repeated the same guard-clone-sort-tiebreak-apply pattern. Replace them with a single generic sortedWithState[T,K] in sort.go that accepts per-tab byKey and tiebreak comparators as parameters. Remove now-unused slices imports from syscalls.go and processes.go. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13refactor: make receiver types consistent per type (pointer receivers)Paul Buetow
fileRankHeap Len/Less/Swap converted to pointer receivers to match Push/Pop; bubbleChart HasNodes and AnimationState Settled converted to pointer receivers to match all other methods on their types. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13use errgroup instead of WaitGroup for concurrent snapshot buildersPaul Buetow
Replace sync.WaitGroup with errgroup.Group in buildSubSnapshots so errors from sub-builders (buildSyscallSnapshots, buildFileSnapshots, buildProcessSnapshots, buildHistogramSnapshot) are captured and propagated rather than silently dropped. Change Engine.Snapshot() to return (*Snapshot, error), update runtime.SnapshotSource and dashboard.SnapshotSource interfaces accordingly, and adjust all callers in tui.go, dashboard/model.go, and the test helpers. Each sub-builder now returns (result, error); the error return is currently always nil but establishes the contract for future validation. The per-type Snapshot() convenience methods (histogram, syscall, file, process) panic on error since they are internal helpers where failure would be a programming bug. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13perf: replace string += concatenation with strings.Builder in TUI render hot ↵Paul Buetow
paths Swap out ad-hoc += string concatenation in the flamegraph toolbar/status lines, dashboard filter summary, bubble/treemap status lines, eventstream view, processes tab, and probes list for strings.Builder, eliminating redundant allocations on every render tick. Also update dashboard/model_test.go fake SnapshotSource implementations to match the updated interface signature. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13fix(pidpicker): eliminate defer-inside-goroutine-in-loop anti-patternsPaul Buetow
Extract the per-pid goroutine body from scanAllThreadsFrom into a named scanThreadsWorker function. This removes both defer statements from the anonymous closure that was spawned inside a for loop: the semaphore release is now an explicit <-sem call immediately after I/O completes, and wg.Done() is called directly after scanThreadsWorker returns. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13fix: avoid int64->int truncation in NumericFilter.EqValue on 32-bit archPaul Buetow
EqValue() previously returned int, silently truncating int64 values that exceed math.MaxInt32 on 32-bit architectures. Change the return type to int64 and update callers (ior.go, filterstack.go) with explicit int() casts that are safe because Linux PID/TID values never exceed 4194304. Add TestEqValueReturnsInt64PreservesLargeValues to guard the fix. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13add compile-time interface assertions for public types (task c4)Paul Buetow
Extend the var _ Interface = (*Concrete)(nil) coverage started in j3 to cover the remaining public types not yet guarded: - *file.FdFile → file.File (file/file.go) - streamrow.Row → globalfilter.Candidate (streamrow/row.go; adds globalfilter import — no cycle since globalfilter does not import streamrow) - *streamrow.RingBuffer → eventstream.Source (tui/eventstream/ringbuffer.go; already a type alias for streamrow.RingBuffer) - *probemanager.Manager → tui/probes.Manager (tui/probes/model.go) - All 9 generated *types.*Event types → event.Event in the new file internal/event/interface_assertions.go (non-generated, lives in the event package which already imports types, so no new import cycle) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13improve unit test coverage to >=60% in probes, common, export, streamrow, ↵Paul Buetow
pidpicker, tui/export Before: probes=30%, tui/common=41%, export=0%, streamrow=25%, pidpicker=59%, tui/export=45% After: probes=89%, tui/common=97%, export=77%, streamrow=100%, pidpicker=73%, tui/export=99% New test files cover RingBuffer push/wrap/reset, Row accessor methods, nil Sequencer safety, SnapshotCSV nil and data paths, helper functions snapValue / snapValueF / trendSummary, all table navigation keys, VisibleTableWindow/ ClampTableCol edge cases, RenderTableHeader/Row, PickerShortHelp, probe modal navigation/search/toggle/view/error paths, truncateText/sanitizeOneLine, export modal View rendering, key navigation, status messages, scanAllThreadsFrom, readThreadInfo guards, formatProcess variants, and clamp helper. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13enforce gofmt formatting and add Fmt/FmtCheck mage targetsPaul Buetow
Run gofmt -w on 9 files that had minor alignment/whitespace drift (pair.go, filter.go, ior_mode_registry.go, ior_mode_test.go, runtime.go, engine.go, dashboard/model.go, flamegraph/model.go, flamegraph/renderer.go). Add two new Mage targets to Magefile.go: - `mage fmt` – rewrites all .go files in-place via go/format - `mage fmtCheck` – dry-run check; fails with a list of offending files, suitable as a CI gate Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13fix: move context.Context to first parameter in startTraceCmd and ↵Paul Buetow
startTraceCmdWithTimeout Per Go convention, context.Context must always be the first parameter. Updated both function signatures and all call sites in tracelifecycle.go and tui_test.go. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13fix: prevent path traversal in TUI stream CSV export filenamePaul Buetow
User-supplied filenames are now sanitised through filepath.Base before being joined with exportDir, so inputs like "../../etc/passwd" can no longer write files outside the intended export directory. Pure directory references ("..") are rejected outright. Two new tests cover both the unit-level sanitisation and the end-to-end exportRowsToCSV path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13cap filterStack undo history to 50 levels to prevent unbounded growthPaul Buetow
Adds maxFilterHistory=50 and evicts the oldest entry from both the history and label-stack slices in filterStack.push whenever the cap is exceeded. Covers the fix with a new table-driven unit test. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13fix: use shell-aware tokenizer in resolveEditorCommand to handle EDITOR ↵Paul Buetow
paths with spaces Replace strings.Fields with a new shellSplit function that implements POSIX-like quoting rules (single-quotes, double-quotes, backslash escapes), so EDITOR values such as '/My Editor/hx' or "/path/with spaces/hx" --wait are correctly parsed rather than mangled into multiple path components. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13fix: add 30s startup timeout to startTraceCmd to prevent indefinite hang on ↵Paul Buetow
BPF attach failure If kernel lock contention or another issue causes BPF probe attachment to stall, the TUI previously remained in the 'Attaching tracepoints...' spinner state forever. startTraceCmdWithTimeout now races the starter goroutine against a configurable deadline (defaultStartupTimeout = 30s) and returns a TracingErrorMsg with a clear message when the deadline expires. The stuck goroutine is cleaned up when the caller cancels the trace context on the next user action (e.g. traceLifecycle.stop). Two new tests cover the timeout and context-cancel paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13split globalfilter presentation and parsing into sub-packagesPaul Buetow
Move ParseDurationNs to globalfilter/parser and move CompareOpSymbol, AppendStringSummary, AppendNumericSummary, FilterSummary to globalfilter/presenter. The domain Filter type retains only matching, equality, clone, and active-predicate logic. All callers updated; tests for the new sub-packages added. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13unify filter summary rendering by reusing globalfilter helpersPaul Buetow
Export AppendStringSummary and AppendNumericSummary from globalfilter so filterstack.go can delegate to the canonical token-formatting logic instead of reimplementing fmt.Sprintf("%s~%s") and fmt.Sprintf("%sOP%s") in appendStringFilterChange / appendNumericFilterChange. Drop the now-unused fmt, strconv, and time imports from filterstack.go. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12add compile-time interface satisfaction assertions for public typesPaul Buetow
Add var _ Interface = (*ConcreteType)(nil) assertions for: - *flamegraph.LiveTrie → runtime.{Snapshotter,Configurator,LiveTrieSource} and tui/flamegraph.{Snapshotter,Configurator,LiveTrieSource} - *probemanager.Manager → runtime.ProbeManager - *statsengine.Engine → runtime.SnapshotSource - *streamrow.RingBuffer → runtime.EventSink - *runtimeBindings (tui) → runtime.TraceRuntimeBindings - *lateBoundDashboardSource → dashboard.SnapshotSource - libbpfTracepointProgram/Module → probemanager.{Program,Attacher} Assertions are grouped close to their interface definitions to avoid introducing new import cycles (runtime already imports all affected packages; tui/flamegraph already imports coreflamegraph). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12split LiveTrieSource into Snapshotter and Configurator (ISP)Paul Buetow
Applies the Interface Segregation Principle to LiveTrieSource in both internal/runtime and internal/tui/flamegraph. The monolithic interface is replaced by two focused sub-interfaces — Snapshotter (read-only: Version, SnapshotJSON, SnapshotTree) and Configurator (mutating: Fields, CountField, Reconfigure, SetCountField, Reset) — which LiveTrieSource then embeds so all existing consumers continue to compile unchanged. The buildSnapshotMsg helper is narrowed to Snapshotter since it only reads snapshot data, preventing accidental mutation from the background goroutine. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12extract dashboard tab framework into Tab registry for OCP compliancePaul Buetow
Introduce tabDescriptor struct and tabDescriptors map in new tabregistry.go. Each tab registers its name, short name, ordered position, allowed viz modes, render function, scroll handler, and optional init tick command. Adding a new tab now requires only a single registry entry — no existing switch/if chains need editing. Key changes: - Tab.String() and tabLabel() use lookupTab() instead of a switch - renderActiveTabContent() dispatches via d.Render (replaces renderActiveTab switch) - handleScrollKey() dispatches via d.HandleScroll (replaces tab switch) - Init() and postKeyTransitionCmd() use d.InitCmd (replaces stream/flame tab checks) - allowedVizModes() delegates to tabAllowedVizModes() from registry - orderedTabs() replaces hardcoded allTabs slice, derived from Position field - bubbleChartFor() helper eliminates 5 repeated switch-on-tab for chart ops - toggleBubbleMetric, tickActiveBubbleChart, moveBubbleSelection, activeBubbleChartHasNodes all use bubbleChartFor() - refreshBubbleData split into two focused functions under 50 lines - Two pre-existing test functions over 50 lines refactored All tests pass; go build ./internal/tui/... clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12extract TUI Model god class into focused sub-controllersPaul Buetow
Split the 1389-line tui.go Model into three focused sub-controllers that each own a single concern: - filterstack.go (filterStack): owns the filter chain, undo history, and label stack; provides push/pop/rebindProcessFilters API so the Model never manipulates filter slices directly. - tracelifecycle.go (traceLifecycle): owns trace start/stop and the active context.CancelFunc; provides beginCmd/stop API; also houses the recorder helpers (recorderStart/Stop/Active/Status) and the auto-reset cycle logic (nextAutoResetInterval, autoResetCycle). - screenrouter.go (screenRouter): owns the picker-return bookmark (pickerReturn) and the applyWindowSizeToPicker helper so screen transition code in tui.go delegates to it. The Model.Update switch is split into dispatchTypedMsg (framework messages) and dispatchAppMsg (app messages) to keep each helper under the 50-line limit. View is split into viewPickerScreen and viewDashboardScreen for the same reason. All functions are ≤50 lines. go test ./internal/tui/... passes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12refactor flamegraph TUI model into focused sub-controllersPaul Buetow
Break the god-class Model in internal/tui/flamegraph/model.go into four focused sub-controllers that each own a single concern: - ZoomNavigator : zoom path, stack, root node, and reset/undo logic - SelectionManager: selected frame index, clamp, traversal, navigation - FrameAnimator : spring animation, frame layout swap, ancestry index - SearchController: search query, match indices, filter-visible set All four are embedded in Model so existing field access (m.selectedIdx, m.zoomPath, etc.) is promoted unchanged, keeping tests and callers intact. Model methods now delegate to the sub-controllers rather than holding all logic inline. Also split RenderTerminalView (88 lines) into computeRenderParams, buildToolbar, buildFilteredStatus, and buildNormalStatus helpers, and extracted buildSnapshotMsg from RefreshFromLiveTrieCmd. All functions are now ≤ 50 lines. All tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12invert dependency: internal no longer imports internal/tuiPaul Buetow
Introduce internal/runtime as a neutral contract package that both the core engine (internal) and the TUI layer (internal/tui) depend on. - internal/runtime: defines TraceStarter, StreamSource, EventSink, LiveTrieSource, SnapshotSource, ProbeManager, RuntimePublisher, RuntimeState, TraceRuntimeBindings, and all context key/helper functions (RuntimeBindingsFromContext, RuntimePublisherFromContext, ContextWithRuntimeBindings, ContextWithTraceFilters, TraceFiltersFromContext). These were previously defined in internal/tui, forcing the core package to import the TUI layer. - internal/streamrow: add RingBuffer (moved from internal/tui/eventstream) so the core tracing engine can use the ring buffer without importing the TUI layer. - internal/tui/eventstream/ringbuffer.go: change to a thin re-export of streamrow.RingBuffer via a type alias, preserving the existing API for all callers within the TUI layer. - internal/tui/tui.go: replace locally-defined interface declarations (TraceStarter, SnapshotSource, ProbeManager, RuntimePublisher, RuntimeState, TraceRuntimeBindings) with type aliases from internal/runtime. Delegate all context helper functions to runtime. - internal/ior.go, internal/ior_bpfsetup.go: remove imports of internal/tui and internal/tui/eventstream; use internal/runtime and internal/streamrow instead. Add SetTUIRunners injection point so the concrete TUI runner functions are wired in by cmd/ior/main.go. - cmd/ior/main.go: call internal.SetTUIRunners with the concrete TUI runner functions before internal.Run, completing the wiring without creating a cycle. - Test files (internal/ior_mode_test.go, internal/bench_pipeline_test.go): updated to use runtime.* and streamrow.* types in place of tui.* and eventstream.* types. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12refactor renderActiveContent to stay under 50-line limitPaul Buetow
Extract renderActiveContentViz (treemap/icicle/bubble dispatch) and renderActiveContentTable (table-with-sort dispatch) from the original 56-line renderActiveContent, reducing it to 15 lines of code. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12refactor: split handleGlobalKeyPress into focused helpers under 50 linesPaul Buetow
Extract handleHelpOverlayKeyPress, handleQuitKeyPress, routeQuitAsEsc, and handleDashboardShortcutKeys from the 89-line handleGlobalKeyPress to comply with the project's 50-line function limit. All existing behaviour is preserved. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12refactor flamegraph model.go: split Update and frameIndexAt below 50-line limitPaul Buetow
Extract handleSearchInput and handleKeyNavigation from Update (111 lines → 33), then split handleKeyNavigation into handleModeKey and handleMovementKey. Extract frameCoordToTargetRow and findFrameAtRow from frameIndexAt (66 lines → 32). All new helpers are ≤ 29 lines; behavior is preserved. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11speed up flame graph TUI under heavy event loadPaul Buetow
Move the per-tick snapshot refresh off the Bubble Tea update goroutine, add a frame ancestry index so navigation and filter helpers run in O(subtree) instead of O(frames), skip refresh and animation while the user is actively pressing keys, and memoize View() output. Keystrokes (pause, filter, navigate) now land within one frame even when the live trie ingests thousands of events per tick. - new SnapshotTree() on LiveTrie bypasses JSON marshal+unmarshal - RefreshFromLiveTrieCmd runs SnapshotTree + layout + ancestry on a background goroutine, coalesced via refreshInFlight, and returns a flameSnapshotReadyMsg the Update loop applies cheaply - driveWindow gate (250 ms after last key press) skips refresh dispatch and snaps frames directly to target without animation while the user is driving - View() caches its rendered string keyed on the inputs that affect output; cache is bypassed during animation Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09show auto-reset countdown and document the cyclePaul Buetow
Renders the next-tick countdown ("12s/30s") in the dashboard chrome so users can see when aggregates will clear, and adds a dedicated H-help line spelling out the cycle keys (off → 10s → 30s → 1m → 2m → 5m). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09refine auto-reset timer cycle and pause on blurPaul Buetow
Two follow-up refinements to the auto-reset timer added in 8da473a. - Hotkey cycle now goes off -> 10s -> 30s -> 60s -> 2m -> 5m -> off, giving the user finer control between 60s and 5m and a quicker starting cadence. - The timer now pauses while the TUI is blurred. SetFocused returns a tea.Cmd that re-arms a fresh tick on focus regain, and bumps the generation counter on every focus change so any tick scheduled before blur is dropped on arrival. autoResetTickCmd and handleAutoResetTick also gate on m.focused as defense in depth. - Dashboard chrome shows 'auto-reset: 30s (paused)' while the timer is enabled but blurred, distinguishing it from the disabled 'off' state. Tests cover the full preset cycle (including custom-value passthrough) and the pause-on-blur lifecycle: stale ticks ignored, current-gen ticks ignored while blurred, focus regain re-arms and fires the reset, no-op focus calls don't churn the generation counter, and the chrome label flips to '(paused)' as expected. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09add auto-reset timer for dashboard aggregatesPaul Buetow
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'.
2026-05-08reconnect flamegraph live trie after in-place filter swapPaul Buetow
PrepareForTraceRestart was designed for the full-restart path, where the dashboard's live-trie reference is rebound when TracingStartedMsg fires. The in-place filter swap skips that message, leaving the flamegraph tab stuck on 'Flame: waiting for data...' until the next real trace restart. Re-bind via SetLiveTrie immediately after PrepareForTraceRestart in both applyGlobalFilter and undoGlobalFilter.
2026-05-08swap global filter in place to skip BPF reattachPaul Buetow
Changing the global filter used to call stopTrace + beginTraceCmd, which detached and re-attached every tracepoint and re-loaded the BPF object. On heavily loaded I/O systems that took several seconds and showed an 'Attaching tracepoints...' overlay each time. The probe set never depends on the global filter (ShouldIAttachTracepoint only reads CLI regex flags), so the restart was gratuitous. Now the eventloop holds its filter behind atomic.Pointer with SetFilter / Filter accessors, and the trace starter registers el.SetFilter via the runtime bindings as a SetLiveFilterSetter callback. applyGlobalFilter and undoGlobalFilter call runtime.applyLiveFilter first; only if no trace is running do they fall back to the full restart path.
2026-05-08add duration metric, tolerate missing tracepoints, ship el8 buildPaul Buetow
- Bubbles, treemap, icicle, and the live flamegraph 'b' cycle now include syscall duration (sum) as a third metric alongside events and bytes. Statsengine snapshots expose TotalLatencyNs to support this. - AttachAll takes an optional warn callback. Production passes one so older kernels that lack newer tracepoints log a warning and keep going instead of aborting startup. - Dockerfile.el8 + scripts/build-with-docker-el8.sh + mage buildDockerEl8 produce ior.el8, a static binary built against Rocky Linux 8 glibc for RHEL/Rocky/Alma 8 hosts. - README.md documents installing mage and the new el8 target.
2026-03-18cleanupPaul Buetow
2026-03-18refactor: extract keyboardState, filterState, processState sub-structs from ↵Paul Buetow
tui.Model (task 429) tui.Model had 33 fields mixing keyboard tracking, filter chain, process selection, and UI presentation concerns (SRP violation). Extract three focused sub-structs: - keyboardState (kb): enhancements, lastEvent{ID,At,WasPress}, suppress{ID,Until} — 7 fields managed by keys_normalize.go - filterState (filter): global filter, history slice, label stack — 3 fields for the trace filter chain - processState (proc): pid, tid, pickerReturn — 3 fields for PID/TID selection and picker navigation Model drops from 33 to 23 top-level fields. All field accesses in tui.go, keys_normalize.go, and tui_test.go are updated accordingly. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-18refactor: split TraceRuntimeBindings into RuntimePublisher and RuntimeState ↵Paul Buetow
(task 427/ISP) TraceRuntimeBindings mixed 4 setter methods (injecting data into TUI) with 4 getter methods (reading persistent TUI-owned state), violating ISP. Split into two focused interfaces: - RuntimePublisher: SetDashboardSnapshotSource, SetEventStreamSource, SetLiveTrie, SetProbeManager — the write/inject side - RuntimeState: StreamBuffer, Recorder, StreamSequencer, FilterEpoch — the read/persistent-state side TraceRuntimeBindings now embeds both, preserving all existing call sites. RuntimePublisherFromContext() added so callers that only inject data do not see the getter surface. Three such callers are narrowed: setupBPFModule, tuiTestFlamesStarter, tuiTestLiveFlamesStarter. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-18refactor: pass flags.Config explicitly, remove flags.Get() from library code ↵Paul Buetow
(task 429) flags.Get() (global mutable singleton) was called inside library packages, coupling them to global state and making tests fragile (DIP violation). - internal.Run() now takes an explicit flags.Config; main.go calls flags.Get() once at the CLI boundary and passes it in. - tui.Run(), RunWithTraceStarter(), RunTestFlamesWithTraceStarter() removed; callers already used the WithConfig variants directly. - tui.NewModel() preserved for test ergonomics but now uses flags.NewFlags() (pure defaults) instead of flags.Get() (global state). - Tests updated to use NewModelWithConfig() with explicit config structs instead of flags.Set*() + NewModel(), eliminating all test-level global-state mutation. flags.Get() is now called only in cmd/ior/main.go, the correct boundary. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-13test: expand parquet recording coveragePaul Buetow
2026-03-13feat: add tui parquet recording controlsPaul Buetow
2026-03-12feat: persist parquet recording across TUI restartsPaul Buetow
2026-03-12refactor: extract shared syscall stream row modelPaul Buetow
2026-03-10dashboard: centralize nil snapshot fallback (task 385)Paul Buetow
2026-03-10dashboard: unify viewport chrome calculation (task 388)Paul Buetow
2026-03-10dashboard: unify offset reanchoring helper (task 394)Paul Buetow
2026-03-10tui: split key normalization and help rendering (task 424)Paul Buetow