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

Commit

Permalink
add somewhat imprecise location info
Browse files Browse the repository at this point in the history
  • Loading branch information
japaric committed Sep 16, 2020
1 parent 636125f commit 39c25a2
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 9 deletions.
25 changes: 23 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::{
collections::{btree_map, BTreeMap, HashSet},
fs,
io::{self, Write as _},
path::PathBuf,
path::{Path, PathBuf},
process,
sync::{Arc, Mutex},
time::Duration,
Expand Down Expand Up @@ -355,7 +355,6 @@ fn notmain() -> Result<i32, anyhow::Error> {
#[cfg(feature = "defmt")]
let mut frames = vec![];
let mut was_halted = false;
#[cfg(feature = "defmt")]
let current_dir = std::env::current_dir()?;
// TODO strip prefix from crates-io paths (?)
while !exit.load(Ordering::Relaxed) {
Expand Down Expand Up @@ -475,6 +474,7 @@ fn notmain() -> Result<i32, anyhow::Error> {
&pc2frames,
&vector_table,
&sp_ram_region,
&current_dir,
)?;

core.reset_and_halt(TIMEOUT)?;
Expand Down Expand Up @@ -620,6 +620,7 @@ fn backtrace(
pc2frames: &pc2frames::Map,
vector_table: &VectorTable,
sp_ram_region: &Option<RamRegion>,
current_dir: &Path,
) -> Result<Option<TopException>, anyhow::Error> {
let mut debug_frame = DebugFrame::new(debug_frame, LittleEndian);
// 32-bit ARM -- this defaults to the host's address size which is likely going to be 8
Expand Down Expand Up @@ -654,10 +655,30 @@ fn backtrace(
} else {
println!("{:>4}: <unknown> @ {:#012x}", frame_index, pc);
}
// XXX is there no location info for external assembly?
println!(" at ???");
frame_index += 1;
} else {
let mut call_loc: Option<&pc2frames::Location> = None;
// this iterates in the "callee to caller" direction
for frame in frames {
println!("{:>4}: {}", frame_index, frame.value.name);

// call location is more precise; prefer that
let loc = call_loc.unwrap_or_else(|| {
&frame.value.decl_loc
});

let relpath = if let Ok(relpath) = loc.file.strip_prefix(&current_dir) {
relpath
} else {
// not relative; use full path
&loc.file
};
println!(" at {}:{}", relpath.display(), loc.line);

// this is from where the caller (next iteration) called the callee (current iteration)
call_loc = frame.value.call_loc.as_ref();
frame_index += 1;
}
}
Expand Down
108 changes: 101 additions & 7 deletions src/pc2frames.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use core::{iter::FromIterator, ops::Range};
use std::{borrow::Cow, collections::HashSet};
use std::{
borrow::Cow,
collections::HashSet,
path::{Path, PathBuf},
};

use anyhow::ensure;
use anyhow::{bail, ensure};
use gimli::{read::Reader, DebuggingInformationEntry, Dwarf, Unit};
use intervaltree::{Element, IntervalTree};
use object::{Object as _, ObjectSection as _};
Expand Down Expand Up @@ -70,7 +74,15 @@ pub fn from(object: &object::File, live_functions: &HashSet<&str>) -> Result<Map
let name = demangle(&sub.name);
elements.push(Element {
range,
value: Frame { name, depth },
value: Frame {
name,
depth,
call_loc: None,
decl_loc: Location {
file: file_index_to_path(sub.decl_file, &unit, &dwarf)?,
line: sub.decl_line,
},
},
});
} else {
// we won't walk into subprograms that are were GC-ed by the linker
Expand All @@ -91,6 +103,18 @@ pub fn from(object: &object::File, live_functions: &HashSet<&str>) -> Result<Map
value: Frame {
name: demangle(&inline_sub.origin.name),
depth,
call_loc: Some(Location {
file: file_index_to_path(inline_sub.call_file, &unit, &dwarf)?,
line: inline_sub.call_line,
}),
decl_loc: Location {
file: file_index_to_path(
inline_sub.origin.decl_file,
&unit,
&dwarf,
)?,
line: inline_sub.origin.decl_line,
},
},
})
} else if entry.tag() == gimli::constants::DW_TAG_lexical_block
Expand All @@ -111,7 +135,14 @@ pub struct Frame {
pub name: String,
// depth in the DIE tree
pub depth: isize,
// TODO add file location
pub call_loc: Option<Location>,
pub decl_loc: Location,
}

#[derive(Debug)]
pub struct Location {
pub file: PathBuf,
pub line: u64,
}

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

impl Subprogram {
Expand All @@ -150,6 +180,8 @@ impl Subprogram {
let mut low_pc = None;
let mut name = None;
let mut pc_offset = None;
let mut decl_file = None;
let mut decl_line = None;
while let Some(attr) = attrs.next()? {
match attr.name() {
gimli::constants::DW_AT_low_pc => {
Expand Down Expand Up @@ -188,12 +220,26 @@ impl Subprogram {
}
}

gimli::constants::DW_AT_decl_file => {
if let gimli::AttributeValue::FileIndex(idx) = attr.value() {
decl_file = Some(idx);
}
}

gimli::constants::DW_AT_decl_line => {
if let gimli::AttributeValue::Udata(line) = attr.value() {
decl_line = Some(line);
}
}

_ => {}
}
}

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

Ok(Some(Subprogram {
depth,
Expand All @@ -205,6 +251,8 @@ impl Subprogram {
Span::Pc(low_pc..(low_pc + pc_off))
},
name,
decl_file,
decl_line,
}))
} else {
// TODO what are these nameless subroutines? They seem to have "abstract origin" info
Expand Down Expand Up @@ -323,3 +371,49 @@ fn demangle(function: &str) -> String {

demangled
}

// XXX copy-pasted from defmt/elf2table :sadface:
fn file_index_to_path<R>(
index: u64,
unit: &gimli::Unit<R>,
dwarf: &gimli::Dwarf<R>,
) -> Result<PathBuf, anyhow::Error>
where
R: gimli::read::Reader,
{
ensure!(index != 0, "`FileIndex` was zero");

let header = if let Some(program) = &unit.line_program {
program.header()
} else {
bail!("no `LineProgram`");
};

let file = if let Some(file) = header.file(index) {
file
} else {
bail!("no `FileEntry` for index {}", index)
};

let mut p = PathBuf::new();
if let Some(dir) = file.directory(header) {
let dir = dwarf.attr_string(unit, dir)?;
let dir_s = dir.to_string_lossy()?;
let dir = Path::new(&dir_s[..]);

if !dir.is_absolute() {
if let Some(ref comp_dir) = unit.comp_dir {
p.push(&comp_dir.to_string_lossy()?[..]);
}
}
p.push(&dir);
}

p.push(
&dwarf
.attr_string(unit, file.path_name())?
.to_string_lossy()?[..],
);

Ok(p)
}

0 comments on commit 39c25a2

Please sign in to comment.