blob: 1cce8c0a5ae381cade1b9f020eaae1e4b76615a1 [file] [log] [blame]
// 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::{health, stats, Inspector, LazyNode};
use inspect_format::constants;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use std::sync::Arc;
lazy_static! {
// The size with which the default inspector is initialized.
static ref 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 ref INSPECTOR: Inspector = Inspector::new_with_size(*INSPECTOR_SIZE.lock());
// Health node based on the global inspector from `inspector()`.
static ref HEALTH: Arc<Mutex<health::Node>> = Arc::new(Mutex::new(health::Node::new(INSPECTOR.root())));
// Stats node based on the global inspector from `inspector()`.
static ref STATS: stats::Node<LazyNode> = stats::Node::new(&INSPECTOR, 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 {
lazy_static::initialize(&INSPECTOR_SIZE);
*INSPECTOR_SIZE.lock() = max_size;
lazy_static::initialize(&INSPECTOR);
&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 {
return 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() {
lazy_static::initialize(&STATS);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{assert_data_tree, health::Reporter, testing::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.vmo.as_ref().unwrap().get_size().unwrap(),
constants::DEFAULT_VMO_SIZE_BYTES as u64
);
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().vmo.as_ref().unwrap().get_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::new();
inspector.root().record_uint("a", 1);
Ok(inspector)
}
.boxed()
});
assert_data_tree!(inspector, root: {
foo: {
a: 1u64,
},
"fuchsia.inspect.Stats": {
current_size: 4096u64,
maximum_size: constants::DEFAULT_VMO_SIZE_BYTES as u64,
total_dynamic_children: 2u64,
allocated_blocks: 7u64,
deallocated_blocks: 0u64,
failed_allocations: 0u64,
}
});
}
}