Frage

Ich arbeite an Linux mit dem GCC Compiler. Als mein C ++ Programm abstürzt würde Ich mag es automatisch einen Stacktrace zu erzeugen.

Mein Programm läuft von vielen verschiedenen Benutzern wird, und es läuft auch auf Linux, Windows und Macintosh (alle Versionen mit gcc zusammengestellt).

Ich würde mein Programm in der Lage sein möge einen Stack-Trace zu erzeugen, wenn er abstürzt und das nächste Mal, wenn der Benutzer es läuft, wird er sich fragen, ob es in Ordnung ist, den Stack-Trace mir zu senden, so kann ich das Problem aufzuspüren . Ich kann behandeln die die Info an mich zu senden, aber ich weiß nicht, wie die Trace-Zeichenfolge zu erzeugen. Irgendwelche Ideen?

War es hilfreich?

Lösung

Für Linux und ich glaube, Mac OS X, wenn Sie gcc verwenden, oder einen Compiler, der glibc verwendet, können Sie den Backtrace () Funktionen in execinfo.h verwenden einen Stacktrace und Ausfahrt anmutig zu drucken, wenn Sie einen Segmentation Fault erhalten. Die Dokumentation kann in dem libc Handbuch .

Hier ist ein Beispielprogramm, das einen SIGSEGV Handler installiert und druckt einen Stacktrace stderr wenn es Segfaults. Die baz() Funktion hier bewirkt, dass der segfault, die die Handler auslöst:

#include <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>


void handler(int sig) {
  void *array[10];
  size_t size;

  // get void*'s for all entries on the stack
  size = backtrace(array, 10);

  // print out all the frames to stderr
  fprintf(stderr, "Error: signal %d:\n", sig);
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  exit(1);
}

void baz() {
 int *foo = (int*)-1; // make a bad pointer
  printf("%d\n", *foo);       // causes segfault
}

void bar() { baz(); }
void foo() { bar(); }


int main(int argc, char **argv) {
  signal(SIGSEGV, handler);   // install our handler
  foo(); // this will call foo, bar, and baz.  baz segfaults.
}

mit -g -rdynamic Kompilieren bekommt man Info-Symbol in der Ausgabe, die glibc einen schönen Stacktrace machen verwenden kann:

$ gcc -g -rdynamic ./test.c -o test

Ausführen dieses erhält man folgende Ausgabe:

$ ./test
Error: signal 11:
./test(handler+0x19)[0x400911]
/lib64/tls/libc.so.6[0x3a9b92e380]
./test(baz+0x14)[0x400962]
./test(bar+0xe)[0x400983]
./test(foo+0xe)[0x400993]
./test(main+0x28)[0x4009bd]
/lib64/tls/libc.so.6(__libc_start_main+0xdb)[0x3a9b91c4bb]
./test[0x40086a]

Dies zeigt das Lademodul, Offset und Funktion, die jeder Rahmen in dem Stapel kam. Hier können Sie die Signal-Handler oben auf dem Stapel sehen, und die libc-Funktionen vor main neben main, foo, bar und baz.

Andere Tipps

Linux

Während die Verwendung der Backtrace () Funktionen in execinfo.h Stacktrace und Ausfahrt anmutig zu drucken, wenn Sie einen Segmentierungsfehler erhalten hat bereits vorgeschlagen worden ist, sehe ich keinen Hinweis auf die Feinheiten notwendig, um die resultierenden Backtrace zeigt auf die tatsächliche Lage, um sicherzustellen, des Fehlers (zumindest für einige Architekturen - x86 und ARM).

Die ersten beiden Einträge in der Stapelrahmen Kette, wenn Sie in die Signal-Handler bekommen enthalten eine Rücksendeadresse innerhalb der Signal-Handler und eine Innen sigaction () in libc. Der Stapelrahmen der letzten Funktion vor dem Signal bezeichnet (was der Ort des Fehlers ist) verloren.

Code

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif

#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>

