Skip to content

Commit

Permalink
main: Handle errors gracefully in main()
Browse files Browse the repository at this point in the history
Handle calls to VMM::try_from() or vmm.run().

This commit adds anyhow and thiserror crates to
handle errors.

The `anyhow` crate is used in the main binary to add
extra context and give a pretty view of error trace.

The create `thiserror` was used to easily
derive `std::error::Error` for errors provided by libs.

The new crates pulls the following extra crates:
```diff
+name = "proc-macro2"
+name = "quote"
+name = "syn"
+name = "thiserror-impl"
+name = "unicode-xid"
```

New error format:
examples:
```sh
$ vmm-reference --kernel \
    path=/path/vmlinuz-5.10.25
Error: Failed to create VMM from configurations

Caused by:
    Error issuing an ioctl to KVM.

$ echo $?
1
```

```sh
$ vmm-reference --kernel path
Error: Failed to parse CLI options

Caused by:
    Failed to parse cli Invalid input for kernel: Missing required
    argument: path
```

```
$vmm-reference
Error: Failed to parse CLI options

Caused by:
    Failed to parse cli error: The following required arguments were not provided:
        --kernel <kernel>

    USAGE:
        vmm-reference [OPTIONS] --kernel <kernel>

    For more information try --help
```

Fixes: rust-vmm#99

Signed-off-by: Carlos Venegas <[email protected]>
  • Loading branch information
jcvenegas committed May 24, 2021
1 parent 6275fc0 commit c676eab
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 48 deletions.
64 changes: 64 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = "2018"
license = "Apache-2.0 OR BSD-3-Clause"

[dependencies]
anyhow = "1.0"
vmm = { path = "src/vmm" }
api = { path = "src/api" }

Expand Down
1 change: 1 addition & 0 deletions src/api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ edition = "2018"

[dependencies]
clap = "2.33.3"
thiserror = "1.0"

vmm = { path = "../vmm" }
22 changes: 14 additions & 8 deletions src/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ use std::result;

use clap::{App, Arg};
use vmm::VMMConfig;
use thiserror::Error;

#[derive(Error, Debug)]
/// Docs
pub enum CliError {
#[error("Failed to parse cli {0}",)]
/// Docs
Parse(String),

}

