組込み

【Linux】6. 1.カーネルクラッシュダンプを取る

1.記事一覧

記事は複数回に分けて投稿する予定です。AMD/XilinxのARM SoC(ZynqMP Kria K26 SOM)で動作する、AMD/Xilinx公式認定Ubuntuやベアメタル開発を勉強していく覚書きです。

Zynq7000(armv7)版はこちら

2.crashでカーネルクラッシュダンプを調査【前編】

2-1. カーネルクラッシュダンプとは

Linuxを扱う上でかなり困る不具合の一つが、「たまにOSがフリーズする(またはpanicする)が、原因の検討がつかない」という状況です。聞いただけでもあまり関わりたくないと思うくらい面倒そうな状況です。

よく知られる方法で、アプリケーションプロセスが何らかの致命的なエラー(Busfault、SEGV)などを起こして強制終了する際、コアダンプ(coredump)というCPUレジスタや、プロセス、メモリ情報を記録したファイルを保存したファイルを生成できます。それをあとでGDBに読み込ませると、エラーを起こした瞬間のスタックトレースを得られます。それをソースコードと突き合わせて原因の箇所を特定できます。

コアダンプはプロセス単位のダンプ情報ですが、Linuxカーネルが管理するメモリ範囲をまるごとダンプできる仕組みがあります。それがカーネルクラッシュダンプです。

Linuxカーネルが動作を継続するのが極めて困難な場合に陥った状態をカーネルパニック(panic)と呼びます。障害情報を収集するクラッシュカーネルを予約メモリ領域にあらかじめ展開しておき、panic時にメインカーネルからクラッシュカーネルに実行が遷移して、メインカーネルのダンプ情報を生成して、ファイルシステムに保存します。

カーネルクラッシュダンプファイルは前回使用したcrashツールで解析できます。ただし、クラッシュした原因のヒントがある可能性があるだけで原因が判明するとは限らないそうです。カーネルクラッシュはできれば遭遇したくないですが、もしものときのためにやり方を練習してみようと思います。

2-2. カーネルクラッシュダンプ解析チュートリアル【前編】

チュートリアルとして、意図的にクラッシュを起こしてpanicさせ、カーネルクラッシュダンプを取得するとこまでを前半としたいと思います。次回の記事でカーネルクラッシュダンプをcrashツールに読み込ませていろいろ見てみることにします。

途中の過程でカーネルをクラッシュさせるので、やる方はバックアップ取りましょう。

目次:前編

  • Linux-crashdumpパッケージの準備
  • bootargs(KERNEL_CMDLINE)にクラッシュカーネル用のメモリ予約領域を設定する
  • Sysrqで意図的にカーネルパニックを発生させクラッシュさせる
  • カーネルクラッシュダンプファイルが生成されたことを確認

目次:次回後編

  • crashでカーネルクラッシュダンプファイルを読み込む
  • crash : bt(backtrace)でpanic発生箇所をさぐる
  • crash : disでクラッシュ箇所の逆アセンブル
  • 逆アセンブルだけでなくソースコードを表示したいとき
  • crash : dis -sでクラッシュ箇所のソースコード表示

この記事の内容はgihyo.jpの柴田充也氏の記事とその他の参考資料を元にやっていきます。

gihyoの記事はx86環境のため、環境準備はUbuntuの公式Docを見て情報を補います。Qiitaの記事も良記事ですが、OSがRHEL x86環境のため、こちらも適宜情報補完用に参考にしました。

開発ツールVSCode(Remote-SSH)
ターゲットOSXilinx認定Ubuntu22.04
Linux Kernel : 5.15.0-1031-xilinx-zynqmp
ターゲットボード(インターネット接続必要)Xilinx社製 Kria KR260 ロボティクススターターキット(SK-KR260-G)
SOM : Xilinx社 Kria K26 SOM (Zynq UltraScale+ MPSoCベース)
   APU : Arm CA53x4 1333MHz arm64
   RPU : Arm CR5x2 533MHz armv7-R
SDRAM : DDR4 4GB

3.環境準備

3-1. linux-crashdumpのセットアップ

Ubuntuでパッケージが用意されているので、インストールでほぼセットアップ出来ます。

Bash
$ sudo apt install linux-crashdump

途中設定を聞かれます。

「このオプションを選択すると、システムを再起動すると、システムブートローダのプロセス全体を経ることなく、kexecによってロードされたカーネルで再起動されます。kexec-toolsは再起動を処理すべきでしょうか?(機械翻訳)」

カーネルクラッシュダンプ取得後に自動再起動するのですが、kexecと呼ばれるクラッシュカーネルを起動する仕組みがそのまま、メインカーネルをロードして起動するか(Yes)、FSBLから起動し直すか(No)の選択になります。

ハードウェアを初期化してほしい場合が多いケースだと思うのでNoを選択します。

この設定は後から変更可能です。
“/etc/default/kexec”の以下を変更します。
LOAD_KEXEC

Bash
$ cat /etc/default/kexec
(...)
# Load a kexec kernel (true/false)
LOAD_KEXEC=false

(...)

続いての設定です。

