perf_event/group.rs
1use std::io;
2
3use crate::events::Software;
4use crate::{Builder, Counter, GroupData, ReadFormat};
5
6/// A group of counters that can be managed as a unit.
7///
8/// A `Group` represents a group of [`Counter`]s that can be enabled,
9/// disabled, reset, or read as a single atomic operation. This is necessary if
10/// you want to compare counter values, produce ratios, and so on, since those
11/// operations are only meaningful on counters that cover exactly the same
12/// period of execution.
13///
14/// A `Counter` is placed in a group when it is created via the
15/// [`Builder::build_with_group`] method. A `Group`'s [`read`] method returns
16/// values of all its member counters at once as a [`GroupData`] value, which
17/// can be indexed by `Counter` to retrieve a specific value.
18///
19/// The lifetime of a `Group` and its associated `Counter`s are independent:
20/// you can drop them in any order and they will continue to work. A `Counter`
21/// will continue to work after the `Group` is dropped. If a `Counter` is
22/// dropped first then it will simply be removed from the `Group`.
23///
24/// Enabling or disabling a `Group` affects each `Counter` that belongs to it.
25/// Subsequent reads from the `Counter` will not reflect activity while the
26/// `Group` was disabled, unless the `Counter` is re-enabled individually.
27///
28/// ## Limits on group size
29///
30/// Hardware counters are implemented using special-purpose registers on the
31/// processor, of which there are only a fixed number. (For example, an Intel
32/// high-end laptop processor from 2015 has four such registers per virtual
33/// processor.) Without using groups, if you request more hardware counters than
34/// the processor can actually support, a complete count isn't possible, but the
35/// kernel will rotate the processor's real registers amongst the measurements
36/// you've requested to at least produce a sample.
37///
38/// But since the point of a counter group is that its members all cover exactly
39/// the same period of time, this tactic can't be applied to support large
40/// groups. If the kernel cannot schedule a group, its counters remain zero. I
41/// think you can detect this situation by comparing the group's
42/// [`time_enabled`] and [`time_running`] values. If the [`pinned`] option is
43/// set then you will also be able to detect this by [`read`] returning an error
44/// with kind [`UnexpectedEof`].
45///
46/// According to the `perf_list(1)` man page, you may be able to free up a
47/// hardware counter by disabling the kernel's NMI watchdog, which reserves one
48/// for detecting kernel hangs:
49///
50/// ```text
51/// $ echo 0 > /proc/sys/kernel/nmi_watchdog
52/// ```
53///
54/// You can reenable the watchdog when you're done like this:
55///
56/// ```text
57/// $ echo 1 > /proc/sys/kernel/nmi_watchdog
58/// ```
59///
60/// [`read`]: Self::read
61/// [`pinned`]: Builder::pinned
62/// [`UnexpectedEof`]: io::ErrorKind::UnexpectedEof
63///
64/// # Examples
65/// Compute the average cycles-per-instruction (CPI) for a call to `println!`:
66/// ```
67/// use perf_event::events::Hardware;
68/// use perf_event::{Builder, Group};
69///
70/// let mut group = Group::new()?;
71/// let cycles = group.add(&Builder::new(Hardware::CPU_CYCLES))?;
72/// let insns = group.add(&Builder::new(Hardware::INSTRUCTIONS))?;
73///
74/// let vec = (0..=51).collect::<Vec<_>>();
75///
76/// group.enable()?;
77/// println!("{:?}", vec);
78/// group.disable()?;
79///
80/// let counts = group.read()?;
81/// println!(
82/// "cycles / instructions: {} / {} ({:.2} cpi)",
83/// counts[&cycles],
84/// counts[&insns],
85/// (counts[&cycles] as f64 / counts[&insns] as f64)
86/// );
87/// # std::io::Result::Ok(())
88/// ```
89///
90/// [`read`]: Group::read
91/// [`time_enabled`]: GroupData::time_enabled
92/// [`time_running`]: GroupData::time_running
93pub struct Group(pub(crate) Counter);
94
95impl Group {
96 /// Construct a new, empty `Group`.
97 ///
98 /// The resulting `Group` is only suitable for observing the current process
99 /// on any CPU. If you need to build a `Group` with different settings you
100 /// will need to use [`Builder::build_group`].
101 pub fn new() -> io::Result<Group> {
102 Self::builder().build_group()
103 }
104
105 /// Construct a [Builder] preconfigured for creating a `Group`.
106 ///
107 /// Specifically, this creates a builder with the [`Software::DUMMY`] event
108 /// and with [`read_format`] set to `GROUP | ID | TOTAL_TIME_ENABLED |
109 /// TOTAL_TIME_RUNNING`. If you override [`read_format`] you will need to
110 /// ensure that [`ReadFormat::GROUP`] is set, otherwise [`build_group`] will
111 /// return an error.
112 ///
113 /// Note that any counter added to this group must observe the same set of
114 /// CPUs and processes as the group itself. That means if you configure the
115 /// group to observe a single CPU then the members of the group must also be
116 /// configured to only observe a single CPU, the same applies when choosing
117 /// target processes. Failing to follow this will result in an error when
118 /// adding the counter to the group.
119 ///
120 /// [`read_format`]: Builder::read_format
121 /// [`build_group`]: Builder::build_group
122 pub fn builder() -> Builder<'static> {
123 let mut builder = Builder::new(Software::DUMMY);
124 builder.read_format(
125 ReadFormat::GROUP
126 | ReadFormat::TOTAL_TIME_ENABLED
127 | ReadFormat::TOTAL_TIME_RUNNING
128 | ReadFormat::ID,
129 );
130
131 builder
132 }
133
134 /// Access the internal counter for this group.
135 pub fn as_counter(&self) -> &Counter {
136 &self.0
137 }
138
139 /// Mutably access the internal counter for this group.
140 pub fn as_counter_mut(&mut self) -> &mut Counter {
141 &mut self.0
142 }
143
144 /// Convert this `Group` into its internal counter.
145 pub fn into_counter(self) -> Counter {
146 self.0
147 }
148
149 /// Return this group's kernel-assigned unique id.
150 pub fn id(&self) -> u64 {
151 self.0.id()
152 }
153
154 /// Enable all counters in this `Group`.
155 pub fn enable(&mut self) -> io::Result<()> {
156 self.0.enable_group()
157 }
158
159 /// Disable all counters in this `Group`
160 pub fn disable(&mut self) -> io::Result<()> {
161 self.0.disable_group()
162 }
163
164 /// Reset the value of all counters in this `Group` to zero.
165 pub fn reset(&mut self) -> io::Result<()> {
166 self.0.reset_group()
167 }
168
169 /// Construct a new counter as a part of this group.
170 ///
171 /// # Example
172 /// ```
173 /// use perf_event::events::Hardware;
174 /// use perf_event::{Builder, Group};
175 ///
176 /// let mut group = Group::new()?;
177 /// let counter = group.add(&Builder::new(Hardware::INSTRUCTIONS).any_cpu());
178 /// #
179 /// # std::io::Result::Ok(())
180 /// ```
181 pub fn add(&mut self, builder: &Builder) -> io::Result<Counter> {
182 builder.build_with_group(self)
183 }
184
185 /// Return the values of all the `Counter`s in this `Group` as a
186 /// [`GroupData`] value.
187 ///
188 /// A [`GroupData`] value is a map from specific `Counter`s to their values.
189 /// You can find a specific `Counter`'s value by indexing:
190 ///
191 /// ```
192 /// # use perf_event::events::Software;
193 /// # const RHOMBUS_INCLINATIONS: Software = Software::DUMMY;
194 /// # const TAXI_MEDALLIONS: Software = Software::DUMMY;
195 /// #
196 /// use perf_event::{Builder, Group};
197 ///
198 /// let mut group = Group::new()?;
199 /// let counter1 = Builder::new(RHOMBUS_INCLINATIONS).build_with_group(&mut group)?;
200 /// let counter2 = Builder::new(TAXI_MEDALLIONS).build_with_group(&mut group)?;
201 /// // ...
202 /// let counts = group.read()?;
203 /// println!(
204 /// "Rhombus inclinations per taxi medallion: {} / {} ({:.0}%)",
205 /// counts[&counter1],
206 /// counts[&counter2],
207 /// (counts[&counter1] as f64 / counts[&counter2] as f64) * 100.0
208 /// );
209 /// # std::io::Result::Ok(())
210 /// ```
211 pub fn read(&mut self) -> io::Result<GroupData> {
212 let mut data = self.0.read_group()?;
213 data.skip_group();
214 Ok(data)
215 }
216}
217
218impl AsRef<Counter> for &'_ Group {
219 fn as_ref(&self) -> &Counter {
220 &self.0
221 }
222}
223
224impl AsMut<Counter> for &'_ mut Group {
225 fn as_mut(&mut self) -> &mut Counter {
226 &mut self.0
227 }
228}