為什么想起寫這篇文章呢?第一是最近在研究 kdump/kexec 等系列的內核崩潰現場保護能力,所以有相關的技術積累,但是這篇文章不打算什么分析,因為接下來的文章我會分析什么是 kdump 以及如何實現內核崩潰現場保護;第二是因為方便記憶,我自己平常不太喜歡寫 Word 這種類型的工作總結,比較喜歡 Markdown 這種標記性語言,他方便我很快的排版以及很好的代碼展示,所以我寫到了這里,既方便了自己也可以服務於有需要的人;還有第三點就是我在剛開始調試 kdump 的時候,手頭上沒有什么特別好的資料,終於在網上找到一份 Video,內容大概是手把手的演示怎么在虛擬機中使用 kdump,這不就是我要找的嗎?WTF ,居然在淘寶上銷售,價格為 299!好吧,我不反對技術服務收費,而且我鼓勵朋友們可以為知識付費1,但是很討厭這種伸手來要錢的范,明明所有的技術都是公開的,沒有一行代碼是你寫的,偏偏就是做了個大約 20 多分鍾的演示視頻居然收費,還這么貴。不管怎樣,我將在博客文章中演示該在虛擬機中利用 kdump 進行崩潰調試。
准備工作
自然要做 kdump 的虛擬機內部崩潰演示,首先得准備一下一個完整的虛擬機環境,由於我的宿主機情況比較復雜,我首先進入到一個相對比較完善的 Docker 鏡像環境中,這樣做的好處就是哪怕虛擬機被我折騰得再亂或者崩潰都不會影響到我的宿主機的其他服務(這台宿主機是一個代碼儲存服務器,運行着很多相關的服務,不允許我瞎折騰)。
啟動 docker 環境
在文章《Ubuntu 下的 Docker 安裝與使用 》中我已經介紹了如何安裝與配置基本的 docker 服務,現在只需要按照文檔中的流程拉取一個需要的 docker 鏡像即可,作為演示我選擇的目前比較穩定的 debian:buster 版本。
[jackieliu@localhost ~]$ sudo docker pull debian:buster-20190204
安裝虛擬機軟件包
直接啟動這個 docker 鏡像並進入容器內部,執行相關的虛擬機軟件包安裝。
[jackieliu@localhost ~]$ sudo docker run -itd debian:buster-20190204
7d05ac0f566c836d74cebfe9d8d18d09b29bdf1d407686e0584324511bba1f36
[jackieliu@localhost ~]$ sudo docker exec -it 7d05ac0f566c bash root@Kylin:/$ apt update Fetched 1789 kB in 25s (72.6 kB/s) Reading package lists... Done Building dependency tree Reading state information... Done root@Kylin:/$ apt install qemu-system-aarch64 -y
等待安裝 qemu 虛擬機安裝完畢即可,需要注意的是在 docker 的文章中我提到的容器內部所有的動作,包括裝包或者文件的修改都需要在宿主機上進行 docker commit
,不然等你退出這個虛擬機再次啟動鏡像時,這些修改又不存在了。
制作 ubuntu 系統鏡像 rootfs.img 文件
進入 Qemu 虛擬機系統需要准備好虛擬機的 rootfs 文件系統,我們可以從 Ubuntu 的官方下載一個比較基礎的系統即可,如果還需要更為復雜的 rootfs,可以在網上搜尋其他人制作的文件系統鏡像文件。
root@Kylin:/$ wget http://cdimage.ubuntu.com/ubuntu-base/releases/18.04/release/ubuntu-base-18.04.1-base-arm64.tar.gz
--2019-03-04 07:37:23-- http://cdimage.ubuntu.com/ubuntu-base/releases/18.04/release/ubuntu-base-18.04.1-base-arm64.tar.gz
Resolving cdimage.ubuntu.com (cdimage.ubuntu.com)... 91.189.88.168, 2001:67c:1360:8001::28
Connecting to cdimage.ubuntu.com (cdimage.ubuntu.com)|91.189.88.168|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 27767381 (26M) [application/x-gzip]
Saving to: 'ubuntu-base-18.04.1-base-arm64.tar.gz'
通過 dd 命令創建一個大小為 4G2 的文件 rootfs.img 並格式化為 ext4 文件系統。
root@Kylin:/$ dd if=/dev/zero of=rootfs.img bs=1M count=4k oflag=direct 4096+0 records in 4096+0 records out 4294967296 bytes (4.3 GB, 4.0 GiB) copied, 38.4543 s, 112 MB/s root@Kylin:/$ mkfs.ext4 rootfs.img mke2fs 1.44.5 (15-Dec-2018) Discarding device blocks: done Creating filesystem with 1048576 4k blocks and 262144 inodes Filesystem UUID: 11f935ee-69b8-4f7f-aab7-20b204f83574 Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736 Allocating group tables: done Writing inode tables: done Creating journal (16384 blocks): done Writing superblocks and filesystem accounting information: done
接下來就掛載這個文件鏡像到系統,並解壓 Ubuntu 官方的 tar.gz 系統到該目錄,卸載該文件鏡像,一個完整的 ubuntu 系統就被導入到 rootfs.img 中。
root@Kylin:/$ mkdir -p rootfs && mount rootfs.img rootfs
root@Kylin:/$ tar -xvf ubuntu-base-18.04.1-base-arm64.tar.gz -C rootfs
root@Kylin:/$ umount rootfs
重編內核 Image
首先需要從官方地址下載最新的 Linux 內核源碼,然后拷貝並修改配置文件 arch/arm64/config/defconfig
到 .config,需要打開 CONFIG_KEXEC=y
、CONFIG_SYSFS=y
、CONFIG_DEBUG_INFO=y
、CONFIG_CRASH_DUMP=y
、CONFIG_PROC_VMCORE=y
配置選項,具體請參考 Linux 內核的官方內置手冊。
root@Kylin:/$ git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
進入 linux 目錄修改配置文件之后,執行編譯 make Image
命令生成 Image 文件,以便虛擬機能夠加載該內核文件。
Kdump 崩潰轉存演示
安裝 kdump-tools 相關的軟件包
由於剛剛下載的 Ubuntu Base 系統中只擁有很少的軟件包,所以需要利用 chroot 進入到這個 base 文件系統安裝一些 kdump 相關的軟件包。
root@Kylin:/$ mount rootfs.img rootfs && chroot rootfs
$ apt install kdump-tools crash kexec-tools makedumpfile systemd -y
$ passwd root << EOF
123123
123123
EOF
$ exit root@Kylin:/$ umount rootfs
由於默認的 Base 系統沒有攜帶大型的啟動管理器,所以為了能夠對服務進行管理,我們還需要安裝 systemd 啟動管理軟件。等待安裝完畢,並卸載文件系統即可,文件系統鏡像中已經保留了當前安裝的軟件,另外還需要將 Linux 內核的 Image 和 vmlinux 二進制程序也拷貝到 rootfs 中,這兩個文件是為了能夠讓 kdump 的服務正確運行,密碼也是需要設置的,不然無法正確登錄進入系統。
進入虛擬機中的 Ubuntu 系統
rootfs.img 已經成功安裝了 kdump-tools 相關的包,那么現在只需要利用 qemu-system-aarch64 虛擬機啟動內核並加載該文件系統鏡像即可)。
root@Kylin:/$ qemu-system-aarch64 -enable-kvm -machine virt -M virt,gic_version=3 -cpu cortex-a57 \ -machine type=virt -nographic -smp 2 -m 4096 -kernel Image -hda ./rootfs.img \ -append "console=ttyAMA0 root=/dev/vda rw crashkernel=256M nr_cpus=2"
此時已經進入了 qemu 虛擬機下的 ubuntu 系統。現在,我們就需要在這個系統下演示如何轉存內核崩潰信息。
調試內核轉存文件
要啟動 kdump-tools 服務,首先需要在內核的啟動參數中添加 crashkernel=X[@Y]
3 這樣的參數,表明需要預留一部分內存用以保存 dump 內核的代碼,以便在第一個內核崩潰的情況下,通過一系列故障處理之后,迅速切換到第二個內核,也就是所謂的 dump 內核,通過該內核收集第一個內核所產生的崩潰現場信息,並保存到 /var/crash 目錄下,方便重啟之后可以查看到該奔潰信息,提供給內核開發者調試問題的方向。
確保了啟動參數添加了 crashkernel 之后,還需要保證服務正確啟動:
root@localhost:~$ /etc/init.d/kdump-tools restart
Restarting kdump-tools (via systemctl): kdump-tools.service[ 21.282910] kdump-tools[2539]: Stopping kdump-tools: * unloaded kdump kernel
[ 21.398844] kdump-tools[2564]: Starting kdump-tools: * Creating symlink /var/lib/kdump/vmlinuz
[ 21.401271] kdump-tools[2564]: * Creating symlink /var/lib/kdump/initrd.img
[ 21.915798] kdump-tools[2564]: * loaded kdump kernel
root@localhost:~$ kdump-config show
DUMP_MODE: kdump
USE_KDUMP: 1
KDUMP_SYSCTL: kernel.panic_on_oops=1
KDUMP_COREDIR: /var/crash
crashkernel addr: 0xefe00000
/var/lib/kdump/vmlinuz: symbolic link to /boot/vmlinuz-5.0.0-rc3-00473-g957491e4ebfe
kdump initrd:
/var/lib/kdump/initrd.img: symbolic link to /var/lib/kdump/initrd.img-5.0.0-rc3-00473-g957491e4ebfe
current state: ready to kdump
kexec command: /sbin/kexec -p --command-line="console=ttyAMA0 root=/dev/vda rw nr_cpus=2 nr_cpus=1 systemd.unit=kdump-tools.service" --initrd=/var/lib/kdump/initrd.img /var/lib/kdump/vmlinuz
可以看到當前的服務狀態已經成功,接下來只需要將內核弄得崩潰即可:
root@localhost:~$ echo c > /proc/sysrq-trigger root@localhost:/boot# echo c > /proc/sysrq-trigger [ 52.026012] sysrq: SysRq : Trigger a crash [ 52.027357] Kernel panic - not syncing: sysrq triggered crash [ 52.028997] CPU: 0 PID: 2481 Comm: bash Kdump: loaded Not tainted 5.0.0-rc3-00473-g957491e4ebfe-dirty #49 [ 52.031751] Hardware name: linux,dummy-virt (DT) [ 52.033126] Call trace: [ 52.034059] dump_backtrace+0x0/0x140 [ 52.035726] show_stack+0x14/0x20 [ 52.037385] dump_stack+0x90/0xb4 [ 52.038770] panic+0x134/0x2c0 [ 52.040175] sysrq_handle_reboot+0x0/0x18 [ 52.041720] __handle_sysrq+0x84/0x170 [ 52.043524] write_sysrq_trigger+0x64/0x80 [ 52.045179] proc_reg_write+0x64/0xa0 [ 52.046902] __vfs_write+0x30/0x170 [ 52.048243] vfs_write+0xa4/0x1b0 [ 52.049767] ksys_write+0x5c/0xc0 [ 52.051018] __arm64_sys_write+0x18/0x20 [ 52.052614] el0_svc_common+0x84/0xf0 [ 52.054060] el0_svc_handler+0x2c/0x80 [ 52.055556] el0_svc+0x8/0xc [ 52.056715] SMP: stopping secondary CPUs [ 52.059009] Starting crashdump kernel... [ 52.060559] Bye!
然后就會自動啟動第二個內核,並且啟動 kdump-tools 服務的 savecore 功能保存崩潰現場信息。
[ OK ] Reached target System Initialization.
Starting Kernel crash dump capture service...
[ 2.431304] kdump-tools[1564]: Starting kdump-tools: * running makedumpfile -c -d 31 /proc/vmcore /var/crash/201903050735/dump-incomplete
[ 2.451777] kdump-tools[1564]: get_mem_section: Could not validate mem_section.
[ 2.454372] kdump-tools[1564]: get_mm_sparsemem: Can't get the address of mem_section.
[ 2.458981] kdump-tools[1564]: The kernel version is not supported.
[ 2.464283] kdump-tools[1564]: The makedumpfile operation may be incomplete.
[ 2.470796] kdump-tools[1564]: makedumpfile Failed.
[ 2.476827] kdump-tools[1564]: * kdump-tools: makedumpfile failed, falling back to 'cp'
[ 12.020359] kdump-tools[1564]: * kdump-tools: saved vmcore in /var/crash/201903050735
[ 15.444973] kdump-tools[1564]: * running makedumpfile --dump-dmesg /proc/vmcore /var/crash/201903050735/dmesg.201903050735
[ 15.460068] kdump-tools[1564]: get_mem_section: Could not validate mem_section.
[ 15.463537] kdump-tools[1564]: get_mm_sparsemem: Can't get the address of mem_section.
[ 15.471594] kdump-tools[1564]: The kernel version is not supported.
[ 15.478798] kdump-tools[1564]: The makedumpfile operation may be incomplete.
[ 15.484190] kdump-tools[1564]: makedumpfile Failed.
[ 15.489301] kdump-tools[1564]: * kdump-tools: makedumpfile --dump-dmesg failed. dmesg content will be unavailable
[ 15.496868] kdump-tools[1564]: * kdump-tools: failed to save dmesg content in /var/crash/201903050735
[ 15.507109] kdump-tools[1564]: Tue, 05 Mar 2019 07:35:58 +0000
[ 15.807040] reboot: Restarting system
等待其完成保存完畢之后,會自動重新啟動系統,此時 /var/crash/ 目錄就保存了一個 vmcore 的調試文件(此處也可能是 dump.xxx 文件),然后通過 crash
工具對其進行調試即可。
root@localhost:~$ crash /vmlinux /var/crash/201903050735/vmcore.201903050735
Copyright (C) 1999, 2002, 2007 Silicon Graphics, Inc.
Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
This program is free software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it under
certain conditions. Enter "help copying" to see the conditions. This program has absolutely no warranty. Enter "help warranty" for details. KERNEL: /vmlinux DUMPFILE: /var/crash/201903050735/vmcore.201903050735 CPUS: 2 DATE: Tue Mar 5 07:35:35 2019 UPTIME: 00:00:51 LOAD AVERAGE: 0.00, 0.00, 0.00 TASKS: 67 NODENAME: localhost.localdomain RELEASE: 5.0.0-rc3-00473-g957491e4ebfe-dirty VERSION: #49 SMP PREEMPT Tue Mar 5 06:34:21 UTC 2019 MACHINE: aarch64 (unknown Mhz) MEMORY: 4 GB PANIC: "sysrq: SysRq : Trigger a crash" PID: 2481 COMMAND: "bash" TASK: ffff8000f7f3ec00 [THREAD_INFO: ffff8000f7f3ec00] CPU: 0 STATE: TASK_RUNNING (SYSRQ) crash>
具體的命令就在 crash 的命令行提示符上輸入 help 獲得幫助即可,命令都很簡單,稍微使用一下就可以上手。
其他
遇到的問題
不清楚是宿主機的原因還是代碼原因,目前主線的 Linux kernel 代碼在執行命令使第一個內核崩潰之后,跳轉到第二個內核的過程中卡死,在社區上也有其他人遇到了類似的情況並給出了補丁,但是並沒有合並到主線,不過目前為了演示暫時不考慮為何原因導致這個問題的出現,如果你的 arm64 宿主機不存在這個問題,那么就不需要打這個補丁了。奉上補丁如下:
diff --git a/arch/arm64/kernel/machine_kexec.c b/arch/arm64/kernel/machine_kexec.c
index aa9c94113700..3b0350d20e31 100644
--- a/arch/arm64/kernel/machine_kexec.c +++ b/arch/arm64/kernel/machine_kexec.c @@ -234,19 +234,12 @@ static void machine_kexec_mask_interrupts(void) for_each_irq_desc(i, desc) { struct irq_chip *chip; - int ret; chip = irq_desc_get_chip(desc); if (!chip) continue; - /* - * First try to remove the active state. If this - * fails, try to EOI the interrupt. - */ - ret = irq_set_irqchip_state(i, IRQCHIP_STATE_ACTIVE, false); - - if (ret && irqd_irq_inprogress(&desc->irq_data) && + if (irqd_irq_inprogress(&desc->irq_data) && chip->irq_eoi) chip->irq_eoi(&desc->irq_data);
還有一點需要說明的就是在 Ubuntu 默認倉庫的 crash 不支持最新版本的 Linux 內核,需要更新到 7.2.5 版本才可以。
演示視頻
這里奉上我自己調試演示的視頻信息,視頻可能比較復雜,請結合文檔提示進行理解消化。
寫在最后
本來准備自己寫一篇關於 kdump/kexec 的原理分析文檔,而且都已經寫了一半了,最終還是決定刪掉了,因為很多現在已經有了很多深入分析的文檔寫得非常清晰明了,《使用 kdump 檢查 Linux 內核崩潰》寫得很棒,既然前人已經種好了樹,我這個后人就直接引用了,沒有必要重復造輪子,而且造得還不如別人好。