偵錯元件

開發軟體意味著處理程式當機問題以及找出錯誤來源。Fuchsia 提供一套工具,可協助您找出並診斷元件中的問題,從分析當機記錄到核心程式碼的完整逐步偵錯,應有盡有。

分析當機事件

Fuchsia 會在啟動時啟動名為 crashanalyzer 的程式,此程式會回報程式當機情形,並將當機執行緒的反向追蹤記錄輸出至系統記錄。雖然您可以在執行階段查看記錄檔,藉此直接探索這些記錄檔,但系統會使用堆疊記憶體位址參照對反向內容進行編碼,而不是指向程式來源檔案中的對應行。

[klog][I] devmgr: crash_analyzer_listener: analyzing exception type 0x108
[klog][I] <== fatal exception: process crasher[42410] thread initial-thread[42424]
[klog][I] <== fatal page fault, PC at 0x1e1888dbbbd7
[klog][I]  CS:                   0 RIP:     0x1e1888dbbbd7 EFL:            0x10246 CR2:                  0
[klog][I]  RAX:                  0 RBX:                0x1 RCX:     0x721ad98697c6 RDX:     0x77accb36f264
[klog][I]  RSI:                  0 RDI:                  0 RBP:     0x2781c4816f90 RSP:     0x2781c4816f80
[klog][I]   R8:                  0  R9:                  0 R10:                  0 R11:              0x246
[klog][I]  R12:     0x773bf11dcda0 R13:     0x773bf11dcdd0 R14:               0x16 R15:         0x78050d69
[klog][I]  errc:               0x6
[klog][I] bottom of user stack:
[klog][I] 0x00002781c4816f80: f11dcda0 0000773b 9ccd2b38 000039b2 |....;w..8+...9..|
[klog][I] 0x00002781c4816f90: c4816fd0 00002781 88dbbba7 00001e18 |.o...'..........|
[klog][I] 0x00002781c4816fa0: 00000008 00000000 9ccd2b38 000039b2 |........8+...9..|
[klog][I] 0x00002781c4816fb0: f11dcf70 0000773b f11dcf70 0000773b |p...;w..p...;w..|
[klog][I] 0x00002781c4816fc0: cb36f570 000077ac f11dcdd0 0000773b |p.6..w......;w..|
[klog][I] 0x00002781c4816fd0: c4816ff0 00002781 cb2b0d0f 000077ac |.o...'....+..w..|
[klog][I] 0x00002781c4816fe0: 00000054 00000000 f11dcf70 0000773b |T.......p...;w..|
[klog][I] 0x00002781c4816ff0: f11dcfe0 0000773b 00000000 00000000 |....;w..........|
[klog][I] arch: x86_64
[klog][I] dso: id=a94c78564173530d51670b6586b1aa471e004f06 base=0x7d3506a49000 name=libfdio.so
[klog][I] dso: id=a61961ba9776a67a00fb322af9ebbdcfd1ce3f62 base=0x77accb297000 name=libc.so
[klog][I] dso: id=760f1e6e47d3dd8b6a19150aa47241279ec75a9c base=0x721ad9863000 name=<vDSO>
[klog][I] dso: id=b18462140c6784a53736105bbf3021852eeda68c base=0x1e1888dbb000 name=app:crasher
[klog][I] bt#01: pc 0x1e1888dbbbd7 sp 0x2781c4816f80 (app:crasher,0xbd7)
[klog][I] bt#02: pc 0x1e1888dbbba7 sp 0x2781c4816fa0 (app:crasher,0xba7)
[klog][I] bt#03: pc 0x77accb2b0d0f sp 0x2781c4816fe0 (libc.so,0x19d0f)
[klog][I] bt#04: pc 0 sp 0x2781c4817000
[klog][I] bt#05: end

