Frage

Ich suche nach einer Antwort in MS VC++.

Beim Debuggen einer großen C++-Anwendung, die leider sehr häufig C++-Ausnahmen verwendet.Manchmal erlebe ich eine Ausnahme etwas später, als ich eigentlich möchte.

Beispiel im Pseudocode:

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

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

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

Ich kann die Ausnahme beim Debuggen mit einem Haltepunkt abfangen.Aber ich kann nicht zurückverfolgen, ob die Ausnahme aufgetreten ist FunctionA() oder FunctionB(), oder eine andere Funktion.(Unter der Annahme einer umfangreichen Ausnahmenutzung und einer großen Version des obigen Beispiels).

Eine Lösung für mein Problem besteht darin, den Aufrufstapel zu ermitteln und zu speichern im Ausnahmekonstruktor (d. h.bevor es gefangen wird).Dafür müsste ich jedoch alle Ausnahmen von dieser Basisausnahmeklasse ableiten.Es würde auch viel Code erfordern und möglicherweise mein Programm verlangsamen.

Gibt es einen einfacheren Weg, der weniger Arbeit erfordert?Ohne meine große Codebasis ändern zu müssen?

Gibt es bessere Lösungen für dieses Problem in anderen Sprachen?

War es hilfreich?

Lösung

Wenn Sie nur daran interessiert sind, woher die Ausnahme kam, können Sie einfach ein einfaches Makro wie schreiben

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

Dadurch werden der Dateiname, die Zeilennummer und der Funktionsname zum Ausnahmetext hinzugefügt (sofern der Compiler die entsprechenden Makros bereitstellt).

Dann werfen Sie Ausnahmen mit

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

Andere Tipps

Sie haben auf einen Haltepunkt im Code hingewiesen.Da Sie sich im Debugger befinden, können Sie einen Haltepunkt im Konstruktor der Ausnahmeklasse festlegen oder den Visual Studio-Debugger so einstellen, dass er bei allen ausgelösten Ausnahmen abbricht (Debug->Ausnahmen. Klicken Sie auf C++-Ausnahmen, wählen Sie ausgelöste und nicht abgefangene Optionen aus).

Es gibt ein ausgezeichnetes Buch von John Robbins, das viele schwierige Debugging-Fragen behandelt.Das Buch heißt Debuggen von Anwendungen für Microsoft .NET und Microsoft Windows.Trotz des Titels enthält das Buch zahlreiche Informationen zum Debuggen nativer C++-Anwendungen.

In diesem Buch gibt es einen langen Abschnitt darüber, wie man den Aufrufstapel für ausgelöste Ausnahmen erhält.Wenn ich mich richtig erinnere, beziehen sich einige seiner Ratschläge auf die Verwendung strukturierte Ausnahmebehandlung (SEH) anstelle von (oder zusätzlich zu) C++-Ausnahmen.Ich kann das Buch wirklich nicht genug empfehlen.

Fügen Sie einen Haltepunkt in den Ausnahmeobjektkonstruktor ein.Sie erhalten Ihren Haltepunkt, bevor die Ausnahme ausgelöst wird.

Es gibt keine Möglichkeit, die Ursache einer Ausnahme herauszufinden nach Es wird abgefangen, es sei denn, Sie geben diese Informationen an, wenn es ausgelöst wird.Wenn Sie die Ausnahme abfangen, ist der Stapel bereits abgewickelt und es gibt keine Möglichkeit, den vorherigen Zustand des Stapels wiederherzustellen.

Ihr Vorschlag, den Stack-Trace in den Konstruktor aufzunehmen, ist die beste Wahl.Ja, es kostet Zeit während der Konstruktion, aber Sie sollten wahrscheinlich nicht oft genug Ausnahmen auslösen, dass dies ein Problem darstellt.Es kann auch mehr sein, als Sie benötigen, wenn Sie alle Ihre Ausnahmen von einer neuen Basis erben lassen.Sie könnten einfach die relevanten Ausnahmen erben lassen (danke, Mehrfachvererbung) und einen separaten Catch für diese haben.

Du kannst den ... benutzen StackTrace64 Funktion, um den Trace zu erstellen (ich glaube, es gibt auch andere Möglichkeiten).Kasse Dieser Artikel zum Beispiel Code.

So mache ich es in C++ mit GCC-Bibliotheken:

#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;
}

Der Konstruktor Ihrer Ausnahme kann diese Funktion einfach aufrufen und den Stack-Trace speichern.Es braucht den Parameter numskip weil ich den Konstruktor der Ausnahme gerne aus meinen Stack-Traces herausschneiden möchte.

