您的位置:首页 > 其它

使用__wrap_malloc替换malloc查找内存泄漏

2016-06-05 23:58 253 查看
由于malloc_hook在多线程中不合用,因此转用__wrap_symbol方法支持多线程调用malloc。

缺点:所有source code都链接重编译。

ld中有一个选项–wrap,当查找某个符号时,它优先先解析__wrap_symbol, 解析不到才去解析symbol。

当其它文件与你实现__wrap_malloc函数的文件链接时使用--wrap,malloc,则所有到malloc的调用都是会链接到__wrap_malloc上,我们只需要在__wrap_malloc里面调用libc中的malloc函数。

如:
-Wl,-wrap,malloc -Wl,-wrap,free -Wl,-wrap,calloc -Wl,-wrap,realloc


Ex:

CCheckMemoryLeak.cpp:

#include <stdlib.h>
#include <execinfo.h>
#include <map>
#include <utility>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <pthread.h>
#include <cxxabi.h>
#include <dirent.h>

#include "CCheckMemoryLeak.h"

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// declare
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define THREAD_AUTOLOCK automutex _lock_(&_mutex, __FUNCTION__, __LINE__);
#define TRUE (1)
#define FALSE (0)
#define PERR printf
#define PWRN printf

#define __HOOK__ (0)

class automutex
{
public:
automutex(pthread_mutex_t *mux, const char *func, const int line);
~automutex();

private:
pthread_mutex_t *m_mux;
};

automutex::automutex(pthread_mutex_t *mux, const char *func, const int line)
{
m_mux = mux;
pthread_mutex_lock(m_mux);
}

automutex::~automutex()
{
pthread_mutex_unlock(m_mux);
}

typedef struct BtInfo_s
{
size_t size;
char **str_bt;
size_t mem_size;
pthread_t tid;
pid_t pid;
} BtInfo_t;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// internel params
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void (*old_free)(void *ptr, const void *caller);
static void *(*old_malloc)(size_t size, const void *caller);
static void *(*old_realloc)(void *ptr, size_t size, const void *caller);
static void *(*old_memalign)(size_t alignment, size_t size, const void *caller);

static void my_free(void *ptr, const void *caller);
static void *my_malloc(size_t size, const void *caller);
static void *my_realloc(void *ptr, size_t size, const void *caller);
static void *my_memalign(size_t alignment, size_t size, const void *caller);

static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
static std::map<const void *,  BtInfo_t> _malloc_info_map;
static char _dym_symbol_dir[256] = "";
static size_t _malloc_size_check_point = 1024;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// internel function
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int get_backtrace_info(char ** &str_bt, size_t *bt_sz)
{
if (NULL == bt_sz)
{
PERR("[%s][%d] error bt is null\n", __FUNCTION__, __LINE__);
return FALSE;
}
#define _BT_SIZE_ (10)
void *array[_BT_SIZE_] = { NULL };
size_t size = backtrace (array, _BT_SIZE_);
#undef _BT_SIZE_
if (0 == size)
{
PERR("[%s][%d] bt sz is 0\n", __FUNCTION__, __LINE__);
return FALSE;
}

str_bt = backtrace_symbols (array, size);
if (NULL == str_bt)
{
PERR("[%s][%d] error str_bt is null\n", __FUNCTION__, __LINE__);
return FALSE;
}

*bt_sz = size;
return TRUE;
}

static pid_t gettid(void)
{
return syscall(SYS_gettid);
}

static int save_malloc_pointer_info(const void *ptr, const size_t size)
{
if (NULL == ptr)
{
PWRN("ptr is null\n");
return TRUE;
}

size_t bt_sz = 0;
BtInfo_t stBtInfo;
if (FALSE == get_backtrace_info(stBtInfo.str_bt, &bt_sz))
{
PWRN("[%s][%d]get bt info error\n", __FUNCTION__, __LINE__);
stBtInfo.str_bt = NULL;
}

stBtInfo.size = bt_sz;
stBtInfo.mem_size = size;
stBtInfo.tid = pthread_self();
stBtInfo.pid = gettid();
_malloc_info_map.insert(std::make_pair<const void *,  BtInfo_t>(ptr, stBtInfo));
return TRUE;
}

