diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-08 20:14:32 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-08 20:14:32 +0200 |
| commit | 4acb116d78588489e79b7e17a79d4609a32fbba7 (patch) | |
| tree | a43e755c890083fec1fb86169844593cf9a70e13 /internal/tui | |
| parent | 21aa0cd0f96087fa040750643109c496e7a1b3ee (diff) | |
task 367: carry full trace filters through TUI context
Diffstat (limited to 'internal/tui')
| -rw-r--r-- | internal/tui/tui.go | 80 | ||||
| -rw-r--r-- | internal/tui/tui_test.go | 24 |
2 files changed, 79 insertions, 25 deletions
diff --git a/internal/tui/tui.go b/internal/tui/tui.go index c375057..510a275 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -11,6 +11,7 @@ import ( coreexport "ior/internal/export" "ior/internal/flags" + "ior/internal/globalfilter" "ior/internal/probemanager" "ior/internal/statsengine" common "ior/internal/tui/common" @@ -76,8 +77,7 @@ type runtimeBindings struct { } type traceFilters struct { - pidFilter int - tidFilter int + filter globalfilter.Filter } func newRuntimeBindings() *runtimeBindings { @@ -157,19 +157,19 @@ func RuntimeBindingsFromContext(ctx context.Context) (TraceRuntimeBindings, bool return bindings, true } -// ContextWithTraceFilters stores the active PID/TID filters for the trace starter. -func ContextWithTraceFilters(ctx context.Context, pidFilter, tidFilter int) context.Context { - filters := traceFilters{pidFilter: pidFilter, tidFilter: tidFilter} +// ContextWithTraceFilters stores the active trace filters for the trace starter. +func ContextWithTraceFilters(ctx context.Context, filter globalfilter.Filter) context.Context { + filters := traceFilters{filter: filter.Clone()} return context.WithValue(ctx, traceFiltersContextKey{}, filters) } -// TraceFiltersFromContext returns the active PID/TID filters when provided by the TUI model. -func TraceFiltersFromContext(ctx context.Context) (pidFilter, tidFilter int, ok bool) { +// TraceFiltersFromContext returns the active trace filters when provided by the TUI model. +func TraceFiltersFromContext(ctx context.Context) (globalfilter.Filter, bool) { filters, ok := ctx.Value(traceFiltersContextKey{}).(traceFilters) if !ok { - return 0, 0, false + return globalfilter.Filter{}, false } - return filters.pidFilter, filters.tidFilter, true + return filters.filter.Clone(), true } // Run starts the TUI program in alternate screen mode. @@ -184,7 +184,7 @@ func RunWithTraceStarter(starter TraceStarter) error { // RunWithTraceStarterConfig starts the TUI with explicit runtime flags. func RunWithTraceStarterConfig(cfg flags.Config, starter TraceStarter) error { - model := newModelWithRuntimeConfig(cfg.PidFilter, cfg.PidFilter, cfg.TidFilter, cfg.TUIExportEnable, starter) + model := newModelWithRuntimeConfig(cfg.PidFilter, filterFromConfig(cfg), cfg.PidFilter, cfg.TidFilter, cfg.TUIExportEnable, starter) program := tea.NewProgram(model) _, err := program.Run() return err @@ -198,7 +198,7 @@ func RunTestFlamesWithTraceStarter(starter TraceStarter) error { // RunTestFlamesWithTraceStarterConfig starts test-flames mode with explicit runtime flags. func RunTestFlamesWithTraceStarterConfig(cfg flags.Config, starter TraceStarter) error { - model := newModelWithRuntimeConfig(1, 1, -1, cfg.TUIExportEnable, starter) + model := newModelWithRuntimeConfig(1, filterFromConfig(cfg), 1, -1, cfg.TUIExportEnable, starter) program := tea.NewProgram(model) _, err := program.Run() return err @@ -230,6 +230,7 @@ type Model struct { pidFilter int tidFilter int + globalFilter globalfilter.Filter pickerReturn *pickerReturnState exportEnabled bool isDark bool @@ -260,10 +261,10 @@ 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, cfg.PidFilter, cfg.TidFilter, cfg.TUIExportEnable, startTrace) + return newModelWithRuntimeConfig(initialPID, filterFromConfig(cfg), cfg.PidFilter, cfg.TidFilter, cfg.TUIExportEnable, startTrace) } -func newModelWithRuntimeConfig(initialPID, startupPidFilter, startupTidFilter int, exportEnabled bool, startTrace TraceStarter) Model { +func newModelWithRuntimeConfig(initialPID int, startupFilter globalfilter.Filter, startupPidFilter, startupTidFilter int, exportEnabled bool, startTrace TraceStarter) Model { common.ApplyPalette(true) syncStylesFromCommon() @@ -300,12 +301,12 @@ func newModelWithRuntimeConfig(initialPID, startupPidFilter, startupTidFilter in keys: keys, spin: spin, startTrace: startTrace, - pidFilter: pidFilter, - tidFilter: tidFilter, + globalFilter: startupFilter.Clone(), exportEnabled: exportEnabled, isDark: true, focused: true, } + model.setProcessFilters(pidFilter, tidFilter) if initialPID > 0 { model.screen = ScreenDashboard @@ -652,10 +653,8 @@ func (m Model) updateActiveModel(msg tea.Msg) (tea.Model, tea.Cmd) { func (m Model) handlePidSelected(msg PidSelectedMsg) (tea.Model, tea.Cmd) { pid := selectedPIDFilter(msg.Pid) m.stopTrace() - m.pidFilter = pid - m.tidFilter = -1 + m.setProcessFilters(pid, -1) m.pickerReturn = nil - m.dashboard.SetPidFilter(pid) m.screen = ScreenDashboard m.attaching = true m.lastErr = nil @@ -669,10 +668,8 @@ func (m Model) handleTidSelected(msg TidSelectedMsg) (tea.Model, tea.Cmd) { pid = msg.Pid } m.stopTrace() - m.pidFilter = pid - m.tidFilter = tid + m.setProcessFilters(pid, tid) m.pickerReturn = nil - m.dashboard.SetPidFilter(pid) m.screen = ScreenDashboard m.attaching = true m.lastErr = nil @@ -741,9 +738,7 @@ func (m Model) cancelPickerToDashboard() (tea.Model, tea.Cmd) { returnState := *m.pickerReturn m.pickerReturn = nil m.stopTrace() - m.pidFilter = returnState.pidFilter - m.tidFilter = returnState.tidFilter - m.dashboard.SetPidFilter(m.pidFilter) + m.setProcessFilters(returnState.pidFilter, returnState.tidFilter) m.screen = ScreenDashboard m.attaching = true m.lastErr = nil @@ -754,7 +749,7 @@ func (m *Model) beginTraceCmd() tea.Cmd { ctx, cancel := context.WithCancel(context.Background()) m.traceStop = cancel ctx = context.WithValue(ctx, runtimeBindingsContextKey{}, m.runtime) - ctx = ContextWithTraceFilters(ctx, m.pidFilter, m.tidFilter) + ctx = ContextWithTraceFilters(ctx, m.globalFilter) return startTraceCmd(m.startTrace, ctx) } @@ -774,6 +769,41 @@ func defaultTraceStarter(context.Context) error { return nil } +func filterFromConfig(cfg flags.Config) globalfilter.Filter { + filter := cfg.GlobalFilter.Clone() + if filter.IsActive() { + return filter + } + if cfg.CommFilter != "" { + filter.Comm = &globalfilter.StringFilter{Pattern: cfg.CommFilter} + } + if cfg.PathFilter != "" { + filter.File = &globalfilter.StringFilter{Pattern: cfg.PathFilter} + } + if cfg.PidFilter > 0 { + filter.PID = eqNumericFilter(cfg.PidFilter) + } + if cfg.TidFilter > 0 { + filter.TID = eqNumericFilter(cfg.TidFilter) + } + return filter +} + +func eqNumericFilter(value int) *globalfilter.NumericFilter { + if value <= 0 { + return nil + } + return &globalfilter.NumericFilter{Op: globalfilter.OpEq, Value: int64(value)} +} + +func (m *Model) setProcessFilters(pid, tid int) { + m.pidFilter = pid + m.tidFilter = tid + m.globalFilter.PID = eqNumericFilter(pid) + m.globalFilter.TID = eqNumericFilter(tid) + m.dashboard.SetPidFilter(pid) +} + func (m *Model) stopTrace() { if m.traceStop != nil { m.traceStop() diff --git a/internal/tui/tui_test.go b/internal/tui/tui_test.go index 85ebb71..608ba41 100644 --- a/internal/tui/tui_test.go +++ b/internal/tui/tui_test.go @@ -11,6 +11,7 @@ import ( "time" coreflamegraph "ior/internal/flamegraph" + "ior/internal/globalfilter" "ior/internal/probemanager" "ior/internal/statsengine" dashboardui "ior/internal/tui/dashboard" @@ -34,6 +35,29 @@ func (f fakeProbeManager) States() []probemanager.ProbeState { return f.states } func (f fakeProbeManager) Toggle(string) error { return nil } func (f fakeProbeManager) ActiveCount() (int, int) { return len(f.states), len(f.states) } +func TestTraceFiltersContextRoundTripClonesPayload(t *testing.T) { + original := globalfilter.Filter{ + Comm: &globalfilter.StringFilter{Pattern: "nginx"}, + File: &globalfilter.StringFilter{Pattern: "/var/log"}, + PID: &globalfilter.NumericFilter{Op: globalfilter.OpEq, Value: 42}, + } + + ctx := ContextWithTraceFilters(context.Background(), original) + original.Comm.Pattern = "mutated" + original.PID.Value = 7 + + got, ok := TraceFiltersFromContext(ctx) + if !ok { + t.Fatalf("expected trace filters in context") + } + if got.Comm == nil || got.Comm.Pattern != "nginx" { + t.Fatalf("expected comm pattern cloned into context, got %+v", got.Comm) + } + if got.PID == nil || got.PID.Value != 42 { + t.Fatalf("expected pid filter cloned into context, got %+v", got.PID) + } +} + func TestPidSelectedTransitionsToDashboardAndSetsPIDFilter(t *testing.T) { flags.SetPidFilter(-1) flags.SetTidFilter(99) |
