Estoy trabajando en Linux con el compilador GCC. Cuando mi programa C ++ falla, me gustaría que genere 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 usando gcc
).
Me gustaría que mi programa pueda generar un seguimiento de la pila cuando falla y la próxima vez que el usuario lo ejecute, les preguntaré si está bien enviarme el seguimiento de la pila para que pueda localizar el problema. Puedo manejar el envío de la información, pero no sé cómo generar la cadena de seguimiento. ¿Algunas ideas?
Respuestas:
Para Linux y creo que Mac OS X, si está usando gcc, o cualquier compilador que use glibc, puede usar las funciones backtrace ()
execinfo.h
para imprimir un stacktrace y salir con gracia cuando se produce 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
SIGSEGV
controlador e imprime un seguimiento de la pilastderr
cuando se configura por defecto. Labaz()
función aquí provoca el segfault que activa el controlador:Compilar con
-g -rdynamic
obtiene la información del símbolo en su salida, que glibc puede usar para hacer un buen seguimiento de pila:Ejecutar esto te da esta salida:
Esto muestra el módulo de carga, el desplazamiento y la función de la que provino cada cuadro en la pila. Aquí se puede ver el manejador de la señal en la parte superior de la pila, y las funciones de libc antes
main
, además demain
,foo
,bar
, ybaz
.fuente
sigaction()
de libc. Si bien su traza inversa parece ser correcta, a veces he descubierto que son necesarios pasos adicionales para garantizar que la ubicación real de la falla aparezca en la traza trasera, ya quesigaction()
el núcleo puede sobrescribirla .catchsegv
no es lo que necesita el OP, pero es increíble para detectar fallas de segmentación y obtener toda la información.Es incluso más fácil que "traza inversa", hay una biblioteca poco documentada (específica de GNU) distribuida con glibc como libSegFault.so, que creo fue escrita por Ulrich Drepper para soportar el programa catchsegv (ver "man catchsegv").
Esto nos da 3 posibilidades. En lugar de ejecutar "program -o hai":
Ejecutar dentro de catchsegv:
Enlace con libSegFault en tiempo de ejecución:
Enlace con libSegFault en tiempo de compilación:
En los 3 casos, obtendrá retrocesos más claros con menos optimización (gcc -O0 u -O1) y símbolos de depuración (gcc -g). De lo contrario, puede terminar con un montón de direcciones de memoria.
También puede capturar más señales para los seguimientos de pila con algo como:
La salida se verá más o menos así (observe la traza en la parte inferior):
Si desea conocer los detalles sangrientos, desafortunadamente la mejor fuente es la fuente: consulte 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=tree;f=debug
fuente
-Wl,--no-as-needed
a los indicadores del compilador. De lo contrario,ld
será de hecho no se enlacen contralibSegFault
, porque reconoce que el binario no utiliza ninguno de sus símbolos.Linux
Aunque ya se ha sugerido el uso de las funciones backtrace () en execinfo.h para imprimir un stacktrace y salir con gracia cuando se produce un error de segmentación , no veo ninguna mención de las complejidades necesarias para garantizar que los puntos de retroceso resultantes a la ubicación real de la falla (al menos para algunas arquitecturas - x86 y ARM).
Las dos primeras entradas en la cadena de cuadros de pila cuando ingresa al controlador de señal contienen una dirección de retorno dentro del controlador de señal y una dentro de sigaction () en libc. El marco de la pila de la última función llamada antes de que se pierda la señal (que es la ubicación de la falla).
Código
Salida
Todos los riesgos de llamar a las funciones backtrace () en un controlador de señal todavía existen y no deben pasarse por alto, pero encuentro que la funcionalidad que describí aquí es bastante útil para la depuración de fallas.
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 en
uc_mcontext.arm_pc
lugar deuc_mcontext.eip
.Aquí hay un enlace al artículo donde aprendí los detalles de esta implementación: http://www.linuxjournal.com/article/6391
fuente
-rdynamic
para indicar al vinculador que agregue todos los símbolos, no solo los usados, a la tabla de símbolos dinámicos. Esto permitebacktrace_symbols()
convertir direcciones a nombres de funcionesaddr2line
comando de alguna manera para obtener la línea exacta donde ocurrió el bloqueo?glibc
uc_mcontext
no contiene un campo llamadoeip
. Ahora hay una matriz que necesita ser indexada,uc_mcontext.gregs[REG_EIP]
es el equivalente.A pesar de que se ha proporcionado una respuesta correcta que describe cómo usar la
backtrace()
función 1 de GNU libc y proporcioné mi propia respuesta que describe cómo garantizar que una traza inversa de un controlador de señal apunte a la ubicación real de la falla 2 , no veo Cualquier mención de la demanda de salida de símbolos C ++ de la traza inversa .Al obtener rastreos de un programa C ++, la salida se puede ejecutar a través de
c++filt
1 para solicitar los símbolos o usando 1 directamente.abi::__cxa_demangle
c++filt
y__cxa_demangle
son específicos de GCCEl siguiente ejemplo de C ++ Linux usa el mismo controlador de señal que mi otra respuesta y demuestra cómo
c++filt
se puede usar para solicitar los símbolos.Código :
Salida (
./test
):Salida Desmangled (
./test 2>&1 | c++filt
):Lo siguiente se basa en el controlador de señal de mi respuesta original y puede reemplazar el controlador de señal en el ejemplo anterior para demostrar cómo
abi::__cxa_demangle
se puede utilizar para solicitar los símbolos. Este controlador de señal produce la misma salida demandada que el ejemplo anterior.Código :
fuente
std::cerr
,free()
yexit()
todos violan las restricciones contra la llamada de llamadas no asíncronas seguras en sistemas POSIX. Este código será un punto muerto si el proceso falla en cualquier llamada comofree()
,malloc()
new
odetete
.Podría valer la pena mirar Google Breakpad , un generador de volcado de plataforma multiplataforma y herramientas para procesar los volcados.
fuente
No especificó su sistema operativo, por lo que es difícil de responder. Si está utilizando un sistema basado en gnu libc, es posible que pueda usar la función libc
backtrace()
.GCC también tiene dos componentes integrados que pueden ayudarlo, pero que pueden o no implementarse completamente en su 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 no es cero, debería ser seguro tomar la dirección de retorno del mismo nivel.fuente
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 provista 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 jschmier.
La salida debería imprimir algo como lo siguiente para cada nivel de la traza:
Código:
fuente
ulimit -c <value>
establece el límite de tamaño del archivo central en Unix. De forma predeterminada, el límite de tamaño de archivo principal es 0. Puede ver susulimit
valores conulimit -a
.Además, si ejecuta su programa desde gdb, detendrá su programa en "violaciones de segmentación" (
SIGSEGV
generalmente cuando accedió a un fragmento de memoria que no había asignado) o puede establecer puntos de interrupción.ddd y nemiver son front-end para gdb que hacen que trabajar con él sea mucho más fácil para el principiante.
fuente
Es importante tener en cuenta que una vez que genere un archivo central, deberá 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, compila con el indicador -g:
Luego, puede configurar "ulimit -c unlimited" para que descargue un núcleo, o simplemente ejecutar su programa dentro de gdb. Me gusta más el segundo enfoque:
Espero que esto ayude.
fuente
gdb
directamente desde su programa de bloqueo. Controlador de configuración para SIGSEGV, SEGILL, SIGBUS, SIGFPE que llamará a gdb. Detalles: stackoverflow.com/questions/3151779/… La ventaja es quebt full
obtienes un seguimiento hermoso y anotado como en , también puedes obtener rastros de la pila de todos los hilos.He estado mirando este problema por un tiempo.
Y enterrado profundamente en el archivo README de Google Performance Tools
http://code.google.com/p/google-perftools/source/browse/trunk/README
habla sobre libunwind
http://www.nongnu.org/libunwind/
Me encantaría conocer las opiniones de esta biblioteca.
El problema con -rdynamic es que puede aumentar el tamaño del binario de manera relativamente significativa en algunos casos
fuente
Algunas versiones de libc contienen funciones que tratan con trazas de pila; puede usarlos:
http://www.gnu.org/software/libc/manual/html_node/Backtraces.html
Recuerdo que usé libunwind hace mucho tiempo para obtener rastros de la pila, pero es posible que no sea compatible con su plataforma.
fuente
Puede usar DeathHandler , una pequeña clase de C ++ que hace todo por usted, confiable.
fuente
execlp()
para realizar llamadas a addr2line ... sería bueno permanecer completamente en el propio programa (que es posible al incluir el código addr2line de alguna forma)Olvídate de cambiar tus fuentes y haz algunos hacks con la función backtrace () o macroses; estas son solo soluciones deficientes.
Como una solución que funciona correctamente, recomendaría:
Esto imprimirá la trazabilidad legible adecuada de su programa en forma legible por humanos (con nombres de archivos de origen y números de línea). Además, este enfoque le dará libertad para automatizar su sistema: tenga una secuencia de comandos corta que verifique si el proceso creó un volcado de núcleo, y luego envíe retrocesos por correo electrónico a los desarrolladores, o inicie sesión en algún sistema de registro.
fuente
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 compilar su código con la información de depuración habilitada!
Saludos
fuente
limit coredumpsize unlimited
Mirar:
traza de hombre 3
Y:
Estas son extensiones GNU.
fuente
Vea la instalación de Stack Trace 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.
fuente
Puedo ayudar con la versión de Linux: se pueden usar la función backtrace, backtrace_symbols y backtrace_symbols_fd. Consulte las páginas del manual correspondientes.
fuente
Parece que en una de las últimas versiones de c ++ boost apareció la biblioteca para proporcionar exactamente lo que desea, probablemente el código sería multiplataforma. Es boost :: stacktrace , que puede usar como en la muestra de impulso :
En Linux, compila el código anterior:
Ejemplo de traza inversa copiada de la documentación de impulso :
fuente
* nix: puedes interceptar SIGSEGV (normalmente esta señal se genera antes de fallar) y mantener la información en un archivo. (además del archivo central que puede usar para depurar usando gdb, por ejemplo).
win: Comprueba esto desde 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.
fuente
Encontré que la solución @tgamblin no está completa. No se puede manejar con stackoverflow. Creo que, por defecto, el controlador de señal se llama con la misma pila y SIGSEGV se lanza dos veces. Para protegerlo, necesita registrar una pila independiente para el manejador de señal.
Puede verificar esto con el código a continuación. Por defecto, el controlador falla. Con la macro definida STACK_OVERFLOW está bien.
fuente
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
fuente
Usaría el código que genera un seguimiento de pila para la memoria perdida en el Detector de fugas visuales . Sin embargo, esto solo funciona en Win32.
fuente
He visto muchas respuestas aquí realizando un controlador de señal y luego saliendo. Ese es el camino a seguir, pero recuerde un hecho muy importante: si desea obtener el volcado del núcleo para el error generado, no puede llamar
exit(status)
. Llame en suabort()
lugar!fuente
Como solución exclusiva de Windows, puede obtener el equivalente de un seguimiento de la pila (con mucha, mucha más información) mediante el Informe de errores de Windows . Con solo unas pocas entradas de registro, se puede configurar para recopilar volcados en modo de usuario :
Puede configurar las entradas del registro desde su instalador, que tiene los privilegios necesarios.
La creación de un volcado en modo de usuario tiene las siguientes ventajas sobre la generación de un seguimiento de pila en el cliente:
Tenga en cuenta que WER solo puede activarse por un bloqueo de la aplicación (es decir, el sistema finaliza 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 distintos de un bloqueo.Lectura obligatoria, si desea evaluar la aplicabilidad de los mini volcados:
fuente
Además de las respuestas anteriores, aquí le mostramos cómo hacer que el sistema operativo Linux de Debian genere un volcado del núcleo
fuente
Si todavía quiere hacerlo solo como lo hice, puede vincular
bfd
y evitar usaraddr2line
como lo he hecho aquí:https://github.com/gnif/LookingGlass/blob/master/common/src/crash.linux.c
Esto produce la salida:
fuente
En Linux / unix / MacOSX, use archivos principales (puede habilitarlos con ulimit o una llamada de sistema compatible ). En Windows, utilice los informes de errores de Microsoft (puede convertirse en socio y obtener acceso a los datos de bloqueo de su aplicación).
fuente
Olvidé la tecnología de GNOME de "apport", pero no sé mucho sobre cómo usarla. Se utiliza para generar seguimientos de pila y otros diagnósticos para el procesamiento y puede archivar errores automáticamente. Ciertamente vale la pena registrarse.
fuente