Exit with a more severe error code if the program traps. (#1274)
* Exit with a more severe error code if the program traps. Previously, the wasmtime CLI would return with a regular failure error code, such as 1 on Unix. However, a program trap indicates a bug in the program, which can be useful to distinguish from a simple error status. Check for the trap case, and return an appropriate OS-specific exit status. * Use a loop to iterate over the error causes to find Traps. * Use anyhow's `chain()` iterator. * For completeness, handle non-Unix and non-Windows platforms too. * Add a CLI test for a trapping program. * Replace a manual `.cause` loop with a `.is` call. * Correct the expected exit status on Windows. * Use assert_eq/assert_ne so that if these fail, it prints the output.
This commit is contained in:
@@ -6,10 +6,11 @@ use std::{
|
||||
ffi::{OsStr, OsString},
|
||||
fs::File,
|
||||
path::{Component, Path, PathBuf},
|
||||
process,
|
||||
};
|
||||
use structopt::{clap::AppSettings, StructOpt};
|
||||
use wasi_common::preopen_dir;
|
||||
use wasmtime::{Engine, Instance, Module, Store};
|
||||
use wasmtime::{Engine, Instance, Module, Store, Trap};
|
||||
use wasmtime_interface_types::ModuleData;
|
||||
use wasmtime_wasi::{old::snapshot_0::Wasi as WasiSnapshot0, Wasi};
|
||||
|
||||
@@ -113,8 +114,31 @@ impl RunCommand {
|
||||
}
|
||||
|
||||
// Load the main wasm module.
|
||||
self.handle_module(&store, &module_registry)
|
||||
.with_context(|| format!("failed to run main module `{}`", self.module.display()))?;
|
||||
match self
|
||||
.handle_module(&store, &module_registry)
|
||||
.with_context(|| format!("failed to run main module `{}`", self.module.display()))
|
||||
{
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
// If the program exited because of a trap, return an error code
|
||||
// to the outside environment indicating a more severe problem
|
||||
// than a simple failure.
|
||||
if e.is::<Trap>() {
|
||||
// Print the error message in the usual way.
|
||||
eprintln!("Error: {:?}", e);
|
||||
|
||||
if cfg!(unix) {
|
||||
// On Unix, return the error code of an abort.
|
||||
process::exit(128 + libc::SIGABRT);
|
||||
} else if cfg!(windows) {
|
||||
// On Windows, return 3.
|
||||
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/abort?view=vs-2019
|
||||
process::exit(3);
|
||||
}
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
use anyhow::{bail, Result};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::process::{Command, Output};
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
fn run_wasmtime(args: &[&str]) -> Result<String> {
|
||||
// Run the wasmtime CLI with the provided args and return the `Output`.
|
||||
fn run_wasmtime_for_output(args: &[&str]) -> Result<Output> {
|
||||
let mut me = std::env::current_exe()?;
|
||||
me.pop(); // chop off the file name
|
||||
me.pop(); // chop off `deps`
|
||||
me.push("wasmtime");
|
||||
let output = Command::new(&me).args(args).output()?;
|
||||
Command::new(&me).args(args).output().map_err(Into::into)
|
||||
}
|
||||
|
||||
// Run the wasmtime CLI with the provided args and, if it succeeds, return
|
||||
// the standard output in a `String`.
|
||||
fn run_wasmtime(args: &[&str]) -> Result<String> {
|
||||
let output = run_wasmtime_for_output(args)?;
|
||||
if !output.status.success() {
|
||||
bail!(
|
||||
"Failed to execute wasmtime with: {:?}\n{}",
|
||||
@@ -74,3 +81,27 @@ fn run_wasmtime_simple_wat() -> Result<()> {
|
||||
])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Running a wat that traps.
|
||||
#[test]
|
||||
fn run_wasmtime_unreachable_wat() -> Result<()> {
|
||||
let wasm = build_wasm("tests/wasm/unreachable.wat")?;
|
||||
let output = run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "--disable-cache"])?;
|
||||
|
||||
assert_ne!(output.stderr, b"");
|
||||
assert_eq!(output.stdout, b"");
|
||||
assert!(!output.status.success());
|
||||
|
||||
let code = output
|
||||
.status
|
||||
.code()
|
||||
.expect("wasmtime process should exit normally");
|
||||
|
||||
// Test for the specific error code Wasmtime uses to indicate a trap return.
|
||||
#[cfg(unix)]
|
||||
assert_eq!(code, 128 + libc::SIGABRT);
|
||||
#[cfg(windows)]
|
||||
assert_eq!(code, 3);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
5
tests/wasm/unreachable.wat
Normal file
5
tests/wasm/unreachable.wat
Normal file
@@ -0,0 +1,5 @@
|
||||
(module
|
||||
(func (export "_start")
|
||||
unreachable
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user