From 040e62e81bde284c6ba5ca59e9bd262c92de2985 Mon Sep 17 00:00:00 2001 From: Rafael Roquetto Date: Thu, 26 Feb 2026 10:21:32 -0700 Subject: [PATCH 1/5] Allow loading multiple specs per tracer --- pkg/ebpf/tracer.go | 15 +-- pkg/ebpf/tracer_linux.go | 114 ++++++++++++------ .../ebpf/generictracer/generictracer.go | 12 +- .../generictracer/generictracer_notlinux.go | 6 +- pkg/internal/ebpf/gotracer/gotracer.go | 18 +-- pkg/internal/ebpf/gpuevent/gpuevent.go | 16 ++- pkg/internal/ebpf/logenricher/logenricher.go | 18 +-- .../ebpf/logenricher/logenricher_notlinux.go | 6 +- pkg/internal/ebpf/logger/logger.go | 18 +-- pkg/internal/ebpf/tpinjector/tpinjector.go | 23 ++-- .../ebpf/tpinjector/tpinjector_notlinux.go | 6 +- .../ebpf/tpinjector/tpinjector_test.go | 110 ++++++++--------- pkg/internal/ebpf/watcher/watcher.go | 18 +-- 13 files changed, 222 insertions(+), 158 deletions(-) diff --git a/pkg/ebpf/tracer.go b/pkg/ebpf/tracer.go index 8213688b50..688c64552e 100644 --- a/pkg/ebpf/tracer.go +++ b/pkg/ebpf/tracer.go @@ -64,13 +64,14 @@ type PIDsAccounter interface { } type CommonTracer interface { - // Load the bpf object that is generated by the bpf2go compiler - Load() (*ebpf.CollectionSpec, error) + // LoadSpecs loads one or more BPF object specs that are generated by the bpf2go compiler. + LoadSpecs() ([]*ebpf.CollectionSpec, error) // AddCloser adds io.Closer instances that need to be invoked when the // Run function ends. AddCloser(c ...io.Closer) - // BpfObjects that are created by the bpf2go compiler - BpfObjects() any + // BpfObjects returns a slice of object pointers matching the specs returned by LoadSpecs(). + // Each object will be populated by spec.LoadAndAssign() for the corresponding spec. + BpfObjects() []any // Sets up any tail call tables if the BPF program has it SetupTailCalls() } @@ -87,9 +88,9 @@ type KprobesTracer interface { type Tracer interface { PIDsAccounter KprobesTracer - // Constants returns a map of constants to be overridden into the eBPF program. - // The key is the constant name and the value is the value to overwrite. - Constants() map[string]any + // Constants returns a slice of constant maps, one per spec from LoadSpecs(). + // Each map contains constants to be overridden into the corresponding eBPF program. + Constants() []map[string]any // GoProbes returns a slice with the name of Go functions that need to be inspected // in the executable, as well as the eBPF programs that optionally need to be // inserted as the Go function start and end probes diff --git a/pkg/ebpf/tracer_linux.go b/pkg/ebpf/tracer_linux.go index 06c353edd6..7e20089127 100644 --- a/pkg/ebpf/tracer_linux.go +++ b/pkg/ebpf/tracer_linux.go @@ -99,6 +99,34 @@ func resolveMaps(eventContext *common.EBPFEventContext, spec *ebpf.CollectionSpe return &collOpts, nil } +func loadSpec(eventContext *common.EBPFEventContext, spec *ebpf.CollectionSpec, constants map[string]any, obj any, otelBPFFSPath string, idx int) error { + if err := ebpfconvenience.RewriteConstants(spec, constants); err != nil { + return fmt.Errorf("rewriting BPF constants for spec %d: %w", idx, err) + } + + collOpts, err := resolveMaps(eventContext, spec) + if err != nil { + return fmt.Errorf("resolving maps for spec %d: %w", idx, err) + } + + collOpts.Programs = ebpf.ProgramOptions{LogSizeStart: 640 * 1024} + collOpts.Maps = ebpf.MapOptions{PinPath: otelBPFFSPath} + + if err := spec.LoadAndAssign(obj, collOpts); err != nil { + return fmt.Errorf("loading spec %d: %w", idx, err) + } + + return nil +} + +func closeLoadedSpecs(objs []any, count int) { + for j := 0; j < count; j++ { + if c, ok := objs[j].(io.Closer); ok { + c.Close() + } + } +} + func unloadInternalMaps(eventContext *common.EBPFEventContext) { eventContext.MapsLock.Lock() defer eventContext.MapsLock.Unlock() @@ -173,18 +201,6 @@ func (pt *ProcessTracer) Run(ctx context.Context, ebpfEventContext *common.EBPFE } } -func (pt *ProcessTracer) loadSpec(p Tracer) (*ebpf.CollectionSpec, error) { - spec, err := p.Load() - if err != nil { - return nil, fmt.Errorf("loading eBPF program: %w", err) - } - if err := ebpfconvenience.RewriteConstants(spec, p.Constants()); err != nil { - return nil, fmt.Errorf("rewriting BPF constants definition: %w", err) - } - - return spec, nil -} - func (pt *ProcessTracer) makeOtelBPFFSPath() (string, error) { otelPath := path.Join(pt.bpffsPath, "otel") @@ -195,20 +211,28 @@ func (pt *ProcessTracer) makeOtelBPFFSPath() (string, error) { return otelPath, nil } -func (pt *ProcessTracer) setupBPFFS(spec *ebpf.CollectionSpec) string { +func (pt *ProcessTracer) setupOtelBPFFFSPath(specs []*ebpf.CollectionSpec) string { + // Set up BPF FS path once for all specs otelBPFFSPath, err := pt.makeOtelBPFFSPath() if err == nil { return otelBPFFSPath } - slog.Warn("creating OTEL namespace in bpffs failed (is bpffs mounted?)", "bpffs_path", pt.bpffsPath, "err", err) - slog.Warn("OBI will still work, but features depending on pinned maps (e.g., log enricher, profile correlation) will be disabled") + log := ptlog() + + log.Warn("creating OTEL namespace in bpffs failed (is bpffs mounted?)", + "bpffs_path", pt.bpffsPath, "err", err) - for _, v := range spec.Maps { - if v.Pinning == ebpf.PinByName { - v.Pinning = ebpf.PinNone - v.MaxEntries = 1 + log.Warn("OBI will still work, but features depending on pinned maps (e.g., log enricher, profile correlation) will be disabled") + + // disable pinning for ALL specs + for _, spec := range specs { + for _, v := range spec.Maps { + if v.Pinning == ebpf.PinByName { + v.Pinning = ebpf.PinNone + v.MaxEntries = 1 + } } } @@ -216,22 +240,33 @@ func (pt *ProcessTracer) setupBPFFS(spec *ebpf.CollectionSpec) string { } func (pt *ProcessTracer) loadAndAssign(eventContext *common.EBPFEventContext, p Tracer) error { - spec, err := pt.loadSpec(p) + specs, err := p.LoadSpecs() if err != nil { - return err + return fmt.Errorf("loading eBPF program specs: %w", err) } - collOpts, err := resolveMaps(eventContext, spec) - if err != nil { - return err + // Set up BPF FS path once for all specs + otelBPFFSPath := pt.setupOtelBPFFFSPath(specs) + + objs := p.BpfObjects() + constants := p.Constants() + + if len(objs) != len(specs) { + return fmt.Errorf("BpfObjects() returned %d objects but LoadSpecs() returned %d specs", len(objs), len(specs)) } - otelBPFFSPath := pt.setupBPFFS(spec) + if len(constants) != len(specs) { + return fmt.Errorf("Constants() returned %d maps but LoadSpecs() returned %d specs", len(constants), len(specs)) + } - collOpts.Programs = ebpf.ProgramOptions{LogSizeStart: 640 * 1024} - collOpts.Maps = ebpf.MapOptions{PinPath: otelBPFFSPath} + for i, spec := range specs { + if err := loadSpec(eventContext, spec, constants[i], objs[i], otelBPFFSPath, i); err != nil { + closeLoadedSpecs(objs, i) + return err + } + } - return spec.LoadAndAssign(p.BpfObjects(), collOpts) + return nil } func (pt *ProcessTracer) loadTracer(eventContext *common.EBPFEventContext, p Tracer, log *slog.Logger) error { @@ -414,19 +449,26 @@ func RunUtilityTracer(ctx context.Context, eventContext *common.EBPFEventContext i := instrumenter{} plog := ptlog() plog.Debug("loading independent eBPF program") - spec, err := p.Load() + specs, err := p.LoadSpecs() if err != nil { - return fmt.Errorf("loading eBPF program: %w", err) + return fmt.Errorf("loading eBPF program specs: %w", err) } - collOpts, err := resolveMaps(eventContext, spec) - if err != nil { - return err + objs := p.BpfObjects() + if len(objs) != len(specs) { + return fmt.Errorf("BpfObjects() returned %d objects but LoadSpecs() returned %d specs", len(objs), len(specs)) } - if err := spec.LoadAndAssign(p.BpfObjects(), collOpts); err != nil { - printVerifierErrorInfo(err) - return fmt.Errorf("loading and assigning BPF objects: %w", err) + for idx, spec := range specs { + collOpts, err := resolveMaps(eventContext, spec) + if err != nil { + return err + } + + if err := spec.LoadAndAssign(objs[idx], collOpts); err != nil { + printVerifierErrorInfo(err) + return fmt.Errorf("loading and assigning BPF objects: %w", err) + } } if err := i.kprobes(p); err != nil { diff --git a/pkg/internal/ebpf/generictracer/generictracer.go b/pkg/internal/ebpf/generictracer/generictracer.go index bcd7f31a9b..7bc78e8408 100644 --- a/pkg/internal/ebpf/generictracer/generictracer.go +++ b/pkg/internal/ebpf/generictracer/generictracer.go @@ -127,7 +127,7 @@ func (p *Tracer) BlockPID(pid app.PID, ns uint32) { p.rebuildValidPids() } -func (p *Tracer) Load() (*ebpf.CollectionSpec, error) { +func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { if p.cfg.EBPF.TrackRequestHeaders || p.cfg.EBPF.ContextPropagation.IsEnabled() { p.log.Info("Enabling trace information parsing", "bpf_loop_enabled", ebpfcommon.SupportsEBPFLoops(p.log, p.cfg.EBPF.OverrideBPFLoopEnabled)) @@ -140,7 +140,7 @@ func (p *Tracer) Load() (*ebpf.CollectionSpec, error) { ebpfcommon.FixupSpec(spec, p.cfg.EBPF.OverrideBPFLoopEnabled) - return spec, err + return []*ebpf.CollectionSpec{spec}, err } func (p *Tracer) SetupTailCalls() { @@ -162,7 +162,7 @@ func (p *Tracer) SetupTailCalls() { } } -func (p *Tracer) Constants() map[string]any { +func (p *Tracer) Constants() []map[string]any { m := make(map[string]any, 2) m["wakeup_data_bytes"] = uint32(p.cfg.EBPF.WakeupLen) * uint32(unsafe.Sizeof(ebpfcommon.HTTPRequestTrace{})) @@ -205,15 +205,15 @@ func (p *Tracer) Constants() map[string]any { m["g_bpf_debug"] = p.cfg.EBPF.BpfDebug m["g_bpf_traceparent_enabled"] = p.cfg.EBPF.TrackRequestHeaders || p.cfg.EBPF.ContextPropagation.IsEnabled() - return m + return []map[string]any{m} } func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} -func (p *Tracer) BpfObjects() any { - return &p.bpfObjects +func (p *Tracer) BpfObjects() []any { + return []any{&p.bpfObjects} } func (p *Tracer) AddCloser(c ...io.Closer) { diff --git a/pkg/internal/ebpf/generictracer/generictracer_notlinux.go b/pkg/internal/ebpf/generictracer/generictracer_notlinux.go index fc25eca7a9..afe6e2ba71 100644 --- a/pkg/internal/ebpf/generictracer/generictracer_notlinux.go +++ b/pkg/internal/ebpf/generictracer/generictracer_notlinux.go @@ -29,8 +29,8 @@ type Tracer struct{} func New(_ ebpfcommon.ServiceFilter, _ *obi.Config, _ imetrics.Reporter) *Tracer { return nil } func (p *Tracer) AllowPID(_ app.PID, _ uint32, _ *svc.Attrs) {} func (p *Tracer) BlockPID(_ app.PID, _ uint32) {} -func (p *Tracer) Load() (*ebpf.CollectionSpec, error) { return nil, nil } -func (p *Tracer) BpfObjects() any { return nil } +func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { return nil, nil } +func (p *Tracer) BpfObjects() []any { return nil } func (p *Tracer) AddCloser(_ ...io.Closer) {} func (p *Tracer) GoProbes() map[string][]*ebpfcommon.ProbeDesc { return nil } func (p *Tracer) KProbes() map[string]ebpfcommon.ProbeDesc { return nil } @@ -47,7 +47,7 @@ func (p *Tracer) UnlinkInstrumentedLib(_ uint64) func (p *Tracer) AlreadyInstrumentedLib(_ uint64) bool { return false } func (p *Tracer) Run(_ context.Context, _ *ebpfcommon.EBPFEventContext, _ *msg.Queue[[]request.Span]) { } -func (p *Tracer) Constants() map[string]any { return nil } +func (p *Tracer) Constants() []map[string]any { return nil } func (p *Tracer) SetupTailCalls() {} func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} diff --git a/pkg/internal/ebpf/gotracer/gotracer.go b/pkg/internal/ebpf/gotracer/gotracer.go index 000cc0071b..ff09bbd204 100644 --- a/pkg/internal/ebpf/gotracer/gotracer.go +++ b/pkg/internal/ebpf/gotracer/gotracer.go @@ -84,24 +84,28 @@ func (p *Tracer) supportsContextPropagation() bool { return !ebpfcommon.IntegrityModeOverride && ebpfcommon.SupportsContextPropagationWithProbe(p.log) } -func (p *Tracer) Load() (*ebpf.CollectionSpec, error) { +func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { if !p.supportsContextPropagation() { p.log.Info("Kernel in lockdown mode or missing CAP_SYS_ADMIN.") } - return LoadBpf() + spec, err := LoadBpf() + if err != nil { + return nil, err + } + return []*ebpf.CollectionSpec{spec}, nil } func (p *Tracer) SetupTailCalls() { } -func (p *Tracer) Constants() map[string]any { +func (p *Tracer) Constants() []map[string]any { blackBoxCP := uint32(0) if p.cfg.DisableBlackBoxCP { blackBoxCP = uint32(1) } - return map[string]any{ + return []map[string]any{{ "g_bpf_debug": p.cfg.BpfDebug, "g_bpf_header_propagation": p.supportsContextPropagation(), "wakeup_data_bytes": uint32(p.cfg.WakeupLen) * uint32(unsafe.Sizeof(ebpfcommon.HTTPRequestTrace{})), @@ -117,7 +121,7 @@ func (p *Tracer) Constants() map[string]any { "attr_type_stringslice": uint64(attribute.STRINGSLICE), "g_bpf_traceparent_enabled": true, "g_bpf_loop_enabled": p.supportsBPFLoop, - } + }} } func (p *Tracer) RegisterOffsets(fileInfo *exec.FileInfo, offsets *goexec.Offsets) { @@ -246,8 +250,8 @@ func (p *Tracer) RegisterOffsets(fileInfo *exec.FileInfo, offsets *goexec.Offset func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} -func (p *Tracer) BpfObjects() any { - return &p.bpfObjects +func (p *Tracer) BpfObjects() []any { + return []any{&p.bpfObjects} } func (p *Tracer) AddCloser(c ...io.Closer) { diff --git a/pkg/internal/ebpf/gpuevent/gpuevent.go b/pkg/internal/ebpf/gpuevent/gpuevent.go index 01d1dfb750..4e4bc08977 100644 --- a/pkg/internal/ebpf/gpuevent/gpuevent.go +++ b/pkg/internal/ebpf/gpuevent/gpuevent.go @@ -86,11 +86,15 @@ func (p *Tracer) BlockPID(pid app.PID, ns uint32) { p.pidsFilter.BlockPID(pid, ns) } -func (p *Tracer) Load() (*ebpf.CollectionSpec, error) { - return LoadBpf() +func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { + spec, err := LoadBpf() + if err != nil { + return nil, err + } + return []*ebpf.CollectionSpec{spec}, nil } -func (p *Tracer) Constants() map[string]any { +func (p *Tracer) Constants() []map[string]any { m := make(map[string]any, 2) // The eBPF side does some basic filtering of events that do not belong to @@ -103,15 +107,15 @@ func (p *Tracer) Constants() map[string]any { } m["g_bpf_debug"] = p.cfg.EBPF.BpfDebug - return m + return []map[string]any{m} } func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} -func (p *Tracer) BpfObjects() any { - return &p.bpfObjects +func (p *Tracer) BpfObjects() []any { + return []any{&p.bpfObjects} } func (p *Tracer) AddCloser(c ...io.Closer) { diff --git a/pkg/internal/ebpf/logenricher/logenricher.go b/pkg/internal/ebpf/logenricher/logenricher.go index 393bfe57cd..c388236342 100644 --- a/pkg/internal/ebpf/logenricher/logenricher.go +++ b/pkg/internal/ebpf/logenricher/logenricher.go @@ -90,24 +90,28 @@ func New(cfg *obi.Config) *Tracer { return tr } -func (p *Tracer) Load() (*ebpf.CollectionSpec, error) { - return LoadBpf() +func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { + spec, err := LoadBpf() + if err != nil { + return nil, err + } + return []*ebpf.CollectionSpec{spec}, nil } func (p *Tracer) SetupTailCalls() {} -func (p *Tracer) Constants() map[string]any { - return map[string]any{ +func (p *Tracer) Constants() []map[string]any { + return []map[string]any{{ "g_bpf_debug": p.cfg.EBPF.BpfDebug, - } + }} } func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} -func (p *Tracer) BpfObjects() any { - return &p.bpfObjects +func (p *Tracer) BpfObjects() []any { + return []any{&p.bpfObjects} } func (p *Tracer) AddCloser(c ...io.Closer) { diff --git a/pkg/internal/ebpf/logenricher/logenricher_notlinux.go b/pkg/internal/ebpf/logenricher/logenricher_notlinux.go index 2f8ef2448b..de051d01a1 100644 --- a/pkg/internal/ebpf/logenricher/logenricher_notlinux.go +++ b/pkg/internal/ebpf/logenricher/logenricher_notlinux.go @@ -26,8 +26,8 @@ type Tracer struct{} func New(_ *obi.Config) *Tracer { return nil } func (p *Tracer) AllowPID(_ app.PID, _ uint32, _ *svc.Attrs) {} func (p *Tracer) BlockPID(_ app.PID, _ uint32) {} -func (p *Tracer) Load() (*ebpf.CollectionSpec, error) { return nil, nil } -func (p *Tracer) BpfObjects() any { return nil } +func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { return nil, nil } +func (p *Tracer) BpfObjects() []any { return nil } func (p *Tracer) AddCloser(_ ...io.Closer) {} func (p *Tracer) GoProbes() map[string][]*ebpfcommon.ProbeDesc { return nil } func (p *Tracer) KProbes() map[string]ebpfcommon.ProbeDesc { return nil } @@ -44,7 +44,7 @@ func (p *Tracer) UnlinkInstrumentedLib(_ uint64) {} func (p *Tracer) AlreadyInstrumentedLib(_ uint64) bool { return false } func (p *Tracer) Run(_ context.Context, _ *ebpfcommon.EBPFEventContext, _ *msg.Queue[[]request.Span]) { } -func (p *Tracer) Constants() map[string]any { return nil } +func (p *Tracer) Constants() []map[string]any { return nil } func (p *Tracer) SetupTailCalls() {} func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} diff --git a/pkg/internal/ebpf/logger/logger.go b/pkg/internal/ebpf/logger/logger.go index ebe568e325..3b2b43353c 100644 --- a/pkg/internal/ebpf/logger/logger.go +++ b/pkg/internal/ebpf/logger/logger.go @@ -42,15 +42,19 @@ func New(cfg *obi.Config) *BPFLogger { } } -func (p *BPFLogger) Load() (*ebpf.CollectionSpec, error) { +func (p *BPFLogger) LoadSpecs() ([]*ebpf.CollectionSpec, error) { if p.cfg.EBPF.BpfDebug { - return LoadBpf() + spec, err := LoadBpf() + if err != nil { + return nil, err + } + return []*ebpf.CollectionSpec{spec}, nil } return nil, errors.New("BPF debug is not enabled") } -func (p *BPFLogger) BpfObjects() any { - return &p.bpfObjects +func (p *BPFLogger) BpfObjects() []any { + return []any{&p.bpfObjects} } func (p *BPFLogger) AddCloser(c ...io.Closer) { @@ -65,10 +69,10 @@ func (p *BPFLogger) Tracepoints() map[string]ebpfcommon.ProbeDesc { return nil } -func (p *BPFLogger) Constants() map[string]any { - return map[string]any{ +func (p *BPFLogger) Constants() []map[string]any { + return []map[string]any{{ "g_bpf_debug": p.cfg.EBPF.BpfDebug, - } + }} } func (p *BPFLogger) SetupTailCalls() {} diff --git a/pkg/internal/ebpf/tpinjector/tpinjector.go b/pkg/internal/ebpf/tpinjector/tpinjector.go index 2ea4ca4f5a..a7ec818c1e 100644 --- a/pkg/internal/ebpf/tpinjector/tpinjector.go +++ b/pkg/internal/ebpf/tpinjector/tpinjector.go @@ -45,20 +45,20 @@ func (p *Tracer) AllowPID(app.PID, uint32, *svc.Attrs) {} func (p *Tracer) BlockPID(app.PID, uint32) {} -func (p *Tracer) Load() (*ebpf.CollectionSpec, error) { - return LoadBpf() +func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { + spec, err := LoadBpf() + if err != nil { + return nil, err + } + return []*ebpf.CollectionSpec{spec}, nil } func (p *Tracer) SetupTailCalls() { } -func (p *Tracer) Constants() map[string]any { - m := make(map[string]any, 3) +func (p *Tracer) Constants() []map[string]any { + m := make(map[string]any, 4) - // The eBPF side does some basic filtering of events that do not belong to - // processes which we monitor. We filter more accurately in the userspace, but - // for performance reasons we enable the PID based filtering in eBPF. - // This must match httpfltr.go, otherwise we get partial events in userspace. if p.cfg.Discovery.BPFPidFilterOff { m["filter_pids"] = int32(0) } else { @@ -67,7 +67,6 @@ func (p *Tracer) Constants() map[string]any { m["max_transaction_time"] = uint64(p.cfg.EBPF.MaxTransactionTime.Nanoseconds()) - // Set injection flags based on context propagation configuration flags := uint32(0) if p.cfg.EBPF.ContextPropagation.HasHeaders() { flags |= 1 // k_inject_http_headers @@ -78,15 +77,15 @@ func (p *Tracer) Constants() map[string]any { m["inject_flags"] = flags m["g_bpf_debug"] = p.cfg.EBPF.BpfDebug - return m + return []map[string]any{m} } func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} -func (p *Tracer) BpfObjects() any { - return &p.bpfObjects +func (p *Tracer) BpfObjects() []any { + return []any{&p.bpfObjects} } func (p *Tracer) AddCloser(c ...io.Closer) { diff --git a/pkg/internal/ebpf/tpinjector/tpinjector_notlinux.go b/pkg/internal/ebpf/tpinjector/tpinjector_notlinux.go index 745a361048..29ad2bec65 100644 --- a/pkg/internal/ebpf/tpinjector/tpinjector_notlinux.go +++ b/pkg/internal/ebpf/tpinjector/tpinjector_notlinux.go @@ -28,8 +28,8 @@ type Tracer struct{} func New(_ *obi.Config) *Tracer { return nil } func (p *Tracer) AllowPID(_ app.PID, _ uint32, _ *svc.Attrs) {} func (p *Tracer) BlockPID(_ app.PID, _ uint32) {} -func (p *Tracer) Load() (*ebpf.CollectionSpec, error) { return nil, nil } -func (p *Tracer) BpfObjects() any { return nil } +func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { return nil, nil } +func (p *Tracer) BpfObjects() []any { return nil } func (p *Tracer) AddCloser(_ ...io.Closer) {} func (p *Tracer) GoProbes() map[string][]*ebpfcommon.ProbeDesc { return nil } func (p *Tracer) KProbes() map[string]ebpfcommon.ProbeDesc { return nil } @@ -46,7 +46,7 @@ func (p *Tracer) UnlinkInstrumentedLib(_ uint64) {} func (p *Tracer) AlreadyInstrumentedLib(_ uint64) bool { return false } func (p *Tracer) Run(_ context.Context, _ *ebpfcommon.EBPFEventContext, _ *msg.Queue[[]request.Span]) { } -func (p *Tracer) Constants() map[string]any { return nil } +func (p *Tracer) Constants() []map[string]any { return nil } func (p *Tracer) SetupTailCalls() {} func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} diff --git a/pkg/internal/ebpf/tpinjector/tpinjector_test.go b/pkg/internal/ebpf/tpinjector/tpinjector_test.go index b90cdd1b6f..54dcecfa8a 100644 --- a/pkg/internal/ebpf/tpinjector/tpinjector_test.go +++ b/pkg/internal/ebpf/tpinjector/tpinjector_test.go @@ -17,85 +17,72 @@ import ( "go.opentelemetry.io/obi/pkg/obi" ) -func TestTracer_Constants_InjectFlags(t *testing.T) { +// tpinjector has a single BPF spec; all constants live in spec 0. +const expectedSpecCount = 1 + +func TestTracer_Constants(t *testing.T) { tests := []struct { name string contextPropagation string + bpfPidFilterOff bool expectedInjectFlags uint32 + expectedFilterPids int32 }{ { - name: "disabled", + name: "all disabled, filter on", contextPropagation: "disabled", - expectedInjectFlags: 0, // neither HTTP headers nor TCP options + bpfPidFilterOff: false, + expectedInjectFlags: 0, + expectedFilterPids: 1, }, { name: "headers only", contextPropagation: "headers", + bpfPidFilterOff: false, expectedInjectFlags: 1, // k_inject_http_headers + expectedFilterPids: 1, }, { name: "tcp only", contextPropagation: "tcp", + bpfPidFilterOff: false, expectedInjectFlags: 2, // k_inject_tcp_options + expectedFilterPids: 1, }, { name: "headers and tcp", contextPropagation: "headers,tcp", + bpfPidFilterOff: false, expectedInjectFlags: 3, // k_inject_http_headers | k_inject_tcp_options + expectedFilterPids: 1, + }, + { + name: "ip only (handled by tctracer)", + contextPropagation: "ip", + bpfPidFilterOff: false, + expectedInjectFlags: 0, + expectedFilterPids: 1, }, { name: "all", contextPropagation: "all", - expectedInjectFlags: 3, // k_inject_http_headers | k_inject_tcp_options + bpfPidFilterOff: false, + expectedInjectFlags: 3, // k_inject_http_headers | k_inject_tcp_options; IP handled by tctracer + expectedFilterPids: 1, }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - cfg := &obi.Config{ - EBPF: config.EBPFTracer{ - MaxTransactionTime: 10 * time.Second, - }, - } - err := cfg.EBPF.ContextPropagation.UnmarshalText([]byte(tt.contextPropagation)) - require.NoError(t, err) - - tracer := New(cfg) - constants := tracer.Constants() - - // Check that inject_flags is set correctly - injectFlags, ok := constants["inject_flags"] - assert.True(t, ok, "inject_flags should be present in constants") - assert.Equal(t, tt.expectedInjectFlags, injectFlags, "inject_flags value mismatch") - - // Verify the logic - expectedFlags := uint32(0) - if cfg.EBPF.ContextPropagation.HasHeaders() { - expectedFlags |= 1 - } - if cfg.EBPF.ContextPropagation.HasTCP() { - expectedFlags |= 2 - } - assert.Equal(t, expectedFlags, injectFlags, "inject_flags should match expected calculation") - }) - } -} - -func TestTracer_Constants_FilterPids(t *testing.T) { - tests := []struct { - name string - bpfPidFilterOff bool - expectedFilterVal int32 - }{ { - name: "filter enabled", - bpfPidFilterOff: false, - expectedFilterVal: 1, + name: "filter off", + contextPropagation: "disabled", + bpfPidFilterOff: true, + expectedInjectFlags: 0, + expectedFilterPids: 0, }, { - name: "filter disabled", - bpfPidFilterOff: true, - expectedFilterVal: 0, + name: "headers, filter off", + contextPropagation: "headers", + bpfPidFilterOff: true, + expectedInjectFlags: 1, + expectedFilterPids: 0, }, } @@ -109,13 +96,28 @@ func TestTracer_Constants_FilterPids(t *testing.T) { MaxTransactionTime: 10 * time.Second, }, } + err := cfg.EBPF.ContextPropagation.UnmarshalText([]byte(tt.contextPropagation)) + require.NoError(t, err) + + constants := New(cfg).Constants() + require.Len(t, constants, expectedSpecCount, "tpinjector should have exactly one spec") + + // Spec 0 carries all constants for this tracer. + c := constants[0] + + injectFlags, ok := c["inject_flags"] + assert.True(t, ok, "inject_flags should be present") + assert.Equal(t, tt.expectedInjectFlags, injectFlags) + + filterPids, ok := c["filter_pids"] + assert.True(t, ok, "filter_pids should be present") + assert.Equal(t, tt.expectedFilterPids, filterPids) - tracer := New(cfg) - constants := tracer.Constants() + _, ok = c["max_transaction_time"] + assert.True(t, ok, "max_transaction_time should be present") - filterPids, ok := constants["filter_pids"] - assert.True(t, ok, "filter_pids should be present in constants") - assert.Equal(t, tt.expectedFilterVal, filterPids, "filter_pids value mismatch") + _, ok = c["g_bpf_debug"] + assert.True(t, ok, "g_bpf_debug should be present") }) } } diff --git a/pkg/internal/ebpf/watcher/watcher.go b/pkg/internal/ebpf/watcher/watcher.go index 643c3f2820..f2582d9a67 100644 --- a/pkg/internal/ebpf/watcher/watcher.go +++ b/pkg/internal/ebpf/watcher/watcher.go @@ -52,18 +52,22 @@ func New(cfg *obi.Config, events chan<- Event) *Watcher { } } -func (p *Watcher) Load() (*ebpf.CollectionSpec, error) { - return LoadBpf() +func (p *Watcher) LoadSpecs() ([]*ebpf.CollectionSpec, error) { + spec, err := LoadBpf() + if err != nil { + return nil, err + } + return []*ebpf.CollectionSpec{spec}, nil } -func (p *Watcher) Constants() map[string]any { - return map[string]any{ +func (p *Watcher) Constants() []map[string]any { + return []map[string]any{{ "g_bpf_debug": p.cfg.EBPF.BpfDebug, - } + }} } -func (p *Watcher) BpfObjects() any { - return &p.bpfObjects +func (p *Watcher) BpfObjects() []any { + return []any{&p.bpfObjects} } func (p *Watcher) AddCloser(c ...io.Closer) { From 20edcda411db84c97fcb0681d7027cd052e62985 Mon Sep 17 00:00:00 2001 From: Rafael Roquetto Date: Thu, 26 Feb 2026 10:59:44 -0700 Subject: [PATCH 2/5] Introduce SpecBundle --- bpf/maps/sock_dir.h | 3 + bpf/tpinjector/sock_iter.c | 2 + bpf/tpinjector/tpinjector.c | 2 - pkg/ebpf/common/spec.go | 16 ++++ pkg/ebpf/tracer.go | 13 +--- pkg/ebpf/tracer_linux.go | 62 ++++++---------- .../ebpf/generictracer/generictracer.go | 12 +-- .../generictracer/generictracer_notlinux.go | 4 +- pkg/internal/ebpf/gotracer/gotracer.go | 52 ++++++------- pkg/internal/ebpf/gpuevent/gpuevent.go | 12 +-- pkg/internal/ebpf/logenricher/logenricher.go | 18 ++--- .../ebpf/logenricher/logenricher_notlinux.go | 4 +- pkg/internal/ebpf/logger/logger.go | 19 ++--- pkg/internal/ebpf/tpinjector/tpinjector.go | 73 ++++++++++++------- .../ebpf/tpinjector/tpinjector_notlinux.go | 4 +- .../ebpf/tpinjector/tpinjector_test.go | 19 +++-- pkg/internal/ebpf/watcher/watcher.go | 20 ++--- 17 files changed, 156 insertions(+), 179 deletions(-) create mode 100644 pkg/ebpf/common/spec.go diff --git a/bpf/maps/sock_dir.h b/bpf/maps/sock_dir.h index 5e96552992..8f4be92518 100644 --- a/bpf/maps/sock_dir.h +++ b/bpf/maps/sock_dir.h @@ -6,6 +6,8 @@ #include #include +#include + // A map of sockets which we track with sock_ops. The sock_msg // program subscribes to this map and runs for each new socket // activity @@ -16,4 +18,5 @@ struct { __uint(max_entries, 65535); __uint(key_size, sizeof(u64)); __uint(value_size, sizeof(u32)); + __uint(pinning, OBI_PIN_INTERNAL); } sock_dir SEC(".maps"); diff --git a/bpf/tpinjector/sock_iter.c b/bpf/tpinjector/sock_iter.c index 7b1317ef36..2fa92433af 100644 --- a/bpf/tpinjector/sock_iter.c +++ b/bpf/tpinjector/sock_iter.c @@ -12,6 +12,8 @@ #include +char __license[] SEC("license") = "Dual MIT/GPL"; + // max IPv6+port: "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535" = 48 chars enum { k_addr_buf_len = 48 }; diff --git a/bpf/tpinjector/tpinjector.c b/bpf/tpinjector/tpinjector.c index b679d1d494..a35a98c412 100644 --- a/bpf/tpinjector/tpinjector.c +++ b/bpf/tpinjector/tpinjector.c @@ -975,5 +975,3 @@ int obi_packet_extender_create_tp(struct sk_msg_md *msg) { return SK_PASS; } - -#include "sock_iter.c" diff --git a/pkg/ebpf/common/spec.go b/pkg/ebpf/common/spec.go new file mode 100644 index 0000000000..5ff325fb5f --- /dev/null +++ b/pkg/ebpf/common/spec.go @@ -0,0 +1,16 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package ebpfcommon // import "go.opentelemetry.io/obi/pkg/ebpf/common" + +import "github.com/cilium/ebpf" + +// SpecBundle groups everything needed to load one BPF collection into the kernel. +type SpecBundle struct { + // Spec is the BPF collection spec to load, as generated by bpf2go. + Spec *ebpf.CollectionSpec + // Objects is the pointer to the generated BpfObjects struct that Spec.LoadAndAssign will populate. + Objects any + // Constants is an optional map of BPF constants to rewrite before loading. + Constants map[string]any +} diff --git a/pkg/ebpf/tracer.go b/pkg/ebpf/tracer.go index 688c64552e..534aa6e4b1 100644 --- a/pkg/ebpf/tracer.go +++ b/pkg/ebpf/tracer.go @@ -64,15 +64,13 @@ type PIDsAccounter interface { } type CommonTracer interface { - // LoadSpecs loads one or more BPF object specs that are generated by the bpf2go compiler. - LoadSpecs() ([]*ebpf.CollectionSpec, error) + // LoadSpecs returns one SpecBundle per BPF collection. Each bundle contains + // the collection spec, the object pointer to populate, and the constants to rewrite. + LoadSpecs() ([]*ebpfcommon.SpecBundle, error) // AddCloser adds io.Closer instances that need to be invoked when the // Run function ends. AddCloser(c ...io.Closer) - // BpfObjects returns a slice of object pointers matching the specs returned by LoadSpecs(). - // Each object will be populated by spec.LoadAndAssign() for the corresponding spec. - BpfObjects() []any - // Sets up any tail call tables if the BPF program has it + // SetupTailCalls sets up any tail call jump tables after all specs are loaded. SetupTailCalls() } @@ -88,9 +86,6 @@ type KprobesTracer interface { type Tracer interface { PIDsAccounter KprobesTracer - // Constants returns a slice of constant maps, one per spec from LoadSpecs(). - // Each map contains constants to be overridden into the corresponding eBPF program. - Constants() []map[string]any // GoProbes returns a slice with the name of Go functions that need to be inspected // in the executable, as well as the eBPF programs that optionally need to be // inserted as the Go function start and end probes diff --git a/pkg/ebpf/tracer_linux.go b/pkg/ebpf/tracer_linux.go index 7e20089127..4bbad8e948 100644 --- a/pkg/ebpf/tracer_linux.go +++ b/pkg/ebpf/tracer_linux.go @@ -99,12 +99,12 @@ func resolveMaps(eventContext *common.EBPFEventContext, spec *ebpf.CollectionSpe return &collOpts, nil } -func loadSpec(eventContext *common.EBPFEventContext, spec *ebpf.CollectionSpec, constants map[string]any, obj any, otelBPFFSPath string, idx int) error { - if err := ebpfconvenience.RewriteConstants(spec, constants); err != nil { +func loadSpec(eventContext *common.EBPFEventContext, bundle *common.SpecBundle, otelBPFFSPath string, idx int) error { + if err := ebpfconvenience.RewriteConstants(bundle.Spec, bundle.Constants); err != nil { return fmt.Errorf("rewriting BPF constants for spec %d: %w", idx, err) } - collOpts, err := resolveMaps(eventContext, spec) + collOpts, err := resolveMaps(eventContext, bundle.Spec) if err != nil { return fmt.Errorf("resolving maps for spec %d: %w", idx, err) } @@ -112,16 +112,16 @@ func loadSpec(eventContext *common.EBPFEventContext, spec *ebpf.CollectionSpec, collOpts.Programs = ebpf.ProgramOptions{LogSizeStart: 640 * 1024} collOpts.Maps = ebpf.MapOptions{PinPath: otelBPFFSPath} - if err := spec.LoadAndAssign(obj, collOpts); err != nil { + if err := bundle.Spec.LoadAndAssign(bundle.Objects, collOpts); err != nil { return fmt.Errorf("loading spec %d: %w", idx, err) } return nil } -func closeLoadedSpecs(objs []any, count int) { - for j := 0; j < count; j++ { - if c, ok := objs[j].(io.Closer); ok { +func closeLoadedSpecs(bundles []*common.SpecBundle) { + for _, bundle := range bundles { + if c, ok := bundle.Objects.(io.Closer); ok { c.Close() } } @@ -211,7 +211,7 @@ func (pt *ProcessTracer) makeOtelBPFFSPath() (string, error) { return otelPath, nil } -func (pt *ProcessTracer) setupOtelBPFFFSPath(specs []*ebpf.CollectionSpec) string { +func (pt *ProcessTracer) setupOtelBPFFFSPath(bundles []*common.SpecBundle) string { // Set up BPF FS path once for all specs otelBPFFSPath, err := pt.makeOtelBPFFSPath() @@ -227,8 +227,8 @@ func (pt *ProcessTracer) setupOtelBPFFFSPath(specs []*ebpf.CollectionSpec) strin log.Warn("OBI will still work, but features depending on pinned maps (e.g., log enricher, profile correlation) will be disabled") // disable pinning for ALL specs - for _, spec := range specs { - for _, v := range spec.Maps { + for _, bundle := range bundles { + for _, v := range bundle.Spec.Maps { if v.Pinning == ebpf.PinByName { v.Pinning = ebpf.PinNone v.MaxEntries = 1 @@ -240,28 +240,16 @@ func (pt *ProcessTracer) setupOtelBPFFFSPath(specs []*ebpf.CollectionSpec) strin } func (pt *ProcessTracer) loadAndAssign(eventContext *common.EBPFEventContext, p Tracer) error { - specs, err := p.LoadSpecs() + bundles, err := p.LoadSpecs() if err != nil { return fmt.Errorf("loading eBPF program specs: %w", err) } - // Set up BPF FS path once for all specs - otelBPFFSPath := pt.setupOtelBPFFFSPath(specs) - - objs := p.BpfObjects() - constants := p.Constants() - - if len(objs) != len(specs) { - return fmt.Errorf("BpfObjects() returned %d objects but LoadSpecs() returned %d specs", len(objs), len(specs)) - } + otelBPFFSPath := pt.setupOtelBPFFFSPath(bundles) - if len(constants) != len(specs) { - return fmt.Errorf("Constants() returned %d maps but LoadSpecs() returned %d specs", len(constants), len(specs)) - } - - for i, spec := range specs { - if err := loadSpec(eventContext, spec, constants[i], objs[i], otelBPFFSPath, i); err != nil { - closeLoadedSpecs(objs, i) + for i, bundle := range bundles { + if err := loadSpec(eventContext, bundle, otelBPFFSPath, i); err != nil { + closeLoadedSpecs(bundles[:i]) return err } } @@ -449,25 +437,17 @@ func RunUtilityTracer(ctx context.Context, eventContext *common.EBPFEventContext i := instrumenter{} plog := ptlog() plog.Debug("loading independent eBPF program") - specs, err := p.LoadSpecs() + + bundles, err := p.LoadSpecs() if err != nil { return fmt.Errorf("loading eBPF program specs: %w", err) } - objs := p.BpfObjects() - if len(objs) != len(specs) { - return fmt.Errorf("BpfObjects() returned %d objects but LoadSpecs() returned %d specs", len(objs), len(specs)) - } - - for idx, spec := range specs { - collOpts, err := resolveMaps(eventContext, spec) - if err != nil { - return err - } - - if err := spec.LoadAndAssign(objs[idx], collOpts); err != nil { + for idx, bundle := range bundles { + if err := loadSpec(eventContext, bundle, "", idx); err != nil { + closeLoadedSpecs(bundles[:idx]) printVerifierErrorInfo(err) - return fmt.Errorf("loading and assigning BPF objects: %w", err) + return err } } diff --git a/pkg/internal/ebpf/generictracer/generictracer.go b/pkg/internal/ebpf/generictracer/generictracer.go index 7bc78e8408..59cb2b60a4 100644 --- a/pkg/internal/ebpf/generictracer/generictracer.go +++ b/pkg/internal/ebpf/generictracer/generictracer.go @@ -127,7 +127,7 @@ func (p *Tracer) BlockPID(pid app.PID, ns uint32) { p.rebuildValidPids() } -func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { +func (p *Tracer) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { if p.cfg.EBPF.TrackRequestHeaders || p.cfg.EBPF.ContextPropagation.IsEnabled() { p.log.Info("Enabling trace information parsing", "bpf_loop_enabled", ebpfcommon.SupportsEBPFLoops(p.log, p.cfg.EBPF.OverrideBPFLoopEnabled)) @@ -140,7 +140,7 @@ func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { ebpfcommon.FixupSpec(spec, p.cfg.EBPF.OverrideBPFLoopEnabled) - return []*ebpf.CollectionSpec{spec}, err + return []*ebpfcommon.SpecBundle{{Spec: spec, Objects: &p.bpfObjects, Constants: p.constants()}}, nil } func (p *Tracer) SetupTailCalls() { @@ -162,7 +162,7 @@ func (p *Tracer) SetupTailCalls() { } } -func (p *Tracer) Constants() []map[string]any { +func (p *Tracer) constants() map[string]any { m := make(map[string]any, 2) m["wakeup_data_bytes"] = uint32(p.cfg.EBPF.WakeupLen) * uint32(unsafe.Sizeof(ebpfcommon.HTTPRequestTrace{})) @@ -205,17 +205,13 @@ func (p *Tracer) Constants() []map[string]any { m["g_bpf_debug"] = p.cfg.EBPF.BpfDebug m["g_bpf_traceparent_enabled"] = p.cfg.EBPF.TrackRequestHeaders || p.cfg.EBPF.ContextPropagation.IsEnabled() - return []map[string]any{m} + return m } func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} -func (p *Tracer) BpfObjects() []any { - return []any{&p.bpfObjects} -} - func (p *Tracer) AddCloser(c ...io.Closer) { p.closers = append(p.closers, c...) } diff --git a/pkg/internal/ebpf/generictracer/generictracer_notlinux.go b/pkg/internal/ebpf/generictracer/generictracer_notlinux.go index afe6e2ba71..8e4f8bbff1 100644 --- a/pkg/internal/ebpf/generictracer/generictracer_notlinux.go +++ b/pkg/internal/ebpf/generictracer/generictracer_notlinux.go @@ -29,8 +29,7 @@ type Tracer struct{} func New(_ ebpfcommon.ServiceFilter, _ *obi.Config, _ imetrics.Reporter) *Tracer { return nil } func (p *Tracer) AllowPID(_ app.PID, _ uint32, _ *svc.Attrs) {} func (p *Tracer) BlockPID(_ app.PID, _ uint32) {} -func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { return nil, nil } -func (p *Tracer) BpfObjects() []any { return nil } +func (p *Tracer) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { return nil, nil } func (p *Tracer) AddCloser(_ ...io.Closer) {} func (p *Tracer) GoProbes() map[string][]*ebpfcommon.ProbeDesc { return nil } func (p *Tracer) KProbes() map[string]ebpfcommon.ProbeDesc { return nil } @@ -47,7 +46,6 @@ func (p *Tracer) UnlinkInstrumentedLib(_ uint64) func (p *Tracer) AlreadyInstrumentedLib(_ uint64) bool { return false } func (p *Tracer) Run(_ context.Context, _ *ebpfcommon.EBPFEventContext, _ *msg.Queue[[]request.Span]) { } -func (p *Tracer) Constants() []map[string]any { return nil } func (p *Tracer) SetupTailCalls() {} func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} diff --git a/pkg/internal/ebpf/gotracer/gotracer.go b/pkg/internal/ebpf/gotracer/gotracer.go index ff09bbd204..9f9fadfbfd 100644 --- a/pkg/internal/ebpf/gotracer/gotracer.go +++ b/pkg/internal/ebpf/gotracer/gotracer.go @@ -84,7 +84,7 @@ func (p *Tracer) supportsContextPropagation() bool { return !ebpfcommon.IntegrityModeOverride && ebpfcommon.SupportsContextPropagationWithProbe(p.log) } -func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { +func (p *Tracer) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { if !p.supportsContextPropagation() { p.log.Info("Kernel in lockdown mode or missing CAP_SYS_ADMIN.") } @@ -93,37 +93,37 @@ func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { if err != nil { return nil, err } - return []*ebpf.CollectionSpec{spec}, nil -} - -func (p *Tracer) SetupTailCalls() { -} -func (p *Tracer) Constants() []map[string]any { blackBoxCP := uint32(0) if p.cfg.DisableBlackBoxCP { blackBoxCP = uint32(1) } - return []map[string]any{{ - "g_bpf_debug": p.cfg.BpfDebug, - "g_bpf_header_propagation": p.supportsContextPropagation(), - "wakeup_data_bytes": uint32(p.cfg.WakeupLen) * uint32(unsafe.Sizeof(ebpfcommon.HTTPRequestTrace{})), - "disable_black_box_cp": blackBoxCP, - "attr_type_invalid": uint64(attribute.INVALID), - "attr_type_bool": uint64(attribute.BOOL), - "attr_type_int64": uint64(attribute.INT64), - "attr_type_float64": uint64(attribute.FLOAT64), - "attr_type_string": uint64(attribute.STRING), - "attr_type_boolslice": uint64(attribute.BOOLSLICE), - "attr_type_int64slice": uint64(attribute.INT64SLICE), - "attr_type_float64slice": uint64(attribute.FLOAT64SLICE), - "attr_type_stringslice": uint64(attribute.STRINGSLICE), - "g_bpf_traceparent_enabled": true, - "g_bpf_loop_enabled": p.supportsBPFLoop, - }} + return []*ebpfcommon.SpecBundle{{ + Spec: spec, + Objects: &p.bpfObjects, + Constants: map[string]any{ + "g_bpf_debug": p.cfg.BpfDebug, + "g_bpf_header_propagation": p.supportsContextPropagation(), + "wakeup_data_bytes": uint32(p.cfg.WakeupLen) * uint32(unsafe.Sizeof(ebpfcommon.HTTPRequestTrace{})), + "disable_black_box_cp": blackBoxCP, + "attr_type_invalid": uint64(attribute.INVALID), + "attr_type_bool": uint64(attribute.BOOL), + "attr_type_int64": uint64(attribute.INT64), + "attr_type_float64": uint64(attribute.FLOAT64), + "attr_type_string": uint64(attribute.STRING), + "attr_type_boolslice": uint64(attribute.BOOLSLICE), + "attr_type_int64slice": uint64(attribute.INT64SLICE), + "attr_type_float64slice": uint64(attribute.FLOAT64SLICE), + "attr_type_stringslice": uint64(attribute.STRINGSLICE), + "g_bpf_traceparent_enabled": true, + "g_bpf_loop_enabled": p.supportsBPFLoop, + }, + }}, nil } +func (p *Tracer) SetupTailCalls() {} + func (p *Tracer) RegisterOffsets(fileInfo *exec.FileInfo, offsets *goexec.Offsets) { offTable := BpfOffTableT{} // Set the field offsets and the logLevel for the Go BPF program in a map @@ -250,10 +250,6 @@ func (p *Tracer) RegisterOffsets(fileInfo *exec.FileInfo, offsets *goexec.Offset func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} -func (p *Tracer) BpfObjects() []any { - return []any{&p.bpfObjects} -} - func (p *Tracer) AddCloser(c ...io.Closer) { p.closers = append(p.closers, c...) } diff --git a/pkg/internal/ebpf/gpuevent/gpuevent.go b/pkg/internal/ebpf/gpuevent/gpuevent.go index 4e4bc08977..fd02bda3fc 100644 --- a/pkg/internal/ebpf/gpuevent/gpuevent.go +++ b/pkg/internal/ebpf/gpuevent/gpuevent.go @@ -86,17 +86,13 @@ func (p *Tracer) BlockPID(pid app.PID, ns uint32) { p.pidsFilter.BlockPID(pid, ns) } -func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { +func (p *Tracer) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { spec, err := LoadBpf() if err != nil { return nil, err } - return []*ebpf.CollectionSpec{spec}, nil -} -func (p *Tracer) Constants() []map[string]any { m := make(map[string]any, 2) - // The eBPF side does some basic filtering of events that do not belong to // processes which we monitor. We filter more accurately in the userspace, but // for performance reasons we enable the PID based filtering in eBPF. @@ -107,17 +103,13 @@ func (p *Tracer) Constants() []map[string]any { } m["g_bpf_debug"] = p.cfg.EBPF.BpfDebug - return []map[string]any{m} + return []*ebpfcommon.SpecBundle{{Spec: spec, Objects: &p.bpfObjects, Constants: m}}, nil } func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} -func (p *Tracer) BpfObjects() []any { - return []any{&p.bpfObjects} -} - func (p *Tracer) AddCloser(c ...io.Closer) { p.closers = append(p.closers, c...) } diff --git a/pkg/internal/ebpf/logenricher/logenricher.go b/pkg/internal/ebpf/logenricher/logenricher.go index c388236342..9ea98033cc 100644 --- a/pkg/internal/ebpf/logenricher/logenricher.go +++ b/pkg/internal/ebpf/logenricher/logenricher.go @@ -90,30 +90,24 @@ func New(cfg *obi.Config) *Tracer { return tr } -func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { +func (p *Tracer) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { spec, err := LoadBpf() if err != nil { return nil, err } - return []*ebpf.CollectionSpec{spec}, nil + return []*ebpfcommon.SpecBundle{{ + Spec: spec, + Objects: &p.bpfObjects, + Constants: map[string]any{"g_bpf_debug": p.cfg.EBPF.BpfDebug}, + }}, nil } func (p *Tracer) SetupTailCalls() {} -func (p *Tracer) Constants() []map[string]any { - return []map[string]any{{ - "g_bpf_debug": p.cfg.EBPF.BpfDebug, - }} -} - func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} -func (p *Tracer) BpfObjects() []any { - return []any{&p.bpfObjects} -} - func (p *Tracer) AddCloser(c ...io.Closer) { p.closers = append(p.closers, c...) } diff --git a/pkg/internal/ebpf/logenricher/logenricher_notlinux.go b/pkg/internal/ebpf/logenricher/logenricher_notlinux.go index de051d01a1..36eb6c5187 100644 --- a/pkg/internal/ebpf/logenricher/logenricher_notlinux.go +++ b/pkg/internal/ebpf/logenricher/logenricher_notlinux.go @@ -26,8 +26,7 @@ type Tracer struct{} func New(_ *obi.Config) *Tracer { return nil } func (p *Tracer) AllowPID(_ app.PID, _ uint32, _ *svc.Attrs) {} func (p *Tracer) BlockPID(_ app.PID, _ uint32) {} -func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { return nil, nil } -func (p *Tracer) BpfObjects() []any { return nil } +func (p *Tracer) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { return nil, nil } func (p *Tracer) AddCloser(_ ...io.Closer) {} func (p *Tracer) GoProbes() map[string][]*ebpfcommon.ProbeDesc { return nil } func (p *Tracer) KProbes() map[string]ebpfcommon.ProbeDesc { return nil } @@ -44,7 +43,6 @@ func (p *Tracer) UnlinkInstrumentedLib(_ uint64) {} func (p *Tracer) AlreadyInstrumentedLib(_ uint64) bool { return false } func (p *Tracer) Run(_ context.Context, _ *ebpfcommon.EBPFEventContext, _ *msg.Queue[[]request.Span]) { } -func (p *Tracer) Constants() []map[string]any { return nil } func (p *Tracer) SetupTailCalls() {} func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} diff --git a/pkg/internal/ebpf/logger/logger.go b/pkg/internal/ebpf/logger/logger.go index 3b2b43353c..4dd5d11c81 100644 --- a/pkg/internal/ebpf/logger/logger.go +++ b/pkg/internal/ebpf/logger/logger.go @@ -9,7 +9,6 @@ import ( "io" "log/slog" - "github.com/cilium/ebpf" "golang.org/x/sys/unix" "go.opentelemetry.io/obi/pkg/appolly/app/request" @@ -42,21 +41,21 @@ func New(cfg *obi.Config) *BPFLogger { } } -func (p *BPFLogger) LoadSpecs() ([]*ebpf.CollectionSpec, error) { +func (p *BPFLogger) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { if p.cfg.EBPF.BpfDebug { spec, err := LoadBpf() if err != nil { return nil, err } - return []*ebpf.CollectionSpec{spec}, nil + return []*ebpfcommon.SpecBundle{{ + Spec: spec, + Objects: &p.bpfObjects, + Constants: map[string]any{"g_bpf_debug": p.cfg.EBPF.BpfDebug}, + }}, nil } return nil, errors.New("BPF debug is not enabled") } -func (p *BPFLogger) BpfObjects() []any { - return []any{&p.bpfObjects} -} - func (p *BPFLogger) AddCloser(c ...io.Closer) { p.closers = append(p.closers, c...) } @@ -69,12 +68,6 @@ func (p *BPFLogger) Tracepoints() map[string]ebpfcommon.ProbeDesc { return nil } -func (p *BPFLogger) Constants() []map[string]any { - return []map[string]any{{ - "g_bpf_debug": p.cfg.EBPF.BpfDebug, - }} -} - func (p *BPFLogger) SetupTailCalls() {} func (p *BPFLogger) Run(ctx context.Context) { diff --git a/pkg/internal/ebpf/tpinjector/tpinjector.go b/pkg/internal/ebpf/tpinjector/tpinjector.go index a7ec818c1e..0c8692ec5f 100644 --- a/pkg/internal/ebpf/tpinjector/tpinjector.go +++ b/pkg/internal/ebpf/tpinjector/tpinjector.go @@ -23,13 +23,15 @@ import ( ) //go:generate $BPF2GO -cc $BPF_CLANG -cflags $BPF_CFLAGS -target amd64,arm64 Bpf ../../../../bpf/tpinjector/tpinjector.c -- -I../../../../bpf -I../../../../bpf +//go:generate $BPF2GO -cc $BPF_CLANG -cflags $BPF_CFLAGS -target amd64,arm64 BpfIter ../../../../bpf/tpinjector/sock_iter.c -- -I../../../../bpf -I../../../../bpf type Tracer struct { - cfg *obi.Config - bpfObjects BpfObjects - closers []io.Closer - log *slog.Logger - iters []*ebpfcommon.Iter + cfg *obi.Config + bpfObjects BpfObjects + bpfIterObjects BpfIterObjects + closers []io.Closer + log *slog.Logger + iters []*ebpfcommon.Iter } func New(cfg *obi.Config) *Tracer { @@ -45,28 +47,32 @@ func (p *Tracer) AllowPID(app.PID, uint32, *svc.Attrs) {} func (p *Tracer) BlockPID(app.PID, uint32) {} -func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { +func (p *Tracer) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { spec, err := LoadBpf() if err != nil { return nil, err } - return []*ebpf.CollectionSpec{spec}, nil -} - -func (p *Tracer) SetupTailCalls() { -} -func (p *Tracer) Constants() []map[string]any { - m := make(map[string]any, 4) - - if p.cfg.Discovery.BPFPidFilterOff { - m["filter_pids"] = int32(0) - } else { - m["filter_pids"] = int32(1) + iterSpec, err := LoadBpfIter() + if err != nil { + return nil, err } - m["max_transaction_time"] = uint64(p.cfg.EBPF.MaxTransactionTime.Nanoseconds()) + return []*ebpfcommon.SpecBundle{ + { + Spec: spec, + Objects: &p.bpfObjects, + Constants: p.constants(), + }, + { + Spec: iterSpec, + Objects: &p.bpfIterObjects, + Constants: p.iterConstants(), + }, + }, nil +} +func (p *Tracer) constants() map[string]any { flags := uint32(0) if p.cfg.EBPF.ContextPropagation.HasHeaders() { flags |= 1 // k_inject_http_headers @@ -74,20 +80,32 @@ func (p *Tracer) Constants() []map[string]any { if p.cfg.EBPF.ContextPropagation.HasTCP() { flags |= 2 // k_inject_tcp_options } - m["inject_flags"] = flags - m["g_bpf_debug"] = p.cfg.EBPF.BpfDebug - return []map[string]any{m} + filterPids := int32(1) + if p.cfg.Discovery.BPFPidFilterOff { + filterPids = 0 + } + + return map[string]any{ + "filter_pids": filterPids, + "max_transaction_time": uint64(p.cfg.EBPF.MaxTransactionTime.Nanoseconds()), + "inject_flags": flags, + "g_bpf_debug": p.cfg.EBPF.BpfDebug, + } } +func (p *Tracer) iterConstants() map[string]any { + return map[string]any{ + "g_bpf_debug": p.cfg.EBPF.BpfDebug, + } +} + +func (p *Tracer) SetupTailCalls() {} + func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} -func (p *Tracer) BpfObjects() []any { - return []any{&p.bpfObjects} -} - func (p *Tracer) AddCloser(c ...io.Closer) { p.closers = append(p.closers, c...) } @@ -146,7 +164,7 @@ func (p *Tracer) Iters() []*ebpfcommon.Iter { return p.iters } - p.iters = []*ebpfcommon.Iter{{Program: p.bpfObjects.ObiSkIterTcp}} + p.iters = []*ebpfcommon.Iter{{Program: p.bpfIterObjects.ObiSkIterTcp}} return p.iters } @@ -177,6 +195,7 @@ func (p *Tracer) Run(ctx context.Context, _ *ebpfcommon.EBPFEventContext, _ *msg <-ctx.Done() p.bpfObjects.Close() + p.bpfIterObjects.Close() p.log.Debug("tpinjector terminated") } diff --git a/pkg/internal/ebpf/tpinjector/tpinjector_notlinux.go b/pkg/internal/ebpf/tpinjector/tpinjector_notlinux.go index 29ad2bec65..574cadd970 100644 --- a/pkg/internal/ebpf/tpinjector/tpinjector_notlinux.go +++ b/pkg/internal/ebpf/tpinjector/tpinjector_notlinux.go @@ -28,8 +28,7 @@ type Tracer struct{} func New(_ *obi.Config) *Tracer { return nil } func (p *Tracer) AllowPID(_ app.PID, _ uint32, _ *svc.Attrs) {} func (p *Tracer) BlockPID(_ app.PID, _ uint32) {} -func (p *Tracer) LoadSpecs() ([]*ebpf.CollectionSpec, error) { return nil, nil } -func (p *Tracer) BpfObjects() []any { return nil } +func (p *Tracer) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { return nil, nil } func (p *Tracer) AddCloser(_ ...io.Closer) {} func (p *Tracer) GoProbes() map[string][]*ebpfcommon.ProbeDesc { return nil } func (p *Tracer) KProbes() map[string]ebpfcommon.ProbeDesc { return nil } @@ -46,7 +45,6 @@ func (p *Tracer) UnlinkInstrumentedLib(_ uint64) {} func (p *Tracer) AlreadyInstrumentedLib(_ uint64) bool { return false } func (p *Tracer) Run(_ context.Context, _ *ebpfcommon.EBPFEventContext, _ *msg.Queue[[]request.Span]) { } -func (p *Tracer) Constants() []map[string]any { return nil } func (p *Tracer) SetupTailCalls() {} func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} func (p *Tracer) ProcessBinary(_ *exec.FileInfo) {} diff --git a/pkg/internal/ebpf/tpinjector/tpinjector_test.go b/pkg/internal/ebpf/tpinjector/tpinjector_test.go index 54dcecfa8a..4ead6218f7 100644 --- a/pkg/internal/ebpf/tpinjector/tpinjector_test.go +++ b/pkg/internal/ebpf/tpinjector/tpinjector_test.go @@ -17,8 +17,8 @@ import ( "go.opentelemetry.io/obi/pkg/obi" ) -// tpinjector has a single BPF spec; all constants live in spec 0. -const expectedSpecCount = 1 +// tpinjector has two BPF specs: the main tpinjector (spec 0) and the sock iterator (spec 1). +const expectedSpecCount = 2 func TestTracer_Constants(t *testing.T) { tests := []struct { @@ -99,11 +99,12 @@ func TestTracer_Constants(t *testing.T) { err := cfg.EBPF.ContextPropagation.UnmarshalText([]byte(tt.contextPropagation)) require.NoError(t, err) - constants := New(cfg).Constants() - require.Len(t, constants, expectedSpecCount, "tpinjector should have exactly one spec") + bundles, err := New(cfg).LoadSpecs() + require.NoError(t, err) + require.Len(t, bundles, expectedSpecCount, "tpinjector should have exactly one spec") - // Spec 0 carries all constants for this tracer. - c := constants[0] + // Spec 0 (tpinjector) carries the main constants. + c := bundles[0].Constants injectFlags, ok := c["inject_flags"] assert.True(t, ok, "inject_flags should be present") @@ -118,6 +119,12 @@ func TestTracer_Constants(t *testing.T) { _, ok = c["g_bpf_debug"] assert.True(t, ok, "g_bpf_debug should be present") + + // Spec 1 (sock_iter) carries only the debug flag. + iterC := bundles[1].Constants + _, ok = iterC["g_bpf_debug"] + assert.True(t, ok, "iter g_bpf_debug should be present") + assert.Len(t, iterC, 1, "iter spec should have only g_bpf_debug") }) } } diff --git a/pkg/internal/ebpf/watcher/watcher.go b/pkg/internal/ebpf/watcher/watcher.go index f2582d9a67..a7e4e09449 100644 --- a/pkg/internal/ebpf/watcher/watcher.go +++ b/pkg/internal/ebpf/watcher/watcher.go @@ -10,8 +10,6 @@ import ( "io" "log/slog" - "github.com/cilium/ebpf" - "go.opentelemetry.io/obi/pkg/appolly/app/request" "go.opentelemetry.io/obi/pkg/config" ebpfcommon "go.opentelemetry.io/obi/pkg/ebpf/common" @@ -52,22 +50,16 @@ func New(cfg *obi.Config, events chan<- Event) *Watcher { } } -func (p *Watcher) LoadSpecs() ([]*ebpf.CollectionSpec, error) { +func (p *Watcher) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { spec, err := LoadBpf() if err != nil { return nil, err } - return []*ebpf.CollectionSpec{spec}, nil -} - -func (p *Watcher) Constants() []map[string]any { - return []map[string]any{{ - "g_bpf_debug": p.cfg.EBPF.BpfDebug, - }} -} - -func (p *Watcher) BpfObjects() []any { - return []any{&p.bpfObjects} + return []*ebpfcommon.SpecBundle{{ + Spec: spec, + Objects: &p.bpfObjects, + Constants: map[string]any{"g_bpf_debug": p.cfg.EBPF.BpfDebug}, + }}, nil } func (p *Watcher) AddCloser(c ...io.Closer) { From 2ad60a20f6c96ea9792c81860c7b9724ec245ca5 Mon Sep 17 00:00:00 2001 From: Rafael Roquetto Date: Thu, 26 Feb 2026 14:29:34 -0700 Subject: [PATCH 3/5] Remove stale tests --- pkg/internal/ebpf/tpinjector/tpinjector_test.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/pkg/internal/ebpf/tpinjector/tpinjector_test.go b/pkg/internal/ebpf/tpinjector/tpinjector_test.go index 4ead6218f7..770ee0cd34 100644 --- a/pkg/internal/ebpf/tpinjector/tpinjector_test.go +++ b/pkg/internal/ebpf/tpinjector/tpinjector_test.go @@ -56,18 +56,11 @@ func TestTracer_Constants(t *testing.T) { expectedInjectFlags: 3, // k_inject_http_headers | k_inject_tcp_options expectedFilterPids: 1, }, - { - name: "ip only (handled by tctracer)", - contextPropagation: "ip", - bpfPidFilterOff: false, - expectedInjectFlags: 0, - expectedFilterPids: 1, - }, { name: "all", contextPropagation: "all", bpfPidFilterOff: false, - expectedInjectFlags: 3, // k_inject_http_headers | k_inject_tcp_options; IP handled by tctracer + expectedInjectFlags: 3, // k_inject_http_headers | k_inject_tcp_options expectedFilterPids: 1, }, { From f0c8272daa3a834bd4e51b001cf673fa15f260de Mon Sep 17 00:00:00 2001 From: Rafael Roquetto Date: Fri, 27 Feb 2026 09:49:39 -0700 Subject: [PATCH 4/5] review feedback --- pkg/internal/ebpf/gotracer/gotracer.go | 46 +++++++++++--------- pkg/internal/ebpf/gpuevent/gpuevent.go | 16 ++++--- pkg/internal/ebpf/logenricher/logenricher.go | 6 ++- pkg/internal/ebpf/logger/logger.go | 6 ++- pkg/internal/ebpf/watcher/watcher.go | 6 ++- 5 files changed, 50 insertions(+), 30 deletions(-) diff --git a/pkg/internal/ebpf/gotracer/gotracer.go b/pkg/internal/ebpf/gotracer/gotracer.go index 9f9fadfbfd..a7a02443d6 100644 --- a/pkg/internal/ebpf/gotracer/gotracer.go +++ b/pkg/internal/ebpf/gotracer/gotracer.go @@ -94,32 +94,36 @@ func (p *Tracer) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { return nil, err } + return []*ebpfcommon.SpecBundle{{ + Spec: spec, + Objects: &p.bpfObjects, + Constants: p.constants(), + }}, nil +} + +func (p *Tracer) constants() map[string]any { blackBoxCP := uint32(0) if p.cfg.DisableBlackBoxCP { blackBoxCP = uint32(1) } - return []*ebpfcommon.SpecBundle{{ - Spec: spec, - Objects: &p.bpfObjects, - Constants: map[string]any{ - "g_bpf_debug": p.cfg.BpfDebug, - "g_bpf_header_propagation": p.supportsContextPropagation(), - "wakeup_data_bytes": uint32(p.cfg.WakeupLen) * uint32(unsafe.Sizeof(ebpfcommon.HTTPRequestTrace{})), - "disable_black_box_cp": blackBoxCP, - "attr_type_invalid": uint64(attribute.INVALID), - "attr_type_bool": uint64(attribute.BOOL), - "attr_type_int64": uint64(attribute.INT64), - "attr_type_float64": uint64(attribute.FLOAT64), - "attr_type_string": uint64(attribute.STRING), - "attr_type_boolslice": uint64(attribute.BOOLSLICE), - "attr_type_int64slice": uint64(attribute.INT64SLICE), - "attr_type_float64slice": uint64(attribute.FLOAT64SLICE), - "attr_type_stringslice": uint64(attribute.STRINGSLICE), - "g_bpf_traceparent_enabled": true, - "g_bpf_loop_enabled": p.supportsBPFLoop, - }, - }}, nil + return map[string]any{ + "g_bpf_debug": p.cfg.BpfDebug, + "g_bpf_header_propagation": p.supportsContextPropagation(), + "wakeup_data_bytes": uint32(p.cfg.WakeupLen) * uint32(unsafe.Sizeof(ebpfcommon.HTTPRequestTrace{})), + "disable_black_box_cp": blackBoxCP, + "attr_type_invalid": uint64(attribute.INVALID), + "attr_type_bool": uint64(attribute.BOOL), + "attr_type_int64": uint64(attribute.INT64), + "attr_type_float64": uint64(attribute.FLOAT64), + "attr_type_string": uint64(attribute.STRING), + "attr_type_boolslice": uint64(attribute.BOOLSLICE), + "attr_type_int64slice": uint64(attribute.INT64SLICE), + "attr_type_float64slice": uint64(attribute.FLOAT64SLICE), + "attr_type_stringslice": uint64(attribute.STRINGSLICE), + "g_bpf_traceparent_enabled": true, + "g_bpf_loop_enabled": p.supportsBPFLoop, + } } func (p *Tracer) SetupTailCalls() {} diff --git a/pkg/internal/ebpf/gpuevent/gpuevent.go b/pkg/internal/ebpf/gpuevent/gpuevent.go index fd02bda3fc..9cc79a5fa6 100644 --- a/pkg/internal/ebpf/gpuevent/gpuevent.go +++ b/pkg/internal/ebpf/gpuevent/gpuevent.go @@ -92,18 +92,22 @@ func (p *Tracer) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { return nil, err } - m := make(map[string]any, 2) + return []*ebpfcommon.SpecBundle{{Spec: spec, Objects: &p.bpfObjects, Constants: p.constants()}}, nil +} + +func (p *Tracer) constants() map[string]any { // The eBPF side does some basic filtering of events that do not belong to // processes which we monitor. We filter more accurately in the userspace, but // for performance reasons we enable the PID based filtering in eBPF. + filterPids := int32(1) if p.cfg.Discovery.BPFPidFilterOff { - m["filter_pids"] = int32(0) - } else { - m["filter_pids"] = int32(1) + filterPids = int32(0) } - m["g_bpf_debug"] = p.cfg.EBPF.BpfDebug - return []*ebpfcommon.SpecBundle{{Spec: spec, Objects: &p.bpfObjects, Constants: m}}, nil + return map[string]any{ + "filter_pids": filterPids, + "g_bpf_debug": p.cfg.EBPF.BpfDebug, + } } func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} diff --git a/pkg/internal/ebpf/logenricher/logenricher.go b/pkg/internal/ebpf/logenricher/logenricher.go index 9ea98033cc..f14c2290fd 100644 --- a/pkg/internal/ebpf/logenricher/logenricher.go +++ b/pkg/internal/ebpf/logenricher/logenricher.go @@ -98,10 +98,14 @@ func (p *Tracer) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { return []*ebpfcommon.SpecBundle{{ Spec: spec, Objects: &p.bpfObjects, - Constants: map[string]any{"g_bpf_debug": p.cfg.EBPF.BpfDebug}, + Constants: p.constants(), }}, nil } +func (p *Tracer) constants() map[string]any { + return map[string]any{"g_bpf_debug": p.cfg.EBPF.BpfDebug} +} + func (p *Tracer) SetupTailCalls() {} func (p *Tracer) RegisterOffsets(_ *exec.FileInfo, _ *goexec.Offsets) {} diff --git a/pkg/internal/ebpf/logger/logger.go b/pkg/internal/ebpf/logger/logger.go index 4dd5d11c81..8ee0242ad9 100644 --- a/pkg/internal/ebpf/logger/logger.go +++ b/pkg/internal/ebpf/logger/logger.go @@ -50,12 +50,16 @@ func (p *BPFLogger) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { return []*ebpfcommon.SpecBundle{{ Spec: spec, Objects: &p.bpfObjects, - Constants: map[string]any{"g_bpf_debug": p.cfg.EBPF.BpfDebug}, + Constants: p.constants(), }}, nil } return nil, errors.New("BPF debug is not enabled") } +func (p *BPFLogger) constants() map[string]any { + return map[string]any{"g_bpf_debug": p.cfg.EBPF.BpfDebug} +} + func (p *BPFLogger) AddCloser(c ...io.Closer) { p.closers = append(p.closers, c...) } diff --git a/pkg/internal/ebpf/watcher/watcher.go b/pkg/internal/ebpf/watcher/watcher.go index a7e4e09449..7482345124 100644 --- a/pkg/internal/ebpf/watcher/watcher.go +++ b/pkg/internal/ebpf/watcher/watcher.go @@ -58,10 +58,14 @@ func (p *Watcher) LoadSpecs() ([]*ebpfcommon.SpecBundle, error) { return []*ebpfcommon.SpecBundle{{ Spec: spec, Objects: &p.bpfObjects, - Constants: map[string]any{"g_bpf_debug": p.cfg.EBPF.BpfDebug}, + Constants: p.constants(), }}, nil } +func (p *Watcher) constants() map[string]any { + return map[string]any{"g_bpf_debug": p.cfg.EBPF.BpfDebug} +} + func (p *Watcher) AddCloser(c ...io.Closer) { p.closers = append(p.closers, c...) } From f5061955444c18c77e0eaa1c168beea2d6b234c6 Mon Sep 17 00:00:00 2001 From: Rafael Roquetto Date: Fri, 27 Feb 2026 11:50:21 -0700 Subject: [PATCH 5/5] Review feedback --- pkg/ebpf/tracer_linux.go | 4 ++-- pkg/internal/ebpf/tpinjector/tpinjector_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/ebpf/tracer_linux.go b/pkg/ebpf/tracer_linux.go index 4bbad8e948..b0813afeb6 100644 --- a/pkg/ebpf/tracer_linux.go +++ b/pkg/ebpf/tracer_linux.go @@ -211,7 +211,7 @@ func (pt *ProcessTracer) makeOtelBPFFSPath() (string, error) { return otelPath, nil } -func (pt *ProcessTracer) setupOtelBPFFFSPath(bundles []*common.SpecBundle) string { +func (pt *ProcessTracer) setupOtelBPFFSPath(bundles []*common.SpecBundle) string { // Set up BPF FS path once for all specs otelBPFFSPath, err := pt.makeOtelBPFFSPath() @@ -245,7 +245,7 @@ func (pt *ProcessTracer) loadAndAssign(eventContext *common.EBPFEventContext, p return fmt.Errorf("loading eBPF program specs: %w", err) } - otelBPFFSPath := pt.setupOtelBPFFFSPath(bundles) + otelBPFFSPath := pt.setupOtelBPFFSPath(bundles) for i, bundle := range bundles { if err := loadSpec(eventContext, bundle, otelBPFFSPath, i); err != nil { diff --git a/pkg/internal/ebpf/tpinjector/tpinjector_test.go b/pkg/internal/ebpf/tpinjector/tpinjector_test.go index 770ee0cd34..8007ddec98 100644 --- a/pkg/internal/ebpf/tpinjector/tpinjector_test.go +++ b/pkg/internal/ebpf/tpinjector/tpinjector_test.go @@ -94,7 +94,7 @@ func TestTracer_Constants(t *testing.T) { bundles, err := New(cfg).LoadSpecs() require.NoError(t, err) - require.Len(t, bundles, expectedSpecCount, "tpinjector should have exactly one spec") + require.Len(t, bundles, expectedSpecCount, "tpinjector bundle count must match") // Spec 0 (tpinjector) carries the main constants. c := bundles[0].Constants