github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/perf/ring_test.go (about) 1 package perf 2 3 import ( 4 "io" 5 "os" 6 "testing" 7 8 "github.com/go-quicktest/qt" 9 10 "github.com/cilium/ebpf/internal/unix" 11 ) 12 13 func TestRingBufferReader(t *testing.T) { 14 ring := makeForwardRing(2, 0) 15 checkRead(t, ring, []byte{0, 1}, io.EOF) 16 checkRead(t, ring, []byte{}, io.EOF) 17 18 // Wrapping read 19 ring = makeForwardRing(2, 1) 20 checkRead(t, ring, []byte{1}, nil) 21 checkRead(t, ring, []byte{0}, io.EOF) 22 checkRead(t, ring, []byte{}, io.EOF) 23 } 24 25 func TestRingBufferReverseReader(t *testing.T) { 26 // First case: read 4, starting from offset 2. 27 // The buffer should contain the following: 28 // 29 // [0 1 2 3] 30 // ^ 31 // | 32 // head 33 // 34 // As we read from position 2, we should get [2, 3]. 35 // Then, when we read it for the second time, we should get [0, 1] as we would 36 // have looped around the buffer. 37 ring := makeReverseRing(4, 2) 38 checkRead(t, ring, []byte{2, 3}, nil) 39 checkRead(t, ring, []byte{0, 1}, io.EOF) 40 checkRead(t, ring, []byte{}, io.EOF) 41 42 // Complicated case: read bytes until previous_head. 43 // 44 // [0 1 2 3] 45 // ^ ^ 46 // | | 47 // | +---previous_head 48 // head 49 ring = makeReverseRing(4, 2) 50 checkReadBuffer(t, ring, []byte{2}, nil, make([]byte, 1)) 51 // Next read would be {3}, but we don't consume it. 52 53 // Pretend the kernel wrote another 2 bytes. 54 ring.meta.Data_head -= 2 55 ring.loadHead() 56 57 // {3} is discarded. 58 checkRead(t, ring, []byte{0, 1}, io.EOF) 59 60 // Complicated case: read the whole buffer because it was "overwritten". 61 // 62 // [0 1 2 3] 63 // ^ 64 // | 65 // +---previous_head 66 // | 67 // head 68 // 69 // So, we should first read [2, 3] then [0, 1]. 70 ring = makeReverseRing(4, 2) 71 ring.meta.Data_head -= ring.meta.Data_size 72 ring.loadHead() 73 74 checkRead(t, ring, []byte{2, 3}, nil) 75 checkRead(t, ring, []byte{0, 1}, io.EOF) 76 } 77 78 // ensure that the next call to Read() yields the correct result. 79 // 80 // Read is called with a buffer that is larger than want so 81 // that corner cases around wrapping can be checked. Use 82 // checkReadBuffer if that is not desired. 83 func checkRead(t *testing.T, r io.Reader, want []byte, wantErr error) { 84 checkReadBuffer(t, r, want, wantErr, make([]byte, len(want)+1)) 85 } 86 87 func checkReadBuffer(t *testing.T, r io.Reader, want []byte, wantErr error, buf []byte) { 88 t.Helper() 89 90 n, err := r.Read(buf) 91 buf = buf[:n] 92 qt.Assert(t, qt.Equals(err, wantErr)) 93 qt.Assert(t, qt.DeepEquals(buf, want)) 94 } 95 96 func makeBuffer(size int) []byte { 97 buf := make([]byte, size) 98 for i := range buf { 99 buf[i] = byte(i) 100 } 101 return buf 102 } 103 104 func makeReverseRing(size, offset int) *reverseReader { 105 if size != 0 && (size&(size-1)) != 0 { 106 panic("size must be power of two") 107 } 108 109 meta := unix.PerfEventMmapPage{ 110 Data_head: 0 - uint64(size) - uint64(offset), 111 Data_tail: 0, // never written by the kernel 112 Data_size: uint64(size), 113 } 114 115 return newReverseReader(&meta, makeBuffer(size)) 116 } 117 118 func makeForwardRing(size, offset int) *forwardReader { 119 if size != 0 && (size&(size-1)) != 0 { 120 panic("size must be power of two") 121 } 122 123 meta := unix.PerfEventMmapPage{ 124 Data_head: uint64(size + offset), 125 Data_tail: uint64(offset), 126 Data_size: uint64(size), 127 } 128 129 return newForwardReader(&meta, makeBuffer(size)) 130 } 131 132 func TestPerfEventRing(t *testing.T) { 133 check := func(buffer, watermark int, overwritable bool) { 134 event, ring, err := newPerfEventRing(0, buffer, ReaderOptions{Watermark: watermark, Overwritable: overwritable}) 135 if err != nil { 136 t.Fatal(err) 137 } 138 defer event.Close() 139 defer ring.Close() 140 141 size := ring.size() 142 143 // Ring size should be at least as big as buffer 144 if size < buffer { 145 t.Fatalf("ring size %d smaller than buffer %d", size, buffer) 146 } 147 148 // Ring size should be of the form 2^n pages (meta page has already been removed) 149 if size%os.Getpagesize() != 0 { 150 t.Fatalf("ring size %d not whole number of pages (pageSize %d)", size, os.Getpagesize()) 151 } 152 nPages := size / os.Getpagesize() 153 if nPages&(nPages-1) != 0 { 154 t.Fatalf("ring size %d (%d pages) not a power of two pages (pageSize %d)", size, nPages, os.Getpagesize()) 155 } 156 } 157 158 // watermark > buffer 159 _, _, err := newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: false}) 160 if err == nil { 161 t.Fatal("watermark > buffer allowed") 162 } 163 _, _, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: true}) 164 if err == nil { 165 t.Fatal("watermark > buffer allowed") 166 } 167 168 // watermark == buffer 169 _, _, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8192, Overwritable: false}) 170 if err == nil { 171 t.Fatal("watermark == buffer allowed") 172 } 173 _, _, err = newPerfEventRing(0, 8192, ReaderOptions{Watermark: 8193, Overwritable: true}) 174 if err == nil { 175 t.Fatal("watermark == buffer allowed") 176 } 177 178 // buffer not a power of two, watermark < buffer 179 check(8193, 8192, false) 180 check(8193, 8192, true) 181 182 // large buffer not a multiple of page size at all (prime) 183 check(65537, 8192, false) 184 check(65537, 8192, true) 185 }