需要调试器

任何编程语言中最简单的调试形式是使用打印语句/日志并写入标准输出。这肯定可以工作,但是当我们的应用程序规模增加并且逻辑变得更加复杂时,它变得极其困难。将打印语句添加到应用程序的每个代码路径都不容易。这是调试器派上用场的地方。调试器可帮助我们使用断点和许多其他功能来跟踪程序的执行路径。Delve是Go的一种此类调试器。使用Delve调试Go应用程序。

安装Delve

请确保您位于一个不包含go.mod文件的目录中。

cd ~/Documents/

接下来,设置GOBIN环境变量。此环境变量指定Delve二进制文件的安装位置。如果GOBIN已经设置,请跳过此步骤。您可以GOBIN通过运行以下命令检查是否设置了。

go env | grep GOBIN

如果以上命令显示GOBIN="",则表示GOBIN未设置。请运行export GOBIN=~/go/bin/命令设置GOBIN。

让通过运行添加GOBINPATHexport PATH=$PATH:~/go/bin

对于macOS,需要Xcode命令行开发人员工具来运行Delve。请运行xcode-select --install以安装命令行工具。Linux用户可以跳过此步骤。

现在我们开始安装Delve。请跑

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

安装delve。运行此命令后,请通过运行来测试安装dlv version。成功安装后,它将打印Delve的版本。

Delve Debugger
Version: 1.4.0
Build: $Id: 67422e6f7148fa1efa0eac1423ab5594b223d93b

开始Delve

编写一个简单的程序,然后使用Delve开始对其进行调试。

使用以下命令为示例程序创建目录。

mkdir ~/Documents/debugsample

main.godebugsample我们刚刚创建的目录内创建一个文件,内容如下。

package mainimport (  "fmt" ) func main() { arr := []int{101, 95, 10, 188, 100} max := arr[0] for _, v := range arr { if v > max { max = v } } fmt.Printf("Max element is %d\n", max) } 

上面的程序将打印切片 的最大元素arr。运行上面的程序将输出,

Max element is 188

可以调试程序了。让我们转到debugsample目录cd ~/Documents/debugsample。之后,键入以下命令以启动Delve。

dlv debug

上面的命令将开始调试当前目录中的main 软件包。键入上面的命令后,您可以看到终端已更改为(dlv)提示。如果您看到此更改,则表明调试器已成功启动并等待我们的命令:)。

让我们启动第一个命令。

dlv提示符下,键入continue

(dlv) continue

continue命令将运行程序,直到出现断点或程序完成为止。由于我们没有定义任何断点,因此程序将一直运行到完成。

Max element is 188
Process 1733 has exited with status 0

如果看到上面的输出,则调试器已运行,程序已完成:)。但这对我们没有任何用处。让我们继续,添加几个断点,并观察调试器如何发挥作用。

创建断点

断点在指定的行处暂停程序的执行。当执行暂停时,我们可以将命令发送到调试器以打印变量的值,查看程序的堆栈跟踪,等等。

下面提供了创建断点的语法,

(dlv) break filename:lineno

上面的命令将lineno在文件的line处创建一个断点filename

让我们在行号上添加一个断点。我们的9 main.go

(dlv) break main.go:9

运行上述命令后,您将看到输出Process 1733 has exited with status 0。实际上没有添加断点。这是因为continue由于当时没有断点,所以我们在较早运行时就退出了程序。让我们重新启动程序,然后尝试再次设置断点。

(dlv) restart
Process restarted with PID 2028
(dlv) break main.go:9
Breakpoint 1 set at 0x10c16e4 for main.main() ./main.go:9

restart命令重新启动程序,然后该break命令设置断点。上面的输出确认具有名称的断点1设置在行号。在main.go中为9。

现在让continue我们编写程序,并检查调试器是否在断点处暂停程序。

