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
next reply other threads:[~2025-04-01 0:43 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).