Skip to content

Commit 0e93cc1

Browse files
committed
feat(debugger): better at function breakpoints:
= now by line breakpoints can be set on one or more places in program - now by line breakpoints may set by part of file name or full file path
1 parent 0146219 commit 0e93cc1

File tree

8 files changed

+289
-192
lines changed

8 files changed

+289
-192
lines changed

src/debugger/command/break.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@ impl<'a> Break<'a> {
3737
Breakpoint::Address(addr) => {
3838
vec![self.dbg.set_breakpoint_at_addr(addr.into())?]
3939
}
40-
Breakpoint::Line(file, line) => {
41-
vec![self.dbg.set_breakpoint_at_line(&file, line)?]
42-
}
40+
Breakpoint::Line(file, line) => self.dbg.set_breakpoint_at_line(&file, line)?,
4341
Breakpoint::Function(func_name) => self.dbg.set_breakpoint_at_fn(&func_name)?,
4442
};
4543
HandlingResult::New(res)
@@ -51,11 +49,9 @@ impl<'a> Break<'a> {
5149
.remove_breakpoint_at_addr(addr.into())?
5250
.map(|brkpt| vec![brkpt])
5351
.unwrap_or_default(),
54-
Breakpoint::Line(file, line) => self
55-
.dbg
56-
.remove_breakpoint_at_line(&file, line)?
57-
.map(|brkpt| vec![brkpt])
58-
.unwrap_or_default(),
52+
Breakpoint::Line(file, line) => {
53+
self.dbg.remove_breakpoint_at_line(&file, line)?
54+
}
5955
Breakpoint::Function(func_name) => {
6056
self.dbg.remove_breakpoint_at_fn(&func_name)?
6157
}

src/debugger/debugee/dwarf/mod.rs

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::debugger::debugee::dwarf::unit::{
1919
DieRef, DieVariant, DwarfUnitParser, Entry, FunctionDie, Node, ParameterDie,
2020
PlaceDescriptorOwned, Unit, VariableDie,
2121
};
22+
use crate::debugger::debugee::dwarf::utils::PathSearchIndex;
2223
use crate::debugger::debugee::{Debugee, Location};
2324
use crate::debugger::register::{DwarfRegisterMap, RegisterMap};
2425
use crate::debugger::ExplorationContext;
@@ -38,9 +39,9 @@ use object::{Object, ObjectSection};
3839
use rayon::prelude::*;
3940
use regex::Regex;
4041
use std::collections::{HashSet, VecDeque};
41-
use std::fs;
4242
use std::ops::{Add, Deref};
4343
use std::path::{Path, PathBuf};
44+
use std::{fs, path};
4445
pub use symbol::Symbol;
4546
use unit::PlaceDescriptor;
4647
use walkdir::WalkDir;
@@ -55,6 +56,9 @@ pub struct DebugInformation<R: gimli::Reader = EndianArcSlice> {
5556
units: Option<Vec<Unit>>,
5657
symbol_table: Option<SymbolTab>,
5758
pub_names: Option<HashSet<String>>,
59+
/// Index for fast search files by full path or part of file path. Contains unit index and
60+
/// indexes of lines in [`Unit::lines`] vector that belongs to a file, indexes ordered by line number.
61+
files_index: PathSearchIndex<(usize, Vec<usize>)>,
5862
}
5963

6064
impl Clone for DebugInformation {
@@ -82,6 +86,7 @@ impl Clone for DebugInformation {
8286
units: self.units.clone(),
8387
symbol_table: self.symbol_table.clone(),
8488
pub_names: self.pub_names.clone(),
89+
files_index: self.files_index.clone(),
8590
}
8691
}
8792
}
@@ -325,11 +330,40 @@ impl DebugInformation {
325330
.collect())
326331
}
327332

