欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解如何保留Go程序崩潰現(xiàn)場

 更新時間:2023年12月04日 12:02:55   作者:Golang技術(shù)分享  
這篇文章主要為大家介紹了如何保留Go程序崩潰現(xiàn)場示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

沒有消滅一切的銀彈,也沒有可以保證永不出錯的程序。我們應(yīng)當(dāng)如何捕捉 Go 程序錯誤?我想同學(xué)們的第一反應(yīng)是:打日志。

但錯誤日志的能力是有限的。第一,日志是開發(fā)者在代碼中定義的打印信息,我們沒法保證日志信息能包含所有的錯誤情況。第二,在 Go 程序中發(fā)生 panic 時,我們也并不總是能通過 recover 捕獲(沒法插入日志代碼)。

那線上 Go 程序突然莫名崩潰后,當(dāng)日志記錄沒有覆蓋到錯誤場景時,還有別的方法排查嗎?

core dump

core dump 又即核心轉(zhuǎn)儲,簡單來說它就是程序意外終止時產(chǎn)生的內(nèi)存快照。我們可以通過 core dump 文件來調(diào)式程序,找出其崩潰原因。

在 linux 平臺上,可通過ulimit -c命令查看核心轉(zhuǎn)儲配置,系統(tǒng)默認為 0,表明未開啟 core dump 記錄功能。

$ ulimit -c
0

可以使用ulimit -c [size]命令指定記錄 core dump 文件的大小,即是開啟 core dump 記錄。當(dāng)然,如果電腦資源足夠,避免 core dump 丟失或記錄不全,也可執(zhí)行ulimit -c unlimited而不限制 core dump 文件大小。

那在 Go 程序中,如何開啟 core dump 呢?

GOTRACEBACK

我們在你真的懂string與[]byte的轉(zhuǎn)換了嗎一文中探討過 string 轉(zhuǎn) []byte 的黑魔法,如下例所示。

package main
import (
 "reflect"
 "unsafe"
)
func String2Bytes(s string) []byte {
 sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
 bh := reflect.SliceHeader{
  Data: sh.Data,
  Len:  sh.Len,
  Cap:  sh.Len,
 }
 return *(*[]byte)(unsafe.Pointer(&bh))
}
func Modify() {
 a := "hello"
 b := String2Bytes(a)
 b[0] = 'H'
}
func main() {
 Modify()
}

string 是不可以被修改的,當(dāng)我們將 string 類型通過黑魔法轉(zhuǎn)為 []byte 后,企圖修改其值,程序會發(fā)生一個不能被 recover 捕獲到的錯誤。

$ go run main.go
unexpected fault address 0x106a6a4
fatal error: fault
[signal SIGBUS: bus error code=0x2 addr=0x106a6a4 pc=0x105b01a]

goroutine 1 [running]:
runtime.throw({0x106a68b, 0x0})
 /usr/local/go/src/runtime/panic.go:1198 +0x71 fp=0xc000092ee8 sp=0xc000092eb8 pc=0x102bad1
runtime.sigpanic()
 /usr/local/go/src/runtime/signal_unix.go:732 +0x1d6 fp=0xc000092f38 sp=0xc000092ee8 pc=0x103f2f6
main.Modify(...)
 /Users/slp/github/PostDemo/coreDemo/main.go:21
main.main()
 /Users/slp/github/PostDemo/coreDemo/main.go:25 +0x5a fp=0xc000092f80 sp=0xc000092f38 pc=0x105b01a
runtime.main()
 /usr/local/go/src/runtime/proc.go:255 +0x227 fp=0xc000092fe0 sp=0xc000092f80 pc=0x102e167
runtime.goexit()
 /usr/local/go/src/runtime/asm_amd64.s:1581 +0x1 fp=0xc000092fe8 sp=0xc000092fe0 pc=0x1052dc1
exit status 2

這些堆棧信息是由 GOTRACEBACK 變量來控制打印粒度的,它有五種級別。

  • none,不顯示任何 goroutine 堆棧信息
  • single,默認級別,顯示當(dāng)前 goroutine 堆棧信息
  • all,顯示所有 user (不包括 runtime)創(chuàng)建的 goroutine 堆棧信息
  • system,顯示所有 user + runtime 創(chuàng)建的 goroutine 堆棧信息
  • crash,和 system 打印一致,但會生成 core dump 文件(Unix 系統(tǒng)上,崩潰會引發(fā) SIGABRT 以觸發(fā)core dump)

如果我們將 GOTRACEBACK 設(shè)置為 system ,我們將看到程序崩潰時所有 goroutine 狀態(tài)信息

$ GOTRACEBACK=system go run main.go
unexpected fault address 0x106a6a4
fatal error: fault
[signal SIGBUS: bus error code=0x2 addr=0x106a6a4 pc=0x105b01a]
goroutine 1 [running]:
runtime.throw({0x106a68b, 0x0})
...
goroutine 2 [force gc (idle)]:
runtime.gopark(0x0, 0x0, 0x0, 0x0, 0x0)
...
created by runtime.init.7
 /usr/local/go/src/runtime/proc.go:294 +0x25
