祝大家国庆节快乐。
说到调试器,首先让人想到大名鼎鼎的GDB,在mac上对应的有lldb,dlv是针对Go语言单独开发的调试利器,而且dlv也是用go语言开发的。在windows平台也可以同样运行。今天我们简单介绍一下dlv如何调试go程序的。
首先我们创建一个main.go文件,main函数先通过一个for循环来初始化一个切片,再打印出切片的内容:
package main
import "fmt"
// dlv 调试器
func main() {
nums := make([]int, 5)
for i := 0; i < len(nums); i++ {
nums[i] = i * i
}
fmt.Println(nums)
}
在命令行输入 dlv debug 开始进行调试。
➜ Delve git:(master) ✗ dlv debug
Type 'help' for list of commands.
(dlv)
如果不知道如何使用,有哪些命令,可以输入help查看命令列表:
(dlv) help
The following commands are available:
Running the program:
call ------------------------ Resumes process, injecting a function call (EXPERIMENTAL!!!)
continue (alias: c) --------- Run until breakpoint or program termination.
next (alias: n) ------------- Step over to next source line.
rebuild --------------------- Rebuild the target executable and restarts it. It does not work if the executable was not built by delve.
restart (alias: r) ---------- Restart process.
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.
Manipulating breakpoints:
break (alias: b) ------- Sets a breakpoint.
breakpoints (alias: bp) Print out info for active breakpoints.
clear ------------------ Deletes breakpoint.
clearall --------------- Deletes multiple breakpoints.
condition (alias: cond) Set breakpoint condition.
on --------------------- Executes a command when a breakpoint is hit.
toggle ----------------- Toggles on or off a breakpoint.
trace (alias: t) ------- Set tracepoint.
watch ------------------ Set watchpoint.
Viewing program variables and memory:
args ----------------- Print function arguments.
display -------------- Print value of an expression every time the program stops.
examinemem (alias: x) Examine raw memory at the given address.
locals --------------- Print local variables.
print (alias: p) ----- Evaluate an expression.
regs ----------------- Print contents of CPU registers.
set ------------------ Changes the value of a variable.
vars ----------------- Print package variables.
whatis --------------- Prints type of an expression.
Listing and switching between threads and goroutines:
goroutine (alias: gr) -- Shows or changes current goroutine
goroutines (alias: grs) List program goroutines.
thread (alias: tr) ----- Switch to the specified thread.
threads ---------------- Print out info for every traced thread.
Viewing the call stack and selecting frames:
deferred --------- Executes command in the context of a deferred call.
down ------------- Move the current frame down.
frame ------------ Set the current frame, or execute command on a different frame.
stack (alias: bt) Print stack trace.
up --------------- Move the current frame up.
Other commands:
config --------------------- Changes configuration parameters.
disassemble (alias: disass) Disassembler.
dump ----------------------- Creates a core dump from the current process state
edit (alias: ed) ----------- Open where you are in $DELVE_EDITOR or $EDITOR
exit (alias: quit | q) ----- Exit the debugger.
funcs ---------------------- Print list of functions.
help (alias: h) ------------ Prints the help message.
libraries ------------------ List loaded dynamic libraries
list (alias: ls | l) ------- Show source code.
source --------------------- Executes a file containing a list of delve commands
sources -------------------- Print list of source files.
transcript ----------------- Appends command output to a file.
types ---------------------- Print list of types
Type help followed by a command for full documentation.
(dlv)
每个Go程序的入口是main.main()函数,可以用break命令在此设置一个断点:
(dlv) break main.main
Breakpoint 1 set at 0x10ad04a for main.main() ./delve.go:7
后通过breakpoints命令 缩写bp 查看已经设置的所有断点:
(dlv) bp
Breakpoint runtime-fatal-throw (enabled) at 0x1033f60 for runtime.throw() /usr/local/go/src/runtime/panic.go:982 (0)
Breakpoint unrecovered-panic (enabled) at 0x1034320 for runtime.fatalpanic() /usr/local/go/src/runtime/panic.go:1065 (0)
print runtime.curg._panic.arg
Breakpoint 1 (enabled) at 0x10ad04a for main.main() ./delve.go:7 (0)
通过实验我们发现除了我们刚才设置的main的断点,dlv还自己设置了一个panic的断点。
如果想查看有哪些包级变量可以用vars 命令
vars main
runtime.main_init_done = chan bool nil
runtime.mainStarted = false
如果想让程序运行到下一个断点可以用命令continue 缩写c
(dlv) c
> main.main() ./delve.go:7 (hits goroutine(1):1 total:1) (PC: 0x10ad04a)
2:
3: import "fmt"
4:
5: // dlv 调试器
6:
=> 7: func main() {
8: nums := make([]int, 5)
9:
10: for i := 0; i < len(nums); i++ {
11: nums[i] = i * i
12: }
我们可以看到它运行到main函数停下来了。
接下来我们单步执行进入main函数内部,可以用命令 next 缩写 n。
(dlv) n
> main.main() ./delve.go:8 (PC: 0x10ad058)
3: import "fmt"
4:
5: // dlv 调试器
6:
7: func main() {
=> 8: nums := make([]int, 5)
9:
10: for i := 0; i < len(nums); i++ {
11: nums[i] = i * i
12: }
13:
进入到函数内部后可以通过args, locals来查看参数 和 局部变量。
(dlv) args
(no args)
(dlv) locals
(no locals)
现在看还是都是空的。
因为函数main 没有参数,所以args 是空的。
而locals命令
我们单步执行一下,再来看下变量。
(dlv) n
> main.main() ./delve.go:10 (PC: 0x10ad083)
5: // dlv 调试器
6:
7: func main() {
8: nums := make([]int, 5)
9:
=> 10: for i := 0; i < len(nums); i++ {
11: nums[i] = i * i
12: }
13:
14: fmt.Println(nums)
15: }
(dlv) locals
nums = []int len: 5, cap: 5, [...]
这时候可以看到局部变量 nums 长度5 容量5了。
有的时候我们需要在循环中到达, 指定的条件才进行断点。
下面我们通过组合使用break和condition这2个命令,在循环内部设置一个条件断点,当循环中变量i == 3的时候,断点生效。
然后我们通过cotinue命令执行到设置的断点。
(dlv) break delve.go:11
Breakpoint 2 set at 0x10ad0a2 for main.main() ./delve.go:11
(dlv) condition 2 i==3
(dlv) c
> main.main() ./delve.go:11 (hits goroutine(1):1 total:1) (PC: 0x10ad0a2)
6:
7: func main() {
8: nums := make([]int, 5)
9:
10: for i := 0; i < len(nums); i++ {
=> 11: nums[i] = i * i
12: }
13:
14: fmt.Println(nums)
15: }
这时候我们再用locals命令查看变量,发现i已经是3了。打印了一下nums发现前面3个数,已经填充好了。
(dlv) locals
nums = []int len: 5, cap: 5, [...]
i = 3
(dlv) p nums
[]int len: 5, cap: 5, [0,1,4,0,0]
好了,今天的dlv 就介绍到这里了,下一篇我们继续介绍用dlv 调试汇编程序。