stack.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // Copyright (c) 2019 Uber Technologies, Inc.
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE.
  20. package xreflect
  21. import (
  22. "fmt"
  23. "io"
  24. "runtime"
  25. "strings"
  26. )
  27. // Frame holds information about a single frame in the call stack.
  28. type Frame struct {
  29. // Unique, package path-qualified name for the function of this call
  30. // frame.
  31. Function string
  32. // File and line number of our location in the frame.
  33. //
  34. // Note that the line number does not refer to where the function was
  35. // defined but where in the function the next call was made.
  36. File string
  37. Line int
  38. }
  39. func (f Frame) String() string {
  40. // This takes the following forms.
  41. // (path/to/file.go)
  42. // (path/to/file.go:42)
  43. // path/to/package.MyFunction
  44. // path/to/package.MyFunction (path/to/file.go)
  45. // path/to/package.MyFunction (path/to/file.go:42)
  46. var sb strings.Builder
  47. sb.WriteString(f.Function)
  48. if len(f.File) > 0 {
  49. if sb.Len() > 0 {
  50. sb.WriteRune(' ')
  51. }
  52. fmt.Fprintf(&sb, "(%v", f.File)
  53. if f.Line > 0 {
  54. fmt.Fprintf(&sb, ":%d", f.Line)
  55. }
  56. sb.WriteRune(')')
  57. }
  58. if sb.Len() == 0 {
  59. return "unknown"
  60. }
  61. return sb.String()
  62. }
  63. const _defaultCallersDepth = 8
  64. // Stack is a stack of call frames.
  65. //
  66. // Formatted with %v, the output is in a single-line, in the form,
  67. //
  68. // foo/bar.Baz() (path/to/foo.go:42); bar/baz.Qux() (bar/baz/qux.go:12); ...
  69. //
  70. // Formatted with %+v, the output is in the form,
  71. //
  72. // foo/bar.Baz()
  73. // path/to/foo.go:42
  74. // bar/baz.Qux()
  75. // bar/baz/qux.go:12
  76. type Stack []Frame
  77. // Returns a single-line, semi-colon representation of a Stack. For a
  78. // multi-line representation, use %+v.
  79. func (fs Stack) String() string {
  80. items := make([]string, len(fs))
  81. for i, f := range fs {
  82. items[i] = f.String()
  83. }
  84. return strings.Join(items, "; ")
  85. }
  86. // Format implements fmt.Formatter to handle "%+v".
  87. func (fs Stack) Format(w fmt.State, c rune) {
  88. if !w.Flag('+') {
  89. // Without %+v, fall back to String().
  90. io.WriteString(w, fs.String())
  91. return
  92. }
  93. for _, f := range fs {
  94. fmt.Fprintln(w, f.Function)
  95. fmt.Fprintf(w, "\t%v:%v\n", f.File, f.Line)
  96. }
  97. }
  98. // CallerName returns the name of the first caller in this stack that isn't
  99. // owned by the di library.
  100. func (fs Stack) CallerName() string {
  101. for _, f := range fs {
  102. if shouldIgnoreFrame(f) {
  103. continue
  104. }
  105. return f.Function
  106. }
  107. return "n/a"
  108. }
  109. // CallerStack returns the call stack for the calling function, up to depth frames
  110. // deep, skipping the provided number of frames, not including Callers itself.
  111. //
  112. // If zero, depth defaults to 8.
  113. func CallerStack(skip, depth int) Stack {
  114. if depth <= 0 {
  115. depth = _defaultCallersDepth
  116. }
  117. pcs := make([]uintptr, depth)
  118. // +2 to skip this frame and runtime.Callers.
  119. n := runtime.Callers(skip+2, pcs)
  120. pcs = pcs[:n] // truncate to number of frames actually read
  121. result := make([]Frame, 0, n)
  122. frames := runtime.CallersFrames(pcs)
  123. for f, more := frames.Next(); more; f, more = frames.Next() {
  124. result = append(result, Frame{
  125. Function: sanitize(f.Function),
  126. File: f.File,
  127. Line: f.Line,
  128. })
  129. }
  130. return result
  131. }