Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bpf/maps/sock_dir.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <bpfcore/vmlinux.h>
#include <bpfcore/bpf_helpers.h>

#include <common/pin_internal.h>

// 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
Expand All @@ -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");
2 changes: 2 additions & 0 deletions bpf/tpinjector/sock_iter.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

#include <maps/sock_dir.h>

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 };

Expand Down
2 changes: 0 additions & 2 deletions bpf/tpinjector/tpinjector.c
Original file line number Diff line number Diff line change
Expand Up @@ -975,5 +975,3 @@ int obi_packet_extender_create_tp(struct sk_msg_md *msg) {

return SK_PASS;
}

#include "sock_iter.c"
16 changes: 16 additions & 0 deletions pkg/ebpf/common/spec.go
Original file line number Diff line number Diff line change
@@ -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
}
12 changes: 4 additions & 8 deletions pkg/ebpf/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,13 @@ type PIDsAccounter interface {
}

type CommonTracer interface {
// Load the bpf object that is generated by the bpf2go compiler
Load() (*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 that are created by the bpf2go compiler
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()
}

Expand All @@ -87,9 +86,6 @@ 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
// 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
Expand Down
100 changes: 61 additions & 39 deletions pkg/ebpf/tracer_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,34 @@ func resolveMaps(eventContext *common.EBPFEventContext, spec *ebpf.CollectionSpe
return &collOpts, nil
}

func loadSpec(eventContext *common.EBPFEventContext, bundle *common.SpecBundle, otelBPFFSPath string, idx int) error {
Comment thread
grcevski marked this conversation as resolved.
if err := ebpfconvenience.RewriteConstants(bundle.Spec, bundle.Constants); err != nil {
return fmt.Errorf("rewriting BPF constants for spec %d: %w", idx, err)
Comment thread
mmat11 marked this conversation as resolved.
}

collOpts, err := resolveMaps(eventContext, bundle.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 := bundle.Spec.LoadAndAssign(bundle.Objects, collOpts); err != nil {
return fmt.Errorf("loading spec %d: %w", idx, err)
}

return nil
}

func closeLoadedSpecs(bundles []*common.SpecBundle) {
for _, bundle := range bundles {
if c, ok := bundle.Objects.(io.Closer); ok {
c.Close()
}
}
}

func unloadInternalMaps(eventContext *common.EBPFEventContext) {
eventContext.MapsLock.Lock()
defer eventContext.MapsLock.Unlock()
Expand Down Expand Up @@ -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")

Expand All @@ -195,43 +211,50 @@ func (pt *ProcessTracer) makeOtelBPFFSPath() (string, error) {
return otelPath, nil
}

func (pt *ProcessTracer) setupBPFFS(spec *ebpf.CollectionSpec) string {
func (pt *ProcessTracer) setupOtelBPFFSPath(bundles []*common.SpecBundle) 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)

log.Warn("OBI will still work, but features depending on pinned maps (e.g., log enricher, profile correlation) will be disabled")

for _, v := range spec.Maps {
if v.Pinning == ebpf.PinByName {
v.Pinning = ebpf.PinNone
v.MaxEntries = 1
// disable pinning for ALL specs
for _, bundle := range bundles {
for _, v := range bundle.Spec.Maps {
if v.Pinning == ebpf.PinByName {
v.Pinning = ebpf.PinNone
v.MaxEntries = 1
}
}
}

return ""
}

func (pt *ProcessTracer) loadAndAssign(eventContext *common.EBPFEventContext, p Tracer) error {
spec, err := pt.loadSpec(p)
if err != nil {
return err
}

collOpts, err := resolveMaps(eventContext, spec)
bundles, err := p.LoadSpecs()
if err != nil {
return err
return fmt.Errorf("loading eBPF program specs: %w", err)
}

otelBPFFSPath := pt.setupBPFFS(spec)
otelBPFFSPath := pt.setupOtelBPFFSPath(bundles)

collOpts.Programs = ebpf.ProgramOptions{LogSizeStart: 640 * 1024}
collOpts.Maps = ebpf.MapOptions{PinPath: otelBPFFSPath}
for i, bundle := range bundles {
if err := loadSpec(eventContext, bundle, otelBPFFSPath, i); err != nil {
closeLoadedSpecs(bundles[:i])
return err
}
}

return spec.LoadAndAssign(p.BpfObjects(), collOpts)
return nil
}

func (pt *ProcessTracer) loadTracer(eventContext *common.EBPFEventContext, p Tracer, log *slog.Logger) error {
Expand Down Expand Up @@ -414,19 +437,18 @@ func RunUtilityTracer(ctx context.Context, eventContext *common.EBPFEventContext
i := instrumenter{}
plog := ptlog()
plog.Debug("loading independent eBPF program")
spec, err := p.Load()
if err != nil {
return fmt.Errorf("loading eBPF program: %w", err)
}

collOpts, err := resolveMaps(eventContext, spec)
bundles, err := p.LoadSpecs()
if err != nil {
return err
return fmt.Errorf("loading eBPF program specs: %w", err)
}

if err := spec.LoadAndAssign(p.BpfObjects(), collOpts); err != nil {
printVerifierErrorInfo(err)
return fmt.Errorf("loading and assigning BPF objects: %w", err)
for idx, bundle := range bundles {
if err := loadSpec(eventContext, bundle, "", idx); err != nil {
closeLoadedSpecs(bundles[:idx])
printVerifierErrorInfo(err)
return err
}
}

if err := i.kprobes(p); err != nil {
Expand Down
10 changes: 3 additions & 7 deletions pkg/internal/ebpf/generictracer/generictracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() ([]*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))
Expand All @@ -140,7 +140,7 @@ func (p *Tracer) Load() (*ebpf.CollectionSpec, error) {

ebpfcommon.FixupSpec(spec, p.cfg.EBPF.OverrideBPFLoopEnabled)

return spec, err
return []*ebpfcommon.SpecBundle{{Spec: spec, Objects: &p.bpfObjects, Constants: p.constants()}}, nil
}

func (p *Tracer) SetupTailCalls() {
Expand All @@ -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{}))
Expand Down Expand Up @@ -212,10 +212,6 @@ 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) AddCloser(c ...io.Closer) {
p.closers = append(p.closers, c...)
}
Expand Down
4 changes: 1 addition & 3 deletions pkg/internal/ebpf/generictracer/generictracer_notlinux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) Load() (*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 }
Expand All @@ -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) {}
Expand Down
22 changes: 13 additions & 9 deletions pkg/internal/ebpf/gotracer/gotracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,24 @@ func (p *Tracer) supportsContextPropagation() bool {
return !ebpfcommon.IntegrityModeOverride && ebpfcommon.SupportsContextPropagationWithProbe(p.log)
}

func (p *Tracer) Load() (*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.")
}

return LoadBpf()
}
spec, err := LoadBpf()
if err != nil {
return nil, err
}

func (p *Tracer) SetupTailCalls() {
return []*ebpfcommon.SpecBundle{{
Spec: spec,
Objects: &p.bpfObjects,
Constants: p.constants(),
}}, nil
}

func (p *Tracer) Constants() map[string]any {
func (p *Tracer) constants() map[string]any {
blackBoxCP := uint32(0)
if p.cfg.DisableBlackBoxCP {
blackBoxCP = uint32(1)
Expand All @@ -120,6 +126,8 @@ func (p *Tracer) Constants() map[string]any {
}
}

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
Expand Down Expand Up @@ -246,10 +254,6 @@ 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) AddCloser(c ...io.Closer) {
p.closers = append(p.closers, c...)
}
Expand Down
28 changes: 14 additions & 14 deletions pkg/internal/ebpf/gpuevent/gpuevent.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,34 +86,34 @@ 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() ([]*ebpfcommon.SpecBundle, error) {
spec, err := LoadBpf()
if err != nil {
return nil, err
}

func (p *Tracer) Constants() map[string]any {
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 m
return map[string]any{
"filter_pids": filterPids,
"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) AddCloser(c ...io.Closer) {
p.closers = append(p.closers, c...)
}
Expand Down
Loading