Skip to content

Commit

Permalink
Add BPF features detection and info subcommand
Browse files Browse the repository at this point in the history
Signed-off-by: Francisco Javier Honduvilla Coto <javierhonduco@gmail.com>
  • Loading branch information
javierhonduco committed Sep 25, 2022
1 parent 26a9e0c commit e81748a
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ BasedOnStyle: Google
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
ColumnLimit: 0
# IndentPPDirectives: AfterHash
IndentWidth: 4
SortIncludes: false
17 changes: 9 additions & 8 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
jobs:
build:
name: Build
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -37,7 +37,7 @@ jobs:
lint:
name: Lint
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
Expand All @@ -50,16 +50,17 @@ jobs:
components: rust-src, rustfmt
- name: Run cargo fmt
run: |
# This file is generated at build time, so rustfmt will fail
# with Error writing files: failed to resolve mod `bpf` if it
# These files are generated at build time, so some rustfmt versions
# fail with Error writing files: failed to resolve mod `bpf` if it
# does not exist
touch src/bpf/mod.rs
touch src/bpf/rbperf.rs
touch src/bpf/features.rs
cargo fmt
git diff --exit-code
clippy:
name: Clippy
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@nightly
Expand All @@ -83,7 +84,7 @@ jobs:
test:
name: Test
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -120,4 +121,4 @@ jobs:
export RUST_BACKTRACE=1
# Running only 3.1.2 for a bit, will enable the rest once we make sure
# that things are looking good
cargo test -- rbperf::tests::rbperf_test_3_1_2 --nocapture
cargo test -- rbperf::tests::rbperf_test_3_1_2 --nocapture
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

/target
src/bpf/mod.rs
src/bpf/rbperf.rs
src/bpf/features.rs

rbperf_out*
rbperf_flame*
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ $ sudo rbperf record --pid `pidof ruby` syscall enter_writev

Some debug information will be printed, and a flamegraph called `rbperf_flame_$date` will be written to disk 🎉

## Developing and troubleshooting

Debug logs can be enabled with `RUST_LOG=debug`. The info subcommand, `rbperf info` shows the supported BPF features as well as other supported details.


## Stability

`rbperf` is in active development and the CLI and APIs might change any time
Expand Down
39 changes: 31 additions & 8 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ use std::io::Read;
use std::io::Write;
use std::path::Path;

const HEADER: &str = "./src/bpf/rbperf.h";
const SRC: &str = "./src/bpf/rbperf.bpf.c";
const RUBY_STACK_SOURCE: &str = "./src/bpf/rbperf.bpf.c";
const RUBY_STACK_HEADER: &str = "./src/bpf/rbperf.h";
const RUBY_STACK_SKELETON: &str = "./src/bpf/rbperf.rs";

const FEATURES_SOURCE: &str = "./src/bpf/features.bpf.c";
const FEATURES_SKELETON: &str = "./src/bpf/features.rs";

