Mirror of the gdb mailing list
 help / color / mirror / Atom feed
* [RFC] Python Finish Breakpoints
@ 2011-05-09 14:11 Kevin Pouget
  2011-05-09 14:31 ` Kevin Pouget
  2011-05-12 19:00 ` Doug Evans
  0 siblings, 2 replies; 13+ messages in thread
From: Kevin Pouget @ 2011-05-09 14:11 UTC (permalink / raw)
  To: gdb

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);
 }
 \f
-/* 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


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC] Python Finish Breakpoints
  2011-05-09 14:11 [RFC] Python Finish Breakpoints Kevin Pouget
@ 2011-05-09 14:31 ` Kevin Pouget
       [not found]   ` <BANLkTikVdqbMqjguTV8ct0TWiBDhHGYtLg@mail.gmail.com>
  2011-05-12 19:00 ` Doug Evans
  1 sibling, 1 reply; 13+ messages in thread
From: Kevin Pouget @ 2011-05-09 14:31 UTC (permalink / raw)
  To: gdb

On Mon, May 9, 2011 at 10:10 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> 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


here is a version of the patch which better reflects what I described,
some parts were missing in the former post.


Kevin

--

From 94c7e70354b4eef80352c6306365f37339bc1a5e 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                        |   79 ++---
 gdb/python/py-breakpoint.h                        |   61 ++++
 gdb/python/py-finishbreakpoint.c                  |  384 +++++++++++++++++++++
 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 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp |   63 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py  |   21 ++
 16 files changed, 629 insertions(+), 80 deletions(-)
 create mode 100644 gdb/python/py-breakpoint.h
 create mode 100644 gdb/python/py-finishbreakpoint.c
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint.py

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);
 }
 \f
-/* 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..db2c411 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
 {
@@ -806,21 +773,25 @@ 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->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 +942,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*/
+  0,                              /*tp_dealloc*/
   0,				  /*tp_print*/
   0,				  /*tp_getattr*/
   0,				  /*tp_setattr*/
@@ -1008,7 +979,7 @@ static PyTypeObject breakpoint_object_type =
   0,				  /* tp_dict */
   0,				  /* tp_descr_get */
   0,				  /* tp_descr_set */
-  0,				  /* tp_dictoffset */
+  0,                              /* tp_dictoffset */
   bppy_init,			  /* tp_init */
   0,				  /* tp_alloc */
   PyType_GenericNew		  /* tp_new */
diff --git a/gdb/python/py-breakpoint.h b/gdb/python/py-breakpoint.h
new file mode 100644
index 0000000..827f488
--- /dev/null
+++ b/gdb/python/py-breakpoint.h
@@ -0,0 +1,61 @@
+/* Python interface to breakpoints
+
+   Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GDB_PY_BREAKPOINT_H
+#define GDB_PY_BREAKPOINT_H
+
+/* 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)
+
+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;
+};
+
+/* Variables used to pass information between the Breakpoint
+   constructor and the breakpoint-created hook function.  */
+extern breakpoint_object *bppy_pending_object;
+
+#endif /* GDB_PY_BREAKPOINT_H */
diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
new file mode 100644
index 0000000..bccb69f
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,384 @@
+/* Python interface to finish breakpoints
+
+   Copyright (C) 2011 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   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 "exceptions.h"
+#include "python-internal.h"
+#include "breakpoint.h"
+#include "py-breakpoint.h"
+#include "frame.h"
+#include "gdbthread.h"
+#include "arch-utils.h"
+#include "language.h"
+#include "observer.h"
+#include "inferior.h"
+
+static PyTypeObject finish_breakpoint_object_type;
+
+/* Function that is called when a Python finish bp is found out of scope.  */
+static char * const outofscope_func = "out_of_scope";
+
+/* struct implementing the gdb.FinishBreakpoint object by extending
+   the gdb.Breakpoint class.  */
+struct finish_breakpoint_object
+{
+  /* gdb.Breakpoint base class.  */
+  struct breakpoint_object py_bp;
+  /* Flag indicating that the BP is out of the callstack and Python callback
+     has been triggered.  */
+  int out_of_scoped;
+  /* The function finished by this breakpoint.  */
+  struct symbol *function;
+};
+
+/* Python function to set the 'out_of_scoped' attribute of
+   FinishBreakpoint.  */
+
+static int
+bpfinishpy_set_outofscoped (PyObject *self, PyObject *newvalue, void *closure)
+{
+  struct finish_breakpoint_object *self_finishbp =
+      (struct finish_breakpoint_object *) self;
+  int cmp;
+
+  BPPY_SET_REQUIRE_VALID (&self_finishbp->py_bp);
+
+  if (newvalue == NULL)
+    {
+      PyErr_SetString (PyExc_TypeError,
+                       _("Cannot delete `out_of_scoped' attribute."));
+      return -1;
+    }
+  else if (! PyBool_Check (newvalue))
+    {
+      PyErr_SetString (PyExc_TypeError,
+                       _("The value of `out_of_scoped' must be a boolean."));
+      return -1;
+    }
+
+  cmp = PyObject_IsTrue (newvalue);
+  if (cmp < 0)
+    return -1;
+  else
+    self_finishbp->out_of_scoped = cmp;
+
+  return 0;
+}
+
+/* Python function to update and get the 'out_of_scoped' attribute
+   of FinishBreakpoint.  */
+
+static PyObject *
+bpfinishpy_get_outofscoped (PyObject *self, void *closure)
+{
+  struct finish_breakpoint_object *self_finishbp =
+      (struct finish_breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
+
+  if (!self_finishbp->out_of_scoped)
+    self_finishbp->out_of_scoped =
+        frame_find_by_id(self_finishbp->py_bp.bp->frame_id) == NULL ;
+
+  if (self_finishbp->out_of_scoped)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+/* Python function to get the 'return_value' attribute of
+   FinishBreakpoint.  */
+
+static PyObject *
+bpfinishpy_get_returnvalue (PyObject *self, void *closure)
+{
+  struct finish_breakpoint_object *self_finishbp =
+      (struct finish_breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
+
+  if (self_finishbp->function == NULL)
+    goto return_none;
+
+  /* Ensure that GDB is stopped at this FinishBreakpoint.  */
+  if (inferior_thread ()->control.stop_bpstat != NULL)
+    {
+      bpstat bs;
+
+      for(bs = inferior_thread ()->control.stop_bpstat;
+          bs; bs = bs->next)
+        {
+          struct breakpoint *bp = bs->breakpoint_at;
+          if (bp != NULL
+              && (PyObject *) bp->py_bp_object == self)
+            {
+              struct type *v_type;
+
+              v_type = TYPE_TARGET_TYPE (SYMBOL_TYPE
+                  (self_finishbp->function));
+              if (!v_type)
+                internal_error (__FILE__, __LINE__,
+                 _("bpfinishpy_get_returnvalue: function has no target type"));
+
+              if (TYPE_CODE (v_type) != TYPE_CODE_VOID)
+                {
+                  struct value *ret = get_return_value
+                      (SYMBOL_TYPE (self_finishbp->function), v_type);
+                  PyObject *return_value = value_to_value_object (ret);
+
+                  Py_INCREF (return_value);
+                  return return_value;
+                }
+              else
+                  goto return_none;
+            }
+        }
+    }
+
+return_none:
+  Py_INCREF (Py_None);
+  return Py_None ;
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack. If BP_OBJ has the attribute OUT_OF_SCOPED and
+   its value is FALSE, trigger the method OUT_OF_SCOPE and set the flag to
+   TRUE.  */
+
+void
+gdbpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
+{
+  breakpoint_object *bp_obj = (breakpoint_object *) bpfinish_obj;
+  PyObject *py_obj = (PyObject *) bp_obj ;
+
+  if (PyObject_HasAttrString (py_obj, outofscope_func))
+    {
+      struct gdbarch *garch =  bp_obj->bp->gdbarch ?
+          bp_obj->bp->gdbarch : get_current_arch ();
+      struct cleanup *cleanup = ensure_python_env (garch, current_language);
+
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+
+      do_cleanups (cleanup);
+    }
+  bpfinish_obj->out_of_scoped = 1;
+}
+
+/* Python function to create a new breakpoint.  */
+
+static int
+bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+  static char *keywords[] = { "frame", "internal", NULL };
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  struct finish_breakpoint_object *self_bpfinish =
+      (struct finish_breakpoint_object *) self;
+  int type = bp_breakpoint;
+  PyObject *frame_obj = NULL;
+  struct frame_info *frame, *prev_frame;
+  struct frame_id frame_id;
+  struct symtabs_and_lines sals;
+  struct symtab_and_line sal;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR pc ;
+  volatile struct gdb_exception except;
+
+  if (! PyArg_ParseTupleAndKeywords (args, kwargs, "O|O", keywords,
+                                     &frame_obj, &internal))
+    return -1;
+
+  if (!frame_obj)
+    goto invalid_frame;
+
+  frame = frame_object_to_frame_info (frame_obj);
+  if (frame == NULL)
+    goto invalid_frame;
+
+  prev_frame = get_prev_frame(frame);
+  if (prev_frame == 0)
+    {
+      PyErr_SetString (PyExc_ValueError,
+           _("\"FinishBreakpoint\" not meaningful in the outermost frame."));
+      return -1;
+    }
+
+  frame_id = get_frame_id (prev_frame);
+  if (frame_id_eq(frame_id, null_frame_id))
+    goto invalid_frame;
+
+  pc = get_frame_pc (prev_frame);
+
+  sal = find_pc_line (pc, 0);
+  sal.pc = pc;
+  sals.sals = &sal;
+  sals.nelts = 1;
+
+  /* Find the function we will return from.  */
+  self_bpfinish->function = find_pc_function (get_frame_pc (frame));
+
+  if (internal)
+    {
+      internal_bp = PyObject_IsTrue (internal);
+      if (internal_bp == -1)
+        {
+          PyErr_SetString (PyExc_ValueError,
+                           _("The value of `internal' must be a boolean."));
+          return -1;
+        }
+    }
+
+  bppy_pending_object = self_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      create_breakpoint_sal (python_gdbarch, sals, NULL, NULL,
+                             bp_breakpoint, disp_donttouch, -1,
+                             0, 0, NULL, 0, 1, internal_bp, 0) ;
+    }
+  if (except.reason < 0)
+    {
+      PyErr_Format (except.reason == RETURN_QUIT
+                    ? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
+                    "%s", except.message);
+      return -1;
+    }
+  BPPY_SET_REQUIRE_VALID (self_bp);
+
+  self_bp->bp->frame_id = frame_id;
+
+  self_bpfinish->out_of_scoped = 0;
+
+  return 0;
+
+invalid_frame:
+  PyErr_SetString (PyExc_ValueError,
+                   _("Invalid ID for the `frame' object."));
+  return -1;
+}
+
+/* Callback for `bpfinishpy_detect_out_scope'*/
+
+static int
+bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
+{
+  struct breakpoint *bp_stopped = (struct breakpoint *) args;
+  PyObject *py_bp = (PyObject *) b->py_bp_object;
+  PyGILState_STATE state;
+
+  /* Prevent python SEGFAULT because of missing thread state.  */
+  state = PyGILState_Ensure();
+
+  /* Trigger out_of_scope notification if this is a FinishBreakpoint
+     and GDB is stopped at this bp or its frame is not in the current
+     callstack and the notification has not been sent yet.  */
+  if (py_bp != NULL
+      && PyObject_TypeCheck (py_bp, &finish_breakpoint_object_type)
+      && (b == bp_stopped || frame_find_by_id(b->frame_id) == NULL)
+      && !((struct finish_breakpoint_object *) py_bp)->out_of_scoped)
+    {
+      gdbpy_out_of_scope ((struct finish_breakpoint_object *) py_bp);
+    }
+
+  PyGILState_Release (state);
+
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run out
+   out of the scope of any FinishBreakpoint before it has been hit.  */
+
+static void
+bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
+{
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
+                            bs == NULL ? NULL : bs->breakpoint_at);
+}
+
+/* Initialize the Python finish breakpoint code.  */
+
+void
+gdbpy_initialize_finishbreakpoints (void)
+{
+  if (PyType_Ready (&finish_breakpoint_object_type) < 0)
+      return;
+
+  Py_INCREF (&finish_breakpoint_object_type);
+  PyModule_AddObject (gdb_module, "FinishBreakpoint",
+                      (PyObject *) &finish_breakpoint_object_type);
+
+  observer_attach_normal_stop (bpfinishpy_handle_stop);
+}
+
+static PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "out_of_scoped", bpfinishpy_get_outofscoped, bpfinishpy_set_outofscoped,
+    "Boolean telling whether the breakpoint is still within the scope \
+of the current callstack.", NULL },
+  { "return_value", bpfinishpy_get_returnvalue, NULL,
+  "gdb.Value object representing the return value, if any. \
+None otherwise.", NULL },
+    { NULL }  /* Sentinel.  */
+};
+
+static PyTypeObject finish_breakpoint_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,                              /*ob_size*/
+  "gdb.FinishBreakpoint",         /*tp_name*/
+  sizeof (struct finish_breakpoint_object), /*tp_basicsize*/
+  0,                              /*tp_itemsize*/
+  0,                              /*tp_dealloc*/
+  0,                              /*tp_print*/
+  0,                              /*tp_getattr*/
+  0,                              /*tp_setattr*/
+  0,                              /*tp_compare*/
+  0,                              /*tp_repr*/
+  0,                              /*tp_as_number*/
+  0,                              /*tp_as_sequence*/
+  0,                              /*tp_as_mapping*/
+  0,                              /*tp_hash */
+  0,                              /*tp_call*/
+  0,                              /*tp_str*/
+  0,                              /*tp_getattro*/
+  0,                              /*tp_setattro */
+  0,                              /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /*tp_flags*/
+  "GDB finish breakpoint object", /* tp_doc */
+  0,                              /* tp_traverse */
+  0,                              /* tp_clear */
+  0,                              /* tp_richcompare */
+  0,                              /* tp_weaklistoffset */
+  0,                              /* tp_iter */
+  0,                              /* tp_iternext */
+  0,                              /* tp_methods */
+  0,                              /* tp_members */
+  finish_breakpoint_object_getset,/* tp_getset */
+  &breakpoint_object_type,        /* tp_base */
+  0,                              /* tp_dict */
+  0,                              /* tp_descr_get */
+  0,                              /* tp_descr_set */
+  0,                              /* tp_dictoffset */
+  bpfinishpy_init,                /* tp_init */
+  0,                              /* tp_alloc */
+  0                               /* 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"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
new file mode 100644
index 0000000..febef50
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,63 @@
+# Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests the mechanism
+# exposing values to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+load_lib gdb-python.exp
+
+set testfile "py-breakpoint"
+set srcfile ${testfile}.c
+set remote_python_file [remote_download host
${srcdir}/${subdir}/py-finish-breakpoint.py]
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+gdb_test "source $remote_python_file" ".*Python script imported.*"
"import python scripts"
+
+gdb_test "python MyBreakpoint(\"increase_1\")" ".*Breakpoint 2.*"
"create Python function breakpoint"
+
+gdb_test "continue" ".*Arrived at MyBreakpoint with 0.*" "check
MyBreakpoint hit"
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval
(\"a\"), gdb.selected_frame ())" ".*Breakpoint 3.*" "set
FinishBreakpoint"
+gdb_test "python print finishbp.out_of_scoped" ".*False.*" "check
out_of_scoped init"
+gdb_test "python print finishbp.return_value" ".*None.*" "check return_value"
+
+gdb_test "continue" ".*MyFinishBreakpoint stop with
1.*#0.*increase.*" "check MyFinishBreakpoint hit"
+gdb_test "python print finishbp.return_value" ".*-5.*" "check return_value"
+gdb_test "python print finishbp.out_of_scoped" ".*False.*" "check
out_of_scoped after hit"
+gdb_test "finish" ".*Run til.*" "return to main()"
+gdb_test "python print finishbp.return_value" ".*None.*" "check return_value"
+
+gdb_test "continue" ".*Arrived at MyBreakpoint with 1.*" "check
MyBreakpoint second hit"
+gdb_test "up" ".*increase_1.*" "go one frame up"
+gdb_test_no_output "python finishbp.out_of_scoped = False" "re-enable
out_of_scoped"
+gdb_test_no_output "set confirm off" "disable confirmation"
+gdb_test_no_output "return" "return from the frame"
+
+gdb_test "next" ".*MyFinishBreakpoint out of scope.*" "check Finish
breakpoint discard"
+gdb_test "python print finishbp.out_of_scoped" ".*True.*" "check out_of_scoped"
\ No newline at end of file
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.py
b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
new file mode 100644
index 0000000..ea82fe9
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,21 @@
+class MyBreakpoint(gdb.Breakpoint):		
+	def stop(self):
+		val = gdb.parse_and_eval ("a")
+		print "Arrived at MyBreakpoint with %d" % int(val.dereference())
+		return True
+		
+class MyFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		super (MyFinishBreakpoint, self).__init__ (frame)
+		print "MyFinishBreakpoint init"
+		self.val = val
+		
+	def stop(self):
+		print "MyFinishBreakpoint stop with %d" % int(self.val.dereference())
+		gdb.execute("where 1")
+		return True
+	
+	def out_of_scope(self):
+		print "MyFinishBreakpoint out of scope..."
+		
+print "Python script imported"
\ No newline at end of file
-- 
1.7.4.4


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC] Python Finish Breakpoints
       [not found]   ` <BANLkTikVdqbMqjguTV8ct0TWiBDhHGYtLg@mail.gmail.com>
@ 2011-05-11  7:44     ` Kevin Pouget
  2011-05-11 10:31       ` Phil Muldoon
  0 siblings, 1 reply; 13+ messages in thread
From: Kevin Pouget @ 2011-05-11  7:44 UTC (permalink / raw)
  To: gdb, gdb-patches

Any feedback ... ?


On Mon, May 9, 2011 at 10:10 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> 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


here is a version of the patch which better reflects what I described,
some parts were missing in the former post.


Kevin

--

From 94c7e70354b4eef80352c6306365f37339bc1a5e 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                        |   79 ++---
 gdb/python/py-breakpoint.h                        |   61 ++++
 gdb/python/py-finishbreakpoint.c                  |  384 +++++++++++++++++++++
 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 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp |   63 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py  |   21 ++
 16 files changed, 629 insertions(+), 80 deletions(-)
 create mode 100644 gdb/python/py-breakpoint.h
 create mode 100644 gdb/python/py-finishbreakpoint.c
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint.py

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..db2c411 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
 {
@@ -806,21 +773,25 @@ 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->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 +942,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*/
+  0,                              /*tp_dealloc*/
  0,                             /*tp_print*/
  0,                             /*tp_getattr*/
  0,                             /*tp_setattr*/
