// Copyright 2014 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.// Go execution tracer.// The tracer captures a wide range of execution events like goroutine// creation/blocking/unblocking, syscall enter/exit/block, GC-related events,// changes of heap size, processor start/stop, etc and writes them to a buffer// in a compact form. A precise nanosecond-precision timestamp and a stack// trace is captured for most events.// See https://golang.org/s/go15trace for more info.package runtimeimport ()// Event types in the trace, args are given in square brackets.const (traceEvNone = 0// unusedtraceEvBatch = 1// start of per-P batch of events [pid, timestamp]traceEvFrequency = 2// contains tracer timer frequency [frequency (ticks per second)]traceEvStack = 3// stack [stack id, number of PCs, array of {PC, func string ID, file string ID, line}]traceEvGomaxprocs = 4// current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack id]traceEvProcStart = 5// start of P [timestamp, thread id]traceEvProcStop = 6// stop of P [timestamp]traceEvGCStart = 7// GC start [timestamp, seq, stack id]traceEvGCDone = 8// GC done [timestamp]traceEvSTWStart = 9// STW start [timestamp, kind]traceEvSTWDone = 10// STW done [timestamp]traceEvGCSweepStart = 11// GC sweep start [timestamp, stack id]traceEvGCSweepDone = 12// GC sweep done [timestamp, swept, reclaimed]traceEvGoCreate = 13// goroutine creation [timestamp, new goroutine id, new stack id, stack id]traceEvGoStart = 14// goroutine starts running [timestamp, goroutine id, seq]traceEvGoEnd = 15// goroutine ends [timestamp]traceEvGoStop = 16// goroutine stops (like in select{}) [timestamp, stack]traceEvGoSched = 17// goroutine calls Gosched [timestamp, stack]traceEvGoPreempt = 18// goroutine is preempted [timestamp, stack]traceEvGoSleep = 19// goroutine calls Sleep [timestamp, stack]traceEvGoBlock = 20// goroutine blocks [timestamp, stack]traceEvGoUnblock = 21// goroutine is unblocked [timestamp, goroutine id, seq, stack]traceEvGoBlockSend = 22// goroutine blocks on chan send [timestamp, stack]traceEvGoBlockRecv = 23// goroutine blocks on chan recv [timestamp, stack]traceEvGoBlockSelect = 24// goroutine blocks on select [timestamp, stack]traceEvGoBlockSync = 25// goroutine blocks on Mutex/RWMutex [timestamp, stack]traceEvGoBlockCond = 26// goroutine blocks on Cond [timestamp, stack]traceEvGoBlockNet = 27// goroutine blocks on network [timestamp, stack]traceEvGoSysCall = 28// syscall enter [timestamp, stack]traceEvGoSysExit = 29// syscall exit [timestamp, goroutine id, seq, real timestamp]traceEvGoSysBlock = 30// syscall blocks [timestamp]traceEvGoWaiting = 31// denotes that goroutine is blocked when tracing starts [timestamp, goroutine id]traceEvGoInSyscall = 32// denotes that goroutine is in syscall when tracing starts [timestamp, goroutine id]traceEvHeapAlloc = 33// gcController.heapLive change [timestamp, heap_alloc]traceEvHeapGoal = 34// gcController.heapGoal() (formerly next_gc) change [timestamp, heap goal in bytes]traceEvTimerGoroutine = 35// not currently used; previously denoted timer goroutine [timer goroutine id]traceEvFutileWakeup = 36// not currently used; denotes that the previous wakeup of this goroutine was futile [timestamp]traceEvString = 37// string dictionary entry [ID, length, string]traceEvGoStartLocal = 38// goroutine starts running on the same P as the last event [timestamp, goroutine id]traceEvGoUnblockLocal = 39// goroutine is unblocked on the same P as the last event [timestamp, goroutine id, stack]traceEvGoSysExitLocal = 40// syscall exit on the same P as the last event [timestamp, goroutine id, real timestamp]traceEvGoStartLabel = 41// goroutine starts running with label [timestamp, goroutine id, seq, label string id]traceEvGoBlockGC = 42// goroutine blocks on GC assist [timestamp, stack]traceEvGCMarkAssistStart = 43// GC mark assist start [timestamp, stack]traceEvGCMarkAssistDone = 44// GC mark assist done [timestamp]traceEvUserTaskCreate = 45// trace.NewTask [timestamp, internal task id, internal parent task id, name string, stack]traceEvUserTaskEnd = 46// end of a task [timestamp, internal task id, stack]traceEvUserRegion = 47// trace.WithRegion [timestamp, internal task id, mode(0:start, 1:end), name string, stack]traceEvUserLog = 48// trace.Log [timestamp, internal task id, key string id, stack, value string]traceEvCPUSample = 49// CPU profiling sample [timestamp, real timestamp, real P id (-1 when absent), goroutine id, stack]traceEvCount = 50// Byte is used but only 6 bits are available for event type. // The remaining 2 bits are used to specify the number of arguments. // That means, the max event type value is 63.)// traceBlockReason is an enumeration of reasons a goroutine might block.// This is the interface the rest of the runtime uses to tell the// tracer why a goroutine blocked. The tracer then propagates this information// into the trace however it sees fit.//// Note that traceBlockReasons should not be compared, since reasons that are// distinct by name may *not* be distinct by value.typetraceBlockReasonuint8// For maximal efficiency, just map the trace block reason directly to a trace// event.const (traceBlockGenerictraceBlockReason = traceEvGoBlocktraceBlockForever = traceEvGoStoptraceBlockNet = traceEvGoBlockNettraceBlockSelect = traceEvGoBlockSelecttraceBlockCondWait = traceEvGoBlockCondtraceBlockSync = traceEvGoBlockSynctraceBlockChanSend = traceEvGoBlockSendtraceBlockChanRecv = traceEvGoBlockRecvtraceBlockGCMarkAssist = traceEvGoBlockGCtraceBlockGCSweep = traceEvGoBlocktraceBlockSystemGoroutine = traceEvGoBlocktraceBlockPreempted = traceEvGoBlocktraceBlockDebugCall = traceEvGoBlocktraceBlockUntilGCEnds = traceEvGoBlocktraceBlockSleep = traceEvGoSleep)const (// Timestamps in trace are cputicks/traceTickDiv. // This makes absolute values of timestamp diffs smaller, // and so they are encoded in less number of bytes. // 64 on x86 is somewhat arbitrary (one tick is ~20ns on a 3GHz machine). // The suggested increment frequency for PowerPC's time base register is // 512 MHz according to Power ISA v2.07 section 6.2, so we use 16 on ppc64 // and ppc64le.traceTimeDiv = 16 + 48*(goarch.Is386|goarch.IsAmd64)// 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// Identifier of a fake P that is used when we trace without a real P.traceGlobProc = -1// Maximum number of bytes to encode uint64 in base-128.traceBytesPerNumber = 10// Shift of the number of arguments in the first event byte.traceArgCountShift = 6)// trace is global tracing context.vartracestruct {// trace.lock must only be acquired on the system stack where // stack splits cannot happen while it is held. lock mutex// protects the following members enabled bool// when set runtime traces events shutdown bool// set when we are waiting for trace reader to finish after setting enabled to false headerWritten bool// whether ReadTrace has emitted trace header footerWritten bool// whether ReadTrace has emitted trace footer shutdownSema uint32// used to wait for ReadTrace completion seqStart uint64// sequence number when tracing was started startTicks int64// cputicks when tracing was started endTicks int64// cputicks when tracing was stopped startNanotime int64// nanotime when tracing was started endNanotime int64// nanotime when tracing was stopped startTime traceTime// traceClockNow when tracing started endTime traceTime// traceClockNow when tracing stopped seqGC uint64// GC start/done sequencer reading traceBufPtr// buffer currently handed off to user empty traceBufPtr// stack of empty buffers fullHead traceBufPtr// queue of full buffers fullTail traceBufPtr stackTab traceStackTable// maps stack traces to unique ids// cpuLogRead accepts CPU profile samples from the signal handler where // they're generated. It uses a two-word header to hold the IDs of the P and // G (respectively) that were active at the time of the sample. Because // profBuf uses a record with all zeros in its header to indicate overflow, // we make sure to make the P field always non-zero: The ID of a real P will // start at bit 1, and bit 0 will be set. Samples that arrive while no P is // running (such as near syscalls) will set the first header field to 0b10. // This careful handling of the first header field allows us to store ID of // the active G directly in the second field, even though that will be 0 // when sampling g0. cpuLogRead *profBuf// cpuLogBuf is a trace buffer to hold events corresponding to CPU profile // samples, which arrive out of band and not directly connected to a // specific P. cpuLogBuf traceBufPtr reader atomic.Pointer[g] // goroutine that called ReadTrace, or nil signalLock atomic.Uint32// protects use of the following member, only usable in signal handlers cpuLogWrite *profBuf// copy of cpuLogRead for use in signal handlers, set without signalLock// Dictionary for traceEvString. // // TODO: central lock to access the map is not ideal. // option: pre-assign ids to all user annotation region names and tags // option: per-P cache // option: sync.Map like data structure stringsLock mutex strings map[string]uint64 stringSeq uint64// markWorkerLabels maps gcMarkWorkerMode to string ID. markWorkerLabels [len(gcMarkWorkerModeStrings)]uint64 bufLock mutex// protects buf buf traceBufPtr// global trace buffer, used when running without a p}// gTraceState is per-G state for the tracer.typegTraceStatestruct {sysExitTimetraceTime// timestamp when syscall has returnedtracedSyscallEnterbool// syscall or cgo was entered while trace was enabled or StartTrace has emitted EvGoInSyscall about this goroutinesequint64// trace event sequencerlastPpuintptr// last P emitted an event for this goroutine}// mTraceState is per-M state for the tracer.typemTraceStatestruct {startingTracebool// this M is in TraceStart, potentially before traceEnabled is truetracedSTWStartbool// this M traced a STW start, so it should trace an end}// pTraceState is per-P state for the tracer.typepTraceStatestruct {buftraceBufPtr// inSweep indicates the sweep events should be traced. // This is used to defer the sweep start event until a span // has actually been swept.inSweepbool// swept and reclaimed track the number of bytes swept and reclaimed // by sweeping in the current sweep loop (while inSweep was true).swept, reclaimeduintptr}// traceLockInit initializes global trace locks.func () {lockInit(&trace.bufLock, lockRankTraceBuf)lockInit(&trace.stringsLock, lockRankTraceStrings)lockInit(&trace.lock, lockRankTrace)lockInit(&trace.stackTab.lock, lockRankTraceStackTab)}// traceBufHeader is per-P tracing buffer.typetraceBufHeaderstruct {linktraceBufPtr// in trace.empty/fulllastTimetraceTime// when we wrote the last eventposint// next write offset in arrstk [traceStackSize]uintptr// scratch buffer for traceback}// traceBuf is per-P tracing buffer.typetraceBufstruct { _ sys.NotInHeaptraceBufHeaderarr [64<<10 - unsafe.Sizeof(traceBufHeader{})]byte// underlying buffer for traceBufHeader.buf}// traceBufPtr is a *traceBuf that is not traced by the garbage// collector and doesn't have write barriers. traceBufs are not// allocated from the GC'd heap, so this is safe, and are often// manipulated in contexts where write barriers are not allowed, so// this is necessary.//// TODO: Since traceBuf is now embedded runtime/internal/sys.NotInHeap, this isn't necessary.typetraceBufPtruintptrfunc ( traceBufPtr) () *traceBuf { return (*traceBuf)(unsafe.Pointer()) }func ( *traceBufPtr) ( *traceBuf) { * = traceBufPtr(unsafe.Pointer()) }func ( *traceBuf) traceBufPtr {returntraceBufPtr(unsafe.Pointer())}// traceEnabled returns true if the trace is currently enabled.////go:nosplitfunc () bool {returntrace.enabled}// traceShuttingDown returns true if the trace is currently shutting down.////go:nosplitfunc () bool {returntrace.shutdown}// StartTrace enables tracing for the current process.// While tracing, the data will be buffered and available via ReadTrace.// StartTrace returns an error if tracing is already enabled.// Most clients should use the runtime/trace package or the testing package's// -test.trace flag instead of calling StartTrace directly.func () error {// Stop the world so that we can take a consistent snapshot // of all goroutines at the beginning of the trace. // Do not stop the world during GC so we ensure we always see // a consistent view of GC-related events (e.g. a start is always // paired with an end).stopTheWorldGC(stwStartTrace)// Prevent sysmon from running any code that could generate events.lock(&sched.sysmonlock)// We are in stop-the-world, but syscalls can finish and write to trace concurrently. // Exitsyscall could check trace.enabled long before and then suddenly wake up // and decide to write to trace at a random point in time. // However, such syscall will use the global trace.buf buffer, because we've // acquired all p's by doing stop-the-world. So this protects us from such races.lock(&trace.bufLock)iftrace.enabled || trace.shutdown {unlock(&trace.bufLock)unlock(&sched.sysmonlock)startTheWorldGC()returnerrorString("tracing is already enabled") }// Can't set trace.enabled yet. While the world is stopped, exitsyscall could // already emit a delayed event (see exitTicks in exitsyscall) if we set trace.enabled here. // That would lead to an inconsistent trace: // - either GoSysExit appears before EvGoInSyscall, // - or GoSysExit appears for a goroutine for which we don't emit EvGoInSyscall below. // To instruct traceEvent that it must not ignore events below, we set trace.startingTrace. // trace.enabled is set afterwards once we have emitted all preliminary events. := getg().m .trace.startingTrace = true// Obtain current stack ID to use in all traceEvGoCreate events below. := make([]uintptr, traceStackSize) := traceStackID(, , 2) := newProfBuf(2, profBufWordCount, profBufTagCount) // after the timestamp, header is [pp.id, gp.goid]trace.cpuLogRead = // We must not acquire trace.signalLock outside of a signal handler: a // profiling signal may arrive at any time and try to acquire it, leading to // deadlock. Because we can't use that lock to protect updates to // trace.cpuLogWrite (only use of the structure it references), reads and // writes of the pointer must be atomic. (And although this field is never // the sole pointer to the profBuf value, it's best to allow a write barrier // here.)atomicstorep(unsafe.Pointer(&trace.cpuLogWrite), unsafe.Pointer())// World is stopped, no need to lock.forEachGRace(func( *g) { := readgstatus()if != _Gdead { .trace.seq = 0 .trace.lastP = getg().m.p// +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum. := trace.stackTab.put([]uintptr{logicalStackSentinel, startPCforTrace(.startpc) + sys.PCQuantum})traceEvent(traceEvGoCreate, -1, .goid, uint64(), ) }if == _Gwaiting {// traceEvGoWaiting is implied to have seq=1. .trace.seq++traceEvent(traceEvGoWaiting, -1, .goid) }if == _Gsyscall { .trace.seq++ .trace.tracedSyscallEnter = truetraceEvent(traceEvGoInSyscall, -1, .goid) } elseif == _Gdead && .m != nil && .m.isextra {// Trigger two trace events for the dead g in the extra m, // since the next event of the g will be traceEvGoSysExit in exitsyscall, // while calling from C thread to Go. .trace.seq = 0 .trace.lastP = getg().m.p// +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum. := trace.stackTab.put([]uintptr{logicalStackSentinel, startPCforTrace(0) + sys.PCQuantum}) // no start pctraceEvent(traceEvGoCreate, -1, .goid, uint64(), ) .trace.seq++ .trace.tracedSyscallEnter = truetraceEvent(traceEvGoInSyscall, -1, .goid) } else {// We need to explicitly clear the flag. A previous trace might have ended with a goroutine // not emitting a GoSysExit and clearing the flag, leaving it in a stale state. Clearing // it here makes it unambiguous to any goroutine exiting a syscall racing with us that // no EvGoInSyscall event was emitted for it. (It's not racy to set this flag here, because // it'll only get checked when the goroutine runs again, which will be after the world starts // again.) .trace.tracedSyscallEnter = false } })traceProcStart()traceGoStart()// Note: startTicks needs to be set after we emit traceEvGoInSyscall events. // If we do it the other way around, it is possible that exitsyscall will // query sysExitTime after startTicks but before traceEvGoInSyscall timestamp. // It will lead to a false conclusion that cputicks is broken.trace.startTime = traceClockNow()trace.startTicks = cputicks()trace.startNanotime = nanotime()trace.headerWritten = falsetrace.footerWritten = false// string to id mapping // 0 : reserved for an empty string // remaining: other strings registered by traceStringtrace.stringSeq = 0trace.strings = make(map[string]uint64)trace.seqGC = 0 .trace.startingTrace = falsetrace.enabled = true// Register runtime goroutine labels. , , := traceAcquireBuffer()for , := rangegcMarkWorkerModeStrings[:] {trace.markWorkerLabels[], = traceString(, , ) }traceReleaseBuffer(, )unlock(&trace.bufLock)unlock(&sched.sysmonlock)// Record the current state of HeapGoal to avoid information loss in trace.traceHeapGoal()startTheWorldGC()returnnil}// StopTrace stops tracing, if it was previously enabled.// StopTrace only returns after all the reads for the trace have completed.func () {// Stop the world so that we can collect the trace buffers from all p's below, // and also to avoid races with traceEvent.stopTheWorldGC(stwStopTrace)// See the comment in StartTrace.lock(&sched.sysmonlock)// See the comment in StartTrace.lock(&trace.bufLock)if !trace.enabled {unlock(&trace.bufLock)unlock(&sched.sysmonlock)startTheWorldGC()return }traceGoSched()atomicstorep(unsafe.Pointer(&trace.cpuLogWrite), nil)trace.cpuLogRead.close()traceReadCPU()// Loop over all allocated Ps because dead Ps may still have // trace buffers.for , := rangeallp[:cap(allp)] { := .trace.bufif != 0 {traceFullQueue() .trace.buf = 0 } }iftrace.buf != 0 { := trace.buftrace.buf = 0if .ptr().pos != 0 {traceFullQueue() } }iftrace.cpuLogBuf != 0 { := trace.cpuLogBuftrace.cpuLogBuf = 0if .ptr().pos != 0 {traceFullQueue() } }// Wait for startNanotime != endNanotime. On Windows the default interval between // system clock ticks is typically between 1 and 15 milliseconds, which may not // have passed since the trace started. Without nanotime moving forward, trace // tooling has no way of identifying how much real time each cputicks time deltas // represent.for {trace.endTime = traceClockNow()trace.endTicks = cputicks()trace.endNanotime = nanotime()iftrace.endNanotime != trace.startNanotime || faketime != 0 {break }osyield() }trace.enabled = falsetrace.shutdown = trueunlock(&trace.bufLock)unlock(&sched.sysmonlock)startTheWorldGC()// The world is started but we've set trace.shutdown, so new tracing can't start. // Wait for the trace reader to flush pending buffers and stop.semacquire(&trace.shutdownSema)ifraceenabled {raceacquire(unsafe.Pointer(&trace.shutdownSema)) }systemstack(func() {// The lock protects us from races with StartTrace/StopTrace because they do stop-the-world.lock(&trace.lock)for , := rangeallp[:cap(allp)] {if .trace.buf != 0 {throw("trace: non-empty trace buffer in proc") } }iftrace.buf != 0 {throw("trace: non-empty global trace buffer") }iftrace.fullHead != 0 || trace.fullTail != 0 {throw("trace: non-empty full trace buffer") }iftrace.reading != 0 || trace.reader.Load() != nil {throw("trace: reading after shutdown") }fortrace.empty != 0 { := trace.emptytrace.empty = .ptr().linksysFree(unsafe.Pointer(), unsafe.Sizeof(*.ptr()), &memstats.other_sys) }trace.strings = niltrace.shutdown = falsetrace.cpuLogRead = nilunlock(&trace.lock) })}// ReadTrace returns the next chunk of binary tracing data, blocking until data// is available. If tracing is turned off and all the data accumulated while it// was on has been returned, ReadTrace returns nil. The caller must copy the// returned data before calling ReadTrace again.// ReadTrace must be called from one goroutine at a time.func () []byte {:var []bytevarboolsystemstack(func() { , = readTrace0() })if {gopark(func( *g, unsafe.Pointer) bool {if !trace.reader.CompareAndSwapNoWB(nil, ) {// We're racing with another reader. // Wake up and handle this case.returnfalse }if := traceReader(); == {// New data arrived between unlocking // and the CAS and we won the wake-up // race, so wake up directly.returnfalse } elseif != nil {printlock()println("runtime: got trace reader", , .goid)throw("unexpected trace reader") }returntrue }, nil, waitReasonTraceReaderBlocked, traceBlockSystemGoroutine, 2)goto }return}// readTrace0 is ReadTrace's continuation on g0. This must run on the// system stack because it acquires trace.lock.////go:systemstackfunc () ( []byte, bool) {ifraceenabled {// g0 doesn't have a race context. Borrow the user G's.ifgetg().racectx != 0 {throw("expected racectx == 0") }getg().racectx = getg().m.curg.racectx// (This defer should get open-coded, which is safe on // the system stack.)deferfunc() { getg().racectx = 0 }() }// Optimistically look for CPU profile samples. This may write new stack // records, and may write new tracing buffers. This must be done with the // trace lock not held. footerWritten and shutdown are safe to access // here. They are only mutated by this goroutine or during a STW.if !trace.footerWritten && !trace.shutdown {traceReadCPU() }// This function must not allocate while holding trace.lock: // allocation can call heap allocate, which will try to emit a trace // event while holding heap lock.lock(&trace.lock)iftrace.reader.Load() != nil {// More than one goroutine reads trace. This is bad. // But we rather do not crash the program because of tracing, // because tracing can be enabled at runtime on prod servers.unlock(&trace.lock)println("runtime: ReadTrace called from multiple goroutines simultaneously")returnnil, false }// Recycle the old buffer.if := trace.reading; != 0 { .ptr().link = trace.emptytrace.empty = trace.reading = 0 }// Write trace header.if !trace.headerWritten {trace.headerWritten = trueunlock(&trace.lock)return []byte("go 1.21 trace\x00\x00\x00"), false }// Wait for new data.iftrace.fullHead == 0 && !trace.shutdown {// We don't simply use a note because the scheduler // executes this goroutine directly when it wakes up // (also a note would consume an M).unlock(&trace.lock)returnnil, true }:assertLockHeld(&trace.lock)// Write a buffer.iftrace.fullHead != 0 { := traceFullDequeue()trace.reading = unlock(&trace.lock)return .ptr().arr[:.ptr().pos], false }// Write footer with timer frequency.if !trace.footerWritten {trace.footerWritten = true := (float64(trace.endTicks-trace.startTicks) / traceTimeDiv) / (float64(trace.endNanotime-trace.startNanotime) / 1e9)if <= 0 {throw("trace: ReadTrace got invalid frequency") }unlock(&trace.lock)// Write frequency event. := traceFlush(0, 0) := .ptr() .byte(traceEvFrequency | 0<<traceArgCountShift) .varint(uint64())// Dump stack table. // This will emit a bunch of full buffers, we will pick them up // on the next iteration. = trace.stackTab.dump()// Flush final buffer.lock(&trace.lock)traceFullQueue()goto// trace.lock should be held at newFull }// Done.iftrace.shutdown {unlock(&trace.lock)ifraceenabled {// Model synchronization on trace.shutdownSema, which race // detector does not see. This is required to avoid false // race reports on writer passed to trace.Start.racerelease(unsafe.Pointer(&trace.shutdownSema)) }// trace.enabled is already reset, so can call traceable functions.semrelease(&trace.shutdownSema)returnnil, false }// Also bad, but see the comment above.unlock(&trace.lock)println("runtime: spurious wakeup of trace reader")returnnil, false}// traceReader returns the trace reader that should be woken up, if any.// Callers should first check that trace.enabled or trace.shutdown is set.//// This must run on the system stack because it acquires trace.lock.////go:systemstackfunc () *g {// Optimistic check firstiftraceReaderAvailable() == nil {returnnil }lock(&trace.lock) := traceReaderAvailable()if == nil || !trace.reader.CompareAndSwapNoWB(, nil) {unlock(&trace.lock)returnnil }unlock(&trace.lock)return}// traceReaderAvailable returns the trace reader if it is not currently// scheduled and should be. Callers should first check that trace.enabled// or trace.shutdown is set.func () *g {iftrace.fullHead != 0 || trace.shutdown {returntrace.reader.Load() }returnnil}// traceProcFree frees trace buffer associated with pp.//// This must run on the system stack because it acquires trace.lock.////go:systemstackfunc ( *p) { := .trace.buf .trace.buf = 0if == 0 {return }lock(&trace.lock)traceFullQueue()unlock(&trace.lock)}// traceFullQueue queues buf into queue of full buffers.func ( traceBufPtr) { .ptr().link = 0iftrace.fullHead == 0 {trace.fullHead = } else {trace.fullTail.ptr().link = }trace.fullTail = }// traceFullDequeue dequeues from queue of full buffers.func () traceBufPtr { := trace.fullHeadif == 0 {return0 }trace.fullHead = .ptr().linkiftrace.fullHead == 0 {trace.fullTail = 0 } .ptr().link = 0return}// traceEvent writes a single event to trace buffer, flushing the buffer if necessary.// ev is event type.// If skip > 0, write current stack id as the last argument (skipping skip top frames).// If skip = 0, this event type should contain a stack, but we don't want// to collect and remember it for this particular call.func ( byte, int, ...uint64) { , , := traceAcquireBuffer()// Double-check trace.enabled now that we've done m.locks++ and acquired bufLock. // This protects from races between traceEvent and StartTrace/StopTrace.// The caller checked that trace.enabled == true, but trace.enabled might have been // turned off between the check and now. Check again. traceLockBuffer did mp.locks++, // StopTrace does stopTheWorld, and stopTheWorld waits for mp.locks to go back to zero, // so if we see trace.enabled == true now, we know it's true for the rest of the function. // Exitsyscall can run even during stopTheWorld. The race with StartTrace/StopTrace // during tracing in exitsyscall is resolved by locking trace.bufLock in traceLockBuffer. // // Note trace_userTaskCreate runs the same check.if !trace.enabled && !.trace.startingTrace {traceReleaseBuffer(, )return }if > 0 {ifgetg() == .curg { ++ // +1 because stack is captured in traceEventLocked. } }traceEventLocked(0, , , , , 0, , ...)traceReleaseBuffer(, )}// traceEventLocked writes a single event of type ev to the trace buffer bufp,// flushing the buffer if necessary. pid is the id of the current P, or// traceGlobProc if we're tracing without a real P.//// Preemption is disabled, and if running without a real P the global tracing// buffer is locked.//// Events types that do not include a stack set skip to -1. Event types that// include a stack may explicitly reference a stackID from the trace.stackTab// (obtained by an earlier call to traceStackID). Without an explicit stackID,// this function will automatically capture the stack of the goroutine currently// running on mp, skipping skip top frames or, if skip is 0, writing out an// empty stack record.//// It records the event's args to the traceBuf, and also makes an effort to// reserve extraBytes bytes of additional space immediately following the event,// in the same traceBuf.func ( int, *m, int32, *traceBufPtr, byte, uint32, int, ...uint64) { := .ptr()// TODO: test on non-zero extraBytes param. := 2 + 5*traceBytesPerNumber + // event type, length, sequence, timestamp, stack id and two add paramsif == nil || len(.arr)-.pos < {systemstack(func() { = traceFlush(traceBufPtrOf(), ).ptr() }) .set() } := traceClockNow()if <= .lastTime { = .lastTime + 1 } := uint64( - .lastTime) .lastTime = := byte(len())if != 0 || >= 0 { ++ }// We have only 2 bits for number of arguments. // If number is >= 3, then the event type is followed by event length in bytes.if > 3 { = 3 } := .pos .byte( | <<traceArgCountShift)var *byteif == 3 {// Reserve the byte for length assuming that length < 128. .varint(0) = &.arr[.pos-1] } .varint()for , := range { .varint() }if != 0 { .varint(uint64()) } elseif == 0 { .varint(0) } elseif > 0 { .varint(traceStackID(, .stk[:], )) } := .pos - if > {throw("invalid length of trace event") }if != nil {// Fill in actual length. * = byte( - 2) }}// traceCPUSample writes a CPU profile sample stack to the execution tracer's// profiling buffer. It is called from a signal handler, so is limited in what// it can do.func ( *g, *p, []uintptr) {if !trace.enabled {// Tracing is usually turned off; don't spend time acquiring the signal // lock unless it's active.return }// Match the clock used in traceEventLocked := traceClockNow()// The "header" here is the ID of the P that was running the profiled code, // followed by the ID of the goroutine. (For normal CPU profiling, it's // usually the number of samples with the given stack.) Near syscalls, pp // may be nil. Reporting goid of 0 is fine for either g0 or a nil gp.var [2]uint64if != nil {// Overflow records in profBuf have all header values set to zero. Make // sure that real headers have at least one bit set. [0] = uint64(.id)<<1 | 0b1 } else { [0] = 0b10 }if != nil { [1] = .goid }// Allow only one writer at a timefor !trace.signalLock.CompareAndSwap(0, 1) {// TODO: Is it safe to osyield here? https://go.dev/issue/52672osyield() }if := (*profBuf)(atomic.Loadp(unsafe.Pointer(&trace.cpuLogWrite))); != nil {// Note: we don't pass a tag pointer here (how should profiling tags // interact with the execution tracer?), but if we did we'd need to be // careful about write barriers. See the long comment in profBuf.write. .write(nil, int64(), [:], ) }trace.signalLock.Store(0)}func () { := &trace.cpuLogBuffor { , , := trace.cpuLogRead.read(profBufNonBlocking)iflen() == 0 {break }forlen() > 0 {iflen() < 4 || [0] > uint64(len()) {break// truncated profile }if [0] < 4 || != nil && len() < 1 {break// malformed profile }iflen() < 1 {break// mismatched profile records and tags } := [1] := [2] >> 1if := ([2] & 0b1) != 0; ! { = ^uint64(0) } := [3] := [4:[0]] := len() == 1 && [2] == 0 && [3] == 0 = [[0]:]// No support here for reporting goroutine tags at the moment; if // that information is to be part of the execution trace, we'd // probably want to see when the tags are applied and when they // change, instead of only seeing them when we get a CPU sample. = [1:]if {// Looks like an overflow record from the profBuf. Not much to // do here, we only want to report full records. // // TODO: should we start a goroutine to drain the profBuf, // rather than relying on a high-enough volume of tracing events // to keep ReadTrace busy? https://go.dev/issue/52674continue } := .ptr()if == nil {systemstack(func() { * = traceFlush(*, 0) }) = .ptr() } := 1 .stk[0] = logicalStackSentinelfor ; < len(.stk) && -1 < len(); ++ { .stk[] = uintptr([-1]) } := trace.stackTab.put(.stk[:])traceEventLocked(0, nil, 0, , traceEvCPUSample, , 1, uint64(), , ) } }}// 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// pcBuf[1:] after inline expansion.constlogicalStackSentinel = ^uintptr(0)// traceStackID captures a stack trace into pcBuf, registers it in the trace// stack table, and returns its unique ID. pcBuf should have a length equal to// traceStackSize. skip controls the number of leaf frames to omit in order to// hide tracer internals from stack traces, see CL 5523.func ( *m, []uintptr, int) uint64 { := getg() := .curg := 1iftracefpunwindoff() || .hasCgoOnStack() {// Slow path: Unwind using default unwinder. Used when frame pointer // unwinding is unavailable or disabled (tracefpunwindoff), or might // produce incomplete results or crashes (hasCgoOnStack). Note that no // cgo callback related crashes have been observed yet. The main // motivation is to take advantage of a potentially registered cgo // symbolizer. [0] = logicalStackSentinelif == { += callers(+1, [1:]) } elseif != nil { += gcallers(, , [1:]) } } else {// Fast path: Unwind using frame pointers. [0] = uintptr()if == { += fpTracebackPCs(unsafe.Pointer(getfp()), [1:]) } elseif != nil {// We're called on the g0 stack through mcall(fn) or systemstack(fn). To // behave like gcallers above, we start unwinding from sched.bp, which // points to the caller frame of the leaf frame on g's stack. The return // address of the leaf frame is stored in sched.pc, which we manually // capture here. [1] = .sched.pc += 1 + fpTracebackPCs(unsafe.Pointer(.sched.bp), [2:]) } }if > 0 { -- // skip runtime.goexit }if > 0 && .goid == 1 { -- // skip runtime.main } := trace.stackTab.put([:])returnuint64()}// tracefpunwindoff returns true if frame pointer unwinding for the tracer is// disabled via GODEBUG or not supported by the architecture.// TODO(#60254): support frame pointer unwinding on plan9/amd64.func () bool {returndebug.tracefpunwindoff != 0 || (goarch.ArchFamily != goarch.AMD64 && goarch.ArchFamily != goarch.ARM64) || goos.IsPlan9 == 1}// fpTracebackPCs populates pcBuf with the return addresses for each frame and// returns the number of PCs written to pcBuf. The returned PCs correspond to// "physical frames" rather than "logical frames"; that is if A is inlined into// B, this will return a PC for only B.func ( unsafe.Pointer, []uintptr) ( int) {for = 0; < len() && != nil; ++ {// return addr sits one word above the frame pointer [] = *(*uintptr)(unsafe.Pointer(uintptr() + goarch.PtrSize))// follow the frame pointer to the next one = unsafe.Pointer(*(*uintptr)()) }return}// traceAcquireBuffer returns trace buffer to use and, if necessary, locks it.func () ( *m, int32, *traceBufPtr) {// Any time we acquire a buffer, we may end up flushing it, // but flushes are rare. Record the lock edge even if it // doesn't happen this time.lockRankMayTraceFlush() = acquirem()if := .p.ptr(); != nil {return , .id, &.trace.buf }lock(&trace.bufLock)return , traceGlobProc, &trace.buf}// traceReleaseBuffer releases a buffer previously acquired with traceAcquireBuffer.func ( *m, int32) {if == traceGlobProc {unlock(&trace.bufLock) }releasem()}// lockRankMayTraceFlush records the lock ranking effects of a// potential call to traceFlush.func () {lockWithRankMayAcquire(&trace.lock, getLockRank(&trace.lock))}// traceFlush puts buf onto stack of full buffers and returns an empty buffer.//// This must run on the system stack because it acquires trace.lock.////go:systemstackfunc ( traceBufPtr, int32) traceBufPtr {lock(&trace.lock)if != 0 {traceFullQueue() }iftrace.empty != 0 { = trace.emptytrace.empty = .ptr().link } else { = traceBufPtr(sysAlloc(unsafe.Sizeof(traceBuf{}), &memstats.other_sys))if == 0 {throw("trace: out of memory") } } := .ptr() .link.set(nil) .pos = 0// initialize the buffer for a new batch := traceClockNow()if <= .lastTime { = .lastTime + 1 } .lastTime = .byte(traceEvBatch | 1<<traceArgCountShift) .varint(uint64()) .varint(uint64())unlock(&trace.lock)return}// traceString adds a string to the trace.strings and returns the id.func ( *traceBufPtr, int32, string) (uint64, *traceBufPtr) {if == "" {return0, }lock(&trace.stringsLock)ifraceenabled {// raceacquire is necessary because the map access // below is race annotated.raceacquire(unsafe.Pointer(&trace.stringsLock)) }if , := trace.strings[]; {ifraceenabled {racerelease(unsafe.Pointer(&trace.stringsLock)) }unlock(&trace.stringsLock)return , }trace.stringSeq++ := trace.stringSeqtrace.strings[] = ifraceenabled {racerelease(unsafe.Pointer(&trace.stringsLock)) }unlock(&trace.stringsLock)// memory allocation in above may trigger tracing and // cause *bufp changes. Following code now works with *bufp, // so there must be no memory allocation or any activities // that causes tracing after this point. := .ptr() := 1 + 2*traceBytesPerNumber + len()if == nil || len(.arr)-.pos < {systemstack(func() { = traceFlush(traceBufPtrOf(), ).ptr() .set() }) } .byte(traceEvString) .varint()// double-check the string and the length can fit. // Otherwise, truncate the string. := len()if := len(.arr) - .pos; < +traceBytesPerNumber { = } .varint(uint64()) .pos += copy(.arr[.pos:], [:]) .set()return , }// varint appends v to buf in little-endian-base-128 encoding.func ( *traceBuf) ( uint64) { := .posfor ; >= 0x80; >>= 7 { .arr[] = 0x80 | byte() ++ } .arr[] = byte() ++ .pos = }// varintAt writes varint v at byte position pos in buf. This always// consumes traceBytesPerNumber bytes. This is intended for when the// caller needs to reserve space for a varint but can't populate it// until later.func ( *traceBuf) ( int, uint64) {for := 0; < traceBytesPerNumber; ++ {if < traceBytesPerNumber-1 { .arr[] = 0x80 | byte() } else { .arr[] = byte() } >>= 7 ++ }}// byte appends v to buf.func ( *traceBuf) ( byte) { .arr[.pos] = .pos++}// traceStackTable maps stack traces (arrays of PC's) to unique uint32 ids.// It is lock-free for reading.typetraceStackTablestruct {lockmutex// Must be acquired on the system stacksequint32memtraceAlloctab [1 << 13]traceStackPtr}// traceStack is a single stack in traceStackTable.typetraceStackstruct {linktraceStackPtrhashuintptriduint32nintstk [0]uintptr// real type [n]uintptr}typetraceStackPtruintptrfunc ( traceStackPtr) () *traceStack { return (*traceStack)(unsafe.Pointer()) }// stack returns slice of PCs.func ( *traceStack) () []uintptr {return (*[traceStackSize]uintptr)(unsafe.Pointer(&.stk))[:.n]}// put returns a unique id for the stack trace pcs and caches it in the table,// if it sees the trace for the first time.func ( *traceStackTable) ( []uintptr) uint32 {iflen() == 0 {return0 } := memhash(unsafe.Pointer(&[0]), 0, uintptr(len())*unsafe.Sizeof([0]))// First, search the hashtable w/o the mutex.if := .find(, ); != 0 {return }// Now, double check under the mutex. // Switch to the system stack so we can acquire tab.lockvaruint32systemstack(func() {lock(&.lock)if = .find(, ); != 0 {unlock(&.lock)return }// Create new record. .seq++ := .newStack(len()) .hash = .id = .seq = .id .n = len() := .stack()copy(, ) := int( % uintptr(len(.tab))) .link = .tab[]atomicstorep(unsafe.Pointer(&.tab[]), unsafe.Pointer())unlock(&.lock) })return}// find checks if the stack trace pcs is already present in the table.func ( *traceStackTable) ( []uintptr, uintptr) uint32 { := int( % uintptr(len(.tab))):for := .tab[].ptr(); != nil; = .link.ptr() {if .hash == && .n == len() {for , := range .stack() {if != [] {continue } }return .id } }return0}// newStack allocates a new stack of size n.func ( *traceStackTable) ( int) *traceStack {return (*traceStack)(.mem.alloc(unsafe.Sizeof(traceStack{}) + uintptr()*goarch.PtrSize))}// traceFrames returns the frames corresponding to pcs. It may// allocate and may emit trace events.func ( traceBufPtr, []uintptr) ([]traceFrame, traceBufPtr) { := make([]traceFrame, 0, len()) := CallersFrames()for {vartraceFrame , := .Next() , = traceFrameForPC(, 0, ) = append(, )if ! {return , } }}// dump writes all previously cached stacks to trace buffers,// releases all memory and resets state.//// This must run on the system stack because it calls traceFlush.////go:systemstackfunc ( *traceStackTable) ( traceBufPtr) traceBufPtr {for := range .tab { := .tab[].ptr()for ; != nil; = .link.ptr() {var []traceFrame , = traceFrames(, fpunwindExpand(.stack()))// Estimate the size of this record. This // bound is pretty loose, but avoids counting // lots of varint sizes. := 1 + traceBytesPerNumber + (2+4*len())*traceBytesPerNumber// Make sure we have enough buffer space.if := .ptr(); len(.arr)-.pos < { = traceFlush(, 0) }// Emit header, with space reserved for length. := .ptr() .byte(traceEvStack | 3<<traceArgCountShift) := .pos .pos += traceBytesPerNumber// Emit body. := .pos .varint(uint64(.id)) .varint(uint64(len()))for , := range { .varint(uint64(.PC)) .varint(.funcID) .varint(.fileID) .varint(.line) }// Fill in size header. .varintAt(, uint64(.pos-)) } } .mem.drop() * = traceStackTable{}lockInit(&((*).lock), lockRankTraceStackTab)return}// fpunwindExpand checks if pcBuf contains logical frames (which include inlined// frames) or physical frames (produced by frame pointer unwinding) using a// sentinel value in pcBuf[0]. Logical frames are simply returned without the// sentinel. Physical frames are turned into logical frames via inline unwinding// and by applying the skip value that's stored in pcBuf[0].func ( []uintptr) []uintptr {iflen() > 0 && [0] == logicalStackSentinel {// pcBuf contains logical rather than inlined frames, skip has already been // applied, just return it without the sentinel value in pcBuf[0].return [1:] }var (pcvalueCache = abi.FuncIDNormal = make([]uintptr, 0, traceStackSize) = [0]// skipOrAdd skips or appends retPC to newPCBuf and returns true if more // pcs can be added. = func( uintptr) bool {if > 0 { -- } else { = append(, ) }returnlen() < cap() } ):for , := range [1:] { := - 1 := findfunc()if !.valid() {// There is no funcInfo if callPC belongs to a C function. In this case // we still keep the pc, but don't attempt to expand inlined frames.if := (); ! {break }continue } , := newInlineUnwinder(, , &)for ; .valid(); = .next() { := .srcFunc()if .funcID == abi.FuncIDWrapper && elideWrapperCalling() {// ignore wrappers } elseif := (.pc + 1); ! {break } = .funcID } }return}typetraceFramestruct {PCuintptrfuncIDuint64fileIDuint64lineuint64}// traceFrameForPC records the frame information.// It may allocate memory.func ( traceBufPtr, int32, Frame) (traceFrame, traceBufPtr) { := &vartraceFrame .PC = .PC := .Functionconst = 1 << 10iflen() > { = [len()-:] } .funcID, = traceString(, , ) .line = uint64(.Line) := .Fileiflen() > { = [len()-:] } .fileID, = traceString(, , )return , (*)}// traceAlloc is a non-thread-safe region allocator.// It holds a linked list of traceAllocBlock.typetraceAllocstruct {headtraceAllocBlockPtroffuintptr}// traceAllocBlock is a block in traceAlloc.//// traceAllocBlock is allocated from non-GC'd memory, so it must not// contain heap pointers. Writes to pointers to traceAllocBlocks do// not need write barriers.typetraceAllocBlockstruct { _ sys.NotInHeapnexttraceAllocBlockPtrdata [64<<10 - goarch.PtrSize]byte}// TODO: Since traceAllocBlock is now embedded runtime/internal/sys.NotInHeap, this isn't necessary.typetraceAllocBlockPtruintptrfunc ( traceAllocBlockPtr) () *traceAllocBlock { return (*traceAllocBlock)(unsafe.Pointer()) }func ( *traceAllocBlockPtr) ( *traceAllocBlock) { * = traceAllocBlockPtr(unsafe.Pointer()) }// alloc allocates n-byte block.func ( *traceAlloc) ( uintptr) unsafe.Pointer { = alignUp(, goarch.PtrSize)if .head == 0 || .off+ > uintptr(len(.head.ptr().data)) {if > uintptr(len(.head.ptr().data)) {throw("trace: alloc too large") } := (*traceAllocBlock)(sysAlloc(unsafe.Sizeof(traceAllocBlock{}), &memstats.other_sys))if == nil {throw("trace: out of memory") } .next.set(.head.ptr()) .head.set() .off = 0 } := &.head.ptr().data[.off] .off += returnunsafe.Pointer()}// drop frees all previously allocated memory and resets the allocator.func ( *traceAlloc) () {for .head != 0 { := .head.ptr() .head.set(.next.ptr())sysFree(unsafe.Pointer(), unsafe.Sizeof(traceAllocBlock{}), &memstats.other_sys) }}// The following functions write specific events to trace.func ( int32) {traceEvent(traceEvGomaxprocs, 1, uint64())}func () {traceEvent(traceEvProcStart, -1, uint64(getg().m.id))}func ( *p) {// Sysmon and stopTheWorld can stop Ps blocked in syscalls, // to handle this we temporary employ the P. := acquirem() := .p .p.set()traceEvent(traceEvProcStop, -1) .p = releasem()}func () {traceEvent(traceEvGCStart, 3, trace.seqGC)trace.seqGC++}func () {traceEvent(traceEvGCDone, -1)}func ( stwReason) {// Don't trace if this STW is for trace start/stop, since traceEnabled // switches during a STW.if == stwStartTrace || == stwStopTrace {return }getg().m.trace.tracedSTWStart = truetraceEvent(traceEvSTWStart, -1, uint64())}func () { := getg().mif !.trace.tracedSTWStart {return } .trace.tracedSTWStart = falsetraceEvent(traceEvSTWDone, -1)}// traceGCSweepStart prepares to trace a sweep loop. This does not// emit any events until traceGCSweepSpan is called.//// traceGCSweepStart must be paired with traceGCSweepDone and there// must be no preemption points between these two calls.func () {// Delay the actual GCSweepStart event until the first span // sweep. If we don't sweep anything, don't emit any events. := getg().m.p.ptr()if .trace.inSweep {throw("double traceGCSweepStart") } .trace.inSweep, .trace.swept, .trace.reclaimed = true, 0, 0}// traceGCSweepSpan traces the sweep of a single page.//// This may be called outside a traceGCSweepStart/traceGCSweepDone// pair; however, it will not emit any trace events in this case.func ( uintptr) { := getg().m.p.ptr()if .trace.inSweep {if .trace.swept == 0 {traceEvent(traceEvGCSweepStart, 1) } .trace.swept += }}func () { := getg().m.p.ptr()if !.trace.inSweep {throw("missing traceGCSweepStart") }if .trace.swept != 0 {traceEvent(traceEvGCSweepDone, -1, uint64(.trace.swept), uint64(.trace.reclaimed)) } .trace.inSweep = false}func () {traceEvent(traceEvGCMarkAssistStart, 1)}func () {traceEvent(traceEvGCMarkAssistDone, -1)}func ( *g, uintptr) { .trace.seq = 0 .trace.lastP = getg().m.p// +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum. := trace.stackTab.put([]uintptr{logicalStackSentinel, startPCforTrace() + sys.PCQuantum})traceEvent(traceEvGoCreate, 2, .goid, uint64())}func () { := getg().m.curg := .m.p .trace.seq++if .ptr().gcMarkWorkerMode != gcMarkWorkerNotWorker {traceEvent(traceEvGoStartLabel, -1, .goid, .trace.seq, trace.markWorkerLabels[.ptr().gcMarkWorkerMode]) } elseif .trace.lastP == {traceEvent(traceEvGoStartLocal, -1, .goid) } else { .trace.lastP = traceEvent(traceEvGoStart, -1, .goid, .trace.seq) }}func () {traceEvent(traceEvGoEnd, -1)}func () { := getg() .trace.lastP = .m.ptraceEvent(traceEvGoSched, 1)}func () { := getg() .trace.lastP = .m.ptraceEvent(traceEvGoPreempt, 1)}func ( traceBlockReason, int) {// Convert the block reason directly to a trace event type. // See traceBlockReason for more information.traceEvent(byte(), )}func ( *g, int) { := getg().m.p .trace.seq++if .trace.lastP == {traceEvent(traceEvGoUnblockLocal, , .goid) } else { .trace.lastP = traceEvent(traceEvGoUnblock, , .goid, .trace.seq) }}func () {varintswitch {casetracefpunwindoff():// Unwind by skipping 1 frame relative to gp.syscallsp which is captured 3 // frames above this frame. For frame pointer unwinding we produce the same // results by hard coding the number of frames in between our caller and the // actual syscall, see cases below. // TODO(felixge): Implement gp.syscallbp to avoid this workaround? = 1caseGOOS == "solaris" || GOOS == "illumos":// These platforms don't use a libc_read_trampoline. = 3default:// Skip the extra trampoline frame used on most systems. = 4 }getg().m.curg.trace.tracedSyscallEnter = truetraceEvent(traceEvGoSysCall, )}func () { := getg().m.curgif !.trace.tracedSyscallEnter {// There was no syscall entry traced for us at all, so there's definitely // no EvGoSysBlock or EvGoInSyscall before us, which EvGoSysExit requires.return } .trace.tracedSyscallEnter = false := .trace.sysExitTimeif != 0 && < trace.startTime {// There is a race between the code that initializes sysExitTimes // (in exitsyscall, which runs without a P, and therefore is not // stopped with the rest of the world) and the code that initializes // a new trace. The recorded sysExitTime must therefore be treated // as "best effort". If they are valid for this trace, then great, // use them for greater accuracy. But if they're not valid for this // trace, assume that the trace was started after the actual syscall // exit (but before we actually managed to start the goroutine, // aka right now), and assign a fresh time stamp to keep the log consistent. = 0 } .trace.sysExitTime = 0 .trace.seq++ .trace.lastP = .m.ptraceEvent(traceEvGoSysExit, -1, .goid, .trace.seq, uint64())}func ( *p) {// Sysmon and stopTheWorld can declare syscalls running on remote Ps as blocked, // to handle this we temporary employ the P. := acquirem() := .p .p.set()traceEvent(traceEvGoSysBlock, -1) .p = releasem()}func ( uint64) {traceEvent(traceEvHeapAlloc, -1, )}func () { := gcController.heapGoal()if == ^uint64(0) {// Heap-based triggering is disabled.traceEvent(traceEvHeapGoal, -1, 0) } else {traceEvent(traceEvHeapGoal, -1, ) }}// To access runtime functions from runtime/trace.// See runtime/trace/annotation.go//go:linkname trace_userTaskCreate runtime/trace.userTaskCreatefunc (, uint64, string) {if !trace.enabled {return }// Same as in traceEvent. , , := traceAcquireBuffer()if !trace.enabled && !.trace.startingTrace {traceReleaseBuffer(, )return } , := traceString(, , )traceEventLocked(0, , , , traceEvUserTaskCreate, 0, 3, , , )traceReleaseBuffer(, )}//go:linkname trace_userTaskEnd runtime/trace.userTaskEndfunc ( uint64) {traceEvent(traceEvUserTaskEnd, 2, )}//go:linkname trace_userRegion runtime/trace.userRegionfunc (, uint64, string) {if !trace.enabled {return } , , := traceAcquireBuffer()if !trace.enabled && !.trace.startingTrace {traceReleaseBuffer(, )return } , := traceString(, , )traceEventLocked(0, , , , traceEvUserRegion, 0, 3, , , )traceReleaseBuffer(, )}//go:linkname trace_userLog runtime/trace.userLogfunc ( uint64, , string) {if !trace.enabled {return } , , := traceAcquireBuffer()if !trace.enabled && !.trace.startingTrace {traceReleaseBuffer(, )return } , := traceString(, , )// The log message is recorded after all of the normal trace event // arguments, including the task, category, and stack IDs. We must ask // traceEventLocked to reserve extra space for the length of the message // and the message itself. := traceBytesPerNumber + len()traceEventLocked(, , , , traceEvUserLog, 0, 3, , ) := .ptr()// double-check the message and its length can fit. // Otherwise, truncate the message. := len()if := len(.arr) - .pos; < +traceBytesPerNumber { = } .varint(uint64()) .pos += copy(.arr[.pos:], [:])traceReleaseBuffer(, )}// the start PC of a goroutine for tracing purposes. If pc is a wrapper,// it returns the PC of the wrapped function. Otherwise it returns pc.func ( uintptr) uintptr { := findfunc()if !.valid() {return// may happen for locked g in extra M since its pc is 0. } := funcdata(, abi.FUNCDATA_WrapInfo)if == nil {return// not a wrapper }return .datap.textAddr(*(*uint32)())}// traceOneNewExtraM registers the fact that a new extra M was created with// the tracer. This matters if the M (which has an attached G) is used while// the trace is still active because if it is, we need the fact that it exists// to show up in the final trace.func ( *g) {// Trigger two trace events for the locked g in the extra m, // since the next event of the g will be traceEvGoSysExit in exitsyscall, // while calling from C thread to Go.traceGoCreate(, 0) // no start pc .trace.seq++traceEvent(traceEvGoInSyscall, -1, .goid)}// traceTime represents a timestamp for the trace.typetraceTimeuint64// traceClockNow returns a monotonic timestamp. The clock this function gets// the timestamp from is specific to tracing, and shouldn't be mixed with other// clock sources.//// nosplit because it's called from exitsyscall, which is nosplit.////go:nosplitfunc () traceTime {returntraceTime(cputicks() / traceTimeDiv)}
The pages are generated with Goldsv0.6.7. (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.