Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly collect/identify used DWARF entries. #186

Merged
merged 5 commits into from Jul 17, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 1 addition & 2 deletions wasmtime-debug/Cargo.toml
Expand Up @@ -12,13 +12,12 @@ readme = "README.md"
edition = "2018"

[dependencies]
gimli = "0.17.0"
gimli = "0.19.0"
wasmparser = { version = "0.32.1" }
cranelift-codegen = "0.33.0"
cranelift-entity = "0.33.0"
cranelift-wasm = "0.33.0"
faerie = "0.10.1"
wasmtime-environ = { path = "../wasmtime-environ", default-features = false }
target-lexicon = { version = "0.4.0", default-features = false }
failure = { version = "0.1.3", default-features = false }
failure_derive = { version = "0.1.3", default-features = false }
Expand Down
39 changes: 29 additions & 10 deletions wasmtime-debug/src/address_transform.rs
@@ -1,11 +1,11 @@
use crate::read_debuginfo::WasmFileInfo;
use crate::transform::ModuleAddressMap;
use cranelift_entity::{EntityRef, PrimaryMap};
use cranelift_wasm::DefinedFuncIndex;
use gimli::write;
use std::collections::BTreeMap;
use std::ops::Bound::{Included, Unbounded};
use std::iter::FromIterator;
use std::vec::Vec;
use wasmtime_environ::AddressTransforms;

