From d2d810eae82219932b627ae8ee0b33949f338651 Mon Sep 17 00:00:00 2001 From: godzie44 Date: Sat, 30 Sep 2023 23:17:36 +0300 Subject: [PATCH] refactor(debugger): use custom errors instead of anyhow errors --- src/console/mod.rs | 55 ++-- src/console/print.rs | 5 +- src/debugger/address.rs | 34 ++- src/debugger/breakpoint.rs | 173 +++++-------- src/debugger/command/break.rs | 16 +- src/debugger/command/continue.rs | 7 +- src/debugger/command/memory.rs | 15 +- src/debugger/command/mod.rs | 5 +- src/debugger/command/register.rs | 2 +- src/debugger/command/run.rs | 6 +- src/debugger/debugee/dwarf/eval.rs | 237 ++++++++---------- src/debugger/debugee/dwarf/loader.rs | 9 +- src/debugger/debugee/dwarf/mod.rs | 86 ++++--- src/debugger/debugee/dwarf/type.rs | 15 +- src/debugger/debugee/dwarf/unit/mod.rs | 12 +- src/debugger/debugee/dwarf/unit/parser.rs | 3 +- src/debugger/debugee/dwarf/unwind.rs | 93 +++---- src/debugger/debugee/ldd.rs | 3 +- src/debugger/debugee/mod.rs | 70 +++--- src/debugger/debugee/registry.rs | 9 +- src/debugger/debugee/rendezvous.rs | 2 +- src/debugger/debugee/tracee.rs | 58 ++--- src/debugger/debugee/tracer.rs | 38 ++- src/debugger/error.rs | 229 +++++++++++++++++ src/debugger/mod.rs | 123 ++++----- src/debugger/process.rs | 9 +- src/debugger/register.rs | 15 +- src/debugger/rust/mod.rs | 13 +- src/debugger/step.rs | 27 +- src/debugger/utils.rs | 54 ---- src/debugger/variable/mod.rs | 77 ++++-- src/debugger/variable/select.rs | 54 ++-- src/debugger/variable/specialization/btree.rs | 92 ++++--- .../variable/specialization/hashbrown.rs | 4 +- src/debugger/variable/specialization/mod.rs | 164 ++++++------ tests/debugger/breakpoints.rs | 7 +- tests/debugger/common/mod.rs | 12 +- 37 files changed, 1030 insertions(+), 803 deletions(-) create mode 100644 src/debugger/error.rs diff --git a/src/console/mod.rs b/src/console/mod.rs index 9b88ac04..51737ba6 100644 --- a/src/console/mod.rs +++ b/src/console/mod.rs @@ -2,14 +2,16 @@ use super::debugger::command::Continue; use crate::console::editor::{create_editor, CommandCompleter, RLHelper}; use crate::console::help::*; use crate::console::hook::TerminalHook; -use crate::console::print::style::{AddressView, FilePathView, FunctionNameView, KeywordView}; +use crate::console::print::style::{ + AddressView, ErrorView, FilePathView, FunctionNameView, KeywordView, +}; use crate::console::print::ExternalPrinter; use crate::console::variable::render_variable_ir; use crate::debugger; use crate::debugger::command::r#break::{Break, HandlingResult}; use crate::debugger::command::{ - r#break, Arguments, Backtrace, Command, Frame, FrameResult, Run, SharedLib, StepI, StepInto, - StepOut, StepOver, Symbol, ThreadCommand, ThreadResult, Variables, + r#break, Arguments, Backtrace, Command, Frame, FrameResult, HandlingError, Run, SharedLib, + StepI, StepInto, StepOut, StepOver, Symbol, ThreadCommand, ThreadResult, Variables, }; use crate::debugger::process::{Child, Installed}; use crate::debugger::variable::render::RenderRepr; @@ -17,6 +19,7 @@ use crate::debugger::variable::select::{Expression, VariableSelector}; use crate::debugger::{command, Debugger}; use command::{Memory, Register}; use crossterm::style::Stylize; +use debugger::Error; use nix::sys::signal::{kill, Signal}; use nix::unistd::Pid; use os_pipe::PipeReader; @@ -25,6 +28,7 @@ use rustyline::error::ReadlineError; use rustyline::history::MemHistory; use rustyline::Editor; use std::io::{BufRead, BufReader}; +use std::process::exit; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{Receiver, SyncSender}; use std::sync::{mpsc, Arc, Mutex}; @@ -206,16 +210,19 @@ struct AppLoop { } impl AppLoop { - fn yes(&self, question: &str) -> anyhow::Result { + fn yes(&self, question: &str) -> bool { self.printer.print(question); - let act = self.control_rx.recv()?; + let act = self + .control_rx + .recv() + .expect("unexpected sender disconnect"); match act { Control::Cmd(cmd) => { let cmd = cmd.to_lowercase(); - Ok(cmd == "y" || cmd == "yes") + cmd == "y" || cmd == "yes" } - Control::Terminate => Ok(false), + Control::Terminate => false, } } @@ -233,7 +240,7 @@ impl AppLoop { Ok(()) } - fn handle_command(&mut self, cmd: &str) -> anyhow::Result<()> { + fn handle_command(&mut self, cmd: &str) -> Result<(), HandlingError> { match Command::parse(cmd)? { Command::PrintVariables(print_var_command) => Variables::new(&self.debugger) .handle(print_var_command)? @@ -326,7 +333,7 @@ impl AppLoop { static ALREADY_RUN: AtomicBool = AtomicBool::new(false); if ALREADY_RUN.load(Ordering::Acquire) { - if self.yes("Restart program? (y or n)")? { + if self.yes("Restart program? (y or n)") { Run::new(&mut self.debugger).restart()? } } else { @@ -386,17 +393,15 @@ impl AppLoop { Ok(r#break::HandlingResult::Dump(brkpts)) => brkpts .iter() .for_each(|brkpt| print_bp("- Breakpoint", brkpt)), - Err(r#break::BreakpointError::SetError( - r#break::SetBreakpointError::PlaceNotFound(_), - )) => { + Err(Error::NoSuitablePlace) => { if self.yes( "Add deferred breakpoint for future shared library load? (y or n)", - )? { + ) { brkpt_cmd = BreakpointCommand::AddDeferred( - brkpt_cmd - .breakpoint() - .expect("unreachable: deferred breakpoint must based on exists breakpoint"), - ); + brkpt_cmd + .breakpoint() + .expect("unreachable: deferred breakpoint must based on exists breakpoint"), + ); continue; } } @@ -497,7 +502,21 @@ impl AppLoop { Control::Cmd(command) => { thread::sleep(Duration::from_millis(1)); if let Err(e) = self.handle_command(&command) { - self.printer.print(format!("error: {:#}", e)); + match e { + HandlingError::Parser(_) => { + self.printer.print(ErrorView::from(e)); + } + HandlingError::Debugger(ref err) if err.is_fatal() => { + self.printer.print(ErrorView::from("shutdown debugger")); + self.printer + .print(ErrorView::from(format!("fatal debugger error: {e:#}"))); + exit(0); + } + HandlingError::Debugger(_) => { + self.printer + .print(ErrorView::from(format!("debugger error: {e:#}"))); + } + } } } Control::Terminate => { diff --git a/src/console/print.rs b/src/console/print.rs index ad53dfb0..d6132cef 100644 --- a/src/console/print.rs +++ b/src/console/print.rs @@ -26,8 +26,8 @@ impl ExternalPrinter { Ok(Self { printer: None }) } - pub fn print(&self, msg: impl Into) { - let msg = msg.into(); + pub fn print(&self, msg: impl ToString) { + let msg = msg.to_string(); match &self.printer { None => { println!("{msg}") @@ -105,4 +105,5 @@ pub mod style { view_struct!(FilePathView, Color::Green); view_struct!(FunctionNameView, Color::Yellow); view_struct!(KeywordView, Color::Magenta); + view_struct!(ErrorView, Color::Red); } diff --git a/src/debugger/address.rs b/src/debugger/address.rs index 801ec5c2..cd191e86 100644 --- a/src/debugger/address.rs +++ b/src/debugger/address.rs @@ -1,4 +1,6 @@ +use crate::debugger::debugee::dwarf::DebugInformation; use crate::debugger::debugee::Debugee; +use crate::debugger::error::Error; use gimli::Range; use std::fmt::{Display, Formatter}; @@ -13,7 +15,7 @@ impl RelocatedAddress { GlobalAddress(self.0 - offset) } - pub fn into_global(self, debugee: &Debugee) -> anyhow::Result { + pub fn into_global(self, debugee: &Debugee) -> Result { Ok(self.remove_vas_region_offset(debugee.mapping_offset_for_pc(self)?)) } @@ -74,10 +76,38 @@ impl Display for RelocatedAddress { pub struct GlobalAddress(usize); impl GlobalAddress { - pub fn relocate(self, offset: usize) -> RelocatedAddress { + fn relocate(self, offset: usize) -> RelocatedAddress { RelocatedAddress(self.0 + offset) } + /// Relocate address to VAS segment determined by debug information. + /// + /// # Errors + /// + /// Return error if no VAS offset for debug information. + pub fn relocate_to_segment( + self, + debugee: &Debugee, + segment: &DebugInformation, + ) -> Result { + let offset = debugee.mapping_offset_for_file(segment)?; + Ok(self.relocate(offset)) + } + + /// Relocate address to VAS segment determined by another address. + /// + /// # Errors + /// + /// Return error if no VAS offset for address. + pub fn relocate_to_segment_by_pc( + self, + debugee: &Debugee, + pc: RelocatedAddress, + ) -> Result { + let offset = debugee.mapping_offset_for_pc(pc)?; + Ok(self.relocate(offset)) + } + pub fn in_range(self, range: &Range) -> bool { u64::from(self) >= range.begin && u64::from(self) < range.end } diff --git a/src/debugger/breakpoint.rs b/src/debugger/breakpoint.rs index 0f91f136..8f8e4154 100644 --- a/src/debugger/breakpoint.rs +++ b/src/debugger/breakpoint.rs @@ -1,9 +1,10 @@ use crate::debugger::address::{Address, RelocatedAddress}; use crate::debugger::debugee::dwarf::unit::PlaceDescriptorOwned; use crate::debugger::debugee::dwarf::DebugInformation; -use crate::debugger::debugee::{dwarf, Debugee}; +use crate::debugger::debugee::Debugee; +use crate::debugger::error::Error; +use crate::debugger::error::Error::{NoDebugInformation, NoSuitablePlace, PlaceNotFound}; use crate::debugger::Debugger; -use anyhow::anyhow; use nix::libc::c_void; use nix::sys; use nix::unistd::Pid; @@ -19,18 +20,6 @@ enum BrkptsToAddRequest { Uninit(Vec), } -#[derive(Debug, thiserror::Error)] -pub enum SetBreakpointError { - #[error("{0}")] - PlaceNotFound(String), - #[error(transparent)] - DebugInfoError(#[from] dwarf::DebugInformationError), - #[error(transparent)] - SettingError(#[from] anyhow::Error), -} - -pub type SetResult = Result; - impl Debugger { /// Create and enable breakpoint at debugee address space /// @@ -40,34 +29,38 @@ impl Debugger { /// /// # Errors /// - /// Return [`SetBreakpointError::PlaceNotFound`] if no place found for address, - /// return [`SetBreakpointError::DebugInfoError`] if errors occurs while fetching debug information. - pub fn set_breakpoint_at_addr(&mut self, addr: RelocatedAddress) -> SetResult { + /// Return [`SetupError::PlaceNotFound`] if no place found for address, + /// return [`BreakpointError::DebugInformation`] if errors occurs while fetching debug information. + pub fn set_breakpoint_at_addr( + &mut self, + addr: RelocatedAddress, + ) -> Result { if self.debugee.is_in_progress() { - let dwarf = self.debugee.debug_info(addr)?; + let dwarf = self + .debugee + .debug_info(addr) + .map_err(|_| NoDebugInformation("current place"))?; let global_addr = addr.into_global(&self.debugee)?; let place = dwarf .find_place_from_pc(global_addr)? .map(|p| p.to_owned()) - .ok_or(SetBreakpointError::PlaceNotFound( - "Unknown address".to_string(), - ))?; + .ok_or(PlaceNotFound(global_addr))?; - Ok(self.breakpoints.add_and_enable(Breakpoint::new( + return self.breakpoints.add_and_enable(Breakpoint::new( dwarf.pathname(), addr, self.process.pid(), Some(place), - ))?) - } else { - Ok(self.breakpoints.add_uninit(UninitBreakpoint::new( - None::, - Address::Relocated(addr), - self.process.pid(), - None, - ))) + )); } + + Ok(self.breakpoints.add_uninit(UninitBreakpoint::new( + None::, + Address::Relocated(addr), + self.process.pid(), + None, + ))) } /// Disable and remove breakpoint by its address. @@ -78,33 +71,19 @@ impl Debugger { pub(super) fn remove_breakpoint( &mut self, addr: Address, - ) -> anyhow::Result> { + ) -> Result, Error> { self.breakpoints.remove_by_addr(addr) } - /// Disable and remove breakpoint by its address. - /// - /// # Arguments - /// - /// * `addr`: breakpoint address - pub fn remove_breakpoint_at_addr( - &mut self, - addr: RelocatedAddress, - ) -> anyhow::Result> { - self.breakpoints.remove_by_addr(Address::Relocated(addr)) - } - fn create_breakpoint_at_places( &self, places: Vec<(&DebugInformation, Vec)>, - ) -> dwarf::Result { + ) -> Result { let brkpts_to_add = if self.debugee.is_in_progress() { let mut to_add = Vec::new(); for (dwarf, places) in places { for place in places { - let addr = place - .address - .relocate(self.debugee.mapping_offset_for_file(dwarf)?); + let addr = place.address.relocate_to_segment(&self.debugee, dwarf)?; to_add.push(Breakpoint::new( dwarf.pathname(), addr, @@ -134,7 +113,7 @@ impl Debugger { fn add_breakpoints( &mut self, brkpts_to_add: BrkptsToAddRequest, - ) -> SetResult> { + ) -> Result, Error> { let result: Vec<_> = match brkpts_to_add { BrkptsToAddRequest::Init(init_brkpts) => { let mut result_addrs = Vec::with_capacity(init_brkpts.len()); @@ -179,14 +158,12 @@ impl Debugger { fn addresses_for_breakpoints_at_places( &self, places: &[(&DebugInformation, Vec)], - ) -> dwarf::Result> { + ) -> Result, Error> { let mut init_addresses_to_remove: Vec
= vec![]; if self.debugee.is_in_progress() { for (dwarf, places) in places.iter() { for place in places { - let addr = place - .address - .relocate(self.debugee.mapping_offset_for_file(dwarf)?); + let addr = place.address.relocate_to_segment(&self.debugee, dwarf)?; init_addresses_to_remove.push(Address::Relocated(addr)); } } @@ -205,7 +182,7 @@ impl Debugger { fn remove_breakpoints_at_addresses( &mut self, addresses: impl Iterator, - ) -> anyhow::Result> { + ) -> Result, Error> { let mut result = vec![]; for to_rem in addresses { if let Some(view) = self.breakpoints.remove_by_addr(to_rem)? { @@ -218,7 +195,7 @@ impl Debugger { fn search_functions( &self, tpl: &str, - ) -> dwarf::Result)>> { + ) -> Result)>, Error> { let dwarfs = self.debugee.debug_info_all(); dwarfs @@ -242,14 +219,12 @@ impl Debugger { /// /// # Errors /// - /// Return [`SetBreakpointError::PlaceNotFound`] if function not found, - /// return [`SetBreakpointError::DebugInfoError`] if errors occurs while fetching debug information. - pub fn set_breakpoint_at_fn(&mut self, template: &str) -> SetResult> { + /// Return [`SetupError::PlaceNotFound`] if function not found, + /// return [`BreakpointError::DebugInformation`] if errors occurs while fetching debug information. + pub fn set_breakpoint_at_fn(&mut self, template: &str) -> Result, Error> { let places = self.search_functions(template)?; if places.iter().all(|(_, places)| places.is_empty()) { - return Err(SetBreakpointError::PlaceNotFound(format!( - "Function \"{template}\" not found" - ))); + return Err(NoSuitablePlace); } let brkpts = self.create_breakpoint_at_places(places)?; @@ -264,7 +239,7 @@ impl Debugger { pub fn remove_breakpoint_at_fn( &mut self, template: &str, - ) -> anyhow::Result> { + ) -> Result, Error> { let places = self.search_functions(template)?; let addresses = self.addresses_for_breakpoints_at_places(&places)?; self.remove_breakpoints_at_addresses(addresses) @@ -274,7 +249,7 @@ impl Debugger { &self, fine_tpl: &str, line: u64, - ) -> anyhow::Result)>> { + ) -> Result)>, Error> { let dwarfs = self.debugee.debug_info_all(); dwarfs @@ -300,18 +275,16 @@ impl Debugger { /// /// # Errors /// - /// Return [`SetBreakpointError::PlaceNotFound`] if line or file not exists, - /// return [`SetBreakpointError::DebugInfoError`] if errors occurs while fetching debug information. + /// Return [`SetupError::PlaceNotFound`] if line or file not exists, + /// return [`BreakpointError::DebugInformation`] if errors occurs while fetching debug information. pub fn set_breakpoint_at_line( &mut self, fine_path_tpl: &str, line: u64, - ) -> SetResult> { + ) -> Result, Error> { let places = self.search_lines(fine_path_tpl, line)?; if places.iter().all(|(_, places)| places.is_empty()) { - return Err(SetBreakpointError::PlaceNotFound(format!( - "No place found for \"{fine_path_tpl}:{line}\"" - ))); + return Err(NoSuitablePlace); } let brkpts = self.create_breakpoint_at_places(places)?; @@ -328,7 +301,7 @@ impl Debugger { &mut self, fine_name_tpl: &str, line: u64, - ) -> anyhow::Result> { + ) -> Result, Error> { let places = self.search_lines(fine_name_tpl, line)?; let addresses = self.addresses_for_breakpoints_at_places(&places)?; self.remove_breakpoints_at_addresses(addresses) @@ -362,7 +335,7 @@ impl Debugger { /// Refresh deferred breakpoints. Trying to set breakpoint, if success - remove /// breakpoint from deferred list. - pub fn refresh_deferred(&mut self) -> Vec { + pub fn refresh_deferred(&mut self) -> Vec { let mut errors = vec![]; let mut deferred_brkpts = mem::take(&mut self.breakpoints.deferred_breakpoints); @@ -377,7 +350,7 @@ impl Debugger { match mb_error { None => false, - Some(SetBreakpointError::PlaceNotFound(_)) => true, + Some(NoSuitablePlace) => true, Some(err) => { errors.push(err); true @@ -536,25 +509,26 @@ impl Breakpoint { matches!(self.r#type, BrkptType::Temporary) } - pub fn enable(&self) -> nix::Result<()> { + pub fn enable(&self) -> Result<(), Error> { let addr = self.addr.as_usize() as *mut c_void; - let data = sys::ptrace::read(self.pid, addr)?; + let data = sys::ptrace::read(self.pid, addr).map_err(Error::Ptrace)?; self.saved_data.set((data & 0xff) as u8); let data_with_pb = (data & !0xff) as u64 | Self::INT3; unsafe { - sys::ptrace::write(self.pid, addr, data_with_pb as *mut c_void)?; + sys::ptrace::write(self.pid, addr, data_with_pb as *mut c_void) + .map_err(Error::Ptrace)?; } self.enabled.set(true); Ok(()) } - pub fn disable(&self) -> nix::Result<()> { + pub fn disable(&self) -> Result<(), Error> { let addr = self.addr.as_usize() as *mut c_void; - let data = sys::ptrace::read(self.pid, addr)? as u64; + let data = sys::ptrace::read(self.pid, addr).map_err(Error::Ptrace)? as u64; let restored: u64 = (data & !0xff) | self.saved_data.get() as u64; unsafe { - sys::ptrace::write(self.pid, addr, restored as *mut c_void)?; + sys::ptrace::write(self.pid, addr, restored as *mut c_void).map_err(Error::Ptrace)?; } self.enabled.set(false); @@ -630,7 +604,7 @@ impl UninitBreakpoint { /// # Panics /// /// Method will panic if calling with unloaded debugee. - pub fn try_into_brkpt(self, debugee: &Debugee) -> anyhow::Result { + pub fn try_into_brkpt(self, debugee: &Debugee) -> Result { debug_assert!( self.r#type == BrkptType::EntryPoint || self.r#type == BrkptType::UserDefined ); @@ -641,14 +615,11 @@ impl UninitBreakpoint { }; let dwarf = match self.debug_info_file { - None if self.r#type == BrkptType::EntryPoint => Some(debugee.program_debug_info()?), - None => rel_addr.map(|addr| debugee.debug_info(addr)).transpose()?, - Some(path) => Some(debugee.debug_info_from_file(&path)?), + None if self.r#type == BrkptType::EntryPoint => debugee.program_debug_info().ok(), + None => rel_addr.and_then(|addr| debugee.debug_info(addr).ok()), + Some(path) => debugee.debug_info_from_file(&path).ok(), } - .ok_or(anyhow!( - "debug information not found for breakpoint {}", - self.number - ))?; + .ok_or(NoDebugInformation("breakpoint"))?; let place = if self.r#type == BrkptType::UserDefined { if self.place.is_some() { @@ -657,7 +628,7 @@ impl UninitBreakpoint { Some( dwarf .find_place_from_pc(global_addr)? - .ok_or(anyhow!("unknown place for address: {}", self.addr))? + .ok_or(PlaceNotFound(global_addr))? .to_owned(), ) } @@ -666,7 +637,7 @@ impl UninitBreakpoint { }; Ok(Breakpoint::new_inner( - global_addr.relocate(debugee.mapping_offset_for_file(dwarf)?), + global_addr.relocate_to_segment(debugee, dwarf)?, self.pid, self.number, place, @@ -757,7 +728,7 @@ pub struct BreakpointRegistry { impl BreakpointRegistry { /// Add new breakpoint to registry and enable it. - pub fn add_and_enable(&mut self, brkpt: Breakpoint) -> anyhow::Result { + pub fn add_and_enable(&mut self, brkpt: Breakpoint) -> Result { if let Some(existed) = self.breakpoints.get(&brkpt.addr) { existed.disable()?; } @@ -787,7 +758,7 @@ impl BreakpointRegistry { pub fn remove_by_addr( &mut self, addr: Address, - ) -> anyhow::Result>> { + ) -> Result>, Error> { if let Some(brkpt) = self.disabled_breakpoints.remove(&addr) { return Ok(Some(brkpt.into())); } @@ -803,32 +774,27 @@ impl BreakpointRegistry { } /// Enable currently disabled breakpoints. - pub fn enable_all_breakpoints( - &mut self, - debugee: &Debugee, - ) -> anyhow::Result> { + pub fn enable_all_breakpoints(&mut self, debugee: &Debugee) -> Vec { let mut errors = vec![]; - let mut disabled_breakpoints = std::mem::take(&mut self.disabled_breakpoints); + let mut disabled_breakpoints = mem::take(&mut self.disabled_breakpoints); for (_, uninit_brkpt) in disabled_breakpoints.drain() { - let number = uninit_brkpt.number; - let brkpt = match uninit_brkpt.try_into_brkpt(debugee) { Ok(b) => b, Err(e) => { - errors.push(e.context(format!("broken breakpoint {number}"))); + errors.push(e); continue; } }; if let Err(e) = self.add_and_enable(brkpt) { - errors.push(e.context(format!("broken breakpoint {number}"))); + errors.push(e); } } - Ok(errors) + errors } /// Enable entry point breakpoint if it disabled. - pub fn enable_entry_breakpoint(&mut self, debugee: &Debugee) -> anyhow::Result<()> { + pub fn enable_entry_breakpoint(&mut self, debugee: &Debugee) -> Result<(), Error> { let Some((&key, _)) = self.disabled_breakpoints.iter().find(|(_, brkpt)| { brkpt.r#type == BrkptType::EntryPoint }) else { @@ -844,15 +810,12 @@ impl BreakpointRegistry { } /// Disable currently enabled breakpoints. - pub fn disable_all_breakpoints( - &mut self, - debugee: &Debugee, - ) -> anyhow::Result> { + pub fn disable_all_breakpoints(&mut self, debugee: &Debugee) -> Result, Error> { let mut errors = vec![]; let mut breakpoints = std::mem::take(&mut self.breakpoints); for (_, brkpt) in breakpoints.drain() { if let Err(e) = brkpt.disable() { - errors.push(anyhow!("broken breakpoint {}: {:#}", brkpt.number(), e)); + errors.push(e); } let addr = Address::Global(brkpt.addr.into_global(debugee)?); diff --git a/src/debugger/command/break.rs b/src/debugger/command/break.rs index c76bff36..165d517c 100644 --- a/src/debugger/command/break.rs +++ b/src/debugger/command/break.rs @@ -1,8 +1,8 @@ +use crate::debugger::address::Address; use crate::debugger::breakpoint::BreakpointView; +use crate::debugger::error::Error; use crate::debugger::Debugger; -pub use crate::debugger::breakpoint::SetBreakpointError; - #[derive(Debug, Clone)] pub enum Breakpoint { Address(usize), @@ -41,20 +41,12 @@ pub enum HandlingResult<'a> { AddDeferred, } -#[derive(Debug, thiserror::Error)] -pub enum BreakpointError { - #[error(transparent)] - SetError(#[from] SetBreakpointError), - #[error(transparent)] - OtherError(#[from] anyhow::Error), -} - impl<'a> Break<'a> { pub fn new(debugger: &'a mut Debugger) -> Self { Self { dbg: debugger } } - pub fn handle(&mut self, cmd: &Command) -> Result { + pub fn handle(&mut self, cmd: &Command) -> Result { let result = match cmd { Command::Add(brkpt) => { let res = match brkpt { @@ -70,7 +62,7 @@ impl<'a> Break<'a> { let res = match brkpt { Breakpoint::Address(addr) => self .dbg - .remove_breakpoint_at_addr((*addr).into())? + .remove_breakpoint(Address::Relocated((*addr).into()))? .map(|brkpt| vec![brkpt]) .unwrap_or_default(), Breakpoint::Line(file, line) => { diff --git a/src/debugger/command/continue.rs b/src/debugger/command/continue.rs index bc9343bf..ba433103 100644 --- a/src/debugger/command/continue.rs +++ b/src/debugger/command/continue.rs @@ -1,5 +1,4 @@ use crate::debugger::{command, Debugger}; -use anyhow::Context; pub struct Continue<'a> { dbg: &'a mut Debugger, @@ -11,9 +10,7 @@ impl<'a> Continue<'a> { } pub fn handle(&mut self) -> command::HandleResult<()> { - Ok(self - .dbg - .continue_debugee() - .context("Failed to continue execution")?) + self.dbg.continue_debugee()?; + Ok(()) } } diff --git a/src/debugger/command/memory.rs b/src/debugger/command/memory.rs index a2dc0e01..3942dfe0 100644 --- a/src/debugger/command/memory.rs +++ b/src/debugger/command/memory.rs @@ -1,5 +1,5 @@ +use crate::debugger::error::Error; use crate::debugger::{command, Debugger}; -use anyhow::anyhow; use nix::libc::uintptr_t; use std::mem; @@ -21,16 +21,13 @@ impl<'a> Memory<'a> { pub fn handle(&self, cmd: Command) -> command::HandleResult { let result = match &cmd { Command::Read(addr) => { - let bytes = self - .dbg - .read_memory(*addr, mem::size_of::()) - .map_err(anyhow::Error::from)?; - uintptr_t::from_ne_bytes(bytes.try_into().map_err(|e| anyhow!("{e:?}"))?) + let bytes = self.dbg.read_memory(*addr, mem::size_of::())?; + uintptr_t::from_ne_bytes(bytes.try_into().map_err(|data: Vec| { + Error::TypeBinaryRepr("uintptr_t", data.into_boxed_slice()) + })?) } Command::Write(addr, ptr) => { - self.dbg - .write_memory(*addr, *ptr) - .map_err(anyhow::Error::from)?; + self.dbg.write_memory(*addr, *ptr)?; *ptr } }; diff --git a/src/debugger/command/mod.rs b/src/debugger/command/mod.rs index 6d27a915..9972616a 100644 --- a/src/debugger/command/mod.rs +++ b/src/debugger/command/mod.rs @@ -36,6 +36,7 @@ pub use thread::Result as ThreadResult; pub use thread::Thread as ThreadCommand; pub use variables::Variables; +use crate::debugger::error::Error; use crate::debugger::variable::select::{Expression, VariableSelector}; use anyhow::anyhow; use nix::libc::uintptr_t; @@ -162,7 +163,7 @@ pub enum HandlingError { #[error("malformed command (try `help command`):\n{0}")] Parser(anyhow::Error), #[error(transparent)] - Debugger(#[from] anyhow::Error), + Debugger(#[from] Error), } pub type HandleResult = Result; @@ -486,7 +487,7 @@ impl Command { command(SHARED_LIB_COMMAND, shared_lib_parser), cut(map(not_line_ending, |_| Command::Help { command: None, - reason: Some("undefined command".to_string()), + reason: Some("unknown command".to_string()), })), ))(input) } diff --git a/src/debugger/command/register.rs b/src/debugger/command/register.rs index 12787b1e..04cf888d 100644 --- a/src/debugger/command/register.rs +++ b/src/debugger/command/register.rs @@ -60,7 +60,7 @@ impl<'a> Register<'a> { ]; let register_map = RegisterMap::current(self.dbg.exploration_ctx().pid_on_focus()) - .map_err(|e| HandlingError::Debugger(e.into()))?; + .map_err(HandlingError::Debugger)?; Ok(registers_to_dump .iter() diff --git a/src/debugger/command/run.rs b/src/debugger/command/run.rs index cc5c1569..f8df4cbf 100644 --- a/src/debugger/command/run.rs +++ b/src/debugger/command/run.rs @@ -1,5 +1,4 @@ use crate::debugger::{command, Debugger}; -use anyhow::Context; pub struct Run<'a> { dbg: &'a mut Debugger, @@ -13,12 +12,13 @@ impl<'a> Run<'a> { /// Run a debugee program. /// Return when debugee stopped or ends. pub fn start(&mut self) -> command::HandleResult<()> { - Ok(self.dbg.start_debugee().context("start error")?) + Ok(self.dbg.start_debugee()?) } /// Restart debugee process with saving all user defined breakpoints. /// Return when new debugee stopped or ends. pub fn restart(&mut self) -> command::HandleResult<()> { - Ok(self.dbg.restart_debugee().context("restart error")?) + self.dbg.restart_debugee()?; + Ok(()) } } diff --git a/src/debugger/debugee/dwarf/eval.rs b/src/debugger/debugee/dwarf/eval.rs index a28f56cd..7a4efe3b 100644 --- a/src/debugger/debugee/dwarf/eval.rs +++ b/src/debugger/debugee/dwarf/eval.rs @@ -1,13 +1,16 @@ use crate::debugger; use crate::debugger::address::{GlobalAddress, RelocatedAddress}; -use crate::debugger::debugee::dwarf::eval::EvalError::{OptionRequired, UnsupportedRequire}; use crate::debugger::debugee::dwarf::unit::DieRef; use crate::debugger::debugee::dwarf::unit::{DieVariant, Unit}; use crate::debugger::debugee::dwarf::{ContextualDieRef, DwarfUnwinder, EndianArcSlice}; use crate::debugger::debugee::Debugee; +use crate::debugger::error::Error; +use crate::debugger::error::Error::{ + DieNotFound, EvalOptionRequired, EvalUnsupportedRequire, FunctionNotFound, ImplicitPointer, + NoDieType, NoFunctionRanges, Ptrace, TypeBinaryRepr, UnwindNoContext, +}; use crate::debugger::register::{DwarfRegisterMap, RegisterMap}; use crate::debugger::{debugee, ExplorationContext}; -use anyhow::anyhow; use bytes::{BufMut, Bytes, BytesMut}; use gimli::{ DebugAddr, Encoding, EndianSlice, EvaluationResult, Expression, Location, Piece, Register, @@ -19,25 +22,7 @@ use std::cell::RefCell; use std::cmp::min; use std::collections::hash_map::Entry; use std::collections::HashMap; -use std::{mem, result}; - -#[derive(thiserror::Error, Debug)] -pub enum EvalError { - #[error(transparent)] - Gimli(#[from] gimli::read::Error), - #[error("eval option {0} required")] - OptionRequired(&'static str), - #[error("unsupported evaluation require {0:?}")] - UnsupportedRequire(String), - #[error("nix error {0}")] - Nix(#[from] nix::Error), - #[error("unwind error {0}")] - Unwind(#[from] unwind::Error), - #[error(transparent)] - Other(#[from] anyhow::Error), -} - -pub type Result = result::Result; +use std::mem; /// Resolve requirements that the `ExpressionEvaluator` may need. Relevant for the current breakpoint. /// Some options are lazy to avoid overhead on recalculation. @@ -57,7 +42,7 @@ impl<'a> RequirementsResolver<'a> { } /// Return base address of current frame. - fn base_addr(&self, ctx: &ExplorationContext) -> anyhow::Result { + fn base_addr(&self, ctx: &ExplorationContext) -> Result { match self.base_address.borrow_mut().entry(ctx.pid_on_focus()) { Entry::Occupied(e) => Ok(*e.get()), Entry::Vacant(e) => { @@ -66,7 +51,7 @@ impl<'a> RequirementsResolver<'a> { .debugee .debug_info(ctx.location().pc)? .find_function_by_pc(loc.global_pc)? - .ok_or_else(|| anyhow!("current function not found"))?; + .ok_or(FunctionNotFound(loc.global_pc))?; let base_addr = func.frame_base_addr(ctx, self.debugee)?; Ok(*e.insert(base_addr)) } @@ -74,7 +59,7 @@ impl<'a> RequirementsResolver<'a> { } /// Return canonical frame address of current frame. - fn cfa(&self, ctx: &ExplorationContext) -> anyhow::Result { + fn cfa(&self, ctx: &ExplorationContext) -> Result { match self.cfa.borrow_mut().entry(ctx.pid_on_focus()) { Entry::Occupied(e) => Ok(*e.get()), Entry::Vacant(e) => { @@ -87,11 +72,11 @@ impl<'a> RequirementsResolver<'a> { } } - fn relocation_addr(&self, ctx: &ExplorationContext) -> anyhow::Result { + fn relocation_addr(&self, ctx: &ExplorationContext) -> Result { self.debugee.mapping_offset_for_pc(ctx.location().pc) } - fn resolve_tls(&self, pid: Pid, offset: u64) -> anyhow::Result { + fn resolve_tls(&self, pid: Pid, offset: u64) -> Result { let lm_addr = self.debugee.rendezvous().link_map_main(); self.debugee .tracee_ctl() @@ -101,17 +86,17 @@ impl<'a> RequirementsResolver<'a> { fn debug_addr_section( &self, ctx: &ExplorationContext, - ) -> anyhow::Result<&DebugAddr> { + ) -> Result<&DebugAddr, Error> { Ok(self.debugee.debug_info(ctx.location().pc)?.debug_addr()) } - fn resolve_registers(&self, ctx: &ExplorationContext) -> anyhow::Result { + fn resolve_registers(&self, ctx: &ExplorationContext) -> Result { let current_loc = ctx.location(); let current_fn = self .debugee .debug_info(ctx.location().pc)? .find_function_by_pc(current_loc.global_pc)? - .ok_or_else(|| anyhow!("not in function"))?; + .ok_or(FunctionNotFound(current_loc.global_pc))?; let entry_pc: GlobalAddress = current_fn .die .base_attributes @@ -119,17 +104,16 @@ impl<'a> RequirementsResolver<'a> { .iter() .map(|r| r.begin) .min() - .ok_or_else(|| anyhow!("entry point pc not found"))? + .ok_or_else(|| NoFunctionRanges(current_fn.full_name()))? .into(); let backtrace = self.debugee.unwind(ctx.pid_on_focus())?; - let entry_pc_rel = - entry_pc.relocate(self.debugee.mapping_offset_for_pc(ctx.location().pc)?); + let entry_pc_rel = entry_pc.relocate_to_segment_by_pc(self.debugee, ctx.location().pc)?; backtrace .iter() .enumerate() .find(|(_, frame)| frame.fn_start_ip == Some(entry_pc_rel)) - .map(|(num, _)| -> anyhow::Result { + .map(|(num, _)| -> Result { // try to use libunwind if frame determined let mut registers = RegisterMap::current(ctx.pid_on_focus())?.into(); self.debugee.restore_registers_at_frame( @@ -149,7 +133,7 @@ impl<'a> RequirementsResolver<'a> { }; Ok(unwinder .context_for(&ExplorationContext::new(location, ctx.focus_frame))? - .ok_or(anyhow!("fetch register fail"))? + .ok_or(UnwindNoContext)? .registers()) }) } @@ -224,7 +208,7 @@ impl<'a> ExpressionEvaluator<'a> { &self, ctx: &'a ExplorationContext, expr: Expression, - ) -> Result { + ) -> Result { self.evaluate_with_resolver(ExternalRequirementsResolver::default(), ctx, expr) } @@ -233,7 +217,7 @@ impl<'a> ExpressionEvaluator<'a> { mut resolver: ExternalRequirementsResolver, ctx: &'a ExplorationContext, expr: Expression, - ) -> Result { + ) -> Result { let mut eval = expr.evaluation(self.encoding); let mut result = eval.evaluate()?; @@ -270,7 +254,7 @@ impl<'a> ExpressionEvaluator<'a> { resolver .at_location .take() - .ok_or(OptionRequired("at_location"))? + .ok_or(EvalOptionRequired("at_location"))? .into(), RunTimeEndian::Little, ); @@ -291,14 +275,15 @@ impl<'a> ExpressionEvaluator<'a> { address as usize, size as usize, ) - .map_err(EvalError::Nix)?; + .map_err(Ptrace)?; let value_type = self.value_type_from_offset(base_type); let value = match value_type { ValueType::Generic => { - let u = u64::from_ne_bytes( - memory.try_into().map_err(|e| anyhow!("{e:?}"))?, - ); + let u = + u64::from_ne_bytes(memory.try_into().map_err(|data: Vec<_>| { + TypeBinaryRepr("u64", data.into_boxed_slice()) + })?); Value::Generic(u) } _ => Value::parse( @@ -342,7 +327,7 @@ impl<'a> ExpressionEvaluator<'a> { result = eval.resume_with_entry_value(Value::Generic(u))?; } EvaluationResult::RequiresParameterRef(_) => { - return Err(UnsupportedRequire(format!("{:?}", result))); + return Err(EvalUnsupportedRequire("parameter_ref")); } EvaluationResult::Complete => { unreachable!() @@ -378,96 +363,96 @@ pub enum AddressKind { } impl<'a> CompletedResult<'a> { - pub fn into_scalar(self, address_kind: AddressKind) -> Result { + pub fn into_scalar(self, address_kind: AddressKind) -> Result { let bytes = self.into_raw_buffer(mem::size_of::(), address_kind)?; Ok(scalar_from_bytes(&bytes)) } - pub fn into_raw_buffer(self, byte_size: usize, address_kind: AddressKind) -> Result { + pub fn into_raw_buffer( + self, + byte_size: usize, + address_kind: AddressKind, + ) -> Result { let mut buf = BytesMut::with_capacity(byte_size); - self.inner.into_iter().try_for_each(|piece| -> Result<()> { - let read_size = piece - .size_in_bits - .map(|bits| bits as usize / 8) - .unwrap_or(byte_size); - let offset = piece.bit_offset.unwrap_or(0); - - match piece.location { - Location::Register { register } => { - buf.put(read_register( - self.debugee, - self.ctx, - register, - read_size, - offset, - )?); - } - Location::Address { address } => { - match address_kind { - AddressKind::MemoryAddress => { - let memory = debugger::read_memory_by_pid( - self.ctx.pid_on_focus(), - address as usize, - read_size, - ) - .map_err(EvalError::Nix)?; - buf.put(Bytes::from(memory)) - } - AddressKind::Value => { - buf.put_slice(&address.to_ne_bytes()); - } - }; - } - Location::Value { value } => { - match value { - Value::Generic(v) | Value::U64(v) => buf.put_slice(&v.to_ne_bytes()), - Value::I8(v) => buf.put_slice(&v.to_ne_bytes()), - Value::U8(v) => buf.put_slice(&v.to_ne_bytes()), - Value::I16(v) => buf.put_slice(&v.to_ne_bytes()), - Value::U16(v) => buf.put_slice(&v.to_ne_bytes()), - Value::I32(v) => buf.put_slice(&v.to_ne_bytes()), - Value::U32(v) => buf.put_slice(&v.to_ne_bytes()), - Value::I64(v) => buf.put_slice(&v.to_ne_bytes()), - Value::F32(v) => buf.put_slice(&v.to_ne_bytes()), - Value::F64(v) => buf.put_slice(&v.to_ne_bytes()), - }; - } - Location::Bytes { value, .. } => { - buf.put_slice(value.bytes()); - } - Location::ImplicitPointer { value, byte_offset } => { - let die_ref = DieRef::Global(value); - let dwarf = self.debugee.debug_info(self.ctx.location().pc)?; - let (entry, unit) = dwarf.deref_die(self.unit, die_ref).ok_or_else(|| { - EvalError::Other(anyhow!("die not found by ref: {die_ref:?}")) - })?; - if let DieVariant::Variable(ref variable) = &entry.die { - let ctx_die = ContextualDieRef { - debug_info: dwarf, - unit_idx: unit.idx(), - node: &entry.node, - die: variable, + self.inner + .into_iter() + .try_for_each(|piece| -> Result<(), Error> { + let read_size = piece + .size_in_bits + .map(|bits| bits as usize / 8) + .unwrap_or(byte_size); + let offset = piece.bit_offset.unwrap_or(0); + + match piece.location { + Location::Register { register } => { + buf.put(read_register( + self.debugee, + self.ctx, + register, + read_size, + offset, + )?); + } + Location::Address { address } => { + match address_kind { + AddressKind::MemoryAddress => { + let memory = debugger::read_memory_by_pid( + self.ctx.pid_on_focus(), + address as usize, + read_size, + ) + .map_err(Ptrace)?; + buf.put(Bytes::from(memory)) + } + AddressKind::Value => { + buf.put_slice(&address.to_ne_bytes()); + } }; - let r#type = ctx_die - .r#type() - .ok_or_else(|| EvalError::Other(anyhow!("unknown die type")))?; - let bytes = ctx_die - .read_value(self.ctx, self.debugee, &r#type) - .ok_or_else(|| { - EvalError::Other(anyhow!("implicit pointer address invalid value")) - })?; - let bytes: &[u8] = bytes - .read_slice_at(byte_offset as u64, byte_size) - .map_err(|_| { - EvalError::Other(anyhow!("implicit pointer address invalid value")) - })?; - buf.put_slice(bytes) } - } - Location::Empty => {} - }; - Ok(()) - })?; + Location::Value { value } => { + match value { + Value::Generic(v) | Value::U64(v) => buf.put_slice(&v.to_ne_bytes()), + Value::I8(v) => buf.put_slice(&v.to_ne_bytes()), + Value::U8(v) => buf.put_slice(&v.to_ne_bytes()), + Value::I16(v) => buf.put_slice(&v.to_ne_bytes()), + Value::U16(v) => buf.put_slice(&v.to_ne_bytes()), + Value::I32(v) => buf.put_slice(&v.to_ne_bytes()), + Value::U32(v) => buf.put_slice(&v.to_ne_bytes()), + Value::I64(v) => buf.put_slice(&v.to_ne_bytes()), + Value::F32(v) => buf.put_slice(&v.to_ne_bytes()), + Value::F64(v) => buf.put_slice(&v.to_ne_bytes()), + }; + } + Location::Bytes { value, .. } => { + buf.put_slice(value.bytes()); + } + Location::ImplicitPointer { value, byte_offset } => { + let die_ref = DieRef::Global(value); + let dwarf = self.debugee.debug_info(self.ctx.location().pc)?; + let (entry, unit) = dwarf + .deref_die(self.unit, die_ref) + .ok_or_else(|| DieNotFound(die_ref))?; + if let DieVariant::Variable(ref variable) = &entry.die { + let ctx_die = ContextualDieRef { + debug_info: dwarf, + unit_idx: unit.idx(), + node: &entry.node, + die: variable, + }; + let r#type = ctx_die.r#type().ok_or(NoDieType)?; + let bytes = ctx_die + .read_value(self.ctx, self.debugee, &r#type) + .ok_or(ImplicitPointer)?; + let bytes: &[u8] = bytes + .read_slice_at(byte_offset as u64, byte_size) + .map_err(|_| ImplicitPointer)?; + buf.put_slice(bytes) + } + } + Location::Empty => {} + }; + Ok(()) + })?; Ok(buf.freeze()) } @@ -479,7 +464,7 @@ fn read_register( reg: Register, size_in_bytes: usize, offset: u64, -) -> Result { +) -> Result { let pid = ctx.pid_on_focus(); let mut registers = DwarfRegisterMap::from(RegisterMap::current(pid)?); // try to use registers for in focus frame diff --git a/src/debugger/debugee/dwarf/loader.rs b/src/debugger/debugee/dwarf/loader.rs index 0a82da08..fc5011be 100644 --- a/src/debugger/debugee/dwarf/loader.rs +++ b/src/debugger/debugee/dwarf/loader.rs @@ -1,4 +1,5 @@ use crate::debugger::debugee::dwarf::EndianArcSlice; +use crate::debugger::error::Error; use gimli::{ AbbreviationsCache, DebugAbbrev, DebugAddr, DebugAranges, DebugInfo, DebugLine, DebugLineStr, DebugLoc, DebugLocLists, DebugRanges, DebugRngLists, DebugStr, DebugStrOffsets, DebugTypes, @@ -32,7 +33,7 @@ pub fn load_section( id: SectionId, file: &File, endian: RunTimeEndian, -) -> gimli::Result { +) -> Result { let data = file .section_by_name(id.name()) .and_then(|section| section.uncompressed_data().ok()) @@ -43,7 +44,7 @@ pub fn load_section( /// Create a function that load section and put in [`Sections`] struct in right place. macro_rules! make_sect_loader { ($file: expr, $endian: expr, $field: tt) => {{ - move |dest: Arc>>| -> gimli::Result<()> { + move |dest: Arc>>| -> Result<(), Error> { let sect = Section::load(|id| load_section(id, $file, $endian))?; let mut lock = dest.lock().expect("unexpected: panic in another lock"); let sections = lock.as_mut().expect("unexpected: sections must exists"); @@ -60,7 +61,7 @@ macro_rules! make_sect_loader { /// /// * `file`: object file with debug information /// * `endian`: file endian -pub fn load_par(file: &File, endian: RunTimeEndian) -> gimli::Result> { +pub fn load_par(file: &File, endian: RunTimeEndian) -> Result, Error> { let load_debug_abbrev = make_sect_loader!(file, endian, debug_abbrev); let load_debug_addr = make_sect_loader!(file, endian, debug_addr); let load_debug_aranges = make_sect_loader!(file, endian, debug_aranges); @@ -76,7 +77,7 @@ pub fn load_par(file: &File, endian: RunTimeEndian) -> gimli::Result = - Vec>>) -> gimli::Result<()> + Send + Sync + 'a>>; + Vec>>) -> Result<(), Error> + Send + Sync + 'a>>; let loaders: SectLoaders = vec![ Box::new(load_debug_abbrev), diff --git a/src/debugger/debugee/dwarf/mod.rs b/src/debugger/debugee/dwarf/mod.rs index 32be4a71..a186f071 100644 --- a/src/debugger/debugee/dwarf/mod.rs +++ b/src/debugger/debugee/dwarf/mod.rs @@ -21,10 +21,13 @@ use crate::debugger::debugee::dwarf::unit::{ }; use crate::debugger::debugee::dwarf::utils::PathSearchIndex; use crate::debugger::debugee::{Debugee, Location}; +use crate::debugger::error::Error; +use crate::debugger::error::Error::{ + DebugIDFormat, FBANotAnExpression, FunctionNotFound, NoFBA, NoFunctionRanges, UnitNotFound, +}; use crate::debugger::register::{DwarfRegisterMap, RegisterMap}; use crate::debugger::ExplorationContext; use crate::{muted_error, resolve_unit_call, weak_error}; -use anyhow::{anyhow, bail}; use bytes::Bytes; use fallible_iterator::FallibleIterator; use gimli::CfaRule::RegisterAndOffset; @@ -91,16 +94,6 @@ impl Clone for DebugInformation { } } -#[derive(Debug, thiserror::Error)] -pub enum DebugInformationError { - #[error(transparent)] - Other(#[from] anyhow::Error), - #[error("Not enough debug information to complete the request")] - IncompleteInformation, -} - -pub type Result = std::result::Result; - /// Using this macro means a promise that debug information exists in context of usage. #[macro_export] macro_rules! debug_info_exists { @@ -123,10 +116,10 @@ impl DebugInformation { } /// Return all dwarf units or error if no debug information found. - fn get_units(&self) -> Result<&[Unit]> { + fn get_units(&self) -> Result<&[Unit], Error> { self.units .as_deref() - .ok_or(DebugInformationError::IncompleteInformation) + .ok_or(Error::NoDebugInformation("file")) } /// Return false if file dont contains a debug information. @@ -179,7 +172,7 @@ impl DebugInformation { registers: &DwarfRegisterMap, utr: &UnwindTableRow, ctx: &ExplorationContext, - ) -> anyhow::Result { + ) -> Result { let rule = utr.cfa(); match rule { RegisterAndOffset { register, offset } => { @@ -188,7 +181,7 @@ impl DebugInformation { } CfaRule::Expression(expr) => { let unit = debug_info_exists!(self.find_unit_by_pc(ctx.location().global_pc)) - .ok_or_else(|| anyhow!("undefined unit"))?; + .ok_or(UnitNotFound(ctx.location().global_pc))?; let evaluator = resolve_unit_call!(&self.inner, unit, evaluator, debugee); let expr_result = evaluator.evaluate(ctx, expr.clone())?; @@ -201,7 +194,7 @@ impl DebugInformation { &self, debugee: &Debugee, expl_ctx: &ExplorationContext, - ) -> anyhow::Result { + ) -> Result { let mut ctx = Box::new(UnwindContext::new()); let row = self.eh_frame.unwind_info_for_address( &self.bases, @@ -222,7 +215,7 @@ impl DebugInformation { } /// Return a list of all known files. - pub fn known_files(&self) -> Result> { + pub fn known_files(&self) -> Result, Error> { Ok(self.get_units()?.iter().flat_map(|unit| unit.files())) } @@ -233,7 +226,7 @@ impl DebugInformation { /// * `pc`: program counter value /// /// returns: `None` if unit not found, error if no debug information found - fn find_unit_by_pc(&self, pc: GlobalAddress) -> Result> { + fn find_unit_by_pc(&self, pc: GlobalAddress) -> Result, Error> { Ok(self.get_units()?.iter().find(|&unit| { match unit .ranges() @@ -249,13 +242,16 @@ impl DebugInformation { } /// Returns best matched place by program counter global address. - pub fn find_place_from_pc(&self, pc: GlobalAddress) -> Result> { + pub fn find_place_from_pc(&self, pc: GlobalAddress) -> Result, Error> { let mb_unit = self.find_unit_by_pc(pc)?; Ok(mb_unit.and_then(|u| u.find_place_by_pc(pc))) } /// Returns place with line address equals to program counter global address. - pub fn find_exact_place_from_pc(&self, pc: GlobalAddress) -> Result> { + pub fn find_exact_place_from_pc( + &self, + pc: GlobalAddress, + ) -> Result, Error> { let mb_unit = self.find_unit_by_pc(pc)?; Ok(mb_unit.and_then(|u| u.find_exact_place_by_pc(pc))) } @@ -268,7 +264,7 @@ impl DebugInformation { pub fn find_function_by_pc( &self, pc: GlobalAddress, - ) -> Result>> { + ) -> Result>, Error> { let mb_unit = self.find_unit_by_pc(pc)?; Ok(mb_unit.and_then(|unit| { let pc = u64::from(pc); @@ -306,7 +302,10 @@ impl DebugInformation { /// # Arguments /// /// * `template`: search template (full function path or part of this path). - pub fn search_functions(&self, template: &str) -> Result>> { + pub fn search_functions( + &self, + template: &str, + ) -> Result>, Error> { Ok(self .get_units()? .iter() @@ -342,7 +341,7 @@ impl DebugInformation { &self, file_tpl: &str, line: u64, - ) -> Result>> { + ) -> Result>, Error> { let files = self.files_index.get(file_tpl); let mut result = vec![]; @@ -371,7 +370,10 @@ impl DebugInformation { /// # Arguments /// /// * `template`: search template (full function path or part of this path). - pub fn search_places_for_fn_tpl(&self, template: &str) -> Result> { + pub fn search_places_for_fn_tpl( + &self, + template: &str, + ) -> Result, Error> { Ok(self .search_functions(template)? .into_iter() @@ -423,7 +425,7 @@ impl DebugInformation { &self, location: Location, name: &str, - ) -> Result>> { + ) -> Result>, Error> { let units = self.get_units()?; let mut found = vec![]; @@ -512,7 +514,7 @@ impl DebugInformationBuilder { fn get_dwarf_from_separate_debug_file<'a, 'b, OBJ>( &self, obj_file: &'a OBJ, - ) -> anyhow::Result> + ) -> Result, Error> where 'a: 'b, OBJ: Object<'a, 'b>, @@ -524,7 +526,7 @@ impl DebugInformationBuilder { // skip 16 byte header let note = &data[16..]; if note.len() < 2 { - bail!("invalid debug-id note format") + return Err(DebugIDFormat); } let dir = format!("{:x}", note[0]); @@ -567,14 +569,14 @@ impl DebugInformationBuilder { Ok(None) } - pub fn build(&self, obj_path: &Path, file: &object::File) -> anyhow::Result { + pub fn build(&self, obj_path: &Path, file: &object::File) -> Result { let endian = if file.is_little_endian() { RunTimeEndian::Little } else { RunTimeEndian::Big }; - let eh_frame = EhFrame::load(|id| -> gimli::Result { + let eh_frame = EhFrame::load(|id| -> Result { loader::load_section(id, file, endian) })?; let section_addr = |name: &str| -> Option { @@ -809,16 +811,12 @@ impl<'ctx> ContextualDieRef<'ctx, FunctionDie> { &self, ctx: &ExplorationContext, debugee: &Debugee, - ) -> anyhow::Result { - let attr = self - .die - .fb_addr - .as_ref() - .ok_or_else(|| anyhow!("no frame base attr"))?; + ) -> Result { + let attr = self.die.fb_addr.as_ref().ok_or(NoFBA)?; let expr = DwarfLocation(attr) .try_as_expression(self.debug_info, self.unit(), ctx.location().global_pc) - .ok_or_else(|| anyhow!("frame base attribute not an expression"))?; + .ok_or(FBANotAnExpression)?; let evaluator = ctx_resolve_unit_call!(self, evaluator, debugee); let result = evaluator @@ -868,23 +866,23 @@ impl<'ctx> ContextualDieRef<'ctx, FunctionDie> { result } - pub fn prolog_start_place(&self) -> anyhow::Result { + pub fn prolog_start_place(&self) -> Result { let low_pc = self .die .base_attributes .ranges .iter() .min_by(|r1, r2| r1.begin.cmp(&r2.begin)) - .ok_or(anyhow!("function ranges not found"))? + .ok_or_else(|| NoFunctionRanges(self.full_name()))? .begin; - debug_info_exists!(self - .debug_info - .find_place_from_pc(GlobalAddress::from(low_pc))) - .ok_or_else(|| anyhow!("invalid function entry")) + let fn_addr = GlobalAddress::from(low_pc); + + debug_info_exists!(self.debug_info.find_place_from_pc(fn_addr)) + .ok_or(FunctionNotFound(fn_addr)) } - pub fn prolog_end_place(&self) -> anyhow::Result { + pub fn prolog_end_place(&self) -> Result { let mut place = self.prolog_start_place()?; while !place.prolog_end { match place.next() { @@ -896,7 +894,7 @@ impl<'ctx> ContextualDieRef<'ctx, FunctionDie> { Ok(place) } - pub fn prolog(&self) -> anyhow::Result { + pub fn prolog(&self) -> Result { let start = self.prolog_start_place()?; let end = self.prolog_end_place()?; Ok(Range { diff --git a/src/debugger/debugee/dwarf/type.rs b/src/debugger/debugee/dwarf/type.rs index 8cead761..293b725b 100644 --- a/src/debugger/debugee/dwarf/type.rs +++ b/src/debugger/debugee/dwarf/type.rs @@ -5,6 +5,7 @@ use crate::debugger::debugee::dwarf::unit::{ VolatileDie, }; use crate::debugger::debugee::dwarf::{eval, ContextualDieRef, EndianArcSlice, NamespaceHierarchy}; +use crate::debugger::error::Error; use crate::debugger::ExplorationContext; use crate::{ctx_resolve_unit_call, weak_error}; use bytes::Bytes; @@ -30,8 +31,8 @@ pub struct MemberLocationExpression { } impl MemberLocationExpression { - fn base_addr(&self, eval_ctx: &EvaluationContext, entity_addr: usize) -> anyhow::Result { - Ok(eval_ctx + fn base_addr(&self, eval_ctx: &EvaluationContext, entity_addr: usize) -> Result { + eval_ctx .evaluator .evaluate_with_resolver( eval::ExternalRequirementsResolver::new() @@ -39,7 +40,7 @@ impl MemberLocationExpression { eval_ctx.expl_ctx, self.expr.clone(), )? - .into_scalar::(AddressKind::Value)?) + .into_scalar::(AddressKind::Value) } } @@ -89,11 +90,11 @@ pub struct ArrayBoundValueExpression { } impl ArrayBoundValueExpression { - fn bound(&self, eval_ctx: &EvaluationContext) -> anyhow::Result { - Ok(eval_ctx + fn bound(&self, eval_ctx: &EvaluationContext) -> Result { + eval_ctx .evaluator .evaluate(eval_ctx.expl_ctx, self.expr.clone())? - .into_scalar::(AddressKind::MemoryAddress)?) + .into_scalar::(AddressKind::MemoryAddress) } } @@ -104,7 +105,7 @@ pub enum ArrayBoundValue { } impl ArrayBoundValue { - pub fn value(&self, eval_ctx: &EvaluationContext) -> anyhow::Result { + pub fn value(&self, eval_ctx: &EvaluationContext) -> Result { match self { ArrayBoundValue::Const(v) => Ok(*v), ArrayBoundValue::Expr(e) => e.bound(eval_ctx), diff --git a/src/debugger/debugee/dwarf/unit/mod.rs b/src/debugger/debugee/dwarf/unit/mod.rs index 4ffbd40f..3bac17c5 100644 --- a/src/debugger/debugee/dwarf/unit/mod.rs +++ b/src/debugger/debugee/dwarf/unit/mod.rs @@ -7,6 +7,7 @@ use crate::debugger::debugee::dwarf::eval::ExpressionEvaluator; use crate::debugger::debugee::dwarf::utils::PathSearchIndex; use crate::debugger::debugee::dwarf::{EndianArcSlice, NamespaceHierarchy}; use crate::debugger::debugee::Debugee; +use crate::debugger::error::Error; use gimli::{ Attribute, AttributeValue, DebugAddrBase, DebugInfoOffset, DebugLocListsBase, DwAte, DwTag, Encoding, Range, UnitHeader, UnitOffset, @@ -389,7 +390,7 @@ macro_rules! resolve_unit_call { UnitResult::Ok(value) => value, UnitResult::Reload => { let parser = DwarfUnitParser::new(&$dwarf); - $unit.reload(parser).expect("parse unit fail unexpectedly"); + $unit.reload(parser).expect("unit parsing was fail unexpectedly"); $unit.$fn_name( $( $arg, @@ -432,9 +433,12 @@ pub struct Unit { impl Unit { /// Update unit to full state. /// Note: this method will panic if called twice. - pub fn reload(&self, parser: DwarfUnitParser) -> anyhow::Result<()> { - let additional = parser.parse_additional(self.header.take().unwrap())?; - self.lazy_part.set(additional).unwrap(); + pub fn reload(&self, parser: DwarfUnitParser) -> Result<(), Error> { + let additional = parser + .parse_additional(self.header.take().expect("unreachable: header must exists"))?; + self.lazy_part + .set(additional) + .expect("unreachable: lazy part must be empty"); Ok(()) } diff --git a/src/debugger/debugee/dwarf/unit/parser.rs b/src/debugger/debugee/dwarf/unit/parser.rs index bf7b6209..dbf2ccbf 100644 --- a/src/debugger/debugee/dwarf/unit/parser.rs +++ b/src/debugger/debugee/dwarf/unit/parser.rs @@ -7,6 +7,7 @@ use crate::debugger::debugee::dwarf::unit::{ }; use crate::debugger::debugee::dwarf::utils::PathSearchIndex; use crate::debugger::debugee::dwarf::{EndianArcSlice, NamespaceHierarchy}; +use crate::debugger::error::Error; use crate::debugger::rust::Environment; use fallible_iterator::FallibleIterator; use gimli::{ @@ -74,7 +75,7 @@ impl<'a> DwarfUnitParser<'a> { pub(super) fn parse_additional( &self, header: UnitHeader, - ) -> gimli::Result { + ) -> Result { let unit = self.dwarf.unit(header)?; let mut entries: Vec = vec![]; diff --git a/src/debugger/debugee/dwarf/unwind.rs b/src/debugger/debugee/dwarf/unwind.rs index a8925345..1f6632c2 100644 --- a/src/debugger/debugee/dwarf/unwind.rs +++ b/src/debugger/debugee/dwarf/unwind.rs @@ -2,11 +2,14 @@ use crate::debugger::address::RelocatedAddress; use crate::debugger::debugee::dwarf::eval::{AddressKind, ExpressionEvaluator}; use crate::debugger::debugee::dwarf::EndianArcSlice; use crate::debugger::debugee::{Debugee, Location}; +use crate::debugger::error::Error; +use crate::debugger::error::Error::{ + TypeBinaryRepr, UnitNotFound, UnwindNoContext, UnwindTooDeepFrame, +}; use crate::debugger::register::{DwarfRegisterMap, RegisterMap}; use crate::debugger::utils::TryGetOrInsert; use crate::debugger::ExplorationContext; use crate::{debugger, resolve_unit_call, weak_error}; -use anyhow::anyhow; use gimli::{EhFrame, FrameDescriptionEntry, RegisterRule, UnwindSection}; use nix::unistd::Pid; use std::mem; @@ -28,14 +31,14 @@ pub type Backtrace = Vec; /// * `debugee`: debugee instance /// * `pid`: thread for unwinding #[allow(unused)] -pub fn unwind(debugee: &Debugee, pid: Pid) -> anyhow::Result { +pub fn unwind(debugee: &Debugee, pid: Pid) -> Result { #[cfg(feature = "no_libunwind")] { let unwinder = DwarfUnwinder::new(debugee); unwinder.unwind(pid) } #[cfg(not(feature = "no_libunwind"))] - Ok(libunwind::unwind(pid)?) + libunwind::unwind(pid) } /// Restore registers at chosen frame. @@ -52,16 +55,14 @@ pub fn restore_registers_at_frame( pid: Pid, registers: &mut DwarfRegisterMap, frame_num: u32, -) -> anyhow::Result<()> { +) -> Result<(), Error> { #[cfg(feature = "no_libunwind")] { let unwinder = DwarfUnwinder::new(debugee); unwinder.restore_registers_at_frame(pid, registers, frame_num) } #[cfg(not(feature = "no_libunwind"))] - Ok(libunwind::restore_registers_at_frame( - pid, registers, frame_num, - )?) + libunwind::restore_registers_at_frame(pid, registers, frame_num) } /// Return return address for thread current program counter. @@ -71,14 +72,14 @@ pub fn restore_registers_at_frame( /// * `debugee`: debugee instance /// * `pid`: thread for unwinding #[allow(unused)] -pub fn return_addr(debugee: &Debugee, pid: Pid) -> anyhow::Result> { +pub fn return_addr(debugee: &Debugee, pid: Pid) -> Result, Error> { #[cfg(feature = "no_libunwind")] { let unwinder = DwarfUnwinder::new(debugee); unwinder.return_address(pid) } #[cfg(not(feature = "no_libunwind"))] - Ok(libunwind::return_addr(pid)?) + libunwind::return_addr(pid) } /// UnwindContext contains information for unwinding single frame. @@ -95,7 +96,7 @@ impl<'a> UnwindContext<'a> { debugee: &'a Debugee, registers: DwarfRegisterMap, expl_ctx: &ExplorationContext, - ) -> anyhow::Result> { + ) -> Result, Error> { let dwarf = &debugee.debug_info(expl_ctx.location().pc)?; let mut next_registers = registers.clone(); let registers_snap = registers; @@ -121,10 +122,10 @@ impl<'a> UnwindContext<'a> { let cfa = dwarf.evaluate_cfa(debugee, ®isters_snap, row, expl_ctx)?; let mut lazy_evaluator = None; - let evaluator_init_fn = || -> anyhow::Result { + let evaluator_init_fn = || -> Result { let unit = dwarf .find_unit_by_pc(expl_ctx.location().global_pc)? - .ok_or_else(|| anyhow!("undefined unit"))?; + .ok_or(UnitNotFound(expl_ctx.location().global_pc))?; let evaluator = resolve_unit_call!(&dwarf.inner, unit, evaluator, debugee); Ok(evaluator) @@ -147,9 +148,9 @@ impl<'a> UnwindContext<'a> { addr.into(), mem::size_of::() ))?; - u64::from_ne_bytes(weak_error!(bytes - .try_into() - .map_err(|e| anyhow!("{e:?}")))?) + u64::from_ne_bytes(weak_error!(bytes.try_into().map_err( + |data: Vec| TypeBinaryRepr("u64", data.into_boxed_slice()) + ))?) } RegisterRule::ValOffset(offset) => cfa.offset(*offset as isize).into(), RegisterRule::Register(reg) => weak_error!(registers_snap.value(*reg))?, @@ -165,9 +166,9 @@ impl<'a> UnwindContext<'a> { addr, mem::size_of::() ))?; - u64::from_ne_bytes(weak_error!(bytes - .try_into() - .map_err(|e| anyhow!("{e:?}")))?) + u64::from_ne_bytes(weak_error!(bytes.try_into().map_err( + |data: Vec| TypeBinaryRepr("u64", data.into_boxed_slice()) + ))?) } RegisterRule::ValExpression(expr) => { let evaluator = @@ -194,7 +195,7 @@ impl<'a> UnwindContext<'a> { pub fn next( previous_ctx: UnwindContext<'a>, ctx: &ExplorationContext, - ) -> anyhow::Result> { + ) -> Result, Error> { let mut next_frame_registers: DwarfRegisterMap = previous_ctx.registers; next_frame_registers.update(gimli::Register(7), previous_ctx.cfa.into()); UnwindContext::new(previous_ctx.debugee, next_frame_registers, ctx) @@ -235,7 +236,7 @@ impl<'a> DwarfUnwinder<'a> { /// # Arguments /// /// * pid: thread for unwinding - pub fn unwind(&self, pid: Pid) -> anyhow::Result> { + pub fn unwind(&self, pid: Pid) -> Result, Error> { let frame_0_location = self .debugee .tracee_ctl() @@ -256,12 +257,15 @@ impl<'a> DwarfUnwinder<'a> { .debugee .debug_info(ctx.location().pc)? .find_function_by_pc(ctx.location().global_pc)?; - let mapping_offset = self.debugee.mapping_offset_for_pc(ctx.location().pc)?; - let fn_start_at = function.and_then(|func| { - func.prolog_start_place() - .ok() - .map(|prolog| prolog.address.relocate(mapping_offset)) - }); + let fn_start_at = function + .and_then(|func| { + func.prolog_start_place().ok().map(|prolog| { + prolog + .address + .relocate_to_segment_by_pc(self.debugee, ctx.location().pc) + }) + }) + .transpose()?; let mut bt = vec![FrameSpan { func_name: function.and_then(|func| func.full_name()), @@ -292,12 +296,15 @@ impl<'a> DwarfUnwinder<'a> { .debugee .debug_info(next_location.pc)? .find_function_by_pc(next_location.global_pc)?; - let mapping_offset = self.debugee.mapping_offset_for_pc(next_location.pc)?; - let fn_start_at = function.and_then(|func| { - func.prolog_start_place() - .ok() - .map(|prolog| prolog.address.relocate(mapping_offset)) - }); + let fn_start_at = function + .and_then(|func| { + func.prolog_start_place().ok().map(|prolog| { + prolog + .address + .relocate_to_segment_by_pc(self.debugee, next_location.pc) + }) + }) + .transpose()?; let span = FrameSpan { func_name: function.and_then(|func| func.full_name()), @@ -315,7 +322,7 @@ impl<'a> DwarfUnwinder<'a> { pid: Pid, registers: &mut DwarfRegisterMap, frame_num: u32, - ) -> anyhow::Result<()> { + ) -> Result<(), Error> { let frame_0_location = self .debugee .tracee_ctl() @@ -332,12 +339,10 @@ impl<'a> DwarfUnwinder<'a> { DwarfRegisterMap::from(RegisterMap::current(ctx.pid_on_focus())?), &ctx, )? - .ok_or(anyhow!("no unwind context"))?; + .ok_or(UnwindNoContext)?; for _ in 0..frame_num { - let ret_addr = unwind_ctx - .return_address() - .ok_or(anyhow!("too deep frame number"))?; + let ret_addr = unwind_ctx.return_address().ok_or(UnwindTooDeepFrame)?; ctx = ExplorationContext::new( Location { @@ -348,8 +353,7 @@ impl<'a> DwarfUnwinder<'a> { ctx.focus_frame + 1, ); - unwind_ctx = - UnwindContext::next(unwind_ctx, &ctx)?.ok_or(anyhow!("no unwind context"))?; + unwind_ctx = UnwindContext::next(unwind_ctx, &ctx)?.ok_or(UnwindNoContext)?; } if let Ok(ip) = unwind_ctx.registers().value(gimli::Register(16)) { @@ -367,7 +371,7 @@ impl<'a> DwarfUnwinder<'a> { /// # Arguments /// /// * `pid`: pid of stopped thread. - pub fn return_address(&self, pid: Pid) -> anyhow::Result> { + pub fn return_address(&self, pid: Pid) -> Result, Error> { let frame_0_location = self .debugee .tracee_ctl() @@ -392,7 +396,7 @@ impl<'a> DwarfUnwinder<'a> { /// # Arguments /// /// * `location`: some debugee thread position. - pub fn context_for(&self, ctx: &ExplorationContext) -> anyhow::Result> { + pub fn context_for(&self, ctx: &ExplorationContext) -> Result, Error> { UnwindContext::new( self.debugee, DwarfRegisterMap::from(RegisterMap::current(ctx.pid_on_focus())?), @@ -405,6 +409,7 @@ impl<'a> DwarfUnwinder<'a> { mod libunwind { use super::FrameSpan; use crate::debugger::address::RelocatedAddress; + use crate::debugger::error::Error; use crate::debugger::register::DwarfRegisterMap; use crate::debugger::unwind::Backtrace; use nix::unistd::Pid; @@ -415,7 +420,7 @@ mod libunwind { /// # Arguments /// /// * `pid`: thread for unwinding. - pub(super) fn unwind(pid: Pid) -> unwind::Result { + pub(super) fn unwind(pid: Pid) -> Result { let state = PTraceState::new(pid.as_raw() as u32)?; let address_space = AddressSpace::new(Accessors::ptrace(), Byteorder::DEFAULT)?; let mut cursor = Cursor::remote(&address_space, &state)?; @@ -455,7 +460,7 @@ mod libunwind { /// # Arguments /// /// * `pid`: pid of stopped thread. - pub(super) fn return_addr(pid: Pid) -> unwind::Result> { + pub(super) fn return_addr(pid: Pid) -> Result, Error> { let state = PTraceState::new(pid.as_raw() as u32)?; let address_space = AddressSpace::new(Accessors::ptrace(), Byteorder::DEFAULT)?; let mut cursor = Cursor::remote(&address_space, &state)?; @@ -471,7 +476,7 @@ mod libunwind { pid: Pid, registers: &mut DwarfRegisterMap, frame_num: u32, - ) -> unwind::Result<()> { + ) -> Result<(), Error> { if frame_num == 0 { return Ok(()); } diff --git a/src/debugger/debugee/ldd.rs b/src/debugger/debugee/ldd.rs index a3791a8b..46a349db 100644 --- a/src/debugger/debugee/ldd.rs +++ b/src/debugger/debugee/ldd.rs @@ -1,3 +1,4 @@ +use crate::debugger::error::Error; use std::path::{Path, PathBuf}; /// Parse output of `ldd` for finding shared object dependencies. @@ -5,7 +6,7 @@ use std::path::{Path, PathBuf}; /// # Arguments /// /// * `file`: path to program -pub fn find_dependencies(file: &Path) -> anyhow::Result> { +pub fn find_dependencies(file: &Path) -> Result, Error> { let mut cmd = std::process::Command::new("ldd"); let output = cmd.arg(file.to_string_lossy().as_ref()).output()?; let output = std::str::from_utf8(&output.stdout)?; diff --git a/src/debugger/debugee/mod.rs b/src/debugger/debugee/mod.rs index dc8f3e43..dd0407ae 100644 --- a/src/debugger/debugee/mod.rs +++ b/src/debugger/debugee/mod.rs @@ -1,4 +1,14 @@ +pub mod dwarf; +mod ldd; +mod registry; +mod rendezvous; +pub mod tracee; +pub mod tracer; +pub use registry::RegionInfo; +pub use rendezvous::RendezvousError; + use crate::debugger::address::{GlobalAddress, RelocatedAddress}; +use crate::debugger::breakpoint::BrkptType; use crate::debugger::debugee::dwarf::unwind; use crate::debugger::debugee::dwarf::unwind::Backtrace; use crate::debugger::debugee::dwarf::DebugInformation; @@ -6,11 +16,12 @@ use crate::debugger::debugee::registry::DwarfRegistry; use crate::debugger::debugee::rendezvous::Rendezvous; use crate::debugger::debugee::tracee::{Tracee, TraceeCtl}; use crate::debugger::debugee::tracer::{StopReason, TraceContext, Tracer}; +use crate::debugger::error::Error; +use crate::debugger::error::Error::{FunctionNotFound, MappingOffsetNotFound, TraceeNotFound}; use crate::debugger::register::DwarfRegisterMap; use crate::debugger::unwind::FrameSpan; use crate::debugger::ExplorationContext; use crate::{muted_error, print_warns, weak_error}; -use anyhow::anyhow; use log::{info, warn}; use nix::unistd::Pid; use nix::NixPath; @@ -20,15 +31,6 @@ use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; -pub mod dwarf; -mod ldd; -mod registry; -mod rendezvous; -pub mod tracee; -pub mod tracer; -use crate::debugger::breakpoint::BrkptType; -pub use registry::RegionInfo; - /// Stack frame information. #[derive(Debug, Default, Clone)] pub struct FrameInfo { @@ -87,7 +89,7 @@ pub struct Debugee { } impl Debugee { - pub fn new_non_running(path: &Path, proc: Pid, object: &object::File) -> anyhow::Result { + pub fn new_non_running(path: &Path, proc: Pid, object: &object::File) -> Result { let dwarf_builder = dwarf::DebugInformationBuilder::default(); let dwarf = dwarf_builder.build(path, object)?; let mut registry = DwarfRegistry::new(proc, path.to_path_buf(), dwarf); @@ -169,7 +171,7 @@ impl Debugee { } } - pub fn trace_until_stop(&mut self, ctx: TraceContext) -> anyhow::Result { + pub fn trace_until_stop(&mut self, ctx: TraceContext) -> Result { let event = self.tracer.resume(ctx)?; match event { StopReason::DebugeeExit(_) => { @@ -214,7 +216,7 @@ impl Debugee { /// # Arguments /// /// * `quite`: true for enable logging of library names - fn update_debug_info_registry(&mut self, quite: bool) -> anyhow::Result<()> { + fn update_debug_info_registry(&mut self, quite: bool) -> Result<(), Error> { let lmaps = self.rendezvous().link_maps()?; let current_deps = lmaps .into_iter() @@ -242,11 +244,11 @@ impl Debugee { &self.tracer.tracee_ctl } - pub fn frame_info(&self, ctx: &ExplorationContext) -> anyhow::Result { + pub fn frame_info(&self, ctx: &ExplorationContext) -> Result { let dwarf = self.debug_info(ctx.location().pc)?; let func = dwarf .find_function_by_pc(ctx.location().global_pc)? - .ok_or_else(|| anyhow!("current function not found"))?; + .ok_or(FunctionNotFound(ctx.location().global_pc))?; let base_addr = func.frame_base_addr(ctx, self)?; let cfa = dwarf.get_cfa(self, ctx)?; @@ -266,7 +268,7 @@ impl Debugee { }) } - pub fn thread_state(&self, ctx: &ExplorationContext) -> anyhow::Result> { + pub fn thread_state(&self, ctx: &ExplorationContext) -> Result, Error> { let threads = self.tracee_ctl().snapshot(); Ok(threads .into_iter() @@ -317,34 +319,34 @@ impl Debugee { /// # Arguments /// /// * `num`: tracee number - pub fn get_tracee_by_num(&self, num: u32) -> anyhow::Result { + pub fn get_tracee_by_num(&self, num: u32) -> Result { let mut snapshot = self.tracee_ctl().snapshot(); let tracee = snapshot.drain(..).find(|tracee| tracee.number == num); - tracee.ok_or(anyhow!("tracee {num} not found")) + tracee.ok_or(TraceeNotFound(num)) } /// Return debug information about program determined by program counter address. #[inline(always)] - pub fn debug_info(&self, addr: RelocatedAddress) -> anyhow::Result<&DebugInformation> { + pub fn debug_info(&self, addr: RelocatedAddress) -> Result<&DebugInformation, Error> { self.dwarf_registry .find_by_addr(addr) - .ok_or(anyhow!("no debugee information for current location")) + .ok_or(Error::NoDebugInformation("current location")) } /// Return debug information about program determined by file which from it been parsed. #[inline(always)] - pub fn debug_info_from_file(&self, path: &Path) -> anyhow::Result<&DebugInformation> { + pub fn debug_info_from_file(&self, path: &Path) -> Result<&DebugInformation, Error> { self.dwarf_registry .find_by_file(path) - .ok_or(anyhow!("no debugee information for file")) + .ok_or(Error::NoDebugInformation("file")) } /// Get main executable object debug information. #[inline(always)] - pub fn program_debug_info(&self) -> anyhow::Result<&DebugInformation> { + pub fn program_debug_info(&self) -> Result<&DebugInformation, Error> { self.dwarf_registry .find_main_program_dwarf() - .ok_or(anyhow!("no debugee information for executable object")) + .ok_or(Error::NoDebugInformation("executable object")) } /// Return all known debug information. Debug info about main executable is located at the zero index. @@ -359,10 +361,10 @@ impl Debugee { /// # Arguments /// /// * `pc`: VAS address, determine region for which offset is needed. - pub fn mapping_offset_for_pc(&self, addr: RelocatedAddress) -> anyhow::Result { - self.dwarf_registry.find_mapping_offset(addr).ok_or(anyhow!( - "determine mapping offset fail, unknown current location" - )) + pub fn mapping_offset_for_pc(&self, addr: RelocatedAddress) -> Result { + self.dwarf_registry + .find_mapping_offset(addr) + .ok_or(MappingOffsetNotFound("address out of bounds")) } /// Return mapped memory region offset for region. @@ -370,10 +372,10 @@ impl Debugee { /// # Arguments /// /// * `dwarf`: debug information (with file path inside) for determine memory region. - pub fn mapping_offset_for_file(&self, dwarf: &DebugInformation) -> anyhow::Result { + pub fn mapping_offset_for_file(&self, dwarf: &DebugInformation) -> Result { self.dwarf_registry .find_mapping_offset_for_file(dwarf) - .ok_or(anyhow!("determine mapping offset fail: unknown segment")) + .ok_or(MappingOffsetNotFound("unknown segment")) } /// Unwind debugee thread stack and return a backtrace. @@ -381,7 +383,7 @@ impl Debugee { /// # Arguments /// /// * `pid`: thread for unwinding - pub fn unwind(&self, pid: Pid) -> anyhow::Result { + pub fn unwind(&self, pid: Pid) -> Result { unwind::unwind(self, pid) } @@ -398,7 +400,7 @@ impl Debugee { pid: Pid, registers: &mut DwarfRegisterMap, frame_num: u32, - ) -> anyhow::Result<()> { + ) -> Result<(), Error> { unwind::restore_registers_at_frame(self, pid, registers, frame_num) } @@ -408,7 +410,7 @@ impl Debugee { /// /// * `pid`: thread for unwinding #[allow(unused)] - pub fn return_addr(&self, pid: Pid) -> anyhow::Result> { + pub fn return_addr(&self, pid: Pid) -> Result, Error> { unwind::return_addr(self, pid) } @@ -419,7 +421,7 @@ impl Debugee { } /// Parse dwarf information from new dependency. -fn parse_dependency(dep_file: impl Into) -> anyhow::Result> { +fn parse_dependency(dep_file: impl Into) -> Result, Error> { let dep_file = dep_file.into(); // empty string represent a program executable that must already parsed diff --git a/src/debugger/debugee/registry.rs b/src/debugger/debugee/registry.rs index 667e7842..c84c83ae 100644 --- a/src/debugger/debugee/registry.rs +++ b/src/debugger/debugee/registry.rs @@ -1,6 +1,7 @@ use crate::debugger::address::RelocatedAddress; use crate::debugger::debugee::dwarf::{DebugInformation, EndianArcSlice}; -use anyhow::{anyhow, Error}; +use crate::debugger::error::Error; +use crate::debugger::error::Error::MappingNotFound; use nix::unistd::Pid; use proc_maps::MapRange; use std::cmp::Ordering; @@ -68,7 +69,7 @@ impl DwarfRegistry { /// # Arguments /// /// * `only_main`: if true - update mappings only for main executable file, false - update all - pub fn update_mappings(&mut self, only_main: bool) -> anyhow::Result> { + pub fn update_mappings(&mut self, only_main: bool) -> Result, Error> { let proc_maps: Vec = proc_maps::get_process_maps(self.pid.as_raw())?; let mut mappings = HashMap::with_capacity(self.files.len()); @@ -97,7 +98,7 @@ impl DwarfRegistry { .collect::>(); if maps.is_empty() { - errors.push(anyhow!("mapping not found for {file:?}")); + errors.push(MappingNotFound(file.to_string_lossy().to_string())); return; } @@ -145,7 +146,7 @@ impl DwarfRegistry { &mut self, file: impl Into, dwarf: DebugInformation, - ) -> anyhow::Result<()> { + ) -> Result<(), Error> { let path = file.into(); // validate path path.canonicalize()?; diff --git a/src/debugger/debugee/rendezvous.rs b/src/debugger/debugee/rendezvous.rs index 5c0d65d5..7412e93b 100644 --- a/src/debugger/debugee/rendezvous.rs +++ b/src/debugger/debugee/rendezvous.rs @@ -17,7 +17,7 @@ pub enum RendezvousError { #[error(".dynamic section not found")] DynamicSectNotFound, #[error("read from remote process: {0}")] - RemoteReadError(#[from] nix::Error), + PtraceRead(#[from] nix::Error), #[error("rendezvous not found")] NotFound, } diff --git a/src/debugger/debugee/tracee.rs b/src/debugger/debugee/tracee.rs index e91966a7..9c9134cb 100644 --- a/src/debugger/debugee/tracee.rs +++ b/src/debugger/debugee/tracee.rs @@ -2,9 +2,9 @@ use crate::debugger::address::RelocatedAddress; use crate::debugger::debugee::tracee::StopType::Interrupt; use crate::debugger::debugee::tracee::TraceeStatus::{Running, Stopped}; use crate::debugger::debugee::{Debugee, Location}; +use crate::debugger::error::Error; +use crate::debugger::error::Error::{MultipleErrors, NoThreadDB, Ptrace, ThreadDB, Waitpid}; use crate::debugger::register::{Register, RegisterMap}; -use anyhow::{anyhow, bail}; -use itertools::Itertools; use log::{debug, warn}; use nix::errno::Errno; use nix::sys; @@ -58,16 +58,16 @@ impl Tracee { } /// Wait for change of tracee status. - pub fn wait_one(&self) -> nix::Result { + pub fn wait_one(&self) -> Result { debug!(target: "tracer", "wait for tracee status, thread {pid}", pid = self.pid); - let status = waitpid(self.pid, None)?; + let status = waitpid(self.pid, None).map_err(Waitpid)?; debug!(target: "tracer", "receive tracee status, thread {pid}, status: {status:?}", pid = self.pid); Ok(status) } /// Move the stopped tracee process forward by a single instruction step. - pub fn step(&self, sig: Option) -> nix::Result<()> { - sys::ptrace::step(self.pid, sig) + pub fn step(&self, sig: Option) -> Result<(), Error> { + sys::ptrace::step(self.pid, sig).map_err(Ptrace) } fn update_status(&mut self, status: TraceeStatus) { @@ -80,17 +80,19 @@ impl Tracee { } /// Resume tracee with, if signal is some - inject signal or resuming. - pub fn r#continue(&mut self, sig: Option) -> nix::Result<()> { + pub fn r#continue(&mut self, sig: Option) -> Result<(), Error> { debug!( target: "tracer", "continue tracee execution with signal {sig:?}, thread: {pid}", pid = self.pid, ); - sys::ptrace::cont(self.pid, sig).map(|ok| { - self.update_status(Running); - ok - }) + sys::ptrace::cont(self.pid, sig) + .map(|ok| { + self.update_status(Running); + ok + }) + .map_err(Ptrace) } /// Set tracee status into stop. @@ -106,20 +108,20 @@ impl Tracee { } /// Get current program counter value. - pub fn pc(&self) -> nix::Result { + pub fn pc(&self) -> Result { RegisterMap::current(self.pid) .map(|reg_map| RelocatedAddress::from(reg_map.value(Register::Rip))) } /// Set new program counter value. - pub fn set_pc(&self, value: u64) -> nix::Result<()> { + pub fn set_pc(&self, value: u64) -> Result<(), Error> { let mut map = RegisterMap::current(self.pid)?; map.update(Register::Rip, value); map.persist(self.pid) } /// Get current tracee location. - pub fn location(&self, debugee: &Debugee) -> anyhow::Result { + pub fn location(&self, debugee: &Debugee) -> Result { let pc = self.pc()?; Ok(Location { pid: self.pid, @@ -180,7 +182,7 @@ impl TraceeCtl { } /// Continue all currently stopped tracees. - pub fn cont_stopped(&mut self) -> Result<(), anyhow::Error> { + pub fn cont_stopped(&mut self) -> Result<(), Vec> { let mut errors = vec![]; self.threads_state.iter_mut().for_each(|(_, tracee)| { @@ -190,17 +192,17 @@ impl TraceeCtl { if let Err(e) = tracee.r#continue(None) { // if no such process - continue, it will be removed later, on PTRACE_EVENT_EXIT event. - if Errno::ESRCH == e { + if matches!(e, Ptrace(err) if err == Errno::ESRCH) { //warn!("thread {} not found, ESRCH", tracee.pid); return; } - errors.push(anyhow::Error::from(e).context(format!("thread: {}", tracee.pid))); + errors.push(e); } }); if !errors.is_empty() { - bail!(errors.into_iter().join(";")) + return Err(errors); } Ok(()) } @@ -215,7 +217,7 @@ impl TraceeCtl { &mut self, inject_request: Option<(Pid, Signal)>, exclude: HashSet, - ) -> Result<(), anyhow::Error> { + ) -> Result<(), Error> { let mut errors = vec![]; let (signal, pid) = (inject_request.map(|s| s.1), inject_request.map(|s| s.0)); @@ -236,17 +238,17 @@ impl TraceeCtl { if let Err(e) = tracee.r#continue(resume_sign) { // if no such process - continue, it will be removed later, on PTRACE_EVENT_EXIT event. - if Errno::ESRCH == e { + if matches!(e, Ptrace(err) if err == Errno::ESRCH) { warn!("thread {} not found, ESRCH", tracee.pid); return; } - errors.push(anyhow::Error::from(e).context(format!("thread: {}", tracee.pid))); + errors.push(e); } }); if !errors.is_empty() { - bail!(errors.into_iter().join(";")) + return Err(MultipleErrors(errors)); } Ok(()) } @@ -257,7 +259,7 @@ impl TraceeCtl { /// Load libthread_db and init libthread_db process handle. /// libthread_db must initialized after first thread created. - pub(super) fn init_thread_db(&mut self) -> anyhow::Result<()> { + pub(super) fn init_thread_db(&mut self) -> Result<(), Error> { let thread_db_lib = thread_db::Lib::try_load()?; let td_process = ThreadDBProcessTryBuilder { lib: thread_db_lib, @@ -274,13 +276,11 @@ impl TraceeCtl { tid: Pid, link_map_addr: RelocatedAddress, offset: usize, - ) -> anyhow::Result { - let td_proc = self - .thread_db_proc - .as_ref() - .ok_or_else(|| anyhow!("libthread_db not enabled"))?; + ) -> Result { + let td_proc = self.thread_db_proc.as_ref().ok_or(NoThreadDB)?; - let thread: thread_db::Thread = td_proc.borrow_process().get_thread(tid)?; + let thread: thread_db::Thread = + td_proc.borrow_process().get_thread(tid).map_err(ThreadDB)?; Ok(RelocatedAddress::from( thread.tls_addr(link_map_addr.into(), offset)? as usize, diff --git a/src/debugger/debugee/tracer.rs b/src/debugger/debugee/tracer.rs index 39b07274..bb57b718 100644 --- a/src/debugger/debugee/tracer.rs +++ b/src/debugger/debugee/tracer.rs @@ -2,7 +2,8 @@ use crate::debugger::address::RelocatedAddress; use crate::debugger::breakpoint::Breakpoint; use crate::debugger::code; use crate::debugger::debugee::tracee::{StopType, TraceeCtl, TraceeStatus}; -use anyhow::bail; +use crate::debugger::error::Error; +use crate::debugger::error::Error::{MultipleErrors, ProcessExit, Ptrace, Waitpid}; use log::{debug, warn}; use nix::errno::Errno; use nix::libc::pid_t; @@ -67,7 +68,7 @@ impl Tracer { } /// Continue debugee execution until stop happened. - pub fn resume(&mut self, ctx: TraceContext) -> anyhow::Result { + pub fn resume(&mut self, ctx: TraceContext) -> Result { loop { if let Some(req) = self.signal_queue.pop_front() { self.tracee_ctl.cont_stopped_ex( @@ -81,7 +82,7 @@ impl Tracer { return Ok(StopReason::SignalStop(pid, sign)); } } else { - self.tracee_ctl.cont_stopped()?; + self.tracee_ctl.cont_stopped().map_err(MultipleErrors)?; } debug!(target: "tracer", "resume debugee execution, wait for updates"); @@ -90,7 +91,7 @@ impl Tracer { Err(Errno::ECHILD) => { return Ok(StopReason::NoSuchProcess(self.tracee_ctl.proc_pid())) } - Err(e) => return Err(e.into()), + Err(e) => return Err(Waitpid(e)), }; debug!(target: "tracer", "received new thread status: {status:?}"); @@ -131,11 +132,7 @@ impl Tracer { /// # Arguments /// /// * `initiator_pid`: tracee with this thread id already stopped, there is no need to interrupt it. - fn group_stop_interrupt( - &mut self, - ctx: TraceContext, - initiator_pid: Pid, - ) -> anyhow::Result<()> { + fn group_stop_interrupt(&mut self, ctx: TraceContext, initiator_pid: Pid) -> Result<(), Error> { if self.group_stop_in_progress() { return Ok(()); } @@ -189,7 +186,7 @@ impl Tracer { } continue; } - bail!(anyhow::Error::from(e).context(format!("thread: {}", tracee.pid))); + return Err(Ptrace(e)); } let mut wait = tracee.wait_one()?; @@ -204,9 +201,7 @@ impl Tracer { break; } } - Some(StopReason::DebugeeExit(code)) => { - bail!("debugee process exit with {code}") - } + Some(StopReason::DebugeeExit(code)) => return Err(ProcessExit(code)), Some(StopReason::DebugeeStart) => { unreachable!("stop at debugee entry point twice") } @@ -264,7 +259,7 @@ impl Tracer { &mut self, ctx: TraceContext, status: WaitStatus, - ) -> anyhow::Result> { + ) -> Result, Error> { match status { WaitStatus::Exited(pid, code) => { // Thread exited with tread id @@ -287,7 +282,8 @@ impl Tracer { self.tracee_ctl .tracee_ensure_mut(pid) .set_stop(StopType::Interrupt); - let new_thread_id = Pid::from_raw(sys::ptrace::getevent(pid)? as pid_t); + let new_thread_id = + Pid::from_raw(sys::ptrace::getevent(pid).map_err(Ptrace)? as pid_t); // PTRACE_EVENT_STOP may be received first, and new tracee may be already registered at this point if self.tracee_ctl.tracee_mut(new_thread_id).is_none() { @@ -330,7 +326,7 @@ impl Tracer { let info = match sys::ptrace::getsiginfo(pid) { Ok(info) => info, Err(Errno::ESRCH) => return Ok(Some(StopReason::NoSuchProcess(pid))), - Err(e) => return Err(e.into()), + Err(e) => return Err(Ptrace(e)), }; match signal { @@ -421,7 +417,7 @@ impl Tracer { &mut self, ctx: TraceContext, pid: Pid, - ) -> anyhow::Result> { + ) -> Result, Error> { let tracee = self.tracee_ctl.tracee_ensure(pid); let initial_pc = tracee.pc()?; tracee.step(None)?; @@ -429,7 +425,7 @@ impl Tracer { let reason = loop { let tracee = self.tracee_ctl.tracee_ensure_mut(pid); let status = tracee.wait_one()?; - let info = sys::ptrace::getsiginfo(pid)?; + let info = sys::ptrace::getsiginfo(pid).map_err(Ptrace)?; // check that debugee step into expected trap (breakpoints ignored and are also considered as a trap) let in_trap = matches!(status, WaitStatus::Stopped(_, Signal::SIGTRAP)) @@ -450,7 +446,7 @@ impl Tracer { matches!(status, WaitStatus::Stopped(_, Signal::SIGTRAP)) && (info.si_code == 5); if in_trap { // if in syscall step to syscall end - sys::ptrace::syscall(tracee.pid, None)?; + sys::ptrace::syscall(tracee.pid, None).map_err(Ptrace)?; let syscall_status = tracee.wait_one()?; debug_assert!(matches!( syscall_status, @@ -477,9 +473,7 @@ impl Tracer { Some(StopReason::Breakpoint(_, _)) => { unreachable!("breakpoints must be ignore"); } - Some(StopReason::DebugeeExit(code)) => { - bail!("debugee process exit with {code}") - } + Some(StopReason::DebugeeExit(code)) => return Err(ProcessExit(code)), Some(StopReason::DebugeeStart) => { unreachable!("stop at debugee entry point twice") } diff --git a/src/debugger/error.rs b/src/debugger/error.rs new file mode 100644 index 00000000..db558a39 --- /dev/null +++ b/src/debugger/error.rs @@ -0,0 +1,229 @@ +use crate::debugger::address::GlobalAddress; +use crate::debugger::debugee::dwarf::unit::DieRef; +use crate::debugger::debugee::RendezvousError; +use crate::debugger::variable::ParsingError; +use std::str::Utf8Error; +use std::string::FromUtf8Error; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + // --------------------------------- generic errors -------------------------------------------- + #[error(transparent)] + IO(#[from] std::io::Error), + #[error(transparent)] + Utf8(#[from] Utf8Error), + #[error(transparent)] + FromUtf8(#[from] FromUtf8Error), + #[error(transparent)] + RegEx(#[from] regex::Error), + + // --------------------------------- debugger entity not found---------------------------------- + #[error("no debug information for {0}")] + NoDebugInformation(&'static str), + #[error("unknown register {0:?}")] + RegisterNotFound(gimli::Register), + #[error("unknown register {0:?}")] + RegisterNameNotFound(String), + #[error("source place not found at address {0}")] + PlaceNotFound(GlobalAddress), + #[error("there are no suitable places for this request")] + NoSuitablePlace, + #[error("unit not found at address {0}")] + UnitNotFound(GlobalAddress), + #[error("function not found at address {0}")] + FunctionNotFound(GlobalAddress), + #[error("frame number {0} not found")] + FrameNotFound(u32), + #[error("tracee number {0} not found")] + TraceeNotFound(u32), + #[error("debug information entry (die) not found, reference: {0:?}")] + DieNotFound(DieRef), + + // --------------------------------- remote memory errors -------------------------------------- + #[error("invalid binary representation of type `{0}`: {1:?}")] + TypeBinaryRepr(&'static str, Box<[u8]>), + #[error("unknown address")] + UnknownAddress, + #[error("memory region offset not found ({0})")] + MappingOffsetNotFound(&'static str), + #[error("memory region not found for a file: {0}")] + MappingNotFound(String), + + // --------------------------------- syscall errors -------------------------------------------- + #[error("waitpid syscall error: {0}")] + Waitpid(nix::Error), + #[error("ptrace syscall error: {0}")] + Ptrace(nix::Error), + #[error("{0} syscall error: {1}")] + Syscall(&'static str, nix::Error), + #[error("multiple syscall errors {0:?}")] + MultipleErrors(Vec), + + // --------------------------------- parsing errors -------------------------------------------- + #[error("dwarf file parsing error: {0}")] + DwarfParsing(#[from] gimli::Error), + #[error("invalid debug-id note format")] + DebugIDFormat, + #[error("object file parsing error: {0}")] + ObjParsing(#[from] object::Error), + #[error(transparent)] + VariableParsing(#[from] ParsingError), + + // --------------------------------- unwind errors --------------------------------------------- + #[error("unwind: no unwind context")] + UnwindNoContext, + #[error("unwind: too deep frame number")] + UnwindTooDeepFrame, + #[error("libunwind error: {0}")] + LibUnwind(#[from] unwind::Error), + + // --------------------------------- dwarf errors ---------------------------------------------- + #[error("dwarf expression evaluation: eval option `{0}` required")] + EvalOptionRequired(&'static str), + #[error("dwarf expression evaluation: unsupported evaluation require ({0})")] + EvalUnsupportedRequire(&'static str), + #[error("no frame base address")] + NoFBA, + #[error("frame base address attribute not an expression")] + FBANotAnExpression, + #[error("range information for function `{0:?}` not exists")] + NoFunctionRanges(Option), + #[error("die type not exists")] + NoDieType, + #[error("fail to read/evaluate implicit pointer address")] + ImplicitPointer, + + // --------------------------------- libthread_db errors --------------------------------------- + #[error("libthread_db not enabled")] + NoThreadDB, + #[error("libthread_db: {0}")] + ThreadDB(#[from] thread_db::ThreadDbError), + + // --------------------------------- linker errors --------------------------------------------- + #[error(transparent)] + Rendezvous(#[from] RendezvousError), + + // --------------------------------- debugee process errors ------------------------------------ + #[error("debugee process exit with code {0}")] + ProcessExit(i32), + #[error("program is not being started")] + ProcessNotStarted, + + // --------------------------------- rust toolchain errors ------------------------------------- + #[error("default toolchain not found")] + DefaultToolchainNotFound, + #[error("unrecognized rustup output")] + UnrecognizedRustupOut, + + // --------------------------------- third party errors ---------------------------------------- + #[error("hook: {0}")] + Hook(anyhow::Error), +} + +impl Error { + /// Return a hint to an interface - continue debugging after error or stop whole process. + pub fn is_fatal(&self) -> bool { + match self { + Error::IO(_) => false, + Error::Utf8(_) => false, + Error::FromUtf8(_) => false, + Error::RegEx(_) => false, + Error::NoDebugInformation(_) => false, + Error::RegisterNotFound(_) => false, + Error::RegisterNameNotFound(_) => false, + Error::PlaceNotFound(_) => false, + Error::NoSuitablePlace => false, + Error::UnitNotFound(_) => false, + Error::FunctionNotFound(_) => false, + Error::FrameNotFound(_) => false, + Error::TraceeNotFound(_) => false, + Error::DieNotFound(_) => false, + Error::TypeBinaryRepr(_, _) => false, + Error::UnknownAddress => false, + Error::MappingOffsetNotFound(_) => false, + Error::MappingNotFound(_) => false, + Error::Waitpid(_) => false, + Error::Ptrace(_) => false, + Error::MultipleErrors(_) => false, + Error::DebugIDFormat => false, + Error::VariableParsing(_) => false, + Error::UnwindNoContext => false, + Error::UnwindTooDeepFrame => false, + Error::LibUnwind(_) => false, + Error::EvalOptionRequired(_) => false, + Error::EvalUnsupportedRequire(_) => false, + Error::NoFBA => false, + Error::FBANotAnExpression => false, + Error::NoFunctionRanges(_) => false, + Error::NoDieType => false, + Error::ImplicitPointer => false, + Error::ThreadDB(_) => false, + Error::Rendezvous(_) => false, + Error::ProcessExit(_) => false, + Error::ProcessNotStarted => false, + Error::DefaultToolchainNotFound => false, + Error::UnrecognizedRustupOut => false, + Error::Hook(_) => false, + + // currently fatal errors + Error::DwarfParsing(_) => true, + Error::ObjParsing(_) => true, + Error::Syscall(_, _) => true, + Error::NoThreadDB => true, + } + } +} + +#[macro_export] +macro_rules! _error { + ($log_fn: path, $res: expr) => { + match $res { + Ok(value) => Some(value), + Err(e) => { + $log_fn!(target: "debugger", "{:#}", e); + None + } + } + }; + ($log_fn: path, $res: expr, $msg: tt) => { + match $res { + Ok(value) => Some(value), + Err(e) => { + $log_fn!(target: "debugger", concat!($msg, " {:#}"), e); + None + } + } + }; +} + +/// Transforms `Result` into `Option` and logs an error if it occurs. +#[macro_export] +macro_rules! weak_error { + ($res: expr) => { + $crate::_error!(log::warn, $res) + }; + ($res: expr, $msg: tt) => { + $crate::_error!(log::warn, $res, $msg) + }; +} + +/// Transforms `Result` into `Option` and put error into debug logs if it occurs. +#[macro_export] +macro_rules! muted_error { + ($res: expr) => { + $crate::_error!(log::debug, $res) + }; + ($res: expr, $msg: tt) => { + $crate::_error!(log::debug, $res, $msg) + }; +} + +/// Macro for handle an error lists as warnings. +#[macro_export] +macro_rules! print_warns { + ($errors:expr) => { + $errors.iter().for_each(|e| { + log::warn!(target: "debugger", "{:#}", e); + }) + }; +} diff --git a/src/debugger/mod.rs b/src/debugger/mod.rs index ab6681ac..e4129057 100644 --- a/src/debugger/mod.rs +++ b/src/debugger/mod.rs @@ -3,6 +3,7 @@ mod breakpoint; mod code; pub mod command; mod debugee; +mod error; pub mod process; pub mod register; pub mod rust; @@ -16,6 +17,7 @@ pub use debugee::dwarf::unit::FunctionDie; pub use debugee::dwarf::unit::PlaceDescriptor; pub use debugee::dwarf::unwind; pub use debugee::ThreadSnapshot; +pub use error::Error; use crate::debugger::address::{Address, GlobalAddress, RelocatedAddress}; use crate::debugger::breakpoint::{Breakpoint, BreakpointRegistry, BrkptType, UninitBreakpoint}; @@ -25,13 +27,16 @@ use crate::debugger::debugee::dwarf::{DwarfUnwinder, Symbol}; use crate::debugger::debugee::tracee::Tracee; use crate::debugger::debugee::tracer::{StopReason, TraceContext}; use crate::debugger::debugee::{Debugee, ExecutionStatus, FrameInfo, Location, RegionInfo}; +use crate::debugger::error::Error::{ + FrameNotFound, Hook, ProcessNotStarted, Ptrace, RegisterNameNotFound, UnwindNoContext, +}; use crate::debugger::process::{Child, Installed}; use crate::debugger::register::{DwarfRegisterMap, Register, RegisterMap}; use crate::debugger::step::StepResult; use crate::debugger::variable::select::{Expression, VariableSelector}; use crate::debugger::variable::VariableIR; +use crate::debugger::Error::Syscall; use crate::{print_warns, weak_error}; -use anyhow::{anyhow, bail}; use nix::libc::{c_void, uintptr_t}; use nix::sys; use nix::sys::signal; @@ -102,9 +107,8 @@ pub trait EventHook { macro_rules! disable_when_not_stared { ($this: expr) => { - use anyhow::bail; if !$this.debugee.is_in_progress() { - bail!("The program is not being started.") + return Err(ProcessNotStarted); } }; } @@ -212,7 +216,7 @@ impl Debugger { } /// Update current program counters for current in focus thread. - fn expl_ctx_update_location(&mut self) -> anyhow::Result<&ExplorationContext> { + fn expl_ctx_update_location(&mut self) -> Result<&ExplorationContext, Error> { let old_ctx = self.exploration_ctx(); self.expl_context = ExplorationContext::new( self.debugee @@ -224,7 +228,7 @@ impl Debugger { } /// Restore frame from user defined to real. - fn expl_ctx_restore_frame(&mut self) -> anyhow::Result<&ExplorationContext> { + fn expl_ctx_restore_frame(&mut self) -> Result<&ExplorationContext, Error> { self.expl_ctx_update_location() } @@ -233,7 +237,7 @@ impl Debugger { /// # Arguments /// /// * `pid`: new in focus thread id - fn expl_ctx_switch_thread(&mut self, pid: Pid) -> anyhow::Result<&ExplorationContext> { + fn expl_ctx_switch_thread(&mut self, pid: Pid) -> Result<&ExplorationContext, Error> { self.expl_context = ExplorationContext::new( self.debugee .get_tracee_ensure(pid) @@ -247,7 +251,7 @@ impl Debugger { /// Return if breakpoint is reached or signal occurred or debugee exit. /// /// **! change exploration context** - fn continue_execution(&mut self) -> anyhow::Result { + fn continue_execution(&mut self) -> Result { if let Some(StopReason::SignalStop(pid, sign)) = self.step_over_breakpoint()? { self.hooks.on_signal(sign); return Ok(StopReason::SignalStop(pid, sign)); @@ -269,7 +273,7 @@ impl Debugger { // no need to update expl context cause next stop been soon, on entry point } StopReason::NoSuchProcess(_) => { - bail!("The program is not being started."); + return Err(ProcessNotStarted); } StopReason::Breakpoint(pid, current_pc) => { self.expl_ctx_switch_thread(pid)?; @@ -279,7 +283,7 @@ impl Debugger { BrkptType::EntryPoint => { print_warns!(self .breakpoints - .enable_all_breakpoints(&self.debugee)?); + .enable_all_breakpoints(&self.debugee)); // rendezvous already available at this point let brk = self.debugee.rendezvous().r_brk(); @@ -308,7 +312,8 @@ impl Debugger { .flatten() .map(|f| f.die); self.hooks - .on_breakpoint(current_pc, bp.number(), place, func)?; + .on_breakpoint(current_pc, bp.number(), place, func) + .map_err(Hook)?; break event; } BrkptType::Temporary => { @@ -336,7 +341,7 @@ impl Debugger { /// Return when new debugee stopped or ends. /// /// **! change exploration context** - pub fn restart_debugee(&mut self) -> anyhow::Result<()> { + pub fn restart_debugee(&mut self) -> Result { match self.debugee.execution_status() { ExecutionStatus::Unload => { // all breakpoints already disabled by default @@ -350,8 +355,8 @@ impl Debugger { } if !self.debugee.is_exited() { - let proc_pid = self.debugee.tracee_ctl().proc_pid(); - signal::kill(proc_pid, SIGKILL)?; + let proc_pid = self.process.pid(); + signal::kill(proc_pid, SIGKILL).map_err(|e| Syscall("kill", e))?; _ = self.debugee.tracer_mut().resume(TraceContext::new(&vec![])); } @@ -366,12 +371,12 @@ impl Debugger { self.hooks.on_process_install(self.process.pid()); self.expl_context = ExplorationContext::new_non_running(self.process.pid()); self.continue_execution()?; - Ok(()) + Ok(self.process.pid()) } /// Start debugee. /// Return when debugee stopped or ends. - pub fn start_debugee(&mut self) -> anyhow::Result<()> { + pub fn start_debugee(&mut self) -> Result<(), Error> { if !self.debugee.is_in_progress() { self.continue_execution()?; } @@ -379,7 +384,7 @@ impl Debugger { } /// Continue debugee execution. - pub fn continue_debugee(&mut self) -> anyhow::Result<()> { + pub fn continue_debugee(&mut self) -> Result<(), Error> { disable_when_not_stared!(self); self.continue_execution()?; Ok(()) @@ -390,7 +395,7 @@ impl Debugger { /// # Arguments /// /// * `regex`: regular expression - pub fn get_symbols(&self, regex: &str) -> anyhow::Result> { + pub fn get_symbols(&self, regex: &str) -> Result, Error> { let regex = Regex::new(regex)?; Ok(self @@ -402,7 +407,7 @@ impl Debugger { } /// Return in focus frame information. - pub fn frame_info(&self) -> anyhow::Result { + pub fn frame_info(&self) -> Result { disable_when_not_stared!(self); self.debugee.frame_info(self.exploration_ctx()) } @@ -412,13 +417,11 @@ impl Debugger { /// # Arguments /// /// * `num`: frame number in backtrace - pub fn set_frame_into_focus(&mut self, num: u32) -> anyhow::Result { + pub fn set_frame_into_focus(&mut self, num: u32) -> Result { disable_when_not_stared!(self); let ctx = self.exploration_ctx(); let backtrace = self.debugee.unwind(ctx.pid_on_focus())?; - let frame = backtrace - .get(num as usize) - .ok_or(anyhow!("frame {num} not found"))?; + let frame = backtrace.get(num as usize).ok_or(FrameNotFound(num))?; self.expl_context = ExplorationContext::new( Location { pc: frame.ip, @@ -431,7 +434,7 @@ impl Debugger { } /// Execute `on_step` callback with current exploration context - fn execute_on_step_hook(&self) -> anyhow::Result<()> { + fn execute_on_step_hook(&self) -> Result<(), Error> { let ctx = self.exploration_ctx(); let pc = ctx.location().pc; let global_pc = ctx.location().global_pc; @@ -440,13 +443,13 @@ impl Debugger { let func = weak_error!(dwarf.find_function_by_pc(global_pc)) .flatten() .map(|f| f.die); - self.hooks.on_step(pc, place, func) + self.hooks.on_step(pc, place, func).map_err(Hook) } /// Do single step (until debugee reaches a different source line). /// /// **! change exploration context** - pub fn step_into(&mut self) -> anyhow::Result<()> { + pub fn step_into(&mut self) -> Result<(), Error> { disable_when_not_stared!(self); self.expl_ctx_restore_frame()?; @@ -464,7 +467,7 @@ impl Debugger { /// Move in focus thread to next instruction. /// /// **! change exploration context** - pub fn stepi(&mut self) -> anyhow::Result<()> { + pub fn stepi(&mut self) -> Result<(), Error> { disable_when_not_stared!(self); self.expl_ctx_restore_frame()?; @@ -477,7 +480,7 @@ impl Debugger { } /// Return list of currently running debugee threads. - pub fn thread_state(&self) -> anyhow::Result> { + pub fn thread_state(&self) -> Result, Error> { disable_when_not_stared!(self); self.debugee.thread_state(self.exploration_ctx()) } @@ -487,7 +490,7 @@ impl Debugger { /// # Arguments /// /// * `num`: thread number - pub fn set_thread_into_focus(&mut self, num: u32) -> anyhow::Result { + pub fn set_thread_into_focus(&mut self, num: u32) -> Result { disable_when_not_stared!(self); let tracee = self.debugee.get_tracee_by_num(num)?; self.expl_ctx_switch_thread(tracee.pid)?; @@ -499,7 +502,7 @@ impl Debugger { /// # Arguments /// /// * `pid`: thread id - pub fn backtrace(&self, pid: Pid) -> anyhow::Result { + pub fn backtrace(&self, pid: Pid) -> Result { disable_when_not_stared!(self); self.debugee.unwind(pid) } @@ -510,13 +513,9 @@ impl Debugger { /// /// * `addr`: address in debugee address space where reads /// * `read_n`: read byte count - pub fn read_memory(&self, addr: usize, read_n: usize) -> anyhow::Result> { + pub fn read_memory(&self, addr: usize, read_n: usize) -> Result, Error> { disable_when_not_stared!(self); - Ok(read_memory_by_pid( - self.debugee.tracee_ctl().proc_pid(), - addr, - read_n, - )?) + read_memory_by_pid(self.debugee.tracee_ctl().proc_pid(), addr, read_n).map_err(Ptrace) } /// Write sizeof(uintptr_t) bytes in debugee address space @@ -525,19 +524,20 @@ impl Debugger { /// /// * `addr`: address to write /// * `value`: value to write - pub fn write_memory(&self, addr: uintptr_t, value: uintptr_t) -> anyhow::Result<()> { + pub fn write_memory(&self, addr: uintptr_t, value: uintptr_t) -> Result<(), Error> { disable_when_not_stared!(self); unsafe { - Ok(sys::ptrace::write( + sys::ptrace::write( self.debugee.tracee_ctl().proc_pid(), addr as *mut c_void, value as *mut c_void, - )?) + ) + .map_err(Ptrace) } } /// Move to higher stack frame. - pub fn step_out(&mut self) -> anyhow::Result<()> { + pub fn step_out(&mut self) -> Result<(), Error> { disable_when_not_stared!(self); self.expl_ctx_restore_frame()?; self.step_out_frame()?; @@ -545,7 +545,7 @@ impl Debugger { } /// Do debugee step (over subroutine calls to). - pub fn step_over(&mut self) -> anyhow::Result<()> { + pub fn step_over(&mut self) -> Result<(), Error> { disable_when_not_stared!(self); self.expl_ctx_restore_frame()?; match self.step_over_any()? { @@ -560,13 +560,13 @@ impl Debugger { } /// Reads all local variables from current function in current thread. - pub fn read_local_variables(&self) -> anyhow::Result> { + pub fn read_local_variables(&self) -> Result, Error> { disable_when_not_stared!(self); let evaluator = variable::select::SelectExpressionEvaluator::new( self, Expression::Variable(VariableSelector::Any), - )?; + ); evaluator.evaluate() } @@ -576,9 +576,9 @@ impl Debugger { /// # Arguments /// /// * `select_expr`: data query expression - pub fn read_variable(&self, select_expr: Expression) -> anyhow::Result> { + pub fn read_variable(&self, select_expr: Expression) -> Result, Error> { disable_when_not_stared!(self); - let evaluator = variable::select::SelectExpressionEvaluator::new(self, select_expr)?; + let evaluator = variable::select::SelectExpressionEvaluator::new(self, select_expr); evaluator.evaluate() } @@ -588,9 +588,9 @@ impl Debugger { /// # Arguments /// /// * `select_expr`: data query expression - pub fn read_variable_names(&self, select_expr: Expression) -> anyhow::Result> { + pub fn read_variable_names(&self, select_expr: Expression) -> Result, Error> { disable_when_not_stared!(self); - let evaluator = variable::select::SelectExpressionEvaluator::new(self, select_expr)?; + let evaluator = variable::select::SelectExpressionEvaluator::new(self, select_expr); evaluator.evaluate_names() } @@ -600,9 +600,9 @@ impl Debugger { /// # Arguments /// /// * `select_expr`: data query expression - pub fn read_argument(&self, select_expr: Expression) -> anyhow::Result> { + pub fn read_argument(&self, select_expr: Expression) -> Result, Error> { disable_when_not_stared!(self); - let evaluator = variable::select::SelectExpressionEvaluator::new(self, select_expr)?; + let evaluator = variable::select::SelectExpressionEvaluator::new(self, select_expr); evaluator.evaluate_on_arguments() } @@ -612,9 +612,9 @@ impl Debugger { /// # Arguments /// /// * `select_expr`: data query expression - pub fn read_argument_names(&self, select_expr: Expression) -> anyhow::Result> { + pub fn read_argument_names(&self, select_expr: Expression) -> Result, Error> { disable_when_not_stared!(self); - let evaluator = variable::select::SelectExpressionEvaluator::new(self, select_expr)?; + let evaluator = variable::select::SelectExpressionEvaluator::new(self, select_expr); evaluator.evaluate_on_arguments_names() } @@ -623,11 +623,12 @@ impl Debugger { /// # Arguments /// /// * `register_name`: x86-64 register name (ex: `rip`) - pub fn get_register_value(&self, register_name: &str) -> anyhow::Result { + pub fn get_register_value(&self, register_name: &str) -> Result { disable_when_not_stared!(self); - Ok(RegisterMap::current(self.exploration_ctx().pid_on_focus())? - .value(Register::from_str(register_name)?)) + let r = Register::from_str(register_name) + .map_err(|_| RegisterNameNotFound(register_name.into()))?; + Ok(RegisterMap::current(self.exploration_ctx().pid_on_focus())?.value(r)) } /// Return registers dump for on focus thread at instruction defined by pc. @@ -638,7 +639,7 @@ impl Debugger { pub fn current_thread_registers_at_pc( &self, pc: RelocatedAddress, - ) -> anyhow::Result { + ) -> Result { disable_when_not_stared!(self); let unwinder = DwarfUnwinder::new(&self.debugee); let location = Location { @@ -650,7 +651,7 @@ impl Debugger { // there is no chance to determine frame number, cause pc may owned by code outside backtrace // so set frame num to 0 is ok .context_for(&ExplorationContext::new(location, 0))? - .ok_or(anyhow!("fetch register fail"))? + .ok_or(UnwindNoContext)? .registers()) } @@ -660,13 +661,17 @@ impl Debugger { /// /// * `register_name`: x86-64 register name (ex: `rip`) /// * `val`: 8 bite value - pub fn set_register_value(&self, register_name: &str, val: u64) -> anyhow::Result<()> { + pub fn set_register_value(&self, register_name: &str, val: u64) -> Result<(), Error> { disable_when_not_stared!(self); let in_focus_pid = self.exploration_ctx().pid_on_focus(); let mut map = RegisterMap::current(in_focus_pid)?; - map.update(Register::try_from(register_name)?, val); - Ok(map.persist(in_focus_pid)?) + map.update( + Register::try_from(register_name) + .map_err(|_| RegisterNameNotFound(register_name.into()))?, + val, + ); + map.persist(in_focus_pid) } /// Return list of known files income from dwarf parser. @@ -737,7 +742,7 @@ impl Drop for Debugger { } /// Read N bytes from `PID` process. -pub fn read_memory_by_pid(pid: Pid, addr: usize, read_n: usize) -> nix::Result> { +pub fn read_memory_by_pid(pid: Pid, addr: usize, read_n: usize) -> Result, nix::Error> { let mut read_reminder = read_n as isize; let mut result = Vec::with_capacity(read_n); diff --git a/src/debugger/process.rs b/src/debugger/process.rs index 630d43a9..d34327c7 100644 --- a/src/debugger/process.rs +++ b/src/debugger/process.rs @@ -1,3 +1,5 @@ +use crate::debugger::error::Error; +use crate::debugger::error::Error::{Ptrace, Waitpid}; use nix::sys; use nix::sys::personality::Persona; use nix::sys::ptrace::Options; @@ -68,7 +70,7 @@ impl Child { impl Child { /// Instantiate process by `fork()` system call with caller as a parent process. /// After installation child process stopped by `SIGSTOP` signal. - pub fn install(&self) -> anyhow::Result> { + pub fn install(&self) -> Result, Error> { let mut debugee_cmd = Command::new(&self.program); let debugee_cmd = debugee_cmd .args(&self.args) @@ -84,13 +86,14 @@ impl Child { match unsafe { fork().expect("fork() error") } { ForkResult::Parent { child: pid } => { - waitpid(Pid::from_raw(-1), Some(WaitPidFlag::WSTOPPED))?; + waitpid(Pid::from_raw(-1), Some(WaitPidFlag::WSTOPPED)).map_err(Waitpid)?; sys::ptrace::seize( pid, Options::PTRACE_O_TRACECLONE .union(Options::PTRACE_O_TRACEEXEC) .union(Options::PTRACE_O_TRACEEXIT), - )?; + ) + .map_err(Ptrace)?; Ok(Child { stdout: self.stdout.try_clone()?, diff --git a/src/debugger/register.rs b/src/debugger/register.rs index a04ab16f..36c4cbbf 100644 --- a/src/debugger/register.rs +++ b/src/debugger/register.rs @@ -1,4 +1,5 @@ -use anyhow::anyhow; +use crate::debugger::error::Error; +use crate::debugger::error::Error::{Ptrace, RegisterNotFound}; use nix::libc::user_regs_struct; use nix::sys; use nix::unistd::Pid; @@ -182,8 +183,8 @@ impl RegisterMap { /// # Arguments /// /// * `pid`: thread id. - pub fn current(pid: Pid) -> nix::Result { - let regs = sys::ptrace::getregs(pid)?; + pub fn current(pid: Pid) -> Result { + let regs = sys::ptrace::getregs(pid).map_err(Ptrace)?; Ok(regs.into()) } @@ -268,8 +269,8 @@ impl RegisterMap { /// # Arguments /// /// * `pid`: target thread. - pub fn persist(self, pid: Pid) -> nix::Result<()> { - sys::ptrace::setregs(pid, self.into()) + pub fn persist(self, pid: Pid) -> Result<(), Error> { + sys::ptrace::setregs(pid, self.into()).map_err(Ptrace) } } @@ -283,12 +284,12 @@ impl DwarfRegisterMap { /// # Arguments /// /// * `register`: target register. - pub fn value(&self, register: gimli::Register) -> anyhow::Result { + pub fn value(&self, register: gimli::Register) -> Result { self.0 .get(register.0 as usize) .copied() .and_then(|v| v) - .ok_or(anyhow!("register {} not found", register.0)) + .ok_or(RegisterNotFound(register)) } /// Set new register value. diff --git a/src/debugger/rust/mod.rs b/src/debugger/rust/mod.rs index f23f1f85..a1a6801b 100644 --- a/src/debugger/rust/mod.rs +++ b/src/debugger/rust/mod.rs @@ -1,4 +1,5 @@ -use anyhow::{anyhow, bail}; +use crate::debugger::error::Error; +use crate::debugger::error::Error::{DefaultToolchainNotFound, UnrecognizedRustupOut}; use log::warn; use once_cell::sync::OnceCell; use std::path::PathBuf; @@ -20,7 +21,7 @@ impl Environment { pub fn init(std_lib_path: Option) { let toolchain = default_toolchain(); if let Err(ref e) = toolchain { - warn!("detect toolchain: {e}") + warn!(target: "debugger", "detect toolchain: {e}") } if ENVIRONMENT .set(Environment { @@ -30,7 +31,7 @@ impl Environment { }) .is_err() { - warn!("rust env already set") + warn!(target: "debugger", "rust env already set") } } } @@ -48,7 +49,7 @@ impl Toolchain { } } -pub fn default_toolchain() -> anyhow::Result { +pub fn default_toolchain() -> Result { let rustup_out = Command::new("rustup") .args(["toolchain", "list", "-v"]) .output()?; @@ -56,12 +57,12 @@ pub fn default_toolchain() -> anyhow::Result { let toolchain = toolchains .lines() .find(|line| line.contains("(default)")) - .ok_or_else(|| anyhow!("default toolchain not found"))?; + .ok_or(DefaultToolchainNotFound)?; let toolchain_verbose_parts = toolchain.split_whitespace().collect::>(); if toolchain_verbose_parts.len() < 3 { - bail!("failed to recognize rustup output") + return Err(UnrecognizedRustupOut); } Ok(Toolchain { diff --git a/src/debugger/step.rs b/src/debugger/step.rs index 65177950..74da4131 100644 --- a/src/debugger/step.rs +++ b/src/debugger/step.rs @@ -2,8 +2,9 @@ use crate::debugger::address::{Address, GlobalAddress}; use crate::debugger::breakpoint::Breakpoint; use crate::debugger::debugee::dwarf::unit::PlaceDescriptorOwned; use crate::debugger::debugee::tracer::{StopReason, TraceContext}; +use crate::debugger::error::Error; +use crate::debugger::error::Error::{NoFunctionRanges, PlaceNotFound, ProcessExit}; use crate::debugger::{Debugger, ExplorationContext}; -use anyhow::{anyhow, bail}; use nix::sys::signal::Signal; /// Result of a step, if [`SignalInterrupt`] then step process interrupted by a signal and user must know it. @@ -35,7 +36,7 @@ impl Debugger { /// or [`StepResult::Done`] if step done. /// /// **! change exploration context** - pub(super) fn step_in(&mut self) -> anyhow::Result { + pub(super) fn step_in(&mut self) -> Result { enum PlaceOrSignal { Place(PlaceDescriptorOwned), Signal(Signal), @@ -44,7 +45,7 @@ impl Debugger { // make instruction step but ignoring functions prolog // initial function must exists (do instruction steps until it's not) // returns stop place or signal if step is undone - fn step_over_prolog(debugger: &mut Debugger) -> anyhow::Result { + fn step_over_prolog(debugger: &mut Debugger) -> Result { loop { // initial step if let Some(StopReason::SignalStop(_, sign)) = debugger.single_step_instruction()? { @@ -146,7 +147,7 @@ impl Debugger { /// May return a [`StopReason::SignalStop`] if the step didn't happen cause signal. /// /// **! change exploration context** - pub(super) fn single_step_instruction(&mut self) -> anyhow::Result> { + pub(super) fn single_step_instruction(&mut self) -> Result, Error> { let loc = self.exploration_ctx().location(); let mb_signal = if self.breakpoints.get_enabled(loc.pc).is_some() { self.step_over_breakpoint()? @@ -165,7 +166,7 @@ impl Debugger { /// May return a [`StopReason::SignalStop`] if the step didn't happen cause signal. /// /// **! change exploration context** - pub(super) fn step_over_breakpoint(&mut self) -> anyhow::Result> { + pub(super) fn step_over_breakpoint(&mut self) -> Result, Error> { // cannot use debugee::Location mapping offset may be not init yet let tracee = self .debugee @@ -190,7 +191,7 @@ impl Debugger { /// Move to higher stack frame. /// /// **! change exploration context** - pub(super) fn step_out_frame(&mut self) -> anyhow::Result<()> { + pub(super) fn step_out_frame(&mut self) -> Result<(), Error> { let ctx = self.exploration_ctx(); let location = ctx.location(); let debug_info = self.debugee.debug_info(location.pc)?; @@ -216,7 +217,7 @@ impl Debugger { /// or [`StepResult::Done`] if step done. /// /// **! change exploration context** - pub(super) fn step_over_any(&mut self) -> anyhow::Result { + pub(super) fn step_over_any(&mut self) -> Result { let ctx = self.exploration_ctx(); let mut current_location = ctx.location(); @@ -239,16 +240,17 @@ impl Debugger { let current_place = dwarf .find_place_from_pc(current_location.global_pc)? - .ok_or_else(|| anyhow!("current line not found"))?; + .ok_or(PlaceNotFound(current_location.global_pc))?; let mut step_over_breakpoints = vec![]; let mut to_delete = vec![]; + let fn_full_name = func.full_name(); for range in func.ranges() { let mut place = func .unit() .find_place_by_pc(GlobalAddress::from(range.begin)) - .ok_or_else(|| anyhow!("unknown function range"))?; + .ok_or_else(|| NoFunctionRanges(fn_full_name.clone()))?; while place.address.in_range(range) { // skip places in function prolog @@ -272,7 +274,7 @@ impl Debugger { { let load_addr = place .address - .relocate(self.debugee.mapping_offset_for_pc(current_location.pc)?); + .relocate_to_segment_by_pc(&self.debugee, current_location.pc)?; if self.breakpoints.get_enabled(load_addr).is_none() { step_over_breakpoints.push(load_addr); to_delete.push(load_addr); @@ -330,7 +332,7 @@ impl Debugger { .debugee .debug_info(new_location.pc)? .find_place_from_pc(new_location.global_pc)? - .ok_or_else(|| anyhow!("unknown function range"))?; + .ok_or_else(|| NoFunctionRanges(fn_full_name))?; if place.address != new_location.global_pc { if let StepResult::SignalInterrupt { signal, .. } = self.step_in()? { return Ok(StepResult::signal_interrupt(signal)); @@ -339,7 +341,8 @@ impl Debugger { } if self.debugee.is_exited() { - bail!("debugee exited while step execution"); + // todo add exit code here + return Err(ProcessExit(0)); } self.expl_ctx_update_location()?; diff --git a/src/debugger/utils.rs b/src/debugger/utils.rs index f63e1a2e..1c9e7676 100644 --- a/src/debugger/utils.rs +++ b/src/debugger/utils.rs @@ -1,57 +1,3 @@ -#[macro_export] -macro_rules! _error { - ($log_fn: path, $res: expr) => { - match $res { - Ok(value) => Some(value), - Err(e) => { - $log_fn!(target: "debugger", "{:#}", e); - None - } - } - }; - ($log_fn: path, $res: expr, $msg: tt) => { - match $res { - Ok(value) => Some(value), - Err(e) => { - $log_fn!(target: "debugger", concat!($msg, " {:#}"), e); - None - } - } - }; -} - -/// Transforms `Result` into `Option` and logs an error if it occurs. -#[macro_export] -macro_rules! weak_error { - ($res: expr) => { - $crate::_error!(log::warn, $res) - }; - ($res: expr, $msg: tt) => { - $crate::_error!(log::warn, $res, $msg) - }; -} - -/// Transforms `Result` into `Option` and put error into debug logs if it occurs. -#[macro_export] -macro_rules! muted_error { - ($res: expr) => { - $crate::_error!(log::debug, $res) - }; - ($res: expr, $msg: tt) => { - $crate::_error!(log::debug, $res, $msg) - }; -} - -/// Macro for handle an error lists as warnings. -#[macro_export] -macro_rules! print_warns { - ($errors:expr) => { - $errors.iter().for_each(|e| { - log::warn!(target: "debugger", "{:#}", e); - }) - }; -} - /// Types can implement this trait for include cache functionality. pub trait TryGetOrInsert { /// Returns inner value if exists, otherwise execute function `f`, then save returned value and return it. diff --git a/src/debugger/variable/mod.rs b/src/debugger/variable/mod.rs index 8f09783d..cbf4354f 100644 --- a/src/debugger/variable/mod.rs +++ b/src/debugger/variable/mod.rs @@ -1,11 +1,11 @@ use crate::debugger::debugee::dwarf::r#type::{ ArrayType, CModifier, EvaluationContext, ScalarType, StructureMember, TypeIdentity, }; +use crate::debugger::debugee::dwarf::r#type::{ComplexType, TypeDeclaration}; use crate::debugger::debugee::dwarf::{AsAllocatedValue, ContextualDieRef, NamespaceHierarchy}; use crate::debugger::variable::render::RenderRepr; use crate::debugger::variable::specialization::VariableParserExtension; use crate::{debugger, weak_error}; -use anyhow::anyhow; use bytes::Bytes; use gimli::{ DW_ATE_address, DW_ATE_boolean, DW_ATE_float, DW_ATE_signed, DW_ATE_signed_char, @@ -14,14 +14,48 @@ use gimli::{ use log::warn; use std::collections::{HashMap, VecDeque}; use std::fmt::{Debug, Display, Formatter}; +use std::string::FromUtf8Error; pub mod render; pub mod select; mod specialization; -use crate::debugger::debugee::dwarf::r#type::{ComplexType, TypeDeclaration}; pub use specialization::SpecializedVariableIR; +#[derive(Debug, thiserror::Error, PartialEq)] +pub enum AssumeError { + #[error("field `{0}` not found")] + FieldNotFound(&'static str), + #[error("field `{0}` not a number")] + FieldNotANumber(&'static str), + #[error("incomplete interpretation of `{0}`")] + IncompleteInterp(&'static str), + #[error("not data for {0}")] + NoData(&'static str), + #[error("not type for {0}")] + NoType(&'static str), + #[error("underline data not a string")] + DataNotAString(#[from] FromUtf8Error), + #[error("undefined size of type `{0}`")] + UnknownSize(String), + #[error("type parameter `{0}` not found")] + TypeParameterNotFound(&'static str), + #[error("unknown type for type parameter `{0}`")] + TypeParameterTypeNotFound(&'static str), + #[error("unexpected type for {0}")] + UnexpectedType(&'static str), + #[error("unexpected binary representation of {0}, expect {1} got {2} bytes")] + UnexpectedBinaryRepr(&'static str, usize, usize), +} + +#[derive(Debug, thiserror::Error, PartialEq)] +pub enum ParsingError { + #[error(transparent)] + Assume(#[from] AssumeError), + #[error("error while reading from debugee memory: {0}")] + ReadDebugeeMemory(#[from] nix::Error), +} + /// Identifier of debugee variables. /// Consists of the name and namespace of the variable. #[derive(Clone)] @@ -594,7 +628,10 @@ impl<'a> VariableParser<'a> { } 16 => render_scalar::(value).map(SupportedScalar::I128), _ => { - warn!("unsupported signed size: {size:?}", size = r#type.byte_size); + warn!( + "parse scalar: unexpected signed size: {size:?}", + size = r#type.byte_size + ); None } }, @@ -613,7 +650,7 @@ impl<'a> VariableParser<'a> { 16 => render_scalar::(value).map(SupportedScalar::U128), _ => { warn!( - "unsupported unsigned size: {size:?}", + "parse scalar: unexpected unsigned size: {size:?}", size = r#type.byte_size ); None @@ -623,7 +660,10 @@ impl<'a> VariableParser<'a> { 4 => render_scalar::(value).map(SupportedScalar::F32), 8 => render_scalar::(value).map(SupportedScalar::F64), _ => { - warn!("unsupported float size: {size:?}", size = r#type.byte_size); + warn!( + "parse scalar: unexpected float size: {size:?}", + size = r#type.byte_size + ); None } }, @@ -631,7 +671,7 @@ impl<'a> VariableParser<'a> { DW_ATE_UTF => render_scalar::(value).map(SupportedScalar::Char), DW_ATE_ASCII => render_scalar::(value).map(SupportedScalar::Char), _ => { - warn!("unsupported base type encoding: {encoding}"); + warn!("parse scalar: unexpected base type encoding: {encoding}"); None } }); @@ -672,10 +712,10 @@ impl<'a> VariableParser<'a> { parent_value: Option<&Bytes>, ) -> Option { let name = member.name.clone(); - let type_ref = weak_error!(member.type_ref.ok_or(anyhow!( - "unknown type for member {}", - name.as_deref().unwrap_or_default() - )))?; + let Some(type_ref) = member.type_ref else { + warn!("parse structure: unknown type for member {}", name.as_deref().unwrap_or_default()); + return None; + }; let member_val = parent_value.and_then(|val| member.value(eval_ctx, self.r#type, val.as_ptr() as usize)); @@ -1042,16 +1082,6 @@ impl<'a> VariableParser<'a> { } } -#[derive(Debug, thiserror::Error)] -enum AssumeError { - #[error("field `{0}` not found")] - FieldNotFound(&'static str), - #[error("field `{0}` not a number")] - FieldNotANumber(&'static str), - #[error("incomplete interpretation of `{0}`")] - IncompleteInterp(&'static str), -} - /// Iterator for visits underline values in BFS order. struct BfsIterator<'a> { queue: VecDeque<&'a VariableIR>, @@ -1251,13 +1281,6 @@ mod test { identity: VariableIdentity::no_namespace(Some("pointer_1".to_owned())), type_name: None, value: None, - // deref: Some(Box::new(VariableIR::Scalar(ScalarVariable { - // identity: VariableIdentity::no_namespace(Some( - // "scalar_4".to_owned(), - // )), - // type_name: None, - // value: None, - // }))), target_type: None, }), ], diff --git a/src/debugger/variable/select.rs b/src/debugger/variable/select.rs index 71b2b5dc..c641a12a 100644 --- a/src/debugger/variable/select.rs +++ b/src/debugger/variable/select.rs @@ -1,10 +1,11 @@ use crate::debugger::debugee::dwarf; use crate::debugger::debugee::dwarf::r#type::ComplexType; use crate::debugger::debugee::dwarf::{AsAllocatedValue, ContextualDieRef}; -use crate::debugger::variable::VariableIR; +use crate::debugger::error::Error; +use crate::debugger::error::Error::FunctionNotFound; +use crate::debugger::variable::{AssumeError, ParsingError, VariableIR}; use crate::debugger::{variable, Debugger}; use crate::{ctx_resolve_unit_call, weak_error}; -use anyhow::anyhow; use std::collections::hash_map::Entry; #[derive(Debug, PartialEq)] @@ -14,7 +15,8 @@ pub enum VariableSelector { } /// List of operations for select variables and their properties. -/// Expression can be parsed from an input string like "*(*variable1.field2)[1]" (see debugger::command module) +/// Expression can be parsed from an input string like `*(*variable1.field2)[1]` (see debugger::command module) +/// /// Supported operations are: dereference, get element by index, get field by name, make slice from pointer. #[derive(Debug, PartialEq)] pub enum Expression { @@ -43,25 +45,21 @@ macro_rules! type_from_cache { Entry::Vacant(v) => $variable.r#type().map(|t| &*v.insert(t)), }, ) - .ok_or(anyhow!( - "unknown type for variable {name}", - name = $variable.die.name().unwrap_or_default() - )) + .ok_or_else(|| ParsingError::Assume(AssumeError::NoType("variable"))) }; } impl<'a> SelectExpressionEvaluator<'a> { - pub fn new(debugger: &'a Debugger, expression: Expression) -> anyhow::Result { - Ok(Self { + pub fn new(debugger: &'a Debugger, expression: Expression) -> Self { + Self { debugger, - // expl_context: debugger.exploration_ctx(), expression, - }) + } } /// Evaluate only variable names. /// Only filter expression supported. - pub fn evaluate_names(&self) -> anyhow::Result> { + pub fn evaluate_names(&self) -> Result, Error> { let ctx = self.debugger.exploration_ctx(); match &self.expression { Expression::Variable(selector) => { @@ -77,7 +75,7 @@ impl<'a> SelectExpressionEvaluator<'a> { .debugee .debug_info(ctx.location().pc)? .find_function_by_pc(ctx.location().global_pc)? - .ok_or_else(|| anyhow!("not in function"))?; + .ok_or(FunctionNotFound(ctx.location().global_pc))?; current_func.local_variables(ctx.location().global_pc) } }; @@ -86,16 +84,16 @@ impl<'a> SelectExpressionEvaluator<'a> { .filter_map(|die| die.die.name().map(ToOwned::to_owned)) .collect()) } - _ => panic!("unsupported"), + _ => unreachable!("unexpected expression variant"), } } /// Evaluate select expression and returns list of matched variables. - pub fn evaluate(&self) -> anyhow::Result> { + pub fn evaluate(&self) -> Result, Error> { self.evaluate_inner(&self.expression) } - fn evaluate_inner(&self, expression: &Expression) -> anyhow::Result> { + fn evaluate_inner(&self, expression: &Expression) -> Result, Error> { let ctx = self.debugger.exploration_ctx(); // evaluate variable one by one in `evaluate_single_variable` method // here just filter variables @@ -113,7 +111,7 @@ impl<'a> SelectExpressionEvaluator<'a> { .debugee .debug_info(ctx.location().pc)? .find_function_by_pc(ctx.location().global_pc)? - .ok_or_else(|| anyhow!("not in function"))?; + .ok_or(FunctionNotFound(ctx.location().global_pc))?; current_func.local_variables(ctx.location().global_pc) } }; @@ -137,15 +135,16 @@ impl<'a> SelectExpressionEvaluator<'a> { } /// Same as [`SelectExpressionEvaluator::evaluate_names`] but for function arguments. - pub fn evaluate_on_arguments_names(&self) -> anyhow::Result> { + pub fn evaluate_on_arguments_names(&self) -> Result, Error> { match &self.expression { Expression::Variable(selector) => { + let expl_ctx_loc = self.debugger.exploration_ctx().location(); let current_function = self .debugger .debugee - .debug_info(self.debugger.exploration_ctx().location().pc)? - .find_function_by_pc(self.debugger.exploration_ctx().location().global_pc)? - .ok_or_else(|| anyhow!("not in function"))?; + .debug_info(expl_ctx_loc.pc)? + .find_function_by_pc(expl_ctx_loc.global_pc)? + .ok_or(FunctionNotFound(expl_ctx_loc.global_pc))?; let params = current_function.parameters(); let params = match selector { @@ -163,27 +162,28 @@ impl<'a> SelectExpressionEvaluator<'a> { .filter_map(|die| die.die.name().map(ToOwned::to_owned)) .collect()) } - _ => panic!("unsupported"), + _ => unreachable!("unexpected expression variant"), } } /// Same as [`SelectExpressionEvaluator::evaluate`] but for function arguments. - pub fn evaluate_on_arguments(&self) -> anyhow::Result> { + pub fn evaluate_on_arguments(&self) -> Result, Error> { self.evaluate_on_arguments_inner(&self.expression) } fn evaluate_on_arguments_inner( &self, expression: &Expression, - ) -> anyhow::Result> { + ) -> Result, Error> { match expression { Expression::Variable(selector) => { + let expl_ctx_loc = self.debugger.exploration_ctx().location(); let current_function = self .debugger .debugee - .debug_info(self.debugger.exploration_ctx().location().pc)? - .find_function_by_pc(self.debugger.exploration_ctx().location().global_pc)? - .ok_or_else(|| anyhow!("not in function"))?; + .debug_info(expl_ctx_loc.pc)? + .find_function_by_pc(expl_ctx_loc.global_pc)? + .ok_or(FunctionNotFound(expl_ctx_loc.global_pc))?; let params = current_function.parameters(); let params = match selector { diff --git a/src/debugger/variable/specialization/btree.rs b/src/debugger/variable/specialization/btree.rs index 997a0ce6..5282e79f 100644 --- a/src/debugger/variable/specialization/btree.rs +++ b/src/debugger/variable/specialization/btree.rs @@ -2,15 +2,17 @@ use crate::debugger; use crate::debugger::debugee::dwarf::r#type::{ ComplexType, EvaluationContext, StructureMember, TypeIdentity, }; +use crate::debugger::variable::AssumeError::NoType; +use crate::debugger::variable::ParsingError::ReadDebugeeMemory; +use crate::debugger::variable::{AssumeError, ParsingError}; use crate::debugger::TypeDeclaration; -use anyhow::anyhow; use fallible_iterator::FallibleIterator; use std::mem; use std::ptr::NonNull; const B: usize = 6; -/// Helper function, returns true if member name exists and starts with `starts_with` string. +/// Helper function, returns true if structure member name exists and starts with `starts_with` string. fn assert_member_name(member: &StructureMember, starts_with: &str) -> bool { member .name @@ -155,13 +157,14 @@ impl Leaf { r#type: &ComplexType, ptr: *const (), markup: &LeafNodeMarkup, - ) -> anyhow::Result { + ) -> Result { let leaf_bytes = debugger::read_memory_by_pid( eval_ctx.expl_ctx.pid_on_focus(), ptr as usize, markup.size, - )?; - Self::from_bytes(eval_ctx, r#type, leaf_bytes, markup) + ) + .map_err(ReadDebugeeMemory)?; + Ok(Self::from_bytes(eval_ctx, r#type, leaf_bytes, markup)?) } fn from_bytes( @@ -169,42 +172,53 @@ impl Leaf { r#type: &ComplexType, bytes: Vec, markup: &LeafNodeMarkup, - ) -> anyhow::Result { + ) -> Result { let parent = unsafe { - mem::transmute::<[u8; mem::size_of::>>()], Option>>( + const EXPECTED_SIZE: usize = mem::size_of::>>(); + mem::transmute::<[u8; EXPECTED_SIZE], Option>>( markup .parent .value(eval_ctx, r#type, bytes.as_ptr() as usize) - .ok_or(anyhow!("read leaf node"))? + .ok_or(AssumeError::NoData("leaf node (parent)"))? .to_vec() .try_into() - .map_err(|e| anyhow!("{e:?}"))?, + .map_err(|data: Vec<_>| { + AssumeError::UnexpectedBinaryRepr( + "leaf node (parent)", + EXPECTED_SIZE, + data.len(), + ) + })?, ) }; let len_bytes = markup .len .value(eval_ctx, r#type, bytes.as_ptr() as usize) - .ok_or(anyhow!("read leaf node"))? + .ok_or(AssumeError::NoData("leaf node (len)"))? .to_vec(); - let len = u16::from_ne_bytes(len_bytes.try_into().map_err(|e| anyhow!("{e:?}"))?); + let len = u16::from_ne_bytes(len_bytes.try_into().map_err(|data: Vec<_>| { + AssumeError::UnexpectedBinaryRepr("leaf node len", 2, data.len()) + })?); let parent_idx_bytes = markup .parent_idx .value(eval_ctx, r#type, bytes.as_ptr() as usize) - .ok_or(anyhow!("read leaf node"))? + .ok_or(AssumeError::NoData("leaf node (parent index)"))? .to_vec(); let parent_idx = - u16::from_ne_bytes(parent_idx_bytes.try_into().map_err(|e| anyhow!("{e:?}"))?); + u16::from_ne_bytes(parent_idx_bytes.try_into().map_err(|data: Vec<_>| { + AssumeError::UnexpectedBinaryRepr("leaf node parent index", 2, data.len()) + })?); let keys_raw = markup .keys .value(eval_ctx, r#type, bytes.as_ptr() as usize) - .ok_or(anyhow!("read leaf node"))? + .ok_or(AssumeError::NoData("leaf node (keys)"))? .to_vec(); let vals_raw = markup .vals .value(eval_ctx, r#type, bytes.as_ptr() as usize) - .ok_or(anyhow!("read leaf node"))? + .ok_or(AssumeError::NoData("leaf node (vals)"))? .to_vec(); Ok(Leaf { @@ -230,32 +244,36 @@ impl Internal { ptr: *const (), l_markup: &LeafNodeMarkup, i_markup: &InternalNodeMarkup, - ) -> anyhow::Result { + ) -> Result { let bytes = debugger::read_memory_by_pid( eval_ctx.expl_ctx.pid_on_focus(), ptr as usize, i_markup.size, - )?; + ) + .map_err(ReadDebugeeMemory)?; let edges_v = i_markup .edges .value(eval_ctx, r#type, bytes.as_ptr() as usize) - .ok_or(anyhow!("read internal node"))? + .ok_or(AssumeError::NoData("internal node (edges_v)"))? .to_vec() - .chunks_exact(8) + .chunks_exact(mem::size_of::()) .map(|chunk| { - Ok( - usize::from_ne_bytes(chunk.try_into().map_err(|e| anyhow!("{e:?}"))?) - as *const (), - ) + usize::from_ne_bytes( + chunk + .try_into() + .expect("unreachable: 8 bytes chunk must be convertible for usize"), + ) as *const () }) - .collect::>>()?; - let edges: [*const (); B * 2] = edges_v.try_into().map_err(|e| anyhow!("{e:?}"))?; + .collect::>(); + let edges: [*const (); B * 2] = edges_v + .try_into() + .map_err(|_edges: Vec<_>| AssumeError::NoData("internal node (edges_v)"))?; let leaf_bytes = i_markup .data .value(eval_ctx, r#type, bytes.as_ptr() as usize) - .ok_or(anyhow!("read internal node"))?; + .ok_or(AssumeError::NoData("internal node (leaf_bytes)"))?; Ok(Internal { leaf: Leaf::from_bytes(eval_ctx, r#type, leaf_bytes.to_vec(), l_markup)?, @@ -328,7 +346,7 @@ impl Handle { self, eval_ctx: &EvaluationContext, reflection: &BTreeReflection, - ) -> anyhow::Result { + ) -> Result { if self.node_is_leaf() { Ok(Handle { node: self.node, @@ -360,7 +378,7 @@ impl Handle { self, eval_ctx: &EvaluationContext, reflection: &BTreeReflection, - ) -> anyhow::Result { + ) -> Result { let mut handle = self; while !handle.node_is_leaf() { @@ -379,7 +397,7 @@ impl Handle { &self, eval_ctx: &EvaluationContext, reflection: &BTreeReflection, - ) -> anyhow::Result> { + ) -> Result, ParsingError> { let leaf = self.node.data.leaf(); let parent = match leaf.parent { None => return Ok(None), @@ -413,14 +431,14 @@ impl<'a> BTreeReflection<'a> { map_id: TypeIdentity, k_type_id: TypeIdentity, v_type_id: TypeIdentity, - ) -> anyhow::Result { + ) -> Result { Ok(Self { root: root_ptr, root_h: root_height, internal_markup: InternalNodeMarkup::from_type(r#type, map_id, k_type_id, v_type_id) - .ok_or(anyhow!("internal node type not found"))?, + .ok_or(NoType("internal node"))?, leaf_markup: LeafNodeMarkup::from_type(r#type, map_id, k_type_id, v_type_id) - .ok_or(anyhow!("leaf node type not found"))?, + .ok_or(NoType("leaf node"))?, r#type, k_type_id, v_type_id, @@ -432,7 +450,7 @@ impl<'a> BTreeReflection<'a> { eval_ctx: &EvaluationContext, node_ptr: *const (), height: usize, - ) -> anyhow::Result { + ) -> Result { let data = if height == 0 { LeafOrInternal::Leaf(Leaf::from_markup( eval_ctx, @@ -454,15 +472,15 @@ impl<'a> BTreeReflection<'a> { } /// Creates new BTreeMap key-value iterator. - pub fn iter(self, eval_ctx: &'a EvaluationContext) -> anyhow::Result> { + pub fn iter(self, eval_ctx: &'a EvaluationContext) -> Result, AssumeError> { let k_size = self .r#type .type_size_in_bytes(eval_ctx, self.k_type_id) - .ok_or_else(|| anyhow!("unknown hashmap bucket size"))?; + .ok_or(AssumeError::UnknownSize("btree key type".into()))?; let v_size = self .r#type .type_size_in_bytes(eval_ctx, self.v_type_id) - .ok_or_else(|| anyhow!("unknown hashmap bucket size"))?; + .ok_or(AssumeError::UnknownSize("btree value type".into()))?; Ok(KVIterator { reflection: self, @@ -484,7 +502,7 @@ pub struct KVIterator<'a> { impl<'a> FallibleIterator for KVIterator<'a> { type Item = (Vec, Vec); - type Error = anyhow::Error; + type Error = ParsingError; fn next(&mut self) -> Result, Self::Error> { let mut handle = match self.handle.take() { diff --git a/src/debugger/variable/specialization/hashbrown.rs b/src/debugger/variable/specialization/hashbrown.rs index cdd0766b..e3fcc706 100644 --- a/src/debugger/variable/specialization/hashbrown.rs +++ b/src/debugger/variable/specialization/hashbrown.rs @@ -42,7 +42,7 @@ impl GroupReflection { } /// Load group of control bytes from debugee process. - fn load(pid: Pid, ptr: *const u8) -> nix::Result { + fn load(pid: Pid, ptr: *const u8) -> Result { let mut data: [u8; 16] = Default::default(); data.copy_from_slice(&debugger::read_memory_by_pid( pid, @@ -118,7 +118,7 @@ impl HashmapReflection { self.bucket_mask + 1 } - pub(super) fn iter(&self, pid: Pid) -> nix::Result { + pub(super) fn iter(&self, pid: Pid) -> Result { unsafe { let ctrl = self.crtl; diff --git a/src/debugger/variable/specialization/mod.rs b/src/debugger/variable/specialization/mod.rs index aea8b52e..326e801c 100644 --- a/src/debugger/variable/specialization/mod.rs +++ b/src/debugger/variable/specialization/mod.rs @@ -5,17 +5,21 @@ use crate::debugger::debugee::dwarf::r#type::{EvaluationContext, TypeIdentity}; use crate::debugger::variable::render::RenderRepr; use crate::debugger::variable::specialization::btree::BTreeReflection; use crate::debugger::variable::specialization::hashbrown::HashmapReflection; +use crate::debugger::variable::AssumeError::{ + TypeParameterNotFound, TypeParameterTypeNotFound, UnexpectedType, +}; +use crate::debugger::variable::ParsingError::Assume; use crate::debugger::variable::{ - ArrayVariable, AssumeError, PointerVariable, ScalarVariable, StructVariable, SupportedScalar, - VariableIR, VariableIdentity, VariableParser, + ArrayVariable, AssumeError, ParsingError, PointerVariable, ScalarVariable, StructVariable, + SupportedScalar, VariableIR, VariableIdentity, VariableParser, }; use crate::{debugger, weak_error}; use anyhow::Context; -use anyhow::{anyhow, bail}; use bytes::Bytes; use fallible_iterator::FallibleIterator; use itertools::Itertools; use std::collections::HashMap; +use AssumeError::{FieldNotFound, IncompleteInterp, UnknownSize}; /// During program execution, the debugger may encounter uninitialized variables. /// For example look at this code: @@ -169,7 +173,7 @@ impl<'a> VariableParserExtension<'a> { &self, eval_ctx: &EvaluationContext, ir: VariableIR, - ) -> anyhow::Result { + ) -> Result { let len = ir.assume_field_as_scalar_number("length")?; let len = guard_len(len); @@ -184,7 +188,7 @@ impl<'a> VariableParserExtension<'a> { Ok(StrVariable { identity: ir.identity().clone(), - value: String::from_utf8(data.to_vec())?, + value: String::from_utf8(data.to_vec()).map_err(AssumeError::from)?, }) } @@ -196,7 +200,7 @@ impl<'a> VariableParserExtension<'a> { SpecializedVariableIR::String { string: weak_error!(self .parse_string_inner(eval_ctx, VariableIR::Struct(structure.clone())) - .context("string interpretation")), + .context("String interpretation")), original: structure, } } @@ -205,7 +209,7 @@ impl<'a> VariableParserExtension<'a> { &self, eval_ctx: &EvaluationContext, ir: VariableIR, - ) -> anyhow::Result { + ) -> Result { let len = ir.assume_field_as_scalar_number("len")?; let len = guard_len(len); @@ -219,7 +223,7 @@ impl<'a> VariableParserExtension<'a> { Ok(StringVariable { identity: ir.identity().clone(), - value: String::from_utf8(data)?, + value: String::from_utf8(data).map_err(AssumeError::from)?, }) } @@ -232,7 +236,7 @@ impl<'a> VariableParserExtension<'a> { SpecializedVariableIR::Vector { vec: weak_error!(self .parse_vector_inner(eval_ctx, VariableIR::Struct(structure.clone()), type_params) - .context("vec interpretation")), + .context("Vec interpretation")), original: structure, } } @@ -242,11 +246,11 @@ impl<'a> VariableParserExtension<'a> { eval_ctx: &EvaluationContext, ir: VariableIR, type_params: &HashMap>, - ) -> anyhow::Result { + ) -> Result { let inner_type = type_params .get("T") - .ok_or_else(|| anyhow!("template parameter `T`"))? - .ok_or_else(|| anyhow!("unreachable: template param die without type"))?; + .ok_or(TypeParameterNotFound("T"))? + .ok_or(TypeParameterTypeNotFound("T"))?; let len = ir.assume_field_as_scalar_number("len")?; let len = guard_len(len); @@ -255,11 +259,12 @@ impl<'a> VariableParserExtension<'a> { let data_ptr = ir.assume_field_as_pointer("pointer")?; - let el_type_size = self - .parser - .r#type + let el_type = self.parser.r#type; + let el_type_size = el_type .type_size_in_bytes(eval_ctx, inner_type) - .ok_or_else(|| anyhow!("unknown element size"))?; + .ok_or(UnknownSize( + el_type.type_name(inner_type).unwrap_or_default(), + ))?; let data = debugger::read_memory_by_pid( eval_ctx.expl_ctx.pid_on_focus(), @@ -323,7 +328,7 @@ impl<'a> VariableParserExtension<'a> { SpecializedVariableIR::Tls { tls_var: weak_error!(self .parse_tls_inner(VariableIR::Struct(structure.clone()), type_params) - .context("tls interpretation")), + .context("TLS variable interpretation")), original: structure, } } @@ -332,7 +337,7 @@ impl<'a> VariableParserExtension<'a> { &self, ir: VariableIR, type_params: &HashMap>, - ) -> anyhow::Result { + ) -> Result { // we assume that tls variable name represent in dwarf // as namespace flowed before "__getit" namespace let namespace = &ir.identity().namespace; @@ -343,17 +348,15 @@ impl<'a> VariableParserExtension<'a> { let inner_type = type_params .get("T") - .ok_or_else(|| anyhow!("template parameter `T`"))? - .ok_or_else(|| anyhow!("unreachable: template param die without type"))?; + .ok_or(TypeParameterNotFound("T"))? + .ok_or(TypeParameterTypeNotFound("T"))?; let inner = ir .bfs_iterator() .find(|child| child.name() == "inner") - .ok_or(AssumeError::FieldNotFound("inner"))?; + .ok_or(FieldNotFound("inner"))?; let inner_option = inner.assume_field_as_rust_enum("value")?; - let inner_value = inner_option - .value - .ok_or(AssumeError::IncompleteInterp(""))?; + let inner_value = inner_option.value.ok_or(IncompleteInterp("value"))?; // we assume that dwarf representation of tls variable contains ::Option if let VariableIR::Struct(opt_variant) = inner_value.as_ref() { @@ -364,7 +367,7 @@ impl<'a> VariableParserExtension<'a> { inner_value .bfs_iterator() .find(|child| child.name() == "0") - .ok_or(AssumeError::FieldNotFound("__0"))? + .ok_or(FieldNotFound("__0"))? .clone(), )) }; @@ -376,9 +379,9 @@ impl<'a> VariableParserExtension<'a> { }); } - bail!(AssumeError::IncompleteInterp( - "expect tls inner value is option" - )) + Err(ParsingError::Assume(IncompleteInterp( + "expect TLS inner value as option", + ))) } pub fn parse_hashmap( @@ -389,16 +392,16 @@ impl<'a> VariableParserExtension<'a> { SpecializedVariableIR::HashMap { map: weak_error!(self .parse_hashmap_inner(eval_ctx, VariableIR::Struct(structure.clone())) - .context("hashmap interpretation")), + .context("HashMap interpretation")), original: structure, } } - pub fn parse_hashmap_inner( + fn parse_hashmap_inner( &self, eval_ctx: &EvaluationContext, ir: VariableIR, - ) -> anyhow::Result { + ) -> Result { let ctrl = ir.assume_field_as_pointer("pointer")?; let bucket_mask = ir.assume_field_as_scalar_number("bucket_mask")?; @@ -406,20 +409,20 @@ impl<'a> VariableParserExtension<'a> { let kv_type = table .type_params .get("T") - .ok_or_else(|| anyhow!("hashmap bucket type not found"))? - .ok_or_else(|| anyhow!("unknown hashmap bucket type"))?; - let kv_size = self - .parser - .r#type + .ok_or(TypeParameterNotFound("T"))? + .ok_or(TypeParameterTypeNotFound("T"))?; + + let r#type = self.parser.r#type; + let kv_size = r#type .type_size_in_bytes(eval_ctx, kv_type) - .ok_or_else(|| anyhow!("unknown hashmap bucket size"))?; + .ok_or(UnknownSize(r#type.type_name(kv_type).unwrap_or_default()))?; let reflection = HashmapReflection::new(ctrl as *mut u8, bucket_mask as usize, kv_size as usize); let iterator = reflection.iter(eval_ctx.expl_ctx.pid_on_focus())?; let kv_items = iterator - .map_err(anyhow::Error::from) + .map_err(ParsingError::from) .filter_map(|bucket| { let data = bucket.read(eval_ctx.expl_ctx.pid_on_focus()); let tuple = self.parser.parse_inner( @@ -437,7 +440,7 @@ impl<'a> VariableParserExtension<'a> { } } - Err(anyhow!("unexpected bucket type")) + Err(Assume(UnexpectedType("hashmap bucket"))) }) .collect()?; @@ -456,16 +459,16 @@ impl<'a> VariableParserExtension<'a> { SpecializedVariableIR::HashSet { set: weak_error!(self .parse_hashset_inner(eval_ctx, VariableIR::Struct(structure.clone())) - .context("hashset interpretation")), + .context("HashSet interpretation")), original: structure, } } - pub fn parse_hashset_inner( + fn parse_hashset_inner( &self, eval_ctx: &EvaluationContext, ir: VariableIR, - ) -> anyhow::Result { + ) -> Result { let ctrl = ir.assume_field_as_pointer("pointer")?; let bucket_mask = ir.assume_field_as_scalar_number("bucket_mask")?; @@ -473,20 +476,21 @@ impl<'a> VariableParserExtension<'a> { let kv_type = table .type_params .get("T") - .ok_or_else(|| anyhow!("hashset bucket type not found"))? - .ok_or_else(|| anyhow!("unknown hashset bucket type"))?; + .ok_or(TypeParameterNotFound("T"))? + .ok_or(TypeParameterTypeNotFound("T"))?; + let r#type = self.parser.r#type; let kv_size = self .parser .r#type .type_size_in_bytes(eval_ctx, kv_type) - .ok_or_else(|| anyhow!("unknown hashset bucket size"))?; + .ok_or_else(|| UnknownSize(r#type.type_name(kv_type).unwrap_or_default()))?; let reflection = HashmapReflection::new(ctrl as *mut u8, bucket_mask as usize, kv_size as usize); let iterator = reflection.iter(eval_ctx.expl_ctx.pid_on_focus())?; let items = iterator - .map_err(anyhow::Error::from) + .map_err(ParsingError::from) .filter_map(|bucket| { let data = bucket.read(eval_ctx.expl_ctx.pid_on_focus()); @@ -505,7 +509,7 @@ impl<'a> VariableParserExtension<'a> { } } - Err(anyhow!("unexpected bucket type")) + Err(Assume(UnexpectedType("hashset bucket"))) }) .collect()?; @@ -531,29 +535,29 @@ impl<'a> VariableParserExtension<'a> { identity, type_params ) - .context("BTreeMap interpretation")), + .context("BTreeMap interpretation")), original: structure, } } - pub fn parse_btree_map_inner( + fn parse_btree_map_inner( &self, eval_ctx: &EvaluationContext, ir: VariableIR, identity: TypeIdentity, type_params: &HashMap>, - ) -> anyhow::Result { + ) -> Result { let height = ir.assume_field_as_scalar_number("height")?; let ptr = ir.assume_field_as_pointer("pointer")?; let k_type = type_params .get("K") - .ok_or_else(|| anyhow!("btree map bucket type not found"))? - .ok_or_else(|| anyhow!("unknown BTreeMap bucket type"))?; + .ok_or(TypeParameterNotFound("K"))? + .ok_or(TypeParameterTypeNotFound("K"))?; let v_type = type_params .get("V") - .ok_or_else(|| anyhow!("btree map bucket type not found"))? - .ok_or_else(|| anyhow!("unknown BTreeMap bucket type"))?; + .ok_or(TypeParameterNotFound("V"))? + .ok_or(TypeParameterTypeNotFound("V"))?; let reflection = BTreeReflection::new( self.parser.r#type, @@ -565,7 +569,7 @@ impl<'a> VariableParserExtension<'a> { )?; let iterator = reflection.iter(eval_ctx)?; let kv_items = iterator - .map_err(anyhow::Error::from) + .map_err(ParsingError::from) .map(|(k, v)| { let key = self.parser.parse_inner( eval_ctx, @@ -601,7 +605,7 @@ impl<'a> VariableParserExtension<'a> { } } - pub fn parse_btree_set_inner(&self, ir: VariableIR) -> anyhow::Result { + fn parse_btree_set_inner(&self, ir: VariableIR) -> Result { let inner_map = ir .bfs_iterator() .find_map(|child| { @@ -616,7 +620,7 @@ impl<'a> VariableParserExtension<'a> { } None }) - .ok_or(AssumeError::IncompleteInterp("BTreeMap"))?; + .ok_or(IncompleteInterp("BTreeMap"))?; Ok(HashSetVariable { identity: ir.identity().clone(), @@ -638,29 +642,29 @@ impl<'a> VariableParserExtension<'a> { VariableIR::Struct(structure.clone()), type_params ) - .context("VeqDequeue interpretation")), + .context("VeqDequeue interpretation")), original: structure, } } - pub fn parse_vec_dequeue_inner( + fn parse_vec_dequeue_inner( &self, eval_ctx: &EvaluationContext, ir: VariableIR, type_params: &HashMap>, - ) -> anyhow::Result { + ) -> Result { let inner_type = type_params .get("T") - .ok_or_else(|| anyhow!("template parameter `T`"))? - .ok_or_else(|| anyhow!("unreachable: template param die without type"))?; + .ok_or(TypeParameterNotFound("T"))? + .ok_or(TypeParameterTypeNotFound("T"))?; let len = ir.assume_field_as_scalar_number("len")? as usize; let len = guard_len(len as i64) as usize; - let el_type_size = self - .parser - .r#type + let r#type = self.parser.r#type; + let el_type_size = r#type .type_size_in_bytes(eval_ctx, inner_type) - .ok_or_else(|| anyhow!("unknown element size"))? as usize; + .ok_or_else(|| UnknownSize(r#type.type_name(inner_type).unwrap_or_default()))? + as usize; let cap = if el_type_size == 0 { usize::MAX } else { @@ -735,18 +739,18 @@ impl<'a> VariableParserExtension<'a> { SpecializedVariableIR::Cell { value: weak_error!(self .parse_cell_inner(VariableIR::Struct(structure.clone())) - .context("cell interpretation")) + .context("Cell interpretation")) .map(Box::new), original: structure, } } - pub fn parse_cell_inner(&self, ir: VariableIR) -> anyhow::Result { + fn parse_cell_inner(&self, ir: VariableIR) -> Result { let unsafe_cell = ir.assume_field_as_struct("value")?; let value = unsafe_cell .members .get(0) - .ok_or(AssumeError::IncompleteInterp("UnsafeCell"))?; + .ok_or(IncompleteInterp("UnsafeCell"))?; Ok(value.clone()) } @@ -754,13 +758,13 @@ impl<'a> VariableParserExtension<'a> { SpecializedVariableIR::RefCell { value: weak_error!(self .parse_refcell_inner(VariableIR::Struct(structure.clone())) - .context("refcell interpretation")) + .context("RefCell interpretation")) .map(Box::new), original: structure, } } - pub fn parse_refcell_inner(&self, ir: VariableIR) -> anyhow::Result { + fn parse_refcell_inner(&self, ir: VariableIR) -> Result { let borrow = ir .bfs_iterator() .find_map(|child| { @@ -773,9 +777,9 @@ impl<'a> VariableParserExtension<'a> { } None }) - .ok_or(AssumeError::IncompleteInterp("Cell"))?; + .ok_or(IncompleteInterp("Cell"))?; let VariableIR::Scalar(mut var) = *borrow else { - return Err(AssumeError::IncompleteInterp("Cell").into()); + return Err(IncompleteInterp("Cell").into()); }; var.identity = VariableIdentity::no_namespace(Some("borrow".to_string())); let borrow = VariableIR::Scalar(var); @@ -784,7 +788,7 @@ impl<'a> VariableParserExtension<'a> { let value = unsafe_cell .members .get(0) - .ok_or(AssumeError::IncompleteInterp("UnsafeCell"))?; + .ok_or(IncompleteInterp("UnsafeCell"))?; Ok(VariableIR::Struct(StructVariable { identity: ir.identity().clone(), @@ -798,12 +802,12 @@ impl<'a> VariableParserExtension<'a> { SpecializedVariableIR::Rc { value: weak_error!(self .parse_rc_inner(VariableIR::Struct(structure.clone())) - .context("rc interpretation")), + .context("Rc interpretation")), original: structure, } } - pub fn parse_rc_inner(&self, ir: VariableIR) -> anyhow::Result { + fn parse_rc_inner(&self, ir: VariableIR) -> Result { Ok(ir .bfs_iterator() .find_map(|child| { @@ -816,19 +820,19 @@ impl<'a> VariableParserExtension<'a> { } None }) - .ok_or(AssumeError::IncompleteInterp("rc"))?) + .ok_or(IncompleteInterp("rc"))?) } pub fn parse_arc(&self, structure: StructVariable) -> SpecializedVariableIR { SpecializedVariableIR::Arc { value: weak_error!(self .parse_arc_inner(VariableIR::Struct(structure.clone())) - .context("arc interpretation")), + .context("Arc interpretation")), original: structure, } } - pub fn parse_arc_inner(&self, ir: VariableIR) -> anyhow::Result { + fn parse_arc_inner(&self, ir: VariableIR) -> Result { Ok(ir .bfs_iterator() .find_map(|child| { @@ -841,6 +845,6 @@ impl<'a> VariableParserExtension<'a> { } None }) - .ok_or(AssumeError::IncompleteInterp("arc"))?) + .ok_or(IncompleteInterp("Arc"))?) } } diff --git a/tests/debugger/breakpoints.rs b/tests/debugger/breakpoints.rs index 09ecebcc..900872d0 100644 --- a/tests/debugger/breakpoints.rs +++ b/tests/debugger/breakpoints.rs @@ -19,7 +19,7 @@ fn test_debugee_run() { #[serial] fn test_multiple_brkpt_on_addr() { let process = prepare_debugee_process(HW_APP, &[]); - let debugee_pid = process.pid(); + let atempt_1_pid = process.pid(); let info = DebugeeRunInfo::default(); let mut dbg = Debugger::new(process, TestHooks::new(info.clone())).unwrap(); dbg.set_breakpoint_at_line("hello_world.rs", 5).unwrap(); @@ -43,7 +43,7 @@ fn test_multiple_brkpt_on_addr() { dbg.set_breakpoint_at_addr(addr_2).unwrap(); // restart - dbg.restart_debugee().unwrap(); + let atempt_2_pid = dbg.restart_debugee().unwrap(); // assert stop points assert_eq!(info.line.take(), Some(5)); @@ -53,7 +53,8 @@ fn test_multiple_brkpt_on_addr() { dbg.continue_debugee().unwrap(); - assert_no_proc!(debugee_pid); + assert_no_proc!(atempt_1_pid); + assert_no_proc!(atempt_2_pid); } #[test] diff --git a/tests/debugger/common/mod.rs b/tests/debugger/common/mod.rs index d1a25bf6..96992fba 100644 --- a/tests/debugger/common/mod.rs +++ b/tests/debugger/common/mod.rs @@ -58,11 +58,11 @@ impl EventHook for TestHooks { #[macro_export] macro_rules! assert_no_proc { ($pid:expr) => { - use sysinfo::{PidExt, SystemExt}; - - let sys = sysinfo::System::new_all(); - assert!(sys - .process(sysinfo::Pid::from_u32($pid.as_raw() as u32)) - .is_none()) + let sys = ::new_all(); + assert!(sysinfo::SystemExt::process( + &sys, + ::from_u32($pid.as_raw() as u32) + ) + .is_none()) }; }