Restructure diffing code & initial 3-way diffing (WIP)

This commit is contained in:
Luke Street
2024-03-18 18:10:18 -06:00
parent 1343f4fd2b
commit 3c74b89f15
17 changed files with 956 additions and 486 deletions
File diff suppressed because it is too large Load Diff
+7 -6
View File
@@ -216,18 +216,18 @@ fn report_object(
_ => {}
}
// println!("Checking {}", object.name());
let mut target = object
let target = object
.target_path
.as_ref()
.map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display())))
.transpose()?;
let mut base = object
let base = object
.base_path
.as_ref()
.map(|p| obj::read::read(p).with_context(|| format!("Failed to open {}", p.display())))
.transpose()?;
let config = diff::DiffObjConfig { relax_reloc_diffs: true, ..Default::default() };
diff::diff_objs(&config, target.as_mut(), base.as_mut())?;
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)?;
let mut unit = ReportUnit {
name: object.name().to_string(),
complete: object.complete,
@@ -239,11 +239,12 @@ fn report_object(
..Default::default()
};
let obj = target.as_ref().or(base.as_ref()).unwrap();
for section in &obj.sections {
let obj_diff = result.left.as_ref().or(result.right.as_ref()).unwrap();
for (section, section_diff) in obj.sections.iter().zip(&obj_diff.sections) {
if section.kind != ObjSectionKind::Code {
continue;
}
for symbol in &section.symbols {
for (symbol, symbol_diff) in section.symbols.iter().zip(&section_diff.symbols) {
if symbol.size == 0 {
continue;
}
@@ -255,7 +256,7 @@ fn report_object(
continue;
}
}
let match_percent = symbol.match_percent.unwrap_or_else(|| {
let match_percent = symbol_diff.match_percent.unwrap_or_else(|| {
// Support cases where we don't have a target object,
// assume complete means 100% match
if object.complete == Some(true) {
+2 -2
View File
@@ -5,8 +5,8 @@ use object::{elf, Endian, Endianness, File, Object, Relocation, RelocationFlags}
use rabbitizer::{config, Abi, InstrCategory, Instruction, OperandType};
use crate::{
arch::ObjArch,
diff::{DiffObjConfig, ProcessCodeResult},
arch::{ObjArch, ProcessCodeResult},
diff::DiffObjConfig,
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
};
+7 -2
View File
@@ -4,8 +4,8 @@ use anyhow::{bail, Result};
use object::{Architecture, Object, Relocation, RelocationFlags};
use crate::{
diff::{DiffObjConfig, ProcessCodeResult},
obj::{ObjReloc, ObjSection},
diff::DiffObjConfig,
obj::{ObjIns, ObjReloc, ObjSection},
};
#[cfg(feature = "mips")]
@@ -33,6 +33,11 @@ pub trait ObjArch: Send + Sync {
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str>;
}
pub struct ProcessCodeResult {
pub ops: Vec<u16>,
pub insts: Vec<ObjIns>,
}
pub fn new_arch(object: &object::File) -> Result<Box<dyn ObjArch>> {
Ok(match object.architecture() {
#[cfg(feature = "ppc")]
+2 -2
View File
@@ -5,8 +5,8 @@ use object::{elf, File, Relocation, RelocationFlags};
use ppc750cl::{disasm_iter, Argument, SimplifiedIns, GPR};
use crate::{
arch::ObjArch,
diff::{DiffObjConfig, ProcessCodeResult},
arch::{ObjArch, ProcessCodeResult},
diff::DiffObjConfig,
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
};
+3 -45
View File
@@ -4,13 +4,13 @@ use anyhow::{anyhow, bail, ensure, Result};
use iced_x86::{
Decoder, DecoderOptions, DecoratorKind, Formatter, FormatterOutput, FormatterTextKind,
GasFormatter, Instruction, IntelFormatter, MasmFormatter, NasmFormatter, NumberKind, OpKind,
PrefixKind, Register, SymbolResult,
PrefixKind, Register,
};
use object::{pe, Endian, Endianness, File, Object, Relocation, RelocationFlags};
use crate::{
arch::ObjArch,
diff::{DiffObjConfig, ProcessCodeResult, X86Formatter},
arch::{ObjArch, ProcessCodeResult},
diff::{DiffObjConfig, X86Formatter},
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
};
@@ -88,29 +88,6 @@ impl ObjArch for ObjArchX86 {
ensure!(output.ins_operands.len() == output.ins.args.len());
output.ins.orig = Some(output.formatted.clone());
// print!("{:016X} ", instruction.ip());
// let start_index = (instruction.ip() - address) as usize;
// let instr_bytes = &data[start_index..start_index + instruction.len()];
// for b in instr_bytes.iter() {
// print!("{:02X}", b);
// }
// if instr_bytes.len() < 32 {
// for _ in 0..32 - instr_bytes.len() {
// print!(" ");
// }
// }
// println!(" {}", output.formatted);
//
// if let Some(reloc) = reloc {
// println!("\tReloc: {:?}", reloc);
// }
//
// for i in 0..instruction.op_count() {
// let kind = instruction.op_kind(i);
// print!("{:?} ", kind);
// }
// println!();
// Make sure we've put the relocation somewhere in the instruction
if reloc.is_some() && !output.ins.args.iter().any(|a| matches!(a, ObjInsArg::Reloc)) {
let mut found = replace_arg(
@@ -229,7 +206,6 @@ impl InstructionFormatterOutput {
impl FormatterOutput for InstructionFormatterOutput {
fn write(&mut self, text: &str, kind: FormatterTextKind) {
// log::debug!("write {} {:?}", text, kind);
self.formatted.push_str(text);
// Skip whitespace after the mnemonic
if self.ins.args.is_empty() && kind == FormatterTextKind::Text {
@@ -252,14 +228,12 @@ impl FormatterOutput for InstructionFormatterOutput {
}
fn write_prefix(&mut self, _instruction: &Instruction, text: &str, _prefix: PrefixKind) {
// log::debug!("write_prefix {} {:?}", text, prefix);
self.formatted.push_str(text);
self.ins_operands.push(None);
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string())));
}
fn write_mnemonic(&mut self, _instruction: &Instruction, text: &str) {
// log::debug!("write_mnemonic {}", text);
self.formatted.push_str(text);
self.ins.mnemonic = text.to_string();
}
@@ -274,7 +248,6 @@ impl FormatterOutput for InstructionFormatterOutput {
number_kind: NumberKind,
kind: FormatterTextKind,
) {
// log::debug!("write_number {} {:?} {} {} {:?} {:?}", operand, instruction_operand, text, value, number_kind, kind);
self.formatted.push_str(text);
self.ins_operands.push(instruction_operand);
@@ -343,7 +316,6 @@ impl FormatterOutput for InstructionFormatterOutput {
text: &str,
_decorator: DecoratorKind,
) {
// log::debug!("write_decorator {} {:?} {} {:?}", operand, instruction_operand, text, decorator);
self.formatted.push_str(text);
self.ins_operands.push(instruction_operand);
self.ins.args.push(ObjInsArg::PlainText(text.to_string()));
@@ -357,22 +329,8 @@ impl FormatterOutput for InstructionFormatterOutput {
text: &str,
_register: Register,
) {
// log::debug!("write_register {} {:?} {} {:?}", operand, instruction_operand, text, register);
self.formatted.push_str(text);
self.ins_operands.push(instruction_operand);
self.ins.args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(text.to_string())));
}
fn write_symbol(
&mut self,
_instruction: &Instruction,
_operand: u32,
_instruction_operand: Option<u32>,
_address: u64,
_symbol: &SymbolResult<'_>,
) {
if self.error.is_none() {
self.error = Some(anyhow!("x86: Unsupported write_symbol"));
}
}
}
+58 -50
View File
@@ -8,56 +8,66 @@ use anyhow::Result;
use similar::{capture_diff_slices_deadline, Algorithm};
use crate::{
arch::ObjArch,
diff::{DiffObjConfig, ProcessCodeResult},
obj::{
ObjInfo, ObjInsArg, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff,
ObjInsDiffKind, ObjReloc, ObjSymbol, ObjSymbolFlags,
arch::ProcessCodeResult,
diff::{
get_symbol, DiffObjConfig, ObjInsArgDiff, ObjInsBranchFrom, ObjInsBranchTo, ObjInsDiff,
ObjInsDiffKind, ObjSymbolDiff, SymbolRef,
},
obj::{ObjInfo, ObjInsArg, ObjReloc, ObjSymbol, ObjSymbolFlags},
};
pub fn no_diff_code(
arch: &dyn ObjArch,
obj: &ObjInfo,
symbol_ref: SymbolRef,
config: &DiffObjConfig,
data: &[u8],
symbol: &mut ObjSymbol,
relocs: &[ObjReloc],
line_info: &Option<BTreeMap<u64, u64>>,
) -> Result<()> {
let code =
&data[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
let out = arch.process_code(config, code, symbol.address, relocs, line_info)?;
) -> Result<ObjSymbolDiff> {
let section = &obj.sections[symbol_ref.section_idx];
let symbol = &section.symbols[symbol_ref.symbol_idx];
let code = &section.data
[symbol.section_address as usize..(symbol.section_address + symbol.size) as usize];
let out = obj.arch.process_code(
config,
code,
symbol.address,
&section.relocations,
&obj.line_info,
)?;
let mut diff = Vec::<ObjInsDiff>::new();
for i in out.insts {
diff.push(ObjInsDiff { ins: Some(i), kind: ObjInsDiffKind::None, ..Default::default() });
}
resolve_branches(&mut diff);
symbol.instructions = diff;
Ok(())
Ok(ObjSymbolDiff { diff_symbol: None, instructions: diff, match_percent: None })
}
#[allow(clippy::too_many_arguments)]
pub fn diff_code(
arch: &dyn ObjArch,
left_obj: &ObjInfo,
right_obj: &ObjInfo,
left_symbol_ref: SymbolRef,
right_symbol_ref: SymbolRef,
config: &DiffObjConfig,
left_data: &[u8],
right_data: &[u8],
left_symbol: &mut ObjSymbol,
right_symbol: &mut ObjSymbol,
left_relocs: &[ObjReloc],
right_relocs: &[ObjReloc],
left_line_info: &Option<BTreeMap<u64, u64>>,
right_line_info: &Option<BTreeMap<u64, u64>>,
) -> Result<()> {
let left_code = &left_data[left_symbol.section_address as usize
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
let (left_section, left_symbol) = get_symbol(left_obj, left_symbol_ref);
let (right_section, right_symbol) = get_symbol(right_obj, right_symbol_ref);
let left_code = &left_section.data[left_symbol.section_address as usize
..(left_symbol.section_address + left_symbol.size) as usize];
let right_code = &right_data[right_symbol.section_address as usize
let right_code = &right_section.data[right_symbol.section_address as usize
..(right_symbol.section_address + right_symbol.size) as usize];
let left_out =
arch.process_code(config, left_code, left_symbol.address, left_relocs, left_line_info)?;
let right_out =
arch.process_code(config, right_code, right_symbol.address, right_relocs, right_line_info)?;
let left_out = left_obj.arch.process_code(
config,
left_code,
left_symbol.address,
&left_section.relocations,
&left_obj.line_info,
)?;
let right_out = left_obj.arch.process_code(
config,
right_code,
right_symbol.address,
&right_section.relocations,
&right_obj.line_info,
)?;
let mut left_diff = Vec::<ObjInsDiff>::new();
let mut right_diff = Vec::<ObjInsDiff>::new();
@@ -81,13 +91,19 @@ pub fn diff_code(
} else {
((total - diff_state.diff_count) as f32 / total as f32) * 100.0
};
left_symbol.match_percent = Some(percent);
right_symbol.match_percent = Some(percent);
left_symbol.instructions = left_diff;
right_symbol.instructions = right_diff;
Ok(())
Ok((
ObjSymbolDiff {
diff_symbol: Some(right_symbol_ref),
instructions: left_diff,
match_percent: Some(percent),
},
ObjSymbolDiff {
diff_symbol: Some(left_symbol_ref),
instructions: right_diff,
match_percent: Some(percent),
},
))
}
fn diff_instructions(
@@ -223,6 +239,9 @@ fn arg_eq(
},
ObjInsArg::Arg(l) => match right {
ObjInsArg::Arg(r) => l == r,
// If relocations are relaxed, match if left is a constant and right is a reloc
// Useful for instances where the target object is created without relocations
ObjInsArg::Reloc => config.relax_reloc_diffs,
_ => false,
},
ObjInsArg::Reloc => {
@@ -334,14 +353,3 @@ fn compare_ins(
}
Ok(result)
}
pub fn find_section_and_symbol(obj: &ObjInfo, name: &str) -> Option<(usize, usize)> {
for (section_idx, section) in obj.sections.iter().enumerate() {
let symbol_idx = match section.symbols.iter().position(|symbol| symbol.name == name) {
Some(symbol_idx) => symbol_idx,
None => continue,
};
return Some((section_idx, symbol_idx));
}
None
}
+47 -28
View File
@@ -6,25 +6,42 @@ use std::{
use anyhow::Result;
use similar::{capture_diff_slices_deadline, Algorithm};
use crate::obj::{ObjDataDiff, ObjDataDiffKind, ObjSection, ObjSymbol};
use crate::{
diff::{get_symbol, ObjDataDiff, ObjDataDiffKind, ObjSectionDiff, ObjSymbolDiff, SymbolRef},
obj::{ObjInfo, ObjSection},
};
pub fn diff_bss_symbols(
left_symbols: &mut [ObjSymbol],
right_symbols: &mut [ObjSymbol],
) -> Result<()> {
for left_symbol in left_symbols {
if let Some(right_symbol) = right_symbols.iter_mut().find(|s| s.name == left_symbol.name) {
left_symbol.diff_symbol = Some(right_symbol.name.clone());
right_symbol.diff_symbol = Some(left_symbol.name.clone());
let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 };
left_symbol.match_percent = Some(percent);
right_symbol.match_percent = Some(percent);
}
}
Ok(())
pub fn diff_bss_symbol(
left_obj: &ObjInfo,
right_obj: &ObjInfo,
left_symbol_ref: SymbolRef,
right_symbol_ref: SymbolRef,
) -> Result<(ObjSymbolDiff, ObjSymbolDiff)> {
let (_, left_symbol) = get_symbol(left_obj, left_symbol_ref);
let (_, right_symbol) = get_symbol(right_obj, right_symbol_ref);
let percent = if left_symbol.size == right_symbol.size { 100.0 } else { 50.0 };
Ok((
ObjSymbolDiff {
diff_symbol: Some(right_symbol_ref),
instructions: vec![],
match_percent: Some(percent),
},
ObjSymbolDiff {
diff_symbol: Some(left_symbol_ref),
instructions: vec![],
match_percent: Some(percent),
},
))
}
pub fn diff_data(left: &mut ObjSection, right: &mut ObjSection) -> Result<()> {
pub fn no_diff_bss_symbol(_obj: &ObjInfo, _symbol_ref: SymbolRef) -> ObjSymbolDiff {
ObjSymbolDiff { diff_symbol: None, instructions: vec![], match_percent: Some(0.0) }
}
pub fn diff_data(
left: &ObjSection,
right: &ObjSection,
) -> Result<(ObjSectionDiff, ObjSectionDiff)> {
let deadline = Instant::now() + Duration::from_secs(5);
let ops =
capture_diff_slices_deadline(Algorithm::Patience, &left.data, &right.data, Some(deadline));
@@ -97,16 +114,18 @@ pub fn diff_data(left: &mut ObjSection, right: &mut ObjSection) -> Result<()> {
}
}
left.data_diff = left_diff;
right.data_diff = right_diff;
Ok(())
}
pub fn no_diff_data(section: &mut ObjSection) {
section.data_diff = vec![ObjDataDiff {
data: section.data.clone(),
kind: ObjDataDiffKind::None,
len: section.data.len(),
symbol: String::new(),
}];
Ok((
ObjSectionDiff {
symbols: vec![],
data_diff: left_diff,
// TODO
match_percent: None,
},
ObjSectionDiff {
symbols: vec![],
data_diff: right_diff,
// TODO
match_percent: None,
},
))
}
+4 -1
View File
@@ -1,6 +1,9 @@
use std::cmp::Ordering;
use crate::obj::{ObjInsArg, ObjInsArgDiff, ObjInsArgValue, ObjInsDiff, ObjReloc, ObjSymbol};
use crate::{
diff::{ObjInsArgDiff, ObjInsDiff},
obj::{ObjInsArg, ObjInsArgValue, ObjReloc, ObjSymbol},
};
#[derive(Debug, Clone)]
pub enum DiffText<'a> {
File diff suppressed because it is too large Load Diff
-72
View File
@@ -39,10 +39,6 @@ pub struct ObjSection {
pub symbols: Vec<ObjSymbol>,
pub relocations: Vec<ObjReloc>,
pub virtual_address: Option<u64>,
// Diff
pub data_diff: Vec<ObjDataDiff>,
pub match_percent: f32,
}
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -94,39 +90,6 @@ impl ObjInsArg {
}
}
#[derive(Debug, Copy, Clone)]
pub struct ObjInsArgDiff {
/// Incrementing index for coloring
pub idx: usize,
}
#[derive(Debug, Clone)]
pub struct ObjInsBranchFrom {
/// Source instruction indices
pub ins_idx: Vec<usize>,
/// Incrementing index for coloring
pub branch_idx: usize,
}
#[derive(Debug, Clone)]
pub struct ObjInsBranchTo {
/// Target instruction index
pub ins_idx: usize,
/// Incrementing index for coloring
pub branch_idx: usize,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub enum ObjInsDiffKind {
#[default]
None,
OpMismatch,
ArgMismatch,
Replace,
Delete,
Insert,
}
#[derive(Debug, Clone)]
pub struct ObjIns {
pub address: u64,
@@ -142,36 +105,6 @@ pub struct ObjIns {
pub orig: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct ObjInsDiff {
pub ins: Option<ObjIns>,
/// Diff kind
pub kind: ObjInsDiffKind,
/// Branches from instruction
pub branch_from: Option<ObjInsBranchFrom>,
/// Branches to instruction
pub branch_to: Option<ObjInsBranchTo>,
/// Arg diffs
pub arg_diff: Vec<Option<ObjInsArgDiff>>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
pub enum ObjDataDiffKind {
#[default]
None,
Replace,
Delete,
Insert,
}
#[derive(Debug, Clone, Default)]
pub struct ObjDataDiff {
pub data: Vec<u8>,
pub kind: ObjDataDiffKind,
pub len: usize,
pub symbol: String,
}
#[derive(Debug, Clone)]
pub struct ObjSymbol {
pub name: String,
@@ -184,11 +117,6 @@ pub struct ObjSymbol {
pub addend: i64,
/// Original virtual address (from .note.split section)
pub virtual_address: Option<u64>,
// Diff
pub diff_symbol: Option<String>,
pub instructions: Vec<ObjInsDiff>,
pub match_percent: Option<f32>,
}
pub struct ObjInfo {
-8
View File
@@ -76,9 +76,6 @@ fn to_obj_symbol(
flags,
addend,
virtual_address,
diff_symbol: None,
instructions: vec![],
match_percent: None,
})
}
@@ -114,8 +111,6 @@ fn filter_sections(obj_file: &File<'_>, split_meta: Option<&SplitMeta>) -> Resul
symbols: Vec::new(),
relocations: Vec::new(),
virtual_address,
data_diff: vec![],
match_percent: 0.0,
});
}
result.sort_by(|a, b| a.name.cmp(&b.name));
@@ -214,9 +209,6 @@ fn find_section_symbol(
flags: Default::default(),
addend: offset_addr as i64,
virtual_address: None,
diff_symbol: None,
instructions: vec![],
match_percent: None,
})
}
+2 -2
View File
@@ -364,12 +364,12 @@ impl App {
}
if let Some(result) = &diff_state.build {
if let Some(obj) = &result.first_obj {
if let Some((obj, _)) = &result.first_obj {
if file_modified(&obj.path, obj.timestamp) {
config.queue_reload = true;
}
}
if let Some(obj) = &result.second_obj {
if let Some((obj, _)) = &result.second_obj {
if file_modified(&obj.path, obj.timestamp) {
config.queue_reload = true;
}
+13 -7
View File
@@ -7,7 +7,7 @@ use std::{
use anyhow::{anyhow, Context, Error, Result};
use objdiff_core::{
diff::{diff_objs, DiffObjConfig},
diff::{diff_objs, DiffObjConfig, ObjDiff},
obj::{read, ObjInfo},
};
use time::OffsetDateTime;
@@ -75,8 +75,8 @@ impl ObjDiffConfig {
pub struct ObjDiffResult {
pub first_status: BuildStatus,
pub second_status: BuildStatus,
pub first_obj: Option<ObjInfo>,
pub second_obj: Option<ObjInfo>,
pub first_obj: Option<(ObjInfo, ObjDiff)>,
pub second_obj: Option<(ObjInfo, ObjDiff)>,
pub time: OffsetDateTime,
}
@@ -214,7 +214,7 @@ fn run_build(
let time = OffsetDateTime::now_utc();
let mut first_obj =
let first_obj =
match &obj_config.target_path {
Some(target_path) if first_status.success => {
update_status(
@@ -231,7 +231,7 @@ fn run_build(
_ => None,
};
let mut second_obj = match &obj_config.base_path {
let second_obj = match &obj_config.base_path {
Some(base_path) if second_status.success => {
update_status(
context,
@@ -249,10 +249,16 @@ fn run_build(
};
update_status(context, "Performing diff".to_string(), 4, total, &cancel)?;
diff_objs(&config.diff_obj_config, first_obj.as_mut(), second_obj.as_mut())?;
let result = diff_objs(&config.diff_obj_config, first_obj.as_ref(), second_obj.as_ref(), None)?;
update_status(context, "Complete".to_string(), total, total, &cancel)?;
Ok(Box::new(ObjDiffResult { first_status, second_status, first_obj, second_obj, time }))
Ok(Box::new(ObjDiffResult {
first_status,
second_status,
first_obj: first_obj.and_then(|o| result.left.map(|d| (o, d))),
second_obj: second_obj.and_then(|o| result.right.map(|d| (o, d))),
time,
}))
}
pub fn start_build(ctx: &egui::Context, config: ObjDiffConfig) -> JobState {
+19 -11
View File
@@ -2,19 +2,22 @@ use std::{cmp::min, default::Default, mem::take};
use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget};
use egui_extras::{Column, TableBuilder};
use objdiff_core::obj::{ObjDataDiff, ObjDataDiffKind, ObjInfo, ObjSection};
use objdiff_core::{
diff::{ObjDataDiff, ObjDataDiffKind, ObjDiff},
obj::ObjInfo,
};
use time::format_description;
use crate::views::{
appearance::Appearance,
symbol_diff::{DiffViewState, SymbolReference, View},
symbol_diff::{DiffViewState, SymbolRefByName, View},
write_text,
};
const BYTES_PER_ROW: usize = 16;
fn find_section<'a>(obj: &'a ObjInfo, selected_symbol: &SymbolReference) -> Option<&'a ObjSection> {
obj.sections.iter().find(|section| section.name == selected_symbol.section_name)
fn find_section(obj: &ObjInfo, selected_symbol: &SymbolRefByName) -> Option<usize> {
obj.sections.iter().position(|section| section.name == selected_symbol.section_name)
}
fn data_row_ui(ui: &mut egui::Ui, address: usize, diffs: &[ObjDataDiff], appearance: &Appearance) {
@@ -130,16 +133,21 @@ fn split_diffs(diffs: &[ObjDataDiff]) -> Vec<Vec<ObjDataDiff>> {
fn data_table_ui(
table: TableBuilder<'_>,
left_obj: Option<&ObjInfo>,
right_obj: Option<&ObjInfo>,
selected_symbol: &SymbolReference,
left_obj: Option<&(ObjInfo, ObjDiff)>,
right_obj: Option<&(ObjInfo, ObjDiff)>,
selected_symbol: &SymbolRefByName,
config: &Appearance,
) -> Option<()> {
let left_section = left_obj.and_then(|obj| find_section(obj, selected_symbol));
let right_section = right_obj.and_then(|obj| find_section(obj, selected_symbol));
let left_section = left_obj.and_then(|(obj, diff)| {
find_section(obj, selected_symbol).map(|i| (&obj.sections[i], &diff.sections[i]))
});
let right_section = right_obj.and_then(|(obj, diff)| {
find_section(obj, selected_symbol).map(|i| (&obj.sections[i], &diff.sections[i]))
});
let total_bytes = left_section
.or(right_section)?
.1
.data_diff
.iter()
.fold(0usize, |accum, item| accum + item.len);
@@ -148,8 +156,8 @@ fn data_table_ui(
}
let total_rows = (total_bytes - 1) / BYTES_PER_ROW + 1;
let left_diffs = left_section.map(|section| split_diffs(&section.data_diff));
let right_diffs = right_section.map(|section| split_diffs(&section.data_diff));
let left_diffs = left_section.map(|(_, section)| split_diffs(&section.data_diff));
let right_diffs = right_section.map(|(_, section)| split_diffs(&section.data_diff));
table.body(|body| {
body.rows(config.code_font.size, total_rows, |mut row| {
+54 -52
View File
@@ -4,17 +4,17 @@ use egui::{text::LayoutJob, Align, Label, Layout, Sense, Vec2, Widget};
use egui_extras::{Column, TableBuilder, TableRow};
use objdiff_core::{
arch::ObjArch,
diff::display::{display_diff, DiffText, HighlightKind},
obj::{
ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjInsDiff, ObjInsDiffKind, ObjSection,
ObjSymbol,
diff::{
display::{display_diff, DiffText, HighlightKind},
get_symbol, ObjDiff, ObjInsDiff, ObjInsDiffKind, SymbolRef,
},
obj::{ObjInfo, ObjIns, ObjInsArg, ObjInsArgValue, ObjSection, ObjSymbol},
};
use time::format_description;
use crate::views::{
appearance::Appearance,
symbol_diff::{match_color_for_symbol, DiffViewState, SymbolReference, View},
symbol_diff::{match_color_for_symbol, DiffViewState, SymbolRefByName, View},
};
#[derive(Default)]
@@ -126,19 +126,15 @@ fn ins_context_menu(ui: &mut egui::Ui, ins: &ObjIns) {
});
}
fn find_symbol<'a>(
obj: &'a ObjInfo,
selected_symbol: &SymbolReference,
) -> Option<(&'a dyn ObjArch, &'a ObjSection, &'a ObjSymbol)> {
obj.sections.iter().find_map(|section| {
section.symbols.iter().find_map(|symbol| {
(symbol.name == selected_symbol.symbol_name).then_some((
obj.arch.as_ref(),
section,
symbol,
))
})
})
fn find_symbol(obj: &ObjInfo, selected_symbol: &SymbolRefByName) -> Option<SymbolRef> {
for (section_idx, section) in obj.sections.iter().enumerate() {
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
if symbol.name == selected_symbol.symbol_name {
return Some(SymbolRef { section_idx, symbol_idx });
}
}
}
None
}
fn diff_text_ui(
@@ -248,18 +244,21 @@ fn asm_row_ui(
fn asm_col_ui(
row: &mut TableRow<'_, '_>,
ins_diff: &ObjInsDiff,
arch: &dyn ObjArch,
section: &ObjSection,
symbol: &ObjSymbol,
obj: &(ObjInfo, ObjDiff),
symbol_ref: SymbolRef,
appearance: &Appearance,
ins_view_state: &mut FunctionViewState,
) {
let (section, symbol) = get_symbol(&obj.0, symbol_ref);
let ins_diff = &obj.1.sections[symbol_ref.section_idx].symbols[symbol_ref.symbol_idx]
.instructions[row.index()];
let (_, response) = row.col(|ui| {
asm_row_ui(ui, ins_diff, symbol, appearance, ins_view_state);
});
if let Some(ins) = &ins_diff.ins {
response.on_hover_ui_at_pointer(|ui| ins_hover_ui(ui, arch, section, ins, appearance));
response.on_hover_ui_at_pointer(|ui| {
ins_hover_ui(ui, obj.0.arch.as_ref(), section, ins, appearance)
});
}
}
@@ -271,41 +270,40 @@ fn empty_col_ui(row: &mut TableRow<'_, '_>) {
fn asm_table_ui(
table: TableBuilder<'_>,
left_obj: Option<&ObjInfo>,
right_obj: Option<&ObjInfo>,
selected_symbol: &SymbolReference,
left_obj: Option<&(ObjInfo, ObjDiff)>,
right_obj: Option<&(ObjInfo, ObjDiff)>,
selected_symbol: &SymbolRefByName,
appearance: &Appearance,
ins_view_state: &mut FunctionViewState,
) -> Option<()> {
let left_symbol = left_obj.and_then(|obj| find_symbol(obj, selected_symbol));
let right_symbol = right_obj.and_then(|obj| find_symbol(obj, selected_symbol));
let instructions_len = left_symbol.or(right_symbol).map(|(_, _, s)| s.instructions.len())?;
let left_symbol = left_obj.and_then(|(obj, _)| find_symbol(obj, selected_symbol));
let right_symbol = right_obj.and_then(|(obj, _)| find_symbol(obj, selected_symbol));
let instructions_len = match (left_symbol, right_symbol) {
(Some(left_symbol_ref), Some(_)) => left_obj.unwrap().1.sections
[left_symbol_ref.section_idx]
.symbols[left_symbol_ref.symbol_idx]
.instructions
.len(),
(Some(left_symbol_ref), None) => left_obj.unwrap().1.sections[left_symbol_ref.section_idx]
.symbols[left_symbol_ref.symbol_idx]
.instructions
.len(),
(None, Some(right_symbol_ref)) => right_obj.unwrap().1.sections
[right_symbol_ref.section_idx]
.symbols[right_symbol_ref.symbol_idx]
.instructions
.len(),
(None, None) => return None,
};
table.body(|body| {
body.rows(appearance.code_font.size, instructions_len, |mut row| {
let row_index = row.index();
if let Some((arch, section, symbol)) = left_symbol {
asm_col_ui(
&mut row,
&symbol.instructions[row_index],
arch,
section,
symbol,
appearance,
ins_view_state,
);
if let (Some(left_obj), Some(left_symbol_ref)) = (left_obj, left_symbol) {
asm_col_ui(&mut row, left_obj, left_symbol_ref, appearance, ins_view_state);
} else {
empty_col_ui(&mut row);
}
if let Some((arch, section, symbol)) = right_symbol {
asm_col_ui(
&mut row,
&symbol.instructions[row_index],
arch,
section,
symbol,
appearance,
ins_view_state,
);
if let (Some(right_obj), Some(right_symbol_ref)) = (right_obj, right_symbol) {
asm_col_ui(&mut row, right_obj, right_symbol_ref, appearance, ins_view_state);
} else {
empty_col_ui(&mut row);
}
@@ -412,8 +410,12 @@ pub fn function_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance
if let Some(match_percent) = result
.second_obj
.as_ref()
.and_then(|obj| find_symbol(obj, selected_symbol))
.and_then(|(_, _, symbol)| symbol.match_percent)
.and_then(|(obj, diff)| {
find_symbol(obj, selected_symbol).map(|sref| {
&diff.sections[sref.section_idx].symbols[sref.symbol_idx]
})
})
.and_then(|symbol| symbol.match_percent)
{
ui.colored_label(
match_color_for_symbol(match_percent, appearance),
+37 -17
View File
@@ -5,7 +5,10 @@ use egui::{
SelectableLabel, TextEdit, Ui, Vec2, Widget,
};
use egui_extras::{Size, StripBuilder};
use objdiff_core::obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags};
use objdiff_core::{
diff::{ObjDiff, ObjSymbolDiff},
obj::{ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags},
};
use crate::{
app::AppConfigRef,
@@ -17,7 +20,7 @@ use crate::{
views::{appearance::Appearance, function_diff::FunctionViewState, write_text},
};
pub struct SymbolReference {
pub struct SymbolRefByName {
pub symbol_name: String,
pub demangled_symbol_name: Option<String>,
pub section_name: String,
@@ -50,7 +53,7 @@ pub struct DiffViewState {
#[derive(Default)]
pub struct SymbolViewState {
pub highlighted_symbol: Option<String>,
pub selected_symbol: Option<SymbolReference>,
pub selected_symbol: Option<SymbolRefByName>,
pub reverse_fn_order: bool,
pub disable_reverse_fn_order: bool,
pub show_hidden_symbols: bool,
@@ -180,6 +183,7 @@ fn symbol_hover_ui(ui: &mut Ui, symbol: &ObjSymbol, appearance: &Appearance) {
fn symbol_ui(
ui: &mut Ui,
symbol: &ObjSymbol,
symbol_diff: &ObjSymbolDiff,
section: Option<&ObjSection>,
state: &mut SymbolViewState,
appearance: &Appearance,
@@ -210,7 +214,7 @@ fn symbol_ui(
write_text("h", appearance.deemphasized_text_color, &mut job, appearance.code_font.clone());
}
write_text("] ", appearance.text_color, &mut job, appearance.code_font.clone());
if let Some(match_percent) = symbol.match_percent {
if let Some(match_percent) = symbol_diff.match_percent {
write_text("(", appearance.text_color, &mut job, appearance.code_font.clone());
write_text(
&format!("{match_percent:.0}%"),
@@ -228,14 +232,14 @@ fn symbol_ui(
if response.clicked() {
if let Some(section) = section {
if section.kind == ObjSectionKind::Code {
state.selected_symbol = Some(SymbolReference {
state.selected_symbol = Some(SymbolRefByName {
symbol_name: symbol.name.clone(),
demangled_symbol_name: symbol.demangled_name.clone(),
section_name: section.name.clone(),
});
ret = Some(View::FunctionDiff);
} else if section.kind == ObjSectionKind::Data {
state.selected_symbol = Some(SymbolReference {
state.selected_symbol = Some(SymbolRefByName {
symbol_name: section.name.clone(),
demangled_symbol_name: None,
section_name: section.name.clone(),
@@ -262,7 +266,7 @@ fn symbol_matches_search(symbol: &ObjSymbol, search_str: &str) -> bool {
#[must_use]
fn symbol_list_ui(
ui: &mut Ui,
obj: &ObjInfo,
obj: &(ObjInfo, ObjDiff),
state: &mut SymbolViewState,
lower_search: &str,
appearance: &Appearance,
@@ -273,34 +277,50 @@ fn symbol_list_ui(
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
ui.style_mut().wrap = Some(false);
if !obj.common.is_empty() {
if !obj.0.common.is_empty() {
CollapsingHeader::new(".comm").default_open(true).show(ui, |ui| {
for symbol in &obj.common {
ret = ret.or(symbol_ui(ui, symbol, None, state, appearance));
for (symbol, symbol_diff) in obj.0.common.iter().zip(&obj.1.common) {
ret = ret.or(symbol_ui(ui, symbol, symbol_diff, None, state, appearance));
}
});
}
for section in &obj.sections {
for (section, section_diff) in obj.0.sections.iter().zip(&obj.1.sections) {
CollapsingHeader::new(format!("{} ({:x})", section.name, section.size))
.id_source(Id::new(section.name.clone()).with(section.index))
.default_open(true)
.show(ui, |ui| {
if section.kind == ObjSectionKind::Code && state.reverse_fn_order {
for symbol in section.symbols.iter().rev() {
for (symbol, symbol_diff) in
section.symbols.iter().zip(&section_diff.symbols).rev()
{
if !symbol_matches_search(symbol, lower_search) {
continue;
}
ret =
ret.or(symbol_ui(ui, symbol, Some(section), state, appearance));
ret = ret.or(symbol_ui(
ui,
symbol,
symbol_diff,
Some(section),
state,
appearance,
));
}
} else {
for symbol in &section.symbols {
for (symbol, symbol_diff) in
section.symbols.iter().zip(&section_diff.symbols)
{
if !symbol_matches_search(symbol, lower_search) {
continue;
}
ret =
ret.or(symbol_ui(ui, symbol, Some(section), state, appearance));
ret = ret.or(symbol_ui(
ui,
symbol,
symbol_diff,
Some(section),
state,
appearance,
));
}
}
});