Merge pull request #13 from pythonbpf/ringbuf

Add ringbuf support
This commit is contained in:
varunrmallya
2025-10-01 23:52:28 +05:30
committed by GitHub
8 changed files with 244 additions and 52 deletions

View File

@ -21,6 +21,10 @@ class DebugInfoGenerator:
)
return self._type_cache[key]
def get_int32_type(self) -> Any:
"""Get debug info for signed 32-bit integer"""
return self.get_basic_type("int", 32, dc.DW_ATE_signed)
def get_uint32_type(self) -> Any:
"""Get debug info for unsigned 32-bit integer"""
return self.get_basic_type("unsigned int", 32, dc.DW_ATE_unsigned)

View File

@ -1,4 +1,4 @@
from .maps import HashMap, PerfEventArray
from .maps import HashMap, PerfEventArray, RingBuf
from .maps_pass import maps_proc
__all__ = ["HashMap", "PerfEventArray", "maps_proc"]
__all__ = ["HashMap", "PerfEventArray", "maps_proc", "RingBuf"]

View File

@ -1,3 +1,4 @@
# This file provides type and function hints only and does not actually give any functionality.
class HashMap:
def __init__(self, key, value, max_entries):
self.key = key
@ -33,3 +34,18 @@ class PerfEventArray:
def output(self, data):
pass # Placeholder for output method
class RingBuf:
def __init__(self, max_entries):
self.max_entries = max_entries
def reserve(self, size: int, flags=0):
if size > self.max_entries:
raise ValueError("size cannot be greater than set maximum entries")
return 0
def submit(self, data, flags=0):
pass
# add discard, output and also give names to flags and stuff

View File

@ -1,11 +1,12 @@
import ast
from logging import Logger
from llvmlite import ir
from enum import Enum
from .maps_utils import MapProcessorRegistry
from ..debuginfo import DebugInfoGenerator
import logging
logger = logging.getLogger(__name__)
logger: Logger = logging.getLogger(__name__)
def maps_proc(tree, module, chunks):
@ -13,7 +14,7 @@ def maps_proc(tree, module, chunks):
map_sym_tab = {}
for func_node in chunks:
if is_map(func_node):
print(f"Found BPF map: {func_node.name}")
logger.info(f"Found BPF map: {func_node.name}")
map_sym_tab[func_node.name] = process_bpf_map(func_node, module)
return map_sym_tab
@ -26,8 +27,41 @@ def is_map(func_node):
class BPFMapType(Enum):
UNSPEC = 0
HASH = 1
ARRAY = 2
PROG_ARRAY = 3
PERF_EVENT_ARRAY = 4
PERCPU_HASH = 5
PERCPU_ARRAY = 6
STACK_TRACE = 7
CGROUP_ARRAY = 8
LRU_HASH = 9
LRU_PERCPU_HASH = 10
LPM_TRIE = 11
ARRAY_OF_MAPS = 12
HASH_OF_MAPS = 13
DEVMAP = 14
SOCKMAP = 15
CPUMAP = 16
XSKMAP = 17
SOCKHASH = 18
CGROUP_STORAGE_DEPRECATED = 19
CGROUP_STORAGE = 19
REUSEPORT_SOCKARRAY = 20
PERCPU_CGROUP_STORAGE_DEPRECATED = 21
PERCPU_CGROUP_STORAGE = 21
QUEUE = 22
STACK = 23
SK_STORAGE = 24
DEVMAP_HASH = 25
STRUCT_OPS = 26
RINGBUF = 27
INODE_STORAGE = 28
TASK_STORAGE = 29
BLOOM_FILTER = 30
USER_RINGBUF = 31
CGRP_STORAGE = 32
def create_bpf_map(module, map_name, map_params):
@ -51,7 +85,7 @@ def create_bpf_map(module, map_name, map_params):
def create_map_debug_info(module, map_global, map_name, map_params):
"""Generate debug information metadata for BPF map"""
"""Generate debug information metadata for BPF maps HASH and PERF_EVENT_ARRAY"""
generator = DebugInfoGenerator(module)
uint_type = generator.get_uint32_type()
@ -112,6 +146,60 @@ def create_map_debug_info(module, map_global, map_name, map_params):
return global_var
def create_ringbuf_debug_info(module, map_global, map_name, map_params):
"""Generate debug information metadata for BPF RINGBUF map"""
generator = DebugInfoGenerator(module)
int_type = generator.get_int32_type()
type_array = generator.create_array_type(
int_type, map_params.get("type", BPFMapType.RINGBUF).value
)
type_ptr = generator.create_pointer_type(type_array, 64)
type_member = generator.create_struct_member("type", type_ptr, 0)
max_entries_array = generator.create_array_type(int_type, map_params["max_entries"])
max_entries_ptr = generator.create_pointer_type(max_entries_array, 64)
max_entries_member = generator.create_struct_member(
"max_entries", max_entries_ptr, 64
)
elements_arr = [type_member, max_entries_member]
struct_type = generator.create_struct_type(elements_arr, 128, is_distinct=True)
global_var = generator.create_global_var_debug_info(
map_name, struct_type, is_local=False
)
map_global.set_metadata("dbg", global_var)
return global_var
@MapProcessorRegistry.register("RingBuf")
def process_ringbuf_map(map_name, rval, module):
"""Process a BPF_RINGBUF map declaration"""
logger.info(f"Processing Ringbuf: {map_name}")
map_params = {"type": BPFMapType.RINGBUF}
# Parse max_entries if present
if len(rval.args) >= 1 and isinstance(rval.args[0], ast.Constant):
const_val = rval.args[0].value
if isinstance(const_val, int):
map_params["max_entries"] = const_val
for keyword in rval.keywords:
if keyword.arg == "max_entries" and isinstance(keyword.value, ast.Constant):
const_val = keyword.value.value
if isinstance(const_val, int):
map_params["max_entries"] = const_val
logger.info(f"Ringbuf map parameters: {map_params}")
map_global = create_bpf_map(module, map_name, map_params)
create_ringbuf_debug_info(module, map_global, map_name, map_params)
return map_global
@MapProcessorRegistry.register("HashMap")
def process_hash_map(map_name, rval, module):
"""Process a BPF_HASH map declaration"""

