diff --git a/CHANGELOG.md b/CHANGELOG.md index 6010e70..6724694 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.12.0] +### Added +- wasm feature to enable object's wasm parsing support + ## [0.11.0] ### Changed - Start storing names as Arc to reduce string allocations diff --git a/Cargo.toml b/Cargo.toml index 46839a7..7f5bfe9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "llvm_profparser" -version = "0.11.0" +version = "0.12.0" authors = ["xd009642 "] description = "Parsing and interpretation of llvm coverage profiles and generated data" repository = "https://github.com/xd009642/llvm-profparser" @@ -15,6 +15,7 @@ rust-version = "1.80.0" [features] default = ["cli", "__llvm_20"] cli = ["clap", "tracing-subscriber"] +wasm = ["object/wasm"] # for testing # to run all tests, run `cargo test --all-features`. @@ -40,7 +41,7 @@ indexmap = "~1.8" leb128 = "0.2.4" md5 = "0.8" nom = "7.0.0" -object = "0.26.0" +object = "0.39" rustc-hash = "2.1" clap = { version = "4", features = ["derive"], optional = true } thiserror = "1.0.30" diff --git a/src/coverage/coverage_mapping.rs b/src/coverage/coverage_mapping.rs index 00ee991..34f23ab 100644 --- a/src/coverage/coverage_mapping.rs +++ b/src/coverage/coverage_mapping.rs @@ -303,15 +303,15 @@ fn parse_coverage_mapping<'data, R: ReadRef<'data>>( while !data.is_empty() { let data_len = data.len(); // Read the number of affixed function records (now just 0 as not in this header) - debug_assert_eq!(endian.read_i32_bytes(data[0..4].try_into().unwrap()), 0); - let filename_data_len = endian.read_i32_bytes(data[4..8].try_into().unwrap()); + debug_assert_eq!(endian.read_i32(data[0..4].try_into().unwrap()), 0); + let filename_data_len = endian.read_i32(data[4..8].try_into().unwrap()); // Read the length of the affixed string that contains encoded coverage mapping data (now 0 // as not in this header) - debug_assert_eq!(endian.read_i32_bytes(data[8..12].try_into().unwrap()), 0); - let _format_version = endian.read_i32_bytes(data[12..16].try_into().unwrap()); + debug_assert_eq!(endian.read_i32(data[8..12].try_into().unwrap()), 0); + let _format_version = endian.read_i32(data[12..16].try_into().unwrap()); let hash = md5::compute(&data[16..(filename_data_len as usize + 16)]); - let hash = endian.read_u64_bytes(hash.0[..8].try_into().unwrap()); + let hash = endian.read_u64(hash.0[..8].try_into().unwrap()); //let bytes = &data[16..(16 + filename_data_len as usize)]; let bytes = &data[16..]; @@ -351,10 +351,10 @@ fn parse_coverage_functions<'data, R: ReadRef<'data>>( let mut res = vec![]; let section_len = bytes.len(); while !bytes.is_empty() { - let name_hash = endian.read_u64_bytes(bytes[0..8].try_into().unwrap()); - let data_len = endian.read_u32_bytes(bytes[8..12].try_into().unwrap()); - let fn_hash = endian.read_u64_bytes(bytes[12..20].try_into().unwrap()); - let filenames_ref = endian.read_u64_bytes(bytes[20..28].try_into().unwrap()); + let name_hash = endian.read_u64(bytes[0..8].try_into().unwrap()); + let data_len = endian.read_u32(bytes[8..12].try_into().unwrap()); + let fn_hash = endian.read_u64(bytes[12..20].try_into().unwrap()); + let filenames_ref = endian.read_u64(bytes[20..28].try_into().unwrap()); let header = FunctionRecordHeader { name_hash, data_len, @@ -546,14 +546,14 @@ fn parse_profile_data<'data, R: ReadRef<'data>>( let mut res = vec![]; while !bytes.is_empty() { // bytes.len() >= 24 { - let name_md5 = endian.read_u64_bytes(bytes[..8].try_into().unwrap()); - let structural_hash = endian.read_u64_bytes(bytes[8..16].try_into().unwrap()); + let name_md5 = endian.read_u64(bytes[..8].try_into().unwrap()); + let structural_hash = endian.read_u64(bytes[8..16].try_into().unwrap()); - let _counter_ptr = endian.read_u64_bytes(bytes[16..24].try_into().unwrap()); + let _counter_ptr = endian.read_u64(bytes[16..24].try_into().unwrap()); let counters_location = 24 + 16; if bytes.len() <= counters_location { bytes = &bytes[counters_location..]; - let counters_len = endian.read_u32_bytes(bytes[..4].try_into().unwrap()); + let counters_len = endian.read_u32(bytes[..4].try_into().unwrap()); // TODO Might need to get the counter offset and get the list of counters from this? // And potentially check against the maximum number of counters just to make sure that // it's not being exceeded? @@ -590,7 +590,7 @@ fn parse_profile_counters<'data, R: ReadRef<'data>>( if data.len() < (i + 8) { break; } - result.push(endian.read_u64_bytes(data[i..(i + 8)].try_into().unwrap())); + result.push(endian.read_u64(data[i..(i + 8)].try_into().unwrap())); } Ok(result) } else { diff --git a/tests/cov.rs b/tests/cov.rs index 37f44c1..4b4f673 100644 --- a/tests/cov.rs +++ b/tests/cov.rs @@ -212,6 +212,34 @@ fn check_matches() { } } +/// WebAssembly objects store LLVM coverage data in custom sections. The parser should read those +/// sections through the object crate and map the resulting profile counts back to Rust source +/// lines. +#[test] +#[cfg(feature = "wasm")] +fn check_wasm_bindgen_mapping() { + let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/data/cov/wasm-bindgen"); + let instr = parse(dir.join("wasm-tarpaulin-check.profraw")).expect("wasm profraw should parse"); + let object_files = &[dir.join("wasm_tarpaulin_check.wasm")]; + let mapping = CoverageMapping::new(object_files, &instr, false) + .expect("wasm coverage mapping should construct"); + let report = mapping + .generate_report() + .expect("wasm coverage report should generate"); + + let (_, result) = report + .files + .iter() + .find(|(path, _)| path.ends_with("wasm-tarpaulin-check/src/lib.rs")) + .expect("fixture source file should be present in wasm coverage report"); + + assert_eq!(result.hits_for_line(1), Some(1)); + assert_eq!(result.hits_for_line(2), Some(1)); + assert_eq!(result.hits_for_line(5), Some(0)); + assert_eq!(result.hits_for_line(6), Some(0)); + assert_eq!(result.hits_for_line(14), Some(1)); +} + #[test] #[ignore] fn check_stable_vec() { diff --git a/tests/data/cov/wasm-bindgen/wasm-tarpaulin-check.profraw b/tests/data/cov/wasm-bindgen/wasm-tarpaulin-check.profraw new file mode 100644 index 0000000..56c7b6b Binary files /dev/null and b/tests/data/cov/wasm-bindgen/wasm-tarpaulin-check.profraw differ diff --git a/tests/data/cov/wasm-bindgen/wasm_tarpaulin_check.wasm b/tests/data/cov/wasm-bindgen/wasm_tarpaulin_check.wasm new file mode 100644 index 0000000..b363af7 Binary files /dev/null and b/tests/data/cov/wasm-bindgen/wasm_tarpaulin_check.wasm differ