kgdb源代码分析(2.6.27)
本文由penny编写, 你可以通过发邮件到[email protected]联系penny,
也可以直接评论本文章与penny交流.
0. 概述
1. 异步通知
2. 准备工作
3.gdb 远程串行协议(GDB remote serial protocol)
4. 命令实现:
4.1 断点.
4.2 continue 和 step
5 初始化时机.
1. 异步通知
当内核踩到一个断点时,当前进程是用什么方式通知 kgdb,控制权又是怎样到 kgdb
手上的呢?
先回顾一下 kgdb 的 patch 在 2.4 上是怎样让内核通知 kgdb 的处理代码的,下面是
2.4.23 的内核打上 kgdb 补丁的部分代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | (arch/i386/kernel/traps.c) #ifdef CONFIG_KGDB #define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) \ { \ if (linux_debug_hook != (gdb_debug_hook *) NULL && !user_mode(regs)) \ { \ (*linux_debug_hook)(trapnr, signr, error_code, regs) ; \ after; \ } \ } #else #define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) #endif #define DO_VM86_ERROR(trapnr, signr, str, name) \ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ {\ CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,goto skip_trap)\ do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \ skip_trap: \ return; \ } |
DO_VM86_ERROR 这个宏是用来生产异常处理函数的。比如你的 name 参数是 int3
那就会出来一个do_int3()的函数, 用它来处理3号异常, 也就是断点异常. 而
linux_debug_hook 是 2.4 里面指向 kgdb 主处理函数的函数指针。
从上面代码可以看到, 在 2.4 里面 kgdb 把它的主处理函数直接插入到异常处理函数的
开头,当异常发生时 kgdb 就直接得到控制权.
说实话,这种做法比较难看. 而在 2.6 里面,做法其实差不多, 但我们有了 notifier chian
这种异步通知机制. 什么是 notifier chain? 它就是一条回调函数的链表, 每一条链表代表一
种事件, 每个关心这个事件的模块或子系统就在这条特定链上面注册自己的回调函数. 代码
中 notifier_block结构体代表链表里面的一个节点, 包含一个函数指针和一个整形变量(描述
优先级). 当事件发生时对应的函数链上的函数就会按优先级被执行.
在内核中根据不同的事件定义了一些不同的链,比如:die_chain,reboot_notifier_list,netlink_chain...等等,
更加具体的描述可以在内核Documentation目录中或网上找到.而它们的实现都是对notifier chain进行封装,
notifier chain的具体实现请见kernel/notifier.c.
die_chain 这条链关心的是中断和异常事件,kgdb 在它上面注册了自己的函数.
1 2 3 4 5 6 7 8 9 | (arch/x86/kernel/kgdb.c) 515 static struct notifier_block kgdb_notifier = { 516 .notifier_call = kgdb_notify, 517 518 /* 519 * Lowest-prio notifier priority, we want to be notified last: 520 */ 521 .priority = -INT_MAX, 522 }; |
kgdb 很谦虚,把自己的优先级设为最低, 先让别人执行, 最后才考虑自己.
异常发生时 die_chain 上的函数都会被触发. 对于 kgdb,最关心的当然是 3 号异常, 它就
是程序踩到一个断点时引发的异常. 函数 notify_die()就是遍历 die_chain 链,执行所有上面
的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | (arch/x86/kernel/traps_32.c) 851 void __kprobes do_int3(struct pt_regs *regs, long error_code) 852 { 853 trace_hardirqs_fixup(); 854 855 if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP) 856 == NOTIFY_STOP) 857 return; 858 /* 859 * This is an interrupt gate, because kprobes wants interrupts 860 * disabled. Normal trap handlers don't. 861 */ 862 restore_interrupts(regs); 863 864 do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL); 865 } |
kgdb的回调函数kgdb_notify()(定义在arch/x86/kernel/kgdb.c)最后被调用,而它又直接调用 __kgdb_notify(),
这个函数对某些特殊情况做了处理,我们后面在遇到这些情况时会回到这个地方进行说明,在大多数情况下程序
会直接进入 kgdb的入口函数kgdb_handle_exception(),它定义在kernel/kgdb.c.
1 2 3 4 5 6 7 8 9 10 11 | int3 中断 | v do_int3() '->notify_die() |-> .. //其他关心异常的函数们 |-> .. |-> .. '-> kgdb_notify() '->__kgdb_notify() //关中断 '->kgdb_handle_exception() //kgdb 入口 |
所以 kgdb 是通过异常触发的, 而且整个过程都处在异常处理中. 当前进程被异常中断,
而异常处理通过 notifier chain 把控制权交给了 kgdb。
[…] 概述 1. 异步通知 2. 准备工作 3.gdb 远程串行协议(GDB remote serial protocol) 4. 命令实现: 4.1 断点. […]
[…] 概述 1. 异步通知 2. 准备工作 3.gdb 远程串行协议(GDB remote serial protocol) 4. 命令实现: 4.1 断点. […]
[…] 概述 1. 异步通知 2. 准备工作 3.gdb 远程串行协议(GDB remote serial protocol) 4. 命令实现: 4.1 断点. […]
[…] 概述 1. 异步通知 2. 准备工作 3.gdb 远程串行协议(GDB remote serial protocol) 4. 命令实现: 4.1 断点. […]
[…] 概述 1. 异步通知 2. 准备工作 3.gdb 远程串行协议(GDB remote serial protocol) 4. 命令实现: 4.1 断点. […]
[…] 概述 1. 异步通知 2. 准备工作 3.gdb 远程串行协议(GDB remote serial protocol) 4. 命令实现: 4.1 断点. […]