Esp-idf: logger redirection: vprintf variadic function

I want to redirect the log output of esp-idf from it's default serial output through my syslog logger.

For that, the esp-idf allows to pass a function of type
unsafe extern "C" fn(arg1: *const ::core::ffi::c_char, arg2: va_list) -> ::core::ffi::c_int
to esp_log_set_vprintf.

My problem with this is, that esp-idf defines the type va_list as:
pub type va_list = [u32; 3usize];

I have no idea how to intepret those 3 u32 variables, or how to make it compatible with any of the va_list rust implementations I've found so far. The nicest one of those seems to be: printf_compat - Rust

Looking at what those 3 u32 actually hold at runtime seems like the first two are pointers and the 3rd one is 16 for info log-lines and 12 for something less, probably debug log-lines.

Can anybody give me some pointers where to go from here?

You don't. va_list is an opaque type that you shouldn't care about or inspect in any way. You are only supposed to manipulate it through whatever the Rust equivalents of the va_start(), va_arg(), and va_end() macros are. (They are nightly-only in std, but there seems to be a 3rd-party crate.)


I found that after enabling #![feature(c_variadic)] that core:ffi contains:

pub struct VaListImpl<'f> {
    stk: *mut i32,
    reg: *mut i32,
    ndx: i32,
    _marker: PhantomData<&'f mut &'f i32>,

this looks like the 3 u32 I get from the esp-idf interface. Now the question is how do I put them in there so I can pass it as an VaList instance into the format method of printf_compat - Rust

I tried this, but of course it fails because although the struct is public, it's field's aren't:

        let va_list_conv = VaListImpl {
            stk: &mut (args[0] as i32),
            reg: &mut (args[1] as i32),
            ndx: args[2] as i32,
            _marker: std::marker::PhantomData,

I didn't dig deep enough to see if it actually addresses your OP, but the way you create those data structures is to define and call a variadic function.


yeah I get that. Sadly to make it work with the esp-idf log system I need to pass a function of the definition

unsafe extern "C" fn(arg1: *const ::core::ffi::c_char, arg2: va_list) -> ::core::ffi::c_int
// where
pub type va_list = [u32; 3usize];

into esp_idf_sys::esp_log_set_vprintf to intercept the esp-idf log lines that I'm doing this stuff for.

Now how do I convert / call from unsafe extern "C" fn(arg1: *const ::core::ffi::c_char, arg2: va_list) -> ::core::ffi::c_int into a function that takes a std:ffi::VaList instead?

Is there any solution for that problem?

Have a citation?

pub type va_list = __builtin_va_list;
pub type __builtin_va_list = *mut c_void;
1 Like

...that last one could be a platform difference. And I guess they're using a custom fork of the compiler for at least the Xtensa platforms? So you may have better luck on an ESP specific forum.

Actual VaList support may be jank.

1 Like

Yes, it's a custom rust compiler fork for the ESP32 Xtensa architecture, installed using espup

My IDE (VS-Code) lead me into this file:
Which contains:

pub type va_list = [u32; 3usize];
pub type vprintf_like_t = ::core::option::Option<
    unsafe extern "C" fn(arg1: *const ::core::ffi::c_char, arg2: va_list) -> ::core::ffi::c_int,
extern "C" {
    pub fn esp_log_set_vprintf(func: vprintf_like_t) -> vprintf_like_t;

That esp_log_set_vprintf has a documentation which is nicer to look at rendered in the docs page: esp_log_set_vprintf in esp_idf_sys - Rust

I would like to use the : printf_compat - Rust
To fill a String with the result of the format-pattern with it's arguments, but to make that happen I'd need a path from [u32; 3] to std:ffi:VaList.

Ohh, now I get it, they're not using the "official" core:ffi:VaList interface, because it's somehow broken for the Xtensa fork: Problems with C variadics · Issue #177 · esp-rs/rust · GitHub

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.