rust-assert-no-alloc icon indicating copy to clipboard operation
rust-assert-no-alloc copied to clipboard

Log panic to file

Open DGriffin91 opened this issue 4 years ago • 5 comments

Thanks for all your work on this awesome crate!

I'm trying to use this in the context of a VST2 plugin (plugin for audio workstation app). In a VST2 plugin there isn't usually a way to see the output of stderr/stdout. I've been using simplelog and log_panics to log to a file.

I made a simple test:

use assert_no_alloc::*;

#[cfg(debug_assertions)] // required when disable_release is set (default)
#[global_allocator]
static A: AllocDisabler = AllocDisabler;

fn setup_logging() {
    let log_folder = ::dirs::home_dir().unwrap().join("tmp");

    let _ = ::std::fs::create_dir(log_folder.clone());

    let log_file = ::std::fs::File::create(log_folder.join("alloc_log_test.log")).unwrap();

    let log_config = ::simplelog::ConfigBuilder::new()
        .set_time_to_local(true)
        .build();

    let _ = ::simplelog::WriteLogger::init(simplelog::LevelFilter::max(), log_config, log_file);

    ::log_panics::init();

    ::log::info!("init");
}

fn main() {
    setup_logging();
    //panic!("Panic!");
    assert_no_alloc(|| {
        println!("{}", vec![1.0][0]);
    });
}

When running this test, I see the "memory allocation of 8 bytes failed" error in the terminal, but not in the log file. But if I uncomment panic!("Panic!"); I do see that panic message in the log file.

DGriffin91 avatar Apr 25 '21 20:04 DGriffin91

Hi, thanks for the nice feedback :)

The issue is likely that the panic handler itself tries to allocate memory (which is still blocked and thus will panic again).

You could try to unblock allocations forever after the first panic! was issues by incrementing ALLOC_PERMIT_COUNT in lib.rs#L158.

The downside of this is, that if a bad-alloc-panic is caught, no subsequent allocation checking will be performed any more. OTOH, the downside of the current behaviour is that panic handlers can not allocate.

Please let me know if the change suggested above solves your issue. One might want to keep this as a config option then.

Windfisch avatar May 03 '21 23:05 Windfisch

Thanks for taking a look at this!

It seems like this didn't resolve the issue. I'm not sure if I implemented it correctly though.

I first tried incrementing by 1 and it still did not show the panic in the log file. I don't know if maybe it was allocating more than once and that would require ALLOC_PERMIT_COUNT being even larger, so I tried adding 100 just as a test and the results were the same. Still no panic message in the log file.

You can see my change here: https://github.com/DGriffin91/rust-assert-no-alloc/blob/a80e16f1be8e64be1c9cd85c56f7fbf66786e26f/src/lib.rs#L166

DGriffin91 avatar May 04 '21 00:05 DGriffin91

ahh, seems like this could be the key: https://doc.rust-lang.org/stable/std/alloc/fn.handle_alloc_error.html

"The default behavior of this function is to print a message to standard error and abort the process. It can be replaced with set_alloc_error_hook and take_alloc_error_hook."

This seems to be what you see: The message is not piped through a panic handler but directly printed to stderr. It seems that you need to use set_alloc_error_hook to register a hook that will actually panic!().

Together with the ALLOC_PERMIT_COUNT change mentioned above, this should do.

I encourage you to experiment with the permit_alloc function instead of using the ALLOC_PERMIT_COUNT hack above. It might work to register this as hook:

fn hook() {
    permit_alloc(|| {
        panic!("yadda yadda");
    });
}

It might not work however, since I am not sure how panics are handled exactly from within rust; stack unwinding may cause the permit guard to expire, decrementing the ALLOC_PERMIT_COUNT again.

Please do try this and tell me how it went, I'm interested! :)

Windfisch avatar May 04 '21 23:05 Windfisch

Thanks for this really useful crate. I also find this issue with panics rather annoying. I am able to work around it by running my application in gdb and inspecting the arguments for the stack frame with the error (info args), but it sure would be convenient if I could use the normal RUST_BACKTRACE=1 cargo run without needing to wrap each (potential) panic in permit_alloc manually.

I'm also interested in figuring out a way to use logging with std::format macros in a way that doesn't allocate within the assert_no_alloc block, but I don't care that it goes to a file (I'm using pretty_env_logger).

Be-ing avatar Jan 27 '22 20:01 Be-ing

It turns out there's a pretty easy way to work around this: #8

Be-ing avatar Feb 10 '22 23:02 Be-ing