高延迟的 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)
|