当前位置: 首页 > 科技观察

学习使用GDB调试Go代码

时间:2023-03-22 11:31:17 科技观察

本文转载自微信公众号《脑筋进炸鱼》,作者陈建宇。转载本文请联系脑筋急转弯公众号。大家好,我是炸鱼。在之前的文章中,我们详细介绍了如何使用Delve在Go语言中进行故障排除和调试,对于解决问题很有帮助。但是调试工具肯定比Delve要多。今天我们来介绍第二个神器,那就是:GDB,它补充了我们的调试工具技术栈。根据小伙伴们的反馈,我们会在后面增加IDE调试章节。什么是GDBGDB是类UNIX系统下的程序调试工具,可以让你看到另一个程序在执行时“内部”发生了什么,或者程序崩溃时在做什么。您可以做的事情主要有四类:启动您的程序,指定可能影响其行为的任何内容。使您的程序在指定条件下停止。检查程序停止时会发生什么。更改程序中的内容,以便您可以尝试纠正一个错误的影响并继续了解另一个错误。如果是在MacOS上安装,可以直接使用brew安装:brewinstallgdb如果是在Linux上,可以使用自带的包管理工具安装,但是需要注意HOME目录下的相关配置安装后。安装完成后,执行gdb,可以看到:$gdbGNUgdb(GDB)10.2...(gdb)写这篇文章的时候gdb最新版本已经是10.2了,我也升级了。这不是一个大问题,但还有更多的功能。编译,我们还是使用之前的demo程序进行调试。但是由于Go语言的编译优化很多,在编译运行程序的时候需要注意以下几点:编译gobuild的时候需要加上-gcflags=all="-N-l"命令关闭内联优化以便于访问。下来调试。对于MacOS,使用gobuild编译时需要添加-ldflags='-compressdwarf=false'指令。如果不禁止,则加载错误Nosymboltable。使用“文件”命令。会出现。Go编译默认会压缩DWARF调试信息,以减小二进制大小,但这会影响gdb的调试,所以需要关闭。编译后的命令为:$gobuild-gcflags=all="-N-l"-ldflags='-compressdwarf=false'。输出:!GDB有两种调试模式,分别是TextUserInterface(简称tui)和默认的命令行模式://debugginginterface$gdb-tui./awesome-project//命令行模式$gdb./awesome-project接下来我们使用gdbtui的debug模式给大家演示功能。我们执行命令gdb-tui./awesome-project后,窗口会切换成如下:在gdbtui的初始界面,你会发现中间有“NoSourceAvailable”的提示。这时候需要继续回车两次,会自动加载Plug-insupport,提示:“LoadingGoRuntimesupport.”。我们可以看到具体的代码块内容,如下:使用macOS的同学需要注意,如果在断点处发现如下错误:(gdb)bmain.mainBreakpoint1at0x10a2ea0:file/Users/eddycjy/go-application/awesomeProject/main.go,line15.(gdb)rStartingprogram:/Users/eddycjy/go-application/awesomeProject/helloUnabletofindMachtaskportforprocess-id64212:(os/kern)failure(0x5).(pleasecheckgdbicodesigned-seetaskgated(8))也是“请检查gdbiscodesigned-seetaskgated(8)”,需要重新处理证书认证和授权,这是MacOS使用中的一个问题。详情请参考:《Codesign gdb on OSX》。解决后,我们的gdb就可以正常运行了!常用的gdb命令在gdb里面,还有dlv等常用的关键字命令。当然,gdb的helpalloutputs很多:(gdb)helpallCommandclass:aliasesCommandclass:breakpointsawatch--Setawatchpointforanexpression.break,brea,bre,br,b--Setbreakpointatspecifiedlocation.break-range--Setabreakpointforanaddressrange.catch--Setcatchpointstocatchevents...。常用关键字如下:b:break的缩写,作用是打断点,例如:main.main,可以包含代码行数。r:run的缩写,作用是让程序运行到下一个断点。c:continue的缩写,作用是继续执行到下一个断点。s:step的缩写,功能是单步执行,如果有被调用的方法,就会进入该方法。l:list的缩写,作用是查看对应的源代码。n:next的缩写,函数是单步执行的,不会进入被调用的方法。q:quit的缩写,作用是退出。infobreakpoints:功能是查看所有设置的断点信息。infolocals:作用是查看变量信息。infoargs:作用是查看函数的输入输出参数的具体取值。infogoroutines:功能是查看goroutines的信息。goroutine1bt:功能是查看指定序号的goroutine调用栈。调试在调试方面和dlv类似,也是先执行关键字b断点:(gdb)bmain.mainBreakpoint1at0x10cbaa0:file/Users/eddycjy/go-application/awesomeProject/main.go,line9。也可以先执行关键字l查看对应代码再决定:(gdb)lmain.main4"fmt"56"github.com/eddycjy/awesome-project/stringer"7)89funcmain(){10fmt.Println(stringer.Reverse("brainIt'stimetofryfish!"))11}查看对应goroutine的运行函数:(gdb)infogoroutines1waitingruntime.gosched*13runningruntime.goexit根据pprof等得到的goroutine序号进行进一步分析.:(gdb)goroutine1bt#00x000000000040facbinruntime.gosched()at/home/user/go/src/runtime/proc.c:873#10x00000000004031c9inruntime.chanrecv(c=void,ep=void,selected=void,received=void)在/home/user/go/src/runtime/chan.c:342#20x0000000000403299inruntime.chanrecv1(t=void,c=void)at/home/user/go/src/runtime/chan.c:423#30x000000000043075bintesting。RunTests(matchString...注意一个细节,gdb调试可以看到和断点分析runtime包内容的代码。也可以像dlv一样执行p关键字,输出对应值的类型和内容:(gdb)pre(gdb)pt$1=(structtesting.T*)0xf840688b60(gdb)pt$1=(structtesting.T*)0xf840688b60(gdb)p*t$2={errors="",failed=false,ch=0xf8406f5690}(gdb)p*t->ch$3=structhchan<*testing.T>类似于dlv。总结一般来说,在MacOS上使用gdb还是比较麻烦的,但是在Linux环境下使用gdb会比较方便。由于dlv和gdb在一般调试中不会相差太远,本文不展开过多。如果分析业务代码,推荐使用dlv,也就是我们上篇文章讲到的。如果对运行时库有调试需求,建议使用gdb作为主要调试工具。如果没有这个要求,建议使用dlv。