Skip to content
This repository has been archived by the owner on Jul 11, 2023. It is now read-only.

Commit

Permalink
Merge pull request #128 from rhdxmr/percpu-array
Browse files Browse the repository at this point in the history
Add array and percpu array maps
  • Loading branch information
rsdy authored Mar 19, 2021
2 parents 147a2d5 + 6db8c80 commit 1820b71
Show file tree
Hide file tree
Showing 12 changed files with 747 additions and 72 deletions.
1 change: 1 addition & 0 deletions cargo-bpf/src/accessors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ pub fn generate_read_accessors(bindings: &str, whitelist: &[&str]) -> String {
let accessors = functions.values();
let ident = Ident::new(item_id, Span::call_site());
let item = quote! {
/// Auto-generated read-accessors by cargo_bpf::accessors::generate_read_accessors
impl #ident {
#(#accessors)*
}
Expand Down
5 changes: 5 additions & 0 deletions examples/example-probes/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ required-features = ["probes"]
name = "echo"
path = "src/echo/main.rs"
required-features = ["probes"]

[[bin]]
name = "biolatpcts"
path = "src/biolatpcts/main.rs"
required-features = ["probes"]
73 changes: 73 additions & 0 deletions examples/example-probes/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use std::env;
use std::fs::File;
use std::io::{self, Write};
use std::path::PathBuf;
use std::process::Command;

use cargo_bpf_lib::bindgen as bpf_bindgen;

fn create_module(path: PathBuf, name: &str, bindings: &str) -> io::Result<()> {
let mut file = File::create(&path)?;
let res = writeln!(
&mut file,
r"
/// Auto generated by BPF bindgen for example-probes
mod {name} {{
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
#![allow(unused_unsafe)]
#![allow(clippy::all)]
{bindings}
}}
pub use {name}::*;
",
name = name,
bindings = bindings
);

// application of the rustfmt is optional and is only for debugging
// auto-generated code.
let _ = Command::new("rustfmt")
.arg("--edition=2018")
.arg("--emit=files")
.arg(&path)
.status();

res
}

fn main() {
// Compilation of example-probes is executed twice.
// - Once for building probes by a build-script of example-userspace
// - Once for compiling modules by a normal cargo
if env::var("CARGO_FEATURE_PROBES").is_err() {
eprintln!("`probes' feature is not set. abort. This build script is designed to be called by cargo-bpf and to build BPF programs (= probes)");
return;
}
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());

let mut builder = bpf_bindgen::builder();
// specify kernel headers in include/bindings.h, not here.
builder = builder.header("include/bindings.h");
// designate whitelist types
let types = ["request"];
// allowing variables
let variables = ["NSEC_PER_MSEC", "NSEC_PER_USEC"];
for &ty in types.iter() {
builder = builder.whitelist_type(ty);
}
for &var in variables.iter() {
builder = builder.whitelist_var(var);
}

let mut bindings = builder
.generate_inline_functions(true)
.generate()
.expect("failed to generate bindings")
.to_string();
let accessors = bpf_bindgen::generate_read_accessors(&bindings, &types);
bindings.push_str("use redbpf_probes::helpers::bpf_probe_read;");
bindings.push_str(&accessors);
create_module(out_dir.join("gen_bindings.rs"), "gen_bindings", &bindings).unwrap();
}
10 changes: 10 additions & 0 deletions examples/example-probes/include/bindings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// include kernel headers here to generate rust bindings of kernel data types
// which are used by kprobe BPF programs.

#define BITS_PER_LONG 64
#include <linux/kconfig.h>
#include <linux/types.h>
#include <linux/math64.h>
#include <linux/blk_types.h>
#include <linux/blkdev.h>
#include <linux/time64.h>
1 change: 1 addition & 0 deletions examples/example-probes/src/bindings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include!(concat!(env!("OUT_DIR"), "/gen_bindings.rs"));
71 changes: 71 additions & 0 deletions examples/example-probes/src/biolatpcts/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// This is a remake of examples/tracing/biolatpcts.py of bcc using RedBPF. The
// purpose of this code is to show how to use percpu array and how to write
// kprobe program. This source code implements a half part, a BPF
// program running inside kernel. The other part of userspace is implemented at
// `example-userspace/examples/biolatpcts.rs`

#![no_std]
#![no_main]
use core::cmp;
use core::mem;
use core::ptr;

use redbpf_probes::kprobe::prelude::*;

// You can use types and variables defined in Linux kernel headers. In order to
// use kernel data types, you may include specific header into
// `example-probes/include/bindings.h` and add some stuff to
// `example-probes/build.rs` first. Please take a look at those files.
use example_probes::bindings::{request, NSEC_PER_MSEC, NSEC_PER_USEC};

program!(0xFFFFFFFE, "GPL");

#[map("lat_100ms")]
static mut LAT_100MS: PerCpuArray<u64> = PerCpuArray::with_max_entries(100);

#[map("lat_1ms")]
static mut LAT_1MS: PerCpuArray<u64> = PerCpuArray::with_max_entries(100);

#[map("lat_10us")]
static mut LAT_10US: PerCpuArray<u64> = PerCpuArray::with_max_entries(100);