(dlv) continue
> main.main() ./main.go:9 (hits goroutine(1):1 total:1) (PC: 0x10c16e4)4:        "fmt"5:    )6:    7:    func main() {8:        arr := []int{101, 95, 10, 188, 100}
=>   9:        max := arr[0]10:        for _, v := range arr {11:            if v > max {12:                max = v13:            }14:        }

continue执行完之后,我们可以看到调试器在第9行暂停了我们的程序。

列出断点

(dlv) breakpoints

上面的命令列出了应用程序的当前断点。

(dlv) breakpoints
Breakpoint runtime-fatal-throw at 0x102de10 for runtime.fatalthrow() /usr/local/Cellar/go/1.13.7/libexec/src/runtime/panic.go:820 (0)
Breakpoint unrecovered-panic at 0x102de80 for runtime.fatalpanic() /usr/local/Cellar/go/1.13.7/libexec/src/runtime/panic.go:847 (0)  print runtime.curg._panic.arg
Breakpoint 1 at 0x10c16e4 for main.main() ./main.go:9 (1)

您可能会惊讶地发现,除了我们添加的断点之外,还有另外两个断点。delve会添加其他两个断点,以确保当没有使用restore处理的运行时紧急情况时,调试会话不会突然结束。

打印变量

程序的执行已在第n行暂停。9. print是用于打印变量值的命令。让我们使用print并在slice的第0个索引处打印元素arr

(dlv) print arr[0]

运行上面的命令将打印101在切片的第0个索引处的元素arr

请注意,如果尝试打印max,则会得到一个垃圾值。

(dlv) print max
824634294736

这是因为程序在行号之前已暂停。执行9,因此打印会max打印一些随机的垃圾值。要打印max的实际值,我们应该移至程序的下一行。可以使用next命令来完成。

移至源代码中的下一行

(dlv) next

将调试器移至下一行,并输出,

> main.main() ./main.go:10 (PC: 0x10c16ee)5:    )6:    7:    func main() {8:        arr := []int{101, 95, 10, 188, 100}9:        max := arr[0]
=>  10:        for _, v := range arr {11:            if v > max {12:                max = v13:            }14:        }15:        fmt.Printf("Max element is %d\n", max)

现在,如果我们尝试(dlv) print max,可以看到输出101

next命令可用于逐行浏览程序。

如果继续输入next,则可以看到调试器在程序中逐行浏览。当循环中的第一个循环重复执行时for。10结束了, next将引导我们完成下一个迭代,程序最终将终止。

打印表达式

print也可以用于评估表达式。例如,如果我们要查找的值max + 10,则可以使用print。

让我们在完成for计算的循环外添加另一个断点max

(dlv) break main.go:15

上面的命令在行号上添加了另一个断点。max的计算已完成的15。

键入continue,程序将在此断点处停止。

print max + 10命令将输出198

清除断点

clear是清除单个断点的命令,clearall是清除程序中所有断点的命令。

首先让我们列出应用程序中的断点。

(dlv) breakpointsBreakpoint runtime-fatal-throw at 0x102de10 for runtime.fatalthrow() /usr/local/Cellar/go/1.13.7/libexec/src/runtime/panic.go:820 (0)
Breakpoint unrecovered-panic at 0x102de80 for runtime.fatalpanic() /usr/local/Cellar/go/1.13.7/libexec/src/runtime/panic.go:847 (0)  print runtime.curg._panic.arg
Breakpoint 1 at 0x10c16e4 for main.main() ./main.go:9 (1)
Breakpoint 2 at 0x10c1785 for main.main() ./main.go:15 (1)

我们有两个断点分别是12

如果我们运行clear 1,它将删除断点1

(dlv) clear 1
Breakpoint 1 cleared at 0x10c16e4 for main.main() ./main.go:9

如果我们运行clearall,它将删除所有断点。我们只有一个断点命名为2剩余。

(dlv) clearall
Breakpoint 2 cleared at 0x10c1785 for main.main() ./main.go:15

从上面的输出中,我们可以看到剩余的一个断点也被清除了。如果我们continue现在执行命令,程序将打印该max值并终止。

(dlv) continue
Max element is 188
Process 3095 has exited with status 0

步入和退出功能

可以使用Delve进入功能或脱离功能。不要担心,如果现在没有意义的话:)。让我们尝试借助示例来理解这一点。

package mainimport (  "fmt" ) func max(arr []int) int { max := arr[0] for _, v := range arr { if v > max { max = v } } return max } func main() { arr := []int{101, 95, 10, 188, 100} m := max(arr) fmt.Printf("Max element is %d\n", m) } 

我已经修改了迄今为止一直在使用的程序,并将找到切片最大元素的逻辑移到了名为的函数中max

使用退出Delve (dlv) q,替换main.go为上面的程序,然后使用命令再次开始调试dlv debug

让我们在行上添加一个断点。否18 max调用函数的位置。

b是添加断点的简写。让我们使用它。

(dlv) b main.go:18
(dlv) continue

我们在第18行添加了断点,并继续执行程序。运行以上命令将打印,

