mirror of
https://github.com/golang/go.git
synced 2025-05-29 03:11:26 +00:00
The failure observed in https://build.golang.org/log/766ad55d098ee1979ba1ae261e0813063567743e appears to be spurious. If it is, this change will fix it. If it is not, and somehow the Read has deadlocked, then this change will help to diagnose it (by causing the test to time out and dump its running goroutines). For #56196. Change-Id: Ic74f018384a64e95566a5b5d8126cbd59ab5e5fa Reviewed-on: https://go-review.googlesource.com/c/go/+/528399 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com> Auto-Submit: Bryan Mills <bcmills@google.com>
177 lines
8.5 KiB
Go
177 lines
8.5 KiB
Go
// Copyright 2017 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.
|
|
|
|
package runtime_test
|
|
|
|
import (
|
|
"reflect"
|
|
. "runtime"
|
|
"testing"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
func TestProfBuf(t *testing.T) {
|
|
const hdrSize = 2
|
|
|
|
write := func(t *testing.T, b *ProfBuf, tag unsafe.Pointer, now int64, hdr []uint64, stk []uintptr) {
|
|
b.Write(&tag, now, hdr, stk)
|
|
}
|
|
read := func(t *testing.T, b *ProfBuf, data []uint64, tags []unsafe.Pointer) {
|
|
rdata, rtags, eof := b.Read(ProfBufNonBlocking)
|
|
if !reflect.DeepEqual(rdata, data) || !reflect.DeepEqual(rtags, tags) {
|
|
t.Fatalf("unexpected profile read:\nhave data %#x\nwant data %#x\nhave tags %#x\nwant tags %#x", rdata, data, rtags, tags)
|
|
}
|
|
if eof {
|
|
t.Fatalf("unexpected eof")
|
|
}
|
|
}
|
|
readBlock := func(t *testing.T, b *ProfBuf, data []uint64, tags []unsafe.Pointer) func() {
|
|
c := make(chan int)
|
|
go func() {
|
|
eof := data == nil
|
|
rdata, rtags, reof := b.Read(ProfBufBlocking)
|
|
if !reflect.DeepEqual(rdata, data) || !reflect.DeepEqual(rtags, tags) || reof != eof {
|
|
// Errorf, not Fatalf, because called in goroutine.
|
|
t.Errorf("unexpected profile read:\nhave data %#x\nwant data %#x\nhave tags %#x\nwant tags %#x\nhave eof=%v, want %v", rdata, data, rtags, tags, reof, eof)
|
|
}
|
|
c <- 1
|
|
}()
|
|
time.Sleep(10 * time.Millisecond) // let goroutine run and block
|
|
return func() { <-c }
|
|
}
|
|
readEOF := func(t *testing.T, b *ProfBuf) {
|
|
rdata, rtags, eof := b.Read(ProfBufBlocking)
|
|
if rdata != nil || rtags != nil || !eof {
|
|
t.Errorf("unexpected profile read: %#x, %#x, eof=%v; want nil, nil, eof=true", rdata, rtags, eof)
|
|
}
|
|
rdata, rtags, eof = b.Read(ProfBufNonBlocking)
|
|
if rdata != nil || rtags != nil || !eof {
|
|
t.Errorf("unexpected profile read (non-blocking): %#x, %#x, eof=%v; want nil, nil, eof=true", rdata, rtags, eof)
|
|
}
|
|
}
|
|
|
|
myTags := make([]byte, 100)
|
|
t.Logf("myTags is %p", &myTags[0])
|
|
|
|
t.Run("BasicWriteRead", func(t *testing.T) {
|
|
b := NewProfBuf(2, 11, 1)
|
|
write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
|
|
read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])})
|
|
read(t, b, nil, nil) // release data returned by previous read
|
|
write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
|
|
read(t, b, []uint64{8, 99, 101, 102, 201, 202, 203, 204}, []unsafe.Pointer{unsafe.Pointer(&myTags[2])})
|
|
})
|
|
|
|
t.Run("ReadMany", func(t *testing.T) {
|
|
b := NewProfBuf(2, 50, 50)
|
|
write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
|
|
write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
|
|
write(t, b, unsafe.Pointer(&myTags[1]), 500, []uint64{502, 504}, []uintptr{506})
|
|
read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 99, 101, 102, 201, 202, 203, 204, 5, 500, 502, 504, 506}, []unsafe.Pointer{unsafe.Pointer(&myTags[0]), unsafe.Pointer(&myTags[2]), unsafe.Pointer(&myTags[1])})
|
|
})
|
|
|
|
t.Run("ReadManyShortData", func(t *testing.T) {
|
|
b := NewProfBuf(2, 50, 50)
|
|
write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
|
|
write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
|
|
read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 99, 101, 102, 201, 202, 203, 204}, []unsafe.Pointer{unsafe.Pointer(&myTags[0]), unsafe.Pointer(&myTags[2])})
|
|
})
|
|
|
|
t.Run("ReadManyShortTags", func(t *testing.T) {
|
|
b := NewProfBuf(2, 50, 50)
|
|
write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
|
|
write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
|
|
read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 99, 101, 102, 201, 202, 203, 204}, []unsafe.Pointer{unsafe.Pointer(&myTags[0]), unsafe.Pointer(&myTags[2])})
|
|
})
|
|
|
|
t.Run("ReadAfterOverflow1", func(t *testing.T) {
|
|
// overflow record synthesized by write
|
|
b := NewProfBuf(2, 16, 5)
|
|
write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9}) // uses 10
|
|
read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])}) // reads 10 but still in use until next read
|
|
write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5}) // uses 6
|
|
read(t, b, []uint64{6, 1, 2, 3, 4, 5}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])}) // reads 6 but still in use until next read
|
|
// now 10 available
|
|
write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204, 205, 206, 207, 208, 209}) // no room
|
|
for i := 0; i < 299; i++ {
|
|
write(t, b, unsafe.Pointer(&myTags[3]), int64(100+i), []uint64{101, 102}, []uintptr{201, 202, 203, 204}) // no room for overflow+this record
|
|
}
|
|
write(t, b, unsafe.Pointer(&myTags[1]), 500, []uint64{502, 504}, []uintptr{506}) // room for overflow+this record
|
|
read(t, b, []uint64{5, 99, 0, 0, 300, 5, 500, 502, 504, 506}, []unsafe.Pointer{nil, unsafe.Pointer(&myTags[1])})
|
|
})
|
|
|
|
t.Run("ReadAfterOverflow2", func(t *testing.T) {
|
|
// overflow record synthesized by read
|
|
b := NewProfBuf(2, 16, 5)
|
|
write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
|
|
write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213})
|
|
for i := 0; i < 299; i++ {
|
|
write(t, b, unsafe.Pointer(&myTags[3]), 100, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
|
|
}
|
|
read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])}) // reads 10 but still in use until next read
|
|
write(t, b, unsafe.Pointer(&myTags[1]), 500, []uint64{502, 504}, []uintptr{}) // still overflow
|
|
read(t, b, []uint64{5, 99, 0, 0, 301}, []unsafe.Pointer{nil}) // overflow synthesized by read
|
|
write(t, b, unsafe.Pointer(&myTags[1]), 500, []uint64{502, 505}, []uintptr{506}) // written
|
|
read(t, b, []uint64{5, 500, 502, 505, 506}, []unsafe.Pointer{unsafe.Pointer(&myTags[1])})
|
|
})
|
|
|
|
t.Run("ReadAtEndAfterOverflow", func(t *testing.T) {
|
|
b := NewProfBuf(2, 12, 5)
|
|
write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
|
|
write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
|
|
for i := 0; i < 299; i++ {
|
|
write(t, b, unsafe.Pointer(&myTags[3]), 100, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
|
|
}
|
|
read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])})
|
|
read(t, b, []uint64{5, 99, 0, 0, 300}, []unsafe.Pointer{nil})
|
|
write(t, b, unsafe.Pointer(&myTags[1]), 500, []uint64{502, 504}, []uintptr{506})
|
|
read(t, b, []uint64{5, 500, 502, 504, 506}, []unsafe.Pointer{unsafe.Pointer(&myTags[1])})
|
|
})
|
|
|
|
t.Run("BlockingWriteRead", func(t *testing.T) {
|
|
b := NewProfBuf(2, 11, 1)
|
|
wait := readBlock(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])})
|
|
write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
|
|
wait()
|
|
wait = readBlock(t, b, []uint64{8, 99, 101, 102, 201, 202, 203, 204}, []unsafe.Pointer{unsafe.Pointer(&myTags[2])})
|
|
time.Sleep(10 * time.Millisecond)
|
|
write(t, b, unsafe.Pointer(&myTags[2]), 99, []uint64{101, 102}, []uintptr{201, 202, 203, 204})
|
|
wait()
|
|
wait = readBlock(t, b, nil, nil)
|
|
b.Close()
|
|
wait()
|
|
wait = readBlock(t, b, nil, nil)
|
|
wait()
|
|
readEOF(t, b)
|
|
})
|
|
|
|
t.Run("DataWraparound", func(t *testing.T) {
|
|
b := NewProfBuf(2, 16, 1024)
|
|
for i := 0; i < 10; i++ {
|
|
write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
|
|
read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])})
|
|
read(t, b, nil, nil) // release data returned by previous read
|
|
}
|
|
})
|
|
|
|
t.Run("TagWraparound", func(t *testing.T) {
|
|
b := NewProfBuf(2, 1024, 2)
|
|
for i := 0; i < 10; i++ {
|
|
write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
|
|
read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])})
|
|
read(t, b, nil, nil) // release data returned by previous read
|
|
}
|
|
})
|
|
|
|
t.Run("BothWraparound", func(t *testing.T) {
|
|
b := NewProfBuf(2, 16, 2)
|
|
for i := 0; i < 10; i++ {
|
|
write(t, b, unsafe.Pointer(&myTags[0]), 1, []uint64{2, 3}, []uintptr{4, 5, 6, 7, 8, 9})
|
|
read(t, b, []uint64{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []unsafe.Pointer{unsafe.Pointer(&myTags[0])})
|
|
read(t, b, nil, nil) // release data returned by previous read
|
|
}
|
|
})
|
|
}
|