Linux下c++程序内存泄漏检测代码范例

Linux下对于程序内存泄漏检测的方法很多,最常用的的莫过于使用valgrind工具。但是valgrind相当于让程序在虚拟机中运行,会带来较大的系统资源开销,还会对程序的运行效率产生较大影响,对于那种资源占用大的程序,如果需要长时间运行才能暴露的泄漏问题,它就显得不太好用。

linux下的c++程序中自己实现一个轻量级的泄漏检测代码其实是比较方便的,下面我就给出一个简单的范例,并作简单的说明。当然,我们还是应该提倡使用共享指针,用共享指针自动管理内存可以避免内存泄漏这样的不必要的麻烦。

基本原理:

1.利用glibc提供的__malloc_hook, __free_hook系列函数对内存分配是否做监控;(详见glibc的官方文档)

2.利用backtrace函数获取函数调用栈,并记录;

3.利用backtrace_symbols对调用栈对应的函数做解析;

进一步处理:

4.使用abi::__cxa_demangle把函数名解析为源代码风格;

5.使用addr2line解析出函数调用栈对应的代码行;

6.对于动态库(.so)中的地址解析,需要先在/proc/<pid>/maps文件中找到动态库映射的基地址,才能做解析。

注意:

编译连接参数中使用-g -rdynamic

以上每步具体实现的代码可能都没有达到最优,甚至可能是笨办法,如果有更好的实现方案请直接替换,也欢迎赐教。

示例代码:

leakmom.cpp

/* Prototypes for __malloc_hook, __free_hook */

#include <malloc.h>

#include <map>

#include <utility>

#include <execinfo.h>

#include <errno.h>

#include  <assert.h>

#include <cxxabi.h>

#include <sys/types.h>

#include <unistd.h>

#include <stdlib.h>

#include "leakmon.h"

CMutexLock gLock ;

std::map <void*, _PtrInfo> gPtrInfo ;

std::map <const LmCallStack*, _AllocInfo ,  __comp> gLeakInfo;

const int LmCallStack:: MAX_STACK_LAYERS =
32;

/* Prototypes for our hooks. */

static void my_init_hook ( void);

static void *my_malloc_hook ( size_t, const void *);

static void my_free_hook ( void*, const void *);

void *(*__MALLOC_HOOK_VOLATILE old_malloc_hook)( size_t __size , const void *)
;

void (*__MALLOC_HOOK_VOLATILE old_free_hook)
( void *__ptr , const void *);

/* Override initializing hook from the C library. */

void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook)
( void) = my_init_hook;

void my_init_hook (void)

{

old_malloc_hook = __malloc_hook ;

old_free_hook = __free_hook ;

__malloc_hook = my_malloc_hook ;

__free_hook = my_free_hook ;

}

static void *my_malloc_hook ( size_t size , const void *caller )

{

void *result ;

gLock.lock ();

/* Restore all old hooks */

__malloc_hook = old_malloc_hook ;

__free_hook = old_free_hook ;

/* Call recursively */

result = malloc (size);

/* Save underlying hooks */

old_malloc_hook = __malloc_hook ;

old_free_hook = __free_hook ;

/* printf might call malloc, so protect it too. */

//printf ("malloc (%u) returns %p\n", (unsigned int) size, result);

RecordPtr( result , size);

/* Restore our own hooks */

__malloc_hook = my_malloc_hook ;

__free_hook = my_free_hook ;

gLock.unlock ();

return result ;

}

static void my_free_hook ( void *ptr , const void *caller )

{

gLock.lock ();

/* Restore all old hooks */

__malloc_hook = old_malloc_hook ;

__free_hook = old_free_hook ;

/* Call recursively */

free (ptr );

/* Save underlying hooks */

old_malloc_hook = __malloc_hook ;

old_free_hook = __free_hook ;

/* printf might call free, so protect it too. */

//printf ("freed pointer %p\n", ptr);

RemovePtr( ptr );

/* Restore our own hooks */

__malloc_hook = my_malloc_hook ;

__free_hook = my_free_hook ;

gLock.unlock ();

}

void RecordPtr ( void* ptr, size_t size)