static int remove_free_pointer_info(const void *ptr)
{
if (NULL == ptr)
{
PWRN("[%s][%d]ptr is null\n", __FUNCTION__, __LINE__);
return TRUE;
}
std::map<const void *,  BtInfo_t>::iterator _it;
_it = _malloc_info_map.find(ptr);
if (_it == _malloc_info_map.end())
{
PERR("[%s][%d]point(%p) not found, must somethine error\n", __FUNCTION__, __LINE__, ptr);
return FALSE;
}

BtInfo_t *pstBtInfo = &(_it->second);
if (NULL != pstBtInfo)
{
if (NULL != pstBtInfo->str_bt)
{
free(pstBtInfo->str_bt);
pstBtInfo->str_bt = NULL;
}
}

_malloc_info_map.erase(_it);
return TRUE;
}

/// format string start
static int _frame_pos = 0;
static void format_output_info(const char *funcname, const char *file, int line)
{
if (NULL == funcname)
{
printf("#%d  (Unkonw Function Name) at (Unknow Source File):0\n", _frame_pos);
}
else if (NULL == file)
{
printf("#%d  %s() at (Unknow Source File):0\n", _frame_pos, funcname);
}
else
{
printf("#%d  %s() at %s:%d\n", _frame_pos, funcname, file, line);
}
_frame_pos++;
}

static int get_libname(const char *symbol, char *libname)
{
if ((NULL == symbol) || (NULL == libname))
{
return FALSE;
}

const char *end = strstr(symbol, ".so");
if (NULL == end)
{
return FALSE;
}

char *sta = (char *)rindex(symbol, '/');
int len = 0;
if (NULL == sta)
{
sta = (char *)symbol;
}
else
{
sta = sta + 1;
}

len = end + 3 - sta;
if (0 >= len)
{
return FALSE;
}

strncpy(libname, sta, len);
return TRUE;
}

static int get_funcname(const char *symbol, char *funcname)
{
if ((NULL == symbol) || (NULL == funcname))
{
return FALSE;
}

const char *sta = strchr(symbol, '(');
if (NULL == sta)
{
return FALSE;
}

const char *end = strchr(symbol, '+');
if (NULL == end)
{
return FALSE;
}

sta = sta + 1;
int len = end - sta;
if (1 >= len)
{
return FALSE;
}

strncpy(funcname, sta, len);
return TRUE;
}

static int trans_symbol_name(char *symbol_name)
{
if (NULL == symbol_name)
{
return FALSE;
}

int status = 0;
size_t size = 0;
char *realname = abi::__cxa_demangle(symbol_name, 0, &size, &status);
if (NULL != realname)
{
memset(symbol_name, 0x0, 256);
strncpy(symbol_name, realname, 256);
free(realname);
realname = NULL;
return TRUE;
}
return FALSE;
}

static void parse_func_info(const char *symbol)
{
if (NULL == symbol)
{
return;
}

char funcname[256];
memset(funcname, 0x0, sizeof(funcname));
if (TRUE == get_funcname(symbol, funcname))
{
trans_symbol_name(funcname);
format_output_info(funcname, NULL, 0);
}
else
{
format_output_info(symbol, NULL, 0);
}
}

static int get_lib_symbol_path(const char *libname, char *libpath)
{
if ((NULL == libname) || (NULL == libpath))
{
return FALSE;
}

struct dirent    *dp;
DIR              *dfd = NULL;

if(NULL == (dfd = opendir(_dym_symbol_dir)))
{
printf("open root dir failed! dir: %s", _dym_symbol_dir);
return FALSE;
}

for(dp = readdir(dfd); NULL != dp; dp = readdir(dfd))
{
if(strstr(dp->d_name, libname) != NULL)
{
strncpy(libpath, dp->d_name, 256);
closedir(dfd);
return TRUE;
}
}

closedir(dfd);
return FALSE;
}

static void parse_lib_symbol_info(const char *symbol, const char *libname)
{
if ('\0' == _dym_symbol_dir[0])
{
parse_func_info(symbol);
return;
}

char funcname[256];
if (FALSE == get_funcname(symbol, funcname))
{
format_output_info(symbol, NULL, 0);
return;
}

char libpath[256];
memset(libpath, 0x0, sizeof(libpath));
if (FALSE == get_lib_symbol_path(libname, libpath))
{
format_output_info(funcname, NULL, 0);
return;
}

///parse symbol lib info
}
/// format string end

