1
2
3
4
5
6
7
8
9 package trace
10
11 import (
12 "fmt"
13 "strings"
14
15 "golang.org/x/exp/trace/internal/event"
16 "golang.org/x/exp/trace/internal/event/go122"
17 "golang.org/x/exp/trace/internal/version"
18 )
19
20
21
22
23
24
25
26
27 type ordering struct {
28 gStates map[GoID]*gState
29 pStates map[ProcID]*pState
30 mStates map[ThreadID]*mState
31 activeTasks map[TaskID]taskState
32 gcSeq uint64
33 gcState gcState
34 initialGen uint64
35 queue queue[Event]
36 }
37
38
39
40
41
42
43
44
45
46
47
48
49
50 func (o *ordering) Advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64) (bool, error) {
51 if o.initialGen == 0 {
52
53 o.initialGen = gen
54 }
55
56 var curCtx, newCtx schedCtx
57 curCtx.M = m
58 newCtx.M = m
59
60 var ms *mState
61 if m == NoThread {
62 curCtx.P = NoProc
63 curCtx.G = NoGoroutine
64 newCtx = curCtx
65 } else {
66
67 var ok bool
68 ms, ok = o.mStates[m]
69 if !ok {
70 ms = &mState{
71 g: NoGoroutine,
72 p: NoProc,
73 }
74 o.mStates[m] = ms
75 }
76 curCtx.P = ms.p
77 curCtx.G = ms.g
78 newCtx = curCtx
79 }
80
81 f := orderingDispatch[ev.typ]
82 if f == nil {
83 return false, fmt.Errorf("bad event type found while ordering: %v", ev.typ)
84 }
85 newCtx, ok, err := f(o, ev, evt, m, gen, curCtx)
86 if err == nil && ok && ms != nil {
87
88 ms.p = newCtx.P
89 ms.g = newCtx.G
90 }
91 return ok, err
92 }
93
94 type orderingHandleFunc func(o *ordering, ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error)
95
96 var orderingDispatch = [256]orderingHandleFunc{
97
98 go122.EvProcsChange: (*ordering).advanceAnnotation,
99 go122.EvProcStart: (*ordering).advanceProcStart,
100 go122.EvProcStop: (*ordering).advanceProcStop,
101 go122.EvProcSteal: (*ordering).advanceProcSteal,
102 go122.EvProcStatus: (*ordering).advanceProcStatus,
103
104
105 go122.EvGoCreate: (*ordering).advanceGoCreate,
106 go122.EvGoCreateSyscall: (*ordering).advanceGoCreateSyscall,
107 go122.EvGoStart: (*ordering).advanceGoStart,
108 go122.EvGoDestroy: (*ordering).advanceGoStopExec,
109 go122.EvGoDestroySyscall: (*ordering).advanceGoDestroySyscall,
110 go122.EvGoStop: (*ordering).advanceGoStopExec,
111 go122.EvGoBlock: (*ordering).advanceGoStopExec,
112 go122.EvGoUnblock: (*ordering).advanceGoUnblock,
113 go122.EvGoSyscallBegin: (*ordering).advanceGoSyscallBegin,
114 go122.EvGoSyscallEnd: (*ordering).advanceGoSyscallEnd,
115 go122.EvGoSyscallEndBlocked: (*ordering).advanceGoSyscallEndBlocked,
116 go122.EvGoStatus: (*ordering).advanceGoStatus,
117
118
119 go122.EvSTWBegin: (*ordering).advanceGoRangeBegin,
120 go122.EvSTWEnd: (*ordering).advanceGoRangeEnd,
121
122
123 go122.EvGCActive: (*ordering).advanceGCActive,
124 go122.EvGCBegin: (*ordering).advanceGCBegin,
125 go122.EvGCEnd: (*ordering).advanceGCEnd,
126 go122.EvGCSweepActive: (*ordering).advanceGCSweepActive,
127 go122.EvGCSweepBegin: (*ordering).advanceGCSweepBegin,
128 go122.EvGCSweepEnd: (*ordering).advanceGCSweepEnd,
129 go122.EvGCMarkAssistActive: (*ordering).advanceGoRangeActive,
130 go122.EvGCMarkAssistBegin: (*ordering).advanceGoRangeBegin,
131 go122.EvGCMarkAssistEnd: (*ordering).advanceGoRangeEnd,
132 go122.EvHeapAlloc: (*ordering).advanceHeapMetric,
133 go122.EvHeapGoal: (*ordering).advanceHeapMetric,
134
135
136 go122.EvGoLabel: (*ordering).advanceAnnotation,
137 go122.EvUserTaskBegin: (*ordering).advanceUserTaskBegin,
138 go122.EvUserTaskEnd: (*ordering).advanceUserTaskEnd,
139 go122.EvUserRegionBegin: (*ordering).advanceUserRegionBegin,
140 go122.EvUserRegionEnd: (*ordering).advanceUserRegionEnd,
141 go122.EvUserLog: (*ordering).advanceAnnotation,
142
143
144 go122.EvGoSwitch: (*ordering).advanceGoSwitch,
145 go122.EvGoSwitchDestroy: (*ordering).advanceGoSwitch,
146 go122.EvGoCreateBlocked: (*ordering).advanceGoCreate,
147
148
149 go122.EvGoStatusStack: (*ordering).advanceGoStatus,
150 }
151
152 func (o *ordering) advanceProcStatus(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
153 pid := ProcID(ev.args[0])
154 status := go122.ProcStatus(ev.args[1])
155 if int(status) >= len(go122ProcStatus2ProcState) {
156 return curCtx, false, fmt.Errorf("invalid status for proc %d: %d", pid, status)
157 }
158 oldState := go122ProcStatus2ProcState[status]
159 if s, ok := o.pStates[pid]; ok {
160 if status == go122.ProcSyscallAbandoned && s.status == go122.ProcSyscall {
161
162
163
164 oldState = ProcRunning
165 ev.args[1] = uint64(go122.ProcSyscall)
166 } else if status == go122.ProcSyscallAbandoned && s.status == go122.ProcSyscallAbandoned {
167
168
169
170 oldState = ProcIdle
171 ev.args[1] = uint64(go122.ProcSyscallAbandoned)
172 } else if s.status != status {
173 return curCtx, false, fmt.Errorf("inconsistent status for proc %d: old %v vs. new %v", pid, s.status, status)
174 }
175 s.seq = makeSeq(gen, 0)
176 } else {
177 o.pStates[pid] = &pState{id: pid, status: status, seq: makeSeq(gen, 0)}
178 if gen == o.initialGen {
179 oldState = ProcUndetermined
180 } else {
181 oldState = ProcNotExist
182 }
183 }
184 ev.extra(version.Go122)[0] = uint64(oldState)
185
186
187 newCtx := curCtx
188 if status == go122.ProcRunning || status == go122.ProcSyscall {
189 newCtx.P = pid
190 }
191
192
193
194
195
196
197 if status == go122.ProcSyscallAbandoned && oldState == ProcRunning {
198
199 found := false
200 for mid, ms := range o.mStates {
201 if ms.p == pid {
202 curCtx.M = mid
203 curCtx.P = pid
204 curCtx.G = ms.g
205 found = true
206 }
207 }
208 if !found {
209 return curCtx, false, fmt.Errorf("failed to find sched context for proc %d that's about to be stolen", pid)
210 }
211 }
212 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
213 return newCtx, true, nil
214 }
215
216 func (o *ordering) advanceProcStart(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
217 pid := ProcID(ev.args[0])
218 seq := makeSeq(gen, ev.args[1])
219
220
221
222
223 state, ok := o.pStates[pid]
224 if !ok || state.status != go122.ProcIdle || !seq.succeeds(state.seq) || curCtx.P != NoProc {
225
226
227
228
229
230 return curCtx, false, nil
231 }
232
233
234
235 reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustNotHave, Goroutine: event.MayHave}
236 if err := validateCtx(curCtx, reqs); err != nil {
237 return curCtx, false, err
238 }
239 state.status = go122.ProcRunning
240 state.seq = seq
241 newCtx := curCtx
242 newCtx.P = pid
243 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
244 return newCtx, true, nil
245 }
246
247 func (o *ordering) advanceProcStop(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
248
249
250
251
252
253
254
255
256
257 state, ok := o.pStates[curCtx.P]
258 if !ok {
259 return curCtx, false, fmt.Errorf("event %s for proc (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.P)
260 }
261 if state.status != go122.ProcRunning && state.status != go122.ProcSyscall {
262 return curCtx, false, fmt.Errorf("%s event for proc that's not %s or %s", go122.EventString(ev.typ), go122.ProcRunning, go122.ProcSyscall)
263 }
264 reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}
265 if err := validateCtx(curCtx, reqs); err != nil {
266 return curCtx, false, err
267 }
268 state.status = go122.ProcIdle
269 newCtx := curCtx
270 newCtx.P = NoProc
271 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
272 return newCtx, true, nil
273 }
274
275 func (o *ordering) advanceProcSteal(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
276 pid := ProcID(ev.args[0])
277 seq := makeSeq(gen, ev.args[1])
278 state, ok := o.pStates[pid]
279 if !ok || (state.status != go122.ProcSyscall && state.status != go122.ProcSyscallAbandoned) || !seq.succeeds(state.seq) {
280
281
282
283 return curCtx, false, nil
284 }
285
286 reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MayHave}
287 if err := validateCtx(curCtx, reqs); err != nil {
288 return curCtx, false, err
289 }
290
291
292
293
294
295
296 oldStatus := state.status
297 ev.extra(version.Go122)[0] = uint64(oldStatus)
298
299
300 state.status = go122.ProcIdle
301 state.seq = seq
302
303
304
305 if oldStatus == go122.ProcSyscallAbandoned {
306 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
307 return curCtx, true, nil
308 }
309
310
311 mid := ThreadID(ev.args[2])
312
313 newCtx := curCtx
314 if mid == curCtx.M {
315
316 if curCtx.P != pid {
317 return curCtx, false, fmt.Errorf("tried to self-steal proc %d (thread %d), but got proc %d instead", pid, mid, curCtx.P)
318 }
319 newCtx.P = NoProc
320 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
321 return newCtx, true, nil
322 }
323
324
325 mState, ok := o.mStates[mid]
326 if !ok {
327 return curCtx, false, fmt.Errorf("stole proc from non-existent thread %d", mid)
328 }
329
330
331 if mState.p != pid {
332 return curCtx, false, fmt.Errorf("tried to steal proc %d from thread %d, but got proc %d instead", pid, mid, mState.p)
333 }
334
335
336
337
338
339
340
341 mState.p = NoProc
342 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
343 return newCtx, true, nil
344 }
345
346 func (o *ordering) advanceGoStatus(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
347 gid := GoID(ev.args[0])
348 mid := ThreadID(ev.args[1])
349 status := go122.GoStatus(ev.args[2])
350
351 if int(status) >= len(go122GoStatus2GoState) {
352 return curCtx, false, fmt.Errorf("invalid status for goroutine %d: %d", gid, status)
353 }
354 oldState := go122GoStatus2GoState[status]
355 if s, ok := o.gStates[gid]; ok {
356 if s.status != status {
357 return curCtx, false, fmt.Errorf("inconsistent status for goroutine %d: old %v vs. new %v", gid, s.status, status)
358 }
359 s.seq = makeSeq(gen, 0)
360 } else if gen == o.initialGen {
361
362 o.gStates[gid] = &gState{id: gid, status: status, seq: makeSeq(gen, 0)}
363 oldState = GoUndetermined
364 } else {
365 return curCtx, false, fmt.Errorf("found goroutine status for new goroutine after the first generation: id=%v status=%v", gid, status)
366 }
367 ev.extra(version.Go122)[0] = uint64(oldState)
368
369 newCtx := curCtx
370 switch status {
371 case go122.GoRunning:
372
373 newCtx.G = gid
374 case go122.GoSyscall:
375 if mid == NoThread {
376 return curCtx, false, fmt.Errorf("found goroutine %d in syscall without a thread", gid)
377 }
378
379
380
381 if mid == curCtx.M {
382 if gen != o.initialGen && curCtx.G != gid {
383
384
385
386
387 return curCtx, false, fmt.Errorf("inconsistent thread for syscalling goroutine %d: thread has goroutine %d", gid, curCtx.G)
388 }
389 newCtx.G = gid
390 break
391 }
392
393
394
395
396
397 ms, ok := o.mStates[mid]
398 if ok {
399
400
401 if ms.g != gid {
402
403 return curCtx, false, fmt.Errorf("inconsistent thread for syscalling goroutine %d: thread has goroutine %d", gid, ms.g)
404 }
405
406
407 curCtx.G = ms.g
408 } else if !ok {
409
410
411
412 o.mStates[mid] = &mState{g: gid, p: NoProc}
413
414
415 }
416
417 curCtx.M = mid
418 }
419 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
420 return newCtx, true, nil
421 }
422
423 func (o *ordering) advanceGoCreate(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
424
425
426 reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}
427 if err := validateCtx(curCtx, reqs); err != nil {
428 return curCtx, false, err
429 }
430
431 if state, ok := o.gStates[curCtx.G]; ok && state.status != go122.GoRunning {
432 return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning)
433 }
434
435 newgid := GoID(ev.args[0])
436 if _, ok := o.gStates[newgid]; ok {
437 return curCtx, false, fmt.Errorf("tried to create goroutine (%v) that already exists", newgid)
438 }
439 status := go122.GoRunnable
440 if ev.typ == go122.EvGoCreateBlocked {
441 status = go122.GoWaiting
442 }
443 o.gStates[newgid] = &gState{id: newgid, status: status, seq: makeSeq(gen, 0)}
444 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
445 return curCtx, true, nil
446 }
447
448 func (o *ordering) advanceGoStopExec(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
449
450
451
452 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
453 return curCtx, false, err
454 }
455 state, ok := o.gStates[curCtx.G]
456 if !ok {
457 return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G)
458 }
459 if state.status != go122.GoRunning {
460 return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning)
461 }
462
463
464 newCtx := curCtx
465 switch ev.typ {
466 case go122.EvGoDestroy:
467
468 delete(o.gStates, curCtx.G)
469 newCtx.G = NoGoroutine
470 case go122.EvGoStop:
471
472 state.status = go122.GoRunnable
473 newCtx.G = NoGoroutine
474 case go122.EvGoBlock:
475
476 state.status = go122.GoWaiting
477 newCtx.G = NoGoroutine
478 }
479 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
480 return newCtx, true, nil
481 }
482
483 func (o *ordering) advanceGoStart(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
484 gid := GoID(ev.args[0])
485 seq := makeSeq(gen, ev.args[1])
486 state, ok := o.gStates[gid]
487 if !ok || state.status != go122.GoRunnable || !seq.succeeds(state.seq) {
488
489
490
491 return curCtx, false, nil
492 }
493
494 reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MustNotHave}
495 if err := validateCtx(curCtx, reqs); err != nil {
496 return curCtx, false, err
497 }
498 state.status = go122.GoRunning
499 state.seq = seq
500 newCtx := curCtx
501 newCtx.G = gid
502 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
503 return newCtx, true, nil
504 }
505
506 func (o *ordering) advanceGoUnblock(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
507
508 gid := GoID(ev.args[0])
509 seq := makeSeq(gen, ev.args[1])
510 state, ok := o.gStates[gid]
511 if !ok || state.status != go122.GoWaiting || !seq.succeeds(state.seq) {
512
513
514
515 return curCtx, false, nil
516 }
517 state.status = go122.GoRunnable
518 state.seq = seq
519
520
521 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
522 return curCtx, true, nil
523 }
524
525 func (o *ordering) advanceGoSwitch(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
526
527
528
529
530
531
532
533
534
535 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
536 return curCtx, false, err
537 }
538 curGState, ok := o.gStates[curCtx.G]
539 if !ok {
540 return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G)
541 }
542 if curGState.status != go122.GoRunning {
543 return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning)
544 }
545 nextg := GoID(ev.args[0])
546 seq := makeSeq(gen, ev.args[1])
547 nextGState, ok := o.gStates[nextg]
548 if !ok || nextGState.status != go122.GoWaiting || !seq.succeeds(nextGState.seq) {
549
550
551
552 return curCtx, false, nil
553 }
554 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
555
556
557
558
559 switch ev.typ {
560 case go122.EvGoSwitch:
561
562 curGState.status = go122.GoWaiting
563
564
565
566 o.queue.push(makeEvent(evt, curCtx, go122.EvGoBlock, ev.time, 0 , 0 ))
567 case go122.EvGoSwitchDestroy:
568
569 delete(o.gStates, curCtx.G)
570
571
572 o.queue.push(makeEvent(evt, curCtx, go122.EvGoDestroy, ev.time))
573 }
574
575 nextGState.status = go122.GoRunning
576 nextGState.seq = seq
577 newCtx := curCtx
578 newCtx.G = nextg
579
580
581 startCtx := curCtx
582 startCtx.G = NoGoroutine
583 o.queue.push(makeEvent(evt, startCtx, go122.EvGoStart, ev.time, uint64(nextg), ev.args[1]))
584 return newCtx, true, nil
585 }
586
587 func (o *ordering) advanceGoSyscallBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
588
589
590 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
591 return curCtx, false, err
592 }
593 state, ok := o.gStates[curCtx.G]
594 if !ok {
595 return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G)
596 }
597 if state.status != go122.GoRunning {
598 return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning)
599 }
600
601 state.status = go122.GoSyscall
602 pState, ok := o.pStates[curCtx.P]
603 if !ok {
604 return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(ev.typ))
605 }
606 pState.status = go122.ProcSyscall
607
608
609
610
611
612
613
614
615
616
617
618 pSeq := makeSeq(gen, ev.args[0])
619 if !pSeq.succeeds(pState.seq) {
620 return curCtx, false, fmt.Errorf("failed to advance %s: can't make sequence: %s -> %s", go122.EventString(ev.typ), pState.seq, pSeq)
621 }
622 pState.seq = pSeq
623 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
624 return curCtx, true, nil
625 }
626
627 func (o *ordering) advanceGoSyscallEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
628
629
630
631 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
632 return curCtx, false, err
633 }
634 state, ok := o.gStates[curCtx.G]
635 if !ok {
636 return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G)
637 }
638 if state.status != go122.GoSyscall {
639 return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning)
640 }
641 state.status = go122.GoRunning
642
643
644 pState, ok := o.pStates[curCtx.P]
645 if !ok {
646 return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(ev.typ))
647 }
648 if pState.status != go122.ProcSyscall {
649 return curCtx, false, fmt.Errorf("expected proc %d in state %v, but got %v instead", curCtx.P, go122.ProcSyscall, pState.status)
650 }
651 pState.status = go122.ProcRunning
652 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
653 return curCtx, true, nil
654 }
655
656 func (o *ordering) advanceGoSyscallEndBlocked(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
657
658
659
660
661
662
663
664
665
666
667 if curCtx.P != NoProc {
668 pState, ok := o.pStates[curCtx.P]
669 if !ok {
670 return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(ev.typ))
671 }
672 if pState.status == go122.ProcSyscall {
673 return curCtx, false, nil
674 }
675 }
676
677
678 if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustHave}); err != nil {
679 return curCtx, false, err
680 }
681 state, ok := o.gStates[curCtx.G]
682 if !ok {
683 return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G)
684 }
685 if state.status != go122.GoSyscall {
686 return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning)
687 }
688 newCtx := curCtx
689 newCtx.G = NoGoroutine
690 state.status = go122.GoRunnable
691 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
692 return newCtx, true, nil
693 }
694
695 func (o *ordering) advanceGoCreateSyscall(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
696
697
698
699 if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustNotHave}); err != nil {
700 return curCtx, false, err
701 }
702
703 newgid := GoID(ev.args[0])
704 if _, ok := o.gStates[newgid]; ok {
705 return curCtx, false, fmt.Errorf("tried to create goroutine (%v) in syscall that already exists", newgid)
706 }
707 o.gStates[newgid] = &gState{id: newgid, status: go122.GoSyscall, seq: makeSeq(gen, 0)}
708
709 newCtx := curCtx
710 newCtx.G = newgid
711 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
712 return newCtx, true, nil
713 }
714
715 func (o *ordering) advanceGoDestroySyscall(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732 if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustHave}); err != nil {
733 return curCtx, false, err
734 }
735
736 state, ok := o.gStates[curCtx.G]
737 if !ok {
738 return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G)
739 }
740 if state.status != go122.GoSyscall {
741 return curCtx, false, fmt.Errorf("%s event for goroutine that's not %v", go122.EventString(ev.typ), GoSyscall)
742 }
743
744 delete(o.gStates, curCtx.G)
745 newCtx := curCtx
746 newCtx.G = NoGoroutine
747
748
749 if curCtx.P != NoProc {
750 pState, ok := o.pStates[curCtx.P]
751 if !ok {
752 return curCtx, false, fmt.Errorf("found invalid proc %d during %s", curCtx.P, go122.EventString(ev.typ))
753 }
754 if pState.status != go122.ProcSyscall {
755 return curCtx, false, fmt.Errorf("proc %d in unexpected state %s during %s", curCtx.P, pState.status, go122.EventString(ev.typ))
756 }
757
758 pState.status = go122.ProcSyscallAbandoned
759 newCtx.P = NoProc
760
761
762 extra := makeEvent(evt, curCtx, go122.EvProcSteal, ev.time, uint64(curCtx.P))
763 extra.base.extra(version.Go122)[0] = uint64(go122.ProcSyscall)
764 o.queue.push(extra)
765 }
766 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
767 return newCtx, true, nil
768 }
769
770 func (o *ordering) advanceUserTaskBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
771
772
773
774
775
776
777
778 id := TaskID(ev.args[0])
779 if _, ok := o.activeTasks[id]; ok {
780 return curCtx, false, fmt.Errorf("task ID conflict: %d", id)
781 }
782
783
784 parentID := TaskID(ev.args[1])
785 if parentID == BackgroundTask {
786
787
788
789 parentID = NoTask
790 ev.args[1] = uint64(NoTask)
791 }
792
793
794
795 nameID := stringID(ev.args[2])
796 name, ok := evt.strings.get(nameID)
797 if !ok {
798 return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, ev.typ)
799 }
800 o.activeTasks[id] = taskState{name: name, parentID: parentID}
801 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
802 return curCtx, false, err
803 }
804 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
805 return curCtx, true, nil
806 }
807
808 func (o *ordering) advanceUserTaskEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
809 id := TaskID(ev.args[0])
810 if ts, ok := o.activeTasks[id]; ok {
811
812
813
814 ev.extra(version.Go122)[0] = uint64(ts.parentID)
815 ev.extra(version.Go122)[1] = uint64(evt.addExtraString(ts.name))
816 delete(o.activeTasks, id)
817 } else {
818
819 ev.extra(version.Go122)[0] = uint64(NoTask)
820 ev.extra(version.Go122)[1] = uint64(evt.addExtraString(""))
821 }
822 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
823 return curCtx, false, err
824 }
825 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
826 return curCtx, true, nil
827 }
828
829 func (o *ordering) advanceUserRegionBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
830 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
831 return curCtx, false, err
832 }
833 tid := TaskID(ev.args[0])
834 nameID := stringID(ev.args[1])
835 name, ok := evt.strings.get(nameID)
836 if !ok {
837 return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, ev.typ)
838 }
839 gState, ok := o.gStates[curCtx.G]
840 if !ok {
841 return curCtx, false, fmt.Errorf("encountered EvUserRegionBegin without known state for current goroutine %d", curCtx.G)
842 }
843 if err := gState.beginRegion(userRegion{tid, name}); err != nil {
844 return curCtx, false, err
845 }
846 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
847 return curCtx, true, nil
848 }
849
850 func (o *ordering) advanceUserRegionEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
851 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
852 return curCtx, false, err
853 }
854 tid := TaskID(ev.args[0])
855 nameID := stringID(ev.args[1])
856 name, ok := evt.strings.get(nameID)
857 if !ok {
858 return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, ev.typ)
859 }
860 gState, ok := o.gStates[curCtx.G]
861 if !ok {
862 return curCtx, false, fmt.Errorf("encountered EvUserRegionEnd without known state for current goroutine %d", curCtx.G)
863 }
864 if err := gState.endRegion(userRegion{tid, name}); err != nil {
865 return curCtx, false, err
866 }
867 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
868 return curCtx, true, nil
869 }
870
871
872
873
874
875
876
877
878 func (o *ordering) advanceGCActive(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
879 seq := ev.args[0]
880 if gen == o.initialGen {
881 if o.gcState != gcUndetermined {
882 return curCtx, false, fmt.Errorf("GCActive in the first generation isn't first GC event")
883 }
884 o.gcSeq = seq
885 o.gcState = gcRunning
886 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
887 return curCtx, true, nil
888 }
889 if seq != o.gcSeq+1 {
890
891 return curCtx, false, nil
892 }
893 if o.gcState != gcRunning {
894 return curCtx, false, fmt.Errorf("encountered GCActive while GC was not in progress")
895 }
896 o.gcSeq = seq
897 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
898 return curCtx, false, err
899 }
900 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
901 return curCtx, true, nil
902 }
903
904 func (o *ordering) advanceGCBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
905 seq := ev.args[0]
906 if o.gcState == gcUndetermined {
907 o.gcSeq = seq
908 o.gcState = gcRunning
909 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
910 return curCtx, true, nil
911 }
912 if seq != o.gcSeq+1 {
913
914 return curCtx, false, nil
915 }
916 if o.gcState == gcRunning {
917 return curCtx, false, fmt.Errorf("encountered GCBegin while GC was already in progress")
918 }
919 o.gcSeq = seq
920 o.gcState = gcRunning
921 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
922 return curCtx, false, err
923 }
924 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
925 return curCtx, true, nil
926 }
927
928 func (o *ordering) advanceGCEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
929 seq := ev.args[0]
930 if seq != o.gcSeq+1 {
931
932 return curCtx, false, nil
933 }
934 if o.gcState == gcNotRunning {
935 return curCtx, false, fmt.Errorf("encountered GCEnd when GC was not in progress")
936 }
937 if o.gcState == gcUndetermined {
938 return curCtx, false, fmt.Errorf("encountered GCEnd when GC was in an undetermined state")
939 }
940 o.gcSeq = seq
941 o.gcState = gcNotRunning
942 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
943 return curCtx, false, err
944 }
945 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
946 return curCtx, true, nil
947 }
948
949 func (o *ordering) advanceAnnotation(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
950
951 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
952 return curCtx, false, err
953 }
954 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
955 return curCtx, true, nil
956 }
957
958 func (o *ordering) advanceHeapMetric(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
959
960 if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil {
961 return curCtx, false, err
962 }
963 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
964 return curCtx, true, nil
965 }
966
967 func (o *ordering) advanceGCSweepBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
968
969 if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil {
970 return curCtx, false, err
971 }
972 if err := o.pStates[curCtx.P].beginRange(makeRangeType(ev.typ, 0)); err != nil {
973 return curCtx, false, err
974 }
975 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
976 return curCtx, true, nil
977 }
978
979 func (o *ordering) advanceGCSweepActive(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
980 pid := ProcID(ev.args[0])
981
982
983
984
985 pState, ok := o.pStates[pid]
986 if !ok {
987 return curCtx, false, fmt.Errorf("encountered GCSweepActive for unknown proc %d", pid)
988 }
989 if err := pState.activeRange(makeRangeType(ev.typ, 0), gen == o.initialGen); err != nil {
990 return curCtx, false, err
991 }
992 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
993 return curCtx, true, nil
994 }
995
996 func (o *ordering) advanceGCSweepEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
997 if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil {
998 return curCtx, false, err
999 }
1000 _, err := o.pStates[curCtx.P].endRange(ev.typ)
1001 if err != nil {
1002 return curCtx, false, err
1003 }
1004 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
1005 return curCtx, true, nil
1006 }
1007
1008 func (o *ordering) advanceGoRangeBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
1009
1010 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
1011 return curCtx, false, err
1012 }
1013 desc := stringID(0)
1014 if ev.typ == go122.EvSTWBegin {
1015 desc = stringID(ev.args[0])
1016 }
1017 gState, ok := o.gStates[curCtx.G]
1018 if !ok {
1019 return curCtx, false, fmt.Errorf("encountered event of type %d without known state for current goroutine %d", ev.typ, curCtx.G)
1020 }
1021 if err := gState.beginRange(makeRangeType(ev.typ, desc)); err != nil {
1022 return curCtx, false, err
1023 }
1024 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
1025 return curCtx, true, nil
1026 }
1027
1028 func (o *ordering) advanceGoRangeActive(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
1029 gid := GoID(ev.args[0])
1030
1031
1032
1033 gState, ok := o.gStates[gid]
1034 if !ok {
1035 return curCtx, false, fmt.Errorf("uninitialized goroutine %d found during %s", gid, go122.EventString(ev.typ))
1036 }
1037 if err := gState.activeRange(makeRangeType(ev.typ, 0), gen == o.initialGen); err != nil {
1038 return curCtx, false, err
1039 }
1040 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
1041 return curCtx, true, nil
1042 }
1043
1044 func (o *ordering) advanceGoRangeEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) {
1045 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
1046 return curCtx, false, err
1047 }
1048 gState, ok := o.gStates[curCtx.G]
1049 if !ok {
1050 return curCtx, false, fmt.Errorf("encountered event of type %d without known state for current goroutine %d", ev.typ, curCtx.G)
1051 }
1052 desc, err := gState.endRange(ev.typ)
1053 if err != nil {
1054 return curCtx, false, err
1055 }
1056 if ev.typ == go122.EvSTWEnd {
1057
1058
1059 ev.args[0] = uint64(desc)
1060 }
1061 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev})
1062 return curCtx, true, nil
1063 }
1064
1065
1066 func (o *ordering) Next() (Event, bool) {
1067 return o.queue.pop()
1068 }
1069
1070
1071 type schedCtx struct {
1072 G GoID
1073 P ProcID
1074 M ThreadID
1075 }
1076
1077
1078
1079 func validateCtx(ctx schedCtx, reqs event.SchedReqs) error {
1080
1081 if reqs.Thread == event.MustHave && ctx.M == NoThread {
1082 return fmt.Errorf("expected a thread but didn't have one")
1083 } else if reqs.Thread == event.MustNotHave && ctx.M != NoThread {
1084 return fmt.Errorf("expected no thread but had one")
1085 }
1086
1087
1088 if reqs.Proc == event.MustHave && ctx.P == NoProc {
1089 return fmt.Errorf("expected a proc but didn't have one")
1090 } else if reqs.Proc == event.MustNotHave && ctx.P != NoProc {
1091 return fmt.Errorf("expected no proc but had one")
1092 }
1093
1094
1095 if reqs.Goroutine == event.MustHave && ctx.G == NoGoroutine {
1096 return fmt.Errorf("expected a goroutine but didn't have one")
1097 } else if reqs.Goroutine == event.MustNotHave && ctx.G != NoGoroutine {
1098 return fmt.Errorf("expected no goroutine but had one")
1099 }
1100 return nil
1101 }
1102
1103
1104
1105
1106 type gcState uint8
1107
1108 const (
1109 gcUndetermined gcState = iota
1110 gcNotRunning
1111 gcRunning
1112 )
1113
1114
1115 func (s gcState) String() string {
1116 switch s {
1117 case gcUndetermined:
1118 return "Undetermined"
1119 case gcNotRunning:
1120 return "NotRunning"
1121 case gcRunning:
1122 return "Running"
1123 }
1124 return "Bad"
1125 }
1126
1127
1128 type userRegion struct {
1129
1130
1131
1132 taskID TaskID
1133 name string
1134 }
1135
1136
1137
1138
1139
1140
1141 type rangeType struct {
1142 typ event.Type
1143 desc stringID
1144 }
1145
1146
1147 func makeRangeType(typ event.Type, desc stringID) rangeType {
1148 if styp := go122.Specs()[typ].StartEv; styp != go122.EvNone {
1149 typ = styp
1150 }
1151 return rangeType{typ, desc}
1152 }
1153
1154
1155 type gState struct {
1156 id GoID
1157 status go122.GoStatus
1158 seq seqCounter
1159
1160
1161 regions []userRegion
1162
1163
1164 rangeState
1165 }
1166
1167
1168 func (s *gState) beginRegion(r userRegion) error {
1169 s.regions = append(s.regions, r)
1170 return nil
1171 }
1172
1173
1174 func (s *gState) endRegion(r userRegion) error {
1175 if len(s.regions) == 0 {
1176
1177 return nil
1178 }
1179 if next := s.regions[len(s.regions)-1]; next != r {
1180 return fmt.Errorf("misuse of region in goroutine %v: region end %v when the inner-most active region start event is %v", s.id, r, next)
1181 }
1182 s.regions = s.regions[:len(s.regions)-1]
1183 return nil
1184 }
1185
1186
1187 type pState struct {
1188 id ProcID
1189 status go122.ProcStatus
1190 seq seqCounter
1191
1192
1193 rangeState
1194 }
1195
1196
1197 type mState struct {
1198 g GoID
1199 p ProcID
1200 }
1201
1202
1203 type rangeState struct {
1204
1205 inFlight []rangeType
1206 }
1207
1208
1209
1210
1211 func (s *rangeState) beginRange(typ rangeType) error {
1212 if s.hasRange(typ) {
1213 return fmt.Errorf("discovered event already in-flight for when starting event %v", go122.Specs()[typ.typ].Name)
1214 }
1215 s.inFlight = append(s.inFlight, typ)
1216 return nil
1217 }
1218
1219
1220
1221 func (s *rangeState) activeRange(typ rangeType, isInitialGen bool) error {
1222 if isInitialGen {
1223 if s.hasRange(typ) {
1224 return fmt.Errorf("found named active range already in first gen: %v", typ)
1225 }
1226 s.inFlight = append(s.inFlight, typ)
1227 } else if !s.hasRange(typ) {
1228 return fmt.Errorf("resource is missing active range: %v %v", go122.Specs()[typ.typ].Name, s.inFlight)
1229 }
1230 return nil
1231 }
1232
1233
1234 func (s *rangeState) hasRange(typ rangeType) bool {
1235 for _, ftyp := range s.inFlight {
1236 if ftyp == typ {
1237 return true
1238 }
1239 }
1240 return false
1241 }
1242
1243
1244
1245
1246 func (s *rangeState) endRange(typ event.Type) (stringID, error) {
1247 st := go122.Specs()[typ].StartEv
1248 idx := -1
1249 for i, r := range s.inFlight {
1250 if r.typ == st {
1251 idx = i
1252 break
1253 }
1254 }
1255 if idx < 0 {
1256 return 0, fmt.Errorf("tried to end event %v, but not in-flight", go122.Specs()[st].Name)
1257 }
1258
1259 desc := s.inFlight[idx].desc
1260 s.inFlight[idx], s.inFlight[len(s.inFlight)-1] = s.inFlight[len(s.inFlight)-1], s.inFlight[idx]
1261 s.inFlight = s.inFlight[:len(s.inFlight)-1]
1262 return desc, nil
1263 }
1264
1265
1266 type seqCounter struct {
1267 gen uint64
1268 seq uint64
1269 }
1270
1271
1272 func makeSeq(gen, seq uint64) seqCounter {
1273 return seqCounter{gen: gen, seq: seq}
1274 }
1275
1276
1277 func (a seqCounter) succeeds(b seqCounter) bool {
1278 return a.gen == b.gen && a.seq == b.seq+1
1279 }
1280
1281
1282 func (c seqCounter) String() string {
1283 return fmt.Sprintf("%d (gen=%d)", c.seq, c.gen)
1284 }
1285
1286 func dumpOrdering(order *ordering) string {
1287 var sb strings.Builder
1288 for id, state := range order.gStates {
1289 fmt.Fprintf(&sb, "G %d [status=%s seq=%s]\n", id, state.status, state.seq)
1290 }
1291 fmt.Fprintln(&sb)
1292 for id, state := range order.pStates {
1293 fmt.Fprintf(&sb, "P %d [status=%s seq=%s]\n", id, state.status, state.seq)
1294 }
1295 fmt.Fprintln(&sb)
1296 for id, state := range order.mStates {
1297 fmt.Fprintf(&sb, "M %d [g=%d p=%d]\n", id, state.g, state.p)
1298 }
1299 fmt.Fprintln(&sb)
1300 fmt.Fprintf(&sb, "GC %d %s\n", order.gcSeq, order.gcState)
1301 return sb.String()
1302 }
1303
1304
1305 type taskState struct {
1306
1307 name string
1308
1309
1310 parentID TaskID
1311 }
1312
1313
1314 type queue[T any] struct {
1315 start, end int
1316 buf []T
1317 }
1318
1319
1320 func (q *queue[T]) push(value T) {
1321 if q.end-q.start == len(q.buf) {
1322 q.grow()
1323 }
1324 q.buf[q.end%len(q.buf)] = value
1325 q.end++
1326 }
1327
1328
1329 func (q *queue[T]) grow() {
1330 if len(q.buf) == 0 {
1331 q.buf = make([]T, 2)
1332 return
1333 }
1334
1335
1336 newBuf := make([]T, len(q.buf)*2)
1337 pivot := q.start % len(q.buf)
1338 first, last := q.buf[pivot:], q.buf[:pivot]
1339 copy(newBuf[:len(first)], first)
1340 copy(newBuf[len(first):], last)
1341
1342
1343 q.start = 0
1344 q.end = len(q.buf)
1345 q.buf = newBuf
1346 }
1347
1348
1349
1350 func (q *queue[T]) pop() (T, bool) {
1351 if q.end-q.start == 0 {
1352 return *new(T), false
1353 }
1354 elem := &q.buf[q.start%len(q.buf)]
1355 value := *elem
1356 *elem = *new(T)
1357 q.start++
1358 return value, true
1359 }
1360
1361
1362
1363
1364
1365
1366 func makeEvent(table *evTable, ctx schedCtx, typ event.Type, time Time, args ...uint64) Event {
1367 ev := Event{
1368 table: table,
1369 ctx: ctx,
1370 base: baseEvent{
1371 typ: typ,
1372 time: time,
1373 },
1374 }
1375 copy(ev.base.args[:], args)
1376 return ev
1377 }
1378
View as plain text