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 >
signature.asc
Description: PGP signature