diff --git a/internal/pkg/instrumentation/manager.go b/internal/pkg/instrumentation/manager.go index 8bac20dd5..4a95d1db3 100644 --- a/internal/pkg/instrumentation/manager.go +++ b/internal/pkg/instrumentation/manager.go @@ -74,10 +74,8 @@ func NewManager(logger *slog.Logger, otelController *opentelemetry.Controller, p } } - pa := process.NewAnalyzer(logger, pid) - var err error - m.proc, err = pa.Analyze(funcs) + m.proc, err = process.NewInfo(pid, funcs) if err != nil { return nil, err } diff --git a/internal/pkg/process/analyze.go b/internal/pkg/process/analyze.go deleted file mode 100644 index 4a959b3e2..000000000 --- a/internal/pkg/process/analyze.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package process - -import ( - "debug/elf" - "errors" - "fmt" - - "github.com/Masterminds/semver/v3" - - "go.opentelemetry.io/auto/internal/pkg/process/binary" -) - -// Info are the details about a target process. -type Info struct { - ID ID - Functions []*binary.Func - GoVersion *semver.Version - Modules map[string]*semver.Version - Allocation *Allocation -} - -// GetFunctionOffset returns the offset for of the function with name. -func (i *Info) GetFunctionOffset(name string) (uint64, error) { - for _, f := range i.Functions { - if f.Name == name { - return f.Offset, nil - } - } - - return 0, fmt.Errorf("could not find offset for function %s", name) -} - -// GetFunctionReturns returns the return value of the call for the function -// with name. -func (i *Info) GetFunctionReturns(name string) ([]uint64, error) { - for _, f := range i.Functions { - if f.Name == name { - return f.ReturnOffsets, nil - } - } - - return nil, fmt.Errorf("could not find returns for function %s", name) -} - -// Analyze returns the target details for an actively running process. -func (a *Analyzer) Analyze(relevantFuncs map[string]interface{}) (*Info, error) { - result := &Info{ID: a.id} - - elfF, err := elf.Open(a.id.ExePath()) - if err != nil { - return nil, err - } - defer elfF.Close() - - bi, err := a.id.BuildInfo() - if err != nil { - return nil, err - } - - goVersion, err := semver.NewVersion(bi.GoVersion) - if err != nil { - return nil, err - } - result.GoVersion = goVersion - result.Modules = make(map[string]*semver.Version, len(bi.Deps)+1) - for _, dep := range bi.Deps { - depVersion, err := semver.NewVersion(dep.Version) - if err != nil { - a.logger.Error("parsing dependency version", "error", err, "dependency", dep) - continue - } - result.Modules[dep.Path] = depVersion - } - result.Modules["std"] = goVersion - - funcs, err := a.findFunctions(elfF, relevantFuncs) - if err != nil { - return nil, err - } - for _, fn := range funcs { - a.logger.Debug("found function", "function_name", fn) - } - - result.Functions = funcs - if len(result.Functions) == 0 { - return nil, errors.New("could not find function offsets for instrumenter") - } - - return result, nil -} - -func (a *Analyzer) findFunctions(elfF *elf.File, relevantFuncs map[string]interface{}) ([]*binary.Func, error) { - result, err := binary.FindFunctionsUnStripped(elfF, relevantFuncs) - if err != nil { - if errors.Is(err, elf.ErrNoSymbols) { - a.logger.Debug("No symbols found in binary, trying to find functions using .gosymtab") - return binary.FindFunctionsStripped(elfF, relevantFuncs) - } - return nil, err - } - - return result, nil -} diff --git a/internal/pkg/process/discover.go b/internal/pkg/process/discover.go deleted file mode 100644 index 3200f74c3..000000000 --- a/internal/pkg/process/discover.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package process - -import ( - "log/slog" -) - -// Analyzer is used to find actively running processes. -type Analyzer struct { - id ID - logger *slog.Logger -} - -// NewAnalyzer returns a new [ProcessAnalyzer]. -func NewAnalyzer(logger *slog.Logger, id ID) *Analyzer { - return &Analyzer{id: id, logger: logger} -} diff --git a/internal/pkg/process/info.go b/internal/pkg/process/info.go new file mode 100644 index 000000000..56c9ebbb4 --- /dev/null +++ b/internal/pkg/process/info.go @@ -0,0 +1,120 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package process + +import ( + "debug/elf" + "errors" + "fmt" + "runtime/debug" + + "github.com/Masterminds/semver/v3" + + "go.opentelemetry.io/auto/internal/pkg/process/binary" +) + +// Info are the details about a target process. +type Info struct { + ID ID + Functions []*binary.Func + GoVersion *semver.Version + Modules map[string]*semver.Version + Allocation *Allocation +} + +// NewInfo returns a new Info with information about the process identified by +// id. The functions of the returned Info are filtered by relevantFuncs. +// +// A partial Info and error may be returned for dependencies that cannot be +// parsed. +func NewInfo(id ID, relevantFuncs map[string]interface{}) (*Info, error) { + elfF, err := elf.Open(id.ExePath()) + if err != nil { + return nil, err + } + defer elfF.Close() + + bi, err := id.BuildInfo() + if err != nil { + return nil, err + } + + goVersion, err := semver.NewVersion(bi.GoVersion) + if err != nil { + return nil, err + } + + result := &Info{ + ID: id, + GoVersion: goVersion, + } + + result.Functions, err = findFunctions(elfF, relevantFuncs) + if err != nil { + return nil, err + } + + result.Modules, err = findModules(goVersion, bi.Deps) + return result, err +} + +func findModules(goVer *semver.Version, deps []*debug.Module) (map[string]*semver.Version, error) { + var err error + out := make(map[string]*semver.Version, len(deps)+1) + for _, dep := range deps { + depVersion, e := semver.NewVersion(dep.Version) + if e != nil { + err = errors.Join( + err, + fmt.Errorf("invalid dependency version %s (%s): %w", dep.Path, dep.Version, e), + ) + continue + } + out[dep.Path] = depVersion + } + out["std"] = goVer + return out, err +} + +func findFunctions(elfF *elf.File, relevantFuncs map[string]interface{}) ([]*binary.Func, error) { + found, err := binary.FindFunctionsUnStripped(elfF, relevantFuncs) + if err != nil { + if !errors.Is(err, elf.ErrNoSymbols) { + return nil, err + } + found, err = binary.FindFunctionsStripped(elfF, relevantFuncs) + if err != nil { + return nil, err + } + } + + if len(found) == 0 { + return nil, errors.New("no functions found") + } + + return found, nil +} + +// GetFunctionOffset returns the offset for of the function with name. +func (i *Info) GetFunctionOffset(name string) (uint64, error) { + for _, f := range i.Functions { + if f.Name == name { + return f.Offset, nil + } + } + + return 0, fmt.Errorf("could not find offset for function %s", name) +} + +// GetFunctionReturns returns the return value of the call for the function +// with name. +func (i *Info) GetFunctionReturns(name string) ([]uint64, error) { + for _, f := range i.Functions { + if f.Name == name { + return f.ReturnOffsets, nil + } + } + + return nil, fmt.Errorf("could not find returns for function %s", name) +}