stack.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. package errors
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "path"
  7. "runtime"
  8. "strconv"
  9. "strings"
  10. )
  11. // Frame represents a program counter inside a stack frame.
  12. type Frame runtime.Frame
  13. // Format formats the frame according to the fmt.Formatter interface.
  14. //
  15. // %s source file
  16. // %d source line
  17. // %n function name
  18. // %v equivalent to %s:%d
  19. //
  20. // Format accepts flags that alter the printing of some verbs, as follows:
  21. //
  22. // %+s function name and path of source file relative to the compile time
  23. // GOPATH separated by \n\t (<funcname>\n\t<path>)
  24. // %+v equivalent to %+s:%d
  25. func (f Frame) Format(s fmt.State, verb rune) {
  26. f.format(s, s, verb)
  27. }
  28. // format allows stack trace printing calls to be made with a bytes.Buffer.
  29. func (f Frame) format(w io.Writer, s fmt.State, verb rune) {
  30. switch verb {
  31. case 's':
  32. switch {
  33. case s.Flag('+'):
  34. fn := f.Func
  35. if fn == nil {
  36. io.WriteString(w, "unknown")
  37. } else {
  38. io.WriteString(w, fn.Name())
  39. io.WriteString(w, "\n\t")
  40. io.WriteString(w, f.File)
  41. }
  42. default:
  43. file := f.File
  44. if file == "" {
  45. file = "unknown"
  46. }
  47. io.WriteString(w, path.Base(file))
  48. }
  49. case 'd':
  50. io.WriteString(w, strconv.Itoa(f.Line))
  51. case 'n':
  52. name := f.Function
  53. io.WriteString(s, funcname(name))
  54. case 'v':
  55. f.format(w, s, 's')
  56. io.WriteString(w, ":")
  57. f.format(w, s, 'd')
  58. }
  59. }
  60. // StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
  61. type StackTrace []Frame
  62. // Format formats the stack of Frames according to the fmt.Formatter interface.
  63. //
  64. // %s lists source files for each Frame in the stack
  65. // %v lists the source file and line number for each Frame in the stack
  66. //
  67. // Format accepts flags that alter the printing of some verbs, as follows:
  68. //
  69. // %+v Prints filename, function, and line number for each Frame in the stack.
  70. func (st StackTrace) Format(s fmt.State, verb rune) {
  71. var b bytes.Buffer
  72. switch verb {
  73. case 'v':
  74. switch {
  75. case s.Flag('+'):
  76. b.Grow(len(st) * stackMinLen)
  77. for _, fr := range st {
  78. b.WriteByte('\n')
  79. fr.format(&b, s, verb)
  80. }
  81. case s.Flag('#'):
  82. fmt.Fprintf(&b, "%#v", []Frame(st))
  83. default:
  84. st.formatSlice(&b, s, verb)
  85. }
  86. case 's':
  87. st.formatSlice(&b, s, verb)
  88. }
  89. io.Copy(s, &b)
  90. }
  91. // formatSlice will format this StackTrace into the given buffer as a slice of
  92. // Frame, only valid when called with '%s' or '%v'.
  93. func (st StackTrace) formatSlice(b *bytes.Buffer, s fmt.State, verb rune) {
  94. b.WriteByte('[')
  95. if len(st) == 0 {
  96. b.WriteByte(']')
  97. return
  98. }
  99. b.Grow(len(st) * (stackMinLen / 4))
  100. st[0].format(b, s, verb)
  101. for _, fr := range st[1:] {
  102. b.WriteByte(' ')
  103. fr.format(b, s, verb)
  104. }
  105. b.WriteByte(']')
  106. }
  107. // stackMinLen is a best-guess at the minimum length of a stack trace. It
  108. // doesn't need to be exact, just give a good enough head start for the buffer
  109. // to avoid the expensive early growth.
  110. const stackMinLen = 96
  111. // stack represents a stack of program counters.
  112. type stack []uintptr
  113. func (s *stack) Format(st fmt.State, verb rune) {
  114. switch verb {
  115. case 'v':
  116. switch {
  117. case st.Flag('+'):
  118. frames := runtime.CallersFrames(*s)
  119. for {
  120. frame, more := frames.Next()
  121. fmt.Fprintf(st, "\n%+v", Frame(frame))
  122. if !more {
  123. break
  124. }
  125. }
  126. }
  127. }
  128. }
  129. func (s *stack) StackTrace() StackTrace {
  130. var st []Frame
  131. frames := runtime.CallersFrames(*s)
  132. for {
  133. frame, more := frames.Next()
  134. st = append(st, Frame(frame))
  135. if !more {
  136. break
  137. }
  138. }
  139. return st
  140. }
  141. func callers() *stack {
  142. const depth = 32
  143. var pcs [depth]uintptr
  144. n := runtime.Callers(3, pcs[:])
  145. var st stack = pcs[0:n]
  146. return &st
  147. }
  148. // funcname removes the path prefix component of a function's name reported by func.Name().
  149. func funcname(name string) string {
  150. i := strings.LastIndex(name, "/")
  151. name = name[i+1:]
  152. i = strings.Index(name, ".")
  153. return name[i+1:]
  154. }