Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pythonbpf/debuginfo/debug_info_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ def get_basic_type(self, name: str, size: int, encoding: int) -> Any:
)
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)
Expand Down
4 changes: 2 additions & 2 deletions pythonbpf/maps/__init__.py
Original file line number Diff line number Diff line change
@@ -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"]
16 changes: 16 additions & 0 deletions pythonbpf/maps/maps.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -33,3 +34,18 @@ def __init__(self, key_size, value_size):

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
94 changes: 91 additions & 3 deletions pythonbpf/maps/maps_pass.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
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):
"""Process all functions decorated with @map to find BPF maps"""
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

Expand All @@ -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):
Expand All @@ -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()
Expand Down Expand Up @@ -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"""
Expand Down
47 changes: 0 additions & 47 deletions tests/c-form/ex8.bpf.c

This file was deleted.

51 changes: 51 additions & 0 deletions tests/c-form/ringbuf.bpf.c
Original file line number Diff line number Diff line change
@@ -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";
45 changes: 45 additions & 0 deletions tests/failing_tests/perf_buffer_map.py
Original file line number Diff line number Diff line change
@@ -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()
35 changes: 35 additions & 0 deletions tests/failing_tests/ringbuf.py
Original file line number Diff line number Diff line change
@@ -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()