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