use std::fs::File;
use std::io::Write;
use std::path::Path;
use clap::{App, Arg, ArgMatches};
use indicatif::{ProgressBar, ProgressStyle};
use log::{debug, trace};
use microvmi::api::params::DriverInitParams;
use microvmi::api::Introspectable;
use utilities::Clappable;
const PAGE_SIZE: usize = 4096;
fn parse_args() -> ArgMatches<'static> {
App::new(file!())
.version("0.1")
.author("Mathieu Tarral")
.about("Dumps VM physical memory")
.args(DriverInitParams::to_clap_args().as_ref())
.arg(
Arg::with_name("no-pause")
.long("no-pause")
.required(false)
.takes_value(false)
.help("Don't pause the VM while dumping the memory"),
)
.arg(
Arg::with_name("output")
.short("o")
.takes_value(true)
.help("Output path"),
)
.get_matches()
}
fn main() {
env_logger::init();
let matches = parse_args();
let init_params = DriverInitParams::from_matches(&matches);
let domain_name: String = init_params
.common
.clone()
.map_or(String::from("unknown_vm_name"), |v| v.vm_name);
let no_pause = matches.is_present("no-pause");
let dump_path = Path::new(
matches
.value_of("output")
.map_or(&*format!("{}.dump", domain_name), |s| s),
)
.to_path_buf();
let mut dump_file = File::create(&dump_path).expect("Fail to open dump file");
dump_path.canonicalize().unwrap();
let spinner = ProgressBar::new_spinner();
spinner.enable_steady_tick(200);
spinner.set_message("Initializing libmicrovmi...");
let mut drv: Box<dyn Introspectable> =
microvmi::init(None, Some(init_params)).expect("Failed to init libmicrovmi");
spinner.finish_and_clear();
if !no_pause {
println!("pausing the VM");
drv.pause().expect("Failed to pause VM");
}
let max_addr = drv.get_max_physical_addr().unwrap();
println!(
"Dumping physical memory to {} until {:#X}",
dump_path.file_name().unwrap().to_str().unwrap(),
max_addr
);
let bar = ProgressBar::new(max_addr);
bar.set_style(ProgressStyle::default_bar().template(
"{prefix} {wide_bar} {bytes_per_sec} • {bytes}/{total_bytes} • {percent}% • {elapsed}",
));
bar.set_draw_delta(max_addr / 1000);
for cur_addr in (0..max_addr).step_by(PAGE_SIZE) {
trace!(
"reading {:#X} bytes of memory at {:#X}",
PAGE_SIZE,
cur_addr
);
let mut buffer: [u8; PAGE_SIZE] = [0; PAGE_SIZE];
let mut _bytes_read = 0;
drv.read_physical(cur_addr, &mut buffer, &mut _bytes_read)
.unwrap_or_else(|_| debug!("failed to read memory at {:#X}", cur_addr));
dump_file
.write_all(&buffer)
.expect("failed to write to file");
bar.set_prefix(format!("{:#X}", cur_addr));
bar.inc(PAGE_SIZE as u64);
}
bar.finish();
println!(
"Finished dumping physical memory at {}",
dump_path.display()
);
if !no_pause {
println!("resuming the VM");
drv.resume().expect("Failed to resume VM");
}
}