github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/debuglog_test.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // TODO(austin): All of these tests are skipped if the debuglog build 6 // tag isn't provided. That means we basically never test debuglog. 7 // There are two potential ways around this: 8 // 9 // 1. Make these tests re-build the runtime test with the debuglog 10 // build tag and re-invoke themselves. 11 // 12 // 2. Always build the whole debuglog infrastructure and depend on 13 // linker dead-code elimination to drop it. This is easy for dlog() 14 // since there won't be any calls to it. For printDebugLog, we can 15 // make panic call a wrapper that is call printDebugLog if the 16 // debuglog build tag is set, or otherwise do nothing. Then tests 17 // could call printDebugLog directly. This is the right answer in 18 // principle, but currently our linker reads in all symbols 19 // regardless, so this would slow down and bloat all links. If the 20 // linker gets more efficient about this, we should revisit this 21 // approach. 22 23 package runtime_test 24 25 import ( 26 "fmt" 27 "internal/testenv" 28 "regexp" 29 "runtime" 30 "strings" 31 "sync" 32 "sync/atomic" 33 "testing" 34 ) 35 36 func skipDebugLog(t *testing.T) { 37 if !runtime.DlogEnabled { 38 t.Skip("debug log disabled (rebuild with -tags debuglog)") 39 } 40 } 41 42 func dlogCanonicalize(x string) string { 43 begin := regexp.MustCompile(`(?m)^>> begin log \d+ <<\n`) 44 x = begin.ReplaceAllString(x, "") 45 prefix := regexp.MustCompile(`(?m)^\[[^]]+\]`) 46 x = prefix.ReplaceAllString(x, "[]") 47 return x 48 } 49 50 func TestDebugLog(t *testing.T) { 51 skipDebugLog(t) 52 runtime.ResetDebugLog() 53 runtime.Dlog().S("testing").End() 54 got := dlogCanonicalize(runtime.DumpDebugLog()) 55 if want := "[] testing\n"; got != want { 56 t.Fatalf("want %q, got %q", want, got) 57 } 58 } 59 60 func TestDebugLogTypes(t *testing.T) { 61 skipDebugLog(t) 62 runtime.ResetDebugLog() 63 var varString = strings.Repeat("a", 4) 64 runtime.Dlog().B(true).B(false).I(-42).I16(0x7fff).U64(^uint64(0)).Hex(0xfff).P(nil).S(varString).S("const string").End() 65 got := dlogCanonicalize(runtime.DumpDebugLog()) 66 if want := "[] true false -42 32767 18446744073709551615 0xfff 0x0 aaaa const string\n"; got != want { 67 t.Fatalf("want %q, got %q", want, got) 68 } 69 } 70 71 func TestDebugLogSym(t *testing.T) { 72 skipDebugLog(t) 73 runtime.ResetDebugLog() 74 pc, _, _, _ := runtime.Caller(0) 75 runtime.Dlog().PC(pc).End() 76 got := dlogCanonicalize(runtime.DumpDebugLog()) 77 want := regexp.MustCompile(`\[\] 0x[0-9a-f]+ \[runtime_test\.TestDebugLogSym\+0x[0-9a-f]+ .*/debuglog_test\.go:[0-9]+\]\n`) 78 if !want.MatchString(got) { 79 t.Fatalf("want matching %s, got %q", want, got) 80 } 81 } 82 83 func TestDebugLogInterleaving(t *testing.T) { 84 skipDebugLog(t) 85 runtime.ResetDebugLog() 86 var wg sync.WaitGroup 87 done := int32(0) 88 wg.Add(1) 89 go func() { 90 // Encourage main goroutine to move around to 91 // different Ms and Ps. 92 for atomic.LoadInt32(&done) == 0 { 93 runtime.Gosched() 94 } 95 wg.Done() 96 }() 97 var want strings.Builder 98 for i := 0; i < 1000; i++ { 99 runtime.Dlog().I(i).End() 100 fmt.Fprintf(&want, "[] %d\n", i) 101 runtime.Gosched() 102 } 103 atomic.StoreInt32(&done, 1) 104 wg.Wait() 105 106 gotFull := runtime.DumpDebugLog() 107 got := dlogCanonicalize(gotFull) 108 if got != want.String() { 109 // Since the timestamps are useful in understand 110 // failures of this test, we print the uncanonicalized 111 // output. 112 t.Fatalf("want %q, got (uncanonicalized) %q", want.String(), gotFull) 113 } 114 } 115 116 func TestDebugLogWraparound(t *testing.T) { 117 skipDebugLog(t) 118 119 // Make sure we don't switch logs so it's easier to fill one up. 120 runtime.LockOSThread() 121 defer runtime.UnlockOSThread() 122 123 runtime.ResetDebugLog() 124 var longString = strings.Repeat("a", 128) 125 var want strings.Builder 126 for i, j := 0, 0; j < 2*runtime.DebugLogBytes; i, j = i+1, j+len(longString) { 127 runtime.Dlog().I(i).S(longString).End() 128 fmt.Fprintf(&want, "[] %d %s\n", i, longString) 129 } 130 log := runtime.DumpDebugLog() 131 132 // Check for "lost" message. 133 lost := regexp.MustCompile(`^>> begin log \d+; lost first \d+KB <<\n`) 134 if !lost.MatchString(log) { 135 t.Fatalf("want matching %s, got %q", lost, log) 136 } 137 idx := lost.FindStringIndex(log) 138 // Strip lost message. 139 log = dlogCanonicalize(log[idx[1]:]) 140 141 // Check log. 142 if !strings.HasSuffix(want.String(), log) { 143 t.Fatalf("wrong suffix:\n%s", log) 144 } 145 } 146 147 func TestDebugLogLongString(t *testing.T) { 148 skipDebugLog(t) 149 150 runtime.ResetDebugLog() 151 var longString = strings.Repeat("a", runtime.DebugLogStringLimit+1) 152 runtime.Dlog().S(longString).End() 153 got := dlogCanonicalize(runtime.DumpDebugLog()) 154 want := "[] " + strings.Repeat("a", runtime.DebugLogStringLimit) + " ..(1 more bytes)..\n" 155 if got != want { 156 t.Fatalf("want %q, got %q", want, got) 157 } 158 } 159 160 // TestDebugLogBuild verifies that the runtime builds with -tags=debuglog. 161 func TestDebugLogBuild(t *testing.T) { 162 testenv.MustHaveGoBuild(t) 163 164 // It doesn't matter which program we build, anything will rebuild the 165 // runtime. 166 if _, err := buildTestProg(t, "testprog", "-tags=debuglog"); err != nil { 167 t.Fatal(err) 168 } 169 }