mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-22 02:53:23 +00:00
feat: support tracepoint-based ebpf programs (#1190)
* feat: support tracepoint-based ebpf programs Signed-off-by: Godones <chenlinfeng25@outlook.com> * remove licenses Signed-off-by: Godones <chenlinfeng25@outlook.com> * feat: Supplement tracepoint related files fix some warnings add docs for tracepoint Signed-off-by: Godones <chenlinfeng25@outlook.com> --------- Signed-off-by: Godones <chenlinfeng25@outlook.com> Co-authored-by: longjin <longjin@DragonOS.org>
This commit is contained in:
@ -10,3 +10,4 @@ pub const HELPER_TRACE_PRINTF: u32 = 6;
|
||||
pub const HELPER_MAP_PUSH_ELEM: u32 = 87;
|
||||
pub const HELPER_MAP_POP_ELEM: u32 = 88;
|
||||
pub const HELPER_MAP_PEEK_ELEM: u32 = 89;
|
||||
pub const HELPER_PROBE_READ_USER_STR: u32 = 114;
|
||||
|
@ -6,6 +6,7 @@ use crate::bpf::map::{BpfCallBackFn, BpfMap};
|
||||
use crate::include::bindings::linux_bpf::BPF_F_CURRENT_CPU;
|
||||
use crate::libs::lazy_init::Lazy;
|
||||
use crate::smp::core::smp_get_processor_id;
|
||||
use crate::syscall::user_access::check_and_clone_cstr;
|
||||
use crate::time::Instant;
|
||||
use alloc::{collections::BTreeMap, sync::Arc};
|
||||
use core::ffi::c_void;
|
||||
@ -89,12 +90,12 @@ pub fn perf_event_output(
|
||||
|
||||
/// See https://ebpf-docs.dylanreimerink.nl/linux/helper-function/bpf_probe_read/
|
||||
fn raw_bpf_probe_read(dst: *mut c_void, size: u32, unsafe_ptr: *const c_void) -> i64 {
|
||||
log::info!(
|
||||
"raw_bpf_probe_read, dst:{:x}, size:{}, unsafe_ptr: {:x}",
|
||||
dst as usize,
|
||||
size,
|
||||
unsafe_ptr as usize
|
||||
);
|
||||
// log::info!(
|
||||
// "raw_bpf_probe_read, dst:{:x}, size:{}, unsafe_ptr: {:x}",
|
||||
// dst as usize,
|
||||
// size,
|
||||
// unsafe_ptr as usize
|
||||
// );
|
||||
let (dst, src) = unsafe {
|
||||
let dst = core::slice::from_raw_parts_mut(dst as *mut u8, size as usize);
|
||||
let src = core::slice::from_raw_parts(unsafe_ptr as *const u8, size as usize);
|
||||
@ -111,7 +112,7 @@ fn raw_bpf_probe_read(dst: *mut c_void, size: u32, unsafe_ptr: *const c_void) ->
|
||||
/// bytes from kernel space address unsafe_ptr and
|
||||
/// store the data in dst.
|
||||
pub fn bpf_probe_read(dst: &mut [u8], src: &[u8]) -> Result<()> {
|
||||
log::info!("bpf_probe_read: len: {}", dst.len());
|
||||
// log::info!("bpf_probe_read: len: {}", dst.len());
|
||||
dst.copy_from_slice(src);
|
||||
Ok(())
|
||||
}
|
||||
@ -305,6 +306,38 @@ pub fn bpf_ktime_get_ns() -> u64 {
|
||||
(Instant::now().total_micros() * 1000) as u64
|
||||
}
|
||||
|
||||
/// Copy a NULL terminated string from an unsafe user address unsafe_ptr to dst.
|
||||
/// The size should include the terminating NULL byte. In case the string length is smaller than size,
|
||||
/// the target is not padded with further NULL bytes. If the string length is larger than size,
|
||||
/// just size-1 bytes are copied and the last byte is set to NULL.
|
||||
///
|
||||
/// On success, the strictly positive length of the output string, including the trailing NULL character. On error, a negative value
|
||||
///
|
||||
/// See https://docs.ebpf.io/linux/helper-function/bpf_probe_read_user_str/
|
||||
unsafe fn raw_probe_read_user_str(dst: *mut c_void, size: u32, unsafe_ptr: *const c_void) -> i64 {
|
||||
// log::info!(
|
||||
// "<raw_probe_read_user_str>: dst:{:x}, size:{}, unsafe_ptr: {:x}",
|
||||
// dst as usize,
|
||||
// size,
|
||||
// unsafe_ptr as usize
|
||||
// );
|
||||
let dst = core::slice::from_raw_parts_mut(dst as *mut u8, size as usize);
|
||||
let res = probe_read_user_str(dst, unsafe_ptr as *const u8);
|
||||
match res {
|
||||
Ok(len) => len as i64,
|
||||
Err(e) => e as i64,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn probe_read_user_str(dst: &mut [u8], src: *const u8) -> Result<usize> {
|
||||
let str = check_and_clone_cstr(src, None).unwrap();
|
||||
let len = str.as_bytes().len();
|
||||
let copy_len = len.min(dst.len() - 1); // Leave space for NULL terminator
|
||||
dst[..copy_len].copy_from_slice(&str.as_bytes()[..copy_len]);
|
||||
dst[copy_len] = 0; // Null-terminate the string
|
||||
Ok(copy_len + 1) // Return length including NULL terminator
|
||||
}
|
||||
|
||||
pub static BPF_HELPER_FUN_SET: Lazy<BTreeMap<u32, RawBPFHelperFn>> = Lazy::new();
|
||||
|
||||
/// Initialize the helper functions.
|
||||
@ -341,6 +374,12 @@ pub fn init_helper_functions() {
|
||||
map.insert(HELPER_MAP_PUSH_ELEM, define_func!(raw_map_push_elem));
|
||||
map.insert(HELPER_MAP_POP_ELEM, define_func!(raw_map_pop_elem));
|
||||
map.insert(HELPER_MAP_PEEK_ELEM, define_func!(raw_map_peek_elem));
|
||||
|
||||
// User access helpers
|
||||
map.insert(
|
||||
HELPER_PROBE_READ_USER_STR,
|
||||
define_func!(raw_probe_read_user_str),
|
||||
);
|
||||
}
|
||||
BPF_HELPER_FUN_SET.init(map);
|
||||
}
|
||||
|
@ -8,11 +8,13 @@ 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 crate::tracepoint::{TraceCmdLineCacheSnapshot, TracePointInfo};
|
||||
use alloc::string::ToString;
|
||||
use alloc::sync::Arc;
|
||||
use system_error::SystemError;
|
||||
|
||||
pub use events::tracing_events_manager;
|
||||
|
||||
static mut TRACING_ROOT_INODE: Option<Arc<KernFSInode>> = None;
|
||||
|
||||
static TRACE_RAW_PIPE: SpinLock<crate::tracepoint::TracePipeRaw> =
|
||||
@ -22,6 +24,7 @@ static TRACE_CMDLINE_CACHE: SpinLock<crate::tracepoint::TraceCmdLineCache> =
|
||||
SpinLock::new(crate::tracepoint::TraceCmdLineCache::new(128));
|
||||
|
||||
pub fn trace_pipe_push_raw_record(record: &[u8]) {
|
||||
// log::debug!("trace_pipe_push_raw_record: {}", record.len());
|
||||
TRACE_RAW_PIPE.lock().push_event(record.to_vec());
|
||||
}
|
||||
|
||||
@ -89,6 +92,13 @@ impl KernInodePrivateData {
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn trace_saved_cmdlines(&mut self) -> Option<&mut TraceCmdLineCacheSnapshot> {
|
||||
return match self {
|
||||
KernInodePrivateData::TraceSavedCmdlines(cache) => Some(cache),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the debugfs tracing directory
|
||||
@ -108,15 +118,7 @@ pub fn init_debugfs_tracing() -> Result<(), SystemError> {
|
||||
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_lazy("trace".to_string(), trace_pipe::kernel_inode_provider_trace)?;
|
||||
|
||||
tracing_root.add_file(
|
||||
"trace_pipe".to_string(),
|
||||
@ -125,6 +127,17 @@ pub fn init_debugfs_tracing() -> Result<(), SystemError> {
|
||||
None,
|
||||
Some(&trace_pipe::TracePipeCallBack),
|
||||
)?;
|
||||
tracing_root.add_file_lazy(
|
||||
"saved_cmdlines".to_string(),
|
||||
trace_pipe::kernel_inode_provider_saved_cmdlines,
|
||||
)?;
|
||||
tracing_root.add_file(
|
||||
"saved_cmdlines_size".to_string(),
|
||||
ModeType::from_bits_truncate(0o444),
|
||||
None,
|
||||
None,
|
||||
Some(&trace_pipe::SavedCmdlinesSizeCallBack),
|
||||
)?;
|
||||
|
||||
events::init_events(events_root)?;
|
||||
|
||||
|
@ -6,6 +6,7 @@ use crate::libs::wait_queue::WaitQueue;
|
||||
use crate::process::{ProcessFlags, ProcessManager};
|
||||
use crate::sched::SchedMode;
|
||||
use crate::tracepoint::{TraceEntryParser, TracePipeOps};
|
||||
use alloc::string::String;
|
||||
use core::fmt::Debug;
|
||||
use system_error::SystemError;
|
||||
|
||||
@ -73,10 +74,14 @@ impl KernFSCallback for TraceCallBack {
|
||||
fn write(
|
||||
&self,
|
||||
_data: KernCallbackData,
|
||||
_buf: &[u8],
|
||||
buf: &[u8],
|
||||
_offset: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
Err(SystemError::EPERM)
|
||||
if buf.len() == 1 {
|
||||
let mut trace_raw_pipe = super::TRACE_RAW_PIPE.lock();
|
||||
trace_raw_pipe.clear();
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn poll(&self, _data: KernCallbackData) -> Result<PollStatus, SystemError> {
|
||||
@ -84,7 +89,7 @@ impl KernFSCallback for TraceCallBack {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kernel_inode_provider() -> KernFSInodeArgs {
|
||||
pub fn kernel_inode_provider_trace() -> KernFSInodeArgs {
|
||||
KernFSInodeArgs {
|
||||
mode: ModeType::from_bits_truncate(0o444),
|
||||
callback: Some(&TraceCallBack),
|
||||
@ -152,3 +157,115 @@ impl KernFSCallback for TracePipeCallBack {
|
||||
Ok(PollStatus::READ)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SavedCmdlinesSizeCallBack;
|
||||
|
||||
impl KernFSCallback for SavedCmdlinesSizeCallBack {
|
||||
fn open(&self, _data: KernCallbackData) -> Result<(), SystemError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
_data: KernCallbackData,
|
||||
buf: &mut [u8],
|
||||
offset: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
let max_record = super::TRACE_CMDLINE_CACHE.lock().max_record();
|
||||
let str = format!("{}\n", max_record);
|
||||
let str_bytes = str.as_bytes();
|
||||
if offset >= str_bytes.len() {
|
||||
return Ok(0); // Offset is beyond the length of the string
|
||||
}
|
||||
let len = buf.len().min(str_bytes.len() - offset);
|
||||
buf[..len].copy_from_slice(&str_bytes[offset..offset + len]);
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn write(
|
||||
&self,
|
||||
_data: KernCallbackData,
|
||||
buf: &[u8],
|
||||
_offset: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
let max_record_str = String::from_utf8_lossy(buf);
|
||||
let max_record: usize = max_record_str
|
||||
.trim()
|
||||
.parse()
|
||||
.map_err(|_| SystemError::EINVAL)?;
|
||||
super::TRACE_CMDLINE_CACHE.lock().set_max_record(max_record);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn poll(&self, _data: KernCallbackData) -> Result<PollStatus, SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kernel_inode_provider_saved_cmdlines() -> KernFSInodeArgs {
|
||||
KernFSInodeArgs {
|
||||
mode: ModeType::from_bits_truncate(0o444),
|
||||
callback: Some(&SavedCmdlinesSnapshotCallBack),
|
||||
inode_type: KernInodeType::File,
|
||||
size: Some(4096),
|
||||
private_data: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SavedCmdlinesSnapshotCallBack;
|
||||
|
||||
impl KernFSCallback for SavedCmdlinesSnapshotCallBack {
|
||||
fn open(&self, mut data: KernCallbackData) -> Result<(), SystemError> {
|
||||
let pri_data = data.private_data_mut();
|
||||
let snapshot = super::TRACE_CMDLINE_CACHE.lock().snapshot();
|
||||
pri_data.replace(KernInodePrivateData::TraceSavedCmdlines(snapshot));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(
|
||||
&self,
|
||||
mut data: KernCallbackData,
|
||||
buf: &mut [u8],
|
||||
_offset: usize,
|
||||
) -> Result<usize, SystemError> {
|
||||
let pri_data = data.private_data_mut().as_mut().unwrap();
|
||||
let snapshot = pri_data.trace_saved_cmdlines().unwrap();
|
||||
|
||||
let mut copy_len = 0;
|
||||
let mut peek_flag = false;
|
||||
loop {
|
||||
if let Some((pid, cmdline)) = snapshot.peek() {
|
||||
let record_str = format!("{} {}\n", pid, String::from_utf8_lossy(cmdline));
|
||||
if copy_len + record_str.len() > buf.len() {
|
||||
break;
|
||||
}
|
||||
let len = record_str.len();
|
||||
buf[copy_len..copy_len + len].copy_from_slice(record_str.as_bytes());
|
||||
copy_len += len;
|
||||
peek_flag = true;
|
||||
}
|
||||
if peek_flag {
|
||||
snapshot.pop(); // Remove the record after reading
|
||||
peek_flag = false;
|
||||
} else {
|
||||
break; // No more records to read
|
||||
}
|
||||
}
|
||||
Ok(copy_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)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::KernFSInode;
|
||||
use crate::tracepoint::{TracePipeSnapshot, TracePointInfo};
|
||||
use crate::tracepoint::{TraceCmdLineCacheSnapshot, TracePipeSnapshot, TracePointInfo};
|
||||
use crate::{
|
||||
filesystem::{sysfs::SysFSKernPrivateData, vfs::PollStatus},
|
||||
libs::spinlock::SpinLockGuard,
|
||||
@ -88,6 +88,7 @@ pub enum KernInodePrivateData {
|
||||
SysFS(SysFSKernPrivateData),
|
||||
DebugFS(Arc<TracePointInfo>),
|
||||
TracePipe(TracePipeSnapshot),
|
||||
TraceSavedCmdlines(TraceCmdLineCacheSnapshot),
|
||||
}
|
||||
|
||||
impl KernInodePrivateData {
|
||||
|
@ -1,6 +1,7 @@
|
||||
use system_error::SystemError;
|
||||
|
||||
use crate::{
|
||||
define_event_trace,
|
||||
filesystem::vfs::{fcntl::AtFlags, file::FileMode, open::do_sys_open, MAX_PATHLEN},
|
||||
syscall::user_access::check_and_clone_cstr,
|
||||
};
|
||||
@ -23,6 +24,7 @@ pub(super) fn do_open(
|
||||
mode: u32,
|
||||
follow_symlink: bool,
|
||||
) -> Result<usize, SystemError> {
|
||||
trace_sys_enter_openat(AtFlags::AT_FDCWD.bits(), path, o_flags, mode);
|
||||
let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?
|
||||
.into_string()
|
||||
.map_err(|_| SystemError::EINVAL)?;
|
||||
@ -37,3 +39,31 @@ pub(super) fn do_open(
|
||||
follow_symlink,
|
||||
);
|
||||
}
|
||||
|
||||
define_event_trace!(
|
||||
sys_enter_openat,
|
||||
TP_system(syscalls),
|
||||
TP_PROTO(dfd: i32, path:*const u8, o_flags: u32, mode: u32),
|
||||
TP_STRUCT__entry{
|
||||
dfd: i32,
|
||||
path: u64,
|
||||
o_flags: u32,
|
||||
mode: u32,
|
||||
},
|
||||
TP_fast_assign{
|
||||
dfd: dfd,
|
||||
path: path as u64,
|
||||
o_flags: o_flags,
|
||||
mode: mode,
|
||||
},
|
||||
TP_ident(__entry),
|
||||
TP_printk({
|
||||
format!(
|
||||
"dfd: {}, path: {:#x}, o_flags: {:?}, mode: {:?}",
|
||||
__entry.dfd,
|
||||
__entry.path,
|
||||
__entry.o_flags,
|
||||
__entry.mode
|
||||
)
|
||||
})
|
||||
);
|
||||
|
@ -1,5 +1,6 @@
|
||||
mod bpf;
|
||||
mod kprobe;
|
||||
mod tracepoint;
|
||||
mod util;
|
||||
|
||||
use crate::filesystem::epoll::{event_poll::EventPoll, EPollEventType, EPollItem};
|
||||
@ -17,7 +18,7 @@ use crate::libs::spinlock::{SpinLock, SpinLockGuard};
|
||||
use crate::mm::fault::{PageFaultHandler, PageFaultMessage};
|
||||
use crate::mm::VmFaultReason;
|
||||
use crate::perf::bpf::BpfPerfEvent;
|
||||
use crate::perf::util::{PerfEventIoc, PerfEventOpenFlags, PerfProbeArgs};
|
||||
use crate::perf::util::{PerfEventIoc, PerfEventOpenFlags, PerfProbeArgs, PerfProbeConfig};
|
||||
use crate::process::ProcessManager;
|
||||
use crate::syscall::user_access::UserBufferReader;
|
||||
use crate::syscall::Syscall;
|
||||
@ -287,7 +288,10 @@ pub fn perf_event_open(
|
||||
}
|
||||
perf_type_id::PERF_TYPE_SOFTWARE => {
|
||||
// For bpf prog output
|
||||
assert_eq!(args.config, perf_sw_ids::PERF_COUNT_SW_BPF_OUTPUT);
|
||||
assert_eq!(
|
||||
args.config,
|
||||
PerfProbeConfig::PerfSwIds(perf_sw_ids::PERF_COUNT_SW_BPF_OUTPUT)
|
||||
);
|
||||
assert_eq!(
|
||||
args.sample_type,
|
||||
Some(perf_event_sample_format::PERF_SAMPLE_RAW)
|
||||
@ -295,6 +299,10 @@ pub fn perf_event_open(
|
||||
let bpf_event = bpf::perf_event_open_bpf(args);
|
||||
Box::new(bpf_event)
|
||||
}
|
||||
perf_type_id::PERF_TYPE_TRACEPOINT => {
|
||||
let tracepoint_event = tracepoint::perf_event_open_tracepoint(args)?;
|
||||
Box::new(tracepoint_event)
|
||||
}
|
||||
_ => {
|
||||
unimplemented!("perf_event_process: unknown type: {:?}", args);
|
||||
}
|
||||
|
178
kernel/src/perf/tracepoint.rs
Normal file
178
kernel/src/perf/tracepoint.rs
Normal file
@ -0,0 +1,178 @@
|
||||
use super::Result;
|
||||
use crate::bpf::helper::BPF_HELPER_FUN_SET;
|
||||
use crate::bpf::prog::BpfProg;
|
||||
use crate::filesystem::page_cache::PageCache;
|
||||
use crate::libs::casting::DowncastArc;
|
||||
use crate::libs::spinlock::SpinLock;
|
||||
use crate::perf::util::PerfProbeConfig;
|
||||
use crate::tracepoint::{TracePoint, TracePointCallBackFunc};
|
||||
use crate::{
|
||||
filesystem::vfs::{file::File, FilePrivateData, FileSystem, IndexNode},
|
||||
libs::spinlock::SpinLockGuard,
|
||||
perf::{util::PerfProbeArgs, PerfEventOps},
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use core::any::Any;
|
||||
use core::sync::atomic::AtomicUsize;
|
||||
use rbpf::EbpfVmRawOwned;
|
||||
use system_error::SystemError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TracepointPerfEvent {
|
||||
_args: PerfProbeArgs,
|
||||
tp: &'static TracePoint,
|
||||
ebpf_list: SpinLock<Vec<usize>>,
|
||||
}
|
||||
|
||||
impl TracepointPerfEvent {
|
||||
pub fn new(args: PerfProbeArgs, tp: &'static TracePoint) -> TracepointPerfEvent {
|
||||
TracepointPerfEvent {
|
||||
_args: args,
|
||||
tp,
|
||||
ebpf_list: SpinLock::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexNode for TracepointPerfEvent {
|
||||
fn read_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
_len: usize,
|
||||
_buf: &mut [u8],
|
||||
_data: SpinLockGuard<FilePrivateData>,
|
||||
) -> Result<usize> {
|
||||
panic!("read_at not implemented for TracepointPerfEvent");
|
||||
}
|
||||
|
||||
fn write_at(
|
||||
&self,
|
||||
_offset: usize,
|
||||
_len: usize,
|
||||
_buf: &[u8],
|
||||
_data: SpinLockGuard<FilePrivateData>,
|
||||
) -> Result<usize> {
|
||||
panic!("write_at not implemented for TracepointPerfEvent");
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<dyn FileSystem> {
|
||||
panic!("fs not implemented for TracepointPerfEvent");
|
||||
}
|
||||
|
||||
fn as_any_ref(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn list(&self) -> Result<Vec<String>> {
|
||||
Err(SystemError::ENOSYS)
|
||||
}
|
||||
|
||||
fn page_cache(&self) -> Option<Arc<PageCache>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TracePointPerfCallBack {
|
||||
_bpf_prog_file: Arc<BpfProg>,
|
||||
vm: EbpfVmRawOwned,
|
||||
}
|
||||
|
||||
impl TracePointPerfCallBack {
|
||||
fn new(bpf_prog_file: Arc<BpfProg>, vm: EbpfVmRawOwned) -> Self {
|
||||
Self {
|
||||
_bpf_prog_file: bpf_prog_file,
|
||||
vm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TracePointCallBackFunc for TracePointPerfCallBack {
|
||||
fn call(&self, entry: &[u8]) {
|
||||
// ebpf needs a mutable slice
|
||||
let entry =
|
||||
unsafe { core::slice::from_raw_parts_mut(entry.as_ptr() as *mut u8, entry.len()) };
|
||||
let res = self.vm.execute_program(entry);
|
||||
if res.is_err() {
|
||||
log::error!("tracepoint callback error: {:?}", res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PerfEventOps for TracepointPerfEvent {
|
||||
fn set_bpf_prog(&self, bpf_prog: Arc<File>) -> Result<()> {
|
||||
static CALLBACK_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
let file = bpf_prog
|
||||
.inode()
|
||||
.downcast_arc::<BpfProg>()
|
||||
.ok_or(SystemError::EINVAL)?;
|
||||
let prog_slice = file.insns();
|
||||
let mut vm = EbpfVmRawOwned::new(Some(prog_slice.to_vec())).map_err(|e| {
|
||||
log::error!("create ebpf vm failed: {:?}", e);
|
||||
SystemError::EINVAL
|
||||
})?;
|
||||
vm.register_helper_set(BPF_HELPER_FUN_SET.get())
|
||||
.map_err(|_| SystemError::EINVAL)?;
|
||||
|
||||
// create a callback to execute the ebpf prog
|
||||
let callback = Box::new(TracePointPerfCallBack::new(file, vm));
|
||||
let id = CALLBACK_ID.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
|
||||
self.tp.register_raw_callback(id, callback);
|
||||
|
||||
log::info!(
|
||||
"Registered BPF program for tracepoint: {}:{} with ID: {}",
|
||||
self.tp.system(),
|
||||
self.tp.name(),
|
||||
id
|
||||
);
|
||||
// Store the ID in the ebpf_list for later cleanup
|
||||
self.ebpf_list.lock().push(id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn enable(&self) -> Result<()> {
|
||||
log::info!(
|
||||
"Enabling tracepoint event: {}:{}",
|
||||
self.tp.system(),
|
||||
self.tp.name()
|
||||
);
|
||||
self.tp.enable();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable(&self) -> Result<()> {
|
||||
self.tp.disable();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn readable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TracepointPerfEvent {
|
||||
fn drop(&mut self) {
|
||||
// Unregister all callbacks associated with this tracepoint event
|
||||
let mut ebpf_list = self.ebpf_list.lock();
|
||||
for id in ebpf_list.iter() {
|
||||
self.tp.unregister_raw_callback(*id);
|
||||
}
|
||||
ebpf_list.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `TracepointPerfEvent` for the given tracepoint ID.
|
||||
pub fn perf_event_open_tracepoint(args: PerfProbeArgs) -> Result<TracepointPerfEvent> {
|
||||
let tp_id = match args.config {
|
||||
PerfProbeConfig::Raw(tp_id) => tp_id as u32,
|
||||
_ => {
|
||||
panic!("Invalid PerfProbeConfig for TracepointPerfEvent");
|
||||
}
|
||||
};
|
||||
let tp_manager = crate::debug::tracing::tracing_events_manager();
|
||||
let tp_map = tp_manager.tracepoint_map();
|
||||
let tp = tp_map.get(&tp_id).ok_or(SystemError::ENOENT)?;
|
||||
Ok(TracepointPerfEvent::new(args, tp))
|
||||
}
|
@ -33,7 +33,7 @@ pub enum PerfEventIoc {
|
||||
#[allow(unused)]
|
||||
/// `perf_event_open` syscall arguments.
|
||||
pub struct PerfProbeArgs {
|
||||
pub config: perf_sw_ids,
|
||||
pub config: PerfProbeConfig,
|
||||
pub name: String,
|
||||
pub offset: u64,
|
||||
pub size: u32,
|
||||
@ -45,6 +45,12 @@ pub struct PerfProbeArgs {
|
||||
pub sample_type: Option<perf_event_sample_format>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PerfProbeConfig {
|
||||
PerfSwIds(perf_sw_ids),
|
||||
Raw(u64),
|
||||
}
|
||||
|
||||
impl PerfProbeArgs {
|
||||
pub fn try_from(
|
||||
attr: &perf_event_attr,
|
||||
@ -54,7 +60,13 @@ impl PerfProbeArgs {
|
||||
flags: u32,
|
||||
) -> Result<Self, SystemError> {
|
||||
let ty = perf_type_id::from_u32(attr.type_).ok_or(SystemError::EINVAL)?;
|
||||
let config = perf_sw_ids::from_u32(attr.config as u32).ok_or(SystemError::EINVAL)?;
|
||||
let config = match ty {
|
||||
perf_type_id::PERF_TYPE_TRACEPOINT => PerfProbeConfig::Raw(attr.config),
|
||||
_ => {
|
||||
let sw_id = perf_sw_ids::from_u32(attr.config as u32).ok_or(SystemError::EINVAL)?;
|
||||
PerfProbeConfig::PerfSwIds(sw_id)
|
||||
}
|
||||
};
|
||||
let name = if ty == perf_type_id::PERF_TYPE_MAX {
|
||||
let name_ptr = unsafe { attr.__bindgen_anon_3.config1 } as *const u8;
|
||||
let name = check_and_clone_cstr(name_ptr, None)?;
|
||||
|
@ -93,12 +93,13 @@ macro_rules! define_event_trace{
|
||||
};
|
||||
|
||||
#[allow(unused,non_snake_case)]
|
||||
#[allow(clippy::redundant_field_names)]
|
||||
pub fn [<trace_default_ $name>](_data:&mut (dyn core::any::Any+Send+Sync), $($arg:$arg_type),* ){
|
||||
#[repr(C)]
|
||||
#[repr(C, packed)]
|
||||
struct Entry {
|
||||
$($entry: $entry_type,)*
|
||||
}
|
||||
#[repr(C)]
|
||||
#[repr(C, packed)]
|
||||
struct FullEntry {
|
||||
common: $crate::tracepoint::TraceEntry,
|
||||
entry: Entry,
|
||||
@ -129,18 +130,25 @@ macro_rules! define_event_trace{
|
||||
core::mem::size_of::<FullEntry>(),
|
||||
)
|
||||
};
|
||||
|
||||
let func = |f:&alloc::boxed::Box<dyn $crate::tracepoint::TracePointCallBackFunc>|{
|
||||
f.call(event_buf);
|
||||
};
|
||||
|
||||
[<__ $name>].raw_callback_list(&func);
|
||||
|
||||
$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 {
|
||||
pub fn [<trace_fmt_ $name>](buf: &[u8]) -> alloc::string::String {
|
||||
#[repr(C)]
|
||||
struct Entry {
|
||||
$($entry: $entry_type,)*
|
||||
}
|
||||
let $tp_ident = unsafe {
|
||||
&*(buf_ptr as *const Entry)
|
||||
&*(buf.as_ptr() as *const Entry)
|
||||
};
|
||||
let fmt = format!("{}", $fmt_expr);
|
||||
fmt
|
||||
|
@ -15,10 +15,13 @@ use core::{
|
||||
ops::{Deref, DerefMut},
|
||||
sync::atomic::AtomicUsize,
|
||||
};
|
||||
pub use point::{CommonTracePointMeta, TraceEntry, TracePoint, TracePointFunc};
|
||||
pub use point::{
|
||||
CommonTracePointMeta, TraceEntry, TracePoint, TracePointCallBackFunc, TracePointFunc,
|
||||
};
|
||||
use system_error::SystemError;
|
||||
pub use trace_pipe::{
|
||||
TraceCmdLineCache, TraceEntryParser, TracePipeOps, TracePipeRaw, TracePipeSnapshot,
|
||||
TraceCmdLineCache, TraceCmdLineCacheSnapshot, TraceEntryParser, TracePipeOps, TracePipeRaw,
|
||||
TracePipeSnapshot,
|
||||
};
|
||||
|
||||
use crate::libs::spinlock::{SpinLock, SpinLockGuard};
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::libs::spinlock::SpinLock;
|
||||
use alloc::{boxed::Box, collections::BTreeMap, format, string::String};
|
||||
use core::{any::Any, sync::atomic::AtomicU32};
|
||||
use core::{any::Any, fmt::Debug, sync::atomic::AtomicU32};
|
||||
use static_keys::StaticFalseKey;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
#[repr(C, packed)]
|
||||
pub struct TraceEntry {
|
||||
pub type_: u16,
|
||||
pub flags: u8,
|
||||
@ -38,16 +38,13 @@ pub struct TracePoint {
|
||||
system: &'static str,
|
||||
key: &'static StaticFalseKey,
|
||||
id: AtomicU32,
|
||||
inner: SpinLock<TracePointInner>,
|
||||
trace_entry_fmt_func: fn(*const u8) -> String,
|
||||
callback: SpinLock<BTreeMap<usize, TracePointFunc>>,
|
||||
raw_callback: SpinLock<BTreeMap<usize, Box<dyn TracePointCallBackFunc>>>,
|
||||
trace_entry_fmt_func: fn(&[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")
|
||||
@ -72,12 +69,16 @@ pub struct TracePointFunc {
|
||||
pub data: Box<dyn Any + Send + Sync>,
|
||||
}
|
||||
|
||||
pub trait TracePointCallBackFunc: Send + Sync {
|
||||
fn call(&self, entry: &[u8]);
|
||||
}
|
||||
|
||||
impl TracePoint {
|
||||
pub const fn new(
|
||||
key: &'static StaticFalseKey,
|
||||
name: &'static str,
|
||||
system: &'static str,
|
||||
fmt_func: fn(*const u8) -> String,
|
||||
fmt_func: fn(&[u8]) -> String,
|
||||
trace_print_func: fn() -> String,
|
||||
) -> Self {
|
||||
Self {
|
||||
@ -88,9 +89,8 @@ impl TracePoint {
|
||||
flags: 0,
|
||||
trace_entry_fmt_func: fmt_func,
|
||||
trace_print_func,
|
||||
inner: SpinLock::new(TracePointInner {
|
||||
callback: BTreeMap::new(),
|
||||
}),
|
||||
callback: SpinLock::new(BTreeMap::new()),
|
||||
raw_callback: SpinLock::new(BTreeMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +120,7 @@ impl TracePoint {
|
||||
}
|
||||
|
||||
/// Returns the format function for the tracepoint.
|
||||
pub(crate) fn fmt_func(&self) -> fn(*const u8) -> String {
|
||||
pub(crate) fn fmt_func(&self) -> fn(&[u8]) -> String {
|
||||
self.trace_entry_fmt_func
|
||||
}
|
||||
|
||||
@ -137,27 +137,50 @@ impl 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);
|
||||
self.callback.lock().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);
|
||||
self.callback.lock().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() {
|
||||
let callback = self.callback.lock();
|
||||
for trace_func in callback.values() {
|
||||
f(trace_func);
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a raw callback function to the tracepoint
|
||||
///
|
||||
/// This function will be called when default tracepoint fmt function is called.
|
||||
pub fn register_raw_callback(
|
||||
&self,
|
||||
callback_id: usize,
|
||||
callback: Box<dyn TracePointCallBackFunc>,
|
||||
) {
|
||||
self.raw_callback
|
||||
.lock()
|
||||
.entry(callback_id)
|
||||
.or_insert(callback);
|
||||
}
|
||||
|
||||
/// Unregister a raw callback function from the tracepoint
|
||||
pub fn unregister_raw_callback(&self, callback_id: usize) {
|
||||
self.raw_callback.lock().remove(&callback_id);
|
||||
}
|
||||
|
||||
/// Iterate over all registered raw callback functions
|
||||
pub fn raw_callback_list(&self, f: &dyn Fn(&Box<dyn TracePointCallBackFunc>)) {
|
||||
let raw_callback = self.raw_callback.lock();
|
||||
for callback in raw_callback.values() {
|
||||
f(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable the tracepoint
|
||||
pub fn enable(&self) {
|
||||
unsafe {
|
||||
|
@ -29,6 +29,7 @@ impl TracePipeRaw {
|
||||
/// 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.
|
||||
#[allow(unused)]
|
||||
pub fn set_max_record(&mut self, max_record: usize) {
|
||||
self.max_record = max_record;
|
||||
if self.event_buf.len() > max_record {
|
||||
@ -44,11 +45,6 @@ impl TracePipeRaw {
|
||||
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();
|
||||
@ -180,6 +176,38 @@ impl TraceCmdLineCache {
|
||||
self.cmdline.truncate(max_len); // Keep only the latest records
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the maximum number of records in the cache.
|
||||
pub fn max_record(&self) -> usize {
|
||||
self.max_record
|
||||
}
|
||||
|
||||
/// Create a snapshot of the current state of the command line cache.
|
||||
pub fn snapshot(&self) -> TraceCmdLineCacheSnapshot {
|
||||
TraceCmdLineCacheSnapshot::new(self.cmdline.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TraceCmdLineCacheSnapshot(Vec<(u32, [u8; 16])>);
|
||||
impl TraceCmdLineCacheSnapshot {
|
||||
pub fn new(cmdline: Vec<(u32, [u8; 16])>) -> Self {
|
||||
Self(cmdline)
|
||||
}
|
||||
|
||||
/// Return the first command line entry in the cache.
|
||||
pub fn peek(&self) -> Option<&(u32, [u8; 16])> {
|
||||
self.0.first()
|
||||
}
|
||||
|
||||
/// Remove and return the first command line entry in the cache.
|
||||
pub fn pop(&mut self) -> Option<(u32, [u8; 16])> {
|
||||
if self.0.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.0.remove(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TraceEntryParser;
|
||||
@ -196,12 +224,14 @@ impl TraceEntryParser {
|
||||
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 str = fmt_func(&entry[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("<...>");
|
||||
// Copy the packed field to a local variable to avoid unaligned reference
|
||||
let pid = trace_entry.pid;
|
||||
let pname = cmdline_cache.get(pid as u32).unwrap_or("<...>");
|
||||
|
||||
let secs = time / 1_000_000_000;
|
||||
let usec_rem = time % 1_000_000_000 / 1000;
|
||||
@ -209,7 +239,7 @@ impl TraceEntryParser {
|
||||
format!(
|
||||
"{:>16}-{:<7} [{:03}] {} {:5}.{:06}: {}({})\n",
|
||||
pname,
|
||||
trace_entry.pid,
|
||||
pid,
|
||||
cpu_id,
|
||||
trace_entry.trace_print_lat_fmt(),
|
||||
secs,
|
||||
|
Reference in New Issue
Block a user