#![cfg(feature = "memory-profiling")]
#![cfg(feature = "analysis")]
#![cfg(target_os = "linux")]
mod common;
use common::{CAPTURE_BUFFER_SIZE, capture_processor, decode_all};
use dial9::analysis::analysis_events::Dial9Event;
use dial9::memory::{Dial9Allocator, MemoryProfiler, MemoryProfilingConfig};
use dial9::{MemoryBuffer, RecorderPipelineExt, RecorderTokioExt, recorder};
use std::time::Duration;
#[global_allocator]
static ALLOC: Dial9Allocator = Dial9Allocator::system();
#[test]
fn hook_captures_sampled_allocations() {
let (capture, batches) = capture_processor();
let recorder = recorder(MemoryBuffer::new(CAPTURE_BUFFER_SIZE).unwrap())
.with_custom_pipeline(|p| p.pipe(capture))
.build();
let (recorder, rt) = recorder
.attach_tokio_runtime(|t| {
t.enable_all();
t.worker_threads(1);
})
.expect("attach tokio");
let handle = recorder.handle().clone();
let _mem_guard = MemoryProfiler::from_config(
MemoryProfilingConfig::builder()
.sample_rate_bytes(1024)
.rng_seed(42)
.build(),
)
.install(handle)
.expect("install should succeed");
rt.block_on(async {
for _ in 0..100 {
let v: Vec<u8> = Vec::with_capacity(1024);
std::hint::black_box(v);
}
tokio::time::sleep(Duration::from_millis(200)).await;
});
drop(rt);
recorder.graceful_shutdown(Duration::from_secs(1));
let b = batches.lock().unwrap();
let events: Vec<Dial9Event> = decode_all(&b);
let allocs: Vec<_> = events
.iter()
.filter(|e| matches!(e, Dial9Event::AllocEvent(_)))
.collect();
assert!(
!allocs.is_empty(),
"expected at least one AllocEvent from sampled allocations, got 0"
);
if let Dial9Event::AllocEvent(e) = &allocs[0] {
assert!(e.size > 0, "size should be non-zero");
assert!(
!e.callchain.is_empty(),
"callchain should have at least one frame"
);
}
}