Merge pull request #108 from deverton/kprobe-debugfs

Support k/uprobes on older kernels.
pull/122/head
Alessandro Decina 3 years ago committed by GitHub
commit 6db30fad9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,7 +2,9 @@ use libc::close;
use std::os::unix::io::RawFd;
use crate::{
sys::perf_event_ioctl, PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE, PERF_EVENT_IOC_SET_BPF,
programs::{probe::detach_debug_fs, ProbeKind},
sys::perf_event_ioctl,
PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE, PERF_EVENT_IOC_SET_BPF,
};
use super::{Link, LinkRef, ProgramData, ProgramError};
@ -10,6 +12,8 @@ use super::{Link, LinkRef, ProgramData, ProgramError};
#[derive(Debug)]
struct PerfLink {
perf_fd: Option<RawFd>,
probe_kind: Option<ProbeKind>,
event_alias: Option<String>,
}
impl Link for PerfLink {
@ -17,6 +21,13 @@ impl Link for PerfLink {
if let Some(fd) = self.perf_fd.take() {
let _ = perf_event_ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
unsafe { close(fd) };
if let Some(probe_kind) = self.probe_kind.take() {
if let Some(event_alias) = self.event_alias.take() {
let _ = detach_debug_fs(probe_kind, &event_alias);
}
}
Ok(())
} else {
Err(ProgramError::AlreadyDetached)
@ -31,6 +42,24 @@ impl Drop for PerfLink {
}
pub(crate) fn perf_attach(data: &mut ProgramData, fd: RawFd) -> Result<LinkRef, ProgramError> {
perf_attach_either(data, fd, None, None)
}
pub(crate) fn perf_attach_debugfs(
data: &mut ProgramData,
fd: RawFd,
probe_kind: ProbeKind,
event_alias: String,
) -> Result<LinkRef, ProgramError> {
perf_attach_either(data, fd, Some(probe_kind), Some(event_alias))
}
fn perf_attach_either(
data: &mut ProgramData,
fd: RawFd,
probe_kind: Option<ProbeKind>,
event_alias: Option<String>,
) -> Result<LinkRef, ProgramError> {
let prog_fd = data.fd_or_err()?;
perf_event_ioctl(fd, PERF_EVENT_IOC_SET_BPF, prog_fd).map_err(|(_, io_error)| {
ProgramError::SyscallError {
@ -45,5 +74,9 @@ pub(crate) fn perf_attach(data: &mut ProgramData, fd: RawFd) -> Result<LinkRef,
}
})?;
Ok(data.link(PerfLink { perf_fd: Some(fd) }))
Ok(data.link(PerfLink {
perf_fd: Some(fd),
probe_kind,
event_alias,
}))
}

@ -1,11 +1,17 @@
use libc::pid_t;
use std::{fs, io};
use std::{
fs::{self, OpenOptions},
io::{self, Write},
process,
};
use crate::{
programs::{
kprobe::KProbeError, perf_attach, uprobe::UProbeError, LinkRef, ProgramData, ProgramError,
kprobe::KProbeError, perf_attach, perf_attach_debugfs,
trace_point::read_sys_fs_trace_point_id, uprobe::UProbeError, LinkRef, ProgramData,
ProgramError,
},
sys::perf_event_open_probe,
sys::{kernel_version, perf_event_open_probe, perf_event_open_trace_point},
};
#[derive(Debug, Copy, Clone)]
@ -20,42 +26,180 @@ pub enum ProbeKind {
URetProbe,
}
impl ProbeKind {
fn pmu(&self) -> &'static str {
match *self {
ProbeKind::KProbe | ProbeKind::KRetProbe => "kprobe",
ProbeKind::UProbe | ProbeKind::URetProbe => "uprobe",
}
}
}
pub(crate) fn attach(
program_data: &mut ProgramData,
kind: ProbeKind,
name: &str,
fn_name: &str,
offset: u64,
pid: Option<pid_t>,
) -> Result<LinkRef, ProgramError> {
// https://github.com/torvalds/linux/commit/e12f03d7031a977356e3d7b75a68c2185ff8d155
// Use debugfs to create probe
let k_ver = kernel_version().unwrap();
if k_ver < (4, 17, 0) {
let (fd, event_alias) = create_as_trace_point(kind, fn_name, offset, pid)?;
return perf_attach_debugfs(program_data, fd, kind, event_alias);
};
let fd = create_as_probe(kind, fn_name, offset, pid)?;
perf_attach(program_data, fd)
}
pub(crate) fn detach_debug_fs(kind: ProbeKind, event_alias: &str) -> Result<(), ProgramError> {
use ProbeKind::*;
let _ = match kind {
KProbe | KRetProbe => delete_probe_event(kind, event_alias)
.map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?,
UProbe | URetProbe => delete_probe_event(kind, event_alias)
.map_err(|(filename, io_error)| UProbeError::FileError { filename, io_error })?,
};
Ok(())
}
fn create_as_probe(
kind: ProbeKind,
fn_name: &str,
offset: u64,
pid: Option<pid_t>,
) -> Result<i32, ProgramError> {
use ProbeKind::*;
let perf_ty = match kind {
KProbe | KRetProbe => read_sys_fs_perf_type("kprobe")
KProbe | KRetProbe => read_sys_fs_perf_type(kind.pmu())
.map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?,
UProbe | URetProbe => read_sys_fs_perf_type("uprobe")
UProbe | URetProbe => read_sys_fs_perf_type(kind.pmu())
.map_err(|(filename, io_error)| UProbeError::FileError { filename, io_error })?,
};
let ret_bit = match kind {
KRetProbe => Some(
read_sys_fs_perf_ret_probe("kprobe")
read_sys_fs_perf_ret_probe(kind.pmu())
.map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?,
),
URetProbe => Some(
read_sys_fs_perf_ret_probe("uprobe")
read_sys_fs_perf_ret_probe(kind.pmu())
.map_err(|(filename, io_error)| UProbeError::FileError { filename, io_error })?,
),
_ => None,
};
let fd = perf_event_open_probe(perf_ty, ret_bit, name, offset, pid).map_err(
let fd = perf_event_open_probe(perf_ty, ret_bit, fn_name, offset, pid).map_err(
|(_code, io_error)| ProgramError::SyscallError {
call: "perf_event_open".to_owned(),
io_error,
},
)? as i32;
perf_attach(program_data, fd)
Ok(fd)
}
fn create_as_trace_point(
kind: ProbeKind,
name: &str,
offset: u64,
pid: Option<pid_t>,
) -> Result<(i32, String), ProgramError> {
use ProbeKind::*;
let event_alias = match kind {
KProbe | KRetProbe => create_probe_event(kind, name, offset)
.map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?,
UProbe | URetProbe => create_probe_event(kind, name, offset)
.map_err(|(filename, io_error)| UProbeError::FileError { filename, io_error })?,
};
let category = format!("{}s", kind.pmu());
let tpid = read_sys_fs_trace_point_id(&category, &event_alias)?;
let fd = perf_event_open_trace_point(tpid, pid).map_err(|(_code, io_error)| {
ProgramError::SyscallError {
call: "perf_event_open".to_owned(),
io_error,
}
})? as i32;
Ok((fd, event_alias))
}
fn create_probe_event(
kind: ProbeKind,
fn_name: &str,
offset: u64,
) -> Result<String, (String, io::Error)> {
use ProbeKind::*;
let events_file_name = format!("/sys/kernel/debug/tracing/{}_events", kind.pmu());
let probe_type_prefix = match kind {
KProbe | UProbe => 'p',
KRetProbe | URetProbe => 'r',
};
let event_alias = format!(
"aya_{}_{}_{}_{:#x}",
process::id(),
probe_type_prefix,
fn_name,
offset
);
let offset_suffix = match kind {
KProbe => format!("+{}", offset),
UProbe => format!(":{:#x}", offset),
_ => "".to_string(),
};
let probe = format!(
"{}:{}s/{} {}{}\n",
probe_type_prefix,
kind.pmu(),
event_alias,
fn_name,
offset_suffix
);
let mut events_file = OpenOptions::new()
.append(true)
.open(&events_file_name)
.map_err(|e| (events_file_name.clone(), e))?;
events_file
.write_all(probe.as_bytes())
.map_err(|e| (events_file_name.clone(), e))?;
Ok(event_alias)
}
fn delete_probe_event(kind: ProbeKind, event_alias: &str) -> Result<(), (String, io::Error)> {
let events_file_name = format!("/sys/kernel/debug/tracing/{}_events", kind.pmu());
let events =
fs::read_to_string(&events_file_name).map_err(|e| (events_file_name.clone(), e))?;
let found = events.lines().any(|line| line.contains(event_alias));
if found {
let mut events_file = OpenOptions::new()
.append(true)
.open(&events_file_name)
.map_err(|e| (events_file_name.to_string(), e))?;
let rm = format!("-:{}\n", event_alias);
events_file
.write_all(rm.as_bytes())
.map_err(|e| (events_file_name.to_string(), e))?;
}
Ok(())
}
fn read_sys_fs_perf_type(pmu: &str) -> Result<u32, (String, io::Error)> {

@ -69,7 +69,7 @@ impl TracePoint {
/// `/sys/kernel/debug/tracing/events`.
pub fn attach(&mut self, category: &str, name: &str) -> Result<LinkRef, ProgramError> {
let id = read_sys_fs_trace_point_id(category, name)?;
let fd = perf_event_open_trace_point(id).map_err(|(_code, io_error)| {
let fd = perf_event_open_trace_point(id, None).map_err(|(_code, io_error)| {
ProgramError::SyscallError {
call: "perf_event_open".to_owned(),
io_error,
@ -80,7 +80,10 @@ impl TracePoint {
}
}
fn read_sys_fs_trace_point_id(category: &str, name: &str) -> Result<u32, TracePointError> {
pub(crate) fn read_sys_fs_trace_point_id(
category: &str,
name: &str,
) -> Result<u32, TracePointError> {
let file = format!("/sys/kernel/debug/tracing/events/{}/{}/id", category, name);
let id = fs::read_to_string(&file).map_err(|io_error| TracePointError::FileError {

@ -93,17 +93,20 @@ pub(crate) fn perf_event_open_probe(
})
}
pub(crate) fn perf_event_open_trace_point(id: u32) -> SysResult {
pub(crate) fn perf_event_open_trace_point(id: u32, pid: Option<pid_t>) -> SysResult {
let mut attr = unsafe { mem::zeroed::<perf_event_attr>() };
attr.size = mem::size_of::<perf_event_attr>() as u32;
attr.type_ = PERF_TYPE_TRACEPOINT as u32;
attr.config = id as u64;
let cpu = if pid.is_some() { -1 } else { 0 };
let pid = pid.unwrap_or(-1);
syscall(Syscall::PerfEventOpen {
attr,
pid: -1,
cpu: 0,
pid,
cpu,
group: -1,
flags: PERF_FLAG_FD_CLOEXEC,
})

Loading…
Cancel
Save