golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/trace/order.go (about) 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. 4 5 // Code generated by "gen.bash" from internal/trace/v2; DO NOT EDIT. 6 7 //go:build go1.21 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 // ordering emulates Go scheduler state for both validation and 21 // for putting events in the right order. 22 // 23 // The interface to ordering consists of two methods: Advance 24 // and Next. Advance is called to try and advance an event and 25 // add completed events to the ordering. Next is used to pick 26 // off events in the ordering. 27 type ordering struct { 28 gStates map[GoID]*gState 29 pStates map[ProcID]*pState // TODO: The keys are dense, so this can be a slice. 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 // Advance checks if it's valid to proceed with ev which came from thread m. 39 // 40 // It assumes the gen value passed to it is monotonically increasing across calls. 41 // 42 // If any error is returned, then the trace is broken and trace parsing must cease. 43 // If it's not valid to advance with ev, but no error was encountered, the caller 44 // should attempt to advance with other candidate events from other threads. If the 45 // caller runs out of candidates, the trace is invalid. 46 // 47 // If this returns true, Next is guaranteed to return a complete event. However, 48 // multiple events may be added to the ordering, so the caller should (but is not 49 // required to) continue to call Next until it is exhausted. 50 func (o *ordering) Advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64) (bool, error) { 51 if o.initialGen == 0 { 52 // Set the initial gen if necessary. 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 // Pull out or create the mState for this event. 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 // Update the mState for this event. 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 // Procs. 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 // Goroutines. 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 // STW. 119 go122.EvSTWBegin: (*ordering).advanceGoRangeBegin, 120 go122.EvSTWEnd: (*ordering).advanceGoRangeEnd, 121 122 // GC events. 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 // Annotations. 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 // Coroutines. Added in Go 1.23. 144 go122.EvGoSwitch: (*ordering).advanceGoSwitch, 145 go122.EvGoSwitchDestroy: (*ordering).advanceGoSwitch, 146 go122.EvGoCreateBlocked: (*ordering).advanceGoCreate, 147 148 // GoStatus event with a stack. Added in Go 1.23. 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 // ProcSyscallAbandoned is a special case of ProcSyscall. It indicates a 162 // potential loss of information, but if we're already in ProcSyscall, 163 // we haven't lost the relevant information. Promote the status and advance. 164 oldState = ProcRunning 165 ev.args[1] = uint64(go122.ProcSyscall) 166 } else if status == go122.ProcSyscallAbandoned && s.status == go122.ProcSyscallAbandoned { 167 // If we're passing through ProcSyscallAbandoned, then there's no promotion 168 // to do. We've lost the M that this P is associated with. However it got there, 169 // it's going to appear as idle in the API, so pass through as idle. 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) // Reset seq. 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) // Smuggle in the old state for StateTransition. 185 186 // Bind the proc to the new context, if it's running. 187 newCtx := curCtx 188 if status == go122.ProcRunning || status == go122.ProcSyscall { 189 newCtx.P = pid 190 } 191 // If we're advancing through ProcSyscallAbandoned *but* oldState is running then we've 192 // promoted it to ProcSyscall. However, because it's ProcSyscallAbandoned, we know this 193 // P is about to get stolen and its status very likely isn't being emitted by the same 194 // thread it was bound to. Since this status is Running -> Running and Running is binding, 195 // we need to make sure we emit it in the right context: the context to which it is bound. 196 // Find it, and set our current context to it. 197 if status == go122.ProcSyscallAbandoned && oldState == ProcRunning { 198 // N.B. This is slow but it should be fairly rare. 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 // Try to advance. We might fail here due to sequencing, because the P hasn't 221 // had a status emitted, or because we already have a P and we're in a syscall, 222 // and we haven't observed that it was stolen from us yet. 223 state, ok := o.pStates[pid] 224 if !ok || state.status != go122.ProcIdle || !seq.succeeds(state.seq) || curCtx.P != NoProc { 225 // We can't make an inference as to whether this is bad. We could just be seeing 226 // a ProcStart on a different M before the proc's state was emitted, or before we 227 // got to the right point in the trace. 228 // 229 // Note that we also don't advance here if we have a P and we're in a syscall. 230 return curCtx, false, nil 231 } 232 // We can advance this P. Check some invariants. 233 // 234 // We might have a goroutine if a goroutine is exiting a syscall. 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 // We must be able to advance this P. 249 // 250 // There are 2 ways a P can stop: ProcStop and ProcSteal. ProcStop is used when the P 251 // is stopped by the same M that started it, while ProcSteal is used when another M 252 // steals the P by stopping it from a distance. 253 // 254 // Since a P is bound to an M, and we're stopping on the same M we started, it must 255 // always be possible to advance the current M's P from a ProcStop. This is also why 256 // ProcStop doesn't need a sequence number. 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 // We can't make an inference as to whether this is bad. We could just be seeing 281 // a ProcStart on a different M before the proc's state was emitted, or before we 282 // got to the right point in the trace. 283 return curCtx, false, nil 284 } 285 // We can advance this P. Check some invariants. 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 // Smuggle in the P state that let us advance so we can surface information to the event. 291 // Specifically, we need to make sure that the event is interpreted not as a transition of 292 // ProcRunning -> ProcIdle but ProcIdle -> ProcIdle instead. 293 // 294 // ProcRunning is binding, but we may be running with a P on the current M and we can't 295 // bind another P. This P is about to go ProcIdle anyway. 296 oldStatus := state.status 297 ev.extra(version.Go122)[0] = uint64(oldStatus) 298 299 // Update the P's status and sequence number. 300 state.status = go122.ProcIdle 301 state.seq = seq 302 303 // If we've lost information then don't try to do anything with the M. 304 // It may have moved on and we can't be sure. 305 if oldStatus == go122.ProcSyscallAbandoned { 306 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) 307 return curCtx, true, nil 308 } 309 310 // Validate that the M we're stealing from is what we expect. 311 mid := ThreadID(ev.args[2]) // The M we're stealing from. 312 313 newCtx := curCtx 314 if mid == curCtx.M { 315 // We're stealing from ourselves. This behaves like a ProcStop. 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 // We're stealing from some other M. 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 // Make sure we're actually stealing the right P. 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 // Tell the M it has no P so it can proceed. 336 // 337 // This is safe because we know the P was in a syscall and 338 // the other M must be trying to get out of the syscall. 339 // GoSyscallEndBlocked cannot advance until the corresponding 340 // M loses its P. 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) // Reset seq. 360 } else if gen == o.initialGen { 361 // Set the state. 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) // Smuggle in the old state for StateTransition. 368 369 newCtx := curCtx 370 switch status { 371 case go122.GoRunning: 372 // Bind the goroutine to the new context, since it's running. 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 // Is the syscall on this thread? If so, bind it to the context. 379 // Otherwise, we're talking about a G sitting in a syscall on an M. 380 // Validate the named M. 381 if mid == curCtx.M { 382 if gen != o.initialGen && curCtx.G != gid { 383 // If this isn't the first generation, we *must* have seen this 384 // binding occur already. Even if the G was blocked in a syscall 385 // for multiple generations since trace start, we would have seen 386 // a previous GoStatus event that bound the goroutine to an M. 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 // Now we're talking about a thread and goroutine that have been 393 // blocked on a syscall for the entire generation. This case must 394 // not have a P; the runtime makes sure that all Ps are traced at 395 // the beginning of a generation, which involves taking a P back 396 // from every thread. 397 ms, ok := o.mStates[mid] 398 if ok { 399 // This M has been seen. That means we must have seen this 400 // goroutine go into a syscall on this thread at some point. 401 if ms.g != gid { 402 // But the G on the M doesn't match. Something's wrong. 403 return curCtx, false, fmt.Errorf("inconsistent thread for syscalling goroutine %d: thread has goroutine %d", gid, ms.g) 404 } 405 // This case is just a Syscall->Syscall event, which needs to 406 // appear as having the G currently bound to this M. 407 curCtx.G = ms.g 408 } else if !ok { 409 // The M hasn't been seen yet. That means this goroutine 410 // has just been sitting in a syscall on this M. Create 411 // a state for it. 412 o.mStates[mid] = &mState{g: gid, p: NoProc} 413 // Don't set curCtx.G in this case because this event is the 414 // binding event (and curCtx represents the "before" state). 415 } 416 // Update the current context to the M we're talking about. 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 // Goroutines must be created on a running P, but may or may not be created 425 // by a running goroutine. 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 // If we have a goroutine, it must be running. 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 // This goroutine created another. Add a state for it. 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 // These are goroutine events that all require an active running 450 // goroutine on some thread. They must *always* be advance-able, 451 // since running goroutines are bound to their M. 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 // Handle each case slightly differently; we just group them together 463 // because they have shared preconditions. 464 newCtx := curCtx 465 switch ev.typ { 466 case go122.EvGoDestroy: 467 // This goroutine is exiting itself. 468 delete(o.gStates, curCtx.G) 469 newCtx.G = NoGoroutine 470 case go122.EvGoStop: 471 // Goroutine stopped (yielded). It's runnable but not running on this M. 472 state.status = go122.GoRunnable 473 newCtx.G = NoGoroutine 474 case go122.EvGoBlock: 475 // Goroutine blocked. It's waiting now and not running on this M. 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 // We can't make an inference as to whether this is bad. We could just be seeing 489 // a GoStart on a different M before the goroutine was created, before it had its 490 // state emitted, or before we got to the right point in the trace yet. 491 return curCtx, false, nil 492 } 493 // We can advance this goroutine. Check some invariants. 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 // N.B. These both reference the goroutine to unblock, not the current goroutine. 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 // We can't make an inference as to whether this is bad. We could just be seeing 513 // a GoUnblock on a different M before the goroutine was created and blocked itself, 514 // before it had its state emitted, or before we got to the right point in the trace yet. 515 return curCtx, false, nil 516 } 517 state.status = go122.GoRunnable 518 state.seq = seq 519 // N.B. No context to validate. Basically anything can unblock 520 // a goroutine (e.g. sysmon). 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 // GoSwitch and GoSwitchDestroy represent a trio of events: 527 // - Unblock of the goroutine to switch to. 528 // - Block or destroy of the current goroutine. 529 // - Start executing the next goroutine. 530 // 531 // Because it acts like a GoStart for the next goroutine, we can 532 // only advance it if the sequence numbers line up. 533 // 534 // The current goroutine on the thread must be actively running. 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]) // seq is for nextg, not curCtx.G. 547 nextGState, ok := o.gStates[nextg] 548 if !ok || nextGState.status != go122.GoWaiting || !seq.succeeds(nextGState.seq) { 549 // We can't make an inference as to whether this is bad. We could just be seeing 550 // a GoSwitch on a different M before the goroutine was created, before it had its 551 // state emitted, or before we got to the right point in the trace yet. 552 return curCtx, false, nil 553 } 554 o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) 555 556 // Update the state of the executing goroutine and emit an event for it 557 // (GoSwitch and GoSwitchDestroy will be interpreted as GoUnblock events 558 // for nextg). 559 switch ev.typ { 560 case go122.EvGoSwitch: 561 // Goroutine blocked. It's waiting now and not running on this M. 562 curGState.status = go122.GoWaiting 563 564 // Emit a GoBlock event. 565 // TODO(mknyszek): Emit a reason. 566 o.queue.push(makeEvent(evt, curCtx, go122.EvGoBlock, ev.time, 0 /* no reason */, 0 /* no stack */)) 567 case go122.EvGoSwitchDestroy: 568 // This goroutine is exiting itself. 569 delete(o.gStates, curCtx.G) 570 571 // Emit a GoDestroy event. 572 o.queue.push(makeEvent(evt, curCtx, go122.EvGoDestroy, ev.time)) 573 } 574 // Update the state of the next goroutine. 575 nextGState.status = go122.GoRunning 576 nextGState.seq = seq 577 newCtx := curCtx 578 newCtx.G = nextg 579 580 // Queue an event for the next goroutine starting to run. 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 // Entering a syscall requires an active running goroutine with a 589 // proc on some thread. It is always advancable. 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 // Goroutine entered a syscall. It's still running on this P and M. 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 // Validate the P sequence number on the event and advance it. 608 // 609 // We have a P sequence number for what is supposed to be a goroutine event 610 // so that we can correctly model P stealing. Without this sequence number here, 611 // the syscall from which a ProcSteal event is stealing can be ambiguous in the 612 // face of broken timestamps. See the go122-syscall-steal-proc-ambiguous test for 613 // more details. 614 // 615 // Note that because this sequence number only exists as a tool for disambiguation, 616 // we can enforce that we have the right sequence number at this point; we don't need 617 // to back off and see if any other events will advance. This is a running P. 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 // This event is always advance-able because it happens on the same 629 // thread that EvGoSyscallStart happened, and the goroutine can't leave 630 // that thread until its done. 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 // Transfer the P back to running from syscall. 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 // This event becomes advanceable when its P is not in a syscall state 658 // (lack of a P altogether is also acceptable for advancing). 659 // The transfer out of ProcSyscall can happen either voluntarily via 660 // ProcStop or involuntarily via ProcSteal. We may also acquire a new P 661 // before we get here (after the transfer out) but that's OK: that new 662 // P won't be in the ProcSyscall state anymore. 663 // 664 // Basically: while we have a preemptible P, don't advance, because we 665 // *know* from the event that we're going to lose it at some point during 666 // the syscall. We shouldn't advance until that happens. 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 // As mentioned above, we may have a P here if we ProcStart 677 // before this event. 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 // This event indicates that a goroutine is effectively 697 // being created out of a cgo callback. Such a goroutine 698 // is 'created' in the syscall state. 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 // This goroutine is effectively being created. Add a state for it. 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 // Goroutine is executing. Bind it to the context. 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 // This event indicates that a goroutine created for a 717 // cgo callback is disappearing, either because the callback 718 // ending or the C thread that called it is being destroyed. 719 // 720 // Also, treat this as if we lost our P too. 721 // The thread ID may be reused by the platform and we'll get 722 // really confused if we try to steal the P is this is running 723 // with later. The new M with the same ID could even try to 724 // steal back this P from itself! 725 // 726 // The runtime is careful to make sure that any GoCreateSyscall 727 // event will enter the runtime emitting events for reacquiring a P. 728 // 729 // Note: we might have a P here. The P might not be released 730 // eagerly by the runtime, and it might get stolen back later 731 // (or never again, if the program is going to exit). 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 // Check to make sure the goroutine exists in the right state. 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 // This goroutine is exiting itself. 744 delete(o.gStates, curCtx.G) 745 newCtx := curCtx 746 newCtx.G = NoGoroutine 747 748 // If we have a proc, then we're dissociating from it now. See the comment at the top of the case. 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 // See the go122-create-syscall-reuse-thread-id test case for more details. 758 pState.status = go122.ProcSyscallAbandoned 759 newCtx.P = NoProc 760 761 // Queue an extra self-ProcSteal event. 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 // Handle tasks. Tasks are interesting because: 772 // - There's no Begin event required to reference a task. 773 // - End for a particular task ID can appear multiple times. 774 // As a result, there's very little to validate. The only 775 // thing we have to be sure of is that a task didn't begin 776 // after it had already begun. Task IDs are allowed to be 777 // reused, so we don't care about a Begin after an End. 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 // Get the parent ID, but don't validate it. There's no guarantee 783 // we actually have information on whether it's active. 784 parentID := TaskID(ev.args[1]) 785 if parentID == BackgroundTask { 786 // Note: a value of 0 here actually means no parent, *not* the 787 // background task. Automatic background task attachment only 788 // applies to regions. 789 parentID = NoTask 790 ev.args[1] = uint64(NoTask) 791 } 792 793 // Validate the name and record it. We'll need to pass it through to 794 // EvUserTaskEnd. 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 // Smuggle the task info. This may happen in a different generation, 812 // which may not have the name in its string table. Add it to the extra 813 // strings table so we can look it up later. 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 // Explicitly clear the task info. 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 // Handle the GC mark phase. 872 // 873 // We have sequence numbers for both start and end because they 874 // can happen on completely different threads. We want an explicit 875 // partial order edge between start and end here, otherwise we're 876 // relying entirely on timestamps to make sure we don't advance a 877 // GCEnd for a _different_ GC cycle if timestamps are wildly broken. 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 // This is not the right GC cycle. 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 // This is not the right GC cycle. 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 // This is not the right GC cycle. 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 // Handle simple instantaneous events that require a G. 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 // Handle allocation metrics, which don't require a G. 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 // Handle sweep, which is bound to a P and doesn't require a G. 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 // N.B. In practice Ps can't block while they're sweeping, so this can only 982 // ever reference curCtx.P. However, be lenient about this like we are with 983 // GCMarkAssistActive; there's no reason the runtime couldn't change to block 984 // in the middle of a sweep. 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 // Handle special goroutine-bound event ranges. 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 // N.B. Like GoStatus, this can happen at any time, because it can 1031 // reference a non-running goroutine. Don't check anything about the 1032 // current scheduler context. 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 // Smuggle the kind into the event. 1058 // Don't use ev.extra here so we have symmetry with STWBegin. 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 // Next returns the next event in the ordering. 1066 func (o *ordering) Next() (Event, bool) { 1067 return o.queue.pop() 1068 } 1069 1070 // schedCtx represents the scheduling resources associated with an event. 1071 type schedCtx struct { 1072 G GoID 1073 P ProcID 1074 M ThreadID 1075 } 1076 1077 // validateCtx ensures that ctx conforms to some reqs, returning an error if 1078 // it doesn't. 1079 func validateCtx(ctx schedCtx, reqs event.SchedReqs) error { 1080 // Check thread requirements. 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 // Check proc requirements. 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 // Check goroutine requirements. 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 // gcState is a trinary variable for the current state of the GC. 1104 // 1105 // The third state besides "enabled" and "disabled" is "undetermined." 1106 type gcState uint8 1107 1108 const ( 1109 gcUndetermined gcState = iota 1110 gcNotRunning 1111 gcRunning 1112 ) 1113 1114 // String returns a human-readable string for the GC state. 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 // userRegion represents a unique user region when attached to some gState. 1128 type userRegion struct { 1129 // name must be a resolved string because the string ID for the same 1130 // string may change across generations, but we care about checking 1131 // the value itself. 1132 taskID TaskID 1133 name string 1134 } 1135 1136 // rangeType is a way to classify special ranges of time. 1137 // 1138 // These typically correspond 1:1 with "Begin" events, but 1139 // they may have an optional subtype that describes the range 1140 // in more detail. 1141 type rangeType struct { 1142 typ event.Type // "Begin" event. 1143 desc stringID // Optional subtype. 1144 } 1145 1146 // makeRangeType constructs a new rangeType. 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 // gState is the state of a goroutine at a point in the trace. 1155 type gState struct { 1156 id GoID 1157 status go122.GoStatus 1158 seq seqCounter 1159 1160 // regions are the active user regions for this goroutine. 1161 regions []userRegion 1162 1163 // rangeState is the state of special time ranges bound to this goroutine. 1164 rangeState 1165 } 1166 1167 // beginRegion starts a user region on the goroutine. 1168 func (s *gState) beginRegion(r userRegion) error { 1169 s.regions = append(s.regions, r) 1170 return nil 1171 } 1172 1173 // endRegion ends a user region on the goroutine. 1174 func (s *gState) endRegion(r userRegion) error { 1175 if len(s.regions) == 0 { 1176 // We do not know about regions that began before tracing started. 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 // pState is the state of a proc at a point in the trace. 1187 type pState struct { 1188 id ProcID 1189 status go122.ProcStatus 1190 seq seqCounter 1191 1192 // rangeState is the state of special time ranges bound to this proc. 1193 rangeState 1194 } 1195 1196 // mState is the state of a thread at a point in the trace. 1197 type mState struct { 1198 g GoID // Goroutine bound to this M. (The goroutine's state is Executing.) 1199 p ProcID // Proc bound to this M. (The proc's state is Executing.) 1200 } 1201 1202 // rangeState represents the state of special time ranges. 1203 type rangeState struct { 1204 // inFlight contains the rangeTypes of any ranges bound to a resource. 1205 inFlight []rangeType 1206 } 1207 1208 // beginRange begins a special range in time on the goroutine. 1209 // 1210 // Returns an error if the range is already in progress. 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 // activeRange marks special range in time on the goroutine as active in the 1220 // initial generation, or confirms that it is indeed active in later generations. 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 // hasRange returns true if a special time range on the goroutine as in progress. 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 // endRange ends a special range in time on the goroutine. 1244 // 1245 // This must line up with the start event type of the range the goroutine is currently in. 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 // Swap remove. 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 // seqCounter represents a global sequence counter for a resource. 1266 type seqCounter struct { 1267 gen uint64 // The generation for the local sequence counter seq. 1268 seq uint64 // The sequence number local to the generation. 1269 } 1270 1271 // makeSeq creates a new seqCounter. 1272 func makeSeq(gen, seq uint64) seqCounter { 1273 return seqCounter{gen: gen, seq: seq} 1274 } 1275 1276 // succeeds returns true if a is the immediate successor of b. 1277 func (a seqCounter) succeeds(b seqCounter) bool { 1278 return a.gen == b.gen && a.seq == b.seq+1 1279 } 1280 1281 // String returns a debug string representation of the seqCounter. 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 // taskState represents an active task. 1305 type taskState struct { 1306 // name is the type of the active task. 1307 name string 1308 1309 // parentID is the parent ID of the active task. 1310 parentID TaskID 1311 } 1312 1313 // queue implements a growable ring buffer with a queue API. 1314 type queue[T any] struct { 1315 start, end int 1316 buf []T 1317 } 1318 1319 // push adds a new event to the back of the queue. 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 // grow increases the size of the queue. 1329 func (q *queue[T]) grow() { 1330 if len(q.buf) == 0 { 1331 q.buf = make([]T, 2) 1332 return 1333 } 1334 1335 // Create new buf and copy data over. 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 // Update the queue state. 1343 q.start = 0 1344 q.end = len(q.buf) 1345 q.buf = newBuf 1346 } 1347 1348 // pop removes an event from the front of the queue. If the 1349 // queue is empty, it returns an EventBad event. 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) // Clear the entry before returning, so we don't hold onto old tables. 1357 q.start++ 1358 return value, true 1359 } 1360 1361 // makeEvent creates an Event from the provided information. 1362 // 1363 // It's just a convenience function; it's always OK to construct 1364 // an Event manually if this isn't quite the right way to express 1365 // the contents of the event. 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 }