kprobe 是 SystemTap 對核心函數進行探測的核心實現。核心提供正式 API,對核心程式設計師或許比 SystemTap 更方便。提供三種處理函數:jprobe、kprobe、kretprobe。
以下程式碼使用三種 probe 觀察 TCP/IP arp_process 執行中對 ip_route_input() 調用的回傳結果,並展示 entry/return handler 間共享參數方法。
/* arp_probe.c by Qianfeng Zhang (frzhang@redhat.com) */
#include
#include
#include
#include
#include
#include
#include
#include
MODULE_AUTHOR("frzhang@redhat.com");
MODULE_DESCRIPTION("Track ip_route_input() call results inside arp_process using jprobe and kretprobe");
MODULE_LICENSE("GPL");
static int j_arp_process(struct sk_buff *skb) {
struct net_device *dev = skb->dev;
struct in_device *in_dev = in_dev_get(dev);
int no_addr = (in_dev->ifa_list == NULL);
int rpf = IN_DEV_RPFILTER(in_dev);
in_dev_put(in_dev);
printk("\narp_process() called with interface device %s, in_dev(no_addr=%d,rpf=%d)\n", dev->name, no_addr, rpf);
jprobe_return();
return 0;
}
static int j_fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,
struct net_device *dev, __be32 *spec_dst, u32 *itag, u32 mark) {
printk("fib_validate_source() called with dst=0x%x, oif=%d\n", dst, oif);
jprobe_return();
return 0;
}
static struct jprobe my_jp1 = { .entry = j_arp_process, .kp.symbol_name = "arp_process" };
static struct jprobe my_jp2 = { .entry = j_fib_validate_source, .kp.symbol_name = "fib_validate_source" };
static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs) {
printk("Calling: %s()\n", ri->rp->kp.symbol_name);
return 0;
}
static int return_handler(struct kretprobe_instance *ri, struct pt_regs *regs) {
int eax = regs->ax & 0xffff;
printk("Returning: %s() with return value: 0x%lx(64bit) 0x%x(32bit)\n", ri->rp->kp.symbol_name, regs->ax, eax);
return 0;
}
static int fib_lookup_entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs) {
struct fib_result *resp = (struct fib_result *) regs->dx;
printk("Calling: %s()\n", ri->rp->kp.symbol_name);
*((struct fib_result **)ri->data) = resp;
return 0;
}
static int fib_lookup_return_handler(struct kretprobe_instance *ri, struct pt_regs *regs) {
struct fib_result *resp = *((struct fib_result **) ri->data);
int eax = regs->ax & 0xffff;
printk("Returning: fib_lookup() with return value: 0x%lx(64bit) 0x%x(32bit), result->type: %d\n", regs->ax, eax, resp->type);
return 0;
}
static struct kretprobe my_rp1 = { .handler = return_handler, .entry_handler = entry_handler, .kp.symbol_name = "ip_route_input_slow" };
static struct kretprobe my_rp2 = { .handler = return_handler, .entry_handler = entry_handler, .kp.symbol_name = "fib_validate_source" };
static struct kretprobe my_rp3 = { .handler = fib_lookup_return_handler, .entry_handler = fib_lookup_entry_handler, .kp.symbol_name = "fib_lookup", .data_size = sizeof(struct fib_result *) };
static int __init init_myprobe(void) {
int ret;
printk("RTN_UNICAST is %d\n", RTN_UNICAST);
if ((ret = register_jprobe(&my_jp1)) < 0) { printk("register_jprobe %s failed, returned %d\n", my_jp1.kp.symbol_name, ret); return -1; }
if ((ret = register_jprobe(&my_jp2)) < 0) { printk("register_jprobe %s failed, returned %d\n", my_jp2.kp.symbol_name, ret); return -1; }
if ((ret = register_kretprobe(&my_rp1)) < 0) { printk("register_kretprobe %s failed, returned %d\n", my_rp1.kp.symbol_name, ret); unregister_jprobe(&my_jp1); unregister_jprobe(&my_jp2); return -1; }
if ((ret = register_kretprobe(&my_rp2)) < 0) { printk("register_kretprobe %s failed, returned %d\n", my_rp2.kp.symbol_name, ret); unregister_jprobe(&my_jp1); unregister_jprobe(&my_jp2); unregister_kretprobe(&my_rp1); return -1; }
if ((ret = register_kretprobe(&my_rp3)) < 0) { printk("register_kretprobe %s failed, returned %d\n", my_rp3.kp.symbol_name, ret); unregister_jprobe(&my_jp1); unregister_jprobe(&my_jp2); unregister_kretprobe(&my_rp1); unregister_kretprobe(&my_rp2); return -1; }
return 0;
}
module_init(init_myprobe);
module_exit(cleanup_myprobe);
jprobe_return() 返回。entry_handler 與 return_handler 可透過 ri->data 共享資料(需設定 data_size)。pt_regs 取得回傳值(regs->ax)與特定參數(如 regs->dx 為 fib_result*)。