Go 部落格
偵錯部署的 Go 1.12
簡介
Go 1.11 和 Go 1.12 朝讓開發人員可以針對他們部署到製作環境的最佳化二進位檔進行偵錯,邁進了一大步。
隨著 Go 編譯器在生產更快的二進位檔方面變得越來越積極,我們在可偵錯性方面卻有所損失。在 Go 1.10 中,使用者必須完全停用最佳化才能從像 Delve 這樣的互動式工具獲得良好的偵錯體驗。但使用者不應為了可偵錯性而犧牲效能,尤其是在執行製作服務時。如果問題發生在製作環境中,則您需要在製作環境中偵錯,而無需部署未最佳化的二進位檔。
對於 Go 1.11 和 1.12,我們專注於改善最佳化二進位檔(Go 編譯器的預設設定)的偵錯體驗。改善包括
- 更精確的值目檢視,尤其是函式輸入參數;
- 更精確地識別陳述式邊界,讓 Stepping 不那麼跳躍,中斷點也更常落於程式設計師的預期位置;
- 以及 Delve 呼叫 Go 函式的初步支援(goroutine 和資源回收讓這比 C 和 C++ 更加棘手)。
使用 Delve 除錯最佳化後的程式碼
Delve 是一個針對 Linux 和 macOS 中 x86 的 Go 除錯程式。Delve 了解 goroutine 和 Go 的其他特色,並且提供最佳的 Go 除錯體驗。Delve 也是下列功能背後的除錯引擎:GoLand、VS Code、以及 Vim。
Delve 通常使用 -gcflags "all=-N -l"
重新建置正在除錯的程式碼,這會停用內嵌和大部分最佳化。若要使用 delve 除錯最佳化的程式碼,請先建置最佳化的二進位檔,然後使用 dlv exec your_program
來除錯。或者,如果已經有來自崩潰的核心檔案,可以使用 dlv core your_program your_core
來檢查它。使用 1.12 和最新版本的 Delve,應該能夠檢查許多的變數,即使是在最佳化的二進位檔中。
改善值檢查
在除錯 Go 1.10 產生的最佳化二進位檔時,通常無法使用變數值。相反地,從 Go 1.11 開始,通常可以在最佳化的二進位檔中檢查變數,除非它們已完全最佳化到無法再最佳化。在 Go 1.11 中,編譯器開始發出 DWARF 位置清單,以便除錯程式可以在變數移入和移出暫存器時追蹤它們,並重建分散在不同暫存器和堆疊區塊中的複雜物件。
改善逐步執行
以下顯示出在 1.10 版除錯器中逐步執行簡單函式的範例,瑕疵處(跳過和重複的列)以紅色箭頭標示。
像這樣的瑕疵會讓人在逐步執行程式時容易迷失,也會妨礙中斷點運作。
Go 1.11 和 1.12 記錄敘述邊界資訊,並且更有效率地追蹤原始程式碼列號,即使經過最佳化和內嵌。因此,在 Go 1.12 中,逐步執行這段程式碼會在每一行暫停,而且是以預期的順序進行。
函式呼叫
Delve 中的函式呼叫支援仍在開發中,但簡單的案例可行。例如
(dlv) call fib(6)
> main.main() ./hello.go:15 (PC: 0x49d648)
Values returned:
~r1: 8
未來展望
對於最佳化後的二進位檔,Go 1.12 是邁向更佳除錯體驗的一大步,而且我們計畫讓它進一步提升。
除錯性與效能之間存在著基本的權衡,因此我們專注於最高優先順位的除錯缺陷,並致力於收集自動化指標來監控進度並捕獲回歸。
我們專注於針對可變位置產生正確的除錯器資訊,因此如果可變數可以被印出,它會以正確的方式被印出。我們也在研究讓可變數值能供應的時間變多,尤其是在呼叫點等關鍵點,不過在許多情況下改善這些會需要讓程式執行速度變慢。最後,我們正在改善逐步處理:我們著重在帶著 panic 的逐步處理順序、迴圈周圍的逐步處理順序,並儘可能遵循原始碼順序。
macOS 支援說明
Go 1.11 開始壓縮除錯資訊以便減小二進位檔案大小。這有原生支援 Delve,但 LLDB 與 GDB 都未在 macOS 上支援壓縮的除錯資訊。如果您使用 LLDB 或 GDB,有兩種解決方法:使用 -ldflags=-compressdwarf=false
編譯二進位檔案,或使用 splitdwarf (go get golang.org/x/tools/cmd/splitdwarf
) 將既存二進位檔案中的除錯資訊解壓縮。
下一篇:Go 2018 調查結果
上一篇:使用 Go 模組
部落格索引