@@ -1008,7 +979,7 @@ static PyTypeObject breakpoint_object_type =
  0,                             /* tp_dict */
  0,                             /* tp_descr_get */
  0,                             /* tp_descr_set */
-  0,                             /* tp_dictoffset */
+  0,                              /* tp_dictoffset */
  bppy_init,                     /* tp_init */
  0,                             /* tp_alloc */
  PyType_GenericNew              /* tp_new */
diff --git a/gdb/python/py-breakpoint.h b/gdb/python/py-breakpoint.h
new file mode 100644
index 0000000..827f488
--- /dev/null
+++ b/gdb/python/py-breakpoint.h
@@ -0,0 +1,61 @@
+/* Python interface to breakpoints
+
+   Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GDB_PY_BREAKPOINT_H
+#define GDB_PY_BREAKPOINT_H
+
+/* 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)
+
+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;
+};
+
+/* Variables used to pass information between the Breakpoint
+   constructor and the breakpoint-created hook function.  */
+extern breakpoint_object *bppy_pending_object;
+
+#endif /* GDB_PY_BREAKPOINT_H */
diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
new file mode 100644
index 0000000..bccb69f
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,384 @@
+/* Python interface to finish breakpoints
+
+   Copyright (C) 2011 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   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 "exceptions.h"
+#include "python-internal.h"
+#include "breakpoint.h"
+#include "py-breakpoint.h"
+#include "frame.h"
+#include "gdbthread.h"
+#include "arch-utils.h"
+#include "language.h"
+#include "observer.h"
+#include "inferior.h"
+
+static PyTypeObject finish_breakpoint_object_type;
+
+/* Function that is called when a Python finish bp is found out of scope.  */
+static char * const outofscope_func = "out_of_scope";
+
+/* struct implementing the gdb.FinishBreakpoint object by extending
+   the gdb.Breakpoint class.  */
+struct finish_breakpoint_object
+{
+  /* gdb.Breakpoint base class.  */
+  struct breakpoint_object py_bp;
+  /* Flag indicating that the BP is out of the callstack and Python callback
+     has been triggered.  */
+  int out_of_scoped;
+  /* The function finished by this breakpoint.  */
+  struct symbol *function;
+};
+
+/* Python function to set the 'out_of_scoped' attribute of
+   FinishBreakpoint.  */
+
+static int
+bpfinishpy_set_outofscoped (PyObject *self, PyObject *newvalue, void *closure)
+{
+  struct finish_breakpoint_object *self_finishbp =
+      (struct finish_breakpoint_object *) self;
+  int cmp;
+
+  BPPY_SET_REQUIRE_VALID (&self_finishbp->py_bp);
+
+  if (newvalue == NULL)
+    {
+      PyErr_SetString (PyExc_TypeError,
+                       _("Cannot delete `out_of_scoped' attribute."));
+      return -1;
+    }
+  else if (! PyBool_Check (newvalue))
+    {
+      PyErr_SetString (PyExc_TypeError,
+                       _("The value of `out_of_scoped' must be a boolean."));
+      return -1;
+    }
+
+  cmp = PyObject_IsTrue (newvalue);
+  if (cmp < 0)
+    return -1;
+  else
+    self_finishbp->out_of_scoped = cmp;
+
+  return 0;
+}
+
+/* Python function to update and get the 'out_of_scoped' attribute
+   of FinishBreakpoint.  */
+
+static PyObject *
+bpfinishpy_get_outofscoped (PyObject *self, void *closure)
+{
+  struct finish_breakpoint_object *self_finishbp =
+      (struct finish_breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
+
+  if (!self_finishbp->out_of_scoped)
+    self_finishbp->out_of_scoped =
+        frame_find_by_id(self_finishbp->py_bp.bp->frame_id) == NULL ;
+
+  if (self_finishbp->out_of_scoped)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+/* Python function to get the 'return_value' attribute of
+   FinishBreakpoint.  */
+
+static PyObject *
+bpfinishpy_get_returnvalue (PyObject *self, void *closure)
+{
+  struct finish_breakpoint_object *self_finishbp =
+      (struct finish_breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
+
+  if (self_finishbp->function == NULL)
+    goto return_none;
+
+  /* Ensure that GDB is stopped at this FinishBreakpoint.  */
+  if (inferior_thread ()->control.stop_bpstat != NULL)
+    {
+      bpstat bs;
+
+      for(bs = inferior_thread ()->control.stop_bpstat;
+          bs; bs = bs->next)
+        {
+          struct breakpoint *bp = bs->breakpoint_at;
+          if (bp != NULL
+              && (PyObject *) bp->py_bp_object == self)
+            {
+              struct type *v_type;
+
+              v_type = TYPE_TARGET_TYPE (SYMBOL_TYPE
+                  (self_finishbp->function));
+              if (!v_type)
+                internal_error (__FILE__, __LINE__,
+                 _("bpfinishpy_get_returnvalue: function has no target type"));
+
+              if (TYPE_CODE (v_type) != TYPE_CODE_VOID)
+                {
+                  struct value *ret = get_return_value
+                      (SYMBOL_TYPE (self_finishbp->function), v_type);
+                  PyObject *return_value = value_to_value_object (ret);
+
+                  Py_INCREF (return_value);
+                  return return_value;
+                }
+              else
+                  goto return_none;
+            }
+        }
+    }
+
+return_none:
+  Py_INCREF (Py_None);
+  return Py_None ;
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack. If BP_OBJ has the attribute OUT_OF_SCOPED and
+   its value is FALSE, trigger the method OUT_OF_SCOPE and set the flag to
+   TRUE.  */
+
+void
+gdbpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
+{
+  breakpoint_object *bp_obj = (breakpoint_object *) bpfinish_obj;
+  PyObject *py_obj = (PyObject *) bp_obj ;
+
+  if (PyObject_HasAttrString (py_obj, outofscope_func))
+    {
+      struct gdbarch *garch =  bp_obj->bp->gdbarch ?
+          bp_obj->bp->gdbarch : get_current_arch ();
+      struct cleanup *cleanup = ensure_python_env (garch, current_language);
+
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+
+      do_cleanups (cleanup);
+    }
+  bpfinish_obj->out_of_scoped = 1;
+}
+
+/* Python function to create a new breakpoint.  */
+
+static int
+bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+  static char *keywords[] = { "frame", "internal", NULL };
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  struct finish_breakpoint_object *self_bpfinish =
+      (struct finish_breakpoint_object *) self;
+  int type = bp_breakpoint;
+  PyObject *frame_obj = NULL;
+  struct frame_info *frame, *prev_frame;
+  struct frame_id frame_id;
+  struct symtabs_and_lines sals;
+  struct symtab_and_line sal;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR pc ;
+  volatile struct gdb_exception except;
+
+  if (! PyArg_ParseTupleAndKeywords (args, kwargs, "O|O", keywords,
+                                     &frame_obj, &internal))
+    return -1;
+
+  if (!frame_obj)
+    goto invalid_frame;
+
+  frame = frame_object_to_frame_info (frame_obj);
+  if (frame == NULL)
+    goto invalid_frame;
+
+  prev_frame = get_prev_frame(frame);
+  if (prev_frame == 0)
+    {
+      PyErr_SetString (PyExc_ValueError,
+           _("\"FinishBreakpoint\" not meaningful in the outermost frame."));
+      return -1;
+    }
+
+  frame_id = get_frame_id (prev_frame);
+  if (frame_id_eq(frame_id, null_frame_id))
+    goto invalid_frame;
+
+  pc = get_frame_pc (prev_frame);
+
+  sal = find_pc_line (pc, 0);
+  sal.pc = pc;
+  sals.sals = &sal;
+  sals.nelts = 1;
+
+  /* Find the function we will return from.  */
+  self_bpfinish->function = find_pc_function (get_frame_pc (frame));
+
+  if (internal)
+    {
+      internal_bp = PyObject_IsTrue (internal);
+      if (internal_bp == -1)
+        {
+          PyErr_SetString (PyExc_ValueError,
+                           _("The value of `internal' must be a boolean."));
+          return -1;
+        }
+    }
+
+  bppy_pending_object = self_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      create_breakpoint_sal (python_gdbarch, sals, NULL, NULL,
+                             bp_breakpoint, disp_donttouch, -1,
+                             0, 0, NULL, 0, 1, internal_bp, 0) ;
+    }
+  if (except.reason < 0)
+    {
+      PyErr_Format (except.reason == RETURN_QUIT
+                    ? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
+                    "%s", except.message);
+      return -1;
+    }
+  BPPY_SET_REQUIRE_VALID (self_bp);
+
+  self_bp->bp->frame_id = frame_id;
+
+  self_bpfinish->out_of_scoped = 0;
+
+  return 0;
+
+invalid_frame:
+  PyErr_SetString (PyExc_ValueError,
+                   _("Invalid ID for the `frame' object."));
+  return -1;
+}
+
+/* Callback for `bpfinishpy_detect_out_scope'*/
+
+static int
+bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
+{
+  struct breakpoint *bp_stopped = (struct breakpoint *) args;
+  PyObject *py_bp = (PyObject *) b->py_bp_object;
+  PyGILState_STATE state;
+
+  /* Prevent python SEGFAULT because of missing thread state.  */
+  state = PyGILState_Ensure();
+
+  /* Trigger out_of_scope notification if this is a FinishBreakpoint
+     and GDB is stopped at this bp or its frame is not in the current
+     callstack and the notification has not been sent yet.  */
+  if (py_bp != NULL
+      && PyObject_TypeCheck (py_bp, &finish_breakpoint_object_type)
+      && (b == bp_stopped || frame_find_by_id(b->frame_id) == NULL)
+      && !((struct finish_breakpoint_object *) py_bp)->out_of_scoped)
+    {
+      gdbpy_out_of_scope ((struct finish_breakpoint_object *) py_bp);
+    }
+
+  PyGILState_Release (state);
+
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run out
+   out of the scope of any FinishBreakpoint before it has been hit.  */
+
+static void
+bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
+{
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
+                            bs == NULL ? NULL : bs->breakpoint_at);
+}
+
+/* Initialize the Python finish breakpoint code.  */
+
+void
+gdbpy_initialize_finishbreakpoints (void)
+{
+  if (PyType_Ready (&finish_breakpoint_object_type) < 0)
+      return;
+
+  Py_INCREF (&finish_breakpoint_object_type);
+  PyModule_AddObject (gdb_module, "FinishBreakpoint",
+                      (PyObject *) &finish_breakpoint_object_type);
+
+  observer_attach_normal_stop (bpfinishpy_handle_stop);
+}
+
+static PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "out_of_scoped", bpfinishpy_get_outofscoped, bpfinishpy_set_outofscoped,
+    "Boolean telling whether the breakpoint is still within the scope \
+of the current callstack.", NULL },
+  { "return_value", bpfinishpy_get_returnvalue, NULL,
+  "gdb.Value object representing the return value, if any. \
+None otherwise.", NULL },
+    { NULL }  /* Sentinel.  */
+};
+
+static PyTypeObject finish_breakpoint_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,                              /*ob_size*/
+  "gdb.FinishBreakpoint",         /*tp_name*/
+  sizeof (struct finish_breakpoint_object), /*tp_basicsize*/
+  0,                              /*tp_itemsize*/
+  0,                              /*tp_dealloc*/
+  0,                              /*tp_print*/
+  0,                              /*tp_getattr*/
+  0,                              /*tp_setattr*/
+  0,                              /*tp_compare*/
+  0,                              /*tp_repr*/
+  0,                              /*tp_as_number*/
+  0,                              /*tp_as_sequence*/
+  0,                              /*tp_as_mapping*/
+  0,                              /*tp_hash */
+  0,                              /*tp_call*/
+  0,                              /*tp_str*/
+  0,                              /*tp_getattro*/
+  0,                              /*tp_setattro */
+  0,                              /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /*tp_flags*/
+  "GDB finish breakpoint object", /* tp_doc */
+  0,                              /* tp_traverse */
+  0,                              /* tp_clear */
+  0,                              /* tp_richcompare */
+  0,                              /* tp_weaklistoffset */
+  0,                              /* tp_iter */
+  0,                              /* tp_iternext */
+  0,                              /* tp_methods */
+  0,                              /* tp_members */
+  finish_breakpoint_object_getset,/* tp_getset */
+  &breakpoint_object_type,        /* tp_base */
+  0,                              /* tp_dict */
+  0,                              /* tp_descr_get */
+  0,                              /* tp_descr_set */
+  0,                              /* tp_dictoffset */
+  bpfinishpy_init,                /* tp_init */
+  0,                              /* tp_alloc */
+  0                               /* 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"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
new file mode 100644
index 0000000..febef50
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,63 @@
+# Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests the mechanism
+# exposing values to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+load_lib gdb-python.exp
+
+set testfile "py-breakpoint"
+set srcfile ${testfile}.c
+set remote_python_file [remote_download host
${srcdir}/${subdir}/py-finish-breakpoint.py]
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+gdb_test "source $remote_python_file" ".*Python script imported.*"
"import python scripts"
+
+gdb_test "python MyBreakpoint(\"increase_1\")" ".*Breakpoint 2.*"
"create Python function breakpoint"
+
+gdb_test "continue" ".*Arrived at MyBreakpoint with 0.*" "check
MyBreakpoint hit"
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval
(\"a\"), gdb.selected_frame ())" ".*Breakpoint 3.*" "set
FinishBreakpoint"
+gdb_test "python print finishbp.out_of_scoped" ".*False.*" "check
out_of_scoped init"
+gdb_test "python print finishbp.return_value" ".*None.*" "check return_value"
+
+gdb_test "continue" ".*MyFinishBreakpoint stop with
1.*#0.*increase.*" "check MyFinishBreakpoint hit"
+gdb_test "python print finishbp.return_value" ".*-5.*" "check return_value"
+gdb_test "python print finishbp.out_of_scoped" ".*False.*" "check
out_of_scoped after hit"
+gdb_test "finish" ".*Run til.*" "return to main()"
+gdb_test "python print finishbp.return_value" ".*None.*" "check return_value"
+
+gdb_test "continue" ".*Arrived at MyBreakpoint with 1.*" "check
MyBreakpoint second hit"
+gdb_test "up" ".*increase_1.*" "go one frame up"
+gdb_test_no_output "python finishbp.out_of_scoped = False" "re-enable
out_of_scoped"
+gdb_test_no_output "set confirm off" "disable confirmation"
+gdb_test_no_output "return" "return from the frame"
+
+gdb_test "next" ".*MyFinishBreakpoint out of scope.*" "check Finish
breakpoint discard"
+gdb_test "python print finishbp.out_of_scoped" ".*True.*" "check out_of_scoped"
\ No newline at end of file
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.py
b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
new file mode 100644
index 0000000..ea82fe9
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,21 @@
+class MyBreakpoint(gdb.Breakpoint):
+       def stop(self):
+               val = gdb.parse_and_eval ("a")
+               print "Arrived at MyBreakpoint with %d" % int(val.dereference())
+               return True
+
+class MyFinishBreakpoint(gdb.FinishBreakpoint):
+       def __init__(self, val, frame):
+               super (MyFinishBreakpoint, self).__init__ (frame)
+               print "MyFinishBreakpoint init"
+               self.val = val
+
+       def stop(self):
+               print "MyFinishBreakpoint stop with %d" %
int(self.val.dereference())
+               gdb.execute("where 1")
+               return True
+
+       def out_of_scope(self):
+               print "MyFinishBreakpoint out of scope..."
+
+print "Python script imported"
\ No newline at end of file
--
1.7.4.4


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC] Python Finish Breakpoints
  2011-05-11  7:44     ` Kevin Pouget
@ 2011-05-11 10:31       ` Phil Muldoon
  2011-05-11 11:29         ` Kevin Pouget
  2011-05-12 10:50         ` Phil Muldoon
  0 siblings, 2 replies; 13+ messages in thread
From: Phil Muldoon @ 2011-05-11 10:31 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb, gdb-patches

Kevin Pouget <kevin.pouget@gmail.com> writes:

> Any feedback ... ?

Apologies, catching up on email after vacation.

>> 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 idea looks good.


>> 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 ...

I don't think you want to universally modify this condition (I am not
sure of the implications either, maybe Pedro will have some more
in-depth info).  Anyway given this case, I would create a function
called something like "gdbpy_is_finish_bp" in python.c and add that to
the condition makeup.


> @@ -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 \

This is a nit I have personally, but you can put the .o file in the
correct alphabetical order?  

> @@ -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 \

Ditto, see above.

> @@ -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)
> +

Ditto.

>  py-cmd.o: $(srcdir)/python/py-cmd.c
>        $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-cmd.c
>        $(POSTCOMPILE)


> +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);


I'm not sure we should be exposing this function (create_breakpoint_sal)
on a global scope, though I have no particular issue with it.


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


This patch context is not wide enough to know, but I this function I
think should be placed next to the corresponding print_ function.

> -  if (inferior_thread ()->control.proceed_to_finish)
> +  /*if (inferior_thread ()->control.proceed_to_finish)*/
>     {
>       /* This should not be necessary.  */
>       if (stop_registers)

See above for my comments on this.


> diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
> index 9c33848..db2c411 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/>.  */
>
> +
> +

Spurious newlines.

>  /* This is used to initialize various gdb.bp_* constants.  */
>  struct pybp_code
>  {
> @@ -806,21 +773,25 @@ 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->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);
>  }


