Delve 调试器

Delve 调试器

目前 Go 语言支持 GDB、LLDB 和 Delve 几种调试器。其中 GDB 是最早支持的调试工具,LLDB 是 macOS 系统推荐的标准调试工具。但是 GDB 和 LLDB 对 Go 语言的专有特性都缺乏很大支持,而只有 Delve 是专门为 Go 语言设计开发的调试工具。而且 Delve 本身也是采用 Go 语言开发,对 Windows 平台也提供了一样的支持。本节我们基于 Delve 简单解释如何调试 Go 汇编程序。

idea 上的 dlv 调试

一般我们都用idea来开发go,这上面带的断点调试工具就是 dlv。

我这边本地 用的版本有以下几个:

idea-IU-203.7148.57

idea-IU-211.7628.21

GoLand-2021.1.3

里面默认带的 dlv 版本 都是 1.6.1 的

1
2
3
Delve Debugger
Version: 1.6.1
Build: 7a3faca71f7e01a97833e11ebf0683543e8159cb

idea 上 断点调试go 的问题

dlv 调试 卡死,分步往下没响应的 问题。

这一段时间发现 有些时候 断点 调试 不能很好的使用了,执行的时候 断点那里
可以停下来,但是 继续按 “步过”, “步入” 等都不好使了。 使得调试非常痛苦。
只能 在下一行 多打几个断点,这样才基线执行 会 停到下一行。

经过尝试 切换 idea 版本 发现 也不行,由于 没有切换 最新的2022版本的也不得而知 是否好使。

然后偶然间 去看了看 这个 dlv 的版本,然后尝试 切换一下 新的版本的dlv,发现好使了!!!!!!!

idea 上 dlv 路径

偶然间发现 dlv 的路径:

在 idea 上是 这个路径

1
~/.local/share/JetBrains/IntelliJIdea2021.1/go/lib/dlv/linux/dlv

在 goland 上 是 这个路径

1
~/GoLand-2021.1.3/plugins/go/lib/dlv/linux/dlv

idea 上 切换 dlv 版本

安装个最新的dlv

1
go install github.com/go-delve/delve/cmd/dlv@v1.9.1

这个会安装到 ~/go/bin 目录下面

把这个 新版本的 复制到 idea 目录下面,如下所示,多个dlv 都带上个版本号,然后做个软连接指向其中的一个,
如果要切换不同版本的,可以修改软连接的指向就行。

1
2
3
4
5
6
7
8
9
10
.
├── dlv -> dlv-1.7.0
├── dlv-1.6.1
├── dlv-1.7.0
├── dlv-1.7.1 好使了
├── dlv-1.7.2 好使了
├── dlv-1.7.3 好使了
└── dlv-1.9.1 好使了


通过多个版本的尝试 ,发现 到 1.7.1 版本好使了, 1.6.1 和 1.7.0 都有问题。

idea 上 配置 dlv 路径

网上 搜到的其他方法:

1
打开IDEA -> help -> Edit Custom Properties, 输入 dlv.path=D:/go/bin/dlv.exe

不过觉得没我上面的方法好。

