use fxprof_processed_profile::{
CategoryPairHandle, Frame, FrameFlags, FrameInfo, Profile, StringHandle,
};
fn should_elide_frames<const N: usize>(full_len: usize) -> Option<(usize, usize)> {
if full_len >= N + N + N / 2 {
let elided_count = (full_len - N - N / 2) / N * N;
Some((N, elided_count))
} else {
None
}
}
#[test]
fn test_should_elide_frames() {
assert_eq!(should_elide_frames::<100>(100), None);
assert_eq!(should_elide_frames::<100>(220), None);
assert_eq!(should_elide_frames::<100>(249), None);
assert_eq!(should_elide_frames::<100>(250), Some((100, 100)));
assert_eq!(should_elide_frames::<100>(290), Some((100, 100)));
assert_eq!(should_elide_frames::<100>(349), Some((100, 100)));
assert_eq!(should_elide_frames::<100>(350), Some((100, 200)));
assert_eq!(should_elide_frames::<100>(352), Some((100, 200)));
assert_eq!(should_elide_frames::<100>(449), Some((100, 200)));
assert_eq!(should_elide_frames::<100>(450), Some((100, 300)));
}
pub struct StackDepthLimitingFrameIter<I: Iterator<Item = FrameInfo>> {
inner: I,
category: CategoryPairHandle,
state: StackDepthLimitingFrameIterState,
}
enum StackDepthLimitingFrameIterState {
BeforeElidedPiece {
index: usize,
first_elided_frame: usize,
elision_frame_string: StringHandle,
first_frame_after_elision: usize,
},
AtElidedPiece {
elision_frame_string: StringHandle,
first_frame_after_elision: usize,
},
NoMoreElision {
index: usize,
},
}
impl<I: Iterator<Item = FrameInfo>> StackDepthLimitingFrameIter<I> {
pub fn new(profile: &mut Profile, iter: I, category: CategoryPairHandle) -> Self {
let full_len = iter.size_hint().0;
let state = if let Some((first_elided_frame, elided_count)) =
should_elide_frames::<200>(full_len)
{
let first_frame_after_elision = first_elided_frame + elided_count;
let elision_frame_string =
profile.intern_string(&format!("({elided_count} frames elided)"));
StackDepthLimitingFrameIterState::BeforeElidedPiece {
index: 0,
first_elided_frame,
elision_frame_string,
first_frame_after_elision,
}
} else {
StackDepthLimitingFrameIterState::NoMoreElision { index: 0 }
};
Self {
inner: iter,
category,
state,
}
}
}
impl<I: Iterator<Item = FrameInfo>> Iterator for StackDepthLimitingFrameIter<I> {
type Item = FrameInfo;
fn next(&mut self) -> Option<Self::Item> {
let frame = match &mut self.state {
StackDepthLimitingFrameIterState::BeforeElidedPiece {
index,
first_elided_frame,
elision_frame_string,
first_frame_after_elision,
} => {
let frame = self.inner.next()?;
*index += 1;
if *index == *first_elided_frame {
while *index < *first_frame_after_elision {
let _frame = self.inner.next()?;
*index += 1;
}
self.state = StackDepthLimitingFrameIterState::AtElidedPiece {
elision_frame_string: *elision_frame_string,
first_frame_after_elision: *first_frame_after_elision,
};
}
frame
}
StackDepthLimitingFrameIterState::AtElidedPiece {
elision_frame_string,
first_frame_after_elision,
} => {
let frame = Frame::Label(*elision_frame_string);
self.state = StackDepthLimitingFrameIterState::NoMoreElision {
index: *first_frame_after_elision,
};
return Some(FrameInfo {
frame,
category_pair: self.category,
flags: FrameFlags::empty(),
});
}
StackDepthLimitingFrameIterState::NoMoreElision { index } => {
let frame = self.inner.next()?;
*index += 1;
frame
}
};
Some(frame)
}
}