Skip to content

Commit

Permalink
Add ringbuf as an opt-in (#12)
Browse files Browse the repository at this point in the history
Perf buffer is still the default. In the future, we could add automatic
feature detection for ring buffers and other features. In the meantime,
this commits gates it behind the `--ringbuf` flag

Tested with:
```
$ cargo b && sudo target/debug/rbperf record -p `pidof ruby` --ringbuf syscall enter_writev
```

and

```
$ cargo b && sudo target/debug/rbperf record -p `pidof ruby` --ringbuf syscall enter_writev
```
  • Loading branch information
javierhonduco committed Jul 16, 2022
1 parent 5bbffdb commit 8a1e048
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 13 deletions.
14 changes: 10 additions & 4 deletions src/bpf/rbperf.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
#include <bpf/bpf_tracing.h>

struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(u32));
// This map's type is a placeholder, it's dynamically set
// in rbperf.rs to either perf/ring buffer depending on
// the configuration.
__uint(type, BPF_MAP_TYPE_RINGBUF);
} events SEC(".maps");

struct {
Expand Down Expand Up @@ -66,6 +67,7 @@ struct {
} global_state SEC(".maps");

const volatile bool verbose = false;
const volatile bool use_ringbug = false;

#define LOG(fmt, ...) \
({ \
Expand Down Expand Up @@ -298,7 +300,11 @@ int read_ruby_stack(struct bpf_perf_event_data *ctx) {
LOG("[error] stack size %d, expected %d", state->stack.size, state->stack.expected_size);
}

bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &state->stack, sizeof(RubyStack));
if (use_ringbug) {
bpf_ringbuf_output(&events, &state->stack, sizeof(RubyStack), 0);
} else {
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &state->stack, sizeof(RubyStack));
}
return 0;
}

Expand Down
3 changes: 3 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ struct RecordSubcommand {
record_type: RecordType,
#[clap(long)]
verbose_bpf_logging: bool,
#[clap(long)]
ringbuf: bool,
}

#[derive(clap::Subcommand, Debug)]
Expand All @@ -56,6 +58,7 @@ fn main() -> Result<()> {
let options = RbperfOptions {
event,
verbose_bpf_logging: record.verbose_bpf_logging,
use_ringbuf: record.ringbuf,
};

let mut r = Rbperf::new(options);
Expand Down
63 changes: 54 additions & 9 deletions src/rbperf.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use libbpf_rs::{num_possible_cpus, MapFlags, PerfBufferBuilder, ProgramType};
use libbpf_rs::{num_possible_cpus, MapFlags, MapType, PerfBufferBuilder, ProgramType};
use serde_yaml;
use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
Expand Down Expand Up @@ -33,11 +33,13 @@ pub struct Rbperf<'a> {
receiver: Arc<Mutex<std::sync::mpsc::Receiver<RubyStack>>>,
ruby_versions: Vec<RubyVersion>,
event: RbperfEvent,
use_ringbuf: bool,
}

pub struct RbperfOptions {
pub event: RbperfEvent,
pub verbose_bpf_logging: bool,
pub use_ringbuf: bool,
}

fn handle_event(
Expand Down Expand Up @@ -110,6 +112,9 @@ impl<'a> Rbperf<'a> {
debug!("verbose_bpf_logging set to {}", options.verbose_bpf_logging);
open_skel.rodata().verbose = options.verbose_bpf_logging;

debug!("use_ringbug set to {}", options.use_ringbuf);
open_skel.rodata().use_ringbug = options.use_ringbuf;

match options.event {
RbperfEvent::Cpu { sample_period: _ } => {
for prog in open_skel.obj.progs_iter_mut() {
Expand All @@ -122,6 +127,22 @@ impl<'a> Rbperf<'a> {
}
}
}

let mut maps = open_skel.maps_mut();
let events = maps.events();

if options.use_ringbuf {
events.set_type(MapType::RingBuf).unwrap();
events.set_key_size(0).unwrap();
events.set_value_size(0).unwrap();
events.set_max_entries(512 * 1024).unwrap(); // 512KB
} else {
events.set_type(MapType::PerfEventArray).unwrap();
events.set_key_size(4).unwrap();
events.set_value_size(4).unwrap();
events.set_max_entries(0).unwrap();
}

let mut bpf = open_skel.load().unwrap();
for prog in bpf.obj.progs_iter() {
debug!(
Expand All @@ -144,6 +165,7 @@ impl<'a> Rbperf<'a> {
receiver: Arc::new(Mutex::new(receiver)),
ruby_versions,
event: options.event,
use_ringbuf: options.use_ringbuf,
}
}

Expand Down Expand Up @@ -207,13 +229,6 @@ impl<'a> Rbperf<'a> {
self.duration = duration;
// Set up the perf buffer and perf events
let mut sender = self.sender.clone();
let perf = PerfBufferBuilder::new(self.bpf.maps().events())
.sample_cb(|cpu: i32, data: &[u8]| {
handle_event(&mut sender, cpu, data);
})
.lost_cb(handle_lost_events)
.build()?;

let mut fds = Vec::new();

match self.event {
Expand Down Expand Up @@ -254,10 +269,39 @@ impl<'a> Rbperf<'a> {
.update(&idx.to_le_bytes(), &val.to_le_bytes(), MapFlags::ANY)
.unwrap();

let maps = self.bpf.maps();
let events = maps.events();

let mut perfbuf = None;
let mut ringbuf = None;

if self.use_ringbuf {
let mut builder = libbpf_rs::RingBufferBuilder::new();
builder.add(events, |data: &[u8]| -> i32 {
handle_event(&mut sender, 0, data);
0
})?;
ringbuf = Some(builder.build()?);
} else {
let perf_buffer = PerfBufferBuilder::new(self.bpf.maps().events())
.sample_cb(|cpu: i32, data: &[u8]| {
handle_event(&mut sender, cpu, data);
})
.lost_cb(handle_lost_events)
.build()?;
perfbuf = Some(perf_buffer);
}

// Start polling
self.started_at = Some(Instant::now());
let timeout = Duration::from_millis(100);

while self.should_run() {
perf.poll(Duration::from_millis(100))?;
if self.use_ringbuf {
ringbuf.as_ref().unwrap().poll(timeout)?;
} else {
perfbuf.as_ref().unwrap().poll(timeout)?;
}
}

// Read all the data and finish
Expand Down Expand Up @@ -450,6 +494,7 @@ mod tests {
let options = RbperfOptions {
event: RbperfEvent::Syscall("enter_writev".to_string()),
verbose_bpf_logging:true,
use_ringbuf: false,
};
let mut r = Rbperf::new(options);
r.add_pid(pid.unwrap()).unwrap();
Expand Down

0 comments on commit 8a1e048

Please sign in to comment.