1use std::fs::File;
2use std::io::prelude::*;
3use std::io::{self};
4
5use byteorder::{LittleEndian, WriteBytesExt};
6
7use crate::interface::*;
8
9extern "C" {
10 fn rftrace_backend_enable();
11 fn rftrace_backend_disable();
12 fn rftrace_backend_init(bufptr: *mut Event, len: usize, overwriting: bool);
13 fn rftrace_backend_get_events() -> *const Event;
14 fn rftrace_backend_get_events_index() -> usize;
15}
16
17pub fn enable() {
19 unsafe { rftrace_backend_enable() }
20}
21
22pub fn disable() {
24 unsafe { rftrace_backend_disable() }
25}
26
27#[derive(Copy, Clone, Debug)]
29pub struct Events {
30 ptr: *mut Event,
31 len: usize,
32 cap: usize,
33}
34
35fn get_events(events: &mut Events) -> (Vec<Event>, usize) {
36 let ptr = unsafe { rftrace_backend_get_events() };
38 println!("{:?}, {:?}", ptr, events);
39 assert!(ptr == events.ptr, "Event buffer pointer mismatch!");
40
41 let eventvec = unsafe { Vec::from_raw_parts(events.ptr, events.len, events.cap) };
42
43 let idx = unsafe { rftrace_backend_get_events_index() };
44 (eventvec, idx)
45}
46
47pub fn init(max_event_count: usize, overwriting: bool) -> &'static mut Events {
54 assert!(
55 max_event_count > MAX_STACK_HEIGHT,
56 "Event buffer has to be larger than maximum stack height!"
57 );
58 let buf = vec![Event::Empty; max_event_count];
59 unsafe {
60 let (ptr, len, cap) = buf.into_raw_parts();
62 rftrace_backend_init(ptr, cap, overwriting);
63 Box::leak(Box::new(Events { ptr, len, cap }))
65 }
66}
67
68pub fn dump_full_uftrace(events: &mut Events, out_dir: &str, binary_name: &str) -> io::Result<()> {
79 let pid = 42;
81 let sid = "00";
82
83 let tids = dump_traces(events, out_dir, false)?;
85
86 if tids.is_empty() {
87 println!("Trace is empty!");
88 return Ok(());
89 }
90
91 println!("Creating fake uftrace data dir at {}..", out_dir);
92 println!(" Creating ./info");
93 let mut info: Vec<u8> = Vec::new();
94
95 info.extend("Ftrace!\x00".as_bytes());
98 info.write_u32::<LittleEndian>(4)
100 .expect("Write interrupted");
101 info.write_u16::<LittleEndian>(40)
103 .expect("Write interrupted");
104 info.push(1);
106 info.push(2);
108 println!(" feats = TASK_SESSION | SYM_REL_ADDR");
110 const TASK_SESSION: u64 = 1 << 1; const SYM_REL_ADDR: u64 = 1 << 5; info.write_u64::<LittleEndian>(TASK_SESSION | SYM_REL_ADDR)
113 .expect("Write interrupted");
114 println!(" info = CMDLINE | TASKINFO");
116 const CMDLINE: u64 = 1 << 3; const TASKINFO: u64 = 1 << 7; info.write_u64::<LittleEndian>(CMDLINE | TASKINFO)
119 .expect("Write interrupted");
120 info.write_u16::<LittleEndian>(0)
122 .expect("Write interrupted");
123 info.write_u16::<LittleEndian>(0)
125 .expect("Write interrupted");
126 info.write_u16::<LittleEndian>(0)
127 .expect("Write interrupted");
128 info.write_u16::<LittleEndian>(0)
129 .expect("Write interrupted");
130 println!(" cmdline = 'fakeuftrace'");
134 writeln!(info, "cmdline:fakeuftrace")?;
135 println!(" tid = {:?}", tids);
137 writeln!(info, "taskinfo:lines=2")?;
138 writeln!(info, "taskinfo:nr_tid={}", tids.len())?;
139 write!(info, "taskinfo:tids={}", tids[0])?;
140 for tid in &tids[1..] {
141 write!(info, ",{}", tid)?;
142 }
143 writeln!(info)?;
144
145 let infofile = format!("{}/info", out_dir);
146 let mut infofile = File::create(infofile)?;
147 infofile.write_all(&info[..])?;
148 drop(infofile);
149
150 println!(" Creating ./task.txt");
151 let taskfile = format!("{}/task.txt", out_dir);
152 let mut taskfile = File::create(taskfile)?;
153 println!(" pid = {}", pid);
154 println!(" sid = {}", sid);
155 println!(" exe = {}", binary_name);
156 writeln!(
157 taskfile,
158 "SESS timestamp=0.0 pid={} sid={} exename=\"{}\"",
159 pid, sid, binary_name
160 )?;
161 for tid in tids {
162 writeln!(taskfile, "TASK timestamp=0.0 tid={} pid={}", tid, pid)?;
163 }
164 drop(taskfile);
165
166 let mapfilename = format!("{}/sid-{}.map", out_dir, sid);
167 let mut mapfile = File::create(mapfilename)?;
168 cfg_if::cfg_if! {
169 if #[cfg(target_os = "linux")] {
170 println!(
173 " Creating (incorrect) ./sid-{}.map by copying /proc/self/maps",
174 sid
175 );
176 let mut procfile = File::open("/proc/self/maps")?;
177 io::copy(&mut procfile, &mut mapfile)?;
178 } else if #[cfg(target_os = "hermit")] {
179 extern "C" {
180 fn sys_image_start_addr() -> usize;
181 }
182
183 let addr = unsafe { sys_image_start_addr() };
184
185 writeln!(mapfile, "{addr:0>12x}-ffffffffffff r-xp 00000000 00:00 0 {binary_name}")?;
186 writeln!(mapfile, "ffffffffffff-ffffffffffff rw-p 00000000 00:00 0 [stack]")?;
187 } else {
188 println!(" Creating ./sid-{sid}.map fake memory map file");
189
190 writeln!(mapfile, "000000000000-ffffffffffff r-xp 00000000 00:00 0 {binary_name}")?;
191 writeln!(mapfile, "ffffffffffff-ffffffffffff rw-p 00000000 00:00 0 [stack]")?;
192 }
193 }
194
195 if cfg!(target_os = "linux") {
196 println!(
197 "\nYou should generate symbols with `nm -n $BINARY > {}/$BINARY.sym`",
198 out_dir
199 );
200 println!(
201 "INFO: Linux mode is NOT fully supported yet! To get symbols working, you have to"
202 );
203 println!(" edit the sid-00.map and merge the section for each binary, so that it only occurs once.");
204 println!(" Needs to contain at least [stack] and the binaries you want symbols of.");
205 } else {
206 println!(
207 "\nYou should generate symbols with `nm -n $BINARY > {}/{}.sym`",
208 out_dir, binary_name
209 );
210 }
211
212 Ok(())
213}
214
215pub fn dump_trace(events: &mut Events, outfile: &str) -> io::Result<()> {
233 dump_traces(events, outfile, true)?;
234 Ok(())
235}
236
237fn dump_traces(events: &mut Events, outpath: &str, singlefile: bool) -> io::Result<Vec<u64>> {
238 disable();
254 println!("Saving traces to disk...!");
255
256 let (events, cidx) = get_events(events);
257 let cidx = cidx % events.len();
258
259 let mut out = Vec::<u8>::with_capacity(16 * events.len());
265
266 let mut tids: Vec<Option<core::num::NonZeroU64>> = Vec::new();
268 for e in events[cidx..].iter().chain(events[..cidx].iter()) {
269 match e {
270 Event::Exit(e) => {
271 if !tids.contains(&e.tid) {
272 tids.push(e.tid);
273 }
274 }
275 Event::Entry(e) => {
276 if !tids.contains(&e.tid) {
277 tids.push(e.tid);
278 }
279 }
280 Event::Empty => {}
281 }
282 }
283
284 for current_tid in &tids {
286 out.clear();
288
289 let tid = current_tid.map_or(0, |tid| tid.get());
290
291 println!(" Parsing TID {:?}...!", tid);
292 for e in events[cidx..].iter().chain(events[..cidx].iter()) {
293 match e {
294 Event::Exit(e) => {
295 if !singlefile && current_tid != &e.tid {
296 continue;
297 };
298 write_event(&mut out, e.time, e.from, 1);
299 }
300 Event::Entry(e) => {
301 if !singlefile && current_tid != &e.tid {
302 continue;
303 };
304 write_event(&mut out, e.time, e.to, 0);
305 }
306 Event::Empty => {
307 continue;
308 }
309 }
310 }
311
312 if !out.is_empty() {
313 let filename = if singlefile {
314 outpath.into()
315 } else {
316 let file = format!("{}.dat", tid);
317 format!("{}/{}", outpath, file)
318 };
319
320 println!(
321 " Writing to disk: {} events, {} bytes ({})",
322 out.len() / 16,
323 out.len(),
324 filename
325 );
326 let mut file = File::create(filename)?;
327 file.write_all(&out[..])?;
328 }
329 }
330 println!(" Parsed all events!");
331
332 Ok(tids
334 .iter()
335 .map(|tid| tid.map_or(0, |tid| tid.get()))
336 .collect())
337}
338
339#[allow(clippy::identity_op)]
340#[allow(clippy::erasing_op)]
341fn write_event(out: &mut Vec<u8>, time: u64, addr: *const usize, kind: u64) {
342 out.write_u64::<LittleEndian>(time)
343 .expect("Write interrupted");
344
345 let mut merged: u64 = 0;
346 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)
352 .expect("Write interrupted");
353}