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        /// 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
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        /// Cache line size in bytes (must be > 0)
112        #[arg(long, default_value = "64", value_parser = clap::value_parser!(u32).range(1..))]
113        cache_line: u32,
114
115        /// Include Go runtime internal types (filtered by default)
116        #[arg(long)]
117        include_go_runtime: bool,
118    },
119
120    /// Suggest optimal field ordering to minimize padding
121    Suggest {
122        /// Path to the binary file to analyze
123        #[arg(value_name = "BINARY")]
124        binary: PathBuf,
125
126        /// Filter structs by name (substring match)
127        #[arg(short, long)]
128        filter: Option<String>,
129
130        /// Output format
131        #[arg(short, long, value_enum, default_value = "table")]
132        output: OutputFormat,
133
134        /// Show only structs with at least N bytes of potential savings
135        #[arg(long)]
136        min_savings: Option<u64>,
137
138        /// Cache line size in bytes (must be > 0)
139        #[arg(long, default_value = "64", value_parser = clap::value_parser!(u32).range(1..))]
140        cache_line: u32,
141
142        /// Pretty-print JSON output
143        #[arg(long)]
144        pretty: bool,
145
146        /// Maximum alignment to assume for types (typically 8 on 64-bit)
147        #[arg(long, default_value = "8", value_parser = clap::value_parser!(u64).range(1..))]
148        max_align: u64,
149
150        /// Sort suggestions by savings amount (largest first)
151        #[arg(long)]
152        sort_by_savings: bool,
153
154        /// Disable colored output
155        #[arg(long)]
156        no_color: bool,
157
158        /// Include Go runtime internal types (filtered by default)
159        #[arg(long)]
160        include_go_runtime: bool,
161    },
162}
163
164#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
165pub enum OutputFormat {
166    Table,
167    Json,
168}
169
170#[derive(Copy, Clone, PartialEq, Eq, ValueEnum)]
171pub enum SortField {
172    /// Sort by struct name (alphabetical)
173    Name,
174    /// Sort by total size (largest first)
175    Size,
176    /// Sort by padding bytes (most padding first)
177    Padding,
178    /// Sort by padding percentage (worst efficiency first)
179    PaddingPct,
180}