diff mbox series

[RFC,3/6] scripts/simpletrace-rust: Add helpers to parse trace file

Message ID 20240527081421.2258624-4-zhao1.liu@intel.com (mailing list archive)
State New, archived
Headers show
Series scripts: Rewrite simpletrace printer in Rust | expand

Commit Message

Zhao Liu May 27, 2024, 8:14 a.m. UTC
Refer to scripts/simpletrace.py, add the helpers to read the trace file
and parse the record type field, record header and log header.

Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
---
 scripts/simpletrace-rust/src/main.rs | 151 +++++++++++++++++++++++++++
 1 file changed, 151 insertions(+)

Comments

Stefan Hajnoczi May 27, 2024, 8:39 p.m. UTC | #1
On Mon, May 27, 2024 at 04:14:18PM +0800, Zhao Liu wrote:
> Refer to scripts/simpletrace.py, add the helpers to read the trace file
> and parse the record type field, record header and log header.
> 
> Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
> Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
> ---
>  scripts/simpletrace-rust/src/main.rs | 151 +++++++++++++++++++++++++++
>  1 file changed, 151 insertions(+)
> 
> diff --git a/scripts/simpletrace-rust/src/main.rs b/scripts/simpletrace-rust/src/main.rs
> index 2d2926b7658d..b3b8baee7c66 100644
> --- a/scripts/simpletrace-rust/src/main.rs
> +++ b/scripts/simpletrace-rust/src/main.rs
> @@ -14,21 +14,172 @@
>  mod trace;
>  
>  use std::env;
> +use std::fs::File;
> +use std::io::Error as IOError;
> +use std::io::ErrorKind;
> +use std::io::Read;
>  
>  use clap::Arg;
>  use clap::Command;
>  use thiserror::Error;
>  use trace::Event;
>  
> +const RECORD_TYPE_MAPPING: u64 = 0;
> +const RECORD_TYPE_EVENT: u64 = 1;
> +
>  #[derive(Error, Debug)]
>  pub enum Error
>  {
>      #[error("usage: {0} [--no-header] <trace-events> <trace-file>")]
>      CliOptionUnmatch(String),
> +    #[error("Failed to read file: {0}")]
> +    ReadFile(IOError),
> +    #[error("Unknown record type ({0})")]
> +    UnknownRecType(u64),
>  }
>  
>  pub type Result<T> = std::result::Result<T, Error>;
>  
> +enum RecordType
> +{
> +    Empty,
> +    Mapping,
> +    Event,
> +}
> +
> +#[repr(C)]
> +#[derive(Clone, Copy, Default)]
> +struct RecordRawType
> +{
> +    rtype: u64,
> +}
> +
> +impl RecordType
> +{
> +    fn read_type(mut fobj: &File) -> Result<RecordType>
> +    {
> +        let mut tbuf = [0u8; 8];
> +        if let Err(e) = fobj.read_exact(&mut tbuf) {
> +            if e.kind() == ErrorKind::UnexpectedEof {
> +                return Ok(RecordType::Empty);
> +            } else {
> +                return Err(Error::ReadFile(e));
> +            }
> +        }
> +
> +        /*
> +         * Safe because the layout of the trace record requires us to parse
> +         * the type first, and then there is a check on the validity of the
> +         * record type.
> +         */
> +        let raw_t =
> +            unsafe { std::mem::transmute::<[u8; 8], RecordRawType>(tbuf) };

A safe alternative: https://doc.rust-lang.org/std/primitive.u64.html#method.from_ne_bytes?

> +        match raw_t.rtype {
> +            RECORD_TYPE_MAPPING => Ok(RecordType::Mapping),
> +            RECORD_TYPE_EVENT => Ok(RecordType::Event),
> +            _ => Err(Error::UnknownRecType(raw_t.rtype)),
> +        }
> +    }
> +}
> +
> +trait ReadHeader
> +{
> +    fn read_header(fobj: &File) -> Result<Self>
> +    where
> +        Self: Sized;
> +}
> +
> +#[repr(C)]
> +#[derive(Clone, Copy)]
> +struct LogHeader
> +{
> +    event_id: u64,
> +    magic: u64,
> +    version: u64,
> +}
> +
> +impl ReadHeader for LogHeader
> +{
> +    fn read_header(mut fobj: &File) -> Result<Self>
> +    {
> +        let mut raw_hdr = [0u8; 24];
> +        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) };