找到 v1.7.1 和 v1.7.0 中哪个提交修复了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
* df1108b2 * *: v1.7.1 (#2662)                                                                                    <Derek Parker      >[2021-08-18 00:08 2021-08-18 09:08] (HEAD, tag: v1.7.1)
* a4d416d5 * service/dap: add test that verifies output path is relative to wd (#2656) <polinasok >[2021-08-17 11:40 2021-08-17 11:40]
* 694b45c8 * service/dap: fix noDebug mode to handle requests while running (#2658) <polinasok >[2021-08-16 08:51 2021-08-16 08:51]
* 51375157 * service: fix sameuser check (#2642) <Alessandro Arzilli>[2021-08-10 11:40 2021-08-10 11:40]
* c426c5b3 * pkg/proc: configure target to not clear stepping breakpoints (#2635) <Suzy Mueller >[2021-08-09 11:56 2021-08-09 10:56]
* 4264bf00 * proc,terminal,service: support stack watchpoints (#2521) <Alessandro Arzilli>[2021-08-09 19:41 2021-08-09 10:41]
* f3e76238 * proc: move breakpoint condition evaluation out of backends (#2628) <Alessandro Arzilli>[2021-08-09 19:16 2021-08-09 10:16]
* 4e5bddee * cmd/dlv: use simple chan read instead of select-case (#2649) <hitzhangjie >[2021-08-10 01:08 2021-08-09 10:08]
* 6b83e6ba * vendor: run go mod vendor (#2647) <hitzhangjie >[2021-08-06 14:26 2021-08-06 08:26]
* e175d68e * service/dap: fix race condition in TestContinueOnEntry (#2641) <Alessandro Arzilli>[2021-08-06 07:19 2021-08-05 22:19]
* 43d50202 * pkg/terminal: Allow fuzzy searching tab completions (#2633) <Derek Parker >[2021-08-05 10:55 2021-08-05 19:55]
* 985eca46 * service/dap: log execution-halted message when setting breakpoints (#2631) <polinasok >[2021-08-05 10:16 2021-08-05 10:16]
* 7caa534d * service/debugger: Remove target lock on GetBufferedTracepoints (#2645) <Derek Parker >[2021-08-05 04:53 2021-08-05 13:53]
* 2971fd4c * service/dap: fix TestNextParked/TestStepInParked (#2643) <Alessandro Arzilli>[2021-08-04 23:12 2021-08-04 14:12]
* 4e242098 * tests: check presence of gcc for cgo tests (#2644) <Alessandro Arzilli>[2021-08-04 23:12 2021-08-04 14:12]
* f95340ae * pkg/proc: Fix build errors (#2637) <Derek Parker >[2021-08-04 01:00 2021-08-04 10:00]
* 74627673 * teamcity: report go test exit code on windows (#2638) <nd >[2021-08-03 19:42 2021-08-03 10:42]
* fdb5189e * dwarf/op,proc: implement more DWARF expression opcodes (#2606) <Alessandro Arzilli>[2021-08-03 18:51 2021-08-03 09:51]
* 229fcf15 * cmd: refactor building logic into a helper (#2629) <polinasok >[2021-08-03 09:48 2021-08-03 09:48]
* df5812bf * pkg/proc: add tests for next interrupted by bp (#2632) <Suzy Mueller >[2021-08-03 10:47 2021-08-03 09:47]
* 10406f96 * *: Initial eBPF tracing support (#2625) <Derek Parker >[2021-07-31 08:16 2021-07-31 17:16]
* 89ed5a0b * service/dap: page stack frames (#2597) <Suzy Mueller >[2021-07-29 11:34 2021-07-29 10:34]
* b87a1fc5 * service/dap: make next while nexting error more clear (#2622) <Suzy Mueller >[2021-07-29 11:27 2021-07-29 10:27]
* a2b83999 * Fix crashes on Go dev.typeparams (soon to be Go main branch) (#2627) <Austin Clements >[2021-07-28 11:18 2021-07-28 08:18]
* cb73ef8f * pkg/terminal: Ignore existing breakpoints for continue until (#2624) <Derek Parker >[2021-07-28 04:13 2021-07-28 13:13]
* 56731bd8 * Documentation: Improve help output for examinemem (#2623) <Derek Parker >[2021-07-28 04:12 2021-07-28 13:12]
* f6681c60 * pkg/proc: Prefer throw instead of fatalthrow (#2616) <Derek Parker >[2021-07-27 23:58 2021-07-27 23:58]
* 26e7f67c * cmd/dlv: dlv version --verbose (#2615) <Hyang-Ah Hana Kim >[2021-07-27 12:38 2021-07-27 09:38]
* 2ecc0253 * terminal: add prompt when breakpoint is hit during next/step/stepout (#2548) <Alessandro Arzilli>[2021-07-26 17:57 2021-07-26 08:57]
* e9b20d5e * proc: use signed comparison when searching image for module data (#2621) <Alessandro Arzilli>[2021-07-26 17:40 2021-07-26 08:40]
* 150ef041 * TeamCity: prefer go rc builds over beta (#2619) <nd >[2021-07-24 20:14 2021-07-24 11:14]
* aaed14ff * service/dap: fix backend parsing in replay mode (#2618) <polinasok >[2021-07-23 14:04 2021-07-23 14:04]
* f1edc45f * service/dap: disable TestFatalThrowBreakpoint for Go 1.17 (#2614) <Suzy Mueller >[2021-07-23 11:12 2021-07-23 10:12]
* 39274f60 * proc: make moduleDataToImage more robust (#2613) <Alessandro Arzilli>[2021-07-23 18:21 2021-07-23 09:21]
* f74b7a6e * all: update github.com/spf13/cobra to v1.1.3 (#2572) <Hyang-Ah Hana Kim >[2021-07-22 14:05 2021-07-22 11:05]
* 89890735 * terminal: improve 'on' command (#2556) <Alessandro Arzilli>[2021-07-22 19:16 2021-07-22 10:16]
* b7d8edcd * service/dap: handle unexpected debugger termination (EOF) error (#2574) <polinasok >[2021-07-22 08:52 2021-07-22 08:52]
* b41e47a3 * teamcity: use same token everywhere (#2609) <nd >[2021-07-21 17:35 2021-07-21 08:35]
* 3941af1d * service/dap: limit the number of goroutines to return from a threads request (#2595) <Suzy Mueller >[2021-07-21 10:26 2021-07-21 08:26]
* 658d36cb * proc: allow multiple overlapping internal breakpoints (#2519) <Alessandro Arzilli>[2021-07-21 17:24 2021-07-21 08:24]
* c5e533b1 * Treat SIGTERM as a server disconnect signal (#2580) <polinasok >[2021-07-21 07:43 2021-07-21 07:43]
* 69615b36 * service/dap: Support for replay and core modes (#2367) <Luis Gabriel Gomez>[2021-07-21 11:38 2021-07-21 07:38]
* a14bec4c * Make teamcity ui settings readonly (#2608) <nd >[2021-07-21 16:26 2021-07-21 07:26]
* 65f43649 * teamcity: specify working github token in dsl (#2607) <nd >[2021-07-21 16:19 2021-07-21 16:19]
* 776b86ff * service/dap: send continued event before step response (#2594) <Suzy Mueller >[2021-07-20 10:51 2021-07-20 08:51]
* 38aaf274 * service/dap: fix TestPreSetBreakpoint (#2600) <Alessandro Arzilli>[2021-07-19 17:09 2021-07-19 08:09]
* d6556b85 * TeamCity: remove 1.16 testing for linux/arm64, windows/amd64 and mac/* (#2601) <Alessandro Arzilli>[2021-07-19 17:03 2021-07-19 08:03]
* 4e78f7f3 * service/dap: add modes comments (TODO from PR/2367) (#2575) <polinasok >[2021-07-18 11:06 2021-07-18 20:06]
* af378d39 * service/dap: add backend launch/attach attribute (#2567) <polinasok >[2021-07-18 02:37 2021-07-18 11:37]
* 2a22af2e * teamcity: fix settings (#2598) <nd >[2021-07-17 18:58 2021-07-17 09:58]
* feb55342 * service/dap: add len as metadata for map (#2584) <Suzy Mueller >[2021-07-16 16:50 2021-07-16 09:50]
* 5459034a * cmd/dap: server - always headless, target - always foregrounded (#2589) <polinasok >[2021-07-16 09:49 2021-07-16 09:49]
* 584191a7 * *: Release 1.7.0 (#2591) <Derek Parker >[2021-07-16 06:31 2021-07-16 15:31] (tag: v1.7.0)
* 9df8f149 * service/dap: variables response must not have null variables array (#2592) <polinasok >[2021-07-15 14:15 2021-07-15 14:15]

git bisect是一个很有用的命令,用来查找哪一次代码提交引入了错误。

它的原理很简单,就是将代码提交的历史,按照两分法不断缩小定位。所谓"两分法",
就是将代码历史一分为二,确定问题出在前半部分,还是后半部分,不断执行这个过程,直到范围缩小到某一次代码提交。

git bisect start命令启动查错,它的格式如下。

1
$ git bisect start [终点] [起点]
1
2
3
4
5
6
$ git bisect start v1.7.1 v1.7.0

或者用 提交 ID

$ git bisect start df1108b2 584191a7

开始之后去测试,如果是好的 就执行

用git bisect good命

1
2
$ git bisect good

如果功能不正常 使用git bisect bad命令

1
2
3
$ git bisect bad


接下来,不断重复这个过程,直到成功找到出问题的那一次提交为止。这时,Git 会给出如下的提示。

然后,使用git bisect reset命令,退出查错,回到最近一次的代码提交。

1
2
$ git bisect reset

我们这里是 找 哪个提交 修复的,正好 感觉是 反着来标记的

就是把 good 标记 为 bad, 把 bad 的 标记为 good。技巧, 可以使用个 别名
[alias]
    bisect-fixed = bisect bad
    bisect-unfixed = bisect good

$ git bisect start
$ git bisect-fixed master
$ git bisect-unfixed $some-old-sha1

高版本git 可以使用 git bisect terms 相关的功能

                            

git bisect terms 用法,自定义 bad,good 标识。

1
git bisect  --term-new fixed --term-old broken
有时,你并不是在寻找引入 坏 的提交,而是在寻找导致之间发生变化的提交。
类似 一种“旧”状态 和 “新”状态。例如,您可能正在寻找引入特定修复的提交。或
您可能正在寻找第一次提交,其中源代码文件名最终全部转换为您公司的文件名
命名标准。等等 之类的。

在这种情况下,使用术语“good”和“bad”来指代 变化之前的状态 和变化之后的状态 可能会非常混乱。
这个时候,你可以分别用“old”和“new”来代替“good”和“bad”。(但
注意,你不能在 git bisect 过程中 混合“good”和“bad”  与  “old”和“new”。)

在这个更一般的用法中,你用一个“new”提交(新的具有一种特殊的属性的)
和一个“old”提交(旧的也要具有一种特殊属性,这样你才能区分 新, 旧。)
来 提供git bisect 命令。
每次git bisect检出一个提交时,你都会测试该提交是否具有该属性。如果是这样,
将提交标记为“新”;否则,标记为“旧”。当二分完成时,git bisect将报告哪个提交。

下面给出完整 执行 过程

1
2
3
4
可以使用 reset 重置工作区 状态。
$ git bisect reset
之前的 HEAD 位置是 f6681c60 pkg/proc: Prefer throw instead of fatalthrow (#2616)
HEAD 目前位于 df1108b2 *: v1.7.1 (#2662)

git bisect start 开始 指定 起始提交,终点提交, 并且自定义 terms

1
2
3
$ git bisect start --term-new fixed --term-old broken v1.7.1 v1.7.0                                                                 
二分查找中:在此之后,还剩 25 个版本待测试 (大概 5 步)
[f6681c60903cc126ba060f01e2eab1341b06b526] pkg/proc: Prefer throw instead of fatalthrow (#2616)

执行编译命令,然后 手动 在idea 进行 断点调试 来 验证 是否有这个问题,如果有问题 就 标记 为 broken。

1
2
3
4
5
6
7
8
9
$ go mod tidy;make build;./dlv version 
go build "-ldflags=-X main.Build=f6681c60903cc126ba060f01e2eab1341b06b526" github.com/go-delve/delve/cmd/dlv
Delve Debugger
Version: 1.7.0
Build: f6681c60903cc126ba060f01e2eab1341b06b526

$ git bisect broken
二分查找中:在此之后,还剩 12 个版本待测试 (大概 4 步)
[2971fd4c685aeda74b087078f203436c73f94a07] service/dap: fix TestNextParked/TestStepInParked (#2643)

继续 执行编译命令,然后 手动 在idea 进行 断点调试 来 验证 是否有这个问题,如果没问题 就 标记 为 fixed

1
2
3
4
5
6
7
8
9
$ go mod tidy;make build;./dlv version                                                                                             
go build -ldflags "-extldflags -static" "-ldflags=-X main.Build=2971fd4c685aeda74b087078f203436c73f94a07" github.com/go-delve/delve/cmd/dlv
Delve Debugger
Version: 1.7.0
Build: 2971fd4c685aeda74b087078f203436c73f94a07

$ git bisect fixed
二分查找中:在此之后,还剩 6 个版本待测试 (大概 3 步)
[10406f96d539d905fbe8e8d102d7a01e61f26f04] *: Initial eBPF tracing support (#2625)

继续 执行编译命令,然后 手动 在idea 进行 断点调试 来 验证 是否有这个问题,如果没问题 就 标记 为 fixed

1
2
3
4
5
6
7
8
$ go mod tidy;make build;./dlv version                                                                                              
go build -ldflags "-extldflags -static" "-ldflags=-X main.Build=10406f96d539d905fbe8e8d102d7a01e61f26f04" github.com/go-delve/delve/cmd/dlv
Delve Debugger
Version: 1.7.0
Build: 10406f96d539d905fbe8e8d102d7a01e61f26f04
$ git bisect fixed
二分查找中:在此之后,还剩 2 个版本待测试 (大概 2 步)
[a2b839990e21690e9e60a74c582c4acece6196d8] Fix crashes on Go dev.typeparams (soon to be Go main branch) (#2627)

继续 执行编译命令,然后 手动 在idea 进行 断点调试 来 验证 是否有这个问题,如果没问题 就 标记 为 fixed

1
2
3
4
5
6
7
8
$ go mod tidy;make build;./dlv version                                                                                             
go build "-ldflags=-X main.Build=a2b839990e21690e9e60a74c582c4acece6196d8" github.com/go-delve/delve/cmd/dlv
Delve Debugger
Version: 1.7.0
Build: a2b839990e21690e9e60a74c582c4acece6196d8
$ git bisect fixed
二分查找中:在此之后,还剩 0 个版本待测试 (大概 1 步)
[cb73ef8f83d7837486b3f7b9043ce07df96e466c] pkg/terminal: Ignore existing breakpoints for continue until (#2624)

继续 执行编译命令,然后 手动 在idea 进行 断点调试 来 验证 是否有这个问题,如果没问题 就 标记 为 broken

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ go mod tidy;make build;./dlv version                                                                                               
go build "-ldflags=-X main.Build=cb73ef8f83d7837486b3f7b9043ce07df96e466c" github.com/go-delve/delve/cmd/dlv
Delve Debugger
Version: 1.7.0
Build: cb73ef8f83d7837486b3f7b9043ce07df96e466c
$ git bisect broken
a2b839990e21690e9e60a74c582c4acece6196d8 is the first fixed commit
commit a2b839990e21690e9e60a74c582c4acece6196d8
Author: Austin Clements <aclements@csail.mit.edu>
Date: Wed Jul 28 11:18:20 2021 -0400

Fix crashes on Go dev.typeparams (soon to be Go main branch) (#2627)

* proc: Go 1.18 removes the _defer.siz field

As of Go 1.17, the _defer.siz field is always 0 because _defer no
longer stores defer call arguments at all. golang.org/cl/326062
removes it entirely for Go 1.18. Simply treat it as 0 if the field is
missing from the _defer type.

* proc: Go 1.18 changes _defer.fn from *funcval to func()

golang.org/cl/325918 changed the type of the _defer.fn field from
*funcval to func() for Go 1.18. This CL was later reverted because it
caused failures in Delve, but we would like to un-revert it. Handle
this change by inspecting the type of this field before decoding it.

:040000 040000 9de9310d2c1811c12c21cc0de6992e5b800a6216 f4a5b6ab218dedd7af97bac64cc99174ef1a849e M pkg

二分 查找 分析过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
* df1108b2 *  (HEAD, tag: v1.7.1)
* a4d416d5 *
* 694b45c8 *
* 51375157 *
* c426c5b3 *
* 4264bf00 *
* f3e76238 *
* 4e5bddee *
* 6b83e6ba *
* e175d68e *
* 43d50202 *
* 985eca46 *
* 7caa534d *
* 2971fd4c * 第 2 次 验证 标识 为 fixed
* 4e242098 *
* f95340ae *
* 74627673 *
* fdb5189e *
* 229fcf15 *
* df5812bf *
* 10406f96 * 第 3 次 验证 标识 为 fixed
* 89ed5a0b *
* b87a1fc5 *
* a2b83999 * 第 4 次 验证 标识 为 fixed 最终找到 这个提交 修复的
* cb73ef8f * 第 5 次 最后一次 验证 标识 为 broken
* 56731bd8 *
* f6681c60 * 第 1 次 验证的 标识 为 broken, 二分法,这个估计是 最中间那一个提交, 一共 53 行,这个是 第 26行,刚刚好是中间那一个。 这个往下的 提交 都是 broken 的。
* 26e7f67c *
* 2ecc0253 *
* e9b20d5e *
* 150ef041 *
* aaed14ff *
* f1edc45f *
* 39274f60 *
* f74b7a6e *
* 89890735 *
* b7d8edcd *
* b41e47a3 *
* 3941af1d *
* 658d36cb *
* c5e533b1 *
* 69615b36 *
* a14bec4c *
* 65f43649 *
* 776b86ff *
* 38aaf274 *
* d6556b85 *
* 4e78f7f3 *
* af378d39 *
* 2a22af2e *
* feb55342 *
* 5459034a *
* 584191a7 * (tag: v1.7.0)
* 9df8f149 *

最终得到如下提交修复了这个问题

a2b839990e21690e9e60a74c582c4acece6196d8 is the first fixed commit

commit a2b839990e21690e9e60a74c582c4acece6196d8
Author: Austin Clements <aclements@csail.mit.edu>
Date:   Wed Jul 28 11:18:20 2021 -0400

    Fix crashes on Go dev.typeparams (soon to be Go main branch) (#2627)
    
    * proc: Go 1.18 removes the _defer.siz field
    
    As of Go 1.17, the _defer.siz field is always 0 because _defer no
    longer stores defer call arguments at all. golang.org/cl/326062
    removes it entirely for Go 1.18. Simply treat it as 0 if the field is
    missing from the _defer type.
    
    * proc: Go 1.18 changes _defer.fn from *funcval to func()
    
    golang.org/cl/325918 changed the type of the _defer.fn field from
    *funcval to func() for Go 1.18. This CL was later reverted because it
    caused failures in Delve, but we would like to un-revert it. Handle
    this change by inspecting the type of this field before decoding it.

:040000 040000 9de9310d2c1811c12c21cc0de6992e5b800a6216 f4a5b6ab218dedd7af97bac64cc99174ef1a849e M	pkg

总结

最终发现 其实 和 go的版本也有关系, dlv 1.6.1 在 go 1.17 及其一下版本可以正常使用。
go 1.18 及其以上 版本 需要使用 dlv 1.7.1 以上的版本了