#![deny(missing_docs)]
#![no_std]
#![allow(clippy::new_without_default)]
extern crate alloc;
mod basic_macro;
mod point;
pub mod ptr;
mod trace_pipe;
use alloc::{
boxed::Box,
collections::BTreeMap,
format,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use core::{
ops::{Deref, DerefMut},
sync::atomic::AtomicU32,
};
pub use paste;
pub use point::{
CommonTracePointMeta, ExtTracePoint, RawTraceEventFunc, TraceCallbackType, TraceDefaultFunc,
TraceEntry, TraceEventFunc, TracePoint,
};
use static_keys::code_manipulate::CodeManipulator;
pub use tp_lexer;
use tp_lexer::compile_with_schema;
pub use trace_pipe::{
TraceCmdLineCache, TraceCmdLineCacheSnapshot, TraceEntryParser, TracePipeOps, TracePipeRaw,
TracePipeRecord, TracePipeSnapshot,
};
pub trait KernelTraceOps: Send + Sync + 'static {
fn current_pid() -> u32;
fn trace_pipe_push_raw_record(buf: &[u8]);
fn trace_cmdline_push(pid: u32);
fn write_kernel_text(addr: *mut core::ffi::c_void, data: &[u8]);
fn read_tracepoint_state<R>(id: u32, f: impl FnOnce(&ExtTracePoint<Self>) -> R) -> R;
fn write_tracepoint_state<R>(id: u32, f: impl FnOnce(&mut ExtTracePoint<Self>) -> R) -> R;
}
pub struct KernelCodeManipulator<T> {
_marker: core::marker::PhantomData<T>,
}
impl<T: KernelTraceOps> CodeManipulator for KernelCodeManipulator<T> {
unsafe fn write_code<const L: usize>(addr: *mut core::ffi::c_void, data: &[u8; L]) {
T::write_kernel_text(addr, data);
}
}
#[derive(Debug)]
pub struct TracePointMap<K: KernelTraceOps>(BTreeMap<u32, &'static TracePoint<K>>);
impl<K: KernelTraceOps> TracePointMap<K> {
pub const fn new() -> Self {
Self(BTreeMap::new())
}
}
impl<K: KernelTraceOps> Deref for TracePointMap<K> {
type Target = BTreeMap<u32, &'static TracePoint<K>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<K: KernelTraceOps> DerefMut for TracePointMap<K> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Debug, Clone)]
pub struct TracePointFormatFile<K: KernelTraceOps> {
tracepoint: &'static TracePoint<K>,
}
impl<K: KernelTraceOps> TracePointFormatFile<K> {
pub fn new(tracepoint: &'static TracePoint<K>) -> Self {
Self { tracepoint }
}
pub fn read(&self) -> String {
self.tracepoint.print_fmt()
}
}
#[derive(Debug, Clone)]
pub struct TracePointEnableFile;
impl TracePointEnableFile {
pub fn new() -> Self {
Self {}
}
pub fn read<K: KernelTraceOps>(&self, tracepoint: &'static TracePoint<K>) -> &'static str {
if tracepoint.key_is_enabled() {
"1\n"
} else {
"0\n"
}
}
pub fn write<K: KernelTraceOps>(&self, ext_tracepoint: &mut ExtTracePoint<K>, enable: char) {
match enable {
'1' => {
let default_callback = ext_tracepoint.default_callback();
ext_tracepoint.register(TraceCallbackType::Default(default_callback));
}
'0' => {
let default_callback = ext_tracepoint.default_callback();
ext_tracepoint.unregister(TraceCallbackType::Default(default_callback));
}
_ => {
log::warn!("Invalid value for tracepoint enable: {enable}");
}
}
}
}
#[derive(Debug, Clone)]
pub struct TracePointIdFile<K: KernelTraceOps> {
tracepoint: &'static TracePoint<K>,
}
impl<K: KernelTraceOps> TracePointIdFile<K> {
pub fn new(tracepoint: &'static TracePoint<K>) -> Self {
Self { tracepoint }
}
pub fn read(&self) -> String {
format!("{}\n", self.tracepoint.id())
}
}
#[derive(Debug)]
pub struct TraceFilterFile {
filter_expr: Option<String>,
pre_error: Option<String>,
}
impl TraceFilterFile {
pub fn new() -> Self {
Self {
filter_expr: None,
pre_error: None,
}
}
pub fn read(&self) -> String {
if let Some(err) = self.pre_error.as_ref() {
return err.clone();
}
if let Some(filter) = self.filter_expr.as_ref() {
filter.clone()
} else {
"none\n".to_string()
}
}
pub fn write<K: KernelTraceOps>(
&mut self,
ext_tracepoint: &mut ExtTracePoint<K>,
filter: &str,
) -> Result<(), &'static str> {
if filter.as_bytes()[0] == b'0' {
self.filter_expr = None;
self.pre_error = None;
ext_tracepoint.set_compiled_expr(None);
Ok(())
} else {
let schema = ext_tracepoint.schema();
let res = compile_with_schema(filter, *schema);
match res {
Ok(compiled_expr) => {
self.filter_expr = Some(filter.to_string());
self.pre_error = None;
ext_tracepoint.set_compiled_expr(Some(compiled_expr));
Ok(())
}
Err(mut e) => {
e.message.push('\n');
self.pre_error = Some(e.message);
self.filter_expr = None;
ext_tracepoint.set_compiled_expr(None);
Err("compile error")
}
}
}
}
}
unsafe extern "C" {
fn __start_tracepoint();
fn __stop_tracepoint();
}
pub fn global_init_events<K: KernelTraceOps>()
-> Result<(TracePointMap<K>, Vec<ExtTracePoint<K>>), &'static str> {
static TRACE_POINT_ID: AtomicU32 = AtomicU32::new(0);
let tracepoint_data_start = __start_tracepoint as *mut CommonTracePointMeta<K>;
let tracepoint_data_end = __stop_tracepoint as *mut CommonTracePointMeta<K>;
log::info!(
"tracepoint_data_start: {:#x}, tracepoint_data_end: {:#x}",
tracepoint_data_start as usize,
tracepoint_data_end as usize
);
let tracepoint_data_len = (tracepoint_data_end as usize - tracepoint_data_start as usize)
/ size_of::<CommonTracePointMeta<K>>();
let tracepoint_data =
unsafe { core::slice::from_raw_parts_mut(tracepoint_data_start, tracepoint_data_len) };
tracepoint_data.sort_by(|a, b| {
a.trace_point
.name()
.cmp(b.trace_point.name())
.then(a.trace_point.system().cmp(b.trace_point.system()))
});
log::info!("tracepoint_data_len: {tracepoint_data_len}");
let mut tp_map = TracePointMap::new();
let mut ext_tps = Vec::with_capacity(tracepoint_data_len);
for tracepoint_meta in tracepoint_data.iter() {
let tracepoint = tracepoint_meta.trace_point;
let id = TRACE_POINT_ID.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
tracepoint.set_id(id);
let default_callback = Arc::new(TraceDefaultFunc {
func: tracepoint_meta.print_func,
data: Box::new(()),
});
let ext_tracepoint = ExtTracePoint::new(tracepoint, default_callback);
log::info!(
"tracepoint registered: {}:{}",
tracepoint.system(),
tracepoint.name(),
);
tp_map.insert(id, tracepoint);
ext_tps.push(ext_tracepoint);
}
Ok((tp_map, ext_tps))
}