Parse ELF exclusively on emulation thread
This commit is contained in:
		
							parent
							
								
									e5abd48337
								
							
						
					
					
						commit
						04c92c1454
					
				| 
						 | 
					@ -481,7 +481,7 @@ dependencies = [
 | 
				
			||||||
 "cfg-if",
 | 
					 "cfg-if",
 | 
				
			||||||
 "libc",
 | 
					 "libc",
 | 
				
			||||||
 "miniz_oxide",
 | 
					 "miniz_oxide",
 | 
				
			||||||
 "object",
 | 
					 "object 0.36.7",
 | 
				
			||||||
 "rustc-demangle",
 | 
					 "rustc-demangle",
 | 
				
			||||||
 "windows-targets 0.52.6",
 | 
					 "windows-targets 0.52.6",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					@ -1201,12 +1201,6 @@ version = "1.15.0"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
 | 
					checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					 | 
				
			||||||
name = "elf"
 | 
					 | 
				
			||||||
version = "0.8.0"
 | 
					 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					 | 
				
			||||||
checksum = "55dd888a213fc57e957abf2aa305ee3e8a28dbe05687a251f33b637cd46b0070"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "elsa"
 | 
					name = "elsa"
 | 
				
			||||||
version = "1.11.2"
 | 
					version = "1.11.2"
 | 
				
			||||||
| 
						 | 
					@ -2176,7 +2170,6 @@ dependencies = [
 | 
				
			||||||
 "egui-wgpu",
 | 
					 "egui-wgpu",
 | 
				
			||||||
 "egui-winit",
 | 
					 "egui-winit",
 | 
				
			||||||
 "egui_extras",
 | 
					 "egui_extras",
 | 
				
			||||||
 "elf",
 | 
					 | 
				
			||||||
 "fixed",
 | 
					 "fixed",
 | 
				
			||||||
 "fxprof-processed-profile",
 | 
					 "fxprof-processed-profile",
 | 
				
			||||||
 "gilrs",
 | 
					 "gilrs",
 | 
				
			||||||
| 
						 | 
					@ -2186,6 +2179,7 @@ dependencies = [
 | 
				
			||||||
 "normpath",
 | 
					 "normpath",
 | 
				
			||||||
 "num-derive",
 | 
					 "num-derive",
 | 
				
			||||||
 "num-traits",
 | 
					 "num-traits",
 | 
				
			||||||
 | 
					 "object 0.37.3",
 | 
				
			||||||
 "oneshot",
 | 
					 "oneshot",
 | 
				
			||||||
 "pollster",
 | 
					 "pollster",
 | 
				
			||||||
 "rand 0.9.2",
 | 
					 "rand 0.9.2",
 | 
				
			||||||
| 
						 | 
					@ -2983,7 +2977,18 @@ checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "flate2",
 | 
					 "flate2",
 | 
				
			||||||
 "memchr",
 | 
					 "memchr",
 | 
				
			||||||
 "ruzstd",
 | 
					 "ruzstd 0.7.3",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "object"
 | 
				
			||||||
 | 
					version = "0.37.3"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "flate2",
 | 
				
			||||||
 | 
					 "memchr",
 | 
				
			||||||
 | 
					 "ruzstd 0.8.1",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -3823,7 +3828,16 @@ version = "0.7.3"
 | 
				
			||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f"
 | 
					checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f"
 | 
				
			||||||
dependencies = [
 | 
					dependencies = [
 | 
				
			||||||
 "twox-hash",
 | 
					 "twox-hash 1.6.3",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "ruzstd"
 | 
				
			||||||
 | 
					version = "0.8.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "3640bec8aad418d7d03c72ea2de10d5c646a598f9883c7babc160d91e3c1b26c"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "twox-hash 2.1.1",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
| 
						 | 
					@ -3861,7 +3875,7 @@ dependencies = [
 | 
				
			||||||
 "memchr",
 | 
					 "memchr",
 | 
				
			||||||
 "msvc-demangler",
 | 
					 "msvc-demangler",
 | 
				
			||||||
 "nom",
 | 
					 "nom",
 | 
				
			||||||
 "object",
 | 
					 "object 0.36.7",
 | 
				
			||||||
 "pdb-addr2line",
 | 
					 "pdb-addr2line",
 | 
				
			||||||
 "rangemap",
 | 
					 "rangemap",
 | 
				
			||||||
 "rustc-demangle",
 | 
					 "rustc-demangle",
 | 
				
			||||||
| 
						 | 
					@ -4610,6 +4624,12 @@ dependencies = [
 | 
				
			||||||
 "static_assertions",
 | 
					 "static_assertions",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "twox-hash"
 | 
				
			||||||
 | 
					version = "2.1.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "8b907da542cbced5261bd3256de1b3a1bf340a3d37f93425a07362a1d687de56"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[package]]
 | 
					[[package]]
 | 
				
			||||||
name = "type-map"
 | 
					name = "type-map"
 | 
				
			||||||
version = "0.5.1"
 | 
					version = "0.5.1"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,7 +21,6 @@ egui_extras = { version = "0.32", features = ["image"] }
 | 
				
			||||||
egui-notify = "0.20"
 | 
					egui-notify = "0.20"
 | 
				
			||||||
egui-winit = "0.32"
 | 
					egui-winit = "0.32"
 | 
				
			||||||
egui-wgpu = { version = "0.32", features = ["winit"] }
 | 
					egui-wgpu = { version = "0.32", features = ["winit"] }
 | 
				
			||||||
elf = "0.8"
 | 
					 | 
				
			||||||
fxprof-processed-profile = "0.8"
 | 
					fxprof-processed-profile = "0.8"
 | 
				
			||||||
fixed = { version = "1.28", features = ["num-traits"] }
 | 
					fixed = { version = "1.28", features = ["num-traits"] }
 | 
				
			||||||
gilrs = { version = "0.11", features = ["serde-serialize"] }
 | 
					gilrs = { version = "0.11", features = ["serde-serialize"] }
 | 
				
			||||||
| 
						 | 
					@ -31,6 +30,7 @@ itertools = "0.14"
 | 
				
			||||||
normpath = "1"
 | 
					normpath = "1"
 | 
				
			||||||
num-derive = "0.4"
 | 
					num-derive = "0.4"
 | 
				
			||||||
num-traits = "0.2"
 | 
					num-traits = "0.2"
 | 
				
			||||||
 | 
					object = "0.37"
 | 
				
			||||||
oneshot = "0.1"
 | 
					oneshot = "0.1"
 | 
				
			||||||
pollster = "0.4"
 | 
					pollster = "0.4"
 | 
				
			||||||
rand = "0.9"
 | 
					rand = "0.9"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,11 +22,13 @@ use crate::{
 | 
				
			||||||
    graphics::TextureSink,
 | 
					    graphics::TextureSink,
 | 
				
			||||||
    memory::{MemoryRange, MemoryRegion},
 | 
					    memory::{MemoryRange, MemoryRegion},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					pub use game_info::GameInfo;
 | 
				
			||||||
use shrooms_vb_core::{EXPECTED_FRAME_SIZE, Sim, StopReason};
 | 
					use shrooms_vb_core::{EXPECTED_FRAME_SIZE, Sim, StopReason};
 | 
				
			||||||
pub use shrooms_vb_core::{SimEvent, VBKey, VBRegister, VBWatchpointType};
 | 
					pub use shrooms_vb_core::{SimEvent, VBKey, VBRegister, VBWatchpointType};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod address_set;
 | 
					mod address_set;
 | 
				
			||||||
mod cart;
 | 
					mod cart;
 | 
				
			||||||
 | 
					mod game_info;
 | 
				
			||||||
mod shrooms_vb_core;
 | 
					mod shrooms_vb_core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
 | 
					#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
 | 
				
			||||||
| 
						 | 
					@ -221,18 +223,16 @@ impl Emulator {
 | 
				
			||||||
            self.sim_state[index].store(SimState::Ready, Ordering::Release);
 | 
					            self.sim_state[index].store(SimState::Ready, Ordering::Release);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let mut profiling = false;
 | 
					        let mut profiling = false;
 | 
				
			||||||
        if let Some(profiler) = self.profilers[sim_id.to_index()].as_ref() {
 | 
					        if let Some(profiler) = self.profilers[sim_id.to_index()].as_ref()
 | 
				
			||||||
            if let Some(cart) = self.carts[index].as_ref() {
 | 
					            && let Some(cart) = self.carts[index].as_ref()
 | 
				
			||||||
                if profiler
 | 
					            && profiler
 | 
				
			||||||
                    .send(ProfileEvent::Start {
 | 
					                .send(ProfileEvent::Start {
 | 
				
			||||||
                        file_path: cart.file_path.clone(),
 | 
					                    info: cart.info.clone(),
 | 
				
			||||||
                    })
 | 
					                })
 | 
				
			||||||
                    .is_ok()
 | 
					                .is_ok()
 | 
				
			||||||
                {
 | 
					        {
 | 
				
			||||||
                    sim.monitor_events(true);
 | 
					            sim.monitor_events(true);
 | 
				
			||||||
                    profiling = true;
 | 
					            profiling = true;
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if !profiling {
 | 
					        if !profiling {
 | 
				
			||||||
            sim.monitor_events(false);
 | 
					            sim.monitor_events(false);
 | 
				
			||||||
| 
						 | 
					@ -476,16 +476,15 @@ impl Emulator {
 | 
				
			||||||
            if !running {
 | 
					            if !running {
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if let Some(p) = profiler {
 | 
					            if let Some(p) = profiler
 | 
				
			||||||
                if p.send(ProfileEvent::Update {
 | 
					                && p.send(ProfileEvent::Update {
 | 
				
			||||||
                    cycles,
 | 
					                    cycles,
 | 
				
			||||||
                    event: sim.take_sim_event(),
 | 
					                    event: sim.take_sim_event(),
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
                .is_err()
 | 
					                .is_err()
 | 
				
			||||||
                {
 | 
					            {
 | 
				
			||||||
                    sim.monitor_events(false);
 | 
					                sim.monitor_events(false);
 | 
				
			||||||
                    *profiler = None;
 | 
					                *profiler = None;
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -838,7 +837,7 @@ pub enum DebugEvent {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub enum ProfileEvent {
 | 
					pub enum ProfileEvent {
 | 
				
			||||||
    Start {
 | 
					    Start {
 | 
				
			||||||
        file_path: PathBuf,
 | 
					        info: Arc<GameInfo>,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    Update {
 | 
					    Update {
 | 
				
			||||||
        cycles: u32,
 | 
					        cycles: u32,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,21 +4,24 @@ use std::{
 | 
				
			||||||
    fs::{self, File},
 | 
					    fs::{self, File},
 | 
				
			||||||
    io::{Read, Seek as _, SeekFrom, Write as _},
 | 
					    io::{Read, Seek as _, SeekFrom, Write as _},
 | 
				
			||||||
    path::{Path, PathBuf},
 | 
					    path::{Path, PathBuf},
 | 
				
			||||||
 | 
					    sync::Arc,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::emulator::SimId;
 | 
					use crate::emulator::{SimId, game_info::GameInfo};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct Cart {
 | 
					pub struct Cart {
 | 
				
			||||||
    pub file_path: PathBuf,
 | 
					    pub file_path: PathBuf,
 | 
				
			||||||
    pub rom: Vec<u8>,
 | 
					    pub rom: Vec<u8>,
 | 
				
			||||||
    sram_file: File,
 | 
					    sram_file: File,
 | 
				
			||||||
    pub sram: Vec<u8>,
 | 
					    pub sram: Vec<u8>,
 | 
				
			||||||
 | 
					    pub info: Arc<GameInfo>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Cart {
 | 
					impl Cart {
 | 
				
			||||||
    pub fn load(file_path: &Path, sim_id: SimId) -> Result<Self> {
 | 
					    pub fn load(file_path: &Path, sim_id: SimId) -> Result<Self> {
 | 
				
			||||||
        let rom = fs::read(file_path)?;
 | 
					        let rom = fs::read(file_path)?;
 | 
				
			||||||
        let rom = try_parse_elf(&rom).unwrap_or(rom);
 | 
					        let (rom, info) =
 | 
				
			||||||
 | 
					            try_parse_elf(file_path, &rom).unwrap_or_else(|| (rom, GameInfo::empty(file_path)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut sram_file = File::options()
 | 
					        let mut sram_file = File::options()
 | 
				
			||||||
            .read(true)
 | 
					            .read(true)
 | 
				
			||||||
| 
						 | 
					@ -46,6 +49,7 @@ impl Cart {
 | 
				
			||||||
            rom,
 | 
					            rom,
 | 
				
			||||||
            sram_file,
 | 
					            sram_file,
 | 
				
			||||||
            sram,
 | 
					            sram,
 | 
				
			||||||
 | 
					            info: Arc::new(info),
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,18 +60,39 @@ impl Cart {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn try_parse_elf(data: &[u8]) -> Option<Vec<u8>> {
 | 
					fn try_parse_elf(file_path: &Path, data: &[u8]) -> Option<(Vec<u8>, GameInfo)> {
 | 
				
			||||||
    let parsed = elf::ElfBytes::<elf::endian::AnyEndian>::minimal_parse(data).ok()?;
 | 
					    use object::read::elf::FileHeader;
 | 
				
			||||||
 | 
					    let program = match object::FileKind::parse(data).ok()? {
 | 
				
			||||||
 | 
					        object::FileKind::Elf32 => {
 | 
				
			||||||
 | 
					            let header = object::elf::FileHeader32::parse(data).ok()?;
 | 
				
			||||||
 | 
					            parse_elf_program(header, data)?
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        object::FileKind::Elf64 => {
 | 
				
			||||||
 | 
					            let header = object::elf::FileHeader64::parse(data).ok()?;
 | 
				
			||||||
 | 
					            parse_elf_program(header, data)?
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        _ => return None,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let info = GameInfo::new(file_path, data).unwrap_or_else(|_| GameInfo::empty(file_path));
 | 
				
			||||||
 | 
					    Some((program, info))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn parse_elf_program<Elf: object::read::elf::FileHeader<Endian = object::Endianness>>(
 | 
				
			||||||
 | 
					    header: &Elf,
 | 
				
			||||||
 | 
					    data: &[u8],
 | 
				
			||||||
 | 
					) -> Option<Vec<u8>> {
 | 
				
			||||||
 | 
					    use object::read::elf::ProgramHeader;
 | 
				
			||||||
 | 
					    let endian = header.endian().ok()?;
 | 
				
			||||||
    let mut bytes = vec![];
 | 
					    let mut bytes = vec![];
 | 
				
			||||||
    let mut pstart = None;
 | 
					    let mut pstart = None;
 | 
				
			||||||
    for phdr in parsed.segments()? {
 | 
					    for phdr in header.program_headers(endian, data).ok()? {
 | 
				
			||||||
        if phdr.p_filesz == 0 {
 | 
					        if phdr.p_filesz(endian).into() == 0 {
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let start = pstart.unwrap_or(phdr.p_paddr);
 | 
					        let start = pstart.unwrap_or(phdr.p_paddr(endian).into());
 | 
				
			||||||
        pstart = Some(start);
 | 
					        pstart = Some(start);
 | 
				
			||||||
        bytes.resize((phdr.p_paddr - start) as usize, 0);
 | 
					        bytes.resize((phdr.p_paddr(endian).into() - start) as usize, 0);
 | 
				
			||||||
        let data = parsed.segment_data(&phdr).ok()?;
 | 
					        let data = phdr.data(endian, data).ok()?;
 | 
				
			||||||
        bytes.extend_from_slice(data);
 | 
					        bytes.extend_from_slice(data);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    Some(bytes)
 | 
					    Some(bytes)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,83 @@
 | 
				
			||||||
 | 
					use std::{path::Path, sync::Arc};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use anyhow::Result;
 | 
				
			||||||
 | 
					use fxprof_processed_profile::{LibraryInfo, Symbol, SymbolTable, debugid::DebugId};
 | 
				
			||||||
 | 
					use object::{Object, ObjectSymbol};
 | 
				
			||||||
 | 
					use wholesym::samply_symbols::{DebugIdExt, demangle_any};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub struct GameInfo {
 | 
				
			||||||
 | 
					    library_info: LibraryInfo,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl GameInfo {
 | 
				
			||||||
 | 
					    pub fn new(file_path: &Path, input: &[u8]) -> Result<Self> {
 | 
				
			||||||
 | 
					        let file = object::File::parse(input)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let (name, path) = name_and_path(file_path);
 | 
				
			||||||
 | 
					        let debug_id = file
 | 
				
			||||||
 | 
					            .build_id()?
 | 
				
			||||||
 | 
					            .map(|id| DebugId::from_identifier(id, true))
 | 
				
			||||||
 | 
					            .unwrap_or_default();
 | 
				
			||||||
 | 
					        let code_id = file.build_id()?.map(hex::encode);
 | 
				
			||||||
 | 
					        let mut symbols = vec![];
 | 
				
			||||||
 | 
					        for sym in file.symbols() {
 | 
				
			||||||
 | 
					            symbols.push(Symbol {
 | 
				
			||||||
 | 
					                address: sym.address() as u32,
 | 
				
			||||||
 | 
					                size: Some(sym.size() as u32),
 | 
				
			||||||
 | 
					                name: demangle_any(sym.name()?),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let library_info = LibraryInfo {
 | 
				
			||||||
 | 
					            name: name.clone(),
 | 
				
			||||||
 | 
					            debug_name: name,
 | 
				
			||||||
 | 
					            path: path.clone(),
 | 
				
			||||||
 | 
					            debug_path: path,
 | 
				
			||||||
 | 
					            debug_id,
 | 
				
			||||||
 | 
					            code_id,
 | 
				
			||||||
 | 
					            arch: None,
 | 
				
			||||||
 | 
					            symbol_table: Some(Arc::new(SymbolTable::new(symbols))),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(Self { library_info })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn empty(file_path: &Path) -> Self {
 | 
				
			||||||
 | 
					        let (name, path) = name_and_path(file_path);
 | 
				
			||||||
 | 
					        let library_info = LibraryInfo {
 | 
				
			||||||
 | 
					            name: name.clone(),
 | 
				
			||||||
 | 
					            debug_name: name,
 | 
				
			||||||
 | 
					            path: path.clone(),
 | 
				
			||||||
 | 
					            debug_path: path,
 | 
				
			||||||
 | 
					            debug_id: DebugId::default(),
 | 
				
			||||||
 | 
					            code_id: None,
 | 
				
			||||||
 | 
					            arch: None,
 | 
				
			||||||
 | 
					            symbol_table: None,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Self { library_info }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn name(&self) -> &str {
 | 
				
			||||||
 | 
					        &self.library_info.name
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn library_info(&self) -> &LibraryInfo {
 | 
				
			||||||
 | 
					        &self.library_info
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn name_and_path(file_path: &Path) -> (String, String) {
 | 
				
			||||||
 | 
					    let normalized = normpath::PathExt::normalize(file_path);
 | 
				
			||||||
 | 
					    let path = normalized
 | 
				
			||||||
 | 
					        .as_ref()
 | 
				
			||||||
 | 
					        .map(|n| n.as_path())
 | 
				
			||||||
 | 
					        .unwrap_or(file_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let name = match path.file_stem() {
 | 
				
			||||||
 | 
					        Some(s) => s.to_string_lossy().into_owned(),
 | 
				
			||||||
 | 
					        None => "game".to_string(),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    let path = path.to_string_lossy().into_owned();
 | 
				
			||||||
 | 
					    (name, path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
use std::{
 | 
					use std::{
 | 
				
			||||||
    path::PathBuf,
 | 
					 | 
				
			||||||
    sync::{Arc, Mutex},
 | 
					    sync::{Arc, Mutex},
 | 
				
			||||||
    thread,
 | 
					    thread,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -7,13 +6,12 @@ use std::{
 | 
				
			||||||
use anyhow::Result;
 | 
					use anyhow::Result;
 | 
				
			||||||
use tokio::{select, sync::mpsc};
 | 
					use tokio::{select, sync::mpsc};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::emulator::{EmulatorClient, EmulatorCommand, ProfileEvent, SimEvent, SimId};
 | 
					use crate::emulator::{EmulatorClient, EmulatorCommand, GameInfo, ProfileEvent, SimEvent, SimId};
 | 
				
			||||||
use recording::Recording;
 | 
					use recording::Recording;
 | 
				
			||||||
use state::ProgramState;
 | 
					use state::ProgramState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod recording;
 | 
					mod recording;
 | 
				
			||||||
mod state;
 | 
					mod state;
 | 
				
			||||||
mod symbols;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct Profiler {
 | 
					pub struct Profiler {
 | 
				
			||||||
    sim_id: SimId,
 | 
					    sim_id: SimId,
 | 
				
			||||||
| 
						 | 
					@ -132,7 +130,7 @@ async fn run_profile(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async fn handle_event(event: ProfileEvent, session: &mut ProfilerSession) -> Result<()> {
 | 
					async fn handle_event(event: ProfileEvent, session: &mut ProfilerSession) -> Result<()> {
 | 
				
			||||||
    match event {
 | 
					    match event {
 | 
				
			||||||
        ProfileEvent::Start { file_path } => session.start_profiling(file_path).await,
 | 
					        ProfileEvent::Start { info } => session.start_profiling(info).await,
 | 
				
			||||||
        ProfileEvent::Update { cycles, event } => {
 | 
					        ProfileEvent::Update { cycles, event } => {
 | 
				
			||||||
            session.track_elapsed_cycles(cycles);
 | 
					            session.track_elapsed_cycles(cycles);
 | 
				
			||||||
            if let Some(event) = event {
 | 
					            if let Some(event) = event {
 | 
				
			||||||
| 
						 | 
					@ -199,8 +197,8 @@ impl ProfilerSession {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn start_profiling(&mut self, file_path: PathBuf) {
 | 
					    async fn start_profiling(&mut self, info: Arc<GameInfo>) {
 | 
				
			||||||
        let program = ProgramState::new(file_path).await;
 | 
					        let program = ProgramState::new(info).await;
 | 
				
			||||||
        let recording = if self.recording.is_some() {
 | 
					        let recording = if self.recording.is_some() {
 | 
				
			||||||
            Some(Recording::new(&program))
 | 
					            Some(Recording::new(&program))
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,10 +24,8 @@ impl Recording {
 | 
				
			||||||
        let process =
 | 
					        let process =
 | 
				
			||||||
            profile.add_process(state.name(), 1, Timestamp::from_nanos_since_reference(0));
 | 
					            profile.add_process(state.name(), 1, Timestamp::from_nanos_since_reference(0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let Some(symbol_file) = state.symbol_file() {
 | 
					        let lib = profile.add_lib(state.library_info().clone());
 | 
				
			||||||
            let lib = profile.add_lib(symbol_file.library_info().clone());
 | 
					        profile.add_lib_mapping(process, lib, 0x00000000, 0xffffffff, 0);
 | 
				
			||||||
            profile.add_lib_mapping(process, lib, 0x00000000, 0xffffffff, 0);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut me = Self {
 | 
					        let mut me = Self {
 | 
				
			||||||
            profile,
 | 
					            profile,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,12 @@
 | 
				
			||||||
use std::{collections::HashMap, path::PathBuf};
 | 
					use std::{collections::HashMap, sync::Arc};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use anyhow::{Result, bail};
 | 
					use anyhow::{Result, bail};
 | 
				
			||||||
 | 
					use fxprof_processed_profile::LibraryInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::profiler::symbols::SymbolFile;
 | 
					use crate::emulator::GameInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct ProgramState {
 | 
					pub struct ProgramState {
 | 
				
			||||||
    name: String,
 | 
					    info: Arc<GameInfo>,
 | 
				
			||||||
    symbol_file: Option<SymbolFile>,
 | 
					 | 
				
			||||||
    call_stacks: HashMap<u16, Vec<StackFrame>>,
 | 
					    call_stacks: HashMap<u16, Vec<StackFrame>>,
 | 
				
			||||||
    context_stack: Vec<u16>,
 | 
					    context_stack: Vec<u16>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -17,17 +17,7 @@ pub struct StackFrame {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const RESET_CODE: u16 = 0xfff0;
 | 
					pub const RESET_CODE: u16 = 0xfff0;
 | 
				
			||||||
impl ProgramState {
 | 
					impl ProgramState {
 | 
				
			||||||
    pub async fn new(file_path: PathBuf) -> Self {
 | 
					    pub async fn new(info: Arc<GameInfo>) -> Self {
 | 
				
			||||||
        let symbol_file = SymbolFile::load(&file_path).await.ok();
 | 
					 | 
				
			||||||
        let name = symbol_file
 | 
					 | 
				
			||||||
            .as_ref()
 | 
					 | 
				
			||||||
            .map(|f| f.name().to_string())
 | 
					 | 
				
			||||||
            .or_else(|| {
 | 
					 | 
				
			||||||
                file_path
 | 
					 | 
				
			||||||
                    .file_stem()
 | 
					 | 
				
			||||||
                    .map(|s| s.to_string_lossy().into_owned())
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            .unwrap_or_else(|| "game".to_string());
 | 
					 | 
				
			||||||
        let mut call_stacks = HashMap::new();
 | 
					        let mut call_stacks = HashMap::new();
 | 
				
			||||||
        call_stacks.insert(
 | 
					        call_stacks.insert(
 | 
				
			||||||
            RESET_CODE,
 | 
					            RESET_CODE,
 | 
				
			||||||
| 
						 | 
					@ -36,19 +26,18 @@ impl ProgramState {
 | 
				
			||||||
            }],
 | 
					            }],
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            name,
 | 
					            info,
 | 
				
			||||||
            symbol_file,
 | 
					 | 
				
			||||||
            call_stacks,
 | 
					            call_stacks,
 | 
				
			||||||
            context_stack: vec![RESET_CODE],
 | 
					            context_stack: vec![RESET_CODE],
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn name(&self) -> &str {
 | 
					    pub fn name(&self) -> &str {
 | 
				
			||||||
        &self.name
 | 
					        self.info.name()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn symbol_file(&self) -> Option<&SymbolFile> {
 | 
					    pub fn library_info(&self) -> &LibraryInfo {
 | 
				
			||||||
        self.symbol_file.as_ref()
 | 
					        self.info.library_info()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn current_stack(&self) -> Option<(u16, &[StackFrame])> {
 | 
					    pub fn current_stack(&self) -> Option<(u16, &[StackFrame])> {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,67 +0,0 @@
 | 
				
			||||||
use std::{path::Path, sync::Arc};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use anyhow::Result;
 | 
					 | 
				
			||||||
use fxprof_processed_profile::{LibraryInfo, Symbol, SymbolTable};
 | 
					 | 
				
			||||||
use wholesym::{SymbolManager, samply_symbols::demangle_any};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub struct SymbolFile {
 | 
					 | 
				
			||||||
    library_info: LibraryInfo,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl SymbolFile {
 | 
					 | 
				
			||||||
    pub async fn load(file_path: &Path) -> Result<Self> {
 | 
					 | 
				
			||||||
        let normalized = normpath::PathExt::normalize(file_path)?;
 | 
					 | 
				
			||||||
        let library_info =
 | 
					 | 
				
			||||||
            SymbolManager::library_info_for_binary_at_path(normalized.as_path(), None).await?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let symbol_manager = SymbolManager::with_config(Default::default());
 | 
					 | 
				
			||||||
        let symbol_map = symbol_manager
 | 
					 | 
				
			||||||
            .load_symbol_map_for_binary_at_path(normalized.as_path(), None)
 | 
					 | 
				
			||||||
            .await?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let name = library_info
 | 
					 | 
				
			||||||
            .name
 | 
					 | 
				
			||||||
            .or_else(|| {
 | 
					 | 
				
			||||||
                normalized
 | 
					 | 
				
			||||||
                    .file_name()
 | 
					 | 
				
			||||||
                    .map(|n| n.to_string_lossy().into_owned())
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            .unwrap_or("game".to_string());
 | 
					 | 
				
			||||||
        let debug_name = library_info.debug_name.unwrap_or_else(|| name.clone());
 | 
					 | 
				
			||||||
        let path = library_info
 | 
					 | 
				
			||||||
            .path
 | 
					 | 
				
			||||||
            .unwrap_or_else(|| normalized.into_os_string().to_string_lossy().into_owned());
 | 
					 | 
				
			||||||
        let debug_path = library_info.debug_path.unwrap_or_else(|| path.clone());
 | 
					 | 
				
			||||||
        let debug_id = library_info.debug_id.unwrap_or_default();
 | 
					 | 
				
			||||||
        let code_id = library_info.code_id.map(|id| id.to_string());
 | 
					 | 
				
			||||||
        let arch = library_info.arch;
 | 
					 | 
				
			||||||
        let symbols = symbol_map
 | 
					 | 
				
			||||||
            .iter_symbols()
 | 
					 | 
				
			||||||
            .map(|(address, name)| Symbol {
 | 
					 | 
				
			||||||
                address: address + 0x07000000,
 | 
					 | 
				
			||||||
                size: None,
 | 
					 | 
				
			||||||
                name: demangle_any(&name),
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            .collect();
 | 
					 | 
				
			||||||
        Ok(Self {
 | 
					 | 
				
			||||||
            library_info: LibraryInfo {
 | 
					 | 
				
			||||||
                name,
 | 
					 | 
				
			||||||
                debug_name,
 | 
					 | 
				
			||||||
                path,
 | 
					 | 
				
			||||||
                debug_path,
 | 
					 | 
				
			||||||
                debug_id,
 | 
					 | 
				
			||||||
                code_id,
 | 
					 | 
				
			||||||
                arch,
 | 
					 | 
				
			||||||
                symbol_table: Some(Arc::new(SymbolTable::new(symbols))),
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn name(&self) -> &str {
 | 
					 | 
				
			||||||
        &self.library_info.name
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn library_info(&self) -> &LibraryInfo {
 | 
					 | 
				
			||||||
        &self.library_info
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Loading…
	
		Reference in New Issue