691 Stimmen

Wie kann ich automatisch einen Stacktrace erzeugen, wenn mein Programm abstürzt?

Ich arbeite unter Linux mit dem GCC-Compiler. Wenn mein C++-Programm abstürzt, möchte ich, dass es automatisch einen Stacktrace erzeugt.

Mein Programm wird von vielen verschiedenen Benutzern ausgeführt und läuft auch unter Linux, Windows und Macintosh (alle Versionen wurden mit gcc ).

Ich möchte, dass mein Programm in der Lage ist, einen Stack-Trace zu erzeugen, wenn es abstürzt, und dass der Benutzer beim nächsten Mal, wenn er es ausführt, gefragt wird, ob es in Ordnung ist, den Stack-Trace an mich zu senden, damit ich das Problem aufspüren kann. Das Senden der Informationen an mich ist kein Problem, aber ich weiß nicht, wie ich den Trace-String erzeugen kann. Hat jemand eine Idee?

4 Stimmen

Backtrace und backtrace_symbols_fd sind nicht async-signal-safe. Sie sollten diese Funktionen nicht in Signalhandlern verwenden

13 Stimmen

Backtrace_symbols ruft malloc auf und darf daher nicht in einem Signalhandler verwendet werden. Die beiden anderen Funktionen (backtrace und backtrace_symbols_fd) haben dieses Problem nicht und werden üblicherweise in Signalhandlern verwendet.

3 Stimmen

@cmccabe das ist falsch backtrace_symbols_fd ruft normalerweise nicht malloc auf, aber es kann sein, dass etwas in seinem catch_error-Block schief geht

22voto

Brian Mitchell Punkte 2252

Sie haben Ihr Betriebssystem nicht angegeben, daher ist es schwierig, diese Frage zu beantworten. Wenn Sie ein System verwenden, das auf gnu libc basiert, können Sie vielleicht die libc-Funktion verwenden backtrace() .

GCC hat auch zwei Builtins, die Ihnen helfen können, die aber auf Ihrer Architektur möglicherweise nicht vollständig implementiert sind, und zwar __builtin_frame_address y __builtin_return_address . Beide wollen einen unmittelbaren ganzzahligen Wert (mit unmittelbar meine ich, dass es sich nicht um eine Variable handeln kann). Wenn __builtin_frame_address für eine bestimmte Ebene ungleich Null ist, sollte es sicher sein, die Rücksprungadresse der gleichen Ebene zu verwenden.

14voto

arr_sea Punkte 819

Vielen Dank an enthusiasticgeek, der mich auf das Dienstprogramm addr2line aufmerksam gemacht hat.

Ich habe ein schnelles und schmutziges Skript geschrieben, um die Ausgabe der bereitgestellten Antwort zu verarbeiten aquí : (Vielen Dank an jschmier!) unter Verwendung des Dienstprogramms addr2line.

Das Skript akzeptiert ein einziges Argument: Der Name der Datei, die die Ausgabe des Dienstprogramms von jschmier enthält.

Die Ausgabe sollte für jede Ebene der Ablaufverfolgung in etwa wie folgt aussehen:

BACKTRACE:  testExe 0x8A5db6b
FILE:       pathToFile/testExe.C:110
FUNCTION:   testFunction(int) 
   107  
   108           
   109           int* i = 0x0;
  *110           *i = 5;
   111      
   112        }
   113        return i;

Code:

#!/bin/bash

LOGFILE=$1

NUM_SRC_CONTEXT_LINES=3

old_IFS=$IFS  # save the field separator           
IFS=$'\n'     # new field separator, the end of line           

for bt in `cat $LOGFILE | grep '\[bt\]'`; do
   IFS=$old_IFS     # restore default field separator 
   printf '\n'
   EXEC=`echo $bt | cut -d' ' -f3 | cut -d'(' -f1`  
   ADDR=`echo $bt | cut -d'[' -f3 | cut -d']' -f1`
   echo "BACKTRACE:  $EXEC $ADDR"
   A2L=`addr2line -a $ADDR -e $EXEC -pfC`
   #echo "A2L:        $A2L"

   FUNCTION=`echo $A2L | sed 's/\<at\>.*//' | cut -d' ' -f2-99`
   FILE_AND_LINE=`echo $A2L | sed 's/.* at //'`
   echo "FILE:       $FILE_AND_LINE"
   echo "FUNCTION:   $FUNCTION"

   # print offending source code
   SRCFILE=`echo $FILE_AND_LINE | cut -d':' -f1`
   LINENUM=`echo $FILE_AND_LINE | cut -d':' -f2`
   if ([ -f $SRCFILE ]); then
      cat -n $SRCFILE | grep -C $NUM_SRC_CONTEXT_LINES "^ *$LINENUM\>" | sed "s/ $LINENUM/*$LINENUM/"
   else
      echo "File not found: $SRCFILE"
   fi
   IFS=$'\n'     # new field separator, the end of line           
done

IFS=$old_IFS     # restore default field separator

14voto

