2009-11-10 19 views
8

¿Cuál es la mejor manera de probar las rutas de código de unidades que implican un error malloc()? En la mayoría de los casos, es probable que no importa porque estás haciendo algo así comoPruebas unitarias para malloc()

thingy *my_thingy = malloc(sizeof(thingy)); 
if (my_thingy == NULL) { 
    fprintf(stderr, "We're so screwed!\n"); 
    exit(EXIT_FAILURE); 
} 

pero en algunas ocasiones puede tener opciones diferentes de morir, debido a que ha asignado algún material extra para almacenar en caché o lo que sea, y usted puede reclamar esa memoria

Sin embargo, en aquellos casos en los que puede intentar recuperarse de un error malloc(), está haciendo algo complicado y propenso a errores en una ruta de código que es bastante inusual, lo que hace que las pruebas sean especialmente importantes. ¿Cómo haces para hacer esto?

+2

Se podría secuestrar 'malloc()' y hacerlo volver 0 veces. –

+2

Muchas funciones de biblioteca como 'printf' pueden fallar cuando el proceso se queda sin memoria. – ephemient

+0

@ephemient Lo que está bien, si 'fprintf()' maneja eso correctamente. ;-) –

Respuesta

15

Vi una solución genial a este problema que me presentó S. Paavolainen. La idea es reemplazar el estándar malloc(), lo que se puede hacer casi en el enlazador, por un asignador de costumbre que

  1. lee la pila de ejecución actual del subproceso llamando malloc()
  2. comprueba si existe la pila en una base de datos que se almacena en el disco duro
    1. si la pila no existe, añade la pila a la base de datos y devuelve NULL
    2. si la pila ya existía, normalmente asigna memoria y devuelve

A continuación, sólo hacer funcionar su unidad de prueba muchas veces: este sistema enumera automáticamente a través de diferentes rutas de control a malloc() fracaso y es mucho más eficiente y fiable que, por ejemplo, pruebas aleatorias.

+0

_Nice_ answer. No confía en la posibilidad de encontrar problemas y le permite evaluar sistemáticamente las consecuencias de la falla de asignación. – quark

+1

+1 para una cobertura completa sobre pruebas aleatorias. SQLite hace algo similar a este http://www.sqlite.org/malloc.html#testing. las fallas malloc() se desencadenan usando un contador en lugar de verificar la singularidad de la pila. –

+0

La teoría sobre esto es sólida y me gustaría implementar esto. Su respuesta realmente no tenía detalles de implementación y una búsqueda de S. Paavolainen malloc tampoco mencionó nada. ¿Sería posible proporcionar algunos detalles de implementación? –

1

En FreeBSD una vez simplemente sobrecargué la biblioteca C del módulo malloc.o (los símbolos eran débiles) y reemplacé la implementación malloc() por una que tenía probabilidad controlada de fallar. Me vinculé estáticamente y comencé a realizar pruebas. srandom() terminó la imagen con una secuencia pseudoaleatoria controlada.

También busque here para un conjunto de buenas herramientas que parece necesitar según mi opinión. Al menos sobrecargan malloc()/free() para rastrear las fugas, así que parece que es un punto utilizable agregar todo lo que desee.

1

Esto es un poco asqueroso, pero si realmente quiere la unidad de pruebas, que podría hacerlo con #ifdefs:

thingy *my_thingy = malloc(sizeof(thingy)); 
#ifdef MALLOC_UNIT_TEST_1 
my_thingy = NULL; 
#endif 
if (my_thingy == NULL) { 
    fprintf(stderr, "We're so screwed!\n"); 
    exit(EXIT_FAILURE); 
} 

Por desgracia, tendría que volver a compilar mucho con esta solución.

Si está utilizando linux, también podría considerar ejecutar su código bajo presión de memoria utilizando ulimit, pero tenga cuidado.

2

Sugiero crear una función específica para su código especial malloc que espera que pueda fallar y que pueda manejar con elegancia. Por ejemplo:

void* special_malloc(size_t bytes) { 
    void* ptr = malloc(bytes); 
    if(ptr == NULL) { 
    /* Do something crafty */ 
    } else { 
    return ptr; 
    } 
} 

entonces se podría unidad de prueba de este negocio astuto aquí pasando en algunos malos valores de bytes. Puede poner esto en una biblioteca separada y hacer una biblioteca simulada que se comporta de manera especial para su prueba de las funciones que llaman a esta.

+2

/* Haz algo astuto */ /* ???? */ /* ¡GANANCIA! */ – Derek

2

escribir su propia biblioteca que implementa malloc por no haber o llamando al malloc reales al azar (ya sea estáticamente vinculado o explícitamente dlopened)

continuación LD_PRELOAD que

+0

Terminé yendo con este método. Se necesitan algunos indicadores de compilación adicionales para usar con mi marco de prueba, pero es muy flexible. Además, en mi biblioteca de objetos compartidos en lugar de tener malloc aleatoriamente fallido, declaré un valor global que haría que malloc fallara cuando lo especificara. Esa variable tenía que declararse como 'extern' en mi código de prueba. –

1

Usted podría secuestrar malloc mediante el uso de algunos define y parámetro global de controlarlo ... Es un poco hackish pero parece funcionar.

#include <stdio.h> 
#include <stdlib.h> 

#define malloc(x) fake_malloc(x) 

struct { 
    size_t last_request; 
    int should_fail; 
    void *(*real_malloc)(size_t); 
} fake_malloc_params; 

void *fake_malloc(size_t size) { 
    fake_malloc_params.last_request = size; 
    if (fake_malloc_params.should_fail) { 
    return NULL; 
    } 
    return (fake_malloc_params.real_malloc)(size);; 
} 

int main(void) { 
    fake_malloc_params.real_malloc = malloc; 
    void *ptr = NULL; 
    ptr = malloc(1); 
    printf("last: %d\n", (int) fake_malloc_params.last_request); 
    printf("ptr: 0x%p\n", ptr); 
    return 0; 
}