1use itertools::Itertools;
2use std::{
3 cmp::Ordering,
4 collections::{BTreeSet, VecDeque},
5 sync::Arc,
6};
7
8use crate::{FrameData, FrameSinkId, ScopeCollection};
9
10#[derive(Clone)]
12pub struct FrameView {
13 recent: VecDeque<OrderedByIndex>,
15 max_recent: usize,
16
17 slowest_by_index: BTreeSet<OrderedByIndex>,
18 slowest_by_duration: BTreeSet<OrderedByDuration>,
19 max_slow: usize,
20
21 pack_frames: bool,
25
26 stats: FrameStats,
28
29 scope_collection: ScopeCollection,
31}
32
33impl Default for FrameView {
34 fn default() -> Self {
35 let max_recent = 1_000;
36 let max_slow = 256;
37
38 Self {
39 recent: VecDeque::with_capacity(max_recent),
40 max_recent,
41 slowest_by_index: BTreeSet::new(),
42 slowest_by_duration: BTreeSet::new(),
43 max_slow,
44 pack_frames: true,
45 stats: Default::default(),
46 scope_collection: Default::default(),
47 }
48 }
49}
50
51impl FrameView {
52 pub fn is_empty(&self) -> bool {
54 self.recent.is_empty() && self.slowest_by_duration.is_empty()
55 }
56
57 pub fn scope_collection(&self) -> &ScopeCollection {
60 &self.scope_collection
61 }
62
63 pub fn add_frame(&mut self, new_frame: Arc<FrameData>) {
65 for new_scope in &new_frame.scope_delta {
67 self.scope_collection.insert(new_scope.clone());
68 }
69
70 if let Some(last) = self.recent.iter().last()
71 && new_frame.frame_index() <= last.0.frame_index()
72 {
73 self.stats.clear();
77 self.recent.clear();
78 self.slowest_by_index.clear();
79 self.slowest_by_duration.clear();
80 }
81
82 if let Some(last) = self.recent.iter().last() {
83 if self.pack_frames {
86 last.0.pack();
87 }
88
89 self.stats.add(&last.0);
90 }
91
92 let add_to_slowest = if self.slowest_by_duration.len() < self.max_slow {
93 true
94 } else if let Some(fastest_of_the_slow) = self.slowest_by_duration.iter().last() {
95 new_frame.duration_ns() > fastest_of_the_slow.0.duration_ns()
96 } else {
97 false
98 };
99
100 if add_to_slowest {
101 self.add_slow_frame(&new_frame);
102 }
103
104 self.add_recent_frame(&new_frame);
105 }
106
107 fn add_slow_frame(&mut self, new_frame: &Arc<FrameData>) {
108 assert_eq!(self.slowest_by_duration.len(), self.slowest_by_index.len());
109
110 self.slowest_by_duration
111 .insert(OrderedByDuration(new_frame.clone()));
112 self.slowest_by_index
113 .insert(OrderedByIndex(new_frame.clone()));
114
115 while self.slowest_by_duration.len() > self.max_slow {
116 if let Some(removed_frame) = self.slowest_by_duration.pop_last() {
117 let removed_by_index = OrderedByIndex(removed_frame.0.clone());
118 self.slowest_by_index.remove(&removed_by_index);
119
120 if self.recent.binary_search(&removed_by_index).is_err() {
122 self.stats.remove(&removed_frame.0);
123 }
124 }
125 }
126 }
127
128 fn add_recent_frame(&mut self, new_frame: &Arc<FrameData>) {
129 self.recent.push_back(OrderedByIndex(new_frame.clone()));
130
131 while self.recent.len() > self.max_recent {
132 if let Some(removed_frame) = self.recent.pop_front() {
133 if !self.slowest_by_index.contains(&removed_frame) {
135 self.stats.remove(&removed_frame.0);
136 }
137 }
138 }
139 }
140
141 pub fn latest_frame(&self) -> Option<Arc<FrameData>> {
143 self.recent.back().map(|f| f.0.clone())
144 }
145
146 pub fn latest_frames(&self, n: usize) -> impl Iterator<Item = &Arc<FrameData>> {
148 self.recent.iter().rev().take(n).rev().map(|f| &f.0)
153 }
154
155 pub fn recent_frames(&self) -> impl Iterator<Item = &Arc<FrameData>> {
157 self.recent.iter().map(|f| &f.0)
158 }
159
160 pub fn slowest_frames_chronological(&self) -> impl Iterator<Item = &Arc<FrameData>> {
163 self.slowest_by_index.iter().map(|f| &f.0)
164 }
165
166 pub fn all_uniq(&self) -> impl Iterator<Item = &Arc<FrameData>> {
168 Itertools::merge(self.recent.iter(), self.slowest_by_index.iter())
169 .dedup()
170 .map(|f| &f.0)
171 }
172
173 pub fn clear_slowest(&mut self) {
175 for frame in self.slowest_by_index.iter() {
176 self.stats.remove(&frame.0);
177 }
178
179 self.slowest_by_duration.clear();
180 self.slowest_by_index.clear();
181 }
182
183 pub fn max_recent(&self) -> usize {
185 self.max_recent
186 }
187
188 pub fn set_max_recent(&mut self, max_recent: usize) {
190 self.max_recent = max_recent;
191 }
192
193 pub fn max_slow(&self) -> usize {
195 self.max_slow
196 }
197
198 pub fn set_max_slow(&mut self, max_slow: usize) {
200 self.max_slow = max_slow;
201 }
202
203 pub fn pack_frames(&self) -> bool {
205 self.pack_frames
206 }
207
208 pub fn set_pack_frames(&mut self, pack_frames: bool) {
211 self.pack_frames = pack_frames;
212 }
213
214 pub fn stats(&self) -> FrameStats {
218 self.stats
219 }
220
221 pub fn stats_full(&self) -> FrameStats {
223 FrameStats::from_frames(self.all_uniq().map(Arc::as_ref))
224 }
225
226 #[cfg(feature = "serialization")]
228 #[cfg(not(target_arch = "wasm32"))] pub fn write(&self, write: &mut impl std::io::Write) -> anyhow::Result<()> {
230 write.write_all(b"PUF0")?;
231
232 for frame in self.all_uniq() {
233 frame.write_into(None, write)?;
234 }
235 Ok(())
236 }
237
238 #[cfg(feature = "serialization")]
240 pub fn read(read: &mut impl std::io::Read) -> anyhow::Result<Self> {
241 let mut magic = [0_u8; 4];
242 read.read_exact(&mut magic)?;
243 if &magic != b"PUF0" {
244 anyhow::bail!("Expected .puffin magic header of 'PUF0', found {magic:?}");
245 }
246
247 let mut slf = Self {
248 max_recent: usize::MAX,
249 ..Default::default()
250 };
251 while let Some(frame) = FrameData::read_next(read)? {
252 slf.add_frame(frame.into());
253 }
254
255 Ok(slf)
256 }
257}
258
259pub fn select_slowest(frames: &[Arc<FrameData>], max: usize) -> Vec<Arc<FrameData>> {
263 let mut slowest: std::collections::BinaryHeap<OrderedByDuration> = Default::default();
264 for frame in frames {
265 slowest.push(OrderedByDuration(frame.clone()));
266 while slowest.len() > max {
267 slowest.pop();
268 }
269 }
270 let mut slowest: Vec<_> = slowest.drain().map(|x| x.0).collect();
271 slowest.sort_by_key(|frame| frame.frame_index());
272 slowest
273}
274
275#[derive(Clone)]
278struct OrderedByDuration(Arc<FrameData>);
279
280impl Ord for OrderedByDuration {
281 fn cmp(&self, other: &Self) -> Ordering {
282 match self.0.duration_ns().cmp(&other.0.duration_ns()).reverse() {
283 Ordering::Equal => self.0.frame_index().cmp(&other.0.frame_index()),
284 res => res,
285 }
286 }
287}
288
289impl PartialOrd for OrderedByDuration {
290 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
291 Some(self.cmp(other))
292 }
293}
294
295impl Eq for OrderedByDuration {}
296
297impl PartialEq for OrderedByDuration {
298 fn eq(&self, other: &Self) -> bool {
299 self.0.duration_ns() == other.0.duration_ns()
300 && self.0.frame_index() == other.0.frame_index()
301 }
302}
303
304#[derive(Clone)]
306struct OrderedByIndex(Arc<FrameData>);
307
308impl Ord for OrderedByIndex {
309 fn cmp(&self, other: &Self) -> Ordering {
310 match self.0.frame_index().cmp(&other.0.frame_index()) {
311 Ordering::Equal => self.0.duration_ns().cmp(&other.0.duration_ns()),
312 res => res,
313 }
314 }
315}
316
317impl PartialOrd for OrderedByIndex {
318 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
319 Some(self.cmp(other))
320 }
321}
322
323impl Eq for OrderedByIndex {}
324
325impl PartialEq for OrderedByIndex {
326 fn eq(&self, other: &Self) -> bool {
327 self.0.frame_index() == other.0.frame_index()
328 && self.0.duration_ns() == other.0.duration_ns()
329 }
330}
331
332pub struct GlobalFrameView {
336 sink_id: FrameSinkId,
337 view: Arc<parking_lot::Mutex<FrameView>>,
338}
339
340impl Default for GlobalFrameView {
341 fn default() -> Self {
342 let view = Arc::new(parking_lot::Mutex::new(FrameView::default()));
343 let view_clone = view.clone();
344 let mut profiler = crate::GlobalProfiler::lock();
345 let sink_id = profiler.add_sink(Box::new(move |frame| {
346 view_clone.lock().add_frame(frame);
347 }));
348 profiler.emit_scope_snapshot();
351
352 Self { sink_id, view }
353 }
354}
355
356impl Drop for GlobalFrameView {
357 fn drop(&mut self) {
358 crate::GlobalProfiler::lock().remove_sink(self.sink_id);
359 }
360}
361
362impl GlobalFrameView {
363 pub fn sink_id(&self) -> FrameSinkId {
365 self.sink_id
366 }
367
368 pub fn lock(&self) -> parking_lot::MutexGuard<'_, FrameView> {
370 self.view.lock()
371 }
372}
373
374fn stats_entry(frame: &FrameData) -> (usize, usize) {
377 let info = frame.packing_info();
378 (
379 info.packed_size.unwrap_or(0) + info.unpacked_size.unwrap_or(0),
380 info.unpacked_size.is_some() as usize,
381 )
382}
383
384#[derive(Clone, Copy, Debug, Default)]
386pub struct FrameStats {
387 unique_frames: usize,
388 total_ram_used: usize,
389 unpacked_frames: usize,
390}
391
392impl FrameStats {
393 pub fn from_frames<'a>(frames: impl Iterator<Item = &'a FrameData>) -> Self {
395 let mut stats = FrameStats::default();
396
397 for frame in frames {
398 stats.add(frame);
399 }
400
401 stats
402 }
403
404 fn add(&mut self, frame: &FrameData) {
406 let (total, unpacked) = stats_entry(frame);
407
408 self.total_ram_used = self.total_ram_used.saturating_add(total);
409 self.unpacked_frames = self.unpacked_frames.saturating_add(unpacked);
410 self.unique_frames = self.unique_frames.saturating_add(1);
411 }
412
413 fn remove(&mut self, frame: &FrameData) {
415 let (total, unpacked) = stats_entry(frame);
416
417 self.total_ram_used = self.total_ram_used.saturating_sub(total);
418 self.unpacked_frames = self.unpacked_frames.saturating_sub(unpacked);
419 self.unique_frames = self.unique_frames.saturating_sub(1);
420 }
421
422 pub fn frames(&self) -> usize {
424 self.unique_frames
425 }
426
427 pub fn unpacked_frames(&self) -> usize {
429 self.unpacked_frames
430 }
431
432 pub fn bytes_of_ram_used(&self) -> usize {
434 self.total_ram_used
435 }
436
437 pub fn clear(&mut self) {
439 self.unique_frames = 0;
440 self.unpacked_frames = 0;
441 self.total_ram_used = 0;
442 }
443}
444
445#[cfg(all(test, feature = "serialization"))]
446mod tests {
447 use super::FrameView;
448
449 #[test]
450 fn read_pfd4_file() -> anyhow::Result<()> {
451 let mut file = std::fs::File::open("tests/data/capture_PFD4.puffin")?;
452 let _ = FrameView::read(&mut file)?;
453 Ok(())
454 }
455
456 #[test]
457 fn read_pfd3_file() -> anyhow::Result<()> {
458 let mut file = std::fs::File::open("tests/data/capture_PFD3.puffin")?;
459 let _ = FrameView::read(&mut file)?;
460 Ok(())
461 }
462
463 #[test]
464 fn read_pfd2_file() -> anyhow::Result<()> {
465 let mut file = std::fs::File::open("tests/data/capture_PFD2.puffin")?;
466 let _ = FrameView::read(&mut file)?;
467 Ok(())
468 }
469
470 #[test]
471 fn read_pfd1_file() -> anyhow::Result<()> {
472 let mut file = std::fs::File::open("tests/data/capture_PFD1.puffin")?;
473 let _ = FrameView::read(&mut file)?;
474 Ok(())
475 }
476}