web-dev-qa-db-fra.com

Quelles sont les raisons pour lesquelles une version Release fonctionnerait différemment d'une version Debug

J'ai un programme Visual Studio 2005 C++ qui s'exécute différemment en mode édition par rapport au mode débogage. En mode de lancement, un crash intermittent (apparent) se produit. En mode débogage, cela ne plante pas. Quelles sont les raisons pour lesquelles une version Release fonctionnerait différemment d'une version Debug?

Il convient également de mentionner que mon programme est assez complexe et utilise plusieurs bibliothèques tierces pour le traitement XML, le courtage de messages, etc.

Merci d'avance!

55
BeachRunnerFred

Survivre à la version de sortie donne un bon aperçu.

Les choses que j'ai rencontrées - la plupart sont déjà mentionnées

Initialisation variable De loin la plus courante. Dans Visual Studio, les versions de débogage initialisent explicitement la mémoire allouée à des valeurs données, voir par exemple. Valeurs de la mémoire ici. Ces valeurs sont généralement faciles à repérer, provoquent une erreur hors limites lorsqu'elles sont utilisées en tant qu'index ou une violation d'accès lorsqu'elles sont utilisées en tant que pointeur. Un booléen non initialisé est vrai, cependant, et peut causer des bogues de mémoire non initialisés qui ne sont pas détectés pendant des années.

Dans les versions Release où la mémoire n'est pas explicitement initialisée, elle ne conserve que le contenu qu'elle avait auparavant. Cela conduit à des "valeurs amusantes" et à des crashs "aléatoires", mais aussi souvent à des crashs déterministes qui nécessitent l'exécution d'une commande apparemment non liée avant la commande qui plante réellement. Cela est dû à la première commande "configuration" de l'emplacement mémoire avec des valeurs spécifiques. Lorsque les emplacements mémoire sont recyclés, la deuxième commande les considère comme des initialisations. C'est plus courant avec les variables de pile non initialisées que les tas, mais ce dernier m'est aussi arrivé.

L'initialisation de la mémoire brute peut également être différente dans une version, que vous démarriez à partir de Visual Studio (débogueur attaché) ou à partir de l'Explorateur. Cela rend le type le plus "gentil" de versions qui construisent des bogues qui n'apparaissent jamais sous le débogueur. 

Les optimisations valides viennent en deuxième position dans mon expérience. La norme C++ permet de nombreuses optimisations, ce qui peut être surprenant mais tout à fait valable, par exemple. Lorsque deux pointeurs alias le même emplacement mémoire, l'ordre d'initialisation n'est pas pris en compte ou que plusieurs threads modifient les mêmes emplacements mémoire, et que vous attendez un certain ordre dans lequel le thread B voit les modifications apportées par le thread A. Souvent, le compilateur est blâmé. celles-ci. Pas si vite, jeune yedi! - voir ci-dessous

Timing Les versions des versions ne sont pas simplement "exécutées plus rapidement", pour diverses raisons (optimisations, fonctions de journalisation fournissant un point de synchronisation des threads, code de débogage comme des assertions non exécutées, etc.). Le problème le plus souvent découvert est les conditions de concurrence, mais aussi les blocages et l’exécution simple «d’ordre différent» du code de message/timer/événement. Même s’ils posent des problèmes de synchronisation, ils peuvent être étonnamment stables entre les versions et les plates-formes, avec des reproductions qui «fonctionnent toujours, sauf sur PC 23».

Octets de garde. Les versions de débogage mettent souvent (davantage) d'octets de garde autour d'instances et d'allocations sélectionnées, afin de se protéger contre les débordements d'index et parfois les débordements. Dans les rares cas où le code repose sur des décalages ou des tailles, par ex. la sérialisation des structures brutes, elles sont différentes. 

Autres différences de code Certaines instructions - par exemple, affirme - n'évaluent rien dans les versions validées. Parfois, ils ont des effets secondaires différents. Ceci est courant avec la supercherie de macro, comme dans le classique

#ifdef DEBUG
#define Log(x) cout << #x << x << "\n";
#else 
#define Log(x)
#endif

if (foo)
  Log(x)
if (bar)
  Run();

Ce qui, dans une version validée, est évalué à if (foo && bar) Ce type d'erreur est très très rare avec le code C/C++ normal et les macros correctement écrites.

Compiler Bugs Cela n'arrive jamais. Eh bien, oui, mais la plupart de votre carrière vaut mieux que ce soit le cas. Au cours d’une décennie de travail avec VC6, j’en ai trouvé un où je suis toujours convaincu qu’il s’agissait d’un bogue du compilateur non corrigé, par rapport à des dizaines de schémas (peut-être même des centaines de cas) avec une compréhension insuffisante de l’Écriture (a.k.a. la norme). 

122
peterchen

