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 }