使用 QEMU 對核心進行偵錯

Zircon 可以使用 QEMU 在模擬中執行。QEMU 可透過預先建構的二進位檔或本機建構而成

預先建構的 QEMU

QEMU 是由 jiri 下載為 jiri updatejiri run-hooks 的一部分。

QEMU 已擷取至 //prebuilt/third_party/qemu。您可以透過 fx qemu 以最簡便的方式執行 (請見下文)。

建構 QEMU

安裝必要條件

在 macOS 上建構 QEMU 需要一些套件。從 macOS 10.12.1 版本:

# Using http://brew.sh
brew install pkg-config glib automake libtool

# Or use http://macports.org ("port install ...") or build manually

建構

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 中新增 /path/to/install/bin,或在叫用 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 先重新建構,以及使用 -g 執行圖形 framebuffer。

如要退出 Qemu,請輸入 Ctrl-a x。按下 Ctrl-a h 鍵即可查看其他指令。

在 QEMU 中啟用「網路」功能

run-zircon 指令碼如果指定 -N 引數,就會嘗試使用名為「qemu」的 Linux tun/tap 網路裝置建立網路介面。QEMU 不需要以任何特殊權限執行,但您必須事先建立永久的釘選/輕觸裝置 (需要具有 Root 權限):

Linux:

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

這樣就足以啟用連結本機 IPv6 (做為記錄工具使用的工具)。

使用 macOS:

macOS 不支援立即可用的 Tun/tap 裝置;不過,還有一組廣泛使用的核心擴充功能,稱為 tuntaposx,您可以在這裡下載。安裝程式完成後,擴充功能會建立最多 16 個觸控裝置/輕觸裝置。run-zircon-x64 指令碼使用的是 /dev/tap0。

sudo chown $USER /dev/tap0

# Run zircon in QEMU, which will open /dev/tap0
fx qemu -N

# (In a different window) bring up tap0 with a link local IPv6 address
sudo ip addr add dev tap0 fc00::/7
sudo ip link set tap0 up

在 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」繼續執行系統。如果您想在不使用 GDB 的情況下執行 QEMU,但稍後能夠透過 GDB 附加,請在上述範例中不含「-S」來啟動 QEMU:

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 開機後,會回到殼層 1,您會看見 Zircon 提示。

mxsh>

如果您在殼層 2 中按下 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 指令碼

zircon/kernel/scripts/zircon.elf-gdb.py 指令碼應由 gdb 自動載入。如果伺服器沒有自動載入,您可能需要將其路徑新增至 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
  • 加強對透過核心錯誤自動解開此操作的支援。

請注意:這個指令碼並非完全隨著 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

即可還原狀態。或者,您也可以透過 cmd 將輸入方式設為:

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

理論上,您可以將 qcow2 映像檔與建構輸出目錄封裝,任何人都應能還原您的狀態,並開始從 QEMU 控制台中取件。