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.as_slice().len()
90    }
91
92    fn is_set(&self, index: usize) -> bool {
93        self.as_slice().is_set(index)
94    }
95
96    fn count_filled(&self) -> usize {
97        self.as_slice().count_filled()
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.as_bitslice().len()
141        }
142
143        fn is_set(&self, index: usize) -> bool {
144            self.as_bitslice().is_set(index)
145        }
146
147        fn count_filled(&self) -> usize {
148            self.as_bitslice().count_filled()
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 word_bytes = std::mem::size_of::<usize>();
166            let bits_per_word = usize::BITS as usize;
167
168            let packed: Vec<usize> = bytes
169                .chunks(bits_per_word)
170                .map(|chunk| {
171                    let mut buf = [0u8; std::mem::size_of::<usize>()];
172                    for (byte_pos, sub) in chunk.chunks(8).enumerate().take(word_bytes) {
173                        let mut packed_byte = 0u8;
174                        for (bit, &b) in sub.iter().enumerate() {
175                            packed_byte |= ((b != 0) as u8) << bit;
176                        }
177                        buf[byte_pos] = packed_byte;
178                    }
179                    usize::from_le_bytes(buf)
180                })
181                .collect();
182            let mut bv = BitVec::from_vec(packed);
183            bv.truncate(len);
184            bv
185        }
186    }
187}
188
189#[cfg(test)]
190mod tests {
191    use super::*;
192    use memmap2::MmapOptions;
193    use std::io::Write;
194
195    fn create_temp_file(size: usize) -> (tempfile::NamedTempFile, Mmap) {
196        let mut f = tempfile::NamedTempFile::new().unwrap();
197        let data = vec![0xABu8; size];
198        f.write_all(&data).unwrap();
199        f.flush().unwrap();
200
201        let mmap = unsafe { MmapOptions::new().map(f.as_file()).unwrap() };
202        (f, mmap)
203    }
204
205    macro_rules! residency_tests {
206        ($t:ty, $mod:ident) => {
207            mod $mod {
208                use super::*;
209
210                #[test]
211                fn page_count() {
212                    let ps = *crate::pagesize::PAGE_SIZE;
213                    let size = ps * 4;
214                    let (_f, mmap) = create_temp_file(size);
215                    let res: $t = residency(&mmap, size).unwrap();
216                    assert_eq!(res.len(), 4);
217                }
218
219                #[test]
220                fn after_touch_all_resident() {
221                    let ps = *crate::pagesize::PAGE_SIZE;
222                    let size = ps * 4;
223                    let (_f, mmap) = create_temp_file(size);
224
225                    let mut junk: u8 = 0;
226                    for i in 0..4 {
227                        junk = junk.wrapping_add(mmap[i * ps]);
228                    }
229                    let _ = junk;
230
231                    let res: $t = residency(&mmap, size).unwrap();
232                    assert!(res.iter().all(|r| *r));
233                }
234
235                #[test]
236                fn partial_page_rounds_up() {
237                    let ps = *crate::pagesize::PAGE_SIZE;
238                    let size = ps * 3 + 1;
239                    let (_f, mmap) = create_temp_file(size);
240                    let res: $t = residency(&mmap, size).unwrap();
241                    assert_eq!(res.len(), 4);
242                }
243
244                #[test]
245                fn single_page() {
246                    let ps = *crate::pagesize::PAGE_SIZE;
247                    let (_f, mmap) = create_temp_file(ps);
248                    let _ = mmap[0];
249                    let res: $t = residency(&mmap, ps).unwrap();
250                    assert_eq!(res.len(), 1);
251                    assert_eq!(PageMapSlice::count_filled(&res), 1);
252                }
253
254                #[test]
255                fn count_filled_after_touch() {
256                    let ps = *crate::pagesize::PAGE_SIZE;
257                    let size = ps * 4;
258                    let (_f, mmap) = create_temp_file(size);
259                    let _ = mmap[0];
260                    let _ = mmap[ps * 2];
261                    let res: $t = residency(&mmap, size).unwrap();
262                    assert!(PageMapSlice::count_filled(&res) >= 2);
263                }
264
265                #[test]
266                fn slice_range() {
267                    let ps = *crate::pagesize::PAGE_SIZE;
268                    let size = ps * 4;
269                    let (_f, mmap) = create_temp_file(size);
270                    let res: $t = residency(&mmap, size).unwrap();
271                    let slice = &res[1..3];
272                    assert_eq!(PageMapSlice::len(slice), 2);
273                }
274
275                #[test]
276                fn fill_sets_all() {
277                    let mut pm = <$t>::from_bools([true, false, true, false].into_iter());
278                    PageMapSlice::fill(&mut pm, true);
279                    assert_eq!(PageMapSlice::count_filled(&pm), 4);
280                    PageMapSlice::fill(&mut pm, false);
281                    assert_eq!(PageMapSlice::count_filled(&pm), 0);
282                }
283
284                #[test]
285                fn from_bools_empty() {
286                    let pm = <$t>::from_bools(std::iter::empty());
287                    assert!(PageMapSlice::is_empty(&pm));
288                }
289
290                #[test]
291                fn from_residency_bytes_empty() {
292                    let pm = <$t>::from_residency_bytes(vec![]);
293                    assert!(PageMapSlice::is_empty(&pm));
294                }
295
296                #[test]
297                fn from_residency_bytes_all_zero() {
298                    let pm = <$t>::from_residency_bytes(vec![0; 8]);
299                    assert_eq!(PageMapSlice::len(&pm), 8);
300                    assert_eq!(PageMapSlice::count_filled(&pm), 0);
301                }
302
303                #[test]
304                fn from_residency_bytes_all_resident() {
305                    let pm = <$t>::from_residency_bytes(vec![1; 8]);
306                    assert_eq!(PageMapSlice::len(&pm), 8);
307                    assert_eq!(PageMapSlice::count_filled(&pm), 8);
308                }
309
310                #[test]
311                fn from_residency_bytes_nonzero_values() {
312                    let pm = <$t>::from_residency_bytes(vec![0xFF, 0x02, 0x80, 0]);
313                    assert_eq!(PageMapSlice::len(&pm), 4);
314                    assert_eq!(PageMapSlice::count_filled(&pm), 3);
315                }
316
317                #[test]
318                fn from_residency_bytes_single() {
319                    let resident = <$t>::from_residency_bytes(vec![1]);
320                    assert_eq!(PageMapSlice::len(&resident), 1);
321                    assert_eq!(PageMapSlice::count_filled(&resident), 1);
322
323                    let absent = <$t>::from_residency_bytes(vec![0]);
324                    assert_eq!(PageMapSlice::len(&absent), 1);
325                    assert_eq!(PageMapSlice::count_filled(&absent), 0);
326                }
327
328                #[test]
329                fn from_residency_bytes_alternating() {
330                    let pm = <$t>::from_residency_bytes(vec![1, 0, 1, 0, 1, 0]);
331                    assert_eq!(PageMapSlice::len(&pm), 6);
332                    assert_eq!(PageMapSlice::count_filled(&pm), 3);
333                }
334
335                #[test]
336                fn from_residency_bytes_matches_from_bools() {
337                    let bytes = vec![0u8, 1, 0, 0xFF, 1, 0, 0x42, 0];
338                    let from_bytes = <$t>::from_residency_bytes(bytes.clone());
339                    let from_bools = <$t>::from_bools(bytes.iter().map(|&b| b != 0));
340                    assert_eq!(
341                        PageMapSlice::len(&from_bytes),
342                        PageMapSlice::len(&from_bools)
343                    );
344                    assert_eq!(
345                        PageMapSlice::count_filled(&from_bytes),
346                        PageMapSlice::count_filled(&from_bools),
347                    );
348                }
349
350                #[test]
351                fn from_residency_bytes_across_word_boundary() {
352                    // 65 elements crosses a 64-bit word boundary
353                    let mut bytes = vec![1u8; 65];
354                    bytes[63] = 0;
355                    bytes[64] = 1;
356                    let pm = <$t>::from_residency_bytes(bytes);
357                    assert_eq!(PageMapSlice::len(&pm), 65);
358                    assert_eq!(PageMapSlice::count_filled(&pm), 64);
359                }
360            }
361        };
362    }
363
364    residency_tests!(Vec<bool>, vec_bool_impl);
365
366    #[cfg(feature = "bitvec")]
367    residency_tests!(::bitvec::vec::BitVec, bitvec_impl);
368}