I'm not adverse to this change, but the new breakpoint initialization
logic does not seem to need to be rewritten in the context of this
patch?  If this is just a change you feel needed to be made, I'd send it
as a separate patch.  That's just my opinion, the actual maintainers
might not care. ;)


> -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*/
> +  0,                              /*tp_dealloc*/

Spurious change.

>   0,                             /*tp_print*/
>   0,                             /*tp_getattr*/
>   0,                             /*tp_setattr*/
> @@ -1008,7 +979,7 @@ static PyTypeObject breakpoint_object_type =
>   0,                             /* tp_dict */
>   0,                             /* tp_descr_get */
>   0,                             /* tp_descr_set */
> -  0,                             /* tp_dictoffset */
> +  0,                              /* tp_dictoffset */

Ditto (Unless you are correcting indention, which is difficult to see in
a patch context).

> +++ b/gdb/python/py-breakpoint.h
> @@ -0,0 +1,61 @@
> +/* Python interface to breakpoints
> +
> +   Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
> +
> +   This file is part of GDB.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GDB_PY_BREAKPOINT_H
> +#define GDB_PY_BREAKPOINT_H
> +
> +/* 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)
> +
> +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;
> +};
> +
> +/* Variables used to pass information between the Breakpoint
> +   constructor and the breakpoint-created hook function.  */
> +extern breakpoint_object *bppy_pending_object;
> +
> +#endif /* GDB_PY_BREAKPOINT_H */

I'm not sure on whether we should be creating header files for
individual Python objects.  Normally, depending on the scope/context of
the exported functions and macros we place them in
python/python-internal.h.  I'll defer this change to Tom's wisdom.




> +/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
> +   the current callstack. If BP_OBJ has the attribute OUT_OF_SCOPED and
> +   its value is FALSE, trigger the method OUT_OF_SCOPE and set the flag to
> +   TRUE.  */

Two spaces after . in the comment.

> +bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
> +{
> +  struct breakpoint *bp_stopped = (struct breakpoint *) args;
> +  PyObject *py_bp = (PyObject *) b->py_bp_object;
> +  PyGILState_STATE state;
> +
> +  /* Prevent python SEGFAULT because of missing thread state.  */
> +  state = PyGILState_Ensure();

There is a specialized cleanup function that does this for you:

For example:

  cleanup = ensure_python_env (get_current_arch (), current_language);

Make sure you get the arch from the breakpoint if applicable.  Then just
call do_cleanups when done.  This ensure several internal GDB settings
are saved and restored, as well as the GIL.

> +  PyGILState_Release (state);

do_cleanups (cleanup).  Also make sure any local failure goto branches
do this too.

> +      return;
> +
> +  Py_INCREF (&finish_breakpoint_object_type);
> +  PyModule_AddObject (gdb_module, "FinishBreakpoint",
> +                      (PyObject *) &finish_breakpoint_object_type);
> +
> +  observer_attach_normal_stop (bpfinishpy_handle_stop);
> +}
> +
> +static PyGetSetDef finish_breakpoint_object_getset[] = {
> +  { "out_of_scoped", bpfinishpy_get_outofscoped, bpfinishpy_set_outofscoped,

Sounds weird, should it be "out_of_scope"?


>
> -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);
>     }

I'm not sure the above is needed for the patch?  If it is a cleanup,
somewhat like the case above I would just send it as a desperate patch.


> -
> +
>   return 0; /* Break at end. */
>  }

Spurious.

Overall I like the idea, but I am unsure of the implementation.  I don't
want to unnecessarily bike-shed something before the maintainer have a
had a look at it.

Thanks for you hard work in GDB.

Cheers,

Phil


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC] Python Finish Breakpoints
  2011-05-11 10:31       ` Phil Muldoon
@ 2011-05-11 11:29         ` Kevin Pouget
  2011-05-12 10:50         ` Phil Muldoon
  1 sibling, 0 replies; 13+ messages in thread
From: Kevin Pouget @ 2011-05-11 11:29 UTC (permalink / raw)
  To: pmuldoon; +Cc: gdb, gdb-patches

thanks for all your comments, all the obvious problems will be fixed
in the next patch; and I answered inline the other remarks

On Wed, May 11, 2011 at 6:31 AM, Phil Muldoon <pmuldoon@redhat.com> wrote:
> Kevin Pouget <kevin.pouget@gmail.com> writes:
>
>> Any feedback ... ?
>
> Apologies, catching up on email after vacation.
>
>>> 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 idea looks good.
>
>
>>> 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 ...
>
> I don't think you want to universally modify this condition (I am not
> sure of the implications either, maybe Pedro will have some more
> in-depth info).  Anyway given this case, I would create a function
> called something like "gdbpy_is_finish_bp" in python.c and add that to
> the condition makeup.

sounds like a good idea, I don't want to change blindly a code which
was working correctly, so gdbpy_is_finish_bp should do the trick

>> @@ -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 \
>
> This is a nit I have personally, but you can put the .o file in the
> correct alphabetical order?

sure, I didn't know that!

>> @@ -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 \
>
> Ditto, see above.
>
>> @@ -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)
>> +
>
> Ditto.
>
>>  py-cmd.o: $(srcdir)/python/py-cmd.c
>>        $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-cmd.c
>>        $(POSTCOMPILE)
>
>
>> +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);
>
>
> I'm not sure we should be exposing this function (create_breakpoint_sal)
> on a global scope, though I have no particular issue with it.

I don't know what's the rule to export or not a function, I considered
that `create_breakpoint_sal' fitted my requirements and was
'high-level enough' to be used from Python, but it might be a bit too
naive ...

>> +extern struct value *get_return_value (struct type *func_type,
>> +                                       struct type *value_type);
>> +
>>  /* Address at which inferior stopped.  */
>
>
> This patch context is not wide enough to know, but I this function I
> think should be placed next to the corresponding print_ function.
>
>> -  if (inferior_thread ()->control.proceed_to_finish)
>> +  /*if (inferior_thread ()->control.proceed_to_finish)*/
>>     {
>>       /* This should not be necessary.  */
>>       if (stop_registers)
>
> See above for my comments on this.
>
>
>> diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
>> index 9c33848..db2c411 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/>.  */
>>
>> +
>> +
>
> Spurious newlines.
>
>>  /* This is used to initialize various gdb.bp_* constants.  */
>>  struct pybp_code
>>  {
>> @@ -806,21 +773,25 @@ 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->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);
>>  }
>
>
> I'm not adverse to this change, but the new breakpoint initialization
> logic does not seem to need to be rewritten in the context of this
> patch?  If this is just a change you feel needed to be made, I'd send it
> as a separate patch.  That's just my opinion, the actual maintainers
> might not care. ;)

you're right, there _was_ a reason at the time I changed it, but I
can't see any now, so i'll revert it to its original form

>> -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*/
>> +  0,                              /*tp_dealloc*/
>
> Spurious change.
>
>>   0,                             /*tp_print*/
>>   0,                             /*tp_getattr*/
>>   0,                             /*tp_setattr*/
>> @@ -1008,7 +979,7 @@ static PyTypeObject breakpoint_object_type =
>>   0,                             /* tp_dict */
>>   0,                             /* tp_descr_get */
>>   0,                             /* tp_descr_set */
>> -  0,                             /* tp_dictoffset */
>> +  0,                              /* tp_dictoffset */
>
> Ditto (Unless you are correcting indention, which is difficult to see in
> a patch context).

no, sorry

>> +++ b/gdb/python/py-breakpoint.h
>> @@ -0,0 +1,61 @@
>> +/* Python interface to breakpoints
>> +
>> +   Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
>> +
>> +   This file is part of GDB.
>> +
>> +   This program is free software; you can redistribute it and/or modify
>> +   it under the terms of the GNU General Public License as published by
>> +   the Free Software Foundation; either version 3 of the License, or
>> +   (at your option) any later version.
>> +
>> +   This program is distributed in the hope that it will be useful,
>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> +   GNU General Public License for more details.
>> +
>> +   You should have received a copy of the GNU General Public License
>> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
>> +
>> +#ifndef GDB_PY_BREAKPOINT_H
>> +#define GDB_PY_BREAKPOINT_H
>> +
>> +/* 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)
>> +
>> +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;
>> +};
>> +
>> +/* Variables used to pass information between the Breakpoint
>> +   constructor and the breakpoint-created hook function.  */
>> +extern breakpoint_object *bppy_pending_object;
>> +
>> +#endif /* GDB_PY_BREAKPOINT_H */
>
> I'm not sure on whether we should be creating header files for
> individual Python objects.  Normally, depending on the scope/context of
> the exported functions and macros we place them in
> python/python-internal.h.  I'll defer this change to Tom's wisdom.

my implementation is based on (what I understood of) existing files,
namely py-breakpoint.h here; and I understand only now that
 python/python-internal.h plays the role of head for all the python
