metrale_gpu_runtime/cuda_backend/
redzone.rs1use super::*;
13
14#[derive(Clone, Copy)]
17pub(crate) struct RedZone {
18 user_ptr: u64,
19 user_bytes: usize,
20 pad_bytes: usize,
21 idx: usize,
22}
23
24pub(crate) fn redzone_bytes() -> usize {
27 static N: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
28 *N.get_or_init(|| {
29 std::env::var("METRALE_REDZONE")
30 .ok()
31 .and_then(|v| v.parse::<usize>().ok())
32 .map(|n| if n == 0 { 0 } else { n.next_multiple_of(16) })
33 .unwrap_or(0)
34 })
35}
36
37pub(crate) fn redzone_min_idx() -> usize {
41 static N: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
42 *N.get_or_init(|| {
43 std::env::var("METRALE_REDZONE_MIN_IDX")
44 .ok()
45 .and_then(|v| v.parse::<usize>().ok())
46 .unwrap_or(0)
47 })
48}
49
50pub(crate) fn redzone_trace_idx() -> Option<usize> {
53 static N: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
54 *N.get_or_init(|| {
55 std::env::var("METRALE_REDZONE_TRACE_IDX")
56 .ok()
57 .and_then(|v| v.parse::<usize>().ok())
58 })
59}
60
61pub(crate) static ALLOC_SEQ: std::sync::atomic::AtomicUsize =
65 std::sync::atomic::AtomicUsize::new(0);
66
67pub(crate) fn redzone_fill() -> u8 {
76 static F: std::sync::OnceLock<u8> = std::sync::OnceLock::new();
77 *F.get_or_init(|| {
78 std::env::var("METRALE_REDZONE_FILL")
79 .ok()
80 .and_then(|v| v.parse::<u8>().ok())
81 .unwrap_or(0xEE)
82 })
83}
84
85impl MetraleCudaBackend {
86 pub(crate) fn record_redzone(
88 &self,
89 user_ptr: u64,
90 user_bytes: usize,
91 pad_bytes: usize,
92 idx: usize,
93 ) {
94 self.redzones.lock().push(RedZone {
95 user_ptr,
96 user_bytes,
97 pad_bytes,
98 idx,
99 });
100 }
101
102 pub(crate) fn forget_redzone(&self, user_ptr: u64) {
103 self.redzones.lock().retain(|z| z.user_ptr != user_ptr);
104 }
105
106 pub fn poison_redzones(&self, lo: usize, hi: usize) -> anyhow::Result<()> {
113 let zones: Vec<RedZone> = self.redzones.lock().clone();
114 for z in &zones {
115 let v = if z.idx >= lo && z.idx < hi {
116 0xEEu8
117 } else {
118 0x00u8
119 };
120 let st = unsafe { cuMemsetD8_v2(z.user_ptr + z.user_bytes as u64, v, z.pad_bytes) };
121 if st != 0 {
122 anyhow::bail!("poison_redzones: cuMemsetD8_v2 failed: status {st}");
123 }
124 }
125 Ok(())
126 }
127
128 pub fn scan_redzones(&self) -> anyhow::Result<usize> {
134 let fill = redzone_fill();
135 let zones: Vec<RedZone> = self.redzones.lock().clone();
136 let mut bad = 0usize;
137 let mut host = Vec::new();
138 for z in &zones {
139 host.clear();
140 host.resize(z.pad_bytes, 0u8);
141 let st = unsafe {
142 cuMemcpyDtoH_v2(
143 host.as_mut_ptr() as *mut std::ffi::c_void,
144 z.user_ptr + z.user_bytes as u64,
145 z.pad_bytes,
146 )
147 };
148 if st != 0 {
149 anyhow::bail!("redzone scan: cuMemcpyDtoH_v2 failed: status {st}");
150 }
151 let Some(first) = host.iter().position(|b| *b != fill) else {
152 continue;
153 };
154 let changed = host.iter().filter(|b| **b != fill).count();
155 let last = host.iter().rposition(|b| *b != fill).unwrap_or(first);
156 tracing::error!(
157 "🔴 REDZONE VIOLATION alloc#{} user_bytes={} pad={} : bytes [{}..={}] past the end were written ({} of {} pad bytes changed), first bad value {:#04x}",
158 z.idx,
159 z.user_bytes,
160 z.pad_bytes,
161 first,
162 last,
163 changed,
164 z.pad_bytes,
165 host[first],
166 );
167 bad += 1;
168 let st = unsafe { cuMemsetD8_v2(z.user_ptr + z.user_bytes as u64, fill, z.pad_bytes) };
169 if st != 0 {
170 anyhow::bail!("redzone scan: refill cuMemsetD8_v2 failed: status {st}");
171 }
172 }
173 tracing::info!(
174 "redzone scan: {} zones checked, {} violated",
175 zones.len(),
176 bad
177 );
178 Ok(bad)
179 }
180}