hermit-verify 0.4.0

Run-to-run verification harness for the Hermit deterministic execution engine.
/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::path::PathBuf;

use clap::Parser;

use crate::CommonOpts;
use crate::cli_wrapper::Hermit;
use crate::common::TemporaryEnvironment;
use crate::common::TemporaryEnvironmentBuilder;
use crate::use_case::UseCase;

/// Verification utility for "hermit run" subcommand.
/// This utility runs the guest program twice and compares the output generated by hermit providing feedback to the caller.
/// The outputs (stdout, stderr, log file) are captured and placed in a temporary directory allowing further inspection (if --keep-temp-dir is provided).
#[derive(Parser, Debug)]
pub struct RunOpts {
    /// Whether to keep temp directory created for each container run. This directory contains (stdout, stderr, log file, etc) of the container process.
    /// This allows manual inspection of container outputs in cases when a cause of failure is unclear.
    #[clap(long)]
    keep_temp_dir: bool,

    /// Underlying hermit container will receive "isolated" workdir
    #[clap(long)]
    isolate_workdir: bool,

    /// Whether to run underlying hermit containers concurrently
    #[clap(long)]
    concurrent: bool,

    // Hermit runs count
    #[clap(long, default_value = "2")]
    run_count: i32,

    /// Additional argument for hermit run subcommand
    #[clap(long, short = 'a')]
    hermit_arg: Vec<String>,

    /// Path to a guest program
    #[clap(value_name = "PROGRAM")]
    guest_program: PathBuf,
    /// Arguments for a guest program
    #[clap(value_name = "ARGS")]
    args: Vec<String>,
}

impl UseCase for RunOpts {
    fn build_temp_env(
        &self,
        common_args: &CommonOpts,
    ) -> crate::common::TemporaryEnvironmentBuilder {
        TemporaryEnvironmentBuilder::new()
            .persist_temp_dir(self.keep_temp_dir)
            .temp_dir_path(common_args.temp_dir_path.as_ref())
            .run_count(self.run_count)
    }

    fn build_first_hermit_args(
        &self,
        temp_env: &TemporaryEnvironment,
        current_run: &crate::common::RunEnvironment,
    ) -> Vec<String> {
        Hermit::new()
            .log_level(tracing::Level::DEBUG)
            .log_file(current_run.log_file_path.clone())
            .run(self.guest_program.clone(), self.args.clone())
            .hermit_args(self.hermit_arg.clone())
            .workdir_isolate(current_run.workdir.clone(), self.isolate_workdir)
            .bind(temp_env.path().to_owned())
            .into_args()
    }

    fn build_next_hermit_args(
        &self,
        _run_no: usize,
        temp_env: &TemporaryEnvironment,
        _prev_run: &crate::common::RunEnvironment,
        current_run: &crate::common::RunEnvironment,
    ) -> Vec<String> {
        self.build_first_hermit_args(temp_env, current_run)
    }
}