Skip to content

Commit 101183c

Browse files
members generated for simple ctypes
1 parent 3a31162 commit 101183c

File tree

3 files changed

+177
-29
lines changed

3 files changed

+177
-29
lines changed

pythonbpf/debuginfo/debug_info_generator.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,21 @@ def create_struct_member(self, name: str, base_type: Any, offset: int) -> Any:
101101
},
102102
)
103103

104+
def create_struct_member_vmlinux(self, name: str, base_type_with_size: Any, offset: int) -> Any:
105+
"""Create a struct member with the given name, type, and offset"""
106+
base_type, type_size = base_type_with_size
107+
return self.module.add_debug_info(
108+
"DIDerivedType",
109+
{
110+
"tag": dc.DW_TAG_member,
111+
"name": name,
112+
"file": self.module._file_metadata,
113+
"baseType": base_type,
114+
"size": getattr(base_type, "size", type_size),
115+
"offset": offset,
116+
},
117+
)
118+
104119
def create_struct_type(
105120
self, members: List[Any], size: int, is_distinct: bool
106121
) -> Any:
@@ -116,6 +131,22 @@ def create_struct_type(
116131
is_distinct=is_distinct,
117132
)
118133

134+
def create_struct_type_with_name(
135+
self, name: str, members: List[Any], size: int, is_distinct: bool
136+
) -> Any:
137+
"""Create a struct type with the given members and size"""
138+
return self.module.add_debug_info(
139+
"DICompositeType",
140+
{
141+
"name": name,
142+
"tag": dc.DW_TAG_structure_type,
143+
"file": self.module._file_metadata,
144+
"size": size,
145+
"elements": members,
146+
},
147+
is_distinct=is_distinct,
148+
)
149+
119150
def create_global_var_debug_info(
120151
self, name: str, var_type: Any, is_local: bool = False
121152
) -> Any:
Lines changed: 145 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,155 @@
1-
from pythonbpf.debuginfo import DebugInfoGenerator
1+
from pythonbpf.debuginfo import DebugInfoGenerator, dwarf_constants as dc
22
from ..dependency_node import DependencyNode
3+
import ctypes
4+
import logging
5+
from typing import List, Any, Tuple, Optional
36

7+
logger = logging.getLogger(__name__)
48

59
def debug_info_generation(
6-
struct: DependencyNode, llvm_module, generated_debug_info: list
7-
):
10+
struct: DependencyNode, llvm_module, generated_debug_info: List[Tuple[DependencyNode, Any]]
11+
) -> Any:
12+
"""
13+
Generate DWARF debug information for a struct defined in a DependencyNode.
14+
15+
Args:
16+
struct: The dependency node containing struct information
17+
llvm_module: The LLVM module to add debug info to
18+
generated_debug_info: List of tuples (struct, debug_info) to track generated debug info
19+
20+
Returns:
21+
The generated global variable debug info
22+
"""
23+
# Set up debug info generator
824
generator = DebugInfoGenerator(llvm_module)
25+
26+
# Check if debug info for this struct has already been generated
27+
for existing_struct, debug_info in generated_debug_info:
28+
if existing_struct.name == struct.name:
29+
return debug_info
30+
31+
# Process all fields and create members for the struct
932
members = []
10-
uint32type = generator.get_uint32_type()
1133
for field_name, field in struct.fields.items():
12-
members.append(generator.create_struct_member(field_name, uint32type, field.offset))
13-
14-
struct_type = generator.create_struct_type(members, struct.__sizeof__(), is_distinct=True)
34+
# Get appropriate debug type for this field
35+
field_type = _get_field_debug_type(
36+
field_name, field, generator, struct, generated_debug_info
37+
)
38+
# Create struct member with proper offset
39+
member = generator.create_struct_member_vmlinux(
40+
field_name, field_type, field.offset * 8
41+
)
42+
members.append(member)
1543

