¿Puede obtener algún programa en Linux para imprimir un seguimiento de la pila si falla?

20

Si ejecuto un programa desde el shell, y esto falla:

$ buggy_program
Segmentation fault

Sin embargo, me dirá, ¿hay alguna manera de hacer que los programas impriman una traza inversa, tal vez ejecutando algo como esto:

$ print_backtrace_if_segfault buggy_program
Segfault in main.c:35
(rest of the backtrace)

También prefiero no usar strace o ltrace para ese tipo de información, ya que se imprimirán de cualquier manera ...

Neil
fuente

Respuestas:

25

Puede haber una mejor manera, pero este tipo de automatiza.

Ponga lo siguiente en ~/backtrace:

backtrace
quit

Ponga esto en un script llamado seg_wrapper.shen un directorio en su ruta:

#!/bin/bash
ulimit -c unlimited
"$@"
if [[ $? -eq 139 ]]; then
    gdb -q $1 core -x ~/backtrace
fi

El ulimitcomando hace que el núcleo sea volcado. "$@"son los argumentos dados al script, por lo que sería su programa y sus argumentos. $?mantiene el estado de salida, 139 parece ser el estado de salida predeterminado para mi máquina para un segfault.

Para gdb, -qsignifica silencioso (sin mensaje de introducción), y -xle dice gdbque ejecute comandos en el archivo que se le ha dado.

Uso

Entonces, para usarlo, simplemente:

seg_wrapper.sh ./mycommand and its arguments 

Actualizar

También podría escribir un controlador de señal que haga esto, vea este enlace .

Kyle Brandt
fuente
2
Su enlace a la solución del controlador de señal está inactivo: es por eso que las respuestas no deben vincular a otros recursos ...
josch
1
probablemente quiere decir "-x le dice a gdb que ejecute" en lugar de "-x le dice a gdb que salga"
josch
19

Lamento venir aquí 2 años después ... tropecé mientras buscaba algo más. Agregando esto para completar.

1) Si bien creo que la respuesta aceptada es excelente, requiere gdb. El método con el que estoy familiarizado usa libSegFault.so.

Si ejecuta su aplicación con

LD_PRELOAD = ... ruta-a ... / libSegFault.so myapp

Obtendría un informe con seguimiento, libs cargadas, etc.

2) También catchsegvestá disponible un script de contenedor que intentaría usar addr2linepara traducir direcciones a nombre de archivo + número de línea.

Estas son soluciones mucho más ligeras que los archivos core o gdb (bueno para sistemas embebidos, por ejemplo)

nhed
fuente
En realidad, LD_PRELOAD=libSegFault.soestá bien si está en la ruta dl.
Fernando Silveira
1
@FernandoSilveira ok. Escribir la respuesta de esta manera sugiere al lector que debe verificar cuál es ese camino.
nhed
6

Necesitas el amigo de todos GDB

gdb <program> [core file]

Una vez que haya cargado su archivo core, el comando 'backtrace' (puede abreviarse como bt) le dará la pila de llamadas actual. Si ejecuta su programa desde dentro de gdb, puede establecer puntos de interrupción arbitrarios y examinar el contenido de la memoria, etc.

Tel Janin
fuente
¿Hay alguna manera de obtenerlo solo para imprimir la traza inversa y salir?
Neil
5

catchsegv

Fue mencionado en otra respuesta (pero de ninguna manera se centró en). Es una herramienta práctica incluida en el proyecto glibc. Proporcionará una traza inversa (y otra información útil de depuración) solo si un programa realmente segfault.

Un buen artículo existe aquí .

Puede incluirlo en sus propios scripts como mejor le parezca.

wulfgarpro
fuente
3

Ubuntu (como proyecto) usa Apport para hacer esto. Puedes ver cómo lo hicieron.

https://wiki.ubuntu.com/Apport

sendmoreinfo
fuente
2
+1: Apport menciona algunos mecanismos útiles con los que no estaba familiarizado, como/proc/sys/kernel/core_pattern
RobM
2

Aquí hay una variante ligeramente modificada del guión de Kyle Brandt. Se mejora de las siguientes maneras:

  • no requieren interacción manual si el seguimiento de la pila es largo
  • algunos coredumps se guardan con el nombre del patrón core., respete esta configuración
  • no requiere un archivo de comando explícito para gdb (creará uno temporal)
  • esperar trabajos en segundo plano

Guión:

#!/bin/bash
gdbcommandfile=$(tempfile)
usepid=$(cat /proc/sys/kernel/core_uses_pid)
printf "set pagination off\nbacktrace\nquit\n" > $gdbcommandfile
ulimit -c unlimited
"$@"&
pid=$!
wait $!
if [[ $? -eq 139 ]]; then
    if [[ $usepid == 1 ]]; then 
        gdb -q $1 core.$pid -x $gdbcommandfile
    else
        gdb -q $1 core -x $gdbcommandfile
    fi
fi
rm $gdbcommandfile
Hasta Schäfer
fuente
1
Para una cadena de comandos tan simples, simplemente usaría -exen su lugar. gdb ... -ex 'set pagination off' -ex backtrace -ex quit
Josh Stone