mirror of https://github.com/rust-bpf/rust-bcc
135 lines
3.4 KiB
Rust
135 lines
3.4 KiB
Rust
use bcc::ring_buf::{RingBufBuilder, RingCallback};
|
|
use bcc::BccError;
|
|
use bcc::{Tracepoint, BPF};
|
|
use clap::{App, Arg};
|
|
|
|
use core::sync::atomic::{AtomicBool, Ordering};
|
|
use std::os::raw::c_int;
|
|
use std::sync::Arc;
|
|
use std::time::Instant;
|
|
|
|
// BPF ring buffer submit example
|
|
//
|
|
// Based on: https://github.com/iovisor/bcc/blob/master/examples/ringbuf/ringbuf_submit.py
|
|
|
|
#[repr(C)]
|
|
struct event_t {
|
|
filename: [u8; 64],
|
|
dfd: c_int,
|
|
flags: c_int,
|
|
mode: c_int,
|
|
}
|
|
|
|
fn do_main(runnable: Arc<AtomicBool>) -> Result<(), BccError> {
|
|
let matches = App::new("ringbuf submit")
|
|
.about("Ring buffer submit example")
|
|
.arg(
|
|
Arg::with_name("duration")
|
|
.long("duration")
|
|
.value_name("Seconds")
|
|
.help("The total duration to run")
|
|
.takes_value(true),
|
|
)
|
|
.get_matches();
|
|
|
|
let duration: Option<std::time::Duration> = matches
|
|
.value_of("duration")
|
|
.map(|v| std::time::Duration::new(v.parse().expect("Invalid argument for duration"), 0));
|
|
|
|
let code = "
|
|
BPF_RINGBUF_OUTPUT(buffer, 1 << 4);
|
|
struct event {
|
|
char filename[64];
|
|
int dfd;
|
|
int flags;
|
|
int mode;
|
|
};
|
|
|
|
int openat_entry(struct tracepoint__syscalls__sys_enter_openat *args) {
|
|
int zero = 0;
|
|
struct event *event = buffer.ringbuf_reserve(sizeof(struct event));
|
|
if (!event) {
|
|
return 1;
|
|
}
|
|
bpf_probe_read_user_str(event->filename, sizeof(event->filename), args->filename);
|
|
event->dfd = args->dfd;
|
|
event->flags = args->flags;
|
|
event->mode = args->mode;
|
|
buffer.ringbuf_submit(event, 0);
|
|
// or, to discard: buffer.ringbuf_discard(event, 0);
|
|
return 0;
|
|
}
|
|
";
|
|
|
|
// compile the above BPF code!
|
|
let mut module = BPF::new(code)?;
|
|
|
|
// tracepoints!
|
|
Tracepoint::new()
|
|
.handler("openat_entry")
|
|
.subsystem("syscalls")
|
|
.tracepoint("sys_enter_openat")
|
|
.attach(&mut module)?;
|
|
|
|
let cb = RingCallback::new(Box::new(ring_buf_callback));
|
|
|
|
let table = module.table("buffer")?;
|
|
let mut ring_buf = RingBufBuilder::new(table, cb).build()?;
|
|
|
|
println!(
|
|
"{:-64} {:10} {:10} {:10}",
|
|
"FILENAME", "DIR_FD", "FLAGS", "MODE"
|
|
);
|
|
let start = Instant::now();
|
|
while runnable.load(Ordering::SeqCst) {
|
|
ring_buf.consume();
|
|
if let Some(d) = duration {
|
|
if Instant::now() - start >= d {
|
|
break;
|
|
}
|
|
}
|
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn parse_struct(x: &[u8]) -> event_t {
|
|
unsafe { std::ptr::read_unaligned(x.as_ptr() as *const event_t) }
|
|
}
|
|
|
|
fn get_string(x: &[u8]) -> String {
|
|
match x.iter().position(|&r| r == 0) {
|
|
Some(zero_pos) => String::from_utf8_lossy(&x[0..zero_pos]).to_string(),
|
|
None => String::from_utf8_lossy(x).to_string(),
|
|
}
|
|
}
|
|
|
|
fn ring_buf_callback(data: &[u8]) {
|
|
let event = parse_struct(data);
|
|
|
|
println!(
|
|
"{:-64} {:10} {:10} {:10}",
|
|
get_string(&event.filename),
|
|
event.dfd,
|
|
event.flags,
|
|
event.mode
|
|
);
|
|
}
|
|
|
|
fn main() {
|
|
let runnable = Arc::new(AtomicBool::new(true));
|
|
let r = runnable.clone();
|
|
ctrlc::set_handler(move || {
|
|
r.store(false, Ordering::SeqCst);
|
|
})
|
|
.expect("Failed to set handler for SIGINT / SIGTERM");
|
|
|
|
match do_main(runnable) {
|
|
Err(x) => {
|
|
eprintln!("Error: {}", x);
|
|
std::process::exit(1);
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|