소스 검색

Support k/uprobes on older kernels.

Prior to kernel 4.17 probes were attached via debugfs and this patch
attempts to make that work.

Tested on kernel 4.14.
Dan Everton 3 년 전
부모
커밋
34aa790
4개의 변경된 파일212개의 추가작업 그리고 9개의 파일을 삭제
  1. 5 1
      aya/src/programs/kprobe.rs
  2. 201 6
      aya/src/programs/probe.rs
  3. 1 1
      aya/src/programs/trace_point.rs
  4. 5 1
      aya/src/programs/uprobe.rs

+ 5 - 1
aya/src/programs/kprobe.rs

@@ -6,7 +6,7 @@ use crate::{
     generated::bpf_prog_type::BPF_PROG_TYPE_KPROBE,
     programs::{
         load_program,
-        probe::{attach, ProbeKind},
+        probe::{attach, detach, ProbeKind},
         LinkRef, ProgramData, ProgramError,
     },
 };
@@ -73,6 +73,10 @@ impl KProbe {
     pub fn attach(&mut self, fn_name: &str, offset: u64) -> Result<LinkRef, ProgramError> {
         attach(&mut self.data, self.kind, fn_name, offset, None)
     }
+
+    pub fn detach(&mut self, fn_name: &str) -> Result<(), ProgramError> {
+        detach(&mut self.data, self.kind, fn_name)
+    }
 }
 
 /// The type returned when attaching a [`KProbe`] fails.

+ 201 - 6
aya/src/programs/probe.rs

@@ -1,11 +1,16 @@
 use libc::pid_t;