328-
pub fn find_place(&self, file: &str, line: u64) -> Result<Option<PlaceDescriptor<'_>>> {
329-
Ok(self
330-
.get_units()?
331-
.iter()
332-
.find_map(|unit| unit.find_stmt_line(file, line)))
333+
/// Return closest [`PlaceDescriptor`] for given file and line.
334+
/// Closest means that returns descriptor for target line or, if no descriptor for target line,
335+
/// place for next line after target.
336+
///
337+
/// # Arguments
338+
///
339+
/// * `file`: file name template (full path or part of a file path)
340+
/// * `line`: line number
341+
pub fn find_closest_place(
342+
&self,
343+
file_tpl: &str,
344+
line: u64,
345+
) -> Result<Vec<PlaceDescriptor<'_>>> {
346+
let files = self.files_index.get(file_tpl);
347+
348+
let mut result = vec![];
349+
for (unit_idx, file_lines) in files {
350+
let unit = self.unit_ensure(*unit_idx);
351+
for line_idx in file_lines {
352+
let line_row = unit.line(*line_idx);
353+
if line_row.line < line {
354+
continue;
355+
}
356+
if line_row.line > line + 1 {
357+
break;
358+
}
359+
360+
if let Some(place) = unit.find_place_by_idx(*line_idx) {
361+
result.push(place);
362+
break;
363+
}
364+
}
365+
}
366+
Ok(result)
333367
}
334368

335369
/// Search all places for functions that relevant to template.
@@ -608,6 +642,7 @@ impl DebugInformationBuilder {
608642
units: None,
609643
symbol_table,
610644
pub_names,
645+
files_index: PathSearchIndex::new(""),
611646
});
612647
}
613648

@@ -622,6 +657,14 @@ impl DebugInformationBuilder {
622657
units.sort_unstable_by_key(|u| u.offset());
623658
units.iter_mut().enumerate().for_each(|(i, u)| u.set_idx(i));
624659

660+
let mut files_index = PathSearchIndex::new(path::MAIN_SEPARATOR_STR);
661+
units.iter().for_each(|unit| {
662+
unit.file_path_with_lines_pairs()
663+
.for_each(|(file_path, lines)| {
664+
files_index.insert(file_path, (unit.idx(), lines));
665+
});
666+
});
667+
625668
Ok(DebugInformation {
626669
file: obj_path.to_path_buf(),
627670
inner: dwarf,
@@ -630,6 +673,7 @@ impl DebugInformationBuilder {
630673
units: Some(units),
631674
symbol_table,
632675
pub_names,
676+
files_index,
633677
})
634678
}
635679
}

src/debugger/debugee/dwarf/unit/mod.rs

Lines changed: 52 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@ use once_cell::sync::OnceCell;
1515
use std::cell::RefCell;
1616
use std::collections::HashMap;
1717
use std::fmt::{Debug, Formatter};
18+
use std::path;
1819
use std::path::{Path, PathBuf};
1920
use uuid::Uuid;
2021

