Question

Je cherche une réponse dans MS VC++.

Lors du débogage d’une grande application C++, qui utilise malheureusement très largement les exceptions C++.Parfois, j'attrape une exception un peu plus tard que je ne le souhaite réellement.

Exemple en pseudo code :

FunctionB()
{
    ...
    throw e;
    ...
}

FunctionA()
{
    ...
    FunctionB()
    ...
}

try
{
    Function A()
}
catch(e)
{
    (<--- breakpoint)
    ...
}

Je peux intercepter l'exception avec un point d'arrêt lors du débogage.Mais je ne peux pas retracer si l'exception s'est produite dans FunctionA() ou FunctionB(), ou une autre fonction.(En supposant une utilisation étendue des exceptions et une version énorme de l'exemple ci-dessus).

Une solution à mon problème consiste à déterminer et à sauvegarder la pile d'appels dans le constructeur d'exception (c'est à dire.avant qu'il ne soit attrapé).Mais cela m'obligerait à dériver toutes les exceptions de cette classe d'exceptions de base.Cela nécessiterait également beaucoup de code et ralentirait peut-être mon programme.

Existe-t-il un moyen plus simple et nécessitant moins de travail ?Sans avoir à changer ma grande base de code ?

Existe-t-il de meilleures solutions à ce problème dans d’autres langues ?

Était-ce utile?

La solution

Si vous souhaitez simplement savoir d'où vient l'exception, vous pouvez simplement écrire une simple macro comme

#define throwException(message) \
    {                           \
        std::ostringstream oss; \
        oss << __FILE __ << " " << __LINE__ << " "  \
           << __FUNC__ << " " << message; \
        throw std::exception(oss.str().c_str()); \
    }

qui ajoutera le nom du fichier, le numéro de ligne et le nom de la fonction au texte d'exception (si le compilateur fournit les macros respectives).

Puis lancez des exceptions en utilisant

throwException("An unknown enum value has been passed!");

Autres conseils

Vous avez indiqué un point d'arrêt dans le code.Puisque vous êtes dans le débogueur, vous pouvez définir un point d'arrêt sur le constructeur de la classe d'exception ou configurer le débogueur Visual Studio pour qu'il s'arrête sur toutes les exceptions levées (Débogage-> Exceptions Cliquez sur les exceptions C++, sélectionnez les options levées et non interceptées)

Il existe un excellent livre écrit par John Robbins qui aborde de nombreuses questions difficiles de débogage.Le livre s'appelle Applications de débogage pour Microsoft .NET et Microsoft Windows.Malgré son titre, le livre contient une multitude d'informations sur le débogage des applications C++ natives.

Dans ce livre, il y a une longue section expliquant comment obtenir la pile d'appels pour les exceptions levées.Si je me souviens bien, certains de ses conseils impliquent d'utiliser gestion structurée des exceptions (SEH) au lieu (ou en plus) des exceptions C++.Je ne peux vraiment pas recommander assez le livre.

Placez un point d'arrêt dans le constructeur de l'objet d'exception.Vous obtiendrez votre point d'arrêt avant que l'exception ne soit levée.

Il n'y a aucun moyen de connaître la source d'une exception après il est attrapé, à moins que vous n'incluiez cette information lorsqu'il est lancé.Au moment où vous détectez l'exception, la pile est déjà déroulée et il n'y a aucun moyen de reconstruire l'état précédent de la pile.

Votre suggestion d'inclure la trace de pile dans le constructeur est votre meilleur pari.Oui, cela prend du temps pendant la construction, mais vous ne devriez probablement pas lancer d'exceptions assez souvent pour que cela soit un problème.Faire hériter de toutes vos exceptions d’une nouvelle base peut également être plus que ce dont vous avez besoin.Vous pouvez simplement faire hériter des exceptions pertinentes (merci, héritage multiple) et avoir une capture distincte pour celles-ci.

Vous pouvez utiliser le StackTrace64 fonction pour construire la trace (je crois qu'il existe également d'autres moyens).Vérifier Cet article par exemple le code.

Voici comment procéder en C++ en utilisant les bibliothèques GCC :

#include <execinfo.h> // Backtrace
#include <cxxabi.h> // Demangling

vector<Str> backtrace(size_t numskip) {
    vector<Str> result;
    std::vector<void*> bt(100);
    bt.resize(backtrace(&(*bt.begin()), bt.size()));
    char **btsyms = backtrace_symbols(&(*bt.begin()), bt.size());
    if (btsyms) {
        for (size_t i = numskip; i < bt.size(); i++) {
            Aiss in(btsyms[i]);
            int idx = 0; Astr nt, addr, mangled;
            in >> idx >> nt >> addr >> mangled;
            if (mangled == "start") break;
            int status = 0;
            char *demangled = abi::__cxa_demangle(mangled.c_str(), 0, 0, &status);

            Str frame = (status==0) ? Str(demangled, demangled+strlen(demangled)) : 
                                      Str(mangled.begin(), mangled.end());
            result.push_back(frame);

            free(demangled);
        }
        free(btsyms);
    }
    return result;
}

Le constructeur de votre exception peut simplement appeler cette fonction et stocker la trace de la pile.Il faut le paramètre numskip parce que j'aime supprimer le constructeur de l'exception de mes traces de pile.

Il n’existe pas de manière standard de procéder.

De plus, la pile d'appels doit généralement être enregistrée au moment où l'exception est jeté;une fois qu'il a été attrapé la pile s'est déroulée, on ne sait donc plus ce qui se passait au moment d'être lancé.

En VC++ sur Win32/Win64, vous pourrait obtenez des résultats suffisamment utilisables en enregistrant la valeur du _ReturnAddress() intrinsèque du compilateur et en vous assurant que votre constructeur de classe d'exception est __declspec(noinline).En conjonction avec la bibliothèque de symboles de débogage, je pense que vous pourriez probablement obtenir le nom de la fonction (et le numéro de ligne, si votre .pdb le contient) qui correspond à l'adresse de retour en utilisant SymGetLineFromAddr64.

En code natif, vous pouvez tenter de parcourir la pile d'appels en installant un Gestionnaire d’exceptions vectorielles.VC++ implémente les exceptions C++ en plus des exceptions SEH et un gestionnaire d'exceptions vectorielles est donné en premier avant tout gestionnaire basé sur des images.Soyez cependant très prudent, les problèmes introduits par la gestion des exceptions vectorielles peuvent être difficiles à diagnostiquer.

Aussi Mike Stall a quelques avertissements à propos de son utilisation dans une application qui gère du code.Enfin, lisez L'article de Matt Pietrek et assurez-vous de bien comprendre SEH et la gestion des exceptions vectorielles avant d'essayer ceci.(Rien n'est aussi grave que de rechercher un problème critique dans le code que vous avez ajouté pour aider à détecter les problèmes critiques.)

Je crois que MSDev vous permet de définir des points d'arrêt lorsqu'une exception est levée.

Vous pouvez également placer le point d'arrêt sur le constructeur de votre objet d'exception.

Si vous déboguez à partir de l'EDI, accédez à Debug->Exceptions, cliquez sur Thrown pour les exceptions C++.

Autres langues?Eh bien, en Java, vous appelez e.printStackTrace();Il n’y a rien de plus simple que cela.

Au cas où quelqu'un serait intéressé, un collègue m'a répondu à cette question par e-mail :

Artem a écrit :

Il existe un indicateur pour MiniDumpWriteDump() qui peut effectuer de meilleurs vidages sur incident qui permettront de voir l'état complet du programme, avec toutes les variables globales, etc.Quant aux piles d'appels, je doute qu'elles puissent être meilleures grâce aux optimisations...à moins que vous désactiviez (peut-être certaines) optimisations.

De plus, je pense que la désactivation des fonctions en ligne et l'optimisation de l'ensemble du programme aideront beaucoup.

En fait, il existe de nombreux types de dumps, vous pourriez peut-être en choisir un assez petit tout en ayant plus d'informations.http://msdn.microsoft.com/en-us/library/ms680519(VS.85).aspx

Ces types n'aideront cependant pas avec la pile d'appels, ils n'affectent que la quantité de variables que vous pourrez voir.

J'ai remarqué que certains de ces types de vidage ne sont pas pris en charge dans la version 5.1 de dbghelp.dll que nous utilisons.Nous pourrions le mettre à jour vers la version 6.9 la plus récente, je viens de vérifier le CLUF pour les outils de débogage MS - le plus récent dbghelp.dll peut toujours être redistribué.

J'utilise mes propres exceptions.Vous pouvez les gérer assez simplement - ils contiennent également du texte.J'utilise le format :

throw Exception( "comms::serial::serial( )", "Something failed!" );

J'ai aussi un deuxième format d'exception :

throw Exception( "comms::serial::serial( )", ::GetLastError( ) );

Qui est ensuite converti d'une valeur DWORD en message réel à l'aide de FormatMessage.L'utilisation du format où/quoi vous montrera ce qui s'est passé et dans quelle fonction.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top