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.

324 lines
8.7 KiB

  1. // +build go1.7
  2. // Package stack implements utilities to capture, manipulate, and format call
  3. // stacks. It provides a simpler API than package runtime.
  4. //
  5. // The implementation takes care of the minutia and special cases of
  6. // interpreting the program counter (pc) values returned by runtime.Callers.
  7. //
  8. // Package stack's types implement fmt.Formatter, which provides a simple and
  9. // flexible way to declaratively configure formatting when used with logging
  10. // or error tracking packages.
  11. package stack
  12. import (
  13. "bytes"
  14. "errors"
  15. "fmt"
  16. "io"
  17. "runtime"
  18. "strconv"
  19. "strings"
  20. )
  21. // Call records a single function invocation from a goroutine stack.
  22. type Call struct {
  23. frame runtime.Frame
  24. }
  25. // Caller returns a Call from the stack of the current goroutine. The argument
  26. // skip is the number of stack frames to ascend, with 0 identifying the
  27. // calling function.
  28. func Caller(skip int) Call {
  29. // As of Go 1.9 we need room for up to three PC entries.
  30. //
  31. // 0. An entry for the stack frame prior to the target to check for
  32. // special handling needed if that prior entry is runtime.sigpanic.
  33. // 1. A possible second entry to hold metadata about skipped inlined
  34. // functions. If inline functions were not skipped the target frame
  35. // PC will be here.
  36. // 2. A third entry for the target frame PC when the second entry
  37. // is used for skipped inline functions.
  38. var pcs [3]uintptr
  39. n := runtime.Callers(skip+1, pcs[:])
  40. frames := runtime.CallersFrames(pcs[:n])
  41. frame, _ := frames.Next()
  42. frame, _ = frames.Next()
  43. return Call{
  44. frame: frame,
  45. }
  46. }
  47. // String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c).
  48. func (c Call) String() string {
  49. return fmt.Sprint(c)
  50. }
  51. // MarshalText implements encoding.TextMarshaler. It formats the Call the same
  52. // as fmt.Sprintf("%v", c).
  53. func (c Call) MarshalText() ([]byte, error) {
  54. if c.frame == (runtime.Frame{}) {
  55. return nil, ErrNoFunc
  56. }
  57. buf := bytes.Buffer{}
  58. fmt.Fprint(&buf, c)
  59. return buf.Bytes(), nil
  60. }
  61. // ErrNoFunc means that the Call has a nil *runtime.Func. The most likely
  62. // cause is a Call with the zero value.
  63. var ErrNoFunc = errors.New("no call stack information")
  64. // Format implements fmt.Formatter with support for the following verbs.
  65. //
  66. // %s source file
  67. // %d line number
  68. // %n function name
  69. // %k last segment of the package path
  70. // %v equivalent to %s:%d
  71. //
  72. // It accepts the '+' and '#' flags for most of the verbs as follows.
  73. //
  74. // %+s path of source file relative to the compile time GOPATH
  75. // %#s full path of source file
  76. // %+n import path qualified function name
  77. // %+k full package path
  78. // %+v equivalent to %+s:%d
  79. // %#v equivalent to %#s:%d
  80. func (c Call) Format(s fmt.State, verb rune) {
  81. if c.frame == (runtime.Frame{}) {
  82. fmt.Fprintf(s, "%%!%c(NOFUNC)", verb)
  83. return
  84. }
  85. switch verb {
  86. case 's', 'v':
  87. file := c.frame.File
  88. switch {
  89. case s.Flag('#'):
  90. // done
  91. case s.Flag('+'):
  92. file = file[pkgIndex(file, c.frame.Function):]
  93. default:
  94. const sep = "/"
  95. if i := strings.LastIndex(file, sep); i != -1 {
  96. file = file[i+len(sep):]
  97. }
  98. }
  99. io.WriteString(s, file)
  100. if verb == 'v' {
  101. buf := [7]byte{':'}
  102. s.Write(strconv.AppendInt(buf[:1], int64(c.frame.Line), 10))
  103. }
  104. case 'd':
  105. buf := [6]byte{}
  106. s.Write(strconv.AppendInt(buf[:0], int64(c.frame.Line), 10))
  107. case 'k':
  108. name := c.frame.Function
  109. const pathSep = "/"
  110. start, end := 0, len(name)
  111. if i := strings.LastIndex(name, pathSep); i != -1 {
  112. start = i + len(pathSep)
  113. }
  114. const pkgSep = "."
  115. if i := strings.Index(name[start:], pkgSep); i != -1 {
  116. end = start + i
  117. }
  118. if s.Flag('+') {
  119. start = 0
  120. }
  121. io.WriteString(s, name[start:end])
  122. case 'n':
  123. name := c.frame.Function
  124. if !s.Flag('+') {
  125. const pathSep = "/"
  126. if i := strings.LastIndex(name, pathSep); i != -1 {
  127. name = name[i+len(pathSep):]
  128. }
  129. const pkgSep = "."
  130. if i := strings.Index(name, pkgSep); i != -1 {
  131. name = name[i+len(pkgSep):]
  132. }
  133. }
  134. io.WriteString(s, name)
  135. }
  136. }
  137. // Frame returns the call frame infomation for the Call.
  138. func (c Call) Frame() runtime.Frame {
  139. return c.frame
  140. }
  141. // PC returns the program counter for this call frame; multiple frames may
  142. // have the same PC value.
  143. //
  144. // Deprecated: Use Call.Frame instead.
  145. func (c Call) PC() uintptr {
  146. return c.frame.PC
  147. }
  148. // CallStack records a sequence of function invocations from a goroutine
  149. // stack.
  150. type CallStack []Call
  151. // String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", cs).
  152. func (cs CallStack) String() string {
  153. return fmt.Sprint(cs)
  154. }
  155. var (
  156. openBracketBytes = []byte("[")
  157. closeBracketBytes = []byte("]")
  158. spaceBytes = []byte(" ")
  159. )
  160. // MarshalText implements encoding.TextMarshaler. It formats the CallStack the
  161. // same as fmt.Sprintf("%v", cs).
  162. func (cs CallStack) MarshalText() ([]byte, error) {
  163. buf := bytes.Buffer{}
  164. buf.Write(openBracketBytes)
  165. for i, pc := range cs {
  166. if i > 0 {
  167. buf.Write(spaceBytes)
  168. }
  169. fmt.Fprint(&buf, pc)
  170. }
  171. buf.Write(closeBracketBytes)
  172. return buf.Bytes(), nil
  173. }
  174. // Format implements fmt.Formatter by printing the CallStack as square brackets
  175. // ([, ]) surrounding a space separated list of Calls each formatted with the
  176. // supplied verb and options.
  177. func (cs CallStack) Format(s fmt.State, verb rune) {
  178. s.Write(openBracketBytes)
  179. for i, pc := range cs {
  180. if i > 0 {
  181. s.Write(spaceBytes)
  182. }
  183. pc.Format(s, verb)
  184. }
  185. s.Write(closeBracketBytes)
  186. }
  187. // Trace returns a CallStack for the current goroutine with element 0
  188. // identifying the calling function.
  189. func Trace() CallStack {
  190. var pcs [512]uintptr
  191. n := runtime.Callers(1, pcs[:])
  192. frames := runtime.CallersFrames(pcs[:n])
  193. cs := make(CallStack, 0, n)
  194. // Skip extra frame retrieved just to make sure the runtime.sigpanic
  195. // special case is handled.
  196. frame, more := frames.Next()
  197. for more {
  198. frame, more = frames.Next()
  199. cs = append(cs, Call{frame: frame})
  200. }
  201. return cs
  202. }
  203. // TrimBelow returns a slice of the CallStack with all entries below c
  204. // removed.
  205. func (cs CallStack) TrimBelow(c Call) CallStack {
  206. for len(cs) > 0 && cs[0] != c {
  207. cs = cs[1:]
  208. }
  209. return cs
  210. }
  211. // TrimAbove returns a slice of the CallStack with all entries above c
  212. // removed.
  213. func (cs CallStack) TrimAbove(c Call) CallStack {
  214. for len(cs) > 0 && cs[len(cs)-1] != c {
  215. cs = cs[:len(cs)-1]
  216. }
  217. return cs
  218. }
  219. // pkgIndex returns the index that results in file[index:] being the path of
  220. // file relative to the compile time GOPATH, and file[:index] being the
  221. // $GOPATH/src/ portion of file. funcName must be the name of a function in
  222. // file as returned by runtime.Func.Name.
  223. func pkgIndex(file, funcName string) int {
  224. // As of Go 1.6.2 there is no direct way to know the compile time GOPATH
  225. // at runtime, but we can infer the number of path segments in the GOPATH.
  226. // We note that runtime.Func.Name() returns the function name qualified by
  227. // the import path, which does not include the GOPATH. Thus we can trim
  228. // segments from the beginning of the file path until the number of path
  229. // separators remaining is one more than the number of path separators in
  230. // the function name. For example, given:
  231. //
  232. // GOPATH /home/user
  233. // file /home/user/src/pkg/sub/file.go
  234. // fn.Name() pkg/sub.Type.Method
  235. //
  236. // We want to produce:
  237. //
  238. // file[:idx] == /home/user/src/
  239. // file[idx:] == pkg/sub/file.go
  240. //
  241. // From this we can easily see that fn.Name() has one less path separator
  242. // than our desired result for file[idx:]. We count separators from the
  243. // end of the file path until it finds two more than in the function name
  244. // and then move one character forward to preserve the initial path
  245. // segment without a leading separator.
  246. const sep = "/"
  247. i := len(file)
  248. for n := strings.Count(funcName, sep) + 2; n > 0; n-- {
  249. i = strings.LastIndex(file[:i], sep)
  250. if i == -1 {
  251. i = -len(sep)
  252. break
  253. }
  254. }
  255. // get back to 0 or trim the leading separator
  256. return i + len(sep)
  257. }
  258. var runtimePath string
  259. func init() {
  260. var pcs [3]uintptr
  261. runtime.Callers(0, pcs[:])
  262. frames := runtime.CallersFrames(pcs[:])
  263. frame, _ := frames.Next()
  264. file := frame.File
  265. idx := pkgIndex(frame.File, frame.Function)
  266. runtimePath = file[:idx]
  267. if runtime.GOOS == "windows" {
  268. runtimePath = strings.ToLower(runtimePath)
  269. }
  270. }
  271. func inGoroot(c Call) bool {
  272. file := c.frame.File
  273. if len(file) == 0 || file[0] == '?' {
  274. return true
  275. }
  276. if runtime.GOOS == "windows" {
  277. file = strings.ToLower(file)
  278. }
  279. return strings.HasPrefix(file, runtimePath) || strings.HasSuffix(file, "/_testmain.go")
  280. }
  281. // TrimRuntime returns a slice of the CallStack with the topmost entries from
  282. // the go runtime removed. It considers any calls originating from unknown
  283. // files, files under GOROOT, or _testmain.go as part of the runtime.
  284. func (cs CallStack) TrimRuntime() CallStack {
  285. for len(cs) > 0 && inGoroot(cs[len(cs)-1]) {
  286. cs = cs[:len(cs)-1]
  287. }
  288. return cs
  289. }