diff --git a/src/runtime/debug/stack.go b/src/runtime/debug/stack.go index ab12bffa6e5da817339149928101b4fb6f88ddbd..952d834bc43bb0d41c3419d9a798634f3b514b73 100644 --- a/src/runtime/debug/stack.go +++ b/src/runtime/debug/stack.go @@ -7,92 +7,26 @@ package debug import ( - "bytes" - "fmt" - "io/ioutil" "os" "runtime" ) -var ( - dunno = []byte("???") - centerDot = []byte("路") - dot = []byte(".") - slash = []byte("/") -) - // PrintStack prints to standard error the stack trace returned by Stack. func PrintStack() { - os.Stderr.Write(stack()) + os.Stderr.Write(Stack()) } // Stack returns a formatted stack trace of the goroutine that calls it. // For each routine, it includes the source line information and PC value, // then attempts to discover, for Go functions, the calling function or -// method and the text of the line containing the invocation. -// -// Deprecated: Use package runtime's Stack instead. +// method. func Stack() []byte { - return stack() -} - -// stack implements Stack, skipping 2 frames -func stack() []byte { - 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 + buf := make([]byte, 1024) + for { + n := runtime.Stack(buf, false) + if n < len(buf) { + return buf[:n] } - // Print this much at least. If we can't find the source, it won't show. - 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:] + buf = make([]byte, 2*len(buf)) } - name = bytes.Replace(name, centerDot, dot, -1) - return name } diff --git a/src/runtime/debug/stack_test.go b/src/runtime/debug/stack_test.go index 28691ee4d5518496bde98107d8aa30cfd083c312..d2a4ea5b374f6c1cc00d9b2751fe005f6b8aaa73 100644 --- a/src/runtime/debug/stack_test.go +++ b/src/runtime/debug/stack_test.go @@ -22,16 +22,19 @@ func (t T) method() []byte { 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. - /Users/r/go/src/runtime/debug/stack_test.go:15 (0x13878) - (*T).ptrmethod: return Stack() - /Users/r/go/src/runtime/debug/stack_test.go:18 (0x138dd) - T.method: return t.ptrmethod() - /Users/r/go/src/runtime/debug/stack_test.go:23 (0x13920) - TestStack: b := T(0).method() - /Users/r/go/src/testing/testing.go:132 (0x14a7a) - tRunner: test.F(t) - /Users/r/go/src/runtime/proc.c:145 (0xc970) - ???: runtime路unlock(&runtime路sched); + goroutine 10 [running]: + runtime/debug.Stack(0x0, 0x0, 0x0) + /Users/r/go/src/runtime/debug/stack.go:28 +0x80 + runtime/debug.(*T).ptrmethod(0xc82005ee70, 0x0, 0x0, 0x0) + /Users/r/go/src/runtime/debug/stack_test.go:15 +0x29 + runtime/debug.T.method(0x0, 0x0, 0x0, 0x0) + /Users/r/go/src/runtime/debug/stack_test.go:18 +0x32 + runtime/debug.TestStack(0xc8201ce000) + /Users/r/go/src/runtime/debug/stack_test.go:37 +0x38 + 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) { b := T(0).method() @@ -41,17 +44,16 @@ func TestStack(t *testing.T) { } n := 0 frame := func(line, code string) { + check(t, lines[n], code) + n++ check(t, lines[n], line) 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()") - frame("src/runtime/debug/stack_test.go", "\tT.method: return t.ptrmethod()") - frame("src/runtime/debug/stack_test.go", "\tTestStack: b := T(0).method()") + n++ + frame("src/runtime/debug/stack.go", "runtime/debug.Stack") + 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", "") }