Skip to content

Commit 980f2af

Browse files
authored
Merge pull request #6 from pythonbpf/refactor-maps
Refactor maps_pass
2 parents 0fb1caf + 87908e8 commit 980f2af

6 files changed

Lines changed: 113 additions & 47 deletions

File tree

examples/c-form/ex8.bpf.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/bpf.h>
3+
#include <bpf/bpf_helpers.h>
4+
#include <bpf/bpf_tracing.h>
5+
#include <linux/blkdev.h>
6+
#define __TARGET_ARCH_aarch64
7+
#define u64 unsigned long long
8+
9+
struct {
10+
__uint(type, BPF_MAP_TYPE_HASH);
11+
__uint(max_entries, 10240);
12+
__type(key, struct request *);
13+
__type(value, u64);
14+
} start SEC(".maps");
15+
16+
SEC("kprobe/blk_start_request")
17+
int BPF_KPROBE(trace_start_req, struct request *req)
18+
{
19+
u64 ts = bpf_ktime_get_ns();
20+
bpf_map_update_elem(&start, &req, &ts, BPF_ANY);
21+
return 0;
22+
}
23+
24+
SEC("kprobe/blk_mq_start_request")
25+
int BPF_KPROBE(trace_start_mq, struct request *req)
26+
{
27+
u64 ts = bpf_ktime_get_ns();
28+
bpf_map_update_elem(&start, &req, &ts, BPF_ANY);
29+
return 0;
30+
}
31+
32+
SEC("kprobe/blk_account_io_completion")
33+
int BPF_KPROBE(trace_completion, struct request *req)
34+
{
35+
u64 *tsp, delta;
36+
37+
tsp = bpf_map_lookup_elem(&start, &req);
38+
if (tsp) {
39+
delta = bpf_ktime_get_ns() - *tsp;
40+
bpf_printk("%d %x %d\n", req->__data_len,
41+
req->cmd_flags, delta / 1000);
42+
bpf_map_delete_elem(&start, &req);
43+
}
44+
return 0;
45+
}
46+
47+
char LICENSE[] SEC("license") = "GPL";

pythonbpf/codegen.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from llvmlite import ir
33
from .license_pass import license_processing
44
from .functions_pass import func_proc
5-
from .maps_pass import maps_proc
5+
from pythonbpf.maps import maps_proc
66
from .structs.structs_pass import structs_proc
77
from .globals_pass import globals_processing
88
import os
@@ -125,8 +125,8 @@ def BPF() -> BpfProgram:
125125
caller_frame = inspect.stack()[1]
126126
src = inspect.getsource(caller_frame.frame)
127127
with tempfile.NamedTemporaryFile(mode="w+", delete=True, suffix=".py") as f, \
128-
tempfile.NamedTemporaryFile(mode="w+", delete=True, suffix=".ll") as inter, \
129-
tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".o") as obj_file:
128+
tempfile.NamedTemporaryFile(mode="w+", delete=True, suffix=".ll") as inter, \
129+
tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".o") as obj_file:
130130
f.write(src)
131131
f.flush()
132132
source = f.name

pythonbpf/maps/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .maps import HashMap, PerfEventArray
2+
from .maps_pass import maps_proc
Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
11
import ast
22
from llvmlite import ir
3-
from .type_deducer import ctypes_to_ir
4-
from . import dwarf_constants as dc
3+
from pythonbpf import dwarf_constants as dc
4+
from enum import Enum
5+
from .maps_utils import MapProcessorRegistry
6+
import logging
57

6-
map_sym_tab = {}
8+
logger = logging.getLogger(__name__)
79

810

