Source file
src/runtime/tracestack.go
1
2
3
4
5
6
7 package runtime
8
9 import (
10 "internal/abi"
11 "internal/goarch"
12 "internal/trace/tracev2"
13 "unsafe"
14 )
15
16 const (
17
18
19
20
21 logicalStackSentinel = ^uintptr(0)
22 )
23
24
25
26
27
28
29
30
31
32
33
34 func traceStack(skip int, gp *g, gen uintptr) uint64 {
35 var pcBuf [tracev2.MaxFramesPerStack]uintptr
36
37
38 var mp *m
39 if gp == nil {
40 mp = getg().m
41 gp = mp.curg
42 }
43
44
45 if debug.traceCheckStackOwnership != 0 && gp != nil {
46 status := readgstatus(gp)
47
48 if status&_Gscan == 0 {
49
50
51
52
53 switch goStatusToTraceGoStatus(status, gp.waitreason) {
54 case tracev2.GoRunning, tracev2.GoSyscall:
55 if getg() == gp || mp.curg == gp {
56 break
57 }
58 fallthrough
59 default:
60 print("runtime: gp=", unsafe.Pointer(gp), " gp.goid=", gp.goid, " status=", gStatusStrings[status], "\n")
61 throw("attempted to trace stack of a goroutine this thread does not own")
62 }
63 }
64 }
65
66 if gp != nil && mp == nil {
67
68
69 mp = gp.lockedm.ptr()
70 }
71 nstk := 1
72 if tracefpunwindoff() || (mp != nil && mp.hasCgoOnStack()) {
73
74
75
76
77
78
79 pcBuf[0] = logicalStackSentinel
80 if getg() == gp {
81 nstk += callers(skip+1, pcBuf[1:])
82 } else if gp != nil {
83 nstk += gcallers(gp, skip, pcBuf[1:])
84 }
85 } else {
86
87 pcBuf[0] = uintptr(skip)
88 if getg() == gp {
89 nstk += fpTracebackPCs(unsafe.Pointer(getfp()), pcBuf[1:])
90 } else if gp != nil {
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 if gp.syscallsp != 0 {
108 pcBuf[1] = gp.syscallpc
109 nstk += 1 + fpTracebackPCs(unsafe.Pointer(gp.syscallbp), pcBuf[2:])
110 } else {
111 pcBuf[1] = gp.sched.pc
112 if gp.syncSafePoint {
113
114
115
116
117
118
119 if usesLR {
120 pcBuf[2] = gp.sched.lr
121 } else {
122 pcBuf[2] = *(*uintptr)(unsafe.Pointer(gp.sched.sp))
123 }
124 nstk += 2 + fpTracebackPCs(unsafe.Pointer(gp.sched.bp), pcBuf[3:])
125 } else {
126 nstk += 1 + fpTracebackPCs(unsafe.Pointer(gp.sched.bp), pcBuf[2:])
127 }
128 }
129 }
130 }
131 if nstk > 0 {
132 nstk--
133 }
134 if nstk > 0 && gp.goid == 1 {
135 nstk--
136 }
137 id := trace.stackTab[gen%2].put(pcBuf[:nstk])
138 return id
139 }
140
141
142
143 type traceStackTable struct {
144 tab traceMap
145 }
146
147
148
149 func (t *traceStackTable) put(pcs []uintptr) uint64 {
150 if len(pcs) == 0 {
151 return 0
152 }
153 id, _ := t.tab.put(noescape(unsafe.Pointer(&pcs[0])), uintptr(len(pcs))*unsafe.Sizeof(uintptr(0)))
154 return id
155 }
156
157
158
159
160 func (t *traceStackTable) dump(gen uintptr) {
161 stackBuf := make([]uintptr, tracev2.MaxFramesPerStack)
162 w := unsafeTraceWriter(gen, nil)
163 if root := (*traceMapNode)(t.tab.root.Load()); root != nil {
164 w = dumpStacksRec(root, w, stackBuf)
165 }
166 w.flush().end()
167 t.tab.reset()
168 }
169
170 func dumpStacksRec(node *traceMapNode, w traceWriter, stackBuf []uintptr) traceWriter {
171 stack := unsafe.Slice((*uintptr)(unsafe.Pointer(&node.data[0])), uintptr(len(node.data))/unsafe.Sizeof(uintptr(0)))
172
173
174
175 n := fpunwindExpand(stackBuf, stack)
176 frames := makeTraceFrames(w.gen, stackBuf[:n])
177
178
179
180 maxBytes := 1 + (2+4*len(frames))*traceBytesPerNumber
181
182
183
184
185
186
187 var flushed bool
188 w, flushed = w.ensure(1 + maxBytes)
189 if flushed {
190 w.byte(byte(tracev2.EvStacks))
191 }
192
193
194 w.byte(byte(tracev2.EvStack))
195 w.varint(uint64(node.id))
196 w.varint(uint64(len(frames)))
197 for _, frame := range frames {
198 w.varint(uint64(frame.PC))
199 w.varint(frame.funcID)
200 w.varint(frame.fileID)
201 w.varint(frame.line)
202 }
203
204
205 for i := range node.children {
206 child := node.children[i].Load()
207 if child == nil {
208 continue
209 }
210 w = dumpStacksRec((*traceMapNode)(child), w, stackBuf)
211 }
212 return w
213 }
214
215
216
217 func makeTraceFrames(gen uintptr, pcs []uintptr) []traceFrame {
218 frames := make([]traceFrame, 0, len(pcs))
219 ci := CallersFrames(pcs)
220 for {
221 f, more := ci.Next()
222 frames = append(frames, makeTraceFrame(gen, f))
223 if !more {
224 return frames
225 }
226 }
227 }
228
229 type traceFrame struct {
230 PC uintptr
231 funcID uint64
232 fileID uint64
233 line uint64
234 }
235
236
237 func makeTraceFrame(gen uintptr, f Frame) traceFrame {
238 var frame traceFrame
239 frame.PC = f.PC
240
241 fn := f.Function
242 const maxLen = 1 << 10
243 if len(fn) > maxLen {
244 fn = fn[len(fn)-maxLen:]
245 }
246 frame.funcID = trace.stringTab[gen%2].put(gen, fn)
247 frame.line = uint64(f.Line)
248 file := f.File
249 if len(file) > maxLen {
250 file = file[len(file)-maxLen:]
251 }
252 frame.fileID = trace.stringTab[gen%2].put(gen, file)
253 return frame
254 }
255
256
257
258 func tracefpunwindoff() bool {
259 return debug.tracefpunwindoff != 0 || (goarch.ArchFamily != goarch.AMD64 && goarch.ArchFamily != goarch.ARM64)
260 }
261
262
263
264
265
266 func fpTracebackPCs(fp unsafe.Pointer, pcBuf []uintptr) (i int) {
267 for i = 0; i < len(pcBuf) && fp != nil; i++ {
268
269 pcBuf[i] = *(*uintptr)(unsafe.Pointer(uintptr(fp) + goarch.PtrSize))
270
271 fp = unsafe.Pointer(*(*uintptr)(fp))
272 }
273 return i
274 }
275
276
277 func pprof_fpunwindExpand(dst, src []uintptr) int {
278 return fpunwindExpand(dst, src)
279 }
280
281
282
283
284
285
286
287
288
289
290 func fpunwindExpand(dst, pcBuf []uintptr) int {
291 if len(pcBuf) == 0 {
292 return 0
293 } else if len(pcBuf) > 0 && pcBuf[0] == logicalStackSentinel {
294
295
296 return copy(dst, pcBuf[1:])
297 }
298
299 var (
300 n int
301 lastFuncID = abi.FuncIDNormal
302 skip = pcBuf[0]
303
304
305 skipOrAdd = func(retPC uintptr) bool {
306 if skip > 0 {
307 skip--
308 } else if n < len(dst) {
309 dst[n] = retPC
310 n++
311 }
312 return n < len(dst)
313 }
314 )
315
316 outer:
317 for _, retPC := range pcBuf[1:] {
318 callPC := retPC - 1
319 fi := findfunc(callPC)
320 if !fi.valid() {
321
322
323 if more := skipOrAdd(retPC); !more {
324 break outer
325 }
326 continue
327 }
328
329 u, uf := newInlineUnwinder(fi, callPC)
330 for ; uf.valid(); uf = u.next(uf) {
331 sf := u.srcFunc(uf)
332 if sf.funcID == abi.FuncIDWrapper && elideWrapperCalling(lastFuncID) {
333
334 } else if more := skipOrAdd(uf.pc + 1); !more {
335 break outer
336 }
337 lastFuncID = sf.funcID
338 }
339 }
340 return n
341 }
342
343
344
345
346 func startPCForTrace(pc uintptr) uintptr {
347 f := findfunc(pc)
348 if !f.valid() {
349 return pc
350 }
351 w := funcdata(f, abi.FUNCDATA_WrapInfo)
352 if w == nil {
353 return pc
354 }
355 return f.datap.textAddr(*(*uint32)(w))
356 }
357
View as plain text