Skip to content

Commit 3264e35

Browse files
committed
(wip) shift process management from Probe to Manager
1 parent e566f24 commit 3264e35

File tree

3 files changed

+270
-190
lines changed

3 files changed

+270
-190
lines changed

internal/pkg/instrumentation/manager.go

+188-3
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,20 @@ import (
99
"context"
1010
"errors"
1111
"fmt"
12+
"io"
1213
"log/slog"
1314
"sync"
1415

16+
"github.com/cilium/ebpf"
1517
"github.com/cilium/ebpf/link"
1618
"github.com/cilium/ebpf/rlimit"
1719

1820
"go.opentelemetry.io/otel/trace"
1921

22+
"go.opentelemetry.io/auto/internal/pkg/inject"
2023
"go.opentelemetry.io/auto/internal/pkg/instrumentation/bpffs"
2124
"go.opentelemetry.io/auto/internal/pkg/instrumentation/probe"
25+
"go.opentelemetry.io/auto/internal/pkg/instrumentation/utils"
2226
"go.opentelemetry.io/auto/internal/pkg/opentelemetry"
2327
"go.opentelemetry.io/auto/internal/pkg/process"
2428
)
@@ -54,6 +58,7 @@ type Manager struct {
5458
probeMu sync.Mutex
5559
state managerState
5660
stateMu sync.RWMutex
61+
collectionOpts *ebpf.CollectionOptions
5762
}
5863

5964
// NewManager returns a new [Manager].
@@ -88,6 +93,18 @@ func NewManager(
8893
return nil, err
8994
}
9095

96+
m.collectionOpts = &ebpf.CollectionOptions{
97+
Maps: ebpf.MapOptions{
98+
PinPath: bpffs.PathForTargetApplication(m.proc),
99+
},
100+
}
101+
102+
alloc, err := process.Allocate(logger, pid)
103+
if err != nil {
104+
return nil, err
105+
}
106+
m.proc.Allocation = alloc
107+
91108
m.logger.Info("loaded process info", "process", m.proc)
92109

