gp.schedlink = 0
// Park the calling goroutine.
- if traceEnabled() {
- traceGoPark(traceBlockDebugCall, 1)
- }
+ trace := traceAcquire()
casGToWaiting(gp, _Grunning, waitReasonDebugCall)
+ if trace.ok() {
+ trace.GoPark(traceBlockDebugCall, 1)
+ traceRelease(trace)
+ }
dropg()
// Directly execute the new goroutine. The debug
// Switch back to the calling goroutine. At some point
// the scheduler will schedule us again and we'll
// finish exiting.
- if traceEnabled() {
- traceGoSched()
- }
+ trace := traceAcquire()
casgstatus(gp, _Grunning, _Grunnable)
+ if trace.ok() {
+ trace.GoSched()
+ traceRelease(trace)
+ }
dropg()
lock(&sched.lock)
globrunqput(gp)
unlock(&sched.lock)
- if traceEnabled() {
- traceGoUnpark(callingG, 0)
- }
+ trace = traceAcquire()
casgstatus(callingG, _Gwaiting, _Grunnable)
+ if trace.ok() {
+ trace.GoUnpark(callingG, 0)
+ traceRelease(trace)
+ }
execute(callingG, true)
})
}
deductSweepCredit(spanBytes, 0)
traceDone := false
- if traceEnabled() {
- traceGCSweepStart()
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GCSweepStart()
+ traceRelease(trace)
}
// If we sweep spanBudget spans without finding any free
}
sweep.active.end(sl)
}
- if traceEnabled() {
- traceGCSweepDone()
+ trace = traceAcquire()
+ if trace.ok() {
+ trace.GCSweepDone()
traceDone = true
+ traceRelease(trace)
}
// We failed to get a span from the mcentral so get one from mheap.
// At this point s is a span that should have free slots.
havespan:
- if traceEnabled() && !traceDone {
- traceGCSweepDone()
+ if !traceDone {
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GCSweepDone()
+ traceRelease(trace)
+ }
}
n := int(s.nelems) - int(s.allocCount)
if n == 0 || s.freeindex == s.nelems || s.allocCount == s.nelems {
// Update it under gcsema to avoid gctrace getting wrong values.
work.userForced = trigger.kind == gcTriggerCycle
- if traceEnabled() {
- traceGCStart()
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GCStart()
+ traceRelease(trace)
}
// Check that all Ps have finished deferred mcache flushes.
mp.traceback = 0
casgstatus(curgp, _Gwaiting, _Grunning)
- if traceEnabled() {
- traceGCDone()
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GCDone()
+ traceRelease(trace)
}
// all done
// If the CPU limiter is enabled, intentionally don't
// assist to reduce the amount of CPU time spent in the GC.
if traced {
- traceGCMarkAssistDone()
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GCMarkAssistDone()
+ traceRelease(trace)
+ }
}
return
}
// We were able to steal all of the credit we
// needed.
if traced {
- traceGCMarkAssistDone()
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GCMarkAssistDone()
+ traceRelease(trace)
+ }
}
return
}
}
-
if traceEnabled() && !traced {
- traced = true
- traceGCMarkAssistStart()
+ trace := traceAcquire()
+ if trace.ok() {
+ traced = true
+ trace.GCMarkAssistStart()
+ traceRelease(trace)
+ }
}
// Perform assist work
// this G's assist debt, or the GC cycle is over.
}
if traced {
- traceGCMarkAssistDone()
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GCMarkAssistDone()
+ traceRelease(trace)
+ }
}
}
// Run the background mark worker.
gp := node.gp.ptr()
+ trace := traceAcquire()
casgstatus(gp, _Gwaiting, _Grunnable)
- if traceEnabled() {
- traceGoUnpark(gp, 0)
+ if trace.ok() {
+ trace.GoUnpark(gp, 0)
+ traceRelease(trace)
}
return gp, now
}
c.triggered = ^uint64(0) // Reset triggered.
// heapLive was updated, so emit a trace event.
- if traceEnabled() {
- traceHeapAlloc(bytesMarked)
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.HeapAlloc(bytesMarked)
+ traceRelease(trace)
}
}
func (c *gcControllerState) update(dHeapLive, dHeapScan int64) {
if dHeapLive != 0 {
+ trace := traceAcquire()
live := gcController.heapLive.Add(dHeapLive)
- if traceEnabled() {
+ if trace.ok() {
// gcController.heapLive changed.
- traceHeapAlloc(live)
+ trace.HeapAlloc(live)
+ traceRelease(trace)
}
}
if gcBlackenEnabled == 0 {
// TODO(mknyszek): This isn't really accurate any longer because the heap
// goal is computed dynamically. Still useful to snapshot, but not as useful.
- if traceEnabled() {
- traceHeapGoal()
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.HeapGoal()
+ traceRelease(trace)
}
trigger, heapGoal := gcController.trigger()
throw("mspan.sweep: bad span state")
}
- if traceEnabled() {
- traceGCSweepSpan(s.npages * _PageSize)
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GCSweepSpan(s.npages * _PageSize)
+ traceRelease(trace)
}
mheap_.pagesSwept.Add(int64(s.npages))
return
}
- if traceEnabled() {
- traceGCSweepStart()
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GCSweepStart()
+ traceRelease(trace)
}
// Fix debt if necessary.
}
}
- if traceEnabled() {
- traceGCSweepDone()
+ trace = traceAcquire()
+ if trace.ok() {
+ trace.GCSweepDone()
+ traceRelease(trace)
}
}
// traceGCSweepStart/Done pair on the P.
mp := acquirem()
- if traceEnabled() {
- traceGCSweepStart()
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GCSweepStart()
+ traceRelease(trace)
}
arenas := h.sweepArenas
unlock(&h.lock)
}
- if traceEnabled() {
- traceGCSweepDone()
+ trace = traceAcquire()
+ if trace.ok() {
+ trace.GCSweepDone()
+ traceRelease(trace)
}
releasem(mp)
}
n -= uintptr(len(inUse) * 8)
}
sweep.active.end(sl)
- if traceEnabled() {
+ trace := traceAcquire()
+ if trace.ok() {
unlock(&h.lock)
// Account for pages scanned but not reclaimed.
- traceGCSweepSpan((n0 - nFreed) * pageSize)
+ trace.GCSweepSpan((n0 - nFreed) * pageSize)
+ traceRelease(trace)
lock(&h.lock)
}
// Mark gp ready to run.
func ready(gp *g, traceskip int, next bool) {
- if traceEnabled() {
- traceGoUnpark(gp, traceskip)
- }
-
status := readgstatus(gp)
// Mark runnable.
}
// status is Gwaiting or Gscanwaiting, make Grunnable and put on runq
+ trace := traceAcquire()
casgstatus(gp, _Gwaiting, _Grunnable)
+ if trace.ok() {
+ trace.GoUnpark(gp, traceskip)
+ traceRelease(trace)
+ }
runqput(mp.p.ptr(), gp, next)
wakep()
releasem(mp)
// Holding worldsema causes any other goroutines invoking
// stopTheWorld to block.
func stopTheWorldWithSema(reason stwReason) {
- if traceEnabled() {
- traceSTWStart(reason)
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.STWStart(reason)
+ traceRelease(trace)
}
gp := getg()
gp.m.p.ptr().status = _Pgcstop // Pgcstop is only diagnostic.
sched.stopwait--
// try to retake all P's in Psyscall status
+ trace = traceAcquire()
for _, pp := range allp {
s := pp.status
if s == _Psyscall && atomic.Cas(&pp.status, s, _Pgcstop) {
- if traceEnabled() {
- traceGoSysBlock(pp)
- traceProcStop(pp)
+ if trace.ok() {
+ trace.GoSysBlock(pp)
+ trace.ProcStop(pp)
}
pp.syscalltick++
sched.stopwait--
}
}
+ if trace.ok() {
+ traceRelease(trace)
+ }
+
// stop idle P's
now := nanotime()
for {
// Capture start-the-world time before doing clean-up tasks.
startTime := nanotime()
- if traceEnabled() {
- traceSTWDone()
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.STWDone()
+ traceRelease(trace)
}
// Wakeup an additional proc in case we have excessive runnable goroutines
// Force Ps currently in _Psyscall into _Pidle and hand them
// off to induce safe point function execution.
+ trace := traceAcquire()
for _, p2 := range allp {
s := p2.status
if s == _Psyscall && p2.runSafePointFn == 1 && atomic.Cas(&p2.status, s, _Pidle) {
- if traceEnabled() {
- traceGoSysBlock(p2)
- traceProcStop(p2)
+ if trace.ok() {
+ trace.GoSysBlock(p2)
+ trace.ProcStop(p2)
}
p2.syscalltick++
handoffp(p2)
}
}
+ if trace.ok() {
+ traceRelease(trace)
+ }
// Wait for remaining Ps to run fn.
if wait {
if raceenabled {
gp.racectx = racegostart(abi.FuncPCABIInternal(newextram) + sys.PCQuantum)
}
- if traceEnabled() {
- traceOneNewExtraM(gp)
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.OneNewExtraM(gp)
+ traceRelease(trace)
}
// put on allg for garbage collector
allgadd(gp)
setThreadCPUProfiler(hz)
}
- if traceEnabled() {
+ trace := traceAcquire()
+ if trace.ok() {
// GoSysExit has to happen when we have a P, but before GoStart.
// So we emit it here.
if gp.syscallsp != 0 {
- traceGoSysExit()
+ trace.GoSysExit()
}
- traceGoStart()
+ trace.GoStart()
+ traceRelease(trace)
}
gogo(&gp.sched)
if traceEnabled() || traceShuttingDown() {
gp := traceReader()
if gp != nil {
+ trace := traceAcquire()
casgstatus(gp, _Gwaiting, _Grunnable)
- traceGoUnpark(gp, 0)
+ if trace.ok() {
+ trace.GoUnpark(gp, 0)
+ traceRelease(trace)
+ }
return gp, false, true
}
}
gp := list.pop()
injectglist(&list)
netpollAdjustWaiters(delta)
+ trace := traceAcquire()
casgstatus(gp, _Gwaiting, _Grunnable)
- if traceEnabled() {
- traceGoUnpark(gp, 0)
+ if trace.ok() {
+ trace.GoUnpark(gp, 0)
+ traceRelease(trace)
}
return gp, false, false
}
if node != nil {
pp.gcMarkWorkerMode = gcMarkWorkerIdleMode
gp := node.gp.ptr()
+
+ trace := traceAcquire()
casgstatus(gp, _Gwaiting, _Grunnable)
- if traceEnabled() {
- traceGoUnpark(gp, 0)
+ if trace.ok() {
+ trace.GoUnpark(gp, 0)
+ traceRelease(trace)
}
return gp, false, false
}
// until a callback was triggered.
gp, otherReady := beforeIdle(now, pollUntil)
if gp != nil {
+ trace := traceAcquire()
casgstatus(gp, _Gwaiting, _Grunnable)
- if traceEnabled() {
- traceGoUnpark(gp, 0)
+ if trace.ok() {
+ trace.GoUnpark(gp, 0)
+ traceRelease(trace)
}
return gp, false, false
}
// Run the idle worker.
pp.gcMarkWorkerMode = gcMarkWorkerIdleMode
+ trace := traceAcquire()
casgstatus(gp, _Gwaiting, _Grunnable)
- if traceEnabled() {
- traceGoUnpark(gp, 0)
+ if trace.ok() {
+ trace.GoUnpark(gp, 0)
+ traceRelease(trace)
}
return gp, false, false
}
gp := list.pop()
injectglist(&list)
netpollAdjustWaiters(delta)
+ trace := traceAcquire()
casgstatus(gp, _Gwaiting, _Grunnable)
- if traceEnabled() {
- traceGoUnpark(gp, 0)
+ if trace.ok() {
+ trace.GoUnpark(gp, 0)
+ traceRelease(trace)
}
return gp, false, false
}
if glist.empty() {
return
}
- if traceEnabled() {
+ trace := traceAcquire()
+ if trace.ok() {
for gp := glist.head.ptr(); gp != nil; gp = gp.schedlink.ptr() {
- traceGoUnpark(gp, 0)
+ trace.GoUnpark(gp, 0)
}
+ traceRelease(trace)
}
// Mark all the goroutines as runnable before we put them
func park_m(gp *g) {
mp := getg().m
- if traceEnabled() {
- traceGoPark(mp.waitTraceBlockReason, mp.waitTraceSkip)
- }
+ trace := traceAcquire()
// N.B. Not using casGToWaiting here because the waitreason is
// set by park_m's caller.
casgstatus(gp, _Grunning, _Gwaiting)
+ if trace.ok() {
+ trace.GoPark(mp.waitTraceBlockReason, mp.waitTraceSkip)
+ traceRelease(trace)
+ }
+
dropg()
if fn := mp.waitunlockf; fn != nil {
mp.waitunlockf = nil
mp.waitlock = nil
if !ok {
- if traceEnabled() {
- traceGoUnpark(gp, 2)
- }
+ trace := traceAcquire()
casgstatus(gp, _Gwaiting, _Grunnable)
+ if trace.ok() {
+ trace.GoUnpark(gp, 2)
+ traceRelease(trace)
+ }
execute(gp, true) // Schedule it back, never returns.
}
}
schedule()
}
-func goschedImpl(gp *g) {
+func goschedImpl(gp *g, preempted bool) {
+ trace := traceAcquire()
status := readgstatus(gp)
if status&^_Gscan != _Grunning {
dumpgstatus(gp)
throw("bad g status")
}
casgstatus(gp, _Grunning, _Grunnable)
+ if trace.ok() {
+ if preempted {
+ trace.GoPreempt()
+ } else {
+ trace.GoSched()
+ }
+ traceRelease(trace)
+ }
+
dropg()
lock(&sched.lock)
globrunqput(gp)
// Gosched continuation on g0.
func gosched_m(gp *g) {
- if traceEnabled() {
- traceGoSched()
- }
- goschedImpl(gp)
+ goschedImpl(gp, false)
}
// goschedguarded is a forbidden-states-avoided version of gosched_m.
func goschedguarded_m(gp *g) {
-
if !canPreemptM(gp.m) {
gogo(&gp.sched) // never return
}
-
- if traceEnabled() {
- traceGoSched()
- }
- goschedImpl(gp)
+ goschedImpl(gp, false)
}
func gopreempt_m(gp *g) {
- if traceEnabled() {
- traceGoPreempt()
- }
- goschedImpl(gp)
+ goschedImpl(gp, true)
}
// preemptPark parks gp and puts it in _Gpreempted.
//
//go:systemstack
func preemptPark(gp *g) {
- if traceEnabled() {
- traceGoPark(traceBlockPreempted, 0)
- }
status := readgstatus(gp)
if status&^_Gscan != _Grunning {
dumpgstatus(gp)
// transitions until we can dropg.
casGToPreemptScan(gp, _Grunning, _Gscan|_Gpreempted)
dropg()
+
+ // Be careful about how we trace this next event. The ordering
+ // is subtle.
+ //
+ // The moment we CAS into _Gpreempted, suspendG could CAS to
+ // _Gwaiting, do its work, and ready the goroutine. All of
+ // this could happen before we even get the chance to emit
+ // an event. The end result is that the events could appear
+ // out of order, and the tracer generally assumes the scheduler
+ // takes care of the ordering between GoPark and GoUnpark.
+ //
+ // The answer here is simple: emit the event while we still hold
+ // the _Gscan bit on the goroutine. We still need to traceAcquire
+ // and traceRelease across the CAS because the tracer could be
+ // what's calling suspendG in the first place, and we want the
+ // CAS and event emission to appear atomic to the tracer.
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GoPark(traceBlockPreempted, 0)
+ }
casfrom_Gscanstatus(gp, _Gscan|_Gpreempted, _Gpreempted)
+ if trace.ok() {
+ traceRelease(trace)
+ }
schedule()
}
}
func goyield_m(gp *g) {
- if traceEnabled() {
- traceGoPreempt()
- }
+ trace := traceAcquire()
pp := gp.m.p.ptr()
casgstatus(gp, _Grunning, _Grunnable)
+ if trace.ok() {
+ trace.GoPreempt()
+ traceRelease(trace)
+ }
dropg()
runqput(pp, gp, false)
schedule()
if raceenabled {
racegoend()
}
- if traceEnabled() {
- traceGoEnd()
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GoEnd()
+ traceRelease(trace)
}
mcall(goexit0)
}
//
//go:nosplit
func reentersyscall(pc, sp uintptr) {
+ trace := traceAcquire()
gp := getg()
// Disable preemption because during this function g is in Gsyscall status,
})
}
- if traceEnabled() {
- systemstack(traceGoSysCall)
+ if trace.ok() {
+ systemstack(func() {
+ trace.GoSysCall()
+ traceRelease(trace)
+ })
// systemstack itself clobbers g.sched.{pc,sp} and we might
// need them later when the G is genuinely blocked in a
// syscall
lock(&sched.lock)
if sched.stopwait > 0 && atomic.Cas(&pp.status, _Psyscall, _Pgcstop) {
- if traceEnabled() {
- traceGoSysBlock(pp)
- traceProcStop(pp)
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GoSysBlock(pp)
+ trace.ProcStop(pp)
+ traceRelease(trace)
}
pp.syscalltick++
if sched.stopwait--; sched.stopwait == 0 {
}
func entersyscallblock_handoff() {
- if traceEnabled() {
- traceGoSysCall()
- traceGoSysBlock(getg().m.p.ptr())
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GoSysCall()
+ trace.GoSysBlock(getg().m.p.ptr())
+ traceRelease(trace)
}
handoffp(releasep())
}
tryRecordGoroutineProfileWB(gp)
})
}
- if traceEnabled() {
+ trace := traceAcquire()
+ if trace.ok() {
if oldp != gp.m.p.ptr() || gp.m.syscalltick != gp.m.p.ptr().syscalltick {
- systemstack(traceGoStart)
+ systemstack(func() {
+ trace.GoStart()
+ })
}
}
// There's a cpu for us, so we can run.
gp.m.p.ptr().syscalltick++
// We need to cas the status and scan before resuming...
casgstatus(gp, _Gsyscall, _Grunning)
+ if trace.ok() {
+ traceRelease(trace)
+ }
// Garbage collector isn't running (since we are),
// so okay to clear syscallsp.
return
}
- if traceEnabled() {
+ trace := traceAcquire()
+ if trace.ok() {
// Wait till traceGoSysBlock event is emitted.
// This ensures consistency of the trace (the goroutine is started after it is blocked).
for oldp != nil && oldp.syscalltick == gp.m.syscalltick {
// So instead we remember the syscall exit time and emit the event
// in execute when we have a P.
gp.trace.sysExitTime = traceClockNow()
+ traceRelease(trace)
}
gp.m.locks--
var ok bool
systemstack(func() {
ok = exitsyscallfast_pidle()
- if ok && traceEnabled() {
- if oldp != nil {
- // Wait till traceGoSysBlock event is emitted.
- // This ensures consistency of the trace (the goroutine is started after it is blocked).
- for oldp.syscalltick == gp.m.syscalltick {
- osyield()
+ if ok {
+ trace := traceAcquire()
+ if trace.ok() {
+ if oldp != nil {
+ // Wait till traceGoSysBlock event is emitted.
+ // This ensures consistency of the trace (the goroutine is started after it is blocked).
+ for oldp.syscalltick == gp.m.syscalltick {
+ osyield()
+ }
}
+ trace.GoSysExit()
+ traceRelease(trace)
}
- traceGoSysExit()
}
})
if ok {
func exitsyscallfast_reacquired() {
gp := getg()
if gp.m.syscalltick != gp.m.p.ptr().syscalltick {
- if traceEnabled() {
+ trace := traceAcquire()
+ 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,
// but here we effectively retake the p from the new syscall running on the same p.
systemstack(func() {
// Denote blocking of the new syscall.
- traceGoSysBlock(gp.m.p.ptr())
+ trace.GoSysBlock(gp.m.p.ptr())
// Denote completion of the current syscall.
- traceGoSysExit()
+ trace.GoSysExit()
+ traceRelease(trace)
})
}
gp.m.p.ptr().syscalltick++
if newg.trackingSeq%gTrackingPeriod == 0 {
newg.tracking = true
}
- casgstatus(newg, _Gdead, _Grunnable)
gcController.addScannableStack(pp, int64(newg.stack.hi-newg.stack.lo))
+ // Get a goid and switch to runnable. Make all this atomic to the tracer.
+ trace := traceAcquire()
+ casgstatus(newg, _Gdead, _Grunnable)
if pp.goidcache == pp.goidcacheend {
// Sched.goidgen is the last allocated id,
// this batch must be [sched.goidgen+1, sched.goidgen+GoidCacheBatch].
}
newg.goid = pp.goidcache
pp.goidcache++
+ if trace.ok() {
+ trace.GoCreate(newg, newg.startpc)
+ traceRelease(trace)
+ }
+
+ // Set up race context.
if raceenabled {
newg.racectx = racegostart(callerpc)
newg.raceignore = 0
racereleasemergeg(newg, unsafe.Pointer(&labelSync))
}
}
- if traceEnabled() {
- traceGoCreate(newg, newg.startpc)
- }
releasem(mp)
return newg
if old < 0 || nprocs <= 0 {
throw("procresize: invalid arg")
}
- if traceEnabled() {
- traceGomaxprocs(nprocs)
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.Gomaxprocs(nprocs)
+ traceRelease(trace)
}
// update statistics
// because p.destroy itself has write barriers, so we
// need to do that from a valid P.
if gp.m.p != 0 {
- if traceEnabled() {
+ trace := traceAcquire()
+ if trace.ok() {
// Pretend that we were descheduled
// and then scheduled again to keep
// the trace sane.
- traceGoSched()
- traceProcStop(gp.m.p.ptr())
+ trace.GoSched()
+ trace.ProcStop(gp.m.p.ptr())
+ traceRelease(trace)
}
gp.m.p.ptr().m = 0
}
pp.m = 0
pp.status = _Pidle
acquirep(pp)
- if traceEnabled() {
- traceGoStart()
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GoStart()
+ traceRelease(trace)
}
}
// from a potentially stale mcache.
pp.mcache.prepareForSweep()
- if traceEnabled() {
- traceProcStart()
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.ProcStart()
+ traceRelease(trace)
}
}
print("releasep: m=", gp.m, " m->p=", gp.m.p.ptr(), " p->m=", hex(pp.m), " p->status=", pp.status, "\n")
throw("releasep: invalid p state")
}
- if traceEnabled() {
- traceProcStop(gp.m.p.ptr())
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.ProcStop(gp.m.p.ptr())
+ traceRelease(trace)
}
gp.m.p = 0
pp.m = 0
// increment nmidle and report deadlock.
incidlelocked(-1)
if atomic.Cas(&pp.status, s, _Pidle) {
- if traceEnabled() {
- traceGoSysBlock(pp)
- traceProcStop(pp)
+ trace := traceAcquire()
+ if trace.ok() {
+ trace.GoSysBlock(pp)
+ trace.ProcStop(pp)
+ traceRelease(trace)
}
n++
pp.syscalltick++
// traceEnabled returns true if the trace is currently enabled.
//
+// nosplit because it's called on the syscall path when stack movement is forbidden.
+//
//go:nosplit
func traceEnabled() bool {
return trace.enabled
return trace.shutdown
}
+// 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.
+//
+// This doesn't do much for the current tracer, because the current tracer doesn't
+// need atomicity around non-trace runtime operations. All the state it needs it
+// collects carefully during a STW.
+type traceLocker struct {
+ enabled bool
+}
+
+// traceAcquire prepares this M for writing one or more trace events.
+//
+// This exists for compatibility with the upcoming new tracer; it doesn't do much
+// in the current tracer.
+//
+// nosplit because it's called on the syscall path when stack movement is forbidden.
+//
+//go:nosplit
+func traceAcquire() traceLocker {
+ if !traceEnabled() {
+ return traceLocker{false}
+ }
+ return traceLocker{true}
+}
+
+// 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.enabled
+}
+
+// traceRelease indicates that this M is done writing trace events.
+//
+// This exists for compatibility with the upcoming new tracer; it doesn't do anything
+// in the current tracer.
+//
+// nosplit because it's called on the syscall path when stack movement is forbidden.
+//
+//go:nosplit
+func traceRelease(tl traceLocker) {
+}
+
// 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.
gp.trace.tracedSyscallEnter = false
}
})
- traceProcStart()
- traceGoStart()
+ // Use a dummy traceLocker. The trace isn't enabled yet, but we can still write events.
+ tl := traceLocker{}
+ tl.ProcStart()
+ tl.GoStart()
// 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.
unlock(&sched.sysmonlock)
// Record the current state of HeapGoal to avoid information loss in trace.
- traceHeapGoal()
+ //
+ // Use the same dummy trace locker. The trace can't end until after we start
+ // the world, and we can safely trace from here.
+ tl.HeapGoal()
startTheWorldGC()
return nil
return
}
- traceGoSched()
+ // Trace GoSched for us, and use a dummy locker. The world is stopped
+ // and we control whether the trace is enabled, so this is safe.
+ tl := traceLocker{}
+ tl.GoSched()
atomicstorep(unsafe.Pointer(&trace.cpuLogWrite), nil)
trace.cpuLogRead.close()
// profiling buffer. It is called from a signal handler, so is limited in what
// it can do.
func traceCPUSample(gp *g, pp *p, stk []uintptr) {
- if !trace.enabled {
+ if !traceEnabled() {
// Tracing is usually turned off; don't spend time acquiring the signal
// lock unless it's active.
return
// The following functions write specific events to trace.
-func traceGomaxprocs(procs int32) {
+func (_ traceLocker) Gomaxprocs(procs int32) {
traceEvent(traceEvGomaxprocs, 1, uint64(procs))
}
-func traceProcStart() {
+func (_ traceLocker) ProcStart() {
traceEvent(traceEvProcStart, -1, uint64(getg().m.id))
}
-func traceProcStop(pp *p) {
+func (_ traceLocker) ProcStop(pp *p) {
// Sysmon and stopTheWorld can stop Ps blocked in syscalls,
// to handle this we temporary employ the P.
mp := acquirem()
releasem(mp)
}
-func traceGCStart() {
+func (_ traceLocker) GCStart() {
traceEvent(traceEvGCStart, 3, trace.seqGC)
trace.seqGC++
}
-func traceGCDone() {
+func (_ traceLocker) GCDone() {
traceEvent(traceEvGCDone, -1)
}
-func traceSTWStart(reason stwReason) {
+func (_ traceLocker) STWStart(reason stwReason) {
// Don't trace if this STW is for trace start/stop, since traceEnabled
// switches during a STW.
if reason == stwStartTrace || reason == stwStopTrace {
traceEvent(traceEvSTWStart, -1, uint64(reason))
}
-func traceSTWDone() {
+func (_ traceLocker) STWDone() {
mp := getg().m
if !mp.trace.tracedSTWStart {
return
//
// traceGCSweepStart must be paired with traceGCSweepDone and there
// must be no preemption points between these two calls.
-func traceGCSweepStart() {
+func (_ traceLocker) GCSweepStart() {
// Delay the actual GCSweepStart event until the first span
// sweep. If we don't sweep anything, don't emit any events.
pp := getg().m.p.ptr()
//
// This may be called outside a traceGCSweepStart/traceGCSweepDone
// pair; however, it will not emit any trace events in this case.
-func traceGCSweepSpan(bytesSwept uintptr) {
+func (_ traceLocker) GCSweepSpan(bytesSwept uintptr) {
pp := getg().m.p.ptr()
if pp.trace.inSweep {
if pp.trace.swept == 0 {
}
}
-func traceGCSweepDone() {
+func (_ traceLocker) GCSweepDone() {
pp := getg().m.p.ptr()
if !pp.trace.inSweep {
throw("missing traceGCSweepStart")
pp.trace.inSweep = false
}
-func traceGCMarkAssistStart() {
+func (_ traceLocker) GCMarkAssistStart() {
traceEvent(traceEvGCMarkAssistStart, 1)
}
-func traceGCMarkAssistDone() {
+func (_ traceLocker) GCMarkAssistDone() {
traceEvent(traceEvGCMarkAssistDone, -1)
}
-func traceGoCreate(newg *g, pc uintptr) {
+func (_ traceLocker) GoCreate(newg *g, pc uintptr) {
newg.trace.seq = 0
newg.trace.lastP = getg().m.p
// +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum.
traceEvent(traceEvGoCreate, 2, newg.goid, uint64(id))
}
-func traceGoStart() {
+func (_ traceLocker) GoStart() {
gp := getg().m.curg
pp := gp.m.p
gp.trace.seq++
}
}
-func traceGoEnd() {
+func (_ traceLocker) GoEnd() {
traceEvent(traceEvGoEnd, -1)
}
-func traceGoSched() {
+func (_ traceLocker) GoSched() {
gp := getg()
gp.trace.lastP = gp.m.p
traceEvent(traceEvGoSched, 1)
}
-func traceGoPreempt() {
+func (_ traceLocker) GoPreempt() {
gp := getg()
gp.trace.lastP = gp.m.p
traceEvent(traceEvGoPreempt, 1)
}
-func traceGoPark(reason traceBlockReason, skip int) {
+func (_ traceLocker) GoPark(reason traceBlockReason, skip int) {
// Convert the block reason directly to a trace event type.
// See traceBlockReason for more information.
traceEvent(byte(reason), skip)
}
-func traceGoUnpark(gp *g, skip int) {
+func (_ traceLocker) GoUnpark(gp *g, skip int) {
pp := getg().m.p
gp.trace.seq++
if gp.trace.lastP == pp {
}
}
-func traceGoSysCall() {
+func (_ traceLocker) GoSysCall() {
var skip int
switch {
case tracefpunwindoff():
traceEvent(traceEvGoSysCall, skip)
}
-func traceGoSysExit() {
+func (_ traceLocker) GoSysExit() {
gp := getg().m.curg
if !gp.trace.tracedSyscallEnter {
// There was no syscall entry traced for us at all, so there's definitely
traceEvent(traceEvGoSysExit, -1, gp.goid, gp.trace.seq, uint64(ts))
}
-func traceGoSysBlock(pp *p) {
+func (_ traceLocker) GoSysBlock(pp *p) {
// Sysmon and stopTheWorld can declare syscalls running on remote Ps as blocked,
// to handle this we temporary employ the P.
mp := acquirem()
releasem(mp)
}
-func traceHeapAlloc(live uint64) {
+func (_ traceLocker) HeapAlloc(live uint64) {
traceEvent(traceEvHeapAlloc, -1, live)
}
-func traceHeapGoal() {
+func (_ traceLocker) HeapGoal() {
heapGoal := gcController.heapGoal()
if heapGoal == ^uint64(0) {
// Heap-based triggering is disabled.
return f.datap.textAddr(*(*uint32)(w))
}
-// traceOneNewExtraM registers the fact that a new extra M was created with
+// OneNewExtraM 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 traceOneNewExtraM(gp *g) {
+func (tl traceLocker) OneNewExtraM(gp *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(gp, 0) // no start pc
+ tl.GoCreate(gp, 0) // no start pc
gp.trace.seq++
traceEvent(traceEvGoInSyscall, -1, gp.goid)
}