1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Mechanism for inspecting the status of the kernel.
//!
//! In particular this provides functions for getting the status of processes
//! on the board. It potentially could be expanded to other kernel state.
//!
//! To restrict access on what can use this module, even though it is public (in
//! a Rust sense) so it is visible outside of this crate, the introspection
//! functions require the caller have the correct capability to call the
//! functions. This prevents arbitrary capsules from being able to use this
//! module, and only capsules that the board author has explicitly passed the
//! correct capabilities to can use it.

use core::cell::Cell;

use crate::callback::AppId;
use crate::capabilities::ProcessManagementCapability;
use crate::common::cells::NumericCellExt;
use crate::process;
use crate::sched::Kernel;

/// This struct provides the inspection functions.
pub struct KernelInfo {
    kernel: &'static Kernel,
}

impl KernelInfo {
    pub fn new(kernel: &'static Kernel) -> KernelInfo {
        KernelInfo { kernel: kernel }
    }

    /// Returns how many processes have been loaded on this platform. This is
    /// functionally equivalent to how many of the process slots have been used
    /// on the board. This does not consider what state the process is in, as
    /// long as it has been loaded.
    pub fn number_loaded_processes(&self, _capability: &dyn ProcessManagementCapability) -> usize {
        let count: Cell<usize> = Cell::new(0);
        self.kernel.process_each(|_| count.increment());
        count.get()
    }

    /// Returns how many processes are considered to be active. This includes
    /// processes in the `Running` and `Yield` states. This does not include
    /// processes which have faulted, or processes which the kernel is no longer
    /// scheduling because they have faulted too frequently or for some other
    /// reason.
    pub fn number_active_processes(&self, _capability: &dyn ProcessManagementCapability) -> usize {
        let count: Cell<usize> = Cell::new(0);
        self.kernel
            .process_each(|process| match process.get_state() {
                process::State::Running => count.increment(),
                process::State::Yielded => count.increment(),
                _ => {}
            });
        count.get()
    }

    /// Returns how many processes are considered to be inactive. This includes
    /// processes in the `Fault` state and processes which the kernel is not
    /// scheduling for any reason.
    pub fn number_inactive_processes(
        &self,
        _capability: &dyn ProcessManagementCapability,
    ) -> usize {
        let count: Cell<usize> = Cell::new(0);
        self.kernel
            .process_each(|process| match process.get_state() {
                process::State::Running => {}
                process::State::Yielded => {}
                _ => count.increment(),
            });
        count.get()
    }

    /// Get the name of the process.
    pub fn process_name(
        &self,
        app: AppId,
        _capability: &dyn ProcessManagementCapability,
    ) -> &'static str {
        self.kernel
            .process_map_or("unknown", app.idx(), |process| process.get_process_name())
    }

    /// Returns the number of syscalls the app has called.
    pub fn number_app_syscalls(
        &self,
        app: AppId,
        _capability: &dyn ProcessManagementCapability,
    ) -> usize {
        self.kernel
            .process_map_or(0, app.idx(), |process| process.debug_syscall_count())
    }

    /// Returns the number of dropped callbacks the app has experience.
    /// Callbacks can be dropped if the queue for the app is full when a capsule
    /// tries to schedule a callback.
    pub fn number_app_dropped_callbacks(
        &self,
        app: AppId,
        _capability: &dyn ProcessManagementCapability,
    ) -> usize {
        self.kernel.process_map_or(0, app.idx(), |process| {
            process.debug_dropped_callback_count()
        })
    }

    /// Returns the number of time this app has been restarted.
    pub fn number_app_restarts(
        &self,
        app: AppId,
        _capability: &dyn ProcessManagementCapability,
    ) -> usize {
        self.kernel
            .process_map_or(0, app.idx(), |process| process.debug_restart_count())
    }

    /// Returns the number of time this app has exceeded its timeslice.
    pub fn number_app_timeslice_expirations(
        &self,
        app: AppId,
        _capability: &dyn ProcessManagementCapability,
    ) -> usize {
        self.kernel.process_map_or(0, app.idx(), |process| {
            process.debug_timeslice_expiration_count()
        })
    }

    /// Returns the total number of times all processes have exceeded
    /// their timeslices.
    pub fn timeslice_expirations(&self, _capability: &dyn ProcessManagementCapability) -> usize {
        let count: Cell<usize> = Cell::new(0);
        self.kernel.process_each(|proc| {
            count.add(proc.debug_timeslice_expiration_count());
        });
        count.get()
    }
}