赞
踩
Kexec 是一种可以从当前运行的内核引导进入另一内核的工具。可以在不执行任何硬件初始化的情况下加快系统重引导速度。还可以准备好系统,以便在系统崩溃的情况下引导进入另一内核。
使用 Kexec 可将运行中的内核替换为另一内核,而无需硬性重引导。该工具非常有用,原因如下:
kdump 是系统崩溃的时候,用来转储运行内存的一个工具。
系统一旦崩溃,内核就没法正常工作了,这个时候将由 kdump 提供一个用于捕获当前运行信息的内核,
该内核会将此时内存中的所有运行状态和数据信息收集到一个 dump core文件中以便之后分析崩溃原因。
一旦内存信息收集完成,可以让系统将自动重启。
kexec 是 kdump机制的关键,包含两部分:
内核空间的系统调用kexec_load。负责在生产内核启动时将捕获内核加载到指定地址。
用户空间的工具kexec-tools。将捕获内核的地址传递给生产内核,从而在系统崩溃的时候找到捕获内核的地址并运行。
kdump是一种基于kexec的内核崩溃转储机制。当系统崩溃时,kdump使用kexec启动到第二个内核。第二个内核通常
叫做捕获内核,以很小内存启动以捕获转储镜像。第一个内核保留了内存的一部分给第二个内核启动使用。
由于kdump利用kexec启动捕获内核,绕过了 bootloader,所以第一个内核的内存得以保留。这是内存崩溃转储的本质。
当系统崩溃时,通过 kdump 可以获得当时的内存转储文件 vmcore,但是该如何分析 vmcore 呢?
crash 是一个用于分析内核转储文件的工具,一般和 kdump 搭配使用。
使用 crash 时,要求调试内核 vmlinux
在编译时带有 -g 选项,即带有调试信息。
如果没有指定 vmcore,则默认使用实时系统的内存来分析。 值得一提的是,crash也可以用来分析实时的系统内存,是一个很强大的调试工具。
Crash 工具 是在 GDB 的基础之上进一步开发的,所以 Crash 工具的很多命令在 gdb 上应该都是可以直接使用的,本篇文档主要介绍如何使用 Crash 来分析panic/hang
类型的一些 bug,这里需要注意的是有些 bug,如 hang
,只能使用 etb 工具来查看最后各个 cpu 都在干什么。
在有些平台上,当 kernel 发生 panic 或者 hang 之后,会触发 SoC Watchdog Reset
。有些公司的做法是:reboot 之前会在 PMIC 里面的某个寄存器里面写个值,来表明重启的原因,当再次重启的时候,会去读该寄存器,获取重启的原因。因为有时抓 ramdump 文件,需要获取重启的原因,比如 发生 Kernel Panic
时,就需要获取 watch dog
重启的原因,如果检测到是 Kernel Panic 的话,就会去dump DDR 4G 的内容(因为 SoC reset
时 DDR 内容不会丢失) 到 PC
或者 SD
卡中。
使用 Crash工具之间需要进行工具的路径设置,如下所示:
vim ~/.profile
export PATH=$PATH:..../devtools/crash_tools/:
source ~./profile
bt -a
dis -lx function_name //函数反汇编
dis -lr function_addr //该地址反汇编
struct -o struct_name //查看结构体各成员地址偏移情况
struct struct_name addr -x //查看具体的值
crash> ps -C 0 -m //查看cpu0上面有哪些进程
CPU: 0
[0 00:00:00.024][IN]PID:3440 TASK:ffffffc042cf8000 CPU:0 COMMAND:"com.sina
[0 00:00:00.024][IN]PID:3771 TASK:ffffffc0b2915100 CPU:0 COMMAND:"ina.weib
crash> dfc_info //直接查看静态变量里面的值
crash> p/x 0xffffff80098dd000+3472 //地址的计算
crash> rd 0xffffffc0b2b65118
crash> vtop ffffff8009ea4000 //虚拟地址转为物理地址
crash> kmem -v
VMAP_AREA VM_STRUCT ADDRESS RANGE SIZE
ffffffc04862cd80 ffffffc03c875f00 ffffff8009e96000-ffffff8009ea0000 40960
ffffffc0b2b5a300 ffffffc0b2b5a280 ffffff8009ea4000-ffffff8009ea7000 12288
crash> vmap_area -x -p ffffffc0b2b5a300 //查看kernel虚拟地址的信息,-x:16进制
struct vmap_area {
va_start = 0xffffff8009ea4000, //上面虚拟地址所在进程的虚拟起始地址
va_end = 0xffffff8009ea7000, //上面虚拟地址所在进程的虚拟结束地址
flags = 0x4,
rb_node = {
__rb_parent_color = 0xffffffc0ac64cd19,
rb_right = 0xffffffc0acd7dd18,
rb_left = 0xffffffc0b3517598
},
list = {
next = 0xffffffc0b2bb70b0,
prev = 0xffffffc04862cdb0
},
purge_list = {
next = 0x0,
prev = 0x0
flags = 0x1,
pages = 0x0,
nr_pages = 0x0,
phys_addr = 0xc0000000,
caller = 0xffffff80083d3828 <devm_ioremap+88>
}
callback_head = {
next = 0xffffff80088b811c <snd_soc_info_volsw>,
func = 0xffffff80088b8bec <snd_soc_get_volsw>
}
/home/xiaoming/android9.1/kernel/linux/kernel/rcu/tree.c: 1717
0xffffff8008118f88 <rcu_advance_cbs+32>: ldr x2, [x29,#40]
0xffffff8008118f8c <rcu_advance_cbs+36>: ldr x3, [x2,#88]
0xffffff8008118f90 <rcu_advance_cbs+40>: cbz x3, 0xffffff8008118ff0\
<rcu_advance_cbs+136>
## dis -lx 0xffffff8008118ff0
crash> p sblog_str //查看静态变量(或者全局变量)的值,即地址值
sblog_str = $13 = (struct sblog_struct *) 0xffffffc0b2b16418
crash> struct sblog_struct 0xffffffc0b2b16418 //查看结构体成员的值
struct sblog_struct {
dev = 0xffffffc0b2b6a400,
clk = 0xffffffc0b2b58c00,
res = {
cipc_base_address = 0xffffff8009fc0100,
comm_sb_addr = 0xffffff8013000000 //kernel虚拟地址无法读
/*struct: read error: kernel virtual address: ffffff8013000000
/*type: "gdb_readmem_callback" struct: read error: kernel virtual
/*address: ffffff8013000000 type:"gdb_readmem_callback"
/*<Address 0xffffff8013000000 out of bounds>,
comm_addr_offst = 0xc88000,
comm_base_addr = 0x17000000 <Address 0x17000000 out of bounds>,
ripc_size = 0x88,
irq = 0x1b,
.....
}
}
crash> vtop 0xffffff8009fc0100 //物理地址转换为虚拟地址
VIRTUAL PHYSICAL
ffffff8009fc0100 d40b0100 //所以对应的物理页帧
/* 1. 当前pc的值,也即函数 __stack_chk_fail 走到了这个位置 */
#1 [ffffffc0198bbb20] __stack_chk_fail at ffffff90080baf48
/* 存储的是上个函数的栈帧(FP)与返回地址(LR) */
ffffffc0198bbb20: ffffffc0198bbb40 ffffff9008b91914
ffffffc0198bbb30: 1ffffff803317774 ffffff9008b91680
/* 2. 函数i2cdev_ioctl_smbus当时走到了这里,
/* 所以返回时需要走下一条指令:ffffff9008b91914
#2 [ffffffc0198bbb40] i2cdev_ioctl_smbus at ffffff9008b91910
常见的死锁有两种:
1)递归死锁:例如, 在中断等延迟操作中, 使用了锁,和外面的锁构成了递归死锁。一个锁导致的问题。
2)AB-BA死锁:多个锁因为处理不当而引发死锁,多个内合路径上的锁处理顺序不一致也会导致死锁。这里是多个锁导致的问题。
当处理器在内核空间访问一个非法的指针时,因为虚拟地址到物理地址的映射关系没有建立,触发一个缺页异常,在缺页中断中因为该地址是非法的,内核无法为改地址建立映射关系,因此内核触发了一个Oops错误。
对于没有编译符号的二进制文件,可以使用 objdump 工具来 dump 出汇编代码,例如使用下面命令来 dump oops.o文件
arm-linux-gnueabi-objdump -d oops.o
内核提供了一个很好的脚本,可以用来帮忙快速定位问题,该脚本位于Linux 内核源代码目录的 scripts/decodecode
,首选将 log 保存到一个 txt 文件中:
./scripts/decodecode < oops.txt
对于 bug_on
当满足条件的时候,就会触发 BUG()
宏,它会调用 panic
函数主动让系统宕机,通常是一些 内核bug 才会触发 BUG_ON
,在实际产品中使用该宏需要小心。
WARN_ON
不会触发 panic 但会答应函数调用栈信息,用来提示作用。
下篇文章:ARM Linux 系统稳定性分析入门及渐进 2 – Kernel Lockup
推荐阅读:
https://documentation.suse.com/zh-cn/sles/15-SP2/html/SLES-all/cha-tuning-kexec.html
https://www.cnblogs.com/muahao/p/9884175.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。