Skip to main content

stalls/
refaults.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl_fuchsia_memory_attribution as fattribution;
6use futures::TryStreamExt;
7use refaults_vmo::PageFaultCounter;
8use std::collections::HashMap;
9use std::sync::atomic::Ordering;
10use std::sync::{Arc, Mutex};
11use zx::Status;
12
13pub trait PageFaultProvider: Clone + Send + Sync + 'static {
14    /// Returns the current number of page refaults of the system, since its start, i.e. pages
15    /// resupplied after eviction.
16    fn get_refault_count(&self) -> u64;
17
18    /// Returns the total number of major page faults of the system, since its start, i.e. pages
19    /// supplied by user pagers including readahead.
20    fn get_fault_count(&self) -> u64;
21}
22
23#[derive(Default, Clone)]
24pub struct PageFaultProviderImpl {
25    inner: Arc<Mutex<Inner>>,
26}
27
28impl PageFaultProviderImpl {
29    pub async fn listen_to_page_faults(
30        &self,
31        mut stream: fattribution::PagerStatsSinkRequestStream,
32    ) -> Result<(), anyhow::Error> {
33        loop {
34            match stream.try_next().await {
35                Ok(Some(request)) => match request {
36                    fattribution::PagerStatsSinkRequest::SendPagerStats {
37                        pager_stats_vmo, ..
38                    } => {
39                        self.inner.lock().unwrap().set_new_counter(pager_stats_vmo)?;
40                    }
41                    fattribution::PagerStatsSinkRequest::_UnknownMethod { .. } => unimplemented!(),
42                },
43                Ok(None) => {
44                    return Ok(());
45                }
46                Err(e) => {
47                    return Err(e.into());
48                }
49            }
50        }
51    }
52}
53
54impl PageFaultProvider for PageFaultProviderImpl {
55    fn get_refault_count(&self) -> u64 {
56        self.inner.lock().unwrap().get_refault_count()
57    }
58
59    fn get_fault_count(&self) -> u64 {
60        self.inner.lock().unwrap().get_fault_count()
61    }
62}
63
64#[derive(Clone, Copy, PartialEq, Eq, Hash)]
65struct CounterIndex(usize);
66
67#[derive(Default)]
68struct Inner {
69    counters: HashMap<CounterIndex, PageFaultCounter>,
70}
71
72impl Inner {
73    fn set_new_counter(&mut self, pager_stats_vmo: zx::Vmo) -> Result<(), Status> {
74        let index = CounterIndex(self.counters.len());
75        self.counters.insert(index, PageFaultCounter::from_vmo_readonly(pager_stats_vmo)?);
76        Ok(())
77    }
78
79    fn get_refault_count(&self) -> u64 {
80        self.counters.values().map(|c| c.read_refaults(Ordering::Relaxed)).sum::<u64>()
81    }
82
83    fn get_fault_count(&self) -> u64 {
84        self.counters.values().map(|c| c.read_faults(Ordering::Relaxed)).sum::<u64>()
85    }
86}