Skip to main content

flowey_lib_hvlite/
run_vmm_perf.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Run the standalone VMM.Perf runner.
5
6use flowey::node::prelude::*;
7
8#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
9pub enum VmmPerfProfile {
10    BootTime,
11    Fio,
12    Iperf3,
13}
14
15impl VmmPerfProfile {
16    pub fn all() -> Vec<Self> {
17        vec![Self::BootTime, Self::Fio, Self::Iperf3]
18    }
19
20    fn cli_name(self) -> &'static str {
21        match self {
22            Self::BootTime => "boot-time",
23            Self::Fio => "fio",
24            Self::Iperf3 => "iperf3",
25        }
26    }
27}
28
29#[derive(Clone, Debug, Serialize, Deserialize)]
30pub struct VmmPerfRunOutput {
31    pub results_dir: PathBuf,
32    pub success: bool,
33    pub exit_code: Option<i32>,
34}
35
36flowey_request! {
37    pub struct Request {
38        pub runner: ReadVar<crate::build_vmm_perf::VmmPerfOutput>,
39        pub openvmm: ReadVar<crate::build_openvmm::OpenvmmOutput>,
40        pub firmware: ReadVar<PathBuf>,
41        pub runtime_archive: ReadVar<PathBuf>,
42        pub output_dir: ReadVar<PathBuf>,
43        pub temp_dir: Option<ReadVar<PathBuf>>,
44        pub profiles: Vec<VmmPerfProfile>,
45        pub vm_sizes_json: Option<String>,
46        pub parameters_json: Option<String>,
47        /// Wait for host dependencies to be installed before running VMM.Perf.
48        pub pre_run_deps: Vec<ReadVar<SideEffect>>,
49        pub output: WriteVar<VmmPerfRunOutput>,
50    }
51}
52
53new_simple_flow_node!(struct Node);
54
55impl SimpleFlowNode for Node {
56    type Request = Request;
57
58    fn imports(_ctx: &mut ImportCtx<'_>) {}
59
60    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
61        let Request {
62            runner,
63            openvmm,
64            firmware,
65            runtime_archive,
66            output_dir,
67            temp_dir,
68            profiles,
69            vm_sizes_json,
70            parameters_json,
71            pre_run_deps,
72            output,
73        } = request;
74
75        ctx.emit_rust_step("run VMM.Perf", |ctx| {
76            pre_run_deps.claim(ctx);
77            let runner = runner.claim(ctx);
78            let openvmm = openvmm.claim(ctx);
79            let firmware = firmware.claim(ctx);
80            let runtime_archive = runtime_archive.claim(ctx);
81            let output_dir = output_dir.claim(ctx);
82            let temp_dir = temp_dir.map(|temp_dir| temp_dir.claim(ctx));
83            let output = output.claim(ctx);
84
85            move |rt| {
86                let runner = match rt.read(runner) {
87                    crate::build_vmm_perf::VmmPerfOutput::LinuxBin { bin, .. } => bin,
88                    crate::build_vmm_perf::VmmPerfOutput::WindowsBin { exe, .. } => exe,
89                };
90                let openvmm = match rt.read(openvmm) {
91                    crate::build_openvmm::OpenvmmOutput::LinuxBin { bin, .. } => bin,
92                    crate::build_openvmm::OpenvmmOutput::WindowsBin { exe, .. } => exe,
93                };
94                let firmware = rt.read(firmware);
95                let runtime_archive = rt.read(runtime_archive);
96                let output_dir = rt.read(output_dir).absolute()?;
97                let temp_dir = temp_dir
98                    .map(|temp_dir| rt.read(temp_dir).absolute())
99                    .transpose()?;
100
101                runner.make_executable()?;
102                openvmm.make_executable()?;
103                fs_err::create_dir_all(&output_dir)?;
104                if let Some(temp_dir) = &temp_dir {
105                    fs_err::create_dir_all(temp_dir)?;
106                }
107
108                let mut args = vec![
109                    "--openvmm".to_string(),
110                    openvmm.display().to_string(),
111                    "--firmware".to_string(),
112                    firmware.display().to_string(),
113                    "--runtime-archive".to_string(),
114                    runtime_archive.display().to_string(),
115                    "--output-dir".to_string(),
116                    output_dir.display().to_string(),
117                ];
118                if let Some(temp_dir) = &temp_dir {
119                    args.push("--temp-dir".into());
120                    args.push(temp_dir.display().to_string());
121                }
122                for profile in &profiles {
123                    args.push("--profile".into());
124                    args.push(profile.cli_name().into());
125                }
126                if let Some(vm_sizes_json) = &vm_sizes_json {
127                    args.push("--vm-sizes-json".into());
128                    args.push(vm_sizes_json.clone());
129                }
130                if let Some(parameters_json) = &parameters_json {
131                    args.push("--parameters-json".into());
132                    args.push(parameters_json.clone());
133                }
134
135                let process = flowey::shell_cmd!(rt, "{runner} {args...}")
136                    .ignore_status()
137                    .output()?;
138                if !process.stdout.is_empty() {
139                    log::info!("{}", String::from_utf8_lossy(&process.stdout));
140                }
141                if !process.stderr.is_empty() {
142                    log::warn!("{}", String::from_utf8_lossy(&process.stderr));
143                }
144
145                rt.write(
146                    output,
147                    &VmmPerfRunOutput {
148                        results_dir: output_dir,
149                        success: process.status.success(),
150                        exit_code: process.status.code(),
151                    },
152                );
153                Ok(())
154            }
155        });
156
157        Ok(())
158    }
159}