Skip to content

Commit ce222af

Browse files
r41k0uclaude
andcommitted
Core: Make vmlinux field lookups agree on one source of truth
has_field() consulted hasattr() on the ctypes class while get_field_type() indexed the parsed members dict. For any member ctypes exposes but the vmlinux parser never registered, has_field() said True and get_field_type() then died with a raw KeyError. Both now consult the parsed members dict, and a lookup that misses raises a ValueError that distinguishes "no such field" from "field exists in vmlinux.py but the parser does not support it yet". get_field_index() additionally derives the index from the ctypes _fields_ list (the C declaration order) instead of the insertion order of the parsed members dict, so it no longer depends on the two happening to coincide. No change to generated IR: every vmlinux/xdp test program compiles to a byte-identical .ll. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent c2de7da commit ce222af

1 file changed

Lines changed: 51 additions & 26 deletions

File tree

pythonbpf/vmlinux_parser/vmlinux_exports_handler.py

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -370,37 +370,62 @@ def load_ctx_field(builder, ctx_arg, offset_global, field_data, struct_name=None
370370

371371
return value
372372

373+
def _parsed_members(self, vmlinux_struct_name):
374+
"""
375+
Return the dict of fields the vmlinux parser actually produced for a struct.
376+
377+
This is the single source of truth for "does the compiler know about this
378+
field", as opposed to `hasattr(python_type, ...)` which merely reports what
379+
ctypes exposes on the class (including members the parser never registered).
380+
"""
381+
if not self.is_vmlinux_struct(vmlinux_struct_name):
382+
raise ValueError(f"{vmlinux_struct_name} is not a vmlinux struct")
383+
return self.vmlinux_symtab[vmlinux_struct_name].members
384+
385+
def _unsupported_field_error(self, vmlinux_struct_name, field_name):
386+
"""Build an actionable error for a field lookup that failed."""
387+
python_type = self.vmlinux_symtab[vmlinux_struct_name].python_type
388+
if hasattr(python_type, field_name):
389+
return ValueError(
390+
f"Field {field_name} of vmlinux struct {vmlinux_struct_name} exists in "
391+
"vmlinux.py but was not registered by the vmlinux parser, so it cannot "
392+
"be accessed yet (unsupported field kind)"
393+
)
394+
return ValueError(
395+
f"Field {field_name} not found in vmlinux struct {vmlinux_struct_name}"
396+
)
397+
373398
def has_field(self, struct_name, field_name):
374-
"""Check if a vmlinux struct has a specific field"""
399+
"""Check if a vmlinux struct has a specific field the parser understands"""
375400
if self.is_vmlinux_struct(struct_name):
376-
python_type = self.vmlinux_symtab[struct_name].python_type
377-
return hasattr(python_type, field_name)
401+
return field_name in self.vmlinux_symtab[struct_name].members
378402
return False
379403

380404
def get_field_type(self, vmlinux_struct_name, field_name):
381405
"""Get the type of a field in a vmlinux struct"""
382-
if self.is_vmlinux_struct(vmlinux_struct_name):
383-
python_type = self.vmlinux_symtab[vmlinux_struct_name].python_type
384-
if hasattr(python_type, field_name):
385-
return self.vmlinux_symtab[vmlinux_struct_name].members[field_name]
386-
else:
387-
raise ValueError(
388-
f"Field {field_name} not found in vmlinux struct {vmlinux_struct_name}"
389-
)
390-
else:
391-
raise ValueError(f"{vmlinux_struct_name} is not a vmlinux struct")
406+
members = self._parsed_members(vmlinux_struct_name)
407+
if field_name in members:
408+
return members[field_name]
409+
raise self._unsupported_field_error(vmlinux_struct_name, field_name)
392410

393411
def get_field_index(self, vmlinux_struct_name, field_name):
394-
"""Get the type of a field in a vmlinux struct"""
395-
if self.is_vmlinux_struct(vmlinux_struct_name):
396-
python_type = self.vmlinux_symtab[vmlinux_struct_name].python_type
397-
if hasattr(python_type, field_name):
398-
return list(
399-
self.vmlinux_symtab[vmlinux_struct_name].members.keys()
400-
).index(field_name)
401-
else:
402-
raise ValueError(
403-
f"Field {field_name} not found in vmlinux struct {vmlinux_struct_name}"
404-
)
405-
else:
406-
raise ValueError(f"{vmlinux_struct_name} is not a vmlinux struct")
412+
"""
413+
Get the declaration index of a field in a vmlinux struct.
414+
415+
The index is derived from the ctypes `_fields_` list, i.e. from the C
416+
declaration order, rather than from the insertion order of the parsed
417+
members dict.
418+
"""
419+
members = self._parsed_members(vmlinux_struct_name)
420+
if field_name not in members:
421+
raise self._unsupported_field_error(vmlinux_struct_name, field_name)
422+
423+
python_type = self.vmlinux_symtab[vmlinux_struct_name].python_type
424+
declared_fields = getattr(python_type, "_fields_", None)
425+
if declared_fields is not None:
426+
for index, declared in enumerate(declared_fields):
427+
if declared[0] == field_name:
428+
return index
429+
# No `_fields_` (or the field is not declared at the top level, e.g. it was
430+
# flattened out of an anonymous member): fall back to the parsed ordering.
431+
return list(members.keys()).index(field_name)

0 commit comments

Comments
 (0)