Skip to content

Commit 6b41f1f

Browse files
committed
Move print logic to helper/printk_emitter.py
1 parent 74d8014 commit 6b41f1f

File tree

3 files changed

+247
-233
lines changed

3 files changed

+247
-233
lines changed

pythonbpf/helper/bpf_helper_handler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
from .helper_utils import (
77
get_or_create_ptr_from_arg,
88
get_flags_val,
9-
handle_fstring_print,
10-
simple_string_print,
119
get_data_ptr_and_size,
1210
)
11+
from .printk_formatter import simple_string_print, handle_fstring_print
12+
1313
from logging import Logger
1414
import logging
1515

pythonbpf/helper/helper_utils.py

Lines changed: 5 additions & 231 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33

44
from llvmlite import ir
55
from pythonbpf.expr import (
6-
eval_expr,
7-
get_base_type_and_depth,
8-
deref_to_depth,
96
get_operand_value,
107
)
118

@@ -47,6 +44,11 @@ def reset_scratch_pool():
4744
_temp_pool_manager.reset()
4845

4946

47+
# ============================================================================
48+
# Argument Preparation
49+
# ============================================================================
50+
51+
5052
def get_var_ptr_from_name(var_name, local_sym_tab):
5153
"""Get a pointer to a variable from the symbol table."""
5254
if local_sym_tab and var_name in local_sym_tab:
@@ -111,234 +113,6 @@ def get_flags_val(arg, builder, local_sym_tab):
111113
)
112114

113115

