golang 使用 core dump 分析定位程序崩潰問題
一、前言
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 unavailable
created by main.main
/opt/gopath/src/test/coredump_test/testcoredump.go:15 +0xdf
goroutine 290 [running]:
goroutine running on other thread; stack unavailable
created by main.main
/opt/gopath/src/test/coredump_test/testcoredump.go:15 +0xdf
Aborted (core dumped)
gdb 可以進行調試,查看程序運行的詳細情況:
gdb testcoredump core.15956
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-110.el7
Copyright (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) start
Temporary 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:9
9 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 進行調試會比較有用。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/YiONLLc614BneyCUsHE6pw