xtask\tasks/
verify_size.rs1use crate::Xtask;
5use anyhow::Context;
6use object::read::Object;
7use object::read::ObjectSection;
8use std::collections::HashSet;
9
10#[derive(Debug, clap::Parser)]
12#[clap(about = "Verify the size of a binary hasn't changed more than allowed.")]
13pub struct VerifySize {
14 #[clap(short, long)]
16 original: std::path::PathBuf,
17
18 #[clap(short, long)]
20 new: std::path::PathBuf,
21}
22
23fn verify_sections_size(
24 new: &object::File<'_>,
25 original: &object::File<'_>,
26) -> anyhow::Result<(u64, i64)> {
27 println!(
28 "{:20} {:>15} {:>15} {:>16}",
29 "Section", "Old Size (KiB)", "New Size (KiB)", "Difference (KiB)"
30 );
31
32 let mut total_diff: u64 = 0;
33 let mut net_diff: i64 = 0;
34 let mut total_size: u64 = 0;
35
36 let all_original_sections: Vec<_> = original
37 .sections()
38 .filter_map(|s| s.name().ok().map(|name| name.to_string()))
39 .collect();
40 let all_new_sections: Vec<_> = new
41 .sections()
42 .filter_map(|s| s.name().ok().map(|name| name.to_string()))
43 .collect();
44
45 let all_sections: HashSet<_> = all_original_sections
46 .into_iter()
47 .chain(all_new_sections)
48 .collect();
49
50 for section in all_sections {
51 let name = section;
52
53 let new_size = new
54 .section_by_name(name.as_str())
55 .map(|s| s.size() / 1024)
56 .unwrap_or(0);
57 let original_size = original
58 .section_by_name(name.as_str())
59 .map(|s| s.size() / 1024)
60 .unwrap_or(0);
61 let diff = (new_size as i64) - (original_size as i64);
62 total_diff += diff.unsigned_abs();
63 net_diff += diff;
64 total_size += new_size;
65
66 if new_size != original_size {
68 println!("{name:20} {original_size:15} {new_size:15} {diff:16}");
69 }
70 }
71
72 println!("Total Size: {total_size} KiB.");
73
74 Ok((total_diff, net_diff))
75}
76
77impl Xtask for VerifySize {
78 fn run(self, _ctx: crate::XtaskCtx) -> anyhow::Result<()> {
79 let original = fs_err::read(&self.original)?;
80 let new = fs_err::read(&self.new)?;
81
82 let original_elf = object::File::parse(&*original).with_context(|| {
83 format!(
84 r#"Unable to parse target file "{}"."#,
85 &self.original.display()
86 )
87 })?;
88
89 let new_elf = object::File::parse(&*new).with_context(|| {
90 format!(r#"Unable to parse target file "{}"."#, &self.new.display(),)
91 })?;
92
93 println!("Verifying size for {}:", (&self.new.display()));
94 let (total_diff, net_diff) = verify_sections_size(&new_elf, &original_elf)?;
95
96 println!("Net difference: {net_diff} KiB.");
97 println!("Total difference: {total_diff} KiB.");
98
99 const ALLOWED: u64 = 50;
100 if total_diff > ALLOWED {
101 anyhow::bail!(
102 "{} size verification failed: \
103 The total difference ({} KiB) is greater than the allowed difference ({} KiB).",
104 self.new.display(),
105 total_diff,
106 ALLOWED
107 );
108 }
109
110 Ok(())
111 }
112}