perf_event/
group_data.rs

1use std::fmt;
2use std::iter::FusedIterator;
3use std::time::Duration;
4
5use crate::{Builder, Counter, Group, ReadFormat};
6
7used_in_docs!(Group);
8used_in_docs!(Builder);
9used_in_docs!(ReadFormat);
10
11/// A collection of counts from a group of counters.
12///
13/// This is the type returned by [`Counter::read_group`] and [`Group::read`].
14/// You can index it with a reference to a specific [`Counter`]:
15///
16/// ```
17/// use perf_event::events::Hardware;
18/// use perf_event::{Builder, Group};
19///
20/// let mut group = Group::new()?;
21/// let cycles = group.add(&Builder::new(Hardware::CPU_CYCLES))?;
22/// let insns = group.add(&Builder::new(Hardware::INSTRUCTIONS))?;
23/// let counts = group.read()?;
24/// println!(
25///     "cycles / instructions: {} / {} ({:.2} cpi)",
26///     counts[&cycles],
27///     counts[&insns],
28///     (counts[&cycles] as f64 / counts[&insns] as f64)
29/// );
30/// # std::io::Result::Ok(())
31/// ```
32///
33/// Or you can iterate over the results it contains:
34///
35/// ```
36/// # fn main() -> std::io::Result<()> {
37/// # use perf_event::Group;
38/// # let counts = Group::new()?.read()?;
39/// for entry in &counts {
40///     println!("Counter id {} has value {}", entry.id(), entry.value());
41/// }
42/// # Ok(())
43/// # }
44/// ```
45///
46/// The `id` values produced by this iteration are internal identifiers assigned
47/// by the kernel. You can use the [`Counter::id`] method to find a
48/// specific counter's id.
49///
50/// For some kinds of events, the kernel may use timesharing to give all
51/// counters access to scarce hardware registers. You can see how long a group
52/// was actually running versus the entire time it was enabled using the
53/// `time_enabled` and `time_running` methods:
54///
55/// ```
56/// # use perf_event::{Builder, Group};
57/// # use perf_event::events::Software;
58/// # let mut group = Group::new()?;
59/// # let insns = group.add(&Builder::new(Software::DUMMY))?;
60/// # let counts = group.read()?;
61/// let scale =
62///     counts.time_enabled().unwrap().as_secs_f64() / counts.time_running().unwrap().as_secs_f64();
63/// for entry in &counts {
64///     let value = entry.value() as f64 * scale;
65///
66///     print!("Counter id {} has value {}", entry.id(), value as u64);
67///     if scale > 1.0 {
68///         print!(" (estimated)");
69///     }
70///     println!();
71/// }
72/// # std::io::Result::Ok(())
73/// ```
74pub struct GroupData {
75    pub(crate) data: crate::data::ReadGroup<'static>,
76    // We need a set of values that we can actually reference for the index implementation.
77    values: Vec<u64>,
78    should_skip: bool,
79}
80
81impl GroupData {
82    pub(crate) fn new(data: crate::data::ReadGroup<'static>) -> Self {
83        let values = data.entries().map(|entry| entry.value()).collect();
84
85        Self {
86            data,
87            values,
88            should_skip: false,
89        }
90    }
91
92    /// Return the number of counters this `Counts` holds results for.
93    pub fn len(&self) -> usize {
94        self.iter().len()
95    }
96
97    /// Whether this `GroupData` is empty.
98    pub fn is_empty(&self) -> bool {
99        self.len() == 0
100    }
101
102    /// The duration for which the group was enabled.
103    ///
104    /// This will only be present if [`TOTAL_TIME_ENABLED`] was passed to
105    /// [`read_format`].
106    ///
107    /// [`TOTAL_TIME_ENABLED`]: ReadFormat::TOTAL_TIME_ENABLED
108    /// [`read_format`]: Builder::read_format
109    pub fn time_enabled(&self) -> Option<Duration> {
110        self.data.time_enabled().map(Duration::from_nanos)
111    }
112
113    /// The duration for which the group was scheduled on the CPU.
114    ///
115    /// This will only be present if [`TOTAL_TIME_RUNNING`] was passed to
116    /// [`read_format`].
117    ///
118    /// [`TOTAL_TIME_RUNNING`]: ReadFormat::TOTAL_TIME_RUNNING
119    /// [`read_format`]: Builder::read_format
120    pub fn time_running(&self) -> Option<Duration> {
121        self.data.time_running().map(Duration::from_nanos)
122    }
123
124    /// Get the entry for `member` in `self`, or `None` if `member` is not
125    /// present.
126    ///
127    /// `member` can be either a `Counter` or a `Group`.
128    ///
129    /// If you know the counter is in the group then you can access the count
130    /// via indexing.
131    /// ```
132    /// use perf_event::events::Hardware;
133    /// use perf_event::{Builder, Group};
134    ///
135    /// let mut group = Group::new()?;
136    /// let instrs = Builder::new(Hardware::INSTRUCTIONS).build_with_group(&mut group)?;
137    /// let cycles = Builder::new(Hardware::CPU_CYCLES).build_with_group(&mut group)?;
138    /// group.enable()?;
139    /// // ...
140    /// let counts = group.read()?;
141    /// let instrs = counts[&instrs];
142    /// let cycles = counts[&cycles];
143    /// # std::io::Result::Ok(())
144    /// ```
145    pub fn get(&self, member: &Counter) -> Option<GroupEntry> {
146        self.data.get_by_id(member.id()).map(GroupEntry)
147    }
148
149    /// Return an iterator over all entries in `self`.
150    ///
151    /// For compatibility reasons, if the [`Group`] this was
152    ///
153    /// # Example
154    /// ```
155    /// # use perf_event::Group;
156    /// # let mut group = Group::new()?;
157    /// let data = group.read()?;
158    /// for entry in &data {
159    ///     println!("Counter with id {} has value {}", entry.id(), entry.value());
160    /// }
161    /// # std::io::Result::Ok(())
162    /// ```
163    pub fn iter(&self) -> GroupIter {
164        let mut iter = self.iter_with_group();
165        if self.should_skip {
166            let _ = iter.next();
167        }
168        iter
169    }
170
171    fn iter_with_group(&self) -> GroupIter {
172        GroupIter(self.data.entries())
173    }
174
175    /// Mark that the first counter in this group is a `Group` and should not be
176    /// included when iterating over this `GroupData` instance.
177    pub(crate) fn skip_group(&mut self) {
178        self.should_skip = true;
179    }
180}
181
182impl std::ops::Index<&Counter> for GroupData {
183    type Output = u64;
184
185    fn index(&self, ctr: &Counter) -> &u64 {
186        let (index, _) = self
187            .iter_with_group()
188            .enumerate()
189            .find(|(_, entry)| entry.id() == ctr.id())
190            .unwrap_or_else(|| panic!("group contained no counter with id {}", ctr.id()));
191
192        &self.values[index]
193    }
194}
195
196impl fmt::Debug for GroupData {
197    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
198        struct GroupEntries<'a>(&'a GroupData);
199
200        impl fmt::Debug for GroupEntries<'_> {
201            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202                f.debug_list().entries(self.0.iter()).finish()
203            }
204        }
205
206        let mut dbg = fmt.debug_struct("GroupData");
207
208        if let Some(time_enabled) = self.time_enabled() {
209            dbg.field("time_enabled", &time_enabled.as_nanos());
210        }
211
212        if let Some(time_running) = self.time_running() {
213            dbg.field("time_running", &time_running.as_nanos());
214        }
215
216        dbg.field("entries", &GroupEntries(self));
217        dbg.finish()
218    }
219}
220
221impl<'a> IntoIterator for &'a GroupData {
222    type IntoIter = GroupIter<'a>;
223    type Item = <GroupIter<'a> as Iterator>::Item;
224
225    fn into_iter(self) -> Self::IntoIter {
226        self.iter()
227    }
228}
229
230/// Individual entry for a counter returned by [`Group::read`].
231#[derive(Copy, Clone)]
232pub struct GroupEntry(pub(crate) crate::data::GroupEntry);
233
234impl GroupEntry {
235    /// The value of the counter.
236    pub fn value(&self) -> u64 {
237        self.0.value()
238    }
239
240    /// The kernel-assigned unique id of the counter that was read.
241    pub fn id(&self) -> u64 {
242        self.0.id().expect("group entry did not have an id")
243    }
244
245    /// The number of lost samples for this event.
246    pub fn lost(&self) -> Option<u64> {
247        self.0.lost()
248    }
249}
250
251impl fmt::Debug for GroupEntry {
252    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253        let mut dbg = f.debug_struct("GroupEntry");
254        dbg.field("value", &self.value());
255        dbg.field("id", &self.id());
256
257        if let Some(lost) = self.lost() {
258            dbg.field("lost", &lost);
259        }
260
261        dbg.finish_non_exhaustive()
262    }
263}
264
265/// Iterator over the entries contained within [`GroupData`].
266#[derive(Clone)]
267pub struct GroupIter<'a>(crate::data::GroupIter<'a>);
268
269impl<'a> Iterator for GroupIter<'a> {
270    type Item = GroupEntry;
271
272    fn next(&mut self) -> Option<Self::Item> {
273        self.0.next().map(GroupEntry)
274    }
275
276    fn size_hint(&self) -> (usize, Option<usize>) {
277        self.0.size_hint()
278    }
279
280    fn count(self) -> usize {
281        self.0.count()
282    }
283
284    fn nth(&mut self, n: usize) -> Option<Self::Item> {
285        self.0.nth(n).map(GroupEntry)
286    }
287
288    fn last(mut self) -> Option<Self::Item> {
289        self.next_back()
290    }
291}
292
293impl<'a> DoubleEndedIterator for GroupIter<'a> {
294    fn next_back(&mut self) -> Option<Self::Item> {
295        self.0.next_back().map(GroupEntry)
296    }
297
298    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
299        self.0.nth_back(n).map(GroupEntry)
300    }
301}
302
303impl<'a> ExactSizeIterator for GroupIter<'a> {
304    fn len(&self) -> usize {
305        self.0.len()
306    }
307}
308
309impl<'a> FusedIterator for GroupIter<'a> {}