這是因為在建構時,系統預設會移除核心二進位檔中的偵錯符號。如要正確分析當機記錄,您必須將這些符號重新套用至反向追蹤記錄,才能查看原始碼行號中的呼叫堆疊。當您呼叫 ffx log 指令時,開發人員工具會透過名為 symbolizer 的額外二進位檔處理原始記錄檔,該二進位檔會將本機建構設定中的符號套用至記錄檔中的任何反向追蹤。

ffx log

您看到的輸出內容包含重新套用至反向追蹤的符號:

[klog][I] devmgr: crash_analyzer_listener: analyzing exception type 0x108
... same output as "raw" backtrace ...
start of symbolized stack:
[klog][I] #01: blind_write at ../../src/developer/forensics/crasher/cpp/crasher.c:21
[klog][I] #02: main at ../../src/developer/forensics/crasher/cpp/crasher.c:137
[klog][I] #03: start_main at ../../zircon/third_party/ulib/musl/src/env/__libc_start_main.c:49
[klog][I] #04: unknown, can't find pc, sp or app/library in line
end of symbolized stack

透過正確符號的反向追蹤記錄,您可以直接從原始碼中發現異常終止的網站。

逐步偵錯

光是知道程式在哪些位置當機,可能不足以完整診斷問題。有時您必須逐步查看程式碼,甚至檢查記憶體中的變數狀態。為了支援這項功能,Fuchsia 新增了名為 zxdb 的核心程式碼偵錯工具。

zxdb 工具是一種用戶端,可連線至目標裝置上正在執行的 debug_agent 元件。您可以使用 zxdb 指令設定 debug_agent 以附加至特定程序,並設定中斷點。將偵錯工作階段連結到執行中的程序後,zxdb 可讓您逐步完成程式碼並檢查堆疊框架。

這張圖表顯示 Fuchsia 偵錯工具 (zxdb) 如何與在 Fuchsia 裝置上執行的 debug_agent 服務互動,以執行程序的互動式偵錯。

設定偵錯工作階段必須遵循下列整體步驟:

  1. 在目標裝置上執行 debug_agent 元件。
  2. 執行 zxdb 用戶端並連線至目標裝置。
  3. 設定 zxdb 的位置,以找出偵錯符號。

如要啟動偵錯工作階段,最簡單的方法是使用 ffx debug connect 指令,在本機 Fuchsia 建構作業中完成上述所有操作。不過,如果您需要單獨設定這些步驟,也可以手動執行這些步驟。

偵錯工作階段啟用後,系統會將您導向至 [zxdb] 提示,以發出偵錯工具指令。您可以使用 zxdbdebug_agent 設為使用名稱篩選器附加至程序,並設定待處理的中斷點 (即使目前並未執行任何比對程序)。

以下範例會在主節點上設定在開始時停止的待處理中斷點,並等待名為「hello-world」的程序開始:

[zxdb] attach hello-world
Waiting for process matching "hello-world"

[zxdb] break main
Breakpoint 1 (Software) on Global, Enabled, stop=All, @ main
Pending: No matches for location, it will be pending library loads.

將偵錯工具附加到程序後,您可以使用 zxdb 指令來控制及檢查程序的狀態。以下是一系列常見的指令:

step 繼續閱讀執行緒中的下一行程式碼
next 跳至執行緒中的下一行程式碼
continue 繼續執行,直到下一個中斷點、例外狀況或結束為止
frame 列出或從目前的堆疊框架選取
print 評估運算式並輸出結果

練習:使用 Fuchsia 偵錯工具

在本練習中,您將使用 Fuchsia 偵錯工具 (zxdb) 檢查 echo 元件的運作中執行個體,並瞭解當機原因。

啟動模擬器

如果您尚未擁有執行中的執行個體,請透過網路支援啟動 FEMU:

ffx emu start workstation_eng.x64 --headless

啟動偵錯工作階段

模擬器啟動後,使用 ffx debug connect 指令啟動 zxdb 偵錯工作階段:

ffx debug connect
Connecting (use "disconnect" to cancel)...
Connected successfully.
👉 To get started, try "status" or "help".
[zxdb]

成功連線後,zxdb 提示即可準備接受指令。

