Skip to content

Commit 079af24

Browse files
committed
Refactor kprobe handling to a unified probe system with fprobe.
1 parent 5779a43 commit 079af24

32 files changed

+547
-247
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ KernelScript supports all major eBPF program types with typed contexts:
9393
return TC_ACT_OK
9494
}
9595
96-
// Kprobe for kernel function tracing
97-
@kprobe fn trace_syscall(ctx: *pt_regs) -> i32 {
96+
// Probe for kernel function tracing
97+
@probe fn trace_syscall(ctx: *pt_regs) -> i32 {
9898
// Trace system call entry
9999
return 0
100100
}
@@ -247,8 +247,8 @@ kernelscript init xdp my_packet_filter
247247
# Create TC project
248248
kernelscript init tc my_traffic_shaper
249249

250-
# Create kprobe project
251-
kernelscript init kprobe/sys_read my_tracer
250+
# Create probe project
251+
kernelscript init probe/sys_read my_tracer
252252

253253
# Create project with custom BTF path
254254
kernelscript init --btf-vmlinux-path /custom/path/vmlinux xdp my_project
@@ -268,7 +268,7 @@ my_project/
268268
**Available program types:**
269269
- `xdp` - XDP programs for packet processing
270270
- `tc` - Traffic control programs
271-
- `kprobe` - Kernel function tracing
271+
- `probe` - Kernel function probing
272272
- `tracepoint` - Kernel tracepoint programs
273273

274274
**Available struct_ops:**

SPEC.md

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ ebpf_program = attribute_list "fn" identifier "(" parameter_list ")" "->" return
174174
175175
attribute_list = attribute { attribute }
176176
attribute = "@" attribute_name [ "(" attribute_args ")" ]
177-
attribute_name = "xdp" | "tc" | "kprobe" | "tracepoint" |
177+
attribute_name = "xdp" | "tc" | "probe" | "tracepoint" |
178178
"struct_ops" | "kfunc" | "private" | "helper" | "test"
179179
attribute_args = string_literal | identifier
180180
@@ -185,44 +185,64 @@ return_type = type_annotation
185185

186186
**Note:** eBPF programs are now simple attributed functions. All configuration is done through global named config blocks.
187187

188-
#### 3.1.1 Advanced Kprobe Functions with BTF Signature Extraction
188+
#### 3.1.1 Advanced Probe Functions with BTF Signature Extraction and Intelligent Probe Type Selection
189189

190-
KernelScript automatically extracts kernel function signatures from BTF (BPF Type Format) for kprobe functions, eliminating the need for `KprobeContext` and providing type-safe access to function parameters.
190+
KernelScript automatically extracts kernel function signatures from BTF (BPF Type Format) for probe functions and intelligently chooses between fprobe (function entrance) and kprobe (arbitrary address) based on the target specification.
191191

192192
```kernelscript
193-
@kprobe("sys_read")
194-
fn new_style(fd: u32, buf: *u8, count: usize) -> i32 {
193+
// Function entrance probe (uses fprobe)
194+
@probe("sys_read")
195+
fn function_entrance(fd: u32, buf: *u8, count: usize) -> i32 {
195196
// Direct access to function parameters with correct types
196197
// Compiler automatically extracts signature from BTF:
197198
// long sys_read(unsigned int fd, char __user *buf, size_t count)
199+
// Uses fprobe for better performance at function entrance
198200
199201
print("Reading %d bytes from fd %d", count, fd)
200202
return 0
201203
}
204+
205+
// Arbitrary address probe (uses kprobe)
206+
@probe("vfs_read+109")
207+
fn arbitrary_address() -> i32 {
208+
// Probes specific instruction offset within vfs_read
209+
// Uses kprobe for arbitrary address probing
210+
// No direct parameters available at arbitrary addresses
211+
212+
print("Probing vfs_read at offset +109")
213+
return 0
214+
}
202215
```
203216

204217
**Key Benefits:**
205-
- **Type Safety**: Parameters have correct types extracted from kernel BTF information
206-
- **No Magic Numbers**: Direct parameter access instead of `ctx.arg_*(index)`
207-
- **Self-Documenting**: Function signature matches the actual kernel function
218+
- **Intelligent Probe Selection**: Automatically chooses fprobe for function entrance (better performance) or kprobe for arbitrary addresses
219+
- **Type Safety**: Function entrance probes have correct types extracted from kernel BTF information
220+
- **No Magic Numbers**: Direct parameter access for function entrance probes
221+
- **Self-Documenting**: Function signature matches the actual kernel function for entrance probes
208222
- **Compile-Time Validation**: Invalid parameter access caught at compile time
209223

210-
**BTF Signature Mapping:**
224+
**Probe Type Selection:**
225+
- `@probe("function_name")` → Uses **fprobe** for function entrance with direct parameter access
226+
- `@probe("function_name+offset")` → Uses **kprobe** for arbitrary address probing
227+
228+
**BTF Signature Mapping for Function Entrance:**
211229
```kernelscript
212230
// Kernel function: long sys_openat(int dfd, const char __user *filename, int flags, umode_t mode)
213-
@kprobe("sys_openat")
231+
@probe("sys_openat")
214232
fn trace_openat(dfd: i32, filename: *u8, flags: i32, mode: u16) -> i32 {
215-
// Parameters automatically mapped to PT_REGS_PARM1, PT_REGS_PARM2, etc.
233+
// Direct parameter access with fprobe (no PT_REGS needed)
216234
print("Opening file with flags %d", flags)
217235
return 0
218236
}
219237
220-
// Kernel function: long sys_write(unsigned int fd, const char __user *buf, size_t count)
221-
@kprobe("sys_write")
222-
fn trace_write(fd: u32, buf: *u8, count: usize) -> i32 {
223-
// Type-safe parameter access
224-
if (count > 1024) {
225-
print("Large write detected: %d bytes to fd %d", count, fd)
238+
// For arbitrary address probing:
239+
@probe("sys_write+50")
240+
fn trace_write_offset() -> i32 {
241+
// Uses kprobe for arbitrary offset - no direct parameters available
242+
print("Probing sys_write at offset +50")
243+
return 0
244+
}
245+
226246
}
227247
return 0
228248
}
@@ -1940,7 +1960,7 @@ struct PersonInfo {
19401960
}
19411961
19421962
// Kernel space usage - kprobe with BTF-extracted function signature
1943-
@kprobe("sys_open")
1963+
@probe("sys_open")
19441964
fn user_monitor(dfd: i32, filename: *u8, flags: i32, mode: u16) -> i32 {
19451965
var process_name: ProcessName = get_current_process_name()
19461966
var file_path: FilePath = get_file_path_from_filename(filename)
@@ -2381,7 +2401,7 @@ fn security_analyzer(ctx: LsmContext) -> i32 {
23812401
pin var global_counters : array<u32, GlobalCounter>(256)
23822402
pin var event_stream : hash<u32, Event>(1024)
23832403
2384-
@kprobe("sys_read")
2404+
@probe("sys_read")
23852405
fn producer(fd: u32, buf: *u8, count: usize) -> i32 {
23862406
var pid = bpf_get_current_pid_tgid() as u32
23872407
@@ -2401,7 +2421,7 @@ fn producer(fd: u32, buf: *u8, count: usize) -> i32 {
24012421
return 0
24022422
}
24032423
2404-
@kprobe("sys_write")
2424+
@probe("sys_write")
24052425
fn consumer(fd: u32, buf: *u8, count: usize) -> i32 {
24062426
var pid = bpf_get_current_pid_tgid() as u32
24072427
@@ -4335,7 +4355,7 @@ fn measure_write_time() -> u64 {
43354355
return bpf_ktime_get_ns()
43364356
}
43374357
4338-
@kprobe("sys_read")
4358+
@probe("sys_read")
43394359
fn perf_monitor(fd: u32, buf: *u8, count: usize) -> i32 {
43404360
var pid = bpf_get_current_pid_tgid() as u32
43414361
var call_info = CallInfo {
@@ -4371,7 +4391,7 @@ fn perf_monitor_return(ret_value: isize) -> i32 {
43714391
return 0
43724392
}
43734393
4374-
@kprobe("sys_write")
4394+
@probe("sys_write")
43754395
fn write_monitor(fd: u32, buf: *u8, count: usize) -> i32 {
43764396
var pid = bpf_get_current_pid_tgid() as u32
43774397
var duration = measure_write_time() // No context needed

examples/map_operations_demo.ks

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ fn event_logger(ctx: *trace_event_raw_sys_enter) -> i32 {
205205
}
206206

207207
// Program 4: Sequential access pattern demonstration
208-
@kprobe("vfs_read")
208+
@probe("vfs_read")
209209
fn data_processor(file: *file, buf: *u8, count: usize, pos: *i64) -> i32 {
210210
// Sequential access pattern - will be detected and optimized
211211
for (i in 0..32) {

examples/kprobe_do_exit.ks renamed to examples/probe_do_exit.ks

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
// Kprobe Example: Monitor process exit events
22
//
3-
// This example demonstrates how to use kprobe to intercept and monitor
3+
// This example demonstrates how to use probe to intercept and monitor
44
// the do_exit() kernel function, which is called when a process exits.
55
// We print the exit code parameter to see why processes are exiting.
66

77
// Target kernel function signature:
8-
// do_exit(code: u64) -> void
8+
// do_exit(code: i64) -> void
99
//
1010
// The 'code' parameter contains the exit status/signal that caused
11-
// the process to exit.
11+
// the process to exit. In the kernel, it's declared as 'long' (signed 64-bit).
1212

1313

14-
@kprobe("do_exit")
15-
fn do_exit(code: u64) -> void {
14+
@probe("do_exit")
15+
fn do_exit(code: i64) -> void {
1616
// Print the exit code parameter
1717
// This will show us the exit status/signal for the exiting process
18-
print("Process exiting with code: %u", code)
18+
print("Process exiting with code: %ld", code)
1919
return 0
2020
}
2121

@@ -24,17 +24,17 @@ fn main() -> i32 {
2424
var result = attach(prog, "do_exit", 0)
2525

2626
if (result == 0) {
27-
print("kprobe program attached to do_exit successfully")
27+
print("probe program attached to do_exit successfully")
2828
print("Monitoring process exits...")
2929

3030
// In a real scenario, you would wait for events or run for a specific time
3131
// For this example, we'll just clean up immediately
3232

3333
// Detach the program
3434
detach(prog)
35-
print("kprobe program detached")
35+
print("probe program detached")
3636
} else {
37-
print("Failed to attach kprobe program")
37+
print("Failed to attach probe program")
3838
return 1
3939
}
4040

examples/ringbuf_demo.ks

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ fn get_timestamp() -> u64 {
9292
}
9393

9494
// Security monitoring program
95-
@kprobe("sys_openat") fn security_monitor(dfd: i32, filename: *u8, flags: i32, mode: u16) -> i32 {
95+
@probe("sys_openat")
96+
fn security_monitor(dfd: i32, filename: *u8, flags: i32, mode: u16) -> i32 {
9697
var reserved = security_events.reserve()
9798
if (reserved != null) {
9899
// Successfully reserved space - populate security event inline

examples/ringbuf_on_event_demo.ks

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ var security_events : ringbuf<SecurityEvent>(8192)
4343
return XDP_PASS
4444
}
4545

46-
@kprobe("sys_openat") fn security_monitor(dfd: i32, filename: *u8, flags: i32, mode: u16) -> i32 {
46+
@probe("sys_openat")
47+
fn security_monitor(dfd: i32, filename: *u8, flags: i32, mode: u16) -> i32 {
4748
var reserved = security_events.reserve()
4849
security_events.submit(reserved)
4950
return 0

src/ast.ml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,14 @@ type attribute =
3333
| SimpleAttribute of string (* @xdp *)
3434
| AttributeWithArg of string * string (* @kprobe("sys_read") *)
3535

36+
(** Probe types for distinguishing between fprobe and kprobe *)
37+
type probe_type =
38+
| Fprobe (* Function entrance/exit probe - no offset *)
39+
| Kprobe (* Kernel probe with offset support *)
40+
3641
(** Program types supported by KernelScript *)
3742
type program_type =
38-
| Xdp | Tc | Kprobe | Tracepoint | StructOps
43+
| Xdp | Tc | Probe of probe_type | Tracepoint | StructOps
3944

4045
(** Map types for eBPF maps *)
4146
type map_type =
@@ -553,7 +558,8 @@ let string_of_position pos =
553558
let string_of_program_type = function
554559
| Xdp -> "xdp"
555560
| Tc -> "tc"
556-
| Kprobe -> "kprobe"
561+
| Probe Fprobe -> "fprobe"
562+
| Probe Kprobe -> "kprobe"
557563
| Tracepoint -> "tracepoint"
558564
| StructOps -> "struct_ops"
559565

src/btf_parser.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ let generate_kernelscript_source template project_name =
348348
| [] -> ("target_function", "fn() -> i32")
349349
in
350350
let func_def = generate_kprobe_function_from_signature first_func first_sig in
351-
(comment, first_func, func_def, Some (sprintf "@kprobe(\"%s\")" first_func))
351+
(comment, first_func, func_def, Some (sprintf "@probe(\"%s\")" first_func))
352352
else if template.program_type = "tracepoint" && template.function_signatures <> [] then
353353
let signature_lines = List.map (fun (event_name, signature) ->
354354
sprintf "// Tracepoint event: %s -> %s" event_name signature

src/context/context_codegen.ml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type context_codegen = {
4242
generate_includes: unit -> string list;
4343
generate_field_access: string -> string -> string; (* ctx_var -> field_name -> C expression *)
4444
map_action_constant: int -> string option; (* Map integer to action constant *)
45+
generate_function_signature: (string -> (string * string) list -> string -> string) option; (* func_name -> parameters -> return_type -> signature *)
4546
}
4647

4748
(** Registry for context code generators *)
@@ -95,6 +96,15 @@ let get_context_action_constants ctx_type =
9596
List.rev (collect_constants [] 0)
9697
| None -> []
9798

99+
(** Generate custom function signature for a context type *)
100+
let generate_context_function_signature ctx_type func_name parameters return_type =
101+
match get_context_codegen ctx_type with
102+
| Some codegen ->
103+
(match codegen.generate_function_signature with
104+
| Some gen_func -> Some (gen_func func_name parameters return_type)
105+
| None -> None)
106+
| None -> None
107+
98108
(** Get struct field definitions for a context type as (name, c_type) pairs *)
99109
let get_context_struct_fields ctx_type =
100110
match get_context_codegen ctx_type with
@@ -221,6 +231,7 @@ let create_context_codegen_from_btf ctx_type_name btf_type_info =
221231
generate_includes;
222232
generate_field_access;
223233
map_action_constant;
234+
generate_function_signature = None;
224235
}
225236

226237
(** Register context codegen from BTF type information *)

src/context/context_codegen.mli

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type context_codegen = {
4040
generate_includes: unit -> string list;
4141
generate_field_access: string -> string -> string;
4242
map_action_constant: int -> string option;
43+
generate_function_signature: (string -> (string * string) list -> string -> string) option;
4344
}
4445

4546
(** Register a context code generator *)
@@ -63,6 +64,9 @@ val map_context_action_constant : string -> int -> string option
6364
(** Get all action constants for a context type *)
6465
val get_context_action_constants : string -> (string * int) list
6566

67+
(** Generate custom function signature for a context type *)
68+
val generate_context_function_signature : string -> string -> (string * string) list -> string -> string option
69+
6670
(** Get struct field definitions for a context type *)
6771
val get_context_struct_fields : string -> (string * string) list
6872

0 commit comments

Comments
 (0)