Cómo generar automáticamente un stacktrace cuando mi programa se bloquea


Estoy trabajando en Linux con el compilador GCC. Cuando mi programa C++ se bloquea, me gustaría que generara automáticamente un stacktrace.

Mi programa está siendo ejecutado por muchos usuarios diferentes y también se ejecuta en Linux, Windows y Macintosh (todas las versiones se compilan utilizando gcc).

Me gustaría que mi programa sea capaz de generar un seguimiento de pila cuando se bloquea y la próxima vez que el usuario lo ejecute, le preguntará si está bien enviarme el seguimiento de pila para que pueda rastrear el problema. Puedo manejar el envío de la información a mí, pero no se cómo generar la cadena de seguimiento. Alguna idea?

Author: jww, 2008-09-17

28 answers

Para Linux y creo que para Mac OS X, si estás usando gcc, o cualquier compilador que use glibc, puedes usar las funciones backtrace() en execinfo.h para imprimir un stacktrace y salir con gracia cuando tengas un error de segmentación. La documentación se puede encontrar en el manual de libc.

Aquí hay un programa de ejemplo que instala un manejador SIGSEGV e imprime un stacktrace a stderr cuando falla segfaults. La función baz() aquí causa la falla de segmento que desencadena la controlador:

#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.
}

Compilar con -g -rdynamic te da información del símbolo en tu salida, que glibc puede usar para hacer una buena traza de apilamiento:

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

Ejecutando esto obtendrás esta salida:

$ ./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]

Esto muestra el módulo de carga, el desplazamiento y la función de los que proviene cada fotograma de la pila. Aquí puede ver el manejador de señales en la parte superior de la pila, y las funciones libc antes de main además de main, foo, bar, y baz.

 427
Author: tgamblin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-20 10:39:36

Linux

Mientras que el uso de backtrace() funciona en execinfo.h para imprimir un stacktrace y salir con gracia cuando se obtiene un error de segmentación ya se ha sugerido, no veo ninguna mención de las complejidades necesarias para garantizar que el backtrace resultante apunte a la ubicación real del error (al menos para algunas arquitecturas - x86 y ARM).

Las dos primeras entradas en la cadena de marco de pila cuando se entra en el controlador de señal contienen una dirección de retorno dentro de la manejador de señal y uno dentro de sigaction () en libc. El marco de pila de la última función llamada antes de la señal (que es la ubicación de la falla) se pierde.

Código

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

Salida

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]

Todos los peligros de llamar a las funciones backtrace() en un manejador de señales todavía existen y no deben pasarse por alto, pero encuentro la funcionalidad que describí aquí bastante útil para depurar bloqueos.

Es importante tener en cuenta que el ejemplo que proporcioné está desarrollado/probado en Linux para x86. También he implementado con éxito esto en ARM usando uc_mcontext.arm_pc en lugar de uc_mcontext.eip.

Aquí hay un enlace al artículo donde aprendí los detalles para esta implementación: http://www.linuxjournal.com/article/6391

 110
Author: jschmier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:22

Es incluso más fácil que "man backtrace", hay una biblioteca poco documentada (específica de GNU) distribuida con glibc como libSegFault.so, que creo fue escrito por Ulrich Drepper para apoyar el programa catchsegv (ver "man catchsegv").

Esto nos da 3 posibilidades. En lugar de ejecutar "program-o hai":

  1. Ejecutar dentro de catchsegv:

    $ catchsegv program -o hai
    
  2. Enlace con libSegFault en tiempo de ejecución:

    $ LD_PRELOAD=/lib/libSegFault.so program -o hai
    
  3. Enlace con libSegFault en compile hora:

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

En los 3 casos, obtendrá retrotracciones más claras con menos optimización (gcc-O0 u-O1) y símbolos de depuración (gcc-g). De lo contrario, puede terminar con una pila de direcciones de memoria.

También puede capturar más señales para trazas de pila con algo como:

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

La salida se verá algo como esto (observe la traza hacia atrás en la parte inferior):

*** 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]

Si quieres conocer los detalles sangrientos, la mejor fuente es desafortunadamente la fuente: Véase http://sourceware.org/git/?p=glibc.git;a = blob; f = debug / segfault. c y su directorio padre http://sourceware.org/git/?p=glibc.git;a=árbol; f = depuración

 105
Author: jhclark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-22 03:59:02

