【问题标题】:Simple C implementation to track memory malloc/free?跟踪内存 malloc/free 的简单 C 实现?
【发布时间】:2010-10-25 12:32:55
【问题描述】:

编程语言: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;
}

【问题讨论】:

  • 我认为 MemAlloc 中不需要两个 malloc()。只需编写一个宏来确定一个合适的对齐大小(或使用 64 位,我认为这对每种情况都足够了)并在分配内存之前按该数量添加 nSize。
  • 谢谢 Makis。我在32位平台上。我已经更新了我的实现以在 MemAlloc 中使用单个 malloc。我不明白对齐的重点。如果不是要求太多,您能否在我的实施中指出这可能是一个问题。据推测,如果传递给 MemFree 或从 malloc 返回的指针已经没有对齐,那么没有什么可以做的,因为如果我不使用我的包装器,这些将是未对齐的,对吧?
  • 这里有一个关于这个问题的很好的解释:goingware.com/tips/getting-started/alignment.html 我也会有 32 位的大小信息,这应该可以解决这个问题。问题可能是这样的:您从位置 X 开始保留内存,前两个字节是您的大小信息,因此您将 x+2 返回给调用者。但是,如果对齐是 4 个字节,您可能会遇到问题。所以检查 size_t 的大小,或者如果你想要可移植的代码,你需要定义一些宏。
  • 好的。谢谢。 sizeof(size_t) 是 4 个字节。我实际上打算使用 uint32_t 使其更明确

标签: c dynamic-memory-allocation


【解决方案1】:

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

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

【讨论】:

  • 确保您返回的指针与 malloc 返回的指针具有相同或更好的对齐方式,以防万一。
  • 好点,埃亚尔。无论我被错位(通常是结构)烧伤多少次,我仍然倾向于忘记它:)
  • 大小可能已经存在,将返回的指针转换为 (int *) 并检查 ptr[-1] 或 ptr[-2] 处的值。请注意,分配的大小可能会向上取整以保持对齐,因此对 123 字节缓冲区的请求可能会返回 128 字节,因此请进行相应检查。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    您始终可以使用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;
    }
    

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

        • 如果你覆盖 malloc()(以及 realloc()、calloc()、alloca() 和 free()),那么你就可以免费获得其他的(可以这么说),因为它们都只是在内部调用 malloc()。
        • 我的程序只使用了 MemAlloc 和 MemFree,所以只拦截 malloc 和 free 就足够了。
        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

          我一直在尝试本页中提到的一些相同技术,并通过谷歌搜索来到这里。我知道这个问题很老,但想添加记录......

          1) 您的操作系统是否没有提供任何工具来查看正在运行的进程中使用了多少堆内存?我看到你在谈论 ARM,所以很可能就是这种情况。在大多数功能齐全的操作系统中,这只是使用命令行工具查看堆大小的问题。

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

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

          【讨论】:

          • 这是一个非常简单的内部操作系统。 1) 否 (2) sbrk 不可用 (3) 共享对象不可用。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-01-03
          • 1970-01-01
          • 1970-01-01
          • 2021-01-18
          • 2014-08-30
          • 1970-01-01
          • 2011-03-22
          相关资源
          最近更新 更多