SkillAgentSearch skills...

ebpf

Use when writing eBPF programs with libbpf, bpftrace, or Rust with aya-ebpf and aya-log, attaching kprobes, tracepoints, or XDP hooks, declaring maps, sharing data with a Tokio loader, triaging verifier or Aya load errors, or porting with CO-RE. Not for full kernel bypass: use dpdk.

Install / Use

npx skills add OutlineDriven/outline-driven-development --skill ebpf

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

68/100

Supported Platforms

Universal

Our assessment of ebpf

ebpf scores 68/100 on our quality scale, 1094th of 1,180 Content & Media skills we index.

Its SKILL.md is 19 KB long, split into 6 sections with 16 code examples: a thorough specification that gives an agent plenty to work with.

It has no GitHub stars yet, so there is no community track record; judge it on its content.

Substance
30/30
Structure
18/20
Description
15/15
Adoption
0/20
Freshness
5/15

Maintenance, license and trust

  • We could not determine when the repository was last updated.
  • No license is declared. By default that means all rights are reserved: you can read it, but reusing or redistributing it is not clearly permitted. Ask the author before building on it commercially.
  • Its trust signals score 68/100, with 3 cautions from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

ebpf compared with similar skills

All 4 of these similar skills score higher than ebpf; compare them before choosing.

SkillScoreStarsUpdatedFormat
ebpf (this skill)by OutlineDriven680—SKILL.md
siyuanby siyuan-note10046.9ktodayMCP Server
algorithmic-artby anthropics100177.9k34d agoSKILL.md
pptxby anthropics100177.9k34d agoSKILL.md
designby nextlevelbuilder100137.2k12d agoSKILL.md

Frequently asked questions

How do I install ebpf?
Run npx skills add OutlineDriven/outline-driven-development --skill ebpf. The install tabs above show the steps for each supported agent.
Which AI agents does ebpf work with?
It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
Is ebpf safe to use?
It declares no license and scores 68/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is ebpf still maintained?
We could not determine when the repository was last updated.

name: ebpf description: 'Use when writing eBPF programs with libbpf, bpftrace, or Rust with aya-ebpf and aya-log, attaching kprobes, tracepoints, or XDP hooks, declaring maps, sharing data with a Tokio loader, triaging verifier or Aya load errors, or porting with CO-RE. Not for full kernel bypass: use dpdk.'

eBPF with libbpf, bpftrace, and Aya

Contract

| Field | Bound contract | |---|---| | Trigger | A kernel event needs tracing, a C/libbpf or Rust/Aya program needs writing or loading, the verifier rejects a program, kernel and user space need to share data, or an XDP filter needs attaching. | | Authority | Reversible local. The write set is the user's C, Rust, or script source and object files, programs and maps loaded into the running kernel, pins under /sys/fs/bpf, and XDP attachments on a named interface. Rollback is detaching the program, unpinning, and ip link set dev <if> xdp off. Loading needs CAP_BPF or root. No remote mutation. | | Side effect | Kernel state changes while a program or loader is loaded. Every load in this skill is paired with its detach; an Aya loader exits cleanly on Ctrl-C. | | Done | The chosen program loads, attaches, and produces output for the traced event, the Aya loader exits cleanly on Ctrl-C, or the verifier/Aya load error is mapped to its cause and the fix is applied. |

Inputs

  • Goal (required): a one-off trace, a production program with a user-space side, an inspection of loaded objects, or packet processing.
  • Implementation (required for a production program): C with libbpf or Rust with Aya; use bpftrace for a one-off trace.
  • Program type and attach point (required for a program): tracepoint, kprobe, uprobe, XDP, TC, LSM, or another type from the mode table in step 4.
  • Kernel (required): the running kernel must expose BTF at /sys/kernel/btf/vmlinux for C/libbpf CO-RE and Aya's target-BTF binding generation. The grounded floor is Linux 7.2 mainline or the 6.18 LTS line; every kernel feature named below exists in both.
  • Toolchain (required for libbpf work): clang with the bpf target, bpftool, and libbpf 1.x with its headers. For Aya work: stable Rust plus a nightly with rust-src, bpf-linker, cargo-generate, bpftool, and a log backend such as env_logger, using Rust edition 2024.

Procedure

  1. Pick the tool and implementation mode by goal. A one-line trace or script is bpftrace. A production program with a loader is libbpf in C or Aya in Rust. Inspecting loaded programs and maps is bpftool. High-rate packet processing is XDP with libbpf or Aya. Done when: one tool and, for a production program, one language mode are chosen.
  2. For a quick trace, write the bpftrace one-liner and confirm the probe exists. Done when: the probe prints for a live event.
