golang使用core dump分析定位程序崩溃问题

发布时间 2023-06-25 16:41:23作者: 技术颜良

 

一、前言

core dump 是一个包含着意外终止的程序其内存快照的文件。这个文件可以被用来事后调试(debugging)以了解为什么会发生崩溃,同时了解其中涉及到的变量。通过 GOTRACEBACK,Go 提供了一个环境变量用于控制程序崩溃时生成的输出信息。这个变量同样可以强制生成 core dump,从而使调试成为可能。

1.1 GOTRACEBACK

GOTRACEBACK 控制程序崩溃时输出的详细程度。它可以采用不同的值:

  • none 不显示任何goroutine栈trace。

  • single, 默认选项,显示当前goroutine栈trace。

  • all 显示所有用户创建的goroutine栈trace。

  • system 显示所有goroutine栈trace,甚至运行时的trace。

  • crash 类似 system, 而且还会生成 core dump。

二、功能设置

2.1 系统设置

 

开启core dump 功能:

ulimit -c unlimited

设置core file size = unlimited,core dump file 的文件的大小无限制。也可以根据自己的需要把core file 设置为特定的大小如:

ulimit -c 1024

需要注意的是core file size 的单位是block, block = 512字节。因通过ulimit 设置core file size 只对当前的终端起作用,当你关闭终端或者重启系统设置会失效。为了避免每次都设置的麻烦,可以在~/.profile 最后添加:

echo "ulimit -c unlimited" >> ~/.profile

设置core dump 文件的位置

*kernel.core_pattern=/var/core/core%t%p_%e*%t: 生成的文件的时间戳%p:发生断言的进程的id%e:发生断言的的代码文件

执行:

sysctl -p /etc/sysctl.conf

 

2.2 GO设置

设置Go环境变量

export GOTRACEBACK=crash

需要注意的是和ulmit 命令一样,在关闭终端和重启系统后,设置的GOTRACEBACK变量会消失,所以要将’export GOTRACEBACK=crash’ 加入到 .profile文件中

echo "export GOTRACEBACK=crash " >> ~/.profile

三、案例分析

 

GOTRACEBAK变量可以控制程序在崩溃时,stack的输出情况。下面结合具体地程序来分析。

package main
import ( "time"
"github.com/astaxie/beego/logs")
func main() { logs.Info("Start...") defer logs.Info("exit.") i := 0 c := make(chan int, 1) for { go func(i int) { mem := make([]int, 100*1024*1024) logs.Info("i=%d,mem:%p", i, mem) mem[0] = <-c }(i) i++ time.Sleep(200 * time.Microsecond) }}

该程序将很快崩溃,产生如下报错:

goroutine 279 [running]:  goroutine running on other thread; stack unavailablecreated by main.main  /opt/gopath/src/test/coredump_test/testcoredump.go:15 +0xdf
goroutine 290 [running]: goroutine running on other thread; stack unavailablecreated by main.main /opt/gopath/src/test/coredump_test/testcoredump.go:15 +0xdfAborted (core dumped)

gdb可以进行调试,查看程序运行的详细情况:

 gdb testcoredump core.15956GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-110.el7Copyright (C) 2013 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.  Type "show copying"...(gdb) startTemporary breakpoint 1 at 0x618c50: file /opt/gopath/src/test/coredump_test/testcoredump.go, line 9.Starting program: /opt/gopath/src/test/coredump_test/testcoredump[Thread debugging using libthread_db enabled]Using host libthread_db library "/lib64/libthread_db.so.1".[New Thread 0x7ffff77f1700 (LWP 15980)][New Thread 0x7ffff6ff0700 (LWP 15981)][New Thread 0x7ffff5fee700 (LWP 15983)][New Thread 0x7ffff67ef700 (LWP 15982)][New Thread 0x7ffff57ed700 (LWP 15984)]
Temporary breakpoint 1, main.main () at /opt/gopath/src/test/coredump_test/testcoredump.go:99 func main() {(gdb)

gdb常用命令:

start    //开始调试n    //一条一条执行step/s    //执行下一条,如果函数进入函数backtrace/bt    //查看函数调用栈帧info/i locals    //查看当前栈帧局部变量frame/f    //选择栈帧,再查看局部变量print/p    //打印变量的值finish    //运行到当前函数返回set var sum=0    //修改变量值list/l 行号或函数名    //列出源码display/undisplay sum    //每次停下显示变量的值/取消跟踪break/b  行号或函数名    //设置断点continue/c    //连续运行info/i breakpoints    //查看已经设置的断点delete breakpoints 2    //删除某个断点disable/enable breakpoints 3    //禁用/启用某个断点break 7 if ok == true    //满足条件才激活断点run/r    //重新从程序开头连续执行watch input[7]    //设置观察点info/i watchpoints    //查看设置的观察点x/7b input    //打印存储器内容,b--每个字节一组,7--7组disassemble    //反汇编当前函数或指定函数 si    // 一条指令一条指令调试 而 s 是一行一行代码 info registers    // 显示所有寄存器的当前值x/20 $esp    //查看内存中开始的20个数

四、总结

程序崩溃可以通过coredump详细地查看程序调用栈的相关信息,可以更迅速的定位到程序的问题,特别是引起程序崩溃的bug:内存泄漏,一些panic等,当然在写程序时尽量多些log更方便调试。golang自带的pprof在涉及到c库的调用时,会监测不到,这时coredump结合gdb进行调试会比较有用。