Skip to content

Commit 2141315

Browse files
felixgeprattmic
authored andcommitted
runtime: move profiling pc buffers to m
Move profiling pc buffers from being stack allocated to an m field. This is motivated by the next patch, which will increase the default stack depth to 128, which might lead to undesirable stack growth for goroutines that produce profiling events. Additionally, this change paves the way to make the stack depth configurable via GODEBUG. Change-Id: Ifa407f899188e2c7c0a81de92194fdb627cb4b36 Reviewed-on: https://go-review.googlesource.com/c/go/+/574699 Reviewed-by: Michael Knyszek <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Michael Pratt <[email protected]>
1 parent c3bd543 commit 2141315

File tree

4 files changed

+30
-20
lines changed

4 files changed

+30
-20
lines changed

src/runtime/malloc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1404,7 +1404,7 @@ func profilealloc(mp *m, x unsafe.Pointer, size uintptr) {
14041404
throw("profilealloc called without a P or outside bootstrapping")
14051405
}
14061406
c.nextSample = nextSample()
1407-
mProf_Malloc(x, size)
1407+
mProf_Malloc(mp, x, size)
14081408
}
14091409

14101410
// nextSample returns the next sampling point for heap profiling. The goal is

src/runtime/mprof.go

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -422,15 +422,13 @@ func mProf_PostSweep() {
422422
}
423423

424424
// Called by malloc to record a profiled block.
425-
func mProf_Malloc(p unsafe.Pointer, size uintptr) {
426-
var stk [maxStack]uintptr
427-
nstk := callers(4, stk[:])
428-
425+
func mProf_Malloc(mp *m, p unsafe.Pointer, size uintptr) {
426+
nstk := callers(4, mp.profStack)
429427
index := (mProfCycle.read() + 2) % uint32(len(memRecord{}.future))
430428

431-
b := stkbucket(memProfile, size, stk[:nstk], true)
432-
mp := b.mp()
433-
mpc := &mp.future[index]
429+
b := stkbucket(memProfile, size, mp.profStack[:nstk], true)
430+
mr := b.mp()
431+
mpc := &mr.future[index]
434432

435433
lock(&profMemFutureLock[index])
436434
mpc.allocs++
@@ -505,16 +503,17 @@ func blocksampled(cycles, rate int64) bool {
505503
}
506504

507505
func saveblockevent(cycles, rate int64, skip int, which bucketType) {
508-
gp := getg()
509506
var nstk int
510-
var stk [maxStack]uintptr
507+
gp := getg()
508+
mp := acquirem() // we must not be preempted while accessing profstack
511509
if gp.m.curg == nil || gp.m.curg == gp {
512-
nstk = callers(skip, stk[:])
510+
nstk = callers(skip, mp.profStack)
513511
} else {
514-
nstk = gcallers(gp.m.curg, skip, stk[:])
512+
nstk = gcallers(gp.m.curg, skip, mp.profStack)
515513
}
516514

517-
saveBlockEventStack(cycles, rate, stk[:nstk], which)
515+
saveBlockEventStack(cycles, rate, mp.profStack[:nstk], which)
516+
releasem(mp)
518517
}
519518

520519
// lockTimer assists with profiling contention on runtime-internal locks.
@@ -613,12 +612,12 @@ func (lt *lockTimer) end() {
613612
}
614613

615614
type mLockProfile struct {
616-
waitTime atomic.Int64 // total nanoseconds spent waiting in runtime.lockWithRank
617-
stack [maxStack]uintptr // stack that experienced contention in runtime.lockWithRank
618-
pending uintptr // *mutex that experienced contention (to be traceback-ed)
619-
cycles int64 // cycles attributable to "pending" (if set), otherwise to "stack"
620-
cyclesLost int64 // contention for which we weren't able to record a call stack
621-
disabled bool // attribute all time to "lost"
615+
waitTime atomic.Int64 // total nanoseconds spent waiting in runtime.lockWithRank
616+
stack []uintptr // stack that experienced contention in runtime.lockWithRank
617+
pending uintptr // *mutex that experienced contention (to be traceback-ed)
618+
cycles int64 // cycles attributable to "pending" (if set), otherwise to "stack"
619+
cyclesLost int64 // contention for which we weren't able to record a call stack
620+
disabled bool // attribute all time to "lost"
622621
}
623622

624623
func (prof *mLockProfile) recordLock(cycles int64, l *mutex) {
@@ -703,7 +702,7 @@ func (prof *mLockProfile) captureStack() {
703702
systemstack(func() {
704703
var u unwinder
705704
u.initAt(pc, sp, 0, gp, unwindSilentErrors|unwindJumpStack)
706-
nstk = tracebackPCs(&u, skip, prof.stack[:])
705+
nstk = tracebackPCs(&u, skip, prof.stack)
707706
})
708707
if nstk < len(prof.stack) {
709708
prof.stack[nstk] = 0

src/runtime/proc.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,16 @@ func mcommoninit(mp *m, id int64) {
922922
if iscgo || GOOS == "solaris" || GOOS == "illumos" || GOOS == "windows" {
923923
mp.cgoCallers = new(cgoCallers)
924924
}
925+
mProfStackInit(mp)
926+
}
927+
928+
// mProfStackInit is used to eagilery initialize stack trace buffers for
929+
// profiling. Lazy allocation would have to deal with reentrancy issues in
930+
// malloc and runtime locks for mLockProfile.
931+
// TODO(mknyszek): Implement lazy allocation if this becomes a problem.
932+
func mProfStackInit(mp *m) {
933+
mp.profStack = make([]uintptr, maxStack)
934+
mp.mLockProfile.stack = make([]uintptr, maxStack)
925935
}
926936

927937
func (mp *m) becomeSpinning() {

src/runtime/runtime2.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,7 @@ type m struct {
599599
nextwaitm muintptr // next m waiting for lock
600600

601601
mLockProfile mLockProfile // fields relating to runtime.lock contention
602+
profStack []uintptr // used for memory/block/mutex stack traces
602603

603604
// wait* are used to carry arguments from gopark into park_m, because
604605
// there's no stack to put them on. That is their sole purpose.

0 commit comments

Comments
 (0)