All of lore.kernel.org
 help / color / mirror / Atom feed
From: saman <saman@enumclass.cc>
To: qemu-devel@nongnu.org
Cc: Stefan Hajnoczi <stefanha@redhat.com>,
	qemu-rust@nongnu.org, Mads Ynddal <mads@ynddal.dk>,
	Manos Pitsidianakis <manos.pitsidianakis@linaro.org>,
	saman <saman@enumclass.cc>
Subject: [PATCH] Rust: Add tracing and logging support for Rust code
Date: Mon, 31 Mar 2025 19:26:33 -0500	[thread overview]
Message-ID: <20250401002633.738345-1-saman@enumclass.cc> (raw)

This change introduces initial support for tracing and logging in Rust-based
QEMU code. As an example, tracing and logging have been implemented in the
pl011 device, which is written in Rust.

- Updated `rust/wrapper.h` to include the `qemu/log.h` and `hw/char/trace.h` header.
- Added log.rs to wrap `qemu_log_mask` and `qemu_log_mask_and_addr`
- Modified `tracetool` scripts to move C function implementation from
  header to .c
- Added log and trace in rust version of PL011 device

Future enhancements could include generating idiomatic Rust APIs for tracing
using the tracetool scripts

Signed-off-by: saman <saman@enumclass.cc>
---
 include/qemu/log-for-trace.h        |  5 +--
 rust/hw/char/pl011/src/device.rs    | 34 +++++++++++++++---
 rust/hw/char/pl011/src/registers.rs | 20 +++++++++++
 rust/qemu-api/meson.build           |  1 +
 rust/qemu-api/src/lib.rs            |  1 +
 rust/qemu-api/src/log.rs            | 54 +++++++++++++++++++++++++++++
 rust/wrapper.h                      |  2 ++
 scripts/tracetool/format/c.py       | 16 +++++++++
 scripts/tracetool/format/h.py       | 11 ++----
 util/log.c                          |  5 +++
 10 files changed, 131 insertions(+), 18 deletions(-)
 create mode 100644 rust/qemu-api/src/log.rs

diff --git a/include/qemu/log-for-trace.h b/include/qemu/log-for-trace.h
index d47c9cd446..ad5cd0dd24 100644
--- a/include/qemu/log-for-trace.h
+++ b/include/qemu/log-for-trace.h
@@ -24,10 +24,7 @@ extern int qemu_loglevel;
 #define LOG_TRACE          (1 << 15)
 
 /* Returns true if a bit is set in the current loglevel mask */
-static inline bool qemu_loglevel_mask(int mask)
-{
-    return (qemu_loglevel & mask) != 0;
-}
+bool qemu_loglevel_mask(int mask);
 
 /* main logging function */
 void G_GNUC_PRINTF(1, 2) qemu_log(const char *fmt, ...);
diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs
index bf88e0b00a..42385a7bf6 100644
--- a/rust/hw/char/pl011/src/device.rs
+++ b/rust/hw/char/pl011/src/device.rs
@@ -2,15 +2,21 @@
 // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
 // SPDX-License-Identifier: GPL-2.0-or-later
 
