Skip to content

Commit

Permalink
Completely remove inlined function program counter resolution
Browse files Browse the repository at this point in the history
Signed-off-by: grantseltzer <[email protected]>
  • Loading branch information
grantseltzer committed Dec 24, 2024
1 parent d558ac7 commit 580cbf7
Show file tree
Hide file tree
Showing 11 changed files with 35 additions and 122 deletions.
1 change: 0 additions & 1 deletion pkg/config/setup/system_probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ func InitSystemProbeConfig(cfg pkgconfigmodel.Config) {

// User Tracer
cfg.BindEnvAndSetDefault(join(diNS, "enabled"), false, "DD_DYNAMIC_INSTRUMENTATION_ENABLED")
cfg.BindEnvAndSetDefault(join(diNS, "resolve_inlined_program_counters"), false, "DD_DYNAMIC_INSTRUMENTATION_RESOLVE_INLINED_PROGRAM_COUNTERS")
cfg.BindEnvAndSetDefault(join(diNS, "offline_mode"), false, "DD_DYNAMIC_INSTRUMENTATION_OFFLINE_MODE")
cfg.BindEnvAndSetDefault(join(diNS, "probes_file_path"), false, "DD_DYNAMIC_INSTRUMENTATION_PROBES_FILE_PATH")
cfg.BindEnvAndSetDefault(join(diNS, "snapshot_output_file_path"), false, "DD_DYNAMIC_INSTRUMENTATION_SNAPSHOT_FILE_PATH")
Expand Down
14 changes: 6 additions & 8 deletions pkg/dynamicinstrumentation/di.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,9 @@ type ReaderWriterOptions struct { //nolint:revive // TODO

// DIOptions is used to configure the running Dynamic Instrumentation process
type DIOptions struct {
OfflineOptions OfflineOptions
ReaderWriterOptions ReaderWriterOptions
RateLimitPerProbePerSecond float64
ResolveInlinedProgramCounters bool
OfflineOptions OfflineOptions
ReaderWriterOptions ReaderWriterOptions
RateLimitPerProbePerSecond float64
ditypes.EventCallback
}

Expand All @@ -82,9 +81,8 @@ func RunDynamicInstrumentation(opts *DIOptions) (*GoDI, error) {
return nil, err
}
stopFunctions := []func(){}
runtimeOpts := &ditypes.RuntimeOptions{ResolveInlinedProgramCounters: opts.ResolveInlinedProgramCounters}
if opts.ReaderWriterOptions.CustomReaderWriters {
cm, err := diconfig.NewReaderConfigManager(runtimeOpts)
cm, err := diconfig.NewReaderConfigManager()
if err != nil {
return nil, fmt.Errorf("could not create new reader config manager: %w", err)
}
Expand All @@ -103,7 +101,7 @@ func RunDynamicInstrumentation(opts *DIOptions) (*GoDI, error) {
stats: newGoDIStats(),
}
} else if opts.OfflineOptions.Offline {
cm, stopFileConfigManager, err := diconfig.NewFileConfigManager(runtimeOpts, opts.OfflineOptions.ProbesFilePath)
cm, stopFileConfigManager, err := diconfig.NewFileConfigManager(opts.OfflineOptions.ProbesFilePath)
if err != nil {
return nil, fmt.Errorf("could not create new file config manager: %w", err)
}
Expand All @@ -123,7 +121,7 @@ func RunDynamicInstrumentation(opts *DIOptions) (*GoDI, error) {
}
stopFunctions = append(stopFunctions, stopFileConfigManager)
} else {
cm, err := diconfig.NewRCConfigManager(runtimeOpts)
cm, err := diconfig.NewRCConfigManager()
if err != nil {
return nil, fmt.Errorf("could not create new RC config manager: %w", err)
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/dynamicinstrumentation/diconfig/binary_inspection.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import (
// inspectGoBinaries goes through each service and populates information about the binary
// and the relevant parameters, and their types
// configEvent maps service names to info about the service and their configurations
func inspectGoBinaries(runtimeOptions *ditypes.RuntimeOptions, configEvent ditypes.DIProcs) error {
func inspectGoBinaries(configEvent ditypes.DIProcs) error {
var err error
for i := range configEvent {
err = AnalyzeBinary(runtimeOptions, configEvent[i])
err = AnalyzeBinary(configEvent[i])
if err != nil {
return fmt.Errorf("inspection of PID %d (path=%s) failed: %w", configEvent[i].PID, configEvent[i].BinaryPath, err)
}
Expand All @@ -33,7 +33,7 @@ func inspectGoBinaries(runtimeOptions *ditypes.RuntimeOptions, configEvent dityp

// AnalyzeBinary reads the binary associated with the specified process and parses
// the DWARF information. It populates relevant fields in the process representation
func AnalyzeBinary(runtimeOptions *ditypes.RuntimeOptions, procInfo *ditypes.ProcessInfo) error {
func AnalyzeBinary(procInfo *ditypes.ProcessInfo) error {
functions := []string{}
targetFunctions := map[string]bool{}
for _, probe := range procInfo.GetProbes() {
Expand All @@ -46,7 +46,7 @@ func AnalyzeBinary(runtimeOptions *ditypes.RuntimeOptions, procInfo *ditypes.Pro
return fmt.Errorf("could not retrieve debug information from binary: %w", err)
}

typeMap, err := getTypeMap(dwarfData, targetFunctions, runtimeOptions.ResolveInlinedProgramCounters)
typeMap, err := getTypeMap(dwarfData, targetFunctions)
if err != nil {
return fmt.Errorf("could not retrieve type information from binary %w", err)
}
Expand Down
23 changes: 9 additions & 14 deletions pkg/dynamicinstrumentation/diconfig/config_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ type rcConfig struct {
}
}

type configUpdateCallback func(*ditypes.RuntimeOptions, *ditypes.ProcessInfo, *ditypes.Probe)
type configUpdateCallback func(*ditypes.ProcessInfo, *ditypes.Probe)

// ConfigManager is a facility to track probe configurations for
// instrumenting tracked processes
Expand All @@ -60,17 +60,15 @@ type ConfigManager interface {
type RCConfigManager struct {
procTracker *proctracker.ProcessTracker

diProcs ditypes.DIProcs
callback configUpdateCallback
runtimeOptions *ditypes.RuntimeOptions
diProcs ditypes.DIProcs
callback configUpdateCallback
}

// NewRCConfigManager creates a new configuration manager which utilizes remote-config
func NewRCConfigManager(opts *ditypes.RuntimeOptions) (*RCConfigManager, error) {
func NewRCConfigManager() (*RCConfigManager, error) {
log.Info("Creating new RC config manager")
cm := &RCConfigManager{
callback: applyConfigUpdate,
runtimeOptions: opts,
callback: applyConfigUpdate,
}

cm.procTracker = proctracker.NewProcessTracker(cm.updateProcesses)
Expand Down Expand Up @@ -130,10 +128,7 @@ func (cm *RCConfigManager) installConfigProbe(procInfo *ditypes.ProcessInfo) err
svcConfigProbe.ServiceName = procInfo.ServiceName
procInfo.ProbesByID[configProbe.ID] = &svcConfigProbe
log.Infof("Installing config probe for service: %s.", svcConfigProbe.ServiceName)
configProbeRuntimeOpts := &ditypes.RuntimeOptions{
ResolveInlinedProgramCounters: false,
}
err = AnalyzeBinary(configProbeRuntimeOpts, procInfo)
err = AnalyzeBinary(procInfo)
if err != nil {
return fmt.Errorf("could not analyze binary for config probe: %w", err)
}
Expand Down Expand Up @@ -237,14 +232,14 @@ func (cm *RCConfigManager) readConfigs(r *ringbuf.Reader, procInfo *ditypes.Proc
// Check hash to see if the configuration changed
if configPath.Hash != probe.InstrumentationInfo.ConfigurationHash {
probe.InstrumentationInfo.ConfigurationHash = configPath.Hash
applyConfigUpdate(cm.runtimeOptions, procInfo, probe)
applyConfigUpdate(procInfo, probe)
}
}
}

func applyConfigUpdate(runtimeOptions *ditypes.RuntimeOptions, procInfo *ditypes.ProcessInfo, probe *ditypes.Probe) {
func applyConfigUpdate(procInfo *ditypes.ProcessInfo, probe *ditypes.Probe) {
log.Tracef("Applying config update: %v", probe)
err := AnalyzeBinary(runtimeOptions, procInfo)
err := AnalyzeBinary(procInfo)
if err != nil {
log.Errorf("couldn't inspect binary: %v\n", err)
return
Expand Down
28 changes: 4 additions & 24 deletions pkg/dynamicinstrumentation/diconfig/dwarf.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import (
"github.com/DataDog/datadog-agent/pkg/util/safeelf"
)

func getTypeMap(dwarfData *dwarf.Data, targetFunctions map[string]bool, resolveInlinedProgramCounters bool) (*ditypes.TypeMap, error) {
return loadFunctionDefinitions(dwarfData, targetFunctions, resolveInlinedProgramCounters)
func getTypeMap(dwarfData *dwarf.Data, targetFunctions map[string]bool) (*ditypes.TypeMap, error) {
return loadFunctionDefinitions(dwarfData, targetFunctions)
}

type seenTypeCounter struct {
Expand All @@ -33,15 +33,14 @@ type seenTypeCounter struct {

var seenTypes = make(map[string]*seenTypeCounter)

func loadFunctionDefinitions(dwarfData *dwarf.Data, targetFunctions map[string]bool, resolveInlinedProgramCounters bool) (*ditypes.TypeMap, error) {
func loadFunctionDefinitions(dwarfData *dwarf.Data, targetFunctions map[string]bool) (*ditypes.TypeMap, error) {
entryReader := dwarfData.Reader()
typeReader := dwarfData.Reader()
readingAFunction := false
var funcName string

var result = ditypes.TypeMap{
Functions: make(map[string][]ditypes.Parameter),
InlinedFunctions: make(map[uint64][]*dwarf.Entry),
Functions: make(map[string][]ditypes.Parameter),
}

var (
Expand Down Expand Up @@ -82,25 +81,6 @@ entryLoop:
}
}

if resolveInlinedProgramCounters && entry.Tag == dwarf.TagInlinedSubroutine {
// This is a inlined function
for i := range entry.Field {
// Find it's high program counter (where it exits in the parent routine)
if entry.Field[i].Attr == dwarf.AttrHighpc {

// The field for HighPC can be a constant or address, which are int64 and uint64 respectively
if entry.Field[i].Class == dwarf.ClassConstant {
result.InlinedFunctions[uint64(entry.Field[i].Val.(int64))] =
append([]*dwarf.Entry{entry}, result.InlinedFunctions[uint64(entry.Field[i].Val.(int64))]...)
} else if entry.Field[i].Class == dwarf.ClassAddress {
result.InlinedFunctions[entry.Field[i].Val.(uint64)] =
append([]*dwarf.Entry{entry}, result.InlinedFunctions[entry.Field[i].Val.(uint64)]...)
}
}
}
continue entryLoop
}

if entry.Tag == dwarf.TagSubprogram {

for _, field := range entry.Field {
Expand Down
5 changes: 2 additions & 3 deletions pkg/dynamicinstrumentation/diconfig/file_config_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@ package diconfig
import (
"fmt"

"github.com/DataDog/datadog-agent/pkg/dynamicinstrumentation/ditypes"
"github.com/DataDog/datadog-agent/pkg/dynamicinstrumentation/util"
"github.com/DataDog/datadog-agent/pkg/util/log"
)

func NewFileConfigManager(opts *ditypes.RuntimeOptions, configFile string) (*ReaderConfigManager, func(), error) { //nolint:revive // TODO
func NewFileConfigManager(configFile string) (*ReaderConfigManager, func(), error) { //nolint:revive // TODO
stopChan := make(chan bool)
stop := func() {
stopChan <- true
}

cm, err := NewReaderConfigManager(opts)
cm, err := NewReaderConfigManager()
if err != nil {
return nil, stop, err
}
Expand Down
20 changes: 9 additions & 11 deletions pkg/dynamicinstrumentation/diconfig/mem_config_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,18 @@ type ReaderConfigManager struct {
ConfigWriter *ConfigWriter
procTracker *proctracker.ProcessTracker

callback configUpdateCallback
configs configsByService
state ditypes.DIProcs
runtimeOptions *ditypes.RuntimeOptions
callback configUpdateCallback
configs configsByService
state ditypes.DIProcs
}

type readerConfigCallback func(configsByService) //nolint:unused // TODO
type configsByService = map[ditypes.ServiceName]map[ditypes.ProbeID]rcConfig

func NewReaderConfigManager(opts *ditypes.RuntimeOptions) (*ReaderConfigManager, error) { //nolint:revive // TODO
func NewReaderConfigManager() (*ReaderConfigManager, error) { //nolint:revive // TODO
cm := &ReaderConfigManager{
callback: applyConfigUpdate,
state: ditypes.NewDIProcs(),
runtimeOptions: opts,
callback: applyConfigUpdate,
state: ditypes.NewDIProcs(),
}

cm.procTracker = proctracker.NewProcessTracker(cm.updateProcessInfo)
Expand Down Expand Up @@ -80,7 +78,7 @@ func (cm *ReaderConfigManager) update() error {
}

if !reflect.DeepEqual(cm.state, updatedState) {
err := inspectGoBinaries(cm.runtimeOptions, updatedState)
err := inspectGoBinaries(updatedState)
if err != nil {
return err
}
Expand All @@ -97,15 +95,15 @@ func (cm *ReaderConfigManager) update() error {
if _, tracked := cm.state[pid]; !tracked {
for _, probe := range procInfo.GetProbes() {
// install all probes from new process
cm.callback(cm.runtimeOptions, procInfo, probe)
cm.callback(procInfo, probe)
}
} else {
currentStateProbes := cm.state[pid].GetProbes()
for _, existingProbe := range currentStateProbes {
cm.state[pid].DeleteProbe(existingProbe.ID)
}
for _, updatedProbe := range procInfo.GetProbes() {
cm.callback(cm.runtimeOptions, procInfo, updatedProbe)
cm.callback(procInfo, updatedProbe)
}
}
}
Expand Down
7 changes: 1 addition & 6 deletions pkg/dynamicinstrumentation/ditypes/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,11 @@ import (
"fmt"
)

// TypeMap contains all the information about functions and their parameters including
// functions that have been inlined in the binary
// TypeMap contains all the information about functions and their parameters
type TypeMap struct {
// Functions maps fully-qualified function names to a slice of its parameters
Functions map[string][]Parameter

// InlinedFunctions maps program counters to a slice of dwarf entries used
// when resolving stack traces that include inlined functions
InlinedFunctions map[uint64][]*dwarf.Entry

// FunctionsByPC places DWARF subprogram (function) entries in order by
// its low program counter which is necessary for resolving stack traces
FunctionsByPC []*LowPCEntry
Expand Down
6 changes: 0 additions & 6 deletions pkg/dynamicinstrumentation/ditypes/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,6 @@ var (
SliceMaxLength = 100 // SliceMaxLength is the default limit in number of elements of a slice
)

// RuntimeOptions contains configuration options for how the dynamic instrumentation product
// should behave at runtime
type RuntimeOptions struct {
ResolveInlinedProgramCounters bool
}

// ProbeID is the unique identifier for probes
type ProbeID = string

Expand Down
1 change: 0 additions & 1 deletion pkg/dynamicinstrumentation/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ func NewModule(config *Config) (*Module, error) { //nolint:revive // TODO
SnapshotOutput: coreconfig.SystemProbe().GetString("dynamic_instrumentation.snapshot_output_file_path"),
DiagnosticOutput: coreconfig.SystemProbe().GetString("dynamic_instrumentation.diagnostics_output_file_path"),
},
ResolveInlinedProgramCounters: coreconfig.SystemProbe().GetBool("dynamic_instrumentation.resolve_inlined_program_counters"),
})
if err != nil {
return nil, err
Expand Down
44 changes: 0 additions & 44 deletions pkg/dynamicinstrumentation/uploader/stack_trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,6 @@ func parseStackTrace(procInfo *ditypes.ProcessInfo, rawProgramCounters []uint64)
break
}

entries, ok := procInfo.TypeMap.InlinedFunctions[rawProgramCounters[i]]
if ok {
for n := range entries {
inlinedFuncInfo, err := pcToLine(procInfo, rawProgramCounters[i])
if err != nil {
return stackTrace, fmt.Errorf("could not resolve pc to inlined function info: %w", err)
}

symName, lineNumber, err := parseInlinedEntry(procInfo.DwarfData.Reader(), entries[n])
if err != nil {
return stackTrace, fmt.Errorf("could not get inlined entries: %w", err)
}
stackFrame := ditypes.StackFrame{Function: fmt.Sprintf("%s [inlined in %s]", symName, inlinedFuncInfo.fn), FileName: inlinedFuncInfo.file, Line: int(lineNumber)}
stackTrace = append(stackTrace, stackFrame)
}
}

funcInfo, err := pcToLine(procInfo, rawProgramCounters[i])
if err != nil {
return stackTrace, fmt.Errorf("could not resolve pc to function info: %w", err)
Expand Down Expand Up @@ -122,30 +105,3 @@ func pcToLine(procInfo *ditypes.ProcessInfo, pc uint64) (*funcInfo, error) {
fn: fn,
}, nil
}

func parseInlinedEntry(reader *dwarf.Reader, e *dwarf.Entry) (name string, line int64, err error) {

var offset dwarf.Offset

for i := range e.Field {
if e.Field[i].Attr == dwarf.AttrAbstractOrigin {
offset = e.Field[i].Val.(dwarf.Offset)
reader.Seek(offset)
entry, err := reader.Next()
if err != nil {
return "", -1, fmt.Errorf("could not read inlined function origin: %w", err)
}
for j := range entry.Field {
if entry.Field[j].Attr == dwarf.AttrName {
name = entry.Field[j].Val.(string)
}
}
}

if e.Field[i].Attr == dwarf.AttrCallLine {
line = e.Field[i].Val.(int64)
}
}

return name, line, nil
}

0 comments on commit 580cbf7

Please sign in to comment.