stack.go 3.3 KB

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