#[kprobe("blk_account_io_done")]
fn blk_account_io_done(regs: Registers) {
let rq: &request = unsafe { (regs.parm1() as *const request).as_ref().unwrap() };

let stime = rq.io_start_time_ns().unwrap();
if stime == 0 {
return;
}
let now = regs.parm2();
let dur = now - stime;
let slot = cmp::min(dur / (100 * NSEC_PER_MSEC) as u64, 99);
unsafe {
match LAT_100MS.get_mut(slot as i32) {
Some(mut val) => *val += 1,
_ => (),
}
}
if slot > 0 {
return;
}

let slot = cmp::min(dur / NSEC_PER_MSEC as u64, 99);
unsafe {
match LAT_1MS.get_mut(slot as i32) {
Some(mut val) => *val += 1,
_ => (),
}
}
if slot > 0 {
return;
}

let slot = cmp::min(dur / (10 * NSEC_PER_USEC) as u64, 99);
unsafe {
match LAT_10US.get_mut(slot as i32) {
Some(mut val) => *val += 1,
_ => (),
}
}
}
12 changes: 12 additions & 0 deletions examples/example-probes/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
#![no_std]
// bindings module is used only for compiling BPF programs (= probes).
// And the compilation of BPF programs is executed by cargo-bpf on behalf of
// example-userspace.
//
// This source code is subject to be compiled twice during building example
// programs.
// - Once for building BPF programs by a cargo-bpf executed by a build-script
// of example-userspace
// - Once for compiling modules of probes by a normal cargo
#[cfg(feature = "probes")]
pub mod bindings;

pub mod echo;
pub mod mallocstacks;
pub mod tcp_lifetime;
Expand Down
133 changes: 133 additions & 0 deletions examples/example-userspace/examples/biolatpcts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// This example is a remake of examples/tracing/biolatpcts.py of bcc using
// RedBPF. The purpose of this program is to show how to use kprobe and percpu
// array in userspace. This source code deals with userspace part only. You can
// find another half, a BPF program, at example-probes/biolatpcts/main.rs
use std::cmp;
use std::time::Duration;
use tokio::time::delay_for;

use redbpf::load::Loader;
use redbpf::PerCpuArray;

fn find_pct(req: f32, total: u64, slots: &[u64], mut idx: usize, mut counted: u64) -> (usize, u64) {
while idx > 0 {
idx -= 1;
if slots[idx] > 0 {
counted += slots[idx];
if counted as f32 / total as f32 * 100.0 >= 100.0 - req {
break;
}
}
}
(idx, counted)
}

fn calc_lat_pct(
req_pcts: &[u64],
total: u64,
lat_100ms: &[u64],
lat_1ms: &[u64],
lat_10us: &[u64],
) -> Box<[u64]> {
let mut pcts = vec![0u64; req_pcts.len()].into_boxed_slice();

if total == 0 {
return pcts;
}

let data = [(100_000, lat_100ms), (1000, lat_1ms), (10, lat_10us)];
let mut data_sel = 0;
let mut idx = 100;
let mut counted = 0;

for pct_idx in (0..req_pcts.len()).rev() {
let req = req_pcts[pct_idx] as f32;
let mut gran;
loop {
let last_counted = counted;
gran = data[data_sel].0;
let slots = data[data_sel].1;
let pct = find_pct(req, total, &slots, idx, counted);
idx = pct.0;
counted = pct.1;
if idx > 0 || data_sel == data.len() - 1 {
break;
}
counted = last_counted;
data_sel += 1;
idx = 100;
}

pcts[pct_idx] = gran * idx as u64 + gran / 2;
}

return pcts;
}

#[tokio::main]
async fn main() -> ! {
if unsafe { libc::getuid() != 0 } {
eprintln!("You must be root to use eBPF!");
std::process::exit(1);
}

let mut loaded = Loader::load(include_bytes!(concat!(
env!("OUT_DIR"),
"/target/bpf/programs/biolatpcts/biolatpcts.elf"
)))
.expect("error loading BPF program");

for kp in loaded.kprobes_mut() {
kp.attach_kprobe(&kp.name(), 0).expect(&format!(
"error attaching kprobe BPF program to kernel function {}",
kp.name()
));
}
let cur_lat_100ms =
PerCpuArray::<u64>::new(loaded.map("lat_100ms").expect("array lat_100ms not found"))
.expect("error creating PerCpuArray in userspace");
let cur_lat_1ms =
PerCpuArray::<u64>::new(loaded.map("lat_1ms").expect("array lat_1ms not found"))
.expect("error creating PerCpuArray in userspace");
let cur_lat_10us =
PerCpuArray::<u64>::new(loaded.map("lat_10us").expect("array lat_10us not found"))
.expect("error creating PerCpuArray in userspace");

let mut last_lat_100ms = [0; 100];
let mut last_lat_1ms = [0; 100];
let mut last_lat_10us = [0; 100];

let mut lat_100ms = [0; 100];
let mut lat_1ms = [0; 100];
let mut lat_10us = [0; 100];

loop {
delay_for(Duration::from_secs(3)).await;

let mut lat_total = 0;

for i in 0usize..100 {
let v: u64 = cur_lat_100ms.get(i as i32).unwrap().iter().sum();
lat_100ms[i] = cmp::max(v - last_lat_100ms[i], 0);
last_lat_100ms[i] = v;

let v: u64 = cur_lat_1ms.get(i as i32).unwrap().iter().sum();
lat_1ms[i] = cmp::max(v - last_lat_1ms[i], 0);
last_lat_1ms[i] = v;

let v: u64 = cur_lat_10us.get(i as i32).unwrap().iter().sum();
lat_10us[i] = cmp::max(v - last_lat_10us[i], 0);
last_lat_10us[i] = v;

lat_total += lat_100ms[i];
}

let target_pcts = [50, 75, 90, 99];
let pcts = calc_lat_pct(&target_pcts, lat_total, &lat_100ms, &lat_1ms, &lat_10us);

for i in 0..target_pcts.len() {
print!("p{}={}us ", target_pcts[i], pcts[i]);
}
println!();
}
}
Loading

0 comments on commit 1820b71

Please sign in to comment.