Actually produce profiler files
This commit is contained in:
+13
-127
@@ -1,15 +1,19 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
thread,
|
||||
};
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use anyhow::Result;
|
||||
use tokio::{select, sync::mpsc};
|
||||
use wholesym::{SymbolManager, SymbolMap};
|
||||
|
||||
use crate::emulator::{EmulatorClient, EmulatorCommand, ProfileEvent, SimEvent, SimId};
|
||||
use recording::Recording;
|
||||
use state::ProgramState;
|
||||
|
||||
mod recording;
|
||||
mod state;
|
||||
mod symbols;
|
||||
|
||||
pub struct Profiler {
|
||||
sim_id: SimId,
|
||||
@@ -182,14 +186,6 @@ enum RecordingAction {
|
||||
Cancel,
|
||||
}
|
||||
|
||||
struct Recording {}
|
||||
|
||||
impl Recording {
|
||||
fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
struct ProfilerSession {
|
||||
program: Option<ProgramState>,
|
||||
recording: Option<Recording>,
|
||||
@@ -210,8 +206,8 @@ impl ProfilerSession {
|
||||
}
|
||||
|
||||
fn track_elapsed_cycles(&mut self, cycles: u32) -> Result<()> {
|
||||
if let Some(program) = &mut self.program {
|
||||
program.track_elapsed_cycles(cycles)?;
|
||||
if let (Some(state), Some(recording)) = (&self.program, &mut self.recording) {
|
||||
recording.track_elapsed_cycles(state, cycles);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -224,126 +220,16 @@ impl ProfilerSession {
|
||||
}
|
||||
|
||||
fn start_recording(&mut self) {
|
||||
self.recording = Some(Recording::new());
|
||||
if let Some(program) = &self.program {
|
||||
self.recording = Some(Recording::new(program));
|
||||
}
|
||||
}
|
||||
|
||||
fn finish_recording(&mut self) -> Option<Vec<u8>> {
|
||||
self.recording.take().map(|_| vec![])
|
||||
self.recording.take().map(|r| r.finish())
|
||||
}
|
||||
|
||||
fn cancel_recording(&mut self) {
|
||||
self.recording.take();
|
||||
}
|
||||
}
|
||||
|
||||
struct ProgramState {
|
||||
symbol_map: SymbolMap,
|
||||
call_stacks: HashMap<u16, Vec<StackFrame>>,
|
||||
context_stack: Vec<u16>,
|
||||
}
|
||||
|
||||
struct StackFrame {
|
||||
#[expect(dead_code)]
|
||||
address: u32,
|
||||
cycles: u64,
|
||||
}
|
||||
|
||||
const RESET_CODE: u16 = 0xfff0;
|
||||
impl ProgramState {
|
||||
async fn new(file_path: PathBuf) -> Result<Self> {
|
||||
let symbol_manager = SymbolManager::with_config(Default::default());
|
||||
let symbol_map = symbol_manager
|
||||
.load_symbol_map_for_binary_at_path(&file_path, None)
|
||||
.await?;
|
||||
let mut call_stacks = HashMap::new();
|
||||
call_stacks.insert(
|
||||
RESET_CODE,
|
||||
vec![StackFrame {
|
||||
address: 0xfffffff0,
|
||||
cycles: 0,
|
||||
}],
|
||||
);
|
||||
Ok(Self {
|
||||
symbol_map,
|
||||
call_stacks,
|
||||
context_stack: vec![RESET_CODE],
|
||||
})
|
||||
}
|
||||
|
||||
fn track_elapsed_cycles(&mut self, cycles: u32) -> Result<()> {
|
||||
let Some(code) = self.context_stack.last() else {
|
||||
return Ok(()); // program is halted, CPU is idle
|
||||
};
|
||||
let Some(stack) = self.call_stacks.get_mut(code) else {
|
||||
bail!("missing stack {code:04x}");
|
||||
};
|
||||
for frame in stack {
|
||||
frame.cycles += cycles as u64;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn track_event(&mut self, event: SimEvent) -> Result<()> {
|
||||
match event {
|
||||
SimEvent::Call(address) => {
|
||||
let Some(code) = self.context_stack.last() else {
|
||||
bail!("How did we call anything when we're halted?");
|
||||
};
|
||||
let Some(stack) = self.call_stacks.get_mut(code) else {
|
||||
bail!("missing stack {code:04x}");
|
||||
};
|
||||
let name = self
|
||||
.symbol_map
|
||||
.lookup_sync(wholesym::LookupAddress::Svma(address as u64));
|
||||
println!("depth {}: {:x?}", stack.len(), name);
|
||||
stack.push(StackFrame { address, cycles: 0 });
|
||||
}
|
||||
SimEvent::Return => {
|
||||
let Some(code) = self.context_stack.last() else {
|
||||
bail!("how did we return when we're halted?");
|
||||
};
|
||||
let Some(stack) = self.call_stacks.get_mut(code) else {
|
||||
bail!("missing stack {code:04x}");
|
||||
};
|
||||
if stack.pop().is_none() {
|
||||
bail!("returned from {code:04x} but stack was empty");
|
||||
}
|
||||
if stack.is_empty() {
|
||||
bail!("returned to oblivion");
|
||||
}
|
||||
}
|
||||
SimEvent::Halt => {
|
||||
let Some(RESET_CODE) = self.context_stack.pop() else {
|
||||
bail!("halted when not in an interrupt");
|
||||
};
|
||||
}
|
||||
SimEvent::Interrupt(code, address) => {
|
||||
// if the CPU was halted before, wake it up now
|
||||
if self.context_stack.is_empty() {
|
||||
self.context_stack.push(RESET_CODE);
|
||||
}
|
||||
|
||||
self.context_stack.push(code);
|
||||
if self
|
||||
.call_stacks
|
||||
.insert(code, vec![StackFrame { address, cycles: 0 }])
|
||||
.is_some()
|
||||
{
|
||||
bail!("{code:04x} fired twice");
|
||||
}
|
||||
}
|
||||
SimEvent::Reti => {
|
||||
let Some(code) = self.context_stack.pop() else {
|
||||
bail!("RETI when halted");
|
||||
};
|
||||
if code == RESET_CODE {
|
||||
bail!("RETI when not in interrupt");
|
||||
}
|
||||
if self.call_stacks.remove(&code).is_none() {
|
||||
bail!("{code:04x} popped but never called");
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user