Diagnostics and monitoring

<<../../_common/fidl/_diagnostics_intro.md>>

<<../../_common/fidl/_diagnostics_fidlcat.md>>

<<../../_common/fidl/_diagnostics_inspect.md>>

Exercise: Monitoring provider components

In this section, you'll use the diagnostics tools to monitor the health and behavior of the echo server component.

<<../_common/_start_femu_with_packages.md>>

Monitor FIDL traffic

You can use fidlcat to monitor and debug the FIDL connections in your components. Launch ffx debug fidl and configure it to monitor the echo server component:

ffx debug fidl --remote-name echo_server.cm
Checking for debug agent on [fe80::d6c5:4526:c282:fb6%qemu]:2345.
Debug agent not found. Starting one.
INFO: [main.cc(238)] Connected to symbol server gs://fuchsia-artifacts-release/debug
INFO: [main.cc(122)] Connecting to port 2345 on fe80::d6c5:4526:c282:fb6%qemu...
INFO: [main.cc(92)] Connected!

Initiate a FIDL connection to the server by starting an echo client instance:

ffx component start /core/ffx-laboratory:echo-realm/echo_client

The client binds to the server component and communicates using the Echo FIDL protocol. Review the ffx debug fidl output to see a list of the FIDL transactions handled by echo server:

Monitoring echo_server.cm

echo_server.cm 58694:58696 zx_channel_read_etc(handle: handle = fb9b5273, options: uint32 = 0, num_bytes: uint32 = 512, num_handles: uint32 = 4)
  -> ZX_OK
    received request fuchsia.io/Directory.Open = { flags: uint32 = 3, mode: uint32 = 493, path: string = "svc/fidl.examples.routing.echo.Echo", object: handle = Channel:f93b597b(ZX_RIGHT_TRANSFER | ZX_RIGHT_READ | ZX_RIGHT_WRITE | ZX_RIGHT_SIGNAL | ZX_RIGHT_SIGNAL_PEER | ZX_RIGHT_WAIT | ZX_RIGHT_INSPECT)(channel:0:svc/fidl.examples.routing.echo.Echo) }

echo_server.cm 58694:58696 zx_channel_read_etc(handle: handle = Channel:f93b597b(channel:0:svc/fidl.examples.routing.echo.Echo), options: uint32 = 0, num_bytes: uint32 = 512, num_handles: uint32 = 4)
  -> ZX_OK
    received request fidl.examples.routing.echo/Echo.EchoString = { value: string = "Hello, Fuchsia" }

echo_server.cm 58694:58696 zx_channel_write_etc(handle: handle = Channel:f93b597b(channel:0:svc/fidl.examples.routing.echo.Echo), options: uint32 = 0)
  sent response fidl.examples.routing.echo/Echo.EchoString = { response: string = "Hello, Fuchsia" }
  -> ZX_OK

echo_server.cm 58694:58696 zx_channel_read_etc(handle: handle = Channel:f93b597b(channel:0:svc/fidl.examples.routing.echo.Echo), options: uint32 = 0, num_bytes: uint32 = 512, num_handles: uint32 = 4)
  -> ZX_ERR_PEER_CLOSED

echo_server.cm 58694:58696 zx_handle_close(handle: handle = Channel:f93b597b(channel:0:svc/fidl.examples.routing.echo.Echo))
  -> ZX_OK

Notice the sequence of events:

  1. A channel to the protocol implementation opens at svc/fidl.examples.routing.echo.Echo.
  2. The server receives an Echo.EchoString request over the open channel, containing the string payload sent by the client.
  3. The server sends a corresponding response with the same string payload.
  4. The channel closes.

By tracing the FIDL connections between your components, fidlcat enables you to find and diagnose potential issues such as failed connections or invalid data payloads.

Add request tracking

Component inspection allows you to publish diagnostic information from your components to assist in debugging. You'll use the Inspect API to track some usage statistics for the echo server component.

Update the echo_server request handler to accept a new struct containing numeric Inspect properties for request count and bytes processed. The handler increments these properties on each incoming request:

  • {Rust}

    echo-server/src/main.rs:

    // Inspect properties managed by the server
    struct EchoConnectionStats {
        total_requests: fuchsia_inspect::UintProperty,
        bytes_processed: fuchsia_inspect::UintProperty,
    }
    
    // Handler for incoming service requests
    async fn handle_echo_request(mut stream: EchoRequestStream, stats: &EchoConnectionStats) {
        while let Some(event) = stream.try_next().await.expect("failed to serve echo service") {
            let EchoRequest::EchoString { value, responder } = event;
            responder.send(value.as_ref().map(|s| &**s)).expect("failed to send echo response");
    
            if let Some(message) = value {
                // Update Inspect property values
                stats.total_requests.add(1);
                stats.bytes_processed.add(message.len() as u64);
            }
        }
    }
    
  • {C++}

    echo-server/main.cc:

    struct EchoConnectionStats {
      inspect::UintProperty bytes_processed;
      inspect::UintProperty total_requests;
    };
    
    // Handler for incoming service requests
    class EchoImplementation : public fidl::examples::routing::echo::Echo {
    public:
      void EchoString(fidl::StringPtr value, EchoStringCallback callback) override {
        stats_->total_requests.Add(1);
        stats_->bytes_processed.Add(value->size());
        callback(std::move(value));
      }
      fidl::examples::routing::echo::Echo_EventSender* event_sender_;
      std::unique_ptr<EchoConnectionStats> stats_;
    };
    

