到眼下为止,先后通过wrap malloc、new函数重载和计算指针内存大小的方法。基本上满足了对内存泄漏检測的须要。

假设发现了内存泄漏。那么就要找到内存泄漏的地方而且修正它了。

茫茫代码。如何去找?假设能依据未释放的内存找到申请它的地方就好了。

我们今天就是要做这个事情。

想要依据内存地址查出申请者的信息。那么在一開始申请的时候就要建立地址与申请者之间的映射。

1.内存地址

内存地址,是一个unsigned long型的数值,用void *来存储也能够。

为了避免类型转换。我使用了void *。

2.申请者信息

申请者的信息比較复杂,不是一个类型能够搞定的。它包括哪些内容呢?

在C情况下,主要是须要知道谁调用了__wrap_malloc。

但在C++情况下。调用__wrap_malloc的一定是new。这没有什么意义,还须要知道是谁调用了new。再进一步说,new有可能是在构造函数中被调用的,那么非常有可能我们真正须要知道的是谁调用了构造函数。

由此可见,仅仅知道是谁调用了__wrap_malloc不够的,我们须要的是整个栈信息。

整个栈包括了非常多内容,在这里。我们仅仅记录栈的深度(int)和每一层的符号名(char **)。

符号名在整个程序中是唯一的(无论C还是C++)且相对位置是确定的(动态库除外)。当程序结束时再依据符号名反推出调用者的文件名称和行号。

为什么不直接获取文件名称和行号?

因为求符号名的实现比較简单。

3.映射方式

说到映射。首先想到的是map、hash这种东西。

但须要说明的是,这里是__wrap_malloc函数,是每次程序动态分配空间时必定会走到的地方。

这有什么关系呢?想象一下,在因为某个动态申请内存的操作来到了这个函数。而在这个函数里又不小心申请了一次内存,会如何呢?在-Wl,--wrap,malloc的作用下又来到了这里,于是开启了“鸡生蛋、蛋生鸡”的死循环中,直到——stack overflow。

所以,在这个函数里能使用的。仅仅能使用栈空间或者全局空间,假设一定要使用堆空间,也必须显示地使用__real_malloc取代new或者malloc。因为在map、hash中会不可避免地使用动态内存空间的情况,还是放弃吧。

怎么办呢?为了避免节外生枝,我这里使用了最简单可是有点笨的方法——数组。

struct memory_record
{
void * addr;
size_t count;
int depth;
char **symbols;
}mc[1000];

4.如何获取栈中的符号?

gcc给我们提对应的函数,依照要求调用即可。

char* stack[20] = {0};
mc[i].depth = backtrace(reinterpret_cast<void ** >(stack), sizeof(stack)/sizeof(stack[0]));
if (mc[i].depth){
mc[i].symbols = backtrace_symbols(reinterpret_cast<void**>(stack), mc[i].depth);
}

backtrace函数用于获取栈的深度(depth),以及每一层栈地址(stack)。

backtrace_symbols函数依据栈地址返回符号名(symbols)。

须要注意的是。backtrace_symbols返回的是符号的数组,这个数组的空间是由backtrace_symbols分配的,但须要调用者释放。

为什么这里backtrace_symbols分配了内存却没有引起stack overflow呢?下面是我的推測:

backtrace_symbols函数和wrap机制都是GNU提供的,属性亲戚关系。

既然是亲戚。那么大家通融一下,让backtrace_symbols绕过wrap机制直接使用内存也是有可能的。

源码:

