Domanda

Sto lavorando su Linux con il compilatore GCC.Quando il mio programma C++ si blocca, vorrei 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 utilizzando 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 eseguirà, chiederà se è ok inviarmi la traccia dello stack in modo da poter rintracciare il problema.Posso gestire l'invio delle informazioni ma non so come generare la stringa di traccia.Qualche idea?

È stato utile?

Soluzione

Per Linux e credo che Mac OS X, se stai utilizzando gcc o qualsiasi compilatore che utilizza glibc, puoi utilizzare le funzioni backtrace() in execinfo.h per stampare uno stacktrace ed uscire con garbo quando si verifica un errore di segmentazione.È possibile trovare la documentazione nel manuale della libc.

Ecco un programma di esempio che installa a SIGSEGV gestore e stampa uno stacktrace su stderr quando va in segfault.IL baz() la funzione 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.
}

Compilazione con -g -rdynamic ti fornisce informazioni sui simboli nel tuo output, che glibc può utilizzare per creare uno stacktrace gradevole:

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

L'esecuzione di questo ti dà questo output:

$ ./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 nello stack.Qui puoi vedere il gestore del segnale in cima allo stack e le funzioni libc prima main inoltre main, foo, bar, E baz.

Altri suggerimenti

Linux

Mentre l'uso delle funzioni backtrace() in execinfo.h per stampare uno stacktrace ed uscire con garbo quando si ottiene un errore di segmentazione ha già stato suggerito, non vedo alcuna menzione delle complessità necessarie per garantire che il backtrace risultante punti alla posizione effettiva dell'errore (almeno per alcune architetture - x86 e ARM).

Le prime due voci nella catena dello stack frame quando si accede al gestore del segnale contengono un indirizzo di ritorno all'interno del gestore del segnale e uno all'interno di sigaction() in libc.Lo stack frame dell'ultima funzione chiamata prima del segnale (che è la posizione dell'errore) viene perso.

Codice

#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);
}

Produzione

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 legati alla chiamata delle funzioni backtrace() in un gestore di segnale esistono ancora e non dovrebbero essere trascurati, ma trovo che la funzionalità che ho descritto qui sia piuttosto utile per il debug degli arresti anomali.

È importante notare che l'esempio fornito è sviluppato/testato su Linux per x86.L'ho implementato con successo anche su ARM utilizzando uc_mcontext.arm_pc invece di uc_mcontext.eip.

Ecco un collegamento 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 GNU) distribuita con glibc come libSegFault.so, che credo sia stata scritta da Ulrich Drepper per supportare il programma catchsegv (vedi "man catchsegv").

Questo ci dà 3 possibilità.Invece di eseguire "program -o hai":

  1. Esegui all'interno di catchsegv:

    $ catchsegv program -o hai
    
  2. Collegamento con libSegFault in fase di esecuzione:

    $ LD_PRELOAD=/lib/libSegFault.so program -o hai
    
  3. Collegamento con libSegFault in fase di compilazione:

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

In tutti e 3 i casi, otterrai backtrace più chiari con meno ottimizzazione (gcc -O0 o -O1) e simboli di debug (gcc -g).Altrimenti, potresti ritrovarti con una pila di indirizzi di memoria.

Puoi anche catturare più segnali per le tracce dello stack con qualcosa come:

$ 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 è purtroppo la fonte:Vedere 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 a risposta corretta è stato fornito che descrive come utilizzare la libc GNU backtrace() funzione1 e ho fornito la mia risposta che descrive come garantire che un backtrace da un gestore di segnale punti alla posizione effettiva dell'errore2, non vedo alcuna menzione di smantellamento Simboli C++ emessi dal backtrace.

Quando si ottengono backtrace da un programma C++, è possibile eseguire l'output c++filt1 per scomporre i simboli o utilizzando abi::__cxa_demangle1 direttamente.

  • 1 Linux e OS XNotare che c++filt E __cxa_demangle sono specifici del GCC
  • 2 Linux

Il seguente esempio C++ Linux utilizza lo stesso gestore di segnale di my altra risposta e dimostra come c++filt può essere usato per scomporre 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;
}

Produzione (./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]

