21 votes

Désactiver l'élimination de "if(0)" dans gcc

Comment puis-je empêcher GCC d'éliminer le code à l'intérieur du bloc if(0) ?

Lorsque j'utilise Visual Studio, l'une de mes techniques de débogage consiste à mettre du code comme celui-ci dans mon programme :

if (0)
    do_some_debug_printing_and_checking();

Ensuite, lorsque j'atteins un point d'arrêt, je clique sur la ligne do_some_debug_printing_and_checking(), sélectionne "définir l'instruction suivante" et la force à s'exécuter.

Lorsque j'utilise gcc/gdb en tant que back-end, la commande "définir l'instruction suivante" ne fonctionne plus, car GCC supprime simplement le code de l'intérieur de l'instruction if(0).

Je utilise bien sûr le drapeau -O0 pour désactiver l'optimisation. J'ai également essayé les drapeaux -fno-dce -fno-tree-dce pour désactiver explicitement l'élimination de code mort, mais cela n'a aucun effet : le contenu de if(0) n'est tout simplement pas présent dans le fichier binaire et je ne peux pas utiliser définir l'instruction suivante pour y accéder.

Y a-t-il un bon moyen de dire à gcc de désactiver l'élimination du contenu de if(0) ?

Éditer :

Merci pour la solution de contournement avec une "variable supplémentaire", cependant il y a 2 choses que je n'aime pas à ce sujet :

  1. C'est toujours une ligne de code supplémentaire
  2. Cela ne sera pas automatiquement optimisé lors de la construction de la version release et je veux vraiment que ces choses de débogage disparaissent. Je peux bien sûr utiliser des #ifdef, mais cela fait encore plus de lignes supplémentaires.

Vraiment, il n'y a absolument aucune option pour dire à GCC de conserver ce code mort ?

12voto

Charles Bailey Points 244082

La chose la plus simple à faire est de faire en sorte que la vérification dépende (disons) d'une variable avec une liaison externe.

Par exemple

extern bool debug;
if (debug)
    do_some_debug_printing_and_checking();

Quelque part au niveau de l'espace de nom :

bool debug = false;

9voto

sfstewman Points 3227

Je ne me fierais pas aux indicateurs du compilateur gcc pour cela. Les indicateurs du compilateur peuvent changer d'une version de gcc à l'autre, et changent entre les compilateurs. Il se peut que vous vous retrouviez à devoir déboguer le même code dans six mois sur Visual C++...

@CharlesBailey fait une belle suggestion pour faire cela avec une variable extern. Voici une alternative qui ne nécessite pas qu'une variable soit exposée à l'ensemble du module ou conservée en stockage statique.

Déclarez une variable temporaire volatile dans la portée de l'instruction if:

if (volatile bool dbg = false)
{
  faire_des_impressions_et_des_vérifications_de_débogage();
}

Cela garde la portée de la variable temporaire assez étroite. Le qualificatif volatile ne permet pas au compilateur de supposer quoi que ce soit sur la variable, ou d'optimiser la branche.

Une chose à garder à l'esprit est que la variable est toujours allouée sur la pile et restera sur la pile jusqu'à ce que la fonction se termine. Les deux approches, celle-ci et celle avec extern, devraient fonctionner, mais ont des compromis légèrement différents (et probablement négligeables).

Si vous êtes prêt à utiliser des macros pour aider à résoudre ce problème, alors vous pouvez facilement désactiver la variable temporaire lorsque vous publiez votre code en production:

#ifndef IS_DEBUGGING
#  define IS_DEBUGGING 0
#endif

#if IS_DEBUGGING
#  define TMP_DBG_FLAG volatile bool dbg_flag = false
#else
#  define TMP_DBG_FLAG false
#endif

Ensuite, déclarez votre instruction if comme suit:

if ( TMP_DBG_FLAG )
{
  faire_des_impressions_et_des_vérifications_de_débogage();
}

Lorsque vous définissez IS_DEBUGGING à 1, la variable locale est créée, déclarée volatile et conservée. Lorsque vous définissez IS_DEBUGGING à 0, la macro se développe en la constante false et le compilateur optimise la branche. Quelque chose de très similaire pourrait être fait pour l'approche avec extern.

Cela ajoute quelques lignes de code supplémentaires, mais elles sont indépendantes du nombre de fois que vous utilisez TMP_DBG_FLAG. Le code est également beaucoup plus lisible que d'utiliser des tonnes de ifdef. La macro pourrait être rendue un peu plus sûre (en ajoutant la valeur de __LINE__ à celle-ci), mais cela nécessiterait trois macros, et n'est probablement pas nécessaire:

#if IS_DEBUGGING
// colle les symboles 'x' et 'y' ensemble
#  define TMP_DBG_FLAG_SYMCAT0(x,y) x ## y

// besoin d'un niveau d'indirection pour développer __LINE__...
#  define TMP_DBG_FLAG_SYMCAT(x,y) TMP_DBG_FLAG_SYMCAT0(x,y)

#  define TMP_DBG_FLAG volatile bool TMP_DBG_FLAG_SYMCAT(dbg_flag_,__LINE__) = false
#else
#  define TMP_DBG_FLAG false
#endif

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X