简单的C实现跟踪内存malloc / free?

时间:2009-05-12 10:10:04

标签: c dynamic-memory-allocation

编程语言:C 平台:ARM 编译:ADS 1.2

我需要在项目中跟踪简单的melloc/free调用。我只需要了解程序分配了所有资源后需要多少堆内存的基本概念。因此,我为malloc/free调用提供了一个包装器。在这些包装器中,我需要在调用malloc时增加当前内存计数,并在调用free时减少它。 malloc案例是直截了当的,因为我有从调用者分配的大小。我想知道如何处理free情况,因为我需要在某处存储指针/大小映射。这是C,我没有标准的地图来轻松实现这一点。

我试图避免在任何库中链接,因此更喜欢* .c / h实现。

所以我想知道是否已经有一个简单的实现可能会引导我。如果没有,这是继续实施的动机。

编辑:纯粹用于调试,此代码不随产品一起提供。

编辑:根据Makis的回答进行初步实施。我很感激对此的反馈。

编辑:重新实施

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <limits.h>

static size_t gnCurrentMemory = 0;
static size_t gnPeakMemory    = 0;

void *MemAlloc (size_t nSize)
{
  void *pMem = malloc(sizeof(size_t) + nSize);

  if (pMem)
  {
    size_t *pSize = (size_t *)pMem;

    memcpy(pSize, &nSize, sizeof(nSize));

    gnCurrentMemory += nSize;

    if (gnCurrentMemory > gnPeakMemory)
    {
      gnPeakMemory = gnCurrentMemory;
    }

    printf("PMemAlloc (%#X) - Size (%d), Current (%d), Peak (%d)\n",
           pSize + 1, nSize, gnCurrentMemory, gnPeakMemory);

    return(pSize + 1);
  }

  return NULL;
}

void  MemFree (void *pMem)
{
  if(pMem)
  {
    size_t *pSize = (size_t *)pMem;

    // Get the size
    --pSize;

    assert(gnCurrentMemory >= *pSize);

    printf("PMemFree (%#X) - Size (%d), Current (%d), Peak (%d)\n",
           pMem,  *pSize, gnCurrentMemory, gnPeakMemory);

    gnCurrentMemory -= *pSize;

    free(pSize);
  }
}

#define BUFFERSIZE (1024*1024)

typedef struct
{
  bool flag;
  int buffer[BUFFERSIZE];
  bool bools[BUFFERSIZE];
} sample_buffer;

typedef struct
{
  unsigned int whichbuffer;
  char ch;
} buffer_info;


int main(void)
{
  unsigned int i;
  buffer_info *bufferinfo;

  sample_buffer  *mybuffer;

  char *pCh;

  printf("Tesint MemAlloc - MemFree\n");

  mybuffer = (sample_buffer *) MemAlloc(sizeof(sample_buffer));

  if (mybuffer == NULL)
  {
    printf("ERROR ALLOCATING mybuffer\n");

    return EXIT_FAILURE;
  }

  bufferinfo = (buffer_info *) MemAlloc(sizeof(buffer_info));

  if (bufferinfo == NULL)
  {
    printf("ERROR ALLOCATING bufferinfo\n");

    MemFree(mybuffer);

    return EXIT_FAILURE;
  }

  pCh = (char *)MemAlloc(sizeof(char));

  printf("finished malloc\n");

  // fill allocated memory with integers and read back some values
  for(i = 0; i < BUFFERSIZE; ++i)
  {
    mybuffer->buffer[i] = i;
    mybuffer->bools[i] = true;
    bufferinfo->whichbuffer = (unsigned int)(i/100);
  }


  MemFree(bufferinfo);
  MemFree(mybuffer);

  if(pCh)
  {
    MemFree(pCh);
  }

  return EXIT_SUCCESS;
}

7 个答案:

答案 0 :(得分:12)

你可以在你的包装器中分配一些额外的字节并放入一个id(如果你想能够耦合malloc()和free())或只是那里的大小。只是malloc()更多的内存,将信息存储在内存块的开头,并移动指针,返回前面的那么多字节。

这可以,顺便说一句,也可以很容易地用于围栏指针/指纹等。

答案 1 :(得分:2)

您可以访问malloc / free使用的内部表(请参阅此问题:Where Do malloc() / free() Store Allocated Sizes and Addresses?获取一些提示),或者您必须在包装器中管理自己的表

