#![cfg(feature = "memory-profiling")]
#![cfg(target_os = "linux")]
mod common;
use common::{CAPTURE_BUFFER_SIZE, capture_processor, decode_all};
use dial9::memory::{Dial9Allocator, MemoryProfiler, MemoryProfilingConfig};
use dial9::{Dial9HandleTokioExt, MemoryBuffer, RecorderPipelineExt, TokioAttachOptions, recorder};
use serde::Deserialize;
use std::time::Duration;
#[global_allocator]
static ALLOC: Dial9Allocator = Dial9Allocator::system();
#[derive(Debug, Deserialize)]
#[serde(tag = "event")]
enum OverflowEvent {
MemoryProfileOverflowEvent {
#[allow(dead_code)]
timestamp_ns: u64,
dropped_allocs: u64,
dropped_frees: u64,
},
#[serde(other)]
Other,
}
#[test]
fn overflow_event_emitted_when_ring_overflows() {
let (capture, batches) = capture_processor();
let recorder = recorder(MemoryBuffer::new(CAPTURE_BUFFER_SIZE).unwrap())
.with_custom_pipeline(|p| p.pipe(capture))
.build();
let mut builder = tokio::runtime::Builder::new_multi_thread();
builder.enable_all().worker_threads(1);
let rt = recorder
.handle()
.attach_tokio_runtime(builder, TokioAttachOptions::default())
.expect("attach tokio");
let handle = recorder.handle().clone();
let _mem_guard = MemoryProfiler::from_config(
MemoryProfilingConfig::builder()
.sample_rate_bytes(1) .ring_capacity(4)
.rng_seed(42)
.build(),
)
.install(handle)
.expect("install should succeed");
rt.block_on(async {
for _ in 0..1000 {
let v: Vec<u8> = vec![0u8; 64];
std::hint::black_box(&v);
drop(v);
}
tokio::time::sleep(Duration::from_millis(100)).await;
});
drop(rt);
recorder.graceful_shutdown(Duration::from_secs(1));
let batches = batches.lock().unwrap();
let events: Vec<OverflowEvent> = decode_all(&batches);
let overflows: Vec<_> = events
.iter()
.filter_map(|e| match e {
OverflowEvent::MemoryProfileOverflowEvent {
dropped_allocs,
dropped_frees,
..
} => Some((*dropped_allocs, *dropped_frees)),
_ => None,
})
.collect();
assert!(
!overflows.is_empty(),
"expected at least one MemoryProfileOverflowEvent"
);
let total_dropped_allocs: u64 = overflows.iter().map(|(a, _)| a).sum();
let total_dropped_frees: u64 = overflows.iter().map(|(_, f)| f).sum();
assert!(
total_dropped_allocs > 0 || total_dropped_frees > 0,
"expected non-zero drops, got allocs={total_dropped_allocs} frees={total_dropped_frees}"
);
}