use core::fmt;
use std::env;
use std::fs;
use std::io;
use std::io::IsTerminal;
use std::process;
use tracepoint_decode as td;
use tracepoint_perf as tp;
const USAGE_COMMON: &str = "
Usage: decode_perf_to_json [options...] PerfDataFiles...
";
const USAGE_SHORT: &str = "
Usage: decode_perf_to_json [options...] PerfDataFiles...
";
const USAGE_LONG: &str = "
Converts perf.data files to JSON.
Options:
-o, --output <file> Set the output filename. The default is stdout.
-h, --help Show this help message and exit.
";
fn main() -> process::ExitCode {
let mut input_names = Vec::new();
let mut output_name = String::new();
let mut show_help = false;
let mut usage_error = false;
let mut args = env::args().skip(1);
while let Some(arg) = args.next() {
if !arg.starts_with('-') {
input_names.push(arg);
} else if let Some(flag) = arg.strip_prefix("--") {
match flag {
"output" => {
if let Some(arg) = env::args().next() {
output_name = arg;
} else {
eprintln!("error: missing filename for flag --output.");
usage_error = true;
}
}
"help" => {
show_help = true;
}
_ => {
eprintln!("error: invalid flag \"--{}\".", flag);
usage_error = true;
}
}
} else {
let flags = &arg[1..];
for flag in flags.chars() {
match flag {
'o' => {
if let Some(arg) = args.next() {
output_name = arg;
} else {
eprintln!("error: missing filename for flag -o.");
usage_error = true;
}
}
'h' => {
show_help = true;
}
_ => {
eprintln!("error: invalid flag -{}.", flag);
usage_error = true;
}
}
}
}
}
if show_help {
println!("{}{}", USAGE_COMMON, USAGE_LONG);
return process::ExitCode::SUCCESS;
}
if usage_error {
eprintln!("{}{}", USAGE_COMMON, USAGE_SHORT);
return process::ExitCode::FAILURE;
}
if input_names.is_empty() {
eprintln!("error: no input files specified.");
return process::ExitCode::FAILURE;
}
let is_tty;
let mut output: Box<dyn io::Write> = if output_name.is_empty() {
let stdout = io::stdout();
is_tty = stdout.is_terminal();
Box::new(stdout)
} else {
match fs::File::create(&output_name) {
Ok(output) => {
is_tty = output.is_terminal();
Box::new(output)
}
Err(e) => {
eprintln!(
"error: failed to open output file \"{}\": {}",
output_name, e
);
return process::ExitCode::FAILURE;
}
}
};
match write_json(&mut output, &input_names, is_tty) {
Err(e) => {
eprintln!("error: {}", e);
process::ExitCode::FAILURE
}
Ok(exit_code) => exit_code,
}
}
fn write_json(
json_out: &mut Box<dyn io::Write>,
input_names: &[String],
is_tty: bool,
) -> io::Result<process::ExitCode> {
let mut exit_code = process::ExitCode::SUCCESS;
let mut comma;
let mut json_buf = String::new();
writeln!(json_out, "{}", if is_tty { "{" } else { "\u{FEFF}{" })?;
comma = false;
let mut reader = tp::PerfDataFileReader::new();
let mut enumerator_ctx = td::EventHeaderEnumeratorContext::new();
for input_name in input_names {
if comma {
writeln!(json_out, ",")?;
}
writeln!(
json_out,
" \"{}\": [",
td::display::JsonEscapeDisplay::new(input_name)
)?;
comma = false;
if let Err(e) = reader.open_file(input_name, tp::PerfDataFileEventOrder::Time) {
eprintln!("error: open_file(\"{}\") failed: {}", input_name, e);
exit_code = process::ExitCode::FAILURE;
} else {
loop {
match reader.move_next_event() {
Err(e) => {
eprintln!("error: read_event(\"{}\") failed: {}", input_name, e);
exit_code = process::ExitCode::FAILURE;
break; }
Ok(false) => break, Ok(true) => {} }
if comma {
writeln!(json_out, ",")?;
}
write!(json_out, " {{")?;
let event = reader.current_event();
if event.header.ty != td::PerfEventHeaderType::Sample {
let nonsample_event_info = reader.get_non_sample_event_info(&event);
if let Err(e) = &nonsample_event_info {
if *e != tp::PerfDataFileError::IdNotFound {
eprintln!(
"warning: get_non_sample_event_info(\"{}\") failed: {}",
input_name, e
);
}
}
write!(
json_out,
" \"NonSample\": \"{}\", \"size\": {}",
event.header.ty, event.header.size
)?;
if let Ok(nonsample_event_info) = nonsample_event_info {
write_json_meta(json_out, &nonsample_event_info.json_meta_display())?;
}
} else {
match reader.get_sample_event_info(&event) {
Err(e) => {
eprintln!(
"warning: get_sample_event_info(\"{}\") failed: {}",
input_name, e
);
write!(
json_out,
" \"n\": null, \"get_sample_event_info\": \"{}\", \"size\":{}",
e, event.header.size
)?;
}
Ok(sample_event_info) => {
if let Ok(mut enumerator) = enumerator_ctx.enumerate(&sample_event_info)
{
let eh_event_info = enumerator.event_info();
write_json_n(json_out, &eh_event_info.json_identity_display())?;
json_buf.clear();
_ = enumerator.write_json_item_and_move_next_sibling(
&mut json_buf, true, td::PerfConvertOptions::Default,
);
write!(json_out, "{}", json_buf)?;
if enumerator.state() == td::EventHeaderEnumeratorState::Error {
eprintln!(
"warning: move_next failed: {}",
enumerator.last_error()
);
}
write_json_meta(
json_out,
&eh_event_info.json_meta_display(Some(&sample_event_info)),
)?;
} else if let Some(event_format) = sample_event_info.format() {
write_json_n(json_out, &sample_event_info.json_name_display())?;
let skip_fields = event_format.common_field_count();
for field_format in event_format.fields().iter().skip(skip_fields) {
let field_value =
field_format.get_field_value(&sample_event_info);
write!(
json_out,
", \"{}\": {}",
td::display::JsonEscapeDisplay::new(field_format.name()),
field_value.json_display(),
)?;
}
write_json_meta(json_out, &sample_event_info.json_meta_display())?;
} else {
eprintln!(
"warning: no format found for event \"{}\"",
sample_event_info.name(),
);
write_json_n(json_out, &sample_event_info.json_name_display())?;
write_json_meta(json_out, &sample_event_info.json_meta_display())?;
}
}
}
}
write!(json_out, " }}")?;
comma = true;
}
}
write!(json_out, " ]")?;
comma = true;
}
writeln!(json_out, " }}")?;
Ok(exit_code)
}
fn write_json_n<D: fmt::Display>(json_out: &mut dyn io::Write, name_display: &D) -> io::Result<()> {
write!(json_out, " \"n\": \"{}\"", name_display)
}
fn write_json_meta<D: fmt::Display>(
json_out: &mut dyn io::Write,
meta_display: &D,
) -> io::Result<()> {
write!(json_out, ", \"meta\": {{ {} }}", meta_display)
}