stack.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  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 path of source file relative to the compile time GOPATH
  44. // %+v equivalent to %+s:%d
  45. func (f Frame) Format(s fmt.State, verb rune) {
  46. switch verb {
  47. case 's':
  48. switch {
  49. case s.Flag('+'):
  50. pc := f.pc()
  51. fn := runtime.FuncForPC(pc)
  52. if fn == nil {
  53. io.WriteString(s, "unknown")
  54. } else {
  55. file, _ := fn.FileLine(pc)
  56. fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
  57. }
  58. default:
  59. io.WriteString(s, path.Base(f.file()))
  60. }
  61. case 'd':
  62. fmt.Fprintf(s, "%d", f.line())
  63. case 'n':
  64. name := runtime.FuncForPC(f.pc()).Name()
  65. io.WriteString(s, funcname(name))
  66. case 'v':
  67. f.Format(s, 's')
  68. io.WriteString(s, ":")
  69. f.Format(s, 'd')
  70. }
  71. }
  72. // StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
  73. type StackTrace []Frame
  74. // Format formats the stack of Frames according to the fmt.Formatter interface.
  75. //
  76. // %s lists source files for each Frame in the stack
  77. // %v lists the source file and line number for each Frame in the stack
  78. //
  79. // Format accepts flags that alter the printing of some verbs, as follows:
  80. //
  81. // %+v Prints filename, function, and line number for each Frame in the stack.
  82. func (st StackTrace) Format(s fmt.State, verb rune) {
  83. switch verb {
  84. case 'v':
  85. switch {
  86. case s.Flag('+'):
  87. for _, f := range st {
  88. fmt.Fprintf(s, "\n%+v", f)
  89. }
  90. case s.Flag('#'):
  91. fmt.Fprintf(s, "%#v", []Frame(st))
  92. default:
  93. fmt.Fprintf(s, "%v", []Frame(st))
  94. }
  95. case 's':
  96. fmt.Fprintf(s, "%s", []Frame(st))
  97. }
  98. }
  99. // stack represents a stack of program counters.
  100. type stack []uintptr
  101. func (s *stack) Format(st fmt.State, verb rune) {
  102. switch verb {
  103. case 'v':
  104. switch {
  105. case st.Flag('+'):
  106. for _, pc := range *s {
  107. f := Frame(pc)
  108. fmt.Fprintf(st, "\n%+v", f)
  109. }
  110. }
  111. }
  112. }
  113. func (s *stack) StackTrace() StackTrace {
  114. f := make([]Frame, len(*s))
  115. for i := 0; i < len(f); i++ {
  116. f[i] = Frame((*s)[i])
  117. }
  118. return f
  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. }
  134. func trimGOPATH(name, file string) string {
  135. // Here we want to get the source file path relative to the compile time
  136. // GOPATH. As of Go 1.6.x there is no direct way to know the compiled
  137. // GOPATH at runtime, but we can infer the number of path segments in the
  138. // GOPATH. We note that fn.Name() returns the function name qualified by
  139. // the import path, which does not include the GOPATH. Thus we can trim
  140. // segments from the beginning of the file path until the number of path
  141. // separators remaining is one more than the number of path separators in
  142. // the function name. For example, given:
  143. //
  144. // GOPATH /home/user
  145. // file /home/user/src/pkg/sub/file.go
  146. // fn.Name() pkg/sub.Type.Method
  147. //
  148. // We want to produce:
  149. //
  150. // pkg/sub/file.go
  151. //
  152. // From this we can easily see that fn.Name() has one less path separator
  153. // than our desired output. We count separators from the end of the file
  154. // path until it finds two more than in the function name and then move
  155. // one character forward to preserve the initial path segment without a
  156. // leading separator.
  157. const sep = "/"
  158. goal := strings.Count(name, sep) + 2
  159. i := len(file)
  160. for n := 0; n < goal; n++ {
  161. i = strings.LastIndex(file[:i], sep)
  162. if i == -1 {
  163. // not enough separators found, set i so that the slice expression
  164. // below leaves file unmodified
  165. i = -len(sep)
  166. break
  167. }
  168. }
  169. // get back to 0 or trim the leading separator
  170. file = file[i+len(sep):]
  171. return file
  172. }