Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WithPID InstrumentationOption #355

Merged
merged 15 commits into from
Oct 9, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ OpenTelemetry Go Automatic Instrumentation adheres to [Semantic Versioning](http
### Added

- Add `WithServiceName` config option for instrumentation. ([#353](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/353))
- Add `WithPID` config option for instrumentation. ([#355](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/355))

### Changed

Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ generate:
docker-generate:
docker run --rm -v $(shell pwd):/app golang:1.20 /bin/sh -c "apt-get update && apt-get install -y clang llvm libbpf-dev && cd ../app && make generate"

.PHONY: docker-test
docker-test:
docker run --rm -v $(shell pwd):/app golang:1.20 /bin/sh -c "apt-get update && apt-get install -y clang llvm libbpf-dev && cd ../app && make test"

.PHONY: go-mod-tidy
go-mod-tidy: $(ALL_GO_MOD_DIRS:%=go-mod-tidy/%)
go-mod-tidy/%: DIR=$*
Expand Down
15 changes: 15 additions & 0 deletions instConfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,18 @@ func TestWithServiceName(t *testing.T) {
c = newInstConfig([]InstrumentationOption{WithServiceName((testServiceName))})
assert.Equal(t, envServiceName, c.serviceName)
}

func TestWithPID(t *testing.T) {
// Current PID
currPID := os.Getpid()
c := newInstConfig([]InstrumentationOption{WithPID(currPID)})
currExe, err := os.Executable()
if err != nil {
t.Error(err)
}
assert.Equal(t, currPID, c.target.Pid)

// PID should override valid target exe
c = newInstConfig([]InstrumentationOption{WithPID(currPID), WithTarget(currExe)})
assert.Equal(t, currPID, c.target.Pid)
}
43 changes: 36 additions & 7 deletions instrumentation.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ type Instrumentation struct {
manager *instrumentors.Manager
}

// Error message returned when instrumentation is launched without a target
// binary.
// Error message returned when instrumentation is launched without a valid target
// binary or pid.
var errUndefinedTarget = fmt.Errorf("undefined target Go binary, consider setting the %s environment variable pointing to the target binary to instrument", envTargetExeKey)

// NewInstrumentation returns a new [Instrumentation] configured with the
Expand Down Expand Up @@ -83,6 +83,14 @@ func NewInstrumentation(opts ...InstrumentationOption) (*Instrumentation, error)
mngr.Close()
return nil, err
}

if log.Logger.IsZero() {
err := log.Init()
if err != nil {
return nil, err
}
}

log.Logger.V(0).Info(
"target process analysis completed",
"pid", td.PID,
Expand Down Expand Up @@ -122,17 +130,19 @@ type instConfig struct {
}

func newInstConfig(opts []InstrumentationOption) instConfig {
var c instConfig
c := instConfig{target: &process.TargetArgs{}}
for _, opt := range opts {
c = opt.apply(c)
if opt != nil {
c = opt.apply(c)
}
}
c = c.applyEnv()
return c
}

func (c instConfig) applyEnv() instConfig {
if v, ok := os.LookupEnv(envTargetExeKey); ok {
c.target = &process.TargetArgs{ExePath: v}
c.target.ExePath = v
}
if v, ok := os.LookupEnv(envServiceNameKey); ok {
c.serviceName = v
Expand All @@ -146,7 +156,7 @@ func (c instConfig) applyEnv() instConfig {
}

func (c instConfig) setDefualtServiceName() instConfig {
if c.target != nil {
if c.target.ExePath != "" {
c.serviceName = fmt.Sprintf("%s:%s", serviceNameDefault, filepath.Base(c.target.ExePath))
} else {
c.serviceName = serviceNameDefault
Expand Down Expand Up @@ -190,14 +200,17 @@ func (o fnOpt) apply(c instConfig) instConfig { return o(c) }
// WithTarget returns an [InstrumentationOption] defining the target binary for
// [Instrumentation] that is being executed at the provided path.
//
// This option conflicts with [WithPID]. If both are used, [WithPID] will take
// precedence and be used.
//
// If multiple of these options are provided to an [Instrumentation], the last
// one will be used.
//
// If OTEL_GO_AUTO_TARGET_EXE is defined it will take precedence over any value
// passed here.
func WithTarget(path string) InstrumentationOption {
return fnOpt(func(c instConfig) instConfig {
c.target = &process.TargetArgs{ExePath: path}
c.target.ExePath = path
return c
})
}
Expand All @@ -215,3 +228,19 @@ func WithServiceName(serviceName string) InstrumentationOption {
return c
})
}

// WithPID returns an [InstrumentationOption] corresponding to the executable
// used by the provided pid.
//
// This option conflicts with [WithTarget]. If both are used, [WithPID]
// will take precedence and be used. If OTEL_GO_AUTO_TARGET_EXE is defined,
// the pid passed here will take precedence.
//
// If multiple of these options are provided to an [Instrumentation], the last
// one will be used.
func WithPID(pid int) InstrumentationOption {
return fnOpt(func(c instConfig) instConfig {
c.target.Pid = pid
return c
})
}
25 changes: 15 additions & 10 deletions internal/pkg/process/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ package process

import (
"errors"
"fmt"
"os"
"syscall"
)

// ExePathEnvVar is the environment variable key whose value points to the
Expand All @@ -26,26 +28,29 @@ const ExePathEnvVar = "OTEL_GO_AUTO_TARGET_EXE"
// TargetArgs are the binary target information.
type TargetArgs struct {
ExePath string
Pid int
}

// Validate validates t and returns an error if not valid.
func (t *TargetArgs) Validate() error {
if t.Pid != 0 {
return validatePID(t.Pid)
}
if t.ExePath == "" {
return errors.New("target binary path not specified, please specify " + ExePathEnvVar + " env variable")
}

return nil
}

// ParseTargetArgs returns TargetArgs for the target pointed to by the
// environment variable OTEL_GO_AUTO_TARGET_EXE.
func ParseTargetArgs() *TargetArgs {
result := &TargetArgs{}

val, exists := os.LookupEnv(ExePathEnvVar)
if exists {
result.ExePath = val
func validatePID(pid int) error {
p, err := os.FindProcess(pid)
if err != nil {
return fmt.Errorf("can't find process with pid %d", pid)
}

return result
err = p.Signal(syscall.Signal(0))
if err != nil {
return fmt.Errorf("process with pid %d does not exist", pid)
}
return nil
}
3 changes: 3 additions & 0 deletions internal/pkg/process/discover.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ func NewAnalyzer() *Analyzer {
// DiscoverProcessID searches for the target as an actively running process,
// returning its PID if found.
func (a *Analyzer) DiscoverProcessID(target *TargetArgs) (int, error) {
if target.Pid != 0 {
return target.Pid, nil
}
for {
select {
case <-a.done:
Expand Down
Loading