Skip to main content

flowey_lib_hvlite/_jobs/
check_openvmm_hcl_size.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Compares the size of the OpenHCL binary in the current PR with the size of the binary from the last successful merge to main.
5
6use crate::artifact_openhcl_igvm_from_recipe_extras;
7use crate::build_openhcl_igvm_from_recipe;
8use crate::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
9use crate::build_openvmm_hcl;
10use crate::build_openvmm_hcl::OpenvmmHclBuildParams;
11use crate::build_openvmm_hcl::OpenvmmHclBuildProfile::OpenvmmHclShip;
12use crate::common::CommonArch;
13use crate::common::CommonTriple;
14use flowey::node::prelude::*;
15use flowey_lib_common::download_gh_artifact;
16use flowey_lib_common::gh_workflow_id;
17use flowey_lib_common::git_merge_commit;
18
19flowey_request! {
20    pub struct Request {
21        pub target: CommonTriple,
22        pub done: WriteVar<SideEffect>,
23        pub pipeline_name: String,
24        pub job_name: String,
25    }
26}
27
28new_simple_flow_node!(struct Node);
29
30impl SimpleFlowNode for Node {
31    type Request = Request;
32
33    fn imports(ctx: &mut ImportCtx<'_>) {
34        ctx.import::<crate::build_xtask::Node>();
35        ctx.import::<crate::git_checkout_openvmm_repo::Node>();
36        ctx.import::<download_gh_artifact::Node>();
37        ctx.import::<git_merge_commit::Node>();
38        ctx.import::<gh_workflow_id::Node>();
39        ctx.import::<build_openhcl_igvm_from_recipe::Node>();
40        ctx.import::<build_openvmm_hcl::Node>();
41        ctx.import::<artifact_openhcl_igvm_from_recipe_extras::publish::Node>();
42    }
43
44    fn process_request(request: Self::Request, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
45        let Request {
46            target,
47            done,
48            pipeline_name,
49            job_name,
50        } = request;
51
52        let xtask_target = CommonTriple::Common {
53            arch: ctx.arch().try_into()?,
54            platform: ctx.platform().try_into()?,
55        };
56
57        let xtask = ctx.reqv(|v| crate::build_xtask::Request {
58            target: xtask_target,
59            xtask: v,
60        });
61        let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
62
63        let recipe = match target.common_arch().unwrap() {
64            CommonArch::X86_64 => OpenhclIgvmRecipe::X64,
65            CommonArch::Aarch64 => OpenhclIgvmRecipe::Aarch64,
66        }
67        .recipe_details(true);
68
69        let built_openvmm_hcl = ctx.reqv(|v| build_openvmm_hcl::Request {
70            build_params: OpenvmmHclBuildParams {
71                target: target.clone(),
72                profile: OpenvmmHclShip,
73                features: recipe.openvmm_hcl_features,
74                no_split_dbg_info: false,
75                max_trace_level: recipe.max_trace_level,
76            },
77            openvmm_hcl_output: v,
78        });
79
80        let file_name = match target.common_arch().unwrap() {
81            CommonArch::X86_64 => "x64-openhcl-baseline",
82            CommonArch::Aarch64 => "aarch64-openhcl-baseline",
83        };
84
85        let merge_commit = ctx.reqv(|v| git_merge_commit::Request {
86            repo_path: openvmm_repo_path.clone(),
87            merge_commit: v,
88            base_branch: "main".into(),
89        });
90
91        let merge_run = ctx.reqv(|v| {
92            gh_workflow_id::Request::WithStatusAndJob(gh_workflow_id::QueryWithStatusAndJob {
93                params: gh_workflow_id::WorkflowQueryParams {
94                    github_commit_hash: merge_commit,
95                    repo_path: openvmm_repo_path.clone(),
96                    pipeline_name,
97                    gh_workflow: v,
98                },
99                gh_run_status: gh_workflow_id::GhRunStatus::Completed,
100                gh_run_job_name: job_name,
101            })
102        });
103
104        let run_id = merge_run.map(ctx, |r| r.id);
105        let merge_head_artifact = ctx.reqv(|old_openhcl| download_gh_artifact::Request {
106            repo_owner: "microsoft".into(),
107            repo_name: "openvmm".into(),
108            file_name: file_name.into(),
109            path: old_openhcl,
110            run_id,
111        });
112
113        // Publish the built binary as an artifact for offline analysis.
114        //
115        // FUTURE: Flowey should have a general mechanism for this. We cannot
116        // use the existing artifact support because all artifacts are only
117        // published at the end of the job, if everything else succeeds.
118        let publish_artifact = if ctx.backend() == FlowBackend::Github {
119            let dir = ctx.emit_rust_stepv("collect openvmm_hcl files for analysis", |ctx| {
120                let built_openvmm_hcl = built_openvmm_hcl.clone().claim(ctx);
121                move |rt| {
122                    let built_openvmm_hcl = rt.read(built_openvmm_hcl);
123                    let path = Path::new("artifact");
124                    fs_err::create_dir_all(path)?;
125                    fs_err::copy(built_openvmm_hcl.bin, path.join("openvmm_hcl"))?;
126                    if let Some(dbg) = built_openvmm_hcl.dbg {
127                        fs_err::copy(dbg, path.join("openvmm_hcl.dbg"))?;
128                    }
129                    Ok(path
130                        .absolute()?
131                        .into_os_string()
132                        .into_string()
133                        .ok()
134                        .unwrap())
135                }
136            });
137            let name = format!(
138                "{}_openvmm_hcl_for_size_analysis",
139                target.common_arch().unwrap().as_arch()
140            );
141            Some(
142                ctx.emit_gh_step(
143                    "publish openvmm_hcl for analysis",
144                    "actions/upload-artifact@v7",
145                )
146                .with("name", name)
147                .with("path", dir)
148                .finish(ctx),
149            )
150        } else {
151            None
152        };
153
154        let comparison = ctx.emit_rust_step("binary size comparison", |ctx| {
155            // Ensure the artifact is published before the analysis since this step may fail.
156            let _publish_artifact = publish_artifact.claim(ctx);
157            let xtask = xtask.claim(ctx);
158            let openvmm_repo_path = openvmm_repo_path.claim(ctx);
159            let old_openhcl = merge_head_artifact.claim(ctx);
160            let new_openhcl = built_openvmm_hcl.claim(ctx);
161            let merge_run = merge_run.claim(ctx);
162
163            move |rt| {
164                let xtask = match rt.read(xtask) {
165                    crate::build_xtask::XtaskOutput::LinuxBin { bin, .. } => bin,
166                    crate::build_xtask::XtaskOutput::WindowsBin { exe, .. } => exe,
167                };
168
169                let old_openhcl = rt.read(old_openhcl);
170                let new_openhcl = rt.read(new_openhcl);
171                let merge_run = rt.read(merge_run);
172
173                let old_path = old_openhcl.join(file_name).join("openhcl");
174                let new_path = new_openhcl.bin;
175
176                println!(
177                    "comparing HEAD to merge commit {} and workflow {}",
178                    merge_run.commit, merge_run.id
179                );
180
181                let path = rt.read(openvmm_repo_path);
182                rt.sh.change_dir(path);
183                flowey::shell_cmd!(
184                    rt,
185                    "{xtask} verify-size --original {old_path} --new {new_path}"
186                )
187                .run()?;
188
189                Ok(())
190            }
191        });
192
193        ctx.emit_side_effect_step(vec![comparison], [done]);
194
195        Ok(())
196    }
197}