C ++ zeigt Stack-Trace bei Ausnahme an

Ich möchte eine Möglichkeit haben, den Stack-Trace an den Benutzer zu melden, wenn eine Ausnahme ausgetriggers wird. Was ist der beste Weg, dies zu tun? Benötigen Sie viel zusätzlichen Code?

Um Fragen zu beantworten:

Ich möchte, dass es tragbar ist, wenn es möglich ist. Ich möchte, dass Informationen angezeigt werden, damit der Benutzer den Stack-Trace kopieren und per E-Mail an mich senden kann, wenn ein Fehler auftritt.

Es hängt davon ab, welche Plattform.

Auf GCC ist es ziemlich trivial, siehe diesen Beitrag für weitere Details.

In MSVC können Sie dann die StackWalker- Bibliothek verwenden, die alle für Windows erforderlichen API-Aufrufe verarbeitet.

Sie müssen herausfinden, wie Sie diese functionalität am besten in Ihre App integrieren, aber die Menge an Code, die Sie schreiben müssen, sollte minimal sein.

Andrew Grants Antwort hilft nicht , einen Stack-Trace der Wurffunktion zu erhalten , zumindest nicht mit GCC, weil eine throw-statement den aktuellen Stack-Trace nicht selbst speichert und der catch-Handler keinen Zugriff auf den Stack-Trace hat dieser Punkt mehr.

Der einzige Weg, GCC zu benutzen, um dies zu lösen, besteht darin, sicherzustellen, dass ein Stapel-Trace am Punkt des Wurfbefehls erzeugt wird, und diesen mit dem Ausnahme-Objekt zu speichern.

Diese Methode erfordert natürlich, dass jeder Code, der eine Ausnahme austriggers, diese bestimmte Exception-class verwendet.

Update 11. Juli 2017 : Für einige hilfreiche Code, casting Sie einen Blick auf Cahit Beyaz Antwort, die auf http://stacktrace.sourceforge.net zeigt – ich habe es noch nicht verwendet, aber es sieht vielversprechend aus.

Wenn Sie Boost 1.65 oder höher verwenden, können Sie boost :: stacktrace verwenden :

 #include  // ... somewhere inside the bar(int) function that is called recursively: std::cout < < boost::stacktrace::stacktrace(); 

Unix: Rückverfolgung

Mac: Zurückverfolgen

Windows: CaptureBackTrace

AFAIK libunwind ist ziemlich portabel und bisher habe ich nichts einfacheres gefunden.

Ich empfehle http://stacktrace.sourceforge.net/ project. Es unterstützt Windows, Mac OS und auch Linux

auf Linux mit g ++ überprüfen Sie diese lib

https://sourceforge.net/projects/libcsdbg

es macht die ganze Arbeit für dich

Unter Windows, überprüfen Sie BugTrap . Es ist nicht mehr am ursprünglichen Link, aber es ist immer noch auf CodeProject verfügbar.

Ich möchte eine Standardbibliotheksoption hinzufügen (dh plattformübergreifend), wie Exception-Backtraces generiert werden, die mit C ++ 11 verfügbar geworden sind:

Verwenden Sie std::nested_exception und std::throw_with_nested

Dies wird Ihnen keinen Stack-Abbau geben, aber meiner Meinung nach das nächstbeste. In StackOverflow wird hier und hier beschrieben , wie Sie ein Backtrace für Ihre Exceptions in Ihrem Code erhalten, ohne einen Debugger oder mühsame Protokollierung zu benötigen, indem Sie einfach einen geeigneten Exception-Handler schreiben, der verschachtelte Exceptions erneut austriggers.

Da Sie dies mit jeder abgeleiteten Ausnahmeklasse tun können, können Sie einem solchen Backtrace viele Informationen hinzufügen! Sie können sich auch meinen MWE auf GitHub anschauen, wo ein Backtrace ungefähr so ​​aussehen würde:

 Library API: Exception caught in function 'api_function' Backtrace: ~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed ~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt" 

Poppy kann nicht nur die Stack-Trace, sondern auch Parameterwerte, lokale Variablen usw. sammeln – alles was zum Absturz führt.

Ich habe ein ähnliches Problem, und obwohl ich Portabilität mag, brauche ich nur GCC-Unterstützung. In gcc sind execinfo.h und die Backtrace- Aufrufe verfügbar. Um die functionsnamen zu entkräften, hat Herr Bingmann ein schönes Stück Code. Um ein Backtrace für eine Ausnahme auszugeben, erstelle ich eine Ausnahme, die das Backtrace im Konstruktor ausgibt. Wenn ich erwarte, dass dies mit einer Ausnahme funktioniert, die in einer Bibliothek ausgetriggers wird, muss möglicherweise neu erstellt / verlinkt werden, sodass die Backtracing-Ausnahme verwendet wird.

 /****************************************** #Makefile with flags for printing backtrace with function names # compile with symbols for backtrace CXXFLAGS=-g # add symbols to dynamic symbol table for backtrace LDFLAGS=-rdynamic turducken: turducken.cc ******************************************/ #include  #include  #include  #include "stacktrace.h" /* https://panthema.net/2008/0901-stacktrace-demangled/ */ // simple exception that prints backtrace when constructed class btoverflow_error: public std::overflow_error { public: btoverflow_error( const std::string& arg ) : std::overflow_error( arg ) { print_stacktrace(); }; }; void chicken(void) { throw btoverflow_error( "too big" ); } void duck(void) { chicken(); } void turkey(void) { duck(); } int main( int argc, char *argv[]) { try { turkey(); } catch( btoverflow_error e) { printf( "caught exception: %s\n", e.what() ); } } 

