shadow_rs/core/
sim_stats.rs

1use std::cell::RefCell;
2use std::sync::Mutex;
3
4use anyhow::Context;
5use serde::Serialize;
6
7use crate::utility::counter::Counter;
8
9/// Simulation statistics to be accessed by a single thread.
10#[derive(Debug)]
11pub struct LocalSimStats {
12    pub alloc_counts: RefCell<Counter>,
13    pub dealloc_counts: RefCell<Counter>,
14    pub syscall_counts: RefCell<Counter>,
15}
16
17impl LocalSimStats {
18    pub fn new() -> Self {
19        Self {
20            alloc_counts: RefCell::new(Counter::new()),
21            dealloc_counts: RefCell::new(Counter::new()),
22            syscall_counts: RefCell::new(Counter::new()),
23        }
24    }
25}
26
27impl Default for LocalSimStats {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33/// Simulation statistics to be accessed by multiple threads.
34#[derive(Debug)]
35pub struct SharedSimStats {
36    pub alloc_counts: Mutex<Counter>,
37    pub dealloc_counts: Mutex<Counter>,
38    pub syscall_counts: Mutex<Counter>,
39}
40
41impl SharedSimStats {
42    pub fn new() -> Self {
43        Self {
44            alloc_counts: Mutex::new(Counter::new()),
45            dealloc_counts: Mutex::new(Counter::new()),
46            syscall_counts: Mutex::new(Counter::new()),
47        }
48    }
49
50    /// Add stats from a local object to a shared object. May reset fields of `local`.
51    pub fn add_from_local_stats(&self, local: &LocalSimStats) {
52        let mut shared_alloc_counts = self.alloc_counts.lock().unwrap();
53        let mut shared_dealloc_counts = self.dealloc_counts.lock().unwrap();
54        let mut shared_syscall_counts = self.syscall_counts.lock().unwrap();
55
56        let mut local_alloc_counts = local.alloc_counts.borrow_mut();
57        let mut local_dealloc_counts = local.dealloc_counts.borrow_mut();
58        let mut local_syscall_counts = local.syscall_counts.borrow_mut();
59
60        shared_alloc_counts.add_counter(&local_alloc_counts);
61        shared_dealloc_counts.add_counter(&local_dealloc_counts);
62        shared_syscall_counts.add_counter(&local_syscall_counts);
63
64        *local_alloc_counts = Counter::new();
65        *local_dealloc_counts = Counter::new();
66        *local_syscall_counts = Counter::new();
67    }
68}
69
70impl Default for SharedSimStats {
71    fn default() -> Self {
72        Self::new()
73    }
74}
75
76/// Simulation statistics in the format to be output.
77#[derive(Serialize, Clone, Debug)]
78struct SimStatsForOutput {
79    pub objects: ObjectStatsForOutput,
80    pub syscalls: Counter,
81}
82
83#[derive(Serialize, Clone, Debug)]
84struct ObjectStatsForOutput {
85    pub alloc_counts: Counter,
86    pub dealloc_counts: Counter,
87}
88
89impl SimStatsForOutput {
90    /// Takes data from `stats` and puts it into a structure designed for output. May reset fields
91    /// of `stats`.
92    pub fn new(stats: &SharedSimStats) -> Self {
93        Self {
94            objects: ObjectStatsForOutput {
95                alloc_counts: std::mem::replace(
96                    &mut stats.alloc_counts.lock().unwrap(),
97                    Counter::new(),
98                ),
99                dealloc_counts: std::mem::replace(
100                    &mut stats.dealloc_counts.lock().unwrap(),
101                    Counter::new(),
102                ),
103            },
104            syscalls: std::mem::replace(&mut stats.syscall_counts.lock().unwrap(), Counter::new()),
105        }
106    }
107}
108
109/// May reset fields of `stats`.
110pub fn write_stats_to_file(
111    filename: &std::path::Path,
112    stats: &SharedSimStats,
113) -> anyhow::Result<()> {
114    let stats = SimStatsForOutput::new(stats);
115
116    let file = std::fs::File::create(filename)
117        .with_context(|| format!("Failed to create file '{}'", filename.display()))?;
118
119    serde_json::to_writer_pretty(file, &stats).with_context(|| {
120        format!(
121            "Failed to write stats json to file '{}'",
122            filename.display()
123        )
124    })?;
125
126    Ok(())
127}