diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md index 54e0eed0234..5d918141d28 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](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 -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/CHANGELOG.md b/CHANGELOG.md index 6871ecb3d68..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. diff --git a/Cargo.lock b/Cargo.lock index ebccbe9a7ea..4a8fe04ec03 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.6" @@ -225,7 +231,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45a9c21f8042b9857bda93f6c1910b9f9f24100187a3d3d52f214a34e3dc5818" dependencies = [ - "cranelift-entity", + "cranelift-entity 0.59.0", ] [[package]] @@ -238,11 +244,11 @@ dependencies = [ "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", - "cranelift-entity", + "cranelift-entity 0.59.0", "gimli", "log", "smallvec 1.2.0", - "target-lexicon", + "target-lexicon 0.10.0", "thiserror", ] @@ -253,7 +259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "084cd6d5fb0d1da28acd72c199471bfb09acc703ec8f3bf07b1699584272a3b9" dependencies = [ "cranelift-codegen-shared", - "cranelift-entity", + "cranelift-entity 0.59.0", ] [[package]] @@ -262,6 +268,12 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "701b599783305a58c25027a4d73f2d6b599b2d8ef3f26677275f480b4d51e05d" +[[package]] +name = "cranelift-entity" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "722957e05064d97a3157bf0976deed0f3e8ee4f8a4ce167a7c724ca63a4e8bd9" + [[package]] name = "cranelift-entity" version = "0.59.0" @@ -276,7 +288,7 @@ checksum = "32daf082da21c0c05d93394ff4842c2ab7c4991b1f3186a1d952f8ac660edd0b" dependencies = [ "cranelift-codegen", "raw-cpuid", - "target-lexicon", + "target-lexicon 0.10.0", ] [[package]] @@ -474,6 +486,28 @@ 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 = "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" @@ -544,8 +578,12 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dd6190aad0f05ddbbf3245c54ed14ca4aa6dd32f22312b70d8f168c3e3e633" dependencies = [ + "arrayvec 0.5.1", "byteorder", + "fallible-iterator", "indexmap", + "smallvec 1.2.0", + "stable_deref_trait", ] [[package]] @@ -568,7 +606,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]] @@ -783,6 +832,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" @@ -1322,7 +1377,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]] @@ -1336,6 +1400,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.8", + "quote 1.0.2", + "syn 1.0.14", +] + [[package]] name = "sdl2" version = "0.32.2" @@ -1452,6 +1527,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" @@ -1521,6 +1605,12 @@ dependencies = [ "syn 1.0.14", ] +[[package]] +name = "target-lexicon" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f4c118a7a38378f305a9e111fcb2f7f838c0be324bfb31a77ea04f7f6e684b4" + [[package]] name = "target-lexicon" version = "0.10.0" @@ -1721,6 +1811,22 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86840eccceaf682e29be7810dcae5785b9c3b0349ce44d3eaecd9e50f893aee0" +dependencies = [ + "anyhow", + "cranelift-entity 0.52.0", + "faerie", + "gimli", + "more-asserts", + "target-lexicon 0.9.0", + "thiserror", + "wasmparser 0.39.3", +] + [[package]] name = "wasmer" version = "0.14.1" @@ -1758,7 +1864,7 @@ version = "0.14.1" dependencies = [ "byteorder", "cranelift-codegen", - "cranelift-entity", + "cranelift-entity 0.59.0", "cranelift-native", "libc", "nix", @@ -1767,12 +1873,13 @@ dependencies = [ "serde-bench", "serde_bytes", "serde_derive", - "target-lexicon", + "target-lexicon 0.10.0", + "wasm-debug", "wasmer-clif-fork-frontend", "wasmer-clif-fork-wasm", "wasmer-runtime-core", "wasmer-win-exception-handler", - "wasmparser", + "wasmparser 0.51.3", "winapi", ] @@ -1785,7 +1892,7 @@ dependencies = [ "cranelift-codegen", "log", "smallvec 1.2.0", - "target-lexicon", + "target-lexicon 0.10.0", ] [[package]] @@ -1795,11 +1902,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a35e21d3aebc51cc6ebc0e830cf8458a9891c3482fb3c65ad18d408102929ae5" dependencies = [ "cranelift-codegen", - "cranelift-entity", + "cranelift-entity 0.59.0", "log", "thiserror", "wasmer-clif-fork-frontend", - "wasmparser", + "wasmparser 0.51.3", ] [[package]] @@ -1858,7 +1965,7 @@ version = "0.14.1" dependencies = [ "byteorder", "cc", - "goblin", + "goblin 0.0.24", "inkwell", "lazy_static", "libc", @@ -1869,7 +1976,7 @@ dependencies = [ "smallvec 0.6.13", "wabt", "wasmer-runtime-core", - "wasmparser", + "wasmparser 0.51.3", "winapi", ] @@ -1954,7 +2061,9 @@ dependencies = [ "serde_bytes", "serde_derive", "smallvec 0.6.13", - "wasmparser", + "target-lexicon 0.9.0", + "wasm-debug", + "wasmparser 0.51.3", "winapi", ] @@ -2051,6 +2160,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.51.3" diff --git a/Cargo.toml b/Cargo.toml index 585b4178fcc..c111dc37403 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,6 +89,8 @@ docs = ["wasmer-runtime/docs"] 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", ] @@ -96,6 +98,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/Makefile b/Makefile index 9d85fa4997a..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 @@ -219,6 +213,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/clif-backend/Cargo.toml b/lib/clif-backend/Cargo.toml index c3d4af50b99..03cc445afce 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 = { optional = true, version = "0.1" } # Dependencies for caching. [dependencies.serde] @@ -38,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.14.1" } + +[features] +generate-debug-information = ["wasm-debug"] diff --git a/lib/clif-backend/src/code.rs b/lib/clif-backend/src/code.rs index c50b7de01ce..67506f93194 100644 --- a/lib/clif-backend/src/code.rs +++ b/lib/clif-backend/src/code.rs @@ -72,6 +72,7 @@ impl ModuleCodeGenerator fn next_function( &mut self, module_info: Arc>, + loc: WasmSpan, ) -> Result<&mut CraneliftFunctionCodeGenerator, CodegenError> { // define_function_body( @@ -102,8 +103,14 @@ impl ModuleCodeGenerator target_config: self.isa.frontend_config().clone(), clif_signatures: self.clif_signatures.clone(), }, + loc, }; + 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_blocks(), 0, "Function must be empty"); debug_assert_eq!(func_env.func.dfg.num_insts(), 0, "Function must be empty"); @@ -113,8 +120,7 @@ impl ModuleCodeGenerator &mut func_env.position, ); - // TODO srcloc - //builder.set_srcloc(cur_srcloc(&reader)); + builder.set_srcloc(ir::SourceLoc::new(loc.start())); let entry_block = builder.create_block(); builder.append_block_params_for_function_params(entry_block); @@ -142,13 +148,20 @@ impl ModuleCodeGenerator fn finalize( self, module_info: &ModuleInfo, - ) -> Result<(Caller, Box), CodegenError> { - 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() { - func_bodies.push(f.func); + func_bodies.push((f.func, f.loc)); } - 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)); @@ -173,6 +186,7 @@ impl ModuleCodeGenerator Ok(( Caller::new(handler_data, trampolines, func_resolver), + debug_metadata, cache_gen, )) } @@ -242,6 +256,8 @@ pub struct CraneliftFunctionCodeGenerator { next_local: usize, position: Position, func_env: FunctionEnvironment, + /// Where the function lives in the Wasm module as a span of bytes + loc: WasmSpan, } pub struct FunctionEnvironment { @@ -1135,13 +1151,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, @@ -1157,7 +1174,12 @@ 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, + source_loc: u32, + ) -> Result<(), CodegenError> { let op = match event { Event::Wasm(x) => x, Event::WasmOwned(ref x) => x, @@ -1179,6 +1201,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(source_loc)); let module_state = ModuleTranslationState::new(); let func_state = &mut self.func_translator.state; translate_operator( @@ -1188,6 +1212,7 @@ impl FunctionCodeGenerator for CraneliftFunctionCodeGenerator { func_state, &mut self.func_env, )?; + Ok(()) } @@ -1206,7 +1231,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 { @@ -1216,6 +1241,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(); diff --git a/lib/clif-backend/src/module.rs b/lib/clif-backend/src/module.rs index dd81b693224..1fb15b5f6b1 100644 --- a/lib/clif-backend/src/module.rs +++ b/lib/clif-backend/src/module.rs @@ -55,7 +55,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/clif-backend/src/resolver.rs b/lib/clif-backend/src/resolver.rs index 6c5ad2eec4e..37b94c03049 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, + ir, isa, CodegenError, Context, ValueLabelsRanges, }; use rayon::prelude::*; use std::{ @@ -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,59 +97,183 @@ 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| match e { - CodegenError::Verifier(v) => CompileError::InternalError { - msg: format!("Verifier error: {}", v), - }, - _ => CompileError::InternalError { msg: e.to_string() }, - })?; - ctx.clear(); - Ok((code_buf, (reloc_sink, local_trap_sink))) - }, - ) - .collect(); + 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, + 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(), + |ctx, (lfi, (func, _loc))| { + 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| match e { + CodegenError::Verifier(v) => CompileError::InternalError { + msg: format!("Verifier error: {}", v), + }, + _ => 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(); + let mut blocks = func.layout.blocks().collect::>(); + blocks.sort_by_key(|block| func.offsets[*block]); + let instructions = blocks + .into_iter() + .flat_map(|block| { + func.inst_offsets(block, &encinfo) + .map(|(offset, inst, length)| { + let srcloc = func.srclocs[inst]; + let val = srcloc.bits(); + wasm_debug::types::CompiledInstructionData { + srcloc: wasm_debug::types::SourceLoc::new(val), + code_offset: offset as usize, + code_len: length as usize, + } + }) + }) + .collect::>(); + + 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_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(), + }; + Some((entry, labels_ranges, stack_slots)) + } else { + 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(); + + let mut debug_metadata = if generate_debug_info { + Some(wasmer_runtime_core::codegen::DebugMetadata { + func_info: Map::new(), + inst_info: Map::new(), + pointers: vec![], + stack_slot_offsets: Map::new(), + }) + } else { + None + }; - let compiled_functions = compiled_functions?; + 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)>) = + let (code_bufs, sinks): (Vec>, Vec) = compiled_functions.into_iter().unzip(); - for (code_buf, (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::()); + #[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 // 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()); @@ -180,7 +305,14 @@ 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::()); + 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()] .copy_from_slice(&compiled[..]); @@ -202,7 +334,7 @@ impl FuncResolverBuilder { func_resolver_builder.relocate_locals(); - Ok((func_resolver_builder, handler_data)) + Ok((func_resolver_builder, debug_metadata, handler_data)) } fn relocate_locals(&mut self) { 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/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index bf98b8c06a4..d33e67fac80 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -999,7 +999,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)?; @@ -1100,7 +1100,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, + _source_loc: u32, + ) -> Result<(), CodegenError> { let mut state = &mut self.state; let builder = self.builder.as_ref().unwrap(); let context = self.context.as_ref().unwrap(); @@ -8732,6 +8737,7 @@ impl<'ctx> ModuleCodeGenerator, LLVMBackend, Cod fn next_function( &mut self, module_info: Arc>, + _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() { @@ -8837,7 +8843,14 @@ 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(), @@ -8925,7 +8938,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/middleware-common/src/block_trace.rs b/lib/middleware-common/src/block_trace.rs index f104ab086b4..77dafa86615 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>, + _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 5cb77534c56..2f3bd7dc984 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>, + _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 a7d10aece10..409035d4ba7 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>, + _source_loc: u32, ) -> Result<(), Self::Error> { match op { Event::Internal(InternalEvent::FunctionBegin(_)) => { diff --git a/lib/runtime-core/Cargo.toml b/lib/runtime-core/Cargo.toml index 7f345fa262e..b49e88fc688 100644 --- a/lib/runtime-core/Cargo.toml +++ b/lib/runtime-core/Cargo.toml @@ -20,6 +20,8 @@ libc = "0.2.60" hex = "0.4" smallvec = "0.6" bincode = "1.1" +wasm-debug = { optional = true, version = "0.1.0" } +target-lexicon = "0.9" [dependencies.indexmap] version = "1.2" @@ -52,3 +54,8 @@ 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"] +# 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 = [] diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index db68e1715d2..4aca2d2a71c 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -140,6 +140,17 @@ pub struct CompilerConfig { pub cpu_features: Option, pub backend_specific_config: Option, + + 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`. diff --git a/lib/runtime-core/src/codegen.rs b/lib/runtime-core/src/codegen.rs index 47f525f0f1a..b65234cf4dc 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> { @@ -112,14 +151,45 @@ 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: WasmSpan, + ) -> 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; } +/// 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, + /// [`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)>, +} + /// 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 +291,7 @@ impl< CGEN: Fn() -> MiddlewareChain, > Compiler for StreamingCompiler { + #[allow(unused_variables)] fn compile( &self, wasm: &[u8], @@ -241,11 +312,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, 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")] + { + 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)), @@ -296,6 +403,7 @@ impl MiddlewareChain { fcg: Option<&mut FCG>, ev: Event, module_info: &ModuleInfo, + source_loc: u32, ) -> Result<(), String> { let mut sink = EventSink { buffer: SmallVec::new(), @@ -304,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)?; + 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) + fcg.feed_event(ev, module_info, source_loc) .map_err(|x| format!("{:?}", x))?; } } @@ -328,6 +436,7 @@ pub trait FunctionMiddleware { op: Event<'a, 'b>, module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + source_loc: u32, ) -> Result<(), Self::Error>; } @@ -337,6 +446,7 @@ pub(crate) trait GenericFunctionMiddleware { op: Event<'a, 'b>, module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + source_loc: u32, ) -> Result<(), String>; } @@ -346,8 +456,9 @@ impl> GenericFunctionMiddleware for T op: Event<'a, 'b>, module_info: &ModuleInfo, sink: &mut EventSink<'a, 'b>, + source_loc: u32, ) -> Result<(), String> { - ::feed_event(self, op, module_info, sink) + ::feed_event(self, op, module_info, sink, source_loc) .map_err(|x| format!("{:?}", x)) } } @@ -361,13 +472,14 @@ 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, source_loc: u32) + -> Result<(), E>; /// Finalizes the function. fn finalize(&mut self) -> Result<(), E>; diff --git a/lib/runtime-core/src/jit_debug.rs b/lib/runtime-core/src/jit_debug.rs new file mode 100644 index 00000000000..dbcf1a7eca9 --- /dev/null +++ b/lib/runtime-core/src/jit_debug.rs @@ -0,0 +1,238 @@ +//! Code for interacting with the +//! [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, Mutex}; + +/// 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 derived from wasmtime and is licensed under +// the Apache 2.0 license. See ATTRIBUTIONS.md for full license and more +// information. +#[cfg(not(feature = "generate-debug-information-no-export-symbols"))] +#[no_mangle] +#[inline(never)] +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 = 42; + unsafe { + std::ptr::read_volatile(&x); + } +} + +/// The operation that the debugger should perform with the entry that we gave it. +#[derive(Debug)] +#[repr(u32)] +enum JitAction { + /// Do nothing. + NoAction = 0, + /// Register the given code. + RegisterFn = 1, + /// Unregister the given code. + UnregisterFn = 2, +} + +/// Node of the doubly linked list that the GDB JIT interface reads from. +#[repr(C)] +struct JitCodeEntry { + /// Next entry in the linked list. + next: *mut Self, + /// Previous entry in the linked list. + prev: *mut Self, + /// Pointer to the data we want the debugger to read. + symfile_addr: *const c_char, + /// The amount of data at the `symfile_addr` pointer. + 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, + } + } +} + +/// Head node of the doubly linked list that the GDB JIT interface expects. +#[no_mangle] +#[repr(C)] +struct JitDebugDescriptor { + /// The version of the JIT interface to use. + version: u32, + /// Which action to perform. + action_flag: JitAction, + /// 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. +#[cfg(not(feature = "generate-debug-information-no-export-symbols"))] +#[no_mangle] +#[allow(non_upper_case_globals)] +static mut __jit_debug_descriptor: JitDebugDescriptor = JitDebugDescriptor { + version: 1, + action_flag: JitAction::NoAction, + relevant_entry: ptr::null_mut(), + first_entry: ptr::null_mut(), +}; + +#[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(); +} + +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: 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; + assert!((*old_first).prev.is_null()); + (*jce).next = old_first; + (*old_first).prev = jce; + __jit_debug_descriptor.first_entry = jce; + } +} + +/// Removes an entry from the doubly linked list, updating both nodes that it's +/// connected to. +/// +/// # Safety +/// - 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 { + 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; + } +} + +/// Type for implementing Drop on the memory shared with the debugger. +#[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); + +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::UnregisterFn; + __jit_debug_register_code(); + __jit_debug_descriptor.relevant_entry = ptr::null_mut(); + __jit_debug_descriptor.action_flag = JitAction::NoAction; + remove_node(self.0); + let entry: Box = Box::from_raw(self.0); + Vec::from_raw_parts( + entry.symfile_addr as *mut u8, + entry.symfile_size as _, + entry.symfile_size as _, + ); + } + } +} + +/// Manager of debug info registered with the debugger. +#[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, + 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 as *const _, + symfile_size: len as _, + ..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::RegisterFn; + __jit_debug_register_code(); + __jit_debug_descriptor.relevant_entry = ptr::null_mut(); + __jit_debug_descriptor.action_flag = JitAction::NoAction; + } + + let handle = JitCodeDebugInfoEntryHandle(Arc::new(JitCodeDebugInfoEntryHandleInner(entry))); + self.inner.push(handle.clone()); + + handle + } +} diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index 831d4e67aac..7b6168c5f1a 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -66,6 +66,8 @@ 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")] pub mod tiering; diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index cf99abc274f..e7af6a7daec 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -16,6 +16,8 @@ use crate::{ }; use crate::backend::CacheGen; +#[cfg(feature = "generate-debug-information")] +use crate::jit_debug; use indexmap::IndexMap; use std::collections::HashMap; use std::sync::Arc; @@ -77,6 +79,15 @@ pub struct ModuleInfo { /// Custom sections. pub custom_sections: HashMap>, + + /// Flag controlling whether or not debug information for use in a debugger + /// 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(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 16a4a08084d..7072f23d95a 100644 --- a/lib/runtime-core/src/parse.rs +++ b/lib/runtime-core/src/parse.rs @@ -96,6 +96,10 @@ pub fn read_module< em_symbol_map: compiler_config.symbol_map.clone(), custom_sections: HashMap::new(), + + generate_debug_info: compiler_config.should_generate_debug_info(), + #[cfg(feature = "generate-debug-information")] + debug_info_manager: crate::jit_debug::JitCodeDebugInfoManager::new(), })); let mut parser = wasmparser::ValidatingParser::new( @@ -212,88 +216,115 @@ 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), + WasmSpan::new(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)))?; - } - for param in sig.params() { - fcg.feed_param(type_to_wp_type(*param)) - .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)))?; + } } - let mut body_begun = false; - + 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 { - let state = parser.read(); 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) + 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(); + } + + // loop until the function body ends + loop { + match state { + ParserState::Error(err) => return Err(err.into()), 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()) + .run(Some(fcg), Event::Wasm(op), &info_read, cur_pos) .map_err(LoadError::Codegen)?; } ParserState::EndFunctionBody => break, _ => unreachable!(), } + cur_pos = parser.current_poisiton() as u32; + state = parser.read(); } middlewares .run( Some(fcg), Event::Internal(InternalEvent::FunctionEnd), - &info.read().unwrap(), + &info_read, + cur_pos, ) .map_err(LoadError::Codegen)?; + fcg.finalize() .map_err(|x| LoadError::Codegen(format!("{:?}", x)))?; func_count = func_count.wrapping_add(1); diff --git a/lib/runtime-core/src/structures/map.rs b/lib/runtime-core/src/structures/map.rs index 08773b50a4b..0ea4beb4ff9 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/vm.rs b/lib/runtime-core/src/vm.rs index 2a39bdec10f..12c5ec41f22 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -1130,6 +1130,10 @@ mod vm_ctx_tests { em_symbol_map: None, custom_sections: HashMap::new(), + + generate_debug_info: false, + #[cfg(feature = "generate-debug-information")] + debug_info_manager: crate::jit_debug::JitCodeDebugInfoManager::new(), }, } } diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index dbf20eed636..78bc0af9a02 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -682,6 +682,7 @@ impl ModuleCodeGenerator fn next_function( &mut self, _module_info: Arc>, + _loc: WasmSpan, ) -> Result<&mut X64FunctionCode, CodegenError> { let (mut assembler, mut function_labels, breakpoints, exception_table) = match self.functions.last_mut() { @@ -740,7 +741,14 @@ 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) => ( @@ -843,6 +851,7 @@ impl ModuleCodeGenerator msm: msm, exception_table, }, + None, Box::new(cache), )) } @@ -2404,7 +2413,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(()) } @@ -2517,7 +2526,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, + _source_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 0f2f694c1e2..0208e22705c 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, @@ -557,6 +561,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[..])?; @@ -722,6 +730,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { features: options.features.into_backend_features(), backend_specific_config, + generate_debug_info: options.generate_debug_info, ..Default::default() }, &*compiler, @@ -1019,6 +1028,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 {