Aunque se ha proporcionado una respuesta correcta que describe cómo usar la función GNU libc backtrace()1 y proporcioné mi propia respuesta que describe cómo garantizar que una traza inversa desde un manejador de señales apunte a la ubicación real de la falla2, No veo ninguna mención de demangling Símbolos C++ de salida de la traza inversa.

Al obtener backtraces de un programa C++, la salida se puede ejecutar a través de c++filt1 to desmangle los símbolos o usando abi::__cxa_demangle1 directamente.

  • 1 Linux Y OS X Tenga en cuenta que c++filt y __cxa_demangle son específicos del CCG
  • 2 Linux

El siguiente ejemplo de C++ Linux usa el mismo manejador de señales que mi otra respuesta y demuestra cómo c++filt se puede usar para desmangular los símbolos.

Código:

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

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

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

Lo siguiente se basa en el manejador de señal de mi respuesta original y puede reemplazar el manejador de señal en el ejemplo anterior para demostrar cómo abi::__cxa_demangle se puede utilizar para desmangle los símbolos. Este manejador de señales produce la misma salida desenredada que el ejemplo anterior.

Código:

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);
}
 73
Author: jschmier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:22

Podría valer la pena mirar Google Breakpad, un generador de volcado de fallos multiplataforma y herramientas para procesar los volcados.

 33
Author: Simon Steele,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-13 14:15:07

No especificó su sistema operativo, por lo que esto es difícil de responder. Si está utilizando un sistema basado en gnu libc, es posible que pueda utilizar la función libc backtrace().

GCC también tiene dos builtins que pueden ayudarte, pero que pueden o no implementarse completamente en tu arquitectura, y esos son __builtin_frame_address y __builtin_return_address. Ambos quieren un nivel entero inmediato (por inmediato, quiero decir que no puede ser una variable). Si __builtin_frame_address para un nivel dado es distinto de cero, debería ser seguro agarrar el remitente del mismo nivel.

 21
Author: Brian Mitchell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-25 00:19:03

ulimit -c <value> establece el límite de tamaño del archivo principal en unix. De forma predeterminada, el límite de tamaño del archivo principal es 0. Puedes ver tus valores ulimit con ulimit -a.

Además, si ejecuta su programa desde gdb, detendrá su programa en "violaciones de segmentación" (SIGSEGV, generalmente cuando accede a una pieza de memoria que no había asignado) o puede establecer puntos de interrupción.

Ddd y nemiver son front-ends para gdb que hacen que trabajar con él sea mucho más fácil para el novato.

 12
Author: user,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-25 00:20:28

Algunas versiones de libc contienen funciones que tratan con trazas de pila; es posible que pueda usarlas:

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

Recuerdo haber usado libunwind hace mucho tiempo para obtener trazas de pila, pero es posible que no sea compatible con su plataforma.

 10
Author: Stephen Deken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 20:51:24

Es importante tener en cuenta que una vez que generes un archivo core necesitarás usar la herramienta gdb para verlo. Para que gdb tenga sentido de su archivo principal, debe decirle a gcc que instrumente el binario con símbolos de depuración: para hacer esto, compile con la bandera-g:

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

Entonces, puede configurar "ulimit-c unlimited" para que pueda volcar un núcleo, o simplemente ejecutar su programa dentro de gdb. Me gusta más el segundo enfoque:

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

Espero que esto ayude.

 10
Author: Benson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 20:57:12

He estado mirando este problema por un tiempo.

Y enterrado profundamente en las Herramientas de rendimiento de Google README

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

Habla de libunwind

Http://www.nongnu.org/libunwind /

Me encantaría escuchar las opiniones de esta biblioteca.

El problema con-rdynamic es que puede aumentar el tamaño del binario relativamente significativamente en algunos casos

 10
Author: Gregory,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-18 02:55:24

Gracias a enthusiasticgeek por llamar mi atención sobre la utilidad addr2line.

He escrito un script rápido y sucio para procesar la salida de la respuesta proporcionada aquí : (muchas gracias a jschmier!) usando la utilidad addr2line.

El script acepta un único argumento: El nombre del archivo que contiene la salida de la utilidad de jschmier.

La salida debe imprimir algo como lo siguiente para cada nivel de la trace:

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;

Código:

#!/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 
 10
Author: arr_sea,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:22
ulimit -c unlimited

