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.
pull/108/head
Dan Everton 3 years ago
parent 5b0e518641
commit 34aa790a91
No known key found for this signature in database
GPG Key ID: 2AF33A2B189A11B3

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

@ -1,11 +1,16 @@
use libc::pid_t; use libc::pid_t;
use std::{fs, io}; use std::{
fs,
io::{self, Write},
process,
};
use crate::{ use crate::{
programs::{ 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)] #[derive(Debug, Copy, Clone)]
@ -23,10 +28,89 @@ pub enum ProbeKind {
pub(crate) fn attach( pub(crate) fn attach(
program_data: &mut ProgramData, program_data: &mut ProgramData,
kind: ProbeKind, kind: ProbeKind,
name: &str, fn_name: &str,
offset: u64, offset: u64,
pid: Option<pid_t>, pid: Option<pid_t>,
) -> Result<LinkRef, ProgramError> { ) -> 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::*; use ProbeKind::*;
let perf_ty = match kind { let perf_ty = match kind {
@ -48,14 +132,125 @@ pub(crate) fn attach(
_ => None, _ => 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 { |(_code, io_error)| ProgramError::SyscallError {
call: "perf_event_open".to_owned(), call: "perf_event_open".to_owned(),
io_error, io_error,
}, },
)? as i32; )? 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)> { fn read_sys_fs_perf_type(pmu: &str) -> Result<u32, (String, io::Error)> {

@ -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 file = format!("/sys/kernel/debug/tracing/events/{}/{}/id", category, name);
let id = fs::read_to_string(&file).map_err(|io_error| TracePointError::FileError { let id = fs::read_to_string(&file).map_err(|io_error| TracePointError::FileError {

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

Loading…
Cancel
Save