Message ID | 20240527081421.2258624-5-zhao1.liu@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | scripts: Rewrite simpletrace printer in Rust | expand |
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 <pbonzini@redhat.com> > Signed-off-by: Zhao Liu <zhao1.liu@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.liu@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 >
> > +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. Thanks your reminder, I'll think about the hardcode approach as the easiest solution...removing the backtrace can help a lot here. Regards, Zhao
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.liu@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(); + + 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 {
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 <pbonzini@redhat.com> Signed-off-by: Zhao Liu <zhao1.liu@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(-)