/* This structure mirrors the one found in /usr/include/asm/ucontext.h */
typedef struct _sig_ucontext {
 unsigned long     uc_flags;
 struct ucontext   *uc_link;
 stack_t           uc_stack;
 struct sigcontext uc_mcontext;
 sigset_t          uc_sigmask;
} sig_ucontext_t;

void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
{
 void *             array[50];
 void *             caller_address;
 char **            messages;
 int                size, i;
 sig_ucontext_t *   uc;

 uc = (sig_ucontext_t *)ucontext;

 /* Get the address at the time the signal was raised */
#if defined(__i386__) // gcc specific
 caller_address = (void *) uc->uc_mcontext.eip; // EIP: x86 specific
#elif defined(__x86_64__) // gcc specific
 caller_address = (void *) uc->uc_mcontext.rip; // RIP: x86_64 specific
#else
#error Unsupported architecture. // TODO: Add support for other arch.
#endif

 fprintf(stderr, "signal %d (%s), address is %p from %p\n", 
  sig_num, strsignal(sig_num), info->si_addr, 
  (void *)caller_address);

 size = backtrace(array, 50);

 /* overwrite sigaction with caller's address */
 array[1] = caller_address;

 messages = backtrace_symbols(array, size);

 /* skip first stack frame (points here) */
 for (i = 1; i < size && messages != NULL; ++i)
 {
  fprintf(stderr, "[bt]: (%d) %s\n", i, messages[i]);
 }

 free(messages);

 exit(EXIT_FAILURE);
}

int crash()
{
 char * p = NULL;
 *p = 0;
 return 0;
}

int foo4()
{
 crash();
 return 0;
}

int foo3()
{
 foo4();
 return 0;
}

int foo2()
{
 foo3();
 return 0;
}

int foo1()
{
 foo2();
 return 0;
}

int main(int argc, char ** argv)
{
 struct sigaction sigact;

 sigact.sa_sigaction = crit_err_hdlr;
 sigact.sa_flags = SA_RESTART | SA_SIGINFO;

 if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) != 0)
 {
  fprintf(stderr, "error setting signal handler for %d (%s)\n",
    SIGSEGV, strsignal(SIGSEGV));

  exit(EXIT_FAILURE);
 }

 foo1();

 exit(EXIT_SUCCESS);
}

Ausgabe

signal 11 (Segmentation fault), address is (nil) from 0x8c50
[bt]: (1) ./test(crash+0x24) [0x8c50]
[bt]: (2) ./test(foo4+0x10) [0x8c70]
[bt]: (3) ./test(foo3+0x10) [0x8c8c]
[bt]: (4) ./test(foo2+0x10) [0x8ca8]
[bt]: (5) ./test(foo1+0x10) [0x8cc4]
[bt]: (6) ./test(main+0x74) [0x8d44]
[bt]: (7) /lib/libc.so.6(__libc_start_main+0xa8) [0x40032e44]

Alle Gefahren von der Backtrace () aufrufen Funktionen in einem Signal-Handler noch existieren und sollen nicht übersehen werden, aber ich finde die Funktionalität, die ich hier hilfreich ziemlich beschrieben Abstürze beim Debuggen.

Es ist wichtig zu beachten, dass das Beispiel I vorgesehen ist für x86 auf Linux entwickelt / getestet. Ich habe auch diese auf ARM mit uc_mcontext.arm_pc statt uc_mcontext.eip erfolgreich umgesetzt.

Hier ist ein Link zu dem Artikel, wo ich die Details für diese Implementierung gelernt: http://www.linuxjournal.com/article/6391

Es ist noch einfacher als „Mann Backtrace“, gibt es ein wenig dokumentierte Bibliothek (GNU-spezifisch) mit glibc als libSegFault.so verteilt, was wurde ich glaube, von Ulrich Drepper geschrieben wurde das Programm catchsegv (siehe „Mann catchsegv“ zu unterstützen ).

Das gibt uns drei Möglichkeiten. Statt läuft "Programm -o hai":

  1. Ausführen innerhalb catchsegv:

    $ catchsegv program -o hai
    
  2. Link mit libSegFault zur Laufzeit:

    $ LD_PRELOAD=/lib/libSegFault.so program -o hai
    
  3. Link mit libSegFault bei der Kompilierung:

    $ gcc -g1 -lSegFault -o program program.cc
    $ program -o hai
    

