Pile d'appels pour les exceptions en C++

Aujourd'hui, dans mon code C++ multi-plate-forme, j'ai un try-catch autour de chaque fonction. Dans chaque bloc de capture j'ajoute le nom de la fonction courante à l'exception et je le jette à nouveau, de sorte que dans le bloc de capture le plus haut (où j'imprime finalement les détails de l'exception) j'ai la pile d'appels complète, ce qui m'aide à tracer la cause de l'exception.

est-ce une bonne pratique, ou y a-t-il de meilleures façons d'obtenir la pile d'appels pour l'exception?

29
demandé sur Igor Oks 2010-07-11 15:31:46

9 réponses

Non, il est profondément horrible, et je ne vois pas pourquoi vous avez besoin d'une pile d'appels dans l'exception elle - même-je trouve la raison de l'exception, le numéro de ligne et le nom du fichier du code où l'exception initiale s'est produite tout à fait suffisante.

après avoir dit que, si vous devez vraiment avoir une trace de pile, la chose à faire est de générer les informations de pile d'appel une fois au site de lancer d'exception. Il n'y a pas une seule façon portable de faire cela, mais en utilisant quelque chose comme http://stacktrace.sourceforge.net / combiné avec et une bibliothèque similaire pour VC++ ne devrait pas être trop difficile.

25
répondu 2010-07-11 13:22:59

Ce que vous faites n'est pas une bonne pratique. Voici pourquoi:

1. Il n'est pas nécessaire.

Si vous compilez votre projet en mode de débogage de sorte que les informations de débogage soient générées, vous pouvez facilement obtenir des rétrotraces pour la gestion des exceptions dans un débogueur tel que GDB.

2. C'est gênant.

