stack.go 3.9 KB

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