#[derive(Debug)]
struct BuildCallbacks;
Expand Down Expand Up @@ -41,7 +45,7 @@ fn main() {
let bindings = bindgen::Builder::default()
// The input header we would like to generate
// bindings for.
.header("src/bpf/rbperf.h")
.header(RUBY_STACK_HEADER)
.parse_callbacks(Box::new(BuildCallbacks))
// Finish the builder and generate the bindings.
.generate()
Expand All @@ -67,16 +71,16 @@ fn main() {
.write_all(new_contents.as_bytes())
.unwrap();

let skel = Path::new("./src/bpf/mod.rs");
let skel = Path::new(RUBY_STACK_SKELETON);
match SkeletonBuilder::new()
.source(SRC)
.source(RUBY_STACK_SOURCE)
.clang_args("-Wextra -Wall -Werror")
.build_and_generate(skel)
{
Ok(_) => {}
Err(err) => match err {
Error::Build(msg) | Error::Generate(msg) => {
panic!("Error running SkeletonBuilder = {}", msg);
panic!("Error running SkeletonBuilder for rbperf = {}", msg);
}
},
}
Expand All @@ -96,6 +100,25 @@ fn main() {
.write_all(new_contents.as_bytes())
.unwrap();

println!("cargo:rerun-if-changed={}", HEADER);
println!("cargo:rerun-if-changed={}", SRC);
// BPF feature detection.
let skel = Path::new(FEATURES_SKELETON);
match SkeletonBuilder::new()
.source(FEATURES_SOURCE)
.clang_args("-Wextra -Wall -Werror")
.build_and_generate(skel)
{
Ok(_) => {}
Err(err) => match err {
Error::Build(msg) | Error::Generate(msg) => {
panic!(
"Error running SkeletonBuilder for feature detector = {}",
msg
);
}
},
}

println!("cargo:rerun-if-changed={}", RUBY_STACK_SOURCE);
println!("cargo:rerun-if-changed={}", RUBY_STACK_HEADER);
println!("cargo:rerun-if-changed={}", FEATURES_SOURCE);
}
28 changes: 28 additions & 0 deletions src/bpf/features.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "vmlinux.h"

#include <bpf/bpf_core_read.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

bool feature_has_run = false;

bool feature_is_jited = false;
bool feature_has_stats = false;
bool feature_has_tail_call = false;
bool feature_has_ringbuf = false;
bool feature_bpf_loop = false;

SEC("kprobe/hrtimer_start_range_ns")
int features_entry() {
static struct bpf_prog *bpf_prog = NULL;
feature_has_run = true;

feature_is_jited = bpf_core_field_exists(bpf_prog->jited);
feature_has_stats = bpf_core_field_exists(bpf_prog->stats);
feature_has_ringbuf = bpf_core_enum_value_exists(enum bpf_map_type, BPF_MAP_TYPE_RINGBUF);
feature_has_tail_call = bpf_core_enum_value_exists(enum bpf_func_id, BPF_FUNC_tail_call);
feature_has_ringbuf = bpf_core_enum_value_exists(enum bpf_map_type, BPF_MAP_TYPE_RINGBUF);
feature_bpf_loop = bpf_core_enum_value_exists(enum bpf_func_id, BPF_FUNC_loop);

return 0;
}
2 changes: 2 additions & 0 deletions src/bpf/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod features;
pub mod rbperf;
2 changes: 0 additions & 2 deletions src/bpf/rbperf.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@
//
// Copyright (c) 2022 The rbperf authors

// clang-format off
#include "rbperf.h"

#include "vmlinux.h"

#include <bpf/bpf_core_read.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
// clang-format on

struct {
// This map's type is a placeholder, it's dynamically set
Expand Down
53 changes: 53 additions & 0 deletions src/info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use crate::bpf::features::FeaturesSkelBuilder;
use anyhow::{anyhow, Result};
use nix::sys::utsname::uname;
use std::fs::File;
use std::thread;
use std::time::Duration;

pub struct SystemInfo {
pub os_release: String,
pub debug_fs: bool,
}

pub struct BpfFeatures {
pub is_jited: bool,
pub has_stats: bool,
pub has_tail_call: bool,
pub has_ringbuf: bool,
pub has_bpf_loop: bool,
}

pub struct Info {
pub system: SystemInfo,
pub bpf: Result<BpfFeatures>,
}

pub fn info() -> Result<Info> {
let skel_builder = FeaturesSkelBuilder::default();
let open_skel = skel_builder.open().unwrap();
let mut bpf = open_skel.load().unwrap();
bpf.attach().unwrap();

thread::sleep(Duration::from_millis(50));

let bpf_features = if bpf.bss().feature_has_run {
Ok(BpfFeatures {
is_jited: bpf.bss().feature_is_jited,
has_stats: bpf.bss().feature_has_stats,
has_tail_call: bpf.bss().feature_has_tail_call,
has_ringbuf: bpf.bss().feature_has_ringbuf,
has_bpf_loop: bpf.bss().feature_bpf_loop,
})
} else {
Err(anyhow!("Could not find out supported BPF features"))
};

Ok(Info {
system: SystemInfo {
os_release: uname().release().to_string(),
debug_fs: File::open("/sys/kernel/debug/").is_ok(),
},
bpf: bpf_features,
})
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod arch;
pub mod binary;
pub mod bpf;
pub mod events;
pub mod info;
pub mod process;
pub mod profile;
pub mod rbperf;
Expand Down
27 changes: 27 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::fs::File;
use std::sync::Arc;

use anyhow::{anyhow, Result};
use rbperf::info::info;
use rbperf::profile::Profile;
use rbperf::rbperf::{Rbperf, RbperfEvent, RbperfOptions};

Expand All @@ -22,6 +23,7 @@ struct Args {
#[derive(clap::Subcommand, Debug)]
enum Command {
Record(RecordSubcommand),
Info(InfoSubcommand),
}

#[derive(Parser, Debug)]
Expand Down Expand Up @@ -55,6 +57,9 @@ struct SycallSubcommand {
list: bool,
}

#[derive(Parser, Debug)]
struct InfoSubcommand {}

fn available_syscalls() -> Vec<String> {
let mut syscalls = Vec::new();

Expand All @@ -70,6 +75,7 @@ fn available_syscalls() -> Vec<String> {

syscalls
}

fn main() -> Result<()> {
env_logger::init();

Expand All @@ -83,6 +89,27 @@ fn main() -> Result<()> {
.expect("Failed to set handler for SIGINT / SIGTERM");

match args.subcmd {
Command::Info(_) => {
if !Uid::current().is_root() {
return Err(anyhow!("rbperf requires root to load and run BPF programs"));
}

let info = info()?;
println!("System info");
println!("-----------");
println!("Kernel release: {}", info.system.os_release);
println!("Debugfs mounted: {}", info.system.debug_fs);
println!();

println!("BPF features");
println!("------------");
let bpf_feature = info.bpf?;
println!("is jited: {}", bpf_feature.is_jited);
println!("has stats: {}", bpf_feature.has_stats);
println!("has tail_call: {}", bpf_feature.has_tail_call);
println!("has ringbuf: {}", bpf_feature.has_ringbuf);
println!("has bpf_loop: {}", bpf_feature.has_bpf_loop);
}
Command::Record(record) => {
if !Uid::current().is_root() {
return Err(anyhow!("rbperf requires root to load and run BPF programs"));
Expand Down
2 changes: 1 addition & 1 deletion src/rbperf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use proc_maps::Pid;
use syscalls;

use crate::arch;
use crate::bpf::{rbperf_rodata_types::rbperf_event_type, RbperfSkel, RbperfSkelBuilder};
use crate::bpf::rbperf::{rbperf_rodata_types::rbperf_event_type, RbperfSkel, RbperfSkelBuilder};
use crate::events::{setup_perf_event, setup_syscall_event};
use crate::process::ProcessInfo;
use crate::profile::Profile;
Expand Down

0 comments on commit e81748a

Please sign in to comment.