前言
- 本文参考:
1.1 性能调优利器:火焰图
SystemTap
- 使用 SystemTap 绘制火焰图的主要流程如下:
1.1 安装 SystemTap 以及 操作系统符号调试表
1.2 根据自己所需绘制的火焰图类型以及进程类型选择合适的脚本
1.3 生成内核模块
1.4 运行 SystemTap 或者运行生成的内核模块统计数据
1.5 将统计数据转换成火焰图
使用SystemTap
- 要部署SystemTap,请安装SystemTap软件包以及内核相应的-devel, -debuginfo和-debuginfo-common软件包集
1.1 如果你的系统安装了多个内核,并且你打算在多个内核上使用SystemTap,需要为每个内核版本安装-devel和-debuginfo包
安装和设置
- 在使用SystemTap之前,需要安装所需的内核信息包
// 以root身份运行 stap-prep
- 安装SystemTap
// 以root身份运行 yum install systemtap systemtap-runtime
手动安装
- 查看centos版本
- 下载内核版本对应的开发包和调试包
2.1 下载kernel-devel-$(uname -r).rpm包
(1) 查看系统内核版本
2.2 下载kernel-debuginfo-common和kernel-debuginfo包 - 安装当前内核版本对应的开发包和调试包
3.1 安装kernel-devel时,有如下提示# rpm -ivh kernel-debuginfo-common-$(uname -r).rpm # rpm -ivh kernel-debuginfo-$(uname -r).rpm # rpm -ivh kernel-devel-$(uname -r).rpm
- 安装 systemtap
4.1 执行上述命令,有如下提示(但貌似不影响)yum install systemtap
- 测试systemtap安装成功否
5.1 执行如下命令
5.2 有如下截图展示,则表示安装成功stap -v -e 'probe vfs.read {printf("read performed\n"); exit()}'
火焰图的绘制
火焰图类型
选择合适的脚本
- 使用SystemTap统计相关数据,需要自己依照它的语法,编写脚本(不会,太难了)
- 春哥(agentzh)开源了两组他常用的 SystemTap 脚本:openresty-systemtap-toolkit 和 stapxx
2.1 根据自己需要绘制的火焰图类型以及进程类型选择合适的脚本 - sample-bt 工具
3.1 这个脚本可以对指定的任意用户进程进行调用栈的采样$ ./sample-bt -p 8736 -t 5 -u > a.bt WARNING: Tracing 8736 (/opt/nginx/sbin/nginx) in user-space only... WARNING: Missing unwind data for module, rerun with 'stap -d stap_df60590ce8827444bfebaf5ea938b5a_11577' WARNING: Time's up. Quitting now...(it may take a while) WARNING: Number of errors: 0, skipped probes: 24
3.2 结果的输出文件 a.bt 可以使用 Brendan Gregg 的 FlameGraph 工具集 来生成火焰图参数: -p:代表进程ID -t:代表采样时间 单位是秒 -u 采集用户空间调用栈信息 -k 采集内核空间调用栈信息
stackcollapse-stap.pl a.bt > a.cbt flamegraph.pl a.cbt > a.svg
生成火焰图
- 执行脚本采集
- 结果的输出文件 a.bt 来生成火焰图a.svg
- 最终非常顺利地生成了如下的火焰图
3.1 函数没有名字,编译器只用内存地址来表示(比如匿名函数)
火焰图缺失Java函数信息的原因
- 原因
1.1 JVM内部即时编译器JIT,不会公开传统的符号表供系统分析器读取
1.2 JVM默认使用帧指针寄存器(x86-64上的RBP)作为通用寄存器,与传统的栈不同 - 解决方案
2.1 使用开源JVMTI代理perf-map-agent,创建/tmp/perf-.map文件,内部列出符号地址(十六进制),大小和符号名称。perf_events默认查找此文件,如果找到,则将其用于符号转换
2.2 使用-XX:+PreserveFramePointer,以便perf可以精准的获取栈结构,会导致一定的性能损耗