Skip to content

Commit 64f847d

Browse files
committed
Add tracepoint support. Implement tracepoint program template and enhance BTF extraction.
1 parent 8803b95 commit 64f847d

12 files changed

Lines changed: 790 additions & 79 deletions

SPEC.md

Lines changed: 120 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ var flows : hash<IpAddress, PacketStats>(1024)
3535
KernelScript uses a simple and clear scoping model that eliminates ambiguity:
3636

3737
- **`@helper` functions**: Kernel-shared functions - accessible by all eBPF programs, compile to eBPF bytecode
38-
- **Attributed functions** (e.g., `@xdp`, `@tc`): eBPF program entry points - compile to eBPF bytecode
38+
- **Attributed functions** (e.g., `@xdp`, `@tc`, `@tracepoint`): eBPF program entry points - compile to eBPF bytecode
3939
- **Regular functions**: User space - functions and data structures compile to native executable
4040
- **Maps and global configs**: Shared resources accessible from both kernel and user space
4141
- **No wrapper syntax**: Direct, flat structure without unnecessary nesting
@@ -234,6 +234,121 @@ fn trace_write(fd: u32, buf: *u8, count: usize) -> i32 {
234234
- Validates parameter count (maximum 6 on x86_64)
235235
- Provides meaningful error messages for unknown functions
236236

237+
#### 3.1.2 Tracepoint Functions with BTF Event Structure Extraction
238+
239+
KernelScript automatically extracts tracepoint event structures from BTF (BPF Type Format) for tracepoint functions, providing type-safe access to tracepoint event data through the appropriate `trace_event_raw_*` structures.
240+
241+
```kernelscript
242+
@tracepoint("sched/sched_switch")
243+
fn sched_switch_handler(ctx: *trace_event_raw_sched_switch) -> i32 {
244+
// Direct access to tracepoint event fields with correct types
245+
// Compiler automatically extracts structure from BTF:
246+
// struct trace_event_raw_sched_switch {
247+
// struct trace_entry ent;
248+
// char prev_comm[16];
249+
// pid_t prev_pid;
250+
// int prev_prio;
251+
// long prev_state;
252+
// char next_comm[16];
253+
// pid_t next_pid;
254+
// int next_prio;
255+
// ...
256+
// }
257+
258+
print("Task switch: %s[%d] -> %s[%d]",
259+
ctx.prev_comm, ctx.prev_pid,
260+
ctx.next_comm, ctx.next_pid)
261+
return 0
262+
}
263+
264+
@tracepoint("syscalls/sys_enter_read")
265+
fn sys_enter_read_handler(ctx: *trace_event_raw_sys_enter) -> i32 {
266+
// Syscall tracepoints use generic sys_enter structure
267+
// struct trace_event_raw_sys_enter {
268+
// struct trace_entry ent;
269+
// long id;
270+
// unsigned long args[6];
271+
// }
272+
273+
var fd = ctx.args[0]
274+
var count = ctx.args[2]
275+
print("sys_read: fd=%d, count=%d", fd, count)
276+
return 0
277+
}
278+
279+
@tracepoint("net/netif_rx")
280+
fn netif_rx_handler(ctx: *trace_event_raw_netif_rx) -> i32 {
281+
// Network tracepoint with packet information
282+
print("Network packet received")
283+
return 0
284+
}
285+
```
286+
287+
**Key Benefits:**
288+
- **Event Structure Access**: Direct access to tracepoint event fields with correct types
289+
- **Category/Event Organization**: Clear separation using `category/event` format
290+
- **BTF Integration**: Automatic extraction of `trace_event_raw_*` structures from kernel BTF
291+
- **Compile-Time Safety**: Type checking for tracepoint context structures
292+
- **Flexible Event Types**: Support for scheduler, syscall, network, and custom tracepoints
293+
294+
**BTF Structure Mapping:**
295+
```kernelscript
296+
// Scheduler tracepoints: trace_event_raw_<event_name>
297+
@tracepoint("sched/sched_wakeup")
298+
fn wakeup_handler(ctx: *trace_event_raw_sched_wakeup) -> i32 {
299+
// Access scheduler-specific fields
300+
print("Waking up PID %d", ctx.pid)
301+
return 0
302+
}
303+
304+
// Syscall enter tracepoints: trace_event_raw_sys_enter (generic)
305+
@tracepoint("syscalls/sys_enter_open")
306+
fn open_handler(ctx: *trace_event_raw_sys_enter) -> i32 {
307+
// Access syscall arguments through args array
308+
var filename_ptr = ctx.args[0]
309+
var flags = ctx.args[1]
310+
print("Opening file with flags %d", flags)
311+
return 0
312+
}
313+
314+
// Syscall exit tracepoints: trace_event_raw_sys_exit (generic)
315+
@tracepoint("syscalls/sys_exit_read")
316+
fn read_exit_handler(ctx: *trace_event_raw_sys_exit) -> i32 {
317+
// Access return value
318+
print("sys_read returned %d", ctx.ret)
319+
return 0
320+
}
321+
322+
// Custom subsystem tracepoints: trace_event_raw_<event_name>
323+
@tracepoint("block/block_rq_complete")
324+
fn block_complete_handler(ctx: *trace_event_raw_block_rq_complete) -> i32 {
325+
// Access block layer specific fields
326+
return 0
327+
}
328+
```
329+
330+
**Compiler Implementation:**
331+
- Automatically determines BTF structure name based on category/event:
332+
- `syscalls/sys_enter_*``trace_event_raw_sys_enter`
333+
- `syscalls/sys_exit_*``trace_event_raw_sys_exit`
334+
- `<category>/<event>``trace_event_raw_<event>`
335+
- Extracts tracepoint structure definitions from kernel BTF information
336+
- Generates appropriate `SEC("tracepoint")` section for eBPF programs
337+
- Validates tracepoint context parameter types at compile time
338+
- Provides meaningful error messages for unknown tracepoints
339+
340+
**Project Initialization:**
341+
```bash
342+
# Initialize project with specific tracepoint
343+
kernelscript init tracepoint/sched/sched_switch my_scheduler_tracer
344+
345+
# Initialize project with syscall tracepoint
346+
kernelscript init tracepoint/syscalls/sys_enter_read my_syscall_tracer
347+
348+
# The init command automatically extracts BTF structures and generates
349+
# appropriate KernelScript templates with correct context types
350+
```
351+
237352
### 3.2 Named Configuration Blocks
238353
```kernelscript
239354
// Named configuration blocks - globally accessible
@@ -545,7 +660,7 @@ fn main() -> i32 {
545660
### 3.4 Kernel-Userspace Scoping Model
546661

547662
KernelScript uses a simple and intuitive scoping model:
548-
- **Attributed functions** (e.g., `@xdp`, `@tc`): Kernel space (eBPF) - compiles to eBPF bytecode
663+
- **Attributed functions** (e.g., `@xdp`, `@tc`, `@tracepoint`): Kernel space (eBPF) - compiles to eBPF bytecode
549664
- **`@kfunc` functions**: Kernel modules (full privileges) - exposed to eBPF programs via BTF
550665
- **`@private` functions**: Kernel modules (full privileges) - internal helpers for kfuncs
551666
- **Regular functions**: User space - compiles to native executable
@@ -2426,7 +2541,7 @@ value = (value % modulus); // From: value %= modulus
24262541
KernelScript functions support both traditional unnamed return types and modern named return values. The complete grammar is defined in Section 15 (Complete Formal Grammar).
24272542

24282543
Key function types:
2429-
- **eBPF program functions**: Attributed with `@xdp`, `@tc`, etc. - compile to eBPF bytecode
2544+
- **eBPF program functions**: Attributed with `@xdp`, `@tc`, `@tracepoint`, etc. - compile to eBPF bytecode
24302545
- **Helper functions**: Attributed with `@helper` - shared across all eBPF programs
24312546
- **Userspace functions**: No attributes - compile to native executable
24322547

@@ -3837,12 +3952,6 @@ mod test {
38373952
// test(program_function, test_context) -> return_value
38383953
// var result = test(packet_filter, xdp_test_ctx) // Returns XDP action
38393954
// var retval = test(kprobe_handler, kprobe_test_ctx) // Returns kprobe return value
3840-
//
3841-
// The test context struct must match the program type being tested:
3842-
// - XdpTestContext for @xdp programs
3843-
// - TcTestContext for @tc programs
3844-
// - KprobeTestContext for @kprobe programs
3845-
// - etc.
38463955
pub fn test(program_function: FunctionRef, test_context: TestContext) -> i32
38473956
}
38483957
```
@@ -4316,7 +4425,7 @@ global_variable_declaration = [ "pin" ] [ "local" ] "var" identifier [ ":" type_
43164425
*)
43174426
43184427
(* Scoping rules for KernelScript:
4319-
- Attributed functions (e.g., @xdp, @tc): Kernel space (eBPF) - compiles to eBPF bytecode
4428+
- Attributed functions (e.g., @xdp, @tc, @tracepoint): Kernel space (eBPF) - compiles to eBPF bytecode
43204429
- Regular functions: User space - compiles to native executable
43214430
- Maps, global configs, and global variables: Shared between both kernel and user space
43224431
@@ -4507,7 +4616,7 @@ whitespace = " " | "\t" | "\n" | "\r"
45074616

45084617
**Function Structure:**
45094618
- `function_declaration` defines functions with optional attributes
4510-
- Functions with attributes (e.g., `@xdp`, `@tc`) are eBPF programs
4619+
- Functions with attributes (e.g., `@xdp`, `@tc`, `@tracepoint`) are eBPF programs
45114620
- Functions without attributes are userspace functions
45124621
- `@helper` functions are shared across all eBPF programs
45134622

src/btf_parser.ml

Lines changed: 106 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ let create_hardcoded_tc_action_enum () = {
6161
}
6262

6363
(** Get program template based on eBPF program type *)
64-
let rec get_program_template prog_type btf_path =
64+
let get_program_template prog_type btf_path =
6565
let (context_type, return_type, common_types) = match prog_type with
6666
| "xdp" -> ("xdp_md", "xdp_action", [
6767
"xdp_md"; "xdp_action"
@@ -89,7 +89,16 @@ let rec get_program_template prog_type btf_path =
8989

9090
(* Extract types from BTF - BTF file is required *)
9191
let extracted_types = match btf_path with
92-
| Some path when Sys.file_exists path -> extract_types_from_btf path common_types
92+
| Some path when Sys.file_exists path ->
93+
let binary_types = Btf_binary_parser.parse_btf_file path common_types in
94+
(* Convert binary parser types to btf_type_info *)
95+
List.map (fun bt -> {
96+
name = bt.Btf_binary_parser.name;
97+
kind = bt.Btf_binary_parser.kind;
98+
size = bt.Btf_binary_parser.size;
99+
members = bt.Btf_binary_parser.members;
100+
kernel_defined = is_well_known_kernel_type bt.Btf_binary_parser.name;
101+
}) binary_types
93102
| Some path -> failwith (sprintf "BTF file not found: %s" path)
94103
| None -> failwith "BTF file path is required. Use --btf-vmlinux-path option."
95104
in
@@ -119,32 +128,57 @@ let rec get_program_template prog_type btf_path =
119128
function_signatures = function_signatures;
120129
}
121130

122-
(** Extract specific types from BTF file using binary parser *)
123-
and extract_types_from_btf btf_path type_names =
124-
try
125-
printf "Extracting types from BTF file: %s\n" btf_path;
126-
let binary_types = Btf_binary_parser.parse_btf_file btf_path type_names in
127-
128-
(* Convert binary parser types to btf_type_info *)
129-
let converted_types = List.map (fun bt ->
130-
{
131-
name = bt.Btf_binary_parser.name;
132-
kind = bt.Btf_binary_parser.kind;
133-
size = bt.Btf_binary_parser.size;
134-
members = bt.Btf_binary_parser.members;
135-
kernel_defined = is_well_known_kernel_type bt.Btf_binary_parser.name;
136-
}
137-
) binary_types in
138-
139-
if List.length converted_types > 0 then (
140-
printf "Successfully extracted %d types from BTF\n" (List.length converted_types);
141-
converted_types
142-
) else (
143-
failwith "No types extracted from BTF - requested types not found"
144-
)
145-
with
146-
| exn ->
147-
failwith (sprintf "BTF extraction failed: %s" (Printexc.to_string exn))
131+
(** Get tracepoint program template for a specific category/event *)
132+
let get_tracepoint_program_template category_event btf_path =
133+
(* Parse category and event from the category_event string *)
134+
let (category, event) =
135+
if String.contains category_event '/' then
136+
let parts = String.split_on_char '/' category_event in
137+
match parts with
138+
| [cat; evt] -> (cat, evt)
139+
| _ -> failwith (sprintf "Invalid tracepoint format '%s'. Use 'category/event'" category_event)
140+
else
141+
failwith (sprintf "Invalid tracepoint format '%s'. Use 'category/event'" category_event)
142+
in
143+
144+
(* Determine typedef_name and raw_name based on the user's logic *)
145+
let (typedef_name, raw_name) =
146+
if category = "syscalls" && String.starts_with event ~prefix:"sys_enter_" then
147+
("btf_trace_sys_enter", "trace_event_raw_sys_enter")
148+
else if category = "syscalls" && String.starts_with event ~prefix:"sys_exit_" then
149+
("btf_trace_sys_exit", "trace_event_raw_sys_exit")
150+
else
151+
(sprintf "btf_trace_%s" event, sprintf "trace_event_raw_%s" event)
152+
in
153+
154+
(* Extract the tracepoint structure from BTF *)
155+
let common_types = [raw_name; typedef_name] in
156+
let extracted_types = match btf_path with
157+
| Some path when Sys.file_exists path ->
158+
let binary_types = Btf_binary_parser.parse_btf_file path common_types in
159+
(* Convert binary parser types to btf_type_info *)
160+
List.map (fun bt -> {
161+
name = bt.Btf_binary_parser.name;
162+
kind = bt.Btf_binary_parser.kind;
163+
size = bt.Btf_binary_parser.size;
164+
members = bt.Btf_binary_parser.members;
165+
kernel_defined = is_well_known_kernel_type bt.Btf_binary_parser.name;
166+
}) binary_types
167+
| Some path -> failwith (sprintf "BTF file not found: %s" path)
168+
| None -> failwith "BTF file path is required for tracepoint extraction. Use --btf-vmlinux-path option."
169+
in
170+
171+
(* Create the context type from the extracted struct *)
172+
let context_type = sprintf "*%s" raw_name in
173+
174+
{
175+
program_type = "tracepoint";
176+
context_type = context_type;
177+
return_type = "i32";
178+
includes = ["linux/bpf.h"; "bpf/bpf_helpers.h"; "bpf/bpf_tracing.h"; "linux/trace_events.h"];
179+
types = extracted_types;
180+
function_signatures = [(sprintf "%s/%s" category event, sprintf "fn(%s) -> i32" context_type)];
181+
}
148182

149183
(** Get kprobe program template for a specific target function *)
150184
let get_kprobe_program_template target_function btf_path =
@@ -312,8 +346,8 @@ let generate_kernelscript_source template project_name =
312346
| [] -> "0"
313347
in
314348

315-
(* Generate function signature comments and actual function definition for kprobe programs *)
316-
let (function_signatures_comment, target_function_name, function_definition) =
349+
(* Generate function signature comments and actual function definition for specific program types *)
350+
let (function_signatures_comment, target_function_name, function_definition, custom_attribute) =
317351
if template.program_type = "kprobe" && template.function_signatures <> [] then
318352
let signature_lines = List.map (fun (func_name, signature) ->
319353
sprintf "// Target function: %s -> %s" func_name signature
@@ -325,27 +359,58 @@ let generate_kernelscript_source template project_name =
325359
| [] -> ("target_function", "fn() -> i32")
326360
in
327361
let func_def = generate_kprobe_function_from_signature first_func first_sig in
328-
(comment, first_func, func_def)
362+
(comment, first_func, func_def, Some (sprintf "@kprobe(\"%s\")" first_func))
363+
else if template.program_type = "tracepoint" && template.function_signatures <> [] then
364+
let signature_lines = List.map (fun (event_name, signature) ->
365+
sprintf "// Tracepoint event: %s -> %s" event_name signature
366+
) template.function_signatures in
367+
let comment = sprintf "\n// Tracepoint event signature:\n%s\n"
368+
(String.concat "\n" signature_lines) in
369+
let first_event, _first_sig = match template.function_signatures with
370+
| (name, sig_str) :: _ -> (name, sig_str)
371+
| [] -> ("category/event", "fn(void*) -> i32")
372+
in
373+
let func_def = sprintf "fn %s_handler(ctx: %s) -> %s"
374+
(String.map (function '/' -> '_' | c -> c) first_event) template.context_type template.return_type in
375+
(comment, first_event, func_def, Some (sprintf "@tracepoint(\"%s\")" first_event))
329376
else
330-
("", "target_function", sprintf "fn %s_handler(ctx: %s) -> %s" project_name template.context_type template.return_type)
377+
("", "target_function", sprintf "fn %s_handler(ctx: %s) -> %s" project_name template.context_type template.return_type, None)
331378
in
332379

333-
(* Customize attach call for kprobe *)
334-
let attach_target = if template.program_type = "kprobe" then target_function_name else "eth0" in
335-
let attach_comment = if template.program_type = "kprobe" then
336-
" // Attach kprobe to target kernel function"
337-
else
338-
" // TODO: Update interface name and attachment parameters"
380+
(* Use custom attribute if available, otherwise use generic program type attribute *)
381+
let attribute_line = match custom_attribute with
382+
| Some attr -> attr
383+
| None -> "@" ^ template.program_type
339384
in
340385

341-
let function_name = if template.program_type = "kprobe" then target_function_name else sprintf "%s_handler" project_name in
386+
(* Customize attach call for kprobe/tracepoint *)
387+
let attach_target =
388+
if template.program_type = "kprobe" then target_function_name
389+
else if template.program_type = "tracepoint" then target_function_name
390+
else "eth0"
391+
in
392+
let attach_comment =
393+
if template.program_type = "kprobe" then
394+
" // Attach kprobe to target kernel function"
395+
else if template.program_type = "tracepoint" then
396+
" // Attach tracepoint to target kernel event"
397+
else
398+
" // TODO: Update interface name and attachment parameters"
399+
in
400+
401+
let function_name =
402+
if template.program_type = "kprobe" then target_function_name
403+
else if template.program_type = "tracepoint" then
404+
String.map (function '/' -> '_' | c -> c) target_function_name ^ "_handler"
405+
else sprintf "%s_handler" project_name
406+
in
342407

343408
sprintf {|%s
344409
// Generated by KernelScript compiler with direct BTF parsing
345410
%s
346411
%s
347412

348-
@%s
413+
%s
349414
%s {
350415
// TODO: Implement your %s logic here
351416

@@ -367,4 +432,4 @@ fn main() -> i32 {
367432

368433
return 0
369434
}
370-
|} context_comment function_signatures_comment type_definitions template.program_type function_definition template.program_type sample_return function_name attach_comment attach_target template.program_type template.program_type
435+
|} context_comment function_signatures_comment type_definitions attribute_line function_definition template.program_type sample_return function_name attach_comment attach_target template.program_type template.program_type

src/btf_parser.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ val get_program_template : string -> string option -> program_template
3939
(** Get kprobe program template for a specific target function *)
4040
val get_kprobe_program_template : string -> string option -> program_template
4141

42+
(** Get tracepoint program template for a specific target function *)
43+
val get_tracepoint_program_template : string -> string option -> program_template
44+
4245
(** Check if a type name is a well-known eBPF kernel type *)
4346
val is_well_known_kernel_type : string -> bool
4447

src/context/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(library
22
(public_name kernelscript.context)
33
(name kernelscript_context)
4-
(modules context_codegen xdp_codegen tc_codegen kprobe_codegen)
4+
(modules context_codegen xdp_codegen tc_codegen kprobe_codegen tracepoint_codegen)
55
(libraries unix str))

0 commit comments

Comments
 (0)