extern crate alloc;
use std::{collections::BTreeMap, sync::Arc};
use ktracepoint::{
RawTraceEventFunc, TraceCallbackType, TraceCmdLineCache, TraceEntryParser, TraceEventFunc,
TraceFilterFile, TracePointEnableFile, TracePointMap, global_init_events,
};
use crate::tracepoint_example::{EXT_TRACEPOINTS, Kops, TRACE_RAW_PIPE};
mod tracepoint_example {
use std::{
collections::BTreeMap,
num::NonZero,
ops::Deref,
sync::{Arc, LazyLock, Mutex, RwLock},
time,
};
use ktracepoint::{ExtTracePoint, TraceCmdLineCache, define_event_trace};
pub static TRACE_RAW_PIPE: Mutex<ktracepoint::TracePipeRaw> =
Mutex::new(ktracepoint::TracePipeRaw::new(1024));
pub static TRACE_CMDLINE_CACHE: LazyLock<Mutex<TraceCmdLineCache>> = LazyLock::new(|| {
Mutex::new(ktracepoint::TraceCmdLineCache::new(
NonZero::new(1024).unwrap(),
))
});
pub static EXT_TRACEPOINTS: RwLock<BTreeMap<u32, ExtTracePoint<Kops>>> =
RwLock::new(BTreeMap::new());
pub struct Kops;
impl ktracepoint::KernelTraceOps for Kops {
fn current_pid() -> u32 {
0xff
}
fn trace_pipe_push_raw_record(buf: &[u8]) {
let time = time::SystemTime::now()
.duration_since(time::UNIX_EPOCH)
.unwrap()
.as_nanos() as u64;
let cpu_id = 8;
let mut pipe = TRACE_RAW_PIPE.lock().unwrap();
pipe.push_record(time, cpu_id, buf.to_vec());
}
fn trace_cmdline_push(pid: u32) {
let mut cache = TRACE_CMDLINE_CACHE.lock().unwrap();
cache.insert(pid, "test_process");
}
fn write_kernel_text(addr: *mut core::ffi::c_void, data: &[u8]) {
let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
let aligned_addr_val = (addr as usize) / page_size * page_size;
let aligned_addr = aligned_addr_val as *mut core::ffi::c_void;
let aligned_length = if (addr as usize) + data.len() - aligned_addr_val > page_size {
page_size * 2
} else {
page_size
};
let mmaped_addr = unsafe {
libc::mmap(
core::ptr::null_mut(),
aligned_length,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
-1,
0,
)
};
if mmaped_addr == libc::MAP_FAILED {
panic!("Failed to create temp mappings");
}
unsafe {
let addr_in_mmap = mmaped_addr.offset(addr.offset_from(aligned_addr));
core::ptr::copy_nonoverlapping(aligned_addr, mmaped_addr, aligned_length);
core::ptr::copy_nonoverlapping(data.as_ptr(), addr_in_mmap.cast(), data.len());
}
let res = unsafe {
libc::mprotect(
mmaped_addr,
aligned_length,
libc::PROT_READ | libc::PROT_EXEC,
)
};
if res != 0 {
panic!("Unable to make mmaped mapping executable.");
}
let res = unsafe {
libc::mremap(
mmaped_addr,
aligned_length,
aligned_length,
libc::MREMAP_MAYMOVE | libc::MREMAP_FIXED,
aligned_addr,
)
};
if res == libc::MAP_FAILED {
panic!("Failed to mremap.");
}
let res = unsafe { clear_cache::clear_cache(addr, addr.add(data.len())) };
if !res {
panic!("Failed to clear cache.");
}
}
fn read_tracepoint_state<R>(id: u32, f: impl FnOnce(&ExtTracePoint<Self>) -> R) -> R {
let states = EXT_TRACEPOINTS.read().unwrap();
states
.get(&id)
.map(f)
.unwrap_or_else(|| panic!("Tracepoint state not found for ID: {}", id))
}
fn write_tracepoint_state<R>(id: u32, f: impl FnOnce(&mut ExtTracePoint<Self>) -> R) -> R {
let mut states = EXT_TRACEPOINTS.write().unwrap();
states
.get_mut(&id)
.map(f)
.unwrap_or_else(|| panic!("Tracepoint state not found for ID: {}", id))
}
}
#[repr(C)]
#[derive(Debug)]
#[allow(clippy::redundant_allocation)]
struct TestS {
a: u32,
b: Box<Arc<u32>>,
}
define_event_trace!(
TEST,
TP_kops(Kops),
TP_system(tracepoint_test),
TP_PROTO(a: u32, b: &TestS),
TP_STRUCT__entry{
a: u32,
pad:[u8;4],
b: u32
},
TP_fast_assign{
a: a,
pad: [0; 4],
b: *b.b.deref().deref()
},
TP_ident(__entry),
TP_printk(
{
let arg1 = __entry.a;
let arg2 = __entry.b;
format!("Hello from tracepoint! a={:?}, b={}", arg1, arg2)
}
)
);
define_event_trace!(
TEST2,
TP_kops(Kops),
TP_system(tracepoint_test),
TP_PROTO(a: u32, b: u32),
TP_STRUCT__entry{
a: u32,
b: u32
},
TP_fast_assign{
a:a,
b:b
},
TP_ident(__entry),
TP_printk(format_args!("Hello from tracepoint! a={}, b={}", __entry.a, __entry.b))
);
pub fn test_trace(a: u32, b: u32) {
let x = TestS {
a,
b: Box::new(Arc::new(b)),
};
trace_TEST(a, &x);
trace_TEST2(a, b);
println!(
"Tracepoint TEST called with a={}, b={}, x ptr={:p}",
a, b, &x
);
}
}
fn print_trace_records(
tracepoint_map: &TracePointMap<Kops>,
trace_cmdline_cache: &TraceCmdLineCache,
) {
use ktracepoint::TracePipeOps;
let mut snapshot = TRACE_RAW_PIPE.lock().unwrap().snapshot();
print!("{}", snapshot.default_fmt_str());
loop {
let mut flag = false;
if let Some(event) = snapshot.peek() {
let trace_str = TraceEntryParser::parse(tracepoint_map, trace_cmdline_cache, event);
print!("{}", trace_str);
flag = true;
}
if flag {
snapshot.pop();
} else {
break;
}
}
}
fn perf_event_callback() -> TraceCallbackType {
let callback = Arc::new(TraceEventFunc::new(
Box::new(|entry, _data| {
println!("FakeEventCallback called with entry: {}", entry.len());
}),
Box::new(()),
));
callback.set_perf_enable(true);
TraceCallbackType::Event(callback)
}
fn raw_event_callback() -> TraceCallbackType {
let callback = RawTraceEventFunc::new(
Box::new(|args, _data| {
println!("FakeEventCallback (raw) called with args: {:x?}", args);
}),
Box::new(()),
);
TraceCallbackType::RawEvent(Arc::new(callback))
}
fn main() {
env_logger::try_init_from_env(env_logger::Env::default().default_filter_or("debug"))
.expect("Failed to initialize logger");
static_keys::global_init();
let (tracepoint_map, ext_tps) = global_init_events::<Kops>().unwrap();
let ext_tps = ext_tps
.into_iter()
.map(|ext_tp| (ext_tp.id(), ext_tp))
.collect::<BTreeMap<_, _>>();
*EXT_TRACEPOINTS.write().unwrap() = ext_tps;
println!("---Before enabling tracepoints---");
tracepoint_example::test_trace(1, 2);
tracepoint_example::test_trace(3, 4);
print_trace_records(
&tracepoint_map,
&tracepoint_example::TRACE_CMDLINE_CACHE.lock().unwrap(),
);
println!();
let perf_event_callback = perf_event_callback();
let raw_event_callback = raw_event_callback();
for ext_tp in EXT_TRACEPOINTS.write().unwrap().values_mut() {
let tp = ext_tp.trace_point();
let enable_file = TracePointEnableFile::new();
enable_file.write(ext_tp, '1');
ext_tp.register(perf_event_callback.clone());
ext_tp.register(raw_event_callback.clone());
let mut filter_file = TraceFilterFile::new();
filter_file
.write(ext_tp, "(a > 8 && a<=10) || b >5")
.unwrap();
let schema = tp.schema();
println!("Schema for {}::{}: {:#?}", tp.system(), tp.name(), schema);
println!("Enabled tracepoint: {}::{}", tp.system(), tp.name());
}
println!("---After enabling tracepoints---");
tracepoint_example::test_trace(1, 2);
tracepoint_example::test_trace(9, 2); tracepoint_example::test_trace(3, 4);
tracepoint_example::test_trace(10, 4); tracepoint_example::test_trace(11, 6);
print_trace_records(
&tracepoint_map,
&tracepoint_example::TRACE_CMDLINE_CACHE.lock().unwrap(),
);
for tracepoint in tracepoint_map.values() {
println!("{}", tracepoint.print_fmt());
}
}