Debugging Go程序运行时错误并打印出所有内容

Debugging Go程序运行时错误并打印出所有内容,debugging,go,Debugging,Go,我的Go代码使用了数百个goroutine。运行时错误可能会不时发生。但当错误发生时,它只会打印出所有goroutine的堆栈跟踪,从而无法调试 如何定位程序中断的位置 Sry我之前没有发布堆栈跟踪,我不知道如何将stderr打印到堆栈,并且输出太长,所以我无法查看所有跟踪 fatal error: unexpected signal during runtime execution [signal SIGSEGV: segmentation violation code=0x1 addr=0x

我的Go代码使用了数百个goroutine。运行时错误可能会不时发生。但当错误发生时,它只会打印出所有goroutine的堆栈跟踪,从而无法调试

如何定位程序中断的位置

Sry我之前没有发布堆栈跟踪,我不知道如何将stderr打印到堆栈,并且输出太长,所以我无法查看所有跟踪

fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0x141edce pc=0x141edce]

runtime stack:
runtime: unexpected return pc for runtime.sigpanic called from 0x141edce
stack: frame={sp:0x7ffbffffa9f0, fp:0x7ffbffffaa40} stack=[0x7ffbff7fbb80,0x7ffbffffabb0)
00007ffbffffa8f0:  00007ffbffffa960  000000000042b58c <runtime.dopanic_m+540> 
00007ffbffffa900:  000000000042b031 <runtime.throw+129>  00007ffbffffa9d0 
00007ffbffffa910:  0000000000000000  000000000097f880 
00007ffbffffa920:  010000000042bae8  0000000000000004 
00007ffbffffa930:  000000000000001f  000000000141edce 
00007ffbffffa940:  000000000141edce  0000000000000001 
00007ffbffffa950:  00000000007996e6  000000c420302180 
00007ffbffffa960:  00007ffbffffa988  00000000004530ac <runtime.dopanic.func1+60> 
00007ffbffffa970:  000000000097f880  000000000042b031 <runtime.throw+129> 
00007ffbffffa980:  00007ffbffffa9d0  00007ffbffffa9c0 
00007ffbffffa990:  000000000042af5a <runtime.dopanic+74>  00007ffbffffa9a0 
00007ffbffffa9a0:  0000000000453070 <runtime.dopanic.func1+0>  000000000097f880 
00007ffbffffa9b0:  000000000042b031 <runtime.throw+129>  00007ffbffffa9d0 
00007ffbffffa9c0:  00007ffbffffa9e0  000000000042b031 <runtime.throw+129> 
00007ffbffffa9d0:  0000000000000000  000000000000002a 
00007ffbffffa9e0:  00007ffbffffaa30  000000000043fb1e <runtime.sigpanic+654> 
00007ffbffffa9f0: <000000000079dce7  000000000000002a 
00007ffbffffaa00:  00007ffbffffaa30  000000000041f08e <runtime.greyobject+302> 
00007ffbffffaa10:  000000c420029c70  000000000097f880 
00007ffbffffaa20:  000000000045247d <runtime.markroot.func1+109>  000000c420a69b00 
00007ffbffffaa30:  00007ffbffffaad8 !000000000141edce 
00007ffbffffaa40: >000000c42160ca40  000000c4206d8000 
00007ffbffffaa50:  0000000000000c00  000000c41ff4f9ad 
00007ffbffffaa60:  000000c400000000  00007efbff5188f8 
00007ffbffffaa70:  000000c420029c70  0000000000000052 
00007ffbffffaa80:  0000000021e84000  00007ffbffffaab0 
00007ffbffffaa90:  0000000000002000  0000000000000c00 
00007ffbffffaaa0:  000000c422b00000  000000c420000000 
00007ffbffffaab0:  00007ffbffffaad8  0000000000421564 <runtime.(*gcWork).tryGet+164> 
00007ffbffffaac0:  000000c41ffc939f  000000c4226eb000 
00007ffbffffaad0:  000000c4226e9000  00007ffbffffab30 
00007ffbffffaae0:  000000000041e527 <runtime.gcDrain+567>  000000c4206d8000 
00007ffbffffaaf0:  000000c420029c70  0000000000000000 
00007ffbffffab00:  7ffffffffff8df47  00007ffc0001fc30 
00007ffbffffab10:  00007ffbffffab70  0000000000000000 
00007ffbffffab20:  000000c420302180  0000000000000000 
00007ffbffffab30:  00007ffbffffab70  00000000004522c0 <runtime.gcBgMarkWorker.func2+128> 
runtime.throw(0x79dce7, 0x2a)
    /usr/lib/go-1.10/src/runtime/panic.go:616 +0x81