「このオプションを選択すると、kdump-tools メカニズムが有効になります。クラッシュカーネルカーネルパラメータを有効にするには、再起動が必要です。kdump-tools をデフォルトで有効にしますか?」

クラッシュダンプを取得するために有効(Yes)にします。

この設定は後から変更可能です。
“/etc/default/kdump-tools”
USE_KDUMP

Bash
$ cat /etc/default/kdump-tools
# kdump-tools configuration
# ---------------------------------------------------------------------------
# USE_KDUMP - controls kdump will be configured
#     0 - kdump kernel will not be loaded
#     1 - kdump kernel will be loaded and kdump is configured
#
USE_KDUMP=1

(...)

3-2. bootargsでクラッシュカーネル用の予約メモリを設定

bootargsを更新します。bootargsの変更方法は以前の記事「【ZynqMP】3.認定UbuntuのDeviceTree変更」でやっています。変更したら必ず再起動が必要です。

今回は以下のように書き換えます。

Bash
$ cat /etc/default/flash-kernel
LINUX_KERNEL_CMDLINE="crashkernel=2G-:192M"
LINUX_KERNEL_CMDLINE_DEFAULTS=""

$ sudo flash-kernel 

“crashkernel=2G-:192M”の意味はメインメモリが2GB以上ならば、192MBのクラッシュカーネルのメモリ領域を確保、です。2GB未満なら確保しません。

crashkernel=<memory min size>-<memory max size(省略可)>:<crash kernel size>

上記のようにメインメモリの容量で条件を分けることが出来ます。

今回のKria k26 SOM DDR4=4GBの環境では、クラッシュカーネル用に192MB程度の領域が必要でした。ちなみに168MB以下ではダンプ失敗の確認をしています。この領域はLinux管理外の領域になるため、単純に有効メモリ容量が減ります(3.6GB程度に減る)。4GBに対して200MBは少なくない容量のため、実際に使うかは状況によると思います。

設定できたら再起動を行います。

dmesgで確保できているか確認します。出来ているようです。

Bash
$ sudo dmesg |grep crash
[    0.000000] crashkernel reserved: 0x000000002b400000 - 0x0000000037400000 (192 MB)
[    0.000000] Kernel command line:  root=LABEL=writable rootwait earlycon console=ttyPS1,115200 console=tty1 clk_ignore_unused uio_pdrv_genirq.of_id=generic-uio xilinx_tsn_ep.st_pcp=4 cma=1000M crashkernel=2G-:192M

カーネルクラッシュダンプが有効になっているか確認します。

Bash
$ kdump-config show
DUMP_MODE:              kdump
USE_KDUMP:              1
KDUMP_COREDIR:          /var/crash
crashkernel addr: 0x
   /var/lib/kdump/vmlinuz: symbolic link to /boot/vmlinuz-5.15.0-1031-xilinx-zynqmp
kdump initrd: 
   /var/lib/kdump/initrd.img: symbolic link to /var/lib/kdump/initrd.img-5.15.0-1031-xilinx-zynqmp
current state:    ready to kdump

kexec command:
  /sbin/kexec -p --command-line=" root=LABEL=writable rootwait earlycon console=ttyPS1,115200 console=tty1 clk_ignore_unused uio_pdrv_genirq.of_id=generic-uio xilinx_tsn_ep.st_pcp=4 cma=1000M reset_devices systemd.unit=kdump-tools-dump.service nr_cpus=1" --initrd=/var/lib/kdump/initrd.img /var/lib/kdump/vmlinuz
  

“current state: ready to kdump”であれば有効になっています。また、”KDUMP_COREDIR:”のpathがカーネルクラッシュダンプファイルの保存先になっています。デフォルトは”/var/crash”です。

4.カーネルクラッシュダンプを取得する

4-1. SysRQによる強制クラッシュの実行

SysRQ(Magic System Request Key)とはLinuxが何らかの障害でShellの入力が出来ない状況でカーネルに割込みを発生させて特定の動作をさせる機能らしいです。

magic SysRq keyについて:MIRACLE Linux

SysRQにカーネルパニックを強制実行する機能があります。今回はそれを実行してダンプを取得できるか確認します。

当然ですが、念の為バックアップをとっておきましょう。

まず、SysRQを有効にします。

Bash
#SysRQを有効にする
$ sudo sysctl -w kernel.sysrq=1
kernel.sysrq = 1

#有効になったか確認
$ cat /proc/sys/kernel/sysrq
1

続いて、クラッシュを起こすのですが、その前にカーネルメッセージが見えたほうがうまく行かないときの原因究明に役立つので、シリアルコンソールに切り替えます。KR260ボードの場合、USB-JTAGと共用されているUARTはデフォルトでシリアルコンソールに設定されているので、そちらでログインします。

私は母艦PCはUbuntuのため、TeratermではなくCuteComを使用しています。ちょっと癖がありますが、通信ログを設定で残せるのでこれを使っています。

クラッシュを実行します。

Bash
$ sudo -s
root# echo c > /proc/sysrq-trigger

クラッシュを実行すると即Kernel panicします。その後クラッシュカーネルが起動し、ダンプを取得した後、再起動でFSBLから起動し直されます。

