mirror of https://github.com/encounter/objdiff.git
Merge cf15d3b7e7
into 1343f4fd2b
This commit is contained in:
commit
9a386cefc6
|
@ -41,7 +41,7 @@ pub struct GenerateArgs {
|
||||||
/// Output JSON file
|
/// Output JSON file
|
||||||
output: Option<PathBuf>,
|
output: Option<PathBuf>,
|
||||||
#[argp(switch, short = 'd')]
|
#[argp(switch, short = 'd')]
|
||||||
/// Deduplicate global and weak symbols
|
/// Deduplicate global and weak symbols (runs single-threaded)
|
||||||
deduplicate: bool,
|
deduplicate: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,9 +63,12 @@ pub struct ChangesArgs {
|
||||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||||
struct Report {
|
struct Report {
|
||||||
fuzzy_match_percent: f32,
|
fuzzy_match_percent: f32,
|
||||||
total_size: u64,
|
total_code: u64,
|
||||||
matched_size: u64,
|
matched_code: u64,
|
||||||
matched_size_percent: f32,
|
matched_code_percent: f32,
|
||||||
|
total_data: u64,
|
||||||
|
matched_data: u64,
|
||||||
|
matched_data_percent: f32,
|
||||||
total_functions: u32,
|
total_functions: u32,
|
||||||
matched_functions: u32,
|
matched_functions: u32,
|
||||||
matched_functions_percent: f32,
|
matched_functions_percent: f32,
|
||||||
|
@ -76,8 +79,10 @@ struct Report {
|
||||||
struct ReportUnit {
|
struct ReportUnit {
|
||||||
name: String,
|
name: String,
|
||||||
fuzzy_match_percent: f32,
|
fuzzy_match_percent: f32,
|
||||||
total_size: u64,
|
total_code: u64,
|
||||||
matched_size: u64,
|
matched_code: u64,
|
||||||
|
total_data: u64,
|
||||||
|
matched_data: u64,
|
||||||
total_functions: u32,
|
total_functions: u32,
|
||||||
matched_functions: u32,
|
matched_functions: u32,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
@ -86,11 +91,12 @@ struct ReportUnit {
|
||||||
module_name: Option<String>,
|
module_name: Option<String>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
module_id: Option<u32>,
|
module_id: Option<u32>,
|
||||||
functions: Vec<ReportFunction>,
|
sections: Vec<ReportItem>,
|
||||||
|
functions: Vec<ReportItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||||
struct ReportFunction {
|
struct ReportItem {
|
||||||
name: String,
|
name: String,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
demangled_name: Option<String>,
|
demangled_name: Option<String>,
|
||||||
|
@ -159,21 +165,29 @@ fn generate(args: GenerateArgs) -> Result<()> {
|
||||||
report.units = units.into_iter().flatten().collect();
|
report.units = units.into_iter().flatten().collect();
|
||||||
}
|
}
|
||||||
for unit in &report.units {
|
for unit in &report.units {
|
||||||
report.fuzzy_match_percent += unit.fuzzy_match_percent * unit.total_size as f32;
|
report.fuzzy_match_percent += unit.fuzzy_match_percent * unit.total_code as f32;
|
||||||
report.total_size += unit.total_size;
|
report.total_code += unit.total_code;
|
||||||
report.matched_size += unit.matched_size;
|
report.matched_code += unit.matched_code;
|
||||||
|
report.total_data += unit.total_data;
|
||||||
|
report.matched_data += unit.matched_data;
|
||||||
report.total_functions += unit.total_functions;
|
report.total_functions += unit.total_functions;
|
||||||
report.matched_functions += unit.matched_functions;
|
report.matched_functions += unit.matched_functions;
|
||||||
}
|
}
|
||||||
if report.total_size == 0 {
|
if report.total_code == 0 {
|
||||||
report.fuzzy_match_percent = 100.0;
|
report.fuzzy_match_percent = 100.0;
|
||||||
} else {
|
} else {
|
||||||
report.fuzzy_match_percent /= report.total_size as f32;
|
report.fuzzy_match_percent /= report.total_code as f32;
|
||||||
}
|
}
|
||||||
report.matched_size_percent = if report.total_size == 0 {
|
|
||||||
|
report.matched_code_percent = if report.total_code == 0 {
|
||||||
100.0
|
100.0
|
||||||
} else {
|
} else {
|
||||||
report.matched_size as f32 / report.total_size as f32 * 100.0
|
report.matched_code as f32 / report.total_code as f32 * 100.0
|
||||||
|
};
|
||||||
|
report.matched_data_percent = if report.total_data == 0 {
|
||||||
|
100.0
|
||||||
|
} else {
|
||||||
|
report.matched_data as f32 / report.total_data as f32 * 100.0
|
||||||
};
|
};
|
||||||
report.matched_functions_percent = if report.total_functions == 0 {
|
report.matched_functions_percent = if report.total_functions == 0 {
|
||||||
100.0
|
100.0
|
||||||
|
@ -215,7 +229,6 @@ fn report_object(
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
// println!("Checking {}", object.name());
|
|
||||||
let mut target = object
|
let mut target = object
|
||||||
.target_path
|
.target_path
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -239,10 +252,39 @@ fn report_object(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let obj = target.as_ref().or(base.as_ref()).unwrap();
|
let obj = target.as_ref().or(base.as_ref()).unwrap();
|
||||||
|
|
||||||
|
for section in target
|
||||||
|
.as_ref()
|
||||||
|
.map_or(&vec![], |o| &o.sections)
|
||||||
|
.iter()
|
||||||
|
.chain(base.as_ref().map_or(&vec![], |o| &o.sections))
|
||||||
|
.filter(|o| o.match_percent != 0.0)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
println!("{}: {}", section.name, section.match_percent);
|
||||||
|
}
|
||||||
|
|
||||||
for section in &obj.sections {
|
for section in &obj.sections {
|
||||||
if section.kind != ObjSectionKind::Code {
|
unit.sections.push(ReportItem {
|
||||||
continue;
|
name: section.name.clone(),
|
||||||
|
demangled_name: None,
|
||||||
|
fuzzy_match_percent: section.match_percent,
|
||||||
|
size: section.size,
|
||||||
|
address: section.virtual_address,
|
||||||
|
});
|
||||||
|
|
||||||
|
match section.kind {
|
||||||
|
ObjSectionKind::Data | ObjSectionKind::Bss => {
|
||||||
|
unit.total_data += section.size;
|
||||||
|
// section.data_diff
|
||||||
|
if section.match_percent == 100.0 {
|
||||||
|
unit.matched_data += section.size;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ObjSectionKind::Code => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
for symbol in §ion.symbols {
|
for symbol in §ion.symbols {
|
||||||
if symbol.size == 0 {
|
if symbol.size == 0 {
|
||||||
continue;
|
continue;
|
||||||
|
@ -265,11 +307,11 @@ fn report_object(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
unit.fuzzy_match_percent += match_percent * symbol.size as f32;
|
unit.fuzzy_match_percent += match_percent * symbol.size as f32;
|
||||||
unit.total_size += symbol.size;
|
unit.total_code += symbol.size;
|
||||||
if match_percent == 100.0 {
|
if match_percent == 100.0 {
|
||||||
unit.matched_size += symbol.size;
|
unit.matched_code += symbol.size;
|
||||||
}
|
}
|
||||||
unit.functions.push(ReportFunction {
|
unit.functions.push(ReportItem {
|
||||||
name: symbol.name.clone(),
|
name: symbol.name.clone(),
|
||||||
demangled_name: symbol.demangled_name.clone(),
|
demangled_name: symbol.demangled_name.clone(),
|
||||||
size: symbol.size,
|
size: symbol.size,
|
||||||
|
@ -282,10 +324,10 @@ fn report_object(
|
||||||
unit.total_functions += 1;
|
unit.total_functions += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if unit.total_size == 0 {
|
if unit.total_code == 0 {
|
||||||
unit.fuzzy_match_percent = 100.0;
|
unit.fuzzy_match_percent = 100.0;
|
||||||
} else {
|
} else {
|
||||||
unit.fuzzy_match_percent /= unit.total_size as f32;
|
unit.fuzzy_match_percent /= unit.total_code as f32;
|
||||||
}
|
}
|
||||||
Ok(Some(unit))
|
Ok(Some(unit))
|
||||||
}
|
}
|
||||||
|
@ -300,9 +342,12 @@ struct Changes {
|
||||||
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
struct ChangeInfo {
|
struct ChangeInfo {
|
||||||
fuzzy_match_percent: f32,
|
fuzzy_match_percent: f32,
|
||||||
total_size: u64,
|
total_code: u64,
|
||||||
matched_size: u64,
|
matched_code: u64,
|
||||||
matched_size_percent: f32,
|
matched_code_percent: f32,
|
||||||
|
total_data: u64,
|
||||||
|
matched_data: u64,
|
||||||
|
matched_data_percent: f32,
|
||||||
total_functions: u32,
|
total_functions: u32,
|
||||||
matched_functions: u32,
|
matched_functions: u32,
|
||||||
matched_functions_percent: f32,
|
matched_functions_percent: f32,
|
||||||
|
@ -312,9 +357,12 @@ impl From<&Report> for ChangeInfo {
|
||||||
fn from(report: &Report) -> Self {
|
fn from(report: &Report) -> Self {
|
||||||
Self {
|
Self {
|
||||||
fuzzy_match_percent: report.fuzzy_match_percent,
|
fuzzy_match_percent: report.fuzzy_match_percent,
|
||||||
total_size: report.total_size,
|
total_code: report.total_code,
|
||||||
matched_size: report.matched_size,
|
matched_code: report.matched_code,
|
||||||
matched_size_percent: report.matched_size_percent,
|
matched_code_percent: report.matched_code_percent,
|
||||||
|
total_data: report.total_data,
|
||||||
|
matched_data: report.matched_data,
|
||||||
|
matched_data_percent: report.matched_data_percent,
|
||||||
total_functions: report.total_functions,
|
total_functions: report.total_functions,
|
||||||
matched_functions: report.matched_functions,
|
matched_functions: report.matched_functions,
|
||||||
matched_functions_percent: report.matched_functions_percent,
|
matched_functions_percent: report.matched_functions_percent,
|
||||||
|
@ -326,12 +374,19 @@ impl From<&ReportUnit> for ChangeInfo {
|
||||||
fn from(value: &ReportUnit) -> Self {
|
fn from(value: &ReportUnit) -> Self {
|
||||||
Self {
|
Self {
|
||||||
fuzzy_match_percent: value.fuzzy_match_percent,
|
fuzzy_match_percent: value.fuzzy_match_percent,
|
||||||
total_size: value.total_size,
|
total_code: value.total_code,
|
||||||
matched_size: value.matched_size,
|
matched_code: value.matched_code,
|
||||||
matched_size_percent: if value.total_size == 0 {
|
matched_code_percent: if value.total_code == 0 {
|
||||||
100.0
|
100.0
|
||||||
} else {
|
} else {
|
||||||
value.matched_size as f32 / value.total_size as f32 * 100.0
|
value.matched_code as f32 / value.total_code as f32 * 100.0
|
||||||
|
},
|
||||||
|
total_data: value.total_data,
|
||||||
|
matched_data: value.matched_data,
|
||||||
|
matched_data_percent: if value.total_data == 0 {
|
||||||
|
100.0
|
||||||
|
} else {
|
||||||
|
value.matched_data as f32 / value.total_data as f32 * 100.0
|
||||||
},
|
},
|
||||||
total_functions: value.total_functions,
|
total_functions: value.total_functions,
|
||||||
matched_functions: value.matched_functions,
|
matched_functions: value.matched_functions,
|
||||||
|
@ -349,24 +404,25 @@ struct ChangeUnit {
|
||||||
name: String,
|
name: String,
|
||||||
from: Option<ChangeInfo>,
|
from: Option<ChangeInfo>,
|
||||||
to: Option<ChangeInfo>,
|
to: Option<ChangeInfo>,
|
||||||
functions: Vec<ChangeFunction>,
|
sections: Vec<ChangeItem>,
|
||||||
|
functions: Vec<ChangeItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
|
||||||
struct ChangeFunction {
|
struct ChangeItem {
|
||||||
name: String,
|
name: String,
|
||||||
from: Option<ChangeFunctionInfo>,
|
from: Option<ChangeItemInfo>,
|
||||||
to: Option<ChangeFunctionInfo>,
|
to: Option<ChangeItemInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
struct ChangeFunctionInfo {
|
struct ChangeItemInfo {
|
||||||
fuzzy_match_percent: f32,
|
fuzzy_match_percent: f32,
|
||||||
size: u64,
|
size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&ReportFunction> for ChangeFunctionInfo {
|
impl From<&ReportItem> for ChangeItemInfo {
|
||||||
fn from(value: &ReportFunction) -> Self {
|
fn from(value: &ReportItem) -> Self {
|
||||||
Self { fuzzy_match_percent: value.fuzzy_match_percent, size: value.size }
|
Self { fuzzy_match_percent: value.fuzzy_match_percent, size: value.size }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,54 +436,18 @@ fn changes(args: ChangesArgs) -> Result<()> {
|
||||||
units: vec![],
|
units: vec![],
|
||||||
};
|
};
|
||||||
for prev_unit in &previous.units {
|
for prev_unit in &previous.units {
|
||||||
let prev_unit_info = ChangeInfo::from(prev_unit);
|
|
||||||
let curr_unit = current.units.iter().find(|u| u.name == prev_unit.name);
|
let curr_unit = current.units.iter().find(|u| u.name == prev_unit.name);
|
||||||
|
let sections = process_items(prev_unit, curr_unit, |u| &u.sections);
|
||||||
|
let functions = process_items(prev_unit, curr_unit, |u| &u.functions);
|
||||||
|
|
||||||
|
let prev_unit_info = ChangeInfo::from(prev_unit);
|
||||||
let curr_unit_info = curr_unit.map(ChangeInfo::from);
|
let curr_unit_info = curr_unit.map(ChangeInfo::from);
|
||||||
let mut functions = vec![];
|
|
||||||
if let Some(curr_unit) = curr_unit {
|
|
||||||
for prev_func in &prev_unit.functions {
|
|
||||||
let prev_func_info = ChangeFunctionInfo::from(prev_func);
|
|
||||||
let curr_func = curr_unit.functions.iter().find(|f| f.name == prev_func.name);
|
|
||||||
let curr_func_info = curr_func.map(ChangeFunctionInfo::from);
|
|
||||||
if let Some(curr_func_info) = curr_func_info {
|
|
||||||
if prev_func_info != curr_func_info {
|
|
||||||
functions.push(ChangeFunction {
|
|
||||||
name: prev_func.name.clone(),
|
|
||||||
from: Some(prev_func_info),
|
|
||||||
to: Some(curr_func_info),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
functions.push(ChangeFunction {
|
|
||||||
name: prev_func.name.clone(),
|
|
||||||
from: Some(prev_func_info),
|
|
||||||
to: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for curr_func in &curr_unit.functions {
|
|
||||||
if !prev_unit.functions.iter().any(|f| f.name == curr_func.name) {
|
|
||||||
functions.push(ChangeFunction {
|
|
||||||
name: curr_func.name.clone(),
|
|
||||||
from: None,
|
|
||||||
to: Some(ChangeFunctionInfo::from(curr_func)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for prev_func in &prev_unit.functions {
|
|
||||||
functions.push(ChangeFunction {
|
|
||||||
name: prev_func.name.clone(),
|
|
||||||
from: Some(ChangeFunctionInfo::from(prev_func)),
|
|
||||||
to: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !functions.is_empty() || !matches!(&curr_unit_info, Some(v) if v == &prev_unit_info) {
|
if !functions.is_empty() || !matches!(&curr_unit_info, Some(v) if v == &prev_unit_info) {
|
||||||
changes.units.push(ChangeUnit {
|
changes.units.push(ChangeUnit {
|
||||||
name: prev_unit.name.clone(),
|
name: prev_unit.name.clone(),
|
||||||
from: Some(prev_unit_info),
|
from: Some(prev_unit_info),
|
||||||
to: curr_unit_info,
|
to: curr_unit_info,
|
||||||
|
sections,
|
||||||
functions,
|
functions,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -438,15 +458,8 @@ fn changes(args: ChangesArgs) -> Result<()> {
|
||||||
name: curr_unit.name.clone(),
|
name: curr_unit.name.clone(),
|
||||||
from: None,
|
from: None,
|
||||||
to: Some(ChangeInfo::from(curr_unit)),
|
to: Some(ChangeInfo::from(curr_unit)),
|
||||||
functions: curr_unit
|
sections: process_new_items(&curr_unit.sections),
|
||||||
.functions
|
functions: process_new_items(&curr_unit.functions),
|
||||||
.iter()
|
|
||||||
.map(|f| ChangeFunction {
|
|
||||||
name: f.name.clone(),
|
|
||||||
from: None,
|
|
||||||
to: Some(ChangeFunctionInfo::from(f)),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -464,6 +477,63 @@ fn changes(args: ChangesArgs) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_items<F: Fn(&ReportUnit) -> &Vec<ReportItem>>(
|
||||||
|
prev_unit: &ReportUnit,
|
||||||
|
curr_unit: Option<&ReportUnit>,
|
||||||
|
getter: F,
|
||||||
|
) -> Vec<ChangeItem> {
|
||||||
|
let prev_items = getter(prev_unit);
|
||||||
|
let mut items = vec![];
|
||||||
|
if let Some(curr_unit) = curr_unit {
|
||||||
|
let curr_items = getter(curr_unit);
|
||||||
|
for prev_func in prev_items {
|
||||||
|
let prev_func_info = ChangeItemInfo::from(prev_func);
|
||||||
|
let curr_func = curr_items.iter().find(|f| f.name == prev_func.name);
|
||||||
|
let curr_func_info = curr_func.map(ChangeItemInfo::from);
|
||||||
|
if let Some(curr_func_info) = curr_func_info {
|
||||||
|
if prev_func_info != curr_func_info {
|
||||||
|
items.push(ChangeItem {
|
||||||
|
name: prev_func.name.clone(),
|
||||||
|
from: Some(prev_func_info),
|
||||||
|
to: Some(curr_func_info),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items.push(ChangeItem {
|
||||||
|
name: prev_func.name.clone(),
|
||||||
|
from: Some(prev_func_info),
|
||||||
|
to: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for curr_func in curr_items {
|
||||||
|
if !prev_items.iter().any(|f| f.name == curr_func.name) {
|
||||||
|
items.push(ChangeItem {
|
||||||
|
name: curr_func.name.clone(),
|
||||||
|
from: None,
|
||||||
|
to: Some(ChangeItemInfo::from(curr_func)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for prev_func in prev_items {
|
||||||
|
items.push(ChangeItem {
|
||||||
|
name: prev_func.name.clone(),
|
||||||
|
from: Some(ChangeItemInfo::from(prev_func)),
|
||||||
|
to: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_new_items(items: &[ReportItem]) -> Vec<ChangeItem> {
|
||||||
|
items
|
||||||
|
.iter()
|
||||||
|
.map(|f| ChangeItem { name: f.name.clone(), from: None, to: Some(ChangeItemInfo::from(f)) })
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn read_report(path: &Path) -> Result<Report> {
|
fn read_report(path: &Path) -> Result<Report> {
|
||||||
serde_json::from_reader(BufReader::new(
|
serde_json::from_reader(BufReader::new(
|
||||||
File::open(path).with_context(|| format!("Failed to open {}", path.display()))?,
|
File::open(path).with_context(|| format!("Failed to open {}", path.display()))?,
|
||||||
|
|
Loading…
Reference in New Issue