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