go test 的這些用途你都懂嗎?
“A Journey With Go” 專屬插圖,由 Renee French 根據原始 Go Gopher 製作。
ℹ️ 本文基於 Go 1.13.
go test
命令提供了許多出色的功能,比如代碼覆蓋率,CPU 和 內存分析。要提供這些統計信息,Go 就需要一種方式來跟蹤 CPU 使用率,或在代碼覆蓋中跟蹤一個函數何時被用到。
性能測量
Go 使用多種方式來產生這些統計信息:
-
動態插入性能測量語句,使其可以跟蹤到代碼何時進入一個函數或條件。這個策略在代碼覆蓋率 [1] 中使用。
-
每秒記錄多次程序樣本。這個策略在 CPU 分析 [2] 中用到。
-
在代碼中使用靜態 hook,以便在執行期間調用所需函數。這個策略在內存分析中用到。
我們來寫一個簡單的程序並回顧所有內容。這是我們在後面的章節將使用的代碼:
package main
import "math/rand"
func main() {
println(run())
}
//go:noinline
func run() int {
a := 0
for i:= 0; i < rand.Intn(100000); i++ {
if i % 2 == 0 {
add(&a)
} else {
sub(&a)
}
}
return a
}
//go:noinline
func add(a *int) {
*a += rand.Intn(10)
}
//go:noinline
func sub(a *int) {
*a -= rand.Intn(10)
}
main.go 託管在 [GitHub] (https://github.com/) 查看 [3]
代碼覆蓋率
通過 GOSSAFUNC=run Go test -cover
命令生成的 SSA 代碼,我們可以查看 Go 對程序進行了什麼樣的修改:
變量 GoCover_0_313837343662366134383538
是一個標誌數組,其中每個鍵是一個代碼塊,當代碼實際進入這一塊時對應的標誌設置爲 1.
你可以在我的文章 “Go: Compiler Phases”[4] 中找到更多關於 SSA 的信息。
生成的代碼將稍後在管理代碼覆蓋率報告的函數中使用。我們可以通過使用 objdump
命令反彙編代碼覆蓋期間生成的目標文件來進行驗證。運行 go test -cover -o main.o && Go tool objdump main.go
將反彙編代碼並顯示缺少的部分。它首先初始化並在自動生成的 init 函數中註冊 coverage:
test.go 添加的 init 方法
然後,如前所述,測試將在執行期間收集覆蓋率數據並且會觸發一個方法來實際寫入和顯示覆蓋率:
go test 調用的 after 函數
CPU 分析
跟蹤 CPU 使用率的策略則有所不同。Go 會停止程序並收集正在運行程序的樣本。這裏是未開啓 CPU 分析的代碼的 trace:
這裏是相同代碼開啓了 CPU 分析的 trace:
增加的 trace 與 pprof
及性能分析相關。這裏是其中一個的放大圖:
profileWriter
方法將循環調用,每 100 毫秒收集 CPU 數據,以在性能分析結束時最終生成報告。
內存分析
內存分析包含在源碼中,並已集成在內存分配系統中。在使用 -memprofile
開啓內存分析 [5] 的情況下,位於 malloc.go[6] 中的內存分配器,將對已分配的內存進行分析 [7]。這裏,依然可以通過反彙編代碼進行驗證。這裏是內存分配器的使用:
開啓了內存分配分析
你可以在我的文章 “Go: Unknown Parts of the Test Package[8]” 中找到更多關於 test 包的信息.
via: https://medium.com/a-journey-with-go/go-instrumentation-in-go-e845cdae0c51
作者:Vincent Blanchon[9] 譯者:krystollia[10] 校對:polaris1119[11]
本文由 GCTT[12] 原創編譯,Go 中文網 [13] 榮譽推出,發佈在 Go 語言中文網公衆號,轉載請聯繫我們授權。
參考資料
[1]
代碼覆蓋率: https://golang.org/doc/go1.2#cover
[2]
CPU 分析: https://blog.golang.org/profiling-go-programs
[3]
GitHub] (https://github.com/) [查看: https://gist.github.com/blanchonvincent/d4ed01d31b3ed99eb5cd87629ecfe926/raw/1fbac76f932d020a2b172b2385fb1cda69b83b1e/main.go
[4]
“Go: Compiler Phases”: https://medium.com/@blanchon.vincent/go-compiler-phases-4e5a153ca889
[5]
開啓內存分析: https://github.com/golang/go/blob/release-branch.go1.13/src/cmd/compile/internal/gc/util.go#L55-L77
[6]
malloc.go: https://github.com/golang/go/blob/release-branch.go1.13/src/runtime/malloc.go#L877
[7]
對已分配的內存進行分析: https://github.com/golang/go/blob/release-branch.go1.13/src/runtime/malloc.go#L1097-L1105
[8]
Go: Unknown Parts of the Test Package: https://medium.com/a-journey-with-go/go-unknown-parts-of-the-test-package-df8988b2ef7f
[9]
Vincent Blanchon: https://medium.com/@blanchon.vincent
[10]
krystollia: https://github.com/krystollia
[11]
polaris1119: https://github.com/polaris1119
[12]
GCTT: https://github.com/studygolang/GCTT
[13]
Go 中文網: https://studygolang.com/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/rt737Zdt30sYw9Fk-5n71A