scanner: Add post-scan progress analysis
This commit is contained in:
parent
d08d2ec783
commit
f7a4593ab4
|
@ -0,0 +1,101 @@
|
|||
//! Post-scan analysis
|
||||
|
||||
use crate::cli_options::AnalyzeOpt;
|
||||
use crate::file_results::{FileResults, Step};
|
||||
use std::cmp::max;
|
||||
use std::fs::File;
|
||||
|
||||
/// Generate and print statistics related to a scan's results
|
||||
pub fn analyze(results: impl Iterator<Item = FileResults>) {
|
||||
let mut total = 0;
|
||||
let mut start = 0;
|
||||
let mut read = 0;
|
||||
let mut decompress = 0;
|
||||
let mut parse = 0;
|
||||
let mut execute = 0;
|
||||
let mut complete = 0;
|
||||
|
||||
for result in results {
|
||||
total += 1;
|
||||
|
||||
match result.progress {
|
||||
Step::Start => start += 1,
|
||||
Step::Read => read += 1,
|
||||
Step::Decompress => decompress += 1,
|
||||
Step::Parse => parse += 1,
|
||||
Step::Execute => execute += 1,
|
||||
Step::Complete => complete += 1,
|
||||
}
|
||||
}
|
||||
|
||||
println!("Scanned {} swf files.", total);
|
||||
|
||||
let digits = max(
|
||||
(start as f64).log10().ceil() as usize,
|
||||
max(
|
||||
(read as f64).log10().ceil() as usize,
|
||||
max(
|
||||
(decompress as f64).log10().ceil() as usize,
|
||||
max(
|
||||
(parse as f64).log10().ceil() as usize,
|
||||
max(
|
||||
(execute as f64).log10().ceil() as usize,
|
||||
(complete as f64).log10().ceil() as usize,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
) + 4;
|
||||
|
||||
println!();
|
||||
|
||||
if start > 0 {
|
||||
println!(
|
||||
"{:>digits$} movies panicked or crashed the scanner",
|
||||
start,
|
||||
digits = digits
|
||||
);
|
||||
}
|
||||
|
||||
println!(
|
||||
"{:>digits$} movies failed when reading",
|
||||
read,
|
||||
digits = digits
|
||||
);
|
||||
println!(
|
||||
"{:>digits$} movies failed to decompress",
|
||||
decompress,
|
||||
digits = digits
|
||||
);
|
||||
println!("{:>digits$} movies failed to parse", parse, digits = digits);
|
||||
println!(
|
||||
"{:>digits$} movies failed to execute",
|
||||
execute,
|
||||
digits = digits
|
||||
);
|
||||
println!(
|
||||
"{:>digits$} movies completed without errors",
|
||||
complete,
|
||||
digits = digits
|
||||
);
|
||||
}
|
||||
|
||||
pub fn analyze_main(opt: AnalyzeOpt) -> Result<(), std::io::Error> {
|
||||
let file = File::open(opt.input_path)?;
|
||||
let reader = csv::Reader::from_reader(file);
|
||||
|
||||
analyze(reader.into_deserialize::<FileResults>().map(|r| {
|
||||
match r {
|
||||
Ok(fr) => fr,
|
||||
Err(e) => {
|
||||
// Treat unparseable CSV rows as a scanner panic
|
||||
FileResults {
|
||||
error: Some(format!("{}", e)),
|
||||
..FileResults::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -15,6 +15,9 @@ pub enum Mode {
|
|||
/// Scan an entire directory for SWF files
|
||||
Scan(ScanOpt),
|
||||
|
||||
/// Analyze a previously executed scan and compile statistics on it
|
||||
Analyze(AnalyzeOpt),
|
||||
|
||||
/// Execute a single SWF file and generate a machine-readable report
|
||||
ExecuteReport(ExecuteReportOpt),
|
||||
}
|
||||
|
@ -34,6 +37,13 @@ pub struct ScanOpt {
|
|||
pub ignore: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
pub struct AnalyzeOpt {
|
||||
/// The CSV file to reanalyze
|
||||
#[clap(name = "input", parse(from_os_str))]
|
||||
pub input_path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
pub struct ExecuteReportOpt {
|
||||
/// The single SWF file to parse and run
|
||||
|
|
|
@ -8,13 +8,13 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|||
use std::fmt;
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum AvmType {
|
||||
Avm1,
|
||||
Avm2,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum Compression {
|
||||
None,
|
||||
Zlib,
|
||||
|
@ -32,7 +32,7 @@ impl From<swf::Compression> for Compression {
|
|||
}
|
||||
|
||||
/// A particular step in the scanner process.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum Step {
|
||||
/// Nothing has been done yet.
|
||||
///
|
||||
|
@ -57,7 +57,7 @@ pub enum Step {
|
|||
}
|
||||
|
||||
/// The result of a single scan.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct FileResults {
|
||||
/// The file name scanned (including path).
|
||||
pub name: String,
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use crate::analyze::analyze_main;
|
||||
use crate::cli_options::{Mode, Opt};
|
||||
use crate::execute::execute_report_main;
|
||||
use crate::scan::scan_main;
|
||||
use clap::Clap;
|
||||
|
||||
mod analyze;
|
||||
mod cli_options;
|
||||
mod execute;
|
||||
mod file_results;
|
||||
|
@ -15,6 +17,7 @@ fn main() -> Result<(), std::io::Error> {
|
|||
|
||||
match opt.mode {
|
||||
Mode::Scan(scan_opt) => scan_main(scan_opt),
|
||||
Mode::Analyze(analyze_opt) => analyze_main(analyze_opt),
|
||||
Mode::ExecuteReport(exeute_report_opt) => {
|
||||
if execute_report_main(exeute_report_opt).is_err() {
|
||||
// Do nothing.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Main/scanner process impls
|
||||
|
||||
use crate::analyze::analyze;
|
||||
use crate::cli_options::ScanOpt;
|
||||
use crate::file_results::FileResults;
|
||||
use crate::ser_bridge::SerBridge;
|
||||
|
@ -119,8 +120,6 @@ pub fn scan_main(opt: ScanOpt) -> Result<(), std::io::Error> {
|
|||
let binary_path = env::current_exe()?;
|
||||
let to_scan = find_files(&opt.input_path, &opt.ignore);
|
||||
let total = to_scan.len() as u64;
|
||||
let mut good = 0;
|
||||
let mut bad = 0;
|
||||
let progress = ProgressBar::new(total);
|
||||
let mut writer = csv::Writer::from_path(opt.output_path.clone())?;
|
||||
|
||||
|
@ -169,22 +168,18 @@ pub fn scan_main(opt: ScanOpt) -> Result<(), std::io::Error> {
|
|||
|
||||
result
|
||||
})
|
||||
.ser_bridge();
|
||||
.ser_bridge()
|
||||
.map(|result| {
|
||||
if let Err(e) = writer.serialize(result.clone()) {
|
||||
eprintln!("{}", e);
|
||||
};
|
||||
|
||||
for result in result_iter {
|
||||
if result.error.is_none() {
|
||||
good += 1;
|
||||
} else {
|
||||
bad += 1;
|
||||
}
|
||||
result
|
||||
});
|
||||
|
||||
writer.serialize(result)?;
|
||||
}
|
||||
progress.finish();
|
||||
|
||||
progress.finish_with_message(format!(
|
||||
"Scanned {} swf files. {} successfully parsed, {} encountered errors",
|
||||
total, good, bad
|
||||
));
|
||||
analyze(result_iter);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue