use std::borrow::Cow;
use std::cell::{RefCell, RefMut};
use std::cmp::Ordering;
use std::collections::btree_map::{BTreeMap, Entry};
use std::fmt;
use std::io::Cursor;
use std::sync::Arc;
use failure::Fail;
use lazycell::LazyCell;
use parking_lot::RwLock;
use pdb::{
AddressMap, FallibleIterator, InlineSiteSymbol, ItemIndex, LineProgram, MachineType, Module,
ModuleInfo, PdbInternalSectionOffset, ProcedureSymbol, SymbolData,
};
use smallvec::SmallVec;
use symbolic_common::{
derive_failure, Arch, AsSelf, CodeId, CpuFamily, DebugId, Name, SelfCell, Uuid,
};
use crate::base::*;
use crate::private::{FunctionStack, Parse};
type Pdb<'d> = pdb::PDB<'d, Cursor<&'d [u8]>>;
const MAGIC_BIG: &[u8] = b"Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53\x00\x00\x00";
#[doc(hidden)]
pub use pdb;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Fail, PartialEq)]
pub enum PdbErrorKind {
#[fail(display = "invalid pdb file")]
BadObject,
#[fail(display = "unexpected inline function without parent")]
UnexpectedInline,
#[fail(display = "failed to format type name")]
FormattingFailed,
}
derive_failure!(
PdbError,
PdbErrorKind,
doc = "An error when dealing with [`PdbObject`](struct.PdbObject.html)."
);
impl From<pdb::Error> for PdbError {
fn from(error: pdb::Error) -> Self {
error.context(PdbErrorKind::BadObject).into()
}
}
impl From<fmt::Error> for PdbError {
fn from(error: fmt::Error) -> Self {
error.context(PdbErrorKind::FormattingFailed).into()
}
}
pub struct PdbObject<'d> {
pdb: Arc<RwLock<Pdb<'d>>>,
debug_info: Arc<pdb::DebugInformation<'d>>,
pdb_info: pdb::PDBInformation<'d>,
public_syms: pdb::SymbolTable<'d>,
data: &'d [u8],
}
unsafe impl Send for PdbObject<'_> {}
unsafe impl Sync for PdbObject<'_> {}
impl<'d> PdbObject<'d> {
pub fn test(data: &[u8]) -> bool {
data.starts_with(MAGIC_BIG)
}
pub fn parse(data: &'d [u8]) -> Result<Self, PdbError> {
let mut pdb = Pdb::open(Cursor::new(data))?;
let dbi = pdb.debug_information()?;
let pdbi = pdb.pdb_information()?;
let pubi = pdb.global_symbols()?;
Ok(PdbObject {
pdb: Arc::new(RwLock::new(pdb)),
debug_info: Arc::new(dbi),
pdb_info: pdbi,
public_syms: pubi,
data,
})
}
pub fn file_format(&self) -> FileFormat {
FileFormat::Pdb
}
pub fn code_id(&self) -> Option<CodeId> {
None
}
pub fn debug_id(&self) -> DebugId {
let age = self.debug_info.age().unwrap_or(self.pdb_info.age);
match Uuid::from_slice(&self.pdb_info.guid.as_bytes()[..]) {
Ok(uuid) => DebugId::from_parts(uuid, age),
Err(_) => DebugId::default(),
}
}
pub fn arch(&self) -> Arch {
self.debug_info
.machine_type()
.ok()
.map(arch_from_machine)
.unwrap_or_default()
}
pub fn kind(&self) -> ObjectKind {
ObjectKind::Debug
}
pub fn load_address(&self) -> u64 {
0
}
pub fn has_symbols(&self) -> bool {
true
}
pub fn symbols(&self) -> PdbSymbolIterator<'d, '_> {
PdbSymbolIterator {
symbols: self.public_syms.iter(),
address_map: self.pdb.write().address_map().ok(),
}
}
pub fn symbol_map(&self) -> SymbolMap<'d> {
self.symbols().collect()
}
pub fn has_debug_info(&self) -> bool {
true
}
pub fn has_sources(&self) -> bool {
false
}
pub fn debug_session(&self) -> Result<PdbDebugSession<'d>, PdbError> {
PdbDebugSession::build(self)
}
pub fn has_unwind_info(&self) -> bool {
self.arch().cpu_family() == CpuFamily::Intel32
}
pub fn data(&self) -> &'d [u8] {
self.data
}
#[doc(hidden)]
pub fn inner(&self) -> &RwLock<Pdb<'d>> {
&self.pdb
}
}
impl fmt::Debug for PdbObject<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PdbObject")
.field("debug_id", &self.debug_id())
.field("arch", &self.arch())
.field("load_address", &format_args!("{:#x}", self.load_address()))
.field("has_symbols", &self.has_symbols())
.field("has_debug_info", &self.has_debug_info())
.field("has_unwind_info", &self.has_unwind_info())
.finish()
}
}
impl<'slf, 'd: 'slf> AsSelf<'slf> for PdbObject<'d> {
type Ref = PdbObject<'slf>;
fn as_self(&'slf self) -> &Self::Ref {
unsafe { std::mem::transmute(self) }
}
}
impl<'d> Parse<'d> for PdbObject<'d> {
type Error = PdbError;
fn test(data: &[u8]) -> bool {
Self::test(data)
}
fn parse(data: &'d [u8]) -> Result<Self, PdbError> {
Self::parse(data)
}
}
impl<'d> ObjectLike for PdbObject<'d> {
type Error = PdbError;
type Session = PdbDebugSession<'d>;
fn file_format(&self) -> FileFormat {
self.file_format()
}
fn code_id(&self) -> Option<CodeId> {
self.code_id()
}
fn debug_id(&self) -> DebugId {
self.debug_id()
}
fn arch(&self) -> Arch {
self.arch()
}
fn kind(&self) -> ObjectKind {
self.kind()
}
fn load_address(&self) -> u64 {
self.load_address()
}
fn has_symbols(&self) -> bool {
self.has_symbols()
}
fn symbols(&self) -> DynIterator<'_, Symbol<'_>> {
unsafe { std::mem::transmute(Box::new(self.symbols()) as DynIterator<'_, _>) }
}
fn symbol_map(&self) -> SymbolMap<'_> {
self.symbol_map()
}
fn has_debug_info(&self) -> bool {
self.has_debug_info()
}
fn debug_session(&self) -> Result<Self::Session, Self::Error> {
self.debug_session()
}
fn has_unwind_info(&self) -> bool {
self.has_unwind_info()
}
fn has_sources(&self) -> bool {
self.has_sources()
}
}
pub(crate) fn arch_from_machine(machine: MachineType) -> Arch {
match machine {
MachineType::X86 => Arch::X86,
MachineType::Amd64 => Arch::Amd64,
MachineType::Arm => Arch::Arm,
MachineType::Arm64 => Arch::Arm64,
MachineType::PowerPC => Arch::Ppc,
_ => Arch::Unknown,
}
}
pub struct PdbSymbolIterator<'d, 'o> {
symbols: pdb::SymbolIter<'o>,
address_map: Option<AddressMap<'d>>,
}
impl<'d, 'o> Iterator for PdbSymbolIterator<'d, 'o> {
type Item = Symbol<'d>;
fn next(&mut self) -> Option<Self::Item> {
let address_map = self.address_map.as_ref()?;
while let Ok(Some(symbol)) = self.symbols.next() {
if let Ok(SymbolData::Public(public)) = symbol.parse() {
if !public.function {
continue;
}
let address = match public.offset.to_rva(address_map) {
Some(address) => address,
None => continue,
};
let cow = public.name.to_string();
let name = Cow::from(String::from(if cow.starts_with('_') {
&cow[1..]
} else {
&cow
}));
return Some(Symbol {
name: Some(name),
address: u64::from(address.0),
size: 0,
});
}
}
None
}
}
struct ItemMap<'s, I: ItemIndex> {
iter: pdb::ItemIter<'s, I>,
finder: pdb::ItemFinder<'s, I>,
}
impl<'s, I> ItemMap<'s, I>
where
I: ItemIndex,
{
pub fn try_get(&mut self, index: I) -> Result<pdb::Item<'s, I>, PdbError> {
if index <= self.finder.max_index() {
return Ok(self.finder.find(index)?);
}
while let Some(item) = self.iter.next()? {
self.finder.update(&self.iter);
match item.index().partial_cmp(&index) {
Some(Ordering::Equal) => return Ok(item),
Some(Ordering::Greater) => break,
_ => continue,
}
}
Err(pdb::Error::TypeNotFound(index.into()).into())
}
}
type TypeMap<'d> = ItemMap<'d, pdb::TypeIndex>;
type IdMap<'d> = ItemMap<'d, pdb::IdIndex>;
struct PdbStreams<'d> {
debug_info: Arc<pdb::DebugInformation<'d>>,
type_info: pdb::TypeInformation<'d>,
id_info: pdb::IdInformation<'d>,
}
impl<'d> PdbStreams<'d> {
fn from_pdb(pdb: &PdbObject<'d>) -> Result<Self, PdbError> {
let mut p = pdb.pdb.write();
Ok(Self {
debug_info: pdb.debug_info.clone(),
type_info: p.type_information()?,
id_info: p.id_information()?,
})
}
fn type_map(&self) -> TypeMap<'_> {
ItemMap {
iter: self.type_info.iter(),
finder: self.type_info.finder(),
}
}
fn id_map(&self) -> IdMap<'_> {
ItemMap {
iter: self.id_info.iter(),
finder: self.id_info.finder(),
}
}
}
struct PdbDebugInfo<'d> {
pdb: Arc<RwLock<Pdb<'d>>>,
modules: Vec<Module<'d>>,
module_infos: Vec<LazyCell<Option<ModuleInfo<'d>>>>,
module_exports: RefCell<BTreeMap<pdb::ModuleRef, Option<pdb::CrossModuleExports>>>,
address_map: pdb::AddressMap<'d>,
string_table: Option<pdb::StringTable<'d>>,
type_map: RefCell<TypeMap<'d>>,
id_map: RefCell<IdMap<'d>>,
}
impl<'d> PdbDebugInfo<'d> {
fn build(pdb: &PdbObject<'d>, streams: &'d PdbStreams<'d>) -> Result<Self, PdbError> {
let modules = streams.debug_info.modules()?.collect::<Vec<_>>()?;
let module_infos = modules.iter().map(|_| LazyCell::new()).collect();
let module_exports = RefCell::new(BTreeMap::new());
let type_map = RefCell::new(streams.type_map());
let id_map = RefCell::new(streams.id_map());
let mut p = pdb.pdb.write();
let address_map = p.address_map()?;
let string_table = match p.string_table() {
Ok(string_table) => Some(string_table),
Err(pdb::Error::StreamNameNotFound) => None,
Err(e) => return Err(e.into()),
};
drop(p);
Ok(PdbDebugInfo {
pdb: pdb.pdb.clone(),
modules,
module_infos,
module_exports,
address_map,
string_table,
type_map,
id_map,
})
}
fn units(&'d self) -> PdbUnitIterator<'_> {
PdbUnitIterator {
debug_info: self,
index: 0,
}
}
fn get_module(&'d self, index: usize) -> Result<Option<&ModuleInfo<'_>>, PdbError> {
let cell = match self.module_infos.get(index) {
Some(cell) => cell,
None => return Ok(None),
};
let module_opt = cell.try_borrow_with(|| {
let module = &self.modules[index];
self.pdb.write().module_info(module)
})?;
Ok(module_opt.as_ref())
}
fn file_info(&self, file_info: pdb::FileInfo<'d>) -> Result<FileInfo<'_>, PdbError> {
let file_path = match self.string_table {
Some(ref string_table) => file_info.name.to_raw_string(string_table)?,
None => "".into(),
};
Ok(FileInfo::from_path(file_path.as_bytes()))
}
fn get_exports(
&'d self,
module_ref: pdb::ModuleRef,
) -> Result<Option<pdb::CrossModuleExports>, PdbError> {
let name = match self.string_table {
Some(ref string_table) => module_ref.0.to_string_lossy(string_table)?,
None => return Ok(None),
};
let module_index = self
.modules
.iter()
.position(|m| m.module_name().eq_ignore_ascii_case(&name));
let module = match module_index {
Some(index) => self.get_module(index)?,
None => None,
};
Ok(match module {
Some(module) => Some(module.exports()?),
None => None,
})
}
fn resolve_import<I: ItemIndex>(
&'d self,
cross_ref: pdb::CrossModuleRef<I>,
) -> Result<Option<I>, PdbError> {
let pdb::CrossModuleRef(module_ref, local_index) = cross_ref;
let mut module_exports = self.module_exports.borrow_mut();
let exports = match module_exports.entry(module_ref) {
Entry::Vacant(vacant) => vacant.insert(self.get_exports(module_ref)?),
Entry::Occupied(occupied) => occupied.into_mut(),
};
Ok(if let Some(ref exports) = *exports {
exports.resolve_import(local_index)?
} else {
None
})
}
}
impl<'slf, 'd: 'slf> AsSelf<'slf> for PdbDebugInfo<'d> {
type Ref = PdbDebugInfo<'slf>;
fn as_self(&'slf self) -> &Self::Ref {
unsafe { std::mem::transmute(self) }
}
}
pub struct PdbDebugSession<'d> {
cell: SelfCell<Box<PdbStreams<'d>>, PdbDebugInfo<'d>>,
}
impl<'d> PdbDebugSession<'d> {
fn build(pdb: &PdbObject<'d>) -> Result<Self, PdbError> {
let streams = PdbStreams::from_pdb(pdb)?;
let cell = SelfCell::try_new(Box::new(streams), |streams| {
PdbDebugInfo::build(pdb, unsafe { &*streams })
})?;
Ok(PdbDebugSession { cell })
}
pub fn files(&self) -> PdbFileIterator<'_> {
PdbFileIterator {
debug_info: self.cell.get(),
units: self.cell.get().units(),
files: pdb::FileIterator::default(),
finished: false,
}
}
pub fn functions(&self) -> PdbFunctionIterator<'_> {
PdbFunctionIterator {
units: self.cell.get().units(),
functions: Vec::new().into_iter(),
finished: false,
}
}
pub fn source_by_path(&self, _path: &str) -> Result<Option<Cow<'_, str>>, PdbError> {
Ok(None)
}
}
impl DebugSession for PdbDebugSession<'_> {
type Error = PdbError;
fn functions(&self) -> DynIterator<'_, Result<Function<'_>, Self::Error>> {
Box::new(self.functions())
}
fn files(&self) -> DynIterator<'_, Result<FileEntry<'_>, Self::Error>> {
Box::new(self.files())
}
fn source_by_path(&self, path: &str) -> Result<Option<Cow<'_, str>>, Self::Error> {
self.source_by_path(path)
}
}
struct TypeFormatter<'u, 'd> {
unit: &'u Unit<'d>,
type_map: RefMut<'u, TypeMap<'d>>,
id_map: RefMut<'u, IdMap<'d>>,
}
impl<'u, 'd> TypeFormatter<'u, 'd> {
pub fn new(unit: &'u Unit<'d>) -> Self {
Self {
unit,
type_map: unit.debug_info.type_map.borrow_mut(),
id_map: unit.debug_info.id_map.borrow_mut(),
}
}
pub fn write_id<W: fmt::Write>(
&mut self,
target: &mut W,
index: pdb::IdIndex,
) -> Result<(), PdbError> {
let index = match self.unit.resolve_index(index)? {
Some(index) => index,
None => return Ok(write!(target, "<redacted>")?),
};
let id = self.id_map.try_get(index)?;
match id.parse() {
Ok(pdb::IdData::Function(data)) => {
if let Some(scope) = data.scope {
self.write_id(target, scope)?;
write!(target, "::")?;
}
write!(target, "{}", data.name.to_string())?;
}
Ok(pdb::IdData::MemberFunction(data)) => {
self.write_type(target, data.parent)?;
write!(target, "::{}", data.name.to_string())?;
}
Ok(pdb::IdData::BuildInfo(_)) => {
}
Ok(pdb::IdData::StringList(data)) => {
write!(target, "\"")?;
for (i, string_index) in data.substrings.iter().enumerate() {
if i > 0 {
write!(target, "\" \"")?;
}
self.write_type(target, *string_index)?;
}
write!(target, "\"")?;
}
Ok(pdb::IdData::String(data)) => {
write!(target, "{}", data.name.to_string())?;
}
Ok(pdb::IdData::UserDefinedTypeSource(_)) => {
}
Err(pdb::Error::UnimplementedTypeKind(_)) => {
write!(target, "<unknown>")?;
}
Err(e) => return Err(e.into()),
}
Ok(())
}
pub fn write_type<W: fmt::Write>(
&mut self,
target: &mut W,
index: pdb::TypeIndex,
) -> Result<(), PdbError> {
let index = match self.unit.resolve_index(index)? {
Some(index) => index,
None => return Ok(write!(target, "<redacted>")?),
};
let ty = self.type_map.try_get(index)?;
match ty.parse() {
Ok(pdb::TypeData::Primitive(_)) => {
}
Ok(pdb::TypeData::Class(data)) => {
write!(target, "{}", data.name.to_string())?;
}
Ok(pdb::TypeData::Member(_)) => {
}
Ok(pdb::TypeData::MemberFunction(data)) => {
self.write_type(target, data.return_type)?;
write!(target, " ")?;
self.write_type(target, data.class_type)?;
write!(target, "::")?;
self.write_type(target, data.argument_list)?;
}
Ok(pdb::TypeData::OverloadedMethod(_)) => {
}
Ok(pdb::TypeData::Method(_)) => {
}
Ok(pdb::TypeData::StaticMember(_)) => {
}
Ok(pdb::TypeData::Nested(_)) => {
}
Ok(pdb::TypeData::BaseClass(_)) => {
}
Ok(pdb::TypeData::VirtualBaseClass(_)) => {
}
Ok(pdb::TypeData::VirtualFunctionTablePointer(_)) => {
}
Ok(pdb::TypeData::Procedure(data)) => {
match data.return_type {
Some(return_type) => self.write_type(target, return_type)?,
None => write!(target, "void")?,
}
write!(target, " ")?;
self.write_type(target, data.argument_list)?;
}
Ok(pdb::TypeData::Pointer(data)) => {
self.write_type(target, data.underlying_type)?;
if let Some(containing_class) = data.containing_class {
write!(target, " ")?;
self.write_type(target, containing_class)?;
} else {
match data.attributes.pointer_mode() {
pdb::PointerMode::Pointer => write!(target, "*")?,
pdb::PointerMode::LValueReference => write!(target, "&")?,
pdb::PointerMode::RValueReference => write!(target, "&&")?,
_ => (),
}
if data.attributes.is_const() {
write!(target, " const")?;
}
if data.attributes.is_volatile() {
write!(target, " volatile")?;
}
if data.attributes.is_unaligned() {
write!(target, " __unaligned")?;
}
if data.attributes.is_restrict() {
write!(target, " __restrict")?;
}
}
}
Ok(pdb::TypeData::Modifier(data)) => {
if data.constant {
write!(target, "const ")?;
}
if data.volatile {
write!(target, "volatile ")?;
}
if data.unaligned {
write!(target, "__unaligned ")?;
}
self.write_type(target, data.underlying_type)?;
}
Ok(pdb::TypeData::Enumeration(data)) => {
write!(target, "{}", data.name.to_string())?;
}
Ok(pdb::TypeData::Enumerate(data)) => {
write!(target, "{}", data.name.to_string())?;
}
Ok(pdb::TypeData::Array(_)) => {
}
Ok(pdb::TypeData::Union(data)) => {
write!(target, "{}", data.name.to_string())?;
}
Ok(pdb::TypeData::Bitfield(_)) => {
}
Ok(pdb::TypeData::FieldList(_)) => {
write!(target, "<field list>")?;
}
Ok(pdb::TypeData::ArgumentList(data)) => {
write!(target, "(")?;
for (i, arg_index) in data.arguments.iter().enumerate() {
if i > 0 {
write!(target, ", ")?;
}
self.write_type(target, *arg_index)?;
}
write!(target, ")")?;
}
Ok(pdb::TypeData::MethodList(_)) => {
}
Err(pdb::Error::UnimplementedTypeKind(_)) => {
write!(target, "<unknown>")?;
}
Err(e) => return Err(e.into()),
}
Ok(())
}
pub fn format_id(&mut self, index: pdb::IdIndex) -> Result<String, PdbError> {
let mut string = String::new();
self.write_id(&mut string, index)?;
Ok(string)
}
}
struct Unit<'s> {
debug_info: &'s PdbDebugInfo<'s>,
module: &'s pdb::ModuleInfo<'s>,
imports: pdb::CrossModuleImports<'s>,
}
impl<'s> Unit<'s> {
fn load(
debug_info: &'s PdbDebugInfo<'s>,
module: &'s pdb::ModuleInfo<'s>,
) -> Result<Self, PdbError> {
let imports = module.imports()?;
Ok(Self {
debug_info,
module,
imports,
})
}
fn resolve_index<I>(&self, index: I) -> Result<Option<I>, PdbError>
where
I: ItemIndex,
{
if index.is_cross_module() {
let cross_ref = self.imports.resolve_import(index)?;
self.debug_info.resolve_import(cross_ref)
} else {
Ok(Some(index))
}
}
fn collect_lines<I>(
&self,
mut line_iter: I,
program: &LineProgram<'s>,
) -> Result<Vec<LineInfo<'s>>, PdbError>
where
I: FallibleIterator<Item = pdb::LineInfo>,
PdbError: From<I::Error>,
{
let address_map = &self.debug_info.address_map;
let mut lines = Vec::new();
while let Some(line_info) = line_iter.next()? {
let rva = match line_info.offset.to_rva(&address_map) {
Some(rva) => u64::from(rva.0),
None => continue,
};
let file_info = program.get_file_info(line_info.file_index)?;
lines.push(LineInfo {
address: rva,
size: line_info.length.map(u64::from),
file: self.debug_info.file_info(file_info)?,
line: line_info.line_start.into(),
});
}
Ok(lines)
}
fn handle_procedure(
&self,
proc: ProcedureSymbol<'s>,
program: &LineProgram<'s>,
) -> Result<Option<Function<'s>>, PdbError> {
let address_map = &self.debug_info.address_map;
let address = match proc.offset.to_rva(&address_map) {
Some(addr) => u64::from(addr.0),
None => return Ok(None),
};
let name = Name::new(proc.name.to_string());
let line_iter = program.lines_at_offset(proc.offset);
let lines = self.collect_lines(line_iter, program)?;
Ok(Some(Function {
address,
size: proc.len.into(),
name,
compilation_dir: &[],
lines,
inlinees: Vec::new(),
inline: false,
}))
}
fn handle_inlinee(
&self,
inline_site: InlineSiteSymbol<'s>,
parent_offset: PdbInternalSectionOffset,
inlinee: &pdb::Inlinee<'s>,
program: &LineProgram<'s>,
) -> Result<Option<Function<'s>>, PdbError> {
let line_iter = inlinee.lines(parent_offset, &inline_site);
let lines = self.collect_lines(line_iter, program)?;
let start = match lines.iter().map(|line| line.address).min() {
Some(address) => address,
None => return Ok(None),
};
let end = match lines
.iter()
.map(|line| line.address + line.size.unwrap_or(1))
.max()
{
Some(address) => address,
None => return Ok(None),
};
let mut formatter = TypeFormatter::new(self);
let name = Name::new(formatter.format_id(inline_site.inlinee)?);
Ok(Some(Function {
address: start,
size: end - start,
name,
compilation_dir: &[],
lines,
inlinees: Vec::new(),
inline: true,
}))
}
fn functions(&self) -> Result<Vec<Function<'s>>, PdbError> {
let program = self.module.line_program()?;
let mut symbols = self.module.symbols()?;
let inlinees: BTreeMap<_, _> = self
.module
.inlinees()?
.map(|i| Ok((i.index(), i)))
.collect()?;
let mut depth = 0;
let mut inc_next = false;
let mut skipped_depth = None;
let mut functions = Vec::new();
let mut stack = FunctionStack::new();
let mut proc_offsets = SmallVec::<[_; 3]>::new();
while let Some(symbol) = symbols.next()? {
if inc_next {
depth += 1;
}
inc_next = symbol.starts_scope();
if symbol.ends_scope() {
depth -= 1;
if proc_offsets.last().map_or(false, |&(d, _)| d >= depth) {
proc_offsets.pop();
}
}
match skipped_depth {
Some(skipped) if depth > skipped => continue,
_ => skipped_depth = None,
}
if symbol.ends_scope() {
stack.flush(depth, &mut functions);
}
let function = match symbol.parse() {
Ok(SymbolData::Procedure(proc)) => {
proc_offsets.push((depth, proc.offset));
self.handle_procedure(proc, &program)?
}
Ok(SymbolData::InlineSite(site)) => {
let parent_offset = proc_offsets
.last()
.map(|&(_, offset)| offset)
.ok_or_else(|| PdbError::from(PdbErrorKind::UnexpectedInline))?;
if let Some(inlinee) = inlinees.get(&site.inlinee) {
self.handle_inlinee(site, parent_offset, inlinee, &program)?
} else {
None
}
}
_ => continue,
};
match function {
Some(function) => stack.push(depth, function),
None => skipped_depth = Some(depth),
}
}
stack.flush(0, &mut functions);
Ok(functions)
}
}
struct PdbUnitIterator<'s> {
debug_info: &'s PdbDebugInfo<'s>,
index: usize,
}
impl<'s> Iterator for PdbUnitIterator<'s> {
type Item = Result<Unit<'s>, PdbError>;
fn next(&mut self) -> Option<Self::Item> {
let debug_info = self.debug_info;
while self.index < debug_info.modules.len() {
let result = debug_info.get_module(self.index);
self.index += 1;
let module = match result {
Ok(Some(module)) => module,
Ok(None) => continue,
Err(error) => return Some(Err(error)),
};
return Some(Unit::load(debug_info, module));
}
None
}
}
pub struct PdbFileIterator<'s> {
debug_info: &'s PdbDebugInfo<'s>,
units: PdbUnitIterator<'s>,
files: pdb::FileIterator<'s>,
finished: bool,
}
impl<'s> Iterator for PdbFileIterator<'s> {
type Item = Result<FileEntry<'s>, PdbError>;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
loop {
if let Some(file_result) = self.files.next().transpose() {
let result = file_result
.map_err(|err| err.into())
.and_then(|i| self.debug_info.file_info(i))
.map(|info| FileEntry {
compilation_dir: &[],
info,
});
return Some(result);
}
let unit = match self.units.next() {
Some(Ok(unit)) => unit,
Some(Err(error)) => return Some(Err(error)),
None => break,
};
let line_program = match unit.module.line_program() {
Ok(line_program) => line_program,
Err(error) => return Some(Err(error.into())),
};
self.files = line_program.files();
}
self.finished = true;
None
}
}
pub struct PdbFunctionIterator<'s> {
units: PdbUnitIterator<'s>,
functions: std::vec::IntoIter<Function<'s>>,
finished: bool,
}
impl<'s> Iterator for PdbFunctionIterator<'s> {
type Item = Result<Function<'s>, PdbError>;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
loop {
if let Some(func) = self.functions.next() {
return Some(Ok(func));
}
let unit = match self.units.next() {
Some(Ok(unit)) => unit,
Some(Err(error)) => return Some(Err(error)),
None => break,
};
self.functions = match unit.functions() {
Ok(functions) => functions.into_iter(),
Err(error) => return Some(Err(error)),
};
}
self.finished = true;
None
}
}
impl std::iter::FusedIterator for PdbFunctionIterator<'_> {}