PNG  IHDRxsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<,tEXtComment File Manager

File Manager

Path: /opt/golang/1.22.0/src/runtime/

Viewing File: trace2runtime.go

// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build goexperiment.exectracer2

// Runtime -> tracer API.

package runtime

import (
	"runtime/internal/atomic"
	_ "unsafe" // for go:linkname
)

// gTraceState is per-G state for the tracer.
type gTraceState struct {
	traceSchedResourceState
}

// reset resets the gTraceState for a new goroutine.
func (s *gTraceState) reset() {
	s.seq = [2]uint64{}
	// N.B. s.statusTraced is managed and cleared separately.
}

// 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]*traceBuf   // Per-M traceBuf for writing. Indexed by trace.gen%2.
	link    *m             // Snapshot of alllink or freelink.
}

// pTraceState is per-P state for the tracer.
type pTraceState struct {
	traceSchedResourceState

	// mSyscallID is the ID of the M this was bound to before entering a syscall.
	mSyscallID int64

	// maySweep indicates the sweep events should be traced.
	// This is used to defer the sweep start event until a span
	// has actually been swept.
	maySweep bool

	// inSweep indicates that at least one sweep event has been traced.
	inSweep bool

	// swept and reclaimed track the number of bytes swept and reclaimed
	// by sweeping in the current sweep loop (while maySweep was true).
	swept, reclaimed uintptr
}

// traceLockInit initializes global trace locks.
func traceLockInit() {
	// Sharing a lock rank here is fine because they should never be accessed
	// together. If they are, we want to find out immediately.
	lockInit(&trace.stringTab[0].lock, lockRankTraceStrings)
	lockInit(&trace.stringTab[0].tab.lock, lockRankTraceStrings)
	lockInit(&trace.stringTab[1].lock, lockRankTraceStrings)
	lockInit(&trace.stringTab[1].tab.lock, lockRankTraceStrings)
	lockInit(&trace.stackTab[0].tab.lock, lockRankTraceStackTab)
	lockInit(&trace.stackTab[1].tab.lock, lockRankTraceStackTab)
	lockInit(&trace.lock, lockRankTrace)
}

// lockRankMayTraceFlush records the lock ranking effects of a
// potential call to traceFlush.
//
// nosplit because traceAcquire is nosplit.
//
//go:nosplit
func lockRankMayTraceFlush() {
	lockWithRankMayAcquire(&trace.lock, getLockRank(&trace.lock))
}

// 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.
type traceBlockReason uint8

const (
	traceBlockGeneric traceBlockReason = iota
	traceBlockForever
	traceBlockNet
	traceBlockSelect
	traceBlockCondWait
	traceBlockSync
	traceBlockChanSend
	traceBlockChanRecv
	traceBlockGCMarkAssist
	traceBlockGCSweep
	traceBlockSystemGoroutine
	traceBlockPreempted
	traceBlockDebugCall
	traceBlockUntilGCEnds
	traceBlockSleep
)

var traceBlockReasonStrings = [...]string{
	traceBlockGeneric:         "unspecified",
	traceBlockForever:         "forever",
	traceBlockNet:             "network",
	traceBlockSelect:          "select",
	traceBlockCondWait:        "sync.(*Cond).Wait",
	traceBlockSync:            "sync",
	traceBlockChanSend:        "chan send",
	traceBlockChanRecv:        "chan receive",
	traceBlockGCMarkAssist:    "GC mark assist wait for work",
	traceBlockGCSweep:         "GC background sweeper wait",
	traceBlockSystemGoroutine: "system goroutine wait",
	traceBlockPreempted:       "preempted",
	traceBlockDebugCall:       "wait for debug call",
	traceBlockUntilGCEnds:     "wait until GC ends",
	traceBlockSleep:           "sleep",
}

// traceGoStopReason is an enumeration of reasons a goroutine might yield.
//
// Note that traceGoStopReasons should not be compared, since reasons that are
// distinct by name may *not* be distinct by value.
type traceGoStopReason uint8

const (
	traceGoStopGeneric traceGoStopReason = iota
	traceGoStopGoSched
	traceGoStopPreempted
)

