On Mon, May 27, 2024 at 04:14:19PM +0800, Zhao Liu wrote:
> Refer to scripts/simpletrace.py, parse and check the simple trace
> backend binary trace file.
> 
> Note, in order to keep certain backtrace information to get frame,
> adjust the cargo debug level for release version to "line-tables-only",
> which slows down the program, but is necessary.
> 
> Suggested-by: Paolo Bonzini <pbonz...@redhat.com>
> Signed-off-by: Zhao Liu <zhao1....@intel.com>
> ---
>  scripts/simpletrace-rust/Cargo.lock  |  79 +++++++++
>  scripts/simpletrace-rust/Cargo.toml  |   4 +
>  scripts/simpletrace-rust/src/main.rs | 253 ++++++++++++++++++++++++++-
>  3 files changed, 329 insertions(+), 7 deletions(-)
> 
> diff --git a/scripts/simpletrace-rust/Cargo.lock 
> b/scripts/simpletrace-rust/Cargo.lock
> index 3d815014eb44..37d80974ffe7 100644
> --- a/scripts/simpletrace-rust/Cargo.lock
> +++ b/scripts/simpletrace-rust/Cargo.lock
> @@ -2,6 +2,21 @@
>  # It is not intended for manual editing.
>  version = 3
>  
> +[[package]]
> +name = "addr2line"
> +version = "0.21.0"
> +source = "registry+https://github.com/rust-lang/crates.io-index";
> +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
> +dependencies = [
> + "gimli",
> +]
> +
> +[[package]]
> +name = "adler"
> +version = "1.0.2"
> +source = "registry+https://github.com/rust-lang/crates.io-index";
> +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
> +
>  [[package]]
>  name = "aho-corasick"
>  version = "1.1.3"
> @@ -60,6 +75,33 @@ dependencies = [
>   "windows-sys",
>  ]
>  
> +[[package]]
> +name = "backtrace"
> +version = "0.3.71"
> +source = "registry+https://github.com/rust-lang/crates.io-index";
> +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
> +dependencies = [
> + "addr2line",
> + "cc",
> + "cfg-if",
> + "libc",
> + "miniz_oxide",
> + "object",
> + "rustc-demangle",
> +]
> +
> +[[package]]
> +name = "cc"
> +version = "1.0.98"
> +source = "registry+https://github.com/rust-lang/crates.io-index";
> +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f"
> +
> +[[package]]
> +name = "cfg-if"
> +version = "1.0.0"
> +source = "registry+https://github.com/rust-lang/crates.io-index";
> +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
> +
>  [[package]]
>  name = "clap"
>  version = "4.5.4"
> @@ -93,18 +135,48 @@ version = "1.0.1"
>  source = "registry+https://github.com/rust-lang/crates.io-index";
>  checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
>  
> +[[package]]
> +name = "gimli"
> +version = "0.28.1"
> +source = "registry+https://github.com/rust-lang/crates.io-index";
> +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
> +
>  [[package]]
>  name = "is_terminal_polyfill"
>  version = "1.70.0"
>  source = "registry+https://github.com/rust-lang/crates.io-index";
>  checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
>  
> +[[package]]
> +name = "libc"
> +version = "0.2.155"
> +source = "registry+https://github.com/rust-lang/crates.io-index";
> +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
> +
>  [[package]]
>  name = "memchr"
>  version = "2.7.2"
>  source = "registry+https://github.com/rust-lang/crates.io-index";
>  checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
>  
> +[[package]]
> +name = "miniz_oxide"
> +version = "0.7.3"
> +source = "registry+https://github.com/rust-lang/crates.io-index";
> +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
> +dependencies = [
> + "adler",
> +]
> +
> +[[package]]
> +name = "object"
> +version = "0.32.2"
> +source = "registry+https://github.com/rust-lang/crates.io-index";
> +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
> +dependencies = [
> + "memchr",
> +]
> +
>  [[package]]
>  name = "once_cell"
>  version = "1.19.0"
> @@ -158,10 +230,17 @@ version = "0.8.3"
>  source = "registry+https://github.com/rust-lang/crates.io-index";
>  checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
>  
> +[[package]]
> +name = "rustc-demangle"
> +version = "0.1.24"
> +source = "registry+https://github.com/rust-lang/crates.io-index";
> +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
> +
>  [[package]]
>  name = "simpletrace-rust"
>  version = "0.1.0"
>  dependencies = [
> + "backtrace",
>   "clap",
>   "once_cell",
>   "regex",
> diff --git a/scripts/simpletrace-rust/Cargo.toml 
> b/scripts/simpletrace-rust/Cargo.toml
> index 24a79d04e566..23a3179de01c 100644
> --- a/scripts/simpletrace-rust/Cargo.toml
> +++ b/scripts/simpletrace-rust/Cargo.toml
> @@ -7,7 +7,11 @@ authors = ["Zhao Liu <zhao1....@intel.com>"]
>  license = "GPL-2.0-or-later"
>  
>  [dependencies]
> +backtrace = "0.3"
>  clap = "4.5.4"
>  once_cell = "1.19.0"
>  regex = "1.10.4"
>  thiserror = "1.0.20"
> +
> +[profile.release]
> +debug = "line-tables-only"
> diff --git a/scripts/simpletrace-rust/src/main.rs 
> b/scripts/simpletrace-rust/src/main.rs
> index b3b8baee7c66..f9a71d8dc243 100644
> --- a/scripts/simpletrace-rust/src/main.rs
> +++ b/scripts/simpletrace-rust/src/main.rs
> @@ -13,17 +13,24 @@
>  
>  mod trace;
>  
> +use std::collections::HashMap;
>  use std::env;
>  use std::fs::File;
>  use std::io::Error as IOError;
>  use std::io::ErrorKind;
>  use std::io::Read;
> +use std::mem::size_of;
>  
> +use backtrace::Backtrace;
>  use clap::Arg;
>  use clap::Command;
>  use thiserror::Error;
>  use trace::Event;
>  
> +const DROPPED_EVENT_ID: u64 = 0xfffffffffffffffe;
> +const HEADER_MAGIC: u64 = 0xf2b177cb0aa429b4;
> +const HEADER_EVENT_ID: u64 = 0xffffffffffffffff;
> +
>  const RECORD_TYPE_MAPPING: u64 = 0;
>  const RECORD_TYPE_EVENT: u64 = 1;
>  
> @@ -32,10 +39,25 @@ pub enum Error
>  {
>      #[error("usage: {0} [--no-header] <trace-events> <trace-file>")]
>      CliOptionUnmatch(String),
> +    #[error("Invalid event name ({0})")]
> +    InvalidEventName(String),
> +    #[error("Not a valid trace file, header id {0} != {1}")]
> +    InvalidHeaderId(u64, u64),
> +    #[error("Not a valid trace file, header magic {0} != {1}")]
> +    InvalidHeaderMagic(u64, u64),
>      #[error("Failed to read file: {0}")]
>      ReadFile(IOError),
> +    #[error(
> +        "event {0} is logged but is not declared in the trace events \
> +        file, try using trace-events-all instead."
> +    )]
> +    UnknownEvent(String),
>      #[error("Unknown record type ({0})")]
>      UnknownRecType(u64),
> +    #[error("Unknown version {0} of tracelog format!")]
> +    UnknownVersion(u64),
> +    #[error("Log format {0} not supported with this QEMU release!")]
> +    UnsupportedVersion(u64),
>  }
>  
>  pub type Result<T> = std::result::Result<T, Error>;
> @@ -98,19 +120,22 @@ struct LogHeader
>      version: u64,
>  }
>  
> +const LOG_HDR_LEN: usize = size_of::<LogHeader>();
> +
>  impl ReadHeader for LogHeader
>  {
>      fn read_header(mut fobj: &File) -> Result<Self>
>      {
> -        let mut raw_hdr = [0u8; 24];
> +        let mut raw_hdr = [0u8; LOG_HDR_LEN];
>          fobj.read_exact(&mut raw_hdr).map_err(Error::ReadFile)?;
>  
>          /*
>           * Safe because the size of log header (struct LogHeader)
>           * is 24 bytes, which is ensured by simple trace backend.
>           */
> -        let hdr =
> -            unsafe { std::mem::transmute::<[u8; 24], LogHeader>(raw_hdr) };
> +        let hdr = unsafe {
> +            std::mem::transmute::<[u8; LOG_HDR_LEN], LogHeader>(raw_hdr)
> +        };
>          Ok(hdr)
>      }
>  }
> @@ -142,6 +167,8 @@ struct RecordHeader
>      record_pid: u32,
>  }
>  
> +const REC_HDR_LEN: usize = size_of::<RecordHeader>();
> +
>  impl RecordHeader
>  {
>      fn extract_record(&self, mut fobj: &File) -> Result<RecordInfo>
> @@ -167,20 +194,232 @@ impl ReadHeader for RecordHeader
>  {
>      fn read_header(mut fobj: &File) -> Result<Self>
>      {
> -        let mut raw_hdr = [0u8; 24];
> +        let mut raw_hdr = [0u8; REC_HDR_LEN];
>          fobj.read_exact(&mut raw_hdr).map_err(Error::ReadFile)?;
>  
>          /*
>           * Safe because the size of record header (struct RecordHeader)
>           * is 24 bytes, which is ensured by simple trace backend.
>           */
> -        let hdr: RecordHeader =
> -            unsafe { std::mem::transmute::<[u8; 24], RecordHeader>(raw_hdr) 
> };
> +        let hdr: RecordHeader = unsafe {
> +            std::mem::transmute::<[u8; REC_HDR_LEN], RecordHeader>(raw_hdr)
> +        };
>          Ok(hdr)
>      }
>  }
>  
> -pub struct EventArgPayload {}
> +#[derive(Clone)]
> +pub struct EventArgPayload
> +{
> +    raw_val: Option<u64>,
> +    raw_str: Option<String>,
> +}
> +
> +impl EventArgPayload
> +{
> +    fn new(raw_val: Option<u64>, raw_str: Option<String>) -> Self
> +    {
> +        EventArgPayload { raw_val, raw_str }
> +    }
> +
> +    fn get_payload_str(
> +        offset: &mut usize,
> +        args_payload: &[u8],
> +    ) -> Result<EventArgPayload>
> +    {
> +        let length = u32::from_le_bytes(
> +            args_payload[*offset..(*offset + 4)].try_into().unwrap(),
> +        );
> +        *offset += 4;
> +        let raw_str: &[u8] = args_payload
> +            .get(*offset..(*offset + length as usize))
> +            .unwrap();
> +        *offset += length as usize;
> +        Ok(EventArgPayload::new(
> +            None,
> +            Some(String::from_utf8_lossy(raw_str).into_owned()),
> +        ))
> +    }
> +
> +    fn get_payload_val(
> +        offset: &mut usize,
> +        args_payload: &[u8],
> +    ) -> Result<EventArgPayload>
> +    {
> +        let raw_val = u64::from_le_bytes(
> +            args_payload[*offset..(*offset + 8)].try_into().unwrap(),
> +        );
> +        *offset += 8;
> +        Ok(EventArgPayload::new(Some(raw_val), None))
> +    }
> +}
> +
> +#[derive(Clone)]
> +struct EventEntry
> +{
> +    event: Event,
> +    event_id: u64,
> +    timestamp_ns: u64,
> +    record_pid: u32,
> +    rec_args: Vec<EventArgPayload>,
> +}
> +
> +impl EventEntry
> +{
> +    fn new(
> +        event: &Event,
> +        event_id: u64,
> +        timestamp_ns: u64,
> +        record_pid: u32,
> +    ) -> Self
> +    {
> +        EventEntry {
> +            event: event.clone(),
> +            event_id,
> +            timestamp_ns,
> +            record_pid,
> +            rec_args: Vec::new(),
> +        }
> +    }
> +}
> +
> +fn get_mapping(mut fobj: &File) -> Result<(u64, String)>
> +{
> +    let mut event_id_buf = [0u8; 8];
> +    fobj.read_exact(&mut event_id_buf)
> +        .map_err(Error::ReadFile)?;
> +    let event_id = u64::from_le_bytes(event_id_buf);
> +
> +    let mut len_buf = [0u8; 4];
> +    fobj.read_exact(&mut len_buf).map_err(Error::ReadFile)?;
> +
> +    let len = u32::from_le_bytes(len_buf);
> +    let mut name_buf = vec![0u8; len as usize];
> +    fobj.read_exact(&mut name_buf).map_err(Error::ReadFile)?;
> +    let name = String::from_utf8(name_buf.clone())
> +        .map_err(|_| Error::InvalidEventName(format!("{:?}", name_buf)))?;
> +
> +    Ok((event_id, name))
> +}
> +
> +fn read_record(fobj: &File) -> Result<RecordInfo>
> +{
> +    let hdr = RecordHeader::read_header(fobj)?;
> +    let info = hdr.extract_record(fobj)?;
> +    Ok(info)
> +}
> +
> +fn read_trace_header(fobj: &File) -> Result<()>
> +{
> +    let log_hdr = LogHeader::read_header(fobj)?;
> +    if log_hdr.event_id != HEADER_EVENT_ID {
> +        return Err(Error::InvalidHeaderId(log_hdr.event_id, 
> HEADER_EVENT_ID));
> +    }
> +    if log_hdr.magic != HEADER_MAGIC {
> +        return Err(Error::InvalidHeaderMagic(log_hdr.magic, HEADER_MAGIC));
> +    }
> +    if ![0, 2, 3, 4].contains(&(log_hdr.version as i64)) {
> +        return Err(Error::UnknownVersion(log_hdr.version));
> +    }
> +    if log_hdr.version != 4 {
> +        return Err(Error::UnsupportedVersion(log_hdr.version));
> +    }
> +    Ok(())
> +}
> +
> +fn read_trace_records(
> +    events: &Vec<Event>,
> +    fobj: &File,
> +    analyzer: &mut Formatter,
> +    read_header: bool,
> +) -> Result<Vec<String>>
> +{
> +    /* backtrace::Backtrace needs this env variable. */
> +    env::set_var("RUST_BACKTRACE", "1");
> +    let bt = Backtrace::new();
> +    let raw_frame = bt.frames().first().unwrap();
> +    let frameinfo = raw_frame.symbols().first().unwrap();
> +
> +    let dropped_event = Event::build(
> +        "Dropped_Event(uint64_t num_events_dropped)",
> +        frameinfo.lineno().unwrap() + 1,
> +        frameinfo.filename().unwrap().to_str().unwrap(),
> +    )
> +    .unwrap();

Is the backtrace necessary? This trick was used in Python where it's
part of the standard library, but I don't think there is any need for
Dropped_Event to refer to this line in the source code.

Maybe Rust has a way to do this at compile-time or you can hardcode
values instead.

> +
> +    let mut event_mapping = HashMap::new();
> +    for e in events {
> +        event_mapping.insert(e.name.clone(), e.clone());
> +    }
> +
> +    let drop_str = "dropped".to_string();
> +    event_mapping.insert(drop_str.clone(), dropped_event.clone());
> +    let mut event_id_to_name: HashMap<u64, String> = HashMap::new();
> +    event_id_to_name.insert(DROPPED_EVENT_ID, drop_str.clone());
> +
> +    if !read_header {
> +        for (event_id, event) in events.iter().enumerate() {
> +            event_id_to_name.insert(event_id as u64, event.name.clone());
> +        }
> +    }
> +
> +    let mut fmt_strs = Vec::new();
> +    loop {
> +        let rtype = RecordType::read_type(fobj)?;
> +        match rtype {
> +            RecordType::Empty => {
> +                break;
> +            }
> +            RecordType::Mapping => {
> +                let (event_id, event_name) =
> +                    get_mapping(fobj).expect("Error reading mapping");
> +                event_id_to_name.insert(event_id, event_name);
> +                continue;
> +            }
> +            RecordType::Event => {
> +                let rec = read_record(fobj).expect("Error reading record");
> +                let event_name =
> +                    event_id_to_name.get(&rec.event_id).unwrap().to_string();
> +                let event = event_mapping
> +                    .get(&event_name)
> +                    .ok_or(Error::UnknownEvent(event_name))?;
> +
> +                let mut entry = EventEntry::new(
> +                    event,
> +                    rec.event_id,
> +                    rec.timestamp_ns,
> +                    rec.record_pid,
> +                );
> +                let mut offset = 0;
> +
> +                for arg in &event.args.props {
> +                    let payload = if arg.is_string() {
> +                        EventArgPayload::get_payload_str(
> +                            &mut offset,
> +                            &rec.args_payload,
> +                        )?
> +                    } else {
> +                        EventArgPayload::get_payload_val(
> +                            &mut offset,
> +                            &rec.args_payload,
> +                        )?
> +                    };
> +                    entry.rec_args.push(payload);
> +                }
> +
> +                let fmt = analyzer.process_event(
> +                    &entry.rec_args,
> +                    &entry.event,
> +                    entry.event_id,
> +                    entry.timestamp_ns,
> +                    entry.record_pid,
> +                )?;
> +                fmt_strs.push(fmt);
> +            }
> +        }
> +    }
> +    Ok(fmt_strs)
> +}
>  
>  trait Analyzer
>  {
> -- 
> 2.34.1
> 

Attachment: signature.asc
Description: PGP signature

Reply via email to