Begin implementing profiling sessions
This commit is contained in:
		
							parent
							
								
									3bfdcc9366
								
							
						
					
					
						commit
						7f819d080f
					
				
										
											
												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