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}