var traceGoStopReasonStrings = [...]string{
	traceGoStopGeneric:   "unspecified",
	traceGoStopGoSched:   "runtime.Gosched",
	traceGoStopPreempted: "preempted",
}

// traceEnabled returns true if the trace is currently enabled.
//
//go:nosplit
func traceEnabled() bool {
	return trace.gen.Load() != 0
}

// traceShuttingDown returns true if the trace is currently shutting down.
func traceShuttingDown() bool {
	return trace.shutdown.Load()
}

// traceLocker represents an M writing trace events. While a traceLocker value
// is valid, the tracer observes all operations on the G/M/P or trace events being
// written as happening atomically.
type traceLocker struct {
	mp  *m
	gen uintptr
}

// debugTraceReentrancy checks if the trace is reentrant.
//
// This is optional because throwing in a function makes it instantly
// not inlineable, and we want traceAcquire to be inlineable for
// low overhead when the trace is disabled.
const debugTraceReentrancy = false

// traceAcquire prepares this M for writing one or more trace events.
//
// nosplit because it's called on the syscall path when stack movement is forbidden.
//
//go:nosplit
func traceAcquire() traceLocker {
	if !traceEnabled() {
		return traceLocker{}
	}
	return traceAcquireEnabled()
}

// traceAcquireEnabled is the traceEnabled path for traceAcquire. It's explicitly
// broken out to make traceAcquire inlineable to keep the overhead of the tracer
// when it's disabled low.
//
// nosplit because it's called by traceAcquire, which is nosplit.
//
//go:nosplit
func traceAcquireEnabled() traceLocker {
	// Any time we acquire a traceLocker, we may flush a trace buffer. But
	// buffer flushes are rare. Record the lock edge even if it doesn't happen
	// this time.
	lockRankMayTraceFlush()

	// Prevent preemption.
	mp := acquirem()

	// Acquire the trace seqlock. This prevents traceAdvance from moving forward
	// until all Ms are observed to be outside of their seqlock critical section.
	//
	// Note: The seqlock is mutated here and also in traceCPUSample. If you update
	// usage of the seqlock here, make sure to also look at what traceCPUSample is
	// doing.
	seq := mp.trace.seqlock.Add(1)
	if debugTraceReentrancy && seq%2 != 1 {
		throw("bad use of trace.seqlock or tracer is reentrant")
	}

	// N.B. This load of gen appears redundant with the one in traceEnabled.
	// However, it's very important that the gen we use for writing to the trace
	// is acquired under a traceLocker so traceAdvance can make sure no stale
	// gen values are being used.
	//
	// Because we're doing this load again, it also means that the trace
	// might end up being disabled when we load it. In that case we need to undo
	// what we did and bail.
	gen := trace.gen.Load()
	if gen == 0 {
		mp.trace.seqlock.Add(1)
		releasem(mp)
		return traceLocker{}
	}
	return traceLocker{mp, gen}
}

// ok returns true if the traceLocker is valid (i.e. tracing is enabled).
//
// nosplit because it's called on the syscall path when stack movement is forbidden.
//
//go:nosplit
func (tl traceLocker) ok() bool {
	return tl.gen != 0
}

// traceRelease indicates that this M is done writing trace events.
//
// nosplit because it's called on the syscall path when stack movement is forbidden.
//
//go:nosplit
func traceRelease(tl traceLocker) {
	seq := tl.mp.trace.seqlock.Add(1)
	if debugTraceReentrancy && seq%2 != 0 {
		print("runtime: seq=", seq, "\n")
		throw("bad use of trace.seqlock")
	}
	releasem(tl.mp)
}

// traceExitingSyscall marks a goroutine as exiting the syscall slow path.
//
// Must be paired with a traceExitedSyscall call.
func traceExitingSyscall() {
	trace.exitingSyscall.Add(1)
}

// traceExitedSyscall marks a goroutine as having exited the syscall slow path.
func traceExitedSyscall() {
	trace.exitingSyscall.Add(-1)
}