911
def maps_proc(tree, module, chunks):
12+
""" Process all functions decorated with @map to find BPF maps """
13+
map_sym_tab = {}
1014
for func_node in chunks:
11-
# Check if this function is a map
12-
is_map = False
13-
for decorator in func_node.decorator_list:
14-
if isinstance(decorator, ast.Name) and decorator.id == "map":
15-
is_map = True
16-
break
17-
if is_map:
15+
if is_map(func_node):
1816
print(f"Found BPF map: {func_node.name}")
19-
process_bpf_map(func_node, module)
20-
continue
17+
map_sym_tab[func_node.name] = process_bpf_map(func_node, module)
2118
return map_sym_tab
2219

2320

24-
BPF_MAP_MAPPINGS = {
25-
"HASH": 1, # BPF_MAP_TYPE_HASH
26-
"PERF_EVENT_ARRAY": 4, # BPF_MAP_TYPE_PERF_EVENT_ARRAY
27-
}
21+
def is_map(func_node):
22+
return any(
23+
isinstance(decorator, ast.Name) and decorator.id == "map"
24+
for decorator in func_node.decorator_list
25+
)
2826

2927

30-
def create_bpf_map(module, map_name, map_params):
31-
"""Create a BPF map in the module with the given parameters and debug info"""
28+
class BPFMapType(Enum):
29+
HASH = 1
30+
PERF_EVENT_ARRAY = 4
3231

33-
map_type_str = map_params.get("type", "HASH")
34-
map_type = BPF_MAP_MAPPINGS.get(map_type_str)
32+
33+
def create_bpf_map(module, map_name, map_params):
34+
"""Create a BPF map in the module with given parameters and debug info"""
3535

3636
# Create the anonymous struct type for BPF map
3737
map_struct_type = ir.LiteralStructType(
@@ -42,15 +42,14 @@ def create_bpf_map(module, map_name, map_params):
4242
map_global.linkage = 'dso_local'
4343
map_global.global_constant = False
4444
map_global.initializer = ir.Constant(
45-
map_struct_type, None) # type: ignore
45+
map_struct_type, None)
4646
map_global.section = ".maps"
47-
map_global.align = 8 # type: ignore
47+
map_global.align = 8
4848

4949
# Generate debug info for BTF
5050
create_map_debug_info(module, map_global, map_name, map_params)
5151

52-
print(f"Created BPF map: {map_name}")
53-
map_sym_tab[map_name] = map_global
52+
logger.info(f"Created BPF map: {map_name} with params {map_params}")
5453
return map_global
5554

5655

@@ -75,7 +74,7 @@ def create_map_debug_info(module, map_global, map_name, map_params):
7574

