Go的調試工具:gdb vs dlv

GoLand編輯器雖然很強大,但是在展示內存及堆棧信息這一塊還是比較的弱,有可能是我的姿勢不對,所以,開始切入了gdb調試,但是gdb踩到了坑,並沒有解決,也就引發了gdb與dlv的對比了

gdb

安裝

yum install ncures-devel
wget http://ftp.gnu.org/gnu/gdb/gdb-8.2.tar.gz
tar zxf gdb-8.2.tar.gz
cd gdb-8.2
make && make install

進入調試

go build -gcflags '-N -l' main.go
gdb main

使用

  1. 啓動調試程序(gdb

    [lday@alex GoDbg]$ gdb ./GoDbg
  2. 在main函數上設置斷點(b

    (gdb) b main.main
    Breakpoint 1 at 0x401000: file /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/main.go, line 9.
  3. 帶參數啓動程序(r

    (gdb) r arg1 arg2
    Starting program: /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/GoDbg arg1 arg2
    [New LWP 8412]
    [New LWP 8413]
    [New LWP 8414]
    [New LWP 8415]
    
    Breakpoint 1, main.main () at /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/main.go:9
    9    func main() {
  4. 在文件dbgTest.go上通過行號設置斷點(b

    (gdb) b dbgTest.go:16
    Breakpoint 3 at 0x457960: file /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/mylib/dbgTest.go, line 16.
  5. 查看斷點設置情況(info b

    (gdb) info b
    Num     Type           Disp Enb Address            What
    1       breakpoint     keep y   0x0000000000401000 in main.main 
                                                       at /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/main.go:9
        breakpoint already hit 1 time
    2       breakpoint     keep y   0x0000000000401000 in main.main 
                                                       at /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/main.go:9
        breakpoint already hit 1 time
    3       breakpoint     keep y   0x0000000000457960 in GoWorks/GoDbg/mylib.DBGTestRun 
                                                       at /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/mylib/dbgTest.go:16
  6. 禁用斷點(dis n

    (gdb) dis 1   
    (gdb) info b
    Num     Type           Disp Enb Address            What
    1       breakpoint     keep n   0x0000000000401000 in main.main 
                                                       at /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/main.go:9
        breakpoint already hit 1 time
    2       breakpoint     keep y   0x0000000000401000 in main.main 
                                                       at /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/main.go:9
        breakpoint already hit 1 time
    3       breakpoint     keep y   0x0000000000457960 in GoWorks/GoDbg/mylib.DBGTestRun 
                                                       at /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/mylib/dbgTest.go:16
  7. 刪除斷點(del n

    (gdb) del 1
    (gdb) info b
    Num     Type           Disp Enb Address            What
    2       breakpoint     keep y   0x0000000000401000 in main.main 
                                                       at /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/main.go:9
        breakpoint already hit 1 time
    3       breakpoint     keep y   0x0000000000457960 in GoWorks/GoDbg/mylib.DBGTestRun 
                                                       at /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/mylib/dbgTest.go:16
  8. 斷點後繼續執行(c

    (gdb) c
    Continuing.
    Golang dbg test...
    argc:3
    argv:[/home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/GoDbg arg1 arg2]
    
    Breakpoint 3, GoWorks/GoDbg/mylib.DBGTestRun (var1=1, var2="golang dbg test")
        at /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/mylib/dbgTest.go:16
    16    func DBGTestRun(var1 int, var2 string, var3 []int, var4 MyStruct) {
    (gdb) 
  9. 顯示代碼(l

    (gdb) l
    11        B string
    12        C map[int]string
    13        D []string
    14    }
    15    
    16    func DBGTestRun(var1 int, var2 string, var3 []int, var4 MyStruct) {
    17        fmt.Println("DBGTestRun Begin!\n")
    18        waiter := &sync.WaitGroup{}
    19    
    20        waiter.Add(1)
  10. 單步執行(n

    (gdb) n
    DBGTestRun Begin!
    
    18        waiter := &sync.WaitGroup{}
  11. 打印變量信息(print/p
    在進入DBGTestRun的地方設置斷點(b dbgTest.go:16),進入該函數後,通過p命令顯示對應變量:

    (gdb) l 17
    12        C map[int]string
    13        D []string
    14    }
    15    
    16    func DBGTestRun(var1 int, var2 string, var3 []int, var4 MyStruct) {
    17        fmt.Println("DBGTestRun Begin!\n")
    18        waiter := &sync.WaitGroup{}
    19    
    20        waiter.Add(1)
    21        go RunFunc1(var1, waiter)
    (gdb) p var1 
    $3 = 1
    (gdb) p var2
    $4 = "golang dbg test"
    (gdb) p var3
    No symbol "var3" in current context.

    從上面的輸出我們可以看到一個很奇怪的事情,雖然DBGTestRun有4個參數傳入,但是,似乎var3和var4 gdb無法識別,在後續對dlv的實驗操作中,我們發現,dlv能夠識別var3, var4.

  12. 查看調用棧(bt),切換調用棧(f n),顯示當前棧變量信息

    (gdb) bt
    #0  GoWorks/GoDbg/mylib.DBGTestRun (var1=1, var2="golang dbg test")
        at /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/mylib/dbgTest.go:17
    #1  0x00000000004018c2 in main.main () at /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/main.go:27
    (gdb) f 1
    #1  0x00000000004018c2 in main.main () at /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/main.go:27
    27        mylib.DBGTestRun(var1, var2, var3, var4)
    (gdb) l
    22        var4.A = 1
    23        var4.B = "golang dbg my struct field B"
    24        var4.C = map[int]string{1: "value1", 2: "value2", 3: "value3"}
    25        var4.D = []string{"D1", "D2", "D3"}
    26    
    27        mylib.DBGTestRun(var1, var2, var3, var4)
    28        fmt.Println("Golang dbg test over")
    29    }
    (gdb) print var1 
    $5 = 1
    (gdb) print var2
    $6 = "golang dbg test"
    (gdb) print var3
    $7 =  []int = {1, 2, 3}
    
    (gdb) print var4
    $8 = {A = 1, B = "golang dbg my struct field B", C = map[int]string = {[1] = "value1", [2] = "value2", [3] = "value3"}, 
    D =  []string = {"D1", "D2", "D3"}}

dlv(推薦)

安裝

go get -u github.com/go-delve/delve/cmd/dlv

進入調試

dlv debug main.go

使用

    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 ------------------- Shows or changes current goroutine
    goroutines ------------------ 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 --------------------- 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.

總結

  1. dlv對goroutine的支持更好,我使用gdb的沒有找到goroutine的調試方法,可能姿勢不對
  2. gdb對於局部引用變量無法調試,dlv不會

    func main() {
      i := 10
      j := &i
    }

    如上代碼,gdb 使用 p j 打印變量j的時候報錯,dlv卻可以

  3. dlv無法調試interface等Go內部實現的一些結構,gdb是可以的

參考

《Golang程序調試工具介紹(gdb vs dlv)》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章