static int malloc_info_map_status(void)
{
THREAD_AUTOLOCK;
size_t size = _malloc_info_map.size();
printf("== unfree pointer count:%ld ==\n", size);
printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
if (0 != size)
{
_frame_pos = 0;
printf("all alloc memory info:\n");
std::map<const void *,  BtInfo_t>::iterator _it;
size_t count = 0;
size_t memsize = 0;
for (_it = _malloc_info_map.begin(); _it != _malloc_info_map.end(); ++_it)
{
BtInfo_t *pstBtInfo = &(_it->second);
if (NULL == pstBtInfo)
{
printf("<< memory address:\t\t%p, size: unknow >>\n\n", _it->first);
continue;
}
printf("<< \t(%ld) memory address:\t%p, size: %ld, thread_id(0x%lx), p_id(0x%x)\t>>\n", ++count, _it->first, pstBtInfo->mem_size, pstBtInfo->tid, pstBtInfo->pid);
printf("\t(%ld) backtrace information(%ld):\n", count, pstBtInfo->size);
memsize += pstBtInfo->mem_size;
size_t i = 0;
char libname[256];
for (i = 0; i < pstBtInfo->size; ++i)
{
if (NULL == pstBtInfo->str_bt[i])
{
continue;
}

memset(libname, 0x0, sizeof(libname));
if (TRUE == get_libname(pstBtInfo->str_bt[i], libname))
{
///trans lib info
parse_lib_symbol_info(pstBtInfo->str_bt[i], libname);
}
else
{
parse_func_info(pstBtInfo->str_bt[i]);
}
}

printf("== all unfree memory time(0x%lx) size(0x%lx) ==\n", count, memsize);
}
}
return TRUE;
}

static void _private_hook_back()
{
#if __HOOK__
old_malloc   = __malloc_hook;
old_free     = __free_hook;
old_realloc  = __realloc_hook;
old_memalign = __memalign_hook;
#endif
}

static void _private_hook_init()
{
#if __HOOK__
__malloc_hook   = my_malloc;
__free_hook     = my_free;
__realloc_hook  = my_realloc;
__memalign_hook = my_memalign;
#endif
}

static void _private_hook_restore()
{
#if __HOOK__
__malloc_hook   = old_malloc;
__free_hook     = old_free;
__realloc_hook  = old_realloc;
__memalign_hook = old_memalign;
#endif
}

#if __HOOK__
static void *my_malloc(size_t size, const void *caller)
#else
void *__wrap_malloc(size_t size)
#endif
{
static int count = 0;
THREAD_AUTOLOCK;
void *p = NULL;
_private_hook_restore();
p = malloc(size);
//printf("xx malloc time(%d) xx\n", ++count);
save_malloc_pointer_info(p, size);
_private_hook_init();
return p;
}

#if __HOOK__
static void my_free(void *ptr, const void *caller)
#else
void __wrap_free(void *ptr)
#endif
{
static int count = 0;
THREAD_AUTOLOCK;
_private_hook_restore();
remove_free_pointer_info(ptr);
free(ptr);
//printf("xx free time(%d) xx\n", ++count);
_private_hook_init();
}

/*
如果是将分配的内存扩大,则有以下情况:
1)如果当前内存段后面有需要的内存空间,则直接扩展这段内存空间,realloc()将返回原指针。
2)如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据块释放掉,返回新的内存块位置。
3)如果申请失败,将返回NULL,此时,原来的指针仍然有效。
*/
static void *my_memalign(size_t boundary, size_t size, const void *caller)
{
static int count = 0;
THREAD_AUTOLOCK;
void *p = NULL;
_private_hook_restore();
p = malloc(size);
//printf("xx memalign time(%d) xx\n", ++count);
save_malloc_pointer_info(p, size);
_private_hook_init();
return p;
}

/*
如果是将分配的内存扩大,则有以下情况:
1)如果当前内存段后面有需要的内存空间,则直接扩展这段内存空间,realloc()将返回原指针。
2)如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据块释放掉,返回新的内存块位置。
3)如果申请失败,将返回NULL,此时,原来的指针仍然有效。
*/
#if __HOOK__
static void *my_realloc(void *__ptr, size_t size, const void *caller)
#else
void *__wrap_realloc(void *__ptr, size_t size)
#endif
{
THREAD_AUTOLOCK;
void *p = NULL;
_private_hook_restore();
//printf("realloc size: %ld\n", size);
void *ptr = __ptr;
p = realloc(__ptr, size);
if (NULL != p)
{
remove_free_pointer_info(ptr);
save_malloc_pointer_info(p, size);
}
_private_hook_init();
return p;
}

