在Android中使用kdump

作者 by adtxl / 2022-08-29 / 暂无评论 / 189 个足迹

1. 简介

内核核心转储又成为 core dump,在 Unix/Linux 中,将主内存 “Main Memory” 称为核心 core, 这是因为在半导体作为内存材料前,便使用核心 “core” 表示内存。另外核心镜像 “core image” 就是内核作为一个内核线程执行时在内存中的内容。当内核线程发生错误或者收到特定信号而终止执行时,系统在借助某些工具的情况下,可以将核心镜像写入一个文件,以便后期调试问题之用,这个过程就是所谓的核心转储 “core dump”.

目前主流的方法获得 core dump 是通过 kexec 工具,该工具当内核核心转储时,其他 CPU 夯住,kexec 工具会在一个预留 CPU 上重启一个精简的内核。当精简内核启动之后,可以通过一定的命令将 core 写入到指定的文件,该文件就是核心转储文件。当获得核心转储文件之后,可以使用 CRASH 等工具进行问题分析。

1.当第一内核(生产内核)启动时预留一定大小的内存空间(reserved memory),第一内核启动完成后使用kexec工具将第二内核(捕获内核)和一个简易的initrd rootfs加载到预留的内存中,如下图。

image.png

2.当生产内核发生crash时,会自动引导捕获内核启动,捕获内核只占用预留的内存,并且在捕获内核中可以通过/proc/vmcore文件获取到发生crash之前的所有内存数据,此时只需要拷贝vmcore到磁盘,便可完成内存转储工作。

imagef7de4ceed9cfba77.png

目前已知Kdump可以覆盖到的情况:

  1. 系统发生crash
  2. 系统发生Soft lockup

注:由硬件导致的系统hang住,kdump目前没法覆盖到。

2. 实现步骤

下面主要记录一些流程,不会写的很细

2.1 下载并编译kexec-tools

在调试过程中,经常需要重启内核以还原现场,进而复现某些问题予以追踪解决. 由于每一次的内核启动,都会伴随着一次的 boot 自检. 但是,对于已经启动过的同一内核,重复的 boot 自检完全没有必要,且造成了资源浪费. 此外,有时候需要使用一个小内核来启动一个大内核. 在这两种需求下,kexec 应运而生,kexec 是一款可以让您重新启动到一个新 Linux 内核的快速重新引导功能部件,不再必须通过固件和引导装载程序阶段,从而跳过序列中最长的部分,大大减少了重启时间. 对企业级系统而言,Kexec 大大减少了重启引起的系统宕机时间. 对内核和系统软件开发者而言,Kexec 帮助您在开发和测试成果时可以迅速重新启动系统,而不必每次都要再经历耗时的固件阶段. 通用 linux 系统中可用通过源码编译的方式安装 kexec-tools 工具,开发者可以参考如下步骤:

  1. 源码下载
wget http://kernel.org/pub/linux/utils/kernel/kexec/kexec-tools.tar.gz
  1. 编译(以ARM64为例)
cd kexec-tools-VERSION
./configure ARCH=arm64 --build=x86_64-unknown-linux-gnu --host=aarch64-linux LDFLAGS=-static
make
make install

2.2 修改当前内核配置

正常运行的kernel,需要下面的config配置

CONFIG_KEXEC=y
CONFIG_SYSFS=y
CONFIG_DEBUG_INFO=Y

2.3 制作捕获内核和initramfs

2.3.1 编译最小文件系统

编译busybox,注意编译的时候进入menuconfig配置成静态编译,如何下载编译从网上找个就好了。
编译完成后,在busybox根目录下会有一个_install的目录,该目录是编译好的文件系统需要的一些命令的集合。

进入_install目录,先创建etc、dev等目录。

#mkdir etc
#mkdir dev
#mkdir mnt
#mkdir -p etc/init.d

_install/etc/init.d目录下新创建一个rcS文件,并写入如下内容:

#! /bin/sh