Das Kompilieren und Ausführen dieses Befehls mit gcc 4.8.4 führt zu einem Backtrace mit gut entmagnetisierten C ++ – functionsnamen:

 stack trace: ./turducken : btoverflow_error::btoverflow_error(std::string const&)+0x43 ./turducken : chicken()+0x48 ./turducken : duck()+0x9 ./turducken : turkey()+0x9 ./turducken : main()+0x15 /lib/x86_64-linux-gnu/libc.so.6 : __libc_start_main()+0xf5 ./turducken() [0x401629] 

Da der Stack beim Eingeben des Catch-Blocks bereits abgewickelt ist, sollte die Lösung in meinem Fall bestimmte Ausnahmen nicht erfassen, die dann zu einem SIGABRT führen. Im Signalhandler für SIGABRT habe ich dann fork () und execl () entweder gdb (in debug builds) oder Google breakpads stackwalk (in release builds). Ich versuche auch, nur Signal-Handler-sichere functionen zu verwenden.

GDB:

 static const char BACKTRACE_START[] = "<2>--- backtrace of entire stack ---\n"; static const char BACKTRACE_STOP[] = "<2>--- backtrace finished ---\n"; static char *ltrim(char *s) { while (' ' == *s) { s++; } return s; } void Backtracer::print() { int child_pid = ::fork(); if (child_pid == 0) { // redirect stdout to stderr ::dup2(2, 1); // create buffer for parent pid (2+16+1 spaces to allow up to a 64 bit hex parent pid) char pid_buf[32]; const char* stem = " "; const char* s = stem; char* d = &pid_buf[0]; while (static_cast(*s)) { *d++ = *s++; } *d-- = '\0'; char* hexppid = d; // write parent pid to buffer and prefix with 0x int ppid = getppid(); while (ppid != 0) { *hexppid = ((ppid & 0xF) + '0'); if(*hexppid > '9') { *hexppid += 'a' - '0' - 10; } --hexppid; ppid >>= 4; } *hexppid-- = 'x'; *hexppid = '0'; // invoke GDB char name_buf[512]; name_buf[::readlink("/proc/self/exe", &name_buf[0], 511)] = 0; ssize_t r = ::write(STDERR_FILENO, &BACKTRACE_START[0], sizeof(BACKTRACE_START)); (void)r; ::execl("/usr/bin/gdb", "/usr/bin/gdb", "--batch", "-n", "-ex", "thread apply all bt full", "-ex", "quit", &name_buf[0], ltrim(&pid_buf[0]), nullptr); ::exit(1); // if GDB failed to start } else if (child_pid == -1) { ::exit(1); // if forking failed } else { // make it work for non root users if (0 != getuid()) { ::prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0); } ::waitpid(child_pid, nullptr, 0); ssize_t r = ::write(STDERR_FILENO, &BACKTRACE_STOP[0], sizeof(BACKTRACE_STOP)); (void)r; } } 

minidump_stackwalk:

 static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded) { int child_pid = ::fork(); if (child_pid == 0) { ::dup2(open("/dev/null", O_WRONLY), 2); // ignore verbose output on stderr ssize_t r = ::write(STDOUT_FILENO, &MINIDUMP_STACKWALK_START[0], sizeof(MINIDUMP_STACKWALK_START)); (void)r; ::execl("/usr/bin/minidump_stackwalk", "/usr/bin/minidump_stackwalk", descriptor.path(), "/usr/share/breakpad-syms", nullptr); ::exit(1); // if minidump_stackwalk failed to start } else if (child_pid == -1) { ::exit(1); // if forking failed } else { ::waitpid(child_pid, nullptr, 0); ssize_t r = ::write(STDOUT_FILENO, &MINIDUMP_STACKWALK_STOP[0], sizeof(MINIDUMP_STACKWALK_STOP)); (void)r; } ::remove(descriptor.path()); // this is not signal safe anymore but should still work return succeeded; } 

Bearbeiten: Um es für Breakpad arbeiten zu können, musste ich auch folgendes hinzufügen:

 std::set_terminate([]() { ssize_t r = ::write(STDERR_FILENO, EXCEPTION, sizeof(EXCEPTION)); (void)r; google_breakpad::ExceptionHandler::WriteMinidump(std::string("/tmp"), dumpCallback, NULL); exit(1); // avoid creating a second dump by not calling std::abort }); 

Quelle: Wie erhält man eine Stack-Trace für C ++ mit gcc mit Zeilennummer Informationen? und ist es möglich, gdb an einen abgestürzten process anzuhängen (aka “Just-in-Time” Debugging)

Cpp-tool ex_diag – easyweight, multiplatform, minimale Ressourcennutzung, einfach und flexibel bei Trace.

Der folgende Code stoppt die Ausführung direkt nach dem Auslösen einer Ausnahme. Sie müssen einen windows_exception_handler zusammen mit einem Beendigungshandler setzen. Ich habe das in MinGW 32Bits getestet.

 void beforeCrash(void); static const bool SET_TERMINATE = std::set_terminate(beforeCrash); void beforeCrash() { __asm("int3"); } int main(int argc, char *argv[]) { SetUnhandledExceptionFilter(windows_exception_handler); ... } 

Überprüfen Sie den folgenden Code für die function windows_exception_handler: http://www.codedisqus.com/0ziVPgVPUk/exception-handling-and-stacktrace-under-windows-mingwgcc.html