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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! # Component inspection utilities
//!
//!
//! This module contains standardized entry points to the Fuchsia inspect subsystem. It works based
//! on the assumpton that a top-level static [`Inspector`][Inspector] is desirable.
//!
//! The [`inspector()`][inspector] function can be used to get a top level inspector, which ensures
//! consistent inspect behavior across components.
//!
//! Use the [`health()`][health] function to report the component health state through the
//! component inspector.
//!
//! While using the component inspector is not mandatory, it is probably a good idea from the
//! standpoint of uniform reporting.
//!
//! # Examples
//!
//! ```rust
//! use fuchsia_inspect::component;
//! let inspector = component::inspector();
//! // Add a standardized health node to the default inspector as early as possible in code.
//! // The component will report `STARTING_UP` as the status from here on.
//! let mut health = component::health();
//!
//! // Add a node with a metric to the inspector.
//! inspector.root().create_string("property", "value");
//!
//! // Report the component health as `OK` when ready. Calls to `health` are thread-safe.
//! health.set_ok();
//! ```
use super::stats::InspectorExt;
use super::{health, Inspector, InspectorConfig};
use fuchsia_sync::Mutex;
use inspect_format::constants;
use std::sync::{Arc, LazyLock};
// The size with which the default inspector is initialized.
static INSPECTOR_SIZE: Mutex<usize> = Mutex::new(constants::DEFAULT_VMO_SIZE_BYTES);
// The component-level inspector. We probably want to use this inspector across components where
// practical.
static INSPECTOR: LazyLock<Inspector> =
LazyLock::new(|| Inspector::new(InspectorConfig::default().size(*INSPECTOR_SIZE.lock())));
// Health node based on the global inspector from `inspector()`.
static HEALTH: LazyLock<Arc<Mutex<health::Node>>> =
LazyLock::new(|| Arc::new(Mutex::new(health::Node::new(INSPECTOR.root()))));
/// A thread-safe handle to a health reporter. See `component::health()` for instructions on how
/// to create one.
pub struct Health {
// The thread-safe component health reporter that reports to the top-level inspector.
health_node: Arc<Mutex<health::Node>>,
}
// A thread-safe implementation of a global health reporter.
impl health::Reporter for Health {
fn set_starting_up(&mut self) {
self.health_node.lock().set_starting_up();
}
fn set_ok(&mut self) {
self.health_node.lock().set_ok();
}
fn set_unhealthy(&mut self, message: &str) {
self.health_node.lock().set_unhealthy(message);
}
}
/// Returns the singleton component inspector.
///
/// It is recommended that all health nodes register with this inspector (as opposed to any other
/// that may have been created).
pub fn inspector() -> &'static Inspector {
&INSPECTOR
}
/// Initializes and returns the singleton component inspector.
pub fn init_inspector_with_size(max_size: usize) -> &'static Inspector {
*INSPECTOR_SIZE.lock() = max_size;
&INSPECTOR
}
/// Returns a handle to the standardized singleton top-level health reporter on each call.
///
/// Calling this function installs a health reporting child node below the default inspector's
/// `root` node. When using it, consider using the default inspector for all health reporting, for
/// uniformity: `fuchsia_inspect::component::inspector()`.
///
/// # Caveats
///
/// The health reporting node is created when it is first referenced. It is advisable to reference
/// it as early as possible, so that it could export a `STARTING_UP` health status while the
/// component is initializing.
pub fn health() -> Health {
Health { health_node: HEALTH.clone() }
}
/// Serves statistics about inspect such as size or number of dynamic children in the
/// `fuchsia.inspect.Stats` lazy node.
pub fn serve_inspect_stats() {
INSPECTOR.record_lazy_stats();
}
#[cfg(test)]
mod tests {
use super::*;
use crate::health::Reporter;
use diagnostics_assertions::{assert_data_tree, assert_json_diff, AnyProperty};
use futures::FutureExt;
#[fuchsia::test]
fn health_checker_lifecycle() {
let inspector = super::inspector();
// In the beginning, the inspector has no stats.
assert_data_tree!(inspector, root: contains {});
let mut health = health();
assert_data_tree!(inspector,
root: contains {
"fuchsia.inspect.Health": {
status: "STARTING_UP",
start_timestamp_nanos: AnyProperty,
}
});
health.set_ok();
assert_data_tree!(inspector,
root: contains {
"fuchsia.inspect.Health": {
status: "OK",
start_timestamp_nanos: AnyProperty,
}
});
health.set_unhealthy("Bad state");
assert_data_tree!(inspector,
root: contains {
"fuchsia.inspect.Health": {
status: "UNHEALTHY",
message: "Bad state",
start_timestamp_nanos: AnyProperty,
}
});
// Verify that the message changes.
health.set_unhealthy("Another bad state");
assert_data_tree!(inspector,
root: contains {
"fuchsia.inspect.Health": {
status: "UNHEALTHY",
message: "Another bad state",
start_timestamp_nanos: AnyProperty,
}
});
// Also verifies that there is no more message.
health.set_ok();
assert_data_tree!(inspector,
root: contains {
"fuchsia.inspect.Health": {
status: "OK",
start_timestamp_nanos: AnyProperty,
}
});
}
#[fuchsia::test]
fn record_on_inspector() {
let inspector = super::inspector();
assert_eq!(inspector.max_size().unwrap(), constants::DEFAULT_VMO_SIZE_BYTES);
inspector.root().record_int("a", 1);
assert_data_tree!(inspector, root: contains {
a: 1i64,
})
}
#[fuchsia::test]
fn init_inspector_with_size() {
super::init_inspector_with_size(8192);
assert_eq!(super::inspector().max_size().unwrap(), 8192);
}
#[fuchsia::test]
fn inspect_stats() {
let inspector = super::inspector();
super::serve_inspect_stats();
inspector.root().record_lazy_child("foo", || {
async move {
let inspector = Inspector::default();
inspector.root().record_uint("a", 1);
Ok(inspector)
}
.boxed()
});
assert_json_diff!(inspector, root: {
foo: {
a: 1u64,
},
"fuchsia.inspect.Stats": {
current_size: 4096u64,
maximum_size: constants::DEFAULT_VMO_SIZE_BYTES as u64,
utilization_per_ten_k: 156u64,
total_dynamic_children: 2u64,
allocated_blocks: 7u64,
deallocated_blocks: 0u64,
failed_allocations: 0u64,
}
});
}
}