Sto lavorando su Linux con il compilatore GCC. Quando il mio programma C++ si blocca mi piacerebbe che generasse automaticamente uno stacktrace.
Il mio programma viene eseguito da molti utenti diversi e funziona anche su Linux, Windows e Macintosh (tutte le versioni sono compilate usando gcc
).
Vorrei che il mio programma fosse in grado di generare una traccia dello stack quando si blocca e la prossima volta che l'utente lo esegue, chiederà loro se è giusto inviare la traccia dello stack per poter rintracciare il problema. Posso gestire l'invio delle informazioni a me ma non so come generare la stringa di traccia. Qualche idea?
Per Linux e credo che Mac OS X, se si sta utilizzando gcc, o qualsiasi compilatore che usa glibc, è possibile utilizzare le funzioni backtrace () in execinfo.h
per stampare uno stacktrace ed uscire con garbo quando si verifica un errore di segmentazione. La documentazione può essere trovata nel manuale di libc .
Ecco un programma di esempio che installa un gestore SIGSEGV
e stampa uno stacktrace su stderr
quando segfaults. La funzione baz()
qui provoca il segfault che attiva il gestore:
#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.
}
Compilando con -g -rdynamic
ottieni informazioni sui simboli nel tuo output, che glibc può usare per creare uno stacktrace di Nizza:
$ gcc -g -rdynamic ./test.c -o test
L'esecuzione di questo ti dà questo risultato:
$ ./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]
Questo mostra il modulo di carico, l'offset e la funzione da cui proviene ciascun frame nella pila. Qui puoi vedere il gestore del segnale in cima allo stack e le funzioni libc prima di main
oltre a main
, foo
, bar
e baz
.
Mentre l'uso delle funzioni backtrace () in execinfo.h per stampare uno stacktrace ed uscire con garbo quando si verifica un errore di segmentazione ha è già stato suggerito , non vedo alcuna menzione delle complessità necessarie per garantire che i punti di backtrace risultanti vadano a la posizione effettiva dell'errore (almeno per alcune architetture - x86 e ARM).
Le prime due voci nella catena del frame dello stack quando si entra nel gestore del segnale contengono un indirizzo di ritorno all'interno del gestore di segnale e uno all'interno di sigaction () in libc. Il frame di stack dell'ultima funzione chiamata prima del segnale (che è la posizione dell'errore) viene perso.
#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);
}
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]
Tutti i rischi di chiamare le funzioni backtrace () in un gestore di segnale esistono ancora e non dovrebbero essere trascurati, ma trovo la funzionalità che ho descritto qui abbastanza utile nel debug degli arresti anomali.
È importante notare che l'esempio che ho fornito è sviluppato/testato su Linux per x86. Ho anche implementato questo successo su ARM usando uc_mcontext.arm_pc
invece di uc_mcontext.eip
.
Ecco un link all'articolo in cui ho appreso i dettagli di questa implementazione: http://www.linuxjournal.com/article/6391
È ancora più semplice di "man backtrace", c'è una libreria poco documentata (specifica per GNU) distribuita con glibc come libSegFault.so, che credo sia stato scritto da Ulrich Drepper per supportare il programma catchsegv (vedi "man catchsegv").
Questo ci dà 3 possibilità. Invece di eseguire "program -o hai":
Esegui all'interno di catchsegv:
$ catchsegv program -o hai
Collegamento con libSegFault in fase di runtime:
$ LD_PRELOAD=/lib/libSegFault.so program -o hai
Collegamento con libSegFault in fase di compilazione:
$ gcc -g1 -lSegFault -o program program.cc
$ program -o hai
In tutti e 3 i casi, otterrete backtrace più chiari con meno ottimizzazione (gcc -O0 o -O1) e simboli di debug (gcc -g). Altrimenti potresti finire con una pila di indirizzi di memoria.
Puoi anche catturare più segnali per le tracce dello stack con qualcosa di simile:
$ export SEGFAULT_SIGNALS="all" # "all" signals
$ export SEGFAULT_SIGNALS="bus abrt" # SIGBUS and SIGABRT
L'output sarà simile a questo (notare il backtrace in basso):
*** 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]
Se vuoi conoscere i dettagli cruenti, la fonte migliore è sfortunatamente la fonte: vedi http://sourceware.org/git/?p=glibc.git;a=blob;f=debug/segfault.c e la sua directory principale http://sourceware.org/git/?p=glibc.git;a=tree;f=debug
Anche se è stata fornita una risposta corretta che descrive come utilizzare la funzione GNU libc backtrace()
1 e ho fornito la mia risposta che descrive come garantire che un backtrace di un gestore di segnali punti alla posizione effettiva del guasto2, Non vedo alcuna menzione dei simboli demangling C++ emessi dal backtrace.
Quando si ottengono backtrace da un programma C++, l'output può essere eseguito attraverso c++filt
1 per demarcare i simboli o usando abi::__cxa_demangle
1 direttamente.
c++filt
e __cxa_demangle
sono specifici per GCCIl seguente esempio C++ Linux usa lo stesso gestore di segnale del mio other answer e dimostra come c++filt
può essere usato per demangolare i simboli.
Codice:
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]
Quanto segue costruisce sul gestore di segnali dalla mia risposta originale e può sostituire il gestore di segnali nell'esempio precedente per dimostrare come abi::__cxa_demangle
può essere usato per demangolare i simboli. Questo gestore di segnale produce la stessa uscita delimitata dell'esempio precedente.
Codice:
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);
}
Potrebbe essere utile guardare a Google Breakpad , un generatore di dump degli arresti anomali multipiattaforma e strumenti per elaborare i dump.
Non hai specificato il tuo sistema operativo, quindi è difficile rispondere. Se stai usando un sistema basato su gnu libc, potresti essere in grado di usare la funzione backtrace()
di libc.
GCC ha anche due builtin che possono aiutarti, ma che possono o non possono essere implementati completamente sulla tua architettura, e quelli sono __builtin_frame_address
e __builtin_return_address
. Entrambi vogliono un livello intero immediato (per immediato, intendo che non può essere una variabile). Se __builtin_frame_address
per un dato livello è diverso da zero, dovrebbe essere sicuro afferrare l'indirizzo di ritorno dello stesso livello.
ulimit -c <value>
imposta il limite della dimensione del file di base su unix. Per impostazione predefinita, il limite della dimensione del file principale è 0. È possibile visualizzare i valori ulimit
con ulimit -a
.
inoltre, se esegui il tuo programma da gdb, fermerà il tuo programma su "violazioni di segmentazione" (SIGSEGV
, generalmente quando accedi a un pezzo di memoria che non hai allocato) o puoi impostare i breakpoint.
ddd e nemiver sono front-end per gdb che rendono molto più semplice il lavoro con i principianti.
Ho osservato questo problema per un po '.
E sepolto in profondità nel README di Google Performance Tools
http://code.google.com/p/google-perftools/source/browse/trunk/README
parla di libunwind
http://www.nongnu.org/libunwind/
Mi piacerebbe sentire le opinioni di questa biblioteca.
Il problema con -rdynamic è che può aumentare la dimensione del file binario in modo relativamente significativo in alcuni casi
Grazie a entusiasta per aver attirato la mia attenzione sull'utilità addr2line.
Ho scritto uno script veloce e sporco per elaborare l'output della risposta fornita qui : (Grazie mille a jschmier!) Usando l'utilità addr2line.
Lo script accetta un singolo argomento: il nome del file che contiene l'output dall'utilità di jschmier.
L'output dovrebbe stampare qualcosa come il seguente per ogni livello della traccia:
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;
Codice:
#!/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
Alcune versioni di libc contengono funzioni che gestiscono le tracce dello stack; potresti essere in grado di usarli:
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
Ricordo di aver usato libunwind molto tempo fa per ottenere tracce dello stack, ma potrebbe non essere supportato sulla tua piattaforma.
È importante notare che una volta generato un file core è necessario utilizzare lo strumento gdb per guardarlo. Per gdb per dare un senso al tuo file core, devi dire a gcc di utilizzare lo strumento binario con i simboli di debug: per farlo, compila con il flag -g:
$ g++ -g prog.cpp -o prog
Quindi, è possibile impostare "ulimit -c unlimited" per consentire di eseguire il dump di un core, o semplicemente eseguire il programma in gdb. Mi piace il secondo approccio in più:
$ gdb ./prog
... gdb startup output ...
(gdb) run
... program runs and crashes ...
(gdb) where
... gdb outputs your stack trace ...
Spero che questo possa essere d'aiuto.
ulimit -c unlimited
è una variabile di sistema, che permetterà di creare un core dump dopo l'arresto anomalo dell'applicazione. In questo caso un importo illimitato. Cerca un file chiamato core nella stessa directory. Assicurati di aver compilato il tuo codice con le informazioni di debug abilitate!
saluti
Dimenticati di cambiare le tue fonti e fare alcuni hack con la funzione backtrace () o macrose - queste sono solo soluzioni scadenti.
Come soluzione perfettamente funzionante, consiglierei:
Questo stamperà il backtrace leggibile corretto del tuo programma in modo leggibile dall'uomo (con nomi di file e numeri di riga sorgente) .__ Inoltre questo approccio ti darà la libertà di automatizzare il tuo sistema: Avere un breve script che controlla se il processo è stato creato un core dump, quindi inviare i backtrace via e-mail agli sviluppatori o registrarli in qualche sistema di registrazione.
Puoi usare DeathHandler - piccola classe C++ che fa tutto per te, affidabile.
win: Che ne dici di StackWalk64 http://msdn.Microsoft.com/en-us/library/ms680650.aspx
Guarda a:
uomo 3 backtrace
E:
#include <exeinfo.h>
int backtrace(void **buffer, int size);
Queste sono estensioni GNU.
Vedere la funzione Stack Trace in ACE (ADAPTIVE Communication Environment). È già stato scritto per coprire tutte le principali piattaforme (e non solo). La libreria è dotata di licenza BSD, quindi è possibile copiare/incollare il codice anche se non si desidera utilizzare ACE.
Posso aiutare con la versione di Linux: la funzione backtrace, backtrace_symbols e backtrace_symbols_fd possono essere usati. Vedi le pagine di manuale corrispondenti.
* nix: puoi intercettare SIGSEGV (normalmente questo segnale viene generato prima di bloccarsi) e mantenere le informazioni in un file. (oltre al file principale che puoi usare per eseguire il debug usando gdb per esempio).
vinci: Controlla questo da msdn.
Puoi anche guardare il codice Chrome di Google per vedere come gestisce gli arresti anomali. Ha un bel meccanismo di gestione delle eccezioni.
Ho trovato che la soluzione @tgamblin non è completa . Non può gestire con stackoverflow . Penso che, per impostazione predefinita, il gestore di segnale sia chiamato con lo stesso stack e SIGSEGV sia lanciato due volte. Per proteggere è necessario registrare uno stack indipendente per il gestore del segnale.
Puoi controllare questo con il codice qui sotto. Di default il gestore fallisce. Con la macro definita STACK_OVERFLOW va tutto bene.
#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;
}
Ho visto molte risposte qui eseguire un gestore di segnale e poi uscire . Questa è la strada da percorrere, ma ricorda un fatto molto importante: se vuoi ottenere il core dump per l'errore generato, non puoi chiamare exit(status)
. Chiama invece abort()
!
Il nuovo re in città è arrivato https://github.com/bombela/backward-cpp
1 intestazione da inserire nel codice e 1 libreria da installare.
Personalmente lo chiamo utilizzando questa funzione
#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);
}
Vorrei usare il codice che genera una traccia dello stack per la perdita di memoria in Visual Leak Detector . Questo funziona solo su Win32, comunque.
Come soluzione solo per Windows, è possibile ottenere l'equivalente di una traccia di stack (con molte, molte più informazioni) usando Segnalazione errori di Windows . Con solo poche voci di registro, può essere impostato per raccogliere dump in modalità utente :
A partire da Windows Server 2008 e Windows Vista con Service Pack 1 (SP1), è possibile configurare Segnalazione errori Windows (WER) in modo che i dump completi in modalità utente vengano raccolti e archiviati localmente dopo l'arresto anomalo dell'applicazione in modalità utente. [...]
Questa funzione non è abilitata per impostazione predefinita. L'abilitazione della funzione richiede i privilegi di amministratore. Per abilitare e configurare la funzione, utilizzare i seguenti valori di registro sotto la chiave HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps.
È possibile impostare le voci del registro dal proprio programma di installazione, che dispone dei privilegi richiesti.
La creazione di un dump in modalità utente presenta i seguenti vantaggi rispetto alla generazione di una traccia di stack sul client:
Prendi nota che il WER può essere attivato solo da un arresto anomalo dell'applicazione (ad esempio il sistema che chiude un processo a causa di un'eccezione non gestita). MiniDumpWriteDump
può essere chiamato in qualsiasi momento. Ciò può essere utile se è necessario scaricare lo stato corrente per diagnosticare problemi diversi da un arresto anomalo.
Lettura obbligatoria, se si desidera valutare l'applicabilità delle mini discariche:
Oltre alle risposte di cui sopra, ecco come si fa in modo che il SO Linux di Debian generi il core dump
Su Linux/unix/MacOSX usa i file core (puoi abilitarli con ulimit o system call compatibile ). Su Windows usa segnalazione degli errori di Microsoft (puoi diventare un partner e ottenere l'accesso ai dati del crash dell'applicazione).
Se vuoi continuare da solo come ho fatto, puoi collegarti a bfd
ed evitare di usare addr2line
come ho fatto qui:
https://github.com/gnif/LookingGlass/blob/master/common/src/crash.linux.c
Questo produce l'output:
[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]
Ho dimenticato la tecnologia GNOME di "apport", ma non ne so molto di usarla. Viene utilizzato per generare stacktraces e altri strumenti diagnostici per l'elaborazione e può automaticamente archiviare i bug. Vale sicuramente la pena di fare il check-in.
Se il programma si arresta in modo anomalo, è il sistema operativo stesso che genera le informazioni di arresto anomalo. Se stai usando un sistema operativo * nix, devi semplicemente non impedirgli di farlo (controlla le opzioni 'coredump' del comando ulimit).
Sembra che in una delle ultime versioni di boost c ++ apparisse la libreria per fornire esattamente quello che vuoi, probabilmente il codice sarebbe multipiattaforma . È boost :: stacktrace , che puoi usare come come in boost campione :
#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 compili il codice qui sopra:
g++ --std=c++17 file.cpp -lstdc++fs -lboost_stacktrace_backtrace -ldl -lbacktrace
Esempio di backtrace copiato da boost documentation :
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