Go調試工具—— Delve

參考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中的命令來進行調試了

 

其餘的命令以後若是用到了再添加

相關文章
相關標籤/搜索