{

// 获取调用栈

void *array [LmCallStack:: MAX_STACK_LAYERS];

int cstSize = backtrace( array, LmCallStack ::MAX_STACK_LAYERS);

// 保存指针 调用栈

LmCallStack* callstack = new LmCallStack(array , cstSize);

gLock.lock ();

std::map <const LmCallStack*, _AllocInfo ,  __comp>:: iterator it = gLeakInfo.find (callstack);

if (it != gLeakInfo. end())

{

it->second .size += size;

it->second .alloc++;

_PtrInfo pi (it-> first, size );

gPtrInfo[ptr ]
= pi;

}

else

{

_AllocInfo aif (size,
1, 0);

std::pair <std:: map<const LmCallStack*, _AllocInfo,  __comp>::iterator , bool> ret = gLeakInfo .insert( std::pair <const LmCallStack*, _AllocInfo >(callstack, aif));

if (ret .second)

{

_PtrInfo pi (ret. first->first , size);

gPtrInfo[ptr ]
= pi;

}

else

{

// failed

}

}

gLock.unlock ();

}

void RemovePtr ( void* ptr )

{

gLock.lock ();

std::map <void*, _PtrInfo>::iterator it = gPtrInfo.find (ptr);

if (it != gPtrInfo. end())

{

std::map <const LmCallStack*, _AllocInfo ,  __comp>:: iterator itc = gLeakInfo .find( it->second .csk);

if (itc != gLeakInfo. end())

{

itc->second .size -= it->second .size;

itc->second .free++;

if (0 == (itc ->second. alloc - itc ->second. free))

{

assert(0 == itc ->second. size);

delete itc ->first;

gLeakInfo.erase (itc);

}

}

gPtrInfo.erase (it);

}

gLock.unlock ();

}

void Report ()

