sdb_debugger 0.2.2

Book: Building a Debugger. Rust port of C++ debugger sdb
Documentation
use nix::unistd::Pid;

use super::register_info::RegisterId;
use super::types::FileAddress;
use super::{dwarf::Die, sdb_error::SdbError};
use super::{dwarf::SourceLocation, registers::Registers, types::VirtualAddress};
use std::rc::{Rc, Weak};

use super::target::Target;

pub struct Stack {
    target: Weak<Target>,
    inline_height: u32, // 0
    frames: Vec<StackFrame>,
    current_frame: usize, // 0
    tid: Pid,             // 0
}

impl Stack {
    pub fn tid(&self) -> Pid {
        self.tid
    }

    pub fn new(target: &Weak<Target>, tid: Pid) -> Self {
        Self {
            target: target.clone(),
            inline_height: 0,
            frames: Vec::new(),
            current_frame: 0,
            tid,
        }
    }

    pub fn reset_inline_height(&mut self) -> Result<(), SdbError> {
        let stack = self.inline_stack_at_pc()?;
        self.inline_height = 0;
        let pc = self.get_target().get_pc_file_address(None);
        for it in stack.iter().rev() {
            if it.low_pc()? == pc {
                self.inline_height += 1;
            } else {
                break;
            }
        }
        Ok(())
    }

    pub fn inline_stack_at_pc(&self) -> Result<Vec<Rc<Die>>, SdbError> {
        let pc = self.get_target().get_pc_file_address(Some(self.tid));
        if !pc.has_elf() {
            return Ok(vec![]);
        }
        return pc.rc_elf_file().get_dwarf().inline_stack_at_address(&pc);
    }

    pub fn inline_height(&self) -> u32 {
        self.inline_height
    }

    pub fn get_target(&self) -> Rc<Target> {
        self.target.upgrade().unwrap()
    }

    pub fn simulate_inlined_step_in(&mut self) {
        self.inline_height -= 1;
        self.current_frame = self.inline_height as usize;
    }

    pub fn unwind(&mut self) -> Result<(), SdbError> {
        self.reset_inline_height()?;
        self.current_frame = self.inline_height as usize;
        let target = self.get_target();
        let mut virt_pc = target.get_process().get_pc(Some(self.tid));
        let mut file_pc = target.get_pc_file_address(Some(self.tid));
        let proc = target.get_process();
        let mut regs = proc.get_registers(Some(self.tid)).borrow().clone();

        self.frames.clear();
        if !file_pc.has_elf() {
            return Ok(());
        }
        let mut elf = file_pc.weak_elf_file();
        while virt_pc.addr() != 0 && elf.upgrade().is_some() {
            let dwarf = elf.upgrade().unwrap().get_dwarf();
            let inline_stack = dwarf.inline_stack_at_address(&file_pc)?;
            if inline_stack.is_empty() {
                return Ok(());
            }
            if inline_stack.len() > 1 {
                self.create_base_frame(&regs, inline_stack.clone(), &file_pc, true)?;
                self.create_inline_stack_frames(&regs, inline_stack.clone(), &file_pc)?;
            } else {
                self.create_base_frame(&regs, inline_stack.clone(), &file_pc, false)?;
            }
            regs = dwarf.cfi().borrow_mut().unwind(
                &proc,
                &file_pc,
                &mut self.frames.last_mut().unwrap().registers,
            )?;
            virt_pc = VirtualAddress::new(regs.read_by_id_as::<u64>(RegisterId::rip)? - 1);
            file_pc = virt_pc.to_file_addr_elves(&target.get_elves().borrow());
            elf = file_pc.weak_elf_file();
        }
        Ok(())
    }

    pub fn up(&mut self) {
        self.current_frame += 1;
    }

    pub fn down(&mut self) {
        self.current_frame -= 1;
    }

    pub fn frames(&self) -> &[StackFrame] {
        &self.frames[self.inline_height as usize..]
    }

    pub fn has_frames(&self) -> bool {
        !self.frames.is_empty()
    }

    pub fn current_frame(&self) -> &StackFrame {
        &self.frames[self.current_frame]
    }

    pub fn current_frame_index(&self) -> usize {
        self.current_frame - self.inline_height as usize
    }

    pub fn regs(&self) -> &Registers {
        &self.current_frame().registers
    }

    pub fn get_pc(&self) -> Result<VirtualAddress, SdbError> {
        Ok(VirtualAddress::new(
            self.regs().read_by_id_as::<u64>(RegisterId::rip)?,
        ))
    }

    fn create_inline_stack_frames(
        &mut self,
        regs: &Registers,
        inline_stack: Vec<Rc<Die>>,
        _pc: &FileAddress,
    ) -> Result<(), SdbError> {
        let mut prev_it = inline_stack.last().unwrap().clone();
        for (i, it) in inline_stack.iter().rev().enumerate().skip(1) {
            let inlined_pc = prev_it.low_pc()?.to_virt_addr();
            self.frames.push(StackFrame {
                registers: regs.clone(),
                backtrace_report_address: inlined_pc,
                func_die: it.as_ref().clone(),
                inlined: i + 1 != inline_stack.len(),
                location: prev_it.location()?,
            });
            prev_it = it.clone();
        }
        Ok(())
    }

    fn create_base_frame(
        &mut self,
        regs: &Registers,
        inline_stack: Vec<Rc<Die>>,
        pc: &FileAddress,
        inlined: bool,
    ) -> Result<(), SdbError> {
        let mut backtrace_pc = pc.to_virt_addr();
        let line_entry = pc.rc_elf_file().get_dwarf().line_entry_at_address(pc)?;
        if !line_entry.is_end() {
            backtrace_pc = line_entry.get_current().address.to_virt_addr();
        }

        self.frames.push(StackFrame {
            registers: regs.clone(),
            backtrace_report_address: backtrace_pc,
            func_die: (*inline_stack.last().unwrap().as_ref()).clone(),
            inlined,
            location: SourceLocation {
                file: line_entry
                    .get_current()
                    .file_entry
                    .as_ref()
                    .unwrap()
                    .clone(),
                line: line_entry.get_current().line,
            },
        });
        Ok(())
    }
}

pub struct StackFrame {
    pub registers: Registers,
    pub backtrace_report_address: VirtualAddress,
    pub func_die: Die,
    pub inlined: bool, /* false */
    pub location: SourceLocation,
}