layout_audit/
cli.rs

1use clap::{Parser, Subcommand, ValueEnum};
2use std::path::PathBuf;
3
4#[derive(Parser)]
5#[command(name = "layout-audit")]
6#[command(
7    author,
8    version,
9    about = "Analyze binary memory layouts to detect padding inefficiencies"
10)]
11#[command(
12    long_about = "layout-audit parses DWARF debugging information to visualize the physical \
13layout of data structures, detect padding holes, and analyze cache line efficiency.\n\n\
14Example:\n  layout-audit inspect ./target/debug/myapp --filter MyStruct"
15)]
16pub struct Cli {
17    #[command(subcommand)]
18    pub command: Commands,
19}
20
21#[derive(Subcommand)]
22pub enum Commands {
23    /// Analyze and display struct layouts from a binary
24    Inspect {
25        /// Path to the binary file to analyze
26        #[arg(value_name = "BINARY")]
27        binary: PathBuf,
28
29        /// Filter structs by name (substring match)
30        #[arg(short, long)]
31        filter: Option<String>,
32
33        /// Output format
34        #[arg(short, long, value_enum, default_value = "table")]
35        output: OutputFormat,
36
37        /// Sort structs by field
38        #[arg(short, long, value_enum, default_value = "name")]
39        sort_by: SortField,
40
41        /// Show only the top N structs (by sort order)
42        #[arg(short = 'n', long)]
43        top: Option<usize>,
44
45        /// Show only structs with at least N bytes of padding
46        #[arg(long)]
47        min_padding: Option<u64>,
48
49        /// Disable colored output
50        #[arg(long)]
51        no_color: bool,
52
53        /// Cache line size in bytes (must be > 0)
54        #[arg(long, default_value = "64", value_parser = clap::value_parser!(u32).range(1..))]
55        cache_line: u32,
56
57        /// Pretty-print JSON output
58        #[arg(long)]
59        pretty: bool,
60
61        /// Warn about potential false sharing between atomic members on the same cache line
62        #[arg(long)]
63        warn_false_sharing: bool,
64    },
65
66    /// Compare struct layouts between two binaries
67    Diff {
68        /// Path to the old (baseline) binary
69        #[arg(value_name = "OLD")]
70        old: PathBuf,
71
72        /// Path to the new binary
73        #[arg(value_name = "NEW")]
74        new: PathBuf,
75
76        /// Filter structs by name (substring match)
77        #[arg(short, long)]
78        filter: Option<String>,
79
80        /// Output format
81        #[arg(short, long, value_enum, default_value = "table")]
82        output: OutputFormat,
83
84        /// Cache line size in bytes (must be > 0)
85        #[arg(long, default_value = "64", value_parser = clap::value_parser!(u32).range(1..))]
86        cache_line: u32,
87
88        /// Exit with error code 1 if any regressions found (size or padding increased)
89        #[arg(long)]
90        fail_on_regression: bool,
91    },
92
93    /// Check struct layouts against budget constraints
94    Check {
95        /// Path to the binary file to analyze
96        #[arg(value_name = "BINARY")]
97        binary: PathBuf,
98
99        /// Path to config file (.layout-audit.yaml)
100        #[arg(short, long, default_value = ".layout-audit.yaml")]
101        config: PathBuf,
102
103        /// Cache line size in bytes (must be > 0)
104        #[arg(long, default_value = "64", value_parser = clap::value_parser!(u32).range(1..))]
105        cache_line: u32,
106    },
107
108    /// Suggest optimal field ordering to minimize padding
109    Suggest {
110        /// Path to the binary file to analyze
111        #[arg(value_name = "BINARY")]
112        binary: PathBuf,
113
114        /// Filter structs by name (substring match)
115        #[arg(short, long)]
116        filter: Option<String>,
117
118        /// Output format
119        #[arg(short, long, value_enum, default_value = "table")]
120        output: OutputFormat,
121
122        /// Show only structs with at least N bytes of potential savings
123        #[arg(long)]
124        min_savings: Option<u64>,
125
126        /// Cache line size in bytes (must be > 0)
127        #[arg(long, default_value = "64", value_parser = clap::value_parser!(u32).range(1..))]
128        cache_line: u32,
129
130        /// Pretty-print JSON output
131        #[arg(long)]
132        pretty: bool,
133
134        /// Maximum alignment to assume for types (typically 8 on 64-bit)
135        #[arg(long, default_value = "8", value_parser = clap::value_parser!(u64).range(1..))]
136        max_align: u64,
137
138        /// Sort suggestions by savings amount (largest first)
139        #[arg(long)]
140        sort_by_savings: bool,
141
142        /// Disable colored output
143        #[arg(long)]
144        no_color: bool,
145    },
146}
147
148#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
149pub enum OutputFormat {
150    Table,
151    Json,
152}
153
154#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
155pub enum SortField {
156    /// Sort by struct name (alphabetical)
157    Name,
158    /// Sort by total size (largest first)
159    Size,
160    /// Sort by padding bytes (most padding first)
161    Padding,
162    /// Sort by padding percentage (worst efficiency first)
163    PaddingPct,
164}