// Gomaxprocs emits a ProcsChange event.
func (tl traceLocker) Gomaxprocs(procs int32) {
	tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvProcsChange, traceArg(procs), tl.stack(1))
}

// ProcStart traces a ProcStart event.
//
// Must be called with a valid P.
func (tl traceLocker) ProcStart() {
	pp := tl.mp.p.ptr()
	// 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).commit(traceEvProcStart, 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).commit(traceEvProcStop)
}

// 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).commit(traceEvGCActive, traceArg(trace.seqGC))
	// N.B. Only one GC can be running at a time, so this is naturally
	// serialized by the caller.
	trace.seqGC++
}

// GCStart traces a GCBegin 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) GCStart() {
	tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvGCBegin, 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++
}

// GCDone traces a GCEnd 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) GCDone() {
	tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvGCEnd, traceArg(trace.seqGC))
	// N.B. Only one GC can be running at a time, so this is naturally
	// serialized by the caller.
	trace.seqGC++
}

// STWStart traces a STWBegin event.
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).commit(traceEvSTWBegin, 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).commit(traceEvSTWEnd)
}

// GCSweepStart prepares to trace a sweep loop. This does not
// emit any events until traceGCSweepSpan is called.
//
// GCSweepStart must be paired with traceGCSweepDone and there
// must be no preemption points between these two calls.
//
// Must be called with a valid P.
func (tl traceLocker) GCSweepStart() {
	// Delay the actual GCSweepBegin event until the first span
	// sweep. If we don't sweep anything, don't emit any events.
	pp := tl.mp.p.ptr()
	if pp.trace.maySweep {
		throw("double traceGCSweepStart")
	}
	pp.trace.maySweep, pp.trace.swept, pp.trace.reclaimed = true, 0, 0
}

// GCSweepSpan traces the sweep of a single span. If this is
// the first span swept since traceGCSweepStart was called, this
// will emit a GCSweepBegin event.
//
// This may be called outside a traceGCSweepStart/traceGCSweepDone
// pair; however, it will not emit any trace events in this case.
//
// Must be called with a valid P.
func (tl traceLocker) GCSweepSpan(bytesSwept uintptr) {
	pp := tl.mp.p.ptr()
	if pp.trace.maySweep {
		if pp.trace.swept == 0 {
			tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvGCSweepBegin, tl.stack(1))
			pp.trace.inSweep = true
		}
		pp.trace.swept += bytesSwept
	}
}

// GCSweepDone finishes tracing a sweep loop. If any memory was
// swept (i.e. traceGCSweepSpan emitted an event) then this will emit
// a GCSweepEnd event.
//
// Must be called with a valid P.
func (tl traceLocker) GCSweepDone() {
	pp := tl.mp.p.ptr()
	if !pp.trace.maySweep {
		throw("missing traceGCSweepStart")
	}
	if pp.trace.inSweep {
		tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvGCSweepEnd, 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).commit(traceEvGCMarkAssistBegin, tl.stack(1))
}

// GCMarkAssistDone emits a MarkAssistEnd event.
func (tl traceLocker) GCMarkAssistDone() {
	tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvGCMarkAssistEnd)
}

// GoCreate emits a GoCreate event.
func (tl traceLocker) GoCreate(newg *g, pc uintptr) {
	newg.trace.setStatusTraced(tl.gen)
	tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvGoCreate, traceArg(newg.goid), tl.startPC(pc), tl.stack(2))
}

// GoStart emits a GoStart event.
//
// Must be called with a valid P.
func (tl traceLocker) GoStart() {
	gp := getg().m.curg
	pp := gp.m.p
	w := tl.eventWriter(traceGoRunnable, traceProcRunning)
	w = w.write(traceEvGoStart, traceArg(gp.goid), gp.trace.nextSeq(tl.gen))
	if pp.ptr().gcMarkWorkerMode != gcMarkWorkerNotWorker {
		w = w.write(traceEvGoLabel, trace.markWorkerLabels[tl.gen%2][pp.ptr().gcMarkWorkerMode])
	}
	w.end()
}