In allen drei Fällen werden Sie klarer Backtraces mit weniger Optimierung (gcc -O0 oder -O1) und Debugging-Symbole (gcc -g) erhalten. Andernfalls können Sie nur mit einem Stapel von Speicheradressen enden.

Sie können auch mehr Signale für Stack-Traces mit so etwas wie fangen:

$ export SEGFAULT_SIGNALS="all"       # "all" signals
$ export SEGFAULT_SIGNALS="bus abrt"  # SIGBUS and SIGABRT

Die Ausgabe wird in etwa so aussehen (man beachte die Backtrace am Boden):

*** Segmentation fault Register dump:

 EAX: 0000000c   EBX: 00000080   ECX:
00000000   EDX: 0000000c  ESI:
bfdbf080   EDI: 080497e0   EBP:
bfdbee38   ESP: bfdbee20

 EIP: 0805640f   EFLAGS: 00010282

 CS: 0073   DS: 007b   ES: 007b   FS:
0000   GS: 0033   SS: 007b

 Trap: 0000000e   Error: 00000004  
OldMask: 00000000  ESP/signal:
bfdbee20   CR2: 00000024

 FPUCW: ffff037f   FPUSW: ffff0000  
TAG: ffffffff  IPOFF: 00000000  
CSSEL: 0000   DATAOFF: 00000000  
DATASEL: 0000

 ST(0) 0000 0000000000000000   ST(1)
0000 0000000000000000  ST(2) 0000
0000000000000000   ST(3) 0000
0000000000000000  ST(4) 0000
0000000000000000   ST(5) 0000
0000000000000000  ST(6) 0000
0000000000000000   ST(7) 0000
0000000000000000

Backtrace:
/lib/libSegFault.so[0xb7f9e100]
??:0(??)[0xb7fa3400]
/usr/include/c++/4.3/bits/stl_queue.h:226(_ZNSt5queueISsSt5dequeISsSaISsEEE4pushERKSs)[0x805647a]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/player.cpp:73(_ZN6Player5inputESs)[0x805377c]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:159(_ZN6Socket4ReadEv)[0x8050698]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:413(_ZN12ServerSocket4ReadEv)[0x80507ad]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:300(_ZN12ServerSocket4pollEv)[0x8050b44]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/main.cpp:34(main)[0x8049a72]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7d1b775]
/build/buildd/glibc-2.9/csu/../sysdeps/i386/elf/start.S:122(_start)[0x8049801]

Wenn Sie die blutigen Details wissen wollen, 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 = Baum; f = debug

Auch wenn ein richtige Antwort wurde bereitgestellt, das beschreibt, wie das GNU libc backtrace() Funktion nutzen 1 und ich zur Verfügung gestellt meine eigene Antwort , der beschreibt, wie man die tatsächlichen Ort des Fehlers ein Backtrace von einem Signal-Handler Punkte, um sicherzustellen, 2 , ich sehe keine Erwähnung von Demangling C ++ Symbole Ausgang von der Backtrace.

Bei Rückverfolgung von einem C ++ Programm zu erhalten, kann der Ausgang durch c++filt ausgeführt werden 1 , um die Symbole demangle oder durch Verwendung von abi::__cxa_demangle 1 direkt.

  • 1 Linux & OS X Beachten Sie, dass c++filt und __cxa_demangle sind GCC spezifische
  • 2 Linux

Das folgende C ++ Linux Beispiel verwendet die gleichen Signal-Handler, wie mein andere Antwort und zeigen, wie c++filt verwendet werden kann, um die Symbole demangle.

Code :

class foo
{
public:
    foo() { foo1(); }

private:
    void foo1() { foo2(); }
    void foo2() { foo3(); }
    void foo3() { foo4(); }
    void foo4() { crash(); }
    void crash() { char * p = NULL; *p = 0; }
};

