| // Copyright 2019 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // TODO(austin): All of these tests are skipped if the debuglog build |
| // tag isn't provided. That means we basically never test debuglog. |
| // There are two potential ways around this: |
| // |
| // 1. Make these tests re-build the runtime test with the debuglog |
| // build tag and re-invoke themselves. |
| // |
| // 2. Always build the whole debuglog infrastructure and depend on |
| // linker dead-code elimination to drop it. This is easy for dlog() |
| // since there won't be any calls to it. For printDebugLog, we can |
| // make panic call a wrapper that is call printDebugLog if the |
| // debuglog build tag is set, or otherwise do nothing. Then tests |
| // could call printDebugLog directly. This is the right answer in |
| // principle, but currently our linker reads in all symbols |
| // regardless, so this would slow down and bloat all links. If the |
| // linker gets more efficient about this, we should revisit this |
| // approach. |
| |
| package runtime_test |
| |
| import ( |
| "bytes" |
| "fmt" |
| "regexp" |
| "runtime" |
| "strings" |
| "sync" |
| "sync/atomic" |
| "testing" |
| ) |
| |
| func skipDebugLog(t *testing.T) { |
| if !runtime.DlogEnabled { |
| t.Skip("debug log disabled (rebuild with -tags debuglog)") |
| } |
| } |
| |
| func dlogCanonicalize(x string) string { |
| begin := regexp.MustCompile(`(?m)^>> begin log \d+ <<\n`) |
| x = begin.ReplaceAllString(x, "") |
| prefix := regexp.MustCompile(`(?m)^\[[^]]+\]`) |
| x = prefix.ReplaceAllString(x, "[]") |
| return x |
| } |
| |
| func TestDebugLog(t *testing.T) { |
| skipDebugLog(t) |
| runtime.ResetDebugLog() |
| runtime.Dlog().S("testing").End() |
| got := dlogCanonicalize(runtime.DumpDebugLog()) |
| if want := "[] testing\n"; got != want { |
| t.Fatalf("want %q, got %q", want, got) |
| } |
| } |
| |
| func TestDebugLogTypes(t *testing.T) { |
| skipDebugLog(t) |
| runtime.ResetDebugLog() |
| var varString = strings.Repeat("a", 4) |
| runtime.Dlog().B(true).B(false).I(-42).I16(0x7fff).U64(^uint64(0)).Hex(0xfff).P(nil).S(varString).S("const string").End() |
| got := dlogCanonicalize(runtime.DumpDebugLog()) |
| if want := "[] true false -42 32767 18446744073709551615 0xfff 0x0 aaaa const string\n"; got != want { |
| t.Fatalf("want %q, got %q", want, got) |
| } |
| } |
| |
| func TestDebugLogSym(t *testing.T) { |
| skipDebugLog(t) |
| runtime.ResetDebugLog() |
| pc, _, _, _ := runtime.Caller(0) |
| runtime.Dlog().PC(pc).End() |
| got := dlogCanonicalize(runtime.DumpDebugLog()) |
| want := regexp.MustCompile(`\[\] 0x[0-9a-f]+ \[runtime_test\.TestDebugLogSym\+0x[0-9a-f]+ .*/debuglog_test\.go:[0-9]+\]\n`) |
| if !want.MatchString(got) { |
| t.Fatalf("want matching %s, got %q", want, got) |
| } |
| } |
| |
| func TestDebugLogInterleaving(t *testing.T) { |
| skipDebugLog(t) |
| runtime.ResetDebugLog() |
| 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 bytes.Buffer |
| for i := 0; i < 1000; i++ { |
| runtime.Dlog().I(i).End() |
| fmt.Fprintf(&want, "[] %d\n", i) |
| runtime.Gosched() |
| } |
| atomic.StoreInt32(&done, 1) |
| wg.Wait() |
| |
| gotFull := runtime.DumpDebugLog() |
| got := dlogCanonicalize(gotFull) |
| if got != want.String() { |
| // Since the timestamps are useful in understand |
| // failures of this test, we print the uncanonicalized |
| // output. |
| t.Fatalf("want %q, got (uncanonicalized) %q", want.String(), gotFull) |
| } |
| } |
| |
| func TestDebugLogWraparound(t *testing.T) { |
| skipDebugLog(t) |
| |
| // Make sure we don't switch logs so it's easier to fill one up. |
| runtime.LockOSThread() |
| defer runtime.UnlockOSThread() |
| |
| runtime.ResetDebugLog() |
| var longString = strings.Repeat("a", 128) |
| var want bytes.Buffer |
| for i, j := 0, 0; j < 2*runtime.DebugLogBytes; i, j = i+1, j+len(longString) { |
| runtime.Dlog().I(i).S(longString).End() |
| fmt.Fprintf(&want, "[] %d %s\n", i, longString) |
| } |
| log := runtime.DumpDebugLog() |
| |
| // Check for "lost" message. |
| lost := regexp.MustCompile(`^>> begin log \d+; lost first \d+KB <<\n`) |
| if !lost.MatchString(log) { |
| t.Fatalf("want matching %s, got %q", lost, log) |
| } |
| idx := lost.FindStringIndex(log) |
| // Strip lost message. |
| log = dlogCanonicalize(log[idx[1]:]) |
| |
| // Check log. |
| if !strings.HasSuffix(want.String(), log) { |
| t.Fatalf("wrong suffix:\n%s", log) |
| } |
| } |
| |
| func TestDebugLogLongString(t *testing.T) { |
| skipDebugLog(t) |
| |
| runtime.ResetDebugLog() |
| var longString = strings.Repeat("a", runtime.DebugLogStringLimit+1) |
| runtime.Dlog().S(longString).End() |
| got := dlogCanonicalize(runtime.DumpDebugLog()) |
| want := "[] " + strings.Repeat("a", runtime.DebugLogStringLimit) + " ..(1 more bytes)..\n" |
| if got != want { |
| t.Fatalf("want %q, got %q", want, got) |
| } |
| } |