github.com/cilium/cilium@v1.16.2/pkg/hubble/container/ring_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Hubble 3 4 package container 5 6 import ( 7 "container/list" 8 "container/ring" 9 "context" 10 "errors" 11 "fmt" 12 "io" 13 "reflect" 14 "sync" 15 "testing" 16 17 "github.com/google/go-cmp/cmp" 18 "github.com/google/go-cmp/cmp/cmpopts" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 "go.uber.org/goleak" 22 "google.golang.org/protobuf/types/known/timestamppb" 23 24 flowpb "github.com/cilium/cilium/api/v1/flow" 25 v1 "github.com/cilium/cilium/pkg/hubble/api/v1" 26 ) 27 28 func BenchmarkRingWrite(b *testing.B) { 29 entry := &v1.Event{} 30 s := NewRing(capacity(b.N)) 31 b.ReportAllocs() 32 b.ResetTimer() 33 for i := 0; i < b.N; i++ { 34 s.Write(entry) 35 } 36 } 37 38 func BenchmarkRingRead(b *testing.B) { 39 entry := &v1.Event{} 40 s := NewRing(capacity(b.N)) 41 a := make([]*v1.Event, b.N) 42 b.ReportAllocs() 43 for i := 0; i < b.N; i++ { 44 s.Write(entry) 45 } 46 b.ResetTimer() 47 lastWriteIdx := s.LastWriteParallel() 48 for i := 0; i < b.N; i++ { 49 a[i], _ = s.read(lastWriteIdx) 50 lastWriteIdx-- 51 } 52 } 53 54 func BenchmarkTimeLibListRead(b *testing.B) { 55 entry := &v1.Event{} 56 s := list.New() 57 a := make([]*v1.Event, b.N) 58 i := 0 59 b.ReportAllocs() 60 for i := 0; i < b.N; i++ { 61 s.PushFront(entry) 62 } 63 b.ResetTimer() 64 for e := s.Front(); e != nil; e = e.Next() { 65 a[i], _ = e.Value.(*v1.Event) 66 } 67 } 68 69 func BenchmarkTimeLibRingRead(b *testing.B) { 70 entry := &v1.Event{} 71 s := ring.New(b.N) 72 a := make([]*v1.Event, b.N) 73 i := 0 74 b.ReportAllocs() 75 b.ResetTimer() 76 for i := 0; i < b.N; i++ { 77 s.Value = entry 78 s.Next() 79 } 80 s.Do(func(e interface{}) { 81 a[i], _ = e.(*v1.Event) 82 i++ 83 }) 84 } 85 86 func TestNewCapacity(t *testing.T) { 87 // check all valid values according to the doc string 88 // ie: value of n MUST satisfy n=2^i -1 for i = [1, 16] 89 for i := 1; i <= 16; i++ { 90 n := (1 << i) - 1 91 t.Run(fmt.Sprintf("n=%d", n), func(t *testing.T) { 92 c, err := NewCapacity(n) 93 assert.NoError(t, err) 94 assert.Equal(t, capacity(n), c) 95 }) 96 } 97 // validate CapacityN constants 98 capacityN := []capacity{ 99 Capacity1, 100 Capacity3, 101 Capacity7, 102 Capacity15, 103 Capacity31, 104 Capacity63, 105 Capacity127, 106 Capacity255, 107 Capacity511, 108 Capacity1023, 109 Capacity2047, 110 Capacity4095, 111 Capacity8191, 112 Capacity16383, 113 Capacity32767, 114 Capacity65535, 115 } 116 for _, n := range capacityN { 117 t.Run(fmt.Sprintf("n=Capacity%d", n.AsInt()), func(t *testing.T) { 118 c, err := NewCapacity(n.AsInt()) 119 assert.NoError(t, err) 120 assert.Equal(t, n, c) 121 }) 122 } 123 124 // test invalid values 125 for _, n := range []int{-127, -10, 0, 2, 128, 131071} { 126 t.Run(fmt.Sprintf("n=%d", n), func(t *testing.T) { 127 c, err := NewCapacity(n) 128 assert.Nil(t, c) 129 assert.NotNil(t, err) 130 }) 131 } 132 } 133 134 func TestNewRing(t *testing.T) { 135 for i := 1; i <= 16; i++ { 136 n := (1 << i) - 1 137 t.Run(fmt.Sprintf("n=%d", n), func(t *testing.T) { 138 r := NewRing(capacity(n)) 139 require.NotNil(t, r) 140 assert.Equal(t, uint64(0), r.Len()) 141 assert.Equal(t, uint64(n), r.Cap()) 142 // fill half the buffer 143 for j := 0; j < n/2; j++ { 144 r.Write(&v1.Event{}) 145 } 146 assert.Equal(t, uint64(n/2), r.Len()) 147 assert.Equal(t, uint64(n), r.Cap()) 148 // fill the buffer to max capacity 149 for j := 0; j <= n/2; j++ { 150 r.Write(&v1.Event{}) 151 } 152 assert.Equal(t, uint64(n), r.Len()) 153 assert.Equal(t, uint64(n), r.Cap()) 154 // write more events 155 for j := 0; j < n; j++ { 156 r.Write(&v1.Event{}) 157 } 158 assert.Equal(t, uint64(n), r.Len()) 159 assert.Equal(t, uint64(n), r.Cap()) 160 }) 161 } 162 } 163 164 func TestRing_Read(t *testing.T) { 165 type fields struct { 166 mask uint64 167 cycleExp uint8 168 data []*v1.Event 169 write uint64 170 } 171 type args struct { 172 read uint64 173 } 174 tests := []struct { 175 name string 176 fields fields 177 args args 178 want *v1.Event 179 wantErr error 180 }{ 181 { 182 name: "normal read for the index 7", 183 fields: fields{ 184 mask: 0x7, 185 cycleExp: 0x3, // 7+1=8=2^3 186 data: []*v1.Event{ 187 0x0: {Timestamp: ×tamppb.Timestamp{Seconds: 0}}, 188 0x1: {Timestamp: ×tamppb.Timestamp{Seconds: 1}}, 189 0x2: {Timestamp: ×tamppb.Timestamp{Seconds: 2}}, 190 0x3: {Timestamp: ×tamppb.Timestamp{Seconds: 3}}, 191 0x4: {Timestamp: ×tamppb.Timestamp{Seconds: 4}}, 192 0x5: {Timestamp: ×tamppb.Timestamp{Seconds: 5}}, 193 0x6: {Timestamp: ×tamppb.Timestamp{Seconds: 6}}, 194 0x7: {Timestamp: ×tamppb.Timestamp{Seconds: 7}}, 195 }, 196 // next to be written: 0x9 (idx: 1), last written: 0x8 (idx: 0) 197 write: 0x9, 198 }, 199 args: args{ 200 read: 0x7, 201 }, 202 want: &v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 7}}, 203 wantErr: nil, 204 }, 205 { 206 name: "we can't read index 0 since we just wrote into it", 207 fields: fields{ 208 mask: 0x7, 209 cycleExp: 0x3, // 7+1=8=2^3 210 data: []*v1.Event{ 211 0x0: {Timestamp: ×tamppb.Timestamp{Seconds: 0}}, 212 0x1: {Timestamp: ×tamppb.Timestamp{Seconds: 1}}, 213 0x2: {Timestamp: ×tamppb.Timestamp{Seconds: 2}}, 214 0x3: {Timestamp: ×tamppb.Timestamp{Seconds: 3}}, 215 0x4: {Timestamp: ×tamppb.Timestamp{Seconds: 4}}, 216 0x5: {Timestamp: ×tamppb.Timestamp{Seconds: 5}}, 217 0x6: {Timestamp: ×tamppb.Timestamp{Seconds: 6}}, 218 0x7: {Timestamp: ×tamppb.Timestamp{Seconds: 7}}, 219 }, 220 // next to be written: 0x9 (idx: 2), last written: 0x8 (idx: 0) 221 write: 0x9, 222 }, 223 args: args{ 224 read: 0x0, 225 }, 226 want: &v1.Event{ 227 Event: &flowpb.LostEvent{ 228 Source: flowpb.LostEventSource_HUBBLE_RING_BUFFER, 229 NumEventsLost: 1, 230 Cpu: nil, 231 }, 232 }, 233 wantErr: nil, 234 }, 235 { 236 name: "we can't read index 0x7 since we are one writing cycle ahead", 237 fields: fields{ 238 mask: 0x7, 239 cycleExp: 0x3, // 7+1=8=2^3 240 data: []*v1.Event{ 241 0x0: {Timestamp: ×tamppb.Timestamp{Seconds: 0}}, 242 0x1: {Timestamp: ×tamppb.Timestamp{Seconds: 1}}, 243 0x2: {Timestamp: ×tamppb.Timestamp{Seconds: 2}}, 244 0x3: {Timestamp: ×tamppb.Timestamp{Seconds: 3}}, 245 0x4: {Timestamp: ×tamppb.Timestamp{Seconds: 4}}, 246 0x5: {Timestamp: ×tamppb.Timestamp{Seconds: 5}}, 247 0x6: {Timestamp: ×tamppb.Timestamp{Seconds: 6}}, 248 0x7: {Timestamp: ×tamppb.Timestamp{Seconds: 7}}, 249 }, 250 // next to be written: 0x10 (idx: 0), last written: 0x0f (idx: 7) 251 write: 0x10, 252 }, 253 args: args{ 254 // The next possible entry that we can read is 0x10-0x7-0x1 = 0x8 (idx: 0) 255 read: 0x7, 256 }, 257 want: &v1.Event{ 258 Event: &flowpb.LostEvent{ 259 Source: flowpb.LostEventSource_HUBBLE_RING_BUFFER, 260 NumEventsLost: 1, 261 Cpu: nil, 262 }, 263 }, 264 wantErr: nil, 265 }, 266 { 267 name: "we can read index 0x8 since it's the last entry that we can read in this cycle", 268 fields: fields{ 269 mask: 0x7, 270 cycleExp: 0x3, // 7+1=8=2^3 271 data: []*v1.Event{ 272 0x0: {Timestamp: ×tamppb.Timestamp{Seconds: 0}}, 273 0x1: {Timestamp: ×tamppb.Timestamp{Seconds: 1}}, 274 0x2: {Timestamp: ×tamppb.Timestamp{Seconds: 2}}, 275 0x3: {Timestamp: ×tamppb.Timestamp{Seconds: 3}}, 276 0x4: {Timestamp: ×tamppb.Timestamp{Seconds: 4}}, 277 0x5: {Timestamp: ×tamppb.Timestamp{Seconds: 5}}, 278 0x6: {Timestamp: ×tamppb.Timestamp{Seconds: 6}}, 279 0x7: {Timestamp: ×tamppb.Timestamp{Seconds: 7}}, 280 }, 281 // next to be written: 0x10 (idx: 0), last written: 0x0f (idx: 7) 282 write: 0x10, 283 }, 284 args: args{ 285 // The next possible entry that we can read is 0x10-0x7-0x1 = 0x8 (idx: 0) 286 read: 0x8, 287 }, 288 want: &v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 0}}, 289 wantErr: nil, 290 }, 291 { 292 name: "we overflow write and we are trying to read the previous writes, that we can't", 293 fields: fields{ 294 mask: 0x7, 295 cycleExp: 0x3, // 7+1=8=2^3 296 data: []*v1.Event{ 297 0x0: {Timestamp: ×tamppb.Timestamp{Seconds: 0}}, 298 0x1: {Timestamp: ×tamppb.Timestamp{Seconds: 1}}, 299 0x2: {Timestamp: ×tamppb.Timestamp{Seconds: 2}}, 300 0x3: {Timestamp: ×tamppb.Timestamp{Seconds: 3}}, 301 0x4: {Timestamp: ×tamppb.Timestamp{Seconds: 4}}, 302 0x5: {Timestamp: ×tamppb.Timestamp{Seconds: 5}}, 303 0x6: {Timestamp: ×tamppb.Timestamp{Seconds: 6}}, 304 0x7: {Timestamp: ×tamppb.Timestamp{Seconds: 7}}, 305 }, 306 // next to be written: 0x0 (idx: 0), last written: 0xffffffffffffffff (idx: 7) 307 write: 0x0, 308 }, 309 args: args{ 310 // We can't read this index because we might be still writing into it 311 // next to be read: ^uint64(0) (idx: 7), last read: 0xfffffffffffffffe (idx: 6) 312 read: ^uint64(0), 313 }, 314 want: &v1.Event{ 315 Event: &flowpb.LostEvent{ 316 Source: flowpb.LostEventSource_HUBBLE_RING_BUFFER, 317 NumEventsLost: 1, 318 Cpu: nil, 319 }, 320 }, 321 wantErr: nil, 322 }, 323 { 324 name: "we overflow write and we are trying to read the previous writes, that we can", 325 fields: fields{ 326 mask: 0x7, 327 cycleExp: 0x3, // 7+1=8=2^3 328 data: []*v1.Event{ 329 0x0: {Timestamp: ×tamppb.Timestamp{Seconds: 0}}, 330 0x1: {Timestamp: ×tamppb.Timestamp{Seconds: 1}}, 331 0x2: {Timestamp: ×tamppb.Timestamp{Seconds: 2}}, 332 0x3: {Timestamp: ×tamppb.Timestamp{Seconds: 3}}, 333 0x4: {Timestamp: ×tamppb.Timestamp{Seconds: 4}}, 334 0x5: {Timestamp: ×tamppb.Timestamp{Seconds: 5}}, 335 0x6: {Timestamp: ×tamppb.Timestamp{Seconds: 6}}, 336 0x7: {Timestamp: ×tamppb.Timestamp{Seconds: 7}}, 337 }, 338 // next to be written: 0x1 (idx: 1), last written: 0x0 (idx: 0) 339 write: 0x1, 340 }, 341 args: args{ 342 // next to be read: ^uint64(0) (idx: 7), last read: 0xfffffffffffffffe (idx: 6) 343 read: ^uint64(0), 344 }, 345 want: &v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 7}}, 346 wantErr: nil, 347 }, 348 { 349 name: "we overflow write and we are trying to read the 2 previously cycles", 350 fields: fields{ 351 mask: 0x7, 352 cycleExp: 0x3, // 7+1=8=2^3 353 data: []*v1.Event{ 354 0x0: {Timestamp: ×tamppb.Timestamp{Seconds: 0}}, 355 0x1: {Timestamp: ×tamppb.Timestamp{Seconds: 1}}, 356 0x2: {Timestamp: ×tamppb.Timestamp{Seconds: 2}}, 357 0x3: {Timestamp: ×tamppb.Timestamp{Seconds: 3}}, 358 0x4: {Timestamp: ×tamppb.Timestamp{Seconds: 4}}, 359 0x5: {Timestamp: ×tamppb.Timestamp{Seconds: 5}}, 360 0x6: {Timestamp: ×tamppb.Timestamp{Seconds: 6}}, 361 0x7: {Timestamp: ×tamppb.Timestamp{Seconds: 7}}, 362 }, 363 // next to be written: 0x8 (idx: 1), last written: 0xffffffffffffffff (idx: 7) 364 write: 0x8, 365 }, 366 args: args{ 367 // next to be read: ^uint64(0)-0x7 (idx: 0), last read: 0xfffffffffffffff7 (idx: 7) 368 // read is: ^uint64(0)-0x7 which should represent index 0x0 but 369 // with a cycle that was already overwritten 370 read: ^uint64(0) - 0x7, 371 }, 372 want: &v1.Event{ 373 Event: &flowpb.LostEvent{ 374 Source: flowpb.LostEventSource_HUBBLE_RING_BUFFER, 375 NumEventsLost: 1, 376 Cpu: nil, 377 }, 378 }, 379 wantErr: nil, 380 }, 381 } 382 for _, tt := range tests { 383 t.Run(tt.name, func(t *testing.T) { 384 r := &Ring{ 385 mask: tt.fields.mask, 386 data: tt.fields.data, 387 dataLen: uint64(len(tt.fields.data)), 388 cycleExp: tt.fields.cycleExp, 389 cycleMask: ^uint64(0) >> tt.fields.cycleExp, 390 } 391 r.write.Store(tt.fields.write) 392 got, got1 := r.read(tt.args.read) 393 if tt.want.GetLostEvent() != nil { 394 assert.NotNil(t, got) 395 assert.NotNil(t, got.GetLostEvent()) 396 if diff := cmp.Diff(tt.want.GetLostEvent(), got.GetLostEvent(), cmpopts.IgnoreUnexported(flowpb.LostEvent{})); diff != "" { 397 t.Errorf("LostEvent mismatch (-want +got):\n%s", diff) 398 } 399 } else { 400 if !reflect.DeepEqual(got, tt.want) { 401 t.Errorf("Ring.read() got = %v, want %v", got, tt.want) 402 } 403 } 404 if !errors.Is(got1, tt.wantErr) { 405 t.Errorf("Ring.read() got1 = %v, want %v", got1, tt.wantErr) 406 } 407 }) 408 } 409 } 410 411 func TestRing_Write(t *testing.T) { 412 type fields struct { 413 len uint64 414 data []*v1.Event 415 write uint64 416 } 417 type args struct { 418 event *v1.Event 419 } 420 tests := []struct { 421 name string 422 fields fields 423 want fields 424 args args 425 }{ 426 { 427 name: "normal write", 428 args: args{ 429 event: &v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 5}}, 430 }, 431 fields: fields{ 432 len: 0x3, 433 write: 0, 434 data: []*v1.Event{ 435 0x0: {Timestamp: ×tamppb.Timestamp{Seconds: 0}}, 436 0x1: {Timestamp: ×tamppb.Timestamp{Seconds: 1}}, 437 0x2: {Timestamp: ×tamppb.Timestamp{Seconds: 2}}, 438 0x3: {Timestamp: ×tamppb.Timestamp{Seconds: 3}}, 439 }, 440 }, 441 want: fields{ 442 len: 0x3, 443 write: 1, 444 data: []*v1.Event{ 445 {Timestamp: ×tamppb.Timestamp{Seconds: 5}}, 446 {Timestamp: ×tamppb.Timestamp{Seconds: 1}}, 447 {Timestamp: ×tamppb.Timestamp{Seconds: 2}}, 448 {Timestamp: ×tamppb.Timestamp{Seconds: 3}}, 449 }, 450 }, 451 }, 452 { 453 name: "overflow write", 454 args: args{ 455 event: &v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 5}}, 456 }, 457 fields: fields{ 458 len: 0x3, 459 write: ^uint64(0), 460 data: []*v1.Event{ 461 {Timestamp: ×tamppb.Timestamp{Seconds: 0}}, 462 {Timestamp: ×tamppb.Timestamp{Seconds: 1}}, 463 {Timestamp: ×tamppb.Timestamp{Seconds: 2}}, 464 {Timestamp: ×tamppb.Timestamp{Seconds: 3}}, 465 }, 466 }, 467 want: fields{ 468 len: 0x3, 469 write: 0, 470 data: []*v1.Event{ 471 {Timestamp: ×tamppb.Timestamp{Seconds: 0}}, 472 {Timestamp: ×tamppb.Timestamp{Seconds: 1}}, 473 {Timestamp: ×tamppb.Timestamp{Seconds: 2}}, 474 {Timestamp: ×tamppb.Timestamp{Seconds: 5}}, 475 }, 476 }, 477 }, 478 } 479 for _, tt := range tests { 480 t.Run(tt.name, func(t *testing.T) { 481 r := &Ring{ 482 mask: tt.fields.len, 483 data: tt.fields.data, 484 } 485 r.write.Store(tt.fields.write) 486 r.Write(tt.args.event) 487 want := &Ring{ 488 mask: tt.want.len, 489 data: tt.want.data, 490 } 491 want.write.Store(tt.want.write) 492 reflect.DeepEqual(want, r) 493 }) 494 } 495 } 496 497 func TestRing_LastWriteParallel(t *testing.T) { 498 type fields struct { 499 len uint64 500 data []*v1.Event 501 write uint64 502 } 503 tests := []struct { 504 name string 505 fields fields 506 want uint64 507 }{ 508 { 509 fields: fields{ 510 len: 0x3, 511 write: 2, 512 data: []*v1.Event{}, 513 }, 514 want: 0, 515 }, 516 { 517 fields: fields{ 518 len: 0x3, 519 write: 1, 520 data: []*v1.Event{}, 521 }, 522 want: ^uint64(0), 523 }, 524 } 525 for _, tt := range tests { 526 t.Run(tt.name, func(t *testing.T) { 527 r := &Ring{ 528 mask: tt.fields.len, 529 data: tt.fields.data, 530 } 531 r.write.Store(tt.fields.write) 532 if got := r.LastWriteParallel(); got != tt.want { 533 t.Errorf("Ring.LastWriteParallel() = %v, want %v", got, tt.want) 534 } 535 }) 536 } 537 } 538 539 func TestRing_LastWrite(t *testing.T) { 540 type fields struct { 541 len uint64 542 data []*v1.Event 543 write uint64 544 } 545 tests := []struct { 546 name string 547 fields fields 548 want uint64 549 }{ 550 { 551 fields: fields{ 552 len: 0x3, 553 write: 1, 554 data: []*v1.Event{}, 555 }, 556 want: 0, 557 }, 558 { 559 fields: fields{ 560 len: 0x3, 561 write: 0, 562 data: []*v1.Event{}, 563 }, 564 want: ^uint64(0), 565 }, 566 } 567 for _, tt := range tests { 568 t.Run(tt.name, func(t *testing.T) { 569 r := &Ring{ 570 mask: tt.fields.len, 571 data: tt.fields.data, 572 } 573 r.write.Store(tt.fields.write) 574 if got := r.LastWrite(); got != tt.want { 575 t.Errorf("Ring.LastWrite() = %v, want %v", got, tt.want) 576 } 577 }) 578 } 579 } 580 func TestRingOldestWrite(t *testing.T) { 581 r := NewRing(Capacity3) 582 583 oldestWrite := r.OldestWrite() 584 entry, err := r.read(oldestWrite) 585 if err != nil { 586 t.Errorf("unexpected error: %s", err) 587 } 588 if entry.GetLostEvent() == nil { 589 t.Errorf("able to read oldest entry without prior write: %s", entry) 590 } 591 592 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 0}}) 593 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 1}}) 594 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 2}}) 595 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 3}}) 596 597 oldestWrite = r.OldestWrite() 598 entry, err = r.read(oldestWrite) 599 if err != nil { 600 t.Errorf("Should be able to read position %x, got %v", oldestWrite, err) 601 } 602 if entry.Timestamp.Seconds != int64(0) { 603 t.Errorf("Read Event should be %+v, got %+v instead (%s)", ×tamppb.Timestamp{Seconds: 0}, entry.Timestamp, entry) 604 } 605 606 // this will trigger a wrap-around and overwrite entry 0 607 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 4}}) 608 609 // the previous oldest entry should now be overwritten 610 entry, err = r.read(oldestWrite) 611 if err != nil { 612 t.Errorf("unexpected error: %s", err) 613 } 614 if entry.GetLostEvent() == nil { 615 t.Errorf("able to read oldest entry even though it has been overwritten: %s", entry) 616 } 617 618 // new oldest entry should have timestamp 1 619 oldestWrite = r.OldestWrite() 620 entry, err = r.read(oldestWrite) 621 if err != nil { 622 t.Errorf("Should be able to read position %x, got %v", oldestWrite, err) 623 } 624 if entry.Timestamp.Seconds != int64(1) { 625 t.Errorf("Read Event should be %+v, got %+v instead (%s)", ×tamppb.Timestamp{Seconds: 1}, entry.Timestamp, entry) 626 } 627 } 628 629 func TestRingFunctionalityInParallel(t *testing.T) { 630 r := NewRing(Capacity15) 631 if len(r.data) != 0x10 { 632 t.Errorf("r.data should have a length of 0x10. Got %x", len(r.data)) 633 } 634 if r.mask != 0xf { 635 t.Errorf("r.mask should be 0xf. Got %x", r.mask) 636 } 637 if r.cycleExp != 4 { 638 t.Errorf("r.cycleExp should be 4. Got %x", r.cycleExp) 639 } 640 if r.cycleMask != 0xfffffffffffffff { 641 t.Errorf("r.cycleMask should be 0xfffffffffffffff. Got %x", r.cycleMask) 642 } 643 lastWrite := r.LastWriteParallel() 644 if lastWrite != ^uint64(0)-1 { 645 t.Errorf("lastWrite should be %x. Got %x", ^uint64(0)-1, lastWrite) 646 } 647 648 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 0}}) 649 lastWrite = r.LastWriteParallel() 650 if lastWrite != ^uint64(0) { 651 t.Errorf("lastWrite should be %x. Got %x", ^uint64(0), lastWrite) 652 } 653 654 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 1}}) 655 lastWrite = r.LastWriteParallel() 656 if lastWrite != 0x0 { 657 t.Errorf("lastWrite should be 0x0. Got %x", lastWrite) 658 } 659 660 entry, err := r.read(lastWrite) 661 if err != nil { 662 t.Errorf("Should be able to read position %x, got %v", lastWrite, err) 663 } 664 if entry.Timestamp.Seconds != int64(0) { 665 t.Errorf("Read Event should be %+v, got %+v instead", ×tamppb.Timestamp{Seconds: 0}, entry.Timestamp) 666 } 667 lastWrite-- 668 lost, _ := r.read(lastWrite) 669 if lost.GetLostEvent() == nil { 670 t.Errorf("Should not be able to read position %x, got %v", lastWrite, lost) 671 } 672 } 673 674 func TestRingFunctionalitySerialized(t *testing.T) { 675 r := NewRing(Capacity15) 676 if len(r.data) != 0x10 { 677 t.Errorf("r.data should have a length of 0x10. Got %x", len(r.data)) 678 } 679 if r.mask != 0xf { 680 t.Errorf("r.mask should be 0xf. Got %x", r.mask) 681 } 682 lastWrite := r.LastWrite() 683 if lastWrite != ^uint64(0) { 684 t.Errorf("lastWrite should be %x. Got %x", ^uint64(0)-1, lastWrite) 685 } 686 687 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 0}}) 688 lastWrite = r.LastWrite() 689 if lastWrite != 0x0 { 690 t.Errorf("lastWrite should be %x. Got %x", 0x0, lastWrite) 691 } 692 693 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: 1}}) 694 lastWrite = r.LastWrite() 695 if lastWrite != 0x1 { 696 t.Errorf("lastWrite should be 0x1. Got %x", lastWrite) 697 } 698 699 _, err := r.read(lastWrite) 700 if !errors.Is(err, io.EOF) { 701 t.Errorf("Should not be able to read position %x, got %v", lastWrite, err) 702 } 703 lastWrite-- 704 entry, err := r.read(lastWrite) 705 if err != nil { 706 t.Errorf("Should be able to read position %x, got %v", lastWrite, err) 707 } 708 if entry.Timestamp.Seconds != int64(0) { 709 t.Errorf("Read Event should be %+v, got %+v instead", ×tamppb.Timestamp{Seconds: 0}, entry.Timestamp) 710 } 711 } 712 713 func TestRing_ReadFrom_Test_1(t *testing.T) { 714 defer goleak.VerifyNone( 715 t, 716 // ignore goroutines started by the redirect we do from klog to logrus 717 goleak.IgnoreTopFunction("k8s.io/klog.(*loggingT).flushDaemon"), 718 goleak.IgnoreTopFunction("k8s.io/klog/v2.(*loggingT).flushDaemon"), 719 goleak.IgnoreTopFunction("io.(*pipe).read")) 720 r := NewRing(Capacity15) 721 if len(r.data) != 0x10 { 722 t.Errorf("r.data should have a length of 0x10. Got %x", len(r.data)) 723 } 724 if r.dataLen != 0x10 { 725 t.Errorf("r.dataLen should have a length of 0x10. Got %x", r.dataLen) 726 } 727 if r.mask != 0xf { 728 t.Errorf("r.mask should be 0xf. Got %x", r.mask) 729 } 730 lastWrite := r.LastWrite() 731 if lastWrite != ^uint64(0) { 732 t.Errorf("lastWrite should be %x. Got %x", ^uint64(0)-1, lastWrite) 733 } 734 735 // Add 5 events 736 for i := uint64(0); i < 5; i++ { 737 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: int64(i)}}) 738 lastWrite = r.LastWrite() 739 if lastWrite != i { 740 t.Errorf("lastWrite should be %x. Got %x", i, lastWrite) 741 } 742 } 743 744 ctx, cancel := context.WithCancel(context.Background()) 745 ch := make(chan *v1.Event, 30) 746 var wg sync.WaitGroup 747 wg.Add(1) 748 go func() { 749 r.readFrom(ctx, 0, ch) 750 wg.Done() 751 }() 752 i := int64(0) 753 for entry := range ch { 754 require.NotNil(t, entry) 755 want := ×tamppb.Timestamp{Seconds: i} 756 if entry.Timestamp.Seconds != want.Seconds { 757 t.Errorf("Read Event should be %+v, got %+v instead", want, entry.Timestamp) 758 } 759 i++ 760 if i == 4 { 761 break 762 } 763 } 764 // next read must be blocked, since ring was not full 765 select { 766 case entry := <-ch: 767 t.Errorf("Read Event %v received when channel should be empty", entry) 768 default: 769 } 770 cancel() 771 wg.Wait() 772 } 773 774 func TestRing_ReadFrom_Test_2(t *testing.T) { 775 defer goleak.VerifyNone( 776 t, 777 // ignore goroutines started by the redirect we do from klog to logrus 778 goleak.IgnoreTopFunction("k8s.io/klog.(*loggingT).flushDaemon"), 779 goleak.IgnoreTopFunction("k8s.io/klog/v2.(*loggingT).flushDaemon"), 780 goleak.IgnoreTopFunction("io.(*pipe).read")) 781 782 r := NewRing(Capacity15) 783 if len(r.data) != 0x10 { 784 t.Errorf("r.data should have a length of 0x10. Got %x", len(r.data)) 785 } 786 if r.dataLen != 0x10 { 787 t.Errorf("r.dataLen should have a length of 0x10. Got %x", r.dataLen) 788 } 789 if r.mask != 0xf { 790 t.Errorf("r.mask should be 0xf. Got %x", r.mask) 791 } 792 lastWrite := r.LastWrite() 793 if lastWrite != ^uint64(0) { 794 t.Errorf("lastWrite should be %x. Got %x", ^uint64(0)-1, lastWrite) 795 } 796 797 // Add 20 events 798 for i := uint64(0); i < 20; i++ { 799 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: int64(i)}}) 800 lastWrite = r.LastWrite() 801 if lastWrite != i { 802 t.Errorf("lastWrite should be %x. Got %x", i, lastWrite) 803 } 804 } 805 806 ctx, cancel := context.WithCancel(context.Background()) 807 // We should be able to read from a previous 'cycles' and ReadFrom will 808 // be able to catch up with the writer. 809 ch := make(chan *v1.Event, 30) 810 var wg sync.WaitGroup 811 wg.Add(1) 812 go func() { 813 defer wg.Done() 814 r.readFrom(ctx, 1, ch) 815 }() 816 i := int64(1) // ReadFrom 817 for event := range ch { 818 require.NotNil(t, event) 819 // Given the buffer length is 16 and there are no more writes being made, 820 // we will receive 15 non-nil events, after that the channel must stall. 821 // 822 // ReadFrom + +----------------valid read------------+ +position possibly being written 823 // | | | | +next position to be written (r.write) 824 // v V V V V 825 // write: 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 826 // index: 0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9 a b c d e f 827 // cycle: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 828 switch { 829 case i < 4: 830 want := &flowpb.LostEvent{ 831 Source: flowpb.LostEventSource_HUBBLE_RING_BUFFER, 832 NumEventsLost: 1, 833 Cpu: nil, 834 } 835 if diff := cmp.Diff(want, event.GetLostEvent(), cmpopts.IgnoreUnexported(flowpb.LostEvent{})); diff != "" { 836 t.Errorf("LostEvent mismatch (-want +got):\n%s", diff) 837 } 838 default: 839 want := ×tamppb.Timestamp{Seconds: i} 840 if diff := cmp.Diff(want, event.Timestamp, cmpopts.IgnoreUnexported(timestamppb.Timestamp{})); diff != "" { 841 t.Errorf("Event timestamp mismatch (-want +got):\n%s", diff) 842 } 843 } 844 if i == 18 { 845 break 846 } 847 i++ 848 } 849 // next read must be blocked, since reader is waiting for the writer 850 select { 851 case entry := <-ch: 852 t.Errorf("Read Event %v received when channel should be empty", entry) 853 default: 854 } 855 856 // Add 20 more events that we read back immediately 857 for i := uint64(0); i < 20; i++ { 858 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: int64(20 + i)}}) 859 860 want := ×tamppb.Timestamp{Seconds: int64(20 + (i - 1))} 861 entry, ok := <-ch 862 if !ok { 863 t.Errorf("Channel was have been closed, expected %+v", entry) 864 } 865 if entry.Timestamp.Seconds != want.Seconds { 866 t.Errorf("Read Event should be %+v, got %+v instead", want, entry.Timestamp) 867 } 868 } 869 cancel() 870 wg.Wait() 871 } 872 873 func TestRing_ReadFrom_Test_3(t *testing.T) { 874 defer goleak.VerifyNone( 875 t, 876 // ignore goroutines started by the redirect we do from klog to logrus 877 goleak.IgnoreTopFunction("k8s.io/klog.(*loggingT).flushDaemon"), 878 goleak.IgnoreTopFunction("k8s.io/klog/v2.(*loggingT).flushDaemon"), 879 goleak.IgnoreTopFunction("io.(*pipe).read")) 880 r := NewRing(Capacity15) 881 if len(r.data) != 0x10 { 882 t.Errorf("r.data should have a length of 0x10. Got %x", len(r.data)) 883 } 884 if r.dataLen != 0x10 { 885 t.Errorf("r.dataLen should have a length of 0x10. Got %x", r.dataLen) 886 } 887 if r.mask != 0xf { 888 t.Errorf("r.mask should be 0xf. Got %x", r.mask) 889 } 890 lastWrite := r.LastWrite() 891 if lastWrite != ^uint64(0) { 892 t.Errorf("lastWrite should be %x. Got %x", ^uint64(0)-1, lastWrite) 893 } 894 895 // Add 20 events 896 for i := uint64(0); i < 20; i++ { 897 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: int64(i)}}) 898 lastWrite = r.LastWrite() 899 if lastWrite != i { 900 t.Errorf("lastWrite should be %x. Got %x", i, lastWrite) 901 } 902 } 903 904 ctx, cancel := context.WithCancel(context.Background()) 905 // We should be able to read from a previous 'cycles' and ReadFrom will 906 // be able to catch up with the writer. 907 ch := make(chan *v1.Event, 30) 908 var wg sync.WaitGroup 909 wg.Add(1) 910 go func() { 911 r.readFrom(ctx, ^uint64(0)-15, ch) 912 wg.Done() 913 }() 914 i := ^uint64(0) - 15 915 for entry := range ch { 916 require.NotNil(t, entry) 917 // Given the buffer length is 16 and there are no more writes being made, 918 // we will receive 15 non-nil events, after that the channel must stall. 919 // 920 // ReadFrom + +-------------------valid read------------+ +position possibly being written 921 // | | | | +next position to be written (r.write) 922 // v V V V V 923 // write: f0 f1 // 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 924 // index: 0 1 // 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9 a b c d e f 925 // cycle: ff ff // 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 926 switch { 927 case i < 4, i > 18: 928 want := &flowpb.LostEvent{ 929 Source: flowpb.LostEventSource_HUBBLE_RING_BUFFER, 930 NumEventsLost: 1, 931 Cpu: nil, 932 } 933 if diff := cmp.Diff(want, entry.GetLostEvent(), cmpopts.IgnoreUnexported(flowpb.LostEvent{})); diff != "" { 934 t.Errorf("%d LostEvent mismatch (-want +got):\n%s", i, diff) 935 } 936 default: 937 want := ×tamppb.Timestamp{Seconds: int64(i)} 938 if diff := cmp.Diff(want, entry.Timestamp, cmpopts.IgnoreUnexported(timestamppb.Timestamp{})); diff != "" { 939 t.Errorf("Event timestamp mismatch (-want +got):\n%s", diff) 940 } 941 } 942 if i == 18 { 943 break 944 } 945 i++ 946 } 947 // next read must be blocked, since reader is waiting for the writer 948 select { 949 case entry := <-ch: 950 t.Errorf("Read Event %v received when channel should be empty", entry) 951 default: 952 } 953 954 // Add 20 more events that we read back immediately 955 for i := uint64(0); i < 20; i++ { 956 r.Write(&v1.Event{Timestamp: ×tamppb.Timestamp{Seconds: int64(20 + i)}}) 957 958 want := ×tamppb.Timestamp{Seconds: int64(20 + (i - 1))} 959 entry, ok := <-ch 960 if !ok { 961 t.Errorf("Channel was have been closed, expected %+v", entry) 962 } 963 if entry.Timestamp.Seconds != want.Seconds { 964 t.Errorf("Read Event should be %+v, got %+v instead", want, entry.Timestamp) 965 } 966 } 967 cancel() 968 wg.Wait() 969 }