From 39c25a21aa63f789667b6cb4c521709a9ebf9043 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 16 Sep 2020 15:40:52 +0200 Subject: [PATCH] add somewhat imprecise location info --- src/main.rs | 25 ++++++++++- src/pc2frames.rs | 108 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 124 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index f16f26bb..a8c1a1e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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, @@ -355,7 +355,6 @@ fn notmain() -> Result { #[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) { @@ -475,6 +474,7 @@ fn notmain() -> Result { &pc2frames, &vector_table, &sp_ram_region, + ¤t_dir, )?; core.reset_and_halt(TIMEOUT)?; @@ -620,6 +620,7 @@ fn backtrace( pc2frames: &pc2frames::Map, vector_table: &VectorTable, sp_ram_region: &Option, + current_dir: &Path, ) -> Result, 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 @@ -654,10 +655,30 @@ fn backtrace( } else { println!("{:>4}: @ {:#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(¤t_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; } } diff --git a/src/pc2frames.rs b/src/pc2frames.rs index 02418dbb..00f81c78 100644 --- a/src/pc2frames.rs +++ b/src/pc2frames.rs @@ -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 _}; @@ -70,7 +74,15 @@ pub fn from(object: &object::File, live_functions: &HashSet<&str>) -> Result) -> Result, + pub decl_loc: Location, +} + +#[derive(Debug)] +pub struct Location { + pub file: PathBuf, + pub line: u64, } #[derive(Clone, Debug, PartialEq)] @@ -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 { @@ -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 => { @@ -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, @@ -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 @@ -323,3 +371,49 @@ fn demangle(function: &str) -> String { demangled } + +// XXX copy-pasted from defmt/elf2table :sadface: +fn file_index_to_path( + index: u64, + unit: &gimli::Unit, + dwarf: &gimli::Dwarf, +) -> Result +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) +}