Dans la version de débogage, les assertions et/ou les symboles de débogage sont souvent activés. Cela peut entraîner une disposition différente de la mémoire. En cas de pointeur incorrect, de débordement d'un tableau ou d'accès mémoire similaire, vous accédez dans un cas à une mémoire défectueuse critique (par exemple, un pointeur de fonction) et dans d'autres cas, à une mémoire non critique (par exemple, une chaîne de documentation est supprimée)

6
flolo

Les variables qui ne sont pas initialisées explicitement seront ou ne pourront pas être mises à zéro dans la version Release.

5
Burkhard

La version finale (espérons-le) serait plus rapide que votre version de débogage. Si vous utilisez plusieurs threads, vous risquez de voir plus d'entrelacement, ou tout simplement un thread s'exécutant plus rapidement que les autres, ce que vous n'avez peut-être pas remarqué dans la construction de débogage.

2
Eugene Yokota

Les versions Release sont généralement compilées avec l'optimisation activée dans le compilateur, contrairement aux versions debug.

Dans certaines langues ou lorsque vous utilisez plusieurs bibliothèques différentes, cela peut entraîner des plantages intermittents, notamment lorsque le niveau d'optimisation choisi est très élevé.

Je sais que c'est le cas avec le compilateur gcc C++, mais je ne suis pas sûr du compilateur de Microsoft.

2
fluffels

J'ai eu un problème similaire il n'y a pas si longtemps , qui a été causé par le traitement différent de la pile dans les versions validées. Autres choses qui peuvent différer:

  • L'allocation de mémoire est gérée différemment avec les versions de débogage dans le compilateur VS (c'est-à-dire l'écriture de 0xcc sur la mémoire effacée, etc.)
  • Déroulement de boucle et autres optimisations du compilateur
  • Allumage des pointeurs
2
Nik Reiman

Cela dépend à la fois du fournisseur du compilateur et des bibliothèques que vous compilez avec les indicateurs DEBUG. Bien que le code DEBUG ne devrait jamais affecter le code en cours d’exécution (il ne devrait avoir aucun effet secondaire), il le fait parfois.

En particulier, les variables peuvent être initialisées uniquement en mode DEBUG et laissées non initialisées en mode RELEASE. Les compilateurs LIST dans Visual Studio sont différents dans les modes DEBUG et RELEASE. L'idée est que les itérateurs sont complètement vérifiés dans DEBUG pour détecter les erreurs possibles (l'utilisation d'itérateurs invalidés, par exemple un itérateur dans un vecteur est invalidée si une insertion survient après la récupération de l'itérateur).

La même chose se produit avec les bibliothèques tierces, la première à laquelle je puisse penser est QT4, qui terminera votre programme par une assertion si un thread différent de celui qui a créé l'objet graphique effectue les opérations de dessin.

Avec tous les changements, votre empreinte de code et de mémoire sera différente dans les deux modes. Un problème de pointeur (lecture de la position d'un passage à la fin d'un tableau) peut passer non détecté si cette position est lisible.

Les assertions sont censées tuer l'application pendant DEBUG et disparaître des versions de RELEASE, je ne penserais donc pas aux assertions comme étant votre problème. Un pointeur non autorisé ou l'accès à un point après la fin seraient mes premiers suspects.

Il y a quelque temps, des problèmes d'optimisation du compilateur ont brisé le code, mais je n'ai pas lu les plaintes récemment. Il pourrait y avoir un problème d'optimisation, mais ce ne serait pas mon premier suspect.

http://www.debuginfo.com/tips/userbpntdll.html

Du fait que des octets de garde sont ajoutés dans les versions de débogage, vous pourrez peut-être accéder en toute sécurité à la mémoire en dehors des limites d'un tableau (notamment des tableaux dynamiques), mais cela entraînera une violation d'accès dans la version de publication. . Cette erreur peut passer inaperçue, provoquant un tas corrompu et éventuellement une violation d'accès dans un emplacement non lié à l'erreur d'origine.

Utilisez PageHeap (ou, si vous avez installé des outils de débogage, vous pouvez utiliser gflags) pour découvrir les bogues liés aux tas corrompus.

http://support.Microsoft.com/?id=286470

1
DCX2

D'après mon expérience, la raison la plus courante semble être que les configurations diffèrent de plus en plus par rapport aux paramètres de version/construction. Par exemple. différentes bibliothèques sont incluses ou la version de débogage a une taille de pile différente de celle de la version finale.

Pour éviter cela dans nos projets Visual Studio 2005, nous utilisons énormément les feuilles de propriétés. De cette façon, les configurations de publication et de débogage peuvent partager des paramètres communs.

0
Tobias Furuholm

Ce message, ainsi que les liens fournis, sont très utiles pour résoudre les problèmes liés aux bogues. En ajoutant à la liste ci-dessus, des différences dans les conventions d’appel peuvent également conduire à ce comportement - La génération de version n’a échoué que dans le but de l’optimisation pour moi . [étrangement, cet avertissement n'est pas pris même dans MSVC avertir de niveau 4?] 

0
FL4SOF