-use std::{fs, io};
+use std::{
+    fs,
+    io::{self, Write},
+    process,
+};
 
 use crate::{
     programs::{
-        kprobe::KProbeError, perf_attach, uprobe::UProbeError, LinkRef, ProgramData, ProgramError,
+        kprobe::KProbeError, perf_attach, 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)]
@@ -23,10 +28,89 @@ pub enum ProbeKind {
 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
+    let k_ver = kernel_version().unwrap();
+    let fd = if k_ver >= (4, 17, 0) {
+        create_as_probe(kind, fn_name, offset, pid)?
+    } else {
+        create_as_trace_point(kind, fn_name, offset, pid)?
+    };
+
+    perf_attach(program_data, fd)
+}
+
+pub(crate) fn detach(
+    program_data: &mut ProgramData,
+    kind: ProbeKind,
+    fn_name: &str,
+) -> Result<(), ProgramError> {
+    use ProbeKind::*;
+
+    /*
+     * Taken from https://github.com/iovisor/bcc/blob/67f59ee80fcf5deedaacba1436d9fa09d32a16a0/src/cc/libbpf.c#L1173
+     *
+     * For [k,u]probe created with perf_event_open (on newer kernel), it is
+     * not necessary to clean it up in [k,u]probe_events. We first look up
+     * the %s_bcc_%d line in [k,u]probe_events. If the event is not found,
+     * it is safe to skip the cleaning up process (write -:... to the file).
+     */
+
+    let event_type = match kind {
+        KProbe | KRetProbe => "kprobe",
+        UProbe | URetProbe => "uprobe",
+    };
+
+    let probe_type_prefix = match kind {
+        KProbe | UProbe => 'p',
+        KRetProbe | URetProbe => 'r',
+    };
+
+    let events_file_name = format!("/sys/kernel/debug/tracing/{}_events", event_type);
+    let event_name = format!(
+        "{}s/{}_{}_aya_{}",
+        probe_type_prefix,
+        event_type,
+        program_data.name,
+        process::id()
+    );
+
+    let found = match kind {
+        KProbe | KRetProbe => {
+            find_in_sys_kernel_debug_tracing_events(&events_file_name, event_name.clone())
+                .map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?
+        }
+        UProbe | URetProbe => {
+            find_in_sys_kernel_debug_tracing_events(&events_file_name, event_name.clone())
+                .map_err(|(filename, io_error)| UProbeError::FileError { filename, io_error })?
+        }
+    };
+
+    if found {
+        match kind {
+            KProbe | KRetProbe => {
+                delete_in_sys_kernel_debug_tracing_events(&events_file_name, &event_name)
+                    .map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?
+            }
+            UProbe | URetProbe => {
+                delete_in_sys_kernel_debug_tracing_events(&events_file_name, &event_name)
+                    .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 {
@@ -48,14 +132,125 @@ pub(crate) fn attach(
         _ => 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, ProgramError> {
+    use ProbeKind::*;
+
+    let (event_type, event_alias) = match kind {
+        KProbe | KRetProbe => (
+            "kprobes",
+            create_probe_event(kind, "kprobe", name, offset)
+                .map_err(|(filename, io_error)| KProbeError::FileError { filename, io_error })?,
+        ),
+        UProbe | URetProbe => (
+            "uprobes",
+            create_probe_event(kind, "uprobe", name, offset)
+                .map_err(|(filename, io_error)| UProbeError::FileError { filename, io_error })?,
+        ),
+    };
+
+    // TODO: pid and cpu handling
+    let tpid = read_sys_fs_trace_point_id(event_type, &event_alias)?;
+    let fd = perf_event_open_trace_point(tpid).map_err(|(_code, io_error)| {
+        ProgramError::SyscallError {
+            call: "perf_event_open".to_owned(),
+            io_error,
+        }
+    })? as i32;
+
+    Ok(fd)
+}
+
+fn create_probe_event(
+    kind: ProbeKind,
+    event_type: &str,
+    fn_name: &str,
+    offset: u64,
+) -> Result<String, (String, io::Error)> {
+    use ProbeKind::*;
+
+    let events_file_name = format!("/sys/kernel/debug/tracing/{}_events", event_type);
+    let probe_type_prefix = match kind {
+        KProbe | UProbe => 'p',
+        KRetProbe | URetProbe => 'r',
+    };
+    let event_alias = format!("{}_{}_aya_{}", probe_type_prefix, fn_name, process::id());
+
+    let mut events_file = fs::OpenOptions::new()
+        .append(true)
+        .open(&events_file_name)
+        .map_err(|e| (events_file_name.clone(), e))?;
+
+    // FIXME: add offset
+    let p = match kind {
+        KProbe => format!(
+            "{}:{}s/{} {}",
+            probe_type_prefix, event_type, event_alias, fn_name
+        ),
+        KRetProbe => format!(
+            "{}:{}s/{} {}",
+            probe_type_prefix, event_type, event_alias, fn_name
+        ),
+        UProbe => format!(
+            "{}:{}s/{} {}",
+            probe_type_prefix, event_type, event_alias, fn_name
+        ),
+        URetProbe => format!(
+            "{}:{}s/{} {}",
+            probe_type_prefix, event_type, event_alias, fn_name
+        ),
+    };
+
+    events_file
+        .write_all(p.as_bytes())
+        .map_err(|e| (events_file_name.clone(), e))?;
+
+    Ok(event_alias)
+}
+
+fn find_in_sys_kernel_debug_tracing_events(
+    events_file_name: &String,
+    event_name: String,
+) -> Result<bool, (String, io::Error)> {
+    use std::io::BufRead;
+
+    let events_file =
+        fs::File::open(events_file_name).map_err(|e| (events_file_name.clone(), e))?;
+
+    Ok(io::BufReader::new(events_file)
+        .lines()
+        .map(|line| line.unwrap())
+        .any(|line| line == event_name))
+}
+
+fn delete_in_sys_kernel_debug_tracing_events(
+    events_file_name: &String,
+    event_name: &String,
+) -> Result<(), (String, io::Error)> {
+    let mut events_file = fs::OpenOptions::new()
+        .append(true)
+        .open(events_file_name)
+        .map_err(|e| (events_file_name.clone(), e))?;
+
+    events_file
+        .write_fmt(format_args!("-:{}", event_name))
+        .map_err(|e| (events_file_name.clone(), e))?;
+
+    Ok(())
 }
 
 fn read_sys_fs_perf_type(pmu: &str) -> Result<u32, (String, io::Error)> {

+ 1 - 1
aya/src/programs/trace_point.rs

@@ -80,7 +80,7 @@ impl TracePoint {
     }
 }
 
-fn read_sys_fs_trace_point_id(category: &str, name: &str) -> Result<u32, TracePointError> {
+pub 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 {

+ 5 - 1
aya/src/programs/uprobe.rs

@@ -17,7 +17,7 @@ use crate::{
     generated::bpf_prog_type::BPF_PROG_TYPE_KPROBE,
     programs::{
         load_program,
-        probe::{attach, ProbeKind},
+        probe::{attach, detach, ProbeKind},
         LinkRef, ProgramData, ProgramError,
     },
 };
@@ -126,6 +126,10 @@ impl UProbe {
 
         attach(&mut self.data, self.kind, &path, sym_offset + offset, pid)
     }
+
+    pub fn detach(&mut self, fn_name: &str) -> Result<(), ProgramError> {
+        detach(&mut self.data, self.kind, fn_name)
+    }
 }
 
 /// The type returned when attaching an [`UProbe`] fails.