This is the mail archive of the gdb@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC] Python Finish Breakpoints


Hello,

I would like to discuss with you guys a new Python interface for
breakpoint handling. Based on the `finish' command, I prepared a
Python class which allows to catch the return of a given frame.
Basically, the motivation behind this class is to allow Python script
to wrap inferior function calls:

with a code like
int do_something(int *a)
{
   *a += 5;
   sleep(a);
   return 10;
}
which may take a few seconds to execute, there was no way to know the
updated value of `a' and the return value (`gdb.execute("finish")'
could do that, but a Ctrl^C during the `sleep' would have screwed up
your results).


The patch is not supposed to be complete, no CHANGELOG, no
documentation, and certainly more unit tests required, but that will
be ready soon.

the class gdb.FinishBreakpoint extends gdb.Breakpoint

the function FinishBreakpoint.__init__ takes the frame to finish as parameter,

the function FinishBreakpoint.stop is triggered as usual (gdb.Breakpoint)

the function FinishBreakpoint.out_of_scope is triggered when GDB
notices (by observing STOP events) that the breakpointed frame is not
in the callstack anymore and GDB did not hit the breakpoint yet
(namely after a `return' command)

the attribute FinishBreakpoint.out_of_scoped is a flag indicating
whether the breakpointed frame is still the callstack

* by breakpointed frame I mean the frame into which the breakpoint is
set, not the one that we want to finish (because of sibling calls).
Currently, reading the value of `FinishBreakpoint.out_of_scoped'
actually check if the frame exists and update the value. IF it is not
(=True), the callback is not triggered, and won't be, until
`out_of_scoped' is re-enabled (set to False)


the attribute FinishBreakpoint.return_value gives, if GDB is stopped
at a FinishBreakpoint, the gdb.Value object corresponding to the
return value.
there is one problem behind this function, I had to change the code:

+++ b/gdb/infrun.c
@@ -5826,7 +5826,7 @@ normal_stop (void)
   /* Save the function value return registers, if we care.
      We might be about to restore their previous contents.  */
-  if (inferior_thread ()->control.proceed_to_finish)
+  /*if (inferior_thread ()->control.proceed_to_finish)*/
    ...
    stop_registers = regcache_dup (get_current_regcache ());