runtime: unexpected return pc for runtime.sigpanic called from 0x141edce
stack: frame={sp:0x7ffbffffa9f0, fp:0x7ffbffffaa40} stack=[0x7ffbff7fbb80,0x7ffbffffabb0)
00007ffbffffa8f0:  00007ffbffffa960  000000000042b58c <runtime.dopanic_m+540> 
00007ffbffffa900:  000000000042b031 <runtime.throw+129>  00007ffbffffa9d0 
00007ffbffffa910:  0000000000000000  000000000097f880 
00007ffbffffa920:  010000000042bae8  0000000000000004 
00007ffbffffa930:  000000000000001f  000000000141edce 
00007ffbffffa940:  000000000141edce  0000000000000001 
00007ffbffffa950:  00000000007996e6  000000c420302180 
00007ffbffffa960:  00007ffbffffa988  00000000004530ac <runtime.dopanic.func1+60> 
00007ffbffffa970:  000000000097f880  000000000042b031 <runtime.throw+129> 
00007ffbffffa980:  00007ffbffffa9d0  00007ffbffffa9c0 
00007ffbffffa990:  000000000042af5a <runtime.dopanic+74>  00007ffbffffa9a0 
00007ffbffffa9a0:  0000000000453070 <runtime.dopanic.func1+0>  000000000097f880 
00007ffbffffa9b0:  000000000042b031 <runtime.throw+129>  00007ffbffffa9d0 
00007ffbffffa9c0:  00007ffbffffa9e0  000000000042b031 <runtime.throw+129> 
00007ffbffffa9d0:  0000000000000000  000000000000002a 
00007ffbffffa9e0:  00007ffbffffaa30  000000000043fb1e <runtime.sigpanic+654> 
00007ffbffffa9f0: <000000000079dce7  000000000000002a 
00007ffbffffaa00:  00007ffbffffaa30  000000000041f08e <runtime.greyobject+302> 
00007ffbffffaa10:  000000c420029c70  000000000097f880 
00007ffbffffaa20:  000000000045247d <runtime.markroot.func1+109>  000000c420a69b00 
00007ffbffffaa30:  00007ffbffffaad8 !000000000141edce 
00007ffbffffaa40: >000000c42160ca40  000000c4206d8000 
00007ffbffffaa50:  0000000000000c00  000000c41ff4f9ad 
00007ffbffffaa60:  000000c400000000  00007efbff5188f8 
00007ffbffffaa70:  000000c420029c70  0000000000000052 
00007ffbffffaa80:  0000000021e84000  00007ffbffffaab0 
00007ffbffffaa90:  0000000000002000  0000000000000c00 
00007ffbffffaaa0:  000000c422b00000  000000c420000000 
00007ffbffffaab0:  00007ffbffffaad8  0000000000421564 <runtime.(*gcWork).tryGet+164> 
00007ffbffffaac0:  000000c41ffc939f  000000c4226eb000 
00007ffbffffaad0:  000000c4226e9000  00007ffbffffab30 
00007ffbffffaae0:  000000000041e527 <runtime.gcDrain+567>  000000c4206d8000 
00007ffbffffaaf0:  000000c420029c70  0000000000000000 
00007ffbffffab00:  7ffffffffff8df47  00007ffc0001fc30 
00007ffbffffab10:  00007ffbffffab70  0000000000000000 
00007ffbffffab20:  000000c420302180  0000000000000000 
00007ffbffffab30:  00007ffbffffab70  00000000004522c0 <runtime.gcBgMarkWorker.func2+128> 
runtime.sigpanic()
    /usr/lib/go-1.10/src/runtime/signal_unix.go:372 +0x28e

实际上,它通过转储这些堆栈来简化调试。 您可能不熟悉这种尸检分析方法,但这是可以解决的-

