¿Implementación simple de C para rastrear la memoria malloc/free?

9 minutos de lectura

avatar de usuario
doblar

lenguaje de programación: C plataforma: ARM Compilador: ADS 1.2

Necesito hacer un seguimiento de simple melloc/free llamadas en mi proyecto. Solo necesito tener una idea muy básica de cuánta memoria de almacenamiento dinámico se requiere cuando el programa ha asignado todos sus recursos. Por lo tanto, he proporcionado un envoltorio para el malloc/free llamadas En estos envoltorios, necesito incrementar un recuento de memoria actual cuando malloc es llamado y decrementarlo cuando free se llama. los malloc El caso es sencillo ya que tengo el tamaño para asignar de la persona que llama. Me pregunto cómo lidiar con el free caso, ya que necesito almacenar el mapeo de puntero/tamaño en algún lugar. Siendo esto C, no tengo un mapa estándar para implementar esto fácilmente.

Estoy tratando de evitar la vinculación en cualquier biblioteca, por lo que preferiría la implementación * .c/h.

Entonces, me pregunto si ya existe una implementación simple a la que me pueda llevar. Si no, esta es la motivación para seguir adelante e implementar uno.

EDITAR: puramente para la depuración y este código no se envía con el producto.

EDITAR: Implementación inicial basada en la respuesta de Makis. Apreciaría comentarios sobre esto.

EDITAR: implementación reelaborada

#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;
}

  • No creo que necesites dos malloc() en MemAlloc. Simplemente escriba una macro para determinar un buen tamaño para la alineación (o use 64 bits, creo que sería suficiente para todos los casos) y agregue nSize por esa cantidad antes de asignar la memoria.

    – Makis

    12 de mayo de 2009 a las 13:15

  • Gracias makis. Estoy en la plataforma de 32 bits. He actualizado mi implementación para usar solo malloc en MemAlloc. Sin embargo, no entiendo el punto sobre la alineación. Si no es pedir demasiado, ¿puede señalar en mi implementación dónde podría ser un problema? Presumiblemente, si el puntero pasado a MemFree o devuelto desde malloc ya no está alineado, no hay mucho que se pueda hacer, ya que de todos modos no estarían alineados si no usara mi contenedor, ¿verdad?

    – Duda

    12 de mayo de 2009 a las 16:31

  • Aquí hay una buena explicación sobre el tema: goingware.com/tips/getting-started/alignment.html También tendría la información de tamaño en 32 bits, esto debería solucionar el problema. El problema puede ser así: reserva memoria a partir de la ubicación X y los dos primeros bytes son su información de tamaño, por lo que devuelve x+2 a la persona que llama. Sin embargo, si la alineación es de 4 bytes, podría encontrarse con un problema. Por lo tanto, verifique qué tamaño es size_t, o si desea un código portátil, debe definir algunas macros.

    – Makis

    13 de mayo de 2009 a las 6:03

  • está bien. Gracias. sizeof(size_t) es de 4 bytes. De hecho, voy a usar uint32_t para hacerlo más explícito.

    – Duda

    13 de mayo de 2009 a las 8:25

Puede asignar algunos bytes adicionales en su contenedor y poner una identificación (si desea poder acoplar malloc() y free()) o simplemente el tamaño allí. Simplemente malloc () esa cantidad de memoria, almacene la información al comienzo de su bloque de memoria y mueva el puntero para devolver esa cantidad de bytes hacia adelante.

Esto, por cierto, también se puede usar fácilmente para punteros de vallas/huellas dactilares y demás.

  • Asegúrese de que el puntero que devuelve tenga la misma alineación o mejor que el puntero que devuelve malloc, por si acaso.

    – Eyal

    12 de mayo de 2009 a las 12:05

  • Buen punto, Eyal. No importa cuántas veces me queme con la desalineación (generalmente con estructuras), todavía tiendo a olvidarlo 🙂

    – Makis

    12 de mayo de 2009 a las 12:42

  • El tamaño probablemente ya esté allí, emita el puntero devuelto a (int *) y verifique el valor en ptr[-1] o ptr[-2]. Tenga en cuenta que el tamaño asignado se puede redondear hacia arriba para mantener la alineación, por lo que una solicitud de un búfer de 123 bytes puede devolver 128 bytes, por lo tanto verifique en consecuencia.

    – TMN

    12 de mayo de 2009 a las 17:44

avatar de usuario
muviciel

