From 61f4c6337191253aa932ed090f91148a3bf74671 Mon Sep 17 00:00:00 2001 From: 0vercl0k <1476421+0vercl0k@users.noreply.github.com> Date: Mon, 10 Jun 2024 20:56:13 -0700 Subject: [PATCH] buncha stuff --- crates/symbolizer/Cargo.toml | 7 +- crates/symbolizer/src/builder.rs | 62 +++++----- crates/symbolizer/src/error.rs | 11 +- crates/symbolizer/src/guid.rs | 103 ++++++++++++++++- crates/symbolizer/src/lib.rs | 2 + crates/symbolizer/src/modules.rs | 2 +- crates/symbolizer/src/pe.rs | 5 +- crates/symbolizer/src/stats.rs | 44 +++++-- crates/symbolizer/src/symbolizer.rs | 23 ++-- crates/symbolizer/tests/basics.rs | 170 ++++++++++++++++++++++++---- src/human.rs | 10 +- src/main.rs | 14 ++- 12 files changed, 355 insertions(+), 98 deletions(-) diff --git a/crates/symbolizer/Cargo.toml b/crates/symbolizer/Cargo.toml index 004249a..b2af2b7 100644 --- a/crates/symbolizer/Cargo.toml +++ b/crates/symbolizer/Cargo.toml @@ -22,4 +22,9 @@ anyhow = "1.0.86" [dev-dependencies] kdmp-parser = "0.2" udmp-parser = "0.2" -object = {version = "0.36.0", default-features = false, features = ["read", "read_core", "pe", "std"]} +object = { version = "0.36.0", default-features = false, features = [ + "read", + "read_core", + "pe", + "std", +] } diff --git a/crates/symbolizer/src/builder.rs b/crates/symbolizer/src/builder.rs index 1ade93c..98aa1b1 100644 --- a/crates/symbolizer/src/builder.rs +++ b/crates/symbolizer/src/builder.rs @@ -11,18 +11,28 @@ pub struct Symcache(PathBuf); /// Builder for [`Symbolizer`]. #[derive(Default, Debug)] -pub struct Builder { +pub struct Builder { symcache: SC, modules: Vec, - mode: M, + mode: PdbLookupMode, } -#[derive(Default)] -pub struct Offline; -pub struct Online(Vec); +impl Builder { + pub fn msft_symsrv(self) -> Builder { + let Self { + symcache, modules, .. + } = self; -impl Builder { - pub fn online(self, symsrvs: impl Iterator>) -> Builder { + Builder { + symcache, + modules, + mode: PdbLookupMode::Online { + symsrvs: vec!["https://msdl.microsoft.com/download/symbols/".into()], + }, + } + } + + pub fn online(self, symsrvs: impl Iterator>) -> Builder { let Self { symcache, modules, .. } = self; @@ -30,13 +40,15 @@ impl Builder { Builder { symcache, modules, - mode: Online(symsrvs.map(Into::into).collect()), + mode: PdbLookupMode::Online { + symsrvs: symsrvs.map(Into::into).collect(), + }, } } } -impl Builder { - pub fn symcache(self, cache: &impl AsRef) -> Builder { +impl Builder { + pub fn symcache(self, cache: &impl AsRef) -> Builder { let Self { modules, mode, .. } = self; Builder { @@ -47,34 +59,16 @@ impl Builder { } } -impl Builder { - pub fn modules(mut self, modules: impl Iterator) -> Self { - self.modules = modules.collect(); +impl Builder { + pub fn modules<'a>(mut self, modules: impl IntoIterator) -> Self { + self.modules = modules.into_iter().cloned().collect(); self } } -impl Builder { - pub fn build(self, addr_space: AS) -> Result> - where - AS: AddrSpace, - { - let Self { - symcache, modules, .. - } = self; - let config = Config { - symcache: symcache.0, - modules, - mode: PdbLookupMode::Offline, - }; - - Symbolizer::new(addr_space, config) - } -} - -impl Builder { - pub fn build(self, addr_space: AS) -> Result> +impl Builder { + pub fn build(self, addr_space: &mut AS) -> Result> where AS: AddrSpace, { @@ -86,7 +80,7 @@ impl Builder { let config = Config { symcache: symcache.0, modules, - mode: PdbLookupMode::Online { symcache: mode.0 }, + mode, }; Symbolizer::new(addr_space, config) diff --git a/crates/symbolizer/src/error.rs b/crates/symbolizer/src/error.rs index 31f84d1..a1f3ebe 100644 --- a/crates/symbolizer/src/error.rs +++ b/crates/symbolizer/src/error.rs @@ -1,7 +1,8 @@ // Axel '0vercl0k' Souchet - May 27 2024 use std::io; -use std::num::TryFromIntError; +use std::num::{ParseIntError, TryFromIntError}; use std::path::PathBuf; +use std::str::Utf8Error; use std::string::FromUtf8Error; use pdb::PdbInternalSectionOffset; @@ -16,9 +17,13 @@ pub enum Error { #[error("pdb error: {0}")] Pdb(#[from] pdb::Error), #[error("from int error: {0}")] - FromIntError(#[from] TryFromIntError), + FromInt(#[from] TryFromIntError), + #[error("parse int error: {0}")] + ParseInt(#[from] ParseIntError), #[error("utf8: {0}")] - Utf8(#[from] FromUtf8Error), + Utf8(#[from] Utf8Error), + #[error("from utf8: {0}")] + FromUtf8(#[from] FromUtf8Error), #[error("pdb path {0:?} does not have a filename")] PdbPathNoName(PathBuf), #[error("failed to perform an i/o: {0}")] diff --git a/crates/symbolizer/src/guid.rs b/crates/symbolizer/src/guid.rs index 25da4d6..1f12cda 100644 --- a/crates/symbolizer/src/guid.rs +++ b/crates/symbolizer/src/guid.rs @@ -2,8 +2,12 @@ //! This module contains the implementation of the [`Guid`] type. use std::fmt::Display; +use anyhow::anyhow; + +use crate::Error; + /// A GUID. -#[derive(Default, Debug)] +#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)] pub struct Guid { d0: u32, d1: u16, @@ -11,6 +15,29 @@ pub struct Guid { d3: [u8; 8], } +impl TryFrom<&str> for Guid { + type Error = Error; + + fn try_from(value: &str) -> Result { + if value.len() != 32 { + return Err(anyhow!("the guid str ({value:?}) should be 32 bytes long").into()); + } + + let mut bytes = [0; 16]; + for (n, chunk) in value.as_bytes().chunks_exact(2).enumerate() { + let s = std::str::from_utf8(chunk)?; + bytes[n] = u8::from_str_radix(s, 16)?; + } + + let d0 = u32::from_be_bytes(bytes[0..4].try_into().unwrap()); + let d1 = u16::from_be_bytes(bytes[4..6].try_into().unwrap()); + let d2 = u16::from_be_bytes(bytes[6..8].try_into().unwrap()); + let d3 = bytes[8..].try_into().unwrap(); + + Ok(Self { d0, d1, d2, d3 }) + } +} + impl From<[u8; 16]> for Guid { fn from(value: [u8; 16]) -> Self { let d0 = u32::from_le_bytes(value[0..4].try_into().unwrap()); @@ -40,3 +67,77 @@ impl Display for Guid { )) } } + +#[cfg(test)] +mod tests { + use crate::Guid; + + const NTDLL_GUID: Guid = Guid { + d0: 0x8d5d5ed5, + d1: 0xd5b8, + d2: 0xaa60, + d3: [0x9a, 0x82, 0x60, 0x0c, 0x14, 0xe3, 0x00, 0x4d], + }; + + #[test] + fn malformed_guids() { + assert!(Guid::try_from("8D5D5ED5D5B8AA609A82600C14E3004D1").is_err()); + + assert!(Guid::try_from("8D5D5ED5D5B8AA609A82600C14E3004").is_err()); + } + + #[test] + fn non_hex_guids() { + assert!(Guid::try_from("8D5D5ED5D5B8AA609A82600C14E3004Z").is_err()); + } + + #[test] + fn str() { + // 0:000> lmvm ntdll + // Browse full module list + // start end module name + // 00007ff9`aa450000 00007ff9`aa667000 ntdll (pdb symbols) + // c:\dbg\sym\ntdll.pdb\8D5D5ED5D5B8AA609A82600C14E3004D1\ntdll.pdb + assert_eq!( + Guid::try_from("8D5D5ED5D5B8AA609A82600C14E3004D").unwrap(), + NTDLL_GUID + ) + } + + #[test] + fn from() { + // 0:000> !dh ntdll + // ... + // SECTION HEADER #5 + // .rdata name + // 4D210 virtual size + // 132000 virtual address + // 4E000 size of raw data + // 132000 file pointer to raw data + // 0 file pointer to relocation table + // 0 file pointer to line numbers + // 0 number of relocations + // 0 number of line numbers + // 40000040 flags + // Initialized Data + // (no align specified) + // Read Only + // ... + // Debug Directories(4) + // Type Size Address Pointer + // cv 22 15b880 15b880 Format: RSDS, guid, 1, ntdll.pdb + // + // 0:000> db ntdll+15b880 + // 00007ff9`aa5ab880 52 53 44 53 d5 5e 5d 8d-b8 d5 60 aa 9a 82 60 0c + // RSDS.^]...`...`. 00007ff9`aa5ab890 14 e3 00 4d 01 00 00 00-6e 74 64 + // 6c 6c 2e 70 64 ...M....ntdll.pd + + assert_eq!( + Guid::from([ + 0xd5, 0x5e, 0x5d, 0x8d, 0xb8, 0xd5, 0x60, 0xaa, 0x9a, 0x82, 0x60, 0x0c, 0x14, 0xe3, + 0x00, 0x4d + ]), + NTDLL_GUID + ) + } +} diff --git a/crates/symbolizer/src/lib.rs b/crates/symbolizer/src/lib.rs index f6aa2e0..f94e1bf 100644 --- a/crates/symbolizer/src/lib.rs +++ b/crates/symbolizer/src/lib.rs @@ -13,6 +13,8 @@ mod symbolizer; pub use addr_space::AddrSpace; pub use builder::Builder; pub use error::{Error, Result}; +pub use guid::Guid; pub use modules::{Module, Modules}; +pub use pe::PdbId; pub use stats::Stats; pub use symbolizer::Symbolizer; diff --git a/crates/symbolizer/src/modules.rs b/crates/symbolizer/src/modules.rs index 79bf064..7411cb0 100644 --- a/crates/symbolizer/src/modules.rs +++ b/crates/symbolizer/src/modules.rs @@ -6,7 +6,7 @@ use std::ops::Range; use crate::misc::Rva; /// A user or kernel module. -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct Module { /// Where the module is loaded into virtual memory. pub at: Range, diff --git a/crates/symbolizer/src/pe.rs b/crates/symbolizer/src/pe.rs index 20d4ed1..bd1774a 100644 --- a/crates/symbolizer/src/pe.rs +++ b/crates/symbolizer/src/pe.rs @@ -192,7 +192,7 @@ pub const IMAGE_DEBUG_TYPE_CODEVIEW: u32 = 2; /// /// To download a PDB off Microsoft's Symbol Server, we need three pieces of /// information: the pdb name, a guid and its age. -#[derive(Debug, Default)] +#[derive(Debug, Default, PartialEq, Eq, Hash, Clone)] pub struct PdbId { pub path: PathBuf, pub guid: Guid, @@ -206,7 +206,8 @@ impl Display for PdbId { } impl PdbId { - pub fn new(path: PathBuf, guid: Guid, age: u32) -> Result { + pub fn new(path: impl Into, guid: Guid, age: u32) -> Result { + let path = path.into(); if path.file_name().is_none() { return Err(E::PdbPathNoName(path)); } diff --git a/crates/symbolizer/src/stats.rs b/crates/symbolizer/src/stats.rs index 9f23fe4..e22cd4c 100644 --- a/crates/symbolizer/src/stats.rs +++ b/crates/symbolizer/src/stats.rs @@ -2,35 +2,59 @@ //! This module contains the [`Stats`] type that is used to keep track of //! various statistics when symbolizing. use std::cell::RefCell; +use std::collections::HashMap; use std::fmt::Debug; +use crate::pe::PdbId; + #[derive(Debug, Default)] pub struct StatsBuilder { inner: RefCell, } /// Various statistics that the symbolizer keeps track of. -#[derive(Default, Clone, Copy, Debug)] +#[derive(Default, Clone, Debug)] pub struct Stats { /// The number of addresses symbolized. pub n_addrs: u64, - /// The number of downloaded PDB files. - pub n_downloads: u64, - /// The total size in bytes of downloads. - pub size_downloaded: u64, + /// The PDB identifiers that have been downloaded & the associated file size + /// in bytes. + pub downloaded: HashMap, /// The number of time the address cache was a hit. pub cache_hit: u64, } +impl Stats { + pub fn did_download(&self, pdb_id: PdbId) -> bool { + self.downloaded.contains_key(&pdb_id) + } + + pub fn amount_downloaded(&self) -> u64 { + let mut total = 0u64; + for value in self.downloaded.values() { + total = total.saturating_add(*value); + } + + total + } + + pub fn amount_pdb_downloaded(&self) -> usize { + self.downloaded.len() + } +} + impl StatsBuilder { pub fn build(&self) -> Stats { - *self.inner.borrow() + self.inner.borrow().clone() } - pub fn downloaded_file(&self, size: u64) { - let mut inner = self.inner.borrow_mut(); - inner.n_downloads += 1; - inner.size_downloaded += size; + pub fn downloaded_file(&self, pdb_id: PdbId, size: u64) { + assert!(self + .inner + .borrow_mut() + .downloaded + .insert(pdb_id, size) + .is_none()); } pub fn addr_symbolized(&self) { diff --git a/crates/symbolizer/src/symbolizer.rs b/crates/symbolizer/src/symbolizer.rs index 394e6a6..a3bc85f 100644 --- a/crates/symbolizer/src/symbolizer.rs +++ b/crates/symbolizer/src/symbolizer.rs @@ -14,7 +14,7 @@ use anyhow::{anyhow, Context}; use log::{debug, trace, warn}; use crate::addr_space::AddrSpace; -use crate::builder::{Builder, NoSymcache, Offline}; +use crate::builder::{Builder, NoSymcache}; use crate::misc::{fast_hex32, fast_hex64}; use crate::modules::{Module, Modules}; use crate::pdbcache::{PdbCache, PdbCacheBuilder}; @@ -206,7 +206,7 @@ pub enum PdbLookupMode { Offline, Online { /// List of symbol servers to try to download PDBs from when needed. - symcache: Vec, + symsrvs: Vec, }, } @@ -226,7 +226,7 @@ pub struct Config { /// The [`Symbolizer`] is the main object that glues all the logic. /// /// It downloads, parses PDB information, and symbolizes. -pub struct Symbolizer +pub struct Symbolizer<'a, AS> where AS: AddrSpace, { @@ -242,7 +242,7 @@ where /// The kernel dump parser. We need this to be able to read PDB identifiers /// out of the PE headers, as well as reading the export tables of those /// modules. - addr_space: RefCell, + addr_space: RefCell<&'a mut AS>, /// List of symbol servers to try to download PDBs from when needed. symsrvs: Vec, /// Caches addresses to symbols. This allows us to not have to symbolize an @@ -254,23 +254,23 @@ where offline: bool, } -impl Symbolizer +impl<'a, AS> Symbolizer<'a, AS> where AS: AddrSpace, { - pub fn builder() -> Builder { + pub fn builder() -> Builder { Builder::default() } /// Create a [`Symbolizer`]. - pub fn new(addr_space: AS, config: Config) -> Result { + pub fn new(addr_space: &'a mut AS, config: Config) -> Result { let (offline, symsrvs) = match config.mode { PdbLookupMode::Offline => // If the user wants offline, then let's do that.. { (true, vec![]) } - PdbLookupMode::Online { symcache } => { + PdbLookupMode::Online { symsrvs } => { // ..otherwise, we'll try to resolve a DNS and see what happens. If we can't do // that, then we'll assume we're offline and turn the offline mode. // Otherwise, we'll assume we have online access and attempt to download PDBs. @@ -279,7 +279,7 @@ where debug!("Turning on 'offline' mode as you seem to not have internet access.."); } - (offline, symcache) + (offline, symsrvs) } }; @@ -341,7 +341,7 @@ where // Let's start by parsing the PE to get its exports, and PDB information if // there's any. - let pe = Pe::new(&mut *self.addr_space.borrow_mut(), module.at.start)?; + let pe = Pe::new(*self.addr_space.borrow_mut(), module.at.start)?; // Ingest the EAT. builder.ingest(pe.exports.into_iter()); @@ -356,7 +356,8 @@ where // .. and ingest it if we have one. if let Some((pdb_path, pdb_kind)) = pdb_path { if matches!(pdb_kind, PdbKind::Download) { - self.stats.downloaded_file(pdb_path.metadata()?.len()) + self.stats + .downloaded_file(pdb_id, pdb_path.metadata()?.len()) } builder.ingest_pdb(pdb_path)?; diff --git a/crates/symbolizer/tests/basics.rs b/crates/symbolizer/tests/basics.rs index 495a990..9e17ded 100644 --- a/crates/symbolizer/tests/basics.rs +++ b/crates/symbolizer/tests/basics.rs @@ -1,4 +1,5 @@ // Axel '0vercl0k' Souchet - May 30 2024 +use std::cmp::min; use std::env::temp_dir; use std::fs::{self, File}; use std::io::{self, Read, Seek, Write}; @@ -6,7 +7,8 @@ use std::path::{Path, PathBuf}; use object::read::pe::PeFile64; use object::{NativeEndian, ReadCache, ReadRef}; -use symbolizer::{AddrSpace, Builder, Module}; +use symbolizer::{AddrSpace, Builder, Guid, Module, PdbId}; +use udmp_parser::UserDumpParser; const EXPECTED_LEN: u64 = 0x90_00; const EXPECTED_RAW: [(u64, &str, &str); 4] = [ @@ -70,6 +72,40 @@ impl AddrSpace for RawAddressSpace { } } +#[test] +fn raw_virt() { + let mut raw_addr_space = RawAddressSpace::new(&fixture("mrt100.raw")).unwrap(); + let len = raw_addr_space.len(); + + let mut symb = Builder::default() + .modules(&vec![Module::new("mrt100", 0x0, len)]) + .msft_symsrv() + .symcache(&symcache("basics")) + .build(&mut raw_addr_space) + .unwrap(); + + for (addr, expected_full, expected_modoff) in EXPECTED_RAW { + let mut full = Vec::new(); + symb.full(addr, &mut full).unwrap(); + assert_eq!(String::from_utf8(full).unwrap(), expected_full); + + let mut modoff = Vec::new(); + symb.modoff(addr, &mut modoff).unwrap(); + assert_eq!(String::from_utf8(modoff).unwrap(), expected_modoff); + } + + let stats = symb.stats(); + assert_eq!(stats.amount_pdb_downloaded(), 1); + assert!(stats.did_download( + PdbId::new( + "mrt100.pdb", + "A20DA44BF08DB27D2BA0928F79447C7D".try_into().unwrap(), + 1 + ) + .unwrap() + )); +} + #[derive(Debug)] struct FileAddressSpace<'data> { pe: PeFile64<'data, &'data ReadCache>, @@ -127,15 +163,17 @@ impl<'data> AddrSpace for FileAddressSpace<'data> { } #[test] -fn raw_virt() { - let raw_addr_space = RawAddressSpace::new(&fixture("mrt100.raw")).unwrap(); - let len = raw_addr_space.len(); +fn raw_file() { + let file = File::open(fixture("mrt100.dll")).unwrap(); + let cache = ReadCache::new(file); + let mut file_addr_space = FileAddressSpace::new(&cache).unwrap(); + let len = file_addr_space.len(); let mut symb = Builder::default() - .modules(vec![Module::new("mrt100", 0x0, len)].into_iter()) + .modules(&vec![Module::new("mrt100", 0x0, len)]) .online(vec!["https://msdl.microsoft.com/download/symbols/"].into_iter()) .symcache(&symcache("basics")) - .build(raw_addr_space) + .build(&mut file_addr_space) .unwrap(); for (addr, expected_full, expected_modoff) in EXPECTED_RAW { @@ -148,32 +186,116 @@ fn raw_virt() { assert_eq!(String::from_utf8(modoff).unwrap(), expected_modoff); } - assert_eq!(symb.stats().n_downloads, 1); + let stats = symb.stats(); + assert_eq!(stats.amount_pdb_downloaded(), 1); + assert!(stats.did_download( + PdbId::new( + "mrt100.pdb", + "A20DA44BF08DB27D2BA0928F79447C7D".try_into().unwrap(), + 1 + ) + .unwrap() + )); +} + +#[derive(Debug)] +struct UserDumpAddrSpace<'a>(UserDumpParser<'a>); + +impl<'a> AddrSpace for UserDumpAddrSpace<'a> { + fn read_at(&mut self, addr: u64, mut buf: &mut [u8]) -> io::Result { + let mut cur_addr = addr; + let mut read_len = 0; + while read_len < buf.len() { + let Some(block) = self.0.get_mem_block(addr) else { + return Err(io::Error::new( + io::ErrorKind::Unsupported, + format!("no mem block found for {addr:#x}"), + )); + }; + + let Some(data) = block.data_from(cur_addr) else { + panic!(); + }; + + let left = buf.len() - read_len; + let len = min(data.len(), left); + buf.write(&data[..len]).unwrap(); + cur_addr += u64::try_from(len).unwrap(); + read_len += len; + } + + Ok(read_len) + } + + fn try_read_at(&mut self, addr: u64, buf: &mut [u8]) -> io::Result> { + match self.read_at(addr, buf) { + Ok(sz) => Ok(Some(sz)), + Err(_) => Ok(None), + } + } } #[test] -fn raw_file() { - let file = File::open(fixture("mrt100.dll")).unwrap(); - let cache = ReadCache::new(file); - let file_addr_space = FileAddressSpace::new(&cache).unwrap(); - let len = file_addr_space.len(); +fn user_dump() { + let dump = UserDumpParser::new(fixture("udmp.dmp")).unwrap(); + let modules = dump + .modules() + .values() + .map(|module| { + Module::new( + module.path.file_name().unwrap().to_string_lossy(), + module.start_addr(), + module.end_addr(), + ) + }) + .collect::>(); + let mut udmp_addr_space = UserDumpAddrSpace(dump); let mut symb = Builder::default() - .modules(vec![Module::new("mrt100", 0x0, len)].into_iter()) - .online(vec!["https://msdl.microsoft.com/download/symbols/"].into_iter()) + .modules(&modules) + .msft_symsrv() .symcache(&symcache("basics")) - .build(file_addr_space) + .build(&mut udmp_addr_space) .unwrap(); - for (addr, expected_full, expected_modoff) in EXPECTED_RAW { - let mut full = Vec::new(); - symb.full(addr, &mut full).unwrap(); - assert_eq!(String::from_utf8(full).unwrap(), expected_full); + // 0:000> u 00007ff9`aa4f8eb2 + // ntdll!EvtIntReportEventWorker$fin$0+0x2: + // 00007ff9`aa4f8eb2 4883ec50 sub rsp,50h + let mut output = Vec::new(); + symb.full(0x7ff9aa4f8eb2, &mut output).unwrap(); + assert_eq!( + String::from_utf8(output).unwrap(), + "ntdll.dll!EvtIntReportEventWorker$fin$0+0x2" + ); - let mut modoff = Vec::new(); - symb.modoff(addr, &mut modoff).unwrap(); - assert_eq!(String::from_utf8(modoff).unwrap(), expected_modoff); - } + let stats = symb.stats(); + assert_eq!(stats.amount_pdb_downloaded(), 1); + assert!(stats.did_download( + PdbId::new( + "ntdll.pdb", + "8D5D5ED5D5B8AA609A82600C14E3004D".try_into().unwrap(), + 1 + ) + .unwrap() + )); + + drop(symb); + let mut symb_offline = Builder::default() + .symcache(&symcache("basics")) + .modules(&modules) + .build(&mut udmp_addr_space) + .unwrap(); + + // 0:000> u 00007ff9`aa4f8eb2 + // ntdll!EvtIntReportEventWorker$fin$0+0x2: + // 00007ff9`aa4f8eb2 4883ec50 sub rsp,50h + let mut output = Vec::new(); + symb_offline.full(0x7ff9aa4f8eb2, &mut output).unwrap(); + assert_ne!( + String::from_utf8(output).unwrap(), + "ntdll.dll!EvtIntReportEventWorker$fin$0+0x2" + ); - assert_eq!(symb.stats().n_downloads, 1); + let stats = symb_offline.stats(); + assert_eq!(stats.amount_pdb_downloaded(), 0); } diff --git a/src/human.rs b/src/human.rs index 9eb1892..1865b51 100644 --- a/src/human.rs +++ b/src/human.rs @@ -24,7 +24,7 @@ pub trait ToHuman: Sized + Copy { /// Blanket implementation for all the `T` that have what we need. impl ToHuman for T where - T: Into, + T: TryInto, T: Copy, { } @@ -34,12 +34,12 @@ pub struct HumanTime(T); impl Display for HumanTime where - T: Into, + T: TryInto, T: Copy, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut unit = "s"; - let mut time = self.0.into() as f64; + let mut time = self.0.try_into().map_err(|_| std::fmt::Error)? as f64; let m = 60f64; let h = m * m; let d = h * 24.0; @@ -92,12 +92,12 @@ pub struct HumanNumber(T); impl Display for HumanNumber where - T: Into, + T: TryInto, T: Copy, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut unit = ""; - let mut size = self.0.into() as f64; + let mut size = self.0.try_into().map_err(|_| std::fmt::Error)? as f64; let k = 1_000f64; let m = k * k; let b = m * k; diff --git a/src/main.rs b/src/main.rs index 0a2c170..f981941 100644 --- a/src/main.rs +++ b/src/main.rs @@ -67,12 +67,13 @@ impl Display for Stats { ) )?; - if self.symbolizer_stats.size_downloaded > 0 { + let size_downloaded = self.symbolizer_stats.amount_downloaded(); + if size_downloaded > 0 { write!( f, ", downloaded {} / {} PDBs)", - self.symbolizer_stats.size_downloaded.human_bytes(), - self.symbolizer_stats.n_downloads.human_number() + size_downloaded.human_bytes(), + self.symbolizer_stats.amount_pdb_downloaded().human_number() ) } else { write!(f, ")") @@ -105,7 +106,7 @@ impl AddrSpace for AddrSpaceWrapper { } } -type KernelDumpSymbolizer = Symbolizer; +type KernelDumpSymbolizer<'a> = Symbolizer<'a, AddrSpaceWrapper>; /// The style of the symbols. #[derive(Default, Debug, Clone, ValueEnum)] @@ -345,11 +346,12 @@ fn main() -> Result<()> { } // All right, ready to create the symbolizer. + let mut wrapper = AddrSpaceWrapper::new(parser); let mut symbolizer = SymbolizerBuilder::default() .online(args.symsrv.iter()) - .modules(modules.into_iter()) + .modules(&modules) .symcache(&symcache) - .build(AddrSpaceWrapper::new(parser))?; + .build(&mut wrapper)?; let paths = if args.trace.is_dir() { // If we received a path to a directory as input, then we will try to symbolize