Begin implementing profiling sessions

This commit is contained in:
2025-08-27 22:49:44 -04:00
parent 3bfdcc9366
commit 7f819d080f
4 changed files with 1055 additions and 6 deletions
+100 -3
View File
@@ -1,4 +1,6 @@
use std::{
collections::HashMap,
path::PathBuf,
sync::{
Arc,
atomic::{AtomicBool, Ordering},
@@ -7,8 +9,9 @@ use std::{
};
use tokio::{select, sync::mpsc};
use wholesym::{SymbolManager, SymbolMap};
use crate::emulator::{EmulatorClient, EmulatorCommand, ProfileEvent, SimId};
use crate::emulator::{EmulatorClient, EmulatorCommand, ProfileEvent, SimEvent, SimId};
pub struct Profiler {
sim_id: SimId,
@@ -65,15 +68,109 @@ async fn run_profile(sim_id: SimId, client: EmulatorClient, running: Arc<AtomicB
client.send_command(EmulatorCommand::StartProfiling(sim_id, profile_sync));
running.store(true, Ordering::Relaxed);
let mut session = None;
while let Some(event) = profile_source.recv().await {
match event {
ProfileEvent::Start { file_path } => {
println!("profiling {}", file_path.display());
session = Some(ProfileSession::new(file_path).await);
}
ProfileEvent::Update { cycles, event } => {
println!("update {cycles} {event:#x?}");
if let Some(session) = &mut session {
session.track_elapsed_cycles(cycles);
if let Some(event) = event {
session.track_event(event);
}
}
}
}
}
running.store(false, Ordering::Release);
}
struct ProfileSession {
symbol_map: SymbolMap,
call_stacks: HashMap<u16, Vec<StackFrame>>,
context_stack: Vec<u16>,
}
struct StackFrame {
#[expect(dead_code)]
address: Option<u32>,
cycles: u64,
}
impl ProfileSession {
async fn new(file_path: PathBuf) -> 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
.expect("cannae load symbols");
let mut call_stacks = HashMap::new();
call_stacks.insert(
0,
vec![StackFrame {
address: None,
cycles: 0,
}],
);
Self {
symbol_map,
call_stacks,
context_stack: vec![],
}
}
fn track_elapsed_cycles(&mut self, cycles: u32) {
let code = self.context_stack.last().copied().unwrap_or(0);
let Some(stack) = self.call_stacks.get_mut(&code) else {
panic!("missing stack {code:04x}");
};
for frame in stack {
frame.cycles += cycles as u64;
}
}
fn track_event(&mut self, event: SimEvent) {
match event {
SimEvent::Interrupt(code) => {
self.context_stack.push(code);
if self.call_stacks.insert(code, vec![]).is_some() {
panic!("{code:04x} fired twice");
}
}
SimEvent::Reti => {
let Some(code) = self.context_stack.pop() else {
panic!("reti when not in interrupt");
};
if self.call_stacks.remove(&code).is_none() {
panic!("{code:04x} popped but never called")
}
}
SimEvent::Call(addr) => {
let code = self.context_stack.last().copied().unwrap_or(0);
let Some(stack) = self.call_stacks.get_mut(&code) else {
panic!("missing stack {code:04x}");
};
let name = self
.symbol_map
.lookup_sync(wholesym::LookupAddress::Svma(addr as u64));
println!("depth {}: {:?}", stack.len(), name);
stack.push(StackFrame {
address: Some(addr),
cycles: 0,
});
}
SimEvent::Return => {
let code = self.context_stack.last().copied().unwrap_or(0);
let Some(stack) = self.call_stacks.get_mut(&code) else {
panic!("missing stack {code:04x}");
};
if stack.pop().is_none() {
panic!("returned from {code:04x} but stack was empty");
}
}
}
}
}