Or u64::from_ne_bytes() for each field.

> +        Ok(hdr)
> +    }
> +}
> +
> +#[derive(Default)]
> +struct RecordInfo
> +{
> +    event_id: u64,
> +    timestamp_ns: u64,
> +    record_pid: u32,
> +    args_payload: Vec<u8>,
> +}
> +
> +impl RecordInfo
> +{
> +    fn new() -> Self
> +    {
> +        Default::default()
> +    }
> +}
> +
> +#[repr(C)]
> +#[derive(Clone, Copy)]
> +struct RecordHeader
> +{
> +    event_id: u64,
> +    timestamp_ns: u64,
> +    record_length: u32,
> +    record_pid: u32,
> +}
> +
> +impl RecordHeader
> +{
> +    fn extract_record(&self, mut fobj: &File) -> Result<RecordInfo>
> +    {
> +        let mut info = RecordInfo::new();
> +
> +        info.event_id = self.event_id;
> +        info.timestamp_ns = self.timestamp_ns;
> +        info.record_pid = self.record_pid;
> +        info.args_payload = vec![
> +            0u8;
> +            self.record_length as usize
> +                - std::mem::size_of::<RecordHeader>()
> +        ];
> +        fobj.read_exact(&mut info.args_payload)
> +            .map_err(Error::ReadFile)?;
> +
> +        Ok(info)
> +    }
> +}
> +
> +impl ReadHeader for RecordHeader
> +{
> +    fn read_header(mut fobj: &File) -> Result<Self>
> +    {
> +        let mut raw_hdr = [0u8; 24];
> +        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) };

Or u64::from_ne_bytes() and u32::from_ne_bytes() for all fields.

> +        Ok(hdr)
> +    }
> +}
> +
>  pub struct EventArgPayload {}
>  
>  trait Analyzer
> -- 
> 2.34.1
>
Zhao Liu May 28, 2024, 8:37 a.m. UTC | #2
> > +    fn read_type(mut fobj: &File) -> Result<RecordType>
> > +    {
> > +        let mut tbuf = [0u8; 8];
> > +        if let Err(e) = fobj.read_exact(&mut tbuf) {
> > +            if e.kind() == ErrorKind::UnexpectedEof {
> > +                return Ok(RecordType::Empty);
> > +            } else {
> > +                return Err(Error::ReadFile(e));
> > +            }
> > +        }
> > +
> > +        /*
> > +         * Safe because the layout of the trace record requires us to parse
> > +         * the type first, and then there is a check on the validity of the
> > +         * record type.
> > +         */
> > +        let raw_t =
> > +            unsafe { std::mem::transmute::<[u8; 8], RecordRawType>(tbuf) };
> 
> A safe alternative: https://doc.rust-lang.org/std/primitive.u64.html#method.from_ne_bytes?

Thanks! Will use it.

> > +        match raw_t.rtype {
> > +            RECORD_TYPE_MAPPING => Ok(RecordType::Mapping),
> > +            RECORD_TYPE_EVENT => Ok(RecordType::Event),
> > +            _ => Err(Error::UnknownRecType(raw_t.rtype)),
> > +        }
> > +    }
> > +}

[snip]

> > +{
> > +    fn read_header(mut fobj: &File) -> Result<Self>
> > +    {
> > +        let mut raw_hdr = [0u8; 24];
> > +        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) };
> 
> Or u64::from_ne_bytes() for each field.

Will do.

> > +        Ok(hdr)
> > +    }
> > +}

[snip]

> > +impl ReadHeader for RecordHeader
> > +{
> > +    fn read_header(mut fobj: &File) -> Result<Self>
> > +    {
> > +        let mut raw_hdr = [0u8; 24];
> > +        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) };
> 
> Or u64::from_ne_bytes() and u32::from_ne_bytes() for all fields.

Will do.

Thanks,
Zhao
diff mbox series

Patch

