为KGDB 增加watchpoint断点支持 on x86

来源:互联网 发布:fanuc铣孔螺旋下刀编程 编辑:程序博客网 时间:2024/05/01 04:42
前言
前面我们在《gdb 和 watchpoint》 文章里讨论了在gdb的watchpoint,这次我们来讨论下如何让kgdb也支持watchpoint特性。

KGDB 相当于一个gdb server,只是这个server是跑在内核里面。所以KGDB支持watchpoint实现和 gdb server的实现如出一辙,即通过GDB远程串行协议里的Stop-Reply-Packets来传达watchpoint信息给gdb,让gdb知道那个watchpoint击中了。

其运行的大致流程为:
A:using gdb to set a watchpoint  B:send out a set watchpoint protocol packet to kgdb from gdb C:kgdb receive/parse the protocol packet D:kgdb set a watchpoint hardware breakpoint on kernel E:Once kernel hit a watchpoint breakpoint, kgdb will collect the     watchpoint breakpoint info, fill them to a Stop-Reply-Packets with     watchpoint format, and send out to gdb



1: 设置watchpoint 断点 in kgdb

在gdb敲入watch命令后,会将发送一个设置watchpoint的数据包给kgdb,kgdb收该数据包后就执行设置断点动作.
 (gdb) watch xxx  (假设我们要监视断点地址为 xxx)  gdb发送数据包"Z2,xxx,4" 给kgdb  kgdb 收到协议数据包后,调用gdb_cmd_break()(kernel/debug/gdbstub.c) 来解析设置断点协议数据 gdb_cmd_break()------------------------------------------parsing  "Z2,xxx,4"--> 'Z'      设置断点--> '2'      断点类型为write watchpoint--> 'xxx'    断点地址为 xxx--> '4'      断点长度为 4------------------------------------------ --> arch_kgdb_ops.set_hw_breakpoint(xxx, 4, BP_WRITE_WATCHPOINT)--> kgdb_set_hw_break(xxx, 4, BP_WRITE_WATCHPOINT) (arch/x86/kernel/kgdb.c)    这个函数完成了将断点信息加入到breakinfo[HBP_NUM]数组里和设置其enabled标志为1,    真正的断点使能是在kgdb退出,让系统正常运行时,调用kgdb_correct_hw_break()函数    来每个cpu上使能硬件断点.


2: watchpoint触发捕获
watchpoint 断点隶属于硬件断点,所以我们先来谈谈硬件断点的触发的整个流程和细节。

