When set to 0 memory profiling is disabled. Refer to the description of
MemProfileRate for the default value.
+ profstackdepth: profstackdepth=128 (the default) will set the maximum stack
+ depth used by all pprof profilers except for the CPU profiler to 128 frames.
+ Stack traces that exceed this limit will be truncated to the limit starting
+ from the leaf frame. Setting profstackdepth to any value above 1024 will
+ silently default to 1024. Future versions of Go may remove this limitation
+ and extend profstackdepth to apply to the CPU profiler and execution tracer.
+
pagetrace: setting pagetrace=/path/to/file will write out a trace of page events
that can be viewed, analyzed, and visualized using the x/debug/cmd/pagetrace tool.
Build your program with GOEXPERIMENT=pagetrace to enable this functionality. Do not
// size of bucket hash table
buckHashSize = 179999
- // maxStack is the max depth of stack to record in bucket.
- // Note that it's only used internally as a guard against
- // wildly out-of-bounds slicing of the PCs that come after
- // a bucket struct, and it could increase in the future.
- // The term "1" accounts for the first stack entry being
- // taken up by a "skip" sentinel value for profilers which
- // defer inline frame expansion until the profile is reported.
- // The term "maxSkip" is for frame pointer unwinding, where we
- // want to end up with maxLogicalStack frames but will discard
- // some "physical" frames to account for skipping.
- maxStack = 1 + maxSkip + maxLogicalStack
-
- // maxLogicalStack is the maximum stack size of a call stack
- // to encode in a profile. This counts "logical" frames, which
- // includes inlined frames. We may record more than this many
- // "physical" frames when using frame pointer unwinding to account
- // for deferred handling of skipping frames & inline expansion.
- maxLogicalStack = 128
// maxSkip is to account for deferred inline expansion
// when using frame pointer unwinding. We record the stack
// with "physical" frame pointers but handle skipping "logical"
// This should be at least as large as the largest skip value
// used for profiling; otherwise stacks may be truncated inconsistently
maxSkip = 5
+
+ // maxProfStackDepth is the highest valid value for debug.profstackdepth.
+ // It's used for the bucket.stk func.
+ // TODO(fg): can we get rid of this?
+ maxProfStackDepth = 1024
)
type bucketType int
return b
}
-// stk returns the slice in b holding the stack.
+// stk returns the slice in b holding the stack. The caller can asssume that the
+// backing array is immutable.
func (b *bucket) stk() []uintptr {
- stk := (*[maxStack]uintptr)(add(unsafe.Pointer(b), unsafe.Sizeof(*b)))
- if b.nstk > maxStack {
+ stk := (*[maxProfStackDepth]uintptr)(add(unsafe.Pointer(b), unsafe.Sizeof(*b)))
+ if b.nstk > maxProfStackDepth {
// prove that slicing works; otherwise a failure requires a P
throw("bad profile stack count")
}
}
// Only use the part of mp.profStack we need and ignore the extra space
// reserved for delayed inline expansion with frame pointer unwinding.
- nstk := callers(4, mp.profStack[:maxLogicalStack])
+ nstk := callers(4, mp.profStack[:debug.profstackdepth])
index := (mProfCycle.read() + 2) % uint32(len(memRecord{}.future))
b := stkbucket(memProfile, size, mp.profStack[:nstk], true)
// skip should be positive if this event is recorded from the current stack
// (e.g. when this is not called from a system stack)
func saveblockevent(cycles, rate int64, skip int, which bucketType) {
+ if debug.profstackdepth == 0 {
+ // profstackdepth is set to 0 by the user, so mp.profStack is nil and we
+ // can't record a stack trace.
+ return
+ }
if skip > maxSkip {
print("requested skip=", skip)
throw("invalid skip value")
}
gp := getg()
mp := acquirem() // we must not be preempted while accessing profstack
+
nstk := 1
if tracefpunwindoff() || gp.m.hasCgoOnStack() {
mp.profStack[0] = logicalStackSentinel
}
func (prof *mLockProfile) captureStack() {
+ if debug.profstackdepth == 0 {
+ // profstackdepth is set to 0 by the user, so mp.profStack is nil and we
+ // can't record a stack trace.
+ return
+ }
+
skip := 3 // runtime.(*mLockProfile).recordUnlock runtime.unlock2 runtime.unlockWithRank
if staticLockRanking {
// When static lock ranking is enabled, we'll always be on the system
mp := acquirem()
prof.disabled = true
- nstk := maxStack
+ nstk := int(debug.profstackdepth)
for i := 0; i < nstk; i++ {
if pc := prof.stack[i]; pc == 0 {
nstk = i
MemProfileRate = 0
}
+ // mcommoninit runs before parsedebugvars, so init profstacks again.
+ mProfStackInit(gp.m)
+
lock(&sched.lock)
sched.lastpoll.Store(nanotime())
procs := ncpu
// malloc and runtime locks for mLockProfile.
// TODO(mknyszek): Implement lazy allocation if this becomes a problem.
func mProfStackInit(mp *m) {
+ if debug.profstackdepth == 0 {
+ // debug.profstack is set to 0 by the user, or we're being called from
+ // schedinit before parsedebugvars.
+ return
+ }
mp.profStack = makeProfStackFP()
mp.mLockProfile.stack = makeProfStackFP()
}
// The "maxSkip" term is for frame pointer unwinding, where we
// want to end up with debug.profstackdebth frames but will discard
// some "physical" frames to account for skipping.
- return make([]uintptr, 1+maxSkip+maxLogicalStack)
+ return make([]uintptr, 1+maxSkip+debug.profstackdepth)
}
// makeProfStack returns a buffer large enough to hold a maximum-sized stack
// trace.
-func makeProfStack() []uintptr { return make([]uintptr, maxLogicalStack) }
+func makeProfStack() []uintptr { return make([]uintptr, debug.profstackdepth) }
//go:linkname pprof_makeProfStack
func pprof_makeProfStack() []uintptr { return makeProfStack() }
tracefpunwindoff int32
traceadvanceperiod int32
traceCheckStackOwnership int32
+ profstackdepth int32
// debug.malloc is used as a combined debug check
// in the malloc function and should be set
{name: "invalidptr", value: &debug.invalidptr},
{name: "madvdontneed", value: &debug.madvdontneed},
{name: "panicnil", atomic: &debug.panicnil},
+ {name: "profstackdepth", value: &debug.profstackdepth, def: 128},
{name: "runtimecontentionstacks", atomic: &debug.runtimeContentionStacks},
{name: "sbrk", value: &debug.sbrk},
{name: "scavtrace", value: &debug.scavtrace},
parsegodebug(godebug, nil)
debug.malloc = (debug.inittrace | debug.sbrk) != 0
+ debug.profstackdepth = min(debug.profstackdepth, maxProfStackDepth)
setTraceback(gogetenv("GOTRACEBACK"))
traceback_env = traceback_cache