連接至元件

在啟動元件之前,請設定 zxdb 以附加至 echo 的執行個體。這樣就能在程序開始時立即附加偵錯工具:

[zxdb] attach echo

設定 greeting() 函式的中斷點:

[zxdb] break greeting

偵錯工具準備就緒後,請啟動新的 echo 元件執行個體:

ffx component run /core/ffx-laboratory:echo fuchsia-pkg://fuchsiasamples.com/echo-example#meta/echo.cm

探索偵錯工作階段

達到 greeting() 中的中斷點時,執行作業會停止,偵錯工具會等待新的指令。使用 list 指令顯示執行作業目前暫停的位置:

[zxdb] list
  17
  18 // Return a proper greeting for the list
▶ 19 std::string greeting(std::vector<std::string>& names) {
  20   // Join the list of names based on length
  21   auto number_of_names = names.size();
  22   switch (number_of_names) {
  23     case 0:
  24       return "Nobody!";
  25     case 1:
  26       return join(names, "");
  27     case 2:
  28       return join(names, " and ");
  29     default:

使用 next 指令進入 greeting() 函式:

[zxdb] next

print 指令會輸出目前堆疊框架中的任何變數狀態。列印 names 目前的值:

[zxdb] print names
{"Alice", "Bob", "Spot"}

使用 next 再多執行 greeting() 函式一次:

[zxdb] next

如要讓程式繼續執行,請使用 continue 指令:

[zxdb] continue

結束偵錯工作階段,返回終端機:

[zxdb] exit

加入部分異常終止程式碼

接下來,您要在 main() 中新增一些程式碼,導致元件停止運作 (或恐慌)。如要模擬這個行為,請在收集引數後立即新增 strlen(nullptr) 參照:

echo/main.cc

int main(int argc, const char* argv[], char* envp[]) {
  // ...

  // Simulate a crash 
  std::strlen(nullptr);

  // Print a greeting to syslog
  std::cout << "Hello, " << echo::greeting(arguments) << "!" << std::endl;

  return 0;
}

建構更新過的套件,並發布至 fuchsiasamples.com 存放區:

bazel run //fuchsia-codelab/echo:pkg.publish -- \
    --repo_name fuchsiasamples.com

使用 zxdb 啟動新的偵錯工作階段:

ffx debug connect

對當機的堆疊框架進行偵錯

設定要附加至 echo 元件的偵錯工具:

[zxdb] attach echo

啟動新的元件執行個體:

ffx component run /core/ffx-laboratory:echo fuchsia-pkg://fuchsiasamples.com/echo-example#meta/echo.cm --recreate

此時,偵錯工具會偵測出例外狀況並停止執行:

Attached Process 1 state=Running koid=1164808 name=echo.cm
════════════════════════════════════════════════
 Page fault reading address 0x0 (second chance)
════════════════════════════════════════════════
 Process 1 (koid=1164808) thread 1 (koid=1164810)
 Faulting instruction: 0x43e0fd349210

🛑 strlen(const char*) • strlen.c:21
[zxdb]

使用 frame 指令檢查當機階段的堆疊追蹤:

[zxdb] frame
▶ 0 strlen(…) • strlen.c:21
  1 main(…) • main.cc:27
  2 «libc startup» (-r expands)
  3 «libc startup» (-r expands)
  4 $elf(_start) + 0x11

請注意,堆疊追蹤中的行 1 表示 main.cc 中發生當機的時間點,與 nullptr 參照對應。

目前的堆疊框架 (頁框 0) 位於系統程式庫內,但您可以在指令前方加上堆疊追蹤的影格編號,藉此檢查任何堆疊框架。

如要列印當機點的引數值,請傳遞影格編號,如下所示:

[zxdb] frame 1 print arguments
{"Alice", "Bob", "Spot"}

結束偵錯工作階段,返回終端機:

[zxdb] exit

刪除執行個體

使用下列指令清除 echo 例項:

ffx component destroy /core/ffx-laboratory:echo