use std::collections;
use std::env;
use std::fs;
use std::io;
use std::process;
use std::string;
use std::vec;
use tracepoint_decode as td;
use tracepoint_perf as tp;
fn main() -> process::ExitCode {
let mut any_files_converted = false;
let args = env::args();
if args.len() <= 1 {
eprintln!("\nUsage: rewrite_perf [perf.data] ... (will generate *.rewrite)",);
return process::ExitCode::FAILURE;
}
let mut input = tp::PerfDataFileReader::new();
let mut output = tp::PerfDataFileWriter::new();
'next_file: for input_path in args.skip(1) {
let mut output_path = string::String::new();
output_path.push_str(&input_path);
output_path.push_str(".rewrite");
if let Err(e) = input.open_file(&input_path, tp::PerfDataFileEventOrder::File) {
write_error_message(&input_path, e, "input.open_file failed, skipping");
continue;
}
if input.byte_reader().byte_swap_needed() {
write_error_message(
&input_path,
io::ErrorKind::Unsupported.into(),
"input file is byte-swapped, skipping",
);
continue;
}
if let Err(e) = output.create_file(&output_path) {
write_error_message(&output_path, e, "output.create_file failed, skipping");
continue;
}
let mut sample_ids_used = collections::HashSet::new();
loop {
match input.move_next_event() {
Err(e) => {
write_error_message(
&input_path,
e,
"input.move_next_event failed, ignoring remainder of file",
);
break; }
Ok(false) => break, Ok(true) => {} }
let event = input.current_event();
match event.header.ty {
td::PerfEventHeaderType::HeaderAttr => {
continue;
}
td::PerfEventHeaderType::HeaderEventType => {
continue;
}
td::PerfEventHeaderType::HeaderTracingData => {
continue;
}
td::PerfEventHeaderType::HeaderBuildId | td::PerfEventHeaderType::HeaderFeature => {
continue;
}
td::PerfEventHeaderType::Sample => {
match input.get_sample_event_info(&event) {
Err(e) => {
eprintln!(
"{}: warning {} : input.get_sample_event_info failed, deleting output file.",
&input_path,
e,
);
output.close_no_finalize();
_ = fs::remove_file(output_path);
continue 'next_file;
}
Ok(info) => {
let desc = info.event_desc;
if let Some(format) = desc.format_arc() {
if output.add_tracepoint_event_desc(
desc.ids(),
desc.attr(),
desc.name(),
format,
) {
for id in desc.ids() {
sample_ids_used.insert(*id);
}
}
}
}
}
}
_ => {
}
};
if let Err(e) = output.write_event_data(event.data) {
write_error_message(
&output_path,
e,
"output.write_event_data failed, deleting output file.",
);
output.close_no_finalize();
_ = fs::remove_file(output_path);
continue 'next_file;
};
}
let mut merge_event_scratch_buffer = vec::Vec::new();
for desc in input.event_desc_list() {
if !desc.name().is_empty() {
merge_event_desc(
&mut merge_event_scratch_buffer,
&mut output,
&output_path,
&mut sample_ids_used,
desc,
);
}
}
for desc in input.event_desc_list() {
if desc.name().is_empty() {
merge_event_desc(
&mut merge_event_scratch_buffer,
&mut output,
&output_path,
&mut sample_ids_used,
desc,
);
}
}
for i in tp::PerfHeaderIndex::FirstFeature.0..tp::PerfHeaderIndex::LastFeature.0 {
match tp::PerfHeaderIndex(i) {
tp::PerfHeaderIndex::TracingData | tp::PerfHeaderIndex::EventDesc => {
continue;
}
index => {
let header_data = input.header(index);
if !header_data.is_empty() {
output.set_header(index, header_data);
}
}
}
}
output.set_tracing_data_required(
input.tracing_data_long_size(),
input.tracing_data_page_size(),
);
output.set_tracing_data_header_page(input.tracing_data_header_page());
output.set_tracing_data_header_event(input.tracing_data_header_event());
output.set_tracing_data_kallsyms(input.tracing_data_kallsyms());
output.set_tracing_data_printk(input.tracing_data_printk());
output.set_tracing_data_saved_cmd_line(input.tracing_data_saved_cmd_line());
output.set_tracing_data_clear_ftraces();
for index in 0..input.tracing_data_ftrace_count() {
output.set_tracing_data_add_ftrace(input.tracing_data_ftrace(index));
}
if let Err(e) = output.finalize_and_close() {
write_error_message(&output_path, e, "output.finalize_and_close failed");
_ = fs::remove_file(output_path);
continue;
}
println!("{} -> {}", input_path, output_path);
any_files_converted = true;
}
if any_files_converted {
process::ExitCode::SUCCESS
} else {
process::ExitCode::FAILURE
}
}
fn merge_event_desc(
merge_event_scratch_buffer: &mut vec::Vec<u64>,
output: &mut tp::PerfDataFileWriter,
output_path: &str,
sample_ids_used: &mut collections::HashSet<u64>,
desc: &td::PerfEventDesc,
) {
let ids = desc.ids();
merge_event_scratch_buffer.clear();
for id in ids {
if sample_ids_used.insert(*id) {
merge_event_scratch_buffer.push(*id);
}
}
if !merge_event_scratch_buffer.is_empty()
&& !output.add_event_desc(merge_event_scratch_buffer, desc.attr(), desc.name())
{
write_warning_message(
output_path,
io::ErrorKind::InvalidData.into(),
"output.add_event_desc failed, metadata incomplete",
);
}
}
fn write_error_message(filename: &str, error: io::Error, context: &str) {
eprintln!(
"{}: error {} : {} ({}).",
filename,
error.kind(),
context,
error
);
}
fn write_warning_message(filename: &str, error: io::Error, context: &str) {
eprintln!(
"{}: warning {} : {} ({}).",
filename,
error.kind(),
context,
error
);
}