转载本文请联系码农桃花源公众号。大家好,我是小X,曹达最近开了围棋课程,小X跟曹达一起围棋。本系列将讨论从课程中学到的一些有启发性的东西,拨开乌云,带你回到Go。今天给大家介绍几个常用的查看Go汇编代码和调试Go程序的命令和工具。跟同事、网友聊天的时候可以用,关键时候也可以扇耳光。比如同事说这段代码:packagemaintypeStudentstruct{Classint}funcmain(){vara=&Student{1}println(a)}的执行效率要高于下面这段代码:packagemaintypeStudentstruct{Classint}funcmain(){vara=Student{1}varb=&aprintln(b)}并告诉你一个理由,你似乎赢不了他。我应该怎么办?一行命令直接生成汇编代码,立马揭穿他,打他脸。Go工具生成程序集其实很简单,有两条命令就可以做到:gotoolcompile-Smain.go和:gobuildmain.go&&gotoolobjdump./main前者是编译,即将源码编译成.o目标文件并输出汇编代码。后者是反汇编,即从一个可执行文件反编译为汇编,所以必须先使用gobuild命令编译出一个可执行文件。两者不一样,但是可以看出前面两个示例代码对应的汇编代码是一致的。同事的“谣言”弄巧成拙,伤了自己的脸。查找运行时源代码Go是一种带有运行时的语言。什么是运行时?其实就是一个辅助程序。用户没有写的代码是runtime帮我们写的,比如Goscheduler的代码。我们只需要知道如何使用go关键字来创建goroutines,然后我们就可以疯狂地堆积业务了。至于goroutine是怎么调度的,完全不用关心。这些是运行时调度程序的作业。那么我们写的代码和runtime中的代码怎么对应呢?前面介绍的方法就可以了,加个grep就可以了。比如我想知道go关键字对应运行时的哪个函数,于是写了一段测试代码:packagemainfuncmain(){gofunc(){println(1+2)}()}因为gofunc(){}()一行代码在第4行,所以在grep的时候加上条件:gotoolcompile-Smain.go|grep"main.go:4"//或gobuildmain.go&&gotoolobjdump./main|grep"main.go:4"gofunc马上可以看到gofunc(){}()对应的是newproc()函数。这时候如果深入研究newproc()函数,你大概就会知道goroutine是怎么创建的了。用dlv调试有同学问,有没有其他方式调试Go和Go程序交互?事实上,有!这就是我们要介绍的dlv调试工具。目前支持调试Go程序。OK之前没怎么研究过,只是一些很简单的命令。这次学习了几个高级命令,相当强大,进一步加深了我对Go的理解。下面我们拿一个任务来讲解一下dlv的使用方法。我们知道将元素附加到nil切片没有错。但是在一个nil映射中插入一个新元素会立即出现panic。为什么是这样?恐慌在哪里?首先写一个让mappanic的示例程序:packagemainfuncmain(){varmmap[int]intm[1]=1}然后用gobuild命令编译生成可执行文件:gobuilda.go然后用dlv进入调试状态:dlvexec./a使用命令b断点,有3种方法:b+地址b+代码行数b+函数名我们需要在赋值的地方加断点地图。首先找到代码位置:cat-na.go看到:hello.go的赋值在第5行,加个断点:(dlv)ba.go:5Breakpoint1setat0x45e55dformain.main()./a.go:5执行c命令,直接运行到断点处:运行到断点处执行disass命令,可以看到汇编指令:disass此时使用si命令执行单条指令,多次执行si,会执行map赋值函数mapassign_fast64:mapassign_fast64当使用单步指令s时,会进入判断h的值为nil的分支,然后执行panic函数:panic至此,当给nil的map赋值时,我们找到了产生恐慌的代码。然后,按照地图找到对应的runtime源码所在的位置,就可以进行更深入的探索了。另外,我们还可以使用bt命令查看调用栈:调用栈可以使用frame1命令跳转到相应位置。这里的1对应图中的a.go:5,也就是我们之前打断的地方,是不是很爽?在上图中,我们也可以清楚的看到用户goroutine其实是一路被goexit函数调用的。当用户goroutine执行完毕后,会回到goexit函数做一些收尾工作。当然,这是题外话。另外,使用dlv还可以做“寻找运行时源代码”的第二部分。综上所述,今天系统地讲了几种通过命令和工具查看用户代码对应的运行时源代码或汇编代码的方法,非常实用。最后总结一下:gotoolcompilegotoolobjdumpdlv使用这些命令和工具,在查看Go源码的过程中可以事半功倍。好了,今天就到这里吧~我是小X,下期见~
