dimanche 23 avril 2017

Détection de memory leaks avec Microsoft Visual Studio 2015 (C++)

Cette semaine je cherchais un moyen de vérifier si le projet sur lequel je travail contient des "memory leaks".

Après une petite recherche, je suis tombé sur la page "CRT Debugging Techniques" du site MSDN (https://msdn.microsoft.com/en-us/library/zh712wwf.aspx).
La page contient une section qui se nomme "Finding Memory Leaks using the CRT Library".

Qu'est que la CRT Library? En français, la bibliothèque runtime C (CRT) est la partie de la bibliothèque C++ standard qui incorpore la bibliothèque ISO C99 standard. Cette bibliothèque contient entres autres plusieurs macros/fonctions permettant d'aider au débogage de code natif. Il suffit d'inclure dans votre code quelques macros et l'appel d'une fonction pour vérifier à la fin de l'exécution si votre code contient des memory leaks.

Prenons un code assez simple en exemple :
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string* chaineTresLongue = new string("blablabla...");
 
    string* cloneChaine = new string(*chaineTresLongue);
    delete cloneChaine;

    return 0;
}


Lorsqu'on exécute de code en mode debug l'application se termine normalement. Aucune fuite de mémoire à l'horizon.
Sortie de la fenêtre Output module Debug
Il y a pourtant une fuite de mémoire car nous n'avons jamais supprimer le pointeur de chaîne de caractères chaineTresLongue. Bon évidemment dans un petit projet comme celui là nous aurions pu détecter la fuite de mémoire rapidement à l’œil mais dans un gros projet qui contient des milliers de lignes de code, c'est beaucoup plus difficile.

Pour activer la détection il suffit d'inclure le fichier d'entête crtdbg.h et d'appeler la fonction _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); au début de la fonction principale du programme.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <iostream>
#include <string>
#include <crtdbg.h>

using namespace std;

int main()
{
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    string* chaineTresLongue = new string("blablabla...");
    string* cloneChaine = new string(*chaineTresLongue);

    delete cloneChaine;
    return 0;
}

On voit maintenant qu'un memory leak a bel et bien été détecté.
Sortie de la fenêtre Output module Debug
Petit détail par contre, il serait pratique de connaitre le numéro de la ligne d'où l'espace mémoire non libéré a été créé pour la première fois. Pour ce faire, il suffit d'ajouter quelques macros sur le fichier source principal.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <string>
#include <crtdbg.h>
#ifdef _DEBUG
#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
#define new DEBUG_NEW
#endif

using namespace std;

int main()
{
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    string* chaineTresLongue = new string("blablabla...");
    string* cloneChaine = new string(*chaineTresLongue);

    delete cloneChaine;
    return 0;
}

Voilà! Un autre outil aider à créer du code natif sans fuite de mémoire.

Sortie de la fenêtre Output module Debug