package runtime_test
import (
+ "fmt"
. "runtime"
"strings"
"sync"
}
return 1 + count1(n-1)
}
+
+type structWithMethod struct{}
+
+func (s structWithMethod) caller() string {
+ _, file, line, ok := Caller(1)
+ if !ok {
+ panic("Caller failed")
+ }
+ return fmt.Sprintf("%s:%d", file, line)
+}
+
+func (s structWithMethod) callers() []uintptr {
+ pc := make([]uintptr, 16)
+ return pc[:Callers(0, pc)]
+}
+
+func (s structWithMethod) stack() string {
+ buf := make([]byte, 4<<10)
+ return string(buf[:Stack(buf, false)])
+}
+
+func TestStackWrapperCaller(t *testing.T) {
+ var d structWithMethod
+ // Force the compiler to construct a wrapper method.
+ wrapper := (*structWithMethod).caller
+ // Check that the wrapper doesn't affect the stack trace.
+ if dc, ic := d.caller(), wrapper(&d); dc != ic {
+ t.Fatalf("direct caller %q != indirect caller %q", dc, ic)
+ }
+}
+
+func TestStackWrapperCallers(t *testing.T) {
+ var d structWithMethod
+ wrapper := (*structWithMethod).callers
+ // Check that <autogenerated> doesn't appear in the stack trace.
+ pcs := wrapper(&d)
+ frames := CallersFrames(pcs)
+ for {
+ fr, more := frames.Next()
+ if fr.File == "<autogenerated>" {
+ t.Fatalf("<autogenerated> appears in stack trace: %+v", fr)
+ }
+ if !more {
+ break
+ }
+ }
+}
+
+func TestStackWrapperStack(t *testing.T) {
+ var d structWithMethod
+ wrapper := (*structWithMethod).stack
+ // Check that <autogenerated> doesn't appear in the stack trace.
+ stk := wrapper(&d)
+ if strings.Contains(stk, "<autogenerated>") {
+ t.Fatalf("<autogenerated> appears in stack trace:\n%s", stk)
+ }
+}
func (se *stackExpander) next(callers []uintptr) (ncallers []uintptr, frame Frame, more bool) {
ncallers = callers
+again:
if !se.pcExpander.more {
// Expand the next PC.
if len(ncallers) == 0 {
}
frame = se.pcExpander.next()
+ if frame.File == "<autogenerated>" {
+ // Ignore autogenerated functions such as pointer
+ // method forwarding functions. These are an
+ // implementation detail that doesn't reflect the
+ // source code.
+ goto again
+ }
return ncallers, frame, se.pcExpander.more || len(ncallers) > 0
}
return true
}
level, _, _ := gotraceback()
+ if level > 1 {
+ // Show all frames.
+ return true
+ }
+
name := funcname(f)
+ file, _ := funcline(f, f.entry)
// Special case: always show runtime.gopanic frame
// in the middle of a stack trace, so that we can
return true
}
- return level > 1 || f.valid() && contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name))
+ return f.valid() && contains(name, ".") && (!hasprefix(name, "runtime.") || isExportedRuntime(name)) && file != "<autogenerated>"
}
// isExportedRuntime reports whether name is an exported runtime function.
+++ /dev/null
-// run
-
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "fmt"
- "io"
- "runtime"
-)
-
-type T struct {
- io.Closer
-}
-
-func f1() {
- // The 5 here and below depends on the number of internal runtime frames
- // that sit between a deferred function called during panic and
- // the original frame. If that changes, this test will start failing and
- // the number here will need to be updated.
- defer checkLine(5)
- var t *T
- var c io.Closer = t
- c.Close()
-}
-
-func f2() {
- defer checkLine(5)
- var t T
- var c io.Closer = t
- c.Close()
-}
-
-func main() {
- f1()
- f2()
-}
-
-func checkLine(n int) {
- if err := recover(); err == nil {
- panic("did not panic")
- }
- var file string
- var line int
- for i := 1; i <= n; i++ {
- _, file, line, _ = runtime.Caller(i)
- if file != "<autogenerated>" || line != 1 {
- continue
- }
- return
- }
- panic(fmt.Sprintf("expected <autogenerated>:1 have %s:%d", file, line))
-}