bpftrace -l 'tracepoint:syscalls:*'                 # list probes
bpftrace -l 'kprobe:tcp_*'
bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%s %s\n", comm, str(args->filename)); }'
bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'
bpftrace -e '
  tracepoint:syscalls:sys_enter_read { @start[tid] = nsecs; }
  tracepoint:syscalls:sys_exit_read  { @us = hist((nsecs - @start[tid]) / 1000); delete(@start[tid]); }'
  1. For a production program, select C/libbpf or Rust/Aya. In C/libbpf mode, write the kernel side, generate the skeleton, and write the loader. Done when: the loader attaches and the map fills.
/* counter.bpf.c: count read() calls per process. */
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __type(key, u32);
    __type(value, u64);
    __uint(max_entries, 1024);
} call_count SEC(".maps");

SEC("tracepoint/syscalls/sys_enter_read")
int trace_read(struct trace_event_raw_sys_enter *ctx)
{
    u32 pid = bpf_get_current_pid_tgid() >> 32;
    u64 *cnt = bpf_map_lookup_elem(&call_count, &pid);
    if (cnt) {
        __sync_fetch_and_add(cnt, 1);
    } else {
        u64 one = 1;
        bpf_map_update_elem(&call_count, &pid, &one, BPF_ANY);
    }
    return 0;
}

char LICENSE[] SEC("license") = "GPL";
/* counter.c: open, load, attach, observe, destroy. */
#include "counter.skel.h"
#include <bpf/bpf.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

static volatile sig_atomic_t stop;

static void stop_handler(int signum)
{
    (void)signum;
    stop = 1;
}

int main(void)
{
    struct counter_bpf *skel = counter_bpf__open();
    int status = 1;
    if (!skel)
        return status;
    if (counter_bpf__load(skel))
        goto cleanup;
    if (counter_bpf__attach(skel))
        goto cleanup;
    if (signal(SIGINT, stop_handler) == SIG_ERR ||
        signal(SIGTERM, stop_handler) == SIG_ERR)
        goto cleanup;

    int map_fd = bpf_map__fd(skel->maps.call_count);
    while (!stop) {
        __u32 pid;
        __u64 count;
        if (bpf_map_get_next_key(map_fd, NULL, &pid) == 0 &&
            bpf_map_lookup_elem(map_fd, &pid, &count) == 0) {
            printf("pid %u: %llu reads\n", pid, (unsigned long long)count);
            status = 0;
            break;
        }
        sleep(1);
    }
    if (stop)
        status = 0;
cleanup:
    counter_bpf__destroy(skel);
    return status;
}
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
clang -g -O2 -target bpf -D__TARGET_ARCH_x86 -c counter.bpf.c -o counter.bpf.o
bpftool gen skeleton counter.bpf.o > counter.skel.h
gcc -o counter counter.c -lbpf -lelf -lz

The skeleton's open, load, attach, and destroy functions replace the older bpf_object__open_file and bpf_object__load split, which still exist for loading an object by path.

Rust/Aya mode for a production program. Install the toolchain and generate the project from the Aya template. Done when: cargo build succeeds on the fresh template.

rustup toolchain install stable
rustup toolchain install nightly --component rust-src
cargo install bpf-linker
cargo install cargo-generate
cargo generate https://github.com/aya-rs/aya-template   # prompts for name and program type

The template lays out <name>/ (user-space crate), <name>-ebpf/ (kernel-side crate), and <name>-common/ (shared types). A build script compiles the eBPF crate and embeds the object, so ordinary cargo build, cargo check, and cargo run --release drive both sides; there is no separate build step.

Write the kernel side with aya-ebpf and aya-log-ebpf. Done when: the crate compiles for the BPF target.

// <name>-ebpf/src/main.rs
#![no_std]
#![no_main]

use aya_ebpf::{
    helpers::bpf_get_current_pid_tgid,
    macros::{map, tracepoint},
    maps::PerCpuHashMap,
    programs::TracePointContext,
};
use aya_log_ebpf::info;

#[map]
static CALL_COUNT: PerCpuHashMap<u32, u64> = PerCpuHashMap::with_max_entries(1024, 0);

#[tracepoint]
pub fn trace_read(ctx: TracePointContext) -> u32 {
    let pid = (bpf_get_current_pid_tgid() >> 32) as u32;
    if let Some(count) = CALL_COUNT.get_ptr_mut(&pid) {
        unsafe { *count += 1; }
    } else {
        let one = 1;
        let _ = CALL_COUNT.insert(&pid, &one, 0);
    }
    info!(&ctx, "read() called by pid {}", pid);
    0
}

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    // The verifier never lets a panic path run; this satisfies no_std.
    loop {}
}

Write the user-space loader on Tokio. Done when: the loader attaches, prints log lines, and exits on Ctrl-C.

