stack.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package xerr
  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. return callersWithSkip(4)
  123. }
  124. func callersWithSkip(skip int) *stack {
  125. const depth = 32
  126. var pcs [depth]uintptr
  127. n := runtime.Callers(skip, pcs[:])
  128. var st stack = pcs[0:n]
  129. return &st
  130. }
  131. func callersWithErr(err error) *stack {
  132. if stackErr, ok := err.(interface{ Stack() *stack }); ok {
  133. return stackErr.Stack()
  134. }
  135. return callersWithSkip(4)
  136. }
  137. // funcname removes the path prefix component of a function's name reported by func.Name().
  138. func funcname(name string) string {
  139. i := strings.LastIndex(name, "/")
  140. name = name[i+1:]
  141. i = strings.Index(name, ".")
  142. return name[i+1:]
  143. }