Delve 是一款很不錯的 Golang 調試工具,可以實現類似 Visual Studio 的斷點調試功能,也可以用來在程序 Crash 的時候生成 Coredump 文件,此外 Delve 也適合用於調試 Web Server。
Delve 項目
安裝 Delve
go get -u github.com/go-delve/delve/cmd/dlv
Delve 常用命令
命令 | 功能 |
---|---|
dlv attach | 後面跟 pid,用來 Debug 編譯好的 Golang 程序 |
dlv core | 用於 coredump |
dlv debug | 後面跟要調試的 go 文件,進入 Debug |
dlv test | Debug test 函數 |
編寫用於 Debug 的 Golang Web Server
main.go, 寫一個 Hello World 的 Web Server
package main
import (
"net/http"
)
const endpoint = ":8000"
type helloHandler struct{}
func (h *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, world!"))
}
func main() {
http.Handle("/", &helloHandler{})
http.ListenAndServe(endpoint, nil)
}
進行斷點調試
➜ test dlv debug ./main.go
Type 'help' for list of commands.
(dlv) help
The following commands are available:
args ------------------------ Print function arguments.
break (alias: b) ------------ Sets a breakpoint.
breakpoints (alias: bp) ----- Print out info for active breakpoints.
call ------------------------ Resumes process, injecting a function call (EXPERIMENTAL!!!)
clear ----------------------- Deletes breakpoint.
clearall -------------------- Deletes multiple breakpoints.
condition (alias: cond) ----- Set breakpoint condition.
config ---------------------- Changes configuration parameters.
continue (alias: c) --------- Run until breakpoint or program termination.
deferred -------------------- Executes command in the context of a deferred call.
disassemble (alias: disass) - Disassembler.
down ------------------------ Move the current frame down.
edit (alias: ed) ------------ Open where you are in $DELVE_EDITOR or $EDITOR
exit (alias: quit | q) ------ Exit the debugger.
frame ----------------------- Set the current frame, or execute command on a different frame.
funcs ----------------------- Print list of functions.
goroutine (alias: gr) ------- Shows or changes current goroutine
goroutines (alias: grs) ----- List program goroutines.
help (alias: h) ------------- Prints the help message.
libraries ------------------- List loaded dynamic libraries
list (alias: ls | l) -------- Show source code.
locals ---------------------- Print local variables.
next (alias: n) ------------- Step over to next source line.
on -------------------------- Executes a command when a breakpoint is hit.
print (alias: p) ------------ Evaluate an expression.
regs ------------------------ Print contents of CPU registers.
restart (alias: r) ---------- Restart process.
set ------------------------- Changes the value of a variable.
source ---------------------- Executes a file containing a list of delve commands
sources --------------------- Print list of source files.
stack (alias: bt) ----------- Print stack trace.
step (alias: s) ------------- Single step through program.
step-instruction (alias: si) Single step a single cpu instruction.
stepout (alias: so) --------- Step out of the current function.
thread (alias: tr) ---------- Switch to the specified thread.
threads --------------------- Print out info for every traced thread.
trace (alias: t) ------------ Set tracepoint.
types ----------------------- Print list of types
up -------------------------- Move the current frame up.
vars ------------------------ Print package variables.
whatis ---------------------- Prints type of an expression.
Type help followed by a command for full documentation.
在這個交換環境下可以進行斷點調試,支持的命令主要有:
命令 | alias | 功能 |
---|---|---|
break | b | 設置斷點 |
continue | c | 運行至斷點 |
p | 打印變量 | |
funcs | 顯示支持的函數 |
查看包名/函數名包含 main 的函數
funcs main
(dlv) funcs main
crypto/x509.domainToReverseLabels
crypto/x509.matchDomainConstraint
internal/x/net/http/httpproxy.(*domainMatch).match
internal/x/net/http/httpproxy.domainMatch.match
main.(*helloHandler).ServeHTTP
main.init
main.main
net.absDomainName
net.isDomainName
net/http.(*body).bodyRemains
net/http.requestBodyRemains
runtime.main
runtime.main.func1
runtime.main.func2
type..eq.internal/x/net/http/httpproxy.domainMatch
type..hash.internal/x/net/http/httpproxy.domainMatch
funcs [param] 默認是返回包含 param 的func,可以看到 main package 包含3個 func
main.(*helloHandler).ServeHTTP
main.init
main.main
設置斷點
break [name] <linespec>
-
在 main 處設置一個斷點,break 的參數可以是 funcs 查看等到的 func name
break main.main
(dlv) break main.main Breakpoint 1 set at 0x1331513 for main.main() ./main.go:15
-
設置斷點的另外一種方式
<filename>:<line>
(dlv) break ./main.go:15 Breakpoint 1 set at 0x1331513 for main.main() ./main.go:15
-
linespec 支持的完整形式
linespec說明*<address> Specifies the location of memory address address. address can be specified as a decimal, hexadecimal or octal number <filename>:<line> Specifies the line line in filename. filename can be the partial path to a file or even just the base name as long as the expression remains unambiguous. <line> Specifies the line line in the current file +<offset> Specifies the line offset lines after the current one -<offset> Specifies the line offset lines before the current one <function>[:<line>] Specifies the line line inside function. The full syntax for function is <package>.(*<receiver type>).<function name> however the only required element is the function name, everything else can be omitted as long as the expression remains unambiguous. For setting a breakpoint on an init function (ex: main.init), the <filename>:<line> syntax should be used to break in the correct init function at the correct location. /<regex>/ Specifies the location of all the functions matching regex
運行至斷點
continue 命令運行至斷點
continue
(dlv) continue
> main.main() ./main.go:15 (hits goroutine(1):1 total:1) (PC: 0x1331513)
10:
11: func (h *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
12: w.Write([]byte("Hello, world!"))
13: }
14:
=> 15: func main() {
16: http.Handle("/", &helloHandler{})
17: http.ListenAndServe(endpoint, nil)
18: }
打印變量
-
可以繼續設置斷點至 ServeHTTP 函數,並執行 continue 開啓 Web Server 服務
(dlv) break main.(*helloHandler).ServeHTTP Breakpoint 2 set at 0x1331443 for main.(*helloHandler).ServeHTTP() ./main.go:11 (dlv) c
-
訪問 Web Server
這時候 Web Server 的 Delve 需要輸入➜ curl http://localhost:8000 Hello, world!%
-
在執行至斷點 ServeHTTP 時,可輸入 print 查詢變量
- 輸入 n 回車,單步執行,
- 輸入 args 打印出所有的方法參數信息
- 輸入 locals 打印所有的本地變量
(dlv) c > main.(*helloHandler).ServeHTTP() ./main.go:11 (hits goroutine(26):1 total:13) (PC: 0x1331443) 6: 7: const endpoint = ":8000" 8: 9: type helloHandler struct{} 10: => 11: func (h *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 12: w.Write([]byte("Hello, world!")) 13: } 14: 15: func main() { 16: http.Handle("/", &helloHandler{}) (dlv) n > main.(*helloHandler).ServeHTTP() ./main.go:12 (PC: 0x1331451) 7: const endpoint = ":8000" 8: 9: type helloHandler struct{} 10: 11: func (h *helloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { => 12: w.Write([]byte("Hello, world!")) 13: } 14: 15: func main() { 16: http.Handle("/", &helloHandler{}) 17: http.ListenAndServe(endpoint, nil) (dlv) args h = (*main.helloHandler)(0x16485d0) w = net/http.ResponseWriter(*net/http.response) 0xc00010b998 r = ("*net/http.Request")(0xc0001c0200) (dlv) locals (no locals) (dlv) print r.Method "GET" (dlv)