goroutine 3 [GC sweep wait]:
runtime.gopark(0x0, 0x0, 0x0, 0x0, 0x0)
...
created by runtime.gcenable
 /usr/local/go/src/runtime/mgc.go:181 +0x55
goroutine 4 [GC scavenge wait]:
runtime.gopark(0x0, 0x0, 0x0, 0x0, 0x0)
...
created by runtime.gcenable
 /usr/local/go/src/runtime/mgc.go:182 +0x65
exit status 2

如果想獲取 core dump 文件,那么就應(yīng)該把 GOTRACEBACK 的值設(shè)置為 crash 。當(dāng)然,我們還可以通過 runtime/debug 包中的 SetTraceback 方法來設(shè)置堆棧打印級別。

delve 調(diào)試

delve 是 Go 語言編寫的 Go 程序調(diào)試器,我們可以通過 dlv core 命令來調(diào)試 core dump。

首先,通過以下命令安裝 delve

go get -u github.com/go-delve/delve/cmd/dlv

還是以上文中的例子為例,我們通過設(shè)置 GOTRACEBACK 為 crash 級別來獲取 core dump 文件

$ tree
.
└── main.go
$ ulimit -c unlimited
$ go build main.go
$ GOTRACEBACK=crash ./main
...
Aborted (core dumped)
$ tree
.
├── core
├── main
└── main.go
$ ls -alh core
-rw------- 1 slp slp 41M Oct 31 22:15 core

此時,在同級目錄得到了 core dump 文件 core(文件名、存儲路徑、是否加上進程號都可以配置修改)。

通過 dlv 調(diào)試器來調(diào)試 core 文件,執(zhí)行命令格式 dlv core 可執(zhí)行文件名 core文件

$ dlv core main core
Type 'help' for list of commands.
(dlv)

命令 goroutines 獲取所有 goroutine 相關(guān)信息

(dlv) goroutines
* Goroutine 1 - User: ./main.go:21 main.main (0x45b81a) (thread 18061)
  Goroutine 2 - User: /usr/local/go/src/runtime/proc.go:367 runtime.gopark (0x42ed96) [force gc (idle)]
  Goroutine 3 - User: /usr/local/go/src/runtime/proc.go:367 runtime.gopark (0x42ed96) [GC sweep wait]
  Goroutine 4 - User: /usr/local/go/src/runtime/proc.go:367 runtime.gopark (0x42ed96) [GC scavenge wait]
[4 goroutines]
(dlv)

Goroutine 1 是出問題的 goroutine (帶有 * 代表當(dāng)前幀),通過命令 goroutine 1 切換到其棧幀

(dlv) goroutine 1
Switched from 1 to 1 (thread 18061)
(dlv)

執(zhí)行命令 bt(breakpoints trace) 查看當(dāng)前的棧幀詳細信息

(dlv) bt
0  0x0000000000454bc1 in runtime.raise
   at /usr/local/go/src/runtime/sys_linux_amd64.s:165
1  0x0000000000452f60 in runtime.systemstack_switch
   at /usr/local/go/src/runtime/asm_amd64.s:350
2  0x000000000042c530 in runtime.fatalthrow
   at /usr/local/go/src/runtime/panic.go:1250
3  0x000000000042c2f1 in runtime.throw
   at /usr/local/go/src/runtime/panic.go:1198
4  0x000000000043fa76 in runtime.sigpanic
   at /usr/local/go/src/runtime/signal_unix.go:742
5  0x000000000045b81a in main.Modify
   at ./main.go:21
6  0x000000000045b81a in main.main
   at ./main.go:25
7  0x000000000042e9c7 in runtime.main
   at /usr/local/go/src/runtime/proc.go:255
8  0x0000000000453361 in runtime.goexit
   at /usr/local/go/src/runtime/asm_amd64.s:1581
(dlv)

通過 5 0x000000000045b81a in main.Modify 發(fā)現(xiàn)了錯誤代碼所在函數(shù),執(zhí)行命令 frame 5 進入函數(shù)具體代碼

(dlv) frame 5
> runtime.raise() /usr/local/go/src/runtime/sys_linux_amd64.s:165 (PC: 0x454bc1)
Warning: debugging optimized function
Frame 5: ./main.go:21 (PC: 45b81a)
    16: }
    17:
    18: func Modify() {
    19:  a := "hello"
    20:  b := String2Bytes(a)
=>  21:  b[0] = 'H'
    22: }
    23:
    24: func main() {
    25:  Modify()
    26: }
(dlv)

自此,破案了,問題就出在了擅自修改 string 底層值。

Mac 不能使用

