乐趣区

关于golang:Go程序性能分析工具和方法

作者:宁亮

一、罕用剖析命令和工具

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

二、程序编译时的参数传递

1、gcflags

// 可应用 go tool compile --help 查看可用参数及含意
go build -gcflags="-m"   

比方 -N 禁用编译优化,-l 禁止内联,-m 打印编译优化策略(包含逃逸状况和函数是否内联,以及变量调配在堆或栈),-S 是打印汇编。

如果只在编译特定包时须要传递参数,格局应恪守“包名 = 参数列表”,如 go build -gcflags=’log=-N -l’ main.go

2、ldflags

go build 用 -ldflags 给 go 链接器传入参数,理论是给 go tool link 的参数,能够用 go tool link –help 查看可用的参数。

罕用 -X 来指定版本号等编译时才决定的参数值。例如代码中定义 var buildVer string,而后在编译时用 go build -ldflags “-X main.buildVer=1.0” 来赋值。留神 -X 只能给 string 类型变量赋值。

三、go build -x

能够列出 go build 触发的所有命令,比方工具链、跨平台编译、传入内部编译器的 flags、链接器等,可应用 -x 来查看所有的触发。

四、竞争检测

应用 go run -race main.go 或 go build -race main.go 来进行竞争检测。

五、GC 日志

  • 执行前增加零碎环境变量 GODEBUG=’gctrace=1′ 来跟踪打印垃圾回收器信息
  • 在代码中应用 runtime.ReadMemStats 来获取程序以后内存的应用状况
  • 应用 pprof 工具

例如 GODEBUG=gctrace=1 go run main.go // 跟踪打印垃圾回收器信息,Go 程序会每隔一段时间打印一些 gc 信息。

六、Pprof

Go 语言内置了获取程序运行数据的工具,包含以下两个规范库

(1)runtime/pprof:采集工具型利用运行数据进行剖析
(2)net/http/pprof:采集服务型利用运行时数据进行剖析

1、工具型利用剖析

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

2、应用 net/http 包时启用 Pprof

package main

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

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

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

3、应用 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")
}

七、拜访 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 运行工夫过长

1、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 的信息的记录,以及调用关系。仅在获取时取样一次。

2、Pprof 可视化

如在 Mac 上通过 brew 装置 graphviz,能够执行以下命令 brew install graphviz

3、以本地文件模式获取剖析后果

能够先把信息 dump 到本地文件,而后用 go tool 去剖析。

4、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…

5、第三方工具 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 查看采集到的指标数据。

注:也能够通过一个端口同时开启 pprof + charts + prometheus。

八、go tool [xxx]系列命令

1、输出 go tool 可查看内置的所有 [xxx] 工具命令

addr2line
api
asm
buildid
cgo
compile:代码汇编
cover:生成代码覆盖率
dist
doc
fix
link
nm:查看符号表(等同于零碎 nm 命令)objdump:反汇编工具,剖析二进制文件(等同于零碎 objdump 命令)oldlink
pack
pprof:性能和指标剖析工具
test2json
trace:采样一段时间,指标跟踪剖析工具
vet

2、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

3、go tool compile 汇编某个文件

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

4、go tool objdump 反汇编二进制的工具

go tool objdump main.o
go tool objdump -s DoFunc main.o // 反汇编具体函数

5、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%,就疏忽它,不要显示在图片中。

九、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

十、Delve

delve 以后是最敌对的 golang 调试程序,ide 调试其实也是调用 dlv 而已,比方 goland 应用的调试。
装置 dlv:go get -u github.com/go-delve/delve/cmd/dlv
查看装置版本信息:dlv version

把程序加载进 Delve 调试器的两种形式(当时须要有 go.mod)

1、加载源码进行调试

dlv debug
(1)执行 dlv debug 进入命令行模式,此时同目录下会主动生成一个 __debug_bin 文件。这个文件是由源码编译生成的,并会主动加载进调试器。
(2)Delve 冀望的是从单个程序或我的项目中构建出单个二进制文件,如果目录中有多个源文件且每个文件都有本人的主函数,Delve 则可能抛出谬误。此种状况下应该应用上面第二种形式,加载二进制文件进行调试。

2、加载二进制文件进行调试

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、1、2、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 能够追踪程序

十一、go race

1、Go 语言提供了 race 检测(Go race detector)来进行竞争剖析和发现。
2、go run -race main.go 是运行时检测,并不是编译时。应用 race 时存在显著的性能开销,因而尽量不要在生产环境中应用这个。

十二、GDB

1、gdb 的相干命令

info goroutines // 打印所有的 goroutines
goroutine ${id} bt // 打印一个 goroutine 的堆栈
iface // 打印动态或者动静的接口类型

2、gdb 的相干函数

len // 打印 string,slices,map,channels 这四种类型的长度
cap // 打印 slices,channels 这两种类型的 cap
dtype // 强制转换接口到动静类型
退出移动版