指针是C语言的最重要的特色,用得好能写出优雅高效的代码,滥用了会成为烦恼的根源。当代码中大量使用指针,运行时内存分配,一不小心就会出现内存泄漏,因此,快速定位内存泄漏点,是高级c语言工程师必备技能。下面跟大家分享一个经过多年项目实践总结出来的定位内存泄漏的方法。
typedef struct
{
int malloc_size;
int size;
void **list;
} MALLOC_DEBUG;
MALLOC_DEBUG g_malloc_list={0,0,NULL};
pthread_mutex_t g_mutex_list=PTHREAD_MUTEX_INITIALIZER;
void find_empty(MALLOC_DEBUG *ls,void *ptr)
{
int i=0;
for(i=0; i<ls->malloc_size; i++){
if(ls->list[i]==NULL){
ls->list[i]=ptr;
ls->size++;
return;
}
}
printf("find_empty()not find!\n");
}
void malloc_add(void *ptr)
{
if(g_malloc_list.list==NULL || (g_malloc_list.size >= g_malloc_list.malloc_size)){
int size=g_malloc_list.malloc_size + 512;
if(size > 10000){
printf("malloc_add()warning:More than 10000 records!\n");
return;
}
void **buf=(void **)malloc(sizeof(void*) * size);
if(buf){
memset(buf,0,size * sizeof(void*));
pthread_mutex_lock(&g_mutex_list);
g_malloc_list.malloc_size=size;
if(g_malloc_list.list){
memcpy(buf,g_malloc_list.list,g_malloc_list.size * sizeof(void*));
free(g_malloc_list.list);
}
g_malloc_list.list=buf;
find_empty(&g_malloc_list,ptr);
pthread_mutex_unlock(&g_mutex_list);
}else{
printf("malloc_add()malloc fail\n");
}
}else{
pthread_mutex_lock(&g_mutex_list);
find_empty(&g_malloc_list,ptr);
pthread_mutex_unlock(&g_mutex_list);
}
}
void malloc_free(void *ptr)
{
int i=0;
if(g_malloc_list.size > 0){
for(i=0; i<g_malloc_list.malloc_size; i++){
if(g_malloc_list.list[i] == ptr){
pthread_mutex_lock(&g_mutex_list);
g_malloc_list.list[i]=NULL;
g_malloc_list.size--;
pthread_mutex_unlock(&g_mutex_list);
return;
}
}
}
printf("error:may be free multiple times of 0x%08x\n",(uint32_t)ptr);
}
void malloc_dump_and_destroy()
{
if(g_malloc_list.size > 0){
printf("The remaining %d malloc are not free:\n",g_malloc_list.size);
for(int i=0; i<g_malloc_list.malloc_size; i++){
if(g_malloc_list.list[i] != NULL){
printf("0x%08x ",(uint32_t)g_malloc_list.list[i]);
}
}
printf("\n");
}else{
printf("malloc and free times are equal\n");
}
if(g_malloc_list.list) free(g_malloc_list.list);
g_malloc_list.list=NULL;
g_malloc_list.malloc_size=0;
g_malloc_list.size=0;
}
#define MALLOC_LOG_FILE "/tmp/malloc_log.txt"
uint8_t g_malloc_log=0;
FILE *g_fd_malloc_log=NULL;
/*malloc_set_log() for one thread only, please delete it after debugging*/
void malloc_set_log(uint8_t enable)
{
g_malloc_log=enable;
if(enable){
g_fd_malloc_log=fopen(MALLOC_LOG_FILE,"a");
if(g_fd_malloc_log){
fprintf(g_fd_malloc_log,">>>>>>>time:%u<<<<<<<\n\n",(uint32_t)time(NULL));
}
}else{
if(g_fd_malloc_log){
fprintf(g_fd_malloc_log,"\n\n");
fclose(g_fd_malloc_log);
g_fd_malloc_log=NULL;
malloc_dump_and_destroy();
}
}
}
void *malloc_sg(size_t size,char *prompt)
{
void *ptr=malloc(size);
if(ptr==NULL){
printf("%s malloc %d is fail!\n",prompt,size);
fflush(stdout);
}
if(g_malloc_log && g_fd_malloc_log){
fprintf(g_fd_malloc_log,"%s malloc %d ok:0x%08x\n",prompt,size,(uint32_t)ptr);
fflush(g_fd_malloc_log);
malloc_add(ptr);
}
return ptr;
}
void *realloc_sg(void *ptr,size_t size,char *prompt)
{
void *ptr_new=realloc(ptr,size);
if(ptr_new==NULL){
printf("%s realloc %d at 0x%x is fail!\n",prompt,size,(uint32_t)ptr);
fflush(stdout);
}else{
if(g_malloc_log && g_fd_malloc_log){
fprintf(g_fd_malloc_log,"%s realloc %d at 0x%08x\n",prompt,size,(uint32_t)ptr);
fprintf(g_fd_malloc_log,"%s realloc %d at 0x%08x new:0x%08x\n",prompt,size,(uint32_t)ptr,(uint32_t)ptr_new);
fflush(g_fd_malloc_log);
malloc_free(ptr);
malloc_add(ptr_new);
}
}
return ptr_new;
}
void free_sg(void *ptr,char *prompt)
{
if(g_malloc_log && g_fd_malloc_log){
fprintf(g_fd_malloc_log,"%s free 0x%08x\n",prompt,(uint32_t)ptr);
fflush(g_fd_malloc_log);
malloc_free(ptr);
}
free(ptr);
}
使用void *malloc_sg(size_t size,char *prompt)分配内存,void free_sg(void *ptr,char *prompt)释放内存,都附带prompt参数,prompt参数用于记录分配、释放内存日记到文件,方便追踪内存泄漏点。void malloc_set_log(uint8_t enable)用于设置追踪的代码段起始点,enable=1是起点,enable=0是结束点,如有未释放内存,将打印未释放的内存地址,结合日记,可以找到内存泄漏点。