| // Copyright 2023 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package tracev2 |
| |
| // Event types in the trace, args are given in square brackets. |
| // |
| // Naming scheme: |
| // - Time range event pairs have suffixes "Begin" and "End". |
| // - "Start", "Stop", "Create", "Destroy", "Block", "Unblock" |
| // are suffixes reserved for scheduling resources. |
| // |
| // NOTE: If you add an event type, make sure you also update all |
| // tables in this file! |
| const ( |
| EvNone EventType = iota // unused |
| |
| // Structural events. |
| EvEventBatch // start of per-M batch of events [generation, M ID, timestamp, batch length] |
| EvStacks // start of a section of the stack table [...EvStack] |
| EvStack // stack table entry [ID, ...{PC, func string ID, file string ID, line #}] |
| EvStrings // start of a section of the string dictionary [...EvString] |
| EvString // string dictionary entry [ID, length, string] |
| EvCPUSamples // start of a section of CPU samples [...EvCPUSample] |
| EvCPUSample // CPU profiling sample [timestamp, M ID, P ID, goroutine ID, stack ID] |
| EvFrequency // timestamp units per sec [freq] |
| |
| // Procs. |
| EvProcsChange // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack ID] |
| EvProcStart // start of P [timestamp, P ID, P seq] |
| EvProcStop // stop of P [timestamp] |
| EvProcSteal // P was stolen [timestamp, P ID, P seq, M ID] |
| EvProcStatus // P status at the start of a generation [timestamp, P ID, status] |
| |
| // Goroutines. |
| EvGoCreate // goroutine creation [timestamp, new goroutine ID, new stack ID, stack ID] |
| EvGoCreateSyscall // goroutine appears in syscall (cgo callback) [timestamp, new goroutine ID] |
| EvGoStart // goroutine starts running [timestamp, goroutine ID, goroutine seq] |
| EvGoDestroy // goroutine ends [timestamp] |
| EvGoDestroySyscall // goroutine ends in syscall (cgo callback) [timestamp] |
| EvGoStop // goroutine yields its time, but is runnable [timestamp, reason, stack ID] |
| EvGoBlock // goroutine blocks [timestamp, reason, stack ID] |
| EvGoUnblock // goroutine is unblocked [timestamp, goroutine ID, goroutine seq, stack ID] |
| EvGoSyscallBegin // syscall enter [timestamp, P seq, stack ID] |
| EvGoSyscallEnd // syscall exit [timestamp] |
| EvGoSyscallEndBlocked // syscall exit and it blocked at some point [timestamp] |
| EvGoStatus // goroutine status at the start of a generation [timestamp, goroutine ID, M ID, status] |
| |
| // STW. |
| EvSTWBegin // STW start [timestamp, kind, stack ID] |
| EvSTWEnd // STW done [timestamp] |
| |
| // GC events. |
| EvGCActive // GC active [timestamp, seq] |
| EvGCBegin // GC start [timestamp, seq, stack ID] |
| EvGCEnd // GC done [timestamp, seq] |
| EvGCSweepActive // GC sweep active [timestamp, P ID] |
| EvGCSweepBegin // GC sweep start [timestamp, stack ID] |
| EvGCSweepEnd // GC sweep done [timestamp, swept bytes, reclaimed bytes] |
| EvGCMarkAssistActive // GC mark assist active [timestamp, goroutine ID] |
| EvGCMarkAssistBegin // GC mark assist start [timestamp, stack ID] |
| EvGCMarkAssistEnd // GC mark assist done [timestamp] |
| EvHeapAlloc // gcController.heapLive change [timestamp, heap alloc in bytes] |
| EvHeapGoal // gcController.heapGoal() change [timestamp, heap goal in bytes] |
| |
| // Annotations. |
| EvGoLabel // apply string label to current running goroutine [timestamp, label string ID] |
| EvUserTaskBegin // trace.NewTask [timestamp, internal task ID, internal parent task ID, name string ID, stack ID] |
| EvUserTaskEnd // end of a task [timestamp, internal task ID, stack ID] |
| EvUserRegionBegin // trace.{Start,With}Region [timestamp, internal task ID, name string ID, stack ID] |
| EvUserRegionEnd // trace.{End,With}Region [timestamp, internal task ID, name string ID, stack ID] |
| EvUserLog // trace.Log [timestamp, internal task ID, key string ID, value string ID, stack] |
| |
| // Coroutines. Added in Go 1.23. |
| EvGoSwitch // goroutine switch (coroswitch) [timestamp, goroutine ID, goroutine seq] |
| EvGoSwitchDestroy // goroutine switch and destroy [timestamp, goroutine ID, goroutine seq] |
| EvGoCreateBlocked // goroutine creation (starts blocked) [timestamp, new goroutine ID, new stack ID, stack ID] |
| |
| // GoStatus with stack. Added in Go 1.23. |
| EvGoStatusStack // goroutine status at the start of a generation, with a stack [timestamp, goroutine ID, M ID, status, stack ID] |
| |
| // Batch event for an experimental batch with a custom format. Added in Go 1.23. |
| EvExperimentalBatch // start of extra data [experiment ID, generation, M ID, timestamp, batch length, batch data...] |
| |
| // Sync batch. Added in Go 1.25. Previously a lone EvFrequency event. |
| EvSync // start of a sync batch [...EvFrequency|EvClockSnapshot] |
| EvClockSnapshot // snapshot of trace, mono and wall clocks [timestamp, mono, sec, nsec] |
| |
| // In-band end-of-generation signal. Added in Go 1.26. |
| // Used in Go 1.25 only internally. |
| EvEndOfGeneration |
| |
| NumEvents |
| ) |
| |
| func (ev EventType) Experimental() bool { |
| return ev > MaxEvent && ev < MaxExperimentalEvent |
| } |
| |
| // Experiments. |
| const ( |
| // AllocFree is the alloc-free events experiment. |
| AllocFree Experiment = 1 + iota |
| |
| NumExperiments |
| ) |
| |
| func Experiments() []string { |
| return experiments[:] |
| } |
| |
| var experiments = [...]string{ |
| NoExperiment: "None", |
| AllocFree: "AllocFree", |
| } |
| |
| // Experimental events. |
| const ( |
| MaxEvent EventType = 127 + iota |
| |
| // Experimental events for AllocFree. |
| |
| // Experimental heap span events. Added in Go 1.23. |
| EvSpan // heap span exists [timestamp, id, npages, type/class] |
| EvSpanAlloc // heap span alloc [timestamp, id, npages, type/class] |
| EvSpanFree // heap span free [timestamp, id] |
| |
| // Experimental heap object events. Added in Go 1.23. |
| EvHeapObject // heap object exists [timestamp, id, type] |
| EvHeapObjectAlloc // heap object alloc [timestamp, id, type] |
| EvHeapObjectFree // heap object free [timestamp, id] |
| |
| // Experimental goroutine stack events. Added in Go 1.23. |
| EvGoroutineStack // stack exists [timestamp, id, order] |
| EvGoroutineStackAlloc // stack alloc [timestamp, id, order] |
| EvGoroutineStackFree // stack free [timestamp, id] |
| |
| MaxExperimentalEvent |
| ) |
| |
| const NumExperimentalEvents = MaxExperimentalEvent - MaxEvent |
| |
| // MaxTimedEventArgs is the maximum number of arguments for timed events. |
| const MaxTimedEventArgs = 5 |
| |
| func Specs() []EventSpec { |
| return specs[:] |
| } |
| |
| var specs = [...]EventSpec{ |
| // "Structural" Events. |
| EvEventBatch: { |
| Name: "EventBatch", |
| Args: []string{"gen", "m", "time", "size"}, |
| }, |
| EvStacks: { |
| Name: "Stacks", |
| }, |
| EvStack: { |
| Name: "Stack", |
| Args: []string{"id", "nframes"}, |
| IsStack: true, |
| }, |
| EvStrings: { |
| Name: "Strings", |
| }, |
| EvString: { |
| Name: "String", |
| Args: []string{"id"}, |
| HasData: true, |
| }, |
| EvCPUSamples: { |
| Name: "CPUSamples", |
| }, |
| EvCPUSample: { |
| Name: "CPUSample", |
| Args: []string{"time", "m", "p", "g", "stack"}, |
| // N.B. There's clearly a timestamp here, but these Events |
| // are special in that they don't appear in the regular |
| // M streams. |
| StackIDs: []int{4}, |
| }, |
| EvFrequency: { |
| Name: "Frequency", |
| Args: []string{"freq"}, |
| }, |
| EvExperimentalBatch: { |
| Name: "ExperimentalBatch", |
| Args: []string{"exp", "gen", "m", "time"}, |
| HasData: true, // Easier to represent for raw readers. |
| }, |
| EvSync: { |
| Name: "Sync", |
| }, |
| EvEndOfGeneration: { |
| Name: "EndOfGeneration", |
| }, |
| |
| // "Timed" Events. |
| EvProcsChange: { |
| Name: "ProcsChange", |
| Args: []string{"dt", "procs_value", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{2}, |
| }, |
| EvProcStart: { |
| Name: "ProcStart", |
| Args: []string{"dt", "p", "p_seq"}, |
| IsTimedEvent: true, |
| }, |
| EvProcStop: { |
| Name: "ProcStop", |
| Args: []string{"dt"}, |
| IsTimedEvent: true, |
| }, |
| EvProcSteal: { |
| Name: "ProcSteal", |
| Args: []string{"dt", "p", "p_seq", "m"}, |
| IsTimedEvent: true, |
| }, |
| EvProcStatus: { |
| Name: "ProcStatus", |
| Args: []string{"dt", "p", "pstatus"}, |
| IsTimedEvent: true, |
| }, |
| EvGoCreate: { |
| Name: "GoCreate", |
| Args: []string{"dt", "new_g", "new_stack", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{3, 2}, |
| }, |
| EvGoCreateSyscall: { |
| Name: "GoCreateSyscall", |
| Args: []string{"dt", "new_g"}, |
| IsTimedEvent: true, |
| }, |
| EvGoStart: { |
| Name: "GoStart", |
| Args: []string{"dt", "g", "g_seq"}, |
| IsTimedEvent: true, |
| }, |
| EvGoDestroy: { |
| Name: "GoDestroy", |
| Args: []string{"dt"}, |
| IsTimedEvent: true, |
| }, |
| EvGoDestroySyscall: { |
| Name: "GoDestroySyscall", |
| Args: []string{"dt"}, |
| IsTimedEvent: true, |
| }, |
| EvGoStop: { |
| Name: "GoStop", |
| Args: []string{"dt", "reason_string", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{2}, |
| StringIDs: []int{1}, |
| }, |
| EvGoBlock: { |
| Name: "GoBlock", |
| Args: []string{"dt", "reason_string", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{2}, |
| StringIDs: []int{1}, |
| }, |
| EvGoUnblock: { |
| Name: "GoUnblock", |
| Args: []string{"dt", "g", "g_seq", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{3}, |
| }, |
| EvGoSyscallBegin: { |
| Name: "GoSyscallBegin", |
| Args: []string{"dt", "p_seq", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{2}, |
| }, |
| EvGoSyscallEnd: { |
| Name: "GoSyscallEnd", |
| Args: []string{"dt"}, |
| StartEv: EvGoSyscallBegin, |
| IsTimedEvent: true, |
| }, |
| EvGoSyscallEndBlocked: { |
| Name: "GoSyscallEndBlocked", |
| Args: []string{"dt"}, |
| StartEv: EvGoSyscallBegin, |
| IsTimedEvent: true, |
| }, |
| EvGoStatus: { |
| Name: "GoStatus", |
| Args: []string{"dt", "g", "m", "gstatus"}, |
| IsTimedEvent: true, |
| }, |
| EvSTWBegin: { |
| Name: "STWBegin", |
| Args: []string{"dt", "kind_string", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{2}, |
| StringIDs: []int{1}, |
| }, |
| EvSTWEnd: { |
| Name: "STWEnd", |
| Args: []string{"dt"}, |
| StartEv: EvSTWBegin, |
| IsTimedEvent: true, |
| }, |
| EvGCActive: { |
| Name: "GCActive", |
| Args: []string{"dt", "gc_seq"}, |
| IsTimedEvent: true, |
| StartEv: EvGCBegin, |
| }, |
| EvGCBegin: { |
| Name: "GCBegin", |
| Args: []string{"dt", "gc_seq", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{2}, |
| }, |
| EvGCEnd: { |
| Name: "GCEnd", |
| Args: []string{"dt", "gc_seq"}, |
| StartEv: EvGCBegin, |
| IsTimedEvent: true, |
| }, |
| EvGCSweepActive: { |
| Name: "GCSweepActive", |
| Args: []string{"dt", "p"}, |
| StartEv: EvGCSweepBegin, |
| IsTimedEvent: true, |
| }, |
| EvGCSweepBegin: { |
| Name: "GCSweepBegin", |
| Args: []string{"dt", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{1}, |
| }, |
| EvGCSweepEnd: { |
| Name: "GCSweepEnd", |
| Args: []string{"dt", "swept_value", "reclaimed_value"}, |
| StartEv: EvGCSweepBegin, |
| IsTimedEvent: true, |
| }, |
| EvGCMarkAssistActive: { |
| Name: "GCMarkAssistActive", |
| Args: []string{"dt", "g"}, |
| StartEv: EvGCMarkAssistBegin, |
| IsTimedEvent: true, |
| }, |
| EvGCMarkAssistBegin: { |
| Name: "GCMarkAssistBegin", |
| Args: []string{"dt", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{1}, |
| }, |
| EvGCMarkAssistEnd: { |
| Name: "GCMarkAssistEnd", |
| Args: []string{"dt"}, |
| StartEv: EvGCMarkAssistBegin, |
| IsTimedEvent: true, |
| }, |
| EvHeapAlloc: { |
| Name: "HeapAlloc", |
| Args: []string{"dt", "heapalloc_value"}, |
| IsTimedEvent: true, |
| }, |
| EvHeapGoal: { |
| Name: "HeapGoal", |
| Args: []string{"dt", "heapgoal_value"}, |
| IsTimedEvent: true, |
| }, |
| EvGoLabel: { |
| Name: "GoLabel", |
| Args: []string{"dt", "label_string"}, |
| IsTimedEvent: true, |
| StringIDs: []int{1}, |
| }, |
| EvUserTaskBegin: { |
| Name: "UserTaskBegin", |
| Args: []string{"dt", "task", "parent_task", "name_string", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{4}, |
| StringIDs: []int{3}, |
| }, |
| EvUserTaskEnd: { |
| Name: "UserTaskEnd", |
| Args: []string{"dt", "task", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{2}, |
| }, |
| EvUserRegionBegin: { |
| Name: "UserRegionBegin", |
| Args: []string{"dt", "task", "name_string", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{3}, |
| StringIDs: []int{2}, |
| }, |
| EvUserRegionEnd: { |
| Name: "UserRegionEnd", |
| Args: []string{"dt", "task", "name_string", "stack"}, |
| StartEv: EvUserRegionBegin, |
| IsTimedEvent: true, |
| StackIDs: []int{3}, |
| StringIDs: []int{2}, |
| }, |
| EvUserLog: { |
| Name: "UserLog", |
| Args: []string{"dt", "task", "key_string", "value_string", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{4}, |
| StringIDs: []int{2, 3}, |
| }, |
| EvGoSwitch: { |
| Name: "GoSwitch", |
| Args: []string{"dt", "g", "g_seq"}, |
| IsTimedEvent: true, |
| }, |
| EvGoSwitchDestroy: { |
| Name: "GoSwitchDestroy", |
| Args: []string{"dt", "g", "g_seq"}, |
| IsTimedEvent: true, |
| }, |
| EvGoCreateBlocked: { |
| Name: "GoCreateBlocked", |
| Args: []string{"dt", "new_g", "new_stack", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{3, 2}, |
| }, |
| EvGoStatusStack: { |
| Name: "GoStatusStack", |
| Args: []string{"dt", "g", "m", "gstatus", "stack"}, |
| IsTimedEvent: true, |
| StackIDs: []int{4}, |
| }, |
| EvClockSnapshot: { |
| Name: "ClockSnapshot", |
| Args: []string{"dt", "mono", "sec", "nsec"}, |
| IsTimedEvent: true, |
| }, |
| |
| // Experimental events. |
| |
| EvSpan: { |
| Name: "Span", |
| Args: []string{"dt", "id", "npages_value", "kindclass"}, |
| IsTimedEvent: true, |
| Experiment: AllocFree, |
| }, |
| EvSpanAlloc: { |
| Name: "SpanAlloc", |
| Args: []string{"dt", "id", "npages_value", "kindclass"}, |
| IsTimedEvent: true, |
| Experiment: AllocFree, |
| }, |
| EvSpanFree: { |
| Name: "SpanFree", |
| Args: []string{"dt", "id"}, |
| IsTimedEvent: true, |
| Experiment: AllocFree, |
| }, |
| EvHeapObject: { |
| Name: "HeapObject", |
| Args: []string{"dt", "id", "type"}, |
| IsTimedEvent: true, |
| Experiment: AllocFree, |
| }, |
| EvHeapObjectAlloc: { |
| Name: "HeapObjectAlloc", |
| Args: []string{"dt", "id", "type"}, |
| IsTimedEvent: true, |
| Experiment: AllocFree, |
| }, |
| EvHeapObjectFree: { |
| Name: "HeapObjectFree", |
| Args: []string{"dt", "id"}, |
| IsTimedEvent: true, |
| Experiment: AllocFree, |
| }, |
| EvGoroutineStack: { |
| Name: "GoroutineStack", |
| Args: []string{"dt", "id", "order"}, |
| IsTimedEvent: true, |
| Experiment: AllocFree, |
| }, |
| EvGoroutineStackAlloc: { |
| Name: "GoroutineStackAlloc", |
| Args: []string{"dt", "id", "order"}, |
| IsTimedEvent: true, |
| Experiment: AllocFree, |
| }, |
| EvGoroutineStackFree: { |
| Name: "GoroutineStackFree", |
| Args: []string{"dt", "id"}, |
| IsTimedEvent: true, |
| Experiment: AllocFree, |
| }, |
| } |
| |
| // GoStatus is the status of a goroutine. |
| // |
| // They correspond directly to the various goroutine states. |
| type GoStatus uint8 |
| |
| const ( |
| GoBad GoStatus = iota |
| GoRunnable |
| GoRunning |
| GoSyscall |
| GoWaiting |
| ) |
| |
| func (s GoStatus) String() string { |
| switch s { |
| case GoRunnable: |
| return "Runnable" |
| case GoRunning: |
| return "Running" |
| case GoSyscall: |
| return "Syscall" |
| case GoWaiting: |
| return "Waiting" |
| } |
| return "Bad" |
| } |
| |
| // ProcStatus is the status of a P. |
| // |
| // They mostly correspond to the various P states. |
| type ProcStatus uint8 |
| |
| const ( |
| ProcBad ProcStatus = iota |
| ProcRunning |
| ProcIdle |
| ProcSyscall |
| |
| // ProcSyscallAbandoned is a special case of |
| // ProcSyscall. It's used in the very specific case |
| // where the first a P is mentioned in a generation is |
| // part of a ProcSteal event. If that's the first time |
| // it's mentioned, then there's no GoSyscallBegin to |
| // connect the P stealing back to at that point. This |
| // special state indicates this to the parser, so it |
| // doesn't try to find a GoSyscallEndBlocked that |
| // corresponds with the ProcSteal. |
| ProcSyscallAbandoned |
| ) |
| |
| func (s ProcStatus) String() string { |
| switch s { |
| case ProcRunning: |
| return "Running" |
| case ProcIdle: |
| return "Idle" |
| case ProcSyscall: |
| return "Syscall" |
| } |
| return "Bad" |
| } |
| |
| const ( |
| // MaxBatchSize sets the maximum size that a batch can be. |
| // |
| // Directly controls the trace batch size in the runtime. |
| // |
| // NOTE: If this number decreases, the trace format version must change. |
| MaxBatchSize = 64 << 10 |
| |
| // Maximum number of PCs in a single stack trace. |
| // |
| // Since events contain only stack ID rather than whole stack trace, |
| // we can allow quite large values here. |
| // |
| // Directly controls the maximum number of frames per stack |
| // in the runtime. |
| // |
| // NOTE: If this number decreases, the trace format version must change. |
| MaxFramesPerStack = 128 |
| |
| // MaxEventTrailerDataSize controls the amount of trailer data that |
| // an event can have in bytes. Must be smaller than MaxBatchSize. |
| // Controls the maximum string size in the trace. |
| // |
| // Directly controls the maximum such value in the runtime. |
| // |
| // NOTE: If this number decreases, the trace format version must change. |
| MaxEventTrailerDataSize = 1 << 10 |
| ) |