On Mon, May 19, 2025 at 10:14 AM Manos Pitsidianakis
<manos.pitsidiana...@linaro.org> wrote:
> There's also the `tracing` framework
> https://docs.rs/tracing/latest/tracing/ that is quite nice for
> defining tracing events **and** spans for logs. I find it confusing
> that it still ties tracing events with log level filters though
> (info/warn/error/debug/trace).

I like the overall look of the tracing crate API a lot. One thing that
I don't like is that it's very string-heavy. In general it seems to
have more overhead than tracetool if you use `Interest::Sometimes`; it
uses Strings for event names and targets but not ids (the default
event name is even "event FILE:LINE" which is not really usable except
for development), making matches (especially "enabled()" checks) slow.
Secondarily, built-in support for fmt::Debug/fmt::Display lets you get
away with not thinking of *which* fields you want to record. But it's
powerful, easy to use and it also has very few dependencies, which is
nice.

> Perhaps this is also a good opportunity to restructure QEMU's logging
> in general. [...]
> If we align QEMU's general logging to use log levels, we can convert
> guest error logs to "Error" level logs of type: "guest", and unimp
> logs to "Error" logs of type "unimplemented". Or, "Warn" instead of
> "Error". Then, all trace_* items become straightforwardly Trace level
> log items.

Focusing for a second on guest and unimp items, do you think that
there would be macros like `guest_error!` and `unimp!` (similar to how
`tracing` has shortcuts `error!` or `warn!`)? If so, we could add them
even now, so that any future changes to the implementation would be
transparent.

> Then, all we have to do in the Rust side is implement a `tracing`
> subscriber that consumes these traces and pushes them into QEMU's C
> implementation.

As I said above, I am a bit worried about performance implications of
having Strings for event names and targets but yes, using the tracing
crate is definitely something to think about! As you say, it provides
an opportunity to add structure to QEMU's logging and maybe we can get
the best of both worlds from both `tracing` and tracetool. Do you
think util/log.c could become a wrapper around event!() and
tracing_subscriber::FmtSubscriber (with log levels based on LOG_*
constants, as you mentioned)? Then the `log` backend for tracetool
could generate Rust code that calls trace!() instead of C code that
calls qemu_log().

Or you could do the same thing to the error_vprintf/error_printf
implementations, and then Rust code could use error! and warn! where C
code uses error_report() and warn_report(). It's a common complaint
that error_report() and warn_report() get lost in logs for example,
and `tracing` could add some structure there.

It could also be a very good starting point for thinking about C->Rust
FFI (i.e. implementing in Rust functionality that is used by C code).

In the meanwhile, another thing that could be done is take inspiration
from the `tracing` API, for example adding an #[instrument] macro to
qemu_api_macros:

impl PL011Registers {
    #[instrument(target = "hw_char", name = "pl011_write",
        fields(offset = offset as u32, value))
    pub(self) fn write(...) { }
}

This way, even if tracetool and trace-events file remain part of the
architecture, the API is more familiar to Rust developers. It'd be a
nice addition to Tanish's work (he's my student that's working on Rust
tracepoints.

Paolo


Reply via email to