使用内存探测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

相关推荐
Linux-lucky2 小时前
45-Linux学习之旅之防火墙与WEB加固
linux·前端·网络·学习·ubuntu
禁默2 小时前
启元 Q1 二次开发实践:Ubuntu 部署与仿真验证,从 PrimeConsole 到 ROS 2 Humble + RViz
linux·运维·ubuntu
skywalk81633 小时前
把 去也网站 从 Ubuntu 搬到 FreeBSD:一次 Service Jail + nginx 的部署实录
linux·运维·nginx·ubuntu·freebsd
爱莉希雅&&&5 小时前
GitLab 完整学习笔记
linux·运维·服务器
风笙9785 小时前
[文件系统]文件系统挂载与卸载详解
linux
GeW5 小时前
新质生产力路线图解读:数据库性能优化与Linux调优专场
linux·数据库
陈希1237 小时前
Linux 服务器性能排查手册(2026):CPU、内存、磁盘IO、网络四大瓶颈定位
linux·服务器·网络
测试界的小码农10 小时前
【Linux 命令实战(03):文件创建、复制、移动与删除,掌握 touch、mkdir、cp、mv、rm】
linux·服务器·php
byte轻骑兵11 小时前
实战 1:修改 BlueZ 源码,添加自定义开机日志
linux·aiot·bluez·电脑蓝牙·嵌入式蓝牙
叼烟扛炮11 小时前
Linux 学习笔记(五十):网络层——IP 协议、子网划分与 NAT 魔法
linux·笔记·学习