使用 QEMU 對核心進行偵錯

注意:QEMU 不太支援核心偵錯。現有的支援和工具不穩定,而且經常無法正常運作。如果下列步驟無法解決問題,請不要感到意外。

Zircon 可使用 QEMU 在模擬環境中執行。QEMU 可以透過預先建構的二進位檔安裝,也可以在本機建構。

預建 QEMU

jiri 會下載 QEMU,做為 jiri updatejiri run-hooks 的一部分。

QEMU 會擷取到 //prebuilt/third_party/qemu。使用 fx qemu 執行這項作業最方便 (詳情請參閱下文)。

建構 QEMU

cd $SRC
git clone --recursive https://fuchsia.googlesource.com/third_party/qemu
cd qemu
./configure --target-list=aarch64-softmmu,x86_64-softmmu
make -j32
sudo make install

如果您不想安裝在 /usr/local (預設位置),這會要求您成為根使用者,請新增 --prefix=/path/to/install (可能是 $HOME/qemu)。然後,您必須將 /path/to/install/bin 新增至 PATH,或在叫用 run-zircon-{arch} 時使用 -q /path/to/install。

在 QEMU 下執行 Zircon

# for aarch64
fx set bringup.arm64
fx build
fx qemu

# for x86
fx set bringup.x64
fx build
fx qemu

如果 QEMU 不在路徑中,請使用 -q 指定其位置。

-h 旗標會列出多個選項,包括視需要先重建的 -b,以及使用圖形 framebuffer 執行的 -g。

如要退出 qemu,請輸入 Ctrl-a x。使用 Ctrl-a h 即可查看其他指令。

在 QEMU 下啟用網路功能

如果提供 -N 引數,run-zircon 指令碼會嘗試使用名為「qemu」的 Linux tun/tap 網路裝置建立網路介面。QEMU 不需要任何特殊權限即可執行這項操作,但您必須事先建立持續性 tun/tap 裝置 (這需要您是根使用者):

sudo ip tuntap add dev qemu mode tap user $USER
sudo ip link set qemu up

這樣就足以啟用連結本機 IPv6 (因為 loglistener 工具會使用)。

在 QEMU 下使用模擬磁碟

使用以核心為基礎的建構版本 (實際上是任何高於啟動的產品),會自動表示提供磁碟來服務 fvm 分區,其中包含用於可變動儲存空間的 minfs 分區,以及用於套件資料儲存空間的 blobfs 分區。

你可以使用下列標記附加其他圖片:

fx qemu -d [-D <disk_image_path (default: "blk.bin")>]

使用 GDB 偵錯核心

範例階段

以下是工作階段範例,可協助您快速上手。

在執行 QEMU 的殼層中:

shell1$ fx qemu -- -s -S
[... some QEMU start up text ...]

這會啟動 QEMU,但系統會在啟動時凍結,等待您在 GDB 中使用「continue」繼續執行。如要執行 QEMU 但不使用 GDB,但稍後可以附加 GDB,請啟動 QEMU,但不要使用上述範例中的「-S」:

shell1$ fx qemu -- -s
[... some QEMU start up text ...]

然後在 out 目錄中找出 zircon.elf

然後在執行 GDB 的殼層中:

shell2$ gdb path-to-zircon.elf -x ${FUCHSIA_DIR}/zircon/kernel/scripts/zircon.elf-gdb.py
Reading symbols from /fuchsia/out/default/kernel_x64-clang/zircon.elf...
Loading zircon.elf-gdb.py ...
Zircon extensions installed for /fuchsia/out/default/kernel_x64-clang/zircon.elf
(gdb) target extended-remote :1234
Remote debugging using :1234
0x000000000000fff0 in ?? ()

Thread 1 hit Breakpoint -1, 0x0000000000100050 in ?? ()
Watchpoint set on KASLR relocated base variable

Thread 1 hit Hardware read watchpoint -2: *0x767ca0

Value = 0
0x00000000001000ef in ?? ()
Update symbols and breakpoints for KASLR
KASLR: Correctly reloaded kernel at 0xffffffff00000000
(gdb) # Don't try to do too much at this point.
(gdb) # GDB can't handle architecture switching in one session,
(gdb) # and at this point the architecture is 16-bit x86.
(gdb) break lk_main
Breakpoint 1 at 0xfffffffff010cb58: file kernel/top/main.c, line 59.
(gdb) continue
Continuing.

