gopkg.in/hedzr/errors.v3@v3.3.1/stack.go (about) 1 package errors 2 3 import ( 4 "fmt" 5 "io" 6 "path" 7 "runtime" 8 "strings" 9 ) 10 11 // Frame represents a program counter inside a Stack frame. 12 type Frame uintptr 13 14 // pc returns the program counter for this frame; 15 // multiple frames may have the same PC value. 16 func (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. 20 func (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. 31 func (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 50 // compiling time. 51 // GOPATH separated by \n\t (<funcname>\n\t<path>) 52 // %+v equivalent to %+s:%d 53 func (f Frame) Format(s fmt.State, verb rune) { 54 switch verb { 55 case 's': 56 switch { 57 case s.Flag('+'): 58 pc := f.pc() 59 fn := runtime.FuncForPC(pc) 60 if fn == nil { 61 _, _ = io.WriteString(s, "unknown") 62 } else { 63 file, _ := fn.FileLine(pc) 64 _, _ = fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) 65 } 66 default: 67 _, _ = io.WriteString(s, path.Base(f.file())) 68 } 69 case 'd': 70 _, _ = fmt.Fprintf(s, "%d", f.line()) 71 case 'n': 72 name := runtime.FuncForPC(f.pc()).Name() 73 _, _ = io.WriteString(s, funcname(name)) 74 case 'v': 75 f.Format(s, 's') 76 _, _ = io.WriteString(s, ":") 77 f.Format(s, 'd') 78 } 79 } 80 81 // StackTrace is Stack of Frames from innermost (newest) to outermost (oldest). 82 type StackTrace []Frame 83 84 // Format formats the Stack of Frames according to the fmt.Formatter interface. 85 // 86 // %s lists source files for each Frame in the Stack 87 // %v lists the source file and line number for each Frame in the Stack 88 // 89 // Format accepts flags that alter the printing of some verbs, as follows: 90 // 91 // %+v Prints filename, function, and line number for each Frame in the Stack. 92 func (st StackTrace) Format(s fmt.State, verb rune) { 93 switch verb { 94 case 'v': 95 switch { 96 case s.Flag('+'): 97 for _, f := range st { 98 _, _ = fmt.Fprintf(s, "\n%+v", f) 99 } 100 case s.Flag('#'): 101 _, _ = fmt.Fprintf(s, "%#v", []Frame(st)) 102 default: 103 _, _ = fmt.Fprintf(s, "%v", []Frame(st)) 104 } 105 case 's': 106 _, _ = fmt.Fprintf(s, "%s", []Frame(st)) 107 } 108 } 109 110 // Stack represents a Stack of program counters. 111 type Stack []uintptr 112 113 // Format formats the stack of Frames according to the fmt.Formatter interface. 114 // 115 // %s lists source files for each Frame in the stack 116 // %v lists the source file and line number for each Frame in the stack 117 // 118 // Format accepts flags that alter the printing of some verbs, as follows: 119 // 120 // %+v Prints filename, function, and line number for each Frame in the stack. 121 func (s *Stack) Format(st fmt.State, verb rune) { 122 if verb == 'v' && st.Flag('+') { 123 for _, pc := range *s { 124 f := Frame(pc) 125 _, _ = fmt.Fprintf(st, "\n%+v", f) 126 } 127 } 128 } 129 130 // StackTrace returns the stacktrace frames 131 func (s *Stack) StackTrace() StackTrace { 132 f := make([]Frame, len(*s)) 133 for i := 0; i < len(f); i++ { 134 f[i] = Frame((*s)[i]) 135 } 136 return f 137 } 138 139 func callers(skip int) *Stack { 140 const depth = 32 141 var pcs [depth]uintptr 142 n := runtime.Callers(2+skip, pcs[:]) // by default, we skip these frames: callers(), and runtime.Callers() 143 var st Stack = pcs[0:n] 144 return &st 145 } 146 147 // funcname removes the path prefix component of a function's name reported by func.Name(). 148 func funcname(name string) string { 149 i := strings.LastIndex(name, "/") 150 name = name[i+1:] //nolint:revive 151 i = strings.Index(name, ".") 152 return name[i+1:] 153 }