LV010-驱动调试
驱动程序出现 bug 时,除了 printk 打印信息,我们还可以怎么调试驱动?
一、相关调试函数
1. dump_stack()
1.1 函数说明
dump_stack() 函数声明如下:
extern asmlinkage void dump_stack(void) __cold;函数实际定义在 dump_stack.c - lib/dump_stack.c
/**
* dump_stack - dump the current task information and its stack trace
*
* Architectures can override this implementation by implementing its own.
*/
#ifdef CONFIG_SMP
static atomic_t dump_lock = ATOMIC_INIT(-1);
asmlinkage __visible void dump_stack(void)
{
// ......
local_irq_save(flags);
// ......
__dump_stack();
// ......
local_irq_restore(flags);
}
#else
asmlinkage __visible void dump_stack(void)
{
__dump_stack();
}
#endif
EXPORT_SYMBOL(dump_stack);这个函数是打印内核调用堆栈, 并打印函数的调用关系。
1.2 使用实例
1.2.1 demo 源码
static int scdev_create(_CHAR_DEVICE *p_chrdev)
{
// ......
/* 初始化等待队列头 */
init_waitqueue_head(&p_chrdev->r_wait);
dump_stack(); // 打印函数的调用关系
//PRT("scdev_create %s success!\n", p_chrdev-> dev_name);
return 0;
}1.2.2 开发板测试
如下图所示,在加载驱动的时候会打印出这个函数相关的一些调用关系:

2. WARN_ON()
2.1 函数说明
WARN_ON() 函数定义如下:
#ifdef CONFIG_BUG
#ifndef WARN_ON
#define WARN_ON(condition) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN(); \
unlikely(__ret_warn_on); \
})
#endif
#else
#ifndef HAVE_ARCH_WARN_ON
#define WARN_ON(condition) ({ \
int __ret_warn_on = !!(condition); \
unlikely(__ret_warn_on); \
})
#endif
#endif在括号中的条件成立时, 内核会抛出栈回溯, 打印函数的调用关系。 通常用于内核抛出一个警告, 暗示某种不太合理的事情发生了。WARN_ON 实际上也是调用 dump_stack, 只是多了参数 condition 判断条件是否成立, 例如 WARN_ON (1)则条件判断成功, 函数会成功执行。
2.2 使用实例
2.2.1 demo 源码
static int scdev_create(_CHAR_DEVICE *p_chrdev)
{
// ......
/* 初始化等待队列头 */
init_waitqueue_head(&p_chrdev->r_wait);
WARN_ON(1);
//PRT("scdev_create %s success!\n", p_chrdev-> dev_name);
return 0;
}2.2.2 开发板测试
如下图所示,在加载驱动的时候会打印出这个函数相关的一些调用关系:

3. BUG_ON()
3.1 函数说明
BUG_ON() 函数定义如下:
#ifdef CONFIG_BUG
#ifndef HAVE_ARCH_BUG_ON
#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while (0)
#endif
#else /* ! CONFIG_BUG */
#ifndef HAVE_ARCH_BUG_ON
#define BUG_ON(condition) do { if (condition) BUG(); } while (0)
#endif
#endif内核中有许多地方调用类似 BUG_ON()的语句, 它非常像一个内核运行时的断言, 意味着本来不该执行到 BUG_ON()这条语句, 一旦 BUG_ON()执行内核就会立刻抛出 oops, 导致栈的回溯和错误信息的打印。 大部分体系结构把 BUG()和 BUG_ON()定义成某种非法操作, 这样自然会产生需要的 oops。 参数 condition 判断条件是否成立, 例如 BUG_ON (1)则条件判断成功,函数会成功执行。
3.2 使用实例
3.2.1 demo 源码
static int scdev_create(_CHAR_DEVICE *p_chrdev)
{
// ......
/* 初始化等待队列头 */
init_waitqueue_head(&p_chrdev->r_wait);
BUG_ON(1);
//PRT("scdev_create %s success!\n", p_chrdev-> dev_name);
return 0;
}3.2.2 开发板测试
如下图所示,在加载驱动的时候会打印出栈相关的信息,并产生一个段错误,但是实际上并没有崩溃:

4. panic()
4.1 函数说明
panic() 函数定义如下:
/**
* panic - halt the system
* @fmt: The text string to print
*
* Display a message, then perform cleanups.
*
* This function never returns.
*/
void panic(const char *fmt, ...)
{
// ......
}
EXPORT_SYMBOL(panic);该函数输出打印会 造成系统死机 并将函数的调用关系以及寄存器值都打印出来。
4.2 使用实例
4.2.1 demo 源码
static int scdev_create(_CHAR_DEVICE *p_chrdev)
{
// ......
/* 初始化等待队列头 */
init_waitqueue_head(&p_chrdev->r_wait);
panic("!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
//PRT("scdev_create %s success!\n", p_chrdev-> dev_name);
return 0;
}4.2.2 开发板测试
如下图所示,在加载驱动的时候会打印出栈相关的信息,然后系统崩溃,终端也进不去了:

二、驱动调试 demo
demo 源码可以看这里:10_driver_debug/02_debug_demo