准备工作
- 安装 graphviz
- brew install graphviz
- 将 $GOPATH/bin 加入 $PATH
- Mac.OS:在 .bash_profile中修改路径
- 安装 go-torch (火炬图)
- 下载并复制 flamegraph.pl (https://github.com/brendangregg/FlameGraph) 至 $GOPATH/bin 路径下
- 将$GOPATH/bin 加入 $PATH
- go get -v github.com/uber/go-torch
通过文件方式输出 Profile
- 灵活性高,适用于特定代码段的分析
- 通过手动调用 runtime/pprof 的 API
- API 相关文档 https://studygolang.com/static/pkgdoc/pkg/runtime_pprof.htm
- go tool pprof [binary][binary.prof]
Go支持的多种 Profile
go help testflag
https://golang.org/src/runtime/pprof/pprof.go
我们来看一下 用于生成profile的示例代码:
prof.go
package main
import (
"log"
"math/rand"
"os"
"runtime/pprof"
"time"
)
const (
row = 10000
col = 10000
)
func fillMatrix(m *[row][col]int) {
s := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < row; i++ {
for j := 0; j < col; j++ {
m[i][j] = s.Intn(100000)
}
}
}
func calculate(m *[row][col]int) {
tmp := 0
for i := 0; i < row; i++ {
for j := 0; j < col; j++ {
tmp += m[i][j]
}
}
}
func main() {
// 创建输出文件
f, err := os.Create("cpu.prof")
if err != nil {
log.Fatal("could not create CPU profile:", err)
}
// 获取系统信息
if err := pprof.StartCPUProfile(f); err != nil { //监控cpu
log.Fatal("could not start CPU profile:", err)
}
defer pprof.StopCPUProfile()
// 主逻辑区,进行一些简单的代码运算
x := [row][col]int{}
fillMatrix(&x)
calculate(&x)
f1, err := os.Create("mem.prof")
if err != nil {
log.Fatal("could not create CPU profile:", err)
}
// runtime.GC() // GC,获取最新的数据信息
if err := pprof.WriteHeapProfile(f1); err != nil { //写入内存信息
log.Fatal("could not write memory profile:", err)
}
f1.Close()
f2, err := os.Create("goroutine.prof")
if err != nil {
log.Fatal("could not create goroutine profile:", err)
}
if gProf := pprof.Lookup("goroutine"); gProf == nil {
log.Fatal("could not write goroutine profile")
} else {
gProf.WriteTo(f2, 0)
}
f2.Close()
}
pprof
- 编译:
go build ./prof.go
- 运行:生成的可执行二进制文件
./prof.go
,生成对应的 cpu.prof、mem.prof、goroutine.prof - 查看:
go tool pprof ./prof ./cpu.prof
进入一个交互式的控制台
File: prof
Type: cpu
Time: Nov 9, 2020 at 4:02pm (CST)
Duration: 200.45ms, Total samples = 20ms ( 9.98%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
使用top命令查看 top CPU 的情况
(pprof) top
Showing nodes accounting for 2180ms, 99.09% of 2200ms total
Dropped 12 nodes (cum <= 11ms)
flat flat% sum% cum cum%
780ms 35.45% 35.45% 2150ms 97.73% main.fillMatrix
710ms 32.27% 67.73% 1100ms 50.00% math/rand.(*Rand).Int31n
270ms 12.27% 80.00% 1370ms 62.27% math/rand.(*Rand).Intn
220ms 10.00% 90.00% 220ms 10.00% math/rand.(*rngSource).Uint64 (inline)
110ms 5.00% 95.00% 330ms 15.00% math/rand.(*rngSource).Int63
60ms 2.73% 97.73% 390ms 17.73% math/rand.(*Rand).Int63 (inline)
30ms 1.36% 99.09% 30ms 1.36% main.calculate
0 0% 99.09% 2190ms 99.55% main.main
0 0% 99.09% 390ms 17.73% math/rand.(*Rand).Int31 (inline)
0 0% 99.09% 2190ms 99.55% runtime.main
(pprof)
- flat 是对应函数本身使用的时间和所占的比例
- cum 即cumulation,表示对应函数还调用了别的函数,总体加合在一起的执行时间和所占的比例
使用 list 命令查看指定函数详情
(pprof) list fillMatrix
Total: 2.20s
ROUTINE ======================== main.fillMatrix in /root/go_learn/src/ch31/tools/file/prof.go
780ms 2.15s (flat, cum) 97.73% of Total
. . 15:
. . 16:func fillMatrix(m *[row][col]int) {
. . 17: s := rand.New(rand.NewSource(time.Now().UnixNano()))
. . 18:
. . 19: for i := 0; i < row; i++ {
40ms 40ms 20: for j := 0; j < col; j++ {
740ms 2.11s 21: m[i][j] = s.Intn(100000)
. . 22: }
. . 23: }
. . 24:}
. . 25:
. . 26:func calculate(m *[row][col]int) {
(pprof)
可以看到所有的耗时基本是在 m[i][j] = s.Intn(100000)
这句;
也可以查看CPU耗时对应的图片
(pprof) svg
Generating report in profile001.svg
mem.prof 及 goroutine.prof 命令与cpu.prof 一样
使用 go-torch 生成火炬图
go-torch cpu.prof
通过HTTP方式输出 Profile
- 简单,适合于持续性运行的应用
- 在应用程序中导入import _ "net/http:/pprof,并启动 http server 即可
http://<host>:<port>/debug/pprof/
go tool pprof _http://<host>:<port>/debug/pprof/profile?seconds=10
(默认值为30秒)go-torch -seconds 10 http://<host>:<port>/debug/pprof/profile
示例代码:
fb_server.go
package main
import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
)
// GetFibonacciserie function
func GetFibonacciserie(n int) []int {
fidList := []int{1, 1}
for i := 2; i < n; i++ {
fidList = append(fidList, fidList[i-2]+fidList[i-1])
}
return fidList
}
func index(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Welcome!"))
}
func createFBS(w http.ResponseWriter, r *http.Request) {
var fbs []int
for i := 0; i < 1000000; i++ {
fbs = GetFibonacciserie(50)
}
w.Write([]byte(fmt.Sprintf("%v", fbs)))
}
func main() {
http.HandleFunc("/", index)
http.HandleFunc("/fb", createFBS)
log.Fatal(http.ListenAndServe(":8081", nil))
}
- 在浏览器查看结果:
http://127.0.0.1:8081/fb
- 在浏览器debug:
http://127.0.0.1:8081/debug/pprof/
- 在命令行:
go tool pprof http://127.0.0.1:8081/debug/pprof/profile
可以看到跟文件是一样的进入一个交互的控制台