| // 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 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(".") | |
| slash = []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. | |
| // | |
| // This function is deprecated. Use package runtime's Stack instead. | |
| 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'}) | |
| 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 | |
| // Since the package path might contains dots (e.g. code.google.com/...), | |
| // we first remove the path prefix if there is one. | |
| if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 { | |
| name = name[lastslash+1:] | |
| } | |
| if period := bytes.Index(name, dot); period >= 0 { | |
| name = name[period+1:] | |
| } | |
| name = bytes.Replace(name, centerDot, dot, -1) | |
| return name | |
| } |