Partager via


Cet article a fait l'objet d'une traduction automatique.

Instrumentation d'application

Analyse d’application avec Pin

Hadi Brais

Analyse des programmes est une étape fondamentale dans le processus de développement. Il s'agit d'analyser un programme afin de déterminer comment il se comportera en cours d'exécution. Il existe deux types d'analyse de programme : statiques et dynamiques.

Vous devez exécuter une analyse statique sans exécuter le programme cible, généralement pendant la compilation de code source. Visual Studio fournit un certain nombre d'excellents outils pour l'analyse statique. Compilateurs plus modernes exécutent automatiquement une analyse statique pour s'assurer que le programme respecte les règles sémantiques de la langue et à optimiser en toute sécurité le code. Bien que l'analyse statique n'est pas toujours précise, son principal avantage est soulignant les problèmes potentiels avec le code avant de l'exécuter, réduisant le nombre de sessions de débogage et de gain de temps précieux.

Vous devez exécuter une analyse dynamique de programme lors de l'exécution du programme cible. Lorsque le programme se termine, l'analyseur dynamique produit un profil avec des informations comportementales. Dans Microsoft .NET Framework, le juste-à-temps (compilateur JIT) effectue une analyse dynamique au moment de l'exécution pour optimiser le code et fait en sorte qu'il ne fera rien qui porte atteinte au système de type.

Le principal avantage de l'analyse statique au cours de l'analyse dynamique est qu'elle assure la couverture de code 100 pour cent. Pour assurer cette couverture de code hautement avec analyse dynamique, vous devez exécuter le programme plusieurs fois, chaque fois avec une entrée différente si l'analyse prend des chemins différents. Le principal avantage de l'analyse dynamique est qu'elle peut produire des renseignements détaillés et exacts. Lorsque vous développez et exécutez une application .NET ou une application C++ sécurisée, les deux types d'analyses seront effectués automatiquement sous le capot pour s'assurer que le code respecte les règles du cadre.

L'accent dans cet article sera mis sur l'analyse des programmes dynamiques, également connu sous le nom de profilage. Il existe de nombreuses façons pour profiler un programme, par exemple en utilisant les événements d'infrastructure, crochets de l'OS et l'instrumentation dynamique. Visual Studio fournit un cadre de profilage, ses capacités d'instrumentation dynamique sont actuellement limitées. Pour tous, mais les scénarios plus simples de l'instrumentation dynamique, vous aurez besoin d'un cadre le plus avancé. C'est où le Pin entre en jeu.

Quel est le code Pin ?

Broche est un framework d'instrumentation binaire dynamique développé par Intel Corp. qui vous permet de créer des outils d'analyse de programme appelés Pintools pour les plates-formes Windows et Linux. Vous pouvez utiliser ces outils pour surveiller et enregistrer le comportement d'un programme pendant son exécution. Ensuite vous pouvez évaluer efficacement de nombreux aspects importants du programme tels que sa rectitude, de performance et de sécurité.

Vous pouvez intégrer le cadre Pin avec Microsoft Visual Studio pour facilement créer et déboguer des Pintools. Dans cet article, je vais montrer comment utiliser la broche avec Visual Studio pour développer et déboguer un Pintool simple mais utile. Le Pintool permet de détecter les problèmes de mémoire critiques tels que les fuites de mémoire et libérer double alloué la mémoire dans un programme C/C++.

Pour mieux comprendre la nature du Pin, regardez la définition complète à terme par terme :

  • Un cadre est une collection de code sur lequel vous écrivez un programme. Typiquement, il comprend un composant runtime qui contrôle partiellement l'exécution du programme (comme le démarrage et arrêt).
  • L'instrumentation est le processus d'analyse d'un programme en ajoutant ou en modifiant le code — ou les deux.
  • Binary indique le code ajouté ou modifié est code machine sous forme binaire.
  • Dynamique indique les processus d'instrumentation sont effectuées au moment de l'exécution, pendant l'exécution du programme.

L'expression complète « instrumentation binaire dynamique » est une bouchée, alors les gens utilisent habituellement l'acronyme DBI. La broche est un cadre DBI.