{

char **strings = NULL;

gLock.lock ();

__malloc_hook = old_malloc_hook ;

__free_hook = old_free_hook ;

for (std ::map< const LmCallStack *, _AllocInfo,  __comp>::iterator it = gLeakInfo .begin();

it != gLeakInfo .end();

it++)

{

printf("\n" );

printf("====> 
size: %ld,  allocs: %d,  frees: %d, a-f: %d\n", it-> second.size , it-> second.alloc , it-> second.free , it->second .alloc- it->second .free );

printf("====> 
stacks back trace:\n" );

strings = backtrace_symbols ((void**) it->first ->callstack, it->first ->size);

if (strings )

{

for(int i =
2; i < it ->first-> size; i ++)

{ //printf("     %s\n", strings[i]);

char output [1024]
= {0};

memset(output ,
0, 1024);

char temp [1024]
= {0};

memset(temp ,
0, 1024);

////

////    get real function name

////

if (1 == sscanf (strings[ i], "%*[^(]%*[^_]%[^)+]" , temp))

{

int status ;

char* realname = abi::__cxa_demangle (temp,
0, 0, & status);

if (0 == status )

{

char* p = strchr( strings[i ], ‘(‘);

memcpy(output , strings[ i], p-strings [i]);

sprintf(output +(p- strings[i ]), "(%s+%p)
" , realname, (( void**)it ->first-> callstack)[i ]); //printf("    
-%s\n", realname);

free(realname );

}

else

{

char* p = strchr( strings[i ], ‘)‘);

memcpy(output , strings[ i], p-strings [i]+2);

}

}

else

{

char* p = strchr( strings[i ], ‘)‘);

memcpy(output , strings[ i], p-strings [i]+2);

}

FILE * fp ;

char module [1024]
= {0};

memset(module ,
0, 1024);

char* pm = strchr( strings[i ], ‘(‘);

memcpy(module , strings[ i], pm -strings[ i]);

if (strstr (module, ".so"))

{

__pid_t pid = getpid();

sprintf(temp , "grep
%s /proc/%d/maps", module, pid );

///

///         get library base-map-address

///

fp = popen (temp, "r");

if (fp )

{

char baseaddr [64];

unsigned long long base;

fgets(temp , sizeof( temp)-1, fp );  //printf("memmap:
%s\n", temp);

sscanf(temp , "%[^-]", baseaddr);

base = strtoll (baseaddr, NULL,
16); //printf("baseaddr:%s\n", baseaddr); //printf(" base:0x%llx\n", base);

sprintf(temp , "addr2line
-e %s %p", module, (void *)((unsigned long long)((void **)it-> first->callstack )[i]- base));

}

}

else

{

sprintf(temp , "addr2line
-e %s %p", module, ((void **)it-> first->callstack )[i]);

}

////

////    get source file name and line number

////

fp = popen (temp, "r");  //printf("cmdline:
%s\n", temp);

if (fp )

{

fgets(temp , sizeof( temp)-1, fp ); //printf("    
-%s\n", temp);

strcat(output , temp);

printf("  
->  %s" , output);

pclose(fp );

}

else

{

printf("  
->  %s\n" , output);

}

}

free(strings );

strings = NULL ;

}

}

__malloc_hook = my_malloc_hook ;

__free_hook = my_free_hook ;

gLock.unlock ();

}

//////////////////////////////////////////////////////////////////////////

CMutexLock::CMutexLock ()

{

pthread_mutexattr_t  m_attr ;

pthread_mutexattr_init(&m_attr );

pthread_mutexattr_settype(&m_attr , PTHREAD_MUTEX_RECURSIVE);

if (0 != pthread_mutex_init (&m_mutex ,
& m_attr))

{

printf("c_lock::c_lock
pthread_mutex_init error<%d>.\n" , errno);

assert(0);

}

pthread_mutexattr_destroy(&m_attr );

}

CMutexLock::~CMutexLock ()

{

if(0 != pthread_mutex_destroy (&m_mutex))

{

printf("c_lock::~c_lock
pthread_mutex_destroy error<%d>.\n" , errno);

assert(0);

}

}

void

CMutexLock::lock ()

{

if(0 != pthread_mutex_lock (&m_mutex))

{

assert("c_lock::lock
pthread_mutex_lock " && 0);

}

}

void

CMutexLock::unlock ()

{

int iRet =
0;

if(0 != (iRet = pthread_mutex_unlock(& m_mutex)))

{

printf("c_lock::unlock
pthread_mutex_unlock ret<%d> error<%d>.\n", iRet, errno );

assert(0);

}

}

示例代码:

leakmom.h

////////////////////////////////////////////////////////////////////////

//

//    The Executable file MUST be linked with parameter ‘-rdynamic‘ !!!

//

////////////////////////////////////////////////////////////////////////

#pragma once

#include <string.h>

#include <pthread.h>

//

class LmCallStack

{

public:

char* callstack ; //
pointer to buffer recording callstack addresses

int size ; //
count of call stacks

static const int MAX_STACK_LAYERS;

public:

LmCallStack(void * csk= NULL, int s=0)

{

if (csk )

{

callstack = new char[ s*sizeof (void*)];

memcpy(callstack , csk, s*sizeof (void*));

}

else

{

callstack =
(char *)csk;

}

size = s ;

}

~ LmCallStack()

{

if (callstack )

{

delete[] callstack ;

}

callstack = NULL ;

size =
0;

}

};

class __comp

{

public:

__comp(){};

bool operator ()
(const LmCallStack* first , const LmCallStack* second)

{

return ((first ->size < second->size )
||

( first->size == second-> size &&

memcmp(first ->callstack, second->callstack , sizeof( void*)*first ->size)
< 0)

);

}

};

struct _PtrInfo

{

_PtrInfo(const LmCallStack* c=NULL , long s=0)

{

csk = c ;

size = s ;

}

const LmCallStack * csk;

long size ;

};

struct _AllocInfo

{

_AllocInfo(long s=0, int a =0, int f=0)

{

size=s ;

alloc=a ;

free=f ;

}

//

long size ;

int alloc ;

int free ;

};

class CMutexLock

{

public:

CMutexLock();

~ CMutexLock();

public:

void lock ();

void unlock ();

private:

pthread_mutex_t m_mutex ;

};

//

void RecordPtr ( void* ptr, size_t size);

void RemovePtr (void* ptr);

void Report ();

时间: 2024-12-19 19:35:38

Linux下c++程序内存泄漏检测代码范例的相关文章

Unix下C程序内存泄漏检测工具Valgrind安装与使用

Unix下C程序内存泄漏检测工具Valgrind安装与使用 Valgrind是一款用于内存调试.内存泄漏检测以及性能分析的软件开发工具. Valgrind的最初作者是Julian Seward,他于2006年由于在开发Valgrind上的工作获得了第二届Google-O'Reilly开源代码奖. Valgrind遵守GNU通用公共许可证条款,是一款自由软件. 官网 http://www.valgrind.org 下载与安装 #wget http://www.valgrind.org/downlo

Unix下C程序内存泄露检测工具:valgrind的安装使用

Valgrind是一款用于内存调试.内存泄漏检测以及性能分析的软件开发工具. Valgrind的最初作者是Julian Seward,他于2006年由于在开发Valgrind上的工作获得了第二届Google-O'Reilly开源代码奖. Valgrind遵守GNU通用公共许可证条款,是一款自由软件. Valgrind的安装和使用 去官网www.valgrind.org下载最新版本的valgrind,我这里下载的是valgrind 3.11.0.tar.bz2. #tar xvf valgrind

linux c程序内存泄漏检测工具-mtrace工具介绍

笔者也是最近去面试被问到怎么做内存泄漏检查,之前都是靠人工屏蔽代码.或者PC-link/KW一类的检查工具进行检查,回来后搜索了下,才知道linux自带的就有mtrace工具. 具体操作步骤如下: 1.在linux下创建test.c文件,编写如下代码: 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #include <mcheck.h> 6 7 8 int main(

C/C++应用程序内存泄漏检查统计方案

一.前绪 C/C++程序给某些程序员的几大印象之一就是内存自己管理容易泄漏容易崩,笔者曾经在一个产品中使用C语言开发维护部分模块,只要产品有内存泄漏和崩溃的问题,就被甩锅“我的程序是C#开发的内存都是托管的,C++那边也没有内存(庇护其好友),肯定是C这边的问题”(话说一个十几年的程序员还停留在语言层面不觉得有点low吗),笔者毕业不到一年,听到此语心里一万头草泥马奔腾而过,默默地修改了程序,注意不是修改bug(哈哈),而是把所有malloc和free都替换成了自定义宏MALLOC和FREE,d

VS2005内存泄漏检测方法[转载]

一.非MFC程序可以用以下方法检测内存泄露: 1. 程序开始包含如下定义: #ifdef _DEBUG #define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__) #else #define DEBUG_CLIENTBLOCK #endif   // _DEBUG #define _CRTDBG_MAP_ALLOC #include #include #ifdef _DEBUG #define new   DEBUG_CLI

linux中内存泄漏的检测(五)记录内存泄漏的代码

到目前为止,先后通过wrap malloc.new函数重载和计算指针内存大小的方法,基本上满足了对内存泄漏检测的需要. 如果发现了内存泄漏,那么就要找到内存泄漏的地方并且修正它了. 茫茫代码,如何去找?如果能根据未释放的内存找到申请它的地方就好了. 我们今天就是要做这个事情. 想要根据内存地址查出申请者的信息,那么在一开始申请的时候就要建立地址与申请者之间的映射. 1.内存地址 内存地址,是一个unsigned long型的数值,用void *来存储也可以.为了避免类型转换,我使用了void *

Windows平台下的内存泄漏检测

一,Windows平台下的内存泄漏检测 检测是否存在内存泄漏问题 Windows平台下面Visual Studio 调试器和 C 运行时 (CRT) 库为我们提供了检测和识别内存泄漏的有效方法,原理大致如下:内存分配要通过CRT在运行时实现,只要在分配内存和释放内存时分别做好记录,程序结束时对比分配内存和释放内存的记录就可以确定是不是有内存泄漏.在vs中启用内存检测的方法如下: •STEP1,在程序中包括以下语句: (#include 语句必须采用上文所示顺序. 如果更改了顺序,所使用的函数可能

Linux c内存泄漏检测

在Linux下些C语言程序,最大的问题就是没有一个好的编程IDE,当然想kdevelop等工具都相当的强大,但我还是习惯使用kdevelop工具,由于没有一个习惯的编程IDE,内存检测也就成了在Linux下编写程序的一个大问题. 是不是说没有一种内存检查工具能够在Linux使用呢,也不是,像valgrind工具还是相当不错的.他的下载地址是 http://valgrind.org/downloads /current.html#current 下载一个valgrind 3.2.3 (tar.bz

Linux内存泄漏检测

如题,就工具而言主要包括valgrind.mtrace.dmalloc和memwatch等,具体使用请参照以下连接 Linux C内存泄露检测工具 http://blog.sina.com.cn/s/blog_4b9216f50100e6o7.html Linux C/C++ 内存泄漏检测工具:Valgrind http://zyan.cc/post/419/ 就内存泄漏检测的理论和实现请参照以下连接: 一个跨平台的 C++ 内存泄漏检测器 http://www.ibm.com/develope