1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
package internal
import (
"encoding/binary"
"os"
"path/filepath"
"runtime"
"testing"
"unsafe"
"ior/internal/generate"
)
// TestSyscallAggregateSchemaMatchesCStruct asserts that the Go
// rawSyscallAggregate struct mirrors the C struct syscall_aggregate defined in
// internal/c/maps.h. If a field is added, removed, or reordered in either
// definition without updating the other, this test fails and prevents silent
// decode corruption at runtime.
func TestSyscallAggregateSchemaMatchesCStruct(t *testing.T) {
cStruct := parseSyscallAggregateFromMapsH(t)
goSize := int(unsafe.Sizeof(rawSyscallAggregate{}))
binarySize := binary.Size(rawSyscallAggregate{})
// The C struct has 5 scalar fields + 1 array field = 6 members.
wantFields := []struct {
cField string
cType string
cArray string
}{
{"count", "__u64", ""},
{"errors", "__u64", ""},
{"total_duration_ns", "__u64", ""},
{"min_duration_ns", "__u64", ""},
{"max_duration_ns", "__u64", ""},
{"duration_histogram", "__u64", "8"},
}
if len(cStruct.Members) != len(wantFields) {
t.Fatalf("C struct syscall_aggregate has %d fields, want %d; did a field change?",
len(cStruct.Members), len(wantFields))
}
for i, want := range wantFields {
got := cStruct.Members[i]
if got.FieldName != want.cField {
t.Errorf("field %d: C name = %q, want %q", i, got.FieldName, want.cField)
}
if got.TypeName != want.cType {
t.Errorf("field %d (%s): C type = %q, want %q", i, want.cField, got.TypeName, want.cType)
}
if got.ArraySize != want.cArray {
t.Errorf("field %d (%s): C array size = %q, want %q", i, want.cField, got.ArraySize, want.cArray)
}
}
// Total size: 5 x uint64 + 8 x uint64 = 13 x 8 = 104 bytes.
wantSize := 13 * 8
if goSize != wantSize {
t.Errorf("unsafe.Sizeof(rawSyscallAggregate) = %d, want %d", goSize, wantSize)
}
if binarySize != wantSize {
t.Errorf("binary.Size(rawSyscallAggregate) = %d, want %d", binarySize, wantSize)
}
// Verify field offsets match C layout expectations (packed, no padding
// needed since all fields are uint64-aligned).
assertOffset(t, "Count", unsafe.Offsetof(rawSyscallAggregate{}.Count), 0)
assertOffset(t, "Errors", unsafe.Offsetof(rawSyscallAggregate{}.Errors), 8)
assertOffset(t, "TotalDuration", unsafe.Offsetof(rawSyscallAggregate{}.TotalDuration), 16)
assertOffset(t, "MinDuration", unsafe.Offsetof(rawSyscallAggregate{}.MinDuration), 24)
assertOffset(t, "MaxDuration", unsafe.Offsetof(rawSyscallAggregate{}.MaxDuration), 32)
assertOffset(t, "Histogram", unsafe.Offsetof(rawSyscallAggregate{}.Histogram), 40)
}
// parseSyscallAggregateFromMapsH reads internal/c/maps.h and returns the
// parsed C struct definition for syscall_aggregate. Fails the test if the
// struct is not found.
func parseSyscallAggregateFromMapsH(t *testing.T) generate.CStruct {
t.Helper()
_, filename, _, ok := runtime.Caller(0)
if !ok {
t.Fatal("runtime.Caller failed")
}
mapsH := filepath.Join(filepath.Dir(filename), "c", "maps.h")
f, err := os.Open(mapsH)
if err != nil {
t.Fatalf("open maps.h: %v", err)
}
defer f.Close()
structs, _, err := generate.ParseCTypesInput(f)
if err != nil {
t.Fatalf("ParseCTypesInput(maps.h): %v", err)
}
for _, s := range structs {
if s.Name == "syscall_aggregate" {
return s
}
}
t.Fatal("struct syscall_aggregate not found in maps.h")
return generate.CStruct{}
}
func assertOffset(t *testing.T, field string, got, want uintptr) {
t.Helper()
if got != want {
t.Errorf("rawSyscallAggregate.%s offset = %d, want %d", field, got, want)
}
}
|