I am debugging a program that is supposed to run continuously. It gives a terminal that accepts user input and reacts to it.
It's randomly segfaulting at startup and I want to debug it by spawning a run, waiting for 3 seconds, interrupting it and spawning another run until it segfaults. I tried something like:
(gdb) while (1)
>run
>shell sleep 3
>interrupt
>end
But this does not interrupt my application after 3 seconds. If instead I do something like:
(gdb) while (1)
>run
>end
The application runs in a loop but I have to keep pressing Ctrl+C to manually interrupt it and when it segfaults it automatically restarts so I can't debug it.
Is there a way to manually interrupt it until it segfaults? What is a good way to do this?
Place the following into a file called run_and_interrupt.py
:
import threading
import time
import os
import signal
# A thread calss which waits for DELAY seconds then sends SIGINT to
# process PID.
class interrupting_thread(threading.Thread):
def __init__(self, delay, pid):
threading.Thread.__init__(self)
self.delay = delay
self.pid = pid
def run(self):
time.sleep(self.delay)
os.kill(self.pid, signal.SIGINT)
# The last signal that stopped the inferior.
last_stop_signal = "SIGINT"
# Handle stop events. Look for signal stops and record the signal
# into the global LAST_STOP_SIGNAL.
def stop_handler(event):
global last_stop_signal
if isinstance(event, gdb.SignalEvent):
last_stop_signal = event.stop_signal
# Register the stop event handler.
gdb.events.stop.connect(stop_handler)
class run_and_interrupt(gdb.Command):
"""run-and-interrupt ARGS
Run the current inferior passing in ARGS.
"""
def __init__(self):
gdb.Command.__init__(self, "run-and-interrupt", gdb.COMMAND_RUNNING)
def invoke(self, args, from_tty):
global last_stop_signal
while last_stop_signal == "SIGINT":
gdb.execute(f"starti {args}")
inf = gdb.selected_inferior()
pid = inf.pid
# Start a new thread.
thr = interrupting_thread(3, pid)
thr.start()
gdb.execute("continue")
thr.join()
# Register the new command.
run_and_interrupt()
Then in a GDB session source run_and_interrupt.py
.
You now have a new GDB command run-and-interrupt
which will start the current executable, wait 3 seconds, then send SIGINT to the inferior.
The command does this repeatedly until the inferior doesn't stop with SIGINT.
The Python code is a little rough, and can certainly be improved, but this should be a good starting point.