Source file src/pkg/runtime/debug/stack.go
1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package debug contains facilities for programs to debug themselves while
6 // they are running.
7 package debug
8
9 import (
10 "bytes"
11 "fmt"
12 "io/ioutil"
13 "os"
14 "runtime"
15 )
16
17 var (
18 dunno = []byte("???")
19 centerDot = []byte("·")
20 dot = []byte(".")
21 )
22
23 // PrintStack prints to standard error the stack trace returned by Stack.
24 func PrintStack() {
25 os.Stderr.Write(stack())
26 }
27
28 // Stack returns a formatted stack trace of the goroutine that calls it.
29 // For each routine, it includes the source line information and PC value,
30 // then attempts to discover, for Go functions, the calling function or
31 // method and the text of the line containing the invocation.
32 func Stack() []byte {
33 return stack()
34 }
35
36 // stack implements Stack, skipping 2 frames
37 func stack() []byte {
38 buf := new(bytes.Buffer) // the returned data
39 // As we loop, we open files and read them. These variables record the currently
40 // loaded file.
41 var lines [][]byte
42 var lastFile string
43 for i := 2; ; i++ { // Caller we care about is the user, 2 frames up
44 pc, file, line, ok := runtime.Caller(i)
45 if !ok {
46 break
47 }
48 // Print this much at least. If we can't find the source, it won't show.
49 fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
50 if file != lastFile {
51 data, err := ioutil.ReadFile(file)
52 if err != nil {
53 continue
54 }
55 lines = bytes.Split(data, []byte{'\n'})
56 lastFile = file
57 }
58 line-- // in stack trace, lines are 1-indexed but our array is 0-indexed
59 fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
60 }
61 return buf.Bytes()
62 }
63
64 // source returns a space-trimmed slice of the n'th line.
65 func source(lines [][]byte, n int) []byte {
66 if n < 0 || n >= len(lines) {
67 return dunno
68 }
69 return bytes.Trim(lines[n], " \t")
70 }
71
72 // function returns, if possible, the name of the function containing the PC.
73 func function(pc uintptr) []byte {
74 fn := runtime.FuncForPC(pc)
75 if fn == nil {
76 return dunno
77 }
78 name := []byte(fn.Name())
79 // The name includes the path name to the package, which is unnecessary
80 // since the file name is already included. Plus, it has center dots.
81 // That is, we see
82 // runtime/debug.*T·ptrmethod
83 // and want
84 // *T.ptrmethod
85 if period := bytes.Index(name, dot); period >= 0 {
86 name = name[period+1:]
87 }
88 name = bytes.Replace(name, centerDot, dot, -1)
89 return name
90 }