All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stefan Hajnoczi <stefanha@redhat.com>
To: Paolo Bonzini <pbonzini@redhat.com>
Cc: qemu-devel@nongnu.org, "Tanish Desai" <tanishdesai37@gmail.com>,
	"Zhao Liu" <zhao1.liu@intel.com>,
	"Manos Pitsidianakis" <manos.pitsidianakis@linaro.org>,
	"Daniel P . Berrangé" <berrange@redhat.com>,
	"Mads Ynddal" <mads@ynddal.dk>
Subject: Re: [PATCH 08/16] tracetool: Add Rust format support
Date: Tue, 23 Sep 2025 15:23:54 -0400	[thread overview]
Message-ID: <20250923192354.GL509965@fedora> (raw)
In-Reply-To: <20250919112536.141782-9-pbonzini@redhat.com>

[-- Attachment #1: Type: text/plain, Size: 10556 bytes --]

On Fri, Sep 19, 2025 at 01:25:28PM +0200, Paolo Bonzini wrote:
> From: Tanish Desai <tanishdesai37@gmail.com>
> 
> Generating .rs files makes it possible to support tracing in rust.
> This support comprises a new format, and common code that converts
> the C expressions in trace-events to Rust.  In particular, types
> need to be converted, and PRI macros expanded.
> 
> As of this commit no backend generates Rust code, but it is already
> possible to use tracetool to generate Rust sources; they are not
> functional but they compile and contain tracepoint functions.
> 
> Signed-off-by: Tanish Desai <tanishdesai37@gmail.com>
> [Move Rust argument conversion from Event to Arguments; string
>  support. - Paolo]
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  scripts/tracetool/__init__.py  | 155 +++++++++++++++++++++++++++++++++
>  scripts/tracetool/format/rs.py |  71 +++++++++++++++
>  2 files changed, 226 insertions(+)
>  create mode 100644 scripts/tracetool/format/rs.py
> 
> diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py
> index a58d7938658..ea3e83f5adf 100644
> --- a/scripts/tracetool/__init__.py
> +++ b/scripts/tracetool/__init__.py
> @@ -31,6 +31,49 @@ def error(*lines):
>      error_write(*lines)
>      sys.exit(1)
>  
> +FMT_TOKEN = re.compile(r'''(?:
> +                       " ( (?: [^"\\] | \\[\\"abfnrt] |            # a string literal
> +                               \\x[0-9a-fA-F][0-9a-fA-F]) *? ) "
> +                       | ( PRI [duixX] (?:8|16|32|64|PTR|MAX) )    # a PRIxxx macro
> +                       | \s+                                       # spaces (ignored)
> +                       )''', re.X)
> +
> +PRI_SIZE_MAP = {
> +    '8':  'hh',
> +    '16': 'h',
> +    '32': '',
> +    '64': 'll',
> +    'PTR': 't',
> +    'MAX': 'j',
> +}
> +
> +def expand_format_string(c_fmt, prefix=""):
> +    def pri_macro_to_fmt(pri_macro):
> +        assert pri_macro.startswith("PRI")
> +        fmt_type = pri_macro[3]  # 'd', 'i', 'u', or 'x'
> +        fmt_size = pri_macro[4:]  # '8', '16', '32', '64', 'PTR', 'MAX'
> +
> +        size = PRI_SIZE_MAP.get(fmt_size, None)
> +        if size is None:
> +            raise Exception(f"unknown macro {pri_macro}")
> +        return size + fmt_type
> +
> +    result = prefix
> +    pos = 0
> +    while pos < len(c_fmt):
> +        m = FMT_TOKEN.match(c_fmt, pos)
> +        if not m:
> +            print("No match at position", pos, ":", repr(c_fmt[pos:]), file=sys.stderr)
> +            raise Exception("syntax error in trace file")
> +        if m[1]:
> +            substr = m[1]
> +        elif m[2]:
> +            substr = pri_macro_to_fmt(m[2])
> +        else:
> +            substr = ""
> +        result += substr
> +        pos = m.end()
> +    return result
>  
>  out_lineno = 1
>  out_filename = '<none>'
> @@ -90,6 +133,49 @@ def out(*lines, **kwargs):
>      "ptrdiff_t",
>  ]
>  
> +C_TYPE_KEYWORDS = {"char", "int", "void", "short", "long", "signed", "unsigned"}
> +
> +C_TO_RUST_TYPE_MAP = {
> +    "int": "std::ffi::c_int",
> +    "long": "std::ffi::c_long",
> +    "long long": "std::ffi::c_longlong",
> +    "short": "std::ffi::c_short",
> +    "char": "std::ffi::c_char",
> +    "bool": "bool",
> +    "unsigned": "std::ffi::c_uint",
> +    # multiple keywords, keep them sorted
> +    "long unsigned": "std::ffi::c_long",
> +    "long long unsigned": "std::ffi::c_ulonglong",
> +    "short unsigned": "std::ffi::c_ushort",
> +    "char unsigned": "u8",
> +    "int8_t": "i8",
> +    "uint8_t": "u8",
> +    "int16_t": "i16",
> +    "uint16_t": "u16",
> +    "int32_t": "i32",
> +    "uint32_t": "u32",
> +    "int64_t": "i64",
> +    "uint64_t": "u64",
> +    "void": "()",
> +    "size_t": "usize",
> +    "ssize_t": "isize",
> +    "uintptr_t": "usize",
> +    "ptrdiff_t": "isize",
> +}
> +
> +# Rust requires manual casting of <32-bit types when passing them to
> +# variable-argument functions.
> +RUST_VARARGS_SMALL_TYPES = {
> +    "std::ffi::c_short",
> +    "std::ffi::c_ushort",
> +    "std::ffi::c_char",
> +    "i8",
> +    "u8",
> +    "i16",
> +    "u16",
> +    "bool",
> +}
> +
>  def validate_type(name):
>      bits = name.split(" ")
>      for bit in bits:
> @@ -105,6 +191,38 @@ def validate_type(name):
>                               "other complex pointer types should be "
>                               "declared as 'void *'" % name)
>  
> +def c_type_to_rust(name):
> +    ptr = False
> +    const = False
> +    name = name.rstrip()
> +    if name[-1] == '*':
> +        name = name[:-1].rstrip()
> +        ptr = True
> +        if name[-1] == '*':
> +            # pointers to pointers are the same as void*
> +            name = "void"
> +
> +    bits = name.split()
> +    if "const" in bits:
> +        const = True
> +        bits.remove("const")
> +    if bits[0] in C_TYPE_KEYWORDS:
> +        if "signed" in bits:
> +            bits.remove("signed")
> +        if len(bits) > 1 and "int" in bits:
> +            bits.remove("int")
> +        bits.sort()
> +        name = ' '.join(bits)
> +    else:
> +        if len(bits) > 1:
> +            raise ValueError("Invalid type '%s'." % name)
> +        name = bits[0]
> +
> +    ty = C_TO_RUST_TYPE_MAP[name.strip()]
> +    if ptr:
> +        ty = f'*{"const" if const else "mut"} {ty}'
> +    return ty
> +
>  class Arguments:
>      """Event arguments description."""
>  
> @@ -193,6 +311,43 @@ def casted(self):
>          """List of argument names casted to their type."""
>          return ["(%s)%s" % (type_, name) for type_, name in self._args]
>  
> +    def rust_decl_extern(self):
> +        """Return a Rust argument list for an extern "C" function"""
> +        return ", ".join((f"_{name}: {c_type_to_rust(type_)}"
> +                          for type_, name in self._args))
> +
> +    def rust_decl(self):
> +        """Return a Rust argument list for a tracepoint function"""
> +        def decl_type(type_):
> +            if type_ == "const char *":
> +                return "&std::ffi::CStr"
> +            return c_type_to_rust(type_)
> +
> +        return ", ".join((f"_{name}: {decl_type(type_)}"
> +                          for type_, name in self._args))
> +
> +    def rust_call_extern(self):
> +        """Return a Rust argument list for a call to an extern "C" function"""
> +        def rust_cast(name, type_):
> +            if type_ == "const char *":
> +                return f"_{name}.as_ptr()"
> +            return f"_{name}"
> +
> +        return ", ".join((rust_cast(name, type_) for type_, name in self._args))
> +
> +    def rust_call_varargs(self):
> +        """Return a Rust argument list for a call to a C varargs function"""
> +        def rust_cast(name, type_):
> +            if type_ == "const char *":
> +                return f"_{name}.as_ptr()"
> +
> +            type_ = c_type_to_rust(type_)
> +            if type_ in RUST_VARARGS_SMALL_TYPES:
> +                return f"_{name} as std::ffi::c_int"
> +            return f"_{name} /* as {type_} */"
> +
> +        return ", ".join((rust_cast(name, type_) for type_, name in self._args))
> +
>  
>  class Event(object):
>      """Event description.
> diff --git a/scripts/tracetool/format/rs.py b/scripts/tracetool/format/rs.py
> new file mode 100644
> index 00000000000..c4ab0e59d85
> --- /dev/null
> +++ b/scripts/tracetool/format/rs.py
> @@ -0,0 +1,71 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +"""
> +trace-DIR.rs
> +"""
> +
> +__author__     = "Tanish Desai <tanishdesai37@gmail.com>"
> +__copyright__  = "Copyright 2025, Tanish Desai <tanishdesai37@gmail.com>"
> +__license__    = "GPL version 2 or (at your option) any later version"
> +
> +__maintainer__ = "Stefan Hajnoczi"
> +__email__      = "stefanha@redhat.com"
> +
> +
> +from tracetool import out
> +
> +
> +def generate(events, backend, group):
> +    out('// 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;',
> +        '',
> +        '#[inline(always)]',
> +        'fn trace_event_get_state_dynamic_by_id(_id: u16) -> bool {',
> +        '    unsafe { (trace_events_enabled_count != 0) && (_id != 0) }',
> +        '}',

This was translated to Rust from:

  /* it's on fast path, avoid consistency checks (asserts) */
  #define trace_event_get_state_dynamic_by_id(id) \
      (unlikely(trace_events_enabled_count) && _ ## id ## _DSTATE)

The _id != 0 expression is incorrect. The purpose was to check whether
the trace event is currently enabled (i.e. dynamically at runtime).

> +        '',
> +        'extern "C" {',
> +        '    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('}')
> +
> +    # static state
> +    for e in events:
> +        if 'disable' in e.properties:
> +            enabled = "false"
> +        else:
> +            enabled = "true"

What is the purpose of this loop? The variable enabled is unused so I
think it can be deleted.

> +
> +    backend.generate_begin(events, group)
> +
> +    for e in events:
> +        out('',
> +			'#[inline(always)]',

Tabs snuck in here. This should be indented with spaces.

> +            '#[allow(dead_code)]',
> +            'pub fn %(api)s(%(args)s)',
> +            '{',
> +            api=e.api(e.QEMU_TRACE),
> +            args=e.args.rust_decl())
> +
> +        if "disable" not in e.properties:
> +            backend.generate(e, group, check_trace_event_get_state=False)
> +            if backend.check_trace_event_get_state:
> +                event_id = 'TRACE_' + e.name.upper()
> +                out('    if trace_event_get_state_dynamic_by_id(unsafe { _%(event_id)s_DSTATE}) {',
> +                    event_id = event_id,
> +                    api=e.api())
> +                backend.generate(e, group, check_trace_event_get_state=True)
> +                out('    }')
> +        out('}')
> +
> +    backend.generate_end(events, group)
> -- 
> 2.51.0
> 
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

  reply	other threads:[~2025-09-23 19:24 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-19 11:25 [PATCH 00/16] tracetool: add Rust support Paolo Bonzini
2025-09-19 11:25 ` [PATCH 01/16] tracetool: fix usage of try_import() Paolo Bonzini
2025-09-23 19:06   ` Stefan Hajnoczi
2025-09-19 11:25 ` [PATCH 02/16] tracetool: remove dead code Paolo Bonzini
2025-09-23 19:06   ` Stefan Hajnoczi
2025-09-19 11:25 ` [PATCH 03/16] treewide: remove unnessary "coding" header Paolo Bonzini
2025-09-23 19:06   ` Stefan Hajnoczi
2025-09-19 11:25 ` [PATCH 04/16] tracetool: add SPDX headers Paolo Bonzini
2025-09-23 19:06   ` Stefan Hajnoczi
2025-09-19 11:25 ` [PATCH 05/16] trace/ftrace: move snprintf+write from tracepoints to ftrace.c Paolo Bonzini
2025-09-19 11:25 ` [PATCH 06/16] tracetool: add CHECK_TRACE_EVENT_GET_STATE Paolo Bonzini
2025-09-19 11:25 ` [PATCH 07/16] tracetool/backend: remove redundant trace event checks Paolo Bonzini
2025-09-19 11:25 ` [PATCH 08/16] tracetool: Add Rust format support Paolo Bonzini
2025-09-23 19:23   ` Stefan Hajnoczi [this message]
2025-09-24  7:13     ` Paolo Bonzini
2025-09-24  7:50       ` Daniel P. Berrangé
2025-09-24 11:49         ` Stefan Hajnoczi
2025-09-24 11:56           ` Daniel P. Berrangé
2025-09-24 18:10       ` Stefan Hajnoczi
2025-09-24 19:58         ` Paolo Bonzini
2025-09-25 11:50           ` Stefan Hajnoczi
2025-09-25 12:38             ` Paolo Bonzini
2025-09-25 15:09               ` Stefan Hajnoczi
2025-09-25 15:37                 ` Paolo Bonzini
2025-09-25 17:16                   ` Stefan Hajnoczi
2025-09-19 11:25 ` [PATCH 09/16] rust: add trace crate Paolo Bonzini
2025-09-23 19:29   ` Stefan Hajnoczi
2025-09-19 11:25 ` [PATCH 10/16] rust: qdev: add minimal clock bindings Paolo Bonzini
2025-09-23 19:31   ` Stefan Hajnoczi
2025-09-19 11:25 ` [PATCH 11/16] rust: pl011: add tracepoints Paolo Bonzini
2025-09-23 19:04   ` Stefan Hajnoczi
2025-09-19 11:25 ` [PATCH 12/16] tracetool/simple: add Rust support Paolo Bonzini
2025-09-23 19:35   ` Stefan Hajnoczi
2025-09-19 11:25 ` [PATCH 13/16] log: change qemu_loglevel to unsigned Paolo Bonzini
2025-09-23 19:06   ` Stefan Hajnoczi
2025-09-19 11:25 ` [PATCH 14/16] tracetool/log: add Rust support Paolo Bonzini
2025-09-23 19:36   ` Stefan Hajnoczi
2025-09-19 11:25 ` [PATCH 15/16] tracetool/ftrace: " Paolo Bonzini
2025-09-23 19:36   ` Stefan Hajnoczi
2025-09-19 11:25 ` [PATCH 16/16] tracetool/syslog: " Paolo Bonzini
2025-09-23 19:37   ` Stefan Hajnoczi
2025-09-23 19:43 ` [PATCH 00/16] tracetool: " Stefan Hajnoczi
  -- strict thread matches above, loose matches on Subject: below --
2025-09-29 15:49 [PATCH v2 " Paolo Bonzini
2025-09-29 15:49 ` [PATCH 08/16] tracetool: Add Rust format support Paolo Bonzini

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=20250923192354.GL509965@fedora \
    --to=stefanha@redhat.com \
    --cc=berrange@redhat.com \
    --cc=mads@ynddal.dk \
    --cc=manos.pitsidianakis@linaro.org \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=tanishdesai37@gmail.com \
    --cc=zhao1.liu@intel.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.