Add new_inferior, inferior_deleted, and new_thread events
authorTom Tromey <tom@tromey.com>
Tue, 5 Sep 2017 18:07:00 +0000 (12:07 -0600)
committerTom Tromey <tom@tromey.com>
Mon, 11 Sep 2017 20:15:20 +0000 (14:15 -0600)
This adds a few new events to gdb's Python layer: new_inferior,
inferior_deleted, and new_thread.  I wanted to be able to add a
combined inferior/thread display window to my GUI, and I needed a few
events to make this work.  This is PR python/15622.

ChangeLog
2017-09-11  Tom Tromey  <tom@tromey.com>

PR python/15622:
* NEWS: Add entry.
* python/python.c (do_start_initialization): Initialize new event
types.
* python/python-internal.h (gdbpy_initialize_new_inferior_event)
(gdbpy_initialize_inferior_deleted_event)
(gdbpy_initialize_new_thread_event): Declare.
* python/py-threadevent.c (create_thread_event_object): Add option
"thread" parameter.
* python/py-inferior.c (new_thread_event_object_type)
(new_inferior_event_object_type)
(inferior_deleted_event_object_type): Declare.
(python_new_inferior, python_inferior_deleted): New functions.
(add_thread_object): Emit new_thread event.
(gdbpy_initialize_inferior): Attach new functions to corresponding
observers.
(new_thread, new_inferior, inferior_deleted): Define new event
types.
* python/py-evts.c (gdbpy_initialize_py_events): Add new
registries.
* python/py-events.h (events_object) <new_inferior,
inferior_deleted, new_thread>: New fields.
* python/py-event.h (create_thread_event_breakpoint): Add optional
"thread" parameter.

doc/ChangeLog
2017-09-11  Tom Tromey  <tom@tromey.com>

* python.texi (Events In Python): Document new events.

testsuite/ChangeLog
2017-09-11  Tom Tromey  <tom@tromey.com>

* gdb.python/py-infthread.exp: Add tests for new_thread event.
* gdb.python/py-inferior.exp: Add tests for new inferior events.

14 files changed:
gdb/ChangeLog
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/python.texi
gdb/python/py-event.h
gdb/python/py-events.h
gdb/python/py-evts.c
gdb/python/py-inferior.c
gdb/python/py-threadevent.c
gdb/python/python-internal.h
gdb/python/python.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.python/py-inferior.exp
gdb/testsuite/gdb.python/py-infthread.exp

index 1781ddd9f5e37e527e2ce577d6c37ddc58119342..086fff5aff5aa3afb811d220cb3f8142349d2be9 100644 (file)
@@ -1,3 +1,30 @@
+2017-09-11  Tom Tromey  <tom@tromey.com>
+
+       PR python/15622:
+       * NEWS: Add entry.
+       * python/python.c (do_start_initialization): Initialize new event
+       types.
+       * python/python-internal.h (gdbpy_initialize_new_inferior_event)
+       (gdbpy_initialize_inferior_deleted_event)
+       (gdbpy_initialize_new_thread_event): Declare.
+       * python/py-threadevent.c (create_thread_event_object): Add option
+       "thread" parameter.
+       * python/py-inferior.c (new_thread_event_object_type)
+       (new_inferior_event_object_type)
+       (inferior_deleted_event_object_type): Declare.
+       (python_new_inferior, python_inferior_deleted): New functions.
+       (add_thread_object): Emit new_thread event.
+       (gdbpy_initialize_inferior): Attach new functions to corresponding
+       observers.
+       (new_thread, new_inferior, inferior_deleted): Define new event
+       types.
+       * python/py-evts.c (gdbpy_initialize_py_events): Add new
+       registries.
+       * python/py-events.h (events_object) <new_inferior,
+       inferior_deleted, new_thread>: New fields.
+       * python/py-event.h (create_thread_event_breakpoint): Add optional
+       "thread" parameter.
+
 2017-09-10  Andrew Burgess  <andrew.burgess@embecosm.com>
 
        * utils.c (abort_with_message): Don't compare gdb_stderr to NULL,
