probe-rs 0.16.0

A collection of on chip debugging tools to communicate with microchips.
Documentation
use crate::{
    core::{Core, RegisterDataType, RegisterFile},
    Error, RegisterId, RegisterValue,
};

/// The group name of a register.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegisterGroup {
    /// Core / CPU Registers. Using the term `Base` rather than `platform`, because that is what the DWARF spec calls these registers.
    Base,
    /// Argument Register
    Argument,
    /// Result Register
    Result,
    /// [`RegisterFile`] contains some register descriptions that are not part of an array, and may or may not have the same `RegisterId` as registers in other groups.
    Singleton,
}

/// Stores the relevant information from [`RegisterDescription`](crate::core::RegisterDescription)
/// as well as additional information required during debug.
#[derive(Debug, Clone, PartialEq)]
pub struct DebugRegister {
    /// To lookup platform specific details of register definitions.
    pub register_file: &'static RegisterFile,
    /// Register definitions are grouped depending on their purpose.
    pub group: RegisterGroup,
    // TODO: Consider capturing reference to RegisterDescription, so we can delegate actions like size_in_bytes.
    /// The name of the register.
    pub name: &'static str,
    /// If a special name exists for an existing register, e.g. Arm register 'r15' is also known as 'pc' (program counter)
    pub special_name: Option<&'static str>,
    /// The location where the register is stored.
    pub id: RegisterId,
    /// [DWARF](https://dwarfstd.org) specification, section 2.6.1.1.3.1 "... operations encode the names of up to 32 registers, numbered from 0 through 31, inclusive ..."
    pub dwarf_id: Option<u16>,
    /// The type of data stored in a register.
    pub data_type: RegisterDataType,
    /// Size in bits, e.g. 32 or 64.
    pub size_in_bits: usize,
    /// The value of the register is read from the target memory and updated as needed.
    pub value: Option<RegisterValue>,
}

impl DebugRegister {
    /// Test if this is a 32-bit unsigned integer register
    pub(crate) fn is_u32(&self) -> bool {
        self.data_type == RegisterDataType::UnsignedInteger && self.size_in_bits == 32
    }

    /// A helper function to determine if the contained register value is equal to the maximum value that can be stored in that datatype.
    /// Will return false if the value is `None`
    pub(crate) fn is_max_value(&self) -> bool {
        match self.value {
            Some(register_value) => register_value.is_max_value(),
            None => false,
        }
    }

    /// A helper function to determine if the contained register value is zero.
    /// Will return false if the value is `None`
    pub(crate) fn is_zero(&self) -> bool {
        match self.value {
            Some(register_value) => register_value.is_zero(),
            None => false,
        }
    }

    /// Retrieve the special name if it exists, else the actual name using the [`RegisterId`] as an identifier.
    pub fn get_register_name(&self) -> String {
        self.special_name.unwrap_or(self.name).to_string()
    }
}

/// All the registers required for debug related operations.
#[derive(Debug, Clone)]
pub struct DebugRegisters(pub Vec<DebugRegister>);

impl DebugRegisters {
    /// Read all registers defined in [`RegisterFile`] from the given core.
    pub fn from_core(core: &mut Core) -> Self {
        let register_file = core.registers();

        let mut debug_registers = Vec::<DebugRegister>::new();

        let all_registers = [
            (RegisterGroup::Base, register_file.platform_registers),
            (RegisterGroup::Argument, register_file.argument_registers),
            (RegisterGroup::Result, register_file.result_registers),
            (
                RegisterGroup::Singleton,
                &[register_file.frame_pointer.to_owned()],
            ),
            (
                RegisterGroup::Singleton,
                &[register_file.program_counter.to_owned()],
            ),
            (
                RegisterGroup::Singleton,
                &[register_file.return_address.to_owned()],
            ),
            (
                RegisterGroup::Singleton,
                &[register_file.stack_pointer.to_owned()],
            ),
        ];

        for (register_group, register_group_members) in all_registers {
            for (dwarf_id, platform_register) in register_group_members.iter().enumerate() {
                // Check to ensure the register type is compatible with u64.
                if matches!(
                    platform_register.data_type(),
                    RegisterDataType::UnsignedInteger
                ) && platform_register.size_in_bits() <= 64
                {
                    if let Some(special_register) = debug_registers
                        .iter_mut()
                        .find(|debug_register| debug_register.id == platform_register.id)
                    {
                        // Some register definitions are descriptive for registers defined with the same [`RegisterId`] elsewhere, so we treat them differently.
                        special_register.special_name = Some(platform_register.name);
                    } else {
                        // It is safe for us to push a new [`DebugRegister`]
                        debug_registers.push(DebugRegister {
                            register_file,
                            group: register_group,
                            name: platform_register.name(),
                            special_name: None,
                            id: platform_register.id,
                            // TODO: Consider adding dwarf_id to RegisterDescription, to ensure we have the right values.
                            dwarf_id: if matches!(register_group, RegisterGroup::Base) {
                                Some(dwarf_id as u16)
                            } else {
                                None
                            },
                            data_type: platform_register.data_type(),
                            size_in_bits: platform_register.size_in_bits(),
                            value: match core.read_core_reg(platform_register.id) {
                                Ok::<RegisterValue, Error>(register_value) => Some(register_value),
                                Err(e) => {
                                    tracing::warn!(
                                        "Failed to read value for register {:?}: {}",
                                        platform_register,
                                        e
                                    );
                                    None
                                }
                            },
                        });
                    }
                } else {
                    tracing::warn!(
                        "Unsupported platform register type or size for register: {:?}",
                        platform_register
                    );
                }
            }
        }
        DebugRegisters(debug_registers)
    }

