Go debugging tool: gdb vs dlv

Keywords: Go yum ftp github

Although the GoLand editor is very powerful, it is still relatively weak in displaying memory and stack information. Maybe my posture is wrong. So, I started to debug the gdb, but the GDB stepped into the pit and did not solve it, which triggered the comparison between GDB and dlv.

gdb

install

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

Enter debugging

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

Use

  1. Start Debugger (gdb)

    [lday@alex GoDbg]$ gdb ./GoDbg
  2. Setting breakpoints on main function (b)

    (gdb) b main.main
    Breakpoint 1 at 0x401000: file /home/lday/Works/Go_Works/GoLocalWorks/src/GoWorks/GoDbg/main.go, line 9.
  3. Start program with parameters (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. Set breakpoints on the file dbgTest.go by line number (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. View breakpoint settings (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. Disable breakpoints (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. Delete breakpoints (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. Continue execution after breakpoint (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. Display code (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. Single step execution (n)

    (gdb) n
    DBGTestRun Begin!
    
    18        waiter := &sync.WaitGroup{}
  11. Print variable information (print/p)
    Set a breakpoint (b dbgTest.go:16) where you enter the DBGTestRun. After entering the function, display the corresponding variables through the p command:

    (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.

    From the above output, we can see a very strange thing. Although DBGTestRun has four parameters passed in, it seems that var3 and var4 gdb can not be recognized. In the subsequent experiments on dlv, we found that DLV can recognize var3 and var4.

  12. View the call stack (bt), switch the call stack (f n), and display the current stack variable information

    (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 (Recommendation)

install

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

Enter debugging

dlv debug main.go

Use

    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.

summary

  1. dlv supports goroutine better. I did not find a way to debug goroutine using gdb. Maybe the posture is wrong.
  2. gdb cannot debug locally referenced variables, dlv does not

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

    As shown in the above code, when gdb prints variable j with pj, dlv can

  3. dlv can't debug some internal structures of Go such as interface. gdb is possible

Reference resources

Introduction to Golang Program Debugging Tools (gdb vs dlv)

Posted by el_quijote on Thu, 15 Aug 2019 06:45:26 -0700