ulimit -c <value> setzt das Limit für die Größe der Kerndateien unter Unix. Standardmäßig ist das Größenlimit für Kerndateien 0. Sie können Ihre ulimit Werte mit ulimit -a .

Wenn Sie Ihr Programm in gdb ausführen, wird es bei "Segmentierungsverletzungen" angehalten ( SIGSEGV (in der Regel, wenn Sie auf ein Stück Speicher zugreifen, das Sie nicht zugewiesen haben) oder Sie können Haltepunkte setzen.

ddd und nemiver sind Frontends für gdb, die die Arbeit mit gdb für den Anfänger wesentlich erleichtern.

6 Stimmen

Kernspeicherauszüge sind unendlich nützlicher als Stacktraces, da Sie den Kernspeicherauszug in den Debugger laden und den Zustand des gesamten Programms und seiner Daten zum Zeitpunkt des Absturzes sehen können.

1 Stimmen

Die von anderen vorgeschlagene Rückverfolgungsfunktion ist wahrscheinlich besser als gar nichts, aber sie ist sehr einfach - sie gibt nicht einmal Zeilennummern an. Mit Core Dumps hingegen können Sie rückwirkend den gesamten Zustand Ihrer Anwendung zum Zeitpunkt des Absturzes einsehen (einschließlich eines detaillierten Stack Trace). Dort könnte Es gibt zwar praktische Probleme bei dem Versuch, dies für das Debugging im Feld zu verwenden, aber es ist definitiv ein leistungsfähigeres Werkzeug für die Analyse von Abstürzen und Asserts während der Entwicklung (zumindest unter Linux).

13voto

Benson Punkte 22021

Es ist wichtig zu beachten, dass Sie nach dem Erzeugen einer Core-Datei das gdb-Tool verwenden müssen, um sie zu betrachten. Damit gdb Ihre Core-Datei verstehen kann, müssen Sie gcc anweisen, die Binärdatei mit Debugging-Symbolen zu instrumentieren: Dazu kompilieren Sie mit dem Flag -g:

$ g++ -g prog.cpp -o prog

Dann können Sie entweder "ulimit -c unlimited" setzen, um einen Kern auszulagern, oder Ihr Programm einfach in gdb ausführen. Ich mag den zweiten Ansatz mehr:

$ gdb ./prog
... gdb startup output ...
(gdb) run
... program runs and crashes ...
(gdb) where
... gdb outputs your stack trace ...

Ich hoffe, das hilft.

5 Stimmen

Sie können auch anrufen gdb direkt aus Ihrem abgestürzten Programm. Einrichten eines Handlers für SIGSEGV, SEGILL, SIGBUS, SIGFPE, der gdb aufruft. Details: stackoverflow.com/questions/3151779/ Der Vorteil ist, dass Sie eine schöne, kommentierte Rückverfolgung wie in bt full können Sie auch Stack Traces aller Threads erhalten.

0 Stimmen

Sie können Backtrace auch einfacher als in der Antwort erhalten: gdb -silent ./prog core --eval-command=backtrace --batch -es würde Backtrace anzeigen und den Debugger schließen

13voto

baziorek Punkte 2147

Es sieht aus wie in einer der letzten C++-Boost-Version erschien Bibliothek, um genau das, was Sie wollen, wahrscheinlich der Code wäre Multiplattform sein. Es ist boost::stacktrace , die Sie wie folgt verwenden können wie bei der Boost-Probe :

#include <filesystem>
#include <sstream>
#include <fstream>
#include <signal.h>     // ::signal, ::raise
#include <boost/stacktrace.hpp>

const char* backtraceFileName = "./backtraceFile.dump";

void signalHandler(int)
{
    ::signal(SIGSEGV, SIG_DFL);
    ::signal(SIGABRT, SIG_DFL);
    boost::stacktrace::safe_dump_to(backtraceFileName);
    ::raise(SIGABRT);
}

void sendReport()
{
    if (std::filesystem::exists(backtraceFileName))
    {
        std::ifstream file(backtraceFileName);

        auto st = boost::stacktrace::stacktrace::from_dump(file);
        std::ostringstream backtraceStream;
        backtraceStream << st << std::endl;

        // sending the code from st

        file.close();
        std::filesystem::remove(backtraceFileName);
    }
}

int main()
{
    ::signal(SIGSEGV, signalHandler);
    ::signal(SIGABRT, signalHandler);

    sendReport();
    // ... rest of code
}

Unter Linux kompilieren Sie den obigen Code:

g++ --std=c++17 file.cpp -lstdc++fs -lboost_stacktrace_backtrace -ldl -lbacktrace

Beispiel Backtrace kopiert von Boost-Dokumentation :

0# bar(int) at /path/to/source/file.cpp:70
1# bar(int) at /path/to/source/file.cpp:70
2# bar(int) at /path/to/source/file.cpp:70
3# bar(int) at /path/to/source/file.cpp:70
4# main at /path/to/main.cpp:93
5# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
6# _start

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X