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