From 75d3ad4fe234e161f087f312f4cc1d5653e45fba Mon Sep 17 00:00:00 2001 From: varun-r-mallya Date: Sat, 11 Oct 2025 22:00:25 +0530 Subject: [PATCH] format chore --- .pre-commit-config.yaml | 2 +- pythonbpf/vmlinux_parser/__init__.py | 2 + ...inux_class_handler.py => class_handler.py} | 24 ++-- pythonbpf/vmlinux_parser/dependency_node.py | 14 ++- pythonbpf/vmlinux_parser/import_detector.py | 21 ++-- tools/vmlinux-gen.py | 119 +++++++++--------- 6 files changed, 104 insertions(+), 78 deletions(-) rename pythonbpf/vmlinux_parser/{vmlinux_class_handler.py => class_handler.py} (85%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a8f1c37..0db824a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ # # See https://github.com/pre-commit/pre-commit -exclude: 'vmlinux.*\.py$' +exclude: 'vmlinux.py' ci: autoupdate_commit_msg: "chore: update pre-commit hooks" diff --git a/pythonbpf/vmlinux_parser/__init__.py b/pythonbpf/vmlinux_parser/__init__.py index 91b031b..b047a28 100644 --- a/pythonbpf/vmlinux_parser/__init__.py +++ b/pythonbpf/vmlinux_parser/__init__.py @@ -1 +1,3 @@ from .import_detector import vmlinux_proc + +__all__ = ["vmlinux_proc"] diff --git a/pythonbpf/vmlinux_parser/vmlinux_class_handler.py b/pythonbpf/vmlinux_parser/class_handler.py similarity index 85% rename from pythonbpf/vmlinux_parser/vmlinux_class_handler.py rename to pythonbpf/vmlinux_parser/class_handler.py index f2d2104..241f7d5 100644 --- a/pythonbpf/vmlinux_parser/vmlinux_class_handler.py +++ b/pythonbpf/vmlinux_parser/class_handler.py @@ -1,4 +1,3 @@ -import ast import logging from functools import lru_cache import importlib @@ -20,9 +19,9 @@ def process_vmlinux_class(node, llvm_module, handler: DependencyHandler): symbols_in_module, imported_module = get_module_symbols("vmlinux") # Handle both node objects and type objects - if hasattr(node, 'name'): + if hasattr(node, "name"): current_symbol_name = node.name - elif hasattr(node, '__name__'): + elif hasattr(node, "__name__"): current_symbol_name = node.__name__ else: current_symbol_name = str(node) @@ -30,7 +29,9 @@ def process_vmlinux_class(node, llvm_module, handler: DependencyHandler): if current_symbol_name not in symbols_in_module: raise ImportError(f"{current_symbol_name} not present in module vmlinux") logger.info(f"Resolving vmlinux class {current_symbol_name}") - logger.debug(f"Current handler state: {handler.is_ready} readiness and {handler.get_all_nodes()} all nodes") + logger.debug( + f"Current handler state: {handler.is_ready} readiness and {handler.get_all_nodes()} all nodes" + ) field_table = {} # should contain the field and it's type. # Get the class object from the module @@ -42,12 +43,12 @@ def process_vmlinux_class(node, llvm_module, handler: DependencyHandler): # Inspect the class fields # Assuming class_obj has fields stored in some standard way # If it's a ctypes-like structure with _fields_ - if hasattr(class_obj, '_fields_'): + if hasattr(class_obj, "_fields_"): for field_name, field_type in class_obj._fields_: field_table[field_name] = field_type # If it's using __annotations__ - elif hasattr(class_obj, '__annotations__'): + elif hasattr(class_obj, "__annotations__"): for field_name, field_type in class_obj.__annotations__.items(): field_table[field_name] = field_type @@ -69,17 +70,24 @@ def process_vmlinux_class(node, llvm_module, handler: DependencyHandler): print("elem_name:", elem_name, "elem_type:", elem_type) # currently fails when a non-normal type appears which is basically everytime identify_ctypes_type(elem_type) - symbol_name = elem_type.__name__ if hasattr(elem_type, '__name__') else str(elem_type) + symbol_name = ( + elem_type.__name__ + if hasattr(elem_type, "__name__") + else str(elem_type) + ) vmlinux_symbol = getattr(imported_module, symbol_name) if process_vmlinux_class(vmlinux_symbol, llvm_module, handler): new_dep_node.set_field_ready(elem_name, True) else: - raise ValueError(f"{elem_name} with type {elem_type} not supported in recursive resolver") + raise ValueError( + f"{elem_name} with type {elem_type} not supported in recursive resolver" + ) handler.add_node(new_dep_node) logger.info(f"added node: {current_symbol_name}") return True + def identify_ctypes_type(t): if isinstance(t, type): # t is a type/class if issubclass(t, ctypes.Array): diff --git a/pythonbpf/vmlinux_parser/dependency_node.py b/pythonbpf/vmlinux_parser/dependency_node.py index 5294ae7..b828f63 100644 --- a/pythonbpf/vmlinux_parser/dependency_node.py +++ b/pythonbpf/vmlinux_parser/dependency_node.py @@ -5,6 +5,7 @@ from typing import Dict, Any, Optional @dataclass class Field: """Represents a field in a dependency node with its type and readiness state.""" + name: str type: type value: Any = None @@ -64,13 +65,22 @@ class DependencyNode: ready_fields = somestruct.get_ready_fields() print(f"Ready fields: {[field.name for field in ready_fields.values()]}") # ['field_1', 'field_2'] """ + name: str fields: Dict[str, Field] = field(default_factory=dict) _ready_cache: Optional[bool] = field(default=None, repr=False) - def add_field(self, name: str, field_type: type, initial_value: Any = None, ready: bool = False) -> None: + def add_field( + self, + name: str, + field_type: type, + initial_value: Any = None, + ready: bool = False, + ) -> None: """Add a field to the node with an optional initial value and readiness state.""" - self.fields[name] = Field(name=name, type=field_type, value=initial_value, ready=ready) + self.fields[name] = Field( + name=name, type=field_type, value=initial_value, ready=ready + ) # Invalidate readiness cache self._ready_cache = None diff --git a/pythonbpf/vmlinux_parser/import_detector.py b/pythonbpf/vmlinux_parser/import_detector.py index a91884f..2ce9cb5 100644 --- a/pythonbpf/vmlinux_parser/import_detector.py +++ b/pythonbpf/vmlinux_parser/import_detector.py @@ -6,7 +6,7 @@ import inspect from .dependency_handler import DependencyHandler from .ir_generation import IRGenerator -from .vmlinux_class_handler import process_vmlinux_class +from .class_handler import process_vmlinux_class logger = logging.getLogger(__name__) @@ -58,8 +58,8 @@ def detect_import_statement(tree: ast.AST) -> List[Tuple[str, ast.ImportFrom]]: # Valid single import for alias in node.names: import_name = alias.name - # Use alias if provided, otherwise use the original name - as_name = alias.asname if alias.asname else alias.name + # Use alias if provided, otherwise use the original name (commented) + # as_name = alias.asname if alias.asname else alias.name vmlinux_imports.append(("vmlinux", node)) logger.info(f"Found vmlinux import: {import_name}") @@ -68,13 +68,14 @@ def detect_import_statement(tree: ast.AST) -> List[Tuple[str, ast.ImportFrom]]: for alias in node.names: if alias.name == "vmlinux" or alias.name.startswith("vmlinux."): raise SyntaxError( - f"Direct import of vmlinux module is not supported. " - f"Use 'from vmlinux import ' instead." + "Direct import of vmlinux module is not supported. " + "Use 'from vmlinux import ' instead." ) logger.info(f"Total vmlinux imports detected: {len(vmlinux_imports)}") return vmlinux_imports + def vmlinux_proc(tree: ast.AST, module): import_statements = detect_import_statement(tree) @@ -107,7 +108,10 @@ def vmlinux_proc(tree: ast.AST, module): imported_name = alias.name found = False for mod_node in mod_ast.body: - if isinstance(mod_node, ast.ClassDef) and mod_node.name == imported_name: + if ( + isinstance(mod_node, ast.ClassDef) + and mod_node.name == imported_name + ): process_vmlinux_class(mod_node, module, handler) found = True break @@ -120,9 +124,12 @@ def vmlinux_proc(tree: ast.AST, module): if found: break if not found: - logger.info(f"{imported_name} not found as ClassDef or Assign in vmlinux") + logger.info( + f"{imported_name} not found as ClassDef or Assign in vmlinux" + ) IRGenerator(module, handler) + def process_vmlinux_assign(node, module, assignments: Dict[str, type]): raise NotImplementedError("Assignment handling has not been implemented yet") diff --git a/tools/vmlinux-gen.py b/tools/vmlinux-gen.py index a1a580b..714d1ce 100755 --- a/tools/vmlinux-gen.py +++ b/tools/vmlinux-gen.py @@ -26,8 +26,13 @@ import tempfile class BTFConverter: - def __init__(self, btf_source="/sys/kernel/btf/vmlinux", output_file="vmlinux.py", - keep_intermediate=False, verbose=False): + def __init__( + self, + btf_source="/sys/kernel/btf/vmlinux", + output_file="vmlinux.py", + keep_intermediate=False, + verbose=False, + ): self.btf_source = btf_source self.output_file = output_file self.keep_intermediate = keep_intermediate @@ -44,11 +49,7 @@ class BTFConverter: self.log(f"{description}...") try: result = subprocess.run( - cmd, - shell=True, - check=True, - capture_output=True, - text=True + cmd, shell=True, check=True, capture_output=True, text=True ) if self.verbose and result.stdout: print(result.stdout) @@ -69,51 +70,55 @@ class BTFConverter: """Step 1.5: Preprocess enum definitions.""" self.log("Preprocessing enum definitions...") - with open(input_file, 'r') as f: + with open(input_file, "r") as f: original_code = f.read() # Extract anonymous enums enums = re.findall( - r'(? 8 else m.group(0) + return ( + f"('{name}', ctypes.c_uint32, {bits})" if int(bits) > 8 else m.group(0) + ) - data = re.sub( - r"\('([^']+)',\s*ctypes\.c_bool,\s*(\d+)\)", - repl, - data - ) + data = re.sub(r"\('([^']+)',\s*ctypes\.c_bool,\s*(\d+)\)", repl, data) # Remove ctypes. prefix from invalid entries invalid_ctypes = ["bpf_iter_state", "_cache_type", "fs_context_purpose"] @@ -269,6 +271,7 @@ class BTFConverter: if not self.keep_intermediate and self.temp_dir != ".": self.log(f"Cleaning up temporary directory: {self.temp_dir}") import shutil + shutil.rmtree(self.temp_dir, ignore_errors=True) def convert(self): @@ -292,6 +295,7 @@ class BTFConverter: except Exception as e: print(f"\nāœ— Error during conversion: {e}", file=sys.stderr) import traceback + traceback.print_exc() sys.exit(1) finally: @@ -304,18 +308,13 @@ class BTFConverter: dependencies = { "bpftool": "bpftool --version", "clang": "clang --version", - "clang2py": "clang2py --version" + "clang2py": "clang2py --version", } missing = [] for tool, cmd in dependencies.items(): try: - subprocess.run( - cmd, - shell=True, - check=True, - capture_output=True - ) + subprocess.run(cmd, shell=True, check=True, capture_output=True) except subprocess.CalledProcessError: missing.append(tool) @@ -337,31 +336,31 @@ Examples: %(prog)s %(prog)s -o kernel_types.py %(prog)s --btf-source /sys/kernel/btf/custom_module -k -v - """ + """, ) parser.add_argument( "--btf-source", default="/sys/kernel/btf/vmlinux", - help="Path to BTF source (default: /sys/kernel/btf/vmlinux)" + help="Path to BTF source (default: /sys/kernel/btf/vmlinux)", ) parser.add_argument( - "-o", "--output", + "-o", + "--output", default="vmlinux.py", - help="Output Python file (default: vmlinux.py)" + help="Output Python file (default: vmlinux.py)", ) parser.add_argument( - "-k", "--keep-intermediate", + "-k", + "--keep-intermediate", action="store_true", - help="Keep intermediate files (vmlinux.h, vmlinux_processed.h, etc.)" + help="Keep intermediate files (vmlinux.h, vmlinux_processed.h, etc.)", ) parser.add_argument( - "-v", "--verbose", - action="store_true", - help="Enable verbose output" + "-v", "--verbose", action="store_true", help="Enable verbose output" ) args = parser.parse_args() @@ -370,7 +369,7 @@ Examples: btf_source=args.btf_source, output_file=args.output, keep_intermediate=args.keep_intermediate, - verbose=args.verbose + verbose=args.verbose, ) converter.convert()