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::filesystem::kernfs::callback::{KernCallbackData, KernFSCallback, KernInodePrivateData};
|
||||
use crate::filesystem::kernfs::KernFSInode;
|
||||
use crate::filesystem::vfs::syscall::ModeType;
|
||||
use crate::filesystem::vfs::PollStatus;
|
||||
use crate::libs::spinlock::SpinLock;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::string::{String, ToString};
|
||||
use crate::tracepoint::*;
|
||||
use alloc::sync::Arc;
|
||||
use system_error::SystemError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TracingEventsManager {
|
||||
root: Arc<KernFSInode>,
|
||||
subsystems: SpinLock<BTreeMap<String, Arc<EventsSubsystem>>>,
|
||||
}
|
||||
|
||||
impl TracingEventsManager {
|
||||
pub fn new(root: Arc<KernFSInode>) -> Self {
|
||||
Self {
|
||||
root,
|
||||
subsystems: SpinLock::new(BTreeMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a subsystem by name
|
||||
///
|
||||
/// If the subsystem already exists, return the existing subsystem.
|
||||
pub fn create_subsystem(
|
||||
&self,
|
||||
subsystem_name: &str,
|
||||
) -> Result<Arc<EventsSubsystem>, SystemError> {
|
||||
if self.subsystems.lock().contains_key(subsystem_name) {
|
||||
return Ok(self.get_subsystem(subsystem_name).unwrap());
|
||||
}
|
||||
let dir = self.root.add_dir(
|
||||
subsystem_name.to_string(),
|
||||
ModeType::from_bits_truncate(0o755),
|
||||
None,
|
||||
Some(&TracingDirCallBack),
|
||||
)?;
|
||||
let subsystem = Arc::new(EventsSubsystem::new(dir));
|
||||
self.subsystems
|
||||
.lock()
|
||||
.insert(subsystem_name.to_string(), subsystem.clone());
|
||||
Ok(subsystem)
|
||||
}
|
||||
|
||||
/// Get the subsystem by name
|
||||
pub fn get_subsystem(&self, subsystem_name: &str) -> Option<Arc<EventsSubsystem>> {
|
||||
self.subsystems.lock().get(subsystem_name).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventsSubsystem {
|
||||
root: Arc<KernFSInode>,
|
||||
events: SpinLock<BTreeMap<String, Arc<EventInfo>>>,
|
||||
}
|
||||
|
||||
impl EventsSubsystem {
|
||||
pub fn new(root: Arc<KernFSInode>) -> Self {
|
||||
Self {
|
||||
root,
|
||||
events: SpinLock::new(BTreeMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a new event into the subsystem
|
||||
pub fn insert_event(
|
||||
&self,
|
||||
event_name: &str,
|
||||
event_info: Arc<EventInfo>,
|
||||
) -> Result<(), SystemError> {
|
||||
self.events
|
||||
.lock()
|
||||
.insert(event_name.to_string(), event_info);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the event by name
|
||||
#[allow(unused)]
|
||||
pub fn get_event(&self, event_name: &str) -> Option<Arc<EventInfo>> {
|
||||
self.events.lock().get(event_name).cloned()
|
||||
}
|
||||
|
||||
/// Get the root inode of the subsystem
|
||||
pub fn root(&self) -> Arc<KernFSInode> {
|
||||
self.root.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventInfo {
|
||||
#[allow(unused)]
|
||||
enable: Arc<KernFSInode>,
|
||||
// filter: Arc<KernFSInode>,
|
||||
// trigger: Arc<KernFSInode>,
|
||||
}
|
||||
|
||||
impl EventInfo {
|
||||
pub fn new(tracepoint: &'static TracePoint, subsystem: Arc<KernFSInode>) -> Arc<Self> {
|
||||
let trace_dir = subsystem
|
||||
.add_dir(
|
||||
tracepoint.name().to_string(),
|
||||
ModeType::from_bits_truncate(0o755),
|
||||
None,
|
||||
Some(&TracingDirCallBack),
|
||||
)
|
||||
.expect("add tracepoint dir failed");
|
||||
let enable_inode = trace_dir
|
||||
.add_file(
|
||||
"enable".to_string(),
|
||||
ModeType::from_bits_truncate(0o644),
|
||||
None,
|
||||
Some(KernInodePrivateData::DebugFS(tracepoint)),
|
||||
Some(&EnableCallBack),
|
||||
)
|
||||
.expect("add enable file failed");
|
||||
|
||||
Arc::new(Self {
|
||||
enable: enable_inode,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EventInfo {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EnableCallBack;
|
||||
|
||||
@ -144,22 +23,15 @@ impl KernFSCallback for EnableCallBack {
|
||||
buf: &mut [u8],
|
||||
offset: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
if offset > 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
let pri_data = data.private_data();
|
||||
match pri_data {
|
||||
Some(pri_data) => {
|
||||
let tracepoint = pri_data.debugfs_tracepoint().ok_or(SystemError::EINVAL)?;
|
||||
let buf_value = if tracepoint.is_enabled() { b"1" } else { b"0" };
|
||||
let len = buf.len().min(buf_value.len());
|
||||
buf[..len].copy_from_slice(&buf_value[..len]);
|
||||
Ok(len)
|
||||
}
|
||||
None => {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
let pri_data = data.private_data().as_ref().unwrap();
|
||||
let tracepoint_info = pri_data.debugfs_tracepoint().unwrap();
|
||||
let enable_value = tracepoint_info.enable_file().read();
|
||||
if offset >= enable_value.as_bytes().len() {
|
||||
return Ok(0); // Offset is beyond the length of the string
|
||||
}
|
||||
let len = buf.len().min(enable_value.as_bytes().len() - offset);
|
||||
buf[..len].copy_from_slice(&enable_value.as_bytes()[offset..offset + len]);
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn write(
|
||||
@ -168,31 +40,91 @@ impl KernFSCallback for EnableCallBack {
|
||||
buf: &[u8],
|
||||
_offset: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
let pri_data = data.private_data();
|
||||
match pri_data {
|
||||
Some(pri_data) => {
|
||||
let tracepoint = pri_data.debugfs_tracepoint().ok_or(SystemError::EINVAL)?;
|
||||
let value = core::str::from_utf8(buf)
|
||||
.map_err(|_| SystemError::EINVAL)?
|
||||
.trim();
|
||||
match value {
|
||||
"0" => {
|
||||
tracepoint.disable();
|
||||
}
|
||||
"1" => {
|
||||
tracepoint.enable();
|
||||
}
|
||||
_ => {
|
||||
log::info!("EnableCallBack invalid value: {}", value);
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
None => {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
let pri_data = data.private_data().as_ref().unwrap();
|
||||
let tracepoint = pri_data.debugfs_tracepoint().unwrap();
|
||||
if buf.is_empty() {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
tracepoint.enable_file().write(buf[0] as _);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn poll(&self, _data: KernCallbackData) -> Result<PollStatus, SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FormatCallBack;
|
||||
|
||||
impl KernFSCallback for FormatCallBack {
|
||||
fn open(&self, _data: KernCallbackData) -> Result<(), SystemError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
data: KernCallbackData,
|
||||
buf: &mut [u8],
|
||||
offset: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
let pri_data = data.private_data().as_ref().unwrap();
|
||||
let tracepoint = pri_data.debugfs_tracepoint().unwrap();
|
||||
let format_str = tracepoint.format_file().read();
|
||||
if offset >= format_str.as_bytes().len() {
|
||||
return Ok(0); // Offset is beyond the length of the string
|
||||
}
|
||||
let len = buf.len().min(format_str.as_bytes().len() - offset);
|
||||
buf[..len].copy_from_slice(&format_str.as_bytes()[offset..offset + len]);
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn write(
|
||||
&self,
|
||||
_data: KernCallbackData,
|
||||
_buf: &[u8],
|
||||
_offset: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
Err(SystemError::EPERM)
|
||||
}
|
||||
|
||||
fn poll(&self, _data: KernCallbackData) -> Result<PollStatus, SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct IDCallBack;
|
||||
impl KernFSCallback for IDCallBack {
|
||||
fn open(&self, _data: KernCallbackData) -> Result<(), SystemError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
data: KernCallbackData,
|
||||
buf: &mut [u8],
|
||||
offset: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
let pri_data = data.private_data().as_ref().unwrap();
|
||||
let tracepoint = pri_data.debugfs_tracepoint().unwrap();
|
||||
let id_str = tracepoint.id_file().read();
|
||||
|
||||
if offset >= id_str.as_bytes().len() {
|
||||
return Ok(0); // Offset is beyond the length of the string
|
||||
}
|
||||
let len = buf.len().min(id_str.as_bytes().len() - offset);
|
||||
buf[..len].copy_from_slice(&id_str.as_bytes()[offset..offset + len]);
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn write(
|
||||
&self,
|
||||
_data: KernCallbackData,
|
||||
_buf: &[u8],
|
||||
_offset: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
Err(SystemError::EPERM)
|
||||
}
|
||||
|
||||
fn poll(&self, _data: KernCallbackData) -> Result<PollStatus, SystemError> {
|
||||
@ -202,38 +134,62 @@ impl KernFSCallback for EnableCallBack {
|
||||
|
||||
static mut TRACING_EVENTS_MANAGER: Option<TracingEventsManager> = None;
|
||||
|
||||
/// Initialize the tracing events
|
||||
pub fn init_events(events_root: Arc<KernFSInode>) -> Result<(), SystemError> {
|
||||
let events_manager = TracingEventsManager::new(events_root);
|
||||
let tracepoint_data_start = _tracepoint as usize as *const CommonTracePointMeta;
|
||||
let tracepoint_data_end = _etracepoint as usize as *const CommonTracePointMeta;
|
||||
let tracepoint_data_len = (tracepoint_data_end as usize - tracepoint_data_start as usize)
|
||||
/ size_of::<CommonTracePointMeta>();
|
||||
let tracepoint_data =
|
||||
unsafe { core::slice::from_raw_parts(tracepoint_data_start, tracepoint_data_len) };
|
||||
for tracepoint_meta in tracepoint_data {
|
||||
let tracepoint = tracepoint_meta.trace_point;
|
||||
tracepoint.register(tracepoint_meta.print_func, Box::new(()));
|
||||
log::info!(
|
||||
"tracepoint name: {}, module path: {}",
|
||||
tracepoint.name(),
|
||||
tracepoint.module_path()
|
||||
);
|
||||
// kernel::{subsystem}::
|
||||
let mut subsys_name = tracepoint.module_path().split("::");
|
||||
let subsys_name = subsys_name.nth(1).ok_or(SystemError::EINVAL)?;
|
||||
let subsys = events_manager.create_subsystem(subsys_name)?;
|
||||
let event_info = EventInfo::new(tracepoint, subsys.root());
|
||||
subsys.insert_event(tracepoint.name(), event_info)?;
|
||||
pub fn tracing_events_manager() -> &'static TracingEventsManager {
|
||||
unsafe {
|
||||
TRACING_EVENTS_MANAGER
|
||||
.as_ref()
|
||||
.expect("TracingEventsManager not initialized")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_events(root: Arc<KernFSInode>) -> Result<(), SystemError> {
|
||||
let events_manager = crate::tracepoint::global_init_events()?;
|
||||
// Register the global tracing events manager
|
||||
for subsystem_name in events_manager.subsystem_names() {
|
||||
let subsystem = events_manager.get_subsystem(&subsystem_name).unwrap();
|
||||
// Register the subsystem in the root inode
|
||||
let subsystem_inode = root.add_dir(
|
||||
subsystem_name,
|
||||
ModeType::from_bits_truncate(0o755),
|
||||
None,
|
||||
Some(&TracingDirCallBack),
|
||||
)?;
|
||||
for event_name in subsystem.event_names() {
|
||||
let event_info = subsystem.get_event(&event_name).unwrap();
|
||||
let event_inode = subsystem_inode.add_dir(
|
||||
event_name,
|
||||
ModeType::from_bits_truncate(0o755),
|
||||
None,
|
||||
Some(&TracingDirCallBack),
|
||||
)?;
|
||||
// add enable file for the event
|
||||
let _enable_inode = event_inode.add_file(
|
||||
"enable".to_string(),
|
||||
ModeType::from_bits_truncate(0o644),
|
||||
None,
|
||||
Some(KernInodePrivateData::DebugFS(event_info.clone())),
|
||||
Some(&EnableCallBack),
|
||||
)?;
|
||||
// add format file for the event
|
||||
let _format_inode = event_inode.add_file(
|
||||
"format".to_string(),
|
||||
ModeType::from_bits_truncate(0o644),
|
||||
None,
|
||||
Some(KernInodePrivateData::DebugFS(event_info.clone())),
|
||||
Some(&FormatCallBack),
|
||||
)?;
|
||||
// add id file for the event
|
||||
let _id_inode = event_inode.add_file(
|
||||
"id".to_string(),
|
||||
ModeType::from_bits_truncate(0o644),
|
||||
None,
|
||||
Some(KernInodePrivateData::DebugFS(event_info)),
|
||||
Some(&IDCallBack),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
TRACING_EVENTS_MANAGER = Some(events_manager);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
extern "C" {
|
||||
fn _tracepoint();
|
||||
fn _etracepoint();
|
||||
}
|
||||
|
@ -1,61 +1,49 @@
|
||||
mod events;
|
||||
pub mod trace_pipe;
|
||||
pub mod tracepoint;
|
||||
|
||||
use crate::debug::sysfs::debugfs_kset;
|
||||
use crate::debug::tracing::trace_pipe::TRACE_PIPE_MAX_RECORD;
|
||||
use crate::driver::base::kobject::KObject;
|
||||
use crate::filesystem::kernfs::callback::{KernCallbackData, KernFSCallback, KernInodePrivateData};
|
||||
use crate::filesystem::kernfs::KernFSInode;
|
||||
use crate::filesystem::vfs::syscall::ModeType;
|
||||
use crate::filesystem::vfs::PollStatus;
|
||||
use crate::libs::spinlock::SpinLock;
|
||||
use crate::tracepoint::TracePointInfo;
|
||||
use alloc::string::ToString;
|
||||
use alloc::sync::Arc;
|
||||
use system_error::SystemError;
|
||||
use tracepoint::TracePoint;
|
||||
|
||||
static mut TRACING_ROOT_INODE: Option<Arc<KernFSInode>> = None;
|
||||
|
||||
static TRACE_RAW_PIPE: SpinLock<crate::tracepoint::TracePipeRaw> =
|
||||
SpinLock::new(crate::tracepoint::TracePipeRaw::new(4096));
|
||||
|
||||
static TRACE_CMDLINE_CACHE: SpinLock<crate::tracepoint::TraceCmdLineCache> =
|
||||
SpinLock::new(crate::tracepoint::TraceCmdLineCache::new(128));
|
||||
|
||||
pub fn trace_pipe_push_raw_record(record: &[u8]) {
|
||||
TRACE_RAW_PIPE.lock().push_event(record.to_vec());
|
||||
}
|
||||
|
||||
pub fn trace_cmdline_push(pid: u32) {
|
||||
let process = crate::process::ProcessManager::current_pcb();
|
||||
let binding = process.basic();
|
||||
let pname = binding
|
||||
.name()
|
||||
.split(' ')
|
||||
.next()
|
||||
.unwrap_or("unknown")
|
||||
.split('/')
|
||||
.last()
|
||||
.unwrap_or("unknown");
|
||||
TRACE_CMDLINE_CACHE.lock().insert(pid, pname.to_string());
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn tracing_root_inode() -> Arc<KernFSInode> {
|
||||
unsafe { TRACING_ROOT_INODE.clone().unwrap() }
|
||||
}
|
||||
|
||||
/// Initialize the debugfs tracing directory
|
||||
pub fn init_debugfs_tracing() -> Result<(), SystemError> {
|
||||
let debugfs = debugfs_kset();
|
||||
let root_dir = debugfs.inode().ok_or(SystemError::ENOENT)?;
|
||||
let tracing_root = root_dir.add_dir(
|
||||
"tracing".to_string(),
|
||||
ModeType::from_bits_truncate(0o555),
|
||||
None,
|
||||
Some(&TracingDirCallBack),
|
||||
)?;
|
||||
let events_root = tracing_root.add_dir(
|
||||
"events".to_string(),
|
||||
ModeType::from_bits_truncate(0o755),
|
||||
None,
|
||||
Some(&TracingDirCallBack),
|
||||
)?;
|
||||
|
||||
tracing_root.add_file(
|
||||
"trace_pipe".to_string(),
|
||||
ModeType::from_bits_truncate(0o444),
|
||||
Some(TRACE_PIPE_MAX_RECORD),
|
||||
None,
|
||||
Some(&trace_pipe::TracePipeCallBack),
|
||||
)?;
|
||||
|
||||
trace_pipe::init_trace_pipe();
|
||||
|
||||
events::init_events(events_root)?;
|
||||
|
||||
unsafe {
|
||||
TRACING_ROOT_INODE = Some(tracing_root);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TracingDirCallBack;
|
||||
|
||||
@ -88,10 +76,60 @@ impl KernFSCallback for TracingDirCallBack {
|
||||
}
|
||||
|
||||
impl KernInodePrivateData {
|
||||
pub fn debugfs_tracepoint(&self) -> Option<&'static TracePoint> {
|
||||
pub fn debugfs_tracepoint(&self) -> Option<&Arc<TracePointInfo>> {
|
||||
return match self {
|
||||
KernInodePrivateData::DebugFS(tracepoint) => Some(tracepoint),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn tracepipe(&mut self) -> Option<&mut crate::tracepoint::TracePipeSnapshot> {
|
||||
return match self {
|
||||
KernInodePrivateData::TracePipe(snapshot) => Some(snapshot),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the debugfs tracing directory
|
||||
pub fn init_debugfs_tracing() -> Result<(), SystemError> {
|
||||
let debugfs = debugfs_kset();
|
||||
let root_dir = debugfs.inode().ok_or(SystemError::ENOENT)?;
|
||||
let tracing_root = root_dir.add_dir(
|
||||
"tracing".to_string(),
|
||||
ModeType::from_bits_truncate(0o555),
|
||||
None,
|
||||
Some(&TracingDirCallBack),
|
||||
)?;
|
||||
let events_root = tracing_root.add_dir(
|
||||
"events".to_string(),
|
||||
ModeType::from_bits_truncate(0o755),
|
||||
None,
|
||||
Some(&TracingDirCallBack),
|
||||
)?;
|
||||
|
||||
// tracing_root.add_file(
|
||||
// "trace".to_string(),
|
||||
// ModeType::from_bits_truncate(0o444),
|
||||
// Some(4096),
|
||||
// None,
|
||||
// Some(&trace_pipe::TraceCallBack),
|
||||
// )?;
|
||||
|
||||
tracing_root.add_file_lazy("trace".to_string(), trace_pipe::kernel_inode_provider)?;
|
||||
|
||||
tracing_root.add_file(
|
||||
"trace_pipe".to_string(),
|
||||
ModeType::from_bits_truncate(0o444),
|
||||
Some(4096),
|
||||
None,
|
||||
Some(&trace_pipe::TracePipeCallBack),
|
||||
)?;
|
||||
|
||||
events::init_events(events_root)?;
|
||||
|
||||
unsafe {
|
||||
TRACING_ROOT_INODE = Some(tracing_root);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,111 +1,142 @@
|
||||
use crate::filesystem::kernfs::callback::{KernCallbackData, KernFSCallback};
|
||||
use crate::filesystem::kernfs::callback::{KernCallbackData, KernFSCallback, KernInodePrivateData};
|
||||
use crate::filesystem::kernfs::{KernFSInodeArgs, KernInodeType};
|
||||
use crate::filesystem::vfs::syscall::ModeType;
|
||||
use crate::filesystem::vfs::PollStatus;
|
||||
use crate::libs::spinlock::SpinLock;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use crate::libs::wait_queue::WaitQueue;
|
||||
use crate::process::{ProcessFlags, ProcessManager};
|
||||
use crate::sched::SchedMode;
|
||||
use crate::tracepoint::{TraceEntryParser, TracePipeOps};
|
||||
use core::fmt::Debug;
|
||||
use system_error::SystemError;
|
||||
|
||||
static mut TRACE_PIPE: Option<TracePipe> = None;
|
||||
|
||||
pub const TRACE_PIPE_MAX_RECORD: usize = 4096;
|
||||
|
||||
pub fn init_trace_pipe() {
|
||||
unsafe {
|
||||
TRACE_PIPE = Some(TracePipe::new(TRACE_PIPE_MAX_RECORD));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trace_pipe() -> &'static TracePipe {
|
||||
unsafe { TRACE_PIPE.as_ref().unwrap() }
|
||||
}
|
||||
|
||||
/// Push a record to trace pipe
|
||||
pub fn trace_pipe_push_record(record: String) {
|
||||
trace_pipe().push_record(record);
|
||||
}
|
||||
|
||||
pub struct TracePipe {
|
||||
buf: SpinLock<TracePipeBuf>,
|
||||
}
|
||||
|
||||
struct TracePipeBuf {
|
||||
size: usize,
|
||||
max_record: usize,
|
||||
buf: Vec<String>,
|
||||
}
|
||||
|
||||
impl TracePipeBuf {
|
||||
pub const fn new(max_record: usize) -> Self {
|
||||
Self {
|
||||
max_record,
|
||||
size: 0,
|
||||
buf: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_str(&mut self, record: String) {
|
||||
let record_size = record.len();
|
||||
if self.size + record_size > self.max_record {
|
||||
let mut i = 0;
|
||||
while i < record_size {
|
||||
let t = self.buf.pop().unwrap();
|
||||
self.size -= t.len();
|
||||
i += t.len();
|
||||
fn common_trace_pipe_read(
|
||||
trace_buf: &mut dyn TracePipeOps,
|
||||
buf: &mut [u8],
|
||||
) -> Result<usize, SystemError> {
|
||||
let manager = super::events::tracing_events_manager();
|
||||
let tracepint_map = manager.tracepoint_map();
|
||||
let trace_cmdline_cache = super::TRACE_CMDLINE_CACHE.lock();
|
||||
// read real trace data
|
||||
let mut copy_len = 0;
|
||||
let mut peek_flag = false;
|
||||
loop {
|
||||
if let Some(record) = trace_buf.peek() {
|
||||
let record_str = TraceEntryParser::parse(&tracepint_map, &trace_cmdline_cache, record);
|
||||
if copy_len + record_str.len() > buf.len() {
|
||||
break; // Buffer is full
|
||||
}
|
||||
let len = record_str.len();
|
||||
buf[copy_len..copy_len + len].copy_from_slice(record_str.as_bytes());
|
||||
copy_len += len;
|
||||
peek_flag = true;
|
||||
}
|
||||
self.buf.push(record);
|
||||
self.size += record_size;
|
||||
}
|
||||
|
||||
pub fn read_at(&self, buf: &mut [u8], offset: usize) -> Result<usize, SystemError> {
|
||||
if offset == self.size {
|
||||
return Ok(0);
|
||||
}
|
||||
if buf.len() < self.size {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
let mut count = 0;
|
||||
for line in self.buf.iter() {
|
||||
let line = line.as_bytes();
|
||||
buf[count..count + line.len()].copy_from_slice(line);
|
||||
count += line.len();
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
impl TracePipe {
|
||||
pub const fn new(max_record: usize) -> Self {
|
||||
Self {
|
||||
buf: SpinLock::new(TracePipeBuf::new(max_record)),
|
||||
if peek_flag {
|
||||
trace_buf.pop(); // Remove the record after reading
|
||||
peek_flag = false;
|
||||
} else {
|
||||
break; // No more records to read
|
||||
}
|
||||
}
|
||||
pub fn push_record(&self, record: String) {
|
||||
self.buf.lock().push_str(record);
|
||||
}
|
||||
|
||||
pub fn read_at(&self, buf: &mut [u8], offset: usize) -> Result<usize, SystemError> {
|
||||
self.buf.lock().read_at(buf, offset)
|
||||
}
|
||||
Ok(copy_len)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TracePipeCallBack;
|
||||
pub struct TraceCallBack;
|
||||
|
||||
impl KernFSCallback for TracePipeCallBack {
|
||||
fn open(&self, _data: KernCallbackData) -> Result<(), SystemError> {
|
||||
impl KernFSCallback for TraceCallBack {
|
||||
fn open(&self, mut data: KernCallbackData) -> Result<(), SystemError> {
|
||||
let pri_data = data.private_data_mut();
|
||||
let snapshot = super::TRACE_RAW_PIPE.lock().snapshot();
|
||||
pri_data.replace(KernInodePrivateData::TracePipe(snapshot));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
_data: KernCallbackData,
|
||||
mut data: KernCallbackData,
|
||||
buf: &mut [u8],
|
||||
offset: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
let trace_pipe = trace_pipe();
|
||||
trace_pipe.read_at(buf, offset)
|
||||
let pri_data = data.private_data_mut().as_mut().unwrap();
|
||||
let snapshot = pri_data.tracepipe().unwrap();
|
||||
|
||||
let default_fmt_str = snapshot.default_fmt_str();
|
||||
if offset >= default_fmt_str.len() {
|
||||
common_trace_pipe_read(snapshot, buf)
|
||||
} else {
|
||||
let len = buf.len().min(default_fmt_str.len() - offset);
|
||||
buf[..len].copy_from_slice(&default_fmt_str.as_bytes()[offset..offset + len]);
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
fn write(
|
||||
&self,
|
||||
_data: KernCallbackData,
|
||||
_buf: &[u8],
|
||||
_offset: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
Err(SystemError::EPERM)
|
||||
}
|
||||
|
||||
fn poll(&self, _data: KernCallbackData) -> Result<PollStatus, SystemError> {
|
||||
Ok(PollStatus::READ)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kernel_inode_provider() -> KernFSInodeArgs {
|
||||
KernFSInodeArgs {
|
||||
mode: ModeType::from_bits_truncate(0o444),
|
||||
callback: Some(&TraceCallBack),
|
||||
inode_type: KernInodeType::File,
|
||||
size: Some(4096),
|
||||
private_data: None,
|
||||
}
|
||||
}
|
||||
|
||||
static TracePipeCallBackWaitQueue: WaitQueue = WaitQueue::default();
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TracePipeCallBack;
|
||||
|
||||
impl TracePipeCallBack {
|
||||
fn readable(&self) -> bool {
|
||||
let trace_raw_pipe = super::TRACE_RAW_PIPE.lock();
|
||||
!trace_raw_pipe.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl KernFSCallback for TracePipeCallBack {
|
||||
fn open(&self, _data: KernCallbackData) -> Result<(), SystemError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
data: KernCallbackData,
|
||||
buf: &mut [u8],
|
||||
_offset: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
drop(data); // We don't need the data here, release the internal lock
|
||||
let read_len = loop {
|
||||
let mut trace_raw_pipe = super::TRACE_RAW_PIPE.lock();
|
||||
let read_len = common_trace_pipe_read(&mut *trace_raw_pipe, buf).unwrap();
|
||||
if read_len != 0 {
|
||||
break read_len;
|
||||
}
|
||||
// Release the lock before waiting
|
||||
drop(trace_raw_pipe);
|
||||
// wait for new data
|
||||
let r = wq_wait_event_interruptible!(TracePipeCallBackWaitQueue, self.readable(), {});
|
||||
if r.is_err() {
|
||||
ProcessManager::current_pcb()
|
||||
.flags()
|
||||
.insert(ProcessFlags::HAS_PENDING_SIGNAL);
|
||||
return Err(SystemError::ERESTARTSYS);
|
||||
}
|
||||
// todo!(wq_wait_event_interruptible may has a bug)
|
||||
};
|
||||
Ok(read_len)
|
||||
}
|
||||
|
||||
fn write(
|
||||
|
@ -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 crate::debug::tracing::tracepoint::TracePoint;
|
||||
use crate::tracepoint::{TracePipeSnapshot, TracePointInfo};
|
||||
use crate::{
|
||||
filesystem::{sysfs::SysFSKernPrivateData, vfs::PollStatus},
|
||||
libs::spinlock::SpinLockGuard,
|
||||
@ -86,7 +86,8 @@ impl<'a> KernCallbackData<'a> {
|
||||
#[derive(Debug)]
|
||||
pub enum KernInodePrivateData {
|
||||
SysFS(SysFSKernPrivateData),
|
||||
DebugFS(&'static TracePoint),
|
||||
DebugFS(Arc<TracePointInfo>),
|
||||
TracePipe(TracePipeSnapshot),
|
||||
}
|
||||
|
||||
impl KernInodePrivateData {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use alloc::string::ToString;
|
||||
use core::{cmp::min, fmt::Debug, intrinsics::unlikely};
|
||||
|
||||
use alloc::{
|
||||
@ -115,6 +116,7 @@ impl KernFS {
|
||||
callback: None,
|
||||
children: SpinLock::new(HashMap::new()),
|
||||
inode_type: KernInodeType::Dir,
|
||||
lazy_list: SpinLock::new(HashMap::new()),
|
||||
});
|
||||
|
||||
return root_inode;
|
||||
@ -138,6 +140,16 @@ pub struct KernFSInode {
|
||||
inode_type: KernInodeType,
|
||||
/// Inode名称
|
||||
name: String,
|
||||
/// lazy list
|
||||
lazy_list: SpinLock<HashMap<String, fn() -> KernFSInodeArgs>>,
|
||||
}
|
||||
|
||||
pub struct KernFSInodeArgs {
|
||||
pub mode: ModeType,
|
||||
pub inode_type: KernInodeType,
|
||||
pub size: Option<usize>,
|
||||
pub private_data: Option<KernInodePrivateData>,
|
||||
pub callback: Option<&'static dyn KernFSCallback>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -245,12 +257,25 @@ impl IndexNode for KernFSInode {
|
||||
}
|
||||
name => {
|
||||
// 在子目录项中查找
|
||||
return Ok(self
|
||||
.children
|
||||
.lock()
|
||||
.get(name)
|
||||
.ok_or(SystemError::ENOENT)?
|
||||
.clone());
|
||||
let child = self.children.lock().get(name).cloned();
|
||||
if let Some(child) = child {
|
||||
return Ok(child);
|
||||
}
|
||||
let lazy_list = self.lazy_list.lock();
|
||||
if let Some(provider) = lazy_list.get(name) {
|
||||
// 如果存在lazy list,则调用提供者函数创建
|
||||
let args = provider();
|
||||
let inode = self.inner_create(
|
||||
name.to_string(),
|
||||
args.inode_type,
|
||||
args.mode,
|
||||
args.size.unwrap_or(4096),
|
||||
args.private_data,
|
||||
args.callback,
|
||||
)?;
|
||||
return Ok(inode);
|
||||
}
|
||||
Err(SystemError::ENOENT)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -321,7 +346,7 @@ impl IndexNode for KernFSInode {
|
||||
offset: usize,
|
||||
len: usize,
|
||||
buf: &mut [u8],
|
||||
_data: SpinLockGuard<FilePrivateData>,
|
||||
data: SpinLockGuard<FilePrivateData>,
|
||||
) -> Result<usize, SystemError> {
|
||||
if self.inode_type == KernInodeType::SymLink {
|
||||
let inner = self.inner.read();
|
||||
@ -350,6 +375,8 @@ impl IndexNode for KernFSInode {
|
||||
warn!("kernfs: callback is none");
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
// release the private data lock before calling the callback
|
||||
drop(data);
|
||||
|
||||
let callback_data =
|
||||
KernCallbackData::new(self.self_ref.upgrade().unwrap(), self.private_data.lock());
|
||||
@ -365,7 +392,7 @@ impl IndexNode for KernFSInode {
|
||||
offset: usize,
|
||||
len: usize,
|
||||
buf: &[u8],
|
||||
_data: SpinLockGuard<FilePrivateData>,
|
||||
data: SpinLockGuard<FilePrivateData>,
|
||||
) -> Result<usize, SystemError> {
|
||||
if self.inode_type != KernInodeType::File {
|
||||
return Err(SystemError::EISDIR);
|
||||
@ -375,6 +402,9 @@ impl IndexNode for KernFSInode {
|
||||
return Err(SystemError::ENOSYS);
|
||||
}
|
||||
|
||||
// release the private data lock before calling the callback
|
||||
drop(data);
|
||||
|
||||
let callback_data =
|
||||
KernCallbackData::new(self.self_ref.upgrade().unwrap(), self.private_data.lock());
|
||||
return self
|
||||
@ -411,6 +441,7 @@ impl KernFSInode {
|
||||
callback,
|
||||
children: SpinLock::new(HashMap::new()),
|
||||
inode_type,
|
||||
lazy_list: SpinLock::new(HashMap::new()),
|
||||
});
|
||||
|
||||
{
|
||||
@ -500,6 +531,18 @@ impl KernFSInode {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn add_file_lazy(
|
||||
&self,
|
||||
name: String,
|
||||
provider: fn() -> KernFSInodeArgs,
|
||||
) -> Result<(), SystemError> {
|
||||
if unlikely(self.inode_type != KernInodeType::Dir) {
|
||||
return Err(SystemError::ENOTDIR);
|
||||
}
|
||||
self.lazy_list.lock().insert(name, provider);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn inner_create(
|
||||
&self,
|
||||
name: String,
|
||||
|
@ -5,7 +5,7 @@ use log::{error, info};
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
define_event_trace, define_trace_point,
|
||||
define_event_trace,
|
||||
driver::base::block::{gendisk::GenDisk, manager::block_dev_manager},
|
||||
filesystem::{
|
||||
devfs::devfs_init,
|
||||
@ -167,15 +167,39 @@ pub fn mount_root_fs() -> Result<(), SystemError> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
define_event_trace!(DO_MKDIR_AT,(path:&str,mode:FileMode),
|
||||
format_args!("mkdir at {} with mode {:?}",path, mode));
|
||||
define_event_trace!(
|
||||
do_mkdir_at,
|
||||
TP_system(vfs),
|
||||
TP_PROTO(path:&str, mode: FileMode),
|
||||
TP_STRUCT__entry {
|
||||
fmode: FileMode,
|
||||
path: [u8;64],
|
||||
},
|
||||
TP_fast_assign {
|
||||
fmode: mode,
|
||||
path: {
|
||||
let mut buf = [0u8; 64];
|
||||
let path = path.as_bytes();
|
||||
let len = path.len().min(63);
|
||||
buf[..len].copy_from_slice(&path[..len]);
|
||||
buf[len] = 0; // null-terminate
|
||||
buf
|
||||
},
|
||||
},
|
||||
TP_ident(__entry),
|
||||
TP_printk({
|
||||
let path = core::str::from_utf8(&__entry.path).unwrap_or("invalid utf8");
|
||||
let mode = __entry.fmode;
|
||||
format!("mkdir at {} with mode {:?}", path, mode)
|
||||
})
|
||||
);
|
||||
/// @brief 创建文件/文件夹
|
||||
pub fn do_mkdir_at(
|
||||
dirfd: i32,
|
||||
path: &str,
|
||||
mode: FileMode,
|
||||
) -> Result<Arc<dyn IndexNode>, SystemError> {
|
||||
TRACE_DO_MKDIR_AT(path, mode);
|
||||
trace_do_mkdir_at(path, mode);
|
||||
// debug!("Call do mkdir at");
|
||||
let (mut current_inode, path) =
|
||||
user_path_at(&ProcessManager::current_pcb(), dirfd, path.trim())?;
|
||||
|
@ -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;
|
||||
|
||||
|
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