Skip to content

Commit da4ac2c

Browse files
Merge pull request #158 from microsoft/main
Fork Sync: Update from parent repository
2 parents bfbf553 + a8d5ab7 commit da4ac2c

File tree

6 files changed

+245
-16
lines changed

6 files changed

+245
-16
lines changed

src/agent/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/agent/coverage/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ procfs = { version = "0.12", default-features = false, features=["flate2"] }
2424

2525
[dev-dependencies]
2626
clap = { version = "4.0", features = ["derive"] }
27+
pretty_assertions = "1.3.0"

src/agent/coverage/src/binary.rs

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
use std::collections::{BTreeMap, BTreeSet};
55

6-
use anyhow::{bail, Result};
6+
use anyhow::Result;
77
use debuggable_module::Module;
88
pub use debuggable_module::{block, path::FilePath, Offset};
99
use symbolic::debuginfo::Object;
@@ -16,24 +16,62 @@ pub struct BinaryCoverage {
1616
pub modules: BTreeMap<FilePath, ModuleBinaryCoverage>,
1717
}
1818

19+
impl BinaryCoverage {
20+
pub fn add(&mut self, rhs: &Self) {
21+
for (path, rhs_module) in &rhs.modules {
22+
let module = self.modules.entry(path.clone()).or_default();
23+
module.add(rhs_module);
24+
}
25+
}
26+
27+
pub fn merge(&mut self, rhs: &Self) {
28+
for (path, rhs_module) in &rhs.modules {
29+
let module = self.modules.entry(path.clone()).or_default();
30+
module.merge(rhs_module);
31+
}
32+
}
33+
}
34+
1935
#[derive(Clone, Debug, Default, Eq, PartialEq)]
2036
pub struct ModuleBinaryCoverage {
2137
pub offsets: BTreeMap<Offset, Count>,
2238
}
2339

2440
impl ModuleBinaryCoverage {
25-
pub fn increment(&mut self, offset: Offset) -> Result<()> {
26-
if let Some(count) = self.offsets.get_mut(&offset) {
27-
count.increment();
28-
} else {
29-
bail!("unknown coverage offset: {offset:x}");
30-
};
31-
32-
Ok(())
41+
pub fn increment(&mut self, offset: Offset) {
42+
let count = self.offsets.entry(offset).or_default();
43+
count.increment();
44+
}
45+
46+
pub fn add(&mut self, rhs: &Self) {
47+
for (&offset, &rhs_count) in &rhs.offsets {
48+
let count = self.offsets.entry(offset).or_default();
49+
*count += rhs_count;
50+
}
51+
}
52+
53+
pub fn merge(&mut self, rhs: &Self) {
54+
for (&offset, &rhs_count) in &rhs.offsets {
55+
let count = self.offsets.entry(offset).or_default();
56+
*count = Count::max(*count, rhs_count)
57+
}
3358
}
3459
}
3560

36-
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
61+
impl<O> From<O> for ModuleBinaryCoverage
62+
where
63+
O: IntoIterator<Item = Offset>,
64+
{
65+
fn from(offsets: O) -> Self {
66+
let offsets = offsets.into_iter().map(|o| (o, Count(0)));
67+
68+
let mut coverage = Self::default();
69+
coverage.offsets.extend(offsets);
70+
coverage
71+
}
72+
}
73+
74+
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
3775
pub struct Count(pub u32);
3876

3977
impl Count {
@@ -44,6 +82,24 @@ impl Count {
4482
pub fn reached(&self) -> bool {
4583
self.0 > 0
4684
}
85+
86+
pub fn max(self, rhs: Self) -> Self {
87+
Count(u32::max(self.0, rhs.0))
88+
}
89+
}
90+
91+
impl std::ops::Add for Count {
92+
type Output = Self;
93+
94+
fn add(self, rhs: Self) -> Self {
95+
Count(self.0.saturating_add(rhs.0))
96+
}
97+
}
98+
99+
impl std::ops::AddAssign for Count {
100+
fn add_assign(&mut self, rhs: Self) {
101+
*self = *self + rhs;
102+
}
47103
}
48104

49105
pub fn find_coverage_sites<'data>(
@@ -81,10 +137,7 @@ pub fn find_coverage_sites<'data>(
81137
}
82138
}
83139

84-
let mut coverage = ModuleBinaryCoverage::default();
85-
coverage
86-
.offsets
87-
.extend(offsets.into_iter().map(|o| (o, Count(0))));
140+
let coverage = ModuleBinaryCoverage::from(offsets.into_iter());
88141

