一、前言
我们在项目开发过程中,很多时候会出现由于某种原因经常会导致手机系统死机重启的情况(重启分Android
重启跟kernel
重启,而我们这里只讨论kernel
重启也就是 kernel panic
的情况),死机重启基本算是影响最严重的系统问题了,有稳定复现的,也有概率出现的,解题难度也千差万别,出现问题后,通常我们会拿到类似这样的kernel log
信息(下面log
仅以调用BUG()
为例,其它异常所致的死机log
信息会有一些不同之处):
[ 2.052157] <2>-(2)[1:swapper/0]------------[ cut here ]------------
[ 2.052163] <2>-(2)[1:swapper/0]Kernel BUG at c04289dc [verbose debug info unavailable]
[ 2.052169] <2>-(2)[1:swapper/0]Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM
[ 2.052178] <2>-(2)[1:swapper/0]disable aee kernel api[ 3.052192] <2>-(2)[1:swapper/0]Non-crashing CPUs did not react to IPI
[ 3.052204] <2>-(2)[1:swapper/0]CPU: 2 PID: 1 Comm: swapper/0 Tainted: G W 3.18.35+ #3
[ 3.052211] <2>-(2)[1:swapper/0]task: df060000 ti: df04a000 task.ti: df04a000
[ 3.052227] <2>-(2)[1:swapper/0]PC is at ltr553_i2c_probe+0x94/0x9c
[ 3.052233] <2>-(2)[1:swapper/0]LR is at 0x0
[ 3.052242] <2>-(2)[1:swapper/0]pc : [<c04289dc>] lr : [<00000000>] psr: a0000113
[ 3.052242] <2>sp : df04bd30 ip : 00000000 fp : df04bd4c
[ 3.052249] <2>-(2)[1:swapper/0]r10: 00000003 r9 : de348fc0 r8 : c0428948
[ 3.052255] <2>-(2)[1:swapper/0]r7 : dea1bc00 r6 : dea1bc04 r5 : dea1bc20 r4 : c0b53358
[ 3.052262] <2>-(2)[1:swapper/0]r3 : c115ef4c r2 : 00000000 r1 : 00000000 r0 : de366a00
[ 4.354655] <2>-(2)[1:swapper/0] oops_end, 1, 11
[ 4.354740] <2>-(2)[1:swapper/0]Kernel panic - not syncing: Fatal exception
这是linux
内核在死机之前输出的相关重要信息,包括PC
指针、调用栈等在内的非常重要的便于Debug
的线索,比如我们可以借助GUN tools
(add2Line
)工具结合内核符号映射表vmlinux
来定位当前PC
指针所在的代码具体行数(定位到出错代码行并不意味着就找到了问题的根本原因跟修复异常,这个需要根据异常的复杂程度而论)。深入理解这些关键打印log
信息的含义和机制非常有助于我们对于此类死机问题的定位和分析(对于内存被踩、硬件不稳定导致的一类问题分析有局限性),这也是我们需要深入学习内核异常流程的初衷。
这里我们必须弄清楚几个问题:
- 这些死机前留下的关键
register
信息是怎么来的,有什么用,具体含义是什么? - 如何利用这些遗留的线索找到出问题代码具体在哪支文件,在哪一行?
- 内核发生致命异常到死机的总流程是怎样的,类似死机问题应该如何着手分析?
为此,本文就从最常见的主动触发BUG()
为例解析上面的疑问及分析整个kernel panic
流程。
二、什么是BUG()
?
有过驱动调试经验的人肯定都知道这个东西,这里的BUG
跟我们一般认为的“软件缺陷”可不是一回事,这里说的BUG()
其实是linux kernel
中用于拦截内核程序超出预期的行为,属于软件主动汇报异常的一种机制。这里有个疑问,就是什么时候会用到呢?一般来说有两种用到的情况,一是软件开发过程中,若发现代码逻辑出现致命fault
后就可以调用BUG()
让kernel
死掉(类似于assert
),这样方便于定位问题,从而修正代码执行逻辑;另外一种情况就是,由于某种特殊原因(通常是为了debug
而需抓ramdump
),我们需要系统进入kernel panic
的情况下使用.
- 原形是什么?
BUG()
跟BUG_ON(1)
其实本质是一回事,后者只是在前者的基础上做了简单的封装而已,BUG()
的实现 本质是埋入一条未定义指令:0xe7f001f2
,触发ARM
发起Undefined Instruction
异常(PS:ARM
有分10
种异常类型,详细可以复习ARM
异常模型章节).
<kernel-3.18/arch/arm/include/asm/bug.h>
#define BUG_INSTR_VALUE 0xe7f001f2
#define BUG_INSTR(__value) __inst_arm(__value)
#define BUG() _BUG(__FILE__, __LINE__, BUG_INSTR_VALUE)
#define _BUG(file, line, value) __BUG(file, line, value)
#define __BUG(__file, __line, __value) \
do { \
asm vol