This repository has been archived by the owner on Jul 11, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 138
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #128 from rhdxmr/percpu-array
Add array and percpu array maps
- Loading branch information
Showing
12 changed files
with
747 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
include!(concat!(env!("OUT_DIR"), "/gen_bindings.rs")); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
_ => (), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!(); | ||
} | ||
} |
Oops, something went wrong.