pub type GeneratedAddress = usize;
pub type WasmAddress = u64;
Expand All @@ -26,13 +26,16 @@ pub struct FunctionMap {

#[derive(Debug)]
pub struct AddressTransform {
lookup: BTreeMap<WasmAddress, (SymbolIndex, GeneratedAddress, GeneratedAddress)>,
lookup: Vec<(
WasmAddress,
(SymbolIndex, GeneratedAddress, GeneratedAddress),
)>,
map: PrimaryMap<DefinedFuncIndex, FunctionMap>,
func_ranges: Vec<(usize, usize)>,
}

impl AddressTransform {
pub fn new(at: &AddressTransforms, wasm_file: &WasmFileInfo) -> Self {
pub fn new(at: &ModuleAddressMap, wasm_file: &WasmFileInfo) -> Self {
let code_section_offset = wasm_file.code_section_offset;
let function_offsets = &wasm_file.function_offsets_and_sizes;
let mut lookup = BTreeMap::new();
Expand All @@ -50,7 +53,7 @@ impl AddressTransform {
(index, ft.body_offset, ft.body_offset),
);
let mut fn_map = Vec::new();
for t in &ft.locations {
for t in &ft.instructions {
if t.srcloc.is_default() {
// TODO extend some range if possible
continue;
Expand All @@ -76,21 +79,37 @@ impl AddressTransform {
addresses: fn_map.into_boxed_slice(),
});
}

let lookup = Vec::from_iter(lookup.into_iter());

AddressTransform {
lookup,
map,
func_ranges,
}
}

pub fn can_translate_address(&self, addr: u64) -> bool {
self.translate(addr).is_some()
}

pub fn translate(&self, addr: u64) -> Option<write::Address> {
if addr == 0 {
// It's normally 0 for debug info without the linked code.
return None;
}
let search = self.lookup.range((Unbounded, Included(addr)));
if let Some((_, value)) = search.last() {
return Some(write::Address::Relative {
let found = match self.lookup.binary_search_by(|entry| entry.0.cmp(&addr)) {
Ok(i) => Some(&self.lookup[i].1),
Err(i) => {
if i > 0 {
Some(&self.lookup[i - 1].1)
} else {
None
}
}
};
if let Some(value) = found {
return Some(write::Address::Symbol {
symbol: value.0,
addend: value.1 as i64,
});
Expand All @@ -106,11 +125,11 @@ impl AddressTransform {
return None;
}
if let (
Some(write::Address::Relative {
Some(write::Address::Symbol {
symbol: s1,
addend: a,
}),
Some(write::Address::Relative {
Some(write::Address::Symbol {
symbol: s2,
addend: b,
}),
Expand Down
232 changes: 232 additions & 0 deletions wasmtime-debug/src/gc.rs
@@ -0,0 +1,232 @@
use crate::address_transform::AddressTransform;
use gimli::constants;
use gimli::read;
use gimli::{Reader, UnitSectionOffset};
use std::collections::{HashMap, HashSet};
use std::vec::Vec;

#[derive(Debug)]
pub struct Dependencies {
edges: HashMap<UnitSectionOffset, HashSet<UnitSectionOffset>>,
roots: HashSet<UnitSectionOffset>,
}
yurydelendik marked this conversation as resolved.
Show resolved Hide resolved

impl Dependencies {
fn new() -> Dependencies {
Dependencies {
edges: HashMap::new(),
roots: HashSet::new(),
}
}

fn add_edge(&mut self, a: UnitSectionOffset, b: UnitSectionOffset) {
use std::collections::hash_map::Entry;
match self.edges.entry(a) {
Entry::Occupied(mut o) => {
o.get_mut().insert(b);
}
Entry::Vacant(v) => {
let mut set = HashSet::new();
set.insert(b);
v.insert(set);
}
}
}

fn add_root(&mut self, root: UnitSectionOffset) {
self.roots.insert(root);
}

pub fn get_reachable(&self) -> HashSet<UnitSectionOffset> {
let mut reachable = self.roots.clone();
let mut queue = Vec::new();
for i in self.roots.iter() {
if let Some(deps) = self.edges.get(i) {
for j in deps {
if reachable.contains(j) {
continue;
}
reachable.insert(*j);
queue.push(*j);
}
}
}
while let Some(i) = queue.pop() {
if let Some(deps) = self.edges.get(&i) {
for j in deps {
if reachable.contains(j) {
continue;
}
reachable.insert(*j);
queue.push(*j);
}
}
}
reachable
}
}

pub fn build_dependencies<R: Reader<Offset = usize>>(
dwarf: &read::Dwarf<R>,
at: &AddressTransform,
) -> read::Result<Dependencies> {
let mut deps = Dependencies::new();
let mut units = dwarf.units();
while let Some(unit) = units.next()? {
build_unit_dependencies(unit, dwarf, at, &mut deps)?;
}
Ok(deps)
}

fn build_unit_dependencies<R: Reader<Offset = usize>>(
header: read::CompilationUnitHeader<R>,
dwarf: &read::Dwarf<R>,
at: &AddressTransform,
deps: &mut Dependencies,
) -> read::Result<()> {
let unit = dwarf.unit(header)?;
let mut tree = unit.entries_tree(None)?;
let root = tree.root()?;
build_die_dependencies(root, dwarf, &unit, at, deps)?;
Ok(())
}

fn has_die_back_edge<R: Reader<Offset = usize>>(die: &read::DebuggingInformationEntry<R>) -> bool {
match die.tag() {
constants::DW_TAG_variable
| constants::DW_TAG_constant
| constants::DW_TAG_inlined_subroutine
| constants::DW_TAG_lexical_block
| constants::DW_TAG_label
| constants::DW_TAG_with_stmt
| constants::DW_TAG_try_block
| constants::DW_TAG_catch_block
| constants::DW_TAG_template_type_parameter
| constants::DW_TAG_member
| constants::DW_TAG_formal_parameter => true,
_ => false,
}
}

fn has_valid_code_range<R: Reader<Offset = usize>>(
die: &read::DebuggingInformationEntry<R>,
dwarf: &read::Dwarf<R>,
unit: &read::Unit<R>,
at: &AddressTransform,
) -> read::Result<bool> {
match die.tag() {
constants::DW_TAG_subprogram => {
if let Some(ranges_attr) = die.attr_value(constants::DW_AT_ranges)? {
let offset = match ranges_attr {
read::AttributeValue::RangeListsRef(val) => val,
read::AttributeValue::DebugRngListsIndex(index) => {
dwarf.ranges_offset(unit, index)?
}
_ => return Ok(false),
};
let mut has_valid_base = if let Some(read::AttributeValue::Addr(low_pc)) =
die.attr_value(constants::DW_AT_low_pc)?
{
Some(at.can_translate_address(low_pc))
} else {
None
};
let mut it = dwarf.ranges.raw_ranges(offset, unit.encoding())?;
while let Some(range) = it.next()? {
// If at least one of the range addresses can be converted,
// declaring code range as valid.
match range {
read::RawRngListEntry::AddressOrOffsetPair { .. }
if has_valid_base.is_some() =>
{
if has_valid_base.unwrap() {
return Ok(true);
}
}
read::RawRngListEntry::StartEnd { begin, .. }
| read::RawRngListEntry::StartLength { begin, .. }
| read::RawRngListEntry::AddressOrOffsetPair { begin, .. } => {
if at.can_translate_address(begin) {
return Ok(true);
}
}
read::RawRngListEntry::StartxEndx { begin, .. }
| read::RawRngListEntry::StartxLength { begin, .. } => {
let addr = dwarf.address(unit, begin)?;
if at.can_translate_address(addr) {
return Ok(true);
}
}
read::RawRngListEntry::BaseAddress { addr } => {
has_valid_base = Some(at.can_translate_address(addr));
}
read::RawRngListEntry::BaseAddressx { addr } => {
let addr = dwarf.address(unit, addr)?;
has_valid_base = Some(at.can_translate_address(addr));
}
read::RawRngListEntry::OffsetPair { .. } => (),
}
}
return Ok(false);
} else if let Some(low_pc) = die.attr_value(constants::DW_AT_low_pc)? {
if let read::AttributeValue::Addr(a) = low_pc {
return Ok(at.can_translate_address(a));
}
}
}
_ => (),
}
Ok(false)
}

fn build_die_dependencies<R: Reader<Offset = usize>>(
die: read::EntriesTreeNode<R>,
dwarf: &read::Dwarf<R>,
unit: &read::Unit<R>,
at: &AddressTransform,
deps: &mut Dependencies,
) -> read::Result<()> {
let entry = die.entry();
let offset = entry.offset().to_unit_section_offset(unit);
let mut attrs = entry.attrs();
while let Some(attr) = attrs.next()? {
build_attr_dependencies(&attr, offset, dwarf, unit, at, deps)?;
}

let mut children = die.children();
while let Some(child) = children.next()? {
let child_entry = child.entry();
let child_offset = child_entry.offset().to_unit_section_offset(unit);
deps.add_edge(child_offset, offset);
if has_die_back_edge(child_entry) {
deps.add_edge(offset, child_offset);
}
if has_valid_code_range(child_entry, dwarf, unit, at)? {
deps.add_root(child_offset);
}
build_die_dependencies(child, dwarf, unit, at, deps)?;
}
Ok(())
}

fn build_attr_dependencies<R: Reader<Offset = usize>>(
attr: &read::Attribute<R>,
offset: UnitSectionOffset,
_dwarf: &read::Dwarf<R>,
unit: &read::Unit<R>,
_at: &AddressTransform,
deps: &mut Dependencies,
) -> read::Result<()> {
match attr.value() {
read::AttributeValue::UnitRef(val) => {
let ref_offset = val.to_unit_section_offset(unit);
deps.add_edge(offset, ref_offset);
}
read::AttributeValue::DebugInfoRef(val) => {
let ref_offset = UnitSectionOffset::DebugInfoOffset(val);
deps.add_edge(offset, ref_offset);
}
_ => (),
}
Ok(())
}