index f6ed6140fae84c390b4d2a0bb81ac90f5ec03a7b..2e6d48c016aeb28fd714d5f301795981b7ecd1ad 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
   the remote inferior is started by the GDBserver, use the "unset
   environment" command.
 
+* Python Scripting
+
+  ** New events gdb.new_inferior, gdb.inferior_deleted, and
+     gdb.new_thread are emitted.  See the manual for further
+     description of these.
+
 * New features in the GDB remote stub, GDBserver
 
   ** New "--selftest" command line option runs some GDBserver self
index 72f3d9e7c81bc4a2ea75fbf03de635e830745ec8..db2f64f2dc6be88a63c0f829667009e4f630ea7b 100644 (file)
@@ -1,3 +1,7 @@
+2017-09-11  Tom Tromey  <tom@tromey.com>
+
+       * python.texi (Events In Python): Document new events.
+
 2017-09-04  Pedro Alves  <palves@redhat.com>
 
        * gdb.texinfo (Variables) <Program Variables>: Document inspecting
index 32d7939e669d10751e415c5a50b8711c7060cc4b..39def2aa6f237d0585bd3bb5bbfe36d0dab5899f 100644 (file)
@@ -2989,6 +2989,39 @@ invalid state; that is, the @code{is_valid} method will return
 This event carries no payload.  It is emitted each time @value{GDBN}
 presents a prompt to the user.
 
+@item events.new_inferior
+This is emitted when a new inferior is created.  Note that the
+inferior is not necessarily running; in fact, it may not even have an
+associated executable.
+
+The event is of type @code{gdb.NewInferiorEvent}.  This has a single
+attribute:
+
+@defvar NewInferiorEvent.inferior
+The new inferior, a @code{gdb.Inferior} object.
+@end defvar
+
+@item events.inferior_deleted
+This is emitted when an inferior has been deleted.  Note that this is
+not the same as process exit; it is notified when the inferior itself
+is removed, say via @code{remove-inferiors}.
+
+The event is of type @code{gdb.InferiorDeletedEvent}.  This has a single
+attribute:
+
+@defvar NewInferiorEvent.inferior
+The inferior that is being removed, a @code{gdb.Inferior} object.
+@end defvar
+
+@item events.new_thread
+This is emitted when @value{GDBN} notices a new thread.  The event is of
+type @code{gdb.NewThreadEvent}, which extends @code{gdb.ThreadEvent}.
+This has a single attribute:
+
+@defvar NewThreadEvent.inferior_thread
+The new thread.
+@end defvar
+
 @end table
 
 @node Threads In Python
index ccb851339c8ede138f2b0f6f96f2e2b5d725d851..2f02c5f861c2d03acdcbf6a4a7c6b156184e535c 100644 (file)
@@ -125,7 +125,8 @@ extern int evpy_emit_event (PyObject *event,
                             eventregistry_object *registry);
 
 extern PyObject *create_event_object (PyTypeObject *py_type);
-extern PyObject *create_thread_event_object (PyTypeObject *py_type);
+extern PyObject *create_thread_event_object (PyTypeObject *py_type,
+                                            PyObject *thread = nullptr);
 extern int emit_new_objfile_event (struct objfile *objfile);
 extern int emit_clear_objfiles_event (void);
 
index 348dabc92255d64326d35e8da8810cc486c5a934..2275d896e64fafa90176f7a08eb7cf2b8a69a3e1 100644 (file)
@@ -47,6 +47,9 @@ typedef struct
   eventregistry_object *exited;
   eventregistry_object *new_objfile;
   eventregistry_object *clear_objfiles;
