1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//! Counter for work packets
//!
//! Provides an abstraction and implementations of counters for collecting
//! work-packet level statistics
//!
//! See [`crate::util::statistics`] for collecting statistics over a GC cycle
use std::time::Instant;

/// Common struct for different work counters
///
/// Stores the total, min and max of counter readings
#[derive(Copy, Clone, Debug)]
pub(super) struct WorkCounterBase {
    pub(super) total: f64,
    pub(super) min: f64,
    pub(super) max: f64,
}

/// Make [`WorkCounter`] trait objects cloneable
pub(super) trait WorkCounterClone {
    /// Clone the object
    fn clone_box(&self) -> Box<dyn WorkCounter>;
}

impl<T: 'static + WorkCounter + Clone> WorkCounterClone for T {
    fn clone_box(&self) -> Box<dyn WorkCounter> {
        Box::new(self.clone())
    }
}

/// An abstraction of work counters
///
/// Use for trait objects, as we have might have types of work counters for
/// the same work packet and the types are not statically known.
/// The overhead should be negligible compared with the cost of executing
/// a work packet.
pub(super) trait WorkCounter: WorkCounterClone + std::fmt::Debug + Send {
    // TODO: consolidate with crate::util::statistics::counter::Counter;
    /// Start the counter
    fn start(&mut self);
    /// Stop the counter
    fn stop(&mut self);
    /// Name of counter
    fn name(&self) -> String;
    /// Return a reference to [`WorkCounterBase`]
    fn get_base(&self) -> &WorkCounterBase;
}

impl Clone for Box<dyn WorkCounter> {
    fn clone(&self) -> Box<dyn WorkCounter> {
        self.clone_box()
    }
}

impl Default for WorkCounterBase {
    fn default() -> Self {
        WorkCounterBase {
            total: 0.0,
            min: f64::INFINITY,
            max: f64::NEG_INFINITY,
        }
    }
}

impl WorkCounterBase {
    /// Merge two [`WorkCounterBase`], keep the semantics of the fields,
    /// and return a new object
    pub(super) fn merge(&self, other: &Self) -> Self {
        let min = self.min.min(other.min);
        let max = self.max.max(other.max);
        let total = self.total + other.total;
        WorkCounterBase { total, min, max }
    }

    /// Merge two [`WorkCounterBase`], modify the current object in place,
    /// and keep the semantics of the fields
    pub(super) fn merge_inplace(&mut self, other: &Self) {
        self.min = self.min.min(other.min);
        self.max = self.max.max(other.max);
        self.total += other.total;
    }

    /// Update the object based on a single value
    pub(super) fn merge_val(&mut self, val: f64) {
        self.min = self.min.min(val);
        self.max = self.max.max(val);
        self.total += val;
    }
}

/// Measure the durations of work packets
///
/// Timing is based on [`Instant`]
#[derive(Copy, Clone, Debug)]
pub(super) struct WorkDuration {
    base: WorkCounterBase,
    start_value: Option<Instant>,
    running: bool,
}

impl WorkDuration {
    pub(super) fn new() -> Self {
        WorkDuration {
            base: Default::default(),
            start_value: None,
            running: false,
        }
    }
}

impl WorkCounter for WorkDuration {
    fn start(&mut self) {
        self.start_value = Some(Instant::now());
        self.running = true;
    }

    fn stop(&mut self) {
        let duration = self.start_value.unwrap().elapsed().as_nanos() as f64;
        self.base.merge_val(duration);
    }

    fn name(&self) -> String {
        "time".to_owned()
    }

    fn get_base(&self) -> &WorkCounterBase {
        &self.base
    }
}

#[cfg(feature = "perf_counter")]
mod perf_event {
    //! Measure the perf events of work packets
    //!
    //! This is built on top of libpfm4.
    //! The events to measure are parsed from MMTk option `perf_events`
    use super::*;
    use libc::{c_int, pid_t};
    use pfm::PerfEvent;
    use std::fmt;

    /// Work counter for perf events
    #[derive(Clone)]
    pub struct WorkPerfEvent {
        base: WorkCounterBase,
        running: bool,
        event_name: String,
        pe: PerfEvent,
    }

    impl WorkPerfEvent {
        /// Create a work counter
        ///
        /// See `perf_event_open` for more details on `pid` and `cpu`
        /// Examples:
        /// 0, -1 measures the calling thread on all CPUs
        /// -1, 0 measures all threads on CPU 0
        /// -1, -1 is invalid
        pub fn new(name: &str, pid: pid_t, cpu: c_int, exclude_kernel: bool) -> WorkPerfEvent {
            let mut pe = PerfEvent::new(name, false)
                .unwrap_or_else(|_| panic!("Failed to create perf event {}", name));
            pe.set_exclude_kernel(exclude_kernel as u64);
            pe.open(pid, cpu)
                .unwrap_or_else(|_| panic!("Failed to open perf event {}", name));
            WorkPerfEvent {
                base: Default::default(),
                running: false,
                event_name: name.to_string(),
                pe,
            }
        }
    }

    impl fmt::Debug for WorkPerfEvent {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            f.debug_struct("WorkPerfEvent")
                .field("base", &self.base)
                .field("running", &self.running)
                .field("event_name", &self.event_name)
                .finish()
        }
    }

    impl WorkCounter for WorkPerfEvent {
        fn start(&mut self) {
            self.running = true;
            self.pe.reset().expect("Failed to reset perf event");
            self.pe.enable().expect("Failed to enable perf event");
        }
        fn stop(&mut self) {
            self.running = true;
            let perf_event_value = self.pe.read().unwrap();
            self.base.merge_val(perf_event_value.value as f64);
            // assert not multiplexing
            assert_eq!(perf_event_value.time_enabled, perf_event_value.time_running);
            self.pe.disable().expect("Failed to disable perf event");
        }
        fn name(&self) -> String {
            self.event_name.to_owned()
        }
        fn get_base(&self) -> &WorkCounterBase {
            &self.base
        }
    }
}

#[cfg(feature = "perf_counter")]
pub(super) use perf_event::WorkPerfEvent;