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 }