// GoEnd emits a GoDestroy event.
//
// TODO(mknyszek): Rename this to GoDestroy.
func (tl traceLocker) GoEnd() {
	tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvGoDestroy)
}

// GoSched emits a GoStop event with a GoSched reason.
func (tl traceLocker) GoSched() {
	tl.GoStop(traceGoStopGoSched)
}

// GoPreempt emits a GoStop event with a GoPreempted reason.
func (tl traceLocker) GoPreempt() {
	tl.GoStop(traceGoStopPreempted)
}

// GoStop emits a GoStop event with the provided reason.
func (tl traceLocker) GoStop(reason traceGoStopReason) {
	tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvGoStop, 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).commit(traceEvGoBlock, 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.
	w := tl.eventWriter(traceGoRunning, traceProcRunning)
	if !gp.trace.statusWasTraced(tl.gen) && gp.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.
		w.w = w.w.writeGoStatus(gp.goid, -1, traceGoWaiting, gp.inMarkAssist)
	}
	w.commit(traceEvGoUnblock, traceArg(gp.goid), gp.trace.nextSeq(tl.gen), tl.stack(skip))
}

// GoSysCall emits a GoSyscallBegin event.
//
// Must be called with a valid P.
func (tl traceLocker) GoSysCall() {
	var skip int
	switch {
	case tracefpunwindoff():
		// Unwind by skipping 1 frame relative to gp.syscallsp which is captured 3
		// 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?
		skip = 1
	case GOOS == "solaris" || GOOS == "illumos":
		// These platforms don't use a libc_read_trampoline.
		skip = 3
	default:
		// Skip the extra trampoline frame used on most systems.
		skip = 4
	}
	// 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).commit(traceEvGoSyscallBegin, pp.trace.nextSeq(tl.gen), tl.stack(skip))
}

// GoSysExit emits a GoSyscallEnd event, possibly along with a GoSyscallBlocked event
// if lostP is true.
//
// lostP must be true in all cases that a goroutine loses its P during a syscall.
// This means it's not sufficient to check if it has no P. In particular, it needs to be
// true in the following cases:
// - The goroutine lost its P, it ran some other code, and then got it back. It's now running with that P.
// - 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.
	if lostP {
		ev = traceEvGoSyscallEndBlocked
		procStatus = traceProcRunning // 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).commit(ev)
}

// ProcSteal indicates that our current M stole a P from another M.
//
// inSyscall indicates that we're stealing the P from a syscall context.
//
// The caller must have ownership of pp.
func (tl traceLocker) ProcSteal(pp *p, inSyscall bool) {
	// Grab the M ID we stole from.
	mStolenFrom := pp.trace.mSyscallID
	pp.trace.mSyscallID = -1

	// The status of the proc and goroutine, if we need to emit one here, is not evident from the
	// context of just emitting this event alone. There are two cases. Either we're trying to steal
	// the P just to get its attention (e.g. STW or sysmon retake) or we're trying to steal a P for
	// 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
	if inSyscall {
		goStatus = traceGoSyscall
		procStatus = traceProcSyscallAbandoned
	}
	w := tl.eventWriter(goStatus, procStatus)

	// Emit the status of the P we're stealing. We may have *just* done this when creating the event
	// writer but it's not guaranteed, even if inSyscall is true. Although it might seem like from a
	// syscall context we're always stealing a P for ourselves, we may have not wired it up yet (so
	// it wouldn't be visible to eventWriter) or we may not even intend to wire it up to ourselves
	// at all (e.g. entersyscall_gcwait).
	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.
		w.w = w.w.writeProcStatus(uint64(pp.id), traceProcSyscallAbandoned, pp.trace.inSweep)
	}
	w.commit(traceEvProcSteal, traceArg(pp.id), pp.trace.nextSeq(tl.gen), traceArg(mStolenFrom))
}

// GoSysBlock is a no-op in the new tracer.
func (tl traceLocker) GoSysBlock(pp *p) {
}

// HeapAlloc emits a HeapAlloc event.
func (tl traceLocker) HeapAlloc(live uint64) {
	tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvHeapAlloc, traceArg(live))
}

