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