to correctly set `stop_registers', but I don't really know the
implication of this modification ...

the testsuite gives some examples about how it works


thanks for your comments,

Kevin
--

From 7eed3802c73133dd89280dc43a016ede264544c7 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Mon, 9 May 2011 15:20:48 -0400
Subject: [PATCH] Python Finish Breakpoints

---
 gdb/Makefile.in                            |    6 ++
 gdb/breakpoint.c                           |    2 +-
 gdb/breakpoint.h                           |   10 +++
 gdb/infcmd.c                               |   19 ++++--
 gdb/inferior.h                             |    3 +
 gdb/infrun.c                               |    2 +-
 gdb/python/py-breakpoint.c                 |   91 +++++++++++----------------
 gdb/python/py-frame.c                      |   32 +++++-----
 gdb/python/python-internal.h               |    3 +
 gdb/python/python.c                        |    1 +
 gdb/testsuite/gdb.python/py-breakpoint.c   |   16 +++++-
 gdb/testsuite/gdb.python/py-breakpoint.exp |    7 ++-
 12 files changed, 112 insertions(+), 80 deletions(-)

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 5bab360..3955b38 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -279,6 +279,7 @@ SUBDIR_PYTHON_OBS = \
 	py-block.o \
 	py-bpevent.o \
 	py-breakpoint.o \
+	py-finishbreakpoint.o \
 	py-cmd.o \
 	py-continueevent.o \
 	py-event.o \
@@ -309,6 +310,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-block.c \
 	python/py-bpevent.c \
 	python/py-breakpoint.c \
+	python/py-finishbreakpoint.c \
 	python/py-cmd.c \
 	python/py-continueevent.c \
 	python/py-event.c \
@@ -2038,6 +2040,10 @@ py-breakpoint.o: $(srcdir)/python/py-breakpoint.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-breakpoint.c
 	$(POSTCOMPILE)

+py-finishbreakpoint.o: $(srcdir)/python/py-finishbreakpoint.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-finishbreakpoint.c
+	$(POSTCOMPILE)
+
 py-cmd.o: $(srcdir)/python/py-cmd.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-cmd.c
 	$(POSTCOMPILE)
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index b5fc448..eff5e23 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -7330,7 +7330,7 @@ bp_loc_is_permanent (struct bp_location *loc)
    as textual description of the location, and COND_STRING
    as condition expression.  */

-static void
+void
 create_breakpoint_sal (struct gdbarch *gdbarch,
 		       struct symtabs_and_lines sals, char *addr_string,
 		       char *cond_string,
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 7a9c2d4..a003651 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -986,6 +986,16 @@ extern int create_breakpoint (struct gdbarch
*gdbarch, char *arg,
 			      int enabled,
 			      int internal);

+extern void create_breakpoint_sal (struct gdbarch *gdbarch,
+                                   struct symtabs_and_lines sals,
+                                   char *addr_string,
+                                   char *cond_string,
+                                   enum bptype type, enum bpdisp disposition,
+                                   int thread, int task, int ignore_count,
+                                   struct breakpoint_ops *ops, int from_tty,
+                                   int enabled, int internal,
+                                   int display_canonical);
+
 extern void insert_breakpoints (void);

 extern int remove_breakpoints (void);
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index fce1e8f..c19a04b 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1378,14 +1378,12 @@ advance_command (char *arg, int from_tty)
   until_break_command (arg, from_tty, 1);
 }
 
-/* Print the result of a function at the end of a 'finish' command.  */
+/* Returns the value of the result at the end of a 'finish' command/BP.  */

-static void
-print_return_value (struct type *func_type, struct type *value_type)
+struct value *
+get_return_value (struct type *func_type, struct type *value_type)
 {
   struct gdbarch *gdbarch = get_regcache_arch (stop_registers);
-  struct cleanup *old_chain;
-  struct ui_stream *stb;
   struct value *value;

   CHECK_TYPEDEF (value_type);
@@ -1415,6 +1413,17 @@ print_return_value (struct type *func_type,
struct type *value_type)
       internal_error (__FILE__, __LINE__, _("bad switch"));
     }

+  return value;
+}
+/* Print the result of a function at the end of a 'finish' command.  */
+
+static void
+print_return_value (struct type *func_type, struct type *value_type)
+{
+  struct value *value = get_return_value(func_type, value_type);
+  struct cleanup *old_chain;
+  struct ui_stream *stb;
+
   if (value)
     {
       struct value_print_options opts;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index f8adb6c..b8d5b13 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -285,6 +285,9 @@ extern void detach_command (char *, int);

 extern void notice_new_inferior (ptid_t, int, int);

+extern struct value *get_return_value (struct type *func_type,
+                                       struct type *value_type);
+
 /* Address at which inferior stopped.  */

 extern CORE_ADDR stop_pc;
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 2d6d523..11fd0da 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5826,7 +5826,7 @@ normal_stop (void)

   /* Save the function value return registers, if we care.
      We might be about to restore their previous contents.  */
-  if (inferior_thread ()->control.proceed_to_finish)
+  /*if (inferior_thread ()->control.proceed_to_finish)*/
     {
       /* This should not be necessary.  */
       if (stop_registers)
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 9c33848..2921924 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -17,6 +17,8 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

+
+
 #include "defs.h"
 #include "value.h"
 #include "exceptions.h"
@@ -30,53 +32,18 @@
 #include "ada-lang.h"
 #include "arch-utils.h"
 #include "language.h"
-
-static PyTypeObject breakpoint_object_type;
+#include "py-breakpoint.h"

 /* Number of live breakpoints.  */
 static int bppy_live;

 /* Variables used to pass information between the Breakpoint
    constructor and the breakpoint-created hook function.  */
-static breakpoint_object *bppy_pending_object;
+breakpoint_object *bppy_pending_object;

 /* Function that is called when a Python condition is evaluated.  */
 static char * const stop_func = "stop";

-struct breakpoint_object
-{
-  PyObject_HEAD
-
-  /* The breakpoint number according to gdb.  */
-  int number;
-
-  /* The gdb breakpoint object, or NULL if the breakpoint has been
-     deleted.  */
-  struct breakpoint *bp;
-};
-
-/* Require that BREAKPOINT be a valid breakpoint ID; throw a Python
-   exception if it is invalid.  */
-#define BPPY_REQUIRE_VALID(Breakpoint)					\
-    do {								\
-      if ((Breakpoint)->bp == NULL)					\
-	return PyErr_Format (PyExc_RuntimeError,                        \
-			     _("Breakpoint %d is invalid."),		\
-			     (Breakpoint)->number);			\
-    } while (0)
-
-/* Require that BREAKPOINT be a valid breakpoint ID; throw a Python
-   exception if it is invalid.  This macro is for use in setter functions.  */
-#define BPPY_SET_REQUIRE_VALID(Breakpoint)				\
-    do {								\
-      if ((Breakpoint)->bp == NULL)					\
-        {								\
-	  PyErr_Format (PyExc_RuntimeError, _("Breakpoint %d is invalid."), \
-			(Breakpoint)->number);				\
-	  return -1;							\
-	}								\
-    } while (0)
-
 /* This is used to initialize various gdb.bp_* constants.  */
 struct pybp_code
 {
@@ -107,6 +74,14 @@ static struct pybp_code pybp_watch_types[] =
   {NULL} /* Sentinel.  */
 };

+/* Disallocate a gdb.Breakpoint object*/
+
+void
+bppy_dealloc (PyObject *self)
+{
+  Py_XDECREF (((breakpoint_object *) self)->dict);
+}
+
 /* Python function which checks the validity of a breakpoint object.  */
 static PyObject *
 bppy_is_valid (PyObject *self, PyObject *args)
@@ -806,21 +781,29 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
     }
   else
     newbp = PyObject_New (breakpoint_object, &breakpoint_object_type);
-  if (newbp)
-    {
-      newbp->number = bp->number;
-      newbp->bp = bp;
-      newbp->bp->py_bp_object = newbp;
-      Py_INCREF (newbp);
-      ++bppy_live;
-    }
-  else
-    {
-      PyErr_SetString (PyExc_RuntimeError,
-		       _("Error while creating breakpoint from GDB."));
-      gdbpy_print_stack ();
-    }
+
+  if (!newbp)
+    goto fail;
+
+  newbp->dict = PyDict_New ();
+  if (!newbp->dict)
+    goto fail;
+
+  newbp->number = bp->number;
+  newbp->bp = bp;
+  newbp->bp->py_bp_object = newbp;
+
+  Py_INCREF (newbp);
+  ++bppy_live;
+
+  goto success;
+
+fail:
+  PyErr_SetString (PyExc_RuntimeError,
+                   _("Error while creating breakpoint from GDB."));
+  gdbpy_print_stack ();

+success:
   PyGILState_Release (state);
 }

@@ -971,14 +954,14 @@ static PyMethodDef breakpoint_object_methods[] =
   { NULL } /* Sentinel.  */
 };

-static PyTypeObject breakpoint_object_type =
+PyTypeObject breakpoint_object_type =
 {
   PyObject_HEAD_INIT (NULL)
   0,				  /*ob_size*/
   "gdb.Breakpoint",		  /*tp_name*/
   sizeof (breakpoint_object),	  /*tp_basicsize*/
   0,				  /*tp_itemsize*/
-  0,				  /*tp_dealloc*/
+  bppy_dealloc,                   /*tp_dealloc*/
   0,				  /*tp_print*/
   0,				  /*tp_getattr*/
   0,				  /*tp_setattr*/
@@ -1008,7 +991,7 @@ static PyTypeObject breakpoint_object_type =
   0,				  /* tp_dict */
   0,				  /* tp_descr_get */
   0,				  /* tp_descr_set */
-  0,				  /* tp_dictoffset */
+  offsetof (breakpoint_object, dict), /* tp_dictoffset */
   bppy_init,			  /* tp_init */
   0,				  /* tp_alloc */
   PyType_GenericNew		  /* tp_new */
diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c
index d7128a9..2109c73 100644
--- a/gdb/python/py-frame.c
+++ b/gdb/python/py-frame.c
@@ -60,9 +60,10 @@ static PyTypeObject frame_object_type;
    object.  If the frame doesn't exist anymore (the frame id doesn't
    correspond to any frame in the inferior), returns NULL.  */

-static struct frame_info *
-frame_object_to_frame_info (frame_object *frame_obj)
+struct frame_info *
+frame_object_to_frame_info (PyObject *obj)
 {
+  frame_object *frame_obj = (frame_object *) obj;
   struct frame_info *frame;

   frame = frame_find_by_id (frame_obj->frame_id);
@@ -103,7 +104,7 @@ frapy_is_valid (PyObject *self, PyObject *args)
 {
   struct frame_info *frame;

-  frame = frame_object_to_frame_info ((frame_object *) self);
+  frame = frame_object_to_frame_info (self);
   if (frame == NULL)
     Py_RETURN_FALSE;

@@ -124,7 +125,7 @@ frapy_name (PyObject *self, PyObject *args)

   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+      FRAPY_REQUIRE_VALID (self, frame);

       find_frame_funname (frame, &name, &lang, NULL);
     }
@@ -153,7 +154,7 @@ frapy_type (PyObject *self, PyObject *args)

   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+      FRAPY_REQUIRE_VALID (self, frame);

       type = get_frame_type (frame);
     }
@@ -174,7 +175,7 @@ frapy_unwind_stop_reason (PyObject *self, PyObject *args)

   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+      FRAPY_REQUIRE_VALID (self, frame);
     }
   GDB_PY_HANDLE_EXCEPTION (except);

@@ -195,7 +196,7 @@ frapy_pc (PyObject *self, PyObject *args)

   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+      FRAPY_REQUIRE_VALID (self, frame);

       pc = get_frame_pc (frame);
     }
@@ -216,7 +217,7 @@ frapy_block (PyObject *self, PyObject *args)

   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+      FRAPY_REQUIRE_VALID (self, frame);
       block = get_frame_block (frame, NULL);
     }
   GDB_PY_HANDLE_EXCEPTION (except);
@@ -257,7 +258,7 @@ frapy_function (PyObject *self, PyObject *args)

   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+      FRAPY_REQUIRE_VALID (self, frame);

       sym = find_pc_function (get_frame_address_in_block (frame));
     }
@@ -319,7 +320,7 @@ frapy_older (PyObject *self, PyObject *args)

   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+      FRAPY_REQUIRE_VALID (self, frame);

       prev = get_prev_frame (frame);
       if (prev)
@@ -348,7 +349,7 @@ frapy_newer (PyObject *self, PyObject *args)

   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+      FRAPY_REQUIRE_VALID (self, frame);

       next = get_next_frame (frame);
       if (next)
@@ -377,7 +378,7 @@ frapy_find_sal (PyObject *self, PyObject *args)

   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+      FRAPY_REQUIRE_VALID (self, frame);

       find_frame_sal (frame, &sal);
       sal_obj = symtab_and_line_to_sal_object (sal);
@@ -433,7 +434,7 @@ frapy_read_var (PyObject *self, PyObject *args)

       TRY_CATCH (except, RETURN_MASK_ALL)
 	{
-	  FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+	  FRAPY_REQUIRE_VALID (self, frame);

 	  if (!block)
 	    block = get_frame_block (frame, NULL);
@@ -461,7 +462,7 @@ frapy_read_var (PyObject *self, PyObject *args)

   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
+      FRAPY_REQUIRE_VALID (self, frame);

       val = read_var_value (var, frame);
     }
@@ -484,12 +485,11 @@ static PyObject *
 frapy_select (PyObject *self, PyObject *args)
 {
   struct frame_info *fi;
-  frame_object *frame = (frame_object *) self;
   volatile struct gdb_exception except;

   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      FRAPY_REQUIRE_VALID (frame, fi);
+      FRAPY_REQUIRE_VALID (self, fi);

       select_frame (fi);
     }
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index d3cb788..9ec1981 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -114,6 +114,7 @@ extern PyTypeObject symbol_object_type;
 extern PyTypeObject event_object_type;
 extern PyTypeObject events_object_type;
 extern PyTypeObject stop_event_object_type;
+extern PyTypeObject breakpoint_object_type;

 /* Defined in py-breakpoint.c */
 typedef struct breakpoint_object breakpoint_object;
@@ -161,6 +162,7 @@ PyObject *block_to_block_object (struct block
*block, struct objfile *objfile);
 PyObject *value_to_value_object (struct value *v);
 PyObject *type_to_type_object (struct type *);
 PyObject *frame_info_to_frame_object (struct frame_info *frame);
+struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);

 PyObject *pspace_to_pspace_object (struct program_space *);
 PyObject *pspy_get_printers (PyObject *, void *);
@@ -194,6 +196,7 @@ void gdbpy_initialize_functions (void);
 void gdbpy_initialize_pspace (void);
 void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_breakpoints (void);
+void gdbpy_initialize_finishbreakpoints (void);
 void gdbpy_initialize_lazy_string (void);
 void gdbpy_initialize_parameters (void);
 void gdbpy_initialize_thread (void);
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 8a7bc66..d620382 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1060,6 +1060,7 @@ Enables or disables printing of Python stack traces."),
   gdbpy_initialize_pspace ();
   gdbpy_initialize_objfile ();
   gdbpy_initialize_breakpoints ();
+  gdbpy_initialize_finishbreakpoints();
   gdbpy_initialize_lazy_string ();
   gdbpy_initialize_thread ();
   gdbpy_initialize_inferior ();
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.c
b/gdb/testsuite/gdb.python/py-breakpoint.c
index 9a96681..f32ff78 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.c
+++ b/gdb/testsuite/gdb.python/py-breakpoint.c
@@ -29,18 +29,32 @@ int add (int i)
   return i + i;
 }

+int increase_1(int *a)
+{
+  *a += 1;
+  return -5;
+}
+
+void increase(int *a)
+{
+  increase_1(a);
+}

 int main (int argc, char *argv[])
 {
   int foo = 5;
   int bar = 42;
   int i;
+  i = 0 ;
+  /* Break at increase. */
+  increase (&i) ;
+  increase (&i) ;

   for (i = 0; i < 10; i++)
     {
       result += multiply (foo);  /* Break at multiply. */
       result += add (bar); /* Break at add. */
     }
-
+
   return 0; /* Break at end. */
 }
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp
b/gdb/testsuite/gdb.python/py-breakpoint.exp
index f0a83f1..8755888 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -44,7 +44,8 @@ gdb_py_test_silent_cmd "python blist =
gdb.breakpoints()" "Get Breakpoint List"
 gdb_test "python print blist\[0\]" "<gdb.Breakpoint object at $hex>"
"Check obj exists"
 gdb_test "python print blist\[0\].location" "main." "Check breakpoint location"

-gdb_breakpoint [gdb_get_line_number "Break at multiply."]
+set mult_line [gdb_get_line_number "Break at multiply."]
+gdb_breakpoint ${mult_line}
 gdb_continue_to_breakpoint "Break at multiply."

 # Check that the Python breakpoint code noted the addition of a
@@ -54,7 +55,9 @@ gdb_test "python print len(blist)" "2" "Check for
two breakpoints"
 gdb_test "python print blist\[0\]" "<gdb.Breakpoint object at $hex>"
"Check obj exists"
 gdb_test "python print blist\[0\].location" "main." "Check breakpoint location"
 gdb_test "python print blist\[1\]" "<gdb.Breakpoint object at $hex>"
"Check obj exists"
-gdb_test "python print blist\[1\].location" "py-breakpoint\.c:41*"
"Check breakpoint location"
+
+gdb_test "python print blist\[1\].location" "py-breakpoint\.c:${mult_line}*" \
+         "Check breakpoint location"

 # Check hit and ignore counts.
 gdb_test "python print blist\[1\].hit_count" "1" "Check breakpoint hit count"
-- 
1.7.4.4


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]