Vous pouvez utiliser le code Pin sur Windows (IA32 et Intel64), Linux (IA32 et Intel64), Mac OS X (IA32 et Intel64) et Android (IA32). Broche prend également en charge le microprocesseur Intel Xeon Phi pour les supercalculateurs. Non seulement il prend en charge Windows, mais également s'intègre avec le Visual Studio. Vous pouvez écrire Pintools en Visual Studio et déboguer avec le débogueur de Visual Studio . Vous pouvez même développer des extensions de débogage pour broche à utiliser en toute transparence de Visual Studio.

S'initier à la broche

Bien que le Pin est un logiciel propriétaire, vous pouvez télécharger et utiliser gratuitement pour usage non-commercial. Broche n'est pas encore en charge Visual Studio 2013, donc je vais l'utiliser Visual Studio 2012. Si vous avez installé les deux Visual Studio 2012 et 2013, vous pouvez créer et ouvrir des projets Visual Studio 2012 2013 et utiliser les bibliothèques C++ et les outils de Visual Studio 2012 à partir de 2013.

Télécharger Pin de intel.ly/1ysiBs4. Outre la documentation et les fichiers binaires, Pin inclut le code source pour une grande collection d'échantillon Pintools que vous trouverez dans outils/source. Dans le dossier MyPinTool, ouvrez la solution MyPinTool dans Visual Studio.

Examinez les propriétés du projet dans le détail afin de déterminer la configuration correcte de la Pintool. Tous les Pintools sont des fichiers DLL. Par conséquent, le Type de Configuration de projet doit être sur la bibliothèque dynamique (.dll). Vous devrez également spécifier tous les en-têtes, les fichiers, bibliothèques et un certain nombre de symboles de préprocesseur requis par les fichiers d'en-tête de Pin. La valeur du point d'entrée Ptrace_DllMainCRTStartup % 4012 pour initialiser correctement le runtime C. Spécifiez l'option /export:main pour importer la fonction principale.

Vous pouvez soit utiliser le projet de MyPinTool correctement configuré ou créez un nouveau projet et le configurer vous-même. Vous pouvez également créer une feuille de propriétés contenant les détails de la configuration requise et qu'importer dans votre projet de Pintool.

Granularité de la broche

NIP vous permet d'insérer du code dans des endroits spécifiques dans le programme que vous êtes instrumentation — généralement juste avant ou après l'exécution d'une fonction ou une instruction particulière. Par exemple, vous pouvez enregistrer toutes les allocations de mémoire dynamique pour détecter les fuites de mémoire.

Il y a trois principaux niveaux de granularité de Pin : routine, instruction et image. La broche est aussi un niveau n'est pas si évident — granularité de trace. Une trace est une séquence d'instructions linéaire avec une seule entrée. Il se termine généralement par un branchement inconditionnel. Une trace peut inclure plusieurs points de sortie tant qu'ils sont conditionnelles. Exemples de branches inconditionnelles des appels, des retours et des sauts inconditionnels. Notez qu'une trace a point exactement une seule entrée. Si le NIP a détecté une branche vers un emplacement dans une trace, il terminera cette trace à cet endroit et commencer une nouvelle trace.

Broche offre des niveaux de granularité ces instruments pour vous aider à choisir le compromis approprié entre performance et niveau de détail. Instrumentation au niveau de l'instruction pourrait aboutir à la dégradation des performances grave, car il pourrait y avoir des milliards d'instructions. En revanche, l'instrumentation au niveau du pourrait être trop générale et, par conséquent, il pourrait augmenter la complexité de l'analyse de code. Traces aident vous instrument sans compromettre les performances ou les détails.

Écrire un Pintool

Maintenant il est temps d'écrire un Pintool utile. Le but de cet exemple de Pintool est de détecter les problèmes de désallocation de mémoire communs aux programmes C/C++. Le Pintool simple, que je vais écrire peut diagnostiquer un programme existant sans avoir à modifier le code source ou recompiler, parce que le Pin effectue ses travaux en cours d'exécution. Voici les problèmes de que la Pintool permet de détecter :

  • Fuites de mémoire : Mémoire allouée, mais pas libéré.
  • Libération double : Mémoire libérée plus d'une fois.
  • Libération de mémoire non alloué : Désallocation de mémoire qui n'a pas été alloué (par exemple, appeler gratuitement et en passant NULL pour elle).

Pour simplifier le code, je supposerai que ce qui suit :

  • La fonction principale du programme est appelée principale. Je ne considère les autres variantes.
  • Les seules fonctions qu'allouer et libérer de la mémoire sont de nouveau/malloc et supprimer/libre, respectivement. Je ne considère calloc et realloc, par exemple.
  • Le programme se compose d'un fichier exécutable.

