Générer des symboles de Debug pour les builds en Release, avec Visual C++ 6 et 2003

Ce document est à la fois un pense-bête de paramétrages pour les options de compilations, et une petite explication sur chacune. Je n'explique malheureusement pas tellement ce que l'on peut faire avec. J'essaierais d'écrire d'autre documents à l'avenir, mais pour ce genre d'information, vous devrez vous pour l'instant vous contenter de chercher ailleurs.

Dans la phase de compilation et link, des symboles de debug peuvent être généré. Avec Visual C++ 6 et 2003, ils vont dans des fichiers .pdb. Pour les configuration Debug tout est automatiquement mis par les environnements de développement, mais pas pour les configurations Release. Alors bien sûr ce n'est pas indispensable pour tout, mais ça l'est pour une grande quantité de cas, comme par exemple les suivants :

Vous pouvez aussi générer des fichiers .map. Ils sont, eux, en mode texte, et représentent la cartographie de votre exécutable (ou dll). Lorsqu'ils sont généré de façon complète, ils permettent :

Remarque importante : Les paramétrages dont je parle ici ne changent pas les performances : ils permettent juste de générer ces fichiers .pdb et .map complets. Je met dans certaines remarques à part des paramètres qui, eux, pourrait les changer.

Autre Remarque importante : Il ne faut surtout pas oublier que les fichiers .pdb complets contiennent des informations détaillées sur votre code : avec un exécutable et son .pdb, on pourrait reconstruire du code C++ presque lisible. Selon votre mode de licence utilisateur, vous souhaiterez peut-être plutôt garder ces fichiers pour vous. Par contre, Visual Studio 2003 permet maintenant de générer des fichiers .pdb "allégés" ne contenant que les informations publiques. Microsoft met ces fichiers de symboles "publiques" à la disposition de tout le monde via ses serveurs de symboles (cf ma page qui indique comment les configurer).

Méthodologie générale

Versionning

