1.記事一覧
記事は複数回に分けて投稿する予定です。AMD/XilinxのARM SoC(ZynqMP Kria K26 SOM)で動作する、AMD/Xilinx公式認定Ubuntuやベアメタル開発を勉強していく覚書きです。
ZynqMP(APU:Cortex-A53,Arm64) 組込みLinux入門編
- 【ZynqMP】1. 認定UbuntuでPLのGPIOを使う
- 【ZynqMP】2. 認定Ubuntuのカーネルビルド
- 【ZynqMP】3. 認定UbuntuのDeviceTree変更
- 【ZynqMP】4. 認定UbuntuでOpenAMPを試す【CR5Lockstep】
- 【ZynqMP】5. UARTでAMP【コア間通信】
- 【Linux】6. crash toolでカーネルメモリを見る
- 【Linux】6. 1.カーネルクラッシュダンプを取る
- 【Linux】6. 2.カーネルクラッシュダンプを見る←本記事
ZynqMP(RPU:Cortex-R5,Arm32) RPU入門編
2.crashでカーネルクラッシュダンプを調査【後編】
2-1. カーネルクラッシュダンプ解析チュートリアル【後編】
前回カーネルクラッシュダンプを取得しました。後編はcrashツールに読み込ませてクラッシュ発生時のメモリの内容を見てみようと思います。母艦PCなどのパワーのあるマシンでやったほうが効率が良いのですが、クロスアーキテクチャ環境を作るのが面倒なので、ZynqMPの上で作業します。
目次:前回前編
- Linux-crashdumpパッケージの準備
- bootargs(KERNEL_CMDLINE)にクラッシュカーネル用のメモリ予約領域を設定する
- Sysrqで意図的にカーネルパニックを発生させクラッシュさせる
- カーネルクラッシュダンプファイルが生成されたことを確認
目次:後編
- crashでカーネルクラッシュダンプファイルを読み込む
- crash : bt(backtrace)でpanic発生箇所をさぐる
- crash : disでクラッシュ箇所の逆アセンブル
- 逆アセンブルだけでなくソースコードを表示したいとき
- crash : dis -sでクラッシュ箇所のソースコード表示
この記事の内容はgihyo.jpの柴田充也氏の記事とその他の参考資料を元にやっていきます。
- 第673回カーネルのクラッシュ情報を取得する:gihyo.jp 柴田充也氏
- 第674回カーネルのクラッシュ情報を解析する:gihyo.jp 柴田充也氏
- Ubuntu Server documentation Kernel crash dump
- カーネルクラッシュダンプ (Kdump) の概要と設定方法:Qiita:@Kernel_OGSun氏
- カーネルクラッシュダンプの解析方法:Qiita:@Kernel_OGSun氏
gihyoの記事はx86環境のため、環境準備はUbuntuの公式Docを見て情報を補います。Qiitaの記事も良記事ですが、OSがRHEL x86環境のため、こちらも適宜情報補完用に参考にしました。
開発ツール | VSCode(Remote-SSH) |
ターゲットOS | Xilinx認定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.環境準備
以前の記事「【Linux】6. crash toolでカーネルメモリを見る」と同様にDebug symbol packagesをインストールしていきます。最後のdbgsymは15分程度インストールに時間がかかります。
$ sudo apt install ubuntu-dbgsym-keyring
$ echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse
deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse
deb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | \
sudo tee -a /etc/apt/sources.list.d/ddebs.list
$ sudo apt update
$ sudo apt install linux-image-$(uname -r)-dbgsym
以下にカーネルデバッグシンボルファイルが出来ます。
ls /usr/lib/debug/boot/
vmlinux-5.15.0-1031-xilinx-zynqmp
4.クラッシュダンプを見てみる
4-1. bt(backtrace)でpanic発生箇所をさぐる
まずcrashでカーネルクラッシュダンプを読み込んでみます。
- 第1引数:カーネルデバッグシンボル
- 第2引数:カーネルクラッシュダンプ
$ crash /usr/lib/debug/boot/vmlinux-5.15.0-1031-xilinx-zynqmp /var/crash/202408290941/dump.202408290941
crash 8.0.0
Copyright (C) 2002-2021 Red Hat, Inc.
Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation
Copyright (C) 1999-2006 Hewlett-Packard Co
Copyright (C) 2005, 2006, 2011, 2012 Fujitsu Limited
Copyright (C) 2006, 2007 VA Linux Systems Japan K.K.
Copyright (C) 2005, 2011, 2020-2021 NEC Corporation
Copyright (C) 1999, 2002, 2007 Silicon Graphics, Inc.
Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
Copyright (C) 2015, 2021 VMware, 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.
GNU gdb (GDB) 10.2
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "aarch64-unknown-linux-gnu".
Type "show configuration" for configuration details.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
KERNEL: /usr/lib/debug/boot/vmlinux-5.15.0-1031-xilinx-zynqmp [TAINTED]
DUMPFILE: /var/crash/202408290941/dump.202408290941 [PARTIAL DUMP]
CPUS: 4
DATE: Thu Aug 29 09:40:21 JST 2024
UPTIME: 00:40:26
LOAD AVERAGE: 0.00, 0.01, 0.05
TASKS: 428
NODENAME: kria
RELEASE: 5.15.0-1031-xilinx-zynqmp
VERSION: #35-Ubuntu SMP Fri Jun 14 02:59:36 UTC 2024
MACHINE: aarch64 (unknown Mhz)
MEMORY: 4 GB
PANIC: "Kernel panic - not syncing: sysrq triggered crash"
PID: 9779
COMMAND: "bash"
TASK: ffff000800899040 [THREAD_INFO: ffff000800899040]
CPU: 3
STATE: TASK_RUNNING (PANIC)
crash>
“STATE: TASK_RUNNING (PANIC)”でKernel panicを起こしたことがわかります。
PANIC: “Kernel panic – not syncing: sysrq triggered crash”で既にクラッシュ原因がほぼ見えていますが、とりあえずバックトレースを見てみます。
crash> bt
PID: 9779 TASK: ffff000800899040 CPU: 3 COMMAND: "bash"
#0 [ffff80000ee438b0] machine_kexec at ffff800008038b10
#1 [ffff80000ee438f0] __crash_kexec at ffff80000818eec4
#2 [ffff80000ee43a80] panic at ffff800009438eec
#3 [ffff80000ee43b60] sysrq_handle_crash at ffff800008b2e8e0
#4 [ffff80000ee43b70] __handle_sysrq at ffff800008b2f57c
#5 [ffff80000ee43bc0] write_sysrq_trigger at ffff800008b2fc38
#6 [ffff80000ee43bf0] proc_reg_write at ffff800008476a50
#7 [ffff80000ee43c10] vfs_write at ffff8000083b4198
#8 [ffff80000ee43c50] ksys_write at ffff8000083b61fc
#9 [ffff80000ee43c90] __arm64_sys_write at ffff8000083b62b0
#10 [ffff80000ee43e10] invoke_syscall at ffff80000802a0b4
#11 [ffff80000ee43e40] el0_svc_common.constprop.0 at ffff80000802a190
#12 [ffff80000ee43e70] do_el0_svc at ffff80000802a2f0
#13 [ffff80000ee43e80] el0_svc at ffff8000094607d4
#14 [ffff80000ee43ea0] el0t_64_sync_handler at ffff800009461a04
#15 [ffff80000ee43fe0] el0t_64_sync at ffff800008011634
PC: 0000ffff87867e10 LR: 0000ffff8780507c SP: 0000ffffdd3b0700
X29: 0000ffffdd3b0700 X28: 0000000000000000 X27: 0000aaaaaca4b000
X26: 0000aaaaaca11468 X25: 0000aaaaaca55810 X24: 0000000000000002
X23: 0000aaaab1394e50 X22: 0000ffff879d27e0 X21: 0000ffff8792c5d8
X20: 0000aaaab1394e50 X19: 0000000000000001 X18: 0000000000000000
X17: 0000ffff87801d40 X16: 0000ffff87806460 X15: 0000000000000000
X14: 0000000000000410 X13: 0000000000000002 X12: 0000ffff878d7720
X11: 0000ffff878d7440 X10: 0000000000000000 X9: 0000aaaab1394190
X8: 0000000000000040 X7: 0000000000000010 X6: 000000000000021a
X5: 00000000fbad2a84 X4: 00000000ffffffff X3: 0000ffff879d2020
X2: 0000000000000002 X1: 0000aaaab1394e50 X0: 0000000000000001
ORI
btコマンドの引数を省略するとpanicを起こしたCPUコアが選択されるようです。
“#2 [ffff80000ee43a80] panic at ffff800009438eec”でpanicの例外?に飛んでいるようなので、直前のスタック周辺が該当しそうです。
- #3 [ffff80000ee43b60] sysrq_handle_crash at ffff800008b2e8e0
- #4 [ffff80000ee43b70] __handle_sysrq at ffff800008b2f57c
- #5 [ffff80000ee43bc0] write_sysrq_trigger at ffff800008b2fc38
sysrq_handle_crash関数の形跡がバッチリ残っているのでクラッシュの原因はこのあたりにあると推測できます。
ただし、現実にはここまできれいにわかりやすく出るとは限りません。複数のダンプでクラッシュ箇所がまばらに出るケースや、通信バッファサイズが少なく、IRQ割込みが頻発して過負荷クラッシュした場合はどんな結果になるんでしょうか? そもそもクラッシュカーネルが起動しなくてダンプが取れない気もしてきます。面白そうなので一度やって見る価値がありそうです。
クラッシュを起こしたコアとは別のコアの様子を見たい場合はまずpsでプロセス一覧を表示できます。上下矢印でスクロール、qで終了です。
crash> ps
PID PPID CPU TASK ST %MEM VSZ RSS COMM
> 0 0 0 ffff80000aa97e80 RU 0.0 0 0 [swapper/0]
> 0 0 1 ffff000800278000 RU 0.0 0 0 [swapper/1]
> 0 0 2 ffff000800279040 RU 0.0 0 0 [swapper/2]
0 0 3 ffff00080027d140 RU 0.0 0 0 [swapper/3]
1 0 0 ffff000800259040 IN 0.0 168044 11932 systemd
2 0 2 ffff00080025d140 IN 0.0 0 0 [kthreadd]
3 2 0 ffff00080025c100 ID 0.0 0 0 [rcu_gp]
(...)
> 9779 9778 3 ffff000800899040 RU 0.0 7516 3684 bash
(...)
crash>
“>”マークが付けられているのがコアで実行中のプロセスです。”[swapper/N]”はいわゆるアイドルタスクのようなカーネルスレッドらしいです。他のコアは全部アイドル状態だったようです。(何か負荷かけておくべきだった…)
試しにswapperカーネルスレッドのbtを見てみます。PID 0なので0を引数にいれます。
crash> bt 0
PID: 0 TASK: ffff80000aa97e80 CPU: 0 COMMAND: "swapper/0"
#0 [ffff800008003d50] crash_save_cpu at ffff80000818f404
#1 [ffff800008003f00] do_handle_IPI at ffff800008028914
#2 [ffff800008003f50] ipi_handler at ffff800008028ab0
#3 [ffff800008003f60] handle_percpu_devid_irq at ffff800008137a8c
#4 [ffff800008003f90] handle_domain_irq at ffff80000812fa74
#5 [ffff800008003fc0] gic_handle_irq at ffff80000801006c
--- <IRQ stack> ---
#6 [ffff80000aa83b70] call_on_irq_stack at ffff800008016d04
#7 [ffff80000aa83b80] do_interrupt_handler at ffff800008018ad8
#8 [ffff80000aa83b90] el1_interrupt at ffff8000094604dc
#9 [ffff80000aa83bb0] el1h_64_irq_handler at ffff8000094618b8
#10 [ffff80000aa83cf0] el1h_64_irq at ffff80000801138c
#11 [ffff80000aa83d10] cpuidle_enter_state at ffff800009022368
#12 [ffff80000aa83d60] cpuidle_enter at ffff8000090226bc
#13 [ffff80000aa83d90] cpuidle_idle_call at ffff8000080f2708
#14 [ffff80000aa83dd0] do_idle at ffff8000080f2868
#15 [ffff80000aa83e00] cpu_startup_entry at ffff8000080f2aac
#16 [ffff80000aa83e20] rest_init at ffff800009462860
#17 [ffff80000aa83e40] arch_call_rest_init at ffff80000a3d151c
#18 [ffff80000aa83e50] start_kernel at ffff80000a3d1a24
PID: 0 TASK: ffff000800278000 CPU: 1 COMMAND: "swapper/1"
#0 [ffff80000800bd50] crash_save_cpu at ffff80000818f404
#1 [ffff80000800bf00] do_handle_IPI at ffff800008028914
#2 [ffff80000800bf50] ipi_handler at ffff800008028ab0
#3 [ffff80000800bf60] handle_percpu_devid_irq at ffff800008137a8c
#4 [ffff80000800bf90] handle_domain_irq at ffff80000812fa74
#5 [ffff80000800bfc0] gic_handle_irq at ffff80000801006c
--- <IRQ stack> ---
#6 [ffff80000b00bbe0] call_on_irq_stack at ffff800008016d04
#7 [ffff80000b00bbf0] do_interrupt_handler at ffff800008018ad8
#8 [ffff80000b00bc00] el1_interrupt at ffff8000094604dc
#9 [ffff80000b00bc20] el1h_64_irq_handler at ffff8000094618b8
#10 [ffff80000b00bd60] el1h_64_irq at ffff80000801138c
#11 [ffff80000b00bd80] cpuidle_enter_state at ffff800009022368
#12 [ffff80000b00bdd0] cpuidle_enter at ffff8000090226bc
#13 [ffff80000b00be00] cpuidle_idle_call at ffff8000080f2708
#14 [ffff80000b00be40] do_idle at ffff8000080f2868
#15 [ffff80000b00be70] cpu_startup_entry at ffff8000080f2aac
#16 [ffff80000b00be90] secondary_start_kernel at ffff800008028d68
PID: 0 TASK: ffff000800279040 CPU: 2 COMMAND: "swapper/2"
#0 [ffff80000af2bd50] crash_save_cpu at ffff80000818f404
#1 [ffff80000af2bf00] do_handle_IPI at ffff800008028914
#2 [ffff80000af2bf50] ipi_handler at ffff800008028ab0
#3 [ffff80000af2bf60] handle_percpu_devid_irq at ffff800008137a8c
#4 [ffff80000af2bf90] handle_domain_irq at ffff80000812fa74
#5 [ffff80000af2bfc0] gic_handle_irq at ffff80000801006c
--- <IRQ stack> ---
#6 [ffff80000b013be0] call_on_irq_stack at ffff800008016d04
#7 [ffff80000b013bf0] do_interrupt_handler at ffff800008018ad8
#8 [ffff80000b013c00] el1_interrupt at ffff8000094604dc
#9 [ffff80000b013c20] el1h_64_irq_handler at ffff8000094618b8
#10 [ffff80000b013d60] el1h_64_irq at ffff80000801138c
#11 [ffff80000b013d80] cpuidle_enter_state at ffff800009022370
#12 [ffff80000b013dd0] cpuidle_enter at ffff8000090226bc
#13 [ffff80000b013e00] cpuidle_idle_call at ffff8000080f2708
#14 [ffff80000b013e40] do_idle at ffff8000080f2868
#15 [ffff80000b013e70] cpu_startup_entry at ffff8000080f2aa8
#16 [ffff80000b013e90] secondary_start_kernel at ffff800008028d68
PID: 0 TASK: ffff00080027d140 CPU: 3 COMMAND: "swapper/3"
#0 [ffff80000b01bd90] __switch_to at ffff8000080197f4
#1 [ffff80000b01bdb0] __schedule at ffff800009467350
#2 [ffff80000b01be20] schedule_idle at ffff800009467bd0
#3 [ffff80000b01be40] do_idle at ffff8000080f288c
#4 [ffff80000b01be70] cpu_startup_entry at ffff8000080f2aa8
#5 [ffff80000b01be90] secondary_start_kernel at ffff800008028d68
見方がよくわかりませんが、panicを起こしたコア3以外のswapper/0~2スレッドはIPI(コア間割込み)を受けて、panic時の終了処理をしているような感じがします。
おまけとして、プロセスが持つファイルディスクリプタを見てみます。panicを起こしたプロセスを見てみます。
crash> files 9779
PID: 9779 TASK: ffff000800899040 CPU: 3 COMMAND: "bash"
ROOT: / CWD: /home/ubuntu
FD FILE DENTRY INODE TYPE PATH
0 ffff000806111f00 ffff00080434b600 ffff00080896e080 CHR /dev/pts/4
1 ffff00080792f300 ffff000807ee4600 ffff000802b893c0 REG /proc/sysrq-trigger
2 ffff000806111f00 ffff00080434b600 ffff00080896e080 CHR /dev/pts/4
10 ffff000806111f00 ffff00080434b600 ffff00080896e080 CHR /dev/pts/4
255 ffff000806111f00 ffff00080434b600 ffff00080896e080 CHR /dev/pts/4
crash>
クラッシュを起こすために使ったSysRQの”/proc/sysrq-trigger”が形跡として残ってました。
4-2. 原因箇所を逆アセンブルで見る
disコマンドで逆アセしてみます。
$ dis write_sysrq_trigger
0xffff800008b2fb80 <write_sysrq_trigger>: mov x9, x30
0xffff800008b2fb84 <write_sysrq_trigger+4>: nop
0xffff800008b2fb80 <write_sysrq_trigger>: mov x9, x30
0xffff800008b2fb84 <write_sysrq_trigger+4>: nop
0xffff800008b2fb88 <write_sysrq_trigger+8>: paciasp
0xffff800008b2fb8c <write_sysrq_trigger+12>: stp x29, x30, [sp, #-48]!
0xffff800008b2fb90 <write_sysrq_trigger+16>: mov x29, sp
0xffff800008b2fb94 <write_sysrq_trigger+20>: stp x19, x20, [sp, #16]
0xffff800008b2fb98 <write_sysrq_trigger+24>: mov x19, x2]
0xffff800008b2fb9c <write_sysrq_trigger+28>: cbnz x2, 0xffff800008b2fbb4 <write_sysrq_trigger+52>
0xffff800008b2fba0 <write_sysrq_trigger+32>: mov x0, x19
0xffff800008b2fba4 <write_sysrq_trigger+36>: ldp x19, x20, [sp, #16]
0xffff800008b2fba8 <write_sysrq_trigger+40>: ldp x29, x30, [sp], #48
0xffff800008b2fbac <write_sysrq_trigger+44>: autiasp
0xffff800008b2fbb0 <write_sysrq_trigger+48>: ret
(...)
さすがに私のスキルでは何もわからないので、次でソースコードを表示できる用にしてみます。
4-3. ソースコードを表示できるようにする
上記で紹介した、gihyo.jpの柴田充也氏の記事ではカーネルのソースコードの入手の仕方が3つ紹介されていました。
- 1.Kernel Teamのgitリポジトリからリリースごとの最新のコードをcloneする(採用)
- 2.「sudo apt install linux-source」を実行する
- 3.「apt source linux-image-unsigned-$(uname -r)」を実行する
あくまでx86版の話なので全て使えるわけではありません。2番目の”apt install linux-source”は”linux-source-$(uname -r)”でソースを落とすと思われるのですが、ZynqMP版はどうやら提供されていないようです。やり方が間違っているだけかな…?
$ apt search linux-source
Sorting... Done
Full Text Search... Done
linux-source/jammy-updates,jammy-security 5.15.0.119.119 all
Linux kernel source with Ubuntu patches
linux-source-5.15.0/unknown 5.15.0-1018.20 all
Linux kernel source for version 5.15.0 with Ubuntu patches
linux-source-5.19.0/jammy-updates,jammy-security 5.19.0-50.50 all
Linux kernel source for version 5.19.0 with Ubuntu patches
linux-source-6.2.0/jammy-updates,jammy-security 6.2.0-39.40~22.04.1 all
Linux kernel source for version 6.2.0 with Ubuntu patches
linux-source-6.5.0/jammy-updates 6.5.0-45.45~22.04.1 all
Linux kernel source for version 6.5.0 with Ubuntu patches
ちなみに”apt source linux”でソースを落とした場合はそもそもZynqMP版かどうかはわかりません。
$ apt source linux
$ ls
linux-5.15.0 linux_5.15.0-119.129.diff.gz linux_5.15.0-119.129.dsc linux_5.15.0.orig.tar.gz
3番目の”linux-image-unsigned-$(uname -r)”もパッケージが用意されていないようです。
$ apt search linux-image-unsigned-5.15.0-103
Sorting... Done
Full Text Search... Done
linux-image-unsigned-5.15.0-1030-gke/jammy-updates,jammy-security 5.15.0-1030.35 arm64
Linux kernel image for version 5.15.0 on ARMv8 SMP
linux-image-unsigned-5.15.0-1030-gke-dbgsym/jammy-updates 5.15.0-1030.35 arm64
Linux kernel debug image for version 5.15.0 on ARMv8 SMP
linux-image-unsigned-5.15.0-1032-gke/jammy-updates,jammy-security 5.15.0-1032.37 arm64
Linux kernel image for version 5.15.0 on ARMv8 SMP
(...)
ということで、1番目のKernel Teamのgitリポジトリソースを落とす方法を行います。これは以前の記事で【ZynqMP】2. 認定Ubuntuのカーネルビルドのときに使用した方法です。
Kernel Teamのgitリポジトリソースを落とす方法で気をつけること
gitのブランチで”uname -r”とほぼ一致するブランチがあるのですが、コミットIDが一致するかわからないので、実際に動作しているカーネルとソースが完全一致するとは限らない気がします。このあたりのCanonicalさんの管理の仕組みを知らないので、なんとも言えませんが、少なくとも私は一致はしない可能性がある前提でソースコードの調査をしてみようと思います。ただし、今回のSysRQの部分はほぼ差分無いと思いますが。
完全一致させたければ、自分でUbuntuカーネルビルドをしたimageを使うのが確実でしょうか。
Xilinx認定Ubuntu22.04(jammy)のgitリポジトリをcloneします。私の回線では20分くらい。
“uname -r”を実行してtagに切り替えます。
$ uname -r
5.15.0-1031-xilinx-zynqmp
$ git clone https://git.launchpad.net/~canonical-kernel/ubuntu/+source/linux-xilinx-zynqmp/+git/jammy
$ cd jammy
# KR260の現行カーネルと同じリリースタグを探してチェックアウトします。
$ git tag
Ubuntu-xilinx-zynqmp-5.15.0-1000.1
Ubuntu-xilinx-zynqmp-5.15.0-1001.2
Ubuntu-xilinx-zynqmp-5.15.0-1002.3
Ubuntu-xilinx-zynqmp-5.15.0-1003.4
Ubuntu-xilinx-zynqmp-5.15.0-1004.5
Ubuntu-xilinx-zynqmp-5.15.0-1005.6
Ubuntu-xilinx-zynqmp-5.15.0-1006.7
Ubuntu-xilinx-zynqmp-5.15.0-1007.8
Ubuntu-xilinx-zynqmp-5.15.0-1008.9
Ubuntu-xilinx-zynqmp-5.15.0-1009.10
Ubuntu-xilinx-zynqmp-5.15.0-1010.11
Ubuntu-xilinx-zynqmp-5.15.0-1011.12
Ubuntu-xilinx-zynqmp-5.15.0-1012.13
Ubuntu-xilinx-zynqmp-5.15.0-1013.14
Ubuntu-xilinx-zynqmp-5.15.0-1014.15
Ubuntu-xilinx-zynqmp-5.15.0-1015.16
Ubuntu-xilinx-zynqmp-5.15.0-1018.20
Ubuntu-xilinx-zynqmp-5.15.0-1019.21
Ubuntu-xilinx-zynqmp-5.15.0-1020.22
Ubuntu-xilinx-zynqmp-5.15.0-1021.23
Ubuntu-xilinx-zynqmp-5.15.0-1021.25
Ubuntu-xilinx-zynqmp-5.15.0-1022.26
Ubuntu-xilinx-zynqmp-5.15.0-1023.27
Ubuntu-xilinx-zynqmp-5.15.0-1024.28
Ubuntu-xilinx-zynqmp-5.15.0-1025.29
Ubuntu-xilinx-zynqmp-5.15.0-1026.30
Ubuntu-xilinx-zynqmp-5.15.0-1027.31
Ubuntu-xilinx-zynqmp-5.15.0-1028.32
Ubuntu-xilinx-zynqmp-5.15.0-1029.33
Ubuntu-xilinx-zynqmp-5.15.0-1030.34
Ubuntu-xilinx-zynqmp-5.15.0-1031.35
Ubuntu-xilinx-zynqmp-5.15.0-1032.36
Ubuntu-xilinx-zynqmp-5.15.0-1033.37
Ubuntu-xilinx-zynqmp-5.15.0-1034.38
Ubuntu-xilinx-zynqmp-5.15.0-1035.39
Ubuntu-xilinx-zynqmp-5.15.0-1036.40
$ git checkout Ubuntu-xilinx-zynqmp-5.15.0-1031.35
# 適当なブランチを作っておく
$ git switch -c kr260-custom
次にcrashツールからソースコードを参照するようにするのですが、ソースのpathがFullPathで指定されているようです。pathをcrashに戻って調べます。”dis -s”コマンドでソースコードを表示できるのですが、このときソースファイルpathも表示されます。
カーネルソースフォルダを指定していないのでこの時点ではソースコードは表示できません。
crash> dis -s write_sysrq_trigger
FILE: /build/linux-xilinx-zynqmp-y1SglW/linux-xilinx-zynqmp-5.15.0/drivers/tty/sysrq.c
LINE: 1158
dis: write_sysrq_trigger: source code is not available
“build/linux-xilinx-zynqmp-y1SglW/linux-xilinx-zynqmp-5.15.0/”以下にソースフォルダを配置すれば良いようです。
#Pathを整える
$ mkdir -p ./build/linux-xilinx-zynqmp-y1SglW/linux-xilinx-zynqmp-5.15.0
$ mv jammy/* ./build/linux-xilinx-zynqmp-y1SglW/linux-xilinx-zynqmp-5.15.0/
crashに”--src“でソースフォルダを指定して起動します。シンボル名とアドレスの両方で表示できます。
$ ls
build jammy
$ crash --src ./ \
/usr/lib/debug/boot/vmlinux-5.15.0-1031-xilinx-zynqmp \
/var/crash/202408290941/dump.202408290941
(...)
#シンボル指定
crash> dis -s write_sysrq_trigger
FILE: /build/linux-xilinx-zynqmp-y1SglW/linux-xilinx-zynqmp-5.15.0/drivers/tty/sysrq.c
LINE: 1158
1153 /*
1154 * writing 'C' to /proc/sysrq-trigger is like sysrq-C
1155 */
1156 static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf,
1157 size_t count, loff_t *ppos)
* 1158 {
1159 if (count) {
1160 char c;
1161
1162 if (get_user(c, buf))
1163 return -EFAULT;
1164 __handle_sysrq(c, false);
1165 }
1166
1167 return count;
1168 }
#アドレス指定
crash> dis -s 0xffff800008b2fb80
FILE: /build/linux-xilinx-zynqmp-y1SglW/linux-xilinx-zynqmp-5.15.0/drivers/tty/sysrq.c
LINE: 1158
1153 /*
1154 * writing 'C' to /proc/sysrq-trigger is like sysrq-C
1155 */
1156 static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf,
1157 size_t count, loff_t *ppos)
* 1158 {
1159 if (count) {
1160 char c;
1161
1162 if (get_user(c, buf))
1163 return -EFAULT;
1164 __handle_sysrq(c, false);
1165 }
1166
1167 return count;
1168 }
crash>
無事ソースの場所が表示されるようになりました。
4-4. btを元にソースを追いかける
シンボル情報を元にソースの該当行を表示できるようになったので、btを元にソースを追いかけてみようと思います。
まずbtの再掲。
crash> bt
PID: 9779 TASK: ffff000800899040 CPU: 3 COMMAND: "bash"
#0 [ffff80000ee438b0] machine_kexec at ffff800008038b10
#1 [ffff80000ee438f0] __crash_kexec at ffff80000818eec4
#2 [ffff80000ee43a80] panic at ffff800009438eec
#3 [ffff80000ee43b60] sysrq_handle_crash at ffff800008b2e8e0
#4 [ffff80000ee43b70] __handle_sysrq at ffff800008b2f57c
#5 [ffff80000ee43bc0] write_sysrq_trigger at ffff800008b2fc38
(...)
スタックの6段目(#5)にwrite_sysrq_trigger関数が積まれています。”at ffff800008b2fc38″はwrite_sysrq_trigger関数内でサブルーチンに飛ぶ場所のアドレスです。すなわち__handle_sysrq関数のアドレスです。
write_sysrq_trigger関数から見ていきましょう。全部はさすがに長くなるので見ません。
crash> dis -s write_sysrq_trigger
FILE: /build/linux-xilinx-zynqmp-y1SglW/linux-xilinx-zynqmp-5.15.0/drivers/tty/sysrq.c
LINE: 1158
1153 /*
1154 * writing 'C' to /proc/sysrq-trigger is like sysrq-C
1155 */
1156 static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf,
1157 size_t count, loff_t *ppos)
* 1158 {
1159 if (count) {
1160 char c;
1161
1162 if (get_user(c, buf))
1163 return -EFAULT;
1164 __handle_sysrq(c, false);
1165 }
1166
1167 return count;
1168 }
次に”at ffff800008b2fc38″のアドレスを指定してみます。__handle_sysrq関数がよばれました。
crash> dis -s 0xffff800008b2fc38
FILE: /build/linux-xilinx-zynqmp-y1SglW/linux-xilinx-zynqmp-5.15.0/drivers/tty/sysrq.c
LINE: 1164
1159 if (count) {
1160 char c;
1161
1162 if (get_user(c, buf))
1163 return -EFAULT;
* 1164 __handle_sysrq(c, false);
1165 }
1166
1167 return count;
1168 }
次のスタックを見ていきます。
“#4 [ffff80000ee43b70] __handle_sysrq at ffff800008b2f57c”
__handle_sysrq関数を指定します。
FILE: /build/linux-xilinx-zynqmp-y1SglW/linux-xilinx-zynqmp-5.15.0/drivers/tty/sysrq.c
LINE: 576
571 if (i != -1)
572 sysrq_key_table[i] = op_p;
573 }
574
575 void __handle_sysrq(int key, bool check_mask)
* 576 {
577 const struct sysrq_key_op *op_p;
578 int orig_log_level;
579 int orig_suppress_printk;
580 int i;
581
582 orig_suppress_printk = suppress_printk;
583 suppress_printk = 0;
584
585 rcu_sysrq_start();
586 rcu_read_lock();
587 /*
588 * Raise the apparent loglevel to maximum so that the sysrq header
589 * is shown to provide the user with positive feedback. We do not
590 * simply emit this at KERN_EMERG as that would change message
591 * routing in the consumers of /proc/kmsg.
592 */
593 orig_log_level = console_loglevel;
594 console_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
595
596 op_p = __sysrq_get_key_op(key);
597 if (op_p) {
598 /*
(...)
次の関数コール先(sysrq_handle_crash関数を呼ぶ場所)を見てみましょう。
“ffff800008b2f57c”を指定します。
crash> dis -s 0xffff800008b2f57c
FILE: /build/linux-xilinx-zynqmp-y1SglW/linux-xilinx-zynqmp-5.15.0/drivers/tty/sysrq.c
LINE: 605
600 * should not) and is the invoked operation enabled?
601 */
602 if (!check_mask || sysrq_on_mask(op_p->enable_mask)) {
603 pr_info("%s\n", op_p->action_msg);
604 console_loglevel = orig_log_level;
* 605 op_p->handler(key);
606 } else {
607 pr_info("This sysrq operation is disabled.\n");
608 console_loglevel = orig_log_level;
609 }
610 } else {
611 pr_info("HELP : ");
612 /* Only print the help msg once per handler */
613 for (i = 0; i < ARRAY_SIZE(sysrq_key_table); i++) {
614 if (sysrq_key_table[i]) {
615 int j;
616
617 for (j = 0; sysrq_key_table[i] !=
618 sysrq_key_table[j] ; j++)
619 ;
620 if (j != i)
621 continue;
622 pr_cont("%s ", sysrq_key_table[i] ->help_msg);
623 }
624 }
625 pr_cont("\n");
626 console_loglevel = orig_log_level;
627 }
628 rcu_read_unlock();
629 rcu_sysrq_end();
630
631 suppress_printk = orig_suppress_printk;
632 }
“op_p->handler(key)”は関数ポインタですね。ソースを読んでいるとポインタが出現した瞬間に気が遠くなりそうになりますが、面倒なポインタの中身が分かるのはありがたいですね。crashはこれからも度々お世話になるかもしれません。
5.最後に
3記事に渡ってcrashツールのチュートリアルをやってみました。今後、Linuxドライバの開発にも手を付けていこうと考えているので、crashツールはかなり役に立ちそうと感じました。まだまだ使っていないコマンドもあるので、少しずつ使えるようにしていこうと思います。