Skip to content

Commit

Permalink
profiler: remove old delta implementation (#1860)
Browse files Browse the repository at this point in the history
  • Loading branch information
nsrip-dd committed Jun 6, 2023
1 parent 4490ba0 commit 8220ea1
Show file tree
Hide file tree
Showing 6 changed files with 4 additions and 348 deletions.
61 changes: 0 additions & 61 deletions profiler/internal/pprofutils/delta.go

This file was deleted.

87 changes: 0 additions & 87 deletions profiler/internal/pprofutils/delta_test.go

This file was deleted.

4 changes: 0 additions & 4 deletions profiler/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ type config struct {
blockRate int
outputDir string
deltaProfiles bool
deltaMethod string
logStartup bool
traceConfig executionTraceConfig
endpointCountEnabled bool
Expand All @@ -123,7 +122,6 @@ func logStartup(c *config) {
LangVersion string `json:"lang_version"` // Go version, e.g. go1.18
Hostname string `json:"hostname"`
DeltaProfiles bool `json:"delta_profiles"`
DeltaMethod string `json:"delta_method"`
Service string `json:"service"`
Env string `json:"env"`
TargetURL string `json:"target_url"`
Expand All @@ -150,7 +148,6 @@ func logStartup(c *config) {
LangVersion: runtime.Version(),
Hostname: c.hostname,
DeltaProfiles: c.deltaProfiles,
DeltaMethod: c.deltaMethod,
Service: c.service,
Env: c.env,
TargetURL: c.targetURL,
Expand Down Expand Up @@ -218,7 +215,6 @@ func defaultConfig() (*config, error) {
uploadTimeout: DefaultUploadTimeout,
maxGoroutinesWait: 1000, // arbitrary value, should limit STW to ~30ms
deltaProfiles: internal.BoolEnv("DD_PROFILING_DELTA", true),
deltaMethod: os.Getenv("DD_PROFILING_DELTA_METHOD"),
logStartup: internal.BoolEnv("DD_TRACE_STARTUP_LOGS", true),
endpointCountEnabled: internal.BoolEnv(traceprof.EndpointCountEnvVar, false),
}
Expand Down
182 changes: 1 addition & 181 deletions profiler/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import (
"runtime/trace"
"time"

"gopkg.in/DataDog/dd-trace-go.v1/internal/log"
"gopkg.in/DataDog/dd-trace-go.v1/profiler/internal"
"gopkg.in/DataDog/dd-trace-go.v1/profiler/internal/fastdelta"
"gopkg.in/DataDog/dd-trace-go.v1/profiler/internal/pprofutils"

Expand Down Expand Up @@ -341,87 +339,14 @@ func (p *profiler) runProfile(pt ProfileType) ([]*profile, error) {
return []*profile{{name: filename, pt: pt, data: data}}, nil
}

type deltaProfiler interface {
Delta(curData []byte) ([]byte, error)
}

type pprofileDeltaProfiler struct {
delta pprofutils.Delta
prev *pprofile.Profile
}

// newDeltaProfiler returns an initialized delta profiler based on cfg.deltaMethod
//
// - fastdelta: uses internal/fastdelta
// - comparing: executes both pprofile and fastdelta, comparing the two with statsd metrics
// - any other value: pprofile delta
//
// If value types are given (e.g. "alloc_space", "alloc_objects"),
// only those values will have deltas computed.
// Otherwise, deltas will be computed for every value.
func newDeltaProfiler(cfg *config, v ...pprofutils.ValueType) deltaProfiler {
switch cfg.deltaMethod {
// TODO: slowdelta and comparing implementation can be removed after 2023-04-11.
case "slowdelta":
return &pprofileDeltaProfiler{delta: pprofutils.Delta{SampleTypes: v}}
case "comparing":
return newComparingDeltaProfiler(
cfg,
&pprofileDeltaProfiler{delta: pprofutils.Delta{SampleTypes: v}},
newFastDeltaProfiler(v...))
default:
return newFastDeltaProfiler(v...)
}
}

// Delta derives the delta profile between curData and the profile passed to the
// previous call to Delta. The first call to Delta will return the profile
// unchanged.
func (d *pprofileDeltaProfiler) Delta(curData []byte) ([]byte, error) {
curProf, err := pprofile.ParseData(curData)
if err != nil {
return nil, fmt.Errorf("delta prof parse: %v", err)
}
var deltaData []byte
prevProf := d.prev
if prevProf == nil {
// First time deltaProfile gets called for a type, there is no prevProf. In
// this case we emit the current profile as a delta profile.
deltaData = curData
} else {
// Delta profiling is also implemented in the Go core, see commit below.
// Unfortunately the core implementation isn't resuable via a API, so we do
// our own delta calculation below.
// https://github.com/golang/go/commit/2ff1e3ebf5de77325c0e96a6c2a229656fc7be50#diff-94594f8f13448da956b02997e50ca5a156b65085993e23bbfdda222da6508258R303-R304
deltaProf, err := d.delta.Convert(prevProf, curProf)
if err != nil {
return nil, fmt.Errorf("delta prof merge: %v", err)
}
// TimeNanos is supposed to be the time the profile was collected, see
// https://github.com/google/pprof/blob/master/proto/profile.proto.
deltaProf.TimeNanos = curProf.TimeNanos
// DurationNanos is the time period covered by the profile.
deltaProf.DurationNanos = curProf.TimeNanos - prevProf.TimeNanos
deltaBuf := &bytes.Buffer{}
if err := deltaProf.Write(deltaBuf); err != nil {
return nil, fmt.Errorf("delta prof write: %v", err)
}
deltaData = deltaBuf.Bytes()
}
// Keep the most recent profiles in memory for future diffing. This needs to
// be taken into account when enforcing memory limits going forward.
d.prev = curProf
return deltaData, nil
}

type fastDeltaProfiler struct {
dc *fastdelta.DeltaComputer
buf bytes.Buffer
gzr gzip.Reader
gzw *gzip.Writer
}

func newFastDeltaProfiler(v ...pprofutils.ValueType) deltaProfiler {
func newFastDeltaProfiler(v ...pprofutils.ValueType) *fastDeltaProfiler {
fd := &fastDeltaProfiler{
dc: fastdelta.NewDeltaComputer(v...),
}
Expand Down Expand Up @@ -461,111 +386,6 @@ func (fdp *fastDeltaProfiler) Delta(data []byte) (b []byte, err error) {
return b, nil
}

type comparingDeltaProfiler struct {
golden deltaProfiler
sut deltaProfiler
tags []string
statsd StatsdClient
}

func newComparingDeltaProfiler(cfg *config, golden, systemUnderTest deltaProfiler) deltaProfiler {
return &comparingDeltaProfiler{
golden: golden,
sut: systemUnderTest,
tags: cfg.tags.Slice(),
statsd: cfg.statsd,
}
}

func (cdp *comparingDeltaProfiler) Delta(data []byte) (res []byte, err error) {
sw := internal.NewStopwatch()
res, err = cdp.golden.Delta(data)
goldenDuration := sw.Tick()

if err != nil {
cdp.reportError(err.Error())
return nil, err
}

resSut, err := cdp.sut.Delta(data)
sutDuration := sw.Tick()

cdp.reportTiming("golden", goldenDuration)
cdp.reportTiming("sut", sutDuration)

if err != nil {
// sut errored, but return the golden result
cdp.reportError(err.Error())
return res, nil
}

pprofGolden, err := pprofile.ParseData(res)
if err != nil {
cdp.reportError(err.Error())
return res, nil
}

pprofSut, err := pprofile.ParseData(resSut)
if err != nil {
cdp.reportError(err.Error())
return res, nil
}

if pprofGolden.DurationNanos != pprofSut.DurationNanos {
log.Error("profiles differ: golden_dur=%d sut_dur=%d", pprofGolden.DurationNanos, pprofSut.DurationNanos)
cdp.reportError("compare_failed")
return res, nil
}

pprofDiff, err := PprofDiff(pprofSut, pprofGolden)
if err != nil {
cdp.reportError(err.Error())
return res, nil
}

cdp.reportTiming("overhead", sw.Tick())

if len(pprofDiff.Sample) > 0 {
var extraTags []string
for _, vt := range pprofSut.SampleType {
extraTags = append(extraTags, "sample_type:"+vt.Type)
}
pprofSut.Scale(-1) // so it prints correctly, PprofDiff mutated it
log.Error("profiles differ: golden: %v\n\nsut: %v\n\ndiff:%v", pprofGolden, pprofSut, pprofDiff)
cdp.reportError("compare_failed", extraTags...)
}

return res, nil
}

// interface hack to get around limitation of public profiler.StatsdClient
// The public interface only offers a Timing method _which_should_not_be_used_
// because histogram aggregation math is fundamentally broken. It's unfortunate
// that the public interface exposed Timing instead of Distribution given
// Distribution was available at the time.
type statsdDistribution interface {
// Distribution creates a distribution metric, prefer over Timing
Distribution(event string, value float64, tags []string, rate float64) error
}

func (cdp *comparingDeltaProfiler) reportTiming(section string, dur time.Duration) {
statsdClient, ok := cdp.statsd.(statsdDistribution)
if !ok {
return
}
_ = statsdClient.Distribution(
"datadog.profiling.go.delta_compare.dist",
float64(dur.Milliseconds()),
append(cdp.tags, "section:"+section),
1)
}

func (cdp *comparingDeltaProfiler) reportError(error string, extraTags ...string) {
tags := append(cdp.tags, "msg:"+error)
tags = append(tags, extraTags...)
_ = cdp.statsd.Count("datadog.profiling.go.delta_compare.error", 1, tags, 1)
}

// PprofDiff computes the delta between all values b-a and returns them as a new
// profile. Samples that end up with a delta of 0 are dropped. WARNING: Profile
// a will be mutated by this function. You should pass a copy if that's
Expand Down

0 comments on commit 8220ea1

Please sign in to comment.