blob: ecdbfdd096efe6ed1e0a06517a568eadb517b399 [file] [log] [blame]
// Copyright 2019 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.
use {
anyhow::Error,
fuchsia_async as fasync,
fuchsia_component::server::ServiceFs,
fuchsia_inspect as inspect,
futures::stream::StreamExt,
std::str::FromStr,
structopt::{self, StructOpt},
};
static MAX_VMO_SIZE: usize = 100_000_000;
/// Create and serve a VMO that is at least `vmo_size` in size.
#[derive(Debug, StructOpt)]
struct ProgramArgs {
/// The size of the Inspect VMO to create.
vmo_size: InspectVmoSize,
}
/// The size of the Inspect VMO to be exposed, in bytes.
#[derive(Debug, PartialEq)]
struct InspectVmoSize(usize);
impl FromStr for InspectVmoSize {
type Err = String;
fn from_str(s: &str) -> Result<Self, String> {
let parsed = s.parse::<usize>().or_else(|e| Err(format!("{:?}", e)))?;
validate_vmo_size(parsed)?;
Ok(InspectVmoSize(parsed))
}
}
fn validate_vmo_size(size: usize) -> Result<(), String> {
if size > MAX_VMO_SIZE {
Err(format!("vmo_size {} is greater than maximum {}", size, MAX_VMO_SIZE))
} else if size == 0 {
Err(String::from("vmo_size cannot be 0"))
} else {
Ok(())
}
}
#[fasync::run_singlethreaded]
async fn main() -> Result<(), Error> {
let args = ProgramArgs::from_args();
let InspectVmoSize(vmo_size) = args.vmo_size;
let inspector = inspect::Inspector::new_with_size(vmo_size);
// Ensures that the Inspector is actually exposing information on the hub. Most components do
// not need to do this since Inspect is best-effort (it will not crash your component if it
// fails to initialize). We have this check because the whole point of this component is to
// expose Inspect information on the hub.
assert!(inspector.is_valid());
// Retain properties so that they are not deleted from the VMO.
let _properties = build_hierarchy(inspector.root(), vmo_size);
let mut fs = ServiceFs::new();
inspector.serve(&mut fs)?;
fs.take_and_serve_directory_handle()?;
fs.collect::<()>().await;
Ok(())
}
// Builds a hierarchy under the given node that is guaranteed to fill up at least |vmo_size| bytes.
//
// Returns the properties so they can be retained, otherwise they would be deleted.
#[must_use]
fn build_hierarchy(node: &inspect::Node, vmo_size: usize) -> Vec<inspect::IntProperty> {
let mut elements = vec![];
let mut size = 0;
while size < vmo_size {
// Keep pushing elements until we hit the desired size.
// Elements have a 16 byte node, 16 byte value, and 16 byte name.
elements.push(node.create_int(format!("{}", size), size as i64));
size += 16 * 3;
}
elements
}
#[cfg(test)]
mod tests {
use super::*;
use fuchsia_inspect::assert_inspect_tree;
#[test]
fn build_hierarchy_sizes() {
// Make 1MiB inspector to hold all hierarchies.
let inspector = inspect::Inspector::new_with_size(1024 * 1024);
let node_256 = inspector.root().create_child("node_256");
let node_4096 = inspector.root().create_child("node_4096");
let node_256k = inspector.root().create_child("node_256k");
let mut properties = vec![];
properties.append(&mut build_hierarchy(&node_256, 256));
properties.append(&mut build_hierarchy(&node_4096, 4096));
properties.append(&mut build_hierarchy(&node_256k, 256 * 1024));
assert_eq!(
256 / 16 / 3 + 4096 / 16 / 3 + 256 * 1024 / 16 / 3 + 3, /* remainders */
properties.len()
);
assert_inspect_tree!(inspector, root: {
node_256:
contains {
"0": 0i64,
"240": 240i64,
}
,
node_4096: contains {
"0": 0i64,
"4080": 4080i64,
},
node_256k: contains {
"0": 0i64,
"262128": 262_128i64,
}
});
}
#[test]
fn vmo_size_arg() {
assert_eq!(
InspectVmoSize(200_000),
InspectVmoSize::from_str("200000").expect("200K is valid")
);
assert_eq!(InspectVmoSize(64), InspectVmoSize::from_str("64").expect("64 is valid"));
}
#[test]
fn invalid_vmo_size_arg() {
InspectVmoSize::from_str("200000000").expect_err("200M should fail");
InspectVmoSize::from_str("300000000").expect_err("300M should fail");
InspectVmoSize::from_str("400000000").expect_err("400M should fail");
InspectVmoSize::from_str("one").expect_err("text should fail");
InspectVmoSize::from_str("0").expect_err("zero should fail");
}
}