use clap::{Parser, Subcommand, CommandFactory};
use clap_complete::{generate, Shell};
use colored::*;
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "alaz")]
#[command(author = "xjz")]
#[command(version)]
#[command(about = "Assembly Language Analyzer - AArch64 汇编语言分析工具")]
#[command(long_about = "\
Assembly Language Analyzer (ALAZ) - AArch64 汇编语言分析工具
功能特性:
• 解析 objdump 输出文件
• 自动生成汇编指令的语义解释
• 支持多优化级别对比分析 (O0/O1/O2)
• 交互式函数选择和分析
• 生成美观的 Markdown 分析报告
使用示例:
# 交互式分析三个优化级别的共同函数
alaz interactive spark_matrix_naive
# 单文件模式分析 (只分析一个 dump 文件)
alaz interactive -s spark_matrix_naive_O2.dump
# 直接分析指定函数
alaz analyze Matrix_add spark_matrix_naive -o ./output
# 生成 shell 补全脚本
alaz completions bash > ~/.local/share/bash-completion/completions/alaz
")]
struct Cli {
#[command(subcommand)]
command: Commands,
#[arg(long, global = true)]
verbose: bool,
}
#[derive(Subcommand)]
enum Commands {
#[command(verbatim_doc_comment)]
Analyze {
#[arg(value_name = "FUNCTION", help = "函数名称 (如: Matrix_add, main)")]
function: String,
#[arg(value_name = "PREFIX", help = "文件前缀 (如: spark_matrix_naive 会查找 *_O0.dump, *_O1.dump, *_O2.dump)")]
prefix: String,
#[arg(short, long, value_name = "DIR", help = "保存分析报告的目录")]
output: Option<PathBuf>,
},
#[command(verbatim_doc_comment)]
Interactive {
#[arg(
value_name = "PREFIX_OR_FILE",
help = "文件前缀 (多文件模式) 或完整文件名 (单文件模式)"
)]
prefix: String,
#[arg(
short = 's',
long,
help = "单文件模式: 只读取并分析一个 dump 文件 (如: -s my_code_O2.dump)"
)]
single: bool,
#[arg(
short = 'm',
long,
conflicts_with = "single",
help = "多文件模式: 分析 O0/O1/O2 三个文件的共同函数 (默认行为)"
)]
multi: bool,
#[arg(short, long, value_name = "DIR", help = "保存分析报告的目录")]
output: Option<PathBuf>,
},
#[command(verbatim_doc_comment)]
Completions {
#[arg(
value_name = "SHELL",
help = "Shell 类型 (bash, fish, zsh, powershell, elvish)"
)]
shell: String,
},
}
fn main() {
let cli = Cli::parse();
let log_level = if cli.verbose { "info" } else { "warn" };
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(log_level))
.init();
let result = match cli.command {
Commands::Analyze { function, prefix, output } => {
analyze_dumps(&function, &prefix, output.as_ref())
}
Commands::Interactive { prefix, single, multi: _, output } => {
interactive_mode(&prefix, single, output.as_ref())
}
Commands::Completions { shell } => {
generate_completions(&shell)
}
};
if let Err(e) = result {
eprintln!("{}", format!("❌ 错误: {}", e).red().bold());
std::process::exit(1);
}
}
fn analyze_dumps(
function: &str,
prefix: &str,
output: Option<&PathBuf>,
) -> anyhow::Result<()> {
use alaz::table::TableGenerator;
println!("{}", "=".repeat(60).cyan());
println!("{}", " ALAZ - 汇编语言分析工具".cyan().bold());
println!("{}", "=".repeat(60).cyan());
println!();
println!("{} {}", "📋 分析函数:".yellow(), function.bold());
println!("{} {}", "📁 文件前缀:".yellow(), prefix);
if let Some(out) = output {
println!("{} {}", "💾 输出目录:".yellow(), out.display());
}
println!();
let generator = TableGenerator::new();
generator.generate_from_dumps(function, prefix, output)?;
println!();
println!("{}", "✅ 分析完成!".green().bold());
Ok(())
}
fn interactive_mode(prefix: &str, single_mode: bool, output: Option<&PathBuf>) -> anyhow::Result<()> {
use alaz::objdump::ObjdumpParser;
use std::io::{self, Write};
println!("{}", "=".repeat(60).cyan());
println!("{}", " ALAZ - 汇编语言分析工具 (交互式模式)".cyan().bold());
println!("{}", "=".repeat(60).cyan());
println!();
if single_mode {
let dump_path = if prefix.ends_with(".dump") {
prefix.to_string()
} else {
format!("{}.dump", prefix)
};
println!("{} {} (单文件模式)", "📂 正在读取:".yellow(), dump_path);
let parser = ObjdumpParser::from_file(&dump_path)?;
let mut functions = parser.list_functions()?;
if functions.is_empty() {
println!("{}", "❌ 未找到任何函数".red());
return Ok(());
}
functions.sort();
println!();
println!("{} {} 个函数", "✓ 检测到".green(), functions.len());
println!();
loop {
println!("{}", "=".repeat(60).cyan());
println!("{}", "可用函数列表:".yellow().bold());
println!("{}", "-".repeat(60));
for (idx, func) in functions.iter().enumerate() {
println!(" {}. {}", format!("{:3}", idx + 1).cyan(), func);
}
println!("{}", "-".repeat(60));
println!();
println!("请选择:");
println!(" {} 输入函数编号进行分析", "●".green());
println!(" {} 输入 'q' 或 'quit' 退出", "●".red());
println!();
print!("{} ", "选择 >".bright_blue().bold());
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let input = input.trim();
if input == "q" || input == "quit" || input.is_empty() {
println!();
println!("{}", "👋 再见!".yellow());
break;
}
match input.parse::<usize>() {
Ok(num) if num > 0 && num <= functions.len() => {
let function = &functions[num - 1];
println!();
println!("{}", "=".repeat(60).cyan());
use alaz::table::TableGenerator;
let generator = TableGenerator::new();
if let Err(e) = generator.generate_from_single_dump(function, &dump_path, output) {
println!();
println!("{} {}", "❌ 分析失败:".red(), e);
}
println!();
println!("按 Enter 继续...");
let mut _pause = String::new();
io::stdin().read_line(&mut _pause)?;
println!();
}
_ => {
println!("{}", "❌ 无效的选择,请输入正确的编号".red());
println!();
}
}
}
return Ok(());
}
let real_prefix = if prefix.ends_with(".dump") {
prefix
.strip_suffix(".dump").unwrap_or(prefix)
.trim_end_matches("_O0")
.trim_end_matches("_O1")
.trim_end_matches("_O2")
.to_string()
} else {
prefix.to_string()
};
let o0_path = format!("{}_O0.dump", &real_prefix);
let o1_path = format!("{}_O1.dump", &real_prefix);
let o2_path = format!("{}_O2.dump", &real_prefix);
println!("{} 读取三个优化级别的文件以找出共同函数...", "⚙".yellow());
let mut common_functions: Option<std::collections::HashSet<String>> = None;
let mut file_count = 0;
for (level, path) in [("O0", &o0_path), ("O1", &o1_path), ("O2", &o2_path)] {
if let Ok(parser) = ObjdumpParser::from_file(path) {
if let Ok(funcs) = parser.list_functions() {
file_count += 1;
let func_set: std::collections::HashSet<_> = funcs.into_iter().collect();
common_functions = Some(match common_functions {
None => func_set,
Some(existing) => existing.intersection(&func_set).cloned().collect(),
});
println!(" {} {} 文件读取成功", "✓".green(), level);
} else {
println!(" {} {} 文件解析失败", "⚠".yellow(), level);
}
} else {
println!(" {} {} 文件未找到", "⚠".yellow(), level);
}
}
let mut functions: Vec<String> = common_functions
.unwrap_or_default()
.into_iter()
.collect();
if functions.is_empty() {
println!("{}", "❌ 未找到任何共同函数".red());
if file_count == 0 {
println!("{}", "提示: 请确保存在 *_O0.dump, *_O1.dump, *_O2.dump 文件".yellow());
}
return Ok(());
}
functions.sort();
println!();
println!("{} {} 个共同函数 (在所有优化级别都存在)", "✓ 检测到".green(), functions.len());
println!();
loop {
println!("{}", "=".repeat(60).cyan());
println!("{}", "可用函数列表:".yellow().bold());
println!("{}", "-".repeat(60));
for (idx, func) in functions.iter().enumerate() {
println!(" {}. {}", format!("{:3}", idx + 1).cyan(), func);
}
println!("{}", "-".repeat(60));
println!();
println!("请选择:");
println!(" {} 输入函数编号进行分析", "●".green());
println!(" {} 输入 'q' 或 'quit' 退出", "●".red());
println!();
print!("{} ", "选择 >".bright_blue().bold());
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let input = input.trim();
if input == "q" || input == "quit" || input.is_empty() {
println!();
println!("{}", "👋 再见!".yellow());
break;
}
match input.parse::<usize>() {
Ok(num) if num > 0 && num <= functions.len() => {
let function = &functions[num - 1];
println!();
println!("{}", "=".repeat(60).cyan());
if let Err(e) = analyze_dumps(function, &real_prefix, output) {
println!();
println!("{} {}", "❌ 分析失败:".red(), e);
}
println!();
println!("按 Enter 继续...");
let mut _pause = String::new();
io::stdin().read_line(&mut _pause)?;
println!();
}
_ => {
println!("{}", "❌ 无效的选择,请输入正确的编号".red());
println!();
}
}
}
Ok(())
}
fn generate_completions(shell_name: &str) -> anyhow::Result<()> {
let shell = match shell_name.to_lowercase().as_str() {
"bash" => Shell::Bash,
"fish" => Shell::Fish,
"zsh" => Shell::Zsh,
"powershell" => Shell::PowerShell,
"elvish" => Shell::Elvish,
_ => {
eprintln!("{}", format!("❌ 不支持的 shell: {}", shell_name).red());
eprintln!("支持的 shell: bash, fish, zsh, powershell, elvish");
return Ok(());
}
};
let mut cmd = Cli::command();
let bin_name = cmd.get_name().to_string();
generate(shell, &mut cmd, bin_name, &mut std::io::stdout());
Ok(())
}