圖解 Go pprof 收集數(shù)據(jù)的工作流

Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.
?? 本文基于 Go 1.13。
pprof 是用于分析諸如 CPU 或 內(nèi)存分配等 profile 數(shù)據(jù)的工具。分析程序的 profile 數(shù)據(jù)需要收集運行時的數(shù)據(jù)用來在之后統(tǒng)計和生成畫像。我們現(xiàn)在來研究下數(shù)據(jù)收集的工作流以及怎么樣去調(diào)整它。
工作流
pprof 以一個固定的時間間隔基礎(chǔ)來收集數(shù)據(jù),這個時間間隔是以每秒的收集器的個數(shù)定義的。默認(rèn)參數(shù)是 100,即 pprof 每秒收集 100 次數(shù)據(jù),例如,每 10 毫秒收集一次。
可以通過調(diào)用 StartCPUProfile 來啟動 pprof。
func?main()?{
?f,?_?:=?os.Create(`cpu.prof`)
?if?err?!=?nil?{
??log.Fatal(err)
?}
?pprof.StartCPUProfile(f)
?defer?pprof.StopCPUProfile()
?...
}
這個過程會在運行的線程中自動設(shè)置一個定時器(下圖中 M 表示線程),讓 Go 定期地收集 profile 數(shù)據(jù)。下面是第一個示意圖:

想了解更多關(guān)于 MPG 調(diào)度模型的信息,我推薦你閱讀我的文章”Go:協(xié)程,操作系統(tǒng)線程和 CPU 管理[1]?!?/em>
然而,目前為止 pprof 僅在收集當(dāng)前運行的線程的 profile 信息。當(dāng) Go 調(diào)度器想調(diào)度一個協(xié)程運行在某個線程上時,這個線程也可以實時被追蹤。下面是更新后的示意圖:

想了解更多關(guān)于 Go 調(diào)度器的信息,我建議你閱讀我的文章”Go: g0,特殊的協(xié)程[2]“。
之后,profile 數(shù)據(jù)會在定義好的每個時間間隔到期后定期地被 dump 到一個緩沖區(qū):

數(shù)據(jù)實際上是由 gsignal 進(jìn)行 dump 的,這個協(xié)程是用來處理發(fā)來的信號的。實際上,在每個時間間隔到期后定時器會發(fā)送信號。
想了解更多關(guān)于信號和 gsignal 的信息,我推薦你閱讀我的文章”Go:gsignal,信號的掌控者[3]“。
基于信號的機制
在每個線程上創(chuàng)建的定時器是由 settimer 方法和時間間隔定時器 ITIMER_PROF 管理的。時間計數(shù)僅在系統(tǒng)代表這個處理運行時才會減少,這樣就能確保 profile 數(shù)據(jù)的準(zhǔn)確。
當(dāng)經(jīng)過了定義的時間間隔后,定時器發(fā)送一個 SIGPROF 信號,這個信號會被 Go 截獲,profile 數(shù)據(jù)會被 dump 到緩沖區(qū)。頻率可以通過調(diào)用 runtime 包里的 SetCPUProfileRate 函數(shù)進(jìn)行配置。這個配置操作需要在啟動 profiler 之前完成:
func?main()?{
?f,?err?:=?os.Create(`cpu.prof`)
?if?err?!=?nil?{
??log.Fatal(err)
?}
?runtime.SetCPUProfileRate(10)
?pprof.StartCPUProfile(f)
?defer?pprof.StopCPUProfile()
?...
}
profile 數(shù)據(jù)采集率只能定義一次,在啟動 profiler 時 pprof 定義它。在啟動 profiler 之前調(diào)用該方法會導(dǎo)致 pprof 忽略默認(rèn)值。
然而,默認(rèn)值能滿足大部分場景中。包的文檔中有對此的詳細(xì)解釋:
100 Hz 是合理的值:既能滿足產(chǎn)出有用數(shù)據(jù)的頻率需求,又不至于過快而使系統(tǒng) hang 住。
當(dāng)然,更高的頻率值似乎也可以,因為它可能會導(dǎo)致一些 `SIGPROF` 事件[4]從 250 或更高的值降下來。pprof 文檔也陳述了怎樣讓它表現(xiàn)得更好:
[…] 實踐中操作系統(tǒng)不能以比 500 Hz 更高的頻率觸發(fā)信號
收集數(shù)據(jù)
現(xiàn)在所有的定時器都已經(jīng)設(shè)置完了。當(dāng)數(shù)據(jù)被 dump 到緩沖區(qū)后,pprof 需要一種把所有數(shù)據(jù)收集起來生成報告的方法。這個處理過程是在獨立的協(xié)程中進(jìn)行的,每 100 毫秒收集和格式化一次數(shù)據(jù)。至于收集到的數(shù)據(jù),Go 生成回溯信息,用來找到函數(shù)調(diào)用關(guān)系以及處理它來格式化內(nèi)聯(lián)調(diào)用。
當(dāng)信息生成過程完成后,例如 profile 數(shù)據(jù)收集結(jié)束后,特定的協(xié)程會把報告 dump 到文件,這樣數(shù)據(jù)就可用和完全可視化了。
via: https://medium.com/a-journey-with-go/go-samples-collection-with-pprof-2a63c3e8a142
作者:Vincent Blanchon[5]譯者:lxbwolf[6]校對:unknwon[7]
本文由 GCTT[8] 原創(chuàng)編譯,Go 中文網(wǎng)[9] 榮譽推出
參考資料
Go:協(xié)程,操作系統(tǒng)線程和 CPU 管理: https://studygolang.com/articles/25292
[2]Go: g0,特殊的協(xié)程: https://medium.com/a-journey-with-go/go-g0-special-goroutine-8c778c6704d8
[3]Go:gsignal,信號的掌控者: https://studygolang.com/articles/28974
[4]SIGPROF 事件: https://github.com/golang/go/issues/35057
Vincent Blanchon: https://medium.com/@blanchon.vincent
[6]lxbwolf: https://github.com/lxbwolf
[7]unknwon: https://github.com/unknwon
[8]GCTT: https://github.com/studygolang/GCTT
[9]Go 中文網(wǎng): https://studygolang.com/
推薦閱讀
