tui-rs icon indicating copy to clipboard operation
tui-rs copied to clipboard

`panic!` does not show

Open piegamesde opened this issue 5 years ago • 6 comments

I tried this both with the Termion and the RustBox backend: when my application panics, it will simply quit without printing any messages to the output. This is presumably because the terminal is in raw mode and exits it after the message was printed, thus clearing the screen.

Is there any way to write stuff to console (especially errors), so that I know why my application crashed?

piegamesde avatar Aug 05 '19 11:08 piegamesde

I managed to get it somewhat working with Termion with a custom panic handler. Since I don't know how to explicitly disable raw mode, I simply switch back to the main Screen and add \r to each println! command. Here's my current handler, with some copy-paste from Rust's default handler:

fn panic_hook(info: &PanicInfo<'_>) {
	let location = info.location().unwrap();  // The current implementation always returns Some

	let msg = match info.payload().downcast_ref::<&'static str>() {
		Some(s) => *s,
		None => match info.payload().downcast_ref::<String>() {
			Some(s) => &s[..],
			None => "Box<Any>",
		}
	};
	println!("{}thread '<unnamed>' panicked at '{}', {}\r", termion::screen::ToMainScreen, msg, location);
}

It works, but has quite a few drawbacks:

  • I couldn't get the Thread's name since the default implementation uses a private API
  • Similar for the back trace

piegamesde avatar Aug 05 '19 13:08 piegamesde

I had a similar issue with crossterm and I also used a panic_hook along with better_panic:

fn setup_panic_hook() {
	panic::set_hook(Box::new(|panic_info| {
		// Exits raw mode.
		cleanup_terminal();
		better_panic::Settings::auto().create_panic_handler()(panic_info);
	}));
}

https://github.com/cjbassi/ytop/blob/master/src/main.rs#L114

cjbassi avatar Mar 11 '20 20:03 cjbassi

With termion backend, the easy workaround is to set a raw handle on startup and to pass it to the hook. Here's a simple case:

use termion::raw::IntoRawMode;
use termion::screen::AlternateScreen;
use tui::backend::TermionBackend;

fn main() {
    setup_panic();

    let mut tui = tui::Terminal::new(TermionBackend::new(AlternateScreen::from(
        std::io::stdout().into_raw_mode().unwrap(),
    )))
    .unwrap();
    tui.clear().unwrap();

    panic!("noes");
}

fn setup_panic() {
    let raw_handle = std::io::stdout().into_raw_mode().unwrap();
    let default_hook = std::panic::take_hook();
    std::panic::set_hook(Box::new(move |info| {
        raw_handle
            .suspend_raw_mode()
            .unwrap_or_else(|e| log::error!("Could not suspend raw mode: {}", e));
        default_hook(info);
        // or better_panic::Settings::new().create_panic_handler()(info);
    }));
}

simpoir avatar Oct 08 '20 21:10 simpoir

I had a similar issue with crossterm and I also used a panic_hook along with better_panic:

fn setup_panic_hook() {
	panic::set_hook(Box::new(|panic_info| {
		// Exits raw mode.
		cleanup_terminal();
		better_panic::Settings::auto().create_panic_handler()(panic_info);
	}));
}

https://github.com/cjbassi/ytop/blob/master/src/main.rs#L114

Thanks for the example, this convinced me to move from termion to crossterm. Crossterm's handling of terminal state (raw mode etc.) seems much more sensible!

ThomWright avatar Oct 31 '20 12:10 ThomWright

How do you accomplish this when using a AlternateScreen?

dtomvan avatar Mar 23 '21 12:03 dtomvan

Nevermind:

std::panic::set_hook(Box::new(move |x| {
        stdout()
            .into_raw_mode()
            .unwrap()
            .suspend_raw_mode()
            .unwrap();
        write!(stdout().into_raw_mode().unwrap(), "{}", ToMainScreen).unwrap();
        write!(stdout(), "{:?}", x).unwrap();
}));

dtomvan avatar Apr 01 '21 11:04 dtomvan