> main.main() ./main.go:18 (hits goroutine(1):1 total:1) (PC: 0x10c17ae)13:        }14:        return max15:    }16:    func main() {17:        arr := []int{101, 95, 10, 188, 100}
=>  18:        m := max(arr)19:        fmt.Printf("Max element is %d\n", m)20:    }

程序执行已在第n行暂停。18如预期。现在我们有两个选择。

  • 继续深入调试max功能
  • 跳过最大功能,然后移至下一行。

根据我们的要求,我们可以选择其中一种。让我们学习如何同时做。

首先,让我们跳过max函数,移至下一行。为此,您可以运行next,调试器将自动移至下一行。默认情况下,Delve不会更深入地介绍函数调用。

(dlv) next
> main.main() ./main.go:19 (PC: 0x10c17d3)14:        return max15:    }16:    func main() {17:        arr := []int{101, 95, 10, 188, 100}18:        m := max(arr)
=>  19:        fmt.Printf("Max element is %d\n", m)20:    }

您可以从上面的输出中看到调试器已移至下一行。

键入continue,程序将完成执行。

让我们学习如何更深入地了解max函数。

键入restartcontinue,我们可以看到程序在已经存在的断点处再次暂停。

(dlv) restart
Process restarted with PID 5378
(dlv) continue
> main.main() ./main.go:18 (hits goroutine(1):1 total:1) (PC: 0x10c17ae)13:        }14:        return max15:    }16:    func main() {17:        arr := []int{101, 95, 10, 188, 100}
=>  18:        m := max(arr)19:        fmt.Printf("Max element is %d\n", m)20:    }

现在输入step,我们可以看到控件已经移入max函数中了。

(dlv) step
> main.max() ./main.go:7 (PC: 0x10c1650)2:    3:    import (4:        "fmt"5:    )6:
=>   7:    func max(arr []int) int {8:        max := arr[0]9:        for _, v := range arr {10:            if v > max {11:                max = v12:            }

键入next,控件将移至max函数的第一行。

(dlv) next
> main.max() ./main.go:8 (PC: 0x10c1667)3:    import (4:        "fmt"5:    )6:    7:    func max(arr []int) int {
=>   8:        max := arr[0]9:        for _, v := range arr {10:            if v > max {11:                max = v12:            }13:        }

如果继续输入next,则可以逐步执行该max函数的执行路径。

您可能想知道是否可以main不通过max函数中的每一行而返回到。是的,使用stepout命令可以做到这一点。

(dlv) stepout
> main.main() ./main.go:18 (PC: 0x10c17c9)
Values returned:  ~r1: 18813:        }14:        return max15:    }16:    func main() {17:        arr := []int{101, 95, 10, 188, 100}
=>  18:        m := max(arr)19:        fmt.Printf("Max element is %d\n", m)20:    }

键入后stepout,控件将返回到main。现在您可以在main:)中继续调试。

打印堆栈跟踪

调试时需要的一个非常重要的功能是打印程序的当前堆栈跟踪。这对于查找当前代码执行路径很有用。stack是用于打印当前堆栈跟踪的命令。

让我们清除所有断点,在行号处添加一个新的断点。11并打印程序的当前堆栈跟踪。

(dlv) restart
(dlv) clearall
(dlv) b main.go:11
(dlv) continue

当程序在断点处暂停时,键入

(dlv) stack

它将输出程序的当前堆栈跟踪。

0  0x00000000010c16e8 in main.max  at ./main.go:11
1  0x00000000010c17c9 in main.main  at ./main.go:18
2  0x000000000102f754 in runtime.main  at /usr/local/Cellar/go/1.13.7/libexec/src/runtime/proc.go:203
3  0x000000000105acc1 in runtime.goexit  at /usr/local/Cellar/go/1.13.7/libexec/src/runtime/asm_amd64.s:1357

到目前为止,我们已经介绍了基本命令,以帮助开始使用Delve调试应用程序。在接下来的教程中,我们将介绍Delve的高级功能,例如调试goroutine,将调试器附加到现有进程,远程调试以及使用VSCode编辑器中的Delve。

