1 // Copyright 2023 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
11 "internal/trace/v2/event"
12 "internal/trace/v2/event/go122"
13 "internal/trace/v2/version"
16 // ordering emulates Go scheduler state for both validation and
17 // for putting events in the right order.
18 type ordering struct {
19 gStates map[GoID]*gState
20 pStates map[ProcID]*pState // TODO: The keys are dense, so this can be a slice.
21 mStates map[ThreadID]*mState
22 activeTasks map[TaskID]taskState
28 // advance checks if it's valid to proceed with ev which came from thread m.
30 // Returns the schedCtx at the point of the event, whether it's OK to advance
31 // with this event, and any error encountered in validation.
33 // It assumes the gen value passed to it is monotonically increasing across calls.
35 // If any error is returned, then the trace is broken and trace parsing must cease.
36 // If it's not valid to advance with ev, but no error was encountered, the caller
37 // should attempt to advance with other candidate events from other threads. If the
38 // caller runs out of candidates, the trace is invalid.
39 func (o *ordering) advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64) (schedCtx, bool, error) {
40 if o.initialGen == 0 {
41 // Set the initial gen if necessary.
45 var curCtx, newCtx schedCtx
51 curCtx.G = NoGoroutine
54 // Pull out or create the mState for this event.
55 ms, ok := o.mStates[m]
67 // Update the mState for this event.
73 switch typ := ev.typ; typ {
75 case go122.EvProcStatus:
76 pid := ProcID(ev.args[0])
77 status := go122.ProcStatus(ev.args[1])
78 oldState := go122ProcStatus2ProcState[status]
79 if s, ok := o.pStates[pid]; ok {
80 if status == go122.ProcSyscallAbandoned && s.status == go122.ProcSyscall {
81 // ProcSyscallAbandoned is a special case of ProcSyscall. It indicates a
82 // potential loss of information, but if we're already in ProcSyscall,
83 // we haven't lost the relevant information. Promote the status and advance.
84 oldState = ProcRunning
85 ev.args[1] = uint64(go122.ProcSyscall)
86 } else if s.status != status {
87 return curCtx, false, fmt.Errorf("inconsistent status for proc %d: old %v vs. new %v", pid, s.status, status)
89 s.seq = makeSeq(gen, 0) // Reset seq.
91 o.pStates[pid] = &pState{id: pid, status: status, seq: makeSeq(gen, 0)}
92 if gen == o.initialGen {
93 oldState = ProcUndetermined
95 oldState = ProcNotExist
98 ev.extra(version.Go122)[0] = uint64(oldState) // Smuggle in the old state for StateTransition.
100 // Bind the proc to the new context, if it's running.
101 if status == go122.ProcRunning || status == go122.ProcSyscall {
104 // Set the current context to the state of the M current running this G. Otherwise
105 // we'll emit a Running -> Running event that doesn't correspond to the right M.
106 if status == go122.ProcSyscallAbandoned && oldState != ProcUndetermined {
107 // N.B. This is slow but it should be fairly rare.
109 for mid, ms := range o.mStates {
118 return curCtx, false, fmt.Errorf("failed to find sched context for proc %d that's about to be stolen", pid)
121 return curCtx, true, nil
122 case go122.EvProcStart:
123 pid := ProcID(ev.args[0])
124 seq := makeSeq(gen, ev.args[1])
126 // Try to advance. We might fail here due to sequencing, because the P hasn't
127 // had a status emitted, or because we already have a P and we're in a syscall,
128 // and we haven't observed that it was stolen from us yet.
129 state, ok := o.pStates[pid]
130 if !ok || state.status != go122.ProcIdle || !seq.succeeds(state.seq) || curCtx.P != NoProc {
131 // We can't make an inference as to whether this is bad. We could just be seeing
132 // a ProcStart on a different M before the proc's state was emitted, or before we
133 // got to the right point in the trace.
135 // Note that we also don't advance here if we have a P and we're in a syscall.
136 return curCtx, false, nil
138 // We can advance this P. Check some invariants.
140 // We might have a goroutine if a goroutine is exiting a syscall.
141 reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustNotHave, Goroutine: event.MayHave}
142 if err := validateCtx(curCtx, reqs); err != nil {
143 return curCtx, false, err
145 state.status = go122.ProcRunning
148 return curCtx, true, nil
149 case go122.EvProcStop:
150 // We must be able to advance this P.
152 // There are 2 ways a P can stop: ProcStop and ProcSteal. ProcStop is used when the P
153 // is stopped by the same M that started it, while ProcSteal is used when another M
154 // steals the P by stopping it from a distance.
156 // Since a P is bound to an M, and we're stopping on the same M we started, it must
157 // always be possible to advance the current M's P from a ProcStop. This is also why
158 // ProcStop doesn't need a sequence number.
159 state, ok := o.pStates[curCtx.P]
161 return curCtx, false, fmt.Errorf("event %s for proc (%v) that doesn't exist", go122.EventString(typ), curCtx.P)
163 if state.status != go122.ProcRunning && state.status != go122.ProcSyscall {
164 return curCtx, false, fmt.Errorf("%s event for proc that's not %s or %s", go122.EventString(typ), go122.ProcRunning, go122.ProcSyscall)
166 reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}
167 if err := validateCtx(curCtx, reqs); err != nil {
168 return curCtx, false, err
170 state.status = go122.ProcIdle
172 return curCtx, true, nil
173 case go122.EvProcSteal:
174 pid := ProcID(ev.args[0])
175 seq := makeSeq(gen, ev.args[1])
176 state, ok := o.pStates[pid]
177 if !ok || (state.status != go122.ProcSyscall && state.status != go122.ProcSyscallAbandoned) || !seq.succeeds(state.seq) {
178 // We can't make an inference as to whether this is bad. We could just be seeing
179 // a ProcStart on a different M before the proc's state was emitted, or before we
180 // got to the right point in the trace.
181 return curCtx, false, nil
183 // We can advance this P. Check some invariants.
184 reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MayHave}
185 if err := validateCtx(curCtx, reqs); err != nil {
186 return curCtx, false, err
188 // Smuggle in the P state that let us advance so we can surface information to the event.
189 // Specifically, we need to make sure that the event is interpreted not as a transition of
190 // ProcRunning -> ProcIdle but ProcIdle -> ProcIdle instead.
192 // ProcRunning is binding, but we may be running with a P on the current M and we can't
193 // bind another P. This P is about to go ProcIdle anyway.
194 oldStatus := state.status
195 ev.extra(version.Go122)[0] = uint64(oldStatus)
197 // Update the P's status and sequence number.
198 state.status = go122.ProcIdle
201 // If we've lost information then don't try to do anything with the M.
202 // It may have moved on and we can't be sure.
203 if oldStatus == go122.ProcSyscallAbandoned {
204 return curCtx, true, nil
207 // Validate that the M we're stealing from is what we expect.
208 mid := ThreadID(ev.args[2]) // The M we're stealing from.
209 mState, ok := o.mStates[mid]
211 return curCtx, false, fmt.Errorf("stole proc from non-existent thread %d", mid)
214 // Make sure we're actually stealing the right P.
216 return curCtx, false, fmt.Errorf("tried to steal proc %d from thread %d, but got proc %d instead", pid, mid, mState.p)
219 // Tell the M it has no P so it can proceed.
221 // This is safe because we know the P was in a syscall and
222 // the other M must be trying to get out of the syscall.
223 // GoSyscallEndBlocked cannot advance until the corresponding
226 return curCtx, true, nil
228 // Handle goroutines.
229 case go122.EvGoStatus:
230 gid := GoID(ev.args[0])
231 mid := ThreadID(ev.args[1])
232 status := go122.GoStatus(ev.args[2])
233 oldState := go122GoStatus2GoState[status]
234 if s, ok := o.gStates[gid]; ok {
235 if s.status != status {
236 return curCtx, false, fmt.Errorf("inconsistent status for goroutine %d: old %v vs. new %v", gid, s.status, status)
238 s.seq = makeSeq(gen, 0) // Reset seq.
239 } else if gen == o.initialGen {
241 o.gStates[gid] = &gState{id: gid, status: status, seq: makeSeq(gen, 0)}
242 oldState = GoUndetermined
244 return curCtx, false, fmt.Errorf("found goroutine status for new goroutine after the first generation: id=%v status=%v", gid, status)
246 ev.extra(version.Go122)[0] = uint64(oldState) // Smuggle in the old state for StateTransition.
249 case go122.GoRunning:
250 // Bind the goroutine to the new context, since it's running.
252 case go122.GoSyscall:
254 return curCtx, false, fmt.Errorf("found goroutine %d in syscall without a thread", gid)
256 // Is the syscall on this thread? If so, bind it to the context.
257 // Otherwise, we're talking about a G sitting in a syscall on an M.
258 // Validate the named M.
263 // Now we're talking about a thread and goroutine that have been
264 // blocked on a syscall for the entire generation. This case must
265 // not have a P; the runtime makes sure that all Ps are traced at
266 // the beginning of a generation, which involves taking a P back
267 // from every thread.
268 ms, ok := o.mStates[mid]
270 // This M has been seen. That means we must have seen this
271 // goroutine go into a syscall on this thread at some point.
273 // But the G on the M doesn't match. Something's wrong.
274 return curCtx, false, fmt.Errorf("inconsistent thread for syscalling goroutine %d: thread has goroutine %d", gid, ms.g)
276 // This case is just a Syscall->Syscall event, which needs to
277 // appear as having the G currently bound to this M.
280 // The M hasn't been seen yet. That means this goroutine
281 // has just been sitting in a syscall on this M. Create
283 o.mStates[mid] = &mState{g: gid, p: NoProc}
284 // Don't set curCtx.G in this case because this event is the
285 // binding event (and curCtx represents the "before" state).
287 // Update the current context to the M we're talking about.
290 return curCtx, true, nil
291 case go122.EvGoCreate:
292 // Goroutines must be created on a running P, but may or may not be created
293 // by a running goroutine.
294 reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}
295 if err := validateCtx(curCtx, reqs); err != nil {
296 return curCtx, false, err
298 // If we have a goroutine, it must be running.
299 if state, ok := o.gStates[curCtx.G]; ok && state.status != go122.GoRunning {
300 return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(typ), GoRunning)
302 // This goroutine created another. Add a state for it.
303 newgid := GoID(ev.args[0])
304 if _, ok := o.gStates[newgid]; ok {
305 return curCtx, false, fmt.Errorf("tried to create goroutine (%v) that already exists", newgid)
307 o.gStates[newgid] = &gState{id: newgid, status: go122.GoRunnable, seq: makeSeq(gen, 0)}
308 return curCtx, true, nil
309 case go122.EvGoDestroy, go122.EvGoStop, go122.EvGoBlock, go122.EvGoSyscallBegin:
310 // These are goroutine events that all require an active running
311 // goroutine on some thread. They must *always* be advance-able,
312 // since running goroutines are bound to their M.
313 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
314 return curCtx, false, err
316 state, ok := o.gStates[curCtx.G]
318 return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(typ), curCtx.G)
320 if state.status != go122.GoRunning {
321 return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(typ), GoRunning)
323 // Handle each case slightly differently; we just group them together
324 // because they have shared preconditions.
326 case go122.EvGoDestroy:
327 // This goroutine is exiting itself.
328 delete(o.gStates, curCtx.G)
329 newCtx.G = NoGoroutine
331 // Goroutine stopped (yielded). It's runnable but not running on this M.
332 state.status = go122.GoRunnable
333 newCtx.G = NoGoroutine
334 case go122.EvGoBlock:
335 // Goroutine blocked. It's waiting now and not running on this M.
336 state.status = go122.GoWaiting
337 newCtx.G = NoGoroutine
338 case go122.EvGoSyscallBegin:
339 // Goroutine entered a syscall. It's still running on this P and M.
340 state.status = go122.GoSyscall
341 pState, ok := o.pStates[curCtx.P]
343 return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(typ))
345 pState.status = go122.ProcSyscall
347 return curCtx, true, nil
348 case go122.EvGoStart:
349 gid := GoID(ev.args[0])
350 seq := makeSeq(gen, ev.args[1])
351 state, ok := o.gStates[gid]
352 if !ok || state.status != go122.GoRunnable || !seq.succeeds(state.seq) {
353 // We can't make an inference as to whether this is bad. We could just be seeing
354 // a GoStart on a different M before the goroutine was created, before it had its
355 // state emitted, or before we got to the right point in the trace yet.
356 return curCtx, false, nil
358 // We can advance this goroutine. Check some invariants.
359 reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MustNotHave}
360 if err := validateCtx(curCtx, reqs); err != nil {
361 return curCtx, false, err
363 state.status = go122.GoRunning
366 return curCtx, true, nil
367 case go122.EvGoUnblock:
368 // N.B. These both reference the goroutine to unblock, not the current goroutine.
369 gid := GoID(ev.args[0])
370 seq := makeSeq(gen, ev.args[1])
371 state, ok := o.gStates[gid]
372 if !ok || state.status != go122.GoWaiting || !seq.succeeds(state.seq) {
373 // We can't make an inference as to whether this is bad. We could just be seeing
374 // a GoUnblock on a different M before the goroutine was created and blocked itself,
375 // before it had its state emitted, or before we got to the right point in the trace yet.
376 return curCtx, false, nil
378 state.status = go122.GoRunnable
380 // N.B. No context to validate. Basically anything can unblock
381 // a goroutine (e.g. sysmon).
382 return curCtx, true, nil
383 case go122.EvGoSyscallEnd:
384 // This event is always advance-able because it happens on the same
385 // thread that EvGoSyscallStart happened, and the goroutine can't leave
386 // that thread until its done.
387 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
388 return curCtx, false, err
390 state, ok := o.gStates[curCtx.G]
392 return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(typ), curCtx.G)
394 if state.status != go122.GoSyscall {
395 return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(typ), GoRunning)
397 state.status = go122.GoRunning
399 // Transfer the P back to running from syscall.
400 pState, ok := o.pStates[curCtx.P]
402 return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(typ))
404 if pState.status != go122.ProcSyscall {
405 return curCtx, false, fmt.Errorf("expected proc %d in state %v, but got %v instead", curCtx.P, go122.ProcSyscall, pState.status)
407 pState.status = go122.ProcRunning
408 return curCtx, true, nil
409 case go122.EvGoSyscallEndBlocked:
410 // This event becomes advanceable when its P is not in a syscall state
411 // (lack of a P altogether is also acceptable for advancing).
412 // The transfer out of ProcSyscall can happen either voluntarily via
413 // ProcStop or involuntarily via ProcSteal. We may also acquire a new P
414 // before we get here (after the transfer out) but that's OK: that new
415 // P won't be in the ProcSyscall state anymore.
417 // Basically: while we have a preemptible P, don't advance, because we
418 // *know* from the event that we're going to lose it at some point during
419 // the syscall. We shouldn't advance until that happens.
420 if curCtx.P != NoProc {
421 pState, ok := o.pStates[curCtx.P]
423 return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(typ))
425 if pState.status == go122.ProcSyscall {
426 return curCtx, false, nil
429 // As mentioned above, we may have a P here if we ProcStart
430 // before this event.
431 if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustHave}); err != nil {
432 return curCtx, false, err
434 state, ok := o.gStates[curCtx.G]
436 return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(typ), curCtx.G)
438 if state.status != go122.GoSyscall {
439 return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(typ), GoRunning)
441 newCtx.G = NoGoroutine
442 state.status = go122.GoRunnable
443 return curCtx, true, nil
444 case go122.EvGoCreateSyscall:
445 // This event indicates that a goroutine is effectively
446 // being created out of a cgo callback. Such a goroutine
447 // is 'created' in the syscall state.
448 if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustNotHave, Goroutine: event.MustNotHave}); err != nil {
449 return curCtx, false, err
451 // This goroutine is effectively being created. Add a state for it.
452 newgid := GoID(ev.args[0])
453 if _, ok := o.gStates[newgid]; ok {
454 return curCtx, false, fmt.Errorf("tried to create goroutine (%v) in syscall that already exists", newgid)
456 o.gStates[newgid] = &gState{id: newgid, status: go122.GoSyscall, seq: makeSeq(gen, 0)}
457 // Goroutine is executing. Bind it to the context.
459 return curCtx, true, nil
460 case go122.EvGoDestroySyscall:
461 // This event indicates that a goroutine created for a
462 // cgo callback is disappearing, either because the callback
463 // ending or the C thread that called it is being destroyed.
465 // Note: we might have a P here. The P might not be released
466 // eagerly by the runtime, and it might get stolen back later
467 // (or never again, if the program is going to exit).
468 if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustHave}); err != nil {
469 return curCtx, false, err
471 // Check to make sure the goroutine exists in the right state.
472 state, ok := o.gStates[curCtx.G]
474 return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(typ), curCtx.G)
476 if state.status != go122.GoSyscall {
477 return curCtx, false, fmt.Errorf("%s event for goroutine that's not %v", go122.EventString(typ), GoSyscall)
479 // This goroutine is exiting itself.
480 delete(o.gStates, curCtx.G)
481 newCtx.G = NoGoroutine
482 return curCtx, true, nil
484 // Handle tasks. Tasks are interesting because:
485 // - There's no Begin event required to reference a task.
486 // - End for a particular task ID can appear multiple times.
487 // As a result, there's very little to validate. The only
488 // thing we have to be sure of is that a task didn't begin
489 // after it had already begun. Task IDs are allowed to be
490 // reused, so we don't care about a Begin after an End.
491 case go122.EvUserTaskBegin:
492 id := TaskID(ev.args[0])
493 if _, ok := o.activeTasks[id]; ok {
494 return curCtx, false, fmt.Errorf("task ID conflict: %d", id)
496 // Get the parent ID, but don't validate it. There's no guarantee
497 // we actually have information on whether it's active.
498 parentID := TaskID(ev.args[1])
500 // Validate the name and record it. We'll need to pass it through to
502 nameID := stringID(ev.args[2])
503 name, ok := evt.strings.get(nameID)
505 return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, typ)
507 o.activeTasks[id] = taskState{name: name, parentID: parentID}
508 return curCtx, true, validateCtx(curCtx, event.UserGoReqs)
509 case go122.EvUserTaskEnd:
510 id := TaskID(ev.args[0])
511 if ts, ok := o.activeTasks[id]; ok {
512 // Smuggle the task info. This may happen in a different generation,
513 // which may not have the name in its string table. Add it to the extra
514 // strings table so we can look it up later.
515 ev.extra(version.Go122)[0] = uint64(ts.parentID)
516 ev.extra(version.Go122)[1] = uint64(evt.addExtraString(ts.name))
517 delete(o.activeTasks, id)
519 // Explicitly clear the task info.
520 ev.extra(version.Go122)[0] = uint64(NoTask)
521 ev.extra(version.Go122)[1] = uint64(evt.addExtraString(""))
523 return curCtx, true, validateCtx(curCtx, event.UserGoReqs)
525 // Handle user regions.
526 case go122.EvUserRegionBegin:
527 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
528 return curCtx, false, err
530 tid := TaskID(ev.args[0])
531 nameID := stringID(ev.args[1])
532 name, ok := evt.strings.get(nameID)
534 return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, typ)
536 if err := o.gStates[curCtx.G].beginRegion(userRegion{tid, name}); err != nil {
537 return curCtx, false, err
539 return curCtx, true, nil
540 case go122.EvUserRegionEnd:
541 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
542 return curCtx, false, err
544 tid := TaskID(ev.args[0])
545 nameID := stringID(ev.args[1])
546 name, ok := evt.strings.get(nameID)
548 return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, typ)
550 if err := o.gStates[curCtx.G].endRegion(userRegion{tid, name}); err != nil {
551 return curCtx, false, err
553 return curCtx, true, nil
555 // Handle the GC mark phase.
557 // We have sequence numbers for both start and end because they
558 // can happen on completely different threads. We want an explicit
559 // partial order edge between start and end here, otherwise we're
560 // relying entirely on timestamps to make sure we don't advance a
561 // GCEnd for a _different_ GC cycle if timestamps are wildly broken.
562 case go122.EvGCActive:
564 if gen == o.initialGen {
565 if o.gcState != gcUndetermined {
566 return curCtx, false, fmt.Errorf("GCActive in the first generation isn't first GC event")
569 o.gcState = gcRunning
570 return curCtx, true, nil
572 if seq != o.gcSeq+1 {
573 // This is not the right GC cycle.
574 return curCtx, false, nil
576 if o.gcState != gcRunning {
577 return curCtx, false, fmt.Errorf("encountered GCActive while GC was not in progress")
580 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
581 return curCtx, false, err
583 return curCtx, true, nil
584 case go122.EvGCBegin:
586 if o.gcState == gcUndetermined {
588 o.gcState = gcRunning
589 return curCtx, true, nil
591 if seq != o.gcSeq+1 {
592 // This is not the right GC cycle.
593 return curCtx, false, nil
595 if o.gcState == gcRunning {
596 return curCtx, false, fmt.Errorf("encountered GCBegin while GC was already in progress")
599 o.gcState = gcRunning
600 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
601 return curCtx, false, err
603 return curCtx, true, nil
606 if seq != o.gcSeq+1 {
607 // This is not the right GC cycle.
608 return curCtx, false, nil
610 if o.gcState == gcNotRunning {
611 return curCtx, false, fmt.Errorf("encountered GCEnd when GC was not in progress")
613 if o.gcState == gcUndetermined {
614 return curCtx, false, fmt.Errorf("encountered GCEnd when GC was in an undetermined state")
617 o.gcState = gcNotRunning
618 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
619 return curCtx, false, err
621 return curCtx, true, nil
623 // Handle simple instantaneous events that require a G.
624 case go122.EvGoLabel, go122.EvProcsChange, go122.EvUserLog:
625 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
626 return curCtx, false, err
628 return curCtx, true, nil
630 // Handle allocation states, which don't require a G.
631 case go122.EvHeapAlloc, go122.EvHeapGoal:
632 if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil {
633 return curCtx, false, err
635 return curCtx, true, nil
637 // Handle sweep, which is bound to a P and doesn't require a G.
638 case go122.EvGCSweepBegin:
639 if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil {
640 return curCtx, false, err
642 if err := o.pStates[curCtx.P].beginRange(makeRangeType(typ, 0)); err != nil {
643 return curCtx, false, err
645 return curCtx, true, nil
646 case go122.EvGCSweepActive:
647 pid := ProcID(ev.args[0])
648 // N.B. In practice Ps can't block while they're sweeping, so this can only
649 // ever reference curCtx.P. However, be lenient about this like we are with
650 // GCMarkAssistActive; there's no reason the runtime couldn't change to block
651 // in the middle of a sweep.
652 if err := o.pStates[pid].activeRange(makeRangeType(typ, 0), gen == o.initialGen); err != nil {
653 return curCtx, false, err
655 return curCtx, true, nil
656 case go122.EvGCSweepEnd:
657 if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil {
658 return curCtx, false, err
660 _, err := o.pStates[curCtx.P].endRange(typ)
662 return curCtx, false, err
664 return curCtx, true, nil
666 // Handle special goroutine-bound event ranges.
667 case go122.EvSTWBegin, go122.EvGCMarkAssistBegin:
668 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
669 return curCtx, false, err
672 if typ == go122.EvSTWBegin {
673 desc = stringID(ev.args[0])
675 if err := o.gStates[curCtx.G].beginRange(makeRangeType(typ, desc)); err != nil {
676 return curCtx, false, err
678 return curCtx, true, nil
679 case go122.EvGCMarkAssistActive:
680 gid := GoID(ev.args[0])
681 // N.B. Like GoStatus, this can happen at any time, because it can
682 // reference a non-running goroutine. Don't check anything about the
683 // current scheduler context.
684 if err := o.gStates[gid].activeRange(makeRangeType(typ, 0), gen == o.initialGen); err != nil {
685 return curCtx, false, err
687 return curCtx, true, nil
688 case go122.EvSTWEnd, go122.EvGCMarkAssistEnd:
689 if err := validateCtx(curCtx, event.UserGoReqs); err != nil {
690 return curCtx, false, err
692 desc, err := o.gStates[curCtx.G].endRange(typ)
694 return curCtx, false, err
696 if typ == go122.EvSTWEnd {
697 // Smuggle the kind into the event.
698 // Don't use ev.extra here so we have symmetry with STWBegin.
699 ev.args[0] = uint64(desc)
701 return curCtx, true, nil
703 return curCtx, false, fmt.Errorf("bad event type found while ordering: %v", ev.typ)
706 // schedCtx represents the scheduling resources associated with an event.
707 type schedCtx struct {
713 // validateCtx ensures that ctx conforms to some reqs, returning an error if
715 func validateCtx(ctx schedCtx, reqs event.SchedReqs) error {
716 // Check thread requirements.
717 if reqs.Thread == event.MustHave && ctx.M == NoThread {
718 return fmt.Errorf("expected a thread but didn't have one")
719 } else if reqs.Thread == event.MustNotHave && ctx.M != NoThread {
720 return fmt.Errorf("expected no thread but had one")
723 // Check proc requirements.
724 if reqs.Proc == event.MustHave && ctx.P == NoProc {
725 return fmt.Errorf("expected a proc but didn't have one")
726 } else if reqs.Proc == event.MustNotHave && ctx.P != NoProc {
727 return fmt.Errorf("expected no proc but had one")
730 // Check goroutine requirements.
731 if reqs.Goroutine == event.MustHave && ctx.G == NoGoroutine {
732 return fmt.Errorf("expected a goroutine but didn't have one")
733 } else if reqs.Goroutine == event.MustNotHave && ctx.G != NoGoroutine {
734 return fmt.Errorf("expected no goroutine but had one")
739 // gcState is a trinary variable for the current state of the GC.
741 // The third state besides "enabled" and "disabled" is "undetermined."
745 gcUndetermined gcState = iota
750 // String returns a human-readable string for the GC state.
751 func (s gcState) String() string {
754 return "Undetermined"
763 // userRegion represents a unique user region when attached to some gState.
764 type userRegion struct {
765 // name must be a resolved string because the string ID for the same
766 // string may change across generations, but we care about checking
772 // rangeType is a way to classify special ranges of time.
774 // These typically correspond 1:1 with "Begin" events, but
775 // they may have an optional subtype that describes the range
777 type rangeType struct {
778 typ event.Type // "Begin" event.
779 desc stringID // Optional subtype.
782 // makeRangeType constructs a new rangeType.
783 func makeRangeType(typ event.Type, desc stringID) rangeType {
784 if styp := go122.Specs()[typ].StartEv; styp != go122.EvNone {
787 return rangeType{typ, desc}
790 // gState is the state of a goroutine at a point in the trace.
793 status go122.GoStatus
796 // regions are the active user regions for this goroutine.
799 // rangeState is the state of special time ranges bound to this goroutine.
803 // beginRegion starts a user region on the goroutine.
804 func (s *gState) beginRegion(r userRegion) error {
805 s.regions = append(s.regions, r)
809 // endRegion ends a user region on the goroutine.
810 func (s *gState) endRegion(r userRegion) error {
811 if next := s.regions[len(s.regions)-1]; next != r {
812 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)
814 s.regions = s.regions[:len(s.regions)-1]
818 // pState is the state of a proc at a point in the trace.
821 status go122.ProcStatus
824 // rangeState is the state of special time ranges bound to this proc.
828 // mState is the state of a thread at a point in the trace.
830 g GoID // Goroutine bound to this M. (The goroutine's state is Executing.)
831 p ProcID // Proc bound to this M. (The proc's state is Executing.)
834 // rangeState represents the state of special time ranges.
835 type rangeState struct {
836 // inFlight contains the rangeTypes of any ranges bound to a resource.
840 // beginRange begins a special range in time on the goroutine.
842 // Returns an error if the range is already in progress.
843 func (s *rangeState) beginRange(typ rangeType) error {
845 return fmt.Errorf("discovered event already in-flight for when starting event %v", go122.Specs()[typ.typ].Name)
847 s.inFlight = append(s.inFlight, typ)
851 // activeRange marks special range in time on the goroutine as active in the
852 // initial generation, or confirms that it is indeed active in later generations.
853 func (s *rangeState) activeRange(typ rangeType, isInitialGen bool) error {
856 return fmt.Errorf("found named active range already in first gen: %v", typ)
858 s.inFlight = append(s.inFlight, typ)
859 } else if !s.hasRange(typ) {
860 return fmt.Errorf("resource is missing active range: %v %v", go122.Specs()[typ.typ].Name, s.inFlight)
865 // hasRange returns true if a special time range on the goroutine as in progress.
866 func (s *rangeState) hasRange(typ rangeType) bool {
867 for _, ftyp := range s.inFlight {
875 // endsRange ends a special range in time on the goroutine.
877 // This must line up with the start event type of the range the goroutine is currently in.
878 func (s *rangeState) endRange(typ event.Type) (stringID, error) {
879 st := go122.Specs()[typ].StartEv
881 for i, r := range s.inFlight {
888 return 0, fmt.Errorf("tried to end event %v, but not in-flight", go122.Specs()[st].Name)
891 desc := s.inFlight[idx].desc
892 s.inFlight[idx], s.inFlight[len(s.inFlight)-1] = s.inFlight[len(s.inFlight)-1], s.inFlight[idx]
893 s.inFlight = s.inFlight[:len(s.inFlight)-1]
897 // seqCounter represents a global sequence counter for a resource.
898 type seqCounter struct {
899 gen uint64 // The generation for the local sequence counter seq.
900 seq uint64 // The sequence number local to the generation.
903 // makeSeq creates a new seqCounter.
904 func makeSeq(gen, seq uint64) seqCounter {
905 return seqCounter{gen: gen, seq: seq}
908 // succeeds returns true if a is the immediate successor of b.
909 func (a seqCounter) succeeds(b seqCounter) bool {
910 return a.gen == b.gen && a.seq == b.seq+1
913 // String returns a debug string representation of the seqCounter.
914 func (c seqCounter) String() string {
915 return fmt.Sprintf("%d (gen=%d)", c.seq, c.gen)
918 func dumpOrdering(order *ordering) string {
919 var sb strings.Builder
920 for id, state := range order.gStates {
921 fmt.Fprintf(&sb, "G %d [status=%s seq=%s]\n", id, state.status, state.seq)
924 for id, state := range order.pStates {
925 fmt.Fprintf(&sb, "P %d [status=%s seq=%s]\n", id, state.status, state.seq)
928 for id, state := range order.mStates {
929 fmt.Fprintf(&sb, "M %d [g=%d p=%d]\n", id, state.g, state.p)
932 fmt.Fprintf(&sb, "GC %d %s\n", order.gcSeq, order.gcState)
936 // taskState represents an active task.
937 type taskState struct {
938 // name is the type of the active task.
941 // parentID is the parent ID of the active task.