| # 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). |
| |
| |
| * {Dart} |
| |
| This example obtains and adds several data types and nested children to the |
| root Inspect node. |
| |
| `BUILD.gn`: |
| |
| ```dart |
| flutter_app("inspect_mod") { |
| [...] |
| deps = [ |
| [...] |
| "//sdk/dart/fuchsia_inspect", |
| [...] |
| ] |
| [...] |
| |
| ``` |
| |
| ```dart {highlight="lines:6"} |
| import 'package:fuchsia_inspect/inspect.dart' as inspect; |
| [...] |
| class RootIntentHandler extends IntentHandler { |
| @override |
| void handleIntent(Intent intent) { |
| var inspectNode = inspect.Inspect().root; |
| runApp(InspectExampleApp(inspectNode)); |
| } |
| } |
| ``` |
| |
| `inspect_example_app.dart`: |
| |
| ```dart {highlight="lines:4,7-10,16"} |
| import 'package:fuchsia_inspect/inspect.dart' as inspect; |
| |
| class InspectExampleApp extends StatelessWidget { |
| final inspect.Node _inspectNode; |
| |
| InspectExampleApp(this._inspectNode) { |
| _inspectNode.stringProperty('greeting').setValue('Hello World'); |
| _inspectNode.doubleProperty('double down')..setValue(1.23)..add(2); |
| _inspectNode.intProperty('interesting')..setValue(123)..subtract(5); |
| _inspectNode.byteDataProperty('bytes').setValue(ByteData(4)..setUint32(0, 0x01020304)); |
| } |
| @override |
| Widget build(BuildContext context) { |
| return MaterialApp( |
| home: _InspectHomePage( |
| inspectNode: _inspectNode.child('home-page')), |
| [...] |
| } |
| ``` |
| |
| You can call `delete()` on a Node or Property when you're done with it. |
| Deleting a node deletes everything under it. |
| |
| `delete()` can also be triggered by a Future completing or broadcast Stream |
| closing: |
| |
| ```dart |
| var answerFuture = _answerFinder.getTheAnswer(); |
| var wait = _inspectNode.stringProperty('waiting')..setValue('for a hint'); |
| answerFuture.whenComplete(wait.delete); |
| |
| stream.listen((_) {}, onDone: node.delete); |
| |
| // FIDL proxies contain a future that completes when the connection closes: |
| final _proxy = my_fidl_import.MyServiceProxy(); |
| _proxy.ctrl.whenClosed.whenComplete(node.delete); |
| ``` |
| |
| ## Inspect Libraries {#inspect-libraries} |
| |
| <!-- TODO(fxbug.dev/84444): 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/value_list.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} |
| |
| Refer to [C++ Library Concepts](#c++), as similar concepts apply in 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 to the caller. |
| When the returned object is dropped, it is removed. For example: |
| |
| ```rust |
| { |
| let property = root.create_int("name", 1); |
| } |
| ``` |
| |
| In this example, the 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 node the method is |
| called on is entangled with the resulting property. When the node the method was called |
| 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, neither the `name` node nor the uint property is visible to readers |
| after `node` is dropped. |
| |
| ### 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().CreateInt("value", 10, &b); |
| return fit::make_ok_promise(std::move(b)); |
| }, &a); |
| |
| return fit::make_ok_promise(std::move(a)); |
| }); |
| ``` |
| |
| Output (CreateLazyNode): |
| |
| ``` |
| root: |
| lazy: |
| version = "1.0" |
| lazy: |
| val = 10 |
| ``` |
| |
| Output (CreateLazyValues): |
| |
| ``` |
| root: |
| val = 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().CreateInt("performance_score", |
| this->CalculatePerformance(), &inspector); |
| |
| // 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 fit::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::new(); |
| inspector.root().record_string("version", "1.0"); |
| inspector.root().record_lazy_{node,values}("lazy", || { |
| let inspector = Inspector::new(); |
| 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: |
| val = 10 |
| |
| Output (create_lazy_values): |
| root: |
| val = 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 pattern is recommended anywhere a global constant key would be used. |
| |
| * {Rust} |
| |
| You can use `fuchsia_inspect::StringReference` to reduce the memory footprint |
| of an Inspect hierarchy that has a lot of repeated data. For instance, |
| |
| ```rust |
| use fuchisa_inspect::Inspector; |
| |
| let inspector = Inspector::new(); |
| for _ in 0..100 { |
| inspector.root().record_child("child"); |
| } |
| ``` |
| |
| Will generate 100 copies of `"child"`. |
| |
| Alternatively, |
| |
| ```rust |
| use fuchsia_inspect::{Inspector, StringReference} |
| |
| lazy_static! { |
| static ref CHILD: StringReference<'static> = "child".into(); |
| } |
| |
| let inspector = Inspector::new(); |
| for _ in 0..100 { |
| inspector.root().record_child(&*CHILD); |
| } |
| ``` |
| |
| Will generate only 1 copy of `"child"` which is referenced 100 times. |
| |
| This pattern is recommended anytime a global constant key would be used. |
| |
| Note: It isn't necessary for the `StringReference` to be static. |
| It can also take a `String`, owning it. |
| |
| ## 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.cmx` as a placeholder for the name of your component. |
| |
| ### Find your Inspect endpoint |
| |
| The command below prints all available components that expose inspect: |
| |
| ```posix-terminal |
| ffx inspect list |
| ``` |
| |
| The command below prints all `fuchsia.diagnostics.ArchiveAccessor` paths: |
| |
| ```posix-terminal |
| ffx inspect list-accessors |
| ``` |
| Your component's endpoint is listed as |
| `<path>/my_component.cmx/<id>/out/diagnostics/fuchsia.inspect.Tree`. |
| However, in some languages (without dynamic value support) and in drivers, |
| the data is placed in VMO files instead. In that case, the endpoint is listed |
| as `<path>/my_component.cmx/<id>/out/diagnostics/root.inspect`. |
| |
| An accessor path listed by `ffx inspect list-accessors` can later be used by |
| `ffx inspect show` and `ffx inspect selectors` using the `--accessor-path` flag. |
| |
| The command below prints all available selectors for a component |
| (for example, `my_component.cmx`): |
| |
| ```posix-terminal |
| ffx inspect selectors my_component.cmx |
| ``` |
| |
| ### 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.cmx`) as input to |
| `ffx inspect show`: |
| |
| ```posix-terminal |
| ffx inspect show my_component.cmx |
| ``` |
| |
| Or specify multiple components (for example, `core/font_provider` |
| and `my_component.cmx`): |
| |
| ```posix-terminal |
| ffx inspect show core/font_provider my_component.cmx |
| ``` |
| |
| You can also specify a node and property value (for example, |
| `my_component.cmx:root.inspect)` from `ffx inspect selectors` |
| as input to `ffx inspect show`: |
| |
| ```posix-terminal |
| ffx inspect show my_component.cmx: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 |