+  eventregistry_object *new_inferior;
+  eventregistry_object *inferior_deleted;
+  eventregistry_object *new_thread;
   eventregistry_object *inferior_call;
   eventregistry_object *memory_changed;
   eventregistry_object *register_changed;
index 126d18cead414e1cbab0a124bc54b24f87c2db64..ad9924190c3db2e71b138d433f9631e771ffa22e 100644 (file)
@@ -89,6 +89,15 @@ gdbpy_initialize_py_events (void)
   if (add_new_registry (&gdb_py_events.clear_objfiles, "clear_objfiles") < 0)
     return -1;
 
+  if (add_new_registry (&gdb_py_events.new_inferior, "new_inferior") < 0)
+    return -1;
+
+  if (add_new_registry (&gdb_py_events.inferior_deleted, "inferior_deleted") < 0)
+    return -1;
+
+  if (add_new_registry (&gdb_py_events.new_thread, "new_thread") < 0)
+    return -1;
+
   if (add_new_registry (&gdb_py_events.breakpoint_created,
                        "breakpoint_created") < 0)
     return -1;
index f6a24a090f2e5834e9925daf4a7ada9197e5c452..d7c6810884fbb7d48576249127f7c7c4a74935bc 100644 (file)
 #include "py-event.h"
 #include "py-stopevent.h"
 
+extern PyTypeObject new_thread_event_object_type
+    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("event_object");
+extern PyTypeObject new_inferior_event_object_type
+    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("event_object");
+extern PyTypeObject inferior_deleted_event_object_type
+    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("event_object");
+
 struct threadlist_entry {
   thread_object *thread_obj;
   struct threadlist_entry *next;
@@ -235,6 +242,60 @@ inferior_to_inferior_object (struct inferior *inferior)
   return (PyObject *) inf_obj;
 }
 
+/* Called when a new inferior is created.  Notifies any Python event
+   listeners.  */
+static void
+python_new_inferior (struct inferior *inf)
+{
+  if (!gdb_python_initialized)
+    return;
+
+  gdbpy_enter enter_py (python_gdbarch, python_language);
+
+  if (evregpy_no_listeners_p (gdb_py_events.new_inferior))
+    return;
+
+  gdbpy_ref<> inf_obj (inferior_to_inferior_object (inf));
+  if (inf_obj == NULL)
+    {
+      gdbpy_print_stack ();
+      return;
+    }
+
+  gdbpy_ref<> event (create_event_object (&new_inferior_event_object_type));
+  if (event == NULL
+      || evpy_add_attribute (event.get (), "inferior", inf_obj.get ()) < 0
+      || evpy_emit_event (event.get (), gdb_py_events.new_inferior) < 0)
+    gdbpy_print_stack ();
+}
+
+/* Called when an inferior is removed.  Notifies any Python event
+   listeners.  */
+static void
+python_inferior_deleted (struct inferior *inf)
+{
+  if (!gdb_python_initialized)
+    return;
+
+  gdbpy_enter enter_py (python_gdbarch, python_language);
+
+  if (evregpy_no_listeners_p (gdb_py_events.inferior_deleted))
+    return;
+
+  gdbpy_ref<> inf_obj (inferior_to_inferior_object (inf));
+  if (inf_obj == NULL)
+    {
+      gdbpy_print_stack ();
+      return;
+    }
+
+  gdbpy_ref<> event (create_event_object (&inferior_deleted_event_object_type));
+  if (event == NULL
+      || evpy_add_attribute (event.get (), "inferior", inf_obj.get ()) < 0
+      || evpy_emit_event (event.get (), gdb_py_events.inferior_deleted) < 0)
+    gdbpy_print_stack ();
+}
+
 /* Finds the Python Inferior object for the given PID.  Returns a
    reference, or NULL if PID does not match any inferior object. */
 
@@ -298,6 +359,15 @@ add_thread_object (struct thread_info *tp)
 
   inf_obj->threads = entry;
   inf_obj->nthreads++;
