use alloc::vec::Vec;
const PERF_RECORD_COMM: u32 = 3;
const PERF_RECORD_EXIT: u32 = 4;
const PERF_RECORD_FORK: u32 = 7;
const PERF_RECORD_MMAP2: u32 = 10;
const PERF_RECORD_MISC_USER: u16 = 2;
const PERF_RECORD_MISC_COMM_EXEC: u16 = 1 << 13;
const PERF_SAMPLE_TID: u64 = 1 << 1;
const PERF_SAMPLE_TIME: u64 = 1 << 2;
const PERF_SAMPLE_ID: u64 = 1 << 6;
const PERF_SAMPLE_CPU: u64 = 1 << 7;
const PERF_SAMPLE_STREAM_ID: u64 = 1 << 9;
const PERF_SAMPLE_IDENTIFIER: u64 = 1 << 16;
const COMM_MAX: usize = 15;
pub struct SidebandTarget {
pub ring_vaddr: usize,
pub ring_len: usize,
pub sample_type: u64,
pub sample_id_all: bool,
pub id: u64,
pub pid: u32,
pub tid: u32,
}
pub struct Mmap2Info {
pub addr: u64,
pub len: u64,
pub pgoff: u64,
pub maj: u32,
pub min: u32,
pub ino: u64,
pub prot: u32,
pub flags: u32,
pub filename: alloc::string::String,
}
#[inline]
fn push_u32(b: &mut Vec<u8>, v: u32) {
b.extend_from_slice(&v.to_ne_bytes());
}
#[inline]
fn push_u64(b: &mut Vec<u8>, v: u64) {
b.extend_from_slice(&v.to_ne_bytes());
}
fn push_cstr_padded(b: &mut Vec<u8>, s: &[u8]) {
b.extend_from_slice(s);
b.push(0);
while b.len() % 8 != 0 {
b.push(0);
}
}
fn push_trailer(b: &mut Vec<u8>, t: &SidebandTarget) {
if !t.sample_id_all {
return;
}
let st = t.sample_type;
if st & PERF_SAMPLE_TID != 0 {
push_u32(b, t.pid);
push_u32(b, t.tid);
}
if st & PERF_SAMPLE_TIME != 0 {
push_u64(b, ax_runtime::hal::time::monotonic_time_nanos());
}
if st & PERF_SAMPLE_ID != 0 {
push_u64(b, t.id);
}
if st & PERF_SAMPLE_STREAM_ID != 0 {
push_u64(b, 0);
}
if st & PERF_SAMPLE_CPU != 0 {
push_u32(b, ax_hal::percpu::this_cpu_id() as u32);
push_u32(b, 0);
}
if st & PERF_SAMPLE_IDENTIFIER != 0 {
push_u64(b, t.id);
}
}
fn finish_and_write(mut b: Vec<u8>, t: &SidebandTarget, type_: u32, misc: u16) {
while b.len() % 8 != 0 {
b.push(0);
}
let size = b.len() as u16;
b[0..4].copy_from_slice(&type_.to_ne_bytes());
b[4..6].copy_from_slice(&misc.to_ne_bytes());
b[6..8].copy_from_slice(&size.to_ne_bytes());
if t.ring_vaddr == 0 {
return;
}
unsafe { super::sampling::ring_write_process(t.ring_vaddr, t.ring_len, &b) };
}
pub fn emit_comm(t: &SidebandTarget, comm: &str, exec: bool) {
let mut b = Vec::with_capacity(64);
b.extend_from_slice(&[0u8; 8]); push_u32(&mut b, t.pid);
push_u32(&mut b, t.tid);
let name = comm.as_bytes();
push_cstr_padded(&mut b, &name[..name.len().min(COMM_MAX)]);
push_trailer(&mut b, t);
let misc = PERF_RECORD_MISC_USER | if exec { PERF_RECORD_MISC_COMM_EXEC } else { 0 };
finish_and_write(b, t, PERF_RECORD_COMM, misc);
}
fn emit_task(t: &SidebandTarget, type_: u32, pid: u32, ppid: u32, tid: u32, ptid: u32) {
let mut b = Vec::with_capacity(64);
b.extend_from_slice(&[0u8; 8]); push_u32(&mut b, pid);
push_u32(&mut b, ppid);
push_u32(&mut b, tid);
push_u32(&mut b, ptid);
push_u64(&mut b, ax_runtime::hal::time::monotonic_time_nanos());
push_trailer(&mut b, t);
finish_and_write(b, t, type_, 0);
}
pub fn emit_fork(t: &SidebandTarget, pid: u32, ppid: u32, tid: u32, ptid: u32) {
emit_task(t, PERF_RECORD_FORK, pid, ppid, tid, ptid);
}
pub fn emit_exit(t: &SidebandTarget, pid: u32, ppid: u32, tid: u32, ptid: u32) {
emit_task(t, PERF_RECORD_EXIT, pid, ppid, tid, ptid);
}
pub fn emit_mmap2(t: &SidebandTarget, m: &Mmap2Info) {
let mut b = Vec::with_capacity(128);
b.extend_from_slice(&[0u8; 8]); push_u32(&mut b, t.pid);
push_u32(&mut b, t.tid);
push_u64(&mut b, m.addr);
push_u64(&mut b, m.len);
push_u64(&mut b, m.pgoff);
push_u32(&mut b, m.maj);
push_u32(&mut b, m.min);
push_u64(&mut b, m.ino);
push_u64(&mut b, 0); push_u32(&mut b, m.prot);
push_u32(&mut b, m.flags);
push_cstr_padded(&mut b, m.filename.as_bytes());
push_trailer(&mut b, t);
finish_and_write(b, t, PERF_RECORD_MMAP2, PERF_RECORD_MISC_USER);
}