Skip to content

Commit 2d99109

Browse files
committed
runtime: replace all callback uses of gentraceback with unwinder
This is a really nice simplification for all of these call sites. It also achieves a nice performance improvement for stack copying: goos: linux goarch: amd64 pkg: runtime cpu: Intel(R) Xeon(R) CPU E5-2690 v3 @ 2.60GHz │ before │ after │ │ sec/op │ sec/op vs base │ StackCopyPtr-48 89.25m ± 1% 79.78m ± 1% -10.62% (p=0.000 n=20) StackCopy-48 83.48m ± 2% 71.88m ± 1% -13.90% (p=0.000 n=20) StackCopyNoCache-48 2.504m ± 2% 2.195m ± 1% -12.32% (p=0.000 n=20) StackCopyWithStkobj-48 21.66m ± 1% 21.02m ± 2% -2.95% (p=0.000 n=20) geomean 25.21m 22.68m -10.04% Updates #54466. Change-Id: I31715b7b6efd65726940041d3052bb1c0a1186f3 Reviewed-on: https://go-review.googlesource.com/c/go/+/468297 Run-TryBot: Austin Clements <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Michael Pratt <[email protected]>
1 parent da38476 commit 2d99109

File tree

6 files changed

+100
-110
lines changed

6 files changed

+100
-110
lines changed

src/runtime/heapdump.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,7 @@ func dumpbv(cbv *bitvector, offset uintptr) {
249249
}
250250
}
251251