Dafür gibt es keine Standardmethode.

Darüber hinaus muss der Aufrufstapel normalerweise zum Zeitpunkt des Auftretens der Ausnahme aufgezeichnet werden geworfen;einmal war es erwischt Der Stapel ist abgerollt, sodass Sie nicht mehr wissen, was zum Zeitpunkt des Werfens passiert ist.

In VC++ unter Win32/Win64 können Sie könnte Erhalten Sie ausreichend brauchbare Ergebnisse, indem Sie den Wert aus der Compiler-internen Funktion _ReturnAddress() aufzeichnen und sicherstellen, dass Ihr Ausnahmeklassenkonstruktor __declspec(noinline) ist.In Verbindung mit der Debug-Symbolbibliothek könnten Sie wahrscheinlich mit SymGetLineFromAddr64 den Funktionsnamen (und die Zeilennummer, falls Ihre .pdb-Datei ihn enthält) erhalten, der der Rücksprungadresse entspricht.

In nativem Code können Sie versuchen, den Callstack zu durchlaufen, indem Sie a installieren Vektorbasierter Ausnahmehandler.VC++ implementiert C++-Ausnahmen zusätzlich zu SEH-Ausnahmen und ein vektorisierter Ausnahmehandler erhält den ersten Schuss vor allen Frame-basierten Handlern.Seien Sie jedoch sehr vorsichtig, da Probleme, die durch die vektorisierte Ausnahmebehandlung entstehen, schwer zu diagnostizieren sein können.

Auch Mike Stall hat einige Warnungen über die Verwendung in einer App, die über verwalteten Code verfügt.Zum Schluss lesen Matt Pietreks Artikel und stellen Sie sicher, dass Sie SEH und die vektorisierte Ausnahmebehandlung verstehen, bevor Sie dies versuchen.(Nichts fühlt sich so schlimm an, wie das Aufspüren eines kritischen Problems. Der von Ihnen hinzugefügte Code hilft dabei, kritische Probleme aufzuspüren.)

Ich glaube, dass MSDev es Ihnen ermöglicht, Haltepunkte festzulegen, wenn eine Ausnahme ausgelöst wird.

Alternativ können Sie den Haltepunkt auch auf den Konstruktor Ihres Ausnahmeobjekts setzen.

Wenn Sie über die IDE debuggen, gehen Sie zu „Debuggen“ -> „Ausnahmen“ und klicken Sie auf „Ausgelöst“ für C++-Ausnahmen.

Andere Sprachen?Nun, in Java rufen Sie e.printStackTrace();Viel einfacher geht es nicht.

Falls es jemanden interessiert, ein Kollege hat mir per E-Mail auf diese Frage geantwortet:

Artem schrieb:

Es gibt ein Flag für MiniDumpWriteDump(), das bessere Crash-Dumps erstellen kann, die es ermöglichen, den vollständigen Programmstatus mit allen globalen Variablen usw. anzuzeigen.Was Call-Stacks betrifft, bezweifle ich, dass sie aufgrund von Optimierungen besser werden können ...es sei denn, Sie deaktivieren (vielleicht einige) Optimierungen.

Außerdem denke ich, dass das Deaktivieren von Inline-Funktionen und der Optimierung des gesamten Programms sehr hilfreich sein wird.

Tatsächlich gibt es viele Dump-Typen. Vielleicht könnten Sie einen auswählen, der klein genug ist, aber dennoch über mehr Informationen verfügthttp://msdn.microsoft.com/en-us/library/ms680519(VS.85).aspx

Diese Typen helfen jedoch nicht beim Aufrufstapel, sie wirken sich nur auf die Anzahl der Variablen aus, die Sie sehen können.

Mir ist aufgefallen, dass einige dieser Dump-Typen in der von uns verwendeten dbghelp.dll Version 5.1 nicht unterstützt werden.Wir könnten es jedoch auf die neueste Version 6.9 aktualisieren. Ich habe gerade die EULA für MS-Debugging-Tools überprüft – die neueste dbghelp.dll kann immer noch weitergegeben werden.

Ich verwende meine eigenen Ausnahmen.Sie können ganz einfach damit umgegangen werden – sie enthalten auch Text.Ich verwende das Format:

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

Außerdem habe ich ein zweites Ausnahmeformat:

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

Dieser wird dann mithilfe von FormatMessage von einem DWORD-Wert in die eigentliche Nachricht umgewandelt.Wenn Sie das Format „wo/was“ verwenden, wird Ihnen angezeigt, was passiert ist und in welcher Funktion.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top