J'ai un problème de type "Schroedinger's Cat" ici - mon programme (en fait la suite de tests de mon programme, mais un programme néanmoins) plante, mais uniquement lorsqu'il est construit en mode release, et uniquement lorsqu'il est lancé à partir de la ligne de commande . Grâce au débogage de l'homme des cavernes (c'est-à-dire, des messages printf () désagréables partout), j'ai déterminé la méthode de test où le code se bloque, bien que malheureusement le crash réel semble se produire dans un destructeur, puisque les derniers messages de trace que je vois sont dans d'autres destructeurs qui s'exécutent proprement.
Lorsque j'essaye d'exécuter ce programme dans Visual Studio, il ne plante pas. Il en va de même lors du lancement à partir de WinDbg.exe. Le plantage se produit uniquement lors du lancement à partir de la ligne de commande. Cela se produit sous Windows Vista, btw, et malheureusement je n'ai pas accès à une machine XP pour le moment pour tester.
Ce serait vraiment bien si je pouvais demander à Windows d'imprimer une trace de pile, ou quelque chose d' autre que de simplement terminer le programme comme s'il s'était terminé proprement. Quelqu'un a-t-il des conseils sur la façon dont je pourrais obtenir des informations plus significatives ici et, espérons-le, corriger ce bogue?
Edit: Le problème était en effet causé par un tableau hors limites, que je décris plus en détail dans cet article . Merci à tous pour votre aide à trouver ce problème!
Réponses:
Dans 100% des cas que j'ai vus ou entendus, où un programme C ou C ++ s'exécute correctement dans le débogueur mais échoue lorsqu'il est exécuté à l'extérieur, la cause a été l'écriture au-delà de la fin d'un tableau local de fonction. (Le débogueur en met plus sur la pile, vous êtes donc moins susceptible d'écraser quelque chose d'important.)
la source
Lorsque j'ai rencontré des problèmes comme celui-ci auparavant, cela était généralement dû à l'initialisation de variables. En mode débogage, les variables et les pointeurs sont automatiquement initialisés à zéro, mais pas en mode version. Par conséquent, si vous avez un code comme celui-ci
En mode débogage, le code dans if n'est pas exécuté mais en mode release p contient une valeur indéfinie, qui est peu susceptible d'être 0, donc le code est exécuté provoquant souvent un crash.
Je vérifierais votre code pour les variables non initialisées. Cela peut également s'appliquer au contenu des tableaux.
la source
Aucune réponse jusqu'à présent n'a tenté de donner un aperçu sérieux des techniques disponibles pour le débogage des applications de version:
Les versions Release et Debug se comportent différemment pour de nombreuses raisons. Voici un excellent aperçu. Chacune de ces différences peut provoquer un bogue dans la version Release qui n'existe pas dans la version Debug.
La présence d'un débogueur peut également modifier le comportement d'un programme , à la fois pour les versions de version et de débogage. Voyez cette réponse. En bref, au moins le débogueur Visual Studio utilise automatiquement le tas de débogage lorsqu'il est attaché à un programme. Vous pouvez désactiver le tas de débogage à l'aide de la variable d'environnement _NO_DEBUG_HEAP. Vous pouvez le spécifier dans les propriétés de votre ordinateur ou dans les paramètres du projet dans Visual Studio. Cela pourrait rendre le crash reproductible avec le débogueur attaché.
Plus d'informations sur le débogage de la corruption du tas ici.
Si la solution précédente ne fonctionne pas, vous devez intercepter l'exception non gérée et attacher un débogueur post-mortem à l'instance où le crash se produit. Vous pouvez utiliser par exemple WinDbg pour cela, des détails sur les débogueurs post-mortem disponibles et leur installation sur MSDN
Vous pouvez améliorer votre code de gestion des exceptions et s'il s'agit d'une application de production, vous devez:
une. Installez un gestionnaire de terminaison personnalisé à l'aide de
std::set_terminate
Si vous souhaitez déboguer ce problème localement, vous pouvez exécuter une boucle sans fin à l'intérieur du gestionnaire de terminaison et envoyer du texte à la console pour vous avertir de l'
std::terminate
appel. Ensuite, attachez le débogueur et vérifiez la pile d'appels. Ou vous imprimez la trace de pile comme décrit dans cette réponse.Dans une application de production, vous souhaiterez peut-être envoyer un rapport d'erreur à la maison, idéalement avec un petit vidage de la mémoire qui vous permet d'analyser le problème comme décrit ici.
b. Utilisez le mécanisme de gestion des exceptions structuré de Microsoft qui vous permet de détecter les exceptions matérielles et logicielles. Voir MSDN . Vous pouvez protéger certaines parties de votre code en utilisant SEH et utiliser la même approche qu'en a) pour déboguer le problème. SEH donne plus d'informations sur l'exception qui s'est produite que vous pouvez utiliser lors de l'envoi d'un rapport d'erreur à partir d'une application de production.
la source
Choses à surveiller:
Dépassements de tableau - le débogueur de Visual Studio insère un remplissage qui peut arrêter les plantages.
Conditions de concurrence - avez-vous plusieurs threads impliqués si oui, une condition de concurrence n'apparaîtra que lorsqu'une application est exécutée directement.
Liaison - votre version de version extrait-elle les bonnes bibliothèques.
Choses à essayer:
Minidump - vraiment facile à utiliser (il suffit de le rechercher dans msdn) vous donnera un vidage sur incident complet pour chaque thread. Vous chargez simplement la sortie dans Visual Studio et c'est comme si vous déboguiez au moment du crash.
la source
Vous pouvez définir WinDbg comme débogueur post-mortem. Cela lancera le débogueur et l'attachera au processus lorsque le crash se produit. Pour installer WinDbg pour le débogage post-mortem, utilisez l'option / I (notez qu'elle est en majuscule ):
Plus de détails ici .
Quant à la cause, c'est très probablement une variable unifiée comme le suggèrent les autres réponses.
la source
Après de nombreuses heures de débogage, j'ai finalement trouvé la cause du problème, qui était en effet causé par un dépassement de mémoire tampon, provoquant une différence d'un octet:
Il s'agit d'une erreur fencepost (erreur off-by-one) et a été corrigée par:
Ce qui est étrange, c'est que j'ai mis plusieurs appels à _CrtCheckMemory () autour de différentes parties de mon code, et ils ont toujours renvoyé 1. J'ai pu trouver la source du problème en plaçant "return false;" appels dans le scénario de test, puis finalement déterminer par essais et erreurs où était la faute.
Merci à tous pour vos commentaires - j'ai beaucoup appris sur windbg.exe aujourd'hui! :)
la source
Même si vous avez construit votre exe en tant que version de version, vous pouvez toujours générer des fichiers PDB (base de données de programme) qui vous permettront d'empiler la trace et d'effectuer une quantité limitée d'inspection de variables. Dans vos paramètres de construction, il existe une option pour créer les fichiers PDB. Activez-le et reconnectez-le. Ensuite, essayez d'abord d'exécuter à partir de l'IDE pour voir si vous obtenez le plantage. Si c'est le cas, tant mieux - vous êtes prêt à regarder les choses. Sinon, lors de l'exécution à partir de la ligne de commande, vous pouvez effectuer l'une des deux opérations suivantes:
Lorsqu'on vous demande de pointer vers des fichiers PDB, recherchez-les. Si les PDB ont été placés dans le même dossier de sortie que votre EXE ou DLL, ils seront probablement récupérés automatiquement.
Les PDB fournissent un lien vers la source avec suffisamment d'informations sur les symboles pour permettre de voir les traces de pile, les variables, etc. Vous pouvez inspecter les valeurs comme d'habitude, mais sachez que vous pouvez obtenir de fausses lectures car la passe d'optimisation peut signifier des choses seulement apparaissent dans les registres ou les choses se passent dans un ordre différent de celui auquel vous vous attendez.
NB: je suppose ici un environnement Windows / Visual Studio.
la source
Des plantages comme celui-ci sont presque toujours dus au fait qu'un IDE définira généralement le contenu d'une variable non initialisée sur des zéros, null ou une autre valeur `` sensible '', alors que lors de l'exécution en mode natif, vous obtiendrez les déchets aléatoires que le système ramassera.
Votre erreur est donc presque certainement que vous utilisez quelque chose comme vous utilisez un pointeur avant qu'il n'ait été correctement initialisé et que vous vous en tiriez dans l'EDI car il ne pointe aucun endroit dangereux - ou la valeur est gérée par votre vérification des erreurs - mais en mode version, cela fait quelque chose de méchant.
la source
Afin d'avoir un vidage sur incident que vous pouvez analyser:
Vous devriez également consulter les outils dans les outils de débogage pour Windows . Vous pouvez surveiller l'application et voir toutes les exceptions de la première chance antérieures à votre exception de la deuxième chance.
J'espère que ça aide...
la source
Un excellent moyen de déboguer une erreur comme celle-ci consiste à activer les optimisations pour votre build de débogage.
la source
Une fois, j'ai eu un problème lorsque l'application se comportait de la même manière que la vôtre. Il s'est avéré être un mauvais dépassement de tampon dans sprintf. Naturellement, cela fonctionnait lorsqu'il était exécuté avec un débogueur attaché. Ce que j'ai fait, c'était d'installer un filtre d'exception non géré ( SetUnhandledExceptionFilter ) dans lequel j'ai simplement bloqué indéfiniment (en utilisant WaitForSingleObject sur un faux handle avec une valeur de délai d'expiration INFINITE).
Ainsi, vous pourriez quelque chose du genre:
J'ai ensuite attaché le débogueur après que le bug se soit manifesté (le programme gui a cessé de répondre).
Ensuite, vous pouvez effectuer un vidage et l'utiliser plus tard:
Ou déboguez-le tout de suite. Le moyen le plus simple consiste à suivre où le contexte du processeur a été enregistré par la machine de gestion des exceptions d'exécution:
La commande recherchera dans l'espace d'adressage de la pile les enregistrements CONTEXT selon la longueur de la recherche. J'utilise généralement quelque chose comme «l? 10000» . Remarque: n'utilisez pas de nombres inhabituellement élevés car l'enregistrement que vous recherchez est généralement proche du cadre de filtre d'exception non géré. 1003f est la combinaison de drapeaux (je crois que cela correspond à CONTEXT_FULL) utilisé pour capturer l'état du processeur. Votre recherche ressemblerait à ceci:
Une fois que vous obtenez les résultats, utilisez l'adresse dans la commande cxr:
Cela vous mènera à ce nouveau CONTEXTE, exactement au moment du crash (vous obtiendrez exactement la trace de la pile au moment où votre application s'est plantée). De plus, utilisez:
pour savoir exactement quelle exception s'est produite.
J'espère que ça aide.
la source
Cela se produit parfois parce que vous avez encapsulé une opération importante dans la macro "assert". Comme vous le savez peut-être, "assert" évalue les expressions uniquement en mode débogage.
la source
En ce qui concerne vos problèmes pour obtenir des informations de diagnostic, avez-vous essayé d'utiliser adplus.vbs comme alternative à WinDbg.exe? Pour vous attacher à un processus en cours, utilisez
Ou pour démarrer l'application dans le cas où le crash se produirait rapidement:
Des informations complètes sur adplus.vbs sont disponibles à l' adresse : http://support.microsoft.com/kb/286350
la source
Ntdll.dll avec le débogueur attaché
Une petite différence entre le lancement d'un programme à partir de l'IDE ou de WinDbg par opposition à son lancement à partir de la ligne de commande / du bureau est que lors du lancement avec un débogueur attaché (c'est-à-dire IDE ou WinDbg) ntdll.dll utilise une implémentation de tas différente qui effectue une petite validation sur l'allocation / la libération de mémoire.
Vous pouvez lire certaines informations pertinentes dans le point d'arrêt utilisateur inattendu dans ntdll.dll . PageHeap.exe est un outil qui pourrait vous aider à identifier le problème .
Analyse de crash
Vous n'avez pas écrit quel est le "crash" que vous rencontrez. Une fois que le programme plante et vous propose d'envoyer les informations d'erreur à Microsoft, vous devriez pouvoir cliquer sur les informations techniques et vérifier au moins le code d'exception, et avec un peu d'effort, vous pouvez même effectuer une analyse post-mortem (voir Heisenbug : Le programme WinApi plante sur certains ordinateurs) pour obtenir des instructions)
la source
Vista SP1 a en fait un très bon générateur de vidage sur incident intégré au système. Malheureusement, il n'est pas activé par défaut!
Consultez cet article: http://msdn.microsoft.com/en-us/library/bb787181(VS.85).aspx
L'avantage de cette approche est qu'aucun logiciel supplémentaire ne doit être installé sur le système affecté. Saisissez-le et déchirez-le, bébé!
la source
D'après mon expérience, ce sont surtout des problèmes de corruption de la mémoire.
Par exemple :
il est très possible d'être normal en mode débogage quand on exécute le code.
Mais en version, ce serait / pourrait être un crash.
Pour moi, fouiller là où la mémoire est hors de propos est trop pénible.
Utilisez certains outils comme Visual Leak Detector (Windows) ou valgrind (Linux) sont plus judicieux.
la source
J'ai vu beaucoup de bonnes réponses. Cependant, il n'y en a aucun qui m'a aidé. Dans mon cas, il y a eu une mauvaise utilisation des instructions SSE avec la mémoire non alignée . Jetez un œil à votre bibliothèque mathématique (si vous en utilisez une) et essayez de désactiver le support SIMD, recompilez et reproduisez le plantage.
Exemple:
Un projet inclut mathfu et utilise les classes avec le vecteur STL: std :: vector <mathfu :: vec2> . Une telle utilisation provoquera probablement un plantage au moment de la construction de l' élément mathfu :: vec2 car l'allocateur par défaut de la STL ne garantit pas l'alignement de 16 octets requis. Dans ce cas pour prouver l'idée, on peut définir
#define MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT 1
avant chaque inclusion du mathfu , recompiler en configuration Release et vérifier à nouveau.Les configurations Debug et RelWithDebInfo ont bien fonctionné pour mon projet, mais pas pour la version Release . La raison derrière ce comportement est probablement parce que le débogueur traite les demandes d'allocation / désallocation et effectue une certaine comptabilité de mémoire pour vérifier et vérifier les accès à la mémoire.
J'ai vécu la situation dans les environnements Visual Studio 2015 et 2017.
la source
Quelque chose de similaire m'est arrivé une fois avec GCC. Il s'est avéré être une optimisation trop agressive qui n'a été activée que lors de la création de la version finale et non pendant le processus de développement.
Eh bien, à vrai dire, c'était ma faute, pas celle de gcc, car je n'avais pas remarqué que mon code reposait sur le fait que cette optimisation particulière n'aurait pas été effectuée.
Il m'a fallu beaucoup de temps pour le retracer et je n'y suis venu que parce que j'ai posé la question sur un groupe de discussion et que quelqu'un m'a fait réfléchir. Alors, permettez-moi de vous rendre la pareille au cas où cela vous arriverait également.
la source
J'ai trouvé cet article utile pour votre scénario. ISTR les options du compilateur étaient un peu obsolètes. Regardez autour de vos options de projet Visual Studio pour voir comment générer des fichiers pdb pour votre version de version, etc.
la source
Il est suspect que cela se produise à l'extérieur du débogueur et non à l'intérieur; l'exécution dans le débogueur ne modifie normalement pas le comportement de l'application. Je vérifierais les différences d'environnement entre la console et l'IDE. Aussi, évidemment, compilez la version sans optimisations et avec des informations de débogage, et voyez si cela affecte le comportement. Enfin, consultez les outils de débogage post-mortem que d'autres personnes ont suggérés ici, vous pouvez généralement en obtenir des indices.
la source
Le débogage des versions de version peut être pénible en raison des optimisations modifiant l'ordre dans lequel les lignes de votre code semblent être exécutées. Cela peut vraiment être déroutant!
Une technique pour au moins réduire le problème consiste à utiliser MessageBox () pour afficher des instructions rapides indiquant à quelle partie du programme votre code est arrivé ("Démarrage de Foo ()", "Démarrage de Foo2 ()"); commencez à les mettre au sommet des fonctions dans la zone de votre code que vous soupçonnez (que faisiez-vous au moment où il s'est écrasé?). Lorsque vous pouvez déterminer quelle fonction, changez les boîtes de message en blocs de code ou même en lignes individuelles dans cette fonction jusqu'à ce que vous la réduisiez à quelques lignes. Ensuite, vous pouvez commencer à imprimer la valeur des variables pour voir dans quel état elles se trouvent au moment de la panne.
la source
Essayez d'utiliser _CrtCheckMemory () pour voir dans quel état se trouve la mémoire allouée. Si tout se passe bien, _CrtCheckMemory renvoie TRUE , sinon FALSE .
la source
Vous pouvez exécuter votre logiciel avec les indicateurs globaux activés (regardez dans les outils de débogage pour Windows). Cela aidera très souvent à résoudre le problème.
la source
Faites en sorte que votre programme génère un mini-vidage lorsque l'exception se produit, puis ouvrez-le dans un débogueur (par exemple, dans WinDbg). Les principales fonctions à examiner: MiniDumpWriteDump, SetUnhandledExceptionFilter
la source
Voici un cas que j'ai eu que quelqu'un pourrait trouver instructif. Il ne s'est écrasé que dans la version de Qt Creator - pas lors du débogage. J'utilisais des fichiers .ini (car je préfère les applications qui peuvent être copiées sur d'autres lecteurs, par rapport à celles qui perdent leurs paramètres si le registre est corrompu). Cela s'applique à toutes les applications qui stockent leurs paramètres dans l'arborescence de répertoires des applications. Si les versions de débogage et de publication se trouvent dans des répertoires différents, vous pouvez également avoir un paramètre différent entre eux. J'avais préférence enregistré dans un qui n'était pas enregistré dans l'autre. Cela s'est avéré être la source de mon accident. Heureusement que je l'ai trouvé.
Je déteste le dire, mais je n'ai diagnostiqué le crash que dans MS Visual Studio Community Edition; après avoir installé VS, laisser mon application se bloquer dans Qt Creator et choisir de l'ouvrir dans le débogueur de Visual Studio . Bien que mon application Qt ne contienne aucune information de symbole, il s'avère que les bibliothèques Qt en avaient. Cela m'a conduit à la ligne offensante; puisque je pouvais voir quelle méthode était appelée. (Pourtant, je pense que Qt est un framework LGPL pratique, puissant et multiplateforme.)
la source
J'ai eu cette erreur et vs s'est écrasé même en essayant de! Nettoyer! mon projet. J'ai donc supprimé les fichiers obj manuellement du répertoire Release, et après cela, il s'est construit très bien.
la source
Je suis d'accord avec Rolf. Parce que la reproductibilité est si importante, vous ne devriez pas avoir de mode sans débogage. Toutes vos versions doivent être déboguables. Avoir deux cibles à déboguer plus que doubler votre charge de débogage. Envoyez simplement la version "mode débogage", sauf si elle est inutilisable. Dans ce cas, rendez-le utilisable.
la source