Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<str> to reduce string allocations
Expand Down
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "llvm_profparser"
version = "0.11.0"
version = "0.12.0"
authors = ["xd009642 <danielmckenna93@gmail.com>"]
description = "Parsing and interpretation of llvm coverage profiles and generated data"
repository = "https://github.com/xd009642/llvm-profparser"
Expand All @@ -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`.
Expand All @@ -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"
Expand Down
28 changes: 14 additions & 14 deletions src/coverage/coverage_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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..];
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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?
Expand Down Expand Up @@ -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 {
Expand Down
28 changes: 28 additions & 0 deletions tests/cov.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
Binary file not shown.
Binary file not shown.
Loading