#![cfg(feature = "memory-profiling")]
#![cfg(target_os = "linux")]
use dial9::memory::{Dial9Allocator, MemoryProfiler, MemoryProfilingConfig};
use dial9::{Dial9HandleTokioExt, MemoryBuffer, TokioAttachOptions, recorder};
use std::cell::RefCell;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;
#[global_allocator]
static ALLOC: Dial9Allocator = Dial9Allocator::system();
static PANICS: AtomicU64 = AtomicU64::new(0);
struct LateGuard;
impl Drop for LateGuard {
fn drop(&mut self) {
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let v: Vec<u8> = Vec::with_capacity(1024);
std::hint::black_box(&v);
drop(v);
let v2: Vec<u8> = Vec::with_capacity(2048);
std::hint::black_box(&v2);
drop(v2);
}));
if result.is_err() {
PANICS.fetch_add(1, Ordering::Relaxed);
}
}
}
thread_local! {
static LATE: RefCell<Option<LateGuard>> = const { RefCell::new(None) };
}
#[test]
fn opt_out_prevents_tls_teardown_panic() {
let prev_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
PANICS.store(0, Ordering::Relaxed);
let recorder = recorder(MemoryBuffer::new(16 * 1024 * 1024).unwrap()).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(64) .track_liveset(true)
.rng_seed(42)
.build(),
)
.install(handle)
.expect("install should succeed");
const N_THREADS: usize = 16;
rt.block_on(async {
tokio::time::sleep(Duration::from_millis(50)).await;
let mut handles = Vec::new();
for _ in 0..N_THREADS {
handles.push(tokio::task::spawn_blocking(|| {
LATE.with(|cell| {
*cell.borrow_mut() = Some(LateGuard);
});
for _ in 0..50 {
let v: Vec<u8> = Vec::with_capacity(256);
std::hint::black_box(&v);
drop(v);
}
}));
}
for h in handles {
h.await.expect("spawn_blocking panicked");
}
tokio::time::sleep(Duration::from_millis(100)).await;
});
drop(rt);
drop(recorder);
std::panic::set_hook(prev_hook);
let panics = PANICS.load(Ordering::Relaxed);
assert_eq!(
panics, 0,
"expected 0 panics with OPT_OUT sentinel, got {panics}"
);
}