Go程序性能分析工具和方法

做者:寧亮html

1、經常使用分析命令和工具

  • pprof
  • go tool [xxx]
  • go test
  • delve
  • go race
  • gdb

2、程序編譯時的參數傳遞

一、gcflags

//可以使用go tool compile --help查看可用參數及含義
go build -gcflags="-m"   

好比 -N 禁用編譯優化,-l 禁止內聯,-m 打印編譯優化策略(包括逃逸狀況和函數是否內聯,以及變量分配在堆或棧),-S 是打印彙編。node

若是隻在編譯特定包時須要傳遞參數,格式應遵照「包名=參數列表」,如 go build -gcflags='log=-N -l' main.gogit

二、ldflags

go build 用 -ldflags 給 go 連接器傳入參數,實際是給 go tool link 的參數,能夠用 go tool link --help 查看可用的參數。github

經常使用 -X 來指定版本號等編譯時才決定的參數值。例如代碼中定義var buildVer string,而後在編譯時用go build -ldflags "-X main.buildVer=1.0" 來賦值。注意 -X 只能給string類型變量賦值。golang

3、go build -x

能夠列出 go build 觸發的全部命令,好比工具鏈、跨平臺編譯、傳入外部編譯器的 flags、連接器等,可以使用 -x 來查看全部的觸發。web

4、競爭檢測

使用 go run -race main.go 或 go build -race main.go 來進行競爭檢測。json

5、GC日誌

  • 執行前添加系統環境變量 GODEBUG='gctrace=1' 來跟蹤打印垃圾回收器信息
  • 在代碼中使用 runtime.ReadMemStats 來獲取程序當前內存的使用狀況
  • 使用 pprof 工具

例如 GODEBUG=gctrace=1 go run main.go //跟蹤打印垃圾回收器信息,Go 程序會每隔一段時間打印一些 gc 信息。api

6、Pprof

Go 語言內置了獲取程序運行數據的工具,包括如下兩個標準庫瀏覽器

(1)runtime/pprof:採集工具型應用運行數據進行分析
(2)net/http/pprof:採集服務型應用運行時數據進行分析sass

一、工具型應用分析

(1)CPU分析
f, err := os.Create(*cpuprofile)
...
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()
(2)內存分析
f, err := os.Create(*memprofile)
pprof.WriteHeapProfile(f)
f.Close()

二、使用 net/http 包時啓用 Pprof

package main

import (
"log"
"net/http"
_ "net/http/pprof"
)

func main() {
//... do something

log.Println(http.ListenAndServe("localhost:8090", nil))
}

三、使用Gin框架時啓用Pprof

package main

import (
"github.com/gin-contrib/pprof"
"github.com/gin-gonic/gin"
)

func main() {
app := gin.Default()

pprof.Register(app)

app.Run(":8090")
}

7、訪問web頁獲取分析結果

如在本機測試服務上啓用 pprof,可使用 http://127.0.0.1:8090/debug/p... http 服務:

/debug/pprof/

Types of profiles available:
Count Profile
3 allocs
0 block //goroutine的阻塞信息
0 cmdline
4 goroutine //此項可排查是否建立了大量的 goroutine
3 heap //堆內存的分配信息
0 mutex //鎖的信息
0 profile
7 threadcreate //線程信息
0 trace
full goroutine stack dump //此項可排查是否有 goroutine 運行時間過長

一、pprof 支持四種類型的分析

pprof開啓後,每隔一段時間(默認10ms)就會收集當前的堆棧信息,獲取各個函數佔用的CPU以及內存資源,而後經過對這些採樣數據進行分析,造成一個性能分析報告。

(1)CPU Profile:CPU 分析,採樣消耗 cpu 的調用,這個通常用來定位排查程序裏耗費計算資源的地方
(2)Memory Profile(Heap Profile):內存分析,通常用來排查內存佔用,內存泄露等問題。堆內存分配狀況的記錄。默認每分配512K字節時取樣一次。
(3)Block Profile:阻塞分析,報告致使阻塞的同步原語的狀況,能夠用來分析和查找鎖的性能瓶頸。Goroutine阻塞事件的堆棧跟蹤記錄。默認每發生一次阻塞事件時取樣一次。
(4)Goroutine Profile :報告 goroutines 的使用狀況,有哪些 goroutine,它們的調用關係是怎樣的。活躍 Goroutine 的信息的記錄,以及調用關係。僅在獲取時取樣一次。

二、Pprof可視化

如在 Mac 上經過 brew 安裝 graphviz,能夠執行如下命令 brew install graphviz

三、以本地文件形式獲取分析結果

能夠先把信息 dump 到本地文件,而後用 go tool 去分析。

四、debugcharts