Une fois que vous comprenez le code, vous pouvez le modifier et rendre l'outil beaucoup plus pratique.

Définir la Solution

Pour détecter ces problèmes de mémoire, la Pintool doit surveiller les appels à des fonctions d'allocation et la désallocation. Parce que l'opérateur new appelle malloc en interne, et l'opérateur delete appelle gratuit en interne, je peux juste surveiller les appels à malloc et free.

Chaque fois que le programme appelle malloc, je vais enregistrer l'adresse retournée (l'adresse de la région de la mémoire allouée ou NULL). Chaque fois qu'il appelle gratuit, je vais correspondre à l'adresse de la mémoire libérée avec mes dossiers. Si elle a été allouée, mais ne pas libéré, je vais marquer comme libéré. Toutefois, si elle a été allouée et libérée, qui serait une tentative pour le libérer à nouveau, ce qui indique un problème. Enfin, si il n'y a aucune trace de la mémoire libérée a été allouée, ce serait une tentative pour libérer la mémoire non allouée. Lorsque le programme se termine, je vais vérifier à nouveau les enregistrements pour les régions de mémoire qui ont été attribuées, mais ne pas libérées pour détecter les fuites de mémoire.

Choisissez une granularité

Goupille peut instrumenter un programme à quatre niveaux de granularité : image, routine, trace et instruction. Quel est le meilleur pour cette Pintool ? Alors que tout les niveaux de granularité va faire le travail, j'ai besoin de choisir celui qui subit la charge moins de performances. Dans ce cas, la granularité de l'image serait le meilleur. Après le chargement de l'image du programme, le Pintool peut localiser le malloc et code libre au sein de l'image et insérez le code d'analyse. De cette façon, instrumentation aérienne sera par image au lieu de, pour exmple, par instruction.

Pour utiliser l'API de code Pin, je dois inclure le code pin.Fichier d'en-tête H dans le code. Le Pintool sera écrit les résultats dans un fichier, si j'ai aussi d'inclure le fichier d'en-tête fstream. Je vais utiliser le type de carte STL pour garder une trace de la mémoire étant allouées et désallouées. Ce type est défini dans le fichier d'en-tête de carte. J'utiliserai également le flux de cerr pour afficher les messages informatifs :

#include "pin.H"
#include <iostream>
#include <fstream>
#include <map>

Je définirai trois symboles pour contenir les noms de la fonctions malloc, libre et principaux :

#define MALLOC "malloc"
#define FREE "free"
#define MAIN "main"

Ce sont les variables globales requises :

bool Record = false;
map<ADDRINT, bool> MallocMap;
ofstream OutFile;
string ProgramImage;
KNOB<string> OutFileName(KNOB_MODE_WRITEONCE, 
  "Pintool", "o", "memtrace.txt",
  "Memory trace file name");

La variable d'enregistrement indique si je suis à l'intérieur de la fonction principale. La variable MallocMap conserve l'état de chaque région de la mémoire allouée. Le type ADDRINT est défini par broche.H et représente une adresse mémoire. Si la valeur associée à une mémoire adressez-le vrai, elle a été libérée.

La variable ProgramImage contient le nom de l'image de programme. La dernière variable est un bouton. Il s'agit d'un commutateur de ligne de commande pour le Pintool. Broche le rend facile de définir les interrupteurs pour un Pintool. Pour chaque commutateur, définissez une variable de bouton. La chaîne de paramètre de type de modèle représente le type des valeurs qui prendra l'interrupteur. Ici, le bouton vous permet de spécifier le nom du fichier en sortie de le Pintool par l'intermédiaire de l'interrupteur « o ». La valeur par défaut est memtrace.txt.

Ensuite, je dois définir les routines d'analyse exécutées sur des points précis dans la séquence de code. J'ai besoin d'une fonction d'analyse, tel que défini dans Figure 1, appelé juste après malloc retourne pour enregistrer l'adresse de la mémoire allouée. Cette fonction prend l'adresse retournée par malloc et retourne nothing.

Figure 1 la Routine RecordMalloc analyse appelée chaque fois que Malloc retourne

