[thin_check (rust)] Provide 3 different report types.
ProgressBar for interactive use. Simple for when not a tty and quiet for -q
This commit is contained in:
parent
7466cd7182
commit
9995751dde
@ -15,12 +15,13 @@ extern crate quickcheck;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate quickcheck_macros;
|
extern crate quickcheck_macros;
|
||||||
|
|
||||||
pub mod io_engine;
|
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
pub mod checksum;
|
pub mod checksum;
|
||||||
pub mod file_utils;
|
pub mod file_utils;
|
||||||
|
pub mod io_engine;
|
||||||
pub mod pack;
|
pub mod pack;
|
||||||
pub mod pdata;
|
pub mod pdata;
|
||||||
|
pub mod report;
|
||||||
pub mod shrink;
|
pub mod shrink;
|
||||||
pub mod thin;
|
pub mod thin;
|
||||||
pub mod version;
|
pub mod version;
|
||||||
|
198
src/report.rs
Normal file
198
src/report.rs
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum ReportOutcome {
|
||||||
|
Success,
|
||||||
|
NonFatal,
|
||||||
|
Fatal,
|
||||||
|
}
|
||||||
|
|
||||||
|
use ReportOutcome::*;
|
||||||
|
|
||||||
|
impl ReportOutcome {
|
||||||
|
pub fn combine(lhs: &ReportOutcome, rhs: &ReportOutcome) -> ReportOutcome {
|
||||||
|
match (lhs, rhs) {
|
||||||
|
(Success, rhs) => rhs.clone(),
|
||||||
|
(lhs, Success) => lhs.clone(),
|
||||||
|
(Fatal, _) => Fatal,
|
||||||
|
(_, Fatal) => Fatal,
|
||||||
|
(_, _) => NonFatal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Report {
|
||||||
|
outcome: Mutex<ReportOutcome>,
|
||||||
|
inner: Mutex<Box<dyn ReportInner + Send>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ReportInner {
|
||||||
|
fn set_title(&mut self, txt: &str) -> Result<()>;
|
||||||
|
fn progress(&mut self, percent: u8) -> Result<()>;
|
||||||
|
fn log(&mut self, txt: &str) -> Result<()>;
|
||||||
|
fn complete(&mut self) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Report {
|
||||||
|
fn new(inner: Box<dyn ReportInner + Send>) -> Report {
|
||||||
|
Report {
|
||||||
|
outcome: Mutex::new(Success),
|
||||||
|
inner: Mutex::new(inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_outcome(&self, rhs: ReportOutcome) {
|
||||||
|
let mut lhs = self.outcome.lock().unwrap();
|
||||||
|
*lhs = ReportOutcome::combine(&lhs, &rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_title(&self, txt: &str) -> Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.set_title(txt)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn progress(&self, percent: u8) -> Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.progress(percent)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn info(&self, txt: &str) -> Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.log(txt)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn non_fatal(&self, txt: &str) -> Result<()> {
|
||||||
|
self.update_outcome(NonFatal);
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.log(txt)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fatal(&self, txt: &str) -> Result<()> {
|
||||||
|
self.update_outcome(Fatal);
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.log(txt)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn complete(&mut self) -> Result<()> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
inner.complete()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
struct PBInner {
|
||||||
|
bar: ProgressBar,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReportInner for PBInner {
|
||||||
|
fn set_title(&mut self, txt: &str) -> Result<()> {
|
||||||
|
let mut fmt = "Checking thin metadata [{bar:40}] Remaining {eta}, ".to_string();
|
||||||
|
fmt.push_str(&txt);
|
||||||
|
self.bar.set_style(
|
||||||
|
ProgressStyle::default_bar()
|
||||||
|
.template(&fmt)
|
||||||
|
.progress_chars("=> "),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn progress(&mut self, percent: u8) -> Result<()> {
|
||||||
|
self.bar.set_position(percent as u64);
|
||||||
|
self.bar.tick();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&mut self, txt: &str) -> Result<()> {
|
||||||
|
self.bar.println(txt);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete(&mut self) -> Result<()> {
|
||||||
|
self.bar.finish();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mk_progress_bar_report() -> Report {
|
||||||
|
Report::new(Box::new(PBInner {
|
||||||
|
bar: ProgressBar::new(100),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
struct SimpleInner {
|
||||||
|
last_progress: std::time::SystemTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleInner {
|
||||||
|
fn new() -> SimpleInner {
|
||||||
|
SimpleInner {
|
||||||
|
last_progress: std::time::SystemTime::now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReportInner for SimpleInner {
|
||||||
|
fn set_title(&mut self, txt: &str) -> Result<()> {
|
||||||
|
println!("{}", txt);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn progress(&mut self, percent: u8) -> Result<()> {
|
||||||
|
let elapsed = self.last_progress.elapsed().unwrap();
|
||||||
|
if elapsed > std::time::Duration::from_secs(5) {
|
||||||
|
println!("Progress: {}%", percent);
|
||||||
|
self.last_progress = std::time::SystemTime::now();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&mut self, txt: &str) -> Result<()> {
|
||||||
|
eprintln!("{}", txt);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mk_simple_report() -> Report {
|
||||||
|
Report::new(Box::new(SimpleInner::new()))
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
||||||
|
|
||||||
|
struct QuietInner {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReportInner for QuietInner {
|
||||||
|
fn set_title(&mut self, _txt: &str) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn progress(&mut self, _percent: u8) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&mut self, _txt: &str) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mk_quiet_report() -> Report {
|
||||||
|
Report::new(Box::new(QuietInner {}))
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------
|
@ -1,11 +1,8 @@
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
|
||||||
use nom::{number::complete::*, IResult};
|
use nom::{number::complete::*, IResult};
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::{thread, time};
|
use std::thread;
|
||||||
use threadpool::ThreadPool;
|
use threadpool::ThreadPool;
|
||||||
|
|
||||||
use crate::checksum;
|
use crate::checksum;
|
||||||
@ -13,6 +10,7 @@ use crate::io_engine::{AsyncIoEngine, Block, IoEngine, SyncIoEngine};
|
|||||||
use crate::pdata::btree::{btree_to_map, btree_to_map_with_sm, BTreeWalker, Node, NodeVisitor};
|
use crate::pdata::btree::{btree_to_map, btree_to_map_with_sm, BTreeWalker, Node, NodeVisitor};
|
||||||
use crate::pdata::space_map::*;
|
use crate::pdata::space_map::*;
|
||||||
use crate::pdata::unpack::*;
|
use crate::pdata::unpack::*;
|
||||||
|
use crate::report::*;
|
||||||
use crate::thin::superblock::*;
|
use crate::thin::superblock::*;
|
||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
@ -156,143 +154,10 @@ impl<'a> NodeVisitor<u32> for OverflowChecker<'a> {
|
|||||||
|
|
||||||
//------------------------------------------
|
//------------------------------------------
|
||||||
|
|
||||||
struct ReportOptions {}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
enum ReportOutcome {
|
|
||||||
Success,
|
|
||||||
NonFatal,
|
|
||||||
Fatal,
|
|
||||||
}
|
|
||||||
|
|
||||||
use ReportOutcome::*;
|
|
||||||
|
|
||||||
impl ReportOutcome {
|
|
||||||
fn combine(lhs: &ReportOutcome, rhs: &ReportOutcome) -> ReportOutcome {
|
|
||||||
match (lhs, rhs) {
|
|
||||||
(Success, rhs) => rhs.clone(),
|
|
||||||
(lhs, Success) => lhs.clone(),
|
|
||||||
(Fatal, _) => Fatal,
|
|
||||||
(_, Fatal) => Fatal,
|
|
||||||
(_, _) => NonFatal,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ReportCmd {
|
|
||||||
Log(String),
|
|
||||||
Complete,
|
|
||||||
Title(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Report {
|
|
||||||
opts: ReportOptions,
|
|
||||||
outcome: ReportOutcome,
|
|
||||||
tx: Sender<ReportCmd>,
|
|
||||||
tid: thread::JoinHandle<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Report {
|
|
||||||
fn new(
|
|
||||||
opts: ReportOptions,
|
|
||||||
sm: Arc<Mutex<dyn SpaceMap + Send + Sync>>,
|
|
||||||
total_allocated: u64,
|
|
||||||
) -> Result<Report> {
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
let tid = thread::spawn(move || report_thread(sm, total_allocated, rx));
|
|
||||||
Ok(Report {
|
|
||||||
opts,
|
|
||||||
outcome: ReportOutcome::Success,
|
|
||||||
tx,
|
|
||||||
tid,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn info<I: Into<String>>(&mut self, txt: I) -> Result<()> {
|
|
||||||
self.tx.send(ReportCmd::Log(txt.into()))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_outcome(&mut self, rhs: ReportOutcome) {
|
|
||||||
self.outcome = ReportOutcome::combine(&self.outcome, &rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn non_fatal<I: Into<String>>(&mut self, txt: I) -> Result<()> {
|
|
||||||
self.add_outcome(NonFatal);
|
|
||||||
self.tx.send(ReportCmd::Log(txt.into()))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fatal<I: Into<String>>(&mut self, txt: I) -> Result<()> {
|
|
||||||
self.add_outcome(Fatal);
|
|
||||||
self.tx.send(ReportCmd::Log(txt.into()))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn complete(self) -> Result<()> {
|
|
||||||
self.tx.send(ReportCmd::Complete)?;
|
|
||||||
self.tid.join();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_title(&mut self, txt: &str) -> Result<()> {
|
|
||||||
self.tx.send(ReportCmd::Title(txt.to_string()))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn report_thread(
|
|
||||||
sm: Arc<Mutex<dyn SpaceMap + Send + Sync>>,
|
|
||||||
total_allocated: u64,
|
|
||||||
rx: Receiver<ReportCmd>,
|
|
||||||
) {
|
|
||||||
let interval = time::Duration::from_millis(250);
|
|
||||||
let bar = ProgressBar::new(total_allocated);
|
|
||||||
loop {
|
|
||||||
loop {
|
|
||||||
match rx.try_recv() {
|
|
||||||
Ok(ReportCmd::Log(txt)) => {
|
|
||||||
bar.println(txt);
|
|
||||||
}
|
|
||||||
Ok(ReportCmd::Complete) => {
|
|
||||||
bar.finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Ok(ReportCmd::Title(txt)) => {
|
|
||||||
let mut fmt = "Checking thin metadata [{bar:40}] Remaining {eta}, ".to_string();
|
|
||||||
fmt.push_str(&txt);
|
|
||||||
bar.set_style(
|
|
||||||
ProgressStyle::default_bar()
|
|
||||||
.template(&fmt)
|
|
||||||
.progress_chars("=> "),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(TryRecvError::Disconnected) => {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Err(TryRecvError::Empty) => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let sm = sm.lock().unwrap();
|
|
||||||
let nr_allocated = sm.get_nr_allocated().unwrap();
|
|
||||||
drop(sm);
|
|
||||||
|
|
||||||
bar.set_position(nr_allocated);
|
|
||||||
bar.tick();
|
|
||||||
|
|
||||||
thread::sleep(interval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------
|
|
||||||
|
|
||||||
fn check_space_map(
|
fn check_space_map(
|
||||||
kind: &str,
|
kind: &str,
|
||||||
engine: Arc<dyn IoEngine + Send + Sync>,
|
engine: Arc<dyn IoEngine + Send + Sync>,
|
||||||
bar: &mut Report,
|
report: &Arc<Report>,
|
||||||
entries: Vec<IndexEntry>,
|
entries: Vec<IndexEntry>,
|
||||||
metadata_sm: Option<Arc<Mutex<dyn SpaceMap + Send + Sync>>>,
|
metadata_sm: Option<Arc<Mutex<dyn SpaceMap + Send + Sync>>>,
|
||||||
sm: Arc<Mutex<dyn SpaceMap + Send + Sync>>,
|
sm: Arc<Mutex<dyn SpaceMap + Send + Sync>>,
|
||||||
@ -344,7 +209,7 @@ fn check_space_map(
|
|||||||
if actual == 1 && expected == 0 {
|
if actual == 1 && expected == 0 {
|
||||||
leaks += 1;
|
leaks += 1;
|
||||||
} else if actual != expected as u8 {
|
} else if actual != expected as u8 {
|
||||||
bar.fatal(format!("Bad reference count for {} block {}. Expected {}, but space map contains {}.",
|
report.fatal(&format!("Bad reference count for {} block {}. Expected {}, but space map contains {}.",
|
||||||
kind, blocknr, expected, actual))?;
|
kind, blocknr, expected, actual))?;
|
||||||
fail = true;
|
fail = true;
|
||||||
}
|
}
|
||||||
@ -352,7 +217,7 @@ fn check_space_map(
|
|||||||
BitmapEntry::Overflow => {
|
BitmapEntry::Overflow => {
|
||||||
let expected = sm.get(blocknr)?;
|
let expected = sm.get(blocknr)?;
|
||||||
if expected < 3 {
|
if expected < 3 {
|
||||||
bar.fatal(format!("Bad reference count for {} block {}. Expected {}, but space map says it's >= 3.",
|
report.fatal(&format!("Bad reference count for {} block {}. Expected {}, but space map says it's >= 3.",
|
||||||
kind, blocknr, expected))?;
|
kind, blocknr, expected))?;
|
||||||
fail = true;
|
fail = true;
|
||||||
}
|
}
|
||||||
@ -363,7 +228,7 @@ fn check_space_map(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if leaks > 0 {
|
if leaks > 0 {
|
||||||
bar.non_fatal(format!(
|
report.non_fatal(&format!(
|
||||||
"{} {} blocks have leaked. Use --auto-repair to fix.",
|
"{} {} blocks have leaked. Use --auto-repair to fix.",
|
||||||
leaks, kind
|
leaks, kind
|
||||||
))?;
|
))?;
|
||||||
@ -421,8 +286,39 @@ pub fn check(opts: &ThinCheckOptions) -> Result<()> {
|
|||||||
let devs = btree_to_map::<DeviceDetail>(engine.clone(), false, sb.details_root)?;
|
let devs = btree_to_map::<DeviceDetail>(engine.clone(), false, sb.details_root)?;
|
||||||
let nr_devs = devs.len();
|
let nr_devs = devs.len();
|
||||||
let metadata_sm = core_sm(engine.get_nr_blocks(), nr_devs as u32);
|
let metadata_sm = core_sm(engine.get_nr_blocks(), nr_devs as u32);
|
||||||
let opts = ReportOptions {};
|
//let report = Arc::new(mk_progress_bar_report());
|
||||||
let mut report = Report::new(opts, metadata_sm.clone(), nr_allocated_metadata)?;
|
//let report = Arc::new(mk_simple_report());
|
||||||
|
let report = Arc::new(mk_quiet_report());
|
||||||
|
|
||||||
|
let tid;
|
||||||
|
let stop_progress = Arc::new(Mutex::new(false));
|
||||||
|
|
||||||
|
{
|
||||||
|
let report = report.clone();
|
||||||
|
let sm = metadata_sm.clone();
|
||||||
|
let stop_progress = stop_progress.clone();
|
||||||
|
tid = thread::spawn(move || {
|
||||||
|
let interval = std::time::Duration::from_millis(250);
|
||||||
|
loop {
|
||||||
|
{
|
||||||
|
let stop_progress = stop_progress.lock().unwrap();
|
||||||
|
if *stop_progress {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sm = sm.lock().unwrap();
|
||||||
|
|
||||||
|
let mut n = sm.get_nr_allocated().unwrap();
|
||||||
|
drop(sm);
|
||||||
|
n *= 100;
|
||||||
|
n /= nr_allocated_metadata;
|
||||||
|
|
||||||
|
let _r = report.progress(n as u8);
|
||||||
|
thread::sleep(interval);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
report.set_title("device details tree")?;
|
report.set_title("device details tree")?;
|
||||||
let _devs = btree_to_map_with_sm::<DeviceDetail>(
|
let _devs = btree_to_map_with_sm::<DeviceDetail>(
|
||||||
@ -439,7 +335,8 @@ pub fn check(opts: &ThinCheckOptions) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// mapping top level
|
// mapping top level
|
||||||
let roots = btree_to_map_with_sm::<u64>(engine.clone(), metadata_sm.clone(), false, sb.mapping_root)?;
|
let roots =
|
||||||
|
btree_to_map_with_sm::<u64>(engine.clone(), metadata_sm.clone(), false, sb.mapping_root)?;
|
||||||
|
|
||||||
// Check the mappings filling in the data_sm as we go.
|
// Check the mappings filling in the data_sm as we go.
|
||||||
report.set_title("mapping tree")?;
|
report.set_title("mapping tree")?;
|
||||||
@ -489,7 +386,7 @@ pub fn check(opts: &ThinCheckOptions) -> Result<()> {
|
|||||||
check_space_map(
|
check_space_map(
|
||||||
"data",
|
"data",
|
||||||
engine.clone(),
|
engine.clone(),
|
||||||
&mut report,
|
&report,
|
||||||
entries,
|
entries,
|
||||||
Some(metadata_sm.clone()),
|
Some(metadata_sm.clone()),
|
||||||
data_sm.clone(),
|
data_sm.clone(),
|
||||||
@ -524,14 +421,20 @@ pub fn check(opts: &ThinCheckOptions) -> Result<()> {
|
|||||||
check_space_map(
|
check_space_map(
|
||||||
"metadata",
|
"metadata",
|
||||||
engine.clone(),
|
engine.clone(),
|
||||||
&mut report,
|
&report,
|
||||||
entries,
|
entries,
|
||||||
None,
|
None,
|
||||||
metadata_sm.clone(),
|
metadata_sm.clone(),
|
||||||
root,
|
root,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
report.complete()?;
|
// Completing consumes the report.
|
||||||
|
{
|
||||||
|
let mut stop_progress = stop_progress.lock().unwrap();
|
||||||
|
*stop_progress = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
tid.join();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user