Skip to content

Commit af134b1

Browse files
committed
runtime: proper panic tracebacks with mid-stack inlining
As a followon to CL 152537, modify the panic-printing traceback to also handle mid-stack inlining correctly. Also declare -fm functions (aka method functions) as wrappers, so that they get elided during traceback. This fixes part 2 of #26839. Fixes #28640 Fixes #24488 Update #26839 Change-Id: I1c535a9b87a9a1ea699621be1e6526877b696c21 Reviewed-on: https://go-review.googlesource.com/c/153477 Reviewed-by: David Chase <[email protected]>
1 parent 6886677 commit af134b1

File tree

3 files changed

+89
-54
lines changed

3 files changed

+89
-54
lines changed

src/cmd/internal/objabi/funcid.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,8 @@ func GetFuncID(name, file string) FuncID {
9292
return FuncID_wrapper
9393
}
9494
}
95+
if strings.HasSuffix(name, "-fm") {
96+
return FuncID_wrapper
97+
}
9598
return FuncID_normal
9699
}

src/runtime/traceback.go

Lines changed: 48 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
146146
cgoCtxt := gp.cgoCtxt
147147
printing := pcbuf == nil && callback == nil
148148
_defer := gp._defer
149-
elideWrapper := false
150149

151150
for _defer != nil && _defer.sp == _NoArgs {
152151
_defer = _defer.link
@@ -392,32 +391,39 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
392391
// any frames. And don't elide wrappers that
393392
// called panic rather than the wrapped
394393
// function. Otherwise, leave them out.
395-
name := funcname(f)
396-
nextElideWrapper := elideWrapperCalling(f.funcID)
397-
if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp, nprint == 0, elideWrapper && nprint != 0) {
398-
// Print during crash.
399-
// main(0x1, 0x2, 0x3)
400-
// /home/rsc/go/src/runtime/x.go:23 +0xf
401-
//
402-
tracepc := frame.pc // back up to CALL instruction for funcline.
403-
if (n > 0 || flags&_TraceTrap == 0) && frame.pc > f.entry && !waspanic {
404-
tracepc--
405-
}
406-
file, line := funcline(f, tracepc)
407-
inldata := funcdata(f, _FUNCDATA_InlTree)
408-
if inldata != nil {
409-
inltree := (*[1 << 20]inlinedCall)(inldata)
394+
395+
// backup to CALL instruction to read inlining info (same logic as below)
396+
tracepc := frame.pc
397+
if (n > 0 || flags&_TraceTrap == 0) && frame.pc > f.entry && !waspanic {
398+
tracepc--
399+
}
400+
// If there is inlining info, print the inner frames.
401+
if inldata := funcdata(f, _FUNCDATA_InlTree); inldata != nil {
402+
inltree := (*[1 << 20]inlinedCall)(inldata)
403+
for {
410404
ix := pcdatavalue(f, _PCDATA_InlTreeIndex, tracepc, nil)
411-
for ix != -1 {
405+
if ix < 0 {
406+
break
407+
}
408+
if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp, nprint == 0, inltree[ix].funcID, lastFuncID) {
412409
name := funcnameFromNameoff(f, inltree[ix].func_)
410+
file, line := funcline(f, tracepc)
413411
print(name, "(...)\n")
414412
print("\t", file, ":", line, "\n")
415-
416-
file = funcfile(f, inltree[ix].file)
417-
line = inltree[ix].line
418-
ix = int32(inltree[ix].parent)
413+
nprint++
419414
}
415+
lastFuncID = inltree[ix].funcID
416+
// Back up to an instruction in the "caller".
417+
tracepc = frame.fn.entry + uintptr(inltree[ix].parentPc)
420418
}
419+
}
420+
if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp, nprint == 0, f.funcID, lastFuncID) {
421+
// Print during crash.
422+
// main(0x1, 0x2, 0x3)
423+
// /home/rsc/go/src/runtime/x.go:23 +0xf
424+
//
425+
name := funcname(f)
426+
file, line := funcline(f, tracepc)
421427
if name == "runtime.gopanic" {
422428
name = "panic"
423429
}
@@ -444,7 +450,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
444450
print("\n")
445451
nprint++
446452
}
447-
elideWrapper = nextElideWrapper
453+
lastFuncID = f.funcID
448454
}
449455
n++
450456

