@@ -144,6 +144,55 @@ return_type = type_annotation
144144
145145** Note:** eBPF programs are now simple attributed functions. All configuration is done through global named config blocks.
146146
147+ #### 3.1.1 Advanced Kprobe Functions with BTF Signature Extraction
148+
149+ 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.
150+
151+ ``` kernelscript
152+ @kprobe("sys_read")
153+ fn new_style(fd: u32, buf: *u8, count: usize) -> i32 {
154+ // Direct access to function parameters with correct types
155+ // Compiler automatically extracts signature from BTF:
156+ // long sys_read(unsigned int fd, char __user *buf, size_t count)
157+
158+ print("Reading %d bytes from fd %d", count, fd)
159+ return 0
160+ }
161+ ```
162+
163+ ** Key Benefits:**
164+ - ** Type Safety** : Parameters have correct types extracted from kernel BTF information
165+ - ** No Magic Numbers** : Direct parameter access instead of ` ctx.arg_*(index) `
166+ - ** Self-Documenting** : Function signature matches the actual kernel function
167+ - ** Compile-Time Validation** : Invalid parameter access caught at compile time
168+
169+ ** BTF Signature Mapping:**
170+ ``` kernelscript
171+ // Kernel function: long sys_openat(int dfd, const char __user *filename, int flags, umode_t mode)
172+ @kprobe("sys_openat")
173+ fn trace_openat(dfd: i32, filename: *u8, flags: i32, mode: u16) -> i32 {
174+ // Parameters automatically mapped to PT_REGS_PARM1, PT_REGS_PARM2, etc.
175+ print("Opening file with flags %d", flags)
176+ return 0
177+ }
178+
179+ // Kernel function: long sys_write(unsigned int fd, const char __user *buf, size_t count)
180+ @kprobe("sys_write")
181+ fn trace_write(fd: u32, buf: *u8, count: usize) -> i32 {
182+ // Type-safe parameter access
183+ if (count > 1024) {
184+ print("Large write detected: %d bytes to fd %d", count, fd)
185+ }
186+ return 0
187+ }
188+ ```
189+
190+ ** Compiler Implementation:**
191+ - Automatically queries BTF information for the target kernel function
192+ - Generates parameter mappings to ` PT_REGS_PARM* ` macros
193+ - Validates parameter count (maximum 6 on x86_64)
194+ - Provides meaningful error messages for unknown functions
195+
147196### 3.2 Named Configuration Blocks
148197``` kernelscript
149198// Named configuration blocks - globally accessible
@@ -1702,21 +1751,20 @@ struct PersonInfo {
17021751 status: ShortString, // str(32)
17031752}
17041753
1705- // Kernel space usage
1706- program user_monitor : kprobe("sys_open") {
1707- fn main(ctx: KprobeContext) -> i32 {
1708- var process_name: ProcessName = get_current_process_name()
1709- var file_path: FilePath = get_file_path(ctx)
1710-
1711- // String operations work the same in kernel space
1712- if (process_name == "malware") {
1713- var log_msg: LogMessage = "Blocked process: " + process_name
1714- print(log_msg)
1715- return -1
1716- }
1717-
1718- return 0
1754+ // Kernel space usage - kprobe with BTF-extracted function signature
1755+ @kprobe("sys_open")
1756+ fn user_monitor(dfd: i32, filename: *u8, flags: i32, mode: u16) -> i32 {
1757+ var process_name: ProcessName = get_current_process_name()
1758+ var file_path: FilePath = get_file_path_from_filename(filename)
1759+
1760+ // String operations work the same in kernel space
1761+ if (process_name == "malware") {
1762+ var log_msg: LogMessage = "Blocked process: " + process_name
1763+ print(log_msg)
1764+ return -1
17191765 }
1766+
1767+ return 0
17201768}
17211769
17221770// Userspace usage
@@ -2147,7 +2195,7 @@ pin map<u32, GlobalCounter> global_counters : Array(256)
21472195pin map<Event> event_stream : RingBuffer(1024 * 1024)
21482196
21492197@kprobe("sys_read")
2150- fn producer(ctx: KprobeContext ) -> i32 {
2198+ fn producer(fd: u32, buf: *u8, count: usize ) -> i32 {
21512199 var pid = bpf_get_current_pid_tgid() as u32
21522200
21532201 // Update global counter (accessible by other programs)
@@ -2157,6 +2205,8 @@ fn producer(ctx: KprobeContext) -> i32 {
21572205 var event = Event {
21582206 pid: pid,
21592207 syscall: "read",
2208+ fd: fd,
2209+ bytes_requested: count,
21602210 timestamp: bpf_ktime_get_ns(),
21612211 }
21622212 event_stream.submit(event)
@@ -2165,14 +2215,14 @@ fn producer(ctx: KprobeContext) -> i32 {
21652215}
21662216
21672217@kprobe("sys_write")
2168- fn consumer(ctx: KprobeContext ) -> i32 {
2218+ fn consumer(fd: u32, buf: *u8, count: usize ) -> i32 {
21692219 var pid = bpf_get_current_pid_tgid() as u32
21702220
21712221 // Access global counter (same map as producer program)
21722222 var read_count = global_counters[pid % 256]
21732223
2174- // Process the read count data
2175- process_read_count (read_count)
2224+ // Process the write count data with actual parameters
2225+ process_write_count (read_count, fd, count )
21762226
21772227 return 0
21782228}
@@ -4075,45 +4125,57 @@ fn analyze_syscall_performance(pid: u32, syscall_nr: u32, bytes: u32, duration:
40754125
40764126// Kernel-shared function for measuring write time
40774127@helper
4078- fn measure_write_time(ctx: KprobeContext ) -> u64 {
4128+ fn measure_write_time() -> u64 {
40794129 return bpf_ktime_get_ns()
40804130}
40814131
40824132@kprobe("sys_read")
4083- fn perf_monitor(ctx: KprobeContext ) -> i32 {
4133+ fn perf_monitor(fd: u32, buf: *u8, count: usize ) -> i32 {
40844134 var pid = bpf_get_current_pid_tgid() as u32
40854135 var call_info = CallInfo {
40864136 start_time: bpf_ktime_get_ns(),
4087- bytes_requested: ctx.arg_u32(2),
4137+ bytes_requested: count, // Use actual parameter instead of ctx.arg_u32(2)
4138+ file_descriptor: fd, // Access file descriptor directly
40884139 }
40894140
40904141 active_calls[pid] = call_info
40914142 return 0
40924143}
40934144
40944145@kretprobe("sys_read")
4095- fn perf_monitor_return(ctx: KretprobeContext ) -> i32 {
4146+ fn perf_monitor_return(ret_value: isize ) -> i32 {
40964147 var pid = bpf_get_current_pid_tgid() as u32
40974148
40984149 var call_info = active_calls[pid]
40994150 if (call_info != null) {
41004151 var duration = bpf_ktime_get_ns() - call_info.start_time
41014152 read_stats[pid % 1024] += duration
41024153
4103- // Use kfunc for detailed analysis and logging
4154+ // Use actual return value and kfunc for detailed analysis and logging
41044155 analyze_syscall_performance(pid, __NR_read, call_info.bytes_requested, duration)
41054156
4157+ // Log if the syscall failed (negative return value)
4158+ if (ret_value < 0) {
4159+ print("sys_read failed with error: %d", ret_value)
4160+ }
4161+
41064162 delete active_calls[pid]
41074163 }
41084164
41094165 return 0
41104166}
41114167
41124168@kprobe("sys_write")
4113- fn write_monitor(ctx: KprobeContext ) -> i32 {
4169+ fn write_monitor(fd: u32, buf: *u8, count: usize ) -> i32 {
41144170 var pid = bpf_get_current_pid_tgid() as u32
4115- var duration = measure_write_time(ctx)
4171+ var duration = measure_write_time() // No context needed
41164172 write_stats[pid % 1024] += duration
4173+
4174+ // Log write operation with actual parameters
4175+ if (count > 0) {
4176+ print("Process %d writing %d bytes to fd %d", pid, count, fd)
4177+ }
4178+
41174179 return 0
41184180}
41194181
0 commit comments