Skip to content

Commit f2f813e

Browse files
authored
Indicate when symbols are in the wrong order (#385)
* Indicate when symbols are in the wrong order * Wrong order button: Cycle through all wrong symbols * Improve order diff algo to further reduce noise * Don't right-justify "Wrong order" button * Clippy
1 parent e42ab3a commit f2f813e

4 files changed

Lines changed: 171 additions & 9 deletions

File tree

‎objdiff-core/src/diff/mod.rs‎

Lines changed: 103 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ use alloc::{
44
vec,
55
vec::Vec,
66
};
7-
use core::{num::NonZeroU32, ops::Range};
7+
use core::{cmp::Ordering, num::NonZeroU32, ops::Range};
88

9-
use anyhow::Result;
9+
use anyhow::{Result, anyhow};
1010

1111
use crate::{
1212
diff::{
@@ -17,7 +17,7 @@ use crate::{
1717
symbol_name_matches,
1818
},
1919
},
20-
obj::{InstructionRef, Object, Relocation, SectionKind, Symbol, SymbolFlag},
20+
obj::{InstructionRef, Object, Relocation, SectionKind, Symbol, SymbolFlag, SymbolKind},
2121
};
2222

2323
pub mod code;
@@ -47,6 +47,7 @@ pub struct SymbolDiff {
4747
pub diff_score: Option<(u64, u64)>,
4848
pub instruction_rows: Vec<InstructionDiffRow>,
4949
pub data_rows: Vec<DataDiffRow>,
50+
pub order: Option<Ordering>,
5051
}
5152

5253
#[derive(Debug, Clone, Default)]
@@ -207,8 +208,8 @@ pub fn diff_objs(
207208
let mut right = right.map(|p| (p, ObjectDiff::new_from_obj(p)));
208209
let mut prev = prev.map(|p| (p, ObjectDiff::new_from_obj(p)));
209210

210-
for symbol_match in symbol_matches {
211-
match symbol_match {
211+
for symbol_match in &symbol_matches {
212+
match *symbol_match {
212213
SymbolMatch {
213214
left: Some(left_symbol_ref),
214215
right: Some(right_symbol_ref),
@@ -412,13 +413,110 @@ pub fn diff_objs(
412413
}
413414
}
414415

416+
if let Some((left_obj, left_out)) = left.as_mut()
417+
&& let Some((right_obj, right_out)) = right.as_mut()
418+
{
419+
let mut done_section_names = BTreeSet::new();
420+
for left_section in left_obj.sections.iter() {
421+
if done_section_names.contains(&left_section.name) {
422+
continue;
423+
}
424+
done_section_names.insert(&left_section.name);
425+
diff_order_for_section_name(
426+
left_obj,
427+
right_obj,
428+
left_out,
429+
right_out,
430+
&left_section.name,
431+
&symbol_matches,
432+
)?;
433+
}
434+
}
435+
415436
Ok(DiffObjsResult {
416437
left: left.map(|(_, o)| o),
417438
right: right.map(|(_, o)| o),
418439
prev: prev.map(|(_, o)| o),
419440
})
420441
}
421442

443+
fn symbols_matching_section_name<'obj>(
444+
obj: &'obj Object,
445+
section_name: &str,
446+
) -> impl Iterator<Item = (usize, &'obj Symbol)> {
447+
obj.symbols.iter().enumerate().filter(move |(_, s)| {
448+
let curr_section_name = symbol_section(obj, s).map(|(n, _)| n);
449+
curr_section_name == Some(section_name)
450+
&& s.kind != SymbolKind::Section
451+
&& s.size > 0
452+
&& !s.flags.contains(SymbolFlag::Hidden)
453+
&& !s.flags.contains(SymbolFlag::Ignored)
454+
})
455+
}
456+
457+
fn diff_order_for_section_name(
458+
left_obj: &Object,
459+
right_obj: &Object,
460+
left_diff: &mut ObjectDiff,
461+
right_diff: &mut ObjectDiff,
462+
section_name: &str,
463+
symbol_matches: &Vec<SymbolMatch>,
464+
) -> Result<()> {
465+
let mut left_paired_symbol_idxs = BTreeSet::new();
466+
let mut right_paired_symbol_idxs = BTreeSet::new();
467+
let mut left_sym_idx_to_right_sym_idx = BTreeMap::new();
468+
for symbol_match in symbol_matches {
469+
let Some(left_symbol_idx) = symbol_match.left else {
470+
continue;
471+
};
472+
let Some(right_symbol_idx) = symbol_match.right else {
473+
continue;
474+
};
475+
left_paired_symbol_idxs.insert(left_symbol_idx);
476+
right_paired_symbol_idxs.insert(right_symbol_idx);
477+
left_sym_idx_to_right_sym_idx.insert(left_symbol_idx, right_symbol_idx);
478+
}
479+
480+
let left_paired_symbols: Vec<_> = symbols_matching_section_name(left_obj, section_name)
481+
.filter(|(sym_idx, _)| left_paired_symbol_idxs.contains(sym_idx))
482+
.collect();
483+
let right_paired_symbols: Vec<_> = symbols_matching_section_name(right_obj, section_name)
484+
.filter(|(sym_idx, _)| right_paired_symbol_idxs.contains(sym_idx))
485+
.collect();
486+
487+
let mut expected_right_order_idx = 0;
488+
for (left_order_idx, (left_symbol_idx, _left_symbol)) in left_paired_symbols.iter().enumerate()
489+
{
490+
let right_symbol_idx = left_sym_idx_to_right_sym_idx.get(left_symbol_idx).unwrap();
491+
let right_order_idx = right_paired_symbols
492+
.iter()
493+
.position(|(sym_idx, _)| sym_idx == right_symbol_idx)
494+
.ok_or_else(|| {
495+
anyhow!("Failed to find right side symbol for paired left side symbol")
496+
})?;
497+
if right_order_idx == left_order_idx {
498+
// In the correct spot.
499+
left_diff.symbols[*left_symbol_idx].order = Some(Ordering::Equal);
500+
right_diff.symbols[*right_symbol_idx].order = Some(Ordering::Equal);
501+
expected_right_order_idx = left_order_idx + 1
502+
} else if right_order_idx == expected_right_order_idx {
503+
// In the wrong spot, but correct relative to the symbol before it.
504+
// Don't show this as a diff to reduce noise.
505+
left_diff.symbols[*left_symbol_idx].order = Some(Ordering::Equal);
506+
right_diff.symbols[*right_symbol_idx].order = Some(Ordering::Equal);
507+
} else {
508+
// In the wrong spot.
509+
left_diff.symbols[*left_symbol_idx].order =
510+
Some(expected_right_order_idx.cmp(&right_order_idx));
511+
right_diff.symbols[*right_symbol_idx].order =
512+
Some(right_order_idx.cmp(&expected_right_order_idx));
513+
expected_right_order_idx = right_order_idx;
514+
}
515+
expected_right_order_idx += 1;
516+
}
517+
Ok(())
518+
}
519+
422520
/// Score entry for a candidate symbol when searching for similar functions.
423521
#[derive(Debug, Clone)]
424522
pub struct SimilarSymbol {

‎objdiff-core/tests/snapshots/arch_ppc__diff_ppc-2.snap‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2646,6 +2646,9 @@ expression: "(target_symbol_diff, base_symbol_diff)"
26462646
},
26472647
],
26482648
data_rows: [],
2649+
order: Some(
2650+
Equal,
2651+
),
26492652
},
26502653
SymbolDiff {
26512654
target_symbol: Some(
@@ -5290,5 +5293,8 @@ expression: "(target_symbol_diff, base_symbol_diff)"
52905293
},
52915294
],
52925295
data_rows: [],
5296+
order: Some(
5297+
Equal,
5298+
),
52935299
},
52945300
)

