嵌入式与Linux那些事

电子技术应用专栏作家——嵌入式与Linux那些事。关注嵌入式与Linux的校招社招,本人整理了《嵌入式软件工程师笔试面试指南》PDF,平时发布嵌入式与Linux相关的实用技术文章

【调试】kdump原理及其使用方法

0
阅读(1155)

kdump机制

简介

Kdump是在系统崩溃、死锁或死机时用来转储内存运行参数的一个工具和服务,是一种新的crash dump捕获机制,用来捕获kernel crash(内核崩溃)的时候产生的crash dump

Kdump 使用两个内核:生产内核和捕获内核。生产内核是一个普通内核,它使用特殊的 kdump 特定标志启动。我们需要告诉生产内核保留一些物理内存,用于加载捕获内核。我们需要提前加载捕获内核,因为在崩溃发生的那一刻,由于内核损坏,无法从磁盘读取任何数据。

生产内核是捕获内核服务的对像。捕获内核会在生产内核崩溃时启动起来,与相应的ramdisk一起组建一个微环境,用以对生产内核下的内存进行收集和转存。

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

dump原理

为了在生产内核崩溃时能顺利启动捕获内核,捕获内核以及它的ramdisk是事先放到生产内核的内存中的

生产内核的内存是通过/proc/vmcore这个文件交给捕获内核的。为了生成它,用户工具在生产内核中分析出内存的使用和分布等情况,然后把这些信息综合起来生成一个ELF头文件保存起来。

捕获内核被引导时会被同时传递这个ELF文件头的地址,通过分析它,捕获内核就可以生成出/proc/vmcore。有了/proc/vmcore这个文件,捕获内核的ramdisk中的脚本就可以通过通常的文件读写和网络来实现各种策略了。

注意,在启动时,kdump保留了一定数量的重要的内存,为了计算系统需要的真正最小内存,加上kdump使用的内存数量,以决定真正的最小内存的需求。

image.png

支持架构

x86,x86_64,arm,arm64,ppc,s390,sh

kexec机制

kexec简介

Kexec是基于kexec机制工作的,因此先了解一下Kexec。

kexec是一个快速启动机制,允许通过已经运行的内核的上下文启动一个Linux内核,不需要经过BIOS。(BIOS可能会消耗很多时间,特别是带有众多数量的外设的大型服务器。这种办法可以为经常启动机器的开发者节省很多时间。)

Kexec的实现包括2个组成部分:

** 一是内核空间的系统调用:kexec_load() **,负责在生产内核(production kernel 或 first kernel)启动时将捕获内核(capture kernel或sencond kernel)加载到指定地址。

** 二是用户空间的工具kexec-tools **,他将捕获内核的地址传递给生产内核,从而在系统崩溃的时候能够找到捕获内核的地址并运行。没有kexec就没有kdump。先有kexec实现了在一个内核中可以启动另一个内核,才让kdump有了用武之地。

kexec_load()

kexec 在 kernel 里以一个系统调用 kexec_load()的形式提供给用户。这个系统调用主要用来把另一个内核和其 ramdisk 加载到当前内核中。在 kdump中,捕获内核只能使用事先预留的一小段内存。

生产内核的内存镜像会被以 /proc/vmcore 的形式提供给用户。这是一个 ELF格式的方件,它的头是由用户空间工具 kexec 生成并传递来的。在系统崩溃时,系统最后会调用machine_kexec()。这通常是一个硬件相关的函数。它会引导捕获内核,从而完成 kdump 的过程。

kexec-tools

kdump 的很大一部分工作都是在用户空间内完成的。与 kexec相关的集中在一个叫kexec-tools的工具中的kexec程序中。

该程序主要是为调用 kexec_load()收集各种信息,然后调用之。这些信息主要包括 purgatory 的入口地址,还有一组由 struct kexec_segment描述的信息。

image.png

kdump使用

内核配置

修改内核中以下的配置宏,可在.config文件中修改,或者通过make menuconfig修改

image.png

如果出现proc/kcorekexec相关节点说明配置生效了。

配置预留内存

预留内存的4种形式

预留内存的设置一般有4种形式:

  1. 第一种是最常用的,直接通过size指定预留的大小,offset指定预留内存地址的起始位置。不过,offset一般不指定,对于一般用户来讲,很难确定预留内存恶起始位置。

image.png

参数含义如下:

如果RAM大小小于512M,则不预留内存。

如果RAM大小为512M - 2G,则预留 64M。

如果RAM大小为2 - 6G,则预留 256M。

如果RAM大小大于8G,则预留768 M。

  1. 一般我们会在0~4G范围内预留内存。如果系统内存大于4G,则支持在4G以上预留内存,比如X86_64架构。当指定high参数时,系统会在0~4G和4G以上预留两段内存。默认情况下,x86_64会在4G以下预留256M内存。

crashkernel=size[KMG],hign

  1. low参数主要是配合high来使用的。如果觉得4G以下默认预留的256M太多了,可以手动指定预留内存。

