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
}
}