‎objdiff-gui/src/views/diff.rs‎

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,49 @@ pub fn diff_view_ui(
367367
ret = Some(DiffViewAction::SetSearch(search));
368368
}
369369

370+
if let Some((_, left_diff)) = left_ctx.obj {
371+
let wrong_order_symbols = left_diff
372+
.symbols
373+
.iter()
374+
.filter(|s| s.order.is_some_and(|o| o != Ordering::Equal))
375+
.count();
376+
let mut wrong_order_symbol_idx_to_select = left_diff
377+
.symbols
378+
.iter()
379+
.position(|s| s.order.is_some_and(|o| o != Ordering::Equal));
380+
if let Some(left_highlight) = state.symbol_state.highlighted_symbol.0 {
381+
let next_wrong_order_symbol_idx = left_diff
382+
.symbols
383+
.iter()
384+
.enumerate()
385+
.skip(left_highlight + 1)
386+
.find(|(_, s)| s.order.is_some_and(|o| o != Ordering::Equal))
387+
.map(|(i, _)| i);
388+
if next_wrong_order_symbol_idx.is_some() {
389+
wrong_order_symbol_idx_to_select = next_wrong_order_symbol_idx;
390+
}
391+
}
392+
if ui
393+
.add_enabled(
394+
wrong_order_symbol_idx_to_select.is_some(),
395+
egui::Button::new(format!(
396+
"Wrong order: {}/{}",
397+
wrong_order_symbols,
398+
left_diff.symbols.len()
399+
)),
400+
)
401+
.clicked()
402+
&& let Some(left_sym_idx) = wrong_order_symbol_idx_to_select
403+
{
404+
let target_symbol = left_diff.symbols[left_sym_idx].target_symbol;
405+
ret = Some(DiffViewAction::SetSymbolHighlight(
406+
Some(left_sym_idx),
407+
target_symbol,
408+
true,
409+
));
410+
}
411+
}
412+
370413
ui.with_layout(Layout::right_to_left(egui::Align::TOP), |ui| {
371414
if ui.small_button("⏷").on_hover_text_at_pointer("Expand all").clicked() {
372415
open_sections.0 = Some(true);
@@ -375,7 +418,7 @@ pub fn diff_view_ui(
375418
{
376419
open_sections.0 = Some(false);
377420
}
378-
})
421+
});
379422
});
380423
}
381424

