use alloc::vec::Vec;
use super::output::PerfRingOutput;
use crate::task::{TgidNumber, TidNumber};
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(crate) ring: PerfRingOutput,
pub sample_type: u64,
pub sample_id_all: bool,
pub id: u64,
pub pid: TgidNumber,
pub tid: TidNumber,
}
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().is_multiple_of(8) {
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.get());
push_u32(b, t.tid.get());
}
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().is_multiple_of(8) {
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());
unsafe { super::sampling::ring_write_process(&t.ring, &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.get());
push_u32(&mut b, t.tid.get());
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: TgidNumber,
ppid: Option<TgidNumber>,
tid: TidNumber,
ptid: Option<TidNumber>,
) {
let mut b = Vec::with_capacity(64);
b.extend_from_slice(&[0u8; 8]); push_u32(&mut b, pid.get());
push_u32(&mut b, ppid.map_or(0, TgidNumber::get));
push_u32(&mut b, tid.get());
push_u32(&mut b, ptid.map_or(0, TidNumber::get));
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: TgidNumber,
ppid: TgidNumber,
tid: TidNumber,
ptid: TidNumber,
) {
emit_task(t, PERF_RECORD_FORK, pid, Some(ppid), tid, Some(ptid));
}
pub fn emit_exit(
t: &SidebandTarget,
pid: TgidNumber,
ppid: Option<TgidNumber>,
tid: TidNumber,
ptid: Option<TidNumber>,
) {
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.get());
push_u32(&mut b, t.tid.get());
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);
}