当前位置: 代码迷 >> 综合 >> 『每周译Go』go 中的 pprof 工具和格式化
  详细解决方案

『每周译Go』go 中的 pprof 工具和格式化

热度:30   发布时间:2023-12-06 00:55:10.0

在 Go 中内置的几种分析器都是为使用 pprof(https://github.com/google/pprof)的可视化工具而设计的。pprof 本身不是谷歌的官方项目,pprof 一开始设计的目的是分析来自 c++、Java 和 Go 程序的数据。该项目使用 protocol buffer 协议定义了所有 Go 分析器的格式,本文都会对他们进行描述。

Go 项目本身捆绑了(https://github.com/golang/go/tree/master/src/cmd/pprof)一个可以通过 go tool pprof 命令调用的 pprof 工具。除了一些调整外,它与原本的 pprof 工具大致相同。Go 建议始终使用 go tool pprof 命令来进行分析,而不是原来的 pprof 工具来分析 Go 。

特点

pprof 工具提供一个交互式命令行界面,一个 Web UI,还包括各种其他输出格式选项。

文件格式

描述

pprof 相关的格式定义在 profile.proto (https://github.com/google/pprof/blob/master/proto/profile.proto)文件中, 这是一个使用 protocol buffer 协议定义的文件,并且这个文件带有比较全面的注释。另外,还有一个官方的说明(https://github.com/google/pprof/blob/master/proto/README.md) 。pprof 保存在磁盘的文件使用的是 gzip 格式压缩的。

俗话说,一张图胜过千言万语,下面是 pprof 工具的可视化格式,这张图片是使用protodot(https://github.com/seamia/protodot) 工具自动生成的。

280d01fd916237ec9840d9610d41547e.png

pprof 的数据格式似乎是为效率、多语言 (编程语言) 和不同的性能分析类型 (CPU、堆等) 而设计的,但正因为如此,它非常抽象,显得不直观。如果你想了解所有细节,请点击这里(https://github.com/google/pprof/blob/master/proto/profile.proto) 。继续向下阅读也能知道这么设计的原因。pprof 文件包含采集的堆栈列表,这些堆栈信息具有一个或多个与之关联的数值。比如对于 CPU 性能分析,该值可能是在性能分析期间观察到堆栈的 CPU 持续时间(以纳秒为单位)。对于堆性能分析,它可能是分配的字节数。值类型本身在文件的开头进行了描述,并用于填充 pprof UI 中的 “SAMPLE” 下拉列表。除了值之外,每个堆栈跟踪还可以包括一组标签。标签是键值对,甚至可以包含一个单元。在 Go 中,这些标签应用在分析器标签(https://rakyll.org/profiler-labels/) 上 。

性能分析还包括记录性能分析的时间(UTC 格式)以及记录的持续时间。

另外,这个格式允许删除/保留正则表达式以排除/包括某些堆栈跟踪信息,但 Go 不使用(https://github.com/golang/go/blob/go1.15.6/src/runtime/pprof/proto.go#L375-L376) 它们。还有一个注释列表(也没有使用(https://github.com/golang/go/search?q=tagProfile_Comment)),以及描述采样的周期间隔时间。

在 Go 中生成 pprof 输出的代码可以在:runtime/pprof/proto.go(https://github.com/brendangregg/FlameGraph#2-fold-stacks) 中找到。

解码

下面是一些用于将 pprof 文件解码为人类可读文本输出的工具。它们按输出格式的复杂程度排序,显示简化输出的工具列在最前面:

使用 pprofutils 来进行辅助分析

pprofutils(https://github.com/felixge/pprofutils) 是一个用于在 pprof 文件和 Brendan Gregg 的折叠文本(https://github.com/felixge/pprofutils) 格式之间转换的小工具。你可以这样使用它

$ pprof2text < examples/cpu/pprof.samples.cpu.001.pb.gzgolang.org/x/sync/errgroup.(*Group).Go.func1;main.run.func2;main.computeSum 19
golang.org/x/sync/errgroup.(*Group).Go.func1;main.run.func2;main.computeSum;runtime.asyncPreempt 5
runtime.mcall;runtime.gopreempt_m;runtime.goschedImpl;runtime.schedule;runtime.findrunnable;runtime.stopm;runtime.notesleep;runtime.semasleep;runtime.pthread_cond_wait 1
runtime.mcall;runtime.park_m;runtime.schedule;runtime.findrunnable;runtime.checkTimers;runtime.nanotime;runtime.nanotime1 1
runtime.mcall;runtime.park_m;runtime.schedule;runtime.findrunnable;runtime.stopm;runtime.notesleep;runtime.semasleep;runtime.pthread_cond_wait 2
runtime.mcall;runtime.park_m;runtime.resetForSleep;runtime.resettimer;runtime.modtimer;runtime.wakeNetPoller;runtime.netpollBreak;runtime.write;runtime.write1 7
runtime.mstart;runtime.mstart1;runtime.sysmon;runtime.usleep 3

使用 go tool pprof 命令

pprof 本身有一个名为 -raw 的输出模式,它将向您显示 pprof 文件的内容。但是,应该注意的是,这并不像它得到的那样原始,签出协议如下所示

$ go tool pprof -raw examples/cpu/pprof.samples.cpu.001.pb.gzPeriodType: cpu nanoseconds
Period: 10000000
Time: 2021-01-08 17:10:32.116825 +0100 CET
Duration: 3.13
Samples:
samples/count cpu/nanoseconds19  190000000: 1 2 35   50000000: 4 5 2 31   10000000: 6 7 8 9 10 11 12 13 141   10000000: 15 16 17 11 18 142   20000000: 6 7 8 9 10 11 18 147   70000000: 19 20 21 22 23 24 143   30000000: 25 26 27 28
Locations1: 0x1372f7f M=1 main.computeSum /Users/felix.geisendoerfer/go/src/github.com/felixge/go-profiler-notes/examples/cpu/main.go:39 s=02: 0x13730f2 M=1 main.run.func2 /Users/felix.geisendoerfer/go/src/github.com/felixge/go-profiler-notes/examples/cpu/main.go:31 s=03: 0x1372cf8 M=1 golang.org/x/sync/errgroup.(*Group).Go.func1 /Users/felix.geisendoerfer/go/pkg/mod/golang.org/x/sync@v0.0.0-20201207232520-09787c993a3a/errgroup/errgroup.go:57 s=0...
Mappings
1: 0x0/0x0/0x0   [FN]

上面的样例没有给出全部的内容,点击这里查看所有的内容(https://github.com/gperftools/gperftools/blob/master/src/pprof)。

Using protoc
使用 protoc

对于有兴趣研究更接近原始二进制存储的数据开发者,则需要借助 protocol buffer 的编译器。在 macOS 上,您可以使用 brew install protobuf 来安装它,对于其他平台,请查看protocol buffer 安装部分的内容(https://github.com/gperftools/gperftools/blob/master/src/pprof) 。

现在让我们来分析上面的 CPU 的性能分析数据:

$ gzcat examples/cpu/pprof.samples.cpu.001.pb.gz | protoc --decode perftools.profiles.Profile ./profile.protosample_type {type: 1unit: 2
}
sample_type {type: 3unit: 4
}
sample {location_id: 1location_id: 2location_id: 3value: 19value: 190000000
}
sample {location_id: 4location_id: 5location_id: 2location_id: 3value: 5value: 50000000
}
...
mapping {id: 1has_functions: true
}
location {id: 1mapping_id: 1address: 20393855line {function_id: 1line: 39}
}
location {id: 2mapping_id: 1address: 20394226line {function_id: 2line: 31}
}
...
function {id: 1name: 5system_name: 5filename: 6
}
function {id: 2name: 7system_name: 7filename: 6
}
...
string_table: ""
string_table: "samples"
string_table: "count"
string_table: "cpu"
string_table: "nanoseconds"
string_table: "main.computeSum"
string_table: "/Users/felix.geisendoerfer/go/src/github.com/felixge/go-profiler-notes/examples/cpu/main.go"
...
time_nanos: 1610122232116825000
duration_nanos: 3135113726
period_type {type: 3unit: 4
}
period: 10000000

这份输出样例也是没有给出全部的内容,点击这里查看所有的内容。

历史

最初的 pprof 工具(https://github.com/gperftools/gperftools/blob/master/src/pprof) 是谷歌内部使用 perl 开发的脚本。根据版权标题,开发可能要追溯到 1998 年。它于 2005 年作为 gperftools (https://github.com/google/tcmalloc/blob/master/docs/gperftools.md)的一部分首次发布,并于 2010 年 添加 (https://github.com/golang/go/commit/8b5221a57b41a19abcb4e3dde20014af494048c2)到 Go 项目中。

2014 年,Go 项目用 Raul Silvera 的 Go 实现取代了(https://github.com/golang/go/commit/8b5221a57b41a19abcb4e3dde20014af494048c2)基于 perl 的 pprof 工具,目前谷歌已经使用了这个实现的工具。这个实现在 2016 年作为一个独立的项目(https://github.com/google/pprof) 进行重新发布。从那时起,Go 项目一直在提供上游项目的 pprof,并定期对其进行 更新(https://github.com/golang/go/commits/master/src/cmd/vendor/github.com/google/pprof) 。

Go 1.9(2017-08-24) 增加了对 pprof 标签的支持。它还开始在默认情况下将符号信息包含到 pprof 文件中,这允许在不访问二进制文件的情况下查看程序性能。

Todo

  • 更多的篇幅应该专注于 go tool pprof 工具本身.

  • 解释为什么可以给 pprof 一个路径到概要文件所属的二进制文件。

  • 更加多关于行号和地址的细节信息

  • 讨论 go 的二进制文件有多个版本时的关联映射问题

免责声明

我是 felixge ,在Datadog 从事关于 go 代码性能分析 的工作。你应该去看看。我们也在 招聘 :)。

本页上的信息不保证是正确的。我们欢迎你的反馈。

原文信息

原文地址:https://github.com/DataDog/go-profiler-notes/blob/main/pprof.md

原文作者:felixge

本文永久链接:https://github.com/gocn/translator/blob/master/2021/w39_go_profiler_notes_pprof_tool_format.md

译者:小超人

校对:fivezh、lsj1342

想要了解关于 Go 的更多资讯,还可以通过扫描的方式,进群一起探讨哦~

e55947b31f416cfc282b741f60dec254.png