7675
# Create array type for map type field (array of 1 unsigned int)
7776
array_subrange = module.add_debug_info(
78-
"DISubrange", {"count": BPF_MAP_MAPPINGS[map_params.get("type", "HASH")]})
77+
"DISubrange", {"count": map_params.get("type", BPFMapType.HASH).value})
7978
array_type = module.add_debug_info("DICompositeType", {
8079
"tag": dc.DW_TAG_array_type,
8180
"baseType": uint_type,
@@ -183,9 +182,11 @@ def create_map_debug_info(module, map_global, map_name, map_params):
183182
return global_var_expr
184183

185184

185+
@MapProcessorRegistry.register("HashMap")
186186
def process_hash_map(map_name, rval, module):
187-
print(f"Creating HashMap map: {map_name}")
188-
map_params: dict[str, object] = {"type": "HASH"}
187+
"""Process a BPF_HASH map declaration"""
188+
logger.info(f"Processing HashMap: {map_name}")
189+
map_params = {"type": BPFMapType.HASH}
189190

190191
# Assuming order: key_type, value_type, max_entries
191192
if len(rval.args) >= 1 and isinstance(rval.args[0], ast.Name):
@@ -202,18 +203,21 @@ def process_hash_map(map_name, rval, module):
202203
map_params["key"] = keyword.value.id
203204
elif keyword.arg == "value" and isinstance(keyword.value, ast.Name):
204205
map_params["value"] = keyword.value.id
205-
elif keyword.arg == "max_entries" and isinstance(keyword.value, ast.Constant):
206+
elif (keyword.arg == "max_entries" and
207+
isinstance(keyword.value, ast.Constant)):
206208
const_val = keyword.value.value
207209
if isinstance(const_val, (int, str)):
208210
map_params["max_entries"] = const_val
209211

210-
print(f"Map parameters: {map_params}")
212+
logger.info(f"Map parameters: {map_params}")
211213
return create_bpf_map(module, map_name, map_params)
212214

213215

216+
@MapProcessorRegistry.register("PerfEventArray")
214217
def process_perf_event_map(map_name, rval, module):
215-
print(f"Creating PerfEventArray map: {map_name}")
216-
map_params = {"type": "PERF_EVENT_ARRAY"}
218+
"""Process a BPF_PERF_EVENT_ARRAY map declaration"""
219+
logger.info(f"Processing PerfEventArray: {map_name}")
220+
map_params = {"type": BPFMapType.PERF_EVENT_ARRAY}
217221

218222
if len(rval.args) >= 1 and isinstance(rval.args[0], ast.Name):
219223
map_params["key_size"] = rval.args[0].id
@@ -223,21 +227,18 @@ def process_perf_event_map(map_name, rval, module):
223227
for keyword in rval.keywords:
224228
if keyword.arg == "key_size" and isinstance(keyword.value, ast.Name):
225229
map_params["key_size"] = keyword.value.id
226-
elif keyword.arg == "value_size" and isinstance(keyword.value, ast.Name):
230+
elif (keyword.arg == "value_size" and
231+
isinstance(keyword.value, ast.Name)):
227232
map_params["value_size"] = keyword.value.id
228233

229-
print(f"Map parameters: {map_params}")
234+
logger.info(f"Map parameters: {map_params}")
230235
return create_bpf_map(module, map_name, map_params)
231236

232237

233238
def process_bpf_map(func_node, module):
234239
"""Process a BPF map (a function decorated with @map)"""
235240
map_name = func_node.name
236-
print(f"Processing BPF map: {map_name}")
237-
238-
BPF_MAP_TYPES = {"HashMap": process_hash_map, # BPF_MAP_TYPE_HASH
239-
"PerfEventArray": process_perf_event_map, # BPF_MAP_TYPE_PERF_EVENT_ARRAY
240-
}
241+
logger.info(f"Processing BPF map: {map_name}")
241242

242243
# For now, assume single return statement
243244
return_stmt = None
@@ -250,13 +251,13 @@ def process_bpf_map(func_node, module):
250251

251252
rval = return_stmt.value
252253

253-
# Handle only HashMap maps
254254
if isinstance(rval, ast.Call) and isinstance(rval.func, ast.Name):
255-
if rval.func.id in BPF_MAP_TYPES:
256-
handler = BPF_MAP_TYPES[rval.func.id]
257-
handler(map_name, rval, module)
255+
handler = MapProcessorRegistry.get_processor(rval.func.id)
256+
if handler:
257+
return handler(map_name, rval, module)
258258
else:
259-
print(f"Unknown map type {rval.func.id}, defaulting to HashMap")
260-
process_hash_map(map_name, rval, module)
259+
logger.warning(f"Unknown map type "
260+
f"{rval.func.id}, defaulting to HashMap")
261+
return process_hash_map(map_name, rval, module)
261262
else:
262263
raise ValueError("Function under @map must return a map")

pythonbpf/maps/maps_utils.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class MapProcessorRegistry:
2+
"""Registry for map processor functions"""
3+
_processors = {}
4+
5+
@classmethod
6+
def register(cls, map_type_name):
7+
"""Decorator to register a processor function for a map type"""
8+
def decorator(func):
9+
cls._processors[map_type_name] = func
10+
return func
11+
return decorator
12+
13+
@classmethod
14+
def get_processor(cls, map_type_name):
15+
"""Get the processor function for a map type"""
16+
return cls._processors.get(map_type_name)

0 commit comments

Comments
 (0)