Skip to content

Commit a6ab935

Browse files
committed
chore(memtrack): use snapshots for c tests
1 parent d444b88 commit a6ab935

12 files changed

Lines changed: 110 additions & 210 deletions

crates/memtrack/src/ebpf/events.rs

Lines changed: 12 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use runner_shared::artifacts::{MemtrackEvent, MemtrackEventKind};
2-
use serde::{Deserialize, Serialize};
32

43
// Include the bindings for event.h
54
pub mod bindings {
@@ -12,55 +11,6 @@ pub mod bindings {
1211
}
1312
use bindings::*;
1413

15-
#[repr(u8)]
16-
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
17-
pub enum EventType {
18-
Malloc = EVENT_TYPE_MALLOC as u8,
19-
Free = EVENT_TYPE_FREE as u8,
20-
Calloc = EVENT_TYPE_CALLOC as u8,
21-
Realloc = EVENT_TYPE_REALLOC as u8,
22-
AlignedAlloc = EVENT_TYPE_ALIGNED_ALLOC as u8,
23-
Mmap = EVENT_TYPE_MMAP as u8,
24-
Munmap = EVENT_TYPE_MUNMAP as u8,
25-
Brk = EVENT_TYPE_BRK as u8,
26-
}
27-
28-
impl From<u8> for EventType {
29-
fn from(val: u8) -> Self {
30-
match val as u32 {
31-
bindings::EVENT_TYPE_MALLOC => EventType::Malloc,
32-
bindings::EVENT_TYPE_FREE => EventType::Free,
33-
bindings::EVENT_TYPE_CALLOC => EventType::Calloc,
34-
bindings::EVENT_TYPE_REALLOC => EventType::Realloc,
35-
bindings::EVENT_TYPE_ALIGNED_ALLOC => EventType::AlignedAlloc,
36-
bindings::EVENT_TYPE_MMAP => EventType::Mmap,
37-
bindings::EVENT_TYPE_MUNMAP => EventType::Munmap,
38-
bindings::EVENT_TYPE_BRK => EventType::Brk,
39-
_ => panic!("Unknown event type: {val}"),
40-
}
41-
}
42-
}
43-
44-
/// Extension trait for MemtrackEvent to get the EventType
45-
pub trait MemtrackEventExt {
46-
fn event_type(&self) -> EventType;
47-
}
48-
49-
impl MemtrackEventExt for MemtrackEvent {
50-
fn event_type(&self) -> EventType {
51-
match self.kind {
52-
MemtrackEventKind::Malloc { .. } => EventType::Malloc,
53-
MemtrackEventKind::Free => EventType::Free,
54-
MemtrackEventKind::Calloc { .. } => EventType::Calloc,
55-
MemtrackEventKind::Realloc { .. } => EventType::Realloc,
56-
MemtrackEventKind::AlignedAlloc { .. } => EventType::AlignedAlloc,
57-
MemtrackEventKind::Mmap { .. } => EventType::Mmap,
58-
MemtrackEventKind::Munmap { .. } => EventType::Munmap,
59-
MemtrackEventKind::Brk { .. } => EventType::Brk,
60-
}
61-
}
62-
}
63-
6414
/// Parse an event from raw bytes into MemtrackEvent
6515
///
6616
/// SAFETY: The data must be a valid `bindings::event`
@@ -70,7 +20,6 @@ pub fn parse_event(data: &[u8]) -> Option<MemtrackEvent> {
7020
}
7121

7222
let event = unsafe { &*(data.as_ptr() as *const bindings::event) };
73-
let event_type = EventType::from(event.header.event_type);
7423

7524
// Common fields from header
7625
let pid = event.header.pid as i32;
@@ -80,51 +29,54 @@ pub fn parse_event(data: &[u8]) -> Option<MemtrackEvent> {
8029
// Parse event data based on type
8130
// SAFETY: The fields must be properly initialized in eBPF
8231
let (addr, kind) = unsafe {
83-
match event_type {
84-
EventType::Malloc => (
32+
match event.header.event_type as u32 {
33+
EVENT_TYPE_MALLOC => (
8534
event.data.alloc.addr,
8635
MemtrackEventKind::Malloc {
8736
size: event.data.alloc.size,
8837
},
8938
),
90-
EventType::Free => (event.data.free.addr, MemtrackEventKind::Free),
91-
EventType::Calloc => (
39+
EVENT_TYPE_FREE => (event.data.free.addr, MemtrackEventKind::Free),
40+
EVENT_TYPE_CALLOC => (
9241
event.data.alloc.addr,
9342
MemtrackEventKind::Calloc {
9443
size: event.data.alloc.size,
9544
},
9645
),
97-
EventType::Realloc => (
46+
EVENT_TYPE_REALLOC => (
9847
event.data.realloc.new_addr,
9948
MemtrackEventKind::Realloc {
10049
old_addr: Some(event.data.realloc.old_addr),
10150
size: event.data.realloc.size,
10251
},
10352
),
104-
EventType::AlignedAlloc => (
53+
EVENT_TYPE_ALIGNED_ALLOC => (
10554
event.data.alloc.addr,
10655
MemtrackEventKind::AlignedAlloc {
10756
size: event.data.alloc.size,
10857
},
10958
),
110-
EventType::Mmap => (
59+
EVENT_TYPE_MMAP => (
11160
event.data.mmap.addr,
11261
MemtrackEventKind::Mmap {
11362
size: event.data.mmap.size,
11463
},
11564
),
116-
EventType::Munmap => (
65+
EVENT_TYPE_MUNMAP => (
11766
event.data.mmap.addr,
11867
MemtrackEventKind::Munmap {
11968
size: event.data.mmap.size,
12069
},
12170
),
122-
EventType::Brk => (
71+
EVENT_TYPE_BRK => (
12372
event.data.mmap.addr,
12473
MemtrackEventKind::Brk {
12574
size: event.data.mmap.size,
12675
},
12776
),
77+
unknown => {
78+
panic!("Unknown event type: {unknown}");
79+
}
12880
}
12981
};
13082

crates/memtrack/src/ebpf/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,5 @@ mod memtrack;
33
mod poller;
44
mod tracker;
55

6-
pub use events::{EventType, MemtrackEventExt};
76
pub use memtrack::MemtrackBpf;
87
pub use tracker::Tracker;

crates/memtrack/tests/c_tests.rs

Lines changed: 12 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1+
#[macro_use]
12
mod shared;
23

3-
use memtrack::EventType;
44
use rstest::rstest;
5-
use runner_shared::artifacts::MemtrackEventKind;
65
use std::fs;
76
use std::path::Path;
87
use std::process::Command;
@@ -32,58 +31,43 @@ fn compile_c_source(
3231
Ok(binary_path)
3332
}
3433

35-
// ============================================================================
36-
// PARAMETERIZED ALLOCATION TESTS
37-
// ============================================================================
38-
39-
/// Test case definition for allocation tracking tests
4034
struct AllocationTestCase {
4135
name: &'static str,
4236
source: &'static str,
43-
assertions: &'static [(EventType, usize)],
44-
allow_excess: bool, // Whether to allow >= instead of exact == for expected counts
4537
}
4638

4739
const ALLOCATION_TEST_CASES: &[AllocationTestCase] = &[
4840
AllocationTestCase {
4941
name: "double_malloc",
5042
source: include_str!("../testdata/double_malloc.c"),
51-
assertions: &[(EventType::Malloc, 2)],
52-
allow_excess: false,
5343
},
5444
AllocationTestCase {
5545
name: "malloc_free",
5646
source: include_str!("../testdata/malloc_free.c"),
57-
assertions: &[(EventType::Malloc, 1), (EventType::Free, 1)],
58-
allow_excess: false,
5947
},
6048
AllocationTestCase {
6149
name: "calloc_test",
6250
source: include_str!("../testdata/calloc_test.c"),
63-
assertions: &[(EventType::Calloc, 1), (EventType::Free, 1)],
64-
allow_excess: false,
6551
},
6652
AllocationTestCase {
6753
name: "realloc_test",
6854
source: include_str!("../testdata/realloc_test.c"),
69-
assertions: &[
70-
(EventType::Malloc, 1),
71-
(EventType::Realloc, 1),
72-
(EventType::Free, 1),
73-
],
74-
allow_excess: false,
7555
},
7656
AllocationTestCase {
7757
name: "aligned_alloc_test",
7858
source: include_str!("../testdata/aligned_alloc_test.c"),
79-
assertions: &[(EventType::AlignedAlloc, 1), (EventType::Free, 1)],
80-
allow_excess: false,
8159
},
8260
AllocationTestCase {
8361
name: "many_allocs",
8462
source: include_str!("../testdata/many_allocs.c"),
85-
assertions: &[(EventType::Malloc, 100), (EventType::Free, 100)],
86-
allow_excess: true, // Allow >= because we allocate ptrs array + 100 allocations
63+
},
64+
AllocationTestCase {
65+
name: "fork_test",
66+
source: include_str!("../testdata/fork_test.c"),
67+
},
68+
AllocationTestCase {
69+
name: "alloc_size",
70+
source: include_str!("../testdata/alloc_size.c"),
8771
},
8872
];
8973

@@ -95,6 +79,8 @@ const ALLOCATION_TEST_CASES: &[AllocationTestCase] = &[
9579
#[case(&ALLOCATION_TEST_CASES[3])]
9680
#[case(&ALLOCATION_TEST_CASES[4])]
9781
#[case(&ALLOCATION_TEST_CASES[5])]
82+
#[case(&ALLOCATION_TEST_CASES[6])]
83+
#[case(&ALLOCATION_TEST_CASES[7])]
9884
#[test_log::test]
9985
fn test_allocation_tracking(
10086
#[case] test_case: &AllocationTestCase,
@@ -104,108 +90,7 @@ fn test_allocation_tracking(
10490

10591
let (events, thread_handle) = shared::track_binary(&binary)?;
10692

107-
for (event_type, expected_count) in test_case.assertions {
108-
let actual_count = shared::count_events_by_type(&events, *event_type);
109-
110-
if test_case.allow_excess {
111-
assert!(
112-
actual_count >= *expected_count,
113-
"Test '{}': Expected at least {} {:?} events, got {}",
114-
test_case.name,
115-
expected_count,
116-
event_type,
117-
actual_count
118-
);
119-
} else {
120-
assert_eq!(
121-
actual_count, *expected_count,
122-
"Test '{}': Expected {} {:?} events, got {}",
123-
test_case.name, expected_count, event_type, actual_count
124-
);
125-
}
126-
}
127-
128-
thread_handle.join().unwrap();
129-
130-
Ok(())
131-
}
132-
133-
#[test]
134-
#[test_with::env(GITHUB_ACTIONS)]
135-
fn test_fork_tracking() -> Result<(), Box<dyn std::error::Error>> {
136-
let temp_dir = TempDir::new()?;
137-
let source = include_str!("../testdata/fork_test.c");
138-
let binary = compile_c_source(source, "fork_test", temp_dir.path())?;
139-
140-
let (events, thread_handle) = shared::track_binary(&binary)?;
141-
142-
let malloc_count = shared::count_events_by_type(&events, EventType::Malloc);
143-
let free_count = shared::count_events_by_type(&events, EventType::Free);
144-
145-
// Should have at least 2 mallocs (parent + child)
146-
assert!(
147-
malloc_count >= 2,
148-
"Expected at least 2 malloc events (parent + child), got {malloc_count}"
149-
);
150-
151-
// Should have at least 2 frees (parent + child)
152-
assert!(
153-
free_count >= 2,
154-
"Expected at least 2 free events (parent + child), got {free_count}"
155-
);
156-
157-
// Verify we have events from different PIDs (parent and child)
158-
let pids: std::collections::HashSet<u32> = events.iter().map(|e| e.pid as u32).collect();
159-
assert!(
160-
!pids.is_empty(),
161-
"Expected to track at least parent process"
162-
);
163-
164-
thread_handle.join().unwrap();
165-
166-
Ok(())
167-
}
168-
169-
#[test]
170-
#[test_with::env(GITHUB_ACTIONS)]
171-
fn test_allocation_sizes() -> Result<(), Box<dyn std::error::Error>> {
172-
let temp_dir = TempDir::new()?;
173-
let source = include_str!("../testdata/alloc_size.c");
174-
let binary = compile_c_source(source, "alloc_size", temp_dir.path())?;
175-
176-
let (events, thread_handle) = shared::track_binary(&binary)?;
177-
178-
// Filter malloc events and collect their sizes
179-
let malloc_events: Vec<u64> = events
180-
.iter()
181-
.filter_map(|e| match e.kind {
182-
MemtrackEventKind::Malloc { size } => Some(size),
183-
_ => None,
184-
})
185-
.collect();
186-
187-
// Expected sizes from alloc_size.c: 1024, 2048, 512, 4096
188-
let expected_sizes = vec![1024u64, 2048, 512, 4096];
189-
190-
// Check that we have exactly 4 malloc events
191-
assert_eq!(
192-
malloc_events.len(),
193-
4,
194-
"Expected 4 malloc events, got {}",
195-
malloc_events.len()
196-
);
197-
198-
// Check that all expected sizes are present
199-
for expected_size in &expected_sizes {
200-
assert!(
201-
malloc_events.contains(expected_size),
202-
"Expected allocation size {expected_size} not found in malloc events: {malloc_events:?}"
203-
);
204-
}
205-
206-
// Check that we have 4 free events
207-
let free_count = shared::count_events_by_type(&events, EventType::Free);
208-
assert_eq!(free_count, 4, "Expected 4 free events, got {free_count}");
93+
assert_events_snapshot!(test_case.name, events);
20994

21095
thread_handle.join().unwrap();
21196

0 commit comments

Comments
 (0)