当前位置:   article > 正文

【ARM Linux 系统稳定性分析入门及渐进 1 -- Crash 工具简介】_linux ramdump

linux ramdump


请阅读【ARM Linux 系统稳定性分析专栏导读】


下篇文章:ARM Linux 系统稳定性分析入门及渐进 2 – Kernel Lockup

1.1 Kexec 和 Kdump

Kexec 是一种可以从当前运行的内核引导进入另一内核的工具。可以在不执行任何硬件初始化的情况下加快系统重引导速度。还可以准备好系统,以便在系统崩溃的情况下引导进入另一内核。

1.1.1 Kexec 加载优点

使用 Kexec 可将运行中的内核替换为另一内核,而无需硬性重引导。该工具非常有用,原因如下:

  • 加快系统重引导速度;
  • 如果需要频繁重引导系统,Kexec 可以节省大量时间;
  • 避免不可靠的固件和硬件, 计算机硬件非常复杂,在系统启动期间可能会出现严重问题。并不总能立即更换掉不可靠的硬件。Kexec 可将内核引导到已初始化硬件的受控环境。这样,就可以最大程度地减少系统启动失败的风险;
  • 保存崩溃内核的转储, Kexec 可以保留物理内存的内容。在生产内核发生故障后,捕获内核(在预留内存范围内运行的附加内核)会保存有故障内核的状态。保存的映像可帮助后续分析;
  • 无需 bootlader 配置即可引导,当系统使用 Kexec 引导内核时,会跳过引导加载程序阶段。常规引导过程可能会由于引导加载程序配置中的错误而失败。使用 Kexec,就不需要考虑引导加载程序配置是否有效。

1.1.2 kdump 功能

kdump 是系统崩溃的时候,用来转储运行内存的一个工具。
系统一旦崩溃,内核就没法正常工作了,这个时候将由 kdump 提供一个用于捕获当前运行信息的内核,

该内核会将此时内存中的所有运行状态和数据信息收集到一个 dump core文件中以便之后分析崩溃原因。

一旦内存信息收集完成,可以让系统将自动重启

1.1.3 kdump原理

kexec 是 kdump机制的关键,包含两部分:
内核空间的系统调用kexec_load。负责在生产内核启动时将捕获内核加载到指定地址。

用户空间的工具kexec-tools。将捕获内核的地址传递给生产内核,从而在系统崩溃的时候找到捕获内核的地址并运行。

kdump是一种基于kexec的内核崩溃转储机制。当系统崩溃时,kdump使用kexec启动到第二个内核。第二个内核通常

叫做捕获内核,以很小内存启动以捕获转储镜像。第一个内核保留了内存的一部分给第二个内核启动使用。

由于kdump利用kexec启动捕获内核,绕过了 bootloader,所以第一个内核的内存得以保留。这是内存崩溃转储的本质。

1.2 Crash tool

当系统崩溃时,通过 kdump 可以获得当时的内存转储文件 vmcore,但是该如何分析 vmcore 呢?
crash 是一个用于分析内核转储文件的工具,一般和 kdump 搭配使用。
使用 crash 时,要求调试内核 vmlinux 在编译时带有 -g 选项,即带有调试信息。

如果没有指定 vmcore,则默认使用实时系统的内存来分析。 值得一提的是,crash也可以用来分析实时的系统内存,是一个很强大的调试工具。

Crash 工具 是在 GDB 的基础之上进一步开发的,所以 Crash 工具的很多命令在 gdb 上应该都是可以直接使用的,本篇文档主要介绍如何使用 Crash 来分析panic/hang 类型的一些 bug,这里需要注意的是有些 bug,如 hang,只能使用 etb 工具来查看最后各个 cpu 都在干什么。

1.2.1 ramdump 机制

在有些平台上,当 kernel 发生 panic 或者 hang 之后,会触发 SoC Watchdog Reset。有些公司的做法是:reboot 之前会在 PMIC 里面的某个寄存器里面写个值,来表明重启的原因,当再次重启的时候,会去读该寄存器,获取重启的原因。因为有时抓 ramdump 文件,需要获取重启的原因,比如 发生 Kernel Panic时,就需要获取 watch dog 重启的原因,如果检测到是 Kernel Panic 的话,就会去dump DDR 4G 的内容(因为 SoC resetDDR 内容不会丢失) 到 PC 或者 SD卡中。

1.2.2 Crash 路径设置

使用 Crash工具之间需要进行工具的路径设置,如下所示:

vim ~/.profile 
export PATH=$PATH:..../devtools/crash_tools/:
source ~./profile
  • 1
  • 2
  • 3

1.2.3 Crash 常用命令

bt -a
dis -lx function_name       //函数反汇编
dis -lr function_addr       //该地址反汇编
  • 1
  • 2
  • 3
struct -o struct_name      //查看结构体各成员地址偏移情况
struct struct_name addr -x  //查看具体的值
  • 1
  • 2
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
  • 1
  • 2
  • 3
  • 4
crash> dfc_info                    //直接查看静态变量里面的值
  • 1
crash> p/x 0xffffff80098dd000+3472 //地址的计算
crash> rd 0xffffffc0b2b65118
crash> vtop ffffff8009ea4000      //虚拟地址转为物理地址
  • 1
  • 2
  • 3
crash> kmem -v
VMAP_AREA         VM_STRUCT         ADDRESS RANGE                   SIZE
ffffffc04862cd80 ffffffc03c875f00 ffffff8009e96000-ffffff8009ea0000 40960
ffffffc0b2b5a300 ffffffc0b2b5a280 ffffff8009ea4000-ffffff8009ea7000 12288
  • 1
  • 2
  • 3
  • 4
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>
}
  • 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
/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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
crash> p sblog_str  //查看静态变量(或者全局变量)的值,即地址值
sblog_str = $13 = (struct sblog_struct *) 0xffffffc0b2b16418
  • 1
  • 2
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, 
    .....
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
crash> vtop 0xffffff8009fc0100 //物理地址转换为虚拟地址
VIRTUAL           PHYSICAL        
ffffff8009fc0100  d40b0100     //所以对应的物理页帧
  • 1
  • 2
  • 3

1.2.4 bt 栈查看

/* 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
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

1.3 Kernel 常见宕机问题

1.3.1 死锁问题

常见的死锁有两种:
1)递归死锁:例如, 在中断等延迟操作中, 使用了锁,和外面的锁构成了递归死锁。一个锁导致的问题。
2)AB-BA死锁:多个锁因为处理不当而引发死锁,多个内合路径上的锁处理顺序不一致也会导致死锁。这里是多个锁导致的问题。

1.3.2 OOPs

当处理器在内核空间访问一个非法的指针时,因为虚拟地址到物理地址的映射关系没有建立,触发一个缺页异常,在缺页中断中因为该地址是非法的,内核无法为改地址建立映射关系,因此内核触发了一个Oops错误

对于没有编译符号的二进制文件,可以使用 objdump 工具来 dump 出汇编代码,例如使用下面命令来 dump oops.o文件

arm-linux-gnueabi-objdump -d oops.o
  • 1

内核提供了一个很好的脚本,可以用来帮忙快速定位问题,该脚本位于Linux 内核源代码目录的 scripts/decodecode ,首选将 log 保存到一个 txt 文件中:

./scripts/decodecode < oops.txt
  • 1

1.3.3 BUG_ON和WARN_ON

对于 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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/755402
推荐阅读
相关标签
  

闽ICP备14008679号