Skip to content

Commit 7b01f1d

Browse files
committed
Complete helpers for fstrings in helper_utils
1 parent 17f60d7 commit 7b01f1d

1 file changed

Lines changed: 78 additions & 0 deletions

File tree

pythonbpf/helper/helper_utils.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ast
22
import logging
33
from llvmlite import ir
4+
from pythonbpf.expr_pass import eval_expr
45

56
logger = logging.getLogger(__name__)
67

@@ -88,6 +89,28 @@ def _handle_fstring_print(joined_str, module, builder, func,
8889
_process_fval(value, fmt_parts, exprs,
8990
local_sym_tab, struct_sym_tab,
9091
local_var_metadata)
92+
else:
93+
raise NotImplementedError(
94+
f"Unsupported f-string value type: {type(value)}")
95+
96+
fmt_str = "".join(fmt_parts) + "\n\0"
97+
fmt_ptr = _create_format_string_global(fmt_str, func, module, builder)
98+
99+
args = [fmt_ptr, ir.Constant(ir.IntType(32), len(fmt_str))]
100+
101+
# NOTE: Process expressions (limited to 3 due to BPF constraints)
102+
if len(exprs) > 3:
103+
logger.warn(
104+
"bpf_printk supports up to 3 arguments, extra arguments will be ignored.")
105+
106+
for expr in exprs[:3]:
107+
arg_value = _prepare_expr_args(expr, func, module, builder,
108+
local_sym_tab, struct_sym_tab,
109+
local_var_metadata)
110+
args.append(arg_value)
111+
112+
# Call the BPF_PRINTK helper
113+
return _call_bpf_printk_helper(args, builder)
91114

92115

93116
def _process_constant_in_fstring(cst, fmt_parts, exprs):
@@ -176,3 +199,58 @@ def _populate_fval(ftype, node, fmt_parts, exprs):
176199
else:
177200
raise NotImplementedError(
178201
f"Unsupported field type in f-string: {ftype}")
202+
203+
204+
def _create_format_string_global(fmt_str, func, module, builder):
205+
"""Create a global variable for the format string."""
206+
fmt_name = f"{func.name}____fmt{func._fmt_counter}"
207+
func._fmt_counter += 1
208+
209+
fmt_gvar = ir.GlobalVariable(
210+
module, ir.ArrayType(ir.IntType(8), len(fmt_str)), name=fmt_name)
211+
fmt_gvar.global_constant = True
212+
fmt_gvar.initializer = ir.Constant(
213+
ir.ArrayType(ir.IntType(8), len(fmt_str)),
214+
bytearray(fmt_str.encode("utf8"))
215+
)
216+
fmt_gvar.linkage = "internal"
217+
fmt_gvar.align = 1
218+
219+
return builder.bitcast(fmt_gvar, ir.PointerType())
220+
221+
222+
def _prepare_expr_args(expr, func, module, builder,
223+
local_sym_tab, struct_sym_tab,
224+
local_var_metadata):
225+
"""Evaluate and prepare an expression to be used as an argument for bpf_printk."""
226+
print(f"{ast.dump(expr)}")
227+
val, _ = eval_expr(func, module, builder, expr,
228+
local_sym_tab, None, struct_sym_tab,
229+
local_var_metadata)
230+
231+
if val:
232+
if isinstance(val.type, ir.PointerType):
233+
val = builder.ptrtoint(val, ir.IntType(64))
234+
elif isinstance(val.type, ir.IntType):
235+
if val.type.width < 64:
236+
val = builder.sext(val, ir.IntType(64))
237+
else:
238+
logger.warn(
239+
"Only int and ptr supported in bpf_printk arguments. Others default to 0.")
240+
val = ir.Constant(ir.IntType(64), 0)
241+
return val
242+
else:
243+
logger.warn(
244+
"Failed to evaluate expression for bpf_printk argument. It will be converted to 0.")
245+
return ir.Constant(ir.IntType(64), 0)
246+
247+
248+
def _call_bpf_printk_helper(args, builder):
249+
"""Call the BPF_PRINTK helper function with the provided arguments."""
250+
fn_type = ir.FunctionType(
251+
ir.IntType(64), [ir.PointerType(), ir.IntType(32)], var_arg=True)
252+
fn_ptr_type = ir.PointerType(fn_type)
253+
fn_addr = ir.Constant(ir.IntType(64), BPFHelperID.BPF_PRINTK.value)
254+
fn_ptr = builder.inttoptr(fn_addr, fn_ptr_type)
255+
256+
return builder.call(fn_ptr, args, tail=True)

0 commit comments

Comments
 (0)