114-
def simple_string_print(string_value, module, builder, func):
115-
"""Prepare arguments for bpf_printk from a simple string value"""
116-
fmt_str = string_value + "\n\0"
117-
fmt_ptr = _create_format_string_global(fmt_str, func, module, builder)
118-
119-
args = [fmt_ptr, ir.Constant(ir.IntType(32), len(fmt_str))]
120-
return args
121-
122-
123-
def handle_fstring_print(
124-
joined_str,
125-
module,
126-
builder,
127-
func,
128-
local_sym_tab=None,
129-
struct_sym_tab=None,
130-
):
131-
"""Handle f-string formatting for bpf_printk emitter."""
132-
fmt_parts = []
133-
exprs = []
134-
135-
for value in joined_str.values:
136-
logger.debug(f"Processing f-string value: {ast.dump(value)}")
137-
138-
if isinstance(value, ast.Constant):
139-
_process_constant_in_fstring(value, fmt_parts, exprs)
140-
elif isinstance(value, ast.FormattedValue):
141-
_process_fval(
142-
value,
143-
fmt_parts,
144-
exprs,
145-
local_sym_tab,
146-
struct_sym_tab,
147-
)
148-
else:
149-
raise NotImplementedError(f"Unsupported f-string value type: {type(value)}")
150-
151-
fmt_str = "".join(fmt_parts)
152-
args = simple_string_print(fmt_str, module, builder, func)
153-
154-
# NOTE: Process expressions (limited to 3 due to BPF constraints)
155-
if len(exprs) > 3:
156-
logger.warning("bpf_printk supports up to 3 args, extra args will be ignored.")
157-
158-
for expr in exprs[:3]:
159-
arg_value = _prepare_expr_args(
160-
expr,
161-
func,
162-
module,
163-
builder,
164-
local_sym_tab,
165-
struct_sym_tab,
166-
)
167-
args.append(arg_value)
168-
169-
return args
170-
171-
172-
def _process_constant_in_fstring(cst, fmt_parts, exprs):
173-
"""Process constant values in f-string."""
174-
if isinstance(cst.value, str):
175-
fmt_parts.append(cst.value)
176-
elif isinstance(cst.value, int):
177-
fmt_parts.append("%lld")
178-
exprs.append(ir.Constant(ir.IntType(64), cst.value))
179-
else:
180-
raise NotImplementedError(
181-
f"Unsupported constant type in f-string: {type(cst.value)}"
182-
)
183-
184-
185-
def _process_fval(fval, fmt_parts, exprs, local_sym_tab, struct_sym_tab):
186-
"""Process formatted values in f-string."""
187-
logger.debug(f"Processing formatted value: {ast.dump(fval)}")
188-
189-
if isinstance(fval.value, ast.Name):
190-
_process_name_in_fval(fval.value, fmt_parts, exprs, local_sym_tab)
191-
elif isinstance(fval.value, ast.Attribute):
192-
_process_attr_in_fval(
193-
fval.value,
194-
fmt_parts,
195-
exprs,
196-
local_sym_tab,
197-
struct_sym_tab,
198-
)
199-
else:
200-
raise NotImplementedError(
201-
f"Unsupported formatted value in f-string: {type(fval.value)}"
202-
)
203-
204-
205-
def _process_name_in_fval(name_node, fmt_parts, exprs, local_sym_tab):
206-
"""Process name nodes in formatted values."""
207-
if local_sym_tab and name_node.id in local_sym_tab:
208-
_, var_type, tmp = local_sym_tab[name_node.id]
209-
_populate_fval(var_type, name_node, fmt_parts, exprs)
210-
211-
212-
def _process_attr_in_fval(attr_node, fmt_parts, exprs, local_sym_tab, struct_sym_tab):
213-
"""Process attribute nodes in formatted values."""
214-
if (
215-
isinstance(attr_node.value, ast.Name)
216-
and local_sym_tab
217-
and attr_node.value.id in local_sym_tab
218-
):
219-
var_name = attr_node.value.id
220-
field_name = attr_node.attr
221-
222-
var_type = local_sym_tab[var_name].metadata
223-
if var_type not in struct_sym_tab:
224-
raise ValueError(
225-
f"Struct '{var_type}' for '{var_name}' not in symbol table"
226-
)
227-
228-
struct_info = struct_sym_tab[var_type]
229-
if field_name not in struct_info.fields:
230-
raise ValueError(f"Field '{field_name}' not found in struct '{var_type}'")
231-
232-
field_type = struct_info.field_type(field_name)
233-
_populate_fval(field_type, attr_node, fmt_parts, exprs)
234-
else:
235-
raise NotImplementedError(
236-
"Only simple attribute on local vars is supported in f-strings."
237-
)
238-
239-
240-
def _populate_fval(ftype, node, fmt_parts, exprs):
241-
"""Populate format parts and expressions based on field type."""
242-
if isinstance(ftype, ir.IntType):
243-
# TODO: We print as signed integers only for now
244-
if ftype.width == 64:
245-
fmt_parts.append("%lld")
246-
exprs.append(node)
247-
elif ftype.width == 32:
248-
fmt_parts.append("%d")
249-
exprs.append(node)
250-
else:
251-
raise NotImplementedError(
252-
f"Unsupported integer width in f-string: {ftype.width}"
253-
)
254-
elif isinstance(ftype, ir.PointerType):
255-
target, depth = get_base_type_and_depth(ftype)
256-
if isinstance(target, ir.IntType):
257-
if target.width == 64:
258-
fmt_parts.append("%lld")
259-
exprs.append(node)
260-
elif target.width == 32:
261-
fmt_parts.append("%d")
262-
exprs.append(node)
263-
elif target.width == 8 and depth == 1:
264-
# NOTE: Assume i8* is a string
265-
fmt_parts.append("%s")
266-
exprs.append(node)
267-
else:
268-
raise NotImplementedError(
269-
f"Unsupported pointer target type in f-string: {target}"
270-
)
271-
else:
272-
raise NotImplementedError(
273-
f"Unsupported pointer target type in f-string: {target}"
274-
)
275-
else:
276-
raise NotImplementedError(f"Unsupported field type in f-string: {ftype}")
277-
278-
279-
def _create_format_string_global(fmt_str, func, module, builder):
280-
"""Create a global variable for the format string."""
281-
fmt_name = f"{func.name}____fmt{func._fmt_counter}"
282-
func._fmt_counter += 1
283-
284-
fmt_gvar = ir.GlobalVariable(
285-
module, ir.ArrayType(ir.IntType(8), len(fmt_str)), name=fmt_name
286-
)
287-
fmt_gvar.global_constant = True
288-
fmt_gvar.initializer = ir.Constant(
289-
ir.ArrayType(ir.IntType(8), len(fmt_str)), bytearray(fmt_str.encode("utf8"))
290-
)
291-
fmt_gvar.linkage = "internal"
292-
fmt_gvar.align = 1
293-
294-
return builder.bitcast(fmt_gvar, ir.PointerType())
295-
296-
297-
def _prepare_expr_args(expr, func, module, builder, local_sym_tab, struct_sym_tab):
298-
"""Evaluate and prepare an expression to use as an arg for bpf_printk."""
299-
val, _ = eval_expr(
300-
func,
301-
module,
302-
builder,
303-
expr,
304-
local_sym_tab,
305-
None,
306-
struct_sym_tab,
307-
)
308-
309-
if val:
310-
if isinstance(val.type, ir.PointerType):
311-
target, depth = get_base_type_and_depth(val.type)
312-
if isinstance(target, ir.IntType):
313-
if target.width >= 32:
314-
val = deref_to_depth(func, builder, val, depth)
315-
val = builder.sext(val, ir.IntType(64))
316-
elif target.width == 8 and depth == 1:
317-
# NOTE: i8* is string, no need to deref
318-
pass
319-
320-
else:
321-
logger.warning(
322-
"Only int and ptr supported in bpf_printk args. Others default to 0."
323-
)
324-
val = ir.Constant(ir.IntType(64), 0)
325-
elif isinstance(val.type, ir.IntType):
326-
if val.type.width < 64:
327-
val = builder.sext(val, ir.IntType(64))
328-
else:
329-
logger.warning(
330-
"Only int and ptr supported in bpf_printk args. Others default to 0."
331-
)
332-
val = ir.Constant(ir.IntType(64), 0)
333-
return val
334-
else:
335-
logger.warning(
336-
"Failed to evaluate expression for bpf_printk argument. "
337-
"It will be converted to 0."
338-
)
339-
return ir.Constant(ir.IntType(64), 0)
340-
341-
342116
def get_data_ptr_and_size(data_arg, local_sym_tab, struct_sym_tab):
343117
"""Extract data pointer and size information for perf event output."""
344118
if isinstance(data_arg, ast.Name):

0 commit comments

Comments
 (0)