Skip to content

Commit fb48063

Browse files
committed
Make char array struct fields work
1 parent 13a804f commit fb48063

File tree

2 files changed

+108
-40
lines changed

2 files changed

+108
-40
lines changed

BCC-Examples/hello_perf_output.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from pythonbpf import bpf, map, struct, section, bpfglobal, compile
1+
from pythonbpf import bpf, map, struct, section, bpfglobal, BPF, trace_pipe
22
from pythonbpf.helper import ktime, pid, comm
33
from pythonbpf.maps import PerfEventArray
44

@@ -23,10 +23,9 @@ def events() -> PerfEventArray:
2323
@section("tracepoint/syscalls/sys_enter_clone")
2424
def hello(ctx: c_void_p) -> c_int64:
2525
dataobj = data_t()
26-
strobj = "hellohellohello"
2726
dataobj.pid, dataobj.ts = pid(), ktime()
2827
comm(dataobj.comm)
29-
print(f"clone called at {dataobj.ts} by pid {dataobj.pid}, comm {strobj}")
28+
print(f"clone called at {dataobj.ts} by pid {dataobj.pid}, comm {dataobj.comm}")
3029
events.output(dataobj)
3130
return 0 # type: ignore [return-value]
3231

@@ -37,4 +36,8 @@ def LICENSE() -> str:
3736
return "GPL"
3837

3938

40-
compile()
39+
# compile
40+
BPF().load_and_attach()
41+
42+
print("Tracing clone()... Ctrl-C to end")
43+
trace_pipe()

pythonbpf/helper/printk_formatter.py

Lines changed: 101 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,15 @@ def _populate_fval(ftype, node, fmt_parts, exprs):
173173
raise NotImplementedError(
174174
f"Unsupported pointer target type in f-string: {target}"
175175
)
176+
elif isinstance(ftype, ir.ArrayType):
177+
if isinstance(ftype.element, ir.IntType) and ftype.element.width == 8:
178+
# Char array
179+
fmt_parts.append("%s")
180+
exprs.append(node)
181+
else:
182+
raise NotImplementedError(
183+
f"Unsupported array element type in f-string: {ftype.element}"
184+
)
176185
else:
177186
raise NotImplementedError(f"Unsupported field type in f-string: {ftype}")
178187

@@ -197,44 +206,100 @@ def _create_format_string_global(fmt_str, func, module, builder):
197206

198207
def _prepare_expr_args(expr, func, module, builder, local_sym_tab, struct_sym_tab):
199208
"""Evaluate and prepare an expression to use as an arg for bpf_printk."""
200-
val, _ = eval_expr(
201-
func,
202-
module,
203-
builder,
204-
expr,
205-
local_sym_tab,
206-
None,
207-
struct_sym_tab,
209+
210+
# Special case: struct field char array needs pointer to first element
211+
char_array_ptr = _get_struct_char_array_ptr(
212+
expr, builder, local_sym_tab, struct_sym_tab
208213
)
214+
if char_array_ptr:
215+
return char_array_ptr
209216

210-
if val:
211-
if isinstance(val.type, ir.PointerType):
212-
target, depth = get_base_type_and_depth(val.type)
213-
if isinstance(target, ir.IntType):
214-
if target.width >= 32:
215-
val = deref_to_depth(func, builder, val, depth)
216-
val = builder.sext(val, ir.IntType(64))
217-
elif target.width == 8 and depth == 1:
218-
# NOTE: i8* is string, no need to deref
219-
pass
217+
# Regular expression evaluation
218+
val, _ = eval_expr(func, module, builder, expr, local_sym_tab, None, struct_sym_tab)
220219

221-
else:
222-
logger.warning(
223-
"Only int and ptr supported in bpf_printk args. Others default to 0."
224-
)
225-
val = ir.Constant(ir.IntType(64), 0)
226-
elif isinstance(val.type, ir.IntType):
227-
if val.type.width < 64:
228-
val = builder.sext(val, ir.IntType(64))
229-
else:
230-
logger.warning(
231-
"Only int and ptr supported in bpf_printk args. Others default to 0."
232-
)
233-
val = ir.Constant(ir.IntType(64), 0)
234-
return val
220+
if not val:
221+
logger.warning("Failed to evaluate expression for bpf_printk, defaulting to 0")
222+
return ir.Constant(ir.IntType(64), 0)
223+
224+
# Convert value to bpf_printk compatible type
225+
if isinstance(val.type, ir.PointerType):
226+
return _handle_pointer_arg(val, func, builder)
227+
elif isinstance(val.type, ir.IntType):
228+
return _handle_int_arg(val, builder)
235229
else:
236-
logger.warning(
237-
"Failed to evaluate expression for bpf_printk argument. "
238-
"It will be converted to 0."
239-
)
230+
logger.warning(f"Unsupported type {val.type} in bpf_printk, defaulting to 0")
240231
return ir.Constant(ir.IntType(64), 0)
232+
233+
234+
def _get_struct_char_array_ptr(expr, builder, local_sym_tab, struct_sym_tab):
235+
"""Get pointer to first element of char array in struct field, or None."""
236+
if not (isinstance(expr, ast.Attribute) and isinstance(expr.value, ast.Name)):
237+
return None
238+
239+
var_name = expr.value.id
240+
field_name = expr.attr
241+
242+
# Check if it's a valid struct field
243+
if not (
244+
local_sym_tab
245+
and var_name in local_sym_tab
246+
and struct_sym_tab
247+
and local_sym_tab[var_name].metadata in struct_sym_tab
248+
):
249+
return None
250+
251+
struct_type = local_sym_tab[var_name].metadata
252+
struct_info = struct_sym_tab[struct_type]
253+
254+
if field_name not in struct_info.fields:
255+
return None
256+
257+
field_type = struct_info.field_type(field_name)
258+
259+
# Check if it's a char array
260+
is_char_array = (
261+
isinstance(field_type, ir.ArrayType)
262+
and isinstance(field_type.element, ir.IntType)
263+
and field_type.element.width == 8
264+
)
265+
266+
if not is_char_array:
267+
return None
268+
269+
# Get field pointer and GEP to first element: [N x i8]* -> i8*
270+
struct_ptr = local_sym_tab[var_name].var
271+
field_ptr = struct_info.gep(builder, struct_ptr, field_name)
272+
273+
return builder.gep(
274+
field_ptr,
275+
[ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)],
276+
inbounds=True,
277+
)
278+
279+
280+
def _handle_pointer_arg(val, func, builder):
281+
"""Convert pointer type for bpf_printk."""
282+
target, depth = get_base_type_and_depth(val.type)
283+
284+
if not isinstance(target, ir.IntType):
285+
logger.warning("Only int pointers supported in bpf_printk, defaulting to 0")
286+
return ir.Constant(ir.IntType(64), 0)
287+
288+
# i8* is string - use as-is
289+
if target.width == 8 and depth == 1:
290+
return val
291+
292+
# Integer pointers: dereference and sign-extend to i64
293+
if target.width >= 32:
294+
val = deref_to_depth(func, builder, val, depth)
295+
return builder.sext(val, ir.IntType(64))
296+
297+
logger.warning("Unsupported pointer width in bpf_printk, defaulting to 0")
298+
return ir.Constant(ir.IntType(64), 0)
299+
300+
301+
def _handle_int_arg(val, builder):
302+
"""Convert integer type for bpf_printk (sign-extend to i64)."""
303+
if val.type.width < 64:
304+
return builder.sext(val, ir.IntType(64))
305+
return val

0 commit comments

Comments
 (0)