From c1466a5bcabfbad3ab62ddb3919a191c0d22c5d1 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 30 Sep 2025 23:43:29 +0530 Subject: [PATCH 01/28] Add BPFHelperID enum to bpf_helper_handler --- pythonbpf/bpf_helper_handler.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pythonbpf/bpf_helper_handler.py b/pythonbpf/bpf_helper_handler.py index ed229cc..df02c8f 100644 --- a/pythonbpf/bpf_helper_handler.py +++ b/pythonbpf/bpf_helper_handler.py @@ -1,6 +1,17 @@ import ast from llvmlite import ir from .expr_pass import eval_expr +from enum import Enum + + +class BPFHelperID(Enum): + BPF_MAP_LOOKUP_ELEM = 1 + BPF_MAP_UPDATE_ELEM = 2 + BPF_MAP_DELETE_ELEM = 3 + BPF_KTIME_GET_NS = 5 + BPF_PRINTK = 6 + BPF_GET_CURRENT_PID_TGID = 14 + BPF_PERF_EVENT_OUTPUT = 25 def bpf_ktime_get_ns_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): From fa2ff0a242fed2b7e05bd8045783578051e3c520 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 30 Sep 2025 23:51:05 +0530 Subject: [PATCH 02/28] Use BPFHelperID Enums in bpf_helper_handler --- pythonbpf/bpf_helper_handler.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pythonbpf/bpf_helper_handler.py b/pythonbpf/bpf_helper_handler.py index df02c8f..cfce892 100644 --- a/pythonbpf/bpf_helper_handler.py +++ b/pythonbpf/bpf_helper_handler.py @@ -19,7 +19,7 @@ def bpf_ktime_get_ns_emitter(call, map_ptr, module, builder, func, local_sym_tab Emit LLVM IR for bpf_ktime_get_ns helper function call. """ # func is an arg to just have a uniform signature with other emitters - helper_id = ir.Constant(ir.IntType(64), 5) + helper_id = ir.Constant(ir.IntType(64), BPFHelperID.BPF_KTIME_GET_NS.value) fn_type = ir.FunctionType(ir.IntType(64), [], var_arg=False) fn_ptr_type = ir.PointerType(fn_type) fn_ptr = builder.inttoptr(helper_id, fn_ptr_type) @@ -66,7 +66,7 @@ def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, func, local_sym_ fn_ptr_type = ir.PointerType(fn_type) # Helper ID 1 is bpf_map_lookup_elem - fn_addr = ir.Constant(ir.IntType(64), 1) + fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_MAP_LOOKUP_ELEM.value) fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) result = builder.call(fn_ptr, [map_void_ptr, key_ptr], tail=False) @@ -225,7 +225,7 @@ def bpf_printk_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, fn_type = ir.FunctionType(ir.IntType( 64), [ir.PointerType(), ir.IntType(32)], var_arg=True) fn_ptr_type = ir.PointerType(fn_type) - fn_addr = ir.Constant(ir.IntType(64), 6) + fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_PRINTK.value) fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) builder.call(fn_ptr, [fmt_ptr, ir.Constant( @@ -315,7 +315,7 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, func, local_sym_ fn_ptr_type = ir.PointerType(fn_type) # helper id - fn_addr = ir.Constant(ir.IntType(64), 2) + fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_MAP_UPDATE_ELEM.value) fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) if isinstance(flags_val, int): @@ -375,7 +375,7 @@ def bpf_map_delete_elem_emitter(call, map_ptr, module, builder, func, local_sym_ fn_ptr_type = ir.PointerType(fn_type) # Helper ID 3 is bpf_map_delete_elem - fn_addr = ir.Constant(ir.IntType(64), 3) + fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_MAP_DELETE_ELEM.value) fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) # Call the helper function @@ -389,7 +389,7 @@ def bpf_get_current_pid_tgid_emitter(call, map_ptr, module, builder, func, local Emit LLVM IR for bpf_get_current_pid_tgid helper function call. """ # func is an arg to just have a uniform signature with other emitters - helper_id = ir.Constant(ir.IntType(64), 14) + helper_id = ir.Constant(ir.IntType(64), BPFHelperID.BPF_GET_CURRENT_PID_TGID.value) fn_type = ir.FunctionType(ir.IntType(64), [], var_arg=False) fn_ptr_type = ir.PointerType(fn_type) fn_ptr = builder.inttoptr(helper_id, fn_ptr_type) @@ -442,7 +442,7 @@ def bpf_perf_event_output_handler(call, map_ptr, module, builder, func, local_sy fn_ptr_type = ir.PointerType(fn_type) # helper id - fn_addr = ir.Constant(ir.IntType(64), 25) + fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_PERF_EVENT_OUTPUT.value) fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) result = builder.call( From 7e458645520dd27e15b38d232f8d1a8f1ec701c8 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 30 Sep 2025 23:57:31 +0530 Subject: [PATCH 03/28] Move helper scripts to a new dir, make temp fixes to allow this --- examples/struct_and_perf.py | 2 -- examples/sys_sync.py | 3 ++- pythonbpf/expr_pass.py | 2 +- pythonbpf/functions_pass.py | 2 +- pythonbpf/{ => helper}/bpf_helper_handler.py | 2 +- pythonbpf/{ => helper}/helpers.py | 0 6 files changed, 5 insertions(+), 6 deletions(-) rename pythonbpf/{ => helper}/bpf_helper_handler.py (99%) rename pythonbpf/{ => helper}/helpers.py (100%) diff --git a/examples/struct_and_perf.py b/examples/struct_and_perf.py index ed74258..abdb7f0 100644 --- a/examples/struct_and_perf.py +++ b/examples/struct_and_perf.py @@ -23,8 +23,6 @@ def events() -> PerfEventArray: @section("tracepoint/syscalls/sys_enter_clone") def hello(ctx: c_void_p) -> c_int32: dataobj = data_t() - ts = ktime() - process_id = pid() strobj = "hellohellohello" dataobj.pid = pid() dataobj.ts = ktime() diff --git a/examples/sys_sync.py b/examples/sys_sync.py index 953cb11..f8cf45b 100644 --- a/examples/sys_sync.py +++ b/examples/sys_sync.py @@ -1,5 +1,5 @@ from pythonbpf import bpf, map, section, bpfglobal, compile -from pythonbpf.helpers import ktime +from pythonbpf.helper.helpers import ktime from pythonbpf.maps import HashMap from ctypes import c_void_p, c_int64, c_uint64 @@ -10,6 +10,7 @@ # 3. Run the program with sudo: sudo tools/check.sh run examples/sys_sync.o # 4. Start a Python repl and `import os` and then keep entering `os.sync()` to see reponses. + @bpf @map def last() -> HashMap: diff --git a/pythonbpf/expr_pass.py b/pythonbpf/expr_pass.py index db3ffbb..1c497be 100644 --- a/pythonbpf/expr_pass.py +++ b/pythonbpf/expr_pass.py @@ -23,7 +23,7 @@ def eval_expr(func, module, builder, expr, local_sym_tab, map_sym_tab, structs_s return None elif isinstance(expr, ast.Call): # delayed import to avoid circular dependency - from .bpf_helper_handler import helper_func_list, handle_helper_call + from .helper.bpf_helper_handler import helper_func_list, handle_helper_call if isinstance(expr.func, ast.Name): # check deref diff --git a/pythonbpf/functions_pass.py b/pythonbpf/functions_pass.py index 0b99546..04e9c52 100644 --- a/pythonbpf/functions_pass.py +++ b/pythonbpf/functions_pass.py @@ -2,7 +2,7 @@ import ast -from .bpf_helper_handler import helper_func_list, handle_helper_call +from .helper.bpf_helper_handler import helper_func_list, handle_helper_call from .type_deducer import ctypes_to_ir from .binary_ops import handle_binary_op from .expr_pass import eval_expr, handle_expr diff --git a/pythonbpf/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py similarity index 99% rename from pythonbpf/bpf_helper_handler.py rename to pythonbpf/helper/bpf_helper_handler.py index cfce892..fe8fbe0 100644 --- a/pythonbpf/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -1,6 +1,6 @@ import ast from llvmlite import ir -from .expr_pass import eval_expr +from pythonbpf.expr_pass import eval_expr from enum import Enum diff --git a/pythonbpf/helpers.py b/pythonbpf/helper/helpers.py similarity index 100% rename from pythonbpf/helpers.py rename to pythonbpf/helper/helpers.py From 6cd07498fe8b56ed9c29c1dec7e630fea0ceeab0 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 03:06:55 +0530 Subject: [PATCH 04/28] Create HelperProcessorRegistry --- pythonbpf/helper/helper_utils.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 pythonbpf/helper/helper_utils.py diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py new file mode 100644 index 0000000..8ff7276 --- /dev/null +++ b/pythonbpf/helper/helper_utils.py @@ -0,0 +1,16 @@ +class HelperHandlerRegistry: + """Registry for BPF helpers""" + _handlers = {} + + @classmethod + def register(cls, helper_name): + """Decorator to register a handler function for a helper""" + def decorator(func): + cls._handlers[helper_name] = func + return func + return decorator + + @classmethod + def get_handler(cls, helper_name): + """Get the handler function for a helper""" + return cls._handlers.get(helper_name) From 61f6743f0a81479afb82f988e545d6994b965261 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 03:53:11 +0530 Subject: [PATCH 05/28] Use HelperHandleRegitry --- pythonbpf/expr_pass.py | 8 ++--- pythonbpf/functions_pass.py | 8 ++--- pythonbpf/helper/__init__.py | 2 ++ pythonbpf/helper/bpf_helper_handler.py | 48 ++++++++++++++------------ pythonbpf/{helper => }/helpers.py | 0 5 files changed, 36 insertions(+), 30 deletions(-) create mode 100644 pythonbpf/helper/__init__.py rename pythonbpf/{helper => }/helpers.py (100%) diff --git a/pythonbpf/expr_pass.py b/pythonbpf/expr_pass.py index 1c497be..abbba23 100644 --- a/pythonbpf/expr_pass.py +++ b/pythonbpf/expr_pass.py @@ -23,7 +23,7 @@ def eval_expr(func, module, builder, expr, local_sym_tab, map_sym_tab, structs_s return None elif isinstance(expr, ast.Call): # delayed import to avoid circular dependency - from .helper.bpf_helper_handler import helper_func_list, handle_helper_call + from pythonbpf.helper import HelperHandlerRegistry, handle_helper_call if isinstance(expr.func, ast.Name): # check deref @@ -50,21 +50,21 @@ def eval_expr(func, module, builder, expr, local_sym_tab, map_sym_tab, structs_s return val, local_sym_tab[expr.args[0].id][1] # check for helpers - if expr.func.id in helper_func_list: + if expr.func.id in HelperHandlerRegistry._handlers: return handle_helper_call( expr, module, builder, func, local_sym_tab, map_sym_tab, structs_sym_tab, local_var_metadata) elif isinstance(expr.func, ast.Attribute): print(f"Handling method call: {ast.dump(expr.func)}") if isinstance(expr.func.value, ast.Call) and isinstance(expr.func.value.func, ast.Name): method_name = expr.func.attr - if method_name in helper_func_list: + if method_name in HelperHandlerRegistry._handlers: return handle_helper_call( expr, module, builder, func, local_sym_tab, map_sym_tab, structs_sym_tab, local_var_metadata) elif isinstance(expr.func.value, ast.Name): obj_name = expr.func.value.id method_name = expr.func.attr if obj_name in map_sym_tab: - if method_name in helper_func_list: + if method_name in HelperHandlerRegistry._handlers: return handle_helper_call( expr, module, builder, func, local_sym_tab, map_sym_tab, structs_sym_tab, local_var_metadata) elif isinstance(expr, ast.Attribute): diff --git a/pythonbpf/functions_pass.py b/pythonbpf/functions_pass.py index 04e9c52..38f6349 100644 --- a/pythonbpf/functions_pass.py +++ b/pythonbpf/functions_pass.py @@ -2,7 +2,7 @@ import ast -from .helper.bpf_helper_handler import helper_func_list, handle_helper_call +from .helper import HelperHandlerRegistry, handle_helper_call from .type_deducer import ctypes_to_ir from .binary_ops import handle_binary_op from .expr_pass import eval_expr, handle_expr @@ -113,7 +113,7 @@ def handle_assign(func, module, builder, stmt, map_sym_tab, local_sym_tab, struc print(f"Assigned {call_type} constant " f"{rval.args[0].value} to {var_name}") # local_sym_tab[var_name] = var - elif call_type in helper_func_list: + elif call_type in HelperHandlerRegistry._handlers: # var = builder.alloca(ir.IntType(64), name=var_name) # var.align = 8 val = handle_helper_call( @@ -154,7 +154,7 @@ def handle_assign(func, module, builder, stmt, map_sym_tab, local_sym_tab, struc method_name = rval.func.attr if map_name in map_sym_tab: map_ptr = map_sym_tab[map_name] - if method_name in helper_func_list: + if method_name in HelperHandlerRegistry._handlers: val = handle_helper_call( rval, module, builder, func, local_sym_tab, map_sym_tab, structs_sym_tab, local_var_metadata) # var = builder.alloca(ir.IntType(64), name=var_name) @@ -344,7 +344,7 @@ def allocate_mem(module, builder, body, func, ret_type, map_sym_tab, local_sym_t var.align = ir_type.width // 8 print( f"Pre-allocated variable {var_name} of type {call_type}") - elif call_type in helper_func_list: + elif call_type in HelperHandlerRegistry._handlers: # Assume return type is int64 for now ir_type = ir.IntType(64) var = builder.alloca(ir_type, name=var_name) diff --git a/pythonbpf/helper/__init__.py b/pythonbpf/helper/__init__.py new file mode 100644 index 0000000..5e538d3 --- /dev/null +++ b/pythonbpf/helper/__init__.py @@ -0,0 +1,2 @@ +from .helper_utils import HelperHandlerRegistry +from .bpf_helper_handler import handle_helper_call diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index fe8fbe0..1a0f065 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -2,6 +2,7 @@ from llvmlite import ir from pythonbpf.expr_pass import eval_expr from enum import Enum +from .helper_utils import HelperHandlerRegistry class BPFHelperID(Enum): @@ -14,6 +15,7 @@ class BPFHelperID(Enum): BPF_PERF_EVENT_OUTPUT = 25 +@HelperHandlerRegistry.register("ktime") def bpf_ktime_get_ns_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): """ Emit LLVM IR for bpf_ktime_get_ns helper function call. @@ -27,6 +29,7 @@ def bpf_ktime_get_ns_emitter(call, map_ptr, module, builder, func, local_sym_tab return result, ir.IntType(64) +@HelperHandlerRegistry.register("lookup") def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): """ Emit LLVM IR for bpf_map_lookup_elem helper function call. @@ -66,7 +69,8 @@ def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, func, local_sym_ fn_ptr_type = ir.PointerType(fn_type) # Helper ID 1 is bpf_map_lookup_elem - fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_MAP_LOOKUP_ELEM.value) + fn_addr = ir.Constant(ir.IntType( + 64), BPFHelperID.BPF_MAP_LOOKUP_ELEM.value) fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) result = builder.call(fn_ptr, [map_void_ptr, key_ptr], tail=False) @@ -74,6 +78,7 @@ def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, func, local_sym_ return result, ir.PointerType() +@HelperHandlerRegistry.register("print") def bpf_printk_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): if not hasattr(func, "_fmt_counter"): func._fmt_counter = 0 @@ -233,6 +238,7 @@ def bpf_printk_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, return None +@HelperHandlerRegistry.register("update") def bpf_map_update_elem_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): """ Emit LLVM IR for bpf_map_update_elem helper function call. @@ -315,7 +321,8 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, func, local_sym_ fn_ptr_type = ir.PointerType(fn_type) # helper id - fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_MAP_UPDATE_ELEM.value) + fn_addr = ir.Constant(ir.IntType( + 64), BPFHelperID.BPF_MAP_UPDATE_ELEM.value) fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) if isinstance(flags_val, int): @@ -329,6 +336,7 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, func, local_sym_ return result, None +@HelperHandlerRegistry.register("delete") def bpf_map_delete_elem_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): """ Emit LLVM IR for bpf_map_delete_elem helper function call. @@ -375,7 +383,8 @@ def bpf_map_delete_elem_emitter(call, map_ptr, module, builder, func, local_sym_ fn_ptr_type = ir.PointerType(fn_type) # Helper ID 3 is bpf_map_delete_elem - fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_MAP_DELETE_ELEM.value) + fn_addr = ir.Constant(ir.IntType( + 64), BPFHelperID.BPF_MAP_DELETE_ELEM.value) fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) # Call the helper function @@ -384,12 +393,14 @@ def bpf_map_delete_elem_emitter(call, map_ptr, module, builder, func, local_sym_ return result, None +@HelperHandlerRegistry.register("pid") def bpf_get_current_pid_tgid_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): """ Emit LLVM IR for bpf_get_current_pid_tgid helper function call. """ # func is an arg to just have a uniform signature with other emitters - helper_id = ir.Constant(ir.IntType(64), BPFHelperID.BPF_GET_CURRENT_PID_TGID.value) + helper_id = ir.Constant(ir.IntType( + 64), BPFHelperID.BPF_GET_CURRENT_PID_TGID.value) fn_type = ir.FunctionType(ir.IntType(64), [], var_arg=False) fn_ptr_type = ir.PointerType(fn_type) fn_ptr = builder.inttoptr(helper_id, fn_ptr_type) @@ -442,7 +453,8 @@ def bpf_perf_event_output_handler(call, map_ptr, module, builder, func, local_sy fn_ptr_type = ir.PointerType(fn_type) # helper id - fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_PERF_EVENT_OUTPUT.value) + fn_addr = ir.Constant(ir.IntType( + 64), BPFHelperID.BPF_PERF_EVENT_OUTPUT.value) fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) result = builder.call( @@ -453,24 +465,14 @@ def bpf_perf_event_output_handler(call, map_ptr, module, builder, func, local_sy "Only simple object names are supported as data in perf event output.") -helper_func_list = { - "lookup": bpf_map_lookup_elem_emitter, - "print": bpf_printk_emitter, - "ktime": bpf_ktime_get_ns_emitter, - "update": bpf_map_update_elem_emitter, - "delete": bpf_map_delete_elem_emitter, - "pid": bpf_get_current_pid_tgid_emitter, - "output": bpf_perf_event_output_handler, -} - - def handle_helper_call(call, module, builder, func, local_sym_tab=None, map_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): print(local_var_metadata) if isinstance(call.func, ast.Name): func_name = call.func.id - if func_name in helper_func_list: + hdl_func = HelperHandlerRegistry.get_handler(func_name) + if hdl_func: # it is not a map method call - return helper_func_list[func_name](call, None, module, builder, func, local_sym_tab, struct_sym_tab, local_var_metadata) + return hdl_func(call, None, module, builder, func, local_sym_tab, struct_sym_tab, local_var_metadata) else: raise NotImplementedError( f"Function {func_name} is not implemented as a helper function.") @@ -481,9 +483,10 @@ def handle_helper_call(call, module, builder, func, local_sym_tab=None, map_sym_ method_name = call.func.attr if map_sym_tab and map_name in map_sym_tab: map_ptr = map_sym_tab[map_name] - if method_name in helper_func_list: + hdl_func = HelperHandlerRegistry.get_handler(method_name) + if hdl_func: print(local_var_metadata) - return helper_func_list[method_name]( + return hdl_func( call, map_ptr, module, builder, func, local_sym_tab, struct_sym_tab, local_var_metadata) else: raise NotImplementedError( @@ -496,8 +499,9 @@ def handle_helper_call(call, module, builder, func, local_sym_tab=None, map_sym_ method_name = call.func.attr if map_sym_tab and obj_name in map_sym_tab: map_ptr = map_sym_tab[obj_name] - if method_name in helper_func_list: - return helper_func_list[method_name]( + hdl_func = HelperHandlerRegistry.get_handler(method_name) + if hdl_func: + return hdl_func( call, map_ptr, module, builder, func, local_sym_tab, struct_sym_tab, local_var_metadata) else: raise NotImplementedError( diff --git a/pythonbpf/helper/helpers.py b/pythonbpf/helpers.py similarity index 100% rename from pythonbpf/helper/helpers.py rename to pythonbpf/helpers.py From 168ab29be3a0ad461b7ccd2c8d148a5849dc061d Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 04:04:32 +0530 Subject: [PATCH 06/28] Format function definitions in bpf_helper_handler --- pythonbpf/helper/bpf_helper_handler.py | 40 +++++++++++++++++++------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index 1a0f065..104078c 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -16,7 +16,9 @@ class BPFHelperID(Enum): @HelperHandlerRegistry.register("ktime") -def bpf_ktime_get_ns_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): +def bpf_ktime_get_ns_emitter(call, map_ptr, module, builder, func, + local_sym_tab=None, struct_sym_tab=None, + local_var_metadata=None): """ Emit LLVM IR for bpf_ktime_get_ns helper function call. """ @@ -30,7 +32,9 @@ def bpf_ktime_get_ns_emitter(call, map_ptr, module, builder, func, local_sym_tab @HelperHandlerRegistry.register("lookup") -def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): +def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, func, + local_sym_tab=None, struct_sym_tab=None, + local_var_metadata=None): """ Emit LLVM IR for bpf_map_lookup_elem helper function call. """ @@ -79,7 +83,9 @@ def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, func, local_sym_ @HelperHandlerRegistry.register("print") -def bpf_printk_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): +def bpf_printk_emitter(call, map_ptr, module, builder, func, + local_sym_tab=None, struct_sym_tab=None, + local_var_metadata=None): if not hasattr(func, "_fmt_counter"): func._fmt_counter = 0 @@ -122,7 +128,9 @@ def bpf_printk_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, f"Variable {value.value.id} not found in local symbol table.") elif isinstance(value.value, ast.Attribute): # object field access from struct - if isinstance(value.value.value, ast.Name) and local_sym_tab and value.value.value.id in local_sym_tab: + if (isinstance(value.value.value, ast.Name) and + local_sym_tab and + value.value.value.id in local_sym_tab): var_name = value.value.value.id field_name = value.value.attr if local_var_metadata and var_name in local_var_metadata: @@ -239,12 +247,16 @@ def bpf_printk_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, @HelperHandlerRegistry.register("update") -def bpf_map_update_elem_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): +def bpf_map_update_elem_emitter(call, map_ptr, module, builder, func, + local_sym_tab=None, struct_sym_tab=None, + local_var_metadata=None): """ Emit LLVM IR for bpf_map_update_elem helper function call. Expected call signature: map.update(key, value, flags=0) """ - if not call.args or len(call.args) < 2 or len(call.args) > 3: + if (not call.args or + len(call.args) < 2 or + len(call.args) > 3): raise ValueError("Map update expects 2 or 3 arguments (key, value, flags), got " f"{len(call.args)}") @@ -337,7 +349,9 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, func, local_sym_ @HelperHandlerRegistry.register("delete") -def bpf_map_delete_elem_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): +def bpf_map_delete_elem_emitter(call, map_ptr, module, builder, func, + local_sym_tab=None, struct_sym_tab=None, + local_var_metadata=None): """ Emit LLVM IR for bpf_map_delete_elem helper function call. Expected call signature: map.delete(key) @@ -394,7 +408,9 @@ def bpf_map_delete_elem_emitter(call, map_ptr, module, builder, func, local_sym_ @HelperHandlerRegistry.register("pid") -def bpf_get_current_pid_tgid_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): +def bpf_get_current_pid_tgid_emitter(call, map_ptr, module, builder, func, + local_sym_tab=None, struct_sym_tab=None, + local_var_metadata=None): """ Emit LLVM IR for bpf_get_current_pid_tgid helper function call. """ @@ -412,7 +428,9 @@ def bpf_get_current_pid_tgid_emitter(call, map_ptr, module, builder, func, local return pid, ir.IntType(64) -def bpf_perf_event_output_handler(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): +def bpf_perf_event_output_handler(call, map_ptr, module, builder, func, + local_sym_tab=None, struct_sym_tab=None, + local_var_metadata=None): if len(call.args) != 1: raise ValueError("Perf event output expects exactly one argument (data), got " f"{len(call.args)}") @@ -465,7 +483,9 @@ def bpf_perf_event_output_handler(call, map_ptr, module, builder, func, local_sy "Only simple object names are supported as data in perf event output.") -def handle_helper_call(call, module, builder, func, local_sym_tab=None, map_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): +def handle_helper_call(call, module, builder, func, + local_sym_tab=None, map_sym_tab=None, + struct_sym_tab=None, local_var_metadata=None): print(local_var_metadata) if isinstance(call.func, ast.Name): func_name = call.func.id From 244ea143d46cebe917c7afb06b880c677811982f Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 17:36:05 +0530 Subject: [PATCH 07/28] Refactor bpf_map_lookup_elem_emitter, add utils --- pythonbpf/helper/bpf_helper_handler.py | 29 ++----------------- pythonbpf/helper/helper_utils.py | 39 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index 104078c..d6301a3 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -2,7 +2,7 @@ from llvmlite import ir from pythonbpf.expr_pass import eval_expr from enum import Enum -from .helper_utils import HelperHandlerRegistry +from .helper_utils import HelperHandlerRegistry, get_key_ptr class BPFHelperID(Enum): @@ -38,31 +38,7 @@ def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, func, """ Emit LLVM IR for bpf_map_lookup_elem helper function call. """ - if call.args and len(call.args) != 1: - raise ValueError("Map lookup expects exactly one argument, got " - f"{len(call.args)}") - key_arg = call.args[0] - if isinstance(key_arg, ast.Name): - key_name = key_arg.id - if local_sym_tab and key_name in local_sym_tab: - key_ptr = local_sym_tab[key_name][0] - else: - raise ValueError( - f"Key variable {key_name} not found in local symbol table.") - elif isinstance(key_arg, ast.Constant) and isinstance(key_arg.value, int): - # handle constant integer keys - key_val = key_arg.value - key_type = ir.IntType(64) - key_ptr = builder.alloca(key_type) - key_ptr.align = key_type // 8 - builder.store(ir.Constant(key_type, key_val), key_ptr) - else: - raise NotImplementedError( - "Only simple variable names are supported as keys in map lookup.") - - if key_ptr is None: - raise ValueError("Key pointer is None.") - + key_ptr = get_key_ptr(call, builder, local_sym_tab) map_void_ptr = builder.bitcast(map_ptr, ir.PointerType()) fn_type = ir.FunctionType( @@ -72,7 +48,6 @@ def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, func, ) fn_ptr_type = ir.PointerType(fn_type) - # Helper ID 1 is bpf_map_lookup_elem fn_addr = ir.Constant(ir.IntType( 64), BPFHelperID.BPF_MAP_LOOKUP_ELEM.value) fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index 8ff7276..3594c3c 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -1,3 +1,7 @@ +import ast +from llvmlite import ir + + class HelperHandlerRegistry: """Registry for BPF helpers""" _handlers = {} @@ -14,3 +18,38 @@ def decorator(func): def get_handler(cls, helper_name): """Get the handler function for a helper""" return cls._handlers.get(helper_name) + + +def get_var_ptr_from_name(var_name, local_sym_tab): + """Get a pointer to a variable from the symbol table.""" + if local_sym_tab and var_name in local_sym_tab: + return local_sym_tab[var_name][0] + raise ValueError(f"Variable '{var_name}' not found in local symbol table") + + +def create_int_constant_ptr(value, builder, int_width=64): + """Create a pointer to an integer constant.""" + # Default to 64-bit integer + int_type = ir.IntType(int_width) + ptr = builder.alloca(int_type) + ptr.align = int_type.width // 8 + builder.store(ir.Constant(int_type, value), ptr) + return ptr + + +def get_key_ptr(call, builder, local_sym_tab): + """Extract key pointer from the call arguments.""" + if not call.args or len(call.args) != 1: + raise ValueError("Map lookup expects exactly one argument, got " + f"{len(call.args)}") + + key_arg = call.args[0] + + if isinstance(key_arg, ast.Name): + key_ptr = get_var_ptr_from_name(key_arg.id, local_sym_tab) + elif isinstance(key_arg, ast.Constant) and isinstance(key_arg.value, int): + key_ptr = create_int_constant_ptr(key_arg.value, builder) + else: + raise NotImplementedError( + "Only simple variable names are supported as keys in map lookup.") + return key_ptr From 4af6c4dcad6ae25c181d117f448bd70c10a2a63d Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 18:00:51 +0530 Subject: [PATCH 08/28] Refactor bpf_map_delete_elem_emitter --- pythonbpf/helper/bpf_helper_handler.py | 33 +------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index d6301a3..de55047 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -331,36 +331,7 @@ def bpf_map_delete_elem_emitter(call, map_ptr, module, builder, func, Emit LLVM IR for bpf_map_delete_elem helper function call. Expected call signature: map.delete(key) """ - # Check for correct number of arguments - if not call.args or len(call.args) != 1: - raise ValueError("Map delete expects exactly 1 argument (key), got " - f"{len(call.args)}") - - key_arg = call.args[0] - - # Handle key argument - if isinstance(key_arg, ast.Name): - key_name = key_arg.id - if local_sym_tab and key_name in local_sym_tab: - key_ptr = local_sym_tab[key_name][0] - else: - raise ValueError( - f"Key variable {key_name} not found in local symbol table.") - elif isinstance(key_arg, ast.Constant) and isinstance(key_arg.value, int): - # Handle constant integer keys - key_val = key_arg.value - key_type = ir.IntType(64) - key_ptr = builder.alloca(key_type) - key_ptr.align = key_type.width // 8 - builder.store(ir.Constant(key_type, key_val), key_ptr) - else: - raise NotImplementedError( - "Only simple variable names and integer constants are supported as keys in map delete.") - - if key_ptr is None: - raise ValueError("Key pointer is None.") - - # Cast map pointer to void* + key_ptr = get_key_ptr(call, builder, local_sym_tab) map_void_ptr = builder.bitcast(map_ptr, ir.PointerType()) # Define function type for bpf_map_delete_elem @@ -371,12 +342,10 @@ def bpf_map_delete_elem_emitter(call, map_ptr, module, builder, func, ) fn_ptr_type = ir.PointerType(fn_type) - # Helper ID 3 is bpf_map_delete_elem fn_addr = ir.Constant(ir.IntType( 64), BPFHelperID.BPF_MAP_DELETE_ELEM.value) fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) - # Call the helper function result = builder.call(fn_ptr, [map_void_ptr, key_ptr], tail=False) return result, None From d2e0f17ca821e57bbf8be356155e2fefd5a22e7b Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 18:14:09 +0530 Subject: [PATCH 09/28] Use key_arg instead of call in get_key_ptr --- pythonbpf/helper/helper_utils.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index 3594c3c..4b7cbbd 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -37,13 +37,8 @@ def create_int_constant_ptr(value, builder, int_width=64): return ptr -def get_key_ptr(call, builder, local_sym_tab): +def get_key_ptr(key_arg, builder, local_sym_tab): """Extract key pointer from the call arguments.""" - if not call.args or len(call.args) != 1: - raise ValueError("Map lookup expects exactly one argument, got " - f"{len(call.args)}") - - key_arg = call.args[0] if isinstance(key_arg, ast.Name): key_ptr = get_var_ptr_from_name(key_arg.id, local_sym_tab) From 7f6c3180692b67fec5a042c15c045cda894eab37 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 18:14:32 +0530 Subject: [PATCH 10/28] Use get_key_ptr in map_update helper --- pythonbpf/helper/bpf_helper_handler.py | 30 ++++++++------------------ 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index de55047..e0b8d3c 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -38,7 +38,10 @@ def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, func, """ Emit LLVM IR for bpf_map_lookup_elem helper function call. """ - key_ptr = get_key_ptr(call, builder, local_sym_tab) + if not call.args or len(call.args) != 1: + raise ValueError("Map lookup expects exactly one argument (key), got " + f"{len(call.args)}") + key_ptr = get_key_ptr(call.args[0], builder, local_sym_tab) map_void_ptr = builder.bitcast(map_ptr, ir.PointerType()) fn_type = ir.FunctionType( @@ -239,25 +242,7 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, func, value_arg = call.args[1] flags_arg = call.args[2] if len(call.args) > 2 else None - # Handle key - if isinstance(key_arg, ast.Name): - key_name = key_arg.id - if local_sym_tab and key_name in local_sym_tab: - key_ptr = local_sym_tab[key_name][0] - else: - raise ValueError( - f"Key variable {key_name} not found in local symbol table.") - elif isinstance(key_arg, ast.Constant) and isinstance(key_arg.value, int): - # Handle constant integer keys - key_val = key_arg.value - key_type = ir.IntType(64) - key_ptr = builder.alloca(key_type) - key_ptr.align = key_type.width // 8 - builder.store(ir.Constant(key_type, key_val), key_ptr) - else: - raise NotImplementedError( - "Only simple variable names and integer constants are supported as keys in map update.") - + key_ptr = get_key_ptr(key_arg, builder, local_sym_tab) # Handle value if isinstance(value_arg, ast.Name): value_name = value_arg.id @@ -331,7 +316,10 @@ def bpf_map_delete_elem_emitter(call, map_ptr, module, builder, func, Emit LLVM IR for bpf_map_delete_elem helper function call. Expected call signature: map.delete(key) """ - key_ptr = get_key_ptr(call, builder, local_sym_tab) + if not call.args or len(call.args) != 1: + raise ValueError("Map delete expects exactly one argument (key), got " + f"{len(call.args)}") + key_ptr = get_key_ptr(call.args[0], builder, local_sym_tab) map_void_ptr = builder.bitcast(map_ptr, ir.PointerType()) # Define function type for bpf_map_delete_elem From 8dd2746411b01a3d7876726b4862ddf913f0712a Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 18:21:42 +0530 Subject: [PATCH 11/28] rename get_key_ptr to get_or_create_ptr_from_arg --- pythonbpf/helper/helper_utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index 4b7cbbd..f3a89eb 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -37,14 +37,14 @@ def create_int_constant_ptr(value, builder, int_width=64): return ptr -def get_key_ptr(key_arg, builder, local_sym_tab): +def get_or_create_ptr_from_arg(arg, builder, local_sym_tab): """Extract key pointer from the call arguments.""" - if isinstance(key_arg, ast.Name): - key_ptr = get_var_ptr_from_name(key_arg.id, local_sym_tab) - elif isinstance(key_arg, ast.Constant) and isinstance(key_arg.value, int): - key_ptr = create_int_constant_ptr(key_arg.value, builder) + if isinstance(arg, ast.Name): + ptr = get_var_ptr_from_name(arg.id, local_sym_tab) + elif isinstance(arg, ast.Constant) and isinstance(arg.value, int): + ptr = create_int_constant_ptr(arg.value, builder) else: raise NotImplementedError( "Only simple variable names are supported as keys in map lookup.") - return key_ptr + return ptr From ffcd2de44d3dc690cda0420938d4cfdf5bcc7a3d Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 18:25:22 +0530 Subject: [PATCH 12/28] Replace usage of get_key_ptr with get_or_create_ptr_from_arg --- pythonbpf/helper/bpf_helper_handler.py | 27 +++++--------------------- pythonbpf/helper/helper_utils.py | 4 ++-- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index e0b8d3c..fc58a02 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -2,7 +2,7 @@ from llvmlite import ir from pythonbpf.expr_pass import eval_expr from enum import Enum -from .helper_utils import HelperHandlerRegistry, get_key_ptr +from .helper_utils import HelperHandlerRegistry, get_or_create_ptr_from_arg class BPFHelperID(Enum): @@ -41,7 +41,7 @@ def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, func, if not call.args or len(call.args) != 1: raise ValueError("Map lookup expects exactly one argument (key), got " f"{len(call.args)}") - key_ptr = get_key_ptr(call.args[0], builder, local_sym_tab) + key_ptr = get_or_create_ptr_from_arg(call.args[0], builder, local_sym_tab) map_void_ptr = builder.bitcast(map_ptr, ir.PointerType()) fn_type = ir.FunctionType( @@ -242,25 +242,8 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, func, value_arg = call.args[1] flags_arg = call.args[2] if len(call.args) > 2 else None - key_ptr = get_key_ptr(key_arg, builder, local_sym_tab) - # Handle value - if isinstance(value_arg, ast.Name): - value_name = value_arg.id - if local_sym_tab and value_name in local_sym_tab: - value_ptr = local_sym_tab[value_name][0] - else: - raise ValueError( - f"Value variable {value_name} not found in local symbol table.") - elif isinstance(value_arg, ast.Constant) and isinstance(value_arg.value, int): - # Handle constant integers - value_val = value_arg.value - value_type = ir.IntType(64) - value_ptr = builder.alloca(value_type) - value_ptr.align = value_type.width // 8 - builder.store(ir.Constant(value_type, value_val), value_ptr) - else: - raise NotImplementedError( - "Only simple variable names and integer constants are supported as values in map update.") + key_ptr = get_or_create_ptr_from_arg(key_arg, builder, local_sym_tab) + value_ptr = get_or_create_ptr_from_arg(value_arg, builder, local_sym_tab) # Handle flags argument (defaults to 0) if flags_arg is not None: @@ -319,7 +302,7 @@ def bpf_map_delete_elem_emitter(call, map_ptr, module, builder, func, if not call.args or len(call.args) != 1: raise ValueError("Map delete expects exactly one argument (key), got " f"{len(call.args)}") - key_ptr = get_key_ptr(call.args[0], builder, local_sym_tab) + key_ptr = get_or_create_ptr_from_arg(call.args[0], builder, local_sym_tab) map_void_ptr = builder.bitcast(map_ptr, ir.PointerType()) # Define function type for bpf_map_delete_elem diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index f3a89eb..9245f5b 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -38,7 +38,7 @@ def create_int_constant_ptr(value, builder, int_width=64): def get_or_create_ptr_from_arg(arg, builder, local_sym_tab): - """Extract key pointer from the call arguments.""" + """Extract or create pointer from the call arguments.""" if isinstance(arg, ast.Name): ptr = get_var_ptr_from_name(arg.id, local_sym_tab) @@ -46,5 +46,5 @@ def get_or_create_ptr_from_arg(arg, builder, local_sym_tab): ptr = create_int_constant_ptr(arg.value, builder) else: raise NotImplementedError( - "Only simple variable names are supported as keys in map lookup.") + "Only simple variable names are supported as args in map helpers.") return ptr From 8d9ff2df3bb354b7c569dc752f65fdf5d59f479d Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 18:28:40 +0530 Subject: [PATCH 13/28] Fix import in sys_sync example --- examples/sys_sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/sys_sync.py b/examples/sys_sync.py index f8cf45b..7020791 100644 --- a/examples/sys_sync.py +++ b/examples/sys_sync.py @@ -1,5 +1,5 @@ from pythonbpf import bpf, map, section, bpfglobal, compile -from pythonbpf.helper.helpers import ktime +from pythonbpf.helpers import ktime from pythonbpf.maps import HashMap from ctypes import c_void_p, c_int64, c_uint64 From 18f164bdeccc0887312b67669f03977280758332 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 18:35:11 +0530 Subject: [PATCH 14/28] Add get_flags_val to helper_utils --- pythonbpf/helper/helper_utils.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index 9245f5b..e093eb0 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -48,3 +48,22 @@ def get_or_create_ptr_from_arg(arg, builder, local_sym_tab): raise NotImplementedError( "Only simple variable names are supported as args in map helpers.") return ptr + + +def get_flags_val(arg, builder, local_sym_tab): + """Extract or create flags value from the call arguments.""" + if not arg: + return 0 + + if isinstance(arg, ast.Name): + if local_sym_tab and arg.id in local_sym_tab: + flags_ptr = local_sym_tab[arg.id][0] + return builder.load(flags_ptr) + else: + raise ValueError( + f"Variable '{arg.id}' not found in local symbol table") + elif isinstance(arg, ast.Constant) and isinstance(arg.value, int): + return arg.value + + raise NotImplementedError( + "Only simple variable names or integer constants are supported as flags in map helpers.") From 9c58116c8295ffcc2a5b6e175e2a1966857de402 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 18:38:18 +0530 Subject: [PATCH 15/28] Use get_flags_val in bpf_map_update_elem_emitter --- pythonbpf/helper/bpf_helper_handler.py | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index fc58a02..2c763b5 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -2,7 +2,7 @@ from llvmlite import ir from pythonbpf.expr_pass import eval_expr from enum import Enum -from .helper_utils import HelperHandlerRegistry, get_or_create_ptr_from_arg +from .helper_utils import HelperHandlerRegistry, get_or_create_ptr_from_arg, get_flags_val class BPFHelperID(Enum): @@ -244,28 +244,7 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, func, key_ptr = get_or_create_ptr_from_arg(key_arg, builder, local_sym_tab) value_ptr = get_or_create_ptr_from_arg(value_arg, builder, local_sym_tab) - - # Handle flags argument (defaults to 0) - if flags_arg is not None: - if isinstance(flags_arg, ast.Constant) and isinstance(flags_arg.value, int): - flags_val = flags_arg.value - elif isinstance(flags_arg, ast.Name): - flags_name = flags_arg.id - if local_sym_tab and flags_name in local_sym_tab: - # Assume it's a stored integer value, load it - flags_ptr = local_sym_tab[flags_name][0] - flags_val = builder.load(flags_ptr) - else: - raise ValueError( - f"Flags variable {flags_name} not found in local symbol table.") - else: - raise NotImplementedError( - "Only integer constants and simple variable names are supported as flags in map update.") - else: - flags_val = 0 - - if key_ptr is None or value_ptr is None: - raise ValueError("Key pointer or value pointer is None.") + flags_val = get_flags_val(flags_arg, builder, local_sym_tab) map_void_ptr = builder.bitcast(map_ptr, ir.PointerType()) fn_type = ir.FunctionType( @@ -275,7 +254,6 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, func, ) fn_ptr_type = ir.PointerType(fn_type) - # helper id fn_addr = ir.Constant(ir.IntType( 64), BPFHelperID.BPF_MAP_UPDATE_ELEM.value) fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) From d18c69fae17bcac5db4bee5f4a0dbfdf86811d02 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 19:56:20 +0530 Subject: [PATCH 16/28] Add _handle_fstring_print scaffolding --- pythonbpf/helper/helper_utils.py | 48 ++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index e093eb0..683c4f5 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -1,6 +1,9 @@ import ast +import logging from llvmlite import ir +logger = logging.getLogger(__name__) + class HelperHandlerRegistry: """Registry for BPF helpers""" @@ -67,3 +70,48 @@ def get_flags_val(arg, builder, local_sym_tab): raise NotImplementedError( "Only simple variable names or integer constants are supported as flags in map helpers.") + + +def _handle_fstring_print(joined_str, module, builder, func, + local_sym_tab=None, struct_sym_tab=None, + local_var_metadata=None): + """Handle f-string formatting for bpf_printk emitter.""" + fmt_parts = [] + exprs = [] + + for value in joined_str.values: + logger.debug(f"Processing f-string value: {ast.dump(value)}") + + if isinstance(value, ast.Constant): + _process_constant_in_fstring(value, fmt_parts, exprs) + elif isinstance(value, ast.FormattedValue): + _process_formatted_value(value, fmt_parts, exprs, + local_sym_tab, struct_sym_tab, + local_var_metadata) + + +def _process_constant_in_fstring(cst, fmt_parts, exprs): + """Process constant values in f-string.""" + if isinstance(cst.value, str): + fmt_parts.append(cst.value) + elif isinstance(cst.value, int): + fmt_parts.append("%lld") + exprs.append(ir.Constant(ir.IntType(64), cst.value)) + else: + raise NotImplementedError( + f"Unsupported constant type in f-string: {type(cst.value)}") + + +def _process_formatted_value(fval, fmt_parts, exprs, + local_sym_tab, struct_sym_tab, + local_var_metadata): + """Process formatted values in f-string.""" + logger.debug(f"Processing formatted value: {ast.dump(fval)}") + + if isinstance(fval.value, ast.Name): + pass + elif isinstance(fval.value, ast.Attribute): + pass + else: + raise NotImplementedError( + f"Unsupported formatted value type in f-string: {type(fval.value)}") From 17f60d721bb5a81d8dc4150b1e6baa05e0ad4e75 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 20:26:18 +0530 Subject: [PATCH 17/28] Add _process_*_in_fval to helper_utils --- pythonbpf/helper/helper_utils.py | 77 ++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 8 deletions(-) diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index 683c4f5..4b5534e 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -85,9 +85,9 @@ def _handle_fstring_print(joined_str, module, builder, func, if isinstance(value, ast.Constant): _process_constant_in_fstring(value, fmt_parts, exprs) elif isinstance(value, ast.FormattedValue): - _process_formatted_value(value, fmt_parts, exprs, - local_sym_tab, struct_sym_tab, - local_var_metadata) + _process_fval(value, fmt_parts, exprs, + local_sym_tab, struct_sym_tab, + local_var_metadata) def _process_constant_in_fstring(cst, fmt_parts, exprs): @@ -102,16 +102,77 @@ def _process_constant_in_fstring(cst, fmt_parts, exprs): f"Unsupported constant type in f-string: {type(cst.value)}") -def _process_formatted_value(fval, fmt_parts, exprs, - local_sym_tab, struct_sym_tab, - local_var_metadata): +def _process_fval(fval, fmt_parts, exprs, + local_sym_tab, struct_sym_tab, + local_var_metadata): """Process formatted values in f-string.""" logger.debug(f"Processing formatted value: {ast.dump(fval)}") if isinstance(fval.value, ast.Name): - pass + _process_name_in_fval(fval.value, fmt_parts, exprs, local_sym_tab) elif isinstance(fval.value, ast.Attribute): - pass + _process_attr_in_fval(fval.value, fmt_parts, exprs, + local_sym_tab, struct_sym_tab, + local_var_metadata) else: raise NotImplementedError( f"Unsupported formatted value type in f-string: {type(fval.value)}") + + +def _process_name_in_fval(name_node, fmt_parts, exprs, local_sym_tab): + """Process name nodes in formatted values.""" + if local_sym_tab and name_node.id in local_sym_tab: + _, var_type = local_sym_tab[name_node.id] + _populate_fval(var_type, name_node, fmt_parts, exprs) + + +def _process_attr_in_fval(attr_node, fmt_parts, exprs, + local_sym_tab, struct_sym_tab, + local_var_metadata): + """Process attribute nodes in formatted values.""" + if (isinstance(attr_node.value, ast.Name) and + local_sym_tab and attr_node.value.id in local_sym_tab): + var_name = attr_node.value.id + field_name = attr_node.attr + + if not local_var_metadata or var_name not in local_var_metadata: + raise ValueError( + f"Variable metadata for '{var_name}' not found in local variable metadata") + + var_type = local_sym_tab[var_name][1] + if var_type not in struct_sym_tab: + raise ValueError( + f"Struct type '{var_type}' for variable '{var_name}' not found in struct symbol table") + + struct_info = struct_sym_tab[var_type] + if field_name not in struct_info.fields: + raise ValueError( + f"Field '{field_name}' not found in struct '{var_type}'") + + field_type = struct_info.field_type(field_name) + _populate_fval(field_type, attr_node, fmt_parts, exprs) + else: + raise NotImplementedError( + "Only simple attribute access on local variables is supported in f-strings.") + + +def _populate_fval(ftype, node, fmt_parts, exprs): + """Populate format parts and expressions based on field type.""" + if isinstance(ftype, ir.IntType): + # TODO: We print as signed integers only for now + if ftype.width == 64: + fmt_parts.append("%lld") + exprs.append(node) + elif ftype.width == 32: + fmt_parts.append("%d") + exprs.append(node) + else: + raise NotImplementedError( + f"Unsupported integer width in f-string: {ftype.width}") + elif ftype == ir.PointerType(ir.IntType(8)): + # NOTE: We assume i8* is a string + fmt_parts.append("%s") + exprs.append(node) + else: + raise NotImplementedError( + f"Unsupported field type in f-string: {ftype}") From 7b01f1dde32ceb60cda539b6be8ef7ca379554aa Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 21:43:11 +0530 Subject: [PATCH 18/28] Complete helpers for fstrings in helper_utils --- pythonbpf/helper/helper_utils.py | 78 ++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index 4b5534e..20d152a 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -1,6 +1,7 @@ import ast import logging from llvmlite import ir +from pythonbpf.expr_pass import eval_expr logger = logging.getLogger(__name__) @@ -88,6 +89,28 @@ def _handle_fstring_print(joined_str, module, builder, func, _process_fval(value, fmt_parts, exprs, local_sym_tab, struct_sym_tab, local_var_metadata) + else: + raise NotImplementedError( + f"Unsupported f-string value type: {type(value)}") + + fmt_str = "".join(fmt_parts) + "\n\0" + fmt_ptr = _create_format_string_global(fmt_str, func, module, builder) + + args = [fmt_ptr, ir.Constant(ir.IntType(32), len(fmt_str))] + + # NOTE: Process expressions (limited to 3 due to BPF constraints) + if len(exprs) > 3: + logger.warn( + "bpf_printk supports up to 3 arguments, extra arguments will be ignored.") + + for expr in exprs[:3]: + arg_value = _prepare_expr_args(expr, func, module, builder, + local_sym_tab, struct_sym_tab, + local_var_metadata) + args.append(arg_value) + + # Call the BPF_PRINTK helper + return _call_bpf_printk_helper(args, builder) def _process_constant_in_fstring(cst, fmt_parts, exprs): @@ -176,3 +199,58 @@ def _populate_fval(ftype, node, fmt_parts, exprs): else: raise NotImplementedError( f"Unsupported field type in f-string: {ftype}") + + +def _create_format_string_global(fmt_str, func, module, builder): + """Create a global variable for the format string.""" + fmt_name = f"{func.name}____fmt{func._fmt_counter}" + func._fmt_counter += 1 + + fmt_gvar = ir.GlobalVariable( + module, ir.ArrayType(ir.IntType(8), len(fmt_str)), name=fmt_name) + fmt_gvar.global_constant = True + fmt_gvar.initializer = ir.Constant( + ir.ArrayType(ir.IntType(8), len(fmt_str)), + bytearray(fmt_str.encode("utf8")) + ) + fmt_gvar.linkage = "internal" + fmt_gvar.align = 1 + + return builder.bitcast(fmt_gvar, ir.PointerType()) + + +def _prepare_expr_args(expr, func, module, builder, + local_sym_tab, struct_sym_tab, + local_var_metadata): + """Evaluate and prepare an expression to be used as an argument for bpf_printk.""" + print(f"{ast.dump(expr)}") + val, _ = eval_expr(func, module, builder, expr, + local_sym_tab, None, struct_sym_tab, + local_var_metadata) + + if val: + if isinstance(val.type, ir.PointerType): + val = builder.ptrtoint(val, ir.IntType(64)) + elif isinstance(val.type, ir.IntType): + if val.type.width < 64: + val = builder.sext(val, ir.IntType(64)) + else: + logger.warn( + "Only int and ptr supported in bpf_printk arguments. Others default to 0.") + val = ir.Constant(ir.IntType(64), 0) + return val + else: + logger.warn( + "Failed to evaluate expression for bpf_printk argument. It will be converted to 0.") + return ir.Constant(ir.IntType(64), 0) + + +def _call_bpf_printk_helper(args, builder): + """Call the BPF_PRINTK helper function with the provided arguments.""" + fn_type = ir.FunctionType( + ir.IntType(64), [ir.PointerType(), ir.IntType(32)], var_arg=True) + fn_ptr_type = ir.PointerType(fn_type) + fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_PRINTK.value) + fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) + + return builder.call(fn_ptr, args, tail=True) From 6ccbab402f3f4320581ab1bce18f6755d9f50bd7 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 22:12:30 +0530 Subject: [PATCH 19/28] Complete printk refactor --- pythonbpf/helper/bpf_helper_handler.py | 175 ++++--------------------- pythonbpf/helper/helper_utils.py | 18 ++- 2 files changed, 35 insertions(+), 158 deletions(-) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index 2c763b5..6a0cf14 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -2,7 +2,7 @@ from llvmlite import ir from pythonbpf.expr_pass import eval_expr from enum import Enum -from .helper_utils import HelperHandlerRegistry, get_or_create_ptr_from_arg, get_flags_val +from .helper_utils import HelperHandlerRegistry, get_or_create_ptr_from_arg, get_flags_val, _handle_fstring_print, _simple_string_print class BPFHelperID(Enum): @@ -64,163 +64,34 @@ def bpf_map_lookup_elem_emitter(call, map_ptr, module, builder, func, def bpf_printk_emitter(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): + """Emit LLVM IR for bpf_printk helper function call.""" if not hasattr(func, "_fmt_counter"): func._fmt_counter = 0 if not call.args: - raise ValueError("print expects at least one argument") + raise ValueError( + "bpf_printk expects at least one argument (format string)") + args = [] if isinstance(call.args[0], ast.JoinedStr): - fmt_parts = [] - exprs = [] - - for value in call.args[0].values: - print("Value in f-string:", ast.dump(value)) - if isinstance(value, ast.Constant): - if isinstance(value.value, str): - fmt_parts.append(value.value) - elif isinstance(value.value, int): - fmt_parts.append("%lld") - exprs.append(ir.Constant(ir.IntType(64), value.value)) - else: - raise NotImplementedError( - "Only string and integer constants are supported in f-string.") - elif isinstance(value, ast.FormattedValue): - print("Formatted value:", ast.dump(value)) - # TODO: Dirty handling here, only checks for int or str - if isinstance(value.value, ast.Name): - if local_sym_tab and value.value.id in local_sym_tab: - var_ptr, var_type = local_sym_tab[value.value.id] - if isinstance(var_type, ir.IntType): - fmt_parts.append("%lld") - exprs.append(value.value) - elif var_type == ir.PointerType(ir.IntType(8)): - # Case with string - fmt_parts.append("%s") - exprs.append(value.value) - else: - raise NotImplementedError( - "Only integer and pointer types are supported in formatted values.") - else: - raise ValueError( - f"Variable {value.value.id} not found in local symbol table.") - elif isinstance(value.value, ast.Attribute): - # object field access from struct - if (isinstance(value.value.value, ast.Name) and - local_sym_tab and - value.value.value.id in local_sym_tab): - var_name = value.value.value.id - field_name = value.value.attr - if local_var_metadata and var_name in local_var_metadata: - var_type = local_var_metadata[var_name] - if var_type in struct_sym_tab: - struct_info = struct_sym_tab[var_type] - if field_name in struct_info.fields: - field_type = struct_info.field_type( - field_name) - if isinstance(field_type, ir.IntType): - fmt_parts.append("%lld") - exprs.append(value.value) - elif field_type == ir.PointerType(ir.IntType(8)): - fmt_parts.append("%s") - exprs.append(value.value) - else: - raise NotImplementedError( - "Only integer and pointer types are supported in formatted values.") - else: - raise ValueError( - f"Field {field_name} not found in struct {var_type}.") - else: - raise ValueError( - f"Struct type {var_type} for variable {var_name} not found in struct symbol table.") - else: - raise ValueError( - f"Metadata for variable {var_name} not found in local variable metadata.") - else: - raise ValueError( - f"Variable {value.value.value.id} not found in local symbol table.") - else: - raise NotImplementedError( - "Only simple variable names are supported in formatted values.") - else: - raise NotImplementedError( - "Unsupported value type in f-string.") - - fmt_str = "".join(fmt_parts) + "\n" + "\0" - fmt_name = f"{func.name}____fmt{func._fmt_counter}" - func._fmt_counter += 1 - - fmt_gvar = ir.GlobalVariable( - module, ir.ArrayType(ir.IntType(8), len(fmt_str)), name=fmt_name) - fmt_gvar.global_constant = True - fmt_gvar.initializer = ir.Constant( # type: ignore - ir.ArrayType(ir.IntType(8), len(fmt_str)), - bytearray(fmt_str.encode("utf8")) - ) - fmt_gvar.linkage = "internal" - fmt_gvar.align = 1 # type: ignore - - fmt_ptr = builder.bitcast(fmt_gvar, ir.PointerType()) - - args = [fmt_ptr, ir.Constant(ir.IntType(32), len(fmt_str))] - - # Only 3 args supported in bpf_printk - if len(exprs) > 3: - print( - "Warning: bpf_printk supports up to 3 arguments, extra arguments will be ignored.") - - for expr in exprs[:3]: - print(f"{ast.dump(expr)}") - val, _ = eval_expr(func, module, builder, - expr, local_sym_tab, None, struct_sym_tab, local_var_metadata) - if val: - if isinstance(val.type, ir.PointerType): - val = builder.ptrtoint(val, ir.IntType(64)) - elif isinstance(val.type, ir.IntType): - if val.type.width < 64: - val = builder.sext(val, ir.IntType(64)) - else: - print( - "Warning: Only integer and pointer types are supported in bpf_printk arguments. Others will be converted to 0.") - val = ir.Constant(ir.IntType(64), 0) - args.append(val) - else: - print( - "Warning: Failed to evaluate expression for bpf_printk argument. It will be converted to 0.") - args.append(ir.Constant(ir.IntType(64), 0)) - fn_type = ir.FunctionType(ir.IntType( - 64), [ir.PointerType(), ir.IntType(32)], var_arg=True) - fn_ptr_type = ir.PointerType(fn_type) - fn_addr = ir.Constant(ir.IntType(64), 6) - fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) - return builder.call(fn_ptr, args, tail=True) - - for arg in call.args: - if isinstance(arg, ast.Constant) and isinstance(arg.value, str): - fmt_str = arg.value + "\n" + "\0" - fmt_name = f"{func.name}____fmt{func._fmt_counter}" - func._fmt_counter += 1 - - fmt_gvar = ir.GlobalVariable( - module, ir.ArrayType(ir.IntType(8), len(fmt_str)), name=fmt_name) - fmt_gvar.global_constant = True - fmt_gvar.initializer = ir.Constant( # type: ignore - ir.ArrayType(ir.IntType(8), len(fmt_str)), - bytearray(fmt_str.encode("utf8")) - ) - fmt_gvar.linkage = "internal" - fmt_gvar.align = 1 # type: ignore - - fmt_ptr = builder.bitcast(fmt_gvar, ir.PointerType()) - - fn_type = ir.FunctionType(ir.IntType( - 64), [ir.PointerType(), ir.IntType(32)], var_arg=True) - fn_ptr_type = ir.PointerType(fn_type) - fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_PRINTK.value) - fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) - - builder.call(fn_ptr, [fmt_ptr, ir.Constant( - ir.IntType(32), len(fmt_str))], tail=True) + args = _handle_fstring_print(call.args[0], module, builder, func, + local_sym_tab, struct_sym_tab, + local_var_metadata) + elif isinstance(call.args[0], ast.Constant) and isinstance(call.args[0].value, str): + # TODO: We are onbly supporting single arguments for now. + # In case of multiple args, the first one will be taken. + args = _simple_string_print(call.args[0], module, builder, func) + else: + raise NotImplementedError( + "Only simple string literals or f-strings are supported in bpf_printk.") + + fn_type = ir.FunctionType( + ir.IntType(64), [ir.PointerType(), ir.IntType(32)], var_arg=True) + fn_ptr_type = ir.PointerType(fn_type) + fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_PRINTK.value) + fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) + + builder.call(fn_ptr, args, tail=True) return None diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index 20d152a..1d0fb73 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -73,6 +73,15 @@ def get_flags_val(arg, builder, local_sym_tab): "Only simple variable names or integer constants are supported as flags in map helpers.") +def _simple_string_print(string_value, module, builder, func): + """Emit code for a simple string print statement.""" + fmt_str = string_value + "\n\0" + fmt_ptr = _create_format_string_global(fmt_str, func, module, builder) + + args = [fmt_ptr, ir.Constant(ir.IntType(32), len(fmt_str))] + return args + + def _handle_fstring_print(joined_str, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): @@ -93,10 +102,8 @@ def _handle_fstring_print(joined_str, module, builder, func, raise NotImplementedError( f"Unsupported f-string value type: {type(value)}") - fmt_str = "".join(fmt_parts) + "\n\0" - fmt_ptr = _create_format_string_global(fmt_str, func, module, builder) - - args = [fmt_ptr, ir.Constant(ir.IntType(32), len(fmt_str))] + fmt_str = "".join(fmt_parts) + args = _simple_string_print(fmt_str, module, builder, func) # NOTE: Process expressions (limited to 3 due to BPF constraints) if len(exprs) > 3: @@ -109,8 +116,7 @@ def _handle_fstring_print(joined_str, module, builder, func, local_var_metadata) args.append(arg_value) - # Call the BPF_PRINTK helper - return _call_bpf_printk_helper(args, builder) + return args def _process_constant_in_fstring(cst, fmt_parts, exprs): From 4f33db206cde25004d3fecab2cedda36090f29c6 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 22:49:50 +0530 Subject: [PATCH 20/28] Refactor bpf_perf_event_output_emitter --- pythonbpf/helper/bpf_helper_handler.py | 71 ++++++++++---------------- pythonbpf/helper/helper_utils.py | 38 ++++++++++---- 2 files changed, 54 insertions(+), 55 deletions(-) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index 6a0cf14..5cf133f 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -2,7 +2,7 @@ from llvmlite import ir from pythonbpf.expr_pass import eval_expr from enum import Enum -from .helper_utils import HelperHandlerRegistry, get_or_create_ptr_from_arg, get_flags_val, _handle_fstring_print, _simple_string_print +from .helper_utils import HelperHandlerRegistry, get_or_create_ptr_from_arg, get_flags_val, _handle_fstring_print, _simple_string_print, _get_data_ptr_and_size class BPFHelperID(Enum): @@ -201,50 +201,31 @@ def bpf_perf_event_output_handler(call, map_ptr, module, builder, func, data_arg = call.args[0] ctx_ptr = func.args[0] # First argument to the function is ctx - if isinstance(data_arg, ast.Name): - data_name = data_arg.id - if local_sym_tab and data_name in local_sym_tab: - data_ptr = local_sym_tab[data_name][0] - else: - raise ValueError( - f"Data variable {data_name} not found in local symbol table.") - # Check is data_name is a struct - if local_var_metadata and data_name in local_var_metadata: - data_type = local_var_metadata[data_name] - if data_type in struct_sym_tab: - struct_info = struct_sym_tab[data_type] - size_val = ir.Constant(ir.IntType(64), struct_info.size) - else: - raise ValueError( - f"Struct type {data_type} for variable {data_name} not found in struct symbol table.") - else: - raise ValueError( - f"Metadata for variable {data_name} not found in local variable metadata.") - - # BPF_F_CURRENT_CPU is -1 in 32 bit - flags_val = ir.Constant(ir.IntType(64), 0xFFFFFFFF) - - map_void_ptr = builder.bitcast(map_ptr, ir.PointerType()) - data_void_ptr = builder.bitcast(data_ptr, ir.PointerType()) - fn_type = ir.FunctionType( - ir.IntType(64), - [ir.PointerType(ir.IntType(8)), ir.PointerType(), ir.IntType(64), - ir.PointerType(), ir.IntType(64)], - var_arg=False - ) - fn_ptr_type = ir.PointerType(fn_type) - - # helper id - fn_addr = ir.Constant(ir.IntType( - 64), BPFHelperID.BPF_PERF_EVENT_OUTPUT.value) - fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) - - result = builder.call( - fn_ptr, [ctx_ptr, map_void_ptr, flags_val, data_void_ptr, size_val], tail=False) - return result, None - else: - raise NotImplementedError( - "Only simple object names are supported as data in perf event output.") + data_ptr, size_val = _get_data_ptr_and_size(data_arg, local_sym_tab, + struct_sym_tab, + local_var_metadata) + + # BPF_F_CURRENT_CPU is -1 in 32 bit + flags_val = ir.Constant(ir.IntType(64), 0xFFFFFFFF) + + map_void_ptr = builder.bitcast(map_ptr, ir.PointerType()) + data_void_ptr = builder.bitcast(data_ptr, ir.PointerType()) + fn_type = ir.FunctionType( + ir.IntType(64), + [ir.PointerType(ir.IntType(8)), ir.PointerType(), ir.IntType(64), + ir.PointerType(), ir.IntType(64)], + var_arg=False + ) + fn_ptr_type = ir.PointerType(fn_type) + + # helper id + fn_addr = ir.Constant(ir.IntType(64), + BPFHelperID.BPF_PERF_EVENT_OUTPUT.value) + fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) + + result = builder.call( + fn_ptr, [ctx_ptr, map_void_ptr, flags_val, data_void_ptr, size_val], tail=False) + return result, None def handle_helper_call(call, module, builder, func, diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index 1d0fb73..5c9248e 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -168,7 +168,7 @@ def _process_attr_in_fval(attr_node, fmt_parts, exprs, raise ValueError( f"Variable metadata for '{var_name}' not found in local variable metadata") - var_type = local_sym_tab[var_name][1] + var_type = local_var_metadata[var_name] if var_type not in struct_sym_tab: raise ValueError( f"Struct type '{var_type}' for variable '{var_name}' not found in struct symbol table") @@ -251,12 +251,30 @@ def _prepare_expr_args(expr, func, module, builder, return ir.Constant(ir.IntType(64), 0) -def _call_bpf_printk_helper(args, builder): - """Call the BPF_PRINTK helper function with the provided arguments.""" - fn_type = ir.FunctionType( - ir.IntType(64), [ir.PointerType(), ir.IntType(32)], var_arg=True) - fn_ptr_type = ir.PointerType(fn_type) - fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_PRINTK.value) - fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) - - return builder.call(fn_ptr, args, tail=True) +def _get_data_ptr_and_size(data_arg, local_sym_tab, struct_sym_tab, + local_var_metadata): + """Extract data pointer and size information for perf event output.""" + if isinstance(data_arg, ast.Name): + data_name = data_arg.id + if local_sym_tab and data_name in local_sym_tab: + data_ptr = local_sym_tab[data_name][0] + else: + raise ValueError( + f"Data variable {data_name} not found in local symbol table.") + + # Check if data_name is a struct + if local_var_metadata and data_name in local_var_metadata: + data_type = local_var_metadata[data_name] + if data_type in struct_sym_tab: + struct_info = struct_sym_tab[data_type] + size_val = ir.Constant(ir.IntType(64), struct_info.size) + return data_ptr, size_val + else: + raise ValueError( + f"Struct type {data_type} for variable {data_name} not found in struct symbol table.") + else: + raise ValueError( + f"Metadata for variable {data_name} not found in local variable metadata.") + else: + raise NotImplementedError( + "Only simple object names are supported as data in perf event output.") From 99d6c193f68abedbe1baaa710dcd30cfc9274a83 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 22:53:19 +0530 Subject: [PATCH 21/28] Fix calling of _simple_string_print --- pythonbpf/helper/bpf_helper_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index 5cf133f..b2bb666 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -80,7 +80,7 @@ def bpf_printk_emitter(call, map_ptr, module, builder, func, elif isinstance(call.args[0], ast.Constant) and isinstance(call.args[0].value, str): # TODO: We are onbly supporting single arguments for now. # In case of multiple args, the first one will be taken. - args = _simple_string_print(call.args[0], module, builder, func) + args = _simple_string_print(call.args[0].value, module, builder, func) else: raise NotImplementedError( "Only simple string literals or f-strings are supported in bpf_printk.") From 28cc0c5eece4d78e3ff38054a3d07998d6cd8f5d Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 23:32:27 +0530 Subject: [PATCH 22/28] Refactor handle_helper_call --- pythonbpf/helper/bpf_helper_handler.py | 71 +++++++++++--------------- 1 file changed, 29 insertions(+), 42 deletions(-) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index b2bb666..06dde15 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -231,50 +231,37 @@ def bpf_perf_event_output_handler(call, map_ptr, module, builder, func, def handle_helper_call(call, module, builder, func, local_sym_tab=None, map_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): - print(local_var_metadata) - if isinstance(call.func, ast.Name): - func_name = call.func.id - hdl_func = HelperHandlerRegistry.get_handler(func_name) - if hdl_func: - # it is not a map method call - return hdl_func(call, None, module, builder, func, local_sym_tab, struct_sym_tab, local_var_metadata) - else: + """Process a BPF helper function call and emit the appropriate LLVM IR.""" + # Helper function to get map pointer and invoke handler + def invoke_helper(method_name, map_ptr=None): + handler = HelperHandlerRegistry.get_handler(method_name) + if not handler: raise NotImplementedError( - f"Function {func_name} is not implemented as a helper function.") + f"Helper function '{method_name}' is not implemented.") + return handler(call, map_ptr, module, builder, func, + local_sym_tab, struct_sym_tab, local_var_metadata) + + # Handle direct function calls (e.g., print(), ktime()) + if isinstance(call.func, ast.Name): + return invoke_helper(call.func.id) + + # Handle method calls (e.g., map.lookup(), map.update()) elif isinstance(call.func, ast.Attribute): - # likely a map method call - if isinstance(call.func.value, ast.Call) and isinstance(call.func.value.func, ast.Name): - map_name = call.func.value.func.id - method_name = call.func.attr - if map_sym_tab and map_name in map_sym_tab: - map_ptr = map_sym_tab[map_name] - hdl_func = HelperHandlerRegistry.get_handler(method_name) - if hdl_func: - print(local_var_metadata) - return hdl_func( - call, map_ptr, module, builder, func, local_sym_tab, struct_sym_tab, local_var_metadata) - else: - raise NotImplementedError( - f"Map method {method_name} is not implemented as a helper function.") - else: - raise ValueError( - f"Map variable {map_name} not found in symbol tables.") - elif isinstance(call.func.value, ast.Name): - obj_name = call.func.value.id - method_name = call.func.attr - if map_sym_tab and obj_name in map_sym_tab: - map_ptr = map_sym_tab[obj_name] - hdl_func = HelperHandlerRegistry.get_handler(method_name) - if hdl_func: - return hdl_func( - call, map_ptr, module, builder, func, local_sym_tab, struct_sym_tab, local_var_metadata) - else: - raise NotImplementedError( - f"Map method {method_name} is not implemented as a helper function.") - else: - raise ValueError( - f"Map variable {obj_name} not found in symbol tables.") + method_name = call.func.attr + value = call.func.value + + # Get map pointer from different styles of map access + if isinstance(value, ast.Call) and isinstance(value.func, ast.Name): + # Variable style: my_map.lookup(key) + map_name = value.func.id else: raise NotImplementedError( - "Attribute not supported for map method calls.") + f"Unsupported map access pattern: {ast.dump(value)}") + + # Verify map exists and get pointer + if not map_sym_tab or map_name not in map_sym_tab: + raise ValueError(f"Map '{map_name}' not found in symbol table") + + return invoke_helper(method_name, map_sym_tab[map_name]) + return None From 929eef31efee9099d146ac03dd1b1d3e2fdd6f0e Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 23:38:38 +0530 Subject: [PATCH 23/28] Add has_handler to HelperHandlerRegistry --- pythonbpf/expr_pass.py | 6 +++--- pythonbpf/functions_pass.py | 6 +++--- pythonbpf/helper/helper_utils.py | 5 +++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/pythonbpf/expr_pass.py b/pythonbpf/expr_pass.py index abbba23..685484d 100644 --- a/pythonbpf/expr_pass.py +++ b/pythonbpf/expr_pass.py @@ -50,21 +50,21 @@ def eval_expr(func, module, builder, expr, local_sym_tab, map_sym_tab, structs_s return val, local_sym_tab[expr.args[0].id][1] # check for helpers - if expr.func.id in HelperHandlerRegistry._handlers: + if HelperHandlerRegistry.has_handler(expr.func.id): return handle_helper_call( expr, module, builder, func, local_sym_tab, map_sym_tab, structs_sym_tab, local_var_metadata) elif isinstance(expr.func, ast.Attribute): print(f"Handling method call: {ast.dump(expr.func)}") if isinstance(expr.func.value, ast.Call) and isinstance(expr.func.value.func, ast.Name): method_name = expr.func.attr - if method_name in HelperHandlerRegistry._handlers: + if HelperHandlerRegistry.has_handler(method_name): return handle_helper_call( expr, module, builder, func, local_sym_tab, map_sym_tab, structs_sym_tab, local_var_metadata) elif isinstance(expr.func.value, ast.Name): obj_name = expr.func.value.id method_name = expr.func.attr if obj_name in map_sym_tab: - if method_name in HelperHandlerRegistry._handlers: + if HelperHandlerRegistry.has_handler(method_name): return handle_helper_call( expr, module, builder, func, local_sym_tab, map_sym_tab, structs_sym_tab, local_var_metadata) elif isinstance(expr, ast.Attribute): diff --git a/pythonbpf/functions_pass.py b/pythonbpf/functions_pass.py index 38f6349..e155666 100644 --- a/pythonbpf/functions_pass.py +++ b/pythonbpf/functions_pass.py @@ -113,7 +113,7 @@ def handle_assign(func, module, builder, stmt, map_sym_tab, local_sym_tab, struc print(f"Assigned {call_type} constant " f"{rval.args[0].value} to {var_name}") # local_sym_tab[var_name] = var - elif call_type in HelperHandlerRegistry._handlers: + elif HelperHandlerRegistry.has_handler(call_type): # var = builder.alloca(ir.IntType(64), name=var_name) # var.align = 8 val = handle_helper_call( @@ -154,7 +154,7 @@ def handle_assign(func, module, builder, stmt, map_sym_tab, local_sym_tab, struc method_name = rval.func.attr if map_name in map_sym_tab: map_ptr = map_sym_tab[map_name] - if method_name in HelperHandlerRegistry._handlers: + if HelperHandlerRegistry.has_handler(method_name): val = handle_helper_call( rval, module, builder, func, local_sym_tab, map_sym_tab, structs_sym_tab, local_var_metadata) # var = builder.alloca(ir.IntType(64), name=var_name) @@ -344,7 +344,7 @@ def allocate_mem(module, builder, body, func, ret_type, map_sym_tab, local_sym_t var.align = ir_type.width // 8 print( f"Pre-allocated variable {var_name} of type {call_type}") - elif call_type in HelperHandlerRegistry._handlers: + elif HelperHandlerRegistry.has_handler(call_type): # Assume return type is int64 for now ir_type = ir.IntType(64) var = builder.alloca(ir_type, name=var_name) diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index 5c9248e..89aa5ca 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -23,6 +23,11 @@ def get_handler(cls, helper_name): """Get the handler function for a helper""" return cls._handlers.get(helper_name) + @classmethod + def has_handler(cls, helper_name): + """Check if a handler function is registered for a helper""" + return helper_name in cls._handlers + def get_var_ptr_from_name(var_name, local_sym_tab): """Get a pointer to a variable from the symbol table.""" From cecf45061c882984524efd20568022d3c283da56 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 23:51:25 +0530 Subject: [PATCH 24/28] Fix line length nitpicks --- pythonbpf/helper/bpf_helper_handler.py | 37 +++++++++++++---------- pythonbpf/helper/helper_utils.py | 42 ++++++++++++++------------ 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index 06dde15..c9d2fc6 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -1,8 +1,10 @@ import ast from llvmlite import ir -from pythonbpf.expr_pass import eval_expr from enum import Enum -from .helper_utils import HelperHandlerRegistry, get_or_create_ptr_from_arg, get_flags_val, _handle_fstring_print, _simple_string_print, _get_data_ptr_and_size +from .helper_utils import (HelperHandlerRegistry, + get_or_create_ptr_from_arg, get_flags_val, + handle_fstring_print, simple_string_print, + get_data_ptr_and_size) class BPFHelperID(Enum): @@ -74,16 +76,17 @@ def bpf_printk_emitter(call, map_ptr, module, builder, func, args = [] if isinstance(call.args[0], ast.JoinedStr): - args = _handle_fstring_print(call.args[0], module, builder, func, - local_sym_tab, struct_sym_tab, - local_var_metadata) - elif isinstance(call.args[0], ast.Constant) and isinstance(call.args[0].value, str): + args = handle_fstring_print(call.args[0], module, builder, func, + local_sym_tab, struct_sym_tab, + local_var_metadata) + elif (isinstance(call.args[0], ast.Constant) and + isinstance(call.args[0].value, str)): # TODO: We are onbly supporting single arguments for now. # In case of multiple args, the first one will be taken. - args = _simple_string_print(call.args[0].value, module, builder, func) + args = simple_string_print(call.args[0].value, module, builder, func) else: raise NotImplementedError( - "Only simple string literals or f-strings are supported in bpf_printk.") + "Only simple strings or f-strings are supported in bpf_printk.") fn_type = ir.FunctionType( ir.IntType(64), [ir.PointerType(), ir.IntType(32)], var_arg=True) @@ -106,8 +109,8 @@ def bpf_map_update_elem_emitter(call, map_ptr, module, builder, func, if (not call.args or len(call.args) < 2 or len(call.args) > 3): - raise ValueError("Map update expects 2 or 3 arguments (key, value, flags), got " - f"{len(call.args)}") + raise ValueError("Map update expects 2 or 3 args (key, value, flags), " + f"got {len(call.args)}") key_arg = call.args[0] value_arg = call.args[1] @@ -196,14 +199,14 @@ def bpf_perf_event_output_handler(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): if len(call.args) != 1: - raise ValueError("Perf event output expects exactly one argument (data), got " - f"{len(call.args)}") + raise ValueError("Perf event output expects exactly one argument, " + f"got {len(call.args)}") data_arg = call.args[0] ctx_ptr = func.args[0] # First argument to the function is ctx - data_ptr, size_val = _get_data_ptr_and_size(data_arg, local_sym_tab, - struct_sym_tab, - local_var_metadata) + data_ptr, size_val = get_data_ptr_and_size(data_arg, local_sym_tab, + struct_sym_tab, + local_var_metadata) # BPF_F_CURRENT_CPU is -1 in 32 bit flags_val = ir.Constant(ir.IntType(64), 0xFFFFFFFF) @@ -224,7 +227,9 @@ def bpf_perf_event_output_handler(call, map_ptr, module, builder, func, fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type) result = builder.call( - fn_ptr, [ctx_ptr, map_void_ptr, flags_val, data_void_ptr, size_val], tail=False) + fn_ptr, + [ctx_ptr, map_void_ptr, flags_val, data_void_ptr, size_val], + tail=False) return result, None diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index 89aa5ca..a75b0c6 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -75,10 +75,10 @@ def get_flags_val(arg, builder, local_sym_tab): return arg.value raise NotImplementedError( - "Only simple variable names or integer constants are supported as flags in map helpers.") + "Only var names or int consts are supported as map helpers flags.") -def _simple_string_print(string_value, module, builder, func): +def simple_string_print(string_value, module, builder, func): """Emit code for a simple string print statement.""" fmt_str = string_value + "\n\0" fmt_ptr = _create_format_string_global(fmt_str, func, module, builder) @@ -87,9 +87,9 @@ def _simple_string_print(string_value, module, builder, func): return args -def _handle_fstring_print(joined_str, module, builder, func, - local_sym_tab=None, struct_sym_tab=None, - local_var_metadata=None): +def handle_fstring_print(joined_str, module, builder, func, + local_sym_tab=None, struct_sym_tab=None, + local_var_metadata=None): """Handle f-string formatting for bpf_printk emitter.""" fmt_parts = [] exprs = [] @@ -108,12 +108,12 @@ def _handle_fstring_print(joined_str, module, builder, func, f"Unsupported f-string value type: {type(value)}") fmt_str = "".join(fmt_parts) - args = _simple_string_print(fmt_str, module, builder, func) + args = simple_string_print(fmt_str, module, builder, func) # NOTE: Process expressions (limited to 3 due to BPF constraints) if len(exprs) > 3: logger.warn( - "bpf_printk supports up to 3 arguments, extra arguments will be ignored.") + "bpf_printk supports up to 3 args, extra args will be ignored.") for expr in exprs[:3]: arg_value = _prepare_expr_args(expr, func, module, builder, @@ -150,7 +150,7 @@ def _process_fval(fval, fmt_parts, exprs, local_var_metadata) else: raise NotImplementedError( - f"Unsupported formatted value type in f-string: {type(fval.value)}") + f"Unsupported formatted value in f-string: {type(fval.value)}") def _process_name_in_fval(name_node, fmt_parts, exprs, local_sym_tab): @@ -171,12 +171,12 @@ def _process_attr_in_fval(attr_node, fmt_parts, exprs, if not local_var_metadata or var_name not in local_var_metadata: raise ValueError( - f"Variable metadata for '{var_name}' not found in local variable metadata") + f"Metadata for '{var_name}' not found in local var metadata") var_type = local_var_metadata[var_name] if var_type not in struct_sym_tab: raise ValueError( - f"Struct type '{var_type}' for variable '{var_name}' not found in struct symbol table") + f"Struct '{var_type}' for '{var_name}' not in symbol table") struct_info = struct_sym_tab[var_type] if field_name not in struct_info.fields: @@ -187,7 +187,7 @@ def _process_attr_in_fval(attr_node, fmt_parts, exprs, _populate_fval(field_type, attr_node, fmt_parts, exprs) else: raise NotImplementedError( - "Only simple attribute access on local variables is supported in f-strings.") + "Only simple attribute on local vars is supported in f-strings.") def _populate_fval(ftype, node, fmt_parts, exprs): @@ -233,7 +233,7 @@ def _create_format_string_global(fmt_str, func, module, builder): def _prepare_expr_args(expr, func, module, builder, local_sym_tab, struct_sym_tab, local_var_metadata): - """Evaluate and prepare an expression to be used as an argument for bpf_printk.""" + """Evaluate and prepare an expression to use as an arg for bpf_printk.""" print(f"{ast.dump(expr)}") val, _ = eval_expr(func, module, builder, expr, local_sym_tab, None, struct_sym_tab, @@ -247,17 +247,19 @@ def _prepare_expr_args(expr, func, module, builder, val = builder.sext(val, ir.IntType(64)) else: logger.warn( - "Only int and ptr supported in bpf_printk arguments. Others default to 0.") + "Only int and ptr supported in bpf_printk args. " + "Others default to 0.") val = ir.Constant(ir.IntType(64), 0) return val else: logger.warn( - "Failed to evaluate expression for bpf_printk argument. It will be converted to 0.") + "Failed to evaluate expression for bpf_printk argument. " + "It will be converted to 0.") return ir.Constant(ir.IntType(64), 0) -def _get_data_ptr_and_size(data_arg, local_sym_tab, struct_sym_tab, - local_var_metadata): +def get_data_ptr_and_size(data_arg, local_sym_tab, struct_sym_tab, + local_var_metadata): """Extract data pointer and size information for perf event output.""" if isinstance(data_arg, ast.Name): data_name = data_arg.id @@ -276,10 +278,12 @@ def _get_data_ptr_and_size(data_arg, local_sym_tab, struct_sym_tab, return data_ptr, size_val else: raise ValueError( - f"Struct type {data_type} for variable {data_name} not found in struct symbol table.") + f"Struct {data_type} for {data_name} not in symbol table.") else: raise ValueError( - f"Metadata for variable {data_name} not found in local variable metadata.") + f"Metadata for variable {data_name} " + "not found in local variable metadata.") else: raise NotImplementedError( - "Only simple object names are supported as data in perf event output.") + "Only simple object names are supported " + "as data in perf event output.") From 9099b3eaec81ae8facb0d8700d7547cb00484804 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 23:55:16 +0530 Subject: [PATCH 25/28] Replace logger.warn with logger.warning --- pythonbpf/helper/helper_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index a75b0c6..108c2a0 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -112,7 +112,7 @@ def handle_fstring_print(joined_str, module, builder, func, # NOTE: Process expressions (limited to 3 due to BPF constraints) if len(exprs) > 3: - logger.warn( + logger.warning( "bpf_printk supports up to 3 args, extra args will be ignored.") for expr in exprs[:3]: @@ -246,13 +246,13 @@ def _prepare_expr_args(expr, func, module, builder, if val.type.width < 64: val = builder.sext(val, ir.IntType(64)) else: - logger.warn( + logger.warning( "Only int and ptr supported in bpf_printk args. " "Others default to 0.") val = ir.Constant(ir.IntType(64), 0) return val else: - logger.warn( + logger.warning( "Failed to evaluate expression for bpf_printk argument. " "It will be converted to 0.") return ir.Constant(ir.IntType(64), 0) From ba3e02052de75aa491b2b246f81261e050371c8f Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 23:56:16 +0530 Subject: [PATCH 26/28] Register output in HelperHandlerRegistry --- pythonbpf/helper/bpf_helper_handler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index c9d2fc6..caa00fa 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -195,6 +195,7 @@ def bpf_get_current_pid_tgid_emitter(call, map_ptr, module, builder, func, return pid, ir.IntType(64) +@HelperHandlerRegistry.register("output") def bpf_perf_event_output_handler(call, map_ptr, module, builder, func, local_sym_tab=None, struct_sym_tab=None, local_var_metadata=None): From 690ff7ffbcd230395c3d8e3cbe26970df55ffce3 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 23:57:22 +0530 Subject: [PATCH 27/28] Remove unnecessary prints --- pythonbpf/helper/bpf_helper_handler.py | 2 +- pythonbpf/helper/helper_utils.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pythonbpf/helper/bpf_helper_handler.py b/pythonbpf/helper/bpf_helper_handler.py index caa00fa..a24d9ee 100644 --- a/pythonbpf/helper/bpf_helper_handler.py +++ b/pythonbpf/helper/bpf_helper_handler.py @@ -81,7 +81,7 @@ def bpf_printk_emitter(call, map_ptr, module, builder, func, local_var_metadata) elif (isinstance(call.args[0], ast.Constant) and isinstance(call.args[0].value, str)): - # TODO: We are onbly supporting single arguments for now. + # TODO: We are only supporting single arguments for now. # In case of multiple args, the first one will be taken. args = simple_string_print(call.args[0].value, module, builder, func) else: diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index 108c2a0..4bb504f 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -234,7 +234,6 @@ def _prepare_expr_args(expr, func, module, builder, local_sym_tab, struct_sym_tab, local_var_metadata): """Evaluate and prepare an expression to use as an arg for bpf_printk.""" - print(f"{ast.dump(expr)}") val, _ = eval_expr(func, module, builder, expr, local_sym_tab, None, struct_sym_tab, local_var_metadata) From 81807ace345ed692635910644672d7e8dd65165d Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 1 Oct 2025 23:59:07 +0530 Subject: [PATCH 28/28] Fix simple_string_print docstring --- pythonbpf/helper/helper_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonbpf/helper/helper_utils.py b/pythonbpf/helper/helper_utils.py index 4bb504f..adb766e 100644 --- a/pythonbpf/helper/helper_utils.py +++ b/pythonbpf/helper/helper_utils.py @@ -79,7 +79,7 @@ def get_flags_val(arg, builder, local_sym_tab): def simple_string_print(string_value, module, builder, func): - """Emit code for a simple string print statement.""" + """Prepare arguments for bpf_printk from a simple string value""" fmt_str = string_value + "\n\0" fmt_ptr = _create_format_string_global(fmt_str, func, module, builder)