使用Delve调试Go应用程序相关推荐

  1. 是否要运行此应用程序_使用Delve调试Go应用程序

    调试器 任何编程语言中最简单的调试形式是使用打印语句或日志来写入标准输出.这肯定没有问题,但是当我们的应用程序规模增加并且逻辑变得更加复杂时,这种方式变得极其困难.将打印语句添加到应用程序的每个代码路 ...

  2. 【转】“无法在Web服务器上启动调试。您不具备调试此应用程序的权限,此项目的URL位于Internet区域”错误提示的解决...

    错误提示: 无法在Web服务器上启动调试.您不具备调试此应用程序的权限,此项目的URL位于Internet区域 一般用下面的方法可以解决: 1:确认在"配置属性"中的"启 ...

  3. Windbg 教程-调试非托管程序的基本命令下

    前面的文章调试非托管程序的基本命令中讲到如何使用windbg在程序中设置断点,既然断点已经设置好了,下一步就是直接执行程序,程序中断以后,第一件事情就是查看堆栈.在windbg中查看堆栈使用k命令就可 ...

  4. mac pro下安装gdb和delve调试器

    2019独角兽企业重金招聘Python工程师标准>>> 我用liteide进行go环境的配置,最近升级以后发现两个调试器都不能debug代码 了,主要原因有两个.一个是软件签名问题和 ...

  5. Shift+F5后,vc6不可以退出调试状态,程序不能再次执行

    当我击F5开始一个项目的调试时,程序在我设置的断点处停止,这时按下Shift+F5后,vc6可以退出调试状态,但是WINDOWS系统的任务栏上会留下前面调试时产生的程序.该进程不能被结束,即使我使用任 ...

  6. 7 php程序的调试方法_php程序调试方法总结

    相信很多朋友们都有调试程序的经历,然而很多时候调试程序是痛苦而又漫长的过程:它不仅需要细心,更需要耐心,切忌心浮气躁.但是当找出问题并顺利 解决它时,又会给人无比激动的喜悦.这里总结一下笔者在程序调试 ...

  7. vc60如何输入c语言,vc60中如何编译运行及调试c语言程序.pdf

    VC++6.0中如何编译运行调试 C语言程序 1. 启动VC++6.0 (如下图) 2. 单个源文件的编译运行 例如下面的源代码 #include void main() { int i,sum=0; ...

  8. wxWidgets:调试 WxWindow 应用程序

    wxWidgets:调试 WxWindow 应用程序 wxWidgets:调试 WxWindow 应用程序 __WXDEBUG__ 日志调试 wxASSERT wxWidgets:调试 WxWindo ...

  9. Windbg教程-调试非托管程序的基本命令中

    前面的文章调试非托管程序的基本命令上讲到如何在windbg里面启动一个程序并且加载调试符号文件.一旦符号文件加载完毕以后,就可以进行调试了,例如设置断点,查看堆栈信息等等. 因为是刚刚启动程序(mai ...

最新文章

  1. android 自定义天气特效,《Android自定义控件》WindMillView,仿华为天气风车效果
  2. UltraEdit使用注册机激活详解
  3. 开源应用框架BitAdminCore:更新日志20180605
  4. lucence学习系列之一 基本概念
  5. linux的文件io操作(转)
  6. gtk+学习笔记(五)
  7. 让visio保存背景透明的图片。
  8. 数据库设计 表和表之间的三种关系
  9. matlab 模拟滤波器,基于MATLAB的模拟滤波器设计
  10. C语言基础入门(经典收藏)
  11. mysql清空数据库的所有数据
  12. MYSQL 查询某个月有多少天数?
  13. java基础提升(二):多线程、线程安全、线程状态、等待唤醒机制、线程池
  14. 双显示屏切单显时打不开关掉的显示屏上打开的软件的问题
  15. 久坐伤身 关爱程序员,从自己做起
  16. 美女手机壁纸采集源码
  17. 牛逼的python代码_牛逼啊!一个随时随地写Python代码的神器
  18. SpringBoot+Vue实现前后端分离旅游资源信息系统
  19. QingScan v1.1.0 版本发布会实录
  20. ABAP GIT 使用教程

热门文章

  1. Catia 零件 曲面 装配 工程图 仿真运动 参数和知识工程视频教程
  2. Springer latex期刊模板使用的坑
  3. 看完这篇文章,彻底解决Mediaplayer重复播放视频黑屏的问题
  4. CCRC信息安全服务资质申请要求
  5. 如何微信增粉5万?从偶遇微信地推员说起
  6. 《操作系统》——分页存储管理方式逻辑地址到物理地址的转换
  7. linux 封禁端口,如何在Linux服务器中使用防火墙封禁端口?
  8. MBR分区概述及代码实现分区
  9. 【笔记】ReLU和LeakyReLu函数
  10. 科技攻关 成功构建出国产芯片区块链智能合约专用并行计算卡