首先要注意的是,在正常的Go代码中,panic/recover机制不用于控制流,因此当一些goroutine出现恐慌时,它通常有一个非常严重的理由这样做。反过来,这意味着,此类原因通常仅限于一组不太广泛的可能原因,并且在100%的此类情况下,它表示程序中存在逻辑错误:尝试取消引用统一的nil指针,尝试发送到封闭通道,等等。 当然,问题可能与第三方代码有关,或者与您使用它的方式有关

好的,要开始分析发生了什么,第一件事是不要认为这是发生了什么错误:相反,发生了一些特定的错误,Go运行时会及时向您显示所有goroutine的状态

因此,首先要做的是实际读取并理解显示的错误。它包含导致Go运行时使程序崩溃的直接原因-可能是零指针释放、内存耗尽、试图关闭关闭通道等

第二件要做的事情——一旦清楚地理解了错误的本质——就是分析堆栈跟踪转储是否有用。很简单:所有运行时错误都可以分为两大类:低级或高级。前者是发生在Go运行时本身深处的事件。分配内存失败就是最好的例子。这些错误甚至可能指示运行时中的错误,尽管在实践中不太可能看到这些错误,除非您使用Go工具集的前沿构建来构建您的程序。这些错误的主要特点是,它们可能与错误发生的确切地点没有多大关系。比如说,分配内存的失败可能是由一些无辜的分配触发的,而一些真正的内存占用者在之前成功地获得了一大块内存

但这样的错误很少发生,而且高级错误发生的频率要高得多。使用它们,检查堆栈跟踪会有很大帮助

在这种情况下,你像这样滚动。 堆栈跟踪转储由导致错误的调用链的堆栈帧的描述组成:发生错误的函数的堆栈帧位于顶部,其调用者位于下方,调用者的调用者位于行的下一个,依此类推-直到执行goroutine的入口点。 每个堆栈帧的描述都包括函数名、定义函数的文件名以及发生错误的语句的行号

这本身就非常有用:您在程序的源代码中找到该语句,眯着眼睛看它,同时记住指示的错误发生在那里,然后开始向后分析它是如何发生的,从而使它发生在那里。如果不清楚该语句前面的函数代码,则可能有助于分析调用方的堆栈帧,其中还包括文件名和行号等

在大多数情况下,上述内容就足够了。 在极少数情况下,如果没有,分析函数的参数(也被转储的堆栈帧捕获)可能会有所帮助

参数的值按源代码顺序列出-从左到右;解释它们的唯一问题是解码那些复合类型的参数,例如字符串、切片、使用的定义结构类型等

比如说,字符串是由两个字段组成的结构,在参数列表中,这些字段将依次展开

但现在我们不要挖得太深。这里还有其他需要探索的东西,比如说,我已经提到了记忆耗尽错误,但没有解释如何处理它们,但是你最好通过实践来学习

如果您在处理此类问题时有任何具体问题,请直接提问,但一定要包括崩溃goroutine的堆栈跟踪,并描述您自己的分析尝试产生了什么,以及您到底有什么问题

还有另一种方法可以使用

可以为环境变量指定一个特殊值,以告知Go运行时以一种对常规交互式调试器友好的方式使程序崩溃 与之一起工作-例如gdb

例如,您可以启用核心文件转储,然后允许Go运行时以这样的方式使您的进程崩溃,以便操作系统转储其核心:

$ulimit-c无限制 $export GOTRACEBACK=崩溃 美元/你的课程 ... ... 你的程序崩溃了 ... $ls*核心* 果心 $gdb-e./您的课程核心 gdb线程应用所有bt *追踪跟踪* 我想,对核心文件捕获的状态的实际调试是IDE或其他任何东西应该处理的;我演示了如何运行gdb调试器


在bash中运行help ulimit,看看上面的ulimit封装是关于什么的。

@kostix已经给出了很好的答案,但是如果您发布堆栈跟踪,它会更好。哎哟,这正是那些深层运行时内部恐慌的不太可能的情况之一!看起来非常接近1.10rc1的申请。你确定你有Go 1.10的发布版本吗?你能试着用一个更新的版本来构建吗?1.12.7现在是最新的稳定版本,1.13将在8月发布。有一个更新的版本可能会发生,因为一些通过cgo链接的外部C代码会造成随机内存损坏。您的完整堆栈转储是否包括单词cgo的出现?