14 条件断点

目前,Golang支持的调试器有:

  • GDB:Golang最早支持的调试工具,对Golang专有特性缺少很好的支持
  • LLDB:macOS推荐的标准调试工具,对Golang专有特性缺少很好的支持
  • Delve:专门为Golang设计开发的调试工具,本身由Golang开发,对Windows平台也提供同样的支持

Golang中的条件断点需要使用delve来实现。

安装Delve

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

sugoi@sugoi:~$ dlv version
Delve Debugger
Version: 1.4.0
Build: $Id: 67422e6f7148fa1efa0eac1423ab5594b223d93b $

Delve介绍

sugoi@sugoi:~$ dlv help
Delve is a source level debugger for Go programs.

Delve enables you to interact with your program by controlling the execution of the process,
evaluating variables, and providing information of thread / goroutine state, CPU register state and more.

The goal of this tool is to provide a simple yet powerful interface for debugging Go programs.

Pass flags to the program you are debugging using `--`, for example:

`dlv exec ./hello -- server --config conf/config.toml`

Usage:
  dlv [command]

Available Commands:
  attach      Attach to running process and begin debugging.    可以用来对一个正在运行的进行进行调试.
  connect     Connect to a headless debug server.   连接到headless调试器.
  core        Examine a core dump.  用来调试core文件.
  debug       Compile and begin debugging main package in current directory, or the package specified.  在当前包或者指定的包编译并debug程序.
  exec        Execute a precompiled binary, and begin a debug session.  如果已经编译好了二进制,可以用该命令启动调试.
  help        Help about any command    帮助命令.
  run         Deprecated command. Use 'debug' instead.  
  test        Compile test binary and begin debugging program.     可以用来测试自己编写的测试源码文件.
  trace       Compile and begin tracing program.    编译并跟踪程序.
  version     Prints version.   输出版本信息

Flags:
      --accept-multiclient   Allows a headless server to accept multiple client connections.
      --api-version int      Selects API version when headless. (default 1)
      --backend string       Backend selection (see 'dlv help backend'). (default "default")
      --build-flags string   Build flags, to be passed to the compiler.
      --check-go-version     Checks that the version of Go in use is compatible with Delve. (default true)
      --headless             Run debug server only, in headless mode.
      --init string          Init file, executed by the terminal client.
  -l, --listen string        Debugging server listen address. (default "127.0.0.1:0")
      --log                  Enable debugging server logging.
      --log-dest string      Writes logs to the specified file or file descriptor (see 'dlv help log').
      --log-output string    Comma separated list of components that should produce debug output (see 'dlv help log')
      --only-same-user       Only connections from the same user that started this instance of Delve are allowed to connect. (default true)
      --wd string            Working directory for running the program. (default ".")

Additional help topics:
  dlv backend Help about the --backend flag.
  dlv log     Help about logging flags.

Use "dlv [command] --help" for more information about a command.

这里需要用到的是debug子命令。

dlv debug介绍

进入项目目录,输入dlv debug这是一个交互式的界面,输入help命令查看调试过程中可以执行的操作:

sugoi@sugoi:~/go/src/awesomeProject/learndelve$ dlv debug
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.

其实有缩写的都是比较常用的

常用的子命令包括:

