Add a way to print inputs on failure

When there is a panic in an extensive test, tracing down where it came
from can be difficult since no information is provides (messeges are
e.g. "attempted to subtract with overflow"). Resolve this by calling the
functions within `panic::catch_unwind`, printing the input, and
continuing.
This commit is contained in:
Trevor Gross 2025-02-12 03:48:20 +00:00 committed by Trevor Gross
parent aa26cab257
commit e106d63516
7 changed files with 27 additions and 8 deletions

View file

@ -14,6 +14,7 @@
//! level. `Op` is also used as the name for generic parameters since it is terse.
use std::fmt;
use std::panic::{RefUnwindSafe, UnwindSafe};
pub use shared::{ALL_OPERATIONS, FloatTy, MathOpInfo, Ty};
@ -64,7 +65,7 @@ pub trait MathOp {
type CRet;
/// The signature of the Rust function as a `fn(...) -> ...` type.
type RustFn: Copy;
type RustFn: Copy + UnwindSafe;
/// Arguments passed to the Rust library function as a tuple.
///
@ -72,7 +73,8 @@ pub trait MathOp {
/// to the Rust function.
type RustArgs: Copy
+ TupleCall<Self::RustFn, Output = Self::RustRet>
+ TupleCall<Self::CFn, Output = Self::RustRet>;
+ TupleCall<Self::CFn, Output = Self::RustRet>
+ RefUnwindSafe;
/// Type returned from the Rust function.
type RustRet: CheckOutput<Self::RustArgs>;

View file

@ -6,7 +6,8 @@
//! - `CheckOutput`: implemented on anything that is an output type for validation against an
//! expected value.
use std::fmt;
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::{fmt, panic};
use anyhow::{Context, anyhow, bail, ensure};
use libm::support::Hexf;
@ -23,6 +24,22 @@ use crate::{
pub trait TupleCall<Func>: fmt::Debug {
type Output;
fn call(self, f: Func) -> Self::Output;
/// Intercept panics and print the input to stderr before continuing.
fn call_intercept_panics(self, f: Func) -> Self::Output
where
Self: RefUnwindSafe + Copy,
Func: UnwindSafe,
{
let res = panic::catch_unwind(|| self.call(f));
match res {
Ok(v) => v,
Err(e) => {
eprintln!("panic with the following input: {self:?}");
panic::resume_unwind(e)
}
}
}
}
/// A trait to implement on any output type so we can verify it in a generic way.

View file

@ -21,7 +21,7 @@ fn musl_runner<Op: MathOp>(
) {
for input in cases {
let musl_res = input.call(musl_fn);
let crate_res = input.call(Op::ROUTINE);
let crate_res = input.call_intercept_panics(Op::ROUTINE);
crate_res.validate(musl_res, input, ctx).unwrap();
}

View file

@ -12,7 +12,7 @@ fn mp_runner<Op: MathOp + MpOp>(ctx: &CheckCtx, cases: impl Iterator<Item = Op::
let mut mp_vals = Op::new_mp();
for input in cases {
let mp_res = Op::run(&mut mp_vals, input);
let crate_res = input.call(Op::ROUTINE);
let crate_res = input.call_intercept_panics(Op::ROUTINE);
crate_res.validate(mp_res, input, ctx).unwrap();
}

View file

@ -10,7 +10,7 @@ fn standalone_runner<Op: MathOp>(
cases: impl Iterator<Item = (Op::RustArgs, Op::RustRet)>,
) {
for (input, expected) in cases {
let crate_res = input.call(Op::ROUTINE);
let crate_res = input.call_intercept_panics(Op::ROUTINE);
crate_res.validate(expected, input, ctx).unwrap();
}
}

View file

@ -113,7 +113,7 @@ where
for input in input_vec {
// Test the input.
let mp_res = Op::run(mp_vals, input);
let crate_res = input.call(Op::ROUTINE);
let crate_res = input.call_intercept_panics(Op::ROUTINE);
crate_res.validate(mp_res, input, ctx)?;
let completed = completed.fetch_add(1, Ordering::Relaxed) + 1;

View file

@ -59,7 +59,7 @@ macro_rules! handle_call {
let libm_fn: <Op as MathOp>::RustFn = libm::$fn_name;
let output = match $basis {
"libm" => input.call(libm_fn),
"libm" => input.call_intercept_panics(libm_fn),
#[cfg(feature = "build-musl")]
"musl" => {
let musl_fn: <Op as MathOp>::CFn =