diff --git a/BCC-Examples/disksnoop.py b/BCC-Examples/disksnoop.py new file mode 100644 index 0000000..d758c0e --- /dev/null +++ b/BCC-Examples/disksnoop.py @@ -0,0 +1,57 @@ +from vmlinux import struct_request, struct_pt_regs +from pythonbpf import bpf, section, bpfglobal, compile_to_ir, compile, map +from pythonbpf.helper import ktime +from pythonbpf.maps import HashMap +import logging +from ctypes import c_int64, c_uint64, c_uint32, c_int32 + +# Constants +REQ_WRITE = 1 # from include/linux/blk_types.h + +@bpf +@map +def start() -> HashMap: + return HashMap(key=c_uint64, value=c_uint64, max_entries=10240) + +@bpf +@section("kprobe/blk_mq_end_request") +def trace_completion(ctx: struct_pt_regs) -> c_int64: + # Get request pointer from first argument + req_ptr = ctx.di + req = struct_request(ctx.di) + # Print: data_len, cmd_flags, latency_us + data_len = req.__data_len + cmd_flags = req.cmd_flags + # Lookup start timestamp + req_tsp = start.lookup(req_ptr) + if req_tsp: + # Calculate delta in nanoseconds + delta = ktime() - req_tsp + + # Convert to microseconds for printing + delta_us = delta // 1000 + + print(f"{data_len} {cmd_flags:x} {delta_us}\n") + + # Delete the entry + start.delete(req_ptr) + + return c_int64(0) + +@bpf +@section("kprobe/blk_mq_start_request") +def trace_start(ctx1: struct_pt_regs) -> c_int32: + req = ctx1.di + ts = ktime() + start.update(req, ts) + return c_int32(0) + +@bpf +@bpfglobal +def LICENSE() -> str: + return "GPL" + + +if __name__ == "__main__": + compile_to_ir("disksnoop.py", "disksnoop.ll", loglevel=logging.INFO) + compile() diff --git a/tests/c-form/disksnoop.bpf.c b/tests/c-form/disksnoop.bpf.c new file mode 100644 index 0000000..79582f7 --- /dev/null +++ b/tests/c-form/disksnoop.bpf.c @@ -0,0 +1,66 @@ +// disksnoop.bpf.c +// eBPF program (compile with: clang -O2 -g -target bpf -c disksnoop.bpf.c -o disksnoop.bpf.o) + +#include "vmlinux.h" +#include +#include + +char LICENSE[] SEC("license") = "GPL"; + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, __u64); + __type(value, __u64); + __uint(max_entries, 10240); +} start_map SEC(".maps"); + +/* kprobe: record start timestamp keyed by request pointer */ +SEC("kprobe/blk_mq_start_request") +int trace_start(struct pt_regs *ctx) +{ + /* request * is first arg */ + __u64 reqp = (__u64)(ctx->di); + __u64 ts = bpf_ktime_get_ns(); + + bpf_map_update_elem(&start_map, &reqp, &ts, BPF_ANY); + +// /* optional debug: + bpf_printk("start: req=%llu ts=%llu\n", reqp, ts); +// */ + return 0; +} + +/* completion: compute latency and print data_len, cmd_flags, latency_us */ +SEC("kprobe/blk_mq_end_request") +int trace_completion(struct pt_regs *ctx) +{ + __u64 reqp = (__u64)(ctx->di); + __u64 *tsp; + __u64 now_ns; + __u64 delta_ns; + __u64 delta_us = 0; + bpf_printk("%lld", reqp); + tsp = bpf_map_lookup_elem(&start_map, &reqp); + if (!tsp) + return 0; + + now_ns = bpf_ktime_get_ns(); + delta_ns = now_ns - *tsp; + delta_us = delta_ns / 1000; + + /* read request fields using CO-RE; needs vmlinux.h/BTF */ + __u32 data_len = 0; + __u32 cmd_flags = 0; + + /* __data_len is usually a 32/64-bit; use CORE read to be safe */ + data_len = ( __u32 ) BPF_CORE_READ((struct request *)reqp, __data_len); + cmd_flags = ( __u32 ) BPF_CORE_READ((struct request *)reqp, cmd_flags); + + /* print: " " */ + bpf_printk("%u %x %llu\n", data_len, cmd_flags, delta_us); + + /* remove from map */ + bpf_map_delete_elem(&start_map, &reqp); + + return 0; +}