「A Journey With Go」 專屬插圖,由 Renee French 根據原始 Go Gopher 製作。
:information_source: 本文基於 Go 1.13.
go test 命令提供了許多出色的功能,比如代碼覆蓋率,CPU 和 內存分析。要提供這些統計信息,Go 就需要一種方式來跟蹤 CPU 使用率,或在代碼覆蓋中跟蹤一個函數何時被用到。
Go 使用多種方式來產生這些統計信息:
我們來寫一個簡單的程序並回顧所有內容。這是我們在後面的章節將使用的代碼:
package main
import "math/rand"
func main() {
\tprintln(run())
}
//go:noinline
func run() int {
\ta := 0
\tfor i:= 0; i < rand.Intn(100000); i++ {
\t\tif i % 2 == 0 {
\t\t\tadd(&a)
\t\t} else {
\t\t\tsub(&a)
\t\t}
\t}
\treturn a
}
//go:noinline
func add(a *int) {
\t*a += rand.Intn(10)
}
//go:noinline
func sub(a *int) {
\t*a -= rand.Intn(10)
}
main.go 託管在 [GitHub] (https://github.com/) 查看
通過 GOSSAFUNC=run Go test -cover 命令生成的 SSA 代碼,我們可以查看 Go 對程序進行了什麼樣的修改:
變量 GoCover_0_313837343662366134383538 是一個標誌數組,其中每個鍵是一個代碼塊,當代碼實際進入這一塊時對應的標誌設置為 1.
你可以在我的文章 「Go: Compiler Phases」 中找到更多關於 SSA 的信息。
生成的代碼將稍後在管理代碼覆蓋率報告的函數中使用。 我們可以通過使用 objdump 命令反彙編代碼覆蓋期間生成的目標文件來進行驗證。 運行 go test -cover -o main.o && Go tool objdump main.go 將反彙編代碼並顯示缺少的部分。 它首先初始化並在自動生成的 init 函數中註冊 coverage:
test.go 添加的 init 方法
然後,如前所述,測試將在執行期間收集覆蓋率數據並且會觸發一個方法來實際寫入和顯示覆蓋率:
go test 調用的 after 函數
跟蹤 CPU 使用率的策略則有所不同。Go 會停止程序並收集正在運行程序的樣本。這裡是未開啟 CPU 分析的代碼的 trace:
這裡是相同代碼開啟了 CPU 分析的 trace:
增加的 trace 與 pprof 及性能分析相關。這裡是其中一個的放大圖:
profileWriter 方法將循環調用,每 100 毫秒收集 CPU 數據,以在性能分析結束時最終生成報告。
內存分析包含在源碼中,並已集成在內存分配系統中。在使用 -memprofile 開啟內存分析 的情況下,位於 malloc.go 中的內存分配器,將 對已分配的內存進行分析 。這裡,依然可以通過反彙編代碼進行驗證。這裡是內存分配器的使用:
開啟了內存分配分析