C/C++代码调试:快速定位内存的申请和释放的位置

1.问题

如果大型项目中出现类似于*** glibc detected *** logcacheinit: double free or corruption (fasttop): 0x00000000017db7f0 ***的错误。更糟糕的是项目既是多线程又是多个节点分布式运行的话,调试定位double free实在让人头痛。内核在程序崩溃的时候,这个信息只给出了被释放两次的内存地址,却没有给出程序出现两次内存释放的具体位置,这就需要我们自己动手排查。

2.调试代码

通过如下代码,对malloc和free进行定位并打印出具体的内存地址,可以根据内核提示的内存地址快速查找到double free的代码位置。

2.1对malloc和free的宏替换

#define free(p) { \
        printf("@@%s:%d:%s():free(0x%lx)\n", __FILE__, __LINE__,            \
            __func__, (unsigned long)p);                                \
        free(p);                                                        \
} 

#define malloc(size) ({ \
        void* ptr=malloc(size);\
        printf("@@%s:%d:%s():malloc(0x%lx)\n", __FILE__, __LINE__,            \
            __FUNCTION__, (unsigned long)ptr);                                \
        ptr;                                                        \
})

注意事项:
(1)__FILE__,__LINE__,__FUNCTION__是编程语言内置宏定义,编译时会被编译器替换成代码所在的文件名称,行号和所在函数名。
(2)多行宏定义需要使用使用转义字符’\’连接。

2.2对operator new和operator delete的重载

如果项目中使用了new和delete来申请和释放内存,那么需要对operator new和operator delete进行重载。代码如下。

void* operator new(size_t size, const char *file, int line,const char *function) throw (std::bad_alloc){
    void * ptr=malloc(size);
    printf("&&%s:%d:%s:new(size=%u)=%p\n",file,line,function,size,ptr);
    if(ptr==NULL)
        throw std::bad_alloc();
    return ptr;
}

void* operator new[](size_t size,const char *file, int line,const char *function) throw (std::bad_alloc){
    void * ptr=malloc(size);
    printf("&&%s:%d:%s:new[](size=%u)=%p\n",file,line,function,size,ptr);
    if(ptr==NULL)
        throw std::bad_alloc();
    return ptr;
} 

void operator delete(void * p) throw()
{
    free(p);
}

void operator delete[] (void * p) throw()
{
    free(p);
}

对全局的operator new和operator delete函数重载完成后,如何才能获取调用new和delete的位置信息呢?那么需要下面的宏:

#define new new(__FILE__, __LINE__,__FUNCTION__)

#define delete ({\
    printf("&&%s:%d:%s()",__FILE__,__LINE__,__FUNCTION__);\
}),delete

注意事项:
(1)delete或者delete[]打印内存地址时,需要依赖对free的宏定义;
(2)delete的使用基本和new一致,包括operator delete的重载方式这些都相似,只不过它的参数是void*,返回值为void。但是有一点需要注意,operator delete的自定义参数重载并不能手动调用。例如:

void* operator new(size_t size, int x)  
{  
    cout<<" x = "<<x<<endl;  
    return malloc(size);      
}  
void operator delete(void* p, int x)  
{  
    cout<<" x = "<<x<<endl;  
    free(p);  
}  

如下调用是无法通过的:
A* p = new(3) A;//Ok
delete(3) p;//error C2541:
上面对delete的宏就是用来解决这个问题的。

参考文献

[1]http://blog.csdn.net/wudaijun/article/details/9273339
[2]http://blog.csdn.net/cangyingzhijia/article/details/8613177
[3]http://blog.csdn.net/wx3046/article/details/5792505

你可能感兴趣的:(多线程,内存,分布式,调试)