在vmware中用kdb调试linux内核和模块

  • Post author:
  • Post category:linux


环境:

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;
}



版权声明:本文为alex_mianmian原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。