// HeapGoal reads the current heap goal and emits a HeapGoal event.
func (tl traceLocker) HeapGoal() {
	heapGoal := gcController.heapGoal()
	if heapGoal == ^uint64(0) {
		// Heap-based triggering is disabled.
		heapGoal = 0
	}
	tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvHeapGoal, traceArg(heapGoal))
}

// OneNewExtraM is a no-op in the new tracer. This is worth keeping around though because
// it's a good place to insert a thread-level event about the new extra M.
func (tl traceLocker) OneNewExtraM(_ *g) {
}

// GoCreateSyscall indicates that a goroutine has transitioned from dead to GoSyscall.
//
// Unlike GoCreate, the caller must be running on gp.
//
// This occurs when C code calls into Go. On pthread platforms it occurs only when
// a C thread calls into Go code for the first time.
func (tl traceLocker) GoCreateSyscall(gp *g) {
	// 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).commit(traceEvGoCreateSyscall, traceArg(gp.goid))
}

// GoDestroySyscall indicates that a goroutine has transitioned from GoSyscall to dead.
//
// Must not have a P.
//
// This occurs when Go code returns back to C. On pthread platforms it occurs only when
// the C thread is destroyed.
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).commit(traceEvGoDestroySyscall)
}

// To access runtime functions from runtime/trace.
// See runtime/trace/annotation.go

// trace_userTaskCreate emits a UserTaskCreate event.
//
//go:linkname trace_userTaskCreate runtime/trace.userTaskCreate
func trace_userTaskCreate(id, parentID uint64, taskType string) {
	tl := traceAcquire()
	if !tl.ok() {
		// Need to do this check because the caller won't have it.
		return
	}
	tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvUserTaskBegin, traceArg(id), traceArg(parentID), tl.string(taskType), tl.stack(3))
	traceRelease(tl)
}

// trace_userTaskEnd emits a UserTaskEnd event.
//
//go:linkname trace_userTaskEnd runtime/trace.userTaskEnd
func trace_userTaskEnd(id uint64) {
	tl := traceAcquire()
	if !tl.ok() {
		// Need to do this check because the caller won't have it.
		return
	}
	tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvUserTaskEnd, traceArg(id), tl.stack(2))
	traceRelease(tl)
}

// trace_userTaskEnd emits a UserRegionBegin or UserRegionEnd event,
// depending on mode (0 == Begin, 1 == End).
//
// TODO(mknyszek): Just make this two functions.
//
//go:linkname trace_userRegion runtime/trace.userRegion
func trace_userRegion(id, mode uint64, name string) {
	tl := traceAcquire()
	if !tl.ok() {
		// Need to do this check because the caller won't have it.
		return
	}
	var ev traceEv
	switch mode {
	case 0:
		ev = traceEvUserRegionBegin
	case 1:
		ev = traceEvUserRegionEnd
	default:
		return
	}
	tl.eventWriter(traceGoRunning, traceProcRunning).commit(ev, traceArg(id), tl.string(name), tl.stack(3))
	traceRelease(tl)
}

// trace_userTaskEnd emits a UserRegionBegin or UserRegionEnd event.
//
//go:linkname trace_userLog runtime/trace.userLog
func trace_userLog(id uint64, category, message string) {
	tl := traceAcquire()
	if !tl.ok() {
		// Need to do this check because the caller won't have it.
		return
	}
	tl.eventWriter(traceGoRunning, traceProcRunning).commit(traceEvUserLog, traceArg(id), tl.string(category), tl.uniqueString(message), tl.stack(3))
	traceRelease(tl)
}

// traceProcFree is called when a P is destroyed.
//
// This must run on the system stack to match the old tracer.
//
//go:systemstack
func traceProcFree(_ *p) {
}