+
+  if (evregpy_no_listeners_p (gdb_py_events.new_thread))
+    return;
+
+  gdbpy_ref<> event (create_thread_event_object (&new_thread_event_object_type,
+                                                (PyObject *) thread_obj));
+  if (event == NULL
+      || evpy_emit_event (event.get (), gdb_py_events.new_thread) < 0)
+    gdbpy_print_stack ();
 }
 
 static void
@@ -823,6 +893,8 @@ gdbpy_initialize_inferior (void)
   observer_attach_register_changed (python_on_register_change);
   observer_attach_inferior_exit (python_inferior_exit);
   observer_attach_new_objfile (python_new_objfile);
+  observer_attach_inferior_added (python_new_inferior);
+  observer_attach_inferior_removed (python_inferior_deleted);
 
   membuf_object_type.tp_new = PyType_GenericNew;
   if (PyType_Ready (&membuf_object_type) < 0)
@@ -970,3 +1042,19 @@ PyTypeObject membuf_object_type = {
   0,                             /* tp_init */
   0,                             /* tp_alloc */
 };
+
+GDBPY_NEW_EVENT_TYPE (new_thread,
+                     "gdb.NewThreadEvent",
+                     "NewThreadEvent",
+                     "GDB new thread event object",
+                     thread_event_object_type);
+GDBPY_NEW_EVENT_TYPE (new_inferior,
+                     "gdb.NewInferiorEvent",
+                     "NewInferiorEvent",
+                     "GDB new inferior event object",
+                     event_object_type);
+GDBPY_NEW_EVENT_TYPE (inferior_deleted,
+                     "gdb.InferiorDeletedEvent",
+                     "InferiorDeletedEvent",
+                     "GDB inferior deleted event object",
+                     event_object_type);
index 921744442f7bf24817a3ea67d854c4c82076ea96..7211fa26da2a168846cf67474a800ed026a11868 100644 (file)
@@ -48,17 +48,18 @@ get_event_thread (void)
 }
 
 PyObject *
