Kdump 是 Linux 核心的一個特殊功能,可在發生 kernel panic 時建立 Core Dump。當出現異常導致 Kdump 被觸發時,kernel 會匯出一個記憶體的 image,其在 Linux 上通稱為 vmcore。該 image 可用於除錯和確定崩潰的原因。這個 image 會以 ELF 格式匯出,可以在處理核心崩潰時通過 /proc/vmcore
直接存取,或者可以也可將其自動儲存到本地或遠端檔案系統。
Kdump 大致的運作方式是: 在 kernel panic 的情況下,Kdump 會引導另一個 Linux kernel(稱為 dump-capture kernel),用它來匯出和儲存記憶體狀態。這麼做的目的是讓系統啟動到一個乾淨、可靠的環境,而不是依賴已經崩潰的 kernel,因為後者可能會引發其他問題反而破壞了原本的異常環境。例如,在寫入記憶體轉儲檔案時導致檔案系統損壞。
為了實現這個「雙核心」的環境,Kdump 在核心崩潰後會立即使用 kexec
booting 到 dump-capture kernel,使用 kexec 引導「覆蓋」當前執行的核心,這做法同時避免執行 bootloader 和系統韌體(BIOS/UEFI)的硬體初始化行為。
整體的運作即如下附圖所展示:
為了讓 dump-capture kernel 在系統崩潰並執行不破壞原本的異常環境,系統需要保留少量 RAM 以利其 booting 與運作。不過像是 x86/ppc64 架構可能必需要 RAM 的一個固定位置來 booting。在這種情況下,為避免覆蓋原始 kernel,kexec 會建立該部分 RAM 的副本,以便之後 dump-capture kernel 可以存取到相關訊息。RAM 需保留部分的大小和可選位置通過 boot paramters crashkernel
指定。則在主核心啟動之後,kexec 會將 dump-capture kernel 及其 initrd image 預載入到那部分保留的 RAM。
接下來,本文將要示範如何使用 Kdump 與相關工具。由於目的旨在介紹使用的方式,並可以輕易的直接在自己的電腦中嘗試,會以虛擬的環境(QEMU)做示範。在實體機器環境上的使用流程其實也大致相同。
實際上,現有材料中已經有許多基於 QEMU 使用 Kdump 的案例,例如:
為了簡化環境的設置,這裡使用更易上手的 virtme-ng 進行示範,詳細的安裝方式可見本文。若對編譯及啟動 Linux 等相關流程陌生的讀者也可先閱讀 Linux 核心設計: 開發、測試與除錯環境 全文。
使用 virtme-ng 和直接使用 QEMU 最大的區別實際上只在檔案系統的存取。前者可以很容易存取到 host 端的檔案,後者則需相對複雜的設定。由於這裡目的只是展示 Kdump 的功能,因此選擇更容易嘗試的前者。
此外,下面的展示僅以 x86_64 環境為例,性質上偏向懶人包。若想知道每個步驟背後細節,甚至是其他支援平台的設定,可直接參照 Linux 文件 Documentation for Kdump - The kexec-based Crash Dumping Solution,後者將提供更清楚的資訊。
首先,我們取得任意的 Linux 核心版本
make defconfig
為簡化用詞,我們將一開始啟動的主核心成為 system kernel,崩潰後會啟動的核心則是之前介紹的 dump-capture kernel。則針對 system kernel,必須確認 .config
中以下的選項是開啟的:
CONFIG_KEXEC=y
CONFIG_KEXEC_CORE=y
CONFIG_SYSFS=y
CONFIG_CRASH_CORE=y
此外還要將除錯資訊編譯到 kernel image 中,否則啟用 kdump 的意義也不大。開啟下面的選項:
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF4=y
CONFIG_DEBUG_KERNEL=y
針對 dump-capture kernel,則需要以下幾個選項:
CONFIG_CRASH_DUMP=y
CONFIG_PROC_VMCORE=y
一般來說,dump-capture kernel 僅做為 core dump 和除錯目的,因此會和 system kernel 分開編譯。不過為求簡化,在 x86_64 我們也可以讓同一個 kernel image 擔任兩者。此時額外以下兩個選項,本文也以此種方式來實驗。
CONFIG_RELOCATABLE=y
CONFIG_PHYSICAL_START=0x100000
完成 config 之後即可編譯核心。
make -j$(nproc)
透過以下步驟可取得 kexec。
wget http://kernel.org/pub/linux/utils/kernel/kexec/kexec-tools.tar.gz
tar xvpzf kexec-tools.tar.gz
cd kexec-tools-VERSION
./configure
make
make install
特別留意由於本文是以 virtme-ng 為環境,因此直接將工具下載到 host 即可。若是用 QEMU 或是實體機器則需將 kexec 放到其可以存取到的檔案系統(例如 rootfs)中。
使用 virtme-ng 的情況下,一般來說不需要另外為 system kernel 建立 initrd。不過由於 dump-capture kernel 也需要另外的 initrd,這裡我們仍需建立一個。
詳細方式請參照 Linux 核心設計: 開發與測試環境/Busybox。
作者也嘗試改為使用 Ubuntu rootfs(ubuntu-base),好處是提供的工具相對於 busybox 會豐富一點。不過似乎容易因檔案過大而無法和 dump-capture kernel 一同載入。
wget http://cdimage.ubuntu.com/ubuntu-base/releases/22.04/release/ubuntu-base-22.04.1-base-amd64.tar.gz
mkdir rootfs; cd rootfs
tar xvpzf ../ubuntu-base-22.04.1-base-amd64.tar.gz
如果對此有任何想法歡迎提出~
完成上述環境的準備後,我們可以用 virtme-ng 來啟動 system kernel。
vng -v --append 'crashkernel=256M'
這裡提供留意到這裏必須提供 crashkernel
以提示 system kernel 需保留給 dump-capture kernel 的記憶體量。
接著我們要使用之前下載的 kexec 來將 dump-capture kernel 和 initrd 預先載入。參考以下命令,但留意到 kernel 和 initrd 的路徑需改為使用者電腦上的正確路徑。此外,選用 kernel 的檔案格式也會影響參數的選用,詳見 Load the Dump-capture Kernel。
sudo /usr/local/sbin/kexec -p arch/x86_64/boot/bzImage \
--initrd=../busybox/root.img \
--append="root=/dev/sda console=ttyS0 1 irqpoll nr_cpus=1 reset_devices earlyprintk=serial"
載入之後,我們就可以透過下面的命令來製造 panic 了!
sudo sh -c "echo c > /proc/sysrq-trigger"
如果一切正常,應該可以看到 panic 導致 kernel 顯示 stack unwinding 的錯誤資訊,之後重新啟動。則啟動完成之後,我們就可以透過以下方式從模擬環境中取得 core dump 的資料了!
(輸入 ctrl+a c 進入 QEMU debug 模式)
dump-guest-memory -z guest.img
實體機器上一般是透過 cp /proc/vmcore <dump-file>
來取得 core dump,不過 QEMU 上似乎受限於模擬的磁碟空間大小不容易仿照這個做法?
如果完整的 vmcore 檔案太大,複製其內容到儲存裝置上將需要一段時間。若只想擷取其中有興趣的內容,則可以改為嘗試 makedumpfile
工具。
$ git clone git@github.com:makedumpfile/makedumpfile.git
$ make
$ make install
$ makedumpfile -R guestdump.img < guest.img
重新開機至正常運作的 system kernel 下,我們就可以使用前面取得的 core dump 來分析錯誤發生的原因。推薦的方式是使用 crash 這個設計於分析 dump 的工具。藉由以下方式取得並安裝:
git clone git@github.com:crash-utility/crash.git
cd crash
make -j$(nproc)
sudo make install
安裝完成後,則可以輸入以下命令。之後應該就可以看到類似的訊息。
$ crash guest.img vmlinux
...
KERNEL: vmlinux
DUMPFILE: guest.img [PARTIAL DUMP]
CPUS: 20
DATE: Thu Jan 1 08:00:00 CST 1970
UPTIME: 00:00:32
LOAD AVERAGE: 0.63, 0.17, 0.06
TASKS: 214
NODENAME: virtme-ng
RELEASE: 6.13.0-rc2-00036-g231825b2e1ff
VERSION: #11 SMP PREEMPT_DYNAMIC Sat Dec 28 22:21:23 CST 2024
MACHINE: x86_64 (2916 Mhz)
MEMORY: 1 GB
PANIC: "Kernel panic - not syncing: sysrq triggered crash"
PID: 507
COMMAND: "sh"
TASK: ffff919186519d80 [THREAD_INFO: ffff919186519d80]
CPU: 8
STATE: TASK_RUNNING (PANIC)
在 PANIC
的地方我們就可以看到 crash 為我們說明了造成 kernel 崩潰的原因! 更多 crash 可做到的分析請參考 crash 底下 README 的說明。