use alloc::{boxed::Box, collections::BTreeMap, format, string::String};
use core::{
any::Any,
sync::atomic::{AtomicBool, AtomicU32},
};
use lock_api::{Mutex, RawMutex};
use static_keys::RawStaticFalseKey;
use tp_lexer::{Compiled, Schema};
use crate::{KernelCodeManipulator, KernelTraceOps};
#[derive(Debug)]
#[repr(C)]
pub struct TraceEntry {
pub common_type: u16,
pub common_flags: u8,
pub common_preempt_count: u8,
pub common_pid: i32,
}
impl TraceEntry {
pub fn trace_print_lat_fmt(&self) -> String {
let irqs_off = '.';
let resched = '.';
let hardsoft_irq = '.';
let mut preempt_low = '.';
if self.common_preempt_count & 0xf != 0 {
preempt_low = ((b'0') + (self.common_preempt_count & 0xf)) as char;
}
let mut preempt_high = '.';
if self.common_preempt_count >> 4 != 0 {
preempt_high = ((b'0') + (self.common_preempt_count >> 4)) as char;
}
format!("{irqs_off}{resched}{hardsoft_irq}{preempt_low}{preempt_high}")
}
}
pub struct TracePoint<L: RawMutex + 'static, K: KernelTraceOps + 'static> {
name: &'static str,
system: &'static str,
key: &'static RawStaticFalseKey<KernelCodeManipulator<K>>,
event_status: AtomicBool,
id: AtomicU32,
default_callbacks: Mutex<L, BTreeMap<usize, TracePointFunc>>,
event_callbacks: Mutex<L, BTreeMap<usize, Box<dyn TracePointCallBackFunc>>>,
raw_event_callbacks: Mutex<L, BTreeMap<usize, Box<dyn RawTracePointCallBackFunc>>>,
trace_entry_fmt_func: fn(&[u8]) -> String,
trace_print_func: fn() -> String,
schema: Schema,
compiled_expr: Mutex<L, Option<Compiled>>,
flags: u8,
}
impl<L: RawMutex + 'static, K: KernelTraceOps + 'static> core::fmt::Debug for TracePoint<L, K> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TracePoint")
.field("name", &self.name)
.field("system", &self.system)
.field("id", &self.id())
.field("flags", &self.flags)
.finish()
}
}
#[derive(Debug)]
#[repr(C)]
pub struct CommonTracePointMeta<L: RawMutex + 'static, K: KernelTraceOps + 'static> {
pub trace_point: &'static TracePoint<L, K>,
pub print_func: fn(),
}
pub trait TracePointCallBackFunc: Send + Sync {
fn call(&self, entry: &[u8]);
}
pub trait RawTracePointCallBackFunc: Send + Sync {
fn call(&self, args: &[u64]);
}
#[derive(Debug)]
pub struct TracePointFunc {
pub func: fn(),
pub data: Box<dyn Any + Send + Sync>,
}
impl<L: RawMutex + 'static, K: KernelTraceOps + 'static> TracePoint<L, K> {
pub const fn new(
key: &'static RawStaticFalseKey<KernelCodeManipulator<K>>,
name: &'static str,
system: &'static str,
fmt_func: fn(&[u8]) -> String,
trace_print_func: fn() -> String,
schema: Schema,
) -> Self {
Self {
name,
system,
key,
event_status: AtomicBool::new(false),
id: AtomicU32::new(0),
flags: 0,
trace_entry_fmt_func: fmt_func,
trace_print_func,
default_callbacks: Mutex::new(BTreeMap::new()),
event_callbacks: Mutex::new(BTreeMap::new()),
raw_event_callbacks: Mutex::new(BTreeMap::new()),
schema,
compiled_expr: Mutex::new(None),
}
}
pub fn schema(&self) -> &Schema {
&self.schema
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn system(&self) -> &'static str {
self.system
}
pub(crate) fn set_id(&self, id: u32) {
self.id.store(id, core::sync::atomic::Ordering::Relaxed);
}
pub fn id(&self) -> u32 {
self.id.load(core::sync::atomic::Ordering::Relaxed)
}
pub fn flags(&self) -> u8 {
self.flags
}
pub fn set_compiled_expr(&self, compiled: Option<Compiled>) {
let mut guard = self.compiled_expr.lock();
*guard = compiled;
}
pub fn get_compiled_expr(&self) -> Option<Compiled> {
let guard = self.compiled_expr.lock();
guard.clone()
}
pub(crate) fn fmt_func(&self) -> fn(&[u8]) -> String {
self.trace_entry_fmt_func
}
pub fn print_fmt(&self) -> String {
let post_str = (self.trace_print_func)();
format!("name: {}\nID: {}\n{}\n", self.name(), self.id(), post_str)
}
pub fn register(&self, func: fn(), data: Box<dyn Any + Sync + Send>) {
let trace_point_func = TracePointFunc { func, data };
let ptr = func as usize;
self.default_callbacks
.lock()
.entry(ptr)
.or_insert(trace_point_func);
}
pub fn unregister(&self, func: fn()) {
let func_ptr = func as usize;
self.default_callbacks.lock().remove(&func_ptr);
}
pub fn callback_list(&self, f: &dyn Fn(&TracePointFunc)) {
let callback = self.default_callbacks.lock();
for trace_func in callback.values() {
f(trace_func);
}
}
pub fn register_event_callback(
&self,
callback_id: usize,
callback: Box<dyn TracePointCallBackFunc>,
) {
self.event_callbacks
.lock()
.entry(callback_id)
.or_insert(callback);
}
pub fn unregister_event_callback(&self, callback_id: usize) {
self.event_callbacks.lock().remove(&callback_id);
}
pub fn event_callback_list(&self, f: &dyn Fn(&Box<dyn TracePointCallBackFunc>)) {
let raw_callback = self.event_callbacks.lock();
for callback in raw_callback.values() {
f(callback);
}
}
pub fn register_raw_event_callback(
&self,
callback_id: usize,
callback: Box<dyn RawTracePointCallBackFunc>,
) {
self.raw_event_callbacks
.lock()
.entry(callback_id)
.or_insert(callback);
}
pub fn unregister_raw_event_callback(&self, callback_id: usize) {
self.raw_event_callbacks.lock().remove(&callback_id);
}
pub fn raw_event_callback_list(&self, f: &dyn Fn(&Box<dyn RawTracePointCallBackFunc>)) {
let raw_callback = self.raw_event_callbacks.lock();
for callback in raw_callback.values() {
f(callback);
}
}
pub fn enable_default(&self) {
unsafe {
self.key.enable();
}
}
pub fn disable_default(&self) {
unsafe {
self.key.disable();
}
}
pub fn default_is_enabled(&self) -> bool {
self.key.is_enabled()
}
pub fn enable_event(&self) {
self.event_status
.store(true, core::sync::atomic::Ordering::Relaxed);
}
pub fn disable_event(&self) {
self.event_status
.store(false, core::sync::atomic::Ordering::Relaxed);
}
pub fn event_is_enabled(&self) -> bool {
self.event_status
.load(core::sync::atomic::Ordering::Relaxed)
}
}