Skip to content
This repository was archived by the owner on Jan 30, 2024. It is now read-only.

Commit 39c25a2

Browse files
committed
add somewhat imprecise location info
1 parent 636125f commit 39c25a2

File tree

2 files changed

+124
-9
lines changed

2 files changed

+124
-9
lines changed

src/main.rs

+23-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::{
1111
collections::{btree_map, BTreeMap, HashSet},
1212
fs,
1313
io::{self, Write as _},
14-
path::PathBuf,
14+
path::{Path, PathBuf},
1515
process,
1616
sync::{Arc, Mutex},
1717
time::Duration,
@@ -355,7 +355,6 @@ fn notmain() -> Result<i32, anyhow::Error> {
355355
#[cfg(feature = "defmt")]
356356
let mut frames = vec![];
357357
let mut was_halted = false;
358-
#[cfg(feature = "defmt")]
359358
let current_dir = std::env::current_dir()?;
360359
// TODO strip prefix from crates-io paths (?)
361360
while !exit.load(Ordering::Relaxed) {
@@ -475,6 +474,7 @@ fn notmain() -> Result<i32, anyhow::Error> {
475474
&pc2frames,
476475
&vector_table,
477476
&sp_ram_region,
477+
&current_dir,
478478
)?;
479479

480480
core.reset_and_halt(TIMEOUT)?;
@@ -620,6 +620,7 @@ fn backtrace(
620620
pc2frames: &pc2frames::Map,
621621
vector_table: &VectorTable,
622622
sp_ram_region: &Option<RamRegion>,
623+
current_dir: &Path,
623624
) -> Result<Option<TopException>, anyhow::Error> {
624625
let mut debug_frame = DebugFrame::new(debug_frame, LittleEndian);
625626
// 32-bit ARM -- this defaults to the host's address size which is likely going to be 8
@@ -654,10 +655,30 @@ fn backtrace(
654655
} else {
655656
println!("{:>4}: <unknown> @ {:#012x}", frame_index, pc);
656657
}
658+
// XXX is there no location info for external assembly?
659+
println!(" at ???");
657660
frame_index += 1;
658661
} else {
662+
let mut call_loc: Option<&pc2frames::Location> = None;
663+
// this iterates in the "callee to caller" direction
659664
for frame in frames {
660665
println!("{:>4}: {}", frame_index, frame.value.name);
666+
667+
// call location is more precise; prefer that
668+
let loc = call_loc.unwrap_or_else(|| {
669+
&frame.value.decl_loc
670+
});
671+
672+
let relpath = if let Ok(relpath) = loc.file.strip_prefix(&current_dir) {
673+
relpath
674+
} else {
675+
// not relative; use full path
676+
&loc.file
677+
};
678+
println!(" at {}:{}", relpath.display(), loc.line);
679+
680+
// this is from where the caller (next iteration) called the callee (current iteration)
681+
call_loc = frame.value.call_loc.as_ref();
661682
frame_index += 1;
662683
}
663684
}

src/pc2frames.rs

+101-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
use core::{iter::FromIterator, ops::Range};
2-
use std::{borrow::Cow, collections::HashSet};
2+
use std::{
3+
borrow::Cow,
4+
collections::HashSet,
5+
path::{Path, PathBuf},
6+
};
37

4-
use anyhow::ensure;
8+
use anyhow::{bail, ensure};
59
use gimli::{read::Reader, DebuggingInformationEntry, Dwarf, Unit};
610
use intervaltree::{Element, IntervalTree};
711
use object::{Object as _, ObjectSection as _};
@@ -70,7 +74,15 @@ pub fn from(object: &object::File, live_functions: &HashSet<&str>) -> Result<Map
7074
let name = demangle(&sub.name);
7175
elements.push(Element {
7276
range,
73-
value: Frame { name, depth },
77+
value: Frame {
78+
name,
79+
depth,
80+
call_loc: None,
81+
decl_loc: Location {
82+
file: file_index_to_path(sub.decl_file, &unit, &dwarf)?,
83+
line: sub.decl_line,
84+
},
85+
},
7486
});
7587
} else {
7688
// we won't walk into subprograms that are were GC-ed by the linker
@@ -91,6 +103,18 @@ pub fn from(object: &object::File, live_functions: &HashSet<&str>) -> Result<Map
91103
value: Frame {
92104
name: demangle(&inline_sub.origin.name),
93105
depth,
106+
call_loc: Some(Location {
107+
file: file_index_to_path(inline_sub.call_file, &unit, &dwarf)?,
108+
line: inline_sub.call_line,
109+
}),
110+
decl_loc: Location {
111+
file: file_index_to_path(
112+
inline_sub.origin.decl_file,
113+
&unit,
114+
&dwarf,
115+
)?,
116+
line: inline_sub.origin.decl_line,
117+
},
94118
},
95119
})
96120
} else if entry.tag() == gimli::constants::DW_TAG_lexical_block
@@ -111,7 +135,14 @@ pub struct Frame {
111135
pub name: String,
112136
// depth in the DIE tree
113137
pub depth: isize,
114-
// TODO add file location
138+
pub call_loc: Option<Location>,
139+
pub decl_loc: Location,
140+
}
141+
142+
#[derive(Debug)]
143+
pub struct Location {
144+
pub file: PathBuf,
145+
pub line: u64,
115146
}
116147