#include <iostream>
using namespace std; #include "string.h"
#include <stdio.h>
#include <malloc.h>
#include <execinfo.h> #if(defined(_X86_) && !defined(__x86_64))
#define _ALLOCA_S_MARKER_SIZE 4
#elif defined(__ia64__) || defined(__x86_64)
#define _ALLOCA_S_MARKER_SIZE 8
#endif size_t count = 0; int backtrace(void **buffer, int size); struct memory_record
{
void * addr;
size_t count;
int depth;
char **symbols;
}mc[1000]; extern "C"
{
void* __real_malloc(int c);
void * __wrap_malloc(size_t size)
{
void *p = __real_malloc(size);
size_t w = *((size_t*)((char*)p - _ALLOCA_S_MARKER_SIZE));
cout<<"malloc "<<p<<endl;
for(int i = 0; i < 1000; i++)
{
if(mc[i].count == 0)
{
count += w;
mc[i].addr = p;
mc[i].count = w;
char* stack[20] = {0};
mc[i].depth = backtrace(reinterpret_cast<void**>(stack), sizeof(stack)/sizeof(stack[0]));
if (mc[i].depth){
mc[i].symbols = backtrace_symbols(reinterpret_cast<void**>(stack), mc[i].depth);
}
break;
}
}
return p;
} void __real_free(void *ptr);
void __wrap_free(void *ptr)
{
cout<<"free "<<ptr<<endl;
size_t w = *((size_t*)((char*)ptr - _ALLOCA_S_MARKER_SIZE));
for(int i = 0; i < 1000; i++)
{
if(mc[i].addr == ptr)
{
mc[i].count -= w;
count -= w;
if(mc[i].symbols)
__real_free(mc[i].symbols);
break;
}
}
__real_free(ptr);
}
} void *operator new(size_t size)
{
return malloc(size);
} void operator delete(void *ptr)
{
free(ptr);
} void print_leaked_memory()
{
if(count != 0)
cout<<"memory leak!"<<endl;
for(int i = 0; i < 1000; i++)
{
if(mc[i].count != 0)
{
cout<<mc[i].addr<<' '<<mc[i].count<<endl;
if (mc[i].symbols){
for(size_t j = 0; j < mc[i].depth; j++){
printf("===[%d]:%s\n", (j+1), mc[i].symbols[j]);
}
}
__real_free(mc[i].symbols);
}
}
} class A
{
int *p1;
public:
A(){p1 = new int;}
~A(){delete p1;}
}; int main(void)
{
memset(mc, 0, sizeof(mc));
count = 0;
int *p1 = new int(4);
int *p2 = new int(5);
delete p1;
print_leaked_memory();
return 0;
}

编译命令:

g++ -o test test.cpp -g -Wl,--wrap,malloc -Wl,--wrap,free

执行:

./test | grep "===" | cut -d"[" -f3 | tr -d "]" | addr2line -e test

方法分析:

长处:

(1)在程序执行结束时。打印程序内存泄漏情况以及导致泄漏发生的代码所在的文件及行号

(2)C/C++都适用

(3)须要改动产品源码即可实现功能

(4)对一起链接的全部.o和静态库都有效

缺点:

(1)对动态库不适用

(2)求堆栈信息和求文件名称行号是两个操作,不能一次性解决这个问题

