#![cfg(feature = "memory-profiling")]
#![cfg(target_os = "linux")]
use dial9::memory::{InstallError, MemoryProfiler, MemoryProfilingConfig};
use dial9::{Dial9HandleTokioExt, TokioAttachOptions, recorder};
use std::sync::{Arc, Mutex};
mod common;
#[derive(Clone, Default)]
struct SharedBuf(Arc<Mutex<Vec<u8>>>);
impl std::io::Write for SharedBuf {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.0.lock().unwrap().extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
fn capturing_subscriber() -> (SharedBuf, impl tracing::Subscriber) {
let buf = SharedBuf::default();
let subscriber = tracing_subscriber::fmt()
.with_writer({
let buf = buf.clone();
move || buf.clone()
})
.with_ansi(false)
.finish();
(buf, subscriber)
}
#[test]
#[ignore = "only meaningful when built without -C force-frame-pointers=yes"]
fn install_without_frame_pointers_returns_missing_frame_pointers() {
let recorder = recorder(common::small_mem_writer()).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 (buf, subscriber) = capturing_subscriber();
let handle = recorder.handle().clone();
let err = tracing::subscriber::with_default(subscriber, || {
MemoryProfiler::with_defaults().install(handle)
})
.expect_err("install should detect missing frame pointers");
assert!(
matches!(err, InstallError::MissingFramePointers { .. }),
"expected MissingFramePointers, got: {err:?}"
);
let logged = String::from_utf8(buf.0.lock().unwrap().clone()).unwrap();
assert!(
logged.contains("frame-pointer self-test failed")
&& logged.contains("fail_on_missing_frame_pointers=true"),
"expected a warning about the failed frame-pointer check even in the \
hard-fail path, got: {logged:?}"
);
}
#[test]
#[ignore = "only meaningful when built without -C force-frame-pointers=yes"]
fn install_without_frame_pointers_succeeds_and_warns_when_fail_is_disabled() {
let recorder = recorder(common::small_mem_writer()).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 (buf, subscriber) = capturing_subscriber();
let handle = recorder.handle().clone();
let config = MemoryProfilingConfig::builder()
.fail_on_missing_frame_pointers(false)
.build();
let _guard = tracing::subscriber::with_default(subscriber, || {
MemoryProfiler::from_config(config).install(handle)
})
.expect("install should proceed despite missing frame pointers");
let logged = String::from_utf8(buf.0.lock().unwrap().clone()).unwrap();
assert!(
logged.contains("frame-pointer self-test failed")
&& logged.contains("fail_on_missing_frame_pointers=false"),
"expected a warning about the failed frame-pointer check, got: {logged:?}"
);
}