use alloc::{boxed::Box, format, string::String, sync::Arc, vec::Vec};
use core::{
any::Any,
ops::Deref,
sync::atomic::{AtomicBool, AtomicU32, Ordering},
};
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}")
}
}
#[derive(Debug)]
#[repr(C)]
pub struct CommonTracePointMeta<K: KernelTraceOps> {
pub trace_point: &'static TracePoint<K>,
pub print_func: fn(),
}
pub struct TraceEventFunc {
func: Box<dyn Fn(&[u8], &(dyn Any + Send + Sync)) + Send + Sync>,
data: Box<dyn Any + Send + Sync>,
perf_enable: AtomicBool,
}
impl TraceEventFunc {
pub fn new(
func: Box<dyn Fn(&[u8], &(dyn Any + Send + Sync)) + Send + Sync>,
data: Box<dyn Any + Send + Sync>,
) -> Self {
Self {
func,
data,
perf_enable: AtomicBool::new(false),
}
}
pub fn call(&self, entry: &[u8]) {
(self.func)(entry, &self.data);
}
pub fn set_perf_enable(&self, enable: bool) {
self.perf_enable.store(enable, Ordering::Relaxed);
}
pub fn perf_enabled(&self) -> bool {
self.perf_enable.load(Ordering::Relaxed)
}
}
pub struct RawTraceEventFunc {
func: Box<dyn Fn(&[u64], &(dyn Any + Send + Sync)) + Send + Sync>,
data: Box<dyn Any + Send + Sync>,
}
impl RawTraceEventFunc {
pub fn new(
func: Box<dyn Fn(&[u64], &(dyn Any + Send + Sync)) + Send + Sync>,
data: Box<dyn Any + Send + Sync>,
) -> Self {
Self { func, data }
}
pub fn call(&self, args: &[u64]) {
(self.func)(args, &self.data);
}
}
#[derive(Debug)]
pub struct TraceDefaultFunc {
pub func: fn(),
pub data: Box<dyn Any + Send + Sync>,
}
#[derive(Clone)]
pub enum TraceCallbackType {
Default(Arc<TraceDefaultFunc>),
Event(Arc<TraceEventFunc>),
RawEvent(Arc<RawTraceEventFunc>),
}
impl PartialEq for TraceCallbackType {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(TraceCallbackType::Default(func1), TraceCallbackType::Default(func2)) => {
Arc::ptr_eq(func1, func2)
}
(TraceCallbackType::Event(func1), TraceCallbackType::Event(func2)) => {
Arc::ptr_eq(func1, func2)
}
(TraceCallbackType::RawEvent(func1), TraceCallbackType::RawEvent(func2)) => {
Arc::ptr_eq(func1, func2)
}
_ => false,
}
}
}
pub struct TracePoint<K: KernelTraceOps> {
name: &'static str,
system: &'static str,
key: &'static RawStaticFalseKey<KernelCodeManipulator<K>>,
id: AtomicU32,
trace_entry_fmt_func: fn(&[u8]) -> String,
trace_print_func: fn() -> String,
schema: Schema,
flags: u8,
}
impl<K: KernelTraceOps> core::fmt::Debug for TracePoint<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()
}
}
pub struct ExtTracePoint<K: KernelTraceOps> {
tracepoint: &'static TracePoint<K>,
callbacks: Vec<TraceCallbackType>,
compiled_expr: Option<Compiled>,
default_callback: Arc<TraceDefaultFunc>,
}
impl<K: KernelTraceOps> core::fmt::Debug for ExtTracePoint<K> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ExtTracePoint")
.field("tracepoint", &self.tracepoint)
.finish()
}
}
impl<K: KernelTraceOps> Deref for ExtTracePoint<K> {
type Target = TracePoint<K>;
fn deref(&self) -> &Self::Target {
self.tracepoint
}
}
impl<K: KernelTraceOps> ExtTracePoint<K> {
pub const fn new(
tracepoint: &'static TracePoint<K>,
default_callback: Arc<TraceDefaultFunc>,
) -> Self {
Self {
tracepoint,
callbacks: Vec::new(),
default_callback,
compiled_expr: None,
}
}
pub fn default_callback(&self) -> Arc<TraceDefaultFunc> {
self.default_callback.clone()
}
pub const fn trace_point(&self) -> &'static TracePoint<K> {
self.tracepoint
}
pub fn set_compiled_expr(&mut self, compiled: Option<Compiled>) {
self.compiled_expr = compiled;
}
pub fn get_compiled_expr(&self) -> Option<&Compiled> {
self.compiled_expr.as_ref()
}
pub fn register(&mut self, callback: TraceCallbackType) {
if !self.callbacks.iter().any(|f| f == &callback) {
self.callbacks.push(callback);
}
if !self.callbacks.is_empty() {
self.tracepoint.enable_key();
}
}
pub fn unregister(&mut self, callback: TraceCallbackType) {
self.callbacks.retain(|f| f != &callback);
if self.callbacks.is_empty() {
self.tracepoint.disable_key();
}
}
pub fn callback_list(&self) -> impl Iterator<Item = &TraceCallbackType> {
self.callbacks.iter()
}
}
impl<K: KernelTraceOps> TracePoint<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,
id: AtomicU32::new(0),
flags: 0,
trace_entry_fmt_func: fmt_func,
trace_print_func,
schema,
}
}
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, Ordering::Relaxed);
}
pub fn id(&self) -> u32 {
self.id.load(Ordering::Relaxed)
}
pub fn flags(&self) -> u8 {
self.flags
}
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)
}
fn enable_key(&self) {
unsafe {
self.key.enable();
}
}
fn disable_key(&self) {
unsafe {
self.key.disable();
}
}
pub fn key_is_enabled(&self) -> bool {
self.key.is_enabled()
}
}