stack.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package errors
  2. import (
  3. "fmt"
  4. "io"
  5. "path"
  6. "runtime"
  7. "strings"
  8. )
  9. // Frame represents a program counter inside a stack frame.
  10. type Frame runtime.Frame
  11. // Format formats the frame according to the fmt.Formatter interface.
  12. //
  13. // %s source file
  14. // %d source line
  15. // %n function name
  16. // %v equivalent to %s:%d
  17. //
  18. // Format accepts flags that alter the printing of some verbs, as follows:
  19. //
  20. // %+s function name and path of source file relative to the compile time
  21. // GOPATH separated by \n\t (<funcname>\n\t<path>)
  22. // %+v equivalent to %+s:%d
  23. func (f Frame) Format(s fmt.State, verb rune) {
  24. switch verb {
  25. case 's':
  26. switch {
  27. case s.Flag('+'):
  28. fn := runtime.Frame(f).Func
  29. if fn == nil {
  30. io.WriteString(s, "unknown")
  31. } else {
  32. file := runtime.Frame(f).File
  33. fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
  34. }
  35. default:
  36. file := runtime.Frame(f).File
  37. if file == "" {
  38. file = "unknown"
  39. }
  40. io.WriteString(s, path.Base(file))
  41. }
  42. case 'd':
  43. fmt.Fprintf(s, "%d", runtime.Frame(f).Line)
  44. case 'n':
  45. name := runtime.Frame(f).Function
  46. io.WriteString(s, funcname(name))
  47. case 'v':
  48. f.Format(s, 's')
  49. io.WriteString(s, ":")
  50. f.Format(s, 'd')
  51. }
  52. }
  53. // StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
  54. type StackTrace []Frame
  55. // Format formats the stack of Frames according to the fmt.Formatter interface.
  56. //
  57. // %s lists source files for each Frame in the stack
  58. // %v lists the source file and line number for each Frame in the stack
  59. //
  60. // Format accepts flags that alter the printing of some verbs, as follows:
  61. //
  62. // %+v Prints filename, function, and line number for each Frame in the stack.
  63. func (st StackTrace) Format(s fmt.State, verb rune) {
  64. switch verb {
  65. case 'v':
  66. switch {
  67. case s.Flag('+'):
  68. for _, f := range st {
  69. fmt.Fprintf(s, "\n%+v", f)
  70. }
  71. case s.Flag('#'):
  72. fmt.Fprintf(s, "%#v", []Frame(st))
  73. default:
  74. fmt.Fprintf(s, "%v", []Frame(st))
  75. }
  76. case 's':
  77. fmt.Fprintf(s, "%s", []Frame(st))
  78. }
  79. }
  80. // stack represents a stack of program counters.
  81. type stack []uintptr
  82. func (s *stack) Format(st fmt.State, verb rune) {
  83. switch verb {
  84. case 'v':
  85. switch {
  86. case st.Flag('+'):
  87. frames := runtime.CallersFrames(*s)
  88. for {
  89. frame, more := frames.Next()
  90. fmt.Fprintf(st, "\n%+v", Frame(frame))
  91. if !more {
  92. break
  93. }
  94. }
  95. }
  96. }
  97. }
  98. func (s *stack) StackTrace() StackTrace {
  99. var st []Frame
  100. frames := runtime.CallersFrames(*s)
  101. for {
  102. frame, more := frames.Next()
  103. st = append(st, Frame(frame))
  104. if !more {
  105. break
  106. }
  107. }
  108. return st
  109. }
  110. func callers() *stack {
  111. const depth = 32
  112. var pcs [depth]uintptr
  113. n := runtime.Callers(3, pcs[:])
  114. var st stack = pcs[0:n]
  115. return &st
  116. }
  117. // funcname removes the path prefix component of a function's name reported by func.Name().
  118. func funcname(name string) string {
  119. i := strings.LastIndex(name, "/")
  120. name = name[i+1:]
  121. i = strings.Index(name, ".")
  122. return name[i+1:]
  123. }