Skip to content

Commit f119abb

Browse files
mknyszekgopherbot
authored andcommitted
runtime: refactor runtime->tracer API to appear more like a lock
Currently the execution tracer synchronizes with itself using very heavyweight operations. As a result, it's totally fine for most of the tracer code to look like: if traceEnabled() { traceXXX(...) } However, if we want to make that synchronization more lightweight (as issue #60773 proposes), then this is insufficient. In particular, we need to make sure the tracer can't observe an inconsistency between g atomicstatus and the event that would be emitted for a particular g transition. This means making the g status change appear to happen atomically with the corresponding trace event being written out from the perspective of the tracer. This requires a change in API to something more like a lock. While we're here, we might as well make sure that trace events can *only* be emitted while this lock is held. This change introduces such an API: traceAcquire, which returns a value that can emit events, and traceRelease, which requires the value that was returned by traceAcquire. In practice, this won't be a real lock, it'll be more like a seqlock. For the current tracer, this API is completely overkill and the value returned by traceAcquire basically just checks trace.enabled. But it's necessary for the tracer described in #60773 and we can implement that more cleanly if we do this refactoring now instead of later. For #60773. Change-Id: Ibb9ff5958376339fafc2b5180aef65cf2ba18646 Reviewed-on: https://go-review.googlesource.com/c/go/+/515635 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Michael Knyszek <mknyszek@google.com> Reviewed-by: Michael Pratt <mpratt@google.com>
1 parent e3585c6 commit f119abb

File tree

9 files changed

+388
-177
lines changed

9 files changed

+388
-177
lines changed

src/runtime/debugcall.go

+15-9
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,12 @@ func debugCallWrap(dispatch uintptr) {
166166
gp.schedlink = 0
167167

168168
// Park the calling goroutine.
169-
if traceEnabled() {
170-
traceGoPark(traceBlockDebugCall, 1)
171-
}
169+
trace := traceAcquire()
172170
casGToWaiting(gp, _Grunning, waitReasonDebugCall)
171+
if trace.ok() {
172+
trace.GoPark(traceBlockDebugCall, 1)
173+
traceRelease(trace)
174+
}
173175
dropg()
174176

175177
// Directly execute the new goroutine. The debug
@@ -225,19 +227,23 @@ func debugCallWrap1() {
225227
// Switch back to the calling goroutine. At some point
226228
// the scheduler will schedule us again and we'll
227229
// finish exiting.
228-
if traceEnabled() {
229-
traceGoSched()
230-
}
230+
trace := traceAcquire()
231231
casgstatus(gp, _Grunning, _Grunnable)
232+
if trace.ok() {
233+
trace.GoSched()
234+
traceRelease(trace)
235+
}
232236
dropg()
233237
lock(&sched.lock)
234238
globrunqput(gp)
235239
unlock(&sched.lock)
236240

237-
if traceEnabled() {
238-
traceGoUnpark(callingG, 0)
239-
}
241+
trace = traceAcquire()
240242
casgstatus(callingG, _Gwaiting, _Grunnable)
243+
if trace.ok() {
244+
trace.GoUnpark(callingG, 0)
245+
traceRelease(trace)
246+
}
241247
execute(callingG, true)
242248
})
243249
}

src/runtime/mcentral.go

