stack.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. package errors
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "path"
  7. "runtime"
  8. "strconv"
  9. "strings"
  10. )
  11. // StackTracer retrieves the StackTrace
  12. // Generally you would want to use the GetStackTracer function to do that.
  13. type StackTracer interface {
  14. StackTrace() StackTrace
  15. }
  16. // GetStackTracer will return the first StackTracer in the causer chain.
  17. // This function is used by AddStack to avoid creating redundant stack traces.
  18. //
  19. // You can also use the StackTracer interface on the returned error to get the stack trace.
  20. func GetStackTracer(origErr error) StackTracer {
  21. var stacked StackTracer
  22. WalkDeep(origErr, func(err error) bool {
  23. if stackTracer, ok := err.(StackTracer); ok {
  24. stacked = stackTracer
  25. return true
  26. }
  27. return false
  28. })
  29. return stacked
  30. }
  31. // Frame represents a program counter inside a stack frame.
  32. type Frame uintptr
  33. // pc returns the program counter for this frame;
  34. // multiple frames may have the same PC value.
  35. func (f Frame) pc() uintptr { return uintptr(f) - 1 }
  36. // file returns the full path to the file that contains the
  37. // function for this Frame's pc.
  38. func (f Frame) file() string {
  39. fn := runtime.FuncForPC(f.pc())
  40. if fn == nil {
  41. return "unknown"
  42. }
  43. file, _ := fn.FileLine(f.pc())
  44. return file
  45. }
  46. // line returns the line number of source code of the
  47. // function for this Frame's pc.
  48. func (f Frame) line() int {
  49. fn := runtime.FuncForPC(f.pc())
  50. if fn == nil {
  51. return 0
  52. }
  53. _, line := fn.FileLine(f.pc())
  54. return line
  55. }
  56. // Format formats the frame according to the fmt.Formatter interface.
  57. //
  58. // %s source file
  59. // %d source line
  60. // %n function name
  61. // %v equivalent to %s:%d
  62. //
  63. // Format accepts flags that alter the printing of some verbs, as follows:
  64. //
  65. // %+s function name and path of source file relative to the compile time
  66. // GOPATH separated by \n\t (<funcname>\n\t<path>)
  67. // %+v equivalent to %+s:%d
  68. func (f Frame) Format(s fmt.State, verb rune) {
  69. f.format(s, s, verb)
  70. }
  71. // format allows stack trace printing calls to be made with a bytes.Buffer.
  72. func (f Frame) format(w io.Writer, s fmt.State, verb rune) {
  73. switch verb {
  74. case 's':
  75. switch {
  76. case s.Flag('+'):
  77. pc := f.pc()
  78. fn := runtime.FuncForPC(pc)
  79. if fn == nil {
  80. io.WriteString(w, "unknown")
  81. } else {
  82. file, _ := fn.FileLine(pc)
  83. io.WriteString(w, fn.Name())
  84. io.WriteString(w, "\n\t")
  85. io.WriteString(w, file)
  86. }
  87. default:
  88. io.WriteString(w, path.Base(f.file()))
  89. }
  90. case 'd':
  91. io.WriteString(w, strconv.Itoa(f.line()))
  92. case 'n':
  93. name := runtime.FuncForPC(f.pc()).Name()
  94. io.WriteString(w, funcname(name))
  95. case 'v':
  96. f.format(w, s, 's')
  97. io.WriteString(w, ":")
  98. f.format(w, s, 'd')
  99. }
  100. }
  101. // StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
  102. type StackTrace []Frame
  103. // Format formats the stack of Frames according to the fmt.Formatter interface.
  104. //
  105. // %s lists source files for each Frame in the stack
  106. // %v lists the source file and line number for each Frame in the stack
  107. //
  108. // Format accepts flags that alter the printing of some verbs, as follows:
  109. //
  110. // %+v Prints filename, function, and line number for each Frame in the stack.
  111. func (st StackTrace) Format(s fmt.State, verb rune) {
  112. var b bytes.Buffer
  113. switch verb {
  114. case 'v':
  115. switch {
  116. case s.Flag('+'):
  117. b.Grow(len(st) * stackMinLen)
  118. for _, fr := range st {
  119. b.WriteByte('\n')
  120. fr.format(&b, s, verb)
  121. }
  122. case s.Flag('#'):
  123. fmt.Fprintf(&b, "%#v", []Frame(st))
  124. default:
  125. st.formatSlice(&b, s, verb)
  126. }
  127. case 's':
  128. st.formatSlice(&b, s, verb)
  129. }
  130. io.Copy(s, &b)
  131. }
  132. // formatSlice will format this StackTrace into the given buffer as a slice of
  133. // Frame, only valid when called with '%s' or '%v'.
  134. func (st StackTrace) formatSlice(b *bytes.Buffer, s fmt.State, verb rune) {
  135. b.WriteByte('[')
  136. if len(st) == 0 {
  137. b.WriteByte(']')
  138. return
  139. }
  140. b.Grow(len(st) * (stackMinLen / 4))
  141. st[0].format(b, s, verb)
  142. for _, fr := range st[1:] {
  143. b.WriteByte(' ')
  144. fr.format(b, s, verb)
  145. }
  146. b.WriteByte(']')
  147. }
  148. // stackMinLen is a best-guess at the minimum length of a stack trace. It
  149. // doesn't need to be exact, just give a good enough head start for the buffer
  150. // to avoid the expensive early growth.
  151. const stackMinLen = 96
  152. // stack represents a stack of program counters.
  153. type stack []uintptr
  154. func (s *stack) Format(st fmt.State, verb rune) {
  155. switch verb {
  156. case 'v':
  157. switch {
  158. case st.Flag('+'):
  159. var b bytes.Buffer
  160. b.Grow(len(*s) * stackMinLen)
  161. for _, pc := range *s {
  162. f := Frame(pc)
  163. b.WriteByte('\n')
  164. f.format(&b, st, 'v')
  165. }
  166. io.Copy(st, &b)
  167. }
  168. }
  169. }
  170. func (s *stack) StackTrace() StackTrace {
  171. f := make([]Frame, len(*s))
  172. for i := 0; i < len(f); i++ {
  173. f[i] = Frame((*s)[i])
  174. }
  175. return f
  176. }
  177. func callers() *stack {
  178. return callersSkip(4)
  179. }
  180. func callersSkip(skip int) *stack {
  181. const depth = 32
  182. var pcs [depth]uintptr
  183. n := runtime.Callers(skip, pcs[:])
  184. var st stack = pcs[0:n]
  185. return &st
  186. }
  187. // funcname removes the path prefix component of a function's name reported by func.Name().
  188. func funcname(name string) string {
  189. i := strings.LastIndex(name, "/")
  190. name = name[i+1:]
  191. i = strings.Index(name, ".")
  192. return name[i+1:]
  193. }
  194. // NewStack is for library implementers that want to generate a stack trace.
  195. // Normally you should insted use AddStack to get an error with a stack trace.
  196. //
  197. // The result of this function can be turned into a stack trace by calling .StackTrace()
  198. //
  199. // This function takes an argument for the number of stack frames to skip.
  200. // This avoids putting stack generation function calls like this one in the stack trace.
  201. // A value of 0 will give you the line that called NewStack(0)
  202. // A library author wrapping this in their own function will want to use a value of at least 1.
  203. func NewStack(skip int) StackTracer {
  204. return callersSkip(skip + 3)
  205. }