crashkernel=size[KMG],low

在ARM上配置预留内存

在X86-64主机上一般是修改/etc/default/grup中的参数来配置及检查, 但是在嵌入式设备上因为是裁剪的系统,并没有grup这个文件。

但我们可以知道,配置grup文件的目的就是更改cmdline中的内容,那我们如何去更改cmdline的内容呢?提供以下几个思路:

  • 在dts中中添加:修改chosen

  • 在BoardConfig中添加

  • 在uboot中添加:在源码中添加或者通过setenv配置bootargs变量

  • 在android的Makefile中添加

这里我们选择在dts中修改。

image.png

预留内存大小评估

在某些情况下,我们需要正确评估预留内存的大小,主要从以下2个方面考虑。

  1. 系统内kernel,initrd,romfs,devices driver的大小。

  2. 捕获内核启动cpu的个数。启动cpu越多,需要的内存越大。一般情况下,捕获内核一般启动一个CPU核即可。

/proc/iomem表示的是系统的物理内存布局, System RAM entry表示当前系统可用的预留内存。例如,我当前设备的内存为3.8G,预留800M内存也是足够的。

image.png

查看kexec参数。

image.png

image.png

注意以下几个参数

image.png

vmlinux,Image,uImage,zImage区别参考:secure boot (一)FIT Image

测试

配置kexec

尝试手动配置kexec

kexec --t vmlinux -p /root/var/vmlinux --ramdisk /root/var/ramdisk.img --append="storagemedia=emmc androidboot.storagemedia=emmc androidboot.mode=normal  storagenode=sdhci@fe330000 androidboot.slot_suffix= androidboot.serialno=3fdce35e50641399  ro rootwait earlycon=uart8250,mmio32,0xff1a0000 swiotlb=1 console=ttyFIQ0 root=PARTLABEL=rootfs rootfstype=ext4 overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 crashkernel=256M"

command line 可以通过 cat /proc/cmdline  查看。

ramdisk.img 也可以叫做initrd.img,  它是一个小文件系统,麻雀虽小五脏俱全,它介于kernel 和 文件系统之间。kernel 启动后会先执行ramdisk.img 里面的init, 挂载这里的小型文件系统,接着开始完成一些必要的操作,最后在交给文件系统/sbin/init 进行执行。

查看捕获内核的加载状态 0:未加载,1:已加载

image.png

kexec_load_disabled:表示kexec_load系统调用是否被禁止,此系统调用用于kdump。当发生了一次kexec_load后,此值会自动设置为1。

测试启动捕获内核

在前面的准备工作完成后,如果触发系统崩溃,系统将重新引导到转储-捕获内核,触发点位于panic()die()die_nmi()和sysrq处理程序中。接下来我将通过 魔术键来触发系统panic。

开启sysrq

echo 1 > /proc/sys/kernel/sysrq 

触发sysrq

echo c > /proc/sysrq-trigger

触发sysrq后,系统重启,串口打印出标志性 log:Bye!Starting crashdump kernel...。

image.png.

image.png

image.png

系统正常启动后,就可以将/proc/vmcore文件拷贝出来在ubuntu上用crash工具分析。

常见问题及解决办法

在ARM平台上,系统崩溃后卡死,未启动第二内核

不清楚是宿主机的原因还是代码原因,目前主线的 Linux kernel 代码在执行命令使第一个内核崩溃之后,跳转到第二个内核的过程中卡死,在社区上也有其他人遇到了类似的情况并给出了补丁,但是并没有合并到主线,不过目前为了演示暂时不考虑为何原因导致这个问题的出现,如果你的 arm64 不存在这个问题,那么就不需要打这个补丁了。奉上补丁如下:

image.png

还有一点需要说明的就是在 Ubuntu 默认仓库的 crash 不支持最新版本的 Linux 内核,需要更新到 7.2.5 版本才可以。

没有生产vmcore

按照kdump执行流程,确定问题来自那个阶段。

预留内存失败

预留内存过大,设备没有足够的可用内存。默认会在0~4G预留内存。比如预留512M的空间,而在0~4G并没有可用的512M空间,就会导致预留失败。

加载内核失败

  1. 是否预留内存,crashkernel是否配置?

  2. 预留内存失败。

  3. 预留内存成功:尝试使用kexec -d -p 查看失败的具体原因。

第二内核启动失败

  1. 打印出bye后没有任何信息输出,可能是第二内核可能未配置串口,earlycon/console

  2. oom后卡死,可能是预留内存太小。

  3. 驱动初始化失败。有些驱动,比如dma32,可能只能使用0~4G内存,在4G以上预留内存会导致驱动加载失败。

makedumpfile失败

加上-D,打印出debug选项,查看失败原因。

用户态工具问题

kernel,kexec,makedumpfile,crash匹配问题,更新到最新的工具。


原文链接:https://mp.weixin.qq.com/s/HQM7PyS0NdA5YaWACulYWw

微信图片_20220708145705.jpg

电子技术应用专栏作家 嵌入式与Linux那些事