+14-6
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,10 @@ func (c *mcentral) cacheSpan() *mspan {
8484
deductSweepCredit(spanBytes, 0)
8585

8686
traceDone := false
87-
if traceEnabled() {
88-
traceGCSweepStart()
87+
trace := traceAcquire()
88+
if trace.ok() {
89+
trace.GCSweepStart()
90+
traceRelease(trace)
8991
}
9092

9193
// If we sweep spanBudget spans without finding any free
@@ -157,9 +159,11 @@ func (c *mcentral) cacheSpan() *mspan {
157159
}
158160
sweep.active.end(sl)
159161
}
160-
if traceEnabled() {
161-
traceGCSweepDone()
162+
trace = traceAcquire()
163+
if trace.ok() {
164+
trace.GCSweepDone()
162165
traceDone = true
166+
traceRelease(trace)
163167
}
164168

165169
// We failed to get a span from the mcentral so get one from mheap.
@@ -170,8 +174,12 @@ func (c *mcentral) cacheSpan() *mspan {
170174

171175
// At this point s is a span that should have free slots.
172176
havespan:
173-
if traceEnabled() && !traceDone {
174-
traceGCSweepDone()
177+
if !traceDone {
178+
trace := traceAcquire()
179+
if trace.ok() {
180+
trace.GCSweepDone()
181+
traceRelease(trace)
182+
}
175183
}
176184
n := int(s.nelems) - int(s.allocCount)
177185
if n == 0 || s.freeindex == s.nelems || s.allocCount == s.nelems {

src/runtime/mgc.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -647,8 +647,10 @@ func gcStart(trigger gcTrigger) {
647647
// Update it under gcsema to avoid gctrace getting wrong values.
648648
work.userForced = trigger.kind == gcTriggerCycle
649649

650-
if traceEnabled() {
651-
traceGCStart()
650+
trace := traceAcquire()
651+
if trace.ok() {
652+
trace.GCStart()
653+
traceRelease(trace)
652654
}
653655

654656
// Check that all Ps have finished deferred mcache flushes.
@@ -989,8 +991,10 @@ func gcMarkTermination() {
989991
mp.traceback = 0
990992
casgstatus(curgp, _Gwaiting, _Grunning)
991993

992-
if traceEnabled() {
993-
traceGCDone()
994+
trace := traceAcquire()
995+
if trace.ok() {
996+
trace.GCDone()
997+
traceRelease(trace)
994998
}
995999

9961000
// all done

src/runtime/mgcmark.go

+21-6
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,11 @@ retry:
420420
// If the CPU limiter is enabled, intentionally don't
421421
// assist to reduce the amount of CPU time spent in the GC.
422422
if traced {
423-
traceGCMarkAssistDone()
423+
trace := traceAcquire()
424+
if trace.ok() {
425+
trace.GCMarkAssistDone()
426+
traceRelease(trace)
427+
}
424428
}
425429
return
426430
}
@@ -461,15 +465,22 @@ retry:
461465
// We were able to steal all of the credit we
462466
// needed.
463467
if traced {
464-
traceGCMarkAssistDone()
468+
trace := traceAcquire()
469+
if trace.ok() {
470+
trace.GCMarkAssistDone()
471+
traceRelease(trace)
472+
}
465473
}
466474
return
467475
}
468476
}
469-
470477
if traceEnabled() && !traced {
471-
traced = true
472-
traceGCMarkAssistStart()
478+
trace := traceAcquire()
479+
if trace.ok() {
480+
traced = true
481+
trace.GCMarkAssistStart()
482+
traceRelease(trace)
483+
}
473484
}
474485

475486
// Perform assist work
@@ -515,7 +526,11 @@ retry:
515526
// this G's assist debt, or the GC cycle is over.
516527
}
517528
if traced {
518-
traceGCMarkAssistDone()
529+
trace := traceAcquire()
530+
if trace.ok() {
531+
trace.GCMarkAssistDone()
532+
traceRelease(trace)
533+
}
519534
}
520535
}
521536

src/runtime/mgcpacer.go

+16-8
Original file line numberDiff line numberDiff line change
@@ -807,9 +807,11 @@ func (c *gcControllerState) findRunnableGCWorker(pp *p, now int64) (*g, int64) {
807807

808808
// Run the background mark worker.
809809
gp := node.gp.ptr()
810+
trace := traceAcquire()
810811
casgstatus(gp, _Gwaiting, _Grunnable)
811-
if traceEnabled() {
812-
traceGoUnpark(gp, 0)
812+
if trace.ok() {
813+
trace.GoUnpark(gp, 0)
814+
traceRelease(trace)
813815
}
814816
return gp, now
815817
}
@@ -828,8 +830,10 @@ func (c *gcControllerState) resetLive(bytesMarked uint64) {
828830
c.triggered = ^uint64(0) // Reset triggered.
829831

830832
// heapLive was updated, so emit a trace event.
831-
if traceEnabled() {
832-
traceHeapAlloc(bytesMarked)
833+
trace := traceAcquire()
834+
if trace.ok() {
835+
trace.HeapAlloc(bytesMarked)
836+
traceRelease(trace)
833837
}
834838
}
835839

@@ -856,10 +860,12 @@ func (c *gcControllerState) markWorkerStop(mode gcMarkWorkerMode, duration int64
856860

857861
func (c *gcControllerState) update(dHeapLive, dHeapScan int64) {
858862
if dHeapLive != 0 {
863+
trace := traceAcquire()
859864
live := gcController.heapLive.Add(dHeapLive)
860-
if traceEnabled() {
865+
if trace.ok() {
861866
// gcController.heapLive changed.
862-
traceHeapAlloc(live)
867+
trace.HeapAlloc(live)
868+
traceRelease(trace)
863869
}
864870
}
865871
if gcBlackenEnabled == 0 {
@@ -1428,8 +1434,10 @@ func gcControllerCommit() {
14281434

14291435
// TODO(mknyszek): This isn't really accurate any longer because the heap
14301436
// goal is computed dynamically. Still useful to snapshot, but not as useful.
1431-
if traceEnabled() {
1432-
traceHeapGoal()
1437+
trace := traceAcquire()
1438+
if trace.ok() {
1439+
trace.HeapGoal()
1440+
traceRelease(trace)
14331441
}
14341442

14351443
trigger, heapGoal := gcController.trigger()

src/runtime/mgcsweep.go

+12-6
Original file line numberDiff line numberDiff line change
@@ -516,8 +516,10 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
516516
throw("mspan.sweep: bad span state")
517517
}
518518

519-
if traceEnabled() {
520-
traceGCSweepSpan(s.npages * _PageSize)
519+
trace := traceAcquire()
520+
if trace.ok() {
521+
trace.GCSweepSpan(s.npages * _PageSize)
522+
traceRelease(trace)
521523
}
522524

523525
mheap_.pagesSwept.Add(int64(s.npages))
@@ -889,8 +891,10 @@ func deductSweepCredit(spanBytes uintptr, callerSweepPages uintptr) {
889891
return
890892
}
891893

892-
if traceEnabled() {
893-
traceGCSweepStart()
894+
trace := traceAcquire()
895+
if trace.ok() {
896+
trace.GCSweepStart()
897+
traceRelease(trace)
894898
}
895899

896900
// Fix debt if necessary.
@@ -929,8 +933,10 @@ retry:
929933
}
930934
}
931935

932-
if traceEnabled() {
933-
traceGCSweepDone()
936+
trace = traceAcquire()
937+
if trace.ok() {
938+
trace.GCSweepDone()
939+
traceRelease(trace)
934940
}
935941
}
936942

src/runtime/mheap.go

+12-6
Original file line numberDiff line numberDiff line change
@@ -791,8 +791,10 @@ func (h *mheap) reclaim(npage uintptr) {
791791
// traceGCSweepStart/Done pair on the P.
792792
mp := acquirem()
793793

794-
if traceEnabled() {
795-
traceGCSweepStart()
794+
trace := traceAcquire()
795+
if trace.ok() {
796+
trace.GCSweepStart()
797+
traceRelease(trace)
796798
}
797799

798800
arenas := h.sweepArenas
@@ -839,8 +841,10 @@ func (h *mheap) reclaim(npage uintptr) {
839841
unlock(&h.lock)
840842
}
841843

842-
if traceEnabled() {
843-
traceGCSweepDone()
844+
trace = traceAcquire()
845+
if trace.ok() {
846+
trace.GCSweepDone()
847+
traceRelease(trace)
844848
}
845849
releasem(mp)
846850
}
@@ -911,10 +915,12 @@ func (h *mheap) reclaimChunk(arenas []arenaIdx, pageIdx, n uintptr) uintptr {
911915
n -= uintptr(len(inUse) * 8)
912916
}
913917
sweep.active.end(sl)
914-
if traceEnabled() {
918+
trace := traceAcquire()
919+
if trace.ok() {
915920
unlock(&h.lock)
916921
// Account for pages scanned but not reclaimed.
917-
traceGCSweepSpan((n0 - nFreed) * pageSize)
922+
trace.GCSweepSpan((n0 - nFreed) * pageSize)
923+
traceRelease(trace)
918924
lock(&h.lock)
919925
}
920926

0 commit comments

Comments
 (0)