答案 2 :(得分:1)

您始终可以使用valgrind而不是滚动自己的实现。如果你不关心你分配的内存量,你可以使用一个更简单的实现:(我很快就这样做了,所以可能会有错误,我意识到它不是最有效的实现。应该给pAllocedStorage一个初始大小,并通过调整大小等因素增加,但你明白了。)

编辑:我错过了这是针对ARM的,据我所知,valgrind在ARM上不可用,因此可能不是一个选项。

static size_t indexAllocedStorage = 0;
static size_t *pAllocedStorage = NULL;
static unsigned int free_calls = 0; 
static unsigned long long int total_mem_alloced = 0; 

void * 
my_malloc(size_t size){
    size_t *temp;
    void *p = malloc(size);
    if(p == NULL){
    fprintf(stderr,"my_malloc malloc failed, %s", strerror(errno));
    exit(EXIT_FAILURE);
    }

    total_mem_alloced += size;

    temp = (size_t *)realloc(pAllocedStorage, (indexAllocedStorage+1) * sizeof(size_t));
    if(temp == NULL){
        fprintf(stderr,"my_malloc realloc failed, %s", strerror(errno));
         exit(EXIT_FAILURE);
    }

    pAllocedStorage = temp; 
    pAllocedStorage[indexAllocedStorage++] = (size_t)p;

    return p;
}

void 
my_free(void *p){
    size_t i;
    int found = 0;

    for(i = 0; i < indexAllocedStorage; i++){
    if(pAllocedStorage[i] == (size_t)p){
        pAllocedStorage[i] = (size_t)NULL;
        found = 1;
        break;
        }
    }

    if(!found){
        printf("Free Called on unknown\n");
    }

    free_calls++;
    free(p);
}

void 
free_check(void) {
    size_t i;

    printf("checking freed memeory\n");
    for(i = 0; i < indexAllocedStorage; i++){   
        if(pAllocedStorage[i] != (size_t)NULL){
            printf( "Memory leak %X\n", (unsigned int)pAllocedStorage[i]);
            free((void *)pAllocedStorage[i]);
        }
    }

    free(pAllocedStorage);
    pAllocedStorage = NULL;
}

答案 3 :(得分:0)

我会使用rmalloc。它是一个简单的库(实际上只有两个文件)来调试内存使用情况,但它也支持统计。由于你已经有了包装函数,所以应该很容易使用rmalloc。请记住,您还需要替换strdup等。

答案 4 :(得分:0)

您的程序可能还需要拦截realloc(),calloc(),getcwd()(因为它可能在缓冲区在某些实现中为NULL时分配内存)并且可能是strdup()或类似函数,如果它受支持你的编译器

答案 5 :(得分:0)

如果您在x86上运行,则可以valgrind 下运行您的二进制文件,它会使用标准实现{{1}为您收集所有这些信息}和malloc。简单。

答案 6 :(得分:0)

我一直在试用本页提到的一些相同的技术,并从谷歌搜索结束。我知道这个问题已经过时了,但是想加入记录......

1)您的操作系统是否提供了任何工具来查看正在运行的进程中正在使用的堆内存量?我看到你在谈论ARM,所以情况可能就是如此。在大多数功能齐全的操作系统中,只需使用cmd-line工具查看堆大小。

2)如果您的libc中有,大多数平台上的sbrk(0)都会告诉您数据段的结束地址。如果你拥有它,你需要做的就是在你的程序开始时存储该地址(比如,startBrk = sbrk(0)),然后在任何时候你的分配大小是sbrk(0) - startBrk。

3)如果可以使用共享对象,则动态链接到libc,并且OS的运行时加载器具有类似LD_PRELOAD的环境变量,您可能会发现构建自己的共享对象更有用,它定义了实际的libc函数具有相同的符号(malloc(),而不是MemAlloc()),然后让加载器首先加载你的lib并“插入”libc函数。您可以使用dlsym()和RTLD_NEXT标志进一步获取实际libc函数的地址,这样您就可以执行上述操作,而无需重新编译所有代码以使用malloc / free包装器。然后,当您启动程序(或任何符合第一句中描述的程序)时,只需要运行时决定,您可以在其中设置环境变量,如LD_PRELOAD = mymemdebug.so然后运行它。 (google for shared object interposition ..这是一项很棒的技术,很多调试器/分析器使用它)