Begin implementing profiling sessions
This commit is contained in:
parent
0c6c9ec4c3
commit
aa3cc3df9b
File diff suppressed because it is too large
Load Diff
|
@ -42,6 +42,7 @@ tokio = { version = "1", features = ["io-util", "macros", "net", "rt", "sync", "
|
||||||
tracing = { version = "0.1", features = ["release_max_level_info"] }
|
tracing = { version = "0.1", features = ["release_max_level_info"] }
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
wgpu = "25"
|
wgpu = "25"
|
||||||
|
wholesym = "0.8"
|
||||||
winit = { version = "0.30", features = ["serde"] }
|
winit = { version = "0.30", features = ["serde"] }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
|
|
@ -18,12 +18,12 @@ use tracing::{error, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
audio::Audio,
|
audio::Audio,
|
||||||
emulator::{cart::Cart, shrooms_vb_core::SimEvent},
|
emulator::cart::Cart,
|
||||||
graphics::TextureSink,
|
graphics::TextureSink,
|
||||||
memory::{MemoryRange, MemoryRegion},
|
memory::{MemoryRange, MemoryRegion},
|
||||||
};
|
};
|
||||||
use shrooms_vb_core::{EXPECTED_FRAME_SIZE, Sim, StopReason};
|
use shrooms_vb_core::{EXPECTED_FRAME_SIZE, Sim, StopReason};
|
||||||
pub use shrooms_vb_core::{VBKey, VBRegister, VBWatchpointType};
|
pub use shrooms_vb_core::{SimEvent, VBKey, VBRegister, VBWatchpointType};
|
||||||
|
|
||||||
mod address_set;
|
mod address_set;
|
||||||
mod cart;
|
mod cart;
|
||||||
|
|
103
src/profiler.rs
103
src/profiler.rs
|
@ -1,4 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
path::PathBuf,
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
Arc,
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
|
@ -7,8 +9,9 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use tokio::{select, sync::mpsc};
|
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 {
|
pub struct Profiler {
|
||||||
sim_id: SimId,
|
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));
|
client.send_command(EmulatorCommand::StartProfiling(sim_id, profile_sync));
|
||||||
|
|
||||||
running.store(true, Ordering::Relaxed);
|
running.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
|
let mut session = None;
|
||||||
while let Some(event) = profile_source.recv().await {
|
while let Some(event) = profile_source.recv().await {
|
||||||
match event {
|
match event {
|
||||||
ProfileEvent::Start { file_path } => {
|
ProfileEvent::Start { file_path } => {
|
||||||
println!("profiling {}", file_path.display());
|
session = Some(ProfileSession::new(file_path).await);
|
||||||
}
|
}
|
||||||
ProfileEvent::Update { cycles, event } => {
|
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);
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue