环境:
vmware:6.0.7
Ubuntu:12.04 LTS
Linux kernel:4.3
KDB的工作原理是把kernel停下来,然后等待命令输入,命令可以来自串口或键盘。输出是到串口和console,但console上的输出只能在退出KDB之后才能看到。
1.需要打开Linux kernel 的KDB选项,编译,安装,重启。Linux kernel 4.3 自带有KGDB和KDB。 实际上KDB是KGDB的一个子集。
2.在vmware的虚拟机上添加串口。
3.在host windows上安装PuTTY。
4.在Linux上配置KGDBOC的参数, 然后触发KDB。
4.1 键盘输入,没有串口输出。如下图:
当输入“echo g > /proc/sysrq-trigger”后, 没有显示,就像死机一样。但如果输入”go”并回车,那么你就能退出KDB并看到KDB的输出。
4.2 串口输入输出,如下图:
左边是PuTTY,右边是Linux。 当触发KDB后,KDB输出到PuTTY,并且可以在PuTTY上输入。当go退出KDB后,右边的Linux则会显示左边一样的内容。
4.3 串口输出,键盘输入。如下图:
同样左边是PuTTY,右边是Linux。 不同于4.2的是,输入在Linux这一侧,而串口只输出。
问题1.为什么要串口?
因为进入KDB后kernel已经停止,输出到console( VT )的东西都已经没办法显示(可以读tty下VT的代码)。所以需要输出到串口。
代码浏览:
static struct kgdb_io kgdboc_io_ops = {
.name = "kgdboc",
.read_char = kgdboc_get_char,
.write_char = kgdboc_put_char,
.pre_exception = kgdboc_pre_exp_handler,
.post_exception = kgdboc_post_exp_handler,
};
首先看看这个结构体,这个结构体会被注册到 dbg_io_ops上。
下面这个函数就是KDB的输出函数。
int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
{
/*remove other code*/
kdb_printit:
/*
* Write to all consoles.
*/
retlen = strlen(kdb_buffer);
cp = (char *) printk_skip_level(kdb_buffer);
if (!dbg_kdb_mode && kgdb_connected) {
gdbstub_msg_write(cp, retlen - (cp - kdb_buffer));
} else {
if (dbg_io_ops && !dbg_io_ops->is_console) {
len = retlen - (cp - kdb_buffer);
cp2 = cp;
while (len--) {
dbg_io_ops->write_char(*cp2); //输出到串口
cp2++;
}
}
while (c) {
c->write(c, cp, retlen - (cp - kdb_buffer)); //输出到console,退出KDB后可以看到
touch_nmi_watchdog();
c = c->next;
}
}
/*remove other code*/
}
下面看看kgdboc_io_ops的write_char函数。
static void kgdboc_put_char(u8 chr)
{
if (!kgdb_tty_driver)
return;
kgdb_tty_driver->ops->poll_put_char(kgdb_tty_driver,
kgdb_tty_line, chr);
}
原来是调用kgdb_tty_driver的poll_put_char()函数。 kgdb_tty_driver是怎么得到的呢?要看configure_kgdboc()了,如下。
static int configure_kgdboc(void)
{
struct tty_driver *p;
int tty_line = 0;
int err;
char *cptr = config;
struct console *cons;
err = kgdboc_option_setup(config);
if (err || !strlen(config) || isspace(config[0]))
goto noconfig;
err = -ENODEV;
kgdboc_io_ops.is_console = 0;
kgdb_tty_driver = NULL;
kgdboc_use_kms = 0;
if (strncmp(cptr, "kms,", 4) == 0) {
cptr += 4;
kgdboc_use_kms = 1;
}
if (kgdboc_register_kbd(&cptr)) //这里就是KDB从键盘取输入的配置地方。
goto do_register;
p = tty_find_polling_driver(cptr, &tty_line); //在tty驱动队列里查找配置给KDB的tty,本例子中是ttyS1。
if (!p)
goto noconfig;
cons = console_drivers;
while (cons) {
int idx;
if (cons->device && cons->device(cons, &idx) == p &&
idx == tty_line) {
kgdboc_io_ops.is_console = 1;
break;
}
cons = cons->next;
}
kgdb_tty_driver = p; //这里就是怎么得到kgdb_tty_driver的地方。
kgdb_tty_line = tty_line;
do_register:
err = kgdb_register_io_module(&kgdboc_io_ops);//dbg_io_ops = kgdboc_io_ops 的地方。
if (err)
goto noconfig;
err = kgdb_register_nmi_console();
if (err)
goto nmi_con_failed;
configured = 1;
return 0;
nmi_con_failed:
kgdb_unregister_io_module(&kgdboc_io_ops);
noconfig:
kgdboc_unregister_kbd();
config[0] = 0;
configured = 0;
cleanup_kgdboc();
return err;
}