硬件断点的触发后,会产生一个调试异常,因此会进入do_debug异常的处理程序(arch/x86/kernel/traps.c). do_debug() { unsigned long dr6;get_debugreg(dr6, 6); //保持dr6寄存器值到dr6变量,DR6 寄存器值含义可见前面博客。  /* DR6 may or may not be cleared by the CPU */set_debugreg(0, 6); //手动清除dr6寄存器 //通过notify_die通知链告诉大家"debug"异常发生了,同时把dr6寄存器值作为参数传递出去。notify_die(DIE_DEBUG, "debug", regs, PTR_ERR(&dr6), error_code,                            SIGTRAP)}

如果kgdb处于早期调试模式(那时候Hardware Breakpoint Layer还没初始化),则是由kgdb自己处理DIE_DEBUG的notify通知,如果是普通模式,即硬件断点管理都是从Hardware Breakpoint Layer获取的,则是由Hardware Breakpoint Layer处理,然后通过回调函数kgdb_hw_overflow_handler()进入kgdb.

Hardware Breakpoint Layer处理流程: static struct notifier_block hw_breakpoint_exceptions_nb = {    .notifier_call = hw_breakpoint_exceptions_notify,    /* we need to be notified first */    .priority = 0x7fffffff};register_die_notifier(&hw_breakpoint_exceptions_nb); 触发硬件断点:do_debug() -- notify_die(DIE_DEBUG)->  arch/x86/kernel/hw_breakpoint.chw_breakpoint_exceptions_notify() ->   hw_breakpoint_handler() {for (i = 0; i < HBP_NUM; ++i) {        if (likely(!(dr6 & (DR_TRAP0 << i))))            continue;        perf_bp_event(bp, args->regs); -->           perf_swevent_overflow(event)->             event->overflow_handler(event, nmi, data, regs);-->                kgdb_hw_overflow_handler(). 这里最终跑到kgdb的代码里了.          }/* end of for()*/}

3: watchpoint 断点信息获取
kgdb只需要知道是哪个watchpoint断点被踩中了,并把它的地址传递给gdb就可以了。

如果kgdb处于早期调试模式,则可以挨个判断dr6寄存器值的bit位来看哪个watchpoint击中了。但是普通模式的Hardware Breakpoint Layer回调函数并没有传递dr6值给我们,不过还好,它传递struct perf_event的event对象给我们,通过这个对象,我们可以得到发生硬断点的地址,然后根据地址和kgdb里保存的硬断点数组对比,相同的,则是踩中的断点..

static void kgdb_hw_overflow_handler(struct perf_event *event, int nmi,        struct perf_sample_data *data, struct pt_regs *regs) { for (i = 0; i < HBP_NUM; i++) {        if (!breakinfo[i].enabled)            continue;        if (breakinfo[i].addr == event->attr.bp_addr) //查找和判断踩中断点信息            break;    }}

在得到是哪个breakinfo[i]被触发后,在查看下breakinfo[i].type,如果是X86_BREAKPOINT_WRITE/X86_BREAKPOINT_RW 就可以认定是watchpoint断点了,
而断点地址则保存在breakinfo[i].addr.



4: 填充Stop-Reply-Packets来通知gdb踩中watchpoint断点了根据watchpoint断点的类型,返回给gdb的Stop-Reply-Packets可以有如下格式:

type = breakinfo[i].type;addr = breakinfo[i].addr;            switch (type) {            case KST_READ_WATCHPOINT:                ptr += strlen(strcpy(ptr, "rwatch:"));                break;            case KST_ACCESS_WATCHPOINT:                ptr += strlen(strcpy(ptr, "awatch:"));                break;            case KST_WRITE_WATCHPOINT:                ptr += strlen(strcpy(ptr, "watch:"));                break;            }            ptr += kgdb_long2hex(addr, ptr);            *ptr++ = ';';


5: test watchpoint support on x86

After gdb connect successly to kgdb, setting a watchpointat softlockup_thresh address. (gdb) watch softlockup_thresh Then change the watchdog_thresh value to 24 on target:# echo "24" > /proc/sys/kernel/watchdog_thresh Then the gdb will stop and show:----------------------------------------------------------Old value = 60New value = 240xffffffff810481f2 in do_proc_dointvec_minmax_conv (negp=0xffff88066cbb9e17,lvalp=0xffffffff8190e718,     valp=0xffffffff8190e718, write=<value optimized out>,data=0xffff88066cbb9e68) at kernel/sysctl.c:24082408*valp = val;(gdb)---------------------------------------------------------- Change the watchdog_thresh value to 24 on target again:# echo "24" > /proc/sys/kernel/watchdog_thresh As the previous watchdog_thresh value is 24, thus gdb will not stop..


6: 扩展阅读
更多有关kgdb和Hardware Breakpoint Layer的内容,可阅读:《抓虫日记之 kgdb 和 删除硬断点 》
更多有关x86硬件调试寄存器信息,可阅读:《x86 调试寄存器》

原文链接:

http://www.kgdb.info/kgdb/kgdb_watchpoint_support_on_x86/



在kvm虚拟机上测试过只能对全局变量下watch断点。(内核编译选项前提是)

CONFIG_DEBUG_RODATA =n

CONFIG_DEBUG_SET_MODULE_RONX = n









原创粉丝点击