命令缩写说明示例
breakb设置断点break [文件名:行数]
conditioncond设置断点的条件condition [断点名称/编号] [判断条件](如i==3
breakpointsbp显示已经设置的断点breakpoints
clear删除断点bp显示的断点有名称,clear [断点名称]
continuec让程序运行到下一个断点处continue
nextn单步执行next
steps进入某个函数内部,无法进入goroutine中step(在函数入口出执行)
stepoutso退出当前函数,回到进入点so
printp打印变量的值print
goroutinegr显示当前go协程或切换go协程goroutine [协程编号]
goroutinesgrs显示全部go协程goroutines
restartr重新运行上次设置的断点依然有效
args输出函数参数
locals输出函数局部变量

条件断点

主函数代码如下:

package main

import "fmt"

func main() {
 nums := make([]int, 5)
 for i := 0; i < len(nums); i++ {
  nums[i] = i * i
 }
 fmt.Println(nums)
}

Delve内部为panic()异常函数设置了断点。

(dlv) bp
Breakpoint runtime-fatal-throw at 0x433f20 for runtime.fatalthrow() /opt/go/src/runtime/panic.go:1158 (0)
Breakpoint unrecovered-panic at 0x433f90 for runtime.fatalpanic() /opt/go/src/runtime/panic.go:1185 (0)
        print runtime.curg._panic.arg

在主函数入口处设置断点,输入c运行代码。

(dlv) b main.main
Breakpoint 1 set at 0x4ad768 for main.main() ./main.go:5
(dlv) bp
Breakpoint runtime-fatal-throw at 0x433f20 for runtime.fatalthrow() /opt/go/src/runtime/panic.go:1158 (0)
Breakpoint unrecovered-panic at 0x433f90 for runtime.fatalpanic() /opt/go/src/runtime/panic.go:1185 (0)
        print runtime.curg._panic.arg
Breakpoint 1 at 0x4ad768 for main.main() ./main.go:5 (0)
(dlv) c
> main.main() ./main.go:5 (hits goroutine(1):1 total:1) (PC: 0x4ad768)
     1: package main
     2:
     3: import "fmt"
     4:
=>   5: func main() {
     6:         nums := make([]int, 5)
     7:         for i := 0; i < len(nums); i++ {
     8:                 nums[i] = i * i
     9:         }
    10:         fmt.Println(nums)
(dlv)

组合使用breakcondition来创建条件断点。

(dlv) b main.go:8
Breakpoint 2 set at 0x4ad7bd for main.main() ./main.go:7
(dlv) cond 2 i==3
(dlv) bp
Breakpoint runtime-fatal-throw at 0x433f20 for runtime.fatalthrow() /opt/go/src/runtime/panic.go:1158 (0)
Breakpoint unrecovered-panic at 0x433f90 for runtime.fatalpanic() /opt/go/src/runtime/panic.go:1185 (0)
        print runtime.curg._panic.arg
Breakpoint 1 at 0x4ad768 for main.main() ./main.go:5 (1)
Breakpoint 2 at 0x4ad7bd for main.main() ./main.go:8 (0)
        cond i == 3

使用continue运行到断点2,使用locals查看当前变量,条件断点生效。

(dlv) c
> main.main() ./main.go:8 (hits goroutine(1):1 total:1) (PC: 0x4ad7db)
     3: import "fmt"
     4:
     5: func main() {
     6:         nums := make([]int, 5)
     7:         for i := 0; i < len(nums); i++ {
=>   8:                 nums[i] = i * i
     9:         }
    10:         fmt.Println(nums)
    11: }
(dlv) locals
nums = []int len: 5, cap: 5, [...]
i = 3

查看其他信息包括协程额函数栈等。

(dlv) stack
0  0x00000000004ad7db in main.main
   at ./main.go:8
1  0x0000000000436438 in runtime.main
   at /opt/go/src/runtime/proc.go:203
2  0x0000000000463df1 in runtime.goexit
   at /opt/go/src/runtime/asm_amd64.s:1373
(dlv) goroutines
* Goroutine 1 - User: ./main.go:8 main.main (0x4ad7db) (thread 197086)
  Goroutine 2 - User: /opt/go/src/runtime/proc.go:305 runtime.gopark (0x4367eb)
  Goroutine 3 - User: /opt/go/src/runtime/proc.go:305 runtime.gopark (0x4367eb)
  Goroutine 4 - User: /opt/go/src/runtime/proc.go:305 runtime.gopark (0x4367eb)
  Goroutine 5 - User: /opt/go/src/runtime/proc.go:305 runtime.gopark (0x4367eb)
[5 goroutines]
(dlv) goroutine
Thread 197086 at ./main.go:8
Goroutine 1:
        Runtime: ./main.go:8 main.main (0x4ad7db)
        User: ./main.go:8 main.main (0x4ad7db)
        Go: /opt/go/src/runtime/asm_amd64.s:220 runtime.rt0_go (0x461d64)
        Start: /opt/go/src/runtime/proc.go:113 runtime.main (0x436270)
(dlv)quit
上次修改: 2 July 2020