]> git.ipfire.org Git - thirdparty/qemu.git/commitdiff
tracetool: add Rust DTrace/SystemTap SDT support
authorPaolo Bonzini <pbonzini@redhat.com>
Thu, 20 Nov 2025 15:00:49 +0000 (16:00 +0100)
committerPaolo Bonzini <pbonzini@redhat.com>
Sat, 27 Dec 2025 09:11:10 +0000 (10:11 +0100)
Implement DTrace/SystemTap SDT by emitting the following:
- The probe crate's probe!() macro is used to emit a DTrace/SystemTap
  SDT probe.
- Every trace event gets a corresponding trace_<name>_enabled() -> bool
  generated function that Rust code can use to avoid expensive
  computation when a trace event is disabled. This API works for other
  trace backends too.

`#[allow(dead_code)]` additions are necessary for QEMU's dstate in
generated trace-<dir>.rs files since they are unused by the dtrace
backend. `./configure --enable-trace-backends=` can enable multiple
backends, so keep it simple and just silence the warning instead of
trying to detect the condition when generating the dstate code can be
skipped.

The tracetool tests are updated. Take a look at
tests/tracetool/dtrace.rs to see what the new generated code looks like.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Link: https://lore.kernel.org/r/20251119205200.173170-5-stefanha@redhat.com
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
13 files changed:
rust/Cargo.lock
rust/trace/Cargo.toml
rust/trace/meson.build
rust/trace/src/lib.rs
scripts/tracetool/__init__.py
scripts/tracetool/backend/dtrace.py
scripts/tracetool/format/rs.py
tests/tracetool/dtrace.rs [new file with mode: 0644]
tests/tracetool/ftrace.rs
tests/tracetool/log.rs
tests/tracetool/simple.rs
tests/tracetool/syslog.rs
tests/tracetool/tracetool-test.py

index f520597f25d2d3e67b7368765cb0da6f24b3281a..42ae857fe533d451cbbcf90f706e7076254c0ab5 100644 (file)
@@ -230,6 +230,10 @@ dependencies = [
  "util",
 ]
 
+[[package]]
+name = "probe"
+version = "0.5.2"
+
 [[package]]
 name = "proc-macro-error"
 version = "1.0.4"
@@ -388,6 +392,7 @@ dependencies = [
  "migration",
  "qom",
  "system",
+ "trace",
  "util",
 ]
 
@@ -430,6 +435,7 @@ name = "trace"
 version = "0.1.0"
 dependencies = [
  "libc",
+ "probe",
 ]
 
 [[package]]
index fc81bce5803d3b8182e5c0fb91466f56ff8ce7ec..11e27f8d286e92ab4dcb316d2adbb1d61e04f1e8 100644 (file)
@@ -14,6 +14,7 @@ rust-version.workspace = true
 
 [dependencies]
 libc = { workspace = true }
+probe = "0.5"
 
 [lints]
 workspace = true
index 1b3498f7fc1ee1c67940d141e74a0465c2016359..0071a49cf7273e4c01b2791d0153c56e6466deb5 100644 (file)
@@ -11,7 +11,7 @@ _trace_rs = static_library(
   'trace',             # Library name,
   lib_rs,
   trace_rs_targets,         # List of generated `.rs` custom targets
-  dependencies: [libc_rs],
+  dependencies: [libc_rs, probe_rs],
 )
 
 trace_rs = declare_dependency(link_with: _trace_rs)
index e03bce43c4717fefcdccc3fed5a922b956efcb97..c2abe430a52b6a9ee02d0c0f2aeaa2a53ca12fe2 100644 (file)
@@ -6,6 +6,9 @@
 #[doc(hidden)]
 /// Re-exported item to avoid adding libc as a dependency everywhere.
 pub use libc::{syslog, LOG_INFO};
+#[doc(hidden)]
+/// Re-exported item to avoid adding probe as a dependency everywhere.
+pub use probe::probe;
 
 #[macro_export]
 /// Define the trace-points from the named directory (which should have slashes
