use std::sync::Arc;
use crate::{FrameData, FrameSinkId, ScopeCollection};
#[derive(Clone)]
pub struct FrameView {
recent: std::collections::VecDeque<Arc<FrameData>>,
max_recent: usize,
slowest: std::collections::BinaryHeap<OrderedByDuration>,
max_slow: usize,
pack_frames: bool,
scope_collection: ScopeCollection,
}
impl Default for FrameView {
fn default() -> Self {
let max_recent = 60 * 60 * 5;
let max_slow = 256;
Self {
recent: std::collections::VecDeque::with_capacity(max_recent),
max_recent,
slowest: std::collections::BinaryHeap::with_capacity(max_slow),
max_slow,
pack_frames: true,
scope_collection: Default::default(),
}
}
}
impl FrameView {
pub fn is_empty(&self) -> bool {
self.recent.is_empty() && self.slowest.is_empty()
}
pub fn scope_collection(&self) -> &ScopeCollection {
&self.scope_collection
}
pub fn add_frame(&mut self, new_frame: Arc<FrameData>) {
for new_scope in &new_frame.scope_delta {
self.scope_collection.insert(new_scope.clone());
}
if let Some(last) = self.recent.back() {
if new_frame.frame_index() <= last.frame_index() {
self.recent.clear();
self.slowest.clear();
}
}
let add_to_slowest = if self.slowest.len() < self.max_slow {
true
} else if let Some(fastest_of_the_slow) = self.slowest.peek() {
new_frame.duration_ns() > fastest_of_the_slow.0.duration_ns()
} else {
false
};
if add_to_slowest {
self.slowest.push(OrderedByDuration(new_frame.clone()));
while self.slowest.len() > self.max_slow {
self.slowest.pop();
}
}
if let Some(last) = self.recent.back() {
if self.pack_frames {
last.pack();
}
}
self.recent.push_back(new_frame);
while self.recent.len() > self.max_recent {
self.recent.pop_front();
}
}
pub fn latest_frame(&self) -> Option<Arc<FrameData>> {
self.recent.back().cloned()
}
pub fn latest_frames(&self, n: usize) -> Vec<Arc<FrameData>> {
self.recent.iter().rev().take(n).rev().cloned().collect()
}
pub fn recent_frames(&self) -> impl Iterator<Item = &Arc<FrameData>> {
self.recent.iter()
}
pub fn slowest_frames_chronological(&self) -> Vec<Arc<FrameData>> {
let mut frames: Vec<_> = self.slowest.iter().map(|f| f.0.clone()).collect();
frames.sort_by_key(|frame| frame.frame_index());
frames
}
pub fn all_uniq(&self) -> Vec<Arc<FrameData>> {
let mut all: Vec<_> = self.slowest.iter().map(|f| f.0.clone()).collect();
all.extend(self.recent.iter().cloned());
all.sort_by_key(|frame| frame.frame_index());
all.dedup_by_key(|frame| frame.frame_index());
all
}
pub fn clear_slowest(&mut self) {
self.slowest.clear();
}
pub fn max_recent(&self) -> usize {
self.max_recent
}
pub fn set_max_recent(&mut self, max_recent: usize) {
self.max_recent = max_recent;
}
pub fn max_slow(&self) -> usize {
self.max_slow
}
pub fn set_max_slow(&mut self, max_slow: usize) {
self.max_slow = max_slow;
}
pub fn pack_frames(&self) -> bool {
self.pack_frames
}
pub fn set_pack_frames(&mut self, pack_frames: bool) {
self.pack_frames = pack_frames;
}
#[cfg(feature = "serialization")]
#[cfg(not(target_arch = "wasm32"))] pub fn write(&self, write: &mut impl std::io::Write) -> anyhow::Result<()> {
write.write_all(b"PUF0")?;
let slowest_frames = self.slowest.iter().map(|f| &f.0);
let mut frames: Vec<_> = slowest_frames.chain(self.recent.iter()).collect();
frames.sort_by_key(|frame| frame.frame_index());
frames.dedup_by_key(|frame| frame.frame_index());
for frame in frames {
frame.write_into(&self.scope_collection, false, write)?;
}
Ok(())
}
#[cfg(feature = "serialization")]
pub fn read(read: &mut impl std::io::Read) -> anyhow::Result<Self> {
let mut magic = [0_u8; 4];
read.read_exact(&mut magic)?;
if &magic != b"PUF0" {
anyhow::bail!("Expected .puffin magic header of 'PUF0', found {:?}", magic);
}
let mut slf = Self {
max_recent: usize::MAX,
..Default::default()
};
while let Some(frame) = FrameData::read_next(read)? {
slf.add_frame(frame.into());
}
Ok(slf)
}
}
pub fn select_slowest(frames: &[Arc<FrameData>], max: usize) -> Vec<Arc<FrameData>> {
let mut slowest: std::collections::BinaryHeap<OrderedByDuration> = Default::default();
for frame in frames {
slowest.push(OrderedByDuration(frame.clone()));
while slowest.len() > max {
slowest.pop();
}
}
let mut slowest: Vec<_> = slowest.drain().map(|x| x.0).collect();
slowest.sort_by_key(|frame| frame.frame_index());
slowest
}
#[derive(Clone)]
struct OrderedByDuration(Arc<FrameData>);
impl PartialEq for OrderedByDuration {
fn eq(&self, other: &Self) -> bool {
self.0.duration_ns().eq(&other.0.duration_ns())
}
}
impl Eq for OrderedByDuration {}
impl PartialOrd for OrderedByDuration {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for OrderedByDuration {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.duration_ns().cmp(&other.0.duration_ns()).reverse()
}
}
pub struct GlobalFrameView {
sink_id: FrameSinkId,
view: Arc<parking_lot::Mutex<FrameView>>,
}
impl Default for GlobalFrameView {
fn default() -> Self {
let view = Arc::new(parking_lot::Mutex::new(FrameView::default()));
let view_clone = view.clone();
let sink_id = crate::GlobalProfiler::lock().add_sink(Box::new(move |frame| {
view_clone.lock().add_frame(frame);
}));
Self { sink_id, view }
}
}
impl Drop for GlobalFrameView {
fn drop(&mut self) {
crate::GlobalProfiler::lock().remove_sink(self.sink_id);
}
}
impl GlobalFrameView {
pub fn sink_id(&self) -> FrameSinkId {
self.sink_id
}
pub fn lock(&self) -> parking_lot::MutexGuard<'_, FrameView> {
self.view.lock()
}
}