diff --git a/kernel/src/debug/tracing/events.rs b/kernel/src/debug/tracing/events.rs index 3b3223d9..5ab123a0 100644 --- a/kernel/src/debug/tracing/events.rs +++ b/kernel/src/debug/tracing/events.rs @@ -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, - subsystems: SpinLock>>, -} - -impl TracingEventsManager { - pub fn new(root: Arc) -> 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, 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> { - self.subsystems.lock().get(subsystem_name).cloned() - } -} - -#[derive(Debug)] -pub struct EventsSubsystem { - root: Arc, - events: SpinLock>>, -} - -impl EventsSubsystem { - pub fn new(root: Arc) -> 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, - ) -> 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> { - self.events.lock().get(event_name).cloned() - } - - /// Get the root inode of the subsystem - pub fn root(&self) -> Arc { - self.root.clone() - } -} - -#[derive(Debug)] -pub struct EventInfo { - #[allow(unused)] - enable: Arc, - // filter: Arc, - // trigger: Arc, -} - -impl EventInfo { - pub fn new(tracepoint: &'static TracePoint, subsystem: Arc) -> Arc { - 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 { - 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 { - 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 { + 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 { + 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 { + Err(SystemError::EPERM) + } + + fn poll(&self, _data: KernCallbackData) -> Result { + 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 { + 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 { + Err(SystemError::EPERM) } fn poll(&self, _data: KernCallbackData) -> Result { @@ -202,38 +134,62 @@ impl KernFSCallback for EnableCallBack { static mut TRACING_EVENTS_MANAGER: Option = None; -/// Initialize the tracing events -pub fn init_events(events_root: Arc) -> 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::(); - 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) -> 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(); -} diff --git a/kernel/src/debug/tracing/mod.rs b/kernel/src/debug/tracing/mod.rs index d2235a29..130bebe0 100644 --- a/kernel/src/debug/tracing/mod.rs +++ b/kernel/src/debug/tracing/mod.rs @@ -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> = None; +static TRACE_RAW_PIPE: SpinLock = + SpinLock::new(crate::tracepoint::TracePipeRaw::new(4096)); + +static TRACE_CMDLINE_CACHE: SpinLock = + 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 { 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> { 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(()) } diff --git a/kernel/src/debug/tracing/trace_pipe.rs b/kernel/src/debug/tracing/trace_pipe.rs index 7c5c8a4e..6fa849e4 100644 --- a/kernel/src/debug/tracing/trace_pipe.rs +++ b/kernel/src/debug/tracing/trace_pipe.rs @@ -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 = 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, -} - -struct TracePipeBuf { - size: usize, - max_record: usize, - buf: Vec, -} - -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 { + 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 { - 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 { - 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 { - 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 { + Err(SystemError::EPERM) + } + + fn poll(&self, _data: KernCallbackData) -> Result { + 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 { + 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( diff --git a/kernel/src/debug/tracing/tracepoint.rs b/kernel/src/debug/tracing/tracepoint.rs deleted file mode 100644 index b9ee0760..00000000 --- a/kernel/src/debug/tracing/tracepoint.rs +++ /dev/null @@ -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, - unregister: Option, - callback: SpinLock>, -} - -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, -} - -impl TracePoint { - pub const fn new( - key: &'static StaticFalseKey, - name: &'static str, - module_path: &'static str, - register: Option, - unregister: Option, - ) -> 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) { - 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> { - 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 []( $($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::(func)}; - func(data $(,$arg)*); - } - } - - } - - #[allow(unused,non_snake_case)] - pub fn [](func:fn(&mut (dyn core::any::Any+Send+Sync),$($arg_type),*),data:alloc::boxed::Box){ - let func = unsafe{core::mem::transmute::(func)}; - [<__ $name>].register(func,data); - } - - #[allow(unused,non_snake_case)] - pub fn [](func:fn(&mut (dyn core::any::Any+Send+Sync),$($arg_type),*)){ - let func = unsafe{core::mem::transmute::(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:[], - }; - #[allow(non_snake_case)] - pub fn [](_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); - } - } - }; -} diff --git a/kernel/src/filesystem/kernfs/callback.rs b/kernel/src/filesystem/kernfs/callback.rs index a6ed1443..db1c96e6 100644 --- a/kernel/src/filesystem/kernfs/callback.rs +++ b/kernel/src/filesystem/kernfs/callback.rs @@ -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), + TracePipe(TracePipeSnapshot), } impl KernInodePrivateData { diff --git a/kernel/src/filesystem/kernfs/mod.rs b/kernel/src/filesystem/kernfs/mod.rs index 867abded..a8eeedd9 100644 --- a/kernel/src/filesystem/kernfs/mod.rs +++ b/kernel/src/filesystem/kernfs/mod.rs @@ -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 KernFSInodeArgs>>, +} + +pub struct KernFSInodeArgs { + pub mode: ModeType, + pub inode_type: KernInodeType, + pub size: Option, + pub private_data: Option, + 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, + data: SpinLockGuard, ) -> Result { 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, + data: SpinLockGuard, ) -> Result { 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, diff --git a/kernel/src/filesystem/vfs/vcore.rs b/kernel/src/filesystem/vfs/vcore.rs index cc422bae..b96796f0 100644 --- a/kernel/src/filesystem/vfs/vcore.rs +++ b/kernel/src/filesystem/vfs/vcore.rs @@ -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, 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())?; diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 50a1287a..b7d37046 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -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; diff --git a/kernel/src/tracepoint/basic_macro.rs b/kernel/src/tracepoint/basic_macro.rs new file mode 100644 index 00000000..1663aad1 --- /dev/null +++ b/kernel/src/tracepoint/basic_macro.rs @@ -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),[], []); + + #[inline(always)] + #[allow(non_snake_case)] + pub fn []( $($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::(func)}; + func(data $(,$arg)*); + }; + let trace_point = &[<__ $name>]; + trace_point.callback_list(&mut f); + } + } + #[allow(unused,non_snake_case)] + pub fn [](func: fn(& (dyn core::any::Any+Send+Sync), $($arg_type),*), data: alloc::boxed::Box){ + let func = unsafe{core::mem::transmute::(func)}; + [<__ $name>].register(func,data); + } + #[allow(unused,non_snake_case)] + pub fn [](func: fn(& (dyn core::any::Any+Send+Sync), $($arg_type),*)){ + let func = unsafe{core::mem::transmute::(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:[], + }; + + #[allow(unused,non_snake_case)] + pub fn [](_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::(), + ) + }; + $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 [](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 []()-> 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() -> bool { + match core::any::type_name::() { + "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 + } + } + }; +} diff --git a/kernel/src/tracepoint/mod.rs b/kernel/src/tracepoint/mod.rs new file mode 100644 index 00000000..b963d43d --- /dev/null +++ b/kernel/src/tracepoint/mod.rs @@ -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); + +impl TracePointMap { + /// Create a new TracePointMap + fn new() -> Self { + Self(BTreeMap::new()) + } +} + +impl Deref for TracePointMap { + type Target = BTreeMap; + + 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>>, + map: SpinLock, +} + +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 { + 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 { + 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> { + self.subsystems.lock().get(subsystem_name).cloned() + } + + #[allow(unused)] + /// Remove the subsystem by name + pub fn remove_subsystem(&self, subsystem_name: &str) -> Option> { + self.subsystems.lock().remove(subsystem_name) + } + + /// Get all subsystems + pub fn subsystem_names(&self) -> Vec { + let res = self + .subsystems + .lock() + .keys() + .cloned() + .collect::>(); + res + } +} + +#[derive(Debug)] +pub struct EventsSubsystem { + events: SpinLock>>, +} + +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> { + self.events.lock().get(event_name).cloned() + } + + /// Get all events in the subsystem + pub fn event_names(&self) -> Vec { + let res = self.events.lock().keys().cloned().collect::>(); + 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 { + 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::(); + 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) +} diff --git a/kernel/src/tracepoint/point.rs b/kernel/src/tracepoint/point.rs new file mode 100644 index 00000000..00275908 --- /dev/null +++ b/kernel/src/tracepoint/point.rs @@ -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, + trace_entry_fmt_func: fn(*const u8) -> String, + trace_print_func: fn() -> String, + flags: u8, +} + +struct TracePointInner { + callback: BTreeMap, +} + +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, +} + +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) { + 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() + } +} diff --git a/kernel/src/tracepoint/trace_pipe.rs b/kernel/src/tracepoint/trace_pipe.rs new file mode 100644 index 00000000..a92a6565 --- /dev/null +++ b/kernel/src/tracepoint/trace_pipe.rs @@ -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>; + + /// Remove and return the first event in the trace pipe buffer. + fn pop(&mut self) -> Option>; + + /// 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>, +} + +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) { + 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> { + self.event_buf.first() + } + + fn pop(&mut self) -> Option> { + 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>); + +impl TracePipeSnapshot { + pub fn new(event_buf: Vec>) -> 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> { + self.0.first() + } + + fn pop(&mut self) -> Option> { + 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::(); + 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 + ) + } +}