stack.go 3.8 KB

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