O puede tener acceso a las tablas internas utilizadas por malloc/free (consulte esta pregunta: ¿Dónde almacenan malloc() / free() los tamaños y las direcciones asignados? para obtener algunas sugerencias), o debe administrar sus propias tablas en sus contenedores.

Siempre puedes usar Valgrind en lugar de rodar su propia implementación. Si no le importa la cantidad de memoria que asigna, podría usar una implementación aún más simple: (Hice esto muy rápido, por lo que podría haber errores y me doy cuenta de que no es la implementación más eficiente. El pAllocedStorage debe recibir una tamaño inicial y aumentar por algún factor para un cambio de tamaño, etc. pero se entiende la idea).

EDITAR: Me perdí que esto era para ARM, que yo sepa, valgrind no está disponible en ARM, por lo que podría no ser una opción.

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;
}

yo usaría rmalloc. Es una biblioteca simple (en realidad son solo dos archivos) para depurar el uso de la memoria, pero también tiene soporte para estadísticas. Dado que ya tiene funciones de envoltorio, debería ser muy fácil usar rmalloc para ello. Tenga en cuenta que también necesita reemplazar strdup, etc.

Es posible que su programa también necesite interceptar realloc(), calloc(), getcwd() (ya que puede asignar memoria cuando el búfer es NULL en algunas implementaciones) y tal vez strdup() o una función similar, si es compatible con su compilador

  • Si anulas malloc() (y realloc(), calloc(), alloca() y free()), obtienes los demás gratis (por así decirlo), ya que todos llaman a malloc() internamente.

    – TMN

    12 de mayo de 2009 a las 17:49

  • mi programa solo usa MemAlloc y MemFree, por lo que es suficiente para este propósito interceptar solo malloc y free.

    – Duda

    13 mayo 2009 a las 15:42

avatar de usuario
norman ramsey

Si estás corriendo x86 podrías simplemente ejecuta tu binario bajo Valgrind y recopilaría toda esta información para usted, utilizando la implementación estándar de malloc y free. Sencillo.

  • Si anulas malloc() (y realloc(), calloc(), alloca() y free()), obtienes los demás gratis (por así decirlo), ya que todos llaman a malloc() internamente.

    – TMN

    12 de mayo de 2009 a las 17:49

  • mi programa solo usa MemAlloc y MemFree, por lo que es suficiente para este propósito interceptar solo malloc y free.

    – Duda

    13 mayo 2009 a las 15:42

avatar de usuario
cptstubing06

He estado probando algunas de las mismas técnicas mencionadas en esta página y terminé aquí después de una búsqueda en Google. Sé que esta pregunta es antigua, pero quería agregar para el registro…

1) ¿Su sistema operativo no proporciona ninguna herramienta para ver cuánta memoria en montón está en uso en un proceso en ejecución? Veo que estás hablando de ARM, por lo que este podría ser el caso. En la mayoría de los sistemas operativos completos, esto es solo una cuestión de usar una herramienta de línea cmd para ver el tamaño del montón.

2) Si está disponible en su libc, sbrk(0) en la mayoría de las plataformas le indicará la dirección final de su segmento de datos. Si lo tiene, todo lo que necesita hacer es almacenar esa dirección al comienzo de su programa (digamos, startBrk=sbrk(0)), luego, en cualquier momento, su tamaño asignado es sbrk(0) – startBrk.

3) Si se pueden usar objetos compartidos, está vinculando dinámicamente a su libc y el cargador de tiempo de ejecución de su sistema operativo tiene algo así como una variable de entorno LD_PRELOAD, puede que le resulte más útil crear su propio objeto compartido que define las funciones reales de libc con los mismos símbolos (malloc(), no MemAlloc()), luego haga que el cargador cargue su lib primero e “interponga” las funciones libc. Puede obtener además las direcciones de las funciones libc reales con dlsym() y el indicador RTLD_NEXT para que pueda hacer lo que está haciendo anteriormente sin tener que volver a compilar todo su código para usar sus envoltorios malloc/free. Entonces es solo una decisión de tiempo de ejecución cuando inicia su programa (o cualquier programa que se ajuste a la descripción en la primera oración) donde establece una variable de entorno como LD_PRELOAD=mymemdebug.so y luego lo ejecuta. (google para la interposición de objetos compartidos… es una gran técnica y una utilizada por muchos depuradores/perfiladores)

  • Es un sistema operativo interno muy simple. 1) No (2) sbrk no disponible (3) objetos compartidos no disponibles.

    – Duda

    7 de agosto de 2013 a las 11:06


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad