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

     1  //go:build bpftests
     2  // +build bpftests
     3  
     4  package gobpfld
     5  
     6  import (
     7  	"fmt"
     8  	"math/rand"
     9  	"testing"
    10  	"unsafe"
    11  
    12  	"github.com/dylandreimerink/gobpfld/bpfsys"
    13  	"github.com/dylandreimerink/gobpfld/bpftypes"
    14  	"github.com/dylandreimerink/gobpfld/kernelsupport"
    15  )
    16  
    17  const (
    18  	sizeOfUint32 = uint32(unsafe.Sizeof(uint32(0)))
    19  	sizeOfUint64 = uint32(unsafe.Sizeof(uint64(0)))
    20  )
    21  
    22  func testArraymap_SingleGetSet_happyPath(t *testing.T, arrayMap *ArrayMap, maxEntries int) {
    23  	err := arrayMap.Load()
    24  	if err != nil {
    25  		t.Fatal(err)
    26  	}
    27  
    28  	// Just a simple go slice used to verify BPF map behavior.
    29  	verificationMap := make([]uint64, maxEntries)
    30  
    31  	// In a loop, read random values from both maps and compare them, then update that key in both maps for later
    32  	// iterations.
    33  	for i := 0; i < 1000000; i++ {
    34  		// Give us a random key that is sometimes(+5) outside of the map
    35  		randKey := rand.Int31n(int32(maxEntries + 5))
    36  
    37  		var a uint64
    38  		err = arrayMap.Get(uint32(randKey), &a)
    39  		// If randkey was outside of the map, we expect an error
    40  		if int(randKey) >= maxEntries {
    41  			if err == nil {
    42  				t.Fatal("getting keys outside of the map didn't result in an error")
    43  			}
    44  
    45  			// Now lets see if Set also gives us an error, it should.
    46  			err = arrayMap.Set(uint32(randKey), &a, bpfsys.BPFMapElemAny)
    47  			if err == nil {
    48  				t.Fatal("setting keys outside of the map didn't result in an error")
    49  			}
    50  
    51  			// Continue, since we don't have any result value to compare
    52  			continue
    53  		} else {
    54  			// In all other cases we don't expect an error
    55  			if err != nil {
    56  				t.Fatal(err)
    57  			}
    58  		}
    59  
    60  		v := verificationMap[randKey]
    61  
    62  		// If the current verification value isn't equal to the actual value, the implementation is broken.
    63  		if v != a {
    64  			t.Fatal(fmt.Errorf("v=%d, a=%d, should be equal", v, a))
    65  		}
    66  
    67  		newVal := rand.Uint64()
    68  		verificationMap[randKey] = newVal
    69  		err = arrayMap.Set(uint32(randKey), &newVal, bpfsys.BPFMapElemAny)
    70  		if err != nil {
    71  			t.Fatal(err)
    72  		}
    73  	}
    74  }
    75  
    76  // Tests that getting and setting of single keys work for a normal array map
    77  func TestArrayMap_SingleGetSet_HappyPath(t *testing.T) {
    78  	const maxEntries = 1000
    79  	arrayMap := ArrayMap{
    80  		AbstractMap: AbstractMap{
    81  			Name: MustNewObjName("test"),
    82  			Definition: BPFMapDef{
    83  				Type:       bpftypes.BPF_MAP_TYPE_ARRAY,
    84  				KeySize:    sizeOfUint32,
    85  				ValueSize:  sizeOfUint64,
    86  				MaxEntries: maxEntries,
    87  			},
    88  		},
    89  	}
    90  
    91  	testArraymap_SingleGetSet_happyPath(t, &arrayMap, maxEntries)
    92  }
    93  
    94  // Tests that getting and setting of single keys work for a mmaped array map
    95  func TestArrayMapMMAP_SingleGetSet_HappyPath(t *testing.T) {
    96  	// We can only perform this test if the kernel we are running on supports it
    97  	if !kernelsupport.CurrentFeatures.API.Has(kernelsupport.KFeatAPIMapMMap) {
    98  		t.Skip("Skip because the feature is not supported by current kernel version")
    99  	}
   100  
   101  	const maxEntries = 1000
   102  	arrayMap := ArrayMap{
   103  		AbstractMap: AbstractMap{
   104  			Name: MustNewObjName("test"),
   105  			Definition: BPFMapDef{
   106  				Type:       bpftypes.BPF_MAP_TYPE_ARRAY,
   107  				KeySize:    sizeOfUint32,
   108  				ValueSize:  sizeOfUint64,
   109  				MaxEntries: maxEntries,
   110  				Flags:      bpftypes.BPFMapFlagsMMapable,
   111  			},
   112  		},
   113  	}
   114  
   115  	testArraymap_SingleGetSet_happyPath(t, &arrayMap, maxEntries)
   116  }
   117  
   118  func testArraymap_BatchGetSet_happyPath(t *testing.T, arrayMap *ArrayMap, maxEntries int) {
   119  	// We can only perform this test if the kernel we are running on supports it
   120  	if !kernelsupport.CurrentFeatures.API.Has(kernelsupport.KFeatAPIMapBatchOps) {
   121  		t.Skip("Skip because the feature is not supported by current kernel version")
   122  	}
   123  
   124  	err := arrayMap.Load()
   125  	if err != nil {
   126  		t.Fatal(err)
   127  	}
   128  
   129  	// Just a simple go slice used to verify BPF map behavior.
   130  	verificationMap := make([]uint64, maxEntries)
   131  
   132  	// In a loop, read random values from both maps and compare them, then update that key in both maps for later
   133  	// iterations.
   134  	for i := 0; i < 10000; i++ {
   135  		batchSize := rand.Intn(maxEntries + 2)
   136  		keys := make([]uint32, batchSize)
   137  		values := make([]uint64, batchSize)
   138  
   139  		count, partial, err := arrayMap.GetBatch(keys, &values)
   140  		if err != nil {
   141  			t.Fatal(err)
   142  		}
   143  		keys = keys[:count]
   144  		values = values[:count]
   145  
   146  		// If the batch was bigger than the map size, we expect to only get a partial return
   147  		if batchSize > maxEntries {
   148  			if !partial {
   149  				t.Fatal("GetBatch returned partial=false when all values were read")
   150  			}
   151  		} else {
   152  			if partial {
   153  				t.Fatal("GetBatch returned partial=true when not all values were read")
   154  			}
   155  			if count != batchSize {
   156  				t.Fatalf("GetBatch, count=%d, batchSize=%d, should be equal when err = nil", count, batchSize)
   157  			}
   158  		}
   159  
   160  		for j := 0; j < count; j++ {
   161  			v := verificationMap[keys[j]]
   162  			a := values[j]
   163  
   164  			// If the current verification value isn't equal to the actual value, the implementation is broken.
   165  			if v != a {
   166  				t.Fatal(fmt.Errorf("v=%d, a=%d, should be equal", v, a))
   167  			}
   168  
   169  			newValue := rand.Uint64()
   170  			verificationMap[keys[j]] = newValue
   171  			values[j] = newValue
   172  		}
   173  
   174  		count, err = arrayMap.SetBatch(keys, &values, bpfsys.BPFMapElemAny)
   175  		if err != nil {
   176  			t.Fatal(err)
   177  		}
   178  		if count != len(keys) {
   179  			t.Fatal(fmt.Errorf("SetBatch, count=%d, len(keys)=%d, should be equal when err = nil", count, len(keys)))
   180  		}
   181  	}
   182  
   183  	// Cleanup the map, in case we run multiple tests in a same run
   184  	err = arrayMap.Close()
   185  	if err != nil {
   186  		t.Fatal(err)
   187  	}
   188  }
   189  
   190  // Tests that getting and setting of bulk keys work for a normal array map
   191  func TestArrayMap_BulkGetSet_HappyPath(t *testing.T) {
   192  	const maxEntries = 1000
   193  	arrayMap := ArrayMap{
   194  		AbstractMap: AbstractMap{
   195  			Name: MustNewObjName("test"),
   196  			Definition: BPFMapDef{
   197  				Type:       bpftypes.BPF_MAP_TYPE_ARRAY,
   198  				KeySize:    sizeOfUint32,
   199  				ValueSize:  sizeOfUint64,
   200  				MaxEntries: maxEntries,
   201  			},
   202  		},
   203  	}
   204  
   205  	testArraymap_BatchGetSet_happyPath(t, &arrayMap, maxEntries)
   206  }
   207  
   208  // Tests that getting and setting of bulk keys work for a normal array map
   209  func TestArrayMapMMap_BulkGetSet_HappyPath(t *testing.T) {
   210  	const maxEntries = 1000
   211  	arrayMap := ArrayMap{
   212  		AbstractMap: AbstractMap{
   213  			Name: MustNewObjName("test"),
   214  			Definition: BPFMapDef{
   215  				Type:       bpftypes.BPF_MAP_TYPE_ARRAY,
   216  				KeySize:    sizeOfUint32,
   217  				ValueSize:  sizeOfUint64,
   218  				MaxEntries: maxEntries,
   219  				Flags:      bpftypes.BPFMapFlagsMMapable,
   220  			},
   221  		},
   222  	}
   223  
   224  	testArraymap_BatchGetSet_happyPath(t, &arrayMap, maxEntries)
   225  }
   226  
   227  func TestArrayMapMMap_BulkGetSet_Edgecases(t *testing.T) {
   228  	// We can only perform this test if the kernel we are running on supports it
   229  	if !kernelsupport.CurrentFeatures.API.Has(kernelsupport.KFeatAPIMapMMap) {
   230  		t.Skip("Skip because the feature is not supported by current kernel version")
   231  	}
   232  
   233  	const maxEntries = 1000
   234  	arrayMap := ArrayMap{
   235  		AbstractMap: AbstractMap{
   236  			Name: MustNewObjName("test"),
   237  			Definition: BPFMapDef{
   238  				Type:       bpftypes.BPF_MAP_TYPE_ARRAY,
   239  				KeySize:    sizeOfUint32,
   240  				ValueSize:  sizeOfUint64,
   241  				MaxEntries: maxEntries,
   242  				Flags:      bpftypes.BPFMapFlagsMMapable,
   243  			},
   244  		},
   245  	}
   246  
   247  	// map is still unloaded
   248  
   249  	var v uint64
   250  	err := arrayMap.Get(0, &v)
   251  	if err == nil {
   252  		t.Fatal("calling Get on an unloaded map didn't give an error")
   253  	}
   254  
   255  	err = arrayMap.Set(0, &v, bpfsys.BPFMapElemAny)
   256  	if err == nil {
   257  		t.Fatal("calling Set on an unloaded map didn't give an error")
   258  	}
   259  
   260  	_, _, err = arrayMap.GetBatch(nil, &v)
   261  	if err == nil {
   262  		t.Fatal("calling GetBatch on an unloaded map didn't give an error")
   263  	}
   264  
   265  	_, err = arrayMap.SetBatch(nil, &v, bpfsys.BPFMapElemAny)
   266  	if err == nil {
   267  		t.Fatal("calling SetBatch on an unloaded map didn't give an error")
   268  	}
   269  
   270  	err = arrayMap.Load()
   271  	if err != nil {
   272  		t.Fatal(err)
   273  	}
   274  
   275  	keys := []uint32{1005}
   276  	values := []uint64{123456}
   277  	_, err = arrayMap.SetBatch(keys, &values, bpfsys.BPFMapElemAny)
   278  	if err == nil {
   279  		t.Fatal("calling SetBatch with out of bound keys should give an error")
   280  	}
   281  
   282  	// Cleanup the map, in case we run multiple tests in a same run
   283  	err = arrayMap.Close()
   284  	if err != nil {
   285  		t.Fatal(err)
   286  	}
   287  }