診斷

簡介

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 視覺化。閱讀 分析 Go 程式 以查看它們的作用。


以文字列出最昂貴的呼叫。


以圖形視覺化最昂貴的呼叫。

Weblist 檢視會在 HTML 頁面中逐行顯示來源的昂貴部分。在以下範例中,530ms 花費在 runtime.concatstrings 中,且每個行的成本都顯示在清單中。


以 Weblist 視覺化最昂貴的呼叫。

視覺化分析資料的另一種方式是 火焰圖。火焰圖讓您可以在特定的祖先路徑中移動,因此您可以放大/縮小特定程式碼區段。上游 pprof 支援火焰圖。


火焰圖提供視覺化以找出最昂貴的程式碼路徑。

我是否受限於內建分析?

除了執行時期提供的內容之外,Go 使用者可以透過 pprof.Profile 建立自訂設定檔,並使用現有的工具來檢查這些設定檔。

我可以在不同的路徑和埠上提供設定檔處理常式 (/debug/pprof/...) 嗎?

可以。net/http/pprof 套件預設會將其處理常式註冊到預設混合器,但您也可以使用從套件匯出的處理常式自行註冊。

例如,以下範例會在 :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))
}

追蹤

追蹤是一種編寫工具程式碼的方式,用於分析呼叫鏈的整個生命週期中的延遲。Go 提供 golang.org/x/net/trace 套件作為每個 Go 節點的最小追蹤後端,並提供一個最小編寫工具程式碼庫,其中包含一個簡單的儀表板。Go 還提供一個執行追蹤器,用於追蹤區間內的執行時期事件。

追蹤讓我們能夠

在單體系統中,從程式碼區塊收集診斷資料相對容易。所有模組都存在於單一程序中,並共用記錄檔、錯誤和其他診斷資訊的共用資源。一旦系統擴展到單一程序之外並開始分散,就更難追蹤從前端網路伺服器開始到所有後端的所有呼叫,直到回應傳回給使用者。這正是分散式追蹤在分析和分析生產系統中扮演重要角色的地方。

分散式追蹤是一種分析使用者要求生命週期中延遲的程式碼工具。當系統分散且傳統的設定檔和除錯工具無法擴充時,您可能想使用分散式追蹤工具來分析使用者要求和 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 環境變數可用於停用標準函式庫和執行時期中指令集擴充功能的使用。