summaryrefslogtreecommitdiff
path: root/internal/syscall_aggregate_consumer_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/syscall_aggregate_consumer_test.go')
-rw-r--r--internal/syscall_aggregate_consumer_test.go280
1 files changed, 280 insertions, 0 deletions
diff --git a/internal/syscall_aggregate_consumer_test.go b/internal/syscall_aggregate_consumer_test.go
index 362dfba..f8ef217 100644
--- a/internal/syscall_aggregate_consumer_test.go
+++ b/internal/syscall_aggregate_consumer_test.go
@@ -3,9 +3,12 @@ package internal
import (
"bytes"
"encoding/binary"
+ "fmt"
"testing"
+ "unsafe"
"ior/internal/flags"
+ "ior/internal/statsengine"
"ior/internal/types"
)
@@ -69,3 +72,280 @@ func TestDecodeRawSyscallAggregateRejectsBadSize(t *testing.T) {
t.Fatal("expected error for short value")
}
}
+
+func TestDecodeRawSyscallAggregatePerCPUSumsActiveCPUs(t *testing.T) {
+ raw := encodeRawAggregates(t,
+ rawSyscallAggregate{
+ Count: 2,
+ Errors: 1,
+ TotalDuration: 30,
+ MinDuration: 10,
+ MaxDuration: 20,
+ Histogram: [8]uint64{1, 0, 1},
+ },
+ rawSyscallAggregate{},
+ rawSyscallAggregate{
+ Count: 3,
+ Errors: 0,
+ TotalDuration: 90,
+ MinDuration: 5,
+ MaxDuration: 50,
+ Histogram: [8]uint64{0, 2, 1},
+ },
+ )
+
+ got, err := decodeRawSyscallAggregatePerCPU(raw)
+ if err != nil {
+ t.Fatalf("decodeRawSyscallAggregatePerCPU error: %v", err)
+ }
+
+ want := rawSyscallAggregate{
+ Count: 5,
+ Errors: 1,
+ TotalDuration: 120,
+ MinDuration: 5,
+ MaxDuration: 50,
+ Histogram: [8]uint64{1, 2, 2},
+ }
+ if got != want {
+ t.Fatalf("per-cpu aggregate = %+v, want %+v", got, want)
+ }
+}
+
+func TestDecodeRawSyscallAggregatePerCPURejectsBadSize(t *testing.T) {
+ raw := encodeRawAggregates(t, rawSyscallAggregate{Count: 1})
+ raw = append(raw, 0)
+
+ if _, err := decodeRawSyscallAggregatePerCPU(raw); err == nil {
+ t.Fatal("expected error for non-stride-aligned value")
+ }
+}
+
+func TestDecodeRawSyscallAggregatePerCPURejectsEmptyValue(t *testing.T) {
+ if _, err := decodeRawSyscallAggregatePerCPU(nil); err == nil {
+ t.Fatal("expected error for empty per-cpu value")
+ }
+}
+
+func TestSyscallAggregateConsumerDrainEmitsDeltas(t *testing.T) {
+ const traceID = uint32(types.SYS_ENTER_FUTEX)
+ fakeMap := newFakeSyscallAggregateMap(traceID, encodeRawAggregates(t,
+ rawSyscallAggregate{
+ Count: 2,
+ Errors: 1,
+ TotalDuration: 30,
+ MinDuration: 10,
+ MaxDuration: 20,
+ Histogram: [8]uint64{1, 0, 1},
+ },
+ rawSyscallAggregate{
+ Count: 3,
+ TotalDuration: 90,
+ MinDuration: 5,
+ MaxDuration: 50,
+ Histogram: [8]uint64{0, 2, 1},
+ },
+ ))
+ consumer := &syscallAggregateConsumer{
+ aggregateMap: fakeMap,
+ last: make(map[types.TraceId]rawSyscallAggregate),
+ }
+
+ rows, err := consumer.Drain()
+ if err != nil {
+ t.Fatalf("first Drain error: %v", err)
+ }
+ assertAggregateRows(t, rows, statsengine.SyscallAggregate{
+ TraceID: types.TraceId(traceID),
+ Count: 5,
+ Errors: 1,
+ TotalLatencyNs: 120,
+ MinLatencyNs: 5,
+ MaxLatencyNs: 50,
+ LatencyHistogramNs: [8]uint64{
+ 1, 2, 2,
+ },
+ })
+
+ fakeMap.values[traceID] = encodeRawAggregates(t,
+ rawSyscallAggregate{
+ Count: 4,
+ Errors: 2,
+ TotalDuration: 80,
+ MinDuration: 4,
+ MaxDuration: 40,
+ Histogram: [8]uint64{2, 1, 1},
+ },
+ rawSyscallAggregate{
+ Count: 3,
+ TotalDuration: 110,
+ MinDuration: 5,
+ MaxDuration: 70,
+ Histogram: [8]uint64{0, 2, 1, 1},
+ },
+ )
+ rows, err = consumer.Drain()
+ if err != nil {
+ t.Fatalf("second Drain error: %v", err)
+ }
+ assertAggregateRows(t, rows, statsengine.SyscallAggregate{
+ TraceID: types.TraceId(traceID),
+ Count: 2,
+ Errors: 1,
+ TotalLatencyNs: 70,
+ MinLatencyNs: 4,
+ MaxLatencyNs: 70,
+ LatencyHistogramNs: [8]uint64{
+ 1, 1, 0, 1,
+ },
+ })
+
+ fakeMap.values[traceID] = encodeRawAggregates(t,
+ rawSyscallAggregate{
+ Count: 5,
+ Errors: 2,
+ TotalDuration: 100,
+ MinDuration: 4,
+ MaxDuration: 40,
+ Histogram: [8]uint64{3, 1, 1},
+ },
+ rawSyscallAggregate{
+ Count: 3,
+ TotalDuration: 110,
+ MinDuration: 5,
+ MaxDuration: 70,
+ Histogram: [8]uint64{0, 2, 1, 1},
+ },
+ )
+ rows, err = consumer.Drain()
+ if err != nil {
+ t.Fatalf("third Drain error: %v", err)
+ }
+ assertAggregateRows(t, rows, statsengine.SyscallAggregate{
+ TraceID: types.TraceId(traceID),
+ Count: 1,
+ Errors: 0,
+ TotalLatencyNs: 20,
+ MinLatencyNs: 0,
+ MaxLatencyNs: 999,
+ LatencyHistogramNs: [8]uint64{
+ 1,
+ },
+ })
+
+ rows, err = consumer.Drain()
+ if err != nil {
+ t.Fatalf("fourth Drain error: %v", err)
+ }
+ if len(rows) != 0 {
+ t.Fatalf("fourth Drain rows = %+v, want none for zero delta", rows)
+ }
+}
+
+func TestRawSyscallAggregateDiffReturnsOnlyNewCounts(t *testing.T) {
+ prev := rawSyscallAggregate{
+ Count: 5,
+ Errors: 1,
+ TotalDuration: 100,
+ MinDuration: 10,
+ MaxDuration: 40,
+ Histogram: [8]uint64{1, 2, 2},
+ }
+ current := rawSyscallAggregate{
+ Count: 9,
+ Errors: 3,
+ TotalDuration: 190,
+ MinDuration: 5,
+ MaxDuration: 80,
+ Histogram: [8]uint64{2, 5, 2, 1},
+ }
+
+ got := current.diff(prev)
+ want := rawSyscallAggregate{
+ Count: 4,
+ Errors: 2,
+ TotalDuration: 90,
+ MinDuration: 5,
+ MaxDuration: 80,
+ Histogram: [8]uint64{1, 3, 0, 1},
+ }
+ if got != want {
+ t.Fatalf("aggregate diff = %+v, want %+v", got, want)
+ }
+}
+
+func encodeRawAggregates(t *testing.T, values ...rawSyscallAggregate) []byte {
+ t.Helper()
+
+ var buf bytes.Buffer
+ for _, value := range values {
+ if err := binary.Write(&buf, binary.LittleEndian, value); err != nil {
+ t.Fatalf("binary write: %v", err)
+ }
+ }
+ return buf.Bytes()
+}
+
+func assertAggregateRows(t *testing.T, got []statsengine.SyscallAggregate, want statsengine.SyscallAggregate) {
+ t.Helper()
+
+ if len(got) != 1 {
+ t.Fatalf("Drain rows = %+v, want one row", got)
+ }
+ if got[0] != want {
+ t.Fatalf("Drain row = %+v, want %+v", got[0], want)
+ }
+}
+
+type fakeSyscallAggregateMap struct {
+ keys [][]byte
+ values map[uint32][]byte
+}
+
+func newFakeSyscallAggregateMap(traceID uint32, value []byte) *fakeSyscallAggregateMap {
+ key := make([]byte, 4)
+ binary.LittleEndian.PutUint32(key, traceID)
+ return &fakeSyscallAggregateMap{
+ keys: [][]byte{key},
+ values: map[uint32][]byte{
+ traceID: value,
+ },
+ }
+}
+
+func (m *fakeSyscallAggregateMap) Iterator() syscallAggregateIterator {
+ return &fakeSyscallAggregateIterator{keys: m.keys}
+}
+
+func (m *fakeSyscallAggregateMap) GetValue(keyPtr unsafe.Pointer) ([]byte, error) {
+ key := *(*uint32)(keyPtr)
+ value, ok := m.values[key]
+ if !ok {
+ return nil, fmt.Errorf("missing value for key %d", key)
+ }
+ return append([]byte(nil), value...), nil
+}
+
+type fakeSyscallAggregateIterator struct {
+ keys [][]byte
+ next int
+}
+
+func (i *fakeSyscallAggregateIterator) Next() bool {
+ if i.next >= len(i.keys) {
+ return false
+ }
+ i.next++
+ return i.next <= len(i.keys)
+}
+
+func (i *fakeSyscallAggregateIterator) Key() []byte {
+ if i.next == 0 || i.next > len(i.keys) {
+ return nil
+ }
+ return i.keys[i.next-1]
+}
+
+func (i *fakeSyscallAggregateIterator) Err() error {
+ return nil
+}