kernel/
introspection.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Mechanism for inspecting the status of the kernel.
6//!
7//! In particular this provides functions for getting the status of processes
8//! on the board. It potentially could be expanded to other kernel state.
9//!
10//! To restrict access on what can use this module, even though it is public (in
11//! a Rust sense) so it is visible outside of this crate, the introspection
12//! functions require the caller have the correct capability to call the
13//! functions. This prevents arbitrary capsules from being able to use this
14//! module, and only capsules that the board author has explicitly passed the
15//! correct capabilities to can use it.
16
17use core::cell::Cell;
18
19use crate::capabilities::ProcessManagementCapability;
20use crate::kernel::Kernel;
21use crate::process;
22use crate::process::ProcessId;
23use crate::utilities::cells::NumericCellExt;
24
25/// This struct provides the inspection functions.
26pub struct KernelInfo {
27    kernel: &'static Kernel,
28}
29
30impl KernelInfo {
31    pub fn new(kernel: &'static Kernel) -> KernelInfo {
32        KernelInfo { kernel }
33    }
34
35    /// Returns how many processes have been loaded on this platform. This is
36    /// functionally equivalent to how many of the process slots have been used
37    /// on the board. This does not consider what state the process is in, as
38    /// long as it has been loaded.
39    pub fn number_loaded_processes(&self, _capability: &dyn ProcessManagementCapability) -> usize {
40        let count: Cell<usize> = Cell::new(0);
41        self.kernel.process_each(|_| count.increment());
42        count.get()
43    }
44
45    /// Returns how many processes are considered to be active. This includes
46    /// processes in the `Running` and `Yield` states. This does not include
47    /// processes which have faulted, or processes which the kernel is no longer
48    /// scheduling because they have faulted too frequently or for some other
49    /// reason.
50    pub fn number_active_processes(&self, _capability: &dyn ProcessManagementCapability) -> usize {
51        let count: Cell<usize> = Cell::new(0);
52        self.kernel
53            .process_each(|process| match process.get_state() {
54                process::State::Running => count.increment(),
55                process::State::Yielded => count.increment(),
56                _ => {}
57            });
58        count.get()
59    }
60
61    /// Returns how many processes are considered to be inactive. This includes
62    /// processes in the `Fault` state and processes which the kernel is not
63    /// scheduling for any reason.
64    pub fn number_inactive_processes(
65        &self,
66        _capability: &dyn ProcessManagementCapability,
67    ) -> usize {
68        let count: Cell<usize> = Cell::new(0);
69        self.kernel
70            .process_each(|process| match process.get_state() {
71                process::State::Running => {}
72                process::State::Yielded => {}
73                _ => count.increment(),
74            });
75        count.get()
76    }
77
78    /// Get the name of the process.
79    pub fn process_name(
80        &self,
81        app: ProcessId,
82        _capability: &dyn ProcessManagementCapability,
83    ) -> &'static str {
84        self.kernel
85            .process_map_or("unknown", app, |process| process.get_process_name())
86    }
87
88    /// Returns the number of syscalls the app has called.
89    pub fn number_app_syscalls(
90        &self,
91        app: ProcessId,
92        _capability: &dyn ProcessManagementCapability,
93    ) -> usize {
94        self.kernel
95            .process_map_or(0, app, |process| process.debug_syscall_count())
96    }
97
98    /// Returns the number of dropped upcalls the app has experience.
99    /// Upcalls can be dropped if the queue for the app is full when a capsule
100    /// tries to schedule a upcall.
101    pub fn number_app_dropped_upcalls(
102        &self,
103        app: ProcessId,
104        _capability: &dyn ProcessManagementCapability,
105    ) -> usize {
106        self.kernel
107            .process_map_or(0, app, |process| process.debug_dropped_upcall_count())
108    }
109
110    /// Returns the number of time this app has been restarted.
111    pub fn number_app_restarts(
112        &self,
113        app: ProcessId,
114        _capability: &dyn ProcessManagementCapability,
115    ) -> usize {
116        self.kernel
117            .process_map_or(0, app, |process| process.get_restart_count())
118    }
119
120    /// Returns the number of time this app has exceeded its timeslice.
121    pub fn number_app_timeslice_expirations(
122        &self,
123        app: ProcessId,
124        _capability: &dyn ProcessManagementCapability,
125    ) -> usize {
126        self.kernel
127            .process_map_or(0, app, |process| process.debug_timeslice_expiration_count())
128    }
129
130    /// Returns a tuple of the (the number of grants in the grant region this
131    /// app has allocated, total number of grants that exist in the system).
132    pub fn number_app_grant_uses(
133        &self,
134        app: ProcessId,
135        _capability: &dyn ProcessManagementCapability,
136    ) -> (usize, usize) {
137        // Just need to get the number, this has already been finalized, but it
138        // doesn't hurt to call this again.
139        let number_of_grants = self.kernel.get_grant_count_and_finalize();
140        let used = self.kernel.process_map_or(0, app, |process| {
141            // Have process tell us the number of allocated grants. If this
142            // process isn't valid then we can't count the grants and all we can
143            // do is return 0.
144            process.grant_allocated_count().unwrap_or(0)
145        });
146
147        (used, number_of_grants)
148    }
149
150    /// Returns the total number of times all processes have exceeded
151    /// their timeslices.
152    pub fn timeslice_expirations(&self, _capability: &dyn ProcessManagementCapability) -> usize {
153        let count: Cell<usize> = Cell::new(0);
154        self.kernel.process_each(|proc| {
155            count.add(proc.debug_timeslice_expiration_count());
156        });
157        count.get()
158    }
159}