Add the following code to main() to initialize the Inspect propertes and pass them to the updated handler:

  • {Rust}

    echo-server/src/main.rs:

    async fn main() -> Result<(), anyhow::Error> {
        // ...
    
        // Component is serving and ready to handle incoming requests
        component::health().set_ok();
    
        {{ '<strong>' }}// Create request tracking properties {{ '</strong>' }}
        {{ '<strong>' }}let root_node = component::inspector().root(); {{ '</strong>' }}
        {{ '<strong>' }}let stats = EchoConnectionStats { {{ '</strong>' }}
            {{ '<strong>' }}total_requests: root_node.create_uint("total_requests", 0), {{ '</strong>' }}
            {{ '<strong>' }}bytes_processed: root_node.create_uint("bytes_processed", 0), {{ '</strong>' }}
        {{ '<strong>' }}}; {{ '</strong>' }}
    
        {{ '<strong>' }}// Attach request handler for incoming requests {{ '</strong>' }}
        {{ '<strong>' }}service_fs {{ '</strong>' }}
            {{ '<strong>' }}.for_each_concurrent(None, |request: IncomingRequest| async { {{ '</strong>' }}
                {{ '<strong>' }}match request { {{ '</strong>' }}
                    {{ '<strong>' }}IncomingRequest::Echo(stream) => handle_echo_request(stream, &stats).await, {{ '</strong>' }}
                {{ '<strong>' }}} {{ '</strong>' }}
            {{ '<strong>' }}}) {{ '</strong>' }}
            {{ '<strong>' }}.await; {{ '</strong>' }}
    
        Ok(())
    }
    
  • {C++}

    echo-server/main.cc:

    int main(int argc, const char** argv) {
      // ...
    
      // Serve the Echo protocol
      EchoImplementation echo_instance;
      fidl::Binding<fidl::examples::routing::echo::Echo> binding(&echo_instance);
      echo_instance.event_sender_ = &binding.events();
    
      {{ '<strong>' }}// Create request tracking properties {{ '</strong>' }}
      {{ '<strong>' }}inspect::Node& root_node = inspector.root(); {{ '</strong>' }}
      {{ '<strong>' }}auto total_requests = root_node.CreateUint("total_requests", 0); {{ '</strong>' }}
      {{ '<strong>' }}auto bytes_processed = root_node.CreateUint("bytes_processed", 0); {{ '</strong>' }}
      {{ '<strong>' }}echo_instance.stats_ = std::make_unique<EchoConnectionStats>(EchoConnectionStats{ {{ '</strong>' }}
        {{ '<strong>' }}std::move(bytes_processed), {{ '</strong>' }}
        {{ '<strong>' }}std::move(total_requests), {{ '</strong>' }}
      {{ '<strong>' }}}); {{ '</strong>' }}
    
      fidl::InterfaceRequestHandler<fidl::examples::routing::echo::Echo> handler =
          [&](fidl::InterfaceRequest<fidl::examples::routing::echo::Echo> request) {
            binding.Bind(std::move(request));
          };
      context->outgoing()->AddPublicService(std::move(handler));
    
      // ...
    }
    

Finally, update the imports to include the new Inspect libraries:

  • {Rust}

    echo-server/src/main.rs:

    use anyhow::{self, Context};
    use fidl_fidl_examples_routing_echo::{EchoRequest, EchoRequestStream};
    use fuchsia_component::server::ServiceFs;
    use fuchsia_inspect::{component, health::Reporter};
    use fuchsia_inspect::NumericProperty;
    use futures::prelude::*;
    
  • {C++}

    echo-server/main.cc:

    #include <fidl/examples/routing/echo/cpp/fidl.h>
    #include <lib/async-loop/cpp/loop.h>
    #include <lib/async-loop/default.h>
    #include <lib/fidl/cpp/binding.h>
    #include <lib/inspect/cpp/inspect.h>
    #include <lib/sys/cpp/component_context.h>
    #include <lib/inspect/component/cpp/component.h>
    

Run fx build again to rebuild the component:

fx build

Verify the Inspect data

Stop the current echo-server component instance. This allows the component to resolve the latest version from the package server the next time it starts.

ffx component stop /core/ffx-laboratory:echo-realm/echo_server

Run the echo client component multiple times. This causes the request count in echo-server to increment with each connection:

ffx component start /core/ffx-laboratory:echo-realm/echo_client

ffx component start /core/ffx-laboratory:echo-realm/echo_client

ffx component start /core/ffx-laboratory:echo-realm/echo_client

View the available Inspect data for the echo server component with ffx inspect. You'll see the values for request count and bytes processed in the tree under the root node alongside the component health status:

ffx inspect show 'core/ffx-laboratory\:echo-realm/echo_server'
core/ffx-laboratory\:echo-realm/echo_server:
  metadata:
    filename = fuchsia.inspect.Tree
    component_url = #meta/echo_server.cm
    timestamp = 1476246046122
  payload:
    root:
      bytes_processed = 42
      total_requests = 3
      fuchsia.inspect.Health:
        start_timestamp_nanos = 1467828507317
        status = OK

Publishing health and behavior information using Inspect enables you to observe the current state of your components and diagnose issues on production devices.

Destroy the instance

Clean up the echo-realm instance using the following command:

ffx component destroy /core/ffx-laboratory:echo-realm

What's next?

Congratulations! You've successfully built a Fuchsia IPC interface using FIDL, and connected two components together using that interface.

You have completed all the modules in this course! Take your newfound understanding to the next level and dive deeper into the:

Fuchsia concepts