2024年内存泄露专题(5)动态内存追踪大杀器:bcc_bcc内存泄漏(1),2024年C C++开发陷入饱和

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

int main() {
int i = 0;
while(1) func1();
return 0;
}

void func1() {
char *str = NULL;
str = (char *)malloc(64*1024); //64k
strcpy(str,“testing”);
usleep(100000);
}


编译:



gcc -o bcc_test bcc_test.c -g


同样需要保证`O0`编译。


以上程序会每隔`100`毫秒去申请`64K`内存,而且程序在系统`OOM`之前永远不会终止。


运行程序:



./bcc_test


关于`memleak`的参数说明如下:



-h 打印使用信息。
-p PID 仅跟踪此进程 ID(在内核中过滤)。
-t 打印所有分配和空闲请求和结果的跟踪。
-a 打印除调用堆栈外未释放的分配列表(及其大小)。
-o OLDER 仅打印早于 OLDER 毫秒的分配。有助于消除误报。默认值为 500 毫秒。
-c COMMAND 运行指定的命令并仅跟踪其分配。这会跟踪 libc 分配器。
–combined-only 使用在内核空间中预先计算的统计信息。从内核中提取的数据量显着减少,代价是失去基于时间的误报过滤 (-o) 的能力。
–wa-missing-free 弥补free的动作,以减轻free缺失时的误判。
-s SAMPLE_RATE 大致记录每个 SAMPLE_RATE-th 分配以减少开销。
-t TOP 仅打印顶部的 TOP 堆栈(按大小排序)。默认值为 10。 -z MIN_SIZE 仅捕获大于或等于 MIN_SIZE 字节的分配。
-Z MAX_SIZE 仅捕获小于或等于 MAX_SIZE 字节的分配。
-O OBJ 附加到指定对象中的分配函数,而不是解析 libc。在分析内核分配时忽略。
INTERVAL 每隔 INTERVAL 秒打印未完成分配及其调用堆栈的摘要。默认间隔为 5 秒。
COUNT 打印未完成的分配汇总 COUNT 次,然后退出。


我们在另外的窗口中打开`bcc`调试:



source scl_source enable devtoolset-7 llvm-toolset-7
python3 /data01/chenyc/bcc/tools/memleak.py -p pidof bcc\_test 10


以上命令是使用`memleak`分析 `bcc_test`程序,且每隔`10`秒收集一次内存信息,我们得到结果如下:



[root@ck08 sbin]# python3 /data01/chenyc/bcc/tools/memleak.py -p pidof bcc\_test 10
Attaching to pid 158449, Ctrl+C to quit.
[10:10:43] Top 10 stacks with outstanding allocations:
6225920 bytes in 95 allocations from stack
0x000000000040057c func1+0x1a [bcc_test]
0x0000000000400560 main+0x19 [bcc_test]
0x00007efe98a9c555 __libc_start_main+0xf5 [libc-2.17.so]
[10:10:53] Top 10 stacks with outstanding allocations:
12779520 bytes in 195 allocations from stack
0x000000000040057c func1+0x1a [bcc_test]
0x0000000000400560 main+0x19 [bcc_test]
0x00007efe98a9c555 __libc_start_main+0xf5 [libc-2.17.so]
[10:11:03] Top 10 stacks with outstanding allocations:
19333120 bytes in 295 allocations from stack
0x000000000040057c func1+0x1a [bcc_test]
0x0000000000400560 main+0x19 [bcc_test]
0x00007efe98a9c555 __libc_start_main+0xf5 [libc-2.17.so]
[10:11:13] Top 10 stacks with outstanding allocations:
25886720 bytes in 395 allocations from stack
0x000000000040057c func1+0x1a [bcc_test]
0x0000000000400560 main+0x19 [bcc_test]
0x00007efe98a9c555 __libc_start_main+0xf5 [libc-2.17.so]
[10:11:23] Top 10 stacks with outstanding allocations:
32440320 bytes in 495 allocations from stack
0x000000000040057c func1+0x1a [bcc_test]
0x0000000000400560 main+0x19 [bcc_test]
0x00007efe98a9c555 __libc_start_main+0xf5 [libc-2.17.so]
[10:11:33] Top 10 stacks with outstanding allocations:
38993920 bytes in 595 allocations from stack
0x000000000040057c func1+0x1a [bcc_test]
0x0000000000400560 main+0x19 [bcc_test]
0x00007efe98a9c555 __libc_start_main+0xf5 [libc-2.17.so]


从以上信息可以知道,`bcc`每隔`10`秒在屏幕上输出当前这段时间内内存的申请和释放情况,从上面的信息可以读出,`10:10:53`有`195`次内存申请,总内存大小为`12779520`字节,即`12480kb`,主要发生在`func1`函数中。


`10:11:03`有`295`次内存申请,总内存大小为`19333120`字节,即`18880kb`,主要也发生在`func1`函数中。也就是说,在`10`秒的时间内,`func1`函数申请的内存次数多了`100`次,内存多了`18880-12480 = 6400kb`。这与实际的代码是符合的。


当我们持续观察一段时间,发现`allocations`的次数越来越多,并没有明显的下降,且调用栈都是相同的地方,基本就可以判断是这个地方内存泄漏了。


#### 优缺点分析


从上面的例子可以看出,`bcc`较之`mtrace`,使用起来更加麻烦一些,它配置繁琐,依赖众多,而且因为要指定`pid`,它要求必须是一个长期运行的程序,否则还没运行就已经结束了,没有办法指定到`pid`。它观察的是运行期的状态,判断是否出现内存泄漏,更加依赖人为的分析判断。所以它比较适合于那种已经确定程序有内存泄漏,但是不知道泄漏在什么地方的场景,可以使用该工具快速定位出问题所在。


但是从上面的例子中也可以看出,虽然`memleak`可以打印出调用栈的层级关系,但是没有更加准确的代码行号,因此当代码封装比较深时,可能并不能非常好的分析出问题所在。


而且,`memleak`分析内存,是使用`eBPF`技术直接侵入内核,因此,在运行了`memleak`之后,`CPU`的占用直接翻了一倍不止。因此,该种方案仅作为开发环境调试手段是不错的,但是能否应用在生产环境,仍然需要慎重考虑。


笔者之所以有使用`memleak`分析内存的需求,自然是基于`memleak`自身的一些优点。首先是`memleak`目前已支持了`mmap`函数([memleak added mmap and munmap tracing by yuzhichang · Pull Request #3866 · iovisor/bcc (github.com)](https://bbs.csdn.net/topics/618668825), 我司同事提交的PR),这对于像笔者开发的代码中实用到`apr_pool`内存池的场景有着得天独厚的支持。


其次,因为内存池的引入,因为在程序开始申请内存池,在程序结束销毁内存池,对于一个应用程序的生命周期来说,只要程序正常结束,是没有任何内存泄漏的(因为在销毁内存池时内存最终会被释放掉),但是在程序运行的过程中可能在不断地申请内存,这种情况一般内存泄漏检测工具并不能很好地检测出来,所以更加依赖运行时的分析。


因此,对于`memleak`的使用,大多时候因场景而异,大家酌情使用即可。


![img](https://img-blog.csdnimg.cn/img_convert/fbaa761763ee5ae98b43b381573a17f2.png)
![img](https://img-blog.csdnimg.cn/img_convert/5a4fc2d3344095fde4dd37d6c9ba38f6.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

  • 25
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值