Générer ces fichers .pdb/.map rentre dans une méthodologie générale de build dont je ne parle que très succinctement ici (ce n'est pas le but de ce document) :

Un petit conseil

Il s'agit juste d'un petit conseil dont vous pouvez faire ce que vous voulez. Si vous avez plusieurs projet, je vous suggère de générer leur résultat dans des répertoire communs à tout les projets. Par exemple, si dans votre arborescence, vous avez un premier répertoire, puis un répertoire par projet, configurez les projets pour qu'ils génèrent leur résultat dans un répertoire comme ..\BinDebug pour la version Debug, et ..\BinRelease pour la version Release.
Pour ce faire, c'est quasiment la même explication avec Visual Studio 6 et 2003 : il vous suffit d'aller dans les settings (ou properties pour VS 2003) de votre projet, et de mettre dans l'onglet general et la zone Output Files la valeur souhaitée, et ce, dans chaque configuration (release, debug...).

Avec Visual C++ 6

Avant tout, un peu de ménage si vous êtes dans un projet ATL

Ce n'est pas vraiment le sujet de ce texte, mais on pourrait l'inclure dans une méthodologie plus générale de build. Si vous êtes dans un projet ATL, le Wizard vous a généré tout une tripoté de configurations :

Je vous suggère déjà d'enlever les deux configurations MinSize, car elles génèrent une dépendance à atl.dll, ce qui vous oblige à le déployer - et dans la bonne version... Bref - que des ennuis. Ensuite, si vous savez que dans votre projet, vous n'avez besoin que d'Unicode (dans les composants serveurs par exemple) ou bien que d'ANSI (dans des composants devant fonctionner sur Windows 9x/ME), enlevez celles dont vous n'avez pas besoin : elles vous gênerons plus qu'autre chose, et vous pourrez toujours les recréer en prenant exemple sur un projet généré de zéro.

Le vif du sujet

Bon, voici donc les différentes cases à cocher pour générer votre .pdb et .map, ainsi que pour quelques autre choses (précisées à chaque étape). Je met des captures d'écran aux endroits importants

Donc allez tout d'abord à la racine de votre projet, puis faite un clic droit puis settings. Dans la combobox Setting For sélectionnez toutes les configurations Release. Allez ensuite dans l'onglet C/C++, puis dans la General. Ensuite, sélectionnez Program Database dans la combobox Debug Info.
Voici une capture d'écran de ce que vous pourriez avoir :



onglet C/C++


Petite remarque au passage : ceci peut par contre changer les performances, contrairement à ma remarque du début. Je vous suggère de mettre l'option d'optimisation Minimize Size, plutôt que Maximize Speed. En effet, la littérature et les faits indiquent :

(fin de la Petite remarque au passage...)

Ensuite, dans l'onglet Link, dans la catégorie General, cochez les cases Generate debug info et Generate map file.
Voici une capture d'écran de ce que vous pourriez avoir :



onglet link - General


Si vous avez utilisé mon petit conseil ci-dessus, le .map sera malheureusement généré dans le mauvais répertoire. Restez donc dans l'onglet Link, et allez dans la catégorie Debug pour mettre dans la zone Mapfile name le bon répertoire : par exemple, ..\BinRelease\.....
Voici une capture d'écran de ce que vous pourriez avoir :



onglet link - Debug


Il y a ensuite des options supplémentaires à rajouter à la main, toujours dans l'onglet Link, dans la catégorie General, dans la zone Project options. Rajouter alors les quatres options /OPT:REF /MAPINFO:EXPORTS /MAPINFO:LINES /RELEASE. Pour la première, lisez la documentation pour être sûr qu'elle convient à votre projet (vous avez peut-être des pragmas bizarres qui pourraient le contre- indiquer, mais je ne vois pas bien dans quel cas). Les options commençant par MAP servent à avoir un fichier map complet (avec même les numéros de lignes !!!). L'option /RELEASE sert à générer un checksum dans votre fichier exécutable. Si vous ne le mettez pas, vous aurez un warning à chaque fois que vous ouvrirez votre exécutable (ou un crash dump) via les outils de debugging (en tout cas au moins sous WinDBG). En plus, ce warning pourrait être justifié si vous avez fait des mélanges entre vos .exe/.dll et vos .pdb.
Voici une capture d'écran de ce que vous pourriez avoir :



onglet link - General - Project Options


Le point suivant concerne les dll, et à vrai dire, plutôt les composants serveurs, car dans ces environnements, on connait toutes les dll chargées par les processus.
Donc dans les dll, je vous suggère de choisir une adresse de base par défaut, et surtout, d'en choisir une différente pour toutes vos dll chargées. Ceci a principalement deux avantages. Tout d'abord, le chargement d'une dll par windows est plus rapide si celui-ci n'a pas besoin de la reloger (cherchez ce terme sur google pour plus d'explications : en gros, ajouter une valeur à tout les endroits du code et des données dépendant de l'adresse de chargement). Le deuxième avantage est que si vous avez un crash dans lequel vous ne connaissez que l'adresse ou que vous n'avez qu'un fichier drwtsn32.log (et pire, s'il généré sous Windows 2000 car ceux de Windows XP/2003 sont plus verbeux) pour vous donner des adresses, et seulement ça, vous n'aurez pas à lancer les dés pour savoir quelle dll est fautive.
Donc pour choisir cette adresse de chargement, restez dans l'onglet Link, dans la catégorie output, et mettez une adresse dans Base address. Vous devriez ensuite vérifier par la suite, sur une machine ressemblant à votre machine cible "typique", si votre dll n'est pas relogée, via, par exemple, l'outil Process Explorer de Sysinternals.
Voici une capture d'écran que vous pourriez avoir :



onglet link - Output - Base address


Avec Visual C++ 2003

Visual C++ 2003 est bien plus arrangeant que le 6 : beaucoup d'options sont déjà bien mises, et il ne faut pas mettre d'option à la main, dans la ligne de commande. Je repasse dessus en supposant que vous avez lu le paragraphe sur Visual C++ 6 : mais je renvoie dessus quand j'y fait expressément référence. Donc tout d'abord, il faut aller dans les properties, puis choisir comme configuration courante la Release. Ensuite, dans la rubrique C/C++, puis General, il faut s'assurer que l'option Debug Information Format est à Program Database (/Zi).
Voici une capture d'écran que vous pourriez avoir :



