From faebddbc5ee68e616ac92432481ddad6d57aa4e5 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sun, 18 Aug 2024 13:42:41 -0600 Subject: [PATCH] More updates to report types --- objdiff-cli/protos/proto_descriptor.bin | Bin 5977 -> 7325 bytes objdiff-cli/protos/report.proto | 120 +++++++++++----- objdiff-cli/src/cmd/report.rs | 182 +++++++++--------------- objdiff-cli/src/util/report.rs | 96 ++++++++++--- 4 files changed, 228 insertions(+), 170 deletions(-) diff --git a/objdiff-cli/protos/proto_descriptor.bin b/objdiff-cli/protos/proto_descriptor.bin index 1c1f0228ddc22edbaf20a3a9f00824ca0134c844..635b4a5cf36075b03efef6c086be49bf3ef75490 100644 GIT binary patch literal 7325 zcmbVQ+inv{8g?HXcb9YQoScRvZIdG*fq`W*8Fqz2V1ONEf((d}M#$>Kox~H{ZR|GL zfLLjSX0`XLy_x4&?W647>@(~m?Dtnym)nUQhT#I`@2dK~zy5QX{j$Xdn?WONMjMT0 z7=_Moxc_6ha(K8Qz83yvFl#sPTkU4hayHobVf)>?vr^5E4vtHWpm`9~qnzexD|&H^ zFYVIGGqK>zG8ToAUo9Pkkn}8wR>K`IZDHZBd+=E>7ASb1t(HanHkc{UEA@^;XWp=Aq&+ z|NKq-)mdX>@pU>|ZN7C>E_613&1NoaR?0B0%=Gy~QlmnH4HUUsoj)+ECjG}9mA#$Y z==SkO?BAl=>D*%JcD)j{;51_;d)qyKM1a4?c~0 ziI3mGz_iO_w%k=I=O~GWp6F19AADMtIIXaaY%MkX=r~8&V?Qy9OniKXZ?VcT`-5RN zSW0j|7-TG#D`mc#KrH-Qi@i@?=;?h#M62>HE(84Ueg%c@(6gWlxA_HuI&P z`KR(s@`!Vt4VQzOUq7m%ICTOUP($i_Z2lW;YvM?r(0V;M=}+~~sZUm^sQV-* zbB!smE~z^37<8uX$)eMzb_g9WD>~`kMJI8vPtl>K)Hk%rRw}e9&l5!_!_p-RlLu*L zl%lY^2>lN^+TreL*v&4(&Yh34&grN1!!T*yI|=iC>XC(&fz7q;yckmlwYDh+mG$<}l3 ztoMQjIFBl~7an>v$bsA2KZ}A^jH&_E)-8#H>@jS6JeGiJPffF%92Ex2>aJVxzG){F z=BS+FJhTqztb!)3vi78e)&ZT(&9GGt4Pi*nyH|TbORL5D8UP(qRxkiMWL=e@2I0(m z(h87cOrVY^RZXCdC@Yvi9Z^=m#<3n&;HW*Vkme_11E&E!rmT<#^q8{3 zfMbmJumXoZXN3WPjw>q+0CZegVE~}xu)+#AZN@Ps^;!3djD>o;wjVSp8dRXgmdyZj zQrRE_%t<>ZEt>)6>S(Rh_++Q z=*w=tA7Y$4v930dXVm>|AkSEMk?=gRfjqOY#1@gsnq$tU3K~>JCjW>@)Wb_jWVA?Q zk-il6v(_+MfHE4T?>qu$TBWZ#LsdYifL(CcJlY+RLIT2kRwNE2s4$O^S>||H%A*{( z9##LAcUp;#kt+DFC2L6xq9J7S3Q_|>J}VMN(@Dkrl*o5TAjl)%@8gcTV=ks{Y3?0w zn^z=qZgd>Pb!w&E!gPZQ<9kulueYi`J#zTYRdIXD%tgy#*PyLVw_nyBcglMK44R%| zJW=iFy4VE!FH6rt0>H98#7zMSx-E~6@hsE{dzEM5#Zt;GkVK^$ugd%5BpsC|g#^@9 zn3SSg*HMes^lOOf=kj&YWz{664x6qiOdSks(xyNG!`jrG)SF7uTE4(bl1{?9zQ9Y8 zd=~blFhH@cC=5`nD+(%5>r?ZRf(q1n;Tl`xm%y=``kFiEJ^BkgUrsC*aYA0q~^An9h00C zP@DK&Luf|jETb zvyHld*9uY?OFMv@=hu%X6e?Q;cdkEUH*j|pE;({X;gTbFfcquKg(Dxqk*gdx>@|ON z@++x1Iqs3{C@3&scqFGQNMLvb$5GmvBs{(}ZIiI%tjE$>IEu7=EYlVeNFK|yg+#bW zTRhJ&jhNr2o@uB)z0$VMZ6s-2;F!----}k)@GE?d5q+F$053;=GvJf+QBcS19R=Qj zc!>*h(WLa;nqURoVSvo_5-~W*@6c!lZ!2NDyC-80T=r5%9weN9DI*UORC|fY%X2g= zz0ybB366HqZZ`4rYFC3F(5b<_Qebq?S9X@)6Keo_H9Ug$qnjDW{2{fkVS*Pgpq95E zM5jT3so^w~n3&+{9YkfkNv97U2Mty(*sK-5V5~{tH4uJ&J*Rohc zf@-gwabCDGlo};iO%|?roKg=Y%HdwO$3ak(6b0SBlodk*)<98`rp!=#_mRXCa$<^( z{Yd(;ECL8y5J0{HqrUFTri(Q|`*K!-1ZW@GdliPY95YBYHFp$MPy|Lf(FAEi4P?Qy z$d*Sv3tp!fV-q6xNO}|!0FGpMKmzd*?nGV9BAqJesiLbXCv^L0C9GAV2)(s(=+%Q$ z*-3F0m#xUlLc(P$axj8Kz!RdALIP(6os^Q)B0HYwS7egLPU%nC3~H#DWkpLiK2fw} z;}bH}{sE$>P9r$O>Yk)%QL zMwWK~f#i)W?~p+92IYMXQD{46JM~u$&94)MJ$kZ8aC+RdWl-7_l&3v|veR_133oV^ zK?w-}r!puZL61`zlr}l>44(QOL5Z`t`t0tXP{qKn1N%NNFmEaST!j4@;kGb7JDYhff}vJ7G&@i6{^ z$rt1;-;k$#MjrD8`H<{7r@9a};K@AAL*CeZYFE{%+Ev{`|9e^IPrE0*)4}q|X>ZUo z#oqhBbPf*=mfh3le|(*7Sp&1I#}3Xuefqh5v^UuQ&_3y&?sty|6|dplRIO2av_mJ~ zy8~0zde9r}eQfXdI^BwbSE(+}U3Lxq z2_`LP*!)MJ_4!j{ba1?P)U~~hTlVj(+E0D`743K3y{7%t)1Y;s{URXStKZVhOIkyH z{FSY7UHq~&bsg>Zj!r&y2i*#_FWSnq-r+c(_{O6BY6tPub+=WWMSSxwUN z`NqnP=JI-q*Mi_a-&VnOvb%0ugUPw+=wycpqsp0i6<>Yx5Pqq8Q+WPbE?q`kcHGZ{mFDdVfm zq}Eym850DV5tK*!LB=zF5EXTz@l0imAF77V>ySW5D3>A_hS5xle4tvLX*kNN3Lkl( zWmS|SI*Z3r1X{K{$uy*tQ`3B;K+6eDftHIW(lgL#gb$IS}aMzP)m}bMd{r_(6l5Of~F6Ur=?XMW7Xg7J*iX(<(=x6{pbzsNLg{F2;Fqpl@v9oG(M&o3ou-!z<*%Bf~FoRn$} zD?!f$Cev^d#Cq{fWUOxc(ZJd?sDM)aFMF9ZXghV3=POvC&2HFIR!Xr>-Q2>Be78*&g%%Z>VyC5uD5V)_MgB>O`IesY>hPzLpZ6GYpEri)NahN6KvnQTHvERoYcih9!~1w zBo8O`vKwGe<>92>oVSCbOwi2Sbb~^QZ1SKe8k9L66aZXoj{5`z!#N%lAh?_3K~dym zUT8EZ^FpIRnHL%j%Dm7pC<{Da$_~l`&leDA3p`&ype^uxS+K|z06+y6xdK3ly~w=) zf|Eu33ZjMr!O7wremS8RipFM1xe5bVQ*rL95t|YKXm^E1^}ZV?=^1EuizVCjzy$X)e{fx=OWlh~ zHj99=xUb5V0swdSRX#;9ykBq=f-|_gKR&@U9IdEvHl!?8ghpAc6x?(Mc0I0`F{YvG ztK4BuVmk%hRM50kYpGcA<(kGH644+DpwCSG;jcWH) zlB7-lRFXt1o=K9l>7PlGwCSHolCD zFZcxk0NM+o(QbdiF9@80_JUs!p~7x|$sZd4puLnN>0{$1e{A3kw3qnUAobu=_El!v zsfVueiq(@P^=$As0>Iq{X8{Dm4IW1zxZB`y%u=ggbMgRa(`!~V5X$(P6%9m|W%u0# z1ShZgDp}IfCKnC>PBz5}jm4%op>UgJ_pXB_oNRI#vjpDoyAA+O-iQ+)PTq(U(%Bn+ z!@!bwH{81}OR=~3H3eXiTa7?)vL#MPbzA&~!5N%vHRo)Jb0+vU^PMX(wf$QzagIv- zmP-tP62IjV1Hteumlz1{-g1d^bmtwu7yxqG;fnzwr=7$tXdtqjc8eVdPImZWIV$lk zOBw)9cEt&mcvqZIxZP>@$q!37*@eJKJCO-~pr1js%1M|%L}k0yBDz?MHxB?V)?z6F p!LY@f2MF$3ym=yu^p5KR0NOjQ2N0ccJ%B)aH!}J@YyXYD`d_AFpvwRN diff --git a/objdiff-cli/protos/report.proto b/objdiff-cli/protos/report.proto index 7fb91a7..ebc0901 100644 --- a/objdiff-cli/protos/report.proto +++ b/objdiff-cli/protos/report.proto @@ -2,84 +2,134 @@ syntax = "proto3"; package objdiff.report; -message Report { +// Progress info for a report or unit +message Measures { + // Overall match percent, including partially matched functions and data float fuzzy_match_percent = 1; + // Total size of code in bytes uint64 total_code = 2; + // Fully matched code size in bytes uint64 matched_code = 3; + // Fully matched code percent float matched_code_percent = 4; + // Total size of data in bytes uint64 total_data = 5; + // Fully matched data size in bytes uint64 matched_data = 6; + // Fully matched data percent float matched_data_percent = 7; + // Total number of functions uint32 total_functions = 8; + // Fully matched functions uint32 matched_functions = 9; + // Fully matched functions percent float matched_functions_percent = 10; - repeated ReportUnit units = 11; } +// Project progress report +message Report { + // Overall progress info + Measures measures = 1; + // Units within this report + repeated ReportUnit units = 2; +} + +// A unit of the report (usually a translation unit) message ReportUnit { + // The name of the unit string name = 1; - float fuzzy_match_percent = 2; - uint64 total_code = 3; - uint64 matched_code = 4; - uint64 total_data = 5; - uint64 matched_data = 6; - uint32 total_functions = 7; - uint32 matched_functions = 8; - optional bool complete = 9; - optional string module_name = 10; - optional uint32 module_id = 11; - repeated ReportItem sections = 12; - repeated ReportItem functions = 13; + // Progress info for this unit + Measures measures = 2; + // Sections within this unit + repeated ReportItem sections = 3; + // Functions within this unit + repeated ReportItem functions = 4; + // Extra metadata for this unit + optional ReportUnitMetadata metadata = 5; } +// Extra metadata for a unit +message ReportUnitMetadata { + // Whether this unit is marked as complete (or "linked") + optional bool complete = 1; + // The name of the module this unit belongs to + optional string module_name = 2; + // The ID of the module this unit belongs to + optional uint32 module_id = 3; + // The path to the source file of this unit + optional string source_path = 4; +} + +// A section or function within a unit message ReportItem { + // The name of the item string name = 1; + // The size of the item in bytes uint64 size = 2; + // The overall match percent for this item float fuzzy_match_percent = 3; - optional string demangled_name = 4; - optional uint64 address = 5; + // Extra metadata for this item + optional ReportItemMetadata metadata = 4; } -// Used as stdin for the changes command +// Extra metadata for an item +message ReportItemMetadata { + // The demangled name of the function + optional string demangled_name = 1; + // The virtual address of the function or section + optional uint64 virtual_address = 2; +} + +// A pair of reports to compare and generate changes message ChangesInput { + // The previous report Report from = 1; + // The current report Report to = 2; } +// Changes between two reports message Changes { - ChangeInfo from = 1; - ChangeInfo to = 2; + // The progress info for the previous report + Measures from = 1; + // The progress info for the current report + Measures to = 2; + // Units that changed repeated ChangeUnit units = 3; } -message ChangeInfo { - float fuzzy_match_percent = 1; - uint64 total_code = 2; - uint64 matched_code = 3; - float matched_code_percent = 4; - uint64 total_data = 5; - uint64 matched_data = 6; - float matched_data_percent = 7; - uint32 total_functions = 8; - uint32 matched_functions = 9; - float matched_functions_percent = 10; -} - +// A changed unit message ChangeUnit { + // The name of the unit string name = 1; - optional ChangeInfo from = 2; - optional ChangeInfo to = 3; + // The previous progress info (omitted if new) + optional Measures from = 2; + // The current progress info (omitted if removed) + optional Measures to = 3; + // Sections that changed repeated ChangeItem sections = 4; + // Functions that changed repeated ChangeItem functions = 5; + // Extra metadata for this unit + optional ReportUnitMetadata metadata = 6; } +// A changed section or function message ChangeItem { + // The name of the item string name = 1; + // The previous progress info (omitted if new) optional ChangeItemInfo from = 2; + // The current progress info (omitted if removed) optional ChangeItemInfo to = 3; + // Extra metadata for this item + optional ReportItemMetadata metadata = 4; } +// Progress info for a section or function message ChangeItemInfo { + // The overall match percent for this item float fuzzy_match_percent = 1; + // The size of the item in bytes uint64 size = 2; -} \ No newline at end of file +} diff --git a/objdiff-cli/src/cmd/report.rs b/objdiff-cli/src/cmd/report.rs index 42b4c1d..b1c8197 100644 --- a/objdiff-cli/src/cmd/report.rs +++ b/objdiff-cli/src/cmd/report.rs @@ -19,8 +19,8 @@ use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; use tracing::{info, warn}; use crate::util::report::{ - ChangeInfo, ChangeItem, ChangeItemInfo, ChangeUnit, Changes, ChangesInput, Report, ReportItem, - ReportUnit, + ChangeItem, ChangeItemInfo, ChangeUnit, Changes, ChangesInput, Measures, Report, ReportItem, + ReportItemMetadata, ReportUnit, ReportUnitMetadata, }; #[derive(FromArgs, PartialEq, Debug)] @@ -90,7 +90,7 @@ impl OutputFormat { fn from_str(s: &str) -> Result { match s { "json" => Ok(Self::Json), - "binpb" | "proto" | "protobuf" => Ok(Self::Proto), + "binpb" | "pb" | "proto" | "protobuf" => Ok(Self::Proto), _ => bail!("Invalid output format: {}", s), } } @@ -117,7 +117,7 @@ fn generate(args: GenerateArgs) -> Result<()> { ); let start = Instant::now(); - let mut report = Report::default(); + let mut units = vec![]; let mut existing_functions: HashSet = HashSet::new(); if args.deduplicate { // If deduplicating, we need to run single-threaded @@ -129,11 +129,11 @@ fn generate(args: GenerateArgs) -> Result<()> { project.base_dir.as_deref(), Some(&mut existing_functions), )? { - report.units.push(unit); + units.push(unit); } } } else { - let units = project + let vec = project .objects .par_iter_mut() .map(|object| { @@ -146,38 +146,10 @@ fn generate(args: GenerateArgs) -> Result<()> { ) }) .collect::>>>()?; - report.units = units.into_iter().flatten().collect(); + units = vec.into_iter().flatten().collect(); } - for unit in &report.units { - report.fuzzy_match_percent += unit.fuzzy_match_percent * unit.total_code as f32; - report.total_code += unit.total_code; - 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.matched_functions += unit.matched_functions; - } - if report.total_code == 0 { - report.fuzzy_match_percent = 100.0; - } else { - report.fuzzy_match_percent /= report.total_code as f32; - } - - report.matched_code_percent = if report.total_code == 0 { - 100.0 - } else { - 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 { - 100.0 - } else { - report.matched_functions as f32 / report.total_functions as f32 * 100.0 - }; + let measures = units.iter().flat_map(|u| u.measures.into_iter()).collect(); + let report = Report { measures: Some(measures), units }; let duration = start.elapsed(); info!("Report generated in {}.{:03}s", duration.as_secs(), duration.subsec_millis()); write_output(&report, args.output.as_deref(), output_format)?; @@ -258,18 +230,21 @@ fn report_object( }) .transpose()?; let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), None)?; - let mut unit = ReportUnit { - name: object.name().to_string(), + + let metadata = ReportUnitMetadata { complete: object.complete, module_name: target .as_ref() .and_then(|o| o.split_meta.as_ref()) .and_then(|m| m.module_name.clone()), module_id: target.as_ref().and_then(|o| o.split_meta.as_ref()).and_then(|m| m.module_id), - ..Default::default() + source_path: None, // TODO }; - let obj = target.as_ref().or(base.as_ref()).unwrap(); + let mut measures = Measures::default(); + let mut sections = vec![]; + let mut functions = vec![]; + let obj = target.as_ref().or(base.as_ref()).unwrap(); 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) { let section_match_percent = section_diff.match_percent.unwrap_or_else(|| { @@ -281,19 +256,21 @@ fn report_object( 0.0 } }); - unit.sections.push(ReportItem { + sections.push(ReportItem { name: section.name.clone(), - demangled_name: None, fuzzy_match_percent: section_match_percent, size: section.size, - address: section.virtual_address, + metadata: Some(ReportItemMetadata { + demangled_name: None, + virtual_address: section.virtual_address, + }), }); match section.kind { ObjSectionKind::Data | ObjSectionKind::Bss => { - unit.total_data += section.size; + measures.total_data += section.size; if section_match_percent == 100.0 { - unit.matched_data += section.size; + measures.matched_data += section.size; } continue; } @@ -321,76 +298,35 @@ fn report_object( 0.0 } }); - unit.fuzzy_match_percent += match_percent * symbol.size as f32; - unit.total_code += symbol.size; + measures.fuzzy_match_percent += match_percent * symbol.size as f32; + measures.total_code += symbol.size; if match_percent == 100.0 { - unit.matched_code += symbol.size; + measures.matched_code += symbol.size; } - unit.functions.push(ReportItem { + functions.push(ReportItem { name: symbol.name.clone(), - demangled_name: symbol.demangled_name.clone(), size: symbol.size, fuzzy_match_percent: match_percent, - address: symbol.virtual_address, + metadata: Some(ReportItemMetadata { + demangled_name: symbol.demangled_name.clone(), + virtual_address: symbol.virtual_address, + }), }); if match_percent == 100.0 { - unit.matched_functions += 1; + measures.matched_functions += 1; } - unit.total_functions += 1; - } - } - if unit.total_code == 0 { - unit.fuzzy_match_percent = 100.0; - } else { - unit.fuzzy_match_percent /= unit.total_code as f32; - } - Ok(Some(unit)) -} - -impl From<&Report> for ChangeInfo { - fn from(report: &Report) -> Self { - Self { - fuzzy_match_percent: report.fuzzy_match_percent, - total_code: report.total_code, - matched_code: report.matched_code, - 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, - matched_functions: report.matched_functions, - matched_functions_percent: report.matched_functions_percent, - } - } -} - -impl From<&ReportUnit> for ChangeInfo { - fn from(value: &ReportUnit) -> Self { - Self { - fuzzy_match_percent: value.fuzzy_match_percent, - total_code: value.total_code, - matched_code: value.matched_code, - matched_code_percent: if value.total_code == 0 { - 100.0 - } else { - 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, - matched_functions: value.matched_functions, - matched_functions_percent: if value.total_functions == 0 { - 100.0 - } else { - value.matched_functions as f32 / value.total_functions as f32 * 100.0 - }, + measures.total_functions += 1; } } + measures.calc_fuzzy_match_percent(); + measures.calc_matched_percent(); + Ok(Some(ReportUnit { + name: object.name().to_string(), + measures: Some(measures), + sections, + functions, + metadata: Some(metadata), + })) } impl From<&ReportItem> for ChangeItemInfo { @@ -417,25 +353,25 @@ fn changes(args: ChangesArgs) -> Result<()> { let current = read_report(&args.current)?; (previous, current) }; - let mut changes = Changes { - from: Some(ChangeInfo::from(&previous)), - to: Some(ChangeInfo::from(¤t)), - units: vec![], - }; + let mut changes = Changes { from: previous.measures, to: current.measures, units: vec![] }; for prev_unit in &previous.units { 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); - if !functions.is_empty() || !matches!(&curr_unit_info, Some(v) if v == &prev_unit_info) { + let prev_measures = prev_unit.measures; + let curr_measures = curr_unit.and_then(|u| u.measures); + if !functions.is_empty() || prev_measures != curr_measures { changes.units.push(ChangeUnit { name: prev_unit.name.clone(), - from: Some(prev_unit_info), - to: curr_unit_info, + from: prev_measures, + to: curr_measures, sections, functions, + metadata: curr_unit + .as_ref() + .and_then(|u| u.metadata.clone()) + .or_else(|| prev_unit.metadata.clone()), }); } } @@ -444,9 +380,10 @@ fn changes(args: ChangesArgs) -> Result<()> { changes.units.push(ChangeUnit { name: curr_unit.name.clone(), from: None, - to: Some(ChangeInfo::from(curr_unit)), + to: curr_unit.measures, sections: process_new_items(&curr_unit.sections), functions: process_new_items(&curr_unit.functions), + metadata: curr_unit.metadata.clone(), }); } } @@ -473,6 +410,7 @@ fn process_items &Vec>( name: prev_func.name.clone(), from: Some(prev_func_info), to: Some(curr_func_info), + metadata: curr_func.as_ref().unwrap().metadata.clone(), }); } } else { @@ -480,6 +418,7 @@ fn process_items &Vec>( name: prev_func.name.clone(), from: Some(prev_func_info), to: None, + metadata: prev_func.metadata.clone(), }); } } @@ -489,6 +428,7 @@ fn process_items &Vec>( name: curr_func.name.clone(), from: None, to: Some(ChangeItemInfo::from(curr_func)), + metadata: curr_func.metadata.clone(), }); } } @@ -498,6 +438,7 @@ fn process_items &Vec>( name: prev_func.name.clone(), from: Some(ChangeItemInfo::from(prev_func)), to: None, + metadata: prev_func.metadata.clone(), }); } } @@ -507,7 +448,12 @@ fn process_items &Vec>( fn process_new_items(items: &[ReportItem]) -> Vec { items .iter() - .map(|f| ChangeItem { name: f.name.clone(), from: None, to: Some(ChangeItemInfo::from(f)) }) + .map(|item| ChangeItem { + name: item.name.clone(), + from: None, + to: Some(ChangeItemInfo::from(item)), + metadata: item.metadata.clone(), + }) .collect() } diff --git a/objdiff-cli/src/util/report.rs b/objdiff-cli/src/util/report.rs index 31581da..ecba347 100644 --- a/objdiff-cli/src/util/report.rs +++ b/objdiff-cli/src/util/report.rs @@ -39,6 +39,56 @@ impl Report { } } +impl Measures { + /// Average the fuzzy match percentage over total code bytes. + pub fn calc_fuzzy_match_percent(&mut self) { + if self.total_code == 0 { + self.fuzzy_match_percent = 100.0; + } else { + self.fuzzy_match_percent /= self.total_code as f32; + } + } + + /// Calculate the percentage of matched code, data, and functions. + pub fn calc_matched_percent(&mut self) { + self.matched_code_percent = if self.total_code == 0 { + 100.0 + } else { + self.matched_code as f32 / self.total_code as f32 * 100.0 + }; + self.matched_data_percent = if self.total_data == 0 { + 100.0 + } else { + self.matched_data as f32 / self.total_data as f32 * 100.0 + }; + self.matched_functions_percent = if self.total_functions == 0 { + 100.0 + } else { + self.matched_functions as f32 / self.total_functions as f32 * 100.0 + }; + } +} + +/// Allows [collect](Iterator::collect) to be used on an iterator of [Measures]. +impl FromIterator for Measures { + fn from_iter(iter: T) -> Self + where T: IntoIterator { + let mut measures = Measures::default(); + for other in iter { + measures.fuzzy_match_percent += other.fuzzy_match_percent * other.total_code as f32; + measures.total_code += other.total_code; + measures.matched_code += other.matched_code; + measures.total_data += other.total_data; + measures.matched_data += other.matched_data; + measures.total_functions += other.total_functions; + measures.matched_functions += other.matched_functions; + } + measures.calc_fuzzy_match_percent(); + measures.calc_matched_percent(); + measures + } +} + // Older JSON report types #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] struct LegacyReport { @@ -58,16 +108,18 @@ struct LegacyReport { impl From for Report { fn from(value: LegacyReport) -> Self { Self { - fuzzy_match_percent: value.fuzzy_match_percent, - total_code: value.total_code, - matched_code: value.matched_code, - matched_code_percent: value.matched_code_percent, - total_data: value.total_data, - matched_data: value.matched_data, - matched_data_percent: value.matched_data_percent, - total_functions: value.total_functions, - matched_functions: value.matched_functions, - matched_functions_percent: value.matched_functions_percent, + measures: Some(Measures { + fuzzy_match_percent: value.fuzzy_match_percent, + total_code: value.total_code, + matched_code: value.matched_code, + matched_code_percent: value.matched_code_percent, + total_data: value.total_data, + matched_data: value.matched_data, + matched_data_percent: value.matched_data_percent, + total_functions: value.total_functions, + matched_functions: value.matched_functions, + matched_functions_percent: value.matched_functions_percent, + }), units: value.units.into_iter().map(ReportUnit::from).collect(), } } @@ -95,8 +147,7 @@ struct LegacyReportUnit { impl From for ReportUnit { fn from(value: LegacyReportUnit) -> Self { - Self { - name: value.name.clone(), + let mut measures = Measures { fuzzy_match_percent: value.fuzzy_match_percent, total_code: value.total_code, matched_code: value.matched_code, @@ -104,11 +155,20 @@ impl From for ReportUnit { matched_data: value.matched_data, total_functions: value.total_functions, matched_functions: value.matched_functions, - complete: value.complete, - module_name: value.module_name.clone(), - module_id: value.module_id, + ..Default::default() + }; + measures.calc_matched_percent(); + Self { + name: value.name.clone(), + measures: Some(measures), sections: value.sections.into_iter().map(ReportItem::from).collect(), functions: value.functions.into_iter().map(ReportItem::from).collect(), + metadata: Some(ReportUnitMetadata { + complete: value.complete, + module_name: value.module_name.clone(), + module_id: value.module_id, + ..Default::default() + }), } } } @@ -133,10 +193,12 @@ impl From for ReportItem { fn from(value: LegacyReportItem) -> Self { Self { name: value.name, - demangled_name: value.demangled_name, - address: value.address, size: value.size, fuzzy_match_percent: value.fuzzy_match_percent, + metadata: Some(ReportItemMetadata { + demangled_name: value.demangled_name, + virtual_address: value.address, + }), } } }