package trace
import (
"sort"
"strings"
)
type GDesc struct {
ID uint64
Name string
PC uint64
CreationTime int64
StartTime int64
EndTime int64
Regions []*UserRegionDesc
GExecutionStat
*gdesc
}
type UserRegionDesc struct {
TaskID uint64
Name string
Start *Event
End *Event
GExecutionStat
}
type GExecutionStat struct {
ExecTime int64
SchedWaitTime int64
IOTime int64
BlockTime int64
SyscallTime int64
GCTime int64
SweepTime int64
TotalTime int64
}
func (s GExecutionStat ) sub (v GExecutionStat ) (r GExecutionStat ) {
r = s
r .ExecTime -= v .ExecTime
r .SchedWaitTime -= v .SchedWaitTime
r .IOTime -= v .IOTime
r .BlockTime -= v .BlockTime
r .SyscallTime -= v .SyscallTime
r .GCTime -= v .GCTime
r .SweepTime -= v .SweepTime
r .TotalTime -= v .TotalTime
return r
}
func (g *GDesc ) snapshotStat (lastTs , activeGCStartTime int64 ) (ret GExecutionStat ) {
ret = g .GExecutionStat
if g .gdesc == nil {
return ret
}
if activeGCStartTime != 0 {
if g .CreationTime < activeGCStartTime {
ret .GCTime += lastTs - activeGCStartTime
} else {
ret .GCTime += lastTs - g .CreationTime
}
}
if g .TotalTime == 0 {
ret .TotalTime = lastTs - g .CreationTime
}
if g .lastStartTime != 0 {
ret .ExecTime += lastTs - g .lastStartTime
}
if g .blockNetTime != 0 {
ret .IOTime += lastTs - g .blockNetTime
}
if g .blockSyncTime != 0 {
ret .BlockTime += lastTs - g .blockSyncTime
}
if g .blockSyscallTime != 0 {
ret .SyscallTime += lastTs - g .blockSyscallTime
}
if g .blockSchedTime != 0 {
ret .SchedWaitTime += lastTs - g .blockSchedTime
}
if g .blockSweepTime != 0 {
ret .SweepTime += lastTs - g .blockSweepTime
}
return ret
}
func (g *GDesc ) finalize (lastTs , activeGCStartTime int64 , trigger *Event ) {
if trigger != nil {
g .EndTime = trigger .Ts
}
finalStat := g .snapshotStat (lastTs , activeGCStartTime )
g .GExecutionStat = finalStat
if !IsSystemGoroutine (g .Name ) {
for _ , s := range g .activeRegions {
s .End = trigger
s .GExecutionStat = finalStat .sub (s .GExecutionStat )
g .Regions = append (g .Regions , s )
}
}
*(g .gdesc ) = gdesc {}
}
type gdesc struct {
lastStartTime int64
blockNetTime int64
blockSyncTime int64
blockSyscallTime int64
blockSweepTime int64
blockGCTime int64
blockSchedTime int64
activeRegions []*UserRegionDesc
}
func GoroutineStats (events []*Event ) map [uint64 ]*GDesc {
gs := make (map [uint64 ]*GDesc )
var lastTs int64
var gcStartTime int64
for _ , ev := range events {
lastTs = ev .Ts
switch ev .Type {
case EvGoCreate :
g := &GDesc {ID : ev .Args [0 ], CreationTime : ev .Ts , gdesc : new (gdesc )}
g .blockSchedTime = ev .Ts
if creatorG := gs [ev .G ]; creatorG != nil && len (creatorG .gdesc .activeRegions ) > 0 {
regions := creatorG .gdesc .activeRegions
s := regions [len (regions )-1 ]
if s .TaskID != 0 {
g .gdesc .activeRegions = []*UserRegionDesc {
{TaskID : s .TaskID , Start : ev },
}
}
}
gs [g .ID ] = g
case EvGoStart , EvGoStartLabel :
g := gs [ev .G ]
if g .PC == 0 && len (ev .Stk ) > 0 {
g .PC = ev .Stk [0 ].PC
g .Name = ev .Stk [0 ].Fn
}
g .lastStartTime = ev .Ts
if g .StartTime == 0 {
g .StartTime = ev .Ts
}
if g .blockSchedTime != 0 {
g .SchedWaitTime += ev .Ts - g .blockSchedTime
g .blockSchedTime = 0
}
case EvGoEnd , EvGoStop :
g := gs [ev .G ]
g .finalize (ev .Ts , gcStartTime , ev )
case EvGoBlockSend , EvGoBlockRecv , EvGoBlockSelect ,
EvGoBlockSync , EvGoBlockCond :
g := gs [ev .G ]
g .ExecTime += ev .Ts - g .lastStartTime
g .lastStartTime = 0
g .blockSyncTime = ev .Ts
case EvGoSched , EvGoPreempt :
g := gs [ev .G ]
g .ExecTime += ev .Ts - g .lastStartTime
g .lastStartTime = 0
g .blockSchedTime = ev .Ts
case EvGoSleep , EvGoBlock :
g := gs [ev .G ]
g .ExecTime += ev .Ts - g .lastStartTime
g .lastStartTime = 0
case EvGoBlockNet :
g := gs [ev .G ]
g .ExecTime += ev .Ts - g .lastStartTime
g .lastStartTime = 0
g .blockNetTime = ev .Ts
case EvGoBlockGC :
g := gs [ev .G ]
g .ExecTime += ev .Ts - g .lastStartTime
g .lastStartTime = 0
g .blockGCTime = ev .Ts
case EvGoUnblock :
g := gs [ev .Args [0 ]]
if g .blockNetTime != 0 {
g .IOTime += ev .Ts - g .blockNetTime
g .blockNetTime = 0
}
if g .blockSyncTime != 0 {
g .BlockTime += ev .Ts - g .blockSyncTime
g .blockSyncTime = 0
}
g .blockSchedTime = ev .Ts
case EvGoSysBlock :
g := gs [ev .G ]
g .ExecTime += ev .Ts - g .lastStartTime
g .lastStartTime = 0
g .blockSyscallTime = ev .Ts
case EvGoSysExit :
g := gs [ev .G ]
if g .blockSyscallTime != 0 {
g .SyscallTime += ev .Ts - g .blockSyscallTime
g .blockSyscallTime = 0
}
g .blockSchedTime = ev .Ts
case EvGCSweepStart :
g := gs [ev .G ]
if g != nil {
g .blockSweepTime = ev .Ts
}
case EvGCSweepDone :
g := gs [ev .G ]
if g != nil && g .blockSweepTime != 0 {
g .SweepTime += ev .Ts - g .blockSweepTime
g .blockSweepTime = 0
}
case EvGCStart :
gcStartTime = ev .Ts
case EvGCDone :
for _ , g := range gs {
if g .EndTime != 0 {
continue
}
if gcStartTime < g .CreationTime {
g .GCTime += ev .Ts - g .CreationTime
} else {
g .GCTime += ev .Ts - gcStartTime
}
}
gcStartTime = 0
case EvUserRegion :
g := gs [ev .G ]
switch mode := ev .Args [1 ]; mode {
case 0 :
g .activeRegions = append (g .activeRegions , &UserRegionDesc {
Name : ev .SArgs [0 ],
TaskID : ev .Args [0 ],
Start : ev ,
GExecutionStat : g .snapshotStat (lastTs , gcStartTime ),
})
case 1 :
var sd *UserRegionDesc
if regionStk := g .activeRegions ; len (regionStk ) > 0 {
n := len (regionStk )
sd = regionStk [n -1 ]
regionStk = regionStk [:n -1 ]
g .activeRegions = regionStk
} else {
sd = &UserRegionDesc {
Name : ev .SArgs [0 ],
TaskID : ev .Args [0 ],
}
}
sd .GExecutionStat = g .snapshotStat (lastTs , gcStartTime ).sub (sd .GExecutionStat )
sd .End = ev
g .Regions = append (g .Regions , sd )
}
}
}
for _ , g := range gs {
g .finalize (lastTs , gcStartTime , nil )
sort .Slice (g .Regions , func (i , j int ) bool {
x := g .Regions [i ].Start
y := g .Regions [j ].Start
if x == nil {
return true
}
if y == nil {
return false
}
return x .Ts < y .Ts
})
g .gdesc = nil
}
return gs
}
func RelatedGoroutines (events []*Event , goid uint64 ) map [uint64 ]bool {
gmap := make (map [uint64 ]bool )
gmap [goid ] = true
for i := 0 ; i < 2 ; i ++ {
gmap1 := make (map [uint64 ]bool )
for g := range gmap {
gmap1 [g ] = true
}
for _ , ev := range events {
if ev .Type == EvGoUnblock && gmap [ev .Args [0 ]] {
gmap1 [ev .G ] = true
}
}
gmap = gmap1
}
gmap [0 ] = true
return gmap
}
func IsSystemGoroutine (entryFn string ) bool {
return entryFn == "" || entryFn != "runtime.main" && strings .HasPrefix (entryFn , "runtime." )
}
The pages are generated with Golds v0.6.9-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 @Go100and1 (reachable from the left QR code) to get the latest news of Golds .