系列文章目录
文章目录
目录
代码地址
cpp
/*
* 作者: 干饭小白
* 时间: 2023-09-25 16:00:00:00
*
* 说明:
* 只能检测 malloc 和 free,无法检测 new delete
*/
#pragma once
// #define CHECKMEM
#define OPEN_TOTAL_INFO 0x001
#define OPEN_SINGLE_INFO 0x010
#define OPEN_THREAD_INFO 0x100
#include <map>
#include <string>
#include <cstddef>
#include <utility>
#ifdef __cplusplus
extern "C"
{
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <stdio.h>
#include <sys/shm.h>
#include <unistd.h>
#include <semaphore.h>
};
#endif
struct _gc_info
{
long _size;
std::string _content;
_gc_info()
{
}
_gc_info(long size, std::string con)
{
_size = size;
_content = con;
}
};
static char Logpath[256] = {0};
static char DirPath[256] = {0};
static std::map<std::string ,_gc_info> total;
volatile static long totalMalloc = 0;
volatile static long totalFree = 0;
volatile static long totalMallocSize = 0;
volatile static long totalFreeSize = 0;
static uint16_t Mode = false;
static sem_t sem_mutex;
extern void get_time(char* str)
{
time_t now;
struct tm *local_time;
now = time(NULL);
local_time = localtime(&now);
sprintf(str, "%d-%02d-%02d_%02d:%02d:%02d",
local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday,
local_time->tm_hour, local_time->tm_min, local_time->tm_sec);
return;
}
extern void init_gc(const char* dirPath, uint16_t mode = OPEN_TOTAL_INFO)
{
sem_init(&sem_mutex, 0, 1);
struct stat st;
if (stat(dirPath, &st) == 0 && S_ISDIR(st.st_mode))
{
// printf("文件夹已存在\n");
}
else
{
if(mkdir(dirPath, 0777) != 0)
{
return;
}
}
Mode = mode;
strncpy(DirPath, dirPath, 256);
char time[256] = {0};
get_time(time);
sprintf(Logpath, "%s/%s_total.log", dirPath, time);
unlink(Logpath);
char buff[128] = {0};
sprintf(buff, "%s/thread.log", DirPath);
unlink(buff);
return;
}
extern void total_gc()
{
sem_destroy (&sem_mutex);
if((Mode & OPEN_TOTAL_INFO) == OPEN_TOTAL_INFO)
{
FILE *fp = fopen(Logpath, "a+w");
fprintf(fp, "\n=========================================\n");
fprintf(fp, "申请次数:%ld\n", totalMalloc);
fprintf(fp, "释放次数:%ld\n", totalFree);
fprintf(fp, "申请总大小:%ld\n", totalMallocSize);
fprintf(fp, "释放总大小:%ld\n", totalFreeSize);
fprintf(fp, "未释放总大小:%ld\n", totalMallocSize - totalFreeSize);
fprintf(fp, "问题统计:%ld\n", total.size());
fprintf(fp, "=========================================\n");
fflush(fp);
for(auto it = total.begin(); it != total.end(); ++it)
{
fprintf(fp, "%s", it->second._content.c_str());
fflush(fp);
}
fclose(fp);
}
total.clear();
}
extern void* malloc_gc(size_t size, const char* file, const char* func, int line)
{
sem_wait(&sem_mutex);
FILE *test = NULL;
if((Mode & OPEN_THREAD_INFO) == OPEN_THREAD_INFO)
{
char buff[128] = {0};
sprintf(buff, "%s/thread.log", DirPath);
test = fopen(buff, "a+w");
fprintf(test, "ERROR 进入malloc_gc--->%ld\n", pthread_self());
fflush(test);
}
void* p =malloc(size);
char time[256] = {0};
get_time(time);
FILE *fp = NULL;
if((Mode & OPEN_SINGLE_INFO) == OPEN_SINGLE_INFO)
{
char buff[128] = {0};
sprintf(buff, "%s/%p.log", DirPath,p);
FILE *fp = fopen(buff, "w");
fprintf(fp, "%s:ERROR [+%s:%s:%d][PID:%ld] --> ADDR:%p, size:%ld\n", time, file, func, line, pthread_self() , p, size);
fflush(fp);
fclose(fp);
}
if((Mode & OPEN_TOTAL_INFO) == OPEN_TOTAL_INFO)
{
totalMallocSize+= size;
fp = fopen(Logpath, "a+w");
fprintf(fp, "%s:[+%s:%s:%d][PID:%ld] --> ADDR:%p, size:%ld\n", time, file, func, line, pthread_self(), p, size);
fflush(fp);
fclose(fp);
char content[256] = {0};
sprintf(content, "%s:ERROR [+!%s:%s:%d][PID:%ld] 内存泄漏 --> ADDR:%p, size:%ld\n", time, file, func, line, pthread_self(), p, size);
char addr[128] = {0};
sprintf(addr, "%p", p);
_gc_info info(size, content);
total.insert(std::make_pair(addr, info));
totalMalloc++;
}
if((Mode & OPEN_THREAD_INFO) == OPEN_THREAD_INFO)
{
fprintf(test, "INFO 离开malloc_gc--->%ld\n", pthread_self());
fflush(test);
fclose(test);
}
sem_post(&sem_mutex);
return p;
}
extern void free_gc(void* p, const char* file, const char* func, int line)
{
sem_wait(&sem_mutex);
FILE *test = NULL;
if((Mode & OPEN_THREAD_INFO) == OPEN_THREAD_INFO)
{
char buff[128] = {0};
sprintf(buff, "%s/thread.log", DirPath);
test = fopen(buff, "a+w");
fprintf(test, "ERROR 进入free_gc--->%ld\n", pthread_self());
fflush(test);
}
char time[256] = {0};
get_time(time);
if((Mode & OPEN_TOTAL_INFO) == OPEN_TOTAL_INFO)
{
totalFree++;
char addr[128] = {0};
sprintf(addr, "%p", p);
if(total.find(addr) == total.end())
{
char content[256] = {0};
sprintf(content, "%s:ERROR [-!%s:%s:%d][PID:%ld] 二次释放 --> ADDR:%p\n", time, file, func, line, pthread_self(), p);
char addr[128] = {0};
sprintf(addr, "%p", p);
_gc_info info(0, content);
sem_post(&sem_mutex);
total.insert(std::make_pair(addr, info));
}
else
{
totalFreeSize += total[addr]._size;
FILE* fp = fopen(Logpath, "a+w");
fprintf(fp, "%s:[-%s:%s:%d][PID:%ld] --> ADDR:%p, size:%ld\n", time, file, func, line, pthread_self(), p, total[addr]._size);
fflush(fp);
fclose(fp);
total.erase(addr);
free(p);
}
}
char buff[128] = {0};
sprintf(buff, "%s/%p.log", DirPath,p);
if((Mode & OPEN_SINGLE_INFO) == OPEN_SINGLE_INFO && unlink(buff) < 0)
{
FILE *fp = fopen(buff, "w");
fprintf(fp, "%s:ERROR [-!%s:%s:%d][PID:%ld] 二次释放 --> ADDR:%p\n", time, file, func, line, pthread_self(), p);
fflush(fp);
fclose(fp);
}
if((Mode & OPEN_THREAD_INFO) == OPEN_THREAD_INFO)
{
fprintf(test, "INFO 离开free_gc--->%ld\n", pthread_self());
fflush(test);
fclose(test);
}
sem_post(&sem_mutex);
return;
}
#ifdef CHECKMEM
#define malloc(size) malloc_gc(size, __FILE__, __FUNCTION__, __LINE__)
#define free(p) free_gc(p, __FILE__, __FUNCTION__, __LINE__)
#endif
相关说明
1.不支持new 和 delete。尝试过,发现效果不好,new本身内部会malloc和free,想要支持多线程,容易加锁两次。delete不方便宏替换。
2.支持多线程,不支持多进程,如果想要支持多进程,相关的控制部分定义到共享内存中
3.定义CHECKMEM,开启内存检查
使用案例
cpp
#include "mem.h"
int main()
{
init_gc("./mem", OPEN_TOTAL_INFO | OPEN_SINGLE_INFO | OPEN_THREAD_INFO);
void* p1 = malloc(1);
void* p2 = malloc(1);
void* p3 = malloc(1);
free(p1);
free(p2);
free(p2);
total_gc();
return 0;
}
部分效果图
多线程下定位具体线程 ==> OPEN_THREAD_INFO
统计信息 ==> OPEN_TOTAL_INFO
单文件快速定位 ==> OPEN_SINGLE_INFO