serial - ¿Cómo puedo hacer que GDB se detenga en SIGTRAP solo en puntos de interrupción?
interrupciones internas arduino (1)
Estoy intentando depurar un programa que a menudo hace que GDB se detenga y muestre SIGTRAP cuando no está en un punto de interrupción. Sucede cuando carga bibliotecas dinámicas y otras cosas ordinarias. Hay como 1000 de estos que ocurren antes de que mi punto de quiebre finalmente se golpee, por lo que no es factible para mí "continuar" manualmente todos estos SIGTRAP irrelevantes. Pero si uso el comando handle SIGTRAP nostop noprint
, entonces GDB no se detendrá en mi punto de interrupción.
Parece que debe haber una forma de educar a GDB para que comprenda qué SIGTRAP es bueno para detener, y cuál no sirve para detenerse. Claramente GDB sabe si está en un punto de quiebre, porque la salida es muy confiablemente diferente: en un punto de interrupción, menciona "punto de corte" y muestra el número de punto de interrupción, pero en cualquier otro SIGTRAP, simplemente dice "SIGTRAP". Entonces, en lugar de imprimir el mensaje sobre un SIGTRAP, realmente me gustaría que GDB se dijera a sí mismo, "wow, esto es un SIGTRAP y no hay punto de ruptura aquí. Mírame, estoy a punto de detenerme e imprimir un inútil mensaje SIGTRAP que arruina por completo la sesión de depuración. ¿Qué tal si sigo en silencio? Por favor, avíseme si alguien tiene una manera de hacer esto.
Puede establecer puntos de captura para capturar la señal SIGTRAP y agregar comandos para decidir si continuar o detenerse. En este controlador, puede inspeccionar variables de conveniencia como $_siginfo
por el motivo de la señal.
De particular interés es $_siginfo.si_code
, su valor depende de la señal que se entregue. La página del manual de sigaction (2) describe las relaciones exactas. Puede encontrar los valores numéricos para esos SI_USER
, SI_KERNEL
, etc. compilando un programa o buscando encabezados (en mi sistema usa el encabezado /usr/include/bits/siginfo.h
). Algunos valores que he encontrado son:
- 0x00 (0):
SI_USER
- 0x80 (128):
SI_KERNEL
- 0x02 (2):
TRAP_TRACE
Con esta información a mano, aquí hay un ejemplo que captura SIGTRAP e imprime el motivo, luego continúa:
catch signal SIGTRAP
commands
p $_siginfo.si_code
c
end
# Set another breakpoint for testing
break sleep
Ahora considere este programa de prueba que duerme 5 segundos, luego activa una trampa de depuración en x86 (-64):
#include <unistd.h>
int main(void) {
for (;;) {
sleep(5);
asm("int3");
}
return 0;
}
Este programa sigue deteniendo gdb
en la línea int3
porque la señal está atrapada ( si_code
pasa a ser 0x80, SI_KERNEL
), pero luego la instrucción se repite nuevamente. Entonces, para omitir esta instrucción, el contador del programa ( $pc
) debe incrementarse. Después de hacerlo, aprendí esta información sobre SIGTRAP
y si_code
:
- Los puntos de interrupción activan SIGTRAP con el código 128 (
SI_KERNEL
). - Después de continuar la división, se recibe un SIGTRAP con código 2 (
TRAP_TRACE
) (debido al punto deSIGTRAP
paraSIGTRAP
). - La instrucción
int3
desencadena SIGTRAP con el código 128. Por lo tanto, necesita algo para diferenciar las instrucciones.
Estos son los últimos comandos de GDB que int3
trampas int3
y aún mantienen los puntos de interrupción funcionales:
catch signal SIGTRAP
commands
silent # do not print catchpoint hits
# ignore the int3 instruction (this address was looked up at
# the tracepoint using print $pc)
if $pc == 0x400568
set $pc++ # skip int3
c
end
# Ignore TRAP_TRACE that is used for breakpoints
if $_siginfo.si_code == 2
c
end
end
Una nota final: el depurador usa el SIGTRAP internamente, es posible que lo anterior capte demasiado. Esto fue probado con GDB 7.10 en Arch Linux.