stack.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // Copyright (c) 2023 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 stacktrace provides support for gathering stack traces
  21. // efficiently.
  22. package stacktrace
  23. import (
  24. "runtime"
  25. "go.uber.org/zap/buffer"
  26. "go.uber.org/zap/internal/bufferpool"
  27. "go.uber.org/zap/internal/pool"
  28. )
  29. var _stackPool = pool.New(func() *Stack {
  30. return &Stack{
  31. storage: make([]uintptr, 64),
  32. }
  33. })
  34. // Stack is a captured stack trace.
  35. type Stack struct {
  36. pcs []uintptr // program counters; always a subslice of storage
  37. frames *runtime.Frames
  38. // The size of pcs varies depending on requirements:
  39. // it will be one if the only the first frame was requested,
  40. // and otherwise it will reflect the depth of the call stack.
  41. //
  42. // storage decouples the slice we need (pcs) from the slice we pool.
  43. // We will always allocate a reasonably large storage, but we'll use
  44. // only as much of it as we need.
  45. storage []uintptr
  46. }
  47. // Depth specifies how deep of a stack trace should be captured.
  48. type Depth int
  49. const (
  50. // First captures only the first frame.
  51. First Depth = iota
  52. // Full captures the entire call stack, allocating more
  53. // storage for it if needed.
  54. Full
  55. )
  56. // Capture captures a stack trace of the specified depth, skipping
  57. // the provided number of frames. skip=0 identifies the caller of
  58. // Capture.
  59. //
  60. // The caller must call Free on the returned stacktrace after using it.
  61. func Capture(skip int, depth Depth) *Stack {
  62. stack := _stackPool.Get()
  63. switch depth {
  64. case First:
  65. stack.pcs = stack.storage[:1]
  66. case Full:
  67. stack.pcs = stack.storage
  68. }
  69. // Unlike other "skip"-based APIs, skip=0 identifies runtime.Callers
  70. // itself. +2 to skip captureStacktrace and runtime.Callers.
  71. numFrames := runtime.Callers(
  72. skip+2,
  73. stack.pcs,
  74. )
  75. // runtime.Callers truncates the recorded stacktrace if there is no
  76. // room in the provided slice. For the full stack trace, keep expanding
  77. // storage until there are fewer frames than there is room.
  78. if depth == Full {
  79. pcs := stack.pcs
  80. for numFrames == len(pcs) {
  81. pcs = make([]uintptr, len(pcs)*2)
  82. numFrames = runtime.Callers(skip+2, pcs)
  83. }
  84. // Discard old storage instead of returning it to the pool.
  85. // This will adjust the pool size over time if stack traces are
  86. // consistently very deep.
  87. stack.storage = pcs
  88. stack.pcs = pcs[:numFrames]
  89. } else {
  90. stack.pcs = stack.pcs[:numFrames]
  91. }
  92. stack.frames = runtime.CallersFrames(stack.pcs)
  93. return stack
  94. }
  95. // Free releases resources associated with this stacktrace
  96. // and returns it back to the pool.
  97. func (st *Stack) Free() {
  98. st.frames = nil
  99. st.pcs = nil
  100. _stackPool.Put(st)
  101. }
  102. // Count reports the total number of frames in this stacktrace.
  103. // Count DOES NOT change as Next is called.
  104. func (st *Stack) Count() int {
  105. return len(st.pcs)
  106. }
  107. // Next returns the next frame in the stack trace,
  108. // and a boolean indicating whether there are more after it.
  109. func (st *Stack) Next() (_ runtime.Frame, more bool) {
  110. return st.frames.Next()
  111. }
  112. // Take returns a string representation of the current stacktrace.
  113. //
  114. // skip is the number of frames to skip before recording the stack trace.
  115. // skip=0 identifies the caller of Take.
  116. func Take(skip int) string {
  117. stack := Capture(skip+1, Full)
  118. defer stack.Free()
  119. buffer := bufferpool.Get()
  120. defer buffer.Free()
  121. stackfmt := NewFormatter(buffer)
  122. stackfmt.FormatStack(stack)
  123. return buffer.String()
  124. }
  125. // Formatter formats a stack trace into a readable string representation.
  126. type Formatter struct {
  127. b *buffer.Buffer
  128. nonEmpty bool // whehther we've written at least one frame already
  129. }
  130. // NewFormatter builds a new Formatter.
  131. func NewFormatter(b *buffer.Buffer) Formatter {
  132. return Formatter{b: b}
  133. }
  134. // FormatStack formats all remaining frames in the provided stacktrace -- minus
  135. // the final runtime.main/runtime.goexit frame.
  136. func (sf *Formatter) FormatStack(stack *Stack) {
  137. // Note: On the last iteration, frames.Next() returns false, with a valid
  138. // frame, but we ignore this frame. The last frame is a runtime frame which
  139. // adds noise, since it's only either runtime.main or runtime.goexit.
  140. for frame, more := stack.Next(); more; frame, more = stack.Next() {
  141. sf.FormatFrame(frame)
  142. }
  143. }
  144. // FormatFrame formats the given frame.
  145. func (sf *Formatter) FormatFrame(frame runtime.Frame) {
  146. if sf.nonEmpty {
  147. sf.b.AppendByte('\n')
  148. }
  149. sf.nonEmpty = true
  150. sf.b.AppendString(frame.Function)
  151. sf.b.AppendByte('\n')
  152. sf.b.AppendByte('\t')
  153. sf.b.AppendString(frame.File)
  154. sf.b.AppendByte(':')
  155. sf.b.AppendInt(int64(frame.Line))
  156. }