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 slash = []byte("/") 22 ) 23 24 // PrintStack prints to standard error the stack trace returned by Stack. 25 func PrintStack() { 26 os.Stderr.Write(stack()) 27 } 28 29 // Stack returns a formatted stack trace of the goroutine that calls it. 30 // For each routine, it includes the source line information and PC value, 31 // then attempts to discover, for Go functions, the calling function or 32 // method and the text of the line containing the invocation. 33 // 34 // Deprecated: Use package runtime's Stack instead. 35 func Stack() []byte { 36 return stack() 37 } 38 39 // stack implements Stack, skipping 2 frames 40 func stack() []byte { 41 buf := new(bytes.Buffer) // the returned data 42 // As we loop, we open files and read them. These variables record the currently 43 // loaded file. 44 var lines [][]byte 45 var lastFile string 46 for i := 2; ; i++ { // Caller we care about is the user, 2 frames up 47 pc, file, line, ok := runtime.Caller(i) 48 if !ok { 49 break 50 } 51 // Print this much at least. If we can't find the source, it won't show. 52 fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) 53 if file != lastFile { 54 data, err := ioutil.ReadFile(file) 55 if err != nil { 56 continue 57 } 58 lines = bytes.Split(data, []byte{'\n'}) 59 lastFile = file 60 } 61 line-- // in stack trace, lines are 1-indexed but our array is 0-indexed 62 fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) 63 } 64 return buf.Bytes() 65 } 66 67 // source returns a space-trimmed slice of the n'th line. 68 func source(lines [][]byte, n int) []byte { 69 if n < 0 || n >= len(lines) { 70 return dunno 71 } 72 return bytes.Trim(lines[n], " \t") 73 } 74 75 // function returns, if possible, the name of the function containing the PC. 76 func function(pc uintptr) []byte { 77 fn := runtime.FuncForPC(pc) 78 if fn == nil { 79 return dunno 80 } 81 name := []byte(fn.Name()) 82 // The name includes the path name to the package, which is unnecessary 83 // since the file name is already included. Plus, it has center dots. 84 // That is, we see 85 // runtime/debug.*Tptrmethod 86 // and want 87 // *T.ptrmethod 88 // Since the package path might contains dots (e.g. code.google.com/...), 89 // we first remove the path prefix if there is one. 90 if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 { 91 name = name[lastslash+1:] 92 } 93 if period := bytes.Index(name, dot); period >= 0 { 94 name = name[period+1:] 95 } 96 name = bytes.Replace(name, centerDot, dot, -1) 97 return name 98 } 99