Use "println!" in libpanic_unwind

I want to use the macro “println!” to print some debug information in “libpanic_unwind”, but get error:

error: cannot find macro println! in this scope

–> src/libpanic_unwind/dwarf/eh.rs:57:2 | 57 | println!(“find_eh_action”); | ^^^^^^^

error: aborting due to previous error

What should I do to use this macro? or what else function should I use for such case?

Thanks, Baoshan

libpanic_unwind is no_std (because it’s linked into libstd, for a start), so you can’t access OS-level stdio facilities. Not sure if there’s a way to work around it

When in a no_std environment with access to the libc, you can use puts or printf.

The only issue is it requiring a null-terminated byte-string:

fn puts (s: &'_ [u8])
{
    if s.contains(&b'\0') { unsafe {
        ::libc::puts(s.as_ptr() as *const ::libc::c_char);
    }}
}

fn main ()
{
    puts(b"Hello, world!\0");
}

I think you want ends_with there, not contains. Also, you might want an assert! rather than an if.

1 Like

I agree that failing paths (e.g., no silent truncation or even silently ignoring a bad argument) are the most sensitive thing to do, in the general case.

  • By the way, ends_with is not flawless either, since it ignores inner null bytes. s.iter().position(|&x| x == b'\0') == Some(s.len() - 1) would be the way to go

Since the OP wanted a debug function, however, in this case a convenience "do the best with what I get" function seemed to me more appropriate than a panic! within a panic! leading to a harder to debug abort, even at the cost of potential message truncation and the worst-case which silently ignores the call altogether.

But I should have stated so in my post, so thanks for pointing that out.


Here goes another shot at handling bad case scenarios (always within a no_std environment):

/// if `$assertion` is `false`, there is an index out of bounds access at compile time.
macro_rules! static_assert {($assertion:expr) => ({
    static _ASSERTION: () = [(); 1][(!$assertion) as usize];
})}

fn puts (s: &'_ [u8])
{
    const BUF_CAPACITY: usize
        = 256
    ;
    const TRUNCATION_MESSAGE_LEN: usize
        = 6
    ;
    static TRUNCATION_MESSAGE: &'static [u8; TRUNCATION_MESSAGE_LEN]
        = b"<...>\0"
    ;
    static_assert!(
        BUF_CAPACITY >= TRUNCATION_MESSAGE_LEN
    );
    static_assert!(
        TRUNCATION_MESSAGE[TRUNCATION_MESSAGE_LEN - 1] == b'\0'
    );
    unsafe {
        if s.iter().position(|&x| x == b'\0') == Some(s.len() - 1) { 
            ::libc::puts(s.as_ptr() as *const ::libc::c_char);
        } else {
            let mut buffer = [0_u8; BUF_CAPACITY];
            buffer
                .iter_mut()
                .zip(s.iter().cloned().filter(|&x| x != b'\0'))
                .for_each(|(p, x)| *p = x);
            if !buffer.ends_with(b"\0") {
                buffer[BUF_CAPACITY - TRUNCATION_MESSAGE_LEN ..]
                    .copy_from_slice(TRUNCATION_MESSAGE)
                ;
            }
            ::libc::puts(buffer.as_ptr() as *const ::libc::c_char);
        }
    }
}

You could avoid needing to copy anything, or worrying about truncation of the message, by looping over the buffer calling putchar.

fn puts (s: &[u8])
{
    use ::libc::{putchar, c_int};
    unsafe {
        for c in s {
           putchar(*c as c_int);
        }
        putchar('\n' as c_int);
    }
}

Debugging messages ought to go to stderr so they don’t corrupt any primary output of the program, and a function like this ought to lock and unlock the FILE object itself so no other thread’s output can get mixed in, which would look like this …

fn eputs (s: &[u8])
{
    use ::libc::{c_int, stderr, fputc_unlocked,
                 flockfile, funlockfile, fflush};
    unsafe {
        flockfile(stderr);
        for c in s {
           fputc_unlocked(*c as c_int, stderr);
        }
        fputc_unlocked('\n' as c_int, stderr);
        fflush(stderr);
        funlockfile(stderr);
    }
}

… but unfortunately libc doesn’t expose flockfile, funlockfile, fputc_unlocked, or even stderr (!) An alternative would be to bypass the stdio layer:

fn eputs (s: &[u8])
{
    use ::libc::{write, c_void, STDERR_FILENO};
    if s.len() > 0 {
        unsafe {
            write(STDERR_FILENO, s.as_ptr() as *const c_void, s.len());
        }
    }
}

With this version, callers have to provide a trailing newline themselves. All of these functions ignore errors, and the last one assumes all of the message will be consumed by a single call to write, which is not guaranteed but usually okay for what stderr gets connected to.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.