Files
renovate-sh-app[bot]GitHubrenovate-sh-app[bot] <219655108+renovate-sh-app[bot]@users.noreply.github.com>A. Stoewer
92ce383d0e fix(deps): update github.com/grafana/dskit digest to 10fce42 (#5749)
* fix(deps): update github.com/grafana/dskit digest to 10fce42

Signed-off-by: renovate-sh-app[bot] <219655108+renovate-sh-app[bot]@users.noreply.github.com>

* make generate-manifest

---------

Signed-off-by: renovate-sh-app[bot] <219655108+renovate-sh-app[bot]@users.noreply.github.com>
Co-authored-by: renovate-sh-app[bot] <219655108+renovate-sh-app[bot]@users.noreply.github.com>
Co-authored-by: A. Stoewer <[email protected]>
2025-10-28 08:29:23 +10:00

89 lines
2.6 KiB
Go

package godeltaprof
import (
"io"
"runtime"
"sync"
"github.com/grafana/pyroscope-go/godeltaprof/internal/pprof"
)
// HeapProfiler is a stateful profiler for heap allocations in Go programs.
// It is based on runtime.MemProfile and provides similar functionality to
// pprof.WriteHeapProfile, but with some key differences.
//
// The HeapProfiler tracks the delta of heap allocations since the last
// profile was written, effectively providing a snapshot of the changes
// in heap usage between two points in time. This is in contrast to the
// pprof.WriteHeapProfile function, which accumulates profiling data
// and results in profiles that represent the entire lifetime of the program.
//
// The HeapProfiler is safe for concurrent use, as it serializes access to
// its internal state using a sync.Mutex. This ensures that multiple goroutines
// can call the Profile method without causing any data race issues.
//
// Usage:
//
// hp := godeltaprof.NewHeapProfiler()
// ...
// err := hp.Profile(someWriter)
type HeapProfiler struct {
impl pprof.DeltaHeapProfiler
mutex sync.Mutex
options pprof.ProfileBuilderOptions
gz gz
}
func NewHeapProfiler() *HeapProfiler {
return &HeapProfiler{
impl: pprof.DeltaHeapProfiler{},
options: pprof.ProfileBuilderOptions{
GenericsFrames: true,
LazyMapping: true,
}}
}
func NewHeapProfilerWithOptions(options ProfileOptions) *HeapProfiler {
return &HeapProfiler{
impl: pprof.DeltaHeapProfiler{},
options: pprof.ProfileBuilderOptions{
GenericsFrames: options.GenericsFrames,
LazyMapping: options.LazyMappings,
},
}
}
func (d *HeapProfiler) Profile(w io.Writer) error {
d.mutex.Lock()
defer d.mutex.Unlock()
// Find out how many records there are (MemProfile(nil, true)),
// allocate that many records, and get the data.
// There's a race—more records might be added between
// the two calls—so allocate a few extra records for safety
// and also try again if we're very unlucky.
// The loop should only execute one iteration in the common case.
var p []runtime.MemProfileRecord
var ok bool
n, _ := runtime.MemProfile(nil, true)
for {
// Allocate room for a slightly bigger profile,
// in case a few more entries have been added
// since the call to MemProfile.
p = make([]runtime.MemProfileRecord, n+50)
n, ok = runtime.MemProfile(p, true)
if ok {
p = p[0:n]
break
}
// Profile grew; try again.
}
rate := int64(runtime.MemProfileRate)
zw := d.gz.get(w)
b := pprof.NewProfileBuilder(w, zw, &d.options, pprof.HeapProfileConfig(rate))
return d.impl.WriteHeapProto(b, p, rate)
}