Rust 压力测试库

本文档介绍了如何使用 Rust 压力测试编写压力测试 库。该库可在以下位置找到://src/sys/lib/stress-test。它会实现测试循环, 运行这些测试所需的并发和同步基元。

编写压力测试

定义 GN build 目标

定义 rustc_binaryfuchsia_componentfuchsia_test_package GN build 目标 测试:

rustc_binary("filesystem-stressor-bin") {
    deps = [
        ...
        "//src/sys/lib/stress-test",
        ...
    ]
    sources = [
        ...
    ]
}

fuchsia_component("filesystem-stressor") {
    deps = [ ":filesystem-stressor-bin" ]
    manifest = "meta/filesystem-stressor.cml"
    testonly = true
}

fuchsia_test_package("filesystem-stress-tests") {
  test_components = [
    ":filesystem-stressor"
  ]
}

写一个演员

每个 activity 都必须实现 Actor trait。执行者特征是一种 perform() 方法,即 由 ActorRunner 调用。在被调用时,执行者必须只执行一项操作并返回 将其结果提供给运行程序。操作者必须存储执行操作所需的所有连接。

pub trait Actor: Sync + Send + 'static {
    // ActorRunner invokes this function, instructing the actor
    // to perform exactly one operation and return result.
    async fn perform(&mut self) -> Result<(), ActorError>;
}

操作者可以在返回结果中指明以下内容:

  • Ok(()):操作成功并添加到全局操作计数中。

  • Err(ActorError::DoNotCount):操作不得计入全局操作 计数。

  • Err(ActorError::ResetEnvironment):必须重置环境,且不得执行此操作 会计入全局操作次数

当操作者遇到意外错误时,它应 panic,从而停止测试。

由于操作者是在同一环境中进行操作,因此他们的操作可能会 碰撞。例如,对于文件系统压力测试,参与者可以对同一组文件执行操作。 如果需要此类冲突,则必须设置 actor 以妥善处理此类冲突。 否则,执行者会恐慌,导致测试停止。

操作者可能会故意破坏被测系统,从而需要重置环境。对于 例如,对于文件系统压力测试,参与者可以随机切断 文件系统和底层块存储设备在此示例中,其他操作者应请求 ActorError::ResetEnvironment,并且环境将重新建立连接

pub struct FilesystemActor {
    /// Store a connection to the root of filesystem here
    pub root_directory: Directory
    ...
}

impl FilesystemActor {
    pub fn new(root_directory: Directory) -> Self {
        ...
    }
}

#[async_trait]
impl Actor for FilesystemActor {
    async pub fn perform(&mut self) -> Result<(), ActorError> {
        // Choose exactly one operation to do on the filesystem
        // using the root_directory
        self.root_directory.delete_all_files();
    }
}

编写环境

该环境为压力测试提供了基本配置,即退出标准、 演员和重置方法。

pub trait Environment: Send + Sync + Debug {
    /// Returns the target number of operations to complete before exiting
    fn target_operations(&self) -> Option<u64>;

    /// Returns the number of seconds to wait before exiting
    fn timeout_seconds(&self) -> Option<u64>;

    /// Return the runners for all the actors
    async fn actor_runners(&mut self) -> Vec<ActorRunner>;

    /// Reset the environment, when an actor requests one
    async fn reset(&mut self);
}

环境可以存储测试的其他配置。您可以提供此配置 使用 argh crate 替换命令行。

actor 在运行程序和环境之间共享,因此必须封装为 Arc<Mutex<dyn Actor>>。当操作者执行操作时,运行者会持有锁。 这意味着环境只能在操作间获取操作者的锁。

当操作者确定 被测系统发生故障。环境应为 并锁定操作者以更新其与新实例的连接。

环境还必须实现 Debug trait。压力测试会记录环境 在测试开始时以及测试 panic 时发出通知。常见的做法是输出 这对于重现测试很有价值,例如所使用的随机种子。

#[derive(Debug)]
pub struct FilesystemEnvironment {
    fs_actor: Arc<Mutex<FilesystemActor>>,
    seed: u64,
    ...
}

impl Environment {
    pub fn new() -> Self {
        ...
    }
}

#[async_trait]
impl Environment for FilesystemEnvironment {
    fn target_operations(&self) -> Option<u64> {
        // By specifying None here, the test will run without an operation limit
        None
    }

    fn timeout_seconds(&self) -> Option<u64> {
        // By specifying None here, the test will run without a time limit
        None
    }

    async fn actor_runners(&mut self) -> Vec<ActorRunner> {
        vec![
            ActorRunner::new(
                "filesystem_actor",  // debug name
                60,  // delay (in seconds) between each operation (0 means no delay)
                self.fs_actor.clone()), // actor
            )
        ]
    }

    async fn reset(&mut self) {
        // If the actor is performing an operation, this will remain
        // locked until the operation is complete.
        let actor = self.fs_actor.lock().await;

        // Now the environment can update the actor before it is run again.
        actor.root_directory = ...;

        // Releasing the lock will resume the runner.
    }
}

编写主函数

压力测试的主要功能非常简单,因为其大部分逻辑都是 在环境和参与者中实现的效果。使用 main 函数来收集命令行 参数(如果有),初始化日志记录并设置日志严重性。

#[fuchsia::main]
async fn main() {
    // Create the environment
    let env = FilesystemEnvironment::new();

    // Run the test.
    // Depending on the exit criteria, this may never return.
    stress_test::run_test(env).await;
}

在本地运行压力测试

由于压力测试是 fuchsia_test_package 的一部分,因此最简单的运行方式之一 与 fx test 命令结合使用:

fx test filesystem-stress-tests

如需使用自定义命令行参数运行测试,请使用 fx shell run

fx shell run fuchsia-pkg://fuchsia.com/filesystem-stress-tests#meta/filesystem-stressor.cm <args>

在基础架构上运行压力测试

压力测试由基础架构通过附加到 stress-tests 的标记进行标识, fuchsia_test_packagefuchsia_unittest_package GN Build 目标。

fuchsia_test_package("filesystem-stress-tests") {
  test_components = [
    ":filesystem-stressor"
  ]
  test_specs = {
    environments = [
      {
        dimensions = {
          device_type = "QEMU"
        }
        tags = [ "stress-tests" ]
      },
    ]
  }
}

专用的 core.x64-stress 构建器可以标识这些测试并运行 最长的 22 小时

调试压力测试

该框架使用 Rust log crate 记录消息。该测试会在以下位置记录环境对象: 以及测试是否出现了问题

--------------------- stressor is starting -----------------------
Environment {
    seed: 268479717856254664270968796173957499835,
    filesystem_actor: { ... }
    ...
}
------------------------------------------------------------------

如果启用了调试日志记录,还会记录各个执行者操作和操作计数。

DEBUG: [0][filesystem_actor][389] Sleeping for 2 seconds
DEBUG: [0][filesystem_actor][389] Performing...
DEBUG: [0][filesystem_actor][389] Done!
DEBUG: Counters -> [total:403] {"filesystem_actor": 403}