Home | History | Annotate | Download | only in debug
      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