Skip to content

Commit e972095

Browse files
committed
runtime: hide <autogenerated> methods from call stack
The compiler generates wrapper methods to forward interface method calls (which are always pointer-based) to value methods. These wrappers appear in the call stack even though they are an implementation detail. This leaves ugly "<autogenerated>" functions in stack traces and can throw off skip counts for stack traces. Fix this by considering these runtime frames in printed stack traces so they will only be printed if runtime frames are being printed, and by eliding them from the call stack expansion used by CallersFrames and Caller. This removes the test for issue 4388 since that was checking that "<autogenerated>" appeared in the stack trace instead of something even weirder. We replace it with various runtime package tests. Fixes #16723. Change-Id: Ice3f118c66f254bb71478a664d62ab3fc7125819 Reviewed-on: https://go-review.googlesource.com/45412 Run-TryBot: Austin Clements <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 354fa9a commit e972095

File tree

4 files changed

+73
-57
lines changed

4 files changed

+73
-57
lines changed

src/runtime/stack_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package runtime_test
66

77
import (
8+
"fmt"
89
. "runtime"
910
"strings"
1011
"sync"
@@ -627,3 +628,60 @@ func count23(n int) int {
627628
}
628629
return 1 + count1(n-1)
629630
}
631+
632+
type structWithMethod struct{}
633+
634+
func (s structWithMethod) caller() string {
635+
_, file, line, ok := Caller(1)
636+
if !ok {
637+
panic("Caller failed")
638+
}
639+
return fmt.Sprintf("%s:%d", file, line)
640+
}
641+
642+
func (s structWithMethod) callers() []uintptr {
643+
pc := make([]uintptr, 16)
644+
return pc[:Callers(0, pc)]
645+
}
646+
647+
func (s structWithMethod) stack() string {
648+
buf := make([]byte, 4<<10)
649+
return string(buf[:Stack(buf, false)])
650+
}
651+
652+
func TestStackWrapperCaller(t *testing.T) {
653+
var d structWithMethod
654+
// Force the compiler to construct a wrapper method.
655+
wrapper := (*structWithMethod).caller
656+
// Check that the wrapper doesn't affect the stack trace.
657+
if dc, ic := d.caller(), wrapper(&d); dc != ic {
658+
t.Fatalf("direct caller %q != indirect caller %q", dc, ic)
659+
}
660+
}
661+
662+
func TestStackWrapperCallers(t *testing.T) {
663+
var d structWithMethod
664+
wrapper := (*structWithMethod).callers
665+
// Check that <autogenerated> doesn't appear in the stack trace.
666+
pcs := wrapper(&d)
667+
frames := CallersFrames(pcs)
668+
for {
669+
fr, more := frames.Next()
670+
if fr.File == "<autogenerated>" {
671+
t.Fatalf("<autogenerated> appears in stack trace: %+v", fr)
672+
}
673+
if !more {
674+
break
675+
}
676+
}
677+
}
678+
679+
func TestStackWrapperStack(t *testing.T) {
680+
var d structWithMethod
681+
wrapper := (*structWithMethod).stack
682+
// Check that <autogenerated> doesn't appear in the stack trace.
683+
stk := wrapper(&d)
684+
if strings.Contains(stk, "<autogenerated>") {
685+
t.Fatalf("<autogenerated> appears in stack trace:\n%s", stk)
686+
}
687+
}

src/runtime/symtab.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ func (ci *Frames) Next() (frame Frame, more bool) {
118118

119119
func (se *stackExpander) next(callers []uintptr) (ncallers []uintptr, frame Frame, more bool) {
120120
ncallers = callers
121+
again:
121122
if !se.pcExpander.more {
122123
// Expand the next PC.
123124
if len(ncallers) == 0 {
@@ -144,6 +145,13 @@ func (se *stackExpander) next(callers []uintptr) (ncallers []uintptr, frame Fram
144145
}
145146

146147
frame = se.pcExpander.next()
148+
if frame.File == "<autogenerated>" {
149+
// Ignore autogenerated functions such as pointer
150+
// method forwarding functions. These are an
151+
// implementation detail that doesn't reflect the
152+
// source code.
153+
goto again
154+
}
147155
return ncallers, frame, se.pcExpander.more || len(ncallers) > 0
148156
}
149157

src/runtime/traceback.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,13 @@ func showframe(f funcInfo, gp *g, firstFrame bool) bool {
733733
return true
734734
}
735735
level, _, _ := gotraceback()
736+
if level > 1 {
737+
// Show all frames.
738+
return true
739+
}
740+
736741
name := funcname(f)
742+
file, _ := funcline(f, f.entry)
737743

738744
// Special case: always show runtime.gopanic frame
739745
// in the middle of a stack trace, so that we can
@@ -744,7 +750,7 @@ func showframe(f funcInfo, gp *g, firstFrame bool) bool {
744750
return true
745751
}
746752

747-
return level > 1 || f.valid() && contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name))
753+
return f.valid() && contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name)) && file != "<autogenerated>"
748754
}
749755

750756
// isExportedRuntime reports whether name is an exported runtime function.

test/fixedbugs/issue4388.go

Lines changed: 0 additions & 56 deletions
This file was deleted.

0 commit comments

Comments
 (0)