C 通过宏定义重定义malloc - free,预防内存泄露

系列文章目录

C++模版基础


文章目录

目录

代码地址

相关说明

使用案例



代码地址

GitHub - CHENLitterWhite/CPPWheel: CPP自封装的库

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

相关推荐
Zfox_27 分钟前
【Linux】进程信号全攻略(二)
linux·运维·c语言·c++
速盾cdn28 分钟前
速盾:vue的cdn是干嘛的?
服务器·前端·网络
安於宿命32 分钟前
【Linux】简易版shell
linux·运维·服务器
丶Darling.35 分钟前
MIT 6.S081 Lab1: Xv6 and Unix utilities翻译
服务器·unix·lab·mit 6.s081·英文翻译中文
黄小耶@44 分钟前
linux常见命令
linux·运维·服务器
叫我龙翔1 小时前
【计网】实现reactor反应堆模型 --- 框架搭建
linux·运维·网络
起名字真南1 小时前
【OJ题解】C++实现字符串大数相乘:无BigInteger库的字符串乘积解决方案
开发语言·c++·leetcode
少年负剑去1 小时前
第十五届蓝桥杯C/C++B组题解——数字接龙
c语言·c++·蓝桥杯
cleveryuoyuo1 小时前
AVL树的旋转
c++
古驿幽情1 小时前
CentOS AppStream 8 手动更新 yum源
linux·运维·centos·yum