files. I'll re-organize it, depending on what Tom will say
>
>
>> +/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
>> +   the current callstack. If BP_OBJ has the attribute OUT_OF_SCOPED and
>> +   its value is FALSE, trigger the method OUT_OF_SCOPE and set the flag to
>> +   TRUE.  */
>
> Two spaces after . in the comment.
>
>> +bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
>> +{
>> +  struct breakpoint *bp_stopped = (struct breakpoint *) args;
>> +  PyObject *py_bp = (PyObject *) b->py_bp_object;
>> +  PyGILState_STATE state;
>> +
>> +  /* Prevent python SEGFAULT because of missing thread state.  */
>> +  state = PyGILState_Ensure();
>
> There is a specialized cleanup function that does this for you:
>
> For example:
>
>  cleanup = ensure_python_env (get_current_arch (), current_language);
>
> Make sure you get the arch from the breakpoint if applicable.  Then just
> call do_cleanups when done.  This ensure several internal GDB settings
> are saved and restored, as well as the GIL.
>
>> +  PyGILState_Release (state);
>
> do_cleanups (cleanup).  Also make sure any local failure goto branches
> do this too.

thanks, I'll look at that. Does it mean that the other
"PyGILState_Ensure/PyGILState_Release" should disappear ?

>> +      return;
>> +
>> +  Py_INCREF (&finish_breakpoint_object_type);
>> +  PyModule_AddObject (gdb_module, "FinishBreakpoint",
>> +                      (PyObject *) &finish_breakpoint_object_type);
>> +
>> +  observer_attach_normal_stop (bpfinishpy_handle_stop);
>> +}
>> +
>> +static PyGetSetDef finish_breakpoint_object_getset[] = {
>> +  { "out_of_scoped", bpfinishpy_get_outofscoped, bpfinishpy_set_outofscoped,
>
> Sounds weird, should it be "out_of_scope"?

yeah, I wasn't sure how 'out_of_scoped" would sound to a (native)
English hear, "out_of_scope" is the name of the function call when GDB
notices that the frame of the FinishBreakpoint is not anymore in the
callstack, and this flag indicates if the Python script already knows
it or not. `out_of_scope_notification' might be a better naming,
although a bit long

>>
>> -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);
>>     }
>
> I'm not sure the above is needed for the patch?  If it is a cleanup,
> somewhat like the case above I would just send it as a desperate patch.

no, this time there is a valid reason:

>> -frame_object_to_frame_info (frame_object *frame_obj)
>> +frame_object_to_frame_info (PyObject *obj)

I exported `frame_object_to_frame_info' to to `python-internal.h', but
`frame_object *' is only defined within 'py-frame.c` (that's also the
way -most of- the other python<-->C translators where prototyped)

all the
>> -      FRAPY_REQUIRE_VALID ((frame_object *) self, frame);
>> +      FRAPY_REQUIRE_VALID (self, frame);
follow from that. (FRAPY_REQUIRE_VALID internally uses
frame_object_to_frame_info.)
>
>> -
>> +
>>   return 0; /* Break at end. */
>>  }
>
> Spurious.
>
> Overall I like the idea, but I am unsure of the implementation.  I don't
> want to unnecessarily bike-shed something before the maintainer have a
> had a look at it.
>
> Thanks for you hard work in GDB.

thanks for all your useful comments,

Kevin


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC] Python Finish Breakpoints
  2011-05-11 10:31       ` Phil Muldoon
  2011-05-11 11:29         ` Kevin Pouget
@ 2011-05-12 10:50         ` Phil Muldoon
  2011-05-12 11:29           ` Kevin Pouget
  1 sibling, 1 reply; 13+ messages in thread
From: Phil Muldoon @ 2011-05-12 10:50 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb, gdb-patches

Phil Muldoon <pmuldoon@redhat.com> writes:

> Kevin Pouget <kevin.pouget@gmail.com> writes:
>
>> Any feedback ... ?


> Overall I like the idea, but I am unsure of the implementation.  I don't
> want to unnecessarily bike-shed something before the maintainer have a
> had a look at it.

I talked with Tom about this on IRC (really we should have done it on
the list, but there things happen).  I'm sure Tom will also comment.
But some things came up that Tom mentioned, and we expanded on, that is
important enough to mention now, before you do anymore work.

There are questions arising on how these breakpoints cope when the
inferior jumps unexpectedly.  I can think of two examples of this
behavior during inferior function calls.  

* What happens with your breakpoint when you perform an inferior function
  call on a C++ function, and that function raises an exception that is
  normally and legally processed by an out-of-frame exception handler?
  This question arises as it triggers special behavior in GDB.  The
  problem originates from the dummy-frame created by GDB.  It insulates the
  out-of-frame exception handler from the exception raising code, and
  the default C++ handler terminates the inferior.  GDB detects this at
  present, and rewinds the stack back to the original location before the
  function call.  What happens, as in your example, if the breakpoint is
  in this inferior function call?  This unwinding behavior related to a
  C++ inferior is 'on' by default in GDB.

* The other related example is what happens when a signal is delivered
  to the inferior during the inferior function call?  Presently GDB will
  stop with an error message.  However the user can turn unwinding 'on'
  in this scenario too.  This signal handling unwinding is 'off' by
  default.  

There are other scenarios and state changes to consider.  What happens if
a longjmp occurs? An exec?

So the behavior is subtle when dealing with inferior changes.  So to
prove this new breakpoint type is robust, you would need to provide
test-cases of this kind of proven behavior in your patch.

Apologies to Tom is I misquoted or mis-phrased our conversation. ;)

Cheers,

Phil


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC] Python Finish Breakpoints
  2011-05-12 10:50         ` Phil Muldoon
@ 2011-05-12 11:29           ` Kevin Pouget
  0 siblings, 0 replies; 13+ messages in thread
From: Kevin Pouget @ 2011-05-12 11:29 UTC (permalink / raw)
  To: pmuldoon; +Cc: gdb, gdb-patches

thanks for these useful feedbacks,
I will try to better understand what can be wrong/unusual with the
situations you described and prepare some tests to catch it.

But globally, my thoughts when I prepared this interface were that it
shouldn't be much different from a classic breakpoint. I'm not really
familiar with C++ mechanisms, but I can't see (right now, but I'll
investigate it) how it differs from setting a BP on the frame above,
checking for recursion upon BP hit and checking for the scope every
once in a while.

for instance, to talk about what I know
> What happens if a longjmp occurs?

nothing! obj->out_of_scope() will be triggered by
`observer_notify_stop' and the script will have the ability to
obj->delete() the breakpoint if it thinks it won't be useful anymore

> An exec?
nothing/the same as above, out_of_scope() will be triggered at the
next stop, I don't know off the top of my head what GDB does with BPs
on exec(), but the same would happen with FinishBreakpoint



I'll prepare the tests for all these situations and I'll see if it is
'at easy' as I thought

thanks,

Kevin

--

> There are questions arising on how these breakpoints cope when the
> inferior jumps unexpectedly.  I can think of two examples of this
> behavior during inferior function calls.
>
> * What happens with your breakpoint when you perform an inferior function
>  call on a C++ function, and that function raises an exception that is
>  normally and legally processed by an out-of-frame exception handler?
>  This question arises as it triggers special behavior in GDB.  The
>  problem originates from the dummy-frame created by GDB.  It insulates the
>  out-of-frame exception handler from the exception raising code, and
>  the default C++ handler terminates the inferior.  GDB detects this at
>  present, and rewinds the stack back to the original location before the
>  function call.  What happens, as in your example, if the breakpoint is
>  in this inferior function call?  This unwinding behavior related to a
>  C++ inferior is 'on' by default in GDB.
>
> * The other related example is what happens when a signal is delivered
>  to the inferior during the inferior function call?  Presently GDB will
>  stop with an error message.  However the user can turn unwinding 'on'
>  in this scenario too.  This signal handling unwinding is 'off' by
>  default.
>
> There are other scenarios and state changes to consider.  What happens if
> a longjmp occurs? An exec?
>
> So the behavior is subtle when dealing with inferior changes.  So to
> prove this new breakpoint type is robust, you would need to provide
> test-cases of this kind of proven behavior in your patch.
>
> Apologies to Tom is I misquoted or mis-phrased our conversation. ;)
>
> Cheers,
>
> Phil
>


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC] Python Finish Breakpoints
  2011-05-09 14:11 [RFC] Python Finish Breakpoints Kevin Pouget
  2011-05-09 14:31 ` Kevin Pouget
@ 2011-05-12 19:00 ` Doug Evans
  2011-05-13  7:51   ` Phil Muldoon
       [not found]   ` <BANLkTikt2hEUcXkGVH44NaUcwiF1SGdMaw@mail.gmail.com>
  1 sibling, 2 replies; 13+ messages in thread
From: Doug Evans @ 2011-05-12 19:00 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb

Hi.  re:

On Mon, May 9, 2011 at 7:10 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> 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).

Plus

>But globally, my thoughts when I prepared this interface were that it
>shouldn't be much different from a classic breakpoint. I'm not really
>familiar with C++ mechanisms, but I can't see (right now, but I'll
>investigate it) how it differs from setting a BP on the frame above,
>checking for recursion upon BP hit and checking for the scope every
>once in a while.

This *may* be a reasonable approach in the end, and I am sensitive to
the time invested (that's why I'm replying ... I'd hate for too much
time being spent hacking down a path that ultimately gets abandoned),
but I wonder if another approach would be better.  I honestly don't
know if it is, but it feels like it should at least be discussed.

To me, there's a difference between providing robust handling a
hand-called function finishing and a general "finish" handler.  The
semantics are sufficiently different, e.g. w.r.t.
longjmp/c++-exception.

So first let me ask a clarifying question: Is the main purpose for the
patch to provide robust handling of the different ways an inferior
function call can "exit"?
And if so, maybe (or maybe not, dunno) it would be better to focus on
making that work as desired, as opposed to a general purpose
finish-frame breakpoint handler.
The latter may be sufficiently useful as well of course.  At this
point I'd just like to understand the main use-case.


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC] Python Finish Breakpoints
  2011-05-12 19:00 ` Doug Evans
@ 2011-05-13  7:51   ` Phil Muldoon
       [not found]   ` <BANLkTikt2hEUcXkGVH44NaUcwiF1SGdMaw@mail.gmail.com>
  1 sibling, 0 replies; 13+ messages in thread
From: Phil Muldoon @ 2011-05-13  7:51 UTC (permalink / raw)
  To: Doug Evans; +Cc: Kevin Pouget, gdb

Doug Evans <dje@google.com> writes:

> So first let me ask a clarifying question: Is the main purpose for the
> patch to provide robust handling of the different ways an inferior
> function call can "exit"?
> And if so, maybe (or maybe not, dunno) it would be better to focus on
> making that work as desired, as opposed to a general purpose
> finish-frame breakpoint handler.
> The latter may be sufficiently useful as well of course.  At this
> point I'd just like to understand the main use-case.

Pretty much just agreeing with what you said, but with a distinction.
Inferior function calls are so transparent in GDB that it can happen
even if we explicitly exclude the case for these breakpoints (IE,
inferior function calls are a special case which would be ignored).  As
inferior function calls can happen in watchpoint expressions, or in
conditional breakpoints, the C++ exception example can happen there,
anyway.  We can exclude the watchpoint case by disallowing them in the
finish breakpoint.  But even so, with the attaching of conditions to a
breakpoint, will the breakpoint 'know' that GDB has done some magic
re-shuffling with the inferior?

Anyway, tests will tell this story better than my word-craft ;)  I am
all for this functionality, as long as we don't leave the Python script
confused about 'what just happened'.

Cheers,

Phil 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC] Python Finish Breakpoints
       [not found]   ` <BANLkTikt2hEUcXkGVH44NaUcwiF1SGdMaw@mail.gmail.com>
@ 2011-05-13  9:04     ` Kevin Pouget
  2011-05-16 11:24       ` Kevin Pouget
  0 siblings, 1 reply; 13+ messages in thread
From: Kevin Pouget @ 2011-05-13  9:04 UTC (permalink / raw)
  To: gdb

Hello,


(sorry for the double post)

On Thu, May 12, 2011 at 3:00 PM, Doug Evans <dje@google.com> wrote:
>
> Hi.  re:
>
> On Mon, May 9, 2011 at 7:10 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> > 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).
>
> Plus
>
> >But globally, my thoughts when I prepared this interface were that it
> >shouldn't be much different from a classic breakpoint. I'm not really
> >familiar with C++ mechanisms, but I can't see (right now, but I'll
> >investigate it) how it differs from setting a BP on the frame above,
> >checking for recursion upon BP hit and checking for the scope every
> >once in a while.
>
> This *may* be a reasonable approach in the end, and I am sensitive to
> the time invested (that's why I'm replying ... I'd hate for too much
> time being spent hacking down a path that ultimately gets abandoned),
> but I wonder if another approach would be better.  I honestly don't
> know if it is, but it feels like it should at least be discussed.
>
> To me, there's a difference between providing robust handling a
> hand-called function finishing and a general "finish" handler.  The
> semantics are sufficiently different, e.g. w.r.t.
> longjmp/c++-exception.
>
> So first let me ask a clarifying question: Is the main purpose for the
> patch to provide robust handling of the different ways an inferior
> function call can "exit"?
> And if so, maybe (or maybe not, dunno) it would be better to focus on
> making that work as desired, as opposed to a general purpose
> finish-frame breakpoint handler.
> The latter may be sufficiently useful as well of course.  At this
> point I'd just like to understand the main use-case.

thanks for your support,
you're right, it appears that I need to explicit what I exactly want
to do, which certainly fits in your second proposition, a 'general
"finish" handler'.

I mainly want to catch normal termination of functions calls, the 'out
of scope' notification being almost just a convenient asynchronous /
'best effort' notification.

To make it clearer, here is an example of the functionality I want to
build up upon FinishBreakpoints:

> #include <stdio.h>
> #include <dlfcn.h>
> int
> main (int argc, char ** argv) {
> -->void * handle = dlopen ("/lib64/libm.so.6", RTLD_LAZY); <--
>     if (!handle) {
>         perror("dlopen: ");
>         return 1;
>     }
> -->double (* cosinus) (double) = dlsym (handle, "cos"); <--
>     printf ("%f\n", (* cosinus)(2.0));
>     return 0;
> }

> (gdb) run
> Starting program: /home/kevin/travail/arm/perso/root/sample/gdb-finish/fork
> --> DlOpen(0x400768 "/lib64/libm.so.6") = 0x601030 <--
> --> DlSym("cos", "/lib64/libm.so.6") = 0x3cbd41d580 <--
> -0.416147
> [Inferior 1 (process 8263) exited normally]


I want to be able to exploit the interactions between the inferior and
a given library (here lib. 'dl'). So in this example, the script (at
the bottom of the mail) followed the `dlopen' to catch it's return
value

> DlOpen(0x400768 "/lib64/libm.so.6") = 0x601030

