From c7227bc72c5ceaf9f957473cdde4753edc6155cd Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 23 Jul 2024 23:09:07 -0400 Subject: [PATCH] runtime: make TestDebugLogInterleaving much more robust The current test often doesn't actually generate enough interleaving to result in multiple log shards. This CL rewrites this test to forcibly create at least 10 log shards with interleaved log messages. It also tests dlog's robustness to being held across M and P switches. Change-Id: Ia913b17c0392384ff679832047f359945669bb15 Reviewed-on: https://go-review.googlesource.com/c/go/+/600699 LUCI-TryBot-Result: Go LUCI Reviewed-by: Carlos Amedee Auto-Submit: Austin Clements --- src/runtime/debuglog_test.go | 70 +++++++++++++++++++++-------- src/runtime/export_debuglog_test.go | 10 +++++ 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/src/runtime/debuglog_test.go b/src/runtime/debuglog_test.go index 0f91398cdd..d958c03740 100644 --- a/src/runtime/debuglog_test.go +++ b/src/runtime/debuglog_test.go @@ -28,7 +28,6 @@ import ( "runtime" "strings" "sync" - "sync/atomic" "testing" ) @@ -82,28 +81,63 @@ func TestDebugLogSym(t *testing.T) { func TestDebugLogInterleaving(t *testing.T) { skipDebugLog(t) runtime.ResetDebugLog() + + n1 := runtime.CountDebugLog() + t.Logf("number of log shards at start: %d", n1) + + const limit = 1000 + const concurrency = 10 + + // Start several goroutines writing to the log simultaneously. var wg sync.WaitGroup - done := int32(0) - wg.Add(1) - go func() { - // Encourage main goroutine to move around to - // different Ms and Ps. - for atomic.LoadInt32(&done) == 0 { - runtime.Gosched() - } - wg.Done() - }() - var want strings.Builder - for i := 0; i < 1000; i++ { - runtime.Dlog().I(i).End() - fmt.Fprintf(&want, "[] %d\n", i) - runtime.Gosched() + i := 0 + chans := make([]chan bool, concurrency) + for gid := range concurrency { + chans[gid] = make(chan bool) + wg.Add(1) + go func() { + defer wg.Done() + var log *runtime.Dlogger + for { + <-chans[gid] + if log != nil { + log.End() + } + next := chans[(gid+1)%len(chans)] + if i >= limit { + close(next) + break + } + // Log an entry, but *don't* release the log shard until its our + // turn again. This should result in at least n=concurrency log + // shards. + log = runtime.Dlog().I(i) + i++ + // Wake up the next logger goroutine. + next <- true + } + }() } - atomic.StoreInt32(&done, 1) - wg.Wait() + // Start the chain reaction. + chans[0] <- true + // Wait for them to finish and get the log. + wg.Wait() gotFull := runtime.DumpDebugLog() got := dlogCanonicalize(gotFull) + + n2 := runtime.CountDebugLog() + t.Logf("number of log shards at end: %d", n2) + if n2 < concurrency { + t.Errorf("created %d log shards, expected >= %d", n2, concurrency) + } + + // Construct the desired output. + var want strings.Builder + for i := 0; i < limit; i++ { + fmt.Fprintf(&want, "[] %d\n", i) + } + if got != want.String() { // Since the timestamps are useful in understand // failures of this test, we print the uncanonicalized diff --git a/src/runtime/export_debuglog_test.go b/src/runtime/export_debuglog_test.go index fc55f73c1f..c370a79336 100644 --- a/src/runtime/export_debuglog_test.go +++ b/src/runtime/export_debuglog_test.go @@ -48,3 +48,13 @@ func ResetDebugLog() { } startTheWorld(stw) } + +func CountDebugLog() int { + stw := stopTheWorld(stwForTestResetDebugLog) + i := 0 + for l := allDloggers; l != nil; l = l.allLink { + i++ + } + startTheWorld(stw) + return i +} -- 2.48.1