PR python/15620, PR python/18620 - breakpoint events in Python
authorTom Tromey <tom@tromey.com>
Tue, 5 May 2015 03:49:11 +0000 (21:49 -0600)
committerTom Tromey <tom@tromey.com>
Wed, 13 Jul 2016 19:59:55 +0000 (13:59 -0600)
This patch adds some breakpoint events to Python.  In particular,
there is a creation event that is emitted when a breakpoint is
created; a modification event that is emitted when a breakpoint
changes somehow; and a deletion event that is emitted when a
breakpoint is deleted.

In this patch, the event's payload is the breakpoint itself.  I
considered making a new event type to hold the breakpoint, but I
didn't see a need.  Still, I thought I would mention this as a spot
where some other choice is possible.

Built and regtested on x86-64 Fedora 23.

2016-07-13  Tom Tromey  <tom@tromey.com>

PR python/15620, PR python/18620:
* python/py-evts.c (gdbpy_initialize_py_events): Call
add_new_registry for new events.
* python/py-events.h (events_object) <breakpoint_created,
breakpoint_deleted, breakpoint_modified>: New fields.
* python/py-breakpoint.c (gdbpy_breakpoint_created): Emit the
breakpoint changed event.
(gdbpy_breakpoint_deleted): Emit the breakpoint deleted event.
(gdbpy_breakpoint_modified): New function.
(gdbpy_initialize_breakpoints): Attach to the breakpoint modified
observer.

2016-07-13  Tom Tromey  <tom@tromey.com>

PR python/15620, PR python/18620:
* python.texi (Events In Python): Document new breakpoint events.

2016-07-13  Tom Tromey  <tom@tromey.com>

PR python/15620, PR python/18620:
* gdb.python/py-breakpoint.exp (connect_event, check_last_event)
(test_bkpt_events): New procs.

gdb/ChangeLog
gdb/doc/ChangeLog
gdb/doc/python.texi
gdb/python/py-breakpoint.c
gdb/python/py-events.h
gdb/python/py-evts.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.python/py-breakpoint.exp

index 6a426b8bb47c0968163c116b35d6147350b83353..ba207ba949606565a6a0d0b1921b532b592e2f1f 100644 (file)
@@ -1,3 +1,17 @@
+2016-07-13  Tom Tromey  <tom@tromey.com>
+
+       PR python/15620, PR python/18620:
+       * python/py-evts.c (gdbpy_initialize_py_events): Call
+       add_new_registry for new events.
+       * python/py-events.h (events_object) <breakpoint_created,
+       breakpoint_deleted, breakpoint_modified>: New fields.
+       * python/py-breakpoint.c (gdbpy_breakpoint_created): Emit the
+       breakpoint changed event.
+       (gdbpy_breakpoint_deleted): Emit the breakpoint deleted event.
+       (gdbpy_breakpoint_modified): New function.
+       (gdbpy_initialize_breakpoints): Attach to the breakpoint modified
+       observer.
+
 2016-07-13  Tom Tromey  <tom@tromey.com>
 
        PR python/17698:
index b1ddc289a25b8310325a52a0957680b735356555..f133f3349186e2ffa7c9c9cb5805bd2c95eeaea3 100644 (file)
@@ -1,3 +1,8 @@
+2016-07-13  Tom Tromey  <tom@tromey.com>
+
+       PR python/15620, PR python/18620:
+       * python.texi (Events In Python): Document new breakpoint events.
+
 2016-07-13  Tom Tromey  <tom@tromey.com>
 
        PR python/17698:
index e00bc2a7046e59301b99188bd18460a66dc2b40f..a17e37d6f3afa61fdf1fca8b5b0c4a53fd58b6fe 100644 (file)
@@ -2965,6 +2965,21 @@ A gdb.Frame object representing the frame in which the register was modified.
 Denotes which register was modified.
 @end defvar
 
+@item events.breakpoint_created
+This is emitted when a new breakpoint has been created.  The argument
+that is passed is the new @code{gdb.Breakpoint} object.
+
+@item events.breakpoint_modified
+This is emitted when a breakpoint has been modified in some way.  The
+argument that is passed is the new @code{gdb.Breakpoint} object.
+
+@item events.breakpoint_deleted
+This is emitted when a breakpoint has been deleted.  The argument that
+is passed is the @code{gdb.Breakpoint} object.  When this event is
+emitted, the @code{gdb.Breakpoint} object will already be in its
+invalid state; that is, the @code{is_valid} method will return
+@code{False}.
+
 @end table
 
 @node Threads In Python
index f2d4385c84e0309ddce23707732565a2d16a66cf..22de791decce176a81dcd175fe48162a76be9557 100644 (file)
@@ -31,6 +31,7 @@
 #include "arch-utils.h"
 #include "language.h"
 #include "location.h"
+#include "py-event.h"
 
 /* Number of live breakpoints.  */
 static int bppy_live;
@@ -916,6 +917,14 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       gdbpy_print_stack ();
     }
 
+  if (!evregpy_no_listeners_p (gdb_py_events.breakpoint_created))
+    {
+      Py_INCREF (newbp);
+      if (evpy_emit_event ((PyObject *) newbp,
+                          gdb_py_events.breakpoint_created) < 0)
+       gdbpy_print_stack ();
+    }
+
   PyGILState_Release (state);
 }
 
