Go 部落格
在 Go 1.12 中除錯您部署的程式碼
引言
Go 1.11 和 Go 1.12 在允許開發者除錯與生產環境部署的最佳化二進位制檔案相同方面取得了顯著進展。
隨著 Go 編譯器在生成更快的二進位制檔案方面變得越來越積極,我們在可除錯性方面有所退步。在 Go 1.10 中,使用者需要完全停用最佳化才能從 Delve 等互動式工具獲得良好的除錯體驗。但是使用者不應該為了可除錯性而犧牲效能,尤其是在執行生產服務時。如果問題發生在生產環境中,您需要在生產環境中進行除錯,這不應該要求部署未經最佳化的二進位制檔案。
對於 Go 1.11 和 1.12,我們專注於改進最佳化二進位制檔案(Go 編譯器的預設設定)的除錯體驗。改進包括:
- 更準確的值檢查,尤其是在函式入口處的引數;
- 更精確地識別語句邊界,從而使單步執行不那麼跳躍,斷點更能準確地落在程式設計師期望的位置;
- 以及對 Delve 呼叫 Go 函式的初步支援(goroutine 和垃圾回收使其比 C 和 C++ 更棘手)。
使用 Delve 除錯最佳化程式碼
Delve 是一個用於 Go 的 x86 偵錯程式,支援 Linux 和 macOS。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 原生支援此功能,但 macOS 上的 LLDB 和 GDB 都不支援壓縮除錯資訊。如果您正在使用 LLDB 或 GDB,有兩種解決方法:使用 -ldflags=-compressdwarf=false
構建二進位制檔案,或者使用 splitdwarf(go get golang.org/x/tools/cmd/splitdwarf
)來解壓縮現有二進位制檔案中的除錯資訊。
下一篇文章: Go 2018 調查結果
上一篇文章: 使用 Go Modules
部落格索引