Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.
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
13 changes: 13 additions & 0 deletions src/agent/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
"onefuzz",
"onefuzz-task",
"onefuzz-agent",
"onefuzz-file-format",
"onefuzz-telemetry",
"reqwest-retry",
"srcview",
Expand Down
16 changes: 16 additions & 0 deletions src/agent/onefuzz-file-format/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "onefuzz-file-format"
version = "0.1.0"
edition = "2021"
license = "MIT"

[dependencies]
anyhow = "1.0"
coverage = { path = "../coverage" }
debuggable-module = { path = "../debuggable-module" }
quick-xml = "0.26.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }

[dev-dependencies]
pretty_assertions = "1.3"
44 changes: 44 additions & 0 deletions src/agent/onefuzz-file-format/examples/cobertura.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use anyhow::Result;

use coverage::source::{Count, FileCoverage, Line, SourceCoverage};
use debuggable_module::path::FilePath;
use onefuzz_file_format::coverage::cobertura::CoberturaCoverage;

fn main() -> Result<()> {
let modoff = vec![
(r"/missing/lib.c", vec![1, 2, 3, 5, 8]),
(
r"test-data/fuzz.c",
vec![
7, 8, 10, 13, 16, 17, 21, 22, 23, 27, 28, 29, 30, 32, 33, 37, 39, 42, 44,
],
),
(r"test-data\fuzz.h", vec![3, 4, 5]),
(r"test-data\lib\explode.h", vec![1, 2, 3]),
];

let mut coverage = SourceCoverage::default();

for (path, lines) in modoff {
let file_path = FilePath::new(path)?;

let mut file = FileCoverage::default();

for line in lines {
let count = if line % 3 == 0 { 1 } else { 0 };
file.lines.insert(Line::new(line)?, Count(count));
}

coverage.files.insert(file_path, file);
}

let cobertura = CoberturaCoverage::from(coverage);

let text = cobertura.to_string()?;
println!("{text}");

Ok(())
}
6 changes: 6 additions & 0 deletions src/agent/onefuzz-file-format/src/coverage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

pub mod binary;
pub mod cobertura;
pub mod source;
31 changes: 31 additions & 0 deletions src/agent/onefuzz-file-format/src/coverage/binary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use anyhow::Result;
use coverage::binary::BinaryCoverage;

pub mod v0;
pub mod v1;

#[derive(Serialize, Deserialize)]
#[serde(tag = "version")]
pub enum BinaryCoverageJson {
#[serde(rename = "0")]
V0(v0::BinaryCoverageJson),

#[serde(rename = "1")]
V1(v1::BinaryCoverageJson),
}

impl TryFrom<BinaryCoverageJson> for BinaryCoverage {
type Error = anyhow::Error;

fn try_from(json: BinaryCoverageJson) -> Result<Self> {
use BinaryCoverageJson::*;

match json {
V0(v0) => v0.try_into(),
V1(v1) => v1.try_into(),
}
}
}
80 changes: 80 additions & 0 deletions src/agent/onefuzz-file-format/src/coverage/binary/v0.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use anyhow::Result;
use coverage::binary::{BinaryCoverage, Count, ModuleBinaryCoverage};
use debuggable_module::path::FilePath;
use debuggable_module::Offset;
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize)]
#[serde(transparent)]
pub struct BinaryCoverageJson {
#[serde(flatten)]
pub modules: Vec<BinaryModuleCoverageJson>,
}

#[derive(Deserialize, Serialize)]
pub struct BinaryModuleCoverageJson {
pub module: String,
pub blocks: Vec<BinaryBlockCoverageJson>,
}

#[derive(Deserialize, Serialize)]
pub struct BinaryBlockCoverageJson {
pub offset: u32,
pub count: u32,
}

impl TryFrom<BinaryCoverage> for BinaryCoverageJson {
type Error = anyhow::Error;

fn try_from(binary: BinaryCoverage) -> Result<Self> {
let mut modules = Vec::new();

for (module, offsets) in binary.modules {
let mut blocks = Vec::new();

for (offset, count) in offsets.as_ref() {
let offset = u32::try_from(offset.0)?;
let count = count.0;
let block = BinaryBlockCoverageJson { offset, count };
blocks.push(block);
}

let module = module.as_str().to_owned();
let module = BinaryModuleCoverageJson { module, blocks };

modules.push(module);
}

Ok(Self { modules })
}
}

impl TryFrom<BinaryCoverageJson> for BinaryCoverage {
type Error = anyhow::Error;

fn try_from(json: BinaryCoverageJson) -> Result<Self> {
let mut process = BinaryCoverage::default();

for coverage_json in json.modules {
let mut coverage = ModuleBinaryCoverage::default();

for block in coverage_json.blocks {
let offset = Offset(u64::from(block.offset));
let count = Count(block.count);
coverage.offsets.insert(offset, count);
}

let path = FilePath::new(coverage_json.module)?;

process.modules.insert(path, coverage);
}

Ok(process)
}
}

#[cfg(test)]
mod tests;
73 changes: 73 additions & 0 deletions src/agent/onefuzz-file-format/src/coverage/binary/v0/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use anyhow::Result;
use pretty_assertions::assert_eq;
use serde_json::json;

use super::*;

const MAIN_EXE: &str = "/setup/main.exe";
const SOME_DLL: &str = "/setup/lib/some.dll";

const EXPECTED: &str = r#"
[
{
"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
}
]
}
]
"#;

#[test]
fn test_serialize_deseralize() -> Result<()> {
let value = json!([
{
"module": MAIN_EXE,
"blocks": [
{ "offset": 1, "count": 0 },
{ "offset": 300, "count": 1 },
{ "offset": 5000, "count": 0 },
],
},
{
"module": SOME_DLL,
"blocks": [
{ "offset": 123, "count": 0 },
{ "offset": 456, "count": 10 },
],
},
]);
let coverage: BinaryCoverageJson = serde_json::from_value(value)?;

let text = serde_json::to_string_pretty(&coverage)?;
assert_eq!(text.trim(), EXPECTED.trim());

Ok(())
}
58 changes: 58 additions & 0 deletions src/agent/onefuzz-file-format/src/coverage/binary/v1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use std::collections::BTreeMap;

use anyhow::Result;
use coverage::binary::{BinaryCoverage, Count, ModuleBinaryCoverage};
use debuggable_module::path::FilePath;
use debuggable_module::Offset;

use crate::hex::Hex;

#[derive(Deserialize, Serialize)]
pub struct BinaryCoverageJson {
#[serde(flatten)]
pub modules: BTreeMap<String, BTreeMap<Hex, u32>>,
}

impl From<BinaryCoverage> for BinaryCoverageJson {
fn from(binary: BinaryCoverage) -> Self {
let mut modules = BTreeMap::new();

for (module, offsets) in &binary.modules {
let mut map: BTreeMap<Hex, u32> = BTreeMap::new();

for (offset, count) in offsets.as_ref() {
map.insert(Hex(offset.0), count.0);
}

let path = module.as_str().to_owned();
modules.insert(path, map);
}

Self { modules }
}
}

impl TryFrom<BinaryCoverageJson> for BinaryCoverage {
type Error = anyhow::Error;

fn try_from(json: BinaryCoverageJson) -> Result<Self> {
let mut process = BinaryCoverage::default();

for (module, offsets) in json.modules {
let mut coverage = ModuleBinaryCoverage::default();

for (hex, count) in offsets {
let offset = Offset(hex.0);
coverage.offsets.insert(offset, Count(count));
}

let path = FilePath::new(module)?;
process.modules.insert(path, coverage);
}

Ok(process)
}
}
Loading