diff --git a/scripts/simpletrace-rust/src/main.rs b/scripts/simpletrace-rust/src/main.rs
index 2d2926b7658d..b3b8baee7c66 100644
--- a/scripts/simpletrace-rust/src/main.rs
+++ b/scripts/simpletrace-rust/src/main.rs
@@ -14,21 +14,172 @@ 
 mod trace;
 
 use std::env;
+use std::fs::File;
+use std::io::Error as IOError;
+use std::io::ErrorKind;
+use std::io::Read;
 
 use clap::Arg;
 use clap::Command;
 use thiserror::Error;
 use trace::Event;
 
+const RECORD_TYPE_MAPPING: u64 = 0;
+const RECORD_TYPE_EVENT: u64 = 1;
+
 #[derive(Error, Debug)]
 pub enum Error
 {
     #[error("usage: {0} [--no-header] <trace-events> <trace-file>")]
     CliOptionUnmatch(String),
+    #[error("Failed to read file: {0}")]
+    ReadFile(IOError),
+    #[error("Unknown record type ({0})")]
+    UnknownRecType(u64),
 }
 
 pub type Result<T> = std::result::Result<T, Error>;
 
+enum RecordType
+{
+    Empty,
+    Mapping,
+    Event,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Default)]
+struct RecordRawType
+{
+    rtype: u64,
+}
+
+impl RecordType
+{
+    fn read_type(mut fobj: &File) -> Result<RecordType>
+    {
+        let mut tbuf = [0u8; 8];
+        if let Err(e) = fobj.read_exact(&mut tbuf) {
+            if e.kind() == ErrorKind::UnexpectedEof {
+                return Ok(RecordType::Empty);
+            } else {
+                return Err(Error::ReadFile(e));
+            }
+        }
+
+        /*
+         * Safe because the layout of the trace record requires us to parse
+         * the type first, and then there is a check on the validity of the
+         * record type.
+         */
+        let raw_t =
+            unsafe { std::mem::transmute::<[u8; 8], RecordRawType>(tbuf) };
+        match raw_t.rtype {
+            RECORD_TYPE_MAPPING => Ok(RecordType::Mapping),
+            RECORD_TYPE_EVENT => Ok(RecordType::Event),
+            _ => Err(Error::UnknownRecType(raw_t.rtype)),
+        }
+    }
+}
+
+trait ReadHeader
+{
+    fn read_header(fobj: &File) -> Result<Self>
+    where
+        Self: Sized;
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+struct LogHeader
+{
+    event_id: u64,
+    magic: u64,
+    version: u64,
+}
+
+impl ReadHeader for LogHeader
+{
+    fn read_header(mut fobj: &File) -> Result<Self>
+    {
+        let mut raw_hdr = [0u8; 24];
+        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) };
+        Ok(hdr)
+    }
+}
+
+#[derive(Default)]
+struct RecordInfo
+{
+    event_id: u64,
+    timestamp_ns: u64,
+    record_pid: u32,
+    args_payload: Vec<u8>,
+}
+
+impl RecordInfo
+{
+    fn new() -> Self
+    {
+        Default::default()
+    }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+struct RecordHeader
+{
+    event_id: u64,
+    timestamp_ns: u64,
+    record_length: u32,
+    record_pid: u32,
+}
+
+impl RecordHeader
+{
+    fn extract_record(&self, mut fobj: &File) -> Result<RecordInfo>
+    {
+        let mut info = RecordInfo::new();
+
+        info.event_id = self.event_id;
+        info.timestamp_ns = self.timestamp_ns;
+        info.record_pid = self.record_pid;
+        info.args_payload = vec![
+            0u8;
+            self.record_length as usize
+                - std::mem::size_of::<RecordHeader>()
+        ];
+        fobj.read_exact(&mut info.args_payload)
+            .map_err(Error::ReadFile)?;
+
+        Ok(info)
+    }
+}
+
+impl ReadHeader for RecordHeader
+{
+    fn read_header(mut fobj: &File) -> Result<Self>
+    {
+        let mut raw_hdr = [0u8; 24];
+        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) };
+        Ok(hdr)
+    }
+}
+
 pub struct EventArgPayload {}
 
 trait Analyzer