#![deny(missing_docs)]
#![no_std]
#![allow(clippy::new_without_default)]
extern crate alloc;
mod basic_macro;
mod point;
mod trace_pipe;
use alloc::{
boxed::Box,
collections::BTreeMap,
format,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use core::{
ops::{Deref, DerefMut},
sync::atomic::AtomicUsize,
};
use static_keys::code_manipulate::CodeManipulator;
use lock_api::{Mutex, MutexGuard, RawMutex};
pub use paste::paste;
pub use point::{
CommonTracePointMeta, TraceEntry, TracePoint, TracePointCallBackFunc, TracePointFunc,
};
pub use trace_pipe::{
TraceCmdLineCache, TraceCmdLineCacheSnapshot, TraceEntryParser, TracePipeOps, TracePipeRaw,
TracePipeSnapshot,
};
pub trait KernelTraceOps {
fn time_now() -> u64;
fn cpu_id() -> u32;
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]);
}
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]) {
log::debug!("Modifying kernel code at address: {:p}", addr);
T::write_kernel_text(addr, data);
}
}
#[derive(Debug)]
pub struct TracePointMap<L: RawMutex + 'static, K: KernelTraceOps + 'static>(
BTreeMap<u32, &'static TracePoint<L, K>>,
);
impl<L: RawMutex + 'static, K: KernelTraceOps + 'static> TracePointMap<L, K> {
fn new() -> Self {
Self(BTreeMap::new())
}
}
impl<L: RawMutex + 'static, K: KernelTraceOps + 'static> Deref for TracePointMap<L, K> {
type Target = BTreeMap<u32, &'static TracePoint<L, K>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<L: RawMutex + 'static, K: KernelTraceOps + 'static> DerefMut for TracePointMap<L, K> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Debug)]
pub struct TracingEventsManager<L: RawMutex + 'static, K: KernelTraceOps + 'static> {
subsystems: Mutex<L, BTreeMap<String, Arc<EventsSubsystem<L, K>>>>,
map: Mutex<L, TracePointMap<L, K>>,
}
impl<L: RawMutex + 'static, K: KernelTraceOps + 'static> TracingEventsManager<L, K> {
fn new(map: TracePointMap<L, K>) -> Self {
Self {
subsystems: Mutex::new(BTreeMap::new()),
map: Mutex::new(map),
}
}
pub fn tracepoint_map(&self) -> MutexGuard<'_, L, TracePointMap<L, K>> {
self.map.lock()
}
fn create_subsystem(&self, subsystem_name: &str) -> Arc<EventsSubsystem<L, K>> {
if self.subsystems.lock().contains_key(subsystem_name) {
return self
.get_subsystem(subsystem_name)
.expect("Subsystem should exist");
}
let subsystem = Arc::new(EventsSubsystem::new());
self.subsystems
.lock()
.insert(subsystem_name.to_string(), subsystem.clone());
subsystem
}
pub fn get_subsystem(&self, subsystem_name: &str) -> Option<Arc<EventsSubsystem<L, K>>> {
self.subsystems.lock().get(subsystem_name).cloned()
}
pub fn remove_subsystem(&self, subsystem_name: &str) -> Option<Arc<EventsSubsystem<L, K>>> {
self.subsystems.lock().remove(subsystem_name)
}
pub fn subsystem_names(&self) -> Vec<String> {
self.subsystems
.lock()
.keys()
.cloned()
.collect::<Vec<String>>()
}
}
#[derive(Debug)]
pub struct EventsSubsystem<L: RawMutex + 'static, K: KernelTraceOps + 'static> {
events: Mutex<L, BTreeMap<String, Arc<EventInfo<L, K>>>>,
}
impl<L: RawMutex + 'static, K: KernelTraceOps + 'static> EventsSubsystem<L, K> {
fn new() -> Self {
Self {
events: Mutex::new(BTreeMap::new()),
}
}
fn create_event(&self, event_name: &str, event_info: EventInfo<L, K>) {
self.events
.lock()
.insert(event_name.to_string(), Arc::new(event_info));
}
pub fn get_event(&self, event_name: &str) -> Option<Arc<EventInfo<L, K>>> {
self.events.lock().get(event_name).cloned()
}
pub fn event_names(&self) -> Vec<String> {
self.events.lock().keys().cloned().collect::<Vec<String>>()
}
}
#[derive(Debug)]
pub struct EventInfo<L: RawMutex + 'static, K: KernelTraceOps + 'static> {
enable: TracePointEnableFile<L, K>,
tracepoint: &'static TracePoint<L, K>,
format: TracePointFormatFile<L, K>,
id: TracePointIdFile<L, K>,
}
impl<L: RawMutex + 'static, K: KernelTraceOps + 'static> EventInfo<L, K> {
fn new(tracepoint: &'static TracePoint<L, K>) -> Self {
let enable = TracePointEnableFile::new(tracepoint);
let format = TracePointFormatFile::new(tracepoint);
let id = TracePointIdFile::new(tracepoint);
Self {
enable,
tracepoint,
format,
id,
}
}
pub fn tracepoint(&self) -> &'static TracePoint<L, K> {
self.tracepoint
}
pub fn enable_file(&self) -> &TracePointEnableFile<L, K> {
&self.enable
}
pub fn format_file(&self) -> &TracePointFormatFile<L, K> {
&self.format
}
pub fn id_file(&self) -> &TracePointIdFile<L, K> {
&self.id
}
}
#[derive(Debug, Clone)]
pub struct TracePointFormatFile<L: RawMutex + 'static, K: KernelTraceOps + 'static> {
tracepoint: &'static TracePoint<L, K>,
}
impl<L: RawMutex + 'static, K: KernelTraceOps + 'static> TracePointFormatFile<L, K> {
fn new(tracepoint: &'static TracePoint<L, K>) -> Self {
Self { tracepoint }
}
pub fn read(&self) -> String {
self.tracepoint.print_fmt()
}
}
#[derive(Debug, Clone)]
pub struct TracePointEnableFile<L: RawMutex + 'static, K: KernelTraceOps + 'static> {
tracepoint: &'static TracePoint<L, K>,
}
impl<L: RawMutex + 'static, K: KernelTraceOps + 'static> TracePointEnableFile<L, K> {
fn new(tracepoint: &'static TracePoint<L, K>) -> Self {
Self { tracepoint }
}
pub fn read(&self) -> &'static str {
if self.tracepoint.is_enabled() {
"1\n"
} else {
"0\n"
}
}
pub fn write(&self, enable: char) {
match enable {
'1' => self.tracepoint.enable(),
'0' => self.tracepoint.disable(),
_ => {
log::warn!("Invalid value for tracepoint enable: {enable}");
}
}
}
}
#[derive(Debug, Clone)]
pub struct TracePointIdFile<L: RawMutex + 'static, K: KernelTraceOps + 'static> {
tracepoint: &'static TracePoint<L, K>,
}
impl<L: RawMutex + 'static, K: KernelTraceOps + 'static> TracePointIdFile<L, K> {
fn new(tracepoint: &'static TracePoint<L, K>) -> Self {
Self { tracepoint }
}
pub fn read(&self) -> String {
format!("{}\n", self.tracepoint.id())
}
}
unsafe extern "C" {
fn __start_tracepoint();
fn __stop_tracepoint();
}
pub fn global_init_events<L: RawMutex + 'static, K: KernelTraceOps + 'static>()
-> Result<TracingEventsManager<L, K>, &'static str> {
static TRACE_POINT_ID: AtomicUsize = AtomicUsize::new(0);
let events_manager = TracingEventsManager::new(TracePointMap::<L, K>::new());
let tracepoint_data_start = __start_tracepoint as usize as *mut CommonTracePointMeta<L, K>;
let tracepoint_data_end = __stop_tracepoint as usize as *mut CommonTracePointMeta<L, 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<L, 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 tracepoint_map = events_manager.tracepoint_map();
for tracepoint_meta in tracepoint_data {
let tracepoint = tracepoint_meta.trace_point;
let id = TRACE_POINT_ID.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
tracepoint.set_id(id as u32);
tracepoint.register(tracepoint_meta.print_func, Box::new(()));
tracepoint_map.insert(id as u32, tracepoint);
log::info!(
"tracepoint registered: {}:{}",
tracepoint.system(),
tracepoint.name(),
);
let subsys_name = tracepoint.system();
let subsys = events_manager.create_subsystem(subsys_name);
let event_info = EventInfo::new(tracepoint);
subsys.create_event(tracepoint.name(), event_info);
}
drop(tracepoint_map); Ok(events_manager)
}