183 lines
4.5 KiB
Go
183 lines
4.5 KiB
Go
package perf
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/cilium/ebpf/internal/unix"
|
|
qt "github.com/frankban/quicktest"
|
|
)
|
|
|
|
func TestRingBufferReader(t *testing.T) {
|
|
ring := makeForwardRing(2, 0)
|
|
checkRead(t, ring, []byte{0, 1}, io.EOF)
|
|
checkRead(t, ring, []byte{}, io.EOF)
|
|
|
|
// Wrapping read
|
|
ring = makeForwardRing(2, 1)
|
|
checkRead(t, ring, []byte{1}, nil)
|
|
checkRead(t, ring, []byte{0}, io.EOF)
|
|
checkRead(t, ring, []byte{}, io.EOF)
|
|
}
|
|
|
|
func TestRingBufferReverseReader(t *testing.T) {
|
|
// First case: read 4, starting from offset 2.
|
|
// The buffer should contain the following:
|
|
//
|
|
// [0 1 2 3]
|
|
// ^
|
|
// |
|
|
// head
|
|
//
|
|
// As we read from position 2, we should get [2, 3].
|
|
// Then, when we read it for the second time, we should get [0, 1] as we would
|
|
// have looped around the buffer.
|
|
ring := makeReverseRing(4, 2)
|
|
checkRead(t, ring, []byte{2, 3}, nil)
|
|
checkRead(t, ring, []byte{0, 1}, io.EOF)
|
|
checkRead(t, ring, []byte{}, io.EOF)
|
|
|
|
// Complicated case: read bytes until previous_head.
|
|
//
|
|
// [0 1 2 3]
|
|
// ^ ^
|
|
// | |
|
|
// | +---previous_head
|
|
// head
|
|
ring = makeReverseRing(4, 2)
|
|
checkReadBuffer(t, ring, []byte{2}, nil, make([]byte, 1))
|
|
// Next read would be {3}, but we don't consume it.
|
|
|
|
// Pretend the kernel wrote another 2 bytes.
|
|
ring.meta.Data_head -= 2
|
|
ring.loadHead()
|
|
|
|
// {3} is discarded.
|
|
checkRead(t, ring, []byte{0, 1}, io.EOF)
|
|
|
|
// Complicated case: read the whole buffer because it was "overwritten".
|
|
//
|
|
// [0 1 2 3]
|
|
// ^
|
|
// |
|
|
// +---previous_head
|
|
// |
|
|
// head
|
|
//
|
|
// So, we should first read [2, 3] then [0, 1].
|
|
ring = makeReverseRing(4, 2)
|
|
ring.meta.Data_head -= ring.meta.Data_size
|
|
ring.loadHead()
|
|
|
|
checkRead(t, ring, []byte{2, 3}, nil)
|
|
checkRead(t, ring, []byte{0, 1}, io.EOF)
|
|
}
|
|
|
|
// ensure that the next call to Read() yields the correct result.
|
|
//
|
|
// Read is called with a buffer that is larger than want so
|
|
// that corner cases around wrapping can be checked. Use
|
|
// checkReadBuffer if that is not desired.
|
|
func checkRead(t *testing.T, r io.Reader, want []byte, wantErr error) {
|
|
checkReadBuffer(t, r, want, wantErr, make([]byte, len(want)+1))
|
|
}
|
|
|
|
func checkReadBuffer(t *testing.T, r io.Reader, want []byte, wantErr error, buf []byte) {
|
|
t.Helper()
|
|
|
|
n, err := r.Read(buf)
|
|
buf = buf[:n]
|
|
qt.Assert(t, err, qt.Equals, wantErr)
|
|
qt.Assert(t, buf, qt.DeepEquals, want)
|
|
}
|
|
|
|
func makeBuffer(size int) []byte {
|
|
buf := make([]byte, size)
|
|
for i := range buf {
|
|
buf[i] = byte(i)
|
|
}
|
|
return buf
|
|
}
|
|
|
|
func makeReverseRing(size, offset int) *reverseReader {
|
|
if size != 0 && (size&(size-1)) != 0 {
|
|
panic("size must be power of two")
|
|
}
|
|
|
|
meta := unix.PerfEventMmapPage{
|
|
Data_head: 0 - uint64(size) - uint64(offset),
|
|
Data_tail: 0, // never written by the kernel
|
|
Data_size: uint64(size),
|
|
}
|
|
|
|
return newReverseReader(&meta, makeBuffer(size))
|
|
}
|
|
|
|
func makeForwardRing(size, offset int) *forwardReader {
|
|
if size != 0 && (size&(size-1)) != 0 {
|
|
panic("size must be power of two")
|
|
}
|
|
|
|
meta := unix.PerfEventMmapPage{
|
|
Data_head: uint64(size + offset),
|
|
Data_tail: uint64(offset),
|
|
Data_size: uint64(size),
|
|
}
|
|
|
|
return newForwardReader(&meta, makeBuffer(size))
|
|
}
|
|
|
|
func TestPerfEventRing(t *testing.T) {
|
|
check := func(buffer, watermark int, overwritable bool) {
|
|
ring, err := newPerfEventRing(0, buffer, watermark, overwritable)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
size := ring.size()
|
|
|
|
// Ring size should be at least as big as buffer
|
|
if size < buffer {
|
|
t.Fatalf("ring size %d smaller than buffer %d", size, buffer)
|
|
}
|
|
|
|
// Ring size should be of the form 2^n pages (meta page has already been removed)
|
|
if size%os.Getpagesize() != 0 {
|
|
t.Fatalf("ring size %d not whole number of pages (pageSize %d)", size, os.Getpagesize())
|
|
}
|
|
nPages := size / os.Getpagesize()
|
|
if nPages&(nPages-1) != 0 {
|
|
t.Fatalf("ring size %d (%d pages) not a power of two pages (pageSize %d)", size, nPages, os.Getpagesize())
|
|
}
|
|
}
|
|
|
|
// watermark > buffer
|
|
_, err := newPerfEventRing(0, 8192, 8193, false)
|
|
if err == nil {
|
|
t.Fatal("watermark > buffer allowed")
|
|
}
|
|
_, err = newPerfEventRing(0, 8192, 8193, true)
|
|
if err == nil {
|
|
t.Fatal("watermark > buffer allowed")
|
|
}
|
|
|
|
// watermark == buffer
|
|
_, err = newPerfEventRing(0, 8192, 8192, false)
|
|
if err == nil {
|
|
t.Fatal("watermark == buffer allowed")
|
|
}
|
|
_, err = newPerfEventRing(0, 8192, 8192, true)
|
|
if err == nil {
|
|
t.Fatal("watermark == buffer allowed")
|
|
}
|
|
|
|
// buffer not a power of two, watermark < buffer
|
|
check(8193, 8192, false)
|
|
check(8193, 8192, true)
|
|
|
|
// large buffer not a multiple of page size at all (prime)
|
|
check(65537, 8192, false)
|
|
check(65537, 8192, true)
|
|
}
|