Then it catched the `dlsym' call and worked out the library name
matching the handler, and printed the function pointer associated with
'cos'.
> DlSym("cos", "/lib64/libm.so.6") = 0x3cbd41d580


Let me know if this vision of the feature still looks interesting to
you, I'll prepare some tests about the cases Phil mentioned


Cordially,

Kevin

--

import re

global dlopen_name
global dlopen_handler

dlopen_name = None
dlopen_handler = None
class DlOpenBreakpoint(gdb.Breakpoint):
    def __init__(self):
        gdb.Breakpoint.__init__(self, spec="dlopen", internal=1)
        self.silent = True

    def stop(self):
        DlOpenFinishBreakpoint(gdb.newest_frame(),
gdb.parse_and_eval("(char*) $rdi"))
        return False
DlOpenBreakpoint()

class DlOpenFinishBreakpoint(gdb.FinishBreakpoint):
    def __init__(self, frame, name):
        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
        self.name = name
        self.silent = True

    def stop(self):
        global dlopen_name
        global dlopen_handler

        dlopen_name = self.name
        dlopen_handler = gdb.parse_and_eval("$rax")

        print "DlOpen(%s) = 0x%x" % (dlopen_name, dlopen_handler)

    def out_of_scope(self):
        print "dlopen didn't finish ..."

class DlSymBreakpoint(gdb.Breakpoint):
    def __init__(self):
        gdb.Breakpoint.__init__(self, spec="dlsym", internal=1)
        self.silent = True

    def stop(self):
        global dlopen_name
        global dlopen_handler

        fct = gdb.parse_and_eval("(char *) $rsi")
        handler = gdb.parse_and_eval("$rdi")

        if (dlopen_handler == handler):
            DlSymFinishBreakpoint(gdb.newest_frame(), fct, dlopen_name)
        else:
            print "Unknown handler"
        return False
DlSymBreakpoint()

class DlSymFinishBreakpoint(gdb.FinishBreakpoint):
    def __init__(self, frame, fct, lib):
        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
        self.fct = fct
        self.lib = lib
        self.silent = True

    def stop(self):
        fct_addr = gdb.parse_and_eval("$rax")

        print "DlSym(%s, %s) = 0x%x" % (self.fct, self.lib, fct_addr)


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC] Python Finish Breakpoints
  2011-05-13  9:04     ` Kevin Pouget
@ 2011-05-16 11:24       ` Kevin Pouget
  2011-05-18  8:58         ` Kevin Pouget
  0 siblings, 1 reply; 13+ messages in thread
From: Kevin Pouget @ 2011-05-16 11:24 UTC (permalink / raw)
  To: gdb, pmuldoon, Doug Evans

Hello,

I prepared some tests which show the behavior of this new Breakpoint
type in various environment:
- normal termination, forced termination (gdb return)
- longjmp
- c++ exception
- breakpoint condition

there is two bits which differs from what I expected, in the
breakpoint conditions

* setting a BP in a DUMMY_FRAME: the `stop' function will be triggerd,
but it's return value (stop or continue/booleans) won't be taken into
account. I'm not sure whether the kind of FinishBreakpoint should be
forbidden or not, because I may want to track ALL the calls to that
function, including GDB inferior calls

* the "normal_stop" notification if not triggered during condition
evaluation, so the `out_of_scope_notif' flag is not turn off (more
generally, the notification should be disabled when the breakpoint is
hit, and not when GDB stops)


otherwise, for c++ exception, the return PC of a frame surrounded by a
try/catch is at the end of the catch, so with
try { fct_1() } catch { ... }, a FinishBreakpoint in fct_1 will be
correcty catch, but it won't work for function nested within fct_1.



