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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
use std::io;

use crate::events::Software;
use crate::{Builder, Counter, GroupData, ReadFormat};

/// A group of counters that can be managed as a unit.
///
/// A `Group` represents a group of [`Counter`]s that can be enabled,
/// disabled, reset, or read as a single atomic operation. This is necessary if
/// you want to compare counter values, produce ratios, and so on, since those
/// operations are only meaningful on counters that cover exactly the same
/// period of execution.
///
/// A `Counter` is placed in a group when it is created via the
/// [`Builder::build_with_group`] method. A `Group`'s [`read`] method returns
/// values of all its member counters at once as a [`GroupData`] value, which
/// can be indexed by `Counter` to retrieve a specific value.
///
/// The lifetime of a `Group` and its associated `Counter`s are independent:
/// you can drop them in any order and they will continue to work. A `Counter`
/// will continue to work after the `Group` is dropped. If a `Counter` is
/// dropped first then it will simply be removed from the `Group`.
///
/// Enabling or disabling a `Group` affects each `Counter` that belongs to it.
/// Subsequent reads from the `Counter` will not reflect activity while the
/// `Group` was disabled, unless the `Counter` is re-enabled individually.
///
/// ## Limits on group size
///
/// Hardware counters are implemented using special-purpose registers on the
/// processor, of which there are only a fixed number. (For example, an Intel
/// high-end laptop processor from 2015 has four such registers per virtual
/// processor.) Without using groups, if you request more hardware counters than
/// the processor can actually support, a complete count isn't possible, but the
/// kernel will rotate the processor's real registers amongst the measurements
/// you've requested to at least produce a sample.
///
/// But since the point of a counter group is that its members all cover exactly
/// the same period of time, this tactic can't be applied to support large
/// groups. If the kernel cannot schedule a group, its counters remain zero. I
/// think you can detect this situation by comparing the group's
/// [`time_enabled`] and [`time_running`] values. If the [`pinned`] option is
/// set then you will also be able to detect this by [`read`] returning an error
/// with kind [`UnexpectedEof`].
///
/// According to the `perf_list(1)` man page, you may be able to free up a
/// hardware counter by disabling the kernel's NMI watchdog, which reserves one
/// for detecting kernel hangs:
///
/// ```text
/// $ echo 0 > /proc/sys/kernel/nmi_watchdog
/// ```
///
/// You can reenable the watchdog when you're done like this:
///
/// ```text
/// $ echo 1 > /proc/sys/kernel/nmi_watchdog
/// ```
///
/// [`read`]: Self::read
/// [`pinned`]: Builder::pinned
/// [`UnexpectedEof`]: io::ErrorKind::UnexpectedEof
///
/// # Examples
/// Compute the average cycles-per-instruction (CPI) for a call to `println!`:
/// ```
/// use perf_event::events::Hardware;
/// use perf_event::{Builder, Group};
///
/// let mut group = Group::new()?;
/// let cycles = group.add(&Builder::new(Hardware::CPU_CYCLES))?;
/// let insns = group.add(&Builder::new(Hardware::INSTRUCTIONS))?;
///
/// let vec = (0..=51).collect::<Vec<_>>();
///
/// group.enable()?;
/// println!("{:?}", vec);
/// group.disable()?;
///
/// let counts = group.read()?;
/// println!(
///     "cycles / instructions: {} / {} ({:.2} cpi)",
///     counts[&cycles],
///     counts[&insns],
///     (counts[&cycles] as f64 / counts[&insns] as f64)
/// );
/// # std::io::Result::Ok(())
/// ```
///
/// [`read`]: Group::read
/// [`time_enabled`]: GroupData::time_enabled
/// [`time_running`]: GroupData::time_running
pub struct Group(pub(crate) Counter);

impl Group {
    /// Construct a new, empty `Group`.
    ///
    /// The resulting `Group` is only suitable for observing the current process
    /// on any CPU. If you need to build a `Group` with different settings you
    /// will need to use [`Builder::build_group`].
    pub fn new() -> io::Result<Group> {
        Self::builder().build_group()
    }