C'est quelque chose que vous devez vous rappeler d'ajouter à chaque fonction. Si vous manquez une fonction, qui pourrait causer beaucoup de confusion, surtout si c'était la fonction qui a provoqué l'exception. Et quiconque regarde votre code devrait se rendre compte de ce que vous faites. Aussi, je parie que vous avez utilisé quelque chose comme __FUNC__ ou __FUNCTION__ ou __PRETTY_FUNCTION__, qui malheureusement pour dire sont tous non-standard (il n'y a pas de moyen standard en C++ pour obtenir le nom de la fonction).

3. C'est lent.

La propagation des exceptions en C++ est déjà assez lente, et l'ajout de cette logique ne fera que ralentir le chemin du code. Ce n'est pas un problème si vous utilisez des macros pour attraper et rethrow, où vous pouvez facilement elide le catch et rethrow dans les versions de sortie de votre code. Sinon, la performance pourrait être un problème.

bonne pratique

Bien qu'il puisse ne pas être une bonne pratique d'attraper et de repenser dans chaque fonction de construisez une trace de pile, c'est une bonne pratique d'attacher le nom de fichier, le numéro de ligne, et le nom de la fonction à laquelle l'exception a été lancée à l'origine. Si vous utilisez boost::exception avec BOOST_THROW_EXCEPTION, vous obtiendrez ce comportement gratuitement. Il est également utile de joindre à votre exception des informations explicatives qui vous aideront à déboguer et à gérer l'exception. Cela dit, tout cela devrait se produire au moment où l'exception est construit; une fois qu'il est construit, il devrait être autorisé à propager à son maître... vous ne devriez pas attraper et repasser plus qu'il n'est strictement nécessaire. Si vous avez besoin d'attraper et de rethrow dans une fonction particulière pour joindre quelques informations cruciales, c'est bien, mais attraper toutes les exceptions dans chaque fonction et pour les fins de joindre des informations déjà disponibles est tout simplement trop.

21
répondu Michael Aaron Safyan 2010-07-11 11:46:17

une solution qui peut être plus gracieuse est de construire une macro/classe traceur. Donc, en haut de chaque fonction, vous écrivez quelque chose comme:

TRACE()

et la macro ressemble à quelque chose comme:

Tracer t(__FUNCTION__);

et le traceur de classe ajoute le nom de la fonction à une pile globale sur la construction, et se supprime lors de la destruction. Alors cette pile est toujours disponible pour la journalisation ou le débogage, la maintenance est beaucoup plus simple (une ligne), et il ne pas encourir exception des frais généraux.

exemples de mise en œuvre comprennent des choses comme http://www.drdobbs.com/184405270 , http://www.codeproject.com/KB/cpp/cmtrace.aspx , et http://www.codeguru.com/cpp/v-s/debug/tracing/article.php/c4429 . Aussi les fonctions de Linux comme ce http://www.linuxjournal.com/article/6391 peut le faire plus nativement, comme décrit par cette pile Question de débordement: comment générer un stacktrace lorsque mon application gcc C++ plante . La course ACE_STACK_TRACE D'ACE vaut peut-être la peine d'être examinée aussi.

quoi qu'il en soit, la méthode de manipulation d'exception est rudimentaire, rigide et coûteuse sur le plan informatique. Les solutions de construction de classe/macro sont beaucoup plus rapides et peuvent être compilées pour les constructions de version si vous le souhaitez.

7
répondu Scott Stafford 2017-05-23 12:26:03

la réponse à tous vos problèmes est un bon débogueur, généralement http://www.gnu.org/software/gdb / sous linux ou Visual Studio sous Windows. Ils peuvent vous donner des traces de pile sur demande à tout moment dans le programme.

votre méthode actuelle est un véritable casse-tête de performance et d'entretien. Les débogueurs sont inventés pour accomplir votre but, mais sans la surcharge.

2
répondu Scott Stafford 2010-07-11 13:02:20

Regardez cette DONC, la Question . Cela pourrait être proche de ce que vous cherchez. Ce n'est pas multiplateforme mais la réponse donne des solutions pour gcc et Visual Studio.

1
répondu zooropa 2017-05-23 11:54:40

une exception qui n'est pas traitée est laissée pour la fonction d'appel à gérer. Cela continue jusqu'à ce que l'exception soit traitée. Cela se produit avec ou sans try/catch autour d'un appel de fonction. En d'autres termes, si une fonction est appelée qui n'est pas dans un bloc d'essai, une exception qui se produit dans cette fonction sera automatiquement passée à la pile d'appels. Ainsi, tout ce que vous devez faire est de mettre la fonction la plus élevée dans un bloc d'essai et de gérer l'exception "..."dans le bloc catch. Cette exception va attraper toutes les exceptions. Ainsi, votre fonction la plus élevée ressemblera à quelque chose comme

int main()
{
  try
  {
    top_most_func()
  }
  catch(...)
  {
    // handle all exceptions here
  }
}

Si vous voulez des blocs de code pour certaines exceptions, vous pouvez le faire aussi. Assurez-vous juste que ceux-ci se produisent avant le "..."l'exception" bloc catch.

0
répondu zooropa 2010-07-11 13:16:55

il y a un joli petit projet qui donne une jolie trace de pile:

https://github.com/bombela/backward-cpp

0
répondu Scott Stafford 2013-03-15 15:36:10
"151900920 Un de plus dans le projet de stack-trace de soutien: ex_diag . Il n'y a pas de macros, il n'y a pas de multiplate-forme, il n'y a pas de besoins énormes en code, l'outil est rapide, clair et facile à utiliser.

ici, vous avez besoin seulement envelopper les objets, qui sont nécessaires pour tracer, et ils seront tracés si l'exception se produit.

0
répondu Boris 2013-06-08 20:16:06

lien avec la bibliothèque libcsdbg (voir https://stackoverflow.com/a/18959030/364818 pour la réponse originale) ressemble à la manière la plus propre d'obtenir une trace de pile sans modifier votre code source ou le code source de tierce partie (i.e. STL).

cela utilise le compilateur pour instrumenter la collection de pile réelle, qui est vraiment que vous voulez faire.

Je ne l'ai pas utilisé et il est GPL teinté, mais il ressemble à la bonne idée.

0
répondu Mark Lakata 2017-05-23 11:47:11