diff --git a/src/main.rs b/src/main.rs index cb6b7ba..b98a303 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ )] mod driver; +mod output; mod panic; mod valgrind; @@ -25,72 +26,9 @@ use colored::Colorize as _; use std::env; use std::process; -/// Nicely format the errors in the valgrind output, if there are any. -fn display_errors(errors: &[valgrind::xml::Error]) { - // format the output in a helpful manner - for error in errors { - if error.kind.is_leak() { - display_leak(error); - } else { - display_generic_error(error); - } - } - - let total: usize = errors.iter().map(|error| error.resources.bytes).sum(); - eprintln!( - "{:>12} Leaked {} total ({} other errors)", - "Summary".red().bold(), - bytesize::to_string(total as _, true), - errors.iter().filter(|e| !e.kind.is_leak()).count() - ); -} - -/// Nicely format a single memory leak error. -fn display_leak(error: &valgrind::xml::Error) { - eprintln!( - "{:>12} leaked {} in {} block{}", - "Error".red().bold(), - bytesize::to_string(error.resources.bytes as _, true), - error.resources.blocks, - if error.resources.blocks == 1 { "" } else { "s" } - ); - - let stack = &error.stack_trace[0]; // always available - display_stack_trace("stack trace (user code at the bottom)", stack); -} - -/// Nicely format a non-memory-leak error. -fn display_generic_error(error: &valgrind::xml::Error) { - eprintln!( - "{:>12} {}", - "Error".red().bold(), - error.main_info.as_ref().map_or("unknown", String::as_str) - ); - - let stack = &error.stack_trace[0]; // always available - display_stack_trace("main stack trace (user code at the bottom)", stack); - error - .stack_trace - .iter() - .skip(1) - .enumerate() - .map(|(index, stack)| (error.auxiliary_info.get(index), stack)) - .for_each(|(msg, stack)| { - display_stack_trace( - msg.map_or_else(|| "additional stack trace", String::as_str), - stack, - ); - }); -} - -/// Write out the full stack trace (indented to match other messages). -fn display_stack_trace(msg: &str, stack: &valgrind::xml::Stack) { - eprintln!("{:>12} {}", "Info".cyan().bold(), msg); - stack - .frames - .iter() - .for_each(|frame| eprintln!(" at {frame}")); -} +/// Part of the output message of `valgrind` if a possible stack overflow is +/// detected. +const STACK_OVERFLOW: &str = "main thread stack using the --main-stacksize= flag"; fn main() { panic::replace_hook(); @@ -134,23 +72,13 @@ fn main() { errors: Some(errors), .. }) => { - display_errors(&errors); + output::display_errors(&errors); 127 } Ok(_) => 0, - Err(e @ valgrind::Error::MalformedOutput(..)) => { - panic_with!(e); - } - Err(valgrind::Error::ValgrindFailure(output)) - if output.contains("main thread stack using the --main-stacksize= flag") => - { - let error = "Error".red().bold(); - let info = "Info".cyan().bold(); - eprintln!("{error:>12}: looks like the program overflowed its stack"); - eprintln!("{info:>12}: valgrind says:"); - output - .lines() - .for_each(|line| eprintln!(" {line}")); + Err(e @ valgrind::Error::MalformedOutput(..)) => std::panic::panic_any(e), // the panic handler catches this and reports it appropriately + Err(valgrind::Error::ValgrindFailure(output)) if output.contains(STACK_OVERFLOW) => { + output::display_stack_overflow(&output); 134 // default exit code for stack overflows } Err(e) => { diff --git a/src/output.rs b/src/output.rs new file mode 100644 index 0000000..f7288e6 --- /dev/null +++ b/src/output.rs @@ -0,0 +1,81 @@ +//! Write human-readable and colored output the the console. +use crate::valgrind; +use colored::Colorize as _; + +/// Nicely format the errors in the valgrind output, if there are any. +pub fn display_errors(errors: &[valgrind::xml::Error]) { + // format the output in a helpful manner + for error in errors { + if error.kind.is_leak() { + display_leak(error); + } else { + display_generic_error(error); + } + } + + let total: usize = errors.iter().map(|error| error.resources.bytes).sum(); + eprintln!( + "{:>12} Leaked {} total ({} other errors)", + "Summary".red().bold(), + bytesize::to_string(total as _, true), + errors.iter().filter(|e| !e.kind.is_leak()).count() + ); +} + +/// Nicely format a single memory leak error. +fn display_leak(error: &valgrind::xml::Error) { + eprintln!( + "{:>12} leaked {} in {} block{}", + "Error".red().bold(), + bytesize::to_string(error.resources.bytes as _, true), + error.resources.blocks, + if error.resources.blocks == 1 { "" } else { "s" } + ); + + let stack = &error.stack_trace[0]; // always available + display_stack_trace("stack trace (user code at the bottom)", stack); +} + +/// Nicely format a non-memory-leak error. +fn display_generic_error(error: &valgrind::xml::Error) { + eprintln!( + "{:>12} {}", + "Error".red().bold(), + error.main_info.as_ref().map_or("unknown", String::as_str) + ); + + let stack = &error.stack_trace[0]; // always available + display_stack_trace("main stack trace (user code at the bottom)", stack); + error + .stack_trace + .iter() + .skip(1) + .enumerate() + .map(|(index, stack)| (error.auxiliary_info.get(index), stack)) + .for_each(|(msg, stack)| { + display_stack_trace( + msg.map_or_else(|| "additional stack trace", String::as_str), + stack, + ); + }); +} + +/// Write out the full stack trace (indented to match other messages). +fn display_stack_trace(msg: &str, stack: &valgrind::xml::Stack) { + eprintln!("{:>12} {}", "Info".cyan().bold(), msg); + stack + .frames + .iter() + .for_each(|frame| eprintln!(" at {frame}")); +} + +/// Write out an error message for describing the stack overflow message. +pub fn display_stack_overflow(output: &str) { + let error = "Error".red().bold(); + let info = "Info".cyan().bold(); + eprintln!("{error:>12}: looks like the program overflowed its stack"); + eprintln!("{info:>12}: valgrind says:"); + output + .lines() + .for_each(|line| eprintln!(" {line}")); +} diff --git a/src/panic.rs b/src/panic.rs index 4a91313..f652c40 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -29,17 +29,6 @@ const PANIC_HEADER: &str = " how to reproduce it. "; -/// Panic with a custom panic output. -/// -/// This is helpful for printing debug information to the panic message. -#[macro_export] -#[allow(clippy::module_name_repetitions)] // necessary for exported macro -macro_rules! panic_with { - ($e:expr) => { - std::panic::panic_any($e) - }; -} - /// Replaces any previous hook with the custom hook of this application. /// /// This custom hook points the user to the project repository and asks them to