1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
//go:build linux
package perf
import (
"io"
"os"
"testing"
"github.com/go-quicktest/qt"
"github.com/cilium/ebpf/internal/unix"
)
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, qt.Equals(err, wantErr))
qt.Assert(t, qt.DeepEquals(buf, 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) {
event, ring, err := newPerfEventRing(0, buffer, ReaderOptions{Watermark: watermark, Overwritable: overwritable})
if err != nil {
t.Fatal(err)
}
defer event.Close()
defer ring.Close()
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, ReaderOptions{Watermark: 8193, Overwritable: false})
if err == nil {
t.Fatal("watermark > buffer allowed")
}
_, _, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: true})
if err == nil {
t.Fatal("watermark > buffer allowed")
}
// watermark == buffer
_, _, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8192, Overwritable: false})
if err == nil {
t.Fatal("watermark == buffer allowed")
}
_, _, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: 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)
}
|