blob: 2874a048cf3e6f03a74179e408aaeedd6b2951b4 [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001package errors
2
3import (
4 "fmt"
5 "io"
6 "path"
7 "runtime"
8 "strings"
9)
10
11// Frame represents a program counter inside a stack frame.
12type Frame uintptr
13
14// pc returns the program counter for this frame;
15// multiple frames may have the same PC value.
16func (f Frame) pc() uintptr { return uintptr(f) - 1 }
17
18// file returns the full path to the file that contains the
19// function for this Frame's pc.
20func (f Frame) file() string {
21 fn := runtime.FuncForPC(f.pc())
22 if fn == nil {
23 return "unknown"
24 }
25 file, _ := fn.FileLine(f.pc())
26 return file
27}
28
29// line returns the line number of source code of the
30// function for this Frame's pc.
31func (f Frame) line() int {
32 fn := runtime.FuncForPC(f.pc())
33 if fn == nil {
34 return 0
35 }
36 _, line := fn.FileLine(f.pc())
37 return line
38}
39
40// Format formats the frame according to the fmt.Formatter interface.
41//
42// %s source file
43// %d source line
44// %n function name
45// %v equivalent to %s:%d
46//
47// Format accepts flags that alter the printing of some verbs, as follows:
48//
49// %+s function name and path of source file relative to the compile time
50// GOPATH separated by \n\t (<funcname>\n\t<path>)
51// %+v equivalent to %+s:%d
52func (f Frame) Format(s fmt.State, verb rune) {
53 switch verb {
54 case 's':
55 switch {
56 case s.Flag('+'):
57 pc := f.pc()
58 fn := runtime.FuncForPC(pc)
59 if fn == nil {
60 io.WriteString(s, "unknown")
61 } else {
62 file, _ := fn.FileLine(pc)
63 fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
64 }
65 default:
66 io.WriteString(s, path.Base(f.file()))
67 }
68 case 'd':
69 fmt.Fprintf(s, "%d", f.line())
70 case 'n':
71 name := runtime.FuncForPC(f.pc()).Name()
72 io.WriteString(s, funcname(name))
73 case 'v':
74 f.Format(s, 's')
75 io.WriteString(s, ":")
76 f.Format(s, 'd')
77 }
78}
79
80// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
81type StackTrace []Frame
82
83// Format formats the stack of Frames according to the fmt.Formatter interface.
84//
85// %s lists source files for each Frame in the stack
86// %v lists the source file and line number for each Frame in the stack
87//
88// Format accepts flags that alter the printing of some verbs, as follows:
89//
90// %+v Prints filename, function, and line number for each Frame in the stack.
91func (st StackTrace) Format(s fmt.State, verb rune) {
92 switch verb {
93 case 'v':
94 switch {
95 case s.Flag('+'):
96 for _, f := range st {
97 fmt.Fprintf(s, "\n%+v", f)
98 }
99 case s.Flag('#'):
100 fmt.Fprintf(s, "%#v", []Frame(st))
101 default:
102 fmt.Fprintf(s, "%v", []Frame(st))
103 }
104 case 's':
105 fmt.Fprintf(s, "%s", []Frame(st))
106 }
107}
108
109// stack represents a stack of program counters.
110type stack []uintptr
111
112func (s *stack) Format(st fmt.State, verb rune) {
113 switch verb {
114 case 'v':
115 switch {
116 case st.Flag('+'):
117 for _, pc := range *s {
118 f := Frame(pc)
119 fmt.Fprintf(st, "\n%+v", f)
120 }
121 }
122 }
123}
124
125func (s *stack) StackTrace() StackTrace {
126 f := make([]Frame, len(*s))
127 for i := 0; i < len(f); i++ {
128 f[i] = Frame((*s)[i])
129 }
130 return f
131}
132
133func callers() *stack {
134 const depth = 32
135 var pcs [depth]uintptr
136 n := runtime.Callers(3, pcs[:])
137 var st stack = pcs[0:n]
138 return &st
139}
140
141// funcname removes the path prefix component of a function's name reported by func.Name().
142func funcname(name string) string {
143 i := strings.LastIndex(name, "/")
144 name = name[i+1:]
145 i = strings.Index(name, ".")
146 return name[i+1:]
147}