@@ -936,6 +945,15 @@ gdbpy_breakpoint_deleted (struct breakpoint *b)
       bp_obj = bp->py_bp_object;
       if (bp_obj)
        {
+         if (!evregpy_no_listeners_p (gdb_py_events.breakpoint_deleted))
+           {
+             PyObject *bp_obj_alias = (PyObject *) bp_obj;
+             Py_INCREF (bp_obj_alias);
+             if (evpy_emit_event (bp_obj_alias,
+                                  gdb_py_events.breakpoint_deleted) < 0)
+               gdbpy_print_stack ();
+           }
+
          bp_obj->bp = NULL;
          --bppy_live;
          Py_DECREF (bp_obj);
@@ -944,6 +962,35 @@ gdbpy_breakpoint_deleted (struct breakpoint *b)
   PyGILState_Release (state);
 }
 
+/* Callback that is used when a breakpoint is modified.  */
+
+static void
+gdbpy_breakpoint_modified (struct breakpoint *b)
+{
+  int num = b->number;
+  PyGILState_STATE state;
+  struct breakpoint *bp = NULL;
+  gdbpy_breakpoint_object *bp_obj;
+
+  state = PyGILState_Ensure ();
+  bp = get_breakpoint (num);
+  if (bp)
+    {
+      PyObject *bp_obj = (PyObject *) bp->py_bp_object;
+      if (bp_obj)
+       {
+         if (!evregpy_no_listeners_p (gdb_py_events.breakpoint_modified))
+           {
+             Py_INCREF (bp_obj);
+             if (evpy_emit_event (bp_obj,
+                                  gdb_py_events.breakpoint_modified) < 0)
+               gdbpy_print_stack ();
+           }
+       }
+    }
+  PyGILState_Release (state);
+}
+
 \f
 
 /* Initialize the Python breakpoint code.  */
@@ -962,6 +1009,7 @@ gdbpy_initialize_breakpoints (void)
 
   observer_attach_breakpoint_created (gdbpy_breakpoint_created);
   observer_attach_breakpoint_deleted (gdbpy_breakpoint_deleted);
+  observer_attach_breakpoint_modified (gdbpy_breakpoint_modified);
 
   /* Add breakpoint types constants.  */
   for (i = 0; pybp_codes[i].name; ++i)
index 9ecee4c3cb2555a44fbe8083c9bf5c5e576cb8bc..1d745581818cccfb7cf713a27b15cc07aa5debd0 100644 (file)
@@ -50,6 +50,9 @@ typedef struct
   eventregistry_object *inferior_call;
   eventregistry_object *memory_changed;
   eventregistry_object *register_changed;
+  eventregistry_object *breakpoint_created;
+  eventregistry_object *breakpoint_deleted;
+  eventregistry_object *breakpoint_modified;
 
   PyObject *module;
 
index 95827e41a973b3c80b67d81895b74d143cdfeca1..961c247af0e3b8f9204d6dc4db64554a031c85c7 100644 (file)
@@ -89,6 +89,17 @@ 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.breakpoint_created,
+                       "breakpoint_created") < 0)
+    return -1;
+
+  if (add_new_registry (&gdb_py_events.breakpoint_deleted,
+                       "breakpoint_deleted") < 0)
+    return -1;
+  if (add_new_registry (&gdb_py_events.breakpoint_modified,
+                       "breakpoint_modified") < 0)
+    return -1;
+
   if (gdb_pymodule_addobject (gdb_module,
                              "events",
                              (PyObject *) gdb_py_events.module) < 0)
index 79a3dd7c39c7a0d254b08d3583f8af260102acbb..5269e0e043438d855de40ec902063d64c00f5823 100644 (file)
@@ -1,3 +1,9 @@
+2016-07-13  Tom Tromey  <tom@tromey.com>
+
+       PR python/15620, PR python/18620:
+       * gdb.python/py-breakpoint.exp (connect_event, check_last_event)
+       (test_bkpt_events): New procs.
+
 2016-07-13  Tom Tromey  <tom@tromey.com>
 
        PR python/17698:
index eb8ec8866921f18be5abc5c937d119f330d4ff5e..d4a1a488ffbf2dfe0afbc12f55e0cd12ebc63978 100644 (file)
@@ -507,6 +507,46 @@ proc test_bkpt_pending {} {
        "Check pending status of pending breakpoint"
 }
 
+# Helper proc to install an event listener for a given breakpoint
+# event.  NAME is the name of the event to listen for.
+proc connect_event {name} {
+    set lambda "lambda x: note_event(\"$name\")"
+    gdb_test_no_output "python gdb.events.$name.connect($lambda)" \
+       "install $name event listener"
+}
+
+# Helper proc to check that the most recently emitted breakpoint event
+# is EXPECTED.
+proc check_last_event {expected} {
+    gdb_test "python print (last_bp_event)" $expected \
+       "check for $expected event"
+}
+
+proc test_bkpt_events {} {
+    global testfile
+
+    clean_restart ${testfile}
+
+    gdb_py_test_multiple "Create event handler" \
+       "python" "" \
+       "def note_event(arg):" "" \
+       "  global last_bp_event" "" \
+       "  last_bp_event = arg" "" \
+       "end" ""
+    gdb_test_no_output "python last_bp_event = None"
+
+    connect_event breakpoint_created
+    connect_event breakpoint_modified
+    connect_event breakpoint_deleted
+
+    gdb_breakpoint [gdb_get_line_number "Break at add."]
+    check_last_event breakpoint_created
+    gdb_test_no_output "disable 1"
+    check_last_event breakpoint_modified
+    gdb_test_no_output "delete 1"
+    check_last_event breakpoint_deleted
+}
+
 test_bkpt_basic
 test_bkpt_deletion
 test_bkpt_cond_and_cmds
@@ -517,3 +557,4 @@ test_bkpt_eval_funcs
 test_bkpt_temporary
 test_bkpt_address
 test_bkpt_pending
+test_bkpt_events