mmtk/util/statistics/
stats.rs

1use crate::mmtk::MMTK;
2use crate::util::options::Options;
3use crate::util::statistics::counter::*;
4use crate::util::statistics::Timer;
5use crate::vm::VMBinding;
6
7#[cfg(feature = "perf_counter")]
8use pfm::Perfmon;
9use std::collections::HashMap;
10use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
11use std::sync::Arc;
12use std::sync::Mutex;
13use std::time::Duration;
14
15/// The default number of phases for statistics.
16pub const DEFAULT_NUM_PHASES: usize = 1 << 12;
17pub const MAX_COUNTERS: usize = 100;
18
19/// GC stats shared among counters
20pub struct SharedStats {
21    phase: AtomicUsize,
22    gathering_stats: AtomicBool,
23}
24
25impl SharedStats {
26    fn increment_phase(&self) {
27        self.phase.fetch_add(1, Ordering::SeqCst);
28    }
29
30    pub fn get_phase(&self) -> usize {
31        self.phase.load(Ordering::SeqCst)
32    }
33
34    pub fn get_gathering_stats(&self) -> bool {
35        self.gathering_stats.load(Ordering::SeqCst)
36    }
37
38    fn set_gathering_stats(&self, val: bool) {
39        self.gathering_stats.store(val, Ordering::SeqCst);
40    }
41}
42
43/// GC statistics
44///
45/// The struct holds basic GC statistics, like the GC count,
46/// and an array of counters.
47pub struct Stats {
48    gc_count: AtomicUsize,
49    total_time: Arc<Mutex<Timer>>,
50    // crate `pfm` uses libpfm4 under the hood for parsing perf event names
51    // Initialization of libpfm4 is required before we can use `PerfEvent` types
52    #[cfg(feature = "perf_counter")]
53    perfmon: Perfmon,
54    pub shared: Arc<SharedStats>,
55    counters: Mutex<Vec<Arc<Mutex<dyn Counter + Send>>>>,
56    time_to_yield: Arc<Mutex<LatencySampler>>,
57    pause_time: Arc<Mutex<LatencySampler>>,
58}
59
60impl Stats {
61    #[allow(unused)]
62    pub fn new(options: &Options) -> Self {
63        // Create a perfmon instance and initialize it
64        // we use perfmon to parse perf event names
65        #[cfg(feature = "perf_counter")]
66        let perfmon = {
67            let mut perfmon: Perfmon = Default::default();
68            perfmon.initialize().expect("Perfmon failed to initialize");
69            perfmon
70        };
71        let shared = Arc::new(SharedStats {
72            phase: AtomicUsize::new(0),
73            gathering_stats: AtomicBool::new(false),
74        });
75        let mut counters: Vec<Arc<Mutex<dyn Counter + Send>>> = vec![];
76        // We always have a time counter enabled
77        let t = Arc::new(Mutex::new(LongCounter::new(
78            "time".to_string(),
79            shared.clone(),
80            true,
81            false,
82            MonotoneNanoTime {},
83        )));
84        counters.push(t.clone());
85        // We always have a time-to-yield counter enabled
86        let time_to_yield = Arc::new(Mutex::new(LatencySampler::new(
87            "time-to-yield",
88            shared.clone(),
89            true,
90        )));
91        counters.push(time_to_yield.clone());
92        // We always have a pause-time counter enabled
93        let pause_time = Arc::new(Mutex::new(LatencySampler::new(
94            "pause-time",
95            shared.clone(),
96            true,
97        )));
98        counters.push(pause_time.clone());
99        // Read from the MMTK option for a list of perf events we want to
100        // measure, and create corresponding counters
101        #[cfg(feature = "perf_counter")]
102        for e in &options.phase_perf_events.events {
103            counters.push(Arc::new(Mutex::new(LongCounter::new(
104                e.0.clone(),
105                shared.clone(),
106                true,
107                false,
108                PerfEventDiffable::new(&e.0, *options.perf_exclude_kernel),
109            ))));
110        }
111        Stats {
112            gc_count: AtomicUsize::new(0),
113            total_time: t,
114            #[cfg(feature = "perf_counter")]
115            perfmon,
116            shared,
117            counters: Mutex::new(counters),
118            time_to_yield,
119            pause_time,
120        }
121    }
122
123    /// Record a "time-to-yield" sample: the time elapsed between MMTk successfully requesting a
124    /// GC pause and the point all mutators have stopped for that pause.
125    pub fn record_time_to_yield(&self, duration: Duration) {
126        self.time_to_yield
127            .lock()
128            .unwrap()
129            .record(duration.as_nanos() as u64);
130    }
131
132    /// Record a "pause time" sample: the duration mutators spent stopped for a GC pause, from
133    /// the point all mutators stopped to the point they are resumed.
134    pub fn record_pause_time(&self, duration: Duration) {
135        self.pause_time
136            .lock()
137            .unwrap()
138            .record(duration.as_nanos() as u64);
139    }
140
141    pub fn new_event_counter(
142        &self,
143        name: &str,
144        implicit_start: bool,
145        merge_phases: bool,
146    ) -> Arc<Mutex<EventCounter>> {
147        let mut guard = self.counters.lock().unwrap();
148        let counter = Arc::new(Mutex::new(EventCounter::new(
149            name.to_string(),
150            self.shared.clone(),
151            implicit_start,
152            merge_phases,
153        )));
154        guard.push(counter.clone());
155        counter
156    }
157
158    pub fn new_size_counter(
159        &self,
160        name: &str,
161        implicit_start: bool,
162        merge_phases: bool,
163    ) -> Mutex<SizeCounter> {
164        let u = self.new_event_counter(name, implicit_start, merge_phases);
165        let v = self.new_event_counter(&format!("{}.volume", name), implicit_start, merge_phases);
166        Mutex::new(SizeCounter::new(u, v))
167    }
168
169    pub fn new_timer(
170        &self,
171        name: &str,
172        implicit_start: bool,
173        merge_phases: bool,
174    ) -> Arc<Mutex<Timer>> {
175        let mut guard = self.counters.lock().unwrap();
176        let counter = Arc::new(Mutex::new(Timer::new(
177            name.to_string(),
178            self.shared.clone(),
179            implicit_start,
180            merge_phases,
181            MonotoneNanoTime {},
182        )));
183        guard.push(counter.clone());
184        counter
185    }
186
187    pub fn start_gc(&self) {
188        self.gc_count.fetch_add(1, Ordering::SeqCst);
189        if !self.get_gathering_stats() {
190            return;
191        }
192        let counters = self.counters.lock().unwrap();
193        for counter in &(*counters) {
194            counter.lock().unwrap().phase_change(self.get_phase());
195        }
196        self.shared.increment_phase();
197    }
198
199    pub fn end_gc(&self) {
200        if !self.get_gathering_stats() {
201            return;
202        }
203        let counters = self.counters.lock().unwrap();
204        for counter in &(*counters) {
205            counter.lock().unwrap().phase_change(self.get_phase());
206        }
207        self.shared.increment_phase();
208    }
209
210    pub fn print_stats<VM: VMBinding>(&self, mmtk: &'static MMTK<VM>) {
211        println!(
212            "============================ MMTk Statistics Totals ============================"
213        );
214        let scheduler_stat = mmtk.scheduler.statistics();
215        self.print_column_names(&scheduler_stat);
216        print!("{}\t", self.get_phase() / 2);
217        let counter = self.counters.lock().unwrap();
218        for iter in &(*counter) {
219            let c = iter.lock().unwrap();
220            if c.merge_phases() {
221                c.print_total(None);
222            } else {
223                c.print_total(Some(true));
224                print!("\t");
225                c.print_total(Some(false));
226            }
227            print!("\t");
228        }
229        for value in scheduler_stat.values() {
230            print!("{}\t", value);
231        }
232        println!();
233        print!("Total time: ");
234        self.total_time.lock().unwrap().print_total(None);
235        println!(" ms");
236        println!("------------------------------ End MMTk Statistics -----------------------------")
237    }
238
239    pub fn print_column_names(&self, scheduler_stat: &HashMap<String, String>) {
240        print!("GC\t");
241        let counter = self.counters.lock().unwrap();
242        for iter in &(*counter) {
243            let c = iter.lock().unwrap();
244            if c.merge_phases() {
245                print!("{}\t", c.name());
246            } else {
247                print!("{}.other\t{}.stw\t", c.name(), c.name());
248            }
249        }
250        for name in scheduler_stat.keys() {
251            print!("{}\t", name);
252        }
253        println!();
254    }
255
256    pub fn start_all(&self) {
257        let counters = self.counters.lock().unwrap();
258        if self.get_gathering_stats() {
259            panic!("calling Stats.startAll() while stats running");
260        }
261        self.shared.set_gathering_stats(true);
262
263        for c in &(*counters) {
264            let mut ctr = c.lock().unwrap();
265            if ctr.implicitly_start() {
266                ctr.start();
267            }
268        }
269    }
270
271    pub fn stop_all<VM: VMBinding>(&self, mmtk: &'static MMTK<VM>) {
272        self.stop_all_counters();
273        self.print_stats(mmtk);
274    }
275
276    fn stop_all_counters(&self) {
277        let counters = self.counters.lock().unwrap();
278        for c in &(*counters) {
279            c.lock().unwrap().stop();
280        }
281        self.shared.set_gathering_stats(false);
282    }
283
284    fn get_phase(&self) -> usize {
285        self.shared.get_phase()
286    }
287
288    pub fn get_gathering_stats(&self) -> bool {
289        self.shared.get_gathering_stats()
290    }
291}