int main(int argc, char ** argv)
{
    // Setup signal handler for SIGSEGV
    ...

    foo * f = new foo();
    return 0;
}

Output (./test):

signal 11 (Segmentation fault), address is (nil) from 0x8048e07
[bt]: (1) ./test(crash__3foo+0x13) [0x8048e07]
[bt]: (2) ./test(foo4__3foo+0x12) [0x8048dee]
[bt]: (3) ./test(foo3__3foo+0x12) [0x8048dd6]
[bt]: (4) ./test(foo2__3foo+0x12) [0x8048dbe]
[bt]: (5) ./test(foo1__3foo+0x12) [0x8048da6]
[bt]: (6) ./test(__3foo+0x12) [0x8048d8e]
[bt]: (7) ./test(main+0xe0) [0x8048d18]
[bt]: (8) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (9) ./test(__register_frame_info+0x3d) [0x8048981]

Demangled Output (./test 2>&1 | c++filt):

signal 11 (Segmentation fault), address is (nil) from 0x8048e07
[bt]: (1) ./test(foo::crash(void)+0x13) [0x8048e07]
[bt]: (2) ./test(foo::foo4(void)+0x12) [0x8048dee]
[bt]: (3) ./test(foo::foo3(void)+0x12) [0x8048dd6]
[bt]: (4) ./test(foo::foo2(void)+0x12) [0x8048dbe]
[bt]: (5) ./test(foo::foo1(void)+0x12) [0x8048da6]
[bt]: (6) ./test(foo::foo(void)+0x12) [0x8048d8e]
[bt]: (7) ./test(main+0xe0) [0x8048d18]
[bt]: (8) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (9) ./test(__register_frame_info+0x3d) [0x8048981]

In der folgenden basiert auf dem Signal-Handler von meinem ursprüngliche Antwort und kann die Signal-Handler in dem obigen Beispiel ersetzen zu demonstrieren, wie abi::__cxa_demangle können die Symbole demangle verwendet werden. Diese Signal-Handler erzeugen den gleichen demangled Ausgang wie im obigen Beispiel.

Code :

void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
{
    sig_ucontext_t * uc = (sig_ucontext_t *)ucontext;

    void * caller_address = (void *) uc->uc_mcontext.eip; // x86 specific

    std::cerr << "signal " << sig_num 
              << " (" << strsignal(sig_num) << "), address is " 
              << info->si_addr << " from " << caller_address 
              << std::endl << std::endl;

    void * array[50];
    int size = backtrace(array, 50);

    array[1] = caller_address;

    char ** messages = backtrace_symbols(array, size);    

    // skip first stack frame (points here)
    for (int i = 1; i < size && messages != NULL; ++i)
    {
        char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;

        // find parantheses and +address offset surrounding mangled name
        for (char *p = messages[i]; *p; ++p)
        {
            if (*p == '(') 
            {
                mangled_name = p; 
            }
            else if (*p == '+') 
            {
                offset_begin = p;
            }
            else if (*p == ')')
            {
                offset_end = p;
                break;
            }
        }

        // if the line could be processed, attempt to demangle the symbol
        if (mangled_name && offset_begin && offset_end && 
            mangled_name < offset_begin)
        {
            *mangled_name++ = '\0';
            *offset_begin++ = '\0';
            *offset_end++ = '\0';

            int status;
            char * real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);

            // if demangling is successful, output the demangled function name
            if (status == 0)
            {    
                std::cerr << "[bt]: (" << i << ") " << messages[i] << " : " 
                          << real_name << "+" << offset_begin << offset_end 
                          << std::endl;

            }
            // otherwise, output the mangled function name
            else
            {
                std::cerr << "[bt]: (" << i << ") " << messages[i] << " : " 
                          << mangled_name << "+" << offset_begin << offset_end 
                          << std::endl;
            }
            free(real_name);
        }
        // otherwise, print the whole line
        else
        {
            std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
        }
    }
    std::cerr << std::endl;

    free(messages);

    exit(EXIT_FAILURE);
}

