pub struct Sampler { /* private fields */ }
Expand description
A sampled perf event.
A sampler for a sampler perf event consists of two things: a Counter
,
and a memory-mapped ring buffer into which the kernel periodically writes
events. The specific event is configured on construction and can vary from
changes to the memory mapping associated with a process, to sampling call
stacks, to getting the output from a bpf program running in the kernel, and
more.
This sampler type provides direct access to the bytes written by the kernel
without doing any parsing of the emitted records. To actually read the
involved fields you will need to parse them yourself. See the
perf_event_open
man page for documentation on how the sample records
are represented in memory.
Implementations§
Source§impl Sampler
impl Sampler
Sourcepub fn into_counter(self) -> Counter
pub fn into_counter(self) -> Counter
Convert this sampler back into a counter.
This will close the ringbuffer associated with the sampler.
Sourcepub fn as_counter(&self) -> &Counter
pub fn as_counter(&self) -> &Counter
Access the underlying counter for this sampler.
Sourcepub fn as_counter_mut(&mut self) -> &mut Counter
pub fn as_counter_mut(&mut self) -> &mut Counter
Mutably access the underlying counter for this sampler.
Sourcepub fn next_record(&mut self) -> Option<Record<'_>>
pub fn next_record(&mut self) -> Option<Record<'_>>
Read the next record from the ring buffer.
This method does not block. If you want blocking behaviour, use
next_blocking
instead.
It is possible to get readiness notifications for when events are
present in the ring buffer (e.g. for async code). See the documentation
on the perf_event_open
manpage for details on how to do this.
Sourcepub fn next_blocking(&mut self, timeout: Option<Duration>) -> Option<Record<'_>>
pub fn next_blocking(&mut self, timeout: Option<Duration>) -> Option<Record<'_>>
Read the next record from the ring buffer. This method will block (with an optional timeout) until a new record is available.
If this sampler is only enabled for a single process and that process
exits, this method will return None
even if no timeout is passed.
Note that this only works on Linux 3.18 and above.
§Panics
This method will panic if an unexpected error is returned from
libc::poll
. There are only two cases where this can happen:
- the current process has run out of file descriptors, or,
- the kernel couldn’t allocate memory for internal poll datastructures.
Sourcepub fn read_user(&self) -> UserReadData
pub fn read_user(&self) -> UserReadData
Read the value of this counter directly from userspace.
Some CPU architectures allow performance counters to be read directly from userspace without having to go through the kernel. This can be much faster than a normal counter read but the tradeoff is that can only be done under certain conditions.
This method allows you to read the counter value, time_enabled
, and
time_running
without going through the kernel, if allowed by the
combination of architecture, kernel, and counter. time_enabled
and
time_running
are always read but will be less accurate on
architectures that do not provide a timestamp counter readable from
userspace.
§Restrictions
In order for counter values to be read using this method the following must be true:
- the CPU architecture must support reading counters from userspace,
- the counter must be recording for the current process,
- perf-event2 must have support for the relevant CPU architecture, and,
- the counter must correspond to a hardware counter.
Note that, despite the above being true, the kernel may still not
support userspace reads for other reasons. Hardware
events should
usually be supported but anything beyond that is unlikely. See the
supported architectures table below to see which are supported by
perf-event2.
Accurate timestamps also require that the kernel, CPU, and perf-event2 support them. They have similar restrictions to counter reads and will just return the base values set by the kernel otherwise. These will may be somewhat accurate but are likely to be out-of-date.
§Supported Architectures
Architecture | Counter Read | Timestamp Read |
---|---|---|
x86/x86_64 | yes | yes |
If you would like to add support for a new architecture here please submit a PR!
Methods from Deref<Target = 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 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.