/// Command line parser.
pub struct CLI;
Expand All @@ -20,7 +30,7 @@ impl CLI {
/// # Arguments
///
/// * `cmdline_args` - command line arguments passed to the application.
pub fn launch(cmdline_args: Vec<&str>) -> result::Result<VMMConfig, String> {
pub fn launch(cmdline_args: Vec<&str>) -> result::Result<VMMConfig, CliError> {
let mut app = App::new(cmdline_args[0].to_string())
.arg(
Arg::with_name("memory")
Expand Down Expand Up @@ -59,21 +69,17 @@ impl CLI {
let mut help_msg_buf: Vec<u8> = vec![];
// If the write fails, we'll just have an empty help message.
let _ = app.write_long_help(&mut help_msg_buf);
let help_msg = String::from_utf8_lossy(&help_msg_buf);

let matches = app.get_matches_from_safe(cmdline_args).map_err(|e| {
eprintln!("{}", help_msg);
format!("Invalid command line arguments: {}", e)
})?;
let matches = app.get_matches_from_safe(cmdline_args).map_err(|e|CliError::Parse(format!("{}",e)))?;

Ok(VMMConfig::builder()
VMMConfig::builder()
.memory_config(matches.value_of("memory"))
.kernel_config(matches.value_of("kernel"))
.vcpu_config(matches.value_of("vcpu"))
.net_config(matches.value_of("net"))
.block_config(matches.value_of("block"))
.build()
.map_err(|e| format!("{:?}", e))?)
.map_err(|e| CliError::Parse(format!("{}",e)))
}
}

Expand Down
45 changes: 23 additions & 22 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,29 @@ use api::CLI;
#[cfg(target_arch = "x86_64")]
use vmm::VMM;

fn main() {
#[cfg(target_arch = "x86_64")]
use anyhow::{Context, Result};

fn run() -> Result<()> {

let vmm_config = CLI::launch(
env::args()
.collect::<Vec<String>>()
.iter()
.map(|s| s.as_str())
.collect()
).context("Failed to parse CLI options")?;

let mut vmm = VMM::try_from(vmm_config).context("Failed to create VMM from configurations")?;

vmm.run().context("failed to run VMM")
}

fn main() -> Result<()> {
#[cfg(target_arch = "aarch64")]
{
match CLI::launch(
env::args()
.collect::<Vec<String>>()
.iter()
.map(|s| s.as_str())
.collect(),
) {
Ok(vmm_config) => {
let mut vmm =
VMM::try_from(vmm_config).expect("Failed to create VMM from configurations");
// For now we are just unwrapping here, in the future we might use a nicer way of
// handling errors such as pretty printing them.
vmm.run().unwrap();
}
Err(e) => {
eprintln!("Failed to parse command line options. {}", e);
}
}
println!("Reference VMM under construction!");
Ok(())
}
#[cfg(target_arch = "aarch64")]
println!("Reference VMM under construction!")
#[cfg(target_arch = "x86_64")]
run()
}
1 change: 1 addition & 0 deletions src/vmm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ authors = ["rust-vmm AWS maintainers <[email protected]>"]
edition = "2018"

[dependencies]
thiserror = "1.0"

event-manager = "0.2.1"
kvm-bindings = { version = "0.4.0", features = ["fam-wrappers"] }
Expand Down
55 changes: 37 additions & 18 deletions src/vmm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ use serial::SerialWrapper;
use vm_vcpu::vcpu::{cpuid::filter_cpuid, VcpuState};
use vm_vcpu::vm::{self, ExitHandler, KvmVm, VmState};

use thiserror::Error;

mod boot;
mod config;

Expand Down Expand Up @@ -81,41 +83,58 @@ pub enum MemoryError {
}

/// VMM errors.
#[derive(Debug)]
#[derive(Error, Debug)]
pub enum Error {
/// Failed to create block device.
///Failed to create block device.
#[error("Failed to create block device.")]
Block(block::Error),
/// Failed to write boot parameters to guest memory.
///Failed to write boot parameters to guest memory.
#[error("Failed to write boot parameters to guest memory.")]
BootConfigure(configurator::Error),
/// Error configuring boot parameters.
///Error configuring boot parameters.
#[error("Error configuring boot parameters.")]
BootParam(boot::Error),
/// Error configuring the kernel command line.
///Error configuring the kernel command line.
#[error("Error configuring the kernel command line.")]
Cmdline(cmdline::Error),
/// Error setting up devices.
///Error setting up devices.
#[error("Error setting up devices.")]
Device(serial::Error),
/// Event management error.
///Event management error.
#[error("Event management error.")]
EventManager(event_manager::Error),
/// I/O error.
///I/O error.
#[error("I/O error.")]
IO(io::Error),
/// Failed to load kernel.
///Failed to load kernel.
#[error("Failed to load kernel.")]
KernelLoad(loader::Error),
/// Failed to create net device.
///Failed to create net device.
#[error("Failed to create net device.")]
Net(net::Error),
/// Address stored in the rip registry does not fit in guest memory.
///Address stored in the rip registry does not fit in guest memory.
#[error("Address stored in the rip registry does not fit in guest memory.")]
RipOutOfGuestMemory,
/// Invalid KVM API version.
///Invalid KVM API version.
#[error("Invalid KVM API version.")]
KvmApiVersion(i32),
/// Unsupported KVM capability.
///Unsupported KVM capability.
#[error("Unsupported KVM capability.")]
KvmCap(Cap),
/// Error issuing an ioctl to KVM.
///Error issuing an ioctl to KVM.
#[error("Error issuing an ioctl to KVM.")]
KvmIoctl(kvm_ioctls::Error),
/// Memory error.
///Memory error.
#[error("Memory error.")]
Memory(MemoryError),
/// Invalid number of vCPUs specified.
///Invalid number of vCPUs specified.
#[error("Invalid number of vCPUs specified.")]
VcpuNumber(u8),
/// VM errors.
///VM errors.
#[error("VM errors.")]
Vm(vm::Error),
/// Exit event errors.
///Exit event errors.
#[error("Exit event errors.")]
ExitEvent(io::Error),
}

Expand Down

0 comments on commit c676eab

Please sign in to comment.