void *__wrap_calloc(size_t numElements, size_t sizeOfElement)
{
static int count = 0;
THREAD_AUTOLOCK;
void *p = NULL;
_private_hook_restore();
p = calloc(numElements, sizeOfElement);
//printf("xx calloc time(%d) xx\n", ++count);
save_malloc_pointer_info(p, numElements * sizeOfElement);
_private_hook_init();
return p;
}

static void my_mempool_destroy()
{
THREAD_AUTOLOCK;
_private_hook_restore();
}

static void my_mempool_init()
{
THREAD_AUTOLOCK;
_private_hook_back();
_private_hook_init();
//atexit(my_mempool_destroy);
}

pthread_t monitor_id = -1;
int thread_exit = FALSE;

void *malloc_size_monitor(void *args)
{
while (FALSE == thread_exit)
{
if (_malloc_size_check_point <= _malloc_info_map.size())
{
printf("== to becarefully, check point detect, malloc size(%ld) ==\n", _malloc_info_map.size());
malloc_info_map_status();
}

usleep(1000 * 1000);
}

return NULL;
}

static void malloc_size_monitor_thread_create(void)
{
THREAD_AUTOLOCK;
_private_hook_restore();
if (0 != pthread_create(&monitor_id, NULL, malloc_size_monitor, NULL))
{
monitor_id = -1;
}
_private_hook_init();
}

static void malloc_size_monitor_thread_destroy(void)
{
THREAD_AUTOLOCK;
_private_hook_restore();
thread_exit = TRUE;
if (-1 != monitor_id)
{
pthread_join(monitor_id, NULL);
}
_private_hook_init();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// extern function
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if (__HOOK__ == 1)
void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook) (void) = my_mempool_init;
#endif
__attribute((constructor)) void CCheckMemoryLeakInit(void)
{
printf("== Check Memory Leak Init ==\n");
memset(_dym_symbol_dir, 0x0, sizeof(_dym_symbol_dir));
malloc_size_monitor_thread_create();
}

__attribute((destructor)) void CCheckMemoryLeakDeInit(void)
{
malloc_size_monitor_thread_destroy();
my_mempool_destroy();
malloc_info_map_status();
printf("== Check Memory Leak Exit ==\n");
}

void CCheckMemoryLeakInfo(void)
{
malloc_info_map_status();
}

void CCheckMemoryLeakSetDymSymbolDir(const char *dir)
{
if (NULL != dir)
{
memcpy(_dym_symbol_dir, dir, sizeof(_dym_symbol_dir));
}
}

void CCheckMemoryLeakSetMallocSizeCheckPoint(size_t size)
{
_malloc_size_check_point = size;
}

size_t CCheckMemoryLeakGetMallocSizeInfo(void)
{
return _malloc_info_map.size();
}


CCheckMemoryLeak.h

#include <stdio.h>
#include <malloc.h>
#include <pthread.h>

#ifdef __cplusplus
extern "C" {
#endif
///private
void *__wrap_malloc(size_t size);
void *__wrap_realloc(void *__ptr, size_t size);
void *__wrap_calloc(size_t numElements, size_t sizeOfElement);
void __wrap_free(void *ptr);
///pulibc
void CCheckMemoryLeakInfo(void);
void CCheckMemoryLeakSetDymSymbolDir(const char *dir);
void CCheckMemoryLeakSetMallocSizeCheckPoint(size_t size);
size_t CCheckMemoryLeakGetMallocSizeInfo(void);
#ifdef __cplusplus
}
#endif


TestChkMemLeak.cpp

#include <stdio.h>
#include <string.h>
#include <execinfo.h>
#include <pthread.h>
#include <unistd.h>
#include <memory.h>

#include "CCheckMemoryLeak.h"

pthread_t monitor_id = -1;
int thread_exit = 0;
pthread_t monitor_id2 = -1;
int thread_exit2 = 0;

