lightswitch 0.7.0

CPU profiler as a library for Linux suitable for on-demand and continuous profiling
extern crate bindgen;

use std::env;
use std::path::PathBuf;

use bindgen::callbacks::{DeriveInfo, ParseCallbacks};
use glob::glob;
use libbpf_cargo::SkeletonBuilder;

const PROFILER_BPF_HEADER: &str = "src/bpf/profiler.h";
const PROFILER_BPF_SOURCE: &str = "src/bpf/profiler.bpf.c";
const PROFILER_SKELETON: &str = "profiler_skel.rs";

const TRACERS_BPF_HEADER: &str = "src/bpf/tracers.h";
const TRACERS_BPF_SOURCE: &str = "src/bpf/tracers.bpf.c";
const TRACERS_SKELETON: &str = "tracers_skel.rs";

#[derive(Debug)]
struct CustomParseCallbacks;

impl ParseCallbacks for CustomParseCallbacks {
    fn add_derives(&self, derive_info: &DeriveInfo) -> Vec<String> {
        if derive_info.name == "native_stack_t" {
            vec!["Hash".into(), "Eq".into(), "PartialEq".into()]
        } else {
            vec![]
        }
    }
}

fn main() {
    // Inform cargo of when to re build
    for path in glob("src/bpf/*[hc]").unwrap().flatten() {
        println!("cargo:rerun-if-changed={}", path.display());
    }

    // Main native profiler.
    let bindings = bindgen::Builder::default()
        .derive_default(true)
        .parse_callbacks(Box::new(CustomParseCallbacks))
        .header(PROFILER_BPF_HEADER)
        .generate()
        .expect("Unable to generate bindings");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    let bindings_out_file = out_path.join("profiler_bindings.rs");
    bindings
        .write_to_file(bindings_out_file)
        .expect("Couldn't write bindings!");

    // Tracers.
    let bindings = bindgen::Builder::default()
        .derive_default(true)
        .header(TRACERS_BPF_HEADER)
        .generate()
        .expect("Unable to generate bindings");

    let bindings_out_file = out_path.join("tracers_bindings.rs");
    bindings
        .write_to_file(bindings_out_file)
        .expect("Couldn't write bindings!");

    SkeletonBuilder::new()
        .source(PROFILER_BPF_SOURCE)
        // Older kernels reject the current code if compiled with v3 of the ISA (the default in
        // LLVM 20+). Select the previous default BPF instruction set.
        .clang_args(["-mcpu=v1", "-Wextra", "-Wall", "-Werror"])
        .build_and_generate(out_path.join(PROFILER_SKELETON))
        .expect("run skeleton builder");

    SkeletonBuilder::new()
        .source(TRACERS_BPF_SOURCE)
        .clang_args(["-Wextra", "-Wall", "-Werror", "-Wno-unused-function"])
        .build_and_generate(out_path.join(TRACERS_SKELETON))
        .expect("run skeleton builder");
}