2

I've been ripping my hair out over this. I've searched the internet and can't seem to find a solution to my problem. I'm trying to auto test some code using the gdb module from python. I can do basic command and things are working except for stopping a process that's running in the background. Currently I continue my program in the background after a break point with this:

gdb.execute("c&")

I then interact with the running program reading different constant values and getting responses from the program.
Next I need to get a chunk of memory so I run these commands:

gdb.execute("interrupt") #Pause execution gdb.execute("dump binary memory montiormem.bin 0x0 (&__etext + 4)") #dump memory to file

But when I run the memory dump I get an error saying the command can't be run when the target is running, after the error the interrupt command is run and the target is paused, then from the gdb console window I can run the memory dump.

I found a similar issue from awhile ago that seems to not be answered here.

I'm using python2.7.

I also found this link which seems to be the issue but no indication if it's in my build of gdb (which seems unlikely).

5 Answers 5

2

I had the same problem, but found that none of the other answers here really work if you are trying to script everything from python. The issue that I ran into was that when I called gdb.execute('continue'), no code in any other python thread would execute. This appears to be because gdb does not release the python GIL while the continue command is waiting for the program to be interrupted.

What I found that actually worked for me was this:

def delayed_interrupt():
    time.sleep(1)
    gdb.execute('interrupt')
gdb.post_event(delayed_interrupt)
gdb.execute('continue')
Sign up to request clarification or add additional context in comments.

Comments

1

I had the same problem, from what I can tell from googling it is a current limitation of gdb: interrupt simply doesn't work in batch mode (when specifying commands with --ex, or -x file, or on stdin, or sourcing from file), it runs the following commands before actually stopping the execution (inserting a delay doesn't help). Building on the @dwjbosman's solution, here's a compact version suitable for feeding to gdb with --ex arguments for example:

python import threading, gdb
python threading.Timer(1.0, lambda: gdb.post_event(lambda: gdb.execute("interrupt"))).start()
cont
thread apply all bt full # or whatever you wanted to do

It schedules an interrupt after 1 second and resumes the program, then you can do whatever you wanted to do after the pause right in the main script.

Comments

0

I just ran into this same issue while writing some automated testing scripts. What I've noticed is that the 'interrupt' command doesn't stop the application until after the current script has exited.

Unfortunately, this means that you would need to segment your scripts anytime you are causing an interrupt.

Script 1:

gdb.execute('c&')
gdb.execute('interrupt')

Script 2:

gdb.execute("dump binary memory montiormem.bin 0x0 (&__etext + 4)")

Comments

0

I used multi threading to get arround this issue:

def post(cmd):
    def _callable():
        print("exec " + cmd , flush=True)
        gdb.execute(cmd)

    print("schedule " + cmd , flush=True)
    gdb.post_event(_callable)

class ScriptThread (threading.Thread):
    def run (self):

        while True:
            post("echo hello\n")
            time.sleep(1)

x = ScriptThread()
x.start()

Save this as "test_script.py"

Use the script as follows:

gdb
> source test_script.py

Note: that you can also pipe "source test_script.py", but you need to keep the pipe open.

Once the thread is started GDB will wait for the thread to end and will process any commands you send to it via the "post_event" function. Even "interrupt"!

Comments

0

Little background

After spending a good amount of time, here's a summary:

The initial bug, being that the GIL does not get released when executing GDB command, was actually fixed in 2014. 1 2

That should have allowed to do something like

gdb.execute("continue")
# Do some processing in the main thread
gdb.execute("interrupt")

However, a few years later, a series of bugs due to a too early release of the GIL were undiscovered, and so other patches were added, which again removed the ability to do so. here's one of them

Also, a Threading module module was added, which can thus execute things in GDB's context, while the GIL is still locked.

In 2023, gdb.interrupt was added. You can find the docs in the Threading module also (not detected by intellisense on my machine, but it does work)

It helps in that it only interrupts if the target is actually running, and sends a Ctrl-C if needed. If you don't want your script to be interrupted by it, you have to catch the exception. I personnally handle it like that:

def halt(self):
    try:
        gdb.interrupt()
    except KeyboardInterrupt:
        pass

Current Situation

If you need to programmatically interrupt GDB, @Chris Moore's Answer remains the best one.

def run_and_stop():
   gdb.post_event(lambda: gdb.interrupt())
   gdb.execute("continue")

I personnally use it like that:

def main():
    while True:
        backtrace = gdb.execute("backtrace")

        def do_async():
            process_backtrace(backtrace)
            halt()
        
        gdb.post_event(do_async)
        gdb.execute("resume")

A few more notes

  • I could not manage to manage to make gdb.execute("continue &") to work.

  • For some reason, executing interrupt when already halted, then continue immediately halted upon resume, like if it had been without the need to run interrupt again. In my case I wanted to make sure it ran for at least a few milliseconds between each run, so I could not use that.

  • Halting repeatedly like this is extremely innefficient, with pyOCD, I can get 2 fps, and with OpenOCD I can get 4.

  • You may be tempted to use monitor resume and monitor halt instead. Not that you'll then have to use maintenance clear register-cache, and that you'll often still end up with broken stacktraces. Why? Good question.

  • My Atmel-ICE crashes after a few minutes when I do that repeatedly. Not sure why. It seems to be less frequent when I stop with PyOCD instead of OpenOCD. Is it juste because of the speed?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.