Skip to content

Commit 2f4f119

Browse files
committed
fixed script
1 parent 1b47162 commit 2f4f119

File tree

1 file changed

+147
-14
lines changed

1 file changed

+147
-14
lines changed

scripts/verify_parity.rs

Lines changed: 147 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
use std::io::{BufRead, BufReader};
4242
use std::path::{Path, PathBuf};
4343
use std::process::Command;
44+
use std::time::{Duration, Instant};
4445
use std::{env, fs};
4546

4647
use sha2::{Digest, Sha256};
@@ -59,6 +60,16 @@ struct DriveResult {
5960
result: VerifyResult,
6061
baseline_lines: usize,
6162
rust_lines: usize,
63+
mft_size_bytes: u64,
64+
parse_duration: Option<Duration>,
65+
}
66+
67+
/// Result from running uffs to regenerate output
68+
#[derive(Debug)]
69+
struct RegenerateResult {
70+
output_path: PathBuf,
71+
parse_duration: Duration,
72+
mft_size_bytes: u64,
6273
}
6374

6475
#[derive(Debug)]
@@ -115,26 +126,27 @@ fn run_legacy_mode(args: &[String], base_dir: &Path) {
115126

116127
// Determine mode
117128
let mode = &args[3];
118-
let rust_output = match mode.as_str() {
129+
let (rust_output, parse_duration, mft_size) = match mode.as_str() {
119130
"--regenerate" => {
120131
let golden_baseline = find_golden_baseline_file(&drive_dir, &drive_lower);
121132
let tz_offset =
122133
explicit_tz.unwrap_or_else(|| detect_tz_from_baseline(&golden_baseline));
123-
regenerate_rust_output(
134+
let regen = regenerate_rust_output(
124135
&drive_dir,
125136
&drive_letter,
126137
&drive_lower,
127138
tz_offset,
128139
custom_bin.as_deref(),
129-
)
140+
);
141+
(regen.output_path, Some(regen.parse_duration), regen.mft_size_bytes)
130142
}
131143
"--rust" => {
132144
if args.len() < 5 {
133145
eprintln!("ERROR: --rust requires a path argument");
134146
print_usage(&args[0]);
135147
std::process::exit(1);
136148
}
137-
PathBuf::from(&args[4])
149+
(PathBuf::from(&args[4]), None, 0)
138150
}
139151
_ => {
140152
eprintln!("ERROR: Unknown mode: {}", mode);
@@ -143,7 +155,19 @@ fn run_legacy_mode(args: &[String], base_dir: &Path) {
143155
}
144156
};
145157

146-
let result = verify_single_drive(base_dir, &drive_dir, &drive_letter, &rust_output);
158+
let result = verify_single_drive(base_dir, &drive_dir, &drive_letter, &rust_output, parse_duration, mft_size);
159+
160+
// Print timing for single drive if available
161+
if let Some(duration) = result.parse_duration {
162+
println!();
163+
println!("⏱️ MFT Parse Time: {}", format_duration(duration));
164+
if result.mft_size_bytes > 0 {
165+
let mb = result.mft_size_bytes as f64 / (1024.0 * 1024.0);
166+
let throughput = mb / duration.as_secs_f64();
167+
println!(" MFT Size: {:.1} MB, Throughput: {:.1} MB/s", mb, throughput);
168+
}
169+
}
170+
147171
std::process::exit(if result.result == VerifyResult::Mismatch {
148172
1
149173
} else {
@@ -217,27 +241,30 @@ fn run_multi_drive_mode(args: &[String], base_dir: &Path) {
217241
result: VerifyResult::Skipped,
218242
baseline_lines: 0,
219243
rust_lines: 0,
244+
mft_size_bytes: 0,
245+
parse_duration: None,
220246
});
221247
continue;
222248
}
223249
};
224250

225251
// Generate or use provided rust output
226-
let rust_output = if let Some(ref path) = rust_output_path {
227-
PathBuf::from(path)
252+
let (rust_output, parse_duration, mft_size) = if let Some(ref path) = rust_output_path {
253+
(PathBuf::from(path), None, 0u64)
228254
} else {
229255
let tz_offset =
230256
explicit_tz.unwrap_or_else(|| detect_tz_from_baseline(&golden_baseline));
231-
regenerate_rust_output(
257+
let regen = regenerate_rust_output(
232258
&drive_dir,
233259
&drive_letter,
234260
drive_lower,
235261
tz_offset,
236262
custom_bin.as_deref(),
237-
)
263+
);
264+
(regen.output_path, Some(regen.parse_duration), regen.mft_size_bytes)
238265
};
239266

240-
let result = verify_single_drive(base_dir, &drive_dir, &drive_letter, &rust_output);
267+
let result = verify_single_drive(base_dir, &drive_dir, &drive_letter, &rust_output, parse_duration, mft_size);
241268
results.push(result);
242269
println!();
243270
}
@@ -294,6 +321,8 @@ fn verify_single_drive(
294321
drive_dir: &Path,
295322
drive_letter: &str,
296323
rust_output: &Path,
324+
parse_duration: Option<Duration>,
325+
mft_size_bytes: u64,
297326
) -> DriveResult {
298327
let drive_lower = drive_letter.to_lowercase();
299328
let golden_baseline_file = find_golden_baseline_file(drive_dir, &drive_lower);
@@ -308,6 +337,8 @@ fn verify_single_drive(
308337
result: VerifyResult::Mismatch,
309338
baseline_lines: 0,
310339
rust_lines: 0,
340+
mft_size_bytes,
341+
parse_duration,
311342
};
312343
}
313344

@@ -341,6 +372,8 @@ fn verify_single_drive(
341372
result: VerifyResult::StrictMatch,
342373
baseline_lines: golden_hashes.line_count,
343374
rust_lines: rust_hashes.line_count,
375+
mft_size_bytes,
376+
parse_duration,
344377
};
345378
}
346379

@@ -361,6 +394,8 @@ fn verify_single_drive(
361394
result: VerifyResult::SortedMatch,
362395
baseline_lines: golden_hashes.line_count,
363396
rust_lines: rust_hashes.line_count,
397+
mft_size_bytes,
398+
parse_duration,
364399
};
365400
}
366401