linux中内存泄漏的检測(五)记录内存泄漏的代码的更多相关文章

  1. Linux中生成Core Dump系统异常信息记录文件的教程

    Linux中生成Core Dump系统异常信息记录文件的教程 http://www.jb51.net/LINUXjishu/473351.html

  2. linux中内存泄漏的检測(一)最简单的方法

    什么是内存泄漏 内存泄漏是指程序动态申请的内存在使用完后没有释放,导致这段内存不能被操作系统回收再利用. 比如这段程序,申请了4个字节的空间但没有释放,有4个字节的内存泄漏. #include < ...

  3. Linux基础教程 linux中使用find命令搜索文件常用方法记录

    find是linux非常强大的搜索命令,通过man find查看find手册,可以发现find的说明一屏接一屏,估计要看完也得花不少时间.兄弟连Linux培训 小编总结了下,整理出find常用的使用方 ...

  4. Linux中Postfix邮件认证配置(五)

    Postfix+Dovecot+Sasl工作原理 1.A用户使用MUA客户端借助smtp协议登陆smtpd服务器,需要先进行用户和密码认证,而SMTPD服务器端支持sasl认证,例如有一个sasl客户 ...

  5. Linux中java项目环境部署,简单记录一下

    这里只是简单的记录一下linux环境下面如何快速的搭配好环境,使你的项目能在linux环境上面运行. 很多时候,我们都是用windows环境进行配置调试的,而真正很多服务器都是在linux服务器上面的 ...

  6. C++内存泄露检測原理

    转自:http://hi.baidu.com/jasonlyy/item/9ca0cecf2c8f113a99b4981c 本文针对 linux 下的 C++ 程序的内存泄漏的检測方法及事实上现进行探 ...

  7. linux中的 IO端口映射和IO内存映射

    参考自:http://blog.csdn.net/zyhorse2010/article/details/6590488 CPU地址空间 (一)地址的概念 1)物理地址:CPU地址总线传来的地址,由硬 ...

  8. Cocos2d-x教程(34)-三维物体OBB碰撞检測算法

    欢迎增加Cocos2d-x 交流群:193411763 个中心点.1个旋转矩阵和3个1/2边长(注:一个旋转矩阵包括了三个旋转轴,若是二维的OBB包围盒则是一个中心点,两个旋转轴,两个1/2边长). ...

  9. Canny边缘检測算法原理及其VC实现具体解释(一)

    图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般能够看作是一个阶跃,既从一个灰度值在非常小的缓冲区域内急剧变化到还有一个灰度相差较大的灰度值.图象的边缘部分集中了图象的大部分信息,图 ...

随机推荐

  1. Windows10通过VNC远程连接Ubuntu18.04

    1.打开终端输入:sudo apt-get install xrdp vnc4server xbase-clients dconf-editor 2.接着在终端输入: 进入到下面这个界面: 接着按照这 ...

  2. Python 读写文件 小应用:生成随机的测验试卷文件

    去年学习了python的读写文件部分,了解了python读写的常用模块os.shelve,今天准备把课后作业试着自己做一下 目标:1)生成35份试卷.每个试卷有50道选择题 2)为了防止有学生作弊,需 ...

  3. Apache CXF实战之二 集成Sping与Web容器

    本文链接:http://blog.csdn.net/kongxx/article/details/7525481 Apache CXF实战之一 Hello World Web Service 书接上文 ...

  4. [Recompose] Refactor React Render Props to Streaming Props with RxJS and Recompose

    This lesson takes the concept of render props and migrates it over to streaming props by keeping the ...

  5. 从 dig(nslookup) bind —— windows 下的域名解析服务器信息的查看

    dig(domain information groper,之所以选择这三个词,在于这三个词的首字母构成的词 dig 也有探索挖掘的含义)本身是 Linux 下的查询 DNS 信息的工具,功能类似 n ...

  6. 日天的终生大事(dp)

    日天的终生大事 题目描述 日天学长向妹子表白了,妹子说:“日天你那么聪明,回答我一个问题就答应你.你告诉我,L位K进制数有多少个?”日天表示这个问题太简单了,要求提高难度.妹子想了想说:“那么我增加一 ...

  7. BZOJ3435: [Wc2014]紫荆花之恋(替罪羊树,Treap)

    Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是 ...

  8. IBM将收购Linux发行商红帽公司,继续发力云计算市场

    10月29日凌晨消息,IBM和Red Hat当地时间星期日联合宣布,IBM将以340亿美元收购红帽公司(Red Hat).根据两家公司发表的一份联合声明,IBM将以每股190美元的价格,以现金方式收购 ...

  9. cp---复制文件

    cp命令用来将一个或多个源文件或者目录复制到指定的目的文件或目录.它可以将单个源文件复制成一个指定文件名的具体的文件或一个已经存在的目录下.cp命令还支持同时复制多个文件,当一次复制多个文件时,目标文 ...

  10. 51nod 编辑距离 + 滚动数组优化

    这道题一开始觉得增加和删除会移动字符串的位置很不好做 两个字符串dp状态一般是第一个前i个和第二个前j个 #include<cstdio> #include<algorithm> ...