pub struct Counter { /* private fields */ }
Expand description
A counter for a single kernel or hardware event.
A counter represents a single performance monitoring counter. When building
the counter you select the event you would like it to count. Once the
counter is created, then you can enable or disable it, call its read
method to retrieve its current value, and reset it to zero.
§Groups
The kernel allows for counters to be grouped together. A group of counters will be scheduled onto the CPU as a unit. This allows you to directly compare the values collected by multiple counters.
There are two ways to go about working with groups:
- Use the
Group
type. It is not configurable but it makes groups easy to setup and use. - Pick one
Counter
to be a group leader, create the other counters withBuilder::build_with_group
and useenable_group
,disable_group
, andreset_group
on any of its members to control the group. To read all counters in the group at once you’ll need to create at least one counter withReadFormat::GROUP
so thatread_group
will read the entire group.
A counter represents a single performance monitoring counter. While
creating the counter - via Builder
- you select the event you would
like to count. Once the counter is created, then you can enable or disable
it, call its read
method to retrieve the current count (or counts if
it is a Group
), and reset it to zero.
Implementations§
Source§impl Counter
impl Counter
Sourcepub fn config(&self) -> &ParseConfig<Native>
pub fn config(&self) -> &ParseConfig<Native>
The ParseConfig
for this Counter
.
Sourcepub fn enable(&mut self) -> Result<()>
pub fn enable(&mut self) -> Result<()>
Allow this Counter
to begin counting its designated event.
This does not affect whatever value the Counter
had previously; new
events add to the current count. To clear a Counter
, use reset
.
Note that, depending on how it was configured, a counter may start off
enabled or be automatically enabled by the kernel when an event occurs.
For example, setting enable_on_exec
will cause this counter to be
automatically enabled when the current process calls execve(2)
.
If you want to enable all counters in the same group as this one then
use enable_group
instead.
§Examples
Enable an individual counter:
use perf_event::events::Hardware;
use perf_event::Builder;
let mut counter = Builder::new(Hardware::INSTRUCTIONS).build()?;
counter.enable()?;
// ...
assert_ne!(counter.read()?, 0);
Sourcepub fn enable_group(&mut self) -> Result<()>
pub fn enable_group(&mut self) -> Result<()>
Enable all counters in the same group as this one.
This does not affect whatever value the Counter
had previously; new
events add to the current count. To clear a counter group, use
reset_group
.
See enable
for the version that only applies to the current
counter.
§Examples
Enable all counters in a group:
use perf_event::events::Hardware;
use perf_event::{Builder, Group};
let mut group = Group::new()?;
let mut cycles = Builder::new(Hardware::CPU_CYCLES).build_with_group(&mut group)?;
group.enable()?;
// ...
assert_ne!(cycles.read()?, 0);
Sourcepub fn disable(&mut self) -> Result<()>
pub fn disable(&mut self) -> Result<()>
Make this Counter
stop counting its designated event.
This does not affect the value of this Counter
.
To disable all counters in the group use
disable_group
.
§Examples
Disable a single counter:
use perf_event::events::Hardware;
use perf_event::Builder;
let mut counter = Builder::new(Hardware::INSTRUCTIONS).build()?;
counter.enable()?;
// Counter is continuously updating
let val1 = counter.read()?;
let val2 = counter.read()?;
counter.disable()?;
// Counter is no longer updating
let val3 = counter.read()?;
let val4 = counter.read()?;
assert_ne!(val1, val2);
assert_eq!(val3, val4);
Sourcepub fn disable_group(&mut self) -> Result<()>
pub fn disable_group(&mut self) -> Result<()>
Disable all counters in the same group as this one.
This does not affect the counter values.
To disable only this counter use disable
.
Sourcepub fn reset(&mut self) -> Result<()>
pub fn reset(&mut self) -> Result<()>
Reset the value of this Counter
to zero.
To reset the value of all counters in the current group use
reset_group
.
§Examples
Reset a single counter
use perf_event::events::Hardware;
use perf_event::Builder;
let mut counter = Builder::new(Hardware::INSTRUCTIONS).build()?;
counter.enable()?;
// ...
counter.disable()?;
assert_ne!(counter.read()?, 0);
counter.reset()?;
assert_eq!(counter.read()?, 0);
Sourcepub fn reset_group(&mut self) -> Result<()>
pub fn reset_group(&mut self) -> Result<()>
Reset the value of all counters in the same group as this one to zero.
To only reset the value of this counter use reset
.
Sourcepub fn set_bpf(&mut self, bpf: RawFd) -> Result<()>
pub fn set_bpf(&mut self, bpf: RawFd) -> Result<()>
Attach an eBPF program to this counter.
This will only work if this counter was created as a kprobe tracepoint event.
This method corresponds to the IOC_SET_BPF
ioctl.
Sourcepub fn sampled(self, map_len: usize) -> Result<Sampler>
pub fn sampled(self, map_len: usize) -> Result<Sampler>
Map a buffer for samples from this counter, returning a Sampler
that can be used to access them.
There are some restrictions on the size of the mapped buffer. To
accomodate this map_len
will always be rounded up to the next
power-of-two multiple of the system page size. There will always
be at least two pages allocated for the ring buffer: one for the
control data structures, and one for actual data.
§Example
This example shows creating a sample to record mmap events within the current process. If you do this early enough, you can then track what libraries your process is loading.
use perf_event::events::Software;
use perf_event::Builder;
let mut sampler = Builder::new(Software::DUMMY)
.mmap(true)
.build()?
.sampled(128)?;
Source§impl Counter
impl Counter
Sourcepub fn read(&mut self) -> Result<u64>
pub fn read(&mut self) -> Result<u64>
Return this Counter
’s current value as a u64
.
Consider using read_full
or (if read_format has the required flags)
read_count_and_time
instead. There are limitations around how
many hardware counters can be on a single CPU at a time. If more
counters are requested than the hardware can support then the kernel
will timeshare them on the hardware. Looking at just the counter value
gives you no indication that this has happened.
If you would like to read the values for an entire group then you will
need to use read_group
(and set ReadFormat::GROUP
) instead.
§Errors
This function may return errors in the following notable cases:
ENOSPC
is returned if theread_format
that thisCounter
was built with does not match the format of the data. This can also occur ifread_format
contained options not supported by this crate.- If the counter is part of a group and was unable to be pinned to the
CPU then reading will return an error with kind
UnexpectedEof
.
Other errors are also possible under unexpected conditions (e.g. EBADF
if the file descriptor is closed).
§Example
use perf_event::events::Hardware;
use perf_event::Builder;
let mut counter = Builder::new(Hardware::INSTRUCTIONS).enabled(true).build()?;
let instrs = counter.read()?;
Sourcepub fn read_full(&mut self) -> Result<CounterData>
pub fn read_full(&mut self) -> Result<CounterData>
Return all data that this Counter
is configured to provide.
The exact fields that are returned within the CounterData
struct
depend on what was specified for read_format
when constructing this
counter. This method is the only one that gives access to all values
returned by the kernel.
If this Counter
was created with ReadFormat::GROUP
then this will
read the entire group but only return the data for this specific
counter.
§Errors
This function may return errors in the following notable cases:
ENOSPC
is returned if theread_format
that thisCounter
was built with does not match the format of the data. This can also occur ifread_format
contained options not supported by this crate.- If the counter is part of a group and was unable to be pinned to the
CPU then reading will return an error with kind
UnexpectedEof
.
Other errors are also possible under unexpected conditions (e.g. EBADF
if the file descriptor is closed).
§Example
use std::time::Duration;
use perf_event::events::Hardware;
use perf_event::{Builder, ReadFormat};
let mut counter = Builder::new(Hardware::INSTRUCTIONS)
.read_format(ReadFormat::TOTAL_TIME_RUNNING)
.enabled(true)
.build()?;
// ...
let data = counter.read_full()?;
let instructions = data.count();
let time_running = data.time_running().unwrap();
let ips = instructions as f64 / time_running.as_secs_f64();
println!("instructions/s: {ips}");
Sourcepub fn read_group(&mut self) -> Result<GroupData>
pub fn read_group(&mut self) -> Result<GroupData>
Read the values of all the counters in the current group.
Note that unless ReadFormat::GROUP
was specified when building this
Counter
this will only read the data for the current Counter
.
§Errors
This function may return errors in the following notable cases:
ENOSPC
is returned if theread_format
that thisCounter
was built with does not match the format of the data. This can also occur ifread_format
contained options not supported by this crate.- If the counter is part of a group and was unable to be pinned to the
CPU then reading will return an error with kind
UnexpectedEof
.
Other errors are also possible under unexpected conditions (e.g. EBADF
if the file descriptor is closed).
§Example
Compute the CPI for a region of code:
use perf_event::events::Hardware;
use perf_event::{Builder, ReadFormat};
let mut instrs = Builder::new(Hardware::INSTRUCTIONS)
.read_format(ReadFormat::GROUP)
.build()?;
let mut cycles = Builder::new(Hardware::CPU_CYCLES).build_with_group(&mut instrs)?;
instrs.enable_group()?;
// ...
instrs.disable_group()?;
let data = instrs.read_group()?;
let instrs = data[&instrs];
let cycles = data[&cycles];
println!("CPI: {}", cycles as f64 / instrs as f64);
Sourcepub fn read_count_and_time(&mut self) -> Result<CountAndTime>
pub fn read_count_and_time(&mut self) -> Result<CountAndTime>
Return this Counter
’s current value and timesharing data.
Some counters are implemented in hardware, and the processor can run only a fixed number of them at a time. If more counters are requested than the hardware can support, the kernel timeshares them on the hardware.
This method returns a CountAndTime
struct, whose count
field holds
the counter’s value, and whose time_enabled
and time_running
fields
indicate how long you had enabled the counter, and how long the counter
was actually scheduled on the processor. This lets you detect whether
the counter was timeshared, and adjust your use accordingly. Times
are reported in nanoseconds.
§Errors
See the man page for possible errors when reading from the
counter. This method will also return an error if read_format
does
not include both TOTAL_TIME_ENABLED
and TOTAL_TIME_RUNNING
.
§Example
let cat = counter.read_count_and_time()?;
if cat.time_running == 0 {
println!("No data collected.");
} else if cat.time_running < cat.time_enabled {
// Note: this way of scaling is accurate, but `u128` division
// is usually implemented in software, which may be slow.
println!(
"{} instructions (estimated)",
(cat.count as u128 * cat.time_enabled as u128 / cat.time_running as u128) as u64
);
} else {
println!("{} instructions", cat.count);
}
Note that Group
also has a read
method, which reads all
its member Counter
s’ values at once.