Profiling #7
|
@ -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