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