@@ -21,6 +24,7 @@ macro_rules! include_trace {
         #[allow(
             clippy::ptr_as_ptr,
             clippy::cast_lossless,
+            clippy::nonminimal_bool,
             clippy::used_underscore_binding
         )]
         mod trace {
index 74062d21a7cedb552f2b0adcedcb2f9884c47d0e..61ba6f1ba8c09788ed266c38fce826f358e364a7 100644 (file)
@@ -461,6 +461,7 @@ class Event(object):
 
     QEMU_TRACE               = "trace_%(name)s"
     QEMU_TRACE_TCG           = QEMU_TRACE + "_tcg"
+    QEMU_RUST_DSTATE         = "trace_%(name)s_enabled"
     QEMU_DSTATE              = "_TRACE_%(NAME)s_DSTATE"
     QEMU_BACKEND_DSTATE      = "TRACE_%(NAME)s_BACKEND_DSTATE"
     QEMU_EVENT               = "_TRACE_%(NAME)s_EVENT"
index b4af403025c84d487793e282d6c2d5d03013694c..f0b58cc15857e6ad6616c2d54dc11a742e80d7da 100644 (file)
@@ -70,3 +70,34 @@ def generate_h(event, group):
 def generate_h_backend_dstate(event, group):
     out('    QEMU_%(uppername)s_ENABLED() || \\',
         uppername=event.name.upper())
+
+
+def generate_rs_begin(events, group):
+    out('use std::cell::UnsafeCell;',
+        '',
+        'extern "C" {')
+    # These are the Rust declarations of the .probes section semaphores
+    # generated by dtrace(1) in its .o file output.
+    for e in events:
+        if 'disable' in e.properties:
+            continue
+        out('    #[allow(dead_code)]',
+            f'    static qemu_{e.name}_semaphore: UnsafeCell<u16>;')
+    out('}',
+        '')
+
+
+def generate_rs(event, group):
+    args = event.args.rust_call_extern()
+    if args:
+        args = ', ' + args
+
+    out(f'    ::trace::probe!(qemu, {event.name}{args});')
+
+
+def generate_rs_backend_dstate(event, group):
+    # Rust does not have access to the <provider>_<name>_ENABLED() macro from
+    # the dtrace(1) generated .h file. Use the matching semaphore declarations
+    # generated by generate_rs_begin() instead.
+    out('    (unsafe {qemu_%(n)s_semaphore.get().read_volatile()}) != 0 ||',
+        n=event.name)
index 32ac4e597704848e52bad17cef317657313c3d66..7d9af7edfee2cfe72ed34783fb6aa29927f45a62 100644 (file)
@@ -24,25 +24,43 @@ def generate(events, backend, group):
         '#[allow(unused_imports)]',
         'use util::bindings;',
         '',
+        '#[allow(dead_code)]',
         '#[inline(always)]',
         'fn trace_event_state_is_enabled(dstate: u16) -> bool {',
         '    (unsafe { trace_events_enabled_count }) != 0 && dstate != 0',
         '}',
         '',
         'extern "C" {',
+        '    #[allow(dead_code)]',
         '    static mut trace_events_enabled_count: u32;',
         '}',)
 
     out('extern "C" {')
 
     for e in events:
-        out('    static mut %s: u16;' % e.api(e.QEMU_DSTATE))
-    out('}')
+        out('    #[allow(dead_code)]',
+            '    static mut %s: u16;' % e.api(e.QEMU_DSTATE))
+    out('}',
+        '')
 
     backend.generate_begin(events, group)
 
     for e in events:
