Wie automatisch einen Stacktrace, wenn mein Programm abstürzt erzeugen
-
09-06-2019 - |
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?
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":
-
Ausführen innerhalb catchsegv:
$ catchsegv program -o hai
-
Link mit libSegFault zur Laufzeit:
$ LD_PRELOAD=/lib/libSegFault.so program -o hai
-
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:
- Übersetzen Sie Ihr Programm mit „-g“ Flag für Debug-Symbole in binäre Einbettung (nicht befürchten Ihre Leistung nicht beeinflussen).
- Unter Linux nächsten Befehl ausführen. "Ulimit -c unlimited" - ermöglichen System große Crash-Dumps machen
- Wenn Ihr Programm abgestürzt ist, im Arbeitsverzeichnis werden Sie sehen, Datei „Kern“.
- 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
win: Wie wäre es StackWalk64 http://msdn.microsoft.com/en -US / library / ms680650.aspx
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
- Erstellen Sie eine „coredumps“ Ordner im Home-Ordner des Benutzers
- 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.
- Hinweis: „* weicher Kern unbegrenzt“ root gehören nicht, weshalb Wurzel in einer eigenen Zeile angegeben werden muss .
- , diese Werte zu überprüfen, sich abzumelden, melden Sie sich wieder in, und geben Sie „ulimit -a“. „Kerndateigröße“ sollte auf unbegrenzt eingestellt werden.
- Ü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.
- Ö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)
- 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.
- 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:
Unter Linux / Unix / Mac OS X verwenden Core-Dateien (Sie können sie mit ulimit aktivieren oder
vergaß ich das GNOME-Tech von „Apport“, aber ich weiß nicht viel über ihn verwenden. Es wird verwendet, stacktraces und andere Diagnose für die Verarbeitung zu erzeugen und Fehler automatisch ablegen kann. Es ist sicherlich lohnt in zu.
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]