Uscita sminuzzata (./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 si basa sul gestore del segnale di my risposta originale e può sostituire il gestore del segnale nell'esempio precedente per dimostrare come abi::__cxa_demangle può essere usato per scomporre i simboli.Questo gestore di segnale produce lo stesso output smantellato 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 valere la pena guardarlo Blocco note di Google, un generatore di crash dump multipiattaforma e strumenti per elaborare i dump.

Non hai specificato il tuo sistema operativo, quindi è difficile rispondere.Se stai utilizzando un sistema basato su gnu libc, potresti essere in grado di utilizzare la funzione libc backtrace().

GCC ha anche due built-in che possono aiutarti, ma che possono o meno 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 poiché un dato livello è diverso da zero, dovrebbe essere sicuro acquisire l'indirizzo di ritorno dello stesso livello.

ulimit -c <value> imposta il limite della dimensione del file principale su UNIX.Per impostazione predefinita, il limite della dimensione del file principale è 0.Puoi vedere il tuo ulimit valori con ulimit -a.

inoltre, se esegui il tuo programma da gdb, il programma verrà interrotto in caso di "violazioni della segmentazione" (SIGSEGV, generalmente quando accedi a una porzione di memoria che non avevi allocato) oppure puoi impostare dei punti di interruzione.

ddd e nemiver sono front-end per gdb che rendono il lavoro con esso molto più semplice per i principianti.

Grazie aentusiastageek per aver attirato la mia attenzione sull'utilità addr2line.

Ho scritto uno script rapido e sporco per elaborare l'output della risposta fornita Qui:(molte grazie a jschmier!) utilizzando l'utilità addr2line.

Lo script accetta un singolo argomento:Il nome del file contenente l'output dell'utilità jschmier.

L'output dovrebbe stampare qualcosa di simile al 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 

È importante notare che una volta generato un file core sarà necessario utilizzare lo strumento gdb per esaminarlo.Affinché gdb possa dare un senso al tuo file core, devi dire a gcc di dotare il binario di simboli di debug:per fare ciò, compili con il flag -g:

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

Quindi, puoi impostare "ulimit -c unlimited" per consentire il dump di un core o semplicemente eseguire il programma all'interno di gdb.Mi piace di più il secondo approccio:

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

Spero che aiuti.

Sto esaminando questo problema da 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 opinioni su questa libreria.

Il problema con -rdynamic è che in alcuni casi può aumentare la dimensione del file binario in modo relativamente significativo

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 le analisi dello stack, ma potrebbe non essere supportato sulla tua piattaforma.

Dimentica di cambiare le tue fonti e fai qualche hack con la funzione backtrace() o le macro: queste sono solo soluzioni scadenti.

Come soluzione correttamente funzionante, consiglierei:

  1. Compila il tuo programma con il flag "-g" per incorporare i simboli di debug nel binario (non preoccuparti, ciò non influirà sulle tue prestazioni).
  2. Su Linux esegui il comando successivo:"ulimit -c unlimited" - per consentire al sistema di effettuare grandi crash dump.
  3. Quando il tuo programma si è bloccato, nella directory di lavoro vedrai il file "core".
  4. Esegui il comando successivo per stampare il backtrace su stdout:gdb -batch -ex "backtrace" ./tuo_programma_exe ./core

Questo stamperà il backtrace leggibile corretto del tuo programma in modo leggibile dall'uomo (con nomi di file sorgente e numeri di riga).Inoltre questo approccio ti darà la libertà di automatizzare il tuo sistema:avere un breve script che controlla se il processo ha creato un core dump, quindi invia backtrace via e-mail agli sviluppatori o lo registra in qualche sistema di registrazione.

ulimit -c unlimited

è una variabile di sistema che consentirà 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 codice con le informazioni di debug abilitate!

saluti

vincita:Che ne dici di StackWalk64 http://msdn.microsoft.com/en-us/library/ms680650.aspx

Puoi usare DeathHandler - piccola classe C++ che fa tutto per te, affidabile.

Guarda a:

uomo 3 indietro

E:

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

Queste sono estensioni GNU.

Vedere la funzione Stack Trace in ASSO (Ambiente di comunicazione ADATTIVO).È già scritto per coprire tutte le principali piattaforme (e altro ancora).La libreria ha una licenza in stile BSD, quindi puoi anche copiare/incollare il codice se non desideri utilizzare ACE.

Posso aiutarti con la versione Linux:è possibile utilizzare la funzione backtrace, backtrace_symbols e backtrace_symbols_fd.Vedere le pagine di manuale corrispondenti.

*niente:puoi intercettare SIGSEGV (di solito questo segnale viene generato prima dell'arresto anomalo) e conserva le informazioni in un file.(oltre al file core che puoi utilizzare per eseguire il debug utilizzando gdb ad esempio).

vincita:Controllo 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 scoperto che la soluzione @tgamblin non è completa.Non può gestire stackoverflow.Penso che perché per impostazione predefinita il gestore di segnale viene chiamato con lo stesso stack e Sigsegv viene lanciato due volte.Per proteggerlo è necessario registrare uno stack indipendente per il gestore del segnale.

Puoi verificarlo con il codice qui sotto.Per impostazione predefinita 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;
} 

Vorrei utilizzare il codice che genera una traccia dello stack per la memoria trapelata Rilevatore di perdite visivo.Funziona solo su Win32, però.

Ho visto molte risposte qui eseguendo un gestore di segnale e poi uscendo.Questa è la strada da percorrere, ma ricorda un fatto molto importante:Se desideri ottenere il core dump per l'errore generato, non puoi chiamare exit(status).Chiamata abort() Invece!

Il nuovo re in città è arrivatohttps://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);
}