Es una variable del sistema, que permitirá crear un volcado de núcleo después de que su aplicación se bloquee. En este caso una cantidad ilimitada. Busque un archivo llamado core en el mismo directorio. Asegúrese de que compiló su código con la información de depuración habilitada!

Saludos

 9
Author: mana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 20:52:38
 8
Author: Roskoto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 21:24:21

Puedes usar DeathHandler - clase C++ pequeña que hace todo por ti, confiable.

 8
Author: markhor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-01 06:38:05

Olvídese de cambiar sus fuentes y haga algunos hacks con la función backtrace() o macros - estas son solo soluciones pobres.

Como una solución que funciona correctamente, yo aconsejaría:

  1. Compile su programa con la bandera "-g" para incrustar símbolos de depuración en binario (no se preocupe, esto no afectará su rendimiento).
  2. En Linux ejecute el siguiente comando: "ulimit-c unlimited" - para permitir que el sistema haga grandes volcados de fallos.
  3. Cuando su programa se estrelló, en el directorio de trabajo verá el archivo "core".
  4. Ejecute el siguiente comando para imprimir backtrace a stdout: gdb-batch-ex "backtrace" ./ su_program_exe ./ core

Esto imprimirá el backtrace legible adecuado de su programa en forma legible por humanos (con nombres de archivo fuente y números de línea). Además, este enfoque le dará libertad para automatizar su sistema: tenga un script corto que verifique si el proceso creó un volcado de núcleo, y luego envíe retrotracciones por correo electrónico a los desarrolladores, o ingrese esto en algún registro sistema.

 8
Author: loopzilla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-06 12:37:15

Mira:

Man 3 backtrace

Y:

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

Estas son extensiones de GNU.

 7
Author: Stéphane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 20:55:21

Puedo ayudar con la versión de Linux: se pueden usar las funciones backtrace, backtrace_symbols y backtrace_symbols_fd. Consulte las páginas de manual correspondientes.

 5
Author: terminus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 21:01:04

Consulte la instalación de seguimiento de pila en ACE (ADAPTIVE Communication Environment). Ya está escrito para cubrir todas las plataformas principales (y más). La biblioteca tiene licencia de estilo BSD, por lo que incluso puede copiar/pegar el código si no desea usar ACE.

 5
Author: Adam Mitz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-17 00:07:57

* nix: puede interceptar SIGSEGV (usualmente esta señal se eleva antes de que se bloquee) y mantener la información en un archivo. (además del archivo core que puedes usar para depurar usando gdb por ejemplo).

Win: Compruebe esto de msdn.

También puede mirar el código de Google Chrome para ver cómo maneja los bloqueos. Tiene un buen mecanismo de manejo de excepciones.

 4
Author: INS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 21:09:52

Encontré que la solución @tgamblin no está completa. No se puede manejar con stackoverflow. Creo que porque por defecto manejador de señal se llama con la misma pila y SIGSEGV es lanzado dos veces. Para protegerlo necesita registrar una pila independiente para el manejador de señales.

Puede verificar esto con el código a continuación. De forma predeterminada, el controlador falla. Con la macro definida STACK_OVERFLOW está bien.

#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;
} 
 4
Author: Daneel S. Yaitskov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-20 11:59:22

Usaría el código que genera un seguimiento de pila para la memoria filtrada en Visual Leak Detector. Sin embargo, esto solo funciona en Win32.

 3
Author: Jim Buck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 21:00:42

He visto muchas respuestas aquí realizando un manejador de señal y luego saliendo. Ese es el camino a seguir, pero recuerde un hecho muy importante: Si desea obtener el volcado de núcleo para el error generado, no puede llamar a exit(status). Llame abort() en su lugar!

 3
Author: jard18,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-12 23:23:10

El nuevo rey en la ciudad ha llegado https://github.com/bombela/backward-cpp

1 encabezado para colocar en su código y 1 biblioteca para instalar.

Personalmente lo llamo usando esta función

#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);
}
 3
Author: Roy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-10 22:29:34

