诊断和监控

在组件内连接到 FIDL 协议是功能路由和目录传送的组合。这意味着,诊断连接问题可以涵盖以下几个不同的层面:

  • 客户端在其清单中请求协议功能。
  • 提供程序在其清单中公开协议功能。
  • 组件拓扑会将功能从提供程序路由到客户端。
  • 提供商通过正确的标识名提供协议。
  • 客户端尝试连接到正确的协议句柄。

在本部分中,您将探索一些 API 和工具,以帮助您查找和修复组件连接问题,并监控组件的长期运行状况。

监控 FIDL 连接

借助 fidlcat 工具,您可以监控和调试 FIDL 连接,以跟踪组件收发的各条 FIDL 消息。与 Fuchsia 调试程序 (zxdb) 类似,fidlcat 会连接到目标设备上正在运行的 debug_agent 组件,并监控正在运行的进程。

显示“fidlcat”如何与 Fuchsia 设备上运行的 debug_agent 服务交互,以监控和调试指定进程的 FIDL 调用的示意图。

设置监控会话需要完成以下简要步骤:

  1. 在目标设备上运行 debug_agent 组件。
  2. 运行 fidlcat 客户端并连接到目标设备。

启动 FIDL 调试会话的最简单方法是使用 ffx debug fidl 命令,该命令将在本地 Fuchsia build 环境中执行所有这些操作。不过,如果您需要单独配置这些步骤,也可以手动执行这些步骤。

以下是 FIDL 协议请求的 fidlcat 消息示例。轨迹输出包含每项翻译的实用信息,包括:

  • 组件或进程名称
  • 调用的系统调用
  • FIDL 库、协议和方法名称
  • 包含参数或返回值的消息载荷
echo-client.cm 256109:256122 zx_channel_read(handle:handle: e4c7c57f, options:uint32: 0, num_bytes:uint32: 48, num_handles:uint32: 0)
  -> ZX_OK
    received response fidl.examples.echo/Echo.EchoString = {
      response: string = "hello world!"
    }

使用检查

借助组件检查,Fuchsia 组件可以使用 Inspect API 显示有关自身的结构化诊断信息。Fuchsia 通过开发者工具和 bug 报告提供此信息,以帮助诊断问题或监控性能。

组件以名为 Node 的树的形式公开检查指标,每个节点都包含一组以键值对形式表示的 Properties。属性支持各种数字、字符串和数组数据类型。组件检查器库提供了指向组件的根节点的接口,您可以在根节点中将其他感兴趣的属性附加到应用。

此树形图显示了组件检查如何以“节点”树的形式提供结构化指标数据,其中每个节点可以包含一个或多个键值对“属性”。

您可以使用开发者工具检索当前发布到 Inspect 的指标组:

  • ffx inspect:可让您使用组件选择器以交互方式查询 Inspect 状态。这有助于在开发期间调试组件。
  • ffx target snapshot:捕获整个系统的调试快照归档文件,其中包含 JSON 格式的检查数据。
ffx inspect show core/foo-example
core/foo-example:
  metadata:
    filename = fuchsia.inspect.Tree
    component_url = fuchsia-pkg://fuchsia.com/foo-example#meta/foo-example.cm
    timestamp = 55457379176
  payload:
    root:
      version = 1.0
      request_metrics:
        request_count = 3
        error = timeout

练习:监控提供程序组件

在本部分中,您将使用诊断工具监控 echo 服务器组件的运行状况和行为。

启动模拟器

如果您尚未运行实例,请启动模拟器:

  1. 启动一个新的模拟器实例:

    ffx emu start --headless
    

    启动完成后,模拟器会输出以下消息并返回:

    Logging to "$HOME/.local/share/Fuchsia/ffx/emu/instances/fuchsia-emulator/emulator.log"
    Waiting for Fuchsia to start (up to 60 seconds)........
    Emulator is ready.
    
  2. 启动软件包服务器,以使模拟器能够加载软件包:

    fx serve
    

监控 FIDL 流量

您可以使用 fidlcat 监控和调试组件中的 FIDL 连接。启动 ffx debug fidl 并将其配置为监控 echo 服务器组件:

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!

通过启动 echo 客户端实例启动与服务器的 FIDL 连接:

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

客户端绑定到服务器组件并使用 Echo FIDL 协议进行通信。查看 ffx debug fidl 输出以查看由 echo 服务器处理的 FIDL 事务的列表:

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

请注意事件的顺序:

  1. 协议实现的通道在 svc/fidl.examples.routing.echo.Echo 处打开。
  2. 服务器通过开放通道收到 Echo.EchoString 请求,其中包含客户端发送的字符串载荷。
  3. 服务器发送具有相同字符串载荷的相应响应。
  4. 渠道结束。

通过跟踪组件之间的 FIDL 连接,您可以利用 fidlcat 查找和诊断潜在问题,例如连接失败或数据载荷无效。

添加请求跟踪

借助组件检查功能,您可以发布组件的诊断信息,以帮助进行调试。您将使用 Inspect API 跟踪 echo 服务器组件的一些使用情况统计信息。

更新 echo_server 请求处理程序,以接受包含数字值的新结构体;检查请求数和处理的字节数属性。该处理程序会针对每个传入请求递增这些属性:

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_;
};

将以下代码添加到 main() 以初始化 Inspect 属性,并将其传递给更新后的处理程序:

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();

    // Create request tracking properties 
    let root_node = component::inspector().root(); 
    let stats = EchoConnectionStats { 
        total_requests: root_node.create_uint("total_requests", 0), 
        bytes_processed: root_node.create_uint("bytes_processed", 0), 
    }; 

    // Attach request handler for incoming requests 
    service_fs 
        .for_each_concurrent(None, |request: IncomingRequest| async { 
            match request { 
                IncomingRequest::Echo(stream) => handle_echo_request(stream, &stats).await, 
            } 
        }) 
        .await; 

    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();

  // Create request tracking properties 
  inspect::Node& root_node = inspector.root(); 
  auto total_requests = root_node.CreateUint("total_requests", 0); 
  auto bytes_processed = root_node.CreateUint("bytes_processed", 0); 
  echo_instance.stats_ = std::make_unique<EchoConnectionStats>(EchoConnectionStats{ 
    std::move(bytes_processed), 
    std::move(total_requests), 
  }); 

  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));

  // ...
}

最后,更新导入内容以包含新的 Inspect 库:

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>

再次运行 fx build 以重新构建组件:

fx build

验证检查数据

停止当前的 echo-server 组件实例。这样,组件在下次启动时就可以从软件包服务器解析最新版本。

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

多次运行 echo 客户端组件。这会导致以 echo-server 为单位的请求计数随每个连接而递增:

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

使用 ffx inspect 查看 echo 服务器组件的可用检查数据。您可以在 root 节点下的树状结构中查看请求数和处理的字节数值以及组件的运行状况:

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

通过使用检查功能发布运行状况和行为信息,您可以观察组件的当前状态,并诊断正式版设备上的问题。

销毁实例

使用以下命令清理 echo-realm 实例:

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

后续操作

恭喜!您已成功使用 FIDL 构建了一个 Fuchsia IPC 接口,并使用该接口将两个组件连接在一起。

您已完成本课程中的所有单元!让您对新知识的理解更上一层楼,并深入了解以下内容:

紫红色概念