diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-18 09:21:25 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-18 09:21:25 +0200 |
| commit | 630ea0ff27b8e9ff9287eaaf67660845406a19a6 (patch) | |
| tree | 84a6d4cec5780fd0f677d9b1f22238f775aa21f1 /internal/ior_profiling.go | |
| parent | 3f85aa438bffaad287a450898c44942634944c22 (diff) | |
refactor: split ior.go mega-file into focused files (task 427)
ior.go had 763 lines covering 9+ concerns. Follow the eventloop_*.go
pattern and extract into three focused files:
- ior_bpfsetup.go: libbpfTracepoint{Program,Module} adapter types,
setupBPFModule, setupBPFModuleError, setupEventChannel
- ior_profiling.go: profilingControl type, setupProfiling,
profilingFilesForMode, stop()
- ior_parquet_sink.go: headlessParquetSink type, runHeadlessParquet,
isHeadlessParquetMode, hasHeadlessParquetContentFilters,
headlessParquetTraceConfig; inline parquetMetadata one-liner
ior.go shrinks from 763 → 453 lines, retaining entry, dispatch,
TUI wiring, and core trace execution.
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'internal/ior_profiling.go')
| -rw-r--r-- | internal/ior_profiling.go | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/internal/ior_profiling.go b/internal/ior_profiling.go new file mode 100644 index 0000000..8ca3922 --- /dev/null +++ b/internal/ior_profiling.go @@ -0,0 +1,118 @@ +package internal + +import ( + "context" + "os" + "runtime" + "runtime/pprof" + "runtime/trace" + "sync" + "time" + + "ior/internal/flags" +) + +// profilingControl manages optional CPU, memory, and execution-trace profiling +// for a single tracing run. +type profilingControl struct { + done chan struct{} + enabled bool + cpuProfile *os.File + memProfile *os.File + stopExecTrace func() + stopOnce sync.Once +} + +// setupProfiling starts profiling if cfg.PprofEnable is set and returns a +// control handle. The caller must wait on control.done after the trace ends. +// started is non-nil in TUI mode; nil in plain/flamegraph mode. +func setupProfiling(ctx context.Context, cfg flags.Config, started chan<- struct{}) (*profilingControl, error) { + control := &profilingControl{ + done: make(chan struct{}), + stopExecTrace: func() {}, + } + if !cfg.PprofEnable { + close(control.done) + return control, nil + } + + control.enabled = true + isTUIMode := started != nil + cpuProfilePath, memProfilePath, execTracePath, execTraceDuration := profilingFilesForMode(isTUIMode) + + cpuProfile, err := os.Create(cpuProfilePath) + if err != nil { + return nil, err + } + memProfile, err := os.Create(memProfilePath) + if err != nil { + _ = cpuProfile.Close() + return nil, err + } + control.cpuProfile = cpuProfile + control.memProfile = memProfile + + if execTracePath != "" { + execTraceProfile, err := os.Create(execTracePath) + if err != nil { + _ = cpuProfile.Close() + _ = memProfile.Close() + return nil, err + } + if err := trace.Start(execTraceProfile); err != nil { + _ = cpuProfile.Close() + _ = memProfile.Close() + _ = execTraceProfile.Close() + return nil, err + } + var stopOnce sync.Once + control.stopExecTrace = func() { + stopOnce.Do(func() { + trace.Stop() + _ = execTraceProfile.Close() + }) + } + go func() { + timer := time.NewTimer(execTraceDuration) + defer timer.Stop() + select { + case <-ctx.Done(): + case <-timer.C: + } + control.stopExecTrace() + }() + } + + if err := pprof.StartCPUProfile(cpuProfile); err != nil { + control.stopExecTrace() + _ = cpuProfile.Close() + _ = memProfile.Close() + return nil, err + } + return control, nil +} + +func (p *profilingControl) stop(logln func(...any)) { + p.stopOnce.Do(func() { + if !p.enabled { + return + } + logln("Stopping profiling and writing profile files") + pprof.StopCPUProfile() + runtime.GC() + _ = pprof.WriteHeapProfile(p.memProfile) + p.stopExecTrace() + _ = p.cpuProfile.Close() + _ = p.memProfile.Close() + close(p.done) + }) +} + +// profilingFilesForMode returns the file paths and exec-trace duration to use +// depending on whether the binary is running in TUI mode or plain/flamegraph mode. +func profilingFilesForMode(tuiMode bool) (cpuProfilePath, memProfilePath, execTracePath string, execTraceDuration time.Duration) { + if tuiMode { + return "ior-tui-cpu.prof", "ior-tui-mem.prof", "ior-tui-trace.out", 10 * time.Second + } + return "ior.cpuprofile", "ior.memprofile", "", 0 +} |