Könnte Blick auf Google Breakpad wert sein, ein plattformübergreifende Crash-Dump-Generator und Tools verarbeitet die Deponien.

Sie nicht angeben, Ihr Betriebssystem, so ist dies schwer zu beantworten. Wenn Sie ein System verwenden, auf NYS basieren, könnten Sie in der Lage sein, die libc-Funktion backtrace() zu verwenden.

GCC hat auch zwei fest eingebaute Funktionen, die Ihnen helfen können, die aber möglicherweise nicht vollständig von Ihrer Architektur implementiert wird, und das ist __builtin_frame_address und __builtin_return_address. Von denen beide wollen eine sofortige integere Ebene (durch sofortige, ich meine, es kann keine Variable sein). Wenn __builtin_frame_address für einen bestimmten Pegel nicht Null ist, sollte es sicher sein, um die Rücksprungadresse auf dem gleichen Niveau zu greifen.

ulimit -c <value> setzt die Kerndateigrößenbeschränkung auf Unix. Standardmäßig ist der Kern Dateigrößenbeschränkung 0. Sie Ihre ulimit Werte mit ulimit -a sehen können.

auch, wenn Sie innerhalb gdb Ihr Programm ausführen, wird es Ihr Programm auf „Segmentierungsverletzungen“ stoppen (SIGSEGV, in der Regel, wenn Sie ein Stück Speicher zugegriffen, die Sie nicht zugeteilt worden) oder Sie können Haltepunkte setzen.

ddd und Nemiver sind Frontends für GDB, die damit viel einfacher für die Anfänger machen arbeiten.

Danke für das Zeichnen meine Aufmerksamkeit auf die addr2line Dienstprogramm enthusiasticgeek.

Ich habe einen schnellen und schmutzigen Skript geschrieben, um die Ausgabe der Antwort zur Verfügung gestellt hier zu verarbeiten: (Viel dank jschmier!) Mit dem addr2line Dienstprogramm.

Das Skript übernimmt ein einziges Argument:. Den Namen der Datei, die die Ausgabe von jschmier des Versorgers mit

Die Ausgabe sollte in etwa wie folgt für jede Ebene der Spur drucken

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 

Es ist wichtig zu beachten, dass, sobald Sie eine Core-Datei generieren werden Sie das GDB-Tool betrachten sie verwenden müssen. Für gdb Sinn Ihrer Core-Datei zu machen, Sie gcc Instrument, um die Binärdatei mit Debugging-Symbolen sagen müssen:, dies zu tun, Sie mit der Option -g kompilieren:

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

Dann können Sie entweder „ulimit -c unlimited“ lassen es einen Core-Dumps, oder einfach nur Ihr Programm in gdb laufen. Ich mag den zweiten Ansatz:

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

Ich hoffe, das hilft.

Ive auf dieses Problem für eine Weile suchen.

Und tief in dem Google-Performance Tools README begraben

http://code.google.com/p/ google-perftools / source / browse / trunk / README

spricht über libunwind

http://www.nongnu.org/libunwind/

Würde gerne Meinungen dieser Bibliothek hören.

Das Problem mit der Option -rdynamic ist, dass es die Größe der binären relativ deutlich in einigen Fällen zu erhöhen

Einige Versionen von libc-Funktionen enthalten, die mit Stack-Traces befassen; Sie könnten in der Lage sein, sie zu benutzen:

http://www.gnu.org/software/libc/ manual / html_node / Backtraces.html

Ich erinnere mich, mit libunwind vor langer Zeit Stack-Traces zu bekommen, aber es wird möglicherweise nicht unterstützt auf Ihrer Plattform.

Vergessen Sie Ihre Quellen zu ändern und einige Hacks mit Backtrace () Funktion oder macroses zu tun -. Dies sind nur schlechte Lösungen

