Struct Group

Source
pub struct Group(/* private fields */);
Expand description

A group of counters that can be managed as a unit.

A Group represents a group of Counters 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 Counters 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:

$ echo 0 > /proc/sys/kernel/nmi_watchdog

You can reenable the watchdog when you’re done like this:

$ echo 1 > /proc/sys/kernel/nmi_watchdog

§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)
);

Implementations§

Source§

impl Group

Source

pub fn new() -> Result<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.

Source

pub fn builder() -> Builder<'static>

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.

Source

pub fn as_counter(&self) -> &Counter

Access the internal counter for this group.

Source

pub fn as_counter_mut(&mut self) -> &mut Counter

Mutably access the internal counter for this group.

Source

pub fn into_counter(self) -> Counter

Convert this Group into its internal counter.

Source

pub fn id(&self) -> u64

Return this group’s kernel-assigned unique id.

Source

pub fn enable(&mut self) -> Result<()>

Enable all counters in this Group.

Source

pub fn disable(&mut self) -> Result<()>

Disable all counters in this Group

Source

pub fn reset(&mut self) -> Result<()>

Reset the value of all counters in this Group to zero.

Source

pub fn add(&mut self, builder: &Builder<'_>) -> Result<Counter>

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());
Source

pub fn read(&mut self) -> Result<GroupData>

Return the values of all the Counters in this Group as a GroupData value.

A GroupData value is a map from specific Counters to their values. You can find a specific Counter’s value by indexing:

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
);

Trait Implementations§

Source§

impl AsMut<Counter> for &mut Group

Source§

fn as_mut(&mut self) -> &mut Counter

Converts this type into a mutable reference of the (usually inferred) input type.
Source§

impl AsRef<Counter> for &Group

Source§

fn as_ref(&self) -> &Counter

Converts this type into a shared reference of the (usually inferred) input type.

Auto Trait Implementations§

§

impl Freeze for Group

§

impl RefUnwindSafe for Group

§

impl Send for Group

§

impl Sync for Group

§

impl Unpin for Group

§

impl UnwindSafe for Group

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> EventData for T
where T: Send + Sync,