Ich arbeite unter Linux mit dem GCC-Compiler. Wenn mein C ++ - Programm abstürzt, möchte ich, dass es automatisch einen Stacktrace generiert.
Mein Programm wird von vielen verschiedenen Benutzern ausgeführt und läuft auch unter Linux, Windows und Macintosh (alle Versionen werden mit kompiliert gcc
).
Ich möchte, dass mein Programm beim Absturz einen Stack-Trace generieren kann. Wenn der Benutzer ihn das nächste Mal ausführt, werden sie gefragt, ob es in Ordnung ist, den Stack-Trace an mich zu senden, damit ich das Problem aufspüren kann. Ich kann die Informationen an mich senden, weiß aber nicht, wie ich die Ablaufverfolgungszeichenfolge generieren soll. Irgendwelche Ideen?
Antworten:
Wenn Sie unter Linux und Mac OS X gcc oder einen Compiler verwenden, der glibc verwendet, können Sie die Funktionen backtrace () verwenden
execinfo.h
, um einen Stacktrace zu drucken und ordnungsgemäß zu beenden, wenn ein Segmentierungsfehler auftritt. Die Dokumentation finden Sie im libc-Handbuch .Hier ist ein Beispielprogramm, das einen
SIGSEGV
Handler installiert und einen Stacktrace druckt,stderr
wenn er fehlerhaft ist. Diebaz()
Funktion hier verursacht den Segfault, der den Handler auslöst:Beim Kompilieren mit erhalten
-g -rdynamic
Sie Symbolinformationen in Ihrer Ausgabe, mit denen glibc eine schöne Stapelverfolgung erstellen kann:Wenn Sie dies ausführen, erhalten Sie folgende Ausgabe:
Dies zeigt das Lademodul, den Versatz und die Funktion, von denen jeder Frame im Stapel stammt. Hier können Sie die Signal - Handler oben auf dem Stapel sehen, und die libc - Funktionen vor ,
main
zusätzlich zumain
,foo
,bar
, undbaz
.quelle
sigaction()
in libc enthalten. Obwohl Ihr Backtrace korrekt zu sein scheint, habe ich manchmal festgestellt, dass zusätzliche Schritte erforderlich sind, um sicherzustellen, dass der tatsächliche Ort des Fehlers im Backtrace angezeigt wird, da ersigaction()
vom Kernel überschrieben werden kann .catchsegv
ist nicht das, was das OP benötigt, aber es ist fantastisch, um Segmentierungsfehler zu erkennen und alle Informationen zu erhalten.Es ist noch einfacher als "man backtrace", es gibt eine wenig dokumentierte Bibliothek (GNU-spezifisch), die mit glibc als libSegFault.so verteilt wird. Ich glaube, sie wurde von Ulrich Drepper geschrieben, um das Programm catchsegv zu unterstützen (siehe "man catchsegv").
Dies gibt uns 3 Möglichkeiten. Anstatt "program -o hai" auszuführen:
In catchsegv ausführen:
Verknüpfung mit libSegFault zur Laufzeit:
Verknüpfung mit libSegFault zur Kompilierungszeit:
In allen drei Fällen erhalten Sie klarere Rückverfolgungen mit weniger Optimierung (gcc -O0 oder -O1) und Debugging-Symbolen (gcc -g). Andernfalls erhalten Sie möglicherweise nur einen Stapel Speicheradressen.
Sie können auch mehr Signale für Stapelspuren abfangen, z. B.:
Die Ausgabe sieht ungefähr so aus (beachten Sie die Rückverfolgung unten):
Wenn Sie die wichtigsten Details wissen möchten, ist die beste Quelle leider die Quelle: Siehe http://sourceware.org/git/?p=glibc.git;a=blob;f=debug/segfault.c und das übergeordnete Verzeichnis http://sourceware.org/git/?p=glibc.git;a=tree;f=debug
quelle
-Wl,--no-as-needed
die Compiler-Flags hinzufügen . Andernfallsld
wird in der Tat keine Verknüpfung hergestelltlibSegFault
, da erkannt wird, dass die Binärdatei keines ihrer Symbole verwendet.Linux
Die Verwendung der Funktionen backtrace () in execinfo.h zum Drucken eines Stacktraces und zum ordnungsgemäßen Beenden, wenn ein Segmentierungsfehler auftritt, wurde bereits vorgeschlagen , werden die Komplikationen, die erforderlich sind, um sicherzustellen, dass die resultierenden Backtrace-Punkte auf den tatsächlichen Speicherort von nicht erwähnt der Fehler (zumindest für einige Architekturen - x86 & ARM).
Die ersten beiden Einträge in der Stapelrahmenkette, wenn Sie in den Signalhandler gelangen, enthalten eine Rücksprungadresse im Signalhandler und einen in sigaction () in libc. Der Stapelrahmen der zuletzt vor dem Signal aufgerufenen Funktion (der der Ort des Fehlers ist) geht verloren.
Code
Ausgabe
Alle Gefahren beim Aufrufen der Funktionen backtrace () in einem Signalhandler bestehen weiterhin und sollten nicht übersehen werden. Ich finde die hier beschriebene Funktionalität jedoch sehr hilfreich beim Debuggen von Abstürzen.
Es ist wichtig zu beachten, dass das von mir bereitgestellte Beispiel unter Linux für x86 entwickelt / getestet wurde. Ich habe dies auch erfolgreich auf ARM implementiert, indem ich
uc_mcontext.arm_pc
anstelle vonuc_mcontext.eip
.Hier ist ein Link zu dem Artikel, in dem ich die Details für diese Implementierung erfahren habe: http://www.linuxjournal.com/article/6391
quelle
-rdynamic
, um den Linker anzuweisen, alle Symbole, nicht nur die verwendeten, zur dynamischen Symboltabelle hinzuzufügen. Dies ermöglichtbacktrace_symbols()
die Konvertierung von Adressen in Funktionsnamenaddr2line
Befehl irgendwie verwenden, um die genaue Zeile zu erhalten, in der der Absturz aufgetreten ist?glibc
uc_mcontext
enthält kein Feld mit dem Nameneip
. Es gibt jetzt ein Array, das indiziert werden muss,uc_mcontext.gregs[REG_EIP]
ist das Äquivalent.Obwohl eine korrekte Antwort gegeben wurde, die beschreibt, wie die GNU libc-
backtrace()
Funktion 1 verwendet wird, und ich meine eigene Antwort gegeben habe , die beschreibt, wie sichergestellt wird, dass eine Rückverfolgung von einem Signalhandler zum tatsächlichen Ort des Fehlers 2 zeigt , sehe ich nicht jede Erwähnung von entwirrenden C ++ - Symbolen, die vom Backtrace ausgegeben werden.Wenn Sie Backtraces von einem C ++ - Programm erhalten, kann die Ausgabe durch
c++filt
1 ausgeführt werden , um die Symbole zu entwirren, oder indem Sie 1 direkt verwenden.abi::__cxa_demangle
c++filt
und__cxa_demangle
GCC-spezifisch sindDas folgende C ++ Linux-Beispiel verwendet denselben Signalhandler wie meine andere Antwort und zeigt, wie
c++filt
die Symbole entwirrt werden können.Code :
Ausgabe (
./test
):Demangled Output (
./test 2>&1 | c++filt
):Das Folgende baut auf dem Signalhandler aus meiner ursprünglichen Antwort auf und kann den Signalhandler im obigen Beispiel ersetzen, um zu demonstrieren, wie
abi::__cxa_demangle
die Symbole entwirrt werden können. Dieser Signalhandler erzeugt den gleichen entwirrten Ausgang wie im obigen Beispiel.Code :
quelle
std::cerr
,free()
undexit()
alle verletzen Einschränkungen gegen Aufruf nicht-Asynchron-Signal-sichere Anrufe auf POSIX - Systemen. Dieser Code wird Deadlock , wenn der Prozess in jedem Anruf wie fehlschlägtfree()
,malloc()
new
oderdetete
.Vielleicht lohnt sich ein Blick auf Google Breakpad , einen plattformübergreifenden Crash-Dump-Generator und Tools zur Verarbeitung der Dumps.
quelle
Sie haben Ihr Betriebssystem nicht angegeben, daher ist dies schwer zu beantworten. Wenn Sie ein auf gnu libc basierendes System verwenden, können Sie möglicherweise die libc-Funktion verwenden
backtrace()
.GCC verfügt auch über zwei integrierte Funktionen, die Sie unterstützen können, die jedoch möglicherweise vollständig in Ihrer Architektur implementiert sind oder nicht. Diese sind
__builtin_frame_address
und__builtin_return_address
. Beide wollen eine sofortige Ganzzahlstufe (mit sofort meine ich, dass es keine Variable sein kann). Wenn__builtin_frame_address
für eine bestimmte Ebene ungleich Null ist, sollte es sicher sein, die Rücksprungadresse derselben Ebene abzurufen.quelle
Vielen Dank an enthusiasticgeek, dass Sie mich auf das Dienstprogramm addr2line aufmerksam gemacht haben.
Ich habe ein schnelles und schmutziges Skript geschrieben, um die Ausgabe der hier angegebenen Antwort zu verarbeiten : (! Vielen Dank jschmier) das addr2line Dienstprogramm.
Das Skript akzeptiert ein einziges Argument: Der Name der Datei, die die Ausgabe von jschmiers Dienstprogramm enthält.
Die Ausgabe sollte für jede Ebene der Ablaufverfolgung etwa Folgendes ausgeben:
Code:
quelle
ulimit -c <value>
Legt die maximale Dateigröße für Unix fest. Standardmäßig ist die maximale Dateigrößenbeschränkung 0. Sie können Ihreulimit
Werte mit anzeigenulimit -a
.Wenn Sie Ihr Programm in gdb ausführen, wird Ihr Programm bei "Segmentierungsverletzungen" angehalten (
SIGSEGV
im Allgemeinen, wenn Sie auf einen Speicher zugegriffen haben, den Sie nicht zugewiesen haben), oder Sie können Haltepunkte festlegen.ddd und nemiver sind Frontends für gdb, die dem Anfänger die Arbeit damit erheblich erleichtern.
quelle
Es ist wichtig zu beachten, dass Sie nach dem Generieren einer Kerndatei das GDB-Tool verwenden müssen, um sie anzuzeigen. Damit gdb für Ihre Kerndatei einen Sinn ergibt, müssen Sie gcc anweisen, die Binärdatei mit Debugging-Symbolen zu instrumentieren. Dazu kompilieren Sie mit dem Flag -g:
Dann können Sie entweder "ulimit -c unlimited" festlegen, damit ein Core ausgegeben wird, oder Ihr Programm einfach in gdb ausführen. Ich mag den zweiten Ansatz mehr:
Ich hoffe das hilft.
quelle
gdb
direkt von Ihrem Absturzprogramm aus aufrufen . Setup-Handler für SIGSEGV, SEGILL, SIGBUS, SIGFPE, der gdb aufruft. Details: stackoverflow.com/questions/3151779/… Der Vorteil ist, dass Sie eine schöne, mit Anmerkungen versehene Rückverfolgung wie in erhaltenbt full
. Außerdem können Sie Stapelspuren aller Threads abrufen .Ich habe mich eine Weile mit diesem Problem befasst.
Und tief in der Google Performance Tools README vergraben
http://code.google.com/p/google-perftools/source/browse/trunk/README
spricht über libunwind
http://www.nongnu.org/libunwind/
Würde gerne Meinungen von dieser Bibliothek hören.
Das Problem mit -rdynamic ist, dass es in einigen Fällen die Größe der Binärdatei relativ signifikant erhöhen kann
quelle
Einige Versionen von libc enthalten Funktionen, die sich mit Stack-Traces befassen. Sie können sie möglicherweise verwenden:
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
Ich erinnere mich, dass ich libunwind vor langer Zeit verwendet habe, um Stack-Traces abzurufen , aber es wird möglicherweise auf Ihrer Plattform nicht unterstützt.
quelle
Sie können DeathHandler verwenden - eine kleine C ++ - Klasse, die alles für Sie erledigt und zuverlässig ist.
quelle
execlp()
, um addr2line-Aufrufe auszuführen ... wäre schön, vollständig im eigenen Programm zu bleiben (was möglich ist, indem der addr2line-Code in irgendeiner Form eingefügt wird)Vergessen Sie das Ändern Ihrer Quellen und machen Sie einige Hacks mit der Funktion backtrace () oder Makros - dies sind nur schlechte Lösungen.
Als richtig funktionierende Lösung würde ich raten:
Dadurch wird die ordnungsgemäß lesbare Rückverfolgung Ihres Programms auf lesbare Weise gedruckt (mit Quelldateinamen und Zeilennummern). Darüber hinaus gibt Ihnen dieser Ansatz die Freiheit, Ihr System zu automatisieren: Erstellen Sie ein kurzes Skript, das prüft, ob der Prozess einen Core-Dump erstellt hat, und senden Sie dann Backtraces per E-Mail an Entwickler oder melden Sie dies in einem Protokollierungssystem an.
quelle
ist eine Systemvariable, mit der nach einem Absturz Ihrer Anwendung ein Core-Dump erstellt werden kann. In diesem Fall eine unbegrenzte Menge. Suchen Sie im selben Verzeichnis nach einer Datei namens core. Stellen Sie sicher, dass Sie Ihren Code mit aktivierten Debugging-Informationen kompiliert haben!
Grüße
quelle
limit coredumpsize unlimited
Ansehen:
Mann 3 zurückverfolgen
Und:
Dies sind GNU-Erweiterungen.
quelle
Siehe die Stack-Trace-Funktion in ACE (ADAPTIVE Communication Environment). Es wurde bereits geschrieben, um alle wichtigen Plattformen (und mehr) abzudecken. Die Bibliothek ist im BSD-Stil lizenziert, sodass Sie den Code sogar kopieren / einfügen können, wenn Sie ACE nicht verwenden möchten.
quelle
Ich kann mit der Linux-Version helfen: Die Funktionen backtrace, backtrace_symbols und backtrace_symbols_fd können verwendet werden. Siehe die entsprechenden Handbuchseiten.
quelle
Es sieht so aus, als ob in einer der letzten C ++ Boost-Versionen die Bibliothek genau das bietet, was Sie wollen. Wahrscheinlich wäre der Code eine Multiplattform. Es ist boost :: stacktrace , das Sie wie im Boost-Beispiel verwenden können :
Unter Linux kompilieren Sie den obigen Code:
Beispiel-Backtrace aus der Boost-Dokumentation kopiert :
quelle
* nix: Sie können SIGSEGV abfangen (normalerweise wird dieses Signal vor dem Absturz ausgelöst) und die Informationen in einer Datei speichern. (Neben der Kerndatei, mit der Sie beispielsweise mit gdb debuggen können).
win: Überprüfen Sie dies von msdn.
Sie können sich auch den Chrome-Code von Google ansehen, um zu sehen, wie er mit Abstürzen umgeht. Es hat einen schönen Mechanismus zur Behandlung von Ausnahmen.
quelle
Ich habe festgestellt, dass die @ tgamblin-Lösung nicht vollständig ist. Es kann nicht mit Stackoverflow umgehen. Ich denke, weil der Signalhandler standardmäßig mit demselben Stack aufgerufen wird und SIGSEGV zweimal ausgelöst wird. Zum Schutz müssen Sie einen unabhängigen Stapel für den Signalhandler registrieren.
Sie können dies mit dem folgenden Code überprüfen. Standardmäßig schlägt der Handler fehl. Mit dem definierten Makro STACK_OVERFLOW ist alles in Ordnung.
quelle
Der neue König in der Stadt ist angekommen https://github.com/bombela/backward-cpp
1 Header zum Platzieren in Ihrem Code und 1 Bibliothek zum Installieren.
Persönlich nenne ich es mit dieser Funktion
quelle
Ich würde den Code verwenden, der eine Stapelverfolgung für durchgesickerten Speicher in Visual Leak Detector generiert . Dies funktioniert jedoch nur unter Win32.
quelle
Ich habe hier viele Antworten gesehen, die einen Signalhandler ausgeführt und dann beendet haben. Das ist der richtige Weg, aber denken Sie an eine sehr wichtige Tatsache: Wenn Sie den Core-Dump für den generierten Fehler erhalten möchten, können Sie nicht aufrufen
exit(status)
. Rufen Sieabort()
stattdessen an!quelle
Als reine Windows-Lösung können Sie mithilfe der Windows-Fehlerberichterstattung das Äquivalent eines Stack-Trace (mit viel, viel mehr Informationen) abrufen . Mit nur wenigen Registrierungseinträgen kann es so eingerichtet werden, dass Dumps im Benutzermodus erfasst werden :
Sie können die Registrierungseinträge in Ihrem Installationsprogramm festlegen, das über die erforderlichen Berechtigungen verfügt.
Das Erstellen eines Dumps im Benutzermodus bietet gegenüber dem Generieren eines Stack-Trace auf dem Client die folgenden Vorteile:
Beachten Sie, dass WER nur durch einen Anwendungsabsturz ausgelöst werden kann (dh das System beendet einen Prozess aufgrund einer nicht behandelten Ausnahme).
MiniDumpWriteDump
kann jederzeit aufgerufen werden. Dies kann hilfreich sein, wenn Sie den aktuellen Status sichern müssen, um andere Probleme als einen Absturz zu diagnostizieren.Obligatorische Lektüre, wenn Sie die Anwendbarkeit von Mini-Dumps bewerten möchten:
quelle
Zusätzlich zu den obigen Antworten erfahren Sie hier, wie Sie Debian Linux OS dazu bringen, einen Core-Dump zu generieren
quelle
Wenn du es trotzdem alleine machen willst, wie ich es getan habe, kannst du gegen es verlinken
bfd
und es vermeidenaddr2line
wie ich es hier getan habe:https://github.com/gnif/LookingGlass/blob/master/common/src/crash.linux.c
Dies erzeugt die Ausgabe:
quelle
Verwenden Sie unter Linux / Unix / MacOSX Kerndateien (Sie können sie mit ulimit oder einem kompatiblen Systemaufruf aktivieren ). Verwenden Sie unter Windows die Microsoft-Fehlerberichterstattung (Sie können Partner werden und Zugriff auf Ihre Anwendungsabsturzdaten erhalten).
quelle
Ich habe die GNOME-Technologie von "apport" vergessen, weiß aber nicht viel darüber. Es wird verwendet, um Stacktraces und andere Diagnosen für die Verarbeitung zu generieren und kann automatisch Fehler melden. Ein Besuch lohnt sich auf jeden Fall.
quelle