stack.go (4221B)
1 package errors 2 3 import ( 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. 15 type Frame uintptr 16 17 // pc returns the program counter for this frame; 18 // multiple frames may have the same PC value. 19 func (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. 23 func (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. 34 func (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. 44 func (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 64 func (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. 88 func (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). 97 type 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. 107 func (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'. 128 func (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. 140 type stack []uintptr 141 142 func (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 155 func (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 163 func 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(). 172 func 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 }