Diagnostics

引言

Go 生態系統提供了一套豐富的 API 和工具,用於診斷 Go 程式中的邏輯和效能問題。本頁將總結可用的工具,並幫助 Go 使用者為他們遇到的具體問題選擇合適的工具。

診斷解決方案可分為以下幾類:

注意:一些診斷工具可能會相互干擾。例如,精確的記憶體分析會影響 CPU 分析,而 goroutine 阻塞分析會影響排程器追蹤。為了獲得更精確的資訊,請單獨使用工具。

效能分析

效能分析有助於識別程式碼中開銷大或頻繁呼叫的部分。Go 執行時以 pprof 視覺化工具 期望的格式提供 效能分析資料。可以透過 go test 或從 net/http/pprof 包提供的端點在測試期間收集效能分析資料。使用者需要收集效能分析資料,並使用 pprof 工具來過濾和視覺化最頂層的程式碼路徑。

runtime/pprof 包提供的預定義分析項:

我還能使用哪些其他分析器來分析 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 還提供了一個執行追蹤器,用於追蹤一個時間間隔內的執行時事件。

追蹤使我們能夠:

在單體系統中,從程式的構建塊收集診斷資料相對容易。所有模組都位於一個程序內,並共享公共資源來報告日誌、錯誤和其他診斷資訊。一旦您的系統超出了單個程序的範圍並開始變得分散式,跟蹤從前端 Web 伺服器到其所有後端直到向用戶返回響應的呼叫就會變得更加困難。這就是分散式追蹤在儀器化和分析生產系統方面發揮重要作用的地方。

分散式追蹤是一種儀器化程式碼的方式,用於分析使用者請求整個生命週期中的延遲。當系統分散式,並且傳統的效能分析和除錯工具無法擴充套件時,您可能需要使用分散式追蹤工具來分析使用者請求和 RPC 的效能。

分散式追蹤使我們能夠:

Go 生態系統為每個追蹤系統和與後端無關的系統提供了各種分散式追蹤庫。

是否有方法可以自動攔截每個函式呼叫並建立追蹤?

Go 不提供自動攔截每個函式呼叫並建立追蹤跨度的直接方法。您需要手動儀器化您的程式碼來建立、結束和註解跨度。

我應該如何在 Go 庫中傳播追蹤頭?

您可以在 context.Context 中傳播追蹤識別符號和標籤。目前行業中還沒有規範的追蹤鍵或通用的追蹤頭表示。每個追蹤提供商負責在其 Go 庫中提供傳播實用程式。

標準庫或執行時中還有哪些低級別事件可以包含在追蹤中?

標準庫和執行時正在嘗試公開多個附加 API,以通知低級別的內部事件。例如,httptrace.ClientTrace 提供了用於跟蹤傳出請求生命週期中低級別事件的 API。目前正在努力從執行時執行追蹤器檢索低級別執行時事件,並允許使用者定義和記錄其使用者事件。

除錯

除錯是識別程式行為異常的原因的過程。偵錯程式允許我們理解程式的執行流程和當前狀態。有幾種除錯風格;本節將僅側重於附加偵錯程式到程式和核心轉儲除錯。

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 程式的整體健康狀況和效能。一些經常監控的統計資訊和狀態:

執行追蹤器

Go 配備了一個執行時執行追蹤器,用於捕獲廣泛的執行時事件。排程、系統呼叫、垃圾回收、堆大小和其他事件由執行時收集,並可供 go tool trace 進行視覺化。執行追蹤器是檢測延遲和利用率問題的工具。您可以檢查 CPU 的利用情況,以及何時網路或系統呼叫是 goroutine 搶佔的原因。

追蹤器有助於:

但是,它對於識別熱點(如分析過量記憶體或 CPU 使用的原因)效果不佳。首先使用效能分析工具來解決這些問題。

上面,go tool trace 的視覺化顯示了執行一開始很好,然後變得序列化。這表明可能存在共享資源的鎖爭用,從而導致了瓶頸。

請參閱 go tool trace 來收集和分析執行時追蹤。

GODEBUG

如果相應地設定了 GODEBUG 環境變數,執行時還會發出事件和資訊。

GODEBUG 環境變數可用於停用標準庫和執行時中指令集擴充套件的使用。