89142
Ok(coverage)
90143
}
@@ -94,3 +147,6 @@ impl AsRef<BTreeMap<Offset, Count>> for ModuleBinaryCoverage {
94147
&self.offsets
95148
}
96149
}
150+
151+
#[cfg(test)]
152+
mod tests;
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use anyhow::Result;
5+
use debuggable_module::path::FilePath;
6+
use pretty_assertions::assert_eq;
7+
8+
use crate::binary::{Count, Offset};
9+
10+
use super::*;
11+
12+
macro_rules! module {
13+
( $( $offset: expr => $count: expr, )* ) => {{
14+
let mut module = ModuleBinaryCoverage::default();
15+
16+
$(
17+
module.offsets.insert(Offset($offset), Count($count));
18+
)*
19+
20+
module
21+
}}
22+
}
23+
24+
macro_rules! coverage {
25+
( $( $path: expr => { $( $offset: expr => $count: expr, )* }, )* ) => {{
26+
let mut coverage = BinaryCoverage::default();
27+
28+
$(
29+
let path = FilePath::new($path)?;
30+
let module = module! { $( $offset => $count, )* };
31+
coverage.modules.insert(path, module);
32+
)*
33+
34+
coverage
35+
}}
36+
}
37+
38+
#[test]
39+
fn test_module_increment() -> Result<()> {
40+
let mut module = module! {
41+
1 => 1,
42+
2 => 0,
43+
};
44+
45+
module.increment(Offset(2));
46+
47+
assert_eq!(
48+
module,
49+
module! {
50+
1 => 1,
51+
2 => 1,
52+
}
53+
);
54+
55+
module.increment(Offset(2));
56+
57+
assert_eq!(
58+
module,
59+
module! {
60+
1 => 1,
61+
2 => 2,
62+
}
63+
);
64+
65+
module.increment(Offset(3));
66+
67+
assert_eq!(
68+
module,
69+
module! {
70+
1 => 1,
71+
2 => 2,
72+
3 => 1,
73+
}
74+
);
75+
76+
Ok(())
77+
}
78+
79+
#[test]
80+
fn test_coverage_add() -> Result<()> {
81+
let mut coverage = coverage! {
82+
"main.exe" => {
83+
1 => 1,
84+
2 => 0,
85+
3 => 1,
86+
4 => 0,
87+
},
88+
"old.dll" => {
89+
1 => 0,
90+
},
91+
};
92+
93+
coverage.add(&coverage! {
94+
"main.exe" => {
95+
1 => 1,
96+
2 => 1,
97+
5 => 1,
98+
},
99+
"new.dll" => {
100+
1 => 1,
101+
},
102+
});
103+
104+
assert_eq!(
105+
coverage,
106+
coverage! {
107+
"main.exe" => {
108+
1 => 2,
109+
2 => 1,
110+
3 => 1,
111+
4 => 0,
112+
5 => 1,
113+
},
114+
"old.dll" => {
115+
1 => 0,
116+
},
117+
"new.dll" => {
118+
1 => 1,
119+
},
120+
}
121+
);
122+
123+
Ok(())
124+
}
125+
126+
#[test]
127+
fn test_coverage_merge() -> Result<()> {
128+
let mut coverage = coverage! {
129+
"main.exe" => {
130+
1 => 1,
131+
2 => 0,
132+
3 => 1,
133+
4 => 0,
134+
},
135+
"old.dll" => {
136+
1 => 0,
137+
},
138+
};
139+
140+
coverage.merge(&coverage! {
141+
"main.exe" => {
142+
1 => 1,
143+
2 => 1,
144+
5 => 1,
145+
},
146+
"new.dll" => {
147+
1 => 1,
148+
},
149+
});
150+
151+
assert_eq!(
152+
coverage,
153+
coverage! {
154+
"main.exe" => {
155+
1 => 1,
156+
2 => 1,
157+
3 => 1,
158+
4 => 0,
159+
5 => 1,
160+
},
161+
"old.dll" => {
162+
1 => 0,
163+
},
164+
"new.dll" => {
165+
1 => 1,
166+
},
167+
}
168+
);
169+
170+
Ok(())
171+
}

src/agent/coverage/src/record/linux.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl<'data> LinuxRecorder<'data> {
4848
if let Some(image) = context.find_image_for_addr(addr) {
4949
if let Some(coverage) = self.coverage.modules.get_mut(image.path()) {
5050
let offset = addr.offset_from(image.base())?;
51-
coverage.increment(offset)?;
51+
coverage.increment(offset);
5252
} else {
5353
bail!("coverage not initialized for module {}", image.path());
5454
}

src/agent/coverage/src/record/windows.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ impl<'data> WindowsRecorder<'data> {
7575
.get_mut(&breakpoint.module)
7676
.ok_or_else(|| anyhow!("coverage not initialized for module: {}", breakpoint.module))?;
7777

78-
coverage.increment(breakpoint.offset)?;
78+
coverage.increment(breakpoint.offset);
7979

8080
Ok(())
8181
}

0 commit comments

Comments
 (0)