stacktrace.go 5.4 KB

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