153 lines
5.4 KiB
Rust
153 lines
5.4 KiB
Rust
use std::{borrow::Cow, collections::HashMap};
|
|
|
|
use fxprof_processed_profile::{
|
|
CategoryHandle, CpuDelta, Frame, FrameFlags, FrameInfo, MarkerTiming, ProcessHandle, Profile,
|
|
ReferenceTimestamp, SamplingInterval, StackHandle, StaticSchemaMarker, StringHandle,
|
|
ThreadHandle, Timestamp,
|
|
};
|
|
|
|
use crate::profiler::state::{ProgramState, RESET_CODE, StackFrame};
|
|
|
|
pub struct Recording {
|
|
profile: Profile,
|
|
process: ProcessHandle,
|
|
threads: HashMap<u16, ThreadHandle>,
|
|
now: u64,
|
|
}
|
|
|
|
impl Recording {
|
|
pub fn new(state: &ProgramState) -> Self {
|
|
let reference_timestamp = ReferenceTimestamp::from_millis_since_unix_epoch(0.0);
|
|
let interval = SamplingInterval::from_hz(20_000_000.0);
|
|
let mut profile = Profile::new(state.name(), reference_timestamp, interval);
|
|
|
|
let process =
|
|
profile.add_process(state.name(), 1, Timestamp::from_nanos_since_reference(0));
|
|
|
|
let lib = profile.add_lib(state.library_info().clone());
|
|
profile.add_lib_mapping(process, lib, 0x00000000, 0xffffffff, 0);
|
|
|
|
let mut me = Self {
|
|
profile,
|
|
process,
|
|
threads: HashMap::new(),
|
|
now: 0,
|
|
};
|
|
me.track_elapsed_cycles(state, 0);
|
|
me
|
|
}
|
|
|
|
pub fn track_elapsed_cycles(&mut self, state: &ProgramState, cycles: u32) {
|
|
self.now += cycles as u64;
|
|
let timestamp = Timestamp::from_nanos_since_reference(self.now * 50);
|
|
let weight = 1;
|
|
|
|
let active_code = if let Some((code, frames)) = state.current_stack() {
|
|
let thread = *self.threads.entry(code).or_insert_with(|| {
|
|
let process = self.process;
|
|
let tid = code as u32;
|
|
let start_time = Timestamp::from_nanos_since_reference(self.now * 50);
|
|
let is_main = code == RESET_CODE;
|
|
let thread = self.profile.add_thread(process, tid, start_time, is_main);
|
|
self.profile
|
|
.set_thread_name(thread, &thread_name_for_code(code));
|
|
thread
|
|
});
|
|
|
|
let stack = self.handle_for_stack(thread, frames);
|
|
let cpu_delta = CpuDelta::from_nanos((self.now - cycles as u64) * 50);
|
|
self.profile
|
|
.add_sample(thread, timestamp, stack, cpu_delta, weight);
|
|
Some(code)
|
|
} else {
|
|
None
|
|
};
|
|
for (code, thread) in &self.threads {
|
|
if active_code == Some(*code) {
|
|
continue;
|
|
}
|
|
self.profile
|
|
.add_sample_same_stack_zero_cpu(*thread, timestamp, weight);
|
|
}
|
|
}
|
|
|
|
pub fn track_marker(&mut self, name: Cow<'static, str>) {
|
|
let Some(thread) = self.threads.get(&RESET_CODE) else {
|
|
return;
|
|
};
|
|
let timing = MarkerTiming::Instant(Timestamp::from_nanos_since_reference(self.now * 50));
|
|
let marker = SimpleMarker(name);
|
|
self.profile.add_marker(*thread, timing, marker);
|
|
}
|
|
|
|
pub fn finish(self) -> Vec<u8> {
|
|
serde_json::to_vec(&self.profile).expect("could not serialize profile")
|
|
}
|
|
|
|
fn handle_for_stack(
|
|
&mut self,
|
|
thread: ThreadHandle,
|
|
frames: &[StackFrame],
|
|
) -> Option<StackHandle> {
|
|
let frames = frames
|
|
.iter()
|
|
.map(|f| {
|
|
let frame = match f {
|
|
StackFrame::Address(address) => Frame::InstructionPointer(*address as u64),
|
|
StackFrame::Label(label) => Frame::Label(self.profile.intern_string(label)),
|
|
};
|
|
FrameInfo {
|
|
frame,
|
|
category_pair: CategoryHandle::OTHER.into(),
|
|
flags: FrameFlags::empty(),
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
self.profile.intern_stack_frames(thread, frames.into_iter())
|
|
}
|
|
}
|
|
|
|
struct SimpleMarker(Cow<'static, str>);
|
|
|
|
impl StaticSchemaMarker for SimpleMarker {
|
|
const UNIQUE_MARKER_TYPE_NAME: &'static str = "Simple";
|
|
const FIELDS: &'static [fxprof_processed_profile::StaticSchemaMarkerField] = &[];
|
|
|
|
fn name(&self, profile: &mut Profile) -> StringHandle {
|
|
profile.intern_string(&self.0)
|
|
}
|
|
|
|
fn category(&self, _profile: &mut Profile) -> CategoryHandle {
|
|
CategoryHandle::OTHER
|
|
}
|
|
|
|
fn string_field_value(&self, _field_index: u32) -> StringHandle {
|
|
unreachable!()
|
|
}
|
|
|
|
fn number_field_value(&self, _field_index: u32) -> f64 {
|
|
unreachable!()
|
|
}
|
|
}
|
|
|
|
fn thread_name_for_code(code: u16) -> std::borrow::Cow<'static, str> {
|
|
match code {
|
|
RESET_CODE => "Main".into(),
|
|
0xffd0 => "Duplexed exception".into(),
|
|
0xfe40 => "VIP interrupt".into(),
|
|
0xfe30 => "Communication interrupt".into(),
|
|
0xfe20 => "Game pak interrupt".into(),
|
|
0xfe10 => "Timer interrupt".into(),
|
|
0xfe00 => "Game pad interrupt".into(),
|
|
0xffc0 => "Address trap".into(),
|
|
0xffa0..0xffc0 => format!("Trap (vector {})", code - 0xffa0).into(),
|
|
0xff90 => "Illegal opcode exception".into(),
|
|
0xff80 => "Zero division exception".into(),
|
|
0xff60 => "Floating-point reserved operand exception".into(),
|
|
0xff70 => "Floating-point invalid operation exception".into(),
|
|
0xff68 => "Floating-point zero division exception".into(),
|
|
0xff64 => "Floating-point overflow exception".into(),
|
|
other => format!("Unrecognized handler (0x{other:04x})").into(),
|
|
}
|
|
}
|