自从kdb集成到内核后,调试内核就多了一种选择,可以选择仅需要目标机,自己调试自己的kdb,
或选择需要额外一台机器来辅助调试的kgdb。
引人kdb,也引发一个新功能需求,即调试器之间的切换选择,如从kdb模式切换到kgdb模式或者反过来。
而且由于kdb和kgdb并不互斥,可在系统中同时存在,而kdb不需要额外的机器来辅助调试这一便利性,
所以在kgdb和kdb同时被使能的情况下,系统调试器缺省设置为kdb模式,这时kgdb用户使用gdb尝试
去连接内核的话,由于缺省设置为kdb模式,为了方便用户使用,还要求系统可以自动从kdb切换到kgdb
模式的功能(当gdb连接的时候)。
1: kdb和kgdb的实现架构
这里我们要搞的是为什么可以互换。这得从kdb和kgdb的实现原理来说了。
先看看现在目前内核调试框架图(Cutting from Jason's PPT of linuxcon2010)
其中
Debug Core:
1:Generic debug API
2:Handles exceptions
3:Syncs/saves/restores CPUs
4:API for SW/HW breakpoints
Arch specific KGDB:
1:Interface to read/write registers
2:Arch specific exceptions and watch dogs
3:Single stepping interface
GDB Stub:
Speaks the gdb serial protocol
Polled I/O Driver (kgdboc / kgdboe / kgdbou / kgdb_8250):
1:Uses the console UART driver to multiplex a single serial line
2:Another host's gdb connects to this port
由框架图可知,在debug core框架下,已经将KGDB和KDB模式抽象成它的"stub",
所以要切换模式,只要切换"stub"处理函数即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 关门放代码, linux/kernel/debug/debug_core.c: kgdb_cpu_enter() { ... while (1) { /* 根据当前设定模式,选择stub */ if (dbg_kdb_mode) { kgdb_connected = 1; error = kdb_stub(ks); if (error == -1) continue; kgdb_connected = 0; } else { error = gdb_serial_stub(ks); } /* stub函数的返回值是否是切换 stub的指令 */ if (error == DBG_PASS_EVENT) { dbg_kdb_mode = !dbg_kdb_mode; } } ... } |
2: kdb模式与kgdb模式互换实现
我们需要实现如下功能:
1: kgdb模式到kdb模式切换,即通过增加 gdb 命令来切换
2: kdb模式到kgdb模式切换,即通过增加 kdb 命令来切换
3: kdb刚启动(缺省状态时),kdb检测gdb的连接,自动切换到kgdb模式
2.1: kgdb模式到kdb模式切换,即通过增加 gdb 命令来切换
这个实现不复杂,可在gdb的远程协议里增加一个子协议表明要切换到kdb模式。
目前那个子协议暂定为 “$3#33“, 即当kgdb stub收到“$3#33“后,返回
DBG_PASS_EVENT 到 debug_core.
1 2 3 4 5 6 7 8 9 10 11 12 13 | 继续关门放代码, linux/kernel/debug/gdbstub.c: gdb_serial_stub() { ... while (1) { switch (gdb协议发送的数据) { case '3': /* Escape into back into kdb */ if (remcom_in_buffer[1] == '\0') { gdb_cmd_detachkill(ks); return DBG_PASS_EVENT; } } ... } |
2.2 : kdb模式到kgdb模式切换,即通过增加 kdb 命令来切换
这个也不复杂,即增加一个‘kgdb‘命令来实现,
1: 先注册'kgdb' 命令到kdb, 其命令对于处理函数为kdb_kgdb(), 即用户
在kdb里敲入'kgdb' 命令后,立刻调用kdb_kgdb()函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 放代码 linux/kernel/debug/kdb/kdb_main.c: void kdb_inittab(void) { kdb_register_repeat("kgdb", kdb_kgdb, "", "Enter kgdb mode", 0, KDB_REPEAT_NONE); } static int kdb_kgdb(int argc, const char **argv) { return KDB_CMD_KGDB; } linux/kernel/debug/kdb/kdb_debugger.c: int kdb_stub() { if (error == KDB_CMD_KGDB) { if (KDB_STATE(DOING_KGDB) || KDB_STATE(DOING_KGDB2)) { /* * This inteface glue which allows kdb to transition in into * the gdb stub. In order to do this the '?' or '' gdb serial * packet response is processed here. And then control is * passed to the gdbstub. */ if (KDB_STATE(DOING_KGDB)) gdbstub_state(ks, "?"); else gdbstub_state(ks, ""); KDB_STATE_CLEAR(DOING_KGDB); KDB_STATE_CLEAR(DOING_KGDB2); } return DBG_PASS_EVENT; } } |
2.3: kdb刚启动(缺省状态时),kdb检测gdb的连接,自动切换到kgdb模式
这里的主要点在于如何检测gdb的连接。
这得关注gdb的远程调试协议,而且还得兼容新老版本的gdb。
由于各个系统支持程度不一样,gdb在连接远程调试时候,一般会先询问下远程调试系统支持哪些指令,
这个动作是通过协议"qSupported"+"一些子命令"来实现的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 放gdb的代码 gdb/gdb/remote.c: remote_start_remote() { if (interrupt_on_connect) send_interrupt_sequence (); /* The first packet we send to the target is the optional "supported packets" request. If the target can answer this, it will tell us which later probes to skip. */ remote_query_supported (); ... } void remote_query_supported () { /* The packet support flags are handled differently for this packet than for most others. We treat an error, a disabled packet, and an empty response identically: any features which must be reported to be used will be automatically disabled. An empty buffer accomplishes this, since that is also the representation for a list containing no features. */ rs->buf[0] = 0; if (remote_protocol_packets[PACKET_qSupported].support != PACKET_DISABLE) { char *q = NULL; struct cleanup *old_chain = make_cleanup (free_current_contents, &q); if (rs->extended) q = remote_query_supported_append (q, "multiprocess+"); if (remote_support_xml) q = remote_query_supported_append (q, remote_support_xml); q = remote_query_supported_append (q, "qRelocInsn+"); q = reconcat (q, "qSupported:", q, (char *) NULL); putpkt (q); } |
从上面的代码中可以得知,qSupported的命令是根据gdb的配置和目标target的配置而决定的,
比如说在i386上, 这个第一个发出去的包可能是:
1 2 3 | 1) $qSupported:xmlRegisters=i386;qRelocInsn+#25 2) $qSupported:qRelocInsn+#9a 3) $qSupported#37 |
因此我们需要在kdb的输入数据流里检测以$qSupported打头的包,如果检测到了,则说明是gdb
在尝试连接,则需要切换到kgdb模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 关门放kdb处理代码: linux/kernel/debug/kdb/kdb_io.c: char* kdb_read() { /* Special escape to kgdb */ if (lastchar - buffer >= 5 && strcmp(lastchar - 5, "$?#3f") == 0) { strcpy(buffer, "kgdb"); KDB_STATE_SET(DOING_KGDB); return buffer; } if (lastchar - buffer >= 11 && strcmp(lastchar - 11, "$qSupported") == 0) { strcpy(buffer, "kgdb"); KDB_STATE_SET(DOING_KGDB2); return buffer; } } |
其中上面的strcpy(buffer, "kgdb");代码表示 当检测到上面的事件时,模拟一个
kgdb命令,这样就可以走2.2里面的实现路程来完成工作了。。
你好,我现在kgdb的调试环境已经搭建起来了,不过没太搞懂怎样在kgdb里切换成kdb模式,代码部分写的是3,你的文章里是$3#33,不是太明白这是什么意思,请教了
[回复]
DDD 回复:
十二月 15th, 2011 at 11:32 下午
@cuic139 @cuic139
$3#33是gdb与kgdb进行通信的远程协议的封包格式。
RSP 是一种基于 ASCII 码的协议,以 gdb 发送命令,目标端(在这里是 kgdb)返回执行
结果或信息的方式来进行的,而命令和回复在传输过程中又是封装在一个包(packet)里面
的.每个包以一个’$’开始,接着是实际数据,数据以一个’#’结束,后面跟着两位十六进制数
字用作校验和($packet-data#checksum).校验和需要对 256 取模,因为只有两位。
http://www.kgdb.info/kgdb/understand_kgdb/kgdb_source_chapter_three/
[回复]
cuic139 回复:
十二月 16th, 2011 at 5:44 下午
@DDD 按照这样,应该是在kgdb的控制台里输入$3#33就可以进入kdb了吧?
不过我现在输入之后提示:
Program received signal SIGTRAP, Trace/breakpoint trap.
kgdb_breakpoint (key=103) at kernel/debug/debug_core.c:960
960 in kernel/debug/debug_core.c
(gdb) $3#33
Undefined command: “$3”. Try “help”.
编译选项是按照您说的确认过了的:
CONFIG_DEBUG_KERNEL=Y
CONFIG_KGDB=Y
CONFIG_KGDB_SERIAL_CONSOLE=Y
Sysrq-g must be used to break in initially.
Selecting this will automatically set:
CONFIG_CONSOLE_POLL=N
CONFIG_MAGIC_SYSRQ=Y
CONFIG_KGDB_KDB=Y
Optional other configuration settings:
CONFIG_FRAME_POINTER=Y
CONFIG_DEBUG_RODATA=N
CONFIG_KALLSYMS=Y
CONFIG_KDB_KEYBOARD
CONFIG_KGDB_TESTS
只有这个CONFIG_CONSOLE_POLL=N 现在是不一样的,不知道是否有影响
现在想进kdb但是进不去。。。
非常感谢
[回复]
DDD 回复:
十二月 19th, 2011 at 11:08 上午
@cuic139 在gdb端,应该使用packet管理模式…
在连接kgdb成功后,使用
(gdb) maintenance packet
(gdb) 3
“maintenance packet”命令比较生僻,所以….
还有一种方法就是直接修改kgdb里面的核心变量值:
在连接kgdb成功后,
(gdb) set dbg_kdb_mode=1
在退出kgdb,然后就是kdb模式了。
在进入kdb模式成功后,可以使用
echo g >/proc/sysrq-trigger
来触发验证下.
CONFIG_CONSOLE_POLL=N
这个应该是选 Y的,
可能是http://www.kgdb.info/kgdb/kdb_introduce/
这个文章里面应该笔误了,我现在手上没有环境,回头等我验证下.
[回复]
cuic139 回复:
十二月 19th, 2011 at 4:47 下午
@DDD 您说的对,是选Y的,是当kgdb和使用串口都为Y时自动选上的,我今天又研究了一下文档重新试验了一下,问题已经解决了,非常感谢您,O(∩_∩)O~
[回复]
在还要请教个问题,哈哈,新内核里的kdb命令,id这个命令貌似给去掉了!!,您关注过这方面的问题吗?
我查看了2.6.39和3.1.5的源代码,都是这样,是有什么可以替代的命令吗…如果只想要一台机器调试的话,kdb的id指令还是对分析问题很有帮助的
[回复]
cuic139 回复:
十二月 20th, 2011 at 4:08 下午
@cuic139 我在kdb的官方网站上看到有3.1的patch,patch里面有id这个命令,之前的版本像2.6.39则都没有相应的patch,这种情况莫非要手动patch。。。?
[回复]
DDD 回复:
十二月 21st, 2011 at 10:32 上午
@cuic139 目前内核版本里是没有做这个支持的。
In the merged kdb & kgdb, presently there is no disassembler (known as the “id” command)
我不清楚kdb是否有特殊都patch,方便都话,你可以发给我下。(kdb的官网打不开)
[回复]
In the merged kdb & kgdb, presently there is no disassembler (known as the “id” command)
这个您是在哪里看到的?官方文档里好像没有,我是看了代码实现才知道不支持的;
怎么发给您呢?不知道您的邮箱,您给我发个邮件吧,[email protected] ,O(∩_∩)O
[回复]
cuic139 回复:
十二月 21st, 2011 at 2:16 下午
@cuic139 那个补丁根据里面的readme是用在3.0的,虽然写的是文件名字写的是3.1,我打了之后编译现在还没有过呢,
struct lock_manager_operations 里面没有成员变量 lm_compare_owner和lm_notify,代码里没有定义。。。不知道是不是官方的bug,~~~~(>_<)~~~~
[回复]
cuic139 回复:
十二月 21st, 2011 at 2:28 下午
@cuic139 从james911那里看到了您的邮箱,已经给您发过去了
[回复]
DDD 回复:
十二月 21st, 2011 at 4:03 下午
@cuic139 @cuic139
这里有描述 集成到内核的KDB和原生KDB的一些差别
https://kgdb.wiki.kernel.org/articles/k/d/b/KDB_FAQ_9859.html
收到你邮件,我找空去看看。
非常感谢.
[回复]
cuic139 回复:
十二月 21st, 2011 at 4:26 下午
@DDD 我手动patch到2.6.*的内核上,有一些patch和编译错误,修正后现在id反汇编能用了,
开心死了,哈哈
[回复]
哈哈,我又来了,还需要请教一下,我现在是用虚拟机装的linux,无论我是采用kgdb远程调试,还是kdb本机调试,只要已进入debug模式,cpu的占用率就满了,之前的时候虽然cpu占用高不过还有反应,今天进了kdb后虚拟机就莫有反应了,所以想求教一下DDD大神有莫有遇到过类似的问题?我kgdboc哪里已经设置kbd了
[回复]
DDD 回复:
十二月 22nd, 2011 at 9:06 下午
@cuic139 @cuic139
呵呵,欢迎欢迎。
虚拟机是比较慢,进入调试模式的时候,虚拟机一直在模拟CPU空转循环。
不知道你机器性能怎么样,在我这台式机这,cpu一般在50%左右(双核,qemu)。
[回复]