@@ -669,7 +675,7 @@ func printcreatedby(gp *g) {
669675
// Show what created goroutine, except main goroutine (goid 1).
670676
pc := gp.gopc
671677
f := findfunc(pc)
672-
if f.valid() && showframe(f, gp, false, false) && gp.goid != 1 {
678+
if f.valid() && showframe(f, gp, false, funcID_normal, funcID_normal) && gp.goid != 1 {
673679
printcreatedby1(f, pc)
674680
}
675681
}
@@ -756,19 +762,18 @@ func traceback1(pc, sp, lr uintptr, gp *g, flags uint) {
756762
// TODO: Unify this with gentraceback and CallersFrames.
757763
func printAncestorTraceback(ancestor ancestorInfo) {
758764
print("[originating from goroutine ", ancestor.goid, "]:\n")
759-
elideWrapper := false
760765
for fidx, pc := range ancestor.pcs {
761766
f := findfunc(pc) // f previously validated
762-
if showfuncinfo(f, fidx == 0, elideWrapper && fidx != 0) {
763-
elideWrapper = printAncestorTracebackFuncInfo(f, pc)
767+
if showfuncinfo(f, fidx == 0, funcID_normal, funcID_normal) {
768+
printAncestorTracebackFuncInfo(f, pc)
764769
}
765770
}
766771
if len(ancestor.pcs) == _TracebackMaxFrames {
767772
print("...additional frames elided...\n")
768773
}
769774
// Show what created goroutine, except main goroutine (goid 1).
770775
f := findfunc(ancestor.gopc)
771-
if f.valid() && showfuncinfo(f, false, false) && ancestor.goid != 1 {
776+
if f.valid() && showfuncinfo(f, false, funcID_normal, funcID_normal) && ancestor.goid != 1 {
772777
printcreatedby1(f, ancestor.gopc)
773778
}
774779
}
@@ -777,27 +782,16 @@ func printAncestorTraceback(ancestor ancestorInfo) {
777782
// within an ancestor traceback. The precision of this info is reduced
778783
// due to only have access to the pcs at the time of the caller
779784
// goroutine being created.
780-
func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) bool {
781-
tracepc := pc // back up to CALL instruction for funcline.
782-
if pc > f.entry {
783-
tracepc -= sys.PCQuantum
784-
}
785-
file, line := funcline(f, tracepc)
786-
inldata := funcdata(f, _FUNCDATA_InlTree)
787-
if inldata != nil {
785+
func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) {
786+
name := funcname(f)
787+
if inldata := funcdata(f, _FUNCDATA_InlTree); inldata != nil {
788788
inltree := (*[1 << 20]inlinedCall)(inldata)
789-
ix := pcdatavalue(f, _PCDATA_InlTreeIndex, tracepc, nil)
790-
for ix != -1 {
791-
name := funcnameFromNameoff(f, inltree[ix].func_)
792-
print(name, "(...)\n")
793-
print("\t", file, ":", line, "\n")
794-
795-
file = funcfile(f, inltree[ix].file)
796-
line = inltree[ix].line
797-
ix = int32(inltree[ix].parent)
789+
ix := pcdatavalue(f, _PCDATA_InlTreeIndex, pc, nil)
790+
if ix >= 0 {
791+
name = funcnameFromNameoff(f, inltree[ix].func_)
798792
}
799793
}
800-
name := funcname(f)
794+
file, line := funcline(f, pc)
801795
if name == "runtime.gopanic" {
802796
name = "panic"
803797
}
@@ -807,7 +801,6 @@ func printAncestorTracebackFuncInfo(f funcInfo, pc uintptr) bool {
807801
print(" +", hex(pc-f.entry))
808802
}
809803
print("\n")
810-
return elideWrapperCalling(f.funcID)
811804
}
812805

813806
func callers(skip int, pcbuf []uintptr) int {
@@ -825,15 +818,19 @@ func gcallers(gp *g, skip int, pcbuf []uintptr) int {
825818
return gentraceback(^uintptr(0), ^uintptr(0), 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
826819
}
827820

828-
func showframe(f funcInfo, gp *g, firstFrame, elideWrapper bool) bool {
821+
// showframe reports whether the frame with the given characteristics should
822+
// be printed during a traceback.
823+
func showframe(f funcInfo, gp *g, firstFrame bool, funcID, childID funcID) bool {
829824
g := getg()
830825
if g.m.throwing > 0 && gp != nil && (gp == g.m.curg || gp == g.m.caughtsig.ptr()) {
831826
return true
832827
}
833-
return showfuncinfo(f, firstFrame, elideWrapper)
828+
return showfuncinfo(f, firstFrame, funcID, childID)
834829
}
835830

836-
func showfuncinfo(f funcInfo, firstFrame, elideWrapper bool) bool {
831+
// showfuncinfo reports whether a function with the given characteristics should
832+
// be printed during a traceback.
833+
func showfuncinfo(f funcInfo, firstFrame bool, funcID, childID funcID) bool {
837834
level, _, _ := gotraceback()
838835
if level > 1 {
839836
// Show all frames.
@@ -844,11 +841,8 @@ func showfuncinfo(f funcInfo, firstFrame, elideWrapper bool) bool {
844841
return false
845842
}
846843

847-
if elideWrapper {
848-
file, _ := funcline(f, f.entry)
849-
if file == "<autogenerated>" {
850-
return false
851-
}
844+
if funcID == funcID_wrapper && elideWrapperCalling(childID) {
845+
return false
852846
}
853847

854848
name := funcname(f)

test/fixedbugs/issue24488.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// run
2+
3+
// Copyright 2018 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
import (
10+
"runtime"
11+
"strings"
12+
)
13+
14+
type Func func()
15+
16+
func (f Func) Foo() {
17+
if f != nil {
18+
f()
19+
}
20+
}
21+
22+
func (f Func) Bar() {
23+
if f != nil {
24+
f()
25+
}
26+
buf := make([]byte, 4000)
27+
n := runtime.Stack(buf, true)
28+
s := string(buf[:n])
29+
if strings.Contains(s, "-fm") {
30+
panic("wrapper present in stack trace:\n" + s)
31+
}
32+
}
33+
34+
func main() {
35+
foo := Func(func() {})
36+
foo = foo.Bar
37+
foo.Foo()
38+
}

0 commit comments

Comments
 (0)