Breakpoint 1, lk_main (arg0=1, arg1=18446744071568293116, arg2=0, arg3=0)
    at kernel/top/main.c:59
59  {
(gdb) continue

此時 Zircon 會啟動,並返回 shell1,您會看到 Zircon 提示。

mxsh>

此時在 shell2 中按下 Ctrl-C,即可返回 GDB。

(gdb) # Having just done "continue"
^C
Program received signal SIGINT, Interrupt.
arch_idle () at kernel/arch/x86/64/ops.S:32
32      ret
(gdb) info threads
  Id   Target Id         Frame
  4    Thread 4 (CPU#3 [halted ]) arch_idle () at kernel/arch/x86/64/ops.S:32
  3    Thread 3 (CPU#2 [halted ]) arch_idle () at kernel/arch/x86/64/ops.S:32
  2    Thread 2 (CPU#1 [halted ]) arch_idle () at kernel/arch/x86/64/ops.S:32
* 1    Thread 1 (CPU#0 [halted ]) arch_idle () at kernel/arch/x86/64/ops.S:32

QEMU 會為每個 CPU 向 GDB 回報一個執行緒。

zircon.elf-gdb.py 指令碼

gdb 應會自動載入 zircon/kernel/scripts/zircon.elf-gdb.py 指令碼。如果未自動載入,您可能需要將路徑新增至 gdb 的 auto-load-safe-path。或者,您也可以在 gdb 的指令列標記中手動設定:

$ gdb path-to-zircon.elf -x ${FUCHSIA_DIR}/zircon/kernel/scripts/zircon.elf-gdb.py

這項功能提供多種服務:

  • gdb 的 KASLR 重新配置,可讓您在函式中正確設定中斷點。

  • Zircon 物件的漂亮列印工具 (目前沒有)。

  • 多個 Zircon 專屬指令,全都以「zircon」為前置字元。如要查看這些資訊,請按照下列步驟操作:

(gdb) help info zircon
(gdb) help set zircon
(gdb) help show zircon
  • 透過核心錯誤,加強對自動解除堆疊的 unwinder 支援。

注意:這個指令碼不會隨著 zircon 變更而更新。

注意:由於 錯誤 67893,如果使用 qemu 和 kvm,KASLR 部分可能無法運作。 如要解決這個問題,您可以在 gdb 中執行下列指令:

(gdb) # before attaching to qemu
(gdb) mem 0 0xffffffffffffffff ro
(gdb) target extended-remote :1234
(gdb) ...
(gdb) # after the script performed the kaslr relocations
(gdb) mem auto

終止工作階段

如要終止 QEMU,可以從 GDB 將指令傳送至 QEMU:

(gdb) monitor quit
(gdb) quit

透過 Gdb 與 QEMU 互動

如要查看可從 GDB 執行的 QEMU 指令清單:

(gdb) monitor help

儲存系統狀態以進行偵錯

如果當機問題難以偵錯,或需要偵錯方面的協助,您可以儲存類似核心傾印的系統狀態。

bash$ qemu-img create -f qcow2 /tmp/my_snapshots.qcow2 32M

會建立「32M」的塊狀儲存裝置。接著啟動 QEMU,並告知裝置相關資訊,但不要將裝置附加至訪客系統。這沒關係,我們不打算用它備份磁碟狀態,只是想要核心傾印。注意:如果您已模擬區塊裝置,且該裝置使用 qcow2 格式,則可略過所有這些步驟。

bash$ qemu <normal_launch_args> -drive if=none,format=qcow2,file=/tmp/my_snapshots.qcow2

當您想儲存核心狀態時,請使用 切換至 QEMU 控制台。這時您應該會看到 (qemu) 提示。 接著只要說出:

(qemu) savevm my_backup_tag_name

之後,您可以使用相同的機器 (以與先前相同的引數啟動), 進入控制台並執行下列指令:

(qemu) loadvm my_backup_tag_name

還原狀態。或者,您也可以透過指令列執行下列指令:

bash$ qemu <normal_launch_args> -drive if=none,format=qcow2,file=/tmp/my_snapshots.qcow2 -loadvm my_backup_tag_name

理論上,您可以將 qcow2 映像檔與建構輸出目錄封裝在一起,任何人都能還原您的狀態,並從 QEMU 控制台開始探索內容。