mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-08 14:16:47 +00:00
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:
parent
41c7f962c9
commit
58e7943c13
@ -1,135 +1,14 @@
|
|||||||
use crate::debug::tracing::tracepoint::{CommonTracePointMeta, TracePoint};
|
use alloc::string::ToString;
|
||||||
|
|
||||||
use crate::debug::tracing::TracingDirCallBack;
|
use crate::debug::tracing::TracingDirCallBack;
|
||||||
use crate::filesystem::kernfs::callback::{KernCallbackData, KernFSCallback, KernInodePrivateData};
|
use crate::filesystem::kernfs::callback::{KernCallbackData, KernFSCallback, KernInodePrivateData};
|
||||||
use crate::filesystem::kernfs::KernFSInode;
|
use crate::filesystem::kernfs::KernFSInode;
|
||||||
use crate::filesystem::vfs::syscall::ModeType;
|
use crate::filesystem::vfs::syscall::ModeType;
|
||||||
use crate::filesystem::vfs::PollStatus;
|
use crate::filesystem::vfs::PollStatus;
|
||||||
use crate::libs::spinlock::SpinLock;
|
use crate::tracepoint::*;
|
||||||
use alloc::boxed::Box;
|
|
||||||
use alloc::collections::BTreeMap;
|
|
||||||
use alloc::string::{String, ToString};
|
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use system_error::SystemError;
|
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)]
|
#[derive(Debug)]
|
||||||
struct EnableCallBack;
|
struct EnableCallBack;
|
||||||
|
|
||||||
@ -144,22 +23,15 @@ impl KernFSCallback for EnableCallBack {
|
|||||||
buf: &mut [u8],
|
buf: &mut [u8],
|
||||||
offset: usize,
|
offset: usize,
|
||||||
) -> Result<usize, SystemError> {
|
) -> Result<usize, SystemError> {
|
||||||
if offset > 0 {
|
let pri_data = data.private_data().as_ref().unwrap();
|
||||||
return Ok(0);
|
let tracepoint_info = pri_data.debugfs_tracepoint().unwrap();
|
||||||
}
|
let enable_value = tracepoint_info.enable_file().read();
|
||||||
let pri_data = data.private_data();
|
if offset >= enable_value.as_bytes().len() {
|
||||||
match pri_data {
|
return Ok(0); // Offset is beyond the length of the string
|
||||||
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 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(
|
fn write(
|
||||||
@ -168,31 +40,91 @@ impl KernFSCallback for EnableCallBack {
|
|||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
_offset: usize,
|
_offset: usize,
|
||||||
) -> Result<usize, SystemError> {
|
) -> Result<usize, SystemError> {
|
||||||
let pri_data = data.private_data();
|
let pri_data = data.private_data().as_ref().unwrap();
|
||||||
match pri_data {
|
let tracepoint = pri_data.debugfs_tracepoint().unwrap();
|
||||||
Some(pri_data) => {
|
if buf.is_empty() {
|
||||||
let tracepoint = pri_data.debugfs_tracepoint().ok_or(SystemError::EINVAL)?;
|
return Err(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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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> {
|
fn poll(&self, _data: KernCallbackData) -> Result<PollStatus, SystemError> {
|
||||||
@ -202,38 +134,62 @@ impl KernFSCallback for EnableCallBack {
|
|||||||
|
|
||||||
static mut TRACING_EVENTS_MANAGER: Option<TracingEventsManager> = None;
|
static mut TRACING_EVENTS_MANAGER: Option<TracingEventsManager> = None;
|
||||||
|
|
||||||
/// Initialize the tracing events
|
pub fn tracing_events_manager() -> &'static TracingEventsManager {
|
||||||
pub fn init_events(events_root: Arc<KernFSInode>) -> Result<(), SystemError> {
|
unsafe {
|
||||||
let events_manager = TracingEventsManager::new(events_root);
|
TRACING_EVENTS_MANAGER
|
||||||
let tracepoint_data_start = _tracepoint as usize as *const CommonTracePointMeta;
|
.as_ref()
|
||||||
let tracepoint_data_end = _etracepoint as usize as *const CommonTracePointMeta;
|
.expect("TracingEventsManager not initialized")
|
||||||
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 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 {
|
unsafe {
|
||||||
TRACING_EVENTS_MANAGER = Some(events_manager);
|
TRACING_EVENTS_MANAGER = Some(events_manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
extern "C" {
|
|
||||||
fn _tracepoint();
|
|
||||||
fn _etracepoint();
|
|
||||||
}
|
|
||||||
|
@ -1,61 +1,49 @@
|
|||||||
mod events;
|
mod events;
|
||||||
pub mod trace_pipe;
|
pub mod trace_pipe;
|
||||||
pub mod tracepoint;
|
|
||||||
|
|
||||||
use crate::debug::sysfs::debugfs_kset;
|
use crate::debug::sysfs::debugfs_kset;
|
||||||
use crate::debug::tracing::trace_pipe::TRACE_PIPE_MAX_RECORD;
|
|
||||||
use crate::driver::base::kobject::KObject;
|
use crate::driver::base::kobject::KObject;
|
||||||
use crate::filesystem::kernfs::callback::{KernCallbackData, KernFSCallback, KernInodePrivateData};
|
use crate::filesystem::kernfs::callback::{KernCallbackData, KernFSCallback, KernInodePrivateData};
|
||||||
use crate::filesystem::kernfs::KernFSInode;
|
use crate::filesystem::kernfs::KernFSInode;
|
||||||
use crate::filesystem::vfs::syscall::ModeType;
|
use crate::filesystem::vfs::syscall::ModeType;
|
||||||
use crate::filesystem::vfs::PollStatus;
|
use crate::filesystem::vfs::PollStatus;
|
||||||
|
use crate::libs::spinlock::SpinLock;
|
||||||
|
use crate::tracepoint::TracePointInfo;
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use system_error::SystemError;
|
use system_error::SystemError;
|
||||||
use tracepoint::TracePoint;
|
|
||||||
|
|
||||||
static mut TRACING_ROOT_INODE: Option<Arc<KernFSInode>> = None;
|
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)]
|
#[allow(unused)]
|
||||||
fn tracing_root_inode() -> Arc<KernFSInode> {
|
fn tracing_root_inode() -> Arc<KernFSInode> {
|
||||||
unsafe { TRACING_ROOT_INODE.clone().unwrap() }
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct TracingDirCallBack;
|
pub struct TracingDirCallBack;
|
||||||
|
|
||||||
@ -88,10 +76,60 @@ impl KernFSCallback for TracingDirCallBack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl KernInodePrivateData {
|
impl KernInodePrivateData {
|
||||||
pub fn debugfs_tracepoint(&self) -> Option<&'static TracePoint> {
|
pub fn debugfs_tracepoint(&self) -> Option<&Arc<TracePointInfo>> {
|
||||||
return match self {
|
return match self {
|
||||||
KernInodePrivateData::DebugFS(tracepoint) => Some(tracepoint),
|
KernInodePrivateData::DebugFS(tracepoint) => Some(tracepoint),
|
||||||
_ => None,
|
_ => 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(())
|
||||||
}
|
}
|
||||||
|
@ -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::filesystem::vfs::PollStatus;
|
||||||
use crate::libs::spinlock::SpinLock;
|
use crate::libs::wait_queue::WaitQueue;
|
||||||
use alloc::string::String;
|
use crate::process::{ProcessFlags, ProcessManager};
|
||||||
use alloc::vec::Vec;
|
use crate::sched::SchedMode;
|
||||||
|
use crate::tracepoint::{TraceEntryParser, TracePipeOps};
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use system_error::SystemError;
|
use system_error::SystemError;
|
||||||
|
|
||||||
static mut TRACE_PIPE: Option<TracePipe> = None;
|
fn common_trace_pipe_read(
|
||||||
|
trace_buf: &mut dyn TracePipeOps,
|
||||||
pub const TRACE_PIPE_MAX_RECORD: usize = 4096;
|
buf: &mut [u8],
|
||||||
|
) -> Result<usize, SystemError> {
|
||||||
pub fn init_trace_pipe() {
|
let manager = super::events::tracing_events_manager();
|
||||||
unsafe {
|
let tracepint_map = manager.tracepoint_map();
|
||||||
TRACE_PIPE = Some(TracePipe::new(TRACE_PIPE_MAX_RECORD));
|
let trace_cmdline_cache = super::TRACE_CMDLINE_CACHE.lock();
|
||||||
}
|
// read real trace data
|
||||||
}
|
let mut copy_len = 0;
|
||||||
|
let mut peek_flag = false;
|
||||||
pub fn trace_pipe() -> &'static TracePipe {
|
loop {
|
||||||
unsafe { TRACE_PIPE.as_ref().unwrap() }
|
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() {
|
||||||
/// Push a record to trace pipe
|
break; // Buffer is full
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
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);
|
if peek_flag {
|
||||||
self.size += record_size;
|
trace_buf.pop(); // Remove the record after reading
|
||||||
}
|
peek_flag = false;
|
||||||
|
} else {
|
||||||
pub fn read_at(&self, buf: &mut [u8], offset: usize) -> Result<usize, SystemError> {
|
break; // No more records to read
|
||||||
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)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn push_record(&self, record: String) {
|
Ok(copy_len)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TracePipeCallBack;
|
pub struct TraceCallBack;
|
||||||
|
|
||||||
impl KernFSCallback for TracePipeCallBack {
|
impl KernFSCallback for TraceCallBack {
|
||||||
fn open(&self, _data: KernCallbackData) -> Result<(), SystemError> {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read(
|
fn read(
|
||||||
&self,
|
&self,
|
||||||
_data: KernCallbackData,
|
mut data: KernCallbackData,
|
||||||
buf: &mut [u8],
|
buf: &mut [u8],
|
||||||
offset: usize,
|
offset: usize,
|
||||||
) -> Result<usize, SystemError> {
|
) -> Result<usize, SystemError> {
|
||||||
let trace_pipe = trace_pipe();
|
let pri_data = data.private_data_mut().as_mut().unwrap();
|
||||||
trace_pipe.read_at(buf, offset)
|
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(
|
fn write(
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
use super::KernFSInode;
|
use super::KernFSInode;
|
||||||
use crate::debug::tracing::tracepoint::TracePoint;
|
use crate::tracepoint::{TracePipeSnapshot, TracePointInfo};
|
||||||
use crate::{
|
use crate::{
|
||||||
filesystem::{sysfs::SysFSKernPrivateData, vfs::PollStatus},
|
filesystem::{sysfs::SysFSKernPrivateData, vfs::PollStatus},
|
||||||
libs::spinlock::SpinLockGuard,
|
libs::spinlock::SpinLockGuard,
|
||||||
@ -86,7 +86,8 @@ impl<'a> KernCallbackData<'a> {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum KernInodePrivateData {
|
pub enum KernInodePrivateData {
|
||||||
SysFS(SysFSKernPrivateData),
|
SysFS(SysFSKernPrivateData),
|
||||||
DebugFS(&'static TracePoint),
|
DebugFS(Arc<TracePointInfo>),
|
||||||
|
TracePipe(TracePipeSnapshot),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KernInodePrivateData {
|
impl KernInodePrivateData {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use alloc::string::ToString;
|
||||||
use core::{cmp::min, fmt::Debug, intrinsics::unlikely};
|
use core::{cmp::min, fmt::Debug, intrinsics::unlikely};
|
||||||
|
|
||||||
use alloc::{
|
use alloc::{
|
||||||
@ -115,6 +116,7 @@ impl KernFS {
|
|||||||
callback: None,
|
callback: None,
|
||||||
children: SpinLock::new(HashMap::new()),
|
children: SpinLock::new(HashMap::new()),
|
||||||
inode_type: KernInodeType::Dir,
|
inode_type: KernInodeType::Dir,
|
||||||
|
lazy_list: SpinLock::new(HashMap::new()),
|
||||||
});
|
});
|
||||||
|
|
||||||
return root_inode;
|
return root_inode;
|
||||||
@ -138,6 +140,16 @@ pub struct KernFSInode {
|
|||||||
inode_type: KernInodeType,
|
inode_type: KernInodeType,
|
||||||
/// Inode名称
|
/// Inode名称
|
||||||
name: String,
|
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)]
|
#[derive(Debug)]
|
||||||
@ -245,12 +257,25 @@ impl IndexNode for KernFSInode {
|
|||||||
}
|
}
|
||||||
name => {
|
name => {
|
||||||
// 在子目录项中查找
|
// 在子目录项中查找
|
||||||
return Ok(self
|
let child = self.children.lock().get(name).cloned();
|
||||||
.children
|
if let Some(child) = child {
|
||||||
.lock()
|
return Ok(child);
|
||||||
.get(name)
|
}
|
||||||
.ok_or(SystemError::ENOENT)?
|
let lazy_list = self.lazy_list.lock();
|
||||||
.clone());
|
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,
|
offset: usize,
|
||||||
len: usize,
|
len: usize,
|
||||||
buf: &mut [u8],
|
buf: &mut [u8],
|
||||||
_data: SpinLockGuard<FilePrivateData>,
|
data: SpinLockGuard<FilePrivateData>,
|
||||||
) -> Result<usize, SystemError> {
|
) -> Result<usize, SystemError> {
|
||||||
if self.inode_type == KernInodeType::SymLink {
|
if self.inode_type == KernInodeType::SymLink {
|
||||||
let inner = self.inner.read();
|
let inner = self.inner.read();
|
||||||
@ -350,6 +375,8 @@ impl IndexNode for KernFSInode {
|
|||||||
warn!("kernfs: callback is none");
|
warn!("kernfs: callback is none");
|
||||||
return Err(SystemError::ENOSYS);
|
return Err(SystemError::ENOSYS);
|
||||||
}
|
}
|
||||||
|
// release the private data lock before calling the callback
|
||||||
|
drop(data);
|
||||||
|
|
||||||
let callback_data =
|
let callback_data =
|
||||||
KernCallbackData::new(self.self_ref.upgrade().unwrap(), self.private_data.lock());
|
KernCallbackData::new(self.self_ref.upgrade().unwrap(), self.private_data.lock());
|
||||||
@ -365,7 +392,7 @@ impl IndexNode for KernFSInode {
|
|||||||
offset: usize,
|
offset: usize,
|
||||||
len: usize,
|
len: usize,
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
_data: SpinLockGuard<FilePrivateData>,
|
data: SpinLockGuard<FilePrivateData>,
|
||||||
) -> Result<usize, SystemError> {
|
) -> Result<usize, SystemError> {
|
||||||
if self.inode_type != KernInodeType::File {
|
if self.inode_type != KernInodeType::File {
|
||||||
return Err(SystemError::EISDIR);
|
return Err(SystemError::EISDIR);
|
||||||
@ -375,6 +402,9 @@ impl IndexNode for KernFSInode {
|
|||||||
return Err(SystemError::ENOSYS);
|
return Err(SystemError::ENOSYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// release the private data lock before calling the callback
|
||||||
|
drop(data);
|
||||||
|
|
||||||
let callback_data =
|
let callback_data =
|
||||||
KernCallbackData::new(self.self_ref.upgrade().unwrap(), self.private_data.lock());
|
KernCallbackData::new(self.self_ref.upgrade().unwrap(), self.private_data.lock());
|
||||||
return self
|
return self
|
||||||
@ -411,6 +441,7 @@ impl KernFSInode {
|
|||||||
callback,
|
callback,
|
||||||
children: SpinLock::new(HashMap::new()),
|
children: SpinLock::new(HashMap::new()),
|
||||||
inode_type,
|
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(
|
fn inner_create(
|
||||||
&self,
|
&self,
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -5,7 +5,7 @@ use log::{error, info};
|
|||||||
use system_error::SystemError;
|
use system_error::SystemError;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
define_event_trace, define_trace_point,
|
define_event_trace,
|
||||||
driver::base::block::{gendisk::GenDisk, manager::block_dev_manager},
|
driver::base::block::{gendisk::GenDisk, manager::block_dev_manager},
|
||||||
filesystem::{
|
filesystem::{
|
||||||
devfs::devfs_init,
|
devfs::devfs_init,
|
||||||
@ -167,15 +167,39 @@ pub fn mount_root_fs() -> Result<(), SystemError> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
define_event_trace!(DO_MKDIR_AT,(path:&str,mode:FileMode),
|
define_event_trace!(
|
||||||
format_args!("mkdir at {} with mode {:?}",path, mode));
|
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 创建文件/文件夹
|
/// @brief 创建文件/文件夹
|
||||||
pub fn do_mkdir_at(
|
pub fn do_mkdir_at(
|
||||||
dirfd: i32,
|
dirfd: i32,
|
||||||
path: &str,
|
path: &str,
|
||||||
mode: FileMode,
|
mode: FileMode,
|
||||||
) -> Result<Arc<dyn IndexNode>, SystemError> {
|
) -> Result<Arc<dyn IndexNode>, SystemError> {
|
||||||
TRACE_DO_MKDIR_AT(path, mode);
|
trace_do_mkdir_at(path, mode);
|
||||||
// debug!("Call do mkdir at");
|
// debug!("Call do mkdir at");
|
||||||
let (mut current_inode, path) =
|
let (mut current_inode, path) =
|
||||||
user_path_at(&ProcessManager::current_pcb(), dirfd, path.trim())?;
|
user_path_at(&ProcessManager::current_pcb(), dirfd, path.trim())?;
|
||||||
|
@ -23,7 +23,12 @@
|
|||||||
#![feature(vec_into_raw_parts)]
|
#![feature(vec_into_raw_parts)]
|
||||||
#![feature(linkage)]
|
#![feature(linkage)]
|
||||||
#![feature(panic_can_unwind)]
|
#![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的配置
|
// clippy的配置
|
||||||
#![deny(clippy::all)]
|
#![deny(clippy::all)]
|
||||||
// 取消下面的注释以启用clippy对栈帧大小的检查
|
// 取消下面的注释以启用clippy对栈帧大小的检查
|
||||||
@ -70,6 +75,7 @@ mod sched;
|
|||||||
mod smp;
|
mod smp;
|
||||||
mod syscall;
|
mod syscall;
|
||||||
mod time;
|
mod time;
|
||||||
|
mod tracepoint;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
mod virt;
|
mod virt;
|
||||||
|
|
||||||
|
175
kernel/src/tracepoint/basic_macro.rs
Normal file
175
kernel/src/tracepoint/basic_macro.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
293
kernel/src/tracepoint/mod.rs
Normal file
293
kernel/src/tracepoint/mod.rs
Normal 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)
|
||||||
|
}
|
179
kernel/src/tracepoint/point.rs
Normal file
179
kernel/src/tracepoint/point.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
221
kernel/src/tracepoint/trace_pipe.rs
Normal file
221
kernel/src/tracepoint/trace_pipe.rs
Normal 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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user