16-
global_var = generator.create_global_var_debug_info(
17-
struct.name, struct_type, is_local=False
44+
if struct.name.startswith("struct_"):
45+
struct_name = struct.name.removeprefix("struct_")
46+
else:
47+
raise ValueError("Unions are not supported in the current version")
48+
# Create struct type with all members
49+
struct_type = generator.create_struct_type_with_name(
50+
struct_name, members, struct.__sizeof__() * 8, is_distinct=True
1851
)
1952

20-
return global_var
53+
return struct_type
54+
55+
56+
def _get_field_debug_type(
57+
field_name: str,
58+
field,
59+
generator: DebugInfoGenerator,
60+
parent_struct: DependencyNode,
61+
generated_debug_info: List[Tuple[DependencyNode, Any]]
62+
) -> Any:
63+
"""
64+
Determine the appropriate debug type for a field based on its Python/ctypes type.
65+
66+
Args:
67+
field_name: Name of the field
68+
field: Field object containing type information
69+
generator: DebugInfoGenerator instance
70+
parent_struct: The parent struct containing this field
71+
generated_debug_info: List of already generated debug info
72+
73+
Returns:
74+
The debug info type for this field
75+
"""
76+
# Handle complex types (arrays, pointers)
77+
if field.ctype_complex_type is not None:
78+
if issubclass(field.ctype_complex_type, ctypes.Array):
79+
# Handle array types
80+
element_type = _get_basic_debug_type(field.containing_type, generator)
81+
return generator.create_array_type(element_type, field.type_size)
82+
elif issubclass(field.ctype_complex_type, ctypes._Pointer):
83+
# Handle pointer types
84+
pointee_type = _get_basic_debug_type(field.containing_type, generator)
85+
return generator.create_pointer_type(pointee_type)
86+
87+
# Handle other vmlinux types (nested structs)
88+
if field.type.__module__ == "vmlinux":
89+
# If it's a struct from vmlinux, check if we've already generated debug info for it
90+
struct_name = field.type.__name__
91+
92+
# Look for existing debug info in the list
93+
for existing_struct, debug_info in generated_debug_info:
94+
if existing_struct.name == struct_name:
95+
# Use existing debug info
96+
return debug_info
97+
98+
# If not found, create a forward declaration
99+
# This will be completed when the actual struct is processed
100+
logger.warning("Forward declaration in struct created")
101+
forward_type = generator.create_struct_type([], 0, is_distinct=True)
102+
return forward_type
103+
104+
# Handle basic C types
105+
return _get_basic_debug_type(field.type, generator)
106+
107+
108+
def _get_basic_debug_type(ctype, generator: DebugInfoGenerator) -> Any:
109+
"""
110+
Map a ctypes type to a DWARF debug type.
111+
112+
Args:
113+
ctype: A ctypes type or Python type
114+
generator: DebugInfoGenerator instance
115+
116+
Returns:
117+
The corresponding debug type
118+
"""
119+
# Map ctypes to debug info types
120+
if ctype == ctypes.c_char or ctype == ctypes.c_byte:
121+
return generator.get_basic_type("char", 8, dc.DW_ATE_signed_char), 8
122+
elif ctype == ctypes.c_ubyte or ctype == ctypes.c_uint8:
123+
return generator.get_basic_type("unsigned char", 8, dc.DW_ATE_unsigned_char), 8
124+
elif ctype == ctypes.c_short or ctype == ctypes.c_int16:
125+
return generator.get_basic_type("short", 16, dc.DW_ATE_signed), 16
126+
elif ctype == ctypes.c_ushort or ctype == ctypes.c_uint16:
127+
return generator.get_basic_type("unsigned short", 16, dc.DW_ATE_unsigned), 16
128+
elif ctype == ctypes.c_int or ctype == ctypes.c_int32:
129+
return generator.get_basic_type("int", 32, dc.DW_ATE_signed), 32
130+
elif ctype == ctypes.c_uint or ctype == ctypes.c_uint32:
131+
return generator.get_basic_type("unsigned int", 32, dc.DW_ATE_unsigned), 32
132+
elif ctype == ctypes.c_long:
133+
return generator.get_basic_type("long", 64, dc.DW_ATE_signed), 64
134+
elif ctype == ctypes.c_ulong:
135+
return generator.get_basic_type("unsigned long", 64, dc.DW_ATE_unsigned), 64
136+
elif ctype == ctypes.c_longlong or ctype == ctypes.c_int64:
137+
return generator.get_basic_type("long long", 64, dc.DW_ATE_signed), 64
138+
elif ctype == ctypes.c_ulonglong or ctype == ctypes.c_uint64:
139+
return generator.get_basic_type("unsigned long long", 64, dc.DW_ATE_unsigned), 64
140+
elif ctype == ctypes.c_float:
141+
return generator.get_basic_type("float", 32, dc.DW_ATE_float), 32
142+
elif ctype == ctypes.c_double:
143+
return generator.get_basic_type("double", 64, dc.DW_ATE_float), 64
144+
elif ctype == ctypes.c_bool:
145+
return generator.get_basic_type("bool", 8, dc.DW_ATE_boolean), 8
146+
elif ctype == ctypes.c_char_p:
147+
char_type = generator.get_basic_type("char", 8, dc.DW_ATE_signed_char), 8
148+
return generator.create_pointer_type(char_type)
149+
elif ctype == ctypes.c_void_p:
150+
void_type = generator.module.add_debug_info(
151+
"DIBasicType", {"name": "void"}
152+
)
153+
return generator.create_pointer_type(void_type), 64
154+
else:
155+
return generator.get_uint64_type(), 64

tests/passing_tests/vmlinux/xdp_pass.py

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from pythonbpf.helper import XDP_PASS
44
from vmlinux import TASK_COMM_LEN # noqa: F401
55
from vmlinux import struct_xdp_md
6-
from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401
6+
# from vmlinux import struct_trace_event_raw_sys_enter # noqa: F401
77
from ctypes import c_int64
88

99
# Instructions to how to run this program
@@ -13,27 +13,9 @@
1313
# 4. Attach object file to any network device with something like ./check.sh xdp examples/xdp_pass.o tailscale0
1414
# 5. send traffic through the device and observe effects
1515

16-
17-
@bpf
18-
@map
19-
def count() -> HashMap:
20-
return HashMap(key=c_int64, value=c_int64, max_entries=1)
21-
22-
2316
@bpf
2417
@section("xdp")
2518
def hello_world(ctx: struct_xdp_md) -> c_int64:
26-
key = 0
27-
one = 1
28-
prev = count().lookup(key)
29-
if prev:
30-
prevval = prev + 1
31-
print(f"count: {prevval}")
32-
count().update(key, prevval)
33-
return XDP_PASS
34-
else:
35-
count().update(key, one)
36-
3719
return XDP_PASS
3820

3921

0 commit comments

Comments
 (0)