diff options
-rw-r--r-- | gdb/ChangeLog | 8 | ||||
-rw-r--r-- | gdb/doc/ChangeLog | 5 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 39 | ||||
-rw-r--r-- | gdb/python/python.c | 113 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/python.exp | 14 |
6 files changed, 184 insertions, 0 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 5098a78..2b548b7 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,11 @@ +2010-08-11 Tom Tromey <tromey@redhat.com> + Phil Muldoon <pmuldoon@redhat.com> + + * python/python.c (gdbpy_run_events): New function. + (gdbpy_post_event): Likewise. + (gdbpy_initialize_events): Likewise. + (_initialize_python): Call gdbpy_initialize_events. + 2010-08-11 Ken Werner <ken.werner@de.ibm.com> * gdb/valarith.c (vector_binop): New function. diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 9cc3664..5745151 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2010-08-11 Tom Tromey <tromey@redhat.com> + Phil Muldoon <pmuldoon@redhat.com> + + * gdb.texinfo (Basic Python): Describe post_event API. + 2010-08-11 Phil Muldoon <pmuldoon@redhat.com> * gdb.texinfo (Basic Python): Describe solib_address and diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 9851212..ba1607c 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -20525,6 +20525,45 @@ compute values, for example, it is the only way to get the value of a convenience variable (@pxref{Convenience Vars}) as a @code{gdb.Value}. @end defun +@findex gdb.post_event +@defun 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 main @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 + @findex gdb.write @defun write string Print a string to @value{GDBN}'s paginated standard output stream. diff --git a/gdb/python/python.c b/gdb/python/python.c index 16c3cba..030b142 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -28,6 +28,7 @@ #include "value.h" #include "language.h" #include "exceptions.h" +#include "event-loop.h" #include <ctype.h> @@ -548,6 +549,114 @@ source_python_script (FILE *stream, const char *file) +/* Posting and handling events. */ + +/* A single event. */ +struct gdbpy_event +{ + /* The Python event. This is just a callable object. */ + PyObject *event; + /* The next event. */ + struct gdbpy_event *next; +}; + +/* All pending events. */ +static struct gdbpy_event *gdbpy_event_list; +/* The final link of the event list. */ +static struct gdbpy_event **gdbpy_event_list_end; + +/* We use a file handler, and not an async handler, so that we can + wake up the main thread even when it is blocked in poll(). */ +static int gdbpy_event_fds[2]; + +/* The file handler callback. This reads from the internal pipe, and + then processes the Python event queue. This will always be run in + the main gdb thread. */ +static void +gdbpy_run_events (int err, gdb_client_data ignore) +{ + struct cleanup *cleanup; + char buffer[100]; + int r; + + cleanup = ensure_python_env (get_current_arch (), current_language); + + /* Just read whatever is available on the fd. It is relatively + harmless if there are any bytes left over. */ + r = read (gdbpy_event_fds[0], buffer, sizeof (buffer)); + + while (gdbpy_event_list) + { + /* Dispatching the event might push a new element onto the event + loop, so we update here "atomically enough". */ + struct gdbpy_event *item = gdbpy_event_list; + gdbpy_event_list = gdbpy_event_list->next; + if (gdbpy_event_list == NULL) + gdbpy_event_list_end = &gdbpy_event_list; + + /* Ignore errors. */ + if (PyObject_CallObject (item->event, NULL) == NULL) + PyErr_Clear (); + + Py_DECREF (item->event); + xfree (item); + } + + do_cleanups (cleanup); +} + +/* Submit an event to the gdb thread. */ +static PyObject * +gdbpy_post_event (PyObject *self, PyObject *args) +{ + struct gdbpy_event *event; + PyObject *func; + int wakeup; + + if (!PyArg_ParseTuple (args, "O", &func)) + return NULL; + + if (!PyCallable_Check (func)) + { + PyErr_SetString (PyExc_RuntimeError, + _("Posted event is not callable")); + return NULL; + } + + Py_INCREF (func); + + /* From here until the end of the function, we have the GIL, so we + can operate on our global data structures without worrying. */ + wakeup = gdbpy_event_list == NULL; + + event = XNEW (struct gdbpy_event); + event->event = func; + event->next = NULL; + *gdbpy_event_list_end = event; + gdbpy_event_list_end = &event->next; + + /* Wake up gdb when needed. */ + if (wakeup) + { + char c = 'q'; /* Anything. */ + if (write (gdbpy_event_fds[1], &c, 1) != 1) + return PyErr_SetFromErrno (PyExc_IOError); + } + + Py_RETURN_NONE; +} + +/* Initialize the Python event handler. */ +static void +gdbpy_initialize_events (void) +{ + if (!pipe (gdbpy_event_fds)) + { + gdbpy_event_list_end = &gdbpy_event_list; + add_file_handler (gdbpy_event_fds[0], gdbpy_run_events, NULL); + } +} + /* Printing. */ /* A python function to write a single string using gdb's filtered @@ -854,6 +963,7 @@ Enables or disables printing of Python stack traces."), gdbpy_initialize_lazy_string (); gdbpy_initialize_thread (); gdbpy_initialize_inferior (); + gdbpy_initialize_events (); PyRun_SimpleString ("import gdb"); PyRun_SimpleString ("gdb.pretty_printers = []"); @@ -974,6 +1084,9 @@ gdb.Symtab_and_line objects (or None)."}, Parse String as an expression, evaluate it, and return the result as a Value." }, + { "post_event", gdbpy_post_event, METH_VARARGS, + "Post an event into gdb's event loop." }, + { "target_charset", gdbpy_target_charset, METH_NOARGS, "target_charset () -> string.\n\ Return the name of the current target charset." }, diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 479e563..e26a138 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2010-08-11 Phil Muldoon <pmuldoon@redhat.com> + + * gdb.python/python.exp (gdb_py_test_multiple): Add gdb.post_event + tests. + 2010-08-11 Ken Werner <ken.werner@de.ibm.com> * gdb.base/Makefile.in (EXECUTABLES): Add gnu_vector. diff --git a/gdb/testsuite/gdb.python/python.exp b/gdb/testsuite/gdb.python/python.exp index e153ab8..dc04911 100644 --- a/gdb/testsuite/gdb.python/python.exp +++ b/gdb/testsuite/gdb.python/python.exp @@ -120,6 +120,20 @@ gdb_test_no_output \ "python x = gdb.execute('printf \"%d\", 23', to_string = True)" gdb_test "python print x" "23" +# Test post_event. +gdb_py_test_multiple "post event insertion" \ + "python" "" \ + "someVal = 0" "" \ + "class Foo():" "" \ + " def __call__(self):" "" \ + " global someVal" "" \ + " someVal += 1" "" \ + "gdb.post_event(Foo())" "" \ + "end" "" + +gdb_test "python print someVal" "1" "test post event execution" +gdb_test "python gdb.post_event(str(1))" "RuntimeError: Posted event is not callable.*" "Test non callable class" + # Test (no) pagination of the executed command. gdb_test "show height" {Number of lines gdb thinks are in a page is unlimited\.} set lines 10 |