Als richtig funktionierende Lösung, würde ich empfehlen:

  1. Übersetzen Sie Ihr Programm mit „-g“ Flag für Debug-Symbole in binäre Einbettung (nicht befürchten Ihre Leistung nicht beeinflussen).
  2. Unter Linux nächsten Befehl ausführen. "Ulimit -c unlimited" - ermöglichen System große Crash-Dumps machen
  3. Wenn Ihr Programm abgestürzt ist, im Arbeitsverzeichnis werden Sie sehen, Datei „Kern“.
  4. Ausführen nächsten Befehl zum Drucken Backtrace zu stdout: GDB -batch -ex "Backtrace" ./your_program_exe ./core

Dies wird in lesbarer Weise (mit Source-Dateinamen und Zeilennummern) proper lesbar Backtrace des Programms drucken. Darüber hinaus wird dieser Ansatz Freiheit gibt Ihnen Ihr System zu automatisieren: habe ein kurzes Skript, das prüft, ob Verfahren zur Herstellung eines Core Dump erstellt und dann Backtraces per E-Mail an Entwickler schicken, oder melden Sie sich dies in ein Logging-System.

ulimit -c unlimited

ist eine Systemvariable, wich einen Core-Dump nach Ihrer Anwendung abstürzt erstellen können. In diesem Fall kann eine unbegrenzte Menge. Suchen Sie nach einer Datei Kern im selben Verzeichnis. Stellen Sie sicher, dass Sie Ihren Code mit Debug-Informationen kompiliert aktiviert!

Bezug

Sie können mit DeathHandler - kleine C ++ Klasse, die alles für Sie tut, zuverlässig .

Lesen Sie:

Mann 3 Backtrace

Und:

#include <exeinfo.h>
int backtrace(void **buffer, int size);

Dies sind GNU-Erweiterungen.

Sehen Sie die Stack-Trace-Anlage in ACE (ADAPTIVE Communication Environment). Es ist bereits geschrieben, alle wichtigen Plattformen zu decken (und mehr). Die Bibliothek ist BSD-Lizenz, so können Sie auch den Code kopieren / einfügen, wenn Sie nicht ACE verwenden wollen.

Ich kann mit der Linux-Version helfen: die Funktion Backtrace, backtrace_symbols und backtrace_symbols_fd verwendet werden. Siehe die entsprechenden Handbuchseiten.

* nichts: Sie können SIGSEGV (usualy dieses Signal vor dem Absturz ausgelöst wird) und die Informationen in einer Datei halten . (Neben der Kern-Datei können Sie zum Debuggen verwenden gdb zum Beispiel verwendet wird).

win: Prüfen Sie dieser von Msdn.

Sie können auch im Chrome-Code von Google sehen, wie es behandelt Abstürze. Es hat eine schöne Ausnahmebehandlung.

Ich fand, dass @tgamblin Lösung ist nicht vollständig. Es kann nicht mit Stackoverflow behandeln. Ich denke, da standardmäßig Signal-Handler mit dem gleichen Stapel aufgerufen wird und SIGSEGV wird zweimal geworfen. Zum Schutz einen unabhängigen Stack für die Signal-Handler registrieren.

Sie können dies im folgenden Code überprüfen. Standardmäßig schlägt der Handler. Mit definierten Makro STACK_OVERFLOW ist es gut.

#include <iostream>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <cassert>

using namespace std;

//#define STACK_OVERFLOW

#ifdef STACK_OVERFLOW
static char stack_body[64*1024];
static stack_t sigseg_stack;
#endif

static struct sigaction sigseg_handler;

void handler(int sig) {
  cerr << "sig seg fault handler" << endl;
  const int asize = 10;
  void *array[asize];
  size_t size;

  // get void*'s for all entries on the stack
  size = backtrace(array, asize);

  // print out all the frames to stderr
  cerr << "stack trace: " << endl;
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  cerr << "resend SIGSEGV to get core dump" << endl;
  signal(sig, SIG_DFL);
  kill(getpid(), sig);
}

void foo() {
  foo();
}

