pprof调试技巧小结 - Tinsley's blog

/ 0评 / 0

pprof是google官方提供的golang内存/CPU/goroutine分析工具, 不可谓不强大, 简单记录下最近用到的几种调试技巧.

使用前

pprof可以用来分析go程序(非Server)的运行时数据(runtime/pprof)和http server的运行时数据(net/http/pprof), 本文主要针对后一种.

如果程序本身使用了net/http来建立http server, 例如:

import (
    "net/http"
)

func main() {
    http.ListenAndServe("0.0.0.0:8080", nil)
}

只需引入net/http/pprof即可:

import (
    "net/http"
    _ "net/http/pprof"   //加入这一行即可
)

pprof会自动注册/debug/pprof/*相关的api path. 但如果使用的是GIN框架, 则需要引入"github.com/gin-contrib/pprof"这个包, 并手动注册一下路由:

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

func main() {
    route = gin.Default()
    pprof.Register(route)
}

别的类似http框架可能也需要特殊处理一下, 此处不赘述.

分析方法

1. 直接访问web路径

上文提到, pprof会注册一系列/debug/pprof/*的api路径, 这些路径可以直接访问, 获取有限的信息.

例如直接访问http://your-host:port/debug/pprof, 可以看到类似此画面:

这里比较有用的信息有:

关于此处各项数值的解读, 可以参考这篇文章的第三节.

2. pprof 交互式命令行工具

要使用pprof 交互式命令行工具, 前提是机器已经配置好了go环境. go版本高的话, pprof cli提供的功能似乎会更丰富些.

go环境配置就不介绍了, 配置完之后安装pprof:

go get -u github.com/google/pprof

如果执行go tool pprof会蹦出一大堆帮助信息, 那大概是安装成功了.

简单地讲使用方法, 先进入交互界面:

# 分析堆内存信息
go tool pprof http://your-host:port/debug/pprof/heap/
# 分析历史内存分配
go tool pprof http://your-host:port/debug/pprof/allocs/
# 分析CPU Profile, 默认等待30s, 可以用 -seconds n 参数指定采样时间
go tool pprof http://your-host:port/debug/pprof/profile/
# 当然也可以直接使用上面提到的下载的profile文件
go tool pprof /local/path/to/file/profile
# 或者是dump内存信息到文件, 再进行分析
curl -s http://your-host:port/debug/pprof/heap > ~/xxxx.heap
go tool pprof ~/xxxx.heap

当你看到(pprof)开头一行闪着光标, 就可以继续输命令了, 常用的有:

top命令可以查看各项占用排名

(pprof) top
Showing nodes accounting for 57.19MB, 97.44% of 58.69MB total
Showing top 10 nodes out of 53
      flat  flat%   sum%        cum   cum%
   27.01MB 46.02% 46.02%    27.01MB 46.02%  reflect.mapassign
      11MB 18.75% 64.77%       11MB 18.75%  reflect.New
    5.50MB  9.37% 74.14%     5.50MB  9.37%  container/list.(*List).insertValue
       5MB  8.52% 82.66%        5MB  8.52%  reflect.makemap
       4MB  6.82% 89.47%        4MB  6.82%  encoding/gob.decString
    1.34MB  2.28% 91.76%     1.34MB  2.28%  github.com/open-falcon/falcon-plus/modules/graph/index.(*IndexCacheBase).Put
       1MB  1.70% 93.46%        1MB  1.70%  bytes.(*Buffer).String
       1MB  1.70% 95.16%        1MB  1.70%  container/list.New
    0.84MB  1.43% 96.59%     0.84MB  1.43%  github.com/open-falcon/falcon-plus/vendor/github.com/toolkits/container/nmap.(*SafeMap).Put
    0.50MB  0.85% 97.44%     0.50MB  0.85%  github.com/open-falcon/falcon-plus/modules/graph/store.(*GraphItemMap).Set

list + 函数名 可以看到这个函数调用的代码上下文:

(pprof) list reflect.mapassign
Total: 58.69MB
ROUTINE ======================== reflect.mapassign in /usr/local/go/src/runtime/map.go
   27.01MB    27.01MB (flat, cum) 46.02% of Total
         .          .   1314:   return elem
         .          .   1315:}
         .          .   1316:
         .          .   1317://go:linkname reflect_mapassign reflect.mapassign
         .          .   1318:func reflect_mapassign(t *maptype, h *hmap, key unsafe.Pointer, elem unsafe.Pointer) {
   27.01MB    27.01MB   1319:   p := mapassign(t, h, key)
         .          .   1320:   typedmemmove(t.elem, p, elem)
         .          .   1321:}
         .          .   1322:
         .          .   1323://go:linkname reflect_mapdelete reflect.mapdelete
         .          .   1324:func reflect_mapdelete(t *maptype, h *hmap, key unsafe.Pointer) {

交互式终端功能也有限, 适用于线上紧急的分析, 目前我大概只用到上述的两种方法.

3. pprof web可视化界面

终于讲到最有用的部分了...

如果说前两个方法只是开胃小菜, pprof本地建立起http server提供的web可视化页面绝对是大杀器了.

使用pprof web可视化界面前需要先安装Graphviz, 请根据你的Linux发行版自行安装.

开启的方式与上一节交互式命令行方法类似, 仅增加一个参数:

# 使用 --http :port参数开启本地http server
go tool pprof --http :8081 http://your-host:port/debug/pprof/heap/
# 也可以对0.0.0.0开放监听端口
go tool pprof --http 0.0.0.0:8081 http://your-host:port/debug/pprof/heap/
# 或者是对于dump内存文件/profile文件进行分析
go tool pprof --http :8081 /local/path/to/file/profile
curl -s http://your-host:port/debug/pprof/heap > ~/xxxx.heap
go tool pprof --http :8081 ~/xxxx.heap

然后打开浏览器, 访问你在--http参数中指定的ip和端口号, 就能看到web可视化界面了!

顶部菜单栏的VIEW中, 可以选择看top信息, 火焰图, 各函数调用上下文等等. SAMPLE中可以选择alloc_objects, alloc_space, inuse_objects, inuse_space四种采样结果.

(falcon-graph的火焰图有些一言难尽...)

另外提个隐藏技巧: 在TopGraph页面可以单击指定函数名, 再跳转到Flame Graph, Source, Peek页面, 可以仅查看该函数的相关内容, 高效地过滤一波无用信息.

好家伙, 我tm直接好家伙.jpg

鉴于内容已经相当直观, 就不一一介绍各个页面了, 不明之处可参见golang官方的这篇博文.

4. 比较两个时间点的内存升降

很多时候, 只查看某一个时间点的内存使用状况, 结果并不能很好的协助定位是什么地方发生了内存泄露, 但如果对比两个时间点的函数使用的内存升降, 就一目了然了. 感谢smallnest此文提供思路.

pprof可以使用--base参数比较不同时间点的两个dump文件:

# 在时间点1 dump内存
curl -s http://your-host:port/debug/pprof/heap > previous.heap
# 在时间点2 dump内存
curl -s http://your-host:port/debug/pprof/heap > current.heap
# 加上 --base 参数进行比较
go tool pprof --http :8081 --base previous.heap current.heap

后面的操作与第三节无异.

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注