1use serde::{Deserialize, Serialize};
7use std::fmt;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub enum MemoryEventType {
12 Allocate,
14 Deallocate,
16 Reallocate,
18 Move,
20 Borrow,
22 Return,
24 Metadata,
26 Clone,
28}
29
30impl fmt::Display for MemoryEventType {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 MemoryEventType::Allocate => write!(f, "Allocate"),
34 MemoryEventType::Deallocate => write!(f, "Deallocate"),
35 MemoryEventType::Reallocate => write!(f, "Reallocate"),
36 MemoryEventType::Move => write!(f, "Move"),
37 MemoryEventType::Borrow => write!(f, "Borrow"),
38 MemoryEventType::Return => write!(f, "Return"),
39 MemoryEventType::Metadata => write!(f, "Metadata"),
40 MemoryEventType::Clone => write!(f, "Clone"),
41 }
42 }
43}
44
45fn is_false(value: &bool) -> bool {
47 !*value
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct MemoryEvent {
56 pub timestamp: u64,
58 pub event_type: MemoryEventType,
60 pub ptr: usize,
62 pub size: usize,
64 pub old_size: Option<usize>,
66 pub new_ptr: Option<usize>,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
74 pub binding_serial: Option<u64>,
75 #[serde(default, skip_serializing_if = "is_false")]
81 pub degraded_release: bool,
82 #[serde(default, skip_serializing_if = "Option::is_none")]
85 pub charge_outcome: Option<crate::task_registry::ChargeOutcome>,
86 pub thread_id: u64,
88 pub var_name: Option<String>,
90 pub type_name: Option<String>,
92 pub call_stack_hash: Option<u64>,
94 pub thread_name: Option<String>,
96 pub source_file: Option<String>,
98 pub source_line: Option<u32>,
100 pub module_path: Option<String>,
102 pub clone_source_ptr: Option<usize>,
104 pub clone_target_ptr: Option<usize>,
106 pub stack_ptr: Option<usize>,
108 pub task_id: Option<u64>,
110}
111
112impl MemoryEvent {
113 pub fn allocate(ptr: usize, size: usize, thread_id: u64) -> Self {
119 Self {
120 timestamp: Self::now(),
121 event_type: MemoryEventType::Allocate,
122 ptr,
123 size,
124 old_size: None,
125 new_ptr: None,
126 binding_serial: None,
127 degraded_release: false,
128 charge_outcome: None,
129 thread_id,
130 var_name: None,
131 type_name: None,
132 call_stack_hash: None,
133 thread_name: None,
134 source_file: None,
135 source_line: None,
136 module_path: None,
137 clone_source_ptr: None,
138 clone_target_ptr: None,
139 stack_ptr: None,
140 task_id: crate::task_registry::TaskIdRegistry::current_task_id(),
141 }
142 }
143
144 pub fn reallocate_moved(
150 ptr: usize,
151 new_ptr: usize,
152 old_size: usize,
153 new_size: usize,
154 thread_id: u64,
155 ) -> Self {
156 let mut event = Self::reallocate(ptr, old_size, new_size, thread_id);
157 event.new_ptr = Some(new_ptr);
158 event
159 }
160
161 pub fn deallocate(ptr: usize, size: usize, thread_id: u64) -> Self {
163 Self {
164 timestamp: Self::now(),
165 event_type: MemoryEventType::Deallocate,
166 ptr,
167 size,
168 old_size: None,
169 new_ptr: None,
170 binding_serial: None,
171 degraded_release: false,
172 charge_outcome: None,
173 thread_id,
174 var_name: None,
175 type_name: None,
176 call_stack_hash: None,
177 thread_name: None,
178 source_file: None,
179 source_line: None,
180 module_path: None,
181 clone_source_ptr: None,
182 clone_target_ptr: None,
183 stack_ptr: None,
184 task_id: crate::task_registry::TaskIdRegistry::current_task_id(),
185 }
186 }
187
188 pub fn reallocate(ptr: usize, old_size: usize, new_size: usize, thread_id: u64) -> Self {
190 Self {
191 timestamp: Self::now(),
192 event_type: MemoryEventType::Reallocate,
193 ptr,
194 size: new_size,
195 old_size: Some(old_size),
196 new_ptr: None,
197 binding_serial: None,
198 degraded_release: false,
199 charge_outcome: None,
200 thread_id,
201 var_name: None,
202 type_name: None,
203 call_stack_hash: None,
204 thread_name: None,
205 source_file: None,
206 source_line: None,
207 module_path: None,
208 clone_source_ptr: None,
209 clone_target_ptr: None,
210 stack_ptr: None,
211 task_id: crate::task_registry::TaskIdRegistry::current_task_id(),
212 }
213 }
214
215 pub fn metadata(var_name: String, type_name: String, thread_id: u64, size: usize) -> Self {
217 Self {
218 timestamp: Self::now(),
219 event_type: MemoryEventType::Metadata,
220 ptr: 0, size,
222 old_size: None,
223 new_ptr: None,
224 binding_serial: None,
225 degraded_release: false,
226 charge_outcome: None,
227 thread_id,
228 var_name: Some(var_name),
229 type_name: Some(type_name),
230 call_stack_hash: None,
231 thread_name: None,
232 source_file: None,
233 source_line: None,
234 module_path: None,
235 clone_source_ptr: None,
236 clone_target_ptr: None,
237 stack_ptr: None,
238 task_id: crate::task_registry::TaskIdRegistry::current_task_id(),
239 }
240 }
241
242 pub fn clone_event(
244 source_ptr: usize,
245 target_ptr: usize,
246 size: usize,
247 thread_id: u64,
248 var_name: Option<String>,
249 type_name: Option<String>,
250 ) -> Self {
251 Self {
252 timestamp: Self::now(),
253 event_type: MemoryEventType::Clone,
254 ptr: target_ptr,
255 size,
256 old_size: None,
257 new_ptr: None,
258 binding_serial: None,
259 degraded_release: false,
260 charge_outcome: None,
261 thread_id,
262 var_name,
263 type_name,
264 call_stack_hash: None,
265 thread_name: None,
266 source_file: None,
267 source_line: None,
268 module_path: None,
269 clone_source_ptr: Some(source_ptr),
270 clone_target_ptr: Some(target_ptr),
271 stack_ptr: None,
272 task_id: crate::task_registry::TaskIdRegistry::current_task_id(),
273 }
274 }
275
276 pub fn borrow(ptr: usize, size: usize, thread_id: u64, is_mutable: bool) -> Self {
278 Self {
279 timestamp: Self::now(),
280 event_type: MemoryEventType::Borrow,
281 ptr,
282 size,
283 old_size: None,
284 new_ptr: None,
285 binding_serial: None,
286 degraded_release: false,
287 charge_outcome: None,
288 thread_id,
289 var_name: None,
290 type_name: Some(if is_mutable {
291 "mutable borrow".to_string()
292 } else {
293 "immutable borrow".to_string()
294 }),
295 call_stack_hash: None,
296 thread_name: None,
297 source_file: None,
298 source_line: None,
299 module_path: None,
300 clone_source_ptr: None,
301 clone_target_ptr: None,
302 stack_ptr: None,
303 task_id: crate::task_registry::TaskIdRegistry::current_task_id(),
304 }
305 }
306
307 pub fn now() -> u64 {
310 use std::time::{SystemTime, UNIX_EPOCH};
311 SystemTime::now()
312 .duration_since(UNIX_EPOCH)
313 .map(|d| d.as_nanos() as u64)
314 .unwrap_or_default()
315 }
316
317 pub fn with_var_name(mut self, name: String) -> Self {
319 self.var_name = Some(name);
320 self
321 }
322
323 pub fn with_type_name(mut self, name: String) -> Self {
325 self.type_name = Some(name);
326 self
327 }
328
329 pub fn with_source_file(mut self, file: String) -> Self {
331 self.source_file = Some(file);
332 self
333 }
334
335 pub fn with_source_line(mut self, line: u32) -> Self {
337 self.source_line = Some(line);
338 self
339 }
340
341 pub fn with_call_stack_hash(mut self, hash: u64) -> Self {
343 self.call_stack_hash = Some(hash);
344 self
345 }
346
347 pub fn with_thread_name(mut self, name: String) -> Self {
349 self.thread_name = Some(name);
350 self
351 }
352
353 pub fn is_allocation(&self) -> bool {
355 matches!(
356 self.event_type,
357 MemoryEventType::Allocate | MemoryEventType::Reallocate | MemoryEventType::Metadata
358 )
359 }
360
361 pub fn is_deallocation(&self) -> bool {
363 matches!(self.event_type, MemoryEventType::Deallocate)
364 }
365
366 pub fn is_move(&self) -> bool {
368 matches!(self.event_type, MemoryEventType::Move)
369 }
370
371 pub fn is_borrow(&self) -> bool {
373 matches!(self.event_type, MemoryEventType::Borrow)
374 }
375
376 pub fn is_return(&self) -> bool {
378 matches!(self.event_type, MemoryEventType::Return)
379 }
380}