Does this testsuite match your expectations, or would you like it to
be more advanced? (I'll need more hints in this case)


cordially,

Kevin


diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc
b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc
@@ -0,0 +1,59 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2011 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   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 <iostream>
+
+void
+throw_exception_1 (int e)
+{
+  throw new int (e);
+}
+
+void
+throw_exception (int e)
+{
+  throw_exception_1 (e);
+}
+
+int
+main (void)
+{
+  int i;
+  try
+    {
+      throw_exception_1 (10);
+    }
+  catch (const int *e)
+    {
+        std::cerr << "Exception #" << *e << std::endl;
+    }
+  i += 1; /* Break after exception 1.  */
+
+  try
+    {
+      throw_exception (10);
+    }
+  catch (const int *e)
+    {
+        std::cerr << "Exception #" << *e << std::endl;
+    }
+  i += 1; /* Break after exception 2.  */
+
+  return i;
+}
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp
b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp
new file mode 100644
index 0000000..e74023d
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp
@@ -0,0 +1,59 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests the mechanism
+# exposing values to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+load_lib gdb-python.exp
+
+set testfile "py-finish-breakpoint-cc"
+set srcfile ${testfile}.cc
+set binfile ${objdir}/${subdir}/${testfile}
+set pyfile  ${srcdir}/${subdir}/${testfile}.py
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}"
executable {debug c++}] != "" } {
+    untested "Couldn't compile ${srcfile}"
+    return -1
+}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+
+gdb_test "break [gdb_get_line_number "Break after exception 1."]"
"Breakpoint.* at .*" \
+         "set watchdog after the exception 1"
+gdb_test "break [gdb_get_line_number "Break after exception 2."]"
"Breakpoint.* at .*" \
+         "set watchdog after the exception 2"
+
+gdb_test "python ExceptionBreakpoint()" "ExceptionBreakpoint init"
"set BP before throwing the exception"
+gdb_test "python print len(gdb.breakpoints())" "4" "check number of BPs"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check
FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "4" "check finish BP removal"
+
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint
with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py
b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py
new file mode 100644
index 0000000..d0dfe2f
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py
@@ -0,0 +1,43 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests python Finish
+# Breakpoints.
+
+class ExceptionBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__(self, spec="throw_exception_1", internal=1)
+        self.silent = True
+        print "ExceptionBreakpoint init"
+
+    def stop(self):
+        ExceptionFinishBreakpoint(gdb.newest_frame())
+        return False
+
+class ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        self.silent = True;
+
+    def stop(self):
+	print "stopped at ExceptionFinishBreakpoint"
+	gdb.post_event(self.delete)
+        return True
+
+    def out_of_scope(self):
+        print "exception did not finish ..."
+
+
+print "Python script imported"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.c
b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..32b8b38
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,82 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2011 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   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 <setjmp.h>
+
+int increase_1(int *a)
+{
+  *a += 1;
+  return -5;
+}
+
+void increase(int *a)
+{
+  increase_1(a);
+}
+
+int
+test_1(int i, int j)
+{
+  return i == j;
+}
+
+int
+test(int i, int j)
+{
+  return test_1(i, j);
+}
+
+int
+call_longjmp_1 (jmp_buf *buf)
+{
+  longjmp (*buf, 1);
+}
+
+int
+call_longjmp (jmp_buf *buf)
+{
+  call_longjmp_1 (buf);
+}
+
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  i = 0 ;
+  /* Break at increase. */
+  increase (&i) ;
+  increase (&i) ;
+  increase (&i) ;
+
+  for (i = 0; i < 10; i++)
+    {
+      j += 1; /* Condition Break. */
+    }
+
+  if (setjmp (env) == 0) /* longjmp caught */
+    {
+      call_longjmp (&env);
+    }
+  else
+	  j += 1; /* after longjmp. */
+
+  return j; /* Break at end. */
+}
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
new file mode 100644
index 0000000..65eebc9
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,183 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests the mechanism
+# exposing values to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+load_lib gdb-python.exp
+
+set testfile "py-finish-breakpoint"
+set srcfile ${testfile}.c
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+set remote_python_file [remote_download host
${srcdir}/${subdir}/${testfile}.py]
+
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+#
+# Test FinishBreakpoint in function returned by longjmp
+#
+
+clean_restart ${testfile}
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "source $remote_python_file" ".*Python script imported.*" \
+         "import python scripts"
+
+gdb_test "python ljmpBP = LongjmpFinishBreakpoint(gdb.newest_frame())" \
+         "LongjmpFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" ".*Longjmp didn't finish.*" "check FinishBP out
of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation
+# (finish in dummy frame)
+#
+
+clean_restart ${testfile}
+
+set cond_line [gdb_get_line_number "Condition Break."]
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+gdb_test "source $remote_python_file" ".*Python script imported.*" \
+         "import python scripts"
+
+gdb_test "break ${cond_line} if test_1(i,8)" ".*Breakpoint .* at .*"
"set conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" "set BP in condition"
+
+
+set msg "check FinishBreakpoint don't stop in GDB Dummy Frame"
+gdb_test_multiple "continue" $msg {
+	-re ".*test don't stop 2.*test stop.*test don't stop 4.*" {
+		pass $msg
+	}
+	-re ".*test don't stop 2.*test stop.*$gdb_prompt" {
+		fail $msg
+	}
+}
+
+gdb_test "print i" "8" "check stopped location"
+
+#
+# Test FinishBreakpoint in BP condition evaluation
+# (finish in normal frame)
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" ".*Python script imported.*" \
+         "import python scripts"
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+gdb_test "break ${cond_line} if test(i,8)" ".*Breakpoint .* at .*"
"set conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" "set BP in condition"
+
+gdb_test "continue" ".*test don't stop 1.*test don't stop 2.*test
stop.*Error in testing breakpoint condition.*The program being
debugged stopped while in a function called from GDB.*" \
+         "stop in condition function"
+
+setup_kfail "normal_stop_notification not triggered during condition
evaluation" *-*-*
+gdb_test "python print gdb.breakpoints()\[2\].out_of_scope_notif"
".*False.*" "check out_of_scope notification disabled"
+gdb_test_no_output "python gdb.breakpoints()\[2\].out_of_scope_notif
= False" "reestablish correct value"
+
+gdb_test "continue" "Continuing.*" "finish condition evaluation"
+gdb_test "continue" "Breakpoint.*" "stop at conditional breakpoint"
+gdb_test "print i" "8" "check stopped location"
+
+#
+# Test FinishBreakpoint in normal conditions
+#
+
+clean_restart ${testfile}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+gdb_test_no_output "set confirm off" "disable confirmation"
+gdb_test "source $remote_python_file" ".*Python script imported.*" \
+         "import python scripts"
+gdb_test "python MyBreakpoint(\"increase_1\")" ".*Breakpoint 2.*" \
+         "create Python function breakpoint"
+gdb_test "continue" ".*Arrived at MyBreakpoint with 0.*" "check
MyBreakpoint hit"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval
(\"a\"), gdb.selected_frame ())" \
+         ".*Breakpoint 3.*" "set FinishBreakpoint"
+gdb_test "python print finishbp.out_of_scope_notif" ".*True.*" \
+         "check out_of_scope_notif at init"
+gdb_test "python print finishbp.return_value" ".*None.*" \
+         "check return_value at init"
+
+# check normal bp hit
+
+gdb_test "continue" ".*MyFinishBreakpoint stop with.*#0.*increase.*" \
+         "check MyFinishBreakpoint hit"
+gdb_test "python print finishbp.return_value" ".*-5.*" "check return_value"
+gdb_test "python print finishbp.out_of_scope_notif" ".*False.*" \
+         "check out_of_scope_notif disabled after hit"
+gdb_test "finish" ".*main.*" "return to main()"
+gdb_test "python print finishbp.return_value" ".*None.*" "check return_value"
+
+# check forced return / check out of scpop
+gdb_test_no_output "python finishbp.out_of_scope_notif = True" \
+         "re-enable out_of_scope_notif"
+
+gdb_test "continue" ".*Arrived at MyBreakpoint with.*" "check
MyBreakpoint second hit"
+gdb_test "up" ".*increase_1.*" "go one frame up"
+gdb_test_no_output "return" "return from the frame"
+gdb_test "python print finishbp.check_scope()" ".*MyFinishBreakpoint
out of scope.*True.*" \
+         "go one frame up"
+
+# check forced return / automatic notification
+
+gdb_test_no_output "python finishbp.out_of_scope_notif = True" \
+         "re-enable out_of_scope_notif"
+
+gdb_test "continue" ".*Arrived at MyBreakpoint with.*" "check
MyBreakpoint third hit"
+gdb_test "up" ".*increase_1.*" "go one frame up"
+gdb_test_no_output "return" "return from the frame"
+gdb_test "next" ".*MyFinishBreakpoint out of scope.*" "check Finish
breakpoint discard"
+gdb_test "python print finishbp.out_of_scope_notif" ".*False.*"
"check out_of_scope_notif"
+
+# check FinishBreakpoint in main
+
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         ".*ValueError: \"FinishBreakpoint\" not meaningful in the
outermost frame..*" \
+         "check FinishBP not allowed in main"
+
+
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.py
b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
new file mode 100644
index 0000000..f014cc6
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,85 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests python Finish
+# Breakpoints.
+
+class MyBreakpoint(gdb.Breakpoint):		
+	def stop(self):
+		val = gdb.parse_and_eval ("a")
+		print "Arrived at MyBreakpoint with %d" % int(val.dereference())
+		return True
+		
+class MyFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		super (MyFinishBreakpoint, self).__init__ (frame)
+		print "MyFinishBreakpoint init"
+		self.val = val
+		
+	def stop(self):
+		print "MyFinishBreakpoint stop with %d" % int(self.val.dereference())
+		gdb.execute("where 1")
+		return True
+	
+	def out_of_scope(self):
+		print "MyFinishBreakpoint out of scope..."
+
+test_finish_bp = None
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__(self, spec="test_1", internal=1)
+        self.silent = True
+        self.finish = None
+        print "TestBreakpoint init"
+
+    def stop(self):
+    	global test_finish_bp
+        if (self.finish == None):
+            self.finish = TestFinishBreakpoint(gdb.newest_frame())
+            test_finish_bp = self.finish
+        return False
+
+
+class TestFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        self.count = 0
+
+    def stop(self):
+        self.count += 1
+        if (self.count == 3):
+            print "test stop ..."
+            return True
+        else:
+            print "test don't stop %d" % self.count
+            return False
+
+
+    def out_of_scope(self):
+        print "test didn't finish ..."
+		
+class LongjmpFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+		print "LongjmpFinishBreakpoint init"
+		
+	def stop(self):
+		print "Stopped at LongjmpFinishBreakpoint"
+
+
+	def out_of_scope(self):
+		print "Longjmp didn't finish ..."
+
+print "Python script imported"
-- 
1.7.4.4


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC] Python Finish Breakpoints
  2011-05-16 11:24       ` Kevin Pouget
@ 2011-05-18  8:58         ` Kevin Pouget
  2011-05-18 10:16           ` Phil Muldoon
  0 siblings, 1 reply; 13+ messages in thread
From: Kevin Pouget @ 2011-05-18  8:58 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb, pmuldoon

[-- Attachment #1: Type: text/plain, Size: 21024 bytes --]

Hello,

I've included with this mail a complete patch build agains the current
HEAD, and checked that there was no regression in the testsuite


Kevin

On Mon, May 16, 2011 at 7:23 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> Hello,
>
> I prepared some tests which show the behavior of this new Breakpoint
> type in various environment:
> - normal termination, forced termination (gdb return)
> - longjmp
> - c++ exception
> - breakpoint condition
>
> there is two bits which differs from what I expected, in the
> breakpoint conditions
>
> * setting a BP in a DUMMY_FRAME: the `stop' function will be triggerd,
> but it's return value (stop or continue/booleans) won't be taken into
> account. I'm not sure whether the kind of FinishBreakpoint should be
> forbidden or not, because I may want to track ALL the calls to that
> function, including GDB inferior calls
>
> * the "normal_stop" notification if not triggered during condition
> evaluation, so the `out_of_scope_notif' flag is not turn off (more
> generally, the notification should be disabled when the breakpoint is
> hit, and not when GDB stops)
>
>
> otherwise, for c++ exception, the return PC of a frame surrounded by a
> try/catch is at the end of the catch, so with
> try { fct_1() } catch { ... }, a FinishBreakpoint in fct_1 will be
> correcty catch, but it won't work for function nested within fct_1.
>
>
>
> Does this testsuite match your expectations, or would you like it to
> be more advanced? (I'll need more hints in this case)
>
>
> cordially,
>
> Kevin
>
>
> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc
> b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc
> new file mode 100644
> index 0000000..a0eea06
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc
> @@ -0,0 +1,59 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2011 Free Software Foundation, Inc.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   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 <iostream>
> +
> +void
> +throw_exception_1 (int e)
> +{
> +  throw new int (e);
> +}
> +
> +void
> +throw_exception (int e)
> +{
> +  throw_exception_1 (e);
> +}
> +
> +int
> +main (void)
> +{
> +  int i;
> +  try
> +    {
> +      throw_exception_1 (10);
> +    }
> +  catch (const int *e)
> +    {
> +        std::cerr << "Exception #" << *e << std::endl;
> +    }
> +  i += 1; /* Break after exception 1.  */
> +
> +  try
> +    {
> +      throw_exception (10);
> +    }
> +  catch (const int *e)
> +    {
> +        std::cerr << "Exception #" << *e << std::endl;
> +    }
> +  i += 1; /* Break after exception 2.  */
> +
> +  return i;
> +}
> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp
> b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp
> new file mode 100644
> index 0000000..e74023d
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp
> @@ -0,0 +1,59 @@
> +# Copyright (C) 2011 Free Software Foundation, Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# This file is part of the GDB testsuite.  It tests the mechanism
> +# exposing values to Python.
> +
> +if $tracelevel then {
> +    strace $tracelevel
> +}
> +
> +load_lib gdb-python.exp
> +
> +set testfile "py-finish-breakpoint-cc"
> +set srcfile ${testfile}.cc
> +set binfile ${objdir}/${subdir}/${testfile}
> +set pyfile  ${srcdir}/${subdir}/${testfile}.py
> +
> +# Start with a fresh gdb.
> +gdb_exit
> +gdb_start
> +gdb_reinitialize_dir $srcdir/$subdir
> +gdb_load ${binfile}
> +
> +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}"
> executable {debug c++}] != "" } {
> +    untested "Couldn't compile ${srcfile}"
> +    return -1
> +}
> +
> +if ![runto_main] then {
> +    fail "Cannot run to main."
> +    return 0
> +}
> +
> +gdb_test "source $pyfile" ".*Python script imported.*" \
> +         "import python scripts"
> +
> +gdb_test "break [gdb_get_line_number "Break after exception 1."]"
> "Breakpoint.* at .*" \
> +         "set watchdog after the exception 1"
> +gdb_test "break [gdb_get_line_number "Break after exception 2."]"
> "Breakpoint.* at .*" \
> +         "set watchdog after the exception 2"
> +
> +gdb_test "python ExceptionBreakpoint()" "ExceptionBreakpoint init"
> "set BP before throwing the exception"
> +gdb_test "python print len(gdb.breakpoints())" "4" "check number of BPs"
> +gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check
> FinishBreakpoint in catch()"
> +gdb_test "python print len(gdb.breakpoints())" "4" "check finish BP removal"
> +
> +gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint
> with exception thrown not caught"
> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py
> b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py
> new file mode 100644
> index 0000000..d0dfe2f
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py
> @@ -0,0 +1,43 @@
> +# Copyright (C) 2011 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# This file is part of the GDB testsuite.  It tests python Finish
> +# Breakpoints.
> +
> +class ExceptionBreakpoint(gdb.Breakpoint):
> +    def __init__(self):
> +        gdb.Breakpoint.__init__(self, spec="throw_exception_1", internal=1)
> +        self.silent = True
> +        print "ExceptionBreakpoint init"
> +
> +    def stop(self):
> +        ExceptionFinishBreakpoint(gdb.newest_frame())
> +        return False
> +
> +class ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
> +    def __init__(self, frame):
> +        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
> +        self.silent = True;
> +
> +    def stop(self):
> +       print "stopped at ExceptionFinishBreakpoint"
> +       gdb.post_event(self.delete)
> +        return True
> +
> +    def out_of_scope(self):
> +        print "exception did not finish ..."
> +
> +
> +print "Python script imported"
> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.c
> b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
> new file mode 100644
> index 0000000..32b8b38
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
> @@ -0,0 +1,82 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2011 Free Software Foundation, Inc.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   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 <setjmp.h>
> +
> +int increase_1(int *a)
> +{
> +  *a += 1;
> +  return -5;
> +}
> +
> +void increase(int *a)
> +{
> +  increase_1(a);
> +}
> +
> +int
> +test_1(int i, int j)
> +{
> +  return i == j;
> +}
> +
> +int
> +test(int i, int j)
> +{
> +  return test_1(i, j);
> +}
> +
> +int
> +call_longjmp_1 (jmp_buf *buf)
> +{
> +  longjmp (*buf, 1);
> +}
> +
> +int
> +call_longjmp (jmp_buf *buf)
> +{
> +  call_longjmp_1 (buf);
> +}
> +
> +
> +int main (int argc, char *argv[])
> +{
> +  jmp_buf env;
> +  int foo = 5;
> +  int bar = 42;
> +  int i, j;
> +
> +  i = 0 ;
> +  /* Break at increase. */
> +  increase (&i) ;
> +  increase (&i) ;
> +  increase (&i) ;
> +
> +  for (i = 0; i < 10; i++)
> +    {
> +      j += 1; /* Condition Break. */
> +    }
> +
> +  if (setjmp (env) == 0) /* longjmp caught */
> +    {
> +      call_longjmp (&env);
> +    }
> +  else
> +         j += 1; /* after longjmp. */
> +
> +  return j; /* Break at end. */
> +}
> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
> b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
> new file mode 100644
> index 0000000..65eebc9
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
> @@ -0,0 +1,183 @@
> +# Copyright (C) 2011 Free Software Foundation, Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# This file is part of the GDB testsuite.  It tests the mechanism
> +# exposing values to Python.
> +
> +if $tracelevel then {
> +    strace $tracelevel
> +}
> +
> +load_lib gdb-python.exp
> +
> +set testfile "py-finish-breakpoint"
> +set srcfile ${testfile}.c
> +
> +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
> +    return -1
> +}
> +
> +set remote_python_file [remote_download host
> ${srcdir}/${subdir}/${testfile}.py]
> +
> +
> +# Skip all tests if Python scripting is not enabled.
> +if { [skip_python_tests] } { continue }
> +
> +#
> +# Test FinishBreakpoint in function returned by longjmp
> +#
> +
> +clean_restart ${testfile}
> +
> +if ![runto call_longjmp_1] then {
> +    perror "couldn't run to breakpoint call_longjmp"
> +    continue
> +}
> +
> +gdb_test "source $remote_python_file" ".*Python script imported.*" \
> +         "import python scripts"
> +
> +gdb_test "python ljmpBP = LongjmpFinishBreakpoint(gdb.newest_frame())" \
> +         "LongjmpFinishBreakpoint init" \
> +         "set finish breakpoint"
> +gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
> +         "set BP after the jump"
> +gdb_test "continue" ".*Longjmp didn't finish.*" "check FinishBP out
> of scope notification"
> +
> +#
> +# Test FinishBreakpoint in BP condition evaluation
> +# (finish in dummy frame)
> +#
> +
> +clean_restart ${testfile}
> +
> +set cond_line [gdb_get_line_number "Condition Break."]
> +if ![runto_main] then {
> +    fail "Cannot run to main."
> +    return 0
> +}
> +
> +gdb_test "source $remote_python_file" ".*Python script imported.*" \
> +         "import python scripts"
> +
> +gdb_test "break ${cond_line} if test_1(i,8)" ".*Breakpoint .* at .*"
> "set conditional BP"
> +gdb_test "python TestBreakpoint()" "TestBreakpoint init" "set BP in condition"
> +
> +
> +set msg "check FinishBreakpoint don't stop in GDB Dummy Frame"
> +gdb_test_multiple "continue" $msg {
> +       -re ".*test don't stop 2.*test stop.*test don't stop 4.*" {
> +               pass $msg
> +       }
> +       -re ".*test don't stop 2.*test stop.*$gdb_prompt" {
> +               fail $msg
> +       }
> +}
> +
> +gdb_test "print i" "8" "check stopped location"
> +
> +#
> +# Test FinishBreakpoint in BP condition evaluation
> +# (finish in normal frame)
> +#
> +
> +clean_restart ${testfile}
> +
> +gdb_test "source $remote_python_file" ".*Python script imported.*" \
> +         "import python scripts"
> +
> +if ![runto_main] then {
> +    fail "Cannot run to main."
> +    return 0
> +}
> +
> +gdb_test "break ${cond_line} if test(i,8)" ".*Breakpoint .* at .*"
> "set conditional BP"
> +gdb_test "python TestBreakpoint()" "TestBreakpoint init" "set BP in condition"
> +
> +gdb_test "continue" ".*test don't stop 1.*test don't stop 2.*test
> stop.*Error in testing breakpoint condition.*The program being
> debugged stopped while in a function called from GDB.*" \
> +         "stop in condition function"
> +
> +setup_kfail "normal_stop_notification not triggered during condition
> evaluation" *-*-*
> +gdb_test "python print gdb.breakpoints()\[2\].out_of_scope_notif"
> ".*False.*" "check out_of_scope notification disabled"
> +gdb_test_no_output "python gdb.breakpoints()\[2\].out_of_scope_notif
> = False" "reestablish correct value"
> +
> +gdb_test "continue" "Continuing.*" "finish condition evaluation"
> +gdb_test "continue" "Breakpoint.*" "stop at conditional breakpoint"
> +gdb_test "print i" "8" "check stopped location"
> +
> +#
> +# Test FinishBreakpoint in normal conditions
> +#
> +
> +clean_restart ${testfile}
> +
> +if ![runto_main] then {
> +    fail "Cannot run to main."
> +    return 0
> +}
> +gdb_test_no_output "set confirm off" "disable confirmation"
> +gdb_test "source $remote_python_file" ".*Python script imported.*" \
> +         "import python scripts"
> +gdb_test "python MyBreakpoint(\"increase_1\")" ".*Breakpoint 2.*" \
> +         "create Python function breakpoint"
> +gdb_test "continue" ".*Arrived at MyBreakpoint with 0.*" "check
> MyBreakpoint hit"
> +
> +# set FinishBreakpoint
> +
> +gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval
> (\"a\"), gdb.selected_frame ())" \
> +         ".*Breakpoint 3.*" "set FinishBreakpoint"
> +gdb_test "python print finishbp.out_of_scope_notif" ".*True.*" \
> +         "check out_of_scope_notif at init"
> +gdb_test "python print finishbp.return_value" ".*None.*" \
> +         "check return_value at init"
> +
> +# check normal bp hit
> +
> +gdb_test "continue" ".*MyFinishBreakpoint stop with.*#0.*increase.*" \
> +         "check MyFinishBreakpoint hit"
> +gdb_test "python print finishbp.return_value" ".*-5.*" "check return_value"
> +gdb_test "python print finishbp.out_of_scope_notif" ".*False.*" \
> +         "check out_of_scope_notif disabled after hit"
> +gdb_test "finish" ".*main.*" "return to main()"
> +gdb_test "python print finishbp.return_value" ".*None.*" "check return_value"
> +
> +# check forced return / check out of scpop
> +gdb_test_no_output "python finishbp.out_of_scope_notif = True" \
> +         "re-enable out_of_scope_notif"
> +
> +gdb_test "continue" ".*Arrived at MyBreakpoint with.*" "check
> MyBreakpoint second hit"
> +gdb_test "up" ".*increase_1.*" "go one frame up"
> +gdb_test_no_output "return" "return from the frame"
> +gdb_test "python print finishbp.check_scope()" ".*MyFinishBreakpoint
> out of scope.*True.*" \
> +         "go one frame up"
> +
> +# check forced return / automatic notification
> +
> +gdb_test_no_output "python finishbp.out_of_scope_notif = True" \
> +         "re-enable out_of_scope_notif"
> +
> +gdb_test "continue" ".*Arrived at MyBreakpoint with.*" "check
> MyBreakpoint third hit"
> +gdb_test "up" ".*increase_1.*" "go one frame up"
> +gdb_test_no_output "return" "return from the frame"
> +gdb_test "next" ".*MyFinishBreakpoint out of scope.*" "check Finish
> breakpoint discard"
> +gdb_test "python print finishbp.out_of_scope_notif" ".*False.*"
> "check out_of_scope_notif"
> +
> +# check FinishBreakpoint in main
> +
> +gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
> +         ".*ValueError: \"FinishBreakpoint\" not meaningful in the
> outermost frame..*" \
> +         "check FinishBP not allowed in main"
> +
> +
> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.py
> b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
> new file mode 100644
> index 0000000..f014cc6
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
> @@ -0,0 +1,85 @@
> +# Copyright (C) 2011 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# This file is part of the GDB testsuite.  It tests python Finish
> +# Breakpoints.
> +
> +class MyBreakpoint(gdb.Breakpoint):
> +       def stop(self):
> +               val = gdb.parse_and_eval ("a")
> +               print "Arrived at MyBreakpoint with %d" % int(val.dereference())
> +               return True
> +
> +class MyFinishBreakpoint(gdb.FinishBreakpoint):
> +       def __init__(self, val, frame):
> +               super (MyFinishBreakpoint, self).__init__ (frame)
> +               print "MyFinishBreakpoint init"
> +               self.val = val
> +
> +       def stop(self):
> +               print "MyFinishBreakpoint stop with %d" % int(self.val.dereference())
> +               gdb.execute("where 1")
> +               return True
> +
> +       def out_of_scope(self):
> +               print "MyFinishBreakpoint out of scope..."
> +
> +test_finish_bp = None
> +class TestBreakpoint(gdb.Breakpoint):
> +    def __init__(self):
> +        gdb.Breakpoint.__init__(self, spec="test_1", internal=1)
> +        self.silent = True
> +        self.finish = None
> +        print "TestBreakpoint init"
> +
> +    def stop(self):
> +       global test_finish_bp
> +        if (self.finish == None):
> +            self.finish = TestFinishBreakpoint(gdb.newest_frame())
> +            test_finish_bp = self.finish
> +        return False
> +
> +
> +class TestFinishBreakpoint(gdb.FinishBreakpoint):
> +    def __init__(self, frame):
> +        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
> +        self.count = 0
> +
> +    def stop(self):
> +        self.count += 1
> +        if (self.count == 3):
> +            print "test stop ..."
> +            return True
> +        else:
> +            print "test don't stop %d" % self.count
> +            return False
> +
> +
> +    def out_of_scope(self):
> +        print "test didn't finish ..."
> +
> +class LongjmpFinishBreakpoint(gdb.FinishBreakpoint):
> +       def __init__(self, frame):
> +               gdb.FinishBreakpoint.__init__(self, frame, internal=1)
> +               print "LongjmpFinishBreakpoint init"
> +
> +       def stop(self):
> +               print "Stopped at LongjmpFinishBreakpoint"
> +
> +
> +       def out_of_scope(self):
> +               print "Longjmp didn't finish ..."
> +
> +print "Python script imported"
> --
> 1.7.4.4
>

[-- Attachment #2: 0001-Python-Finish-Breakpoints.txt --]
[-- Type: text/plain, Size: 50813 bytes --]

From 735ddf1d48e94553d401d39afc3b49fb1b4e898e Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Thu, 12 May 2011 15:55:54 -0400
Subject: [PATCH] Python Finish Breakpoints

---
 gdb/Makefile.in                                    |    6 +
 gdb/breakpoint.c                                   |    2 +-
 gdb/breakpoint.h                                   |   10 +
 gdb/infcmd.c                                       |   20 +-
 gdb/inferior.h                                     |    3 +
 gdb/infrun.c                                       |    4 +-
 gdb/python/py-breakpoint.c                         |   40 +--
 gdb/python/py-finishbreakpoint.c                   |  447 ++++++++++++++++++++
 gdb/python/py-frame.c                              |   32 +-
 gdb/python/python-internal.h                       |   44 ++-
 gdb/python/python.c                                |    6 +
 gdb/python/python.h                                |    2 +
 gdb/testsuite/gdb.python/py-breakpoint.exp         |    7 +-
 .../gdb.python/py-finish-breakpoint-cc.cc          |   59 +++
 .../gdb.python/py-finish-breakpoint-cc.exp         |   59 +++
 .../gdb.python/py-finish-breakpoint-cc.py          |   43 ++
 gdb/testsuite/gdb.python/py-finish-breakpoint.c    |   82 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp  |  183 ++++++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py   |   85 ++++
 19 files changed, 1070 insertions(+), 64 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint.c
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint.py

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 5bab360..2507938 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -285,6 +285,7 @@ SUBDIR_PYTHON_OBS = \
 	py-evtregistry.o \
 	py-evts.o \
 	py-exitedevent.o \
+	py-finishbreakpoint.o \
 	py-frame.o \
 	py-function.o \
 	py-inferior.o \
@@ -315,6 +316,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-evtregistry.c \
 	python/py-evts.c \
 	python/py-exitedevent.c \
+	python/py-finishbreakpoint.c \
 	python/py-frame.c \
 	python/py-function.c \
 	python/py-inferior.c \
@@ -2062,6 +2064,10 @@ py-exitedevent.o: $(srcdir)/python/py-exitedevent.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-exitedevent.c
 	$(POSTCOMPILE)
 
+py-finishbreakpoint.o: $(srcdir)/python/py-finishbreakpoint.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-finishbreakpoint.c
+	$(POSTCOMPILE)
+
 py-frame.o: $(srcdir)/python/py-frame.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-frame.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..1b8c612 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);
 }
 \f
-/* 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,18 @@ 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..3cf1cd5 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -54,6 +54,7 @@
 #include "inline-frame.h"
 #include "jit.h"
 #include "tracepoint.h"
+#include "python/python.h"
 
 /* Prototypes for local functions */
 
@@ -5826,7 +5827,8 @@ 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 (gdbpy_is_stopped_at_finish_bp (inferior_thread ()->control.stop_bpstat)
+      || 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..f9a3a8f 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -31,52 +31,18 @@
 #include "arch-utils.h"
 #include "language.h"
 
-static PyTypeObject breakpoint_object_type;
+PyTypeObject breakpoint_object_type;
 
 /* 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
 {
@@ -971,7 +937,7 @@ static PyMethodDef breakpoint_object_methods[] =
   { NULL } /* Sentinel.  */
 };
 
-static PyTypeObject breakpoint_object_type =
+PyTypeObject breakpoint_object_type =
 {
   PyObject_HEAD_INIT (NULL)
   0,				  /*ob_size*/
diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
new file mode 100644
index 0000000..f69928e
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,447 @@
+/* Python interface to finish breakpoints
+
+   Copyright (C) 2011 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   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 "exceptions.h"
+#include "python-internal.h"
+#include "breakpoint.h"
+#include "frame.h"
+#include "gdbthread.h"
+#include "arch-utils.h"
+#include "language.h"
+#include "observer.h"
+#include "inferior.h"
+
+static PyTypeObject finish_breakpoint_object_type;
+
+/* Function that is called when a Python finish bp is found out of scope.  */
+static char * const outofscope_func = "out_of_scope";
+
+/* struct implementing the gdb.FinishBreakpoint object by extending
+   the gdb.Breakpoint class.  */
+struct finish_breakpoint_object
+{
+  /* gdb.Breakpoint base class.  */
+  struct breakpoint_object py_bp;
+  /* Flag indicating that the BP is out of the callstack and Python callback
+     has been triggered.  */
+  int out_of_scope_notif;
+  /* The function finished by this breakpoint.  */
+  struct symbol *function;
+};
+
+/* Python function to set the 'out_of_scope_notif' attribute of
+   FinishBreakpoint.  */
+
+static int
+bpfinishpy_set_outofscope_notif (PyObject *self, PyObject *newvalue,
+                                 void *closure)
+{
+  struct finish_breakpoint_object *self_finishbp =
+      (struct finish_breakpoint_object *) self;
+  int cmp;
+
+  BPPY_SET_REQUIRE_VALID (&self_finishbp->py_bp);
+
+  if (newvalue == NULL)
+    {
+      PyErr_SetString (PyExc_TypeError,
+                           _("Cannot delete `out_of_scope_notif' attribute."));
+      return -1;
+    }
+  else if (!PyBool_Check (newvalue))
+    {
+      PyErr_SetString (PyExc_TypeError,
+                    _("The value of `out_of_scope_notif' must be a boolean."));
+      return -1;
+    }
+
+  cmp = PyObject_IsTrue (newvalue);
+  if (cmp < 0)
+    return -1;
+  else
+    self_finishbp->out_of_scope_notif = cmp;
+
+  return 0;
+}
+
+/* Python function to update and get the 'out_of_scope_notif'
+   attribute of FinishBreakpoint.  */
+
+static PyObject *
+bpfinishpy_get_outofscope_notif (PyObject *self, void *closure)
+{
+  struct finish_breakpoint_object *self_finishbp =
+      (struct finish_breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
+
+  if (self_finishbp->out_of_scope_notif)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+/* Python function to get the 'return_value' attribute of
+   FinishBreakpoint.  */
+
+static PyObject *
+bpfinishpy_get_returnvalue (PyObject *self, void *closure)
+{
+  struct finish_breakpoint_object *self_finishbp =
+      (struct finish_breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
+
+  if (self_finishbp->function == NULL)
+    goto return_none;
+
+  /* Ensure that GDB is stopped at this FinishBreakpoint.  */
+  if (inferior_thread ()->control.stop_bpstat != NULL)
+    {
+      bpstat bs;
+
+      for(bs = inferior_thread ()->control.stop_bpstat;
+          bs; bs = bs->next)
+        {
+          struct breakpoint *bp = bs->breakpoint_at;
+          if (bp != NULL
+              && (PyObject *) bp->py_bp_object == self)
+            {
+              struct type *v_type;
+
+              v_type = TYPE_TARGET_TYPE (SYMBOL_TYPE
+                  (self_finishbp->function));
+              if (!v_type)
+                internal_error (__FILE__, __LINE__,
+                 _("bpfinishpy_get_returnvalue: function has no target type"));
+
+              if (TYPE_CODE (v_type) != TYPE_CODE_VOID)
+                {
+                  struct value *ret = get_return_value
+                      (SYMBOL_TYPE (self_finishbp->function), v_type);
+                  PyObject *return_value = value_to_value_object (ret);
+
+                  Py_INCREF (return_value);
+                  return return_value;
+                }
+              else
+                  goto return_none;
+            }
+        }
+    }
+
+return_none:
+  Py_RETURN_NONE;
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of 
+   the current callstack. If BP_OBJ has the attribute OUT_OF_SCOPE_NOTIF
+   and its value is FALSE, trigger the method OUT_OF_SCOPE and set the flag
+   to TRUE.  */
+
+static void
+gdbpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
+{
+  breakpoint_object *bp_obj = (breakpoint_object *) bpfinish_obj;
+  PyObject *py_obj = (PyObject *) bp_obj ;
+  
+  bpfinish_obj->out_of_scope_notif = 0;
+
+  if (PyObject_HasAttrString (py_obj, outofscope_func))
+    {
+      struct gdbarch *garch =  bp_obj->bp->gdbarch ?  
+          bp_obj->bp->gdbarch : get_current_arch ();
+      struct cleanup *cleanup = ensure_python_env (garch, current_language);
+      
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+      
+      do_cleanups (cleanup);
+    }
+}
+
+/* Python function to check if the FinishBreakpoint SELF is not anymore
+   in the callstack. Triggers self->out_of_scope  */
+static PyObject *
+bpfinishpy_check_scope (PyObject *self, PyObject *args)
+{
+  struct finish_breakpoint_object *self_finishbp =
+      (struct finish_breakpoint_object *) self;
+
+  if (self_finishbp->out_of_scope_notif
+      && frame_find_by_id(self_finishbp->py_bp.bp->frame_id) == NULL)
+    {
+      gdbpy_out_of_scope (self_finishbp);
+
+      Py_RETURN_TRUE;
+    }
+  Py_RETURN_FALSE;
+}
+
+/* Python function to create a new breakpoint.  */
+
+static int
+bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+  static char *keywords[] = { "frame", "internal", NULL };
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  struct finish_breakpoint_object *self_bpfinish =
+      (struct finish_breakpoint_object *) self;
+  int type = bp_breakpoint;
+  PyObject *frame_obj = NULL;
+  struct frame_info *frame, *prev_frame;
+  struct frame_id frame_id;
+  struct symtabs_and_lines sals;
+  struct symtab_and_line sal;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR pc ;
+  volatile struct gdb_exception except;
+
+  if (! PyArg_ParseTupleAndKeywords (args, kwargs, "O|O", keywords,
+                                     &frame_obj, &internal))
+    return -1;
+
+  if (!frame_obj)
+    goto invalid_frame;
+  
+  frame = frame_object_to_frame_info (frame_obj);
+  if (frame == NULL)
+    goto invalid_frame;
+  
+  prev_frame = get_prev_frame(frame);
+  if (prev_frame == 0)
+    {
+      PyErr_SetString (PyExc_ValueError, 
+           _("\"FinishBreakpoint\" not meaningful in the outermost frame."));
+      return -1;
+    }
+  
+  frame_id = get_frame_id (prev_frame);
+  if (frame_id_eq(frame_id, null_frame_id))
+    goto invalid_frame;
+  
+  pc = get_frame_pc (prev_frame);
+
+  sal = find_pc_line (pc, 0);
+  sal.pc = pc;
+  sals.sals = &sal;
+  sals.nelts = 1;
+  
+  /* Find the function we will return from.  */
+  self_bpfinish->function = find_pc_function (get_frame_pc (frame));
+
+  if (internal)
+    {
+      internal_bp = PyObject_IsTrue (internal);
+      if (internal_bp == -1) 
+        {
+          PyErr_SetString (PyExc_ValueError, 
+                           _("The value of `internal' must be a boolean."));
+          return -1;
+        }
+    }
+
+  bppy_pending_object = self_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      create_breakpoint_sal (python_gdbarch, sals, NULL, NULL,
+                             bp_breakpoint, disp_donttouch, -1,
+                             0, 0, NULL, 0, 1, internal_bp, 0) ; 
+    }
+  if (except.reason < 0)
+    {
+      PyErr_Format (except.reason == RETURN_QUIT
+                    ? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
+                    "%s", except.message);
+      return -1;
+    }
+  BPPY_SET_REQUIRE_VALID (self_bp);
+  
+  self_bp->bp->frame_id = frame_id;
+  
+  self_bpfinish->out_of_scope_notif = 1;
+  
+  return 0;
+  
+invalid_frame:
+  PyErr_SetString (PyExc_ValueError, 
+                   _("Invalid ID for the `frame' object."));
+  return -1;
+}
+
+/* Returns 1 if OBJ is not NULL and references a FinishBreakpoint object.  */
+
+static int
+bpfinishpy_is_finish_bp (PyObject *obj)
+{
+  return obj != NULL
+         && PyObject_TypeCheck (obj, &finish_breakpoint_object_type) ;
+}
+
+/* Returns 1 if STOP_BPSTAT contains a FinishBreakpoint, 0 if not.  */
+
+int
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  bpstat bs;
+
+  for (bs = stop_bpstat; bs; bs = bs->next)
+    {
+      if (bs->breakpoint_at
+          && bpfinishpy_is_finish_bp((PyObject *)
+        		                     bs->breakpoint_at->py_bp_object))
+        return 1;
+    }
+
+  return 0;
+}
+
+/* Callback for `bpfinishpy_detect_out_scope'.  Triggers Python's
+   `B->out_of_scope' function if B is a FinishBreakpoint out of its
+    scope or turn of out_of_scope notification if B has been hit.  */
+
+static int
+bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
+{
+  struct breakpoint *bp_stopped = (struct breakpoint *) args;
+  PyObject *py_bp = (PyObject *) b->py_bp_object;
+  struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
+  struct cleanup *cleanup = ensure_python_env (garch, current_language);
+  
+  /* Trigger out_of_scope if this is a FinishBreakpoint its frame is not in the
+     current callstack and the notification has not been sent yet.  */
+  if (bpfinishpy_is_finish_bp (py_bp))
+    {
+      struct finish_breakpoint_object *finish_bp =
+          (struct finish_breakpoint_object *) py_bp;
+
+      if (b == bp_stopped)
+        finish_bp->out_of_scope_notif = 0;
+      else if (frame_find_by_id(b->frame_id) == NULL
+           && finish_bp->out_of_scope_notif)
+        {
+          gdbpy_out_of_scope (finish_bp);
+        }
+    }
+  
+  do_cleanups (cleanup);
+  
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run out
+   out of the scope of any FinishBreakpoint before it has been hit.  */
+
+static void
+bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
+{
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
+                            bs == NULL ? NULL : bs->breakpoint_at);
+}
+
+/* Attached to `exit' notifications, triggers all the necessary out of
+   scope notifications.  */
+
+static void
+bpfinishpy_handle_exit (struct inferior *inf)
+{
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
+}
+
+/* Initialize the Python finish breakpoint code.  */
+
+void
+gdbpy_initialize_finishbreakpoints (void)
+{
+  if (PyType_Ready (&finish_breakpoint_object_type) < 0)
+      return;
+  
+  Py_INCREF (&finish_breakpoint_object_type);
+  PyModule_AddObject (gdb_module, "FinishBreakpoint",
+                      (PyObject *) &finish_breakpoint_object_type);
+    
+  observer_attach_normal_stop (bpfinishpy_handle_stop);
+  observer_attach_inferior_exit(bpfinishpy_handle_exit);
+}
+
+static PyMethodDef finish_breakpoint_object_methods[] = {
+  { "check_scope", bpfinishpy_check_scope, METH_NOARGS,
+    "check_scope () -> Boolean.\n\
+Return true if out_of_scope() has been triggered, false if not." },
+  {NULL}  /* Sentinel */
+};
+
+static PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "out_of_scope_notif", bpfinishpy_get_outofscope_notif, bpfinishpy_set_outofscope_notif,
+    "Boolean telling whether the breakpoint is still within the scope \
+of the current callstack.", NULL },
+  { "return_value", bpfinishpy_get_returnvalue, NULL,
+  "gdb.Value object representing the return value, if any. \
+None otherwise.", NULL },
+    { NULL }  /* Sentinel.  */
+};
+
+static PyTypeObject finish_breakpoint_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,                              /*ob_size*/
+  "gdb.FinishBreakpoint",         /*tp_name*/
+  sizeof (struct finish_breakpoint_object),  /*tp_basicsize*/
+  0,                              /*tp_itemsize*/
+  0,                              /*tp_dealloc*/
+  0,                              /*tp_print*/
+  0,                              /*tp_getattr*/
+  0,                              /*tp_setattr*/
+  0,                              /*tp_compare*/
+  0,                              /*tp_repr*/
+  0,                              /*tp_as_number*/
+  0,                              /*tp_as_sequence*/
+  0,                              /*tp_as_mapping*/
+  0,                              /*tp_hash */
+  0,                              /*tp_call*/
+  0,                              /*tp_str*/
+  0,                              /*tp_getattro*/
+  0,                              /*tp_setattro */
+  0,                              /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,  /*tp_flags*/
+  "GDB finish breakpoint object", /* tp_doc */
+  0,                              /* tp_traverse */
+  0,                              /* tp_clear */
+  0,                              /* tp_richcompare */
+  0,                              /* tp_weaklistoffset */
+  0,                              /* tp_iter */
+  0,                              /* tp_iternext */
+  finish_breakpoint_object_methods,  /* tp_methods */
+  0,                              /* tp_members */
+  finish_breakpoint_object_getset,/* tp_getset */
+  &breakpoint_object_type,        /* tp_base */
+  0,                              /* tp_dict */
+  0,                              /* tp_descr_get */
+  0,                              /* tp_descr_set */
+  0,                              /* tp_dictoffset */
+  bpfinishpy_init,                /* tp_init */
+  0,                              /* tp_alloc */
+  0                               /* 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..31ec1b8 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -114,9 +114,47 @@ 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;
+
+typedef 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;
+} breakpoint_object;
+
+/* 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)
+
+
+/* Variables used to pass information between the Breakpoint
+   constructor and the breakpoint-created hook function.  */
+extern breakpoint_object *bppy_pending_object;
 
