使用内存探测bpf

一、完整实操步骤(标准化流程)

1. 启动KV服务端程序

执行启动命令,正常拉起KV存储服务,确保服务端口监听正常、无启动报错:

./kvstore

启动后保持服务常驻后台运行,等待客户端压测/请求访问。

2. 获取KV服务进程PID

通过pidof精准获取进程ID,避免ps筛选出错:

pidof kvstore

示例输出:12345,将该PID赋值为环境变量,方便后续命令复用:

KV_PID=12345

3. 启动bpftrace实时内存追踪(核心命令)

该命令通过进程挂载探针,直接跟踪KV程序内部自定义的内存申请/释放函数,不依赖libc库、精准过滤业务内存操作,每秒输出一次累计调用次数:

sudo bpftrace -p "$KV_PID" -e "

uprobe:/proc/$KV_PID/exe:kvs_malloc { @alloc_calls = count(); } uprobe:/proc/$KV_PID/exe:kvs_free { @free_calls = count(); }

interval:s:1 { printf(\"===== 每秒内存调用统计 =====\n\"); printf(\"累计kvs_malloc调用次数:\"); print(@alloc_calls); printf(\"累计kvs_free调用次数:\"); print(@free_calls); } "

执行后该终端保持常驻,持续采集内存操作数据,不要关闭。

4. 执行客户端压测/业务请求

新开终端,运行KV客户端测试脚本、压测工具或业务读写请求,触发KV服务的增删改查、数据落地、缓存读写等逻辑,让服务产生内存分配和释放行为。

5. 实时观察bpftrace输出,判断内存状态

持续查看追踪终端的每秒统计数据,结合数据变化判断内存异常,核心排查逻辑见下文。

二、核心命令逐行深度解析

1. 进程挂载参数

-p "$KV_PID":指定追踪目标进程,仅对该KV进程生效,不影响其他进程,精准度极高。

2. 探针原理

uprobe:/proc/$KV_PID/exe:kvs_malloc:用户态探针,挂载当前KV进程可执行文件内的自定义内存分配函数 kvs_malloc

uprobe:/proc/$KV_PID/exe:kvs_free:挂载自定义内存释放函数kvs_free

优势:无需修改代码、无需重启服务、无侵入式追踪,生产环境可临时排查。

3. 统计逻辑

@alloc_calls = count():全局计数器,每次调用kvs_malloc就累加,记录累计申请次数

@free_calls = count():记录累计释放次数

4. 输出频率

interval:s:1:定时器,每秒触发一次打印,实时监控内存调用变化趋势。

三、输出结果解读 & 故障判断标准

1. 正常业务状态

客户端正常读写、请求平稳时:

  • kvs_malloc、kvs_free 次数同步稳步增长

  • 两者差值保持稳定、无持续拉大

  • 请求结束后,无新增malloc调用,内存趋于平稳

说明:内存分配释放均衡,无内存泄漏,业务内存逻辑正常。

2. 典型内存泄漏特征(核心排查重点)

  • kvs_malloc 持续上涨,kvs_free 几乎不增长

  • 两者差值随时间不断拉大

  • 客户端停止请求后,malloc累计数不再增加,但free无补偿释放,差值永久保留

结论:KV业务存在申请内存未释放,大概率是key过期未回收、临时缓冲区未释放、迭代器内存遗漏、事务回滚内存未清理等问题。

3. 内存突增异常

短时间内malloc调用暴涨,free调用跟不上,伴随服务RSS内存飙升:大概率是批量写入、批量加载数据时,临时内存批量申请未及时释放。

四、进阶增强版脚本(比原生命令更实用)

原生命令仅统计次数,无法看分配大小、单次内存粒度、调用栈,以下是生产排查增强脚本,可直接替换使用:

sudo bpftrace -p "$KV_PID" -e " // 统计分配/释放总次数 uprobe:/proc/$KV_PID/exe:kvs_malloc {

@alloc_cnt = count();

@alloc_size_sum += arg0; // 累计分配总内存

@alloc_hist = hist(arg0); // 内存分配大小分布直方图 } uprobe:/proc/$KV_PID/exe:kvs_free {

@free_cnt = count(); } /

/ 每秒打印详细统计

interval:s:1 {

printf(\"===== KV内存实时统计 =====\n\");

printf(\"累计分配次数: %-8d 累计释放次数: %-8d\n\",

@alloc_cnt, @free_cnt);

printf(\"累计分配总内存: %ld Bytes\n\", @alloc_size_sum);

printf(\"内存分配大小分布:\n\");

print(@alloc_hist); }

// 退出时打印最终汇总

END {

printf(\"\n===== 最终内存统计汇总 =====\n\");

print(@alloc_cnt);

print(@free_cnt);

print(@alloc_size_sum);

}"

增强功能说明

  • 新增累计分配总内存大小,直观看到内存增长量

  • 新增内存分配直方图,识别是小内存频繁分配还是大内存单次申请

  • 退出脚本后输出最终汇总数据,方便复盘

五、终极定位:抓取内存泄漏调用栈(精准定位代码行)

发现malloc/free差值持续拉大后,用以下脚本抓取内存分配的代码调用栈,直接定位是哪个业务函数在频繁分配且不释放内存:

sudo bpftrace -p "$KV_PID" -e "

uprobe:/proc/$KV_PID/exe:kvs_malloc { // 统计每个调用栈的分配次数 @mem_stack[ustack(10)] = count(); }

END { // 打印Top10高频分配堆栈 print(@mem_stack, 10); }"

使用方法:压测30秒后按下Ctrl+C退出,直接打印高频内存分配的代码堆栈,精准定位泄漏代码位置。

六、常见问题 & 避坑指南

1. 探针挂载失败:no such function

原因:程序编译时被优化、函数被内联、无符号表

解决:编译kvstore时添加编译参数 -g -fno-inline,保留函数符号和栈信息

2. 统计数据一直为0

原因:客户端无有效读写请求、未触发kvs_malloc逻辑

解决:确认压测脚本有效,触发KV数据写入、缓存创建等内存操作逻辑

3. 生产环境性能影响

bpftrace uprobe探针性能极低,短时排查(1-5分钟)完全不影响服务性能,禁止长时间挂着常驻,排查完毕立即Ctrl+C退出

七、排查结论判定总结

  1. 次数均衡、差值稳定:内存正常,无泄漏

  2. malloc持续涨、free停滞:存在业务内存泄漏,结合堆栈日志修代码

  3. 单次大内存分配:批量数据加载逻辑,可优化内存复用、分批释放

  4. 大量小内存频繁分配:可优化内存池,减少频繁alloc/free开销

参考链接 :0voice · GitHub

相关推荐
重生之我来学Python1 小时前
Docker套装的简介、安装、超级详细教程
linux·docker·容器·eureka·github
Ruiery2 小时前
Linux 6.6内核 PCIe 深度解析(十):PCIe Port Driver(portdrv)— 一个物理 Port,拆出五个逻辑服务
linux·运维·服务器
码农小韩3 小时前
Linux应用开发(二)——Linux的文件IO
linux·嵌入式软件·嵌入式操作系统·linux应用
Horn Still Sounds4 小时前
Linux网络并发服务器模型与SQLite数据库学习笔记
linux·服务器·数据库
10mAh4 小时前
【Linux】CPU 100% 怎么排查?——top、pidstat、jstack 到线程定位实战
linux·运维·服务器
深念Y4 小时前
开机启动优化记录
linux·开机
Titan20245 小时前
网络基础:传输层协议知识梳理
linux·服务器·网络
一叶龙洲5 小时前
Ubuntu从图标到卸载应用
linux·windows·ubuntu
爱吃提升5 小时前
VMware虚拟机迁移系统磁盘完整教程(VMware Workstation17)
linux·运维·服务器