use fxprof_processed_profile::{
CategoryHandle, Frame, FrameFlags, FrameInfo, MarkerFieldFlags, MarkerFieldFormat,
MarkerTiming, ProcessHandle, Profile, StaticSchemaMarker, StaticSchemaMarkerField,
StringHandle, ThreadHandle, Timestamp,
};
use crate::shared::context_switch::ThreadContextSwitchData;
use crate::shared::timestamp_converter::TimestampConverter;
pub struct Cpus {
start_time: Timestamp,
process_handle: ProcessHandle,
combined_thread_handle: ThreadHandle,
cpus: Vec<Cpu>,
idle_frame_label: FrameInfo,
}
pub struct Cpu {
pub name: StringHandle,
pub thread_handle: ThreadHandle,
pub context_switch_data: ThreadContextSwitchData,
current_tid: Option<(i32, StringHandle, u64)>,
}
impl Cpu {
pub fn new(name: StringHandle, thread_handle: ThreadHandle) -> Self {
Self {
name,
thread_handle,
context_switch_data: Default::default(),
current_tid: None,
}
}
#[allow(clippy::too_many_arguments)]
pub fn notify_switch_in_for_marker(
&mut self,
tid: i32,
thread_name: StringHandle,
timestamp: u64,
converter: &TimestampConverter,
thread_handles: &[ThreadHandle],
profile: &mut Profile,
) {
let previous_tid =
std::mem::replace(&mut self.current_tid, Some((tid, thread_name, timestamp)));
if let Some((_previous_tid, previous_thread_name, switch_in_timestamp)) = previous_tid {
let start_timestamp = converter.convert_time(switch_in_timestamp);
let end_timestamp = converter.convert_time(timestamp);
let timing = MarkerTiming::Interval(start_timestamp, end_timestamp);
for thread_handle in thread_handles {
profile.add_marker(
*thread_handle,
timing.clone(),
ThreadNameMarkerForCpuTrack(self.name, previous_thread_name),
);
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn notify_switch_out_for_marker(
&mut self,
tid: i32, timestamp: u64,
converter: &TimestampConverter,
thread_handles: &[ThreadHandle], thread_handle: ThreadHandle, preempted: bool,
profile: &mut Profile,
) {
let previous_tid = self.current_tid.take();
if let Some((previous_tid, previous_thread_name, switch_in_timestamp)) = previous_tid {
let start_timestamp = converter.convert_time(switch_in_timestamp);
let end_timestamp = converter.convert_time(timestamp);
let timing = MarkerTiming::Interval(start_timestamp, end_timestamp);
for thread_handle in thread_handles {
profile.add_marker(
*thread_handle,
timing.clone(),
ThreadNameMarkerForCpuTrack(self.name, previous_thread_name),
);
}
let switch_out_reason = match preempted {
true => profile.intern_string("preempted"),
false => profile.intern_string("blocked"),
};
profile.add_marker(
thread_handle,
timing.clone(),
OnCpuMarkerForThreadTrack {
cpu_name: self.name,
switch_out_reason,
},
);
if previous_tid != tid {
}
} else {
}
}
}
impl Cpus {
pub fn new(start_time: Timestamp, profile: &mut Profile) -> Self {
let process_handle = profile.add_process("CPU", 0, start_time);
let combined_thread_handle = profile.add_thread(process_handle, 0, start_time, true);
let idle_string = profile.intern_string("<Idle>");
let idle_frame_label = FrameInfo {
frame: Frame::Label(idle_string),
category_pair: CategoryHandle::OTHER.into(),
flags: FrameFlags::empty(),
};
Self {
start_time,
process_handle,
combined_thread_handle,
cpus: Vec::new(),
idle_frame_label,
}
}
pub fn combined_thread_handle(&self) -> ThreadHandle {
self.combined_thread_handle
}
pub fn idle_frame_label(&self) -> FrameInfo {
self.idle_frame_label.clone()
}
pub fn get_mut(&mut self, cpu: usize, profile: &mut Profile) -> &mut Cpu {
while self.cpus.len() <= cpu {
let i = self.cpus.len();
let thread = profile.add_thread(self.process_handle, i as u32, self.start_time, false);
let name = format!("CPU {i}");
profile.set_thread_name(thread, &name);
self.cpus
.push(Cpu::new(profile.intern_string(&name), thread));
}
&mut self.cpus[cpu]
}
}
#[derive(Debug, Clone)]
pub struct ThreadNameMarkerForCpuTrack(pub StringHandle, pub StringHandle);
impl StaticSchemaMarker for ThreadNameMarkerForCpuTrack {
const UNIQUE_MARKER_TYPE_NAME: &'static str = "ContextSwitch";
const CHART_LABEL: Option<&'static str> = Some("{marker.data.thread}");
const TOOLTIP_LABEL: Option<&'static str> = Some("{marker.data.thread}");
const TABLE_LABEL: Option<&'static str> = Some("{marker.name} - {marker.data.thread}");
const FIELDS: &'static [StaticSchemaMarkerField] = &[StaticSchemaMarkerField {
key: "thread",
label: "Thread",
format: MarkerFieldFormat::String,
flags: MarkerFieldFlags::SEARCHABLE,
}];
fn name(&self, _profile: &mut Profile) -> StringHandle {
self.0
}
fn category(&self, _profile: &mut Profile) -> CategoryHandle {
CategoryHandle::OTHER
}
fn string_field_value(&self, _field_index: u32) -> StringHandle {
self.1
}
fn number_field_value(&self, _field_index: u32) -> f64 {
unreachable!()
}
}
#[derive(Debug, Clone)]
pub struct OnCpuMarkerForThreadTrack {
cpu_name: StringHandle,
switch_out_reason: StringHandle,
}
impl StaticSchemaMarker for OnCpuMarkerForThreadTrack {
const UNIQUE_MARKER_TYPE_NAME: &'static str = "OnCpu";
const CHART_LABEL: Option<&'static str> = Some("{marker.data.cpu}");
const TOOLTIP_LABEL: Option<&'static str> = Some("{marker.data.cpu}");
const TABLE_LABEL: Option<&'static str> =
Some("{marker.name} - {marker.data.cpu}, switch-out reason: {marker.data.outwhy}");
const FIELDS: &'static [StaticSchemaMarkerField] = &[
StaticSchemaMarkerField {
key: "cpu",
label: "CPU",
format: MarkerFieldFormat::String,
flags: MarkerFieldFlags::SEARCHABLE,
},
StaticSchemaMarkerField {
key: "outwhy",
label: "Switch-out reason",
format: MarkerFieldFormat::String,
flags: MarkerFieldFlags::SEARCHABLE,
},
];
fn name(&self, profile: &mut Profile) -> StringHandle {
profile.intern_string("Running on CPU")
}
fn category(&self, _profile: &mut Profile) -> CategoryHandle {
CategoryHandle::OTHER
}
fn string_field_value(&self, field_index: u32) -> StringHandle {
match field_index {
0 => self.cpu_name,
1 => self.switch_out_reason,
_ => unreachable!(),
}
}
fn number_field_value(&self, _field_index: u32) -> f64 {
unreachable!()
}
}