<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      Fork me on GitHub

      讓golang程序生成coredump文件并進行調試

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

      posted @ 2023-07-08 10:28  apocelipes  閱讀(5176)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产毛片精品一区二区色| 潮喷失禁大喷水无码| 国产美女被遭强高潮免费一视频| 国产三级精品片| 久久精品国产福利一区二区| 国产人与zoxxxx另类| 亚洲日韩成人无码不卡网站| 国产精品中出一区二区三区| 精品国产福利一区二区| 欧美交a欧美精品喷水| 精品人妻午夜福利一区二区| 国产日韩av二区三区| 国内精品自线在拍| 亚洲中文字幕一区精品自| 国语精品自产拍在线观看网站| 常州市| 久久这里都是精品二| 國产AV天堂| 免费又大粗又爽又黄少妇毛片 | 亚洲精品一区二区天堂| 色综合久久精品中文字幕| 国产精品亚洲二区在线播放| 国产精品毛片一区二区 | 樱花草在线社区www| 国产99久久亚洲综合精品西瓜tv| 九九热在线免费视频观看| 国产96在线 | 亚洲| 亚洲国产大胸一区二区三区| 99RE6在线观看国产精品| 无套内射视频囯产| 熟女蜜臀av麻豆一区二区| 少妇人妻偷人精品免费视频| 国产高清av首播原创麻豆| 久久一日本道色综合久久| 精品国产成人午夜福利| 亚洲悠悠色综合中文字幕| 又爽又黄无遮挡高潮视频网站| 久青草视频在线观看免费| 亚洲国产日韩伦中文字幕| 2019国产精品青青草原| 午夜福利国产区在线观看|