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  }