組込み

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

1.記事一覧

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

Zynq7000(armv7)版はこちら

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の柴田充也氏の記事とその他の参考資料を元にやっていきます。

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.環境準備

以前の記事「【Linux】6. crash toolでカーネルメモリを見る」と同様にDebug symbol packagesをインストールしていきます。最後のdbgsymは15分程度インストールに時間がかかります。

Bash
$ 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

以下にカーネルデバッグシンボルファイルが出来ます。

Bash
 ls /usr/lib/debug/boot/
vmlinux-5.15.0-1031-xilinx-zynqmp

4.クラッシュダンプを見てみる

4-1. bt(backtrace)でpanic発生箇所をさぐる

まずcrashでカーネルクラッシュダンプを読み込んでみます。

  • 第1引数:カーネルデバッグシンボル
  • 第2引数:カーネルクラッシュダンプ
Bash
$ 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”で既にクラッシュ原因がほぼ見えていますが、とりあえずバックトレースを見てみます。

Bash
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で終了です。

Bash
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を引数にいれます。

Bash
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を起こしたプロセスを見てみます。

Bash
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コマンドで逆アセしてみます。

Bash
$ 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版はどうやら提供されていないようです。やり方が間違っているだけかな…?

Bash
$ 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版かどうかはわかりません。

Bash
$ 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)”もパッケージが用意されていないようです。

Bash
$ 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に切り替えます。

Bash
$ 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も表示されます。

カーネルソースフォルダを指定していないのでこの時点ではソースコードは表示できません。

Bash
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/”以下にソースフォルダを配置すれば良いようです。

Bash
#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“でソースフォルダを指定して起動します。シンボル名とアドレスの両方で表示できます。

Bash
$ 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の再掲。

Bash
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関数から見ていきましょう。全部はさすがに長くなるので見ません。

Bash
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関数がよばれました。

Bash
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関数を指定します。

Bash
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”を指定します。

Bash
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ツールはかなり役に立ちそうと感じました。まだまだ使っていないコマンドもあるので、少しずつ使えるようにしていこうと思います。

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