1pub mod atomic_vec;
6pub use atomic_vec::{AtomicBitVec, Growable, NoOverflow, Overflow};
7
8use memory_mapped_vmo::{ReadOnlyMemoryMappedVmo, ReadWriteMemoryMappedVmo};
9use std::sync::atomic::{AtomicU64, Ordering};
10use zx::Rights;
11
12pub struct PageFaultCounter {
20 vmo: zx::Vmo,
21 _storage: ReadOnlyMemoryMappedVmo,
22 refault_count_ptr: *const AtomicU64,
23 fault_count_ptr: *const AtomicU64,
24}
25
26unsafe impl Send for PageFaultCounter {}
32unsafe impl Sync for PageFaultCounter {}
33
34const VMO_SIZE: usize = 2 * size_of::<AtomicU64>();
35const REFAULT_OFFSET: usize = 0;
36const FAULT_OFFSET: usize = size_of::<AtomicU64>();
37
38impl PageFaultCounter {
39 pub fn new() -> Result<Self, zx::Status> {
41 let vmo = zx::Vmo::create(VMO_SIZE.try_into().unwrap())?;
42 vmo.set_name(&zx::Name::new_lossy("page_fault_counter"))?;
43
44 let mut storage = unsafe { ReadWriteMemoryMappedVmo::new(&vmo)? };
46 let refault_count_ptr: *const AtomicU64 = storage
47 .get_object_mut::<AtomicU64>(REFAULT_OFFSET)
48 .map_err(|_| zx::Status::INVALID_ARGS)?;
49 let fault_count_ptr: *const AtomicU64 = storage
50 .get_object_mut::<AtomicU64>(FAULT_OFFSET)
51 .map_err(|_| zx::Status::INVALID_ARGS)?;
52 Ok(PageFaultCounter {
53 vmo,
54 _storage: storage.into_readonly(),
55 refault_count_ptr,
56 fault_count_ptr,
57 })
58 }
59
60 pub fn from_vmo_readonly(vmo: zx::Vmo) -> Result<Self, zx::Status> {
63 if vmo.get_size()? < VMO_SIZE.try_into().unwrap() {
64 return Err(zx::Status::INVALID_ARGS);
65 }
66 let storage = unsafe { ReadOnlyMemoryMappedVmo::new(&vmo)? };
68 let refault_count_ptr: *const AtomicU64 = storage
69 .get_object::<AtomicU64>(REFAULT_OFFSET)
70 .map_err(|_| zx::Status::INVALID_ARGS)?;
71 let fault_count_ptr: *const AtomicU64 =
72 storage.get_object::<AtomicU64>(FAULT_OFFSET).map_err(|_| zx::Status::INVALID_ARGS)?;
73 Ok(PageFaultCounter { vmo, _storage: storage, refault_count_ptr, fault_count_ptr })
74 }
75
76 pub fn increment_refaults(&self, count: u64, order: Ordering) {
78 unsafe { &*self.refault_count_ptr }.fetch_add(count, order);
81 }
82
83 pub fn read_refaults(&self, order: Ordering) -> u64 {
85 unsafe { &*self.refault_count_ptr }.load(order)
88 }
89
90 pub fn increment_faults(&self, count: u64, order: Ordering) {
93 unsafe { &*self.fault_count_ptr }.fetch_add(count, order);
96 }
97
98 pub fn read_faults(&self, order: Ordering) -> u64 {
100 unsafe { &*self.fault_count_ptr }.load(order)
103 }
104
105 pub fn readonly_vmo(&self) -> Result<zx::Vmo, zx::Status> {
107 self.vmo.duplicate_handle(Rights::BASIC | Rights::READ | Rights::MAP | Rights::GET_PROPERTY)
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_page_fault_counter() {
117 let counter = PageFaultCounter::new().unwrap();
118
119 let ro_vmo = counter.readonly_vmo().unwrap();
120 let ro_counter = PageFaultCounter::from_vmo_readonly(ro_vmo).unwrap();
121
122 counter.increment_refaults(100, Ordering::SeqCst);
123 counter.increment_faults(250, Ordering::SeqCst);
124 assert_eq!(ro_counter.read_refaults(Ordering::SeqCst), 100);
125 assert_eq!(ro_counter.read_faults(Ordering::SeqCst), 250);
126
127 counter.increment_refaults(100, Ordering::SeqCst);
128 counter.increment_faults(50, Ordering::SeqCst);
129 assert_eq!(ro_counter.read_refaults(Ordering::SeqCst), 200);
130 assert_eq!(ro_counter.read_faults(Ordering::SeqCst), 300);
131 }
132}