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 `CLI`, `VMM::try_from()` and `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: #99

Signed-off-by: Carlos Venegas <[email protected]>
  • Loading branch information
jcvenegas committed May 24, 2021
1 parent 6275fc0 commit d0e73b7
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 34 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" }
23 changes: 15 additions & 8 deletions src/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@
use std::result;

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

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

/// Command line parser.
pub struct CLI;

Expand All @@ -20,7 +29,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 +68,19 @@ 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
57 changes: 32 additions & 25 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,44 @@
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
#[cfg(target_arch = "x86_64")]
use std::convert::TryFrom;
#[cfg(target_arch = "x86_64")]
use std::env;

#[cfg(target_arch = "x86_64")]
use api::CLI;
#[cfg(target_arch = "x86_64")]
use vmm::VMM;
mod main {
use std::convert::TryFrom;
use std::env;

use api::CLI;
use vmm::VMM;

fn main() {
#[cfg(target_arch = "x86_64")]
{
match CLI::launch(
use anyhow::Context;

pub type Result<T> = anyhow::Result<T>;

pub fn run() -> Result<()> {
let vmm_config = 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);
}
}
)
.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")
}
}

#[cfg(target_arch = "aarch64")]
mod main {
pub type Result<T> = std::result::Result<T, String>;
pub fn run() -> Result<()> {
println!("Reference VMM under construction!");
Ok(())
}
#[cfg(target_arch = "aarch64")]
println!("Reference VMM under construction!")
}

fn main() -> main::Result<()> {
main::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
21 changes: 20 additions & 1 deletion 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.
#[error("Failed to create block device.")]
Block(block::Error),
/// 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("Error configuring boot parameters.")]
BootParam(boot::Error),
/// Error configuring the kernel command line.
#[error("Error configuring the kernel command line.")]
Cmdline(cmdline::Error),
/// Error setting up devices.
#[error("Error setting up devices.")]
Device(serial::Error),
/// Event management error.
#[error("Event management error.")]
EventManager(event_manager::Error),
/// I/O error.
#[error("I/O error.")]
IO(io::Error),
/// Failed to load kernel.
#[error("Failed to load kernel.")]
KernelLoad(loader::Error),
/// 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.
#[error("Address stored in the rip registry does not fit in guest memory.")]
RipOutOfGuestMemory,
/// Invalid KVM API version.
#[error("Invalid KVM API version.")]
KvmApiVersion(i32),
/// Unsupported KVM capability.
#[error("Unsupported KVM capability.")]
KvmCap(Cap),
/// Error issuing an ioctl to KVM.
#[error("Error issuing an ioctl to KVM.")]
KvmIoctl(kvm_ioctls::Error),
/// Memory error.
#[error("Memory error.")]
Memory(MemoryError),
/// Invalid number of vCPUs specified.
#[error("Invalid number of vCPUs specified.")]
VcpuNumber(u8),
/// VM errors.
#[error("VM errors.")]
Vm(vm::Error),
/// Exit event errors.
#[error("Exit event errors.")]
ExitEvent(io::Error),
}

Expand Down

0 comments on commit d0e73b7

Please sign in to comment.