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 = 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}", )); // redraw every 0.1% change, otherwise it becomes the bottleneck 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 ); // reset buffer each loop 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"); // update bar 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"); } }