93110
m.filterUnusedProbes()
@@ -215,7 +232,7 @@ func (m *Manager) applyConfig(c Config) error {
215232

216233
if !currentlyEnabled && newEnabled {
217234
m.logger.Info("Enabling probe", "id", id)
218-
err = errors.Join(err, p.Load(m.exe, m.proc, c.SamplingConfig))
235+
err = errors.Join(err, m.LoadProbe(p, id, c))
219236
if err == nil {
220237
m.runProbe(p)
221238
}
@@ -379,8 +396,7 @@ func (m *Manager) loadProbes() error {
379396
// Load probes
380397
for name, i := range m.probes {
381398
if isProbeEnabled(name, m.currentConfig) {
382-
m.logger.Info("loading probe", "name", name)
383-
err := i.Load(exe, m.proc, m.currentConfig.SamplingConfig)
399+
err := m.LoadProbe(i, name, m.currentConfig)
384400
if err != nil {
385401
m.logger.Error(
386402
"error while loading probes, cleaning up",
@@ -398,6 +414,175 @@ func (m *Manager) loadProbes() error {
398414
return nil
399415
}
400416

417+
func (m *Manager) LoadProbe(i probe.Probe, name probe.ID, cfg Config) error {
418+
m.logger.Info("loading probe", "name", name)
419+
420+
err := i.Init(cfg.SamplingConfig)
421+
if err != nil {
422+
return errors.Join(err, m.cleanup())
423+
}
424+
425+
spec, err := i.Spec()
426+
if err != nil {
427+
return errors.Join(err, m.cleanup())
428+
}
429+
430+
err = m.InjectProbeConsts(i, spec)
431+
if err != nil {
432+
return err
433+
}
434+
435+
c, err := utils.InitializeEBPFCollection(spec, m.collectionOpts)
436+
if err != nil {
437+
return err
438+
}
439+
i.SetCollection(c)
440+
return nil
441+
}
442+
443+
func (m *Manager) InjectProbeConsts(i probe.Probe, spec *ebpf.CollectionSpec) error {
444+
var err error
445+
var opts []inject.Option
446+
for _, cnst := range i.GetConsts() {
447+
if l, ok := cnst.(probe.SetLogger); ok {
448+
cnst = l.SetLogger(i.GetLogger())
449+
}
450+
451+
o, e := cnst.InjectOption(m.proc)
452+
err = errors.Join(err, e)
453+
if e == nil && o != nil {
454+
opts = append(opts, o)
455+
}
456+
}
457+
if err != nil {
458+
return err
459+
}
460+
461+
return inject.Constants(spec, opts...)
462+
}
463+
464+
func (m *Manager) loadUprobesFromProbe(i probe.Probe) error {
465+
for _, up := range i.GetUprobes() {
466+
var skip bool
467+
for _, pc := range up.PackageConstraints {
468+
if pc.Constraints.Check(m.proc.Modules[pc.Package]) {
469+
continue
470+
}
471+
472+
var logFn func(string, ...any)
473+
switch pc.FailureMode {
474+
case probe.FailureModeIgnore:
475+
logFn = i.GetLogger().Debug
476+
case probe.FailureModeWarn:
477+
logFn = i.GetLogger().Warn
478+
default:
479+
// Unknown and FailureModeError.
480+
return fmt.Errorf(
481+
"uprobe %s package constraint (%s) not met, version %v",
482+
up.Sym,
483+
pc.Constraints.String(),
484+
m.proc.Modules[pc.Package])
485+
}
486+
487+
logFn(
488+
"package constraint not meet, skipping uprobe",
489+
"probe", i.Manifest().ID,
490+
"symbol", up.Sym,
491+
"package", pc.Package,
492+
"constraint", pc.Constraints.String(),
493+
"version", m.proc.Modules[pc.Package],
494+
)
495+
496+
skip = true
497+
break
498+
}
499+
if skip {
500+
continue
501+
}
502+
503+
err := m.loadUprobe(up, i.GetCollection())
504+
if err != nil {
505+
var logFn func(string, ...any)
506+
switch up.FailureMode {
507+
case probe.FailureModeIgnore:
508+
logFn = i.GetLogger().Debug
509+
case probe.FailureModeWarn:
510+
logFn = i.GetLogger().Warn
511+
default:
512+
// Unknown and FailureModeError.
513+
return err
514+
}
515+
logFn("failed to load uprobe", "probe", i.Manifest().ID, "symbol", up.Sym, "error", err)
516+
continue
517+
}
518+
_ = i.UpdateClosers(up)
519+
}
520+
return nil
521+
}
522+
523+
func (m *Manager) loadUprobe(u *probe.Uprobe, c *ebpf.Collection) error {
524+
offset, err := m.proc.GetFunctionOffset(u.Sym)
525+
if err != nil {
526+
return err
527+
}
528+
529+
var closers []io.Closer
530+
531+
if u.EntryProbe != "" {
532+
entryProg, ok := c.Programs[u.EntryProbe]
533+
if !ok {
534+
return fmt.Errorf("entry probe %s not found", u.EntryProbe)
535+
}
536+
opts := &link.UprobeOptions{Address: offset, PID: int(m.proc.ID)}
537+
l, err := m.exe.Uprobe("", entryProg, opts)
538+
if err != nil {
539+
return err
540+
}
541+
closers = append(closers, l)
542+
}
543+
544+
if u.ReturnProbe != "" {
545+
retProg, ok := c.Programs[u.ReturnProbe]
546+
if !ok {
547+
return fmt.Errorf("return probe %s not found", u.ReturnProbe)
548+
}
549+
retOffsets, err := m.proc.GetFunctionReturns(u.Sym)
550+
if err != nil {
551+
return err
552+
}
553+
554+
for _, ret := range retOffsets {
555+
opts := &link.UprobeOptions{Address: ret, PID: int(m.proc.ID)}
556+
l, err := m.exe.Uprobe("", retProg, opts)
557+
if err != nil {
558+
return err
559+
}
560+
closers = append(closers, l)
561+
}
562+
}
563+
564+
old := u.Closers.Swap(&closers)
565+
if old != nil {
566+
// load called twice without calling Close. Try and handle gracefully.
567+
var err error
568+
for _, closer := range *old {
569+
err = errors.Join(err, closer.Close())
570+
}
571+
return err
572+
}
573+
574+
return nil
575+
}
576+
577+
func (m *Manager) mount() error {
578+
if m.proc.Allocation != nil {
579+
m.logger.Debug("Mounting bpffs", "allocation", m.proc.Allocation)
580+
} else {
581+
m.logger.Debug("Mounting bpffs")
582+
}
583+
return bpffsMount(m.proc)
584+
}
585+
401586
func (m *Manager) cleanup() error {
402587
ctx := context.Background()
403588
err := m.cp.Shutdown(context.Background())

internal/pkg/instrumentation/manager_test.go

+31-1
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ package instrumentation
88
import (
99
"context"
1010
"errors"
11+
"io"
1112
"log/slog"
1213
"sync/atomic"
1314
"testing"
1415
"time"
1516

1617
"github.com/Masterminds/semver/v3"
18+
"github.com/cilium/ebpf"
1719
"github.com/cilium/ebpf/link"
1820
"github.com/stretchr/testify/assert"
1921
"github.com/stretchr/testify/require"
@@ -352,7 +354,35 @@ type noopProbe struct {
352354

353355
var _ probe.Probe = (*noopProbe)(nil)
354356

355-
func (p *noopProbe) Load(*link.Executable, *process.Info, *sampling.Config) error {
357+
func (p *noopProbe) GetLogger() *slog.Logger {
358+
return nil
359+
}
360+
361+
func (p *noopProbe) Spec() (*ebpf.CollectionSpec, error) {
362+
return nil, nil
363+
}
364+
365+
func (p *noopProbe) SetCollection(*ebpf.Collection) {
366+
return
367+
}
368+
369+
func (p *noopProbe) GetCollection() (*ebpf.Collection) {
370+
return nil
371+
}
372+
373+
func (p *noopProbe) GetConsts() []probe.Const {
374+
return nil
375+
}
376+
377+
func (p *noopProbe) GetUprobes() []*probe.Uprobe {
378+
return nil
379+
}
380+
381+
func (p *noopProbe) UpdateClosers(...io.Closer) []io.Closer {
382+
return nil
383+
}
384+
385+
func (p *noopProbe) Init(*sampling.Config) error {
356386
p.loaded.Store(true)
357387
return nil
358388
}

0 commit comments

Comments
 (0)