View File

@ -1,47 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <linux/blkdev.h>
#define __TARGET_ARCH_aarch64
#define u64 unsigned long long
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 10240);
__type(key, struct request *);
__type(value, u64);
} start SEC(".maps");
SEC("kprobe/blk_start_request")
int BPF_KPROBE(trace_start_req, struct request *req)
{
u64 ts = bpf_ktime_get_ns();
bpf_map_update_elem(&start, &req, &ts, BPF_ANY);
return 0;
}
SEC("kprobe/blk_mq_start_request")
int BPF_KPROBE(trace_start_mq, struct request *req)
{
u64 ts = bpf_ktime_get_ns();
bpf_map_update_elem(&start, &req, &ts, BPF_ANY);
return 0;
}
SEC("kprobe/blk_account_io_completion")
int BPF_KPROBE(trace_completion, struct request *req)
{
u64 *tsp, delta;
tsp = bpf_map_lookup_elem(&start, &req);
if (tsp) {
delta = bpf_ktime_get_ns() - *tsp;
bpf_printk("%d %x %d\n", req->__data_len,
req->cmd_flags, delta / 1000);
bpf_map_delete_elem(&start, &req);
}
return 0;
}
char LICENSE[] SEC("license") = "GPL";

View File

@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <linux/types.h>
// Define the structure to be sent via ringbuf
struct event {
__u32 pid;
__u32 uid;
__u64 timestamp;
char comm[16]; // Process name
};
// Define the ringbuffer map
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024); // 256 KB
} events SEC(".maps");
// Tracepoint for execve system calls
SEC("tracepoint/syscalls/sys_enter_execve")
int trace_execve(void *ctx)
{
struct event *e;
__u64 pid_tgid;
__u64 uid_gid;
// Reserve space in the ringbuffer
e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
if (!e)
return 0;
// Fill the struct with data
pid_tgid = bpf_get_current_pid_tgid();
e->pid = pid_tgid >> 32;
uid_gid = bpf_get_current_uid_gid();
e->uid = uid_gid & 0xFFFFFFFF;
e->timestamp = bpf_ktime_get_ns();
bpf_get_current_comm(&e->comm, sizeof(e->comm));
// Submit the event to ringbuffer
bpf_ringbuf_submit(e, 0);
return 0;
}
char LICENSE[] SEC("license") = "GPL";

View File

@ -0,0 +1,45 @@
from pythonbpf import bpf, map, struct, section, bpfglobal, compile
from pythonbpf.helpers import ktime, pid
from pythonbpf.maps import PerfEventArray
from ctypes import c_void_p, c_int32, c_uint64
# PLACEHOLDER EXAMPLE. THIS SHOULD TECHNICALLY STILL FAIL TESTS
@bpf
@struct
class data_t:
pid: c_uint64
ts: c_uint64
comm: str(16)
@bpf
@map
def events() -> PerfEventArray:
return PerfEventArray(key_size=c_int32, value_size=c_int32)
@bpf
@section("tracepoint/syscalls/sys_enter_clone")
def hello(ctx: c_void_p) -> c_int32:
dataobj = data_t()
ts = ktime()
strobj = "hellohellohello"
dataobj.pid = pid()
dataobj.ts = ktime()
# dataobj.comm = strobj
print(
f"clone called at {dataobj.ts} by pid {dataobj.pid}, comm {strobj} at time {ts}"
)
events.output(dataobj)
return c_int32(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
compile()

View File

@ -0,0 +1,35 @@
from pythonbpf import bpf, BPF, map, bpfglobal, section, compile, compile_to_ir
from pythonbpf.maps import RingBuf, HashMap
from ctypes import c_int32, c_void_p
# Define a map
@bpf
@map
def mymap() -> RingBuf:
return RingBuf(max_entries=(1024))
@bpf
@map
def mymap2() -> HashMap:
return HashMap(key=c_int32, value=c_int32, max_entries=1024)
@bpf
@section("tracepoint/syscalls/sys_enter_clone")
def random_section(ctx: c_void_p) -> c_int32:
print("Hello")
return c_int32(0)
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
compile_to_ir("ringbuf.py", "ringbuf.ll")
compile()
b = BPF()
b.load_and_attach()