stack.go 3.2 KB

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