github.com/aristanetworks/goarista@v0.0.0-20240514173732-cca2755bbd44/sizeof/sizeof_test.go (about) 1 // Copyright (c) 2017 Arista Networks, Inc. 2 // Use of this source code is governed by the Apache License 2.0 3 // that can be found in the COPYING file. 4 5 package sizeof 6 7 import ( 8 "fmt" 9 "strconv" 10 "testing" 11 "unsafe" 12 13 "github.com/aristanetworks/goarista/test" 14 ) 15 16 type yolo struct { 17 i int32 18 a [3]int8 19 p unsafe.Pointer 20 } 21 22 func (y yolo) String() string { 23 return "Yolo" 24 } 25 26 func TestDeepSizeof(t *testing.T) { 27 ptrSize := uintptr(unsafe.Sizeof(unsafe.Pointer(t))) 28 // hmapStructSize represent the size of struct hmap defined in 29 // file /go/src/runtime/hashmap.go 30 hmapStructSize := uintptr(unsafe.Sizeof(int(0)) + 2*1 + 2 + 4 + 31 2*ptrSize + ptrSize + ptrSize) 32 var alignement uintptr = 4 33 if ptrSize == 4 { 34 alignement = 0 35 } 36 strHdrSize := unsafe.Sizeof("") // int + ptr to data 37 sliceHdrSize := 3 * ptrSize // ptr to data + 2 * int 38 39 // struct hchan is defined in /go/src/runtime/chan.go 40 chanHdrSize := 2*ptrSize + ptrSize + 2 + 2 /* padding */ + 4 + ptrSize + 2*ptrSize + 41 2*(2*ptrSize) + ptrSize 42 yoloSize := unsafe.Sizeof(yolo{}) 43 interfaceSize := 2 * ptrSize 44 topHashSize := uintptr(8) 45 tests := map[string]struct { 46 getStruct func() interface{} 47 expectedSize uintptr 48 }{ 49 "bool": { 50 getStruct: func() interface{} { 51 var test bool 52 return &test 53 }, 54 expectedSize: 1, 55 }, 56 "int8": { 57 getStruct: func() interface{} { 58 test := int8(4) 59 return &test 60 }, 61 expectedSize: 1, 62 }, 63 "int16": { 64 getStruct: func() interface{} { 65 test := int16(4) 66 return &test 67 }, 68 expectedSize: 2, 69 }, 70 "int32": { 71 getStruct: func() interface{} { 72 test := int32(4) 73 return &test 74 }, 75 expectedSize: 4, 76 }, 77 "int64": { 78 getStruct: func() interface{} { 79 test := int64(4) 80 return &test 81 }, 82 expectedSize: 8, 83 }, 84 "uint": { 85 getStruct: func() interface{} { 86 test := uint(4) 87 return &test 88 }, 89 expectedSize: ptrSize, 90 }, 91 "uint8": { 92 getStruct: func() interface{} { 93 test := uint8(4) 94 return &test 95 }, 96 expectedSize: 1, 97 }, 98 "uint16": { 99 getStruct: func() interface{} { 100 test := uint16(4) 101 return &test 102 }, 103 expectedSize: 2, 104 }, 105 "uint32": { 106 getStruct: func() interface{} { 107 test := uint32(4) 108 return &test 109 }, 110 expectedSize: 4, 111 }, 112 "uint64": { 113 getStruct: func() interface{} { 114 test := uint64(4) 115 return &test 116 }, 117 expectedSize: 8, 118 }, 119 "uintptr": { 120 getStruct: func() interface{} { 121 test := uintptr(4) 122 return &test 123 }, 124 expectedSize: ptrSize, 125 }, 126 "float32": { 127 getStruct: func() interface{} { 128 test := float32(4) 129 return &test 130 }, 131 expectedSize: 4, 132 }, 133 "float64": { 134 getStruct: func() interface{} { 135 test := float64(4) 136 return &test 137 }, 138 expectedSize: 8, 139 }, 140 "complex64": { 141 getStruct: func() interface{} { 142 test := complex64(4 + 1i) 143 return &test 144 }, 145 expectedSize: 8, 146 }, 147 "complex128": { 148 getStruct: func() interface{} { 149 test := complex128(4 + 1i) 150 return &test 151 }, 152 expectedSize: 16, 153 }, 154 "string": { 155 getStruct: func() interface{} { 156 test := "Hello Dolly!" 157 return &test 158 }, 159 expectedSize: strHdrSize + 12, 160 }, 161 "unsafe_Pointer": { 162 getStruct: func() interface{} { 163 tmp := uint64(54) 164 var test unsafe.Pointer 165 test = unsafe.Pointer(&tmp) 166 return &test 167 }, 168 expectedSize: ptrSize, 169 }, "rune": { 170 getStruct: func() interface{} { 171 test := rune('A') 172 return &test 173 }, 174 expectedSize: 4, 175 }, "intPtr": { 176 getStruct: func() interface{} { 177 test := int(4) 178 return &test 179 }, 180 expectedSize: ptrSize, 181 }, "FuncPtr": { 182 getStruct: func() interface{} { 183 test := TestDeepSizeof 184 return &test 185 }, 186 expectedSize: ptrSize, 187 }, "struct_1": { 188 getStruct: func() interface{} { 189 v := struct { 190 a uint32 191 b *uint32 192 c struct { 193 e [8]byte 194 d string 195 } 196 f string 197 }{ 198 a: 10, 199 c: struct { 200 e [8]byte 201 d string 202 }{ 203 e: [8]byte{0, 1, 2, 3, 4, 5, 6, 7}, 204 d: "Hello Test!", 205 }, 206 f: "Hello Test!", 207 } 208 a := uint32(47) 209 v.b = &a 210 return &v 211 }, 212 expectedSize: 4 + alignement + ptrSize + 8 + strHdrSize*2 + 213 11 /* "Hello Test!" */ + 4, /* uint32(47) */ 214 }, "struct_2": { 215 getStruct: func() interface{} { 216 v := struct { 217 a []byte 218 b []byte 219 c []byte 220 }{ 221 c: make([]byte, 32, 64), 222 } 223 v.a = v.c[20:32] 224 v.b = v.c[10:20] 225 return &v 226 }, 227 expectedSize: 3*sliceHdrSize + 64, /*slice capacity*/ 228 }, "struct_3": { 229 getStruct: func() interface{} { 230 type test struct { 231 a *byte 232 c []byte 233 } 234 tmp := make([]byte, 64, 128) 235 v := (*test)(unsafe.Pointer(&tmp[16])) 236 v.c = tmp 237 v.a = (*byte)(unsafe.Pointer(&tmp[5])) 238 return v 239 }, 240 // we expect to see 128 bytes as struct test is part of the bytes slice 241 // and field c point to it. 242 expectedSize: 128, 243 }, "map_string_interface": { 244 getStruct: func() interface{} { 245 return &map[string]interface{}{} 246 }, 247 expectedSize: ptrSize + topHashSize + hmapStructSize + 248 (8*(strHdrSize+interfaceSize) + ptrSize), 249 }, "map_interface_interface": { 250 getStruct: func() interface{} { 251 // All the map will use only one bucket because there is less than 8 252 // entries in each map. Also for amd64 and i386 the bucket size is 253 // computed like in function bucketOf in /go/src/reflect/type.go: 254 return &map[interface{}]interface{}{ 255 // 2 + (8 + 4) 386 256 // 2 + (16 + 4) amd64 257 uint16(123): "yolo", 258 // (4 + 8) + (4 + 28 + 140) + (4 for SWAG) 386 259 // (4 + 16) + (8 + 48 + 272) + (4 for SWAG) amd64 260 "meow": map[string]string{"SWAG": "yolo"}, 261 // (12) + (4 + 12) 386 262 // (16) + (8 + 16) amd64 263 yolo{i: 523}: &yolo{i: 126}, 264 // (12) + (12) 386 265 // (16) + (16) amd64 266 fmt.Stringer(yolo{i: 123}): yolo{i: 234}, 267 } 268 }, 269 // Total 270 // 386: (4 + 28 + 140) + 2 + (8 + 4) + (4 + 8) + (4 + 28 + 140) + 4 + (12) + 271 // (4 + 12) + 12 + 12 272 // amd64: (8 + 48 + 272) + 2 + (16 + 4) + (4 + 16) + (8 + 48 + 272) + (4) + 273 // (16) + (8 + 16) + (16) + (16) 274 expectedSize: (ptrSize + topHashSize + hmapStructSize + 275 (8*(2*interfaceSize) + ptrSize)) + 276 (unsafe.Sizeof(uint16(123)) + strHdrSize + 4 /* "yolo" */) + 277 (strHdrSize + 4 /* "meow" */ + 278 (ptrSize + hmapStructSize + topHashSize + 279 (8*(2*strHdrSize) + ptrSize)) /*map[string]string*/ + 280 4 /* "SWAG" */) + 281 (yoloSize /* obj: */ + (ptrSize + yoloSize) /* &obj */) + 282 yoloSize*2, 283 }, "struct_4": { 284 getStruct: func() interface{} { 285 return &struct { 286 a map[interface{}]interface{} 287 c string 288 d []string 289 }{ 290 a: map[interface{}]interface{}{ 291 uint16(123): "yolo", 292 "meow": map[string]string{"SWAG": "yolo"}, 293 yolo{i: 127}: &yolo{i: 124}, 294 fmt.Stringer(yolo{i: 123}): yolo{i: 234}, 295 }, // 4 (386) or 8 (amd64) 296 c: "Hello", // 8 (386) or 16 (amd64) 297 d: []string{"Bonjour", "Hello", "Hola"}, // 12 (386) or 24 (amd64) 298 } 299 }, 300 // Total 301 // 386: sizeof(tmp map) + 8(test.c) + 12(test.d) + 302 // 3 * 8 (strSlice) + 16(len("Bonjour") + len("Hello")...) 303 // amd64: sizeof(tmp map) + 8 (test.b) + 16(test.c) + 24(test.d) + 304 // 3 * 16 (strSlice) + 16(len("Bonjour") + len("Hello")...) 305 expectedSize: (ptrSize + strHdrSize + sliceHdrSize) + (hmapStructSize + 306 topHashSize + (8*(2*2*ptrSize /* interface size */) + ptrSize) + 307 unsafe.Sizeof(uint16(123)) + 308 strHdrSize + 4 /* "yolo" */ + strHdrSize + 4 /* "meow" */ + 309 +(ptrSize + hmapStructSize + topHashSize + (8*(2*strHdrSize) + ptrSize) + 310 4 /* "SWAG" */) + yoloSize /* obj */ + (ptrSize + yoloSize) /* &obj */ + 311 yoloSize*2) + 5 /* "Hello" */ + 312 3*strHdrSize /*strings in strSlice*/ + 11, /* "Bonjour" + "Hola" */ 313 }, "chan_int": { 314 getStruct: func() interface{} { 315 test := make(chan int) 316 return &test 317 }, 318 // The expected size should be equal to the size of the struct hchan 319 // defined in /go/src/runtime/chan.go 320 expectedSize: ptrSize + chanHdrSize, 321 }, "chan_int_16": { 322 getStruct: func() interface{} { 323 test := make(chan int, 16) 324 return &test 325 }, 326 expectedSize: ptrSize + chanHdrSize + 16*ptrSize, 327 }, "chan_yoloPtr_16": { 328 getStruct: func() interface{} { 329 test := make(chan *yolo, 16) 330 for i := 0; i < 16; i++ { 331 tmp := &yolo{ 332 i: int32(i), 333 } 334 tmp.p = unsafe.Pointer(&tmp.i) 335 test <- tmp 336 } 337 return &test 338 }, 339 expectedSize: ptrSize + chanHdrSize + 16*(ptrSize+yoloSize), 340 }, "struct_5": { 341 getStruct: func() interface{} { 342 tmp := make([]byte, 32) 343 test := struct { 344 a []byte 345 b **uint32 346 }{ 347 a: tmp, 348 } 349 bob := uint32(42) 350 ptrInt := (*uintptr)(unsafe.Pointer(&tmp[0])) 351 *ptrInt = uintptr(unsafe.Pointer(&bob)) 352 test.b = (**uint32)(unsafe.Pointer(&tmp[0])) 353 return &test 354 }, 355 expectedSize: sliceHdrSize + ptrSize + 32 + 4, 356 }, "struct_6": { 357 getStruct: func() interface{} { 358 type A struct { 359 a uintptr 360 b *yolo 361 } 362 type B struct { 363 a *A 364 b uintptr 365 } 366 tmp := make([]byte, 32) 367 test := struct { 368 a []byte 369 b *B 370 }{ 371 a: tmp, 372 } 373 y := yolo{i: 42} 374 test.b = (*B)(unsafe.Pointer(&tmp[0])) 375 test.b.a = (*A)(unsafe.Pointer(&tmp[0])) 376 test.b.a.b = &y 377 return &test 378 }, 379 expectedSize: sliceHdrSize + ptrSize + 32 + yoloSize, 380 }, "chan_chan_int_16": { 381 getStruct: func() interface{} { 382 test := make(chan chan int, 16) 383 for i := 0; i < 16; i++ { 384 tmp := make(chan int) 385 test <- tmp 386 } 387 return &test 388 }, 389 expectedSize: ptrSize + chanHdrSize*17 + 16*ptrSize, 390 }, "chan_yolo_16": { 391 getStruct: func() interface{} { 392 test := make(chan yolo, 16) 393 for i := 0; i < 16; i++ { 394 tmp := yolo{ 395 i: int32(i), 396 } 397 test <- tmp 398 } 399 return &test 400 }, 401 expectedSize: ptrSize + chanHdrSize + 16*yoloSize, 402 }, "chan_map_string_interface_16)": { 403 getStruct: func() interface{} { 404 test := make(chan map[string]interface{}, 16) 405 for i := 0; i < 16; i++ { 406 tmp := make(map[string]interface{}) 407 test <- tmp 408 } 409 return &test 410 }, 411 expectedSize: ptrSize + chanHdrSize + 16*(ptrSize+hmapStructSize+ 412 (8*(1+strHdrSize+interfaceSize)+ptrSize)), 413 }, "chan_unsafe_Pointer_16": { 414 getStruct: func() interface{} { 415 test := make(chan unsafe.Pointer, 16) 416 for i := 0; i < 16; i++ { 417 var a int 418 ptrToA := (unsafe.Pointer)(unsafe.Pointer(&a)) 419 test <- ptrToA 420 } 421 return &test 422 }, 423 expectedSize: ptrSize + chanHdrSize + 16*ptrSize, 424 }, "chan_[]int_16": { 425 getStruct: func() interface{} { 426 test := make(chan []int, 16) 427 for i := 0; i < 8; i++ { 428 intSlice := make([]int, 16) 429 test <- intSlice 430 } 431 return &test 432 }, 433 expectedSize: ptrSize + chanHdrSize + 16*sliceHdrSize + 8*16*ptrSize, 434 }, "chan_func": { 435 getStruct: func() interface{} { 436 test := make(chan func(), 16) 437 f := func() { 438 fmt.Printf("Hello!") 439 } 440 for i := 0; i < 8; i++ { 441 test <- f 442 } 443 return &test 444 }, 445 expectedSize: ptrSize + chanHdrSize + 16*ptrSize, 446 }, 447 } 448 449 for key, tcase := range tests { 450 t.Run(key, func(t *testing.T) { 451 v := tcase.getStruct() 452 m, err := DeepSizeof(v) 453 if err != nil { 454 t.Fatal(err) 455 } 456 var totalSize uintptr 457 for _, size := range m { 458 totalSize += size 459 } 460 expectedSize := tcase.expectedSize 461 if totalSize != expectedSize { 462 t.Fatalf("Expected size: %v, but got %v", expectedSize, totalSize) 463 } 464 }) 465 } 466 } 467 468 func TestUpdateSeenAreas(t *testing.T) { 469 tests := []struct { 470 seen []block 471 expectedSeen []block 472 expectedSize uintptr 473 update block 474 }{{ 475 seen: []block{ 476 {start: 0x100000, end: 0x100050}, 477 }, 478 expectedSeen: []block{ 479 {start: 0x100000, end: 0x100050}, 480 {start: 0x100100, end: 0x100150}, 481 }, 482 expectedSize: 0x50, 483 update: block{start: 0x100100, end: 0x100150}, 484 }, { 485 seen: []block{ 486 {start: 0x100000, end: 0x100050}, 487 }, 488 expectedSeen: []block{ 489 {start: 0x100, end: 0x150}, 490 {start: 0x100000, end: 0x100050}, 491 }, 492 expectedSize: 0x50, 493 update: block{start: 0x100, end: 0x150}, 494 }, { 495 seen: []block{ 496 {start: 0x100000, end: 0x100500}, 497 }, 498 expectedSeen: []block{ 499 {start: 0x100000, end: 0x100750}, 500 }, 501 expectedSize: 0x250, 502 update: block{start: 0x100250, end: 0x100750}, 503 }, { 504 seen: []block{ 505 {start: 0x100250, end: 0x100750}, 506 }, 507 expectedSeen: []block{ 508 {start: 0x100000, end: 0x100750}, 509 }, 510 expectedSize: 0x250, 511 update: block{start: 0x100000, end: 0x100500}, 512 }, { 513 seen: []block{ 514 {start: 0x1000, end: 0x1250}, 515 {start: 0x1500, end: 0x1750}, 516 }, 517 expectedSeen: []block{ 518 {start: 0x1000, end: 0x1750}, 519 }, 520 expectedSize: 0x2B0, 521 update: block{start: 0x1200, end: 0x1700}, 522 }, { 523 seen: []block{ 524 {start: 0x1000, end: 0x1250}, 525 {start: 0x1500, end: 0x1750}, 526 {start: 0x1F50, end: 0x21A0}, 527 }, 528 expectedSeen: []block{ 529 {start: 0xF00, end: 0x1F00}, 530 {start: 0x1F50, end: 0x21A0}, 531 }, 532 expectedSize: 0xB60, 533 update: block{start: 0xF00, end: 0x1F00}, 534 }, { 535 seen: []block{ 536 {start: 0x1000, end: 0x1250}, 537 {start: 0x1500, end: 0x1750}, 538 {start: 0x1F00, end: 0x2150}, 539 }, 540 expectedSeen: []block{ 541 {start: 0xF00, end: 0x2150}, 542 }, 543 expectedSize: 0xB60, 544 update: block{start: 0xF00, end: 0x1F00}, 545 }, { 546 seen: []block{ 547 {start: 0x1000, end: 0x1250}, 548 {start: 0x1500, end: 0x1750}, 549 {start: 0x1F00, end: 0x2150}, 550 }, 551 expectedSeen: []block{ 552 {start: 0x1000, end: 0x1750}, 553 {start: 0x1F00, end: 0x2150}, 554 }, 555 expectedSize: 0x2B0, 556 update: block{start: 0x1250, end: 0x1500}, 557 }} 558 559 for i, tcase := range tests { 560 t.Run(strconv.Itoa(i), func(t *testing.T) { 561 seen, size := updateSeenBlocks(tcase.update, tcase.seen) 562 if !test.DeepEqual(seen, tcase.expectedSeen) { 563 t.Fatalf("seen blocks %x for iterration %v are different than the "+ 564 "one expected:\n %x", seen, i, tcase.expectedSeen) 565 } 566 if size != tcase.expectedSize { 567 t.Fatalf("Size does not match, expected 0x%x got 0x%x", 568 tcase.expectedSize, size) 569 } 570 }) 571 } 572 }