diff --git a/containers/registry.go b/containers/registry.go index 82465fe..b239874 100644 --- a/containers/registry.go +++ b/containers/registry.go @@ -716,6 +716,11 @@ func (r *Registry) getOrCreateContainer(pid uint32) *Container { // updateEbpfStatsAndActiveConns reads eBPF maps and updates container stats // directly from the event handler goroutine. Also refreshes active connection gauges. func (r *Registry) updateEbpfStatsAndActiveConns() { + // handleEvents starts before tracer.Run has loaded the eBPF collection; + // iterating its maps before then panics on a nil dereference. + if !r.tracer.Ready() { + return + } // Traffic stats from eBPF maps iter := r.tracer.ActiveConnectionsIterator() cid := ebpftracer.ConnectionId{} diff --git a/ebpftracer/tracer.go b/ebpftracer/tracer.go index 26ec9de..71370c7 100644 --- a/ebpftracer/tracer.go +++ b/ebpftracer/tracer.go @@ -104,6 +104,12 @@ type Tracer struct { ringbufReader *ringbuf.Reader // Ring buffer reader for l7_events links []link.Link uprobes map[string]*ebpf.Program + + // ready is set once Run has loaded the eBPF collection and completed the + // initial process scan. Consumers running before Run completes (e.g. the + // registry event-handler goroutine) must check it before touching + // collection maps. + ready atomic.Bool } func NewTracer(hostNetNs, selfNetNs netns.NsHandle, disableL7Tracing bool) *Tracer { @@ -130,9 +136,16 @@ func (t *Tracer) Run(events chan<- Event) error { if err := t.init(events); err != nil { return err } + t.ready.Store(true) return nil } +// Ready reports whether Run has loaded the eBPF collection and finished the +// initial process scan. Map iterators must not be used until this returns true. +func (t *Tracer) Ready() bool { + return t.ready.Load() +} + func (t *Tracer) Close() { for _, p := range t.uprobes { _ = p.Close()