Actually produce profiler files

This commit is contained in:
2025-08-27 22:49:44 -04:00
parent ce15d22ab1
commit ed06004a60
6 changed files with 325 additions and 127 deletions
+104
View File
@@ -0,0 +1,104 @@
use std::{collections::HashMap, path::PathBuf};
use anyhow::{Result, bail};
use crate::{emulator::SimEvent, profiler::symbols::SymbolFile};
pub struct ProgramState {
symbol_file: SymbolFile,
call_stacks: HashMap<u16, Vec<StackFrame>>,
context_stack: Vec<u16>,
}
pub struct StackFrame {
pub address: u32,
}
pub const RESET_CODE: u16 = 0xfff0;
impl ProgramState {
pub async fn new(file_path: PathBuf) -> Result<Self> {
let symbol_file = SymbolFile::load(&file_path).await?;
let mut call_stacks = HashMap::new();
call_stacks.insert(
RESET_CODE,
vec![StackFrame {
address: 0xfffffff0,
}],
);
Ok(Self {
symbol_file,
call_stacks,
context_stack: vec![RESET_CODE],
})
}
pub fn symbol_file(&self) -> &SymbolFile {
&self.symbol_file
}
pub fn current_stack(&self) -> Option<(u16, &[StackFrame])> {
let code = self.context_stack.last()?;
let call_stack = self.call_stacks.get(code)?;
Some((*code, call_stack))
}
pub 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}");
};
stack.push(StackFrame { address });
}
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 }])
.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(())
}
}