Debugging Go Program
定位Go 程式的錯誤,通常有兩種方式:
- 列印日誌
- 除錯
Go是編譯型語言,且IDE
對除錯的支援不太好,絕大多數Go
的初學者除錯Go
程式都是通過log.Printf
等列印日誌方式定位問題。通常過程如下:
-
程式
panic
或者報錯 - 修改Go 程式,新增列印除錯日誌程式碼
- 編譯Go 程式
-
重複錯誤出現時的操作,檢視日誌
debug
如果程式比較複雜,需要反覆增加日誌輸出才能找到問題原因。熟練的使用偵錯程式能夠提高我們面對這樣問題的靈活性。此文就是介紹如何使用delve 等偵錯程式除錯Go 程式。本文重點是全面介紹除錯相關知識,具體除錯工具的操作網上相關資料已經很全面(見最後一章參考),不作為重點。
使用GDB除錯Go程式
簡介
GDB不能很好的理解Go 程式。Go 程式和GDB 的stack management 、threading 、runtime 模型差異很大,並可能導致偵錯程式輸出不正確的結果。因此,雖然GDB 在某些場景下有用,比如除錯Cgo 程式碼、除錯runtime ,但是對於Go 程式來說,尤其是高併發程式,GDB 不是一個可靠的偵錯程式。而且,對於Go 語言專案本身來說,解決這些的問題很困難,也不是一個高優先順序的事情。
當你在 Linux 、 macOS 、 FreeBSD 、 NetBSD 上使用gc 工具鏈編譯和連線Go 程式的時候,產生的二進位制包含 DWARFv4 除錯資訊,最近版本的GDB 偵錯程式可以利用這些資訊觀察一個執行的程序或者core dump 。
可以通過-w
標記告訴聯結器去掉這些除錯資訊,比如:
go build -ldflags "-w" .
gc編譯器生成的程式包含函式內聯 和變數註冊。這些優化可能會讓gdb 除錯更加困難。如果你需要禁用這些優化,使用下面的引數構建程式:
go build -gcflags "all=-N -l" .
如果你想要使用gdb
檢查一個core dump
,你可以在程式崩潰的時候觸發一個dump。在支援dump的OS
上,使用GOTRACEBACK=crash
環境變數(參考runtime package documentation
)。
Go 1.11版本中,由於編譯器會產生更多更準確的除錯資訊,為了減少二進位制的大小,DWARF
除錯資訊編譯時候會預設被壓縮。這對於大多數ELF
工具來說這是透明的,也得到Delve
支援。但是macOS和Windows上一些工具不支援。如果要禁用DWARF
壓縮,可以在編譯的時候傳入引數-ldflags "-compressdwarf=false"
。
Go 1.11添加了一個實驗性的功能,允許在偵錯程式中呼叫函式。目前這個特性僅得到Delve (version 1.1.0及以上)的支援。
常用命令和教程
可以參考下面幾篇文章,這裡不做贅述:
使用LLDB除錯Go程式
簡介
Mac下如果你安裝XCode,應該會自動安裝了LLDB,LLDB是XCode的預設偵錯程式。LLDB的安裝方法可以參考這裡 。
GDB的命令格式非常自由,和GDB的命令不同,LLDB命令格式非常結構化(“嚴格”的婉轉說法)。LLDB的命令格式如下:
<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
解釋一下:
-
<command>
(命令)和<subcommand>
(子命令):LLDB除錯命令的名稱。命令和子命令按層級結構來排列:一個命令物件為跟隨其的子命令物件建立一個上下文,子命令又為其子命令建立一個上下文,依此類推。 -
<action>
:執行命令的操作 -
<options>
:命令選項。需要注意的是,如果aguments的第一個字母是”-“,<options>
和<arguments>
中間必須以”–”分隔開。所以如果你想啟動一個程式,並給這個程式傳入-program_arg value
引數,可以輸入(lldb) process launch --stop-at-entry -- -program_arg value
-
<arguement>
:命令的引數 -
[]
:表示命令是可選的,可以有也可以沒有
LLDB也減少了gdb中一些命令的特殊寫法,讓使用者更加容易理解命令的意圖。可以閱讀LLDB文件 中下面一段文字瞭解細節:
We also tried to reduce the number of special purpose argument parsers, which sometimes forces the user to be a little more explicit about stating their intentions. ……
LLDB的命令同樣給很多命令提供了縮寫形式,可以通過(lldb) help
檢視所有的縮寫命令。
gdb和LLDB的命令之間的差別 可以訪問這裡檢視。
常用命令
使用LLDB需要熟悉的常用命令如下:
幫助
(lldb) help help
Show a list of all debugger commands, or give details about a specific command.
Syntax: help [
使用LLDB載入一個程式
$lldb /binary-path Current executable set to '/binary-path'(x86_64). $lldb (lldb) file /binary-path Current executable set to '/binary-path'(x86_64).
設定斷點(breakpoints)
常見的設定斷點的命令如下:
(lldb) breakpoint set --file source-file.go --line 11 Breakpoint 1: where = sample1`github.com/ethancai/go-debug-practice/sample1/model.(*MyStruct).Print + 19 at my_struct.go:11, address = 0x00000000010b2713
breakpoint
命令會建立一個邏輯的
斷點,一個邏輯的斷點可以對應一個或者多個位置location
。比如,通過selector
設定的斷點對應所有實現了selector
的方法。
breakpoint
命令:
(lldb) help breakpoint Commands for operating on breakpoints (see 'help b' for shorthand.) Syntax: breakpoint <subcommand> [<command-options>] ...
設定觀察點(Watchpoints)
watchpoint
命令:
(lldb) help watchpoint Commands for operating on watchpoints. Syntax: watchpoint <subcommand> [<command-options>] ...
執行程式或者附著程式
process
命令:
(lldb) help process Commands for interacting with processes on the current platform. Syntax: process <subcommand> [<subcommand-options>] ...
控制程式執行或者檢查Thread狀態
thread
命令
(lldb) help thread Commands for operating on one or more threads in the current process. Syntax: thread <subcommand> [<subcommand-options>] ...
檢查堆疊結構(Stack Frame)狀態
frame
命令
(lldb) help frame Commands for selecting and examing the current thread's stack frames. Syntax: frame <subcommand> [<subcommand-options>] ...
expression
命令
(lldb) help expression Evaluate an expression on the current thread.Displays any returned value with LLDB's default formatting.Expects 'raw' input (see 'help raw-input'.) Syntax: expression <cmd-options> -- <expr> ...
操作教程
可以參考下面幾篇文章:
- Debugging Go Code with LLDB](http://ribrdb.github.io/lldb/ ): (中文翻譯 )
- 熟練使用 LLDB,讓你除錯事半功倍
- LLDB Tutorial
使用Delve除錯Go程式
可以參考下面幾篇文章:
不要使用偵錯程式
對於偵錯程式,一眾計算機大牛都給出了明確而且強烈的建議:不要使用偵錯程式。
- Linus Torvalds , the creator of Linux,does not use a debugger .
- Robert C. Martin , one of the inventors of agile programming, thinks that debuggers area wasteful timesink .
- John Graham-Cumming hates debuggers .
- Brian W. Kernighan andRob Pike wrote thatstepping through a program less productive than thinking harder and adding output statements and self-checking code at critical places . Kernighan once wrote thatthe most effective debugging tool is still careful thought, coupled with judiciously placed print statements .
- The author of Python,Guido van Rossum has been quoted as saying that uses print statements for 90% of his debugging.
除錯技術是一眾純手工的技術,誕生於計算機程式的規模還不是很大的時期。在當今軟體規模不斷擴充套件的情況下,除錯無法解決軟體質量問題。深入的思考、合理的架構、優美的程式碼、充分的單元測試才是提高軟體質量的正確方向。除錯應該僅作為調查問題最後一種辦法。