VOID RecordMalloc(ADDRINT addr) {
  if (!Record) return;
  if (addr == NULL) {
    cerr << "Heap full!";
    return;
  }
  map<ADDRINT, bool>::iterator it = MallocMap.find(addr);
  if (it != MallocMap.end()) {
    if (it->second) {
      // Allocating a previously allocated and freed memory.
      it->second = false;
    }
    else {
      // Malloc should not allocate memory that has
      // already been allocated but not freed.
      cerr << "Imposible!" << endl;
    }
  }
  else {
    // First time allocating at this address.
    MallocMap.insert(pair<ADDRINT, bool>(addr, false));
  }
}

Cette fonction sera appelée chaque fois que malloc est appelée. Cependant, je suis seulement intéressé dans la mémoire s'il fait partie du programme instrumenté. Donc je vais enregistrer l'adresse uniquement lorsque l'enregistrement a la valeur TRUE. Si l'adresse est NULL, je vais simplement l'ignorer.

La fonction détermine ensuite si l'adresse est déjà en MallocMap. Si c'est le cas, alors il a avoir été précédemment allouée et désallouée et, par conséquent, il est maintenant réutilisée. Si l'adresse n'est pas en MallocMap, je vais l'insérer avec FALSE comme la valeur indiquant qu'elle n'a pas été libérée.

Je vais définir une autre analyse systématique, montre Figure 2, que j'ai vais avoir appelé juste avant gratuite est appelée pour enregistrer l'adresse de la région de la mémoire libérée. À l'aide de MallocMap, je peux détecter facilement si la mémoire libérée a déjà été libérée, ou elle n'a pas été allouée.

Figure 2 la Routine d'analyse RecordFree

VOID RecordFree(ADDRINT addr) {
  if (!Record) return;
  map<ADDRINT, bool>::iterator it = MallocMap.find(addr);
  if (it != MallocMap.end()) {
    if (it->second) {
      // Double freeing.
      OutFile << "Object at address " << hex << addr << "
        has been freed more than once."  << endl;
    }
    else {
      it->second = true; // Mark as freed.
    }
  }
  else {
    // Freeing unallocated memory.
    OutFile << "Freeing unallocated memory at " 
      << hex << addr << "." << endl;
  }
}

Ensuite, je vais avoir besoin de deux routines d'analyse plus pour marquer l'exécution et le retour de la fonction principale :

 

VOID RecordMainBegin() {
  Record = true;
}
VOID RecordMainEnd() {
  Record = false;
}

Routines d'analyse déterminent le code d'instrumenter le programme. Je dois aussi dire Pin quand exécuter ces routines. C'est le but des routines de l'instrumentation. J'ai défini une routine d'instrumentation, comme le montre Figure 3. Cette routine est appelée chaque fois qu'une image est chargée dans le processus en cours d'exécution. Lorsque l'image de programme est chargé, je vais dire Pin pour insérer les routines d'analyse aux endroits appropriés.

Figure 3 la Routine d'Instrumentation Image

VOID Image(IMG img, VOID *v) {
  if (IMG_Name(img) == ProgramImage) {
    RTN mallocRtn = RTN_FindByName(img, MALLOC);
    if (mallocRtn.is_valid()) {
      RTN_Open(mallocRtn);
      RTN_InsertCall(mallocRtn, IPOINT_AFTER, (AFUNPTR)RecordMalloc,
        IARG_FUNCRET_EXITPOINT_VALUE,
        IARG_END);
      RTN_Close(mallocRtn);
    }
    RTN freeRtn = RTN_FindByName(img, FREE);
    if (freeRtn.is_valid()) {
      RTN_Open(freeRtn);
      RTN_InsertCall(freeRtn, IPOINT_BEFORE, (AFUNPTR)RecordFree,
        IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
        IARG_END);
      RTN_Close(freeRtn);
    }
    RTN mainRtn = RTN_FindByName(img, MAIN);
    if (mainRtn.is_valid()) {
      RTN_Open(mainRtn);
      RTN_InsertCall(mainRtn, IPOINT_BEFORE, (AFUNPTR)RecordMainBegin,
        IARG_END);
      RTN_InsertCall(mainRtn, IPOINT_AFTER, (AFUNPTR)RecordMainEnd,
        IARG_END);
      RTN_Close(mainRtn);
    }
  }
}

