Linux内核调试方法总结之Kprobes

Kprobes

【用途】【参考kernel/Documentation/kprobes.txt帮助文档】

Kprobes是一个轻量级内核调试工具,同时又是其他一些更高级的内核调试工具(如perf和systemtap)的基础,在Linux 4.0内核版本中,eBPF特性也寄生于kprobe之上。

【原理】

Kprobes的主要作用是在指定函数调用前后添加自定义函数,从而实现动态探测点的功能。

Kprobes有两种使用方法:1)模块加载;2)通过debugfs操作。

【接口说明】【参考kernel/sample/kprobes】

关键结构体

#include <linux/kprobes.h>

struct kprobe {

         kprobe_opcode_t *addr;                     /* Location of the probe point */

         const char *symbol_name;                           /* Allow user to indicate symbol name of the probe point */

         kprobe_pre_handler_t pre_handler;      /* Called before addr is executed */

kprobe_pre_handler_t post_handler;    /* Called after addr is executed */

kprobe_pre_handler_t fault_handler;    /* Called if executing addr caused a fault */

};

注册kprobes探测点

int register_kprobe(struct kprobe *p);

注销kprobes探测点

void unregister_kprobe(struct kprobe *p);

【实例】

/*

 * NOTE: This example is works on x86 and powerpc.

 * Here's a sample kernel module showing the use of kprobes to dump a

 * stack trace and selected registers when do_fork() is called.

 *  * For more information on theory of operation of kprobes, see

 * Documentation/kprobes.txt

 *  * You will see the trace data in /var/log/messages and on the console

 * whenever do_fork() is invoked to create a new process.

 */

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/kprobes.h>

/* For each probe you need to allocate a kprobe structure */

static struct kprobe kp = {

    .symbol_name    = "do_fork",

};

 

/* kprobe pre_handler: called just before the probed instruction is executed */

static int handler_pre(struct kprobe *p, struct pt_regs *regs) {

#ifdef CONFIG_X86

    printk(KERN_INFO "pre_handler: p->addr = 0x%p, ip = %lx,"             " flags = 0x%lx\r\n",         p->addr, regs->ip, regs->flags);

#endif

#ifdef CONFIG_PPC

    printk(KERN_INFO "pre_handler: p->addr = 0x%p, nip = 0x%lx,"             " msr = 0x%lx\r\n",         p->addr, regs->nip, regs->msr);

#endif

#ifdef CONFIG_MIPS

    printk(KERN_INFO "pre_handler: p->addr = 0x%p, epc = 0x%lx,"             " status = 0x%lx\r\n",         p->addr, regs->cp0_epc, regs->cp0_status);

#endif

    /* A dump_stack() here will give a stack backtrace */

    return 0;

}

/* kprobe post_handler: called after the probed instruction is executed */

static void handler_post(struct kprobe *p, struct pt_regs *regs,                 unsigned long flags) {

#ifdef CONFIG_X86

    printk(KERN_INFO "post_handler: p->addr = 0x%p, flags = 0x%lx\r\n",         p->addr, regs->flags);

#endif

#ifdef CONFIG_PPC

    printk(KERN_INFO "post_handler: p->addr = 0x%p, msr = 0x%lx\r\n",         p->addr, regs->msr);

#endif

#ifdef CONFIG_MIPS

    printk(KERN_INFO "post_handler: p->addr = 0x%p, status = 0x%lx\r\n",         p->addr, regs->cp0_status);

#endif

}

/*

 * fault_handler: this is called if an exception is generated for any

 * instruction within the pre- or post-handler, or when Kprobes

 * single-steps the probed instruction.

 */

static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr) {

    printk(KERN_INFO "fault_handler: p->addr = 0x%p, trap #%dn",         p->addr, trapnr);

    /* Return 0 because we don't handle the fault. */

    return 0;

}

 

static int __init kprobe_init(void) {

    int ret;

    kp.pre_handler = handler_pre;

    kp.post_handler = handler_post;

    kp.fault_handler = handler_fault;

    ret = register_kprobe(&kp);

 

    if (ret < 0) {

        printk(KERN_INFO "register_kprobe failed, returned %d\r\n", ret);

        return ret;

    }

 

    printk(KERN_INFO "Planted kprobe at %p\r\n", kp.addr);

    return 0;

}

 

static void __exit kprobe_exit(void) {

    unregister_kprobe(&kp);

    printk(KERN_INFO "kprobe at %p unregistered\r\n", kp.addr);

}

module_init(kprobe_init) module_exit(kprobe_exit) MODULE_LICENSE("GPL");

posted on 2016-04-23 14:27  者旨於陽  阅读(1244)  评论(0编辑  收藏  举报

导航