From d237c0302ff80e93dcd6201a07c30b4ff81261c7 Mon Sep 17 00:00:00 2001 From: Jan Civlin <30603832+jcivlin@users.noreply.github.com> Date: Wed, 25 Oct 2023 06:51:33 -0700 Subject: [PATCH] Add Dwarf test (#390) --- .gitignore | 1 + Cargo.lock | 1 + .../move-to-solana/src/stackless/dwarf.rs | 158 +++++++++--------- .../tools/move-mv-llvm-compiler/Cargo.toml | 5 + .../move-mv-llvm-compiler/docs/Development.md | 18 ++ .../tests/dwarf-tests.rs | 127 ++++++++++++++ .../0x1__signer.ll.debug_info.expected | 7 + .../0xcafe__BasicCoin.ll.debug_info.expected | 7 + .../tests/dwarf-tests/basic-coin.move | 100 +++++++++++ .../tests/dwarf-tests/basic-coin/Move.toml | 10 ++ .../tests/test_common.rs | 98 ++++++++++- 11 files changed, 453 insertions(+), 79 deletions(-) create mode 100644 language/tools/move-mv-llvm-compiler/tests/dwarf-tests.rs create mode 100644 language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin-build/0x1__signer.ll.debug_info.expected create mode 100644 language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin-build/0xcafe__BasicCoin.ll.debug_info.expected create mode 100644 language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin.move create mode 100644 language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin/Move.toml diff --git a/.gitignore b/.gitignore index 58e0603d46..5e3f484ac9 100644 --- a/.gitignore +++ b/.gitignore @@ -65,6 +65,7 @@ target-out-docker **/move-mv-llvm-compiler/**/cli-tests/**/*.ll.actual **/move-mv-llvm-compiler/**/cli-tests/basic-coin-build/absolute_path_results/* **/move-mv-llvm-compiler/**/cli-tests/basic-coin-build/relative_path_results/* +**/move-mv-llvm-compiler/**/dwarf-tests/basic-coin-build/stored_results/* **/move-mv-llvm-compiler/**/rbpf-tests/**/*.mv **/move-mv-llvm-compiler/**/rbpf-tests/**/*.o **/move-mv-llvm-compiler/**/rbpf-tests/**/*.so diff --git a/Cargo.lock b/Cargo.lock index 84f7f47d22..3256c0ee27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4385,6 +4385,7 @@ dependencies = [ "num-traits 0.2.16", "once_cell", "parking_lot 0.11.1", + "rand 0.8.5", "regex", "semver 1.0.17", "serde 1.0.188", diff --git a/language/solana/move-to-solana/src/stackless/dwarf.rs b/language/solana/move-to-solana/src/stackless/dwarf.rs index c3dfb084e0..148a21d55a 100644 --- a/language/solana/move-to-solana/src/stackless/dwarf.rs +++ b/language/solana/move-to-solana/src/stackless/dwarf.rs @@ -16,27 +16,17 @@ use llvm_sys::{ core::*, debuginfo::{ - LLVMDIBuilderCreateCompileUnit, LLVMDIBuilderCreateModule, LLVMDIBuilderFinalize, - LLVMDWARFEmissionKind, LLVMDWARFSourceLanguage::LLVMDWARFSourceLanguageRust, + LLVMCreateDIBuilder, LLVMDIBuilderCreateCompileUnit, LLVMDIBuilderCreateFile, + LLVMDIBuilderCreateModule, LLVMDIBuilderFinalize, LLVMDWARFEmissionKind, + LLVMDWARFSourceLanguage::LLVMDWARFSourceLanguageRust, }, prelude::*, }; -use std::{ - ffi::{CStr, CString}, - ptr, -}; +use std::{env, ffi::CStr, ptr}; use crate::stackless::Module; -pub use llvm_sys::{ - debuginfo::{LLVMCreateDIBuilder, LLVMDIBuilderCreateFile, LLVMDisposeDIBuilder}, - LLVMAttributeFunctionIndex, LLVMAttributeIndex, LLVMAttributeReturnIndex, LLVMIntPredicate, - LLVMLinkage, - LLVMLinkage::LLVMInternalLinkage, - LLVMTypeKind::LLVMIntegerTypeKind, -}; - #[derive(Clone, Debug)] pub struct DIBuilderCore { module_di: LLVMModuleRef, @@ -52,41 +42,14 @@ pub struct DIBuilderCore { #[derive(Clone, Debug)] pub struct DIBuilder(Option); -/// Convert &str to a CString -fn str_to_c_params(s: &str) -> (*const ::libc::c_char, ::libc::size_t) { - (s.as_ptr() as *const libc::c_char, s.len()) -} - -/// Convert the Rust String to a CString (null-terminated C-style string) -fn string_to_c_params(s: String) -> (*const ::libc::c_char, ::libc::size_t) { - let cstr = match CString::new(s) { - Ok(cstr) => cstr, - Err(_) => CString::new("").expect("Failed to create an empty CString"), - }; - (cstr.as_ptr(), cstr.as_bytes().len()) -} -fn path_to_c_params( - file_path: &str, -) -> ( - *const ::libc::c_char, - ::libc::size_t, - *const ::libc::c_char, - ::libc::size_t, -) { - let path = std::path::Path::new(&file_path); - let directory = path - .parent() - .expect("Failed to get directory") - .to_str() - .expect("Failed to convert to string"); - let (dir_ptr, dir_len) = str_to_c_params(directory); - let file = path - .file_name() - .expect("Failed to get file name") - .to_str() - .expect("Failed to convert to string"); - let (filename_ptr, filename_len) = str_to_c_params(file); - (filename_ptr, filename_len, dir_ptr, dir_len) +macro_rules! to_cstring { + ($x:expr) => {{ + let cstr = match std::ffi::CString::new($x) { + Ok(cstr) => cstr, + Err(_) => std::ffi::CString::new("unknown").expect("Failed to create CString"), + }; + cstr + }}; } pub fn from_raw_slice_to_string(raw_ptr: *const i8, raw_len: ::libc::size_t) -> String { @@ -96,6 +59,16 @@ pub fn from_raw_slice_to_string(raw_ptr: *const i8, raw_len: ::libc::size_t) -> String::from_utf8_lossy(byte_slice).to_string() } +fn relative_to_absolute(relative_path: &str) -> std::io::Result { + let current_dir = env::current_dir()?; + let absolute_path = current_dir + .join(relative_path) + .canonicalize() + .expect("Cannot canonicanize path"); + + Ok(absolute_path.to_string_lossy().to_string()) +} + impl DIBuilder { pub fn new(module: &mut Module, source: &str, debug: bool) -> DIBuilder { use log::debug; @@ -103,37 +76,71 @@ impl DIBuilder { let module_ref_name = module.get_module_id(); let module_ref = module.as_mut(); - // create module - let module_name = format!("{}.dbg_info", module_ref_name); - let (mod_nm_ptr, _mod_nm_len) = str_to_c_params(module_name.as_str()); - let module_di = unsafe { LLVMModuleCreateWithName(mod_nm_ptr) }; + // create new module + let module_name = module_ref_name + ".dbg_info"; + let cstr = to_cstring!(module_name.as_str()); + let (mut mod_nm_ptr, mut mod_nm_len) = (cstr.as_ptr(), cstr.as_bytes().len()); + let module_di = + unsafe { LLVMModuleCreateWithName(mod_nm_ptr as *const ::libc::c_char) }; + + // check dbg module name + mod_nm_ptr = unsafe { LLVMGetModuleIdentifier(module_di, &mut mod_nm_len) }; + let module_di_name = &from_raw_slice_to_string(mod_nm_ptr, mod_nm_len); + debug!(target: "dwarf", "Created dbg module {:#?}", module_di_name); - // set source to created module - let (src_ptr, src_len) = str_to_c_params(source); - unsafe { LLVMSetSourceFileName(module_di, src_ptr, src_len) }; - // check the name + let source = relative_to_absolute(source).expect("Must be the legal path"); + let cstr = to_cstring!(source.as_str()); + unsafe { LLVMSetSourceFileName(module_di, cstr.as_ptr(), cstr.as_bytes().len()) }; + + // check the source name let mut src_len: ::libc::size_t = 0; let src_ptr = unsafe { LLVMGetSourceFileName(module_di, &mut src_len) }; - let src0 = from_raw_slice_to_string(src_ptr, src_len); - debug!(target: "dwarf", "Module {:#?} has source {:#?}", module_name, src0); + let module_src = &from_raw_slice_to_string(src_ptr, src_len); + debug!(target: "dwarf", "Module {:#?} has source {:#?}", module_name, module_src); // create builder let builder_ref = unsafe { LLVMCreateDIBuilder(module_di) }; - // create builder file - let (mod_nm_ptr, mod_nm_len, dir_ptr, dir_len) = path_to_c_params(source); + // create file + let path = std::path::Path::new(&source); + let directory = path + .parent() + .expect("Failed to get directory") + .to_str() + .expect("Failed to convert to string"); + let cstr = to_cstring!(directory); + let (dir_ptr, dir_len) = (cstr.as_ptr(), cstr.as_bytes().len()); + + let file = path + .file_name() + .expect("Failed to get file name") + .to_str() + .expect("Failed to convert to string"); + let cstr = to_cstring!(file); + let (filename_ptr, filename_len) = (cstr.as_ptr(), cstr.as_bytes().len()); + let (mod_nm_ptr, mod_nm_len, dir_ptr, dir_len) = + (filename_ptr, filename_len, dir_ptr, dir_len); + let builder_file = unsafe { LLVMDIBuilderCreateFile(builder_ref, mod_nm_ptr, mod_nm_len, dir_ptr, dir_len) }; + // create compile unit let producer = "move-mv-llvm-compiler".to_string(); - let (producer_ptr, producer_len) = str_to_c_params(producer.as_str()); + let cstr = to_cstring!(producer); + let (producer_ptr, producer_len) = (cstr.as_ptr(), cstr.as_bytes().len()); + let flags = "".to_string(); - let (flags_ptr, flags_len) = str_to_c_params(flags.as_str()); + let cstr = to_cstring!(flags); + let (flags_ptr, flags_len) = (cstr.as_ptr(), cstr.as_bytes().len()); + let slash = "/".to_string(); - let (slash_ptr, slash_len) = str_to_c_params(slash.as_str()); + let cstr = to_cstring!(slash); + let (slash_ptr, slash_len) = (cstr.as_ptr(), cstr.as_bytes().len()); + let none = String::new(); - let (none_ptr, none_len) = str_to_c_params(none.as_str()); + let cstr = to_cstring!(none); + let (none_ptr, none_len) = (cstr.as_ptr(), cstr.as_bytes().len()); let compiled_unit = unsafe { LLVMDIBuilderCreateCompileUnit( @@ -159,19 +166,15 @@ impl DIBuilder { ) }; - // check the name - let mut src_len: ::libc::size_t = 0; - let src_ptr = unsafe { LLVMGetSourceFileName(module_di, &mut src_len) }; - let src1 = from_raw_slice_to_string(src_ptr, src_len); - debug!(target: "dwarf", "Self-check: module {:#?} has source {:#?}", module_name, src1); - - // create compiled unit + // create di module let parent_scope = compiled_unit; let name = module_name; - let (name_ptr, name_len) = str_to_c_params(name.as_str()); - let (config_macros_ptr, config_macros_len) = str_to_c_params(none.as_str()); - let (include_path_ptr, include_path_len) = str_to_c_params(none.as_str()); - let (api_notes_file_ptr, api_notes_file_len) = str_to_c_params(none.as_str()); + let cstr = to_cstring!(name); + let (name_ptr, name_len) = (cstr.as_ptr(), cstr.as_bytes().len()); + + let (config_macros_ptr, config_macros_len) = (none_ptr, none_len); + let (include_path_ptr, include_path_len) = (none_ptr, none_len); + let (api_notes_file_ptr, api_notes_file_len) = (none_ptr, none_len); let compiled_module = unsafe { LLVMDIBuilderCreateModule( builder_ref, @@ -235,7 +238,8 @@ impl DIBuilder { pub fn print_module_to_file(&self, file_path: String) { if let Some(x) = &self.0 { let mut err_string = ptr::null_mut(); - let (filename_ptr, _filename_ptr_len) = string_to_c_params(file_path); + let cstr = to_cstring!(file_path); + let (filename_ptr, _filename_ptr_len) = (cstr.as_ptr(), cstr.as_bytes().len()); unsafe { let res = LLVMPrintModuleToFile(x.module_di, filename_ptr, &mut err_string); if res != 0 { diff --git a/language/tools/move-mv-llvm-compiler/Cargo.toml b/language/tools/move-mv-llvm-compiler/Cargo.toml index f0c29cf108..823b435ab1 100644 --- a/language/tools/move-mv-llvm-compiler/Cargo.toml +++ b/language/tools/move-mv-llvm-compiler/Cargo.toml @@ -18,6 +18,7 @@ once_cell = "1.10" parking_lot = "0.11" toml = "0.5.8" regex = "1.1.9" +rand = "0.8" move-bytecode-verifier = { path = "../../move-bytecode-verifier" } move-bytecode-source-map = { path = "../../move-ir-compiler/move-bytecode-source-map" } move-command-line-common = { path = "../../move-command-line-common" } @@ -87,3 +88,7 @@ harness = false [[test]] name = "cli-tests" harness = false + +[[test]] +name = "dwarf-tests" +harness = false diff --git a/language/tools/move-mv-llvm-compiler/docs/Development.md b/language/tools/move-mv-llvm-compiler/docs/Development.md index 0d8523698f..5cffd03bd3 100644 --- a/language/tools/move-mv-llvm-compiler/docs/Development.md +++ b/language/tools/move-mv-llvm-compiler/docs/Development.md @@ -193,6 +193,24 @@ Install [CodeLLDB plugin](https://marketplace.visualstudio.com/items?itemName=va - lldb with gdbserver +To debug in VS Code add this config: + { + "type": "lldb", + "request": "launch", + "name": "dwarf-tests direct call", + "env": { + "RUST_BACKTRACE": "all", + "RUST_LOG": "debug", + "CARGO_MANIFEST_DIR": "something like /home/sol/work/git/move/language/tools/move-mv-llvm-compiler", + "LLVM_SYS_150_PREFIX": "something like /home/sol/work/git/platform-tools/out/rust/build/x86_64-unknown-linux-gnu/llvm", + "PLATFORM_TOOLS_ROOT": "something like /home/sol/work/git/platform-tools/out/deploy" + }, + "program": "something like /home/sol/work/git/move/target/debug/deps/dwarf_tests-XXXXXXXXXXXXXXXX", + "args": ["--test"], + "cwd": "something like /home/sol/work/git/move/language/tools/move-mv-llvm-compiler", + "stopOnEntry": false + } + ### Protip ---- diff --git a/language/tools/move-mv-llvm-compiler/tests/dwarf-tests.rs b/language/tools/move-mv-llvm-compiler/tests/dwarf-tests.rs new file mode 100644 index 0000000000..1691b13ab9 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/dwarf-tests.rs @@ -0,0 +1,127 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// SPDX-License-Identifier: Apache-2.0 + +//! Tests of compilation generated debug information. Only *.dbg_info files are checked. +//! +//! # Usage +//! +//! These tests require `move-compiler` to be pre-built: +//! +//! ``` +//! cargo build -p move-compiler +//! ``` +//! +//! Running the tests: +//! +//! ``` +//! cargo test -p move-mv-llvm-compiler --test dwarf-tests +//! ``` +//! +//! Running a specific test: +//! +//! ``` +//! cargo test -p move-mv-llvm-compiler --test dwarf-tests -- basic-coin.move +//! ``` +//! +//! # Details +//! +//! They do the following: +//! +//! - Create a test for every .move file in dwarf-tests/, for example for test basic-coin.move +//! directort basic-coin-build is created. +//! - Run `move-mv-llvm-compiler` with -g option. This will create *.dbg_info files. +//! - Compare the dbg_info.actual files with dbg_info.expected files. + +use std::{env, path::Path}; + +mod test_common; +use anyhow::bail; +use test_common as tc; + +pub const TEST_DIR: &str = "tests/dwarf-tests"; + +datatest_stable::harness!(run_test, TEST_DIR, r".*\.move$"); + +fn run_test(test_path: &Path) -> Result<(), Box> { + tc::setup_logging_for_test(); + Ok(run_test_inner(test_path)?) +} + +fn run_test_inner(test_path: &Path) -> anyhow::Result<()> { + let harness_paths = tc::get_harness_paths("move-compiler")?; + let test_plan = tc::get_test_plan(test_path)?; + + if test_plan.should_ignore() { + eprintln!("ignoring {}", test_plan.name); + return Ok(()); + } + + let current_dir = env::current_dir() + .or_else(|err| bail!("Cannot get currecnt directory. Got error: {}", err)) + .unwrap(); + + let test_name = &test_plan.name; + + let toml_dir: String; + if let Some(pos) = test_name.rfind('.') { + toml_dir = test_name[..pos].to_string(); + } else { + bail!("No extension found in the filename {}", test_name); + } + + let p_absolute_path = current_dir.join(toml_dir).to_str().unwrap().to_owned(); + + let src = &test_plan.build_dir; + let dst = &src.join("stored_results"); + + tc::clean_results(src)?; + std::fs::remove_dir_all(dst).ok(); + + tc::run_move_to_llvm_build( + &harness_paths, + &test_plan, + vec![&"-p".to_string(), &p_absolute_path, &"-g".to_string()], + )?; + + // remove .actual files; this will not remove.dbg_info files + tc::clean_results(src)?; + + let spot = current_dir + .ancestors() + .nth(3) + .expect("Cannot go up in directory") + .to_str() + .unwrap(); + + // dbg_info files contain lines with absolute path, since it is host dependent, remove prefix in all paths. + match tc::list_files_with_extension(src, "debug_info") { + Ok(files) => { + for file in files { + tc::filter_file(&file, spot, |line: &str, spot: &str| line.replace(spot, ""))?; + } + } + Err(_err) => {} + } + + rename_dwarf_files(&test_plan); // will rename *.actual.dbg_info to *.dbg_info.actual + tc::compare_results(&test_plan)?; + + tc::store_results(src, dst)?; + tc::clean_results(src)?; + + Ok(()) +} + +fn rename_dwarf_files(test_plan: &tc::TestPlan) { + let build_dir = &test_plan.build_dir; + + match tc::list_files_with_extension(build_dir, "debug_info") { + Ok(files) => { + for file in files { + tc::switch_last_two_extensions_and_rename(&file); + } + } + Err(_err) => {} + } +} diff --git a/language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin-build/0x1__signer.ll.debug_info.expected b/language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin-build/0x1__signer.ll.debug_info.expected new file mode 100644 index 0000000000..5448c60975 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin-build/0x1__signer.ll.debug_info.expected @@ -0,0 +1,7 @@ +; ModuleID = '0x1__signer.dbg_info' +source_filename = "/language/move-stdlib/sources/signer.move" + +!llvm.dbg.cu = !{!0} + +!0 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !1, producer: "move-mv-llvm-compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, sysroot: "/") +!1 = !DIFile(filename: "signer.move", directory: "/language/move-stdlib/sources") diff --git a/language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin-build/0xcafe__BasicCoin.ll.debug_info.expected b/language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin-build/0xcafe__BasicCoin.ll.debug_info.expected new file mode 100644 index 0000000000..13c456231d --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin-build/0xcafe__BasicCoin.ll.debug_info.expected @@ -0,0 +1,7 @@ +; ModuleID = '0xcafe__BasicCoin.dbg_info' +source_filename = "/language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin.move" + +!llvm.dbg.cu = !{!0} + +!0 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !1, producer: "move-mv-llvm-compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, sysroot: "/") +!1 = !DIFile(filename: "basic-coin.move", directory: "/language/tools/move-mv-llvm-compiler/tests/dwarf-tests") diff --git a/language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin.move b/language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin.move new file mode 100644 index 0000000000..d936ca43f3 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin.move @@ -0,0 +1,100 @@ +/// This module defines a minimal and generic Coin and Balance. +module NamedAddr::BasicCoin { + use std::signer; + + /// Error codes + const ENOT_MODULE_OWNER: u64 = 0; + const EINSUFFICIENT_BALANCE: u64 = 1; + const EALREADY_HAS_BALANCE: u64 = 2; + const EEQUAL_ADDR: u64 = 4; + + struct Coin has store { + value: u64 + } + + struct Balance has key { + coin: Coin + } + + /// Publish an empty balance resource under `account`'s address. This function must be called before + /// minting or transferring to the account. + public fun publish_balance(account: &signer) { + let empty_coin = Coin { value: 0 }; + assert!(!exists>(signer::address_of(account)), EALREADY_HAS_BALANCE); + move_to(account, Balance { coin: empty_coin }); + } + + /// Mint `amount` tokens to `mint_addr`. This method requires a witness with `CoinType` so that the + /// module that owns `CoinType` can decide the minting policy. + public fun mint(mint_addr: address, amount: u64, _witness: CoinType) acquires Balance { + // Deposit `total_value` amount of tokens to mint_addr's balance + deposit(mint_addr, Coin { value: amount }); + } + + public fun balance_of(owner: address): u64 acquires Balance { + borrow_global>(owner).coin.value + } + + spec balance_of { + pragma aborts_if_is_strict; + aborts_if !exists>(owner); + } + + /// Transfers `amount` of tokens from `from` to `to`. This method requires a witness with `CoinType` so that the + /// module that owns `CoinType` can decide the transferring policy. + public fun transfer(from: &signer, to: address, amount: u64, _witness: CoinType) acquires Balance { + let from_addr = signer::address_of(from); + assert!(from_addr != to, EEQUAL_ADDR); + let check = withdraw(from_addr, amount); + deposit(to, check); + } + + spec transfer { + let addr_from = signer::address_of(from); + + let balance_from = global>(addr_from).coin.value; + let balance_to = global>(to).coin.value; + let post balance_from_post = global>(addr_from).coin.value; + let post balance_to_post = global>(to).coin.value; + + ensures balance_from_post == balance_from - amount; + ensures balance_to_post == balance_to + amount; + } + + fun withdraw(addr: address, amount: u64) : Coin acquires Balance { + let balance = balance_of(addr); + assert!(balance >= amount, EINSUFFICIENT_BALANCE); + let balance_ref = &mut borrow_global_mut>(addr).coin.value; + *balance_ref = balance - amount; + Coin { value: amount } + } + + spec withdraw { + let balance = global>(addr).coin.value; + + aborts_if !exists>(addr); + aborts_if balance < amount; + + let post balance_post = global>(addr).coin.value; + ensures result == Coin { value: amount }; + ensures balance_post == balance - amount; + } + + fun deposit(addr: address, check: Coin) acquires Balance{ + let balance = balance_of(addr); + let balance_ref = &mut borrow_global_mut>(addr).coin.value; + let Coin { value } = check; + *balance_ref = balance + value; + } + + spec deposit { + let balance = global>(addr).coin.value; + let check_value = check.value; + + aborts_if !exists>(addr); + aborts_if balance + check_value > MAX_U64; + + let post balance_post = global>(addr).coin.value; + ensures balance_post == balance + check_value; + } +} diff --git a/language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin/Move.toml b/language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin/Move.toml new file mode 100644 index 0000000000..1756e2f035 --- /dev/null +++ b/language/tools/move-mv-llvm-compiler/tests/dwarf-tests/basic-coin/Move.toml @@ -0,0 +1,10 @@ +[package] +name = "BasicCoin" +version = "0.0.0" + +[addresses] +NamedAddr = "0xCAFE" +std = "0x1" + +[dependencies] +MoveStdlib = { local = "../../../../../move-stdlib/" } diff --git a/language/tools/move-mv-llvm-compiler/tests/test_common.rs b/language/tools/move-mv-llvm-compiler/tests/test_common.rs index 60b70b8408..c3c0b66add 100644 --- a/language/tools/move-mv-llvm-compiler/tests/test_common.rs +++ b/language/tools/move-mv-llvm-compiler/tests/test_common.rs @@ -3,12 +3,15 @@ // SPDX-License-Identifier: Apache-2.0 #![allow(dead_code)] use anyhow::Context; +use core::result::Result::Ok; use log::debug; +use rand::Rng; use serde::{Deserialize, Serialize}; use serde_json::Result; use std::{ ffi::OsStr, - fs, + fs::{self, File}, + io::{BufRead, BufReader, BufWriter, Write}, path::{Path, PathBuf}, process::Command, }; @@ -396,7 +399,6 @@ pub fn run_move_build_full( Ok(()) } -// use std::io::{self, Write}; pub fn run_move_to_llvm_build( harness_paths: &HarnessPaths, test_plan: &TestPlan, @@ -774,6 +776,7 @@ pub fn store_results>(source: P, destination: P) -> std::io::Resu Ok(()) } +// Removes files with extension "actual" pub fn clean_results>(source: P) -> std::io::Result<()> { for entry in fs::read_dir(source)? { let entry = entry?; @@ -790,3 +793,94 @@ pub fn clean_results>(source: P) -> std::io::Result<()> { Ok(()) } + +pub fn switch_last_two_extensions_and_rename(path: &Path) { + if let Some(filename) = path.file_name() { + if let Some(filename_str) = filename.to_str() { + let mut parts: Vec<&str> = filename_str.split('.').collect(); + + if parts.len() >= 3 { + // Swap the last two extensions + let last_ext = parts.pop().unwrap(); + let second_last_ext = parts.pop().unwrap(); + parts.push(last_ext); + parts.push(second_last_ext); + + let new_filename = parts.join("."); + + let mut new_path = path.to_path_buf(); + new_path.set_file_name(new_filename); + + fs::rename(path, new_path).expect("Error in renameing"); + } + } + } +} + +use std::ffi::OsString; +pub fn list_files_with_extension( + directory_path: &Path, + extension: &str, +) -> std::io::Result> { + let dir = fs::read_dir(directory_path)?; + let dummy = OsString::from(extension.to_owned() + "dummy"); + let dummy_ext = dummy.as_os_str(); + + let file_names: Vec = dir + .filter_map(anyhow::Result::ok) + .filter(|f| extension == f.path().extension().unwrap_or(dummy_ext)) + .map(|f| f.path()) + .collect(); + + Ok(file_names) +} + +pub fn filter_file(file_path: &PathBuf, filter_key: &str, mut filter: F) -> std::io::Result<()> +where + F: FnMut(&str, &str) -> String, +{ + // Open the original file for reading + let input_file = File::open(file_path)?; + let input_file_reader = BufReader::new(input_file); + + // Create a temporary file for writing the modified content + let random_extension = generate_random_string(); + let tmp_file_path = file_path.with_extension(random_extension); + let tmp_file = File::create(&tmp_file_path)?; + let mut tmp_file_writer = BufWriter::new(tmp_file); + + for line in input_file_reader.lines() { + let original_line = line?; + let modified_line = filter(&original_line, filter_key); + + // Write the modified line to the temporary file + writeln!(tmp_file_writer, "{}", modified_line)?; + } + + // Replace the original file with the temporary file + std::fs::rename(&tmp_file_path, file_path)?; + + Ok(()) +} + +fn generate_random_number() -> u32 { + let mut rng = rand::thread_rng(); + rng.gen::() +} + +fn generate_random_string() -> String { + let rand = generate_random_number(); + let charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + let charset_length = charset.len(); + let length = rand as usize % charset_length; + + let mut rng = rand::thread_rng(); + let random_string: String = (0..length) + .map(|_| { + let index = rng.gen_range(0..charset_length); + charset.chars().nth(index).unwrap() + }) + .collect(); + + random_string +}