讓golang程式生成coredump文件併進行調試

来源:https://www.cnblogs.com/apocelipes/archive/2023/07/08/17536722.html
-Advertisement-
Play Games

今天講講怎麼讓golang程式生成coredump文件,並且進行調試的。 別看我寫了不少golang的博客,其實我平時寫c++的時間更多,所以也算和coredump是老相識了。`core dump`文件實際上是進程在某個時間點時的記憶體映像,當時進程使用的記憶體是啥樣就會被原樣保存下來存在文件系統的某個 ...


今天講講怎麼讓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

首先,如果你不做任何額外的設置,那麼golang程式崩潰的時候只會列印崩潰信息和簡單的調用棧信息,並不會生成coredump文件。

想改變這個行為有兩種方式:設置環境變數和在代碼里調用相關的標準庫介面。

在這之前先用ulimit命令檢測下系統當前能不能生成coredump:

$ ulimit -c
unlimited

如果是unlimited就表示可以,如果是0那就不會生成,需要修改ulimit的設置。

修改GOTRACEBACK環境變數

我們先看修改環境變數的辦法。

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子命令可以看到存放這些文件的路徑和崩潰的進程的信息:

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,那麼這個函數就只能設置systemcrash,不能設置nonesingle

代碼例子:

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

coredump里保存了程式崩潰前的所有狀態,包括執行到哪行代碼了,各個變數的值是什麼,還包含了runtime當前的狀態等等。

仔細檢查這些信息就可以發現程式崩潰的原因。

還是用這條命令打開調試器:

coredumpctl debug 156814 --debugger=dlv -A core ./main

然後按下麵的步驟查看信息:

  1. bt,查看當前的調用堆棧,找到觸發panic的那行代碼在哪個frame(棧幀)里
  2. 看到是編號為10的frame,使用frame 10進入這個棧幀
  3. 使用locals查看當前棧幀內變數的值
  4. p <變數名/表達式>查看變數的具體內容,或者執行一些簡單的表達式
  5. 還可以修改變數的值,設置斷點後再次運行查看結果,不過例子里的問題到第四步就已經明瞭了。

coredump-debug

這裡的問題很明顯:數組長度是10,索引最大隻有9,而index變數的值是10。所以索引訪問越界,導致了panic。

QA

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/


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # 1.用戶角色配置 ![image.png](https://cdn.nlark.com/yuque/0/2023/png/38371876/1688636206975-acd927ca-1559-4236-85f2-07283999d50b.png#averageHue=%23f3f2f2&cl ...
  • Java存在很多ORM框架,MyBaits框架是我們項目中使用得最多也是最願意推薦的框架,它既有數據表和Java對象映射功能,又有原生SQL的特性。在與SpringBoot集成上,和其他框架一樣,可以做到全註解化,無XML配置…… ...
  • 要用Python寫一個網站,你可以使用Python的Web框架來開發。常見的Python Web框架包括Django、Flask、Bottle等。以下是一個簡單的使用Flask框架開發的示例。 ### 1. 安裝Flask 在開始開發之前,你需要安裝Flask框架。你可以使用以下命令來安裝: ``` ...
  • ### 1.進程 進程是一個具有一定獨立功能的程式在一個數據集上的一次動態執行的過程,是操作系統進行資源分配和調度的一個獨立單位,是應用程式運行的載體。進程是一種抽象的概念,從來沒有統一的標准定義。進程一般由程式、數據集合和進程式控制制塊三部分組成。程式用於描述進程要完成的功能,是控制進程執行的指令集; ...
  • ## 1.生產者工程 - pom.xml里引入依賴 ~~~xml org.springframework.boot spring-boot-starter-amqp ~~~ - application.yml里配置基本信息 ~~~yaml spring: rabbitmq: host: localh ...
  • 哈嘍兄弟們,今天來實現採集一下最新的qcwu招聘數據。因為網站嘛,大家都爬來爬去的,人家就會經常更新,所以代碼對應的也要經常重新去寫。 對於會的人來說,當然無所謂,任他更新也攔不住,但是對於不會的小伙伴來說,網站一更新,當場自閉。 所以這期是出給不會的小伙伴的,我還錄製了視頻進行詳細講解,跟源碼一起 ...
  • ### 歡迎訪問我的GitHub > 這裡分類和彙總了欣宸的全部原創(含配套源碼):[https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) ### 本篇概覽 - 作為《Java版人臉跟蹤三部曲》系列的終 ...
  • #[[洛谷]P3378 【模板】堆](https://www.luogu.com.cn/problem/P3378 "[【洛谷】P3378 【模板】堆]") ##方法一 手寫堆 - 最小堆插入 從新增的最後一個結點的父結點開始,用要插入元素向下過濾上層結點(相當於要插入的元素向上滲透) ```c++ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...