Skip to content

Commit 928f8a9

Browse files
committed
expose snapshots to the public API
Signed-off-by: Jorge Prendes <[email protected]>
1 parent 21077d6 commit 928f8a9

File tree

5 files changed

+55
-9
lines changed

5 files changed

+55
-9
lines changed

src/hyperlight_host/src/mem/mgr.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -262,10 +262,25 @@ where
262262
}
263263
}
264264

265+
pub(crate) fn snapshot(&mut self) -> Result<SharedMemorySnapshot> {
266+
SharedMemorySnapshot::new(&mut self.shared_mem)
267+
}
268+
269+
pub(crate) fn restore_snapshot(&mut self, snapshot: &SharedMemorySnapshot) -> Result<()> {
270+
if self.shared_mem.mem_size() != snapshot.mem_size() {
271+
return Err(new_error!(
272+
"Snapshot size does not match current memory size: {} != {}",
273+
self.shared_mem.raw_mem_size(),
274+
snapshot.mem_size()
275+
));
276+
}
277+
snapshot.restore_from_snapshot(&mut self.shared_mem)
278+
}
279+
265280
/// this function will create a memory snapshot and push it onto the stack of snapshots
266281
/// It should be used when you want to save the state of the memory, for example, when evolving a sandbox to a new state
267282
pub(crate) fn push_state(&mut self) -> Result<()> {
268-
let snapshot = SharedMemorySnapshot::new(&mut self.shared_mem)?;
283+
let snapshot = self.snapshot()?;
269284
self.snapshots
270285
.try_lock()
271286
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
@@ -278,16 +293,14 @@ where
278293
/// It should be used when you want to restore the state of the memory to a previous state but still want to
279294
/// retain that state, for example after calling a function in the guest
280295
pub(crate) fn restore_state_from_last_snapshot(&mut self) -> Result<()> {
281-
let mut snapshots = self
296+
let snapshots: std::sync::MutexGuard<'_, Vec<SharedMemorySnapshot>> = self
282297
.snapshots
283298
.try_lock()
284299
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?;
285-
let last = snapshots.last_mut();
286-
if last.is_none() {
300+
let last = snapshots.last();
301+
let Some(snapshot) = last else {
287302
log_then_return!(NoMemorySnapshot);
288-
}
289-
#[allow(clippy::unwrap_used)] // We know that last is not None because we checked it above
290-
let snapshot = last.unwrap();
303+
};
291304
snapshot.restore_from_snapshot(&mut self.shared_mem)
292305
}
293306

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
}
2828

@@ -49,11 +49,17 @@ impl SharedMemorySnapshot {
4949
/// into the internally-stored `SharedMemory`
5050
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
5151
pub(super) fn restore_from_snapshot<S: SharedMemory>(
52-
&mut self,
52+
&self,
5353
shared_mem: &mut S,
5454
) -> Result<()> {
5555
shared_mem.with_exclusivity(|e| e.copy_from_slice(self.snapshot.as_slice(), 0))?
5656
}
57+
58+
/// Return the size of the snapshot in bytes.
59+
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
60+
pub(super) fn mem_size(&self) -> usize {
61+
self.snapshot.len()
62+
}
5763
}
5864

5965
#[cfg(test)]

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::{
2323
use tracing::{Span, instrument};
2424

2525
use super::host_funcs::FunctionRegistry;
26+
use super::snapshot::Snapshot;
2627
use super::{MemMgrWrapper, WrapperGetter};
2728
use crate::func::call_ctx::MultiUseGuestCallContext;
2829
use crate::func::guest_err::check_for_guest_error;
@@ -155,6 +156,21 @@ impl MultiUseSandbox {
155156
MultiUseGuestCallContext::start(self)
156157
}
157158

159+
/// Create a snapshot of the current state of the sandbox's memory.
160+
#[instrument(err(Debug), skip_all, parent = Span::current())]
161+
pub fn snapshot(&mut self) -> Result<Snapshot> {
162+
let snapshot = self.mem_mgr.unwrap_mgr_mut().snapshot()?;
163+
Ok(Snapshot { inner: snapshot })
164+
}
165+
166+
/// Restore the sandbox's memory to the state captured in the given snapshot.
167+
#[instrument(err(Debug), skip_all, parent = Span::current())]
168+
pub fn restore(&mut self, snapshot: &Snapshot) -> Result<()> {
169+
self.mem_mgr
170+
.unwrap_mgr_mut()
171+
.restore_snapshot(&snapshot.inner)
172+
}
173+
158174
/// Call a guest function by name, with the given return type and arguments.
159175
#[instrument(err(Debug), skip(self, args), parent = Span::current())]
160176
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)