refactor: refactor tracepoint (#1186)

* Refactor: refactor tracepoint

Move tracepoints into separate module.
Macro implementation closer to Linux.
Add the id and formt files corresponding to each tracepoint.
Correctly handle trace/trace_pipe files.

Signed-off-by: Godones <chenlinfeng25@outlook.com>
This commit is contained in:
linfeng 2025-06-04 13:53:27 +08:00 committed by GitHub
parent 41c7f962c9
commit 58e7943c13
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1300 additions and 520 deletions

View File

@ -1,135 +1,14 @@
use crate::debug::tracing::tracepoint::{CommonTracePointMeta, TracePoint};
use alloc::string::ToString;
use crate::debug::tracing::TracingDirCallBack;
use crate::filesystem::kernfs::callback::{KernCallbackData, KernFSCallback, KernInodePrivateData};
use crate::filesystem::kernfs::KernFSInode;
use crate::filesystem::vfs::syscall::ModeType;
use crate::filesystem::vfs::PollStatus;
use crate::libs::spinlock::SpinLock;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::string::{String, ToString};
use crate::tracepoint::*;
use alloc::sync::Arc;
use system_error::SystemError;
#[derive(Debug)]
pub struct TracingEventsManager {
root: Arc<KernFSInode>,
subsystems: SpinLock<BTreeMap<String, Arc<EventsSubsystem>>>,
}
impl TracingEventsManager {
pub fn new(root: Arc<KernFSInode>) -> Self {
Self {
root,
subsystems: SpinLock::new(BTreeMap::new()),
}
}
/// Create a subsystem by name
///
/// If the subsystem already exists, return the existing subsystem.
pub fn create_subsystem(
&self,
subsystem_name: &str,
) -> Result<Arc<EventsSubsystem>, SystemError> {
if self.subsystems.lock().contains_key(subsystem_name) {
return Ok(self.get_subsystem(subsystem_name).unwrap());
}
let dir = self.root.add_dir(
subsystem_name.to_string(),
ModeType::from_bits_truncate(0o755),
None,
Some(&TracingDirCallBack),
)?;
let subsystem = Arc::new(EventsSubsystem::new(dir));
self.subsystems
.lock()
.insert(subsystem_name.to_string(), subsystem.clone());
Ok(subsystem)
}
/// Get the subsystem by name
pub fn get_subsystem(&self, subsystem_name: &str) -> Option<Arc<EventsSubsystem>> {
self.subsystems.lock().get(subsystem_name).cloned()
}
}
#[derive(Debug)]
pub struct EventsSubsystem {
root: Arc<KernFSInode>,
events: SpinLock<BTreeMap<String, Arc<EventInfo>>>,
}
impl EventsSubsystem {
pub fn new(root: Arc<KernFSInode>) -> Self {
Self {
root,
events: SpinLock::new(BTreeMap::new()),
}
}
/// Insert a new event into the subsystem
pub fn insert_event(
&self,
event_name: &str,
event_info: Arc<EventInfo>,
) -> Result<(), SystemError> {
self.events
.lock()
.insert(event_name.to_string(), event_info);
Ok(())
}
/// Get the event by name
#[allow(unused)]
pub fn get_event(&self, event_name: &str) -> Option<Arc<EventInfo>> {
self.events.lock().get(event_name).cloned()
}
/// Get the root inode of the subsystem
pub fn root(&self) -> Arc<KernFSInode> {
self.root.clone()
}
}
#[derive(Debug)]
pub struct EventInfo {
#[allow(unused)]
enable: Arc<KernFSInode>,
// filter: Arc<KernFSInode>,
// trigger: Arc<KernFSInode>,
}
impl EventInfo {
pub fn new(tracepoint: &'static TracePoint, subsystem: Arc<KernFSInode>) -> Arc<Self> {
let trace_dir = subsystem
.add_dir(
tracepoint.name().to_string(),
ModeType::from_bits_truncate(0o755),
None,
Some(&TracingDirCallBack),
)
.expect("add tracepoint dir failed");
let enable_inode = trace_dir
.add_file(
"enable".to_string(),
ModeType::from_bits_truncate(0o644),
None,
Some(KernInodePrivateData::DebugFS(tracepoint)),
Some(&EnableCallBack),
)
.expect("add enable file failed");
Arc::new(Self {
enable: enable_inode,
})
}
}
impl Drop for EventInfo {
fn drop(&mut self) {}
}
#[derive(Debug)]
struct EnableCallBack;
@ -144,22 +23,15 @@ impl KernFSCallback for EnableCallBack {
buf: &mut [u8],
offset: usize,
) -> Result<usize, SystemError> {
if offset > 0 {
return Ok(0);
}
let pri_data = data.private_data();
match pri_data {
Some(pri_data) => {
let tracepoint = pri_data.debugfs_tracepoint().ok_or(SystemError::EINVAL)?;
let buf_value = if tracepoint.is_enabled() { b"1" } else { b"0" };
let len = buf.len().min(buf_value.len());
buf[..len].copy_from_slice(&buf_value[..len]);
Ok(len)
}
None => {
return Err(SystemError::EINVAL);
}
let pri_data = data.private_data().as_ref().unwrap();
let tracepoint_info = pri_data.debugfs_tracepoint().unwrap();
let enable_value = tracepoint_info.enable_file().read();
if offset >= enable_value.as_bytes().len() {
return Ok(0); // Offset is beyond the length of the string
}
let len = buf.len().min(enable_value.as_bytes().len() - offset);
buf[..len].copy_from_slice(&enable_value.as_bytes()[offset..offset + len]);
Ok(len)
}
fn write(
@ -168,31 +40,91 @@ impl KernFSCallback for EnableCallBack {
buf: &[u8],
_offset: usize,
) -> Result<usize, SystemError> {
let pri_data = data.private_data();
match pri_data {
Some(pri_data) => {
let tracepoint = pri_data.debugfs_tracepoint().ok_or(SystemError::EINVAL)?;
let value = core::str::from_utf8(buf)
.map_err(|_| SystemError::EINVAL)?
.trim();
match value {
"0" => {
tracepoint.disable();
}
"1" => {
tracepoint.enable();
}
_ => {
log::info!("EnableCallBack invalid value: {}", value);
return Err(SystemError::EINVAL);
}
}
Ok(buf.len())
}
None => {
return Err(SystemError::EINVAL);
}
let pri_data = data.private_data().as_ref().unwrap();
let tracepoint = pri_data.debugfs_tracepoint().unwrap();
if buf.is_empty() {
return Err(SystemError::EINVAL);
}
tracepoint.enable_file().write(buf[0] as _);
Ok(buf.len())
}
fn poll(&self, _data: KernCallbackData) -> Result<PollStatus, SystemError> {
Err(SystemError::ENOSYS)
}
}
#[derive(Debug)]
struct FormatCallBack;
impl KernFSCallback for FormatCallBack {
fn open(&self, _data: KernCallbackData) -> Result<(), SystemError> {
Ok(())
}
fn read(
&self,
data: KernCallbackData,
buf: &mut [u8],
offset: usize,
) -> Result<usize, SystemError> {
let pri_data = data.private_data().as_ref().unwrap();
let tracepoint = pri_data.debugfs_tracepoint().unwrap();
let format_str = tracepoint.format_file().read();
if offset >= format_str.as_bytes().len() {
return Ok(0); // Offset is beyond the length of the string
}
let len = buf.len().min(format_str.as_bytes().len() - offset);
buf[..len].copy_from_slice(&format_str.as_bytes()[offset..offset + len]);
Ok(len)
}
fn write(
&self,
_data: KernCallbackData,
_buf: &[u8],
_offset: usize,
) -> Result<usize, SystemError> {
Err(SystemError::EPERM)
}
fn poll(&self, _data: KernCallbackData) -> Result<PollStatus, SystemError> {
Err(SystemError::ENOSYS)
}
}
#[derive(Debug)]
struct IDCallBack;
impl KernFSCallback for IDCallBack {
fn open(&self, _data: KernCallbackData) -> Result<(), SystemError> {
Ok(())
}
fn read(
&self,
data: KernCallbackData,
buf: &mut [u8],
offset: usize,
) -> Result<usize, SystemError> {
let pri_data = data.private_data().as_ref().unwrap();
let tracepoint = pri_data.debugfs_tracepoint().unwrap();
let id_str = tracepoint.id_file().read();
if offset >= id_str.as_bytes().len() {
return Ok(0); // Offset is beyond the length of the string
}
let len = buf.len().min(id_str.as_bytes().len() - offset);
buf[..len].copy_from_slice(&id_str.as_bytes()[offset..offset + len]);
Ok(len)
}
fn write(
&self,
_data: KernCallbackData,
_buf: &[u8],
_offset: usize,
) -> Result<usize, SystemError> {
Err(SystemError::EPERM)
}
fn poll(&self, _data: KernCallbackData) -> Result<PollStatus, SystemError> {
@ -202,38 +134,62 @@ impl KernFSCallback for EnableCallBack {
static mut TRACING_EVENTS_MANAGER: Option<TracingEventsManager> = None;
/// Initialize the tracing events
pub fn init_events(events_root: Arc<KernFSInode>) -> Result<(), SystemError> {
let events_manager = TracingEventsManager::new(events_root);
let tracepoint_data_start = _tracepoint as usize as *const CommonTracePointMeta;
let tracepoint_data_end = _etracepoint as usize as *const CommonTracePointMeta;
let tracepoint_data_len = (tracepoint_data_end as usize - tracepoint_data_start as usize)
/ size_of::<CommonTracePointMeta>();
let tracepoint_data =
unsafe { core::slice::from_raw_parts(tracepoint_data_start, tracepoint_data_len) };
for tracepoint_meta in tracepoint_data {
let tracepoint = tracepoint_meta.trace_point;
tracepoint.register(tracepoint_meta.print_func, Box::new(()));
log::info!(
"tracepoint name: {}, module path: {}",
tracepoint.name(),
tracepoint.module_path()
);
// kernel::{subsystem}::
let mut subsys_name = tracepoint.module_path().split("::");
let subsys_name = subsys_name.nth(1).ok_or(SystemError::EINVAL)?;
let subsys = events_manager.create_subsystem(subsys_name)?;
let event_info = EventInfo::new(tracepoint, subsys.root());
subsys.insert_event(tracepoint.name(), event_info)?;
pub fn tracing_events_manager() -> &'static TracingEventsManager {
unsafe {
TRACING_EVENTS_MANAGER
.as_ref()
.expect("TracingEventsManager not initialized")
}
}
pub fn init_events(root: Arc<KernFSInode>) -> Result<(), SystemError> {
let events_manager = crate::tracepoint::global_init_events()?;
// Register the global tracing events manager
for subsystem_name in events_manager.subsystem_names() {
let subsystem = events_manager.get_subsystem(&subsystem_name).unwrap();
// Register the subsystem in the root inode
let subsystem_inode = root.add_dir(
subsystem_name,
ModeType::from_bits_truncate(0o755),
None,
Some(&TracingDirCallBack),
)?;
for event_name in subsystem.event_names() {
let event_info = subsystem.get_event(&event_name).unwrap();
let event_inode = subsystem_inode.add_dir(
event_name,
ModeType::from_bits_truncate(0o755),
None,
Some(&TracingDirCallBack),
)?;
// add enable file for the event
let _enable_inode = event_inode.add_file(
"enable".to_string(),
ModeType::from_bits_truncate(0o644),
None,
Some(KernInodePrivateData::DebugFS(event_info.clone())),
Some(&EnableCallBack),
)?;
// add format file for the event
let _format_inode = event_inode.add_file(
"format".to_string(),
ModeType::from_bits_truncate(0o644),
None,
Some(KernInodePrivateData::DebugFS(event_info.clone())),
Some(&FormatCallBack),
)?;
// add id file for the event
let _id_inode = event_inode.add_file(
"id".to_string(),
ModeType::from_bits_truncate(0o644),
None,
Some(KernInodePrivateData::DebugFS(event_info)),
Some(&IDCallBack),
)?;
}
}
unsafe {
TRACING_EVENTS_MANAGER = Some(events_manager);
}
Ok(())
}
extern "C" {
fn _tracepoint();
fn _etracepoint();
}

View File

@ -1,61 +1,49 @@
mod events;
pub mod trace_pipe;
pub mod tracepoint;
use crate::debug::sysfs::debugfs_kset;
use crate::debug::tracing::trace_pipe::TRACE_PIPE_MAX_RECORD;
use crate::driver::base::kobject::KObject;
use crate::filesystem::kernfs::callback::{KernCallbackData, KernFSCallback, KernInodePrivateData};
use crate::filesystem::kernfs::KernFSInode;
use crate::filesystem::vfs::syscall::ModeType;
use crate::filesystem::vfs::PollStatus;
use crate::libs::spinlock::SpinLock;
use crate::tracepoint::TracePointInfo;
use alloc::string::ToString;
use alloc::sync::Arc;
use system_error::SystemError;
use tracepoint::TracePoint;
static mut TRACING_ROOT_INODE: Option<Arc<KernFSInode>> = None;
static TRACE_RAW_PIPE: SpinLock<crate::tracepoint::TracePipeRaw> =
SpinLock::new(crate::tracepoint::TracePipeRaw::new(4096));
static TRACE_CMDLINE_CACHE: SpinLock<crate::tracepoint::TraceCmdLineCache> =
SpinLock::new(crate::tracepoint::TraceCmdLineCache::new(128));
pub fn trace_pipe_push_raw_record(record: &[u8]) {
TRACE_RAW_PIPE.lock().push_event(record.to_vec());
}
pub fn trace_cmdline_push(pid: u32) {
let process = crate::process::ProcessManager::current_pcb();
let binding = process.basic();
let pname = binding
.name()
.split(' ')
.next()
.unwrap_or("unknown")
.split('/')
.last()
.unwrap_or("unknown");
TRACE_CMDLINE_CACHE.lock().insert(pid, pname.to_string());
}
#[allow(unused)]
fn tracing_root_inode() -> Arc<KernFSInode> {
unsafe { TRACING_ROOT_INODE.clone().unwrap() }
}
/// Initialize the debugfs tracing directory
pub fn init_debugfs_tracing() -> Result<(), SystemError> {
let debugfs = debugfs_kset();
let root_dir = debugfs.inode().ok_or(SystemError::ENOENT)?;
let tracing_root = root_dir.add_dir(
"tracing".to_string(),
ModeType::from_bits_truncate(0o555),
None,
Some(&TracingDirCallBack),
)?;
let events_root = tracing_root.add_dir(
"events".to_string(),
ModeType::from_bits_truncate(0o755),
None,
Some(&TracingDirCallBack),
)?;
tracing_root.add_file(
"trace_pipe".to_string(),
ModeType::from_bits_truncate(0o444),
Some(TRACE_PIPE_MAX_RECORD),
None,
Some(&trace_pipe::TracePipeCallBack),
)?;
trace_pipe::init_trace_pipe();
events::init_events(events_root)?;
unsafe {
TRACING_ROOT_INODE = Some(tracing_root);
}
Ok(())
}
#[derive(Debug)]
pub struct TracingDirCallBack;
@ -88,10 +76,60 @@ impl KernFSCallback for TracingDirCallBack {
}
impl KernInodePrivateData {
pub fn debugfs_tracepoint(&self) -> Option<&'static TracePoint> {
pub fn debugfs_tracepoint(&self) -> Option<&Arc<TracePointInfo>> {
return match self {
KernInodePrivateData::DebugFS(tracepoint) => Some(tracepoint),
_ => None,
};
}
pub fn tracepipe(&mut self) -> Option<&mut crate::tracepoint::TracePipeSnapshot> {
return match self {
KernInodePrivateData::TracePipe(snapshot) => Some(snapshot),
_ => None,
};
}
}
/// Initialize the debugfs tracing directory
pub fn init_debugfs_tracing() -> Result<(), SystemError> {
let debugfs = debugfs_kset();
let root_dir = debugfs.inode().ok_or(SystemError::ENOENT)?;
let tracing_root = root_dir.add_dir(
"tracing".to_string(),
ModeType::from_bits_truncate(0o555),
None,
Some(&TracingDirCallBack),
)?;
let events_root = tracing_root.add_dir(
"events".to_string(),
ModeType::from_bits_truncate(0o755),
None,
Some(&TracingDirCallBack),
)?;
// tracing_root.add_file(
// "trace".to_string(),
// ModeType::from_bits_truncate(0o444),
// Some(4096),
// None,
// Some(&trace_pipe::TraceCallBack),
// )?;
tracing_root.add_file_lazy("trace".to_string(), trace_pipe::kernel_inode_provider)?;
tracing_root.add_file(
"trace_pipe".to_string(),
ModeType::from_bits_truncate(0o444),
Some(4096),
None,
Some(&trace_pipe::TracePipeCallBack),
)?;
events::init_events(events_root)?;
unsafe {
TRACING_ROOT_INODE = Some(tracing_root);
}
Ok(())
}

View File

@ -1,111 +1,142 @@
use crate::filesystem::kernfs::callback::{KernCallbackData, KernFSCallback};
use crate::filesystem::kernfs::callback::{KernCallbackData, KernFSCallback, KernInodePrivateData};
use crate::filesystem::kernfs::{KernFSInodeArgs, KernInodeType};
use crate::filesystem::vfs::syscall::ModeType;
use crate::filesystem::vfs::PollStatus;
use crate::libs::spinlock::SpinLock;
use alloc::string::String;
use alloc::vec::Vec;
use crate::libs::wait_queue::WaitQueue;
use crate::process::{ProcessFlags, ProcessManager};
use crate::sched::SchedMode;
use crate::tracepoint::{TraceEntryParser, TracePipeOps};
use core::fmt::Debug;
use system_error::SystemError;
static mut TRACE_PIPE: Option<TracePipe> = None;
pub const TRACE_PIPE_MAX_RECORD: usize = 4096;
pub fn init_trace_pipe() {
unsafe {
TRACE_PIPE = Some(TracePipe::new(TRACE_PIPE_MAX_RECORD));
}
}
pub fn trace_pipe() -> &'static TracePipe {
unsafe { TRACE_PIPE.as_ref().unwrap() }
}
/// Push a record to trace pipe
pub fn trace_pipe_push_record(record: String) {
trace_pipe().push_record(record);
}
pub struct TracePipe {
buf: SpinLock<TracePipeBuf>,
}
struct TracePipeBuf {
size: usize,
max_record: usize,
buf: Vec<String>,
}
impl TracePipeBuf {
pub const fn new(max_record: usize) -> Self {
Self {
max_record,
size: 0,
buf: Vec::new(),
}
}
pub fn push_str(&mut self, record: String) {
let record_size = record.len();
if self.size + record_size > self.max_record {
let mut i = 0;
while i < record_size {
let t = self.buf.pop().unwrap();
self.size -= t.len();
i += t.len();
fn common_trace_pipe_read(
trace_buf: &mut dyn TracePipeOps,
buf: &mut [u8],
) -> Result<usize, SystemError> {
let manager = super::events::tracing_events_manager();
let tracepint_map = manager.tracepoint_map();
let trace_cmdline_cache = super::TRACE_CMDLINE_CACHE.lock();
// read real trace data
let mut copy_len = 0;
let mut peek_flag = false;
loop {
if let Some(record) = trace_buf.peek() {
let record_str = TraceEntryParser::parse(&tracepint_map, &trace_cmdline_cache, record);
if copy_len + record_str.len() > buf.len() {
break; // Buffer is full
}
let len = record_str.len();
buf[copy_len..copy_len + len].copy_from_slice(record_str.as_bytes());
copy_len += len;
peek_flag = true;
}
self.buf.push(record);
self.size += record_size;
}
pub fn read_at(&self, buf: &mut [u8], offset: usize) -> Result<usize, SystemError> {
if offset == self.size {
return Ok(0);
}
if buf.len() < self.size {
return Err(SystemError::EINVAL);
}
let mut count = 0;
for line in self.buf.iter() {
let line = line.as_bytes();
buf[count..count + line.len()].copy_from_slice(line);
count += line.len();
}
Ok(count)
}
}
impl TracePipe {
pub const fn new(max_record: usize) -> Self {
Self {
buf: SpinLock::new(TracePipeBuf::new(max_record)),
if peek_flag {
trace_buf.pop(); // Remove the record after reading
peek_flag = false;
} else {
break; // No more records to read
}
}
pub fn push_record(&self, record: String) {
self.buf.lock().push_str(record);
}
pub fn read_at(&self, buf: &mut [u8], offset: usize) -> Result<usize, SystemError> {
self.buf.lock().read_at(buf, offset)
}
Ok(copy_len)
}
#[derive(Debug)]
pub struct TracePipeCallBack;
pub struct TraceCallBack;
impl KernFSCallback for TracePipeCallBack {
fn open(&self, _data: KernCallbackData) -> Result<(), SystemError> {
impl KernFSCallback for TraceCallBack {
fn open(&self, mut data: KernCallbackData) -> Result<(), SystemError> {
let pri_data = data.private_data_mut();
let snapshot = super::TRACE_RAW_PIPE.lock().snapshot();
pri_data.replace(KernInodePrivateData::TracePipe(snapshot));
Ok(())
}
fn read(
&self,
_data: KernCallbackData,
mut data: KernCallbackData,
buf: &mut [u8],
offset: usize,
) -> Result<usize, SystemError> {
let trace_pipe = trace_pipe();
trace_pipe.read_at(buf, offset)
let pri_data = data.private_data_mut().as_mut().unwrap();
let snapshot = pri_data.tracepipe().unwrap();
let default_fmt_str = snapshot.default_fmt_str();
if offset >= default_fmt_str.len() {
common_trace_pipe_read(snapshot, buf)
} else {
let len = buf.len().min(default_fmt_str.len() - offset);
buf[..len].copy_from_slice(&default_fmt_str.as_bytes()[offset..offset + len]);
Ok(len)
}
}
fn write(
&self,
_data: KernCallbackData,
_buf: &[u8],
_offset: usize,
) -> Result<usize, SystemError> {
Err(SystemError::EPERM)
}
fn poll(&self, _data: KernCallbackData) -> Result<PollStatus, SystemError> {
Ok(PollStatus::READ)
}
}
pub fn kernel_inode_provider() -> KernFSInodeArgs {
KernFSInodeArgs {
mode: ModeType::from_bits_truncate(0o444),
callback: Some(&TraceCallBack),
inode_type: KernInodeType::File,
size: Some(4096),
private_data: None,
}
}
static TracePipeCallBackWaitQueue: WaitQueue = WaitQueue::default();
#[derive(Debug)]
pub struct TracePipeCallBack;
impl TracePipeCallBack {
fn readable(&self) -> bool {
let trace_raw_pipe = super::TRACE_RAW_PIPE.lock();
!trace_raw_pipe.is_empty()
}
}
impl KernFSCallback for TracePipeCallBack {
fn open(&self, _data: KernCallbackData) -> Result<(), SystemError> {
Ok(())
}
fn read(
&self,
data: KernCallbackData,
buf: &mut [u8],
_offset: usize,
) -> Result<usize, SystemError> {
drop(data); // We don't need the data here, release the internal lock
let read_len = loop {
let mut trace_raw_pipe = super::TRACE_RAW_PIPE.lock();
let read_len = common_trace_pipe_read(&mut *trace_raw_pipe, buf).unwrap();
if read_len != 0 {
break read_len;
}
// Release the lock before waiting
drop(trace_raw_pipe);
// wait for new data
let r = wq_wait_event_interruptible!(TracePipeCallBackWaitQueue, self.readable(), {});
if r.is_err() {
ProcessManager::current_pcb()
.flags()
.insert(ProcessFlags::HAS_PENDING_SIGNAL);
return Err(SystemError::ERESTARTSYS);
}
// todo!(wq_wait_event_interruptible may has a bug)
};
Ok(read_len)
}
fn write(

View File

@ -1,187 +0,0 @@
use crate::libs::spinlock::{SpinLock, SpinLockGuard};
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use core::any::Any;
use core::fmt::Debug;
use static_keys::StaticFalseKey;
pub struct TracePoint {
name: &'static str,
module_path: &'static str,
key: &'static StaticFalseKey,
register: Option<fn()>,
unregister: Option<fn()>,
callback: SpinLock<BTreeMap<usize, TracePointFunc>>,
}
impl Debug for TracePoint {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TracePoint")
.field("name", &self.name)
.finish()
}
}
#[derive(Debug)]
#[repr(C)]
pub struct CommonTracePointMeta {
pub trace_point: &'static TracePoint,
pub print_func: fn(),
}
#[derive(Debug)]
pub struct TracePointFunc {
pub func: fn(),
pub data: Box<dyn Any + Send + Sync>,
}
impl TracePoint {
pub const fn new(
key: &'static StaticFalseKey,
name: &'static str,
module_path: &'static str,
register: Option<fn()>,
unregister: Option<fn()>,
) -> Self {
Self {
name,
module_path,
key,
register,
unregister,
callback: SpinLock::new(BTreeMap::new()),
}
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn module_path(&self) -> &'static str {
self.module_path
}
/// Register a callback function to the tracepoint
pub fn register(&self, func: fn(), data: Box<dyn Any + Sync + Send>) {
let trace_point_func = TracePointFunc { func, data };
let mut funcs = self.callback.lock();
if let Some(register) = self.register {
register();
}
let ptr = func as usize;
funcs.entry(ptr).or_insert(trace_point_func);
}
/// Unregister a callback function from the tracepoint
pub fn unregister(&self, func: fn()) {
let mut funcs = self.callback.lock();
if let Some(unregister) = self.unregister {
unregister();
}
let func_ptr = func as usize;
funcs.remove(&func_ptr);
}
/// Get the callback list
pub fn callback_list(&self) -> SpinLockGuard<BTreeMap<usize, TracePointFunc>> {
self.callback.lock()
}
/// Enable the tracepoint
pub fn enable(&self) {
unsafe {
self.key.enable();
}
}
/// Disable the tracepoint
pub fn disable(&self) {
unsafe {
self.key.disable();
}
}
/// Check if the tracepoint is enabled
pub fn is_enabled(&self) -> bool {
self.key.is_enabled()
}
}
/// Define a tracepoint
///
/// User should call register_trace_\$name to register a callback function to the tracepoint and
/// call trace_\$name to trigger the callback function
#[macro_export]
macro_rules! define_trace_point {
($name:ident $(,$arg:ident:$arg_type:ty),*) => {
paste::paste!{
static_keys::define_static_key_false!([<__ $name _KEY>]);
#[allow(non_upper_case_globals)]
#[used]
static [<__ $name>]: $crate::debug::tracing::tracepoint::TracePoint = $crate::debug::tracing::tracepoint::TracePoint::new(&[<__ $name _KEY>],stringify!($name), module_path!(),None,None);
#[inline(always)]
#[allow(non_snake_case)]
pub fn [<TRACE_ $name>]( $($arg:$arg_type),* ){
if static_keys::static_branch_unlikely!([<__ $name _KEY>]){
let mut funcs = [<__ $name>].callback_list();
for trace_func in funcs.values_mut(){
let func = trace_func.func;
let data = trace_func.data.as_mut();
let func = unsafe{core::mem::transmute::<fn(),fn(&mut (dyn core::any::Any+Send+Sync),$($arg_type),*)>(func)};
func(data $(,$arg)*);
}
}
}
#[allow(unused,non_snake_case)]
pub fn [<register_trace_ $name>](func:fn(&mut (dyn core::any::Any+Send+Sync),$($arg_type),*),data:alloc::boxed::Box<dyn core::any::Any+Send+Sync>){
let func = unsafe{core::mem::transmute::<fn(&mut (dyn core::any::Any+Send+Sync),$($arg_type),*),fn()>(func)};
[<__ $name>].register(func,data);
}
#[allow(unused,non_snake_case)]
pub fn [<unregister_trace_ $name>](func:fn(&mut (dyn core::any::Any+Send+Sync),$($arg_type),*)){
let func = unsafe{core::mem::transmute::<fn(&mut (dyn core::any::Any+Send+Sync),$($arg_type),*),fn()>(func)};
[<__ $name>].unregister(func);
}
}
};
}
#[macro_export]
macro_rules! define_event_trace{
($name:ident,
($($arg:ident:$arg_type:ty),*),
$fmt:expr) =>{
define_trace_point!($name $(,$arg:$arg_type),*);
paste::paste!{
#[derive(Debug)]
#[repr(C)]
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
struct [<__ $name _TracePointMeta>]{
trace_point: &'static $crate::debug::tracing::tracepoint::TracePoint,
print_func: fn(&mut (dyn core::any::Any+Send+Sync),$($arg_type),*),
}
#[allow(non_upper_case_globals)]
#[link_section = ".tracepoint"]
#[used]
static [<__ $name _meta>]: [<__ $name _TracePointMeta>] = [<__ $name _TracePointMeta>]{
trace_point:&[<__ $name>],
print_func:[<TRACE_PRINT_ $name>],
};
#[allow(non_snake_case)]
pub fn [<TRACE_PRINT_ $name>](_data:&mut (dyn core::any::Any+Send+Sync),$($arg:$arg_type),* ){
let time = $crate::time::Instant::now();
let cpu_id = $crate::arch::cpu::current_cpu_id().data();
let current_pid = $crate::process::ProcessManager::current_pcb().pid().data();
let format = format!("[{}][{}][{}] {}\n",time,cpu_id,current_pid,$fmt);
$crate::debug::tracing::trace_pipe::trace_pipe_push_record(format);
}
}
};
}

View File

@ -1,5 +1,5 @@
use super::KernFSInode;
use crate::debug::tracing::tracepoint::TracePoint;
use crate::tracepoint::{TracePipeSnapshot, TracePointInfo};
use crate::{
filesystem::{sysfs::SysFSKernPrivateData, vfs::PollStatus},
libs::spinlock::SpinLockGuard,
@ -86,7 +86,8 @@ impl<'a> KernCallbackData<'a> {
#[derive(Debug)]
pub enum KernInodePrivateData {
SysFS(SysFSKernPrivateData),
DebugFS(&'static TracePoint),
DebugFS(Arc<TracePointInfo>),
TracePipe(TracePipeSnapshot),
}
impl KernInodePrivateData {

View File

@ -1,3 +1,4 @@
use alloc::string::ToString;
use core::{cmp::min, fmt::Debug, intrinsics::unlikely};
use alloc::{
@ -115,6 +116,7 @@ impl KernFS {
callback: None,
children: SpinLock::new(HashMap::new()),
inode_type: KernInodeType::Dir,
lazy_list: SpinLock::new(HashMap::new()),
});
return root_inode;
@ -138,6 +140,16 @@ pub struct KernFSInode {
inode_type: KernInodeType,
/// Inode名称
name: String,
/// lazy list
lazy_list: SpinLock<HashMap<String, fn() -> KernFSInodeArgs>>,
}
pub struct KernFSInodeArgs {
pub mode: ModeType,
pub inode_type: KernInodeType,
pub size: Option<usize>,
pub private_data: Option<KernInodePrivateData>,
pub callback: Option<&'static dyn KernFSCallback>,
}
#[derive(Debug)]
@ -245,12 +257,25 @@ impl IndexNode for KernFSInode {
}
name => {
// 在子目录项中查找
return Ok(self
.children
.lock()
.get(name)
.ok_or(SystemError::ENOENT)?
.clone());
let child = self.children.lock().get(name).cloned();
if let Some(child) = child {
return Ok(child);
}
let lazy_list = self.lazy_list.lock();
if let Some(provider) = lazy_list.get(name) {
// 如果存在lazy list则调用提供者函数创建
let args = provider();
let inode = self.inner_create(
name.to_string(),
args.inode_type,
args.mode,
args.size.unwrap_or(4096),
args.private_data,
args.callback,
)?;
return Ok(inode);
}
Err(SystemError::ENOENT)
}
}
}
@ -321,7 +346,7 @@ impl IndexNode for KernFSInode {
offset: usize,
len: usize,
buf: &mut [u8],
_data: SpinLockGuard<FilePrivateData>,
data: SpinLockGuard<FilePrivateData>,
) -> Result<usize, SystemError> {
if self.inode_type == KernInodeType::SymLink {
let inner = self.inner.read();
@ -350,6 +375,8 @@ impl IndexNode for KernFSInode {
warn!("kernfs: callback is none");
return Err(SystemError::ENOSYS);
}
// release the private data lock before calling the callback
drop(data);
let callback_data =
KernCallbackData::new(self.self_ref.upgrade().unwrap(), self.private_data.lock());
@ -365,7 +392,7 @@ impl IndexNode for KernFSInode {
offset: usize,
len: usize,
buf: &[u8],
_data: SpinLockGuard<FilePrivateData>,
data: SpinLockGuard<FilePrivateData>,
) -> Result<usize, SystemError> {
if self.inode_type != KernInodeType::File {
return Err(SystemError::EISDIR);
@ -375,6 +402,9 @@ impl IndexNode for KernFSInode {
return Err(SystemError::ENOSYS);
}
// release the private data lock before calling the callback
drop(data);
let callback_data =
KernCallbackData::new(self.self_ref.upgrade().unwrap(), self.private_data.lock());
return self
@ -411,6 +441,7 @@ impl KernFSInode {
callback,
children: SpinLock::new(HashMap::new()),
inode_type,
lazy_list: SpinLock::new(HashMap::new()),
});
{
@ -500,6 +531,18 @@ impl KernFSInode {
);
}
pub fn add_file_lazy(
&self,
name: String,
provider: fn() -> KernFSInodeArgs,
) -> Result<(), SystemError> {
if unlikely(self.inode_type != KernInodeType::Dir) {
return Err(SystemError::ENOTDIR);
}
self.lazy_list.lock().insert(name, provider);
Ok(())
}
fn inner_create(
&self,
name: String,

View File

@ -5,7 +5,7 @@ use log::{error, info};
use system_error::SystemError;
use crate::{
define_event_trace, define_trace_point,
define_event_trace,
driver::base::block::{gendisk::GenDisk, manager::block_dev_manager},
filesystem::{
devfs::devfs_init,
@ -167,15 +167,39 @@ pub fn mount_root_fs() -> Result<(), SystemError> {
return Ok(());
}
define_event_trace!(DO_MKDIR_AT,(path:&str,mode:FileMode),
format_args!("mkdir at {} with mode {:?}",path, mode));
define_event_trace!(
do_mkdir_at,
TP_system(vfs),
TP_PROTO(path:&str, mode: FileMode),
TP_STRUCT__entry {
fmode: FileMode,
path: [u8;64],
},
TP_fast_assign {
fmode: mode,
path: {
let mut buf = [0u8; 64];
let path = path.as_bytes();
let len = path.len().min(63);
buf[..len].copy_from_slice(&path[..len]);
buf[len] = 0; // null-terminate
buf
},
},
TP_ident(__entry),
TP_printk({
let path = core::str::from_utf8(&__entry.path).unwrap_or("invalid utf8");
let mode = __entry.fmode;
format!("mkdir at {} with mode {:?}", path, mode)
})
);
/// @brief 创建文件/文件夹
pub fn do_mkdir_at(
dirfd: i32,
path: &str,
mode: FileMode,
) -> Result<Arc<dyn IndexNode>, SystemError> {
TRACE_DO_MKDIR_AT(path, mode);
trace_do_mkdir_at(path, mode);
// debug!("Call do mkdir at");
let (mut current_inode, path) =
user_path_at(&ProcessManager::current_pcb(), dirfd, path.trim())?;

View File

@ -23,7 +23,12 @@
#![feature(vec_into_raw_parts)]
#![feature(linkage)]
#![feature(panic_can_unwind)]
#![allow(static_mut_refs, non_local_definitions, internal_features)]
#![allow(
static_mut_refs,
non_local_definitions,
internal_features,
non_upper_case_globals
)]
// clippy的配置
#![deny(clippy::all)]
// 取消下面的注释以启用clippy对栈帧大小的检查
@ -70,6 +75,7 @@ mod sched;
mod smp;
mod syscall;
mod time;
mod tracepoint;
#[cfg(target_arch = "x86_64")]
mod virt;

View File

@ -0,0 +1,175 @@
/// Define a tracepoint with the given parameters.
///
/// This macro generates a tracepoint with the specified name, arguments, entry structure, assignment logic, identifier, and print format.
/// # Parameters
/// - `name`: The name of the tracepoint.
/// - `TP_system`: The subsystem or system to which the tracepoint belongs.
/// - `TP_PROTO`: The prototype of the tracepoint function.
/// - `TP_STRUCT__entry`: The structure of the tracepoint entry.
/// - `TP_fast_assign`: The assignment logic for the tracepoint entry.
/// - `TP_ident`: The identifier for the tracepoint entry.
/// - `TP_printk`: The print format for the tracepoint.
///
/// # Example
/// ```rust
/// define_event_trace!(
/// TEST2,
/// TP_PROTO(a: u32, b: u32),
/// TP_STRUCT__entry{
/// a: u32,
/// b: u32,
/// },
/// TP_fast_assign{
/// a:a,
/// b:{
/// // do something with b
/// b
/// }
/// },
/// TP_ident(__entry),
/// TP_printk({
/// // do something with __entry
/// format!("Hello from tracepoint! a={}, b={}", __entry.a, __entry.b)
/// })
/// );
/// ```
#[macro_export]
macro_rules! define_event_trace{
(
$name:ident,
TP_system($system:ident),
TP_PROTO($($arg:ident:$arg_type:ty),*),
TP_STRUCT__entry{$($entry:ident:$entry_type:ty,)*},
TP_fast_assign{$($assign:ident:$value:expr,)*},
TP_ident($tp_ident:ident),
TP_printk($fmt_expr: expr)
) => {
paste::paste!{
static_keys::define_static_key_false!([<__ $name _KEY>]);
#[allow(non_upper_case_globals)]
#[used]
static [<__ $name>]: $crate::tracepoint::TracePoint = $crate::tracepoint::TracePoint::new(&[<__ $name _KEY>],stringify!($name), stringify!($system),[<trace_fmt_ $name>], [<trace_fmt_show $name>]);
#[inline(always)]
#[allow(non_snake_case)]
pub fn [<trace_ $name>]( $($arg:$arg_type),* ){
if static_keys::static_branch_unlikely!([<__ $name _KEY>]){
let mut f = |trace_func: &$crate::tracepoint::TracePointFunc |{
let func = trace_func.func;
let data = trace_func.data.as_ref();
let func = unsafe{core::mem::transmute::<fn(),fn(& (dyn core::any::Any+Send+Sync), $($arg_type),*)>(func)};
func(data $(,$arg)*);
};
let trace_point = &[<__ $name>];
trace_point.callback_list(&mut f);
}
}
#[allow(unused,non_snake_case)]
pub fn [<register_trace_ $name>](func: fn(& (dyn core::any::Any+Send+Sync), $($arg_type),*), data: alloc::boxed::Box<dyn core::any::Any+Send+Sync>){
let func = unsafe{core::mem::transmute::<fn(& (dyn core::any::Any+Send+Sync), $($arg_type),*), fn()>(func)};
[<__ $name>].register(func,data);
}
#[allow(unused,non_snake_case)]
pub fn [<unregister_trace_ $name>](func: fn(& (dyn core::any::Any+Send+Sync), $($arg_type),*)){
let func = unsafe{core::mem::transmute::<fn(& (dyn core::any::Any+Send+Sync), $($arg_type),*), fn()>(func)};
[<__ $name>].unregister(func);
}
#[derive(Debug)]
#[repr(C)]
#[allow(non_snake_case,non_camel_case_types)]
struct [<__ $name _TracePointMeta>]{
trace_point: &'static $crate::tracepoint::TracePoint,
print_func: fn(&mut (dyn core::any::Any+Send+Sync), $($arg_type),*),
}
#[allow(non_upper_case_globals)]
#[link_section = ".tracepoint"]
#[used]
static [<__ $name _meta>]: [<__ $name _TracePointMeta>] = [<__ $name _TracePointMeta>]{
trace_point:& [<__ $name>],
print_func:[<trace_default_ $name>],
};
#[allow(unused,non_snake_case)]
pub fn [<trace_default_ $name>](_data:&mut (dyn core::any::Any+Send+Sync), $($arg:$arg_type),* ){
#[repr(C)]
struct Entry {
$($entry: $entry_type,)*
}
#[repr(C)]
struct FullEntry {
common: $crate::tracepoint::TraceEntry,
entry: Entry,
}
let entry = Entry {
$($assign: $value,)*
};
let process = $crate::process::ProcessManager::current_pcb();
let pid = process.pid().data() as _;
let common = $crate::tracepoint::TraceEntry {
type_: [<__ $name>].id() as _,
flags: [<__ $name>].flags(),
preempt_count: 0,
pid,
};
let full_entry = FullEntry {
common,
entry,
};
let event_buf = unsafe {
core::slice::from_raw_parts(
&full_entry as *const FullEntry as *const u8,
core::mem::size_of::<FullEntry>(),
)
};
$crate::debug::tracing::trace_cmdline_push(pid as u32);
$crate::debug::tracing::trace_pipe_push_raw_record(event_buf);
}
#[allow(unused,non_snake_case)]
pub fn [<trace_fmt_ $name>](buf_ptr: *const u8) -> alloc::string::String {
#[repr(C)]
struct Entry {
$($entry: $entry_type,)*
}
let $tp_ident = unsafe {
&*(buf_ptr as *const Entry)
};
let fmt = format!("{}", $fmt_expr);
fmt
}
#[allow(unused,non_snake_case)]
pub fn [<trace_fmt_show $name>]()-> alloc::string::String {
let mut fmt = format!("format:
\tfield: u16 common_type; offset: 0; size: 2; signed: 0;
\tfield: u8 common_flags; offset: 2; size: 1; signed: 0;
\tfield: u8 common_preempt_count; offset: 3; size: 1; signed: 0;
\tfield: i32 common_pid; offset: 4; size: 4; signed: 1;
");
fn is_signed<T>() -> bool {
match core::any::type_name::<T>() {
"i8" | "i16" | "i32" | "i64" | "i128" | "isize" => true,
_ => false,
}
}
let mut offset = 8;
$(
fmt.push_str(&format!("\tfield: {} {} offset: {}; size: {}; signed: {};\n",
stringify!($entry_type), stringify!($entry), offset, core::mem::size_of::<$entry_type>(), if is_signed::<$entry_type>() { 1 } else { 0 }));
offset += core::mem::size_of::<$entry_type>();
)*
fmt.push_str(&format!("\nprint fmt: \"{}\"", stringify!($fmt_expr)));
fmt
}
}
};
}

View File

@ -0,0 +1,293 @@
#![allow(clippy::new_without_default)]
mod basic_macro;
mod point;
mod trace_pipe;
use alloc::{
boxed::Box,
collections::BTreeMap,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use core::{
ops::{Deref, DerefMut},
sync::atomic::AtomicUsize,
};
pub use point::{CommonTracePointMeta, TraceEntry, TracePoint, TracePointFunc};
use system_error::SystemError;
pub use trace_pipe::{
TraceCmdLineCache, TraceEntryParser, TracePipeOps, TracePipeRaw, TracePipeSnapshot,
};
use crate::libs::spinlock::{SpinLock, SpinLockGuard};
#[derive(Debug)]
pub struct TracePointMap(BTreeMap<u32, &'static TracePoint>);
impl TracePointMap {
/// Create a new TracePointMap
fn new() -> Self {
Self(BTreeMap::new())
}
}
impl Deref for TracePointMap {
type Target = BTreeMap<u32, &'static TracePoint>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for TracePointMap {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Debug)]
pub struct TracingEventsManager {
subsystems: SpinLock<BTreeMap<String, Arc<EventsSubsystem>>>,
map: SpinLock<TracePointMap>,
}
impl TracingEventsManager {
fn new(map: TracePointMap) -> Self {
Self {
subsystems: SpinLock::new(BTreeMap::new()),
map: SpinLock::new(map),
}
}
/// Get the tracepoint map
pub fn tracepoint_map(&self) -> SpinLockGuard<TracePointMap> {
self.map.lock()
}
/// Create a subsystem by name
///
/// If the subsystem already exists, return the existing subsystem.
fn create_subsystem(&self, subsystem_name: &str) -> Arc<EventsSubsystem> {
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
}
/// Get the subsystem by name
pub fn get_subsystem(&self, subsystem_name: &str) -> Option<Arc<EventsSubsystem>> {
self.subsystems.lock().get(subsystem_name).cloned()
}
#[allow(unused)]
/// Remove the subsystem by name
pub fn remove_subsystem(&self, subsystem_name: &str) -> Option<Arc<EventsSubsystem>> {
self.subsystems.lock().remove(subsystem_name)
}
/// Get all subsystems
pub fn subsystem_names(&self) -> Vec<String> {
let res = self
.subsystems
.lock()
.keys()
.cloned()
.collect::<Vec<String>>();
res
}
}
#[derive(Debug)]
pub struct EventsSubsystem {
events: SpinLock<BTreeMap<String, Arc<TracePointInfo>>>,
}
impl EventsSubsystem {
fn new() -> Self {
Self {
events: SpinLock::new(BTreeMap::new()),
}
}
/// Create an event by name
fn create_event(&self, event_name: &str, event_info: TracePointInfo) {
self.events
.lock()
.insert(event_name.to_string(), Arc::new(event_info));
}
/// Get the event by name
pub fn get_event(&self, event_name: &str) -> Option<Arc<TracePointInfo>> {
self.events.lock().get(event_name).cloned()
}
/// Get all events in the subsystem
pub fn event_names(&self) -> Vec<String> {
let res = self.events.lock().keys().cloned().collect::<Vec<String>>();
res
}
}
#[derive(Debug)]
pub struct TracePointInfo {
enable: TracePointEnableFile,
tracepoint: &'static TracePoint,
format: TracePointFormatFile,
id: TracePointIdFile,
// filter:,
// trigger:,
}
impl TracePointInfo {
fn new(tracepoint: &'static TracePoint) -> Self {
let enable = TracePointEnableFile::new(tracepoint);
let format = TracePointFormatFile::new(tracepoint);
let id = TracePointIdFile::new(tracepoint);
Self {
enable,
tracepoint,
format,
id,
}
}
/// Get the tracepoint
pub fn tracepoint(&self) -> &'static TracePoint {
self.tracepoint
}
/// Get the enable file
pub fn enable_file(&self) -> &TracePointEnableFile {
&self.enable
}
/// Get the format file
pub fn format_file(&self) -> &TracePointFormatFile {
&self.format
}
/// Get the ID file
pub fn id_file(&self) -> &TracePointIdFile {
&self.id
}
}
/// TracePointFormatFile provides a way to get the format of the tracepoint.
#[derive(Debug, Clone)]
pub struct TracePointFormatFile {
tracepoint: &'static TracePoint,
}
impl TracePointFormatFile {
fn new(tracepoint: &'static TracePoint) -> Self {
Self { tracepoint }
}
/// Read the tracepoint format
///
/// Returns the format string of the tracepoint.
pub fn read(&self) -> String {
self.tracepoint.print_fmt()
}
}
#[derive(Debug, Clone)]
pub struct TracePointEnableFile {
tracepoint: &'static TracePoint,
}
impl TracePointEnableFile {
fn new(tracepoint: &'static TracePoint) -> Self {
Self { tracepoint }
}
/// Read the tracepoint status
///
/// Returns true if the tracepoint is enabled, false otherwise.
pub fn read(&self) -> &'static str {
if self.tracepoint.is_enabled() {
"1\n"
} else {
"0\n"
}
}
/// Enable or disable the tracepoint
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 {
tracepoint: &'static TracePoint,
}
impl TracePointIdFile {
fn new(tracepoint: &'static TracePoint) -> Self {
Self { tracepoint }
}
/// Read the tracepoint ID
///
/// Returns the ID of the tracepoint.
pub fn read(&self) -> String {
format!("{}\n", self.tracepoint.id())
}
}
extern "C" {
fn _tracepoint();
fn _etracepoint();
}
/// Initialize the tracing events
pub fn global_init_events() -> Result<TracingEventsManager, SystemError> {
static TRACE_POINT_ID: AtomicUsize = AtomicUsize::new(0);
let events_manager = TracingEventsManager::new(TracePointMap::new());
let tracepoint_data_start = _tracepoint as usize as *mut CommonTracePointMeta;
let tracepoint_data_end = _etracepoint as usize as *mut CommonTracePointMeta;
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>();
let tracepoint_data =
unsafe { core::slice::from_raw_parts_mut(tracepoint_data_start, tracepoint_data_len) };
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 = TracePointInfo::new(tracepoint);
subsys.create_event(tracepoint.name(), event_info);
}
drop(tracepoint_map); // Release the lock on the tracepoint map
Ok(events_manager)
}

View File

@ -0,0 +1,179 @@
use crate::libs::spinlock::SpinLock;
use alloc::{boxed::Box, collections::BTreeMap, format, string::String};
use core::{any::Any, sync::atomic::AtomicU32};
use static_keys::StaticFalseKey;
#[derive(Debug)]
#[repr(C)]
pub struct TraceEntry {
pub type_: u16,
pub flags: u8,
pub preempt_count: u8,
pub pid: i32,
}
impl TraceEntry {
pub fn trace_print_lat_fmt(&self) -> String {
// todo!("Implement IRQs off logic");
let irqs_off = '.';
let resched = '.';
let hardsoft_irq = '.';
let mut preempt_low = '.';
if self.preempt_count & 0xf != 0 {
preempt_low = ((b'0') + (self.preempt_count & 0xf)) as char;
}
let mut preempt_high = '.';
if self.preempt_count >> 4 != 0 {
preempt_high = ((b'0') + (self.preempt_count >> 4)) as char;
}
format!(
"{}{}{}{}{}",
irqs_off, resched, hardsoft_irq, preempt_low, preempt_high
)
}
}
pub struct TracePoint {
name: &'static str,
system: &'static str,
key: &'static StaticFalseKey,
id: AtomicU32,
inner: SpinLock<TracePointInner>,
trace_entry_fmt_func: fn(*const u8) -> String,
trace_print_func: fn() -> String,
flags: u8,
}
struct TracePointInner {
callback: BTreeMap<usize, TracePointFunc>,
}
impl core::fmt::Debug for TracePoint {
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 {
pub trace_point: &'static TracePoint,
pub print_func: fn(),
}
#[derive(Debug)]
pub struct TracePointFunc {
pub func: fn(),
pub data: Box<dyn Any + Send + Sync>,
}
impl TracePoint {
pub const fn new(
key: &'static StaticFalseKey,
name: &'static str,
system: &'static str,
fmt_func: fn(*const u8) -> String,
trace_print_func: fn() -> String,
) -> Self {
Self {
name,
system,
key,
id: AtomicU32::new(0),
flags: 0,
trace_entry_fmt_func: fmt_func,
trace_print_func,
inner: SpinLock::new(TracePointInner {
callback: BTreeMap::new(),
}),
}
}
/// Returns the name of the tracepoint.
pub fn name(&self) -> &'static str {
self.name
}
/// Returns the system of the tracepoint.
pub fn system(&self) -> &'static str {
self.system
}
/// Sets the ID of the tracepoint.
pub(crate) fn set_id(&self, id: u32) {
self.id.store(id, core::sync::atomic::Ordering::Relaxed);
}
/// Returns the ID of the tracepoint.
pub fn id(&self) -> u32 {
self.id.load(core::sync::atomic::Ordering::Relaxed)
}
/// Returns the flags of the tracepoint.
pub fn flags(&self) -> u8 {
self.flags
}
/// Returns the format function for the tracepoint.
pub(crate) fn fmt_func(&self) -> fn(*const u8) -> String {
self.trace_entry_fmt_func
}
/// Returns a string representation of the format function for the tracepoint.
///
/// You can use `cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_openat/format` in linux
/// to see the format of the tracepoint.
pub fn print_fmt(&self) -> String {
let post_str = (self.trace_print_func)();
format!("name: {}\nID: {}\n{}\n", self.name(), self.id(), post_str)
}
/// Register a callback function to the tracepoint
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.inner
.lock()
.callback
.entry(ptr)
.or_insert(trace_point_func);
}
/// Unregister a callback function from the tracepoint
pub fn unregister(&self, func: fn()) {
let func_ptr = func as usize;
self.inner.lock().callback.remove(&func_ptr);
}
/// Iterate over all registered callback functions
pub fn callback_list(&self, f: &dyn Fn(&TracePointFunc)) {
let inner = self.inner.lock();
for trace_func in inner.callback.values() {
f(trace_func);
}
}
/// Enable the tracepoint
pub fn enable(&self) {
unsafe {
self.key.enable();
}
}
/// Disable the tracepoint
pub fn disable(&self) {
unsafe {
self.key.disable();
}
}
/// Check if the tracepoint is enabled
pub fn is_enabled(&self) -> bool {
self.key.is_enabled()
}
}

View File

@ -0,0 +1,221 @@
use crate::tracepoint::{TraceEntry, TracePointMap};
use alloc::{format, string::String, vec::Vec};
pub trait TracePipeOps {
/// Returns the first event in the trace pipe buffer without removing it.
fn peek(&self) -> Option<&Vec<u8>>;
/// Remove and return the first event in the trace pipe buffer.
fn pop(&mut self) -> Option<Vec<u8>>;
/// Whether the trace pipe buffer is empty.
fn is_empty(&self) -> bool;
}
/// A raw trace pipe buffer that stores trace events as byte vectors.
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(),
}
}
/// Set the maximum number of records to keep in the trace pipe buffer.
///
/// If the current number of records exceeds this limit, the oldest records will be removed.
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); // Keep only the latest records
}
}
/// Push a new event into the trace pipe buffer.
pub fn push_event(&mut self, event: Vec<u8>) {
if self.event_buf.len() >= self.max_record {
self.event_buf.remove(0); // Remove the oldest record
}
self.event_buf.push(event);
}
/// The number of events currently in the trace pipe buffer.
pub fn event_count(&self) -> usize {
self.event_buf.len()
}
/// Clear the trace pipe buffer.
pub fn clear(&mut self) {
self.event_buf.clear();
}
/// Create a snapshot of the current state of the trace pipe buffer.
pub fn snapshot(&self) -> TracePipeSnapshot {
TracePipeSnapshot::new(self.event_buf.clone())
}
}
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)
}
/// The formatted string representation to be used as a header for the trace pipe output.
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()
}
}
/// A cache for storing command line arguments for each trace point.
///
/// See https://www.kernel.org/doc/Documentation/trace/ftrace.txt
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,
}
}
/// Insert a command line argument for a trace point.
///
/// If the command line exceeds 16 bytes, it will be truncated.
/// If the cache exceeds the maximum record limit, the oldest entry will be removed.
pub fn insert(&mut self, id: u32, cmdline: String) {
if self.cmdline.len() >= self.max_record {
// Remove the oldest entry if we exceed the max record limit
self.cmdline.remove(0);
}
let mut cmdline_bytes = [0u8; 16];
if cmdline.len() > 16 {
// Truncate to fit the fixed size
cmdline_bytes.copy_from_slice(&cmdline.as_bytes()[..16]);
} else {
// Copy the command line bytes into the fixed size array
cmdline_bytes[..cmdline.len()].copy_from_slice(cmdline.as_bytes());
}
self.cmdline.push((id, cmdline_bytes));
}
/// Get the command line argument for a trace point.
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
}
})
}
/// Set the maximum length for command line arguments.
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); // Keep only the latest records
}
}
}
pub struct TraceEntryParser;
impl TraceEntryParser {
/// Parse the trace entry and return a formatted string.
pub fn parse(
tracepoint_map: &TracePointMap,
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(unsafe { entry.as_ptr().add(offset) });
let time = crate::time::Instant::now().total_micros() * 1000; // Convert to nanoseconds
let cpu_id = crate::arch::cpu::current_cpu_id().data();
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,
trace_entry.pid,
cpu_id,
trace_entry.trace_print_lat_fmt(),
secs,
usec_rem,
tracepoint.name(),
str
)
}
}