explorer

万丈高楼平地起,勿在浮沙筑高台

0%

[What]Linux内核调试之Oops

总结在Linux内核下使用 Oops 输出调试内核。

输出分析

当内核出现异常时,内核会抛出Oops信息,这些信息会被输出到控制台。

在Oops 的输出信息中, 需要注意的一些信息是:

  • Oops 最开始的输出信息中已经大致说明了异常的原因
  • PC is at [function_name] + [address]
    • 此行指出了出错的函数, 以及执行语句在函数中的偏移地址. 然后可以通过命令得出反汇编代码, 找出C代码位置
arm-linux-gnueabihf-objdump -d -S file.o
  • 寄存器列表
    • 当出错的函数有参数时, 可以通过寄存器列表来查看输入的参数是否正确(如果参数过多, 还要查看栈信息)
  • 函数调用顺序
    • 通过查看栈信息, 可以知道此函数是如何被以层层调用进来的

Oops 与 Panic

Panic 属于 Oops 的一种,内核发现异常会抛出Oops,但错误不严重的情况下内核还会继续运行。如果严重则会抛出 Panic,此时内核便会停止运行。

但为了便于调试,一般需要做如下操作:

echo 1 > /proc/sys/kernel/panic_on_oops

这是为了内核无论是出现Oops还是Panic都要停止内核运行,以便正确并及时的捕捉错误的位置。

显示使用 BUG_ON() 和 WARN_ON()

BUG_ON() 和 WARN_ON() 是内核提供的警告宏。

首先使能 CONFIG_BUG 宏:

General setup -> Configure standard kernel features(expert users) -> BUG() support

其中BUG_ON()成立时主动抛出 Oops 的宏,WARN_ON() 成立时抛出栈调用。我们在必要的位置可以加上者两个宏,以达到:

  • 判断出了错误
  • 查看错误时的参数调用
  • 查看函数调用栈
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//file: include/asm-generic/bug.h
/**
* @brief 立即抛出 Oops 并内核停止
*/
#ifndef HAVE_ARCH_BUG
#define BUG() do { \
printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \
panic("BUG!"); \
} while (0)
#endif

/**
* @brief 当condition为真时抛出 Oops 并内核停止
*/
#ifndef HAVE_ARCH_BUG_ON
#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while (0)
#endif


/**
* @brief __WARN()立即抛出调用栈
*/
#define WANT_WARN_ON_SLOWPATH
#define __WARN() warn_slowpath_null(__FILE__, __LINE__)
#define __WARN_printf(arg...) warn_slowpath_fmt(__FILE__, __LINE__, arg)
#define __WARN_printf_taint(taint, arg...)\
warn_slowpath_fmt_taint(__FILE__, __LINE__, taint, arg)
#else
#define __WARN() __WARN_TAINT(TAINT_WARN)
#define __WARN_printf(arg...) do { printk(arg); __WARN(); } while (0)
#define __WARN_printf_taint(taint, arg...)\
do { printk(arg); __WARN_TAINT(taint); } while (0)
#endif

/**
* @brief 当condition为真时抛出警告信息并打印调用栈
*/
#ifndef WARN_ON
#define WARN_ON(condition) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN(); \
unlikely(__ret_warn_on); \
})
#endif

#ifndef WARN
#define WARN(condition, format...) ({ \
int __ret_warn_on = !!(condition); \
if (unlikely(__ret_warn_on)) \
__WARN_printf(format); \
unlikely(__ret_warn_on); \
})
#endif
Last Updated 2021-01-26 二 12:12.
Render by hexo-renderer-org with Emacs 26.3 (Org mode 9.4)