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 }