-/* Defined in py-breakpoint.c */
-typedef struct breakpoint_object breakpoint_object;
 
 typedef struct
 {
@@ -161,6 +199,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 +233,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..97007b3 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -926,6 +926,11 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj)
 		    "scripting is not supported."));
 }
 
+int
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  return 0;
+}
 #endif /* HAVE_PYTHON */
 
 \f
@@ -1060,6 +1065,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/python/python.h b/gdb/python/python.h
index ce0eb35..aa2d096 100644
--- a/gdb/python/python.h
+++ b/gdb/python/python.h
@@ -21,6 +21,7 @@
 #define GDB_PYTHON_H
 
 #include "value.h"
+#include "breakpoint.h"
 
 struct breakpoint_object;
 
@@ -47,4 +48,5 @@ int gdbpy_should_stop (struct breakpoint_object *bp_obj);
 
 int gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj);
 
+int gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat);
 #endif /* GDB_PYTHON_H */
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"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc
@@ -0,0 +1,59 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2011 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   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 <iostream>
+
+void
+throw_exception_1 (int e)
+{
+  throw new int (e);
+}
+
+void
+throw_exception (int e)
+{
+  throw_exception_1 (e);
+}
+
+int
+main (void)
+{
+  int i;
+  try
+    {
+      throw_exception_1 (10);
+    }
+  catch (const int *e)
+    {
+        std::cerr << "Exception #" << *e << std::endl;
+    }
+  i += 1; /* Break after exception 1.  */
+
+  try
+    {
+      throw_exception (10);
+    }
+  catch (const int *e)
+    {
+        std::cerr << "Exception #" << *e << std::endl;
+    }
+  i += 1; /* Break after exception 2.  */
+
+  return i;
+}
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp
new file mode 100644
index 0000000..e74023d
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp
@@ -0,0 +1,59 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests the mechanism
+# exposing values to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+load_lib gdb-python.exp
+
+set testfile "py-finish-breakpoint-cc"
+set srcfile ${testfile}.cc
+set binfile ${objdir}/${subdir}/${testfile}
+set pyfile  ${srcdir}/${subdir}/${testfile}.py
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
+    untested "Couldn't compile ${srcfile}"
+    return -1
+}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+
+gdb_test "break [gdb_get_line_number "Break after exception 1."]" "Breakpoint.* at .*" \
+         "set watchdog after the exception 1"
+gdb_test "break [gdb_get_line_number "Break after exception 2."]" "Breakpoint.* at .*" \
+         "set watchdog after the exception 2"
+         
+gdb_test "python ExceptionBreakpoint()" "ExceptionBreakpoint init" "set BP before throwing the exception"
+gdb_test "python print len(gdb.breakpoints())" "4" "check number of BPs"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "4" "check finish BP removal"
+
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py
new file mode 100644
index 0000000..d0dfe2f
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py
@@ -0,0 +1,43 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests python Finish
+# Breakpoints.
+
+class ExceptionBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__(self, spec="throw_exception_1", internal=1)
+        self.silent = True
+        print "ExceptionBreakpoint init"
+        
+    def stop(self):
+        ExceptionFinishBreakpoint(gdb.newest_frame())
+        return False
+
+class ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        self.silent = True;
+ 
+    def stop(self):
+	print "stopped at ExceptionFinishBreakpoint"
+	gdb.post_event(self.delete)
+        return True 
+    
+    def out_of_scope(self):
+        print "exception did not finish ..."
+
+
+print "Python script imported"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..32b8b38
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,82 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2011 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   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 <setjmp.h>
+
+int increase_1(int *a)
+{
+  *a += 1;
+  return -5;
+}
+
+void increase(int *a)
+{
+  increase_1(a);
+}
+
+int
+test_1(int i, int j)
+{
+  return i == j;
+}
+
+int
+test(int i, int j)
+{
+  return test_1(i, j);
+}
+
+int
+call_longjmp_1 (jmp_buf *buf)
+{
+  longjmp (*buf, 1);
+}
+
+int
+call_longjmp (jmp_buf *buf)
+{
+  call_longjmp_1 (buf);
+}
+
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  i = 0 ;
+  /* Break at increase. */
+  increase (&i) ;
+  increase (&i) ;
+  increase (&i) ;
+
+  for (i = 0; i < 10; i++)
+    {
+      j += 1; /* Condition Break. */
+    }
+
+  if (setjmp (env) == 0) /* longjmp caught */
+    {
+      call_longjmp (&env);
+    }
+  else
+	  j += 1; /* after longjmp. */
+
+  return j; /* Break at end. */
+}
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
new file mode 100644
index 0000000..65eebc9
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,183 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests the mechanism
+# exposing values to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+load_lib gdb-python.exp
+
+set testfile "py-finish-breakpoint"
+set srcfile ${testfile}.c
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+set remote_python_file [remote_download host ${srcdir}/${subdir}/${testfile}.py]
+
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "source $remote_python_file" ".*Python script imported.*" \
+         "import python scripts"
+
+gdb_test "python ljmpBP = LongjmpFinishBreakpoint(gdb.newest_frame())" \
+         "LongjmpFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" ".*Longjmp didn't finish.*" "check FinishBP out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy frame)
+#
+
+clean_restart ${testfile}
+
+set cond_line [gdb_get_line_number "Condition Break."]
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+gdb_test "source $remote_python_file" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_test "break ${cond_line} if test_1(i,8)" ".*Breakpoint .* at .*" "set conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" "set BP in condition"
+
+
+set msg "check FinishBreakpoint don't stop in GDB Dummy Frame"
+gdb_test_multiple "continue" $msg {
+	-re ".*test don't stop 2.*test stop.*test don't stop 4.*" {
+		pass $msg
+	}
+	-re ".*test don't stop 2.*test stop.*$gdb_prompt" {
+		fail $msg
+	}
+}
+
+gdb_test "print i" "8" "check stopped location"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in normal frame)
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" ".*Python script imported.*" \
+         "import python scripts"
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+gdb_test "break ${cond_line} if test(i,8)" ".*Breakpoint .* at .*" "set conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" "set BP in condition"
+
+gdb_test "continue" ".*test don't stop 1.*test don't stop 2.*test stop.*Error in testing breakpoint condition.*The program being debugged stopped while in a function called from GDB.*" \
+         "stop in condition function"
+         
+setup_kfail "normal_stop_notification not triggered during condition evaluation" *-*-*
+gdb_test "python print gdb.breakpoints()\[2\].out_of_scope_notif" ".*False.*" "check out_of_scope notification disabled"
+gdb_test_no_output "python gdb.breakpoints()\[2\].out_of_scope_notif = False" "reestablish correct value"
+
+gdb_test "continue" "Continuing.*" "finish condition evaluation"
+gdb_test "continue" "Breakpoint.*" "stop at conditional breakpoint"
+gdb_test "print i" "8" "check stopped location"
+
+#
+# Test FinishBreakpoint in normal conditions
+#
+
+clean_restart ${testfile}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+gdb_test_no_output "set confirm off" "disable confirmation"
+gdb_test "source $remote_python_file" ".*Python script imported.*" \
+         "import python scripts"
+gdb_test "python MyBreakpoint(\"increase_1\")" ".*Breakpoint 2.*" \
+         "create Python function breakpoint"
+gdb_test "continue" ".*Arrived at MyBreakpoint with 0.*" "check MyBreakpoint hit"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval (\"a\"), gdb.selected_frame ())" \
+         ".*Breakpoint 3.*" "set FinishBreakpoint"
+gdb_test "python print finishbp.out_of_scope_notif" ".*True.*" \
+         "check out_of_scope_notif at init"
+gdb_test "python print finishbp.return_value" ".*None.*" \
+         "check return_value at init"
+
+# check normal bp hit
+
+gdb_test "continue" ".*MyFinishBreakpoint stop with.*#0.*increase.*" \
+         "check MyFinishBreakpoint hit"
+gdb_test "python print finishbp.return_value" ".*-5.*" "check return_value"
+gdb_test "python print finishbp.out_of_scope_notif" ".*False.*" \
+         "check out_of_scope_notif disabled after hit"
+gdb_test "finish" ".*main.*" "return to main()"
+gdb_test "python print finishbp.return_value" ".*None.*" "check return_value"
+
+# check forced return / check out of scpop
+gdb_test_no_output "python finishbp.out_of_scope_notif = True" \
+         "re-enable out_of_scope_notif"
+
+gdb_test "continue" ".*Arrived at MyBreakpoint with.*" "check MyBreakpoint second hit"
+gdb_test "up" ".*increase_1.*" "go one frame up"
+gdb_test_no_output "return" "return from the frame"
+gdb_test "python print finishbp.check_scope()" ".*MyFinishBreakpoint out of scope.*True.*" \
+         "go one frame up"
+
+# check forced return / automatic notification
+
+gdb_test_no_output "python finishbp.out_of_scope_notif = True" \
+         "re-enable out_of_scope_notif"
+
+gdb_test "continue" ".*Arrived at MyBreakpoint with.*" "check MyBreakpoint third hit"
+gdb_test "up" ".*increase_1.*" "go one frame up"
+gdb_test_no_output "return" "return from the frame"
+gdb_test "next" ".*MyFinishBreakpoint out of scope.*" "check Finish breakpoint discard"
+gdb_test "python print finishbp.out_of_scope_notif" ".*False.*" "check out_of_scope_notif"
+
+# check FinishBreakpoint in main
+
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         ".*ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \
+         "check FinishBP not allowed in main"
+         
+         
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.py b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
new file mode 100644
index 0000000..f014cc6
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,85 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests python Finish
+# Breakpoints.
+
+class MyBreakpoint(gdb.Breakpoint):		
+	def stop(self):
+		val = gdb.parse_and_eval ("a")
+		print "Arrived at MyBreakpoint with %d" % int(val.dereference())
+		return True
+		
+class MyFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		super (MyFinishBreakpoint, self).__init__ (frame)
+		print "MyFinishBreakpoint init"
+		self.val = val
+		
+	def stop(self):
+		print "MyFinishBreakpoint stop with %d" % int(self.val.dereference())
+		gdb.execute("where 1")
+		return True
+	
+	def out_of_scope(self):
+		print "MyFinishBreakpoint out of scope..."
+
+test_finish_bp = None 
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__(self, spec="test_1", internal=1)
+        self.silent = True
+        self.finish = None
+        print "TestBreakpoint init"
+        
+    def stop(self):
+    	global test_finish_bp
+        if (self.finish == None):
+            self.finish = TestFinishBreakpoint(gdb.newest_frame())
+            test_finish_bp = self.finish
+        return False
+
+
+class TestFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        self.count = 0
+        
+    def stop(self):
+        self.count += 1
+        if (self.count == 3):
+            print "test stop ..."
+            return True
+        else:
+            print "test don't stop %d" % self.count
+            return False 
+        
+    
+    def out_of_scope(self):
+        print "test didn't finish ..."
+		 
+class LongjmpFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+		print "LongjmpFinishBreakpoint init"
+		
+	def stop(self):
+		print "Stopped at LongjmpFinishBreakpoint"
+        
+    
+	def out_of_scope(self):
+		print "Longjmp didn't finish ..."
+
+print "Python script imported"
-- 
1.7.4.4


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC] Python Finish Breakpoints
  2011-05-18  8:58         ` Kevin Pouget
@ 2011-05-18 10:16           ` Phil Muldoon
  0 siblings, 0 replies; 13+ messages in thread
