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