rubrique C/C++, General


Ensuite, toujours dans la rubrique dans la sous rubrique C/C++, puis dans optimisation, il est parfois conseillé de changer le mode d'optimisation - je fais ici une remarque semblable à celle de Visual C++ 6, mais adaptée à Visual C++ 2003 :

Remarque : ceci peut par contre changer les performances, contrairement à ma remarque du début. Il est parfois conseillé de mettre l'option d'optimisation Minimize Size (/O1), plutôt que Maximize Speed (/O2). En effet, la litérature et les faits indiquent :


Dans le cas où vous suivez cette remarque, voici une capture d'écran que vous pourriez avoir :



rubrique C/C++, Optimization


Ensuite, dans la rubrique Linker, puis Debugging, il faut mettre trois option à Yes : Generate Debug Info, Generate Map File, Map Exports, et Map Lines. Avec cette version, vous pouvez aussi générer un fichier .pdb allégé, ne contenant que des symboles publiques. En effet, si vous souhaitez distribuer vos .pdb, il ne faut pas oublier qu'ils contiennent des informations très détaillées sur votre code : avec un exécutable et son .pdb, on pourrait reconstruire du code C++ presque lisible. Vous pouvez donc ici utiliser l'option Strip Private Symbols, pour générer un .pdb "light", qui contient moins d'informations.
Voici une capture d'écran que vous pourriez avoir :



rubrique Linker, Debugging


Ensuite, toujours dans la rubrique Linker, dans Optimization, il faut vérifier que les options Reference et Enable COMDAT Folding sont respectivement à Eliminate Unreferenced Data (/OPT:REF) et Remove Redundant COMDATs (/OPT:ICF) cf les documentations, et aussi la remarque que je met ci-dessus pour Visual C++ 6.
Voici une capture d'écran que vous pourriez avoir :



rubrique Linker, Optimization


Et pour la même raison que le /RELEASE mis à la main pour Visual C++ 6 ci-dessus, il faut, ici, dans Visual C++ 2003, cocher la case Set Checksum, dans la rubrique Linker puis Advanced.
Voici une capture d'écran que vous pourriez avoir :



rubrique Linker, Advanced


Le point suivant concerne les dll, et je vais vous renvoyer à l'explication plus détaillée que je donne ci-dessus. Pour rappel, il est préférable de choisir une adresse de base par défaut pour toutes vos dll, et bien sûr, d'en choisir une différente pour chacune d'entre-elles. Pour choisir l'adresse par défaut, il faut la fixer dans Base address.
Voici une capture d'écran que vous pourriez avoir (avec ici une adresse à 0x2B000000 pour cette dll - et je le rappelle, uniquement pour elle) :



rubrique Linker, Advanced, Base Address


Et après ?

Ensuite, si vous ne l'avez pas déjà fait, je vous invite à configurer vos outils de développement pour qu'ils téléchargent automatiquement les symboles publiques de Windows chez Microsoft : pour cela, allez sur ma page décrivant comment faire.

Sinon, pour l'instant j'ai fait ce document comme un pense-bête, mais avec quand même par mal d'explications. Ensuite, on peut faire beaucoup de choses dont j'évoque certaines ci-dessus (sans tellement les expliquer : j'essaierais de faire de nouveaux documents orientés dans ces directions). Dans l'immédiat, je vous renvoie plutôt aux liens cités, et aussi à google pour faire le reste.

Je voudrais tout de même citer un Livre qui pourrais vous intéresser : il s'agit de "Debugging Applications for Microsoft .Net and Microsoft Windows", de John Robbins. Vous Pouvez le trouver sur Amazon.fr et sûrement aussi dans des librairies spécialisées.

pour m'écrire
retour page informatique
retour page principale