Oltre alle risposte di cui sopra, ecco come fare in modo che il sistema operativo Debian Linux generi il core dump

  1. Creare una cartella "coredumps" nella cartella home dell'utente
  2. Vai a /etc/security/limits.conf.Sotto la riga ' ', digitare "soft core unlimited" e "root soft core unlimited" se si abilitano i core dump per root, per consentire spazio illimitato per i core dump.
  3. NOTA:"* soft core unlimited" non copre root, motivo per cui root deve essere specificato in una riga separata.
  4. Per verificare questi valori, disconnettersi, accedere nuovamente e digitare "ulimit -a".La “dimensione del file principale” deve essere impostata su illimitata.
  5. Controlla i file .bashrc (utente e root se applicabile) per assicurarti che ulimit non sia impostato lì.In caso contrario, il valore sopra verrà sovrascritto all'avvio.
  6. Apri /etc/sysctl.conf.Inserisci quanto segue in basso:“kernel.core_pattern = /home//coredumps/%e_%t.dump”.(%e sarà il nome del processo e %t sarà l'ora del sistema)
  7. Esci e digita "sysctl -p" per caricare il nuovo controllo di configurazione/proc/sys/kernel/core_pattern e verificare che questo corrisponda a ciò che hai appena digitato.
  8. Il core dumping può essere testato eseguendo un processo sulla riga di comando (“ &”) e quindi terminandolo con “kill -11 ”.Se il core dumping ha esito positivo, verrà visualizzato "(core dumped)" dopo l'indicazione dell'errore di segmentazione.

Come soluzione solo per Windows, puoi ottenere l'equivalente di un'analisi dello stack (con molte, molte più informazioni) utilizzando Segnalazione errori di Windows.Con solo poche voci di registro, può essere configurato raccogliere dump in modalità utente:

A partire da Windows Server 2008 e Windows Vista con Service Pack 1 (SP1), Segnalazione errori Windows (WER) può essere configurato in modo che i dump completi in modalità utente vengano raccolti e archiviati localmente dopo l'arresto anomalo di un'applicazione in modalità utente.[...]

Questa funzionalità non è abilitata per impostazione predefinita.L'abilitazione della funzionalità richiede privilegi di amministratore.Per abilitare e configurare la funzionalità, utilizzare i seguenti valori di registro in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Segnalazione errori Windows\LocalDumps chiave.

È possibile impostare le voci di 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 un'analisi dello stack sul client:

  • È già implementato nel sistema.Puoi utilizzare il WER come descritto sopra oppure chiamare MiniDumpWriteDump te stesso, se hai bisogno di un controllo più preciso sulla quantità di informazioni da scaricare.(Assicurati di chiamarlo da un processo diverso.)
  • Modo più completo di un'analisi dello stack.Tra l'altro può contenere variabili locali, argomenti di funzioni, stack per altri thread, moduli caricati e così via.La quantità di dati (e di conseguenza la dimensione) è altamente personalizzabile.
  • Non è necessario spedire simboli di debug.Ciò riduce drasticamente le dimensioni della distribuzione e rende più difficile il reverse engineering dell'applicazione.
  • In gran parte indipendente dal compilatore utilizzato.L'utilizzo del WER non richiede nemmeno alcun codice.In ogni caso, avere un modo per ottenere un database di simboli (PDB) lo è molto utile per l'analisi offline.Credo che GCC possa generare PDB oppure che esistano strumenti per convertire il database dei simboli nel formato PDB.

Tieni presente che il WER può essere attivato solo da un arresto anomalo dell'applicazione (ad es.il sistema termina un processo a causa di un'eccezione non gestita). MiniDumpWriteDump può essere chiamato in qualsiasi momento.Ciò può essere utile se è necessario eseguire il dump dello stato corrente per diagnosticare problemi diversi da un arresto anomalo.

Lettura obbligatoria, se vuoi valutare l'applicabilità delle mini discariche:

Su Linux/unix/MacOSX usa i file core (puoi abilitarli con ulimit o chiamata di sistema compatibile).Su Windows utilizza la segnalazione degli errori Microsoft (puoi diventare un partner e ottenere l'accesso ai dati sugli arresti anomali dell'applicazione).

Ho dimenticato la tecnologia GNOME di "apport", ma non ne so molto sull'utilizzo.Viene utilizzato per generare stacktrace e altri strumenti diagnostici per l'elaborazione e può archiviare automaticamente bug.Vale sicuramente la pena di verificarlo.

Sembra che in una delle ultime versioni boost di C++ sia apparsa una libreria per fornire esattamente ciò che desideri, probabilmente il codice sarebbe multipiattaforma.È boost::stacktrace, che puoi usare come come nel campione boost:

#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 sopra:

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

Esempio di backtrace copiato da incrementare la documentazione:

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

Se vuoi ancora fare da solo come ho fatto io puoi collegarti contro bfd ed evitare l'uso 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]
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top