Skip to content

Commit 320efcb

Browse files
ribbanyaencounter
andauthored
objdiff-cli report: Support data sections (#49)
* objdiff-cli report: Support data sections * Minor fixes for section match % --------- Co-authored-by: Luke Street <luke.street@encounterpc.com>
1 parent 7148b51 commit 320efcb

File tree

1 file changed

+157
-90
lines changed

1 file changed

+157
-90
lines changed

objdiff-cli/src/cmd/report.rs

Lines changed: 157 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub struct GenerateArgs {
4242
/// Output JSON file
4343
output: Option<PathBuf>,
4444
#[argp(switch, short = 'd')]
45-
/// Deduplicate global and weak symbols
45+
/// Deduplicate global and weak symbols (runs single-threaded)
4646
deduplicate: bool,
4747
}
4848

@@ -64,9 +64,12 @@ pub struct ChangesArgs {
6464
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
6565
struct Report {
6666
fuzzy_match_percent: f32,
67-
total_size: u64,
68-
matched_size: u64,
69-
matched_size_percent: f32,
67+
total_code: u64,
68+
matched_code: u64,
69+
matched_code_percent: f32,
70+
total_data: u64,
71+
matched_data: u64,
72+
matched_data_percent: f32,
7073
total_functions: u32,
7174
matched_functions: u32,
7275
matched_functions_percent: f32,
@@ -77,8 +80,10 @@ struct Report {
7780
struct ReportUnit {
7881
name: String,
7982
fuzzy_match_percent: f32,
80-
total_size: u64,
81-
matched_size: u64,
83+
total_code: u64,
84+
matched_code: u64,
85+
total_data: u64,
86+
matched_data: u64,
8287
total_functions: u32,
8388
matched_functions: u32,
8489
#[serde(skip_serializing_if = "Option::is_none")]
@@ -87,11 +92,12 @@ struct ReportUnit {
8792
module_name: Option<String>,
8893
#[serde(skip_serializing_if = "Option::is_none")]
8994
module_id: Option<u32>,
90-
functions: Vec<ReportFunction>,
95+
sections: Vec<ReportItem>,
96+
functions: Vec<ReportItem>,
9197
}
9298

9399
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
94-
struct ReportFunction {
100+
struct ReportItem {
95101
name: String,
96102
#[serde(skip_serializing_if = "Option::is_none")]
97103
demangled_name: Option<String>,
@@ -160,21 +166,29 @@ fn generate(args: GenerateArgs) -> Result<()> {
160166
report.units = units.into_iter().flatten().collect();
161167
}
162168
for unit in &report.units {
163-
report.fuzzy_match_percent += unit.fuzzy_match_percent * unit.total_size as f32;
164-
report.total_size += unit.total_size;
165-
report.matched_size += unit.matched_size;
169+
report.fuzzy_match_percent += unit.fuzzy_match_percent * unit.total_code as f32;
170+
report.total_code += unit.total_code;
171+
report.matched_code += unit.matched_code;
172+
report.total_data += unit.total_data;
173+
report.matched_data += unit.matched_data;
166174
report.total_functions += unit.total_functions;
167175
report.matched_functions += unit.matched_functions;
168176
}
169-
if report.total_size == 0 {
177+
if report.total_code == 0 {
170178
report.fuzzy_match_percent = 100.0;
171179
} else {
172-
report.fuzzy_match_percent /= report.total_size as f32;
180+
report.fuzzy_match_percent /= report.total_code as f32;
173181
}
174-
report.matched_size_percent = if report.total_size == 0 {
182+
183+
report.matched_code_percent = if report.total_code == 0 {
184+
100.0
185+
} else {
186+
report.matched_code as f32 / report.total_code as f32 * 100.0
187+
};
188+
report.matched_data_percent = if report.total_data == 0 {
175189
100.0
176190
} else {
177-
report.matched_size as f32 / report.total_size as f32 * 100.0
191+
report.matched_data as f32 / report.total_data as f32 * 100.0
178192
};
179193
report.matched_functions_percent = if report.total_functions == 0 {
180194
100.0
@@ -216,7 +230,6 @@ fn report_object(
216230
}
217231
_ => {}
218232
}
219-
// println!("Checking {}", object.name());
220233
let target = object
221234
.target_path
222235
.as_ref()
@@ -240,11 +253,37 @@ fn report_object(
240253
..Default::default()
241254
};
242255
let obj = target.as_ref().or(base.as_ref()).unwrap();
256+
243257
let obj_diff = result.left.as_ref().or(result.right.as_ref()).unwrap();
244258
for (section, section_diff) in obj.sections.iter().zip(&obj_diff.sections) {
245-
if section.kind != ObjSectionKind::Code {
246-
continue;
259+
let section_match_percent = section_diff.match_percent.unwrap_or_else(|| {
260+
// Support cases where we don't have a target object,
261+
// assume complete means 100% match
262+
if object.complete == Some(true) {
263+
100.0
264+
} else {
265+
0.0
266+
}
267+
});
268+
unit.sections.push(ReportItem {
269+
name: section.name.clone(),
270+
demangled_name: None,
271+
fuzzy_match_percent: section_match_percent,
272+
size: section.size,
273+
address: section.virtual_address,
274+
});
275+
276+
match section.kind {
277+
ObjSectionKind::Data | ObjSectionKind::Bss => {
278+
unit.total_data += section.size;
279+
if section_match_percent == 100.0 {
280+
unit.matched_data += section.size;
281+
}
282+
continue;
283+
}
284+
ObjSectionKind::Code => (),
247285
}
286+
248287
for (symbol, symbol_diff) in section.symbols.iter().zip(&section_diff.symbols) {
249288
if symbol.size == 0 {
250289
continue;
@@ -267,11 +306,11 @@ fn report_object(
267306
}
268307
});
269308
unit.fuzzy_match_percent += match_percent * symbol.size as f32;
270-
unit.total_size += symbol.size;
309+
unit.total_code += symbol.size;
271310
if match_percent == 100.0 {
272-
unit.matched_size += symbol.size;
311+
unit.matched_code += symbol.size;
273312
}
274-
unit.functions.push(ReportFunction {
313+
unit.functions.push(ReportItem {
275314
name: symbol.name.clone(),
276315
demangled_name: symbol.demangled_name.clone(),
277316
size: symbol.size,
@@ -284,10 +323,10 @@ fn report_object(
284323
unit.total_functions += 1;
285324
}
286325
}
287-
if unit.total_size == 0 {
326+
if unit.total_code == 0 {
288327
unit.fuzzy_match_percent = 100.0;
289328
} else {
290-
unit.fuzzy_match_percent /= unit.total_size as f32;
329+
unit.fuzzy_match_percent /= unit.total_code as f32;
291330
}
292331
Ok(Some(unit))
293332
}
@@ -302,9 +341,12 @@ struct Changes {
302341
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
303342
struct ChangeInfo {
304343
fuzzy_match_percent: f32,
305-
total_size: u64,
306-
matched_size: u64,
307-
matched_size_percent: f32,
344+
total_code: u64,
345+
matched_code: u64,
346+
matched_code_percent: f32,
347+
total_data: u64,
348+
matched_data: u64,
349+
matched_data_percent: f32,
308350
total_functions: u32,
309351
matched_functions: u32,
310352
matched_functions_percent: f32,
@@ -314,9 +356,12 @@ impl From<&Report> for ChangeInfo {
314356
fn from(report: &Report) -> Self {
315357
Self {
316358
fuzzy_match_percent: report.fuzzy_match_percent,
317-
total_size: report.total_size,
318-
matched_size: report.matched_size,
319-
matched_size_percent: report.matched_size_percent,
359+
total_code: report.total_code,
360+
matched_code: report.matched_code,
361+
matched_code_percent: report.matched_code_percent,
362+
total_data: report.total_data,
363+
matched_data: report.matched_data,
364+
matched_data_percent: report.matched_data_percent,
320365
total_functions: report.total_functions,
321366
matched_functions: report.matched_functions,
322367
matched_functions_percent: report.matched_functions_percent,
@@ -328,12 +373,19 @@ impl From<&ReportUnit> for ChangeInfo {
328373
fn from(value: &ReportUnit) -> Self {
329374
Self {
330375
fuzzy_match_percent: value.fuzzy_match_percent,
331-
total_size: value.total_size,
332-
matched_size: value.matched_size,
333-
matched_size_percent: if value.total_size == 0 {
376+
total_code: value.total_code,
377+
matched_code: value.matched_code,
378+
matched_code_percent: if value.total_code == 0 {
334379
100.0
335380
} else {
336-
value.matched_size as f32 / value.total_size as f32 * 100.0
381+
value.matched_code as f32 / value.total_code as f32 * 100.0
382+
},
383+
total_data: value.total_data,
384+
matched_data: value.matched_data,
385+
matched_data_percent: if value.total_data == 0 {
386+
100.0
387+
} else {
388+
value.matched_data as f32 / value.total_data as f32 * 100.0
337389
},
338390
total_functions: value.total_functions,
339391
matched_functions: value.matched_functions,
@@ -351,24 +403,25 @@ struct ChangeUnit {
351403
name: String,
352404
from: Option<ChangeInfo>,
353405
to: Option<ChangeInfo>,
354-
functions: Vec<ChangeFunction>,
406+
sections: Vec<ChangeItem>,
407+
functions: Vec<ChangeItem>,
355408
}
356409

357410
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
358-
struct ChangeFunction {
411+
struct ChangeItem {
359412
name: String,
360-
from: Option<ChangeFunctionInfo>,
361-
to: Option<ChangeFunctionInfo>,
413+
from: Option<ChangeItemInfo>,
414+
to: Option<ChangeItemInfo>,
362415
}
363416

364417
#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
365-
struct ChangeFunctionInfo {
418+
struct ChangeItemInfo {
366419
fuzzy_match_percent: f32,
367420
size: u64,
368421
}
369422

370-
impl From<&ReportFunction> for ChangeFunctionInfo {
371-
fn from(value: &ReportFunction) -> Self {
423+
impl From<&ReportItem> for ChangeItemInfo {
424+
fn from(value: &ReportItem) -> Self {
372425
Self { fuzzy_match_percent: value.fuzzy_match_percent, size: value.size }
373426
}
374427
}
@@ -382,54 +435,18 @@ fn changes(args: ChangesArgs) -> Result<()> {
382435
units: vec![],
383436
};
384437
for prev_unit in &previous.units {
385-
let prev_unit_info = ChangeInfo::from(prev_unit);
386438
let curr_unit = current.units.iter().find(|u| u.name == prev_unit.name);
439+
let sections = process_items(prev_unit, curr_unit, |u| &u.sections);
440+
let functions = process_items(prev_unit, curr_unit, |u| &u.functions);
441+
442+
let prev_unit_info = ChangeInfo::from(prev_unit);
387443
let curr_unit_info = curr_unit.map(ChangeInfo::from);
388-
let mut functions = vec![];
389-
if let Some(curr_unit) = curr_unit {
390-
for prev_func in &prev_unit.functions {
391-
let prev_func_info = ChangeFunctionInfo::from(prev_func);
392-
let curr_func = curr_unit.functions.iter().find(|f| f.name == prev_func.name);
393-
let curr_func_info = curr_func.map(ChangeFunctionInfo::from);
394-
if let Some(curr_func_info) = curr_func_info {
395-
if prev_func_info != curr_func_info {
396-
functions.push(ChangeFunction {
397-
name: prev_func.name.clone(),
398-
from: Some(prev_func_info),
399-
to: Some(curr_func_info),
400-
});
401-
}
402-
} else {
403-
functions.push(ChangeFunction {
404-
name: prev_func.name.clone(),
405-
from: Some(prev_func_info),
406-
to: None,
407-
});
408-
}
409-
}
410-
for curr_func in &curr_unit.functions {
411-
if !prev_unit.functions.iter().any(|f| f.name == curr_func.name) {
412-
functions.push(ChangeFunction {
413-
name: curr_func.name.clone(),
414-
from: None,
415-
to: Some(ChangeFunctionInfo::from(curr_func)),
416-
});
417-
}
418-
}
419-
} else {
420-
for prev_func in &prev_unit.functions {
421-
functions.push(ChangeFunction {
422-
name: prev_func.name.clone(),
423-
from: Some(ChangeFunctionInfo::from(prev_func)),
424-
to: None,
425-
});
426-
}
427-
}
428444
if !functions.is_empty() || !matches!(&curr_unit_info, Some(v) if v == &prev_unit_info) {
429445
changes.units.push(ChangeUnit {
430446
name: prev_unit.name.clone(),
431447
from: Some(prev_unit_info),
432448
to: curr_unit_info,
449+
sections,
433450
functions,
434451
});
435452
}
@@ -440,15 +457,8 @@ fn changes(args: ChangesArgs) -> Result<()> {
440457
name: curr_unit.name.clone(),
441458
from: None,
442459
to: Some(ChangeInfo::from(curr_unit)),
443-
functions: curr_unit
444-
.functions
445-
.iter()
446-
.map(|f| ChangeFunction {
447-
name: f.name.clone(),
448-
from: None,
449-
to: Some(ChangeFunctionInfo::from(f)),
450-
})
451-
.collect(),
460+
sections: process_new_items(&curr_unit.sections),
461+
functions: process_new_items(&curr_unit.functions),
452462
});
453463
}
454464
}
@@ -466,6 +476,63 @@ fn changes(args: ChangesArgs) -> Result<()> {
466476
Ok(())
467477
}
468478

479+
fn process_items<F: Fn(&ReportUnit) -> &Vec<ReportItem>>(
480+
prev_unit: &ReportUnit,
481+
curr_unit: Option<&ReportUnit>,
482+
getter: F,
483+
) -> Vec<ChangeItem> {
484+
let prev_items = getter(prev_unit);
485+
let mut items = vec![];
486+
if let Some(curr_unit) = curr_unit {
487+
let curr_items = getter(curr_unit);
488+
for prev_func in prev_items {
489+
let prev_func_info = ChangeItemInfo::from(prev_func);
490+
let curr_func = curr_items.iter().find(|f| f.name == prev_func.name);
491+
let curr_func_info = curr_func.map(ChangeItemInfo::from);
492+
if let Some(curr_func_info) = curr_func_info {
493+
if prev_func_info != curr_func_info {
494+
items.push(ChangeItem {
495+
name: prev_func.name.clone(),
496+
from: Some(prev_func_info),
497+
to: Some(curr_func_info),
498+
});
499+
}
500+
} else {
501+
items.push(ChangeItem {
502+
name: prev_func.name.clone(),
503+
from: Some(prev_func_info),
504+
to: None,
505+
});
506+
}
507+
}
508+
for curr_func in curr_items {
509+
if !prev_items.iter().any(|f| f.name == curr_func.name) {
510+
items.push(ChangeItem {
511+
name: curr_func.name.clone(),
512+
from: None,
513+
to: Some(ChangeItemInfo::from(curr_func)),
514+
});
515+
}
516+
}
517+
} else {
518+
for prev_func in prev_items {
519+
items.push(ChangeItem {
520+
name: prev_func.name.clone(),
521+
from: Some(ChangeItemInfo::from(prev_func)),
522+
to: None,
523+
});
524+
}
525+
}
526+
items
527+
}
528+
529+
fn process_new_items(items: &[ReportItem]) -> Vec<ChangeItem> {
530+
items
531+
.iter()
532+
.map(|f| ChangeItem { name: f.name.clone(), from: None, to: Some(ChangeItemInfo::from(f)) })
533+
.collect()
534+
}
535+
469536
fn read_report(path: &Path) -> Result<Report> {
470537
serde_json::from_reader(BufReader::new(
471538
File::open(path).with_context(|| format!("Failed to open {}", path.display()))?,

0 commit comments

Comments
 (0)