From 800b2a42cc749c1ea1625ab7c890893e6e3fd87c Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 31 Jan 2020 12:42:09 -0800 Subject: [PATCH 01/26] Add debug prototype 2 (fork of wasmtime-debug) --- Cargo.lock | 135 +++- Cargo.toml | 4 +- lib/clif-backend/src/module.rs | 1 + lib/debug-writer/Cargo.toml | 16 + lib/debug-writer/README.md | 63 ++ lib/debug-writer/src/gc.rs | 250 +++++++ lib/debug-writer/src/lib.rs | 338 +++++++++ lib/debug-writer/src/read_debug_info.rs | 265 +++++++ .../src/transform/address_transform.rs | 676 ++++++++++++++++++ lib/debug-writer/src/transform/attr.rs | 308 ++++++++ lib/debug-writer/src/transform/expression.rs | 515 +++++++++++++ .../src/transform/line_program.rs | 248 +++++++ lib/debug-writer/src/transform/mod.rs | 133 ++++ .../src/transform/range_info_builder.rs | 237 ++++++ lib/debug-writer/src/transform/simulate.rs | 392 ++++++++++ lib/debug-writer/src/transform/unit.rs | 387 ++++++++++ lib/debug-writer/src/transform/utils.rs | 172 +++++ lib/debug-writer/src/write_debug_info.rs | 163 +++++ lib/runtime-core/src/codegen.rs | 1 + lib/runtime-core/src/module.rs | 6 + 20 files changed, 4296 insertions(+), 14 deletions(-) create mode 100644 lib/debug-writer/Cargo.toml create mode 100644 lib/debug-writer/README.md create mode 100644 lib/debug-writer/src/gc.rs create mode 100644 lib/debug-writer/src/lib.rs create mode 100644 lib/debug-writer/src/read_debug_info.rs create mode 100644 lib/debug-writer/src/transform/address_transform.rs create mode 100644 lib/debug-writer/src/transform/attr.rs create mode 100644 lib/debug-writer/src/transform/expression.rs create mode 100644 lib/debug-writer/src/transform/line_program.rs create mode 100644 lib/debug-writer/src/transform/mod.rs create mode 100644 lib/debug-writer/src/transform/range_info_builder.rs create mode 100644 lib/debug-writer/src/transform/simulate.rs create mode 100644 lib/debug-writer/src/transform/unit.rs create mode 100644 lib/debug-writer/src/transform/utils.rs create mode 100644 lib/debug-writer/src/write_debug_info.rs diff --git a/Cargo.lock b/Cargo.lock index 363d4597173..f4001ad09cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anyhow" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c" + [[package]] name = "arrayref" version = "0.3.5" @@ -228,8 +234,8 @@ dependencies = [ "cranelift-codegen-shared", "cranelift-entity", "log", - "smallvec", - "target-lexicon", + "smallvec 1.1.0", + "target-lexicon 0.9.0", "thiserror", ] @@ -263,7 +269,7 @@ checksum = "21398a0bc6ba389ea86964ac4a495426dd61080f2ddd306184777a8560fe9976" dependencies = [ "cranelift-codegen", "raw-cpuid", - "target-lexicon", + "target-lexicon 0.9.0", ] [[package]] @@ -450,6 +456,28 @@ dependencies = [ "libc", ] +[[package]] +name = "faerie" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74b9ed6159e4a6212c61d9c6a86bee01876b192a64accecf58d5b5ae3b667b52" +dependencies = [ + "anyhow", + "goblin 0.1.3", + "indexmap", + "log", + "scroll 0.10.1", + "string-interner", + "target-lexicon 0.10.0", + "thiserror", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fern" version = "0.5.9" @@ -514,6 +542,20 @@ dependencies = [ "syn 1.0.11", ] +[[package]] +name = "gimli" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dd6190aad0f05ddbbf3245c54ed14ca4aa6dd32f22312b70d8f168c3e3e633" +dependencies = [ + "arrayvec", + "byteorder", + "fallible-iterator", + "indexmap", + "smallvec 1.1.0", + "stable_deref_trait", +] + [[package]] name = "glob" version = "0.2.11" @@ -534,7 +576,18 @@ checksum = "e3fa261d919c1ae9d1e4533c4a2f99e10938603c4208d56c05bec7a872b661b0" dependencies = [ "log", "plain", - "scroll", + "scroll 0.9.2", +] + +[[package]] +name = "goblin" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3081214398d39e4bd7f2c1975f0488ed04614ffdd976c6fc7a0708278552c0da" +dependencies = [ + "log", + "plain", + "scroll 0.10.1", ] [[package]] @@ -870,7 +923,7 @@ dependencies = [ "cloudabi", "libc", "redox_syscall", - "smallvec", + "smallvec 1.1.0", "winapi", ] @@ -1255,7 +1308,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383" dependencies = [ "rustc_version", - "scroll_derive", + "scroll_derive 0.9.5", +] + +[[package]] +name = "scroll" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1" +dependencies = [ + "scroll_derive 0.10.1", ] [[package]] @@ -1269,6 +1331,17 @@ dependencies = [ "syn 0.15.44", ] +[[package]] +name = "scroll_derive" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28" +dependencies = [ + "proc-macro2 1.0.6", + "quote 1.0.2", + "syn 1.0.11", +] + [[package]] name = "sdl2" version = "0.32.2" @@ -1358,6 +1431,15 @@ dependencies = [ "serde", ] +[[package]] +name = "smallvec" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" +dependencies = [ + "maybe-uninit", +] + [[package]] name = "smallvec" version = "1.1.0" @@ -1370,6 +1452,15 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +[[package]] +name = "string-interner" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd710eadff449a1531351b0e43eb81ea404336fa2f56c777427ab0e32a4cf183" +dependencies = [ + "serde", +] + [[package]] name = "strsim" version = "0.8.0" @@ -1439,6 +1530,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f4c118a7a38378f305a9e111fcb2f7f838c0be324bfb31a77ea04f7f6e684b4" +[[package]] +name = "target-lexicon" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d" + [[package]] name = "tempfile" version = "3.1.0" @@ -1643,6 +1740,7 @@ dependencies = [ "typetag", "wabt", "wasmer-clif-backend", + "wasmer-debug-writer", "wasmer-dev-utils", "wasmer-emscripten", "wasmer-emscripten-tests", @@ -1673,7 +1771,7 @@ dependencies = [ "serde-bench", "serde_bytes", "serde_derive", - "target-lexicon", + "target-lexicon 0.9.0", "wasmer-clif-fork-frontend", "wasmer-clif-fork-wasm", "wasmer-runtime-core", @@ -1690,8 +1788,8 @@ checksum = "6d2e13201ef9ef527ad30a6bf1b08e3e024a40cf2731f393d80375dc88506207" dependencies = [ "cranelift-codegen", "log", - "smallvec", - "target-lexicon", + "smallvec 1.1.0", + "target-lexicon 0.9.0", ] [[package]] @@ -1708,6 +1806,17 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "wasmer-debug-writer" +version = "0.13.1" +dependencies = [ + "faerie", + "gimli", + "target-lexicon 0.10.0", + "wasmer-runtime-core", + "wasmparser", +] + [[package]] name = "wasmer-dev-utils" version = "0.13.1" @@ -1756,7 +1865,7 @@ version = "0.13.1" dependencies = [ "byteorder", "cc", - "goblin", + "goblin 0.0.24", "inkwell", "lazy_static", "libc", @@ -1764,7 +1873,7 @@ dependencies = [ "regex", "rustc_version", "semver", - "smallvec", + "smallvec 0.6.13", "wabt", "wasmer-runtime-core", "wasmparser", @@ -1851,7 +1960,7 @@ dependencies = [ "serde-bench", "serde_bytes", "serde_derive", - "smallvec", + "smallvec 0.6.13", "wasmparser", "winapi", ] @@ -1880,7 +1989,7 @@ dependencies = [ "nix", "serde", "serde_derive", - "smallvec", + "smallvec 0.6.13", "wasmer-runtime-core", ] diff --git a/Cargo.toml b/Cargo.toml index 8b74cd54c35..65260638475 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ wasmer-wasi-tests = { path = "lib/wasi-tests", optional = true } wasmer-middleware-common-tests = { path = "lib/middleware-common-tests", optional = true } wasmer-emscripten-tests = { path = "lib/emscripten-tests", optional = true } wasmer-wasi-experimental-io-devices = { path = "lib/wasi-experimental-io-devices", optional = true } +wasmer-debug-writer = { path = "lib/debug-writer", optional = true } [workspace] members = [ @@ -64,6 +65,7 @@ members = [ "lib/wasi-tests", "lib/emscripten-tests", "lib/middleware-common-tests", + "lib/debug-writer", "examples/parallel", "examples/plugin-for-example", "examples/parallel-guest", @@ -79,7 +81,7 @@ serde = { version = "1", features = ["derive"] } # used by the plugin example typetag = "0.1" # used by the plugin example [features] -default = ["fast-tests", "wasi", "backend-cranelift", "wabt"] +default = ["fast-tests", "wasi", "backend-cranelift", "wabt", "wasmer-debug-writer"] "loader-kernel" = ["wasmer-kernel-loader"] debug = ["fern", "log/max_level_debug", "log/release_max_level_debug"] trace = ["fern", "log/max_level_trace", "log/release_max_level_trace"] diff --git a/lib/clif-backend/src/module.rs b/lib/clif-backend/src/module.rs index dd81b693224..99d40f0459f 100644 --- a/lib/clif-backend/src/module.rs +++ b/lib/clif-backend/src/module.rs @@ -45,6 +45,7 @@ impl Module { runnable_module: Arc::new(Box::new(runnable_module)), cache_gen, info, + debug_info: None, }) } } diff --git a/lib/debug-writer/Cargo.toml b/lib/debug-writer/Cargo.toml new file mode 100644 index 00000000000..6f9019617a2 --- /dev/null +++ b/lib/debug-writer/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "wasmer-debug-writer" +version = "0.13.1" +authors = ["The Wasmer Engineering Team "] +edition = "2018" +repository = "https://github.com/wasmerio/wasmer" +publish = false +description = "Library for writing debug information from Wasm" +license = "MIT" + +[dependencies] +faerie = "0.14" +gimli = "0.20" +target-lexicon = "0.10" +wasmer-runtime-core = { path = "../runtime-core", version = "0.13.1" } +wasmparser = "0.45" diff --git a/lib/debug-writer/README.md b/lib/debug-writer/README.md new file mode 100644 index 00000000000..e55264abbe8 --- /dev/null +++ b/lib/debug-writer/README.md @@ -0,0 +1,63 @@ +# Wasmer debug info writer + +This crate deals with passing DWARF debug information along from +compiled Wasm modules to the machine code that we generate. + +This crate is effectively a derivative work of WasmTime's +[`wasmtime-debug`](https://github.com/bytecodealliance/wasmtime/tree/master/crates/debug) +crate. After beginning work on a clean reimplementation we realized +that the WasmTime implementation is high quality and it didn't make +sense for us to duplicate their hard work. + +Additionally by keeping the code structure of `wasmer-debug-writer` +similar to `wasmtime-debug`, we hope to upstream bug fixes and +improvements to `wasmtime-debug`. + +Copied files include the copyright notice as well, but as a catch all, +this crate is a derivative work of WasmTime's `wasmtime-debug` + +``` +Copyright 2020 WasmTime Project Developers + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` + +The latest revision at the time of cloning is `3992b8669f9b9e185abe81e9998ce2ff4d40ff68`. + +Changes to this crate are copyright of Wasmer inc. unless otherwise indicated +and are licensed under the Wasmer project's license: + +``` +MIT License + +Copyright (c) 2020 Wasmer, Inc. and its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` diff --git a/lib/debug-writer/src/gc.rs b/lib/debug-writer/src/gc.rs new file mode 100644 index 00000000000..7d20c9f8f4b --- /dev/null +++ b/lib/debug-writer/src/gc.rs @@ -0,0 +1,250 @@ +// Copyright 2019 WasmTime Project Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is from the WasmTime project. +// It was copied at revision `39e57e3e9ac9c15bef45eb77a2544a7c0b76501a`. +// +// Changes to this file are copyright of Wasmer inc. unless otherwise indicated +// and are licensed under the Wasmer project's license. +use crate::transform::AddressTransform; +use gimli::constants; +use gimli::read; +use gimli::{Reader, UnitSectionOffset}; +use std::collections::{HashMap, HashSet}; + +#[derive(Debug)] +pub struct Dependencies { + edges: HashMap>, + roots: HashSet, +} + +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 { + 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>( + dwarf: &read::Dwarf, + at: &AddressTransform, +) -> read::Result { + 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>( + header: read::CompilationUnitHeader, + dwarf: &read::Dwarf, + 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>(die: &read::DebuggingInformationEntry) -> 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>( + die: &read::DebuggingInformationEntry, + dwarf: &read::Dwarf, + unit: &read::Unit, + at: &AddressTransform, +) -> read::Result { + 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>( + die: read::EntriesTreeNode, + dwarf: &read::Dwarf, + unit: &read::Unit, + 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>( + attr: &read::Attribute, + offset: UnitSectionOffset, + _dwarf: &read::Dwarf, + unit: &read::Unit, + _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(()) +} diff --git a/lib/debug-writer/src/lib.rs b/lib/debug-writer/src/lib.rs new file mode 100644 index 00000000000..25928ff92ef --- /dev/null +++ b/lib/debug-writer/src/lib.rs @@ -0,0 +1,338 @@ +// TODO: add attribution to LLVM for data definitions and WasmTime for code structure +use std::str::FromStr; +use std::ptr; +use std::ffi::c_void; + +mod read_debug_info; +mod write_debug_info; +mod gc; +mod transform; + +pub use crate::read_debug_info::{read_debug_info, DebugInfoData, WasmFileInfo}; +pub use crate::write_debug_info::{emit_dwarf, ResolvedSymbol, SymbolResolver}; +use crate::transform::WasmTypesDieRefs; + +use target_lexicon::{Triple, Architecture, Vendor, OperatingSystem, Environment, BinaryFormat}; +use gimli::write::{self, DwarfUnit, Sections, Address, RangeList, EndianVec, AttributeValue, Range}; + +use wasmer_runtime_core::{module::ModuleInfo, state::CodeVersion}; + +/// Triple of x86_64 GNU/Linux +const X86_64_GNU_LINUX: Triple = Triple { + architecture: Architecture::X86_64, + vendor: Vendor::Unknown, + operating_system: OperatingSystem::Linux, + environment: Environment::Gnu, + binary_format: BinaryFormat::Elf, +}; + +/// Triple of x86_64 OSX +const X86_64_OSX: Triple = Triple { + architecture: Architecture::X86_64, + vendor: Vendor::Apple, + operating_system: OperatingSystem::Darwin, + environment: Environment::Unknown, + binary_format: BinaryFormat::Macho, +}; + +/// Triple of x86_64 Windows +const X86_64_WINDOWS: Triple = Triple { + architecture: Architecture::X86_64, + vendor: Vendor::Pc, + operating_system: OperatingSystem::Windows, + environment: Environment::Msvc, + binary_format: BinaryFormat::Coff, +}; + +// this code also from WasmTime +// TODO: attribute +struct ImageRelocResolver<'a> { + func_offsets: &'a Vec, +} + +// this code also from WasmTime +// TODO: attribute +impl<'a> SymbolResolver for ImageRelocResolver<'a> { + fn resolve_symbol(&self, symbol: usize, addend: i64) -> ResolvedSymbol { + let func_start = self.func_offsets[symbol]; + ResolvedSymbol::PhysicalAddress(func_start + addend as u64) + } +} + +// the structure of this function and some of its details come from WasmTime +// TODO: attribute +pub fn generate_dwarf(module_info: &ModuleInfo, debug_info_data: &DebugInfoData, code_version: &CodeVersion, platform: Triple) -> Result, String> { + let func_offsets = unimplemented!(); + let resolver = ImageRelocResolver { func_offsets }; + // copied from https://docs.rs/gimli/0.20.0/gimli/write/index.html ; TODO: review these values + let processed_dwarf = reprocess_dwarf(module_info, debug_info_data, code_version, platform).ok_or_else(|| "Failed to reprocess Wasm's dwarf".to_string())?; + let encoding = gimli::Encoding { + format: gimli::Format::Dwarf32, + version: 3, + address_size: 8, + }; + let mut dwarf = DwarfUnit::new(encoding); + // TODO: figure out what range is (from example) + let range_list = RangeList(vec![Range::StartLength { + begin: Address::Constant(0x100), + length: 42, + }]); + let range_list_id = dwarf.unit.ranges.add(range_list); + let root = dwarf.unit.root(); + dwarf.unit.get_mut(root).set( + gimli::DW_AT_ranges, + AttributeValue::RangeListRef(range_list_id), + ); + let mut string_table = write::StringTable::default(); + let mut line_string_table = write::LineStringTable::default(); + + let mut obj = faerie::Artifact::new(platform, String::from("module")); + + let mut sections = Sections::new(EndianVec::new(gimli::LittleEndian)); + // Finally, write the DWARF data to the sections. + dwarf.write(&mut sections).map_err(|e| e.to_string())?; + emit_dwarf(&mut obj, dwarf, &resolver); + sections.for_each(|id, data| { + // Here you can add the data to the output object file. + Ok(()) + }); + + obj.emit_as(BinaryFormat::Elf).expect("TODO"); + // We want to populate DwarfUnit::line_str_table with WAT probably + // and set up the string table with things like function signatures in WAT, function names, etc + + // NOTES from DWARF spec: + // http://dwarfstd.org/doc/DWARF5.pdf + // - `DIE`s form the core of dwarf and live in .debug_info + // - the tags can get fairly specific, it looks like we'll just need a mapping + // from object code to a bunch of tags and ranges? created with the Wasm + // data for extra info about types, etc. + // - debug info can live in a separate object file (that's what we'll do here) + // - attribute types are unique per DIE (lots of info here (like is tail call, + // return addr, etc.) + // - DW_AT_language: WebAssembly :bonjour: + // - `DW_AT_linkage_name` function namespaces? (later described as the raw, mangled name) + // `DW_AT_name` function name? + // - `DW_AT_location` where in the code it is + // - `DW_AT_main_subprogram` where to start from + // - `DW_AT_producer`: wasmer + // - `DW_AT_recursive` -- is this mandatory? what is it used for? TODO: find out + // - `DW_AT_signature` -- can we use wasm type signature info here? TODO: + // - `DIE`s form a graph/tree though a tree-like graph when it is a graph, docs say + // this is how structs and relationship of code blocks is represented. + // - when serialized the tree is in post-fix order (probably not important for our + // purposes but mildly interesting) + // - we'll need pointer sizer and platform information + // - dwarf executes a typed stack-machine to compute the locations of things + // - lots of neat info about the dwarf stack machine skipping for now because I + // think gimli exposes a higher-level interface (if not, I'll add notes here + // or further down about it) + // - can use dwarf expressions/dynamically computing things to handle things like + // a tiering JIT? + // - location lists are needed for things that aren't lexically scoped, otherwise + // single location descriptions (dwarf expressions) are sufficient + // - I wonder what this means in the context of spilling registers... do we have + // to create dwarf expressions that can handle that? + // - `DW_AT_artificial` is used to tag `DIE` that didn't come directly from the code + // - `DW_AT_declaration` for function/etc declarations at the top of the wasm module, + // see section 2.13.2 for how to connect the definiton and the declaration + // - `DW_AT_decl_line`, `DW_AT_decl_column` refer to the exact location in the source + // file, so presumably we include the entire source file in one of the sections? + // or perhaps that's purely for human consumption. + // - `DW_AT_ranges` is for non-contiguous ranges of address and, + // `DW_AT_low_pc` and `DW_AT_high_pc` are good for continuous + // `DW_AT_low_pc` alone can work for a single address, but we can probably not + // worry about that for now. These attribtues associate machine code with the DIE + // - + + match platform { + X86_64_GNU_LINUX => unimplemented!("in progress"), + X86_64_OSX => unimplemented!("in progress"), + X86_64_WINDOWS => unimplemented!("in progress"), + _ => return Err(format!("Debug output for the platform {} is not yet supported", platform)), + } + Ok(vec![]) +} + +// converts existing dwarf into a usable form with metadata from the JIT +fn reprocess_dwarf(module_info: &ModuleInfo, debug_info_data: &DebugInfoData, code_version: &CodeVersion, platform: Triple) -> Option { + None +} + +// black box, needs some kind of input, some kind of processing +// and returns a bunch of bytes we can give to GDB +// +// where is this documented? +// we need to pass in target triple, isa config, memories/pointers to memories, ranges of where things are, +// and info like function names +pub fn generate_debug_sections_image() -> Option> { + None +} + +// do it + +// this code copied from WasmTime, TODO: give attribution + + +// The `emit_wasm_types` function is a derative work of code in WasmTime: +// TODO: update attributions file and/or do clean reimplementation of this logic +// +// Copyright 2019 WasmTime Project Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +fn emit_wasm_types(unit: &mut write::Unit, root_id: write::UnitEntryId, string_table: &mut write::StringTable) -> WasmTypesDieRefs { + macro_rules! def_type { + ($id:literal, $size:literal, $enc:path) => {{ + let die_id = unit.add(root_id, gimli::DW_TAG_base_type); + let die = unit.get_mut(die_id); + die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(string_table.add($id)), + ); + die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1($size)); + die.set(gimli::DW_AT_encoding, write::AttributeValue::Encoding($enc)); + die_id + }}; + } + let vmctx_id = { + // TODO: get memory_offset + let memory_offset = 0; + let vmctx_die_id = unit.add(root_id, gimli::DW_TAG_structure_type); + let vmctx_die = unit.get_mut(vmctx_die_id); + vmctx_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(string_table.add("WasmerVMContext")), + ); + vmctx_die.set( + gimli::DW_AT_byte_size, + write::AttributeValue::Data4(memory_offset as u32 + 8), + ); + let vmctx_ptr_id = unit.add(root_id, gimli::DW_TAG_pointer_type); + let vmctx_ptr_die = unit.get_mut(vmctx_ptr_id); + vmctx_ptr_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(string_table.add("WasmerVMContext*")), + ); + vmctx_ptr_die.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(vmctx_die_id), + ); + + vmctx_ptr_id + }; + + let i32_id = def_type!("i32", 4, gimli::DW_ATE_signed); + let i64_id = def_type!("i64", 8, gimli::DW_ATE_signed); + let i128_id = def_type!("i128", 16, gimli::DW_ATE_signed); + let f32_id = def_type!("f32", 4, gimli::DW_ATE_float); + let f64_id = def_type!("f64", 8, gimli::DW_ATE_float); + + WasmTypesDieRefs { + vmctx: vmctx_id, + i32: i32_id, + i64: i64_id, + i128: i128_id, + f32: f32_id, + f64: f64_id, + } +} + + +// ============================================================================= +// LLDB hook magic: +// see lldb/packages/Python/lldbsuite/test/functionalities/jitloader_gdb in +// llvm repo for example +// +// see also https://sourceware.org/gdb/current/onlinedocs/gdb.html#JIT-Interface + +#[inline(never)] +pub extern "C" fn __jit_debug_register_code() { + +} + +#[allow(non_camel_case_types)] +#[derive(Debug)] +#[repr(u32)] +pub enum JITAction { JIT_NOACTION = 0, JIT_REGISTER_FN = 1, JIT_UNREGISTER_FN = 2 } + +#[no_mangle] +#[repr(C)] +pub struct JITCodeEntry { + next: *mut JITCodeEntry, + prev: *mut JITCodeEntry, + // TODO: use CStr here? + symfile_addr: *const u8, + symfile_size: u64, +} + +impl Default for JITCodeEntry { + fn default() -> Self { + Self { + next: ptr::null_mut(), + prev: ptr::null_mut(), + symfile_addr: ptr::null(), + symfile_size: 0, + } + } +} + +#[no_mangle] +#[repr(C)] +pub struct JitDebugDescriptor { + version: u32, + action_flag: u32, + relevant_entry: *mut JITCodeEntry, + first_entry: *mut JITCodeEntry, +} + +#[no_mangle] +#[allow(non_upper_case_globals)] +pub static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { + version: 1, + action_flag: JITAction::JIT_NOACTION as _, + relevant_entry: ptr::null_mut(), + first_entry: ptr::null_mut(), +}; + +/// Prepend an item to the front of the `__jit_debug_descriptor` entry list +/// +/// # Safety +/// - Pointer to [`JITCodeEntry`] should point to a valid entry and stay alive +/// for the 'static lifetime +unsafe fn push_front(jce: *mut JITCodeEntry) { + if __jit_debug_descriptor.first_entry.is_null() { + __jit_debug_descriptor.first_entry = jce; + } else { + let old_first = __jit_debug_descriptor.first_entry; + debug_assert!((*old_first).prev.is_null()); + (*jce).next = old_first; + (*old_first).prev = jce; + __jit_debug_descriptor.first_entry = jce; + } +} + +pub fn register_new_jit_code_entry(bytes: &'static [u8], action: JITAction) -> *mut JITCodeEntry { + let entry: *mut JITCodeEntry = Box::into_raw(Box::new(JITCodeEntry { + symfile_addr: bytes.as_ptr(), + symfile_size: bytes.len() as _, + ..JITCodeEntry::default() + })); + + unsafe { + push_front(entry); + __jit_debug_descriptor.relevant_entry = entry; + __jit_debug_descriptor.action_flag = action as u32; + } + + entry +} diff --git a/lib/debug-writer/src/read_debug_info.rs b/lib/debug-writer/src/read_debug_info.rs new file mode 100644 index 00000000000..595a3430d0f --- /dev/null +++ b/lib/debug-writer/src/read_debug_info.rs @@ -0,0 +1,265 @@ +// Copyright 2019 WasmTime Project Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is from the WasmTime project. It reads DWARF info from a Wasm module. +// It was copied at revision `39e57e3e9ac9c15bef45eb77a2544a7c0b76501a`. +// +// Changes to this file are copyright of Wasmer inc. unless otherwise indicated +// and are licensed under the Wasmer project's license. + +use gimli::{ + DebugAbbrev, DebugAddr, DebugInfo, DebugLine, DebugLineStr, DebugLoc, DebugLocLists, + DebugRanges, DebugRngLists, DebugStr, DebugStrOffsets, DebugTypes, EndianSlice, LittleEndian, + LocationLists, RangeLists, +}; +use std::collections::HashMap; +use std::path::PathBuf; +use wasmparser::{self, ModuleReader, SectionCode}; + +trait Reader: gimli::Reader {} + +impl<'input> Reader for gimli::EndianSlice<'input, LittleEndian> {} + +pub use wasmparser::Type as WasmType; + +pub type Dwarf<'input> = gimli::Dwarf>; + +#[derive(Debug)] +pub struct FunctionMetadata { + pub params: Box<[WasmType]>, + pub locals: Box<[(u32, WasmType)]>, +} + +#[derive(Debug)] +pub struct WasmFileInfo { + pub path: Option, + pub code_section_offset: u64, + pub funcs: Box<[FunctionMetadata]>, +} + +#[derive(Debug)] +pub struct NameSection { + pub module_name: Option, + pub func_names: HashMap, + pub locals_names: HashMap>, +} + +#[derive(Debug)] +pub struct DebugInfoData<'a> { + pub dwarf: Dwarf<'a>, + pub name_section: Option, + pub wasm_file: WasmFileInfo, +} + +fn convert_sections<'a>(sections: HashMap<&str, &'a [u8]>) -> Dwarf<'a> { + const EMPTY_SECTION: &[u8] = &[]; + + let endian = LittleEndian; + let debug_str = DebugStr::new(sections.get(".debug_str").unwrap_or(&EMPTY_SECTION), endian); + let debug_abbrev = DebugAbbrev::new( + sections.get(".debug_abbrev").unwrap_or(&EMPTY_SECTION), + endian, + ); + let debug_info = DebugInfo::new( + sections.get(".debug_info").unwrap_or(&EMPTY_SECTION), + endian, + ); + let debug_line = DebugLine::new( + sections.get(".debug_line").unwrap_or(&EMPTY_SECTION), + endian, + ); + + if sections.contains_key(".debug_addr") { + panic!("Unexpected .debug_addr"); + } + + let debug_addr = DebugAddr::from(EndianSlice::new(EMPTY_SECTION, endian)); + + if sections.contains_key(".debug_line_str") { + panic!("Unexpected .debug_line_str"); + } + + let debug_line_str = DebugLineStr::from(EndianSlice::new(EMPTY_SECTION, endian)); + let debug_str_sup = DebugStr::from(EndianSlice::new(EMPTY_SECTION, endian)); + + if sections.contains_key(".debug_rnglists") { + panic!("Unexpected .debug_rnglists"); + } + + let debug_ranges = match sections.get(".debug_ranges") { + Some(section) => DebugRanges::new(section, endian), + None => DebugRanges::new(EMPTY_SECTION, endian), + }; + let debug_rnglists = DebugRngLists::new(EMPTY_SECTION, endian); + let ranges = RangeLists::new(debug_ranges, debug_rnglists); + + if sections.contains_key(".debug_loclists") { + panic!("Unexpected .debug_loclists"); + } + + let debug_loc = match sections.get(".debug_loc") { + Some(section) => DebugLoc::new(section, endian), + None => DebugLoc::new(EMPTY_SECTION, endian), + }; + let debug_loclists = DebugLocLists::new(EMPTY_SECTION, endian); + let locations = LocationLists::new(debug_loc, debug_loclists); + + if sections.contains_key(".debug_str_offsets") { + panic!("Unexpected .debug_str_offsets"); + } + + let debug_str_offsets = DebugStrOffsets::from(EndianSlice::new(EMPTY_SECTION, endian)); + + if sections.contains_key(".debug_types") { + panic!("Unexpected .debug_types"); + } + + let debug_types = DebugTypes::from(EndianSlice::new(EMPTY_SECTION, endian)); + + Dwarf { + debug_abbrev, + debug_addr, + debug_info, + debug_line, + debug_line_str, + debug_str, + debug_str_offsets, + debug_str_sup, + debug_types, + locations, + ranges, + } +} + +fn read_name_section(reader: wasmparser::NameSectionReader) -> wasmparser::Result { + let mut module_name = None; + let mut func_names = HashMap::new(); + let mut locals_names = HashMap::new(); + for i in reader.into_iter() { + match i? { + wasmparser::Name::Module(m) => { + module_name = Some(String::from(m.get_name()?)); + } + wasmparser::Name::Function(f) => { + let mut reader = f.get_map()?; + while let Ok(naming) = reader.read() { + func_names.insert(naming.index, String::from(naming.name)); + } + } + wasmparser::Name::Local(l) => { + let mut reader = l.get_function_local_reader()?; + while let Ok(f) = reader.read() { + let mut names = HashMap::new(); + let mut reader = f.get_map()?; + while let Ok(naming) = reader.read() { + names.insert(naming.index, String::from(naming.name)); + } + locals_names.insert(f.func_index, names); + } + } + } + } + let result = NameSection { + module_name, + func_names, + locals_names, + }; + Ok(result) +} + +pub fn read_debug_info(data: &[u8]) -> DebugInfoData { + let mut reader = ModuleReader::new(data).expect("reader"); + let mut sections = HashMap::new(); + let mut name_section = None; + let mut code_section_offset = 0; + + let mut signatures_params: Vec> = Vec::new(); + let mut func_params_refs: Vec = Vec::new(); + let mut func_locals: Vec> = Vec::new(); + + while !reader.eof() { + let section = reader.read().expect("section"); + match section.code { + SectionCode::Custom { name, .. } => { + if name.starts_with(".debug_") { + let mut reader = section.get_binary_reader(); + let len = reader.bytes_remaining(); + sections.insert(name, reader.read_bytes(len).expect("bytes")); + } + if name == "name" { + if let Ok(reader) = section.get_name_section_reader() { + if let Ok(section) = read_name_section(reader) { + name_section = Some(section); + } + } + } + } + SectionCode::Type => { + signatures_params = section + .get_type_section_reader() + .expect("type section") + .into_iter() + .map(|ft| ft.expect("type").params) + .collect::>(); + } + SectionCode::Function => { + func_params_refs = section + .get_function_section_reader() + .expect("function section") + .into_iter() + .map(|index| index.expect("func index") as usize) + .collect::>(); + } + SectionCode::Code => { + code_section_offset = section.range().start as u64; + func_locals = section + .get_code_section_reader() + .expect("code section") + .into_iter() + .map(|body| { + let locals = body + .expect("body") + .get_locals_reader() + .expect("locals reader"); + locals + .into_iter() + .collect::, _>>() + .expect("locals data") + .into_boxed_slice() + }) + .collect::>(); + } + _ => (), + } + } + + let func_meta = func_params_refs + .into_iter() + .zip(func_locals.into_iter()) + .map(|(params_index, locals)| FunctionMetadata { + params: signatures_params[params_index].clone(), + locals, + }) + .collect::>(); + + DebugInfoData { + dwarf: convert_sections(sections), + name_section, + wasm_file: WasmFileInfo { + path: None, + code_section_offset, + funcs: func_meta.into_boxed_slice(), + }, + } +} diff --git a/lib/debug-writer/src/transform/address_transform.rs b/lib/debug-writer/src/transform/address_transform.rs new file mode 100644 index 00000000000..876e2a6c7dd --- /dev/null +++ b/lib/debug-writer/src/transform/address_transform.rs @@ -0,0 +1,676 @@ +// Copyright 2019 WasmTime Project Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is from the WasmTime project. +// It was copied at revision `907e7aac01af333a0af310ce0472abbc8a9adb6c`. +// +// Changes to this file are copyright of Wasmer inc. unless otherwise indicated +// and are licensed under the Wasmer project's license. +use crate::WasmFileInfo; +use cranelift_codegen::ir::SourceLoc; +use cranelift_entity::{EntityRef, PrimaryMap}; +use gimli::write; +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::iter::FromIterator; +use wasmer_runtime_core::types::FuncIndex; +use wasmtime_environ::{FunctionAddressMap, ModuleAddressMap}; + +pub type GeneratedAddress = usize; +pub type WasmAddress = u64; + +/// Contains mapping of the generated address to its original +/// source location. +#[derive(Debug)] +pub struct AddressMap { + pub generated: GeneratedAddress, + pub wasm: WasmAddress, +} + +/// Information about generated function code: its body start, +/// length, and instructions addresses. +#[derive(Debug)] +pub struct FunctionMap { + pub offset: GeneratedAddress, + pub len: GeneratedAddress, + pub wasm_start: WasmAddress, + pub wasm_end: WasmAddress, + pub addresses: Box<[AddressMap]>, +} + +/// Mapping of the source location to its generated code range. +#[derive(Debug)] +struct Position { + wasm_pos: WasmAddress, + gen_start: GeneratedAddress, + gen_end: GeneratedAddress, +} + +/// Mapping of continuous range of source location to its generated +/// code. The positions are always in accending order for search. +#[derive(Debug)] +struct Range { + wasm_start: WasmAddress, + wasm_end: WasmAddress, + gen_start: GeneratedAddress, + gen_end: GeneratedAddress, + positions: Box<[Position]>, +} + +/// Helper function address lookup data. Contains ranges start positions +/// index and ranges data. The multiple ranges can include the same +/// original source position. The index (B-Tree) uses range start +/// position as a key. +#[derive(Debug)] +struct FuncLookup { + index: Vec<(WasmAddress, Box<[usize]>)>, + ranges: Box<[Range]>, +} + +/// Mapping of original functions to generated code locations/ranges. +#[derive(Debug)] +struct FuncTransform { + start: WasmAddress, + end: WasmAddress, + index: FuncIndex, + lookup: FuncLookup, +} + +/// Module functions mapping to generated code. +#[derive(Debug)] +pub struct AddressTransform { + map: PrimaryMap, + func: Vec<(WasmAddress, FuncTransform)>, +} + +/// Returns a wasm bytecode offset in the code section from SourceLoc. +pub fn get_wasm_code_offset(loc: SourceLoc, code_section_offset: u64) -> WasmAddress { + // Code section size <= 4GB, allow wrapped SourceLoc to recover the overflow. + loc.bits().wrapping_sub(code_section_offset as u32) as WasmAddress +} + +fn build_function_lookup( + ft: &FunctionAddressMap, + code_section_offset: u64, +) -> (WasmAddress, WasmAddress, FuncLookup) { + assert!(code_section_offset <= ft.start_srcloc.bits() as u64); + let fn_start = get_wasm_code_offset(ft.start_srcloc, code_section_offset); + let fn_end = get_wasm_code_offset(ft.end_srcloc, code_section_offset); + assert!(fn_start <= fn_end); + + // Build ranges of continuous source locations. The new ranges starts when + // non-descending order is interrupted. Assuming the same origin location can + // be present in multiple ranges. + let mut range_wasm_start = fn_start; + let mut range_gen_start = ft.body_offset; + let mut last_wasm_pos = range_wasm_start; + let mut ranges = Vec::new(); + let mut ranges_index = BTreeMap::new(); + let mut current_range = Vec::new(); + for t in &ft.instructions { + if t.srcloc.is_default() { + continue; + } + + let offset = get_wasm_code_offset(t.srcloc, code_section_offset); + assert!(fn_start <= offset); + assert!(offset <= fn_end); + + let inst_gen_start = t.code_offset; + let inst_gen_end = t.code_offset + t.code_len; + + if last_wasm_pos > offset { + // Start new range. + ranges_index.insert(range_wasm_start, ranges.len()); + ranges.push(Range { + wasm_start: range_wasm_start, + wasm_end: last_wasm_pos, + gen_start: range_gen_start, + gen_end: inst_gen_start, + positions: current_range.into_boxed_slice(), + }); + range_wasm_start = offset; + range_gen_start = inst_gen_start; + current_range = Vec::new(); + } + // Continue existing range: add new wasm->generated code position. + current_range.push(Position { + wasm_pos: offset, + gen_start: inst_gen_start, + gen_end: inst_gen_end, + }); + last_wasm_pos = offset; + } + let last_gen_addr = ft.body_offset + ft.body_len; + ranges_index.insert(range_wasm_start, ranges.len()); + ranges.push(Range { + wasm_start: range_wasm_start, + wasm_end: fn_end, + gen_start: range_gen_start, + gen_end: last_gen_addr, + positions: current_range.into_boxed_slice(), + }); + + // Making ranges lookup faster by building index: B-tree with every range + // start position that maps into list of active ranges at this position. + let ranges = ranges.into_boxed_slice(); + let mut active_ranges = Vec::new(); + let mut index = BTreeMap::new(); + let mut last_wasm_pos = None; + for (wasm_start, range_index) in ranges_index { + if Some(wasm_start) == last_wasm_pos { + active_ranges.push(range_index); + continue; + } + if last_wasm_pos.is_some() { + index.insert( + last_wasm_pos.unwrap(), + active_ranges.clone().into_boxed_slice(), + ); + } + active_ranges.retain(|r| ranges[*r].wasm_end.cmp(&wasm_start) != std::cmp::Ordering::Less); + active_ranges.push(range_index); + last_wasm_pos = Some(wasm_start); + } + index.insert(last_wasm_pos.unwrap(), active_ranges.into_boxed_slice()); + let index = Vec::from_iter(index.into_iter()); + (fn_start, fn_end, FuncLookup { index, ranges }) +} + +fn build_function_addr_map( + at: &ModuleAddressMap, + code_section_offset: u64, +) -> PrimaryMap { + let mut map = PrimaryMap::new(); + for (_, ft) in at { + let mut fn_map = Vec::new(); + for t in &ft.instructions { + if t.srcloc.is_default() { + continue; + } + let offset = get_wasm_code_offset(t.srcloc, code_section_offset); + fn_map.push(AddressMap { + generated: t.code_offset, + wasm: offset, + }); + } + + if cfg!(debug) { + // fn_map is sorted by the generated field -- see FunctionAddressMap::instructions. + for i in 1..fn_map.len() { + assert!(fn_map[i - 1].generated <= fn_map[i].generated); + } + } + + map.push(FunctionMap { + offset: ft.body_offset, + len: ft.body_len, + wasm_start: get_wasm_code_offset(ft.start_srcloc, code_section_offset), + wasm_end: get_wasm_code_offset(ft.end_srcloc, code_section_offset), + addresses: fn_map.into_boxed_slice(), + }); + } + map +} + +struct TransformRangeIter<'a> { + addr: u64, + indicies: &'a [usize], + ranges: &'a [Range], +} + +impl<'a> TransformRangeIter<'a> { + fn new(func: &'a FuncTransform, addr: u64) -> Self { + let found = match func + .lookup + .index + .binary_search_by(|entry| entry.0.cmp(&addr)) + { + Ok(i) => Some(&func.lookup.index[i].1), + Err(i) => { + if i > 0 { + Some(&func.lookup.index[i - 1].1) + } else { + None + } + } + }; + if let Some(range_indices) = found { + TransformRangeIter { + addr, + indicies: range_indices, + ranges: &func.lookup.ranges, + } + } else { + unreachable!(); + } + } +} +impl<'a> Iterator for TransformRangeIter<'a> { + type Item = (usize, usize); + fn next(&mut self) -> Option { + if let Some((first, tail)) = self.indicies.split_first() { + let range_index = *first; + let range = &self.ranges[range_index]; + self.indicies = tail; + let address = match range + .positions + .binary_search_by(|a| a.wasm_pos.cmp(&self.addr)) + { + Ok(i) => range.positions[i].gen_start, + Err(i) => { + if i == 0 { + range.gen_start + } else { + range.positions[i - 1].gen_end + } + } + }; + Some((address, range_index)) + } else { + None + } + } +} + +struct TransformRangeEndIter<'a> { + addr: u64, + indicies: &'a [usize], + ranges: &'a [Range], +} + +impl<'a> TransformRangeEndIter<'a> { + fn new(func: &'a FuncTransform, addr: u64) -> Self { + let found = match func + .lookup + .index + .binary_search_by(|entry| entry.0.cmp(&addr)) + { + Ok(i) => Some(&func.lookup.index[i].1), + Err(i) => { + if i > 0 { + Some(&func.lookup.index[i - 1].1) + } else { + None + } + } + }; + if let Some(range_indices) = found { + TransformRangeEndIter { + addr, + indicies: range_indices, + ranges: &func.lookup.ranges, + } + } else { + unreachable!(); + } + } +} + +impl<'a> Iterator for TransformRangeEndIter<'a> { + type Item = (usize, usize); + fn next(&mut self) -> Option { + while let Some((first, tail)) = self.indicies.split_first() { + let range_index = *first; + let range = &self.ranges[range_index]; + if range.wasm_start >= self.addr { + continue; + } + self.indicies = tail; + let address = match range + .positions + .binary_search_by(|a| a.wasm_pos.cmp(&self.addr)) + { + Ok(i) => range.positions[i].gen_end, + Err(i) => { + if i == range.positions.len() { + range.gen_end + } else { + range.positions[i].gen_start + } + } + }; + return Some((address, range_index)); + } + None + } +} + +impl AddressTransform { + pub fn new(at: &ModuleAddressMap, wasm_file: &WasmFileInfo) -> Self { + let code_section_offset = wasm_file.code_section_offset; + + let mut func = BTreeMap::new(); + for (i, ft) in at { + let (fn_start, fn_end, lookup) = build_function_lookup(ft, code_section_offset); + + func.insert( + fn_start, + FuncTransform { + start: fn_start, + end: fn_end, + index: i, + lookup, + }, + ); + } + + let map = build_function_addr_map(at, code_section_offset); + let func = Vec::from_iter(func.into_iter()); + AddressTransform { map, func } + } + + fn find_func(&self, addr: u64) -> Option<&FuncTransform> { + // TODO check if we need to include end address + let func = match self.func.binary_search_by(|entry| entry.0.cmp(&addr)) { + Ok(i) => &self.func[i].1, + Err(i) => { + if i > 0 { + &self.func[i - 1].1 + } else { + return None; + } + } + }; + if addr >= func.start { + return Some(func); + } + None + } + + pub fn find_func_index(&self, addr: u64) -> Option { + self.find_func(addr).map(|f| f.index) + } + + pub fn translate_raw(&self, addr: u64) -> Option<(FuncIndex, GeneratedAddress)> { + if addr == 0 { + // It's normally 0 for debug info without the linked code. + return None; + } + if let Some(func) = self.find_func(addr) { + if addr == func.end { + // Clamp last address to the end to extend translation to the end + // of the function. + let map = &self.map[func.index]; + return Some((func.index, map.len)); + } + let first_result = TransformRangeIter::new(func, addr).next(); + first_result.map(|(address, _)| (func.index, address)) + } else { + // Address was not found: function was not compiled? + None + } + } + + pub fn can_translate_address(&self, addr: u64) -> bool { + self.translate(addr).is_some() + } + + pub fn translate(&self, addr: u64) -> Option { + self.translate_raw(addr) + .map(|(func_index, address)| write::Address::Symbol { + symbol: func_index.index(), + addend: address as i64, + }) + } + + pub fn translate_ranges_raw( + &self, + start: u64, + end: u64, + ) -> Option<(FuncIndex, Vec<(GeneratedAddress, GeneratedAddress)>)> { + if start == 0 { + // It's normally 0 for debug info without the linked code. + return None; + } + if let Some(func) = self.find_func(start) { + let mut starts: HashMap = + HashMap::from_iter(TransformRangeIter::new(func, start).map(|(a, r)| (r, a))); + let mut result = Vec::new(); + TransformRangeEndIter::new(func, end).for_each(|(a, r)| { + let range_start = if let Some(range_start) = starts.get(&r) { + let range_start = *range_start; + starts.remove(&r); + range_start + } else { + let range = &func.lookup.ranges[r]; + range.gen_start + }; + result.push((range_start, a)); + }); + for (r, range_start) in starts { + let range = &func.lookup.ranges[r]; + result.push((range_start, range.gen_end)); + } + return Some((func.index, result)); + } + // Address was not found: function was not compiled? + None + } + + pub fn translate_ranges(&self, start: u64, end: u64) -> Vec<(write::Address, u64)> { + self.translate_ranges_raw(start, end) + .map_or(vec![], |(func_index, ranges)| { + ranges + .iter() + .map(|(start, end)| { + ( + write::Address::Symbol { + symbol: func_index.index(), + addend: *start as i64, + }, + (*end - *start) as u64, + ) + }) + .collect::>() + }) + } + + pub fn map(&self) -> &PrimaryMap { + &self.map + } + + pub fn func_range(&self, index: FuncIndex) -> (GeneratedAddress, GeneratedAddress) { + let map = &self.map[index]; + (map.offset, map.offset + map.len) + } + + pub fn func_source_range(&self, index: FuncIndex) -> (WasmAddress, WasmAddress) { + let map = &self.map[index]; + (map.wasm_start, map.wasm_end) + } + + pub fn convert_to_code_range( + &self, + addr: write::Address, + len: u64, + ) -> (GeneratedAddress, GeneratedAddress) { + let start = if let write::Address::Symbol { addend, .. } = addr { + // TODO subtract self.map[symbol].offset ? + addend as GeneratedAddress + } else { + unreachable!(); + }; + (start, start + len as GeneratedAddress) + } +} + +#[cfg(test)] +mod tests { + use super::{build_function_lookup, get_wasm_code_offset, AddressTransform}; + use crate::read_debug_info::WasmFileInfo; + use cranelift_codegen::ir::SourceLoc; + use cranelift_entity::PrimaryMap; + use gimli::write::Address; + use std::iter::FromIterator; + use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap, ModuleAddressMap}; + + #[test] + fn test_get_wasm_code_offset() { + let offset = get_wasm_code_offset(SourceLoc::new(3), 1); + assert_eq!(2, offset); + let offset = get_wasm_code_offset(SourceLoc::new(16), 0xF000_0000); + assert_eq!(0x1000_0010, offset); + let offset = get_wasm_code_offset(SourceLoc::new(1), 0x20_8000_0000); + assert_eq!(0x8000_0001, offset); + } + + fn create_simple_func(wasm_offset: u32) -> FunctionAddressMap { + FunctionAddressMap { + instructions: vec![ + InstructionAddressMap { + srcloc: SourceLoc::new(wasm_offset + 2), + code_offset: 5, + code_len: 3, + }, + InstructionAddressMap { + srcloc: SourceLoc::new(wasm_offset + 7), + code_offset: 15, + code_len: 8, + }, + ], + start_srcloc: SourceLoc::new(wasm_offset), + end_srcloc: SourceLoc::new(wasm_offset + 10), + body_offset: 0, + body_len: 30, + } + } + + fn create_simple_module(func: FunctionAddressMap) -> ModuleAddressMap { + PrimaryMap::from_iter(vec![func]) + } + + #[test] + fn test_build_function_lookup_simple() { + let input = create_simple_func(11); + let (start, end, lookup) = build_function_lookup(&input, 1); + assert_eq!(10, start); + assert_eq!(20, end); + + assert_eq!(1, lookup.index.len()); + let index_entry = lookup.index.into_iter().next().unwrap(); + assert_eq!((10u64, vec![0].into_boxed_slice()), index_entry); + assert_eq!(1, lookup.ranges.len()); + let range = &lookup.ranges[0]; + assert_eq!(10, range.wasm_start); + assert_eq!(20, range.wasm_end); + assert_eq!(0, range.gen_start); + assert_eq!(30, range.gen_end); + let positions = &range.positions; + assert_eq!(2, positions.len()); + assert_eq!(12, positions[0].wasm_pos); + assert_eq!(5, positions[0].gen_start); + assert_eq!(8, positions[0].gen_end); + assert_eq!(17, positions[1].wasm_pos); + assert_eq!(15, positions[1].gen_start); + assert_eq!(23, positions[1].gen_end); + } + + #[test] + fn test_build_function_lookup_two_ranges() { + let mut input = create_simple_func(11); + // append instruction with same srcloc as input.instructions[0] + input.instructions.push(InstructionAddressMap { + srcloc: SourceLoc::new(11 + 2), + code_offset: 23, + code_len: 3, + }); + let (start, end, lookup) = build_function_lookup(&input, 1); + assert_eq!(10, start); + assert_eq!(20, end); + + assert_eq!(2, lookup.index.len()); + let index_entries = Vec::from_iter(lookup.index.into_iter()); + assert_eq!((10u64, vec![0].into_boxed_slice()), index_entries[0]); + assert_eq!((12u64, vec![0, 1].into_boxed_slice()), index_entries[1]); + assert_eq!(2, lookup.ranges.len()); + + let range = &lookup.ranges[0]; + assert_eq!(10, range.wasm_start); + assert_eq!(17, range.wasm_end); + assert_eq!(0, range.gen_start); + assert_eq!(23, range.gen_end); + let positions = &range.positions; + assert_eq!(2, positions.len()); + assert_eq!(12, positions[0].wasm_pos); + assert_eq!(5, positions[0].gen_start); + assert_eq!(8, positions[0].gen_end); + assert_eq!(17, positions[1].wasm_pos); + assert_eq!(15, positions[1].gen_start); + assert_eq!(23, positions[1].gen_end); + + let range = &lookup.ranges[1]; + assert_eq!(12, range.wasm_start); + assert_eq!(20, range.wasm_end); + assert_eq!(23, range.gen_start); + assert_eq!(30, range.gen_end); + let positions = &range.positions; + assert_eq!(1, positions.len()); + assert_eq!(12, positions[0].wasm_pos); + assert_eq!(23, positions[0].gen_start); + assert_eq!(26, positions[0].gen_end); + } + + #[test] + fn test_addr_translate() { + let input = create_simple_module(create_simple_func(11)); + let at = AddressTransform::new( + &input, + &WasmFileInfo { + path: None, + code_section_offset: 1, + funcs: Box::new([]), + }, + ); + + let addr = at.translate(10); + assert_eq!( + Some(Address::Symbol { + symbol: 0, + addend: 0, + }), + addr + ); + + let addr = at.translate(20); + assert_eq!( + Some(Address::Symbol { + symbol: 0, + addend: 30, + }), + addr + ); + + let addr = at.translate(0); + assert_eq!(None, addr); + + let addr = at.translate(12); + assert_eq!( + Some(Address::Symbol { + symbol: 0, + addend: 5, + }), + addr + ); + + let addr = at.translate(18); + assert_eq!( + Some(Address::Symbol { + symbol: 0, + addend: 23, + }), + addr + ); + } +} diff --git a/lib/debug-writer/src/transform/attr.rs b/lib/debug-writer/src/transform/attr.rs new file mode 100644 index 00000000000..bd15586e9ba --- /dev/null +++ b/lib/debug-writer/src/transform/attr.rs @@ -0,0 +1,308 @@ +// Copyright 2019 WasmTime Project Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is from the WasmTime project. +// It was copied at revision `907e7aac01af333a0af310ce0472abbc8a9adb6c`. +// +// Changes to this file are copyright of Wasmer inc. unless otherwise indicated +// and are licensed under the Wasmer project's license. +use super::address_transform::AddressTransform; +use super::expression::{compile_expression, CompiledExpression, FunctionFrameInfo}; +use super::range_info_builder::RangeInfoBuilder; +use super::unit::PendingDieRef; +use super::{DebugInputContext, Reader, TransformError}; +use anyhow::Error; +use gimli::{ + write, AttributeValue, DebugLineOffset, DebugStr, DebuggingInformationEntry, UnitOffset, +}; +use std::collections::HashMap; + +pub(crate) enum FileAttributeContext<'a> { + Root(Option), + Children(&'a Vec, Option<&'a CompiledExpression>), +} + +fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool { + match attr_name { + gimli::DW_AT_location + | gimli::DW_AT_string_length + | gimli::DW_AT_return_addr + | gimli::DW_AT_data_member_location + | gimli::DW_AT_frame_base + | gimli::DW_AT_segment + | gimli::DW_AT_static_link + | gimli::DW_AT_use_location + | gimli::DW_AT_vtable_elem_location => true, + _ => false, + } +} + +pub(crate) fn clone_die_attributes<'a, R>( + entry: &DebuggingInformationEntry, + context: &DebugInputContext, + addr_tr: &'a AddressTransform, + frame_info: Option<&FunctionFrameInfo>, + unit_encoding: gimli::Encoding, + out_unit: &mut write::Unit, + current_scope_id: write::UnitEntryId, + subprogram_range_builder: Option, + scope_ranges: Option<&Vec<(u64, u64)>>, + cu_low_pc: u64, + out_strings: &mut write::StringTable, + die_ref_map: &HashMap, + pending_die_refs: &mut Vec, + file_context: FileAttributeContext<'a>, +) -> Result<(), Error> +where + R: Reader, +{ + let _tag = &entry.tag(); + let endian = gimli::RunTimeEndian::Little; + + let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder { + subprogram_range_builder + } else if entry.tag() == gimli::DW_TAG_compile_unit { + // FIXME currently address_transform operate on a single func range, + // once it is fixed we can properly set DW_AT_ranges attribute. + // Using for now DW_AT_low_pc = 0. + RangeInfoBuilder::Position(0) + } else { + RangeInfoBuilder::from(entry, context, unit_encoding, cu_low_pc)? + }; + range_info.build(addr_tr, out_unit, current_scope_id); + + let mut attrs = entry.attrs(); + while let Some(attr) = attrs.next()? { + let attr_value = match attr.value() { + AttributeValue::Addr(_) if attr.name() == gimli::DW_AT_low_pc => { + continue; + } + AttributeValue::Udata(_) if attr.name() == gimli::DW_AT_high_pc => { + continue; + } + AttributeValue::RangeListsRef(_) if attr.name() == gimli::DW_AT_ranges => { + continue; + } + AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => { + continue; + } + + AttributeValue::Addr(u) => { + let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); + write::AttributeValue::Address(addr) + } + AttributeValue::Udata(u) => write::AttributeValue::Udata(u), + AttributeValue::Data1(d) => write::AttributeValue::Data1(d), + AttributeValue::Data2(d) => write::AttributeValue::Data2(d), + AttributeValue::Data4(d) => write::AttributeValue::Data4(d), + AttributeValue::Sdata(d) => write::AttributeValue::Sdata(d), + AttributeValue::Flag(f) => write::AttributeValue::Flag(f), + AttributeValue::DebugLineRef(line_program_offset) => { + if let FileAttributeContext::Root(o) = file_context { + if o != Some(line_program_offset) { + return Err(TransformError("invalid debug_line offset").into()); + } + write::AttributeValue::LineProgramRef + } else { + return Err(TransformError("unexpected debug_line index attribute").into()); + } + } + AttributeValue::FileIndex(i) => { + if let FileAttributeContext::Children(file_map, _) = file_context { + write::AttributeValue::FileIndex(Some(file_map[(i - 1) as usize])) + } else { + return Err(TransformError("unexpected file index attribute").into()); + } + } + AttributeValue::DebugStrRef(str_offset) => { + let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec(); + write::AttributeValue::StringRef(out_strings.add(s)) + } + AttributeValue::RangeListsRef(r) => { + let range_info = + RangeInfoBuilder::from_ranges_ref(r, context, unit_encoding, cu_low_pc)?; + let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges); + write::AttributeValue::RangeListRef(range_list_id) + } + AttributeValue::LocationListsRef(r) => { + let low_pc = 0; + let mut locs = context.loclists.locations( + r, + unit_encoding, + low_pc, + &context.debug_addr, + context.debug_addr_base, + )?; + let frame_base = if let FileAttributeContext::Children(_, frame_base) = file_context + { + frame_base + } else { + None + }; + let mut result = None; + while let Some(loc) = locs.next()? { + if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? { + if result.is_none() { + result = Some(Vec::new()); + } + for (start, len, expr) in expr.build_with_locals( + &[(loc.range.begin, loc.range.end)], + addr_tr, + frame_info, + endian, + ) { + if len == 0 { + // Ignore empty range + continue; + } + result.as_mut().unwrap().push(write::Location::StartLength { + begin: start, + length: len, + data: expr, + }); + } + } else { + // FIXME _expr contains invalid expression + continue; // ignore entry + } + } + if result.is_none() { + continue; // no valid locations + } + let list_id = out_unit.locations.add(write::LocationList(result.unwrap())); + write::AttributeValue::LocationListRef(list_id) + } + AttributeValue::Exprloc(ref expr) => { + let frame_base = if let FileAttributeContext::Children(_, frame_base) = file_context + { + frame_base + } else { + None + }; + if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? { + if expr.is_simple() { + if let Some(expr) = expr.build() { + write::AttributeValue::Exprloc(expr) + } else { + continue; + } + } else { + // Conversion to loclist is required. + if let Some(scope_ranges) = scope_ranges { + let exprs = + expr.build_with_locals(scope_ranges, addr_tr, frame_info, endian); + if exprs.is_empty() { + continue; + } + let found_single_expr = { + // Micro-optimization all expressions alike, use one exprloc. + let mut found_expr: Option = None; + for (_, _, expr) in &exprs { + if let Some(ref prev_expr) = found_expr { + if expr.0.eq(&prev_expr.0) { + continue; // the same expression + } + found_expr = None; + break; + } + found_expr = Some(expr.clone()) + } + found_expr + }; + if found_single_expr.is_some() { + write::AttributeValue::Exprloc(found_single_expr.unwrap()) + } else if is_exprloc_to_loclist_allowed(attr.name()) { + // Converting exprloc to loclist. + let mut locs = Vec::new(); + for (begin, length, data) in exprs { + if length == 0 { + // Ignore empty range + continue; + } + locs.push(write::Location::StartLength { + begin, + length, + data, + }); + } + let list_id = out_unit.locations.add(write::LocationList(locs)); + write::AttributeValue::LocationListRef(list_id) + } else { + continue; + } + } else { + continue; + } + } + } else { + // FIXME _expr contains invalid expression + continue; // ignore attribute + } + } + AttributeValue::Encoding(e) => write::AttributeValue::Encoding(e), + AttributeValue::DecimalSign(e) => write::AttributeValue::DecimalSign(e), + AttributeValue::Endianity(e) => write::AttributeValue::Endianity(e), + AttributeValue::Accessibility(e) => write::AttributeValue::Accessibility(e), + AttributeValue::Visibility(e) => write::AttributeValue::Visibility(e), + AttributeValue::Virtuality(e) => write::AttributeValue::Virtuality(e), + AttributeValue::Language(e) => write::AttributeValue::Language(e), + AttributeValue::AddressClass(e) => write::AttributeValue::AddressClass(e), + AttributeValue::IdentifierCase(e) => write::AttributeValue::IdentifierCase(e), + AttributeValue::CallingConvention(e) => write::AttributeValue::CallingConvention(e), + AttributeValue::Inline(e) => write::AttributeValue::Inline(e), + AttributeValue::Ordering(e) => write::AttributeValue::Ordering(e), + AttributeValue::UnitRef(ref offset) => { + if let Some(unit_id) = die_ref_map.get(offset) { + write::AttributeValue::ThisUnitEntryRef(*unit_id) + } else { + pending_die_refs.push((current_scope_id, attr.name(), *offset)); + continue; + } + } + // AttributeValue::DebugInfoRef(_) => { + // continue; + // } + _ => panic!(), //write::AttributeValue::StringRef(out_strings.add("_")), + }; + let current_scope = out_unit.get_mut(current_scope_id); + current_scope.set(attr.name(), attr_value); + } + Ok(()) +} + +pub(crate) fn clone_attr_string( + attr_value: &AttributeValue, + form: gimli::DwForm, + debug_str: &DebugStr, + out_strings: &mut write::StringTable, +) -> Result +where + R: Reader, +{ + let content = match attr_value { + AttributeValue::DebugStrRef(str_offset) => { + debug_str.get_str(*str_offset)?.to_slice()?.to_vec() + } + AttributeValue::String(b) => b.to_slice()?.to_vec(), + _ => panic!("Unexpected attribute value"), + }; + Ok(match form { + gimli::DW_FORM_strp => { + let id = out_strings.add(content); + write::LineString::StringRef(id) + } + gimli::DW_FORM_string => write::LineString::String(content), + _ => panic!("DW_FORM_line_strp or other not supported"), + }) +} diff --git a/lib/debug-writer/src/transform/expression.rs b/lib/debug-writer/src/transform/expression.rs new file mode 100644 index 00000000000..390f5cba25a --- /dev/null +++ b/lib/debug-writer/src/transform/expression.rs @@ -0,0 +1,515 @@ +// Copyright 2029 WasmTime Project Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is from the WasmTime project. +// It was copied at revision `3992b8669f9b9e185abe81e9998ce2ff4d40ff68`. +// +// Changes to this file are copyright of Wasmer inc. unless otherwise indicated +// and are licensed under the Wasmer project's license. +use super::address_transform::AddressTransform; +use anyhow::Error; +use cranelift_codegen::ir::{StackSlots, ValueLabel, ValueLoc}; +use cranelift_codegen::isa::RegUnit; +use cranelift_codegen::ValueLabelsRanges; +use cranelift_entity::EntityRef; +use cranelift_wasm::{get_vmctx_value_label, DefinedFuncIndex}; +use gimli::{self, write, Expression, Operation, Reader, ReaderOffset, Register, X86_64}; +use std::collections::{HashMap, HashSet}; + +#[derive(Debug)] +pub struct FunctionFrameInfo<'a> { + pub value_ranges: &'a ValueLabelsRanges, + pub memory_offset: i64, + pub stack_slots: &'a StackSlots, +} + +#[derive(Debug)] +enum CompiledExpressionPart { + Code(Vec), + Local(ValueLabel), + Deref, +} + +#[derive(Debug)] +pub struct CompiledExpression { + parts: Vec, + need_deref: bool, +} + +impl Clone for CompiledExpressionPart { + fn clone(&self) -> Self { + match self { + CompiledExpressionPart::Code(c) => CompiledExpressionPart::Code(c.clone()), + CompiledExpressionPart::Local(i) => CompiledExpressionPart::Local(*i), + CompiledExpressionPart::Deref => CompiledExpressionPart::Deref, + } + } +} + +impl CompiledExpression { + pub fn vmctx() -> CompiledExpression { + CompiledExpression::from_label(get_vmctx_value_label()) + } + + pub fn from_label(label: ValueLabel) -> CompiledExpression { + CompiledExpression { + parts: vec![ + CompiledExpressionPart::Local(label), + CompiledExpressionPart::Code(vec![gimli::constants::DW_OP_stack_value.0 as u8]), + ], + need_deref: false, + } + } +} + +fn map_reg(reg: RegUnit) -> Register { + static mut REG_X86_MAP: Option> = None; + // FIXME lazy initialization? + unsafe { + if REG_X86_MAP.is_none() { + REG_X86_MAP = Some(HashMap::new()); + } + if let Some(val) = REG_X86_MAP.as_mut().unwrap().get(®) { + return *val; + } + let result = match reg { + 0 => X86_64::RAX, + 1 => X86_64::RCX, + 2 => X86_64::RDX, + 3 => X86_64::RBX, + 4 => X86_64::RSP, + 5 => X86_64::RBP, + 6 => X86_64::RSI, + 7 => X86_64::RDI, + 8 => X86_64::R8, + 9 => X86_64::R9, + 10 => X86_64::R10, + 11 => X86_64::R11, + 12 => X86_64::R12, + 13 => X86_64::R13, + 14 => X86_64::R14, + 15 => X86_64::R15, + 16 => X86_64::XMM0, + 17 => X86_64::XMM1, + 18 => X86_64::XMM2, + 19 => X86_64::XMM3, + 20 => X86_64::XMM4, + 21 => X86_64::XMM5, + 22 => X86_64::XMM6, + 23 => X86_64::XMM7, + 24 => X86_64::XMM8, + 25 => X86_64::XMM9, + 26 => X86_64::XMM10, + 27 => X86_64::XMM11, + 28 => X86_64::XMM12, + 29 => X86_64::XMM13, + 30 => X86_64::XMM14, + 31 => X86_64::XMM15, + _ => panic!("unknown x86_64 register {}", reg), + }; + REG_X86_MAP.as_mut().unwrap().insert(reg, result); + result + } +} + +fn translate_loc(loc: ValueLoc, frame_info: Option<&FunctionFrameInfo>) -> Option> { + match loc { + ValueLoc::Reg(reg) => { + let machine_reg = map_reg(reg).0 as u8; + assert!(machine_reg < 32); // FIXME + Some(vec![gimli::constants::DW_OP_reg0.0 + machine_reg]) + } + ValueLoc::Stack(ss) => { + if let Some(frame_info) = frame_info { + if let Some(ss_offset) = frame_info.stack_slots[ss].offset { + use gimli::write::Writer; + let endian = gimli::RunTimeEndian::Little; + let mut writer = write::EndianVec::new(endian); + writer + .write_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8) + .expect("bp wr"); + writer.write_sleb128(ss_offset as i64 + 16).expect("ss wr"); + writer + .write_u8(gimli::constants::DW_OP_deref.0 as u8) + .expect("bp wr"); + let buf = writer.into_vec(); + return Some(buf); + } + } + None + } + _ => None, + } +} + +fn append_memory_deref( + buf: &mut Vec, + frame_info: &FunctionFrameInfo, + vmctx_loc: ValueLoc, + endian: gimli::RunTimeEndian, +) -> write::Result { + use gimli::write::Writer; + let mut writer = write::EndianVec::new(endian); + match vmctx_loc { + ValueLoc::Reg(vmctx_reg) => { + let reg = map_reg(vmctx_reg); + writer.write_u8(gimli::constants::DW_OP_breg0.0 + reg.0 as u8)?; + writer.write_sleb128(frame_info.memory_offset)?; + } + ValueLoc::Stack(ss) => { + if let Some(ss_offset) = frame_info.stack_slots[ss].offset { + writer.write_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8)?; + writer.write_sleb128(ss_offset as i64 + 16)?; + writer.write_u8(gimli::constants::DW_OP_deref.0 as u8)?; + + writer.write_u8(gimli::constants::DW_OP_consts.0 as u8)?; + writer.write_sleb128(frame_info.memory_offset)?; + writer.write_u8(gimli::constants::DW_OP_plus.0 as u8)?; + } else { + return Ok(false); + } + } + _ => { + return Ok(false); + } + } + writer.write_u8(gimli::constants::DW_OP_deref.0 as u8)?; + writer.write_u8(gimli::constants::DW_OP_swap.0 as u8)?; + writer.write_u8(gimli::constants::DW_OP_stack_value.0 as u8)?; + writer.write_u8(gimli::constants::DW_OP_constu.0 as u8)?; + writer.write_uleb128(0xffff_ffff)?; + writer.write_u8(gimli::constants::DW_OP_and.0 as u8)?; + writer.write_u8(gimli::constants::DW_OP_plus.0 as u8)?; + buf.extend_from_slice(writer.slice()); + Ok(true) +} + +impl CompiledExpression { + pub fn is_simple(&self) -> bool { + if let [CompiledExpressionPart::Code(_)] = self.parts.as_slice() { + true + } else { + self.parts.is_empty() + } + } + + pub fn build(&self) -> Option { + if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() { + return Some(write::Expression(code.to_vec())); + } + // locals found, not supported + None + } + + pub fn build_with_locals( + &self, + scope: &[(u64, u64)], // wasm ranges + addr_tr: &AddressTransform, + frame_info: Option<&FunctionFrameInfo>, + endian: gimli::RunTimeEndian, + ) -> Vec<(write::Address, u64, write::Expression)> { + if scope.is_empty() { + return vec![]; + } + + if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() { + let mut result_scope = Vec::new(); + for s in scope { + for (addr, len) in addr_tr.translate_ranges(s.0, s.1) { + result_scope.push((addr, len, write::Expression(code.to_vec()))); + } + } + return result_scope; + } + + let vmctx_label = get_vmctx_value_label(); + + // Some locals are present, preparing and divided ranges based on the scope + // and frame_info data. + let mut ranges_builder = ValueLabelRangesBuilder::new(scope, addr_tr, frame_info); + for p in &self.parts { + match p { + CompiledExpressionPart::Code(_) => (), + CompiledExpressionPart::Local(label) => ranges_builder.process_label(*label), + CompiledExpressionPart::Deref => ranges_builder.process_label(vmctx_label), + } + } + if self.need_deref { + ranges_builder.process_label(vmctx_label); + } + ranges_builder.remove_incomplete_ranges(); + let ranges = ranges_builder.ranges; + + let mut result = Vec::new(); + 'range: for CachedValueLabelRange { + func_index, + start, + end, + label_location, + } in ranges + { + // build expression + let mut code_buf = Vec::new(); + for part in &self.parts { + match part { + CompiledExpressionPart::Code(c) => code_buf.extend_from_slice(c.as_slice()), + CompiledExpressionPart::Local(label) => { + let loc = *label_location.get(&label).expect("loc"); + if let Some(expr) = translate_loc(loc, frame_info) { + code_buf.extend_from_slice(&expr) + } else { + continue 'range; + } + } + CompiledExpressionPart::Deref => { + if let (Some(vmctx_loc), Some(frame_info)) = + (label_location.get(&vmctx_label), frame_info) + { + if !append_memory_deref(&mut code_buf, frame_info, *vmctx_loc, endian) + .expect("append_memory_deref") + { + continue 'range; + } + } else { + continue 'range; + }; + } + } + } + if self.need_deref { + if let (Some(vmctx_loc), Some(frame_info)) = + (label_location.get(&vmctx_label), frame_info) + { + if !append_memory_deref(&mut code_buf, frame_info, *vmctx_loc, endian) + .expect("append_memory_deref") + { + continue 'range; + } + } else { + continue 'range; + }; + } + result.push(( + write::Address::Symbol { + symbol: func_index.index(), + addend: start as i64, + }, + (end - start) as u64, + write::Expression(code_buf), + )); + } + + result + } +} + +pub fn compile_expression( + expr: &Expression, + encoding: gimli::Encoding, + frame_base: Option<&CompiledExpression>, +) -> Result, Error> +where + R: Reader, +{ + let mut parts = Vec::new(); + let mut need_deref = false; + if let Some(frame_base) = frame_base { + parts.extend_from_slice(&frame_base.parts); + need_deref = frame_base.need_deref; + } + let base_len = parts.len(); + let mut pc = expr.0.clone(); + let mut code_chunk = Vec::new(); + let buf = expr.0.to_slice()?; + while !pc.is_empty() { + let next = buf[pc.offset_from(&expr.0).into_u64() as usize]; + need_deref = true; + if next == 0xED { + // WebAssembly DWARF extension + pc.read_u8()?; + let ty = pc.read_uleb128()?; + assert_eq!(ty, 0); + let index = pc.read_sleb128()?; + pc.read_u8()?; // consume 159 + if code_chunk.len() > 0 { + parts.push(CompiledExpressionPart::Code(code_chunk)); + code_chunk = Vec::new(); + } + let label = ValueLabel::from_u32(index as u32); + parts.push(CompiledExpressionPart::Local(label)); + } else { + let pos = pc.offset_from(&expr.0).into_u64() as usize; + let op = Operation::parse(&mut pc, &expr.0, encoding)?; + match op { + Operation::Literal { .. } | Operation::PlusConstant { .. } => (), + Operation::StackValue => { + need_deref = false; + } + Operation::Deref { .. } => { + if code_chunk.len() > 0 { + parts.push(CompiledExpressionPart::Code(code_chunk)); + code_chunk = Vec::new(); + } + parts.push(CompiledExpressionPart::Deref); + } + _ => { + return Ok(None); + } + } + let chunk = &buf[pos..pc.offset_from(&expr.0).into_u64() as usize]; + code_chunk.extend_from_slice(chunk); + } + } + + if code_chunk.len() > 0 { + parts.push(CompiledExpressionPart::Code(code_chunk)); + } + + if base_len > 0 && base_len + 1 < parts.len() { + // see if we can glue two code chunks + if let [CompiledExpressionPart::Code(cc1), CompiledExpressionPart::Code(cc2)] = + &parts[base_len..base_len + 1] + { + let mut combined = cc1.clone(); + combined.extend_from_slice(cc2); + parts[base_len] = CompiledExpressionPart::Code(combined); + parts.remove(base_len + 1); + } + } + + Ok(Some(CompiledExpression { parts, need_deref })) +} + +#[derive(Debug, Clone)] +struct CachedValueLabelRange { + func_index: DefinedFuncIndex, + start: usize, + end: usize, + label_location: HashMap, +} + +struct ValueLabelRangesBuilder<'a, 'b> { + ranges: Vec, + addr_tr: &'a AddressTransform, + frame_info: Option<&'a FunctionFrameInfo<'b>>, + processed_labels: HashSet, +} + +impl<'a, 'b> ValueLabelRangesBuilder<'a, 'b> { + fn new( + scope: &[(u64, u64)], // wasm ranges + addr_tr: &'a AddressTransform, + frame_info: Option<&'a FunctionFrameInfo<'b>>, + ) -> Self { + let mut ranges = Vec::new(); + for s in scope { + if let Some((func_index, tr)) = addr_tr.translate_ranges_raw(s.0, s.1) { + for (start, end) in tr { + ranges.push(CachedValueLabelRange { + func_index, + start, + end, + label_location: HashMap::new(), + }) + } + } + } + ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start)); + ValueLabelRangesBuilder { + ranges, + addr_tr, + frame_info, + processed_labels: HashSet::new(), + } + } + + fn process_label(&mut self, label: ValueLabel) { + if self.processed_labels.contains(&label) { + return; + } + self.processed_labels.insert(label); + + let value_ranges = if let Some(frame_info) = self.frame_info { + &frame_info.value_ranges + } else { + return; + }; + + let ranges = &mut self.ranges; + if let Some(local_ranges) = value_ranges.get(&label) { + for local_range in local_ranges { + let wasm_start = local_range.start; + let wasm_end = local_range.end; + let loc = local_range.loc; + // Find all native ranges for the value label ranges. + for (addr, len) in self + .addr_tr + .translate_ranges(wasm_start as u64, wasm_end as u64) + { + let (range_start, range_end) = self.addr_tr.convert_to_code_range(addr, len); + if range_start == range_end { + continue; + } + assert!(range_start < range_end); + // Find acceptable scope of ranges to intersect with. + let i = match ranges.binary_search_by(|s| s.start.cmp(&range_start)) { + Ok(i) => i, + Err(i) => { + if i > 0 && range_start < ranges[i - 1].end { + i - 1 + } else { + i + } + } + }; + let j = match ranges.binary_search_by(|s| s.start.cmp(&range_end)) { + Ok(i) | Err(i) => i, + }; + // Starting for the end, intersect (range_start..range_end) with + // self.ranges array. + for i in (i..j).rev() { + if range_end <= ranges[i].start || ranges[i].end <= range_start { + continue; + } + if range_end < ranges[i].end { + // Cutting some of the range from the end. + let mut tail = ranges[i].clone(); + ranges[i].end = range_end; + tail.start = range_end; + ranges.insert(i + 1, tail); + } + assert!(ranges[i].end <= range_end); + if range_start <= ranges[i].start { + ranges[i].label_location.insert(label, loc); + continue; + } + // Cutting some of the range from the start. + let mut tail = ranges[i].clone(); + ranges[i].end = range_start; + tail.start = range_start; + tail.label_location.insert(label, loc); + ranges.insert(i + 1, tail); + } + } + } + } + } + + fn remove_incomplete_ranges(&mut self) { + // Ranges with not-enough labels are discarded. + let processed_labels_len = self.processed_labels.len(); + self.ranges + .retain(|r| r.label_location.len() == processed_labels_len); + } +} diff --git a/lib/debug-writer/src/transform/line_program.rs b/lib/debug-writer/src/transform/line_program.rs new file mode 100644 index 00000000000..f584630a082 --- /dev/null +++ b/lib/debug-writer/src/transform/line_program.rs @@ -0,0 +1,248 @@ +// Copyright 2019 WasmTime Project Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is from the WasmTime project. +// It was copied at revision `cc6e8e1af25e5f9b64e183970d50f62c8338f259`. +// +// Changes to this file are copyright of Wasmer inc. unless otherwise indicated +// and are licensed under the Wasmer project's license. +use super::address_transform::AddressTransform; +use super::attr::clone_attr_string; +use super::{Reader, TransformError}; +use anyhow::Error; +use cranelift_entity::EntityRef; +use gimli::{ + write, DebugLine, DebugLineOffset, DebugStr, DebuggingInformationEntry, LineEncoding, Unit, +}; +use std::collections::BTreeMap; +use std::iter::FromIterator; + +#[derive(Debug)] +enum SavedLineProgramRow { + Normal { + address: u64, + op_index: u64, + file_index: u64, + line: u64, + column: u64, + discriminator: u64, + is_stmt: bool, + basic_block: bool, + prologue_end: bool, + epilogue_begin: bool, + isa: u64, + }, + EndOfSequence(u64), +} + +#[derive(Debug, Eq, PartialEq)] +enum ReadLineProgramState { + SequenceEnded, + ReadSequence, + IgnoreSequence, +} + +pub(crate) fn clone_line_program( + unit: &Unit, + root: &DebuggingInformationEntry, + addr_tr: &AddressTransform, + out_encoding: gimli::Encoding, + debug_str: &DebugStr, + debug_line: &DebugLine, + out_strings: &mut write::StringTable, +) -> Result<(write::LineProgram, DebugLineOffset, Vec), Error> +where + R: Reader, +{ + let offset = match root.attr_value(gimli::DW_AT_stmt_list)? { + Some(gimli::AttributeValue::DebugLineRef(offset)) => offset, + _ => { + return Err(TransformError("Debug line offset is not found").into()); + } + }; + let comp_dir = root.attr_value(gimli::DW_AT_comp_dir)?; + let comp_name = root.attr_value(gimli::DW_AT_name)?; + let out_comp_dir = clone_attr_string( + comp_dir.as_ref().expect("comp_dir"), + gimli::DW_FORM_strp, + debug_str, + out_strings, + )?; + let out_comp_name = clone_attr_string( + comp_name.as_ref().expect("comp_name"), + gimli::DW_FORM_strp, + debug_str, + out_strings, + )?; + + let program = debug_line.program( + offset, + unit.header.address_size(), + comp_dir.and_then(|val| val.string_value(&debug_str)), + comp_name.and_then(|val| val.string_value(&debug_str)), + ); + if let Ok(program) = program { + let header = program.header(); + assert!(header.version() <= 4, "not supported 5"); + let line_encoding = LineEncoding { + minimum_instruction_length: header.minimum_instruction_length(), + maximum_operations_per_instruction: header.maximum_operations_per_instruction(), + default_is_stmt: header.default_is_stmt(), + line_base: header.line_base(), + line_range: header.line_range(), + }; + let mut out_program = write::LineProgram::new( + out_encoding, + line_encoding, + out_comp_dir, + out_comp_name, + None, + ); + let mut dirs = Vec::new(); + dirs.push(out_program.default_directory()); + for dir_attr in header.include_directories() { + let dir_id = out_program.add_directory(clone_attr_string( + dir_attr, + gimli::DW_FORM_string, + debug_str, + out_strings, + )?); + dirs.push(dir_id); + } + let mut files = Vec::new(); + for file_entry in header.file_names() { + let dir_id = dirs[file_entry.directory_index() as usize]; + let file_id = out_program.add_file( + clone_attr_string( + &file_entry.path_name(), + gimli::DW_FORM_string, + debug_str, + out_strings, + )?, + dir_id, + None, + ); + files.push(file_id); + } + + let mut rows = program.rows(); + let mut saved_rows = BTreeMap::new(); + let mut state = ReadLineProgramState::SequenceEnded; + while let Some((_header, row)) = rows.next_row()? { + if state == ReadLineProgramState::IgnoreSequence { + if row.end_sequence() { + state = ReadLineProgramState::SequenceEnded; + } + continue; + } + let saved_row = if row.end_sequence() { + state = ReadLineProgramState::SequenceEnded; + SavedLineProgramRow::EndOfSequence(row.address()) + } else { + if state == ReadLineProgramState::SequenceEnded { + // Discard sequences for non-existent code. + if row.address() == 0 { + state = ReadLineProgramState::IgnoreSequence; + continue; + } + state = ReadLineProgramState::ReadSequence; + } + SavedLineProgramRow::Normal { + address: row.address(), + op_index: row.op_index(), + file_index: row.file_index(), + line: row.line().unwrap_or(0), + column: match row.column() { + gimli::ColumnType::LeftEdge => 0, + gimli::ColumnType::Column(val) => val, + }, + discriminator: row.discriminator(), + is_stmt: row.is_stmt(), + basic_block: row.basic_block(), + prologue_end: row.prologue_end(), + epilogue_begin: row.epilogue_begin(), + isa: row.isa(), + } + }; + saved_rows.insert(row.address(), saved_row); + } + + let saved_rows = Vec::from_iter(saved_rows.into_iter()); + for (i, map) in addr_tr.map() { + if map.len == 0 { + continue; // no code generated + } + let symbol = i.index(); + let base_addr = map.offset; + out_program.begin_sequence(Some(write::Address::Symbol { symbol, addend: 0 })); + // TODO track and place function declaration line here + let mut last_address = None; + for addr_map in map.addresses.iter() { + let saved_row = match saved_rows.binary_search_by_key(&addr_map.wasm, |i| i.0) { + Ok(i) => Some(&saved_rows[i].1), + Err(i) => { + if i > 0 { + Some(&saved_rows[i - 1].1) + } else { + None + } + } + }; + if let Some(SavedLineProgramRow::Normal { + address, + op_index, + file_index, + line, + column, + discriminator, + is_stmt, + basic_block, + prologue_end, + epilogue_begin, + isa, + }) = saved_row + { + // Ignore duplicates + if Some(*address) != last_address { + let address_offset = if last_address.is_none() { + // Extend first entry to the function declaration + // TODO use the function declaration line instead + 0 + } else { + (addr_map.generated - base_addr) as u64 + }; + out_program.row().address_offset = address_offset; + out_program.row().op_index = *op_index; + out_program.row().file = files[(file_index - 1) as usize]; + out_program.row().line = *line; + out_program.row().column = *column; + out_program.row().discriminator = *discriminator; + out_program.row().is_statement = *is_stmt; + out_program.row().basic_block = *basic_block; + out_program.row().prologue_end = *prologue_end; + out_program.row().epilogue_begin = *epilogue_begin; + out_program.row().isa = *isa; + out_program.generate_row(); + last_address = Some(*address); + } + } + } + let end_addr = (map.offset + map.len - 1) as u64; + out_program.end_sequence(end_addr); + } + Ok((out_program, offset, files)) + } else { + Err(TransformError("Valid line program not found").into()) + } +} diff --git a/lib/debug-writer/src/transform/mod.rs b/lib/debug-writer/src/transform/mod.rs new file mode 100644 index 00000000000..9b79765c209 --- /dev/null +++ b/lib/debug-writer/src/transform/mod.rs @@ -0,0 +1,133 @@ +// Copyright 2019 WasmTime Project Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is from the WasmTime project. +// It was copied at revision `907e7aac01af333a0af310ce0472abbc8a9adb6c`. +// +// Changes to this file are copyright of Wasmer inc. unless otherwise indicated +// and are licensed under the Wasmer project's license. +use crate::gc::build_dependencies; +use crate::DebugInfoData; +use anyhow::Error; +use cranelift_codegen::isa::TargetFrontendConfig; +use gimli::{ + write, DebugAddr, DebugAddrBase, DebugLine, DebugStr, LocationLists, RangeLists, + UnitSectionOffset, +}; +use simulate::generate_simulated_dwarf; +use std::collections::HashSet; +use thiserror::Error; +use unit::clone_unit; +use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges}; + +pub use address_transform::AddressTransform; + +mod address_transform; +mod attr; +mod expression; +mod line_program; +mod range_info_builder; +mod simulate; +mod unit; +mod utils; + +pub(crate) trait Reader: gimli::Reader {} + +impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> where Endian: gimli::Endianity {} + +#[derive(Error, Debug)] +#[error("Debug info transform error: {0}")] +pub struct TransformError(&'static str); + +pub(crate) struct DebugInputContext<'a, R> +where + R: Reader, +{ + debug_str: &'a DebugStr, + debug_line: &'a DebugLine, + debug_addr: &'a DebugAddr, + debug_addr_base: DebugAddrBase, + rnglists: &'a RangeLists, + loclists: &'a LocationLists, + reachable: &'a HashSet, +} + +pub fn transform_dwarf( + target_config: &TargetFrontendConfig, + di: &DebugInfoData, + at: &ModuleAddressMap, + vmctx_info: &ModuleVmctxInfo, + ranges: &ValueLabelsRanges, +) -> Result { + let addr_tr = AddressTransform::new(at, &di.wasm_file); + let reachable = build_dependencies(&di.dwarf, &addr_tr)?.get_reachable(); + + let context = DebugInputContext { + debug_str: &di.dwarf.debug_str, + debug_line: &di.dwarf.debug_line, + debug_addr: &di.dwarf.debug_addr, + debug_addr_base: DebugAddrBase(0), + rnglists: &di.dwarf.ranges, + loclists: &di.dwarf.locations, + reachable: &reachable, + }; + + let out_encoding = gimli::Encoding { + format: gimli::Format::Dwarf32, + // TODO: this should be configurable + // macOS doesn't seem to support DWARF > 3 + version: 3, + address_size: target_config.pointer_bytes(), + }; + + let mut out_strings = write::StringTable::default(); + let mut out_units = write::UnitTable::default(); + + let out_line_strings = write::LineStringTable::default(); + + let mut translated = HashSet::new(); + let mut iter = di.dwarf.debug_info.units(); + while let Some(unit) = iter.next().unwrap_or(None) { + let unit = di.dwarf.unit(unit)?; + clone_unit( + unit, + &context, + &addr_tr, + &ranges, + out_encoding, + &vmctx_info, + &mut out_units, + &mut out_strings, + &mut translated, + )?; + } + + generate_simulated_dwarf( + &addr_tr, + di, + &vmctx_info, + &ranges, + &translated, + out_encoding, + &mut out_units, + &mut out_strings, + )?; + + Ok(write::Dwarf { + units: out_units, + line_programs: vec![], + line_strings: out_line_strings, + strings: out_strings, + }) +} diff --git a/lib/debug-writer/src/transform/range_info_builder.rs b/lib/debug-writer/src/transform/range_info_builder.rs new file mode 100644 index 00000000000..881dcca1e52 --- /dev/null +++ b/lib/debug-writer/src/transform/range_info_builder.rs @@ -0,0 +1,237 @@ +// Copyright 2019 WasmTime Project Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is from the WasmTime project. +// It was copied at revision `907e7aac01af333a0af310ce0472abbc8a9adb6c`. +// +// Changes to this file are copyright of Wasmer inc. unless otherwise indicated +// and are licensed under the Wasmer project's license. +use super::address_transform::AddressTransform; +use super::{DebugInputContext, Reader}; +use anyhow::Error; +use cranelift_entity::EntityRef; +use wasmer_runtime_core::types::FuncIndex +use gimli::{write, AttributeValue, DebuggingInformationEntry, RangeListsOffset}; + +pub(crate) enum RangeInfoBuilder { + Undefined, + Position(u64), + Ranges(Vec<(u64, u64)>), + Function(FuncIndex), +} + +impl RangeInfoBuilder { + pub(crate) fn from( + entry: &DebuggingInformationEntry, + context: &DebugInputContext, + unit_encoding: gimli::Encoding, + cu_low_pc: u64, + ) -> Result + where + R: Reader, + { + if let Some(AttributeValue::RangeListsRef(r)) = entry.attr_value(gimli::DW_AT_ranges)? { + return RangeInfoBuilder::from_ranges_ref(r, context, unit_encoding, cu_low_pc); + }; + + let low_pc = + if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? { + addr + } else { + return Ok(RangeInfoBuilder::Undefined); + }; + + Ok( + if let Some(AttributeValue::Udata(u)) = entry.attr_value(gimli::DW_AT_high_pc)? { + RangeInfoBuilder::Ranges(vec![(low_pc, low_pc + u)]) + } else { + RangeInfoBuilder::Position(low_pc) + }, + ) + } + + pub(crate) fn from_ranges_ref( + ranges: RangeListsOffset, + context: &DebugInputContext, + unit_encoding: gimli::Encoding, + cu_low_pc: u64, + ) -> Result + where + R: Reader, + { + let mut ranges = context.rnglists.ranges( + ranges, + unit_encoding, + cu_low_pc, + &context.debug_addr, + context.debug_addr_base, + )?; + let mut result = Vec::new(); + while let Some(range) = ranges.next()? { + if range.begin >= range.end { + // ignore empty ranges + } + result.push((range.begin, range.end)); + } + + Ok(if result.len() > 0 { + RangeInfoBuilder::Ranges(result) + } else { + RangeInfoBuilder::Undefined + }) + } + + pub(crate) fn from_subprogram_die( + entry: &DebuggingInformationEntry, + context: &DebugInputContext, + unit_encoding: gimli::Encoding, + addr_tr: &AddressTransform, + cu_low_pc: u64, + ) -> Result + where + R: Reader, + { + let addr = + if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? { + addr + } else if let Some(AttributeValue::RangeListsRef(r)) = + entry.attr_value(gimli::DW_AT_ranges)? + { + let mut ranges = context.rnglists.ranges( + r, + unit_encoding, + cu_low_pc, + &context.debug_addr, + context.debug_addr_base, + )?; + if let Some(range) = ranges.next()? { + range.begin + } else { + return Ok(RangeInfoBuilder::Undefined); + } + } else { + return Ok(RangeInfoBuilder::Undefined); + }; + + let index = addr_tr.find_func_index(addr); + if index.is_none() { + return Ok(RangeInfoBuilder::Undefined); + } + Ok(RangeInfoBuilder::Function(index.unwrap())) + } + + pub(crate) fn build( + &self, + addr_tr: &AddressTransform, + out_unit: &mut write::Unit, + current_scope_id: write::UnitEntryId, + ) { + match self { + RangeInfoBuilder::Undefined => (), + RangeInfoBuilder::Position(pc) => { + let addr = addr_tr + .translate(*pc) + .unwrap_or(write::Address::Constant(0)); + let current_scope = out_unit.get_mut(current_scope_id); + current_scope.set(gimli::DW_AT_low_pc, write::AttributeValue::Address(addr)); + } + RangeInfoBuilder::Ranges(ranges) => { + let mut result = Vec::new(); + for (begin, end) in ranges { + for tr in addr_tr.translate_ranges(*begin, *end) { + if tr.1 == 0 { + // Ignore empty range + continue; + } + result.push(tr); + } + } + if result.len() != 1 { + let range_list = result + .iter() + .map(|tr| write::Range::StartLength { + begin: tr.0, + length: tr.1, + }) + .collect::>(); + let range_list_id = out_unit.ranges.add(write::RangeList(range_list)); + let current_scope = out_unit.get_mut(current_scope_id); + current_scope.set( + gimli::DW_AT_ranges, + write::AttributeValue::RangeListRef(range_list_id), + ); + } else { + let current_scope = out_unit.get_mut(current_scope_id); + current_scope.set( + gimli::DW_AT_low_pc, + write::AttributeValue::Address(result[0].0), + ); + current_scope.set( + gimli::DW_AT_high_pc, + write::AttributeValue::Udata(result[0].1), + ); + } + } + RangeInfoBuilder::Function(index) => { + let range = addr_tr.func_range(*index); + let symbol = index.index(); + let addr = write::Address::Symbol { + symbol, + addend: range.0 as i64, + }; + let len = (range.1 - range.0) as u64; + let current_scope = out_unit.get_mut(current_scope_id); + current_scope.set(gimli::DW_AT_low_pc, write::AttributeValue::Address(addr)); + current_scope.set(gimli::DW_AT_high_pc, write::AttributeValue::Udata(len)); + } + } + } + + pub(crate) fn get_ranges(&self, addr_tr: &AddressTransform) -> Vec<(u64, u64)> { + match self { + RangeInfoBuilder::Undefined | RangeInfoBuilder::Position(_) => vec![], + RangeInfoBuilder::Ranges(ranges) => ranges.clone(), + RangeInfoBuilder::Function(index) => { + let range = addr_tr.func_source_range(*index); + vec![(range.0, range.1)] + } + } + } + + pub(crate) fn build_ranges( + &self, + addr_tr: &AddressTransform, + out_range_lists: &mut write::RangeListTable, + ) -> write::RangeListId { + if let RangeInfoBuilder::Ranges(ranges) = self { + let mut range_list = Vec::new(); + for (begin, end) in ranges { + assert!(begin < end); + for tr in addr_tr.translate_ranges(*begin, *end) { + if tr.1 == 0 { + // Ignore empty range + continue; + } + range_list.push(write::Range::StartLength { + begin: tr.0, + length: tr.1, + }); + } + } + out_range_lists.add(write::RangeList(range_list)) + } else { + unreachable!(); + } + } +} diff --git a/lib/debug-writer/src/transform/simulate.rs b/lib/debug-writer/src/transform/simulate.rs new file mode 100644 index 00000000000..244878c7364 --- /dev/null +++ b/lib/debug-writer/src/transform/simulate.rs @@ -0,0 +1,392 @@ +// Copyright 2019 WasmTime Project Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is from the WasmTime project. +// It was copied at revision `cc6e8e1af25e5f9b64e183970d50f62c8338f259`. +// +// Changes to this file are copyright of Wasmer inc. unless otherwise indicated +// and are licensed under the Wasmer project's license. +use super::expression::{CompiledExpression, FunctionFrameInfo}; +use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info}; +use super::AddressTransform; +use crate::read_debug_info::WasmFileInfo; +use anyhow::Error; +use cranelift_entity::EntityRef; +use cranelift_wasm::get_vmctx_value_label; +use gimli::write; +use gimli::{self, LineEncoding}; +use std::collections::{HashMap, HashSet}; +use std::path::PathBuf; +// TODO: ValueLabelsRanges +use wasmer_runtime_core::{module::ModuleInfo, state::CodeVersion}; + +pub use crate::read_debug_info::{DebugInfoData, FunctionMetadata, WasmType}; + +const PRODUCER_NAME: &str = "wasmtime"; + +fn generate_line_info( + addr_tr: &AddressTransform, + translated: &HashSet, + out_encoding: gimli::Encoding, + w: &WasmFileInfo, + comp_dir_id: write::StringId, + name_id: write::StringId, + name: &str, +) -> Result { + let out_comp_dir = write::LineString::StringRef(comp_dir_id); + let out_comp_name = write::LineString::StringRef(name_id); + + let line_encoding = LineEncoding::default(); + + let mut out_program = write::LineProgram::new( + out_encoding, + line_encoding, + out_comp_dir, + out_comp_name, + None, + ); + + let file_index = out_program.add_file( + write::LineString::String(name.as_bytes().to_vec()), + out_program.default_directory(), + None, + ); + + for (i, map) in addr_tr.map() { + let symbol = i.index(); + if translated.contains(&(symbol as u32)) { + continue; + } + + let base_addr = map.offset; + out_program.begin_sequence(Some(write::Address::Symbol { symbol, addend: 0 })); + for addr_map in map.addresses.iter() { + let address_offset = (addr_map.generated - base_addr) as u64; + out_program.row().address_offset = address_offset; + out_program.row().op_index = 0; + out_program.row().file = file_index; + let wasm_offset = w.code_section_offset + addr_map.wasm as u64; + out_program.row().line = wasm_offset; + out_program.row().column = 0; + out_program.row().discriminator = 1; + out_program.row().is_statement = true; + out_program.row().basic_block = false; + out_program.row().prologue_end = false; + out_program.row().epilogue_begin = false; + out_program.row().isa = 0; + out_program.generate_row(); + } + let end_addr = (map.offset + map.len - 1) as u64; + out_program.end_sequence(end_addr); + } + + Ok(out_program) +} + +fn autogenerate_dwarf_wasm_path(di: &DebugInfoData) -> PathBuf { + let module_name = di + .name_section + .as_ref() + .and_then(|ns| ns.module_name.to_owned()) + .unwrap_or_else(|| unsafe { + static mut GEN_ID: u32 = 0; + GEN_ID += 1; + format!("", GEN_ID) + }); + let path = format!("//{}.wasm", module_name); + PathBuf::from(path) +} + +struct WasmTypesDieRefs { + vmctx: write::UnitEntryId, + i32: write::UnitEntryId, + i64: write::UnitEntryId, + i128: write::UnitEntryId, + f32: write::UnitEntryId, + f64: write::UnitEntryId, +} + +fn add_wasm_types( + unit: &mut write::Unit, + root_id: write::UnitEntryId, + out_strings: &mut write::StringTable, + vmctx_info: &ModuleInfo, +) -> WasmTypesDieRefs { + let (_wp_die_id, vmctx_die_id) = add_internal_types(unit, root_id, out_strings, vmctx_info); + + macro_rules! def_type { + ($id:literal, $size:literal, $enc:path) => {{ + let die_id = unit.add(root_id, gimli::DW_TAG_base_type); + let die = unit.get_mut(die_id); + die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add($id)), + ); + die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1($size)); + die.set(gimli::DW_AT_encoding, write::AttributeValue::Encoding($enc)); + die_id + }}; + } + + let i32_die_id = def_type!("i32", 4, gimli::DW_ATE_signed); + let i64_die_id = def_type!("i64", 8, gimli::DW_ATE_signed); + let i128_die_id = def_type!("i128", 16, gimli::DW_ATE_signed); + let f32_die_id = def_type!("f32", 4, gimli::DW_ATE_float); + let f64_die_id = def_type!("f64", 8, gimli::DW_ATE_float); + + WasmTypesDieRefs { + vmctx: vmctx_die_id, + i32: i32_die_id, + i64: i64_die_id, + i128: i128_die_id, + f32: f32_die_id, + f64: f64_die_id, + } +} + +fn resolve_var_type( + index: usize, + wasm_types: &WasmTypesDieRefs, + func_meta: &FunctionMetadata, +) -> Option<(write::UnitEntryId, bool)> { + let (ty, is_param) = if index < func_meta.params.len() { + (func_meta.params[index], true) + } else { + let mut i = (index - func_meta.params.len()) as u32; + let mut j = 0; + while j < func_meta.locals.len() && i >= func_meta.locals[j].0 { + i -= func_meta.locals[j].0; + j += 1; + } + if j >= func_meta.locals.len() { + // Ignore the var index out of bound. + return None; + } + (func_meta.locals[j].1, false) + }; + let type_die_id = match ty { + WasmType::I32 => wasm_types.i32, + WasmType::I64 => wasm_types.i64, + WasmType::F32 => wasm_types.f32, + WasmType::F64 => wasm_types.f64, + _ => { + // Ignore unsupported types. + return None; + } + }; + Some((type_die_id, is_param)) +} + +fn generate_vars( + unit: &mut write::Unit, + die_id: write::UnitEntryId, + addr_tr: &AddressTransform, + frame_info: &FunctionFrameInfo, + scope_ranges: &[(u64, u64)], + wasm_types: &WasmTypesDieRefs, + func_meta: &FunctionMetadata, + locals_names: Option<&HashMap>, + out_strings: &mut write::StringTable, +) { + let vmctx_label = get_vmctx_value_label(); + + for label in frame_info.value_ranges.keys() { + if label.index() == vmctx_label.index() { + append_vmctx_info( + unit, + die_id, + wasm_types.vmctx, + addr_tr, + Some(frame_info), + scope_ranges, + out_strings, + ) + .expect("append_vmctx_info success"); + } else { + let var_index = label.index(); + let (type_die_id, is_param) = + if let Some(result) = resolve_var_type(var_index, wasm_types, func_meta) { + result + } else { + // Skipping if type of local cannot be detected. + continue; + }; + + let loc_list_id = { + let endian = gimli::RunTimeEndian::Little; + + let expr = CompiledExpression::from_label(*label); + let mut locs = Vec::new(); + for (begin, length, data) in + expr.build_with_locals(scope_ranges, addr_tr, Some(frame_info), endian) + { + locs.push(write::Location::StartLength { + begin, + length, + data, + }); + } + unit.locations.add(write::LocationList(locs)) + }; + + let var_id = unit.add( + die_id, + if is_param { + gimli::DW_TAG_formal_parameter + } else { + gimli::DW_TAG_variable + }, + ); + let var = unit.get_mut(var_id); + + let name_id = match locals_names.and_then(|m| m.get(&(var_index as u32))) { + Some(n) => out_strings.add(n.to_owned()), + None => out_strings.add(format!("var{}", var_index)), + }; + + var.set(gimli::DW_AT_name, write::AttributeValue::StringRef(name_id)); + var.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(type_die_id), + ); + var.set( + gimli::DW_AT_location, + write::AttributeValue::LocationListRef(loc_list_id), + ); + } + } +} + +pub fn generate_simulated_dwarf( + addr_tr: &AddressTransform, + di: &DebugInfoData, + vmctx_info: &ModuleVmctxInfo, + ranges: &ValueLabelsRanges, + translated: &HashSet, + out_encoding: gimli::Encoding, + out_units: &mut write::UnitTable, + out_strings: &mut write::StringTable, +) -> Result<(), Error> { + let path = di + .wasm_file + .path + .to_owned() + .unwrap_or_else(|| autogenerate_dwarf_wasm_path(di)); + + let (func_names, locals_names) = if let Some(ref name_section) = di.name_section { + ( + Some(&name_section.func_names), + Some(&name_section.locals_names), + ) + } else { + (None, None) + }; + + let (unit, root_id, name_id) = { + let comp_dir_id = out_strings.add(path.parent().expect("path dir").to_str().unwrap()); + let name = path.file_name().expect("path name").to_str().unwrap(); + let name_id = out_strings.add(name); + + let out_program = generate_line_info( + addr_tr, + translated, + out_encoding, + &di.wasm_file, + comp_dir_id, + name_id, + name, + )?; + + let unit_id = out_units.add(write::Unit::new(out_encoding, out_program)); + let unit = out_units.get_mut(unit_id); + + let root_id = unit.root(); + let root = unit.get_mut(root_id); + + let id = out_strings.add(PRODUCER_NAME); + root.set(gimli::DW_AT_producer, write::AttributeValue::StringRef(id)); + root.set(gimli::DW_AT_name, write::AttributeValue::StringRef(name_id)); + root.set( + gimli::DW_AT_stmt_list, + write::AttributeValue::LineProgramRef, + ); + root.set( + gimli::DW_AT_comp_dir, + write::AttributeValue::StringRef(comp_dir_id), + ); + (unit, root_id, name_id) + }; + + let wasm_types = add_wasm_types(unit, root_id, out_strings, vmctx_info); + + for (i, map) in addr_tr.map().iter() { + let index = i.index(); + if translated.contains(&(index as u32)) { + continue; + } + + let start = map.offset as u64; + let end = start + map.len as u64; + let die_id = unit.add(root_id, gimli::DW_TAG_subprogram); + let die = unit.get_mut(die_id); + die.set( + gimli::DW_AT_low_pc, + write::AttributeValue::Address(write::Address::Symbol { + symbol: index, + addend: start as i64, + }), + ); + die.set( + gimli::DW_AT_high_pc, + write::AttributeValue::Udata((end - start) as u64), + ); + + let id = match func_names.and_then(|m| m.get(&(index as u32))) { + Some(n) => out_strings.add(n.to_owned()), + None => out_strings.add(format!("wasm-function[{}]", index)), + }; + + die.set(gimli::DW_AT_name, write::AttributeValue::StringRef(id)); + + die.set( + gimli::DW_AT_decl_file, + write::AttributeValue::StringRef(name_id), + ); + + let f = addr_tr.map().get(i).unwrap(); + let f_start = f.addresses[0].wasm; + let wasm_offset = di.wasm_file.code_section_offset + f_start as u64; + die.set( + gimli::DW_AT_decl_file, + write::AttributeValue::Udata(wasm_offset), + ); + + if let Some(frame_info) = get_function_frame_info(vmctx_info, i, ranges) { + let source_range = addr_tr.func_source_range(i); + generate_vars( + unit, + die_id, + addr_tr, + &frame_info, + &[(source_range.0, source_range.1)], + &wasm_types, + &di.wasm_file.funcs[index], + locals_names.and_then(|m| m.get(&(index as u32))), + out_strings, + ); + } + } + + Ok(()) +} diff --git a/lib/debug-writer/src/transform/unit.rs b/lib/debug-writer/src/transform/unit.rs new file mode 100644 index 00000000000..042095072fd --- /dev/null +++ b/lib/debug-writer/src/transform/unit.rs @@ -0,0 +1,387 @@ +// Copyright 2019 WasmTime Project Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is from the WasmTime project. +// It was copied at revision `907e7aac01af333a0af310ce0472abbc8a9adb6c`. +// +// Changes to this file are copyright of Wasmer inc. unless otherwise indicated +// and are licensed under the Wasmer project's license. +use super::address_transform::AddressTransform; +use super::attr::{clone_die_attributes, FileAttributeContext}; +use super::expression::compile_expression; +use super::line_program::clone_line_program; +use super::range_info_builder::RangeInfoBuilder; +use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info}; +use super::{DebugInputContext, Reader, TransformError}; +use anyhow::Error; +use cranelift_entity::EntityRef; +use gimli::write; +use gimli::{AttributeValue, DebuggingInformationEntry, Unit, UnitOffset}; +use std::collections::{HashMap, HashSet}; +use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges}; + +pub(crate) type PendingDieRef = (write::UnitEntryId, gimli::DwAt, UnitOffset); + +struct InheritedAttr { + stack: Vec<(usize, T)>, +} + +impl InheritedAttr { + fn new() -> Self { + InheritedAttr { stack: Vec::new() } + } + + fn update(&mut self, depth: usize) { + while !self.stack.is_empty() && self.stack.last().unwrap().0 >= depth { + self.stack.pop(); + } + } + + fn push(&mut self, depth: usize, value: T) { + self.stack.push((depth, value)); + } + + fn top(&self) -> Option<&T> { + self.stack.last().map(|entry| &entry.1) + } + + fn is_empty(&self) -> bool { + self.stack.is_empty() + } +} + +fn get_base_type_name( + type_entry: &DebuggingInformationEntry, + unit: &Unit, + context: &DebugInputContext, +) -> Result +where + R: Reader, +{ + // FIXME remove recursion. + match type_entry.attr_value(gimli::DW_AT_type)? { + Some(AttributeValue::UnitRef(ref offset)) => { + let mut entries = unit.entries_at_offset(*offset)?; + entries.next_entry()?; + if let Some(die) = entries.current() { + if let Some(AttributeValue::DebugStrRef(str_offset)) = + die.attr_value(gimli::DW_AT_name)? + { + return Ok(String::from( + context.debug_str.get_str(str_offset)?.to_string()?, + )); + } + match die.tag() { + gimli::DW_TAG_const_type => { + return Ok(format!("const {}", get_base_type_name(die, unit, context)?)); + } + gimli::DW_TAG_pointer_type => { + return Ok(format!("{}*", get_base_type_name(die, unit, context)?)); + } + gimli::DW_TAG_reference_type => { + return Ok(format!("{}&", get_base_type_name(die, unit, context)?)); + } + gimli::DW_TAG_array_type => { + return Ok(format!("{}[]", get_base_type_name(die, unit, context)?)); + } + _ => (), + } + } + } + _ => (), + }; + Ok(String::from("??")) +} + +fn replace_pointer_type( + parent_id: write::UnitEntryId, + comp_unit: &mut write::Unit, + wp_die_id: write::UnitEntryId, + entry: &DebuggingInformationEntry, + unit: &Unit, + context: &DebugInputContext, + out_strings: &mut write::StringTable, + pending_die_refs: &mut Vec<(write::UnitEntryId, gimli::DwAt, UnitOffset)>, +) -> Result +where + R: Reader, +{ + let die_id = comp_unit.add(parent_id, gimli::DW_TAG_structure_type); + let die = comp_unit.get_mut(die_id); + + let name = format!( + "WebAssemblyPtrWrapper<{}>", + get_base_type_name(entry, unit, context)? + ); + die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add(name.as_str())), + ); + die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(4)); + + let p_die_id = comp_unit.add(die_id, gimli::DW_TAG_template_type_parameter); + let p_die = comp_unit.get_mut(p_die_id); + p_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("T")), + ); + p_die.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(wp_die_id), + ); + match entry.attr_value(gimli::DW_AT_type)? { + Some(AttributeValue::UnitRef(ref offset)) => { + pending_die_refs.push((p_die_id, gimli::DW_AT_type, *offset)) + } + _ => (), + } + + let m_die_id = comp_unit.add(die_id, gimli::DW_TAG_member); + let m_die = comp_unit.get_mut(m_die_id); + m_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("__ptr")), + ); + m_die.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(wp_die_id), + ); + m_die.set( + gimli::DW_AT_data_member_location, + write::AttributeValue::Data1(0), + ); + Ok(die_id) +} + +pub(crate) fn clone_unit<'a, R>( + unit: Unit, + context: &DebugInputContext, + addr_tr: &'a AddressTransform, + value_ranges: &'a ValueLabelsRanges, + out_encoding: gimli::Encoding, + module_info: &ModuleVmctxInfo, + out_units: &mut write::UnitTable, + out_strings: &mut write::StringTable, + translated: &mut HashSet, +) -> Result<(), Error> +where + R: Reader, +{ + let mut die_ref_map = HashMap::new(); + let mut pending_die_refs = Vec::new(); + let mut stack = Vec::new(); + + // Iterate over all of this compilation unit's entries. + let mut entries = unit.entries(); + let (mut comp_unit, file_map, cu_low_pc, wp_die_id, vmctx_die_id) = + if let Some((depth_delta, entry)) = entries.next_dfs()? { + assert_eq!(depth_delta, 0); + let (out_line_program, debug_line_offset, file_map) = clone_line_program( + &unit, + entry, + addr_tr, + out_encoding, + context.debug_str, + context.debug_line, + out_strings, + )?; + + if entry.tag() == gimli::DW_TAG_compile_unit { + let unit_id = out_units.add(write::Unit::new(out_encoding, out_line_program)); + let comp_unit = out_units.get_mut(unit_id); + + let root_id = comp_unit.root(); + die_ref_map.insert(entry.offset(), root_id); + + let cu_low_pc = if let Some(AttributeValue::Addr(addr)) = + entry.attr_value(gimli::DW_AT_low_pc)? + { + addr + } else { + // FIXME? return Err(TransformError("No low_pc for unit header").into()); + 0 + }; + + clone_die_attributes( + entry, + context, + addr_tr, + None, + unit.encoding(), + comp_unit, + root_id, + None, + None, + cu_low_pc, + out_strings, + &die_ref_map, + &mut pending_die_refs, + FileAttributeContext::Root(Some(debug_line_offset)), + )?; + + let (wp_die_id, vmctx_die_id) = + add_internal_types(comp_unit, root_id, out_strings, module_info); + + stack.push(root_id); + (comp_unit, file_map, cu_low_pc, wp_die_id, vmctx_die_id) + } else { + return Err(TransformError("Unexpected unit header").into()); + } + } else { + return Ok(()); // empty + }; + let mut skip_at_depth = None; + let mut current_frame_base = InheritedAttr::new(); + let mut current_value_range = InheritedAttr::new(); + let mut current_scope_ranges = InheritedAttr::new(); + while let Some((depth_delta, entry)) = entries.next_dfs()? { + let depth_delta = if let Some((depth, cached)) = skip_at_depth { + let new_depth = depth + depth_delta; + if new_depth > 0 { + skip_at_depth = Some((new_depth, cached)); + continue; + } + skip_at_depth = None; + new_depth + cached + } else { + depth_delta + }; + + if !context + .reachable + .contains(&entry.offset().to_unit_section_offset(&unit)) + { + // entry is not reachable: discarding all its info. + skip_at_depth = Some((0, depth_delta)); + continue; + } + + let new_stack_len = stack.len().wrapping_add(depth_delta as usize); + current_frame_base.update(new_stack_len); + current_scope_ranges.update(new_stack_len); + current_value_range.update(new_stack_len); + let range_builder = if entry.tag() == gimli::DW_TAG_subprogram { + let range_builder = RangeInfoBuilder::from_subprogram_die( + entry, + context, + unit.encoding(), + addr_tr, + cu_low_pc, + )?; + if let RangeInfoBuilder::Function(func_index) = range_builder { + if let Some(frame_info) = + get_function_frame_info(module_info, func_index, value_ranges) + { + current_value_range.push(new_stack_len, frame_info); + } + translated.insert(func_index.index() as u32); + current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr)); + Some(range_builder) + } else { + // FIXME current_scope_ranges.push() + None + } + } else { + let high_pc = entry.attr_value(gimli::DW_AT_high_pc)?; + let ranges = entry.attr_value(gimli::DW_AT_ranges)?; + if high_pc.is_some() || ranges.is_some() { + let range_builder = + RangeInfoBuilder::from(entry, context, unit.encoding(), cu_low_pc)?; + current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr)); + Some(range_builder) + } else { + None + } + }; + + if depth_delta <= 0 { + for _ in depth_delta..1 { + stack.pop(); + } + } else { + assert_eq!(depth_delta, 1); + } + + if let Some(AttributeValue::Exprloc(expr)) = entry.attr_value(gimli::DW_AT_frame_base)? { + if let Some(expr) = compile_expression(&expr, unit.encoding(), None)? { + current_frame_base.push(new_stack_len, expr); + } + } + + let parent = stack.last().unwrap(); + + if entry.tag() == gimli::DW_TAG_pointer_type { + // Wrap pointer types. + // TODO reference types? + let die_id = replace_pointer_type( + *parent, + comp_unit, + wp_die_id, + entry, + &unit, + context, + out_strings, + &mut pending_die_refs, + )?; + stack.push(die_id); + assert_eq!(stack.len(), new_stack_len); + die_ref_map.insert(entry.offset(), die_id); + continue; + } + + let die_id = comp_unit.add(*parent, entry.tag()); + + stack.push(die_id); + assert_eq!(stack.len(), new_stack_len); + die_ref_map.insert(entry.offset(), die_id); + + clone_die_attributes( + entry, + context, + addr_tr, + current_value_range.top(), + unit.encoding(), + &mut comp_unit, + die_id, + range_builder, + current_scope_ranges.top(), + cu_low_pc, + out_strings, + &die_ref_map, + &mut pending_die_refs, + FileAttributeContext::Children(&file_map, current_frame_base.top()), + )?; + + if entry.tag() == gimli::DW_TAG_subprogram && !current_scope_ranges.is_empty() { + append_vmctx_info( + comp_unit, + die_id, + vmctx_die_id, + addr_tr, + current_value_range.top(), + current_scope_ranges.top().expect("range"), + out_strings, + )?; + } + } + for (die_id, attr_name, offset) in pending_die_refs { + let die = comp_unit.get_mut(die_id); + if let Some(unit_id) = die_ref_map.get(&offset) { + die.set(attr_name, write::AttributeValue::ThisUnitEntryRef(*unit_id)); + } else { + // TODO check why loosing DIEs + } + } + Ok(()) +} diff --git a/lib/debug-writer/src/transform/utils.rs b/lib/debug-writer/src/transform/utils.rs new file mode 100644 index 00000000000..207ec654079 --- /dev/null +++ b/lib/debug-writer/src/transform/utils.rs @@ -0,0 +1,172 @@ +// Copyright 2019 WasmTime Project Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is from the WasmTime project. +// It was copied at revision `cc6e8e1af25e5f9b64e183970d50f62c8338f259`. +// +// Changes to this file are copyright of Wasmer inc. unless otherwise indicated +// and are licensed under the Wasmer project's license. +use super::address_transform::AddressTransform; +use super::expression::{CompiledExpression, FunctionFrameInfo}; +use anyhow::Error; +// TODO: review +use wasmer_runtime_core::types::FuncIndex; +use gimli::write; +use wasmer_runtime_core::{module::ModuleInfo, state::CodeVersion}; +// TODO: ValueLabelsRanges + +pub(crate) fn add_internal_types( + comp_unit: &mut write::Unit, + root_id: write::UnitEntryId, + out_strings: &mut write::StringTable, + module_info: &ModuleInfo, +) -> (write::UnitEntryId, write::UnitEntryId) { + let wp_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type); + let wp_die = comp_unit.get_mut(wp_die_id); + wp_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("WebAssemblyPtr")), + ); + wp_die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(4)); + wp_die.set( + gimli::DW_AT_encoding, + write::AttributeValue::Encoding(gimli::DW_ATE_unsigned), + ); + + let memory_byte_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type); + let memory_byte_die = comp_unit.get_mut(memory_byte_die_id); + memory_byte_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("u8")), + ); + memory_byte_die.set( + gimli::DW_AT_encoding, + write::AttributeValue::Encoding(gimli::DW_ATE_unsigned), + ); + memory_byte_die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(1)); + + let memory_bytes_die_id = comp_unit.add(root_id, gimli::DW_TAG_pointer_type); + let memory_bytes_die = comp_unit.get_mut(memory_bytes_die_id); + memory_bytes_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("u8*")), + ); + memory_bytes_die.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(memory_byte_die_id), + ); + + let memory_offset = unimplemented!("TODO"); + let vmctx_die_id = comp_unit.add(root_id, gimli::DW_TAG_structure_type); + let vmctx_die = comp_unit.get_mut(vmctx_die_id); + vmctx_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("WasmerVMContext")), + ); + vmctx_die.set( + gimli::DW_AT_byte_size, + write::AttributeValue::Data4(memory_offset as u32 + 8), + ); + + let m_die_id = comp_unit.add(vmctx_die_id, gimli::DW_TAG_member); + let m_die = comp_unit.get_mut(m_die_id); + m_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("memory")), + ); + m_die.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(memory_bytes_die_id), + ); + m_die.set( + gimli::DW_AT_data_member_location, + write::AttributeValue::Udata(memory_offset as u64), + ); + + let vmctx_ptr_die_id = comp_unit.add(root_id, gimli::DW_TAG_pointer_type); + let vmctx_ptr_die = comp_unit.get_mut(vmctx_ptr_die_id); + vmctx_ptr_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("WasmerVMContext*")), + ); + vmctx_ptr_die.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(vmctx_die_id), + ); + + (wp_die_id, vmctx_ptr_die_id) +} + +pub(crate) fn append_vmctx_info( + comp_unit: &mut write::Unit, + parent_id: write::UnitEntryId, + vmctx_die_id: write::UnitEntryId, + addr_tr: &AddressTransform, + frame_info: Option<&FunctionFrameInfo>, + scope_ranges: &[(u64, u64)], + out_strings: &mut write::StringTable, +) -> Result<(), Error> { + let loc = { + let endian = gimli::RunTimeEndian::Little; + + let expr = CompiledExpression::vmctx(); + let mut locs = Vec::new(); + for (begin, length, data) in + expr.build_with_locals(scope_ranges, addr_tr, frame_info, endian) + { + locs.push(write::Location::StartLength { + begin, + length, + data, + }); + } + let list_id = comp_unit.locations.add(write::LocationList(locs)); + write::AttributeValue::LocationListRef(list_id) + }; + + let var_die_id = comp_unit.add(parent_id, gimli::DW_TAG_variable); + let var_die = comp_unit.get_mut(var_die_id); + var_die.set( + gimli::DW_AT_name, + write::AttributeValue::StringRef(out_strings.add("__vmctx")), + ); + var_die.set( + gimli::DW_AT_type, + write::AttributeValue::ThisUnitEntryRef(vmctx_die_id), + ); + var_die.set(gimli::DW_AT_location, loc); + + Ok(()) +} + +pub(crate) fn get_function_frame_info<'a, 'b, 'c>( + module_info: &'b ModuleInfo, + func_index: FuncIndex, + value_ranges: &'c ValueLabelsRanges, +) -> Option> +where + 'b: 'a, + 'c: 'a, +{ + if let Some(value_ranges) = value_ranges.get(func_index) { + let frame_info = FunctionFrameInfo { + value_ranges, + memory_offset: module_info.memory_offset, + stack_slots: &module_info.stack_slots[func_index], + }; + Some(frame_info) + } else { + None + } +} diff --git a/lib/debug-writer/src/write_debug_info.rs b/lib/debug-writer/src/write_debug_info.rs new file mode 100644 index 00000000000..365643733b8 --- /dev/null +++ b/lib/debug-writer/src/write_debug_info.rs @@ -0,0 +1,163 @@ +// Copyright 2019 WasmTime Project Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file is from the WasmTime project. It reads DWARF info from a Wasm module. +// It was copied at revision `ea73d4fa91399349fd8d42e833363a0b1cad9f1c`. +// +// Changes to this file are copyright of Wasmer inc. unless otherwise indicated +// and are licensed under the Wasmer project's license. + +use faerie::artifact::{Decl, SectionKind}; +use faerie::*; +use gimli::write::{Address, Dwarf, EndianVec, Result, Sections, Writer}; +use gimli::{RunTimeEndian, SectionId}; + +#[derive(Clone)] +struct DebugReloc { + offset: u32, + size: u8, + name: String, + addend: i64, +} + +pub enum ResolvedSymbol { + PhysicalAddress(u64), + Reloc { name: String, addend: i64 }, +} + +pub trait SymbolResolver { + fn resolve_symbol(&self, symbol: usize, addend: i64) -> ResolvedSymbol; +} + +pub fn emit_dwarf( + artifact: &mut Artifact, + mut dwarf: Dwarf, + symbol_resolver: &dyn SymbolResolver, +) -> anyhow::Result<()> { + let endian = RunTimeEndian::Little; + + let mut sections = Sections::new(WriterRelocate::new(endian, symbol_resolver)); + dwarf.write(&mut sections)?; + sections.for_each_mut(|id, s| -> anyhow::Result<()> { + artifact.declare_with( + id.name(), + Decl::section(SectionKind::Debug), + s.writer.take(), + ) + })?; + sections.for_each_mut(|id, s| -> anyhow::Result<()> { + for reloc in &s.relocs { + artifact.link_with( + faerie::Link { + from: id.name(), + to: &reloc.name, + at: u64::from(reloc.offset), + }, + faerie::Reloc::Debug { + size: reloc.size, + addend: reloc.addend as i32, + }, + )?; + } + Ok(()) + })?; + Ok(()) +} + +#[derive(Clone)] +pub struct WriterRelocate<'a> { + relocs: Vec, + writer: EndianVec, + symbol_resolver: &'a dyn SymbolResolver, +} + +impl<'a> WriterRelocate<'a> { + pub fn new(endian: RunTimeEndian, symbol_resolver: &'a dyn SymbolResolver) -> Self { + WriterRelocate { + relocs: Vec::new(), + writer: EndianVec::new(endian), + symbol_resolver, + } + } +} + +impl<'a> Writer for WriterRelocate<'a> { + type Endian = RunTimeEndian; + + fn endian(&self) -> Self::Endian { + self.writer.endian() + } + + fn len(&self) -> usize { + self.writer.len() + } + + fn write(&mut self, bytes: &[u8]) -> Result<()> { + self.writer.write(bytes) + } + + fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> { + self.writer.write_at(offset, bytes) + } + + fn write_address(&mut self, address: Address, size: u8) -> Result<()> { + match address { + Address::Constant(val) => self.write_udata(val, size), + Address::Symbol { symbol, addend } => { + match self.symbol_resolver.resolve_symbol(symbol, addend as i64) { + ResolvedSymbol::PhysicalAddress(addr) => self.write_udata(addr, size), + ResolvedSymbol::Reloc { name, addend } => { + let offset = self.len() as u64; + self.relocs.push(DebugReloc { + offset: offset as u32, + size, + name, + addend, + }); + self.write_udata(addend as u64, size) + } + } + } + } + } + + fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> { + let offset = self.len() as u32; + let name = section.name().to_string(); + self.relocs.push(DebugReloc { + offset, + size, + name, + addend: val as i64, + }); + self.write_udata(val as u64, size) + } + + fn write_offset_at( + &mut self, + offset: usize, + val: usize, + section: SectionId, + size: u8, + ) -> Result<()> { + let name = section.name().to_string(); + self.relocs.push(DebugReloc { + offset: offset as u32, + size, + name, + addend: val as i64, + }); + self.write_udata_at(offset, val as u64, size) + } +} diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index 9322b9d5bee..e45c32fcd4e 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -250,6 +250,7 @@ impl< cache_gen, runnable_module: Arc::new(Box::new(exec_context)), info: Arc::try_unwrap(info).unwrap().into_inner().unwrap(), + debug_info: None, }) } diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index cf99abc274f..cfe9aaa3c78 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -20,12 +20,18 @@ use indexmap::IndexMap; use std::collections::HashMap; use std::sync::Arc; +#[doc(hidden)] +pub struct DebugInfo { + +} + /// This is used to instantiate a new WebAssembly module. #[doc(hidden)] pub struct ModuleInner { pub runnable_module: Arc>, pub cache_gen: Box, pub info: ModuleInfo, + pub debug_info: Option, } /// Container for module data including memories, globals, tables, imports, and exports. From 42132c42b617faed3ac54bd4fa83553eba30424d Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 3 Feb 2020 16:01:23 -0800 Subject: [PATCH 02/26] Restructure to use external crate, add integration --- Cargo.lock | 61 ++++++++-- lib/clif-backend/Cargo.toml | 1 + lib/clif-backend/src/code.rs | 30 ++++- lib/clif-backend/src/module.rs | 2 +- lib/debug-writer/Cargo.toml | 3 +- lib/debug-writer/src/lib.rs | 186 ++---------------------------- lib/runtime-core/Cargo.toml | 5 + lib/runtime-core/src/backend.rs | 7 ++ lib/runtime-core/src/codegen.rs | 47 +++++++- lib/runtime-core/src/jit_debug.rs | 92 +++++++++++++++ lib/runtime-core/src/lib.rs | 1 + lib/runtime-core/src/types.rs | 11 ++ src/bin/wasmer.rs | 1 + 13 files changed, 256 insertions(+), 191 deletions(-) create mode 100644 lib/runtime-core/src/jit_debug.rs diff --git a/Cargo.lock b/Cargo.lock index f4001ad09cd..e484a5eeda0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -456,6 +456,22 @@ dependencies = [ "libc", ] +[[package]] +name = "faerie" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f902f2af041f6c7177a2a04f805687cdc71e69c7cbef059a2755d8923f4cd7a8" +dependencies = [ + "anyhow", + "goblin 0.1.3", + "indexmap", + "log", + "scroll 0.10.1", + "string-interner", + "target-lexicon 0.9.0", + "thiserror", +] + [[package]] name = "faerie" version = "0.14.0" @@ -786,6 +802,12 @@ dependencies = [ "x11-dl", ] +[[package]] +name = "more-asserts" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238" + [[package]] name = "nix" version = "0.15.0" @@ -1724,6 +1746,20 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +[[package]] +name = "wasm-debug" +version = "0.1.0" +dependencies = [ + "anyhow", + "cranelift-entity", + "faerie 0.13.0", + "gimli", + "more-asserts", + "target-lexicon 0.9.0", + "thiserror", + "wasmparser 0.39.3", +] + [[package]] name = "wasmer" version = "0.13.1" @@ -1772,11 +1808,12 @@ dependencies = [ "serde_bytes", "serde_derive", "target-lexicon 0.9.0", + "wasm-debug", "wasmer-clif-fork-frontend", "wasmer-clif-fork-wasm", "wasmer-runtime-core", "wasmer-win-exception-handler", - "wasmparser", + "wasmparser 0.45.0", "winapi", ] @@ -1803,18 +1840,19 @@ dependencies = [ "log", "thiserror", "wasmer-clif-fork-frontend", - "wasmparser", + "wasmparser 0.45.0", ] [[package]] name = "wasmer-debug-writer" version = "0.13.1" dependencies = [ - "faerie", + "faerie 0.14.0", "gimli", - "target-lexicon 0.10.0", + "target-lexicon 0.9.0", + "wasm-debug", "wasmer-runtime-core", - "wasmparser", + "wasmparser 0.45.0", ] [[package]] @@ -1876,7 +1914,7 @@ dependencies = [ "smallvec 0.6.13", "wabt", "wasmer-runtime-core", - "wasmparser", + "wasmparser 0.45.0", "winapi", ] @@ -1946,6 +1984,7 @@ dependencies = [ "bincode", "blake3", "cc", + "cranelift-entity", "digest", "errno", "hex", @@ -1961,7 +2000,9 @@ dependencies = [ "serde_bytes", "serde_derive", "smallvec 0.6.13", - "wasmparser", + "target-lexicon 0.9.0", + "wasm-debug", + "wasmparser 0.45.0", "winapi", ] @@ -2058,6 +2099,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "wasmparser" +version = "0.39.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c702914acda5feeeffbc29e4d953e5b9ce79d8b98da4dbf18a77086e116c5470" + [[package]] name = "wasmparser" version = "0.45.0" diff --git a/lib/clif-backend/Cargo.toml b/lib/clif-backend/Cargo.toml index 83818ec11ec..9bd5286e4c4 100644 --- a/lib/clif-backend/Cargo.toml +++ b/lib/clif-backend/Cargo.toml @@ -23,6 +23,7 @@ byteorder = "1.3.2" nix = "0.15.0" libc = "0.2.60" rayon = "1.1" +wasm-debug = { version = "0.1", path = "../../../Dev/wasm-debug" } # Dependencies for caching. [dependencies.serde] diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index 74e59d3445b..24e36e58f66 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -6,7 +6,7 @@ use crate::{ resolver::FuncResolverBuilder, signal::Caller, trampoline::Trampolines, }; -use cranelift_codegen::entity::EntityRef; +use cranelift_codegen::entity::{EntityRef, PrimaryMap}; use cranelift_codegen::ir::{self, Ebb, Function, InstBuilder}; use cranelift_codegen::isa::CallConv; use cranelift_codegen::{cursor::FuncCursor, isa}; @@ -141,9 +141,33 @@ impl ModuleCodeGenerator fn finalize( self, module_info: &ModuleInfo, - ) -> Result<(Caller, Box), CodegenError> { + ) -> Result<((Caller, Option), Box), CodegenError> { + + use wasm_debug::types::{CompiledFunctionData}; + let mut debug_metadata = wasmer_runtime_core::codegen::DebugMetadata { + func_info: PrimaryMap::new(), + inst_info: PrimaryMap::new(), + }; let mut func_bodies: Map = Map::new(); for f in self.functions.into_iter() { + // TODO: review + // if container is stable, then first and last probably makes more sense here, + let min_srcloc = f.func.srclocs.iter().map(|x| x.1.bits()).min().unwrap_or_default(); + let max_srcloc = f.func.srclocs.iter().map(|x| x.1.bits()).max().unwrap_or_default(); + let entry = CompiledFunctionData { + instructions: vec![], + start: wasm_debug::types::SourceLoc::new(min_srcloc), + end: wasm_debug::types::SourceLoc::new(max_srcloc), + compiled_offset: 0, + compiled_size: 0, + }; + debug_metadata.func_info.push(entry); + /*let mut map = std::collections::HashMap::new(); + for (k, v) in f.func.srclocs { + map. + } + debug_metadata.inst_info.push(map);*/ + func_bodies.push(f.func); } @@ -171,7 +195,7 @@ impl ModuleCodeGenerator )); Ok(( - Caller::new(handler_data, trampolines, func_resolver), + (Caller::new(handler_data, trampolines, func_resolver), Some(debug_metadata)), cache_gen, )) } diff --git a/lib/clif-backend/src/module.rs b/lib/clif-backend/src/module.rs index 99d40f0459f..11999ba02f6 100644 --- a/lib/clif-backend/src/module.rs +++ b/lib/clif-backend/src/module.rs @@ -56,7 +56,7 @@ macro_rules! convert_clif_to_runtime_index { ($clif_index:ident, $runtime_index:ident) => { impl From> for $runtime_index { fn from(clif_index: Converter) -> Self { - $runtime_index::new(clif_index.0.index()) + <$runtime_index as TypedIndex>::new(clif_index.0.index()) } } diff --git a/lib/debug-writer/Cargo.toml b/lib/debug-writer/Cargo.toml index 6f9019617a2..e3f6dba4cf9 100644 --- a/lib/debug-writer/Cargo.toml +++ b/lib/debug-writer/Cargo.toml @@ -11,6 +11,7 @@ license = "MIT" [dependencies] faerie = "0.14" gimli = "0.20" -target-lexicon = "0.10" +target-lexicon = "0.9" +wasm-debug = { version = "0.1.0", path = "../../../Dev/wasm-debug" } wasmer-runtime-core = { path = "../runtime-core", version = "0.13.1" } wasmparser = "0.45" diff --git a/lib/debug-writer/src/lib.rs b/lib/debug-writer/src/lib.rs index 25928ff92ef..66a2b2513ed 100644 --- a/lib/debug-writer/src/lib.rs +++ b/lib/debug-writer/src/lib.rs @@ -3,14 +3,8 @@ use std::str::FromStr; use std::ptr; use std::ffi::c_void; -mod read_debug_info; -mod write_debug_info; -mod gc; -mod transform; - -pub use crate::read_debug_info::{read_debug_info, DebugInfoData, WasmFileInfo}; -pub use crate::write_debug_info::{emit_dwarf, ResolvedSymbol, SymbolResolver}; -use crate::transform::WasmTypesDieRefs; +pub use wasm_debug::{read_debuginfo, DebugInfoData, WasmFileInfo}; +pub use wasm_debug::{emit_dwarf, ResolvedSymbol, SymbolResolver}; use target_lexicon::{Triple, Architecture, Vendor, OperatingSystem, Environment, BinaryFormat}; use gimli::write::{self, DwarfUnit, Sections, Address, RangeList, EndianVec, AttributeValue, Range}; @@ -59,6 +53,7 @@ impl<'a> SymbolResolver for ImageRelocResolver<'a> { } } +/* // the structure of this function and some of its details come from WasmTime // TODO: attribute pub fn generate_dwarf(module_info: &ModuleInfo, debug_info_data: &DebugInfoData, code_version: &CodeVersion, platform: Triple) -> Result, String> { @@ -153,6 +148,7 @@ pub fn generate_dwarf(module_info: &ModuleInfo, debug_info_data: &DebugInfoData, } Ok(vec![]) } +*/ // converts existing dwarf into a usable form with metadata from the JIT fn reprocess_dwarf(module_info: &ModuleInfo, debug_info_data: &DebugInfoData, code_version: &CodeVersion, platform: Triple) -> Option { @@ -165,174 +161,10 @@ fn reprocess_dwarf(module_info: &ModuleInfo, debug_info_data: &DebugInfoData, co // where is this documented? // we need to pass in target triple, isa config, memories/pointers to memories, ranges of where things are, // and info like function names -pub fn generate_debug_sections_image() -> Option> { - None -} - -// do it - -// this code copied from WasmTime, TODO: give attribution - - -// The `emit_wasm_types` function is a derative work of code in WasmTime: -// TODO: update attributions file and/or do clean reimplementation of this logic -// -// Copyright 2019 WasmTime Project Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -fn emit_wasm_types(unit: &mut write::Unit, root_id: write::UnitEntryId, string_table: &mut write::StringTable) -> WasmTypesDieRefs { - macro_rules! def_type { - ($id:literal, $size:literal, $enc:path) => {{ - let die_id = unit.add(root_id, gimli::DW_TAG_base_type); - let die = unit.get_mut(die_id); - die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(string_table.add($id)), - ); - die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1($size)); - die.set(gimli::DW_AT_encoding, write::AttributeValue::Encoding($enc)); - die_id - }}; - } - let vmctx_id = { - // TODO: get memory_offset - let memory_offset = 0; - let vmctx_die_id = unit.add(root_id, gimli::DW_TAG_structure_type); - let vmctx_die = unit.get_mut(vmctx_die_id); - vmctx_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(string_table.add("WasmerVMContext")), - ); - vmctx_die.set( - gimli::DW_AT_byte_size, - write::AttributeValue::Data4(memory_offset as u32 + 8), - ); - let vmctx_ptr_id = unit.add(root_id, gimli::DW_TAG_pointer_type); - let vmctx_ptr_die = unit.get_mut(vmctx_ptr_id); - vmctx_ptr_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(string_table.add("WasmerVMContext*")), - ); - vmctx_ptr_die.set( - gimli::DW_AT_type, - write::AttributeValue::ThisUnitEntryRef(vmctx_die_id), - ); - - vmctx_ptr_id - }; - - let i32_id = def_type!("i32", 4, gimli::DW_ATE_signed); - let i64_id = def_type!("i64", 8, gimli::DW_ATE_signed); - let i128_id = def_type!("i128", 16, gimli::DW_ATE_signed); - let f32_id = def_type!("f32", 4, gimli::DW_ATE_float); - let f64_id = def_type!("f64", 8, gimli::DW_ATE_float); - - WasmTypesDieRefs { - vmctx: vmctx_id, - i32: i32_id, - i64: i64_id, - i128: i128_id, - f32: f32_id, - f64: f64_id, - } -} - - -// ============================================================================= -// LLDB hook magic: -// see lldb/packages/Python/lldbsuite/test/functionalities/jitloader_gdb in -// llvm repo for example -// -// see also https://sourceware.org/gdb/current/onlinedocs/gdb.html#JIT-Interface +pub fn generate_debug_sections_image(bytes: &[u8]) -> Option> { + let debug_info = read_debuginfo(bytes); + dbg!(debug_info); -#[inline(never)] -pub extern "C" fn __jit_debug_register_code() { - -} - -#[allow(non_camel_case_types)] -#[derive(Debug)] -#[repr(u32)] -pub enum JITAction { JIT_NOACTION = 0, JIT_REGISTER_FN = 1, JIT_UNREGISTER_FN = 2 } - -#[no_mangle] -#[repr(C)] -pub struct JITCodeEntry { - next: *mut JITCodeEntry, - prev: *mut JITCodeEntry, - // TODO: use CStr here? - symfile_addr: *const u8, - symfile_size: u64, -} - -impl Default for JITCodeEntry { - fn default() -> Self { - Self { - next: ptr::null_mut(), - prev: ptr::null_mut(), - symfile_addr: ptr::null(), - symfile_size: 0, - } - } -} - -#[no_mangle] -#[repr(C)] -pub struct JitDebugDescriptor { - version: u32, - action_flag: u32, - relevant_entry: *mut JITCodeEntry, - first_entry: *mut JITCodeEntry, -} - -#[no_mangle] -#[allow(non_upper_case_globals)] -pub static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { - version: 1, - action_flag: JITAction::JIT_NOACTION as _, - relevant_entry: ptr::null_mut(), - first_entry: ptr::null_mut(), -}; - -/// Prepend an item to the front of the `__jit_debug_descriptor` entry list -/// -/// # Safety -/// - Pointer to [`JITCodeEntry`] should point to a valid entry and stay alive -/// for the 'static lifetime -unsafe fn push_front(jce: *mut JITCodeEntry) { - if __jit_debug_descriptor.first_entry.is_null() { - __jit_debug_descriptor.first_entry = jce; - } else { - let old_first = __jit_debug_descriptor.first_entry; - debug_assert!((*old_first).prev.is_null()); - (*jce).next = old_first; - (*old_first).prev = jce; - __jit_debug_descriptor.first_entry = jce; - } -} - -pub fn register_new_jit_code_entry(bytes: &'static [u8], action: JITAction) -> *mut JITCodeEntry { - let entry: *mut JITCodeEntry = Box::into_raw(Box::new(JITCodeEntry { - symfile_addr: bytes.as_ptr(), - symfile_size: bytes.len() as _, - ..JITCodeEntry::default() - })); - - unsafe { - push_front(entry); - __jit_debug_descriptor.relevant_entry = entry; - __jit_debug_descriptor.action_flag = action as u32; - } - - entry + //emit_debugsections_image(X86_64_OSX, 8, debug_info, ) + None } diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index 7f07a9d2d77..da0881baee8 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -20,6 +20,11 @@ libc = "0.2.60" hex = "0.4" smallvec = "0.6" bincode = "1.1" +# todo: review +cranelift-entity = "0.52" +# todo, make optional, etc +wasm-debug = { version = "0.1.0", path = "../../../Dev/wasm-debug" } +target-lexicon = "0.9" [dependencies.indexmap] version = "1.2" diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index db6b8ef65f9..2e010a2ea5c 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -120,6 +120,8 @@ pub struct CompilerConfig { pub cpu_features: Option, pub backend_specific_config: Option, + + pub generate_debug_info: bool, } /// An exception table for a `RunnableModule`. @@ -210,6 +212,11 @@ pub trait RunnableModule: Send + Sync { None } + /// TODO: document before shipppping + fn get_local_function_pointers_and_lengths(&self) -> Option> { + None + } + /// Returns the inline breakpoint size corresponding to an Architecture (None in case is not implemented) fn get_inline_breakpoint_size(&self, _arch: Architecture) -> Option { None diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index e45c32fcd4e..c72ca25df5e 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -114,12 +114,21 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, /// Creates a new function and returns the function-scope code generator for it. fn next_function(&mut self, module_info: Arc>) -> Result<&mut FCG, E>; /// Finalizes this module. - fn finalize(self, module_info: &ModuleInfo) -> Result<(RM, Box), E>; + fn finalize(self, module_info: &ModuleInfo) -> Result<((RM, Option), Box), E>; /// Creates a module from cache. unsafe fn from_cache(cache: Artifact, _: Token) -> Result; } +use cranelift_entity::PrimaryMap; +/// missing documentation! +pub struct DebugMetadata { + ///f unc info + pub func_info: PrimaryMap, + /// inst_info + pub inst_info: PrimaryMap, +} + /// A streaming compiler which is designed to generated code for a module based on a stream /// of wasm parser events. pub struct StreamingCompiler< @@ -221,6 +230,7 @@ impl< CGEN: Fn() -> MiddlewareChain, > Compiler for StreamingCompiler { + #[allow(unused_variables)] fn compile( &self, wasm: &[u8], @@ -231,6 +241,7 @@ impl< validate_with_features(wasm, &compiler_config.features)?; } + let mut mcg = match MCG::backend_id() { "llvm" => MCG::new_with_target( compiler_config.triple.clone(), @@ -241,11 +252,43 @@ impl< }; let mut chain = (self.middleware_chain_generator)(); let info = crate::parse::read_module(wasm, &mut mcg, &mut chain, &compiler_config)?; - let (exec_context, cache_gen) = + let ((exec_context, debug_metadata), cache_gen) = mcg.finalize(&info.read().unwrap()) .map_err(|x| CompileError::InternalError { msg: format!("{:?}", x), })?; + + use target_lexicon::{Triple, Architecture, Vendor, OperatingSystem, Environment, BinaryFormat}; + const X86_64_OSX: Triple = Triple { + architecture: Architecture::X86_64, + vendor: Vendor::Apple, + operating_system: OperatingSystem::Darwin, + environment: Environment::Unknown, + binary_format: BinaryFormat::Macho, + }; + + if compiler_config.generate_debug_info { + let debug_metadata = debug_metadata.expect("debug metadata"); + let debug_info = wasm_debug::read_debuginfo(wasm); + let extra_info = wasm_debug::types::ModuleVmctxInfo { + memory_offset: 0, + stack_slot_offsets: cranelift_entity::PrimaryMap::new(), + }; + // lazy type hack (TODO:) + let compiled_fn_map = unsafe { std::mem::transmute(debug_metadata.func_info) }; + let range_map = unsafe { std::mem::transmute(debug_metadata.inst_info) }; + let raw_func_slice = vec![];//exec_context.get_local_function_pointers_and_lengths().expect("raw func slice"); + dbg!("DEBUG INFO GENERATED"); + let debug_image = wasm_debug::emit_debugsections_image(X86_64_OSX, + std::mem::size_of::() as u8, + &debug_info, + &extra_info, + &compiled_fn_map, + &range_map, + &raw_func_slice).expect("make debug image"); + + crate::jit_debug::register_new_jit_code_entry(&debug_image, crate::jit_debug::JITAction::JIT_REGISTER_FN); + } Ok(ModuleInner { cache_gen, runnable_module: Arc::new(Box::new(exec_context)), diff --git a/lib/runtime-core/src/jit_debug.rs b/lib/runtime-core/src/jit_debug.rs new file mode 100644 index 00000000000..8a56fcd7fac --- /dev/null +++ b/lib/runtime-core/src/jit_debug.rs @@ -0,0 +1,92 @@ +#![allow(missing_docs)] +use std::ptr; + +// ============================================================================= +// LLDB hook magic: +// see lldb/packages/Python/lldbsuite/test/functionalities/jitloader_gdb in +// llvm repo for example +// +// see also https://sourceware.org/gdb/current/onlinedocs/gdb.html#JIT-Interface + +#[inline(never)] +pub extern "C" fn __jit_debug_register_code() { + +} + +#[allow(non_camel_case_types)] +#[derive(Debug)] +#[repr(u32)] +pub enum JITAction { JIT_NOACTION = 0, JIT_REGISTER_FN = 1, JIT_UNREGISTER_FN = 2 } + +#[no_mangle] +#[repr(C)] +pub struct JITCodeEntry { + next: *mut JITCodeEntry, + prev: *mut JITCodeEntry, + // TODO: use CStr here? + symfile_addr: *const u8, + symfile_size: u64, +} + +impl Default for JITCodeEntry { + fn default() -> Self { + Self { + next: ptr::null_mut(), + prev: ptr::null_mut(), + symfile_addr: ptr::null(), + symfile_size: 0, + } + } +} + +#[no_mangle] +#[repr(C)] +pub struct JitDebugDescriptor { + version: u32, + action_flag: u32, + relevant_entry: *mut JITCodeEntry, + first_entry: *mut JITCodeEntry, +} + +#[no_mangle] +#[allow(non_upper_case_globals)] +pub static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { + version: 1, + action_flag: JITAction::JIT_NOACTION as _, + relevant_entry: ptr::null_mut(), + first_entry: ptr::null_mut(), +}; + +/// Prepend an item to the front of the `__jit_debug_descriptor` entry list +/// +/// # Safety +/// - Pointer to [`JITCodeEntry`] should point to a valid entry and stay alive +/// for the 'static lifetime +unsafe fn push_front(jce: *mut JITCodeEntry) { + if __jit_debug_descriptor.first_entry.is_null() { + __jit_debug_descriptor.first_entry = jce; + } else { + let old_first = __jit_debug_descriptor.first_entry; + debug_assert!((*old_first).prev.is_null()); + (*jce).next = old_first; + (*old_first).prev = jce; + __jit_debug_descriptor.first_entry = jce; + } +} + +// deleted static (added and deleted by Mark): TODO: +pub fn register_new_jit_code_entry(bytes: &[u8], action: JITAction) -> *mut JITCodeEntry { + let entry: *mut JITCodeEntry = Box::into_raw(Box::new(JITCodeEntry { + symfile_addr: bytes.as_ptr(), + symfile_size: bytes.len() as _, + ..JITCodeEntry::default() + })); + + unsafe { + push_front(entry); + __jit_debug_descriptor.relevant_entry = entry; + __jit_debug_descriptor.action_flag = action as u32; + } + + entry +} diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index f211e80dbe6..46f46f34aaa 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -69,6 +69,7 @@ pub mod fault; pub mod state; #[cfg(feature = "managed")] pub mod tiering; +pub mod jit_debug; use self::error::CompileResult; #[doc(inline)] diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs index 1b19bbc3d91..3878e7eac33 100644 --- a/lib/runtime-core/src/types.rs +++ b/lib/runtime-core/src/types.rs @@ -502,6 +502,17 @@ define_map_index![ | imported: ImportedFuncIndex, ImportedMemoryIndex, ImportedTableIndex, ImportedGlobalIndex, ]; +// lol +impl cranelift_entity::EntityRef for FuncIndex { + fn index(self) -> usize { + self.0 as usize + } + + fn new(x: usize) -> Self { + Self(x as u32) + } +} + #[rustfmt::skip] macro_rules! define_local_or_import { ($ty:ident, $local_ty:ident, $imported_ty:ident, $imports:ident) => { diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 5d5bb6e42a6..761daec25a5 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -719,6 +719,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { track_state, features: options.features.into_backend_features(), backend_specific_config, + generate_debug_info: true, ..Default::default() }, &*compiler, From 0564000da3d84269f1d114ca9ea3356a94745383 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 11 Feb 2020 09:52:21 -0800 Subject: [PATCH 03/26] Add wip debugging code --- lib/clif-backend/src/code.rs | 73 +++++---- lib/clif-backend/src/lib.rs | 2 +- lib/clif-backend/src/module.rs | 1 - lib/clif-backend/src/resolver.rs | 180 ++++++++++++++++++----- lib/debug-writer/src/lib.rs | 23 ++- lib/middleware-common/src/block_trace.rs | 1 + lib/middleware-common/src/call_trace.rs | 1 + lib/middleware-common/src/metering.rs | 1 + lib/runtime-core/src/codegen.rs | 83 ++++++----- lib/runtime-core/src/instance.rs | 5 + lib/runtime-core/src/jit_debug.rs | 29 +++- lib/runtime-core/src/lib.rs | 2 +- lib/runtime-core/src/memory/mod.rs | 2 + lib/runtime-core/src/module.rs | 18 ++- lib/runtime-core/src/parse.rs | 158 +++++++++++--------- lib/runtime-core/src/vm.rs | 16 +- 16 files changed, 399 insertions(+), 196 deletions(-) diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index 24e36e58f66..a7cb465174b 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -6,8 +6,8 @@ use crate::{ resolver::FuncResolverBuilder, signal::Caller, trampoline::Trampolines, }; -use cranelift_codegen::entity::{EntityRef, PrimaryMap}; -use cranelift_codegen::ir::{self, Ebb, Function, InstBuilder}; +use cranelift_codegen::entity::EntityRef; +use cranelift_codegen::ir::{self, Ebb, Function, InstBuilder, ValueLabel}; use cranelift_codegen::isa::CallConv; use cranelift_codegen::{cursor::FuncCursor, isa}; use cranelift_frontend::{FunctionBuilder, Position, Variable}; @@ -71,6 +71,7 @@ impl ModuleCodeGenerator fn next_function( &mut self, module_info: Arc>, + loc: (u32, u32), ) -> Result<&mut CraneliftFunctionCodeGenerator, CodegenError> { // define_function_body( @@ -101,8 +102,12 @@ impl ModuleCodeGenerator target_config: self.isa.frontend_config().clone(), clif_signatures: self.clif_signatures.clone(), }, + start: loc.0, + end: loc.1, }; + func_env.func.collect_debug_info(); + debug_assert_eq!(func_env.func.dfg.num_ebbs(), 0, "Function must be empty"); debug_assert_eq!(func_env.func.dfg.num_insts(), 0, "Function must be empty"); @@ -112,8 +117,7 @@ impl ModuleCodeGenerator &mut func_env.position, ); - // TODO srcloc - //builder.set_srcloc(cur_srcloc(&reader)); + builder.set_srcloc(ir::SourceLoc::new(loc.0)); let entry_block = builder.create_ebb(); builder.append_ebb_params_for_function_params(entry_block); @@ -141,37 +145,19 @@ impl ModuleCodeGenerator fn finalize( self, module_info: &ModuleInfo, - ) -> Result<((Caller, Option), Box), CodegenError> { - - use wasm_debug::types::{CompiledFunctionData}; - let mut debug_metadata = wasmer_runtime_core::codegen::DebugMetadata { - func_info: PrimaryMap::new(), - inst_info: PrimaryMap::new(), - }; - let mut func_bodies: Map = Map::new(); + ) -> Result< + ( + (Caller, Option), + Box, + ), + CodegenError, + > { + let mut func_bodies: Map = Map::new(); for f in self.functions.into_iter() { - // TODO: review - // if container is stable, then first and last probably makes more sense here, - let min_srcloc = f.func.srclocs.iter().map(|x| x.1.bits()).min().unwrap_or_default(); - let max_srcloc = f.func.srclocs.iter().map(|x| x.1.bits()).max().unwrap_or_default(); - let entry = CompiledFunctionData { - instructions: vec![], - start: wasm_debug::types::SourceLoc::new(min_srcloc), - end: wasm_debug::types::SourceLoc::new(max_srcloc), - compiled_offset: 0, - compiled_size: 0, - }; - debug_metadata.func_info.push(entry); - /*let mut map = std::collections::HashMap::new(); - for (k, v) in f.func.srclocs { - map. - } - debug_metadata.inst_info.push(map);*/ - - func_bodies.push(f.func); + func_bodies.push((f.func, (f.start, f.end))); } - let (func_resolver_builder, handler_data) = + let (func_resolver_builder, debug_metadata, handler_data) = FuncResolverBuilder::new(&*self.isa, func_bodies, module_info)?; let trampolines = Arc::new(Trampolines::new(&*self.isa, module_info)); @@ -195,7 +181,10 @@ impl ModuleCodeGenerator )); Ok(( - (Caller::new(handler_data, trampolines, func_resolver), Some(debug_metadata)), + ( + Caller::new(handler_data, trampolines, func_resolver), + debug_metadata, + ), cache_gen, )) } @@ -265,6 +254,8 @@ pub struct CraneliftFunctionCodeGenerator { next_local: usize, position: Position, func_env: FunctionEnvironment, + start: u32, + end: u32, } pub struct FunctionEnvironment { @@ -467,7 +458,7 @@ impl FuncEnvironment for FunctionEnvironment { let local_memory_bound = func.create_global_value(ir::GlobalValueData::Load { base: local_memory_ptr, offset: (vm::LocalMemory::offset_bound() as i32).into(), - global_type: ptr_type, + global_type: ir::types::I32, readonly: false, }); @@ -575,7 +566,7 @@ impl FuncEnvironment for FunctionEnvironment { let table_count = func.create_global_value(ir::GlobalValueData::Load { base: table_struct_ptr, offset: (vm::LocalTable::offset_count() as i32).into(), - global_type: ptr_type, + global_type: ir::types::I32, // The table length can change, so it can't be readonly. readonly: false, }); @@ -1092,13 +1083,14 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { Ok(()) } - fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError> { + fn feed_local(&mut self, ty: WpType, n: usize, loc: u32) -> Result<(), CodegenError> { let mut next_local = self.next_local; let mut builder = FunctionBuilder::new( &mut self.func, &mut self.func_translator.func_ctx, &mut self.position, ); + builder.set_srcloc(ir::SourceLoc::new(loc)); cranelift_wasm::declare_locals( &mut builder, n as u32, @@ -1114,7 +1106,7 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { Ok(()) } - fn feed_event(&mut self, event: Event, _module_info: &ModuleInfo) -> Result<(), CodegenError> { + fn feed_event(&mut self, event: Event, _module_info: &ModuleInfo, loc: u32) -> Result<(), CodegenError> { let op = match event { Event::Wasm(x) => x, Event::WasmOwned(ref x) => x, @@ -1136,6 +1128,8 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { &mut self.func_translator.func_ctx, &mut self.position, ); + builder.func.collect_debug_info(); + builder.set_srcloc(ir::SourceLoc::new(loc)); let module_state = ModuleTranslationState::new(); let func_state = &mut self.func_translator.state; translate_operator( @@ -1145,6 +1139,7 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { func_state, &mut self.func_env, )?; + Ok(()) } @@ -1173,6 +1168,8 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { } } + builder.finalize(); + // Discard any remaining values on the stack. Either we just returned them, // or the end of the function is unreachable. state.stack.clear(); @@ -1249,9 +1246,11 @@ fn declare_wasm_parameters(builder: &mut FunctionBuilder, entry_block: Ebb) -> u // This is a normal WebAssembly signature parameter, so create a local for it. let local = Variable::new(next_local); builder.declare_var(local, param_type.value_type); + let value_label = ValueLabel::from_u32(next_local as u32); next_local += 1; let param_value = builder.ebb_params(entry_block)[i]; + builder.set_val_label(param_value, value_label); builder.def_var(local, param_value); } if param_type.purpose == ir::ArgumentPurpose::VMContext { diff --git a/lib/clif-backend/src/lib.rs b/lib/clif-backend/src/lib.rs index 06bffc791cc..5eccdcb2d56 100644 --- a/lib/clif-backend/src/lib.rs +++ b/lib/clif-backend/src/lib.rs @@ -42,7 +42,7 @@ fn get_isa() -> Box { builder.set("opt_level", "speed_and_size").unwrap(); builder.set("jump_tables_enabled", "false").unwrap(); - if cfg!(not(test)) { + if cfg!(not(test)) && cfg!(not(debug)) { builder.set("enable_verifier", "false").unwrap(); } diff --git a/lib/clif-backend/src/module.rs b/lib/clif-backend/src/module.rs index 11999ba02f6..1fb15b5f6b1 100644 --- a/lib/clif-backend/src/module.rs +++ b/lib/clif-backend/src/module.rs @@ -45,7 +45,6 @@ impl Module { runnable_module: Arc::new(Box::new(runnable_module)), cache_gen, info, - debug_info: None, }) } } diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index efc038d5843..e039af433fb 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -10,7 +10,9 @@ use crate::{ }; use byteorder::{ByteOrder, LittleEndian}; use cranelift_codegen::{ + ValueLabelsRanges, binemit::{Stackmap, StackmapSink}, + entity::PrimaryMap, ir, isa, Context, }; use rayon::prelude::*; @@ -96,54 +98,159 @@ impl FuncResolverBuilder { pub fn new( isa: &dyn isa::TargetIsa, - function_bodies: Map, + function_bodies: Map, info: &ModuleInfo, - ) -> CompileResult<(Self, HandlerData)> { + ) -> CompileResult<( + Self, + Option, + HandlerData, + )> { let num_func_bodies = function_bodies.len(); let mut local_relocs = Map::with_capacity(num_func_bodies); let mut external_relocs = Map::with_capacity(num_func_bodies); let mut trap_sink = TrapSink::new(); - let compiled_functions: Result, (RelocSink, LocalTrapSink))>, CompileError> = - function_bodies - .into_vec() - .par_iter() - .map_init( - || Context::new(), - |ctx, func| { - let mut code_buf = Vec::new(); - ctx.func = func.to_owned(); - let mut reloc_sink = RelocSink::new(); - let mut local_trap_sink = LocalTrapSink::new(); - let mut stackmap_sink = NoopStackmapSink {}; - ctx.compile_and_emit( - isa, - &mut code_buf, - &mut reloc_sink, - &mut local_trap_sink, - &mut stackmap_sink, - ) - .map_err(|e| CompileError::InternalError { msg: e.to_string() })?; - ctx.clear(); - Ok((code_buf, (reloc_sink, local_trap_sink))) - }, - ) - .collect(); + let fb = function_bodies.iter().collect::>(); + rayon::ThreadPoolBuilder::new().num_threads(1).build_global().unwrap(); + + let compiled_functions: Result< + Vec<(Vec, + ( + LocalFuncIndex, + CompiledFunctionData, + ValueLabelsRanges, + Vec>, + RelocSink, + LocalTrapSink, + ), + )>, + CompileError, + > = fb + .par_iter() + .map_init( + || Context::new(), + |ctx, (lfi, (func, (start, end)))| { + let mut code_buf = Vec::new(); + ctx.func = func.to_owned(); + let mut reloc_sink = RelocSink::new(); + let mut local_trap_sink = LocalTrapSink::new(); + let mut stackmap_sink = NoopStackmapSink {}; + + //ctx.eliminate_unreachable_code(isa).unwrap(); + /*ctx.dce(isa).unwrap(); + ctx.shrink_instructions(isa).unwrap(); + ctx.redundant_reload_remover(isa).unwrap(); + ctx.preopt(isa).unwrap(); + ctx.legalize(isa).unwrap(); + ctx.postopt(isa).unwrap();*/ + ctx.verify(isa).unwrap(); + + ctx.compile_and_emit( + isa, + &mut code_buf, + &mut reloc_sink, + &mut local_trap_sink, + &mut stackmap_sink, + ) + .map_err(|e| CompileError::InternalError { msg: e.to_string() })?; + + // begin debug stuff + let func = &ctx.func; + let encinfo = isa.encoding_info(); + let mut ebbs = func.layout.ebbs().collect::>(); + ebbs.sort_by_key(|ebb| func.offsets[*ebb]); + let instructions = /*func + .layout + .ebbs()*/ + ebbs.into_iter() + .flat_map(|ebb| { + func.inst_offsets(ebb, &encinfo) + .map(|(offset, inst, length)| { + let srcloc = func.srclocs[inst]; + let val = srcloc.bits(); + wasm_debug::types::CompiledInstructionData { + // we write this data later + loc: wasm_debug::types::SourceLoc::new(val),//srcloc.bits()), + offset: offset as usize, + length: length as usize, + } + }) + }) + .collect::>(); + + /*let mut unwind = vec![]; + ctx.emit_unwind_info(isa, &mut unwind); + dbg!(unwind.len());*/ + + let stack_slots = ctx.func.stack_slots.iter().map(|(_, ssd)| ssd.offset).collect::>>(); + let labels_ranges = ctx.build_value_labels_ranges(isa).unwrap_or_default(); + if labels_ranges.len() == 24 || labels_ranges.len() == 25 { + let mut vec = labels_ranges.iter().map(|(a,b)| (*a, b.clone())).collect::>(); + vec.sort_by(|a, b| a.0.as_u32().cmp(&b.0.as_u32())); + dbg!(labels_ranges.len(), &vec); + } - let compiled_functions = compiled_functions?; + let entry = CompiledFunctionData { + instructions, + start: wasm_debug::types::SourceLoc::new(*start), + end: wasm_debug::types::SourceLoc::new(*end), + // this not being 0 breaks inst-level debugging + compiled_offset: 0, + compiled_size: code_buf.len(), + }; + + // end debug stuff + + ctx.clear(); + Ok((code_buf, (*lfi, entry, labels_ranges, stack_slots, reloc_sink, local_trap_sink))) + }, + ) + .collect(); + + + use wasm_debug::types::CompiledFunctionData; + let mut debug_metadata = wasmer_runtime_core::codegen::DebugMetadata { + func_info: PrimaryMap::new(), + inst_info: PrimaryMap::new(), + pointers: vec![], + stack_slot_offsets: PrimaryMap::new(), + }; + + let mut compiled_functions = compiled_functions?; + compiled_functions.sort_by(|a, b| (a.1).0.cmp(&(b.1).0)); + let compiled_functions = compiled_functions; let mut total_size = 0; // We separate into two iterators, one iterable and one into iterable - let (code_bufs, sinks): (Vec>, Vec<(RelocSink, LocalTrapSink)>) = - compiled_functions.into_iter().unzip(); - for (code_buf, (reloc_sink, mut local_trap_sink)) in code_bufs.iter().zip(sinks.into_iter()) + let (code_bufs, sinks): ( + Vec>, + Vec<( + LocalFuncIndex, + CompiledFunctionData, + ValueLabelsRanges, + Vec>, + RelocSink, + LocalTrapSink, + )>, + ) = compiled_functions.into_iter().unzip(); + for ( + code_buf, + (_, entry, vlr, stackslots, reloc_sink, mut local_trap_sink), + ) in code_bufs + .iter() + .zip(sinks.into_iter()) { + let rounded_size = round_up(code_buf.len(), mem::size_of::()); + debug_metadata.func_info.push(entry); + debug_metadata.inst_info.push(unsafe {std::mem::transmute(vlr)}); + debug_metadata.stack_slot_offsets.push(stackslots); + // Clear the local trap sink and consolidate all trap info // into a single location. trap_sink.drain_local(total_size, &mut local_trap_sink); // Round up each function's size to pointer alignment. - total_size += round_up(code_buf.len(), mem::size_of::()); + total_size += rounded_size; local_relocs.push(reloc_sink.local_relocs.into_boxed_slice()); external_relocs.push(reloc_sink.external_relocs.into_boxed_slice()); @@ -175,7 +282,12 @@ impl FuncResolverBuilder { let mut previous_end = 0; for compiled in code_bufs.iter() { - let new_end = previous_end + round_up(compiled.len(), mem::size_of::()); + let length = round_up(compiled.len(), mem::size_of::()); + debug_metadata.pointers.push(( + (memory.as_ptr() as usize + previous_end) as *const u8, + length, + )); + let new_end = previous_end + length; unsafe { memory.as_slice_mut()[previous_end..previous_end + compiled.len()] .copy_from_slice(&compiled[..]); @@ -197,7 +309,7 @@ impl FuncResolverBuilder { func_resolver_builder.relocate_locals(); - Ok((func_resolver_builder, handler_data)) + Ok((func_resolver_builder, Some(debug_metadata), handler_data)) } fn relocate_locals(&mut self) { diff --git a/lib/debug-writer/src/lib.rs b/lib/debug-writer/src/lib.rs index 66a2b2513ed..655a5a6dc59 100644 --- a/lib/debug-writer/src/lib.rs +++ b/lib/debug-writer/src/lib.rs @@ -1,13 +1,15 @@ // TODO: add attribution to LLVM for data definitions and WasmTime for code structure -use std::str::FromStr; -use std::ptr; use std::ffi::c_void; +use std::ptr; +use std::str::FromStr; -pub use wasm_debug::{read_debuginfo, DebugInfoData, WasmFileInfo}; pub use wasm_debug::{emit_dwarf, ResolvedSymbol, SymbolResolver}; +pub use wasm_debug::{read_debuginfo, DebugInfoData, WasmFileInfo}; -use target_lexicon::{Triple, Architecture, Vendor, OperatingSystem, Environment, BinaryFormat}; -use gimli::write::{self, DwarfUnit, Sections, Address, RangeList, EndianVec, AttributeValue, Range}; +use gimli::write::{ + self, Address, AttributeValue, DwarfUnit, EndianVec, Range, RangeList, Sections, +}; +use target_lexicon::{Architecture, BinaryFormat, Environment, OperatingSystem, Triple, Vendor}; use wasmer_runtime_core::{module::ModuleInfo, state::CodeVersion}; @@ -138,8 +140,8 @@ pub fn generate_dwarf(module_info: &ModuleInfo, debug_info_data: &DebugInfoData, // `DW_AT_low_pc` and `DW_AT_high_pc` are good for continuous // `DW_AT_low_pc` alone can work for a single address, but we can probably not // worry about that for now. These attribtues associate machine code with the DIE - // - - + // - + match platform { X86_64_GNU_LINUX => unimplemented!("in progress"), X86_64_OSX => unimplemented!("in progress"), @@ -151,7 +153,12 @@ pub fn generate_dwarf(module_info: &ModuleInfo, debug_info_data: &DebugInfoData, */ // converts existing dwarf into a usable form with metadata from the JIT -fn reprocess_dwarf(module_info: &ModuleInfo, debug_info_data: &DebugInfoData, code_version: &CodeVersion, platform: Triple) -> Option { +fn reprocess_dwarf( + module_info: &ModuleInfo, + debug_info_data: &DebugInfoData, + code_version: &CodeVersion, + platform: Triple, +) -> Option { None } diff --git a/lib/middleware-common/src/block_trace.rs b/lib/middleware-common/src/block_trace.rs index f104ab086b4..1d539e44a98 100644 --- a/lib/middleware-common/src/block_trace.rs +++ b/lib/middleware-common/src/block_trace.rs @@ -25,6 +25,7 @@ impl FunctionMiddleware for BlockTrace { op: Event<'a, 'b>, _module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + _loc: u32, ) -> Result<(), Self::Error> { match op { Event::Internal(InternalEvent::FunctionBegin(_)) => { diff --git a/lib/middleware-common/src/call_trace.rs b/lib/middleware-common/src/call_trace.rs index 5cb77534c56..5f7f3203252 100644 --- a/lib/middleware-common/src/call_trace.rs +++ b/lib/middleware-common/src/call_trace.rs @@ -26,6 +26,7 @@ impl FunctionMiddleware for CallTrace { op: Event<'a, 'b>, _module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + _loc: u32, ) -> Result<(), Self::Error> { let counter = self.counter.clone(); diff --git a/lib/middleware-common/src/metering.rs b/lib/middleware-common/src/metering.rs index a7d10aece10..a66fea509e0 100644 --- a/lib/middleware-common/src/metering.rs +++ b/lib/middleware-common/src/metering.rs @@ -44,6 +44,7 @@ impl FunctionMiddleware for Metering { op: Event<'a, 'b>, _module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + _loc: u32, ) -> Result<(), Self::Error> { match op { Event::Internal(InternalEvent::FunctionBegin(_)) => { diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index c72ca25df5e..835bc450162 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -112,9 +112,12 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, /// Checks the precondition for a module. fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), E>; /// Creates a new function and returns the function-scope code generator for it. - fn next_function(&mut self, module_info: Arc>) -> Result<&mut FCG, E>; + fn next_function(&mut self, module_info: Arc>, loc: (u32, u32)) -> Result<&mut FCG, E>; /// Finalizes this module. - fn finalize(self, module_info: &ModuleInfo) -> Result<((RM, Option), Box), E>; + fn finalize( + self, + module_info: &ModuleInfo, + ) -> Result<((RM, Option), Box), E>; /// Creates a module from cache. unsafe fn from_cache(cache: Artifact, _: Token) -> Result; @@ -127,6 +130,10 @@ pub struct DebugMetadata { pub func_info: PrimaryMap, /// inst_info pub inst_info: PrimaryMap, + /// stack slot offsets! + pub stack_slot_offsets: PrimaryMap>>, + /// function pointers and their lengths + pub pointers: Vec<(*const u8, usize)>, } /// A streaming compiler which is designed to generated code for a module based on a stream @@ -241,7 +248,6 @@ impl< validate_with_features(wasm, &compiler_config.features)?; } - let mut mcg = match MCG::backend_id() { "llvm" => MCG::new_with_target( compiler_config.triple.clone(), @@ -252,13 +258,15 @@ impl< }; let mut chain = (self.middleware_chain_generator)(); let info = crate::parse::read_module(wasm, &mut mcg, &mut chain, &compiler_config)?; - let ((exec_context, debug_metadata), cache_gen) = - mcg.finalize(&info.read().unwrap()) - .map_err(|x| CompileError::InternalError { - msg: format!("{:?}", x), - })?; - - use target_lexicon::{Triple, Architecture, Vendor, OperatingSystem, Environment, BinaryFormat}; + let ((exec_context, debug_metadata), cache_gen) = mcg + .finalize(&info.read().unwrap()) + .map_err(|x| CompileError::InternalError { + msg: format!("{:?}", x), + })?; + + use target_lexicon::{ + Architecture, BinaryFormat, Environment, OperatingSystem, Triple, Vendor, + }; const X86_64_OSX: Triple = Triple { architecture: Architecture::X86_64, vendor: Vendor::Apple, @@ -266,34 +274,37 @@ impl< environment: Environment::Unknown, binary_format: BinaryFormat::Macho, }; - + if compiler_config.generate_debug_info { let debug_metadata = debug_metadata.expect("debug metadata"); let debug_info = wasm_debug::read_debuginfo(wasm); - let extra_info = wasm_debug::types::ModuleVmctxInfo { - memory_offset: 0, - stack_slot_offsets: cranelift_entity::PrimaryMap::new(), - }; + let extra_info = wasm_debug::types::ModuleVmctxInfo::new(14 * 8, debug_metadata.stack_slot_offsets.values()); // lazy type hack (TODO:) - let compiled_fn_map = unsafe { std::mem::transmute(debug_metadata.func_info) }; - let range_map = unsafe { std::mem::transmute(debug_metadata.inst_info) }; - let raw_func_slice = vec![];//exec_context.get_local_function_pointers_and_lengths().expect("raw func slice"); + let compiled_fn_map = wasm_debug::types::create_module_address_map(debug_metadata.func_info.values()); + let range_map = wasm_debug::types::build_values_ranges(debug_metadata.inst_info.values()); + let raw_func_slice = debug_metadata.pointers; + dbg!("DEBUG INFO GENERATED"); - let debug_image = wasm_debug::emit_debugsections_image(X86_64_OSX, - std::mem::size_of::() as u8, - &debug_info, - &extra_info, - &compiled_fn_map, - &range_map, - &raw_func_slice).expect("make debug image"); - - crate::jit_debug::register_new_jit_code_entry(&debug_image, crate::jit_debug::JITAction::JIT_REGISTER_FN); - } + let debug_image = wasm_debug::emit_debugsections_image( + X86_64_OSX, + std::mem::size_of::() as u8, + &debug_info, + &extra_info, + &compiled_fn_map, + &range_map, + &raw_func_slice, + ) + .expect("make debug image"); + + crate::jit_debug::register_new_jit_code_entry( + &debug_image, + crate::jit_debug::JITAction::JIT_REGISTER_FN, + ); + } Ok(ModuleInner { cache_gen, runnable_module: Arc::new(Box::new(exec_context)), info: Arc::try_unwrap(info).unwrap().into_inner().unwrap(), - debug_info: None, }) } @@ -340,6 +351,7 @@ impl MiddlewareChain { fcg: Option<&mut FCG>, ev: Event, module_info: &ModuleInfo, + loc: u32, ) -> Result<(), String> { let mut sink = EventSink { buffer: SmallVec::new(), @@ -348,12 +360,12 @@ impl MiddlewareChain { for m in &mut self.chain { let prev: SmallVec<[Event; 2]> = sink.buffer.drain().collect(); for ev in prev { - m.feed_event(ev, module_info, &mut sink)?; + m.feed_event(ev, module_info, &mut sink, loc)?; } } if let Some(fcg) = fcg { for ev in sink.buffer { - fcg.feed_event(ev, module_info) + fcg.feed_event(ev, module_info, loc) .map_err(|x| format!("{:?}", x))?; } } @@ -372,6 +384,7 @@ pub trait FunctionMiddleware { op: Event<'a, 'b>, module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + loc: u32, ) -> Result<(), Self::Error>; } @@ -381,6 +394,7 @@ pub(crate) trait GenericFunctionMiddleware { op: Event<'a, 'b>, module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + loc: u32, ) -> Result<(), String>; } @@ -390,8 +404,9 @@ impl> GenericFunctionMiddleware for T op: Event<'a, 'b>, module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + loc: u32, ) -> Result<(), String> { - ::feed_event(self, op, module_info, sink) + ::feed_event(self, op, module_info, sink, loc) .map_err(|x| format!("{:?}", x)) } } @@ -405,13 +420,13 @@ pub trait FunctionCodeGenerator { fn feed_param(&mut self, ty: WpType) -> Result<(), E>; /// Adds `n` locals to the function. - fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), E>; + fn feed_local(&mut self, ty: WpType, n: usize, loc: u32) -> Result<(), E>; /// Called before the first call to `feed_opcode`. fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), E>; /// Called for each operator. - fn feed_event(&mut self, op: Event, module_info: &ModuleInfo) -> Result<(), E>; + fn feed_event(&mut self, op: Event, module_info: &ModuleInfo, loc: u32) -> Result<(), E>; /// Finalizes the function. fn finalize(&mut self) -> Result<(), E>; diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index d012ae8d6be..bb8f29ad039 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -86,9 +86,14 @@ impl Instance { None => vm::Ctx::new(backing, import_backing, &module), }; vmctx.as_mut_ptr().write(real_ctx); + for (_, memory) in backing.vm_memories.iter_mut() { + let mem: &mut vm::LocalMemory = &mut **memory; + mem.vmctx = dbg!(vmctx.as_mut_ptr()); + } }; Box::leak(vmctx); + let instance = Instance { module, inner, diff --git a/lib/runtime-core/src/jit_debug.rs b/lib/runtime-core/src/jit_debug.rs index 8a56fcd7fac..f00d07bf615 100644 --- a/lib/runtime-core/src/jit_debug.rs +++ b/lib/runtime-core/src/jit_debug.rs @@ -8,15 +8,25 @@ use std::ptr; // // see also https://sourceware.org/gdb/current/onlinedocs/gdb.html#JIT-Interface +#[no_mangle] #[inline(never)] -pub extern "C" fn __jit_debug_register_code() { - +extern "C" fn __jit_debug_register_code() { + // implementation of this function copied from wasmtime (TODO: link and attribution) + // prevent optimization of this function + let x = 3; + unsafe { + std::ptr::read_volatile(&x); + } } #[allow(non_camel_case_types)] #[derive(Debug)] #[repr(u32)] -pub enum JITAction { JIT_NOACTION = 0, JIT_REGISTER_FN = 1, JIT_UNREGISTER_FN = 2 } +pub enum JITAction { + JIT_NOACTION = 0, + JIT_REGISTER_FN = 1, + JIT_UNREGISTER_FN = 2, +} #[no_mangle] #[repr(C)] @@ -76,9 +86,15 @@ unsafe fn push_front(jce: *mut JITCodeEntry) { // deleted static (added and deleted by Mark): TODO: pub fn register_new_jit_code_entry(bytes: &[u8], action: JITAction) -> *mut JITCodeEntry { + let owned_bytes = bytes.iter().cloned().collect::>(); + let ptr = owned_bytes.as_ptr(); + let len = owned_bytes.len(); + + std::mem::forget(bytes); + let entry: *mut JITCodeEntry = Box::into_raw(Box::new(JITCodeEntry { - symfile_addr: bytes.as_ptr(), - symfile_size: bytes.len() as _, + symfile_addr: ptr, + symfile_size: len as _, ..JITCodeEntry::default() })); @@ -86,6 +102,9 @@ pub fn register_new_jit_code_entry(bytes: &[u8], action: JITAction) -> *mut JITC push_front(entry); __jit_debug_descriptor.relevant_entry = entry; __jit_debug_descriptor.action_flag = action as u32; + __jit_debug_register_code(); + __jit_debug_descriptor.relevant_entry = ptr::null_mut(); + __jit_debug_descriptor.action_flag = JITAction::JIT_NOACTION as _; } entry diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index 46f46f34aaa..8f808be7466 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -66,10 +66,10 @@ pub mod vmcalls; pub use trampoline_x64 as trampoline; #[cfg(unix)] pub mod fault; +pub mod jit_debug; pub mod state; #[cfg(feature = "managed")] pub mod tiering; -pub mod jit_debug; use self::error::CompileResult; #[doc(inline)] diff --git a/lib/runtime-core/src/memory/mod.rs b/lib/runtime-core/src/memory/mod.rs index 75a02e2276a..95458eda407 100644 --- a/lib/runtime-core/src/memory/mod.rs +++ b/lib/runtime-core/src/memory/mod.rs @@ -227,6 +227,7 @@ impl UnsharedMemory { base: std::ptr::null_mut(), bound: 0, memory: std::ptr::null_mut(), + vmctx: std::ptr::null_mut(), }; let storage = match desc.memory_type() { @@ -314,6 +315,7 @@ impl SharedMemory { base: std::ptr::null_mut(), bound: 0, memory: std::ptr::null_mut(), + vmctx: std::ptr::null_mut(), }; let memory = StaticMemory::new(desc, &mut local)?; diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index cfe9aaa3c78..4b04a6d6b0b 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -20,18 +20,12 @@ use indexmap::IndexMap; use std::collections::HashMap; use std::sync::Arc; -#[doc(hidden)] -pub struct DebugInfo { - -} - /// This is used to instantiate a new WebAssembly module. #[doc(hidden)] pub struct ModuleInner { pub runnable_module: Arc>, pub cache_gen: Box, pub info: ModuleInfo, - pub debug_info: Option, } /// Container for module data including memories, globals, tables, imports, and exports. @@ -83,6 +77,18 @@ pub struct ModuleInfo { /// Custom sections. pub custom_sections: HashMap>, + + /// Debug info for funcs + pub func_debug_info: Map, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +/// info about functions for debugging +pub struct FuncDebugInfo { + /// byte offset from start of code section where the function starts at + pub start: u32, + /// byte offset from start of code section where the function starts at + pub end: u32, } impl ModuleInfo { diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index ad91afb77b1..b6a7ff6d560 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -6,8 +6,8 @@ use crate::{ backend::{CompilerConfig, RunnableModule}, error::CompileError, module::{ - DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, - TableInitializer, + DataInitializer, ExportIndex, FuncDebugInfo, ImportName, ModuleInfo, StringTable, + StringTableBuilder, TableInitializer, }, structures::{Map, TypedIndex}, types::{ @@ -90,6 +90,8 @@ pub fn read_module< em_symbol_map: compiler_config.symbol_map.clone(), custom_sections: HashMap::new(), + + func_debug_info: Map::new(), })); let mut parser = wasmparser::ValidatingParser::new( @@ -206,91 +208,113 @@ pub fn read_module< ParserState::StartSectionEntry(start_index) => { info.write().unwrap().start_func = Some(FuncIndex::new(start_index as usize)); } - ParserState::BeginFunctionBody { .. } => { + ParserState::BeginFunctionBody { range } => { let id = func_count; if !mcg_info_fed { mcg_info_fed = true; - info.write().unwrap().namespace_table = - namespace_builder.take().unwrap().finish(); - info.write().unwrap().name_table = name_builder.take().unwrap().finish(); - mcg.feed_signatures(info.read().unwrap().signatures.clone()) + { + let mut info_write = info.write().unwrap(); + info_write.namespace_table = namespace_builder.take().unwrap().finish(); + info_write.name_table = name_builder.take().unwrap().finish(); + } + let info_read = info.read().unwrap(); + mcg.feed_signatures(info_read.signatures.clone()) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - mcg.feed_function_signatures(info.read().unwrap().func_assoc.clone()) + mcg.feed_function_signatures(info_read.func_assoc.clone()) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - mcg.check_precondition(&info.read().unwrap()) + mcg.check_precondition(&info_read) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; } + let fcg = mcg - .next_function(Arc::clone(&info)) + .next_function(Arc::clone(&info), (range.start as u32, range.end as u32)) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - let info_read = info.read().unwrap(); - let sig = info_read - .signatures - .get( - *info - .read() - .unwrap() - .func_assoc - .get(FuncIndex::new( - id as usize + info.read().unwrap().imported_functions.len(), - )) - .unwrap(), - ) - .unwrap(); - for ret in sig.returns() { - fcg.feed_return(type_to_wp_type(*ret)) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + { + let info_read = info.read().unwrap(); + let sig = info_read + .signatures + .get( + *info + .read() + .unwrap() + .func_assoc + .get(FuncIndex::new( + id as usize + info_read.imported_functions.len(), + )) + .unwrap(), + ) + .unwrap(); + for ret in sig.returns() { + fcg.feed_return(type_to_wp_type(*ret)) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + for param in sig.params() { + fcg.feed_param(type_to_wp_type(*param)) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } } - for param in sig.params() { - fcg.feed_param(type_to_wp_type(*param)) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + + let mut operator_parser = parser.create_binary_reader(); + + // read locals in function body + { + let local_count = operator_parser.read_local_count().unwrap(); + let mut total = 0; + for _ in 0..local_count { + let cur_pos = range.start as u32 + operator_parser.current_position() as u32; + let (count, ty) = operator_parser + .read_local_decl(&mut total) + .expect("Expected local"); + fcg.feed_local(ty, count as usize, cur_pos) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } } - let mut body_begun = false; + // read instruction locations into vector for debug purposes + { + let info_read = info.read().unwrap(); + let mut cur_pos = range.start as u32 + operator_parser.current_position() as u32; + fcg.begin_body(&info_read) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + middlewares + .run( + Some(fcg), + Event::Internal(InternalEvent::FunctionBegin(id as u32)), + &info_read, + cur_pos, + ) + .map_err(LoadError::Codegen)?; - loop { - let state = parser.read(); - match state { - ParserState::Error(err) => return Err(LoadError::Parse(*err)), - ParserState::FunctionBodyLocals { ref locals } => { - for &(count, ty) in locals.iter() { - fcg.feed_local(ty, count as usize) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - } - } - ParserState::CodeOperator(op) => { - if !body_begun { - body_begun = true; - fcg.begin_body(&info.read().unwrap()) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - middlewares - .run( - Some(fcg), - Event::Internal(InternalEvent::FunctionBegin(id as u32)), - &info.read().unwrap(), - ) - .map_err(LoadError::Codegen)?; - } - middlewares - .run(Some(fcg), Event::Wasm(op), &info.read().unwrap()) - .map_err(LoadError::Codegen)?; - } - ParserState::EndFunctionBody => break, - _ => unreachable!(), + while let Ok(op) = operator_parser.read_operator() { + middlewares + .run(Some(fcg), Event::WasmOwned(op), &info_read, cur_pos) + .map_err(LoadError::Codegen)?; + cur_pos = range.start as u32 + operator_parser.current_position() as u32; } + + cur_pos = range.start as u32 + operator_parser.current_position() as u32; + middlewares + .run( + Some(fcg), + Event::Internal(InternalEvent::FunctionEnd), + &info_read, + cur_pos, + ) + .map_err(LoadError::Codegen)?; } - middlewares - .run( - Some(fcg), - Event::Internal(InternalEvent::FunctionEnd), - &info.read().unwrap(), - ) - .map_err(LoadError::Codegen)?; + fcg.finalize() .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; func_count = func_count.wrapping_add(1); + + // debug info + let debug_entry = FuncDebugInfo { + start: range.start as u32, + end: range.end as u32, + }; + info.write().unwrap().func_debug_info.push(debug_entry); } ParserState::BeginActiveElementSectionEntry(table_index) => { let table_index = TableIndex::new(table_index as usize); diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 2a39bdec10f..ceb7e311447 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -8,7 +8,7 @@ use crate::{ module::{ModuleInfo, ModuleInner}, sig_registry::SigRegistry, structures::TypedIndex, - types::{LocalOrImport, MemoryIndex, TableIndex, Value}, + types::{LocalOrImport, MemoryIndex, TableIndex, Value, LocalMemoryIndex}, vmcalls, }; use std::{ @@ -134,6 +134,9 @@ pub struct InternalCtx { /// Interrupt signal mem. pub interrupt_signal_mem: *mut u8, + + /// hmm + pub first_mem: *mut LocalMemory, } static INTERNAL_FIELDS: AtomicUsize = AtomicUsize::new(0); @@ -283,6 +286,7 @@ impl Ctx { }; ((*mem).base, (*mem).bound) }; + dbg!(mem_bound); Self { internal: InternalCtx { memories: local_backing.vm_memories.as_mut_ptr(), @@ -306,6 +310,7 @@ impl Ctx { internals: &mut local_backing.internals.0, interrupt_signal_mem: get_interrupt_signal_mem(), + first_mem: local_backing.vm_memories[LocalMemoryIndex::new(0)], }, local_functions: local_backing.local_functions.as_ptr(), @@ -336,6 +341,8 @@ impl Ctx { }; ((*mem).base, (*mem).bound) }; + + dbg!(mem_bound); Self { internal: InternalCtx { memories: local_backing.vm_memories.as_mut_ptr(), @@ -359,6 +366,8 @@ impl Ctx { internals: &mut local_backing.internals.0, interrupt_signal_mem: get_interrupt_signal_mem(), + + first_mem: local_backing.vm_memories[LocalMemoryIndex::new(0)], }, local_functions: local_backing.local_functions.as_ptr(), @@ -537,7 +546,7 @@ impl Ctx { } pub const fn offset_local_functions() -> u8 { - 14 * (mem::size_of::() as u8) + 15 * (mem::size_of::() as u8) } } @@ -666,6 +675,9 @@ pub struct LocalMemory { /// This is either `*mut DynamicMemory`, `*mut StaticMemory`, /// or `*mut SharedStaticMemory`. pub memory: *mut (), + + /// wat + pub vmctx: *mut Ctx, } // manually implemented because LocalMemory contains raw pointers From 5e3c5f703f2f13ec73ffa78df9f52d951f201548 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 11 Feb 2020 16:35:42 -0800 Subject: [PATCH 04/26] Fix validation issue, cast to the correct type --- lib/clif-backend/src/code.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index a7cb465174b..4705b07a652 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -688,7 +688,8 @@ impl FuncEnvironment for FunctionEnvironment { colocated: false, }); - pos.ins().symbol_value(ir::types::I64, sig_index_global) + let val = pos.ins().symbol_value(ir::types::I64, sig_index_global); + pos.ins().ireduce(ir::types::I32, val) // let dynamic_sigindices_array_ptr = pos.ins().load( // ptr_type, From e34e0bb8973b664b9975100ec4c3a706542c1f61 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 12 Feb 2020 17:09:30 -0800 Subject: [PATCH 05/26] Clean up, everything works --- Cargo.lock | 51 +- Cargo.toml | 4 +- lib/clif-backend/src/code.rs | 20 +- lib/clif-backend/src/resolver.rs | 166 ++--- lib/debug-writer/Cargo.toml | 17 - lib/debug-writer/README.md | 63 -- lib/debug-writer/src/gc.rs | 250 ------- lib/debug-writer/src/lib.rs | 177 ----- lib/debug-writer/src/read_debug_info.rs | 265 ------- .../src/transform/address_transform.rs | 676 ------------------ lib/debug-writer/src/transform/attr.rs | 308 -------- lib/debug-writer/src/transform/expression.rs | 515 ------------- .../src/transform/line_program.rs | 248 ------- lib/debug-writer/src/transform/mod.rs | 133 ---- .../src/transform/range_info_builder.rs | 237 ------ lib/debug-writer/src/transform/simulate.rs | 392 ---------- lib/debug-writer/src/transform/unit.rs | 387 ---------- lib/debug-writer/src/transform/utils.rs | 172 ----- lib/debug-writer/src/write_debug_info.rs | 163 ----- lib/llvm-backend/src/code.rs | 23 +- lib/runtime-core/src/codegen.rs | 18 +- lib/runtime-core/src/instance.rs | 2 +- lib/runtime-core/src/module.rs | 14 +- lib/runtime-core/src/parse.rs | 20 +- lib/runtime-core/src/vm.rs | 3 +- lib/singlepass-backend/src/codegen_x64.rs | 44 +- src/bin/wasmer.rs | 6 +- 27 files changed, 182 insertions(+), 4192 deletions(-) delete mode 100644 lib/debug-writer/Cargo.toml delete mode 100644 lib/debug-writer/README.md delete mode 100644 lib/debug-writer/src/gc.rs delete mode 100644 lib/debug-writer/src/lib.rs delete mode 100644 lib/debug-writer/src/read_debug_info.rs delete mode 100644 lib/debug-writer/src/transform/address_transform.rs delete mode 100644 lib/debug-writer/src/transform/attr.rs delete mode 100644 lib/debug-writer/src/transform/expression.rs delete mode 100644 lib/debug-writer/src/transform/line_program.rs delete mode 100644 lib/debug-writer/src/transform/mod.rs delete mode 100644 lib/debug-writer/src/transform/range_info_builder.rs delete mode 100644 lib/debug-writer/src/transform/simulate.rs delete mode 100644 lib/debug-writer/src/transform/unit.rs delete mode 100644 lib/debug-writer/src/transform/utils.rs delete mode 100644 lib/debug-writer/src/write_debug_info.rs diff --git a/Cargo.lock b/Cargo.lock index e484a5eeda0..514b12118d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,7 +235,7 @@ dependencies = [ "cranelift-entity", "log", "smallvec 1.1.0", - "target-lexicon 0.9.0", + "target-lexicon", "thiserror", ] @@ -269,7 +269,7 @@ checksum = "21398a0bc6ba389ea86964ac4a495426dd61080f2ddd306184777a8560fe9976" dependencies = [ "cranelift-codegen", "raw-cpuid", - "target-lexicon 0.9.0", + "target-lexicon", ] [[package]] @@ -468,23 +468,7 @@ dependencies = [ "log", "scroll 0.10.1", "string-interner", - "target-lexicon 0.9.0", - "thiserror", -] - -[[package]] -name = "faerie" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74b9ed6159e4a6212c61d9c6a86bee01876b192a64accecf58d5b5ae3b667b52" -dependencies = [ - "anyhow", - "goblin 0.1.3", - "indexmap", - "log", - "scroll 0.10.1", - "string-interner", - "target-lexicon 0.10.0", + "target-lexicon", "thiserror", ] @@ -1552,12 +1536,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f4c118a7a38378f305a9e111fcb2f7f838c0be324bfb31a77ea04f7f6e684b4" -[[package]] -name = "target-lexicon" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d" - [[package]] name = "tempfile" version = "3.1.0" @@ -1752,10 +1730,10 @@ version = "0.1.0" dependencies = [ "anyhow", "cranelift-entity", - "faerie 0.13.0", + "faerie", "gimli", "more-asserts", - "target-lexicon 0.9.0", + "target-lexicon", "thiserror", "wasmparser 0.39.3", ] @@ -1776,7 +1754,6 @@ dependencies = [ "typetag", "wabt", "wasmer-clif-backend", - "wasmer-debug-writer", "wasmer-dev-utils", "wasmer-emscripten", "wasmer-emscripten-tests", @@ -1807,7 +1784,7 @@ dependencies = [ "serde-bench", "serde_bytes", "serde_derive", - "target-lexicon 0.9.0", + "target-lexicon", "wasm-debug", "wasmer-clif-fork-frontend", "wasmer-clif-fork-wasm", @@ -1826,7 +1803,7 @@ dependencies = [ "cranelift-codegen", "log", "smallvec 1.1.0", - "target-lexicon 0.9.0", + "target-lexicon", ] [[package]] @@ -1843,18 +1820,6 @@ dependencies = [ "wasmparser 0.45.0", ] -[[package]] -name = "wasmer-debug-writer" -version = "0.13.1" -dependencies = [ - "faerie 0.14.0", - "gimli", - "target-lexicon 0.9.0", - "wasm-debug", - "wasmer-runtime-core", - "wasmparser 0.45.0", -] - [[package]] name = "wasmer-dev-utils" version = "0.13.1" @@ -2000,7 +1965,7 @@ dependencies = [ "serde_bytes", "serde_derive", "smallvec 0.6.13", - "target-lexicon 0.9.0", + "target-lexicon", "wasm-debug", "wasmparser 0.45.0", "winapi", diff --git a/Cargo.toml b/Cargo.toml index 65260638475..8b74cd54c35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,6 @@ wasmer-wasi-tests = { path = "lib/wasi-tests", optional = true } wasmer-middleware-common-tests = { path = "lib/middleware-common-tests", optional = true } wasmer-emscripten-tests = { path = "lib/emscripten-tests", optional = true } wasmer-wasi-experimental-io-devices = { path = "lib/wasi-experimental-io-devices", optional = true } -wasmer-debug-writer = { path = "lib/debug-writer", optional = true } [workspace] members = [ @@ -65,7 +64,6 @@ members = [ "lib/wasi-tests", "lib/emscripten-tests", "lib/middleware-common-tests", - "lib/debug-writer", "examples/parallel", "examples/plugin-for-example", "examples/parallel-guest", @@ -81,7 +79,7 @@ serde = { version = "1", features = ["derive"] } # used by the plugin example typetag = "0.1" # used by the plugin example [features] -default = ["fast-tests", "wasi", "backend-cranelift", "wabt", "wasmer-debug-writer"] +default = ["fast-tests", "wasi", "backend-cranelift", "wabt"] "loader-kernel" = ["wasmer-kernel-loader"] debug = ["fern", "log/max_level_debug", "log/release_max_level_debug"] trace = ["fern", "log/max_level_trace", "log/release_max_level_trace"] diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index 4705b07a652..e78333f6688 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -7,7 +7,7 @@ use crate::{ }; use cranelift_codegen::entity::EntityRef; -use cranelift_codegen::ir::{self, Ebb, Function, InstBuilder, ValueLabel}; +use cranelift_codegen::ir::{self, Ebb, Function, InstBuilder}; use cranelift_codegen::isa::CallConv; use cranelift_codegen::{cursor::FuncCursor, isa}; use cranelift_frontend::{FunctionBuilder, Position, Variable}; @@ -106,7 +106,10 @@ impl ModuleCodeGenerator end: loc.1, }; - func_env.func.collect_debug_info(); + let generate_debug_info = module_info.read().unwrap().generate_debug_info; + if generate_debug_info { + func_env.func.collect_debug_info(); + } debug_assert_eq!(func_env.func.dfg.num_ebbs(), 0, "Function must be empty"); debug_assert_eq!(func_env.func.dfg.num_insts(), 0, "Function must be empty"); @@ -1107,7 +1110,12 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { Ok(()) } - fn feed_event(&mut self, event: Event, _module_info: &ModuleInfo, loc: u32) -> Result<(), CodegenError> { + fn feed_event( + &mut self, + event: Event, + _module_info: &ModuleInfo, + loc: u32, + ) -> Result<(), CodegenError> { let op = match event { Event::Wasm(x) => x, Event::WasmOwned(ref x) => x, @@ -1159,7 +1167,7 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { // // If the exit block is unreachable, it may not have the correct arguments, so we would // generate a return instruction that doesn't match the signature. - if state.reachable { + if state.reachable() { debug_assert!(builder.is_pristine()); if !builder.is_unreachable() { match return_mode { @@ -1247,11 +1255,11 @@ fn declare_wasm_parameters(builder: &mut FunctionBuilder, entry_block: Ebb) -> u // This is a normal WebAssembly signature parameter, so create a local for it. let local = Variable::new(next_local); builder.declare_var(local, param_type.value_type); - let value_label = ValueLabel::from_u32(next_local as u32); + //let value_label = ValueLabel::from_u32(next_local as u32); next_local += 1; let param_value = builder.ebb_params(entry_block)[i]; - builder.set_val_label(param_value, value_label); + //builder.set_val_label(param_value, value_label); builder.def_var(local, param_value); } if param_type.purpose == ir::ArgumentPurpose::VMContext { diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index e039af433fb..a608ad50464 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -10,10 +10,9 @@ use crate::{ }; use byteorder::{ByteOrder, LittleEndian}; use cranelift_codegen::{ - ValueLabelsRanges, binemit::{Stackmap, StackmapSink}, entity::PrimaryMap, - ir, isa, Context, + ir, isa, Context, ValueLabelsRanges, }; use rayon::prelude::*; use std::{ @@ -111,22 +110,21 @@ impl FuncResolverBuilder { let mut trap_sink = TrapSink::new(); - let fb = function_bodies.iter().collect::>(); - rayon::ThreadPoolBuilder::new().num_threads(1).build_global().unwrap(); + let generate_debug_info = info.generate_debug_info; + let fb = function_bodies.iter().collect::>(); let compiled_functions: Result< - Vec<(Vec, + Vec<( + Vec, ( LocalFuncIndex, - CompiledFunctionData, - ValueLabelsRanges, - Vec>, + Option<(CompiledFunctionData, ValueLabelsRanges, Vec>)>, RelocSink, LocalTrapSink, ), )>, CompileError, - > = fb + > = fb .par_iter() .map_init( || Context::new(), @@ -137,15 +135,6 @@ impl FuncResolverBuilder { let mut local_trap_sink = LocalTrapSink::new(); let mut stackmap_sink = NoopStackmapSink {}; - //ctx.eliminate_unreachable_code(isa).unwrap(); - /*ctx.dce(isa).unwrap(); - ctx.shrink_instructions(isa).unwrap(); - ctx.redundant_reload_remover(isa).unwrap(); - ctx.preopt(isa).unwrap(); - ctx.legalize(isa).unwrap(); - ctx.postopt(isa).unwrap();*/ - ctx.verify(isa).unwrap(); - ctx.compile_and_emit( isa, &mut code_buf, @@ -155,66 +144,68 @@ impl FuncResolverBuilder { ) .map_err(|e| CompileError::InternalError { msg: e.to_string() })?; - // begin debug stuff - let func = &ctx.func; - let encinfo = isa.encoding_info(); - let mut ebbs = func.layout.ebbs().collect::>(); - ebbs.sort_by_key(|ebb| func.offsets[*ebb]); - let instructions = /*func - .layout - .ebbs()*/ - ebbs.into_iter() - .flat_map(|ebb| { - func.inst_offsets(ebb, &encinfo) - .map(|(offset, inst, length)| { - let srcloc = func.srclocs[inst]; - let val = srcloc.bits(); - wasm_debug::types::CompiledInstructionData { - // we write this data later - loc: wasm_debug::types::SourceLoc::new(val),//srcloc.bits()), - offset: offset as usize, - length: length as usize, - } - }) - }) - .collect::>(); - - /*let mut unwind = vec![]; - ctx.emit_unwind_info(isa, &mut unwind); - dbg!(unwind.len());*/ - - let stack_slots = ctx.func.stack_slots.iter().map(|(_, ssd)| ssd.offset).collect::>>(); - let labels_ranges = ctx.build_value_labels_ranges(isa).unwrap_or_default(); - if labels_ranges.len() == 24 || labels_ranges.len() == 25 { - let mut vec = labels_ranges.iter().map(|(a,b)| (*a, b.clone())).collect::>(); - vec.sort_by(|a, b| a.0.as_u32().cmp(&b.0.as_u32())); - dbg!(labels_ranges.len(), &vec); - } - - let entry = CompiledFunctionData { - instructions, - start: wasm_debug::types::SourceLoc::new(*start), - end: wasm_debug::types::SourceLoc::new(*end), - // this not being 0 breaks inst-level debugging - compiled_offset: 0, - compiled_size: code_buf.len(), + let debug_entry = if generate_debug_info { + let func = &ctx.func; + let encinfo = isa.encoding_info(); + let mut ebbs = func.layout.ebbs().collect::>(); + ebbs.sort_by_key(|ebb| func.offsets[*ebb]); + let instructions = ebbs + .into_iter() + .flat_map(|ebb| { + func.inst_offsets(ebb, &encinfo) + .map(|(offset, inst, length)| { + let srcloc = func.srclocs[inst]; + let val = srcloc.bits(); + wasm_debug::types::CompiledInstructionData { + loc: wasm_debug::types::SourceLoc::new(val), + offset: offset as usize, + length: length as usize, + } + }) + }) + .collect::>(); + + /*let mut unwind = vec![]; + ctx.emit_unwind_info(isa, &mut unwind); + dbg!(unwind.len());*/ + + let stack_slots = ctx + .func + .stack_slots + .iter() + .map(|(_, ssd)| ssd.offset) + .collect::>>(); + let labels_ranges = ctx.build_value_labels_ranges(isa).unwrap_or_default(); + + let entry = CompiledFunctionData { + instructions, + start: wasm_debug::types::SourceLoc::new(*start), + end: wasm_debug::types::SourceLoc::new(*end), + // this not being 0 breaks inst-level debugging + compiled_offset: 0, + compiled_size: code_buf.len(), + }; + Some((entry, labels_ranges, stack_slots)) + } else { + None }; - // end debug stuff - ctx.clear(); - Ok((code_buf, (*lfi, entry, labels_ranges, stack_slots, reloc_sink, local_trap_sink))) + Ok((code_buf, (*lfi, debug_entry, reloc_sink, local_trap_sink))) }, ) .collect(); - use wasm_debug::types::CompiledFunctionData; - let mut debug_metadata = wasmer_runtime_core::codegen::DebugMetadata { - func_info: PrimaryMap::new(), - inst_info: PrimaryMap::new(), - pointers: vec![], - stack_slot_offsets: PrimaryMap::new(), + let mut debug_metadata = if generate_debug_info { + Some(wasmer_runtime_core::codegen::DebugMetadata { + func_info: PrimaryMap::new(), + inst_info: PrimaryMap::new(), + pointers: vec![], + stack_slot_offsets: PrimaryMap::new(), + }) + } else { + None }; let mut compiled_functions = compiled_functions?; @@ -226,24 +217,23 @@ impl FuncResolverBuilder { Vec>, Vec<( LocalFuncIndex, - CompiledFunctionData, - ValueLabelsRanges, - Vec>, + Option<(CompiledFunctionData, ValueLabelsRanges, Vec>)>, RelocSink, LocalTrapSink, )>, ) = compiled_functions.into_iter().unzip(); - for ( - code_buf, - (_, entry, vlr, stackslots, reloc_sink, mut local_trap_sink), - ) in code_bufs - .iter() - .zip(sinks.into_iter()) + for (code_buf, (_, debug_info, reloc_sink, mut local_trap_sink)) in + code_bufs.iter().zip(sinks.into_iter()) { let rounded_size = round_up(code_buf.len(), mem::size_of::()); - debug_metadata.func_info.push(entry); - debug_metadata.inst_info.push(unsafe {std::mem::transmute(vlr)}); - debug_metadata.stack_slot_offsets.push(stackslots); + if let Some(ref mut dbg_metadata) = debug_metadata { + let (entry, vlr, stackslots) = debug_info.unwrap(); + dbg_metadata.func_info.push(entry); + dbg_metadata + .inst_info + .push(unsafe { std::mem::transmute(vlr) }); + dbg_metadata.stack_slot_offsets.push(stackslots); + } // Clear the local trap sink and consolidate all trap info // into a single location. @@ -283,10 +273,12 @@ impl FuncResolverBuilder { let mut previous_end = 0; for compiled in code_bufs.iter() { let length = round_up(compiled.len(), mem::size_of::()); - debug_metadata.pointers.push(( - (memory.as_ptr() as usize + previous_end) as *const u8, - length, - )); + if let Some(ref mut dbg_metadata) = debug_metadata { + dbg_metadata.pointers.push(( + (memory.as_ptr() as usize + previous_end) as *const u8, + length, + )); + } let new_end = previous_end + length; unsafe { memory.as_slice_mut()[previous_end..previous_end + compiled.len()] @@ -309,7 +301,7 @@ impl FuncResolverBuilder { func_resolver_builder.relocate_locals(); - Ok((func_resolver_builder, Some(debug_metadata), handler_data)) + Ok((func_resolver_builder, debug_metadata, handler_data)) } fn relocate_locals(&mut self) { diff --git a/lib/debug-writer/Cargo.toml b/lib/debug-writer/Cargo.toml deleted file mode 100644 index e3f6dba4cf9..00000000000 --- a/lib/debug-writer/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "wasmer-debug-writer" -version = "0.13.1" -authors = ["The Wasmer Engineering Team "] -edition = "2018" -repository = "https://github.com/wasmerio/wasmer" -publish = false -description = "Library for writing debug information from Wasm" -license = "MIT" - -[dependencies] -faerie = "0.14" -gimli = "0.20" -target-lexicon = "0.9" -wasm-debug = { version = "0.1.0", path = "../../../Dev/wasm-debug" } -wasmer-runtime-core = { path = "../runtime-core", version = "0.13.1" } -wasmparser = "0.45" diff --git a/lib/debug-writer/README.md b/lib/debug-writer/README.md deleted file mode 100644 index e55264abbe8..00000000000 --- a/lib/debug-writer/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Wasmer debug info writer - -This crate deals with passing DWARF debug information along from -compiled Wasm modules to the machine code that we generate. - -This crate is effectively a derivative work of WasmTime's -[`wasmtime-debug`](https://github.com/bytecodealliance/wasmtime/tree/master/crates/debug) -crate. After beginning work on a clean reimplementation we realized -that the WasmTime implementation is high quality and it didn't make -sense for us to duplicate their hard work. - -Additionally by keeping the code structure of `wasmer-debug-writer` -similar to `wasmtime-debug`, we hope to upstream bug fixes and -improvements to `wasmtime-debug`. - -Copied files include the copyright notice as well, but as a catch all, -this crate is a derivative work of WasmTime's `wasmtime-debug` - -``` -Copyright 2020 WasmTime Project Developers - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -``` - -The latest revision at the time of cloning is `3992b8669f9b9e185abe81e9998ce2ff4d40ff68`. - -Changes to this crate are copyright of Wasmer inc. unless otherwise indicated -and are licensed under the Wasmer project's license: - -``` -MIT License - -Copyright (c) 2020 Wasmer, Inc. and its affiliates. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -``` diff --git a/lib/debug-writer/src/gc.rs b/lib/debug-writer/src/gc.rs deleted file mode 100644 index 7d20c9f8f4b..00000000000 --- a/lib/debug-writer/src/gc.rs +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright 2019 WasmTime Project Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file is from the WasmTime project. -// It was copied at revision `39e57e3e9ac9c15bef45eb77a2544a7c0b76501a`. -// -// Changes to this file are copyright of Wasmer inc. unless otherwise indicated -// and are licensed under the Wasmer project's license. -use crate::transform::AddressTransform; -use gimli::constants; -use gimli::read; -use gimli::{Reader, UnitSectionOffset}; -use std::collections::{HashMap, HashSet}; - -#[derive(Debug)] -pub struct Dependencies { - edges: HashMap>, - roots: HashSet, -} - -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 { - 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>( - dwarf: &read::Dwarf, - at: &AddressTransform, -) -> read::Result { - 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>( - header: read::CompilationUnitHeader, - dwarf: &read::Dwarf, - 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>(die: &read::DebuggingInformationEntry) -> 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>( - die: &read::DebuggingInformationEntry, - dwarf: &read::Dwarf, - unit: &read::Unit, - at: &AddressTransform, -) -> read::Result { - 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>( - die: read::EntriesTreeNode, - dwarf: &read::Dwarf, - unit: &read::Unit, - 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>( - attr: &read::Attribute, - offset: UnitSectionOffset, - _dwarf: &read::Dwarf, - unit: &read::Unit, - _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(()) -} diff --git a/lib/debug-writer/src/lib.rs b/lib/debug-writer/src/lib.rs deleted file mode 100644 index 655a5a6dc59..00000000000 --- a/lib/debug-writer/src/lib.rs +++ /dev/null @@ -1,177 +0,0 @@ -// TODO: add attribution to LLVM for data definitions and WasmTime for code structure -use std::ffi::c_void; -use std::ptr; -use std::str::FromStr; - -pub use wasm_debug::{emit_dwarf, ResolvedSymbol, SymbolResolver}; -pub use wasm_debug::{read_debuginfo, DebugInfoData, WasmFileInfo}; - -use gimli::write::{ - self, Address, AttributeValue, DwarfUnit, EndianVec, Range, RangeList, Sections, -}; -use target_lexicon::{Architecture, BinaryFormat, Environment, OperatingSystem, Triple, Vendor}; - -use wasmer_runtime_core::{module::ModuleInfo, state::CodeVersion}; - -/// Triple of x86_64 GNU/Linux -const X86_64_GNU_LINUX: Triple = Triple { - architecture: Architecture::X86_64, - vendor: Vendor::Unknown, - operating_system: OperatingSystem::Linux, - environment: Environment::Gnu, - binary_format: BinaryFormat::Elf, -}; - -/// Triple of x86_64 OSX -const X86_64_OSX: Triple = Triple { - architecture: Architecture::X86_64, - vendor: Vendor::Apple, - operating_system: OperatingSystem::Darwin, - environment: Environment::Unknown, - binary_format: BinaryFormat::Macho, -}; - -/// Triple of x86_64 Windows -const X86_64_WINDOWS: Triple = Triple { - architecture: Architecture::X86_64, - vendor: Vendor::Pc, - operating_system: OperatingSystem::Windows, - environment: Environment::Msvc, - binary_format: BinaryFormat::Coff, -}; - -// this code also from WasmTime -// TODO: attribute -struct ImageRelocResolver<'a> { - func_offsets: &'a Vec, -} - -// this code also from WasmTime -// TODO: attribute -impl<'a> SymbolResolver for ImageRelocResolver<'a> { - fn resolve_symbol(&self, symbol: usize, addend: i64) -> ResolvedSymbol { - let func_start = self.func_offsets[symbol]; - ResolvedSymbol::PhysicalAddress(func_start + addend as u64) - } -} - -/* -// the structure of this function and some of its details come from WasmTime -// TODO: attribute -pub fn generate_dwarf(module_info: &ModuleInfo, debug_info_data: &DebugInfoData, code_version: &CodeVersion, platform: Triple) -> Result, String> { - let func_offsets = unimplemented!(); - let resolver = ImageRelocResolver { func_offsets }; - // copied from https://docs.rs/gimli/0.20.0/gimli/write/index.html ; TODO: review these values - let processed_dwarf = reprocess_dwarf(module_info, debug_info_data, code_version, platform).ok_or_else(|| "Failed to reprocess Wasm's dwarf".to_string())?; - let encoding = gimli::Encoding { - format: gimli::Format::Dwarf32, - version: 3, - address_size: 8, - }; - let mut dwarf = DwarfUnit::new(encoding); - // TODO: figure out what range is (from example) - let range_list = RangeList(vec![Range::StartLength { - begin: Address::Constant(0x100), - length: 42, - }]); - let range_list_id = dwarf.unit.ranges.add(range_list); - let root = dwarf.unit.root(); - dwarf.unit.get_mut(root).set( - gimli::DW_AT_ranges, - AttributeValue::RangeListRef(range_list_id), - ); - let mut string_table = write::StringTable::default(); - let mut line_string_table = write::LineStringTable::default(); - - let mut obj = faerie::Artifact::new(platform, String::from("module")); - - let mut sections = Sections::new(EndianVec::new(gimli::LittleEndian)); - // Finally, write the DWARF data to the sections. - dwarf.write(&mut sections).map_err(|e| e.to_string())?; - emit_dwarf(&mut obj, dwarf, &resolver); - sections.for_each(|id, data| { - // Here you can add the data to the output object file. - Ok(()) - }); - - obj.emit_as(BinaryFormat::Elf).expect("TODO"); - // We want to populate DwarfUnit::line_str_table with WAT probably - // and set up the string table with things like function signatures in WAT, function names, etc - - // NOTES from DWARF spec: - // http://dwarfstd.org/doc/DWARF5.pdf - // - `DIE`s form the core of dwarf and live in .debug_info - // - the tags can get fairly specific, it looks like we'll just need a mapping - // from object code to a bunch of tags and ranges? created with the Wasm - // data for extra info about types, etc. - // - debug info can live in a separate object file (that's what we'll do here) - // - attribute types are unique per DIE (lots of info here (like is tail call, - // return addr, etc.) - // - DW_AT_language: WebAssembly :bonjour: - // - `DW_AT_linkage_name` function namespaces? (later described as the raw, mangled name) - // `DW_AT_name` function name? - // - `DW_AT_location` where in the code it is - // - `DW_AT_main_subprogram` where to start from - // - `DW_AT_producer`: wasmer - // - `DW_AT_recursive` -- is this mandatory? what is it used for? TODO: find out - // - `DW_AT_signature` -- can we use wasm type signature info here? TODO: - // - `DIE`s form a graph/tree though a tree-like graph when it is a graph, docs say - // this is how structs and relationship of code blocks is represented. - // - when serialized the tree is in post-fix order (probably not important for our - // purposes but mildly interesting) - // - we'll need pointer sizer and platform information - // - dwarf executes a typed stack-machine to compute the locations of things - // - lots of neat info about the dwarf stack machine skipping for now because I - // think gimli exposes a higher-level interface (if not, I'll add notes here - // or further down about it) - // - can use dwarf expressions/dynamically computing things to handle things like - // a tiering JIT? - // - location lists are needed for things that aren't lexically scoped, otherwise - // single location descriptions (dwarf expressions) are sufficient - // - I wonder what this means in the context of spilling registers... do we have - // to create dwarf expressions that can handle that? - // - `DW_AT_artificial` is used to tag `DIE` that didn't come directly from the code - // - `DW_AT_declaration` for function/etc declarations at the top of the wasm module, - // see section 2.13.2 for how to connect the definiton and the declaration - // - `DW_AT_decl_line`, `DW_AT_decl_column` refer to the exact location in the source - // file, so presumably we include the entire source file in one of the sections? - // or perhaps that's purely for human consumption. - // - `DW_AT_ranges` is for non-contiguous ranges of address and, - // `DW_AT_low_pc` and `DW_AT_high_pc` are good for continuous - // `DW_AT_low_pc` alone can work for a single address, but we can probably not - // worry about that for now. These attribtues associate machine code with the DIE - // - - - match platform { - X86_64_GNU_LINUX => unimplemented!("in progress"), - X86_64_OSX => unimplemented!("in progress"), - X86_64_WINDOWS => unimplemented!("in progress"), - _ => return Err(format!("Debug output for the platform {} is not yet supported", platform)), - } - Ok(vec![]) -} -*/ - -// converts existing dwarf into a usable form with metadata from the JIT -fn reprocess_dwarf( - module_info: &ModuleInfo, - debug_info_data: &DebugInfoData, - code_version: &CodeVersion, - platform: Triple, -) -> Option { - None -} - -// black box, needs some kind of input, some kind of processing -// and returns a bunch of bytes we can give to GDB -// -// where is this documented? -// we need to pass in target triple, isa config, memories/pointers to memories, ranges of where things are, -// and info like function names -pub fn generate_debug_sections_image(bytes: &[u8]) -> Option> { - let debug_info = read_debuginfo(bytes); - dbg!(debug_info); - - //emit_debugsections_image(X86_64_OSX, 8, debug_info, ) - None -} diff --git a/lib/debug-writer/src/read_debug_info.rs b/lib/debug-writer/src/read_debug_info.rs deleted file mode 100644 index 595a3430d0f..00000000000 --- a/lib/debug-writer/src/read_debug_info.rs +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2019 WasmTime Project Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file is from the WasmTime project. It reads DWARF info from a Wasm module. -// It was copied at revision `39e57e3e9ac9c15bef45eb77a2544a7c0b76501a`. -// -// Changes to this file are copyright of Wasmer inc. unless otherwise indicated -// and are licensed under the Wasmer project's license. - -use gimli::{ - DebugAbbrev, DebugAddr, DebugInfo, DebugLine, DebugLineStr, DebugLoc, DebugLocLists, - DebugRanges, DebugRngLists, DebugStr, DebugStrOffsets, DebugTypes, EndianSlice, LittleEndian, - LocationLists, RangeLists, -}; -use std::collections::HashMap; -use std::path::PathBuf; -use wasmparser::{self, ModuleReader, SectionCode}; - -trait Reader: gimli::Reader {} - -impl<'input> Reader for gimli::EndianSlice<'input, LittleEndian> {} - -pub use wasmparser::Type as WasmType; - -pub type Dwarf<'input> = gimli::Dwarf>; - -#[derive(Debug)] -pub struct FunctionMetadata { - pub params: Box<[WasmType]>, - pub locals: Box<[(u32, WasmType)]>, -} - -#[derive(Debug)] -pub struct WasmFileInfo { - pub path: Option, - pub code_section_offset: u64, - pub funcs: Box<[FunctionMetadata]>, -} - -#[derive(Debug)] -pub struct NameSection { - pub module_name: Option, - pub func_names: HashMap, - pub locals_names: HashMap>, -} - -#[derive(Debug)] -pub struct DebugInfoData<'a> { - pub dwarf: Dwarf<'a>, - pub name_section: Option, - pub wasm_file: WasmFileInfo, -} - -fn convert_sections<'a>(sections: HashMap<&str, &'a [u8]>) -> Dwarf<'a> { - const EMPTY_SECTION: &[u8] = &[]; - - let endian = LittleEndian; - let debug_str = DebugStr::new(sections.get(".debug_str").unwrap_or(&EMPTY_SECTION), endian); - let debug_abbrev = DebugAbbrev::new( - sections.get(".debug_abbrev").unwrap_or(&EMPTY_SECTION), - endian, - ); - let debug_info = DebugInfo::new( - sections.get(".debug_info").unwrap_or(&EMPTY_SECTION), - endian, - ); - let debug_line = DebugLine::new( - sections.get(".debug_line").unwrap_or(&EMPTY_SECTION), - endian, - ); - - if sections.contains_key(".debug_addr") { - panic!("Unexpected .debug_addr"); - } - - let debug_addr = DebugAddr::from(EndianSlice::new(EMPTY_SECTION, endian)); - - if sections.contains_key(".debug_line_str") { - panic!("Unexpected .debug_line_str"); - } - - let debug_line_str = DebugLineStr::from(EndianSlice::new(EMPTY_SECTION, endian)); - let debug_str_sup = DebugStr::from(EndianSlice::new(EMPTY_SECTION, endian)); - - if sections.contains_key(".debug_rnglists") { - panic!("Unexpected .debug_rnglists"); - } - - let debug_ranges = match sections.get(".debug_ranges") { - Some(section) => DebugRanges::new(section, endian), - None => DebugRanges::new(EMPTY_SECTION, endian), - }; - let debug_rnglists = DebugRngLists::new(EMPTY_SECTION, endian); - let ranges = RangeLists::new(debug_ranges, debug_rnglists); - - if sections.contains_key(".debug_loclists") { - panic!("Unexpected .debug_loclists"); - } - - let debug_loc = match sections.get(".debug_loc") { - Some(section) => DebugLoc::new(section, endian), - None => DebugLoc::new(EMPTY_SECTION, endian), - }; - let debug_loclists = DebugLocLists::new(EMPTY_SECTION, endian); - let locations = LocationLists::new(debug_loc, debug_loclists); - - if sections.contains_key(".debug_str_offsets") { - panic!("Unexpected .debug_str_offsets"); - } - - let debug_str_offsets = DebugStrOffsets::from(EndianSlice::new(EMPTY_SECTION, endian)); - - if sections.contains_key(".debug_types") { - panic!("Unexpected .debug_types"); - } - - let debug_types = DebugTypes::from(EndianSlice::new(EMPTY_SECTION, endian)); - - Dwarf { - debug_abbrev, - debug_addr, - debug_info, - debug_line, - debug_line_str, - debug_str, - debug_str_offsets, - debug_str_sup, - debug_types, - locations, - ranges, - } -} - -fn read_name_section(reader: wasmparser::NameSectionReader) -> wasmparser::Result { - let mut module_name = None; - let mut func_names = HashMap::new(); - let mut locals_names = HashMap::new(); - for i in reader.into_iter() { - match i? { - wasmparser::Name::Module(m) => { - module_name = Some(String::from(m.get_name()?)); - } - wasmparser::Name::Function(f) => { - let mut reader = f.get_map()?; - while let Ok(naming) = reader.read() { - func_names.insert(naming.index, String::from(naming.name)); - } - } - wasmparser::Name::Local(l) => { - let mut reader = l.get_function_local_reader()?; - while let Ok(f) = reader.read() { - let mut names = HashMap::new(); - let mut reader = f.get_map()?; - while let Ok(naming) = reader.read() { - names.insert(naming.index, String::from(naming.name)); - } - locals_names.insert(f.func_index, names); - } - } - } - } - let result = NameSection { - module_name, - func_names, - locals_names, - }; - Ok(result) -} - -pub fn read_debug_info(data: &[u8]) -> DebugInfoData { - let mut reader = ModuleReader::new(data).expect("reader"); - let mut sections = HashMap::new(); - let mut name_section = None; - let mut code_section_offset = 0; - - let mut signatures_params: Vec> = Vec::new(); - let mut func_params_refs: Vec = Vec::new(); - let mut func_locals: Vec> = Vec::new(); - - while !reader.eof() { - let section = reader.read().expect("section"); - match section.code { - SectionCode::Custom { name, .. } => { - if name.starts_with(".debug_") { - let mut reader = section.get_binary_reader(); - let len = reader.bytes_remaining(); - sections.insert(name, reader.read_bytes(len).expect("bytes")); - } - if name == "name" { - if let Ok(reader) = section.get_name_section_reader() { - if let Ok(section) = read_name_section(reader) { - name_section = Some(section); - } - } - } - } - SectionCode::Type => { - signatures_params = section - .get_type_section_reader() - .expect("type section") - .into_iter() - .map(|ft| ft.expect("type").params) - .collect::>(); - } - SectionCode::Function => { - func_params_refs = section - .get_function_section_reader() - .expect("function section") - .into_iter() - .map(|index| index.expect("func index") as usize) - .collect::>(); - } - SectionCode::Code => { - code_section_offset = section.range().start as u64; - func_locals = section - .get_code_section_reader() - .expect("code section") - .into_iter() - .map(|body| { - let locals = body - .expect("body") - .get_locals_reader() - .expect("locals reader"); - locals - .into_iter() - .collect::, _>>() - .expect("locals data") - .into_boxed_slice() - }) - .collect::>(); - } - _ => (), - } - } - - let func_meta = func_params_refs - .into_iter() - .zip(func_locals.into_iter()) - .map(|(params_index, locals)| FunctionMetadata { - params: signatures_params[params_index].clone(), - locals, - }) - .collect::>(); - - DebugInfoData { - dwarf: convert_sections(sections), - name_section, - wasm_file: WasmFileInfo { - path: None, - code_section_offset, - funcs: func_meta.into_boxed_slice(), - }, - } -} diff --git a/lib/debug-writer/src/transform/address_transform.rs b/lib/debug-writer/src/transform/address_transform.rs deleted file mode 100644 index 876e2a6c7dd..00000000000 --- a/lib/debug-writer/src/transform/address_transform.rs +++ /dev/null @@ -1,676 +0,0 @@ -// Copyright 2019 WasmTime Project Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file is from the WasmTime project. -// It was copied at revision `907e7aac01af333a0af310ce0472abbc8a9adb6c`. -// -// Changes to this file are copyright of Wasmer inc. unless otherwise indicated -// and are licensed under the Wasmer project's license. -use crate::WasmFileInfo; -use cranelift_codegen::ir::SourceLoc; -use cranelift_entity::{EntityRef, PrimaryMap}; -use gimli::write; -use std::collections::BTreeMap; -use std::collections::HashMap; -use std::iter::FromIterator; -use wasmer_runtime_core::types::FuncIndex; -use wasmtime_environ::{FunctionAddressMap, ModuleAddressMap}; - -pub type GeneratedAddress = usize; -pub type WasmAddress = u64; - -/// Contains mapping of the generated address to its original -/// source location. -#[derive(Debug)] -pub struct AddressMap { - pub generated: GeneratedAddress, - pub wasm: WasmAddress, -} - -/// Information about generated function code: its body start, -/// length, and instructions addresses. -#[derive(Debug)] -pub struct FunctionMap { - pub offset: GeneratedAddress, - pub len: GeneratedAddress, - pub wasm_start: WasmAddress, - pub wasm_end: WasmAddress, - pub addresses: Box<[AddressMap]>, -} - -/// Mapping of the source location to its generated code range. -#[derive(Debug)] -struct Position { - wasm_pos: WasmAddress, - gen_start: GeneratedAddress, - gen_end: GeneratedAddress, -} - -/// Mapping of continuous range of source location to its generated -/// code. The positions are always in accending order for search. -#[derive(Debug)] -struct Range { - wasm_start: WasmAddress, - wasm_end: WasmAddress, - gen_start: GeneratedAddress, - gen_end: GeneratedAddress, - positions: Box<[Position]>, -} - -/// Helper function address lookup data. Contains ranges start positions -/// index and ranges data. The multiple ranges can include the same -/// original source position. The index (B-Tree) uses range start -/// position as a key. -#[derive(Debug)] -struct FuncLookup { - index: Vec<(WasmAddress, Box<[usize]>)>, - ranges: Box<[Range]>, -} - -/// Mapping of original functions to generated code locations/ranges. -#[derive(Debug)] -struct FuncTransform { - start: WasmAddress, - end: WasmAddress, - index: FuncIndex, - lookup: FuncLookup, -} - -/// Module functions mapping to generated code. -#[derive(Debug)] -pub struct AddressTransform { - map: PrimaryMap, - func: Vec<(WasmAddress, FuncTransform)>, -} - -/// Returns a wasm bytecode offset in the code section from SourceLoc. -pub fn get_wasm_code_offset(loc: SourceLoc, code_section_offset: u64) -> WasmAddress { - // Code section size <= 4GB, allow wrapped SourceLoc to recover the overflow. - loc.bits().wrapping_sub(code_section_offset as u32) as WasmAddress -} - -fn build_function_lookup( - ft: &FunctionAddressMap, - code_section_offset: u64, -) -> (WasmAddress, WasmAddress, FuncLookup) { - assert!(code_section_offset <= ft.start_srcloc.bits() as u64); - let fn_start = get_wasm_code_offset(ft.start_srcloc, code_section_offset); - let fn_end = get_wasm_code_offset(ft.end_srcloc, code_section_offset); - assert!(fn_start <= fn_end); - - // Build ranges of continuous source locations. The new ranges starts when - // non-descending order is interrupted. Assuming the same origin location can - // be present in multiple ranges. - let mut range_wasm_start = fn_start; - let mut range_gen_start = ft.body_offset; - let mut last_wasm_pos = range_wasm_start; - let mut ranges = Vec::new(); - let mut ranges_index = BTreeMap::new(); - let mut current_range = Vec::new(); - for t in &ft.instructions { - if t.srcloc.is_default() { - continue; - } - - let offset = get_wasm_code_offset(t.srcloc, code_section_offset); - assert!(fn_start <= offset); - assert!(offset <= fn_end); - - let inst_gen_start = t.code_offset; - let inst_gen_end = t.code_offset + t.code_len; - - if last_wasm_pos > offset { - // Start new range. - ranges_index.insert(range_wasm_start, ranges.len()); - ranges.push(Range { - wasm_start: range_wasm_start, - wasm_end: last_wasm_pos, - gen_start: range_gen_start, - gen_end: inst_gen_start, - positions: current_range.into_boxed_slice(), - }); - range_wasm_start = offset; - range_gen_start = inst_gen_start; - current_range = Vec::new(); - } - // Continue existing range: add new wasm->generated code position. - current_range.push(Position { - wasm_pos: offset, - gen_start: inst_gen_start, - gen_end: inst_gen_end, - }); - last_wasm_pos = offset; - } - let last_gen_addr = ft.body_offset + ft.body_len; - ranges_index.insert(range_wasm_start, ranges.len()); - ranges.push(Range { - wasm_start: range_wasm_start, - wasm_end: fn_end, - gen_start: range_gen_start, - gen_end: last_gen_addr, - positions: current_range.into_boxed_slice(), - }); - - // Making ranges lookup faster by building index: B-tree with every range - // start position that maps into list of active ranges at this position. - let ranges = ranges.into_boxed_slice(); - let mut active_ranges = Vec::new(); - let mut index = BTreeMap::new(); - let mut last_wasm_pos = None; - for (wasm_start, range_index) in ranges_index { - if Some(wasm_start) == last_wasm_pos { - active_ranges.push(range_index); - continue; - } - if last_wasm_pos.is_some() { - index.insert( - last_wasm_pos.unwrap(), - active_ranges.clone().into_boxed_slice(), - ); - } - active_ranges.retain(|r| ranges[*r].wasm_end.cmp(&wasm_start) != std::cmp::Ordering::Less); - active_ranges.push(range_index); - last_wasm_pos = Some(wasm_start); - } - index.insert(last_wasm_pos.unwrap(), active_ranges.into_boxed_slice()); - let index = Vec::from_iter(index.into_iter()); - (fn_start, fn_end, FuncLookup { index, ranges }) -} - -fn build_function_addr_map( - at: &ModuleAddressMap, - code_section_offset: u64, -) -> PrimaryMap { - let mut map = PrimaryMap::new(); - for (_, ft) in at { - let mut fn_map = Vec::new(); - for t in &ft.instructions { - if t.srcloc.is_default() { - continue; - } - let offset = get_wasm_code_offset(t.srcloc, code_section_offset); - fn_map.push(AddressMap { - generated: t.code_offset, - wasm: offset, - }); - } - - if cfg!(debug) { - // fn_map is sorted by the generated field -- see FunctionAddressMap::instructions. - for i in 1..fn_map.len() { - assert!(fn_map[i - 1].generated <= fn_map[i].generated); - } - } - - map.push(FunctionMap { - offset: ft.body_offset, - len: ft.body_len, - wasm_start: get_wasm_code_offset(ft.start_srcloc, code_section_offset), - wasm_end: get_wasm_code_offset(ft.end_srcloc, code_section_offset), - addresses: fn_map.into_boxed_slice(), - }); - } - map -} - -struct TransformRangeIter<'a> { - addr: u64, - indicies: &'a [usize], - ranges: &'a [Range], -} - -impl<'a> TransformRangeIter<'a> { - fn new(func: &'a FuncTransform, addr: u64) -> Self { - let found = match func - .lookup - .index - .binary_search_by(|entry| entry.0.cmp(&addr)) - { - Ok(i) => Some(&func.lookup.index[i].1), - Err(i) => { - if i > 0 { - Some(&func.lookup.index[i - 1].1) - } else { - None - } - } - }; - if let Some(range_indices) = found { - TransformRangeIter { - addr, - indicies: range_indices, - ranges: &func.lookup.ranges, - } - } else { - unreachable!(); - } - } -} -impl<'a> Iterator for TransformRangeIter<'a> { - type Item = (usize, usize); - fn next(&mut self) -> Option { - if let Some((first, tail)) = self.indicies.split_first() { - let range_index = *first; - let range = &self.ranges[range_index]; - self.indicies = tail; - let address = match range - .positions - .binary_search_by(|a| a.wasm_pos.cmp(&self.addr)) - { - Ok(i) => range.positions[i].gen_start, - Err(i) => { - if i == 0 { - range.gen_start - } else { - range.positions[i - 1].gen_end - } - } - }; - Some((address, range_index)) - } else { - None - } - } -} - -struct TransformRangeEndIter<'a> { - addr: u64, - indicies: &'a [usize], - ranges: &'a [Range], -} - -impl<'a> TransformRangeEndIter<'a> { - fn new(func: &'a FuncTransform, addr: u64) -> Self { - let found = match func - .lookup - .index - .binary_search_by(|entry| entry.0.cmp(&addr)) - { - Ok(i) => Some(&func.lookup.index[i].1), - Err(i) => { - if i > 0 { - Some(&func.lookup.index[i - 1].1) - } else { - None - } - } - }; - if let Some(range_indices) = found { - TransformRangeEndIter { - addr, - indicies: range_indices, - ranges: &func.lookup.ranges, - } - } else { - unreachable!(); - } - } -} - -impl<'a> Iterator for TransformRangeEndIter<'a> { - type Item = (usize, usize); - fn next(&mut self) -> Option { - while let Some((first, tail)) = self.indicies.split_first() { - let range_index = *first; - let range = &self.ranges[range_index]; - if range.wasm_start >= self.addr { - continue; - } - self.indicies = tail; - let address = match range - .positions - .binary_search_by(|a| a.wasm_pos.cmp(&self.addr)) - { - Ok(i) => range.positions[i].gen_end, - Err(i) => { - if i == range.positions.len() { - range.gen_end - } else { - range.positions[i].gen_start - } - } - }; - return Some((address, range_index)); - } - None - } -} - -impl AddressTransform { - pub fn new(at: &ModuleAddressMap, wasm_file: &WasmFileInfo) -> Self { - let code_section_offset = wasm_file.code_section_offset; - - let mut func = BTreeMap::new(); - for (i, ft) in at { - let (fn_start, fn_end, lookup) = build_function_lookup(ft, code_section_offset); - - func.insert( - fn_start, - FuncTransform { - start: fn_start, - end: fn_end, - index: i, - lookup, - }, - ); - } - - let map = build_function_addr_map(at, code_section_offset); - let func = Vec::from_iter(func.into_iter()); - AddressTransform { map, func } - } - - fn find_func(&self, addr: u64) -> Option<&FuncTransform> { - // TODO check if we need to include end address - let func = match self.func.binary_search_by(|entry| entry.0.cmp(&addr)) { - Ok(i) => &self.func[i].1, - Err(i) => { - if i > 0 { - &self.func[i - 1].1 - } else { - return None; - } - } - }; - if addr >= func.start { - return Some(func); - } - None - } - - pub fn find_func_index(&self, addr: u64) -> Option { - self.find_func(addr).map(|f| f.index) - } - - pub fn translate_raw(&self, addr: u64) -> Option<(FuncIndex, GeneratedAddress)> { - if addr == 0 { - // It's normally 0 for debug info without the linked code. - return None; - } - if let Some(func) = self.find_func(addr) { - if addr == func.end { - // Clamp last address to the end to extend translation to the end - // of the function. - let map = &self.map[func.index]; - return Some((func.index, map.len)); - } - let first_result = TransformRangeIter::new(func, addr).next(); - first_result.map(|(address, _)| (func.index, address)) - } else { - // Address was not found: function was not compiled? - None - } - } - - pub fn can_translate_address(&self, addr: u64) -> bool { - self.translate(addr).is_some() - } - - pub fn translate(&self, addr: u64) -> Option { - self.translate_raw(addr) - .map(|(func_index, address)| write::Address::Symbol { - symbol: func_index.index(), - addend: address as i64, - }) - } - - pub fn translate_ranges_raw( - &self, - start: u64, - end: u64, - ) -> Option<(FuncIndex, Vec<(GeneratedAddress, GeneratedAddress)>)> { - if start == 0 { - // It's normally 0 for debug info without the linked code. - return None; - } - if let Some(func) = self.find_func(start) { - let mut starts: HashMap = - HashMap::from_iter(TransformRangeIter::new(func, start).map(|(a, r)| (r, a))); - let mut result = Vec::new(); - TransformRangeEndIter::new(func, end).for_each(|(a, r)| { - let range_start = if let Some(range_start) = starts.get(&r) { - let range_start = *range_start; - starts.remove(&r); - range_start - } else { - let range = &func.lookup.ranges[r]; - range.gen_start - }; - result.push((range_start, a)); - }); - for (r, range_start) in starts { - let range = &func.lookup.ranges[r]; - result.push((range_start, range.gen_end)); - } - return Some((func.index, result)); - } - // Address was not found: function was not compiled? - None - } - - pub fn translate_ranges(&self, start: u64, end: u64) -> Vec<(write::Address, u64)> { - self.translate_ranges_raw(start, end) - .map_or(vec![], |(func_index, ranges)| { - ranges - .iter() - .map(|(start, end)| { - ( - write::Address::Symbol { - symbol: func_index.index(), - addend: *start as i64, - }, - (*end - *start) as u64, - ) - }) - .collect::>() - }) - } - - pub fn map(&self) -> &PrimaryMap { - &self.map - } - - pub fn func_range(&self, index: FuncIndex) -> (GeneratedAddress, GeneratedAddress) { - let map = &self.map[index]; - (map.offset, map.offset + map.len) - } - - pub fn func_source_range(&self, index: FuncIndex) -> (WasmAddress, WasmAddress) { - let map = &self.map[index]; - (map.wasm_start, map.wasm_end) - } - - pub fn convert_to_code_range( - &self, - addr: write::Address, - len: u64, - ) -> (GeneratedAddress, GeneratedAddress) { - let start = if let write::Address::Symbol { addend, .. } = addr { - // TODO subtract self.map[symbol].offset ? - addend as GeneratedAddress - } else { - unreachable!(); - }; - (start, start + len as GeneratedAddress) - } -} - -#[cfg(test)] -mod tests { - use super::{build_function_lookup, get_wasm_code_offset, AddressTransform}; - use crate::read_debug_info::WasmFileInfo; - use cranelift_codegen::ir::SourceLoc; - use cranelift_entity::PrimaryMap; - use gimli::write::Address; - use std::iter::FromIterator; - use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap, ModuleAddressMap}; - - #[test] - fn test_get_wasm_code_offset() { - let offset = get_wasm_code_offset(SourceLoc::new(3), 1); - assert_eq!(2, offset); - let offset = get_wasm_code_offset(SourceLoc::new(16), 0xF000_0000); - assert_eq!(0x1000_0010, offset); - let offset = get_wasm_code_offset(SourceLoc::new(1), 0x20_8000_0000); - assert_eq!(0x8000_0001, offset); - } - - fn create_simple_func(wasm_offset: u32) -> FunctionAddressMap { - FunctionAddressMap { - instructions: vec![ - InstructionAddressMap { - srcloc: SourceLoc::new(wasm_offset + 2), - code_offset: 5, - code_len: 3, - }, - InstructionAddressMap { - srcloc: SourceLoc::new(wasm_offset + 7), - code_offset: 15, - code_len: 8, - }, - ], - start_srcloc: SourceLoc::new(wasm_offset), - end_srcloc: SourceLoc::new(wasm_offset + 10), - body_offset: 0, - body_len: 30, - } - } - - fn create_simple_module(func: FunctionAddressMap) -> ModuleAddressMap { - PrimaryMap::from_iter(vec![func]) - } - - #[test] - fn test_build_function_lookup_simple() { - let input = create_simple_func(11); - let (start, end, lookup) = build_function_lookup(&input, 1); - assert_eq!(10, start); - assert_eq!(20, end); - - assert_eq!(1, lookup.index.len()); - let index_entry = lookup.index.into_iter().next().unwrap(); - assert_eq!((10u64, vec![0].into_boxed_slice()), index_entry); - assert_eq!(1, lookup.ranges.len()); - let range = &lookup.ranges[0]; - assert_eq!(10, range.wasm_start); - assert_eq!(20, range.wasm_end); - assert_eq!(0, range.gen_start); - assert_eq!(30, range.gen_end); - let positions = &range.positions; - assert_eq!(2, positions.len()); - assert_eq!(12, positions[0].wasm_pos); - assert_eq!(5, positions[0].gen_start); - assert_eq!(8, positions[0].gen_end); - assert_eq!(17, positions[1].wasm_pos); - assert_eq!(15, positions[1].gen_start); - assert_eq!(23, positions[1].gen_end); - } - - #[test] - fn test_build_function_lookup_two_ranges() { - let mut input = create_simple_func(11); - // append instruction with same srcloc as input.instructions[0] - input.instructions.push(InstructionAddressMap { - srcloc: SourceLoc::new(11 + 2), - code_offset: 23, - code_len: 3, - }); - let (start, end, lookup) = build_function_lookup(&input, 1); - assert_eq!(10, start); - assert_eq!(20, end); - - assert_eq!(2, lookup.index.len()); - let index_entries = Vec::from_iter(lookup.index.into_iter()); - assert_eq!((10u64, vec![0].into_boxed_slice()), index_entries[0]); - assert_eq!((12u64, vec![0, 1].into_boxed_slice()), index_entries[1]); - assert_eq!(2, lookup.ranges.len()); - - let range = &lookup.ranges[0]; - assert_eq!(10, range.wasm_start); - assert_eq!(17, range.wasm_end); - assert_eq!(0, range.gen_start); - assert_eq!(23, range.gen_end); - let positions = &range.positions; - assert_eq!(2, positions.len()); - assert_eq!(12, positions[0].wasm_pos); - assert_eq!(5, positions[0].gen_start); - assert_eq!(8, positions[0].gen_end); - assert_eq!(17, positions[1].wasm_pos); - assert_eq!(15, positions[1].gen_start); - assert_eq!(23, positions[1].gen_end); - - let range = &lookup.ranges[1]; - assert_eq!(12, range.wasm_start); - assert_eq!(20, range.wasm_end); - assert_eq!(23, range.gen_start); - assert_eq!(30, range.gen_end); - let positions = &range.positions; - assert_eq!(1, positions.len()); - assert_eq!(12, positions[0].wasm_pos); - assert_eq!(23, positions[0].gen_start); - assert_eq!(26, positions[0].gen_end); - } - - #[test] - fn test_addr_translate() { - let input = create_simple_module(create_simple_func(11)); - let at = AddressTransform::new( - &input, - &WasmFileInfo { - path: None, - code_section_offset: 1, - funcs: Box::new([]), - }, - ); - - let addr = at.translate(10); - assert_eq!( - Some(Address::Symbol { - symbol: 0, - addend: 0, - }), - addr - ); - - let addr = at.translate(20); - assert_eq!( - Some(Address::Symbol { - symbol: 0, - addend: 30, - }), - addr - ); - - let addr = at.translate(0); - assert_eq!(None, addr); - - let addr = at.translate(12); - assert_eq!( - Some(Address::Symbol { - symbol: 0, - addend: 5, - }), - addr - ); - - let addr = at.translate(18); - assert_eq!( - Some(Address::Symbol { - symbol: 0, - addend: 23, - }), - addr - ); - } -} diff --git a/lib/debug-writer/src/transform/attr.rs b/lib/debug-writer/src/transform/attr.rs deleted file mode 100644 index bd15586e9ba..00000000000 --- a/lib/debug-writer/src/transform/attr.rs +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright 2019 WasmTime Project Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file is from the WasmTime project. -// It was copied at revision `907e7aac01af333a0af310ce0472abbc8a9adb6c`. -// -// Changes to this file are copyright of Wasmer inc. unless otherwise indicated -// and are licensed under the Wasmer project's license. -use super::address_transform::AddressTransform; -use super::expression::{compile_expression, CompiledExpression, FunctionFrameInfo}; -use super::range_info_builder::RangeInfoBuilder; -use super::unit::PendingDieRef; -use super::{DebugInputContext, Reader, TransformError}; -use anyhow::Error; -use gimli::{ - write, AttributeValue, DebugLineOffset, DebugStr, DebuggingInformationEntry, UnitOffset, -}; -use std::collections::HashMap; - -pub(crate) enum FileAttributeContext<'a> { - Root(Option), - Children(&'a Vec, Option<&'a CompiledExpression>), -} - -fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool { - match attr_name { - gimli::DW_AT_location - | gimli::DW_AT_string_length - | gimli::DW_AT_return_addr - | gimli::DW_AT_data_member_location - | gimli::DW_AT_frame_base - | gimli::DW_AT_segment - | gimli::DW_AT_static_link - | gimli::DW_AT_use_location - | gimli::DW_AT_vtable_elem_location => true, - _ => false, - } -} - -pub(crate) fn clone_die_attributes<'a, R>( - entry: &DebuggingInformationEntry, - context: &DebugInputContext, - addr_tr: &'a AddressTransform, - frame_info: Option<&FunctionFrameInfo>, - unit_encoding: gimli::Encoding, - out_unit: &mut write::Unit, - current_scope_id: write::UnitEntryId, - subprogram_range_builder: Option, - scope_ranges: Option<&Vec<(u64, u64)>>, - cu_low_pc: u64, - out_strings: &mut write::StringTable, - die_ref_map: &HashMap, - pending_die_refs: &mut Vec, - file_context: FileAttributeContext<'a>, -) -> Result<(), Error> -where - R: Reader, -{ - let _tag = &entry.tag(); - let endian = gimli::RunTimeEndian::Little; - - let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder { - subprogram_range_builder - } else if entry.tag() == gimli::DW_TAG_compile_unit { - // FIXME currently address_transform operate on a single func range, - // once it is fixed we can properly set DW_AT_ranges attribute. - // Using for now DW_AT_low_pc = 0. - RangeInfoBuilder::Position(0) - } else { - RangeInfoBuilder::from(entry, context, unit_encoding, cu_low_pc)? - }; - range_info.build(addr_tr, out_unit, current_scope_id); - - let mut attrs = entry.attrs(); - while let Some(attr) = attrs.next()? { - let attr_value = match attr.value() { - AttributeValue::Addr(_) if attr.name() == gimli::DW_AT_low_pc => { - continue; - } - AttributeValue::Udata(_) if attr.name() == gimli::DW_AT_high_pc => { - continue; - } - AttributeValue::RangeListsRef(_) if attr.name() == gimli::DW_AT_ranges => { - continue; - } - AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => { - continue; - } - - AttributeValue::Addr(u) => { - let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); - write::AttributeValue::Address(addr) - } - AttributeValue::Udata(u) => write::AttributeValue::Udata(u), - AttributeValue::Data1(d) => write::AttributeValue::Data1(d), - AttributeValue::Data2(d) => write::AttributeValue::Data2(d), - AttributeValue::Data4(d) => write::AttributeValue::Data4(d), - AttributeValue::Sdata(d) => write::AttributeValue::Sdata(d), - AttributeValue::Flag(f) => write::AttributeValue::Flag(f), - AttributeValue::DebugLineRef(line_program_offset) => { - if let FileAttributeContext::Root(o) = file_context { - if o != Some(line_program_offset) { - return Err(TransformError("invalid debug_line offset").into()); - } - write::AttributeValue::LineProgramRef - } else { - return Err(TransformError("unexpected debug_line index attribute").into()); - } - } - AttributeValue::FileIndex(i) => { - if let FileAttributeContext::Children(file_map, _) = file_context { - write::AttributeValue::FileIndex(Some(file_map[(i - 1) as usize])) - } else { - return Err(TransformError("unexpected file index attribute").into()); - } - } - AttributeValue::DebugStrRef(str_offset) => { - let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec(); - write::AttributeValue::StringRef(out_strings.add(s)) - } - AttributeValue::RangeListsRef(r) => { - let range_info = - RangeInfoBuilder::from_ranges_ref(r, context, unit_encoding, cu_low_pc)?; - let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges); - write::AttributeValue::RangeListRef(range_list_id) - } - AttributeValue::LocationListsRef(r) => { - let low_pc = 0; - let mut locs = context.loclists.locations( - r, - unit_encoding, - low_pc, - &context.debug_addr, - context.debug_addr_base, - )?; - let frame_base = if let FileAttributeContext::Children(_, frame_base) = file_context - { - frame_base - } else { - None - }; - let mut result = None; - while let Some(loc) = locs.next()? { - if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? { - if result.is_none() { - result = Some(Vec::new()); - } - for (start, len, expr) in expr.build_with_locals( - &[(loc.range.begin, loc.range.end)], - addr_tr, - frame_info, - endian, - ) { - if len == 0 { - // Ignore empty range - continue; - } - result.as_mut().unwrap().push(write::Location::StartLength { - begin: start, - length: len, - data: expr, - }); - } - } else { - // FIXME _expr contains invalid expression - continue; // ignore entry - } - } - if result.is_none() { - continue; // no valid locations - } - let list_id = out_unit.locations.add(write::LocationList(result.unwrap())); - write::AttributeValue::LocationListRef(list_id) - } - AttributeValue::Exprloc(ref expr) => { - let frame_base = if let FileAttributeContext::Children(_, frame_base) = file_context - { - frame_base - } else { - None - }; - if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? { - if expr.is_simple() { - if let Some(expr) = expr.build() { - write::AttributeValue::Exprloc(expr) - } else { - continue; - } - } else { - // Conversion to loclist is required. - if let Some(scope_ranges) = scope_ranges { - let exprs = - expr.build_with_locals(scope_ranges, addr_tr, frame_info, endian); - if exprs.is_empty() { - continue; - } - let found_single_expr = { - // Micro-optimization all expressions alike, use one exprloc. - let mut found_expr: Option = None; - for (_, _, expr) in &exprs { - if let Some(ref prev_expr) = found_expr { - if expr.0.eq(&prev_expr.0) { - continue; // the same expression - } - found_expr = None; - break; - } - found_expr = Some(expr.clone()) - } - found_expr - }; - if found_single_expr.is_some() { - write::AttributeValue::Exprloc(found_single_expr.unwrap()) - } else if is_exprloc_to_loclist_allowed(attr.name()) { - // Converting exprloc to loclist. - let mut locs = Vec::new(); - for (begin, length, data) in exprs { - if length == 0 { - // Ignore empty range - continue; - } - locs.push(write::Location::StartLength { - begin, - length, - data, - }); - } - let list_id = out_unit.locations.add(write::LocationList(locs)); - write::AttributeValue::LocationListRef(list_id) - } else { - continue; - } - } else { - continue; - } - } - } else { - // FIXME _expr contains invalid expression - continue; // ignore attribute - } - } - AttributeValue::Encoding(e) => write::AttributeValue::Encoding(e), - AttributeValue::DecimalSign(e) => write::AttributeValue::DecimalSign(e), - AttributeValue::Endianity(e) => write::AttributeValue::Endianity(e), - AttributeValue::Accessibility(e) => write::AttributeValue::Accessibility(e), - AttributeValue::Visibility(e) => write::AttributeValue::Visibility(e), - AttributeValue::Virtuality(e) => write::AttributeValue::Virtuality(e), - AttributeValue::Language(e) => write::AttributeValue::Language(e), - AttributeValue::AddressClass(e) => write::AttributeValue::AddressClass(e), - AttributeValue::IdentifierCase(e) => write::AttributeValue::IdentifierCase(e), - AttributeValue::CallingConvention(e) => write::AttributeValue::CallingConvention(e), - AttributeValue::Inline(e) => write::AttributeValue::Inline(e), - AttributeValue::Ordering(e) => write::AttributeValue::Ordering(e), - AttributeValue::UnitRef(ref offset) => { - if let Some(unit_id) = die_ref_map.get(offset) { - write::AttributeValue::ThisUnitEntryRef(*unit_id) - } else { - pending_die_refs.push((current_scope_id, attr.name(), *offset)); - continue; - } - } - // AttributeValue::DebugInfoRef(_) => { - // continue; - // } - _ => panic!(), //write::AttributeValue::StringRef(out_strings.add("_")), - }; - let current_scope = out_unit.get_mut(current_scope_id); - current_scope.set(attr.name(), attr_value); - } - Ok(()) -} - -pub(crate) fn clone_attr_string( - attr_value: &AttributeValue, - form: gimli::DwForm, - debug_str: &DebugStr, - out_strings: &mut write::StringTable, -) -> Result -where - R: Reader, -{ - let content = match attr_value { - AttributeValue::DebugStrRef(str_offset) => { - debug_str.get_str(*str_offset)?.to_slice()?.to_vec() - } - AttributeValue::String(b) => b.to_slice()?.to_vec(), - _ => panic!("Unexpected attribute value"), - }; - Ok(match form { - gimli::DW_FORM_strp => { - let id = out_strings.add(content); - write::LineString::StringRef(id) - } - gimli::DW_FORM_string => write::LineString::String(content), - _ => panic!("DW_FORM_line_strp or other not supported"), - }) -} diff --git a/lib/debug-writer/src/transform/expression.rs b/lib/debug-writer/src/transform/expression.rs deleted file mode 100644 index 390f5cba25a..00000000000 --- a/lib/debug-writer/src/transform/expression.rs +++ /dev/null @@ -1,515 +0,0 @@ -// Copyright 2029 WasmTime Project Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file is from the WasmTime project. -// It was copied at revision `3992b8669f9b9e185abe81e9998ce2ff4d40ff68`. -// -// Changes to this file are copyright of Wasmer inc. unless otherwise indicated -// and are licensed under the Wasmer project's license. -use super::address_transform::AddressTransform; -use anyhow::Error; -use cranelift_codegen::ir::{StackSlots, ValueLabel, ValueLoc}; -use cranelift_codegen::isa::RegUnit; -use cranelift_codegen::ValueLabelsRanges; -use cranelift_entity::EntityRef; -use cranelift_wasm::{get_vmctx_value_label, DefinedFuncIndex}; -use gimli::{self, write, Expression, Operation, Reader, ReaderOffset, Register, X86_64}; -use std::collections::{HashMap, HashSet}; - -#[derive(Debug)] -pub struct FunctionFrameInfo<'a> { - pub value_ranges: &'a ValueLabelsRanges, - pub memory_offset: i64, - pub stack_slots: &'a StackSlots, -} - -#[derive(Debug)] -enum CompiledExpressionPart { - Code(Vec), - Local(ValueLabel), - Deref, -} - -#[derive(Debug)] -pub struct CompiledExpression { - parts: Vec, - need_deref: bool, -} - -impl Clone for CompiledExpressionPart { - fn clone(&self) -> Self { - match self { - CompiledExpressionPart::Code(c) => CompiledExpressionPart::Code(c.clone()), - CompiledExpressionPart::Local(i) => CompiledExpressionPart::Local(*i), - CompiledExpressionPart::Deref => CompiledExpressionPart::Deref, - } - } -} - -impl CompiledExpression { - pub fn vmctx() -> CompiledExpression { - CompiledExpression::from_label(get_vmctx_value_label()) - } - - pub fn from_label(label: ValueLabel) -> CompiledExpression { - CompiledExpression { - parts: vec![ - CompiledExpressionPart::Local(label), - CompiledExpressionPart::Code(vec![gimli::constants::DW_OP_stack_value.0 as u8]), - ], - need_deref: false, - } - } -} - -fn map_reg(reg: RegUnit) -> Register { - static mut REG_X86_MAP: Option> = None; - // FIXME lazy initialization? - unsafe { - if REG_X86_MAP.is_none() { - REG_X86_MAP = Some(HashMap::new()); - } - if let Some(val) = REG_X86_MAP.as_mut().unwrap().get(®) { - return *val; - } - let result = match reg { - 0 => X86_64::RAX, - 1 => X86_64::RCX, - 2 => X86_64::RDX, - 3 => X86_64::RBX, - 4 => X86_64::RSP, - 5 => X86_64::RBP, - 6 => X86_64::RSI, - 7 => X86_64::RDI, - 8 => X86_64::R8, - 9 => X86_64::R9, - 10 => X86_64::R10, - 11 => X86_64::R11, - 12 => X86_64::R12, - 13 => X86_64::R13, - 14 => X86_64::R14, - 15 => X86_64::R15, - 16 => X86_64::XMM0, - 17 => X86_64::XMM1, - 18 => X86_64::XMM2, - 19 => X86_64::XMM3, - 20 => X86_64::XMM4, - 21 => X86_64::XMM5, - 22 => X86_64::XMM6, - 23 => X86_64::XMM7, - 24 => X86_64::XMM8, - 25 => X86_64::XMM9, - 26 => X86_64::XMM10, - 27 => X86_64::XMM11, - 28 => X86_64::XMM12, - 29 => X86_64::XMM13, - 30 => X86_64::XMM14, - 31 => X86_64::XMM15, - _ => panic!("unknown x86_64 register {}", reg), - }; - REG_X86_MAP.as_mut().unwrap().insert(reg, result); - result - } -} - -fn translate_loc(loc: ValueLoc, frame_info: Option<&FunctionFrameInfo>) -> Option> { - match loc { - ValueLoc::Reg(reg) => { - let machine_reg = map_reg(reg).0 as u8; - assert!(machine_reg < 32); // FIXME - Some(vec![gimli::constants::DW_OP_reg0.0 + machine_reg]) - } - ValueLoc::Stack(ss) => { - if let Some(frame_info) = frame_info { - if let Some(ss_offset) = frame_info.stack_slots[ss].offset { - use gimli::write::Writer; - let endian = gimli::RunTimeEndian::Little; - let mut writer = write::EndianVec::new(endian); - writer - .write_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8) - .expect("bp wr"); - writer.write_sleb128(ss_offset as i64 + 16).expect("ss wr"); - writer - .write_u8(gimli::constants::DW_OP_deref.0 as u8) - .expect("bp wr"); - let buf = writer.into_vec(); - return Some(buf); - } - } - None - } - _ => None, - } -} - -fn append_memory_deref( - buf: &mut Vec, - frame_info: &FunctionFrameInfo, - vmctx_loc: ValueLoc, - endian: gimli::RunTimeEndian, -) -> write::Result { - use gimli::write::Writer; - let mut writer = write::EndianVec::new(endian); - match vmctx_loc { - ValueLoc::Reg(vmctx_reg) => { - let reg = map_reg(vmctx_reg); - writer.write_u8(gimli::constants::DW_OP_breg0.0 + reg.0 as u8)?; - writer.write_sleb128(frame_info.memory_offset)?; - } - ValueLoc::Stack(ss) => { - if let Some(ss_offset) = frame_info.stack_slots[ss].offset { - writer.write_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8)?; - writer.write_sleb128(ss_offset as i64 + 16)?; - writer.write_u8(gimli::constants::DW_OP_deref.0 as u8)?; - - writer.write_u8(gimli::constants::DW_OP_consts.0 as u8)?; - writer.write_sleb128(frame_info.memory_offset)?; - writer.write_u8(gimli::constants::DW_OP_plus.0 as u8)?; - } else { - return Ok(false); - } - } - _ => { - return Ok(false); - } - } - writer.write_u8(gimli::constants::DW_OP_deref.0 as u8)?; - writer.write_u8(gimli::constants::DW_OP_swap.0 as u8)?; - writer.write_u8(gimli::constants::DW_OP_stack_value.0 as u8)?; - writer.write_u8(gimli::constants::DW_OP_constu.0 as u8)?; - writer.write_uleb128(0xffff_ffff)?; - writer.write_u8(gimli::constants::DW_OP_and.0 as u8)?; - writer.write_u8(gimli::constants::DW_OP_plus.0 as u8)?; - buf.extend_from_slice(writer.slice()); - Ok(true) -} - -impl CompiledExpression { - pub fn is_simple(&self) -> bool { - if let [CompiledExpressionPart::Code(_)] = self.parts.as_slice() { - true - } else { - self.parts.is_empty() - } - } - - pub fn build(&self) -> Option { - if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() { - return Some(write::Expression(code.to_vec())); - } - // locals found, not supported - None - } - - pub fn build_with_locals( - &self, - scope: &[(u64, u64)], // wasm ranges - addr_tr: &AddressTransform, - frame_info: Option<&FunctionFrameInfo>, - endian: gimli::RunTimeEndian, - ) -> Vec<(write::Address, u64, write::Expression)> { - if scope.is_empty() { - return vec![]; - } - - if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() { - let mut result_scope = Vec::new(); - for s in scope { - for (addr, len) in addr_tr.translate_ranges(s.0, s.1) { - result_scope.push((addr, len, write::Expression(code.to_vec()))); - } - } - return result_scope; - } - - let vmctx_label = get_vmctx_value_label(); - - // Some locals are present, preparing and divided ranges based on the scope - // and frame_info data. - let mut ranges_builder = ValueLabelRangesBuilder::new(scope, addr_tr, frame_info); - for p in &self.parts { - match p { - CompiledExpressionPart::Code(_) => (), - CompiledExpressionPart::Local(label) => ranges_builder.process_label(*label), - CompiledExpressionPart::Deref => ranges_builder.process_label(vmctx_label), - } - } - if self.need_deref { - ranges_builder.process_label(vmctx_label); - } - ranges_builder.remove_incomplete_ranges(); - let ranges = ranges_builder.ranges; - - let mut result = Vec::new(); - 'range: for CachedValueLabelRange { - func_index, - start, - end, - label_location, - } in ranges - { - // build expression - let mut code_buf = Vec::new(); - for part in &self.parts { - match part { - CompiledExpressionPart::Code(c) => code_buf.extend_from_slice(c.as_slice()), - CompiledExpressionPart::Local(label) => { - let loc = *label_location.get(&label).expect("loc"); - if let Some(expr) = translate_loc(loc, frame_info) { - code_buf.extend_from_slice(&expr) - } else { - continue 'range; - } - } - CompiledExpressionPart::Deref => { - if let (Some(vmctx_loc), Some(frame_info)) = - (label_location.get(&vmctx_label), frame_info) - { - if !append_memory_deref(&mut code_buf, frame_info, *vmctx_loc, endian) - .expect("append_memory_deref") - { - continue 'range; - } - } else { - continue 'range; - }; - } - } - } - if self.need_deref { - if let (Some(vmctx_loc), Some(frame_info)) = - (label_location.get(&vmctx_label), frame_info) - { - if !append_memory_deref(&mut code_buf, frame_info, *vmctx_loc, endian) - .expect("append_memory_deref") - { - continue 'range; - } - } else { - continue 'range; - }; - } - result.push(( - write::Address::Symbol { - symbol: func_index.index(), - addend: start as i64, - }, - (end - start) as u64, - write::Expression(code_buf), - )); - } - - result - } -} - -pub fn compile_expression( - expr: &Expression, - encoding: gimli::Encoding, - frame_base: Option<&CompiledExpression>, -) -> Result, Error> -where - R: Reader, -{ - let mut parts = Vec::new(); - let mut need_deref = false; - if let Some(frame_base) = frame_base { - parts.extend_from_slice(&frame_base.parts); - need_deref = frame_base.need_deref; - } - let base_len = parts.len(); - let mut pc = expr.0.clone(); - let mut code_chunk = Vec::new(); - let buf = expr.0.to_slice()?; - while !pc.is_empty() { - let next = buf[pc.offset_from(&expr.0).into_u64() as usize]; - need_deref = true; - if next == 0xED { - // WebAssembly DWARF extension - pc.read_u8()?; - let ty = pc.read_uleb128()?; - assert_eq!(ty, 0); - let index = pc.read_sleb128()?; - pc.read_u8()?; // consume 159 - if code_chunk.len() > 0 { - parts.push(CompiledExpressionPart::Code(code_chunk)); - code_chunk = Vec::new(); - } - let label = ValueLabel::from_u32(index as u32); - parts.push(CompiledExpressionPart::Local(label)); - } else { - let pos = pc.offset_from(&expr.0).into_u64() as usize; - let op = Operation::parse(&mut pc, &expr.0, encoding)?; - match op { - Operation::Literal { .. } | Operation::PlusConstant { .. } => (), - Operation::StackValue => { - need_deref = false; - } - Operation::Deref { .. } => { - if code_chunk.len() > 0 { - parts.push(CompiledExpressionPart::Code(code_chunk)); - code_chunk = Vec::new(); - } - parts.push(CompiledExpressionPart::Deref); - } - _ => { - return Ok(None); - } - } - let chunk = &buf[pos..pc.offset_from(&expr.0).into_u64() as usize]; - code_chunk.extend_from_slice(chunk); - } - } - - if code_chunk.len() > 0 { - parts.push(CompiledExpressionPart::Code(code_chunk)); - } - - if base_len > 0 && base_len + 1 < parts.len() { - // see if we can glue two code chunks - if let [CompiledExpressionPart::Code(cc1), CompiledExpressionPart::Code(cc2)] = - &parts[base_len..base_len + 1] - { - let mut combined = cc1.clone(); - combined.extend_from_slice(cc2); - parts[base_len] = CompiledExpressionPart::Code(combined); - parts.remove(base_len + 1); - } - } - - Ok(Some(CompiledExpression { parts, need_deref })) -} - -#[derive(Debug, Clone)] -struct CachedValueLabelRange { - func_index: DefinedFuncIndex, - start: usize, - end: usize, - label_location: HashMap, -} - -struct ValueLabelRangesBuilder<'a, 'b> { - ranges: Vec, - addr_tr: &'a AddressTransform, - frame_info: Option<&'a FunctionFrameInfo<'b>>, - processed_labels: HashSet, -} - -impl<'a, 'b> ValueLabelRangesBuilder<'a, 'b> { - fn new( - scope: &[(u64, u64)], // wasm ranges - addr_tr: &'a AddressTransform, - frame_info: Option<&'a FunctionFrameInfo<'b>>, - ) -> Self { - let mut ranges = Vec::new(); - for s in scope { - if let Some((func_index, tr)) = addr_tr.translate_ranges_raw(s.0, s.1) { - for (start, end) in tr { - ranges.push(CachedValueLabelRange { - func_index, - start, - end, - label_location: HashMap::new(), - }) - } - } - } - ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start)); - ValueLabelRangesBuilder { - ranges, - addr_tr, - frame_info, - processed_labels: HashSet::new(), - } - } - - fn process_label(&mut self, label: ValueLabel) { - if self.processed_labels.contains(&label) { - return; - } - self.processed_labels.insert(label); - - let value_ranges = if let Some(frame_info) = self.frame_info { - &frame_info.value_ranges - } else { - return; - }; - - let ranges = &mut self.ranges; - if let Some(local_ranges) = value_ranges.get(&label) { - for local_range in local_ranges { - let wasm_start = local_range.start; - let wasm_end = local_range.end; - let loc = local_range.loc; - // Find all native ranges for the value label ranges. - for (addr, len) in self - .addr_tr - .translate_ranges(wasm_start as u64, wasm_end as u64) - { - let (range_start, range_end) = self.addr_tr.convert_to_code_range(addr, len); - if range_start == range_end { - continue; - } - assert!(range_start < range_end); - // Find acceptable scope of ranges to intersect with. - let i = match ranges.binary_search_by(|s| s.start.cmp(&range_start)) { - Ok(i) => i, - Err(i) => { - if i > 0 && range_start < ranges[i - 1].end { - i - 1 - } else { - i - } - } - }; - let j = match ranges.binary_search_by(|s| s.start.cmp(&range_end)) { - Ok(i) | Err(i) => i, - }; - // Starting for the end, intersect (range_start..range_end) with - // self.ranges array. - for i in (i..j).rev() { - if range_end <= ranges[i].start || ranges[i].end <= range_start { - continue; - } - if range_end < ranges[i].end { - // Cutting some of the range from the end. - let mut tail = ranges[i].clone(); - ranges[i].end = range_end; - tail.start = range_end; - ranges.insert(i + 1, tail); - } - assert!(ranges[i].end <= range_end); - if range_start <= ranges[i].start { - ranges[i].label_location.insert(label, loc); - continue; - } - // Cutting some of the range from the start. - let mut tail = ranges[i].clone(); - ranges[i].end = range_start; - tail.start = range_start; - tail.label_location.insert(label, loc); - ranges.insert(i + 1, tail); - } - } - } - } - } - - fn remove_incomplete_ranges(&mut self) { - // Ranges with not-enough labels are discarded. - let processed_labels_len = self.processed_labels.len(); - self.ranges - .retain(|r| r.label_location.len() == processed_labels_len); - } -} diff --git a/lib/debug-writer/src/transform/line_program.rs b/lib/debug-writer/src/transform/line_program.rs deleted file mode 100644 index f584630a082..00000000000 --- a/lib/debug-writer/src/transform/line_program.rs +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2019 WasmTime Project Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file is from the WasmTime project. -// It was copied at revision `cc6e8e1af25e5f9b64e183970d50f62c8338f259`. -// -// Changes to this file are copyright of Wasmer inc. unless otherwise indicated -// and are licensed under the Wasmer project's license. -use super::address_transform::AddressTransform; -use super::attr::clone_attr_string; -use super::{Reader, TransformError}; -use anyhow::Error; -use cranelift_entity::EntityRef; -use gimli::{ - write, DebugLine, DebugLineOffset, DebugStr, DebuggingInformationEntry, LineEncoding, Unit, -}; -use std::collections::BTreeMap; -use std::iter::FromIterator; - -#[derive(Debug)] -enum SavedLineProgramRow { - Normal { - address: u64, - op_index: u64, - file_index: u64, - line: u64, - column: u64, - discriminator: u64, - is_stmt: bool, - basic_block: bool, - prologue_end: bool, - epilogue_begin: bool, - isa: u64, - }, - EndOfSequence(u64), -} - -#[derive(Debug, Eq, PartialEq)] -enum ReadLineProgramState { - SequenceEnded, - ReadSequence, - IgnoreSequence, -} - -pub(crate) fn clone_line_program( - unit: &Unit, - root: &DebuggingInformationEntry, - addr_tr: &AddressTransform, - out_encoding: gimli::Encoding, - debug_str: &DebugStr, - debug_line: &DebugLine, - out_strings: &mut write::StringTable, -) -> Result<(write::LineProgram, DebugLineOffset, Vec), Error> -where - R: Reader, -{ - let offset = match root.attr_value(gimli::DW_AT_stmt_list)? { - Some(gimli::AttributeValue::DebugLineRef(offset)) => offset, - _ => { - return Err(TransformError("Debug line offset is not found").into()); - } - }; - let comp_dir = root.attr_value(gimli::DW_AT_comp_dir)?; - let comp_name = root.attr_value(gimli::DW_AT_name)?; - let out_comp_dir = clone_attr_string( - comp_dir.as_ref().expect("comp_dir"), - gimli::DW_FORM_strp, - debug_str, - out_strings, - )?; - let out_comp_name = clone_attr_string( - comp_name.as_ref().expect("comp_name"), - gimli::DW_FORM_strp, - debug_str, - out_strings, - )?; - - let program = debug_line.program( - offset, - unit.header.address_size(), - comp_dir.and_then(|val| val.string_value(&debug_str)), - comp_name.and_then(|val| val.string_value(&debug_str)), - ); - if let Ok(program) = program { - let header = program.header(); - assert!(header.version() <= 4, "not supported 5"); - let line_encoding = LineEncoding { - minimum_instruction_length: header.minimum_instruction_length(), - maximum_operations_per_instruction: header.maximum_operations_per_instruction(), - default_is_stmt: header.default_is_stmt(), - line_base: header.line_base(), - line_range: header.line_range(), - }; - let mut out_program = write::LineProgram::new( - out_encoding, - line_encoding, - out_comp_dir, - out_comp_name, - None, - ); - let mut dirs = Vec::new(); - dirs.push(out_program.default_directory()); - for dir_attr in header.include_directories() { - let dir_id = out_program.add_directory(clone_attr_string( - dir_attr, - gimli::DW_FORM_string, - debug_str, - out_strings, - )?); - dirs.push(dir_id); - } - let mut files = Vec::new(); - for file_entry in header.file_names() { - let dir_id = dirs[file_entry.directory_index() as usize]; - let file_id = out_program.add_file( - clone_attr_string( - &file_entry.path_name(), - gimli::DW_FORM_string, - debug_str, - out_strings, - )?, - dir_id, - None, - ); - files.push(file_id); - } - - let mut rows = program.rows(); - let mut saved_rows = BTreeMap::new(); - let mut state = ReadLineProgramState::SequenceEnded; - while let Some((_header, row)) = rows.next_row()? { - if state == ReadLineProgramState::IgnoreSequence { - if row.end_sequence() { - state = ReadLineProgramState::SequenceEnded; - } - continue; - } - let saved_row = if row.end_sequence() { - state = ReadLineProgramState::SequenceEnded; - SavedLineProgramRow::EndOfSequence(row.address()) - } else { - if state == ReadLineProgramState::SequenceEnded { - // Discard sequences for non-existent code. - if row.address() == 0 { - state = ReadLineProgramState::IgnoreSequence; - continue; - } - state = ReadLineProgramState::ReadSequence; - } - SavedLineProgramRow::Normal { - address: row.address(), - op_index: row.op_index(), - file_index: row.file_index(), - line: row.line().unwrap_or(0), - column: match row.column() { - gimli::ColumnType::LeftEdge => 0, - gimli::ColumnType::Column(val) => val, - }, - discriminator: row.discriminator(), - is_stmt: row.is_stmt(), - basic_block: row.basic_block(), - prologue_end: row.prologue_end(), - epilogue_begin: row.epilogue_begin(), - isa: row.isa(), - } - }; - saved_rows.insert(row.address(), saved_row); - } - - let saved_rows = Vec::from_iter(saved_rows.into_iter()); - for (i, map) in addr_tr.map() { - if map.len == 0 { - continue; // no code generated - } - let symbol = i.index(); - let base_addr = map.offset; - out_program.begin_sequence(Some(write::Address::Symbol { symbol, addend: 0 })); - // TODO track and place function declaration line here - let mut last_address = None; - for addr_map in map.addresses.iter() { - let saved_row = match saved_rows.binary_search_by_key(&addr_map.wasm, |i| i.0) { - Ok(i) => Some(&saved_rows[i].1), - Err(i) => { - if i > 0 { - Some(&saved_rows[i - 1].1) - } else { - None - } - } - }; - if let Some(SavedLineProgramRow::Normal { - address, - op_index, - file_index, - line, - column, - discriminator, - is_stmt, - basic_block, - prologue_end, - epilogue_begin, - isa, - }) = saved_row - { - // Ignore duplicates - if Some(*address) != last_address { - let address_offset = if last_address.is_none() { - // Extend first entry to the function declaration - // TODO use the function declaration line instead - 0 - } else { - (addr_map.generated - base_addr) as u64 - }; - out_program.row().address_offset = address_offset; - out_program.row().op_index = *op_index; - out_program.row().file = files[(file_index - 1) as usize]; - out_program.row().line = *line; - out_program.row().column = *column; - out_program.row().discriminator = *discriminator; - out_program.row().is_statement = *is_stmt; - out_program.row().basic_block = *basic_block; - out_program.row().prologue_end = *prologue_end; - out_program.row().epilogue_begin = *epilogue_begin; - out_program.row().isa = *isa; - out_program.generate_row(); - last_address = Some(*address); - } - } - } - let end_addr = (map.offset + map.len - 1) as u64; - out_program.end_sequence(end_addr); - } - Ok((out_program, offset, files)) - } else { - Err(TransformError("Valid line program not found").into()) - } -} diff --git a/lib/debug-writer/src/transform/mod.rs b/lib/debug-writer/src/transform/mod.rs deleted file mode 100644 index 9b79765c209..00000000000 --- a/lib/debug-writer/src/transform/mod.rs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2019 WasmTime Project Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file is from the WasmTime project. -// It was copied at revision `907e7aac01af333a0af310ce0472abbc8a9adb6c`. -// -// Changes to this file are copyright of Wasmer inc. unless otherwise indicated -// and are licensed under the Wasmer project's license. -use crate::gc::build_dependencies; -use crate::DebugInfoData; -use anyhow::Error; -use cranelift_codegen::isa::TargetFrontendConfig; -use gimli::{ - write, DebugAddr, DebugAddrBase, DebugLine, DebugStr, LocationLists, RangeLists, - UnitSectionOffset, -}; -use simulate::generate_simulated_dwarf; -use std::collections::HashSet; -use thiserror::Error; -use unit::clone_unit; -use wasmtime_environ::{ModuleAddressMap, ModuleVmctxInfo, ValueLabelsRanges}; - -pub use address_transform::AddressTransform; - -mod address_transform; -mod attr; -mod expression; -mod line_program; -mod range_info_builder; -mod simulate; -mod unit; -mod utils; - -pub(crate) trait Reader: gimli::Reader {} - -impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> where Endian: gimli::Endianity {} - -#[derive(Error, Debug)] -#[error("Debug info transform error: {0}")] -pub struct TransformError(&'static str); - -pub(crate) struct DebugInputContext<'a, R> -where - R: Reader, -{ - debug_str: &'a DebugStr, - debug_line: &'a DebugLine, - debug_addr: &'a DebugAddr, - debug_addr_base: DebugAddrBase, - rnglists: &'a RangeLists, - loclists: &'a LocationLists, - reachable: &'a HashSet, -} - -pub fn transform_dwarf( - target_config: &TargetFrontendConfig, - di: &DebugInfoData, - at: &ModuleAddressMap, - vmctx_info: &ModuleVmctxInfo, - ranges: &ValueLabelsRanges, -) -> Result { - let addr_tr = AddressTransform::new(at, &di.wasm_file); - let reachable = build_dependencies(&di.dwarf, &addr_tr)?.get_reachable(); - - let context = DebugInputContext { - debug_str: &di.dwarf.debug_str, - debug_line: &di.dwarf.debug_line, - debug_addr: &di.dwarf.debug_addr, - debug_addr_base: DebugAddrBase(0), - rnglists: &di.dwarf.ranges, - loclists: &di.dwarf.locations, - reachable: &reachable, - }; - - let out_encoding = gimli::Encoding { - format: gimli::Format::Dwarf32, - // TODO: this should be configurable - // macOS doesn't seem to support DWARF > 3 - version: 3, - address_size: target_config.pointer_bytes(), - }; - - let mut out_strings = write::StringTable::default(); - let mut out_units = write::UnitTable::default(); - - let out_line_strings = write::LineStringTable::default(); - - let mut translated = HashSet::new(); - let mut iter = di.dwarf.debug_info.units(); - while let Some(unit) = iter.next().unwrap_or(None) { - let unit = di.dwarf.unit(unit)?; - clone_unit( - unit, - &context, - &addr_tr, - &ranges, - out_encoding, - &vmctx_info, - &mut out_units, - &mut out_strings, - &mut translated, - )?; - } - - generate_simulated_dwarf( - &addr_tr, - di, - &vmctx_info, - &ranges, - &translated, - out_encoding, - &mut out_units, - &mut out_strings, - )?; - - Ok(write::Dwarf { - units: out_units, - line_programs: vec![], - line_strings: out_line_strings, - strings: out_strings, - }) -} diff --git a/lib/debug-writer/src/transform/range_info_builder.rs b/lib/debug-writer/src/transform/range_info_builder.rs deleted file mode 100644 index 881dcca1e52..00000000000 --- a/lib/debug-writer/src/transform/range_info_builder.rs +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2019 WasmTime Project Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file is from the WasmTime project. -// It was copied at revision `907e7aac01af333a0af310ce0472abbc8a9adb6c`. -// -// Changes to this file are copyright of Wasmer inc. unless otherwise indicated -// and are licensed under the Wasmer project's license. -use super::address_transform::AddressTransform; -use super::{DebugInputContext, Reader}; -use anyhow::Error; -use cranelift_entity::EntityRef; -use wasmer_runtime_core::types::FuncIndex -use gimli::{write, AttributeValue, DebuggingInformationEntry, RangeListsOffset}; - -pub(crate) enum RangeInfoBuilder { - Undefined, - Position(u64), - Ranges(Vec<(u64, u64)>), - Function(FuncIndex), -} - -impl RangeInfoBuilder { - pub(crate) fn from( - entry: &DebuggingInformationEntry, - context: &DebugInputContext, - unit_encoding: gimli::Encoding, - cu_low_pc: u64, - ) -> Result - where - R: Reader, - { - if let Some(AttributeValue::RangeListsRef(r)) = entry.attr_value(gimli::DW_AT_ranges)? { - return RangeInfoBuilder::from_ranges_ref(r, context, unit_encoding, cu_low_pc); - }; - - let low_pc = - if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? { - addr - } else { - return Ok(RangeInfoBuilder::Undefined); - }; - - Ok( - if let Some(AttributeValue::Udata(u)) = entry.attr_value(gimli::DW_AT_high_pc)? { - RangeInfoBuilder::Ranges(vec![(low_pc, low_pc + u)]) - } else { - RangeInfoBuilder::Position(low_pc) - }, - ) - } - - pub(crate) fn from_ranges_ref( - ranges: RangeListsOffset, - context: &DebugInputContext, - unit_encoding: gimli::Encoding, - cu_low_pc: u64, - ) -> Result - where - R: Reader, - { - let mut ranges = context.rnglists.ranges( - ranges, - unit_encoding, - cu_low_pc, - &context.debug_addr, - context.debug_addr_base, - )?; - let mut result = Vec::new(); - while let Some(range) = ranges.next()? { - if range.begin >= range.end { - // ignore empty ranges - } - result.push((range.begin, range.end)); - } - - Ok(if result.len() > 0 { - RangeInfoBuilder::Ranges(result) - } else { - RangeInfoBuilder::Undefined - }) - } - - pub(crate) fn from_subprogram_die( - entry: &DebuggingInformationEntry, - context: &DebugInputContext, - unit_encoding: gimli::Encoding, - addr_tr: &AddressTransform, - cu_low_pc: u64, - ) -> Result - where - R: Reader, - { - let addr = - if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? { - addr - } else if let Some(AttributeValue::RangeListsRef(r)) = - entry.attr_value(gimli::DW_AT_ranges)? - { - let mut ranges = context.rnglists.ranges( - r, - unit_encoding, - cu_low_pc, - &context.debug_addr, - context.debug_addr_base, - )?; - if let Some(range) = ranges.next()? { - range.begin - } else { - return Ok(RangeInfoBuilder::Undefined); - } - } else { - return Ok(RangeInfoBuilder::Undefined); - }; - - let index = addr_tr.find_func_index(addr); - if index.is_none() { - return Ok(RangeInfoBuilder::Undefined); - } - Ok(RangeInfoBuilder::Function(index.unwrap())) - } - - pub(crate) fn build( - &self, - addr_tr: &AddressTransform, - out_unit: &mut write::Unit, - current_scope_id: write::UnitEntryId, - ) { - match self { - RangeInfoBuilder::Undefined => (), - RangeInfoBuilder::Position(pc) => { - let addr = addr_tr - .translate(*pc) - .unwrap_or(write::Address::Constant(0)); - let current_scope = out_unit.get_mut(current_scope_id); - current_scope.set(gimli::DW_AT_low_pc, write::AttributeValue::Address(addr)); - } - RangeInfoBuilder::Ranges(ranges) => { - let mut result = Vec::new(); - for (begin, end) in ranges { - for tr in addr_tr.translate_ranges(*begin, *end) { - if tr.1 == 0 { - // Ignore empty range - continue; - } - result.push(tr); - } - } - if result.len() != 1 { - let range_list = result - .iter() - .map(|tr| write::Range::StartLength { - begin: tr.0, - length: tr.1, - }) - .collect::>(); - let range_list_id = out_unit.ranges.add(write::RangeList(range_list)); - let current_scope = out_unit.get_mut(current_scope_id); - current_scope.set( - gimli::DW_AT_ranges, - write::AttributeValue::RangeListRef(range_list_id), - ); - } else { - let current_scope = out_unit.get_mut(current_scope_id); - current_scope.set( - gimli::DW_AT_low_pc, - write::AttributeValue::Address(result[0].0), - ); - current_scope.set( - gimli::DW_AT_high_pc, - write::AttributeValue::Udata(result[0].1), - ); - } - } - RangeInfoBuilder::Function(index) => { - let range = addr_tr.func_range(*index); - let symbol = index.index(); - let addr = write::Address::Symbol { - symbol, - addend: range.0 as i64, - }; - let len = (range.1 - range.0) as u64; - let current_scope = out_unit.get_mut(current_scope_id); - current_scope.set(gimli::DW_AT_low_pc, write::AttributeValue::Address(addr)); - current_scope.set(gimli::DW_AT_high_pc, write::AttributeValue::Udata(len)); - } - } - } - - pub(crate) fn get_ranges(&self, addr_tr: &AddressTransform) -> Vec<(u64, u64)> { - match self { - RangeInfoBuilder::Undefined | RangeInfoBuilder::Position(_) => vec![], - RangeInfoBuilder::Ranges(ranges) => ranges.clone(), - RangeInfoBuilder::Function(index) => { - let range = addr_tr.func_source_range(*index); - vec![(range.0, range.1)] - } - } - } - - pub(crate) fn build_ranges( - &self, - addr_tr: &AddressTransform, - out_range_lists: &mut write::RangeListTable, - ) -> write::RangeListId { - if let RangeInfoBuilder::Ranges(ranges) = self { - let mut range_list = Vec::new(); - for (begin, end) in ranges { - assert!(begin < end); - for tr in addr_tr.translate_ranges(*begin, *end) { - if tr.1 == 0 { - // Ignore empty range - continue; - } - range_list.push(write::Range::StartLength { - begin: tr.0, - length: tr.1, - }); - } - } - out_range_lists.add(write::RangeList(range_list)) - } else { - unreachable!(); - } - } -} diff --git a/lib/debug-writer/src/transform/simulate.rs b/lib/debug-writer/src/transform/simulate.rs deleted file mode 100644 index 244878c7364..00000000000 --- a/lib/debug-writer/src/transform/simulate.rs +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright 2019 WasmTime Project Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file is from the WasmTime project. -// It was copied at revision `cc6e8e1af25e5f9b64e183970d50f62c8338f259`. -// -// Changes to this file are copyright of Wasmer inc. unless otherwise indicated -// and are licensed under the Wasmer project's license. -use super::expression::{CompiledExpression, FunctionFrameInfo}; -use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info}; -use super::AddressTransform; -use crate::read_debug_info::WasmFileInfo; -use anyhow::Error; -use cranelift_entity::EntityRef; -use cranelift_wasm::get_vmctx_value_label; -use gimli::write; -use gimli::{self, LineEncoding}; -use std::collections::{HashMap, HashSet}; -use std::path::PathBuf; -// TODO: ValueLabelsRanges -use wasmer_runtime_core::{module::ModuleInfo, state::CodeVersion}; - -pub use crate::read_debug_info::{DebugInfoData, FunctionMetadata, WasmType}; - -const PRODUCER_NAME: &str = "wasmtime"; - -fn generate_line_info( - addr_tr: &AddressTransform, - translated: &HashSet, - out_encoding: gimli::Encoding, - w: &WasmFileInfo, - comp_dir_id: write::StringId, - name_id: write::StringId, - name: &str, -) -> Result { - let out_comp_dir = write::LineString::StringRef(comp_dir_id); - let out_comp_name = write::LineString::StringRef(name_id); - - let line_encoding = LineEncoding::default(); - - let mut out_program = write::LineProgram::new( - out_encoding, - line_encoding, - out_comp_dir, - out_comp_name, - None, - ); - - let file_index = out_program.add_file( - write::LineString::String(name.as_bytes().to_vec()), - out_program.default_directory(), - None, - ); - - for (i, map) in addr_tr.map() { - let symbol = i.index(); - if translated.contains(&(symbol as u32)) { - continue; - } - - let base_addr = map.offset; - out_program.begin_sequence(Some(write::Address::Symbol { symbol, addend: 0 })); - for addr_map in map.addresses.iter() { - let address_offset = (addr_map.generated - base_addr) as u64; - out_program.row().address_offset = address_offset; - out_program.row().op_index = 0; - out_program.row().file = file_index; - let wasm_offset = w.code_section_offset + addr_map.wasm as u64; - out_program.row().line = wasm_offset; - out_program.row().column = 0; - out_program.row().discriminator = 1; - out_program.row().is_statement = true; - out_program.row().basic_block = false; - out_program.row().prologue_end = false; - out_program.row().epilogue_begin = false; - out_program.row().isa = 0; - out_program.generate_row(); - } - let end_addr = (map.offset + map.len - 1) as u64; - out_program.end_sequence(end_addr); - } - - Ok(out_program) -} - -fn autogenerate_dwarf_wasm_path(di: &DebugInfoData) -> PathBuf { - let module_name = di - .name_section - .as_ref() - .and_then(|ns| ns.module_name.to_owned()) - .unwrap_or_else(|| unsafe { - static mut GEN_ID: u32 = 0; - GEN_ID += 1; - format!("", GEN_ID) - }); - let path = format!("//{}.wasm", module_name); - PathBuf::from(path) -} - -struct WasmTypesDieRefs { - vmctx: write::UnitEntryId, - i32: write::UnitEntryId, - i64: write::UnitEntryId, - i128: write::UnitEntryId, - f32: write::UnitEntryId, - f64: write::UnitEntryId, -} - -fn add_wasm_types( - unit: &mut write::Unit, - root_id: write::UnitEntryId, - out_strings: &mut write::StringTable, - vmctx_info: &ModuleInfo, -) -> WasmTypesDieRefs { - let (_wp_die_id, vmctx_die_id) = add_internal_types(unit, root_id, out_strings, vmctx_info); - - macro_rules! def_type { - ($id:literal, $size:literal, $enc:path) => {{ - let die_id = unit.add(root_id, gimli::DW_TAG_base_type); - let die = unit.get_mut(die_id); - die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add($id)), - ); - die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1($size)); - die.set(gimli::DW_AT_encoding, write::AttributeValue::Encoding($enc)); - die_id - }}; - } - - let i32_die_id = def_type!("i32", 4, gimli::DW_ATE_signed); - let i64_die_id = def_type!("i64", 8, gimli::DW_ATE_signed); - let i128_die_id = def_type!("i128", 16, gimli::DW_ATE_signed); - let f32_die_id = def_type!("f32", 4, gimli::DW_ATE_float); - let f64_die_id = def_type!("f64", 8, gimli::DW_ATE_float); - - WasmTypesDieRefs { - vmctx: vmctx_die_id, - i32: i32_die_id, - i64: i64_die_id, - i128: i128_die_id, - f32: f32_die_id, - f64: f64_die_id, - } -} - -fn resolve_var_type( - index: usize, - wasm_types: &WasmTypesDieRefs, - func_meta: &FunctionMetadata, -) -> Option<(write::UnitEntryId, bool)> { - let (ty, is_param) = if index < func_meta.params.len() { - (func_meta.params[index], true) - } else { - let mut i = (index - func_meta.params.len()) as u32; - let mut j = 0; - while j < func_meta.locals.len() && i >= func_meta.locals[j].0 { - i -= func_meta.locals[j].0; - j += 1; - } - if j >= func_meta.locals.len() { - // Ignore the var index out of bound. - return None; - } - (func_meta.locals[j].1, false) - }; - let type_die_id = match ty { - WasmType::I32 => wasm_types.i32, - WasmType::I64 => wasm_types.i64, - WasmType::F32 => wasm_types.f32, - WasmType::F64 => wasm_types.f64, - _ => { - // Ignore unsupported types. - return None; - } - }; - Some((type_die_id, is_param)) -} - -fn generate_vars( - unit: &mut write::Unit, - die_id: write::UnitEntryId, - addr_tr: &AddressTransform, - frame_info: &FunctionFrameInfo, - scope_ranges: &[(u64, u64)], - wasm_types: &WasmTypesDieRefs, - func_meta: &FunctionMetadata, - locals_names: Option<&HashMap>, - out_strings: &mut write::StringTable, -) { - let vmctx_label = get_vmctx_value_label(); - - for label in frame_info.value_ranges.keys() { - if label.index() == vmctx_label.index() { - append_vmctx_info( - unit, - die_id, - wasm_types.vmctx, - addr_tr, - Some(frame_info), - scope_ranges, - out_strings, - ) - .expect("append_vmctx_info success"); - } else { - let var_index = label.index(); - let (type_die_id, is_param) = - if let Some(result) = resolve_var_type(var_index, wasm_types, func_meta) { - result - } else { - // Skipping if type of local cannot be detected. - continue; - }; - - let loc_list_id = { - let endian = gimli::RunTimeEndian::Little; - - let expr = CompiledExpression::from_label(*label); - let mut locs = Vec::new(); - for (begin, length, data) in - expr.build_with_locals(scope_ranges, addr_tr, Some(frame_info), endian) - { - locs.push(write::Location::StartLength { - begin, - length, - data, - }); - } - unit.locations.add(write::LocationList(locs)) - }; - - let var_id = unit.add( - die_id, - if is_param { - gimli::DW_TAG_formal_parameter - } else { - gimli::DW_TAG_variable - }, - ); - let var = unit.get_mut(var_id); - - let name_id = match locals_names.and_then(|m| m.get(&(var_index as u32))) { - Some(n) => out_strings.add(n.to_owned()), - None => out_strings.add(format!("var{}", var_index)), - }; - - var.set(gimli::DW_AT_name, write::AttributeValue::StringRef(name_id)); - var.set( - gimli::DW_AT_type, - write::AttributeValue::ThisUnitEntryRef(type_die_id), - ); - var.set( - gimli::DW_AT_location, - write::AttributeValue::LocationListRef(loc_list_id), - ); - } - } -} - -pub fn generate_simulated_dwarf( - addr_tr: &AddressTransform, - di: &DebugInfoData, - vmctx_info: &ModuleVmctxInfo, - ranges: &ValueLabelsRanges, - translated: &HashSet, - out_encoding: gimli::Encoding, - out_units: &mut write::UnitTable, - out_strings: &mut write::StringTable, -) -> Result<(), Error> { - let path = di - .wasm_file - .path - .to_owned() - .unwrap_or_else(|| autogenerate_dwarf_wasm_path(di)); - - let (func_names, locals_names) = if let Some(ref name_section) = di.name_section { - ( - Some(&name_section.func_names), - Some(&name_section.locals_names), - ) - } else { - (None, None) - }; - - let (unit, root_id, name_id) = { - let comp_dir_id = out_strings.add(path.parent().expect("path dir").to_str().unwrap()); - let name = path.file_name().expect("path name").to_str().unwrap(); - let name_id = out_strings.add(name); - - let out_program = generate_line_info( - addr_tr, - translated, - out_encoding, - &di.wasm_file, - comp_dir_id, - name_id, - name, - )?; - - let unit_id = out_units.add(write::Unit::new(out_encoding, out_program)); - let unit = out_units.get_mut(unit_id); - - let root_id = unit.root(); - let root = unit.get_mut(root_id); - - let id = out_strings.add(PRODUCER_NAME); - root.set(gimli::DW_AT_producer, write::AttributeValue::StringRef(id)); - root.set(gimli::DW_AT_name, write::AttributeValue::StringRef(name_id)); - root.set( - gimli::DW_AT_stmt_list, - write::AttributeValue::LineProgramRef, - ); - root.set( - gimli::DW_AT_comp_dir, - write::AttributeValue::StringRef(comp_dir_id), - ); - (unit, root_id, name_id) - }; - - let wasm_types = add_wasm_types(unit, root_id, out_strings, vmctx_info); - - for (i, map) in addr_tr.map().iter() { - let index = i.index(); - if translated.contains(&(index as u32)) { - continue; - } - - let start = map.offset as u64; - let end = start + map.len as u64; - let die_id = unit.add(root_id, gimli::DW_TAG_subprogram); - let die = unit.get_mut(die_id); - die.set( - gimli::DW_AT_low_pc, - write::AttributeValue::Address(write::Address::Symbol { - symbol: index, - addend: start as i64, - }), - ); - die.set( - gimli::DW_AT_high_pc, - write::AttributeValue::Udata((end - start) as u64), - ); - - let id = match func_names.and_then(|m| m.get(&(index as u32))) { - Some(n) => out_strings.add(n.to_owned()), - None => out_strings.add(format!("wasm-function[{}]", index)), - }; - - die.set(gimli::DW_AT_name, write::AttributeValue::StringRef(id)); - - die.set( - gimli::DW_AT_decl_file, - write::AttributeValue::StringRef(name_id), - ); - - let f = addr_tr.map().get(i).unwrap(); - let f_start = f.addresses[0].wasm; - let wasm_offset = di.wasm_file.code_section_offset + f_start as u64; - die.set( - gimli::DW_AT_decl_file, - write::AttributeValue::Udata(wasm_offset), - ); - - if let Some(frame_info) = get_function_frame_info(vmctx_info, i, ranges) { - let source_range = addr_tr.func_source_range(i); - generate_vars( - unit, - die_id, - addr_tr, - &frame_info, - &[(source_range.0, source_range.1)], - &wasm_types, - &di.wasm_file.funcs[index], - locals_names.and_then(|m| m.get(&(index as u32))), - out_strings, - ); - } - } - - Ok(()) -} diff --git a/lib/debug-writer/src/transform/unit.rs b/lib/debug-writer/src/transform/unit.rs deleted file mode 100644 index 042095072fd..00000000000 --- a/lib/debug-writer/src/transform/unit.rs +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright 2019 WasmTime Project Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file is from the WasmTime project. -// It was copied at revision `907e7aac01af333a0af310ce0472abbc8a9adb6c`. -// -// Changes to this file are copyright of Wasmer inc. unless otherwise indicated -// and are licensed under the Wasmer project's license. -use super::address_transform::AddressTransform; -use super::attr::{clone_die_attributes, FileAttributeContext}; -use super::expression::compile_expression; -use super::line_program::clone_line_program; -use super::range_info_builder::RangeInfoBuilder; -use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info}; -use super::{DebugInputContext, Reader, TransformError}; -use anyhow::Error; -use cranelift_entity::EntityRef; -use gimli::write; -use gimli::{AttributeValue, DebuggingInformationEntry, Unit, UnitOffset}; -use std::collections::{HashMap, HashSet}; -use wasmtime_environ::{ModuleVmctxInfo, ValueLabelsRanges}; - -pub(crate) type PendingDieRef = (write::UnitEntryId, gimli::DwAt, UnitOffset); - -struct InheritedAttr { - stack: Vec<(usize, T)>, -} - -impl InheritedAttr { - fn new() -> Self { - InheritedAttr { stack: Vec::new() } - } - - fn update(&mut self, depth: usize) { - while !self.stack.is_empty() && self.stack.last().unwrap().0 >= depth { - self.stack.pop(); - } - } - - fn push(&mut self, depth: usize, value: T) { - self.stack.push((depth, value)); - } - - fn top(&self) -> Option<&T> { - self.stack.last().map(|entry| &entry.1) - } - - fn is_empty(&self) -> bool { - self.stack.is_empty() - } -} - -fn get_base_type_name( - type_entry: &DebuggingInformationEntry, - unit: &Unit, - context: &DebugInputContext, -) -> Result -where - R: Reader, -{ - // FIXME remove recursion. - match type_entry.attr_value(gimli::DW_AT_type)? { - Some(AttributeValue::UnitRef(ref offset)) => { - let mut entries = unit.entries_at_offset(*offset)?; - entries.next_entry()?; - if let Some(die) = entries.current() { - if let Some(AttributeValue::DebugStrRef(str_offset)) = - die.attr_value(gimli::DW_AT_name)? - { - return Ok(String::from( - context.debug_str.get_str(str_offset)?.to_string()?, - )); - } - match die.tag() { - gimli::DW_TAG_const_type => { - return Ok(format!("const {}", get_base_type_name(die, unit, context)?)); - } - gimli::DW_TAG_pointer_type => { - return Ok(format!("{}*", get_base_type_name(die, unit, context)?)); - } - gimli::DW_TAG_reference_type => { - return Ok(format!("{}&", get_base_type_name(die, unit, context)?)); - } - gimli::DW_TAG_array_type => { - return Ok(format!("{}[]", get_base_type_name(die, unit, context)?)); - } - _ => (), - } - } - } - _ => (), - }; - Ok(String::from("??")) -} - -fn replace_pointer_type( - parent_id: write::UnitEntryId, - comp_unit: &mut write::Unit, - wp_die_id: write::UnitEntryId, - entry: &DebuggingInformationEntry, - unit: &Unit, - context: &DebugInputContext, - out_strings: &mut write::StringTable, - pending_die_refs: &mut Vec<(write::UnitEntryId, gimli::DwAt, UnitOffset)>, -) -> Result -where - R: Reader, -{ - let die_id = comp_unit.add(parent_id, gimli::DW_TAG_structure_type); - let die = comp_unit.get_mut(die_id); - - let name = format!( - "WebAssemblyPtrWrapper<{}>", - get_base_type_name(entry, unit, context)? - ); - die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add(name.as_str())), - ); - die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(4)); - - let p_die_id = comp_unit.add(die_id, gimli::DW_TAG_template_type_parameter); - let p_die = comp_unit.get_mut(p_die_id); - p_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("T")), - ); - p_die.set( - gimli::DW_AT_type, - write::AttributeValue::ThisUnitEntryRef(wp_die_id), - ); - match entry.attr_value(gimli::DW_AT_type)? { - Some(AttributeValue::UnitRef(ref offset)) => { - pending_die_refs.push((p_die_id, gimli::DW_AT_type, *offset)) - } - _ => (), - } - - let m_die_id = comp_unit.add(die_id, gimli::DW_TAG_member); - let m_die = comp_unit.get_mut(m_die_id); - m_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("__ptr")), - ); - m_die.set( - gimli::DW_AT_type, - write::AttributeValue::ThisUnitEntryRef(wp_die_id), - ); - m_die.set( - gimli::DW_AT_data_member_location, - write::AttributeValue::Data1(0), - ); - Ok(die_id) -} - -pub(crate) fn clone_unit<'a, R>( - unit: Unit, - context: &DebugInputContext, - addr_tr: &'a AddressTransform, - value_ranges: &'a ValueLabelsRanges, - out_encoding: gimli::Encoding, - module_info: &ModuleVmctxInfo, - out_units: &mut write::UnitTable, - out_strings: &mut write::StringTable, - translated: &mut HashSet, -) -> Result<(), Error> -where - R: Reader, -{ - let mut die_ref_map = HashMap::new(); - let mut pending_die_refs = Vec::new(); - let mut stack = Vec::new(); - - // Iterate over all of this compilation unit's entries. - let mut entries = unit.entries(); - let (mut comp_unit, file_map, cu_low_pc, wp_die_id, vmctx_die_id) = - if let Some((depth_delta, entry)) = entries.next_dfs()? { - assert_eq!(depth_delta, 0); - let (out_line_program, debug_line_offset, file_map) = clone_line_program( - &unit, - entry, - addr_tr, - out_encoding, - context.debug_str, - context.debug_line, - out_strings, - )?; - - if entry.tag() == gimli::DW_TAG_compile_unit { - let unit_id = out_units.add(write::Unit::new(out_encoding, out_line_program)); - let comp_unit = out_units.get_mut(unit_id); - - let root_id = comp_unit.root(); - die_ref_map.insert(entry.offset(), root_id); - - let cu_low_pc = if let Some(AttributeValue::Addr(addr)) = - entry.attr_value(gimli::DW_AT_low_pc)? - { - addr - } else { - // FIXME? return Err(TransformError("No low_pc for unit header").into()); - 0 - }; - - clone_die_attributes( - entry, - context, - addr_tr, - None, - unit.encoding(), - comp_unit, - root_id, - None, - None, - cu_low_pc, - out_strings, - &die_ref_map, - &mut pending_die_refs, - FileAttributeContext::Root(Some(debug_line_offset)), - )?; - - let (wp_die_id, vmctx_die_id) = - add_internal_types(comp_unit, root_id, out_strings, module_info); - - stack.push(root_id); - (comp_unit, file_map, cu_low_pc, wp_die_id, vmctx_die_id) - } else { - return Err(TransformError("Unexpected unit header").into()); - } - } else { - return Ok(()); // empty - }; - let mut skip_at_depth = None; - let mut current_frame_base = InheritedAttr::new(); - let mut current_value_range = InheritedAttr::new(); - let mut current_scope_ranges = InheritedAttr::new(); - while let Some((depth_delta, entry)) = entries.next_dfs()? { - let depth_delta = if let Some((depth, cached)) = skip_at_depth { - let new_depth = depth + depth_delta; - if new_depth > 0 { - skip_at_depth = Some((new_depth, cached)); - continue; - } - skip_at_depth = None; - new_depth + cached - } else { - depth_delta - }; - - if !context - .reachable - .contains(&entry.offset().to_unit_section_offset(&unit)) - { - // entry is not reachable: discarding all its info. - skip_at_depth = Some((0, depth_delta)); - continue; - } - - let new_stack_len = stack.len().wrapping_add(depth_delta as usize); - current_frame_base.update(new_stack_len); - current_scope_ranges.update(new_stack_len); - current_value_range.update(new_stack_len); - let range_builder = if entry.tag() == gimli::DW_TAG_subprogram { - let range_builder = RangeInfoBuilder::from_subprogram_die( - entry, - context, - unit.encoding(), - addr_tr, - cu_low_pc, - )?; - if let RangeInfoBuilder::Function(func_index) = range_builder { - if let Some(frame_info) = - get_function_frame_info(module_info, func_index, value_ranges) - { - current_value_range.push(new_stack_len, frame_info); - } - translated.insert(func_index.index() as u32); - current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr)); - Some(range_builder) - } else { - // FIXME current_scope_ranges.push() - None - } - } else { - let high_pc = entry.attr_value(gimli::DW_AT_high_pc)?; - let ranges = entry.attr_value(gimli::DW_AT_ranges)?; - if high_pc.is_some() || ranges.is_some() { - let range_builder = - RangeInfoBuilder::from(entry, context, unit.encoding(), cu_low_pc)?; - current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr)); - Some(range_builder) - } else { - None - } - }; - - if depth_delta <= 0 { - for _ in depth_delta..1 { - stack.pop(); - } - } else { - assert_eq!(depth_delta, 1); - } - - if let Some(AttributeValue::Exprloc(expr)) = entry.attr_value(gimli::DW_AT_frame_base)? { - if let Some(expr) = compile_expression(&expr, unit.encoding(), None)? { - current_frame_base.push(new_stack_len, expr); - } - } - - let parent = stack.last().unwrap(); - - if entry.tag() == gimli::DW_TAG_pointer_type { - // Wrap pointer types. - // TODO reference types? - let die_id = replace_pointer_type( - *parent, - comp_unit, - wp_die_id, - entry, - &unit, - context, - out_strings, - &mut pending_die_refs, - )?; - stack.push(die_id); - assert_eq!(stack.len(), new_stack_len); - die_ref_map.insert(entry.offset(), die_id); - continue; - } - - let die_id = comp_unit.add(*parent, entry.tag()); - - stack.push(die_id); - assert_eq!(stack.len(), new_stack_len); - die_ref_map.insert(entry.offset(), die_id); - - clone_die_attributes( - entry, - context, - addr_tr, - current_value_range.top(), - unit.encoding(), - &mut comp_unit, - die_id, - range_builder, - current_scope_ranges.top(), - cu_low_pc, - out_strings, - &die_ref_map, - &mut pending_die_refs, - FileAttributeContext::Children(&file_map, current_frame_base.top()), - )?; - - if entry.tag() == gimli::DW_TAG_subprogram && !current_scope_ranges.is_empty() { - append_vmctx_info( - comp_unit, - die_id, - vmctx_die_id, - addr_tr, - current_value_range.top(), - current_scope_ranges.top().expect("range"), - out_strings, - )?; - } - } - for (die_id, attr_name, offset) in pending_die_refs { - let die = comp_unit.get_mut(die_id); - if let Some(unit_id) = die_ref_map.get(&offset) { - die.set(attr_name, write::AttributeValue::ThisUnitEntryRef(*unit_id)); - } else { - // TODO check why loosing DIEs - } - } - Ok(()) -} diff --git a/lib/debug-writer/src/transform/utils.rs b/lib/debug-writer/src/transform/utils.rs deleted file mode 100644 index 207ec654079..00000000000 --- a/lib/debug-writer/src/transform/utils.rs +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2019 WasmTime Project Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file is from the WasmTime project. -// It was copied at revision `cc6e8e1af25e5f9b64e183970d50f62c8338f259`. -// -// Changes to this file are copyright of Wasmer inc. unless otherwise indicated -// and are licensed under the Wasmer project's license. -use super::address_transform::AddressTransform; -use super::expression::{CompiledExpression, FunctionFrameInfo}; -use anyhow::Error; -// TODO: review -use wasmer_runtime_core::types::FuncIndex; -use gimli::write; -use wasmer_runtime_core::{module::ModuleInfo, state::CodeVersion}; -// TODO: ValueLabelsRanges - -pub(crate) fn add_internal_types( - comp_unit: &mut write::Unit, - root_id: write::UnitEntryId, - out_strings: &mut write::StringTable, - module_info: &ModuleInfo, -) -> (write::UnitEntryId, write::UnitEntryId) { - let wp_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type); - let wp_die = comp_unit.get_mut(wp_die_id); - wp_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("WebAssemblyPtr")), - ); - wp_die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(4)); - wp_die.set( - gimli::DW_AT_encoding, - write::AttributeValue::Encoding(gimli::DW_ATE_unsigned), - ); - - let memory_byte_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type); - let memory_byte_die = comp_unit.get_mut(memory_byte_die_id); - memory_byte_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("u8")), - ); - memory_byte_die.set( - gimli::DW_AT_encoding, - write::AttributeValue::Encoding(gimli::DW_ATE_unsigned), - ); - memory_byte_die.set(gimli::DW_AT_byte_size, write::AttributeValue::Data1(1)); - - let memory_bytes_die_id = comp_unit.add(root_id, gimli::DW_TAG_pointer_type); - let memory_bytes_die = comp_unit.get_mut(memory_bytes_die_id); - memory_bytes_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("u8*")), - ); - memory_bytes_die.set( - gimli::DW_AT_type, - write::AttributeValue::ThisUnitEntryRef(memory_byte_die_id), - ); - - let memory_offset = unimplemented!("TODO"); - let vmctx_die_id = comp_unit.add(root_id, gimli::DW_TAG_structure_type); - let vmctx_die = comp_unit.get_mut(vmctx_die_id); - vmctx_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("WasmerVMContext")), - ); - vmctx_die.set( - gimli::DW_AT_byte_size, - write::AttributeValue::Data4(memory_offset as u32 + 8), - ); - - let m_die_id = comp_unit.add(vmctx_die_id, gimli::DW_TAG_member); - let m_die = comp_unit.get_mut(m_die_id); - m_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("memory")), - ); - m_die.set( - gimli::DW_AT_type, - write::AttributeValue::ThisUnitEntryRef(memory_bytes_die_id), - ); - m_die.set( - gimli::DW_AT_data_member_location, - write::AttributeValue::Udata(memory_offset as u64), - ); - - let vmctx_ptr_die_id = comp_unit.add(root_id, gimli::DW_TAG_pointer_type); - let vmctx_ptr_die = comp_unit.get_mut(vmctx_ptr_die_id); - vmctx_ptr_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("WasmerVMContext*")), - ); - vmctx_ptr_die.set( - gimli::DW_AT_type, - write::AttributeValue::ThisUnitEntryRef(vmctx_die_id), - ); - - (wp_die_id, vmctx_ptr_die_id) -} - -pub(crate) fn append_vmctx_info( - comp_unit: &mut write::Unit, - parent_id: write::UnitEntryId, - vmctx_die_id: write::UnitEntryId, - addr_tr: &AddressTransform, - frame_info: Option<&FunctionFrameInfo>, - scope_ranges: &[(u64, u64)], - out_strings: &mut write::StringTable, -) -> Result<(), Error> { - let loc = { - let endian = gimli::RunTimeEndian::Little; - - let expr = CompiledExpression::vmctx(); - let mut locs = Vec::new(); - for (begin, length, data) in - expr.build_with_locals(scope_ranges, addr_tr, frame_info, endian) - { - locs.push(write::Location::StartLength { - begin, - length, - data, - }); - } - let list_id = comp_unit.locations.add(write::LocationList(locs)); - write::AttributeValue::LocationListRef(list_id) - }; - - let var_die_id = comp_unit.add(parent_id, gimli::DW_TAG_variable); - let var_die = comp_unit.get_mut(var_die_id); - var_die.set( - gimli::DW_AT_name, - write::AttributeValue::StringRef(out_strings.add("__vmctx")), - ); - var_die.set( - gimli::DW_AT_type, - write::AttributeValue::ThisUnitEntryRef(vmctx_die_id), - ); - var_die.set(gimli::DW_AT_location, loc); - - Ok(()) -} - -pub(crate) fn get_function_frame_info<'a, 'b, 'c>( - module_info: &'b ModuleInfo, - func_index: FuncIndex, - value_ranges: &'c ValueLabelsRanges, -) -> Option> -where - 'b: 'a, - 'c: 'a, -{ - if let Some(value_ranges) = value_ranges.get(func_index) { - let frame_info = FunctionFrameInfo { - value_ranges, - memory_offset: module_info.memory_offset, - stack_slots: &module_info.stack_slots[func_index], - }; - Some(frame_info) - } else { - None - } -} diff --git a/lib/debug-writer/src/write_debug_info.rs b/lib/debug-writer/src/write_debug_info.rs deleted file mode 100644 index 365643733b8..00000000000 --- a/lib/debug-writer/src/write_debug_info.rs +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2019 WasmTime Project Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// This file is from the WasmTime project. It reads DWARF info from a Wasm module. -// It was copied at revision `ea73d4fa91399349fd8d42e833363a0b1cad9f1c`. -// -// Changes to this file are copyright of Wasmer inc. unless otherwise indicated -// and are licensed under the Wasmer project's license. - -use faerie::artifact::{Decl, SectionKind}; -use faerie::*; -use gimli::write::{Address, Dwarf, EndianVec, Result, Sections, Writer}; -use gimli::{RunTimeEndian, SectionId}; - -#[derive(Clone)] -struct DebugReloc { - offset: u32, - size: u8, - name: String, - addend: i64, -} - -pub enum ResolvedSymbol { - PhysicalAddress(u64), - Reloc { name: String, addend: i64 }, -} - -pub trait SymbolResolver { - fn resolve_symbol(&self, symbol: usize, addend: i64) -> ResolvedSymbol; -} - -pub fn emit_dwarf( - artifact: &mut Artifact, - mut dwarf: Dwarf, - symbol_resolver: &dyn SymbolResolver, -) -> anyhow::Result<()> { - let endian = RunTimeEndian::Little; - - let mut sections = Sections::new(WriterRelocate::new(endian, symbol_resolver)); - dwarf.write(&mut sections)?; - sections.for_each_mut(|id, s| -> anyhow::Result<()> { - artifact.declare_with( - id.name(), - Decl::section(SectionKind::Debug), - s.writer.take(), - ) - })?; - sections.for_each_mut(|id, s| -> anyhow::Result<()> { - for reloc in &s.relocs { - artifact.link_with( - faerie::Link { - from: id.name(), - to: &reloc.name, - at: u64::from(reloc.offset), - }, - faerie::Reloc::Debug { - size: reloc.size, - addend: reloc.addend as i32, - }, - )?; - } - Ok(()) - })?; - Ok(()) -} - -#[derive(Clone)] -pub struct WriterRelocate<'a> { - relocs: Vec, - writer: EndianVec, - symbol_resolver: &'a dyn SymbolResolver, -} - -impl<'a> WriterRelocate<'a> { - pub fn new(endian: RunTimeEndian, symbol_resolver: &'a dyn SymbolResolver) -> Self { - WriterRelocate { - relocs: Vec::new(), - writer: EndianVec::new(endian), - symbol_resolver, - } - } -} - -impl<'a> Writer for WriterRelocate<'a> { - type Endian = RunTimeEndian; - - fn endian(&self) -> Self::Endian { - self.writer.endian() - } - - fn len(&self) -> usize { - self.writer.len() - } - - fn write(&mut self, bytes: &[u8]) -> Result<()> { - self.writer.write(bytes) - } - - fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> { - self.writer.write_at(offset, bytes) - } - - fn write_address(&mut self, address: Address, size: u8) -> Result<()> { - match address { - Address::Constant(val) => self.write_udata(val, size), - Address::Symbol { symbol, addend } => { - match self.symbol_resolver.resolve_symbol(symbol, addend as i64) { - ResolvedSymbol::PhysicalAddress(addr) => self.write_udata(addr, size), - ResolvedSymbol::Reloc { name, addend } => { - let offset = self.len() as u64; - self.relocs.push(DebugReloc { - offset: offset as u32, - size, - name, - addend, - }); - self.write_udata(addend as u64, size) - } - } - } - } - } - - fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> { - let offset = self.len() as u32; - let name = section.name().to_string(); - self.relocs.push(DebugReloc { - offset, - size, - name, - addend: val as i64, - }); - self.write_udata(val as u64, size) - } - - fn write_offset_at( - &mut self, - offset: usize, - val: usize, - section: SectionId, - size: u8, - ) -> Result<()> { - let name = section.name().to_string(); - self.relocs.push(DebugReloc { - offset: offset as u32, - size, - name, - addend: val as i64, - }); - self.write_udata_at(offset, val as u64, size) - } -} diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index a892349bb4a..b3c20752169 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -1000,7 +1000,7 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct Ok(()) } - fn feed_local(&mut self, ty: WpType, count: usize) -> Result<(), CodegenError> { + fn feed_local(&mut self, ty: WpType, count: usize, _loc: u32) -> Result<(), CodegenError> { let param_len = self.num_params; let wasmer_ty = wp_type_to_type(ty)?; @@ -1101,7 +1101,12 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct Ok(()) } - fn feed_event(&mut self, event: Event, module_info: &ModuleInfo) -> Result<(), CodegenError> { + fn feed_event( + &mut self, + event: Event, + module_info: &ModuleInfo, + _loc: u32, + ) -> Result<(), CodegenError> { let mut state = &mut self.state; let builder = self.builder.as_ref().unwrap(); let context = self.context.as_ref().unwrap(); @@ -8734,6 +8739,7 @@ impl<'ctx> ModuleCodeGenerator, LLVMBackend, Cod fn next_function( &mut self, _module_info: Arc>, + _loc: (u32, u32), ) -> Result<&mut LLVMFunctionCodeGenerator<'ctx>, CodegenError> { // Creates a new function and returns the function-scope code generator for it. let (context, builder, intrinsics) = match self.functions.last_mut() { @@ -8839,7 +8845,16 @@ impl<'ctx> ModuleCodeGenerator, LLVMBackend, Cod fn finalize( mut self, module_info: &ModuleInfo, - ) -> Result<(LLVMBackend, Box), CodegenError> { + ) -> Result< + ( + ( + LLVMBackend, + Option, + ), + Box, + ), + CodegenError, + > { let (context, builder, intrinsics) = match self.functions.last_mut() { Some(x) => ( x.context.take().unwrap(), @@ -8927,7 +8942,7 @@ impl<'ctx> ModuleCodeGenerator, LLVMBackend, Cod &self.target_machine, &mut self.llvm_callbacks, ); - Ok((backend, Box::new(cache_gen))) + Ok(((backend, None), Box::new(cache_gen))) } fn feed_compiler_config(&mut self, config: &CompilerConfig) -> Result<(), CodegenError> { diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index 835bc450162..79fa8c21b75 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -112,7 +112,11 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, /// Checks the precondition for a module. fn check_precondition(&mut self, module_info: &ModuleInfo) -> Result<(), E>; /// Creates a new function and returns the function-scope code generator for it. - fn next_function(&mut self, module_info: Arc>, loc: (u32, u32)) -> Result<&mut FCG, E>; + fn next_function( + &mut self, + module_info: Arc>, + loc: (u32, u32), + ) -> Result<&mut FCG, E>; /// Finalizes this module. fn finalize( self, @@ -278,13 +282,17 @@ impl< if compiler_config.generate_debug_info { let debug_metadata = debug_metadata.expect("debug metadata"); let debug_info = wasm_debug::read_debuginfo(wasm); - let extra_info = wasm_debug::types::ModuleVmctxInfo::new(14 * 8, debug_metadata.stack_slot_offsets.values()); + let extra_info = wasm_debug::types::ModuleVmctxInfo::new( + 14 * 8, + debug_metadata.stack_slot_offsets.values(), + ); // lazy type hack (TODO:) - let compiled_fn_map = wasm_debug::types::create_module_address_map(debug_metadata.func_info.values()); - let range_map = wasm_debug::types::build_values_ranges(debug_metadata.inst_info.values()); + let compiled_fn_map = + wasm_debug::types::create_module_address_map(debug_metadata.func_info.values()); + let range_map = + wasm_debug::types::build_values_ranges(debug_metadata.inst_info.values()); let raw_func_slice = debug_metadata.pointers; - dbg!("DEBUG INFO GENERATED"); let debug_image = wasm_debug::emit_debugsections_image( X86_64_OSX, std::mem::size_of::() as u8, diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index bb8f29ad039..d6eb65934e0 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -88,12 +88,12 @@ impl Instance { vmctx.as_mut_ptr().write(real_ctx); for (_, memory) in backing.vm_memories.iter_mut() { let mem: &mut vm::LocalMemory = &mut **memory; + // remaining left to do: mem.vmctx = dbg!(vmctx.as_mut_ptr()); } }; Box::leak(vmctx); - let instance = Instance { module, inner, diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index 4b04a6d6b0b..1661515368b 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -78,17 +78,9 @@ pub struct ModuleInfo { /// Custom sections. pub custom_sections: HashMap>, - /// Debug info for funcs - pub func_debug_info: Map, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -/// info about functions for debugging -pub struct FuncDebugInfo { - /// byte offset from start of code section where the function starts at - pub start: u32, - /// byte offset from start of code section where the function starts at - pub end: u32, + /// Flag controlling whether or not debug information for use in a debugger + /// will be generated + pub generate_debug_info: bool, } impl ModuleInfo { diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index b6a7ff6d560..205db736dad 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -6,8 +6,8 @@ use crate::{ backend::{CompilerConfig, RunnableModule}, error::CompileError, module::{ - DataInitializer, ExportIndex, FuncDebugInfo, ImportName, ModuleInfo, StringTable, - StringTableBuilder, TableInitializer, + DataInitializer, ExportIndex, ImportName, ModuleInfo, StringTable, StringTableBuilder, + TableInitializer, }, structures::{Map, TypedIndex}, types::{ @@ -91,7 +91,7 @@ pub fn read_module< custom_sections: HashMap::new(), - func_debug_info: Map::new(), + generate_debug_info: compiler_config.generate_debug_info, })); let mut parser = wasmparser::ValidatingParser::new( @@ -226,7 +226,6 @@ pub fn read_module< .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; } - let fcg = mcg .next_function(Arc::clone(&info), (range.start as u32, range.end as u32)) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; @@ -263,7 +262,8 @@ pub fn read_module< let local_count = operator_parser.read_local_count().unwrap(); let mut total = 0; for _ in 0..local_count { - let cur_pos = range.start as u32 + operator_parser.current_position() as u32; + let cur_pos = + range.start as u32 + operator_parser.current_position() as u32; let (count, ty) = operator_parser .read_local_decl(&mut total) .expect("Expected local"); @@ -275,7 +275,8 @@ pub fn read_module< // read instruction locations into vector for debug purposes { let info_read = info.read().unwrap(); - let mut cur_pos = range.start as u32 + operator_parser.current_position() as u32; + let mut cur_pos = + range.start as u32 + operator_parser.current_position() as u32; fcg.begin_body(&info_read) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; middlewares @@ -308,13 +309,6 @@ pub fn read_module< fcg.finalize() .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; func_count = func_count.wrapping_add(1); - - // debug info - let debug_entry = FuncDebugInfo { - start: range.start as u32, - end: range.end as u32, - }; - info.write().unwrap().func_debug_info.push(debug_entry); } ParserState::BeginActiveElementSectionEntry(table_index) => { let table_index = TableIndex::new(table_index as usize); diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index ceb7e311447..a566e7a880c 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -8,7 +8,7 @@ use crate::{ module::{ModuleInfo, ModuleInner}, sig_registry::SigRegistry, structures::TypedIndex, - types::{LocalOrImport, MemoryIndex, TableIndex, Value, LocalMemoryIndex}, + types::{LocalMemoryIndex, LocalOrImport, MemoryIndex, TableIndex, Value}, vmcalls, }; use std::{ @@ -286,7 +286,6 @@ impl Ctx { }; ((*mem).base, (*mem).bound) }; - dbg!(mem_bound); Self { internal: InternalCtx { memories: local_backing.vm_memories.as_mut_ptr(), diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index b58b84cedf5..3fc299522ac 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -678,6 +678,7 @@ impl ModuleCodeGenerator fn next_function( &mut self, _module_info: Arc>, + _loc: (u32, u32), ) -> Result<&mut X64FunctionCode, CodegenError> { let (mut assembler, mut function_labels, breakpoints, exception_table) = match self.functions.last_mut() { @@ -736,7 +737,16 @@ impl ModuleCodeGenerator fn finalize( mut self, _: &ModuleInfo, - ) -> Result<(X64ExecutionContext, Box), CodegenError> { + ) -> Result< + ( + ( + X64ExecutionContext, + Option, + ), + Box, + ), + CodegenError, + > { let (assembler, function_labels, breakpoints, exception_table) = match self.functions.last_mut() { Some(x) => ( @@ -829,16 +839,19 @@ impl ModuleCodeGenerator }; Ok(( - X64ExecutionContext { - code: output, - signatures: self.signatures.as_ref().unwrap().clone(), - breakpoints: breakpoints, - func_import_count: self.func_import_count, - function_pointers: out_labels, - function_offsets: out_offsets, - msm: msm, - exception_table, - }, + ( + X64ExecutionContext { + code: output, + signatures: self.signatures.as_ref().unwrap().clone(), + breakpoints: breakpoints, + func_import_count: self.func_import_count, + function_pointers: out_labels, + function_offsets: out_offsets, + msm: msm, + exception_table, + }, + None, + ), Box::new(cache), )) } @@ -2405,7 +2418,7 @@ impl FunctionCodeGenerator for X64FunctionCode { Ok(()) } - fn feed_local(&mut self, _ty: WpType, n: usize) -> Result<(), CodegenError> { + fn feed_local(&mut self, _ty: WpType, n: usize, _loc: u32) -> Result<(), CodegenError> { self.num_locals += n; Ok(()) } @@ -2515,7 +2528,12 @@ impl FunctionCodeGenerator for X64FunctionCode { Ok(()) } - fn feed_event(&mut self, ev: Event, module_info: &ModuleInfo) -> Result<(), CodegenError> { + fn feed_event( + &mut self, + ev: Event, + module_info: &ModuleInfo, + _loc: u32, + ) -> Result<(), CodegenError> { let a = self.assembler.as_mut().unwrap(); match ev { diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 761daec25a5..a46541166d6 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -258,6 +258,10 @@ struct Run { #[structopt(long = "debug", short = "d")] debug: bool, + /// Generate debug information for use in a debugger + #[structopt(long = "generate-debug-info", short = "g")] + generate_debug_info: bool, + /// Application arguments #[structopt(name = "--", multiple = true)] args: Vec, @@ -719,7 +723,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { track_state, features: options.features.into_backend_features(), backend_specific_config, - generate_debug_info: true, + generate_debug_info: options.generate_debug_info, ..Default::default() }, &*compiler, From 923e4ac83a2befe4ba91198d355fe818c5fc57f9 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 14 Feb 2020 12:34:25 -0800 Subject: [PATCH 06/26] Add command line flag, make debug crate optional, clean up API --- Cargo.lock | 1 - Cargo.toml | 2 +- lib/clif-backend/src/resolver.rs | 7 ++- lib/runtime-core/Cargo.toml | 7 ++- lib/runtime-core/src/codegen.rs | 72 +++++++++++++------------- lib/runtime-core/src/structures/map.rs | 5 ++ lib/runtime-core/src/types.rs | 11 ---- src/bin/wasmer.rs | 5 ++ 8 files changed, 54 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 514b12118d6..e93639fa956 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1949,7 +1949,6 @@ dependencies = [ "bincode", "blake3", "cc", - "cranelift-entity", "digest", "errno", "hex", diff --git a/Cargo.toml b/Cargo.toml index 8b74cd54c35..8d0f54496e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,7 @@ serde = { version = "1", features = ["derive"] } # used by the plugin example typetag = "0.1" # used by the plugin example [features] -default = ["fast-tests", "wasi", "backend-cranelift", "wabt"] +default = ["fast-tests", "wasi", "backend-cranelift", "wabt", "wasmer-runtime-core/generate-debug-information"] "loader-kernel" = ["wasmer-kernel-loader"] debug = ["fern", "log/max_level_debug", "log/release_max_level_debug"] trace = ["fern", "log/max_level_trace", "log/release_max_level_trace"] diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index a608ad50464..f46bed62976 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -11,7 +11,6 @@ use crate::{ use byteorder::{ByteOrder, LittleEndian}; use cranelift_codegen::{ binemit::{Stackmap, StackmapSink}, - entity::PrimaryMap, ir, isa, Context, ValueLabelsRanges, }; use rayon::prelude::*; @@ -199,10 +198,10 @@ impl FuncResolverBuilder { use wasm_debug::types::CompiledFunctionData; let mut debug_metadata = if generate_debug_info { Some(wasmer_runtime_core::codegen::DebugMetadata { - func_info: PrimaryMap::new(), - inst_info: PrimaryMap::new(), + func_info: Map::new(), + inst_info: Map::new(), pointers: vec![], - stack_slot_offsets: PrimaryMap::new(), + stack_slot_offsets: Map::new(), }) } else { None diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index da0881baee8..8665eab183b 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -20,10 +20,7 @@ libc = "0.2.60" hex = "0.4" smallvec = "0.6" bincode = "1.1" -# todo: review -cranelift-entity = "0.52" -# todo, make optional, etc -wasm-debug = { version = "0.1.0", path = "../../../Dev/wasm-debug" } +wasm-debug = { optional = true, version = "0.1.0", path = "../../../Dev/wasm-debug" } target-lexicon = "0.9" [dependencies.indexmap] @@ -57,3 +54,5 @@ cc = "1.0" [features] managed = [] deterministic-execution = ["wasmparser/deterministic"] +# generate debug information from Wasm DWARF for use with the GDB JIT interface +generate-debug-information = ["wasm-debug"] diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index 79fa8c21b75..a27e0d323f0 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -18,6 +18,7 @@ use std::fmt; use std::fmt::Debug; use std::marker::PhantomData; use std::sync::{Arc, RwLock}; +use wasm_debug::types::{CompiledFunctionData, ValueLabelsRangesInner}; use wasmparser::{self, WasmDecoder}; use wasmparser::{Operator, Type as WpType}; @@ -127,15 +128,14 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, unsafe fn from_cache(cache: Artifact, _: Token) -> Result; } -use cranelift_entity::PrimaryMap; /// missing documentation! pub struct DebugMetadata { - ///f unc info - pub func_info: PrimaryMap, - /// inst_info - pub inst_info: PrimaryMap, - /// stack slot offsets! - pub stack_slot_offsets: PrimaryMap>>, + /// [`CompiledFunctionData`] in [`FuncIndex`] order + pub func_info: Map, + /// [`ValueLabelsRangesInner`] in [`FuncIndex`] order + pub inst_info: Map, + /// Stack slot offsets in [`FuncIndex`] order + pub stack_slot_offsets: Map>>, /// function pointers and their lengths pub pointers: Vec<(*const u8, usize)>, } @@ -280,34 +280,36 @@ impl< }; if compiler_config.generate_debug_info { - let debug_metadata = debug_metadata.expect("debug metadata"); - let debug_info = wasm_debug::read_debuginfo(wasm); - let extra_info = wasm_debug::types::ModuleVmctxInfo::new( - 14 * 8, - debug_metadata.stack_slot_offsets.values(), - ); - // lazy type hack (TODO:) - let compiled_fn_map = - wasm_debug::types::create_module_address_map(debug_metadata.func_info.values()); - let range_map = - wasm_debug::types::build_values_ranges(debug_metadata.inst_info.values()); - let raw_func_slice = debug_metadata.pointers; - - let debug_image = wasm_debug::emit_debugsections_image( - X86_64_OSX, - std::mem::size_of::() as u8, - &debug_info, - &extra_info, - &compiled_fn_map, - &range_map, - &raw_func_slice, - ) - .expect("make debug image"); - - crate::jit_debug::register_new_jit_code_entry( - &debug_image, - crate::jit_debug::JITAction::JIT_REGISTER_FN, - ); + if let Some(debug_metadata) = debug_metadata { + let debug_info = wasm_debug::read_debuginfo(wasm); + let extra_info = wasm_debug::types::ModuleVmctxInfo::new( + 14 * 8, + debug_metadata.stack_slot_offsets.values(), + ); + let compiled_fn_map = + wasm_debug::types::create_module_address_map(debug_metadata.func_info.values()); + let range_map = + wasm_debug::types::build_values_ranges(debug_metadata.inst_info.values()); + let raw_func_slice = debug_metadata.pointers; + + let debug_image = wasm_debug::emit_debugsections_image( + X86_64_OSX, + std::mem::size_of::() as u8, + &debug_info, + &extra_info, + &compiled_fn_map, + &range_map, + &raw_func_slice, + ) + .expect("make debug image"); + + crate::jit_debug::register_new_jit_code_entry( + &debug_image, + crate::jit_debug::JITAction::JIT_REGISTER_FN, + ); + } else { + eprintln!("Failed to generate debug information!"); + } } Ok(ModuleInner { cache_gen, diff --git a/lib/runtime-core/src/structures/map.rs b/lib/runtime-core/src/structures/map.rs index 08773b50a4b..0f8eac6f6ba 100644 --- a/lib/runtime-core/src/structures/map.rs +++ b/lib/runtime-core/src/structures/map.rs @@ -78,6 +78,11 @@ where pub fn into_vec(self) -> Vec { self.elems } + + /// Iterate over the values of the map in order + pub fn values(&self) -> impl Iterator { + self.elems.iter() + } } impl Map diff --git a/lib/runtime-core/src/types.rs b/lib/runtime-core/src/types.rs index 3878e7eac33..1b19bbc3d91 100644 --- a/lib/runtime-core/src/types.rs +++ b/lib/runtime-core/src/types.rs @@ -502,17 +502,6 @@ define_map_index![ | imported: ImportedFuncIndex, ImportedMemoryIndex, ImportedTableIndex, ImportedGlobalIndex, ]; -// lol -impl cranelift_entity::EntityRef for FuncIndex { - fn index(self) -> usize { - self.0 as usize - } - - fn new(x: usize) -> Self { - Self(x as u32) - } -} - #[rustfmt::skip] macro_rules! define_local_or_import { ($ty:ident, $local_ty:ident, $imported_ty:ident, $imports:ident) => { diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index a46541166d6..c32766bf4cc 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -567,6 +567,10 @@ impl LLVMCallbacks for LLVMCLIOptions { /// Execute a wasm/wat file fn execute_wasm(options: &Run) -> Result<(), String> { + if options.generate_debug_info && options.backend != Backend::Cranelift { + return Err("Generating debug information is currently only available with the `cranelift` backend.".to_owned()); + } + let disable_cache = options.disable_cache; let mapped_dirs = get_mapped_dirs(&options.mapped_dirs[..])?; @@ -1021,6 +1025,7 @@ fn get_backend(backend: Backend, path: &PathBuf) -> Backend { fn run(options: &mut Run) { options.backend = get_backend(options.backend, &options.path); + #[cfg(any(feature = "debug", feature = "trace"))] { if options.debug { From 3653a448f5a4fab590957369527fbe9f16c9b412 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 18 Feb 2020 16:51:02 -0800 Subject: [PATCH 07/26] Clean up code, add documentation, implement pieces properly --- Makefile | 1 + lib/runtime-core/src/backend.rs | 14 ++-- lib/runtime-core/src/codegen.rs | 101 ++++++++++++------------ lib/runtime-core/src/instance.rs | 8 +- lib/runtime-core/src/jit_debug.rs | 118 ++++++++++++++++++++++------- lib/runtime-core/src/memory/mod.rs | 2 - lib/runtime-core/src/module.rs | 7 +- lib/runtime-core/src/parse.rs | 3 +- lib/runtime-core/src/vm.rs | 12 ++- 9 files changed, 167 insertions(+), 99 deletions(-) diff --git a/Makefile b/Makefile index 96bbf55d185..4ee38e30c75 100644 --- a/Makefile +++ b/Makefile @@ -219,6 +219,7 @@ check: check-bench # builds, test as many combined features as possible with each backend # as default, and test a minimal set of features with only one backend # at a time. + cargo check --manifest-path lib/runtime-core/Cargo.toml cargo check --manifest-path lib/runtime/Cargo.toml # Check some of the cases where deterministic execution could matter cargo check --manifest-path lib/runtime/Cargo.toml --features "deterministic-execution" diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index 2e010a2ea5c..2a3a0e3e93c 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -124,6 +124,15 @@ pub struct CompilerConfig { pub generate_debug_info: bool, } +impl CompilerConfig { + /// Use this to check if we should be generating debug information. + /// This function takes into account the features that runtime-core was + /// compiled with in addition to the value of the `generate_debug_info` field. + pub(crate) fn should_generate_debug_info(&self) -> bool { + cfg!(feature = "generate-debug-information") && self.generate_debug_info + } +} + /// An exception table for a `RunnableModule`. #[derive(Clone, Debug, Default, Serialize, Deserialize)] pub struct ExceptionTable { @@ -212,11 +221,6 @@ pub trait RunnableModule: Send + Sync { None } - /// TODO: document before shipppping - fn get_local_function_pointers_and_lengths(&self) -> Option> { - None - } - /// Returns the inline breakpoint size corresponding to an Architecture (None in case is not implemented) fn get_inline_breakpoint_size(&self, _arch: Architecture) -> Option { None diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index a27e0d323f0..3d697e33f09 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -18,7 +18,6 @@ use std::fmt; use std::fmt::Debug; use std::marker::PhantomData; use std::sync::{Arc, RwLock}; -use wasm_debug::types::{CompiledFunctionData, ValueLabelsRangesInner}; use wasmparser::{self, WasmDecoder}; use wasmparser::{Operator, Type as WpType}; @@ -128,7 +127,19 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, unsafe fn from_cache(cache: Artifact, _: Token) -> Result; } -/// missing documentation! +/// Mock item when compiling without debug info generation. +#[cfg(not(feature = "generate-debug-information"))] +type CompiledFunctionData = (); + +/// Mock item when compiling without debug info generation. +#[cfg(not(feature = "generate-debug-information"))] +type ValueLabelsRangesInner = (); + +#[cfg(feature = "generate-debug-information")] +use wasm_debug::types::{CompiledFunctionData, ValueLabelsRangesInner}; + +#[derive(Clone, Debug)] +/// Useful information for debugging gathered by compiling a Wasm module. pub struct DebugMetadata { /// [`CompiledFunctionData`] in [`FuncIndex`] order pub func_info: Map, @@ -262,55 +273,47 @@ impl< }; let mut chain = (self.middleware_chain_generator)(); let info = crate::parse::read_module(wasm, &mut mcg, &mut chain, &compiler_config)?; - let ((exec_context, debug_metadata), cache_gen) = mcg - .finalize(&info.read().unwrap()) - .map_err(|x| CompileError::InternalError { - msg: format!("{:?}", x), - })?; - - use target_lexicon::{ - Architecture, BinaryFormat, Environment, OperatingSystem, Triple, Vendor, - }; - const X86_64_OSX: Triple = Triple { - architecture: Architecture::X86_64, - vendor: Vendor::Apple, - operating_system: OperatingSystem::Darwin, - environment: Environment::Unknown, - binary_format: BinaryFormat::Macho, - }; - - if compiler_config.generate_debug_info { - if let Some(debug_metadata) = debug_metadata { - let debug_info = wasm_debug::read_debuginfo(wasm); - let extra_info = wasm_debug::types::ModuleVmctxInfo::new( - 14 * 8, - debug_metadata.stack_slot_offsets.values(), - ); - let compiled_fn_map = - wasm_debug::types::create_module_address_map(debug_metadata.func_info.values()); - let range_map = - wasm_debug::types::build_values_ranges(debug_metadata.inst_info.values()); - let raw_func_slice = debug_metadata.pointers; - - let debug_image = wasm_debug::emit_debugsections_image( - X86_64_OSX, - std::mem::size_of::() as u8, - &debug_info, - &extra_info, - &compiled_fn_map, - &range_map, - &raw_func_slice, - ) - .expect("make debug image"); - - crate::jit_debug::register_new_jit_code_entry( - &debug_image, - crate::jit_debug::JITAction::JIT_REGISTER_FN, - ); - } else { - eprintln!("Failed to generate debug information!"); + let ((exec_context, compile_debug_info), cache_gen) = + mcg.finalize(&info.read().unwrap()) + .map_err(|x| CompileError::InternalError { + msg: format!("{:?}", x), + })?; + + #[cfg(feature = "generate-debug-information")] + { + if compiler_config.should_generate_debug_info() { + if let Some(dbg_info) = compile_debug_info { + let debug_info = wasm_debug::read_debuginfo(wasm); + let extra_info = wasm_debug::types::ModuleVmctxInfo::new( + crate::vm::Ctx::offset_memory_base() as _, + std::mem::size_of::() as _, + dbg_info.stack_slot_offsets.values(), + ); + let compiled_fn_map = + wasm_debug::types::create_module_address_map(dbg_info.func_info.values()); + let range_map = + wasm_debug::types::build_values_ranges(dbg_info.inst_info.values()); + let raw_func_slice = &dbg_info.pointers; + + let debug_image = wasm_debug::emit_debugsections_image( + target_lexicon::HOST, + std::mem::size_of::() as u8, + &debug_info, + &extra_info, + &compiled_fn_map, + &range_map, + raw_func_slice, + ) + .expect("make debug image"); + + let mut writer = info.write().unwrap(); + writer + .debug_info_manager + .register_new_jit_code_entry(&debug_image); + } } } + Ok(ModuleInner { cache_gen, runnable_module: Arc::new(Box::new(exec_context)), diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index d6eb65934e0..6b31a08c66e 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -79,18 +79,14 @@ impl Instance { unsafe { let backing = &mut *(&mut inner.backing as *mut _); let import_backing = &mut *(&mut inner.import_backing as *mut _); - let real_ctx = match imports.call_state_creator() { + let mut real_ctx = match imports.call_state_creator() { Some((data, dtor)) => { vm::Ctx::new_with_data(backing, import_backing, &module, data, dtor) } None => vm::Ctx::new(backing, import_backing, &module), }; + real_ctx.internal.ctx = vmctx.as_mut_ptr(); vmctx.as_mut_ptr().write(real_ctx); - for (_, memory) in backing.vm_memories.iter_mut() { - let mem: &mut vm::LocalMemory = &mut **memory; - // remaining left to do: - mem.vmctx = dbg!(vmctx.as_mut_ptr()); - } }; Box::leak(vmctx); diff --git a/lib/runtime-core/src/jit_debug.rs b/lib/runtime-core/src/jit_debug.rs index f00d07bf615..aff81f81b06 100644 --- a/lib/runtime-core/src/jit_debug.rs +++ b/lib/runtime-core/src/jit_debug.rs @@ -1,5 +1,6 @@ #![allow(missing_docs)] use std::ptr; +use std::sync::Arc; // ============================================================================= // LLDB hook magic: @@ -22,7 +23,7 @@ extern "C" fn __jit_debug_register_code() { #[allow(non_camel_case_types)] #[derive(Debug)] #[repr(u32)] -pub enum JITAction { +pub(crate) enum JITAction { JIT_NOACTION = 0, JIT_REGISTER_FN = 1, JIT_UNREGISTER_FN = 2, @@ -30,11 +31,10 @@ pub enum JITAction { #[no_mangle] #[repr(C)] -pub struct JITCodeEntry { +pub(crate) struct JITCodeEntry { next: *mut JITCodeEntry, prev: *mut JITCodeEntry, - // TODO: use CStr here? - symfile_addr: *const u8, + symfile_addr: *mut u8, symfile_size: u64, } @@ -43,7 +43,7 @@ impl Default for JITCodeEntry { Self { next: ptr::null_mut(), prev: ptr::null_mut(), - symfile_addr: ptr::null(), + symfile_addr: ptr::null_mut(), symfile_size: 0, } } @@ -51,7 +51,7 @@ impl Default for JITCodeEntry { #[no_mangle] #[repr(C)] -pub struct JitDebugDescriptor { +pub(crate) struct JitDebugDescriptor { version: u32, action_flag: u32, relevant_entry: *mut JITCodeEntry, @@ -60,7 +60,7 @@ pub struct JitDebugDescriptor { #[no_mangle] #[allow(non_upper_case_globals)] -pub static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { +pub(crate) static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { version: 1, action_flag: JITAction::JIT_NOACTION as _, relevant_entry: ptr::null_mut(), @@ -70,8 +70,8 @@ pub static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { /// Prepend an item to the front of the `__jit_debug_descriptor` entry list /// /// # Safety -/// - Pointer to [`JITCodeEntry`] should point to a valid entry and stay alive -/// for the 'static lifetime +/// - Access to underlying global variable is unsynchronized. +/// - Pointer to [`JITCodeEntry`] should point to a valid entry. unsafe fn push_front(jce: *mut JITCodeEntry) { if __jit_debug_descriptor.first_entry.is_null() { __jit_debug_descriptor.first_entry = jce; @@ -84,28 +84,90 @@ unsafe fn push_front(jce: *mut JITCodeEntry) { } } -// deleted static (added and deleted by Mark): TODO: -pub fn register_new_jit_code_entry(bytes: &[u8], action: JITAction) -> *mut JITCodeEntry { - let owned_bytes = bytes.iter().cloned().collect::>(); - let ptr = owned_bytes.as_ptr(); - let len = owned_bytes.len(); +/// Removes an entry from the doubly linked list, updating both nodes that it's +/// connected to. +/// +/// # Safety +/// - Access to underlying global variable is unsynchronized. +/// - Pointer must point to a valid `JitCodeEntry`. +unsafe fn remove_node(jce: *mut JITCodeEntry) { + if __jit_debug_descriptor.first_entry == jce { + debug_assert!((*jce).prev.is_null()); + __jit_debug_descriptor.first_entry = (*jce).next; + } + if !(*jce).prev.is_null() { + (*(*jce).prev).next = (*jce).next; + } + if !(*jce).next.is_null() { + (*(*jce).next).prev = (*jce).prev; + } +} - std::mem::forget(bytes); +/// Type for implementing Drop on the memory shared with the debugger. +#[derive(Debug)] +struct JITCodeDebugInfoEntryHandleInner(*mut JITCodeEntry); - let entry: *mut JITCodeEntry = Box::into_raw(Box::new(JITCodeEntry { - symfile_addr: ptr, - symfile_size: len as _, - ..JITCodeEntry::default() - })); +/// Handle to debug info about JIT code registered with a debugger +#[derive(Debug, Clone)] +pub(crate) struct JITCodeDebugInfoEntryHandle(Arc); - unsafe { - push_front(entry); - __jit_debug_descriptor.relevant_entry = entry; - __jit_debug_descriptor.action_flag = action as u32; - __jit_debug_register_code(); - __jit_debug_descriptor.relevant_entry = ptr::null_mut(); - __jit_debug_descriptor.action_flag = JITAction::JIT_NOACTION as _; +impl Drop for JITCodeDebugInfoEntryHandleInner { + fn drop(&mut self) { + unsafe { + // unregister the function when dropping the JIT code entry + __jit_debug_descriptor.relevant_entry = self.0; + __jit_debug_descriptor.action_flag = JITAction::JIT_UNREGISTER_FN as u32; + __jit_debug_register_code(); + __jit_debug_descriptor.relevant_entry = ptr::null_mut(); + __jit_debug_descriptor.action_flag = JITAction::JIT_NOACTION as u32; + remove_node(self.0); + let entry: Box = Box::from_raw(self.0); + Vec::from_raw_parts( + entry.symfile_addr, + entry.symfile_size as _, + entry.symfile_size as _, + ); + } } +} - entry +/// Manager of debug info registered with the debugger. +#[derive(Debug, Clone, Default)] +pub struct JITCodeDebugInfoManager { + inner: Vec, +} + +impl JITCodeDebugInfoManager { + pub(crate) fn register_new_jit_code_entry( + &mut self, + bytes: &[u8], + ) -> JITCodeDebugInfoEntryHandle { + let mut owned_bytes = bytes.iter().cloned().collect::>(); + // ensure length == capacity to simplify memory freeing code + owned_bytes.shrink_to_fit(); + let ptr = owned_bytes.as_mut_ptr(); + let len = owned_bytes.len(); + + std::mem::forget(owned_bytes); + + let entry: *mut JITCodeEntry = Box::into_raw(Box::new(JITCodeEntry { + symfile_addr: ptr, + symfile_size: len as _, + ..JITCodeEntry::default() + })); + + unsafe { + push_front(entry); + __jit_debug_descriptor.relevant_entry = entry; + __jit_debug_descriptor.action_flag = JITAction::JIT_REGISTER_FN as u32; + __jit_debug_register_code(); + __jit_debug_descriptor.relevant_entry = ptr::null_mut(); + __jit_debug_descriptor.action_flag = JITAction::JIT_NOACTION as u32; + } + + let handle = JITCodeDebugInfoEntryHandle(Arc::new(JITCodeDebugInfoEntryHandleInner(entry))); + self.inner.push(handle.clone()); + + handle + } } diff --git a/lib/runtime-core/src/memory/mod.rs b/lib/runtime-core/src/memory/mod.rs index 95458eda407..75a02e2276a 100644 --- a/lib/runtime-core/src/memory/mod.rs +++ b/lib/runtime-core/src/memory/mod.rs @@ -227,7 +227,6 @@ impl UnsharedMemory { base: std::ptr::null_mut(), bound: 0, memory: std::ptr::null_mut(), - vmctx: std::ptr::null_mut(), }; let storage = match desc.memory_type() { @@ -315,7 +314,6 @@ impl SharedMemory { base: std::ptr::null_mut(), bound: 0, memory: std::ptr::null_mut(), - vmctx: std::ptr::null_mut(), }; let memory = StaticMemory::new(desc, &mut local)?; diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index 1661515368b..f2fd1e94cb7 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -16,6 +16,7 @@ use crate::{ }; use crate::backend::CacheGen; +use crate::jit_debug; use indexmap::IndexMap; use std::collections::HashMap; use std::sync::Arc; @@ -79,8 +80,12 @@ pub struct ModuleInfo { pub custom_sections: HashMap>, /// Flag controlling whether or not debug information for use in a debugger - /// will be generated + /// will be generated. pub generate_debug_info: bool, + + #[serde(skip)] + /// Resource manager of debug information being used by a debugger. + pub debug_info_manager: jit_debug::JITCodeDebugInfoManager, } impl ModuleInfo { diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index 205db736dad..8dd3e59056f 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -91,7 +91,8 @@ pub fn read_module< custom_sections: HashMap::new(), - generate_debug_info: compiler_config.generate_debug_info, + generate_debug_info: compiler_config.should_generate_debug_info(), + debug_info_manager: Default::default(), })); let mut parser = wasmparser::ValidatingParser::new( diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index a566e7a880c..42cc29efa40 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -8,7 +8,7 @@ use crate::{ module::{ModuleInfo, ModuleInner}, sig_registry::SigRegistry, structures::TypedIndex, - types::{LocalMemoryIndex, LocalOrImport, MemoryIndex, TableIndex, Value}, + types::{LocalOrImport, MemoryIndex, TableIndex, Value}, vmcalls, }; use std::{ @@ -136,7 +136,7 @@ pub struct InternalCtx { pub interrupt_signal_mem: *mut u8, /// hmm - pub first_mem: *mut LocalMemory, + pub ctx: *mut Ctx, } static INTERNAL_FIELDS: AtomicUsize = AtomicUsize::new(0); @@ -309,7 +309,7 @@ impl Ctx { internals: &mut local_backing.internals.0, interrupt_signal_mem: get_interrupt_signal_mem(), - first_mem: local_backing.vm_memories[LocalMemoryIndex::new(0)], + ctx: std::ptr::null_mut(), //local_backing.vm_memories[LocalMemoryIndex::new(0)], }, local_functions: local_backing.local_functions.as_ptr(), @@ -366,7 +366,8 @@ impl Ctx { interrupt_signal_mem: get_interrupt_signal_mem(), - first_mem: local_backing.vm_memories[LocalMemoryIndex::new(0)], + ctx: std::ptr::null_mut(), + //first_mem: local_backing.vm_memories[LocalMemoryIndex::new(0)], }, local_functions: local_backing.local_functions.as_ptr(), @@ -674,9 +675,6 @@ pub struct LocalMemory { /// This is either `*mut DynamicMemory`, `*mut StaticMemory`, /// or `*mut SharedStaticMemory`. pub memory: *mut (), - - /// wat - pub vmctx: *mut Ctx, } // manually implemented because LocalMemory contains raw pointers From 1ac59a31f6d996addcdff6a4dc604b433072801a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 18 Feb 2020 17:31:12 -0800 Subject: [PATCH 08/26] Add various small improvements, update attributions file --- ATTRIBUTIONS.md | 25 ++++++++++++------ lib/clif-backend/src/resolver.rs | 35 ++++++++++++++++++++------ lib/runtime-core/src/jit_debug.rs | 42 +++++++++++++++++++++++-------- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md index 54e0eed0234..83aae0689af 100644 --- a/ATTRIBUTIONS.md +++ b/ATTRIBUTIONS.md @@ -1,21 +1,30 @@ # Wasmer Attributions -Wasmer is a community effort. -In order to build the best WebAssembly runtime it's our duty to see how other runtimes are approaching the same space -and get inspired from them on the things that they got right, so Wasmer and its community can benefit from a solid -foundation. +Wasmer is a community effort and makes use of code from various other +projects. Listed below are notable sections of code that are licensed +from other projects and the relevant license of those projects. -These are the different project that we used as inspiration: +These are the projects that were used as inspiration and/or that we are using code from: - [Nebulet](https://github.com/nebulet/nebulet): as the base for creating a great Rust WebAssembly runtime - [WAVM](https://github.com/wavm/wavm): for their great integration and testing framework - [greenwasm](https://github.com/Kimundi/greenwasm): for their [spectests framework](https://github.com/Kimundi/greenwasm/tree/master/greenwasm-spectest) -- [wasmtime](https://github.com/CraneStation/wasmtime): for their [mmap implementation](https://github.com/CraneStation/wasmtime/blob/3f24098edc81cd9bf0f877fb7fba018cad0f039e/lib/runtime/src/mmap.rs) +- [wasmtime](https://github.com/CraneStation/wasmtime): + - For their [mmap implementation](https://github.com/CraneStation/wasmtime/blob/3f24098edc81cd9bf0f877fb7fba018cad0f039e/lib/runtime/src/mmap.rs) + - For the implementation of the `__jit_debug_register_code` function + in Rust, the structure of using Cranelift with the GDB JIT + interface including implementation details regarding the structure + of generating debug information for each function with Cranelift + (for example, the sorting of the extended basic blocks before + processing the instructions), and the API for transforming DWARF + see wasm-debug's attribution file for more information (TODO: link + and update here). - [stackoverflow](https://stackoverflow.com/a/45795699/1072990): to create an efficient HashMap with pair keys - [Emscripten](https://github.com/kripken/emscripten): for emtests test sources to ensure compatibility +- [The WebAssembly spec](https://github.com/WebAssembly/spec/tree/master/test): for implementation details of WebAssembly and spectests -We would love to hear from you if you think we can take inspiration from other projects that we haven't covered here. -😊 +Please let us know if you believe there is an error or omission in +this list and we will do our best to correct it. ## Licenses diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index f46bed62976..99030bffb54 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -164,10 +164,6 @@ impl FuncResolverBuilder { }) .collect::>(); - /*let mut unwind = vec![]; - ctx.emit_unwind_info(isa, &mut unwind); - dbg!(unwind.len());*/ - let stack_slots = ctx .func .stack_slots @@ -228,9 +224,34 @@ impl FuncResolverBuilder { if let Some(ref mut dbg_metadata) = debug_metadata { let (entry, vlr, stackslots) = debug_info.unwrap(); dbg_metadata.func_info.push(entry); - dbg_metadata - .inst_info - .push(unsafe { std::mem::transmute(vlr) }); + let new_vlr = vlr + .into_iter() + .map(|(k, v)| { + ( + wasm_debug::types::ValueLabel::from_u32(k.as_u32()), + v.into_iter() + .map(|item| wasm_debug::types::ValueLocRange { + start: item.start, + end: item.end, + loc: match item.loc { + cranelift_codegen::ir::ValueLoc::Unassigned => { + wasm_debug::types::ValueLoc::Unassigned + } + cranelift_codegen::ir::ValueLoc::Reg(ru) => { + wasm_debug::types::ValueLoc::Reg(ru) + } + cranelift_codegen::ir::ValueLoc::Stack(st) => { + wasm_debug::types::ValueLoc::Stack( + wasm_debug::types::StackSlot::from_u32(st.as_u32()), + ) + } + }, + }) + .collect::>(), + ) + }) + .collect::(); + dbg_metadata.inst_info.push(new_vlr); dbg_metadata.stack_slot_offsets.push(stackslots); } diff --git a/lib/runtime-core/src/jit_debug.rs b/lib/runtime-core/src/jit_debug.rs index aff81f81b06..14a2668cb3b 100644 --- a/lib/runtime-core/src/jit_debug.rs +++ b/lib/runtime-core/src/jit_debug.rs @@ -1,40 +1,52 @@ -#![allow(missing_docs)] +//! Code for interacting with the +//! [GDB JIT inteface](https://sourceware.org/gdb/current/onlinedocs/gdb.html#JIT-Interface). + use std::ptr; use std::sync::Arc; -// ============================================================================= -// LLDB hook magic: -// see lldb/packages/Python/lldbsuite/test/functionalities/jitloader_gdb in -// llvm repo for example -// -// see also https://sourceware.org/gdb/current/onlinedocs/gdb.html#JIT-Interface - +/// Entrypoint that the debugger will use to trigger a read from the +/// [`__jit_debug_descriptor`] global variable. +/// +/// The debugger will wait for this function to be called and then take +/// control to read the data we prepared. +// implementation of this function is from wasmtime and is licensed under +// the Apache 2.0 license. See ATTRIBUTIONS.md for full license and more +// information. #[no_mangle] #[inline(never)] extern "C" fn __jit_debug_register_code() { - // implementation of this function copied from wasmtime (TODO: link and attribution) - // prevent optimization of this function + // This code exists to prevent optimization of this function so that the + // GDB JIT interface behaves as expected let x = 3; unsafe { std::ptr::read_volatile(&x); } } +/// The operation that the debugger should perform with the entry that we gave it. #[allow(non_camel_case_types)] #[derive(Debug)] #[repr(u32)] pub(crate) enum JITAction { + /// Do nothing. JIT_NOACTION = 0, + /// Register the given code. JIT_REGISTER_FN = 1, + /// Unregister the given code. JIT_UNREGISTER_FN = 2, } +/// Node of the doubly linked list that the GDB JIT interface reads from. #[no_mangle] #[repr(C)] pub(crate) struct JITCodeEntry { + /// Next entry in the linked list. next: *mut JITCodeEntry, + /// Previous entry in the linked list. prev: *mut JITCodeEntry, + /// Pointer to the data we want the debugger to read. symfile_addr: *mut u8, + /// The amount of data at the `symfile_addr` pointer. symfile_size: u64, } @@ -49,15 +61,24 @@ impl Default for JITCodeEntry { } } +/// Head node of the doubly linked list that the GDB JIT interface expects. #[no_mangle] #[repr(C)] pub(crate) struct JitDebugDescriptor { + /// The version of the JIT interface to use (presumably, TODO: double check this) version: u32, + /// Which action to perform, see [`JITAction`]. action_flag: u32, + /// The entry in the list that the `action_flag` applies to. relevant_entry: *mut JITCodeEntry, + /// The first entry in the doubly linked list. first_entry: *mut JITCodeEntry, } +/// Global variable that the GDB JIT interface will read the data from. +/// The data is in the form of a doubly linked list. This global variable acts +/// as a head node with extra information about the operation that we want the +/// debugger to perform. #[no_mangle] #[allow(non_upper_case_globals)] pub(crate) static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { @@ -138,6 +159,7 @@ pub struct JITCodeDebugInfoManager { } impl JITCodeDebugInfoManager { + /// Register debug info relating to JIT code with the debugger. pub(crate) fn register_new_jit_code_entry( &mut self, bytes: &[u8], From 68af917d4caf0a5565f92669714cb50bba9ad1c8 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 19 Feb 2020 15:46:43 -0800 Subject: [PATCH 09/26] Update field names in wasm-debug types --- lib/clif-backend/src/resolver.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index 99030bffb54..3dbd81b50cc 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -156,9 +156,9 @@ impl FuncResolverBuilder { let srcloc = func.srclocs[inst]; let val = srcloc.bits(); wasm_debug::types::CompiledInstructionData { - loc: wasm_debug::types::SourceLoc::new(val), - offset: offset as usize, - length: length as usize, + srcloc: wasm_debug::types::SourceLoc::new(val), + code_offset: offset as usize, + code_len: length as usize, } }) }) @@ -174,11 +174,11 @@ impl FuncResolverBuilder { let entry = CompiledFunctionData { instructions, - start: wasm_debug::types::SourceLoc::new(*start), - end: wasm_debug::types::SourceLoc::new(*end), + start_srcloc: wasm_debug::types::SourceLoc::new(*start), + end_srcloc: wasm_debug::types::SourceLoc::new(*end), // this not being 0 breaks inst-level debugging - compiled_offset: 0, - compiled_size: code_buf.len(), + body_offset: 0, + body_len: code_buf.len(), }; Some((entry, labels_ranges, stack_slots)) } else { From a2e531bda444a1c4ffb43ae6e8d51c919e1ea4f1 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 19 Feb 2020 15:53:36 -0800 Subject: [PATCH 10/26] Update to use published wasm-debug; make dep optional in clif-backend --- Cargo.lock | 2 ++ Cargo.toml | 1 + lib/clif-backend/Cargo.toml | 5 ++++- lib/runtime-core/Cargo.toml | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e93639fa956..2b6fe58e330 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1727,6 +1727,8 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-debug" version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86840eccceaf682e29be7810dcae5785b9c3b0349ce44d3eaecd9e50f893aee0" dependencies = [ "anyhow", "cranelift-entity", diff --git a/Cargo.toml b/Cargo.toml index 8d0f54496e0..e4dc711d660 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,6 +88,7 @@ docs = ["wasmer-runtime/docs"] fast-tests = [] backend-cranelift = [ "wasmer-clif-backend", + "wasmer-clif-backend/generate-debug-information", "wasmer-runtime/cranelift", "wasmer-middleware-common-tests/clif", ] diff --git a/lib/clif-backend/Cargo.toml b/lib/clif-backend/Cargo.toml index 9bd5286e4c4..2bb4684f373 100644 --- a/lib/clif-backend/Cargo.toml +++ b/lib/clif-backend/Cargo.toml @@ -23,7 +23,7 @@ byteorder = "1.3.2" nix = "0.15.0" libc = "0.2.60" rayon = "1.1" -wasm-debug = { version = "0.1", path = "../../../Dev/wasm-debug" } +wasm-debug = { optional = true, version = "0.1" } # Dependencies for caching. [dependencies.serde] @@ -39,3 +39,6 @@ version = "0.0.7" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["errhandlingapi", "minwindef", "minwinbase", "winnt"] } wasmer-win-exception-handler = { path = "../win-exception-handler", version = "0.13.1" } + +[features] +generate-debug-information = ["wasm-debug"] diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index 8665eab183b..fe43c17338c 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -20,7 +20,7 @@ libc = "0.2.60" hex = "0.4" smallvec = "0.6" bincode = "1.1" -wasm-debug = { optional = true, version = "0.1.0", path = "../../../Dev/wasm-debug" } +wasm-debug = { optional = true, version = "0.1.0" } target-lexicon = "0.9" [dependencies.indexmap] From 3bca20d3d3e989789a8d575e207c2be24c7c3cc7 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 19 Feb 2020 16:37:43 -0800 Subject: [PATCH 11/26] Add changelog entry, clean up --- CHANGELOG.md | 1 + Cargo.lock | 31 +++++++++++++++++++++++-------- lib/clif-backend/src/resolver.rs | 14 +++++++------- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a0c33badc2..47b4b0d563c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## **[Unreleased]** - [#1216](https://github.com/wasmerio/wasmer/pull/1216) `wasmer-interface-types` receives a binary encoder. +- [#1212](https://github.com/wasmerio/wasmer/pull/1212) Add `--generate-debug-info` and `-g` flags to `wasmer run` to generate debug information during compilation that is passed via the GDB JIT interface to a debugger to allow source-level debugging of Wasm files. Currently only available on clif-backend, see PR for more information on its implementation. - [#1228](https://github.com/wasmerio/wasmer/pull/1228) Singlepass cleanup: Resolve several FIXMEs and remove protect_unix. - [#1218](https://github.com/wasmerio/wasmer/pull/1218) Enable Cranelift verifier in debug mode. Fix bug with table indices being the wrong type. - [#787](https://github.com/wasmerio/wasmer/pull/787) New crate `wasmer-interface-types` to implement WebAssembly Interface Types. diff --git a/Cargo.lock b/Cargo.lock index caf9db8043e..25a70dace60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -571,11 +571,11 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dd6190aad0f05ddbbf3245c54ed14ca4aa6dd32f22312b70d8f168c3e3e633" dependencies = [ - "arrayvec", + "arrayvec 0.5.1", "byteorder", "fallible-iterator", "indexmap", - "smallvec 1.1.0", + "smallvec 1.2.0", "stable_deref_trait", ] @@ -1393,9 +1393,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28" dependencies = [ - "proc-macro2 1.0.6", + "proc-macro2 1.0.8", "quote 1.0.2", - "syn 1.0.11", + "syn 1.0.14", ] [[package]] @@ -1514,6 +1514,15 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" +[[package]] +name = "string-interner" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd710eadff449a1531351b0e43eb81ea404336fa2f56c777427ab0e32a4cf183" +dependencies = [ + "serde", +] + [[package]] name = "strsim" version = "0.8.0" @@ -1851,7 +1860,7 @@ dependencies = [ "wasmer-clif-fork-wasm", "wasmer-runtime-core", "wasmer-win-exception-handler", - "wasmparser 0.45.0", + "wasmparser 0.45.2", "winapi", ] @@ -1878,7 +1887,7 @@ dependencies = [ "log", "thiserror", "wasmer-clif-fork-frontend", - "wasmparser 0.45.0", + "wasmparser 0.45.2", ] [[package]] @@ -1947,7 +1956,7 @@ dependencies = [ "smallvec 0.6.13", "wabt", "wasmer-runtime-core", - "wasmparser 0.45.0", + "wasmparser 0.45.2", "winapi", ] @@ -2034,7 +2043,7 @@ dependencies = [ "smallvec 0.6.13", "target-lexicon", "wasm-debug", - "wasmparser 0.45.0", + "wasmparser 0.45.2", "winapi", ] @@ -2131,6 +2140,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "wasmparser" +version = "0.39.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c702914acda5feeeffbc29e4d953e5b9ce79d8b98da4dbf18a77086e116c5470" + [[package]] name = "wasmparser" version = "0.45.2" diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index 3933f04de3d..25ec4ab21da 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -11,7 +11,7 @@ use crate::{ use byteorder::{ByteOrder, LittleEndian}; use cranelift_codegen::{ binemit::{Stackmap, StackmapSink}, - ir, isa,CodegenError, Context, ValueLabelsRanges, + ir, isa, CodegenError, Context, ValueLabelsRanges, }; use rayon::prelude::*; use std::{ @@ -141,12 +141,12 @@ impl FuncResolverBuilder { &mut local_trap_sink, &mut stackmap_sink, ) - .map_err(|e| match e { - CodegenError::Verifier(v) => CompileError::InternalError { - msg: format!("Verifier error: {}", v), - }, - _ => CompileError::InternalError { msg: e.to_string() }, - })?; + .map_err(|e| match e { + CodegenError::Verifier(v) => CompileError::InternalError { + msg: format!("Verifier error: {}", v), + }, + _ => CompileError::InternalError { msg: e.to_string() }, + })?; let debug_entry = if generate_debug_info { let func = &ctx.func; From 65a9e04f3c8505ad88f85b1d82d6a09f9cb38791 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 20 Feb 2020 11:20:40 -0800 Subject: [PATCH 12/26] Add updates from feedback Co-authored-by: Nick Lewycky --- lib/clif-backend/src/code.rs | 6 ++++-- lib/clif-backend/src/resolver.rs | 24 ++++++++++++------------ lib/runtime-core/src/instance.rs | 3 +-- lib/runtime-core/src/vm.rs | 11 +---------- 4 files changed, 18 insertions(+), 26 deletions(-) diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index e78333f6688..7b247281a38 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -257,7 +257,11 @@ pub struct CraneliftFunctionCodeGenerator { next_local: usize, position: Position, func_env: FunctionEnvironment, + /// Start location of the function as an offset in bytes in the Wasm module + /// from the beginning of the code section. start: u32, + /// End location of the function as an offset in bytes in the Wasm module + /// from the beginning of the code section. end: u32, } @@ -1255,11 +1259,9 @@ fn declare_wasm_parameters(builder: &mut FunctionBuilder, entry_block: Ebb) -> u // This is a normal WebAssembly signature parameter, so create a local for it. let local = Variable::new(next_local); builder.declare_var(local, param_type.value_type); - //let value_label = ValueLabel::from_u32(next_local as u32); next_local += 1; let param_value = builder.ebb_params(entry_block)[i]; - //builder.set_val_label(param_value, value_label); builder.def_var(local, param_value); } if param_type.purpose == ir::ArgumentPurpose::VMContext { diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index 25ec4ab21da..43c5291fa51 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -112,18 +112,18 @@ impl FuncResolverBuilder { let generate_debug_info = info.generate_debug_info; let fb = function_bodies.iter().collect::>(); - let compiled_functions: Result< - Vec<( - Vec, - ( - LocalFuncIndex, - Option<(CompiledFunctionData, ValueLabelsRanges, Vec>)>, - RelocSink, - LocalTrapSink, - ), - )>, - CompileError, - > = fb + /// Data about the the compiled machine code. + type CompileMetadata = ( + LocalFuncIndex, + Option<(CompiledFunctionData, ValueLabelsRanges, Vec>)>, + RelocSink, + LocalTrapSink, + ); + + /// Compiled machine code and information about it + type CompileData = (Vec, CompileMetadata); + + let compiled_functions: Result, CompileError> = fb .par_iter() .map_init( || Context::new(), diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index b07e0168c67..fff1797d777 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -79,13 +79,12 @@ impl Instance { unsafe { let backing = &mut *(&mut inner.backing as *mut _); let import_backing = &mut *(&mut inner.import_backing as *mut _); - let mut real_ctx = match imports.call_state_creator() { + let real_ctx = match imports.call_state_creator() { Some((data, dtor)) => { vm::Ctx::new_with_data(backing, import_backing, &module, data, dtor) } None => vm::Ctx::new(backing, import_backing, &module), }; - real_ctx.internal.ctx = vmctx.as_mut_ptr(); vmctx.as_mut_ptr().write(real_ctx); }; Box::leak(vmctx); diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 42cc29efa40..2a39bdec10f 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -134,9 +134,6 @@ pub struct InternalCtx { /// Interrupt signal mem. pub interrupt_signal_mem: *mut u8, - - /// hmm - pub ctx: *mut Ctx, } static INTERNAL_FIELDS: AtomicUsize = AtomicUsize::new(0); @@ -309,7 +306,6 @@ impl Ctx { internals: &mut local_backing.internals.0, interrupt_signal_mem: get_interrupt_signal_mem(), - ctx: std::ptr::null_mut(), //local_backing.vm_memories[LocalMemoryIndex::new(0)], }, local_functions: local_backing.local_functions.as_ptr(), @@ -340,8 +336,6 @@ impl Ctx { }; ((*mem).base, (*mem).bound) }; - - dbg!(mem_bound); Self { internal: InternalCtx { memories: local_backing.vm_memories.as_mut_ptr(), @@ -365,9 +359,6 @@ impl Ctx { internals: &mut local_backing.internals.0, interrupt_signal_mem: get_interrupt_signal_mem(), - - ctx: std::ptr::null_mut(), - //first_mem: local_backing.vm_memories[LocalMemoryIndex::new(0)], }, local_functions: local_backing.local_functions.as_ptr(), @@ -546,7 +537,7 @@ impl Ctx { } pub const fn offset_local_functions() -> u8 { - 15 * (mem::size_of::() as u8) + 14 * (mem::size_of::() as u8) } } From 0ec08b5bbd1fe375e97fd1b8da1f4232207a453c Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 20 Feb 2020 13:06:50 -0800 Subject: [PATCH 13/26] Add updates from feedback --- lib/clif-backend/src/resolver.rs | 11 ++--------- lib/runtime-core/src/jit_debug.rs | 2 +- lib/runtime-core/src/structures/map.rs | 2 +- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index 43c5291fa51..933b1911f07 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -213,15 +213,8 @@ impl FuncResolverBuilder { let compiled_functions = compiled_functions; let mut total_size = 0; // We separate into two iterators, one iterable and one into iterable - let (code_bufs, sinks): ( - Vec>, - Vec<( - LocalFuncIndex, - Option<(CompiledFunctionData, ValueLabelsRanges, Vec>)>, - RelocSink, - LocalTrapSink, - )>, - ) = compiled_functions.into_iter().unzip(); + let (code_bufs, sinks): (Vec>, Vec) = + compiled_functions.into_iter().unzip(); for (code_buf, (_, debug_info, reloc_sink, mut local_trap_sink)) in code_bufs.iter().zip(sinks.into_iter()) { diff --git a/lib/runtime-core/src/jit_debug.rs b/lib/runtime-core/src/jit_debug.rs index 14a2668cb3b..84256b8feda 100644 --- a/lib/runtime-core/src/jit_debug.rs +++ b/lib/runtime-core/src/jit_debug.rs @@ -9,7 +9,7 @@ use std::sync::Arc; /// /// The debugger will wait for this function to be called and then take /// control to read the data we prepared. -// implementation of this function is from wasmtime and is licensed under +// Implementation of this function is derived from wasmtime and is licensed under // the Apache 2.0 license. See ATTRIBUTIONS.md for full license and more // information. #[no_mangle] diff --git a/lib/runtime-core/src/structures/map.rs b/lib/runtime-core/src/structures/map.rs index 0f8eac6f6ba..0ea4beb4ff9 100644 --- a/lib/runtime-core/src/structures/map.rs +++ b/lib/runtime-core/src/structures/map.rs @@ -79,7 +79,7 @@ where self.elems } - /// Iterate over the values of the map in order + /// Iterate over the values of the map in order. pub fn values(&self) -> impl Iterator { self.elems.iter() } From 7c5f8251e358d4510e43ca3bd192cfcdf2847cac Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 20 Feb 2020 16:59:59 -0800 Subject: [PATCH 14/26] Flatten tuple in return type, conditionally remove more jit debug code --- lib/clif-backend/src/code.rs | 9 +++---- lib/llvm-backend/src/code.rs | 8 +++--- lib/runtime-core/src/codegen.rs | 12 ++++----- lib/runtime-core/src/lib.rs | 1 + lib/runtime-core/src/module.rs | 2 ++ lib/runtime-core/src/parse.rs | 1 + lib/singlepass-backend/src/codegen_x64.rs | 30 ++++++++++------------- 7 files changed, 30 insertions(+), 33 deletions(-) diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index 7b247281a38..9c5c0b2d6c4 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -150,7 +150,8 @@ impl ModuleCodeGenerator module_info: &ModuleInfo, ) -> Result< ( - (Caller, Option), + Caller, + Option, Box, ), CodegenError, @@ -184,10 +185,8 @@ impl ModuleCodeGenerator )); Ok(( - ( - Caller::new(handler_data, trampolines, func_resolver), - debug_metadata, - ), + Caller::new(handler_data, trampolines, func_resolver), + debug_metadata, cache_gen, )) } diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index b3c20752169..24a6b409feb 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -8847,10 +8847,8 @@ impl<'ctx> ModuleCodeGenerator, LLVMBackend, Cod module_info: &ModuleInfo, ) -> Result< ( - ( - LLVMBackend, - Option, - ), + LLVMBackend, + Option, Box, ), CodegenError, @@ -8942,7 +8940,7 @@ impl<'ctx> ModuleCodeGenerator, LLVMBackend, Cod &self.target_machine, &mut self.llvm_callbacks, ); - Ok(((backend, None), Box::new(cache_gen))) + Ok((backend, None, Box::new(cache_gen))) } fn feed_compiler_config(&mut self, config: &CompilerConfig) -> Result<(), CodegenError> { diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index 3d697e33f09..98ac04b1391 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -121,7 +121,7 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, fn finalize( self, module_info: &ModuleInfo, - ) -> Result<((RM, Option), Box), E>; + ) -> Result<(RM, Option, Box), E>; /// Creates a module from cache. unsafe fn from_cache(cache: Artifact, _: Token) -> Result; @@ -273,11 +273,11 @@ impl< }; let mut chain = (self.middleware_chain_generator)(); let info = crate::parse::read_module(wasm, &mut mcg, &mut chain, &compiler_config)?; - let ((exec_context, compile_debug_info), cache_gen) = - mcg.finalize(&info.read().unwrap()) - .map_err(|x| CompileError::InternalError { - msg: format!("{:?}", x), - })?; + let (exec_context, compile_debug_info, cache_gen) = mcg + .finalize(&info.read().unwrap()) + .map_err(|x| CompileError::InternalError { + msg: format!("{:?}", x), + })?; #[cfg(feature = "generate-debug-information")] { diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index d0b81d6ba81..2dc226ccc09 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -66,6 +66,7 @@ pub mod vmcalls; pub use trampoline_x64 as trampoline; #[cfg(unix)] pub mod fault; +#[cfg(feature = "generate-debug-information")] pub mod jit_debug; pub mod state; #[cfg(feature = "managed")] diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index f2fd1e94cb7..94ee3a3a143 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -16,6 +16,7 @@ use crate::{ }; use crate::backend::CacheGen; +#[cfg(feature = "generate-debug-information")] use crate::jit_debug; use indexmap::IndexMap; use std::collections::HashMap; @@ -83,6 +84,7 @@ pub struct ModuleInfo { /// will be generated. pub generate_debug_info: bool, + #[cfg(feature = "generate-debug-information")] #[serde(skip)] /// Resource manager of debug information being used by a debugger. pub debug_info_manager: jit_debug::JITCodeDebugInfoManager, diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index 8dd3e59056f..8d8feb97c40 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -92,6 +92,7 @@ pub fn read_module< custom_sections: HashMap::new(), generate_debug_info: compiler_config.should_generate_debug_info(), + #[cfg(feature = "generate-debug-information")] debug_info_manager: Default::default(), })); diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index fad2ead454e..f0b7879549b 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -743,10 +743,8 @@ impl ModuleCodeGenerator _: &ModuleInfo, ) -> Result< ( - ( - X64ExecutionContext, - Option, - ), + X64ExecutionContext, + Option, Box, ), CodegenError, @@ -843,19 +841,17 @@ impl ModuleCodeGenerator }; Ok(( - ( - X64ExecutionContext { - code: output, - signatures: self.signatures.as_ref().unwrap().clone(), - breakpoints: breakpoints, - func_import_count: self.func_import_count, - function_pointers: out_labels, - function_offsets: out_offsets, - msm: msm, - exception_table, - }, - None, - ), + X64ExecutionContext { + code: output, + signatures: self.signatures.as_ref().unwrap().clone(), + breakpoints: breakpoints, + func_import_count: self.func_import_count, + function_pointers: out_labels, + function_offsets: out_offsets, + msm: msm, + exception_table, + }, + None, Box::new(cache), )) } From 40e4dddc4b311f541fff4c23d2a56e413b786407 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 21 Feb 2020 14:33:32 -0800 Subject: [PATCH 15/26] Add updates from feedback Co-authored-by: Ivan Enderlin --- ATTRIBUTIONS.md | 4 +- CHANGELOG.md | 2 +- lib/clif-backend/src/code.rs | 19 ++-- lib/clif-backend/src/resolver.rs | 9 +- lib/llvm-backend/src/code.rs | 2 +- lib/runtime-core/src/codegen.rs | 41 +++++++- lib/runtime-core/src/jit_debug.rs | 111 ++++++++++++---------- lib/runtime-core/src/module.rs | 2 +- lib/runtime-core/src/parse.rs | 5 +- lib/singlepass-backend/src/codegen_x64.rs | 2 +- 10 files changed, 124 insertions(+), 73 deletions(-) diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md index 83aae0689af..5d918141d28 100644 --- a/ATTRIBUTIONS.md +++ b/ATTRIBUTIONS.md @@ -17,8 +17,8 @@ These are the projects that were used as inspiration and/or that we are using co of generating debug information for each function with Cranelift (for example, the sorting of the extended basic blocks before processing the instructions), and the API for transforming DWARF - see wasm-debug's attribution file for more information (TODO: link - and update here). + see [wasm-debug's attribution file](https://github.com/wasmerio/wasm-debug/blob/master/ATTRIBUTIONS.md) + for more information - [stackoverflow](https://stackoverflow.com/a/45795699/1072990): to create an efficient HashMap with pair keys - [Emscripten](https://github.com/kripken/emscripten): for emtests test sources to ensure compatibility - [The WebAssembly spec](https://github.com/WebAssembly/spec/tree/master/test): for implementation details of WebAssembly and spectests diff --git a/CHANGELOG.md b/CHANGELOG.md index 5716be771f2..63a9bba72c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ - [#1233](https://github.com/wasmerio/wasmer/pull/1233) Improved Wasmer C API release artifacts. - [#1216](https://github.com/wasmerio/wasmer/pull/1216) `wasmer-interface-types` receives a binary encoder. -- [#1212](https://github.com/wasmerio/wasmer/pull/1212) Add `--generate-debug-info` and `-g` flags to `wasmer run` to generate debug information during compilation that is passed via the GDB JIT interface to a debugger to allow source-level debugging of Wasm files. Currently only available on clif-backend, see PR for more information on its implementation. +- [#1212](https://github.com/wasmerio/wasmer/pull/1212) Add `--generate-debug-info` and `-g` flags to `wasmer run` to generate debug information during compilation that is passed via the GDB JIT interface to a debugger to allow source-level debugging of Wasm files. Currently only available on clif-backend, see PR for more information on its implementation. - [#1228](https://github.com/wasmerio/wasmer/pull/1228) Singlepass cleanup: Resolve several FIXMEs and remove protect_unix. - [#1218](https://github.com/wasmerio/wasmer/pull/1218) Enable Cranelift verifier in debug mode. Fix bug with table indices being the wrong type. - [#787](https://github.com/wasmerio/wasmer/pull/787) New crate `wasmer-interface-types` to implement WebAssembly Interface Types. diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index 9c5c0b2d6c4..53dae42ecd3 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -71,7 +71,7 @@ impl ModuleCodeGenerator fn next_function( &mut self, module_info: Arc>, - loc: (u32, u32), + loc: WasmSpan, ) -> Result<&mut CraneliftFunctionCodeGenerator, CodegenError> { // define_function_body( @@ -102,8 +102,7 @@ impl ModuleCodeGenerator target_config: self.isa.frontend_config().clone(), clif_signatures: self.clif_signatures.clone(), }, - start: loc.0, - end: loc.1, + loc, }; let generate_debug_info = module_info.read().unwrap().generate_debug_info; @@ -120,7 +119,7 @@ impl ModuleCodeGenerator &mut func_env.position, ); - builder.set_srcloc(ir::SourceLoc::new(loc.0)); + builder.set_srcloc(ir::SourceLoc::new(loc.start())); let entry_block = builder.create_ebb(); builder.append_ebb_params_for_function_params(entry_block); @@ -156,9 +155,9 @@ impl ModuleCodeGenerator ), CodegenError, > { - let mut func_bodies: Map = Map::new(); + let mut func_bodies: Map = Map::new(); for f in self.functions.into_iter() { - func_bodies.push((f.func, (f.start, f.end))); + func_bodies.push((f.func, f.loc)); } let (func_resolver_builder, debug_metadata, handler_data) = @@ -256,12 +255,8 @@ pub struct CraneliftFunctionCodeGenerator { next_local: usize, position: Position, func_env: FunctionEnvironment, - /// Start location of the function as an offset in bytes in the Wasm module - /// from the beginning of the code section. - start: u32, - /// End location of the function as an offset in bytes in the Wasm module - /// from the beginning of the code section. - end: u32, + /// Where the function lives in the Wasm module as a span of bytes + loc: WasmSpan, } pub struct FunctionEnvironment { diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index 933b1911f07..a2972aa36bf 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -26,6 +26,7 @@ use wasmer_runtime_core::{ SigRegistry, }, cache::Error as CacheError, + codegen::WasmSpan, error::{CompileError, CompileResult}, module::ModuleInfo, structures::{Map, SliceMap, TypedIndex}, @@ -96,7 +97,7 @@ impl FuncResolverBuilder { pub fn new( isa: &dyn isa::TargetIsa, - function_bodies: Map, + function_bodies: Map, info: &ModuleInfo, ) -> CompileResult<( Self, @@ -127,7 +128,7 @@ impl FuncResolverBuilder { .par_iter() .map_init( || Context::new(), - |ctx, (lfi, (func, (start, end)))| { + |ctx, (lfi, (func, loc))| { let mut code_buf = Vec::new(); ctx.func = func.to_owned(); let mut reloc_sink = RelocSink::new(); @@ -179,8 +180,8 @@ impl FuncResolverBuilder { let entry = CompiledFunctionData { instructions, - start_srcloc: wasm_debug::types::SourceLoc::new(*start), - end_srcloc: wasm_debug::types::SourceLoc::new(*end), + start_srcloc: wasm_debug::types::SourceLoc::new(loc.start()), + end_srcloc: wasm_debug::types::SourceLoc::new(loc.end()), // this not being 0 breaks inst-level debugging body_offset: 0, body_len: code_buf.len(), diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 24a6b409feb..7d73371cec1 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -8739,7 +8739,7 @@ impl<'ctx> ModuleCodeGenerator, LLVMBackend, Cod fn next_function( &mut self, _module_info: Arc>, - _loc: (u32, u32), + _loc: WasmSpan, ) -> Result<&mut LLVMFunctionCodeGenerator<'ctx>, CodegenError> { // Creates a new function and returns the function-scope code generator for it. let (context, builder, intrinsics) = match self.functions.last_mut() { diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index 98ac04b1391..2ae0c6ba2fb 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -65,6 +65,45 @@ impl fmt::Debug for InternalEvent { } } +/// Type representing an area of Wasm code in bytes as an offset from the +/// beginning of the code section. +/// +/// `start` must be less than or equal to `end`. +#[derive(Copy, Clone, Debug)] +pub struct WasmSpan { + /// Start offset in bytes from the beginning of the Wasm code section + start: u32, + /// End offset in bytes from the beginning of the Wasm code section + end: u32, +} + +impl WasmSpan { + /// Create a new `WasmSpan`. + /// + /// `start` must be less than or equal to `end`. + // TODO: mark this function as `const` when asserts get stabilized as `const` + // see: https://github.com/rust-lang/rust/issues/57563 + pub fn new(start: u32, end: u32) -> Self { + debug_assert!(start <= end); + Self { start, end } + } + + /// Start offset in bytes from the beginning of the Wasm code section + pub const fn start(&self) -> u32 { + self.start + } + + /// End offset in bytes from the beginning of the Wasm code section + pub const fn end(&self) -> u32 { + self.end + } + + /// Size in bytes of the span + pub const fn size(&self) -> u32 { + self.end - self.start + } +} + /// Information for a breakpoint #[cfg(unix)] pub struct BreakpointInfo<'a> { @@ -115,7 +154,7 @@ pub trait ModuleCodeGenerator, RM: RunnableModule, fn next_function( &mut self, module_info: Arc>, - loc: (u32, u32), + loc: WasmSpan, ) -> Result<&mut FCG, E>; /// Finalizes this module. fn finalize( diff --git a/lib/runtime-core/src/jit_debug.rs b/lib/runtime-core/src/jit_debug.rs index 84256b8feda..1ab2ae2508b 100644 --- a/lib/runtime-core/src/jit_debug.rs +++ b/lib/runtime-core/src/jit_debug.rs @@ -1,8 +1,11 @@ //! Code for interacting with the -//! [GDB JIT inteface](https://sourceware.org/gdb/current/onlinedocs/gdb.html#JIT-Interface). +//! [GDB JIT interface](https://sourceware.org/gdb/current/onlinedocs/gdb.html#JIT-Interface). +use lazy_static::lazy_static; + +use std::os::raw::c_char; use std::ptr; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; /// Entrypoint that the debugger will use to trigger a read from the /// [`__jit_debug_descriptor`] global variable. @@ -17,45 +20,43 @@ use std::sync::Arc; extern "C" fn __jit_debug_register_code() { // This code exists to prevent optimization of this function so that the // GDB JIT interface behaves as expected - let x = 3; + let x = 42; unsafe { std::ptr::read_volatile(&x); } } /// The operation that the debugger should perform with the entry that we gave it. -#[allow(non_camel_case_types)] #[derive(Debug)] #[repr(u32)] -pub(crate) enum JITAction { +enum JitAction { /// Do nothing. - JIT_NOACTION = 0, + NoAction = 0, /// Register the given code. - JIT_REGISTER_FN = 1, + RegisterFn = 1, /// Unregister the given code. - JIT_UNREGISTER_FN = 2, + UnregisterFn = 2, } /// Node of the doubly linked list that the GDB JIT interface reads from. -#[no_mangle] #[repr(C)] -pub(crate) struct JITCodeEntry { +struct JitCodeEntry { /// Next entry in the linked list. - next: *mut JITCodeEntry, + next: *mut Self, /// Previous entry in the linked list. - prev: *mut JITCodeEntry, + prev: *mut Self, /// Pointer to the data we want the debugger to read. - symfile_addr: *mut u8, + symfile_addr: *const c_char, /// The amount of data at the `symfile_addr` pointer. symfile_size: u64, } -impl Default for JITCodeEntry { +impl Default for JitCodeEntry { fn default() -> Self { Self { next: ptr::null_mut(), prev: ptr::null_mut(), - symfile_addr: ptr::null_mut(), + symfile_addr: ptr::null(), symfile_size: 0, } } @@ -64,41 +65,50 @@ impl Default for JITCodeEntry { /// Head node of the doubly linked list that the GDB JIT interface expects. #[no_mangle] #[repr(C)] -pub(crate) struct JitDebugDescriptor { - /// The version of the JIT interface to use (presumably, TODO: double check this) +struct JitDebugDescriptor { + /// The version of the JIT interface to use. version: u32, - /// Which action to perform, see [`JITAction`]. - action_flag: u32, + /// Which action to perform. + action_flag: JitAction, /// The entry in the list that the `action_flag` applies to. - relevant_entry: *mut JITCodeEntry, + relevant_entry: *mut JitCodeEntry, /// The first entry in the doubly linked list. - first_entry: *mut JITCodeEntry, + first_entry: *mut JitCodeEntry, } /// Global variable that the GDB JIT interface will read the data from. -/// The data is in the form of a doubly linked list. This global variable acts +/// The data is in the form of a doubly linked list. This global variable acts /// as a head node with extra information about the operation that we want the /// debugger to perform. #[no_mangle] #[allow(non_upper_case_globals)] -pub(crate) static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { +static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { version: 1, - action_flag: JITAction::JIT_NOACTION as _, + action_flag: JitAction::NoAction, relevant_entry: ptr::null_mut(), first_entry: ptr::null_mut(), }; +lazy_static! { + /// Global lock on [`__jit_debug_descriptor`]. Acquire this lock when + /// reading or writing to the global variable. This includes calls to + /// [`__jit_debug_register_code`] which may cause a debugger to read from + /// the global variable. + static ref JIT_DEBUG_DESCRIPTOR_LOCK: Mutex<()> = Mutex::new(()); +} + /// Prepend an item to the front of the `__jit_debug_descriptor` entry list /// /// # Safety -/// - Access to underlying global variable is unsynchronized. -/// - Pointer to [`JITCodeEntry`] should point to a valid entry. -unsafe fn push_front(jce: *mut JITCodeEntry) { +/// - Access to underlying global variable is unsynchronized: acquire a lock on +/// [`JIT_DEBUG_DESCRIPTOR_LOCK`] before calling this function. +/// - Pointer to [`JitCodeEntry`] must point to a valid entry. +unsafe fn push_front(jce: *mut JitCodeEntry) { if __jit_debug_descriptor.first_entry.is_null() { __jit_debug_descriptor.first_entry = jce; } else { let old_first = __jit_debug_descriptor.first_entry; - debug_assert!((*old_first).prev.is_null()); + assert!((*old_first).prev.is_null()); (*jce).next = old_first; (*old_first).prev = jce; __jit_debug_descriptor.first_entry = jce; @@ -109,11 +119,12 @@ unsafe fn push_front(jce: *mut JITCodeEntry) { /// connected to. /// /// # Safety -/// - Access to underlying global variable is unsynchronized. -/// - Pointer must point to a valid `JitCodeEntry`. -unsafe fn remove_node(jce: *mut JITCodeEntry) { +/// - Access to underlying global variable is unsynchronized: acquire a lock on +/// [`JIT_DEBUG_DESCRIPTOR_LOCK`] before calling this function. +/// - Pointer to [`JitCodeEntry`] must point to a valid entry. +unsafe fn remove_node(jce: *mut JitCodeEntry) { if __jit_debug_descriptor.first_entry == jce { - debug_assert!((*jce).prev.is_null()); + assert!((*jce).prev.is_null()); __jit_debug_descriptor.first_entry = (*jce).next; } if !(*jce).prev.is_null() { @@ -126,25 +137,26 @@ unsafe fn remove_node(jce: *mut JITCodeEntry) { /// Type for implementing Drop on the memory shared with the debugger. #[derive(Debug)] -struct JITCodeDebugInfoEntryHandleInner(*mut JITCodeEntry); +struct JitCodeDebugInfoEntryHandleInner(*mut JitCodeEntry); /// Handle to debug info about JIT code registered with a debugger #[derive(Debug, Clone)] -pub(crate) struct JITCodeDebugInfoEntryHandle(Arc); +pub(crate) struct JitCodeDebugInfoEntryHandle(Arc); -impl Drop for JITCodeDebugInfoEntryHandleInner { +impl Drop for JitCodeDebugInfoEntryHandleInner { fn drop(&mut self) { + let _guard = JIT_DEBUG_DESCRIPTOR_LOCK.lock().unwrap(); unsafe { // unregister the function when dropping the JIT code entry __jit_debug_descriptor.relevant_entry = self.0; - __jit_debug_descriptor.action_flag = JITAction::JIT_UNREGISTER_FN as u32; + __jit_debug_descriptor.action_flag = JitAction::UnregisterFn; __jit_debug_register_code(); __jit_debug_descriptor.relevant_entry = ptr::null_mut(); - __jit_debug_descriptor.action_flag = JITAction::JIT_NOACTION as u32; + __jit_debug_descriptor.action_flag = JitAction::NoAction; remove_node(self.0); - let entry: Box = Box::from_raw(self.0); + let entry: Box = Box::from_raw(self.0); Vec::from_raw_parts( - entry.symfile_addr, + entry.symfile_addr as *mut u8, entry.symfile_size as _, entry.symfile_size as _, ); @@ -154,16 +166,16 @@ impl Drop for JITCodeDebugInfoEntryHandleInner { /// Manager of debug info registered with the debugger. #[derive(Debug, Clone, Default)] -pub struct JITCodeDebugInfoManager { - inner: Vec, +pub(crate) struct JitCodeDebugInfoManager { + inner: Vec, } -impl JITCodeDebugInfoManager { +impl JitCodeDebugInfoManager { /// Register debug info relating to JIT code with the debugger. pub(crate) fn register_new_jit_code_entry( &mut self, bytes: &[u8], - ) -> JITCodeDebugInfoEntryHandle { + ) -> JitCodeDebugInfoEntryHandle { let mut owned_bytes = bytes.iter().cloned().collect::>(); // ensure length == capacity to simplify memory freeing code owned_bytes.shrink_to_fit(); @@ -172,22 +184,23 @@ impl JITCodeDebugInfoManager { std::mem::forget(owned_bytes); - let entry: *mut JITCodeEntry = Box::into_raw(Box::new(JITCodeEntry { - symfile_addr: ptr, + let entry: *mut JitCodeEntry = Box::into_raw(Box::new(JitCodeEntry { + symfile_addr: ptr as *const _, symfile_size: len as _, - ..JITCodeEntry::default() + ..JitCodeEntry::default() })); unsafe { + let _guard = JIT_DEBUG_DESCRIPTOR_LOCK.lock().unwrap(); push_front(entry); __jit_debug_descriptor.relevant_entry = entry; - __jit_debug_descriptor.action_flag = JITAction::JIT_REGISTER_FN as u32; + __jit_debug_descriptor.action_flag = JitAction::RegisterFn; __jit_debug_register_code(); __jit_debug_descriptor.relevant_entry = ptr::null_mut(); - __jit_debug_descriptor.action_flag = JITAction::JIT_NOACTION as u32; + __jit_debug_descriptor.action_flag = JitAction::NoAction; } - let handle = JITCodeDebugInfoEntryHandle(Arc::new(JITCodeDebugInfoEntryHandleInner(entry))); + let handle = JitCodeDebugInfoEntryHandle(Arc::new(JitCodeDebugInfoEntryHandleInner(entry))); self.inner.push(handle.clone()); handle diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index 94ee3a3a143..e7af6a7daec 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -87,7 +87,7 @@ pub struct ModuleInfo { #[cfg(feature = "generate-debug-information")] #[serde(skip)] /// Resource manager of debug information being used by a debugger. - pub debug_info_manager: jit_debug::JITCodeDebugInfoManager, + pub(crate) debug_info_manager: jit_debug::JitCodeDebugInfoManager, } impl ModuleInfo { diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index 8d8feb97c40..0c0592f568a 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -229,7 +229,10 @@ pub fn read_module< } let fcg = mcg - .next_function(Arc::clone(&info), (range.start as u32, range.end as u32)) + .next_function( + Arc::clone(&info), + WasmSpan::new(range.start as u32, range.end as u32), + ) .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; { diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index f0b7879549b..e46a52e7c62 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -682,7 +682,7 @@ impl ModuleCodeGenerator fn next_function( &mut self, _module_info: Arc>, - _loc: (u32, u32), + _loc: WasmSpan, ) -> Result<&mut X64FunctionCode, CodegenError> { let (mut assembler, mut function_labels, breakpoints, exception_table) = match self.functions.last_mut() { From d7fca539c15a003254ec7bd0b2398846cf11d92a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 21 Feb 2020 14:52:28 -0800 Subject: [PATCH 16/26] Make JitCodeDebugInfoEntryHandleInner Send and Sync --- lib/runtime-core/src/jit_debug.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/runtime-core/src/jit_debug.rs b/lib/runtime-core/src/jit_debug.rs index 1ab2ae2508b..a25fc5f8048 100644 --- a/lib/runtime-core/src/jit_debug.rs +++ b/lib/runtime-core/src/jit_debug.rs @@ -139,6 +139,12 @@ unsafe fn remove_node(jce: *mut JitCodeEntry) { #[derive(Debug)] struct JitCodeDebugInfoEntryHandleInner(*mut JitCodeEntry); +// this is safe because the pointer is never mutated directly and then +// [`JIT_DEBUG_DESCRIPTOR_LOCK`] should always be held whenever any mutation +// can happen. +unsafe impl Send for JitCodeDebugInfoEntryHandleInner {} +unsafe impl Sync for JitCodeDebugInfoEntryHandleInner {} + /// Handle to debug info about JIT code registered with a debugger #[derive(Debug, Clone)] pub(crate) struct JitCodeDebugInfoEntryHandle(Arc); From 6f5ebb564ca0c9a016486e79c2864bbdb9db4aba Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 25 Feb 2020 17:08:09 -0800 Subject: [PATCH 17/26] Allow building clif-backend without wasm-debug --- lib/clif-backend/src/resolver.rs | 88 ++++++++++++++++++-------------- lib/runtime-core/src/parse.rs | 1 - 2 files changed, 51 insertions(+), 38 deletions(-) diff --git a/lib/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index a2972aa36bf..2e97ee699f4 100644 --- a/lib/clif-backend/src/resolver.rs +++ b/lib/clif-backend/src/resolver.rs @@ -113,6 +113,12 @@ impl FuncResolverBuilder { let generate_debug_info = info.generate_debug_info; let fb = function_bodies.iter().collect::>(); + #[cfg(feature = "generate-debug-information")] + use wasm_debug::types::CompiledFunctionData; + + #[cfg(not(feature = "generate-debug-information"))] + type CompiledFunctionData = (); + /// Data about the the compiled machine code. type CompileMetadata = ( LocalFuncIndex, @@ -128,7 +134,7 @@ impl FuncResolverBuilder { .par_iter() .map_init( || Context::new(), - |ctx, (lfi, (func, loc))| { + |ctx, (lfi, (func, _loc))| { let mut code_buf = Vec::new(); ctx.func = func.to_owned(); let mut reloc_sink = RelocSink::new(); @@ -149,6 +155,7 @@ impl FuncResolverBuilder { _ => CompileError::InternalError { msg: e.to_string() }, })?; + #[cfg(feature = "generate-debug-information")] let debug_entry = if generate_debug_info { let func = &ctx.func; let encinfo = isa.encoding_info(); @@ -180,8 +187,8 @@ impl FuncResolverBuilder { let entry = CompiledFunctionData { instructions, - start_srcloc: wasm_debug::types::SourceLoc::new(loc.start()), - end_srcloc: wasm_debug::types::SourceLoc::new(loc.end()), + start_srcloc: wasm_debug::types::SourceLoc::new(_loc.start()), + end_srcloc: wasm_debug::types::SourceLoc::new(_loc.end()), // this not being 0 breaks inst-level debugging body_offset: 0, body_len: code_buf.len(), @@ -191,13 +198,15 @@ impl FuncResolverBuilder { None }; + #[cfg(not(feature = "generate-debug-information"))] + let debug_entry = None; + ctx.clear(); Ok((code_buf, (*lfi, debug_entry, reloc_sink, local_trap_sink))) }, ) .collect(); - use wasm_debug::types::CompiledFunctionData; let mut debug_metadata = if generate_debug_info { Some(wasmer_runtime_core::codegen::DebugMetadata { func_info: Map::new(), @@ -216,42 +225,47 @@ impl FuncResolverBuilder { // We separate into two iterators, one iterable and one into iterable let (code_bufs, sinks): (Vec>, Vec) = compiled_functions.into_iter().unzip(); - for (code_buf, (_, debug_info, reloc_sink, mut local_trap_sink)) in + for (code_buf, (_, _debug_info, reloc_sink, mut local_trap_sink)) in code_bufs.iter().zip(sinks.into_iter()) { let rounded_size = round_up(code_buf.len(), mem::size_of::()); - if let Some(ref mut dbg_metadata) = debug_metadata { - let (entry, vlr, stackslots) = debug_info.unwrap(); - dbg_metadata.func_info.push(entry); - let new_vlr = vlr - .into_iter() - .map(|(k, v)| { - ( - wasm_debug::types::ValueLabel::from_u32(k.as_u32()), - v.into_iter() - .map(|item| wasm_debug::types::ValueLocRange { - start: item.start, - end: item.end, - loc: match item.loc { - cranelift_codegen::ir::ValueLoc::Unassigned => { - wasm_debug::types::ValueLoc::Unassigned - } - cranelift_codegen::ir::ValueLoc::Reg(ru) => { - wasm_debug::types::ValueLoc::Reg(ru) - } - cranelift_codegen::ir::ValueLoc::Stack(st) => { - wasm_debug::types::ValueLoc::Stack( - wasm_debug::types::StackSlot::from_u32(st.as_u32()), - ) - } - }, - }) - .collect::>(), - ) - }) - .collect::(); - dbg_metadata.inst_info.push(new_vlr); - dbg_metadata.stack_slot_offsets.push(stackslots); + #[cfg(feature = "generate-debug-information")] + { + if let Some(ref mut dbg_metadata) = debug_metadata { + let (entry, vlr, stackslots) = _debug_info.unwrap(); + dbg_metadata.func_info.push(entry); + let new_vlr = vlr + .into_iter() + .map(|(k, v)| { + ( + wasm_debug::types::ValueLabel::from_u32(k.as_u32()), + v.into_iter() + .map(|item| wasm_debug::types::ValueLocRange { + start: item.start, + end: item.end, + loc: match item.loc { + cranelift_codegen::ir::ValueLoc::Unassigned => { + wasm_debug::types::ValueLoc::Unassigned + } + cranelift_codegen::ir::ValueLoc::Reg(ru) => { + wasm_debug::types::ValueLoc::Reg(ru) + } + cranelift_codegen::ir::ValueLoc::Stack(st) => { + wasm_debug::types::ValueLoc::Stack( + wasm_debug::types::StackSlot::from_u32( + st.as_u32(), + ), + ) + } + }, + }) + .collect::>(), + ) + }) + .collect::(); + dbg_metadata.inst_info.push(new_vlr); + dbg_metadata.stack_slot_offsets.push(stackslots); + } } // Clear the local trap sink and consolidate all trap info diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index 0c0592f568a..0226a971214 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -277,7 +277,6 @@ pub fn read_module< } } - // read instruction locations into vector for debug purposes { let info_read = info.read().unwrap(); let mut cur_pos = From 52d4dac45090aa8fd23442a305ac026270d5df68 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Tue, 25 Feb 2020 17:15:17 -0800 Subject: [PATCH 18/26] Rename `feed_event`'s `loc` to `source_loc` --- lib/clif-backend/src/code.rs | 4 ++-- lib/llvm-backend/src/code.rs | 2 +- lib/middleware-common/src/block_trace.rs | 2 +- lib/middleware-common/src/call_trace.rs | 2 +- lib/middleware-common/src/metering.rs | 2 +- lib/runtime-core/src/codegen.rs | 17 +++++++++-------- lib/singlepass-backend/src/codegen_x64.rs | 2 +- 7 files changed, 16 insertions(+), 15 deletions(-) diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index 53dae42ecd3..b0e52274073 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -1112,7 +1112,7 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { &mut self, event: Event, _module_info: &ModuleInfo, - loc: u32, + source_loc: u32, ) -> Result<(), CodegenError> { let op = match event { Event::Wasm(x) => x, @@ -1136,7 +1136,7 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { &mut self.position, ); builder.func.collect_debug_info(); - builder.set_srcloc(ir::SourceLoc::new(loc)); + builder.set_srcloc(ir::SourceLoc::new(source_loc)); let module_state = ModuleTranslationState::new(); let func_state = &mut self.func_translator.state; translate_operator( diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 7d73371cec1..5d568e7cdfc 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -1105,7 +1105,7 @@ impl<'ctx> FunctionCodeGenerator for LLVMFunctionCodeGenerator<'ct &mut self, event: Event, module_info: &ModuleInfo, - _loc: u32, + _source_loc: u32, ) -> Result<(), CodegenError> { let mut state = &mut self.state; let builder = self.builder.as_ref().unwrap(); diff --git a/lib/middleware-common/src/block_trace.rs b/lib/middleware-common/src/block_trace.rs index 1d539e44a98..77dafa86615 100644 --- a/lib/middleware-common/src/block_trace.rs +++ b/lib/middleware-common/src/block_trace.rs @@ -25,7 +25,7 @@ impl FunctionMiddleware for BlockTrace { op: Event<'a, 'b>, _module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, - _loc: u32, + _source_loc: u32, ) -> Result<(), Self::Error> { match op { Event::Internal(InternalEvent::FunctionBegin(_)) => { diff --git a/lib/middleware-common/src/call_trace.rs b/lib/middleware-common/src/call_trace.rs index 5f7f3203252..2f3bd7dc984 100644 --- a/lib/middleware-common/src/call_trace.rs +++ b/lib/middleware-common/src/call_trace.rs @@ -26,7 +26,7 @@ impl FunctionMiddleware for CallTrace { op: Event<'a, 'b>, _module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, - _loc: u32, + _source_loc: u32, ) -> Result<(), Self::Error> { let counter = self.counter.clone(); diff --git a/lib/middleware-common/src/metering.rs b/lib/middleware-common/src/metering.rs index a66fea509e0..409035d4ba7 100644 --- a/lib/middleware-common/src/metering.rs +++ b/lib/middleware-common/src/metering.rs @@ -44,7 +44,7 @@ impl FunctionMiddleware for Metering { op: Event<'a, 'b>, _module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, - _loc: u32, + _source_loc: u32, ) -> Result<(), Self::Error> { match op { Event::Internal(InternalEvent::FunctionBegin(_)) => { diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index 2ae0c6ba2fb..eed5d51e820 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -403,7 +403,7 @@ impl MiddlewareChain { fcg: Option<&mut FCG>, ev: Event, module_info: &ModuleInfo, - loc: u32, + source_loc: u32, ) -> Result<(), String> { let mut sink = EventSink { buffer: SmallVec::new(), @@ -412,12 +412,12 @@ impl MiddlewareChain { for m in &mut self.chain { let prev: SmallVec<[Event; 2]> = sink.buffer.drain().collect(); for ev in prev { - m.feed_event(ev, module_info, &mut sink, loc)?; + m.feed_event(ev, module_info, &mut sink, source_loc)?; } } if let Some(fcg) = fcg { for ev in sink.buffer { - fcg.feed_event(ev, module_info, loc) + fcg.feed_event(ev, module_info, source_loc) .map_err(|x| format!("{:?}", x))?; } } @@ -436,7 +436,7 @@ pub trait FunctionMiddleware { op: Event<'a, 'b>, module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, - loc: u32, + source_loc: u32, ) -> Result<(), Self::Error>; } @@ -446,7 +446,7 @@ pub(crate) trait GenericFunctionMiddleware { op: Event<'a, 'b>, module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, - loc: u32, + source_loc: u32, ) -> Result<(), String>; } @@ -456,9 +456,9 @@ impl> GenericFunctionMiddleware for T op: Event<'a, 'b>, module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, - loc: u32, + source_loc: u32, ) -> Result<(), String> { - ::feed_event(self, op, module_info, sink, loc) + ::feed_event(self, op, module_info, sink, source_loc) .map_err(|x| format!("{:?}", x)) } } @@ -478,7 +478,8 @@ pub trait FunctionCodeGenerator { fn begin_body(&mut self, module_info: &ModuleInfo) -> Result<(), E>; /// Called for each operator. - fn feed_event(&mut self, op: Event, module_info: &ModuleInfo, loc: u32) -> Result<(), E>; + fn feed_event(&mut self, op: Event, module_info: &ModuleInfo, source_loc: u32) + -> Result<(), E>; /// Finalizes the function. fn finalize(&mut self) -> Result<(), E>; diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index e46a52e7c62..78bc0af9a02 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -2530,7 +2530,7 @@ impl FunctionCodeGenerator for X64FunctionCode { &mut self, ev: Event, module_info: &ModuleInfo, - _loc: u32, + _source_loc: u32, ) -> Result<(), CodegenError> { let a = self.assembler.as_mut().unwrap(); From 56e47c17b0cc9ff8aef0579fe4077933c72adc6e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 26 Feb 2020 16:35:25 -0800 Subject: [PATCH 19/26] Revert Wasm parsing to improved old style, fixing singlepass --- lib/runtime-core/src/parse.rs | 97 +++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 44 deletions(-) diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index 8189e33f002..220912ef40b 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -266,55 +266,64 @@ pub fn read_module< } } - let mut operator_parser = parser.create_binary_reader(); - - // read locals in function body - { - let local_count = operator_parser.read_local_count().unwrap(); - let mut total = 0; - for _ in 0..local_count { - let cur_pos = - range.start as u32 + operator_parser.current_position() as u32; - let (count, ty) = operator_parser - .read_local_decl(&mut total) - .expect("Expected local"); - fcg.feed_local(ty, count as usize, cur_pos) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + let info_read = info.read().unwrap(); + let mut cur_pos = parser.current_poisiton() as u32; + let mut state = parser.read(); + // loop until the function body starts + loop { + match state { + ParserState::Error(err) => return Err(err.into()), + ParserState::FunctionBodyLocals { ref locals } => { + for &(count, ty) in locals.iter() { + fcg.feed_local(ty, count as usize, cur_pos) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + } + } + ParserState::CodeOperator(_) => { + // the body of the function has started + fcg.begin_body(&info_read) + .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; + middlewares + .run( + Some(fcg), + Event::Internal(InternalEvent::FunctionBegin(id as u32)), + &info_read, + cur_pos, + ) + .map_err(LoadError::Codegen)?; + // go to other loop + break; + } + ParserState::EndFunctionBody => break, + _ => unreachable!(), } + cur_pos = parser.current_poisiton() as u32; + state = parser.read(); } - { - let info_read = info.read().unwrap(); - let mut cur_pos = - range.start as u32 + operator_parser.current_position() as u32; - fcg.begin_body(&info_read) - .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; - middlewares - .run( - Some(fcg), - Event::Internal(InternalEvent::FunctionBegin(id as u32)), - &info_read, - cur_pos, - ) - .map_err(LoadError::Codegen)?; - - while let Ok(op) = operator_parser.read_operator() { - middlewares - .run(Some(fcg), Event::WasmOwned(op), &info_read, cur_pos) - .map_err(LoadError::Codegen)?; - cur_pos = range.start as u32 + operator_parser.current_position() as u32; + // loop until the function body ends + loop { + match state { + ParserState::Error(err) => return Err(err.into()), + ParserState::CodeOperator(op) => { + middlewares + .run(Some(fcg), Event::Wasm(op), &info_read, cur_pos) + .map_err(LoadError::Codegen)?; + } + ParserState::EndFunctionBody => break, + _ => unreachable!(), } - - cur_pos = range.start as u32 + operator_parser.current_position() as u32; - middlewares - .run( - Some(fcg), - Event::Internal(InternalEvent::FunctionEnd), - &info_read, - cur_pos, - ) - .map_err(LoadError::Codegen)?; + cur_pos = parser.current_poisiton() as u32; + state = parser.read(); } + middlewares + .run( + Some(fcg), + Event::Internal(InternalEvent::FunctionEnd), + &info_read, + cur_pos, + ) + .map_err(LoadError::Codegen)?; fcg.finalize() .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; From 0c8464e3bcb78165fe32b1f9a1ef7bf9e64f432e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 26 Feb 2020 16:40:55 -0800 Subject: [PATCH 20/26] Update changelog entry for gdb jit PR --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0cce846312..e7044146d28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## **[Unreleased]** +- [#1212](https://github.com/wasmerio/wasmer/pull/1212) Add support for GDB JIT debugging: + - Add `--generate-debug-info` and `-g` flags to `wasmer run` to generate debug information during compilation. The debug info is passed via the GDB JIT interface to a debugger to allow source-level debugging of Wasm files. Currently only available on clif-backend. + - Break public middleware APIs: there is now a `source_loc` parameter that should be passed through if applicable. + - Break compiler trait methods such as `feed_local`, `feed_event` as well as `ModuleCodeGenerator::finalize`. + ## 0.14.1 - 2020-02-24 - [#1245](https://github.com/wasmerio/wasmer/pull/1245) Use Ubuntu 16.04 in CI so that we use an earlier version of GLIBC. @@ -12,7 +17,6 @@ - [#1233](https://github.com/wasmerio/wasmer/pull/1233) Improved Wasmer C API release artifacts. - [#1216](https://github.com/wasmerio/wasmer/pull/1216) `wasmer-interface-types` receives a binary encoder. -- [#1212](https://github.com/wasmerio/wasmer/pull/1212) Add `--generate-debug-info` and `-g` flags to `wasmer run` to generate debug information during compilation that is passed via the GDB JIT interface to a debugger to allow source-level debugging of Wasm files. Currently only available on clif-backend, see PR for more information on its implementation. - [#1228](https://github.com/wasmerio/wasmer/pull/1228) Singlepass cleanup: Resolve several FIXMEs and remove protect_unix. - [#1218](https://github.com/wasmerio/wasmer/pull/1218) Enable Cranelift verifier in debug mode. Fix bug with table indices being the wrong type. - [#787](https://github.com/wasmerio/wasmer/pull/787) New crate `wasmer-interface-types` to implement WebAssembly Interface Types. From 3691c80b7d1de7fd71bc89f2f45749de3a0a7d2e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 26 Feb 2020 17:29:18 -0800 Subject: [PATCH 21/26] Add generate_debug_info field to test --- lib/runtime-core/src/vm.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index 2a39bdec10f..e7f5fed0550 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -1130,6 +1130,8 @@ mod vm_ctx_tests { em_symbol_map: None, custom_sections: HashMap::new(), + + generate_debug_info: false, }, } } From a089cf5a0c65671a2ce9989121d06e21b6f52974 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 26 Feb 2020 17:59:11 -0800 Subject: [PATCH 22/26] Link GDB JIT exposed global and fn as "linkonce" This is required because LLVM exposes its own --- lib/runtime-core/src/jit_debug.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/runtime-core/src/jit_debug.rs b/lib/runtime-core/src/jit_debug.rs index a25fc5f8048..45a620cae1b 100644 --- a/lib/runtime-core/src/jit_debug.rs +++ b/lib/runtime-core/src/jit_debug.rs @@ -15,6 +15,7 @@ use std::sync::{Arc, Mutex}; // Implementation of this function is derived from wasmtime and is licensed under // the Apache 2.0 license. See ATTRIBUTIONS.md for full license and more // information. +#[linkage = "linkonce"] #[no_mangle] #[inline(never)] extern "C" fn __jit_debug_register_code() { @@ -80,6 +81,7 @@ struct JitDebugDescriptor { /// The data is in the form of a doubly linked list. This global variable acts /// as a head node with extra information about the operation that we want the /// debugger to perform. +#[linkage = "linkonce"] #[no_mangle] #[allow(non_upper_case_globals)] static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { From dbb2ececf81d450ad161f6b5ff4f5cc3ac21c91d Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 26 Feb 2020 18:32:40 -0800 Subject: [PATCH 23/26] Work around unstable linkage attribute --- Cargo.toml | 1 + lib/runtime-core/Cargo.toml | 3 +++ lib/runtime-core/src/jit_debug.rs | 28 +++++++++++++++++++++++++--- lib/runtime-core/src/parse.rs | 2 +- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1bf73378b9f..8ad43687fac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,6 +97,7 @@ backend-llvm = [ "wasmer-llvm-backend", "wasmer-runtime/llvm", "wasmer-middleware-common-tests/llvm", + "wasmer-runtime-core/generate-debug-information-no-export-symbols", ] backend-singlepass = [ "wasmer-singlepass-backend", diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index ef7db2e8078..6a0479458ac 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -56,3 +56,6 @@ managed = [] deterministic-execution = ["wasmparser/deterministic"] # generate debug information from Wasm DWARF for use with the GDB JIT interface generate-debug-information = ["wasm-debug"] +# don't export symbols related to the GDB JIT interafce, LLVM or some other native +# code will be providing them +generate-debug-information-no-export-symbols = ["generate-debug-information", "wasm-debug"] diff --git a/lib/runtime-core/src/jit_debug.rs b/lib/runtime-core/src/jit_debug.rs index 45a620cae1b..59a8e364220 100644 --- a/lib/runtime-core/src/jit_debug.rs +++ b/lib/runtime-core/src/jit_debug.rs @@ -15,7 +15,7 @@ use std::sync::{Arc, Mutex}; // Implementation of this function is derived from wasmtime and is licensed under // the Apache 2.0 license. See ATTRIBUTIONS.md for full license and more // information. -#[linkage = "linkonce"] +#[cfg(not(feature = "generate-debug-information-no-export-symbols"))] #[no_mangle] #[inline(never)] extern "C" fn __jit_debug_register_code() { @@ -81,7 +81,7 @@ struct JitDebugDescriptor { /// The data is in the form of a doubly linked list. This global variable acts /// as a head node with extra information about the operation that we want the /// debugger to perform. -#[linkage = "linkonce"] +#[cfg(not(feature = "generate-debug-information-no-export-symbols"))] #[no_mangle] #[allow(non_upper_case_globals)] static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { @@ -91,6 +91,12 @@ static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { first_entry: ptr::null_mut(), }; +#[cfg(feature = "generate-debug-information-no-export-symbols")] +extern "C" { + static mut __jit_debug_descriptor: JitDebugDescriptor; + fn __jit_debug_register_code(); +} + lazy_static! { /// Global lock on [`__jit_debug_descriptor`]. Acquire this lock when /// reading or writing to the global variable. This includes calls to @@ -173,12 +179,28 @@ impl Drop for JitCodeDebugInfoEntryHandleInner { } /// Manager of debug info registered with the debugger. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub(crate) struct JitCodeDebugInfoManager { inner: Vec, } +impl Default for JitCodeDebugInfoManager { + fn default() -> Self { + Self::new() + } +} + impl JitCodeDebugInfoManager { + pub(crate) fn new() -> Self { + unsafe { + // ensure we set the version, even if externally linked + __jit_debug_descriptor.version = 1; + } + Self { + inner: vec![], + } + } + /// Register debug info relating to JIT code with the debugger. pub(crate) fn register_new_jit_code_entry( &mut self, diff --git a/lib/runtime-core/src/parse.rs b/lib/runtime-core/src/parse.rs index 220912ef40b..7072f23d95a 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -99,7 +99,7 @@ pub fn read_module< generate_debug_info: compiler_config.should_generate_debug_info(), #[cfg(feature = "generate-debug-information")] - debug_info_manager: Default::default(), + debug_info_manager: crate::jit_debug::JitCodeDebugInfoManager::new(), })); let mut parser = wasmparser::ValidatingParser::new( From b5cbb9df60d477366c5640c699c8176be6e28d10 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 26 Feb 2020 19:07:30 -0800 Subject: [PATCH 24/26] Fix test and build for musl --- Cargo.toml | 2 ++ lib/runtime-core/src/vm.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 8ad43687fac..d075aabea83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,6 +90,7 @@ fast-tests = [] backend-cranelift = [ "wasmer-clif-backend", "wasmer-clif-backend/generate-debug-information", + "wasmer-runtime-core/generate-debug-information", "wasmer-runtime/cranelift", "wasmer-middleware-common-tests/clif", ] @@ -97,6 +98,7 @@ backend-llvm = [ "wasmer-llvm-backend", "wasmer-runtime/llvm", "wasmer-middleware-common-tests/llvm", + "wasmer-runtime-core/generate-debug-information", "wasmer-runtime-core/generate-debug-information-no-export-symbols", ] backend-singlepass = [ diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index e7f5fed0550..12c5ec41f22 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -1132,6 +1132,8 @@ mod vm_ctx_tests { custom_sections: HashMap::new(), generate_debug_info: false, + #[cfg(feature = "generate-debug-information")] + debug_info_manager: crate::jit_debug::JitCodeDebugInfoManager::new(), }, } } From 0593965459ff31ba106d5805d449bf0e1cc3365a Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 26 Feb 2020 19:41:53 -0800 Subject: [PATCH 25/26] Make gdb jit symbol weak linking default in llvm-backend features --- lib/llvm-backend/Cargo.toml | 2 +- lib/runtime-core/Cargo.toml | 2 +- lib/runtime-core/src/jit_debug.rs | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index 8593e1cb5e1..6236a04ca68 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -10,7 +10,7 @@ edition = "2018" readme = "README.md" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1" } +wasmer-runtime-core = { path = "../runtime-core", version = "0.14.1", features = ["generate-debug-information-no-export-symbols"] } wasmparser = "0.51.3" smallvec = "0.6" goblin = "0.0.24" diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index 6a0479458ac..b49e88fc688 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -58,4 +58,4 @@ deterministic-execution = ["wasmparser/deterministic"] generate-debug-information = ["wasm-debug"] # don't export symbols related to the GDB JIT interafce, LLVM or some other native # code will be providing them -generate-debug-information-no-export-symbols = ["generate-debug-information", "wasm-debug"] +generate-debug-information-no-export-symbols = [] diff --git a/lib/runtime-core/src/jit_debug.rs b/lib/runtime-core/src/jit_debug.rs index 59a8e364220..334a1a05b90 100644 --- a/lib/runtime-core/src/jit_debug.rs +++ b/lib/runtime-core/src/jit_debug.rs @@ -196,11 +196,9 @@ impl JitCodeDebugInfoManager { // ensure we set the version, even if externally linked __jit_debug_descriptor.version = 1; } - Self { - inner: vec![], - } + Self { inner: vec![] } } - + /// Register debug info relating to JIT code with the debugger. pub(crate) fn register_new_jit_code_entry( &mut self, From 91808b9df24f31f94413150f0de2e9f29b04d94e Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 27 Feb 2020 14:22:26 -0800 Subject: [PATCH 26/26] Update test-rest to explicitly test each crate --- Cargo.toml | 5 ++--- Makefile | 24 +++++++++--------------- lib/runtime-core/src/jit_debug.rs | 2 ++ 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d075aabea83..c111dc37403 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,7 +80,7 @@ serde = { version = "1", features = ["derive"] } # used by the plugin example typetag = "0.1" # used by the plugin example [features] -default = ["fast-tests", "wasi", "backend-cranelift", "wabt", "wasmer-runtime-core/generate-debug-information"] +default = ["fast-tests", "wasi", "backend-cranelift", "wabt"] "loader-kernel" = ["wasmer-kernel-loader"] debug = ["fern", "log/max_level_debug", "log/release_max_level_debug"] trace = ["fern", "log/max_level_trace", "log/release_max_level_trace"] @@ -98,8 +98,7 @@ backend-llvm = [ "wasmer-llvm-backend", "wasmer-runtime/llvm", "wasmer-middleware-common-tests/llvm", - "wasmer-runtime-core/generate-debug-information", - "wasmer-runtime-core/generate-debug-information-no-export-symbols", + "wasmer-runtime-core/generate-debug-information-no-export-symbols" ] backend-singlepass = [ "wasmer-singlepass-backend", diff --git a/Makefile b/Makefile index 27fa8b7f993..24620fda22b 100644 --- a/Makefile +++ b/Makefile @@ -142,21 +142,15 @@ test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm test-capi-ems capi-test: test-capi test-rest: - cargo test --release \ - --all \ - --exclude wasmer-runtime-c-api \ - --exclude wasmer-emscripten \ - --exclude wasmer-spectests \ - --exclude wasmer-wasi \ - --exclude wasmer-middleware-common \ - --exclude wasmer-middleware-common-tests \ - --exclude wasmer-singlepass-backend \ - --exclude wasmer-clif-backend \ - --exclude wasmer-llvm-backend \ - --exclude wasmer-wasi-tests \ - --exclude wasmer-emscripten-tests \ - --exclude wasmer-runtime-core-tests - + cargo test --release -p wasmer-dev-utils + cargo test --release -p wasmer-interface-types + cargo test --release -p wasmer-kernel-loader + cargo test --release -p kernel-net + cargo test --release -p wasmer-llvm-backend-tests + cargo test --release -p wasmer-runtime + cargo test --release -p wasmer-runtime-core + cargo test --release -p wasmer-wasi-experimental-io-devices + cargo test --release -p wasmer-win-exception-handler test: spectests emtests middleware wasitests test-rest examples diff --git a/lib/runtime-core/src/jit_debug.rs b/lib/runtime-core/src/jit_debug.rs index 334a1a05b90..dbcf1a7eca9 100644 --- a/lib/runtime-core/src/jit_debug.rs +++ b/lib/runtime-core/src/jit_debug.rs @@ -93,7 +93,9 @@ static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { #[cfg(feature = "generate-debug-information-no-export-symbols")] extern "C" { + #[no_mangle] static mut __jit_debug_descriptor: JitDebugDescriptor; + #[no_mangle] fn __jit_debug_register_code(); }