@menu
* Basic Python:: Basic Python Functions.
+* Threading in GDB:: Using Python threads in GDB.
* Exception Handling:: How Python exceptions are translated.
* Values From Inferior:: Python representation of values.
* Types In Python:: Python representation of types.
historical compatibility.
@end defun
-@defun gdb.post_event (event)
-Put @var{event}, a callable object taking no arguments, into
-@value{GDBN}'s internal event queue. This callable will be invoked at
-some later point, during @value{GDBN}'s event processing. Events
-posted using @code{post_event} will be run in the order in which they
-were posted; however, there is no way to know when they will be
-processed relative to other events inside @value{GDBN}.
-
-@value{GDBN} is not thread-safe. If your Python program uses multiple
-threads, you must be careful to only call @value{GDBN}-specific
-functions in the @value{GDBN} thread. @code{post_event} ensures
-this. For example:
-
-@smallexample
-(@value{GDBP}) python
->import threading
->
->class Writer():
-> def __init__(self, message):
-> self.message = message;
-> def __call__(self):
-> gdb.write(self.message)
->
->class MyThread1 (threading.Thread):
-> def run (self):
-> gdb.post_event(Writer("Hello "))
->
->class MyThread2 (threading.Thread):
-> def run (self):
-> gdb.post_event(Writer("World\n"))
->
->MyThread1().start()
->MyThread2().start()
->end
-(@value{GDBP}) Hello World
-@end smallexample
-@end defun
-
@defun gdb.write (string @r{[}, stream@r{]})
Print a string to @value{GDBN}'s paginated output stream. The
optional @var{stream} determines the stream to print to. The default
cases, as that is not affected by the user's language setting.
@end defun
+@node Threading in GDB
+@subsubsection Threading in GDB
+
+@value{GDBN} is not thread-safe. If your Python program uses multiple
+threads, you must be careful to only call @value{GDBN}-specific
+functions in the @value{GDBN} thread. @value{GDBN} provides some
+functions to help with this.
+
+@defun gdb.block_signals ()
+As mentioned earlier (@pxref{Basic Python}), certain signals must be
+delivered to the @value{GDBN} main thread. The @code{block_signals}
+function returns a context manager that will block these signals on
+entry. This can be used when starting a new thread to ensure that the
+signals are blocked there, like:
+
+@smallexample
+with gdb.block_signals():
+ start_new_thread()
+@end smallexample
+@end defun
+
+@deftp {class} gdb.Thread
+This is a subclass of Python's @code{threading.Thread} class. It
+overrides the @code{start} method to call @code{block_signals}, making
+this an easy-to-use drop-in replacement for creating threads that will
+work well in @value{GDBN}.
+@end deftp
+
+@defun gdb.post_event (event)
+Put @var{event}, a callable object taking no arguments, into
+@value{GDBN}'s internal event queue. This callable will be invoked at
+some later point, during @value{GDBN}'s event processing. Events
+posted using @code{post_event} will be run in the order in which they
+were posted; however, there is no way to know when they will be
+processed relative to other events inside @value{GDBN}.
+
+Unlike most Python APIs in @value{GDBN}, @code{post_event} is
+thread-safe. For example:
+
+@smallexample
+(@value{GDBP}) python
+>import threading
+>
+>class Writer():
+> def __init__(self, message):
+> self.message = message;
+> def __call__(self):
+> gdb.write(self.message)
+>
+>class MyThread1 (threading.Thread):
+> def run (self):
+> gdb.post_event(Writer("Hello "))
+>
+>class MyThread2 (threading.Thread):
+> def run (self):
+> gdb.post_event(Writer("World\n"))
+>
+>MyThread1().start()
+>MyThread2().start()
+>end
+(@value{GDBP}) Hello World
+@end smallexample
+@end defun
+
+
@node Exception Handling
@subsubsection Exception Handling
@cindex python exceptions
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import signal
+import threading
import traceback
import os
import sys
yield None
finally:
set_parameter(name, old_value)
+
+
+@contextmanager
+def blocked_signals():
+ """A helper function that blocks and unblocks signals."""
+ if not hasattr(signal, "pthread_sigmask"):
+ yield
+ return
+
+ to_block = {signal.SIGCHLD, signal.SIGINT, signal.SIGALRM, signal.SIGWINCH}
+ signal.pthread_sigmask(signal.SIG_BLOCK, to_block)
+ try:
+ yield None
+ finally:
+ signal.pthread_sigmask(signal.SIG_UNBLOCK, to_block)
+
+
+class Thread(threading.Thread):
+ """A GDB-specific wrapper around threading.Thread
+
+ This wrapper ensures that the new thread blocks any signals that
+ must be delivered on GDB's main thread."""
+
+ def start(self):
+ # GDB requires that these be delivered to the main thread. We
+ # do this here to avoid any possible race with the creation of
+ # the new thread. The thread mask is inherited by new
+ # threads.
+ with blocked_signals():
+ super().start()
import functools
import gdb
import queue
-import signal
import threading
import traceback
-from contextlib import contextmanager
import sys
_dap_thread = None
-@contextmanager
-def blocked_signals():
- """A helper function that blocks and unblocks signals."""
- if not hasattr(signal, "pthread_sigmask"):
- yield
- return
-
- to_block = {signal.SIGCHLD, signal.SIGINT, signal.SIGALRM, signal.SIGWINCH}
- signal.pthread_sigmask(signal.SIG_BLOCK, to_block)
- try:
- yield None
- finally:
- signal.pthread_sigmask(signal.SIG_UNBLOCK, to_block)
-
-
def start_thread(name, target, args=()):
"""Start a new thread, invoking TARGET with *ARGS there.
This is a helper function that ensures that any GDB signals are
correctly blocked."""
- # GDB requires that these be delivered to the gdb thread. We
- # do this here to avoid any possible race with the creation of
- # the new thread. The thread mask is inherited by new
- # threads.
- with blocked_signals():
- result = threading.Thread(target=target, args=args, daemon=True)
- result.start()
+ result = gdb.Thread(target=target, args=args, daemon=True)
+ result.start()
def start_dap(target):