159 lines
4.6 KiB
Go
159 lines
4.6 KiB
Go
// 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)
|
|
}
|
|
}
|