From ce7b170feacb82e88766363ebb95b58412933a97 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Wed, 15 Oct 2025 18:19:51 +0530 Subject: [PATCH 01/12] float vmlinux_assignments_symtab --- pythonbpf/codegen.py | 4 ++-- pythonbpf/functions/functions_pass.py | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pythonbpf/codegen.py b/pythonbpf/codegen.py index 5db9f880..6044e562 100644 --- a/pythonbpf/codegen.py +++ b/pythonbpf/codegen.py @@ -45,14 +45,14 @@ def processor(source_code, filename, module): for func_node in bpf_chunks: logger.info(f"Found BPF function/struct: {func_node.name}") - vmlinux_proc(tree, module) + vmlinux_assignments_symtab = vmlinux_proc(tree, module) populate_global_symbol_table(tree, module) license_processing(tree, module) globals_processing(tree, module) structs_sym_tab = structs_proc(tree, module, bpf_chunks) map_sym_tab = maps_proc(tree, module, bpf_chunks) - func_proc(tree, module, bpf_chunks, map_sym_tab, structs_sym_tab) + func_proc(tree, module, bpf_chunks, map_sym_tab, structs_sym_tab, vmlinux_assignments_symtab) globals_list_creation(tree, module) diff --git a/pythonbpf/functions/functions_pass.py b/pythonbpf/functions/functions_pass.py index 8d0bce1e..647fb414 100644 --- a/pythonbpf/functions/functions_pass.py +++ b/pythonbpf/functions/functions_pass.py @@ -311,7 +311,7 @@ def process_stmt( def process_func_body( - module, builder, func_node, func, ret_type, map_sym_tab, structs_sym_tab + module, builder, func_node, func, ret_type, map_sym_tab, structs_sym_tab, vmlinux_assignments_symtab ): """Process the body of a bpf function""" # TODO: A lot. We just have print -> bpf_trace_printk for now @@ -350,7 +350,7 @@ def process_func_body( builder.ret(ir.Constant(ir.IntType(64), 0)) -def process_bpf_chunk(func_node, module, return_type, map_sym_tab, structs_sym_tab): +def process_bpf_chunk(func_node, module, return_type, map_sym_tab, structs_sym_tab, vmlinux_assignments_symtab): """Process a single BPF chunk (function) and emit corresponding LLVM IR.""" func_name = func_node.name @@ -384,7 +384,7 @@ def process_bpf_chunk(func_node, module, return_type, map_sym_tab, structs_sym_t builder = ir.IRBuilder(block) process_func_body( - module, builder, func_node, func, ret_type, map_sym_tab, structs_sym_tab + module, builder, func_node, func, ret_type, map_sym_tab, structs_sym_tab, vmlinux_assignments_symtab ) return func @@ -394,7 +394,7 @@ def process_bpf_chunk(func_node, module, return_type, map_sym_tab, structs_sym_t # ============================================================================ -def func_proc(tree, module, chunks, map_sym_tab, structs_sym_tab): +def func_proc(tree, module, chunks, map_sym_tab, structs_sym_tab, vmlinux_assignments_symtab): for func_node in chunks: if is_global_function(func_node): continue @@ -407,6 +407,7 @@ def func_proc(tree, module, chunks, map_sym_tab, structs_sym_tab): ctypes_to_ir(infer_return_type(func_node)), map_sym_tab, structs_sym_tab, + vmlinux_assignments_symtab ) From eb4ee64ee579dd533ee375b365d6306ba6d31cdc Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Wed, 15 Oct 2025 19:11:53 +0530 Subject: [PATCH 02/12] Revert "float vmlinux_assignments_symtab" This reverts commit ce7b170feacb82e88766363ebb95b58412933a97. --- pythonbpf/codegen.py | 4 ++-- pythonbpf/functions/functions_pass.py | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pythonbpf/codegen.py b/pythonbpf/codegen.py index 6044e562..5db9f880 100644 --- a/pythonbpf/codegen.py +++ b/pythonbpf/codegen.py @@ -45,14 +45,14 @@ def processor(source_code, filename, module): for func_node in bpf_chunks: logger.info(f"Found BPF function/struct: {func_node.name}") - vmlinux_assignments_symtab = vmlinux_proc(tree, module) + vmlinux_proc(tree, module) populate_global_symbol_table(tree, module) license_processing(tree, module) globals_processing(tree, module) structs_sym_tab = structs_proc(tree, module, bpf_chunks) map_sym_tab = maps_proc(tree, module, bpf_chunks) - func_proc(tree, module, bpf_chunks, map_sym_tab, structs_sym_tab, vmlinux_assignments_symtab) + func_proc(tree, module, bpf_chunks, map_sym_tab, structs_sym_tab) globals_list_creation(tree, module) diff --git a/pythonbpf/functions/functions_pass.py b/pythonbpf/functions/functions_pass.py index 647fb414..8d0bce1e 100644 --- a/pythonbpf/functions/functions_pass.py +++ b/pythonbpf/functions/functions_pass.py @@ -311,7 +311,7 @@ def process_stmt( def process_func_body( - module, builder, func_node, func, ret_type, map_sym_tab, structs_sym_tab, vmlinux_assignments_symtab + module, builder, func_node, func, ret_type, map_sym_tab, structs_sym_tab ): """Process the body of a bpf function""" # TODO: A lot. We just have print -> bpf_trace_printk for now @@ -350,7 +350,7 @@ def process_func_body( builder.ret(ir.Constant(ir.IntType(64), 0)) -def process_bpf_chunk(func_node, module, return_type, map_sym_tab, structs_sym_tab, vmlinux_assignments_symtab): +def process_bpf_chunk(func_node, module, return_type, map_sym_tab, structs_sym_tab): """Process a single BPF chunk (function) and emit corresponding LLVM IR.""" func_name = func_node.name @@ -384,7 +384,7 @@ def process_bpf_chunk(func_node, module, return_type, map_sym_tab, structs_sym_t builder = ir.IRBuilder(block) process_func_body( - module, builder, func_node, func, ret_type, map_sym_tab, structs_sym_tab, vmlinux_assignments_symtab + module, builder, func_node, func, ret_type, map_sym_tab, structs_sym_tab ) return func @@ -394,7 +394,7 @@ def process_bpf_chunk(func_node, module, return_type, map_sym_tab, structs_sym_t # ============================================================================ -def func_proc(tree, module, chunks, map_sym_tab, structs_sym_tab, vmlinux_assignments_symtab): +def func_proc(tree, module, chunks, map_sym_tab, structs_sym_tab): for func_node in chunks: if is_global_function(func_node): continue @@ -407,7 +407,6 @@ def func_proc(tree, module, chunks, map_sym_tab, structs_sym_tab, vmlinux_assign ctypes_to_ir(infer_return_type(func_node)), map_sym_tab, structs_sym_tab, - vmlinux_assignments_symtab ) From 8372111616ce5a117acfa2571577e4ac31fe977a Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Wed, 15 Oct 2025 21:25:53 +0530 Subject: [PATCH 03/12] add basic IR gen strategy --- pythonbpf/codegen.py | 14 +++++- .../vmlinux_parser/ir_gen/debug_info_gen.py | 15 ++++++ .../vmlinux_parser/ir_gen/ir_generation.py | 50 +++++++++++++++---- 3 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 pythonbpf/vmlinux_parser/ir_gen/debug_info_gen.py diff --git a/pythonbpf/codegen.py b/pythonbpf/codegen.py index 5db9f880..8d25644b 100644 --- a/pythonbpf/codegen.py +++ b/pythonbpf/codegen.py @@ -19,12 +19,22 @@ import tempfile from logging import Logger import logging +import re logger: Logger = logging.getLogger(__name__) VERSION = "v0.1.4" +def finalize_module(original_str): + """After all IR generation is complete, we monkey patch btf_ama attribute""" + + # Create a string with applied transformation of btf_ama attribute addition to BTF struct field accesses. + pattern = r'(@"llvm\.[^"]+:[^"]*" = external global i64, !llvm\.preserve\.access\.index ![0-9]+)' + replacement = r'\1 "btf_ama"' + return re.sub(pattern, replacement, original_str) + + def find_bpf_chunks(tree): """Find all functions decorated with @bpf in the AST.""" bpf_functions = [] @@ -121,10 +131,12 @@ def compile_to_ir(filename: str, output: str, loglevel=logging.INFO): module.add_named_metadata("llvm.ident", [f"PythonBPF {VERSION}"]) + module_string = finalize_module(str(module)) + logger.info(f"IR written to {output}") with open(output, "w") as f: f.write(f'source_filename = "{filename}"\n') - f.write(str(module)) + f.write(module_string) f.write("\n") return output diff --git a/pythonbpf/vmlinux_parser/ir_gen/debug_info_gen.py b/pythonbpf/vmlinux_parser/ir_gen/debug_info_gen.py new file mode 100644 index 00000000..0b38cd67 --- /dev/null +++ b/pythonbpf/vmlinux_parser/ir_gen/debug_info_gen.py @@ -0,0 +1,15 @@ +from pythonbpf.debuginfo import DebugInfoGenerator + + +def debug_info_generation(struct, llvm_module): + generator = DebugInfoGenerator(llvm_module) + # this is sample debug info generation + # i64type = generator.get_uint64_type() + + struct_type = generator.create_struct_type([], 64 * 4, is_distinct=True) + + global_var = generator.create_global_var_debug_info( + struct.name, struct_type, is_local=False + ) + + return global_var diff --git a/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py b/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py index d500cf06..01e55da0 100644 --- a/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py +++ b/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py @@ -1,12 +1,16 @@ import logging -from pythonbpf.vmlinux_parser.dependency_handler import DependencyHandler +from ..dependency_handler import DependencyHandler +from .debug_info_gen import debug_info_generation +from ..dependency_node import DependencyNode +import llvmlite.ir as ir logger = logging.getLogger(__name__) class IRGenerator: - def __init__(self, module, handler: DependencyHandler): - self.module = module + # get the assignments dict and add this stuff to it. + def __init__(self, llvm_module, handler: DependencyHandler, assignment=None): + self.llvm_module = llvm_module self.handler: DependencyHandler = handler self.generated: list[str] = [] if not handler.is_ready: @@ -15,22 +19,48 @@ def __init__(self, module, handler: DependencyHandler): ) for struct in handler: self.struct_processor(struct) - print() def struct_processor(self, struct): if struct.name not in self.generated: print(f"IR generating for {struct.name}") - print(f"Struct is {struct}") for dependency in struct.depends_on: if dependency not in self.generated: dep_node_from_dependency = self.handler[dependency] self.struct_processor(dep_node_from_dependency) self.generated.append(dependency) - # write actual processor logic here after assuming all dependencies are resolved + # actual processor logic here after assuming all dependencies are resolved # this part cannot yet resolve circular dependencies. Gets stuck on an infinite loop during that. + self.gen_ir(struct) self.generated.append(struct.name) - def struct_name_generator( - self, - ) -> None: - pass + def gen_ir(self, struct): + # currently we generate all possible field accesses for CO-RE and put into the assignment table + debug_info = debug_info_generation(struct, self.llvm_module) + field_index = 0 + for field_name, field in struct.fields.items(): + # does not take arrays and similar types into consideration yet. + field_co_re_name = self._struct_name_generator(struct, field, field_index) + field_index += 1 + globvar = ir.GlobalVariable( + self.llvm_module, ir.IntType(64), name=field_co_re_name + ) + globvar.linkage = "external" + globvar.set_metadata("llvm.preserve.access.index", debug_info) + print() + + def _struct_name_generator( + self, struct: DependencyNode, field, field_index: int + ) -> str: + if struct.name.startswith("struct_"): + name = ( + "llvm." + + struct.name.removeprefix("struct_") + + f":0:{field.offset}" + + "$" + + f"0:{field_index}" + ) + return name + else: + raise TypeError( + "Name generation cannot occur due to type name not starting with struct" + ) From 2b3c81affabd7429fa752b0b39432c3219048b43 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Wed, 15 Oct 2025 21:33:08 +0530 Subject: [PATCH 04/12] TODO added for llvmlite attribute issue *Refer: https://github.com/numba/llvmlite/issues/1331 Signed-off-by: varun-r-mallya --- pythonbpf/vmlinux_parser/ir_gen/ir_generation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py b/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py index 01e55da0..c5fe740d 100644 --- a/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py +++ b/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py @@ -34,6 +34,8 @@ def struct_processor(self, struct): self.generated.append(struct.name) def gen_ir(self, struct): + # TODO: we add the btf_ama attribute by monkey patching in the end of compilation, but once llvmlite + # accepts our issue, we will resort to normal accessed attribute based attribute addition # currently we generate all possible field accesses for CO-RE and put into the assignment table debug_info = debug_info_generation(struct, self.llvm_module) field_index = 0 From c22d85ceb805aa2c47146bb1678bcc2c2d4c2359 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Wed, 15 Oct 2025 23:56:04 +0530 Subject: [PATCH 05/12] add array field generation support --- .../vmlinux_parser/dependency_handler.py | 4 + pythonbpf/vmlinux_parser/dependency_node.py | 1 + .../vmlinux_parser/ir_gen/ir_generation.py | 104 +++++++++++++++--- tests/failing_tests/xdp_pass.py | 3 +- 4 files changed, 92 insertions(+), 20 deletions(-) diff --git a/pythonbpf/vmlinux_parser/dependency_handler.py b/pythonbpf/vmlinux_parser/dependency_handler.py index b960ab3e..b34d27f7 100644 --- a/pythonbpf/vmlinux_parser/dependency_handler.py +++ b/pythonbpf/vmlinux_parser/dependency_handler.py @@ -167,3 +167,7 @@ def __getitem__(self, name: str) -> DependencyNode: if name not in self._nodes: raise KeyError(f"No node with name '{name}' found") return self._nodes[name] + + @property + def nodes(self): + return self._nodes diff --git a/pythonbpf/vmlinux_parser/dependency_node.py b/pythonbpf/vmlinux_parser/dependency_node.py index feebec35..ddfd0551 100644 --- a/pythonbpf/vmlinux_parser/dependency_node.py +++ b/pythonbpf/vmlinux_parser/dependency_node.py @@ -240,6 +240,7 @@ def _calculate_size( size_of_field = ctypes.sizeof(processing_field.type) return size_of_field elif processing_field.type.__module__ == "vmlinux": + #TODO: does not take into account offset calculation when not array but has type size if processing_field.ctype_complex_type is not None: if issubclass(processing_field.ctype_complex_type, ctypes.Array): if processing_field.containing_type.__module__ == ctypes.__name__: diff --git a/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py b/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py index c5fe740d..6c7d3e33 100644 --- a/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py +++ b/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py @@ -1,8 +1,10 @@ +import ctypes import logging from ..dependency_handler import DependencyHandler from .debug_info_gen import debug_info_generation from ..dependency_node import DependencyNode import llvmlite.ir as ir +from typing import Optional logger = logging.getLogger(__name__) @@ -20,19 +22,50 @@ def __init__(self, llvm_module, handler: DependencyHandler, assignment=None): for struct in handler: self.struct_processor(struct) - def struct_processor(self, struct): - if struct.name not in self.generated: - print(f"IR generating for {struct.name}") + def struct_processor(self, struct, processing_stack=None): + # Initialize processing stack on first call + if processing_stack is None: + processing_stack = set() + + # If already generated, skip + if struct.name in self.generated: + return + + # Detect circular dependency + if struct.name in processing_stack: + logger.info(f"Circular dependency detected for {struct.name}, skipping recursive processing") + # For circular dependencies, we can either: + # 1. Use forward declarations (opaque pointers) + # 2. Mark as incomplete and process later + # 3. Generate a placeholder type + # Here we'll just skip and let it be processed in its own call + return + + logger.info(f"IR generating for {struct.name}") + + # Add to processing stack before processing dependencies + processing_stack.add(struct.name) + + try: + # Process all dependencies first for dependency in struct.depends_on: if dependency not in self.generated: - dep_node_from_dependency = self.handler[dependency] - self.struct_processor(dep_node_from_dependency) - self.generated.append(dependency) - # actual processor logic here after assuming all dependencies are resolved - # this part cannot yet resolve circular dependencies. Gets stuck on an infinite loop during that. + # Check if dependency exists in handler + if dependency in self.handler.nodes: + dep_node_from_dependency = self.handler[dependency] + # Pass the processing_stack down to track circular refs + self.struct_processor(dep_node_from_dependency, processing_stack) + else: + raise RuntimeError(f"Warning: Dependency {dependency} not found in handler") + + # Actual processor logic here after dependencies are resolved self.gen_ir(struct) self.generated.append(struct.name) + finally: + # Remove from processing stack after we're done + processing_stack.discard(struct.name) + def gen_ir(self, struct): # TODO: we add the btf_ama attribute by monkey patching in the end of compilation, but once llvmlite # accepts our issue, we will resort to normal accessed attribute based attribute addition @@ -41,19 +74,54 @@ def gen_ir(self, struct): field_index = 0 for field_name, field in struct.fields.items(): # does not take arrays and similar types into consideration yet. - field_co_re_name = self._struct_name_generator(struct, field, field_index) - field_index += 1 - globvar = ir.GlobalVariable( - self.llvm_module, ir.IntType(64), name=field_co_re_name - ) - globvar.linkage = "external" - globvar.set_metadata("llvm.preserve.access.index", debug_info) - print() + if field.ctype_complex_type is not None and issubclass(field.ctype_complex_type, ctypes.Array): + array_size = field.type_size + containing_type = field.containing_type + if containing_type.__module__ == ctypes.__name__: + containing_type_size = ctypes.sizeof(containing_type) + for i in range(0,array_size): + field_co_re_name = self._struct_name_generator(struct, field, field_index, True, i, containing_type_size) + globvar = ir.GlobalVariable( + self.llvm_module, ir.IntType(64), name=field_co_re_name + ) + globvar.linkage = "external" + globvar.set_metadata("llvm.preserve.access.index", debug_info) + field_index += 1 + elif field.type_size is not None: + array_size = field.type_size + containing_type = field.containing_type + if containing_type.__module__ == "vmlinux": + containing_type_size = self.handler[containing_type.__name__].current_offset + for i in range(0,array_size): + field_co_re_name = self._struct_name_generator(struct, field, field_index, True, i, containing_type_size) + globvar = ir.GlobalVariable( + self.llvm_module, ir.IntType(64), name=field_co_re_name + ) + globvar.linkage = "external" + globvar.set_metadata("llvm.preserve.access.index", debug_info) + field_index += 1 + else: + field_co_re_name = self._struct_name_generator(struct, field, field_index) + field_index += 1 + globvar = ir.GlobalVariable( + self.llvm_module, ir.IntType(64), name=field_co_re_name + ) + globvar.linkage = "external" + globvar.set_metadata("llvm.preserve.access.index", debug_info) def _struct_name_generator( - self, struct: DependencyNode, field, field_index: int + self, struct: DependencyNode, field, field_index: int, is_indexed: bool=False, index: Optional[int]=None, containing_type_size: Optional[int]=None ) -> str: - if struct.name.startswith("struct_"): + if is_indexed: + name = ( + "llvm." + + struct.name.removeprefix("struct_") + + f":0:{field.offset + index*containing_type_size}" + + "$" + + f"0:{field_index}:{index}" + ) + return name + elif struct.name.startswith("struct_"): name = ( "llvm." + struct.name.removeprefix("struct_") diff --git a/tests/failing_tests/xdp_pass.py b/tests/failing_tests/xdp_pass.py index 99006955..3354e75e 100644 --- a/tests/failing_tests/xdp_pass.py +++ b/tests/failing_tests/xdp_pass.py @@ -3,8 +3,7 @@ from pythonbpf.helper import XDP_PASS from vmlinux import TASK_COMM_LEN # noqa: F401 from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 - -# from vmlinux import struct_request +from vmlinux import struct_posix_cputimers from vmlinux import struct_xdp_md # from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 # from vmlinux import struct_ring_buffer_per_cpu # noqa: F401 From de02731ea1b860e8cc5b06f3b2a0dfe715075179 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Thu, 16 Oct 2025 04:08:06 +0530 Subject: [PATCH 06/12] add support with ctypes getattr offset. Also supports bitfields. * breaks when struct_ring_buffer_per_cpu --- pythonbpf/vmlinux_parser/class_handler.py | 9 +- pythonbpf/vmlinux_parser/dependency_node.py | 103 ++++++++++++++------ 2 files changed, 76 insertions(+), 36 deletions(-) diff --git a/pythonbpf/vmlinux_parser/class_handler.py b/pythonbpf/vmlinux_parser/class_handler.py index 50f2fd65..6ef70ba6 100644 --- a/pythonbpf/vmlinux_parser/class_handler.py +++ b/pythonbpf/vmlinux_parser/class_handler.py @@ -25,7 +25,7 @@ def process_vmlinux_class(node, llvm_module, handler: DependencyHandler): def process_vmlinux_post_ast( - elem_type_class, llvm_handler, handler: DependencyHandler, processing_stack=None + elem_type_class, llvm_handler, handler: DependencyHandler, processing_stack=None ): # Initialize processing stack on first call if processing_stack is None: @@ -60,6 +60,10 @@ def process_vmlinux_post_ast( pass else: new_dep_node = DependencyNode(name=current_symbol_name) + + # elem_type_class is the actual vmlinux struct/class + new_dep_node.set_ctype_struct(elem_type_class) + handler.add_node(new_dep_node) class_obj = getattr(imported_module, current_symbol_name) # Inspect the class fields @@ -71,9 +75,6 @@ def process_vmlinux_post_ast( if len(field_elem) == 2: field_name, field_type = field_elem elif len(field_elem) == 3: - raise NotImplementedError( - "Bitfields are not supported in the current version" - ) field_name, field_type, bitfield_size = field_elem field_table[field_name] = [field_type, bitfield_size] elif hasattr(class_obj, "__annotations__"): diff --git a/pythonbpf/vmlinux_parser/dependency_node.py b/pythonbpf/vmlinux_parser/dependency_node.py index ddfd0551..3046f326 100644 --- a/pythonbpf/vmlinux_parser/dependency_node.py +++ b/pythonbpf/vmlinux_parser/dependency_node.py @@ -35,7 +35,7 @@ def set_type(self, given_type, mark_ready: bool = False) -> None: self.ready = True def set_containing_type( - self, containing_type: Optional[Any], mark_ready: bool = False + self, containing_type: Optional[Any], mark_ready: bool = False ) -> None: """Set the containing_type of this field and optionally mark it as ready.""" self.containing_type = containing_type @@ -49,7 +49,7 @@ def set_type_size(self, type_size: Any, mark_ready: bool = False) -> None: self.ready = True def set_ctype_complex_type( - self, ctype_complex_type: Any, mark_ready: bool = False + self, ctype_complex_type: Any, mark_ready: bool = False ) -> None: """Set the ctype_complex_type of this field and optionally mark it as ready.""" self.ctype_complex_type = ctype_complex_type @@ -116,18 +116,19 @@ class DependencyNode: fields: Dict[str, Field] = field(default_factory=dict) _ready_cache: Optional[bool] = field(default=None, repr=False) current_offset: int = 0 + ctype_struct: Optional[Any] = field(default=None, repr=False) def add_field( - self, - name: str, - field_type: type, - initial_value: Any = None, - containing_type: Optional[Any] = None, - type_size: Optional[int] = None, - ctype_complex_type: Optional[int] = None, - bitfield_size: Optional[int] = None, - ready: bool = False, - offset: int = 0, + self, + name: str, + field_type: type, + initial_value: Any = None, + containing_type: Optional[Any] = None, + type_size: Optional[int] = None, + ctype_complex_type: Optional[int] = None, + bitfield_size: Optional[int] = None, + ready: bool = False, + offset: int = 0, ) -> None: """Add a field to the node with an optional initial value and readiness state.""" if self.depends_on is None: @@ -146,7 +147,14 @@ def add_field( # Invalidate readiness cache self._ready_cache = None + def set_ctype_struct(self, ctype_struct: Any) -> None: + """Set the ctypes structure for automatic offset calculation.""" + self.ctype_struct = ctype_struct + def __sizeof__(self): + # If we have a ctype_struct, use its size + if self.ctype_struct is not None: + return ctypes.sizeof(self.ctype_struct) return self.current_offset def get_field(self, name: str) -> Field: @@ -172,7 +180,7 @@ def set_field_type(self, name: str, type: Any, mark_ready: bool = False) -> None self._ready_cache = None def set_field_containing_type( - self, name: str, containing_type: Any, mark_ready: bool = False + self, name: str, containing_type: Any, mark_ready: bool = False ) -> None: """Set a field's containing_type and optionally mark it as ready.""" if name not in self.fields: @@ -183,7 +191,7 @@ def set_field_containing_type( self._ready_cache = None def set_field_type_size( - self, name: str, type_size: Any, mark_ready: bool = False + self, name: str, type_size: Any, mark_ready: bool = False ) -> None: """Set a field's type_size and optionally mark it as ready.""" if name not in self.fields: @@ -194,7 +202,7 @@ def set_field_type_size( self._ready_cache = None def set_field_ctype_complex_type( - self, name: str, ctype_complex_type: Any, mark_ready: bool = False + self, name: str, ctype_complex_type: Any, mark_ready: bool = False ) -> None: """Set a field's ctype_complex_type and optionally mark it as ready.""" if name not in self.fields: @@ -205,7 +213,7 @@ def set_field_ctype_complex_type( self._ready_cache = None def set_field_bitfield_size( - self, name: str, bitfield_size: Any, mark_ready: bool = False + self, name: str, bitfield_size: Any, mark_ready: bool = False ) -> None: """Set a field's bitfield_size and optionally mark it as ready.""" if name not in self.fields: @@ -216,23 +224,35 @@ def set_field_bitfield_size( self._ready_cache = None def set_field_ready( - self, - name: str, - is_ready: bool = False, - size_of_containing_type: Optional[int] = None, + self, + name: str, + is_ready: bool = False, + size_of_containing_type: Optional[int] = None, ) -> None: """Mark a field as ready or not ready.""" if name not in self.fields: raise KeyError(f"Field '{name}' does not exist in node '{self.name}'") self.fields[name].set_ready(is_ready) - self.fields[name].set_offset(self.current_offset) - self.current_offset += self._calculate_size(name, size_of_containing_type) + + # Use ctypes built-in offset if available + if self.ctype_struct is not None: + try: + self.fields[name].set_offset(getattr(self.ctype_struct, name).offset) + except AttributeError: + # Fallback to manual calculation if field not found in ctype_struct + self.fields[name].set_offset(self.current_offset) + self.current_offset += self._calculate_size(name, size_of_containing_type) + else: + # Manual offset calculation when no ctype_struct is available + self.fields[name].set_offset(self.current_offset) + self.current_offset += self._calculate_size(name, size_of_containing_type) + # Invalidate readiness cache self._ready_cache = None def _calculate_size( - self, name: str, size_of_containing_type: Optional[int] = None + self, name: str, size_of_containing_type: Optional[int] = None ) -> int: processing_field = self.fields[name] # size_of_field will be in bytes @@ -240,17 +260,16 @@ def _calculate_size( size_of_field = ctypes.sizeof(processing_field.type) return size_of_field elif processing_field.type.__module__ == "vmlinux": - #TODO: does not take into account offset calculation when not array but has type size if processing_field.ctype_complex_type is not None: if issubclass(processing_field.ctype_complex_type, ctypes.Array): if processing_field.containing_type.__module__ == ctypes.__name__: if ( - processing_field.containing_type is not None - and processing_field.type_size is not None + processing_field.containing_type is not None + and processing_field.type_size is not None ): size_of_field = ( - ctypes.sizeof(processing_field.containing_type) - * processing_field.type_size + ctypes.sizeof(processing_field.containing_type) + * processing_field.type_size ) else: raise RuntimeError( @@ -259,11 +278,11 @@ def _calculate_size( return size_of_field elif processing_field.containing_type.__module__ == "vmlinux": if ( - size_of_containing_type is not None - and processing_field.type_size is not None + size_of_containing_type is not None + and processing_field.type_size is not None ): size_of_field = ( - size_of_containing_type * processing_field.type_size + size_of_containing_type * processing_field.type_size ) else: raise RuntimeError( @@ -276,8 +295,28 @@ def _calculate_size( raise NotImplementedError( "This subclass of ctype not supported yet" ) + elif processing_field.type_size is not None: + # Handle vmlinux types with type_size but no ctype_complex_type + # This means it's a direct vmlinux struct field (not array/pointer wrapped) + # The type_size should already contain the full size of the struct + # But if there's a containing_type from vmlinux, we need that size + if processing_field.containing_type is not None: + if processing_field.containing_type.__module__ == "vmlinux": + # For vmlinux containing types, we need the pre-calculated size + if size_of_containing_type is not None: + return size_of_containing_type * processing_field.type_size + else: + raise RuntimeError( + f"Field {name}: vmlinux containing_type requires size_of_containing_type" + ) + else: + raise ModuleNotFoundError( + f"Containing type module {processing_field.containing_type.__module__} not supported" + ) + else: + raise RuntimeError("Wrong type found with no containing type") else: - # search up pre-created stuff and get size + # No ctype_complex_type and no type_size, must rely on size_of_containing_type if size_of_containing_type is None: raise RuntimeError( f"Size of containing type {size_of_containing_type} is None" From 0f5c1fa75244df8011b31ca0f903eec4cb1aa7a4 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Thu, 16 Oct 2025 04:10:24 +0530 Subject: [PATCH 07/12] format chore --- pythonbpf/vmlinux_parser/class_handler.py | 2 +- pythonbpf/vmlinux_parser/dependency_node.py | 60 ++++++++++--------- .../vmlinux_parser/ir_gen/ir_generation.py | 54 ++++++++++++----- tests/failing_tests/xdp_pass.py | 1 - 4 files changed, 70 insertions(+), 47 deletions(-) diff --git a/pythonbpf/vmlinux_parser/class_handler.py b/pythonbpf/vmlinux_parser/class_handler.py index 6ef70ba6..48668a2a 100644 --- a/pythonbpf/vmlinux_parser/class_handler.py +++ b/pythonbpf/vmlinux_parser/class_handler.py @@ -25,7 +25,7 @@ def process_vmlinux_class(node, llvm_module, handler: DependencyHandler): def process_vmlinux_post_ast( - elem_type_class, llvm_handler, handler: DependencyHandler, processing_stack=None + elem_type_class, llvm_handler, handler: DependencyHandler, processing_stack=None ): # Initialize processing stack on first call if processing_stack is None: diff --git a/pythonbpf/vmlinux_parser/dependency_node.py b/pythonbpf/vmlinux_parser/dependency_node.py index 3046f326..e266761b 100644 --- a/pythonbpf/vmlinux_parser/dependency_node.py +++ b/pythonbpf/vmlinux_parser/dependency_node.py @@ -35,7 +35,7 @@ def set_type(self, given_type, mark_ready: bool = False) -> None: self.ready = True def set_containing_type( - self, containing_type: Optional[Any], mark_ready: bool = False + self, containing_type: Optional[Any], mark_ready: bool = False ) -> None: """Set the containing_type of this field and optionally mark it as ready.""" self.containing_type = containing_type @@ -49,7 +49,7 @@ def set_type_size(self, type_size: Any, mark_ready: bool = False) -> None: self.ready = True def set_ctype_complex_type( - self, ctype_complex_type: Any, mark_ready: bool = False + self, ctype_complex_type: Any, mark_ready: bool = False ) -> None: """Set the ctype_complex_type of this field and optionally mark it as ready.""" self.ctype_complex_type = ctype_complex_type @@ -119,16 +119,16 @@ class DependencyNode: ctype_struct: Optional[Any] = field(default=None, repr=False) def add_field( - self, - name: str, - field_type: type, - initial_value: Any = None, - containing_type: Optional[Any] = None, - type_size: Optional[int] = None, - ctype_complex_type: Optional[int] = None, - bitfield_size: Optional[int] = None, - ready: bool = False, - offset: int = 0, + self, + name: str, + field_type: type, + initial_value: Any = None, + containing_type: Optional[Any] = None, + type_size: Optional[int] = None, + ctype_complex_type: Optional[int] = None, + bitfield_size: Optional[int] = None, + ready: bool = False, + offset: int = 0, ) -> None: """Add a field to the node with an optional initial value and readiness state.""" if self.depends_on is None: @@ -180,7 +180,7 @@ def set_field_type(self, name: str, type: Any, mark_ready: bool = False) -> None self._ready_cache = None def set_field_containing_type( - self, name: str, containing_type: Any, mark_ready: bool = False + self, name: str, containing_type: Any, mark_ready: bool = False ) -> None: """Set a field's containing_type and optionally mark it as ready.""" if name not in self.fields: @@ -191,7 +191,7 @@ def set_field_containing_type( self._ready_cache = None def set_field_type_size( - self, name: str, type_size: Any, mark_ready: bool = False + self, name: str, type_size: Any, mark_ready: bool = False ) -> None: """Set a field's type_size and optionally mark it as ready.""" if name not in self.fields: @@ -202,7 +202,7 @@ def set_field_type_size( self._ready_cache = None def set_field_ctype_complex_type( - self, name: str, ctype_complex_type: Any, mark_ready: bool = False + self, name: str, ctype_complex_type: Any, mark_ready: bool = False ) -> None: """Set a field's ctype_complex_type and optionally mark it as ready.""" if name not in self.fields: @@ -213,7 +213,7 @@ def set_field_ctype_complex_type( self._ready_cache = None def set_field_bitfield_size( - self, name: str, bitfield_size: Any, mark_ready: bool = False + self, name: str, bitfield_size: Any, mark_ready: bool = False ) -> None: """Set a field's bitfield_size and optionally mark it as ready.""" if name not in self.fields: @@ -224,10 +224,10 @@ def set_field_bitfield_size( self._ready_cache = None def set_field_ready( - self, - name: str, - is_ready: bool = False, - size_of_containing_type: Optional[int] = None, + self, + name: str, + is_ready: bool = False, + size_of_containing_type: Optional[int] = None, ) -> None: """Mark a field as ready or not ready.""" if name not in self.fields: @@ -242,7 +242,9 @@ def set_field_ready( except AttributeError: # Fallback to manual calculation if field not found in ctype_struct self.fields[name].set_offset(self.current_offset) - self.current_offset += self._calculate_size(name, size_of_containing_type) + self.current_offset += self._calculate_size( + name, size_of_containing_type + ) else: # Manual offset calculation when no ctype_struct is available self.fields[name].set_offset(self.current_offset) @@ -252,7 +254,7 @@ def set_field_ready( self._ready_cache = None def _calculate_size( - self, name: str, size_of_containing_type: Optional[int] = None + self, name: str, size_of_containing_type: Optional[int] = None ) -> int: processing_field = self.fields[name] # size_of_field will be in bytes @@ -264,12 +266,12 @@ def _calculate_size( if issubclass(processing_field.ctype_complex_type, ctypes.Array): if processing_field.containing_type.__module__ == ctypes.__name__: if ( - processing_field.containing_type is not None - and processing_field.type_size is not None + processing_field.containing_type is not None + and processing_field.type_size is not None ): size_of_field = ( - ctypes.sizeof(processing_field.containing_type) - * processing_field.type_size + ctypes.sizeof(processing_field.containing_type) + * processing_field.type_size ) else: raise RuntimeError( @@ -278,11 +280,11 @@ def _calculate_size( return size_of_field elif processing_field.containing_type.__module__ == "vmlinux": if ( - size_of_containing_type is not None - and processing_field.type_size is not None + size_of_containing_type is not None + and processing_field.type_size is not None ): size_of_field = ( - size_of_containing_type * processing_field.type_size + size_of_containing_type * processing_field.type_size ) else: raise RuntimeError( diff --git a/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py b/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py index 6c7d3e33..989a4489 100644 --- a/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py +++ b/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py @@ -33,7 +33,9 @@ def struct_processor(self, struct, processing_stack=None): # Detect circular dependency if struct.name in processing_stack: - logger.info(f"Circular dependency detected for {struct.name}, skipping recursive processing") + logger.info( + f"Circular dependency detected for {struct.name}, skipping recursive processing" + ) # For circular dependencies, we can either: # 1. Use forward declarations (opaque pointers) # 2. Mark as incomplete and process later @@ -54,9 +56,13 @@ def struct_processor(self, struct, processing_stack=None): if dependency in self.handler.nodes: dep_node_from_dependency = self.handler[dependency] # Pass the processing_stack down to track circular refs - self.struct_processor(dep_node_from_dependency, processing_stack) + self.struct_processor( + dep_node_from_dependency, processing_stack + ) else: - raise RuntimeError(f"Warning: Dependency {dependency} not found in handler") + raise RuntimeError( + f"Warning: Dependency {dependency} not found in handler" + ) # Actual processor logic here after dependencies are resolved self.gen_ir(struct) @@ -74,13 +80,17 @@ def gen_ir(self, struct): field_index = 0 for field_name, field in struct.fields.items(): # does not take arrays and similar types into consideration yet. - if field.ctype_complex_type is not None and issubclass(field.ctype_complex_type, ctypes.Array): + if field.ctype_complex_type is not None and issubclass( + field.ctype_complex_type, ctypes.Array + ): array_size = field.type_size containing_type = field.containing_type if containing_type.__module__ == ctypes.__name__: containing_type_size = ctypes.sizeof(containing_type) - for i in range(0,array_size): - field_co_re_name = self._struct_name_generator(struct, field, field_index, True, i, containing_type_size) + for i in range(0, array_size): + field_co_re_name = self._struct_name_generator( + struct, field, field_index, True, i, containing_type_size + ) globvar = ir.GlobalVariable( self.llvm_module, ir.IntType(64), name=field_co_re_name ) @@ -91,9 +101,13 @@ def gen_ir(self, struct): array_size = field.type_size containing_type = field.containing_type if containing_type.__module__ == "vmlinux": - containing_type_size = self.handler[containing_type.__name__].current_offset - for i in range(0,array_size): - field_co_re_name = self._struct_name_generator(struct, field, field_index, True, i, containing_type_size) + containing_type_size = self.handler[ + containing_type.__name__ + ].current_offset + for i in range(0, array_size): + field_co_re_name = self._struct_name_generator( + struct, field, field_index, True, i, containing_type_size + ) globvar = ir.GlobalVariable( self.llvm_module, ir.IntType(64), name=field_co_re_name ) @@ -101,7 +115,9 @@ def gen_ir(self, struct): globvar.set_metadata("llvm.preserve.access.index", debug_info) field_index += 1 else: - field_co_re_name = self._struct_name_generator(struct, field, field_index) + field_co_re_name = self._struct_name_generator( + struct, field, field_index + ) field_index += 1 globvar = ir.GlobalVariable( self.llvm_module, ir.IntType(64), name=field_co_re_name @@ -110,15 +126,21 @@ def gen_ir(self, struct): globvar.set_metadata("llvm.preserve.access.index", debug_info) def _struct_name_generator( - self, struct: DependencyNode, field, field_index: int, is_indexed: bool=False, index: Optional[int]=None, containing_type_size: Optional[int]=None + self, + struct: DependencyNode, + field, + field_index: int, + is_indexed: bool = False, + index: int = 0, + containing_type_size: Optional[int] = None, ) -> str: if is_indexed: name = ( - "llvm." - + struct.name.removeprefix("struct_") - + f":0:{field.offset + index*containing_type_size}" - + "$" - + f"0:{field_index}:{index}" + "llvm." + + struct.name.removeprefix("struct_") + + f":0:{field.offset + index * containing_type_size}" + + "$" + + f"0:{field_index}:{index}" ) return name elif struct.name.startswith("struct_"): diff --git a/tests/failing_tests/xdp_pass.py b/tests/failing_tests/xdp_pass.py index 3354e75e..595632b9 100644 --- a/tests/failing_tests/xdp_pass.py +++ b/tests/failing_tests/xdp_pass.py @@ -3,7 +3,6 @@ from pythonbpf.helper import XDP_PASS from vmlinux import TASK_COMM_LEN # noqa: F401 from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 -from vmlinux import struct_posix_cputimers from vmlinux import struct_xdp_md # from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 # from vmlinux import struct_ring_buffer_per_cpu # noqa: F401 From f21837aefe4773ecbef9ee96d2593cea1ec863f2 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Thu, 16 Oct 2025 04:12:09 +0530 Subject: [PATCH 08/12] support most bitfields --- pythonbpf/vmlinux_parser/ir_gen/ir_generation.py | 3 +-- tests/failing_tests/xdp_pass.py | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py b/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py index 989a4489..cbf7f9d5 100644 --- a/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py +++ b/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py @@ -4,7 +4,6 @@ from .debug_info_gen import debug_info_generation from ..dependency_node import DependencyNode import llvmlite.ir as ir -from typing import Optional logger = logging.getLogger(__name__) @@ -132,7 +131,7 @@ def _struct_name_generator( field_index: int, is_indexed: bool = False, index: int = 0, - containing_type_size: Optional[int] = None, + containing_type_size: int = 0, ) -> str: if is_indexed: name = ( diff --git a/tests/failing_tests/xdp_pass.py b/tests/failing_tests/xdp_pass.py index 595632b9..bf31ee8f 100644 --- a/tests/failing_tests/xdp_pass.py +++ b/tests/failing_tests/xdp_pass.py @@ -3,6 +3,7 @@ from pythonbpf.helper import XDP_PASS from vmlinux import TASK_COMM_LEN # noqa: F401 from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 +from vmlinux import struct_posix_cputimers # noqa: F401 from vmlinux import struct_xdp_md # from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 # from vmlinux import struct_ring_buffer_per_cpu # noqa: F401 From 5413cc793b1ce7dc8a84580416a717cc27e35680 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Thu, 16 Oct 2025 18:06:36 +0530 Subject: [PATCH 09/12] something fixed itself. --- pythonbpf/vmlinux_parser/class_handler.py | 36 +++++++++++++++-------- tests/failing_tests/xdp_pass.py | 2 +- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/pythonbpf/vmlinux_parser/class_handler.py b/pythonbpf/vmlinux_parser/class_handler.py index 48668a2a..34cdf738 100644 --- a/pythonbpf/vmlinux_parser/class_handler.py +++ b/pythonbpf/vmlinux_parser/class_handler.py @@ -145,15 +145,30 @@ def process_vmlinux_post_ast( ) new_dep_node.set_field_type(elem_name, elem_type) if containing_type.__module__ == "vmlinux": - process_vmlinux_post_ast( - containing_type, llvm_handler, handler, processing_stack - ) - size_of_containing_type = ( - handler[containing_type.__name__] - ).__sizeof__() - new_dep_node.set_field_ready( - elem_name, True, size_of_containing_type + containing_type_name = ( + containing_type.__name__ + if hasattr(containing_type, "__name__") + else str(containing_type) ) + + # Check for self-reference or already processed + if containing_type_name == current_symbol_name: + # Self-referential pointer + logger.debug( + f"Self-referential pointer in {current_symbol_name}.{elem_name}" + ) + new_dep_node.set_field_ready(elem_name, True) + elif handler.has_node(containing_type_name): + # Already processed + logger.debug(f"Reusing already processed {containing_type_name}") + new_dep_node.set_field_ready(elem_name, True) + else: + # Process recursively - THIS WAS MISSING + new_dep_node.add_dependent(containing_type_name) + process_vmlinux_post_ast( + containing_type, llvm_handler, handler, processing_stack + ) + new_dep_node.set_field_ready(elem_name, True) elif containing_type.__module__ == ctypes.__name__: logger.debug(f"Processing ctype internal{containing_type}") new_dep_node.set_field_ready(elem_name, True) @@ -170,11 +185,8 @@ def process_vmlinux_post_ast( process_vmlinux_post_ast( elem_type, llvm_handler, handler, processing_stack ) - size_of_containing_type = ( - handler[elem_type.__name__] - ).__sizeof__() new_dep_node.set_field_ready( - elem_name, True, size_of_containing_type + elem_name, True ) else: raise ValueError( diff --git a/tests/failing_tests/xdp_pass.py b/tests/failing_tests/xdp_pass.py index bf31ee8f..83433bee 100644 --- a/tests/failing_tests/xdp_pass.py +++ b/tests/failing_tests/xdp_pass.py @@ -6,7 +6,7 @@ from vmlinux import struct_posix_cputimers # noqa: F401 from vmlinux import struct_xdp_md # from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 -# from vmlinux import struct_ring_buffer_per_cpu # noqa: F401 +from vmlinux import struct_ring_buffer_per_cpu # noqa: F401 from ctypes import c_int64 From 041e538b53c67c51a6fb49f7578793ba6d71bd35 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Thu, 16 Oct 2025 18:21:14 +0530 Subject: [PATCH 10/12] fix errors. Does not support union name resolution yet. --- .../vmlinux_parser/ir_gen/ir_generation.py | 30 +++++++++++-------- tests/failing_tests/xdp_pass.py | 9 +++--- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py b/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py index cbf7f9d5..1cf3794c 100644 --- a/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py +++ b/pythonbpf/vmlinux_parser/ir_gen/ir_generation.py @@ -49,19 +49,22 @@ def struct_processor(self, struct, processing_stack=None): try: # Process all dependencies first - for dependency in struct.depends_on: - if dependency not in self.generated: - # Check if dependency exists in handler - if dependency in self.handler.nodes: - dep_node_from_dependency = self.handler[dependency] - # Pass the processing_stack down to track circular refs - self.struct_processor( - dep_node_from_dependency, processing_stack - ) - else: - raise RuntimeError( - f"Warning: Dependency {dependency} not found in handler" - ) + if struct.depends_on is None: + pass + else: + for dependency in struct.depends_on: + if dependency not in self.generated: + # Check if dependency exists in handler + if dependency in self.handler.nodes: + dep_node_from_dependency = self.handler[dependency] + # Pass the processing_stack down to track circular refs + self.struct_processor( + dep_node_from_dependency, processing_stack + ) + else: + raise RuntimeError( + f"Warning: Dependency {dependency} not found in handler" + ) # Actual processor logic here after dependencies are resolved self.gen_ir(struct) @@ -152,6 +155,7 @@ def _struct_name_generator( ) return name else: + print(self.handler[struct.name]) raise TypeError( "Name generation cannot occur due to type name not starting with struct" ) diff --git a/tests/failing_tests/xdp_pass.py b/tests/failing_tests/xdp_pass.py index 83433bee..a4702781 100644 --- a/tests/failing_tests/xdp_pass.py +++ b/tests/failing_tests/xdp_pass.py @@ -2,12 +2,13 @@ from pythonbpf.maps import HashMap from pythonbpf.helper import XDP_PASS from vmlinux import TASK_COMM_LEN # noqa: F401 -from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 -from vmlinux import struct_posix_cputimers # noqa: F401 +# from vmlinux import struct_qspinlock_0_1 +# from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 +# from vmlinux import struct_posix_cputimers # noqa: F401 from vmlinux import struct_xdp_md # from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 -from vmlinux import struct_ring_buffer_per_cpu # noqa: F401 - +# from vmlinux import struct_ring_buffer_per_cpu # noqa: F401 +from vmlinux import struct_request from ctypes import c_int64 # Instructions to how to run this program From 5d9a29ee8ec5f412da990f6a5858b7c8c5f9e319 Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Thu, 16 Oct 2025 18:22:25 +0530 Subject: [PATCH 11/12] format chore --- pythonbpf/vmlinux_parser/class_handler.py | 13 ++++++++----- tests/failing_tests/xdp_pass.py | 6 ++++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/pythonbpf/vmlinux_parser/class_handler.py b/pythonbpf/vmlinux_parser/class_handler.py index 34cdf738..c9407116 100644 --- a/pythonbpf/vmlinux_parser/class_handler.py +++ b/pythonbpf/vmlinux_parser/class_handler.py @@ -160,13 +160,18 @@ def process_vmlinux_post_ast( new_dep_node.set_field_ready(elem_name, True) elif handler.has_node(containing_type_name): # Already processed - logger.debug(f"Reusing already processed {containing_type_name}") + logger.debug( + f"Reusing already processed {containing_type_name}" + ) new_dep_node.set_field_ready(elem_name, True) else: # Process recursively - THIS WAS MISSING new_dep_node.add_dependent(containing_type_name) process_vmlinux_post_ast( - containing_type, llvm_handler, handler, processing_stack + containing_type, + llvm_handler, + handler, + processing_stack, ) new_dep_node.set_field_ready(elem_name, True) elif containing_type.__module__ == ctypes.__name__: @@ -185,9 +190,7 @@ def process_vmlinux_post_ast( process_vmlinux_post_ast( elem_type, llvm_handler, handler, processing_stack ) - new_dep_node.set_field_ready( - elem_name, True - ) + new_dep_node.set_field_ready(elem_name, True) else: raise ValueError( f"{elem_name} with type {elem_type} from module {module_name} not supported in recursive resolver" diff --git a/tests/failing_tests/xdp_pass.py b/tests/failing_tests/xdp_pass.py index a4702781..1ab4eb25 100644 --- a/tests/failing_tests/xdp_pass.py +++ b/tests/failing_tests/xdp_pass.py @@ -2,13 +2,15 @@ from pythonbpf.maps import HashMap from pythonbpf.helper import XDP_PASS from vmlinux import TASK_COMM_LEN # noqa: F401 -# from vmlinux import struct_qspinlock_0_1 + +# from vmlinux import struct_qspinlock_0_1 # noqa: F401 # from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 # from vmlinux import struct_posix_cputimers # noqa: F401 from vmlinux import struct_xdp_md + # from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 # from vmlinux import struct_ring_buffer_per_cpu # noqa: F401 -from vmlinux import struct_request +from vmlinux import struct_request # noqa: F401 from ctypes import c_int64 # Instructions to how to run this program From 71d005b6b17f8935a8eb129986e72fe9949f341a Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Thu, 16 Oct 2025 18:58:05 +0530 Subject: [PATCH 12/12] complete vmlinux struct name generation in IR. * Breaks when it finds unions. * Still does not support function pointers. --- tests/failing_tests/xdp_pass.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/failing_tests/xdp_pass.py b/tests/failing_tests/xdp_pass.py index 1ab4eb25..c8510dcd 100644 --- a/tests/failing_tests/xdp_pass.py +++ b/tests/failing_tests/xdp_pass.py @@ -3,14 +3,15 @@ from pythonbpf.helper import XDP_PASS from vmlinux import TASK_COMM_LEN # noqa: F401 -# from vmlinux import struct_qspinlock_0_1 # noqa: F401 +from vmlinux import struct_qspinlock # noqa: F401 + # from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 # from vmlinux import struct_posix_cputimers # noqa: F401 from vmlinux import struct_xdp_md # from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401 # from vmlinux import struct_ring_buffer_per_cpu # noqa: F401 -from vmlinux import struct_request # noqa: F401 +# from vmlinux import struct_request # noqa: F401 from ctypes import c_int64 # Instructions to how to run this program