參考https://github.com/go-delve/delvegit
安裝github
首先你必須有等於或高於1.8版本的Go,個人版本是:編程
userdeMBP:go-learning user$ go version go version go1.11.4 darwin/amd64
我是用的是Mac,因此使用的是OSX安裝方法:後端
而後使用go get 進行安裝:api
go get -u github.com/go-delve/delve/cmd/dlv
使用這種方法,你將沒法使用delve的本機後端,但不管如何您都不須要它:macOS上的本機後端已經知道該操做系統最近出現的問題,而且目前沒有進行維護。詳情可見https://github.com/go-delve/delve/issues/1112sass
安裝完後查看版本:服務器
userdeMBP:go-learning user$ dlv version Delve Debugger Version: 1.2.0 Build: $Id: 068e...57e34f0f08ce01466 $
使用:less
1)首先先使用dlv或dlv --help來查看delve支持的命令:函數
userdeMBP:go-learning user$ dlv Delve是Go程序的源代碼級調試器. Delve經過控制進程的執行、評估變量以及提供線程/ goroutine狀態、CPU寄存器狀態等信息,使你可以與程序進行交互。 這個工具的目標是爲調試Go程序提供一個簡單而強大的接口. 使用「--」將標誌傳遞給正在調試的程序,例如: `dlv exec ./hello -- server --config conf/config.toml` Usage: dlv [command] Available Commands: attach 鏈接到正在運行的流程並開始調試. connect 鏈接到無頭調試服務器. core 檢查覈心轉儲. debug 編譯並開始調試當前目錄下的主包或指定的包. exec 執行預編譯的二進制文件,並開始調試會話. help 幫助信息 run 棄用的命令。使用「debug」替代它. test 編譯測試二進制文件並開始調試程序. trace 編譯並開始跟蹤程序. version 打印版本. Flags: --accept-multiclient 容許無頭服務器接受多個客戶機鏈接。注意,服務器API不是可重入的,客戶端必須協調. --api-version int 無頭時選擇API版本. (default 1) --backend string 後端選擇: default 在macOS上使用lldb,其餘地方都是本地的. native 本地後端. lldb 使用lldb-server或debugserver. rr 使用mozilla rr (https://github.com/mozilla/rr). (default "default") 默認使用的是default --build-flags string 生成標誌,以傳遞給編譯器. --headless 僅在headless模式下運行調試服務器. --init string 初始化文件,由終端客戶端執行. -l, --listen string 調試服務器監聽地址. (default "localhost:0") --log 啓用調試服務器日誌記錄. --log-output string 應該產生調試輸出的組件的逗號分隔列表,可能的值爲: debugger 記錄調試器命令 gdbwire 日誌鏈接到gdbserial後端 lldbout 將輸出從debugserver/lldb複製到標準輸出 debuglineerr 讀取.debug_line時日誌可恢復錯誤 rpc 記錄全部RPC消息 fncall 日誌函數調用協議 minidump 日誌minidump加載 使用--log啓用日誌時,默認爲「debugger」. --wd string 用於運行程序的工做目錄. (default ".") 使用"dlv [command] --help"獲取有關命令的詳細信息.
支持的命令太多了,在這裏咱們主要介紹它的調試命令——debug工具
2.dlv debug
首先使用dlv debug --help 查看其的幫助信息:
userdeMBP:go-learning user$ dlv debug --help 編譯禁用優化的程序,啓動並附加到該程序。 默認狀況下,沒有參數,Delve將編譯當前目錄中的「main」包,並開始調試它。或者,您能夠指定一個包名,Delve將編譯該包,並開始一個新的調試會話。 Usage: dlv debug [package] [flags] Flags: --output string 二進制文件的輸出路徑. (default "debug") Global Flags:和上面的同樣,這裏省略
舉例說明:
首先要進行調試的代碼爲:
package main import ( "fmt" "time" ) func counting(c chan<- int){ for i := 0; i < 10; i++{ time.Sleep(2 * time.Second) c <- i } close(c) } func main() { msg := "Starting main" fmt.Println(msg) bus := make(chan int) msg = "starting a gofunc" go counting(bus) for count := range bus{ fmt.Println("count : ", count) } }
而後開啓調試:
userdeMBP:go-learning user$ dlv debug test.go Type 'help' for list of commands. (dlv)
而後咱們能夠輸入help來查看可以使用的debug命令有哪些:
The following commands are available: args ------------------------ 打印函數參數. break (alias: b) ------------ 設置斷點. breakpoints (alias: bp) ----- 輸出活動斷點的信息. call ------------------------ 恢復進程,注入一個函數調用(還在實驗階段!!) clear ----------------------- 刪除斷點. clearall -------------------- 刪除多個斷點. condition (alias: cond) ----- 設置斷點條件. config ---------------------- 修改配置參數. continue (alias: c) --------- 運行到斷點或程序終止. deferred -------------------- 在延遲調用的上下文中執行命令. disassemble (alias: disass) - 反彙編程序. down ------------------------ 將當前幀向下移動. edit (alias: ed) ------------ 在$DELVE_EDITOR或$EDITOR中打開你所在的位置 exit (alias: quit | q) ------ 退出調試器. frame ----------------------- 設置當前幀,或在不一樣的幀上執行命令. funcs ----------------------- 打印函數列表. goroutine ------------------- 顯示或更改當前goroutine goroutines ------------------ 列舉程序goroutines. help (alias: h) ------------- 打印幫助信息. list (alias: ls | l) -------- 顯示源代碼. locals ---------------------- 打印局部變量. next (alias: n) ------------- 轉到下一個源行. on -------------------------- 在命中斷點時執行命令. print (alias: p) ------------ 計算一個表達式. regs ------------------------ 打印CPU寄存器的內容. restart (alias: r) ---------- 重啓進程. set ------------------------- 更改變量的值. source ---------------------- 執行包含delve命令列表的文件 sources --------------------- 打印源文件列表. stack (alias: bt) ----------- 打印堆棧跟蹤信息. step (alias: s) ------------- 單步執行程序. step-instruction (alias: si) 單步執行一條cpu指令. stepout --------------------- 跳出當前函數. thread (alias: tr) ---------- 切換到指定的線程. threads --------------------- 打印每一個跟蹤線程的信息. trace (alias: t) ------------ 設置跟蹤點. types ----------------------- 打印類型列表 up -------------------------- 向上移動當前幀. vars ------------------------ 打印包變量. whatis ---------------------- 打印表達式的類型. 在命令前鍵入help來得到命令的完整文檔,如help goroutine
2)首先開始進行調試,這裏先什麼都不作直接輸入continue,即c,運行到斷點或程序終止
(dlv) c Starting main count : 0 count : 1 count : 2 count : 3 count : 4 count : 5 count : 6 count : 7 count : 8 count : 9 Process 1324 has exited with status 0
可見這個代碼跑了起來
3)restart(縮寫r)重啓進程
(dlv) b main.main Process 1324 has exited with status 0
上面進行添加斷點的操做,可是並無返回想要的信息
這是由於咱們以前已經將程序運行結束了,這時候不能直接添加斷點信息
因此要先調用restart來將進程重啓
4)break(即b)添加斷點
如今咱們能夠爲其添加斷點了
(dlv) b main.main //在main函數處添加斷點 Breakpoint 1 set at 0x10b123b for main.main() ./test.go:16 (dlv) b main.counting //在counting函數處添加斷點 Breakpoint 2 set at 0x10b118f for main.counting() ./test.go:8
咱們也可使用"文件:行號"的格式來添加斷點,如:
b /Users/user/go-learning/test.go:8 //等價於在函數counting處添加斷點
而後繼續程序運行,可見會先停在main函數處:
(dlv) c > main.main() ./test.go:16 (hits goroutine(1):1 total:1) (PC: 0x10b123b) 11: c <- i 12: } 13: close(c) 14: } 15: => 16: func main() { 17: msg := "Starting main" 18: fmt.Println(msg) 19: bus := make(chan int) 20: msg = "starting a gofunc" 21: go counting(bus)
5)breakpoints(縮寫bp)輸出活動斷點信息
(dlv) bp Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:654 (0) Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:681 (0) print runtime.curg._panic.arg Breakpoint 1 at 0x10b123b for main.main() ./test.go:16 (1) Breakpoint 2 at 0x10b118f for main.counting() ./test.go:8 (0)
可見上面的兩個設置的斷點
6)list 顯示從中止行起先後5行的源代碼
(dlv) list > main.main() ./test.go:16 (hits goroutine(1):1 total:1) (PC: 0x10b123b) 11: c <- i 12: } 13: close(c) 14: } 15: => 16: func main() { 17: msg := "Starting main" 18: fmt.Println(msg) 19: bus := make(chan int) 20: msg = "starting a gofunc" 21: go counting(bus)
7)next (縮寫n)轉到下一個源行
可見如今的指針從16行轉到了17行
若是想繼續向下,能夠直接回車,Delve會默認重複上一條命令
(dlv) n > main.main() ./test.go:17 (PC: 0x10b1252) 12: } 13: close(c) 14: } 15: 16: func main() { => 17: msg := "Starting main" 18: fmt.Println(msg) 19: bus := make(chan int) 20: msg = "starting a gofunc" 21: go counting(bus) 22: for count := range bus{
8)print(縮寫p)計算一個表達式
在這裏用來打印一個變量的值,舉例看下面
9)locals 打印局部變量
10)step(縮寫s)單步執行程序
(dlv) p msg "H�\a\x00�\x00\x00\x00��\x05\x01\x00\x00\x00\x00\x00\x03\x00\x00�\x00\x00\x00��\a\x00�\x00\x00\x00�Q\x00\x01\x00\x00\x00\x00X�\x06\x00�\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00�\x0f\v\x01\x00\x00\x00\x00...+16825344 more" (dlv) locals msg = "H�\a\x00�\x00\x00\x00��\x05\x01\x00\x00\x00\x00\x00\x03\x00\x00�\x00\x00\x00��\a\x00�\x00\x00\x00�Q\x00\x01\x00\x00\x00\x00X�\x06\x00�\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00�\x0f\v\x01\x00\x00\x00\x00...+16825344 more" (dlv) p count Command failed: could not find symbol value for count (dlv) s > main.main() ./test.go:18 (PC: 0x10b1267) 13: close(c) 14: } 15: 16: func main() { 17: msg := "Starting main" => 18: fmt.Println(msg) 19: bus := make(chan int) 20: msg = "starting a gofunc" 21: go counting(bus) 22: for count := range bus{ 23: fmt.Println("count : ", count) (dlv) p msg "Starting main" (dlv) locals msg = "Starting main"
在執行s前,使用p打印了msg局部變量的值,locals打印了局部變量的值,由於尚未運行到給msg複製的代碼,因此返回的結果不是指望的值
而後當咱們運行s後,可見指針也從17變成了18,而後這時候再運行p和locals,可見返回了指望的值
print還能夠用來判斷一個表達式:
(dlv) p msg == "Starting main" true
11)whatis打印表達式類型
(dlv) whatis msg string
可以獲得msg變量爲string類型
而後咱們接着運行到下一個斷點,這時候開啓了一個groutine
(dlv) c Starting main > main.counting() ./test.go:8 (hits goroutine(18):1 total:1) (PC: 0x10b118f) 3: import ( 4: "fmt" 5: "time" 6: ) 7: => 8: func counting(c chan<- int){ 9: for i := 0; i < 10; i++{ 10: time.Sleep(2 * time.Second) 11: c <- i 12: } 13: close(c)
12)goroutine 顯示或更改當前goroutine
(dlv) help goroutine goroutine goroutine <id> goroutine <id> <command> 調用時不帶參數,它將顯示關於當前goroutine的信息。 使用單個參數調用時,它將切換到指定的goroutine。 使用更多參數調用時,它將在指定的goroutine上執行命令。
可見如今正在運行的goroutine
(dlv) goroutine Thread 77795 at ./test.go:8 Goroutine 18: Runtime: ./test.go:8 main.counting (0x10b118f) User: ./test.go:8 main.counting (0x10b118f) Go: ./test.go:21 main.main (0x10b138a) Start: ./test.go:8 main.counting (0x10b1180)
指定查看的是goroutine 1 :
(dlv) goroutine 1 Switched from 18 to 1 (thread 77795) (dlv) goroutine Thread 77795 at ./test.go:8 Goroutine 1: Runtime: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:303 runtime.gopark (0x102d0f4) User: ./test.go:22 main.main (0x10b13ae) Go: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/asm_amd64.s:201 runtime.rt0_go (0x10557fb) Start: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:110 runtime.main (0x102cb60)
這時候若是運行locals,獲得的是goroutine 1所在處的局部變量:
(dlv) locals gp = (*runtime.g)(0xc000000300) mp = (*runtime.m)(0x11904c0) status = (unreadable read out of bounds)
要回去原來的goroutine就調用:
(dlv) goroutine 18 Switched from 1 to 18 (thread 77795)
13)goroutines 列舉程序goroutines
(dlv) help goroutines 列舉程序goroutines. goroutines [-u (default: user location)|-r (runtime location)|-g (go statement location)|-s (start location)] [ -t (stack trace)] Print out info for every goroutine. The flag controls what information is shown along with each goroutine: -u 顯示用戶代碼中最頂層堆棧幀的位置 -r 顯示最頂層stackframe的位置(包括私有運行時函數中的幀) -g 顯示建立goroutine的go指令的位置 -s 顯示起始函數的位置 -t 顯示goroutine的堆棧跟蹤 若是沒有指定具體的標誌,則默認使用-u.
(dlv) goroutines Goroutine 1 - User: ./test.go:22 main.main (0x10b13ae) Goroutine 2 - User: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:303 runtime.gopark (0x102d0f4) Goroutine 3 - User: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:303 runtime.gopark (0x102d0f4) Goroutine 17 - User: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:303 runtime.gopark (0x102d0f4) * Goroutine 18 - User: ./test.go:8 main.counting (0x10b118f) (thread 77795) [5 goroutines] (dlv) goroutines -s Goroutine 1 - Start: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:110 runtime.main (0x102cb60) Goroutine 2 - Start: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:243 runtime.forcegchelper (0x102ced0) Goroutine 3 - Start: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/mgcsweep.go:46 runtime.bgsweep (0x1020170) Goroutine 17 - Start: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/mfinal.go:161 runtime.runfinq (0x1017ae0) * Goroutine 18 - Start: ./test.go:8 main.counting (0x10b1180) (thread 77795) [5 goroutines]
14)args 打印函數參數
獲得counting函數的參數值:
(dlv) args c = chan<- int 0/0
由於以前只設置了兩個斷點,所以再次調用c則又將運行程序完畢:
(dlv) c count : 0 count : 1
...
15)stepout 跳出當前函數
(dlv) restart Process restarted with PID 1352 (dlv) b main.main //以前的斷點一直存在,即便程序結束,所以此時的counting函數中也有斷點 Command failed: Breakpoint exists at /Users/user/go-learning/test.go:16 at 10b123b (dlv) c > main.main() ./test.go:16 (hits goroutine(1):1 total:1) (PC: 0x10b123b) 11: c <- i 12: } 13: close(c) 14: } 15: => 16: func main() { 17: msg := "Starting main" 18: fmt.Println(msg) 19: bus := make(chan int) 20: msg = "starting a gofunc" 21: go counting(bus) (dlv) stepout Starting main > main.counting() ./test.go:8 (hits goroutine(5):1 total:1) (PC: 0x10b118f) breakpoint hit during stepout, continuing... count : 0 count : 1 count : 2 ...
由於stepout跳過的是main()函數,可見直接也跳過了counting的斷點,而後執行程序到結束
16)clear刪除斷點\clearall刪除多個斷點
由於若是不手動清除斷點,即便程序結束並restart程序,斷點信息也仍然在
(dlv) breakpoints //查看當前的斷點信息 Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:654 (0) Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:681 (0) print runtime.curg._panic.arg Breakpoint 1 at 0x10b123b for main.main() ./test.go:16 (1) Breakpoint 2 at 0x10b118f for main.counting() ./test.go:8 (0) (dlv) clear 1 //先刪除第一個斷點,即main函數上的斷點 Breakpoint 1 cleared at 0x10b123b for main.main() ./test.go:16 (dlv) breakpoints //查看可見果真成功刪除 Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:654 (0) Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:681 (0) print runtime.curg._panic.arg Breakpoint 2 at 0x10b118f for main.counting() ./test.go:8 (0) (dlv) clearall //而後將全部斷點都刪除 Breakpoint 2 cleared at 0x10b118f for main.counting() ./test.go:8 (dlv) breakpoints //查看可見斷點信息爲空 Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:654 (0) Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:681 (0) print runtime.curg._panic.arg (dlv)
17)on 在命中斷點時執行命令
(dlv) help on Executes a command when a breakpoint is hit. on <breakpoint name or id> <command>. Supported commands: print, stack and goroutine)
(dlv) b /Users/user/go-learning/test.go:21 //添加斷點 Breakpoint 2 set at 0x10b1368 for main.main() ./test.go:21 (dlv) on 2 print msg == "starting a gofunc" //設置當到達該斷點後運行的命令,即判斷msg的值是否爲"starting a gofunc" (dlv) breakpoints //查看此時的斷點信息 Breakpoint runtime-fatal-throw at 0x102b3e0 for runtime.fatalthrow() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:654 (0) Breakpoint unrecovered-panic at 0x102b450 for runtime.fatalpanic() /usr/local/Cellar/go/1.11.4/libexec/src/runtime/panic.go:681 (0) print runtime.curg._panic.arg Breakpoint 2 at 0x10b1368 for main.main() ./test.go:21 (0) print msg == "starting a gofunc" (dlv) c Starting main > main.main() ./test.go:21 (hits goroutine(1):1 total:1) (PC: 0x10b1368) msg == "starting a gofunc": true //可見打印出來了,並返回值爲true 16: func main() { 17: msg := "Starting main" 18: fmt.Println(msg) 19: bus := make(chan int) 20: msg = "starting a gofunc" => 21: go counting(bus) 22: for count := range bus{ 23: fmt.Println("count : ", count) 24: } 25: }
18)set 更改變量的值
(dlv) help set Changes the value of a variable. [goroutine <n>] [frame <m>] set <variable> = <value>
⚠️Only numerical variables and pointers can be changed.
所以更改string類型變量將報錯:
(dlv) set msg = "change msg" Command failed: can not set variables of type string (not implemented)
19)up向上移動當前幀/dowm向下移動當前幀
(dlv) up > main.main() ./test.go:21 (hits goroutine(1):1 total:1) (PC: 0x10b1368) Frame 1: /usr/local/Cellar/go/1.11.4/libexec/src/runtime/proc.go:201 (PC: 102cd25) 196: // A program compiled with -buildmode=c-archive or c-shared 197: // has a main, but it is not executed. 198: return 199: } 200: fn = main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime => 201: fn() 202: if raceenabled { 203: racefini() 204: } 205: 206: // Make racy client program work: if panicking on (dlv) down > main.main() ./test.go:21 (hits goroutine(1):1 total:1) (PC: 0x10b1368) Frame 0: ./test.go:21 (PC: 10b1368) 16: func main() { 17: msg := "Starting main" 18: fmt.Println(msg) 19: bus := make(chan int) 20: msg = "starting a gofunc" => 21: go counting(bus) 22: for count := range bus{ 23: fmt.Println("count : ", count) 24: } 25: }
20)exit (alias: quit | q)退出調試器
(dlv) exit
userdeMBP:go-learning user$
3.dlv attach
若是項目正在運行中,想要對正在運行的項目進行調試
1)首先使用"ps aux | grep 編譯文件名"來查看運行程序的進程號pid
2)而後就可以使用"dlv attach pid"來鏈接該運行程序,而後就能使用以前debug中的命令來進行調試了
其餘的命令以後若是用到了再添加