int main(int argc, char **argv) {
#ifdef STACK_OVERFLOW
  sigseg_stack.ss_sp = stack_body;
  sigseg_stack.ss_flags = SS_ONSTACK;
  sigseg_stack.ss_size = sizeof(stack_body);
  assert(!sigaltstack(&sigseg_stack, nullptr));
  sigseg_handler.sa_flags = SA_ONSTACK;
#else
  sigseg_handler.sa_flags = SA_RESTART;  
#endif
  sigseg_handler.sa_handler = &handler;
  assert(!sigaction(SIGSEGV, &sigseg_handler, nullptr));
  cout << "sig action set" << endl;
  foo();
  return 0;
} 

würde ich den Code verwenden, die für durchgesickert Speicher in von Visual Leak Detector eine Stack-Trace erzeugt . Dies funktioniert nur auf Win32, though.

Ich habe eine Menge Antworten hier zu sehen, ein Signal-Handler durchführt und dann verlassen. Das ist der Weg zu gehen, aber denken Sie daran eine sehr wichtige Tatsache: Wenn Sie den Core-Dump für den erzeugten Fehler erhalten möchten, können Sie nicht exit(status) nennen. Rufen Sie abort() statt!

Der neue König in der Stadt angekommen https://github.com/bombela/backward-cpp

1 Header im Code und 1 Bibliothek zu platzieren zu installieren.

persönlich nenne ich es diese Funktion mit

#include "backward.hpp"
void stacker() {

using namespace backward;
StackTrace st;


st.load_here(99); //Limit the number of trace depth to 99
st.skip_n_firsts(3);//This will skip some backward internal function from the trace

Printer p;
p.snippet = true;
p.object = true;
p.color = true;
p.address = true;
p.print(st, stderr);
}

Zusätzlich zu den oben Antworten hier, wie Sie machen Debian Linux OS Core-Dump erzeugen

  1. Erstellen Sie eine „coredumps“ Ordner im Home-Ordner des Benutzers
  2. Zum /etc/security/limits.conf. Unterhalb der ‚‘ Zeile „weicher Kern unbegrenzt“ und „root weichen Kern unbegrenzt“, wenn ermöglicht Kern für root-Dumps, unbegrenzten Raum zu ermöglichen, für Core-Dumps.
  3. Hinweis: „* weicher Kern unbegrenzt“ root gehören nicht, weshalb Wurzel in einer eigenen Zeile angegeben werden muss
  4. .
  5. , diese Werte zu überprüfen, sich abzumelden, melden Sie sich wieder in, und geben Sie „ulimit -a“. „Kerndateigröße“ sollte auf unbegrenzt eingestellt werden.
  6. Überprüfen Sie die Bashrc Dateien (Benutzer, und root falls zutreffend), um sicherzustellen, dass ulimit nicht dort festgelegt ist. Andernfalls wird der Wert oben beim Start überschreibt.
  7. Öffnen /etc/sysctl.conf. Geben Sie folgende an der Unterseite: „kernel.core_pattern = /home//coredumps/%e_%t.dump“. (% E wird der Prozessname sein, und% t wird die Systemzeit sein)
  8. Beenden und geben Sie „sysctl -p“, um die neue Konfiguration zu laden Überprüfen / proc / sys / kernel / core_pattern und stellen Sie sicher, dass dieser übereinstimmt, was Sie gerade eingegeben.
  9. Kern Dumping kann durch Ausführen eines Prozesses auf der Kommandozeile ( „&“), und dann mit „kill -11“ Tötung getestet werden. Wenn Kern Dumping erfolgreich ist, werden Sie sehen „(core dumped)“ nach der Segmentierung Fehleranzeige.

Als reines Windows-Lösung können Sie das äquivalent einen Stack-Trace (mit viel, viel mehr Informationen) erhalten mit Windows-Fehlerberichterstattung . Mit nur wenigen Einträgen in der Registry, kann es so eingerichtet werden, href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb787181.aspx" sammeln User-Mode-Dumps :

  

Beginnend mit Windows Server 2008 und Windows Vista mit Service Pack 1 (SP1), Windows Error Reporting (WER) kann so konfiguriert werden, so dass ein vollständige Benutzermodus Deponien gesammelt und lokal nach einer Benutzermodus-Anwendung abstürzt gespeichert. [...]

     

