ddbug_parser 0.3.0

Unified debug information parser
Documentation
use crate::file::FileHash;
use crate::{Address, Range, Size};

/// A register number.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Register(pub u16);

impl Register {
    /// The name of the register, if known.
    pub fn name(self, hash: &FileHash) -> Option<&'static str> {
        hash.file.get_register_name(self)
    }
}

/// A location within the stack frame.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct FrameLocation {
    /// The offset from the frame pointer.
    pub offset: i64,
    /// The size of the value in bits.
    pub bit_size: Size,
}

/// A piece of a value.
// TODO: include the address ranges for which this piece is valid
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Piece {
    /// The offset of the piece within the containing object.
    pub bit_offset: u64,
    /// The size of the piece. If none, then the piece is the complete value.
    pub bit_size: Size,
    /// The location of the piece.
    pub location: Location,
    /// The offset of the piece within the location.
    pub location_offset: u64,
    /// If `true`, then the piece does not have a location.
    /// Instead, `location` is the value of the piece.
    pub is_value: bool,
}

/// A value location.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum Location {
    /// The value has been optimized away.
    Empty,
    /// A literal address or value.
    Literal {
        /// The literal address or value.
        value: u64,
    },
    /// The value is stored in a register.
    Register {
        /// The register number.
        register: Register,
    },
    /// The value is stored in memory at an offset from an address stored in a register.
    RegisterOffset {
        /// The register number.
        register: Register,
        /// The offset.
        offset: i64,
    },
    /// The value is stored in memory at an offset from the frame base.
    FrameOffset {
        /// The offset.
        offset: i64,
    },
    /// The value is stored in memory at an offset from the CFA.
    CfaOffset {
        /// The offset.
        offset: i64,
    },
    /// The value is stored in memory at an address. This address may need relocation.
    Address {
        /// The offset.
        address: Address,
    },
    /// The value is stored in memory at an offset within TLS.
    TlsOffset {
        /// The offset.
        offset: u64,
    },
    /// The value is more complex than any of the above variants.
    Other,
}

pub(crate) fn registers<'a>(
    locations: &'a [(Range, Piece)],
) -> impl Iterator<Item = (Range, Register)> + 'a {
    locations.iter().filter_map(|(range, piece)| {
        if piece.is_value {
            return None;
        }
        match piece.location {
            Location::Register { register } => Some((*range, register)),
            _ => None,
        }
    })
}

pub(crate) fn frame_locations<'a>(
    locations: &'a [(Range, Piece)],
) -> impl Iterator<Item = FrameLocation> + 'a {
    locations.iter().filter_map(|(_, piece)| {
        if piece.is_value {
            return None;
        }
        match piece.location {
            // TODO: do we need to distinguish between these?
            Location::FrameOffset { offset } | Location::CfaOffset { offset } => {
                Some(FrameLocation {
                    offset,
                    bit_size: piece.bit_size,
                })
            }
            _ => None,
        }
    })
}

pub(crate) fn register_offsets<'a>(
    locations: &'a [(Range, Piece)],
) -> impl Iterator<Item = (Range, Register, i64)> + 'a {
    locations.iter().filter_map(|(range, piece)| {
        if piece.is_value {
            return None;
        }
        match piece.location {
            Location::RegisterOffset { register, offset } => Some((*range, register, offset)),
            _ => None,
        }
    })
}