Skip to main content

pagers_core/
mincore.rs

1use std::ops::{Index, IndexMut, Range};
2
3use memmap2::Mmap;
4use nix::errno::Errno;
5
6#[cfg(target_os = "macos")]
7type MincoreType = libc::c_char;
8
9#[cfg(not(target_os = "macos"))]
10type MincoreType = libc::c_uchar;
11
12pub fn residency<PM: PageMap>(mmap: &Mmap, len: usize) -> nix::Result<PM> {
13    let page_size = *crate::pagesize::PAGE_SIZE;
14    let vec_len = len.div_ceil(page_size);
15    let mut vec_out: Vec<u8> = Vec::with_capacity(vec_len);
16
17    unsafe {
18        // SAFETY: mincore takes a pointer to a virtual memory region and writes
19        // RAM residency information to the memory region at vec_out, with the
20        // length computed above using the expression from the mincore man page.
21        // We have allocated the underlying buffer by using with_capacity.
22        if libc::mincore(
23            mmap.as_ptr() as *mut libc::c_void,
24            len as libc::size_t,
25            vec_out.as_mut_ptr().cast::<MincoreType>(),
26        ) != 0
27        {
28            return Err(Errno::last());
29        }
30        // SAFETY: we just filled up the vector with valid values
31        vec_out.set_len(vec_len);
32    }
33    Ok(PM::from_residency_bytes(vec_out))
34}
35
36pub trait PageMapSlice {
37    fn len(&self) -> usize;
38    fn is_empty(&self) -> bool {
39        self.len() == 0
40    }
41    fn is_set(&self, index: usize) -> bool;
42    fn count_filled(&self) -> usize;
43    fn fill(&mut self, value: bool);
44}
45
46pub trait PageMap:
47    PageMapSlice + Index<Range<usize>, Output = Self::Slice> + IndexMut<Range<usize>>
48{
49    type Slice: ?Sized + PageMapSlice;
50
51    fn from_bools(iter: impl Iterator<Item = bool>) -> Self;
52
53    fn from_residency_bytes(bytes: Vec<u8>) -> Self
54    where
55        Self: Sized,
56    {
57        Self::from_bools(bytes.into_iter().map(|b| b != 0))
58    }
59}
60
61#[cfg(feature = "bitvec")]
62pub type DefaultPageMap = bitvec::vec::BitVec;
63
64#[cfg(not(feature = "bitvec"))]
65pub type DefaultPageMap = Vec<bool>;
66
67// Vec<bool> / [bool] impls
68
69impl PageMapSlice for [bool] {
70    fn len(&self) -> usize {
71        self.len()
72    }
73
74    fn is_set(&self, index: usize) -> bool {
75        self[index]
76    }
77
78    fn count_filled(&self) -> usize {
79        self.iter().filter(|&&v| v).count()
80    }
81
82    fn fill(&mut self, value: bool) {
83        self.fill(value);
84    }
85}
86
87impl PageMapSlice for Vec<bool> {
88    fn len(&self) -> usize {
89        self.len()
90    }
91
92    fn is_set(&self, index: usize) -> bool {
93        self[index]
94    }
95
96    fn count_filled(&self) -> usize {
97        self.iter().filter(|&&v| v).count()
98    }
99
100    fn fill(&mut self, value: bool) {
101        self.as_mut_slice().fill(value);
102    }
103}
104
105impl PageMap for Vec<bool> {
106    type Slice = [bool];
107
108    fn from_bools(iter: impl Iterator<Item = bool>) -> Self {
109        iter.collect()
110    }
111}
112
113// BitVec / BitSlice impls
114
115#[cfg(feature = "bitvec")]
116mod bitvec_impl {
117    use super::*;
118    use bitvec::prelude::*;
119
120    impl PageMapSlice for BitSlice {
121        fn len(&self) -> usize {
122            self.len()
123        }
124
125        fn is_set(&self, index: usize) -> bool {
126            self[index]
127        }
128
129        fn count_filled(&self) -> usize {
130            self.count_ones()
131        }
132
133        fn fill(&mut self, value: bool) {
134            self.fill(value);
135        }
136    }
137
138    impl PageMapSlice for BitVec {
139        fn len(&self) -> usize {
140            self.len()
141        }
142
143        fn is_set(&self, index: usize) -> bool {
144            self[index]
145        }
146
147        fn count_filled(&self) -> usize {
148            self.count_ones()
149        }
150
151        fn fill(&mut self, value: bool) {
152            self.as_mut_bitslice().fill(value);
153        }
154    }
155
156    impl PageMap for BitVec {
157        type Slice = BitSlice;
158
159        fn from_bools(iter: impl Iterator<Item = bool>) -> Self {
160            iter.collect()
161        }
162
163        fn from_residency_bytes(bytes: Vec<u8>) -> Self {
164            let len = bytes.len();
165            let bits_per_word = usize::BITS as usize;
166            let packed: Vec<usize> = bytes
167                .chunks(bits_per_word)
168                .map(|chunk| {
169                    chunk
170                        .iter()
171                        .enumerate()
172                        .fold(0usize, |acc, (i, &b)| acc | (((b != 0) as usize) << i))
173                })
174                .collect();
175            let mut bv = BitVec::from_vec(packed);
176            bv.truncate(len);
177            bv
178        }
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185    use memmap2::MmapOptions;
186    use std::io::Write;
187
188    fn create_temp_file(size: usize) -> (tempfile::NamedTempFile, Mmap) {
189        let mut f = tempfile::NamedTempFile::new().unwrap();
190        let data = vec![0xABu8; size];
191        f.write_all(&data).unwrap();
192        f.flush().unwrap();
193
194        let mmap = unsafe { MmapOptions::new().map(f.as_file()).unwrap() };
195        (f, mmap)
196    }
197
198    macro_rules! residency_tests {
199        ($t:ty, $mod:ident) => {
200            mod $mod {
201                use super::*;
202
203                #[test]
204                fn page_count() {
205                    let ps = *crate::pagesize::PAGE_SIZE;
206                    let size = ps * 4;
207                    let (_f, mmap) = create_temp_file(size);
208                    let res: $t = residency(&mmap, size).unwrap();
209                    assert_eq!(res.len(), 4);
210                }
211
212                #[test]
213                fn after_touch_all_resident() {
214                    let ps = *crate::pagesize::PAGE_SIZE;
215                    let size = ps * 4;
216                    let (_f, mmap) = create_temp_file(size);
217
218                    let mut junk: u8 = 0;
219                    for i in 0..4 {
220                        junk = junk.wrapping_add(mmap[i * ps]);
221                    }
222                    let _ = junk;
223
224                    let res: $t = residency(&mmap, size).unwrap();
225                    assert!(res.iter().all(|r| *r));
226                }
227
228                #[test]
229                fn partial_page_rounds_up() {
230                    let ps = *crate::pagesize::PAGE_SIZE;
231                    let size = ps * 3 + 1;
232                    let (_f, mmap) = create_temp_file(size);
233                    let res: $t = residency(&mmap, size).unwrap();
234                    assert_eq!(res.len(), 4);
235                }
236
237                #[test]
238                fn single_page() {
239                    let ps = *crate::pagesize::PAGE_SIZE;
240                    let (_f, mmap) = create_temp_file(ps);
241                    let _ = mmap[0];
242                    let res: $t = residency(&mmap, ps).unwrap();
243                    assert_eq!(res.len(), 1);
244                    assert_eq!(PageMapSlice::count_filled(&res), 1);
245                }
246
247                #[test]
248                fn count_filled_after_touch() {
249                    let ps = *crate::pagesize::PAGE_SIZE;
250                    let size = ps * 4;
251                    let (_f, mmap) = create_temp_file(size);
252                    let _ = mmap[0];
253                    let _ = mmap[ps * 2];
254                    let res: $t = residency(&mmap, size).unwrap();
255                    assert!(PageMapSlice::count_filled(&res) >= 2);
256                }
257
258                #[test]
259                fn slice_range() {
260                    let ps = *crate::pagesize::PAGE_SIZE;
261                    let size = ps * 4;
262                    let (_f, mmap) = create_temp_file(size);
263                    let res: $t = residency(&mmap, size).unwrap();
264                    let slice = &res[1..3];
265                    assert_eq!(PageMapSlice::len(slice), 2);
266                }
267
268                #[test]
269                fn fill_sets_all() {
270                    let mut pm = <$t>::from_bools([true, false, true, false].into_iter());
271                    PageMapSlice::fill(&mut pm, true);
272                    assert_eq!(PageMapSlice::count_filled(&pm), 4);
273                    PageMapSlice::fill(&mut pm, false);
274                    assert_eq!(PageMapSlice::count_filled(&pm), 0);
275                }
276
277                #[test]
278                fn from_bools_empty() {
279                    let pm = <$t>::from_bools(std::iter::empty());
280                    assert!(PageMapSlice::is_empty(&pm));
281                }
282
283                #[test]
284                fn from_residency_bytes_empty() {
285                    let pm = <$t>::from_residency_bytes(vec![]);
286                    assert!(PageMapSlice::is_empty(&pm));
287                }
288
289                #[test]
290                fn from_residency_bytes_all_zero() {
291                    let pm = <$t>::from_residency_bytes(vec![0; 8]);
292                    assert_eq!(PageMapSlice::len(&pm), 8);
293                    assert_eq!(PageMapSlice::count_filled(&pm), 0);
294                }
295
296                #[test]
297                fn from_residency_bytes_all_resident() {
298                    let pm = <$t>::from_residency_bytes(vec![1; 8]);
299                    assert_eq!(PageMapSlice::len(&pm), 8);
300                    assert_eq!(PageMapSlice::count_filled(&pm), 8);
301                }
302
303                #[test]
304                fn from_residency_bytes_nonzero_values() {
305                    let pm = <$t>::from_residency_bytes(vec![0xFF, 0x02, 0x80, 0]);
306                    assert_eq!(PageMapSlice::len(&pm), 4);
307                    assert_eq!(PageMapSlice::count_filled(&pm), 3);
308                }
309
310                #[test]
311                fn from_residency_bytes_single() {
312                    let resident = <$t>::from_residency_bytes(vec![1]);
313                    assert_eq!(PageMapSlice::len(&resident), 1);
314                    assert_eq!(PageMapSlice::count_filled(&resident), 1);
315
316                    let absent = <$t>::from_residency_bytes(vec![0]);
317                    assert_eq!(PageMapSlice::len(&absent), 1);
318                    assert_eq!(PageMapSlice::count_filled(&absent), 0);
319                }
320
321                #[test]
322                fn from_residency_bytes_alternating() {
323                    let pm = <$t>::from_residency_bytes(vec![1, 0, 1, 0, 1, 0]);
324                    assert_eq!(PageMapSlice::len(&pm), 6);
325                    assert_eq!(PageMapSlice::count_filled(&pm), 3);
326                }
327
328                #[test]
329                fn from_residency_bytes_matches_from_bools() {
330                    let bytes = vec![0u8, 1, 0, 0xFF, 1, 0, 0x42, 0];
331                    let from_bytes = <$t>::from_residency_bytes(bytes.clone());
332                    let from_bools = <$t>::from_bools(bytes.iter().map(|&b| b != 0));
333                    assert_eq!(
334                        PageMapSlice::len(&from_bytes),
335                        PageMapSlice::len(&from_bools)
336                    );
337                    assert_eq!(
338                        PageMapSlice::count_filled(&from_bytes),
339                        PageMapSlice::count_filled(&from_bools),
340                    );
341                }
342
343                #[test]
344                fn from_residency_bytes_across_word_boundary() {
345                    // 65 elements crosses a 64-bit word boundary
346                    let mut bytes = vec![1u8; 65];
347                    bytes[63] = 0;
348                    bytes[64] = 1;
349                    let pm = <$t>::from_residency_bytes(bytes);
350                    assert_eq!(PageMapSlice::len(&pm), 65);
351                    assert_eq!(PageMapSlice::count_filled(&pm), 64);
352                }
353            }
354        };
355    }
356
357    residency_tests!(Vec<bool>, vec_bool_impl);
358
359    #[cfg(feature = "bitvec")]
360    residency_tests!(::bitvec::vec::BitVec, bitvec_impl);
361}