// traceThreadDestroy is called when a thread is removed from
// sched.freem.
//
// mp must not be able to emit trace events anymore.
//
// sched.lock must be held to synchronize with traceAdvance.
func traceThreadDestroy(mp *m) {
	assertLockHeld(&sched.lock)

	// Flush all outstanding buffers to maintain the invariant
	// that an M only has active buffers while on sched.freem
	// or allm.
	//
	// Perform a traceAcquire/traceRelease on behalf of mp to
	// synchronize with the tracer trying to flush our buffer
	// as well.
	seq := mp.trace.seqlock.Add(1)
	if debugTraceReentrancy && seq%2 != 1 {
		throw("bad use of trace.seqlock or tracer is reentrant")
	}
	systemstack(func() {
		lock(&trace.lock)
		for i := range mp.trace.buf {
			if mp.trace.buf[i] != nil {
				// N.B. traceBufFlush accepts a generation, but it
				// really just cares about gen%2.
				traceBufFlush(mp.trace.buf[i], uintptr(i))
				mp.trace.buf[i] = nil
			}
		}
		unlock(&trace.lock)
	})
	seq1 := mp.trace.seqlock.Add(1)
	if seq1 != seq+1 {
		print("runtime: seq1=", seq1, "\n")
		throw("bad use of trace.seqlock")
	}
}

