diff --git a/docs/kernel/trace/index.rst b/docs/kernel/trace/index.rst index 23b4c8fa..43afc6d3 100644 --- a/docs/kernel/trace/index.rst +++ b/docs/kernel/trace/index.rst @@ -9,3 +9,4 @@ eBPF kprobe + tracepoint diff --git a/docs/kernel/trace/tracepoint.md b/docs/kernel/trace/tracepoint.md new file mode 100644 index 00000000..36ce91b7 --- /dev/null +++ b/docs/kernel/trace/tracepoint.md @@ -0,0 +1,60 @@ +# Tracepoints + +> 作者: 陈林峰 +> +> Email: chenlinfeng25@outlook.com + + +## 概述 +Tracepoints 是 Linux 内核提供的一种跟踪机制,允许开发者在内核代码的特定位置插入探测点,以便收集运行时信息。与 kprobes 不同,tracepoints 是预定义的,并且通常用于收集性能数据或调试信息,而不需要修改内核代码。 + +## 工作流程 +1. **定义 Tracepoint**: 内核开发者在代码中定义 tracepoint,使用宏 `define_event_trace`。 +2. **触发 Tracepoint**: 在代码的其他部分,开发者可以使用 `define_event_trace` 定义的函数来触发已定义的 tracepoint,并传递相关的上下文信息。比如定义的tracepoint名称为`my_tracepoint`,则可以使用 `trace_my_tracepoint()` 来触发它。 +3. **收集数据**: 当 tracepoint 被触发时,内核会记录相关的数据,这些数据可以通过用户空间工具(如 `trace-cmd` 或 `perf`)进行分析。现在DragonOS中还只能支持查看内核为这些数据创建的文件。 + 1. 读取`/sys/kernel/debug/tracing/trace`文件可以查看tracepoint收集的数据。缓冲区的内容不会被清除。 + 2. 读取`/sys/kernel/debug/tracing/trace_pipe`文件可以查看tracepoint收集的数据。缓冲区的内容会被清除。如果缓冲区没有内容,则会阻塞等待新的数据。 + 3. 读取`/sys/kernel/debug/tracing/events/`目录可以查看所有可用的 tracepoints。 + 4. 读取`/sys/kernel/debug/tracing/events//format`文件可以查看特定 tracepoint 的数据格式。 + 5. 写入`/sys/kernel/debug/tracing/events//enable` 文件可以启用特定的 tracepoint。 + 6. 写入空值到 `/sys/kernel/debug/tracing/trace` 可以清空当前的 trace 数据。 +4. **分析数据**: 用户空间工具可以读取 tracepoint 收集的数据,并生成报告或图表,以帮助开发者理解内核行为。 + + +## 接口 +```rust +define_event_trace!() // 定义一个 tracepoint +``` + +```rust +// example +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 + ) + }) +); +``` +## Tracepoints For eBPF +在 DragonOS 中,tracepoints 也可以与 eBPF 结合使用,以便在内核中收集更丰富的数据。eBPF 程序可以附加到 tracepoints 上,以便在 tracepoint 被触发时执行自定义的逻辑。 diff --git a/kernel/src/bpf/helper/consts.rs b/kernel/src/bpf/helper/consts.rs index 0c368545..8c64a4f7 100644 --- a/kernel/src/bpf/helper/consts.rs +++ b/kernel/src/bpf/helper/consts.rs @@ -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; diff --git a/kernel/src/bpf/helper/mod.rs b/kernel/src/bpf/helper/mod.rs index 2c876b25..2dccb697 100644 --- a/kernel/src/bpf/helper/mod.rs +++ b/kernel/src/bpf/helper/mod.rs @@ -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!( + // ": 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 { + 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> = 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); } diff --git a/kernel/src/debug/tracing/mod.rs b/kernel/src/debug/tracing/mod.rs index 130bebe0..a59911b1 100644 --- a/kernel/src/debug/tracing/mod.rs +++ b/kernel/src/debug/tracing/mod.rs @@ -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> = None; static TRACE_RAW_PIPE: SpinLock = @@ -22,6 +24,7 @@ static TRACE_CMDLINE_CACHE: SpinLock = 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)?; diff --git a/kernel/src/debug/tracing/trace_pipe.rs b/kernel/src/debug/tracing/trace_pipe.rs index 6fa849e4..7d49be51 100644 --- a/kernel/src/debug/tracing/trace_pipe.rs +++ b/kernel/src/debug/tracing/trace_pipe.rs @@ -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 { - 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 { @@ -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 { + 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 { + 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 { + 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 { + 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 { + Err(SystemError::EPERM) + } + + fn poll(&self, _data: KernCallbackData) -> Result { + Err(SystemError::ENOSYS) + } +} diff --git a/kernel/src/filesystem/kernfs/callback.rs b/kernel/src/filesystem/kernfs/callback.rs index db1c96e6..2ef68868 100644 --- a/kernel/src/filesystem/kernfs/callback.rs +++ b/kernel/src/filesystem/kernfs/callback.rs @@ -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), TracePipe(TracePipeSnapshot), + TraceSavedCmdlines(TraceCmdLineCacheSnapshot), } impl KernInodePrivateData { diff --git a/kernel/src/filesystem/vfs/syscall/open_utils.rs b/kernel/src/filesystem/vfs/syscall/open_utils.rs index b887ae47..d0076674 100644 --- a/kernel/src/filesystem/vfs/syscall/open_utils.rs +++ b/kernel/src/filesystem/vfs/syscall/open_utils.rs @@ -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 { + 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 + ) + }) +); diff --git a/kernel/src/perf/mod.rs b/kernel/src/perf/mod.rs index b5622780..da39b395 100644 --- a/kernel/src/perf/mod.rs +++ b/kernel/src/perf/mod.rs @@ -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); } diff --git a/kernel/src/perf/tracepoint.rs b/kernel/src/perf/tracepoint.rs new file mode 100644 index 00000000..28ec56d6 --- /dev/null +++ b/kernel/src/perf/tracepoint.rs @@ -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>, +} + +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, + ) -> Result { + panic!("read_at not implemented for TracepointPerfEvent"); + } + + fn write_at( + &self, + _offset: usize, + _len: usize, + _buf: &[u8], + _data: SpinLockGuard, + ) -> Result { + panic!("write_at not implemented for TracepointPerfEvent"); + } + + fn fs(&self) -> Arc { + panic!("fs not implemented for TracepointPerfEvent"); + } + + fn as_any_ref(&self) -> &dyn Any { + self + } + + fn list(&self) -> Result> { + Err(SystemError::ENOSYS) + } + + fn page_cache(&self) -> Option> { + None + } +} + +pub struct TracePointPerfCallBack { + _bpf_prog_file: Arc, + vm: EbpfVmRawOwned, +} + +impl TracePointPerfCallBack { + fn new(bpf_prog_file: Arc, 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) -> Result<()> { + static CALLBACK_ID: AtomicUsize = AtomicUsize::new(0); + + let file = bpf_prog + .inode() + .downcast_arc::() + .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 { + 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)) +} diff --git a/kernel/src/perf/util.rs b/kernel/src/perf/util.rs index 1b24eed6..7170fbd8 100644 --- a/kernel/src/perf/util.rs +++ b/kernel/src/perf/util.rs @@ -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, } +#[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 { 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)?; diff --git a/kernel/src/tracepoint/basic_macro.rs b/kernel/src/tracepoint/basic_macro.rs index 1663aad1..3216b3e6 100644 --- a/kernel/src/tracepoint/basic_macro.rs +++ b/kernel/src/tracepoint/basic_macro.rs @@ -93,12 +93,13 @@ macro_rules! define_event_trace{ }; #[allow(unused,non_snake_case)] + #[allow(clippy::redundant_field_names)] pub fn [](_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::(), ) }; + + let func = |f:&alloc::boxed::Box|{ + 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 [](buf_ptr: *const u8) -> alloc::string::String { + pub fn [](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 diff --git a/kernel/src/tracepoint/mod.rs b/kernel/src/tracepoint/mod.rs index b963d43d..fe133981 100644 --- a/kernel/src/tracepoint/mod.rs +++ b/kernel/src/tracepoint/mod.rs @@ -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}; diff --git a/kernel/src/tracepoint/point.rs b/kernel/src/tracepoint/point.rs index 00275908..e40548e1 100644 --- a/kernel/src/tracepoint/point.rs +++ b/kernel/src/tracepoint/point.rs @@ -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, - trace_entry_fmt_func: fn(*const u8) -> String, + callback: SpinLock>, + raw_callback: SpinLock>>, + trace_entry_fmt_func: fn(&[u8]) -> String, trace_print_func: fn() -> String, flags: u8, } -struct TracePointInner { - callback: BTreeMap, -} - impl core::fmt::Debug for TracePoint { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("TracePoint") @@ -72,12 +69,16 @@ pub struct TracePointFunc { pub data: Box, } +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) { 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, + ) { + 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)) { + let raw_callback = self.raw_callback.lock(); + for callback in raw_callback.values() { + f(callback); + } + } + /// Enable the tracepoint pub fn enable(&self) { unsafe { diff --git a/kernel/src/tracepoint/trace_pipe.rs b/kernel/src/tracepoint/trace_pipe.rs index a92a6565..8e6d12cc 100644 --- a/kernel/src/tracepoint/trace_pipe.rs +++ b/kernel/src/tracepoint/trace_pipe.rs @@ -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::(); - 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, diff --git a/user/apps/test_tracepoint/.gitignore b/user/apps/test_tracepoint/.gitignore new file mode 100644 index 00000000..9db7029f --- /dev/null +++ b/user/apps/test_tracepoint/.gitignore @@ -0,0 +1,9 @@ +### https://raw.github.com/github/gitignore/master/Rust.gitignore + +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt +**/*.rs.bk diff --git a/user/apps/test_tracepoint/Cargo.lock b/user/apps/test_tracepoint/Cargo.lock new file mode 100644 index 00000000..f3497308 --- /dev/null +++ b/user/apps/test_tracepoint/Cargo.lock @@ -0,0 +1,794 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "aya" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d18bc4e506fbb85ab7392ed993a7db4d1a452c71b75a246af4a80ab8c9d2dd50" +dependencies = [ + "assert_matches", + "aya-obj", + "bitflags", + "bytes", + "libc", + "log", + "object", + "once_cell", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "aya-build" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c92c523541cbf5e3a94c7a6ff4068a4d9537f98a2eeb136461c0537ded8c1" +dependencies = [ + "anyhow", + "cargo_metadata", +] + +[[package]] +name = "aya-ebpf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8dbaf5409a1a0982e5c9bdc0f499a55fe5ead39fe9c846012053faf0d404f73" +dependencies = [ + "aya-ebpf-bindings", + "aya-ebpf-cty", + "aya-ebpf-macros", + "rustversion", +] + +[[package]] +name = "aya-ebpf-bindings" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "783dc1a82a3d71d83286165381dcc1b1d41643f4b110733d135547527c000a9a" +dependencies = [ + "aya-ebpf-cty", +] + +[[package]] +name = "aya-ebpf-cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cce099aaf3abb89f9a1f8594ffe07fa53738ebc2882fac624d10d9ba31a1b10" + +[[package]] +name = "aya-ebpf-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f47f7b4a75eb5f1d7ba0fb5628d247b1cf20388658899177875dabdda66865" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "aya-log" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b600d806c1d07d3b81ab5f4a2a95fd80f479a0d3f1d68f29064d660865f85f02" +dependencies = [ + "aya", + "aya-log-common", + "bytes", + "log", + "thiserror 1.0.69", + "tokio", +] + +[[package]] +name = "aya-log-common" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "befef9fe882e63164a2ba0161874e954648a72b0e1c4b361f532d590638c4eec" +dependencies = [ + "num_enum", +] + +[[package]] +name = "aya-log-ebpf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae348f459df78a79e5cd5e164b6562b927033b97ca3b033605b341a474f44510" +dependencies = [ + "aya-ebpf", + "aya-log-common", + "aya-log-ebpf-macros", +] + +[[package]] +name = "aya-log-ebpf-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d8251a75f56077db51892041aa6b77c70ef2723845d7a210979700b2f01bc4" +dependencies = [ + "aya-log-common", + "aya-log-parser", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "aya-log-parser" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b102eb5c88c9aa0b49102d3fbcee08ecb0dfa81014f39b373311de7a7032cb" +dependencies = [ + "aya-log-common", +] + +[[package]] +name = "aya-obj" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51b96c5a8ed8705b40d655273bc4212cbbf38d4e3be2788f36306f154523ec7" +dependencies = [ + "bytes", + "core-error", + "hashbrown", + "log", + "object", + "thiserror 1.0.69", +] + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.12", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "core-error" +version = "0.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efcdb2972eb64230b4c50646d8498ff73f5128d196a90c7236eec4cbe8619b8f" +dependencies = [ + "version_check", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "env_filter", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.59.0", +] + +[[package]] +name = "mytrace" +version = "0.1.0" +dependencies = [ + "anyhow", + "aya", + "aya-build", + "aya-log", + "env_logger", + "libc", + "log", + "mytrace-common", + "mytrace-ebpf", + "tokio", +] + +[[package]] +name = "mytrace-common" +version = "0.1.0" +dependencies = [ + "aya", +] + +[[package]] +name = "mytrace-ebpf" +version = "0.1.0" +dependencies = [ + "aya-ebpf", + "aya-log-ebpf", + "mytrace-common", + "which", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "crc32fast", + "hashbrown", + "indexmap", + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +dependencies = [ + "libc", +] + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +dependencies = [ + "backtrace", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "which" +version = "6.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" +dependencies = [ + "either", + "home", + "rustix", + "winsafe", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" diff --git a/user/apps/test_tracepoint/Cargo.toml b/user/apps/test_tracepoint/Cargo.toml new file mode 100644 index 00000000..b6f76384 --- /dev/null +++ b/user/apps/test_tracepoint/Cargo.toml @@ -0,0 +1,33 @@ +[workspace] +resolver = "2" +members = [ + "mytrace", + "mytrace-common", + "mytrace-ebpf", +] +default-members = ["mytrace", "mytrace-common"] + +[workspace.package] +license = "MIT OR Apache-2.0" + +[workspace.dependencies] +aya = { version = "0.13.1", default-features = false } +aya-build = { version = "0.1.2", default-features = false } +aya-ebpf = { version = "0.1.1", default-features = false } +aya-log = { version = "0.2.1", default-features = false } +aya-log-ebpf = { version = "0.1.1", default-features = false } + +anyhow = { version = "1", default-features = false } +# `std` feature is currently required to build `clap`. +# +# See https://github.com/clap-rs/clap/blob/61f5ee5/clap_builder/src/lib.rs#L15. +clap = { version = "4.5.20", default-features = false, features = ["std"] } +env_logger = { version = "0.11.5", default-features = false } +libc = { version = "0.2.159", default-features = false } +log = { version = "0.4.22", default-features = false } +tokio = { version = "1.40.0", default-features = false } +which = { version = "6.0.0", default-features = false } + +[profile.release.package.mytrace-ebpf] +debug = 2 +codegen-units = 1 diff --git a/user/apps/test_tracepoint/Makefile b/user/apps/test_tracepoint/Makefile new file mode 100644 index 00000000..27b55141 --- /dev/null +++ b/user/apps/test_tracepoint/Makefile @@ -0,0 +1,57 @@ +TOOLCHAIN="+nightly-2024-11-05-x86_64-unknown-linux-gnu" +RUSTFLAGS+="" + +ifdef DADK_CURRENT_BUILD_DIR +# 如果是在dadk中编译,那么安装到dadk的安装目录中 + INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR) +else +# 如果是在本地编译,那么安装到当前目录下的install目录中 + INSTALL_DIR = ./install +endif + +ifeq ($(ARCH), x86_64) + export RUST_TARGET=x86_64-unknown-linux-musl +else ifeq ($(ARCH), riscv64) + export RUST_TARGET=riscv64gc-unknown-linux-gnu +else +# 默认为x86_86,用于本地编译 + export RUST_TARGET=x86_64-unknown-linux-musl +endif + +run: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) + +build: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) + +clean: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) + +test: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) + +doc: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET) + +fmt: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt + +fmt-check: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check + +run-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release + +build-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release + +clean-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release + +test-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release + + +.PHONY: install +install: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path ./mytrace --no-track --root $(INSTALL_DIR) --force diff --git a/user/apps/test_tracepoint/README.md b/user/apps/test_tracepoint/README.md new file mode 100644 index 00000000..9a95d3d7 --- /dev/null +++ b/user/apps/test_tracepoint/README.md @@ -0,0 +1,57 @@ +# mytrace + +## Prerequisites + +1. stable rust toolchains: `rustup toolchain install stable` +1. nightly rust toolchains: `rustup toolchain install nightly --component rust-src` +1. (if cross-compiling) rustup target: `rustup target add ${ARCH}-unknown-linux-musl` +1. (if cross-compiling) LLVM: (e.g.) `brew install llvm` (on macOS) +1. (if cross-compiling) C toolchain: (e.g.) [`brew install filosottile/musl-cross/musl-cross`](https://github.com/FiloSottile/homebrew-musl-cross) (on macOS) +1. bpf-linker: `cargo install bpf-linker` (`--no-default-features` on macOS) + +## Build & Run + +Use `cargo build`, `cargo check`, etc. as normal. Run your program with: + +```shell +cargo run --release --config 'target."cfg(all())".runner="sudo -E"' +``` + +Cargo build scripts are used to automatically build the eBPF correctly and include it in the +program. + +## Cross-compiling on macOS + +Cross compilation should work on both Intel and Apple Silicon Macs. + +```shell +CC=${ARCH}-linux-musl-gcc cargo build --package mytrace --release \ + --target=${ARCH}-unknown-linux-musl \ + --config=target.${ARCH}-unknown-linux-musl.linker=\"${ARCH}-linux-musl-gcc\" +``` +The cross-compiled program `target/${ARCH}-unknown-linux-musl/release/mytrace` can be +copied to a Linux server or VM and run there. + +## License + +With the exception of eBPF code, mytrace is distributed under the terms +of either the [MIT license] or the [Apache License] (version 2.0), at your +option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + +### eBPF + +All eBPF code is distributed under either the terms of the +[GNU General Public License, Version 2] or the [MIT license], at your +option. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this project by you, as defined in the GPL-2 license, shall be +dual licensed as above, without any additional terms or conditions. + +[Apache license]: LICENSE-APACHE +[MIT license]: LICENSE-MIT +[GNU General Public License, Version 2]: LICENSE-GPL2 diff --git a/user/apps/test_tracepoint/mytrace-common/Cargo.toml b/user/apps/test_tracepoint/mytrace-common/Cargo.toml new file mode 100644 index 00000000..82ebe98f --- /dev/null +++ b/user/apps/test_tracepoint/mytrace-common/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "mytrace-common" +version = "0.1.0" +edition = "2021" + +license.workspace = true + +[features] +default = [] +user = ["aya"] + +[dependencies] +aya = { workspace = true, optional = true } + +[lib] +path = "src/lib.rs" diff --git a/user/apps/test_tracepoint/mytrace-common/src/lib.rs b/user/apps/test_tracepoint/mytrace-common/src/lib.rs new file mode 100644 index 00000000..0c9ac1ac --- /dev/null +++ b/user/apps/test_tracepoint/mytrace-common/src/lib.rs @@ -0,0 +1 @@ +#![no_std] diff --git a/user/apps/test_tracepoint/mytrace-ebpf/Cargo.toml b/user/apps/test_tracepoint/mytrace-ebpf/Cargo.toml new file mode 100644 index 00000000..dc7781cf --- /dev/null +++ b/user/apps/test_tracepoint/mytrace-ebpf/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "mytrace-ebpf" +version = "0.1.0" +edition = "2021" + +[dependencies] +mytrace-common = { path = "../mytrace-common" } + +aya-ebpf = { workspace = true } +aya-log-ebpf = { workspace = true } + +[build-dependencies] +which = { workspace = true } + +[[bin]] +name = "mytrace" +path = "src/main.rs" diff --git a/user/apps/test_tracepoint/mytrace-ebpf/build.rs b/user/apps/test_tracepoint/mytrace-ebpf/build.rs new file mode 100644 index 00000000..f83c317a --- /dev/null +++ b/user/apps/test_tracepoint/mytrace-ebpf/build.rs @@ -0,0 +1,17 @@ +use which::which; + +/// Building this crate has an undeclared dependency on the `bpf-linker` binary. This would be +/// better expressed by [artifact-dependencies][bindeps] but issues such as +/// https://github.com/rust-lang/cargo/issues/12385 make their use impractical for the time being. +/// +/// This file implements an imperfect solution: it causes cargo to rebuild the crate whenever the +/// mtime of `which bpf-linker` changes. Note that possibility that a new bpf-linker is added to +/// $PATH ahead of the one used as the cache key still exists. Solving this in the general case +/// would require rebuild-if-changed-env=PATH *and* rebuild-if-changed={every-directory-in-PATH} +/// which would likely mean far too much cache invalidation. +/// +/// [bindeps]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html?highlight=feature#artifact-dependencies +fn main() { + let bpf_linker = which("bpf-linker").unwrap(); + println!("cargo:rerun-if-changed={}", bpf_linker.to_str().unwrap()); +} diff --git a/user/apps/test_tracepoint/mytrace-ebpf/src/lib.rs b/user/apps/test_tracepoint/mytrace-ebpf/src/lib.rs new file mode 100644 index 00000000..3ac3e595 --- /dev/null +++ b/user/apps/test_tracepoint/mytrace-ebpf/src/lib.rs @@ -0,0 +1,3 @@ +#![no_std] + +// This file exists to enable the library target. diff --git a/user/apps/test_tracepoint/mytrace-ebpf/src/main.rs b/user/apps/test_tracepoint/mytrace-ebpf/src/main.rs new file mode 100644 index 00000000..da5f078e --- /dev/null +++ b/user/apps/test_tracepoint/mytrace-ebpf/src/main.rs @@ -0,0 +1,119 @@ +#![no_std] +#![no_main] + +use aya_ebpf::{ + helpers::bpf_probe_read_user_str_bytes, + macros::{map, tracepoint}, + maps::PerCpuArray, + programs::TracePointContext, +}; +use aya_log_ebpf::info; + +const MAX_PATH: usize = 4096; + +#[repr(C)] +pub struct Buf { + pub buf: [u8; MAX_PATH], +} + +#[map] +pub static mut BUF: PerCpuArray = PerCpuArray::with_max_entries(1, 0); // + +#[tracepoint] +pub fn mytrace(ctx: TracePointContext) -> u32 { + match try_mytrace(ctx) { + Ok(ret) => ret, + Err(ret) => ret, + } +} + +fn try_mytrace(ctx: TracePointContext) -> Result { + // info!(&ctx, "tracepoint sys_enter_openat called"); + match try_aya_tracepoint_echo_open(&ctx) { + Ok(_) => Ok(0), + Err(e) => { + info!(&ctx, "tracepoint sys_enter_openat called, error: {}", e); + Err(e as u32) + } + } +} + +fn try_aya_tracepoint_echo_open(ctx: &TracePointContext) -> Result { + // Load the pointer to the filename. The offset value can be found running: + // sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_open/format + const FILENAME_OFFSET: usize = 12; + + if let Ok(filename_addr) = unsafe { ctx.read_at::(FILENAME_OFFSET) } { + // get the map-backed buffer that we're going to use as storage for the filename + let buf = unsafe { + let ptr = BUF.get_ptr_mut(0).ok_or(0)?; // + + &mut *ptr + }; + + // read the filename + let filename = unsafe { + core::str::from_utf8_unchecked(bpf_probe_read_user_str_bytes( + filename_addr as *const u8, + &mut buf.buf, + )?) + }; + + if filename.len() < MAX_PATH { + // log the filename + info!( + ctx, + "Kernel tracepoint sys_enter_openat called, filename :{}", filename + ); + } + } + Ok(0) +} + +// This function assumes that the maximum length of a file's path can be of 16 bytes. This is meant +// to be read as an example, only. Refer to the accompanying `tracepoints.md` for its inclusion in the +// code. +fn try_aya_tracepoint_echo_open_small_file_path(ctx: &TracePointContext) -> Result { + const MAX_SMALL_PATH: usize = 16; + let mut buf: [u8; MAX_SMALL_PATH] = [0; MAX_SMALL_PATH]; + + // Load the pointer to the filename. The offset value can be found running: + // sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_open/format + const FILENAME_OFFSET: usize = 12; + if let Ok(filename_addr) = unsafe { ctx.read_at::(FILENAME_OFFSET) } { + // read the filename + let filename = unsafe { + // Get an UTF-8 String from an array of bytes + core::str::from_utf8_unchecked( + // Use the address of the kernel's string // + + // to copy its contents into the array named 'buf' + match bpf_probe_read_user_str_bytes(filename_addr as *const u8, &mut buf) { + Ok(_) => &buf, + Err(e) => { + info!( + ctx, + "tracepoint sys_enter_openat called buf_probe failed {}", e + ); + return Err(e); + } + }, + ) + }; + info!( + ctx, + "tracepoint sys_enter_openat called, filename {}", filename + ); + } + Ok(0) +} + +#[cfg(not(test))] +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[link_section = "license"] +#[no_mangle] +static LICENSE: [u8; 13] = *b"Dual MIT/GPL\0"; diff --git a/user/apps/test_tracepoint/mytrace/Cargo.toml b/user/apps/test_tracepoint/mytrace/Cargo.toml new file mode 100644 index 00000000..e733b848 --- /dev/null +++ b/user/apps/test_tracepoint/mytrace/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "mytrace" +version = "0.1.0" +edition = "2021" + +license.workspace = true + +[dependencies] +mytrace-common = { path = "../mytrace-common", features = ["user"] } + +anyhow = { workspace = true, default-features = true } +aya = { workspace = true } +aya-log = { workspace = true } +env_logger = { workspace = true } +libc = { workspace = true } +log = { workspace = true } +tokio = { workspace = true, features = [ + "macros", + "rt", + "rt-multi-thread", + "net", + "signal", + "time" +] } +[build-dependencies] +anyhow = { workspace = true } +aya-build = { workspace = true } +# TODO(https://github.com/rust-lang/cargo/issues/12375): this should be an artifact dependency, but +# it's not possible to tell cargo to use `-Z build-std` to build it. We cargo-in-cargo in the build +# script to build this, but we want to teach cargo about the dependecy so that cache invalidation +# works properly. +# +# Note also that https://github.com/rust-lang/cargo/issues/10593 occurs when `target = ...` is added +# to an artifact dependency; it seems possible to work around that by setting `resolver = "1"` in +# Cargo.toml in the workspace root. +# +# Finally note that *any* usage of `artifact = ...` in *any* Cargo.toml in the workspace breaks +# workflows with stable cargo; stable cargo outright refuses to load manifests that use unstable +# features. +mytrace-ebpf = { path = "../mytrace-ebpf" } + +[[bin]] +name = "mytrace" +path = "src/main.rs" diff --git a/user/apps/test_tracepoint/mytrace/build.rs b/user/apps/test_tracepoint/mytrace/build.rs new file mode 100644 index 00000000..6822c487 --- /dev/null +++ b/user/apps/test_tracepoint/mytrace/build.rs @@ -0,0 +1,14 @@ +use anyhow::{anyhow, Context as _}; +use aya_build::cargo_metadata; + +fn main() -> anyhow::Result<()> { + let cargo_metadata::Metadata { packages, .. } = cargo_metadata::MetadataCommand::new() + .no_deps() + .exec() + .context("MetadataCommand::exec")?; + let ebpf_package = packages + .into_iter() + .find(|cargo_metadata::Package { name, .. }| name == "mytrace-ebpf") + .ok_or_else(|| anyhow!("mytrace-ebpf package not found"))?; + aya_build::build_ebpf([ebpf_package]) +} diff --git a/user/apps/test_tracepoint/mytrace/src/main.rs b/user/apps/test_tracepoint/mytrace/src/main.rs new file mode 100644 index 00000000..1d2649b7 --- /dev/null +++ b/user/apps/test_tracepoint/mytrace/src/main.rs @@ -0,0 +1,62 @@ +use aya::programs::TracePoint; +#[rustfmt::skip] +use log::{debug, warn}; +use tokio::{signal, task::yield_now, time}; +#[tokio::main(flavor = "current_thread")] +async fn main() -> anyhow::Result<()> { + // env_logger::init(); + + env_logger::builder() + .filter_level(log::LevelFilter::Info) + .format_timestamp(None) + .init(); + // Bump the memlock rlimit. This is needed for older kernels that don't use the + // new memcg based accounting, see https://lwn.net/Articles/837122/ + let rlim = libc::rlimit { + rlim_cur: libc::RLIM_INFINITY, + rlim_max: libc::RLIM_INFINITY, + }; + let ret = unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; + if ret != 0 { + debug!("remove limit on locked memory failed, ret is: {ret}"); + } + + // This will include your eBPF object file as raw bytes at compile-time and load it at + // runtime. This approach is recommended for most real-world use cases. If you would + // like to specify the eBPF program at runtime rather than at compile-time, you can + // reach for `Bpf::load_file` instead. + let mut ebpf = aya::Ebpf::load(aya::include_bytes_aligned!(concat!( + env!("OUT_DIR"), + "/mytrace" + )))?; + if let Err(e) = aya_log::EbpfLogger::init(&mut ebpf) { + // This can happen if you remove all log statements from your eBPF program. + warn!("failed to initialize eBPF logger: {e}"); + } + let program: &mut TracePoint = ebpf.program_mut("mytrace").unwrap().try_into()?; + program.load()?; + program.attach("syscalls", "sys_enter_openat")?; + + tokio::spawn(async move { + let mut now = time::Instant::now(); + loop { + let new_now = time::Instant::now(); + let duration = new_now.duration_since(now); + if duration.as_secs() >= 2 { + // open a file to trigger the tracepoint + log::info!("Triggering tracepoint by opening /bin"); + let bin = std::fs::File::open("/bin").unwrap(); + drop(bin); + now = new_now; + } + yield_now().await; + } + }); + + let ctrl_c = signal::ctrl_c(); + println!("Waiting for Ctrl-C..."); + ctrl_c.await?; + println!("Exiting..."); + + Ok(()) +} diff --git a/user/apps/test_tracepoint/rustfmt.toml b/user/apps/test_tracepoint/rustfmt.toml new file mode 100644 index 00000000..53f7b6d7 --- /dev/null +++ b/user/apps/test_tracepoint/rustfmt.toml @@ -0,0 +1,4 @@ +group_imports = "StdExternalCrate" +imports_granularity = "Crate" +reorder_imports = true +unstable_features = true diff --git a/user/dadk/config/test_tracepoint.toml b/user/dadk/config/test_tracepoint.toml new file mode 100644 index 00000000..d3e02ea9 --- /dev/null +++ b/user/dadk/config/test_tracepoint.toml @@ -0,0 +1,37 @@ +# 用户程序名称 +name = "test_ebpf_tp" +# 版本号 +version = "0.1.0" +# 用户程序描述信息 +description = "to test eBPF" +# (可选)默认: false 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果 +build-once = false +# (可选) 默认: false 是否只安装一次,如果为true,DADK会在安装成功后,不再重复安装 +install-once = false +# 目标架构 +# 可选值:"x86_64", "aarch64", "riscv64" +target-arch = ["x86_64"] +# 任务源 +[task-source] +# 构建类型 +# 可选值:"build-from_source", "install-from-prebuilt" +type = "build-from-source" +# 构建来源 +# "build_from_source" 可选值:"git", "local", "archive" +# "install_from_prebuilt" 可选值:"local", "archive" +source = "local" +# 路径或URL +source-path = "user/apps/test_tracepoint" +# 构建相关信息 +[build] +# (可选)构建命令 +build-command = "make install" +# 安装相关信息 +[install] +# (可选)安装到DragonOS的路径 +in-dragonos-path = "/" +# 清除相关信息 +[clean] +# (可选)清除命令 +clean-command = "make clean" +