一個能夠實時查看 go 程序內存、CPU、GC、協程等變化狀況的可視化工具。啓用方式跟 pprof 相似,都是先 import 引入,而後開啓端口監聽便可。

package main

import (
_ "github.com/mkevac/debugcharts"
"log"
"net/http"
)

func main() {
//... do something

log.Println(http.ListenAndServe("localhost:8090", nil))
}

而後在瀏覽器中打開查看:http://127.0.0.1:8090/debug/c...

五、第三方工具prometheus

prometheus 是 grafana 的插件,支持 go 監控的可視化。

啓用方式也是先引入包:

import (

"github.com/prometheus/client_golang/prometheus/promhttp"

)

而後增長路由:

//prometheus
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":8090", nil)

最後,經過訪問 http://127.0.0.1:8090/metrics 查看採集到的指標數據。
image.png

注:也能夠經過一個端口同時開啓 pprof + charts + prometheus。

8、go tool [xxx]系列命令

一、輸入 go tool 可查看內置的全部[xxx]工具命令

addr2line
api
asm
buildid
cgo
compile:代碼彙編
cover:生成代碼覆蓋率
dist
doc
fix
link
nm:查看符號表(等同於系統 nm 命令)
objdump:反彙編工具,分析二進制文件(等同於系統 objdump 命令)
oldlink
pack
pprof:性能和指標分析工具
test2json
trace:採樣一段時間,指標跟蹤分析工具
vet

二、go tool nm 查看符號表的命令

在斷點的時候,若是不知道斷點的函數符號,能夠用這個命令進行查詢(命令處理的是二進制程序文件)。
go tool nm ./main
輸出的第一列是地址,第二列是類型,第三列是符號。

115aa00 T bufio.(*ReadWriter).Available
115aa20 T bufio.(*ReadWriter).Discard
115aa60 T bufio.(*ReadWriter).Flush
115aa80 T bufio.(*ReadWriter).Peek

三、go tool compile 彙編某個文件

go tool compile -N -l -S main.go

四、go tool objdump 反彙編二進制的工具

go tool objdump main.o
go tool objdump -s DoFunc main.o //反彙編具體函數

五、go tool pprof 性能指標分析工具

5.1 命令行分析模式

go tool pprof http://localhost:8090/debug/p... 分析heap,進入命令行模式,輸入 web 便可以web方式打開(前提是安裝了graphviz)。

或者繼續輸入命令:
在命令行輸入top默認查看程序中佔用內存前10位的函數,在命令行輸入top 3能夠查看程序中佔用內存前3位的函數。一樣,若是採集的是cpu使用top命令能夠看佔用cpu的函數

(1)輸入top後顯示的最後一列爲函數名稱,其餘各項內容意義以下:

flat:當前函數佔用CPU的耗時
flat%:當前函數佔用CPU的耗時百分比
sum%:函數佔用CPU的累積耗時百分比
cum:當前函數+調用的子函數 佔用CPU總耗時
cum%:當前函數+調用的子函數 佔用CPU總耗時百分比

(2)能夠在命令行輸入 list+函數名 命令查看具體的函數分析
(3)能夠在命令行輸入 pdf 生成可視化的pdf文件
(4)能夠在命令行輸入 help 提供全部pprof支持的命令說明

5.2 web分析模式

(1)go tool pprof -http=:1234 http://localhost:8090/debug/p... 會直接以web方式打開。或者:http://localhost:1234/ui/ 也能夠直接打開,從中能夠直接篩選查看火焰圖(Flame Graph)。-http 表示使用交互式web接口查看獲取的性能信息,指定可用的端口便可。debug/pprof/須要查看的指標(allocs,block,goroutine,heap等)。火焰圖從上往下是方法的調用棧,長度表明使用的cpu時長。

5.3 go tool pprof 分析命令
go tool pprof -http=:1234 http://localhost:8090/debug/pprof/goroutine?second=10
go tool pprof --seconds 10 http://localhost:8090/debug/pprof/goroutine

若是應用比較複雜,生成的調用圖特別大,看起來很亂,有兩個辦法能夠優化:
(1)使用 web [funcName] 的方式,只打印和某個函數相關的內容。
(2)運行 go tool pprof 命令時加上 --nodefration 參數,能夠忽略內存使用較少的函數,好比--nodefration=0.05表示若是調用的子函數使用的 CPU、memory 不超過 5%,就忽略它,不要顯示在圖片中。

9、go test 單元測試

(1)本質上,golang 跑單測是先編譯 *_test.go 文件,編譯成二進制後,再運行這個二進制文件

go test . //直接在本目錄中運行go test
go test -run=TestPutAndGetKeyValue //指定運行函數
go test -v //提供詳細的測試輸出,打印測試名稱、狀態(經過或者失敗)、耗時、測試用例的日誌等

