聊一聊 Valgrind 监视非托管内存泄露和崩溃
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聊一聊 Valgrind 监视非托管内存泄露和崩溃相关的知识,希望对你有一定的参考价值。
一:背景
1. 讲故事
只要是程序总会出现各种莫名其妙的问题,比如:非托管内存泄露,程序崩溃,在 Windows 平台上一般用微软自家的官方工具 App Verifier
就可以洞察,那问题出在 Linux 上怎么办呢?由于 Linux 崇尚自由,需要在各种牛鬼蛇神写的非官方开源软件中寻找一个比较靠谱的,比如本篇所说的 Valgrind
。
个人感觉 Valgrind
和 App Verifer
定位是差不多的,技术上前者使用 hook 钩子,后者使用仿真cpu,有点像 windbg 的 TTD 调试,具体信息参考:https://valgrind.org/
二:Valgrind 内存洞察
1. 安装
如果你用的是 ubuntu,可以用 apt-get
直接安装,方便快捷,目前最新的版本是 3.15.0
。
root@skyfly-virtual-machine:/home/skyfly/Desktop# apt-get install valgrind
Reading package lists... Done
Building dependency tree
Reading state information... Done
valgrind is already the newest version (1:3.15.0-1ubuntu9.1).
0 upgraded, 0 newly installed, 0 to remove and 23 not upgraded.
root@skyfly-virtual-machine:/home/skyfly/Desktop# valgrind --version
valgrind-3.15.0
2. 内存泄露洞察
内存泄露大多是 new/delete
, malloc/free
不匹配造成的,接下来写一个不匹配的 malloc/free 观察下。
#include <iostream>
#include <stdio.h>
using namespace std;
int main(void)
int *p = (int *)malloc(4);
*p = 10;
printf("p=%d", *p);
return EXIT_SUCCESS;
这里使用 vscode 进行编译,怎么编译就不说了,参见上一篇,接下来用 algrind --leak-check=full ./main.out
以仿真的方式把程序跑起来,截图如下:
从图中的 HEAP SUMMARY
区域可以看到,当前有 3 个 malloc,但只有 2 个 free,而且还找到了那个没有 free 的 malloc,在代码的 main.cpp:8
行,即 int *p = (int *)malloc(4);
处。
3. 栈溢出洞察
相信经常写递归的朋友总会遇到这类问题,为了方便演示,我故意实现一个 栈上溢
的例子吧。
#include <iostream>
#include <stdio.h>
using namespace std;
int main(void)
int num = 10;
int *ptr = #
ptr -= 0x40;
*ptr = 15;
return EXIT_SUCCESS;
从图中可以清晰的看到,主线程栈上的地址 0x1ffefffd5c
无法写入,还指出了在 main.cpp:12
行,即 *ptr = 15;
处。
为了方便验证,这里用 g++ 调试的方式洞察一下。
-exec p num
$5 = 10
-exec x ptr
0x7fffffffde2c: 0xf7fb0e9800007fff
-exec info reg
...
rbp 0x7fffffffdf40 0x7fffffffdf40
rsp 0x7fffffffdf20 0x7fffffffdf20
...
从输出看,ptr 已经逃出了 rsp ~ rbp
范围之内,很明显上溢了,如果用 x 命令观察的话,可以看到里面的内容就很随机,在 windbg 中用以 ???
来表示的。
-exec x ptr
0x7fffffffde2c: 0xf7fb0e9800007fff
-exec x 0xf7fb0e9800007fff
0xf7fb0e9800007fff: Cannot access memory at address 0xf7fb0e9800007fff
除了观察 rsp,rbp,还可以看虚拟地址中的 stack 空间。
-exec i proc mapping
process 8327
Mapped address spaces:
Start Addr End Addr Size Offset objfile
...
0x7ffff7ffd000 0x7ffff7ffe000 0x1000 0x2d000 /usr/lib/x86_64-linux-gnu/ld-2.31.so
0x7ffff7ffe000 0x7ffff7fff000 0x1000 0x0
0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack]
4. 数组越界洞察
valgrind 这玩意也有弱鸡的时候,比如数组越界之类的问题它就搞不定,比如下面的代码:
#include <iostream>
#include <stdio.h>
using namespace std;
int main(void)
int num[5] = 1, 2, 3, 4, 5;
int num2[10];
num[6] = 15;
return EXIT_SUCCESS;
通过下面的汇编代码观察,可以看到目前分配了 0x30 = 0n48 byte
的栈空间,截图如下:
虽然 num[6]
是越界操作,但是在合理的 栈空间
内,这种仿生cpu的方式洞察不出来,App Verifier 这种 hook 的方式是没有问题的。
接下来用 valgrind 尝试一下,可以看到果然没发现任何问题,输出如下:
skyfly@skyfly-virtual-machine:~/code$ valgrind ./main.out
==10244== Memcheck, a memory error detector
==10244== Copyright (C) 2002-2017, and GNU GPL\'d, by Julian Seward et al.
==10244== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==10244== Command: ./main.out
==10244==
==10244==
==10244== HEAP SUMMARY:
==10244== in use at exit: 0 bytes in 0 blocks
==10244== total heap usage: 1 allocs, 1 frees, 72,704 bytes allocated
==10244==
==10244== All heap blocks were freed -- no leaks are possible
==10244==
==10244== For lists of detected and suppressed errors, rerun with: -s
==10244== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
三:总结
总的来说,Valgrind 在洞察内存方面还是非常强大的,但也有它不能触及到的地方,熟悉它的优点和缺点,全面了解对我们调试师来说至关重要,希望本篇对你有帮助。
聊一聊内存泄漏 ------在valgrind检查不出来时怎么办??
这几天多个版本出现了多个内存泄漏问题-----------------------------------------怎么办????
Q1:进程调用某库函数分配内存不释放, 但是在进程退出时,统一释放内存,valgrind 检查不出来,但是频繁调用库函数会耗尽内存。怎么快速定位那个库函数导致!!!!!除了看源码这种sa办法
Q2:某些设备不支持跑valgrind 怎么办??-
目前C&C++ 动态分配内存都是使用malloc-free new-delete
所以第一个方法是 每次分配内存时先记录下来 释放时记录下来 这样一对比就出来。。。or malloc记录地址 free 去掉记录地址
最后剩下的就是 没有释放的。。。。
第二个方法就是hook malloc了
http://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Hooks-for-Malloc.html
再就是 http://jemalloc.net/jemalloc.3.html#arena.i.chunk_hooks
第三个:malloc是一个weak symbol,所以直接写一个malloc同名函数就行,同名函数中可以通过符号 __libc_malloc,这个符号直接指向malloc的实现部分
--------------->GCC wrap
ld中有一个选项 –wrap,当查找某个符号时,它优先先解析__wrap_symbol, 解析不到才去解析symbol。
好像对C++的new不起作用。
extern void *__libc_malloc(size_t size); void* malloc (size_t size) { // do your stuff { } // call the original malloc return __libc_malloc(size); }
再就是mtrace 了
再就是对比之前版本---看下这个版本改了什么了。。。再或者就是看覆盖率,那些没有跑到,然后review代码了。。。
还要啥方法?????????????????????
以上是关于聊一聊 Valgrind 监视非托管内存泄露和崩溃的主要内容,如果未能解决你的问题,请参考以下文章
聊一聊内存泄漏 ------在valgrind检查不出来时怎么办??