github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/map_percpu_array_test.go (about)

     1  //go:build bpftests
     2  // +build bpftests
     3  
     4  package gobpfld
     5  
     6  import (
     7  	"fmt"
     8  	"math/rand"
     9  	"reflect"
    10  	"runtime"
    11  	"testing"
    12  
    13  	"github.com/dylandreimerink/gobpfld/bpfsys"
    14  	"github.com/dylandreimerink/gobpfld/bpftypes"
    15  	"github.com/dylandreimerink/gobpfld/kernelsupport"
    16  )
    17  
    18  func testPerCPUArraymap_SingleGetSet_happyPath(t *testing.T, arrayMap *PerCPUArrayMap, maxEntries int) {
    19  	err := arrayMap.Load()
    20  	if err != nil {
    21  		t.Fatal(err)
    22  	}
    23  
    24  	cpuCount := runtime.NumCPU()
    25  
    26  	// Just a simple go slice used to verify BPF map behavior.
    27  	verificationMap := make([]uint64, maxEntries*cpuCount)
    28  
    29  	// Test with less iterations in short mode
    30  	iter := 100000
    31  	if testing.Short() {
    32  		iter = 1000
    33  	}
    34  
    35  	// In a loop, read random values from both maps and compare them, then update that key in both maps for later
    36  	// iterations.
    37  	for i := 0; i < iter; i++ {
    38  		// Give us a random key that is sometimes(+5) outside of the map
    39  		randKey := rand.Int31n(int32(maxEntries + 5))
    40  
    41  		a := make([]uint64, cpuCount)
    42  		err = arrayMap.Get(uint32(randKey), &a)
    43  		// If randkey was outside of the map, we expect an error
    44  		if int(randKey) >= maxEntries {
    45  			if err == nil {
    46  				t.Fatal("getting keys outside of the map didn't result in an error")
    47  			}
    48  
    49  			// Now lets see if Set also gives us an error, it should.
    50  			err = arrayMap.Set(uint32(randKey), &a, bpfsys.BPFMapElemAny)
    51  			if err == nil {
    52  				t.Fatal("setting keys outside of the map didn't result in an error")
    53  			}
    54  
    55  			// Continue, since we don't have any result value to compare
    56  			continue
    57  		} else {
    58  			// In all other cases we don't expect an error
    59  			if err != nil {
    60  				t.Fatal(err)
    61  			}
    62  		}
    63  
    64  		v := verificationMap[randKey*int32(cpuCount) : (randKey+1)*int32(cpuCount)]
    65  
    66  		// If the current verification value isn't equal to the actual value, the implementation is broken.
    67  		if !reflect.DeepEqual(v, a) {
    68  			t.Fatal(fmt.Errorf("v=%v, a=%v, should be equal", v, a))
    69  		}
    70  
    71  		newVal := make([]uint64, cpuCount)
    72  		for j := range newVal {
    73  			newVal[j] = rand.Uint64()
    74  		}
    75  		copy(verificationMap[randKey*int32(cpuCount):(randKey+1)*int32(cpuCount)], newVal)
    76  
    77  		err = arrayMap.Set(uint32(randKey), &newVal, bpfsys.BPFMapElemAny)
    78  		if err != nil {
    79  			t.Fatal(err)
    80  		}
    81  	}
    82  
    83  	// Cleanup the map, in case we run multiple tests in a same run
    84  	err = arrayMap.Close()
    85  	if err != nil {
    86  		t.Fatal(err)
    87  	}
    88  }
    89  
    90  // Tests that getting and setting of single keys work for a normal array map
    91  func TestPerCPUArrayMap_SingleGetSet_HappyPath(t *testing.T) {
    92  	const maxEntries = 1000
    93  	arrayMap := PerCPUArrayMap{
    94  		AbstractMap: AbstractMap{
    95  			Name: MustNewObjName("test"),
    96  			Definition: BPFMapDef{
    97  				Type:       bpftypes.BPF_MAP_TYPE_PERCPU_ARRAY,
    98  				KeySize:    sizeOfUint32,
    99  				ValueSize:  sizeOfUint64,
   100  				MaxEntries: maxEntries,
   101  			},
   102  		},
   103  	}
   104  
   105  	testPerCPUArraymap_SingleGetSet_happyPath(t, &arrayMap, maxEntries)
   106  }
   107  
   108  func testPerCPUArrayMap_BatchGetSet_happyPath(t *testing.T, arrayMap *PerCPUArrayMap, maxEntries int) {
   109  	err := arrayMap.Load()
   110  	if err != nil {
   111  		t.Fatal(err)
   112  	}
   113  
   114  	cpuCount := runtime.NumCPU()
   115  
   116  	// Just a simple go slice used to verify BPF map behavior.
   117  	verificationMap := make([]uint64, maxEntries*cpuCount)
   118  
   119  	// Test with less iterations in short mode
   120  	iter := 10000
   121  	if testing.Short() {
   122  		iter = 1000
   123  	}
   124  
   125  	// In a loop, read random values from both maps and compare them, then update that key in both maps for later
   126  	// iterations.
   127  	for i := 0; i < iter; i++ {
   128  		batchSize := rand.Intn(maxEntries + 2)
   129  		keys := make([]uint32, batchSize)
   130  		values := make([]uint64, batchSize*cpuCount)
   131  
   132  		count, partial, err := arrayMap.GetBatch(keys, &values)
   133  		if err != nil {
   134  			t.Fatal(err)
   135  		}
   136  		keys = keys[:count]
   137  		values = values[:count*cpuCount]
   138  
   139  		// If the batch was bigger than the map size, we expect to only get a partial return
   140  		if batchSize > maxEntries {
   141  			if !partial {
   142  				t.Fatal("GetBatch returned partial=false when all values were read")
   143  			}
   144  		} else {
   145  			if partial {
   146  				t.Fatal("GetBatch returned partial=true when not all values were read")
   147  			}
   148  			if count != batchSize {
   149  				t.Fatalf("GetBatch, count=%d, batchSize=%d, should be equal when err = nil", count, batchSize)
   150  			}
   151  		}
   152  
   153  		for j := 0; j < count; j++ {
   154  			v := verificationMap[keys[j]]
   155  			a := values[j]
   156  
   157  			// If the current verification value isn't equal to the actual value, the implementation is broken.
   158  			if v != a {
   159  				t.Fatal(fmt.Errorf("v=%d, a=%d, should be equal", v, a))
   160  			}
   161  
   162  			newValue := rand.Uint64()
   163  			verificationMap[keys[j]] = newValue
   164  			values[j] = newValue
   165  		}
   166  
   167  		count, err = arrayMap.SetBatch(keys, &values, bpfsys.BPFMapElemAny)
   168  		if err != nil {
   169  			t.Fatal(err)
   170  		}
   171  		if count != len(keys) {
   172  			t.Fatal(fmt.Errorf("SetBatch, count=%d, len(keys)=%d, should be equal when err = nil", count, len(keys)))
   173  		}
   174  	}
   175  
   176  	// Cleanup the map, in case we run multiple tests in a same run
   177  	err = arrayMap.Close()
   178  	if err != nil {
   179  		t.Fatal(err)
   180  	}
   181  }
   182  
   183  // Tests that getting and setting of bulk keys work for a normal array map
   184  func TestPerCPUArrayMap_BulkGetSet_HappyPath(t *testing.T) {
   185  	// We can only perform this test if the kernel we are running on supports it
   186  	if !kernelsupport.CurrentFeatures.Map.Has(kernelsupport.KFeatMapPerCPUArrayBatchOps) {
   187  		t.Skip("Skip because the feature is not supported by kernel")
   188  	}
   189  
   190  	const maxEntries = 1000
   191  	arrayMap := PerCPUArrayMap{
   192  		AbstractMap: AbstractMap{
   193  			Name: MustNewObjName("test"),
   194  			Definition: BPFMapDef{
   195  				Type:       bpftypes.BPF_MAP_TYPE_PERCPU_ARRAY,
   196  				KeySize:    sizeOfUint32,
   197  				ValueSize:  sizeOfUint64,
   198  				MaxEntries: maxEntries,
   199  			},
   200  		},
   201  	}
   202  
   203  	testPerCPUArrayMap_BatchGetSet_happyPath(t, &arrayMap, maxEntries)
   204  }