package trace
import (
"errors"
"fmt"
"internal/trace/event"
"internal/trace/event/go122"
"internal/trace/internal/oldtrace"
"io"
)
type oldTraceConverter struct {
trace oldtrace .Trace
evt *evTable
preInit bool
createdPreInit map [GoID ]struct {}
events oldtrace .Events
extra []Event
extraArr [3 ]Event
tasks map [TaskID ]taskState
seenProcs map [ProcID ]struct {}
lastTs Time
procMs map [ProcID ]ThreadID
lastStwReason uint64
inlineToStringID []uint64
builtinToStringID []uint64
}
const (
sForever = iota
sPreempted
sGosched
sSleep
sChanSend
sChanRecv
sNetwork
sSync
sSyncCond
sSelect
sEmpty
sMarkAssistWait
sSTWUnknown
sSTWGCMarkTermination
sSTWGCSweepTermination
sSTWWriteHeapDump
sSTWGoroutineProfile
sSTWGoroutineProfileCleanup
sSTWAllGoroutinesStackTrace
sSTWReadMemStats
sSTWAllThreadsSyscall
sSTWGOMAXPROCS
sSTWStartTrace
sSTWStopTrace
sSTWCountPagesInUse
sSTWReadMetricsSlow
sSTWReadMemStatsSlow
sSTWPageCachePagesLeaked
sSTWResetDebugLog
sLast
)
func (it *oldTraceConverter ) init (pr oldtrace .Trace ) error {
it .trace = pr
it .preInit = true
it .createdPreInit = make (map [GoID ]struct {})
it .evt = &evTable {pcs : make (map [uint64 ]frame )}
it .events = pr .Events
it .extra = it .extraArr [:0 ]
it .tasks = make (map [TaskID ]taskState )
it .seenProcs = make (map [ProcID ]struct {})
it .procMs = make (map [ProcID ]ThreadID )
it .lastTs = -1
evt := it .evt
var max uint64
for id , s := range pr .Strings {
evt .strings .insert (stringID (id ), s )
if id > max {
max = id
}
}
pr .Strings = nil
if max +uint64 (len (pr .InlineStrings )) < max {
return errors .New ("trace contains too many strings" )
}
var addErr error
add := func (id stringID , s string ) {
if err := evt .strings .insert (id , s ); err != nil && addErr == nil {
addErr = err
}
}
for id , s := range pr .InlineStrings {
nid := max + 1 + uint64 (id )
it .inlineToStringID = append (it .inlineToStringID , nid )
add (stringID (nid ), s )
}
max += uint64 (len (pr .InlineStrings ))
pr .InlineStrings = nil
if max +uint64 (sLast ) < max {
return errors .New ("trace contains too many strings" )
}
it .builtinToStringID = make ([]uint64 , sLast )
addBuiltin := func (c int , s string ) {
nid := max + 1 + uint64 (c )
it .builtinToStringID [c ] = nid
add (stringID (nid ), s )
}
addBuiltin (sForever , "forever" )
addBuiltin (sPreempted , "preempted" )
addBuiltin (sGosched , "runtime.Gosched" )
addBuiltin (sSleep , "sleep" )
addBuiltin (sChanSend , "chan send" )
addBuiltin (sChanRecv , "chan receive" )
addBuiltin (sNetwork , "network" )
addBuiltin (sSync , "sync" )
addBuiltin (sSyncCond , "sync.(*Cond).Wait" )
addBuiltin (sSelect , "select" )
addBuiltin (sEmpty , "" )
addBuiltin (sMarkAssistWait , "GC mark assist wait for work" )
addBuiltin (sSTWUnknown , "" )
addBuiltin (sSTWGCMarkTermination , "GC mark termination" )
addBuiltin (sSTWGCSweepTermination , "GC sweep termination" )
addBuiltin (sSTWWriteHeapDump , "write heap dump" )
addBuiltin (sSTWGoroutineProfile , "goroutine profile" )
addBuiltin (sSTWGoroutineProfileCleanup , "goroutine profile cleanup" )
addBuiltin (sSTWAllGoroutinesStackTrace , "all goroutine stack trace" )
addBuiltin (sSTWReadMemStats , "read mem stats" )
addBuiltin (sSTWAllThreadsSyscall , "AllThreadsSyscall" )
addBuiltin (sSTWGOMAXPROCS , "GOMAXPROCS" )
addBuiltin (sSTWStartTrace , "start trace" )
addBuiltin (sSTWStopTrace , "stop trace" )
addBuiltin (sSTWCountPagesInUse , "CountPagesInUse (test)" )
addBuiltin (sSTWReadMetricsSlow , "ReadMetricsSlow (test)" )
addBuiltin (sSTWReadMemStatsSlow , "ReadMemStatsSlow (test)" )
addBuiltin (sSTWPageCachePagesLeaked , "PageCachePagesLeaked (test)" )
addBuiltin (sSTWResetDebugLog , "ResetDebugLog (test)" )
if addErr != nil {
return fmt .Errorf ("couldn't add strings: %w" , addErr )
}
it .evt .strings .compactify ()
for id , stk := range pr .Stacks {
evt .stacks .insert (stackID (id ), stack {pcs : stk })
}
for pc , f := range pr .PCs {
evt .pcs [pc ] = frame {
pc : pc ,
funcID : stringID (f .Fn ),
fileID : stringID (f .File ),
line : uint64 (f .Line ),
}
}
pr .Stacks = nil
pr .PCs = nil
evt .stacks .compactify ()
return nil
}
func (it *oldTraceConverter ) next () (Event , error ) {
if len (it .extra ) > 0 {
ev := it .extra [0 ]
it .extra = it .extra [1 :]
if len (it .extra ) == 0 {
it .extra = it .extraArr [:0 ]
}
if ev .base .time <= it .lastTs {
ev .base .time = it .lastTs + 1
}
it .lastTs = ev .base .time
return ev , nil
}
oev , ok := it .events .Pop ()
if !ok {
return Event {}, io .EOF
}
ev , err := it .convertEvent (oev )
if err == errSkip {
return it .next ()
} else if err != nil {
return Event {}, err
}
if ev .base .time <= it .lastTs {
ev .base .time = it .lastTs + 1
}
it .lastTs = ev .base .time
return ev , nil
}
var errSkip = errors .New ("skip event" )
func (it *oldTraceConverter ) convertEvent (ev *oldtrace .Event ) (OUT Event , ERR error ) {
var mappedType event .Type
var mappedArgs timedEventArgs
copy (mappedArgs [:], ev .Args [:])
switch ev .Type {
case oldtrace .EvGomaxprocs :
mappedType = go122 .EvProcsChange
if it .preInit {
it .preInit = false
for gid := range it .createdPreInit {
it .extra = append (it .extra , Event {
ctx : schedCtx {
G : NoGoroutine ,
P : NoProc ,
M : NoThread ,
},
table : it .evt ,
base : baseEvent {
typ : go122 .EvGoStatus ,
time : Time (ev .Ts ),
args : timedEventArgs {uint64 (gid ), ^uint64 (0 ), uint64 (go122 .GoRunnable )},
},
})
}
it .createdPreInit = nil
return Event {}, errSkip
}
case oldtrace .EvProcStart :
it .procMs [ProcID (ev .P )] = ThreadID (ev .Args [0 ])
if _ , ok := it .seenProcs [ProcID (ev .P )]; ok {
mappedType = go122 .EvProcStart
mappedArgs = timedEventArgs {uint64 (ev .P )}
} else {
it .seenProcs [ProcID (ev .P )] = struct {}{}
mappedType = go122 .EvProcStatus
mappedArgs = timedEventArgs {uint64 (ev .P ), uint64 (go122 .ProcRunning )}
}
case oldtrace .EvProcStop :
if _ , ok := it .seenProcs [ProcID (ev .P )]; ok {
mappedType = go122 .EvProcStop
mappedArgs = timedEventArgs {uint64 (ev .P )}
} else {
it .seenProcs [ProcID (ev .P )] = struct {}{}
mappedType = go122 .EvProcStatus
mappedArgs = timedEventArgs {uint64 (ev .P ), uint64 (go122 .ProcIdle )}
}
case oldtrace .EvGCStart :
mappedType = go122 .EvGCBegin
case oldtrace .EvGCDone :
mappedType = go122 .EvGCEnd
case oldtrace .EvSTWStart :
sid := it .builtinToStringID [sSTWUnknown +it .trace .STWReason (ev .Args [0 ])]
it .lastStwReason = sid
mappedType = go122 .EvSTWBegin
mappedArgs = timedEventArgs {uint64 (sid )}
case oldtrace .EvSTWDone :
mappedType = go122 .EvSTWEnd
mappedArgs = timedEventArgs {it .lastStwReason }
case oldtrace .EvGCSweepStart :
mappedType = go122 .EvGCSweepBegin
case oldtrace .EvGCSweepDone :
mappedType = go122 .EvGCSweepEnd
case oldtrace .EvGoCreate :
if it .preInit {
it .createdPreInit [GoID (ev .Args [0 ])] = struct {}{}
return Event {}, errSkip
}
mappedType = go122 .EvGoCreate
case oldtrace .EvGoStart :
if it .preInit {
mappedType = go122 .EvGoStatus
mappedArgs = timedEventArgs {ev .Args [0 ], ^uint64 (0 ), uint64 (go122 .GoRunning )}
delete (it .createdPreInit , GoID (ev .Args [0 ]))
} else {
mappedType = go122 .EvGoStart
}
case oldtrace .EvGoStartLabel :
it .extra = []Event {{
ctx : schedCtx {
G : GoID (ev .G ),
P : ProcID (ev .P ),
M : it .procMs [ProcID (ev .P )],
},
table : it .evt ,
base : baseEvent {
typ : go122 .EvGoLabel ,
time : Time (ev .Ts ),
args : timedEventArgs {ev .Args [2 ]},
},
}}
return Event {
ctx : schedCtx {
G : GoID (ev .G ),
P : ProcID (ev .P ),
M : it .procMs [ProcID (ev .P )],
},
table : it .evt ,
base : baseEvent {
typ : go122 .EvGoStart ,
time : Time (ev .Ts ),
args : mappedArgs ,
},
}, nil
case oldtrace .EvGoEnd :
mappedType = go122 .EvGoDestroy
case oldtrace .EvGoStop :
mappedType = go122 .EvGoBlock
mappedArgs = timedEventArgs {uint64 (it .builtinToStringID [sForever ]), uint64 (ev .StkID )}
case oldtrace .EvGoSched :
mappedType = go122 .EvGoStop
mappedArgs = timedEventArgs {uint64 (it .builtinToStringID [sGosched ]), uint64 (ev .StkID )}
case oldtrace .EvGoPreempt :
mappedType = go122 .EvGoStop
mappedArgs = timedEventArgs {uint64 (it .builtinToStringID [sPreempted ]), uint64 (ev .StkID )}
case oldtrace .EvGoSleep :
mappedType = go122 .EvGoBlock
mappedArgs = timedEventArgs {uint64 (it .builtinToStringID [sSleep ]), uint64 (ev .StkID )}
case oldtrace .EvGoBlock :
mappedType = go122 .EvGoBlock
mappedArgs = timedEventArgs {uint64 (it .builtinToStringID [sEmpty ]), uint64 (ev .StkID )}
case oldtrace .EvGoUnblock :
mappedType = go122 .EvGoUnblock
case oldtrace .EvGoBlockSend :
mappedType = go122 .EvGoBlock
mappedArgs = timedEventArgs {uint64 (it .builtinToStringID [sChanSend ]), uint64 (ev .StkID )}
case oldtrace .EvGoBlockRecv :
mappedType = go122 .EvGoBlock
mappedArgs = timedEventArgs {uint64 (it .builtinToStringID [sChanRecv ]), uint64 (ev .StkID )}
case oldtrace .EvGoBlockSelect :
mappedType = go122 .EvGoBlock
mappedArgs = timedEventArgs {uint64 (it .builtinToStringID [sSelect ]), uint64 (ev .StkID )}
case oldtrace .EvGoBlockSync :
mappedType = go122 .EvGoBlock
mappedArgs = timedEventArgs {uint64 (it .builtinToStringID [sSync ]), uint64 (ev .StkID )}
case oldtrace .EvGoBlockCond :
mappedType = go122 .EvGoBlock
mappedArgs = timedEventArgs {uint64 (it .builtinToStringID [sSyncCond ]), uint64 (ev .StkID )}
case oldtrace .EvGoBlockNet :
mappedType = go122 .EvGoBlock
mappedArgs = timedEventArgs {uint64 (it .builtinToStringID [sNetwork ]), uint64 (ev .StkID )}
case oldtrace .EvGoBlockGC :
mappedType = go122 .EvGoBlock
mappedArgs = timedEventArgs {uint64 (it .builtinToStringID [sMarkAssistWait ]), uint64 (ev .StkID )}
case oldtrace .EvGoSysCall :
blocked := false
it .events .All ()(func (nev *oldtrace .Event ) bool {
if nev .G != ev .G {
return true
}
if nev .Type == oldtrace .EvGoSysBlock {
blocked = true
}
return false
})
if blocked {
mappedType = go122 .EvGoSyscallBegin
mappedArgs = timedEventArgs {1 : uint64 (ev .StkID )}
} else {
out1 := Event {
ctx : schedCtx {
G : GoID (ev .G ),
P : ProcID (ev .P ),
M : it .procMs [ProcID (ev .P )],
},
table : it .evt ,
base : baseEvent {
typ : go122 .EvGoSyscallBegin ,
time : Time (ev .Ts ),
args : timedEventArgs {1 : uint64 (ev .StkID )},
},
}
out2 := Event {
ctx : out1 .ctx ,
table : it .evt ,
base : baseEvent {
typ : go122 .EvGoSyscallEnd ,
time : Time (ev .Ts + 1 ),
args : timedEventArgs {},
},
}
it .extra = append (it .extra , out2 )
return out1 , nil
}
case oldtrace .EvGoSysExit :
mappedType = go122 .EvGoSyscallEndBlocked
case oldtrace .EvGoSysBlock :
return Event {}, errSkip
case oldtrace .EvGoWaiting :
mappedType = go122 .EvGoStatus
mappedArgs = timedEventArgs {ev .Args [0 ], ^uint64 (0 ), uint64 (go122 .GoWaiting )}
delete (it .createdPreInit , GoID (ev .Args [0 ]))
case oldtrace .EvGoInSyscall :
mappedType = go122 .EvGoStatus
mappedArgs = timedEventArgs {ev .Args [0 ], ^uint64 (0 ), uint64 (go122 .GoSyscall )}
delete (it .createdPreInit , GoID (ev .Args [0 ]))
case oldtrace .EvHeapAlloc :
mappedType = go122 .EvHeapAlloc
case oldtrace .EvHeapGoal :
mappedType = go122 .EvHeapGoal
case oldtrace .EvGCMarkAssistStart :
mappedType = go122 .EvGCMarkAssistBegin
case oldtrace .EvGCMarkAssistDone :
mappedType = go122 .EvGCMarkAssistEnd
case oldtrace .EvUserTaskCreate :
mappedType = go122 .EvUserTaskBegin
parent := ev .Args [1 ]
if parent == 0 {
parent = uint64 (NoTask )
}
mappedArgs = timedEventArgs {ev .Args [0 ], parent , ev .Args [2 ], uint64 (ev .StkID )}
name , _ := it .evt .strings .get (stringID (ev .Args [2 ]))
it .tasks [TaskID (ev .Args [0 ])] = taskState {name : name , parentID : TaskID (ev .Args [1 ])}
case oldtrace .EvUserTaskEnd :
mappedType = go122 .EvUserTaskEnd
ts , ok := it .tasks [TaskID (ev .Args [0 ])]
if ok {
delete (it .tasks , TaskID (ev .Args [0 ]))
mappedArgs = timedEventArgs {
ev .Args [0 ],
ev .Args [1 ],
uint64 (ts .parentID ),
uint64 (it .evt .addExtraString (ts .name )),
}
} else {
mappedArgs = timedEventArgs {ev .Args [0 ], ev .Args [1 ], uint64 (NoTask ), uint64 (it .evt .addExtraString ("" ))}
}
case oldtrace .EvUserRegion :
switch ev .Args [1 ] {
case 0 :
mappedType = go122 .EvUserRegionBegin
case 1 :
mappedType = go122 .EvUserRegionEnd
}
mappedArgs = timedEventArgs {ev .Args [0 ], ev .Args [2 ], uint64 (ev .StkID )}
case oldtrace .EvUserLog :
mappedType = go122 .EvUserLog
mappedArgs = timedEventArgs {ev .Args [0 ], ev .Args [1 ], it .inlineToStringID [ev .Args [3 ]], uint64 (ev .StkID )}
case oldtrace .EvCPUSample :
mappedType = go122 .EvCPUSample
mappedArgs = timedEventArgs {uint64 (ev .StkID ), ^uint64 (0 ), uint64 (ev .P ), ev .G }
default :
return Event {}, fmt .Errorf ("unexpected event type %v" , ev .Type )
}
if oldtrace .EventDescriptions [ev .Type ].Stack {
if stackIDs := go122 .Specs ()[mappedType ].StackIDs ; len (stackIDs ) > 0 {
mappedArgs [stackIDs [0 ]-1 ] = uint64 (ev .StkID )
}
}
m := NoThread
if ev .P != -1 && ev .Type != oldtrace .EvCPUSample {
if t , ok := it .procMs [ProcID (ev .P )]; ok {
m = ThreadID (t )
}
}
if ev .Type == oldtrace .EvProcStop {
delete (it .procMs , ProcID (ev .P ))
}
g := GoID (ev .G )
if g == 0 {
g = NoGoroutine
}
out := Event {
ctx : schedCtx {
G : GoID (g ),
P : ProcID (ev .P ),
M : m ,
},
table : it .evt ,
base : baseEvent {
typ : mappedType ,
time : Time (ev .Ts ),
args : mappedArgs ,
},
}
return out , nil
}
func convertOldFormat(pr oldtrace .Trace ) *oldTraceConverter {
it := &oldTraceConverter {}
it .init (pr )
return it
}
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 .