From 2a93a325ce02c5e33405b6966ecaf3caeb600af5 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Thu, 2 Oct 2025 06:07:17 +0530 Subject: [PATCH 1/3] add ringbuf reserve function --- .pre-commit-config.yaml | 4 +- examples/hello_world.py | 4 -- pythonbpf/helper/bpf_helper_handler.py | 61 ++++++++++++++++++++++++++ tests/c-form/ringbuf.bpf.c | 32 +++++++------- tests/passing_tests/ringbuf.py | 14 ++---- 5 files changed, 82 insertions(+), 33 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 89c6a807..bc11687a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,14 +41,14 @@ repos: - id: ruff args: ["--fix", "--show-fixes"] - id: ruff-format - exclude: ^(docs)|^(tests)|^(examples) + exclude: ^(tests/|examples/|docs/) # Checking static types - repo: https://github.com/pre-commit/mirrors-mypy rev: "v1.10.0" hooks: - id: mypy - exclude: ^(tests)|^(examples) + exclude: ^(tests/|examples/) additional_dependencies: [types-setuptools] # Changes tabs to spaces diff --git a/examples/hello_world.py b/examples/hello_world.py index 5ce35f90..9d29d597 100644 --- a/examples/hello_world.py +++ b/examples/hello_world.py @@ -22,9 +22,5 @@ def LICENSE() -> str: b = BPF() b.load_and_attach() -if b.is_loaded() and b.is_attached(): - print("Successfully loaded and attached") -else: - print("Could not load successfully") # Now cat /sys/kernel/debug/tracing/trace_pipe to see results of the execve syscall. diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index 87c02308..aa7bf80e 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -19,6 +19,7 @@ class BPFHelperID(Enum): BPF_PRINTK = 6 BPF_GET_CURRENT_PID_TGID = 14 BPF_PERF_EVENT_OUTPUT = 25 + BPF_RINGBUF_RESERVE = 131 @HelperHandlerRegistry.register("ktime") @@ -181,6 +182,66 @@ def bpf_map_update_elem_emitter( return result, None +@HelperHandlerRegistry.register("reserve") +def bpf_ringbuf_reserve_emitter( + call, + map_ptr, + module, + builder, + func, + local_sym_tab=None, + struct_sym_tab=None, + local_var_metadata=None, +): + """ + Emit LLVM IR for bpf_ringbuf_reserve helper function call. + Expected call signature: ringbuf.reserve(size, flags=0) + """ + if not call.args or len(call.args) < 1 or len(call.args) > 2: + raise ValueError( + "Ringbuf reserve expects 1 or 2 args (size, flags), " + f"got {len(call.args)}" + ) + + # TODO: here, getting length of stuff does not actually work. need to fix this. + size_arg = call.args[0] + if isinstance(size_arg, ast.Constant): + size_val = ir.Constant(ir.IntType(64), size_arg.value) + elif isinstance(size_arg, ast.Name): + if size_arg.id not in local_sym_tab: + raise ValueError( + f"Variable '{size_arg.id}' not found in local symbol table" + ) + size_val = builder.load(local_sym_tab[size_arg.id]) + else: + raise NotImplementedError(f"Unsupported size argument type: {type(size_arg)}") + + flags_arg = call.args[1] if len(call.args) > 1 else None + flags_val = get_flags_val(flags_arg, builder, local_sym_tab) + + map_void_ptr = builder.bitcast(map_ptr, ir.PointerType()) + + # Args: (void* ringbuf, u64 size, u64 flags) + fn_type = ir.FunctionType( + ir.PointerType(), + [ir.PointerType(), ir.IntType(64), ir.IntType(64)], + var_arg=False, + ) + fn_ptr_type = ir.PointerType(fn_type) + + fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_RINGBUF_RESERVE.value) + fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) + + if isinstance(flags_val, int): + flags_const = ir.Constant(ir.IntType(64), flags_val) + else: + flags_const = flags_val + + result = builder.call(fn_ptr, [map_void_ptr, size_val, flags_const], tail=True) + + return result, ir.PointerType() + + @HelperHandlerRegistry.register("delete") def bpf_map_delete_elem_emitter( call, diff --git a/tests/c-form/ringbuf.bpf.c b/tests/c-form/ringbuf.bpf.c index f0abb911..88319f3b 100644 --- a/tests/c-form/ringbuf.bpf.c +++ b/tests/c-form/ringbuf.bpf.c @@ -28,22 +28,22 @@ int trace_execve(void *ctx) // 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); +// 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; } diff --git a/tests/passing_tests/ringbuf.py b/tests/passing_tests/ringbuf.py index 0566d855..8d335fe6 100644 --- a/tests/passing_tests/ringbuf.py +++ b/tests/passing_tests/ringbuf.py @@ -1,5 +1,5 @@ -from pythonbpf import bpf, BPF, map, bpfglobal, section, compile, compile_to_ir -from pythonbpf.maps import RingBuf, HashMap +from pythonbpf import bpf, map, bpfglobal, section, compile, compile_to_ir +from pythonbpf.maps import RingBuf from ctypes import c_int32, c_void_p @@ -9,17 +9,11 @@ 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") + e = mymap().reserve(16) return c_int32(0) @@ -31,5 +25,3 @@ def LICENSE() -> str: compile_to_ir("ringbuf.py", "ringbuf.ll") compile() -b = BPF() -b.load_and_attach() From e83215391a31063faa7a9f87473144277c971d98 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Thu, 2 Oct 2025 06:31:35 +0530 Subject: [PATCH 2/3] add ringbuf submit function. commit does not verify on input, but the mirror C code does not as well. --- pythonbpf/helper/bpf_helper_handler.py | 49 ++++++++++++++++++++++++++ tests/c-form/ringbuf.bpf.c | 18 +++++----- tests/passing_tests/ringbuf.py | 10 ++++-- 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index aa7bf80e..90021875 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -20,6 +20,7 @@ class BPFHelperID(Enum): BPF_GET_CURRENT_PID_TGID = 14 BPF_PERF_EVENT_OUTPUT = 25 BPF_RINGBUF_RESERVE = 131 + BPF_RINGBUF_SUBMIT = 132 @HelperHandlerRegistry.register("ktime") @@ -181,6 +182,54 @@ def bpf_map_update_elem_emitter( return result, None +@HelperHandlerRegistry.register("submit") +def bpf_ringbuf_submit_emitter( + call, + map_ptr, + module, + builder, + func, + local_sym_tab=None, + struct_sym_tab=None, + local_var_metadata=None, +): + """ + Emit LLVM IR for bpf_ringbuf_submit helper function call. + Expected call signature: ringbuf.submit(data, flags=0) + """ + if not call.args or len(call.args) < 1 or len(call.args) > 2: + raise ValueError( + "Ringbuf submit expects 1 or 2 args (data, flags), " + f"got {len(call.args)}" + ) + + data_arg = call.args[0] + data_ptr = get_or_create_ptr_from_arg(data_arg, builder, local_sym_tab) + + # Get flags argument (default to 0) + flags_arg = call.args[1] if len(call.args) > 1 else None + flags_val = get_flags_val(flags_arg, builder, local_sym_tab) + + # Returns: void + # Args: (void* data, u64 flags) + fn_type = ir.FunctionType( + ir.VoidType(), + [ir.PointerType(), ir.IntType(64)], + var_arg=False, + ) + fn_ptr_type = ir.PointerType(fn_type) + + fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_RINGBUF_SUBMIT.value) + fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) + + if isinstance(flags_val, int): + flags_const = ir.Constant(ir.IntType(64), flags_val) + else: + flags_const = flags_val + + builder.call(fn_ptr, [data_ptr, flags_const], tail=True) + + return None @HelperHandlerRegistry.register("reserve") def bpf_ringbuf_reserve_emitter( diff --git a/tests/c-form/ringbuf.bpf.c b/tests/c-form/ringbuf.bpf.c index 88319f3b..1a91ecbb 100644 --- a/tests/c-form/ringbuf.bpf.c +++ b/tests/c-form/ringbuf.bpf.c @@ -22,14 +22,14 @@ struct { SEC("tracepoint/syscalls/sys_enter_execve") int trace_execve(void *ctx) { - struct event *e; - __u64 pid_tgid; - __u64 uid_gid; - +// struct event *e; +// __u64 pid_tgid; +// __u64 uid_gid; + __u32 *e; // Reserve space in the ringbuffer e = bpf_ringbuf_reserve(&events, sizeof(*e), 0); -// if (!e) -// return 0; + if (!e) + return 0; // // // Fill the struct with data // pid_tgid = bpf_get_current_pid_tgid(); @@ -39,11 +39,13 @@ int trace_execve(void *ctx) // 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); + __u32 temp = 32; + e = &temp; + bpf_ringbuf_submit(e, 0); return 0; } diff --git a/tests/passing_tests/ringbuf.py b/tests/passing_tests/ringbuf.py index 8d335fe6..f4b71ff9 100644 --- a/tests/passing_tests/ringbuf.py +++ b/tests/passing_tests/ringbuf.py @@ -1,4 +1,4 @@ -from pythonbpf import bpf, map, bpfglobal, section, compile, compile_to_ir +from pythonbpf import bpf, map, bpfglobal, section, compile, compile_to_ir, BPF from pythonbpf.maps import RingBuf from ctypes import c_int32, c_void_p @@ -13,7 +13,9 @@ def mymap() -> RingBuf: @section("tracepoint/syscalls/sys_enter_clone") def random_section(ctx: c_void_p) -> c_int32: print("Hello") - e = mymap().reserve(16) + e = mymap().reserve(6) + if e: + mymap().submit(e) return c_int32(0) @@ -25,3 +27,7 @@ def LICENSE() -> str: compile_to_ir("ringbuf.py", "ringbuf.ll") compile() +b = BPF() +b.load_and_attach() +while True: + print("running") \ No newline at end of file From 83d9f4b34f6a724db133634befcff7c0586cf97b Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Thu, 2 Oct 2025 15:47:35 +0530 Subject: [PATCH 3/3] add failing test --- tests/failing_tests/condition_issue.py | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/failing_tests/condition_issue.py diff --git a/tests/failing_tests/condition_issue.py b/tests/failing_tests/condition_issue.py new file mode 100644 index 00000000..e32d2e3a --- /dev/null +++ b/tests/failing_tests/condition_issue.py @@ -0,0 +1,33 @@ +from pythonbpf import bpf, map, bpfglobal, section, compile, compile_to_ir, BPF +from pythonbpf.maps import RingBuf +from ctypes import c_int32, c_void_p + + +# Define a map +@bpf +@map +def mymap() -> RingBuf: + return RingBuf(max_entries=(1024)) + +@bpf +@section("tracepoint/syscalls/sys_enter_clone") +def random_section(ctx: c_void_p) -> c_int32: + e: c_int32 = mymap().reserve(64) + if e == 0: # here is the issue i think + return c_int32(0) + mymap().submit(e) + 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() +while True: + print("running")