-        out('',
+        out('#[inline(always)]',
+            '#[allow(dead_code)]',
+            'pub fn %(api)s() -> bool',
+            '{',
+            api=e.api(e.QEMU_RUST_DSTATE))
+
+        if "disable" not in e.properties:
+            backend.generate_backend_dstate(e, group)
+            if backend.check_trace_event_get_state:
+                out('    trace_event_state_is_enabled(unsafe { _%(event_id)s_DSTATE}) ||',
+                    event_id = 'TRACE_' + e.name.upper())
+
+        out('    false',
+            '}',
+            '',
             '#[inline(always)]',
             '#[allow(dead_code)]',
             'pub fn %(api)s(%(args)s)',
@@ -59,6 +77,7 @@ def generate(events, backend, group):
                     api=e.api())
                 backend.generate(e, group, check_trace_event_get_state=True)
                 out('    }')
-        out('}')
+        out('}',
+            '')
 
     backend.generate_end(events, group)
diff --git a/tests/tracetool/dtrace.rs b/tests/tracetool/dtrace.rs
new file mode 100644 (file)
index 0000000..3490fcb
--- /dev/null
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// This file is @generated by tracetool, do not edit.
+
+#[allow(unused_imports)]
+use std::ffi::c_char;
+#[allow(unused_imports)]
+use util::bindings;
+
+#[allow(dead_code)]
+#[inline(always)]
+fn trace_event_state_is_enabled(dstate: u16) -> bool {
+    (unsafe { trace_events_enabled_count }) != 0 && dstate != 0
+}
+
+extern "C" {
+    #[allow(dead_code)]
+    static mut trace_events_enabled_count: u32;
+}
+extern "C" {
+    #[allow(dead_code)]
+    static mut _TRACE_TEST_BLAH_DSTATE: u16;
+    #[allow(dead_code)]
+    static mut _TRACE_TEST_WIBBLE_DSTATE: u16;
+}
+
+use std::cell::UnsafeCell;
+
+extern "C" {
+    #[allow(dead_code)]
+    static qemu_test_blah_semaphore: UnsafeCell<u16>;
+    #[allow(dead_code)]
+    static qemu_test_wibble_semaphore: UnsafeCell<u16>;
+}
+
+#[inline(always)]
+#[allow(dead_code)]
+pub fn trace_test_blah_enabled() -> bool
+{
+    (unsafe {qemu_test_blah_semaphore.get().read_volatile()}) != 0 ||
+    false
+}
+
+#[inline(always)]
+#[allow(dead_code)]
+pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr)
+{
+    ::trace::probe!(qemu, test_blah, _context, _filename.as_ptr());
+}
+
+#[inline(always)]
+#[allow(dead_code)]
+pub fn trace_test_wibble_enabled() -> bool
+{
+    (unsafe {qemu_test_wibble_semaphore.get().read_volatile()}) != 0 ||
+    false
+}
+
+#[inline(always)]
+#[allow(dead_code)]
+pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int)
+{
+    ::trace::probe!(qemu, test_wibble, _context, _value);
+}
+
index 07b9259cf29f31adc1ed73a35c782a16da5a39d7..34f66004902a9ff94b934ce004afb297cd57d53e 100644 (file)
@@ -6,19 +6,31 @@ use std::ffi::c_char;
 #[allow(unused_imports)]
 use util::bindings;
 
+#[allow(dead_code)]
 #[inline(always)]
 fn trace_event_state_is_enabled(dstate: u16) -> bool {
     (unsafe { trace_events_enabled_count }) != 0 && dstate != 0
 }
 
 extern "C" {
+    #[allow(dead_code)]
     static mut trace_events_enabled_count: u32;
 }
 extern "C" {
+    #[allow(dead_code)]
     static mut _TRACE_TEST_BLAH_DSTATE: u16;
+    #[allow(dead_code)]
     static mut _TRACE_TEST_WIBBLE_DSTATE: u16;
 }
 
+#[inline(always)]
+#[allow(dead_code)]
+pub fn trace_test_blah_enabled() -> bool
+{
+    trace_event_state_is_enabled(unsafe { _TRACE_TEST_BLAH_DSTATE}) ||
+    false
+}
+
 #[inline(always)]
 #[allow(dead_code)]
 pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr)
@@ -29,6 +41,14 @@ pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr)
     }
 }
 
+#[inline(always)]
+#[allow(dead_code)]
+pub fn trace_test_wibble_enabled() -> bool
+{
+    trace_event_state_is_enabled(unsafe { _TRACE_TEST_WIBBLE_DSTATE}) ||
+    false
+}
+
 #[inline(always)]
 #[allow(dead_code)]
 pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int)
@@ -38,3 +58,4 @@ pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int)
         unsafe {bindings::ftrace_write(format_string.as_ptr() as *const c_char, _context /* as *mut () */, _value /* as std::ffi::c_int */);}
     }
 }
+
index c191895c8f87e9d9596b7a8db4c98713fa75e207..770758611d134624d6363632f167c3f976b45d38 100644 (file)
@@ -6,19 +6,31 @@ use std::ffi::c_char;
 #[allow(unused_imports)]
 use util::bindings;
 
+#[allow(dead_code)]
 #[inline(always)]
 fn trace_event_state_is_enabled(dstate: u16) -> bool {
     (unsafe { trace_events_enabled_count }) != 0 && dstate != 0
 }
 
 extern "C" {
+    #[allow(dead_code)]
     static mut trace_events_enabled_count: u32;
 }
 extern "C" {
+    #[allow(dead_code)]
     static mut _TRACE_TEST_BLAH_DSTATE: u16;
+    #[allow(dead_code)]
     static mut _TRACE_TEST_WIBBLE_DSTATE: u16;
 }
 
+#[inline(always)]
+#[allow(dead_code)]
+pub fn trace_test_blah_enabled() -> bool
+{
+    trace_event_state_is_enabled(unsafe { _TRACE_TEST_BLAH_DSTATE}) ||
+    false
+}
+
 #[inline(always)]
 #[allow(dead_code)]
 pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr)
@@ -31,6 +43,14 @@ pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr)
     }
 }
 
+#[inline(always)]
+#[allow(dead_code)]
+pub fn trace_test_wibble_enabled() -> bool
+{
+    trace_event_state_is_enabled(unsafe { _TRACE_TEST_WIBBLE_DSTATE}) ||
+    false
+}
+
 #[inline(always)]
 #[allow(dead_code)]
 pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int)
@@ -42,3 +62,4 @@ pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int)
         }
     }
 }
+
index 9ee39495e387da65d560669e382ef2b5046b172b..92f896ef178fd1689b9cb0b71aa68a1359a6519a 100644 (file)
@@ -6,19 +6,31 @@ use std::ffi::c_char;
 #[allow(unused_imports)]
 use util::bindings;
 
+#[allow(dead_code)]
 #[inline(always)]
 fn trace_event_state_is_enabled(dstate: u16) -> bool {
     (unsafe { trace_events_enabled_count }) != 0 && dstate != 0
 }
 
 extern "C" {
+    #[allow(dead_code)]
     static mut trace_events_enabled_count: u32;
 }
 extern "C" {
+    #[allow(dead_code)]
     static mut _TRACE_TEST_BLAH_DSTATE: u16;
+    #[allow(dead_code)]
     static mut _TRACE_TEST_WIBBLE_DSTATE: u16;
 }
 
+#[inline(always)]
+#[allow(dead_code)]
+pub fn trace_test_blah_enabled() -> bool
+{
+    trace_event_state_is_enabled(unsafe { _TRACE_TEST_BLAH_DSTATE}) ||
+    false
+}
+
 #[inline(always)]
 #[allow(dead_code)]
 pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr)
@@ -29,6 +41,14 @@ pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr)
     }
 }
 
+#[inline(always)]
+#[allow(dead_code)]
+pub fn trace_test_wibble_enabled() -> bool
+{
+    trace_event_state_is_enabled(unsafe { _TRACE_TEST_WIBBLE_DSTATE}) ||
+    false
+}
+
 #[inline(always)]
 #[allow(dead_code)]
 pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int)
@@ -38,3 +58,4 @@ pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int)
         unsafe { _simple_trace_test_wibble(_context, _value); }
     }
 }
+
index 9d3675a0b5767558f6c1a27c9d03a29c148a0370..378d03d34b214677ef00382f47cdb4574694f9f6 100644 (file)
@@ -6,19 +6,31 @@ use std::ffi::c_char;
 #[allow(unused_imports)]
 use util::bindings;
 
+#[allow(dead_code)]
 #[inline(always)]
 fn trace_event_state_is_enabled(dstate: u16) -> bool {
     (unsafe { trace_events_enabled_count }) != 0 && dstate != 0
 }
 
 extern "C" {
+    #[allow(dead_code)]
     static mut trace_events_enabled_count: u32;
 }
 extern "C" {
+    #[allow(dead_code)]
     static mut _TRACE_TEST_BLAH_DSTATE: u16;
+    #[allow(dead_code)]
     static mut _TRACE_TEST_WIBBLE_DSTATE: u16;
 }
 
+#[inline(always)]
+#[allow(dead_code)]
+pub fn trace_test_blah_enabled() -> bool
+{
+    trace_event_state_is_enabled(unsafe { _TRACE_TEST_BLAH_DSTATE}) ||
+    false
+}
+
 #[inline(always)]
 #[allow(dead_code)]
 pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr)
@@ -29,6 +41,14 @@ pub fn trace_test_blah(_context: *mut (), _filename: &std::ffi::CStr)
     }
 }
 
+#[inline(always)]
+#[allow(dead_code)]
+pub fn trace_test_wibble_enabled() -> bool
+{
+    trace_event_state_is_enabled(unsafe { _TRACE_TEST_WIBBLE_DSTATE}) ||
+    false
+}
+
 #[inline(always)]
 #[allow(dead_code)]
 pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int)
@@ -38,3 +58,4 @@ pub fn trace_test_wibble(_context: *mut (), _value: std::ffi::c_int)
         unsafe {::trace::syslog(::trace::LOG_INFO, format_string.as_ptr() as *const c_char, _context /* as *mut () */, _value /* as std::ffi::c_int */);}
     }
 }
+
index 786083ad7fb7a94be26068d06be2001df679ee64..30006a9919070b6affb224012ac7fae6ad5e16b3 100755 (executable)
@@ -14,7 +14,7 @@ def get_formats(backend):
         "c",
         "h",
     ]
-    if backend in {"ftrace", "log", "simple", "syslog"}:
+    if backend in {"dtrace", "ftrace", "log", "simple", "syslog"}:
         formats += ["rs"]
     if backend == "dtrace":
         formats += [