Skip to content

Commit b9a7efe

Browse files
authored
Move process Info creation into NewManager (#1943)
The Manager manages probes for a process. Have the manager handle the process info creation and coordination instead of extraneously passing it from the Instrumentation.
1 parent df6b05f commit b9a7efe

File tree

4 files changed

+119
-141
lines changed

4 files changed

+119
-141
lines changed

instrumentation.go

+4-31
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,7 @@ const (
5353
// Instrumentation manages and controls all OpenTelemetry Go
5454
// auto-instrumentation.
5555
type Instrumentation struct {
56-
target *process.Info
57-
analyzer *process.Analyzer
58-
manager *instrumentation.Manager
56+
manager *instrumentation.Manager
5957

6058
stopMu sync.Mutex
6159
stop context.CancelFunc
@@ -97,42 +95,17 @@ func NewInstrumentation(ctx context.Context, opts ...InstrumentationOption) (*In
9795
}
9896

9997
cp := convertConfigProvider(c.cp)
100-
mngr, err := instrumentation.NewManager(c.logger, ctrl, cp, p...)
98+
mngr, err := instrumentation.NewManager(c.logger, ctrl, c.pid, cp, p...)
10199
if err != nil {
102100
return nil, err
103101
}
104102

105-
pa := process.NewAnalyzer(c.logger, c.pid)
106-
pi, err := pa.Analyze(mngr.GetRelevantFuncs())
107-
if err != nil {
108-
return nil, err
109-
}
110-
111-
alloc, err := process.Allocate(c.logger, c.pid)
112-
if err != nil {
113-
return nil, err
114-
}
115-
pi.Allocation = alloc
116-
117-
c.logger.Info(
118-
"target process analysis completed",
119-
"pid", pi.ID,
120-
"go_version", pi.GoVersion,
121-
"dependencies", pi.Modules,
122-
"total_functions_found", len(pi.Functions),
123-
)
124-
mngr.FilterUnusedProbes(pi)
125-
126-
return &Instrumentation{
127-
target: pi,
128-
analyzer: pa,
129-
manager: mngr,
130-
}, nil
103+
return &Instrumentation{manager: mngr}, nil
131104
}
132105

133106
// Load loads and attaches the relevant probes to the target process.
134107
func (i *Instrumentation) Load(ctx context.Context) error {
135-
return i.manager.Load(ctx, i.target)
108+
return i.manager.Load(ctx)
136109
}
137110

138111
// Run starts the instrumentation. It must be called after [Instrumentation.Load].

internal/pkg/instrumentation/manager.go

+44-42
Original file line numberDiff line numberDiff line change
@@ -55,19 +55,43 @@ type Manager struct {
5555
}
5656

5757
// NewManager returns a new [Manager].
58-
func NewManager(logger *slog.Logger, otelController *opentelemetry.Controller, cp ConfigProvider, probes ...probe.Probe) (*Manager, error) {
58+
func NewManager(logger *slog.Logger, otelController *opentelemetry.Controller, pid process.ID, cp ConfigProvider, probes ...probe.Probe) (*Manager, error) {
5959
m := &Manager{
6060
logger: logger,
6161
probes: make(map[probe.ID]probe.Probe),
6262
otelController: otelController,
6363
cp: cp,
6464
}
6565

66-
err := m.registerProbes(probes)
66+
funcs := make(map[string]any)
67+
for _, p := range probes {
68+
if err := m.registerProbe(p); err != nil {
69+
return nil, err
70+
}
71+
72+
for _, s := range p.Manifest().Symbols {
73+
funcs[s.Symbol] = nil
74+
}
75+
}
76+
77+
pa := process.NewAnalyzer(logger, pid)
78+
79+
var err error
80+
m.proc, err = pa.Analyze(funcs)
6781
if err != nil {
6882
return nil, err
6983
}
7084

85+
alloc, err := process.Allocate(logger, pid)
86+
if err != nil {
87+
return nil, err
88+
}
89+
m.proc.Allocation = alloc
90+
91+
m.logger.Info("loaded process info", "process", m.proc)
92+
93+
m.filterUnusedProbes()
94+
7195
return m, nil
7296
}
7397

@@ -103,23 +127,11 @@ func (m *Manager) registerProbe(p probe.Probe) error {
103127
return nil
104128
}
105129

106-
// GetRelevantFuncs returns the instrumented functions for all managed probes.
107-
func (m *Manager) GetRelevantFuncs() map[string]interface{} {
108-
funcsMap := make(map[string]interface{})
109-
for _, i := range m.probes {
110-
for _, s := range i.Manifest().Symbols {
111-
funcsMap[s.Symbol] = nil
112-
}
113-
}
114-
115-
return funcsMap
116-
}
117-
118-
// FilterUnusedProbes filterers probes whose functions are already instrumented
130+
// filterUnusedProbes filterers probes whose functions are already instrumented
119131
// out of the Manager.
120-
func (m *Manager) FilterUnusedProbes(target *process.Info) {
132+
func (m *Manager) filterUnusedProbes() {
121133
existingFuncMap := make(map[string]interface{})
122-
for _, f := range target.Functions {
134+
for _, f := range m.proc.Functions {
123135
existingFuncMap[f.Name] = nil
124136
}
125137

@@ -237,14 +249,14 @@ func (m *Manager) ConfigLoop(ctx context.Context) {
237249
}
238250
}
239251

240-
func (m *Manager) Load(ctx context.Context, target *process.Info) error {
252+
func (m *Manager) Load(ctx context.Context) error {
241253
if len(m.probes) == 0 {
242254
return errors.New("no instrumentation for target process")
243255
}
244256
if m.cp == nil {
245257
return errors.New("no config provider set")
246258
}
247-
if target == nil {
259+
if m.proc == nil {
248260
return errors.New("target details not set - load is called on non-initialized instrumentation")
249261
}
250262
m.stateMu.Lock()
@@ -255,12 +267,11 @@ func (m *Manager) Load(ctx context.Context, target *process.Info) error {
255267
}
256268

257269
m.currentConfig = m.cp.InitialConfig(ctx)
258-
err := m.loadProbes(target)
270+
err := m.loadProbes()
259271
if err != nil {
260272
return err
261273
}
262274

263-
m.proc = target
264275
m.state = managerStateLoaded
265276

266277
return nil
@@ -330,7 +341,7 @@ func (m *Manager) Stop() error {
330341
defer m.probeMu.Unlock()
331342

332343
m.logger.Debug("Shutting down all probes")
333-
err := m.cleanup(m.proc)
344+
err := m.cleanup()
334345

335346
// Wait for all probes to stop.
336347
m.runningProbesWG.Wait()
@@ -339,30 +350,30 @@ func (m *Manager) Stop() error {
339350
return err
340351
}
341352

342-
func (m *Manager) loadProbes(target *process.Info) error {
353+
func (m *Manager) loadProbes() error {
343354
// Remove resource limits for kernels <5.11.
344355
if err := rlimitRemoveMemlock(); err != nil {
345356
return err
346357
}
347358

348-
exe, err := openExecutable(target.ID.ExePath())
359+
exe, err := openExecutable(m.proc.ID.ExePath())
349360
if err != nil {
350361
return err
351362
}
352363
m.exe = exe
353364

354-
if err := m.mount(target); err != nil {
365+
if err := m.mount(); err != nil {
355366
return err
356367
}
357368

358369
// Load probes
359370
for name, i := range m.probes {
360371
if isProbeEnabled(name, m.currentConfig) {
361372
m.logger.Info("loading probe", "name", name)
362-
err := i.Load(exe, target, m.currentConfig.SamplingConfig)
373+
err := i.Load(exe, m.proc, m.currentConfig.SamplingConfig)
363374
if err != nil {
364375
m.logger.Error("error while loading probes, cleaning up", "error", err, "name", name)
365-
return errors.Join(err, m.cleanup(target))
376+
return errors.Join(err, m.cleanup())
366377
}
367378
}
368379
}
@@ -371,16 +382,16 @@ func (m *Manager) loadProbes(target *process.Info) error {
371382
return nil
372383
}
373384

374-
func (m *Manager) mount(target *process.Info) error {
375-
if target.Allocation != nil {
376-
m.logger.Debug("Mounting bpffs", "allocation", target.Allocation)
385+
func (m *Manager) mount() error {
386+
if m.proc.Allocation != nil {
387+
m.logger.Debug("Mounting bpffs", "allocation", m.proc.Allocation)
377388
} else {
378389
m.logger.Debug("Mounting bpffs")
379390
}
380-
return bpffsMount(target)
391+
return bpffsMount(m.proc)
381392
}
382393

383-
func (m *Manager) cleanup(target *process.Info) error {
394+
func (m *Manager) cleanup() error {
384395
ctx := context.Background()
385396
err := m.cp.Shutdown(context.Background())
386397
for _, i := range m.probes {
@@ -394,14 +405,5 @@ func (m *Manager) cleanup(target *process.Info) error {
394405
}
395406

396407
m.logger.Debug("Cleaning bpffs")
397-
return errors.Join(err, bpffsCleanup(target))
398-
}
399-
400-
func (m *Manager) registerProbes(probes []probe.Probe) error {
401-
for _, p := range probes {
402-
if err := m.registerProbe(p); err != nil {
403-
return err
404-
}
405-
}
406-
return nil
408+
return errors.Join(err, bpffsCleanup(m.proc))
407409
}

internal/pkg/instrumentation/manager_load_test.go

+26-6
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ import (
2222
grpcServer "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/google.golang.org/grpc/server"
2323
httpClient "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/net/http/client"
2424
httpServer "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpf/net/http/server"
25+
"go.opentelemetry.io/auto/internal/pkg/instrumentation/probe"
2526
"go.opentelemetry.io/auto/internal/pkg/instrumentation/testutils"
2627
"go.opentelemetry.io/auto/internal/pkg/instrumentation/utils"
28+
"go.opentelemetry.io/auto/internal/pkg/process"
29+
"go.opentelemetry.io/auto/internal/pkg/process/binary"
2730
)
2831

2932
func TestLoadProbes(t *testing.T) {
@@ -51,10 +54,9 @@ func TestLoadProbes(t *testing.T) {
5154
}
5255
}
5356

54-
func fakeManager(t *testing.T) *Manager {
57+
func fakeManager(t *testing.T, fnNames ...string) *Manager {
5558
logger := slog.Default()
56-
m, err := NewManager(
57-
logger, nil, NewNoopConfigProvider(nil),
59+
probes := []probe.Probe{
5860
grpcClient.New(logger, ""),
5961
grpcServer.New(logger, ""),
6062
httpServer.New(logger, ""),
@@ -64,9 +66,27 @@ func fakeManager(t *testing.T) *Manager {
6466
kafkaConsumer.New(logger, ""),
6567
autosdk.New(logger),
6668
otelTraceGlobal.New(logger),
67-
)
68-
assert.NoError(t, err)
69-
assert.NotNil(t, m)
69+
}
70+
ver := semver.New(1, 20, 0, "", "")
71+
var fn []*binary.Func
72+
for _, name := range fnNames {
73+
fn = append(fn, &binary.Func{Name: name})
74+
}
75+
m := &Manager{
76+
logger: slog.Default(),
77+
cp: NewNoopConfigProvider(nil),
78+
probes: make(map[probe.ID]probe.Probe),
79+
proc: &process.Info{
80+
ID: 1,
81+
Functions: fn,
82+
GoVersion: ver,
83+
Modules: map[string]*semver.Version{},
84+
},
85+
}
86+
for _, p := range probes {
87+
m.probes[p.Manifest().ID] = p
88+
}
89+
m.filterUnusedProbes()
7090

7191
return m
7292
}

0 commit comments

Comments
 (0)