use alloc::{format, string::String, vec::Vec};
use lock_api::RawMutex;
use crate::{KernelTraceOps, TraceEntry, TracePointMap};
pub trait TracePipeOps {
fn peek(&self) -> Option<&Vec<u8>>;
fn pop(&mut self) -> Option<Vec<u8>>;
fn is_empty(&self) -> bool;
}
pub struct TracePipeRaw {
max_record: usize,
event_buf: Vec<Vec<u8>>,
}
impl TracePipeRaw {
pub const fn new(max_record: usize) -> Self {
Self {
max_record,
event_buf: Vec::new(),
}
}
pub fn set_max_record(&mut self, max_record: usize) {
self.max_record = max_record;
if self.event_buf.len() > max_record {
self.event_buf.truncate(max_record); }
}
pub fn push_event(&mut self, event: Vec<u8>) {
if self.event_buf.len() >= self.max_record {
self.event_buf.remove(0); }
self.event_buf.push(event);
}
pub fn event_count(&self) -> usize {
self.event_buf.len()
}
pub fn clear(&mut self) {
self.event_buf.clear();
}
pub fn snapshot(&self) -> TracePipeSnapshot {
TracePipeSnapshot::new(self.event_buf.clone())
}
pub fn max_record(&self) -> usize {
self.max_record
}
}
impl TracePipeOps for TracePipeRaw {
fn peek(&self) -> Option<&Vec<u8>> {
self.event_buf.first()
}
fn pop(&mut self) -> Option<Vec<u8>> {
if self.event_buf.is_empty() {
None
} else {
Some(self.event_buf.remove(0))
}
}
fn is_empty(&self) -> bool {
self.event_buf.is_empty()
}
}
#[derive(Debug)]
pub struct TracePipeSnapshot(Vec<Vec<u8>>);
impl TracePipeSnapshot {
pub fn new(event_buf: Vec<Vec<u8>>) -> Self {
Self(event_buf)
}
pub fn default_fmt_str(&self) -> String {
let show = "#
#
# _-----=> irqs-off/BH-disabled
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / _-=> migrate-disable
# |||| / delay
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
";
format!(
"# tracer: nop\n#\n# entries-in-buffer/entries-written: {}/{} #P:32\n{}",
self.0.len(),
self.0.len(),
show
)
}
}
impl TracePipeOps for TracePipeSnapshot {
fn peek(&self) -> Option<&Vec<u8>> {
self.0.first()
}
fn pop(&mut self) -> Option<Vec<u8>> {
if self.0.is_empty() {
None
} else {
Some(self.0.remove(0))
}
}
fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
pub struct TraceCmdLineCache {
cmdline: Vec<(u32, [u8; 16])>,
max_record: usize,
}
impl TraceCmdLineCache {
pub const fn new(max_record: usize) -> Self {
Self {
cmdline: Vec::new(),
max_record,
}
}
pub fn insert(&mut self, id: u32, cmdline: String) {
if self.cmdline.len() >= self.max_record {
self.cmdline.remove(0);
}
let mut cmdline_bytes = [0u8; 16];
if cmdline.len() > 16 {
cmdline_bytes.copy_from_slice(&cmdline.as_bytes()[..16]);
} else {
cmdline_bytes[..cmdline.len()].copy_from_slice(cmdline.as_bytes());
}
self.cmdline.push((id, cmdline_bytes));
}
pub fn get(&self, id: u32) -> Option<&str> {
self.cmdline.iter().find_map(|(key, value)| {
if *key == id {
Some(core::str::from_utf8(value).unwrap().trim_end_matches('\0'))
} else {
None
}
})
}
pub fn set_max_record(&mut self, max_len: usize) {
self.max_record = max_len;
if self.cmdline.len() > max_len {
self.cmdline.truncate(max_len); }
}
pub fn max_record(&self) -> usize {
self.max_record
}
pub fn snapshot(&self) -> TraceCmdLineCacheSnapshot {
TraceCmdLineCacheSnapshot::new(self.cmdline.clone())
}
}
#[derive(Debug)]
pub struct TraceCmdLineCacheSnapshot(Vec<(u32, [u8; 16])>);
impl TraceCmdLineCacheSnapshot {
pub fn new(cmdline: Vec<(u32, [u8; 16])>) -> Self {
Self(cmdline)
}
pub fn peek(&self) -> Option<&(u32, [u8; 16])> {
self.0.first()
}
pub fn pop(&mut self) -> Option<(u32, [u8; 16])> {
if self.0.is_empty() {
None
} else {
Some(self.0.remove(0))
}
}
}
pub struct TraceEntryParser;
impl TraceEntryParser {
pub fn parse<K: KernelTraceOps, L: RawMutex + 'static>(
tracepoint_map: &TracePointMap<L, K>,
cmdline_cache: &TraceCmdLineCache,
entry: &[u8],
) -> String {
let trace_entry = unsafe { &*(entry.as_ptr() as *const TraceEntry) };
let id = trace_entry.type_ as u32;
let tracepoint = tracepoint_map.get(&id).expect("TracePoint not found");
let fmt_func = tracepoint.fmt_func();
let offset = core::mem::size_of::<TraceEntry>();
let str = fmt_func(&entry[offset..]);
let time = K::time_now();
let cpu_id = K::cpu_id();
let pid = trace_entry.pid;
let pname = cmdline_cache.get(trace_entry.pid as u32).unwrap_or("<...>");
let secs = time / 1_000_000_000;
let usec_rem = time % 1_000_000_000 / 1000;
format!(
"{:>16}-{:<7} [{:03}] {} {:5}.{:06}: {}({})\n",
pname,
pid,
cpu_id,
trace_entry.trace_print_lat_fmt(),
secs,
usec_rem,
tracepoint.name(),
str
)
}
}