嵌入式之内存泄漏定位篇
在嵌入式开发中,经常会使用malloc,free分配释放堆内存,当malloc,free不配对使用时,就会导致内存一点点地泄露,直至堆内存泄露完,导致设备异常重启或死机现象。对于内存泄漏的情况,如果一开始不做预防,定位内存泄漏就会相当繁琐,定位也会很长,非常的耗时、耗力。
这里可通过malloc、free的第二次封装来预防内存泄漏,主要表现是分别记录不同地方调用malloc、free的次数,来判断malloc、free是否配对使用,如果不配对使用,可以帮助定位到哪个地方是不配对使用的,以此达到内存泄漏定位的效果。
以下将提供测试用例,效果描述:记录不同地方调用malloc、free的次数,每隔三秒将不同地方调用malloc、free的次数打印到终端,并文件的形式将信息保存到/tmp下。
编译后运行后,效果如下:
以下是malloc和free的第二次封装测试用例
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define Inf_Free(p) MemFree(p) // free
#define Inf_Malloc(l) MemMalloc(l, __FUNCTION__, __LINE__) // malloc
#define addrlistlen 1024*5
#define MAX_FILE_LEN (1024*1024)
#define MAX_MEM_LEN 256
static pthread_mutex_t g_alloc_mem_mutex = PTHREAD_MUTEX_INITIALIZER;//静态创建
static int memory_check = 0;
typedef struct StatisticalMemoryNode
{
char szFile[MAX_MEM_LEN];
unsigned int iLine;
int statisticalMemoryTatolcounts;
int statisticalMemoryReleaseTatolcounts;
long addrList[addrlistlen];
int addrListCounts;
int addrListNext;
struct StatisticalMemoryNode* next;
}StatisticalMemoryNode_S;
static StatisticalMemoryNode_S *g_pStatisticaMemHead = NULL;
int StatisticalMemoryApplication(const char *str_string, int lines, void *addr)
{
if(0 == memory_check)
{
return 0;
}
int malloc_sign = 0,found = 0;
int temp_add = 0 ,i;
pthread_mutex_lock(&g_alloc_mem_mutex);
StatisticalMemoryNode_S *temp = g_pStatisticaMemHead;
while(temp)
{
if(lines == temp->iLine && strcmp(temp->szFile,str_string) == 0)
{
malloc_sign =1;
break;
}
temp = temp->next;
}
if(malloc_sign != 1)
{
StatisticalMemoryNode_S *node = (StatisticalMemoryNode_S *)malloc(sizeof(StatisticalMemoryNode_S));
strncpy(node->szFile , str_string , sizeof(node->szFile));
node->iLine = lines;
node->next = NULL;
node->statisticalMemoryTatolcounts = 1;
node->statisticalMemoryReleaseTatolcounts = 0;
node->addrListNext = 0;
memset(node->addrList , 0 , sizeof(node->addrList));
node->addrList[node->addrListNext] = (long)addr;
node->addrListCounts=1;
if(!g_pStatisticaMemHead)
{
g_pStatisticaMemHead = node;
}
else
{
node->next = g_pStatisticaMemHead;
g_pStatisticaMemHead = node;
}
}
else
{
temp_add = temp->addrListNext;
for(i = 0 ; i < addrlistlen ; i++ )
{
temp_add = temp_add % addrlistlen;
if(temp->addrList[temp_add] == 0)
{
found = 1;
temp->addrListNext = temp_add;
break;
}
temp_add++;
}
if(found)
{
temp->statisticalMemoryTatolcounts++;
temp->addrListCounts++;
temp->addrList[temp->addrListNext] = (long)addr;
}
}
pthread_mutex_unlock(&g_alloc_mem_mutex);
return 0;
}
int StatisticalMemoryRelease(const char *str_string, int lines, void *addr)
{
if(0 == memory_check)
{
return 0;
}
int i,found = 0;
pthread_mutex_lock(&g_alloc_mem_mutex);
StatisticalMemoryNode_S *temp = g_pStatisticaMemHead;
while(temp)
{
for(i=0;i<addrlistlen;i++)
{
if(temp->addrList[i] == (long)addr)
{
found =1;
break;
}
}
if(found)
break;
temp = temp->next;
}
if(found)
{
temp->statisticalMemoryReleaseTatolcounts++;
temp->addrListCounts--;
temp->addrList[temp->addrListNext] = 0;
}
pthread_mutex_unlock(&g_alloc_mem_mutex);
return 0;
}
void* MemMalloc(int ilen, const char* pszFile, int iLine)
{
if(0 == memory_check)
{
return malloc(ilen);
}
else
{
void *addr = malloc(ilen);
StatisticalMemoryApplication(pszFile, iLine, addr);
return addr;
}
}
void MemFree(void* pBuf)
{
if (pBuf == NULL) return;
if(0 == memory_check)
{
free(pBuf);
}
else
{
free(pBuf);
StatisticalMemoryRelease(NULL, 0, pBuf);
}
}
void *Thread_Mem_Debug(void)
{
prctl(PR_SET_NAME, "Thread_Mem_Debug", 0, 0, 0);
pthread_detach(pthread_self());
while(1)
{
if (g_pStatisticaMemHead == NULL)
return 0;
pthread_mutex_lock(&g_alloc_mem_mutex);
char filePath[256] = {0};
sprintf(filePath,"/tmp/statisticalMemory_%s", "app");
int clearFlag = 0;
FILE *fp = NULL;
if (access(filePath, F_OK ) == 0)
{
struct stat filestat;
stat(filePath, &filestat);
if (filestat.st_size > MAX_FILE_LEN)
{
clearFlag = 1;
}
}
fp = fopen(filePath, (clearFlag == 0) ? "a+" : "w+");
if (NULL == fp)
{
pthread_mutex_unlock(&g_alloc_mem_mutex);
printf("fopen error! %s %d\n",__FUNCTION__,__LINE__);
return NULL;
}
char buffer[1024] = {0};
time_t currentTime;
currentTime = time((time_t*)NULL);
struct tm tmTmp;
gmtime_r(¤tTime, &tmTmp);
sprintf(buffer, "\n********************%d.%d.%d %d:%d:%d*********************\n",\
tmTmp.tm_year+1900, tmTmp.tm_mon+1, tmTmp.tm_mday, tmTmp.tm_hour+8, tmTmp.tm_min, tmTmp.tm_sec);
printf("%s\n",buffer);
fwrite(buffer, strlen(buffer), 1, fp);
StatisticalMemoryNode_S *tmp = g_pStatisticaMemHead;
while(tmp)
{
memset(buffer, 0, 1024);
sprintf(buffer,"[%s-%u] statisticalMemoryTatolcounts(%d) - ReleaseTatolcounts(%d) = %d\r\n",
tmp->szFile, tmp->iLine, tmp->statisticalMemoryTatolcounts, tmp->statisticalMemoryReleaseTatolcounts, tmp->addrListCounts);
printf("%s\n",buffer);
fwrite(buffer, strlen(buffer), 1, fp);
tmp = tmp->next;
}
fclose(fp);
pthread_mutex_unlock(&g_alloc_mem_mutex);
sleep(3);
}
return 0;
}
void *Thread_Mem_Test2(void)
{
prctl(PR_SET_NAME, "Thread_Mem_Test2", 0, 0, 0);
pthread_detach(pthread_self());
while(1)
{
char *p = (char *)Inf_Malloc(12);
Inf_Free(p);
p=NULL;
sleep(2);
}
return 0;
}
void *Thread_Mem_Test1(void)
{
prctl(PR_SET_NAME, "Thread_Mem_Test1", 0, 0, 0);
pthread_detach(pthread_self());
while(1)
{
char *p = (char *)Inf_Malloc(12);
Inf_Free(p);
p=NULL;
sleep(2);
}
return 0;
}
int main()
{
//pthread_mutex_init(&g_alloc_mem_mutex, NULL);//动态创建
memory_check =1;//
pthread_t id[3] = {-1};
if(0 != pthread_create(&id[0], NULL, (void *)Thread_Mem_Debug, NULL))
{
printf("Thread_Mem_Debug debug print create failed!\n");
return -1;
}
if(0 != pthread_create(&id[1], NULL, (void *)Thread_Mem_Test2, NULL))
{
printf("Thread_Mem_Test2 debug print create failed!\n");
return -1;
}
if(0 != pthread_create(&id[2], NULL, (void *)Thread_Mem_Test1, NULL))
{
printf("Thread_Mem_Test1 debug print create failed!\n");
return -1;
}
while(1)
{
char *p = (char *)Inf_Malloc(12);
Inf_Free(p);
p=NULL;
sleep(2);
}
return 0;
}
总结:测试用例不仅适用于maloc,free的内存泄漏定位,也适用于open close等描述符,比如文件描述符、套接字描述符等泄漏定位的操作。