Diese Funktion ist standardmäßig nicht aktiviert. die Funktion aktivieren, sind Administratorrechte erforderlich. So aktivieren und die Funktion zu konfigurieren, verwenden Sie die folgenden Registrierungswerte unter dem HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows \ Windows-Fehlerberichterstattung \ LocalDumps drücken.

Sie können die Registrierungseinträge von Ihrem Installateur festgelegt, die die erforderlichen Rechte besitzt.

Erstellen eines Benutzermodus-Dump folgende Vorteile gegenüber Erzeugen eines Stack-Trace auf dem Client hat:

  • Es ist bereits im System implementiert. Sie können entweder WER verwenden, wie oben beschrieben, oder rufen Sie MiniDumpWriteDump Sie sich, wenn Sie brauchen mehr feinkörnige Kontrolle über die Menge an Informationen zu entleeren. (Stellen Sie sicher, dass es von einem anderen Prozess nennen.)
  • Way vollständiger als ein Stack-Trace. Unter anderem kann es lokale Variablen enthält, Funktionsargumente, Stacks für andere Threads, geladenen Module, und so weiter. Die Datenmenge (und damit Größe) ist hochgradig anpassbar.
  • Keine Notwendigkeit Debug-Symbole zu versenden. Diese beiden drastisch verringert die Größe Ihrer Bereitstellung sowie macht es schwieriger, Ihre Anwendung ein Reverse-Engineering.
  • Weitgehend unabhängig von der Compiler Sie verwenden. WER Mit erfordert nicht einmal einen Code. So oder so, eine Möglichkeit, eine Symboldatenbank (PDB) zu erhalten, die ist sehr nützlich für die Offline-Analyse. Ich glaube, dass GCC kann entweder HVE erzeugen, oder gibt es Tools, die Symboldatenbank in das PDB-Format zu konvertieren.

Beachten Sie, dass WER kann nur durch einen Absturz der Anwendung ausgelöst werden (das heißt das System ein Verfahren aufgrund einer nicht behandelten Ausnahme beendet). MiniDumpWriteDump kann jederzeit aufgerufen werden. Dies kann hilfreich sein, wenn Sie den aktuellen Zustand entleeren müssen Probleme zu diagnostizieren andere als ein Absturz.

Pflichtlektüre, wenn Sie die Anwendbarkeit von Mini bewerten wollen Dumps:

Es sieht aus wie in einem der letzten C ++ Version erschienen Bibliothek steigern genau das, was Sie wollen, wahrscheinlich wäre der Code Multi-Plattform. Es ist boost :: stacktrace , wo Du verwenden wie wie im 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
}

In Linux Sie kompilieren den Code oben:

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

Wenn Sie es noch hinwollen allein als ich dich gegen bfd verknüpfen können und mit addr2line zu vermeiden, wie ich hier getan haben:

https://github.com/gnif /LookingGlass/blob/master/common/src/crash.linux.c

Dies erzeugt die Ausgabe:

[E]        crash.linux.c:170  | crit_err_hdlr                  | ==== FATAL CRASH (a12-151-g28b12c85f4+1) ====
[E]        crash.linux.c:171  | crit_err_hdlr                  | signal 11 (Segmentation fault), address is (nil)
[E]        crash.linux.c:194  | crit_err_hdlr                  | [trace]: (0) /home/geoff/Projects/LookingGlass/client/src/main.c:936 (register_key_binds)
[E]        crash.linux.c:194  | crit_err_hdlr                  | [trace]: (1) /home/geoff/Projects/LookingGlass/client/src/main.c:1069 (run)
[E]        crash.linux.c:194  | crit_err_hdlr                  | [trace]: (2) /home/geoff/Projects/LookingGlass/client/src/main.c:1314 (main)
[E]        crash.linux.c:199  | crit_err_hdlr                  | [trace]: (3) /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7f8aa65f809b]
[E]        crash.linux.c:199  | crit_err_hdlr                  | [trace]: (4) ./looking-glass-client(_start+0x2a) [0x55c70fc4aeca]
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top