diff --git a/src/agent/coverage/src/binary.rs b/src/agent/coverage/src/binary.rs index 18ca1af8f9..382b71b92a 100644 --- a/src/agent/coverage/src/binary.rs +++ b/src/agent/coverage/src/binary.rs @@ -4,18 +4,19 @@ use std::collections::{BTreeMap, BTreeSet}; use anyhow::{bail, Result}; -use debuggable_module::{block, path::FilePath, Module, Offset}; +use debuggable_module::Module; +pub use debuggable_module::{block, path::FilePath, Offset}; use symbolic::debuginfo::Object; use symbolic::symcache::{SymCache, SymCacheConverter}; use crate::allowlist::TargetAllowList; -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct BinaryCoverage { pub modules: BTreeMap, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct ModuleBinaryCoverage { pub offsets: BTreeMap, } diff --git a/src/agent/onefuzz-file-format/src/coverage/binary/v1.rs b/src/agent/onefuzz-file-format/src/coverage/binary/v1.rs index d1b97d96a1..154a056bcd 100644 --- a/src/agent/onefuzz-file-format/src/coverage/binary/v1.rs +++ b/src/agent/onefuzz-file-format/src/coverage/binary/v1.rs @@ -13,22 +13,29 @@ use crate::hex::Hex; #[derive(Deserialize, Serialize)] pub struct BinaryCoverageJson { #[serde(flatten)] - pub modules: BTreeMap>, + pub modules: BTreeMap, +} + +#[derive(Deserialize, Serialize)] +pub struct ModuleCoverageJson { + pub blocks: BTreeMap, } impl From for BinaryCoverageJson { fn from(binary: BinaryCoverage) -> Self { let mut modules = BTreeMap::new(); - for (module, offsets) in &binary.modules { - let mut map: BTreeMap = BTreeMap::new(); + for (path, offsets) in &binary.modules { + let mut blocks: BTreeMap = BTreeMap::new(); for (offset, count) in offsets.as_ref() { - map.insert(Hex(offset.0), count.0); + blocks.insert(Hex(offset.0), count.0); } - let path = module.as_str().to_owned(); - modules.insert(path, map); + let path = path.as_str().to_owned(); + let module = ModuleCoverageJson { blocks }; + + modules.insert(path, module); } Self { modules } @@ -41,15 +48,15 @@ impl TryFrom for BinaryCoverage { fn try_from(json: BinaryCoverageJson) -> Result { let mut process = BinaryCoverage::default(); - for (module, offsets) in json.modules { + for (path, module) in json.modules { let mut coverage = ModuleBinaryCoverage::default(); - for (hex, count) in offsets { + for (hex, count) in module.blocks { let offset = Offset(hex.0); coverage.offsets.insert(offset, Count(count)); } - let path = FilePath::new(module)?; + let path = FilePath::new(path)?; process.modules.insert(path, coverage); } diff --git a/src/agent/onefuzz-file-format/tests/files/binary-coverage.v0.json b/src/agent/onefuzz-file-format/tests/files/binary-coverage.v0.json new file mode 100644 index 0000000000..32ce281152 --- /dev/null +++ b/src/agent/onefuzz-file-format/tests/files/binary-coverage.v0.json @@ -0,0 +1,32 @@ +[ + { + "module": "/setup/main.exe", + "blocks": [ + { + "offset": 1, + "count": 0 + }, + { + "offset": 300, + "count": 1 + }, + { + "offset": 5000, + "count": 0 + } + ] + }, + { + "module": "/setup/lib/some.dll", + "blocks": [ + { + "offset": 123, + "count": 0 + }, + { + "offset": 456, + "count": 10 + } + ] + } +] diff --git a/src/agent/onefuzz-file-format/tests/files/binary-coverage.v1.json b/src/agent/onefuzz-file-format/tests/files/binary-coverage.v1.json new file mode 100644 index 0000000000..6ee25e581b --- /dev/null +++ b/src/agent/onefuzz-file-format/tests/files/binary-coverage.v1.json @@ -0,0 +1,15 @@ +{ + "/setup/main.exe": { + "blocks": { + "1": 0, + "12c": 1, + "1388": 0 + } + }, + "/setup/lib/some.dll": { + "blocks": { + "7b": 0, + "1c8": 10 + } + } +} diff --git a/src/agent/onefuzz-file-format/tests/integration.rs b/src/agent/onefuzz-file-format/tests/integration.rs new file mode 100644 index 0000000000..6e718fc6e8 --- /dev/null +++ b/src/agent/onefuzz-file-format/tests/integration.rs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use pretty_assertions::assert_eq; + +use anyhow::Result; +use coverage::binary::{BinaryCoverage, Count, FilePath, ModuleBinaryCoverage, Offset}; +use onefuzz_file_format::coverage::binary::{v0, v1}; + +fn expected_binary_coverage() -> Result { + let main_exe_path = FilePath::new("/setup/main.exe")?; + let some_dll_path = FilePath::new("/setup/lib/some.dll")?; + + let mut main_exe = ModuleBinaryCoverage::default(); + main_exe.offsets.insert(Offset(1), Count(0)); + main_exe.offsets.insert(Offset(300), Count(1)); + main_exe.offsets.insert(Offset(5000), Count(0)); + + let mut some_dll = ModuleBinaryCoverage::default(); + some_dll.offsets.insert(Offset(123), Count(0)); + some_dll.offsets.insert(Offset(456), Count(10)); + + let mut binary = BinaryCoverage::default(); + binary.modules.insert(some_dll_path, some_dll); + binary.modules.insert(main_exe_path, main_exe); + + Ok(binary) +} + +#[test] +fn test_binary_coverage_formats() -> Result<()> { + let expected = expected_binary_coverage()?; + + let v0_text = include_str!("files/binary-coverage.v0.json"); + let v0_json: v0::BinaryCoverageJson = serde_json::from_str(v0_text)?; + let from_v0 = BinaryCoverage::try_from(v0_json)?; + assert_eq!(from_v0, expected); + + let v1_text = include_str!("files/binary-coverage.v1.json"); + let v1_json: v1::BinaryCoverageJson = serde_json::from_str(v1_text)?; + let from_v1 = BinaryCoverage::try_from(v1_json)?; + assert_eq!(from_v1, expected); + + Ok(()) +}