L'objet IMG représente l'image exécutable. Toutes les fonctions de Pin qui opèrent au niveau de l'image commencent par IMG_ *. Par exemple, nom_img renvoie le nom de l'image spécifiée. De même, toutes les fonctions de Pin qui opèrent à l'échelle systématique commencent par RTN_ *. Par exemple, RTN_FindByName accepte une image et une chaîne de style C et retourne un objet RTN représentant la routine pour laquelle je suis à la recherche. Si la routine demandée est définie dans l'image, l'objet retourné de RTN serait valide. Une fois que j'ai trouvé le malloc, libre et les principaux programmes courants, je peux insérer des routines d'analyse aux endroits appropriés à l'aide de la fonction RTN_InsertCall.

Cette fonction accepte trois arguments obligatoires, suivies d'un nombre variable d'arguments :

  • Le premier est la routine je veux instrument.
  • La seconde est une énumération de type IPOINT qui spécifie où insérer la routine d'analyse.
  • La troisième est la routine d'analyse doit être inséré.

Ensuite, je peux spécifier une liste d'arguments à passer à la routine d'analyse. Cette liste doit se terminer par IARG_END. Pour passer la valeur de retour de la fonction malloc dans la routine d'analyse, je vais préciser IARG_FUNCRET_EXITPOINT_VALUE. Pour passer l'argument de la fonction free à la routine d'analyse, je vais spécifier IARG_FUNCARG_ENTRYPOINT_VALUE suivie de l'index de l'argument de la fonction free. Toutes ces valeurs à partir de IARG_ * sont définis par l'énumération IARG_TYPE. L'appel à RTN_InsertCall doit être enveloppé par les appels à RTN_Open et RTN_Close, afin que le Pintool peut insérer les routines d'analyse.

Maintenant que j'ai défini mes routines d'analyse et instrumentation, je vais devoir définir une routine de mise au point définitive. Cela sera appelé à la cessation du programme instrumenté. Elle accepte deux arguments, l'un étant l'argument de code qui contient la valeur retournée par la fonction principale du programme. L'autre nous le verrons plus tard. J'ai utilisé une gamme axée sur les boucle for pour rendre le code plus lisible :

VOID Fini(INT32 code, VOID *v) {
  for (pair<ADDRINT, bool> p : MallocMap) {
    if (!p.second) {
      // Unfreed memory.
      OutFile << "Memory at " << hex << p.first << "
        allocated but not freed." << endl;
    }
  }
  OutFile.close();
}

Tout ce que j'ai à faire dans la routine de finalisation est d'itérer sur MallocMap et de détecter ces allocations qui n'ont pas été libérées. Le retour de Fini marque la fin du processus d'instrumentation.

La dernière partie du code est la fonction principale de le Pintool. Dans la fonction principale, PIN_Init est appelée pour imposer la broche analyser la ligne de commande pour initialiser les boutons. Parce que je suis à la recherche pour les fonctions à l'aide de leurs noms, de la broche est de charger la table de symboles de l'image de programme. Je peux faire ceci en appelant PIN_InitSymbols. La fonction IMG_AddInstrumentFunction enregistre la fonction d'instrumentation Image à être appelée chaque fois qu'une image est chargée.

En outre, la fonction de mise au point est enregistrée à l'aide de PIN_AddFiniFunction. Notez que le deuxième argument de ces fonctions est passé au paramètre v. Je peux utiliser ce paramètre pour passer des informations supplémentaires aux fonctions de l'instrumentation. Enfin, PIN_StartProgram est appelée pour démarrer le programme que je suis analysant. Cette fonction renvoie en fait jamais à la fonction principale. Une fois qu'on l'appelle, Pin prend sur tout :

int main(int argc, char *argv[]) {
  PIN_Init(argc, argv);
  ProgramImage = argv[6]; // Assume that the image name is always at index 6.
  PIN_InitSymbols();
  OutFile.open(OutFileName.Value().c_str());
  IMG_AddInstrumentFunction(Image, NULL);
  PIN_AddFiniFunction(Fini, NULL);
  PIN_StartProgram();
  return 0;
}

Assembler tous ces morceaux de code constitue un func entièrement­international Pintool.

Exécutez le Pintool

Vous devriez être en mesure de construire ce projet sans aucune erreur. Vous aurez également besoin d'un programme pour tester la Pintool. Vous pouvez utiliser le programme de test suivant :

#include <new>
void foo(char* y) {
  int *x = (int*)malloc(4);
}
int main(int argc, char* argv[]) {
  free(NULL);
  foo(new char[10]);
  return 0;
}

