diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-12 22:12:32 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-12 22:12:32 +0300 |
| commit | 8a4cb57703845c1d8ffbc9318a4125818a72a545 (patch) | |
| tree | 5bd9dbc8f77b99de7dced2e867c36ccbf653e533 /internal/runtime/runtime.go | |
| parent | a256cbf9f54ab89aeae0aa9408c1c2b25622fa9d (diff) | |
invert dependency: internal no longer imports internal/tui
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>
Diffstat (limited to 'internal/runtime/runtime.go')
| -rw-r--r-- | internal/runtime/runtime.go | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/internal/runtime/runtime.go b/internal/runtime/runtime.go new file mode 100644 index 0000000..a9e959a --- /dev/null +++ b/internal/runtime/runtime.go @@ -0,0 +1,161 @@ +// Package runtime defines the shared interface contract between the core tracing +// engine (internal) and the TUI layer (internal/tui). By placing these +// interfaces in a neutral sub-package, neither layer imports the other; instead +// both depend on runtime. +package runtime + +import ( + "context" + + "ior/internal/flamegraph" + "ior/internal/globalfilter" + "ior/internal/parquet" + "ior/internal/probemanager" + "ior/internal/statsengine" + "ior/internal/streamrow" +) + +// TraceStarter starts tracing and returns when startup succeeds or fails. +// Long-lived tracing work must continue in background goroutines. +type TraceStarter func(context.Context) error + +// StreamSource is the minimal stream-buffer contract needed by the tracing +// engine and the TUI stream view. It mirrors eventstream.Source but is defined +// here so the core package need not import internal/tui/eventstream. +type StreamSource interface { + Len() int + Snapshot() []streamrow.Row +} + +// EventSink is the write side of the stream buffer: the tracing engine pushes +// events, the TUI reads them via StreamSource. Embedding StreamSource keeps the +// two sides co-located while allowing callers to hold only the read interface. +type EventSink interface { + StreamSource + Push(streamrow.Row) +} + +// SnapshotSource provides statsengine snapshots for the TUI dashboard. +// The core tracing engine passes a *statsengine.Engine; the TUI stores it +// behind this interface so the dashboard can retrieve live snapshots. +type SnapshotSource interface { + Snapshot() *statsengine.Snapshot +} + +// LiveTrieSource is the minimal flamegraph-trie contract needed by the tracing +// engine and the flamegraph TUI model. It mirrors the interface defined in +// internal/tui/flamegraph but lives here so the core package need not import +// that TUI sub-package. Both interfaces are satisfied by *flamegraph.LiveTrie. +type LiveTrieSource interface { + Fields() []string + CountField() string + Reconfigure([]string) error + SetCountField(string) error + Reset() + Version() uint64 + SnapshotJSON() ([]byte, uint64) + SnapshotTree() (*flamegraph.SnapshotNode, uint64) +} + +// ProbeManager exposes runtime probe controls to the TUI probes modal. +// *probemanager.Manager implements this interface. +type ProbeManager interface { + States() []probemanager.ProbeState + Toggle(syscall string) error + ActiveCount() (int, int) +} + +// RuntimePublisher is the write side of the TUI runtime contract. +// A trace starter calls these methods to inject live data into the active TUI. +type RuntimePublisher interface { + // SetDashboardSnapshotSource wires the stats engine into the dashboard. + SetDashboardSnapshotSource(source SnapshotSource) + // SetEventStreamSource wires the stream buffer into the TUI stream view. + SetEventStreamSource(source StreamSource) + // SetLiveTrie wires the live flamegraph trie into the TUI flamegraph view. + SetLiveTrie(liveTrie LiveTrieSource) + // SetProbeManager wires the BPF probe manager into the TUI probes modal. + SetProbeManager(manager ProbeManager) + // SetLiveFilterSetter registers (or, with nil, unregisters) a callback that + // applies a new global filter to the running trace pipeline in-place without + // restarting BPF probes. The trace starter passes its eventloop's SetFilter; + // the TUI calls it on every filter change. + SetLiveFilterSetter(setter func(globalfilter.Filter)) +} + +// RuntimeState is the read side of the TUI runtime contract. +// A trace starter calls these methods to obtain persistent state owned by the TUI. +type RuntimeState interface { + // StreamBuffer returns the TUI-owned ring buffer used for stream events. + StreamBuffer() StreamSource + // Recorder returns the parquet recorder for optional stream recording. + Recorder() *parquet.Recorder + // StreamSequencer returns the shared monotonic sequence counter for stream rows. + StreamSequencer() *streamrow.Sequencer + // FilterEpoch returns the current filter epoch used for parquet recording. + FilterEpoch() uint64 +} + +// TraceRuntimeBindings composes RuntimePublisher and RuntimeState so a trace +// starter can both inject live data and read persistent TUI-owned state. +type TraceRuntimeBindings interface { + RuntimePublisher + RuntimeState +} + +// --- context key types and helpers --- + +// runtimeBindingsKey is an unexported context key for runtime bindings. +type runtimeBindingsKey struct{} + +// traceFiltersKey is an unexported context key for trace filter values. +type traceFiltersKey struct{} + +// traceFilters wraps a cloned filter stored on the context by the TUI model. +type traceFilters struct { + filter globalfilter.Filter +} + +// ContextWithRuntimeBindings stores trace runtime bindings on the context so +// a trace starter can retrieve them via RuntimeBindingsFromContext. +func ContextWithRuntimeBindings(ctx context.Context, bindings TraceRuntimeBindings) context.Context { + return context.WithValue(ctx, runtimeBindingsKey{}, bindings) +} + +// RuntimeBindingsFromContext returns the full TraceRuntimeBindings when the +// context was created by the TUI. Use RuntimePublisherFromContext when only +// write access is needed. +func RuntimeBindingsFromContext(ctx context.Context) (TraceRuntimeBindings, bool) { + bindings, ok := ctx.Value(runtimeBindingsKey{}).(TraceRuntimeBindings) + if !ok || bindings == nil { + return nil, false + } + return bindings, true +} + +// RuntimePublisherFromContext returns only the RuntimePublisher side of the TUI +// bindings. Use this when the caller only injects data and does not need to +// read persistent TUI state. +func RuntimePublisherFromContext(ctx context.Context) (RuntimePublisher, bool) { + bindings, ok := ctx.Value(runtimeBindingsKey{}).(RuntimePublisher) + if !ok || bindings == nil { + return nil, false + } + return bindings, true +} + +// ContextWithTraceFilters stores the active trace filters on the context so +// a trace starter can retrieve them via TraceFiltersFromContext. +func ContextWithTraceFilters(ctx context.Context, filter globalfilter.Filter) context.Context { + filters := traceFilters{filter: filter.Clone()} + return context.WithValue(ctx, traceFiltersKey{}, filters) +} + +// TraceFiltersFromContext returns the active trace filters when provided by the TUI model. +func TraceFiltersFromContext(ctx context.Context) (globalfilter.Filter, bool) { + filters, ok := ctx.Value(traceFiltersKey{}).(traceFilters) + if !ok { + return globalfilter.Filter{}, false + } + return filters.filter.Clone(), true +} |