Bash
#ログ
root@kria:/home/ubuntu# echo c > /proc/sysrq-trigger
[ 2426.813967] sysrq: Trigger a crash
[ 2426.820550] Kernel panic - not syncing: sysrq triggered crash <カーネルパニック!
[ 2426.826288] CPU: 3 PID: 9779 Comm: bash Kdump: loaded Tainted: G         C        5.15.0-1031-xilinx-zynqmp #35-Ubuntu
[ 2426.836970] Hardware name: ZynqMP KR260 revB (DT)
[ 2426.841659] Call trace:
[ 2426.844088]  dump_backtrace+0x0/0x200
[ 2426.847743]  show_stack+0x20/0x30
[ 2426.851049]  dump_stack_lvl+0x68/0x84
[ 2426.854703]  dump_stack+0x18/0x34
[ 2426.858011]  panic+0x16c/0x35c
[ 2426.861057]  sysrq_reset_seq_param_set+0x0/0x9c
[ 2426.865580]  __handle_sysrq+0x90/0x1a0
[ 2426.869321]  write_sysrq_trigger+0xbc/0x1a0
[ 2426.873496]  proc_reg_write+0xb0/0x10c
[ 2426.877237]  vfs_write+0xf8/0x2cc
[ 2426.880544]  ksys_write+0x70/0x100
[ 2426.883938]  __arm64_sys_write+0x24/0x30
[ 2426.887853]  invoke_syscall+0x78/0x100
[ 2426.891594]  el0_svc_common.constprop.0+0x54/0x184
[ 2426.896377]  do_el0_svc+0x30/0xac
[ 2426.899684]  el0_svc+0x28/0xb0
[ 2426.902730]  el0t_64_sync_handler+0xa4/0x12c
[ 2426.906992]  el0t_64_sync+0x1a4/0x1a8
[ 2426.910654] SMP: stopping secondary CPUs
[ 2426.914669] Starting crashdump kernel...
[ 2426.918582] Bye! <ーここからクラッシュカーネルが起動する
[    0.000000] Booting Linux on physical CPU 0x0000000003 [0x410fd034] 
[    0.000000] Linux version 5.15.0-1031-xilinx-zynqmp (buildd@bos03-arm64-030) (gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #35-Ubuntu SMP Fri Jun 14 02:59:36 UTC 2024 (Ubuntu 5.15.0-1031.35-xilinx-zynqmp 5.15.152)
[    0.000000] Machine model: ZynqMP KR260 revB
[    0.000000] efi: UEFI not found.

(...)

[   19.624926] systemd[1]: Started Journal Service.
[   19.820471] systemd-journald[471]: Received client request to flush runtime journal.
[   67.164737] reboot: Restarting system <ーダンプ取得完了。再起動
Zynq MP First Stage Boot Loader 

Release 2023.2   Mar 18 2024  -  06:43:04
MultiBootOffset: 0x40
Reset Mode	:	System Reset
Platform: Silicon (4.0), Running on A53-0 (64-bit) Processor, Device Name: XCZUUNKNEG

QSPI 32 bit Boot Mode 

FlashID=0x20 0xBB 0x20

PMU Firmware 2023.2	Mar 18 2024   06:43:04

(...)

Ubuntu 22.04.4 LTS kria ttyPS1

kria login: 

うまく行けば再起動まで完了します。途中でメッセージが止まるようであれば、クラッシュカーネル用の予約メモリ容量が少なすぎる可能性があります。

4-2. クラッシュダンプファイル生成の確認

ダンプファイルは”/var/crash/”にあります。

Bash
$ ls /var/crash/
202408290941  kdump_lock  kexec_cmd  linux-image-5.15.0-1031-xilinx-zynqmp-202408290941.crash

$ ls /var/crash/202408290941/
dmesg.202408290941  dump.202408290941

日時が名前のフォルダが作成され、”dump.YYYYMMDDHHMM”のファイルがカーネルクラッシュダンプファイルになります。

ちなみに、”dmesg.YYYYMMDDHHMM”はその名の通りdmesgのログです。

5.最後に

今回はカーネルクラッシュダンプ解析のチュートリアル前編をやってみました。Ubuntu公式のドキュメントがしっかりしているのと、Xilinx認定Ubuntuだけあってパッケージも用意されていることからあまり困ることはありませんでした。

強いて言えば、bootargsのクラッシュカーネルのメモリ容量がドキュメントだと128MBを指定していたので、その値をそのまま使ったのですが、ダンプが生成されず困りました。海外のサイトで、メモリ容量は最初は十分大きいサイズを指定してから、徐々に減らしていって動作するか確認すると良いと記述があり、とりあえず2倍の256MBにしたらダンプが生成されたというエビソードがありました。

シリアルコンソールのbootメッセージも途中で止まるのが見えるので、可視化の手間を惜しまないのも大切だと思います。メモリ量によって、bootメッセージの止まる位置が変わるので、メモリ枯渇の瞬間が確実にわかります。

次回は後編です。クラッシュダンプをcrashツールで覗いてみます。

ABOUT ME
sh-goto
低レイヤで遊んでいます
関連記事