Skip to content

Commit

Permalink
feat(debugger): better at function breakpoints:
Browse files Browse the repository at this point in the history
- use special data structure for search places by function names (or any other "path templates")
- now by function breakpoints can be set on one or more places in program
- now by function breakpoints set by function name or part of full function path (with namespaces)
  • Loading branch information
godzie44 committed Sep 9, 2023
1 parent a6784f5 commit e5ebade
Show file tree
Hide file tree
Showing 15 changed files with 524 additions and 112 deletions.
8 changes: 4 additions & 4 deletions examples/shlib/calc_bin/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
extern "C" {
pub fn add(a: u32, b: u32) -> u32;
pub fn sub(a: u32, b: u32) -> u32;
pub fn calc_add(a: u32, b: u32) -> u32;
pub fn calc_sub(a: u32, b: u32) -> u32;
}

pub fn main() {
let sum_1_2 = unsafe { add(1, 2) };
let sub_2_1 = unsafe { sub(2, 1) };
let sum_1_2 = unsafe { calc_add(1, 2) };
let sub_2_1 = unsafe { calc_sub(2, 1) };

let print_lib =
unsafe { libloading::Library::new("./examples/target/debug/libprinter_lib.so").unwrap() };
Expand Down
4 changes: 2 additions & 2 deletions examples/shlib/calc_lib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#[no_mangle]
pub extern "C" fn add(a: u32, b: u32) -> u32 {
pub extern "C" fn calc_add(a: u32, b: u32) -> u32 {
a + b
}

#[no_mangle]
pub extern "C" fn sub(a: u32, b: u32) -> u32 {
pub extern "C" fn calc_sub(a: u32, b: u32) -> u32 {
a - b
}
13 changes: 13 additions & 0 deletions src/bin/calc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,16 @@ fn print(v: i64, description: &str) {

println!("{output}")
}

pub mod float {
#[no_mangle]
pub fn sum2(a: f64, b: f64) -> f64 {
a + b
}

#[no_mangle]
pub fn sum3(a: f64, b: f64, c: f64) -> f64 {
let ab = sum2(a, b);
sum2(ab, c)
}
}
10 changes: 7 additions & 3 deletions src/console/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,16 @@ Manage breakpoints.
Available subcomands:
break <location> - set breakpoint to location
break remove <location> - deactivate and delete selected breakpoint.
break info - show all breakpoints.
break remove <location> - deactivate and delete selected breakpoint
break info - show all breakpoints
Posible location format:
- at instruction. Example: break 0x55555555BD30
- at function start. Example: break main
- at function start. A function can be defined by its full name (with namespace)
or by function name (in case of possible collisions, breakpoints will be set in
all matching functions). Examples:
* break fn1
* break module1::fn1
- at code line. Example: break hello_world.rs:15
";

Expand Down
17 changes: 9 additions & 8 deletions src/console/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,15 +370,16 @@ impl AppLoop {
};

match Break::new(&mut self.debugger).handle(bp_cmd)? {
BreakpointHandlingResult::New(bp) => {
print_bp("New breakpoint", &bp);
BreakpointHandlingResult::New(brkpts) => {
brkpts
.iter()
.for_each(|brkpt| print_bp("New breakpoint", brkpt));
}
BreakpointHandlingResult::Removed(brkpts) => {
brkpts
.iter()
.for_each(|brkpt| print_bp("Remove breakpoint", brkpt));
}
BreakpointHandlingResult::Removed(mb_bp) => match mb_bp {
None => self.printer.print("Breakpoint not found"),
Some(ref bp) => {
print_bp("Remove breakpoint", bp);
}
},
BreakpointHandlingResult::Dump(brkpts) => brkpts
.iter()
.for_each(|brkpt| print_bp("- Breakpoint", brkpt)),
Expand Down
9 changes: 8 additions & 1 deletion src/debugger/breakpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,10 @@ impl BreakpointRegistry {
self.breakpoints.get(&addr)
}

pub fn get_disabled(&self, addr: Address) -> Option<&UninitBreakpoint> {
self.disabled_breakpoints.get(&addr)
}

/// Add uninit breakpoint, this means that breakpoint will be created later.
pub fn add_uninit(&mut self, brkpt: UninitBreakpoint) -> BreakpointView {
let addr = brkpt.addr;
Expand All @@ -378,7 +382,10 @@ impl BreakpointRegistry {
}

/// Remove breakpoint or uninit breakpoint from registry.
pub fn remove_by_addr(&mut self, addr: Address) -> anyhow::Result<Option<BreakpointView>> {
pub fn remove_by_addr(
&mut self,
addr: Address,
) -> anyhow::Result<Option<BreakpointView<'static>>> {
if let Some(brkpt) = self.disabled_breakpoints.remove(&addr) {
return Ok(Some(brkpt.into()));
}
Expand Down
34 changes: 24 additions & 10 deletions src/debugger/command/break.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ pub struct Break<'a> {
}

pub enum HandlingResult<'a> {
New(BreakpointView<'a>),
Removed(Option<BreakpointView<'a>>),
New(Vec<BreakpointView<'a>>),
Removed(Vec<BreakpointView<'a>>),
Dump(Vec<BreakpointView<'a>>),
}

Expand All @@ -34,19 +34,33 @@ impl<'a> Break<'a> {
let result = match cmd {
Command::Add(brkpt) => {
let res = match brkpt {
Breakpoint::Address(addr) => self.dbg.set_breakpoint_at_addr(addr.into()),
Breakpoint::Line(file, line) => self.dbg.set_breakpoint_at_line(&file, line),
Breakpoint::Function(func_name) => self.dbg.set_breakpoint_at_fn(&func_name),
Breakpoint::Address(addr) => {
vec![self.dbg.set_breakpoint_at_addr(addr.into())?]
}
Breakpoint::Line(file, line) => {
vec![self.dbg.set_breakpoint_at_line(&file, line)?]
}
Breakpoint::Function(func_name) => self.dbg.set_breakpoint_at_fn(&func_name)?,
};
HandlingResult::New(res?)
HandlingResult::New(res)
}
Command::Remove(brkpt) => {
let res = match brkpt {
Breakpoint::Address(addr) => self.dbg.remove_breakpoint_at_addr(addr.into()),
Breakpoint::Line(file, line) => self.dbg.remove_breakpoint_at_line(&file, line),
Breakpoint::Function(func_name) => self.dbg.remove_breakpoint_at_fn(&func_name),
Breakpoint::Address(addr) => self
.dbg
.remove_breakpoint_at_addr(addr.into())?
.map(|brkpt| vec![brkpt])
.unwrap_or_default(),
Breakpoint::Line(file, line) => self
.dbg
.remove_breakpoint_at_line(&file, line)?
.map(|brkpt| vec![brkpt])
.unwrap_or_default(),
Breakpoint::Function(func_name) => {
self.dbg.remove_breakpoint_at_fn(&func_name)?
}
};
HandlingResult::Removed(res?)
HandlingResult::Removed(res)
}
Command::Info => HandlingResult::Dump(self.dbg.breakpoints_snapshot()),
};
Expand Down
24 changes: 23 additions & 1 deletion src/debugger/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ fn hexadecimal(input: &str) -> IResult<&str, &str, ErrorTree<&str>> {
pub fn rust_identifier(input: &str) -> IResult<&str, &str, ErrorTree<&str>> {
recognize(pair(
alt((alpha1, tag("_"))),
many0_count(alt((alphanumeric1, tag("_")))),
many0_count(alt((alphanumeric1, tag("_"), tag("::")))),
))(input)
}

Expand Down Expand Up @@ -634,6 +634,15 @@ mod test {
));
},
},
TestCase {
inputs: vec!["b ns1::some_func", "break ns1::ns2::some_func"],
command_matcher: |result| {
assert!(matches!(
result.unwrap(),
Command::Breakpoint(r#break::Command::Add(Breakpoint::Function(f))) if f == "ns1::some_func" || f == "ns1::ns2::some_func"
));
},
},
TestCase {
inputs: vec!["b file:123", "break file:123", " break file:123 "],
command_matcher: |result| {
Expand Down Expand Up @@ -665,6 +674,19 @@ mod test {
));
},
},
TestCase {
inputs: vec![
"b r ns1::some_func",
"break r ns1::some_func",
" break r ns1::some_func ",
],
command_matcher: |result| {
assert!(matches!(
result.unwrap(),
Command::Breakpoint(r#break::Command::Remove(Breakpoint::Function(f))) if f == "ns1::some_func"
));
},
},
TestCase {
inputs: vec![
"b remove file:123",
Expand Down
76 changes: 45 additions & 31 deletions src/debugger/debugee/dwarf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod symbol;
pub mod r#type;
pub mod unit;
pub mod unwind;
mod utils;

pub use self::unwind::DwarfUnwinder;

Expand Down Expand Up @@ -43,6 +44,7 @@ use std::ops::{Add, Deref};
use std::path::{Path, PathBuf};
use std::sync::Arc;
pub use symbol::Symbol;
use unit::PlaceDescriptor;
use walkdir::WalkDir;

pub type EndianArcSlice = gimli::EndianArcSlice<gimli::RunTimeEndian>;
Expand Down Expand Up @@ -88,7 +90,7 @@ impl Clone for DebugInformation {
pub enum DebugInformationError {
#[error(transparent)]
Other(#[from] anyhow::Error),
#[error("not enough debug information to complete the request")]
#[error("Not enough debug information to complete the request")]
IncompleteInformation,
}

Expand Down Expand Up @@ -122,6 +124,11 @@ impl DebugInformation {
.ok_or(DebugInformationError::IncompleteInformation)
}

/// Return false if file dont contains a debug information.
pub fn has_debug_info(&self) -> bool {
self.units.is_some()
}

/// Return unit by its index.
///
/// # Arguments
Expand Down Expand Up @@ -220,16 +227,13 @@ impl DebugInformation {
}

/// Returns best matched place by program counter global address.
pub fn find_place_from_pc(&self, pc: GlobalAddress) -> Result<Option<unit::PlaceDescriptor>> {
pub fn find_place_from_pc(&self, pc: GlobalAddress) -> Result<Option<PlaceDescriptor>> {
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<Option<unit::PlaceDescriptor>> {
pub fn find_exact_place_from_pc(&self, pc: GlobalAddress) -> Result<Option<PlaceDescriptor>> {
let mb_unit = self.find_unit_by_pc(pc)?;
Ok(mb_unit.and_then(|u| u.find_exact_place_by_pc(pc)))
}
Expand Down Expand Up @@ -275,45 +279,55 @@ impl DebugInformation {
}))
}

/// Return a function by its name.
/// Return a functions relevant to template.
///
/// # Arguments
///
/// * `needle`: function name.
pub fn find_function_by_name(
&self,
needle: &str,
) -> Result<Option<ContextualDieRef<FunctionDie>>> {
Ok(self.get_units()?.iter().find_map(|unit| {
let mut entry_it = resolve_unit_call!(self.dwarf(), unit, entries_it);
entry_it.find_map(|entry| {
if let DieVariant::Function(func) = &entry.die {
if func.base_attributes.name.as_deref() == Some(needle) {
return Some(ContextualDieRef {
/// * `template`: search template (full function path or part of this path).
pub fn search_functions(&self, template: &str) -> Result<Vec<ContextualDieRef<FunctionDie>>> {
Ok(self
.get_units()?
.iter()
.flat_map(|unit| {
let entries = resolve_unit_call!(self.dwarf(), unit, search_functions, template);
entries
.iter()
.map(|entry| {
let DieVariant::Function(func) = &entry.die else {
unreachable!()
};
ContextualDieRef {
debug_info: self,
unit_idx: unit.idx(),
node: &entry.node,
die: func,
});
}
}
None
}
})
.collect::<Vec<_>>()
})
}))
.collect())
}

pub fn find_place(&self, file: &str, line: u64) -> Result<Option<unit::PlaceDescriptor<'_>>> {
pub fn find_place(&self, file: &str, line: u64) -> Result<Option<PlaceDescriptor<'_>>> {
Ok(self
.get_units()?
.iter()
.find_map(|unit| unit.find_stmt_line(file, line)))
}

pub fn get_function_place(&self, fn_name: &str) -> Result<PlaceDescriptorOwned> {
let func = self
.find_function_by_name(fn_name)?
.ok_or_else(|| anyhow!("function not found"))?;
Ok(func.prolog_end_place()?.to_owned())
/// Search all places for functions that relevant to template.
///
/// # Arguments
///
/// * `template`: search template (full function path or part of this path).
pub fn search_places_for_fn_tpl(&self, template: &str) -> Result<Vec<PlaceDescriptorOwned>> {
Ok(self
.search_functions(template)?
.into_iter()
.filter_map(|fn_die| {
weak_error!(fn_die.prolog_end_place()).map(|place| place.to_owned())
})
.collect())
}

pub fn find_symbols(&self, regex: &Regex) -> Vec<&Symbol> {
Expand Down Expand Up @@ -785,7 +799,7 @@ impl<'ctx> ContextualDieRef<'ctx, FunctionDie> {
result
}

pub fn prolog_start_place(&self) -> anyhow::Result<unit::PlaceDescriptor> {
pub fn prolog_start_place(&self) -> anyhow::Result<PlaceDescriptor> {
let low_pc = self
.die
.base_attributes
Expand All @@ -801,7 +815,7 @@ impl<'ctx> ContextualDieRef<'ctx, FunctionDie> {
.ok_or_else(|| anyhow!("invalid function entry"))
}

pub fn prolog_end_place(&self) -> anyhow::Result<unit::PlaceDescriptor> {
pub fn prolog_end_place(&self) -> anyhow::Result<PlaceDescriptor> {
let mut place = self.prolog_start_place()?;
while !place.prolog_end {
match place.next() {
Expand Down
Loading

0 comments on commit e5ebade

Please sign in to comment.