@@ -384,6 +419,8 @@ fn verify_single_drive(
384419
result: VerifyResult::Mismatch,
385420
baseline_lines: golden_hashes.line_count,
386421
rust_lines: rust_hashes.line_count,
422+
mft_size_bytes,
423+
parse_duration,
387424
}
388425
}
389426

@@ -440,9 +477,84 @@ fn print_summary(results: &[DriveResult]) {
440477
} else {
441478
println!(" ⚠️ {} drive(s) had mismatches", mismatch);
442479
}
480+
481+
// Print timing table if any drives have timing data
482+
let timed_results: Vec<_> = results.iter().filter(|r| r.parse_duration.is_some()).collect();
483+
if !timed_results.is_empty() {
484+
print_timing_table(&timed_results);
485+
}
443486
println!();
444487
}
445488

489+
/// Print a timing table for MFT parsing performance
490+
fn print_timing_table(results: &[&DriveResult]) {
491+
println!();
492+
println!("╔══════════════════════════════════════════════════════════════════════════════╗");
493+
println!("║ MFT PARSING PERFORMANCE ║");
494+
println!("╠═══════════╦══════════════╦═══════════════╦═══════════════╦══════════════════╣");
495+
println!("║ Drive ║ MFT Size ║ Parse Time ║ Throughput ║ Files/sec ║");
496+
println!("╠═══════════╬══════════════╬═══════════════╬═══════════════╬══════════════════╣");
497+
498+
let mut total_bytes: u64 = 0;
499+
let mut total_duration = Duration::ZERO;
500+
let mut total_files: usize = 0;
501+
502+
for result in results {
503+
if let Some(duration) = result.parse_duration {
504+
let mft_mb = result.mft_size_bytes as f64 / (1024.0 * 1024.0);
505+
let secs = duration.as_secs_f64();
506+
let throughput_mb = if secs > 0.0 { mft_mb / secs } else { 0.0 };
507+
let files_per_sec = if secs > 0.0 { result.rust_lines as f64 / secs } else { 0.0 };
508+
509+
total_bytes += result.mft_size_bytes;
510+
total_duration += duration;
511+
total_files += result.rust_lines;
512+
513+
println!(
514+
"║ {} ║ {:>10.1} MB ║ {:>13} ║ {:>10.1} MB/s ║ {:>14.0}/s ║",
515+
result.drive_letter,
516+
mft_mb,
517+
format_duration(duration),
518+
throughput_mb,
519+
files_per_sec
520+
);
521+
}
522+
}
523+
524+
// Print totals
525+
if results.len() > 1 {
526+
println!("╠═══════════╬══════════════╬═══════════════╬═══════════════╬══════════════════╣");
527+
let total_mb = total_bytes as f64 / (1024.0 * 1024.0);
528+
let total_secs = total_duration.as_secs_f64();
529+
let avg_throughput = if total_secs > 0.0 { total_mb / total_secs } else { 0.0 };
530+
let avg_files_per_sec = if total_secs > 0.0 { total_files as f64 / total_secs } else { 0.0 };
531+
532+
println!(
533+
"║ TOTAL ║ {:>10.1} MB ║ {:>13} ║ {:>10.1} MB/s ║ {:>14.0}/s ║",
534+
total_mb,
535+
format_duration(total_duration),
536+
avg_throughput,
537+
avg_files_per_sec
538+
);
539+
}
540+
541+
println!("╚═══════════╩══════════════╩═══════════════╩═══════════════╩══════════════════╝");
542+
}
543+
544+
/// Format a Duration as a human-readable string
545+
fn format_duration(duration: Duration) -> String {
546+
let total_secs = duration.as_secs_f64();
547+
if total_secs < 1.0 {
548+
format!("{:.0}ms", duration.as_millis())
549+
} else if total_secs < 60.0 {
550+
format!("{:.2}s", total_secs)
551+
} else {
552+
let mins = (total_secs / 60.0).floor() as u64;
553+
let secs = total_secs % 60.0;
554+
format!("{}m {:.1}s", mins, secs)
555+
}
556+
}
557+
446558
/// Resolves the drive data directory.
447559
///
448560
/// Supports two directory structures:
@@ -805,7 +917,7 @@ fn regenerate_rust_output(
805917
drive_lower: &str,
806918
tz_offset: i32,
807919
custom_bin: Option<&Path>,
808-
) -> PathBuf {
920+
) -> RegenerateResult {
809921
let tz_str = format!("{}", tz_offset);
810922
let tz_label = match tz_offset {
811923
-7 => "PDT (Pacific Daylight)",
@@ -826,7 +938,14 @@ fn regenerate_rust_output(
826938
eprintln!("ERROR: MFT file not found: {}", mft_file.display());
827939
std::process::exit(1);
828940
}
829-
println!("MFT file: {}", mft_file.display());
941+
942+
// Get MFT file size
943+
let mft_size_bytes = fs::metadata(&mft_file)
944+
.map(|m| m.len())
945+
.unwrap_or(0);
946+
let mft_mb = mft_size_bytes as f64 / (1024.0 * 1024.0);
947+
948+
println!("MFT file: {} ({:.1} MB)", mft_file.display(), mft_mb);
830949

831950
// Determine which binary to use
832951
let binary_path = if let Some(custom) = custom_bin {
@@ -863,6 +982,9 @@ fn regenerate_rust_output(
863982
let rust_output = data_dir.join(format!("verify_rust_{}.txt", drive_lower));
864983
println!("Running uffs scan (baseline-compatible algorithms)...");
865984

985+
// Time the uffs execution
986+
let start_time = Instant::now();
987+
866988
let status = Command::new(&binary_path)
867989
.args([
868990
"*",
@@ -877,9 +999,16 @@ fn regenerate_rust_output(
877999
])
8781000
.status();
8791001

1002+
let parse_duration = start_time.elapsed();
1003+
8801004
match status {
8811005
Ok(s) if s.success() => {
882-
println!(" uffs scan completed successfully.");
1006+
let throughput = mft_mb / parse_duration.as_secs_f64();
1007+
println!(
1008+
" ✅ uffs scan completed in {} ({:.1} MB/s)",
1009+
format_duration(parse_duration),
1010+
throughput
1011+
);
8831012
println!();
8841013
}
8851014
Ok(s) => {
@@ -892,7 +1021,11 @@ fn regenerate_rust_output(
8921021
}
8931022
}
8941023

895-
rust_output
1024+
RegenerateResult {
1025+
output_path: rust_output,
1026+
parse_duration,
1027+
mft_size_bytes,
1028+
}
8961029
}
8971030

8981031
/// Find the authoritative workspace release artifact via Cargo metadata.

0 commit comments

Comments
 (0)