Clairement, ce programme souffre de deux fuites de mémoire et un appel inutile à libérer, ce qui indique un problème avec la logique du programme. Créer un autre projet qui comprend le programme de test. Générez le projet pour produire un fichier EXE.

La dernière étape pour exécuter le Pintool est d'ajouter Pin comme un outil externe à Visual Studio. Dans le menu Outils, choisissez outils externes. Une boîte de dialogue s'ouvre, comme le montre Figure 4. Cliquez sur le bouton Ajouter pour ajouter un nouvel outil externe. Le titre devrait être la goupille et la commande doit être le répertoire du fichier pin.exe. Les Arguments incluent les arguments à passer à pin.exe. Le commutateur-t spécifie le répertoire Pintool. Spécifier le programme à instrumenter après les deux traits d'Union. Cliquez sur OK et vous devriez être en mesure d'exécuter le code Pin dans le menu Outils.

Ajouter Pin à Visual Studio à l'aide de la boîte de dialogue outils externes
Figure 4 Ajouter Pin à Visual Studio à l'aide de la boîte de dialogue outils externes

Lors de l'exécution du programme, la fenêtre Sortie imprime tout ce que vous jetez dans les ruisseaux cerr et cout. Le flux de cerr imprime généralement des messages d'information de Pintool pendant l'exécution. Une fois la goupille se termine, vous pouvez afficher les résultats en ouvrant le fichier que le Pintool a créé. Par défaut, cela s'appelle memtrace.txt. Lorsque vous ouvrez le fichier, vous devriez voir quelque chose comme ceci :

Freeing unallocated memory at 0.
Memory at 9e5108 allocated but not freed.
Memory at 9e5120 allocated but not freed.

Si vous avez des programmes plus complexes qui adhèrent aux hypothèses Pintool, vous devriez l'instrument à l'aide de la Pintool, que vous pourriez trouver d'autres problèmes de mémoire dont vous étiez pas au courant.

Déboguer la Pintool

Lors du développement d'un Pintool, vous allez tomber à travers un certain nombre de bogues. Vous pouvez sans problème le déboguer avec le débogueur de Visual Studio en ajoutant le commutateur de pause_tool. La valeur de cette option spécifie le nombre de secondes que pin attendra avant d'exécuter effectivement le Pintool. Cela vous permet de joindre le débogueur Visual Studio au processus en cours d'exécution du Pintool (qui est le même que le processus exécute le programme instrumenté). Ensuite, vous pouvez déboguer votre Pintool normalement.

Le Pintool que j'ai développé ici suppose que le nom de l'image est à l'index 6 du tableau argv. Donc si vous voulez ajouter le commutateur de pause-outil, le nom de l'image sera à index 8. Vous pouvez automatiser ce processus en écrivant du code un peu plus.

Synthèse

Pour développer vos compétences, vous pouvez améliorer la Pintool donc il peut détecter d'autres types de problèmes de mémoire tels que les pointeurs qui pendent et pointeurs sauvages. Aussi, la sortie de Pintool n'est pas très utile car il n'est pas remarquer de quelle partie du code est la cause du problème. Ce serait bien d'imprimer le nom de la variable à l'origine du problème et le nom de la fonction dans laquelle la variable est déclarée. Cela vous permettrait facilement localiser et corriger le bogue dans le code source. Bien qu'il soit facile de noms des fonctions d'impression, impression des noms de variables est plus difficile en raison du manque de soutien de Pin.

Il y a beaucoup d'interactions entre le Pin, le Pintool et le programme instrumenté passe. Il est important de comprendre ces interactions lors de l'élaboration des Pintools avancées. Pour l'instant, vous devriez travailler à travers les exemples fournis avec épingle pour acquérir une meilleure compréhension de sa puissance.


Hadi Brais est un Ph.d. chercheur à l'Institut indien de technologie Delhi (IITD), recherches sur la conception de compilateur optimisant pour la technologie de mémoire de nouvelle génération. Il passe le plus clair de son temps à l'écriture de code en C / C++ c++ / C# et creuser profondément dans le CLR et le CRT. Il blogs à hadibrais.wordpress.com. Le contacter au hadi.b@live.com.

Merci aux experts techniques suivants d'avoir relu cet article : Preeti Ranjan Panda