Skip to content

Commit 862dfc5

Browse files
Skip digest checks when reading unwind info (#275)
This check was put in place to make sure we aren't loading corrupted unwind information. This is an unlikely thing to happen but we are paying a high cost, especially in memory-constrained environments as unwind information will be evicted and re-read from disk with the consequent digest checks. Keeping the digest is very useful for debugging purposes, but checking it on every read is not worth it performance wise, right now in some of my tests, 50% of lightswitch's CPU time is spent computing the digest. Some of the future things that could be done: - switching to a faster hash algorithm and optimise the buffer sizes - check a small % of reads Test Plan ========= CI
1 parent 8eb2019 commit 862dfc5

File tree

3 files changed

+47
-21
lines changed

3 files changed

+47
-21
lines changed

src/profiler.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,6 +1592,7 @@ impl Profiler {
15921592
&executable_path,
15931593
executable_id,
15941594
Some((start_low_address, start_high_address)),
1595+
false,
15951596
)
15961597
}
15971598
Runtime::CLike => {
@@ -1613,6 +1614,7 @@ impl Profiler {
16131614
&executable_path,
16141615
executable_id,
16151616
None,
1617+
false,
16161618
)
16171619
}
16181620
}

src/unwind_info/manager.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,9 @@ impl UnwindInfoManager {
8282
executable_path: &Path,
8383
executable_id: ExecutableId,
8484
first_frame_override: Option<(u64, u64)>,
85+
check_digest: bool,
8586
) -> Result<Vec<CompactUnwindRow>, FetchUnwindInfoError> {
86-
match self.read_from_cache(executable_id) {
87+
match self.read_from_cache(executable_id, check_digest) {
8788
Ok(unwind_info) => Ok(unwind_info),
8889
Err(e) => {
8990
if matches!(e, FetchUnwindInfoError::NotFound) {
@@ -103,6 +104,7 @@ impl UnwindInfoManager {
103104
fn read_from_cache(
104105
&self,
105106
executable_id: ExecutableId,
107+
check_digest: bool,
106108
) -> Result<Vec<CompactUnwindRow>, FetchUnwindInfoError> {
107109
let unwind_info_path = self.path_for(executable_id);
108110
let file = File::open(unwind_info_path).map_err(|e| {
@@ -116,7 +118,7 @@ impl UnwindInfoManager {
116118
let mut buffer = BufReader::new(file);
117119
let mut data = Vec::new();
118120
buffer.read_to_end(&mut data)?;
119-
let reader = Reader::new(&data).map_err(FetchUnwindInfoError::Reader)?;
121+
let reader = Reader::new(&data, check_digest).map_err(FetchUnwindInfoError::Reader)?;
120122

121123
Ok(reader.unwind_info()?)
122124
}
@@ -223,6 +225,7 @@ mod tests {
223225
&PathBuf::from("/proc/self/exe"),
224226
ExecutableId(0xFABADA),
225227
None,
228+
true,
226229
);
227230
let manager_unwind_info = manager_unwind_info.unwrap();
228231
assert_eq!(unwind_info, manager_unwind_info);
@@ -240,6 +243,7 @@ mod tests {
240243
&PathBuf::from("/proc/self/exe"),
241244
ExecutableId(0xFABADA),
242245
None,
246+
true,
243247
);
244248
assert!(manager_unwind_info.is_ok());
245249
let manager_unwind_info = manager_unwind_info.unwrap();
@@ -258,6 +262,7 @@ mod tests {
258262
&PathBuf::from("/proc/self/exe"),
259263
ExecutableId(0xFABADA),
260264
None,
265+
true,
261266
);
262267
let manager_unwind_info = manager_unwind_info.unwrap();
263268
assert_eq!(unwind_info, manager_unwind_info);

src/unwind_info/persist.rs

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,17 @@ pub enum ReaderError {
145145
pub struct Reader<'a> {
146146
header: Header,
147147
data: &'a [u8],
148+
check_digest: bool,
148149
}
149150

150151
impl<'a> Reader<'a> {
151-
pub fn new(data: &'a [u8]) -> Result<Self, ReaderError> {
152+
pub fn new(data: &'a [u8], check_digest: bool) -> Result<Self, ReaderError> {
152153
let header = Self::parse_header(data)?;
153-
Ok(Reader { header, data })
154+
Ok(Reader {
155+
header,
156+
data,
157+
check_digest,
158+
})
154159
}
155160

156161
fn parse_header(data: &[u8]) -> Result<Header, ReaderError> {
@@ -190,22 +195,26 @@ impl<'a> Reader<'a> {
190195
let unwind_row_data = unwind_info_data
191196
.get(step..step + unwind_row_size)
192197
.ok_or(ReaderError::OutOfRange)?;
193-
context.update(unwind_row_data);
198+
if self.check_digest {
199+
context.update(unwind_row_data);
200+
}
194201
plain::copy_from_bytes(&mut unwind_row, unwind_row_data)
195202
.map_err(|e| ReaderError::Generic(format!("{e:?}")))?;
196203
unwind_info.push(unwind_row);
197204
}
198205

199-
let mut buffer = [0; 8];
200-
let _ = context
201-
.finish()
202-
.as_ref()
203-
.read(&mut buffer)
204-
.map_err(|e| ReaderError::Generic(e.to_string()));
205-
let digest = u64::from_ne_bytes(buffer);
206-
207-
if self.header.unwind_info_digest != digest {
208-
return Err(ReaderError::Digest);
206+
if self.check_digest {
207+
let mut buffer = [0; 8];
208+
let _ = context
209+
.finish()
210+
.as_ref()
211+
.read(&mut buffer)
212+
.map_err(|e| ReaderError::Generic(e.to_string()));
213+
let digest = u64::from_ne_bytes(buffer);
214+
215+
if self.header.unwind_info_digest != digest {
216+
return Err(ReaderError::Digest);
217+
}
209218
}
210219

211220
Ok(unwind_info)
@@ -226,7 +235,7 @@ mod tests {
226235
let writer = Writer::new(&path, None);
227236
assert!(writer.write(&mut buffer).is_ok());
228237

229-
let reader = Reader::new(&buffer.get_ref()[..]);
238+
let reader = Reader::new(&buffer.get_ref()[..], true);
230239
let unwind_info = reader.unwrap().unwind_info();
231240
assert!(unwind_info.is_ok());
232241
let unwind_info = unwind_info.unwrap();
@@ -247,7 +256,7 @@ mod tests {
247256
.write_all(unsafe { plain::as_bytes(&header) })
248257
.unwrap();
249258
assert!(matches!(
250-
Reader::new(&buffer),
259+
Reader::new(&buffer, true),
251260
Err(ReaderError::MagicNumber)
252261
));
253262
}
@@ -263,7 +272,10 @@ mod tests {
263272
buffer
264273
.write_all(unsafe { plain::as_bytes(&header) })
265274
.unwrap();
266-
assert!(matches!(Reader::new(&buffer), Err(ReaderError::Version)));
275+
assert!(matches!(
276+
Reader::new(&buffer, true),
277+
Err(ReaderError::Version)
278+
));
267279
}
268280

269281
#[test]
@@ -277,15 +289,22 @@ mod tests {
277289
buffer.seek(SeekFrom::End(-10)).unwrap();
278290
buffer.write_all(&[0, 0, 0, 0, 0, 0, 0]).unwrap();
279291

280-
let reader = Reader::new(&buffer.get_ref()[..]);
292+
let reader = Reader::new(&buffer.get_ref()[..], true);
281293
let unwind_info = reader.unwrap().unwind_info();
282294
assert!(matches!(unwind_info, Err(ReaderError::Digest)));
295+
296+
let reader = Reader::new(&buffer.get_ref()[..], false);
297+
let unwind_info = reader.unwrap().unwind_info();
298+
assert!(unwind_info.is_ok());
283299
}
284300

285301
#[test]
286302
fn test_header_too_small() {
287303
let buffer = Vec::new();
288-
assert!(matches!(Reader::new(&buffer), Err(ReaderError::OutOfRange)));
304+
assert!(matches!(
305+
Reader::new(&buffer, true),
306+
Err(ReaderError::OutOfRange)
307+
));
289308
}
290309

291310
#[test]
@@ -301,7 +320,7 @@ mod tests {
301320
.write_all(unsafe { plain::as_bytes(&header) })
302321
.unwrap();
303322
assert!(matches!(
304-
Reader::new(&buffer).unwrap().unwind_info(),
323+
Reader::new(&buffer, true).unwrap().unwind_info(),
305324
Err(ReaderError::OutOfRange)
306325
));
307326
}

0 commit comments

Comments
 (0)