use std::env;
use std::fs;
use std::process;
use std::str;
use std::vec;
use tracepoint_decode as td;
fn main() -> process::ExitCode {
let mut result = process::ExitCode::SUCCESS;
let mut filenames = vec::Vec::new();
for arg in env::args().skip(1) {
if arg.starts_with('-') {
eprintln!("Unknown option: {}", arg);
return usage();
} else {
filenames.push(arg);
}
}
if filenames.is_empty() {
eprintln!("No input files specified.");
return usage();
}
for filename in &filenames {
println!("Processing: {}", filename);
match fs::read(filename) {
Err(e) => {
eprintln!("Error {} reading from {}", e, filename);
result = process::ExitCode::FAILURE;
}
Ok(file_bytes) => {
if !print_events_from_dat_file(filename, &file_bytes) {
result = process::ExitCode::FAILURE;
}
}
}
}
result
}
fn usage() -> process::ExitCode {
eprintln!("Usage: decode_dat <filename1.dat> [<filename2.dat> ...]");
process::ExitCode::FAILURE
}
fn print_events_from_dat_file(filename: &str, file_bytes: &[u8]) -> bool {
let mut parser = DatFileParser {
file_bytes,
filename,
file_pos: 0,
any_errors: false,
};
let mut enumerator_context = td::EventHeaderEnumeratorContext::new();
let mut value_json = String::new();
while let Some((tracepoint_name, event_data)) = parser.next() {
match enumerator_context.enumerate_with_name_and_data(
tracepoint_name,
event_data,
td::EventHeaderEnumeratorContext::MOVE_NEXT_LIMIT_DEFAULT,
) {
Err(e) => {
eprintln!(
"Error {} enumerating {} in {}",
e, tracepoint_name, filename,
);
parser.any_errors = true;
}
Ok(mut enumerator) => {
let event_info = enumerator.event_info();
println!(
"- {} {{ {} }}",
event_info.identity_display(), event_info.json_meta_display(None), );
enumerator.move_next();
while enumerator.state().can_item_info() {
let item_info = enumerator.item_info();
let item_value = item_info.value();
match enumerator.state() {
td::EventHeaderEnumeratorState::Value => {
println!(
" - {} = {}",
item_info.name_display(),
item_value.display()
);
enumerator.move_next();
}
td::EventHeaderEnumeratorState::ArrayBegin
| td::EventHeaderEnumeratorState::StructBegin => {
const CONVERT_OPTIONS: td::PerfConvertOptions =
td::PerfConvertOptions::Default
.and_not(td::PerfConvertOptions::RootName);
enumerator
.write_json_item_and_move_next_sibling(
&mut value_json,
false,
CONVERT_OPTIONS,
)
.unwrap();
println!(" + {} = {}", item_info.name_display(), &value_json,);
value_json.clear();
}
_ => {
eprintln!(" - Unexpected state: {:?}", enumerator.state());
enumerator.move_next();
}
}
}
}
}
}
!parser.any_errors
}
struct DatFileParser<'a, 'b> {
file_bytes: &'a [u8],
filename: &'b str,
file_pos: usize,
any_errors: bool,
}
impl<'a, 'b> DatFileParser<'a, 'b> {
fn next(&mut self) -> Option<(&str, &'a [u8])> {
'retry: loop {
let event_begin_pos = self.file_pos;
let mut pos = event_begin_pos;
let remaining_size = self.file_bytes.len() - pos;
if remaining_size <= 4 {
if remaining_size != 0 {
eprintln!("Early EOF at pos {} in {}", event_begin_pos, self.filename);
self.any_errors = true;
}
return None;
}
let event_size =
u32::from_le_bytes(self.file_bytes[pos..pos + 4].try_into().unwrap()) as usize;
pos += 4;
if event_size <= 4 || event_size > remaining_size {
eprintln!(
"Invalid size {} at pos {} in {}",
event_size, event_begin_pos, self.filename
);
self.any_errors = true;
return None;
}
let event_end_pos = event_begin_pos + event_size;
self.file_pos = event_end_pos;
let tracepoint_name_start = pos;
while self.file_bytes[pos] != 0 {
pos += 1;
if pos == event_end_pos {
eprintln!(
"Unterminated string at pos {} in {}",
tracepoint_name_start, self.filename
);
self.any_errors = true;
continue 'retry;
}
}
let tracepoint_name_bytes = &self.file_bytes[tracepoint_name_start..pos];
pos += 1;
let tracepoint_name = match str::from_utf8(tracepoint_name_bytes) {
Err(e) => {
eprintln!(
"Invalid UTF-8 at pos {} in {}: {}",
tracepoint_name_start, self.filename, e
);
self.any_errors = true;
continue 'retry;
}
Ok(s) => s,
};
return Some((tracepoint_name, &self.file_bytes[pos..event_end_pos]));
}
}
}