内核调试器kdb与kgdb模式相互切换

DDD  2011年03月16日 星期三 17:51 | 6610次浏览 | 0条评论

自从kdb集成到内核后,调试内核就多了一种选择,可以选择仅需要目标机,自己调试自己的kdb
或选择需要额外一台机器来辅助调试的kgdb。

人kdb,也引发一个新功能需求,即调试器之间的切换选择,如从kdb模式切换到kgdb模式或者反过来。

且由于kdb和kgdb并不互斥,可在系统中同时存在,而kdb不需要额外的机器来辅助调试这一便利性,
所以在kgdb和kdb同时被使能的情况下,系统调试器缺省设置为kdb模式,这时kgdb用户使用gdb尝试
去连接内核的话,由于缺省设置为kdb模式,为了方便用户使用,还要求系统可以自动从kdb切换到kgdb
模式的功能(当gdb连接的时候)。

自从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"处理函数即可。

关门放代码, 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.

继续关门放代码, 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()函数。

放代码 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"+"一些子命令"来实现的.

放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) $qSupported:xmlRegisters=i386;qRelocInsn+#25
2) $qSupported:qRelocInsn+#9a
3) $qSupported#37

因此我们需要在kdb的输入数据流里检测以$qSupported打头的包,如果检测到了,则说明是gdb
在尝试连接,则需要切换到kgdb模式。

关门放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里面的实现路程来完成工作了。。

 

本文转载自:
http://www.kgdb.info/gdb/kdb_kgdb_mode_change/

 

 

评论

我的评论:

发表评论

请 登录 后发表评论。还没有在Zeuux哲思注册吗?现在 注册 !

暂时没有评论

Zeuux © 2024

京ICP备05028076号