summaryrefslogtreecommitdiff
path: root/internal/ior_profiling.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-18 09:21:25 +0200
committerPaul Buetow <paul@buetow.org>2026-03-18 09:21:25 +0200
commit630ea0ff27b8e9ff9287eaaf67660845406a19a6 (patch)
tree84a6d4cec5780fd0f677d9b1f22238f775aa21f1 /internal/ior_profiling.go
parent3f85aa438bffaad287a450898c44942634944c22 (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.go118
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
+}