--- /dev/null
+// Copyright 2025 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 contains definitions for the v2 execution trace wire format.
+
+These definitions are shared between the trace parser and the runtime, so it
+must not depend on any package that depends on the runtime (most packages).
+*/
+package tracev2
// We'll releasem after this point and thus this P may run
// something else. We must clear the worker mode to avoid
// attributing the mode to a different (non-worker) G in
- // traceGoStart.
+ // tracev2.GoStart.
pp.gcMarkWorkerMode = gcMarkWorkerNotWorker
// If this worker reached a background mark completion
trace.GoSysExit(lostP)
if lostP {
// We lost the P at some point, even though we got it back here.
- // Trace that we're starting again, because there was a traceGoSysBlock
+ // Trace that we're starting again, because there was a tracev2.GoSysBlock
// call somewhere in exitsyscallfast (indicating that this goroutine
// had blocked) and we're about to start running again.
trace.GoStart()
if gp.m.syscalltick != gp.m.p.ptr().syscalltick {
if trace.ok() {
// The p was retaken and then enter into syscall again (since gp.m.syscalltick has changed).
- // traceGoSysBlock for this syscall was already emitted,
+ // tracev2.GoSysBlock for this syscall was already emitted,
// but here we effectively retake the p from the new syscall running on the same p.
systemstack(func() {
// We're stealing the P. It's treated
import (
"internal/abi"
"internal/runtime/sys"
+ "internal/trace/tracev2"
)
// Batch type values for the alloc/free experiment.
// Write a batch containing information that'll be necessary to
// interpret the events.
var flushed bool
- w := unsafeTraceExpWriter(gen, nil, traceExperimentAllocFree)
+ w := unsafeTraceExpWriter(gen, nil, tracev2.AllocFree)
w, flushed = w.ensure(1 + 4*traceBytesPerNumber)
if flushed {
// Annotate the batch as containing additional info.
// SpanExists records an event indicating that the span exists.
func (tl traceLocker) SpanExists(s *mspan) {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvSpan, traceSpanID(s), traceArg(s.npages), traceSpanTypeAndClass(s))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvSpan, traceSpanID(s), traceArg(s.npages), traceSpanTypeAndClass(s))
}
// SpanAlloc records an event indicating that the span has just been allocated.
func (tl traceLocker) SpanAlloc(s *mspan) {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvSpanAlloc, traceSpanID(s), traceArg(s.npages), traceSpanTypeAndClass(s))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvSpanAlloc, traceSpanID(s), traceArg(s.npages), traceSpanTypeAndClass(s))
}
// SpanFree records an event indicating that the span is about to be freed.
func (tl traceLocker) SpanFree(s *mspan) {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvSpanFree, traceSpanID(s))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvSpanFree, traceSpanID(s))
}
// traceSpanID creates a trace ID for the span s for the trace.
// The type is optional, and the size of the slot occupied the object is inferred from the
// span containing it.
func (tl traceLocker) HeapObjectExists(addr uintptr, typ *abi.Type) {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvHeapObject, traceHeapObjectID(addr), tl.rtype(typ))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvHeapObject, traceHeapObjectID(addr), tl.rtype(typ))
}
// HeapObjectAlloc records that an object was newly allocated at addr with the provided type.
// The type is optional, and the size of the slot occupied the object is inferred from the
// span containing it.
func (tl traceLocker) HeapObjectAlloc(addr uintptr, typ *abi.Type) {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvHeapObjectAlloc, traceHeapObjectID(addr), tl.rtype(typ))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvHeapObjectAlloc, traceHeapObjectID(addr), tl.rtype(typ))
}
// HeapObjectFree records that an object at addr is about to be freed.
func (tl traceLocker) HeapObjectFree(addr uintptr) {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvHeapObjectFree, traceHeapObjectID(addr))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvHeapObjectFree, traceHeapObjectID(addr))
}
// traceHeapObjectID creates a trace ID for a heap object at address addr.
// GoroutineStackExists records that a goroutine stack already exists at address base with the provided size.
func (tl traceLocker) GoroutineStackExists(base, size uintptr) {
order := traceCompressStackSize(size)
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoroutineStack, traceGoroutineStackID(base), order)
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoroutineStack, traceGoroutineStackID(base), order)
}
// GoroutineStackAlloc records that a goroutine stack was newly allocated at address base with the provided size..
func (tl traceLocker) GoroutineStackAlloc(base, size uintptr) {
order := traceCompressStackSize(size)
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoroutineStackAlloc, traceGoroutineStackID(base), order)
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoroutineStackAlloc, traceGoroutineStackID(base), order)
}
// GoroutineStackFree records that a goroutine stack at address base is about to be freed.
func (tl traceLocker) GoroutineStackFree(base uintptr) {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoroutineStackFree, traceGoroutineStackID(base))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoroutineStackFree, traceGoroutineStackID(base))
}
// traceGoroutineStackID creates a trace ID for the goroutine stack from its base address.
import (
"internal/runtime/sys"
+ "internal/trace/tracev2"
"unsafe"
)
// we can change it if it's deemed too error-prone.
type traceWriter struct {
traceLocker
- exp traceExperiment
+ exp tracev2.Experiment
*traceBuf
}
gp.throwsplit = true
}
}
- return traceWriter{traceLocker: tl, traceBuf: tl.mp.trace.buf[tl.gen%2][traceNoExperiment]}
+ return traceWriter{traceLocker: tl, traceBuf: tl.mp.trace.buf[tl.gen%2][tracev2.NoExperiment]}
}
// unsafeTraceWriter produces a traceWriter that doesn't lock the trace.
// have any stack growth.
//
//go:nosplit
-func (w traceWriter) event(ev traceEv, args ...traceArg) traceWriter {
+func (w traceWriter) event(ev tracev2.EventType, args ...traceArg) traceWriter {
// N.B. Everything in this call must be nosplit to maintain
// the stack growth related invariants for writing events.
}
// Write the buffer's header.
- if w.exp == traceNoExperiment {
- w.byte(byte(traceEvEventBatch))
+ if w.exp == tracev2.NoExperiment {
+ w.byte(byte(tracev2.EvEventBatch))
} else {
- w.byte(byte(traceEvExperimentalBatch))
+ w.byte(byte(tracev2.EvExperimentalBatch))
w.byte(byte(w.exp))
}
w.varint(uint64(w.gen))
return w
}
+// expWriter returns a traceWriter that writes into the current M's stream for
+// the given experiment.
+func (tl traceLocker) expWriter(exp tracev2.Experiment) traceWriter {
+ return traceWriter{traceLocker: tl, traceBuf: tl.mp.trace.buf[tl.gen%2][exp], exp: exp}
+}
+
+// unsafeTraceExpWriter produces a traceWriter for experimental trace batches
+// that doesn't lock the trace. Data written to experimental batches need not
+// conform to the standard trace format.
+//
+// It should only be used in contexts where either:
+// - Another traceLocker is held.
+// - trace.gen is prevented from advancing.
+//
+// This does not have the same stack growth restrictions as traceLocker.writer.
+//
+// buf may be nil.
+func unsafeTraceExpWriter(gen uintptr, buf *traceBuf, exp tracev2.Experiment) traceWriter {
+ return traceWriter{traceLocker: traceLocker{gen: gen}, traceBuf: buf, exp: exp}
+}
+
// traceBufQueue is a FIFO of traceBufs.
type traceBufQueue struct {
head, tail *traceBuf
type traceBuf struct {
_ sys.NotInHeap
traceBufHeader
- arr [64<<10 - unsafe.Sizeof(traceBufHeader{})]byte // underlying buffer for traceBufHeader.buf
+ arr [tracev2.MaxBatchSize - unsafe.Sizeof(traceBufHeader{})]byte // underlying buffer for traceBufHeader.buf
}
// byte appends v to buf.
package runtime
+import "internal/trace/tracev2"
+
// traceInitReadCPU initializes CPU profile -> tracer state for tracing.
//
// Returns a profBuf for reading from.
// Must not run on the system stack because profBuf.read performs race
// operations.
func traceReadCPU(gen uintptr) bool {
- var pcBuf [traceStackSize]uintptr
+ var pcBuf [tracev2.MaxFramesPerStack]uintptr
data, tags, eof := trace.cpuLogRead[gen%2].read(profBufNonBlocking)
for len(data) > 0 {
// Ensure we have a place to write to.
var flushed bool
- w, flushed = w.ensure(2 + 5*traceBytesPerNumber /* traceEvCPUSamples + traceEvCPUSample + timestamp + g + m + p + stack ID */)
+ w, flushed = w.ensure(2 + 5*traceBytesPerNumber /* tracev2.EvCPUSamples + tracev2.EvCPUSample + timestamp + g + m + p + stack ID */)
if flushed {
// Annotate the batch as containing strings.
- w.byte(byte(traceEvCPUSamples))
+ w.byte(byte(tracev2.EvCPUSamples))
}
// Add the stack to the table.
stackID := trace.stackTab[gen%2].put(pcBuf[:nstk])
// Write out the CPU sample.
- w.byte(byte(traceEvCPUSample))
+ w.byte(byte(tracev2.EvCPUSample))
w.varint(timestamp)
w.varint(mpid)
w.varint(ppid)
import (
"internal/abi"
"internal/runtime/sys"
-)
-
-// 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!
-type traceEv uint8
-
-const (
- traceEvNone traceEv = iota // unused
-
- // Structural events.
- traceEvEventBatch // start of per-M batch of events [generation, M ID, timestamp, batch length]
- traceEvStacks // start of a section of the stack table [...traceEvStack]
- traceEvStack // stack table entry [ID, ...{PC, func string ID, file string ID, line #}]
- traceEvStrings // start of a section of the string dictionary [...traceEvString]
- traceEvString // string dictionary entry [ID, length, string]
- traceEvCPUSamples // start of a section of CPU samples [...traceEvCPUSample]
- traceEvCPUSample // CPU profiling sample [timestamp, M ID, P ID, goroutine ID, stack ID]
- traceEvFrequency // timestamp units per sec [freq]
-
- // Procs.
- traceEvProcsChange // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack ID]
- traceEvProcStart // start of P [timestamp, P ID, P seq]
- traceEvProcStop // stop of P [timestamp]
- traceEvProcSteal // P was stolen [timestamp, P ID, P seq, M ID]
- traceEvProcStatus // P status at the start of a generation [timestamp, P ID, status]
-
- // Goroutines.
- traceEvGoCreate // goroutine creation [timestamp, new goroutine ID, new stack ID, stack ID]
- traceEvGoCreateSyscall // goroutine appears in syscall (cgo callback) [timestamp, new goroutine ID]
- traceEvGoStart // goroutine starts running [timestamp, goroutine ID, goroutine seq]
- traceEvGoDestroy // goroutine ends [timestamp]
- traceEvGoDestroySyscall // goroutine ends in syscall (cgo callback) [timestamp]
- traceEvGoStop // goroutine yields its time, but is runnable [timestamp, reason, stack ID]
- traceEvGoBlock // goroutine blocks [timestamp, reason, stack ID]
- traceEvGoUnblock // goroutine is unblocked [timestamp, goroutine ID, goroutine seq, stack ID]
- traceEvGoSyscallBegin // syscall enter [timestamp, P seq, stack ID]
- traceEvGoSyscallEnd // syscall exit [timestamp]
- traceEvGoSyscallEndBlocked // syscall exit and it blocked at some point [timestamp]
- traceEvGoStatus // goroutine status at the start of a generation [timestamp, goroutine ID, M ID, status]
-
- // STW.
- traceEvSTWBegin // STW start [timestamp, kind]
- traceEvSTWEnd // STW done [timestamp]
-
- // GC events.
- traceEvGCActive // GC active [timestamp, seq]
- traceEvGCBegin // GC start [timestamp, seq, stack ID]
- traceEvGCEnd // GC done [timestamp, seq]
- traceEvGCSweepActive // GC sweep active [timestamp, P ID]
- traceEvGCSweepBegin // GC sweep start [timestamp, stack ID]
- traceEvGCSweepEnd // GC sweep done [timestamp, swept bytes, reclaimed bytes]
- traceEvGCMarkAssistActive // GC mark assist active [timestamp, goroutine ID]
- traceEvGCMarkAssistBegin // GC mark assist start [timestamp, stack ID]
- traceEvGCMarkAssistEnd // GC mark assist done [timestamp]
- traceEvHeapAlloc // gcController.heapLive change [timestamp, heap alloc in bytes]
- traceEvHeapGoal // gcController.heapGoal() change [timestamp, heap goal in bytes]
-
- // Annotations.
- traceEvGoLabel // apply string label to current running goroutine [timestamp, label string ID]
- traceEvUserTaskBegin // trace.NewTask [timestamp, internal task ID, internal parent task ID, name string ID, stack ID]
- traceEvUserTaskEnd // end of a task [timestamp, internal task ID, stack ID]
- traceEvUserRegionBegin // trace.{Start,With}Region [timestamp, internal task ID, name string ID, stack ID]
- traceEvUserRegionEnd // trace.{End,With}Region [timestamp, internal task ID, name string ID, stack ID]
- traceEvUserLog // trace.Log [timestamp, internal task ID, key string ID, stack, value string ID]
-
- // Coroutines.
- traceEvGoSwitch // goroutine switch (coroswitch) [timestamp, goroutine ID, goroutine seq]
- traceEvGoSwitchDestroy // goroutine switch and destroy [timestamp, goroutine ID, goroutine seq]
- traceEvGoCreateBlocked // goroutine creation (starts blocked) [timestamp, new goroutine ID, new stack ID, stack ID]
-
- // GoStatus with stack.
- traceEvGoStatusStack // 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.
- traceEvExperimentalBatch // start of extra data [experiment ID, generation, M ID, timestamp, batch length, batch data...]
+ "internal/trace/tracev2"
)
// traceArg is a simple wrapper type to help ensure that arguments passed
// been Runnable before a GoStart). Otherwise, callers can query the status of either the goroutine
// or P and pass the appropriate status.
//
-// In this case, the default status should be traceGoBad or traceProcBad to help identify bugs sooner.
-func (tl traceLocker) eventWriter(goStatus traceGoStatus, procStatus traceProcStatus) traceEventWriter {
+// In this case, the default status should be tracev2.GoBad or tracev2.ProcBad to help identify bugs sooner.
+func (tl traceLocker) eventWriter(goStatus tracev2.GoStatus, procStatus tracev2.ProcStatus) traceEventWriter {
if pp := tl.mp.p.ptr(); pp != nil && !pp.trace.statusWasTraced(tl.gen) && pp.trace.acquireStatus(tl.gen) {
tl.writer().writeProcStatus(uint64(pp.id), procStatus, pp.trace.inSweep).end()
}
}
// event writes out a trace event.
-func (e traceEventWriter) event(ev traceEv, args ...traceArg) {
+func (e traceEventWriter) event(ev tracev2.EventType, args ...traceArg) {
e.tl.writer().event(ev, args...).end()
}
+++ /dev/null
-// Copyright 2024 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 runtime
-
-// expWriter returns a traceWriter that writes into the current M's stream for
-// the given experiment.
-func (tl traceLocker) expWriter(exp traceExperiment) traceWriter {
- return traceWriter{traceLocker: tl, traceBuf: tl.mp.trace.buf[tl.gen%2][exp], exp: exp}
-}
-
-// unsafeTraceExpWriter produces a traceWriter for experimental trace batches
-// that doesn't lock the trace. Data written to experimental batches need not
-// conform to the standard trace format.
-//
-// It should only be used in contexts where either:
-// - Another traceLocker is held.
-// - trace.gen is prevented from advancing.
-//
-// This does not have the same stack growth restrictions as traceLocker.writer.
-//
-// buf may be nil.
-func unsafeTraceExpWriter(gen uintptr, buf *traceBuf, exp traceExperiment) traceWriter {
- return traceWriter{traceLocker: traceLocker{gen: gen}, traceBuf: buf, exp: exp}
-}
-
-// traceExperiment is an enumeration of the different kinds of experiments supported for tracing.
-type traceExperiment uint8
-
-const (
- // traceNoExperiment indicates no experiment.
- traceNoExperiment traceExperiment = iota
-
- // traceExperimentAllocFree is an experiment to add alloc/free events to the trace.
- traceExperimentAllocFree
-
- // traceNumExperiments is the number of trace experiments (and 1 higher than
- // the highest numbered experiment).
- traceNumExperiments
-)
-
-// Experimental events.
-const (
- _ traceEv = 127 + iota
-
- // Experimental events for ExperimentAllocFree.
-
- // Experimental heap span events. IDs map reversibly to base addresses.
- traceEvSpan // heap span exists [timestamp, id, npages, type/class]
- traceEvSpanAlloc // heap span alloc [timestamp, id, npages, type/class]
- traceEvSpanFree // heap span free [timestamp, id]
-
- // Experimental heap object events. IDs map reversibly to addresses.
- traceEvHeapObject // heap object exists [timestamp, id, type]
- traceEvHeapObjectAlloc // heap object alloc [timestamp, id, type]
- traceEvHeapObjectFree // heap object free [timestamp, id]
-
- // Experimental goroutine stack events. IDs map reversibly to addresses.
- traceEvGoroutineStack // stack exists [timestamp, id, order]
- traceEvGoroutineStackAlloc // stack alloc [timestamp, id, order]
- traceEvGoroutineStackFree // stack free [timestamp, id]
-)
import (
"internal/runtime/atomic"
+ "internal/trace/tracev2"
_ "unsafe" // for go:linkname
)
// mTraceState is per-M state for the tracer.
type mTraceState struct {
- seqlock atomic.Uintptr // seqlock indicating that this M is writing to a trace buffer.
- buf [2][traceNumExperiments]*traceBuf // Per-M traceBuf for writing. Indexed by trace.gen%2.
- link *m // Snapshot of alllink or freelink.
- reentered uint32 // Whether we've reentered tracing from within tracing.
- oldthrowsplit bool // gp.throwsplit upon calling traceLocker.writer. For debugging.
+ seqlock atomic.Uintptr // seqlock indicating that this M is writing to a trace buffer.
+ buf [2][tracev2.NumExperiments]*traceBuf // Per-M traceBuf for writing. Indexed by trace.gen%2.
+ link *m // Snapshot of alllink or freelink.
+ reentered uint32 // Whether we've reentered tracing from within tracing.
+ oldthrowsplit bool // gp.throwsplit upon calling traceLocker.writer. For debugging.
}
// pTraceState is per-P state for the tracer.
// Gomaxprocs emits a ProcsChange event.
func (tl traceLocker) Gomaxprocs(procs int32) {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvProcsChange, traceArg(procs), tl.stack(1))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvProcsChange, traceArg(procs), tl.stack(1))
}
// ProcStart traces a ProcStart event.
// Procs are typically started within the scheduler when there is no user goroutine. If there is a user goroutine,
// it must be in _Gsyscall because the only time a goroutine is allowed to have its Proc moved around from under it
// is during a syscall.
- tl.eventWriter(traceGoSyscall, traceProcIdle).event(traceEvProcStart, traceArg(pp.id), pp.trace.nextSeq(tl.gen))
+ tl.eventWriter(tracev2.GoSyscall, tracev2.ProcIdle).event(tracev2.EvProcStart, traceArg(pp.id), pp.trace.nextSeq(tl.gen))
}
// ProcStop traces a ProcStop event.
func (tl traceLocker) ProcStop(pp *p) {
// The only time a goroutine is allowed to have its Proc moved around
// from under it is during a syscall.
- tl.eventWriter(traceGoSyscall, traceProcRunning).event(traceEvProcStop)
+ tl.eventWriter(tracev2.GoSyscall, tracev2.ProcRunning).event(tracev2.EvProcStop)
}
// GCActive traces a GCActive event.
// Must be emitted by an actively running goroutine on an active P. This restriction can be changed
// easily and only depends on where it's currently called.
func (tl traceLocker) GCActive() {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCActive, traceArg(trace.seqGC))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCActive, traceArg(trace.seqGC))
// N.B. Only one GC can be running at a time, so this is naturally
// serialized by the caller.
trace.seqGC++
// Must be emitted by an actively running goroutine on an active P. This restriction can be changed
// easily and only depends on where it's currently called.
func (tl traceLocker) GCStart() {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCBegin, traceArg(trace.seqGC), tl.stack(3))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCBegin, traceArg(trace.seqGC), tl.stack(3))
// N.B. Only one GC can be running at a time, so this is naturally
// serialized by the caller.
trace.seqGC++
// Must be emitted by an actively running goroutine on an active P. This restriction can be changed
// easily and only depends on where it's currently called.
func (tl traceLocker) GCDone() {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCEnd, traceArg(trace.seqGC))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCEnd, traceArg(trace.seqGC))
// N.B. Only one GC can be running at a time, so this is naturally
// serialized by the caller.
trace.seqGC++
func (tl traceLocker) STWStart(reason stwReason) {
// Although the current P may be in _Pgcstop here, we model the P as running during the STW. This deviates from the
// runtime's state tracking, but it's more accurate and doesn't result in any loss of information.
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvSTWBegin, tl.string(reason.String()), tl.stack(2))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvSTWBegin, tl.string(reason.String()), tl.stack(2))
}
// STWDone traces a STWEnd event.
func (tl traceLocker) STWDone() {
// Although the current P may be in _Pgcstop here, we model the P as running during the STW. This deviates from the
// runtime's state tracking, but it's more accurate and doesn't result in any loss of information.
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvSTWEnd)
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvSTWEnd)
}
// GCSweepStart prepares to trace a sweep loop. This does not
pp := tl.mp.p.ptr()
if pp.trace.maySweep {
if pp.trace.swept == 0 {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCSweepBegin, tl.stack(1))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCSweepBegin, tl.stack(1))
pp.trace.inSweep = true
}
pp.trace.swept += bytesSwept
throw("missing traceGCSweepStart")
}
if pp.trace.inSweep {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCSweepEnd, traceArg(pp.trace.swept), traceArg(pp.trace.reclaimed))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCSweepEnd, traceArg(pp.trace.swept), traceArg(pp.trace.reclaimed))
pp.trace.inSweep = false
}
pp.trace.maySweep = false
// GCMarkAssistStart emits a MarkAssistBegin event.
func (tl traceLocker) GCMarkAssistStart() {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCMarkAssistBegin, tl.stack(1))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCMarkAssistBegin, tl.stack(1))
}
// GCMarkAssistDone emits a MarkAssistEnd event.
func (tl traceLocker) GCMarkAssistDone() {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCMarkAssistEnd)
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCMarkAssistEnd)
}
// GoCreate emits a GoCreate event.
func (tl traceLocker) GoCreate(newg *g, pc uintptr, blocked bool) {
newg.trace.setStatusTraced(tl.gen)
- ev := traceEvGoCreate
+ ev := tracev2.EvGoCreate
if blocked {
- ev = traceEvGoCreateBlocked
+ ev = tracev2.EvGoCreateBlocked
}
- tl.eventWriter(traceGoRunning, traceProcRunning).event(ev, traceArg(newg.goid), tl.startPC(pc), tl.stack(2))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(ev, traceArg(newg.goid), tl.startPC(pc), tl.stack(2))
}
// GoStart emits a GoStart event.
func (tl traceLocker) GoStart() {
gp := getg().m.curg
pp := gp.m.p
- w := tl.eventWriter(traceGoRunnable, traceProcRunning)
- w.event(traceEvGoStart, traceArg(gp.goid), gp.trace.nextSeq(tl.gen))
+ w := tl.eventWriter(tracev2.GoRunnable, tracev2.ProcRunning)
+ w.event(tracev2.EvGoStart, traceArg(gp.goid), gp.trace.nextSeq(tl.gen))
if pp.ptr().gcMarkWorkerMode != gcMarkWorkerNotWorker {
- w.event(traceEvGoLabel, trace.markWorkerLabels[tl.gen%2][pp.ptr().gcMarkWorkerMode])
+ w.event(tracev2.EvGoLabel, trace.markWorkerLabels[tl.gen%2][pp.ptr().gcMarkWorkerMode])
}
}
//
// TODO(mknyszek): Rename this to GoDestroy.
func (tl traceLocker) GoEnd() {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoDestroy)
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoDestroy)
}
// GoSched emits a GoStop event with a GoSched reason.
// GoStop emits a GoStop event with the provided reason.
func (tl traceLocker) GoStop(reason traceGoStopReason) {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoStop, traceArg(trace.goStopReasons[tl.gen%2][reason]), tl.stack(1))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoStop, traceArg(trace.goStopReasons[tl.gen%2][reason]), tl.stack(1))
}
// GoPark emits a GoBlock event with the provided reason.
// TODO(mknyszek): Replace traceBlockReason with waitReason. It's silly
// that we have both, and waitReason is way more descriptive.
func (tl traceLocker) GoPark(reason traceBlockReason, skip int) {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoBlock, traceArg(trace.goBlockReasons[tl.gen%2][reason]), tl.stack(skip))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoBlock, traceArg(trace.goBlockReasons[tl.gen%2][reason]), tl.stack(skip))
}
// GoUnpark emits a GoUnblock event.
func (tl traceLocker) GoUnpark(gp *g, skip int) {
// Emit a GoWaiting status if necessary for the unblocked goroutine.
tl.emitUnblockStatus(gp, tl.gen)
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoUnblock, traceArg(gp.goid), gp.trace.nextSeq(tl.gen), tl.stack(skip))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoUnblock, traceArg(gp.goid), gp.trace.nextSeq(tl.gen), tl.stack(skip))
}
// GoSwitch emits a GoSwitch event. If destroy is true, the calling goroutine
func (tl traceLocker) GoSwitch(nextg *g, destroy bool) {
// Emit a GoWaiting status if necessary for the unblocked goroutine.
tl.emitUnblockStatus(nextg, tl.gen)
- w := tl.eventWriter(traceGoRunning, traceProcRunning)
- ev := traceEvGoSwitch
+ w := tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning)
+ ev := tracev2.EvGoSwitch
if destroy {
- ev = traceEvGoSwitchDestroy
+ ev = tracev2.EvGoSwitchDestroy
}
w.event(ev, traceArg(nextg.goid), nextg.trace.nextSeq(tl.gen))
}
// TODO(go.dev/issue/65634): Although it would be nice to add a stack trace here of gp,
// we cannot safely do so. gp is in _Gwaiting and so we don't have ownership of its stack.
// We can fix this by acquiring the goroutine's scan bit.
- tl.writer().writeGoStatus(gp.goid, -1, traceGoWaiting, gp.inMarkAssist, 0).end()
+ tl.writer().writeGoStatus(gp.goid, -1, tracev2.GoWaiting, gp.inMarkAssist, 0).end()
}
}
// Scribble down the M that the P is currently attached to.
pp := tl.mp.p.ptr()
pp.trace.mSyscallID = int64(tl.mp.procid)
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoSyscallBegin, pp.trace.nextSeq(tl.gen), tl.stack(1))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoSyscallBegin, pp.trace.nextSeq(tl.gen), tl.stack(1))
}
// GoSysExit emits a GoSyscallEnd event, possibly along with a GoSyscallBlocked event
// - The goroutine lost its P and was unable to reacquire it, and is now running without a P.
// - The goroutine lost its P and acquired a different one, and is now running with that P.
func (tl traceLocker) GoSysExit(lostP bool) {
- ev := traceEvGoSyscallEnd
- procStatus := traceProcSyscall // Procs implicitly enter traceProcSyscall on GoSyscallBegin.
+ ev := tracev2.EvGoSyscallEnd
+ procStatus := tracev2.ProcSyscall // Procs implicitly enter tracev2.ProcSyscall on GoSyscallBegin.
if lostP {
- ev = traceEvGoSyscallEndBlocked
- procStatus = traceProcRunning // If a G has a P when emitting this event, it reacquired a P and is indeed running.
+ ev = tracev2.EvGoSyscallEndBlocked
+ procStatus = tracev2.ProcRunning // If a G has a P when emitting this event, it reacquired a P and is indeed running.
} else {
tl.mp.p.ptr().trace.mSyscallID = -1
}
- tl.eventWriter(traceGoSyscall, procStatus).event(ev)
+ tl.eventWriter(tracev2.GoSyscall, procStatus).event(ev)
}
// ProcSteal indicates that our current M stole a P from another M.
if !pp.trace.statusWasTraced(tl.gen) && pp.trace.acquireStatus(tl.gen) {
// Careful: don't use the event writer. We never want status or in-progress events
// to trigger more in-progress events.
- tl.writer().writeProcStatus(uint64(pp.id), traceProcSyscallAbandoned, pp.trace.inSweep).end()
+ tl.writer().writeProcStatus(uint64(pp.id), tracev2.ProcSyscallAbandoned, pp.trace.inSweep).end()
}
// The status of the proc and goroutine, if we need to emit one here, is not evident from the
// ourselves specifically to keep running. The two contexts look different, but can be summarized
// fairly succinctly. In the former, we're a regular running goroutine and proc, if we have either.
// In the latter, we're a goroutine in a syscall.
- goStatus := traceGoRunning
- procStatus := traceProcRunning
+ goStatus := tracev2.GoRunning
+ procStatus := tracev2.ProcRunning
if inSyscall {
- goStatus = traceGoSyscall
- procStatus = traceProcSyscallAbandoned
+ goStatus = tracev2.GoSyscall
+ procStatus = tracev2.ProcSyscallAbandoned
}
- tl.eventWriter(goStatus, procStatus).event(traceEvProcSteal, traceArg(pp.id), pp.trace.nextSeq(tl.gen), traceArg(mStolenFrom))
+ tl.eventWriter(goStatus, procStatus).event(tracev2.EvProcSteal, traceArg(pp.id), pp.trace.nextSeq(tl.gen), traceArg(mStolenFrom))
}
// HeapAlloc emits a HeapAlloc event.
func (tl traceLocker) HeapAlloc(live uint64) {
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvHeapAlloc, traceArg(live))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvHeapAlloc, traceArg(live))
}
// HeapGoal reads the current heap goal and emits a HeapGoal event.
// Heap-based triggering is disabled.
heapGoal = 0
}
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvHeapGoal, traceArg(heapGoal))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvHeapGoal, traceArg(heapGoal))
}
// GoCreateSyscall indicates that a goroutine has transitioned from dead to GoSyscall.
// N.B. We should never trace a status for this goroutine (which we're currently running on),
// since we want this to appear like goroutine creation.
gp.trace.setStatusTraced(tl.gen)
- tl.eventWriter(traceGoBad, traceProcBad).event(traceEvGoCreateSyscall, traceArg(gp.goid))
+ tl.eventWriter(tracev2.GoBad, tracev2.ProcBad).event(tracev2.EvGoCreateSyscall, traceArg(gp.goid))
}
// GoDestroySyscall indicates that a goroutine has transitioned from GoSyscall to dead.
func (tl traceLocker) GoDestroySyscall() {
// N.B. If we trace a status here, we must never have a P, and we must be on a goroutine
// that is in the syscall state.
- tl.eventWriter(traceGoSyscall, traceProcBad).event(traceEvGoDestroySyscall)
+ tl.eventWriter(tracev2.GoSyscall, tracev2.ProcBad).event(tracev2.EvGoDestroySyscall)
}
// To access runtime functions from runtime/trace.
// Need to do this check because the caller won't have it.
return
}
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvUserTaskBegin, traceArg(id), traceArg(parentID), tl.string(taskType), tl.stack(3))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvUserTaskBegin, traceArg(id), traceArg(parentID), tl.string(taskType), tl.stack(3))
traceRelease(tl)
}
// Need to do this check because the caller won't have it.
return
}
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvUserTaskEnd, traceArg(id), tl.stack(2))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvUserTaskEnd, traceArg(id), tl.stack(2))
traceRelease(tl)
}
// Need to do this check because the caller won't have it.
return
}
- var ev traceEv
+ var ev tracev2.EventType
switch mode {
case 0:
- ev = traceEvUserRegionBegin
+ ev = tracev2.EvUserRegionBegin
case 1:
- ev = traceEvUserRegionEnd
+ ev = tracev2.EvUserRegionEnd
default:
return
}
- tl.eventWriter(traceGoRunning, traceProcRunning).event(ev, traceArg(id), tl.string(name), tl.stack(3))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(ev, traceArg(id), tl.string(name), tl.stack(3))
traceRelease(tl)
}
// Need to do this check because the caller won't have it.
return
}
- tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvUserLog, traceArg(id), tl.string(category), tl.uniqueString(message), tl.stack(3))
+ tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvUserLog, traceArg(id), tl.string(category), tl.uniqueString(message), tl.stack(3))
traceRelease(tl)
}
import (
"internal/abi"
"internal/goarch"
+ "internal/trace/tracev2"
"unsafe"
)
const (
- // 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.
- traceStackSize = 128
-
// logicalStackSentinel is a sentinel value at pcBuf[0] signifying that
// pcBuf[1:] holds a logical stack requiring no further processing. Any other
// value at pcBuf[0] represents a skip value to apply to the physical stack in
// that this stack trace is being written out for, which needs to be synchronized with
// generations moving forward. Prefer traceEventWriter.stack.
func traceStack(skip int, gp *g, gen uintptr) uint64 {
- var pcBuf [traceStackSize]uintptr
+ var pcBuf [tracev2.MaxFramesPerStack]uintptr
// Figure out gp and mp for the backtrace.
var mp *m
// are totally fine for taking a stack trace. They're captured
// correctly in goStatusToTraceGoStatus.
switch goStatusToTraceGoStatus(status, gp.waitreason) {
- case traceGoRunning, traceGoSyscall:
+ case tracev2.GoRunning, tracev2.GoSyscall:
if getg() == gp || mp.curg == gp {
break
}
// releases all memory and resets state. It must only be called once the caller
// can guarantee that there are no more writers to the table.
func (t *traceStackTable) dump(gen uintptr) {
- stackBuf := make([]uintptr, traceStackSize)
+ stackBuf := make([]uintptr, tracev2.MaxFramesPerStack)
w := unsafeTraceWriter(gen, nil)
if root := (*traceMapNode)(t.tab.root.Load()); root != nil {
w = dumpStacksRec(root, w, stackBuf)
// bound is pretty loose, but avoids counting
// lots of varint sizes.
//
- // Add 1 because we might also write traceEvStacks.
+ // Add 1 because we might also write tracev2.EvStacks.
var flushed bool
w, flushed = w.ensure(1 + maxBytes)
if flushed {
- w.byte(byte(traceEvStacks))
+ w.byte(byte(tracev2.EvStacks))
}
// Emit stack event.
- w.byte(byte(traceEvStack))
+ w.byte(byte(tracev2.EvStack))
w.varint(uint64(node.id))
w.varint(uint64(len(frames)))
for _, frame := range frames {
package runtime
-import "internal/runtime/atomic"
-
-// traceGoStatus is the status of a goroutine.
-//
-// They correspond directly to the various goroutine
-// statuses.
-type traceGoStatus uint8
-
-const (
- traceGoBad traceGoStatus = iota
- traceGoRunnable
- traceGoRunning
- traceGoSyscall
- traceGoWaiting
-)
-
-// traceProcStatus is the status of a P.
-//
-// They mostly correspond to the various P statuses.
-type traceProcStatus uint8
-
-const (
- traceProcBad traceProcStatus = iota
- traceProcRunning
- traceProcIdle
- traceProcSyscall
-
- // traceProcSyscallAbandoned is a special case of
- // traceProcSyscall. 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.
- traceProcSyscallAbandoned
+import (
+ "internal/runtime/atomic"
+ "internal/trace/tracev2"
)
// writeGoStatus emits a GoStatus event as well as any active ranges on the goroutine.
// have any stack growth.
//
//go:nosplit
-func (w traceWriter) writeGoStatus(goid uint64, mid int64, status traceGoStatus, markAssist bool, stackID uint64) traceWriter {
+func (w traceWriter) writeGoStatus(goid uint64, mid int64, status tracev2.GoStatus, markAssist bool, stackID uint64) traceWriter {
// The status should never be bad. Some invariant must have been violated.
- if status == traceGoBad {
+ if status == tracev2.GoBad {
print("runtime: goid=", goid, "\n")
throw("attempted to trace a bad status for a goroutine")
}
// Trace the status.
if stackID == 0 {
- w = w.event(traceEvGoStatus, traceArg(goid), traceArg(uint64(mid)), traceArg(status))
+ w = w.event(tracev2.EvGoStatus, traceArg(goid), traceArg(uint64(mid)), traceArg(status))
} else {
- w = w.event(traceEvGoStatusStack, traceArg(goid), traceArg(uint64(mid)), traceArg(status), traceArg(stackID))
+ w = w.event(tracev2.EvGoStatusStack, traceArg(goid), traceArg(uint64(mid)), traceArg(status), traceArg(stackID))
}
// Trace any special ranges that are in-progress.
if markAssist {
- w = w.event(traceEvGCMarkAssistActive, traceArg(goid))
+ w = w.event(tracev2.EvGCMarkAssistActive, traceArg(goid))
}
return w
}
if !pp.trace.acquireStatus(w.gen) {
return w
}
- var status traceProcStatus
+ var status tracev2.ProcStatus
switch pp.status {
case _Pidle, _Pgcstop:
- status = traceProcIdle
+ status = tracev2.ProcIdle
if pp.status == _Pgcstop && inSTW {
// N.B. a P that is running and currently has the world stopped will be
// in _Pgcstop, but we model it as running in the tracer.
- status = traceProcRunning
+ status = tracev2.ProcRunning
}
case _Prunning:
- status = traceProcRunning
+ status = tracev2.ProcRunning
// There's a short window wherein the goroutine may have entered _Gsyscall
// but it still owns the P (it's not in _Psyscall yet). The goroutine entering
// _Gsyscall is the tracer's signal that the P its bound to is also in a syscall,
// so we need to emit a status that matches. See #64318.
if w.mp.p.ptr() == pp && w.mp.curg != nil && readgstatus(w.mp.curg)&^_Gscan == _Gsyscall {
- status = traceProcSyscall
+ status = tracev2.ProcSyscall
}
case _Psyscall:
- status = traceProcSyscall
+ status = tracev2.ProcSyscall
default:
throw("attempt to trace invalid or unsupported P status")
}
// have any stack growth.
//
//go:nosplit
-func (w traceWriter) writeProcStatus(pid uint64, status traceProcStatus, inSweep bool) traceWriter {
+func (w traceWriter) writeProcStatus(pid uint64, status tracev2.ProcStatus, inSweep bool) traceWriter {
// The status should never be bad. Some invariant must have been violated.
- if status == traceProcBad {
+ if status == tracev2.ProcBad {
print("runtime: pid=", pid, "\n")
throw("attempted to trace a bad status for a proc")
}
// Trace the status.
- w = w.event(traceEvProcStatus, traceArg(pid), traceArg(status))
+ w = w.event(tracev2.EvProcStatus, traceArg(pid), traceArg(status))
// Trace any special ranges that are in-progress.
if inSweep {
- w = w.event(traceEvGCSweepActive, traceArg(pid))
+ w = w.event(tracev2.EvGCSweepActive, traceArg(pid))
}
return w
}
// have any stack growth.
//
//go:nosplit
-func goStatusToTraceGoStatus(status uint32, wr waitReason) traceGoStatus {
+func goStatusToTraceGoStatus(status uint32, wr waitReason) tracev2.GoStatus {
// N.B. Ignore the _Gscan bit. We don't model it in the tracer.
- var tgs traceGoStatus
+ var tgs tracev2.GoStatus
switch status &^ _Gscan {
case _Grunnable:
- tgs = traceGoRunnable
+ tgs = tracev2.GoRunnable
case _Grunning, _Gcopystack:
- tgs = traceGoRunning
+ tgs = tracev2.GoRunning
case _Gsyscall:
- tgs = traceGoSyscall
+ tgs = tracev2.GoSyscall
case _Gwaiting, _Gpreempted:
// There are a number of cases where a G might end up in
// _Gwaiting but it's actually running in a non-preemptive
// garbage collector. In these cases, we're not going to
// emit an event, and we want these goroutines to appear in
// the final trace as if they're running, not blocked.
- tgs = traceGoWaiting
+ tgs = tracev2.GoWaiting
if status == _Gwaiting && wr.isWaitingForGC() {
- tgs = traceGoRunning
+ tgs = tracev2.GoRunning
}
case _Gdead:
throw("tried to trace dead goroutine")
package runtime
-// Trace strings.
+import "internal/trace/tracev2"
-const maxTraceStringLen = 1024
+// Trace strings.
// traceStringTable is map of string -> unique ID that also manages
// writing strings out into the trace.
//go:systemstack
func (t *traceStringTable) writeString(gen uintptr, id uint64, s string) {
// Truncate the string if necessary.
- if len(s) > maxTraceStringLen {
- s = s[:maxTraceStringLen]
+ if len(s) > tracev2.MaxEventTrailerDataSize {
+ s = s[:tracev2.MaxEventTrailerDataSize]
}
lock(&t.lock)
// Ensure we have a place to write to.
var flushed bool
- w, flushed = w.ensure(2 + 2*traceBytesPerNumber + len(s) /* traceEvStrings + traceEvString + ID + len + string data */)
+ w, flushed = w.ensure(2 + 2*traceBytesPerNumber + len(s) /* tracev2.EvStrings + tracev2.EvString + ID + len + string data */)
if flushed {
// Annotate the batch as containing strings.
- w.byte(byte(traceEvStrings))
+ w.byte(byte(tracev2.EvStrings))
}
// Write out the string.
- w.byte(byte(traceEvString))
+ w.byte(byte(tracev2.EvString))
w.varint(id)
w.varint(uint64(len(s)))
w.stringData(s)
import (
"internal/goarch"
+ "internal/trace/tracev2"
_ "unsafe"
)
w := unsafeTraceWriter(gen, nil)
// Ensure we have a place to write to.
- w, _ = w.ensure(1 + traceBytesPerNumber /* traceEvFrequency + frequency */)
+ w, _ = w.ensure(1 + traceBytesPerNumber /* tracev2.EvFrequency + frequency */)
// Write out the string.
- w.byte(byte(traceEvFrequency))
+ w.byte(byte(tracev2.EvFrequency))
w.varint(traceClockUnitsPerSecond())
// Immediately flush the buffer.
import (
"internal/abi"
"internal/goarch"
+ "internal/trace/tracev2"
"unsafe"
)
// releases all memory and resets state. It must only be called once the caller
// can guarantee that there are no more writers to the table.
func (t *traceTypeTable) dump(gen uintptr) {
- w := unsafeTraceExpWriter(gen, nil, traceExperimentAllocFree)
+ w := unsafeTraceExpWriter(gen, nil, tracev2.AllocFree)
if root := (*traceMapNode)(t.tab.root.Load()); root != nil {
w = dumpTypesRec(root, w)
}