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

2024-05-28 Thread Zhao Liu
> > +fn read_type(mut fobj: ) -> Result
> > +{
> > +let mut tbuf = [0u8; 8];
> > +if let Err(e) = fobj.read_exact( 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: ) -> Result
> > +{
> > +let mut raw_hdr = [0u8; 24];
> > +fobj.read_exact( 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: ) -> Result
> > +{
> > +let mut raw_hdr = [0u8; 24];
> > +fobj.read_exact( 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




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

2024-05-27 Thread Stefan Hajnoczi
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 
> Signed-off-by: Zhao Liu 
> ---
>  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]  ")]
>  CliOptionUnmatch(String),
> +#[error("Failed to read file: {0}")]
> +ReadFile(IOError),
> +#[error("Unknown record type ({0})")]
> +UnknownRecType(u64),
>  }
>  
>  pub type Result = std::result::Result;
>  
> +enum RecordType
> +{
> +Empty,
> +Mapping,
> +Event,
> +}
> +
> +#[repr(C)]
> +#[derive(Clone, Copy, Default)]
> +struct RecordRawType
> +{
> +rtype: u64,
> +}
> +
> +impl RecordType
> +{
> +fn read_type(mut fobj: ) -> Result
> +{
> +let mut tbuf = [0u8; 8];
> +if let Err(e) = fobj.read_exact( 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: ) -> Result
> +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: ) -> Result
> +{
> +let mut raw_hdr = [0u8; 24];
> +fobj.read_exact( 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,
> +}
> +
> +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(, mut fobj: ) -> Result
> +{
> +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::()
> +];
> +fobj.read_exact( info.args_payload)
> +.map_err(Error::ReadFile)?;
> +
> +Ok(info)
> +}
> +}
> +
> +impl ReadHeader for RecordHeader
> +{
> +fn read_header(mut fobj: ) -> Result
> +{
> +let mut raw_hdr = [0u8; 24];
> +fobj.read_exact( 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
> 


signature.asc
Description: PGP signature


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

2024-05-27 Thread Zhao Liu
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 
Signed-off-by: Zhao Liu 
---
 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]  ")]
 CliOptionUnmatch(String),
+#[error("Failed to read file: {0}")]
+ReadFile(IOError),
+#[error("Unknown record type ({0})")]
+UnknownRecType(u64),
 }
 
 pub type Result = std::result::Result;
 
+enum RecordType
+{
+Empty,
+Mapping,
+Event,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Default)]
+struct RecordRawType
+{
+rtype: u64,
+}
+
+impl RecordType
+{
+fn read_type(mut fobj: ) -> Result
+{
+let mut tbuf = [0u8; 8];
+if let Err(e) = fobj.read_exact( 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: ) -> Result
+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: ) -> Result
+{
+let mut raw_hdr = [0u8; 24];
+fobj.read_exact( 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,
+}
+
+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(, mut fobj: ) -> Result
+{
+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::()
+];
+fobj.read_exact( info.args_payload)
+.map_err(Error::ReadFile)?;
+
+Ok(info)
+}
+}
+
+impl ReadHeader for RecordHeader
+{
+fn read_header(mut fobj: ) -> Result
+{
+let mut raw_hdr = [0u8; 24];
+fobj.read_exact( 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
-- 
2.34.1