go test -race //測試時支持對競爭進行檢測和報告
go test -coverprofile=c.out && go tool cover -html=c.out //輸出一個覆蓋信息結果並可在瀏覽器上可視化觀看
(2)統計代碼覆蓋率

加一個 -coverprofile 的參數,聲明在跑單測的時候,記錄代碼覆蓋率。例如 go test -coverprofile=coverage.out
使用 go tool cover 命令分析,能夠得出覆蓋率報告 go tool cover -func=coverage.out

10、Delve

delve 當前是最友好的 golang 調試程序,ide 調試其實也是調用 dlv 而已,好比 goland使用的調試。
安裝dlv:go get -u github.com/go-delve/delve/cmd/dlv
檢查安裝版本信息:dlv version

把程序加載進 Delve 調試器的兩種方式(事先須要有go.mod)

一、加載源碼進行調試

dlv debug
(1)執行 dlv debug 進入命令行模式,此時同目錄下會自動生成一個 __debug_bin 文件。這個文件是由源碼編譯生成的,並會自動加載進調試器。
(2)Delve 指望的是從單個程序或項目中構建出單個二進制文件,若是目錄中有多個源文件且每一個文件都有本身的主函數, Delve 則可能拋出錯誤。此種狀況下應該使用下面第二種方式,加載二進制文件進行調試。

二、加載二進制文件進行調試

dlv exec ./main
(1)使用 dlv exec 命令將二進制文件加載進調試器。
(2)在命令行模式下輸入 help 查看可用命令。
(3)其常使用的一些命令:

b main.main //在 main 函數處設置斷點,等同於 break main.main
b func.go:5 //使用 文件名:行號 的格式來設置斷點,也能夠直接用行號設置斷點
bp //查看設置的斷點,等同於 breakpoints
clear [斷點標號如2] //清除單個斷點
clearall //清除全部斷點
on //設置一段命令,當斷點命中的時候
c //繼續運行程序,運行到斷點處停止,等同於 continue
n //單步調試下一行源碼,等同於 next。默認狀況下,Delve不會更深刻地調試函數調用。
s //單步調試下一個函數,等同於 step
step-instruction //單步調試某個彙編指令
stack //打印當前堆棧的內容信息,能夠看到0、一、二、3...等棧位置的函數
frame 0 //實現幀之間的跳轉,可使用 stack 輸出的位置序號
args //打印出命令行傳給函數的參數
disassemble //查看編譯器生成的彙編語言指令
stepout //跳回到函數被調用的地方
print [var_name] //打印變量的值
whatis [var_name] //打印變量的類型
locals //打印函數內的全部局部變量
regs //打印寄存器的信息
x //等同於examinemem,這個是解析內存用的,和 gdb 的 x 命令同樣
set //set賦值
vars //打印全局變量(包變量)
whatis //打印類型信息
r //從新啓動並調試執行程序,等同於restart
call //整個程序執行
quit //退出調試器

協程相關

goroutine (alias: gr) //打印某個特定協程的信息
goroutines (alias: grs) //列舉全部的協程
goroutines -t //展開全部協程詳細信息
thread (alias: tr) //切換到某個線程
threads //打印全部的線程信息

棧相關

deferred //在 defer 函數上下文裏執行命令
down //上堆棧
frame //跳到某個具體的堆棧
stack (alias: bt) //打印堆棧信息
up //下堆棧

其餘命令

config //配置變動
disassemble (alias: disass) //反彙編
funcs //打印全部函數符號
libraries //打印全部加載的動態庫
list (alias: ls | l) //顯示源碼
source //加載命令
sources //打印源碼
types //打印全部類型信息

(4)dlv的其它命令

dlv debug:使用dlv debug能夠在main函數文件所在目錄直接對main函數進行調試,也能夠在根目錄以指定包路徑的方式對main函數進行調試
dlv exec:使用dlv exec能夠對編譯好的二進制進行調試
dlv test:使用dlv test能夠對test包進行調試
dlv attach:使用dlv attach能夠附加到一個已在運行的進程進行調試
dlv connect:使用dlv connect能夠鏈接到調試服務器進行調試
dlv trace:使用dlv trace能夠追蹤程序

11、go race

一、Go 語言提供了 race 檢測(Go race detector)來進行競爭分析和發現。
二、go run -race main.go 是運行時檢測,並非編譯時。使用 race 時存在明顯的性能開銷,所以儘可能不要在生產環境中使用這個。

12、GDB

一、gdb的相關命令

info goroutines //打印全部的goroutines
goroutine ${id} bt //打印一個goroutine的堆棧
iface //打印靜態或者動態的接口類型

二、gdb的相關函數

len //打印string,slices,map,channels 這四種類型的長度
cap //打印slices,channels 這兩種類型的cap
dtype //強制轉換接口到動態類型
相關文章
相關標籤/搜索