Skip to content

Commit db55edc

Browse files
committed
expose snapshots to the public API
Signed-off-by: Jorge Prendes <[email protected]>
1 parent 39df600 commit db55edc

File tree

5 files changed

+65
-9
lines changed

5 files changed

+65
-9
lines changed

src/hyperlight_host/src/mem/mgr.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -265,10 +265,33 @@ where
265265
}
266266
}
267267

268+
pub(crate) fn snapshot(&mut self) -> Result<SharedMemorySnapshot> {
269+
SharedMemorySnapshot::new(&mut self.shared_mem, self.mapped_rgns)
270+
}
271+
272+
273+
/// This function restores a memory snapshot the given snapshot.
274+
///
275+
/// Returns the number of memory regions mapped into the sandbox
276+
/// that need to be unmapped in order for the restore to be
277+
/// completed.
278+
pub(crate) fn restore_snapshot(&mut self, snapshot: &SharedMemorySnapshot) -> Result<u64> {
279+
if self.shared_mem.mem_size() != snapshot.mem_size() {
280+
return Err(new_error!(
281+
"Snapshot size does not match current memory size: {} != {}",
282+
self.shared_mem.raw_mem_size(),
283+
snapshot.mem_size()
284+
));
285+
}
286+
let old_rgns = self.mapped_rgns;
287+
self.mapped_rgns = snapshot.restore_from_snapshot(&mut self.shared_mem)?;
288+
Ok(old_rgns - self.mapped_rgns)
289+
}
290+
268291
/// this function will create a memory snapshot and push it onto the stack of snapshots
269292
/// It should be used when you want to save the state of the memory, for example, when evolving a sandbox to a new state
270293
pub(crate) fn push_state(&mut self) -> Result<()> {
271-
let snapshot = SharedMemorySnapshot::new(&mut self.shared_mem, self.mapped_rgns)?;
294+
let snapshot = self.snapshot()?;
272295
self.snapshots
273296
.try_lock()
274297
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
@@ -285,16 +308,14 @@ where
285308
/// that need to be unmapped in order for the restore to be
286309
/// completed.
287310
pub(crate) fn restore_state_from_last_snapshot(&mut self) -> Result<u64> {
288-
let mut snapshots = self
311+
let snapshots = self
289312
.snapshots
290313
.try_lock()
291314
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
292-
let last = snapshots.last_mut();
293-
if last.is_none() {
315+
let last = snapshots.last();
316+
let Some(snapshot) = last else {
294317
log_then_return!(NoMemorySnapshot);
295-
}
296-
#[allow(clippy::unwrap_used)] // We know that last is not None because we checked it above
297-
let snapshot = last.unwrap();
318+
};
298319
let old_rgns = self.mapped_rgns;
299320
self.mapped_rgns = snapshot.restore_from_snapshot(&mut self.shared_mem)?;
300321
Ok(old_rgns - self.mapped_rgns)

src/hyperlight_host/src/mem/shared_mem_snapshot.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::Result;
2222
/// A wrapper around a `SharedMemory` reference and a snapshot
2323
/// of the memory therein
2424
#[derive(Clone)]
25-
pub(super) struct SharedMemorySnapshot {
25+
pub(crate) struct SharedMemorySnapshot {
2626
snapshot: Vec<u8>,
2727
/// How many non-main-RAM regions were mapped when this snapshot was taken?
2828
mapped_rgns: u64,
@@ -54,12 +54,18 @@ impl SharedMemorySnapshot {
5454
/// into the internally-stored `SharedMemory`
5555
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
5656
pub(super) fn restore_from_snapshot<S: SharedMemory>(
57-
&mut self,
57+
&self,
5858
shared_mem: &mut S,
5959
) -> Result<u64> {
6060
shared_mem.with_exclusivity(|e| e.copy_from_slice(self.snapshot.as_slice(), 0))??;
6161
Ok(self.mapped_rgns)
6262
}
63+
64+
/// Return the size of the snapshot in bytes.
65+
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
66+
pub(super) fn mem_size(&self) -> usize {
67+
self.snapshot.len()
68+
}
6369
}
6470

6571
#[cfg(test)]

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::{
2828
use tracing::{Span, instrument};
2929

3030
use super::host_funcs::FunctionRegistry;
31+
use super::snapshot::Snapshot;
3132
use super::{MemMgrWrapper, WrapperGetter};
3233
use crate::func::call_ctx::MultiUseGuestCallContext;
3334
use crate::func::guest_err::check_for_guest_error;
@@ -163,6 +164,23 @@ impl MultiUseSandbox {
163164
MultiUseGuestCallContext::start(self)
164165
}
165166

167+
/// Create a snapshot of the current state of the sandbox's memory.
168+
#[instrument(err(Debug), skip_all, parent = Span::current())]
169+
pub fn snapshot(&mut self) -> Result<Snapshot> {
170+
let snapshot = self.mem_mgr.unwrap_mgr_mut().snapshot()?;
171+
Ok(Snapshot { inner: snapshot })
172+
}
173+
174+
/// Restore the sandbox's memory to the state captured in the given snapshot.
175+
#[instrument(err(Debug), skip_all, parent = Span::current())]
176+
pub fn restore(&mut self, snapshot: &Snapshot) -> Result<()> {
177+
let rgns_to_unmap = self.mem_mgr
178+
.unwrap_mgr_mut()
179+
.restore_snapshot(&snapshot.inner)?;
180+
unsafe { self.vm.unmap_regions(rgns_to_unmap)? };
181+
Ok(())
182+
}
183+
166184
/// Call a guest function by name, with the given return type and arguments.
167185
#[instrument(err(Debug), skip(self, args), parent = Span::current())]
168186
pub fn call_guest_function_by_name<Output: SupportedReturnType>(

src/hyperlight_host/src/sandbox/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ pub mod uninitialized;
3737
/// initialized `Sandbox`es.
3838
pub(crate) mod uninitialized_evolve;
3939

40+
/// Representation of a snapshot of a `Sandbox`.
41+
pub mod snapshot;
42+
4043
/// Trait used by the macros to paper over the differences between hyperlight and hyperlight-wasm
4144
mod callable;
4245

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use crate::mem::shared_mem_snapshot::SharedMemorySnapshot;
2+
3+
/// A snapshot capturing the state of the memory in a `MultiUseSandbox`.
4+
#[derive(Clone)]
5+
pub struct Snapshot {
6+
/// TODO: Use Arc<SharedMemorySnapshot>
7+
pub(crate) inner: SharedMemorySnapshot,
8+
}

0 commit comments

Comments
 (0)