252-
func dumpframe(s *stkframe, arg unsafe.Pointer) bool {
253-
child := (*childInfo)(arg)
252+
func dumpframe(s *stkframe, child *childInfo) {
254253
f := s.fn
255254

256255
// Figure out what we can about our stack map
@@ -333,7 +332,7 @@ func dumpframe(s *stkframe, arg unsafe.Pointer) bool {
333332
} else {
334333
child.args.n = -1
335334
}
336-
return true
335+
return
337336
}
338337

339338
func dumpgoroutine(gp *g) {
@@ -369,7 +368,10 @@ func dumpgoroutine(gp *g) {
369368
child.arglen = 0
370369
child.sp = nil
371370
child.depth = 0
372-
gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, dumpframe, noescape(unsafe.Pointer(&child)), 0)
371+
var u unwinder
372+
for u.initAt(pc, sp, lr, gp, 0); u.valid(); u.next() {
373+
dumpframe(&u.frame, &child)
374+
}
373375

374376
// dump defer & panic records
375377
for d := gp._defer; d != nil; d = d.link {

src/runtime/mbitmap.go

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,15 +1397,6 @@ func dumpGCProg(p *byte) {
13971397

13981398
// Testing.
13991399

1400-
func getgcmaskcb(frame *stkframe, ctxt unsafe.Pointer) bool {
1401-
target := (*stkframe)(ctxt)
1402-
if frame.sp <= target.sp && target.sp < frame.varp {
1403-
*target = *frame
1404-
return false
1405-
}
1406-
return true
1407-
}
1408-
14091400
// reflect_gcbits returns the GC type info for x, for testing.
14101401
// The result is the bitmap entries (0 or 1), one entry per byte.
14111402
//
@@ -1472,19 +1463,24 @@ func getgcmask(ep any) (mask []byte) {
14721463

14731464
// stack
14741465
if gp := getg(); gp.m.curg.stack.lo <= uintptr(p) && uintptr(p) < gp.m.curg.stack.hi {
1475-
var frame stkframe
1476-
frame.sp = uintptr(p)
1477-
gentraceback(gp.m.curg.sched.pc, gp.m.curg.sched.sp, 0, gp.m.curg, 0, nil, 1000, getgcmaskcb, noescape(unsafe.Pointer(&frame)), 0)
1478-
if frame.fn.valid() {
1479-
locals, _, _ := frame.getStackMap(nil, false)
1466+
found := false
1467+
var u unwinder
1468+
for u.initAt(gp.m.curg.sched.pc, gp.m.curg.sched.sp, 0, gp.m.curg, 0); u.valid(); u.next() {
1469+
if u.frame.sp <= uintptr(p) && uintptr(p) < u.frame.varp {
1470+
found = true
1471+
break
1472+
}
1473+
}
1474+
if found {
1475+
locals, _, _ := u.frame.getStackMap(nil, false)
14801476
if locals.n == 0 {
14811477
return
14821478
}
14831479
size := uintptr(locals.n) * goarch.PtrSize
14841480
n := (*ptrtype)(unsafe.Pointer(t)).elem.size
14851481
mask = make([]byte, n/goarch.PtrSize)
14861482
for i := uintptr(0); i < n; i += goarch.PtrSize {
1487-
off := (uintptr(p) + i - frame.varp + size) / goarch.PtrSize
1483+
off := (uintptr(p) + i - u.frame.varp + size) / goarch.PtrSize
14881484
mask[i/goarch.PtrSize] = locals.ptrbit(off)
14891485
}
14901486
}

src/runtime/mgcmark.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -797,11 +797,10 @@ func scanstack(gp *g, gcw *gcWork) int64 {
797797
}
798798

799799
// Scan the stack. Accumulate a list of stack objects.
800-
scanframe := func(frame *stkframe, unused unsafe.Pointer) bool {
801-
scanframeworker(frame, &state, gcw)
802-
return true
800+
var u unwinder
801+
for u.init(gp, 0); u.valid(); u.next() {
802+
scanframeworker(&u.frame, &state, gcw)
803803
}
804-
gentraceback(^uintptr(0), ^uintptr(0), 0, gp, 0, nil, 0x7fffffff, scanframe, nil, 0)
805804

806805
// Find additional pointers that point into the stack from the heap.
807806
// Currently this includes defers and panics. See also function copystack.

src/runtime/panic.go

Lines changed: 67 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -642,77 +642,78 @@ func addOneOpenDeferFrame(gp *g, pc uintptr, sp unsafe.Pointer) {
642642
sp = unsafe.Pointer(prevDefer.sp)
643643
}
644644
systemstack(func() {
645-
gentraceback(pc, uintptr(sp), 0, gp, 0, nil, 0x7fffffff,
646-
func(frame *stkframe, unused unsafe.Pointer) bool {
647-
if prevDefer != nil && prevDefer.sp == frame.sp {
648-
// Skip the frame for the previous defer that
649-
// we just finished (and was used to set
650-
// where we restarted the stack scan)
651-
return true
652-
}
653-
f := frame.fn
654-
fd := funcdata(f, _FUNCDATA_OpenCodedDeferInfo)
655-
if fd == nil {
656-
return true
645+
var u unwinder
646+
frames:
647+
for u.initAt(pc, uintptr(sp), 0, gp, 0); u.valid(); u.next() {
648+
frame := &u.frame
649+
if prevDefer != nil && prevDefer.sp == frame.sp {
650+
// Skip the frame for the previous defer that
651+
// we just finished (and was used to set
652+
// where we restarted the stack scan)
653+
continue
654+
}
655+
f := frame.fn
656+
fd := funcdata(f, _FUNCDATA_OpenCodedDeferInfo)
657+
if fd == nil {
658+
continue
659+
}
660+
// Insert the open defer record in the
661+
// chain, in order sorted by sp.
662+
d := gp._defer
663+
var prev *_defer
664+
for d != nil {
665+
dsp := d.sp
666+
if frame.sp < dsp {
667+
break
657668
}
658-
// Insert the open defer record in the
659-
// chain, in order sorted by sp.
660-
d := gp._defer
661-
var prev *_defer
662-
for d != nil {
663-
dsp := d.sp
664-
if frame.sp < dsp {
665-
break
669+
if frame.sp == dsp {
670+
if !d.openDefer {
671+
throw("duplicated defer entry")
666672
}
667-
if frame.sp == dsp {
668-
if !d.openDefer {
669-
throw("duplicated defer entry")
670-
}
671-
// Don't add any record past an
672-
// in-progress defer entry. We don't
673-
// need it, and more importantly, we
674-
// want to keep the invariant that
675-
// there is no open defer entry
676-
// passed an in-progress entry (see
677-
// header comment).
678-
if d.started {
679-
return false
680-
}
681-
return true
673+
// Don't add any record past an
674+
// in-progress defer entry. We don't
675+
// need it, and more importantly, we
676+
// want to keep the invariant that
677+
// there is no open defer entry
678+
// passed an in-progress entry (see
679+
// header comment).
680+
if d.started {
681+
break frames
682682
}
683-
prev = d
684-
d = d.link
685-
}
686-
if frame.fn.deferreturn == 0 {
687-
throw("missing deferreturn")
683+
continue frames
688684
}
685+
prev = d
686+
d = d.link
687+
}
688+
if frame.fn.deferreturn == 0 {
689+
throw("missing deferreturn")
690+
}
689691

690-
d1 := newdefer()
691-
d1.openDefer = true
692-
d1._panic = nil
693-
// These are the pc/sp to set after we've
694-
// run a defer in this frame that did a
695-
// recover. We return to a special
696-
// deferreturn that runs any remaining
697-
// defers and then returns from the
698-
// function.
699-
d1.pc = frame.fn.entry() + uintptr(frame.fn.deferreturn)
700-
d1.varp = frame.varp
701-
d1.fd = fd
702-
// Save the SP/PC associated with current frame,
703-
// so we can continue stack trace later if needed.
704-
d1.framepc = frame.pc
705-
d1.sp = frame.sp
706-
d1.link = d
707-
if prev == nil {
708-
gp._defer = d1
709-
} else {
710-
prev.link = d1
711-
}
712-
// Stop stack scanning after adding one open defer record
713-
return false
714-
},
715-
nil, 0)
692+
d1 := newdefer()
693+
d1.openDefer = true
694+
d1._panic = nil
695+
// These are the pc/sp to set after we've
696+
// run a defer in this frame that did a
697+
// recover. We return to a special
698+
// deferreturn that runs any remaining
699+
// defers and then returns from the
700+
// function.
701+
d1.pc = frame.fn.entry() + uintptr(frame.fn.deferreturn)
702+
d1.varp = frame.varp
703+
d1.fd = fd
704+
// Save the SP/PC associated with current frame,
705+
// so we can continue stack trace later if needed.
706+
d1.framepc = frame.pc
707+
d1.sp = frame.sp
708+
d1.link = d
709+
if prev == nil {
710+
gp._defer = d1
711+
} else {
712+
prev.link = d1
713+
}
714+
// Stop stack scanning after adding one open defer record
715+
break
716+
}
716717
})
717718
}
718719

src/runtime/stack.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -649,11 +649,10 @@ func adjustpointers(scanp unsafe.Pointer, bv *bitvector, adjinfo *adjustinfo, f
649649
}
650650

651651
// Note: the argument/return area is adjusted by the callee.
652-
func adjustframe(frame *stkframe, arg unsafe.Pointer) bool {
653-
adjinfo := (*adjustinfo)(arg)
652+
func adjustframe(frame *stkframe, adjinfo *adjustinfo) {
654653
if frame.continpc == 0 {
655654
// Frame is dead.
656-
return true
655+
return
657656
}
658657
f := frame.fn
659658
if stackDebug >= 2 {
@@ -663,7 +662,7 @@ func adjustframe(frame *stkframe, arg unsafe.Pointer) bool {
663662
// A special routine at the bottom of stack of a goroutine that does a systemstack call.
664663
// We will allow it to be copied even though we don't
665664
// have full GC info for it (because it is written in asm).
666-
return true
665+
return
667666
}
668667

669668
locals, args, objs := frame.getStackMap(&adjinfo.cache, true)
@@ -736,8 +735,6 @@ func adjustframe(frame *stkframe, arg unsafe.Pointer) bool {
736735
}
737736
}
738737
}
739-
740-
return true
741738
}
742739

743740
func adjustctxt(gp *g, adjinfo *adjustinfo) {
@@ -931,7 +928,10 @@ func copystack(gp *g, newsize uintptr) {
931928
gp.stktopsp += adjinfo.delta
932929

933930
// Adjust pointers in the new stack.
934-
gentraceback(^uintptr(0), ^uintptr(0), 0, gp, 0, nil, 0x7fffffff, adjustframe, noescape(unsafe.Pointer(&adjinfo)), 0)
931+
var u unwinder
932+
for u.init(gp, 0); u.valid(); u.next() {
933+
adjustframe(&u.frame, &adjinfo)
934+
}
935935

936936
// free old stack
937937
if stackPoisonCopy != 0 {

src/runtime/traceback.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -542,24 +542,24 @@ func (u *unwinder) finishInternal() {
542542
}
543543

544544
// Generic traceback. Handles runtime stack prints (pcbuf == nil),
545-
// the runtime.Callers function (pcbuf != nil), as well as the garbage
546-
// collector (callback != nil). A little clunky to merge these, but avoids
545+
// and the runtime.Callers function (pcbuf != nil).
546+
// A little clunky to merge these, but avoids
547547
// duplicating the code and all its subtlety.
548548
//
549549
// The skip argument is only valid with pcbuf != nil and counts the number
550550
// of logical frames to skip rather than physical frames (with inlining, a
551551
// PC in pcbuf can represent multiple calls).
552552
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 {
553-
if skip > 0 && callback != nil {
554-
throw("gentraceback callback cannot be used with non-zero skip")
553+
if callback != nil {
554+
throw("callback argument no longer supported")
555555
}
556556

557557
// Translate flags
558558
var uflags unwindFlags
559559
printing := pcbuf == nil && callback == nil
560560
if printing {
561561
uflags |= unwindPrintErrors
562-
} else if callback == nil {
562+
} else {
563563
uflags |= unwindSilentErrors
564564
}
565565
if flags&_TraceTrap != 0 {
@@ -581,12 +581,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
581581
frame := &u.frame
582582
f := frame.fn
583583

584-
if callback != nil {
585-
if !callback((*stkframe)(noescape(unsafe.Pointer(frame))), v) {
586-
return n
587-
}
588-
}
589-
590584
// Backup to the CALL instruction to read inlining info
591585
//
592586
// Normally, pc is a return address. In that case, we want to look up
@@ -670,9 +664,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
670664
u.cgoCtxt--
671665

672666
// skip only applies to Go frames.
673-
// callback != nil only used when we only care
674-
// about Go frames.
675-
if skip == 0 && callback == nil {
667+
if skip == 0 {
676668
n = tracebackCgoContext(pcbuf, printing, ctxt, n, max)
677669
}
678670
}

0 commit comments

Comments
 (0)