- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
作者: @apocelipes 本文为作者原创,转载请注明出处: https://www.cnblogs.com/apocelipes/p/17536722.html 。
今天讲讲怎么让golang程序生成coredump文件,并且进行调试的.
别看我写了不少golang的博客,其实我平时写c++的时间更多,所以也算和coredump是老相识了。 core dump 文件实际上是进程在某个时间点时的内存映像,当时进程使用的内存是啥样就会被原样保存下来存在文件系统的某个位置上,这个时间点一般是触发了 SIGSEGV 或者 SIGABRT 这两个信号的时候,当进程的内存映像保存完毕后进程就会异常终止,也就是大家喜闻乐见的“程序崩了”和“段错误:核心已转储”.
因此coredump就像是程序出错崩溃后的“第一现场”,是用来排查错误的主要资源.
不过我很少在golang里调试coredump文件,通常来说可靠的日志和panic时打印的错误信息加堆栈就足够定位错误了。然而有时光靠这些信息还不够,不得不去求助老朋友coredump了.
下面我们主要针对这段代码调试,这只是个事例,所以你一眼看出问题在哪了也不要介意:
package main
import (
"fmt"
"math/rand"
)
func main() {
arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
for {
index := rand.Intn(11)
fmt.Println(arr[index])
}
}
编译并运行这段代码,运行上一小会儿就会看到程序panic了。假设报错信息没能帮助我们定位问题,接下来我们看看如何用coredump调试golang程序.
首先,如果你不做任何额外的设置,那么golang程序崩溃的时候只会打印崩溃信息和简单的调用栈信息,并不会生成coredump文件.
想改变这个行为有两种方式:设置环境变量和在代码里调用相关的标准库接口.
在这之前先用ulimit命令检测下系统当前能不能生成coredump:
$ ulimit -c
unlimited
如果是unlimited就表示可以,如果是0那就不会生成,需要修改ulimit的设置.
我们先看修改环境变量的办法.
GOTRACEBACK 是用来控制panic发生时golang程序行为的,值是字符串,具体内容如下:
值 | 行为 |
---|---|
none | 不打印任何堆栈跟踪信息,不过崩溃的原因和哪行代码触发的panic还是会打印 |
single | 只打印当前正在运行的触发panic的goroutine的堆栈以及runtime的堆栈;如果panic是runtime里发出的,则打印所有goroutine的堆栈跟踪信息 |
all | 打印所有用户创建的goroutine的堆栈信息(不包含runtime的) |
system | 在前面 all 的基础上把runtime相关的所有协程的堆栈信息也一起打印出来 |
crash | 打印的内容和前面 system 一样,但还会额外生成对应操作系统上的coredump文件 |
将这个环境变量设置成 crash 就可以获得信息最全面的coredump文件。所以我们要做的就是像下面这样:
go build main.go
GOTRACEBACK=crash ./main
或者你嫌麻烦,那就在服务器系统里做全局设置,一般是修改/etc/profile:
# 其他内容
# 全局设置,需要让所有已登录的用户注销会话重新登录或者干脆重启系统才会生效
export GOTRACEBACK=crash
上面的全局设置是针对Linux的,Windows就按正常设置环境变量那样操作,然后重新登录用户即可.
这样运行后就会生成coredump文件了。一般会生成在当前的工作目录里.
还有一点要注意:如果你正在使用较新的linux发行版, 那么coredump文件会被 coredumpctl 接管,并不会生成在当前目录 :
可以看到coredump文件被集中管理了,使用info子命令可以看到存放这些文件的路径和崩溃的进程的信息:
其中的 present 表示coredump的文件还保存着,可以用来调试, missing 的哪些就代码coredump文件已经没了.
想要用dlv来调试的话得用这样的命令:
coredumpctl debug <list那给出的崩溃的进程的id> --debugger=<调试器程序的名字或路径> -A <传给调试器的参数>
填一下空就是这样:
coredumpctl debug 156814 --debugger=dlv -A core ./main
这样就能正常进行调试了。另外编译main程序的时候记得把优化关了,以免代码被优化得和写的不一样导致没法调试.
coredumpctl 除了把coredump文件压缩了一下节约了一点硬盘空间之外没有什么优势,整个就体现了systemd家族的臭毛病:多管闲事.
没有标准库函数可以主动触发coredump生成,但有可以在代码里设置panic时候的行为的,使用的值和 GPTRACEBACK 一模一样:
debug.SetTraceback 。
这个函数优先级比环境变量高,但有个限制,它只能设置比环境变量的值打印更多信息的值,也就是说如果环境变量是 all ,那么这个函数就只能设置 system 和 crash ,不能设置 none 和 single .
代码例子:
package main
import (
"fmt"
"math/rand"
+ "runtime/debug"
)
func main() {
+ debug.SetTraceback("crash")
arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
for {
index := rand.Intn(11)
fmt.Println(arr[index])
}
}
效果和设置环境变量一样,这里就不展示了.
没什么特别的需求的话,我推荐你只用 GOTRACEBACK 环境变量.
环境变量可以在不修改代码或者配置文件的情况下控制程序的行为,不需要花时间改代码改配置然后再编译运行。用标准库的接口想达到类似效果就得写不少代码了.
还有个好处是方便在容器里管理,也符合云原生十二要素.
coredump里保存了程序崩溃前的所有状态,包括执行到哪行代码了,各个变量的值是什么,还包含了runtime当前的状态等等.
仔细检查这些信息就可以发现程序崩溃的原因.
还是用这条命令打开调试器:
coredumpctl debug 156814 --debugger=dlv -A core ./main
然后按下面的步骤查看信息:
bt
,查看当前的调用堆栈,找到触发panic的那行代码在哪个frame(栈帧)里 frame 10
进入这个栈帧 locals
查看当前栈帧内变量的值 p <变量名/表达式>
查看变量的具体内容,或者执行一些简单的表达式
这里的问题很明显:数组长度是10,索引最大只有9,而index变量的值是10。所以索引访问越界,导致了panic.
Q: 上面只说了panic的时候生成coredump,如果我想要个程序正常运行时的快照该怎么做?
A: Linux上有不少进程内存快照生成工具,不过delve内置的交互式命令 dump 就可以满足需求.
具体方法是 dlv attach <pid> 之后直接运行 dump <输出coredump的文件名> 命令,然后退出。或者还有全自动化的:
$ echo 'dump coredump'|dlv attach <pid> ./main --allow-non-terminal-interactive
$ ls -lh
总计 47M
-rw-r--r-- 1 a a 45M 7月 8日 00:34 coredump
-rw-r--r-- 1 a a 25 7月 8日 00:20 go.mod
-rwxr-xr-x 1 a a 1.8M 7月 8日 00:31 main
-rw-r--r-- 1 a a 141 7月 8日 00:30 main.go
可以看到当前目录下生成了一个名为“coredump”的coredump文件.
这个命令本身比较耗时, 进程用的内存越多就越慢,请谨慎在生产环境使用 .
Q: 这个例子里没看出来有调试coredump的必要.
A: 是这个例子的问题,它不够好。我可以简单举一个以前遇到的真实情况:
以前有个处理用户输入的程序,用户可以输入任何utf8字符,程序会简单处理这些字符然后存到一块内存里,这东西上线后隔三差五就会panic,每次都是越界访问,但越界的值和发生的时间都没有规律可言.
最后实在没办法,抓了一次coredump,仔细检查了用户的输入,发现是我们的代码在处理某些特殊字符时想当然了,没能正确处理数据的长度。如果光看代码本身的话这个问题很难排查.
至于为什么不把用户输入打进日志,这涉及了隐私和权益问题,不能这么做,但调试完coredump后删除勉强能规避这些问题.
Q: 我有必要总是开启coredump吗?
A: 没有。正如我前面所说,一般日志和panic打印的信息就够用了。coredump本身会占据很多磁盘空间,而且在容器里dump下来的东西容器重启后就没了,除非单独设置数据卷但这非常复杂.
Q: 一些web框架会用recover处理panic,请问这时候还能获得coredump吗?
A: 不能 。被recover的panic不会触发coredump。这时候你得想想其他办法了,比如用第一个QA那的办法生成个实时快照.
coredump对于golang来说并不常用,但技多不压身,了解一下对以后处理各种问题总是有帮助的.
https://github.com/go-delve/delve/blob/master/Documentation/usage/dlv_attach.md 。
https://pkg.go.dev/runtime 。
https://linderud.dev/blog/coredumpctl-delve-and-debug-packages-for-go/ 。
最后此篇关于让golang程序生成coredump文件并进行调试的文章就讲到这里了,如果你想了解更多关于让golang程序生成coredump文件并进行调试的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
昨晚我因为这个问题脑子崩溃了。在确保没有来 self 的 eclipse 错误检查的明显错误之后,我开始调试我的程序。顺便说一下,我正在使用 Jre7。无论如何,每次我进入我的类调用(我们称之为“a”
(前言:我对 C/C++ 还很陌生,我真的不知道 native 代码中的调试实际上是如何工作的。) 一些消息来源说 gdb 和 lldb 可以调试 any program compiled to ma
我正在尝试从 Visual Studio 2012 外部调试 T4Scaffolding.Core Nuget 包。我使用的是安装了 Powershell 3.0 的 Powershell ISE,并
如何调试汇编代码?我在 Linux 上使用 gdb。我知道我可以看寄存器。有哪些调试汇编代码的方法? 最佳答案 您当然可以使用 breakpoints就像 C 或任何其他编译语言一样。 This ar
如何在每次通话时打印列表或 haskell 中的内容,例如: funct a list = funct (a + 1) (a : list) print list her
让我用我对 Makefiles 或 make 知之甚少的评论作为这个问题的前缀。 有一个非常大的项目,每晚自动构建。它以 Debug 和 Release 模式构建,Debug 用于 Valgrind
我正在创建一个计算每周工资的程序,那么任何加类工资都是该周正常工资的 1.5 倍。我的代码如下: #include int main() { double payrate; double h
我使用的是 Visual Studio 2010 Express Developer 版本。开发网站。我在我的 .aspx 页面中使用 JavaScript。 如何在 Javascript 中放置断点
我最近开始修补 Project Euler 问题,并尝试用 Javascript 解决它们。这样做我往往会产生许多无限循环,现在我想知道是否有比终止 Firefox 或 Chrome 中的选项卡更好的
有没有办法在程序执行期间生成一个交互式 python 控制台(最好是 iPython)而不暂停主程序并且能够检查和修改程序变量?类似于浏览器为 JavaScript 提供的功能。 我知道 pdb.se
我正在使用 FFmpeg @ Android 并希望能够进入 FFmpeg 代码(Eclipse + Seqouya),同时编译 FFmpeg 我使用 --disable-stripping --en
我从使用互操作调用 win32 api 函数的 .net 进程中得到一个异常。 我有一个调试器,我想查看 LastError 的值。 是否可以从 Visual Studio 调试器中查看 LastEr
我正在尝试通过 VBA 创建一个宏,以在 IE 的多个选项卡中打开一组指定的链接。目前我正在使用下面的代码,如果我试图打开 3 个或更少的选项卡,它大部分时间都可以工作。任何超过 3 的代码都会在“N
好的,这似乎是一个愚蠢的问题,因为 MonoDevelop 越来越成熟,所以我确定我只是想念它,但我环顾四周,所有关于这个主题的问题似乎都是关于远程调试或 Mac 上的调试。 我使用的是 Ubuntu
如何调试 Rscripts是从命令行运行的? 我目前正在使用 getopt传递命令行选项的包,当有错误时,我很难: 看看到底出了什么问题; 在 R 中交互式调试(因为脚本需要命令行选项。) 有没有人有
支持 PDF 和网络上的信息很少。我碰巧在博客中看到一篇文章,提到 $.write() 或 $.writeln() 将向 javascript 控制台写入一个字符串。相当有用。有谁知道这个 $ 对象是
PyCharm 1.5 中是否可以使用 Firefox 和 Chrome 支持的 JavaScript 调试? 如果是这样,它能否与 Python/Django 调试器一起有效运行? 如果没有,有没有
我确定这以前发生在人们身上,某些东西在 Debug模式下工作,你在发布时编译,但有些东西坏了。 这发生在我在嵌入式 XP 环境中工作时,我发现最好的方法确实是编写一个日志文件来确定它会出错的地方。 您
我目前正在为即将到来的项目评估 Flow3。 AOP 模式和依赖注入(inject)将非常适合我们的目的。 现在我想不通的是如何在 Controller Action 中调试一些结果。 public
最初,我有一个包含测试服务器的 Django 应用程序。要调试此设置,我只需添加 import pdb; pdb.set_trace()代码中的任何位置,并且有一个断点将我扔到终端中的交互式调试器中(
我是一名优秀的程序员,十分优秀!