Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  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. // or the module path joined to the path of source file relative
  76. // to module root
  77. // %#s full path of source file
  78. // %+n import path qualified function name
  79. // %+k full package path
  80. // %+v equivalent to %+s:%d
  81. // %#v equivalent to %#s:%d
  82. func (c Call) Format(s fmt.State, verb rune) {
  83. if c.frame == (runtime.Frame{}) {
  84. fmt.Fprintf(s, "%%!%c(NOFUNC)", verb)
  85. return
  86. }
  87. switch verb {
  88. case 's', 'v':
  89. file := c.frame.File
  90. switch {
  91. case s.Flag('#'):
  92. // done
  93. case s.Flag('+'):
  94. file = pkgFilePath(&c.frame)
  95. default:
  96. const sep = "/"
  97. if i := strings.LastIndex(file, sep); i != -1 {
  98. file = file[i+len(sep):]
  99. }
  100. }
  101. io.WriteString(s, file)
  102. if verb == 'v' {
  103. buf := [7]byte{':'}
  104. s.Write(strconv.AppendInt(buf[:1], int64(c.frame.Line), 10))
  105. }
  106. case 'd':
  107. buf := [6]byte{}
  108. s.Write(strconv.AppendInt(buf[:0], int64(c.frame.Line), 10))
  109. case 'k':
  110. name := c.frame.Function
  111. const pathSep = "/"
  112. start, end := 0, len(name)
  113. if i := strings.LastIndex(name, pathSep); i != -1 {
  114. start = i + len(pathSep)
  115. }
  116. const pkgSep = "."
  117. if i := strings.Index(name[start:], pkgSep); i != -1 {
  118. end = start + i
  119. }
  120. if s.Flag('+') {
  121. start = 0
  122. }
  123. io.WriteString(s, name[start:end])
  124. case 'n':
  125. name := c.frame.Function
  126. if !s.Flag('+') {
  127. const pathSep = "/"
  128. if i := strings.LastIndex(name, pathSep); i != -1 {
  129. name = name[i+len(pathSep):]
  130. }
  131. const pkgSep = "."
  132. if i := strings.Index(name, pkgSep); i != -1 {
  133. name = name[i+len(pkgSep):]
  134. }
  135. }
  136. io.WriteString(s, name)
  137. }
  138. }
  139. // Frame returns the call frame infomation for the Call.
  140. func (c Call) Frame() runtime.Frame {
  141. return c.frame
  142. }
  143. // PC returns the program counter for this call frame; multiple frames may
  144. // have the same PC value.
  145. //
  146. // Deprecated: Use Call.Frame instead.
  147. func (c Call) PC() uintptr {
  148. return c.frame.PC
  149. }
  150. // CallStack records a sequence of function invocations from a goroutine
  151. // stack.
  152. type CallStack []Call
  153. // String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", cs).
  154. func (cs CallStack) String() string {
  155. return fmt.Sprint(cs)
  156. }
  157. var (
  158. openBracketBytes = []byte("[")
  159. closeBracketBytes = []byte("]")
  160. spaceBytes = []byte(" ")
  161. )
  162. // MarshalText implements encoding.TextMarshaler. It formats the CallStack the
  163. // same as fmt.Sprintf("%v", cs).
  164. func (cs CallStack) MarshalText() ([]byte, error) {
  165. buf := bytes.Buffer{}
  166. buf.Write(openBracketBytes)
  167. for i, pc := range cs {
  168. if i > 0 {
  169. buf.Write(spaceBytes)
  170. }
  171. fmt.Fprint(&buf, pc)
  172. }
  173. buf.Write(closeBracketBytes)
  174. return buf.Bytes(), nil
  175. }
  176. // Format implements fmt.Formatter by printing the CallStack as square brackets
  177. // ([, ]) surrounding a space separated list of Calls each formatted with the
  178. // supplied verb and options.
  179. func (cs CallStack) Format(s fmt.State, verb rune) {
  180. s.Write(openBracketBytes)
  181. for i, pc := range cs {
  182. if i > 0 {
  183. s.Write(spaceBytes)
  184. }
  185. pc.Format(s, verb)
  186. }
  187. s.Write(closeBracketBytes)
  188. }
  189. // Trace returns a CallStack for the current goroutine with element 0
  190. // identifying the calling function.
  191. func Trace() CallStack {
  192. var pcs [512]uintptr
  193. n := runtime.Callers(1, pcs[:])
  194. frames := runtime.CallersFrames(pcs[:n])
  195. cs := make(CallStack, 0, n)
  196. // Skip extra frame retrieved just to make sure the runtime.sigpanic
  197. // special case is handled.
  198. frame, more := frames.Next()
  199. for more {
  200. frame, more = frames.Next()
  201. cs = append(cs, Call{frame: frame})
  202. }
  203. return cs
  204. }
  205. // TrimBelow returns a slice of the CallStack with all entries below c
  206. // removed.
  207. func (cs CallStack) TrimBelow(c Call) CallStack {
  208. for len(cs) > 0 && cs[0] != c {
  209. cs = cs[1:]
  210. }
  211. return cs
  212. }
  213. // TrimAbove returns a slice of the CallStack with all entries above c
  214. // removed.
  215. func (cs CallStack) TrimAbove(c Call) CallStack {
  216. for len(cs) > 0 && cs[len(cs)-1] != c {
  217. cs = cs[:len(cs)-1]
  218. }
  219. return cs
  220. }
  221. // pkgIndex returns the index that results in file[index:] being the path of
  222. // file relative to the compile time GOPATH, and file[:index] being the
  223. // $GOPATH/src/ portion of file. funcName must be the name of a function in
  224. // file as returned by runtime.Func.Name.
  225. func pkgIndex(file, funcName string) int {
  226. // As of Go 1.6.2 there is no direct way to know the compile time GOPATH
  227. // at runtime, but we can infer the number of path segments in the GOPATH.
  228. // We note that runtime.Func.Name() returns the function name qualified by
  229. // the import path, which does not include the GOPATH. Thus we can trim
  230. // segments from the beginning of the file path until the number of path
  231. // separators remaining is one more than the number of path separators in
  232. // the function name. For example, given:
  233. //
  234. // GOPATH /home/user
  235. // file /home/user/src/pkg/sub/file.go
  236. // fn.Name() pkg/sub.Type.Method
  237. //
  238. // We want to produce:
  239. //
  240. // file[:idx] == /home/user/src/
  241. // file[idx:] == pkg/sub/file.go
  242. //
  243. // From this we can easily see that fn.Name() has one less path separator
  244. // than our desired result for file[idx:]. We count separators from the
  245. // end of the file path until it finds two more than in the function name
  246. // and then move one character forward to preserve the initial path
  247. // segment without a leading separator.
  248. const sep = "/"
  249. i := len(file)
  250. for n := strings.Count(funcName, sep) + 2; n > 0; n-- {
  251. i = strings.LastIndex(file[:i], sep)
  252. if i == -1 {
  253. i = -len(sep)
  254. break
  255. }
  256. }
  257. // get back to 0 or trim the leading separator
  258. return i + len(sep)
  259. }
  260. // pkgFilePath returns the frame's filepath relative to the compile-time GOPATH,
  261. // or its module path joined to its path relative to the module root.
  262. //
  263. // As of Go 1.11 there is no direct way to know the compile time GOPATH or
  264. // module paths at runtime, but we can piece together the desired information
  265. // from available information. We note that runtime.Frame.Function contains the
  266. // function name qualified by the package path, which includes the module path
  267. // but not the GOPATH. We can extract the package path from that and append the
  268. // last segments of the file path to arrive at the desired package qualified
  269. // file path. For example, given:
  270. //
  271. // GOPATH /home/user
  272. // import path pkg/sub
  273. // frame.File /home/user/src/pkg/sub/file.go
  274. // frame.Function pkg/sub.Type.Method
  275. // Desired return pkg/sub/file.go
  276. //
  277. // It appears that we simply need to trim ".Type.Method" from frame.Function and
  278. // append "/" + path.Base(file).
  279. //
  280. // But there are other wrinkles. Although it is idiomatic to do so, the internal
  281. // name of a package is not required to match the last segment of its import
  282. // path. In addition, the introduction of modules in Go 1.11 allows working
  283. // without a GOPATH. So we also must make these work right:
  284. //
  285. // GOPATH /home/user
  286. // import path pkg/go-sub
  287. // package name sub
  288. // frame.File /home/user/src/pkg/go-sub/file.go
  289. // frame.Function pkg/sub.Type.Method
  290. // Desired return pkg/go-sub/file.go
  291. //
  292. // Module path pkg/v2
  293. // import path pkg/v2/go-sub
  294. // package name sub
  295. // frame.File /home/user/cloned-pkg/go-sub/file.go
  296. // frame.Function pkg/v2/sub.Type.Method
  297. // Desired return pkg/v2/go-sub/file.go
  298. //
  299. // We can handle all of these situations by using the package path extracted
  300. // from frame.Function up to, but not including, the last segment as the prefix
  301. // and the last two segments of frame.File as the suffix of the returned path.
  302. // This preserves the existing behavior when working in a GOPATH without modules
  303. // and a semantically equivalent behavior when used in module aware project.
  304. func pkgFilePath(frame *runtime.Frame) string {
  305. pre := pkgPrefix(frame.Function)
  306. post := pathSuffix(frame.File)
  307. if pre == "" {
  308. return post
  309. }
  310. return pre + "/" + post
  311. }
  312. // pkgPrefix returns the import path of the function's package with the final
  313. // segment removed.
  314. func pkgPrefix(funcName string) string {
  315. const pathSep = "/"
  316. end := strings.LastIndex(funcName, pathSep)
  317. if end == -1 {
  318. return ""
  319. }
  320. return funcName[:end]
  321. }
  322. // pathSuffix returns the last two segments of path.
  323. func pathSuffix(path string) string {
  324. const pathSep = "/"
  325. lastSep := strings.LastIndex(path, pathSep)
  326. if lastSep == -1 {
  327. return path
  328. }
  329. return path[strings.LastIndex(path[:lastSep], pathSep)+1:]
  330. }
  331. var runtimePath string
  332. func init() {
  333. var pcs [3]uintptr
  334. runtime.Callers(0, pcs[:])
  335. frames := runtime.CallersFrames(pcs[:])
  336. frame, _ := frames.Next()
  337. file := frame.File
  338. idx := pkgIndex(frame.File, frame.Function)
  339. runtimePath = file[:idx]
  340. if runtime.GOOS == "windows" {
  341. runtimePath = strings.ToLower(runtimePath)
  342. }
  343. }
  344. func inGoroot(c Call) bool {
  345. file := c.frame.File
  346. if len(file) == 0 || file[0] == '?' {
  347. return true
  348. }
  349. if runtime.GOOS == "windows" {
  350. file = strings.ToLower(file)
  351. }
  352. return strings.HasPrefix(file, runtimePath) || strings.HasSuffix(file, "/_testmain.go")
  353. }
  354. // TrimRuntime returns a slice of the CallStack with the topmost entries from
  355. // the go runtime removed. It considers any calls originating from unknown
  356. // files, files under GOROOT, or _testmain.go as part of the runtime.
  357. func (cs CallStack) TrimRuntime() CallStack {
  358. for len(cs) > 0 && inGoroot(cs[len(cs)-1]) {
  359. cs = cs[:len(cs)-1]
  360. }
  361. return cs
  362. }