From 7b4f74ab11a2504d107372afebdfd77dec59ea42 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Wed, 13 May 2026 19:38:09 +0300 Subject: fix: stop BPF ring-buffer polling goroutine on trace end to prevent leak setupEventChannel now returns the *bpf.RingBuffer handle alongside the event channel. Both callers (runTraceWithContext, runHeadlessParquet) defer rb.Stop() so the libbpfgo polling goroutine and C ring_buffer struct are released promptly when the trace context is cancelled, rather than waiting for bpfModule.Close() to eventually call rb.Close(). rb.Stop() and rb.Close() are both idempotent, so the double-call from the defer and from bpfModule.Close() is safe. Co-Authored-By: Claude Sonnet 4.6 --- internal/ior.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'internal/ior.go') diff --git a/internal/ior.go b/internal/ior.go index 9a60869..02a5b30 100644 --- a/internal/ior.go +++ b/internal/ior.go @@ -499,10 +499,16 @@ func runTraceWithContext(parentCtx context.Context, cfg flags.Config, started ch }() defer releaseBindings() - ch, err := setupEventChannel(bpfModule) + ch, rb, err := setupEventChannel(bpfModule) if err != nil { return err } + // Stop the ring-buffer polling goroutine before the module is closed. + // rb.Stop() signals the background goroutine, drains the channel, and + // waits for the goroutine to exit; bpfModule.Close() (deferred above) + // then calls rb.Close() which frees the C ring_buffer struct. Both are + // idempotent so double-calling is safe. + defer rb.Stop() ctx, cancel, stopSignals := setupTraceContext(parentCtx, cfg, logln) defer cancel() defer stopSignals() -- cgit v1.2.3