From e4a750b33a0b34f6630ad1ff5067e042cf34ef01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Geyslan=20Greg=C3=B3rio?= Date: Fri, 17 Jan 2025 07:42:59 -0300 Subject: [PATCH] perf(controlplane): introduce signal pool It helps to reduce the stack dynamic growth and the number of allocations, which is good for performance. --- pkg/ebpf/controlplane/controller.go | 34 ++++++++++++++++++++++++++--- pkg/ebpf/controlplane/signal.go | 4 ++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/pkg/ebpf/controlplane/controller.go b/pkg/ebpf/controlplane/controller.go index 2112d52af753..981496054078 100644 --- a/pkg/ebpf/controlplane/controller.go +++ b/pkg/ebpf/controlplane/controller.go @@ -3,6 +3,7 @@ package controlplane import ( "context" "fmt" + "sync" "time" "github.com/aquasecurity/libbpfgo" @@ -25,6 +26,7 @@ type Controller struct { lostSignalChan chan uint64 bpfModule *libbpfgo.Module signalBuffer *libbpfgo.PerfBuffer + signalPool *sync.Pool cgroupManager *containers.Containers processTree *proctree.ProcessTree enrichDisabled bool @@ -43,6 +45,11 @@ func NewController( signalChan: make(chan []byte, 100), lostSignalChan: make(chan uint64), bpfModule: bpfModule, + signalPool: &sync.Pool{ + New: func() interface{} { + return &signal{} + }, + }, cgroupManager: cgroupManager, processTree: procTree, enrichDisabled: enrichDisabled, @@ -69,16 +76,22 @@ func (ctrl *Controller) Run(ctx context.Context) { for { select { case signalData := <-ctrl.signalChan: - signal := signal{} + signal := ctrl.getSignalFromPool() + + // NOTE: override all the fields of the signal, to avoid any previous data. err := signal.Unmarshal(signalData) if err != nil { logger.Errorw("error unmarshaling signal ebpf buffer", "error", err) + ctrl.putSignalInPool(signal) continue } + err = ctrl.processSignal(signal) if err != nil { logger.Errorw("error processing control plane signal", "error", err) } + + ctrl.putSignalInPool(signal) case lost := <-ctrl.lostSignalChan: logger.Warnw(fmt.Sprintf("Lost %d control plane signals", lost)) case <-ctrl.ctx.Done(): @@ -93,8 +106,10 @@ func (ctrl *Controller) Stop() error { return nil } +// Private + // processSignal processes a signal from the control plane. -func (ctrl *Controller) processSignal(signal signal) error { +func (ctrl *Controller) processSignal(signal *signal) error { switch signal.id { case events.SignalCgroupMkdir: return ctrl.processCgroupMkdir(signal.args) @@ -111,7 +126,20 @@ func (ctrl *Controller) processSignal(signal signal) error { return nil } -// Private +// getSignalFromPool gets a signal from the pool. +// signal certainly contains old data, so it must be updated before use. +func (ctrl *Controller) getSignalFromPool() *signal { + // revive:disable:unchecked-type-assertion + sig := ctrl.signalPool.Get().(*signal) + // revive:enable:unchecked-type-assertion + + return sig +} + +// putSignalInPool puts a signal back in the pool. +func (ctrl *Controller) putSignalInPool(sig *signal) { + ctrl.signalPool.Put(sig) +} // debug prints the process tree every 5 seconds (for debugging purposes). func (ctrl *Controller) debug(enable bool) { diff --git a/pkg/ebpf/controlplane/signal.go b/pkg/ebpf/controlplane/signal.go index da5aa9cea294..33360d82c981 100644 --- a/pkg/ebpf/controlplane/signal.go +++ b/pkg/ebpf/controlplane/signal.go @@ -19,7 +19,9 @@ func (sig *signal) Unmarshal(buffer []byte) error { if err != nil { return errfmt.Errorf("failed to decode signal event ID: %v", err) } + sig.id = events.ID(eventIdUint32) + var argnum uint8 err = ebpfDecoder.DecodeUint8(&argnum) if err != nil { @@ -33,7 +35,9 @@ func (sig *signal) Unmarshal(buffer []byte) error { evtFields := eventDefinition.GetFields() evtName := eventDefinition.GetName() + sig.args = make([]trace.Argument, len(evtFields)) + err = ebpfDecoder.DecodeArguments(sig.args, int(argnum), evtFields, evtName, sig.id) if err != nil { return errfmt.Errorf("failed to decode signal arguments: %v", err)