浏览代码

Merge pull request #108 from deverton/kprobe-debugfs

Support k/uprobes on older kernels.
Alessandro Decina 3 年之前
父节点
当前提交
6db30fad9c
共有 4 个文件被更改,包括 200 次插入17 次删除
  1. 35 2
      aya/src/programs/perf_attach.rs
  2. 154 10
      aya/src/programs/probe.rs
  3. 5 2
      aya/src/programs/trace_point.rs
  4. 6 3
      aya/src/sys/perf_event.rs

+ 35 - 2
aya/src/programs/perf_attach.rs

@@ -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,
+    }))
 }

+ 154 - 10
aya/src/programs/probe.rs

@@ -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)> {

+ 5 - 2
aya/src/programs/trace_point.rs

@@ -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 {

+ 6 - 3
aya/src/sys/perf_event.rs

@@ -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,
     })