Skip to content

Commit 3e360b0

Browse files
committed
runtime: new API for filling PC traceback buffers
Currently, filling PC traceback buffers is one of the jobs of gentraceback. This moves it into a new function, tracebackPCs, with a simple API built around unwinder, and changes all callers to use this new API. Updates #54466. Change-Id: Id2038bded81bf533a5a4e71178a7c014904d938c Reviewed-on: https://go-review.googlesource.com/c/go/+/468300 Reviewed-by: Michael Pratt <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Austin Clements <[email protected]>
1 parent 9872428 commit 3e360b0

File tree

4 files changed

+66
-49
lines changed

4 files changed

+66
-49
lines changed

src/runtime/export_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,9 @@ func LockOSCounts() (external, internal uint32) {
483483
func TracebackSystemstack(stk []uintptr, i int) int {
484484
if i == 0 {
485485
pc, sp := getcallerpc(), getcallersp()
486-
return gentraceback(pc, sp, 0, getg(), 0, &stk[0], len(stk), nil, nil, _TraceJumpStack)
486+
var u unwinder
487+
u.initAt(pc, sp, 0, getg(), unwindJumpStack) // Don't ignore errors, for testing
488+
return tracebackPCs(&u, 0, stk)
487489
}
488490
n := 0
489491
systemstack(func() {

src/runtime/mprof.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1173,7 +1173,9 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
11731173
}
11741174

11751175
func saveg(pc, sp uintptr, gp *g, r *StackRecord) {
1176-
n := gentraceback(pc, sp, 0, gp, 0, &r.Stack0[0], len(r.Stack0), nil, nil, 0)
1176+
var u unwinder
1177+
u.initAt(pc, sp, 0, gp, unwindSilentErrors)
1178+
n := tracebackPCs(&u, 0, r.Stack0[:])
11771179
if n < len(r.Stack0) {
11781180
r.Stack0[n] = 0
11791181
}

src/runtime/proc.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4687,6 +4687,7 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
46874687
// See golang.org/issue/17165.
46884688
getg().m.mallocing++
46894689

4690+
var u unwinder
46904691
var stk [maxCPUProfStack]uintptr
46914692
n := 0
46924693
if mp.ncgo > 0 && mp.curg != nil && mp.curg.syscallpc != 0 && mp.curg.syscallsp != 0 {
@@ -4700,26 +4701,24 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
47004701
for cgoOff < len(mp.cgoCallers) && mp.cgoCallers[cgoOff] != 0 {
47014702
cgoOff++
47024703
}
4703-
copy(stk[:], mp.cgoCallers[:cgoOff])
4704+
n += copy(stk[:], mp.cgoCallers[:cgoOff])
47044705
mp.cgoCallers[0] = 0
47054706
}
47064707

47074708
// Collect Go stack that leads to the cgo call.
4708-
n = gentraceback(mp.curg.syscallpc, mp.curg.syscallsp, 0, mp.curg, 0, &stk[cgoOff], len(stk)-cgoOff, nil, nil, 0)
4709-
if n > 0 {
4710-
n += cgoOff
4711-
}
4709+
u.initAt(mp.curg.syscallpc, mp.curg.syscallsp, 0, mp.curg, unwindSilentErrors)
47124710
} else if usesLibcall() && mp.libcallg != 0 && mp.libcallpc != 0 && mp.libcallsp != 0 {
47134711
// Libcall, i.e. runtime syscall on windows.
47144712
// Collect Go stack that leads to the call.
4715-
n = gentraceback(mp.libcallpc, mp.libcallsp, 0, mp.libcallg.ptr(), 0, &stk[n], len(stk[n:]), nil, nil, 0)
4713+
u.initAt(mp.libcallpc, mp.libcallsp, 0, mp.libcallg.ptr(), unwindSilentErrors)
47164714
} else if mp != nil && mp.vdsoSP != 0 {
47174715
// VDSO call, e.g. nanotime1 on Linux.
47184716
// Collect Go stack that leads to the call.
4719-
n = gentraceback(mp.vdsoPC, mp.vdsoSP, 0, gp, 0, &stk[n], len(stk[n:]), nil, nil, _TraceJumpStack)
4717+
u.initAt(mp.vdsoPC, mp.vdsoSP, 0, gp, unwindSilentErrors|unwindJumpStack)
47204718
} else {
4721-
n = gentraceback(pc, sp, lr, gp, 0, &stk[0], len(stk), nil, nil, _TraceTrap|_TraceJumpStack)
4719+
u.initAt(pc, sp, lr, gp, unwindSilentErrors|unwindTrap|unwindJumpStack)
47224720
}
4721+
n += tracebackPCs(&u, 0, stk[n:])
47234722

47244723
if n <= 0 {
47254724
// Normal traceback is impossible or has failed.

src/runtime/traceback.go

Lines changed: 53 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -591,27 +591,64 @@ func (u *unwinder) cgoCallers(pcBuf []uintptr) int {
591591
return len(pcBuf)
592592
}
593593

594-
// Generic traceback. Handles runtime stack prints (pcbuf == nil),
595-
// and the runtime.Callers function (pcbuf != nil).
596-
// A little clunky to merge these, but avoids
597-
// duplicating the code and all its subtlety.
594+
// tracebackPCs populates pcBuf with the return addresses for each frame from u
595+
// and returns the number of PCs written to pcBuf. The returned PCs correspond
596+
// to "logical frames" rather than "physical frames"; that is if A is inlined
597+
// into B, this will still return a PCs for both A and B. This also includes PCs
598+
// generated by the cgo unwinder, if one is registered.
599+
//
600+
// If skip != 0, this skips this many logical frames.
601+
//
602+
// Callers should set the unwindSilentErrors flag on u.
603+
func tracebackPCs(u *unwinder, skip int, pcBuf []uintptr) int {
604+
var cgoBuf [32]uintptr
605+
n := 0
606+
for ; n < len(pcBuf) && u.valid(); u.next() {
607+
f := u.frame.fn
608+
cgoN := u.cgoCallers(cgoBuf[:])
609+
610+
// TODO: Why does &u.cache cause u to escape?
611+
for iu, uf := newInlineUnwinder(f, u.symPC(), noEscapePtr(&u.cache)); n < len(pcBuf) && uf.valid(); uf = iu.next(uf) {
612+
sf := iu.srcFunc(uf)
613+
if sf.funcID == funcID_wrapper && elideWrapperCalling(u.calleeFuncID) {
614+
// ignore wrappers
615+
} else if skip > 0 {
616+
skip--
617+
} else {
618+
// Callers expect the pc buffer to contain return addresses
619+
// and do the -1 themselves, so we add 1 to the call PC to
620+
// create a return PC.
621+
pcBuf[n] = uf.pc + 1
622+
n++
623+
}
624+
u.calleeFuncID = sf.funcID
625+
}
626+
// Add cgo frames (if we're done skipping over the requested number of
627+
// Go frames).
628+
if skip == 0 {
629+
n += copy(pcBuf[n:], cgoBuf[:cgoN])
630+
}
631+
}
632+
return n
633+
}
634+
635+
// Generic traceback. Handles runtime stack prints (pcbuf == nil).
598636
//
599637
// The skip argument is only valid with pcbuf != nil and counts the number
600638
// of logical frames to skip rather than physical frames (with inlining, a
601639
// PC in pcbuf can represent multiple calls).
602640
func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max int, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer, flags uint) int {
641+
if pcbuf != nil {
642+
throw("pcbuf argument no longer supported")
643+
}
603644
if callback != nil {
604645
throw("callback argument no longer supported")
605646
}
606647

607648
// Translate flags
608649
var uflags unwindFlags
609-
printing := pcbuf == nil && callback == nil
610-
if printing {
611-
uflags |= unwindPrintErrors
612-
} else {
613-
uflags |= unwindSilentErrors
614-
}
650+
printing := true
651+
uflags |= unwindPrintErrors
615652
if flags&_TraceTrap != 0 {
616653
uflags |= unwindTrap
617654
}
@@ -634,33 +671,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
634671

635672
cgoN := u.cgoCallers(cgoBuf[:])
636673

637-
if pcbuf != nil {
638-
// TODO: Why does cache escape? (Same below)
639-
for iu, uf := newInlineUnwinder(f, u.symPC(), noEscapePtr(&u.cache)); uf.valid(); uf = iu.next(uf) {
640-
sf := iu.srcFunc(uf)
641-
if sf.funcID == funcID_wrapper && elideWrapperCalling(u.calleeFuncID) {
642-
// ignore wrappers
643-
} else if skip > 0 {
644-
skip--
645-
} else if n < max {
646-
// Callers expect the pc buffer to contain return addresses
647-
// and do the -1 themselves, so we add 1 to the call PC to
648-
// create a return PC.
649-
(*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = uf.pc + 1
650-
n++
651-
}
652-
u.calleeFuncID = sf.funcID
653-
}
654-
// Add cgo frames
655-
if skip == 0 { // skip only applies to Go frames
656-
for i := 0; i < cgoN && n < max; i++ {
657-
(*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = cgoBuf[i]
658-
n++
659-
}
660-
}
661-
n-- // offset n++ below
662-
}
663-
664674
if printing {
665675
// assume skip=0 for printing.
666676
//
@@ -981,13 +991,17 @@ func callers(skip int, pcbuf []uintptr) int {
981991
gp := getg()
982992
var n int
983993
systemstack(func() {
984-
n = gentraceback(pc, sp, 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
994+
var u unwinder
995+
u.initAt(pc, sp, 0, gp, unwindSilentErrors)
996+
n = tracebackPCs(&u, skip, pcbuf)
985997
})
986998
return n
987999
}
9881000

9891001
func gcallers(gp *g, skip int, pcbuf []uintptr) int {
990-
return gentraceback(^uintptr(0), ^uintptr(0), 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
1002+
var u unwinder
1003+
u.init(gp, unwindSilentErrors)
1004+
return tracebackPCs(&u, skip, pcbuf)
9911005
}
9921006

9931007
// showframe reports whether the frame with the given characteristics should

0 commit comments

Comments
 (0)