Skip to content

Commit 9468272

Browse files
authored
Merge pull request #31 from rex-rs/perf_array_map_impl
- samples/trace_event: use the new TaskStruct::get_comm signature - rex/tracepoint: implement PerfEventStreamer trait - rex: add support for PERF_EVENT_ARRAY - rex-macros: add macro support for uprobe - rex/task_struct: update get_comm method Signed-off-by: Jinghao Jia <[email protected]>
2 parents e7bb614 + 80a2b48 commit 9468272

File tree

9 files changed

+194
-30
lines changed

9 files changed

+194
-30
lines changed

rex-macros/src/kprobe.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
1+
use std::fmt;
2+
13
use proc_macro2::TokenStream;
24
use quote::{format_ident, quote};
35
use syn::{parse2, ItemFn, Result};
46

57
use crate::args::parse_string_args;
68

9+
#[allow(dead_code)]
10+
pub enum KprobeFlavor {
11+
Kprobe,
12+
Kretprobe,
13+
Uprobe,
14+
Uretprobe,
15+
}
16+
17+
impl fmt::Display for KprobeFlavor {
18+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19+
match self {
20+
KprobeFlavor::Kprobe => write!(f, "kprobe"),
21+
KprobeFlavor::Kretprobe => write!(f, "kretprobe"),
22+
KprobeFlavor::Uprobe => write!(f, "uprobe"),
23+
KprobeFlavor::Uretprobe => write!(f, "uretprobe"),
24+
}
25+
}
26+
}
27+
728
pub(crate) struct KProbe {
829
function: Option<String>,
930
item: ItemFn,
@@ -23,17 +44,17 @@ impl KProbe {
2344
Ok(KProbe { function, item })
2445
}
2546

26-
pub(crate) fn expand(&self) -> Result<TokenStream> {
47+
pub(crate) fn expand(&self, flavor: KprobeFlavor) -> Result<TokenStream> {
2748
let fn_name = self.item.sig.ident.clone();
2849
let item = &self.item;
2950
let function_name = format!("{}", fn_name);
3051
let prog_ident =
3152
format_ident!("PROG_{}", fn_name.to_string().to_uppercase());
3253

3354
let attached_function = if self.function.is_some() {
34-
format!("rex/kprobe/{}", self.function.as_ref().unwrap())
55+
format!("rex/{}/{}", flavor, self.function.as_ref().unwrap())
3556
} else {
36-
"rex/kprobe".to_string()
57+
format!("rex/{}", flavor)
3758
};
3859

3960
let function_body_tokens = quote! {

rex-macros/src/lib.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ mod xdp;
88

99
use std::borrow::Cow;
1010

11-
use kprobe::KProbe;
11+
use kprobe::{KProbe, KprobeFlavor};
1212
use perf_event::PerfEvent;
1313
use proc_macro::TokenStream;
1414
use proc_macro_error::{abort, proc_macro_error};
@@ -47,7 +47,19 @@ pub fn rex_tc(attrs: TokenStream, item: TokenStream) -> TokenStream {
4747
pub fn rex_kprobe(attrs: TokenStream, item: TokenStream) -> TokenStream {
4848
match KProbe::parse(attrs.into(), item.into()) {
4949
Ok(prog) => prog
50-
.expand()
50+
.expand(KprobeFlavor::Kprobe)
51+
.unwrap_or_else(|err| abort!(err.span(), "{}", err))
52+
.into(),
53+
Err(err) => abort!(err.span(), "{}", err),
54+
}
55+
}
56+
57+
#[proc_macro_error]
58+
#[proc_macro_attribute]
59+
pub fn rex_uprobe(attrs: TokenStream, item: TokenStream) -> TokenStream {
60+
match KProbe::parse(attrs.into(), item.into()) {
61+
Ok(prog) => prog
62+
.expand(KprobeFlavor::Uprobe)
5163
.unwrap_or_else(|err| abort!(err.span(), "{}", err))
5264
.into(),
5365
Err(err) => abort!(err.span(), "{}", err),

rex/librexstub/lib.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ KSYM_FUNC(bpf_spin_unlock)
2222
KSYM_FUNC(just_return_func)
2323
KSYM_FUNC(bpf_get_stackid_pe)
2424
KSYM_FUNC(bpf_perf_prog_read_value)
25+
KSYM_FUNC(bpf_perf_event_output_tp)
26+
KSYM_FUNC(bpf_perf_event_read_value)
27+
KSYM_FUNC(bpf_skb_event_output)
28+
KSYM_FUNC(bpf_xdp_event_output)
2529
KSYM_FUNC(bpf_xdp_adjust_head)
2630
KSYM_FUNC(bpf_xdp_adjust_tail)
2731
KSYM_FUNC(bpf_clone_redirect)

rex/src/ffi.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,53 @@ unsafe extern "C" {
132132
size: u32,
133133
) -> i64;
134134

135+
/// `long bpf_perf_event_output_tp(void *tp_buff, struct bpf_map *map, u64
136+
/// flags, void *data, u64 size)`
137+
pub(crate) fn bpf_perf_event_output_tp(
138+
tp_buff: *const (),
139+
map: *mut (),
140+
flags: u64,
141+
data: *const (),
142+
size: u64,
143+
) -> i64;
144+
145+
/// `long bpf_perf_event_read_value(struct bpf_map *map, u64 flags,
146+
/// struct bpf_perf_event_value *buf, u32 buf_size)`
147+
/// same reason for use of improper_ctypes as bpf_perf_prog_read_value
148+
#[allow(improper_ctypes)]
149+
pub(crate) fn bpf_perf_event_read_value(
150+
map: *mut (),
151+
flags: u64,
152+
buf: &mut bpf_perf_event_value,
153+
buf_size: u32,
154+
) -> i64;
155+
156+
/// `long bpf_skb_event_output(struct sk_buff *skb, struct bpf_map *map, u64 flags,
157+
/// void *meta, u64 meta_size)`
158+
/// The compiler complains about some non-FFI safe type, but since the
159+
/// kernel is using it fine it should be safe for an FFI call using C ABI
160+
#[allow(improper_ctypes)]
161+
pub(crate) fn bpf_skb_event_output(
162+
skb: *const sk_buff,
163+
map: *mut (),
164+
flags: u64,
165+
meta: *const (),
166+
meta_size: u64,
167+
) -> i64;
168+
169+
/// `long bpf_xdp_event_output(struct xdp_buff *xdp, struct bpf_map *map, u64 flags,
170+
/// void *meta, u64 meta_size)`
171+
/// The compiler complains about some non-FFI safe type, but since the
172+
/// kernel is using it fine it should be safe for an FFI call using C ABI
173+
#[allow(improper_ctypes)]
174+
pub(crate) fn bpf_xdp_event_output(
175+
xdp: *const xdp_buff,
176+
map: *mut (),
177+
flags: u64,
178+
meta: *const (),
179+
meta_size: u64,
180+
) -> i64;
181+
135182
/// `long bpf_xdp_adjust_head(struct xdp_buff *xdp, int offset)`
136183
///
137184
/// The compiler complains about some non-FFI safe type, but since the

rex/src/map.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ use crate::base_helper::{
1111
use crate::ffi;
1212
use crate::linux::bpf::{
1313
bpf_map_type, BPF_ANY, BPF_EXIST, BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_HASH,
14-
BPF_MAP_TYPE_PERCPU_ARRAY, BPF_MAP_TYPE_QUEUE, BPF_MAP_TYPE_RINGBUF,
15-
BPF_MAP_TYPE_STACK, BPF_MAP_TYPE_STACK_TRACE, BPF_NOEXIST,
16-
BPF_RB_AVAIL_DATA, BPF_RB_CONS_POS, BPF_RB_PROD_POS, BPF_RB_RING_SIZE,
14+
BPF_MAP_TYPE_PERCPU_ARRAY, BPF_MAP_TYPE_PERF_EVENT_ARRAY,
15+
BPF_MAP_TYPE_QUEUE, BPF_MAP_TYPE_RINGBUF, BPF_MAP_TYPE_STACK,
16+
BPF_MAP_TYPE_STACK_TRACE, BPF_NOEXIST, BPF_RB_AVAIL_DATA, BPF_RB_CONS_POS,
17+
BPF_RB_PROD_POS, BPF_RB_RING_SIZE,
1718
};
1819
use crate::linux::errno::EINVAL;
19-
use crate::utils::{to_result, NoRef, Result};
20+
use crate::utils::{
21+
to_result, NoRef, PerfEventMaskedCPU, PerfEventStreamer, Result,
22+
};
2023

2124
/// Rex equivalent to be used for map APIs in place of the `struct bpf_map`.
2225
/// The key and the value type are encoded as generics types `K` and `V`.
@@ -66,6 +69,8 @@ unsafe impl<const MT: bpf_map_type, K, V> Sync for RexMapHandle<MT, K, V> where
6669

6770
pub type RexStackTrace<K, V> = RexMapHandle<BPF_MAP_TYPE_STACK_TRACE, K, V>;
6871
pub type RexPerCPUArrayMap<V> = RexMapHandle<BPF_MAP_TYPE_PERCPU_ARRAY, u32, V>;
72+
pub type RexPerfEventArray<V> =
73+
RexMapHandle<BPF_MAP_TYPE_PERF_EVENT_ARRAY, u32, V>;
6974
pub type RexArrayMap<V> = RexMapHandle<BPF_MAP_TYPE_ARRAY, u32, V>;
7075
pub type RexHashMap<K, V> = RexMapHandle<BPF_MAP_TYPE_HASH, K, V>;
7176
pub type RexStack<V> = RexMapHandle<BPF_MAP_TYPE_STACK, (), V>;
@@ -114,6 +119,21 @@ where
114119
}
115120
}
116121

122+
impl<V> RexPerfEventArray<V>
123+
where
124+
V: Copy + NoRef,
125+
{
126+
pub fn output<P: PerfEventStreamer>(
127+
&'static self,
128+
program: &P,
129+
ctx: &P::Context,
130+
data: &V,
131+
cpu: PerfEventMaskedCPU,
132+
) -> Result {
133+
program.output_event(ctx, self, data, cpu)
134+
}
135+
}
136+
117137
impl<V> RexStack<V>
118138
where
119139
V: Copy + NoRef,

rex/src/task_struct.rs

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::bindings::linux::kernel::task_struct;
2-
use crate::bindings::uapi::linux::errno::EINVAL;
32
use crate::per_cpu::{current_task, this_cpu_read};
43
use crate::pt_regs::PtRegs;
4+
use core::ffi::{self as core_ffi, CStr};
55

66
// Bindgen has problem generating these constants
77
const TOP_OF_KERNEL_STACK_PADDING: u64 = 0;
@@ -43,27 +43,19 @@ impl TaskStruct {
4343
self.kptr.tgid
4444
}
4545

46-
// Design decision: the original BPF interface does not have type safety,
47-
// since buf is just a buffer. But in Rust we can use const generics to
48-
// restrict it to only [u8; N] given that comm is a cstring. This also
49-
// automatically achieves size check, since N is a constexpr.
50-
pub fn get_comm<const N: usize>(&self, buf: &mut [i8; N]) -> i32 {
51-
if N == 0 {
52-
return -(EINVAL as i32);
53-
}
54-
55-
let size = core::cmp::min::<usize>(N, self.kptr.comm.len()) - 1;
56-
if size == 0 {
57-
return -(EINVAL as i32);
58-
}
59-
60-
buf[..size].copy_from_slice(&self.kptr.comm[..size]);
61-
buf[size] = 0;
62-
0
46+
// Design decision: the equivalent BPF helper writes the program name to
47+
// a user-provided buffer, here we can take advantage of Rust's ownership by
48+
// just providing a &CStr instead
49+
pub fn get_comm(&self) -> Result<&CStr, core_ffi::FromBytesUntilNulError> {
50+
// casting from c_char to u8 is sound, see:
51+
// https://doc.rust-lang.org/src/core/ffi/c_str.rs.html#264
52+
let comm_bytes =
53+
unsafe { &*(&self.kptr.comm[..] as *const _ as *const [u8]) };
54+
CStr::from_bytes_until_nul(comm_bytes)
6355
}
6456

6557
pub fn get_pt_regs(&self) -> &'static PtRegs {
66-
// X86 sepcific
58+
// X86 specific
6759
// stack_top is actually bottom of the kernel stack, it refers to the
6860
// highest address of the stack pages
6961
let stack_top =

rex/src/tracepoint/tp_impl.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
use crate::base_helper::termination_check;
12
use crate::bindings::uapi::linux::bpf::{
23
bpf_map_type, BPF_PROG_TYPE_TRACEPOINT,
34
};
5+
use crate::ffi;
6+
use crate::map::RexPerfEventArray;
47
use crate::prog_type::rex_prog;
58
use crate::task_struct::TaskStruct;
9+
use crate::utils::{to_result, NoRef, PerfEventMaskedCPU, PerfEventStreamer};
610
use crate::Result;
711

812
use super::{
@@ -65,3 +69,26 @@ impl<C: TracepointContext + 'static> rex_prog for tracepoint<C> {
6569
((self.prog)(self, newctx)).unwrap_or_else(|e| e) as u32
6670
}
6771
}
72+
73+
impl<C: TracepointContext + 'static> PerfEventStreamer for tracepoint<C> {
74+
type Context = C;
75+
fn output_event<T: Copy + NoRef>(
76+
&self,
77+
ctx: &Self::Context,
78+
map: &'static RexPerfEventArray<T>,
79+
data: &T,
80+
cpu: PerfEventMaskedCPU,
81+
) -> Result {
82+
let map_kptr = unsafe { core::ptr::read_volatile(&map.kptr) };
83+
let ctx_ptr = ctx as *const C as *const ();
84+
termination_check!(unsafe {
85+
to_result!(ffi::bpf_perf_event_output_tp(
86+
ctx_ptr,
87+
map_kptr,
88+
cpu.masked_cpu,
89+
data as *const T as *const (),
90+
core::mem::size_of::<T>() as u64
91+
))
92+
})
93+
}
94+
}

rex/src/utils.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::bindings::uapi::linux::bpf::{BPF_F_CURRENT_CPU, BPF_F_INDEX_MASK};
2+
use crate::map::RexPerfEventArray;
13
use core::ffi::{c_int, c_uchar};
24
use core::mem;
35
use core::ops::{Deref, DerefMut, Drop};
@@ -286,3 +288,39 @@ macro_rules! read_field {
286288
)
287289
}};
288290
}
291+
292+
// For implementers, see tp_impl.rs for how to implement
293+
// this trait
294+
/// Programs that can stream data through a
295+
/// RexPerfEventArray will implement this trait
296+
pub trait PerfEventStreamer {
297+
type Context;
298+
fn output_event<T: Copy + NoRef>(
299+
&self,
300+
ctx: &Self::Context,
301+
map: &'static RexPerfEventArray<T>,
302+
data: &T,
303+
cpu: PerfEventMaskedCPU,
304+
) -> Result;
305+
}
306+
307+
/// Newtype for a cpu for perf event output to ensure
308+
/// type safety since the cpu must be masked with
309+
/// BPF_F_INDEX_MASK
310+
#[derive(Debug, Copy, Clone)]
311+
pub struct PerfEventMaskedCPU {
312+
pub(crate) masked_cpu: u64,
313+
}
314+
315+
impl PerfEventMaskedCPU {
316+
pub fn current_cpu() -> Self {
317+
PerfEventMaskedCPU {
318+
masked_cpu: BPF_F_CURRENT_CPU,
319+
}
320+
}
321+
pub fn any_cpu(cpu: u64) -> Self {
322+
PerfEventMaskedCPU {
323+
masked_cpu: cpu & BPF_F_INDEX_MASK,
324+
}
325+
}
326+
}

samples/trace_event/src/main.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub const TASK_COMM_LEN: usize = 16;
1515
#[repr(C)]
1616
#[derive(Copy, Clone)]
1717
pub struct KeyT {
18-
pub comm: [i8; TASK_COMM_LEN],
18+
pub comm: [u8; TASK_COMM_LEN],
1919
pub kernstack: u32,
2020
pub userstack: u32,
2121
}
@@ -50,7 +50,10 @@ fn rex_prog1(obj: &perf_event, ctx: &bpf_perf_event_data) -> Result {
5050

5151
obj.bpf_get_current_task()
5252
.map(|t| {
53-
t.get_comm(&mut key.comm);
53+
let prog_name = t.get_comm().unwrap_or_default();
54+
key.comm
55+
.copy_from_slice(&prog_name.to_bytes()[..TASK_COMM_LEN]);
56+
key.comm[TASK_COMM_LEN - 1] = 0;
5457
0u64
5558
})
5659
.ok_or(0i32)?;

0 commit comments

Comments
 (0)