From: Phil Muldoon @ 2011-05-18 10:16 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: Doug Evans, gdb

Kevin Pouget <kevin.pouget@gmail.com> writes:

> Hello,
>
> I've included with this mail a complete patch build agains the current
> HEAD, and checked that there was no regression in the testsuite

...

>> Does this testsuite match your expectations, or would you like it to
>> be more advanced? (I'll need more hints in this case)

Yes thanks, it answers my questions.  That for making the effort.
However the maintainers may have other comments, so you should wait on
their review.

Cheers

Phil


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2011-05-18 10:16 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-09 14:11 [RFC] Python Finish Breakpoints Kevin Pouget
2011-05-09 14:31 ` Kevin Pouget
     [not found]   ` <BANLkTikVdqbMqjguTV8ct0TWiBDhHGYtLg@mail.gmail.com>
2011-05-11  7:44     ` Kevin Pouget
2011-05-11 10:31       ` Phil Muldoon
2011-05-11 11:29         ` Kevin Pouget
2011-05-12 10:50         ` Phil Muldoon
2011-05-12 11:29           ` Kevin Pouget
2011-05-12 19:00 ` Doug Evans
2011-05-13  7:51   ` Phil Muldoon
     [not found]   ` <BANLkTikt2hEUcXkGVH44NaUcwiF1SGdMaw@mail.gmail.com>
2011-05-13  9:04     ` Kevin Pouget
2011-05-16 11:24       ` Kevin Pouget
2011-05-18  8:58         ` Kevin Pouget
2011-05-18 10:16           ` Phil Muldoon

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox