2009-11-10 7 views
8

Quelle est la meilleure façon de tester les unités de code impliquant un échec malloc()? Dans la plupart des cas, il n'a probablement pas d'importance parce que vous faites quelque chose commeTests unitaires pour l'échec de malloc()

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

mais dans certains cas, vous avez d'autres choix que de mourir, parce que vous avez alloué quelques trucs supplémentaires pour la mise en cache ou que ce soit, et vous peut récupérer cette mémoire. Cependant, dans les cas où vous pouvez essayer de récupérer à partir d'un malloc() qui a échoué, vous faites quelque chose de compliqué et d'erreur dans un chemin de code assez inhabituel, ce qui rend le test particulièrement important. Comment faites-vous réellement cela?

+2

Vous pourriez pirater 'malloc()' et le rendre 0 parfois. –

+2

De nombreuses fonctions de bibliothèque telles que 'printf' peuvent échouer lorsque le processus manque de mémoire. – ephemient

+0

@ephemient Que se passe-t-il si 'fprintf()' se comporte correctement? ;-) –

Répondre

15

J'ai vu une solution sympa à ce problème qui m'a été présenté par S. Paavolainen. L'idée est de remplacer l'malloc() standard, que vous pouvez le faire dans l'éditeur de liens, par un allocateur personnalisé qui

  1. lit la pile d'exécution actuelle du thread appelant malloc()
  2. vérifie si la pile existe dans une base de données qui est stocké sur le disque dur
    1. si la pile n'existe pas, ajoute la pile à la base de données et retourne NULL
    2. si la pile existait déjà, alloue de la mémoire normale et retourne

Ensuite, vous venez de lancer votre test plusieurs fois de l'unité: ce système automatiquement par différents énumère les chemins de contrôle à l'échec malloc() et est beaucoup plus efficace et plus fiable que par exemple test aléatoire.

+0

_Nice_ réponse. Ne comptez pas sur le hasard pour trouver des problèmes et vous permet de tester systématiquement les conséquences de l'échec de l'allocation. – quark

+1

+1 pour une couverture complète par test aléatoire. SQLite fait quelque chose de similaire à cela http://www.sqlite.org/malloc.html#testing. Les échecs malloc() sont déclenchés en utilisant un compteur au lieu de vérifier l'unicité de la pile. –

+0

La théorie sur ceci est solide et je voudrais l'implémenter. Votre réponse n'avait pas vraiment de détails de mise en œuvre et la recherche de S. Paavolainen malloc n'a rien apporté non plus. Serait-il possible de fournir quelques détails de mise en œuvre? –

1

Dans FreeBSD, j'ai simplement surchargé le module malloc.o de la bibliothèque C (les symboles étaient faibles) et remplacé l'implémentation de malloc() par une implémentation dont la probabilité de panne était contrôlée. Donc, j'ai lié statiquement et a commencé à effectuer des tests. srandom() a terminé l'image avec une séquence pseudo-aléatoire contrôlée.

Regardez également here pour un ensemble de bons outils dont vous semblez avoir besoin à mon avis. Au moins, ils surchargent malloc()/free() pour suivre les fuites, il semble donc utile d'ajouter tout ce que vous voulez.

1

C'est un peu brut, mais si vous voulez vraiment les tests unitaires, vous pourriez le faire aveC#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); 
} 

Malheureusement, vous auriez à recompiler beaucoup avec cette solution.

Si vous utilisez Linux, vous pouvez également envisager d'exécuter votre code sous la pression de la mémoire en utilisant ulimit, mais soyez prudent.

2

Je suggère de créer une fonction spécifique pour votre code malloc spécial qui, selon vous, pourrait échouer et que vous pourriez gérer avec élégance. Par exemple:

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

Ensuite, vous pouvez tester cette unité d'affaires rusés ici en passant dans quelques mauvaises valeurs pour les octets. Vous pouvez mettre cela dans une bibliothèque séparée et créer une maquette-bibliothèque qui se comporte de manière spéciale pour vos tests des fonctions qui appellent celle-ci.

+2

/* Faire quelque chose astucieux */ /* ???? */ /* PROFIT! */ – Derek

2

écrire votre propre bibliothèque qui implémente malloc en omettant au hasard ou en appelant le réel malloc (soit statiquement lié ou explicitement dlopened)

LD_PRELOAD il

+0

J'ai fini par aller avec cette méthode. Il faut quelques drapeaux de compilation supplémentaires à utiliser avec mon framework de test, mais c'est très flexible. De plus, dans ma bibliothèque d'objets partagés au lieu d'avoir un échec aléatoire de malloc, j'ai déclaré une valeur globale qui ferait échouer malloc quand je l'aurais spécifié. Cette variable devait être déclarée comme extern dans mon code de test. –

1

Vous pourriez pirater malloc en utilisant des définit et paramètre global à contrôle-le ... C'est un peu hack mais ça a l'air de marcher.

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