From 38723f2b3881ea71039c1d475cab001113adc83d Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sat, 9 Mar 2024 13:41:24 -0500 Subject: [PATCH] runtime: add minimal debug tracing of runtime timers Toggled by a compile-time const, so there should be no runtime footprint in ordinary builds. Change-Id: I7751847524f4fda3853388d3e5a18188bd737c27 Reviewed-on: https://go-review.googlesource.com/c/go/+/570336 Auto-Submit: Russ Cox Reviewed-by: Ian Lance Taylor LUCI-TryBot-Result: Go LUCI Reviewed-by: Austin Clements --- src/runtime/time.go | 49 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/runtime/time.go b/src/runtime/time.go index 6802cc4b29..adbe8ac126 100644 --- a/src/runtime/time.go +++ b/src/runtime/time.go @@ -133,13 +133,43 @@ const ( timerZombie ) +// timerDebug enables printing a textual debug trace of all timer operations to stderr. +const timerDebug = false + +func (t *timer) trace(op string) { + if timerDebug { + t.trace1(op) + } +} + +func (t *timer) trace1(op string) { + if !timerDebug { + return + } + bits := [3]string{"h", "m", "z"} + for i := range bits { + if t.state&(1< 0 + need := t.state&timerHeaped == 0 && t.when > 0 + if need { + t.trace("needsAdd+") + } else { + t.trace("needsAdd-") + } + return need } // maybeAdd adds t to the local timers heap if it needs to be in a heap. @@ -434,6 +474,7 @@ func (t *timer) maybeAdd() { ts.lock() ts.cleanHead() t.lock() + t.trace("maybeAdd") when := int64(0) if t.needsAdd() { t.state |= timerHeaped @@ -459,6 +500,7 @@ func (t *timer) reset(when, period int64) bool { // slows down heap operations. // The caller must have locked ts. func (ts *timers) cleanHead() { + ts.trace("cleanHead") assertLockHeld(&ts.mu) gp := getg() for { @@ -500,6 +542,7 @@ func (ts *timers) cleanHead() { // The caller must not have locked either timers. // For now this is only called when the world is stopped. func (ts *timers) take(src *timers) { + ts.trace("take") assertWorldStopped() if len(src.heap) > 0 { // The world is stopped, so we ignore the locking of ts and src here. @@ -535,6 +578,7 @@ func (ts *timers) move(timers []*timer) { // it also moves timers that have been modified to run later, // and removes deleted timers. The caller must have locked ts. func (ts *timers) adjust(now int64, force bool) { + ts.trace("adjust") assertLockHeld(&ts.mu) // If we haven't yet reached the time of the earliest modified // timer, don't do anything. This speeds up programs that adjust @@ -664,6 +708,7 @@ func (ts *timers) wakeTime() int64 { // //go:yeswritebarrierrec func (ts *timers) check(now int64) (rnow, pollUntil int64, ran bool) { + ts.trace("check") // If it's not yet time for the first timer, or the first adjusted // timer, then there is nothing to do. next := ts.wakeTime() @@ -718,6 +763,7 @@ func (ts *timers) check(now int64) (rnow, pollUntil int64, ran bool) { // //go:systemstack func (ts *timers) run(now int64) int64 { + ts.trace("run") assertLockHeld(&ts.mu) Redo: if len(ts.heap) == 0 { @@ -763,6 +809,7 @@ Redo: // //go:systemstack func (t *timer) unlockAndRun(now int64) { + t.trace("unlockAndRun") assertLockHeld(&t.mu) if t.ts != nil { assertLockHeld(&t.ts.mu) -- 2.48.1