linux中内存泄漏的检測(五)记录内存泄漏的代码
到眼下为止,先后通过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中内存泄漏的检測(五)记录内存泄漏的代码的更多相关文章
- Linux中生成Core Dump系统异常信息记录文件的教程
		Linux中生成Core Dump系统异常信息记录文件的教程 http://www.jb51.net/LINUXjishu/473351.html 
- linux中内存泄漏的检測(一)最简单的方法
		什么是内存泄漏 内存泄漏是指程序动态申请的内存在使用完后没有释放,导致这段内存不能被操作系统回收再利用. 比如这段程序,申请了4个字节的空间但没有释放,有4个字节的内存泄漏. #include < ... 
- Linux基础教程 linux中使用find命令搜索文件常用方法记录
		find是linux非常强大的搜索命令,通过man find查看find手册,可以发现find的说明一屏接一屏,估计要看完也得花不少时间.兄弟连Linux培训 小编总结了下,整理出find常用的使用方 ... 
- Linux中Postfix邮件认证配置(五)
		Postfix+Dovecot+Sasl工作原理 1.A用户使用MUA客户端借助smtp协议登陆smtpd服务器,需要先进行用户和密码认证,而SMTPD服务器端支持sasl认证,例如有一个sasl客户 ... 
- Linux中java项目环境部署,简单记录一下
		这里只是简单的记录一下linux环境下面如何快速的搭配好环境,使你的项目能在linux环境上面运行. 很多时候,我们都是用windows环境进行配置调试的,而真正很多服务器都是在linux服务器上面的 ... 
- C++内存泄露检測原理
		转自:http://hi.baidu.com/jasonlyy/item/9ca0cecf2c8f113a99b4981c 本文针对 linux 下的 C++ 程序的内存泄漏的检測方法及事实上现进行探 ... 
- linux中的 IO端口映射和IO内存映射
		参考自:http://blog.csdn.net/zyhorse2010/article/details/6590488 CPU地址空间 (一)地址的概念 1)物理地址:CPU地址总线传来的地址,由硬 ... 
- Cocos2d-x教程(34)-三维物体OBB碰撞检測算法
		欢迎增加Cocos2d-x 交流群:193411763 个中心点.1个旋转矩阵和3个1/2边长(注:一个旋转矩阵包括了三个旋转轴,若是二维的OBB包围盒则是一个中心点,两个旋转轴,两个1/2边长). ... 
- Canny边缘检測算法原理及其VC实现具体解释(一)
		图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般能够看作是一个阶跃,既从一个灰度值在非常小的缓冲区域内急剧变化到还有一个灰度相差较大的灰度值.图象的边缘部分集中了图象的大部分信息,图 ... 
随机推荐
- 洛谷—— P2419 [USACO08JAN]牛大赛Cow Contest
			https://www.luogu.org/problem/show?pid=2419 题目背景 [Usaco2008 Jan] 题目描述 N (1 ≤ N ≤ 100) cows, convenie ... 
- 对GPDB查询计划的Motion结点的理解
			GPDB在进行join查询时,可能会产生Motion结点 根据官方文档,总共有这几种Motion: redistribute 重分布(用hash取模的方法把join字段重分布到各个segment,相当 ... 
- glEnable(GL_DEPTH_TEST)作用
			glEnable(GL_DEPTH_TEST): 用来开启更新深度缓冲区的功能,也就是,如果通过比较后深度值发生变化了,会进行更新深度缓冲区的操作.启动它,OpenGL就可以跟踪再Z轴上的像素,这样, ... 
- ArcGIS api for javascript——显示地图属性
			描述 本例展示了如哦读取地图和图层的属性和返回信息给用户.本例中的四个按钮允许用户接收地图属性.每个按钮调用不同的函数. ·Get All Map Layers - 这个按钮调用getMapLayer ... 
- 3D打印技术之切片引擎(6)
			[此系列文章基于熔融沉积( fused depostion modeling, FDM )成形工艺] 这一篇文章说一下填充算法中的网格填充.网格填充在现有的较为成熟的引擎中是非常普遍的:skeinfo ... 
- C++ 鼠标模拟程序
			关于鼠标模拟程序应用不算少见.在游戏外挂或者一些操作频繁位置确定的程序上应用往往有奇效. 比較旧的API是mouse_event,本人一開始也用这个在搞,只是后来才看到新的API在操作上更加统一.稍作 ... 
- css footer not displaying at the bottom of the page
			https://stackoverflow.com/questions/15960290/css-footer-not-displaying-at-the-bottom-of-the-page The ... 
- 英语音乐---一、Scarborough Fair
			英语音乐---一.Scarborough Fair 一.总结 一句话总结:斯卡布罗集市 <斯卡布罗集市>诉说了一个缠绵凄美的爱情故事:一个参军的男青年远离自己相爱的姑娘在战争中不幸遇难,但 ... 
- 安装vnc出现的问题
			重启vnc 命令:/sbin/service vncserver start或者vncserver VNC的启动/停止/重启 #service vncserver start/stop/restart ... 
- netflix zuul 1.x与zuul2.x之比较
			1.zuul 1.x的架构如下所示: 线程模型: 其web应用的web.xml <?xml version="1.0" encoding="UTF-8"? ... 