2122
/// A row in the line number program's resulting matrix.
2223
#[derive(PartialEq, Debug, Clone)]
23-
struct LineRow {
24-
address: u64,
25-
file_index: u64,
26-
line: u64,
27-
column: u64,
28-
is_stmt: bool,
29-
prolog_end: bool,
30-
epilog_begin: bool,
24+
pub(super) struct LineRow {
25+
pub(super) address: u64,
26+
pub(super) file_index: u64,
27+
pub(super) line: u64,
28+
pub(super) column: u64,
29+
pub(super) is_stmt: bool,
30+
pub(super) prolog_end: bool,
31+
pub(super) epilog_begin: bool,
3132
}
3233

3334
/// An address range of debug information entry,
@@ -76,7 +77,7 @@ impl<'a> Debug for PlaceDescriptor<'a> {
7677

7778
impl<'a> PlaceDescriptor<'a> {
7879
pub fn next(&self) -> Option<PlaceDescriptor<'a>> {
79-
self.unit.find_place(self.pos_in_unit + 1)
80+
self.unit.find_place_by_idx(self.pos_in_unit + 1)
8081
}
8182

8283
pub fn line_eq(&self, other: &PlaceDescriptor) -> bool {
@@ -489,13 +490,13 @@ impl Unit {
489490
}
490491

491492
/// Return [`PlaceDescriptor`] by index for lines vector in unit.
492-
fn find_place(&self, line_pos: usize) -> Option<PlaceDescriptor> {
493+
pub(super) fn find_place_by_idx(&self, line_pos: usize) -> Option<PlaceDescriptor> {
493494
let line = self.lines.get(line_pos)?;
494495
Some(PlaceDescriptor {
495496
file: self
496497
.files
497498
.get(line.file_index as usize)
498-
.expect("parse error"),
499+
.expect("unreachable: file must exists"),
499500
address: line.address.into(),
500501
line_number: line.line,
501502
column_number: line.column,
@@ -507,6 +508,10 @@ impl Unit {
507508
})
508509
}
509510

511+
pub(super) fn line(&self, index: usize) -> &LineRow {
512+
&self.lines[index]
513+
}
514+
510515
/// Return nearest [`PlaceDescriptor`] for given program counter.
511516
///
512517
/// # Arguments
@@ -519,7 +524,7 @@ impl Unit {
519524
Err(p) => p - 1,
520525
};
521526

522-
self.find_place(pos)
527+
self.find_place_by_idx(pos)
523528
}
524529

525530
/// Return place with address equals to given program counter.
@@ -530,32 +535,11 @@ impl Unit {
530535
pub fn find_exact_place_by_pc(&self, pc: GlobalAddress) -> Option<PlaceDescriptor> {
531536
let pc = u64::from(pc);
532537
match self.lines.binary_search_by_key(&pc, |line| line.address) {
533-
Ok(p) => self.find_place(p),
538+
Ok(p) => self.find_place_by_idx(p),
534539
Err(_) => None,
535540
}
536541
}
537542

538-
/// Return [`PlaceDescriptor`] for given file and line.
539-
///
540-
/// # Arguments
541-
///
542-
/// * `file`: file name
543-
/// * `line`: line number
544-
pub fn find_stmt_line(&self, file: &str, line: u64) -> Option<PlaceDescriptor<'_>> {
545-
let found = self
546-
.files
547-
.iter()
548-
.enumerate()
549-
.find(|(_, file_path)| file_path.ends_with(file))?;
550-
for (pos, line_row) in self.lines.iter().enumerate() {
551-
if line_row.line == line && line_row.file_index == found.0 as u64 {
552-
return self.find_place(pos);
553-
}
554-
}
555-
556-
None
557-
}
558-
559543
/// Return list on debug entries.
560544
/// Note: this method requires a full unit.
561545
pub fn entries(&self) -> UnitResult<&Vec<Entry>> {
@@ -663,4 +647,38 @@ impl Unit {
663647
pub fn files(&self) -> &[PathBuf] {
664648
&self.files
665649
}
650+
651+
/// Return pairs (file path, indexes of file lines in unit.lines list). This useful for
652+
/// create searching indexes.
653+
pub(super) fn file_path_with_lines_pairs(
654+
&self,
655+
) -> impl Iterator<Item = (impl IntoIterator<Item = impl ToString + '_>, Vec<usize>)> {
656+
let mut grouped_by_file_lines = HashMap::new();
657+
for (line_idx, line) in self.lines.iter().enumerate() {
658+
let file_idx = line.file_index as usize;
659+
let entry = grouped_by_file_lines.entry(file_idx).or_insert(vec![]);
660+
entry.push(line_idx)
661+
}
662+
663+
self.files
664+
.iter()
665+
.enumerate()
666+
.filter_map(move |(idx, file)| {
667+
let mut file_lines = grouped_by_file_lines.get(&idx).cloned().unwrap_or_default();
668+
// files without lines not interests
669+
if file_lines.is_empty() {
670+
return None;
671+
}
672+
673+
file_lines.sort_unstable_by_key(|line| self.lines[*line].line);
674+
675+
Some((
676+
file.iter().filter_map(|s| {
677+
let s = s.to_string_lossy();
678+
(s != path::MAIN_SEPARATOR_STR).then_some(s)
679+
}),
680+
file_lines,
681+
))
682+
})
683+
}
666684
}

src/debugger/debugee/dwarf/unit/parser.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ impl<'a> DwarfUnitParser<'a> {
151151
if let Some(ref fn_name) = base_attrs.name {
152152
// function without range are useless for this index
153153
if !base_attrs.ranges.is_empty() {
154-
function_index.insert_w_head(&fn_ns, fn_name, current_idx);
154+
function_index.insert_w_head(fn_ns.iter(), fn_name, current_idx);
155155
}
156156
}
157157

@@ -373,7 +373,7 @@ where
373373
let header = rows.header();
374374
match header.file(0) {
375375
Some(file) => files.push(render_file_path(unit, file, header, dwarf)?),
376-
None => files.push(PathBuf::from("")),
376+
None => files.push(PathBuf::default()),
377377
}
378378
let mut index = 1;
379379
while let Some(file) = header.file(index) {

0 commit comments

Comments
 (0)