返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

调试

发布于 2024-10-12 19:16:09 字数 6444 浏览 0 评论 0 收藏 0

Delve 是官方扶持的源码级别调试器,能满足日常开发需要。

https://github.com/go-delve/delve ,version 1.8

详细信息请参考官方文档,本文记录一些基本操作。

  • 编译并调试,使用 dlv debug
  • 已编译程序,使用 dlv exec
  • 已运行程序,使用 dlv attach
  • 检查 core dump,使用 dlv core
  • 服务器模式(deubg server)以 --headless 启动,客户端 dlv connect 接入。

调试模式:

  • 自动以 -gcflags all=-N -l 方式编译。
  • 如向 go build 传递参数,使用 --build-flags
  • 支持 runtime.Breakpoint() 设置断点。

示例代码:

package main

func add(x, y int) int {
    z := x + y
    return z
}

func main() {
    println(add(1, 2))
}

1. 位置

相关命令需要一个位置参数(linespec)。

  • l, ls, list : 查看源码。
  • <filename>:<line> : 源文件名和行号。
  • <line> : 当前源文件行号。
  • <func>:<line> : 函数内第几行。(定义为起点 0)
  • +<offset> , -<offset> : 基于当前行的偏移量。
  • *<address> : 内存地址。
  • /regex/ : 正则表达式匹配的函数。
(dlv) b main.add:2
(dlv) c

     3:	func add(x, y int) int {
     4:		z := x + y
=>   5:		return z
     6:	}

2. 断点

以下面的示例进行演示。

  • b, break : 设置断点。
  • bp, breakpoints : 显示所有已设置断点。
  • toggle : 开关断点。
  • clear, clearall : 删除指定或全部断点。
  • cond, condtion : 设置断点命中条件。
  • on : 设置断点命中执行命令。
  • t, tracepoint : 设置跟踪点。
(dlv) b main.add:2

(dlv) bp
Breakpoint 1 (enabled) at 0x45b4ef for main.add() ./main.go:5 (0)

(dlv) on 1 locals
(dlv) cond 1 x == 1

(dlv) bp
Breakpoint 1 (enabled) at 0x45b4ef for main.add() ./main.go:5 (0)
	cond x == 1
	locals
  
(dlv) c
> main.add() ./main.go:5 (hits goroutine(1):1 total:1) (PC: 0x45b4ef)
	z: 3
     1:	package main
     2:	
     3:	func add(x, y int) int {
     4:		z := x + y
=>   5:		return z
     6:	}
(dlv) clear 1
Breakpoint 1 cleared at 0x45b4ef for main.add() ./main.go:5

(dlv) clearall

特殊 “断点” (tracepoint),它不会中断程序执行,仅在命中时(hit)输出信息。

可用来跟踪代码是否被执行。

(dlv) t abc main.add
Tracepoint abc set at 0x45b4c0 for main.add() ./main.go:3

可在汇编指令级别设置断点。

(dlv) b *0x45b53e
Breakpoint 2 set at 0x45b53e for main.main() ./main.go:9

(dlv) c
> main.main() ./main.go:9 (hits goroutine(1):1 total:1) (PC: 0x45b53e)

3. 运行

调试器启动时,会在 _rt0_amd64_linux 处停住,并非 main.main 用户代码入口。

  • c, continue : 继续运行,直到某个断点被命中。
  • n, next : 下一源码行(或指定行数)。
  • s, step : 下一行,进入所调用函数内部。
  • so, stepout : 执行,直到跳出当前函数。
  • si, step-instruction : 下一条指令(汇编)。
  • r, restart : 重启。
  • rebuild : 重新编译,重启调试。
  • q, exit : 退出。

不输入命令,直接回车,表示执行上一条命令。连续单行执行时,很方便。

4. 堆栈

调用堆栈(call stack)由函数栈帧(stack frame)组成。

  • bt, stack : 显示整个调用堆栈。
  • frame : 设置缺省栈帧。
  • up, down : 调整缺省堆栈。

调整缺省栈帧后,bt 列表里没有相关提示。可用 list 输出源码,开头会显示栈帧编号。

(dlv) b main.add
(dlv) c
     
(dlv) bt
0  0x000000000045b4c0 in main.add
   at ./main.go:3
1  0x000000000045b525 in main.main
   at ./main.go:9
2  0x0000000000432a18 in runtime.main
   at src/runtime/proc.go:250
3  0x0000000000458501 in runtime.goexit
   at src/runtime/asm_amd64.s:1571

可在某个具体的 goroutine 上执行调试命令。

  • gr, goroutine : 显示或修改默认 G。
  • grs, goroutines : 列出所有 G。
  • tr, thread : 切换线程。
  • threads : 列出所有线程。
(dlv) gr
Thread 13551 at ./main.go:3
Goroutine 1:
	Runtime: ./main.go:3 main.add (0x45b4c0)
	User: ./main.go:3 main.add (0x45b4c0)
	Go: <autogenerated>:1 runtime.newproc (0x45a9c5)
	Start: src/runtime/proc.go:145 runtime.main (0x432840)
  
(dlv) grs
* Goroutine 1 - User: ./main.go:3 main.add (0x45b4c0) (thread 13551)
  Goroutine 2 - User: proc.go:362 runtime.gopark (0x432e32) [force gc (idle)]
  Goroutine 3 - User: proc.go:362 runtime.gopark (0x432e32) [GC sweep wait]
  Goroutine 4 - User: proc.go:362 runtime.gopark (0x432e32) [GC scavenge wait]
[4 goroutines]

(dlv) gr 1 bt
0  0x000000000045b4c0 in main.add
   at ./main.go:3
1  0x000000000045b525 in main.main
   at ./main.go:9
2  0x0000000000432a18 in runtime.main
   at src/runtime/proc.go:250
3  0x0000000000458501 in runtime.goexit
   at src/runtime/asm_amd64.s:1571
(dlv) grs -g   # 查看 goroutine 创建位置。

* Goroutine 1 - Go: <autogenerated>:1 runtime.newproc (0x45a9c5) (thread 13551)
  Goroutine 2 - Go: proc.go:289 runtime.init.6 (0x432ba5) [force gc (idle)]
  Goroutine 3 - Go: mgc.go:177 runtime.gcenable (0x414b0b) [GC sweep wait]
  Goroutine 4 - Go: mgc.go:178 runtime.gcenable (0x414b4a) [GC scavenge wait]
[4 goroutines]

5. 查看

命中断点后,通过输出一系列相关信息来确定我们要做什么。

  • args : 函数参数。
  • locals : 局部变量。
  • vars : 全局变量。
  • whatis : 类型信息。
  • p, print : 输出表达式值。
  • display : 每次程序中断时,输出表达式结果。
  • set : 修改变量值。
  • regs : 输出寄存器。
  • x, examinemem : 输出内存值。
(dlv) b main.add
(dlv) c
     
(dlv) args
x = 1
y = 2
~r0 = 4213498        # 返回值。

(dlv) set x = 100
(dlv) args
x = 100
y = 2
~r0 = 4213498

6. 测试

可以单元测试为调试入口。

调试器用 -- 分隔目标程序参数,可用 test.run 指定具体的目标函数。

$ dlv test -- -test.run "TestB"

(dlv) funcs TestB
test.TestB

(dlv) b test.TestB

7. 其他

文档某些命令似乎被移除。

  • call : 调用函数(实验)。
  • disass, disassemble : 反汇编。
  • funcs : 函数列表(不确定函数全名时,可查)。
  • types : 类型列表。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文