‎objdiff-gui/src/views/symbol_diff.rs‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::mem::take;
1+
use std::{cmp::Ordering, mem::take};
22

33
use egui::{
44
CollapsingHeader, Color32, Id, OpenUrl, ScrollArea, Ui, Widget, style::ScrollAnimation,
@@ -734,14 +734,29 @@ fn symbol_ui(
734734
write_text(name, appearance.highlight_color, &mut job, appearance.code_font.clone());
735735
if diff_config.show_symbol_sizes == ShowSymbolSizes::Decimal {
736736
write_text(
737-
&format!(" (size={})", symbol.size),
737+
&format!(" (size:{})", symbol.size),
738738
appearance.text_color,
739739
&mut job,
740740
appearance.code_font.clone(),
741741
);
742742
} else if diff_config.show_symbol_sizes == ShowSymbolSizes::Hex {
743743
write_text(
744-
&format!(" (size={:x})", symbol.size),
744+
&format!(" (size:{:x})", symbol.size),
745+
appearance.text_color,
746+
&mut job,
747+
appearance.code_font.clone(),
748+
);
749+
}
750+
if let Some(order) = symbol_diff.order
751+
&& order != Ordering::Equal
752+
{
753+
let order_char = match order {
754+
Ordering::Less => "⏷",
755+
Ordering::Equal => unreachable!(),
756+
Ordering::Greater => "⏶",
757+
};
758+
write_text(
759+
&format!(" {order_char}"),
745760
appearance.text_color,
746761
&mut job,
747762
appearance.code_font.clone(),

0 commit comments

Comments
 (0)