-create_thread_event_object (PyTypeObject *py_type)
+create_thread_event_object (PyTypeObject *py_type, PyObject *thread)
 {
-  PyObject *thread = NULL;
-
   gdbpy_ref<> thread_event_obj (create_event_object (py_type));
   if (thread_event_obj == NULL)
     return NULL;
 
-  thread = get_event_thread ();
-  if (!thread)
-    return NULL;
+  if (thread == NULL)
+    {
+      thread = get_event_thread ();
+      if (!thread)
+       return NULL;
+    }
 
   if (evpy_add_attribute (thread_event_obj.get (),
                           "inferior_thread",
index ebb83f0cba8465b1f8d0f2a8b571bb989ceaf36e..0c3582f180f664a3f2e42ff3392678efecff88bf 100644 (file)
@@ -630,6 +630,12 @@ int gdbpy_initialize_new_objfile_event (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 int gdbpy_initialize_clear_objfiles_event (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_new_inferior_event (void)
+  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_inferior_deleted_event (void)
+  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_new_thread_event (void)
+  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 int gdbpy_initialize_arch (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 int gdbpy_initialize_xmethods (void)
index b086cef2510f83f92ebecce5c45da30a3dcb0ae6..fbb4747c5fa635d2855156a84663a9df693d8981 100644 (file)
@@ -1603,6 +1603,9 @@ do_start_initialization ()
       || gdbpy_initialize_thread_event () < 0
       || gdbpy_initialize_new_objfile_event ()  < 0
       || gdbpy_initialize_clear_objfiles_event ()  < 0
+      || gdbpy_initialize_new_inferior_event () < 0
+      || gdbpy_initialize_inferior_deleted_event () < 0
+      || gdbpy_initialize_new_thread_event () < 0
       || gdbpy_initialize_arch () < 0
       || gdbpy_initialize_xmethods () < 0
       || gdbpy_initialize_unwind () < 0)
index 8d8dc3c04c86915aca04a3683dba1c89f41d101a..3862a7c861f7befc9a35281d4c7e4879607c18a4 100644 (file)
@@ -1,3 +1,8 @@
+2017-09-11  Tom Tromey  <tom@tromey.com>
+
+       * gdb.python/py-infthread.exp: Add tests for new_thread event.
+       * gdb.python/py-inferior.exp: Add tests for new inferior events.
+
 2017-09-08  Christoph Weinmann  <christoph.t.weinmann@intel.com>
 
        * gdb.fortran/printing-types.exp: New file.
index c2ea2c2a4bc9e4dacc791d5d993e3a00f3ee4e91..715c693c4085b509c3e8f9dee13eff86e7c87235 100644 (file)
@@ -214,12 +214,33 @@ with_test_prefix "is_valid" {
     gdb_test "python print (inf_list\[0\].is_valid())" "True" \
        "check inferior validity 1"
 
+    gdb_py_test_multiple "install new inferior event handler" \
+       "python" "" \
+       "my_inferior_count = 1" "" \
+       "def new_inf_handler(evt):" "" \
+       "  global my_inferior_count" "" \
+       "  if evt.inferior is not None:" "" \
+       "    my_inferior_count = my_inferior_count + 1" "" \
+       "gdb.events.new_inferior.connect(new_inf_handler)" "" \
+       "end" ""
+    gdb_py_test_multiple "install inferior deleted event handler" \
+       "python" "" \
+       "def del_inf_handler(evt):" "" \
+       "  global my_inferior_count" "" \
+       "  if evt.inferior is not None:" "" \
+       "    my_inferior_count = my_inferior_count - 1" "" \
+       "gdb.events.inferior_deleted.connect(del_inf_handler)" "" \
+       "end" ""
+
     gdb_test "add-inferior" "Added inferior 2.*" "add empty inferior 2"
     gdb_py_test_silent_cmd "python inf_list = gdb.inferiors()" "get new list" 1
     gdb_test "python print (len(inf_list))" "2" "get inferior list length 2"
     gdb_test "python print (inf_list\[0\].is_valid())" "True" \
        "check inferior validity 2"
 
+    gdb_test "python print (my_inferior_count)" "2" \
+       "test new-inferior event handler"
+
     gdb_test "python print (inf_list\[1\].is_valid())" "True" \
        "check inferior validity 3"
 
@@ -229,6 +250,9 @@ with_test_prefix "is_valid" {
 
     gdb_test "python print (inf_list\[1\].is_valid())" "False" \
        "check inferior validity 5"
+
+    gdb_test "python print (my_inferior_count)" "1" \
+       "test inferior-deleted event handler"
 }
 
 # Test gdb.selected_inferior()
index a5fed8d893411b7aba70b56685e62c19d93dde64..0711d6994e8878c75b021c8697296d05747dffb5 100644 (file)
@@ -30,6 +30,16 @@ clean_restart ${testfile}
 # Skip all tests if Python scripting is not enabled.
 if { [skip_python_tests] } { continue }
 
+gdb_py_test_multiple "install new_thread event handler" \
+    "python" "" \
+    "seen_a_thread = False" "" \
+    "def thread_handler(evt):" "" \
+    "  if evt.inferior_thread is not None:" "" \
+    "    global seen_a_thread" "" \
+    "    seen_a_thread = True" "" \
+    "gdb.events.new_thread.connect(thread_handler)" "" \
+    "end" ""
+
 # The following tests require execution.
 
 if ![runto_main] then {
@@ -37,6 +47,8 @@ if ![runto_main] then {
     return 0
 }
 
+gdb_test "python print(seen_a_thread)" "True"
+
 # Test basic gdb.Inferior attributes and methods.
 
 gdb_py_test_silent_cmd "python t0 = gdb.selected_thread ()" "test gdb.selected_thread" 1