    /// Gets the address size for this target, in bytes
    pub fn get_address_size_bytes(&self) -> usize {
        self.get_program_counter().map_or_else(
            || 0,
            |debug_register| (debug_register.size_in_bits + 7) / 8,
            //TODO: use `div_ceil(8)` when it stabilizes
        )
    }

    /// Get the canonical frame address, as specified in the [DWARF](https://dwarfstd.org) specification, section 6.4.
    /// [DWARF](https://dwarfstd.org)
    pub fn get_frame_pointer(&self) -> Option<&DebugRegister> {
        self.0.iter().find(|debug_register| {
            debug_register.id == debug_register.register_file.frame_pointer().id
        })
    }

    /// Get the program counter.
    pub fn get_program_counter(&self) -> Option<&DebugRegister> {
        self.0.iter().find(|debug_register| {
            debug_register.id == debug_register.register_file.program_counter().id
        })
    }

    /// Get a mutable reference to the program counter.
    pub fn get_program_counter_mut(&mut self) -> Option<&mut DebugRegister> {
        self.0.iter_mut().find(|debug_register| {
            debug_register.id == debug_register.register_file.program_counter().id
        })
    }

    /// Get the stack pointer.
    pub fn get_stack_pointer(&self) -> Option<&DebugRegister> {
        self.0.iter().find(|debug_register| {
            debug_register.id == debug_register.register_file.stack_pointer().id
        })
    }

    /// Get the return address.
    pub fn get_return_address(&self) -> Option<&DebugRegister> {
        self.0.iter().find(|debug_register| {
            debug_register.id == debug_register.register_file.return_address().id
        })
    }

    /// Get a register by [`RegisterId`]
    pub fn get_register(&self, register_id: RegisterId) -> Option<&DebugRegister> {
        self.0
            .iter()
            .find(|debug_register| debug_register.id == register_id)
    }

    /// Get a mutable reference register by [`RegisterId`]
    pub fn get_register_mut(&mut self, register_id: RegisterId) -> Option<&mut DebugRegister> {
        self.0
            .iter_mut()
            .find(|debug_register| debug_register.id == register_id)
    }

    /// Get the register value using the positional index into platform registers.
    /// [DWARF](https://dwarfstd.org) specification, section 2.6.1.1.3.1 "... operations encode the names of up to 32 registers, numbered from 0 through 31, inclusive ..."
    pub fn get_register_by_dwarf_id(&self, dwarf_id: u16) -> Option<&DebugRegister> {
        self.0
            .iter()
            .find(|debug_register| debug_register.dwarf_id == Some(dwarf_id))
    }

    /// Retrieve the special name if it exists, else the actual name using the [`RegisterId`] as an identifier.
    pub fn get_register_name(&self, register_id: RegisterId) -> String {
        self.0
            .iter()
            .find(|debug_register| debug_register.id == register_id)
            .map(|debug_register| debug_register.get_register_name())
            .unwrap_or_else(|| "unknown register".to_string())
    }

    /// Retrieve a register by searching against either the name or the special_name.
    pub fn get_register_by_name(&self, register_name: &str) -> Option<DebugRegister> {
        self.0
            .iter()
            .find(|&debug_register| {
                debug_register.name == register_name
                    || debug_register.special_name == Some(register_name)
            })
            .cloned()
    }
}