use crate::interface::*;
use byteorder::{LittleEndian, WriteBytesExt};
use std::fs::File;
use std::io::{self, prelude::*};
extern "C" {
fn rftrace_backend_enable();
fn rftrace_backend_disable();
fn rftrace_backend_init(bufptr: *mut Event, len: usize, overwriting: bool);
fn rftrace_backend_get_events() -> *const Event;
fn rftrace_backend_get_events_index() -> usize;
}
pub fn enable() {
unsafe { rftrace_backend_enable() }
}
pub fn disable() {
unsafe { rftrace_backend_disable() }
}
#[derive(Copy, Clone, Debug)]
pub struct Events {
ptr: *mut Event,
len: usize,
cap: usize,
}
fn get_events(events: &mut Events) -> (Vec<Event>, usize) {
let ptr = unsafe { rftrace_backend_get_events() };
println!("{:?}, {:?}", ptr, events);
assert!(ptr == events.ptr, "Event buffer pointer mismatch!");
let eventvec = unsafe { Vec::from_raw_parts(events.ptr, events.len, events.cap) };
let idx = unsafe { rftrace_backend_get_events_index() };
(eventvec, idx)
}
pub fn init(max_event_count: usize, overwriting: bool) -> &'static mut Events {
assert!(
max_event_count > MAX_STACK_HEIGHT,
"Event buffer has to be larger than maximum stack height!"
);
let buf = vec![Event::Empty; max_event_count];
unsafe {
let (ptr, len, cap) = buf.into_raw_parts();
rftrace_backend_init(ptr, cap, overwriting);
return Box::leak(Box::new(Events { ptr, len, cap }));
}
}
pub fn dump_full_uftrace(
events: &mut Events,
out_dir: &str,
binary_name: &str,
linux: bool,
) -> io::Result<()> {
let pid = 42;
let sid = "00";
let tids = dump_traces(events, out_dir, false)?;
if tids.len() == 0 {
println!("Trace is empty!");
return Ok(());
}
println!("Creating fake uftrace data dir at {}..", out_dir);
println!(" Creating ./info");
let mut info: Vec<u8> = Vec::new();
info.extend("Ftrace!\x00".as_bytes());
info.write_u32::<LittleEndian>(4)
.expect("Write interrupted");
info.write_u16::<LittleEndian>(40)
.expect("Write interrupted");
info.push(1);
info.push(2);
println!(" feats = TASK_SESSION | SYM_REL_ADDR");
const TASK_SESSION: u64 = 1 << 1; const SYM_REL_ADDR: u64 = 1 << 5; info.write_u64::<LittleEndian>(TASK_SESSION | SYM_REL_ADDR)
.expect("Write interrupted");
println!(" info = CMDLINE | TASKINFO");
const CMDLINE: u64 = 1 << 3; const TASKINFO: u64 = 1 << 7; info.write_u64::<LittleEndian>(CMDLINE | TASKINFO)
.expect("Write interrupted");
info.write_u16::<LittleEndian>(0)
.expect("Write interrupted");
info.write_u16::<LittleEndian>(0)
.expect("Write interrupted");
info.write_u16::<LittleEndian>(0)
.expect("Write interrupted");
info.write_u16::<LittleEndian>(0)
.expect("Write interrupted");
println!(" cmdline = 'fakeuftrace'");
write!(info, "cmdline:fakeuftrace\n")?;
println!(" tid = {:?}", tids);
write!(info, "taskinfo:lines=2\n")?;
write!(info, "taskinfo:nr_tid={}\n", tids.len())?;
write!(info, "taskinfo:tids={}", tids[0])?;
for tid in &tids[1..] {
write!(info, ",{}", tid)?;
}
write!(info, "\n")?;
let infofile = format!("{}/info", out_dir);
let mut infofile = File::create(infofile)?;
infofile.write_all(&info[..])?;
drop(infofile);
println!(" Creating ./task.txt");
let taskfile = format!("{}/task.txt", out_dir);
let mut taskfile = File::create(taskfile)?;
println!(" pid = {}", pid);
println!(" sid = {}", sid);
println!(" exe = {}", binary_name);
write!(
taskfile,
"SESS timestamp=0.0 pid={} sid={} exename=\"{}\"\n",
pid, sid, binary_name
)?;
for tid in tids {
write!(taskfile, "TASK timestamp=0.0 tid={} pid={}\n", tid, pid)?;
}
drop(taskfile);
let mapfilename = format!("{}/sid-{}.map", out_dir, sid);
let mut mapfile = File::create(mapfilename)?;
if linux {
println!(
" Creating (incorrect) ./sid-{}.map by copying /proc/self/maps",
sid
);
let mut procfile = File::open("/proc/self/maps")?;
io::copy(&mut procfile, &mut mapfile)?;
} else {
println!(" Creating ./sid-{}.map fake memory map file", sid);
write!(
mapfile,
"000000000000-ffffffffffff r-xp 00000000 00:00 0 {}\n",
binary_name
)?;
write!(
mapfile,
"ffffffffffff-ffffffffffff rw-p 00000000 00:00 0 [stack]\n"
)?;
}
if linux {
println!(
"\nYou should generate symbols with `nm -n $BINARY > {}/$BINARY.sym`",
out_dir
);
println!(
"INFO: Linux mode is NOT fully supported yet! To get symbols working, you have to"
);
println!(" edit the sid-00.map and merge the section for each binary, so that it only occurs once.");
println!(" Needs to contain at least [stack] and the binaries you want symbols of.");
} else {
println!(
"\nYou should generate symbols with `nm -n $BINARY > {}/{}.sym`",
out_dir, binary_name
);
}
Ok(())
}
pub fn dump_trace(events: &mut Events, outfile: &str) -> io::Result<()> {
dump_traces(events, outfile, true)?;
Ok(())
}
fn dump_traces(events: &mut Events, outpath: &str, singlefile: bool) -> io::Result<Vec<u64>> {
disable();
println!("Saving traces to disk...!");
let (events, cidx) = get_events(events);
let cidx = cidx % events.len();
let mut out = Vec::<u8>::with_capacity(16 * events.len());
let mut tids: Vec<Option<core::num::NonZeroU64>> = Vec::new();
for e in events[cidx..].iter().chain(events[..cidx].iter()) {
match e {
Event::Exit(e) => {
if !tids.contains(&e.tid) {
tids.push(e.tid);
}
}
Event::Entry(e) => {
if !tids.contains(&e.tid) {
tids.push(e.tid);
}
}
Event::Empty => {}
}
}
for current_tid in &tids {
out.clear();
let tid = current_tid.map_or(0, |tid| tid.get());
println!(" Parsing TID {:?}...!", tid);
for e in events[cidx..].iter().chain(events[..cidx].iter()) {
match e {
Event::Exit(e) => {
if !singlefile && current_tid != &e.tid {
continue;
};
write_event(&mut out, e.time, e.from, 1);
}
Event::Entry(e) => {
if !singlefile && current_tid != &e.tid {
continue;
};
write_event(&mut out, e.time, e.to, 0);
}
Event::Empty => {
continue;
}
}
}
if !out.is_empty() {
let filename = if singlefile {
outpath.into()
} else {
let file = format!("{}.dat", tid);
format!("{}/{}", outpath, file)
};
println!(
" Writing to disk: {} events, {} bytes ({})",
out.len() / 16,
out.len(),
filename
);
let mut file = File::create(filename)?;
file.write_all(&out[..])?;
}
}
println!(" Parsed all events!");
Ok(tids
.iter()
.map(|tid| tid.map_or(0, |tid| tid.get()))
.collect())
}
fn write_event(out: &mut Vec<u8>, time: u64, addr: *const usize, kind: u64) {
out.write_u64::<LittleEndian>(time)
.expect("Write interrupted");
let mut merged: u64 = 0;
merged |= (kind & 0b11) << 0; merged |= 0 << 2; merged |= 0b101 << 3; merged |= (0 & ((1 << 10) - 1)) << 6; merged |= (addr as u64 & ((1 << 48) - 1)) << 16; out.write_u64::<LittleEndian>(merged)
.expect("Write interrupted");
}