| Age | Commit message (Collapse) | Author |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Snapshot.NonIOFamilies, Snapshot.NonIOFamiliesCount, and
types.IsNonIOSyscallFamily encoded a TUI tab concept in core packages.
Move this filtering into internal/tui/dashboard/nonio.go as unexported
helpers so the dashboard owns its own grouping policy and
Snapshot.Families remains the neutral core API.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
|
|
|
|
|
|
|
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
- 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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|