// Not used in the new tracer; solely for compatibility with the old tracer.
// nosplit because it's called from exitsyscall without a P.
//
//go:nosplit
func (_ traceLocker) RecordSyscallExitedTime(_ *g, _ *p) {
}
b IDATxytVսϓ22 A@IR :hCiZ[v*E:WũZA ^dQeQ @ !jZ'>gsV仿$|?g)&x-EIENT ;@xT.i%-X}SvS5.r/UHz^_$-W"w)Ɗ/@Z &IoX P$K}JzX:;` &, ŋui,e6mX ԵrKb1ԗ)DADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADA݀!I*]R;I2$eZ#ORZSrr6mteffu*((Pu'v{DIߔ4^pIm'77WEEE;vƎ4-$]'RI{\I&G :IHJ DWBB=\WR޽m o$K(V9ABB.}jѢv`^?IOȅ} ڶmG}T#FJ`56$-ھ}FI&v;0(h;Б38CӧOWf!;A i:F_m9s&|q%=#wZprrrla A &P\\СC[A#! {olF} `E2}MK/vV)i{4BffV\|ۭX`b@kɶ@%i$K z5zhmX[IXZ` 'b%$r5M4º/l ԃߖxhʔ)[@=} K6IM}^5k㏷݆z ΗÿO:gdGBmyT/@+Vɶ纽z񕏵l.y޴it뭷zV0[Y^>Wsqs}\/@$(T7f.InݺiR$푔n.~?H))\ZRW'Mo~v Ov6oԃxz! S,&xm/yɞԟ?'uaSѽb,8GלKboi&3t7Y,)JJ c[nzӳdE&KsZLӄ I?@&%ӟ۶mSMMњ0iؐSZ,|J+N ~,0A0!5%Q-YQQa3}$_vVrf9f?S8`zDADADADADADADADADAdqP,تmMmg1V?rSI꒟]u|l RCyEf٢9 jURbztѰ!m5~tGj2DhG*{H9)꒟ר3:(+3\?/;TUݭʴ~S6lڧUJ*i$d(#=Yݺd{,p|3B))q:vN0Y.jkק6;SɶVzHJJЀ-utѹսk>QUU\޲~]fFnK?&ߡ5b=z9)^|u_k-[y%ZNU6 7Mi:]ۦtk[n X(e6Bb."8cۭ|~teuuw|ήI-5"~Uk;ZicEmN/:]M> cQ^uiƞ??Ңpc#TUU3UakNwA`:Y_V-8.KKfRitv޲* 9S6ֿj,ՃNOMߤ]z^fOh|<>@Å5 _/Iu?{SY4hK/2]4%it5q]GGe2%iR| W&f*^]??vq[LgE_3f}Fxu~}qd-ږFxu~I N>\;͗O֊:̗WJ@BhW=y|GgwܷH_NY?)Tdi'?խwhlmQi !SUUsw4kӺe4rfxu-[nHtMFj}H_u~w>)oV}(T'ebʒv3_[+vn@Ȭ\S}ot}w=kHFnxg S 0eޢm~l}uqZfFoZuuEg `zt~? b;t%>WTkķh[2eG8LIWx,^\thrl^Ϊ{=dž<}qV@ ⠨Wy^LF_>0UkDuʫuCs$)Iv:IK;6ֲ4{^6եm+l3>݆uM 9u?>Zc }g~qhKwڭeFMM~pМuqǿz6Tb@8@Y|jx](^]gf}M"tG -w.@vOqh~/HII`S[l.6nØXL9vUcOoB\xoǤ'T&IǍQw_wpv[kmO{w~>#=P1Pɞa-we:iǏlHo׈꒟f9SzH?+shk%Fs:qVhqY`jvO'ρ?PyX3lх]˾uV{ݞ]1,MzYNW~̈́ joYn}ȚF߾׮mS]F z+EDxm/d{F{-W-4wY듏:??_gPf ^3ecg ҵs8R2מz@TANGj)}CNi/R~}c:5{!ZHӋӾ6}T]G]7W6^n 9*,YqOZj:P?Q DFL|?-^.Ɵ7}fFh׶xe2Pscz1&5\cn[=Vn[ĶE鎀uˌd3GII k;lNmشOuuRVfBE]ۣeӶu :X-[(er4~LHi6:Ѻ@ԅrST0trk%$Č0ez" *z"T/X9|8.C5Feg}CQ%͞ˣJvL/?j^h&9xF`њZ(&yF&Iݻfg#W;3^{Wo^4'vV[[K';+mӍִ]AC@W?1^{එyh +^]fm~iԵ]AB@WTk̏t uR?l.OIHiYyԶ]Aˀ7c:q}ힽaf6Z~қm(+sK4{^6}T*UUu]n.:kx{:2 _m=sAߤU@?Z-Vކеz왍Nэ{|5 pڶn b p-@sPg]0G7fy-M{GCF'%{4`=$-Ge\ eU:m+Zt'WjO!OAF@ik&t݆ϥ_ e}=]"Wz_.͜E3leWFih|t-wZۍ-uw=6YN{6|} |*={Ѽn.S.z1zjۻTH]흾 DuDvmvK.`V]yY~sI@t?/ϓ. m&["+P?MzovVЫG3-GRR[(!!\_,^%?v@ҵő m`Y)tem8GMx.))A]Y i`ViW`?^~!S#^+ѽGZj?Vģ0.))A꨷lzL*]OXrY`DBBLOj{-MH'ii-ϰ ok7^ )쭡b]UXSְmռY|5*cֽk0B7镹%ڽP#8nȎq}mJr23_>lE5$iwui+ H~F`IjƵ@q \ @#qG0".0" l`„.0! ,AQHN6qzkKJ#o;`Xv2>,tێJJ7Z/*A .@fفjMzkg @TvZH3Zxu6Ra'%O?/dQ5xYkU]Rֽkق@DaS^RSּ5|BeHNN͘p HvcYcC5:y #`οb;z2.!kr}gUWkyZn=f Pvsn3p~;4p˚=ē~NmI] ¾ 0lH[_L hsh_ғߤc_њec)g7VIZ5yrgk̞W#IjӪv>՞y睝M8[|]\շ8M6%|@PZڨI-m>=k='aiRo-x?>Q.}`Ȏ:Wsmu u > .@,&;+!!˱tﭧDQwRW\vF\~Q7>spYw$%A~;~}6¾ g&if_=j,v+UL1(tWake:@Ș>j$Gq2t7S?vL|]u/ .(0E6Mk6hiۺzښOrifޱxm/Gx> Lal%%~{lBsR4*}{0Z/tNIɚpV^#Lf:u@k#RSu =S^ZyuR/.@n&΃z~B=0eg뺆#,Þ[B/?H uUf7y Wy}Bwegל`Wh(||`l`.;Ws?V@"c:iɍL֯PGv6zctM̠':wuW;d=;EveD}9J@B(0iհ bvP1{\P&G7D޴Iy_$-Qjm~Yrr&]CDv%bh|Yzni_ˆR;kg}nJOIIwyuL}{ЌNj}:+3Y?:WJ/N+Rzd=hb;dj͒suݔ@NKMԄ jqzC5@y°hL m;*5ezᕏ=ep XL n?מ:r`۵tŤZ|1v`V뽧_csج'ߤ%oTuumk%%%h)uy]Nk[n 'b2 l.=͜E%gf$[c;s:V-͞WߤWh-j7]4=F-X]>ZLSi[Y*We;Zan(ӇW|e(HNNP5[= r4tP &0<pc#`vTNV GFqvTi*Tyam$ߏWyE*VJKMTfFw>'$-ؽ.Ho.8c"@DADADADADADADADADA~j*֘,N;Pi3599h=goضLgiJ5փy~}&Zd9p֚ e:|hL``b/d9p? fgg+%%hMgXosج, ΩOl0Zh=xdjLmhݻoO[g_l,8a]٭+ӧ0$I]c]:粹:Teꢢ"5a^Kgh,&= =՟^߶“ߢE ܹS J}I%:8 IDAT~,9/ʃPW'Mo}zNƍ쨓zPbNZ~^z=4mswg;5 Y~SVMRXUյڱRf?s:w ;6H:ºi5-maM&O3;1IKeamZh͛7+##v+c ~u~ca]GnF'ټL~PPPbn voC4R,ӟgg %hq}@#M4IÇ Oy^xMZx ) yOw@HkN˖-Sǎmb]X@n+i͖!++K3gd\$mt$^YfJ\8PRF)77Wא!Cl$i:@@_oG I{$# 8磌ŋ91A (Im7֭>}ߴJq7ޗt^ -[ԩSj*}%]&' -ɓ'ꫯVzzvB#;a 7@GxI{j޼ƌ.LÇWBB7`O"I$/@R @eee@۷>}0,ɒ2$53Xs|cS~rpTYYY} kHc %&k.], @ADADADADADADADADA@lT<%''*Lo^={رc5h %$+CnܸQ3fҥK}vUVVs9G R,_{xˇ3o߾;TTTd}馛]uuuG~iԩ@4bnvmvfϞ /Peeeq}}za I~,誫{UWW뮻}_~YƍSMMMYχ֝waw\ďcxꩧtEƍկ_?۷5@u?1kNׯWzz/wy>}zj3 k(ٺuq_Zvf̘:~ ABQ&r|!%KҥKgԞ={<_X-z !CyFUUz~ ABQIIIjݺW$UXXDٳZ~ ABQƍecW$<(~<RSSvZujjjԧOZQu@4 8m&&&jԩg$ď1h ͟?_{768@g =@`)))5o6m3)ѣƌJ;wҿUTT /KZR{~a=@0o<*狔iFɶ[ˎ;T]]OX@?K.ۈxN pppppppppppppppppPfl߾] ,{ァk۶mڿo5BTӦMӴiӴ|r DB2e|An!Dy'tkΝ[A $***t5' "!駟oaDnΝ:t֭[gDШQ06qD;@ x M6v(PiizmZ4ew"@̴ixf [~-Fٱc&IZ2|n!?$@{[HTɏ#@hȎI# _m(F /6Z3z'\r,r!;w2Z3j=~GY7"I$iI.p_"?pN`y DD?: _  Gÿab7J !Bx@0 Bo cG@`1C[@0G @`0C_u V1 aCX>W ` | `!<S `"<. `#c`?cAC4 ?c p#~@0?:08&_MQ1J h#?/`7;I  q 7a wQ A 1 Hp !#<8/#@1Ul7=S=K.4Z?E_$i@!1!E4?`P_  @Bă10#: "aU,xbFY1 [n|n #'vEH:`xb #vD4Y hi.i&EΖv#O H4IŶ}:Ikh @tZRF#(tXҙzZ ?I3l7q@õ|ۍ1,GpuY Ꮿ@hJv#xxk$ v#9 5 }_$c S#=+"K{F*m7`#%H:NRSp6I?sIՖ{Ap$I$I:QRv2$Z @UJ*$]<FO4IENDB`