Commit 2d697b24 authored by David Crawshaw's avatar David Crawshaw

runtime/debug: implement Stack using runtime.Stack

Fixes #12363

Change-Id: I1a025ab6a1cbd5a58f5c2bce5416788387495428
Reviewed-on: https://go-review.googlesource.com/14604Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent fb302700
...@@ -7,92 +7,26 @@ ...@@ -7,92 +7,26 @@
package debug package debug
import ( import (
"bytes"
"fmt"
"io/ioutil"
"os" "os"
"runtime" "runtime"
) )
var (
dunno = []byte("???")
centerDot = []byte("·")
dot = []byte(".")
slash = []byte("/")
)
// PrintStack prints to standard error the stack trace returned by Stack. // PrintStack prints to standard error the stack trace returned by Stack.
func PrintStack() { func PrintStack() {
os.Stderr.Write(stack()) os.Stderr.Write(Stack())
} }
// Stack returns a formatted stack trace of the goroutine that calls it. // Stack returns a formatted stack trace of the goroutine that calls it.
// For each routine, it includes the source line information and PC value, // For each routine, it includes the source line information and PC value,
// then attempts to discover, for Go functions, the calling function or // then attempts to discover, for Go functions, the calling function or
// method and the text of the line containing the invocation. // method.
//
// Deprecated: Use package runtime's Stack instead.
func Stack() []byte { func Stack() []byte {
return stack() buf := make([]byte, 1024)
} for {
n := runtime.Stack(buf, false)
// stack implements Stack, skipping 2 frames if n < len(buf) {
func stack() []byte { return buf[:n]
buf := new(bytes.Buffer) // the returned data
// As we loop, we open files and read them. These variables record the currently
// loaded file.
var lines [][]byte
var lastFile string
for i := 2; ; i++ { // Caller we care about is the user, 2 frames up
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
} }
// Print this much at least. If we can't find the source, it won't show. buf = make([]byte, 2*len(buf))
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
if file != lastFile {
data, err := ioutil.ReadFile(file)
if err != nil {
continue
}
lines = bytes.Split(data, []byte{'\n'})
lastFile = file
}
line-- // in stack trace, lines are 1-indexed but our array is 0-indexed
fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
}
return buf.Bytes()
}
// source returns a space-trimmed slice of the n'th line.
func source(lines [][]byte, n int) []byte {
if n < 0 || n >= len(lines) {
return dunno
}
return bytes.Trim(lines[n], " \t")
}
// function returns, if possible, the name of the function containing the PC.
func function(pc uintptr) []byte {
fn := runtime.FuncForPC(pc)
if fn == nil {
return dunno
}
name := []byte(fn.Name())
// The name includes the path name to the package, which is unnecessary
// since the file name is already included. Plus, it has center dots.
// That is, we see
// runtime/debug.*T·ptrmethod
// and want
// *T.ptrmethod
// Since the package path might contains dots (e.g. code.google.com/...),
// we first remove the path prefix if there is one.
if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 {
name = name[lastslash+1:]
}
if period := bytes.Index(name, dot); period >= 0 {
name = name[period+1:]
} }
name = bytes.Replace(name, centerDot, dot, -1)
return name
} }
...@@ -22,16 +22,19 @@ func (t T) method() []byte { ...@@ -22,16 +22,19 @@ func (t T) method() []byte {
The traceback should look something like this, modulo line numbers and hex constants. The traceback should look something like this, modulo line numbers and hex constants.
Don't worry much about the base levels, but check the ones in our own package. Don't worry much about the base levels, but check the ones in our own package.
/Users/r/go/src/runtime/debug/stack_test.go:15 (0x13878) goroutine 10 [running]:
(*T).ptrmethod: return Stack() runtime/debug.Stack(0x0, 0x0, 0x0)
/Users/r/go/src/runtime/debug/stack_test.go:18 (0x138dd) /Users/r/go/src/runtime/debug/stack.go:28 +0x80
T.method: return t.ptrmethod() runtime/debug.(*T).ptrmethod(0xc82005ee70, 0x0, 0x0, 0x0)
/Users/r/go/src/runtime/debug/stack_test.go:23 (0x13920) /Users/r/go/src/runtime/debug/stack_test.go:15 +0x29
TestStack: b := T(0).method() runtime/debug.T.method(0x0, 0x0, 0x0, 0x0)
/Users/r/go/src/testing/testing.go:132 (0x14a7a) /Users/r/go/src/runtime/debug/stack_test.go:18 +0x32
tRunner: test.F(t) runtime/debug.TestStack(0xc8201ce000)
/Users/r/go/src/runtime/proc.c:145 (0xc970) /Users/r/go/src/runtime/debug/stack_test.go:37 +0x38
???: runtime·unlock(&runtime·sched); testing.tRunner(0xc8201ce000, 0x664b58)
/Users/r/go/src/testing/testing.go:456 +0x98
created by testing.RunTests
/Users/r/go/src/testing/testing.go:561 +0x86d
*/ */
func TestStack(t *testing.T) { func TestStack(t *testing.T) {
b := T(0).method() b := T(0).method()
...@@ -41,17 +44,16 @@ func TestStack(t *testing.T) { ...@@ -41,17 +44,16 @@ func TestStack(t *testing.T) {
} }
n := 0 n := 0
frame := func(line, code string) { frame := func(line, code string) {
check(t, lines[n], code)
n++
check(t, lines[n], line) check(t, lines[n], line)
n++ n++
// The source might not be available while running the test.
if strings.HasPrefix(lines[n], "\t") {
check(t, lines[n], code)
n++
}
} }
frame("src/runtime/debug/stack_test.go", "\t(*T).ptrmethod: return Stack()") n++
frame("src/runtime/debug/stack_test.go", "\tT.method: return t.ptrmethod()") frame("src/runtime/debug/stack.go", "runtime/debug.Stack")
frame("src/runtime/debug/stack_test.go", "\tTestStack: b := T(0).method()") frame("src/runtime/debug/stack_test.go", "runtime/debug.(*T).ptrmethod")
frame("src/runtime/debug/stack_test.go", "runtime/debug.T.method")
frame("src/runtime/debug/stack_test.go", "runtime/debug.TestStack")
frame("src/testing/testing.go", "") frame("src/testing/testing.go", "")
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment