|
1 | 1 | import ast |
2 | 2 | import logging |
3 | 3 | from llvmlite import ir |
| 4 | +from pythonbpf.expr_pass import eval_expr |
4 | 5 |
|
5 | 6 | logger = logging.getLogger(__name__) |
6 | 7 |
|
@@ -88,6 +89,28 @@ def _handle_fstring_print(joined_str, module, builder, func, |
88 | 89 | _process_fval(value, fmt_parts, exprs, |
89 | 90 | local_sym_tab, struct_sym_tab, |
90 | 91 | 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) |
91 | 114 |
|
92 | 115 |
|
93 | 116 | def _process_constant_in_fstring(cst, fmt_parts, exprs): |
@@ -176,3 +199,58 @@ def _populate_fval(ftype, node, fmt_parts, exprs): |
176 | 199 | else: |
177 | 200 | raise NotImplementedError( |
178 | 201 | 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