Source file
src/cmd/trace/gstate.go
1
2
3
4
5 package main
6
7 import (
8 "fmt"
9 "internal/trace"
10 "internal/trace/traceviewer"
11 "internal/trace/traceviewer/format"
12 "strings"
13 )
14
15
16 type resource interface {
17 trace.GoID | trace.ProcID | trace.ThreadID
18 }
19
20
21 const noResource = -1
22
23
24
25
26
27
28 type gState[R resource] struct {
29 baseName string
30 named bool
31 label string
32 isSystemG bool
33
34 executing R
35
36
37
38
39
40 lastStopStack trace.Stack
41
42
43 activeRanges map[string]activeRange
44
45
46
47 completedRanges []completedRange
48
49
50
51 startRunningTime trace.Time
52
53
54
55 syscall struct {
56 time trace.Time
57 stack trace.Stack
58 active bool
59 }
60
61
62
63 startBlockReason string
64
65
66
67
68
69
70
71
72 startCause struct {
73 time trace.Time
74 name string
75 resource uint64
76 stack trace.Stack
77 }
78 }
79
80
81
82 func newGState[R resource](goID trace.GoID) *gState[R] {
83 return &gState[R]{
84 baseName: fmt.Sprintf("G%d", goID),
85 executing: R(noResource),
86 activeRanges: make(map[string]activeRange),
87 }
88 }
89
90
91
92
93 func (gs *gState[R]) augmentName(stk trace.Stack) {
94 if gs.named {
95 return
96 }
97 if stk == trace.NoStack {
98 return
99 }
100 name := lastFunc(stk)
101 gs.baseName += fmt.Sprintf(" %s", name)
102 gs.named = true
103 gs.isSystemG = trace.IsSystemGoroutine(name)
104 }
105
106
107 func (gs *gState[R]) setLabel(label string) {
108 gs.label = label
109 }
110
111
112 func (gs *gState[R]) name() string {
113 name := gs.baseName
114 if gs.label != "" {
115 name += " (" + gs.label + ")"
116 }
117 return name
118 }
119
120
121
122 func (gs *gState[R]) setStartCause(ts trace.Time, name string, resource uint64, stack trace.Stack) {
123 gs.startCause.time = ts
124 gs.startCause.name = name
125 gs.startCause.resource = resource
126 gs.startCause.stack = stack
127 }
128
129
130 func (gs *gState[R]) created(ts trace.Time, creator R, stack trace.Stack) {
131 if creator == R(noResource) {
132 return
133 }
134 gs.setStartCause(ts, "go", uint64(creator), stack)
135 }
136
137
138 func (gs *gState[R]) start(ts trace.Time, resource R, ctx *traceContext) {
139
140 for name := range gs.activeRanges {
141 gs.activeRanges[name] = activeRange{ts, trace.NoStack}
142 }
143
144 if gs.startCause.name != "" {
145
146 ctx.Arrow(traceviewer.ArrowEvent{
147 Name: gs.startCause.name,
148 Start: ctx.elapsed(gs.startCause.time),
149 End: ctx.elapsed(ts),
150 FromResource: uint64(gs.startCause.resource),
151 ToResource: uint64(resource),
152 FromStack: ctx.Stack(viewerFrames(gs.startCause.stack)),
153 })
154 gs.startCause.time = 0
155 gs.startCause.name = ""
156 gs.startCause.resource = 0
157 gs.startCause.stack = trace.NoStack
158 }
159 gs.executing = resource
160 gs.startRunningTime = ts
161 }
162
163
164 func (gs *gState[R]) syscallBegin(ts trace.Time, resource R, stack trace.Stack) {
165 gs.syscall.time = ts
166 gs.syscall.stack = stack
167 gs.syscall.active = true
168 if gs.executing == R(noResource) {
169 gs.executing = resource
170 gs.startRunningTime = ts
171 }
172 }
173
174
175
176
177
178
179
180 func (gs *gState[R]) syscallEnd(ts trace.Time, blocked bool, ctx *traceContext) {
181 if !gs.syscall.active {
182 return
183 }
184 blockString := "no"
185 if blocked {
186 blockString = "yes"
187 }
188 gs.completedRanges = append(gs.completedRanges, completedRange{
189 name: "syscall",
190 startTime: gs.syscall.time,
191 endTime: ts,
192 startStack: gs.syscall.stack,
193 arg: format.BlockedArg{Blocked: blockString},
194 })
195 gs.syscall.active = false
196 gs.syscall.time = 0
197 gs.syscall.stack = trace.NoStack
198 }
199
200
201
202
203 func (gs *gState[R]) blockedSyscallEnd(ts trace.Time, stack trace.Stack, ctx *traceContext) {
204 name := "exit blocked syscall"
205 gs.setStartCause(ts, name, trace.SyscallP, stack)
206
207
208 ctx.Instant(traceviewer.InstantEvent{
209 Name: name,
210 Ts: ctx.elapsed(ts),
211 Resource: trace.SyscallP,
212 Stack: ctx.Stack(viewerFrames(stack)),
213 })
214 }
215
216
217 func (gs *gState[R]) unblock(ts trace.Time, stack trace.Stack, resource R, ctx *traceContext) {
218 name := "unblock"
219 viewerResource := uint64(resource)
220 if gs.startBlockReason != "" {
221 name = fmt.Sprintf("%s (%s)", name, gs.startBlockReason)
222 }
223 if strings.Contains(gs.startBlockReason, "network") {
224
225
226
227
228
229 if _, ok := any(resource).(trace.ThreadID); !ok {
230
231 viewerResource = trace.NetpollP
232 }
233 ctx.Instant(traceviewer.InstantEvent{
234 Name: name,
235 Ts: ctx.elapsed(ts),
236 Resource: viewerResource,
237 Stack: ctx.Stack(viewerFrames(stack)),
238 })
239 }
240 gs.startBlockReason = ""
241 if viewerResource != 0 {
242 gs.setStartCause(ts, name, viewerResource, stack)
243 }
244 }
245
246
247
248 func (gs *gState[R]) block(ts trace.Time, stack trace.Stack, reason string, ctx *traceContext) {
249 gs.startBlockReason = reason
250 gs.stop(ts, stack, ctx)
251 }
252
253
254 func (gs *gState[R]) stop(ts trace.Time, stack trace.Stack, ctx *traceContext) {
255
256 var stk int
257 if gs.lastStopStack != trace.NoStack {
258 stk = ctx.Stack(viewerFrames(gs.lastStopStack))
259 }
260 var endStk int
261 if stack != trace.NoStack {
262 endStk = ctx.Stack(viewerFrames(stack))
263 }
264
265 if gs.startRunningTime == 0 {
266 panic("silently broken trace or generator invariant (startRunningTime != 0) not held")
267 }
268 if gs.executing == R(noResource) {
269 panic("non-executing goroutine stopped")
270 }
271 ctx.Slice(traceviewer.SliceEvent{
272 Name: gs.name(),
273 Ts: ctx.elapsed(gs.startRunningTime),
274 Dur: ts.Sub(gs.startRunningTime),
275 Resource: uint64(gs.executing),
276 Stack: stk,
277 EndStack: endStk,
278 })
279
280
281 for _, cr := range gs.completedRanges {
282 ctx.Slice(traceviewer.SliceEvent{
283 Name: cr.name,
284 Ts: ctx.elapsed(cr.startTime),
285 Dur: cr.endTime.Sub(cr.startTime),
286 Resource: uint64(gs.executing),
287 Stack: ctx.Stack(viewerFrames(cr.startStack)),
288 EndStack: ctx.Stack(viewerFrames(cr.endStack)),
289 Arg: cr.arg,
290 })
291 }
292 gs.completedRanges = gs.completedRanges[:0]
293
294
295 for name, r := range gs.activeRanges {
296
297 if r.time == 0 {
298 panic("silently broken trace or generator invariant (activeRanges time != 0) not held")
299 }
300 ctx.Slice(traceviewer.SliceEvent{
301 Name: name,
302 Ts: ctx.elapsed(r.time),
303 Dur: ts.Sub(r.time),
304 Resource: uint64(gs.executing),
305 Stack: ctx.Stack(viewerFrames(r.stack)),
306 })
307 }
308
309
310 for name := range gs.activeRanges {
311 gs.activeRanges[name] = activeRange{0, trace.NoStack}
312 }
313
314 gs.startRunningTime = 0
315 gs.lastStopStack = stack
316 gs.executing = R(noResource)
317 }
318
319
320
321
322
323 func (gs *gState[R]) finish(ctx *traceContext) {
324 if gs.executing != R(noResource) {
325 gs.syscallEnd(ctx.endTime, false, ctx)
326 gs.stop(ctx.endTime, trace.NoStack, ctx)
327 }
328 }
329
330
331 func (gs *gState[R]) rangeBegin(ts trace.Time, name string, stack trace.Stack) {
332 if gs.executing != R(noResource) {
333
334 gs.activeRanges[name] = activeRange{ts, stack}
335 } else {
336
337
338 gs.activeRanges[name] = activeRange{0, stack}
339 }
340 }
341
342
343 func (gs *gState[R]) rangeActive(name string) {
344 if gs.executing != R(noResource) {
345
346
347 gs.activeRanges[name] = activeRange{gs.startRunningTime, trace.NoStack}
348 } else {
349
350
351 gs.activeRanges[name] = activeRange{0, trace.NoStack}
352 }
353 }
354
355
356 func (gs *gState[R]) rangeEnd(ts trace.Time, name string, stack trace.Stack, ctx *traceContext) {
357 if gs.executing != R(noResource) {
358 r := gs.activeRanges[name]
359 gs.completedRanges = append(gs.completedRanges, completedRange{
360 name: name,
361 startTime: r.time,
362 endTime: ts,
363 startStack: r.stack,
364 endStack: stack,
365 })
366 }
367 delete(gs.activeRanges, name)
368 }
369
370 func lastFunc(s trace.Stack) (fn string) {
371 for frame := range s.Frames() {
372 fn = frame.Func
373 }
374 return
375 }
376
View as plain text