Diagnostics
引言
Go 生態系統提供了一套豐富的 API 和工具,用於診斷 Go 程式中的邏輯和效能問題。本頁將總結可用的工具,並幫助 Go 使用者為他們遇到的具體問題選擇合適的工具。
診斷解決方案可分為以下幾類:
- 效能分析 (Profiling):效能分析工具分析 Go 程式的複雜性和成本,例如記憶體使用情況和頻繁呼叫的函式,以識別 Go 程式中開銷較大的部分。
- 追蹤 (Tracing):追蹤是一種儀器化程式碼的方式,用於分析呼叫或使用者請求整個生命週期中的延遲。追蹤可以提供一個系統整體延遲中每個元件貢獻多少延遲的概覽。追蹤可以跨越多個 Go 程序。
- 除錯 (Debugging):除錯允許我們暫停 Go 程式並檢查其執行情況。可以透過除錯來驗證程式的狀態和流程。
- 執行時統計資訊和事件:收集和分析執行時統計資訊和事件可以提供 Go 程式健康狀況的高級別概覽。指標的峰值/谷值有助於我們識別吞吐量、利用率和效能的變化。
注意:一些診斷工具可能會相互干擾。例如,精確的記憶體分析會影響 CPU 分析,而 goroutine 阻塞分析會影響排程器追蹤。為了獲得更精確的資訊,請單獨使用工具。
效能分析
效能分析有助於識別程式碼中開銷大或頻繁呼叫的部分。Go 執行時以 pprof 視覺化工具 期望的格式提供 效能分析資料。可以透過 go
test
或從 net/http/pprof 包提供的端點在測試期間收集效能分析資料。使用者需要收集效能分析資料,並使用 pprof 工具來過濾和視覺化最頂層的程式碼路徑。
runtime/pprof
包提供的預定義分析項:
- cpu:CPU 分析確定程式在積極消耗 CPU 週期時(而不是在睡眠或等待 I/O 時)將時間花在了哪裡。
- heap:堆分析報告記憶體分配樣本;用於監控當前和歷史記憶體使用情況,以及檢查記憶體洩漏。
- threadcreate:執行緒建立分析報告導致建立新 OS 執行緒的程式部分。
- goroutine:Goroutine 分析報告所有當前 goroutine 的堆疊跟蹤。
-
block:阻塞分析顯示 goroutine 在等待同步原語(包括定時器通道)時發生阻塞的位置。阻塞分析預設不啟用;使用
runtime.SetBlockProfileRate
來啟用它。 -
mutex:互斥鎖分析報告鎖的爭用情況。當您認為 CPU 未完全利用是由於互斥鎖爭用時,可以使用此分析。互斥鎖分析預設不啟用,請參閱
runtime.SetMutexProfileFraction
來啟用它。
我還能使用哪些其他分析器來分析 Go 程式?
在 Linux 上,perf 工具 可用於分析 Go 程式。Perf 可以分析和展開 cgo/SWIG 程式碼和核心,因此可以幫助深入瞭解原生/核心效能瓶頸。在 macOS 上,可以使用 Instruments 套件來分析 Go 程式。
我可以分析我的生產服務嗎?
可以。分析生產中的程式是安全的,但啟用某些分析(例如 CPU 分析)會增加成本。您應該預期效能會有所下降。可以透過在生產環境中啟用分析器之前測量其開銷來估算效能損失。
您可能希望定期分析您的生產服務。特別是在具有單個程序的許多副本的系統中,定期選擇一個隨機副本是一個安全的選擇。選擇一個生產程序,每 Y 秒對其進行 X 秒的分析,然後儲存結果以供視覺化和分析;然後定期重複。結果可以手動和/或自動審查以查詢問題。分析的收集可能會相互干擾,因此建議一次只收集一個分析。
視覺化效能分析資料的最佳方法是什麼?
Go 工具透過 go tool pprof
提供文字、圖形和 callgrind 格式的視覺化效能分析資料。閱讀 Profiling Go programs 來檢視它們的使用示例。
以文字形式列出開銷最大的呼叫。
以圖形方式視覺化最昂貴的呼叫。
Weblist 檢視以 HTML 頁面的形式逐行顯示原始碼中最昂貴的部分。在下面的示例中,530ms 花費在 runtime.concatstrings
上,並顯示了每行的開銷。
以 weblist 形式視覺化最昂貴的呼叫。
視覺化效能分析資料的另一種方法是 火焰圖。火焰圖允許您在特定的祖先路徑中移動,因此您可以放大/縮小程式碼的特定部分。 上游 pprof 支援火焰圖。
火焰圖提供了視覺化的方式來發現最昂貴的程式碼路徑。
我是否僅限於內建分析項?
除了執行時提供的功能外,Go 使用者還可以透過 pprof.Profile 建立自己的自定義分析項,並使用現有工具來檢查它們。
我可以將分析器處理程式 (/debug/pprof/...) 部署在不同的路徑和埠上嗎?
可以。net/http/pprof
包預設將其處理程式註冊到預設的 mux,但您也可以透過使用該包匯出的處理程式自行註冊它們。
例如,以下示例將在 :7777 上的 /custom_debug_path/profile 提供 pprof.Profile 處理程式。
package main import ( "log" "net/http" "net/http/pprof" ) func main() { mux := http.NewServeMux() mux.HandleFunc("/custom_debug_path/profile", pprof.Profile) log.Fatal(http.ListenAndServe(":7777", mux)) }
追蹤 (Tracing)
追蹤是一種儀器化程式碼的方式,用於分析呼叫鏈整個生命週期中的延遲。Go 提供了 golang.org/x/net/trace 包作為每個 Go 節點的最小追蹤後端,並提供了一個最小的儀器化庫和一個簡單的儀表板。Go 還提供了一個執行追蹤器,用於追蹤一個時間間隔內的執行時事件。
追蹤使我們能夠:
- 儀器化和分析 Go 程序中的應用程式延遲。
- 衡量長呼叫鏈中特定呼叫的開銷。
- 找出利用率和效能的改進。沒有追蹤資料,瓶頸並不總是顯而易見的。
在單體系統中,從程式的構建塊收集診斷資料相對容易。所有模組都位於一個程序內,並共享公共資源來報告日誌、錯誤和其他診斷資訊。一旦您的系統超出了單個程序的範圍並開始變得分散式,跟蹤從前端 Web 伺服器到其所有後端直到向用戶返回響應的呼叫就會變得更加困難。這就是分散式追蹤在儀器化和分析生產系統方面發揮重要作用的地方。
分散式追蹤是一種儀器化程式碼的方式,用於分析使用者請求整個生命週期中的延遲。當系統分散式,並且傳統的效能分析和除錯工具無法擴充套件時,您可能需要使用分散式追蹤工具來分析使用者請求和 RPC 的效能。
分散式追蹤使我們能夠:
- 儀器化和分析大型系統中的應用程式延遲。
- 跟蹤使用者請求生命週期內的所有 RPC,並檢視僅在生產環境中可見的整合問題。
- 找出可以應用於我們系統的效能改進。在收集追蹤資料之前,許多瓶頸並不明顯。
Go 生態系統為每個追蹤系統和與後端無關的系統提供了各種分散式追蹤庫。
是否有方法可以自動攔截每個函式呼叫並建立追蹤?
Go 不提供自動攔截每個函式呼叫並建立追蹤跨度的直接方法。您需要手動儀器化您的程式碼來建立、結束和註解跨度。
我應該如何在 Go 庫中傳播追蹤頭?
您可以在 context.Context
中傳播追蹤識別符號和標籤。目前行業中還沒有規範的追蹤鍵或通用的追蹤頭表示。每個追蹤提供商負責在其 Go 庫中提供傳播實用程式。
標準庫或執行時中還有哪些低級別事件可以包含在追蹤中?
標準庫和執行時正在嘗試公開多個附加 API,以通知低級別的內部事件。例如,httptrace.ClientTrace
提供了用於跟蹤傳出請求生命週期中低級別事件的 API。目前正在努力從執行時執行追蹤器檢索低級別執行時事件,並允許使用者定義和記錄其使用者事件。
除錯
除錯是識別程式行為異常的原因的過程。偵錯程式允許我們理解程式的執行流程和當前狀態。有幾種除錯風格;本節將僅側重於附加偵錯程式到程式和核心轉儲除錯。
Go 使用者主要使用以下偵錯程式:
- Delve:Delve 是 Go 程式語言的偵錯程式。它支援 Go 的執行時概念和內建型別。Delve 致力於成為 Go 程式功能齊全且可靠的偵錯程式。
- GDB:Go 透過標準的 Go 編譯器和 Gccgo 提供 GDB 支援。堆疊管理、執行緒和執行時包含與 GDB 期望的執行模型足夠不同的方面,它們可能會混淆偵錯程式,即使程式是用 gccgo 編譯的。儘管 GDB 可以用於除錯 Go 程式,但它並不理想,並且可能會造成混淆。
偵錯程式與 Go 程式的配合效果如何?
gc
編譯器執行函式內聯和變數暫存器化等最佳化。這些最佳化有時會使使用偵錯程式進行除錯更加困難。目前正在努力改進為最佳化後的二進位制檔案生成的 DWARF 資訊質量。在這些改進可用之前,我們建議在構建正在除錯的程式碼時停用最佳化。以下命令構建一個沒有編譯器最佳化的包:
$ go build -gcflags=all="-N -l"作為改進工作的一部分,Go 1.10 引入了一個新的編譯器標誌
-dwarflocationlists
。該標誌會導致編譯器新增位置列表,以幫助偵錯程式處理最佳化後的二進位制檔案。以下命令使用最佳化但帶 DWARF 位置列表構建一個包:
$ go build -gcflags="-dwarflocationlists=true"
推薦的偵錯程式使用者介面是什麼?
儘管 delve 和 gdb 都提供了 CLI,但大多數編輯器整合和 IDE 都提供了特定於除錯的使用者介面。
是否可以對 Go 程式進行事後除錯?
核心轉儲檔案是包含正在執行的程序的記憶體轉儲及其程序狀態的檔案。它主要用於程式的記憶體轉儲除錯,以及瞭解程式在執行時狀態。這兩種情況使得核心轉儲的除錯成為事後分析和分析生產服務的良好診斷輔助工具。可以從 Go 程式獲取核心檔案並使用 delve 或 gdb 進行除錯,請參閱 核心轉儲除錯 頁面以獲取分步指南。
執行時統計資訊和事件
執行時提供統計資訊和內部事件的報告,供使用者在執行時級別診斷效能和利用率問題。
使用者可以監控這些統計資訊,以更好地瞭解 Go 程式的整體健康狀況和效能。一些經常監控的統計資訊和狀態:
runtime.ReadMemStats
報告與堆分配和垃圾回收相關的指標。記憶體統計資訊對於監控程序消耗的記憶體資源量、程序是否能很好地利用記憶體以及捕獲記憶體洩漏非常有用。debug.ReadGCStats
讀取有關垃圾回收的統計資訊。檢視有多少資源花在了 GC 暫停上很有用。它還報告垃圾回收器暫停的時間線和暫停時間百分位數。debug.Stack
返回當前堆疊跟蹤。堆疊跟蹤有助於檢視當前正在執行多少 goroutine、它們在做什麼,以及它們是否被阻塞。debug.WriteHeapDump
暫停所有 goroutine 的執行,並允許您將堆轉儲到檔案。堆轉儲是 Go 程序在給定時間點的記憶體快照。它包含所有已分配的物件以及 goroutine、finalizer 等。runtime.NumGoroutine
返回當前 goroutine 的數量。可以監控該值,以檢視是否使用了足夠的 goroutine,或檢測 goroutine 洩漏。
執行追蹤器
Go 配備了一個執行時執行追蹤器,用於捕獲廣泛的執行時事件。排程、系統呼叫、垃圾回收、堆大小和其他事件由執行時收集,並可供 go tool trace 進行視覺化。執行追蹤器是檢測延遲和利用率問題的工具。您可以檢查 CPU 的利用情況,以及何時網路或系統呼叫是 goroutine 搶佔的原因。
追蹤器有助於:
- 瞭解您的 goroutine 如何執行。
- 瞭解一些核心執行時事件,例如 GC 執行。
- 識別並行性差的執行。
但是,它對於識別熱點(如分析過量記憶體或 CPU 使用的原因)效果不佳。首先使用效能分析工具來解決這些問題。
上面,go tool trace 的視覺化顯示了執行一開始很好,然後變得序列化。這表明可能存在共享資源的鎖爭用,從而導致了瓶頸。
請參閱 go
tool
trace
來收集和分析執行時追蹤。
GODEBUG
如果相應地設定了 GODEBUG 環境變數,執行時還會發出事件和資訊。
- GODEBUG=gctrace=1 在每次 GC 時列印垃圾回收事件,總結收集的記憶體量和暫停時間。
- GODEBUG=inittrace=1 列印已完成的包初始化工作的執行時間和記憶體分配資訊摘要。
- GODEBUG=schedtrace=X 每 X 毫秒列印排程事件。
GODEBUG 環境變數可用於停用標準庫和執行時中指令集擴充套件的使用。
- GODEBUG=cpu.all=off 停用所有可選指令集擴充套件的使用。
- GODEBUG=cpu.extension=off 停用來自指定指令集擴充套件的指令的使用。
extension 是指令集擴充套件的名稱(小寫),例如 sse41 或 avx。