blob: 9ddcfbed8d0ffa3208008c41adf1c6fc891deed0 [file] [log] [blame] [view]
# Inspect Quickstart
This quickstart guides you through the basics of using
[Component Inspection][overview]. You will learn how to integrate Inspect into
your component using the language-specific libraries and review the data using
[`ffx inspect`][ffx-inspect].
For a more detailed walkthrough of Inspect concepts, see the
[Inspect codelab](codelab/codelab.md).
## Project setup
See below for the quick start guide in your language of choice:
* {C++}
This section assumes you are writing an asynchronous component and that
some part of your component (typically `main.cc`) looks like this:
```cpp
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
auto context_ = sys::ComponentContext::CreateAndServeOutgoingDirectory();
// ...
loop.Run();
```
This sets up an async loop, creates a `ComponentContext` wrapping handles
provided by the runtime, and then runs that loop following some other
initialization work.
**Add the Inspect library dependencies to your `BUILD.gn` file:**
```gn
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/cpp/BUILD.gn" region_tag="inspect_libs" adjust_indentation="auto" %}
```
**Add the following includes:**
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/cpp/example_server_app.h" region_tag="inspect_imports" adjust_indentation="auto" %}
```
**Add the following code to initialize Inspect:**
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/cpp/example_server_app.cc" region_tag="initialization" adjust_indentation="auto" %}
```
You are now using Inspect! Create properties in the Inspect tree by attaching
them to the root node:
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/cpp/example_server_app.cc" region_tag="properties" adjust_indentation="auto" %}
```
Note: For a complete working example, see
[//examples/diagnostics/inspect/cpp](/examples/diagnostics/inspect/cpp).
See [Supported Data Types](#supported-types) for a full list of data
types you can try.
#### Health checks
The health check subsystem provides a standardized inspection metric for
component health. You can use the health node to report the overall status
of your component:
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/cpp/example_server_app.cc" region_tag="health_check" adjust_indentation="auto" %}
```
Note: For more details on health metrics, see [Health check][health-check].
#### Testing
To test your inspect code, you can use
[//sdklib/inspect/testing/cpp/inspect.h](/sdk/lib/inspect/testing):
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/cpp/example_unittests.cc" region_tag="test_imports" adjust_indentation="auto" %}
```
This library includes a full set of matchers to validate the contents of the
Inspect tree.
```cpp
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/cpp/example_unittests.cc" region_tag="inspect_test" adjust_indentation="auto" %}
```
* {Rust}
This section assumes you are writing an asynchronous component and that some
part of your component (typically `main.rs`) looks similar to this:
```rust
async fn main() -> Result<(), Error> {
// ...
let mut service_fs = ServiceFs::new();
// ...
service_fs.take_and_serve_directory_handle().unwrap();
service_fs.collect::<()>().await;
Ok(())
}
```
**Add the Inspect library dependencies to your `BUILD.gn` file:**
```gn
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust/BUILD.gn" region_tag="inspect_libs" adjust_indentation="auto" %}
```
**Add the following code to initialize Inspect:**
```rust
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust/src/echo_server.rs" region_tag="initialization" adjust_indentation="auto" %}
```
You are now using Inspect! Create properties in the Inspect tree by attaching
them to the root node:
```rust
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust/src/echo_server.rs" region_tag="properties" adjust_indentation="auto" %}
```
Note: For a complete working example, see
[//examples/diagnostics/inspect/rust](/examples/diagnostics/inspect/rust).
See [Supported Data Types](#supported-types) for a full list of data
types you can try.
#### Health checks
The health check subsystem provides a standardized inspection metric for
component health. You can use the health node to report the overall status
of your component:
```rust
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust/src/echo_server.rs" region_tag="health_check" adjust_indentation="auto" %}
```
Note: For more details on health metrics, see [Health check][health-check].
#### Testing
To test your Inspect code, you can use `assert_data_tree` to validate the
contents of the Inspect tree:
```rust
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust/src/echo_server.rs" region_tag="inspect_test" adjust_indentation="auto" %}
```
Note: To learn more about the Rust library, see the complete reference for
[`fuchsia_inspect`](https://fuchsia-docs.firebaseapp.com/rust/fuchsia_inspect/index.html).
## Inspect Libraries {#inspect-libraries}
<!-- TODO(https://fxbug.dev/42165233): Replace code snippets with examples -->
Now that you have a `root_node` you may start building your
hierarchy. This section describes some important concepts and patterns
to help you get started.
* A Node may have any number of key/value pairs called **Properties**.
* The key for a Value is always a UTF-8 string, the value may be one of the
[supported types](#supported-types) below.
* A Node may have any number of children, which are also Nodes.
* {C++}
The code above gives you access to a single node named
"root". `hello_world_property` is a Property that contains a string value
(aptly called a **StringProperty**).
* Values and Nodes are created under a parent Node.
Class `Node` has creator methods for every type of
supported value. `hello_world_property` was created using
`CreateStringProperty`. You could create a child under the root node
by calling `root_node.CreateChild("child name")`. Note that names must
always be UTF-8 strings.
* Values and Nodes have strict ownership semantics.
`hello_world_property` owns the Property. When it is destroyed (goes
out of scope) the underlying Property is deleted and no longer present
in your component's Inspect output. This is true for child Nodes as well.
If you are creating a value that doesn't need to be modified, use a
[`ValueList`](/zircon/system/ulib/inspect/include/lib/inspect/cpp/vmo/types.h)
to keep them alive until they are no longer needed.
* Inspection is best-effort.
Due to space limitations, the Inspect library may be unable to satisfy
a `Create` request. This error is not surfaced to your code: you will
receive a Node/Property object for which the methods are no-ops.
* Pattern: Pass in child Nodes to child objects.
It is useful to add an `inspect::Node` argument to the constructors
for your own classes. The parent object, which should own its own
`inspect::Node`, may then pass in the result of `CreateChild(...)`
to its children when they are constructed:
```cpp
class Child {
public:
Child(inspect::Node my_node) : my_node_(std::move(my_node)) {
// Create a string that doesn't change, and emplace it in the ValueList
my_node_.CreateString("version", "1.0", &values_);
// Create metrics and properties on my_node_.
}
private:
inspect::Node my_node_;
inspect::StringProperty some_property_;
inspect::ValueList values_;
// ... more properties and metrics
};
class Parent {
public:
// ...
void AddChild() {
// Note: inspect::UniqueName returns a globally unique name with the specified prefix.
children_.emplace_back(my_node_.CreateChild(inspect::UniqueName("child-")));
}
private:
std::vector<Child> children_;
inspect::Node my_node_;
};
```
* {Rust}
The Rust library provides two ways of managing nodes and properties: creation
and recording.
With the `create_*` methods, the ownership of the property or node object belongs
to the caller. When the returned object is dropped, the property is removed.
For example:
```rust
{
let property = root.create_int("name", 1);
}
```
In this example, `property` went out of scope so a drop on the property is
called. Readers won't see this property.
With the `record_*` methods, the lifetime of the property is tied to the parent
node. When the node is deleted, the recorded property is deleted.
```rust
{
let node = root.create_child("name");
{
node.record_uint(2); // no return
}
// The uint property will still be visible to readers.
}
```
In this example, the uint property associated with `name` is visible to readers
until the parent `node` goes out of scope.
### Dynamic values
This section describes support in the Inspect libraries for nodes that are
inflated lazily at read-time. The methods accept a callback function instead of
a value. The callback function is invoked when the property value is read.
* {C++}
The C++ library has two property creators for dynamic values:
`CreateLazyNode` and `CreateLazyValues`.
Both of these methods take a callback returning a promise for an
`inspect::Inspector`, the only difference is how the dynamic values are
stored in the tree.
`root->CreateLazyNode(name, callback)` creates a child node of
`root` with the given `name`. The `callback` returns a promise for an
`inspect::Inspector` whose root node is spliced into the parent hierarchy
when read. The example below shows that a child called "lazy" exists with
the string property "version" and has an additional child that is called
"lazy."
`root->CreateLazyValues(name, callback)` works like `root->CreateLazyNode(name,
callback)`, except all properties and child nodes on the promised root node are
added directly as values
to the original `root`. In the second output of this example, the internal
lazy nodes do not appear and their values are flattened into properties on
`root`.
```cpp
root->CreateLazy{Node,Values}("lazy", [] {
Inspector a;
a.GetRoot().CreateString("version", "1.0", &a);
a.GetRoot().CreateLazy{Node,Values}("lazy", [] {
Inspector b;
b.GetRoot().RecordInt("value", 10);
return fpromise::make_ok_promise(std::move(b));
}, &a);
return fpromise::make_ok_promise(std::move(a));
});
```
Output (CreateLazyNode):
```
root:
lazy:
version = "1.0"
lazy:
value = 10
```
Output (CreateLazyValues):
```
root:
value = 10
version = "1.0"
```
Warning: It is the developer's responsibility to ensure that names
flattened from multiple lazy value nodes do not conflict. If they do,
output behavior is undefined.
The return value of `CreateLazy{Node,Values}` is a `LazyNode` that owns
the passed callback. The callback is never called once the `LazyNode` is
destroyed. If you destroy a `LazyNode` concurrently with the execution of
a callback, the destroy operation is blocked until the callback returns
its promise.
If you want to dynamically expose properties on `this`, you may simply
write the following:
```cpp
class Employee {
public:
Employee(inspect::Node node) : node_(std::move(node)) {
calls_ = node_.CreateInt("calls", 0);
// Create a lazy node that populates values on its parent
// dynamically.
// Note: The callback will never be called after the LazyNode is
// destroyed, so it is safe to capture "this."
lazy_ = node_.CreateLazyValues("lazy", [this] {
// Create a new Inspector and put any data in it you want.
inspect::Inspector inspector;
// Keep track of the number of times this callback is executed.
// This is safe because the callback is executed without locking
// any state in the parent node.
calls_.Add(1);
// ERROR: You cannot modify the LazyNode from the callback. Doing
// so may deadlock!
// lazy_ = ...
// The value is set to the result of calling a method on "this".
inspector.GetRoot().RecordInt("performance_score",
this->CalculatePerformance());
// Callbacks return a fpromise::promise<Inspector>, so return a result
// promise containing the value we created.
// You can alternatively return a promise that is completed by
// some asynchronous task.
return fpromise::make_ok_promise(std::move(inspector));
});
}
private:
inspect::Node node_;
inspect::IntProperty calls_;
inspect::LazyNode lazy_;
};
```
* {Rust}
Refer to [C++ Dynamic Value Support](#c++), as similar concepts apply in Rust.
Example:
```rust
root.create_lazy_{child,values}("lazy", [] {
async move {
let inspector = Inspector::default();
inspector.root().record_string("version", "1.0");
inspector.root().record_lazy_{node,values}("lazy", || {
let inspector = Inspector::default();
inspector.root().record_int("value", 10);
// `_value`'s drop is called when the function returns, so it will be removed.
// For these situations `record_` is provided.
let _value = inspector.root().create_int("gone", 2);
Ok(inspector)
});
Ok(inspector)
}
.boxed()
});
Output (create_lazy_node):
root:
lazy:
version = "1.0"
lazy:
value = 10
Output (create_lazy_values):
root:
value = 10
version = "1.0"
```
### String references {#string-reference}
* {C++}
You can use `inspect::StringReference` to reduce the memory footprint
of an Inspect hierarchy that has a lot of repeated data. For instance,
```cpp
using inspect::Inspector;
Inspector inspector;
for (int i = 0; i < 100; i++) {
inspector.GetRoot().CreateChild("child", &inspector);
}
```
Will include 100 copies of the string `"child"` in your inspect
output.
Alternatively,
```cpp
using inspect::Inspector;
using inspect::StringReference;
namespace {
const StringReference kChild("child");
}
Inspector inspector;
for (int i = 0; i < 100; i++) {
inspector.GetRoot().CreateChild(kChild, &inspector)
}
```
Will generate only one copy of `"child"` which is referenced 100 times.
This saves 16 bytes for each child node, and has a cost of 32 bytes
for the shared data. The net result is a savings of 1568 bytes.
This pattern is recommended anywhere a global constant key would be used.
* {Rust}
String names are automatically de-duplicated in Rust Inspect. For example,
```rust
use fuchsia_inspect::Inspector;
let inspector = Inspector::default();
for _ in 0..100 {
inspector.root().record_child("child");
}
```
Will generate only 1 copy of `"child"` which is referenced 100 times.
This saves 16 bytes for each child node, and has a cost of 32 bytes
for the shared data. The net result is a savings of 1568 bytes.
## Viewing Inspect Data {#view-inspect-data}
You can use the [`ffx inspect`][ffx-inspect] command to view the Inspect data
you exported from your component.
This section assumes you have SSH access to your running Fuchsia system and
that you started running your component. We will use the name
`my_component.cm` as a placeholder for the name of your component's manifest.
Note: Your component's full URL may be
`fuchsia-pkg://fuchsia.com/my_component#meta/my_component.cm`, but
you only need the manifest name to find it. This is separate from
your component's *[moniker]*, which is the location it is running in
the component hierarchy. You may refer to your component by either
moniker or manifest name.
### Read your Inspect data
The command below prints the inspect hierarchies of all components
running in the system:
```posix-terminal
ffx inspect show
```
Using the output from `ffx inspect list`, you can specify a
single component (for example, `my_component.cm`) as input to
`ffx inspect show`:
```posix-terminal
ffx inspect show --manifest my_component.cm
```
Specifying `--manifest` above will return data for all instances
of your component running on the system. If you know a specific
moniker of your component (for example, `core/my_component`) you
may pass that instead:
```posix-terminal
ffx inspect show core/my_component
```
You may specify multiple components (for example, `core/font_provider`
and `core/my_component`):
```posix-terminal
ffx inspect show core/font_provider core/my_component
```
You can also specify a node and property value. To see the
list of all possible [selectors], use `ffx inspect selectors`:
```posix-terminal
ffx inspect selectors core/my_component
```
You may then specify a selector (for example, `core/my_component:root`)
as input to `ffx inspect show`:
```posix-terminal
ffx inspect show core/my_component:root
```
If you don't know the moniker for your component, you may use
`--manifest` with a selector that applies to all matched component
monikers (using `*`):
```posix-terminal
ffx inspect show --manifest my_component.cm *:root
```
This will print out the following if you followed the suggested
steps above:
```none {:.devsite-disable-click-to-copy}
root:
hello = world
```
## Supported Data Types {#supported-types}
Type | Description | Notes
-----|-------------|-------
IntProperty | A metric containing a signed 64-bit integer. | All Languages
UIntProperty | A metric containing an unsigned 64-bit integer. | Not supported in Dart
DoubleProperty | A metric containing a double floating-point number. | All Languages
BoolProperty | A metric containing a double floating-point number. | All Languages
{Int,Double,Uint}Array | An array of metric types, includes typed wrappers for various histograms. | Same language support as base metric type
StringArray | An array of strings. Represented as a [StringReference](#string-reference). | Not supported in Dart.
StringProperty | A property with a UTF-8 string value. | All Languages
ByteVectorProperty | A property with an arbitrary byte value. | All Languages
Node | A node under which metrics, properties, and more nodes may be nested. | All Languages
LazyNode | Instantiates a complete tree of Nodes dynamically. | C++, Rust
<!-- Reference links -->
[ffx-inspect]: https://fuchsia.dev/reference/tools/sdk/ffx.md#inspect
[health-check]: /docs/development/diagnostics/inspect/health.md
[overview]: /docs/development/diagnostics/inspect/README.md
[moniker]: /docs/reference/components/moniker.md
[selectors]: /docs/reference/diagnostics/selectors.md