Además de las respuestas anteriores, aquí cómo hacer que Debian Linux OS genere core dump

  1. Crear una carpeta "coredumps" en la carpeta de inicio del usuario
  2. Vaya a /etc/security/limits.conf. Debajo de la línea'', escriba "soft core unlimited" y "root soft core unlimited" si habilita los volcados de núcleo para root, para permitir un espacio ilimitado para los volcados de núcleo.
  3. NOTA: "* soft core unlimited" no cubre root, por lo que root debe especificarse en su propia línea.
  4. Para comprobar estos valores, cerrar sesión, volver a iniciar sesión y escribir "ulimit-a". "Core file size" debe establecerse en unlimited.
  5. Compruebe el .archivos bashrc (usuario, y root si corresponde) para asegurarse de que ulimit no está establecido allí. De lo contrario, el valor anterior se sobrescribirá al iniciar.
  6. Abra /etc/sysctl.conf. Introduzca lo siguiente en la parte inferior: "kernel.core_pattern = / home / / coredumps/ % e_ % t.dump". (%e será el nombre del proceso, y %t será la hora del sistema)
  7. Salga y escriba "sysctl-p" para cargar la nueva configuración Compruebe /proc/sys/kernel / core_pattern y compruebe que coincide con lo que acaba de escribir.
  8. El volcado de núcleo se puede probar ejecutando un proceso en la línea de comandos (" &"), y luego matándolo con "kill -11 ". Si el volcado de núcleo es exitoso, verá "(volcado de núcleo)" después de la indicación de falla de segmentación.
 2
Author: enthusiasticgeek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-08 19:13:58

En Linux/unix/MacOSX use archivos core (puede habilitarlos con ulimit o llamada al sistema compatible). En Windows, utilice informes de errores de Microsoft (puede convertirse en socio y obtener acceso a los datos de bloqueo de la aplicación).

 1
Author: Kasprzol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 21:16:42

Si su programa se bloquea, es el propio sistema operativo el que genera la información del volcado de bloqueo. Si está utilizando un sistema operativo *nix, simplemente no debe evitar que lo haga (consulte las opciones 'coredump' del comando ulimit).

 0
Author: nsayer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 21:01:26

Me olvidé de la tecnología GNOME de "apport", pero no se mucho sobre su uso. Se utiliza para generar stacktraces y otros diagnósticos para el procesamiento y puede archivar errores automáticamente. Vale la pena comprobarlo.

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 21:24:29

Como una solución solo para Windows, puede obtener el equivalente de un seguimiento de pila (con mucha, mucha más información) utilizando Informes de errores de Windows. Con solo unas pocas entradas de registro, se puede configurar para recopilar volcados de modo de usuario :

A partir de Windows Server 2008 y Windows Vista con Service Pack 1 (SP1), el informe de errores de Windows (WER) se puede configurar para que los volcados completos en modo de usuario se recopilen y almacenen localmente después de que se bloquee una aplicación en modo de usuario. [...]

Esta función no está habilitada de forma predeterminada. Habilitar la función requiere privilegios de administrador. Para habilitar y configurar la característica, utilice los siguientes valores de registro bajo la clave HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps.

Puede establecer las entradas del registro desde su instalador, que tiene los privilegios necesarios.

Crear un volcado en modo usuario tiene las siguientes ventajas sobre generar un seguimiento de pila en el cliente:

  • Ya está implementado en el sistema. Puede usar WER como se ha descrito anteriormente, o llamar a MiniDumpWriteDump usted mismo, si necesita un control más detallado sobre la cantidad de información a volcar. (Asegúrese de llamarlo desde un proceso diferente.)
  • Way más completo que un seguimiento de pila. Entre otros, puede contener variables locales, argumentos de función, pilas para otros subprocesos, módulos cargados, etc. La cantidad de datos (y en consecuencia tamaño) es altamente personalizable.
  • No es necesario enviar símbolos de depuración. Esto reduce drásticamente el tamaño de su implementación, así como hace que sea más difícil aplicar ingeniería inversa a su aplicación.
  • En gran medida independiente del compilador que utilice. Usar WER ni siquiera requiere ningún código. De cualquier manera, tener una manera de obtener una base de datos de símbolos (PDB) es muy útil para el análisis sin conexión. Creo que GCC puede generar PDB, o hay herramientas para convertir el símbolo base de datos en formato PDB.

Tenga en cuenta que WER solo se puede activar por un bloqueo de la aplicación (es decir, el sistema que termina un proceso debido a una excepción no controlada). MiniDumpWriteDump se puede llamar en cualquier momento. Esto puede ser útil si necesita volcar el estado actual para diagnosticar problemas que no sean un bloqueo.

Lectura obligatoria, si desea evaluar la aplicabilidad de mini volcados:

 0
Author: IInspectable,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-01 13:00:47