    /// Construct a [Builder] preconfigured for creating a `Group`.
    ///
    /// Specifically, this creates a builder with the [`Software::DUMMY`] event
    /// and with [`read_format`] set to `GROUP | ID | TOTAL_TIME_ENABLED |
    /// TOTAL_TIME_RUNNING`. If you override [`read_format`] you will need to
    /// ensure that [`ReadFormat::GROUP`] is set, otherwise [`build_group`] will
    /// return an error.
    ///
    /// Note that any counter added to this group must observe the same set of
    /// CPUs and processes as the group itself. That means if you configure the
    /// group to observe a single CPU then the members of the group must also be
    /// configured to only observe a single CPU, the same applies when choosing
    /// target processes. Failing to follow this will result in an error when
    /// adding the counter to the group.
    ///
    /// [`read_format`]: Builder::read_format
    /// [`build_group`]: Builder::build_group
    pub fn builder() -> Builder<'static> {
        let mut builder = Builder::new(Software::DUMMY);
        builder.read_format(
            ReadFormat::GROUP
                | ReadFormat::TOTAL_TIME_ENABLED
                | ReadFormat::TOTAL_TIME_RUNNING
                | ReadFormat::ID,
        );

        builder
    }

    /// Access the internal counter for this group.
    pub fn as_counter(&self) -> &Counter {
        &self.0
    }

    /// Mutably access the internal counter for this group.
    pub fn as_counter_mut(&mut self) -> &mut Counter {
        &mut self.0
    }

    /// Convert this `Group` into its internal counter.
    pub fn into_counter(self) -> Counter {
        self.0
    }

    /// Return this group's kernel-assigned unique id.
    pub fn id(&self) -> u64 {
        self.0.id()
    }

    /// Enable all counters in this `Group`.
    pub fn enable(&mut self) -> io::Result<()> {
        self.0.enable_group()
    }

    /// Disable all counters in this `Group`
    pub fn disable(&mut self) -> io::Result<()> {
        self.0.disable_group()
    }

    /// Reset the value of all counters in this `Group` to zero.
    pub fn reset(&mut self) -> io::Result<()> {
        self.0.reset_group()
    }

    /// Construct a new counter as a part of this group.
    ///
    /// # Example
    /// ```
    /// use perf_event::events::Hardware;
    /// use perf_event::{Builder, Group};
    ///
    /// let mut group = Group::new()?;
    /// let counter = group.add(&Builder::new(Hardware::INSTRUCTIONS).any_cpu());
    /// #
    /// # std::io::Result::Ok(())
    /// ```
    pub fn add(&mut self, builder: &Builder) -> io::Result<Counter> {
        builder.build_with_group(self)
    }

    /// Return the values of all the `Counter`s in this `Group` as a
    /// [`GroupData`] value.
    ///
    /// A [`GroupData`] value is a map from specific `Counter`s to their values.
    /// You can find a specific `Counter`'s value by indexing:
    ///
    /// ```
    /// # use perf_event::events::Software;
    /// # const RHOMBUS_INCLINATIONS: Software = Software::DUMMY;
    /// # const TAXI_MEDALLIONS: Software = Software::DUMMY;
    /// #
    /// use perf_event::{Builder, Group};
    ///
    /// let mut group = Group::new()?;
    /// let counter1 = Builder::new(RHOMBUS_INCLINATIONS).build_with_group(&mut group)?;
    /// let counter2 = Builder::new(TAXI_MEDALLIONS).build_with_group(&mut group)?;
    /// // ...
    /// let counts = group.read()?;
    /// println!(
    ///     "Rhombus inclinations per taxi medallion: {} / {} ({:.0}%)",
    ///     counts[&counter1],
    ///     counts[&counter2],
    ///     (counts[&counter1] as f64 / counts[&counter2] as f64) * 100.0
    /// );
    /// # std::io::Result::Ok(())
    /// ```
    pub fn read(&mut self) -> io::Result<GroupData> {
        let mut data = self.0.read_group()?;
        data.skip_group();
        Ok(data)
    }
}

impl AsRef<Counter> for &'_ Group {
    fn as_ref(&self) -> &Counter {
        &self.0
    }
}

impl AsMut<Counter> for &'_ mut Group {
    fn as_mut(&mut self) -> &mut Counter {
        &mut self.0
    }
}