|
1 | | -#[cfg(feature = "rr-component")] |
2 | | -use crate::rr::{RecordBuffer, Recorder, component_events::MemorySliceWriteEvent}; |
3 | | - |
4 | | -use core::ops::{Deref, DerefMut}; |
5 | | - |
6 | 1 | /// Component specific RR hooks that use `component-model` feature gating |
| 2 | +#[cfg(feature = "component-model")] |
7 | 3 | pub mod component_hooks; |
8 | 4 | /// Core RR hooks |
9 | 5 | pub mod core_hooks; |
10 | | - |
11 | | -/// Same as [`ConstMemorySliceCell`] except allows for dynamically sized slices. |
12 | | -/// |
13 | | -/// Prefer the above for efficiency if slice size is known statically. |
14 | | -/// |
15 | | -/// **Note**: The correct operation of this type relies of several invariants. |
16 | | -/// See [`ConstMemorySliceCell`] for detailed description on the role |
17 | | -/// of these types. |
18 | | -pub struct MemorySliceCell<'a> { |
19 | | - pub bytes: &'a mut [u8], |
20 | | - #[cfg(feature = "rr-component")] |
21 | | - pub offset: usize, |
22 | | - #[cfg(feature = "rr-component")] |
23 | | - pub recorder: Option<&'a mut RecordBuffer>, |
24 | | -} |
25 | | -impl<'a> Deref for MemorySliceCell<'a> { |
26 | | - type Target = [u8]; |
27 | | - fn deref(&self) -> &Self::Target { |
28 | | - self.bytes |
29 | | - } |
30 | | -} |
31 | | -impl DerefMut for MemorySliceCell<'_> { |
32 | | - fn deref_mut(&mut self) -> &mut Self::Target { |
33 | | - self.bytes |
34 | | - } |
35 | | -} |
36 | | -impl Drop for MemorySliceCell<'_> { |
37 | | - /// Drop serves as a recording hook for stores to the memory slice |
38 | | - fn drop(&mut self) { |
39 | | - #[cfg(feature = "rr-component")] |
40 | | - if let Some(buf) = &mut self.recorder { |
41 | | - buf.record_event(|| MemorySliceWriteEvent { |
42 | | - offset: self.offset, |
43 | | - bytes: self.bytes.to_vec(), |
44 | | - }) |
45 | | - .unwrap(); |
46 | | - } |
47 | | - } |
48 | | -} |
49 | | - |
50 | | -/// Zero-cost encapsulation type for a statically sized slice of mutable memory |
51 | | -/// |
52 | | -/// # Purpose and Usage (Read Carefully!) |
53 | | -/// |
54 | | -/// This type (and its dynamic counterpart [`MemorySliceCell`]) are critical to |
55 | | -/// record/replay (RR) support in Wasmtime. In practice, all lowering operations utilize |
56 | | -/// a [`LowerContext`], which provides a capability to modify guest Wasm module state in |
57 | | -/// the following ways: |
58 | | -/// |
59 | | -/// 1. Write to slices of memory with [`get`](LowerContext::get)/[`get_dyn`](LowerContext::get_dyn) |
60 | | -/// 2. Movement of memory with [`realloc`](LowerContext::realloc) |
61 | | -/// |
62 | | -/// The above are intended to be the narrow waists for recording changes to guest state, and |
63 | | -/// should be the **only** interfaces used during lowerng. In particular, |
64 | | -/// [`get`](LowerContext::get)/[`get_dyn`](LowerContext::get_dyn) return |
65 | | -/// ([`ConstMemorySliceCell`]/[`MemorySliceCell`]), which implement [`Drop`] |
66 | | -/// allowing us a hook to just capture the final aggregate changes made to guest memory by the host. |
67 | | -/// |
68 | | -/// ## Critical Invariants |
69 | | -/// |
70 | | -/// Typically recording would need to know both when the slice was borrowed AND when it was |
71 | | -/// dropped, since memory movement with [`realloc`](LowerContext::realloc) can be interleaved between |
72 | | -/// borrows and drops, and replays would have to be aware of this. **However**, with this abstraction, |
73 | | -/// we can be more efficient and get away with **only** recording drops, because of the implicit interaction between |
74 | | -/// [`realloc`](LowerContext::realloc) and [`get`](LowerContext::get)/[`get_dyn`](LowerContext::get_dyn), |
75 | | -/// which both take a `&mut self`. Since the latter implements [`Drop`], which also takes a `&mut self`, |
76 | | -/// the compiler will automatically enforce that drops of this type need to be triggered before a |
77 | | -/// [`realloc`](LowerContext::realloc), preventing any interleavings in between the borrow and drop of the slice. |
78 | | -pub struct ConstMemorySliceCell<'a, const N: usize> { |
79 | | - pub bytes: &'a mut [u8; N], |
80 | | - #[cfg(feature = "rr-component")] |
81 | | - pub offset: usize, |
82 | | - #[cfg(feature = "rr-component")] |
83 | | - pub recorder: Option<&'a mut RecordBuffer>, |
84 | | -} |
85 | | -impl<'a, const N: usize> Deref for ConstMemorySliceCell<'a, N> { |
86 | | - type Target = [u8; N]; |
87 | | - fn deref(&self) -> &Self::Target { |
88 | | - self.bytes |
89 | | - } |
90 | | -} |
91 | | -impl<'a, const N: usize> DerefMut for ConstMemorySliceCell<'a, N> { |
92 | | - fn deref_mut(&mut self) -> &mut Self::Target { |
93 | | - self.bytes |
94 | | - } |
95 | | -} |
96 | | -impl<'a, const N: usize> Drop for ConstMemorySliceCell<'a, N> { |
97 | | - /// Drops serves as a recording hook for stores to the memory slice |
98 | | - fn drop(&mut self) { |
99 | | - #[cfg(feature = "rr-component")] |
100 | | - if let Some(buf) = &mut self.recorder { |
101 | | - buf.record_event(|| MemorySliceWriteEvent { |
102 | | - offset: self.offset, |
103 | | - bytes: self.bytes.to_vec(), |
104 | | - }) |
105 | | - .unwrap(); |
106 | | - } |
107 | | - } |
108 | | -} |
0 commit comments