diff --git a/.envrc b/.envrc new file mode 100644 index 0000000000..5166a810c5 --- /dev/null +++ b/.envrc @@ -0,0 +1,16 @@ +# shellcheck shell=bash +if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4=" +fi + +dotenv_if_exists + +# watch_file "$(find ./nix -name "*.nix" -type f)" + +OS_AND_SYSTEM=$(uname -a) + +if [[ "${NO_NIX:-}" == "1" || $OS_AND_SYSTEM == "Darwin"* ]]; then + source non-nix-build/env.sh +else + use flake +fi diff --git a/.gitignore b/.gitignore index 08f2b95e18..877f83c211 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ spec/target **/fuzz/artifacts/ **/fuzz/crash-inputs/ **/fuzz/Cargo.lock + +*.wasm +.direnv/ diff --git a/Cargo.lock b/Cargo.lock index 48125ddfa9..87a4fe31bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,12 @@ dependencies = [ "gimli", ] +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aho-corasick" version = "1.1.3" @@ -681,6 +687,16 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec" +[[package]] +name = "flate2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "foldhash" version = "0.1.5" @@ -989,6 +1005,24 @@ dependencies = [ "rustix 0.38.44", ] +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" +dependencies = [ + "adler2", +] + [[package]] name = "multi-stash" version = "0.2.0" @@ -1011,9 +1045,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", + "flate2", "hashbrown", "indexmap", "memchr", + "ruzstd", + "wasmparser 0.222.1", ] [[package]] @@ -1250,6 +1287,15 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +[[package]] +name = "ruzstd" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f" +dependencies = [ + "twox-hash", +] + [[package]] name = "ryu" version = "1.0.20" @@ -1356,6 +1402,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "string-interner" version = "0.19.0" @@ -1481,6 +1533,16 @@ dependencies = [ "once_cell", ] +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + [[package]] name = "typenum" version = "1.18.0" @@ -1670,12 +1732,16 @@ dependencies = [ "arrayvec", "assert_matches", "criterion", + "gimli", + "memmap2", "multi-stash", + "object", "smallvec", "spin", "wasmi_collections", "wasmi_core 0.44.0", "wasmi_ir", + "wasmi_tracer", "wasmparser 0.227.1", "wat", ] @@ -1769,6 +1835,10 @@ dependencies = [ "wasmi_core 0.44.0", ] +[[package]] +name = "wasmi_tracer" +version = "0.1.0" + [[package]] name = "wasmi_wasi" version = "0.44.0" @@ -1787,6 +1857,15 @@ dependencies = [ "wast 227.0.1", ] +[[package]] +name = "wasmparser" +version = "0.222.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa210fd1788e6b37a1d1930f3389c48e1d6ebd1a013d34fa4b7f9e3e3bf03146" +dependencies = [ + "bitflags", +] + [[package]] name = "wasmparser" version = "0.226.0" diff --git a/Cargo.toml b/Cargo.toml index 747aa536af..f40f5442e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,8 @@ members = [ "crates/ir", "crates/fuzz", "crates/wast", - "fuzz", + "fuzz", "crates/tracer", + # "wasm-test", ] exclude = [] resolver = "2" @@ -39,6 +40,7 @@ wasmi_c_api_impl = { version = "0.44.0", path = "crates/c_api" } wasmi_c_api_macros = { version = "0.44.0", path = "crates/c_api/macro" } wasmi_fuzz = { version = "0.44.0", path = "crates/fuzz" } wasmi_wast = { version = "0.44.0", path = "crates/wast" } +wasmi_tracer = { version = "0.1.0", path = "crates/tracer" } # wasm-tools dependencies wat = { version = "1.227.1", default-features = false } diff --git a/crates/tracer/Cargo.toml b/crates/tracer/Cargo.toml new file mode 100644 index 0000000000..bc3817651c --- /dev/null +++ b/crates/tracer/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "wasmi_tracer" +version = "0.1.0" +authors.workspace = true +repository.workspace = true +# version.workspace = true +rust-version.workspace = true +edition.workspace = true +readme.workspace = true +license.workspace = true +keywords.workspace = true +categories.workspace = true +exclude.workspace = true + +[dependencies] diff --git a/crates/tracer/src/lib.rs b/crates/tracer/src/lib.rs new file mode 100644 index 0000000000..ca02e3cbb8 --- /dev/null +++ b/crates/tracer/src/lib.rs @@ -0,0 +1,74 @@ +use std::path::{Path, PathBuf}; +use std::println; + +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +// TODO +#[derive(Debug, Clone)] +struct DebugInfo { + // for now minimal info here + wasm_exe_path: std::path::PathBuf, + // TODO eventually dwarf/gimli stuff, not sure + // TODO: eventually the tracer from runtime_tracer but maybe DebugInfo is just PDO for debug info?A + // maybe there are several kinds, e.g. frame-based, maybe others + // so maybe our fn-s will know how to deal with them + // TODO: local_variables: HashMap, +} + +impl DebugInfo { + fn new(wasm_exe_path: &Path) -> Self { + DebugInfo { wasm_exe_path: PathBuf::from(wasm_exe_path) } + } +} + +#[derive(Debug, Clone)] +pub struct WasmTracer { + debug_info: DebugInfo, + // TODO: tracer: runtime_tracing.Tracer, + // etc +} + +// just to make it build so we can branch-out + +impl WasmTracer { + pub fn new(wasm_exe_path: &Path) -> Self { + WasmTracer { + debug_info: DebugInfo::new(wasm_exe_path), + } + } + + pub fn load_local_variables(&mut self, address: usize) { // -> ??? + println!("load_local_variables {address}"); + // e.g. here we might call something like + // some kind of check if we already have the info for the current context + // let cached = TODO; + let cached = false; + + if !cached { + // TODO etc + // load debuginfo etc + } + } +} + + // load wasm file + // find out its subprogram and scope for a certain address and somehow + // + // print mapping with expr => relevant location + // x <- 0x000A7321 + // ip; (debuginfo address of some code(?)) + // + // + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/crates/wasmi/Cargo.toml b/crates/wasmi/Cargo.toml index eab447b783..f05d640910 100644 --- a/crates/wasmi/Cargo.toml +++ b/crates/wasmi/Cargo.toml @@ -19,9 +19,16 @@ exclude = [ ] [dependencies] + +# Dwarf utilities +gimli = "0.31.1" +object = { "version" = "0.36.7", "features" = ["elf", "wasm"] } +memmap2 = "0.9.5" + wasmi_core = { workspace = true } wasmi_collections = { workspace = true } wasmi_ir = { workspace = true } +wasmi_tracer = { workspace = true } wasmparser = { workspace = true, features = ["validate", "features"] } wat = { workspace = true, optional = true } spin = { version = "0.9", default-features = false, features = [ diff --git a/crates/wasmi/src/engine/executor/instrs.rs b/crates/wasmi/src/engine/executor/instrs.rs index f9b283191f..e7a448f962 100644 --- a/crates/wasmi/src/engine/executor/instrs.rs +++ b/crates/wasmi/src/engine/executor/instrs.rs @@ -2,6 +2,7 @@ pub use self::call::{dispatch_host_func, ResumableHostError}; use super::{cache::CachedInstance, InstructionPtr, Stack}; +use wasmi_tracer::WasmTracer; use crate::{ core::{hint, wasm, ReadAs, TrapCode, UntypedVal, WriteAs}, engine::{ @@ -91,6 +92,9 @@ struct Executor<'engine> { /// /// [`Engine`]: crate::Engine code_map: &'engine CodeMap, + + // debug: + // TODO tracer: &'engine mut WasmTracer, } impl<'engine> Executor<'engine> { @@ -110,12 +114,14 @@ impl<'engine> Executor<'engine> { // valid for all register indices used by the associated function body. let sp = unsafe { stack.values.stack_ptr_at(frame.base_offset()) }; let ip = frame.instr_ptr(); + Self { sp, ip, cache, stack, code_map, + // TODO tracer: &mut tracer, // probably were not gonna make it } } @@ -124,6 +130,22 @@ impl<'engine> Executor<'engine> { fn execute(mut self, store: &mut Store) -> Result<(), Error> { use Instruction as Instr; loop { + // TODO: change that, just startting from somewehre + // Args: path : Path, instruction address(?) , step? + // TODO: good way to take address + //let address = usize::from(self.ip.get()); + let address = 0x000000010; // should be in the main subprogram >= low_pc + //std::println!("address of {:?}: {:?}", *self.ip.get(), address); + // TODO: make this be a field with correct lifetime + let mut tracer = WasmTracer::new(std::path::Path::new("/home/pesho/code/codetracer-wasmi-recorder/wasm_test.wasm")); // ": for now hardcoded TODO pass"); + tracer.load_local_variables(address); + + // load debuginfo from gimli <- for this ip; + // -> find out current scope and vars in it: + // some kind of mapping between them and locations + // -> load them with some general(for us) mechanism from wasmi's memory(?) + // e.g. load_expression(expr, location, ..) -> ValueRecord + // (to be iterated on, just an idea) match *self.ip.get() { Instr::Trap { trap_code } => self.execute_trap(trap_code)?, Instr::ConsumeFuel { block_fuel } => { diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000..af41ccd1da --- /dev/null +++ b/flake.lock @@ -0,0 +1,100 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1742452566, + "narHash": "sha256-sVuLDQ2UIWfXUBbctzrZrXM2X05YjX08K7XHMztt36E=", + "owner": "nix-community", + "repo": "fenix", + "rev": "7d9ba794daf5e8cc7ee728859bc688d8e26d5f06", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1743550720, + "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1743827369, + "narHash": "sha256-rpqepOZ8Eo1zg+KJeWoq1HAOgoMCDloqv5r2EAa9TSA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "42a1c966be226125b48c384171c44c651c236c22", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1743296961, + "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1742296961, + "narHash": "sha256-gCpvEQOrugHWLimD1wTFOJHagnSEP6VYBDspq96Idu0=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "15d87419f1a123d8f888d608129c3ce3ff8f13d4", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000..2d80bf015e --- /dev/null +++ b/flake.nix @@ -0,0 +1,28 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + inputs@{ flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + systems = [ "x86_64-linux" ]; + perSystem = + { + pkgs, + inputs', + self', + ... + }: + { + + devShells.default = import ./shell.nix { inherit pkgs self' inputs'; }; + + }; + }; +} diff --git a/justfile b/justfile new file mode 100644 index 0000000000..d2287c40f2 --- /dev/null +++ b/justfile @@ -0,0 +1,10 @@ +build-wasm-test-c: + emcc -O0 -g3 -o wasm_test.wasm wasm_test.c + +dwarfdump wasm-path: + # from llvm + llvm-dwarfdump {{wasm-path}} + +dis wasm-path: + # from binaryen + wasm-dis {{wasm-path}} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000000..d145d35563 --- /dev/null +++ b/shell.nix @@ -0,0 +1,40 @@ +{ + pkgs, + self', + inputs', +}: +let + wasm-rust = + with inputs'.fenix.packages; + with latest; + combine [ + cargo + rustc + llvm-tools + targets.wasm32-unknown-emscripten.latest.rust-std + ]; +in +with pkgs; +mkShell { + + hardeningDisable = [ "all" ]; + + packages = [ + + cargo + wasm-rust + emscripten + binaryen + llvm + just + rust-analyzer + + figlet + ]; + + shellHook = '' + export EM_CACHE=/tmp/emcc/ + + figlet "welcome to wasmi recorder" + ''; +} diff --git a/wasm_test.c b/wasm_test.c new file mode 100644 index 0000000000..f9cb952d43 --- /dev/null +++ b/wasm_test.c @@ -0,0 +1,4 @@ +int main() { + int x = 1; + int y = 2; +}