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. 7package debug 8 9import ( 10 "bytes" 11 "fmt" 12 "io/ioutil" 13 "os" 14 "runtime" 15) 16 17var ( 18 dunno = []byte("???") 19 centerDot = []byte("·") 20 dot = []byte(".") 21) 22 23// PrintStack prints to standard error the stack trace returned by Stack. 24func 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// 33// This function is deprecated. Use package runtime's Stack instead. 34func Stack() []byte { 35 return stack() 36} 37 38// stack implements Stack, skipping 2 frames 39func stack() []byte { 40 buf := new(bytes.Buffer) // the returned data 41 // As we loop, we open files and read them. These variables record the currently 42 // loaded file. 43 var lines [][]byte 44 var lastFile string 45 for i := 2; ; i++ { // Caller we care about is the user, 2 frames up 46 pc, file, line, ok := runtime.Caller(i) 47 if !ok { 48 break 49 } 50 // Print this much at least. If we can't find the source, it won't show. 51 fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) 52 if file != lastFile { 53 data, err := ioutil.ReadFile(file) 54 if err != nil { 55 continue 56 } 57 lines = bytes.Split(data, []byte{'\n'}) 58 lastFile = file 59 } 60 line-- // in stack trace, lines are 1-indexed but our array is 0-indexed 61 fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) 62 } 63 return buf.Bytes() 64} 65 66// source returns a space-trimmed slice of the n'th line. 67func source(lines [][]byte, n int) []byte { 68 if n < 0 || n >= len(lines) { 69 return dunno 70 } 71 return bytes.Trim(lines[n], " \t") 72} 73 74// function returns, if possible, the name of the function containing the PC. 75func function(pc uintptr) []byte { 76 fn := runtime.FuncForPC(pc) 77 if fn == nil { 78 return dunno 79 } 80 name := []byte(fn.Name()) 81 // The name includes the path name to the package, which is unnecessary 82 // since the file name is already included. Plus, it has center dots. 83 // That is, we see 84 // runtime/debug.*T·ptrmethod 85 // and want 86 // *T.ptrmethod 87 if period := bytes.Index(name, dot); period >= 0 { 88 name = name[period+1:] 89 } 90 name = bytes.Replace(name, centerDot, dot, -1) 91 return name 92} 93