mkdir -p /proc
mkdir -p /tmp
mkdir -p /sys
mkdir -p /mnt
/bin/mount -a
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

将rcS文件的权限改为可执行权限,比如:chmod +x _install/etc/init.d/rcS

_install/etc目录新创建一个fstab文件,并写入以下内容:

proc /proc proc defaults 0 0 
tmpfs /tmp tmpfs nodev,nosuid 0 0 
sysfs /sys sysfs defaults 0 0 
debugfs /sys/kernel/debug debugfs defaults 0 0 

_install/etc目录新创建一个inittab文件,并写入以下内容:

::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r

_install/dev目录下创建如下设备节点,需要root权限:

$cd _install/dev
$sudo mknod console c 5 1
$sudo mknod null c 1 3

2.3.2 编译kernel

将上面的_install目录复制到kernel源码目录下面。

通过menuconfig,配置initramfs,在initramfs source file中填入_install

制作捕获kernel可以裁掉一些不必要的配置,以减少内存的占用,但最好保留usb driver,以方便拷贝vmcore文件。
必须包含下面两个配置:

CONFIG_CRASH_DUMP=y
CONFIG_PROC_VMCORE=y

注意:

1.关闭这两个config

CONFIG_DMA_CMA=y
CONFIG_CMA_SIZE_MBYTES=32

2.确保usb driver是编译进kernel的

2.4 在uboot中添加bootargs

使用下面的命令或者修改kernel command line,在kernel启动时为捕获内核预留内存,预留太少可能导致捕获内核启动有问题,根据自己的实际情况预留即可。可以通过开机log来判断预留内存是否成功。

setenv bootargs $bootargs 'crashkernel=128M'
saveenv

2.5 加载捕获内核到预留内存

使用kexec工具,首先要通过adb push,将编译好的kexec,Image,dtb文件push 到开发板中,然后使用下面的命令

   kexec -p /vendor/etc/Image --dtb="/vendor/etc/xxx8421.dtb" --append="console=ttyS0,115200n8 earlycon init=/init rootfstype=ramfs rootwait 1 maxcpus=1 reset_devices"

2.6 验证与测试

使用 echo c > /proc/sysrq-trigger 命令触发kernel panic,打印完Call trace信息之后会引导捕获kernel启动.

如果启动没成功,就根据log来解决就好了。
ps:本人遇到的一个问题是,当时捕获内核有个默认的CMA配置,会reserve 32M内存,导致的启动失败。解决办法:在config把这个配置去掉就好了

启动后,生成的vmcore文件在/proc/vmcore,将/proc/vmcore拷贝保存到其他地方以供后期分析,也可压缩后再进行拷贝。

例如,拷贝到u盘

/ # fdisk -l
Disk /dev/mmcblk0: 7456 MB, 7818182656 bytes, 15269888 sectors
946 cylinders, 256 heads, 63 sectors/track
Units: sectors of 1 * 512 = 512 bytes

Device       Boot StartCHS    EndCHS        StartLBA     EndLBA    Sectors  Size Id Type
/dev/mmcblk0p1    0,0,2       1023,255,63          1 4294967295 4294967295 2047G ee EFI GPT
Partition 1 has different physical/logical end:
     phys=(1023,255,63) logical=(266305,4,4)
Disk /dev/sda: 29 GB, 30943995904 bytes, 60437492 sectors
3762 cylinders, 255 heads, 63 sectors/track
Units: sectors of 1 * 512 = 512 bytes

Device  Boot StartCHS    EndCHS        StartLBA     EndLBA    Sectors  Size Id Type
/dev/sda1 *  0,32,33     1023,254,63       2048   60436479   60434432 28.8G  c Win95 FAT32 (LBA)

/ # mount /dev/sda1 /mnt/
/ # cp /proc/vmcore /mnt/
/ # umount /mnt/

关于分析该文件可以使用crash工具,这个后面还需要继续研究。

参考:

  1. 内核核心转储: Kdump with kexec and crash

独特见解