package trace
import (
"fmt"
"math"
"strings"
"time"
"internal/trace/event"
"internal/trace/event/go122"
"internal/trace/version"
)
type EventKind uint16
const (
EventBad EventKind = iota
EventSync
EventMetric
EventLabel
EventStackSample
EventRangeBegin
EventRangeActive
EventRangeEnd
EventTaskBegin
EventTaskEnd
EventRegionBegin
EventRegionEnd
EventLog
EventStateTransition
EventExperimental
)
func (e EventKind ) String () string {
if int (e ) >= len (eventKindStrings ) {
return eventKindStrings [0 ]
}
return eventKindStrings [e ]
}
var eventKindStrings = [...]string {
EventBad : "Bad" ,
EventSync : "Sync" ,
EventMetric : "Metric" ,
EventLabel : "Label" ,
EventStackSample : "StackSample" ,
EventRangeBegin : "RangeBegin" ,
EventRangeActive : "RangeActive" ,
EventRangeEnd : "RangeEnd" ,
EventTaskBegin : "TaskBegin" ,
EventTaskEnd : "TaskEnd" ,
EventRegionBegin : "RegionBegin" ,
EventRegionEnd : "RegionEnd" ,
EventLog : "Log" ,
EventStateTransition : "StateTransition" ,
EventExperimental : "Experimental" ,
}
const maxTime = Time (math .MaxInt64 )
type Time int64
func (t Time ) Sub (t0 Time ) time .Duration {
return time .Duration (int64 (t ) - int64 (t0 ))
}
type Metric struct {
Name string
Value Value
}
type Label struct {
Label string
Resource ResourceID
}
type Range struct {
Name string
Scope ResourceID
}
type RangeAttribute struct {
Name string
Value Value
}
type TaskID uint64
const (
NoTask = TaskID (^uint64 (0 ))
BackgroundTask = TaskID (0 )
)
type Task struct {
ID TaskID
Parent TaskID
Type string
}
type Region struct {
Task TaskID
Type string
}
type Log struct {
Task TaskID
Category string
Message string
}
type Stack struct {
table *evTable
id stackID
}
func (s Stack ) Frames (yield func (f StackFrame ) bool ) bool {
if s .id == 0 {
return true
}
stk := s .table .stacks .mustGet (s .id )
for _ , pc := range stk .pcs {
f := s .table .pcs [pc ]
sf := StackFrame {
PC : f .pc ,
Func : s .table .strings .mustGet (f .funcID ),
File : s .table .strings .mustGet (f .fileID ),
Line : f .line ,
}
if !yield (sf ) {
return false
}
}
return true
}
var NoStack = Stack {}
type StackFrame struct {
PC uint64
Func string
File string
Line uint64
}
type ExperimentalEvent struct {
Name string
ArgNames []string
Args []uint64
Data *ExperimentalData
}
type ExperimentalData struct {
Batches []ExperimentalBatch
}
type ExperimentalBatch struct {
Thread ThreadID
Data []byte
}
type Event struct {
table *evTable
ctx schedCtx
base baseEvent
}
func (e Event ) Kind () EventKind {
return go122Type2Kind [e .base .typ ]
}
func (e Event ) Time () Time {
return e .base .time
}
func (e Event ) Goroutine () GoID {
return e .ctx .G
}
func (e Event ) Proc () ProcID {
return e .ctx .P
}
func (e Event ) Thread () ThreadID {
return e .ctx .M
}
func (e Event ) Stack () Stack {
if e .base .typ == evSync {
return NoStack
}
if e .base .typ == go122 .EvCPUSample {
return Stack {table : e .table , id : stackID (e .base .args [0 ])}
}
spec := go122 .Specs ()[e .base .typ ]
if len (spec .StackIDs ) == 0 {
return NoStack
}
id := stackID (e .base .args [spec .StackIDs [0 ]-1 ])
if id == 0 {
return NoStack
}
return Stack {table : e .table , id : id }
}
func (e Event ) Metric () Metric {
if e .Kind () != EventMetric {
panic ("Metric called on non-Metric event" )
}
var m Metric
switch e .base .typ {
case go122 .EvProcsChange :
m .Name = "/sched/gomaxprocs:threads"
m .Value = Value {kind : ValueUint64 , scalar : e .base .args [0 ]}
case go122 .EvHeapAlloc :
m .Name = "/memory/classes/heap/objects:bytes"
m .Value = Value {kind : ValueUint64 , scalar : e .base .args [0 ]}
case go122 .EvHeapGoal :
m .Name = "/gc/heap/goal:bytes"
m .Value = Value {kind : ValueUint64 , scalar : e .base .args [0 ]}
default :
panic (fmt .Sprintf ("internal error: unexpected event type for Metric kind: %s" , go122 .EventString (e .base .typ )))
}
return m
}
func (e Event ) Label () Label {
if e .Kind () != EventLabel {
panic ("Label called on non-Label event" )
}
if e .base .typ != go122 .EvGoLabel {
panic (fmt .Sprintf ("internal error: unexpected event type for Label kind: %s" , go122 .EventString (e .base .typ )))
}
return Label {
Label : e .table .strings .mustGet (stringID (e .base .args [0 ])),
Resource : ResourceID {Kind : ResourceGoroutine , id : int64 (e .ctx .G )},
}
}
func (e Event ) Range () Range {
if kind := e .Kind (); kind != EventRangeBegin && kind != EventRangeActive && kind != EventRangeEnd {
panic ("Range called on non-Range event" )
}
var r Range
switch e .base .typ {
case go122 .EvSTWBegin , go122 .EvSTWEnd :
r .Name = "stop-the-world (" + e .table .strings .mustGet (stringID (e .base .args [0 ])) + ")"
r .Scope = ResourceID {Kind : ResourceGoroutine , id : int64 (e .Goroutine ())}
case go122 .EvGCBegin , go122 .EvGCActive , go122 .EvGCEnd :
r .Name = "GC concurrent mark phase"
r .Scope = ResourceID {Kind : ResourceNone }
case go122 .EvGCSweepBegin , go122 .EvGCSweepActive , go122 .EvGCSweepEnd :
r .Name = "GC incremental sweep"
r .Scope = ResourceID {Kind : ResourceProc }
if e .base .typ == go122 .EvGCSweepActive {
r .Scope .id = int64 (e .base .args [0 ])
} else {
r .Scope .id = int64 (e .Proc ())
}
r .Scope .id = int64 (e .Proc ())
case go122 .EvGCMarkAssistBegin , go122 .EvGCMarkAssistActive , go122 .EvGCMarkAssistEnd :
r .Name = "GC mark assist"
r .Scope = ResourceID {Kind : ResourceGoroutine }
if e .base .typ == go122 .EvGCMarkAssistActive {
r .Scope .id = int64 (e .base .args [0 ])
} else {
r .Scope .id = int64 (e .Goroutine ())
}
default :
panic (fmt .Sprintf ("internal error: unexpected event type for Range kind: %s" , go122 .EventString (e .base .typ )))
}
return r
}
func (e Event ) RangeAttributes () []RangeAttribute {
if e .Kind () != EventRangeEnd {
panic ("Range called on non-Range event" )
}
if e .base .typ != go122 .EvGCSweepEnd {
return nil
}
return []RangeAttribute {
{
Name : "bytes swept" ,
Value : Value {kind : ValueUint64 , scalar : e .base .args [0 ]},
},
{
Name : "bytes reclaimed" ,
Value : Value {kind : ValueUint64 , scalar : e .base .args [1 ]},
},
}
}
func (e Event ) Task () Task {
if kind := e .Kind (); kind != EventTaskBegin && kind != EventTaskEnd {
panic ("Task called on non-Task event" )
}
parentID := NoTask
var typ string
switch e .base .typ {
case go122 .EvUserTaskBegin :
parentID = TaskID (e .base .args [1 ])
typ = e .table .strings .mustGet (stringID (e .base .args [2 ]))
case go122 .EvUserTaskEnd :
parentID = TaskID (e .base .extra (version .Go122 )[0 ])
typ = e .table .getExtraString (extraStringID (e .base .extra (version .Go122 )[1 ]))
default :
panic (fmt .Sprintf ("internal error: unexpected event type for Task kind: %s" , go122 .EventString (e .base .typ )))
}
return Task {
ID : TaskID (e .base .args [0 ]),
Parent : parentID ,
Type : typ ,
}
}
func (e Event ) Region () Region {
if kind := e .Kind (); kind != EventRegionBegin && kind != EventRegionEnd {
panic ("Region called on non-Region event" )
}
if e .base .typ != go122 .EvUserRegionBegin && e .base .typ != go122 .EvUserRegionEnd {
panic (fmt .Sprintf ("internal error: unexpected event type for Region kind: %s" , go122 .EventString (e .base .typ )))
}
return Region {
Task : TaskID (e .base .args [0 ]),
Type : e .table .strings .mustGet (stringID (e .base .args [1 ])),
}
}
func (e Event ) Log () Log {
if e .Kind () != EventLog {
panic ("Log called on non-Log event" )
}
if e .base .typ != go122 .EvUserLog {
panic (fmt .Sprintf ("internal error: unexpected event type for Log kind: %s" , go122 .EventString (e .base .typ )))
}
return Log {
Task : TaskID (e .base .args [0 ]),
Category : e .table .strings .mustGet (stringID (e .base .args [1 ])),
Message : e .table .strings .mustGet (stringID (e .base .args [2 ])),
}
}
func (e Event ) StateTransition () StateTransition {
if e .Kind () != EventStateTransition {
panic ("StateTransition called on non-StateTransition event" )
}
var s StateTransition
switch e .base .typ {
case go122 .EvProcStart :
s = procStateTransition (ProcID (e .base .args [0 ]), ProcIdle , ProcRunning )
case go122 .EvProcStop :
s = procStateTransition (e .ctx .P , ProcRunning , ProcIdle )
case go122 .EvProcSteal :
beforeState := ProcRunning
if go122 .ProcStatus (e .base .extra (version .Go122 )[0 ]) == go122 .ProcSyscallAbandoned {
beforeState = ProcIdle
}
s = procStateTransition (ProcID (e .base .args [0 ]), beforeState , ProcIdle )
case go122 .EvProcStatus :
s = procStateTransition (ProcID (e .base .args [0 ]), ProcState (e .base .extra (version .Go122 )[0 ]), go122ProcStatus2ProcState [e .base .args [1 ]])
case go122 .EvGoCreate , go122 .EvGoCreateBlocked :
status := GoRunnable
if e .base .typ == go122 .EvGoCreateBlocked {
status = GoWaiting
}
s = goStateTransition (GoID (e .base .args [0 ]), GoNotExist , status )
s .Stack = Stack {table : e .table , id : stackID (e .base .args [1 ])}
case go122 .EvGoCreateSyscall :
s = goStateTransition (GoID (e .base .args [0 ]), GoNotExist , GoSyscall )
case go122 .EvGoStart :
s = goStateTransition (GoID (e .base .args [0 ]), GoRunnable , GoRunning )
case go122 .EvGoDestroy :
s = goStateTransition (e .ctx .G , GoRunning , GoNotExist )
s .Stack = e .Stack ()
case go122 .EvGoDestroySyscall :
s = goStateTransition (e .ctx .G , GoSyscall , GoNotExist )
case go122 .EvGoStop :
s = goStateTransition (e .ctx .G , GoRunning , GoRunnable )
s .Reason = e .table .strings .mustGet (stringID (e .base .args [0 ]))
s .Stack = e .Stack ()
case go122 .EvGoBlock :
s = goStateTransition (e .ctx .G , GoRunning , GoWaiting )
s .Reason = e .table .strings .mustGet (stringID (e .base .args [0 ]))
s .Stack = e .Stack ()
case go122 .EvGoUnblock , go122 .EvGoSwitch , go122 .EvGoSwitchDestroy :
s = goStateTransition (GoID (e .base .args [0 ]), GoWaiting , GoRunnable )
case go122 .EvGoSyscallBegin :
s = goStateTransition (e .ctx .G , GoRunning , GoSyscall )
s .Stack = e .Stack ()
case go122 .EvGoSyscallEnd :
s = goStateTransition (e .ctx .G , GoSyscall , GoRunning )
s .Stack = e .Stack ()
case go122 .EvGoSyscallEndBlocked :
s = goStateTransition (e .ctx .G , GoSyscall , GoRunnable )
s .Stack = e .Stack ()
case go122 .EvGoStatus , go122 .EvGoStatusStack :
s = goStateTransition (GoID (e .base .args [0 ]), GoState (e .base .extra (version .Go122 )[0 ]), go122GoStatus2GoState [e .base .args [2 ]])
default :
panic (fmt .Sprintf ("internal error: unexpected event type for StateTransition kind: %s" , go122 .EventString (e .base .typ )))
}
return s
}
func (e Event ) Experimental () ExperimentalEvent {
if e .Kind () != EventExperimental {
panic ("Experimental called on non-Experimental event" )
}
spec := go122 .Specs ()[e .base .typ ]
argNames := spec .Args [1 :]
return ExperimentalEvent {
Name : spec .Name ,
ArgNames : argNames ,
Args : e .base .args [:len (argNames )],
Data : e .table .expData [spec .Experiment ],
}
}
const evSync = ^event .Type (0 )
var go122Type2Kind = [...]EventKind {
go122 .EvCPUSample : EventStackSample ,
go122 .EvProcsChange : EventMetric ,
go122 .EvProcStart : EventStateTransition ,
go122 .EvProcStop : EventStateTransition ,
go122 .EvProcSteal : EventStateTransition ,
go122 .EvProcStatus : EventStateTransition ,
go122 .EvGoCreate : EventStateTransition ,
go122 .EvGoCreateSyscall : EventStateTransition ,
go122 .EvGoStart : EventStateTransition ,
go122 .EvGoDestroy : EventStateTransition ,
go122 .EvGoDestroySyscall : EventStateTransition ,
go122 .EvGoStop : EventStateTransition ,
go122 .EvGoBlock : EventStateTransition ,
go122 .EvGoUnblock : EventStateTransition ,
go122 .EvGoSyscallBegin : EventStateTransition ,
go122 .EvGoSyscallEnd : EventStateTransition ,
go122 .EvGoSyscallEndBlocked : EventStateTransition ,
go122 .EvGoStatus : EventStateTransition ,
go122 .EvSTWBegin : EventRangeBegin ,
go122 .EvSTWEnd : EventRangeEnd ,
go122 .EvGCActive : EventRangeActive ,
go122 .EvGCBegin : EventRangeBegin ,
go122 .EvGCEnd : EventRangeEnd ,
go122 .EvGCSweepActive : EventRangeActive ,
go122 .EvGCSweepBegin : EventRangeBegin ,
go122 .EvGCSweepEnd : EventRangeEnd ,
go122 .EvGCMarkAssistActive : EventRangeActive ,
go122 .EvGCMarkAssistBegin : EventRangeBegin ,
go122 .EvGCMarkAssistEnd : EventRangeEnd ,
go122 .EvHeapAlloc : EventMetric ,
go122 .EvHeapGoal : EventMetric ,
go122 .EvGoLabel : EventLabel ,
go122 .EvUserTaskBegin : EventTaskBegin ,
go122 .EvUserTaskEnd : EventTaskEnd ,
go122 .EvUserRegionBegin : EventRegionBegin ,
go122 .EvUserRegionEnd : EventRegionEnd ,
go122 .EvUserLog : EventLog ,
go122 .EvGoSwitch : EventStateTransition ,
go122 .EvGoSwitchDestroy : EventStateTransition ,
go122 .EvGoCreateBlocked : EventStateTransition ,
go122 .EvGoStatusStack : EventStateTransition ,
go122 .EvSpan : EventExperimental ,
go122 .EvSpanAlloc : EventExperimental ,
go122 .EvSpanFree : EventExperimental ,
go122 .EvHeapObject : EventExperimental ,
go122 .EvHeapObjectAlloc : EventExperimental ,
go122 .EvHeapObjectFree : EventExperimental ,
go122 .EvGoroutineStack : EventExperimental ,
go122 .EvGoroutineStackAlloc : EventExperimental ,
go122 .EvGoroutineStackFree : EventExperimental ,
evSync : EventSync ,
}
var go122GoStatus2GoState = [...]GoState {
go122 .GoRunnable : GoRunnable ,
go122 .GoRunning : GoRunning ,
go122 .GoWaiting : GoWaiting ,
go122 .GoSyscall : GoSyscall ,
}
var go122ProcStatus2ProcState = [...]ProcState {
go122 .ProcRunning : ProcRunning ,
go122 .ProcIdle : ProcIdle ,
go122 .ProcSyscall : ProcRunning ,
go122 .ProcSyscallAbandoned : ProcIdle ,
}
func (e Event ) String () string {
var sb strings .Builder
fmt .Fprintf (&sb , "M=%d P=%d G=%d" , e .Thread (), e .Proc (), e .Goroutine ())
fmt .Fprintf (&sb , " %s Time=%d" , e .Kind (), e .Time ())
switch kind := e .Kind (); kind {
case EventMetric :
m := e .Metric ()
fmt .Fprintf (&sb , " Name=%q Value=%s" , m .Name , valueAsString (m .Value ))
case EventLabel :
l := e .Label ()
fmt .Fprintf (&sb , " Label=%q Resource=%s" , l .Label , l .Resource )
case EventRangeBegin , EventRangeActive , EventRangeEnd :
r := e .Range ()
fmt .Fprintf (&sb , " Name=%q Scope=%s" , r .Name , r .Scope )
if kind == EventRangeEnd {
fmt .Fprintf (&sb , " Attributes=[" )
for i , attr := range e .RangeAttributes () {
if i != 0 {
fmt .Fprintf (&sb , " " )
}
fmt .Fprintf (&sb , "%q=%s" , attr .Name , valueAsString (attr .Value ))
}
fmt .Fprintf (&sb , "]" )
}
case EventTaskBegin , EventTaskEnd :
t := e .Task ()
fmt .Fprintf (&sb , " ID=%d Parent=%d Type=%q" , t .ID , t .Parent , t .Type )
case EventRegionBegin , EventRegionEnd :
r := e .Region ()
fmt .Fprintf (&sb , " Task=%d Type=%q" , r .Task , r .Type )
case EventLog :
l := e .Log ()
fmt .Fprintf (&sb , " Task=%d Category=%q Message=%q" , l .Task , l .Category , l .Message )
case EventStateTransition :
s := e .StateTransition ()
fmt .Fprintf (&sb , " Resource=%s Reason=%q" , s .Resource , s .Reason )
switch s .Resource .Kind {
case ResourceGoroutine :
id := s .Resource .Goroutine ()
old , new := s .Goroutine ()
fmt .Fprintf (&sb , " GoID=%d %s->%s" , id , old , new )
case ResourceProc :
id := s .Resource .Proc ()
old , new := s .Proc ()
fmt .Fprintf (&sb , " ProcID=%d %s->%s" , id , old , new )
}
if s .Stack != NoStack {
fmt .Fprintln (&sb )
fmt .Fprintln (&sb , "TransitionStack=" )
s .Stack .Frames (func (f StackFrame ) bool {
fmt .Fprintf (&sb , "\t%s @ 0x%x\n" , f .Func , f .PC )
fmt .Fprintf (&sb , "\t\t%s:%d\n" , f .File , f .Line )
return true
})
}
case EventExperimental :
r := e .Experimental ()
fmt .Fprintf (&sb , " Name=%s ArgNames=%v Args=%v" , r .Name , r .ArgNames , r .Args )
}
if stk := e .Stack (); stk != NoStack {
fmt .Fprintln (&sb )
fmt .Fprintln (&sb , "Stack=" )
stk .Frames (func (f StackFrame ) bool {
fmt .Fprintf (&sb , "\t%s @ 0x%x\n" , f .Func , f .PC )
fmt .Fprintf (&sb , "\t\t%s:%d\n" , f .File , f .Line )
return true
})
}
return sb .String ()
}
func (e Event ) validateTableIDs () error {
if e .base .typ == evSync {
return nil
}
spec := go122 .Specs ()[e .base .typ ]
for _ , i := range spec .StackIDs {
id := stackID (e .base .args [i -1 ])
_ , ok := e .table .stacks .get (id )
if !ok {
return fmt .Errorf ("found invalid stack ID %d for event %s" , id , spec .Name )
}
}
for _ , i := range spec .StringIDs {
id := stringID (e .base .args [i -1 ])
_ , ok := e .table .strings .get (id )
if !ok {
return fmt .Errorf ("found invalid string ID %d for event %s" , id , spec .Name )
}
}
return nil
}
func syncEvent(table *evTable , ts Time ) Event {
return Event {
table : table ,
ctx : schedCtx {
G : NoGoroutine ,
P : NoProc ,
M : NoThread ,
},
base : baseEvent {
typ : evSync ,
time : ts ,
},
}
}
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 .