Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ thread not stopping in gdb async mode using user-defined or python command sequence

I'm using gdb 7.4.1 on embedded powerpc target to perform some analysis on my multi-threaded C++ program that uses pthreads. My end goal is to script gdb with python to automate some common analysis functions. The problem is that I am finding some discrepancy in behavior when I run commands individually vs. in a gdb user-defined command (or invoking the same commands via python script).

edit: I found this reference to a very similar problem on the main gdb mailing list. Although I don't completely follow Pedro's response about the limitation of async mode, I think he's implying that in async mode, the relative timing of user-defined command sequences cannot be trusted. This is what I found empirically.

In both scenarios, I perform the following start-up steps, loading my program, setting its args, and turning on asynchronous and non-stop debugging modes, then running the program in the background:

(gdb) file myprogram
(gdb) set args --interface=eth0 --try-count=0
(gdb) set target-async on
(gdb) set pagination off
(gdb) set non-stop on
(gdb) run &

At this point, if I manually issue interrupt and then info threads commands, I see the list of all threads running except one that got stopped. Then I can continue & and repeat to my hearts content, it works consistently. When stopped, I can inspect that thread's stack frames and all is well.

However, if instead I put these commands into a user-defined gdb command:

(gdb) define foo
(gdb) interrupt
(gdb) info threads
(gdb) continue &
(gdb) end
(gdb) foo
Cannot execute this command while the selected thread is running.

Then the thread list printed by foo indicates no threads were stopped, and so the continue & command returns Cannot execute this command while the selected thread is running.. I thought this was a problem inherent to the asynchronous gdb commanding, so I inserted an absurdly long wait after the interrupt command and got the same behavior:

(gdb) define foo
(gdb) interrupt
(gdb) shell sleep 5
(gdb) info threads
(gdb) continue &
(gdb) end
(gdb) foo
Cannot execute this command while the selected thread is running.

With or without the sleep command, I can always issue the manual CLI commands and the threads get stopped correctly.

Similarly, I get the same results sourcing a python script to do the thread perusal:

import gdb, time

gdb.execute("file myprogram")
gdb.execute("set args --interface=eth0 --try-count=0")
gdb.execute("set target-async on")
gdb.execute("set pagination off") 
gdb.execute("set non-stop on")
gdb.execute("run &")
time.sleep(5)
gdb.execute("interrupt")

# here, I inspect threads via gdb module interface
# in practice, they're always all running bc the program neven got interrupted
for thread in gdb.selected_inferior().threads():
    print thread.is_running(),

gdb.execute("continue &")

I get the same result even if I specify from_tty=True in the gdb.execute calls. Also, if I use continue -a it suppresses the error string but does not help otherwise bc the interrupt call still doesn't work.

So... is this:

  • cockpit error? Is there something that I'm omitting or doing incorrectly, given what I'm trying to accomplish? Should this work, or do I have to use GDB/MI to asynchronously "drive" gdb like this?
  • a timing problem? Maybe invoking shell sleep (or python time.sleep()) doesn't do what I assume it would, in this context.
  • problem with my usage of pthreads? I have assumed that since using manual gdb commands always works correctly this is not the case.
  • a gdb problem?

Thanks.

like image 525
timblaktu Avatar asked May 15 '12 18:05

timblaktu


1 Answers

I think this is most likely a gdb problem. I don't know enough about the inferior-control stuff to be more confident. I do know that inferior control generally has not been wired up to Python...

One thing worth trying is having a separate Python thread that does the wait, then sends an "interrupt" command to the main gdb thread using gdb.post_event.

Then, instead of synchronously examining the threads or doing work after the "interrupt", instead use the gdb.events.stop event source to trigger your actions.

Please file bugs liberally about holes in the Python API.

like image 189
Tom Tromey Avatar answered Sep 30 '22 02:09

Tom Tromey