package trace
import (
"fmt"
"strings"
"internal/trace/event"
"internal/trace/event/go122"
"internal/trace/version"
)
type ordering struct {
gStates map [GoID ]*gState
pStates map [ProcID ]*pState
mStates map [ThreadID ]*mState
activeTasks map [TaskID ]taskState
gcSeq uint64
gcState gcState
initialGen uint64
queue queue [Event ]
}
func (o *ordering ) Advance (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 ) (bool , error ) {
if o .initialGen == 0 {
o .initialGen = gen
}
var curCtx , newCtx schedCtx
curCtx .M = m
newCtx .M = m
var ms *mState
if m == NoThread {
curCtx .P = NoProc
curCtx .G = NoGoroutine
newCtx = curCtx
} else {
var ok bool
ms , ok = o .mStates [m ]
if !ok {
ms = &mState {
g : NoGoroutine ,
p : NoProc ,
}
o .mStates [m ] = ms
}
curCtx .P = ms .p
curCtx .G = ms .g
newCtx = curCtx
}
f := orderingDispatch [ev .typ ]
if f == nil {
return false , fmt .Errorf ("bad event type found while ordering: %v" , ev .typ )
}
newCtx , ok , err := f (o , ev , evt , m , gen , curCtx )
if err == nil && ok && ms != nil {
ms .p = newCtx .P
ms .g = newCtx .G
}
return ok , err
}
type orderingHandleFunc func (o *ordering , ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error )
var orderingDispatch = [256 ]orderingHandleFunc {
go122 .EvProcsChange : (*ordering ).advanceAnnotation ,
go122 .EvProcStart : (*ordering ).advanceProcStart ,
go122 .EvProcStop : (*ordering ).advanceProcStop ,
go122 .EvProcSteal : (*ordering ).advanceProcSteal ,
go122 .EvProcStatus : (*ordering ).advanceProcStatus ,
go122 .EvGoCreate : (*ordering ).advanceGoCreate ,
go122 .EvGoCreateSyscall : (*ordering ).advanceGoCreateSyscall ,
go122 .EvGoStart : (*ordering ).advanceGoStart ,
go122 .EvGoDestroy : (*ordering ).advanceGoStopExec ,
go122 .EvGoDestroySyscall : (*ordering ).advanceGoDestroySyscall ,
go122 .EvGoStop : (*ordering ).advanceGoStopExec ,
go122 .EvGoBlock : (*ordering ).advanceGoStopExec ,
go122 .EvGoUnblock : (*ordering ).advanceGoUnblock ,
go122 .EvGoSyscallBegin : (*ordering ).advanceGoSyscallBegin ,
go122 .EvGoSyscallEnd : (*ordering ).advanceGoSyscallEnd ,
go122 .EvGoSyscallEndBlocked : (*ordering ).advanceGoSyscallEndBlocked ,
go122 .EvGoStatus : (*ordering ).advanceGoStatus ,
go122 .EvSTWBegin : (*ordering ).advanceGoRangeBegin ,
go122 .EvSTWEnd : (*ordering ).advanceGoRangeEnd ,
go122 .EvGCActive : (*ordering ).advanceGCActive ,
go122 .EvGCBegin : (*ordering ).advanceGCBegin ,
go122 .EvGCEnd : (*ordering ).advanceGCEnd ,
go122 .EvGCSweepActive : (*ordering ).advanceGCSweepActive ,
go122 .EvGCSweepBegin : (*ordering ).advanceGCSweepBegin ,
go122 .EvGCSweepEnd : (*ordering ).advanceGCSweepEnd ,
go122 .EvGCMarkAssistActive : (*ordering ).advanceGoRangeActive ,
go122 .EvGCMarkAssistBegin : (*ordering ).advanceGoRangeBegin ,
go122 .EvGCMarkAssistEnd : (*ordering ).advanceGoRangeEnd ,
go122 .EvHeapAlloc : (*ordering ).advanceHeapMetric ,
go122 .EvHeapGoal : (*ordering ).advanceHeapMetric ,
go122 .EvGoLabel : (*ordering ).advanceAnnotation ,
go122 .EvUserTaskBegin : (*ordering ).advanceUserTaskBegin ,
go122 .EvUserTaskEnd : (*ordering ).advanceUserTaskEnd ,
go122 .EvUserRegionBegin : (*ordering ).advanceUserRegionBegin ,
go122 .EvUserRegionEnd : (*ordering ).advanceUserRegionEnd ,
go122 .EvUserLog : (*ordering ).advanceAnnotation ,
go122 .EvGoSwitch : (*ordering ).advanceGoSwitch ,
go122 .EvGoSwitchDestroy : (*ordering ).advanceGoSwitch ,
go122 .EvGoCreateBlocked : (*ordering ).advanceGoCreate ,
go122 .EvGoStatusStack : (*ordering ).advanceGoStatus ,
go122 .EvSpan : (*ordering ).advanceAllocFree ,
go122 .EvSpanAlloc : (*ordering ).advanceAllocFree ,
go122 .EvSpanFree : (*ordering ).advanceAllocFree ,
go122 .EvHeapObject : (*ordering ).advanceAllocFree ,
go122 .EvHeapObjectAlloc : (*ordering ).advanceAllocFree ,
go122 .EvHeapObjectFree : (*ordering ).advanceAllocFree ,
go122 .EvGoroutineStack : (*ordering ).advanceAllocFree ,
go122 .EvGoroutineStackAlloc : (*ordering ).advanceAllocFree ,
go122 .EvGoroutineStackFree : (*ordering ).advanceAllocFree ,
}
func (o *ordering ) advanceProcStatus (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
pid := ProcID (ev .args [0 ])
status := go122 .ProcStatus (ev .args [1 ])
if int (status ) >= len (go122ProcStatus2ProcState ) {
return curCtx , false , fmt .Errorf ("invalid status for proc %d: %d" , pid , status )
}
oldState := go122ProcStatus2ProcState [status ]
if s , ok := o .pStates [pid ]; ok {
if status == go122 .ProcSyscallAbandoned && s .status == go122 .ProcSyscall {
oldState = ProcRunning
ev .args [1 ] = uint64 (go122 .ProcSyscall )
} else if status == go122 .ProcSyscallAbandoned && s .status == go122 .ProcSyscallAbandoned {
oldState = ProcIdle
ev .args [1 ] = uint64 (go122 .ProcSyscallAbandoned )
} else if s .status != status {
return curCtx , false , fmt .Errorf ("inconsistent status for proc %d: old %v vs. new %v" , pid , s .status , status )
}
s .seq = makeSeq (gen , 0 )
} else {
o .pStates [pid ] = &pState {id : pid , status : status , seq : makeSeq (gen , 0 )}
if gen == o .initialGen {
oldState = ProcUndetermined
} else {
oldState = ProcNotExist
}
}
ev .extra (version .Go122 )[0 ] = uint64 (oldState )
newCtx := curCtx
if status == go122 .ProcRunning || status == go122 .ProcSyscall {
newCtx .P = pid
}
if status == go122 .ProcSyscallAbandoned && oldState == ProcRunning {
found := false
for mid , ms := range o .mStates {
if ms .p == pid {
curCtx .M = mid
curCtx .P = pid
curCtx .G = ms .g
found = true
}
}
if !found {
return curCtx , false , fmt .Errorf ("failed to find sched context for proc %d that's about to be stolen" , pid )
}
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return newCtx , true , nil
}
func (o *ordering ) advanceProcStart (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
pid := ProcID (ev .args [0 ])
seq := makeSeq (gen , ev .args [1 ])
state , ok := o .pStates [pid ]
if !ok || state .status != go122 .ProcIdle || !seq .succeeds (state .seq ) || curCtx .P != NoProc {
return curCtx , false , nil
}
reqs := event .SchedReqs {Thread : event .MustHave , Proc : event .MustNotHave , Goroutine : event .MayHave }
if err := validateCtx (curCtx , reqs ); err != nil {
return curCtx , false , err
}
state .status = go122 .ProcRunning
state .seq = seq
newCtx := curCtx
newCtx .P = pid
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return newCtx , true , nil
}
func (o *ordering ) advanceProcStop (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
state , ok := o .pStates [curCtx .P ]
if !ok {
return curCtx , false , fmt .Errorf ("event %s for proc (%v) that doesn't exist" , go122 .EventString (ev .typ ), curCtx .P )
}
if state .status != go122 .ProcRunning && state .status != go122 .ProcSyscall {
return curCtx , false , fmt .Errorf ("%s event for proc that's not %s or %s" , go122 .EventString (ev .typ ), go122 .ProcRunning , go122 .ProcSyscall )
}
reqs := event .SchedReqs {Thread : event .MustHave , Proc : event .MustHave , Goroutine : event .MayHave }
if err := validateCtx (curCtx , reqs ); err != nil {
return curCtx , false , err
}
state .status = go122 .ProcIdle
newCtx := curCtx
newCtx .P = NoProc
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return newCtx , true , nil
}
func (o *ordering ) advanceProcSteal (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
pid := ProcID (ev .args [0 ])
seq := makeSeq (gen , ev .args [1 ])
state , ok := o .pStates [pid ]
if !ok || (state .status != go122 .ProcSyscall && state .status != go122 .ProcSyscallAbandoned ) || !seq .succeeds (state .seq ) {
return curCtx , false , nil
}
reqs := event .SchedReqs {Thread : event .MustHave , Proc : event .MayHave , Goroutine : event .MayHave }
if err := validateCtx (curCtx , reqs ); err != nil {
return curCtx , false , err
}
oldStatus := state .status
ev .extra (version .Go122 )[0 ] = uint64 (oldStatus )
state .status = go122 .ProcIdle
state .seq = seq
if oldStatus == go122 .ProcSyscallAbandoned {
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
mid := ThreadID (ev .args [2 ])
newCtx := curCtx
if mid == curCtx .M {
if curCtx .P != pid {
return curCtx , false , fmt .Errorf ("tried to self-steal proc %d (thread %d), but got proc %d instead" , pid , mid , curCtx .P )
}
newCtx .P = NoProc
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return newCtx , true , nil
}
mState , ok := o .mStates [mid ]
if !ok {
return curCtx , false , fmt .Errorf ("stole proc from non-existent thread %d" , mid )
}
if mState .p != pid {
return curCtx , false , fmt .Errorf ("tried to steal proc %d from thread %d, but got proc %d instead" , pid , mid , mState .p )
}
mState .p = NoProc
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return newCtx , true , nil
}
func (o *ordering ) advanceGoStatus (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
gid := GoID (ev .args [0 ])
mid := ThreadID (ev .args [1 ])
status := go122 .GoStatus (ev .args [2 ])
if int (status ) >= len (go122GoStatus2GoState ) {
return curCtx , false , fmt .Errorf ("invalid status for goroutine %d: %d" , gid , status )
}
oldState := go122GoStatus2GoState [status ]
if s , ok := o .gStates [gid ]; ok {
if s .status != status {
return curCtx , false , fmt .Errorf ("inconsistent status for goroutine %d: old %v vs. new %v" , gid , s .status , status )
}
s .seq = makeSeq (gen , 0 )
} else if gen == o .initialGen {
o .gStates [gid ] = &gState {id : gid , status : status , seq : makeSeq (gen , 0 )}
oldState = GoUndetermined
} else {
return curCtx , false , fmt .Errorf ("found goroutine status for new goroutine after the first generation: id=%v status=%v" , gid , status )
}
ev .extra (version .Go122 )[0 ] = uint64 (oldState )
newCtx := curCtx
switch status {
case go122 .GoRunning :
newCtx .G = gid
case go122 .GoSyscall :
if mid == NoThread {
return curCtx , false , fmt .Errorf ("found goroutine %d in syscall without a thread" , gid )
}
if mid == curCtx .M {
if gen != o .initialGen && curCtx .G != gid {
return curCtx , false , fmt .Errorf ("inconsistent thread for syscalling goroutine %d: thread has goroutine %d" , gid , curCtx .G )
}
newCtx .G = gid
break
}
ms , ok := o .mStates [mid ]
if ok {
if ms .g != gid {
return curCtx , false , fmt .Errorf ("inconsistent thread for syscalling goroutine %d: thread has goroutine %d" , gid , ms .g )
}
curCtx .G = ms .g
} else if !ok {
o .mStates [mid ] = &mState {g : gid , p : NoProc }
}
curCtx .M = mid
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return newCtx , true , nil
}
func (o *ordering ) advanceGoCreate (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
reqs := event .SchedReqs {Thread : event .MustHave , Proc : event .MustHave , Goroutine : event .MayHave }
if err := validateCtx (curCtx , reqs ); err != nil {
return curCtx , false , err
}
if state , ok := o .gStates [curCtx .G ]; ok && state .status != go122 .GoRunning {
return curCtx , false , fmt .Errorf ("%s event for goroutine that's not %s" , go122 .EventString (ev .typ ), GoRunning )
}
newgid := GoID (ev .args [0 ])
if _ , ok := o .gStates [newgid ]; ok {
return curCtx , false , fmt .Errorf ("tried to create goroutine (%v) that already exists" , newgid )
}
status := go122 .GoRunnable
if ev .typ == go122 .EvGoCreateBlocked {
status = go122 .GoWaiting
}
o .gStates [newgid ] = &gState {id : newgid , status : status , seq : makeSeq (gen , 0 )}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceGoStopExec (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
state , ok := o .gStates [curCtx .G ]
if !ok {
return curCtx , false , fmt .Errorf ("event %s for goroutine (%v) that doesn't exist" , go122 .EventString (ev .typ ), curCtx .G )
}
if state .status != go122 .GoRunning {
return curCtx , false , fmt .Errorf ("%s event for goroutine that's not %s" , go122 .EventString (ev .typ ), GoRunning )
}
newCtx := curCtx
switch ev .typ {
case go122 .EvGoDestroy :
delete (o .gStates , curCtx .G )
newCtx .G = NoGoroutine
case go122 .EvGoStop :
state .status = go122 .GoRunnable
newCtx .G = NoGoroutine
case go122 .EvGoBlock :
state .status = go122 .GoWaiting
newCtx .G = NoGoroutine
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return newCtx , true , nil
}
func (o *ordering ) advanceGoStart (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
gid := GoID (ev .args [0 ])
seq := makeSeq (gen , ev .args [1 ])
state , ok := o .gStates [gid ]
if !ok || state .status != go122 .GoRunnable || !seq .succeeds (state .seq ) {
return curCtx , false , nil
}
reqs := event .SchedReqs {Thread : event .MustHave , Proc : event .MustHave , Goroutine : event .MustNotHave }
if err := validateCtx (curCtx , reqs ); err != nil {
return curCtx , false , err
}
state .status = go122 .GoRunning
state .seq = seq
newCtx := curCtx
newCtx .G = gid
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return newCtx , true , nil
}
func (o *ordering ) advanceGoUnblock (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
gid := GoID (ev .args [0 ])
seq := makeSeq (gen , ev .args [1 ])
state , ok := o .gStates [gid ]
if !ok || state .status != go122 .GoWaiting || !seq .succeeds (state .seq ) {
return curCtx , false , nil
}
state .status = go122 .GoRunnable
state .seq = seq
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceGoSwitch (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
curGState , ok := o .gStates [curCtx .G ]
if !ok {
return curCtx , false , fmt .Errorf ("event %s for goroutine (%v) that doesn't exist" , go122 .EventString (ev .typ ), curCtx .G )
}
if curGState .status != go122 .GoRunning {
return curCtx , false , fmt .Errorf ("%s event for goroutine that's not %s" , go122 .EventString (ev .typ ), GoRunning )
}
nextg := GoID (ev .args [0 ])
seq := makeSeq (gen , ev .args [1 ])
nextGState , ok := o .gStates [nextg ]
if !ok || nextGState .status != go122 .GoWaiting || !seq .succeeds (nextGState .seq ) {
return curCtx , false , nil
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
switch ev .typ {
case go122 .EvGoSwitch :
curGState .status = go122 .GoWaiting
o .queue .push (makeEvent (evt , curCtx , go122 .EvGoBlock , ev .time , 0 , 0 ))
case go122 .EvGoSwitchDestroy :
delete (o .gStates , curCtx .G )
o .queue .push (makeEvent (evt , curCtx , go122 .EvGoDestroy , ev .time ))
}
nextGState .status = go122 .GoRunning
nextGState .seq = seq
newCtx := curCtx
newCtx .G = nextg
startCtx := curCtx
startCtx .G = NoGoroutine
o .queue .push (makeEvent (evt , startCtx , go122 .EvGoStart , ev .time , uint64 (nextg ), ev .args [1 ]))
return newCtx , true , nil
}
func (o *ordering ) advanceGoSyscallBegin (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
state , ok := o .gStates [curCtx .G ]
if !ok {
return curCtx , false , fmt .Errorf ("event %s for goroutine (%v) that doesn't exist" , go122 .EventString (ev .typ ), curCtx .G )
}
if state .status != go122 .GoRunning {
return curCtx , false , fmt .Errorf ("%s event for goroutine that's not %s" , go122 .EventString (ev .typ ), GoRunning )
}
state .status = go122 .GoSyscall
pState , ok := o .pStates [curCtx .P ]
if !ok {
return curCtx , false , fmt .Errorf ("uninitialized proc %d found during %s" , curCtx .P , go122 .EventString (ev .typ ))
}
pState .status = go122 .ProcSyscall
pSeq := makeSeq (gen , ev .args [0 ])
if !pSeq .succeeds (pState .seq ) {
return curCtx , false , fmt .Errorf ("failed to advance %s: can't make sequence: %s -> %s" , go122 .EventString (ev .typ ), pState .seq , pSeq )
}
pState .seq = pSeq
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceGoSyscallEnd (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
state , ok := o .gStates [curCtx .G ]
if !ok {
return curCtx , false , fmt .Errorf ("event %s for goroutine (%v) that doesn't exist" , go122 .EventString (ev .typ ), curCtx .G )
}
if state .status != go122 .GoSyscall {
return curCtx , false , fmt .Errorf ("%s event for goroutine that's not %s" , go122 .EventString (ev .typ ), GoRunning )
}
state .status = go122 .GoRunning
pState , ok := o .pStates [curCtx .P ]
if !ok {
return curCtx , false , fmt .Errorf ("uninitialized proc %d found during %s" , curCtx .P , go122 .EventString (ev .typ ))
}
if pState .status != go122 .ProcSyscall {
return curCtx , false , fmt .Errorf ("expected proc %d in state %v, but got %v instead" , curCtx .P , go122 .ProcSyscall , pState .status )
}
pState .status = go122 .ProcRunning
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceGoSyscallEndBlocked (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if curCtx .P != NoProc {
pState , ok := o .pStates [curCtx .P ]
if !ok {
return curCtx , false , fmt .Errorf ("uninitialized proc %d found during %s" , curCtx .P , go122 .EventString (ev .typ ))
}
if pState .status == go122 .ProcSyscall {
return curCtx , false , nil
}
}
if err := validateCtx (curCtx , event .SchedReqs {Thread : event .MustHave , Proc : event .MayHave , Goroutine : event .MustHave }); err != nil {
return curCtx , false , err
}
state , ok := o .gStates [curCtx .G ]
if !ok {
return curCtx , false , fmt .Errorf ("event %s for goroutine (%v) that doesn't exist" , go122 .EventString (ev .typ ), curCtx .G )
}
if state .status != go122 .GoSyscall {
return curCtx , false , fmt .Errorf ("%s event for goroutine that's not %s" , go122 .EventString (ev .typ ), GoRunning )
}
newCtx := curCtx
newCtx .G = NoGoroutine
state .status = go122 .GoRunnable
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return newCtx , true , nil
}
func (o *ordering ) advanceGoCreateSyscall (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .SchedReqs {Thread : event .MustHave , Proc : event .MayHave , Goroutine : event .MustNotHave }); err != nil {
return curCtx , false , err
}
newgid := GoID (ev .args [0 ])
if _ , ok := o .gStates [newgid ]; ok {
return curCtx , false , fmt .Errorf ("tried to create goroutine (%v) in syscall that already exists" , newgid )
}
o .gStates [newgid ] = &gState {id : newgid , status : go122 .GoSyscall , seq : makeSeq (gen , 0 )}
newCtx := curCtx
newCtx .G = newgid
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return newCtx , true , nil
}
func (o *ordering ) advanceGoDestroySyscall (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .SchedReqs {Thread : event .MustHave , Proc : event .MayHave , Goroutine : event .MustHave }); err != nil {
return curCtx , false , err
}
state , ok := o .gStates [curCtx .G ]
if !ok {
return curCtx , false , fmt .Errorf ("event %s for goroutine (%v) that doesn't exist" , go122 .EventString (ev .typ ), curCtx .G )
}
if state .status != go122 .GoSyscall {
return curCtx , false , fmt .Errorf ("%s event for goroutine that's not %v" , go122 .EventString (ev .typ ), GoSyscall )
}
delete (o .gStates , curCtx .G )
newCtx := curCtx
newCtx .G = NoGoroutine
if curCtx .P != NoProc {
pState , ok := o .pStates [curCtx .P ]
if !ok {
return curCtx , false , fmt .Errorf ("found invalid proc %d during %s" , curCtx .P , go122 .EventString (ev .typ ))
}
if pState .status != go122 .ProcSyscall {
return curCtx , false , fmt .Errorf ("proc %d in unexpected state %s during %s" , curCtx .P , pState .status , go122 .EventString (ev .typ ))
}
pState .status = go122 .ProcSyscallAbandoned
newCtx .P = NoProc
extra := makeEvent (evt , curCtx , go122 .EvProcSteal , ev .time , uint64 (curCtx .P ))
extra .base .extra (version .Go122 )[0 ] = uint64 (go122 .ProcSyscall )
o .queue .push (extra )
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return newCtx , true , nil
}
func (o *ordering ) advanceUserTaskBegin (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
id := TaskID (ev .args [0 ])
if _ , ok := o .activeTasks [id ]; ok {
return curCtx , false , fmt .Errorf ("task ID conflict: %d" , id )
}
parentID := TaskID (ev .args [1 ])
if parentID == BackgroundTask {
parentID = NoTask
ev .args [1 ] = uint64 (NoTask )
}
nameID := stringID (ev .args [2 ])
name , ok := evt .strings .get (nameID )
if !ok {
return curCtx , false , fmt .Errorf ("invalid string ID %v for %v event" , nameID , ev .typ )
}
o .activeTasks [id ] = taskState {name : name , parentID : parentID }
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceUserTaskEnd (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
id := TaskID (ev .args [0 ])
if ts , ok := o .activeTasks [id ]; ok {
ev .extra (version .Go122 )[0 ] = uint64 (ts .parentID )
ev .extra (version .Go122 )[1 ] = uint64 (evt .addExtraString (ts .name ))
delete (o .activeTasks , id )
} else {
ev .extra (version .Go122 )[0 ] = uint64 (NoTask )
ev .extra (version .Go122 )[1 ] = uint64 (evt .addExtraString ("" ))
}
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceUserRegionBegin (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
tid := TaskID (ev .args [0 ])
nameID := stringID (ev .args [1 ])
name , ok := evt .strings .get (nameID )
if !ok {
return curCtx , false , fmt .Errorf ("invalid string ID %v for %v event" , nameID , ev .typ )
}
gState , ok := o .gStates [curCtx .G ]
if !ok {
return curCtx , false , fmt .Errorf ("encountered EvUserRegionBegin without known state for current goroutine %d" , curCtx .G )
}
if err := gState .beginRegion (userRegion {tid , name }); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceUserRegionEnd (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
tid := TaskID (ev .args [0 ])
nameID := stringID (ev .args [1 ])
name , ok := evt .strings .get (nameID )
if !ok {
return curCtx , false , fmt .Errorf ("invalid string ID %v for %v event" , nameID , ev .typ )
}
gState , ok := o .gStates [curCtx .G ]
if !ok {
return curCtx , false , fmt .Errorf ("encountered EvUserRegionEnd without known state for current goroutine %d" , curCtx .G )
}
if err := gState .endRegion (userRegion {tid , name }); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceGCActive (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
seq := ev .args [0 ]
if gen == o .initialGen {
if o .gcState != gcUndetermined {
return curCtx , false , fmt .Errorf ("GCActive in the first generation isn't first GC event" )
}
o .gcSeq = seq
o .gcState = gcRunning
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
if seq != o .gcSeq +1 {
return curCtx , false , nil
}
if o .gcState != gcRunning {
return curCtx , false , fmt .Errorf ("encountered GCActive while GC was not in progress" )
}
o .gcSeq = seq
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceGCBegin (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
seq := ev .args [0 ]
if o .gcState == gcUndetermined {
o .gcSeq = seq
o .gcState = gcRunning
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
if seq != o .gcSeq +1 {
return curCtx , false , nil
}
if o .gcState == gcRunning {
return curCtx , false , fmt .Errorf ("encountered GCBegin while GC was already in progress" )
}
o .gcSeq = seq
o .gcState = gcRunning
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceGCEnd (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
seq := ev .args [0 ]
if seq != o .gcSeq +1 {
return curCtx , false , nil
}
if o .gcState == gcNotRunning {
return curCtx , false , fmt .Errorf ("encountered GCEnd when GC was not in progress" )
}
if o .gcState == gcUndetermined {
return curCtx , false , fmt .Errorf ("encountered GCEnd when GC was in an undetermined state" )
}
o .gcSeq = seq
o .gcState = gcNotRunning
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceAnnotation (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceHeapMetric (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .SchedReqs {Thread : event .MustHave , Proc : event .MustHave , Goroutine : event .MayHave }); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceGCSweepBegin (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .SchedReqs {Thread : event .MustHave , Proc : event .MustHave , Goroutine : event .MayHave }); err != nil {
return curCtx , false , err
}
if err := o .pStates [curCtx .P ].beginRange (makeRangeType (ev .typ , 0 )); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceGCSweepActive (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
pid := ProcID (ev .args [0 ])
pState , ok := o .pStates [pid ]
if !ok {
return curCtx , false , fmt .Errorf ("encountered GCSweepActive for unknown proc %d" , pid )
}
if err := pState .activeRange (makeRangeType (ev .typ , 0 ), gen == o .initialGen ); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceGCSweepEnd (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .SchedReqs {Thread : event .MustHave , Proc : event .MustHave , Goroutine : event .MayHave }); err != nil {
return curCtx , false , err
}
_ , err := o .pStates [curCtx .P ].endRange (ev .typ )
if err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceGoRangeBegin (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
desc := stringID (0 )
if ev .typ == go122 .EvSTWBegin {
desc = stringID (ev .args [0 ])
}
gState , ok := o .gStates [curCtx .G ]
if !ok {
return curCtx , false , fmt .Errorf ("encountered event of type %d without known state for current goroutine %d" , ev .typ , curCtx .G )
}
if err := gState .beginRange (makeRangeType (ev .typ , desc )); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceGoRangeActive (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
gid := GoID (ev .args [0 ])
gState , ok := o .gStates [gid ]
if !ok {
return curCtx , false , fmt .Errorf ("uninitialized goroutine %d found during %s" , gid , go122 .EventString (ev .typ ))
}
if err := gState .activeRange (makeRangeType (ev .typ , 0 ), gen == o .initialGen ); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceGoRangeEnd (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .UserGoReqs ); err != nil {
return curCtx , false , err
}
gState , ok := o .gStates [curCtx .G ]
if !ok {
return curCtx , false , fmt .Errorf ("encountered event of type %d without known state for current goroutine %d" , ev .typ , curCtx .G )
}
desc , err := gState .endRange (ev .typ )
if err != nil {
return curCtx , false , err
}
if ev .typ == go122 .EvSTWEnd {
ev .args [0 ] = uint64 (desc )
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) advanceAllocFree (ev *baseEvent , evt *evTable , m ThreadID , gen uint64 , curCtx schedCtx ) (schedCtx , bool , error ) {
if err := validateCtx (curCtx , event .SchedReqs {Thread : event .MustHave , Proc : event .MayHave , Goroutine : event .MayHave }); err != nil {
return curCtx , false , err
}
o .queue .push (Event {table : evt , ctx : curCtx , base : *ev })
return curCtx , true , nil
}
func (o *ordering ) Next () (Event , bool ) {
return o .queue .pop ()
}
type schedCtx struct {
G GoID
P ProcID
M ThreadID
}
func validateCtx(ctx schedCtx , reqs event .SchedReqs ) error {
if reqs .Thread == event .MustHave && ctx .M == NoThread {
return fmt .Errorf ("expected a thread but didn't have one" )
} else if reqs .Thread == event .MustNotHave && ctx .M != NoThread {
return fmt .Errorf ("expected no thread but had one" )
}
if reqs .Proc == event .MustHave && ctx .P == NoProc {
return fmt .Errorf ("expected a proc but didn't have one" )
} else if reqs .Proc == event .MustNotHave && ctx .P != NoProc {
return fmt .Errorf ("expected no proc but had one" )
}
if reqs .Goroutine == event .MustHave && ctx .G == NoGoroutine {
return fmt .Errorf ("expected a goroutine but didn't have one" )
} else if reqs .Goroutine == event .MustNotHave && ctx .G != NoGoroutine {
return fmt .Errorf ("expected no goroutine but had one" )
}
return nil
}
type gcState uint8
const (
gcUndetermined gcState = iota
gcNotRunning
gcRunning
)
func (s gcState ) String () string {
switch s {
case gcUndetermined :
return "Undetermined"
case gcNotRunning :
return "NotRunning"
case gcRunning :
return "Running"
}
return "Bad"
}
type userRegion struct {
taskID TaskID
name string
}
type rangeType struct {
typ event .Type
desc stringID
}
func makeRangeType(typ event .Type , desc stringID ) rangeType {
if styp := go122 .Specs ()[typ ].StartEv ; styp != go122 .EvNone {
typ = styp
}
return rangeType {typ , desc }
}
type gState struct {
id GoID
status go122 .GoStatus
seq seqCounter
regions []userRegion
rangeState
}
func (s *gState ) beginRegion (r userRegion ) error {
s .regions = append (s .regions , r )
return nil
}
func (s *gState ) endRegion (r userRegion ) error {
if len (s .regions ) == 0 {
return nil
}
if next := s .regions [len (s .regions )-1 ]; next != r {
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 )
}
s .regions = s .regions [:len (s .regions )-1 ]
return nil
}
type pState struct {
id ProcID
status go122 .ProcStatus
seq seqCounter
rangeState
}
type mState struct {
g GoID
p ProcID
}
type rangeState struct {
inFlight []rangeType
}
func (s *rangeState ) beginRange (typ rangeType ) error {
if s .hasRange (typ ) {
return fmt .Errorf ("discovered event already in-flight for when starting event %v" , go122 .Specs ()[typ .typ ].Name )
}
s .inFlight = append (s .inFlight , typ )
return nil
}
func (s *rangeState ) activeRange (typ rangeType , isInitialGen bool ) error {
if isInitialGen {
if s .hasRange (typ ) {
return fmt .Errorf ("found named active range already in first gen: %v" , typ )
}
s .inFlight = append (s .inFlight , typ )
} else if !s .hasRange (typ ) {
return fmt .Errorf ("resource is missing active range: %v %v" , go122 .Specs ()[typ .typ ].Name , s .inFlight )
}
return nil
}
func (s *rangeState ) hasRange (typ rangeType ) bool {
for _ , ftyp := range s .inFlight {
if ftyp == typ {
return true
}
}
return false
}
func (s *rangeState ) endRange (typ event .Type ) (stringID , error ) {
st := go122 .Specs ()[typ ].StartEv
idx := -1
for i , r := range s .inFlight {
if r .typ == st {
idx = i
break
}
}
if idx < 0 {
return 0 , fmt .Errorf ("tried to end event %v, but not in-flight" , go122 .Specs ()[st ].Name )
}
desc := s .inFlight [idx ].desc
s .inFlight [idx ], s .inFlight [len (s .inFlight )-1 ] = s .inFlight [len (s .inFlight )-1 ], s .inFlight [idx ]
s .inFlight = s .inFlight [:len (s .inFlight )-1 ]
return desc , nil
}
type seqCounter struct {
gen uint64
seq uint64
}
func makeSeq(gen , seq uint64 ) seqCounter {
return seqCounter {gen : gen , seq : seq }
}
func (a seqCounter ) succeeds (b seqCounter ) bool {
return a .gen == b .gen && a .seq == b .seq +1
}
func (c seqCounter ) String () string {
return fmt .Sprintf ("%d (gen=%d)" , c .seq , c .gen )
}
func dumpOrdering(order *ordering ) string {
var sb strings .Builder
for id , state := range order .gStates {
fmt .Fprintf (&sb , "G %d [status=%s seq=%s]\n" , id , state .status , state .seq )
}
fmt .Fprintln (&sb )
for id , state := range order .pStates {
fmt .Fprintf (&sb , "P %d [status=%s seq=%s]\n" , id , state .status , state .seq )
}
fmt .Fprintln (&sb )
for id , state := range order .mStates {
fmt .Fprintf (&sb , "M %d [g=%d p=%d]\n" , id , state .g , state .p )
}
fmt .Fprintln (&sb )
fmt .Fprintf (&sb , "GC %d %s\n" , order .gcSeq , order .gcState )
return sb .String ()
}
type taskState struct {
name string
parentID TaskID
}
type queue[T any ] struct {
start, end int
buf []T
}
func (q *queue [T ]) push (value T ) {
if q .end -q .start == len (q .buf ) {
q .grow ()
}
q .buf [q .end %len (q .buf )] = value
q .end ++
}
func (q *queue [T ]) grow () {
if len (q .buf ) == 0 {
q .buf = make ([]T , 2 )
return
}
newBuf := make ([]T , len (q .buf )*2 )
pivot := q .start % len (q .buf )
first , last := q .buf [pivot :], q .buf [:pivot ]
copy (newBuf [:len (first )], first )
copy (newBuf [len (first ):], last )
q .start = 0
q .end = len (q .buf )
q .buf = newBuf
}
func (q *queue [T ]) pop () (T , bool ) {
if q .end -q .start == 0 {
return *new (T ), false
}
elem := &q .buf [q .start %len (q .buf )]
value := *elem
*elem = *new (T )
q .start ++
return value , true
}
func makeEvent(table *evTable , ctx schedCtx , typ event .Type , time Time , args ...uint64 ) Event {
ev := Event {
table : table ,
ctx : ctx ,
base : baseEvent {
typ : typ ,
time : time ,
},
}
copy (ev .base .args [:], args )
return ev
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .