tracetool: add Rust DTrace/SystemTap SDT support
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>
This commit is contained in:
6
rust/Cargo.lock
generated
6
rust/Cargo.lock
generated
@@ -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]]
|
||||
|
||||
@@ -14,6 +14,7 @@ rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
libc = { workspace = true }
|
||||
probe = "0.5"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -461,6 +461,7 @@ def formats(self):
|
||||
|
||||
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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
64
tests/tracetool/dtrace.rs
Normal file
64
tests/tracetool/dtrace.rs
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
@@ -6,19 +6,31 @@
|
||||
#[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 */);}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,19 +6,31 @@
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,19 +6,31 @@
|
||||
#[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); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,19 +6,31 @@
|
||||
#[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 */);}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 += [
|
||||
|
||||
Reference in New Issue
Block a user