stack.go 8.6 KB

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