117148
#[derive(Clone, Debug, PartialEq)]
@@ -126,9 +157,8 @@ struct Subprogram {
126157
depth: isize,
127158
name: String,
128159
span: Span,
129-
// NOTE the DIE contains `decl_file` and `decl_line` info but those points into
130-
// the *declaration* of the function, e.g. `fn foo() {`, which is not particularly useful.
131-
// We are more interested in the location of the statements within the function
160+
decl_file: u64,
161+
decl_line: u64,
132162
}
133163

134164
impl Subprogram {
@@ -150,6 +180,8 @@ impl Subprogram {
150180
let mut low_pc = None;
151181
let mut name = None;
152182
let mut pc_offset = None;
183+
let mut decl_file = None;
184+
let mut decl_line = None;
153185
while let Some(attr) = attrs.next()? {
154186
match attr.name() {
155187
gimli::constants::DW_AT_low_pc => {
@@ -188,12 +220,26 @@ impl Subprogram {
188220
}
189221
}
190222

223+
gimli::constants::DW_AT_decl_file => {
224+
if let gimli::AttributeValue::FileIndex(idx) = attr.value() {
225+
decl_file = Some(idx);
226+
}
227+
}
228+
229+
gimli::constants::DW_AT_decl_line => {
230+
if let gimli::AttributeValue::Udata(line) = attr.value() {
231+
decl_line = Some(line);
232+
}
233+
}
234+
191235
_ => {}
192236
}
193237
}
194238

195239
if let Some(off) = linkage_name.or(name) {
196240
let name = dwarf.string(off)?.to_string()?.into_owned();
241+
let decl_file = decl_file.expect("no `decl_file`");
242+
let decl_line = decl_line.expect("no `decl_line`");
197243

198244
Ok(Some(Subprogram {
199245
depth,
@@ -205,6 +251,8 @@ impl Subprogram {
205251
Span::Pc(low_pc..(low_pc + pc_off))
206252
},
207253
name,
254+
decl_file,
255+
decl_line,
208256
}))
209257
} else {
210258
// TODO what are these nameless subroutines? They seem to have "abstract origin" info
@@ -323,3 +371,49 @@ fn demangle(function: &str) -> String {
323371

324372
demangled
325373
}
374+
375+
// XXX copy-pasted from defmt/elf2table :sadface:
376+
fn file_index_to_path<R>(
377+
index: u64,
378+
unit: &gimli::Unit<R>,
379+
dwarf: &gimli::Dwarf<R>,
380+
) -> Result<PathBuf, anyhow::Error>
381+
where
382+
R: gimli::read::Reader,
383+
{
384+
ensure!(index != 0, "`FileIndex` was zero");
385+
386+
let header = if let Some(program) = &unit.line_program {
387+
program.header()
388+
} else {
389+
bail!("no `LineProgram`");
390+
};
391+
392+
let file = if let Some(file) = header.file(index) {
393+
file
394+
} else {
395+
bail!("no `FileEntry` for index {}", index)
396+
};
397+
398+
let mut p = PathBuf::new();
399+
if let Some(dir) = file.directory(header) {
400+
let dir = dwarf.attr_string(unit, dir)?;
401+
let dir_s = dir.to_string_lossy()?;
402+
let dir = Path::new(&dir_s[..]);
403+
404+
if !dir.is_absolute() {
405+
if let Some(ref comp_dir) = unit.comp_dir {
406+
p.push(&comp_dir.to_string_lossy()?[..]);
407+
}
408+
}
409+
p.push(&dir);
410+
}
411+
412+
p.push(
413+
&dwarf
414+
.attr_string(unit, file.path_name())?
415+
.to_string_lossy()?[..],
416+
);
417+
418+
Ok(p)
419+
}

0 commit comments

Comments
 (0)