-use std::{ffi::CStr, mem::size_of, ptr::addr_of_mut};
+use std::{
+    ffi::{CStr, CString},
+    mem::size_of,
+    ptr::addr_of_mut,
+};
 
 use qemu_api::{
     chardev::{CharBackend, Chardev, Event},
     impl_vmstate_forward,
     irq::{IRQState, InterruptSource},
+    log::Mask,
     memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
     prelude::*,
     qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
+    qemu_log_mask,
     qom::{ObjectImpl, Owned, ParentField},
     static_assert,
     sysbus::{SysBusDevice, SysBusDeviceImpl},
@@ -298,7 +304,7 @@ pub(self) fn write(
             DMACR => {
                 self.dmacr = value;
                 if value & 3 > 0 {
-                    // qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
+                    qemu_log_mask!(Mask::log_unimp, "pl011: DMA not implemented\n");
                     eprintln!("pl011: DMA not implemented");
                 }
             }
@@ -535,11 +541,21 @@ fn read(&self, offset: hwaddr, _size: u32) -> u64 {
                 u64::from(device_id[(offset - 0xfe0) >> 2])
             }
             Err(_) => {
-                // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset);
+                qemu_log_mask!(
+                    Mask::log_guest_error,
+                    "pl011_read: Bad offset 0x%x\n",
+                    offset as i32
+                );
                 0
             }
             Ok(field) => {
+                let regname = field.as_str();
                 let (update_irq, result) = self.regs.borrow_mut().read(field);
+                let c_string = CString::new(regname).expect("CString::new failed");
+                let name_ptr = c_string.as_ptr();
+                unsafe {
+                    qemu_api::bindings::trace_pl011_read(offset as u32, result, name_ptr);
+                }
                 if update_irq {
                     self.update();
                     self.char_backend.accept_input();
@@ -576,8 +592,16 @@ fn write(&self, offset: hwaddr, value: u64, _size: u32) {
 
     fn can_receive(&self) -> u32 {
         let regs = self.regs.borrow();
-        // trace_pl011_can_receive(s->lcr, s->read_count, r);
-        u32::from(regs.read_count < regs.fifo_depth())
+        let fifo_available = u32::from(regs.read_count < regs.fifo_depth());
+        unsafe {
+            qemu_api::bindings::trace_pl011_can_receive(
+                u32::from(regs.line_control),
+                regs.read_count,
+                regs.fifo_depth().try_into().unwrap(),
+                fifo_available,
+            );
+        }
+        fifo_available
     }
 
     fn receive(&self, buf: &[u8]) {
diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs
index cd92fa2c30..11c085030d 100644
--- a/rust/hw/char/pl011/src/registers.rs
+++ b/rust/hw/char/pl011/src/registers.rs
@@ -72,6 +72,26 @@ pub enum RegisterOffset {
     //Reserved = 0x04C,
 }
 
+impl RegisterOffset {
+    pub fn as_str(&self) -> &'static str {
+        match self {
+            RegisterOffset::DR => "DR",
+            RegisterOffset::RSR => "RSR",
+            RegisterOffset::FR => "FR",
+            RegisterOffset::FBRD => "FBRD",
+            RegisterOffset::ILPR => "ILPR",
+            RegisterOffset::IBRD => "IBRD",
+            RegisterOffset::LCR_H => "LCR_H",
+            RegisterOffset::CR => "CR",
+            RegisterOffset::FLS => "FLS",
+            RegisterOffset::IMSC => "IMSC",
+            RegisterOffset::RIS => "RIS",
+            RegisterOffset::MIS => "MIS",
+            RegisterOffset::ICR => "ICR",
+            RegisterOffset::DMACR => "DMACR",
+        }
+    }
+}
 /// Receive Status Register / Data Register common error bits
 ///
 /// The `UARTRSR` register is updated only when a read occurs
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index 858685ddd4..f8eddf7887 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -34,6 +34,7 @@ _qemu_api_rs = static_library(
       'src/qom.rs',
       'src/sysbus.rs',
       'src/timer.rs',
+      'src/log.rs',
       'src/vmstate.rs',
       'src/zeroable.rs',
     ],
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 05f38b51d3..b54989a243 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -21,6 +21,7 @@
 pub mod chardev;
 pub mod errno;
 pub mod irq;
+pub mod log;
 pub mod memory;
 pub mod module;
 pub mod offset_of;
diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs
new file mode 100644
index 0000000000..07e8bceb34
--- /dev/null
+++ b/rust/qemu-api/src/log.rs
@@ -0,0 +1,54 @@
+#[allow(non_camel_case_types)]
+#[repr(u32)]
+pub enum Mask {
+    cpu_log_tb_out_asm = crate::bindings::CPU_LOG_TB_OUT_ASM,
+    cpu_log_tb_in_asm = crate::bindings::CPU_LOG_TB_IN_ASM,
+    cpu_log_tb_op = crate::bindings::CPU_LOG_TB_OP,
+    cpu_log_tb_op_opt = crate::bindings::CPU_LOG_TB_OP_OPT,
+    cpu_log_int = crate::bindings::CPU_LOG_INT,
+    cpu_log_exec = crate::bindings::CPU_LOG_EXEC,
+    cpu_log_pcall = crate::bindings::CPU_LOG_PCALL,
+    cpu_log_tb_cpu = crate::bindings::CPU_LOG_TB_CPU,
+    cpu_log_reset = crate::bindings::CPU_LOG_RESET,
+    log_unimp = crate::bindings::LOG_UNIMP,
+    log_guest_error = crate::bindings::LOG_GUEST_ERROR,
+    cpu_log_mmu = crate::bindings::CPU_LOG_MMU,
+    cpu_log_tb_nochain = crate::bindings::CPU_LOG_TB_NOCHAIN,
+    cpu_log_page = crate::bindings::CPU_LOG_PAGE,
+    cpu_log_tb_op_ind = crate::bindings::CPU_LOG_TB_OP_IND,
+    cpu_log_tb_fpu = crate::bindings::CPU_LOG_TB_FPU,
+    cpu_log_plugin = crate::bindings::CPU_LOG_PLUGIN,
+    log_strace = crate::bindings::LOG_STRACE,
+    log_per_thread = crate::bindings::LOG_PER_THREAD,
+    cpu_log_tb_vpu = crate::bindings::CPU_LOG_TB_VPU,
+    log_tb_op_plugin = crate::bindings::LOG_TB_OP_PLUGIN,
+    log_invalid_mem = crate::bindings::LOG_INVALID_MEM,
+}
+
+#[macro_export]
+macro_rules! qemu_log_mask {
+    ($mask:expr, $fmt:expr $(, $arg:expr)*) => {{
+        let mask: Mask = $mask;
+        unsafe {
+            if $crate::bindings::qemu_loglevel_mask(mask as std::os::raw::c_int) {
+                let format_str = std::ffi::CString::new($fmt).expect("CString::new failed");
+                $crate::bindings::qemu_log(format_str.as_ptr() $(, $arg)*);
+            }
+        }
+    }};
+}
+
+#[macro_export]
+macro_rules! qemu_log_mask_and_addr {
+    ($mask:expr, $addr:expr, $fmt:expr $(, $arg:expr)*) => {{
+        let mask: Mask = $mask;
+        let addr: $crate::bindings::hwaddr = $addr;
+        unsafe {
+            if $crate::bindings::qemu_loglevel_mask(mask as std::os::raw::c_int) &&
+                $crate::bindings::qemu_log_in_addr_range(addr) {
+                let format_str = std::ffi::CString::new($fmt).expect("CString::new failed");
+                $crate::bindings::qemu_log(format_str.as_ptr() $(, $arg)*);
+            }
+        }
+    }};
+}
diff --git a/rust/wrapper.h b/rust/wrapper.h
index d4fec54657..cd2f311d71 100644
--- a/rust/wrapper.h
+++ b/rust/wrapper.h
@@ -64,5 +64,7 @@ typedef enum memory_order {
 #include "chardev/char-serial.h"
 #include "exec/memattrs.h"
 #include "qemu/timer.h"
+#include "qemu/log.h"
 #include "exec/address-spaces.h"
 #include "hw/char/pl011.h"
+#include "hw/char/trace.h"
diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py
index 69edf0d588..f2d383f89c 100644
--- a/scripts/tracetool/format/c.py
+++ b/scripts/tracetool/format/c.py
@@ -43,6 +43,22 @@ def generate(events, backend, group):
             sstate = "TRACE_%s_ENABLED" % e.name.upper(),
             dstate = e.api(e.QEMU_DSTATE))
 
+        cond = "true"
+
+        out('',
+            'void %(api)s(%(args)s)',
+            '{',
+            '    if (%(cond)s) {',
+            '        %(api_nocheck)s(%(names)s);',
+            '    }',
+            '}',
+            api=e.api(),
+            api_nocheck=e.api(e.QEMU_TRACE_NOCHECK),
+            args=e.args,
+            names=", ".join(e.args.names()),
+            cond=cond
+            )
+
     out('TraceEvent *%(group)s_trace_events[] = {',
         group = group.lower())
 
diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py
index ea126b07ea..16b360ae49 100644
--- a/scripts/tracetool/format/h.py
+++ b/scripts/tracetool/format/h.py
@@ -74,17 +74,10 @@ def generate(events, backend, group):
         cond = "true"
 
         out('',
-            'static inline void %(api)s(%(args)s)',
-            '{',
-            '    if (%(cond)s) {',
-            '        %(api_nocheck)s(%(names)s);',
-            '    }',
-            '}',
+            'void %(api)s(%(args)s);',
             api=e.api(),
-            api_nocheck=e.api(e.QEMU_TRACE_NOCHECK),
             args=e.args,
-            names=", ".join(e.args.names()),
-            cond=cond)
+            )
 
     backend.generate_end(events, group)
 
diff --git a/util/log.c b/util/log.c
index b87d399e4c..51f659be0a 100644
--- a/util/log.c
+++ b/util/log.c
@@ -143,6 +143,11 @@ void qemu_log_unlock(FILE *logfile)
     }
 }
 
+bool qemu_loglevel_mask(int mask)
+{
+    return (qemu_loglevel & mask) != 0;
+}
+
 void qemu_log(const char *fmt, ...)
 {
     FILE *f = qemu_log_trylock();
-- 
2.43.0



             reply	other threads:[~2025-04-01  0:42 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-01  0:26 saman [this message]
2025-04-01  1:00 ` [PATCH] Rust: Add tracing and logging support for Rust code Saman Dehghan
2025-04-01  8:27 ` Daniel P. Berrangé
2025-04-01  8:39   ` Paolo Bonzini
2025-04-01 19:21 ` Stefan Hajnoczi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250401002633.738345-1-saman@enumclass.cc \
    --to=saman@enumclass.cc \
    --cc=mads@ynddal.dk \
    --cc=manos.pitsidianakis@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-rust@nongnu.org \
    --cc=stefanha@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.