Je travaille sur Linux avec le compilateur GCC. Lorsque mon programme C ++ plante, je voudrais qu'il génère automatiquement une trace de pile.
Mon programme est exécuté par de nombreux utilisateurs différents et il fonctionne également sur Linux, Windows et Macintosh (toutes les versions sont compilées à l'aide gcc
).
J'aimerais que mon programme puisse générer une trace de pile quand il se bloque et la prochaine fois que l'utilisateur l'exécutera, il leur demandera s'il est correct de m'envoyer la trace de pile afin que je puisse dépister le problème. Je peux gérer l'envoi des informations pour moi mais je ne sais pas comment générer la chaîne de trace. Des idées?
Réponses:
Pour Linux et je crois que Mac OS X, si vous utilisez gcc ou tout compilateur qui utilise glibc, vous pouvez utiliser les fonctions backtrace ()
execinfo.h
pour imprimer une trace de pile et quitter avec élégance lorsque vous obtenez une erreur de segmentation. La documentation se trouve dans le manuel libc .Voici un exemple de programme qui installe un
SIGSEGV
gestionnaire et imprime une trace de pilestderr
lorsqu'elle se sépare. Labaz()
fonction provoque ici le segfault qui déclenche le gestionnaire:La compilation avec
-g -rdynamic
vous permet d' obtenir des informations sur les symboles dans votre sortie, que la glibc peut utiliser pour créer une belle trace de pile:L'exécution de ceci vous obtient cette sortie:
Cela montre le module de chargement, le décalage et la fonction dont chaque trame de la pile est issue. Ici vous pouvez voir le gestionnaire de signal au - dessus de la pile, et les fonctions libc avant
main
en plusmain
,foo
,bar
etbaz
.la source
sigaction()
de libc. Bien que votre backtrace semble être correcte, j'ai parfois constaté que des étapes supplémentaires sont nécessaires pour garantir que l'emplacement réel du défaut apparaît dans la backtrace car il peut être remplacésigaction()
par le noyau.catchsegv
n'est pas ce dont l'OP a besoin mais est génial pour détecter les défauts de segmentation et obtenir toutes les informations.C'est encore plus facile que "man backtrace", il y a une bibliothèque peu documentée (spécifique à GNU) distribuée avec glibc comme libSegFault.so, qui a été je crois écrite par Ulrich Drepper pour supporter le programme catchsegv (voir "man catchsegv").
Cela nous donne 3 possibilités. Au lieu d'exécuter "programme -o hai":
Exécutez dans catchsegv:
Lien avec libSegFault lors de l'exécution:
Lien avec libSegFault au moment de la compilation:
Dans les 3 cas, vous obtiendrez des traces plus claires avec moins d'optimisation (gcc -O0 ou -O1) et des symboles de débogage (gcc -g). Sinon, vous risquez de vous retrouver avec une pile d'adresses mémoire.
Vous pouvez également capturer plus de signaux pour les traces de pile avec quelque chose comme:
La sortie ressemblera à ceci (remarquez la trace en bas):
Si vous voulez connaître les détails sanglants, la meilleure source est malheureusement la source: voir http://sourceware.org/git/?p=glibc.git;a=blob;f=debug/segfault.c et son répertoire parent http://sourceware.org/git/?p=glibc.git;a=tree;f=debug
la source
-Wl,--no-as-needed
aux drapeaux du compilateur. Sinon,ld
il ne sera en effet pas liélibSegFault
, car il reconnaît que le binaire n'utilise aucun de ses symboles.Linux
Bien que l'utilisation des fonctions backtrace () dans execinfo.h pour imprimer un stacktrace et sortir avec élégance lorsque vous obtenez une erreur de segmentation ait déjà été suggérée , je ne vois aucune mention des subtilités nécessaires pour garantir que la trace résultante pointe vers l'emplacement réel de la faute (au moins pour certaines architectures - x86 & ARM).
Les deux premières entrées de la chaîne de trames de pile lorsque vous entrez dans le gestionnaire de signaux contiennent une adresse de retour à l'intérieur du gestionnaire de signaux et une à l'intérieur de sigaction () dans libc. La trame de pile de la dernière fonction appelée avant le signal (qui est l'emplacement du défaut) est perdue.
Code
Production
Tous les risques d'appeler les fonctions backtrace () dans un gestionnaire de signaux existent toujours et ne doivent pas être négligés, mais je trouve les fonctionnalités que j'ai décrites ici très utiles pour déboguer les plantages.
Il est important de noter que l'exemple que j'ai fourni est développé / testé sur Linux pour x86. J'ai également réussi à implémenter cela sur ARM en utilisant
uc_mcontext.arm_pc
au lieu deuc_mcontext.eip
.Voici un lien vers l'article où j'ai appris les détails de cette implémentation: http://www.linuxjournal.com/article/6391
la source
-rdynamic
pour demander à l'éditeur de liens d'ajouter tous les symboles, pas seulement ceux utilisés, à la table des symboles dynamiques. Cela permetbacktrace_symbols()
de convertir des adresses en noms de fonctionaddr2line
commande pour obtenir la ligne exacte où le crash s'est produit?glibc
uc_mcontext
ne contient pas de champ nomméeip
. Il y a maintenant un tableau qui doit être indexé,uc_mcontext.gregs[REG_EIP]
c'est l'équivalent.Même si une réponse correcte a été fournie qui décrit comment utiliser la
backtrace()
fonction GNU libc 1 et que j'ai fourni ma propre réponse qui décrit comment garantir qu'une trace arrière d'un gestionnaire de signal pointe vers l'emplacement réel de l'erreur 2 , je ne vois pas toute mention de démêlage des symboles C ++ sortis de la trace.Lors de l'obtention de traces d'un programme C ++, la sortie peut être exécutée via
c++filt
1 pour démêler les symboles ou en utilisant directement 1 .abi::__cxa_demangle
c++filt
et__cxa_demangle
sont spécifiques à GCCL'exemple C ++ Linux suivant utilise le même gestionnaire de signaux que mon autre réponse et montre comment
c++filt
peut être utilisé pour démêler les symboles.Code :
Sortie (
./test
):Sortie démêlée (
./test 2>&1 | c++filt
):Ce qui suit s'appuie sur le gestionnaire de signal de ma réponse d'origine et peut remplacer le gestionnaire de signal dans l'exemple ci-dessus pour montrer comment
abi::__cxa_demangle
peut être utilisé pour démêler les symboles. Ce gestionnaire de signal produit la même sortie démêlée que l'exemple ci-dessus.Code :
la source
std::cerr
,free()
etexit()
toutes les restrictions violent contre l' appel des appels non-signaux asynchrones de sécurité sur les systèmes POSIX. Ce code impasse si le processus échoue dans un appel tel quefree()
,malloc()
new
oudetete
.Cela pourrait valoir la peine de regarder Google Breakpad , un générateur de vidage sur incident multiplateforme et des outils pour traiter les vidages.
la source
Vous n'avez pas spécifié votre système d'exploitation, il est donc difficile de répondre. Si vous utilisez un système basé sur gnu libc, vous pourrez peut-être utiliser la fonction libc
backtrace()
.GCC dispose également de deux fonctions intégrées qui peuvent vous aider, mais qui peuvent ou non être implémentées entièrement sur votre architecture, et ce sont
__builtin_frame_address
et__builtin_return_address
. Les deux veulent un niveau entier immédiat (par immédiat, je veux dire que ce ne peut pas être une variable). Si__builtin_frame_address
pour un niveau donné est différent de zéro, il devrait être sûr de saisir l'adresse de retour du même niveau.la source
Merci à enthousiastegeek d'avoir attiré mon attention sur l'utilitaire addr2line.
J'ai écrit un script rapide et sale pour traiter la sortie de la réponse fournie ici : (merci beaucoup à jschmier!) En utilisant l'utilitaire addr2line.
Le script accepte un seul argument: le nom du fichier contenant la sortie de l'utilitaire jschmier.
La sortie doit imprimer quelque chose comme ce qui suit pour chaque niveau de la trace:
Code:
la source
ulimit -c <value>
définit la taille maximale du fichier sous unix. Par défaut, la taille limite du fichier principal est 0. Vous pouvez voir vosulimit
valeurs aveculimit -a
.De plus, si vous exécutez votre programme à partir de gdb, il arrêtera votre programme sur les "violations de segmentation" (
SIGSEGV
, généralement lorsque vous accédez à un morceau de mémoire que vous n'avez pas alloué) ou vous pouvez définir des points d'arrêt.ddd et nemiver sont des frontaux pour gdb, ce qui facilite son utilisation pour le novice.
la source
Il est important de noter qu'une fois que vous générez un fichier core, vous devrez utiliser l'outil gdb pour le consulter. Pour que gdb donne un sens à votre fichier core, vous devez dire à gcc d'instrumenter le binaire avec des symboles de débogage: pour ce faire, vous compilez avec le drapeau -g:
Ensuite, vous pouvez soit définir "ulimit -c unlimited" pour le laisser vider un noyau, soit simplement exécuter votre programme dans gdb. J'aime plus la deuxième approche:
J'espère que ça aide.
la source
gdb
directement depuis votre programme de plantage. Gestionnaire d'installation pour SIGSEGV, SEGILL, SIGBUS, SIGFPE qui appellera gdb. Détails: stackoverflow.com/questions/3151779/… L'avantage est que vous obtenez une belle trace annotée comme dansbt full
, vous pouvez également obtenir des traces de pile de tous les threads.Cela fait un moment que j'examine ce problème.
Et enfoui profondément dans le README de Google Performance Tools
http://code.google.com/p/google-perftools/source/browse/trunk/README
parle de libunwind
http://www.nongnu.org/libunwind/
J'adorerais entendre les opinions de cette bibliothèque.
Le problème avec -rdynamic est qu'il peut augmenter la taille du binaire de manière relativement significative dans certains cas
la source
Certaines versions de libc contiennent des fonctions qui traitent des traces de pile; vous pourrez peut-être les utiliser:
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
Je me souviens d'avoir utilisé libunwind il y a longtemps pour obtenir des traces de pile, mais il n'est peut-être pas pris en charge sur votre plate-forme.
la source
Vous pouvez utiliser DeathHandler - petite classe C ++ qui fait tout pour vous, fiable.
la source
execlp()
pour effectuer des appels addr2line ... serait bien de rester entièrement dans son propre programme (ce qui est possible en incluant le code addr2line sous une certaine forme)Oubliez de changer vos sources et faites quelques hacks avec la fonction backtrace () ou les macroses - ce ne sont que de mauvaises solutions.
En tant que solution fonctionnant correctement, je conseillerais:
Cela imprimera une trace lisible appropriée de votre programme de manière lisible par l'homme (avec les noms de fichier source et les numéros de ligne). De plus, cette approche vous donnera la liberté d'automatiser votre système: ayez un court script qui vérifie si le processus a créé un vidage de mémoire, puis envoyez des traces par e-mail aux développeurs, ou connectez-le à un système de journalisation.
la source
est une variable système, qui permettra de créer un core dump après le crash de votre application. Dans ce cas, un montant illimité. Recherchez un fichier appelé core dans le même répertoire. Assurez-vous d'avoir compilé votre code avec les informations de débogage activées!
Cordialement
la source
limit coredumpsize unlimited
Regarder:
homme 3 backtrace
Et:
Ce sont des extensions GNU.
la source
Voir la fonction Stack Trace dans ACE (ADAPTIVE Communication Environment). Il est déjà écrit pour couvrir toutes les principales plateformes (et plus). La bibliothèque est sous licence BSD, vous pouvez donc même copier / coller le code si vous ne souhaitez pas utiliser ACE.
la source
Je peux vous aider avec la version Linux: les fonctions backtrace, backtrace_symbols et backtrace_symbols_fd peuvent être utilisées. Voir les pages de manuel correspondantes.
la source
Il semble que dans l'une des dernières librairies de la version boost de C ++ est apparue pour fournir exactement ce que vous voulez, probablement le code serait multiplateforme. C'est boost :: stacktrace , que vous pouvez utiliser comme dans l'exemple de boost :
Sous Linux, vous compilez le code ci-dessus:
Exemple de trace copiée à partir de la documentation de boost :
la source
* nix: vous pouvez intercepter SIGSEGV (généralement ce signal est émis avant de planter) et conserver les informations dans un fichier. (en plus du fichier core que vous pouvez utiliser pour déboguer avec gdb par exemple).
win: Vérifiez cela à partir de msdn.
Vous pouvez également consulter le code chrome de Google pour voir comment il gère les plantages. Il a un joli mécanisme de gestion des exceptions.
la source
J'ai trouvé que la solution @tgamblin n'est pas complète. Il ne peut pas gérer avec stackoverflow. Je pense que, par défaut, le gestionnaire de signal est appelé avec la même pile et SIGSEGV est lancé deux fois. Pour vous protéger, vous devez enregistrer une pile indépendante pour le gestionnaire de signaux.
Vous pouvez vérifier cela avec le code ci-dessous. Par défaut, le gestionnaire échoue. Avec la macro définie STACK_OVERFLOW, tout va bien.
la source
Le nouveau roi de la ville est arrivé https://github.com/bombela/backward-cpp
1 en-tête à placer dans votre code et 1 bibliothèque à installer.
Personnellement, je l'appelle en utilisant cette fonction
la source
J'utiliserais le code qui génère une trace de pile pour la fuite de mémoire dans Visual Leak Detector . Cela ne fonctionne que sur Win32.
la source
J'ai vu beaucoup de réponses ici effectuer un gestionnaire de signal puis quitter. C'est la voie à suivre, mais rappelez-vous un fait très important: si vous voulez obtenir le vidage de mémoire pour l'erreur générée, vous ne pouvez pas appeler
exit(status)
. Appelezabort()
plutôt!la source
En tant que solution Windows uniquement, vous pouvez obtenir l'équivalent d'une trace de pile (avec beaucoup, beaucoup plus d'informations) en utilisant le rapport d'erreurs Windows . Avec seulement quelques entrées de registre, il peut être configuré pour collecter les vidages en mode utilisateur :
Vous pouvez définir les entrées de registre à partir de votre programme d'installation, qui dispose des privilèges requis.
La création d'un vidage en mode utilisateur présente les avantages suivants par rapport à la génération d'une trace de pile sur le client:
Notez que le WER ne peut être déclenché que par un crash d'application (c'est-à-dire que le système met fin à un processus en raison d'une exception non gérée).
MiniDumpWriteDump
peut être appelé à tout moment. Cela peut être utile si vous avez besoin de vider l'état actuel pour diagnostiquer des problèmes autres qu'un crash.Lecture obligatoire, si vous souhaitez évaluer l'applicabilité des mini dumps:
la source
En plus des réponses ci-dessus, voici comment faire pour que Debian Linux OS génère un vidage de mémoire
la source
Si vous voulez toujours faire cavalier seul comme je l'ai fait, vous pouvez créer un lien
bfd
et éviter d'utiliseraddr2line
comme je l'ai fait ici:https://github.com/gnif/LookingGlass/blob/master/common/src/crash.linux.c
Cela produit la sortie:
la source
Sous Linux / unix / MacOSX, utilisez les fichiers de base (vous pouvez les activer avec ulimit ou un appel système compatible ). Sous Windows, utilisez le rapport d'erreurs Microsoft (vous pouvez devenir partenaire et accéder aux données de plantage de votre application).
la source
J'ai oublié la technologie GNOME de "répart", mais je ne sais pas grand chose sur son utilisation. Il est utilisé pour générer des traces de pile et d'autres diagnostics pour le traitement et peut automatiquement déposer des bogues. Cela vaut certainement la peine de s'enregistrer.
la source