]> Cypherpunks repositories - gostls13.git/commitdiff
runtime/debug: new package
authorRob Pike <r@golang.org>
Wed, 19 Jan 2011 20:28:38 +0000 (12:28 -0800)
committerRob Pike <r@golang.org>
Wed, 19 Jan 2011 20:28:38 +0000 (12:28 -0800)
Facilities for printing stack traces from within a running goroutine.

R=rsc
CC=golang-dev
https://golang.org/cl/4031041

src/pkg/Makefile
src/pkg/runtime/debug/stack.go [new file with mode: 0644]
src/pkg/runtime/debug/stack_test.go [new file with mode: 0644]

index 05e2a26d1fdeb8500c1aecbf268447dd5b8d3f1e..b9031d359aa45e90f5560dba935f5e2b4537e47a 100644 (file)
@@ -113,6 +113,7 @@ DIRS=\
        rpc/jsonrpc\
        runtime\
        runtime/cgo\
+       runtime/debug\
        runtime/pprof\
        scanner\
        smtp\
diff --git a/src/pkg/runtime/debug/stack.go b/src/pkg/runtime/debug/stack.go
new file mode 100644 (file)
index 0000000..e7d56ac
--- /dev/null
@@ -0,0 +1,90 @@
+// Copyright 2011 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.
+
+// The debug package contains facilities for programs to debug themselves
+// while they are running.
+package debug
+
+import (
+       "bytes"
+       "fmt"
+       "io/ioutil"
+       "os"
+       "runtime"
+)
+
+var (
+       dunno     = []byte("???")
+       centerDot = []byte("·")
+       dot       = []byte(".")
+)
+
+// PrintStack prints to standard error the stack trace returned by Stack.
+func PrintStack() {
+       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.
+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
+               }
+               // 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'}, -1)
+                       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
+       if period := bytes.Index(name, dot); period >= 0 {
+               name = name[period+1:]
+       }
+       name = bytes.Replace(name, centerDot, dot, -1)
+       return name
+}
diff --git a/src/pkg/runtime/debug/stack_test.go b/src/pkg/runtime/debug/stack_test.go
new file mode 100644 (file)
index 0000000..f4bdc46
--- /dev/null
@@ -0,0 +1,55 @@
+// Copyright 2011 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 debug
+
+import (
+       "strings"
+       "testing"
+)
+
+type T int
+
+func (t *T) ptrmethod() []byte {
+       return Stack()
+}
+func (t T) method() []byte {
+       return t.ptrmethod()
+}
+
+/*
+       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/pkg/runtime/debug/stack_test.go:15 (0x13878)
+                       *T.ptrmethod: return Stack()
+               /Users/r/go/src/pkg/runtime/debug/stack_test.go:18 (0x138dd)
+                       T.method: return t.ptrmethod()
+               /Users/r/go/src/pkg/runtime/debug/stack_test.go:23 (0x13920)
+                       TestStack: b := T(0).method()
+               /Users/r/go/src/pkg/testing/testing.go:132 (0x14a7a)
+                       tRunner: test.F(t)
+               /Users/r/go/src/pkg/runtime/proc.c:145 (0xc970)
+                       ???: runtime·unlock(&runtime·sched);
+*/
+func TestStack(t *testing.T) {
+       b := T(0).method()
+       lines := strings.Split(string(b), "\n", -1)
+       if len(lines) <= 6 {
+               t.Fatal("too few lines")
+       }
+       check(t, lines[0], "src/pkg/runtime/debug/stack_test.go")
+       check(t, lines[1], "\t*T.ptrmethod: return Stack()")
+       check(t, lines[2], "src/pkg/runtime/debug/stack_test.go")
+       check(t, lines[3], "\tT.method: return t.ptrmethod()")
+       check(t, lines[4], "src/pkg/runtime/debug/stack_test.go")
+       check(t, lines[5], "\tTestStack: b := T(0).method()")
+       check(t, lines[6], "src/pkg/testing/testing.go")
+}
+
+func check(t *testing.T, line, has string) {
+       if strings.Index(line, has) < 0 {
+               t.Errorf("expected %q in %q", has, line)
+       }
+}