stack.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // Fork from https://github.com/pkg/errors
  2. package errors
  3. import (
  4. "fmt"
  5. "io"
  6. "path"
  7. "runtime"
  8. "strings"
  9. )
  10. // Frame represents a program counter inside a stack frame.
  11. type Frame uintptr
  12. // pc returns the program counter for this frame;
  13. // multiple frames may have the same PC value.
  14. func (f Frame) pc() uintptr { return uintptr(f) - 1 }
  15. // file returns the full path to the file that contains the
  16. // function for this Frame's pc.
  17. func (f Frame) file() string {
  18. fn := runtime.FuncForPC(f.pc())
  19. if fn == nil {
  20. return "unknown"
  21. }
  22. file, _ := fn.FileLine(f.pc())
  23. return file
  24. }
  25. // line returns the line number of source code of the
  26. // function for this Frame's pc.
  27. func (f Frame) line() int {
  28. fn := runtime.FuncForPC(f.pc())
  29. if fn == nil {
  30. return 0
  31. }
  32. _, line := fn.FileLine(f.pc())
  33. return line
  34. }
  35. // Format formats the frame according to the fmt.Formatter interface.
  36. //
  37. // %s source file
  38. // %d source line
  39. // %n function name
  40. // %v equivalent to %s:%d
  41. //
  42. // Format accepts flags that alter the printing of some verbs, as follows:
  43. //
  44. // %+s path of source file relative to the compile time GOPATH
  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. }
  135. func trimGOPATH(name, file string) string {
  136. // Here we want to get the source file path relative to the compile time
  137. // GOPATH. As of Go 1.6.x there is no direct way to know the compiled
  138. // GOPATH at runtime, but we can infer the number of path segments in the
  139. // GOPATH. We note that fn.Name() returns the function name qualified by
  140. // the import path, which does not include the GOPATH. Thus we can trim
  141. // segments from the beginning of the file path until the number of path
  142. // separators remaining is one more than the number of path separators in
  143. // the function name. For example, given:
  144. //
  145. // GOPATH /home/user
  146. // file /home/user/src/pkg/sub/file.go
  147. // fn.Name() pkg/sub.Type.Method
  148. //
  149. // We want to produce:
  150. //
  151. // pkg/sub/file.go
  152. //
  153. // From this we can easily see that fn.Name() has one less path separator
  154. // than our desired output. We count separators from the end of the file
  155. // path until it finds two more than in the function name and then move
  156. // one character forward to preserve the initial path segment without a
  157. // leading separator.
  158. const sep = "/"
  159. goal := strings.Count(name, sep) + 2
  160. i := len(file)
  161. for n := 0; n < goal; n++ {
  162. i = strings.LastIndex(file[:i], sep)
  163. if i == -1 {
  164. // not enough separators found, set i so that the slice expression
  165. // below leaves file unmodified
  166. i = -len(sep)
  167. break
  168. }
  169. }
  170. // get back to 0 or trim the leading separator
  171. file = file[i+len(sep):]
  172. return file
  173. }