高延迟的 2 个场景,触发 perf 录包思路
当前 perf 没有常驻内存,后续提供 perf 常驻内存功能。且 perf 启动需要 0.5~1s,所以,存在 2 个场景
1.频繁连续高延迟(复现后的几秒内,继续频繁复现):可以配置自动触发工具,自动触发 perf trace 记录函数耗时,或 perf record 记录 cpu 火焰图。
2.出现一次后,过好久才会复现第二次:需要一直开启 perf trace 或 perf record,直到复现高延迟。(后续 perf 常驻内存后,可同场景 1,使用自动触发工具解决)
场景 1,配置高延迟自动触发工具去抓包
场景 2,使用 root 用户,在 MAP 容器内执行如下命令,会一直抓包 6 小时。当抓到包后,需要用户手动杀掉该进程,停止抓包:
./trigger_perf_trace.sh loop
抓 perf trace 和 perf record 包脚本:
可以放到自己的目录下,例如放到:/home/mogo/data/shizhonghe/trigger_perf_trace.sh
#!/bin/bash -e function trigger_once() { now_str=` date "+%Y%m%d_%H%M%S.%N" ` date_str=` date "+%Y-%m-%d" ` uptime=` cat /proc/uptime | awk '{print $1}' ` log_dir= /home/mogo/data/log/monitor_cpu_mem_net/ ${date_str} /szh_ ${now_str} pid=` ps -ef | grep '__name:=local_planning' | grep - v grep | awk '{print $2}' ` mkdir -p ${log_dir} if [ "${pid}" != "" ] ; then perf trace -T -p ${pid} -o "${log_dir}/perf_trace_${pid}_${now_str}_${uptime}.log" sleep $1 & else echo "pid null" > ${log_dir} /perf_trace_ ${pid}_${now_str}_${uptime}.log sleep $1 fi if which lsof > /dev/null ; then lsof -Pn -p ${pid} > ${log_dir} /lsof_ ${pid}_${now_str}.log fi wait } function main() { if [ "$1" == "loop" ] ; then local run_time=300 for ((i=1; i<=72; i++)) do trigger_once ${run_time} done elif [ "$1" == "trigger" ] ; then local run_time=5 trigger_once ${run_time} else echo "param unmatched!" fi } main "$@" |
使用 perf trace 还是 perf record?
1. perf trace: linux 内核中存在一些埋点,这些埋点会记录内核 api 的时延信息。睡眠时延高(死锁了、io 操作多等),使用 perf trace 可以排查到
例如: perf trace 显示工作线程 write 函数写文件,耗时 30ms,此时说明写文件卡住了 30ms,此时一般是同步写文件了,需要改为异步写文件
perf trace 显示工作线程中 futex wait 了 30ms,说明等待条件变量等待了 30ms
2.perf record 会对 cpu 采样,记录单个进程/多个进程/MAP 容器所有进程的, cpu 运行的函数样本,每个采样点都是一个堆栈,采集一段时间后,会得到很多堆栈,可以把这些堆栈合并到一起,即:火焰图
某个函数/进程的 cpu 占用率 = 该函数/进程的采样点数量 ÷ 总采样数量
如果某个模块
使用 perf trace 抓到数据后,如何找到高耗时 api
打印耗时 > 20ms 的内核 api
import sys for line in open (sys.argv[ 1 ], "r" ): if "local_planning" not in line: continue l1 = line.split( "(" ) if len (l1) > 1 : l1 = l1[ 1 ] else : continue l2 = l1.split( "ms):" ) if len (l2) > 1 : l2 = l2[ 0 ] else : continue tc = float (l2) if tc > = 20.0 and tc < 10000 : print (line) |