有一點需要注意,上文 core dump 生成的例子,我是在 linux 系統(tǒng)下完成的,mac amd64 系統(tǒng)沒法弄(很氣,害我折騰了兩個晚上)。

這是由于 mac 系統(tǒng)下的 Go 限制了生成 core dump 文件,這個在 Go 源碼 src/runtime/signal_unix.go 中有相關(guān)說明。

//go:nosplit
func crash() {
 // OS X core dumps are linear dumps of the mapped memory,
 // from the first virtual byte to the last, with zeros in the gaps.
 // Because of the way we arrange the address space on 64-bit systems,
 // this means the OS X core file will be >128 GB and even on a zippy
 // workstation can take OS X well over an hour to write (uninterruptible).
 // Save users from making that mistake.
 if GOOS == "darwin" && GOARCH == "amd64" {
  return
 }
 dieFromSignal(_SIGABRT)
}

總結(jié)

core dump 文件是操作系統(tǒng)提供給我們的一把利器,它是程序意外終止時產(chǎn)生的內(nèi)存快照。利用 core dump,我們可以在程序崩潰后更好地恢復(fù)事故現(xiàn)場,為故障排查保駕護航。

當(dāng)然,core dump 文件的生成也是有弊端的。core dump 文件較大,如果線上服務(wù)本身內(nèi)存占用就很高,那在生成 core dump 文件上的內(nèi)存與時間開銷都會很大。另外,我們往往會布置服務(wù)守護進程,如果我們的程序頻繁崩潰和重啟,那會生成大量的 core dump 文件(設(shè)定了core+pid 命名規(guī)則),產(chǎn)生磁盤打滿的風(fēng)險(如果放開了內(nèi)核限制 ulimit -c unlimited)。

最后,如果擔(dān)心錯誤日志不能幫助我們定位 Go 代碼問題,我們可以為它開啟 core dump 功能,在 hotfix 上增加奇兵。對于有守護進程的服務(wù),建議設(shè)置好 ulimt -c 大小限制。

以上就是詳解如何保留Go程序崩潰現(xiàn)場的詳細內(nèi)容,更多關(guān)于Go程序崩潰保留的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解go語言單鏈表及其常用方法的實現(xiàn)

    詳解go語言單鏈表及其常用方法的實現(xiàn)

    這篇文章主要介紹了詳解go語言單鏈表及其常用方法的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Go?defer?去掉閉包函數(shù)及用法分析

    Go?defer?去掉閉包函數(shù)及用法分析

    這篇文章主要為大家介紹了Go?defer?去掉閉包函數(shù)及用法分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07
  • Go語言實現(xiàn)的web爬蟲實例

    Go語言實現(xiàn)的web爬蟲實例

    這篇文章主要介紹了Go語言實現(xiàn)的web爬蟲,實例分析了web爬蟲的原理與Go語言的實現(xiàn)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02
  • golang簡單獲取上傳文件大小的實現(xiàn)代碼

    golang簡單獲取上傳文件大小的實現(xiàn)代碼

    這篇文章主要介紹了golang簡單獲取上傳文件大小的方法,涉及Go語言文件傳輸及文件屬性操作的相關(guān)技巧,需要的朋友可以參考下
    2016-07-07
  • Golang中如何對MySQL進行操作詳解

    Golang中如何對MySQL進行操作詳解

    這篇文章主要給大家介紹了關(guān)于在Golang中如何對MySQL進行操作的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者使用Golang具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Go單例模式與Once源碼實現(xiàn)

    Go單例模式與Once源碼實現(xiàn)

    這篇文章主要介紹了Go單例模式與Once源碼實現(xiàn),本文結(jié)合示例代碼給大家講解的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-12-12
  • Go日志管理庫zap安裝及使用過程

    Go日志管理庫zap安裝及使用過程

    Zap是Go語言中一個高性能的日志記錄庫,它支持日志切割、多級別日志記錄等功能,zap還支持使用Lumberjack進行日志文件的自動切割和歸檔,適用于需要高效日志管理的Go項目開發(fā),本文介紹Go日志管理庫zap安裝及使用過程,感興趣的朋友一起看看吧
    2024-09-09
  • Go?net?http超時應(yīng)用場景全面詳解

    Go?net?http超時應(yīng)用場景全面詳解

    HTTP是一個復(fù)雜的多階段協(xié)議,因此沒有一個一刀切的超時解決方案,在這篇文章中,我將分解您可能需要應(yīng)用超時的各個階段,并研究在服務(wù)器端和客戶端上執(zhí)行超時的不同方法
    2024-01-01
  • Go Web框架gin的入門教程

    Go Web框架gin的入門教程

    本篇文章主要介紹了Go Web框架gin的入門教程,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • go中如何獲取本機ip地址

    go中如何獲取本機ip地址

    這篇文章主要介紹了go中如何獲取本機ip地址問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09

最新評論