summaryrefslogtreecommitdiff
path: root/internal/tui
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-08 20:14:32 +0200
committerPaul Buetow <paul@buetow.org>2026-03-08 20:14:32 +0200
commit4acb116d78588489e79b7e17a79d4609a32fbba7 (patch)
treea43e755c890083fec1fb86169844593cf9a70e13 /internal/tui
parent21aa0cd0f96087fa040750643109c496e7a1b3ee (diff)
task 367: carry full trace filters through TUI context
Diffstat (limited to 'internal/tui')
-rw-r--r--internal/tui/tui.go80
-rw-r--r--internal/tui/tui_test.go24
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)