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