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 (table, json, sarif)
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        /// Include Go runtime internal types (filtered by default)
66        #[arg(long)]
67        include_go_runtime: bool,
68    },
69
70    /// Compare struct layouts between two binaries
71    Diff {
72        /// Path to the old (baseline) binary
73        #[arg(value_name = "OLD")]
74        old: PathBuf,
75
76        /// Path to the new binary
77        #[arg(value_name = "NEW")]
78        new: PathBuf,
79
80        /// Filter structs by name (substring match)
81        #[arg(short, long)]
82        filter: Option<String>,
83
84        /// Output format (table, json, sarif)
85        #[arg(short, long, value_enum, default_value = "table")]
86        output: OutputFormat,
87
88        /// Cache line size in bytes (must be > 0)
89        #[arg(long, default_value = "64", value_parser = clap::value_parser!(u32).range(1..))]
90        cache_line: u32,
91
92        /// Exit with error code 1 if any regressions found (size or padding increased)
93        #[arg(long)]
94        fail_on_regression: bool,
95
96        /// Include Go runtime internal types (filtered by default)
97        #[arg(long)]
98        include_go_runtime: bool,
99    },
100
101    /// Check struct layouts against budget constraints
102    Check {
103        /// Path to the binary file to analyze
104        #[arg(value_name = "BINARY")]
105        binary: PathBuf,
106
107        /// Path to config file (.layout-audit.yaml)
108        #[arg(short, long, default_value = ".layout-audit.yaml")]
109        config: PathBuf,
110
111        /// Output format (table, json, sarif)
112        #[arg(short, long, value_enum, default_value = "table")]
113        output: OutputFormat,
114
115        /// Cache line size in bytes (must be > 0)
116        #[arg(long, default_value = "64", value_parser = clap::value_parser!(u32).range(1..))]
117        cache_line: u32,
118
119        /// Include Go runtime internal types (filtered by default)
120        #[arg(long)]
121        include_go_runtime: bool,
122    },
123
124    /// Suggest optimal field ordering to minimize padding
125    Suggest {
126        /// Path to the binary file to analyze
127        #[arg(value_name = "BINARY")]
128        binary: PathBuf,
129
130        /// Filter structs by name (substring match)
131        #[arg(short, long)]
132        filter: Option<String>,
133
134        /// Output format (table, json, sarif)
135        #[arg(short, long, value_enum, default_value = "table")]
136        output: OutputFormat,
137
138        /// Show only structs with at least N bytes of potential savings
139        #[arg(long)]
140        min_savings: Option<u64>,
141
142        /// Cache line size in bytes (must be > 0)
143        #[arg(long, default_value = "64", value_parser = clap::value_parser!(u32).range(1..))]
144        cache_line: u32,
145
146        /// Pretty-print JSON output
147        #[arg(long)]
148        pretty: bool,
149
150        /// Maximum alignment to assume for types (typically 8 on 64-bit)
151        #[arg(long, default_value = "8", value_parser = clap::value_parser!(u64).range(1..))]
152        max_align: u64,
153
154        /// Sort suggestions by savings amount (largest first)
155        #[arg(long)]
156        sort_by_savings: bool,
157
158        /// Disable colored output
159        #[arg(long)]
160        no_color: bool,
161
162        /// Include Go runtime internal types (filtered by default)
163        #[arg(long)]
164        include_go_runtime: bool,
165    },
166}
167
168#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
169pub enum OutputFormat {
170    Table,
171    Json,
172    Sarif,
173}
174
175#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
176pub enum SortField {
177    /// Sort by struct name (alphabetical)
178    Name,
179    /// Sort by total size (largest first)
180    Size,
181    /// Sort by padding bytes (most padding first)
182    Padding,
183    /// Sort by padding percentage (worst efficiency first)
184    PaddingPct,
185}