// <name>/src/main.rs
use aya::{include_bytes_aligned, maps::PerCpuHashMap, programs::TracePoint, Ebpf};
use aya_log::EbpfLogger;
use tokio::{
    io::{unix::AsyncFd, Interest},
    signal,
    task,
};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mut bpf = Ebpf::load(include_bytes_aligned!(concat!(env!("OUT_DIR"), "/<name>")))?;
    env_logger::init();
    let logger = EbpfLogger::init(&mut bpf)?;
    let mut logger = AsyncFd::with_interest(logger, Interest::READABLE)?;
    let logger_task = task::spawn(async move {
        loop {
            let mut guard = logger.readable_mut().await.expect("logger fd wait");
            guard.get_inner_mut().flush();
            guard.clear_ready();
        }
    });

    let program: &mut TracePoint = bpf.program_mut("trace_read").unwrap().try_into()?;
    program.load()?;
    program.attach("syscalls", "sys_enter_read")?;

    let counts: PerCpuHashMap<_, u32, u64> =
        PerCpuHashMap::try_from(bpf.map("CALL_COUNT").unwrap())?;

    signal::ctrl_c().await?;
    logger_task.abort();
    let _ = logger_task.await;
    for entry in counts.iter().filter_map(Result::ok) {
        let (pid, per_cpu) = entry;
        let count: u64 = per_cpu.iter().copied().sum();
        println!("pid {pid}: {count} reads");
    }
    Ok(())
}

The template's build script sets the object path; take it from the generated main.rs rather than hard-coding one.

  1. Choose the program type, attach target, and map. In C/libbpf, use a hash map for per-key state, an array for fixed-index config, a per-CPU map for a hot counter that must not lock, an LRU hash for bounded connection tracking, a program array for tail calls, and a ring buffer for kernel-to-user events. In Rust/Aya, select the program macro from the table below and the corresponding map. Prefer BPF_MAP_TYPE_RINGBUF over BPF_MAP_TYPE_PERF_EVENT_ARRAY (or RingBuf over PerfEventArray) for new code: one shared buffer, ordering across CPUs, variable-size records, and a reserve that fails instead of dropping. The full C type table, operations, and pinning live in references/ebpf-map-types.md. Done when: the program type, attach target, map type, and its capacity (max_entries or ring-buffer byte size) are chosen.

| Macro | Program type | Attach target | |---|---|---| | #[tracepoint] | Tracepoint | category and name, such as "syscalls", "sys_enter_read" | | #[kprobe], #[kretprobe] | Kernel probe | kernel function name | | #[uprobe], #[uretprobe] | User probe | binary path and symbol or offset | | #[xdp] | XDP | network interface | | #[classifier] | TC | interface and direction | | #[socket_filter] | Socket filter | socket fd | | #[perf_event] | Perf event | perf event fd | | #[lsm] | LSM hook | hook name | | #[sk_msg] | Sockmap | socket map |

Kernel-side Aya maps live in aya_ebpf::maps: HashMap, LruHashMap, PerCpuHashMap, Array, PerCpuArray, RingBuf, PerfEventArray, LpmTrie, ProgramArray, XskMap, and others. RingBuf::reserve returns an Option, so the kernel side must handle a full buffer.

use aya_ebpf::maps::RingBuf;

#[map]
static EVENTS: RingBuf = RingBuf::with_byte_size(256 * 1024, 0);

if let Some(mut entry) = EVENTS.reserve::<MyEvent>(0) {
    entry.write(MyEvent { pid, ts });
    entry.submit(0);
}
// User space: poll the ring buffer through tokio's AsyncFd.
use aya::maps::RingBuf;
use tokio::io::unix::AsyncFd;

let ring = RingBuf::try_from(bpf.take_map("EVENTS").unwrap())?;
let mut ring = AsyncFd::new(ring)?;
loop {
    let mut guard = ring.readable_mut().await?;
    let rb = guard.get_inner_mut();
    while let Some(item) = rb.next() {
        let event: &MyEvent = unsafe { &*(item.as_ptr() as *const MyEvent) };
        println!("event from pid {}", event.pid);
    }
    guard.clear_ready();
}
struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 256 * 1024);
} rb SEC(".maps");

SEC("kprobe/do_sys_openat2")
int handle_open(struct pt_regs *ctx)
{
    struct event *e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
    if (!e)
        return 0;
    e->pid = bpf_get_current_pid_tgid() >> 32;
    bpf_ringbuf_submit(e, 0);   /* bpf_ringbuf_discard(e, 0) on an error path */
    return 0;
}

User space consumes with ring_buffer__poll; the perf buffer equivalent is perf_buffer__poll over bpf_perf_event_output.

  1. Triage a verifier rejection or Aya load failure. Load with bpftool to see the full log, then map the message

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars0
CategoryContent
UpdatedNaNy ago
Forks0

Trust signals

68/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

2 medium1 low