void *monitor(void *args)
{
int i = 0;
while (0 == thread_exit)
{
if (10 > i)
{
char *p = (char *)malloc(10);
printf("(%p)thread 1 exit(%d)\n", p, i);
free(p);
}
else{
printf("thread 1 exit(%d)\n", i);
break;
}
++i;
}

return NULL;
}

void *monitor2(void *args)
{
int i = 0;
while (0 == thread_exit2)
{
if (10 > i)
{
char *p = (char *)malloc(10);
printf("(%p)thread 2 exit(%d)\n", p, i);
free(p);
}
else{
printf("thread 2 exit(%d)\n", i);
break;
}
i++;
}

return NULL;
}

static void malloc_size_monitor_init(void)
{
if (0 != pthread_create(&monitor_id, NULL, monitor, NULL))
{
monitor_id = -1;
}
}

static void malloc_size_monitor_exit(void)
{
thread_exit = 1;
if (-1 != monitor_id)
{
pthread_join(monitor_id, NULL);
}
}

static void malloc_size_monitor_init2(void)
{
if (0 != pthread_create(&monitor_id2, NULL, monitor2, NULL))
{
monitor_id2 = -1;
}
}

static void malloc_size_monitor_exit2(void)
{
thread_exit2 = 1;
if (-1 != monitor_id2)
{
pthread_join(monitor_id2, NULL);
}
}

void next()
{
printf("\npause now, press 'n' to contine\n");
char c = 'x';
while (1)
{
scanf("%c", &c);
if ('n' == c)
{
return;
}
}
}
int main(int argc, char *argv[])
{
int len = 100;
char *p = (char *)malloc(len);
if (NULL != p)
{
memset(p, 0x1F, len);
CCheckMemoryLeakInfo();
char *p2 = (char *)realloc(p, 200);
CCheckMemoryLeakInfo();
free(p2);
p = NULL;
CCheckMemoryLeakInfo();
}

p = (char *)calloc(2, len);
free(p);
p = (char *)calloc(2, 200);
free(p);
#if 1
malloc_size_monitor_init();
malloc_size_monitor_init2();
malloc_size_monitor_exit();
malloc_size_monitor_exit2();
next();
#endif
return 0;
}


Makefile

GCC := gcc
GXX := g++
SRC := CCheckMemoryLeak.cpp
INC := ./
LIBDIR :=
BINLIBDIR := ./
LIB := -lpthread
BINLIB := -lChkMemLeak
CFLAGS += -Wno-deprecated-declarations -fPIC -Wl,--rpath=./ -rdynamic
CPPFLAGS += -Wno-deprecated-declarations -fPIC -Wl,--rpath=./ -rdynamic
TARGETLIB := libChkMemLeak.so
TARGETBIN := TestChkMemLeak
WRAPFUNC := -Wl,-wrap,malloc -Wl,-wrap,free -Wl,-wrap,calloc -Wl,-wrap,realloc
.PHONY : all libChkMemLeakCreate clean

all: libChkMemLeakCreate
$(GXX) $(CPPFLAGS) $(WRAPFUNC)  -g -o  $(TARGETBIN) TestChkMemLeak.cpp -L$(BINLIBDIR) $(BINLIB) -I$(INC) $(LIB)

libChkMemLeakCreate:
$(GXX) $(CPPFLAGS) -shared -g $(SRC) -o $(TARGETLIB) -L$(LIBDIR) -I$(INC) $(LIB)

clean:
rm -rf ./$(TARGETLIB)
rm -rf ./$(TARGETBIN)


Test Result:

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

all alloc memory info:

<< (1) memory address: 0x2490430, size: 200, thread_id(0x7f25f2caa780), p_id(0x3602) >>

(1) backtrace information(6):

#0 ./libChkMemLeak.so(+0x49fd) [0x7f25f28a19fd]() at (Unknow Source File):0

#1 ./libChkMemLeak.so(+0x4b01) [0x7f25f28a1b01]() at (Unknow Source File):0

#2 __wrap_realloc() at (Unknow Source File):0

#3 main() at (Unknow Source File):0

#4 __libc_start_main() at (Unknow Source File):0

#5 ./TestChkMemLeak() [0x400b79]() at (Unknow Source File):0

== all unfree memory time(0x1) size(0xc8) ==

== unfree pointer count:0 ==
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: