You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

147 lines
3.3 KiB

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