public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* 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; 74+ 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] 74+ messages in thread

* Re: [RFC] Python Finish Breakpoints
  2011-05-11  7:44     ` [RFC] Python Finish Breakpoints 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; 74+ 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] 74+ 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:38           ` Kevin Pouget
  2011-05-12 10:50         ` Phil Muldoon
  1 sibling, 1 reply; 74+ 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] 74+ messages in thread

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

Hello,

here is an updated version of the patch which address all your remarks

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

I changed it to:

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

which only verifies the IF condition when of the of breakpoint of the
stop_bpstat list is a FinishBreakpoint (the function return 0 when
Python is not enabled)

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

`print_return_value' is declared as static, so I added
get_return_value at the end of the function prototypes

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

I removed this  py-breakpoint.h header and moved the appropriate lines
to python-internal.h

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

it might be more intuitive now, the flag is named `out_of_scope_notif'
and indicates if the `out_of_scope' callback should be triggered when
GDB notices that the BP is not in the callstack. The attribute is
read/writable, so that the user can disable and re-enable it when
necessary. It is set to FALSE when the breakpoint is hit and  when the
callback is triggered.

the method check_scope() will force the checking of the scope (normal
checking occurs during the `inferior_stop' observer notification) and
return TRUE is the callback was triggered (FALSE otherwise)


I hope that this implementation will look better to you, and wait for
a maintainer review before preparing the documentation/changelog


thanks,

Kevin

--

From 492216ccd8250de3f13038b67b24728038f1e9b7 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                                      |   20 +-
 gdb/inferior.h                                    |    3 +
 gdb/infrun.c                                      |    4 +-
 gdb/python/py-breakpoint.c                        |   40 +--
 gdb/python/py-finishbreakpoint.c                  |  436 +++++++++++++++++++++
 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/testsuite/gdb.python/py-finish-breakpoint.c   |   42 ++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp |   92 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py  |   39 ++
 16 files changed, 721 insertions(+), 64 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 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..8c1045b
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,436 @@
+/* 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 (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);
+}
+
+/* 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 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.c
b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..726a5ee
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,42 @@
+/* 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/>.
+*/
+
+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) ;
+  increase (&i) ;
+
+  return 0; /* 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..ee82374
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,92 @@
+# 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
+set remote_python_file [remote_download host
${srcdir}/${subdir}/${testfile}.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_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..bdb142b
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,39 @@
+# 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..."
+		
+print "Python script imported"
-- 
1.7.4.4

^ permalink raw reply	[flat|nested] 74+ 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; 74+ 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] 74+ 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; 74+ 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] 74+ messages in thread

* Re: [RFC] Python Finish Breakpoints
       [not found]         ` <BANLkTimmYEmvKT_984jYEVZnA5RGFpEgNw@mail.gmail.com>
@ 2011-05-19 16:21           ` Tom Tromey
  2011-05-24 12:51             ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Tom Tromey @ 2011-05-19 16:21 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches

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

Moving to gdb-patches.

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

Thanks.

I'm sorry about the delay in my reply.  I'm going to try to prioritize
patch review and email a bit higher in the future.

I think this functionality is very good.  I am not sure about some
aspects of the implementation, though, and I have some style nits to
pick.

Kevin> -static void
Kevin> +void
Kevin>  create_breakpoint_sal (struct gdbarch *gdbarch,
Kevin>  		       struct symtabs_and_lines sals, char *addr_string,
Kevin>  		       char *cond_string,

What is the rationale for using this function in particular?

It seems to me that it would probably be better to make an explicit_pc=1
SAL using the unwound PC.  Then there would be no reason to avoid the
currently exported API.

Kevin> +  struct value *value = get_return_value(func_type, value_type);

Missing space before the "(".  This appears multiple places in the
patch.

Kevin> -  if (inferior_thread ()->control.proceed_to_finish)
Kevin> +  if (gdbpy_is_stopped_at_finish_bp (inferior_thread ()->control.stop_bpstat)
Kevin> +      || inferior_thread ()->control.proceed_to_finish)

gdbpy_is_stopped_at_finish_bp is not safe to call here -- it assumes the 
GIL has been acquired, which it has not.  I would rather it not be changed
to acquire the GIL, however.  I think one of two other approaches would
be preferable.

One way you could handle this is to add a new constant to enum bptype.
This is likely to be pretty invasive.

Another way would be to add a flag to the struct breakpoint itself.

Yet another way would be a new breakpoint_ops method.

This is related, in a way, to the out-of-scope handling.  Right now the
patch tries to reimplement the existing breakpoint re-setting logic in
py-finishbreakpoint.c, via observers.  I think it would be better to
have this be done automatically by the existing code in breakpoint.c,
perhaps adding some additional python-visible notification step.

Kevin> +static char * const outofscope_func = "out_of_scope";

"const char *".

Kevin> +  /* The function finished by this breakpoint.  */
Kevin> +  struct symbol *function;

If you want to store a pointer to a symbol, then you have to account for
the case where the objfile is destroyed.  Otherwise you can end up with
a dangling pointer.

Alternatively, perhaps you could have this refer to a Symbol object; but
then you have to be careful to check it for validity before using it.

Actually, it seems that the symbol is only used to get the function's
return type.  You might as well just compute that up front and store a
Type.

Kevin> +/* Python function to set the 'out_of_scope_notif' attribute of
Kevin> +   FinishBreakpoint.  */
Kevin> +
Kevin> +static int
Kevin> +bpfinishpy_set_outofscope_notif (PyObject *self, PyObject *newvalue,
Kevin> +                                 void *closure)

I don't understand the point of this function.

I think documentation would help.

Kevin> +/* Python function to get the 'return_value' attribute of
Kevin> +   FinishBreakpoint.  */
Kevin> +
Kevin> +static PyObject *
Kevin> +bpfinishpy_get_returnvalue (PyObject *self, void *closure)
Kevin> +{
Kevin> +  struct finish_breakpoint_object *self_finishbp =
Kevin> +      (struct finish_breakpoint_object *) self;
Kevin> +
Kevin> +  BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
Kevin> +
Kevin> +  if (self_finishbp->function == NULL)
Kevin> +    goto return_none;
Kevin> +
Kevin> +  /* Ensure that GDB is stopped at this FinishBreakpoint.  */
Kevin> +  if (inferior_thread ()->control.stop_bpstat != NULL)
Kevin> +    {
Kevin> +      bpstat bs;
Kevin> +
Kevin> +      for(bs = inferior_thread ()->control.stop_bpstat;
Kevin> +          bs; bs = bs->next)

I am not an expert here, but I think it is probably no good to rely on
this state remaining live.

I think it would be better to simply have a stop at one of these
breakpoints compute and cache the Value object immediately.

Kevin> +static void
Kevin> +gdbpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
Kevin> +{
Kevin> +  breakpoint_object *bp_obj = (breakpoint_object *) bpfinish_obj;
Kevin> +  PyObject *py_obj = (PyObject *) bp_obj ;
Kevin> +  
Kevin> +  bpfinish_obj->out_of_scope_notif = 0;
Kevin> +
Kevin> +  if (PyObject_HasAttrString (py_obj, outofscope_func))
Kevin> +    {
Kevin> +      struct gdbarch *garch =  bp_obj->bp->gdbarch ?  
Kevin> +          bp_obj->bp->gdbarch : get_current_arch ();
Kevin> +      struct cleanup *cleanup = ensure_python_env (garch, current_language);

You can't call any Python functions without the GIL.
This applies to PyObject_HasAttrString here.

In this case, though, gdbpy_out_of_scope is called by one function that
already has the GIL.  So, I think the acquisition should be pushed into
its other caller.

Kevin> +  pc = get_frame_pc (prev_frame);

This can throw an exception.  So, it needs to be wrapped in a TRY_CATCH.
This may apply to some other GDB functions called by the "Python-facing"
code, I did not check them all.

Kevin> +static PyMethodDef finish_breakpoint_object_methods[] = {
Kevin> +  { "check_scope", bpfinishpy_check_scope, METH_NOARGS,
Kevin> +    "check_scope () -> Boolean.\n\
Kevin> +Return true if out_of_scope() has been triggered, false if not." },

How is this useful?
And, why a method instead of an attribute?

Kevin> +static PyGetSetDef finish_breakpoint_object_getset[] = {
Kevin> +  { "out_of_scope_notif", bpfinishpy_get_outofscope_notif, bpfinishpy_set_outofscope_notif,

I don't like the name "out_of_scope_notif", particularly if this is an
apt description of its meaning:

Kevin> +    "Boolean telling whether the breakpoint is still within the scope \
Kevin> +of the current callstack.", NULL },


Kevin> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp

Funny file name.

Kevin> +gdb_test "python ExceptionBreakpoint()" "ExceptionBreakpoint init" "set BP before throwing the exception"
Kevin> +gdb_test "python print len(gdb.breakpoints())" "4" "check number of BPs"
Kevin> +gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
Kevin> +gdb_test "python print len(gdb.breakpoints())" "4" "check finish BP removal"

I don't think this tests the most important case -- where an exception
is thrown, invalidating the return breakpoint.

That is, put a breakpoint in throw_exception_1, continue to there, then
set the breakpoint, then continue again.

I didn't examine the longjmp test but the same idea applies there.

Other cases to consider are -

* inferior exec
* inferior exit
* explicit inferior function call
* "return" command

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2011-05-19 16:21           ` Tom Tromey
@ 2011-05-24 12:51             ` Kevin Pouget
  2011-05-27 20:30               ` Tom Tromey
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-05-24 12:51 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

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

Hello,

thanks for you comments, I replied inline:


> Kevin> -static void
> Kevin> +void
> Kevin>  create_breakpoint_sal (struct gdbarch *gdbarch,
> Kevin>                         struct symtabs_and_lines sals, char *addr_string,
> Kevin>                         char *cond_string,
>
> What is the rationale for using this function in particular?
>
> It seems to me that it would probably be better to make an explicit_pc=1
> SAL using the unwound PC.  Then there would be no reason to avoid the
> currently exported API.

There isn't. I just followed the "finish" command implementation and
understood that this function looked like what I was looking for!
I re-wrote this part of the code to use the existing API

> Kevin> -  if (inferior_thread ()->control.proceed_to_finish)
> Kevin> +  if (gdbpy_is_stopped_at_finish_bp (inferior_thread ()->control.stop_bpstat)
> Kevin> +      || inferior_thread ()->control.proceed_to_finish)
>
> gdbpy_is_stopped_at_finish_bp is not safe to call here -- it assumes the
> GIL has been acquired, which it has not.  I would rather it not be changed
> to acquire the GIL, however.  I think one of two other approaches would
> be preferable.
>
> One way you could handle this is to add a new constant to enum bptype.
> This is likely to be pretty invasive.
>
> Another way would be to add a flag to the struct breakpoint itself.
>
> Yet another way would be a new breakpoint_ops method.

you're right; I chose the second way,

breakpoint.h:
enum py_bp_type
  {
    py_bp_none,         /* No Python object.  */
    py_bp_standard,     /* Ordinary breakpoint object.  */
    py_bp_finish        /* FinishBreakpoint object.  */
  };

    /* Type of the Python Breakpoint object stored in `py_bp_object'.  */
    enum py_bp_type py_bp_type;

py-breakpoint.c:
gdbpy_is_stopped_at_bp_type (bpstat stop_bpstat, enum py_bp_type type)

so that it doesn't require GIL state handling

> This is related, in a way, to the out-of-scope handling.  Right now the
> patch tries to reimplement the existing breakpoint re-setting logic in
> py-finishbreakpoint.c, via observers.  I think it would be better to
> have this be done automatically by the existing code in breakpoint.c,
> perhaps adding some additional python-visible notification step.

I'm not sure what you're talking about; I re-explain and document this
aspect at the bottom of the mail, you'll tell me if this is still
relevant

> Kevin> +static char * const outofscope_func = "out_of_scope";
>
> "const char *".

I copied it from py-breakpoint.c:
> static char * const stop_func = "stop";

and I think thank we can't change it, otherwise it conflicts with
> PyObject* PyObject_CallMethod(PyObject *o, char *method, char *format, ...)

and creates a compiler warning


> Kevin> +  /* The function finished by this breakpoint.  */
> Kevin> +  struct symbol *function;
>
> If you want to store a pointer to a symbol, then you have to account for
> the case where the objfile is destroyed.  Otherwise you can end up with
> a dangling pointer.
>
> Alternatively, perhaps you could have this refer to a Symbol object; but
> then you have to be careful to check it for validity before using it.
>
> Actually, it seems that the symbol is only used to get the function's
> return type.  You might as well just compute that up front and store a
> Type.

> Kevin> +/* Python function to get the 'return_value' attribute of
> Kevin> +   FinishBreakpoint.  */
> Kevin> +
> Kevin> +static PyObject *
> Kevin> +bpfinishpy_get_returnvalue (PyObject *self, void *closure)
> Kevin> +{
> Kevin> +  struct finish_breakpoint_object *self_finishbp =
> Kevin> +      (struct finish_breakpoint_object *) self;
> Kevin> +
> Kevin> +  BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
> Kevin> +
> Kevin> +  if (self_finishbp->function == NULL)
> Kevin> +    goto return_none;
> Kevin> +
> Kevin> +  /* Ensure that GDB is stopped at this FinishBreakpoint.  */
> Kevin> +  if (inferior_thread ()->control.stop_bpstat != NULL)
> Kevin> +    {
> Kevin> +      bpstat bs;
> Kevin> +
> Kevin> +      for(bs = inferior_thread ()->control.stop_bpstat;
> Kevin> +          bs; bs = bs->next)
>
> I am not an expert here, but I think it is probably no good to rely on
> this state remaining live.
>
> I think it would be better to simply have a stop at one of these
> breakpoints compute and cache the Value object immediately.

as per your two comments, I now only store the `struct type'  of the
function and the return value,
and cache the `gdb.Value' object when GDB stops at a FinishBreakpoint.

`FinishBreakpoint.return_value' may have to populate the cache when
it's accessed from Breakpoint.stop() because there is no guarantee the
FinishBreakpoint observer will be notified first.

> Kevin> +static void
> Kevin> +gdbpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
> Kevin> +{
> Kevin> +  breakpoint_object *bp_obj = (breakpoint_object *) bpfinish_obj;
> Kevin> +  PyObject *py_obj = (PyObject *) bp_obj ;
> Kevin> +
> Kevin> +  bpfinish_obj->out_of_scope_notif = 0;
> Kevin> +
> Kevin> +  if (PyObject_HasAttrString (py_obj, outofscope_func))
> Kevin> +    {
> Kevin> +      struct gdbarch *garch =  bp_obj->bp->gdbarch ?
> Kevin> +          bp_obj->bp->gdbarch : get_current_arch ();
> Kevin> +      struct cleanup *cleanup = ensure_python_env (garch, current_language);
>
> You can't call any Python functions without the GIL.
> This applies to PyObject_HasAttrString here.
>
> In this case, though, gdbpy_out_of_scope is called by one function that
> already has the GIL.  So, I think the acquisition should be pushed into
> its other caller.

fixed

> Kevin> +  pc = get_frame_pc (prev_frame);
>
> This can throw an exception.  So, it needs to be wrapped in a TRY_CATCH.
> This may apply to some other GDB functions called by the "Python-facing"
> code, I did not check them all.

fixed

> Kevin> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp
>
> Funny file name.

funny but correct, or too funny? ;)

> Kevin> +gdb_test "python ExceptionBreakpoint()" "ExceptionBreakpoint init" "set BP before throwing the exception"
> Kevin> +gdb_test "python print len(gdb.breakpoints())" "4" "check number of BPs"
> Kevin> +gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
> Kevin> +gdb_test "python print len(gdb.breakpoints())" "4" "check finish BP removal"
>
> I don't think this tests the most important case -- where an exception
> is thrown, invalidating the return breakpoint.
>
> That is, put a breakpoint in throw_exception_1, continue to there, then
> set the breakpoint, then continue again.
>
> I didn't examine the longjmp test but the same idea applies there.
>
> Other cases to consider are -
>
> * inferior exec
> * inferior exit
> * explicit inferior function call
> * "return" command

I'll work on the tests for the next version of the patch ("return"
should already be covered)


--

so, about the `out_of_scope' stuffs,

> Kevin> +/* Python function to set the 'out_of_scope_notif' attribute of
> Kevin> +   FinishBreakpoint.  */
> Kevin> +
> Kevin> +static int
> Kevin> +bpfinishpy_set_outofscope_notif (PyObject *self, PyObject *newvalue,
> Kevin> +                                 void *closure)
>
> I don't understand the point of this function.
>
> I think documentation would help.

here is a bit of documentation about this class:

@subsubsection Finish Breakpoints

@cindex python finish breakpoints
@tindex gdb.FinishBreakpoint

A @fdb{finish breakpoint} is a breakpoint set at the return address of
a frame, based on the "finish command. @code{gdb.FinishBreakpoint} extends
@code{gdb.Breakpoint}

@defmethod FinishBreakpoint __init__ frame @r{[}internal@r{]}
Create a FinishBreakpoint at the return of the @code{gdb.Frame} object
@var{frame}. The optional @var{internal} argument allows the breakpoint
to become invisible to the user. @xref{Breakpoints In Python}, for further
details about this argument.
@end defmethod

@defop Operation {gdb.Breakpoint} out_of_scope (self)
In some circonstances (eg, @code{longjmp}, C++ exceptions, @value{GDBN}
@code{return} command, ...), a function may not properly terminate, and thus
never hit a @{code} FinishBreakpoint. When @value{GDBN} notices such a
situation, the @code{out_of_scope} function will be triggered.

This method will only be called if the @code{out_of_scope_notif} attribute
is @code{True}.

Deleting (@code{gdb.Breakpoint.delete()}) the @code{FinishBreakpoint} is
allowed in this function.

You may want to sub-class the @code{gdb.FinishBreakpoint} and implement the
@code{out_of_scope} method:

@smallexample
class MyFinishBreakpoint (gdb.FinishBreakpoint)
	def stop (self):
		print "normal finish"
		return True
	
	def out_of_scope():
		print "abnormal finish"
@end smallexample
@end defop

@defmethod FinishBreakpoint check_scope
Because @value{GDBN} currently only checks if @code{FinishBreakpoint}s ran
out of scope at normal stops, this method allows to force the verification
and trigger the @code{out_of_scope} method if necessary. It will return
@code{True} if @code{out_of_scope()} was triggered, @code{False} otherwise.
@end defmethod


@defivar FinishBreakpoint out_of_scope_notif
This attribute will be @code{True} until the @code{out_of_scope} method has
been called and @code{False} afterwards. This attribute is writeable, so out
of scope notifications can be re-enabled.
@end defivar

@defivar FinishBreakpoint return_value
When @value{GDBN} is stopped at a @code{FinishBreakpoint}, and the frame
used to build the @code{FinishBreakpoint} had debug symbols, this attribute
will contain a @code{gdb.Value} object corresponding to the return value of
the function. The value will be @code{None} if the value was not computable.
This attribute is not writable.
@end defivar


> Kevin> +static PyMethodDef finish_breakpoint_object_methods[] = {
> Kevin> +  { "check_scope", bpfinishpy_check_scope, METH_NOARGS,
> Kevin> +    "check_scope () -> Boolean.\n\
> Kevin> +Return true if out_of_scope() has been triggered, false if not." },
>
> How is this useful?
> And, why a method instead of an attribute?

as described, this is useful especially in the case of a GDB-forced
"return": normal_stop observers are not notified, so `out_of_scope()'
is not triggered. `check_scope()' allows Python scripts to force this
verification when they need. The reason why it's a method is that it
may execute `out_of_scope()', which was, from my point of view,
inconsistent with the concept of attribute.

> Kevin> +static PyGetSetDef finish_breakpoint_object_getset[] = {
> Kevin> +  { "out_of_scope_notif", bpfinishpy_get_outofscope_notif, bpfinishpy_set_outofscope_notif,
>
> I don't like the name "out_of_scope_notif", particularly if this is an
> apt description of its meaning:
> Kevin> +    "Boolean telling whether the breakpoint is still within the scope \
> Kevin> +of the current callstack.", NULL },

I've got some difficulties to find a correct name for this attribute,
which is used for two purposes:
- avoid calling `out_of_scope' every normal_stop when the breakpoint
is not anymore in the callstack
- allow the script to re-activate notification when it wants to
're-use' the FinishBreakpoint (instead of deleting it / creating a new
one)


cordially,

Kevin

[-- Attachment #2: finish_bp.txt --]
[-- Type: text/plain, Size: 54968 bytes --]

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 7093104..c1c4f06 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5814,6 +5814,7 @@ set_raw_breakpoint_without_location (struct gdbarch *gdbarch,
   b->ops = NULL;
   b->condition_not_parsed = 0;
   b->py_bp_object = NULL;
+  b->py_bp_type = py_bp_none;
   b->related_breakpoint = b;
 
   /* Add this breakpoint to the end of the chain so that a list of
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 7fa705f..a1575a5 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -164,6 +164,15 @@ enum bptype
     bp_gnu_ifunc_resolver_return,
   };
 
+/* Type of the Python object associated with a breakpoint.  */
+
+enum py_bp_type
+  {
+    py_bp_none,         /* No Python object.  */
+    py_bp_standard,     /* Ordinary breakpoint object.  */
+    py_bp_finish        /* FinishBreakpoint object.  */
+  };
+
 /* States of enablement of breakpoint.  */
 
 enum enable_state
@@ -654,6 +663,9 @@ struct breakpoint
        types are tracked by the Python scripting API.  */
     struct breakpoint_object *py_bp_object;
 
+    /* Type of the Python Breakpoint object stored in `py_bp_object'.  */
+    enum py_bp_type py_bp_type;
+
     /* Whether this watchpoint is exact (see target_exact_watchpoints).  */
     int exact;
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 584a520..27738b4 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -23513,6 +23513,70 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defivar
 
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A @fdb{finish breakpoint} is a breakpoint set at the return address of
+a frame, based on the "finish command. @code{gdb.FinishBreakpoint} extends
+@code{gdb.Breakpoint}
+
+@defmethod FinishBreakpoint __init__ frame @r{[}internal@r{]}
+Create a FinishBreakpoint at the return of the @code{gdb.Frame} object 
+@var{frame}. The optional @var{internal} argument allows the breakpoint
+to become invisible to the user. @xref{Breakpoints In Python}, for further 
+details about this argument.
+@end defmethod
+
+@defop Operation {gdb.Breakpoint} out_of_scope (self)
+In some circonstances (eg, @code{longjmp}, C++ exceptions, @value{GDBN} 
+@code{return} command, ...), a function may not properly terminate, and thus 
+never hit a @{code} FinishBreakpoint. When @value{GDBN} notices such a 
+situation, the @code{out_of_scope} function will be triggered.
+
+This method will only be called if the @code{out_of_scope_notif} attribute 
+is @code{True}.
+
+Deleting (@code{gdb.Breakpoint.delete ()}) the @code{FinishBreakpoint} is
+allowed in this function.
+
+You may want to sub-class the @code{gdb.FinishBreakpoint} and implement the
+@code{out_of_scope} method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+	def stop (self):
+		print "normal finish"
+		return True
+	
+	def out_of_scope ():
+		print "abnormal finish"
+@end smallexample 
+@end defop
+
+@defmethod FinishBreakpoint check_scope
+Because @value{GDBN} currently only checks if @code{FinishBreakpoint}s ran
+out of scope at normal stops, this method allows to force the verification
+and trigger the @code{out_of_scope} method if necessary. It will return 
+@code{True} if @code{out_of_scope ()} was triggered, @code{False} otherwise.
+@end defmethod
+
+
+@defivar FinishBreakpoint out_of_scope_notif
+This attribute will be @code{True} until the @code{out_of_scope} method has
+been called and @code{False} afterwards. This attribute is writeable, so out
+of scope notifications can be re-enabled.
+@end defivar
+
+@defivar FinishBreakpoint return_value
+When @value{GDBN} is stopped at a @code{FinishBreakpoint}, and the frame 
+used to build the @code{FinishBreakpoint} had debug symbols, this attribute
+will contain a @code{gdb.Value} object corresponding to the return value of
+the function. The value will be @code{None} if the value was not computable.
+This attribute is not writable.
+@end defivar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
@@ -35239,7 +35303,7 @@ is stopped at the user's request.
 
 @node List of Supported Calls
 @subsection List of Supported Calls
-@cindex list of supported file-i/o calls
+@cindex list of supported fFinile-i/o calls
 
 @menu
 * open::
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index be1f0a5..2d19ea9 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 15e3ca2..e38ce54 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 8d6910d..c90a5df 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 */
 
@@ -336,7 +337,8 @@ show_stop_on_solib_events (struct ui_file *file, int from_tty,
 int stop_after_trap;
 
 /* Save register contents here when executing a "finish" command or are
-   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set.
+   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set
+   or a Python FinishBreakpoint have been hit.
    Thus this contains the return value from the called function (assuming
    values are returned in a register).  */
 
@@ -5823,7 +5825,9 @@ 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_bp_type (inferior_thread ()->control.stop_bpstat,
+                                   py_bp_finish)
+      || 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..07247d8 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
 {
@@ -753,6 +719,23 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
   return stop;
 }
 
+/* If STOP_BPSTAT contains a Python breakpoint whose type is TYPE, returns
+   this breakpoint. Returns NULL otherwise.  */
+
+struct breakpoint *
+gdbpy_is_stopped_at_bp_type (bpstat stop_bpstat, enum py_bp_type type)
+{
+  bpstat bs;
+
+  for (bs = stop_bpstat; bs; bs = bs->next)
+    {
+      if (bs->breakpoint_at && bs->breakpoint_at->py_bp_type == type)
+        return bs->breakpoint_at;
+    }
+
+  return NULL;
+}
+
 /* Checks if the  "stop" method exists in this breakpoint.
    Used by condition_command to ensure mutual exclusion of breakpoint
    conditions.  */
@@ -813,6 +796,11 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->bp->py_bp_object = newbp;
       Py_INCREF (newbp);
       ++bppy_live;
+
+      if (bpfinishpy_is_finish_bp ((PyObject *) newbp))
+        bp->py_bp_type = py_bp_finish;
+      else
+        bp->py_bp_type = py_bp_standard;
     }
   else
     {
@@ -971,7 +959,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..26918cc
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,478 @@
+/* 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;
+  /* Type of the function finished by this breakpoint.  */
+  struct type *function_type;
+  /* Type of the value return by the breakpointed function.  */
+  struct type *return_type;
+  /* When stopped at this FinishBreakpoint, value returned by the function;
+     Py_None if the value is not computable;
+     NULL if GDB is not stopped at a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* 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;
+}
+
+/* Triggered when GDB stops at PY_BP. Disable `out_of_scope' notifications
+   and, if possible, computes and caches the `return_value'.  */
+
+static void
+bpfinish_stopped_at_finish_bp (struct finish_breakpoint_object *py_bp)
+{
+  py_bp->out_of_scope_notif = 0;
+
+  if (py_bp->return_type)
+    {
+      struct value *ret = get_return_value (py_bp->function_type,
+                                            py_bp->return_type);
+      if (ret)
+        {
+          py_bp->return_value = value_to_value_object (ret);
+        }
+      else
+        py_bp->return_value = Py_None;
+    }
+}
+
+/* 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->return_type == NULL)
+    Py_RETURN_NONE;
+
+  /* Check if we have a cached value.  */
+  if (!self_finishbp->return_value)
+    {
+      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)
+              bpfinish_stopped_at_finish_bp (self_finishbp);
+        }
+    }
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* 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))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+}
+
+/* Python function to check if the FinishBreakpoint SELF is not anymore
+   in the callstack. Triggers self->out_of_scope if necessary.  */
+
+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;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  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;
+
+  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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  if (get_frame_pc_if_available (frame, &pc))
+    {
+      function = find_pc_function (pc);
+      self_bpfinish->return_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+      /* Remember only non-VOID return types.  */
+      if (TYPE_CODE (self_bpfinish->return_type) != TYPE_CODE_VOID)
+        {
+          self_bpfinish->function_type = SYMBOL_TYPE (function);
+        }
+      else
+        self_bpfinish->return_type = NULL;
+    }
+  else
+    {
+      self_bpfinish->return_type = NULL;
+      self_bpfinish->function_type = NULL;
+    }
+
+  bppy_pending_object = self_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, -1,
+                         0,
+                         0, bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         NULL, 0, 1, internal_bp);
+    }
+  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.  */
+
+int
+bpfinishpy_is_finish_bp (PyObject *obj)
+{
+  return obj != NULL
+         && PyObject_TypeCheck (obj, &finish_breakpoint_object_type);
+}
+
+/* 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)
+        bpfinish_stopped_at_finish_bp (finish_bp);
+      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);
+}
+
+/* Callback to the bpfinishpy_handle_resume function.  */
+
+static int
+bpfinishpy_handle_resume_cb (struct breakpoint *b, void *args)
+{
+  if (bpfinishpy_is_finish_bp ((PyObject *) b->py_bp_object))
+    {
+      struct finish_breakpoint_object *finish_bp =
+                      (struct finish_breakpoint_object *) b->py_bp_object;
+      Py_XDECREF (finish_bp->return_value);
+      finish_bp->return_value = NULL;
+    }
+  return 0;
+}
+
+/* Attached to `target_resume' notifications, resets the `return_value'
+   cache.  */
+
+static void
+bpfinishpy_handle_resume (ptid_t ptid)
+{
+  iterate_over_breakpoints (bpfinishpy_handle_resume_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);
+  observer_attach_target_resumed (bpfinishpy_handle_resume);
+}
+
+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..e404a32 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 *);
@@ -181,6 +220,8 @@ struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
 
+int bpfinishpy_is_finish_bp (PyObject *obj);
+
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
 void gdbpy_initialize_frames (void);
@@ -194,6 +235,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..f71fd4a 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."));
 }
 
+struct breakpoint *
+gdbpy_is_stopped_at_bp_type (bpstat stop_bpstat, enum py_bp_type type)
+{
+  return NULL;
+}
 #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..9be6ac9 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,6 @@ int gdbpy_should_stop (struct breakpoint_object *bp_obj);
 
 int gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj);
 
+struct breakpoint *gdbpy_is_stopped_at_bp_type (bpstat stop_bpstat,
+                                                enum py_bp_type type);
 #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..57a6e06
--- /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"

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

* Re: [RFC] Python Finish Breakpoints
  2011-05-24 12:51             ` Kevin Pouget
@ 2011-05-27 20:30               ` Tom Tromey
  2011-05-30  9:29                 ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Tom Tromey @ 2011-05-27 20:30 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches

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

Kevin> you're right; I chose the second way,

Kevin> breakpoint.h:
Kevin> enum py_bp_type
Kevin>   {
Kevin>     py_bp_none,         /* No Python object.  */

I don't think this one is needed.

Kevin>     py_bp_standard,     /* Ordinary breakpoint object.  */
Kevin>     py_bp_finish        /* FinishBreakpoint object.  */

These should be uppercase, but it seems to me that if there are just 2
states you might as well use an ordinary boolean(-ish) flag.

Kevin> as per your two comments, I now only store the `struct type'  of the
Kevin> function and the return value,

You need to store a gdb.Type wrapper.
A 'struct type' can also be invalidated when an objfile is destroyed.

Kevin> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp

Tom> Funny file name.

Kevin> funny but correct, or too funny? ;)

It is more usual in the gdb test suite to give the .cc and .exp files
the same base name.

Kevin> I'll work on the tests for the next version of the patch ("return"
Kevin> should already be covered)

I will wait for this to do more review.

Kevin> @defivar FinishBreakpoint out_of_scope_notif
Kevin> This attribute will be @code{True} until the @code{out_of_scope} method has
Kevin> been called and @code{False} afterwards. This attribute is writeable, so out
Kevin> of scope notifications can be re-enabled.
Kevin> @end defivar

I still don't really understand under what circumstances it is useful
for a program to set this attribute.

Kevin> - avoid calling `out_of_scope' every normal_stop when the breakpoint
Kevin> is not anymore in the callstack

I think it would be ok to just leave this up to the subclass to handle.

Kevin> - allow the script to re-activate notification when it wants to
Kevin> 're-use' the FinishBreakpoint (instead of deleting it / creating a new
Kevin> one)

I am not sure when this makes sense.

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2011-05-27 20:30               ` Tom Tromey
@ 2011-05-30  9:29                 ` Kevin Pouget
  2011-10-13 14:34                   ` Kevin Pouget
  2011-10-20 20:58                   ` Tom Tromey
  0 siblings, 2 replies; 74+ messages in thread
From: Kevin Pouget @ 2011-05-30  9:29 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

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

> Kevin> breakpoint.h:
> Kevin> enum py_bp_type
> Kevin>   {
> Kevin>     py_bp_none,         /* No Python object.  */
>
> I don't think this one is needed.
>
> Kevin>     py_bp_standard,     /* Ordinary breakpoint object.  */
> Kevin>     py_bp_finish        /* FinishBreakpoint object.  */
>
> These should be uppercase, but it seems to me that if there are just 2
> states you might as well use an ordinary boolean(-ish) flag.

OK, I wanted to let a room free for further Python-specific breakpoint
handling, but if you feel like it's not necessary ...
I changed it to "int is_py_finish_bp"

> Kevin> as per your two comments, I now only store the `struct type'  of the
> Kevin> function and the return value,
>
> You need to store a gdb.Type wrapper.
> A 'struct type' can also be invalidated when an objfile is destroyed.

I wouldn't mind, but I can't see how gdb.Type ensures validity, as far
as I've seen, there is no "is_valid" method and I can't and no further
verification during the Python -> C translation:

struct type *
type_object_to_type (PyObject *obj)
{
  if (! PyObject_TypeCheck (obj, &type_object_type))
    return NULL;
  return ((type_object *) obj)->type;
}


> Kevin> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp
>
> Tom> Funny file name.
>
> Kevin> funny but correct, or too funny? ;)
>
> It is more usual in the gdb test suite to give the .cc and .exp files
> the same base name.

so py-finish-breakpoint2.{exp,cc,py} should look more serious!

>> Other cases to consider are -
>> * inferior exec
>> * inferior exit
>> * explicit inferior function call
>> * "return" command
> Kevin> I'll work on the tests for the next version of the patch ("return"
> Kevin> should already be covered)
>
> I will wait for this to do more review.

these situations should now be covered in `'py-finish-breakpoint.exp"

> Kevin> @defivar FinishBreakpoint out_of_scope_notif
> Kevin> This attribute will be @code{True} until the @code{out_of_scope} method has
> Kevin> been called and @code{False} afterwards. This attribute is writeable, so out
> Kevin> of scope notifications can be re-enabled.
> Kevin> @end defivar
>
> I still don't really understand under what circumstances it is useful
> for a program to set this attribute.
>
> Kevin> - avoid calling `out_of_scope' every normal_stop when the breakpoint
> Kevin> is not anymore in the callstack
>
> I think it would be ok to just leave this up to the subclass to handle.

I thought about just getting rid of this part of the patch, but it
really seem important to me.
Here is a (pseudo-code) example to try to demonstrate that:

function inner (param) {
  if (param)
    return 1
  else
    throw exception
}

function outter (param) {
  return innter(param)
}

function main () {
  try:
    outter (TRUE)
  except:
    pass

  try:
    outter (FALSE)
  except:
    pass

  try:
    outter (TRUE)
  except:
    pass
}

in a Python script, you want to know the return value of OUTTER (in a
script --> you don't know the actual shape of the Main function -- for
instance think about the 'write(2)` function which returns the size of
the data actually written)

you'll need one gdb.Breakpoint and several gdb.FinishBreakpoint:

class OutterBreakpoint(gdb.Breakpoint):
  def __init__(self):
    gdb.Breakpoint.__init__(self, "outter", internal=True)
    self.silent = True

  def stop():
    OutterFinishBreakpoint(gdb.newest_frame())
    return False

class OutterFinishBreakpoint(gdb.FinishBreakpoint):
  def stop():
    print "outter finished with :", self.return_value
    self.enable = False
    gdb.post_event(self.delete)

  def out_of_scope()
    self.enable = False
    gdb.post_event(self.delete)

This `out_of_scope' function is the easiest way to cleanup the
FinishBreakpoint when the finished function didn't return correctly
(and is only useful in this situation, I admit)

> Kevin> - allow the script to re-activate notification when it wants to
> Kevin> 're-use' the FinishBreakpoint (instead of deleting it / creating a new
> Kevin> one)
>
> I am not sure when this makes sense.

that's the opposite situation, you know know the function will only be
triggered from one (limitted set of) place, so you don't want to
create/delete BPs each time:


class OutterBreakpoint(gdb.Breakpoint):
  ...
  def stop():
    if self.finish is None:
        self.finish = OutterFinishBreakpoint(gdb.newest_frame())
    return False

class OutterFinishBreakpoint(gdb.FinishBreakpoint):
  ...
  def stop():
     #don't delete
     #treat normal termination

  def out_of_scope():
     #treat exception termination
     self.out_of_scope_notif = True # allows to catch further
exception termination


I also added a verification which checks that `out_of_scope'  is only
trigged for the inferior for the inferior which owns the breakpoint:

+bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
...
+      else if (finish_bp->out_of_scope_notif
+          && b->pspace == current_inferior()->pspace
+          && (!target_has_registers || frame_find_by_id(b->frame_id) == NULL))
+        {
+          bpfinishpy_out_of_scope (finish_bp);
+        }


thanks,

Kevin

[-- Attachment #2: finish_bp2.txt --]
[-- Type: text/plain, Size: 56570 bytes --]

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 7093104..7a3da26 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5814,6 +5814,7 @@ set_raw_breakpoint_without_location (struct gdbarch *gdbarch,
   b->ops = NULL;
   b->condition_not_parsed = 0;
   b->py_bp_object = NULL;
+  b->is_py_finish_bp = 0;
   b->related_breakpoint = b;
 
   /* Add this breakpoint to the end of the chain so that a list of
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 7fa705f..bb45512 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -654,6 +654,9 @@ struct breakpoint
        types are tracked by the Python scripting API.  */
     struct breakpoint_object *py_bp_object;
 
+    /* TRUE is py_bp_object is a gdb.FinishBreakpoint object .  */
+    int is_py_finish_bp;
+
     /* Whether this watchpoint is exact (see target_exact_watchpoints).  */
     int exact;
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 584a520..972b608 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -23513,6 +23513,70 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defivar
 
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A @fdb{finish breakpoint} is a breakpoint set at the return address of
+a frame, based on the "finish command. @code{gdb.FinishBreakpoint} extends
+@code{gdb.Breakpoint}
+
+@defmethod FinishBreakpoint __init__ frame @r{[}internal@r{]}
+Create a FinishBreakpoint at the return of the @code{gdb.Frame} object 
+@var{frame}. The optional @var{internal} argument allows the breakpoint
+to become invisible to the user. @xref{Breakpoints In Python}, for further 
+details about this argument.
+@end defmethod
+
+@defop Operation {gdb.Breakpoint} out_of_scope (self)
+In some circonstances (eg, @code{longjmp}, C++ exceptions, @value{GDBN} 
+@code{return} command, ...), a function may not properly terminate, and thus 
+never hit a @{code} FinishBreakpoint. When @value{GDBN} notices such a 
+situation, the @code{out_of_scope} function will be triggered.
+
+This method will only be called if the @code{out_of_scope_notif} attribute 
+is @code{True}.
+
+Deleting (@code{gdb.Breakpoint.delete ()}) the @code{FinishBreakpoint} is
+allowed in this function.
+
+You may want to sub-class the @code{gdb.FinishBreakpoint} and implement the
+@code{out_of_scope} method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+	def stop (self):
+		print "normal finish"
+		return True
+	
+	def out_of_scope ():
+		print "abnormal finish"
+@end smallexample 
+@end defop
+
+@defmethod FinishBreakpoint check_scope
+Because @value{GDBN} currently only checks if @code{FinishBreakpoint}s ran
+out of scope at normal stops, this method allows to force the verification
+and trigger the @code{out_of_scope} method if necessary. It will return 
+@code{True} if @code{out_of_scope ()} was triggered, @code{False} otherwise.
+@end defmethod
+
+
+@defivar FinishBreakpoint out_of_scope_notif
+This attribute will be @code{True} until the @code{out_of_scope} method has
+been called and @code{False} afterwards. This attribute is writeable, so out
+of scope notifications can be re-enabled.
+@end defivar
+
+@defivar FinishBreakpoint return_value
+When @value{GDBN} is stopped at a @code{FinishBreakpoint}, and the frame 
+used to build the @code{FinishBreakpoint} had debug symbols, this attribute
+will contain a @code{gdb.Value} object corresponding to the return value of
+the function. The value will be @code{None} if the value was not computable.
+This attribute is not writable.
+@end defivar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index be1f0a5..2d19ea9 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 15e3ca2..e38ce54 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 8d6910d..efe21c3 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 */
 
@@ -336,7 +337,8 @@ show_stop_on_solib_events (struct ui_file *file, int from_tty,
 int stop_after_trap;
 
 /* Save register contents here when executing a "finish" command or are
-   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set.
+   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set
+   or a Python FinishBreakpoint have been hit.
    Thus this contains the return value from the called function (assuming
    values are returned in a register).  */
 
@@ -5823,7 +5825,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..47946d9 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
 {
@@ -813,6 +779,8 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->bp->py_bp_object = newbp;
       Py_INCREF (newbp);
       ++bppy_live;
+
+      bp->is_py_finish_bp = bpfinishpy_is_finish_bp ((PyObject *) newbp);
     }
   else
     {
@@ -971,7 +939,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..9b849bf
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,497 @@
+/* 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;
+  /* Type of the function finished by this breakpoint.  */
+  struct type *function_type;
+  /* Type of the value return by the breakpointed function.  */
+  struct type *return_type;
+  /* When stopped at this FinishBreakpoint, value returned by the function;
+     Py_None if the value is not computable;
+     NULL if GDB is not stopped at a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* 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;
+}
+
+/* Triggered when GDB stops at PY_BP. Disable `out_of_scope' notifications
+   and, if possible, computes and caches the `return_value'.  */
+
+static void
+bpfinish_stopped_at_finish_bp (struct finish_breakpoint_object *py_bp)
+{
+  py_bp->out_of_scope_notif = 0;
+
+  if (py_bp->return_type)
+    {
+      struct value *ret = get_return_value (py_bp->function_type,
+                                            py_bp->return_type);
+      if (ret)
+        {
+          py_bp->return_value = value_to_value_object (ret);
+        }
+      else
+        py_bp->return_value = Py_None;
+    }
+}
+
+/* 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->return_type == NULL)
+    Py_RETURN_NONE;
+
+  /* Check if we have a cached value.  */
+  if (!self_finishbp->return_value)
+    {
+      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)
+              bpfinish_stopped_at_finish_bp (self_finishbp);
+        }
+    }
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* 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
+bpfinishpy_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))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+}
+
+/* Python function to check if the FinishBreakpoint SELF is not anymore
+   in the callstack. Triggers self->out_of_scope if necessary.  */
+
+static PyObject *
+bpfinishpy_check_scope (PyObject *self, PyObject *args)
+{
+  struct finish_breakpoint_object *self_finishbp =
+      (struct finish_breakpoint_object *) self;
+  struct frame_id bp_frame_id = self_finishbp->py_bp.bp->frame_id;
+
+  if (self_finishbp->out_of_scope_notif
+      && (frame_id_eq (bp_frame_id, get_frame_id (get_current_frame()))
+      ||  frame_find_by_id (bp_frame_id) == NULL))
+    {
+      bpfinishpy_out_of_scope (self_finishbp);
+
+      Py_RETURN_TRUE;
+    }
+  Py_RETURN_FALSE;
+}
+
+/* If STOP_BPSTAT contains a Python breakpoint whose type is TYPE, returns
+   this breakpoint. Returns NULL otherwise.  */
+
+struct breakpoint *
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  bpstat bs;
+
+  for (bs = stop_bpstat; bs; bs = bs->next)
+    {
+      if (bs->breakpoint_at && bs->breakpoint_at->is_py_finish_bp)
+        return bs->breakpoint_at;
+    }
+
+  return NULL;
+}
+
+/* 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;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  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;
+
+  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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  self_bpfinish->return_type = NULL;
+  self_bpfinish->function_type = NULL;
+  if (get_frame_pc_if_available (frame, &pc))
+    {
+      function = find_pc_function (pc);
+      if (function != NULL)
+        {
+          struct type *ret_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+          /* Remember only non-VOID return types.  */
+          if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
+            {
+              self_bpfinish->return_type = ret_type;
+              self_bpfinish->function_type = SYMBOL_TYPE (function);
+            }
+        }
+    }
+
+  bppy_pending_object = self_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, -1,
+                         0,
+                         0, bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         NULL, 0, 1, internal_bp);
+    }
+  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.  */
+
+int
+bpfinishpy_is_finish_bp (PyObject *obj)
+{
+  return obj != NULL
+         && PyObject_TypeCheck (obj, &finish_breakpoint_object_type);
+}
+
+/* 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)
+        bpfinish_stopped_at_finish_bp (finish_bp);
+      else if (finish_bp->out_of_scope_notif
+          && b->pspace == current_inferior()->pspace
+          && (!target_has_registers || frame_find_by_id(b->frame_id) == NULL))
+        {
+          bpfinishpy_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);
+}
+
+/* Callback to the bpfinishpy_handle_resume function.  */
+
+static int
+bpfinishpy_handle_resume_cb (struct breakpoint *b, void *args)
+{
+  if (bpfinishpy_is_finish_bp ((PyObject *) b->py_bp_object))
+    {
+      struct finish_breakpoint_object *finish_bp =
+                      (struct finish_breakpoint_object *) b->py_bp_object;
+      Py_XDECREF (finish_bp->return_value);
+      finish_bp->return_value = NULL;
+    }
+  return 0;
+}
+
+/* Attached to `target_resume' notifications, resets the `return_value'
+   cache.  */
+
+static void
+bpfinishpy_handle_resume (ptid_t ptid)
+{
+  iterate_over_breakpoints (bpfinishpy_handle_resume_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);
+  observer_attach_target_resumed (bpfinishpy_handle_resume);
+}
+
+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..e404a32 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 *);
@@ -181,6 +220,8 @@ struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
 
+int bpfinishpy_is_finish_bp (PyObject *obj);
+
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
 void gdbpy_initialize_frames (void);
@@ -194,6 +235,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..e2472fc 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."));
 }
 
+struct breakpoint *
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  return NULL;
+}
 #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..e341d76 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);
 
+struct breakpoint *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.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..5b708e3
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,97 @@
+/* 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>
+#include <stdlib.h>
+#include <unistd.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);
+}
+
+void
+test_exec_exit(int do_exit)
+{
+  if (do_exit)
+    exit(0);
+  else
+    execl ("/bin/echo", "echo", "-1", (char *)0);
+}
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  getpid();
+
+  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. */
+
+  test_exec_exit(1);
+
+  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..237e96a
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,258 @@
+# 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 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_breakpoint "increase_1"
+gdb_test "continue" ".*Breakpoint .*at.*" "continue to function to finish"
+
+# 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_scope()
+
+gdb_test_no_output "python finishbp.out_of_scope_notif = True" \
+         "re-enable out_of_scope_notif"
+
+gdb_test "continue" ".*Breakpoint.*at.*" "continue to function to finish"
+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.*" \
+         "check_scope of finishBreakpoint"
+
+# check forced return / out_of_scope automatic notification
+
+gdb_test_no_output "python finishbp.out_of_scope_notif = True" \
+         "re-enable out_of_scope_notif"
+
+gdb_test "continue" ".*Breakpoint.*at.*" "continue to function to finish"
+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"
+
+
+#
+# Test FinishBreakpoint with no debug symbol 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" ".*Python script imported.*" \
+         "import python scripts"
+
+if ![runto "getpid"] then {
+    fail "Cannot run to getpid."
+    return 0
+}
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "continue" ".*SimpleFinishBreakpoint stop.*" "check FinishBreakpoint hit"
+gdb_test "python print finishBP.return_value" "None" "check return value without debug symbol"
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" ".*Python script imported.*" \
+         "import python scripts"
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" ".*SimpleFinishBreakpoint out of scope.*" \
+         "check FinishBP out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy frame)
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" ".*Python script imported.*" \
+         "import python scripts"
+
+set cond_line [gdb_get_line_number "Condition Break."]
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+         
+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 "Breakpoint.stop() does't stop in GDB dummy frame" *-*-*
+gdb_test "python print test_finish_bp.out_of_scope_notif" ".*False.*" "check out_of_scope notification disabled"
+gdb_test_no_output "python test_finish_bp.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 explicit inferior function call
+#
+
+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 "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+if {[gdb_test "print increase_1(&i)" \
+         ".*SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*\\\$1 = -5.*" \
+         "don't stop at FinishBreakpoint"] == 0} then {
+	setup_kfail "Breakpoint.stop() does't stop in GDB dummy frame" *-*-*
+	fail "FinishBreakpoint stop in dummy frame" 
+}
+
+delete_breakpoints
+gdb_test "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase(&i)" \
+         ".*SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*The program being debugged stopped while in a function called from GDB.*" \
+         "FinishBP stop at during explicit function call"
+
+#
+# Test FinishBreakpoint when inferior exits
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}
+
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
+gdb_test "continue" ".*SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
+
+#
+# Test FinishBreakpoint when inferior execs
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}     
+
+gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
+gdb_test "catch exec" ".*Catchpoint.*\(exec\).*" "catch exec"
+gdb_test "continue" ".*SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ 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..6f41b5f
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,84 @@
+# 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 MyFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		gdb.FinishBreakpoint.__init__ (self, 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 TestExplicitBreakpoint(gdb.Breakpoint):
+	def stop(self):
+		SimpleFinishBreakpoint(gdb.newest_frame())
+		return False
+
+class SimpleFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		print "SimpleFinishBreakpoint init"
+		
+	def stop(self):
+		print "SimpleFinishBreakpoint stop" 
+		return True
+	
+	def out_of_scope(self):
+		print "SimpleFinishBreakpoint out of scope..."
+
+print "Python script imported"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.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-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
new file mode 100644
index 0000000..0715188
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -0,0 +1,61 @@
+# 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-breakpoint2"
+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_breakpoint [gdb_get_line_number "Break after exception 2"]
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_breakpoint "throw_exception_1"
+gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
+
+gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.py b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
new file mode 100644
index 0000000..9ea4cf1
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
@@ -0,0 +1,34 @@
+# 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 ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        self.silent = True;
+        print "init ExceptionFinishBreakpoint"
+        
+    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"

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

* Re: [RFC] Python Finish Breakpoints
  2011-05-30  9:29                 ` Kevin Pouget
@ 2011-10-13 14:34                   ` Kevin Pouget
  2011-10-20 20:12                     ` Tom Tromey
  2011-10-20 20:58                   ` Tom Tromey
  1 sibling, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-10-13 14:34 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Mon, May 30, 2011 at 11:28 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
>> Kevin> breakpoint.h:
>> Kevin> enum py_bp_type
>> Kevin>   {
>> Kevin>     py_bp_none,         /* No Python object.  */
>>
>> I don't think this one is needed.
>>
>> Kevin>     py_bp_standard,     /* Ordinary breakpoint object.  */
>> Kevin>     py_bp_finish        /* FinishBreakpoint object.  */
>>
>> These should be uppercase, but it seems to me that if there are just 2
>> states you might as well use an ordinary boolean(-ish) flag.
>
> OK, I wanted to let a room free for further Python-specific breakpoint
> handling, but if you feel like it's not necessary ...
> I changed it to "int is_py_finish_bp"
>
>> Kevin> as per your two comments, I now only store the `struct type'  of the
>> Kevin> function and the return value,
>>
>> You need to store a gdb.Type wrapper.
>> A 'struct type' can also be invalidated when an objfile is destroyed.
>
> I wouldn't mind, but I can't see how gdb.Type ensures validity, as far
> as I've seen, there is no "is_valid" method and I can't and no further
> verification during the Python -> C translation:
>
> struct type *
> type_object_to_type (PyObject *obj)
> {
>  if (! PyObject_TypeCheck (obj, &type_object_type))
>    return NULL;
>  return ((type_object *) obj)->type;
> }
>
>
>> Kevin> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp
>>
>> Tom> Funny file name.
>>
>> Kevin> funny but correct, or too funny? ;)
>>
>> It is more usual in the gdb test suite to give the .cc and .exp files
>> the same base name.
>
> so py-finish-breakpoint2.{exp,cc,py} should look more serious!
>
>>> Other cases to consider are -
>>> * inferior exec
>>> * inferior exit
>>> * explicit inferior function call
>>> * "return" command
>> Kevin> I'll work on the tests for the next version of the patch ("return"
>> Kevin> should already be covered)
>>
>> I will wait for this to do more review.
>
> these situations should now be covered in `'py-finish-breakpoint.exp"
>
>> Kevin> @defivar FinishBreakpoint out_of_scope_notif
>> Kevin> This attribute will be @code{True} until the @code{out_of_scope} method has
>> Kevin> been called and @code{False} afterwards. This attribute is writeable, so out
>> Kevin> of scope notifications can be re-enabled.
>> Kevin> @end defivar
>>
>> I still don't really understand under what circumstances it is useful
>> for a program to set this attribute.
>>
>> Kevin> - avoid calling `out_of_scope' every normal_stop when the breakpoint
>> Kevin> is not anymore in the callstack
>>
>> I think it would be ok to just leave this up to the subclass to handle.
>
> I thought about just getting rid of this part of the patch, but it
> really seem important to me.
> Here is a (pseudo-code) example to try to demonstrate that:
>
> function inner (param) {
>  if (param)
>    return 1
>  else
>    throw exception
> }
>
> function outter (param) {
>  return innter(param)
> }
>
> function main () {
>  try:
>    outter (TRUE)
>  except:
>    pass
>
>  try:
>    outter (FALSE)
>  except:
>    pass
>
>  try:
>    outter (TRUE)
>  except:
>    pass
> }
>
> in a Python script, you want to know the return value of OUTTER (in a
> script --> you don't know the actual shape of the Main function -- for
> instance think about the 'write(2)` function which returns the size of
> the data actually written)
>
> you'll need one gdb.Breakpoint and several gdb.FinishBreakpoint:
>
> class OutterBreakpoint(gdb.Breakpoint):
>  def __init__(self):
>    gdb.Breakpoint.__init__(self, "outter", internal=True)
>    self.silent = True
>
>  def stop():
>    OutterFinishBreakpoint(gdb.newest_frame())
>    return False
>
> class OutterFinishBreakpoint(gdb.FinishBreakpoint):
>  def stop():
>    print "outter finished with :", self.return_value
>    self.enable = False
>    gdb.post_event(self.delete)
>
>  def out_of_scope()
>    self.enable = False
>    gdb.post_event(self.delete)
>
> This `out_of_scope' function is the easiest way to cleanup the
> FinishBreakpoint when the finished function didn't return correctly
> (and is only useful in this situation, I admit)
>
>> Kevin> - allow the script to re-activate notification when it wants to
>> Kevin> 're-use' the FinishBreakpoint (instead of deleting it / creating a new
>> Kevin> one)
>>
>> I am not sure when this makes sense.
>
> that's the opposite situation, you know know the function will only be
> triggered from one (limitted set of) place, so you don't want to
> create/delete BPs each time:
>
>
> class OutterBreakpoint(gdb.Breakpoint):
>  ...
>  def stop():
>    if self.finish is None:
>        self.finish = OutterFinishBreakpoint(gdb.newest_frame())
>    return False
>
> class OutterFinishBreakpoint(gdb.FinishBreakpoint):
>  ...
>  def stop():
>     #don't delete
>     #treat normal termination
>
>  def out_of_scope():
>     #treat exception termination
>     self.out_of_scope_notif = True # allows to catch further
> exception termination
>
>
> I also added a verification which checks that `out_of_scope'  is only
> trigged for the inferior for the inferior which owns the breakpoint:
>
> +bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
> ...
> +      else if (finish_bp->out_of_scope_notif
> +          && b->pspace == current_inferior()->pspace
> +          && (!target_has_registers || frame_find_by_id(b->frame_id) == NULL))
> +        {
> +          bpfinishpy_out_of_scope (finish_bp);
> +        }


Hello,

I would like to come back on this patch discussion, I know it has been
a while since the last post (May 30th), but I was waiting for my
copyright assignment to be ready, and then had to empty the patches
already in the pipe.


it appeared that the most stormy part was related to the
FinishBreakpoint.out_of_scope() callback:

> @defop Operation {gdb.Breakpoint} out_of_scope (self)
> In some circonstances (eg, @code{longjmp}, C++ exceptions, @value{GDBN}
> @code{return} command, ...), a function may not properly terminate, and thus
> never hit a @{code} FinishBreakpoint. When @value{GDBN} notices such a
> situation, the @code{out_of_scope} function will be triggered.

I'll try a last time to convince you about its necessity, then I'll
consider dropping it off !

The main rational behind this callback is housekeeping: I'm building
new functionalities on top of the Python interface, and I would like
it to be invisible to the user (eg, when GDB performs a 'next/finish',
you don't want to let the internal breakpoints in the inferior when
the commands returns, in any situation.

In the case of FinishBreakpoints, I would like to easily know if when
the BP became meaningless (that is, the exec ran out of the scope
where the FBP can be trigged) and time has come to remove it.
Does it make sense?


Thanks,

Kevin

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

* Re: [RFC] Python Finish Breakpoints
  2011-10-13 14:34                   ` Kevin Pouget
@ 2011-10-20 20:12                     ` Tom Tromey
  0 siblings, 0 replies; 74+ messages in thread
From: Tom Tromey @ 2011-10-20 20:12 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches

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

Kevin> I would like to come back on this patch discussion, I know it has been
Kevin> a while since the last post (May 30th), but I was waiting for my
Kevin> copyright assignment to be ready, and then had to empty the patches
Kevin> already in the pipe.

Thanks for reopening this.
I'd like this functionality, in some form :), to go in.

Kevin> it appeared that the most stormy part was related to the
Kevin> FinishBreakpoint.out_of_scope() callback:

>> @defop Operation {gdb.Breakpoint} out_of_scope (self)
>> In some circonstances (eg, @code{longjmp}, C++ exceptions, @value{GDBN}
>> @code{return} command, ...), a function may not properly terminate, and thus
>> never hit a @{code} FinishBreakpoint. When @value{GDBN} notices such a
>> situation, the @code{out_of_scope} function will be triggered.

Kevin> I'll try a last time to convince you about its necessity, then I'll
Kevin> consider dropping it off !

Kevin> The main rational behind this callback is housekeeping: I'm building
Kevin> new functionalities on top of the Python interface, and I would like
Kevin> it to be invisible to the user (eg, when GDB performs a 'next/finish',
Kevin> you don't want to let the internal breakpoints in the inferior when
Kevin> the commands returns, in any situation.

Kevin> In the case of FinishBreakpoints, I would like to easily know if when
Kevin> the BP became meaningless (that is, the exec ran out of the scope
Kevin> where the FBP can be trigged) and time has come to remove it.
Kevin> Does it make sense?

I think a callback like this does make sense for finish breakpoints.
From looking back at the old thread, the problem I had was that I did
not understand the way that the callback was used, or the purpose of the
"out_of_scope_notif" attribute.

I'll send some notes on the latest version of the patch in a bit.

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2011-05-30  9:29                 ` Kevin Pouget
  2011-10-13 14:34                   ` Kevin Pouget
@ 2011-10-20 20:58                   ` Tom Tromey
  2011-10-21  8:15                     ` Kevin Pouget
  1 sibling, 1 reply; 74+ messages in thread
From: Tom Tromey @ 2011-10-20 20:58 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches

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

Tom> You need to store a gdb.Type wrapper.
Tom> A 'struct type' can also be invalidated when an objfile is destroyed.

Kevin> I wouldn't mind, but I can't see how gdb.Type ensures validity, as far
Kevin> as I've seen, there is no "is_valid" method and I can't and no further
Kevin> verification during the Python -> C translation:

When an objfile is destroyed, py-type.c:save_objfile_types is called.
This makes a copy of the struct type underlying any relevant Type object.

[ out_of_scope_notif ]
Kevin> - avoid calling `out_of_scope' every normal_stop when the breakpoint
Kevin> is not anymore in the callstack

It seems to me that manually managing this is not the best we could do.

I was re-reading the code and I realized that this isn't actually
working the way I had remembered it working:

+A @fdb{finish breakpoint} is a breakpoint set at the return address of
+a frame, based on the "finish command. @code{gdb.FinishBreakpoint} extends
+@code{gdb.Breakpoint}

This approach is fine; but once a frame is gone, it is gone -- we should
just destroy the breakpoint at that point.  Maybe this would make the
internal logic simpler as well.

(I had been laboring under the misconception that a finish breakpoint
took a linespec and then did the bookkeeping of "note function entry /
set a new breakpoint" -- but actually I like your simpler approach
better, and the latter can be done more easily purely in Python.)

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2011-10-20 20:58                   ` Tom Tromey
@ 2011-10-21  8:15                     ` Kevin Pouget
  2011-10-24 11:43                       ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-10-21  8:15 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Thu, Oct 20, 2011 at 10:41 PM, Tom Tromey <tromey@redhat.com> wrote:
>>>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
>
> Tom> You need to store a gdb.Type wrapper.
> Tom> A 'struct type' can also be invalidated when an objfile is destroyed.
>
> Kevin> I wouldn't mind, but I can't see how gdb.Type ensures validity, as far
> Kevin> as I've seen, there is no "is_valid" method and I can't and no further
> Kevin> verification during the Python -> C translation:
>
> When an objfile is destroyed, py-type.c:save_objfile_types is called.
> This makes a copy of the struct type underlying any relevant Type object.

I'll take a look at that, I don't remember what was the problem!

> [ out_of_scope_notif ]
> Kevin> - avoid calling `out_of_scope' every normal_stop when the breakpoint
> Kevin> is not anymore in the callstack
>
> It seems to me that manually managing this is not the best we could do.
>
> I was re-reading the code and I realized that this isn't actually
> working the way I had remembered it working:
>
> +A @fdb{finish breakpoint} is a breakpoint set at the return address of
> +a frame, based on the "finish command. @code{gdb.FinishBreakpoint} extends
> +@code{gdb.Breakpoint}
>
> This approach is fine; but once a frame is gone, it is gone -- we should
> just destroy the breakpoint at that point.  Maybe this would make the
> internal logic simpler as well.

yes, I think you're right with that, "once a frame is gone, it is
gone", I actually don't use myself this 'permanent finish breakpoint'
feature, so I'll change it.
So basically, the BP must be deleted right after it has been it (maybe
through the 'temporary' mechanism). 'out_of_scope' will be triggered
in the same conditions as before, but instead of playing with
'out_of_scope_notif' I'll delete the GDB breakpoint (thus invalidating
the Python BP)

I'll post a patch going this way later today

> (I had been laboring under the misconception that a finish breakpoint
> took a linespec and then did the bookkeeping of "note function entry /
> set a new breakpoint" -- but actually I like your simpler approach
> better, and the latter can be done more easily purely in Python.)

I'm not sure to understand exactly what you had in mind, but if you
prefer my solution that's okay


Thanks,

Kevin

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

* Re: [RFC] Python Finish Breakpoints
  2011-10-21  8:15                     ` Kevin Pouget
@ 2011-10-24 11:43                       ` Kevin Pouget
  2011-10-24 13:20                         ` Eli Zaretskii
  2011-10-24 15:31                         ` Kevin Pouget
  0 siblings, 2 replies; 74+ messages in thread
From: Kevin Pouget @ 2011-10-24 11:43 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

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

On Fri, Oct 21, 2011 at 9:45 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> On Thu, Oct 20, 2011 at 10:41 PM, Tom Tromey <tromey@redhat.com> wrote:
>>>>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
>>
>> Tom> You need to store a gdb.Type wrapper.
>> Tom> A 'struct type' can also be invalidated when an objfile is destroyed.
>>
>> Kevin> I wouldn't mind, but I can't see how gdb.Type ensures validity, as far
>> Kevin> as I've seen, there is no "is_valid" method and I can't and no further
>> Kevin> verification during the Python -> C translation:
>>
>> When an objfile is destroyed, py-type.c:save_objfile_types is called.
>> This makes a copy of the struct type underlying any relevant Type object.
>
> I'll take a look at that, I don't remember what was the problem!

I understood that, and replaced 'struct type *' fields by the
corresponding 'PyObject/Type' python objects

>> [ out_of_scope_notif ]
>> Kevin> - avoid calling `out_of_scope' every normal_stop when the breakpoint
>> Kevin> is not anymore in the callstack
>>
>> It seems to me that manually managing this is not the best we could do.
>>
>> I was re-reading the code and I realized that this isn't actually
>> working the way I had remembered it working:
>>
>> +A @fdb{finish breakpoint} is a breakpoint set at the return address of
>> +a frame, based on the "finish command. @code{gdb.FinishBreakpoint} extends
>> +@code{gdb.Breakpoint}
>>
>> This approach is fine; but once a frame is gone, it is gone -- we should
>> just destroy the breakpoint at that point.  Maybe this would make the
>> internal logic simpler as well.
>
> yes, I think you're right with that, "once a frame is gone, it is
> gone", I actually don't use myself this 'permanent finish breakpoint'
> feature, so I'll change it.
> So basically, the BP must be deleted right after it has been it (maybe
> through the 'temporary' mechanism). 'out_of_scope' will be triggered
> in the same conditions as before, but instead of playing with
> 'out_of_scope_notif' I'll delete the GDB breakpoint (thus invalidating
> the Python BP)
>
> I'll post a patch going this way later today

So I've changed the way FinishBreakpoints are 'garbage collected'.
Now, they're more similar to Temporary Breakpoints, that is, they're
deleted *soon* after they've been hit.
Soon, because when Breakpoint.stop tells GDB *not* to stop at the
[finish] breakpoint, there is no way to delete this breakpoint (and I
guess that may be the reason why Temporary Breakpoints are not
available in Python yet).

So I disable it, and "b->disposition == disp_del" will ensure that the
BP will be removed when it's possible.

The `out_of_scope' callback will also delete the breakpoint.


I've updated the doc and testsuite accordingly, and as far as I've
seen, there is no regression on X86_64/Fedora
(I need still need to improve my test environment)


Thanks,

Kevin

2011-10-24  Kevin Pouget  <kevin.pouget@st.com>

	Introduce gdb.FinishBreakpoints in Python

	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-finishbreakpoint.o.
	(SUBDIR_PYTHON_SRCS): Add python/py-finishbreakpoint.c.
	Add build rule for this file.
	* breakpoint.h (struct breakpoint): New field: is_py_finish_bp.
	* breakpoint.c (init_raw_breakpoint_without_location):
	Initialize is_py_finish_bp.
	* infcmd.c (get_return_value): New function.
	(print_return_value): Split to create get_return_value.
	* inferior.h (get_return_value): New prototype.
	* infrun.c: Include python/python.h.
	(stop_registers): Mention FinishBreakpoint in description.
	(normal_stop): Set stop_registers if stopped at FinishBreakpoint.
	* python/py-breakpoint.c (breakpoint_object_type): Make non-static.
	(bppy_pending_object): Likewise
	(gdbpy_should_stop): Disable temporary breakpoints.
	(gdbpy_breakpoint_created): Set is_py_finish_bp is necessary.
	(struct breakpoint_object): Move to python-internal.h
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	* python/python-internal.h (breakpoint_object_type): Add as extern.
	(bppy_pending_object): Likewise.
	(typedef struct breakpoint_object) Removed.
	(struct breakpoint_object): Moved from py-breakpoint.c.
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(frame_object_to_frame_info): New prototype.
	(gdbpy_initialize_finishbreakpoints): New prototype.
	(bpfinishpy_is_finish_bp): Likewise.
	* python/py-finishbreakpoint.c: New file.
	* python/py-frame.c(frame_object_to_frame_info): Make non-static and
	accept PyObject instead of frame_object.
	(frapy_is_valid): Don't cast to frame_object.
	(frapy_name): Likewise.
	(frapy_type): Likewise.
	(frapy_unwind_stop_reason): Likewise.
	(frapy_pc): Likewise.
	(frapy_block): Likewise.
	(frapy_function): Likewise.
	(frapy_older): Likewise.
	(frapy_newer): Likewise.
	(frapy_find_sal): Likewise.
	(frapy_read_var): Likewise.
	(frapy_select): Likewise.
	* python/python.c (gdbpy_is_stopped_at_finish_bp): New noop function.
	(_initialize_python): Add gdbpy_initialize_finishbreakpoints.
	* python/python.h: Include breakpoint.h
	(gdbpy_is_stopped_at_finish_bp): New prototype.

doc/
	* gdb.texinfo (Breakpoints In Python): New subsection: Finish
	Breakpoints.

testsuite/
	* gdb.python/py-breakpoint.exp (mult_line): Define and use variable
	instead of line number.
	* gdb.python/py-finish-breakpoint.c: New file.
	* gdb.python/py-finish-breakpoint.exp: New file.
	* gdb.python/py-finish-breakpoint.py: New file.
	* gdb.python/py-finish-breakpoint2.cc: New file.
	* gdb.python/py-finish-breakpoint2.exp: New file.
	* gdb.python/py-finish-breakpoint2.py: New file.

[-- Attachment #2: 0001-Introduce-gdb.FinishBreakpoints.patch --]
[-- Type: text/x-patch, Size: 53570 bytes --]

From cb3497ab8b0098d23a9ec2299ff0c3eaabff14f9 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Tue, 20 Sep 2011 13:59:23 +0200
Subject: [PATCH] Introduce gdb.FinishBreakpoints

---
 gdb/Makefile.in                                    |    6 +
 gdb/breakpoint.c                                   |    1 +
 gdb/breakpoint.h                                   |    3 +
 gdb/doc/gdb.texinfo                                |   46 +++
 gdb/infcmd.c                                       |   21 +-
 gdb/inferior.h                                     |    3 +
 gdb/infrun.c                                       |    9 +-
 gdb/python/py-breakpoint.c                         |   50 +--
 gdb/python/py-finishbreakpoint.c                   |  392 ++++++++++++++++++++
 gdb/python/py-frame.c                              |   32 +-
 gdb/python/python-internal.h                       |   46 +++-
 gdb/python/python.c                                |    6 +
 gdb/python/python.h                                |    2 +
 gdb/testsuite/gdb.python/py-breakpoint.exp         |    7 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.c    |   97 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp  |  230 ++++++++++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py   |   88 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc  |   59 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   65 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.py  |   34 ++
 20 files changed, 1132 insertions(+), 65 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 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
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.py

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 32d8ef3..6cedf12 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,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 \
@@ -318,6 +319,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 \
@@ -2103,6 +2105,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 ba1b08f..ffd2ef6 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5700,6 +5700,7 @@ init_raw_breakpoint_without_location (struct breakpoint *b,
   b->frame_id = null_frame_id;
   b->condition_not_parsed = 0;
   b->py_bp_object = NULL;
+  b->is_py_finish_bp = 0;
   b->related_breakpoint = b;
 }
 
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index c2116e2..3f232e7 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -625,6 +625,9 @@ struct breakpoint
        can sometimes be NULL for enabled GDBs as not all breakpoint
        types are tracked by the Python scripting API.  */
     struct breakpoint_object *py_bp_object;
+
+    /* TRUE is py_bp_object is a gdb.FinishBreakpoint object .  */
+    int is_py_finish_bp;
   };
 
 /* An instance of this type is used to represent a watchpoint.  It
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 69e2bda..10ff1c6 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24082,6 +24082,52 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A finish breakpoint is a temporary breakpoint set at the return address of
+a frame, based on the @code{finish} command.  @code{gdb.FinishBreakpoint}
+extends @code{gdb.Breakpoint}. The underlying breakpoint will be disabled 
+and deleted when the execution will run out of the breakoint scope (i.e.@: 
+@code{Breakpoint.stop} or @code{FinishBreakpoint.out_of_scope} triggered).
+
+@defun FinishBreakpoint.__init__ (@var{frame}, @r{[}@var{internal}@r{]})
+Create a finish breakpoint at the return address of the @code{gdb.Frame}
+object @var{frame}.  The optional @var{internal} argument allows the
+breakpoint to become invisible to the user.  @xref{Breakpoints In Python}
+for further details about this argument.
+@end defun
+
+@defun FinishBreakpoint.out_of_scope (self)
+In some circonstances (e.g.@: @code{longjmp}, C++ exceptions, @value{GDBN} 
+@code{return} command, ...), a function may not properly terminate, and thus 
+never hit the finish breakpoint.  When @value{GDBN} notices such a 
+situation, the @code{out_of_scope} callback will be triggered.
+
+You may want to sub-class @code{gdb.FinishBreakpoint} and override this
+method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+    def stop (self):
+        print "normal finish"
+        return True
+    
+    def out_of_scope ():
+        print "abnormal finish"
+@end smallexample 
+@end defun
+
+@defvar FinishBreakpoint.return_value
+When @value{GDBN} is stopped at a finish breakpoint and the frame 
+used to build the @code{gdb.FinishBreakpoint} object had debug symbols, this
+attribute will contain a @code{gdb.Value} object corresponding to the return
+value of the function.  The value will be @code{None} if the return value
+was not computable. This attribute is not writable.
+@end defvar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 3e8bf5d..33ce1fc 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1413,14 +1413,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.  */
+/* Return 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;
   struct ui_out *uiout = current_uiout;
 
@@ -1451,6 +1449,19 @@ 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;
+  struct ui_out *uiout = current_uiout;
+
   if (value)
     {
       struct value_print_options opts;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..f198696 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -269,6 +269,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 db6a5d1..5ce0c94 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -56,6 +56,7 @@
 #include "tracepoint.h"
 #include "continuations.h"
 #include "interps.h"
+#include "python/python.h"
 
 /* Prototypes for local functions */
 
@@ -365,7 +366,8 @@ show_stop_on_solib_events (struct ui_file *file, int from_tty,
 int stop_after_trap;
 
 /* Save register contents here when executing a "finish" command or are
-   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set.
+   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set
+   or a Python FinishBreakpoint has been hit.
    Thus this contains the return value from the called function (assuming
    values are returned in a register).  */
 
@@ -5985,8 +5987,9 @@ 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
-      && execution_direction != EXEC_REVERSE)
+  if (gdbpy_is_stopped_at_finish_bp (inferior_thread ()->control.stop_bpstat)
+      || (inferior_thread ()->control.proceed_to_finish
+          && execution_direction != EXEC_REVERSE))
     {
       /* This should not be necessary.  */
       if (stop_registers)
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index daceb99..32b5f16 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
 {
@@ -757,6 +723,14 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
       else
 	gdbpy_print_stack ();
     }
+
+  /* If it's a temporary breakpoint.  */
+  if (b->disposition == disp_del)
+    {
+      /* Can't delete it here.  */
+      disable_breakpoint (b);
+    }
+
   do_cleanups (cleanup);
 
   return stop;
@@ -821,6 +795,8 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->bp->py_bp_object = newbp;
       Py_INCREF (newbp);
       ++bppy_live;
+
+      bp->is_py_finish_bp = bpfinishpy_is_finish_bp ((PyObject *) newbp);
     }
   else
     {
@@ -980,7 +956,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..1ffaa9b
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,392 @@
+/* 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;
+  /* gdb.Type object of the function finished by this breakpoint.  */
+  PyObject *function_type;
+  /* gdb.Type object of the value return by the breakpointed function.  */
+  PyObject *return_type;
+  /* When stopped at this FinishBreakpoint, value returned by the function;
+     Py_None if the value is not computable;
+     NULL if GDB is not stopped at a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* Triggered when GDB stops at PY_BP. Computes and caches the `return_value',
+   if possible.  */
+
+static void
+bpfinish_stopped_at_finish_bp (struct finish_breakpoint_object *py_bp)
+{
+  if (py_bp->return_type)
+    {
+      struct value *ret =
+          get_return_value (type_object_to_type (py_bp->function_type),
+                            type_object_to_type (py_bp->return_type));
+      if (ret)
+        {
+          py_bp->return_value = value_to_value_object (ret);
+        }
+      else
+        py_bp->return_value = Py_None;
+    }
+}
+
+/* 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;
+
+  if (self_finishbp->return_type == NULL)
+    Py_RETURN_NONE;
+
+  /* Check if we have a cached value.  */
+  if (!self_finishbp->return_value)
+    {
+      bpstat bs;
+
+      BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
+
+      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)
+              bpfinish_stopped_at_finish_bp (self_finishbp);
+        }
+    }
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* If STOP_BPSTAT contains a Python breakpoint whose type is TYPE, returns
+   this breakpoint. Returns NULL otherwise.  */
+
+struct breakpoint *
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  bpstat bs;
+
+  for (bs = stop_bpstat; bs; bs = bs->next)
+    {
+      if (bs->breakpoint_at && bs->breakpoint_at->is_py_finish_bp)
+        return bs->breakpoint_at;
+    }
+
+  return NULL;
+}
+
+/* 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;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  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;
+    }
+  else if (get_frame_type(prev_frame) == DUMMY_FRAME)
+      {
+        PyErr_SetString (PyExc_ValueError,
+                  _("\"FinishBreakpoint\" cannot be set on a dummy frame."));
+        return -1;
+      }
+
+  frame_id = get_frame_id (prev_frame);
+  if (frame_id_eq (frame_id, null_frame_id))
+    goto invalid_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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  self_bpfinish->return_type = NULL;
+  self_bpfinish->function_type = NULL;
+  if (get_frame_pc_if_available (frame, &pc))
+    {
+      function = find_pc_function (pc);
+      if (function != NULL)
+        {
+          struct type *ret_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+          /* Remember only non-VOID return types.  */
+          if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
+            {
+              self_bpfinish->return_type = type_to_type_object (ret_type);
+              self_bpfinish->function_type =
+                  type_to_type_object (SYMBOL_TYPE (function));
+            }
+        }
+    }
+
+  bppy_pending_object = self_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, -1,
+                         0,
+                         1 /*temp_flag*/,
+                         bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         &bkpt_breakpoint_ops,
+                         0, 1, internal_bp);
+    }
+  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;
+  
+  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.  */
+
+int
+bpfinishpy_is_finish_bp (PyObject *obj)
+{
+  return obj != NULL
+         && PyObject_TypeCheck (obj, &finish_breakpoint_object_type);
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack. Triggers the method OUT_OF_SCOPE if implemented,
+   then delete the breakpoint.  */
+
+static void
+bpfinishpy_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))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+
+  delete_breakpoint (bpfinish_obj->py_bp.bp);
+}
+
+/* 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 and its frame is
+     not anymore in the current callstack.  */
+  if (bpfinishpy_is_finish_bp (py_bp))
+    {
+      struct finish_breakpoint_object *finish_bp =
+          (struct finish_breakpoint_object *) py_bp;
+
+      if (b == bp_stopped)
+        bpfinish_stopped_at_finish_bp (finish_bp);
+      else if (b->pspace == current_inferior()->pspace
+           && (!target_has_registers
+               || frame_find_by_id(b->frame_id) == NULL))
+        {
+          bpfinishpy_out_of_scope (finish_bp);
+        }
+    }
+  
+  do_cleanups (cleanup);
+  
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run outg
+   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 PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "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 398ce84..c4494cb 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);
     }
@@ -476,12 +477,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 e593612..46f08ed 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -120,9 +120,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
 {
@@ -188,6 +226,9 @@ struct value *convert_value_from_python (PyObject *obj);
 struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
+struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);
+
+int bpfinishpy_is_finish_bp (PyObject *obj);
 
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
@@ -202,6 +243,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 df8e3d5..db23bbf 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1054,6 +1054,11 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj)
 		    "scripting is not supported."));
 }
 
+struct breakpoint *
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  return NULL;
+}
 #endif /* HAVE_PYTHON */
 
 \f
@@ -1243,6 +1248,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..e341d76 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);
 
+struct breakpoint *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 e0dd087..0e3adbd 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.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..5b708e3
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,97 @@
+/* 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>
+#include <stdlib.h>
+#include <unistd.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);
+}
+
+void
+test_exec_exit(int do_exit)
+{
+  if (do_exit)
+    exit(0);
+  else
+    execl ("/bin/echo", "echo", "-1", (char *)0);
+}
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  getpid();
+
+  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. */
+
+  test_exec_exit(1);
+
+  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..c4d3bef
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,230 @@
+# 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 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_breakpoint "increase_1"
+gdb_test "continue" "Breakpoint .*at.*" "continue to the function to finish"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval ('a'), gdb.selected_frame ())" \
+         "Temporary breakpoint.*" "set FinishBreakpoint"
+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.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer normal hit"
+
+# check FinishBreakpoint in main no allowed
+
+gdb_test "finish" "main.*" "return to main()"
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         "ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \
+         "check FinishBP not allowed in main"
+
+#
+# Test FinishBreakpoint with no debug symbol 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+set cond_line [gdb_get_line_number "Condition Break."]
+
+if ![runto "getpid"] then {
+    fail "Cannot run to getpid."
+    return 0
+}
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "continue" "SimpleFinishBreakpoint stop.*" "check FinishBreakpoint hit"
+gdb_test "python print finishBP.return_value" "None" "check return value without debug symbol"
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "python finishbp = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" \
+         "check FinishBP out of scope notification"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy 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_1(i,8)" "Breakpoint .* at .*" \
+         "set a conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" \
+         "set FinishBP in a breakpoint condition"
+gdb_test "continue" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+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"
+
+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 explicit inferior function call
+#
+
+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
+}
+
+# return address in dummy frame
+
+gdb_test "python TestExplicitBreakpoint('increase_1')" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase_1(&i)" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+
+# return address in normal frame
+
+delete_breakpoints
+gdb_test "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase(&i)" \
+         "SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*The program being debugged stopped while in a function called from GDB.*" \
+         "FinishBP stop at during explicit function call"
+
+
+#
+# Test FinishBreakpoint when inferior exits
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}
+
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
+
+#
+# Test FinishBreakpoint when inferior execs
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}     
+
+gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
+gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ 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..adf10b0
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,88 @@
+# 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 MyFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		gdb.FinishBreakpoint.__init__ (self, 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..."
+
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__(self, spec="test_1", internal=1)
+        self.silent = True
+        self.count = 0
+        print "TestBreakpoint init"
+        
+    def stop(self):
+    	self.count += 1
+    	try:
+        	TestFinishBreakpoint(gdb.newest_frame(), self.count)
+        except ValueError as e:
+        	print e
+        return False
+
+class TestFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame, count):
+    	self.count = count
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        
+        
+    def stop(self):
+        print "-->", self.number
+        if (self.count == 3):
+            print "test stop ... %d" % self.count
+            return True
+        else:
+            print "test don't stop %d" % self.count
+            return False 
+        
+    
+    def out_of_scope(self):
+        print "test didn't finish ... %d" % self.count
+
+class TestExplicitBreakpoint(gdb.Breakpoint):
+	def stop(self):
+		try:
+			SimpleFinishBreakpoint(gdb.newest_frame())
+		except ValueError as e:
+			print e
+		return False
+
+class SimpleFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		
+		print "SimpleFinishBreakpoint init"
+		
+	def stop(self):
+		print "SimpleFinishBreakpoint stop" 
+		return True
+	
+	def out_of_scope(self):
+		print "SimpleFinishBreakpoint out of scope..."
+
+print "Python script importedd"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.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-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
new file mode 100644
index 0000000..fae798c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -0,0 +1,65 @@
+# 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-breakpoint2"
+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
+}
+
+#
+# Check FinishBreakpoints against C++ exceptions
+#
+
+gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_breakpoint "throw_exception_1"
+gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
+
+gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.py b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
new file mode 100644
index 0000000..9ea4cf1
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
@@ -0,0 +1,34 @@
+# 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 ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        self.silent = True;
+        print "init ExceptionFinishBreakpoint"
+        
+    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"
-- 
1.7.6.4


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

* Re: [RFC] Python Finish Breakpoints
  2011-10-24 11:43                       ` Kevin Pouget
@ 2011-10-24 13:20                         ` Eli Zaretskii
  2011-10-24 14:30                           ` Kevin Pouget
  2011-10-24 15:31                         ` Kevin Pouget
  1 sibling, 1 reply; 74+ messages in thread
From: Eli Zaretskii @ 2011-10-24 13:20 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: tromey, gdb-patches

> From: Kevin Pouget <kevin.pouget@gmail.com>
> Date: Mon, 24 Oct 2011 11:14:42 +0200
> Cc: gdb-patches@sourceware.org
> 
> I've updated the doc and testsuite accordingly, and as far as I've
> seen, there is no regression on X86_64/Fedora
> (I need still need to improve my test environment)

Thanks.

> +@subsubsection Finish Breakpoints
> +
> +@cindex python finish breakpoints
> +@tindex gdb.FinishBreakpoint

No @node?  Why not?

> +extends @code{gdb.Breakpoint}. The underlying breakpoint will be disabled 
                                                 ^^^^^^^^^^
A typo.

> +and deleted when the execution will run out of the breakoint scope (i.e.@: 
                                                      ^^^^^^^^^
Another one.

> +breakpoint to become invisible to the user.  @xref{Breakpoints In Python}
> +for further details about this argument.

You need a comma after the braces (didn't "make info" bitch at you?).

> +In some circonstances (e.g.@: @code{longjmp}, C++ exceptions, @value{GDBN} 
           ^^^^^^^^^^^^^
A typo.  Also, we use "C@{++}" throughout the document, it looks
prettier in print.

> +@code{return} command, ...), a function may not properly terminate, and thus
                          ^^^
Please use @dots{} here instead of literal dots.
 
> +was not computable. This attribute is not writable.

Two blanks, please.

Okay with these changes.

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

* Re: [RFC] Python Finish Breakpoints
  2011-10-24 13:20                         ` Eli Zaretskii
@ 2011-10-24 14:30                           ` Kevin Pouget
  2011-10-24 17:14                             ` Eli Zaretskii
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-10-24 14:30 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: tromey, gdb-patches

On Mon, Oct 24, 2011 at 3:00 PM, Eli Zaretskii <eliz@gnu.org> wrote:
>> From: Kevin Pouget <kevin.pouget@gmail.com>
>> Date: Mon, 24 Oct 2011 11:14:42 +0200
>> Cc: gdb-patches@sourceware.org
>>
>> I've updated the doc and testsuite accordingly, and as far as I've
>> seen, there is no regression on X86_64/Fedora
>> (I need still need to improve my test environment)
>
> Thanks.

thanks for your review, and sorry for the typos ...

>> +@subsubsection Finish Breakpoints
>> +
>> +@cindex python finish breakpoints
>> +@tindex gdb.FinishBreakpoint
>
> No @node?  Why not?

my original idea was to insert "Finish Breakpoints" inside the
Breakpoint page, because they address (almost) the same idea, but
that's obviously not the right way to do!
I can add something like
@menu
...
* Finish Breakpoints in Python:: Setting Breakpoints on function
return using Python
...
@node Finish Breakpoints in Python

but can you/Tom confirm that you're happy with a new page/entry menu
for Finish Breakpoints?
It doesn't compile without the menu entry.

> ...
>> +breakpoint to become invisible to the user.  @xref{Breakpoints In Python}
>> +for further details about this argument.
>
> You need a comma after the braces (didn't "make info" bitch at you?).

no, now I make sure it compiles and looks fine on the pdf before submitting!
the comma is outside of the braces, so I guess it's not taken into
consideration by the compiler.

I've fixed it

>> +In some circonstances (e.g.@: @code{longjmp}, C++ exceptions, @value{GDBN}
>           ^^^^^^^^^^^^^
> A typo.  Also, we use "C@{++}" throughout the document, it looks
> prettier in print.

just for the record, it's "C@t{++}" which works

> Okay with these changes.

Thanks

I've also prepared a NEWS entry (I'll merge it with the next code
update). I assume the order is not important and put it next to the
other breakpoint-related entry; let me know if it's not the right
place:

diff --git a/gdb/NEWS b/gdb/NEWS
index 80c59f6..655e013 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -50,6 +50,10 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".

+  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
+     of a function.  This class is based on the "finish" command
+     available in the CLI.
+
   ** Type objects for struct and union types now allow access to
      the fields using standard Python dictionary (mapping) methods.
      For example, "some_type['myfield']" now works, as does

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

* Re: [RFC] Python Finish Breakpoints
  2011-10-24 11:43                       ` Kevin Pouget
  2011-10-24 13:20                         ` Eli Zaretskii
@ 2011-10-24 15:31                         ` Kevin Pouget
  2011-10-24 16:27                           ` Phil Muldoon
  1 sibling, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-10-24 15:31 UTC (permalink / raw)
  To: Tom Tromey, Eli Zaretskii; +Cc: gdb-patches

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

On Mon, Oct 24, 2011 at 11:14 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> On Fri, Oct 21, 2011 at 9:45 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
>> On Thu, Oct 20, 2011 at 10:41 PM, Tom Tromey <tromey@redhat.com> wrote:
>>>>>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
>>>
>>> Tom> You need to store a gdb.Type wrapper.
>>> Tom> A 'struct type' can also be invalidated when an objfile is destroyed.
>>>
>>> Kevin> I wouldn't mind, but I can't see how gdb.Type ensures validity, as far
>>> Kevin> as I've seen, there is no "is_valid" method and I can't and no further
>>> Kevin> verification during the Python -> C translation:
>>>
>>> When an objfile is destroyed, py-type.c:save_objfile_types is called.
>>> This makes a copy of the struct type underlying any relevant Type object.
>>
>> I'll take a look at that, I don't remember what was the problem!
>
> I understood that, and replaced 'struct type *' fields by the
> corresponding 'PyObject/Type' python objects
>
>>> [ out_of_scope_notif ]
>>> Kevin> - avoid calling `out_of_scope' every normal_stop when the breakpoint
>>> Kevin> is not anymore in the callstack
>>>
>>> It seems to me that manually managing this is not the best we could do.
>>>
>>> I was re-reading the code and I realized that this isn't actually
>>> working the way I had remembered it working:
>>>
>>> +A @fdb{finish breakpoint} is a breakpoint set at the return address of
>>> +a frame, based on the "finish command. @code{gdb.FinishBreakpoint} extends
>>> +@code{gdb.Breakpoint}
>>>
>>> This approach is fine; but once a frame is gone, it is gone -- we should
>>> just destroy the breakpoint at that point.  Maybe this would make the
>>> internal logic simpler as well.
>>
>> yes, I think you're right with that, "once a frame is gone, it is
>> gone", I actually don't use myself this 'permanent finish breakpoint'
>> feature, so I'll change it.
>> So basically, the BP must be deleted right after it has been it (maybe
>> through the 'temporary' mechanism). 'out_of_scope' will be triggered
>> in the same conditions as before, but instead of playing with
>> 'out_of_scope_notif' I'll delete the GDB breakpoint (thus invalidating
>> the Python BP)
>>
>> I'll post a patch going this way later today
>
> So I've changed the way FinishBreakpoints are 'garbage collected'.
> Now, they're more similar to Temporary Breakpoints, that is, they're
> deleted *soon* after they've been hit.
> Soon, because when Breakpoint.stop tells GDB *not* to stop at the
> [finish] breakpoint, there is no way to delete this breakpoint (and I
> guess that may be the reason why Temporary Breakpoints are not
> available in Python yet).
>
> So I disable it, and "b->disposition == disp_del" will ensure that the
> BP will be removed when it's possible.
>
> The `out_of_scope' callback will also delete the breakpoint.
>
>
> I've updated the doc and testsuite accordingly, and as far as I've
> seen, there is no regression on X86_64/Fedora
> (I need still need to improve my test environment)


there was indeed a little regression hidden in the middle of the
testsuite logs, which made "tbreak ... if condition" fail if the
condition was not satisfied at the first try.

I've fixed it with:

  /* If it's a temporary breakpoint.  */
  if (bpfinishpy_is_finish_bp(py_bp))
    {
      /* Can't delete it here.  */
      gdb_assert (b->disposition == disp_del);
      disable_breakpoint (b);
    }

which now only affects gdb.FinishBreakpoint-s.
They can be hit only once, in opposition with proper temporary
breakpoints which remain until their condition succeeds.

This patch also encompasses the doc modifications asked by Eli and a NEWS entry.


Thanks,

Kevin

2011-10-24  Kevin Pouget  <kevin.pouget@st.com>

	Introduce gdb.FinishBreakpoints in Python

	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-finishbreakpoint.o.
	(SUBDIR_PYTHON_SRCS): Add python/py-finishbreakpoint.c.
	Add build rule for this file.
	* breakpoint.h (struct breakpoint): New field: is_py_finish_bp.
	* breakpoint.c (init_raw_breakpoint_without_location):
	Initialize is_py_finish_bp.
	* infcmd.c (get_return_value): New function.
	(print_return_value): Split to create get_return_value.
	* inferior.h (get_return_value): New prototype.
	* infrun.c: Include python/python.h.
	(stop_registers): Mention FinishBreakpoint in description.
	(normal_stop): Set stop_registers if stopped at FinishBreakpoint.
	* python/py-breakpoint.c (breakpoint_object_type): Make non-static.
	(bppy_pending_object): Likewise
	(gdbpy_should_stop): Disable temporary breakpoints.
	(gdbpy_breakpoint_created): Set is_py_finish_bp is necessary.
	(struct breakpoint_object): Move to python-internal.h
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	* python/python-internal.h (breakpoint_object_type): Add as extern.
	(bppy_pending_object): Likewise.
	(typedef struct breakpoint_object) Removed.
	(struct breakpoint_object): Moved from py-breakpoint.c.
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(frame_object_to_frame_info): New prototype.
	(gdbpy_initialize_finishbreakpoints): New prototype.
	(bpfinishpy_is_finish_bp): Likewise.
	* python/py-finishbreakpoint.c: New file.
	* python/py-frame.c(frame_object_to_frame_info): Make non-static and
	accept PyObject instead of frame_object.
	(frapy_is_valid): Don't cast to frame_object.
	(frapy_name): Likewise.
	(frapy_type): Likewise.
	(frapy_unwind_stop_reason): Likewise.
	(frapy_pc): Likewise.
	(frapy_block): Likewise.
	(frapy_function): Likewise.
	(frapy_older): Likewise.
	(frapy_newer): Likewise.
	(frapy_find_sal): Likewise.
	(frapy_read_var): Likewise.
	(frapy_select): Likewise.
	* python/python.c (gdbpy_is_stopped_at_finish_bp): New noop function.
	(_initialize_python): Add gdbpy_initialize_finishbreakpoints.
	* python/python.h: Include breakpoint.h
	(gdbpy_is_stopped_at_finish_bp): New prototype.

doc/
	* gdb.texinfo (Breakpoints In Python): New subsection: Finish
	Breakpoints.

testsuite/
	* gdb.python/py-breakpoint.exp (mult_line): Define and use variable
	instead of line number.
	* gdb.python/py-finish-breakpoint.c: New file.
	* gdb.python/py-finish-breakpoint.exp: New file.
	* gdb.python/py-finish-breakpoint.py: New file.
	* gdb.python/py-finish-breakpoint2.cc: New file.
	* gdb.python/py-finish-breakpoint2.exp: New file.
	* gdb.python/py-finish-breakpoint2.py: New file.

[-- Attachment #2: 0001-Introduce-gdb.FinishBreakpoints.patch --]
[-- Type: text/x-patch, Size: 54730 bytes --]

From fb0c9e8802b7c79d540709829cddb72c31dae1f2 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Tue, 20 Sep 2011 13:59:23 +0200
Subject: [PATCH] Introduce gdb.FinishBreakpoints

---
 gdb/Makefile.in                                    |    6 +
 gdb/NEWS                                           |    4 +
 gdb/breakpoint.c                                   |    1 +
 gdb/breakpoint.h                                   |    3 +
 gdb/doc/gdb.texinfo                                |   48 +++
 gdb/infcmd.c                                       |   21 +-
 gdb/inferior.h                                     |    3 +
 gdb/infrun.c                                       |    9 +-
 gdb/python/py-breakpoint.c                         |   51 +--
 gdb/python/py-finishbreakpoint.c                   |  392 ++++++++++++++++++++
 gdb/python/py-frame.c                              |   32 +-
 gdb/python/python-internal.h                       |   46 +++-
 gdb/python/python.c                                |    6 +
 gdb/python/python.h                                |    2 +
 gdb/testsuite/gdb.python/py-breakpoint.exp         |    7 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.c    |   97 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp  |  230 ++++++++++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py   |   88 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc  |   59 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   65 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.py  |   34 ++
 21 files changed, 1139 insertions(+), 65 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 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
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.py

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 32d8ef3..6cedf12 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,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 \
@@ -318,6 +319,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 \
@@ -2103,6 +2105,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/NEWS b/gdb/NEWS
index 80c59f6..655e013 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -50,6 +50,10 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".
 
+  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
+     of a function.  This class is based on the "finish" command
+     available in the CLI. 
+
   ** Type objects for struct and union types now allow access to
      the fields using standard Python dictionary (mapping) methods.
      For example, "some_type['myfield']" now works, as does
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index ba1b08f..ffd2ef6 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5700,6 +5700,7 @@ init_raw_breakpoint_without_location (struct breakpoint *b,
   b->frame_id = null_frame_id;
   b->condition_not_parsed = 0;
   b->py_bp_object = NULL;
+  b->is_py_finish_bp = 0;
   b->related_breakpoint = b;
 }
 
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index c2116e2..3f232e7 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -625,6 +625,9 @@ struct breakpoint
        can sometimes be NULL for enabled GDBs as not all breakpoint
        types are tracked by the Python scripting API.  */
     struct breakpoint_object *py_bp_object;
+
+    /* TRUE is py_bp_object is a gdb.FinishBreakpoint object .  */
+    int is_py_finish_bp;
   };
 
 /* An instance of this type is used to represent a watchpoint.  It
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 69e2bda..ad17ef3 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21289,6 +21289,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Symbol Tables In Python::     Python representation of symbol tables.
 * Lazy Strings In Python::      Python representation of lazy strings.
 * Breakpoints In Python::       Manipulating breakpoints using Python.
+* Finish Breakpoints in Python:: Setting Breakpoints on function return using Python
 @end menu
 
 @node Basic Python
@@ -24082,6 +24083,53 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@node Finish Breakpoints in Python
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A finish breakpoint is a temporary breakpoint set at the return address of
+a frame, based on the @code{finish} command.  @code{gdb.FinishBreakpoint}
+extends @code{gdb.Breakpoint}.  The underlying breakpoint will be disabled 
+and deleted when the execution will run out of the breakpoint scope (i.e.@: 
+@code{Breakpoint.stop} or @code{FinishBreakpoint.out_of_scope} triggered).
+
+@defun FinishBreakpoint.__init__ (@var{frame}, @r{[}@var{internal}@r{]})
+Create a finish breakpoint at the return address of the @code{gdb.Frame}
+object @var{frame}.  The optional @var{internal} argument allows the
+breakpoint to become invisible to the user.  @xref{Breakpoints In Python},
+for further details about this argument.
+@end defun
+
+@defun FinishBreakpoint.out_of_scope (self)
+In some circumstances (e.g.@: @code{longjmp}, C@t{++} exceptions, @value{GDBN} 
+@code{return} command, @dots{}), a function may not properly terminate, and thus 
+never hit the finish breakpoint.  When @value{GDBN} notices such a 
+situation, the @code{out_of_scope} callback will be triggered.
+
+You may want to sub-class @code{gdb.FinishBreakpoint} and override this
+method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+    def stop (self):
+        print "normal finish"
+        return True
+    
+    def out_of_scope ():
+        print "abnormal finish"
+@end smallexample 
+@end defun
+
+@defvar FinishBreakpoint.return_value
+When @value{GDBN} is stopped at a finish breakpoint and the frame 
+used to build the @code{gdb.FinishBreakpoint} object had debug symbols, this
+attribute will contain a @code{gdb.Value} object corresponding to the return
+value of the function.  The value will be @code{None} if the return value
+was not computable.  This attribute is not writable.
+@end defvar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 3e8bf5d..33ce1fc 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1413,14 +1413,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.  */
+/* Return 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;
   struct ui_out *uiout = current_uiout;
 
@@ -1451,6 +1449,19 @@ 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;
+  struct ui_out *uiout = current_uiout;
+
   if (value)
     {
       struct value_print_options opts;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..f198696 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -269,6 +269,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 db6a5d1..5ce0c94 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -56,6 +56,7 @@
 #include "tracepoint.h"
 #include "continuations.h"
 #include "interps.h"
+#include "python/python.h"
 
 /* Prototypes for local functions */
 
@@ -365,7 +366,8 @@ show_stop_on_solib_events (struct ui_file *file, int from_tty,
 int stop_after_trap;
 
 /* Save register contents here when executing a "finish" command or are
-   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set.
+   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set
+   or a Python FinishBreakpoint has been hit.
    Thus this contains the return value from the called function (assuming
    values are returned in a register).  */
 
@@ -5985,8 +5987,9 @@ 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
-      && execution_direction != EXEC_REVERSE)
+  if (gdbpy_is_stopped_at_finish_bp (inferior_thread ()->control.stop_bpstat)
+      || (inferior_thread ()->control.proceed_to_finish
+          && execution_direction != EXEC_REVERSE))
     {
       /* This should not be necessary.  */
       if (stop_registers)
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index daceb99..eaf1015 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
 {
@@ -757,6 +723,15 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
       else
 	gdbpy_print_stack ();
     }
+
+  /* If it's a temporary breakpoint.  */
+  if (bpfinishpy_is_finish_bp(py_bp))
+    {
+      /* Can't delete it here.  */
+      gdb_assert (b->disposition == disp_del);
+      disable_breakpoint (b);
+    }
+
   do_cleanups (cleanup);
 
   return stop;
@@ -821,6 +796,8 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->bp->py_bp_object = newbp;
       Py_INCREF (newbp);
       ++bppy_live;
+
+      bp->is_py_finish_bp = bpfinishpy_is_finish_bp ((PyObject *) newbp);
     }
   else
     {
@@ -980,7 +957,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..1ffaa9b
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,392 @@
+/* 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;
+  /* gdb.Type object of the function finished by this breakpoint.  */
+  PyObject *function_type;
+  /* gdb.Type object of the value return by the breakpointed function.  */
+  PyObject *return_type;
+  /* When stopped at this FinishBreakpoint, value returned by the function;
+     Py_None if the value is not computable;
+     NULL if GDB is not stopped at a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* Triggered when GDB stops at PY_BP. Computes and caches the `return_value',
+   if possible.  */
+
+static void
+bpfinish_stopped_at_finish_bp (struct finish_breakpoint_object *py_bp)
+{
+  if (py_bp->return_type)
+    {
+      struct value *ret =
+          get_return_value (type_object_to_type (py_bp->function_type),
+                            type_object_to_type (py_bp->return_type));
+      if (ret)
+        {
+          py_bp->return_value = value_to_value_object (ret);
+        }
+      else
+        py_bp->return_value = Py_None;
+    }
+}
+
+/* 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;
+
+  if (self_finishbp->return_type == NULL)
+    Py_RETURN_NONE;
+
+  /* Check if we have a cached value.  */
+  if (!self_finishbp->return_value)
+    {
+      bpstat bs;
+
+      BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
+
+      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)
+              bpfinish_stopped_at_finish_bp (self_finishbp);
+        }
+    }
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* If STOP_BPSTAT contains a Python breakpoint whose type is TYPE, returns
+   this breakpoint. Returns NULL otherwise.  */
+
+struct breakpoint *
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  bpstat bs;
+
+  for (bs = stop_bpstat; bs; bs = bs->next)
+    {
+      if (bs->breakpoint_at && bs->breakpoint_at->is_py_finish_bp)
+        return bs->breakpoint_at;
+    }
+
+  return NULL;
+}
+
+/* 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;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  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;
+    }
+  else if (get_frame_type(prev_frame) == DUMMY_FRAME)
+      {
+        PyErr_SetString (PyExc_ValueError,
+                  _("\"FinishBreakpoint\" cannot be set on a dummy frame."));
+        return -1;
+      }
+
+  frame_id = get_frame_id (prev_frame);
+  if (frame_id_eq (frame_id, null_frame_id))
+    goto invalid_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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  self_bpfinish->return_type = NULL;
+  self_bpfinish->function_type = NULL;
+  if (get_frame_pc_if_available (frame, &pc))
+    {
+      function = find_pc_function (pc);
+      if (function != NULL)
+        {
+          struct type *ret_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+          /* Remember only non-VOID return types.  */
+          if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
+            {
+              self_bpfinish->return_type = type_to_type_object (ret_type);
+              self_bpfinish->function_type =
+                  type_to_type_object (SYMBOL_TYPE (function));
+            }
+        }
+    }
+
+  bppy_pending_object = self_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, -1,
+                         0,
+                         1 /*temp_flag*/,
+                         bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         &bkpt_breakpoint_ops,
+                         0, 1, internal_bp);
+    }
+  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;
+  
+  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.  */
+
+int
+bpfinishpy_is_finish_bp (PyObject *obj)
+{
+  return obj != NULL
+         && PyObject_TypeCheck (obj, &finish_breakpoint_object_type);
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack. Triggers the method OUT_OF_SCOPE if implemented,
+   then delete the breakpoint.  */
+
+static void
+bpfinishpy_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))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+
+  delete_breakpoint (bpfinish_obj->py_bp.bp);
+}
+
+/* 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 and its frame is
+     not anymore in the current callstack.  */
+  if (bpfinishpy_is_finish_bp (py_bp))
+    {
+      struct finish_breakpoint_object *finish_bp =
+          (struct finish_breakpoint_object *) py_bp;
+
+      if (b == bp_stopped)
+        bpfinish_stopped_at_finish_bp (finish_bp);
+      else if (b->pspace == current_inferior()->pspace
+           && (!target_has_registers
+               || frame_find_by_id(b->frame_id) == NULL))
+        {
+          bpfinishpy_out_of_scope (finish_bp);
+        }
+    }
+  
+  do_cleanups (cleanup);
+  
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run outg
+   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 PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "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 398ce84..c4494cb 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);
     }
@@ -476,12 +477,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 e593612..46f08ed 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -120,9 +120,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
 {
@@ -188,6 +226,9 @@ struct value *convert_value_from_python (PyObject *obj);
 struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
+struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);
+
+int bpfinishpy_is_finish_bp (PyObject *obj);
 
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
@@ -202,6 +243,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 df8e3d5..db23bbf 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1054,6 +1054,11 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj)
 		    "scripting is not supported."));
 }
 
+struct breakpoint *
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  return NULL;
+}
 #endif /* HAVE_PYTHON */
 
 \f
@@ -1243,6 +1248,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..e341d76 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);
 
+struct breakpoint *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 e0dd087..0e3adbd 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.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..5b708e3
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,97 @@
+/* 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>
+#include <stdlib.h>
+#include <unistd.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);
+}
+
+void
+test_exec_exit(int do_exit)
+{
+  if (do_exit)
+    exit(0);
+  else
+    execl ("/bin/echo", "echo", "-1", (char *)0);
+}
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  getpid();
+
+  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. */
+
+  test_exec_exit(1);
+
+  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..c4d3bef
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,230 @@
+# 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 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_breakpoint "increase_1"
+gdb_test "continue" "Breakpoint .*at.*" "continue to the function to finish"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval ('a'), gdb.selected_frame ())" \
+         "Temporary breakpoint.*" "set FinishBreakpoint"
+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.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer normal hit"
+
+# check FinishBreakpoint in main no allowed
+
+gdb_test "finish" "main.*" "return to main()"
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         "ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \
+         "check FinishBP not allowed in main"
+
+#
+# Test FinishBreakpoint with no debug symbol 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+set cond_line [gdb_get_line_number "Condition Break."]
+
+if ![runto "getpid"] then {
+    fail "Cannot run to getpid."
+    return 0
+}
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "continue" "SimpleFinishBreakpoint stop.*" "check FinishBreakpoint hit"
+gdb_test "python print finishBP.return_value" "None" "check return value without debug symbol"
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "python finishbp = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" \
+         "check FinishBP out of scope notification"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy 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_1(i,8)" "Breakpoint .* at .*" \
+         "set a conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" \
+         "set FinishBP in a breakpoint condition"
+gdb_test "continue" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+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"
+
+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 explicit inferior function call
+#
+
+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
+}
+
+# return address in dummy frame
+
+gdb_test "python TestExplicitBreakpoint('increase_1')" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase_1(&i)" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+
+# return address in normal frame
+
+delete_breakpoints
+gdb_test "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase(&i)" \
+         "SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*The program being debugged stopped while in a function called from GDB.*" \
+         "FinishBP stop at during explicit function call"
+
+
+#
+# Test FinishBreakpoint when inferior exits
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}
+
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
+
+#
+# Test FinishBreakpoint when inferior execs
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}     
+
+gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
+gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ 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..adf10b0
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,88 @@
+# 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 MyFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		gdb.FinishBreakpoint.__init__ (self, 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..."
+
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__(self, spec="test_1", internal=1)
+        self.silent = True
+        self.count = 0
+        print "TestBreakpoint init"
+        
+    def stop(self):
+    	self.count += 1
+    	try:
+        	TestFinishBreakpoint(gdb.newest_frame(), self.count)
+        except ValueError as e:
+        	print e
+        return False
+
+class TestFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame, count):
+    	self.count = count
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        
+        
+    def stop(self):
+        print "-->", self.number
+        if (self.count == 3):
+            print "test stop ... %d" % self.count
+            return True
+        else:
+            print "test don't stop %d" % self.count
+            return False 
+        
+    
+    def out_of_scope(self):
+        print "test didn't finish ... %d" % self.count
+
+class TestExplicitBreakpoint(gdb.Breakpoint):
+	def stop(self):
+		try:
+			SimpleFinishBreakpoint(gdb.newest_frame())
+		except ValueError as e:
+			print e
+		return False
+
+class SimpleFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		
+		print "SimpleFinishBreakpoint init"
+		
+	def stop(self):
+		print "SimpleFinishBreakpoint stop" 
+		return True
+	
+	def out_of_scope(self):
+		print "SimpleFinishBreakpoint out of scope..."
+
+print "Python script importedd"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.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-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
new file mode 100644
index 0000000..fae798c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -0,0 +1,65 @@
+# 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-breakpoint2"
+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
+}
+
+#
+# Check FinishBreakpoints against C++ exceptions
+#
+
+gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_breakpoint "throw_exception_1"
+gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
+
+gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.py b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
new file mode 100644
index 0000000..9ea4cf1
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
@@ -0,0 +1,34 @@
+# 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 ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        self.silent = True;
+        print "init ExceptionFinishBreakpoint"
+        
+    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"
-- 
1.7.6.4


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

* Re: [RFC] Python Finish Breakpoints
  2011-10-24 15:31                         ` Kevin Pouget
@ 2011-10-24 16:27                           ` Phil Muldoon
  2011-10-25 11:05                             ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Phil Muldoon @ 2011-10-24 16:27 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: Tom Tromey, Eli Zaretskii, gdb-patches

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

I have some comments regarding the Python bits.
>  
> +  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
> +     of a function.  This class is based on the "finish" command
> +     available in the CLI. 
> +
>    ** Type objects for struct and union types now allow access to
>       the fields using standard Python dictionary (mapping) methods.
>       For example, "some_type['myfield']" now works, as does
> diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
> index ba1b08f..ffd2ef6 100644
> --- a/gdb/breakpoint.c
> +++ b/gdb/breakpoint.c
> @@ -5700,6 +5700,7 @@ init_raw_breakpoint_without_location (struct breakpoint *b,
>    b->frame_id = null_frame_id;
>    b->condition_not_parsed = 0;
>    b->py_bp_object = NULL;
> +  b->is_py_finish_bp = 0;

Is there any reason why this need to be in the breakpoint struct? I
think this should be encapsulated in breakpoint PyObject, and a accessor
method provided for it.  As a finish breakpoint can only ever be
instantiated by Python, there will always be a py_bp_object attached to
make the call.

> +/* 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;
> +  /* gdb.Type object of the function finished by this breakpoint.  */
> +  PyObject *function_type;
> +  /* gdb.Type object of the value return by the breakpointed function.  */
> +  PyObject *return_type;
> +  /* When stopped at this FinishBreakpoint, value returned by the function;
> +     Py_None if the value is not computable;
> +     NULL if GDB is not stopped at a FinishBreakpoint.  */
> +  PyObject *return_value;
> +};

I think these comments should wrap? They wrap for me in emacs.  

Cheers

Phil

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

* Re: [RFC] Python Finish Breakpoints
  2011-10-24 14:30                           ` Kevin Pouget
@ 2011-10-24 17:14                             ` Eli Zaretskii
  0 siblings, 0 replies; 74+ messages in thread
From: Eli Zaretskii @ 2011-10-24 17:14 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: tromey, gdb-patches

> From: Kevin Pouget <kevin.pouget@gmail.com>
> Date: Mon, 24 Oct 2011 16:26:28 +0200
> Cc: tromey@redhat.com, gdb-patches@sourceware.org
> 
> I can add something like
> @menu
> ...
> * Finish Breakpoints in Python:: Setting Breakpoints on function
> return using Python
> ...
> @node Finish Breakpoints in Python
> 
> but can you/Tom confirm that you're happy with a new page/entry menu
> for Finish Breakpoints?

It's okay, but please format it like this:

* Finish Breakpoints in Python:: Setting Breakpoints on function return
                                 using Python

and make sure you use only blanks, not TABs, to indent the second
line.

> It doesn't compile without the menu entry.

Yes, you need that for the Texinfo structuring commands to be happy.

> >> +In some circonstances (e.g.@: @code{longjmp}, C++ exceptions, @value{GDBN}
> >           ^^^^^^^^^^^^^
> > A typo.  Also, we use "C@{++}" throughout the document, it looks
> > prettier in print.
> 
> just for the record, it's "C@t{++}" which works

Yes, of course.  It was a typo on my part ;-)

> I've also prepared a NEWS entry (I'll merge it with the next code
> update). I assume the order is not important and put it next to the
> other breakpoint-related entry; let me know if it's not the right
> place:

It's fine, thanks.

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

* Re: [RFC] Python Finish Breakpoints
  2011-10-24 16:27                           ` Phil Muldoon
@ 2011-10-25 11:05                             ` Kevin Pouget
  2011-10-25 11:37                               ` Phil Muldoon
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-10-25 11:05 UTC (permalink / raw)
  To: pmuldoon; +Cc: Tom Tromey, gdb-patches

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

On Mon, Oct 24, 2011 at 6:06 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
> Kevin Pouget <kevin.pouget@gmail.com> writes:
>
> I have some comments regarding the Python bits.

Thanks for that, I replied inline

>>
>> +  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
>> +     of a function.  This class is based on the "finish" command
>> +     available in the CLI.
>> +
>>    ** Type objects for struct and union types now allow access to
>>       the fields using standard Python dictionary (mapping) methods.
>>       For example, "some_type['myfield']" now works, as does
>> diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
>> index ba1b08f..ffd2ef6 100644
>> --- a/gdb/breakpoint.c
>> +++ b/gdb/breakpoint.c
>> @@ -5700,6 +5700,7 @@ init_raw_breakpoint_without_location (struct breakpoint *b,
>>    b->frame_id = null_frame_id;
>>    b->condition_not_parsed = 0;
>>    b->py_bp_object = NULL;
>> +  b->is_py_finish_bp = 0;
>
> Is there any reason why this need to be in the breakpoint struct? I
> think this should be encapsulated in breakpoint PyObject, and a accessor
> method provided for it.  As a finish breakpoint can only ever be
> instantiated by Python, there will always be a py_bp_object attached to
> make the call.

just to put it back in context (in was back in May ...), here is the
rational behind the flag:

On Thu, May 19, 2011 at 6:20 PM, Tom Tromey <tromey@redhat.com> wrote:
> gdbpy_is_stopped_at_finish_bp is not safe to call here -- it assumes the
> GIL has been acquired, which it has not.  I would rather it not be changed
> to acquire the GIL, however.  I think one of two other approaches would
> be preferable.
>
> One way you could handle this is to add a new constant to enum bptype.
> This is likely to be pretty invasive.
>
> Another way would be to add a flag to the struct breakpoint itself.
>
> Yet another way would be a new breakpoint_ops method.

--> PyObject_TypeCheck (obj, &finish_breakpoint_object_type) is not
safe to call everywhere


I've refactored the code according to your comment anyway, it make
sense, so now there are two version:

bpfinishpy_is_finish_bp (PyObject *obj)   --> strong one, used in
"gdbpy_breakpoint_created"
bpfinishpy_bp_is_finish_bp (struct breakpoint_object *bp_obj) -->
'weak' one, used in 'gdbpy_is_stopped_at_finish_bp', does't require
the GIL

>> +/* 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;
>> +  /* gdb.Type object of the function finished by this breakpoint.  */
>> +  PyObject *function_type;
>> +  /* gdb.Type object of the value return by the breakpointed function.  */
>> +  PyObject *return_type;
>> +  /* When stopped at this FinishBreakpoint, value returned by the function;
>> +     Py_None if the value is not computable;
>> +     NULL if GDB is not stopped at a FinishBreakpoint.  */
>> +  PyObject *return_value;
>> +};
>
> I think these comments should wrap? They wrap for me in emacs.

I'm not sure about the exact meaning of 'wrap' here, but I assume it's
about the new line between computable and NULL; I've reformatted it.


Thanks for your comments,

Kevin



2011-10-25  Kevin Pouget  <kevin.pouget@st.com>

	Introduce gdb.FinishBreakpoints in Python

	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-finishbreakpoint.o.
	(SUBDIR_PYTHON_SRCS): Add python/py-finishbreakpoint.c.
	Add build rule for this file.
	* infcmd.c (get_return_value): New function.
	(print_return_value): Split to create get_return_value.
	* inferior.h (get_return_value): New prototype.
	* infrun.c: Include python/python.h.
	(stop_registers): Mention FinishBreakpoint in description.
	(normal_stop): Set stop_registers if stopped at FinishBreakpoint.
	* python/py-breakpoint.c (breakpoint_object_type): Make non-static.
	(bppy_pending_object): Likewise
	(gdbpy_should_stop): Disable temporary breakpoints.
	(gdbpy_breakpoint_created): Set is_py_finish_bp is necessary.
	(struct breakpoint_object): Move to python-internal.h
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(gdbpy_breakpoint_created): Initialize is_finish_bp.
	* python/python-internal.h (breakpoint_object_type): Add as extern.
	(bppy_pending_object): Likewise.
	(typedef struct breakpoint_object) Removed.
	(struct breakpoint_object): Moved from py-breakpoint.c.
	Add field is_finish_bp.
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(frame_object_to_frame_info): New prototype.
	(gdbpy_initialize_finishbreakpoints): New prototype.
	(bpfinishpy_bp_is_finish_bp): Likewise.
	(bpfinishpy_is_finish_bp): Likewise.
	* python/py-finishbreakpoint.c: New file.
	* python/py-frame.c(frame_object_to_frame_info): Make non-static and
	accept PyObject instead of frame_object.
	(frapy_is_valid): Don't cast to frame_object.
	(frapy_name): Likewise.
	(frapy_type): Likewise.
	(frapy_unwind_stop_reason): Likewise.
	(frapy_pc): Likewise.
	(frapy_block): Likewise.
	(frapy_function): Likewise.
	(frapy_older): Likewise.
	(frapy_newer): Likewise.
	(frapy_find_sal): Likewise.
	(frapy_read_var): Likewise.
	(frapy_select): Likewise.
	* python/python.c (gdbpy_is_stopped_at_finish_bp): New noop function.
	(_initialize_python): Add gdbpy_initialize_finishbreakpoints.
	* python/python.h: Include breakpoint.h
	(gdbpy_is_stopped_at_finish_bp): New prototype.

doc/
	* gdb.texinfo (Breakpoints In Python): New subsection: Finish
	Breakpoints.
	(Python API): Add menu entry for Finish Breakpoints

testsuite/
	* gdb.python/py-breakpoint.exp (mult_line): Define and use variable
	instead of line number.
	* gdb.python/py-finish-breakpoint.c: New file.
	* gdb.python/py-finish-breakpoint.exp: New file.
	* gdb.python/py-finish-breakpoint.py: New file.
	* gdb.python/py-finish-breakpoint2.cc: New file.
	* gdb.python/py-finish-breakpoint2.exp: New file.
	* gdb.python/py-finish-breakpoint2.py: New file.

[-- Attachment #2: 0001-Introduce-gdb.FinishBreakpoints.patch --]
[-- Type: text/x-patch, Size: 54287 bytes --]

From 1bcf68eef465b62555768ec3bca2dfa3e5858c93 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Tue, 20 Sep 2011 13:59:23 +0200
Subject: [PATCH] Introduce gdb.FinishBreakpoints

---
 gdb/Makefile.in                                    |    6 +
 gdb/NEWS                                           |    4 +
 gdb/doc/gdb.texinfo                                |   49 +++
 gdb/infcmd.c                                       |   21 +-
 gdb/inferior.h                                     |    3 +
 gdb/infrun.c                                       |    9 +-
 gdb/python/py-breakpoint.c                         |   51 +--
 gdb/python/py-finishbreakpoint.c                   |  404 ++++++++++++++++++++
 gdb/python/py-frame.c                              |   32 +-
 gdb/python/python-internal.h                       |   50 +++-
 gdb/python/python.c                                |    6 +
 gdb/python/python.h                                |    2 +
 gdb/testsuite/gdb.python/py-breakpoint.exp         |    7 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.c    |   97 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp  |  230 +++++++++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py   |   88 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc  |   59 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   65 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.py  |   34 ++
 19 files changed, 1152 insertions(+), 65 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 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
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.py

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 32d8ef3..6cedf12 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,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 \
@@ -318,6 +319,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 \
@@ -2103,6 +2105,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/NEWS b/gdb/NEWS
index 80c59f6..655e013 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -50,6 +50,10 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".
 
+  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
+     of a function.  This class is based on the "finish" command
+     available in the CLI. 
+
   ** Type objects for struct and union types now allow access to
      the fields using standard Python dictionary (mapping) methods.
      For example, "some_type['myfield']" now works, as does
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 69e2bda..f82b7fc 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21289,6 +21289,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Symbol Tables In Python::     Python representation of symbol tables.
 * Lazy Strings In Python::      Python representation of lazy strings.
 * Breakpoints In Python::       Manipulating breakpoints using Python.
+* Finish Breakpoints in Python:: Setting Breakpoints on function return
+                                using Python
 @end menu
 
 @node Basic Python
@@ -24082,6 +24084,53 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@node Finish Breakpoints in Python
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A finish breakpoint is a temporary breakpoint set at the return address of
+a frame, based on the @code{finish} command.  @code{gdb.FinishBreakpoint}
+extends @code{gdb.Breakpoint}.  The underlying breakpoint will be disabled 
+and deleted when the execution will run out of the breakpoint scope (i.e.@: 
+@code{Breakpoint.stop} or @code{FinishBreakpoint.out_of_scope} triggered).
+
+@defun FinishBreakpoint.__init__ (@var{frame}, @r{[}@var{internal}@r{]})
+Create a finish breakpoint at the return address of the @code{gdb.Frame}
+object @var{frame}.  The optional @var{internal} argument allows the
+breakpoint to become invisible to the user.  @xref{Breakpoints In Python},
+for further details about this argument.
+@end defun
+
+@defun FinishBreakpoint.out_of_scope (self)
+In some circumstances (e.g.@: @code{longjmp}, C@t{++} exceptions, @value{GDBN} 
+@code{return} command, @dots{}), a function may not properly terminate, and thus 
+never hit the finish breakpoint.  When @value{GDBN} notices such a 
+situation, the @code{out_of_scope} callback will be triggered.
+
+You may want to sub-class @code{gdb.FinishBreakpoint} and override this
+method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+    def stop (self):
+        print "normal finish"
+        return True
+    
+    def out_of_scope ():
+        print "abnormal finish"
+@end smallexample 
+@end defun
+
+@defvar FinishBreakpoint.return_value
+When @value{GDBN} is stopped at a finish breakpoint and the frame 
+used to build the @code{gdb.FinishBreakpoint} object had debug symbols, this
+attribute will contain a @code{gdb.Value} object corresponding to the return
+value of the function.  The value will be @code{None} if the return value
+was not computable.  This attribute is not writable.
+@end defvar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 3e8bf5d..33ce1fc 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1413,14 +1413,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.  */
+/* Return 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;
   struct ui_out *uiout = current_uiout;
 
@@ -1451,6 +1449,19 @@ 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;
+  struct ui_out *uiout = current_uiout;
+
   if (value)
     {
       struct value_print_options opts;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..f198696 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -269,6 +269,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 db6a5d1..5ce0c94 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -56,6 +56,7 @@
 #include "tracepoint.h"
 #include "continuations.h"
 #include "interps.h"
+#include "python/python.h"
 
 /* Prototypes for local functions */
 
@@ -365,7 +366,8 @@ show_stop_on_solib_events (struct ui_file *file, int from_tty,
 int stop_after_trap;
 
 /* Save register contents here when executing a "finish" command or are
-   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set.
+   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set
+   or a Python FinishBreakpoint has been hit.
    Thus this contains the return value from the called function (assuming
    values are returned in a register).  */
 
@@ -5985,8 +5987,9 @@ 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
-      && execution_direction != EXEC_REVERSE)
+  if (gdbpy_is_stopped_at_finish_bp (inferior_thread ()->control.stop_bpstat)
+      || (inferior_thread ()->control.proceed_to_finish
+          && execution_direction != EXEC_REVERSE))
     {
       /* This should not be necessary.  */
       if (stop_registers)
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index daceb99..6d1c6c1 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
 {
@@ -757,6 +723,15 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
       else
 	gdbpy_print_stack ();
     }
+
+  /* If it's a temporary breakpoint.  */
+  if (bpfinishpy_is_finish_bp(py_bp))
+    {
+      /* Can't delete it here.  */
+      gdb_assert (b->disposition == disp_del);
+      disable_breakpoint (b);
+    }
+
   do_cleanups (cleanup);
 
   return stop;
@@ -821,6 +796,8 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->bp->py_bp_object = newbp;
       Py_INCREF (newbp);
       ++bppy_live;
+
+      newbp->is_finish_bp = bpfinishpy_is_finish_bp ((PyObject *) newbp);
     }
   else
     {
@@ -980,7 +957,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..658f139
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,404 @@
+/* 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.  */
+  breakpoint_object py_bp;
+  /* gdb.Type object of the function finished by this breakpoint.  */
+  PyObject *function_type;
+  /* gdb.Type object of the value return by the breakpointed function.  */
+  PyObject *return_type;
+  /* When stopped at this FinishBreakpoint, value returned by the function;
+     Py_None if the value is not computable; NULL if GDB is not stopped at
+     a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* Triggered when GDB stops at PY_BP. Computes and caches the `return_value',
+   if possible.  */
+
+static void
+bpfinish_stopped_at_finish_bp (struct finish_breakpoint_object *py_bp)
+{
+  if (py_bp->return_type)
+    {
+      struct value *ret =
+          get_return_value (type_object_to_type (py_bp->function_type),
+                            type_object_to_type (py_bp->return_type));
+      if (ret)
+        {
+          py_bp->return_value = value_to_value_object (ret);
+        }
+      else
+        py_bp->return_value = Py_None;
+    }
+}
+
+/* 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;
+
+  if (self_finishbp->return_type == NULL)
+    Py_RETURN_NONE;
+
+  /* Check if we have a cached value.  */
+  if (!self_finishbp->return_value)
+    {
+      bpstat bs;
+
+      BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
+
+      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)
+              bpfinish_stopped_at_finish_bp (self_finishbp);
+        }
+    }
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* If STOP_BPSTAT contains a Python breakpoint whose type is TYPE, returns
+   this breakpoint. Returns NULL otherwise.  */
+
+struct breakpoint *
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  bpstat bs;
+
+  for (bs = stop_bpstat; bs; bs = bs->next)
+    {
+      if (bs->breakpoint_at
+          && bpfinishpy_bp_is_finish_bp ((breakpoint_object *)
+                                         bs->breakpoint_at->py_bp_object))
+        return bs->breakpoint_at;
+    }
+
+  return NULL;
+}
+
+/* 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;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  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;
+    }
+  else if (get_frame_type(prev_frame) == DUMMY_FRAME)
+      {
+        PyErr_SetString (PyExc_ValueError,
+                  _("\"FinishBreakpoint\" cannot be set on a dummy frame."));
+        return -1;
+      }
+
+  frame_id = get_frame_id (prev_frame);
+  if (frame_id_eq (frame_id, null_frame_id))
+    goto invalid_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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  self_bpfinish->return_type = NULL;
+  self_bpfinish->function_type = NULL;
+  if (get_frame_pc_if_available (frame, &pc))
+    {
+      function = find_pc_function (pc);
+      if (function != NULL)
+        {
+          struct type *ret_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+          /* Remember only non-VOID return types.  */
+          if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
+            {
+              self_bpfinish->return_type = type_to_type_object (ret_type);
+              self_bpfinish->function_type =
+                  type_to_type_object (SYMBOL_TYPE (function));
+            }
+        }
+    }
+
+  bppy_pending_object = self_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, -1,
+                         0,
+                         1 /*temp_flag*/,
+                         bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         &bkpt_breakpoint_ops,
+                         0, 1, internal_bp);
+    }
+  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;
+  
+  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.  */
+
+int
+bpfinishpy_is_finish_bp (PyObject *obj)
+{
+  return obj != NULL
+         && PyObject_TypeCheck (obj, &finish_breakpoint_object_type);
+}
+
+/* Returns 1 if PY_OBJ is not NULL and references a FinishBreakpoint object.
+   This version doesn't assume that GIL has been acquired.  */
+
+int
+bpfinishpy_bp_is_finish_bp (breakpoint_object *bp_obj)
+{
+  return bp_obj != NULL
+         && bp_obj->is_finish_bp;
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack. Triggers the method OUT_OF_SCOPE if implemented,
+   then delete the breakpoint.  */
+
+static void
+bpfinishpy_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))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+
+  delete_breakpoint (bpfinish_obj->py_bp.bp);
+}
+
+/* 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 and its frame is
+     not anymore in the current callstack.  */
+  if (bpfinishpy_is_finish_bp (py_bp))
+    {
+      struct finish_breakpoint_object *finish_bp =
+          (struct finish_breakpoint_object *) py_bp;
+
+      if (b == bp_stopped)
+        bpfinish_stopped_at_finish_bp (finish_bp);
+      else if (b->pspace == current_inferior()->pspace
+           && (!target_has_registers
+               || frame_find_by_id(b->frame_id) == NULL))
+        {
+          bpfinishpy_out_of_scope (finish_bp);
+        }
+    }
+  
+  do_cleanups (cleanup);
+  
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run outg
+   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 PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "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 398ce84..c4494cb 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);
     }
@@ -476,12 +477,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 e593612..f899b77 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -120,9 +120,50 @@ 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;
+
+  /* 1 is this is a FinishBreakpoint object, 0 otherwise.  */
+  int is_finish_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
 {
@@ -188,6 +229,10 @@ struct value *convert_value_from_python (PyObject *obj);
 struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
+struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);
+
+int bpfinishpy_is_finish_bp (PyObject *obj);
+int bpfinishpy_bp_is_finish_bp (breakpoint_object *bp_obj);
 
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
@@ -202,6 +247,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 df8e3d5..db23bbf 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1054,6 +1054,11 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj)
 		    "scripting is not supported."));
 }
 
+struct breakpoint *
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  return NULL;
+}
 #endif /* HAVE_PYTHON */
 
 \f
@@ -1243,6 +1248,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..e341d76 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);
 
+struct breakpoint *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 e0dd087..0e3adbd 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.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..5b708e3
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,97 @@
+/* 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>
+#include <stdlib.h>
+#include <unistd.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);
+}
+
+void
+test_exec_exit(int do_exit)
+{
+  if (do_exit)
+    exit(0);
+  else
+    execl ("/bin/echo", "echo", "-1", (char *)0);
+}
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  getpid();
+
+  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. */
+
+  test_exec_exit(1);
+
+  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..c4d3bef
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,230 @@
+# 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 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_breakpoint "increase_1"
+gdb_test "continue" "Breakpoint .*at.*" "continue to the function to finish"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval ('a'), gdb.selected_frame ())" \
+         "Temporary breakpoint.*" "set FinishBreakpoint"
+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.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer normal hit"
+
+# check FinishBreakpoint in main no allowed
+
+gdb_test "finish" "main.*" "return to main()"
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         "ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \
+         "check FinishBP not allowed in main"
+
+#
+# Test FinishBreakpoint with no debug symbol 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+set cond_line [gdb_get_line_number "Condition Break."]
+
+if ![runto "getpid"] then {
+    fail "Cannot run to getpid."
+    return 0
+}
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "continue" "SimpleFinishBreakpoint stop.*" "check FinishBreakpoint hit"
+gdb_test "python print finishBP.return_value" "None" "check return value without debug symbol"
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "python finishbp = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" \
+         "check FinishBP out of scope notification"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy 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_1(i,8)" "Breakpoint .* at .*" \
+         "set a conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" \
+         "set FinishBP in a breakpoint condition"
+gdb_test "continue" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+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"
+
+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 explicit inferior function call
+#
+
+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
+}
+
+# return address in dummy frame
+
+gdb_test "python TestExplicitBreakpoint('increase_1')" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase_1(&i)" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+
+# return address in normal frame
+
+delete_breakpoints
+gdb_test "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase(&i)" \
+         "SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*The program being debugged stopped while in a function called from GDB.*" \
+         "FinishBP stop at during explicit function call"
+
+
+#
+# Test FinishBreakpoint when inferior exits
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}
+
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
+
+#
+# Test FinishBreakpoint when inferior execs
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}     
+
+gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
+gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ 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..adf10b0
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,88 @@
+# 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 MyFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		gdb.FinishBreakpoint.__init__ (self, 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..."
+
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__(self, spec="test_1", internal=1)
+        self.silent = True
+        self.count = 0
+        print "TestBreakpoint init"
+        
+    def stop(self):
+    	self.count += 1
+    	try:
+        	TestFinishBreakpoint(gdb.newest_frame(), self.count)
+        except ValueError as e:
+        	print e
+        return False
+
+class TestFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame, count):
+    	self.count = count
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        
+        
+    def stop(self):
+        print "-->", self.number
+        if (self.count == 3):
+            print "test stop ... %d" % self.count
+            return True
+        else:
+            print "test don't stop %d" % self.count
+            return False 
+        
+    
+    def out_of_scope(self):
+        print "test didn't finish ... %d" % self.count
+
+class TestExplicitBreakpoint(gdb.Breakpoint):
+	def stop(self):
+		try:
+			SimpleFinishBreakpoint(gdb.newest_frame())
+		except ValueError as e:
+			print e
+		return False
+
+class SimpleFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		
+		print "SimpleFinishBreakpoint init"
+		
+	def stop(self):
+		print "SimpleFinishBreakpoint stop" 
+		return True
+	
+	def out_of_scope(self):
+		print "SimpleFinishBreakpoint out of scope..."
+
+print "Python script importedd"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.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-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
new file mode 100644
index 0000000..fae798c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -0,0 +1,65 @@
+# 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-breakpoint2"
+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
+}
+
+#
+# Check FinishBreakpoints against C++ exceptions
+#
+
+gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_breakpoint "throw_exception_1"
+gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
+
+gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.py b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
new file mode 100644
index 0000000..9ea4cf1
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
@@ -0,0 +1,34 @@
+# 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 ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        self.silent = True;
+        print "init ExceptionFinishBreakpoint"
+        
+    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"
-- 
1.7.6.4


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

* Re: [RFC] Python Finish Breakpoints
  2011-10-25 11:05                             ` Kevin Pouget
@ 2011-10-25 11:37                               ` Phil Muldoon
  2011-10-25 12:22                                 ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Phil Muldoon @ 2011-10-25 11:37 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: Tom Tromey, gdb-patches

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

> On Mon, Oct 24, 2011 at 6:06 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
>> Kevin Pouget <kevin.pouget@gmail.com> writes:
>>
>> I have some comments regarding the Python bits.
>
> Thanks for that, I replied inline

Thanks.

>>> @@ -5700,6 +5700,7 @@ init_raw_breakpoint_without_location (struct breakpoint *b,
>>>    b->frame_id = null_frame_id;
>>>    b->condition_not_parsed = 0;
>>>    b->py_bp_object = NULL;
>>> +  b->is_py_finish_bp = 0;


>Phil>> Is there any reason why this needs to be in the breakpoint struct? 

>Kevin> just to put it back in context (in was back in May ...), here is the
>Kevin> rational behind the flag:


> On Thu, May 19, 2011 at 6:20 PM, Tom Tromey <tromey@redhat.com> wrote:
>> gdbpy_is_stopped_at_finish_bp is not safe to call here -- it assumes the
>> GIL has been acquired, which it has not.  I would rather it not be changed
>> to acquire the GIL, however.  I think one of two other approaches would
>> be preferable.
>>
>> One way you could handle this is to add a new constant to enum bptype.
>> This is likely to be pretty invasive.
>>
>> Another way would be to add a flag to the struct breakpoint itself.
>>
>> Yet another way would be a new breakpoint_ops method.

>Kevin> I've refactored the code according to your comment anyway, it make
>Kevin> sense, so now there are two version:

First let me apologies for not picking up on these previous comments.

My own personal opinion is to abstract the details to the GDB Python
code, instead of adding another flag to 'struct breakpoint'. That was
the original ethos of adding a pointer inside the breakpoint struct to
the Python breakpoint-object - so we can have access to the whole of the
breakpoint object without breaking out pieces of it here and there.

That being said, I don't want to delay this patch any further (and I'm
not sure why you cannot acquire the GIL in the accessor function?  There
is a performance hit involved in acquiring the GIL, maybe that). Tom
gave three options that make sense, so whatever works for you and Tom
will be fine.  Thanks for taking the time to refactor it.  Tom, what do
you think?

>> I think these comments should wrap? They wrap for me in emacs.
>
> I'm not sure about the exact meaning of 'wrap' here, but I assume it's
> about the new line between computable and NULL; I've reformatted it.

Yeah I meant, split the comments up.

Cheers,

Phil

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

* Re: [RFC] Python Finish Breakpoints
  2011-10-25 11:37                               ` Phil Muldoon
@ 2011-10-25 12:22                                 ` Kevin Pouget
  2011-10-28  8:33                                   ` Kevin Pouget
  2011-10-28 19:26                                   ` Tom Tromey
  0 siblings, 2 replies; 74+ messages in thread
From: Kevin Pouget @ 2011-10-25 12:22 UTC (permalink / raw)
  To: pmuldoon; +Cc: Tom Tromey, gdb-patches

On Tue, Oct 25, 2011 at 1:05 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
>
> Kevin Pouget <kevin.pouget@gmail.com> writes:
>
> > On Mon, Oct 24, 2011 at 6:06 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
> >> Kevin Pouget <kevin.pouget@gmail.com> writes:
> >>
> >> I have some comments regarding the Python bits.
> >
> > Thanks for that, I replied inline
>
> Thanks.
>
> >>> @@ -5700,6 +5700,7 @@ init_raw_breakpoint_without_location (struct breakpoint *b,
> >>>    b->frame_id = null_frame_id;
> >>>    b->condition_not_parsed = 0;
> >>>    b->py_bp_object = NULL;
> >>> +  b->is_py_finish_bp = 0;
>
>
> >Phil>> Is there any reason why this needs to be in the breakpoint struct?
>
> >Kevin> just to put it back in context (in was back in May ...), here is the
> >Kevin> rational behind the flag:
>
>
> > On Thu, May 19, 2011 at 6:20 PM, Tom Tromey <tromey@redhat.com> wrote:
> >> gdbpy_is_stopped_at_finish_bp is not safe to call here -- it assumes the
> >> GIL has been acquired, which it has not.  I would rather it not be changed
> >> to acquire the GIL, however.  I think one of two other approaches would
> >> be preferable.
> >>
> >> One way you could handle this is to add a new constant to enum bptype.
> >> This is likely to be pretty invasive.
> >>
> >> Another way would be to add a flag to the struct breakpoint itself.
> >>
> >> Yet another way would be a new breakpoint_ops method.
>
> >Kevin> I've refactored the code according to your comment anyway, it make
> >Kevin> sense, so now there are two version:
>
> First let me apologies for not picking up on these previous comments.
>
> My own personal opinion is to abstract the details to the GDB Python
> code, instead of adding another flag to 'struct breakpoint'. That was
> the original ethos of adding a pointer inside the breakpoint struct to
> the Python breakpoint-object - so we can have access to the whole of the
> breakpoint object without breaking out pieces of it here and there.

yes, I totally agree with this opinion, and that's why I changed the
code arguing,
"what's for Python stays in Python" !

> That being said, I don't want to delay this patch any further (and I'm
> not sure why you cannot acquire the GIL in the accessor function?  There
> is a performance hit involved in acquiring the GIL, maybe that). Tom
> gave three options that make sense, so whatever works for you and Tom
> will be fine.  Thanks for taking the time to refactor it.  Tom, what do
> you think?


I've got time for my patches, provided that they're not "forgotten",
and that the
discussions are constructive, I'll be happy to fix and refactor accordingly :)

I've got no idea about the cost of Python functions in general, let
see if that's
what Tom had in mind.


Cordially,

Kevin

> >> I think these comments should wrap? They wrap for me in emacs.
> >
> > I'm not sure about the exact meaning of 'wrap' here, but I assume it's
> > about the new line between computable and NULL; I've reformatted it.
>
> Yeah I meant, split the comments up.
>
> Cheers,
>
> Phil

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

* Re: [RFC] Python Finish Breakpoints
  2011-10-25 12:22                                 ` Kevin Pouget
@ 2011-10-28  8:33                                   ` Kevin Pouget
  2011-10-28 20:51                                     ` Tom Tromey
  2011-10-28 19:26                                   ` Tom Tromey
  1 sibling, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-10-28  8:33 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, pmuldoon

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

On Tue, Oct 25, 2011 at 1:36 PM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> On Tue, Oct 25, 2011 at 1:05 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
>>
>> Kevin Pouget <kevin.pouget@gmail.com> writes:
>>
>> > On Mon, Oct 24, 2011 at 6:06 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
>> >> Kevin Pouget <kevin.pouget@gmail.com> writes:
>> >>
>> >> I have some comments regarding the Python bits.
>> >
>> > Thanks for that, I replied inline
>>
>> Thanks.
>>
>> >>> @@ -5700,6 +5700,7 @@ init_raw_breakpoint_without_location (struct breakpoint *b,
>> >>>    b->frame_id = null_frame_id;
>> >>>    b->condition_not_parsed = 0;
>> >>>    b->py_bp_object = NULL;
>> >>> +  b->is_py_finish_bp = 0;
>>
>>
>> >Phil>> Is there any reason why this needs to be in the breakpoint struct?
>>
>> >Kevin> just to put it back in context (in was back in May ...), here is the
>> >Kevin> rational behind the flag:
>>
>>
>> > On Thu, May 19, 2011 at 6:20 PM, Tom Tromey <tromey@redhat.com> wrote:
>> >> gdbpy_is_stopped_at_finish_bp is not safe to call here -- it assumes the
>> >> GIL has been acquired, which it has not.  I would rather it not be changed
>> >> to acquire the GIL, however.  I think one of two other approaches would
>> >> be preferable.
>> >>
>> >> One way you could handle this is to add a new constant to enum bptype.
>> >> This is likely to be pretty invasive.
>> >>
>> >> Another way would be to add a flag to the struct breakpoint itself.
>> >>
>> >> Yet another way would be a new breakpoint_ops method.
>>
>> >Kevin> I've refactored the code according to your comment anyway, it make
>> >Kevin> sense, so now there are two version:
>>
>> First let me apologies for not picking up on these previous comments.
>>
>> My own personal opinion is to abstract the details to the GDB Python
>> code, instead of adding another flag to 'struct breakpoint'. That was
>> the original ethos of adding a pointer inside the breakpoint struct to
>> the Python breakpoint-object - so we can have access to the whole of the
>> breakpoint object without breaking out pieces of it here and there.
>
> yes, I totally agree with this opinion, and that's why I changed the
> code arguing,
> "what's for Python stays in Python" !
>
>> That being said, I don't want to delay this patch any further (and I'm
>> not sure why you cannot acquire the GIL in the accessor function?  There
>> is a performance hit involved in acquiring the GIL, maybe that). Tom
>> gave three options that make sense, so whatever works for you and Tom
>> will be fine.  Thanks for taking the time to refactor it.  Tom, what do
>> you think?
>
>
> I've got time for my patches, provided that they're not "forgotten",
> and that the
> discussions are constructive, I'll be happy to fix and refactor accordingly :)
>
> I've got no idea about the cost of Python functions in general, let
> see if that's
> what Tom had in mind.
>
>
> Cordially,
>
> Kevin


Hello,

I've updated this patch against the last GIT tree and fixed some typos
in the code, if you want to give it another look Tom...
I followed your suggestion (delete BP once the frame in gone) as well
as Phil's (move is_finish_py from 'struct breakpoint' to the Py
object, looks with encapsulation)


Thanks,

Kevin

[-- Attachment #2: 0001-Introduce-gdb.FinishBreakpoints.patch --]
[-- Type: text/x-patch, Size: 54292 bytes --]

From 7a8abc74d54726e4c5ceccb34e704c7465e43409 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Tue, 20 Sep 2011 13:59:23 +0200
Subject: [PATCH] Introduce gdb.FinishBreakpoints

---
 gdb/Makefile.in                                    |    6 +
 gdb/NEWS                                           |    4 +
 gdb/doc/gdb.texinfo                                |   49 +++
 gdb/infcmd.c                                       |   21 +-
 gdb/inferior.h                                     |    3 +
 gdb/infrun.c                                       |    9 +-
 gdb/python/py-breakpoint.c                         |   51 +--
 gdb/python/py-finishbreakpoint.c                   |  404 ++++++++++++++++++++
 gdb/python/py-frame.c                              |   32 +-
 gdb/python/python-internal.h                       |   50 +++-
 gdb/python/python.c                                |    6 +
 gdb/python/python.h                                |    2 +
 gdb/testsuite/gdb.python/py-breakpoint.exp         |    7 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.c    |   97 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp  |  230 +++++++++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py   |   88 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc  |   59 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   65 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.py  |   34 ++
 19 files changed, 1152 insertions(+), 65 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 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
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.py

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 32d8ef3..6cedf12 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,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 \
@@ -318,6 +319,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 \
@@ -2103,6 +2105,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/NEWS b/gdb/NEWS
index 80c59f6..655e013 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -50,6 +50,10 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".
 
+  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
+     of a function.  This class is based on the "finish" command
+     available in the CLI. 
+
   ** Type objects for struct and union types now allow access to
      the fields using standard Python dictionary (mapping) methods.
      For example, "some_type['myfield']" now works, as does
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 69e2bda..847dc42 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21289,6 +21289,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Symbol Tables In Python::     Python representation of symbol tables.
 * Lazy Strings In Python::      Python representation of lazy strings.
 * Breakpoints In Python::       Manipulating breakpoints using Python.
+* Finish Breakpoints in Python:: Setting Breakpoints on function return
+                                using Python.
 @end menu
 
 @node Basic Python
@@ -24082,6 +24084,53 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@node Finish Breakpoints in Python
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A finish breakpoint is a temporary breakpoint set at the return address of
+a frame, based on the @code{finish} command.  @code{gdb.FinishBreakpoint}
+extends @code{gdb.Breakpoint}.  The underlying breakpoint will be disabled 
+and deleted when the execution will run out of the breakpoint scope (i.e.@: 
+@code{Breakpoint.stop} or @code{FinishBreakpoint.out_of_scope} triggered).
+
+@defun FinishBreakpoint.__init__ (@var{frame}, @r{[}@var{internal}@r{]})
+Create a finish breakpoint at the return address of the @code{gdb.Frame}
+object @var{frame}.  The optional @var{internal} argument allows the
+breakpoint to become invisible to the user.  @xref{Breakpoints In Python},
+for further details about this argument.
+@end defun
+
+@defun FinishBreakpoint.out_of_scope (self)
+In some circumstances (e.g.@: @code{longjmp}, C@t{++} exceptions, @value{GDBN} 
+@code{return} command, @dots{}), a function may not properly terminate, and
+thus never hit the finish breakpoint.  When @value{GDBN} notices such a
+situation, the @code{out_of_scope} callback will be triggered.
+
+You may want to sub-class @code{gdb.FinishBreakpoint} and override this
+method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+    def stop (self):
+        print "normal finish"
+        return True
+    
+    def out_of_scope ():
+        print "abnormal finish"
+@end smallexample 
+@end defun
+
+@defvar FinishBreakpoint.return_value
+When @value{GDBN} is stopped at a finish breakpoint and the frame 
+used to build the @code{gdb.FinishBreakpoint} object had debug symbols, this
+attribute will contain a @code{gdb.Value} object corresponding to the return
+value of the function.  The value will be @code{None} if the return value
+was not computable.  This attribute is not writable.
+@end defvar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 3e8bf5d..33ce1fc 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1413,14 +1413,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.  */
+/* Return 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;
   struct ui_out *uiout = current_uiout;
 
@@ -1451,6 +1449,19 @@ 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;
+  struct ui_out *uiout = current_uiout;
+
   if (value)
     {
       struct value_print_options opts;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..f198696 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -269,6 +269,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 db6a5d1..5ce0c94 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -56,6 +56,7 @@
 #include "tracepoint.h"
 #include "continuations.h"
 #include "interps.h"
+#include "python/python.h"
 
 /* Prototypes for local functions */
 
@@ -365,7 +366,8 @@ show_stop_on_solib_events (struct ui_file *file, int from_tty,
 int stop_after_trap;
 
 /* Save register contents here when executing a "finish" command or are
-   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set.
+   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set
+   or a Python FinishBreakpoint has been hit.
    Thus this contains the return value from the called function (assuming
    values are returned in a register).  */
 
@@ -5985,8 +5987,9 @@ 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
-      && execution_direction != EXEC_REVERSE)
+  if (gdbpy_is_stopped_at_finish_bp (inferior_thread ()->control.stop_bpstat)
+      || (inferior_thread ()->control.proceed_to_finish
+          && execution_direction != EXEC_REVERSE))
     {
       /* This should not be necessary.  */
       if (stop_registers)
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index daceb99..e4720c0 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
 {
@@ -757,6 +723,15 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
       else
 	gdbpy_print_stack ();
     }
+
+  /* If it's a temporary breakpoint.  */
+  if (bpfinishpy_is_finish_bp (py_bp))
+    {
+      /* Can't delete it here.  */
+      gdb_assert (b->disposition == disp_del);
+      disable_breakpoint (b);
+    }
+
   do_cleanups (cleanup);
 
   return stop;
@@ -821,6 +796,8 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->bp->py_bp_object = newbp;
       Py_INCREF (newbp);
       ++bppy_live;
+
+      newbp->is_finish_bp = bpfinishpy_is_finish_bp ((PyObject *) newbp);
     }
   else
     {
@@ -980,7 +957,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..027695b
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,404 @@
+/* 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.  */
+  breakpoint_object py_bp;
+  /* gdb.Type object of the function finished by this breakpoint.  */
+  PyObject *function_type;
+  /* gdb.Type object of the value return by the breakpointed function.  */
+  PyObject *return_type;
+  /* When stopped at this FinishBreakpoint, value returned by the function;
+     Py_None if the value is not computable; NULL if GDB is not stopped at
+     a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* Triggered when GDB stops at PY_BP.  Computes and caches the `return_value',
+   if possible.  */
+
+static void
+bpfinish_stopped_at_finish_bp (struct finish_breakpoint_object *py_bp)
+{
+  if (py_bp->return_type)
+    {
+      struct value *ret =
+          get_return_value (type_object_to_type (py_bp->function_type),
+                            type_object_to_type (py_bp->return_type));
+      if (ret)
+        {
+          py_bp->return_value = value_to_value_object (ret);
+        }
+      else
+        py_bp->return_value = Py_None;
+    }
+}
+
+/* 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;
+
+  if (self_finishbp->return_type == NULL)
+    Py_RETURN_NONE;
+
+  /* Check if we have a cached value.  */
+  if (!self_finishbp->return_value)
+    {
+      bpstat bs;
+
+      BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
+
+      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)
+              bpfinish_stopped_at_finish_bp (self_finishbp);
+        }
+    }
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* If STOP_BPSTAT contains a Python breakpoint whose type is TYPE, returns
+   this breakpoint. Returns NULL otherwise.  */
+
+struct breakpoint *
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  bpstat bs;
+
+  for (bs = stop_bpstat; bs; bs = bs->next)
+    {
+      if (bs->breakpoint_at
+          && bpfinishpy_bp_is_finish_bp ((breakpoint_object *)
+                                         bs->breakpoint_at->py_bp_object))
+        return bs->breakpoint_at;
+    }
+
+  return NULL;
+}
+
+/* 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;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  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;
+    }
+  else if (get_frame_type (prev_frame) == DUMMY_FRAME)
+      {
+        PyErr_SetString (PyExc_ValueError,
+                  _("\"FinishBreakpoint\" cannot be set on a dummy frame."));
+        return -1;
+      }
+
+  frame_id = get_frame_id (prev_frame);
+  if (frame_id_eq (frame_id, null_frame_id))
+    goto invalid_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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  self_bpfinish->return_type = NULL;
+  self_bpfinish->function_type = NULL;
+  if (get_frame_pc_if_available (frame, &pc))
+    {
+      function = find_pc_function (pc);
+      if (function != NULL)
+        {
+          struct type *ret_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+          /* Remember only non-VOID return types.  */
+          if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
+            {
+              self_bpfinish->return_type = type_to_type_object (ret_type);
+              self_bpfinish->function_type =
+                  type_to_type_object (SYMBOL_TYPE (function));
+            }
+        }
+    }
+
+  bppy_pending_object = self_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, -1,
+                         0,
+                         1 /*temp_flag*/,
+                         bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         &bkpt_breakpoint_ops,
+                         0, 1, internal_bp);
+    }
+  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;
+  
+  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.  */
+
+int
+bpfinishpy_is_finish_bp (PyObject *obj)
+{
+  return obj != NULL
+         && PyObject_TypeCheck (obj, &finish_breakpoint_object_type);
+}
+
+/* Returns 1 if PY_OBJ is not NULL and references a FinishBreakpoint object.
+   This version doesn't assume that GIL has been acquired.  */
+
+int
+bpfinishpy_bp_is_finish_bp (breakpoint_object *bp_obj)
+{
+  return bp_obj != NULL
+         && bp_obj->is_finish_bp;
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack.  Triggers the method OUT_OF_SCOPE if implemented,
+   then delete the breakpoint.  */
+
+static void
+bpfinishpy_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))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+
+  delete_breakpoint (bpfinish_obj->py_bp.bp);
+}
+
+/* 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 and its frame is
+     not anymore in the current callstack.  */
+  if (bpfinishpy_is_finish_bp (py_bp))
+    {
+      struct finish_breakpoint_object *finish_bp =
+          (struct finish_breakpoint_object *) py_bp;
+
+      if (b == bp_stopped)
+        bpfinish_stopped_at_finish_bp (finish_bp);
+      else if (b->pspace == current_inferior ()->pspace
+           && (!target_has_registers
+               || frame_find_by_id (b->frame_id) == NULL))
+        {
+          bpfinishpy_out_of_scope (finish_bp);
+        }
+    }
+  
+  do_cleanups (cleanup);
+  
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run outg
+   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 PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "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 398ce84..c4494cb 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);
     }
@@ -476,12 +477,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 e593612..f899b77 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -120,9 +120,50 @@ 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;
+
+  /* 1 is this is a FinishBreakpoint object, 0 otherwise.  */
+  int is_finish_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
 {
@@ -188,6 +229,10 @@ struct value *convert_value_from_python (PyObject *obj);
 struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
+struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);
+
+int bpfinishpy_is_finish_bp (PyObject *obj);
+int bpfinishpy_bp_is_finish_bp (breakpoint_object *bp_obj);
 
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
@@ -202,6 +247,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 df8e3d5..db23bbf 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1054,6 +1054,11 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj)
 		    "scripting is not supported."));
 }
 
+struct breakpoint *
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  return NULL;
+}
 #endif /* HAVE_PYTHON */
 
 \f
@@ -1243,6 +1248,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..e341d76 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);
 
+struct breakpoint *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 e0dd087..0e3adbd 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.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..5b708e3
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,97 @@
+/* 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>
+#include <stdlib.h>
+#include <unistd.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);
+}
+
+void
+test_exec_exit(int do_exit)
+{
+  if (do_exit)
+    exit(0);
+  else
+    execl ("/bin/echo", "echo", "-1", (char *)0);
+}
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  getpid();
+
+  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. */
+
+  test_exec_exit(1);
+
+  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..c4d3bef
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,230 @@
+# 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 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_breakpoint "increase_1"
+gdb_test "continue" "Breakpoint .*at.*" "continue to the function to finish"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval ('a'), gdb.selected_frame ())" \
+         "Temporary breakpoint.*" "set FinishBreakpoint"
+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.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer normal hit"
+
+# check FinishBreakpoint in main no allowed
+
+gdb_test "finish" "main.*" "return to main()"
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         "ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \
+         "check FinishBP not allowed in main"
+
+#
+# Test FinishBreakpoint with no debug symbol 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+set cond_line [gdb_get_line_number "Condition Break."]
+
+if ![runto "getpid"] then {
+    fail "Cannot run to getpid."
+    return 0
+}
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "continue" "SimpleFinishBreakpoint stop.*" "check FinishBreakpoint hit"
+gdb_test "python print finishBP.return_value" "None" "check return value without debug symbol"
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "python finishbp = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" \
+         "check FinishBP out of scope notification"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy 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_1(i,8)" "Breakpoint .* at .*" \
+         "set a conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" \
+         "set FinishBP in a breakpoint condition"
+gdb_test "continue" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+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"
+
+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 explicit inferior function call
+#
+
+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
+}
+
+# return address in dummy frame
+
+gdb_test "python TestExplicitBreakpoint('increase_1')" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase_1(&i)" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+
+# return address in normal frame
+
+delete_breakpoints
+gdb_test "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase(&i)" \
+         "SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*The program being debugged stopped while in a function called from GDB.*" \
+         "FinishBP stop at during explicit function call"
+
+
+#
+# Test FinishBreakpoint when inferior exits
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}
+
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
+
+#
+# Test FinishBreakpoint when inferior execs
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}     
+
+gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
+gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ 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..adf10b0
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,88 @@
+# 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 MyFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		gdb.FinishBreakpoint.__init__ (self, 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..."
+
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__(self, spec="test_1", internal=1)
+        self.silent = True
+        self.count = 0
+        print "TestBreakpoint init"
+        
+    def stop(self):
+    	self.count += 1
+    	try:
+        	TestFinishBreakpoint(gdb.newest_frame(), self.count)
+        except ValueError as e:
+        	print e
+        return False
+
+class TestFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame, count):
+    	self.count = count
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        
+        
+    def stop(self):
+        print "-->", self.number
+        if (self.count == 3):
+            print "test stop ... %d" % self.count
+            return True
+        else:
+            print "test don't stop %d" % self.count
+            return False 
+        
+    
+    def out_of_scope(self):
+        print "test didn't finish ... %d" % self.count
+
+class TestExplicitBreakpoint(gdb.Breakpoint):
+	def stop(self):
+		try:
+			SimpleFinishBreakpoint(gdb.newest_frame())
+		except ValueError as e:
+			print e
+		return False
+
+class SimpleFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		
+		print "SimpleFinishBreakpoint init"
+		
+	def stop(self):
+		print "SimpleFinishBreakpoint stop" 
+		return True
+	
+	def out_of_scope(self):
+		print "SimpleFinishBreakpoint out of scope..."
+
+print "Python script importedd"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.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-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
new file mode 100644
index 0000000..fae798c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -0,0 +1,65 @@
+# 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-breakpoint2"
+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
+}
+
+#
+# Check FinishBreakpoints against C++ exceptions
+#
+
+gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_breakpoint "throw_exception_1"
+gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
+
+gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.py b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
new file mode 100644
index 0000000..9ea4cf1
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
@@ -0,0 +1,34 @@
+# 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 ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        self.silent = True;
+        print "init ExceptionFinishBreakpoint"
+        
+    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"
-- 
1.7.6.4


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

* Re: [RFC] Python Finish Breakpoints
  2011-10-25 12:22                                 ` Kevin Pouget
  2011-10-28  8:33                                   ` Kevin Pouget
@ 2011-10-28 19:26                                   ` Tom Tromey
  1 sibling, 0 replies; 74+ messages in thread
From: Tom Tromey @ 2011-10-28 19:26 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: pmuldoon, gdb-patches

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

Phil> My own personal opinion is to abstract the details to the GDB Python
Phil> code, instead of adding another flag to 'struct breakpoint'. That was
Phil> the original ethos of adding a pointer inside the breakpoint struct to
Phil> the Python breakpoint-object - so we can have access to the whole of the
Phil> breakpoint object without breaking out pieces of it here and there.

Kevin> yes, I totally agree with this opinion, and that's why I changed the
Kevin> code arguing,
Kevin> "what's for Python stays in Python" !

There are some other options available.

You could subclass breakpoint and add a new field there.  This would
segregate the changes in the python subdirectory.  This may be best.

Alternatively you could make a bp_finish breakpoint.  I'm not sure
whether this is sufficient or whether you would also need an additional
flag.  If the latter you could make a bp_finish_python or something like
that -- I said earlier that this was probably invasive, but grepping for
bp_finish makes me think it wouldn't be too bad.

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2011-10-28  8:33                                   ` Kevin Pouget
@ 2011-10-28 20:51                                     ` Tom Tromey
  2011-11-02 14:44                                       ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Tom Tromey @ 2011-10-28 20:51 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches, pmuldoon

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

You need a ChangeLog.

Kevin> -static PyTypeObject breakpoint_object_type;
Kevin> +PyTypeObject breakpoint_object_type;

The old line here was a forward declaration.
I think you should just entirely remove it.

Kevin> +  /* If it's a temporary breakpoint.  */
Kevin> +  if (bpfinishpy_is_finish_bp (py_bp))
Kevin> +    {
Kevin> +      /* Can't delete it here.  */
Kevin> +      gdb_assert (b->disposition == disp_del);
Kevin> +      disable_breakpoint (b);
Kevin> +    }

I don't understand this code.
It seems to me that disp_del is a better setting here.

Kevin> +      newbp->is_finish_bp = bpfinishpy_is_finish_bp ((PyObject *) newbp);

I think this flag could be set more cleanly in bpfinishpy_init.

Kevin> +  /* gdb.Type object of the function finished by this breakpoint.  */
Kevin> +  PyObject *function_type;
Kevin> +  /* gdb.Type object of the value return by the breakpointed function.  */
Kevin> +  PyObject *return_type;
Kevin> +  /* When stopped at this FinishBreakpoint, value returned by the function;
Kevin> +     Py_None if the value is not computable; NULL if GDB is not stopped at
Kevin> +     a FinishBreakpoint.  */
Kevin> +  PyObject *return_value;

I think the new class needs a destructor which decrefs these.

Kevin> +static void
Kevin> +bpfinish_stopped_at_finish_bp (struct finish_breakpoint_object *py_bp)
Kevin> +{
Kevin> +  if (py_bp->return_type)
Kevin> +    {
Kevin> +      struct value *ret =
Kevin> +          get_return_value (type_object_to_type (py_bp->function_type),
Kevin> +                            type_object_to_type (py_bp->return_type));

Calls into gdb have to be wrapped in TRY_CATCH.
Then errors have to be propagated to Python somehow, or printed and
ignored via gdbpy_print_stack.  Given the context I would say propagate.

Kevin> +  /* Check if we have a cached value.  */
Kevin> +  if (!self_finishbp->return_value)
Kevin> +    {
Kevin> +      bpstat bs;
Kevin> +
Kevin> +      BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
Kevin> +
Kevin> +      for (bs = inferior_thread ()->control.stop_bpstat;
Kevin> +          bs; bs = bs->next)
Kevin> +        {
Kevin> +          struct breakpoint *bp = bs->breakpoint_at;
Kevin> +
Kevin> +          if (bp != NULL && (PyObject *) bp->py_bp_object == self)
Kevin> +              bpfinish_stopped_at_finish_bp (self_finishbp);
Kevin> +        }

It seems like it should be an error to try to compute the return value
when not stopped at this breakpoint.

Kevin> +struct breakpoint *
Kevin> +gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
Kevin> +{

Since the name implies that this is a predicate and since the result is
only ever used as a boolean, I think this should return int.

Kevin> +  bpstat bs;
Kevin> +
Kevin> +  for (bs = stop_bpstat; bs; bs = bs->next)
Kevin> +    {
Kevin> +      if (bs->breakpoint_at
Kevin> +          && bpfinishpy_bp_is_finish_bp ((breakpoint_object *)
Kevin> +                                         bs->breakpoint_at->py_bp_object))

I am not really sure about this.  It seems like it may be pedantically
incorrect, though it is hard to see when it could possibly fail.  That
is, is the GIL required or not?  It doesn't call a function and the
breakpoint owns a ref to the breakpoint object, so it seems like it
could not be deleted out from under us.

I'm inclined to say it is ok.

Kevin> +  breakpoint_object *self_bp = (breakpoint_object *) self;
Kevin> +  struct finish_breakpoint_object *self_bpfinish =
Kevin> +      (struct finish_breakpoint_object *) self;

I think this is the only use of self in this function.
Just drop it and cast directly to the most specific subclass.

Kevin> +  prev_frame = get_prev_frame (frame);
Kevin> +  if (prev_frame == 0)
Kevin> +    {
Kevin> +      PyErr_SetString (PyExc_ValueError, 
Kevin> +           _("\"FinishBreakpoint\" not meaningful in the outermost frame."));
Kevin> +      return -1;
Kevin> +    }
Kevin> +  else if (get_frame_type (prev_frame) == DUMMY_FRAME)
Kevin> +      {
Kevin> +        PyErr_SetString (PyExc_ValueError,
Kevin> +                  _("\"FinishBreakpoint\" cannot be set on a dummy frame."));
Kevin> +        return -1;
Kevin> +      }

I think the calls to get_prev_frame and get_frame_type need to be
wrapped in a TRY_CATCH.

Kevin> +  frame_id = get_frame_id (prev_frame);
Kevin> +  if (frame_id_eq (frame_id, null_frame_id))

Likewise.

I'd try to put all the gdb-facing logic into a single big TRY_CATCH.

Kevin> +  if (internal)
Kevin> +    {
Kevin> +      internal_bp = PyObject_IsTrue (internal);
Kevin> +      if (internal_bp == -1) 
Kevin> +        {
Kevin> +          PyErr_SetString (PyExc_ValueError, 
Kevin> +                           _("The value of `internal' must be a boolean."));
Kevin> +          return -1;

Do you need to decref 'frame_obj' here?  I suspect so.
There are other early returns that probably need this.
A typical solution is a label where all the locals are xdecref'd then
return -1.

Kevin> +  /* Find the function we will return from.  */
Kevin> +  self_bpfinish->return_type = NULL;
Kevin> +  self_bpfinish->function_type = NULL;

These can be left NULL in the object.  What happens if you try to fetch
the return value in that case?

Kevin> +  if (get_frame_pc_if_available (frame, &pc))
Kevin> +    {
Kevin> +      function = find_pc_function (pc);
Kevin> +      if (function != NULL)
Kevin> +        {
Kevin> +          struct type *ret_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
Kevin> +

More TRY_CATCH.

Kevin> +          /* Remember only non-VOID return types.  */
Kevin> +          if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
Kevin> +            {
Kevin> +              self_bpfinish->return_type = type_to_type_object (ret_type);

Error check.

Kevin> +              self_bpfinish->function_type =
Kevin> +                  type_to_type_object (SYMBOL_TYPE (function));

Likewise.

Kevin> +  if (except.reason < 0)
Kevin> +    {
Kevin> +      PyErr_Format (except.reason == RETURN_QUIT
Kevin> +                    ? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
Kevin> +                    "%s", except.message);
Kevin> +      return -1;

Just use GDB_PY_SET_HANDLE_EXCEPTION.

Kevin> +int
Kevin> +bpfinishpy_is_finish_bp (PyObject *obj)
Kevin> +{
Kevin> +  return obj != NULL
Kevin> +         && PyObject_TypeCheck (obj, &finish_breakpoint_object_type);
Kevin> +}

Kevin> +int
Kevin> +bpfinishpy_bp_is_finish_bp (breakpoint_object *bp_obj)
Kevin> +{
Kevin> +  return bp_obj != NULL
Kevin> +         && bp_obj->is_finish_bp;
Kevin> +}

Are both of these needed?

Kevin> +static void
Kevin> +bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
Kevin> +{
Kevin> +  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
Kevin> +                            bs == NULL ? NULL : bs->breakpoint_at);
Kevin> +}

The way this is written, it will acquire and release the GIL for each
breakpoint.

I think it would be better to acquire + release just once.

Kevin> +static void
Kevin> +bpfinishpy_handle_exit (struct inferior *inf)
Kevin> +{
Kevin> +  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
Kevin> +}

Likewise.

Kevin> +struct breakpoint *gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat);
Kevin>  #endif /* GDB_PYTHON_H */

Newline between these two lines.

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2011-10-28 20:51                                     ` Tom Tromey
@ 2011-11-02 14:44                                       ` Kevin Pouget
  2011-11-04 14:25                                         ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-11-02 14:44 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, pmuldoon

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

On Fri, Oct 28, 2011 at 10:37 PM, Tom Tromey <tromey@redhat.com> wrote:

Hello,
thanks for you're answer, I reply inline

> You need a ChangeLog.

sorry, I forgot to include it in the e-mail, you'll find it at the bottom

> Kevin> -static PyTypeObject breakpoint_object_type;
> Kevin> +PyTypeObject breakpoint_object_type;
>
> The old line here was a forward declaration.
> I think you should just entirely remove it.
>
> Kevin> +  /* If it's a temporary breakpoint.  */
> Kevin> +  if (bpfinishpy_is_finish_bp (py_bp))
> Kevin> +    {
> Kevin> +      /* Can't delete it here.  */
> Kevin> +      gdb_assert (b->disposition == disp_del);
> Kevin> +      disable_breakpoint (b);
> Kevin> +    }
>
> I don't understand this code.
> It seems to me that disp_del is a better setting here.

I need to do it this way because as far as I've seen,
"breakpoint_auto_delete" is only triggered in infrun.c::normal_stop,
so if Breakpoint.stop returns False, the breakpoint is not deleted
immediately, and may be hit twice.
So I disable the breakpoint to avoid it.

> Kevin> +      newbp->is_finish_bp = bpfinishpy_is_finish_bp ((PyObject *) newbp);
>
> I think this flag could be set more cleanly in bpfinishpy_init.

you're right, fixed

> Kevin> +  /* gdb.Type object of the function finished by this breakpoint.  */
> Kevin> +  PyObject *function_type;
> Kevin> +  /* gdb.Type object of the value return by the breakpointed function.  */
> Kevin> +  PyObject *return_type;
> Kevin> +  /* When stopped at this FinishBreakpoint, value returned by the function;
> Kevin> +     Py_None if the value is not computable; NULL if GDB is not stopped at
> Kevin> +     a FinishBreakpoint.  */
> Kevin> +  PyObject *return_value;
>
> I think the new class needs a destructor which decrefs these.

fixed

> Kevin> +static void
> Kevin> +bpfinish_stopped_at_finish_bp (struct finish_breakpoint_object *py_bp)
> Kevin> +{
> Kevin> +  if (py_bp->return_type)
> Kevin> +    {
> Kevin> +      struct value *ret =
> Kevin> +          get_return_value (type_object_to_type (py_bp->function_type),
> Kevin> +                            type_object_to_type (py_bp->return_type));
>
> Calls into gdb have to be wrapped in TRY_CATCH.
> Then errors have to be propagated to Python somehow, or printed and
> ignored via gdbpy_print_stack.  Given the context I would say propagate.

you're right,

bpfinish_stopped_at_finish_bp is called in 2 situations, so it just
sets the error flag, then

- direct access to returnvalue member --> propage exception to Python
- bpfinishpy_handle_stop --> bpfinishpy_handle_stop()

> Kevin> +  /* Check if we have a cached value.  */
> Kevin> +  if (!self_finishbp->return_value)
> Kevin> +    {
> Kevin> +      bpstat bs;
> Kevin> +
> Kevin> +      BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
> Kevin> +
> Kevin> +      for (bs = inferior_thread ()->control.stop_bpstat;
> Kevin> +          bs; bs = bs->next)
> Kevin> +        {
> Kevin> +          struct breakpoint *bp = bs->breakpoint_at;
> Kevin> +
> Kevin> +          if (bp != NULL && (PyObject *) bp->py_bp_object == self)
> Kevin> +              bpfinish_stopped_at_finish_bp (self_finishbp);
> Kevin> +        }
>
> It seems like it should be an error to try to compute the return value
> when not stopped at this breakpoint.

I'm not totally convinced ...
what would you think about throwing an AttributeError("return_value
not available yet") when accessing the attribute before the breakpoint
is hit, but keep the cached value afterwards?


> Kevin> +struct breakpoint *
> Kevin> +gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
> Kevin> +{
>
> Since the name implies that this is a predicate and since the result is
> only ever used as a boolean, I think this should return int.

changed

> Kevin> +  bpstat bs;
> Kevin> +
> Kevin> +  for (bs = stop_bpstat; bs; bs = bs->next)
> Kevin> +    {
> Kevin> +      if (bs->breakpoint_at
> Kevin> +          && bpfinishpy_bp_is_finish_bp ((breakpoint_object *)
> Kevin> +                                         bs->breakpoint_at->py_bp_object))
>
> I am not really sure about this.  It seems like it may be pedantically
> incorrect, though it is hard to see when it could possibly fail.  That
> is, is the GIL required or not?  It doesn't call a function and the
> breakpoint owns a ref to the breakpoint object, so it seems like it
> could not be deleted out from under us.
>
> I'm inclined to say it is ok.

no, the GIL is not required.
This function is triggered from infrun.c:normal_stop, to determine if
we want to save
the stop_registers, like it's done for `finish'

> Kevin> +  breakpoint_object *self_bp = (breakpoint_object *) self;
> Kevin> +  struct finish_breakpoint_object *self_bpfinish =
> Kevin> +      (struct finish_breakpoint_object *) self;
>
> I think this is the only use of self in this function.
> Just drop it and cast directly to the most specific subclass.

I'm not sure what you meant, *self_bp* was redundant so I removed it

> Kevin> +  prev_frame = get_prev_frame (frame);
> Kevin> +  if (prev_frame == 0)
> Kevin> +    {
> Kevin> +      PyErr_SetString (PyExc_ValueError,
> Kevin> +           _("\"FinishBreakpoint\" not meaningful in the outermost frame."));
> Kevin> +      return -1;
> Kevin> +    }
> Kevin> +  else if (get_frame_type (prev_frame) == DUMMY_FRAME)
> Kevin> +      {
> Kevin> +        PyErr_SetString (PyExc_ValueError,
> Kevin> +                  _("\"FinishBreakpoint\" cannot be set on a dummy frame."));
> Kevin> +        return -1;
> Kevin> +      }
>
> I think the calls to get_prev_frame and get_frame_type need to be
> wrapped in a TRY_CATCH.
>
> Kevin> +  frame_id = get_frame_id (prev_frame);
> Kevin> +  if (frame_id_eq (frame_id, null_frame_id))
>
> Likewise.
> I'd try to put all the gdb-facing logic into a single big TRY_CATCH.

I wrapped them all and propagate the error with gdbpy_convert_exception

> Kevin> +  if (internal)
> Kevin> +    {
> Kevin> +      internal_bp = PyObject_IsTrue (internal);
> Kevin> +      if (internal_bp == -1)
> Kevin> +        {
> Kevin> +          PyErr_SetString (PyExc_ValueError,
> Kevin> +                           _("The value of `internal' must be a boolean."));
> Kevin> +          return -1;
>
> Do you need to decref 'frame_obj' here?  I suspect so.
> There are other early returns that probably need this.
> A typical solution is a label where all the locals are xdecref'd then
> return -1.

> PyArg_ParseTupleAndKeywords (args, kwargs, "O|O", keywords, &frame_obj, &internal)

No, according to the documentation,
> Note that any Python object references which are provided to the caller are borrowed references; do not decrement their reference count!


> Kevin> +  /* Find the function we will return from.  */
> Kevin> +  self_bpfinish->return_type = NULL;
> Kevin> +  self_bpfinish->function_type = NULL;
>
> These can be left NULL in the object.  What happens if you try to fetch
> the return value in that case?

I've updated the comments to make it clearer:

  /* gdb.Type object of the value return by the breakpointed function.
     May be NULL if no debug information was available or return type
     was VOID.  */
  PyObject *return_type;
  /* gdb.Type object of the function finished by this breakpoint.  Will be
     NULL if return_type is NULL.  */
  PyObject *function_type;

if return_type is NULL, "return_value" attribute will be None (the
return value is not computed/computable)

I've updated the doc as well:
> The value will be @code{None} if the function return type is @code{VOID} or if the return value was not computable.

> Kevin> +  if (get_frame_pc_if_available (frame, &pc))
> Kevin> +    {
> Kevin> +      function = find_pc_function (pc);
> Kevin> +      if (function != NULL)
> Kevin> +        {
> Kevin> +          struct type *ret_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
> Kevin> +
>
> More TRY_CATCH.
>
> Kevin> +          /* Remember only non-VOID return types.  */
> Kevin> +          if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
> Kevin> +            {
> Kevin> +              self_bpfinish->return_type = type_to_type_object (ret_type);
>
> Error check.
>
> Kevin> +              self_bpfinish->function_type =
> Kevin> +                  type_to_type_object (SYMBOL_TYPE (function));
>
> Likewise.

I'm not sure about the best way to handle these exceptions, so for all
the error thrown when trying to compute the return_type/function_type,
I just silently swallow it, and set the return_type to None.

Propagating the exception is not suitable, because I don't want not to
prevent the F_BP creation just because of the return value;
and I don't want to write a warning within Python code ...


> Kevin> +  if (except.reason < 0)
> Kevin> +    {
> Kevin> +      PyErr_Format (except.reason == RETURN_QUIT
> Kevin> +                    ? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
> Kevin> +                    "%s", except.message);
> Kevin> +      return -1;
>
> Just use GDB_PY_SET_HANDLE_EXCEPTION.

right, fixed
(the same should apply to gdb.Breakpoint creation, I copied the code from there)

> Kevin> +int
> Kevin> +bpfinishpy_is_finish_bp (PyObject *obj)
> Kevin> +{
> Kevin> +  return obj != NULL
> Kevin> +         && PyObject_TypeCheck (obj, &finish_breakpoint_object_type);
> Kevin> +}
>
> Kevin> +int
> Kevin> +bpfinishpy_bp_is_finish_bp (breakpoint_object *bp_obj)
> Kevin> +{
> Kevin> +  return bp_obj != NULL
> Kevin> +         && bp_obj->is_finish_bp;
> Kevin> +}
>
> Are both of these needed?

no, not anymore,
I even removed both of them,

> newbp->is_finish_bp = bpfinishpy_is_finish_bp ((PyObject *) newbp);
> I think this flag could be set more cleanly in bpfinishpy_init.

but I've rewritten this bit

> Kevin> +static void
> Kevin> +bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
> Kevin> +{
> Kevin> +  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
> Kevin> +                            bs == NULL ? NULL : bs->breakpoint_at);
> Kevin> +}
>
> The way this is written, it will acquire and release the GIL for each
> breakpoint.
>
> I think it would be better to acquire + release just once.
>
> Kevin> +static void
> Kevin> +bpfinishpy_handle_exit (struct inferior *inf)
> Kevin> +{
> Kevin> +  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
> Kevin> +}
>
> Likewise.

fixed

I'm not 100% confident with this "ensure_python_env" and the value of
the "arch" parameter;
I based my code on how py-inferior does, so:

observer "normal stop" --> get_current_arch ()
observer "exit" --> target_gdbarch

and I kept
> garch = b->gdbarch ? b->gdbarch : get_current_arch ();
in the callback, to ensure that the arch is actually the one from the BP, if any


let me know if this is not the right way to do

> Kevin> +struct breakpoint *gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat);
> Kevin>  #endif /* GDB_PYTHON_H */
>
> Newline between these two lines.

fixed

and finally, from the previous mail

> You could subclass breakpoint and add a new field there.  This would
> segregate the changes in the python subdirectory.  This may be best.

> Alternatively you could make a bp_finish breakpoint.  I'm not sure
> whether this is sufficient or whether you would also need an additional
> flag.  If the latter you could make a bp_finish_python or something like
> that -- I said earlier that this was probably invasive, but grepping for
> bp_finish makes me think it wouldn't be too bad.


I'm not sure about what you mean exactly, seems like half of it was
already done ... anyway, I think that the original problem is almost
gone:

- FinishBreakpoint inherits from Breakpoint
- there is a field "is_finish_bp" in gdb.Breakpoint C structure,
- infrun.c can know without the GIL if a breakpoint object is a
FinishBreakpoint using "gdbpy_is_stopped_at_finish_bp"


Thanks for this thorough review,

Kevin

(tested with no regression against 7.3.50.20111028-cvs, Fedora-15 X86_64)

2011-11-02  Kevin Pouget  <kevin.pouget@st.com>

	Introduce gdb.FinishBreakpoints in Python

	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-finishbreakpoint.o.
	(SUBDIR_PYTHON_SRCS): Add python/py-finishbreakpoint.c.
	Add build rule for this file.
	* infcmd.c (get_return_value): New function.
	(print_return_value): Split to create get_return_value.
	* inferior.h (get_return_value): New prototype.
	* infrun.c: Include python/python.h.
	(stop_registers): Mention FinishBreakpoint in description.
	(normal_stop): Set stop_registers if stopped at FinishBreakpoint.
	* python/py-breakpoint.c (bppy_pending_object): Make non-static.
	(gdbpy_should_stop): Disable temporary breakpoints.
	(gdbpy_breakpoint_created): Set is_py_finish_bp is necessary.
	(struct breakpoint_object): Move to python-internal.h
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(gdbpy_breakpoint_created): Initialize is_finish_bp.
	* python/python-internal.h (breakpoint_object_type): Add as extern.
	(bppy_pending_object): Likewise.
	(typedef struct breakpoint_object) Removed.
	(struct breakpoint_object): Moved from py-breakpoint.c.
	Add field is_finish_bp.
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(frame_object_to_frame_info): New prototype.
	(gdbpy_initialize_finishbreakpoints): New prototype.
	(bpfinishpy_is_finish_bp): Likewise.
	* python/py-finishbreakpoint.c: New file.
	* python/py-frame.c(frame_object_to_frame_info): Make non-static and
	accept PyObject instead of frame_object.
	(frapy_is_valid): Don't cast to frame_object.
	(frapy_name): Likewise.
	(frapy_type): Likewise.
	(frapy_unwind_stop_reason): Likewise.
	(frapy_pc): Likewise.
	(frapy_block): Likewise.
	(frapy_function): Likewise.
	(frapy_older): Likewise.
	(frapy_newer): Likewise.
	(frapy_find_sal): Likewise.
	(frapy_read_var): Likewise.
	(frapy_select): Likewise.
	* python/python.c (gdbpy_is_stopped_at_finish_bp): New noop function.
	(_initialize_python): Add gdbpy_initialize_finishbreakpoints.
	* python/python.h: Include breakpoint.h
	(gdbpy_is_stopped_at_finish_bp): New prototype.

doc/
	* gdb.texinfo (Breakpoints In Python): New subsection: Finish
	Breakpoints.
	(Python API): Add menu entry for Finish Breakpoints

testsuite/
	* gdb.python/py-breakpoint.exp (mult_line): Define and use variable
	instead of line number.
	* gdb.python/py-finish-breakpoint.c: New file.
	* gdb.python/py-finish-breakpoint.exp: New file.
	* gdb.python/py-finish-breakpoint.py: New file.
	* gdb.python/py-finish-breakpoint2.cc: New file.
	* gdb.python/py-finish-breakpoint2.exp: New file.
	* gdb.python/py-finish-breakpoint2.py: New file.

[-- Attachment #2: 0001-Introduce-gdb.FinishBreakpoints.patch --]
[-- Type: text/x-patch, Size: 55389 bytes --]

From 3f26baa6e969fbdb8778ff87af701e4f8776b159 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Tue, 20 Sep 2011 13:59:23 +0200
Subject: [PATCH] Introduce gdb.FinishBreakpoints

---
 gdb/Makefile.in                                    |    6 +
 gdb/NEWS                                           |    4 +
 gdb/doc/gdb.texinfo                                |   50 +++
 gdb/infcmd.c                                       |   21 +-
 gdb/inferior.h                                     |    3 +
 gdb/infrun.c                                       |    9 +-
 gdb/python/py-breakpoint.c                         |   50 +--
 gdb/python/py-finishbreakpoint.c                   |  442 ++++++++++++++++++++
 gdb/python/py-frame.c                              |   32 +-
 gdb/python/python-internal.h                       |   47 ++-
 gdb/python/python.c                                |    6 +
 gdb/python/python.h                                |    3 +
 gdb/testsuite/gdb.python/py-breakpoint.exp         |    7 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.c    |   97 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp  |  230 ++++++++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py   |   88 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc  |   59 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   65 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.py  |   34 ++
 19 files changed, 1187 insertions(+), 66 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 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
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.py

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 32d8ef3..6cedf12 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,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 \
@@ -318,6 +319,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 \
@@ -2103,6 +2105,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/NEWS b/gdb/NEWS
index 5cdb63e..92ce490 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -48,6 +48,10 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".
 
+  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
+     of a function.  This class is based on the "finish" command
+     available in the CLI. 
+
   ** Type objects for struct and union types now allow access to
      the fields using standard Python dictionary (mapping) methods.
      For example, "some_type['myfield']" now works, as does
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 3e78832..89f8baf 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21289,6 +21289,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Symbol Tables In Python::     Python representation of symbol tables.
 * Lazy Strings In Python::      Python representation of lazy strings.
 * Breakpoints In Python::       Manipulating breakpoints using Python.
+* Finish Breakpoints in Python:: Setting Breakpoints on function return
+                                using Python.
 @end menu
 
 @node Basic Python
@@ -24123,6 +24125,54 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@node Finish Breakpoints in Python
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A finish breakpoint is a temporary breakpoint set at the return address of
+a frame, based on the @code{finish} command.  @code{gdb.FinishBreakpoint}
+extends @code{gdb.Breakpoint}.  The underlying breakpoint will be disabled 
+and deleted when the execution will run out of the breakpoint scope (i.e.@: 
+@code{Breakpoint.stop} or @code{FinishBreakpoint.out_of_scope} triggered).
+
+@defun FinishBreakpoint.__init__ (@var{frame}, @r{[}@var{internal}@r{]})
+Create a finish breakpoint at the return address of the @code{gdb.Frame}
+object @var{frame}.  The optional @var{internal} argument allows the
+breakpoint to become invisible to the user.  @xref{Breakpoints In Python},
+for further details about this argument.
+@end defun
+
+@defun FinishBreakpoint.out_of_scope (self)
+In some circumstances (e.g.@: @code{longjmp}, C@t{++} exceptions, @value{GDBN} 
+@code{return} command, @dots{}), a function may not properly terminate, and
+thus never hit the finish breakpoint.  When @value{GDBN} notices such a
+situation, the @code{out_of_scope} callback will be triggered.
+
+You may want to sub-class @code{gdb.FinishBreakpoint} and override this
+method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+    def stop (self):
+        print "normal finish"
+        return True
+    
+    def out_of_scope ():
+        print "abnormal finish"
+@end smallexample 
+@end defun
+
+@defvar FinishBreakpoint.return_value
+When @value{GDBN} is stopped at a finish breakpoint and the frame 
+used to build the @code{gdb.FinishBreakpoint} object had debug symbols, this
+attribute will contain a @code{gdb.Value} object corresponding to the return
+value of the function.  The value will be @code{None} if the function return 
+type is @code{VOID} or if the return value was not computable.  This attribute
+is not writable.
+@end defvar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 3e8bf5d..33ce1fc 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1413,14 +1413,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.  */
+/* Return 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;
   struct ui_out *uiout = current_uiout;
 
@@ -1451,6 +1449,19 @@ 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;
+  struct ui_out *uiout = current_uiout;
+
   if (value)
     {
       struct value_print_options opts;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..f198696 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -269,6 +269,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 ac2b5ae..b989ad8 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -56,6 +56,7 @@
 #include "tracepoint.h"
 #include "continuations.h"
 #include "interps.h"
+#include "python/python.h"
 
 /* Prototypes for local functions */
 
@@ -365,7 +366,8 @@ show_stop_on_solib_events (struct ui_file *file, int from_tty,
 int stop_after_trap;
 
 /* Save register contents here when executing a "finish" command or are
-   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set.
+   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set
+   or a Python FinishBreakpoint has been hit.
    Thus this contains the return value from the called function (assuming
    values are returned in a register).  */
 
@@ -5987,8 +5989,9 @@ 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
-      && execution_direction != EXEC_REVERSE)
+  if (gdbpy_is_stopped_at_finish_bp (inferior_thread ()->control.stop_bpstat)
+      || (inferior_thread ()->control.proceed_to_finish
+          && execution_direction != EXEC_REVERSE))
     {
       /* This should not be necessary.  */
       if (stop_registers)
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index f235bbc..67de8de 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -31,52 +31,16 @@
 #include "arch-utils.h"
 #include "language.h"
 
-static 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
 {
@@ -777,6 +741,15 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
       else
 	gdbpy_print_stack ();
     }
+
+  /* If it's a temporary breakpoint.  */
+  if (bp_obj->is_finish_bp)
+    {
+      /* Can't delete it here.  */
+      gdb_assert (b->disposition == disp_del);
+      disable_breakpoint (b);
+    }
+
   do_cleanups (cleanup);
 
   return stop;
@@ -839,6 +812,7 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->number = bp->number;
       newbp->bp = bp;
       newbp->bp->py_bp_object = newbp;
+      newbp->is_finish_bp = 0;
       Py_INCREF (newbp);
       ++bppy_live;
     }
@@ -1000,7 +974,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..e69978a
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,442 @@
+/* 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.  */
+  breakpoint_object py_bp;
+  /* gdb.Type object of the value return by the breakpointed function.
+     May be NULL if no debug information was available or return type
+     was VOID.  */
+  PyObject *return_type;
+  /* gdb.Type object of the function finished by this breakpoint.  Will be
+     NULL if return_type is NULL.  */
+  PyObject *function_type;
+  /* When stopped at this FinishBreakpoint, gdb.Value object returned by
+     the function; Py_None if the value is not computable; NULL if GDB is
+     not stopped at a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* Triggered when GDB stops at PY_BP.  Computes and caches the `return_value',
+   if available.  May set Python exception flag if the return value couldn't
+   be computed.  */
+
+static void
+bpfinish_stopped_at_finish_bp (struct finish_breakpoint_object *py_bp)
+{
+  volatile struct gdb_exception except;
+
+  if (!py_bp->return_type)
+    return;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      struct value *ret =
+          get_return_value (type_object_to_type (py_bp->function_type),
+                            type_object_to_type (py_bp->return_type));
+
+      if (ret)
+        py_bp->return_value = value_to_value_object (ret);
+      else
+        py_bp->return_value = Py_None;
+    }
+  if (except.reason < 0)
+    gdbpy_convert_exception(except);
+}
+
+/* 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;
+
+  if (self_finishbp->return_type == NULL)
+    Py_RETURN_NONE;
+
+  /* Check if we have a cached value.  */
+  if (!self_finishbp->return_value)
+    {
+      bpstat bs;
+
+      BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
+
+      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)
+            {
+              bpfinish_stopped_at_finish_bp (self_finishbp);
+              if (PyErr_Occurred ())
+                return NULL;
+            }
+        }
+    }
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* Deallocate FinishBreakpoint object.  */
+
+static void
+bpfinishpy_dealloc (PyObject *self)
+{
+  struct finish_breakpoint_object *self_bpfinish =
+        (struct finish_breakpoint_object *) self;
+
+  Py_XDECREF (self_bpfinish->function_type);
+  Py_XDECREF (self_bpfinish->return_type);
+  Py_XDECREF (self_bpfinish->return_value);
+}
+
+/* If STOP_BPSTAT contains a Python breakpoint whose type is TYPE, returns
+   this breakpoint. Returns NULL otherwise.  */
+
+int
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  bpstat bs;
+
+  for (bs = stop_bpstat; bs; bs = bs->next)
+    {
+      if (bs->breakpoint_at
+          && bs->breakpoint_at->py_bp_object
+          && ((breakpoint_object *)
+              bs->breakpoint_at->py_bp_object)->is_finish_bp)
+        return 1;
+    }
+
+  return 0;
+}
+
+/* Python function to create a new breakpoint.  */
+
+static int
+bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+  static char *keywords[] = { "frame", "internal", NULL };
+  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 = NULL;
+  struct frame_id frame_id;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  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;
+  
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      prev_frame = get_prev_frame (frame);
+      if (prev_frame == 0)
+        {
+          PyErr_SetString (PyExc_ValueError,
+            _("\"FinishBreakpoint\" not meaningful in the outermost frame."));
+        }
+      else if (get_frame_type (prev_frame) == DUMMY_FRAME)
+        {
+          PyErr_SetString (PyExc_ValueError,
+                   _("\"FinishBreakpoint\" cannot be set on a dummy frame."));
+        }
+      else
+        {
+          frame_id = get_frame_id (prev_frame);
+          if (frame_id_eq (frame_id, null_frame_id))
+            PyErr_SetString (PyExc_ValueError,
+                                     _("Invalid ID for the `frame' object."));
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception(except);
+      return -1;
+    }
+  else if (PyErr_Occurred ())
+    return -1;
+
+  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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  self_bpfinish->return_type = NULL;
+  self_bpfinish->function_type = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (get_frame_pc_if_available (frame, &pc))
+        {
+          function = find_pc_function (pc);
+          if (function != NULL)
+            {
+              struct type *ret_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+              /* Remember only non-VOID return types.  */
+              if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
+                {
+                  self_bpfinish->return_type = type_to_type_object (ret_type);
+                  self_bpfinish->function_type =
+                      type_to_type_object (SYMBOL_TYPE (function));
+                }
+            }
+        }
+    }
+  if (except.reason < 0
+      || !self_bpfinish->return_type || !self_bpfinish->function_type)
+    {
+      /* Won't be able to compute return value.  */
+      self_bpfinish->return_type = NULL;
+      self_bpfinish->function_type = NULL;
+    }
+
+  bppy_pending_object = &self_bpfinish->py_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, -1,
+                         0,
+                         1 /*temp_flag*/,
+                         bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         &bkpt_breakpoint_ops,
+                         0, 1, internal_bp);
+    }
+  GDB_PY_SET_HANDLE_EXCEPTION (except);
+
+  BPPY_SET_REQUIRE_VALID (&self_bpfinish->py_bp);
+  
+  self_bpfinish->py_bp.bp->frame_id = frame_id;
+  self_bpfinish->py_bp.is_finish_bp = 1;
+  
+  return 0;
+  
+ invalid_frame:
+  PyErr_SetString (PyExc_ValueError, 
+                   _("Invalid ID for the `frame' object."));
+  return -1;
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack.  Triggers the method OUT_OF_SCOPE if implemented,
+   then delete the breakpoint.  */
+
+static void
+bpfinishpy_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))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+
+  delete_breakpoint (bpfinish_obj->py_bp.bp);
+}
+
+/* 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 and its frame is
+     not anymore in the current callstack.  */
+  if (py_bp != NULL && b->py_bp_object->is_finish_bp)
+    {
+      struct finish_breakpoint_object *finish_bp =
+          (struct finish_breakpoint_object *) py_bp;
+
+      if (b == bp_stopped)
+        {
+          bpfinish_stopped_at_finish_bp (finish_bp);
+          if (PyErr_Occurred ())
+            gdbpy_print_stack ();
+        }
+      else if (b->pspace == current_inferior ()->pspace
+           && (!target_has_registers
+               || frame_find_by_id (b->frame_id) == NULL))
+        {
+          bpfinishpy_out_of_scope (finish_bp);
+        }
+    }
+  do_cleanups (cleanup);
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run
+   out of the scope of any FinishBreakpoint before it has been hit.  */
+
+static void
+bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
+{
+  //struct cleanup *cleanup = ensure_python_env (get_current_arch (),
+  //                                             current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
+                            bs == NULL ? NULL : bs->breakpoint_at);
+
+  //do_cleanups (cleanup);
+}
+
+/* Attached to `exit' notifications, triggers all the necessary out of
+   scope notifications.  */
+
+static void
+bpfinishpy_handle_exit (struct inferior *inf)
+{
+  //struct cleanup *cleanup = ensure_python_env (target_gdbarch,
+  //    current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
+
+  //do_cleanups (cleanup);
+}
+
+/* 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 PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "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*/
+  bpfinishpy_dealloc,             /*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 20064ca..c334f63 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);
@@ -106,7 +107,7 @@ frapy_is_valid (PyObject *self, PyObject *args)
 
   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      frame = frame_object_to_frame_info ((frame_object *) self);
+      frame = frame_object_to_frame_info (self);
     }
   GDB_PY_HANDLE_EXCEPTION (except);
 
@@ -130,7 +131,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);
     }
@@ -159,7 +160,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);
     }
@@ -180,7 +181,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);
 
@@ -201,7 +202,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);
     }
@@ -222,7 +223,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);
@@ -263,7 +264,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));
     }
@@ -330,7 +331,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)
@@ -359,7 +360,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)
@@ -388,7 +389,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);
@@ -444,7 +445,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);
@@ -472,7 +473,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);
     }
@@ -487,12 +488,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 ef39d5d..4d46466 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -120,9 +120,50 @@ 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;
+
+  /* 1 is this is a FinishBreakpoint object, 0 otherwise.  */
+  int is_finish_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
 {
@@ -188,6 +229,7 @@ struct value *convert_value_from_python (PyObject *obj);
 struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
+struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);
 
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
@@ -202,6 +244,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 3a5a6b5..55c0c1e 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1048,6 +1048,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
@@ -1237,6 +1242,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 ae55cc2..cdf0263 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,6 @@ 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 e0dd087..0e3adbd 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.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..5b708e3
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,97 @@
+/* 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>
+#include <stdlib.h>
+#include <unistd.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);
+}
+
+void
+test_exec_exit(int do_exit)
+{
+  if (do_exit)
+    exit(0);
+  else
+    execl ("/bin/echo", "echo", "-1", (char *)0);
+}
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  getpid();
+
+  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. */
+
+  test_exec_exit(1);
+
+  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..c4d3bef
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,230 @@
+# 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 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_breakpoint "increase_1"
+gdb_test "continue" "Breakpoint .*at.*" "continue to the function to finish"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval ('a'), gdb.selected_frame ())" \
+         "Temporary breakpoint.*" "set FinishBreakpoint"
+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.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer normal hit"
+
+# check FinishBreakpoint in main no allowed
+
+gdb_test "finish" "main.*" "return to main()"
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         "ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \
+         "check FinishBP not allowed in main"
+
+#
+# Test FinishBreakpoint with no debug symbol 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+set cond_line [gdb_get_line_number "Condition Break."]
+
+if ![runto "getpid"] then {
+    fail "Cannot run to getpid."
+    return 0
+}
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "continue" "SimpleFinishBreakpoint stop.*" "check FinishBreakpoint hit"
+gdb_test "python print finishBP.return_value" "None" "check return value without debug symbol"
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "python finishbp = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" \
+         "check FinishBP out of scope notification"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy 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_1(i,8)" "Breakpoint .* at .*" \
+         "set a conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" \
+         "set FinishBP in a breakpoint condition"
+gdb_test "continue" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+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"
+
+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 explicit inferior function call
+#
+
+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
+}
+
+# return address in dummy frame
+
+gdb_test "python TestExplicitBreakpoint('increase_1')" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase_1(&i)" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+
+# return address in normal frame
+
+delete_breakpoints
+gdb_test "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase(&i)" \
+         "SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*The program being debugged stopped while in a function called from GDB.*" \
+         "FinishBP stop at during explicit function call"
+
+
+#
+# Test FinishBreakpoint when inferior exits
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}
+
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
+
+#
+# Test FinishBreakpoint when inferior execs
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}     
+
+gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
+gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ 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..adf10b0
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,88 @@
+# 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 MyFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		gdb.FinishBreakpoint.__init__ (self, 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..."
+
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__(self, spec="test_1", internal=1)
+        self.silent = True
+        self.count = 0
+        print "TestBreakpoint init"
+        
+    def stop(self):
+    	self.count += 1
+    	try:
+        	TestFinishBreakpoint(gdb.newest_frame(), self.count)
+        except ValueError as e:
+        	print e
+        return False
+
+class TestFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame, count):
+    	self.count = count
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        
+        
+    def stop(self):
+        print "-->", self.number
+        if (self.count == 3):
+            print "test stop ... %d" % self.count
+            return True
+        else:
+            print "test don't stop %d" % self.count
+            return False 
+        
+    
+    def out_of_scope(self):
+        print "test didn't finish ... %d" % self.count
+
+class TestExplicitBreakpoint(gdb.Breakpoint):
+	def stop(self):
+		try:
+			SimpleFinishBreakpoint(gdb.newest_frame())
+		except ValueError as e:
+			print e
+		return False
+
+class SimpleFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		
+		print "SimpleFinishBreakpoint init"
+		
+	def stop(self):
+		print "SimpleFinishBreakpoint stop" 
+		return True
+	
+	def out_of_scope(self):
+		print "SimpleFinishBreakpoint out of scope..."
+
+print "Python script importedd"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.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-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
new file mode 100644
index 0000000..fae798c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -0,0 +1,65 @@
+# 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-breakpoint2"
+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
+}
+
+#
+# Check FinishBreakpoints against C++ exceptions
+#
+
+gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_breakpoint "throw_exception_1"
+gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
+
+gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.py b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
new file mode 100644
index 0000000..9ea4cf1
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
@@ -0,0 +1,34 @@
+# 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 ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        self.silent = True;
+        print "init ExceptionFinishBreakpoint"
+        
+    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"
-- 
1.7.6.4


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

* Re: [RFC] Python Finish Breakpoints
  2011-11-02 14:44                                       ` Kevin Pouget
@ 2011-11-04 14:25                                         ` Kevin Pouget
  2011-11-04 21:04                                           ` Tom Tromey
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-11-04 14:25 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, pmuldoon

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

On Wed, Nov 2, 2011 at 3:43 PM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> On Fri, Oct 28, 2011 at 10:37 PM, Tom Tromey <tromey@redhat.com> wrote:
>
> Hello,
> thanks for you're answer, I reply inline
>
>> You need a ChangeLog.
>
> sorry, I forgot to include it in the e-mail, you'll find it at the bottom
>
>> Kevin> -static PyTypeObject breakpoint_object_type;
>> Kevin> +PyTypeObject breakpoint_object_type;
>>
>> The old line here was a forward declaration.
>> I think you should just entirely remove it.
>>
>> Kevin> +  /* If it's a temporary breakpoint.  */
>> Kevin> +  if (bpfinishpy_is_finish_bp (py_bp))
>> Kevin> +    {
>> Kevin> +      /* Can't delete it here.  */
>> Kevin> +      gdb_assert (b->disposition == disp_del);
>> Kevin> +      disable_breakpoint (b);
>> Kevin> +    }
>>
>> I don't understand this code.
>> It seems to me that disp_del is a better setting here.
>
> I need to do it this way because as far as I've seen,
> "breakpoint_auto_delete" is only triggered in infrun.c::normal_stop,
> so if Breakpoint.stop returns False, the breakpoint is not deleted
> immediately, and may be hit twice.
> So I disable the breakpoint to avoid it.
>
>> Kevin> +      newbp->is_finish_bp = bpfinishpy_is_finish_bp ((PyObject *) newbp);
>>
>> I think this flag could be set more cleanly in bpfinishpy_init.
>
> you're right, fixed
>
>> Kevin> +  /* gdb.Type object of the function finished by this breakpoint.  */
>> Kevin> +  PyObject *function_type;
>> Kevin> +  /* gdb.Type object of the value return by the breakpointed function.  */
>> Kevin> +  PyObject *return_type;
>> Kevin> +  /* When stopped at this FinishBreakpoint, value returned by the function;
>> Kevin> +     Py_None if the value is not computable; NULL if GDB is not stopped at
>> Kevin> +     a FinishBreakpoint.  */
>> Kevin> +  PyObject *return_value;
>>
>> I think the new class needs a destructor which decrefs these.
>
> fixed
>
>> Kevin> +static void
>> Kevin> +bpfinish_stopped_at_finish_bp (struct finish_breakpoint_object *py_bp)
>> Kevin> +{
>> Kevin> +  if (py_bp->return_type)
>> Kevin> +    {
>> Kevin> +      struct value *ret =
>> Kevin> +          get_return_value (type_object_to_type (py_bp->function_type),
>> Kevin> +                            type_object_to_type (py_bp->return_type));
>>
>> Calls into gdb have to be wrapped in TRY_CATCH.
>> Then errors have to be propagated to Python somehow, or printed and
>> ignored via gdbpy_print_stack.  Given the context I would say propagate.
>
> you're right,
>
> bpfinish_stopped_at_finish_bp is called in 2 situations, so it just
> sets the error flag, then
>
> - direct access to returnvalue member --> propage exception to Python
> - bpfinishpy_handle_stop --> bpfinishpy_handle_stop()
>
>> Kevin> +  /* Check if we have a cached value.  */
>> Kevin> +  if (!self_finishbp->return_value)
>> Kevin> +    {
>> Kevin> +      bpstat bs;
>> Kevin> +
>> Kevin> +      BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
>> Kevin> +
>> Kevin> +      for (bs = inferior_thread ()->control.stop_bpstat;
>> Kevin> +          bs; bs = bs->next)
>> Kevin> +        {
>> Kevin> +          struct breakpoint *bp = bs->breakpoint_at;
>> Kevin> +
>> Kevin> +          if (bp != NULL && (PyObject *) bp->py_bp_object == self)
>> Kevin> +              bpfinish_stopped_at_finish_bp (self_finishbp);
>> Kevin> +        }
>>
>> It seems like it should be an error to try to compute the return value
>> when not stopped at this breakpoint.
>
> I'm not totally convinced ...
> what would you think about throwing an AttributeError("return_value
> not available yet") when accessing the attribute before the breakpoint
> is hit, but keep the cached value afterwards?
>
>
>> Kevin> +struct breakpoint *
>> Kevin> +gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
>> Kevin> +{
>>
>> Since the name implies that this is a predicate and since the result is
>> only ever used as a boolean, I think this should return int.
>
> changed
>
>> Kevin> +  bpstat bs;
>> Kevin> +
>> Kevin> +  for (bs = stop_bpstat; bs; bs = bs->next)
>> Kevin> +    {
>> Kevin> +      if (bs->breakpoint_at
>> Kevin> +          && bpfinishpy_bp_is_finish_bp ((breakpoint_object *)
>> Kevin> +                                         bs->breakpoint_at->py_bp_object))
>>
>> I am not really sure about this.  It seems like it may be pedantically
>> incorrect, though it is hard to see when it could possibly fail.  That
>> is, is the GIL required or not?  It doesn't call a function and the
>> breakpoint owns a ref to the breakpoint object, so it seems like it
>> could not be deleted out from under us.
>>
>> I'm inclined to say it is ok.
>
> no, the GIL is not required.
> This function is triggered from infrun.c:normal_stop, to determine if
> we want to save
> the stop_registers, like it's done for `finish'
>
>> Kevin> +  breakpoint_object *self_bp = (breakpoint_object *) self;
>> Kevin> +  struct finish_breakpoint_object *self_bpfinish =
>> Kevin> +      (struct finish_breakpoint_object *) self;
>>
>> I think this is the only use of self in this function.
>> Just drop it and cast directly to the most specific subclass.
>
> I'm not sure what you meant, *self_bp* was redundant so I removed it
>
>> Kevin> +  prev_frame = get_prev_frame (frame);
>> Kevin> +  if (prev_frame == 0)
>> Kevin> +    {
>> Kevin> +      PyErr_SetString (PyExc_ValueError,
>> Kevin> +           _("\"FinishBreakpoint\" not meaningful in the outermost frame."));
>> Kevin> +      return -1;
>> Kevin> +    }
>> Kevin> +  else if (get_frame_type (prev_frame) == DUMMY_FRAME)
>> Kevin> +      {
>> Kevin> +        PyErr_SetString (PyExc_ValueError,
>> Kevin> +                  _("\"FinishBreakpoint\" cannot be set on a dummy frame."));
>> Kevin> +        return -1;
>> Kevin> +      }
>>
>> I think the calls to get_prev_frame and get_frame_type need to be
>> wrapped in a TRY_CATCH.
>>
>> Kevin> +  frame_id = get_frame_id (prev_frame);
>> Kevin> +  if (frame_id_eq (frame_id, null_frame_id))
>>
>> Likewise.
>> I'd try to put all the gdb-facing logic into a single big TRY_CATCH.
>
> I wrapped them all and propagate the error with gdbpy_convert_exception
>
>> Kevin> +  if (internal)
>> Kevin> +    {
>> Kevin> +      internal_bp = PyObject_IsTrue (internal);
>> Kevin> +      if (internal_bp == -1)
>> Kevin> +        {
>> Kevin> +          PyErr_SetString (PyExc_ValueError,
>> Kevin> +                           _("The value of `internal' must be a boolean."));
>> Kevin> +          return -1;
>>
>> Do you need to decref 'frame_obj' here?  I suspect so.
>> There are other early returns that probably need this.
>> A typical solution is a label where all the locals are xdecref'd then
>> return -1.
>
>> PyArg_ParseTupleAndKeywords (args, kwargs, "O|O", keywords, &frame_obj, &internal)
>
> No, according to the documentation,
>> Note that any Python object references which are provided to the caller are borrowed references; do not decrement their reference count!
>
>
>> Kevin> +  /* Find the function we will return from.  */
>> Kevin> +  self_bpfinish->return_type = NULL;
>> Kevin> +  self_bpfinish->function_type = NULL;
>>
>> These can be left NULL in the object.  What happens if you try to fetch
>> the return value in that case?
>
> I've updated the comments to make it clearer:
>
>  /* gdb.Type object of the value return by the breakpointed function.
>     May be NULL if no debug information was available or return type
>     was VOID.  */
>  PyObject *return_type;
>  /* gdb.Type object of the function finished by this breakpoint.  Will be
>     NULL if return_type is NULL.  */
>  PyObject *function_type;
>
> if return_type is NULL, "return_value" attribute will be None (the
> return value is not computed/computable)
>
> I've updated the doc as well:
>> The value will be @code{None} if the function return type is @code{VOID} or if the return value was not computable.
>
>> Kevin> +  if (get_frame_pc_if_available (frame, &pc))
>> Kevin> +    {
>> Kevin> +      function = find_pc_function (pc);
>> Kevin> +      if (function != NULL)
>> Kevin> +        {
>> Kevin> +          struct type *ret_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
>> Kevin> +
>>
>> More TRY_CATCH.
>>
>> Kevin> +          /* Remember only non-VOID return types.  */
>> Kevin> +          if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
>> Kevin> +            {
>> Kevin> +              self_bpfinish->return_type = type_to_type_object (ret_type);
>>
>> Error check.
>>
>> Kevin> +              self_bpfinish->function_type =
>> Kevin> +                  type_to_type_object (SYMBOL_TYPE (function));
>>
>> Likewise.
>
> I'm not sure about the best way to handle these exceptions, so for all
> the error thrown when trying to compute the return_type/function_type,
> I just silently swallow it, and set the return_type to None.
>
> Propagating the exception is not suitable, because I don't want not to
> prevent the F_BP creation just because of the return value;
> and I don't want to write a warning within Python code ...
>
>
>> Kevin> +  if (except.reason < 0)
>> Kevin> +    {
>> Kevin> +      PyErr_Format (except.reason == RETURN_QUIT
>> Kevin> +                    ? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
>> Kevin> +                    "%s", except.message);
>> Kevin> +      return -1;
>>
>> Just use GDB_PY_SET_HANDLE_EXCEPTION.
>
> right, fixed
> (the same should apply to gdb.Breakpoint creation, I copied the code from there)
>
>> Kevin> +int
>> Kevin> +bpfinishpy_is_finish_bp (PyObject *obj)
>> Kevin> +{
>> Kevin> +  return obj != NULL
>> Kevin> +         && PyObject_TypeCheck (obj, &finish_breakpoint_object_type);
>> Kevin> +}
>>
>> Kevin> +int
>> Kevin> +bpfinishpy_bp_is_finish_bp (breakpoint_object *bp_obj)
>> Kevin> +{
>> Kevin> +  return bp_obj != NULL
>> Kevin> +         && bp_obj->is_finish_bp;
>> Kevin> +}
>>
>> Are both of these needed?
>
> no, not anymore,
> I even removed both of them,
>
>> newbp->is_finish_bp = bpfinishpy_is_finish_bp ((PyObject *) newbp);
>> I think this flag could be set more cleanly in bpfinishpy_init.
>
> but I've rewritten this bit
>
>> Kevin> +static void
>> Kevin> +bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
>> Kevin> +{
>> Kevin> +  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
>> Kevin> +                            bs == NULL ? NULL : bs->breakpoint_at);
>> Kevin> +}
>>
>> The way this is written, it will acquire and release the GIL for each
>> breakpoint.
>>
>> I think it would be better to acquire + release just once.
>>
>> Kevin> +static void
>> Kevin> +bpfinishpy_handle_exit (struct inferior *inf)
>> Kevin> +{
>> Kevin> +  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
>> Kevin> +}
>>
>> Likewise.
>
> fixed
>
> I'm not 100% confident with this "ensure_python_env" and the value of
> the "arch" parameter;
> I based my code on how py-inferior does, so:
>
> observer "normal stop" --> get_current_arch ()
> observer "exit" --> target_gdbarch
>
> and I kept
>> garch = b->gdbarch ? b->gdbarch : get_current_arch ();
> in the callback, to ensure that the arch is actually the one from the BP, if any
>
>
> let me know if this is not the right way to do
>
>> Kevin> +struct breakpoint *gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat);
>> Kevin>  #endif /* GDB_PYTHON_H */
>>
>> Newline between these two lines.
>
> fixed
>
> and finally, from the previous mail
>
>> You could subclass breakpoint and add a new field there.  This would
>> segregate the changes in the python subdirectory.  This may be best.
>
>> Alternatively you could make a bp_finish breakpoint.  I'm not sure
>> whether this is sufficient or whether you would also need an additional
>> flag.  If the latter you could make a bp_finish_python or something like
>> that -- I said earlier that this was probably invasive, but grepping for
>> bp_finish makes me think it wouldn't be too bad.
>
>
> I'm not sure about what you mean exactly, seems like half of it was
> already done ... anyway, I think that the original problem is almost
> gone:
>
> - FinishBreakpoint inherits from Breakpoint
> - there is a field "is_finish_bp" in gdb.Breakpoint C structure,
> - infrun.c can know without the GIL if a breakpoint object is a
> FinishBreakpoint using "gdbpy_is_stopped_at_finish_bp"
>
>
> Thanks for this thorough review,
>
> Kevin

Hello,

there was a little confusion in the previous patch,
ensure_python_env/cleanup commented out,

here is a cleaner version.

Thanks,

Kevin


2011-11-04  Kevin Pouget  <kevin.pouget@st.com>

	Introduce gdb.FinishBreakpoints in Python

	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-finishbreakpoint.o.
	(SUBDIR_PYTHON_SRCS): Add python/py-finishbreakpoint.c.
	Add build rule for this file.
	* infcmd.c (get_return_value): New function.
	(print_return_value): Split to create get_return_value.
	* inferior.h (get_return_value): New prototype.
	* infrun.c: Include python/python.h.
	(stop_registers): Mention FinishBreakpoint in description.
	(normal_stop): Set stop_registers if stopped at FinishBreakpoint.
	* python/py-breakpoint.c (bppy_pending_object): Make non-static.
	(gdbpy_should_stop): Disable temporary breakpoints.
	(gdbpy_breakpoint_created): Set is_py_finish_bp is necessary.
	(struct breakpoint_object): Move to python-internal.h
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(gdbpy_breakpoint_created): Initialize is_finish_bp.
	* python/python-internal.h (breakpoint_object_type): Add as extern.
	(bppy_pending_object): Likewise.
	(typedef struct breakpoint_object) Removed.
	(struct breakpoint_object): Moved from py-breakpoint.c.
	Add field is_finish_bp.
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(frame_object_to_frame_info): New prototype.
	(gdbpy_initialize_finishbreakpoints): New prototype.
	(bpfinishpy_is_finish_bp): Likewise.
	* python/py-finishbreakpoint.c: New file.
	* python/py-frame.c(frame_object_to_frame_info): Make non-static and
	accept PyObject instead of frame_object.
	(frapy_is_valid): Don't cast to frame_object.
	(frapy_name): Likewise.
	(frapy_type): Likewise.
	(frapy_unwind_stop_reason): Likewise.
	(frapy_pc): Likewise.
	(frapy_block): Likewise.
	(frapy_function): Likewise.
	(frapy_older): Likewise.
	(frapy_newer): Likewise.
	(frapy_find_sal): Likewise.
	(frapy_read_var): Likewise.
	(frapy_select): Likewise.
	* python/python.c (gdbpy_is_stopped_at_finish_bp): New noop function.
	(_initialize_python): Add gdbpy_initialize_finishbreakpoints.
	* python/python.h: Include breakpoint.h
	(gdbpy_is_stopped_at_finish_bp): New prototype.

doc/
	* gdb.texinfo (Breakpoints In Python): New subsection: Finish
	Breakpoints.
	(Python API): Add menu entry for Finish Breakpoints

testsuite/
	* gdb.python/py-breakpoint.exp (mult_line): Define and use variable
	instead of line number.
	* gdb.python/py-finish-breakpoint.c: New file.
	* gdb.python/py-finish-breakpoint.exp: New file.
	* gdb.python/py-finish-breakpoint.py: New file.
	* gdb.python/py-finish-breakpoint2.cc: New file.
	* gdb.python/py-finish-breakpoint2.exp: New file.
	* gdb.python/py-finish-breakpoint2.py: New file.

[-- Attachment #2: 0001-Introduce-gdb.FinishBreakpoints.patch --]
[-- Type: text/x-patch, Size: 55389 bytes --]

From ad87340e9eaa52b1b72c7ab7837fe02ac243e3ba Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Tue, 20 Sep 2011 13:59:23 +0200
Subject: [PATCH] Introduce gdb.FinishBreakpoint

---
 gdb/Makefile.in                                    |    6 +
 gdb/NEWS                                           |    4 +
 gdb/doc/gdb.texinfo                                |   50 +++
 gdb/infcmd.c                                       |   21 +-
 gdb/inferior.h                                     |    3 +
 gdb/infrun.c                                       |    9 +-
 gdb/python/py-breakpoint.c                         |   50 +--
 gdb/python/py-finishbreakpoint.c                   |  443 ++++++++++++++++++++
 gdb/python/py-frame.c                              |   32 +-
 gdb/python/python-internal.h                       |   47 ++-
 gdb/python/python.c                                |    6 +
 gdb/python/python.h                                |    3 +
 gdb/testsuite/gdb.python/py-breakpoint.exp         |    7 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.c    |   97 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp  |  230 ++++++++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py   |   88 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc  |   59 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   65 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.py  |   34 ++
 19 files changed, 1188 insertions(+), 66 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 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
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.py

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 7a7ff9f..31cda1b 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,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 \
@@ -318,6 +319,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 \
@@ -2103,6 +2105,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/NEWS b/gdb/NEWS
index 1713049..b170b0a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -51,6 +51,10 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".
 
+  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
+     of a function.  This class is based on the "finish" command
+     available in the CLI. 
+
   ** Type objects for struct and union types now allow access to
      the fields using standard Python dictionary (mapping) methods.
      For example, "some_type['myfield']" now works, as does
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 93450c6..3de11fc 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21405,6 +21405,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Symbol Tables In Python::     Python representation of symbol tables.
 * Lazy Strings In Python::      Python representation of lazy strings.
 * Breakpoints In Python::       Manipulating breakpoints using Python.
+* Finish Breakpoints in Python:: Setting Breakpoints on function return
+                                using Python.
 @end menu
 
 @node Basic Python
@@ -24239,6 +24241,54 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@node Finish Breakpoints in Python
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A finish breakpoint is a temporary breakpoint set at the return address of
+a frame, based on the @code{finish} command.  @code{gdb.FinishBreakpoint}
+extends @code{gdb.Breakpoint}.  The underlying breakpoint will be disabled 
+and deleted when the execution will run out of the breakpoint scope (i.e.@: 
+@code{Breakpoint.stop} or @code{FinishBreakpoint.out_of_scope} triggered).
+
+@defun FinishBreakpoint.__init__ (@var{frame}, @r{[}@var{internal}@r{]})
+Create a finish breakpoint at the return address of the @code{gdb.Frame}
+object @var{frame}.  The optional @var{internal} argument allows the
+breakpoint to become invisible to the user.  @xref{Breakpoints In Python},
+for further details about this argument.
+@end defun
+
+@defun FinishBreakpoint.out_of_scope (self)
+In some circumstances (e.g.@: @code{longjmp}, C@t{++} exceptions, @value{GDBN} 
+@code{return} command, @dots{}), a function may not properly terminate, and
+thus never hit the finish breakpoint.  When @value{GDBN} notices such a
+situation, the @code{out_of_scope} callback will be triggered.
+
+You may want to sub-class @code{gdb.FinishBreakpoint} and override this
+method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+    def stop (self):
+        print "normal finish"
+        return True
+    
+    def out_of_scope ():
+        print "abnormal finish"
+@end smallexample 
+@end defun
+
+@defvar FinishBreakpoint.return_value
+When @value{GDBN} is stopped at a finish breakpoint and the frame 
+used to build the @code{gdb.FinishBreakpoint} object had debug symbols, this
+attribute will contain a @code{gdb.Value} object corresponding to the return
+value of the function.  The value will be @code{None} if the function return 
+type is @code{VOID} or if the return value was not computable.  This attribute
+is not writable.
+@end defvar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 7b935fe..ce21851 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1413,14 +1413,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.  */
+/* Return 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;
   struct ui_out *uiout = current_uiout;
 
@@ -1451,6 +1449,19 @@ 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;
+  struct ui_out *uiout = current_uiout;
+
   if (value)
     {
       struct value_print_options opts;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..f198696 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -269,6 +269,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 7ec0788..4d6db30 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -57,6 +57,7 @@
 #include "continuations.h"
 #include "interps.h"
 #include "skip.h"
+#include "python/python.h"
 
 /* Prototypes for local functions */
 
@@ -366,7 +367,8 @@ show_stop_on_solib_events (struct ui_file *file, int from_tty,
 int stop_after_trap;
 
 /* Save register contents here when executing a "finish" command or are
-   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set.
+   about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set
+   or a Python FinishBreakpoint has been hit.
    Thus this contains the return value from the called function (assuming
    values are returned in a register).  */
 
@@ -6030,8 +6032,9 @@ 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
-      && execution_direction != EXEC_REVERSE)
+  if (gdbpy_is_stopped_at_finish_bp (inferior_thread ()->control.stop_bpstat)
+      || (inferior_thread ()->control.proceed_to_finish
+          && execution_direction != EXEC_REVERSE))
     {
       /* This should not be necessary.  */
       if (stop_registers)
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index f235bbc..67de8de 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -31,52 +31,16 @@
 #include "arch-utils.h"
 #include "language.h"
 
-static 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
 {
@@ -777,6 +741,15 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
       else
 	gdbpy_print_stack ();
     }
+
+  /* If it's a temporary breakpoint.  */
+  if (bp_obj->is_finish_bp)
+    {
+      /* Can't delete it here.  */
+      gdb_assert (b->disposition == disp_del);
+      disable_breakpoint (b);
+    }
+
   do_cleanups (cleanup);
 
   return stop;
@@ -839,6 +812,7 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->number = bp->number;
       newbp->bp = bp;
       newbp->bp->py_bp_object = newbp;
+      newbp->is_finish_bp = 0;
       Py_INCREF (newbp);
       ++bppy_live;
     }
@@ -1000,7 +974,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..26dc7ef
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,443 @@
+/* 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.  */
+  breakpoint_object py_bp;
+  /* gdb.Type object of the value return by the breakpointed function.
+     May be NULL if no debug information was available or return type
+     was VOID.  */
+  PyObject *return_type;
+  /* gdb.Type object of the function finished by this breakpoint.  Will be
+     NULL if return_type is NULL.  */
+  PyObject *function_type;
+  /* When stopped at this FinishBreakpoint, gdb.Value object returned by
+     the function; Py_None if the value is not computable; NULL if GDB is
+     not stopped at a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* Triggered when GDB stops at PY_BP.  Computes and caches the `return_value',
+   if available.  May set Python exception flag if the return value couldn't
+   be computed.  */
+
+static void
+bpfinish_stopped_at_finish_bp (struct finish_breakpoint_object *py_bp)
+{
+  volatile struct gdb_exception except;
+
+  if (!py_bp->return_type)
+    return;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      struct value *ret =
+          get_return_value (type_object_to_type (py_bp->function_type),
+                            type_object_to_type (py_bp->return_type));
+
+      if (ret)
+        py_bp->return_value = value_to_value_object (ret);
+      else
+        py_bp->return_value = Py_None;
+    }
+  if (except.reason < 0)
+    gdbpy_convert_exception(except);
+}
+
+/* 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;
+
+  if (self_finishbp->return_type == NULL)
+    Py_RETURN_NONE;
+
+  /* Check if we have a cached value.  */
+  if (!self_finishbp->return_value)
+    {
+      bpstat bs;
+
+      BPPY_REQUIRE_VALID (&self_finishbp->py_bp);
+
+      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)
+            {
+              bpfinish_stopped_at_finish_bp (self_finishbp);
+              if (PyErr_Occurred ())
+                return NULL;
+            }
+        }
+    }
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* Deallocate FinishBreakpoint object.  */
+
+static void
+bpfinishpy_dealloc (PyObject *self)
+{
+  struct finish_breakpoint_object *self_bpfinish =
+        (struct finish_breakpoint_object *) self;
+
+  Py_XDECREF (self_bpfinish->function_type);
+  Py_XDECREF (self_bpfinish->return_type);
+  Py_XDECREF (self_bpfinish->return_value);
+}
+
+/* If STOP_BPSTAT contains a Python breakpoint whose type is TYPE, returns
+   this breakpoint. Returns NULL otherwise.  */
+
+int
+gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat)
+{
+  bpstat bs;
+
+  for (bs = stop_bpstat; bs; bs = bs->next)
+    {
+      if (bs->breakpoint_at
+          && bs->breakpoint_at->py_bp_object
+          && ((breakpoint_object *)
+              bs->breakpoint_at->py_bp_object)->is_finish_bp)
+        return 1;
+    }
+
+  return 0;
+}
+
+/* Python function to create a new breakpoint.  */
+
+static int
+bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+  static char *keywords[] = { "frame", "internal", NULL };
+  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 = NULL;
+  struct frame_id frame_id;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  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;
+  
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      prev_frame = get_prev_frame (frame);
+      if (prev_frame == 0)
+        {
+          PyErr_SetString (PyExc_ValueError,
+            _("\"FinishBreakpoint\" not meaningful in the outermost frame."));
+        }
+      else if (get_frame_type (prev_frame) == DUMMY_FRAME)
+        {
+          PyErr_SetString (PyExc_ValueError,
+                   _("\"FinishBreakpoint\" cannot be set on a dummy frame."));
+        }
+      else
+        {
+          frame_id = get_frame_id (prev_frame);
+          if (frame_id_eq (frame_id, null_frame_id))
+            PyErr_SetString (PyExc_ValueError,
+                                     _("Invalid ID for the `frame' object."));
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception(except);
+      return -1;
+    }
+  else if (PyErr_Occurred ())
+    return -1;
+
+  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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  self_bpfinish->return_type = NULL;
+  self_bpfinish->function_type = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (get_frame_pc_if_available (frame, &pc))
+        {
+          function = find_pc_function (pc);
+          if (function != NULL)
+            {
+              struct type *ret_type =
+                  TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+              /* Remember only non-VOID return types.  */
+              if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
+                {
+                  self_bpfinish->return_type = type_to_type_object (ret_type);
+                  self_bpfinish->function_type =
+                      type_to_type_object (SYMBOL_TYPE (function));
+                }
+            }
+        }
+    }
+  if (except.reason < 0
+      || !self_bpfinish->return_type || !self_bpfinish->function_type)
+    {
+      /* Won't be able to compute return value.  */
+      self_bpfinish->return_type = NULL;
+      self_bpfinish->function_type = NULL;
+    }
+
+  bppy_pending_object = &self_bpfinish->py_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, -1,
+                         0,
+                         1 /*temp_flag*/,
+                         bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         &bkpt_breakpoint_ops,
+                         0, 1, internal_bp);
+    }
+  GDB_PY_SET_HANDLE_EXCEPTION (except);
+
+  BPPY_SET_REQUIRE_VALID (&self_bpfinish->py_bp);
+  
+  self_bpfinish->py_bp.bp->frame_id = frame_id;
+  self_bpfinish->py_bp.is_finish_bp = 1;
+  
+  return 0;
+  
+ invalid_frame:
+  PyErr_SetString (PyExc_ValueError, 
+                   _("Invalid ID for the `frame' object."));
+  return -1;
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack.  Triggers the method OUT_OF_SCOPE if implemented,
+   then delete the breakpoint.  */
+
+static void
+bpfinishpy_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))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+
+  delete_breakpoint (bpfinish_obj->py_bp.bp);
+}
+
+/* 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 and its frame is
+     not anymore in the current callstack.  */
+  if (py_bp != NULL && b->py_bp_object->is_finish_bp)
+    {
+      struct finish_breakpoint_object *finish_bp =
+          (struct finish_breakpoint_object *) py_bp;
+
+      if (b == bp_stopped)
+        {
+          bpfinish_stopped_at_finish_bp (finish_bp);
+          if (PyErr_Occurred ())
+            gdbpy_print_stack ();
+        }
+      else if (b->pspace == current_inferior ()->pspace
+           && (!target_has_registers
+               || frame_find_by_id (b->frame_id) == NULL))
+        {
+          bpfinishpy_out_of_scope (finish_bp);
+        }
+    }
+  do_cleanups (cleanup);
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run
+   out of the scope of any FinishBreakpoint before it has been hit.  */
+
+static void
+bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
+{
+  struct cleanup *cleanup = ensure_python_env (get_current_arch (),
+                                               current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
+                            bs == NULL ? NULL : bs->breakpoint_at);
+
+  do_cleanups (cleanup);
+}
+
+/* Attached to `exit' notifications, triggers all the necessary out of
+   scope notifications.  */
+
+static void
+bpfinishpy_handle_exit (struct inferior *inf)
+{
+  struct cleanup *cleanup = ensure_python_env (target_gdbarch,
+      current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
+
+  do_cleanups (cleanup);
+}
+
+/* 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 PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "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*/
+  bpfinishpy_dealloc,             /*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 20064ca..c334f63 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);
@@ -106,7 +107,7 @@ frapy_is_valid (PyObject *self, PyObject *args)
 
   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      frame = frame_object_to_frame_info ((frame_object *) self);
+      frame = frame_object_to_frame_info (self);
     }
   GDB_PY_HANDLE_EXCEPTION (except);
 
@@ -130,7 +131,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);
     }
@@ -159,7 +160,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);
     }
@@ -180,7 +181,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);
 
@@ -201,7 +202,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);
     }
@@ -222,7 +223,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);
@@ -263,7 +264,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));
     }
@@ -330,7 +331,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)
@@ -359,7 +360,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)
@@ -388,7 +389,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);
@@ -444,7 +445,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);
@@ -472,7 +473,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);
     }
@@ -487,12 +488,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 ef39d5d..4d46466 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -120,9 +120,50 @@ 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;
+
+  /* 1 is this is a FinishBreakpoint object, 0 otherwise.  */
+  int is_finish_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
 {
@@ -188,6 +229,7 @@ struct value *convert_value_from_python (PyObject *obj);
 struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
+struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);
 
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
@@ -202,6 +244,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 3a5a6b5..55c0c1e 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1048,6 +1048,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
@@ -1237,6 +1242,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 ae55cc2..cdf0263 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,6 @@ 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 e0dd087..0e3adbd 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.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..5b708e3
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,97 @@
+/* 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>
+#include <stdlib.h>
+#include <unistd.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);
+}
+
+void
+test_exec_exit(int do_exit)
+{
+  if (do_exit)
+    exit(0);
+  else
+    execl ("/bin/echo", "echo", "-1", (char *)0);
+}
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  getpid();
+
+  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. */
+
+  test_exec_exit(1);
+
+  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..c4d3bef
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,230 @@
+# 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 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_breakpoint "increase_1"
+gdb_test "continue" "Breakpoint .*at.*" "continue to the function to finish"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval ('a'), gdb.selected_frame ())" \
+         "Temporary breakpoint.*" "set FinishBreakpoint"
+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.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer normal hit"
+
+# check FinishBreakpoint in main no allowed
+
+gdb_test "finish" "main.*" "return to main()"
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         "ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \
+         "check FinishBP not allowed in main"
+
+#
+# Test FinishBreakpoint with no debug symbol 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+set cond_line [gdb_get_line_number "Condition Break."]
+
+if ![runto "getpid"] then {
+    fail "Cannot run to getpid."
+    return 0
+}
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "continue" "SimpleFinishBreakpoint stop.*" "check FinishBreakpoint hit"
+gdb_test "python print finishBP.return_value" "None" "check return value without debug symbol"
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "python finishbp = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" \
+         "check FinishBP out of scope notification"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy 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_1(i,8)" "Breakpoint .* at .*" \
+         "set a conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" \
+         "set FinishBP in a breakpoint condition"
+gdb_test "continue" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+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"
+
+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 explicit inferior function call
+#
+
+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
+}
+
+# return address in dummy frame
+
+gdb_test "python TestExplicitBreakpoint('increase_1')" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase_1(&i)" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+
+# return address in normal frame
+
+delete_breakpoints
+gdb_test "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase(&i)" \
+         "SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*The program being debugged stopped while in a function called from GDB.*" \
+         "FinishBP stop at during explicit function call"
+
+
+#
+# Test FinishBreakpoint when inferior exits
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}
+
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
+
+#
+# Test FinishBreakpoint when inferior execs
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}     
+
+gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
+gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ 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..adf10b0
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,88 @@
+# 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 MyFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		gdb.FinishBreakpoint.__init__ (self, 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..."
+
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__(self, spec="test_1", internal=1)
+        self.silent = True
+        self.count = 0
+        print "TestBreakpoint init"
+        
+    def stop(self):
+    	self.count += 1
+    	try:
+        	TestFinishBreakpoint(gdb.newest_frame(), self.count)
+        except ValueError as e:
+        	print e
+        return False
+
+class TestFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame, count):
+    	self.count = count
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        
+        
+    def stop(self):
+        print "-->", self.number
+        if (self.count == 3):
+            print "test stop ... %d" % self.count
+            return True
+        else:
+            print "test don't stop %d" % self.count
+            return False 
+        
+    
+    def out_of_scope(self):
+        print "test didn't finish ... %d" % self.count
+
+class TestExplicitBreakpoint(gdb.Breakpoint):
+	def stop(self):
+		try:
+			SimpleFinishBreakpoint(gdb.newest_frame())
+		except ValueError as e:
+			print e
+		return False
+
+class SimpleFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		
+		print "SimpleFinishBreakpoint init"
+		
+	def stop(self):
+		print "SimpleFinishBreakpoint stop" 
+		return True
+	
+	def out_of_scope(self):
+		print "SimpleFinishBreakpoint out of scope..."
+
+print "Python script importedd"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.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-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
new file mode 100644
index 0000000..fae798c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -0,0 +1,65 @@
+# 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-breakpoint2"
+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
+}
+
+#
+# Check FinishBreakpoints against C++ exceptions
+#
+
+gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_breakpoint "throw_exception_1"
+gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
+
+gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.py b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
new file mode 100644
index 0000000..9ea4cf1
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
@@ -0,0 +1,34 @@
+# 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 ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__(self, frame, internal=1)
+        self.silent = True;
+        print "init ExceptionFinishBreakpoint"
+        
+    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"
-- 
1.7.6.4


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

* Re: [RFC] Python Finish Breakpoints
  2011-11-04 14:25                                         ` Kevin Pouget
@ 2011-11-04 21:04                                           ` Tom Tromey
  2011-11-09 14:10                                             ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Tom Tromey @ 2011-11-04 21:04 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches, pmuldoon

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

Tom> It seems like it should be an error to try to compute the return value
Tom> when not stopped at this breakpoint.

Kevin> I'm not totally convinced ...
Kevin> what would you think about throwing an AttributeError("return_value
Kevin> not available yet") when accessing the attribute before the breakpoint
Kevin> is hit, but keep the cached value afterwards?

What I meant was that accessing the cached value any time is fine --
just that attempting to compute the value for the first time at any
point other than the breakpoint location was wrong, just because
whatever we compute then will be unrelated to what the user might want.

It is hard to be sure that the current code handles this properly.
See below.

Kevin> +  TRY_CATCH (except, RETURN_MASK_ALL)
Kevin> +    {
Kevin> +      struct value *ret =
Kevin> +          get_return_value (type_object_to_type (py_bp->function_type),
Kevin> +                            type_object_to_type (py_bp->return_type));
Kevin> +
Kevin> +      if (ret)
Kevin> +        py_bp->return_value = value_to_value_object (ret);
Kevin> +      else
Kevin> +        py_bp->return_value = Py_None;
Kevin> +    }
Kevin> +  if (except.reason < 0)
Kevin> +    gdbpy_convert_exception(except);

Missing a space.

I think you need to Py_INCREF Py_None here.

Kevin> +static PyObject *
Kevin> +bpfinishpy_get_returnvalue (PyObject *self, void *closure)
Kevin> +{
[...]
Kevin> +      for (bs = inferior_thread ()->control.stop_bpstat;
Kevin> +          bs; bs = bs->next)
Kevin> +        {
Kevin> +          struct breakpoint *bp = bs->breakpoint_at;
Kevin> +
Kevin> +          if (bp != NULL && (PyObject *) bp->py_bp_object == self)
Kevin> +            {
Kevin> +              bpfinish_stopped_at_finish_bp (self_finishbp);
Kevin> +              if (PyErr_Occurred ())
Kevin> +                return NULL;

This seems to try to do the right thing -- but is
inferior_thread()->control even valid at all points that can reach this?

What about just computing the value before calling the 'stop' method?
As long as it computes a lazy value this won't be very expensive.

Kevin> +  if (except.reason < 0)
Kevin> +    {
Kevin> +      gdbpy_convert_exception(except);

Missing space.

Kevin> +                  self_bpfinish->return_type = type_to_type_object (ret_type);
Kevin> +                  self_bpfinish->function_type =
Kevin> +                      type_to_type_object (SYMBOL_TYPE (function));

These can fail with NULL and must be handled, probably by returning.
But you can't return out of a TRY_CATCH.

Kevin> +  if (except.reason < 0
Kevin> +      || !self_bpfinish->return_type || !self_bpfinish->function_type)
Kevin> +    {
Kevin> +      /* Won't be able to compute return value.  */
Kevin> +      self_bpfinish->return_type = NULL;
Kevin> +      self_bpfinish->function_type = NULL;

Need decrefs here.

Kevin> +  BPPY_SET_REQUIRE_VALID (&self_bpfinish->py_bp);

Hm, why is this here?

Kevin> +static void
Kevin> +bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
Kevin> +{
[...]
Kevin> +  delete_breakpoint (bpfinish_obj->py_bp.bp);

I think it needs a TRY_CATCH.

Kevin> +      else if (b->pspace == current_inferior ()->pspace
Kevin> +           && (!target_has_registers
Kevin> +               || frame_find_by_id (b->frame_id) == NULL))
Kevin> +        {
Kevin> +          bpfinishpy_out_of_scope (finish_bp);
Kevin> +        }
Kevin> +    }

This too, I think.

Kevin> +static void
Kevin> +bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
Kevin> +{
Kevin> +  struct cleanup *cleanup = ensure_python_env (get_current_arch (),
Kevin> +                                               current_language);
Kevin> +
Kevin> +  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
Kevin> +                            bs == NULL ? NULL : bs->breakpoint_at);

bpfinishpy_detect_out_scope_cb still acquires the GIL (via ensure_python_env),
but should not.

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2011-11-04 21:04                                           ` Tom Tromey
@ 2011-11-09 14:10                                             ` Kevin Pouget
  2011-11-16 10:53                                               ` Kevin Pouget
  2011-11-17 17:48                                               ` Tom Tromey
  0 siblings, 2 replies; 74+ messages in thread
From: Kevin Pouget @ 2011-11-09 14:10 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, pmuldoon

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

On Fri, Nov 4, 2011 at 10:03 PM, Tom Tromey <tromey@redhat.com> wrote:
>
> >>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
>
> Tom> It seems like it should be an error to try to compute the return value
> Tom> when not stopped at this breakpoint.
>
> Kevin> I'm not totally convinced ...
> Kevin> what would you think about throwing an AttributeError("return_value
> Kevin> not available yet") when accessing the attribute before the breakpoint
> Kevin> is hit, but keep the cached value afterwards?
>
> What I meant was that accessing the cached value any time is fine --
> just that attempting to compute the value for the first time at any
> point other than the breakpoint location was wrong, just because
> whatever we compute then will be unrelated to what the user might want.
>
> It is hard to be sure that the current code handles this properly.
> See below.
...
> Kevin> +static PyObject *
> Kevin> +bpfinishpy_get_returnvalue (PyObject *self, void *closure)
> Kevin> +{
> [...]
> Kevin> +      for (bs = inferior_thread ()->control.stop_bpstat;
> Kevin> +          bs; bs = bs->next)
> Kevin> +        {
> Kevin> +          struct breakpoint *bp = bs->breakpoint_at;
> Kevin> +
> Kevin> +          if (bp != NULL && (PyObject *) bp->py_bp_object == self)
> Kevin> +            {
> Kevin> +              bpfinish_stopped_at_finish_bp (self_finishbp);
> Kevin> +              if (PyErr_Occurred ())
> Kevin> +                return NULL;
>
> This seems to try to do the right thing -- but is
> inferior_thread()->control even valid at all points that can reach this?
>
> What about just computing the value before calling the 'stop' method?
> As long as it computes a lazy value this won't be very expensive.

This part is a bit tricky, and your suggestion has highlighted a
problem I didn't consider/test:

py-breakpoint.c::gdbpy_should_stop is executed _before_ we have the
chance to save the stop_registers, used to compute the return value
(in infrun.c::normal_stop).
(the problem existed since the beginning, but I never faced it before)

I've updated the function infcmd.c::get_return_value to take this
situation into account, using the current registers if the
'stop_registers' are not set, based on what is done in
infrun.c::normal_stop:

> struct value *
> get_return_value (struct type *func_type, struct type *value_type)
> ...
>   /* If stop_registers where not saved, use the current registers.  */
>   if (!stop_regs)
>     {
>       stop_regs = regcache_dup (get_current_regcache ());
>       cleanup = make_cleanup_regcache_xfree (stop_regs);
>     }

but I can't say that I'm confident with regcache handling, and I don't
know if these lines would have unexpected side effects ...


The patch enclosed passes the testsuite with no regression on x86_64/fedora 15

> Kevin> +  TRY_CATCH (except, RETURN_MASK_ALL)
> Kevin> +    {
> Kevin> +      struct value *ret =
> Kevin> +          get_return_value (type_object_to_type (py_bp->function_type),
> Kevin> +                            type_object_to_type (py_bp->return_type));
> Kevin> +
> Kevin> +      if (ret)
> Kevin> +        py_bp->return_value = value_to_value_object (ret);
> Kevin> +      else
> Kevin> +        py_bp->return_value = Py_None;
> Kevin> +    }
> Kevin> +  if (except.reason < 0)
> Kevin> +    gdbpy_convert_exception(except);
>
> Missing a space.
>
> I think you need to Py_INCREF Py_None here.
>
>
> Kevin> +  if (except.reason < 0)
> Kevin> +    {
> Kevin> +      gdbpy_convert_exception(except);
>
> Missing space.
>
> Kevin> +  if (except.reason < 0
> Kevin> +      || !self_bpfinish->return_type || !self_bpfinish->function_type)
> Kevin> +    {
> Kevin> +      /* Won't be able to compute return value.  */
> Kevin> +      self_bpfinish->return_type = NULL;
> Kevin> +      self_bpfinish->function_type = NULL;
>
> Need decrefs here.

all fixed, thanks

> Kevin> +                  self_bpfinish->return_type = type_to_type_object (ret_type);
> Kevin> +                  self_bpfinish->function_type =
> Kevin> +                      type_to_type_object (SYMBOL_TYPE (function));
>
> These can fail with NULL and must be handled, probably by returning.
> But you can't return out of a TRY_CATCH.

I think that I handle that in the following lines:

> Kevin> +  if (except.reason < 0
> Kevin> +      || !self_bpfinish->return_type || !self_bpfinish->function_type)

I think I wrote a word about that in the previous mail, anyway, my
feeling was that I don't want to abort the FinishBreakpoint creation
just because of a return value not computable, so I currently nullify
these fields and silently disable return value computation. I can't
see any straightforward way to notify Python about that,
warning/exceptions won't suite ... otherwise, I could expose the
return_type to the Python interface, this way, one could check that
it's not None and now if GDB will/might be able to compute the return
value when the FinishBP is hit

> Kevin> +  BPPY_SET_REQUIRE_VALID (&self_bpfinish->py_bp);
>
> Hm, why is this here?

no reason apparently, the try/catch above shall ensure that the BP is valid

> Kevin> +static void
> Kevin> +bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
> Kevin> +{
> [...]
> Kevin> +  delete_breakpoint (bpfinish_obj->py_bp.bp);
>
> I think it needs a TRY_CATCH.
>
> Kevin> +      else if (b->pspace == current_inferior ()->pspace
> Kevin> +           && (!target_has_registers
> Kevin> +               || frame_find_by_id (b->frame_id) == NULL))
> Kevin> +        {
> Kevin> +          bpfinishpy_out_of_scope (finish_bp);
> Kevin> +        }
> Kevin> +    }
>
> This too, I think.

right, done

> Kevin> +static void
> Kevin> +bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
> Kevin> +{
> Kevin> +  struct cleanup *cleanup = ensure_python_env (get_current_arch (),
> Kevin> +                                               current_language);
> Kevin> +
> Kevin> +  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
> Kevin> +                            bs == NULL ? NULL : bs->breakpoint_at);
>
> bpfinishpy_detect_out_scope_cb still acquires the GIL (via ensure_python_env),
> but should not.

I'm not exactly sure what was you concern here, as far as I
understand, the GIL was acquired in bpfinishpy_handle_stop, so it
should have no effect in detect_out_scope_cb. So I've removed it from
detect_out_scope_cb as it was useless.

I've also made a litlle modification in the FinishBreakpoint __init__,
which now default it's frame argument to gdb.current_frame(), instead
of making it mandatory. I've updated the documentation and testsuite
accordingly.


Thanks for the time you spend reviewing my patches,

Kevin

--

2011-11-09  Kevin Pouget  <kevin.pouget@st.com>

	Introduce gdb.FinishBreakpoint in Python

	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-finishbreakpoint.o.
	(SUBDIR_PYTHON_SRCS): Add python/py-finishbreakpoint.c.
	Add build rule for this file.
	* infcmd.c (print_return_value): Split to create get_return_value.
	(get_return_value): New function based on print_return_value. Handle
	case where stop_registers are not set.
	* inferior.h (get_return_value): New prototype.
	* python/py-breakpoint.c (bppy_pending_object): Make non-static.
	(gdbpy_breakpoint_created): Set is_py_finish_bp is necessary.
	(struct breakpoint_object): Move to python-internal.h
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(gdbpy_breakpoint_created): Initialize is_finish_bp.
	(gdbpy_should_stop): Add  pre/post hooks before/after calling stop
	method.
	* python/python-internal.h (breakpoint_object_type): Add as extern.
	(bppy_pending_object): Likewise.
	(typedef struct breakpoint_object) Removed.
	(struct breakpoint_object): Moved from py-breakpoint.c.
	Add field is_finish_bp.
	(BPPY_REQUIRE_VALID): Moved from py-breakpoint.c.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(frame_object_to_frame_info): New prototype.
	(gdbpy_initialize_finishbreakpoints): New prototype.
	(bpfinishpy_is_finish_bp): Likewise.
	(bpfinishpy_pre_stop_hook): Likewise.
	(bpfinishpy_post_stop_hook): Likewise.
	* python/py-finishbreakpoint.c: New file.
	* python/py-frame.c(frame_object_to_frame_info): Make non-static and
	accept PyObject instead of frame_object.
	(frapy_is_valid): Don't cast to frame_object.
	(frapy_name): Likewise.
	(frapy_type): Likewise.
	(frapy_unwind_stop_reason): Likewise.
	(frapy_pc): Likewise.
	(frapy_block): Likewise.
	(frapy_function): Likewise.
	(frapy_older): Likewise.
	(frapy_newer): Likewise.
	(frapy_find_sal): Likewise.
	(frapy_read_var): Likewise.
	(frapy_select): Likewise.
	* python/python.c (gdbpy_is_stopped_at_finish_bp): New noop function.
	(_initialize_python): Add gdbpy_initialize_finishbreakpoints.
	* python/python.h: Include breakpoint.h
	(gdbpy_is_stopped_at_finish_bp): New prototype.

doc/
	* gdb.texinfo (Breakpoints In Python): New subsection: Finish
	Breakpoints.
	(Python API): Add menu entry for Finish Breakpoints

testsuite/
	* gdb.python/py-breakpoint.exp (mult_line): Define and use variable
	instead of line number.
	* gdb.python/py-finish-breakpoint.c: New file.
	* gdb.python/py-finish-breakpoint.exp: New file.
	* gdb.python/py-finish-breakpoint.py: New file.
	* gdb.python/py-finish-breakpoint2.cc: New file.
	* gdb.python/py-finish-breakpoint2.exp: New file.
	* gdb.python/py-finish-breakpoint2.py: New file.

[-- Attachment #2: 0001-Introduce-gdb.FinishBreakpoint.patch --]
[-- Type: text/x-patch, Size: 55589 bytes --]

From 82aadee4505069f28117b91e6170045cef8cccc6 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Tue, 20 Sep 2011 13:59:23 +0200
Subject: [PATCH]  Introduce gdb.FinishBreakpoint

---
 gdb/Makefile.in                                    |    6 +
 gdb/NEWS                                           |    4 +
 gdb/doc/gdb.texinfo                                |   51 +++
 gdb/infcmd.c                                       |   40 ++-
 gdb/inferior.h                                     |    3 +
 gdb/python/py-breakpoint.c                         |   48 +--
 gdb/python/py-finishbreakpoint.c                   |  447 ++++++++++++++++++++
 gdb/python/py-frame.c                              |   32 +-
 gdb/python/python-internal.h                       |   50 +++-
 gdb/python/python.c                                |    1 +
 gdb/testsuite/gdb.python/py-breakpoint.exp         |    7 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.c    |   97 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp  |  233 ++++++++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py   |   89 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc  |   59 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   65 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.py  |   33 ++
 17 files changed, 1200 insertions(+), 65 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 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
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.py

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 7a7ff9f..31cda1b 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,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 \
@@ -318,6 +319,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 \
@@ -2103,6 +2105,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/NEWS b/gdb/NEWS
index 1713049..b170b0a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -51,6 +51,10 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".
 
+  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
+     of a function.  This class is based on the "finish" command
+     available in the CLI. 
+
   ** Type objects for struct and union types now allow access to
      the fields using standard Python dictionary (mapping) methods.
      For example, "some_type['myfield']" now works, as does
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index d2bdefa..10ea760 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21404,6 +21404,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Symbol Tables In Python::     Python representation of symbol tables.
 * Lazy Strings In Python::      Python representation of lazy strings.
 * Breakpoints In Python::       Manipulating breakpoints using Python.
+* Finish Breakpoints in Python:: Setting Breakpoints on function return
+                                using Python.
 @end menu
 
 @node Basic Python
@@ -24238,6 +24240,55 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@node Finish Breakpoints in Python
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A finish breakpoint is a temporary breakpoint set at the return address of
+a frame, based on the @code{finish} command.  @code{gdb.FinishBreakpoint}
+extends @code{gdb.Breakpoint}.  The underlying breakpoint will be disabled 
+and deleted when the execution will run out of the breakpoint scope (i.e.@: 
+@code{Breakpoint.stop} or @code{FinishBreakpoint.out_of_scope} triggered).
+
+@defun FinishBreakpoint.__init__ (@r{[}frame@r{]} @r{[}, internal@r{]})
+Create a finish breakpoint at the return address of the @code{gdb.Frame}
+object @var{frame} (or @code{gdb.selected_frame()} is not provided).  The 
+optional @var{internal} argument allows the breakpoint to become invisible to
+the user.  @xref{Breakpoints In Python}, for further details about 
+this argument.
+@end defun
+
+@defun FinishBreakpoint.out_of_scope (self)
+In some circumstances (e.g.@: @code{longjmp}, C@t{++} exceptions, @value{GDBN} 
+@code{return} command, @dots{}), a function may not properly terminate, and
+thus never hit the finish breakpoint.  When @value{GDBN} notices such a
+situation, the @code{out_of_scope} callback will be triggered.
+
+You may want to sub-class @code{gdb.FinishBreakpoint} and override this
+method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+    def stop (self):
+        print "normal finish"
+        return True
+    
+    def out_of_scope ():
+        print "abnormal finish"
+@end smallexample 
+@end defun
+
+@defvar FinishBreakpoint.return_value
+When @value{GDBN} is stopped at a finish breakpoint and the frame 
+used to build the @code{gdb.FinishBreakpoint} object had debug symbols, this
+attribute will contain a @code{gdb.Value} object corresponding to the return
+value of the function.  The value will be @code{None} if the function return 
+type is @code{VOID} or if the return value was not computable.  This attribute
+is not writable.
+@end defvar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 7b935fe..89d1b1d 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1413,16 +1413,26 @@ 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.  */
+/* Return the value of the result of a function 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 regcache *stop_regs = stop_registers;
+  struct gdbarch *gdbarch;
   struct value *value;
   struct ui_out *uiout = current_uiout;
+  struct cleanup *cleanup = NULL;
+
+  /* If stop_registers where not saved, use the current registers.  */
+  if (!stop_regs)
+    {
+      stop_regs = regcache_dup (get_current_regcache ());
+      cleanup = make_cleanup_regcache_xfree (stop_regs);
+    }
+
+  gdbarch = get_regcache_arch (stop_regs);
 
   CHECK_TYPEDEF (value_type);
   gdb_assert (TYPE_CODE (value_type) != TYPE_CODE_VOID);
@@ -1441,7 +1451,7 @@ print_return_value (struct type *func_type, struct type *value_type)
     case RETURN_VALUE_ABI_RETURNS_ADDRESS:
     case RETURN_VALUE_ABI_PRESERVES_ADDRESS:
       value = allocate_value (value_type);
-      gdbarch_return_value (gdbarch, func_type, value_type, stop_registers,
+      gdbarch_return_value (gdbarch, func_type, value_type, stop_regs,
 			    value_contents_raw (value), NULL);
       break;
     case RETURN_VALUE_STRUCT_CONVENTION:
@@ -1451,6 +1461,22 @@ print_return_value (struct type *func_type, struct type *value_type)
       internal_error (__FILE__, __LINE__, _("bad switch"));
     }
 
+  if (cleanup)
+    do_cleanups (cleanup);
+
+  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;
+  struct ui_out *uiout = current_uiout;
+
   if (value)
     {
       struct value_print_options opts;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..f198696 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -269,6 +269,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/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index f235bbc..94f9cb1 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -31,52 +31,16 @@
 #include "arch-utils.h"
 #include "language.h"
 
-static 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
 {
@@ -756,6 +720,9 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
   struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
   struct cleanup *cleanup = ensure_python_env (garch, current_language);
 
+  if (bp_obj->is_finish_bp)
+    bpfinishpy_pre_stop_hook (bp_obj);
+
   if (PyObject_HasAttrString (py_bp, stop_func))
     {
       PyObject *result = PyObject_CallMethod (py_bp, stop_func, NULL);
@@ -777,6 +744,10 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
       else
 	gdbpy_print_stack ();
     }
+
+  if (bp_obj->is_finish_bp)
+    bpfinishpy_post_stop_hook (bp_obj);
+
   do_cleanups (cleanup);
 
   return stop;
@@ -839,6 +810,7 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->number = bp->number;
       newbp->bp = bp;
       newbp->bp->py_bp_object = newbp;
+      newbp->is_finish_bp = 0;
       Py_INCREF (newbp);
       ++bppy_live;
     }
@@ -1000,7 +972,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..1c3b627
--- /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.  */
+  breakpoint_object py_bp;
+  /* gdb.Type object of the value return by the breakpointed function.
+     May be NULL if no debug information was available or return type
+     was VOID.  */
+  PyObject *return_type;
+  /* gdb.Type object of the function finished by this breakpoint.  Will be
+     NULL if return_type is NULL.  */
+  PyObject *function_type;
+  /* When stopped at this FinishBreakpoint, gdb.Value object returned by
+     the function; Py_None if the value is not computable; NULL if GDB is
+     not stopped at a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* 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;
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* Deallocate FinishBreakpoint object.  */
+
+static void
+bpfinishpy_dealloc (PyObject *self)
+{
+  struct finish_breakpoint_object *self_bpfinish =
+        (struct finish_breakpoint_object *) self;
+
+  Py_XDECREF (self_bpfinish->function_type);
+  Py_XDECREF (self_bpfinish->return_type);
+  Py_XDECREF (self_bpfinish->return_value);
+}
+
+/* Triggered when gdbpy_should_stop is about to execute the `stop' callback
+   of the gdb.FinishBreakpoint object BP_OBJ.  Will compute and cache the
+   `return_value', if possible.  */
+
+void
+bpfinishpy_pre_stop_hook (struct breakpoint_object *bp_obj)
+{
+  struct finish_breakpoint_object *self_finishbp =
+        (struct finish_breakpoint_object *) bp_obj;
+  volatile struct gdb_exception except;
+
+  /* Can compute return_value only once.  */
+  gdb_assert (!self_finishbp->return_value);
+
+  if (!self_finishbp->return_type)
+    return;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      struct value *ret =
+          get_return_value (type_object_to_type (self_finishbp->function_type),
+                            type_object_to_type (self_finishbp->return_type));
+
+      if (ret)
+        self_finishbp->return_value = value_to_value_object (ret);
+      else
+        {
+          Py_INCREF (Py_None);
+          self_finishbp->return_value = Py_None;
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      gdbpy_print_stack ();
+    }
+}
+
+/* Triggered when gdbpy_should_stop has triggered the `stop' callback
+   of the gdb.FinishBreakpoint object BP_OBJ.  */
+
+void
+bpfinishpy_post_stop_hook (struct breakpoint_object *bp_obj)
+{
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Can't delete it here, but it will be removed at the next stop.  */
+      disable_breakpoint (bp_obj->bp);
+      gdb_assert (bp_obj->bp->disposition == disp_del);
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      gdbpy_print_stack ();
+    }
+}
+
+/* Python function to create a new breakpoint.  */
+
+static int
+bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+  static char *keywords[] = { "frame", "internal", NULL };
+  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 = NULL;
+  struct frame_id frame_id;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs, "|OO", keywords,
+                                    &frame_obj, &internal))
+    return -1;
+
+  /* Default to gdb.selected_frame if necessary.  */
+  if (!frame_obj)
+    frame_obj = gdbpy_selected_frame (NULL, NULL);
+  
+  frame = frame_object_to_frame_info (frame_obj);
+  if (frame == NULL)
+    goto invalid_frame;
+  
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      prev_frame = get_prev_frame (frame);
+      if (prev_frame == 0)
+        {
+          PyErr_SetString (PyExc_ValueError,
+            _("\"FinishBreakpoint\" not meaningful in the outermost frame."));
+        }
+      else if (get_frame_type (prev_frame) == DUMMY_FRAME)
+        {
+          PyErr_SetString (PyExc_ValueError,
+                   _("\"FinishBreakpoint\" cannot be set on a dummy frame."));
+        }
+      else
+        {
+          frame_id = get_frame_id (prev_frame);
+          if (frame_id_eq (frame_id, null_frame_id))
+            PyErr_SetString (PyExc_ValueError,
+                                     _("Invalid ID for the `frame' object."));
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      return -1;
+    }
+  else if (PyErr_Occurred ())
+    return -1;
+
+  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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  self_bpfinish->return_type = NULL;
+  self_bpfinish->function_type = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (get_frame_pc_if_available (frame, &pc))
+        {
+          function = find_pc_function (pc);
+          if (function != NULL)
+            {
+              struct type *ret_type =
+                  TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+              /* Remember only non-VOID return types.  */
+              if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
+                {
+                  self_bpfinish->return_type = type_to_type_object (ret_type);
+                  self_bpfinish->function_type =
+                      type_to_type_object (SYMBOL_TYPE (function));
+                }
+            }
+        }
+    }
+  if (except.reason < 0
+      || !self_bpfinish->return_type || !self_bpfinish->function_type)
+    {
+      /* Won't be able to compute return value.  */
+      Py_XDECREF (self_bpfinish->return_type);
+      Py_XDECREF (self_bpfinish->function_type);
+
+      self_bpfinish->return_type = NULL;
+      self_bpfinish->function_type = NULL;
+    }
+
+  bppy_pending_object = &self_bpfinish->py_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, -1,
+                         0,
+                         1 /*temp_flag*/,
+                         bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         &bkpt_breakpoint_ops,
+                         0, 1, internal_bp);
+    }
+  GDB_PY_SET_HANDLE_EXCEPTION (except);
+  
+  self_bpfinish->py_bp.bp->frame_id = frame_id;
+  self_bpfinish->py_bp.is_finish_bp = 1;
+  
+  return 0;
+  
+ invalid_frame:
+  PyErr_SetString (PyExc_ValueError, 
+                   _("Invalid ID for the `frame' object."));
+  return -1;
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack.  Triggers the method OUT_OF_SCOPE if implemented,
+   then delete the breakpoint.  */
+
+static void
+bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
+{
+  volatile struct gdb_exception except;
+  breakpoint_object *bp_obj = (breakpoint_object *) bpfinish_obj;
+  PyObject *py_obj = (PyObject *) bp_obj;
+
+  if (PyObject_HasAttrString (py_obj, outofscope_func))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      delete_breakpoint (bpfinish_obj->py_bp.bp);
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      gdbpy_print_stack ();
+    }
+}
+
+/* Callback for `bpfinishpy_detect_out_scope'.  Triggers Python's
+   `B->out_of_scope' function if B is a FinishBreakpoint out of its scope.  */
+
+static int
+bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
+{
+  volatile struct gdb_exception except;
+  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 ();
+  
+  /* Trigger out_of_scope if this is a FinishBreakpoint and its frame is
+     not anymore in the current callstack.  */
+  if (py_bp != NULL && b->py_bp_object->is_finish_bp)
+    {
+      struct finish_breakpoint_object *finish_bp =
+          (struct finish_breakpoint_object *) py_bp;
+
+      /* Check scope if not currently stopped at the FinishBreakpoint.  */
+      if (b != bp_stopped)
+        {
+          TRY_CATCH (except, RETURN_MASK_ALL)
+            {
+              if (b->pspace == current_inferior ()->pspace
+                 && (!target_has_registers
+                     || frame_find_by_id (b->frame_id) == NULL))
+              {
+                bpfinishpy_out_of_scope (finish_bp);
+              }
+            }
+          if (except.reason < 0)
+            {
+              gdbpy_convert_exception (except);
+              gdbpy_print_stack ();
+            }
+        }
+    }
+
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run
+   out of the scope of any FinishBreakpoint before it has been hit.  */
+
+static void
+bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
+{
+  struct cleanup *cleanup = ensure_python_env (get_current_arch (),
+                                               current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
+                            bs == NULL ? NULL : bs->breakpoint_at);
+
+  do_cleanups (cleanup);
+}
+
+/* Attached to `exit' notifications, triggers all the necessary out of
+   scope notifications.  */
+
+static void
+bpfinishpy_handle_exit (struct inferior *inf)
+{
+  struct cleanup *cleanup = ensure_python_env (target_gdbarch,
+      current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
+
+  do_cleanups (cleanup);
+}
+
+/* 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 PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "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*/
+  bpfinishpy_dealloc,             /*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 20064ca..c334f63 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);
@@ -106,7 +107,7 @@ frapy_is_valid (PyObject *self, PyObject *args)
 
   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      frame = frame_object_to_frame_info ((frame_object *) self);
+      frame = frame_object_to_frame_info (self);
     }
   GDB_PY_HANDLE_EXCEPTION (except);
 
@@ -130,7 +131,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);
     }
@@ -159,7 +160,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);
     }
@@ -180,7 +181,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);
 
@@ -201,7 +202,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);
     }
@@ -222,7 +223,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);
@@ -263,7 +264,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));
     }
@@ -330,7 +331,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)
@@ -359,7 +360,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)
@@ -388,7 +389,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);
@@ -444,7 +445,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);
@@ -472,7 +473,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);
     }
@@ -487,12 +488,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 ef39d5d..1ba7133 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -120,9 +120,50 @@ 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;
+
+  /* 1 is this is a FinishBreakpoint object, 0 otherwise.  */
+  int is_finish_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
 {
@@ -188,6 +229,7 @@ struct value *convert_value_from_python (PyObject *obj);
 struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
+struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);
 
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
@@ -202,6 +244,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);
@@ -275,6 +318,9 @@ PyObject *gdbpy_get_varobj_pretty_printer (struct value *value);
 char *gdbpy_get_display_hint (PyObject *printer);
 PyObject *gdbpy_default_visualizer (PyObject *self, PyObject *args);
 
+void bpfinishpy_pre_stop_hook (struct breakpoint_object *bp_obj);
+void bpfinishpy_post_stop_hook (struct breakpoint_object *bp_obj);
+
 extern PyObject *gdbpy_doc_cst;
 extern PyObject *gdbpy_children_cst;
 extern PyObject *gdbpy_to_string_cst;
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 108e542..155f8da 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1247,6 +1247,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.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index e0dd087..0e3adbd 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.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..7cdbead
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,97 @@
+/* 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>
+#include <stdlib.h>
+#include <unistd.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);
+}
+
+void
+test_exec_exit (int do_exit)
+{
+  if (do_exit)
+    exit (0);
+  else
+    execl ("/bin/echo", "echo", "-1", (char *)0);
+}
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  getpid ();
+
+  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.  */
+
+  test_exec_exit (1);
+
+  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..444019c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,233 @@
+# 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 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_breakpoint "increase_1"
+gdb_test "continue" "Breakpoint .*at.*" "continue to the function to finish"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp_default = gdb.FinishBreakpoint ()" \
+         "Temporary breakpoint.*" "set FinishBreakpoint with default frame value"
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval ('a'), gdb.selected_frame ())" \
+         "Temporary breakpoint.*" "set FinishBreakpoint"
+gdb_test "python print finishbp.return_value" "None.*" \
+         "check return_value at init"
+
+# check normal bp hit
+
+gdb_test "continue" "MyFinishBreakpoint stop with.*return_value is: -5.*#0.*increase.*" \
+         "check MyFinishBreakpoint hit"
+gdb_test "python print finishbp.return_value" "-5.*" "check return_value"
+
+gdb_test "python print finishbp_default.hit_count" "1.*" "check finishBP on default frame has been hit"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer normal hit"
+
+# check FinishBreakpoint in main no allowed
+
+gdb_test "finish" "main.*" "return to main()"
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         "ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \
+         "check FinishBP not allowed in main"
+
+#
+# Test FinishBreakpoint with no debug symbol 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+set cond_line [gdb_get_line_number "Condition Break."]
+
+if ![runto "getpid"] then {
+    fail "Cannot run to getpid."
+    return 0
+}
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "continue" "SimpleFinishBreakpoint stop.*" "check FinishBreakpoint hit"
+gdb_test "python print finishBP.return_value" "None" "check return value without debug symbol"
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "python finishbp = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" \
+         "check FinishBP out of scope notification"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy 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_1(i,8)" "Breakpoint .* at .*" \
+         "set a conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" \
+         "set FinishBP in a breakpoint condition"
+gdb_test "continue" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+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"
+
+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 explicit inferior function call
+#
+
+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
+}
+
+# return address in dummy frame
+
+gdb_test "python TestExplicitBreakpoint('increase_1')" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase_1(&i)" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+
+# return address in normal frame
+
+delete_breakpoints
+gdb_test "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase(&i)" \
+         "SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*The program being debugged stopped while in a function called from GDB.*" \
+         "FinishBP stop at during explicit function call"
+
+
+#
+# Test FinishBreakpoint when inferior exits
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}
+
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
+
+#
+# Test FinishBreakpoint when inferior execs
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}     
+
+gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
+gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ 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..dea2a73
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,89 @@
+# 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 MyFinishBreakpoint (gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		print "MyFinishBreakpoint init"
+		self.val = val
+		
+	def stop(self):
+		print "MyFinishBreakpoint stop with %d" % int (self.val.dereference ())
+		print "return_value is: %d" % int (self.return_value)
+		gdb.execute ("where 1")
+		return True
+	
+	def out_of_scope(self):
+		print "MyFinishBreakpoint out of scope"
+
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__ (self, spec="test_1", internal=1)
+        self.silent = True
+        self.count = 0
+        print "TestBreakpoint init"
+        
+    def stop(self):
+    	self.count += 1
+    	try:
+        	TestFinishBreakpoint (gdb.newest_frame (), self.count)
+        except ValueError as e:
+        	print e
+        return False
+
+class TestFinishBreakpoint (gdb.FinishBreakpoint):
+    def __init__ (self, frame, count):
+    	self.count = count
+        gdb.FinishBreakpoint.__init__ (self, frame, internal=1)
+        
+        
+    def stop(self):
+        print "-->", self.number
+        if (self.count == 3):
+            print "test stop: %d" % self.count
+            return True
+        else:
+            print "test don't stop: %d" % self.count
+            return False 
+        
+    
+    def out_of_scope(self):
+        print "test didn't finish: %d" % self.count
+
+class TestExplicitBreakpoint(gdb.Breakpoint):
+	def stop(self):
+		try:
+			SimpleFinishBreakpoint (gdb.newest_frame ())
+		except ValueError as e:
+			print e
+		return False
+
+class SimpleFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		
+		print "SimpleFinishBreakpoint init"
+		
+	def stop(self):
+		print "SimpleFinishBreakpoint stop" 
+		return True
+	
+	def out_of_scope(self):
+		print "SimpleFinishBreakpoint out of scope"
+
+print "Python script importedd"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.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-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
new file mode 100644
index 0000000..fae798c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -0,0 +1,65 @@
+# 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-breakpoint2"
+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
+}
+
+#
+# Check FinishBreakpoints against C++ exceptions
+#
+
+gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_breakpoint "throw_exception_1"
+gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
+
+gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.py b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
new file mode 100644
index 0000000..0fb6955
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
@@ -0,0 +1,33 @@
+# 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 ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__ (self, frame, internal=1)
+        self.silent = True;
+        print "init ExceptionFinishBreakpoint"
+        
+    def stop(self):
+        print "stopped at ExceptionFinishBreakpoint"
+        return True 
+    
+    def out_of_scope(self):
+        print "exception did not finish ..."
+
+
+print "Python script imported"
-- 
1.7.6.4


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

* Re: [RFC] Python Finish Breakpoints
  2011-11-09 14:10                                             ` Kevin Pouget
@ 2011-11-16 10:53                                               ` Kevin Pouget
  2011-11-17 17:49                                                 ` Tom Tromey
  2011-11-17 17:48                                               ` Tom Tromey
  1 sibling, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-11-16 10:53 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, pmuldoon

On Wed, Nov 9, 2011 at 3:09 PM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
>
> On Fri, Nov 4, 2011 at 10:03 PM, Tom Tromey <tromey@redhat.com> wrote:
> >
> > >>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
> >
> > Tom> It seems like it should be an error to try to compute the return value
> > Tom> when not stopped at this breakpoint.
> >
> > Kevin> I'm not totally convinced ...
> > Kevin> what would you think about throwing an AttributeError("return_value
> > Kevin> not available yet") when accessing the attribute before the breakpoint
> > Kevin> is hit, but keep the cached value afterwards?
> >
> > What I meant was that accessing the cached value any time is fine --
> > just that attempting to compute the value for the first time at any
> > point other than the breakpoint location was wrong, just because
> > whatever we compute then will be unrelated to what the user might want.
> >
> > It is hard to be sure that the current code handles this properly.
> > See below.
> ...
> > Kevin> +static PyObject *
> > Kevin> +bpfinishpy_get_returnvalue (PyObject *self, void *closure)
> > Kevin> +{
> > [...]
> > Kevin> +      for (bs = inferior_thread ()->control.stop_bpstat;
> > Kevin> +          bs; bs = bs->next)
> > Kevin> +        {
> > Kevin> +          struct breakpoint *bp = bs->breakpoint_at;
> > Kevin> +
> > Kevin> +          if (bp != NULL && (PyObject *) bp->py_bp_object == self)
> > Kevin> +            {
> > Kevin> +              bpfinish_stopped_at_finish_bp (self_finishbp);
> > Kevin> +              if (PyErr_Occurred ())
> > Kevin> +                return NULL;
> >
> > This seems to try to do the right thing -- but is
> > inferior_thread()->control even valid at all points that can reach this?
> >
> > What about just computing the value before calling the 'stop' method?
> > As long as it computes a lazy value this won't be very expensive.
>
> This part is a bit tricky, and your suggestion has highlighted a
> problem I didn't consider/test:
>
> py-breakpoint.c::gdbpy_should_stop is executed _before_ we have the
> chance to save the stop_registers, used to compute the return value
> (in infrun.c::normal_stop).
> (the problem existed since the beginning, but I never faced it before)
>
> I've updated the function infcmd.c::get_return_value to take this
> situation into account, using the current registers if the
> 'stop_registers' are not set, based on what is done in
> infrun.c::normal_stop:
>
> > struct value *
> > get_return_value (struct type *func_type, struct type *value_type)
> > ...
> >   /* If stop_registers where not saved, use the current registers.  */
> >   if (!stop_regs)
> >     {
> >       stop_regs = regcache_dup (get_current_regcache ());
> >       cleanup = make_cleanup_regcache_xfree (stop_regs);
> >     }
>
> but I can't say that I'm confident with regcache handling, and I don't
> know if these lines would have unexpected side effects ...
>
>
> The patch enclosed passes the testsuite with no regression on x86_64/fedora 15
>
> > Kevin> +  TRY_CATCH (except, RETURN_MASK_ALL)
> > Kevin> +    {
> > Kevin> +      struct value *ret =
> > Kevin> +          get_return_value (type_object_to_type (py_bp->function_type),
> > Kevin> +                            type_object_to_type (py_bp->return_type));
> > Kevin> +
> > Kevin> +      if (ret)
> > Kevin> +        py_bp->return_value = value_to_value_object (ret);
> > Kevin> +      else
> > Kevin> +        py_bp->return_value = Py_None;
> > Kevin> +    }
> > Kevin> +  if (except.reason < 0)
> > Kevin> +    gdbpy_convert_exception(except);
> >
> > Missing a space.
> >
> > I think you need to Py_INCREF Py_None here.
> >
> >
> > Kevin> +  if (except.reason < 0)
> > Kevin> +    {
> > Kevin> +      gdbpy_convert_exception(except);
> >
> > Missing space.
> >
> > Kevin> +  if (except.reason < 0
> > Kevin> +      || !self_bpfinish->return_type || !self_bpfinish->function_type)
> > Kevin> +    {
> > Kevin> +      /* Won't be able to compute return value.  */
> > Kevin> +      self_bpfinish->return_type = NULL;
> > Kevin> +      self_bpfinish->function_type = NULL;
> >
> > Need decrefs here.
>
> all fixed, thanks
>
> > Kevin> +                  self_bpfinish->return_type = type_to_type_object (ret_type);
> > Kevin> +                  self_bpfinish->function_type =
> > Kevin> +                      type_to_type_object (SYMBOL_TYPE (function));
> >
> > These can fail with NULL and must be handled, probably by returning.
> > But you can't return out of a TRY_CATCH.
>
> I think that I handle that in the following lines:
>
> > Kevin> +  if (except.reason < 0
> > Kevin> +      || !self_bpfinish->return_type || !self_bpfinish->function_type)
>
> I think I wrote a word about that in the previous mail, anyway, my
> feeling was that I don't want to abort the FinishBreakpoint creation
> just because of a return value not computable, so I currently nullify
> these fields and silently disable return value computation. I can't
> see any straightforward way to notify Python about that,
> warning/exceptions won't suite ... otherwise, I could expose the
> return_type to the Python interface, this way, one could check that
> it's not None and now if GDB will/might be able to compute the return
> value when the FinishBP is hit
>
> > Kevin> +  BPPY_SET_REQUIRE_VALID (&self_bpfinish->py_bp);
> >
> > Hm, why is this here?
>
> no reason apparently, the try/catch above shall ensure that the BP is valid
>
> > Kevin> +static void
> > Kevin> +bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
> > Kevin> +{
> > [...]
> > Kevin> +  delete_breakpoint (bpfinish_obj->py_bp.bp);
> >
> > I think it needs a TRY_CATCH.
> >
> > Kevin> +      else if (b->pspace == current_inferior ()->pspace
> > Kevin> +           && (!target_has_registers
> > Kevin> +               || frame_find_by_id (b->frame_id) == NULL))
> > Kevin> +        {
> > Kevin> +          bpfinishpy_out_of_scope (finish_bp);
> > Kevin> +        }
> > Kevin> +    }
> >
> > This too, I think.
>
> right, done
>
> > Kevin> +static void
> > Kevin> +bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
> > Kevin> +{
> > Kevin> +  struct cleanup *cleanup = ensure_python_env (get_current_arch (),
> > Kevin> +                                               current_language);
> > Kevin> +
> > Kevin> +  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
> > Kevin> +                            bs == NULL ? NULL : bs->breakpoint_at);
> >
> > bpfinishpy_detect_out_scope_cb still acquires the GIL (via ensure_python_env),
> > but should not.
>
> I'm not exactly sure what was you concern here, as far as I
> understand, the GIL was acquired in bpfinishpy_handle_stop, so it
> should have no effect in detect_out_scope_cb. So I've removed it from
> detect_out_scope_cb as it was useless.
>
> I've also made a litlle modification in the FinishBreakpoint __init__,
> which now default it's frame argument to gdb.current_frame(), instead
> of making it mandatory. I've updated the documentation and testsuite
> accordingly.
>
>
> Thanks for the time you spend reviewing my patches,
>
> Kevin
>
> --
>
> 2011-11-09  Kevin Pouget  <kevin.pouget@st.com>
>
>        Introduce gdb.FinishBreakpoint in Python
>
>        * Makefile.in (SUBDIR_PYTHON_OBS): Add py-finishbreakpoint.o.
>        (SUBDIR_PYTHON_SRCS): Add python/py-finishbreakpoint.c.
>        Add build rule for this file.
>        * infcmd.c (print_return_value): Split to create get_return_value.
>        (get_return_value): New function based on print_return_value. Handle
>        case where stop_registers are not set.
>        * inferior.h (get_return_value): New prototype.
>        * python/py-breakpoint.c (bppy_pending_object): Make non-static.
>        (gdbpy_breakpoint_created): Set is_py_finish_bp is necessary.
>        (struct breakpoint_object): Move to python-internal.h
>        (BPPY_REQUIRE_VALID): Likewise.
>        (BPPY_SET_REQUIRE_VALID): Likewise.
>        (gdbpy_breakpoint_created): Initialize is_finish_bp.
>        (gdbpy_should_stop): Add  pre/post hooks before/after calling stop
>        method.
>        * python/python-internal.h (breakpoint_object_type): Add as extern.
>        (bppy_pending_object): Likewise.
>        (typedef struct breakpoint_object) Removed.
>        (struct breakpoint_object): Moved from py-breakpoint.c.
>        Add field is_finish_bp.
>        (BPPY_REQUIRE_VALID): Moved from py-breakpoint.c.
>        (BPPY_SET_REQUIRE_VALID): Likewise.
>        (frame_object_to_frame_info): New prototype.
>        (gdbpy_initialize_finishbreakpoints): New prototype.
>        (bpfinishpy_is_finish_bp): Likewise.
>        (bpfinishpy_pre_stop_hook): Likewise.
>        (bpfinishpy_post_stop_hook): Likewise.
>        * python/py-finishbreakpoint.c: New file.
>        * python/py-frame.c(frame_object_to_frame_info): Make non-static and
>        accept PyObject instead of frame_object.
>        (frapy_is_valid): Don't cast to frame_object.
>        (frapy_name): Likewise.
>        (frapy_type): Likewise.
>        (frapy_unwind_stop_reason): Likewise.
>        (frapy_pc): Likewise.
>        (frapy_block): Likewise.
>        (frapy_function): Likewise.
>        (frapy_older): Likewise.
>        (frapy_newer): Likewise.
>        (frapy_find_sal): Likewise.
>        (frapy_read_var): Likewise.
>        (frapy_select): Likewise.
>        * python/python.c (gdbpy_is_stopped_at_finish_bp): New noop function.
>        (_initialize_python): Add gdbpy_initialize_finishbreakpoints.
>        * python/python.h: Include breakpoint.h
>        (gdbpy_is_stopped_at_finish_bp): New prototype.
>
> doc/
>        * gdb.texinfo (Breakpoints In Python): New subsection: Finish
>        Breakpoints.
>        (Python API): Add menu entry for Finish Breakpoints
>
> testsuite/
>        * gdb.python/py-breakpoint.exp (mult_line): Define and use variable
>        instead of line number.
>        * gdb.python/py-finish-breakpoint.c: New file.
>        * gdb.python/py-finish-breakpoint.exp: New file.
>        * gdb.python/py-finish-breakpoint.py: New file.
>        * gdb.python/py-finish-breakpoint2.cc: New file.
>        * gdb.python/py-finish-breakpoint2.exp: New file.
>        * gdb.python/py-finish-breakpoint2.py: New file.

ping after one week, if you have a bit of time


Thanks,

Kevin

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

* Re: [RFC] Python Finish Breakpoints
  2011-11-09 14:10                                             ` Kevin Pouget
  2011-11-16 10:53                                               ` Kevin Pouget
@ 2011-11-17 17:48                                               ` Tom Tromey
       [not found]                                                 ` <CAPftXUJwtGJhqFyfX6LVK87QH2xeLkvv5kx=yaEnYJMv0YR_rw@mail.gmail.com>
  1 sibling, 1 reply; 74+ messages in thread
From: Tom Tromey @ 2011-11-17 17:48 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches, pmuldoon

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

Kevin> py-breakpoint.c::gdbpy_should_stop is executed _before_ we have the
Kevin> chance to save the stop_registers, used to compute the return value
Kevin> (in infrun.c::normal_stop).
Kevin> (the problem existed since the beginning, but I never faced it before)

Kevin> I've updated the function infcmd.c::get_return_value to take this
Kevin> situation into account, using the current registers if the
Kevin> 'stop_registers' are not set, based on what is done in
Kevin> infrun.c::normal_stop:

This approach seems reasonable to me but I am not sure whether or not it
is really ok.  Most times I've tried to change infrun, I've been burned.

Kevin> I think that I handle that in the following lines:
Kevin> +  if (except.reason < 0
Kevin> +      || !self_bpfinish->return_type || !self_bpfinish->function_type)

The problem is that Python errors are sticky.  So, if one occurs one
must either pass it upward (having the current function fail), or clear
it somehow.  It isn't ok to ignore them, call other Python functions,
and then later check.

Kevin> I think I wrote a word about that in the previous mail, anyway, my
Kevin> feeling was that I don't want to abort the FinishBreakpoint creation
Kevin> just because of a return value not computable, so I currently nullify
Kevin> these fields and silently disable return value computation. I can't
Kevin> see any straightforward way to notify Python about that,
Kevin> warning/exceptions won't suite ... otherwise, I could expose the
Kevin> return_type to the Python interface, this way, one could check that
Kevin> it's not None and now if GDB will/might be able to compute the return
Kevin> value when the FinishBP is hit

Ok, this makes sense to me.

Tom> bpfinishpy_detect_out_scope_cb still acquires the GIL (via
Tom> ensure_python_env), but should not.

Kevin> I'm not exactly sure what was you concern here, as far as I
Kevin> understand, the GIL was acquired in bpfinishpy_handle_stop, so it
Kevin> should have no effect in detect_out_scope_cb. So I've removed it from
Kevin> detect_out_scope_cb as it was useless.

I think it is inefficient to recursively acquire the GIL.

Kevin> +@defun FinishBreakpoint.__init__ (@r{[}frame@r{]} @r{[}, internal@r{]})
Kevin> +Create a finish breakpoint at the return address of the @code{gdb.Frame}
Kevin> +object @var{frame} (or @code{gdb.selected_frame()} is not provided).  The 

I think it should read something like:

    Create a finish breakpoint at the return address of the @code{gdb.Frame}
    object @var{frame}.  If @var{frame} is not provided, this defaults to
    the newest frame.

I think it is better to default to the newest frame, because the
selected frame is more of a user-interface thing, and I think code
wanting this should explicitly use it.

Kevin> +type is @code{VOID}

I think just @code{void} is better.

Kevin> +  /* If stop_registers where not saved, use the current registers.  */

s/where/were/

Kevin> +  if (cleanup)
Kevin> +    do_cleanups (cleanup);

This is a gdb anti-pattern.  A call to make_cleanup can return NULL in
some scenarios.

It is better to install a null cleanup and then unconditionally call
do_cleanups.

Kevin> +  /* Default to gdb.selected_frame if necessary.  */
Kevin> +  if (!frame_obj)
Kevin> +    frame_obj = gdbpy_selected_frame (NULL, NULL);

gdbpy_newest_frame

I don't think there is a decref for the frame_obj along this path.

Kevin> +          PyErr_SetString (PyExc_ValueError,
Kevin> +            _("\"FinishBreakpoint\" not meaningful in the outermost frame."));
       
Indentation.

Kevin> +          PyErr_SetString (PyExc_ValueError,
Kevin> +                   _("\"FinishBreakpoint\" cannot be set on a dummy frame."));

Indentation.

Kevin> +            PyErr_SetString (PyExc_ValueError,
Kevin> +                                     _("Invalid ID for the `frame' object."));

Indentation.

Kevin> +              /* Remember only non-VOID return types.  */
Kevin> +              if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
Kevin> +                {
Kevin> +                  self_bpfinish->return_type = type_to_type_object (ret_type);
Kevin> +                  self_bpfinish->function_type =
Kevin> +                      type_to_type_object (SYMBOL_TYPE (function));

As discussed above, you need to check for errors immediately after each
call, and DTRT.  You can ignore them with PyErr_Clear.

Kevin> +static void
Kevin> +bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
Kevin> +{
[...]
Kevin> +  TRY_CATCH (except, RETURN_MASK_ALL)
Kevin> +    {
Kevin> +      delete_breakpoint (bpfinish_obj->py_bp.bp);
Kevin> +    }
Kevin> +  if (except.reason < 0)
Kevin> +    {
Kevin> +      gdbpy_convert_exception (except);
Kevin> +      gdbpy_print_stack ();
Kevin> +    }

I probably asked you to add this, but if bpfinishpy_out_of_scope can
only be called in one spot:

Kevin> +          TRY_CATCH (except, RETURN_MASK_ALL)
Kevin> +            {
Kevin> +              if (b->pspace == current_inferior ()->pspace
Kevin> +                 && (!target_has_registers
Kevin> +                     || frame_find_by_id (b->frame_id) == NULL))
Kevin> +              {
Kevin> +                bpfinishpy_out_of_scope (finish_bp);
Kevin> +              }
Kevin> +            }
Kevin> +          if (except.reason < 0)
Kevin> +            {
Kevin> +              gdbpy_convert_exception (except);
Kevin> +              gdbpy_print_stack ();
Kevin> +            }

... then the TRY_CATCH in bpfinishpy_out_of_scope is not needed.

Kevin> +  struct cleanup *cleanup = ensure_python_env (target_gdbarch,
Kevin> +      current_language);

Indentation.

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2011-11-16 10:53                                               ` Kevin Pouget
@ 2011-11-17 17:49                                                 ` Tom Tromey
  0 siblings, 0 replies; 74+ messages in thread
From: Tom Tromey @ 2011-11-17 17:49 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches, pmuldoon

Kevin> ping after one week, if you have a bit of time

BTW, thanks for the ping.

Tom

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

* Re: [RFC] Python Finish Breakpoints
       [not found]                                                 ` <CAPftXUJwtGJhqFyfX6LVK87QH2xeLkvv5kx=yaEnYJMv0YR_rw@mail.gmail.com>
@ 2011-11-24  8:27                                                   ` Kevin Pouget
  2011-11-30 16:02                                                     ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-11-24  8:27 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Thu, Nov 17, 2011 at 6:48 PM, Tom Tromey <tromey@redhat.com> wrote:
>
> >>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
>
> Kevin> py-breakpoint.c::gdbpy_should_stop is executed _before_ we have the
> Kevin> chance to save the stop_registers, used to compute the return value
> Kevin> (in infrun.c::normal_stop).
> Kevin> (the problem existed since the beginning, but I never faced it before)
>
> Kevin> I've updated the function infcmd.c::get_return_value to take this
> Kevin> situation into account, using the current registers if the
> Kevin> 'stop_registers' are not set, based on what is done in
> Kevin> infrun.c::normal_stop:
>
> This approach seems reasonable to me but I am not sure whether or not it
> is really ok.  Most times I've tried to change infrun, I've been burned.

I'm not sure what to do here,
I don't change the value of the global variable "stop_registers" so
if shouldn't affect more than my code,
but it also depends of the side effects of
> regcache_dup (get_current_regcache ())
which is now triggered 'slightly' before when it used to be ...

> Kevin> I think that I handle that in the following lines:
> Kevin> +  if (except.reason < 0
> Kevin> +      || !self_bpfinish->return_type || !self_bpfinish->function_type)
>
> The problem is that Python errors are sticky.  So, if one occurs one
> must either pass it upward (having the current function fail), or clear
> it somehow.  It isn't ok to ignore them, call other Python functions,
> and then later check.

> Kevin> +              /* Remember only non-VOID return types.  */
> Kevin> +              if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
> Kevin> +                {
> Kevin> +                  self_bpfinish->return_type = type_to_type_object (ret_type);
> Kevin> +                  self_bpfinish->function_type =
> Kevin> +                      type_to_type_object (SYMBOL_TYPE (function));
>
> As discussed above, you need to check for errors immediately after each
> call, and DTRT.  You can ignore them with PyErr_Clear.

ok, I didn't know this point, so I've rewritten this to
unconditionally clear the Py exception after these two calls, and then
test against NULL as before.

> Kevin> I think I wrote a word about that in the previous mail, anyway, my
> Kevin> feeling was that I don't want to abort the FinishBreakpoint creation
> Kevin> just because of a return value not computable, so I currently nullify
> Kevin> these fields and silently disable return value computation. I can't
> Kevin> see any straightforward way to notify Python about that,
> Kevin> warning/exceptions won't suite ... otherwise, I could expose the
> Kevin> return_type to the Python interface, this way, one could check that
> Kevin> it's not None and now if GDB will/might be able to compute the return
> Kevin> value when the FinishBP is hit
>
> Ok, this makes sense to me.
>
> Tom> bpfinishpy_detect_out_scope_cb still acquires the GIL (via
> Tom> ensure_python_env), but should not.
>
> Kevin> I'm not exactly sure what was you concern here, as far as I
> Kevin> understand, the GIL was acquired in bpfinishpy_handle_stop, so it
> Kevin> should have no effect in detect_out_scope_cb. So I've removed it from
> Kevin> detect_out_scope_cb as it was useless.
>
> I think it is inefficient to recursively acquire the GIL.

right, the doc doesn't say anything about the efficiency of GIL
acquisition, so let's
assume you're right!

> Kevin> +@defun FinishBreakpoint.__init__ (@r{[}frame@r{]} @r{[}, internal@r{]})
> Kevin> +Create a finish breakpoint at the return address of the @code{gdb.Frame}
> Kevin> +object @var{frame} (or @code{gdb.selected_frame()} is not provided).
>
> I think it should read something like:
>
>    Create a finish breakpoint at the return address of the @code{gdb.Frame}
>    object @var{frame}.  If @var{frame} is not provided, this defaults to
>    the newest frame.

ok for the sentence construction,

> I think it is better to default to the newest frame, because the
> selected frame is more of a user-interface thing, and I think code
> wanting this should explicitly use it.

My thought when I chose to use 'selected_frame' was to match the
behavior of the CLI finish command. When you type it, you finish the
'selected' frame, and not the newest one.

In my use-cases, I always have gdb.selected_frame == gdb.newest_frame,
so I don't have a strong preference. I've switch the code/doc to
newest_frame.

> Kevin> +type is @code{VOID}
>
> I think just @code{void} is better.

sure

> Kevin> +  /* If stop_registers where not saved, use the current registers.  */
>
> s/where/were/

fixed

> Kevin> +  if (cleanup)
> Kevin> +    do_cleanups (cleanup);
>
> This is a gdb anti-pattern.  A call to make_cleanup can return NULL in
> some scenarios.
>
> It is better to install a null cleanup and then unconditionally call
> do_cleanups.

ok, didn't know it either, fixed with the null cleanup

> Kevin> +  /* Default to gdb.selected_frame if necessary.  */
> Kevin> +  if (!frame_obj)
> Kevin> +    frame_obj = gdbpy_selected_frame (NULL, NULL);
>
> gdbpy_newest_frame
>
> I don't think there is a decref for the frame_obj along this path.

I've changed it to:

 if (!frame_obj)
   frame_obj = gdbpy_newest_frame (NULL, NULL);
 else
   Py_INCREF (frame_obj);

 frame = frame_object_to_frame_info (frame_obj);
 Py_DECREF (frame_obj);

which looks clearer to me than setting a flag, or testing the kwargs
for the "frame" keyword

> Kevin> +          PyErr_SetString (PyExc_ValueError,
> Kevin> +            _("\"FinishBreakpoint\" not meaningful in the outermost frame."));
>
> Kevin> +          PyErr_SetString (PyExc_ValueError,
> Kevin> +                   _("\"FinishBreakpoint\" cannot be set on a dummy frame."));
>
> Indentation.
>
> Kevin> +            PyErr_SetString (PyExc_ValueError,
> Kevin> +                                     _("Invalid ID for the `frame' object."));
>
> Indentation.

These 3 lines where right-justified to column 79, I've split them.

> Kevin> +static void
> Kevin> +bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
> Kevin> +{
> [...]
> Kevin> +  TRY_CATCH (except, RETURN_MASK_ALL)
> Kevin> +    {
> Kevin> +      delete_breakpoint (bpfinish_obj->py_bp.bp);
> Kevin> +    }
> Kevin> +  if (except.reason < 0)
> Kevin> +    {
> Kevin> +      gdbpy_convert_exception (except);
> Kevin> +      gdbpy_print_stack ();
> Kevin> +    }
>
> I probably asked you to add this, but if bpfinishpy_out_of_scope can
> only be called in one spot:
>
> Kevin> +          TRY_CATCH (except, RETURN_MASK_ALL)
> Kevin> +            {
> Kevin> +              if (b->pspace == current_inferior ()->pspace
> Kevin> +                 && (!target_has_registers
> Kevin> +                     || frame_find_by_id (b->frame_id) == NULL))
> Kevin> +              {
> Kevin> +                bpfinishpy_out_of_scope (finish_bp);
> Kevin> +              }
> Kevin> +            }
> Kevin> +          if (except.reason < 0)
> Kevin> +            {
> Kevin> +              gdbpy_convert_exception (except);
> Kevin> +              gdbpy_print_stack ();
> Kevin> +            }
>
> ... then the TRY_CATCH in bpfinishpy_out_of_scope is not needed.

right

> Kevin> +  struct cleanup *cleanup = ensure_python_env (target_gdbarch,
> Kevin> +      current_language);
>
> Indentation.

fixed


There is no regression against the current tree (2011-11-18)


Thanks,

Kevin

2011-11-18  Kevin Pouget  <kevin.pouget@st.com>

       Introduce gdb.FinishBreakpoint in Python

       * Makefile.in (SUBDIR_PYTHON_OBS): Add py-finishbreakpoint.o.
       (SUBDIR_PYTHON_SRCS): Add python/py-finishbreakpoint.c.
       Add build rule for this file.
       * infcmd.c (print_return_value): Split to create get_return_value.
       (get_return_value): New function based on print_return_value. Handle
       case where stop_registers are not set.
       * inferior.h (get_return_value): New prototype.
       * python/py-breakpoint.c (bppy_pending_object): Make non-static.
       (gdbpy_breakpoint_created): Set is_py_finish_bp is necessary.
       (struct breakpoint_object): Move to python-internal.h
       (BPPY_REQUIRE_VALID): Likewise.
       (BPPY_SET_REQUIRE_VALID): Likewise.
       (gdbpy_breakpoint_created): Initialize is_finish_bp.
       (gdbpy_should_stop): Add  pre/post hooks before/after calling stop
       method.
       * python/python-internal.h (breakpoint_object_type): Add as extern.
       (bppy_pending_object): Likewise.
       (typedef struct breakpoint_object) Removed.
       (struct breakpoint_object): Moved from py-breakpoint.c.
       Add field is_finish_bp.
       (BPPY_REQUIRE_VALID): Moved from py-breakpoint.c.
       (BPPY_SET_REQUIRE_VALID): Likewise.
       (frame_object_to_frame_info): New prototype.
       (gdbpy_initialize_finishbreakpoints): New prototype.
       (bpfinishpy_is_finish_bp): Likewise.
       (bpfinishpy_pre_stop_hook): Likewise.
       (bpfinishpy_post_stop_hook): Likewise.
       * python/py-finishbreakpoint.c: New file.
       * python/py-frame.c(frame_object_to_frame_info): Make non-static and
       accept PyObject instead of frame_object.
       (frapy_is_valid): Don't cast to frame_object.
       (frapy_name): Likewise.
       (frapy_type): Likewise.
       (frapy_unwind_stop_reason): Likewise.
       (frapy_pc): Likewise.
       (frapy_block): Likewise.
       (frapy_function): Likewise.
       (frapy_older): Likewise.
       (frapy_newer): Likewise.
       (frapy_find_sal): Likewise.
       (frapy_read_var): Likewise.
       (frapy_select): Likewise.
       * python/python.c (gdbpy_is_stopped_at_finish_bp): New noop function.
       (_initialize_python): Add gdbpy_initialize_finishbreakpoints.
       * python/python.h: Include breakpoint.h
       (gdbpy_is_stopped_at_finish_bp): New prototype.

doc/
       * gdb.texinfo (Finish Breakpoints in Python): New subsection.
       (Python API): Add menu entry for Finish Breakpoints.

testsuite/
       * gdb.python/py-breakpoint.exp (mult_line): Define and use variable
       instead of line number.
       * gdb.python/py-finish-breakpoint.c: New file.
       * gdb.python/py-finish-breakpoint.exp: New file.
       * gdb.python/py-finish-breakpoint.py: New file.
       * gdb.python/py-finish-breakpoint2.cc: New file.
       * gdb.python/py-finish-breakpoint2.exp: New file.
       * gdb.python/py-finish-breakpoint2.py: New file.

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

* Re: [RFC] Python Finish Breakpoints
  2011-11-24  8:27                                                   ` Kevin Pouget
@ 2011-11-30 16:02                                                     ` Kevin Pouget
  2011-12-02 21:49                                                       ` Tom Tromey
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-11-30 16:02 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

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

On Thu, Nov 24, 2011 at 9:26 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
>
> On Thu, Nov 17, 2011 at 6:48 PM, Tom Tromey <tromey@redhat.com> wrote:
> >
> > >>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
> >
> > Kevin> py-breakpoint.c::gdbpy_should_stop is executed _before_ we have the
> > Kevin> chance to save the stop_registers, used to compute the return value
> > Kevin> (in infrun.c::normal_stop).
> > Kevin> (the problem existed since the beginning, but I never faced it before)
> >
> > Kevin> I've updated the function infcmd.c::get_return_value to take this
> > Kevin> situation into account, using the current registers if the
> > Kevin> 'stop_registers' are not set, based on what is done in
> > Kevin> infrun.c::normal_stop:
> >
> > This approach seems reasonable to me but I am not sure whether or not it
> > is really ok.  Most times I've tried to change infrun, I've been burned.
>
> I'm not sure what to do here,
> I don't change the value of the global variable "stop_registers" so
> if shouldn't affect more than my code,
> but it also depends of the side effects of
> > regcache_dup (get_current_regcache ())
> which is now triggered 'slightly' before when it used to be ...
>
> > Kevin> I think that I handle that in the following lines:
> > Kevin> +  if (except.reason < 0
> > Kevin> +      || !self_bpfinish->return_type || !self_bpfinish->function_type)
> >
> > The problem is that Python errors are sticky.  So, if one occurs one
> > must either pass it upward (having the current function fail), or clear
> > it somehow.  It isn't ok to ignore them, call other Python functions,
> > and then later check.
>
> > Kevin> +              /* Remember only non-VOID return types.  */
> > Kevin> +              if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
> > Kevin> +                {
> > Kevin> +                  self_bpfinish->return_type = type_to_type_object (ret_type);
> > Kevin> +                  self_bpfinish->function_type =
> > Kevin> +                      type_to_type_object (SYMBOL_TYPE (function));
> >
> > As discussed above, you need to check for errors immediately after each
> > call, and DTRT.  You can ignore them with PyErr_Clear.
>
> ok, I didn't know this point, so I've rewritten this to
> unconditionally clear the Py exception after these two calls, and then
> test against NULL as before.
>
> > Kevin> I think I wrote a word about that in the previous mail, anyway, my
> > Kevin> feeling was that I don't want to abort the FinishBreakpoint creation
> > Kevin> just because of a return value not computable, so I currently nullify
> > Kevin> these fields and silently disable return value computation. I can't
> > Kevin> see any straightforward way to notify Python about that,
> > Kevin> warning/exceptions won't suite ... otherwise, I could expose the
> > Kevin> return_type to the Python interface, this way, one could check that
> > Kevin> it's not None and now if GDB will/might be able to compute the return
> > Kevin> value when the FinishBP is hit
> >
> > Ok, this makes sense to me.
> >
> > Tom> bpfinishpy_detect_out_scope_cb still acquires the GIL (via
> > Tom> ensure_python_env), but should not.
> >
> > Kevin> I'm not exactly sure what was you concern here, as far as I
> > Kevin> understand, the GIL was acquired in bpfinishpy_handle_stop, so it
> > Kevin> should have no effect in detect_out_scope_cb. So I've removed it from
> > Kevin> detect_out_scope_cb as it was useless.
> >
> > I think it is inefficient to recursively acquire the GIL.
>
> right, the doc doesn't say anything about the efficiency of GIL
> acquisition, so let's
> assume you're right!
>
> > Kevin> +@defun FinishBreakpoint.__init__ (@r{[}frame@r{]} @r{[}, internal@r{]})
> > Kevin> +Create a finish breakpoint at the return address of the @code{gdb.Frame}
> > Kevin> +object @var{frame} (or @code{gdb.selected_frame()} is not provided).
> >
> > I think it should read something like:
> >
> >    Create a finish breakpoint at the return address of the @code{gdb.Frame}
> >    object @var{frame}.  If @var{frame} is not provided, this defaults to
> >    the newest frame.
>
> ok for the sentence construction,
>
> > I think it is better to default to the newest frame, because the
> > selected frame is more of a user-interface thing, and I think code
> > wanting this should explicitly use it.
>
> My thought when I chose to use 'selected_frame' was to match the
> behavior of the CLI finish command. When you type it, you finish the
> 'selected' frame, and not the newest one.
>
> In my use-cases, I always have gdb.selected_frame == gdb.newest_frame,
> so I don't have a strong preference. I've switch the code/doc to
> newest_frame.
>
> > Kevin> +type is @code{VOID}
> >
> > I think just @code{void} is better.
>
> sure
>
> > Kevin> +  /* If stop_registers where not saved, use the current registers.  */
> >
> > s/where/were/
>
> fixed
>
> > Kevin> +  if (cleanup)
> > Kevin> +    do_cleanups (cleanup);
> >
> > This is a gdb anti-pattern.  A call to make_cleanup can return NULL in
> > some scenarios.
> >
> > It is better to install a null cleanup and then unconditionally call
> > do_cleanups.
>
> ok, didn't know it either, fixed with the null cleanup
>
> > Kevin> +  /* Default to gdb.selected_frame if necessary.  */
> > Kevin> +  if (!frame_obj)
> > Kevin> +    frame_obj = gdbpy_selected_frame (NULL, NULL);
> >
> > gdbpy_newest_frame
> >
> > I don't think there is a decref for the frame_obj along this path.
>
> I've changed it to:
>
>  if (!frame_obj)
>    frame_obj = gdbpy_newest_frame (NULL, NULL);
>  else
>    Py_INCREF (frame_obj);
>
>  frame = frame_object_to_frame_info (frame_obj);
>  Py_DECREF (frame_obj);
>
> which looks clearer to me than setting a flag, or testing the kwargs
> for the "frame" keyword
>
> > Kevin> +          PyErr_SetString (PyExc_ValueError,
> > Kevin> +            _("\"FinishBreakpoint\" not meaningful in the outermost frame."));
> >
> > Kevin> +          PyErr_SetString (PyExc_ValueError,
> > Kevin> +                   _("\"FinishBreakpoint\" cannot be set on a dummy frame."));
> >
> > Indentation.
> >
> > Kevin> +            PyErr_SetString (PyExc_ValueError,
> > Kevin> +                                     _("Invalid ID for the `frame' object."));
> >
> > Indentation.
>
> These 3 lines where right-justified to column 79, I've split them.
>
> > Kevin> +static void
> > Kevin> +bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
> > Kevin> +{
> > [...]
> > Kevin> +  TRY_CATCH (except, RETURN_MASK_ALL)
> > Kevin> +    {
> > Kevin> +      delete_breakpoint (bpfinish_obj->py_bp.bp);
> > Kevin> +    }
> > Kevin> +  if (except.reason < 0)
> > Kevin> +    {
> > Kevin> +      gdbpy_convert_exception (except);
> > Kevin> +      gdbpy_print_stack ();
> > Kevin> +    }
> >
> > I probably asked you to add this, but if bpfinishpy_out_of_scope can
> > only be called in one spot:
> >
> > Kevin> +          TRY_CATCH (except, RETURN_MASK_ALL)
> > Kevin> +            {
> > Kevin> +              if (b->pspace == current_inferior ()->pspace
> > Kevin> +                 && (!target_has_registers
> > Kevin> +                     || frame_find_by_id (b->frame_id) == NULL))
> > Kevin> +              {
> > Kevin> +                bpfinishpy_out_of_scope (finish_bp);
> > Kevin> +              }
> > Kevin> +            }
> > Kevin> +          if (except.reason < 0)
> > Kevin> +            {
> > Kevin> +              gdbpy_convert_exception (except);
> > Kevin> +              gdbpy_print_stack ();
> > Kevin> +            }
> >
> > ... then the TRY_CATCH in bpfinishpy_out_of_scope is not needed.
>
> right
>
> > Kevin> +  struct cleanup *cleanup = ensure_python_env (target_gdbarch,
> > Kevin> +      current_language);
> >
> > Indentation.
>
> fixed
>
>
> There is no regression against the current tree (2011-11-18)
>
>
> Thanks,
>
> Kevin

Hello,

I noticed a bug in the previous version of the patch, which led to
`out_of_scope' callback being triggered when it was not supposed to be
(namely, when the breakpoint was hit, the execution continued, and
then the callback was triggered before the temporary breakpoint could
be deleted.

I fixed it by ensuring that the breakpoint is active before triggering
the notification:
> if (bpfinish_obj->py_bp.bp->enable_state == bp_enabled
>       && PyObject_HasAttrString (py_obj, outofscope_func))
>   ...


Cordially,

Kevin

--

2011-11-30  Kevin Pouget  <kevin.pouget@st.com>

	Introduce gdb.FinishBreakpoint in Python

	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-finishbreakpoint.o.
	(SUBDIR_PYTHON_SRCS): Add python/py-finishbreakpoint.c.
	Add build rule for this file.
	* infcmd.c (print_return_value): Split to create get_return_value.
	(get_return_value): New function based on print_return_value. Handle
	case where stop_registers are not set.
	* inferior.h (get_return_value): New prototype.
	* python/py-breakpoint.c (bppy_pending_object): Make non-static.
	(gdbpy_breakpoint_created): Set is_py_finish_bp is necessary.
	(struct breakpoint_object): Move to python-internal.h
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(gdbpy_breakpoint_created): Initialize is_finish_bp.
	(gdbpy_should_stop): Add  pre/post hooks before/after calling stop
	method.
	* python/python-internal.h (breakpoint_object_type): Add as extern.
	(bppy_pending_object): Likewise.
	(typedef struct breakpoint_object) Removed.
	(struct breakpoint_object): Moved from py-breakpoint.c.
	Add field is_finish_bp.
	(BPPY_REQUIRE_VALID): Moved from py-breakpoint.c.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(frame_object_to_frame_info): New prototype.
	(gdbpy_initialize_finishbreakpoints): New prototype.
	(bpfinishpy_is_finish_bp): Likewise.
	(bpfinishpy_pre_stop_hook): Likewise.
	(bpfinishpy_post_stop_hook): Likewise.
	* python/py-finishbreakpoint.c: New file.
	* python/py-frame.c(frame_object_to_frame_info): Make non-static and
	accept PyObject instead of frame_object.
	(frapy_is_valid): Don't cast to frame_object.
	(frapy_name): Likewise.
	(frapy_type): Likewise.
	(frapy_unwind_stop_reason): Likewise.
	(frapy_pc): Likewise.
	(frapy_block): Likewise.
	(frapy_function): Likewise.
	(frapy_older): Likewise.
	(frapy_newer): Likewise.
	(frapy_find_sal): Likewise.
	(frapy_read_var): Likewise.
	(frapy_select): Likewise.
	* python/python.c (gdbpy_is_stopped_at_finish_bp): New noop function.
	(_initialize_python): Add gdbpy_initialize_finishbreakpoints.
	* python/python.h: Include breakpoint.h
	(gdbpy_is_stopped_at_finish_bp): New prototype.

doc/
	* gdb.texinfo (Finish Breakpoints in Python): New subsection.
	(Python API): Add menu entry for Finish Breakpoints.

testsuite/
	* gdb.python/py-breakpoint.exp (mult_line): Define and use variable
	instead of line number.
	* gdb.python/py-finish-breakpoint.c: New file.
	* gdb.python/py-finish-breakpoint.exp: New file.
	* gdb.python/py-finish-breakpoint.py: New file.
	* gdb.python/py-finish-breakpoint2.cc: New file.
	* gdb.python/py-finish-breakpoint2.exp: New file.
	* gdb.python/py-finish-breakpoint2.py: New file.

[-- Attachment #2: 0001-Introduce-gdb.FinishBreakpoint.patch --]
[-- Type: text/x-patch, Size: 55855 bytes --]

From 0e18c4912b0263d8db1ea545e35e9a63744c765d Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Tue, 20 Sep 2011 13:59:23 +0200
Subject: [PATCH]  Introduce gdb.FinishBreakpoint

---
 gdb/Makefile.in                                    |    6 +
 gdb/NEWS                                           |    4 +
 gdb/doc/gdb.texinfo                                |   51 +++
 gdb/infcmd.c                                       |   39 ++-
 gdb/inferior.h                                     |    3 +
 gdb/python/py-breakpoint.c                         |   48 +--
 gdb/python/py-finishbreakpoint.c                   |  448 ++++++++++++++++++++
 gdb/python/py-frame.c                              |   32 +-
 gdb/python/python-internal.h                       |   50 ++-
 gdb/python/python.c                                |    1 +
 gdb/testsuite/gdb.python/py-breakpoint.exp         |    7 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.c    |   97 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp  |  233 ++++++++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py   |   89 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc  |   59 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   65 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.py  |   33 ++
 17 files changed, 1200 insertions(+), 65 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 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
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.py

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 48221f2..724f25d 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,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 \
@@ -318,6 +319,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 \
@@ -2108,6 +2110,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/NEWS b/gdb/NEWS
index f13caf8..7b3f7f2 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -51,6 +51,10 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".
 
+  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
+     of a function.  This class is based on the "finish" command
+     available in the CLI. 
+
   ** Type objects for struct and union types now allow access to
      the fields using standard Python dictionary (mapping) methods.
      For example, "some_type['myfield']" now works, as does
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 3321a77..eebf048 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21502,6 +21502,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Symbol Tables In Python::     Python representation of symbol tables.
 * Lazy Strings In Python::      Python representation of lazy strings.
 * Breakpoints In Python::       Manipulating breakpoints using Python.
+* Finish Breakpoints in Python:: Setting Breakpoints on function return
+                                using Python.
 @end menu
 
 @node Basic Python
@@ -24336,6 +24338,55 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@node Finish Breakpoints in Python
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A finish breakpoint is a temporary breakpoint set at the return address of
+a frame, based on the @code{finish} command.  @code{gdb.FinishBreakpoint}
+extends @code{gdb.Breakpoint}.  The underlying breakpoint will be disabled 
+and deleted when the execution will run out of the breakpoint scope (i.e.@: 
+@code{Breakpoint.stop} or @code{FinishBreakpoint.out_of_scope} triggered).
+
+@defun FinishBreakpoint.__init__ (@r{[}frame@r{]} @r{[}, internal@r{]})
+Create a finish breakpoint at the return address of the @code{gdb.Frame}
+object @var{frame}.  If @var{frame} is not provided, this defaults to the
+newest frame.  The optional @var{internal} argument allows the breakpoint to
+become invisible to the user.  @xref{Breakpoints In Python}, for further 
+details about this argument.
+@end defun
+
+@defun FinishBreakpoint.out_of_scope (self)
+In some circumstances (e.g.@: @code{longjmp}, C@t{++} exceptions, @value{GDBN} 
+@code{return} command, @dots{}), a function may not properly terminate, and
+thus never hit the finish breakpoint.  When @value{GDBN} notices such a
+situation, the @code{out_of_scope} callback will be triggered.
+
+You may want to sub-class @code{gdb.FinishBreakpoint} and override this
+method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+    def stop (self):
+        print "normal finish"
+        return True
+    
+    def out_of_scope ():
+        print "abnormal finish"
+@end smallexample 
+@end defun
+
+@defvar FinishBreakpoint.return_value
+When @value{GDBN} is stopped at a finish breakpoint and the frame 
+used to build the @code{gdb.FinishBreakpoint} object had debug symbols, this
+attribute will contain a @code{gdb.Value} object corresponding to the return
+value of the function.  The value will be @code{None} if the function return 
+type is @code{void} or if the return value was not computable.  This attribute
+is not writable.
+@end defvar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 7b935fe..76addf0 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1413,16 +1413,26 @@ 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.  */
+/* Return the value of the result of a function 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 regcache *stop_regs = stop_registers;
+  struct gdbarch *gdbarch;
   struct value *value;
   struct ui_out *uiout = current_uiout;
+  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
+
+  /* If stop_registers were not saved, use the current registers.  */
+  if (!stop_regs)
+    {
+      stop_regs = regcache_dup (get_current_regcache ());
+      cleanup = make_cleanup_regcache_xfree (stop_regs);
+    }
+
+  gdbarch = get_regcache_arch (stop_regs);
 
   CHECK_TYPEDEF (value_type);
   gdb_assert (TYPE_CODE (value_type) != TYPE_CODE_VOID);
@@ -1441,7 +1451,7 @@ print_return_value (struct type *func_type, struct type *value_type)
     case RETURN_VALUE_ABI_RETURNS_ADDRESS:
     case RETURN_VALUE_ABI_PRESERVES_ADDRESS:
       value = allocate_value (value_type);
-      gdbarch_return_value (gdbarch, func_type, value_type, stop_registers,
+      gdbarch_return_value (gdbarch, func_type, value_type, stop_regs,
 			    value_contents_raw (value), NULL);
       break;
     case RETURN_VALUE_STRUCT_CONVENTION:
@@ -1451,6 +1461,21 @@ print_return_value (struct type *func_type, struct type *value_type)
       internal_error (__FILE__, __LINE__, _("bad switch"));
     }
 
+  do_cleanups (cleanup);
+
+  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;
+  struct ui_out *uiout = current_uiout;
+
   if (value)
     {
       struct value_print_options opts;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..f198696 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -269,6 +269,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/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 11d60fe..64ef45f 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -31,52 +31,16 @@
 #include "arch-utils.h"
 #include "language.h"
 
-static 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
 {
@@ -762,6 +726,9 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
   struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
   struct cleanup *cleanup = ensure_python_env (garch, current_language);
 
+  if (bp_obj->is_finish_bp)
+    bpfinishpy_pre_stop_hook (bp_obj);
+
   if (PyObject_HasAttrString (py_bp, stop_func))
     {
       PyObject *result = PyObject_CallMethod (py_bp, stop_func, NULL);
@@ -783,6 +750,10 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
       else
 	gdbpy_print_stack ();
     }
+
+  if (bp_obj->is_finish_bp)
+    bpfinishpy_post_stop_hook (bp_obj);
+
   do_cleanups (cleanup);
 
   return stop;
@@ -845,6 +816,7 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->number = bp->number;
       newbp->bp = bp;
       newbp->bp->py_bp_object = newbp;
+      newbp->is_finish_bp = 0;
       Py_INCREF (newbp);
       ++bppy_live;
     }
@@ -1006,7 +978,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..5dc3532
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,448 @@
+/* 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.  */
+  breakpoint_object py_bp;
+  /* gdb.Type object of the value return by the breakpointed function.
+     May be NULL if no debug information was available or return type
+     was VOID.  */
+  PyObject *return_type;
+  /* gdb.Type object of the function finished by this breakpoint.  Will be
+     NULL if return_type is NULL.  */
+  PyObject *function_type;
+  /* When stopped at this FinishBreakpoint, gdb.Value object returned by
+     the function; Py_None if the value is not computable; NULL if GDB is
+     not stopped at a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* 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;
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* Deallocate FinishBreakpoint object.  */
+
+static void
+bpfinishpy_dealloc (PyObject *self)
+{
+  struct finish_breakpoint_object *self_bpfinish =
+        (struct finish_breakpoint_object *) self;
+
+  Py_XDECREF (self_bpfinish->function_type);
+  Py_XDECREF (self_bpfinish->return_type);
+  Py_XDECREF (self_bpfinish->return_value);
+}
+
+/* Triggered when gdbpy_should_stop is about to execute the `stop' callback
+   of the gdb.FinishBreakpoint object BP_OBJ.  Will compute and cache the
+   `return_value', if possible.  */
+
+void
+bpfinishpy_pre_stop_hook (struct breakpoint_object *bp_obj)
+{
+  struct finish_breakpoint_object *self_finishbp =
+        (struct finish_breakpoint_object *) bp_obj;
+  volatile struct gdb_exception except;
+
+  /* Can compute return_value only once.  */
+  gdb_assert (!self_finishbp->return_value);
+
+  if (!self_finishbp->return_type)
+    return;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      struct value *ret =
+          get_return_value (type_object_to_type (self_finishbp->function_type),
+                            type_object_to_type (self_finishbp->return_type));
+
+      if (ret)
+        self_finishbp->return_value = value_to_value_object (ret);
+      else
+        {
+          Py_INCREF (Py_None);
+          self_finishbp->return_value = Py_None;
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      gdbpy_print_stack ();
+    }
+}
+
+/* Triggered when gdbpy_should_stop has triggered the `stop' callback
+   of the gdb.FinishBreakpoint object BP_OBJ.  */
+
+void
+bpfinishpy_post_stop_hook (struct breakpoint_object *bp_obj)
+{
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Can't delete it here, but it will be removed at the next stop.  */
+      disable_breakpoint (bp_obj->bp);
+      gdb_assert (bp_obj->bp->disposition == disp_del);
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      gdbpy_print_stack ();
+    }
+}
+
+/* Python function to create a new breakpoint.  */
+
+static int
+bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+  static char *keywords[] = { "frame", "internal", NULL };
+  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 = NULL;
+  struct frame_id frame_id;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs, "|OO", keywords,
+                                    &frame_obj, &internal))
+    return -1;
+
+  /* Default to gdb.newest_frame if necessary.  */
+  if (!frame_obj)
+    frame_obj = gdbpy_newest_frame (NULL, NULL);
+  else
+    Py_INCREF (frame_obj);
+
+  frame = frame_object_to_frame_info (frame_obj);
+  Py_DECREF (frame_obj);
+
+  if (frame == NULL)
+    goto invalid_frame;
+  
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      prev_frame = get_prev_frame (frame);
+      if (prev_frame == 0)
+        {
+          PyErr_SetString (PyExc_ValueError, _("\"FinishBreakpoint\" not "   \
+                                               "meaningful in the outermost "\
+                                               "frame."));
+        }
+      else if (get_frame_type (prev_frame) == DUMMY_FRAME)
+        {
+          PyErr_SetString (PyExc_ValueError, _("\"FinishBreakpoint\" cannot "\
+                                               "be set on a dummy frame."));
+        }
+      else
+        {
+          frame_id = get_frame_id (prev_frame);
+          if (frame_id_eq (frame_id, null_frame_id))
+            PyErr_SetString (PyExc_ValueError,
+                             _("Invalid ID for the `frame' object."));
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      return -1;
+    }
+  else if (PyErr_Occurred ())
+    return -1;
+
+  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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  self_bpfinish->return_type = NULL;
+  self_bpfinish->function_type = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (get_frame_pc_if_available (frame, &pc))
+        {
+          function = find_pc_function (pc);
+          if (function != NULL)
+            {
+              struct type *ret_type =
+                  TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+              /* Remember only non-void return types.  */
+              if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
+                {
+                  /* Ignore Python errors at this stage.  */
+                  self_bpfinish->return_type = type_to_type_object (ret_type);
+                  PyErr_Clear ();
+                  self_bpfinish->function_type =
+                      type_to_type_object (SYMBOL_TYPE (function));
+                  PyErr_Clear ();
+                }
+            }
+        }
+    }
+  if (except.reason < 0
+      || !self_bpfinish->return_type || !self_bpfinish->function_type)
+    {
+      /* Won't be able to compute return value.  */
+      Py_XDECREF (self_bpfinish->return_type);
+      Py_XDECREF (self_bpfinish->function_type);
+
+      self_bpfinish->return_type = NULL;
+      self_bpfinish->function_type = NULL;
+    }
+
+  bppy_pending_object = &self_bpfinish->py_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, -1,
+                         0,
+                         1 /*temp_flag*/,
+                         bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         &bkpt_breakpoint_ops,
+                         0, 1, internal_bp);
+    }
+  GDB_PY_SET_HANDLE_EXCEPTION (except);
+  
+  self_bpfinish->py_bp.bp->frame_id = frame_id;
+  self_bpfinish->py_bp.is_finish_bp = 1;
+  
+  return 0;
+  
+ invalid_frame:
+  PyErr_SetString (PyExc_ValueError, 
+                   _("Invalid ID for the `frame' object."));
+  return -1;
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack.  Triggers the method OUT_OF_SCOPE if implemented,
+   then delete the breakpoint.  */
+
+static void
+bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
+{
+  volatile struct gdb_exception except;
+  breakpoint_object *bp_obj = (breakpoint_object *) bpfinish_obj;
+  PyObject *py_obj = (PyObject *) bp_obj;
+
+  if (bpfinish_obj->py_bp.bp->enable_state == bp_enabled
+      && PyObject_HasAttrString (py_obj, outofscope_func))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+
+  delete_breakpoint (bpfinish_obj->py_bp.bp);
+}
+
+/* Callback for `bpfinishpy_detect_out_scope'.  Triggers Python's
+   `B->out_of_scope' function if B is a FinishBreakpoint out of its scope.  */
+
+static int
+bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
+{
+  volatile struct gdb_exception except;
+  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 ();
+  
+  /* Trigger out_of_scope if this is a FinishBreakpoint and its frame is
+     not anymore in the current callstack.  */
+  if (py_bp != NULL && b->py_bp_object->is_finish_bp)
+    {
+      struct finish_breakpoint_object *finish_bp =
+          (struct finish_breakpoint_object *) py_bp;
+
+      /* Check scope if not currently stopped at the FinishBreakpoint.  */
+      if (b != bp_stopped)
+        {
+          TRY_CATCH (except, RETURN_MASK_ALL)
+            {
+              if (b->pspace == current_inferior ()->pspace
+                 && (!target_has_registers
+                     || frame_find_by_id (b->frame_id) == NULL))
+              {
+                bpfinishpy_out_of_scope (finish_bp);
+              }
+            }
+          if (except.reason < 0)
+            {
+              gdbpy_convert_exception (except);
+              gdbpy_print_stack ();
+            }
+        }
+    }
+
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run
+   out of the scope of any FinishBreakpoint before it has been hit.  */
+
+static void
+bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
+{
+  struct cleanup *cleanup = ensure_python_env (get_current_arch (),
+                                               current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
+                            bs == NULL ? NULL : bs->breakpoint_at);
+
+  do_cleanups (cleanup);
+}
+
+/* Attached to `exit' notifications, triggers all the necessary out of
+   scope notifications.  */
+
+static void
+bpfinishpy_handle_exit (struct inferior *inf)
+{
+  struct cleanup *cleanup = ensure_python_env (target_gdbarch,
+                                               current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
+
+  do_cleanups (cleanup);
+}
+
+/* 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 PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "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*/
+  bpfinishpy_dealloc,             /*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 20064ca..c334f63 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);
@@ -106,7 +107,7 @@ frapy_is_valid (PyObject *self, PyObject *args)
 
   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      frame = frame_object_to_frame_info ((frame_object *) self);
+      frame = frame_object_to_frame_info (self);
     }
   GDB_PY_HANDLE_EXCEPTION (except);
 
@@ -130,7 +131,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);
     }
@@ -159,7 +160,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);
     }
@@ -180,7 +181,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);
 
@@ -201,7 +202,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);
     }
@@ -222,7 +223,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);
@@ -263,7 +264,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));
     }
@@ -330,7 +331,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)
@@ -359,7 +360,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)
@@ -388,7 +389,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);
@@ -444,7 +445,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);
@@ -472,7 +473,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);
     }
@@ -487,12 +488,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 ef39d5d..1ba7133 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -120,9 +120,50 @@ 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;
+
+  /* 1 is this is a FinishBreakpoint object, 0 otherwise.  */
+  int is_finish_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
 {
@@ -188,6 +229,7 @@ struct value *convert_value_from_python (PyObject *obj);
 struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
+struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);
 
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
@@ -202,6 +244,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);
@@ -275,6 +318,9 @@ PyObject *gdbpy_get_varobj_pretty_printer (struct value *value);
 char *gdbpy_get_display_hint (PyObject *printer);
 PyObject *gdbpy_default_visualizer (PyObject *self, PyObject *args);
 
+void bpfinishpy_pre_stop_hook (struct breakpoint_object *bp_obj);
+void bpfinishpy_post_stop_hook (struct breakpoint_object *bp_obj);
+
 extern PyObject *gdbpy_doc_cst;
 extern PyObject *gdbpy_children_cst;
 extern PyObject *gdbpy_to_string_cst;
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 6022572..2f8f828 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1247,6 +1247,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.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index e0dd087..0e3adbd 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.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..7cdbead
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,97 @@
+/* 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>
+#include <stdlib.h>
+#include <unistd.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);
+}
+
+void
+test_exec_exit (int do_exit)
+{
+  if (do_exit)
+    exit (0);
+  else
+    execl ("/bin/echo", "echo", "-1", (char *)0);
+}
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  getpid ();
+
+  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.  */
+
+  test_exec_exit (1);
+
+  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..57dce1b
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,233 @@
+# 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 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_breakpoint "increase_1"
+gdb_test "continue" "Breakpoint .*at.*" "continue to the function to finish"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp_default = gdb.FinishBreakpoint ()" \
+         "Temporary breakpoint.*" "set FinishBreakpoint with default frame value"
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval ('a'), gdb.newest_frame ())" \
+         "Temporary breakpoint.*" "set FinishBreakpoint"
+gdb_test "python print finishbp.return_value" "None.*" \
+         "check return_value at init"
+
+# check normal bp hit
+
+gdb_test "continue" "MyFinishBreakpoint stop with.*return_value is: -5.*#0.*increase.*" \
+         "check MyFinishBreakpoint hit"
+gdb_test "python print finishbp.return_value" "-5.*" "check return_value"
+
+gdb_test "python print finishbp_default.hit_count" "1.*" "check finishBP on default frame has been hit"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer normal hit"
+
+# check FinishBreakpoint in main no allowed
+
+gdb_test "finish" "main.*" "return to main()"
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         "ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \
+         "check FinishBP not allowed in main"
+
+#
+# Test FinishBreakpoint with no debug symbol 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+set cond_line [gdb_get_line_number "Condition Break."]
+
+if ![runto "getpid"] then {
+    fail "Cannot run to getpid."
+    return 0
+}
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "continue" "SimpleFinishBreakpoint stop.*" "check FinishBreakpoint hit"
+gdb_test "python print finishBP.return_value" "None" "check return value without debug symbol"
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "python finishbp = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" \
+         "check FinishBP out of scope notification"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy 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_1(i,8)" "Breakpoint .* at .*" \
+         "set a conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" \
+         "set FinishBP in a breakpoint condition"
+gdb_test "continue" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+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"
+
+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 explicit inferior function call
+#
+
+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
+}
+
+# return address in dummy frame
+
+gdb_test "python TestExplicitBreakpoint('increase_1')" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase_1(&i)" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+
+# return address in normal frame
+
+delete_breakpoints
+gdb_test "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase(&i)" \
+         "SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*The program being debugged stopped while in a function called from GDB.*" \
+         "FinishBP stop at during explicit function call"
+
+
+#
+# Test FinishBreakpoint when inferior exits
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}
+
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
+
+#
+# Test FinishBreakpoint when inferior execs
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}     
+
+gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
+gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ 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..dea2a73
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,89 @@
+# 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 MyFinishBreakpoint (gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		print "MyFinishBreakpoint init"
+		self.val = val
+		
+	def stop(self):
+		print "MyFinishBreakpoint stop with %d" % int (self.val.dereference ())
+		print "return_value is: %d" % int (self.return_value)
+		gdb.execute ("where 1")
+		return True
+	
+	def out_of_scope(self):
+		print "MyFinishBreakpoint out of scope"
+
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__ (self, spec="test_1", internal=1)
+        self.silent = True
+        self.count = 0
+        print "TestBreakpoint init"
+        
+    def stop(self):
+    	self.count += 1
+    	try:
+        	TestFinishBreakpoint (gdb.newest_frame (), self.count)
+        except ValueError as e:
+        	print e
+        return False
+
+class TestFinishBreakpoint (gdb.FinishBreakpoint):
+    def __init__ (self, frame, count):
+    	self.count = count
+        gdb.FinishBreakpoint.__init__ (self, frame, internal=1)
+        
+        
+    def stop(self):
+        print "-->", self.number
+        if (self.count == 3):
+            print "test stop: %d" % self.count
+            return True
+        else:
+            print "test don't stop: %d" % self.count
+            return False 
+        
+    
+    def out_of_scope(self):
+        print "test didn't finish: %d" % self.count
+
+class TestExplicitBreakpoint(gdb.Breakpoint):
+	def stop(self):
+		try:
+			SimpleFinishBreakpoint (gdb.newest_frame ())
+		except ValueError as e:
+			print e
+		return False
+
+class SimpleFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		
+		print "SimpleFinishBreakpoint init"
+		
+	def stop(self):
+		print "SimpleFinishBreakpoint stop" 
+		return True
+	
+	def out_of_scope(self):
+		print "SimpleFinishBreakpoint out of scope"
+
+print "Python script importedd"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.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-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
new file mode 100644
index 0000000..fae798c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -0,0 +1,65 @@
+# 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-breakpoint2"
+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
+}
+
+#
+# Check FinishBreakpoints against C++ exceptions
+#
+
+gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_breakpoint "throw_exception_1"
+gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
+
+gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.py b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
new file mode 100644
index 0000000..0fb6955
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
@@ -0,0 +1,33 @@
+# 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 ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__ (self, frame, internal=1)
+        self.silent = True;
+        print "init ExceptionFinishBreakpoint"
+        
+    def stop(self):
+        print "stopped at ExceptionFinishBreakpoint"
+        return True 
+    
+    def out_of_scope(self):
+        print "exception did not finish ..."
+
+
+print "Python script imported"
-- 
1.7.6.4


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

* Re: [RFC] Python Finish Breakpoints
  2011-11-30 16:02                                                     ` Kevin Pouget
@ 2011-12-02 21:49                                                       ` Tom Tromey
  2011-12-05  9:29                                                         ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Tom Tromey @ 2011-12-02 21:49 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches

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

Sorry about the delay on this.
I got kind of distracted this week.

Kevin> I noticed a bug in the previous version of the patch, which led to
Kevin> `out_of_scope' callback being triggered when it was not supposed to be
Kevin> (namely, when the breakpoint was hit, the execution continued, and
Kevin> then the callback was triggered before the temporary breakpoint could
Kevin> be deleted.

Looking close now.

Kevin> +      if (ret)
Kevin> +        self_finishbp->return_value = value_to_value_object (ret);

If this fails then the error is never cleared or handled.

I think checking == NULL and then calling gdbpy_print_stack is appropriate.

Kevin> +                  /* Ignore Python errors at this stage.  */
Kevin> +                  self_bpfinish->return_type = type_to_type_object (ret_type);
Kevin> +                  PyErr_Clear ();
Kevin> +                  self_bpfinish->function_type =
Kevin> +                      type_to_type_object (SYMBOL_TYPE (function));
Kevin> +                  PyErr_Clear ();

This looked a little weird, but I verified from the docs that it is ok.

I think this still needs a NEWS entry.

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2011-12-02 21:49                                                       ` Tom Tromey
@ 2011-12-05  9:29                                                         ` Kevin Pouget
  2011-12-06 21:18                                                           ` Tom Tromey
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-12-05  9:29 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

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

On Fri, Dec 2, 2011 at 10:48 PM, Tom Tromey <tromey@redhat.com> wrote:
>
> >>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
>
> Sorry about the delay on this.
> I got kind of distracted this week.
>
> Kevin> I noticed a bug in the previous version of the patch, which led to
> Kevin> `out_of_scope' callback being triggered when it was not supposed to be
> Kevin> (namely, when the breakpoint was hit, the execution continued, and
> Kevin> then the callback was triggered before the temporary breakpoint could
> Kevin> be deleted.
>
> Looking close now.
>
> Kevin> +      if (ret)
> Kevin> +        self_finishbp->return_value = value_to_value_object (ret);
>
> If this fails then the error is never cleared or handled.
>
> I think checking == NULL and then calling gdbpy_print_stack is appropriate.

you're right, fixed

>
> Kevin> +                  /* Ignore Python errors at this stage.  */
> Kevin> +                  self_bpfinish->return_type = type_to_type_object (ret_type);
> Kevin> +                  PyErr_Clear ();
> Kevin> +                  self_bpfinish->function_type =
> Kevin> +                      type_to_type_object (SYMBOL_TYPE (function));
> Kevin> +                  PyErr_Clear ();
>
> This looked a little weird, but I verified from the docs that it is ok.
>
> I think this still needs a NEWS entry.

nop, it was in the patch :)

diff --git a/gdb/NEWS b/gdb/NEWS
index f13caf8..7b3f7f2 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -51,6 +51,10 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".

+  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
+     of a function.  This class is based on the "finish" command
+     available in the CLI.
+
   ** Type objects for struct and union types now allow access to
      the fields using standard Python dictionary (mapping) methods.
      For example, "some_type['myfield']" now works, as does

it was also reviewed by Eli in :
http://sourceware.org/ml/gdb-patches/2011-10/msg00645.html


Thanks for your review,

cordially,

Kevin

(no regression against current git source tree)

--

2011-12-05  Kevin Pouget  <kevin.pouget@st.com>

	Introduce gdb.FinishBreakpoint in Python

	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-finishbreakpoint.o.
	(SUBDIR_PYTHON_SRCS): Add python/py-finishbreakpoint.c.
	Add build rule for this file.
	* infcmd.c (print_return_value): Split to create get_return_value.
	(get_return_value): New function based on print_return_value. Handle
	case where stop_registers are not set.
	* inferior.h (get_return_value): New prototype.
	* python/py-breakpoint.c (bppy_pending_object): Make non-static.
	(gdbpy_breakpoint_created): Set is_py_finish_bp is necessary.
	(struct breakpoint_object): Move to python-internal.h
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(gdbpy_breakpoint_created): Initialize is_finish_bp.
	(gdbpy_should_stop): Add  pre/post hooks before/after calling stop
	method.
	* python/python-internal.h (breakpoint_object_type): Add as extern.
	(bppy_pending_object): Likewise.
	(typedef struct breakpoint_object) Removed.
	(struct breakpoint_object): Moved from py-breakpoint.c.
	Add field is_finish_bp.
	(BPPY_REQUIRE_VALID): Moved from py-breakpoint.c.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(frame_object_to_frame_info): New prototype.
	(gdbpy_initialize_finishbreakpoints): New prototype.
	(bpfinishpy_is_finish_bp): Likewise.
	(bpfinishpy_pre_stop_hook): Likewise.
	(bpfinishpy_post_stop_hook): Likewise.
	* python/py-finishbreakpoint.c: New file.
	* python/py-frame.c(frame_object_to_frame_info): Make non-static and
	accept PyObject instead of frame_object.
	(frapy_is_valid): Don't cast to frame_object.
	(frapy_name): Likewise.
	(frapy_type): Likewise.
	(frapy_unwind_stop_reason): Likewise.
	(frapy_pc): Likewise.
	(frapy_block): Likewise.
	(frapy_function): Likewise.
	(frapy_older): Likewise.
	(frapy_newer): Likewise.
	(frapy_find_sal): Likewise.
	(frapy_read_var): Likewise.
	(frapy_select): Likewise.
	* python/python.c (gdbpy_is_stopped_at_finish_bp): New noop function.
	(_initialize_python): Add gdbpy_initialize_finishbreakpoints.
	* python/python.h: Include breakpoint.h
	(gdbpy_is_stopped_at_finish_bp): New prototype.

doc/
	* gdb.texinfo (Finish Breakpoints in Python): New subsection.
	(Python API): Add menu entry for Finish Breakpoints.

testsuite/
	* gdb.python/py-breakpoint.exp (mult_line): Define and use variable
	instead of line number.
	* gdb.python/py-finish-breakpoint.c: New file.
	* gdb.python/py-finish-breakpoint.exp: New file.
	* gdb.python/py-finish-breakpoint.py: New file.
	* gdb.python/py-finish-breakpoint2.cc: New file.
	* gdb.python/py-finish-breakpoint2.exp: New file.
	* gdb.python/py-finish-breakpoint2.py: New file.

[-- Attachment #2: 0001-Introduce-gdb.FinishBreakpoint.patch --]
[-- Type: text/x-patch, Size: 55961 bytes --]

From 25feda77b25e5f66a272ca06066943515ebdacc7 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Tue, 20 Sep 2011 13:59:23 +0200
Subject: [PATCH]  Introduce gdb.FinishBreakpoint

---
 gdb/Makefile.in                                    |    6 +
 gdb/NEWS                                           |    4 +
 gdb/doc/gdb.texinfo                                |   51 +++
 gdb/infcmd.c                                       |   39 ++-
 gdb/inferior.h                                     |    3 +
 gdb/python/py-breakpoint.c                         |   48 +--
 gdb/python/py-finishbreakpoint.c                   |  452 ++++++++++++++++++++
 gdb/python/py-frame.c                              |   32 +-
 gdb/python/python-internal.h                       |   50 ++-
 gdb/python/python.c                                |    1 +
 gdb/testsuite/gdb.python/py-breakpoint.exp         |    7 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.c    |   97 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp  |  233 ++++++++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py   |   89 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc  |   59 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   65 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.py  |   33 ++
 17 files changed, 1204 insertions(+), 65 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 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
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.py

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b71db33..d8d1b40 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,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 \
@@ -318,6 +319,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 \
@@ -2109,6 +2111,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/NEWS b/gdb/NEWS
index f13caf8..7b3f7f2 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -51,6 +51,10 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".
 
+  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
+     of a function.  This class is based on the "finish" command
+     available in the CLI. 
+
   ** Type objects for struct and union types now allow access to
      the fields using standard Python dictionary (mapping) methods.
      For example, "some_type['myfield']" now works, as does
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index c6d58fb..778251d 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21502,6 +21502,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Symbol Tables In Python::     Python representation of symbol tables.
 * Lazy Strings In Python::      Python representation of lazy strings.
 * Breakpoints In Python::       Manipulating breakpoints using Python.
+* Finish Breakpoints in Python:: Setting Breakpoints on function return
+                                using Python.
 @end menu
 
 @node Basic Python
@@ -24336,6 +24338,55 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@node Finish Breakpoints in Python
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A finish breakpoint is a temporary breakpoint set at the return address of
+a frame, based on the @code{finish} command.  @code{gdb.FinishBreakpoint}
+extends @code{gdb.Breakpoint}.  The underlying breakpoint will be disabled 
+and deleted when the execution will run out of the breakpoint scope (i.e.@: 
+@code{Breakpoint.stop} or @code{FinishBreakpoint.out_of_scope} triggered).
+
+@defun FinishBreakpoint.__init__ (@r{[}frame@r{]} @r{[}, internal@r{]})
+Create a finish breakpoint at the return address of the @code{gdb.Frame}
+object @var{frame}.  If @var{frame} is not provided, this defaults to the
+newest frame.  The optional @var{internal} argument allows the breakpoint to
+become invisible to the user.  @xref{Breakpoints In Python}, for further 
+details about this argument.
+@end defun
+
+@defun FinishBreakpoint.out_of_scope (self)
+In some circumstances (e.g.@: @code{longjmp}, C@t{++} exceptions, @value{GDBN} 
+@code{return} command, @dots{}), a function may not properly terminate, and
+thus never hit the finish breakpoint.  When @value{GDBN} notices such a
+situation, the @code{out_of_scope} callback will be triggered.
+
+You may want to sub-class @code{gdb.FinishBreakpoint} and override this
+method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+    def stop (self):
+        print "normal finish"
+        return True
+    
+    def out_of_scope ():
+        print "abnormal finish"
+@end smallexample 
+@end defun
+
+@defvar FinishBreakpoint.return_value
+When @value{GDBN} is stopped at a finish breakpoint and the frame 
+used to build the @code{gdb.FinishBreakpoint} object had debug symbols, this
+attribute will contain a @code{gdb.Value} object corresponding to the return
+value of the function.  The value will be @code{None} if the function return 
+type is @code{void} or if the return value was not computable.  This attribute
+is not writable.
+@end defvar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 7b935fe..76addf0 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1413,16 +1413,26 @@ 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.  */
+/* Return the value of the result of a function 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 regcache *stop_regs = stop_registers;
+  struct gdbarch *gdbarch;
   struct value *value;
   struct ui_out *uiout = current_uiout;
+  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
+
+  /* If stop_registers were not saved, use the current registers.  */
+  if (!stop_regs)
+    {
+      stop_regs = regcache_dup (get_current_regcache ());
+      cleanup = make_cleanup_regcache_xfree (stop_regs);
+    }
+
+  gdbarch = get_regcache_arch (stop_regs);
 
   CHECK_TYPEDEF (value_type);
   gdb_assert (TYPE_CODE (value_type) != TYPE_CODE_VOID);
@@ -1441,7 +1451,7 @@ print_return_value (struct type *func_type, struct type *value_type)
     case RETURN_VALUE_ABI_RETURNS_ADDRESS:
     case RETURN_VALUE_ABI_PRESERVES_ADDRESS:
       value = allocate_value (value_type);
-      gdbarch_return_value (gdbarch, func_type, value_type, stop_registers,
+      gdbarch_return_value (gdbarch, func_type, value_type, stop_regs,
 			    value_contents_raw (value), NULL);
       break;
     case RETURN_VALUE_STRUCT_CONVENTION:
@@ -1451,6 +1461,21 @@ print_return_value (struct type *func_type, struct type *value_type)
       internal_error (__FILE__, __LINE__, _("bad switch"));
     }
 
+  do_cleanups (cleanup);
+
+  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;
+  struct ui_out *uiout = current_uiout;
+
   if (value)
     {
       struct value_print_options opts;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..f198696 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -269,6 +269,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/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 11d60fe..64ef45f 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -31,52 +31,16 @@
 #include "arch-utils.h"
 #include "language.h"
 
-static 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
 {
@@ -762,6 +726,9 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
   struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
   struct cleanup *cleanup = ensure_python_env (garch, current_language);
 
+  if (bp_obj->is_finish_bp)
+    bpfinishpy_pre_stop_hook (bp_obj);
+
   if (PyObject_HasAttrString (py_bp, stop_func))
     {
       PyObject *result = PyObject_CallMethod (py_bp, stop_func, NULL);
@@ -783,6 +750,10 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
       else
 	gdbpy_print_stack ();
     }
+
+  if (bp_obj->is_finish_bp)
+    bpfinishpy_post_stop_hook (bp_obj);
+
   do_cleanups (cleanup);
 
   return stop;
@@ -845,6 +816,7 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->number = bp->number;
       newbp->bp = bp;
       newbp->bp->py_bp_object = newbp;
+      newbp->is_finish_bp = 0;
       Py_INCREF (newbp);
       ++bppy_live;
     }
@@ -1006,7 +978,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..12bfe5a
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,452 @@
+/* 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.  */
+  breakpoint_object py_bp;
+  /* gdb.Type object of the value return by the breakpointed function.
+     May be NULL if no debug information was available or return type
+     was VOID.  */
+  PyObject *return_type;
+  /* gdb.Type object of the function finished by this breakpoint.  Will be
+     NULL if return_type is NULL.  */
+  PyObject *function_type;
+  /* When stopped at this FinishBreakpoint, gdb.Value object returned by
+     the function; Py_None if the value is not computable; NULL if GDB is
+     not stopped at a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* 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;
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* Deallocate FinishBreakpoint object.  */
+
+static void
+bpfinishpy_dealloc (PyObject *self)
+{
+  struct finish_breakpoint_object *self_bpfinish =
+        (struct finish_breakpoint_object *) self;
+
+  Py_XDECREF (self_bpfinish->function_type);
+  Py_XDECREF (self_bpfinish->return_type);
+  Py_XDECREF (self_bpfinish->return_value);
+}
+
+/* Triggered when gdbpy_should_stop is about to execute the `stop' callback
+   of the gdb.FinishBreakpoint object BP_OBJ.  Will compute and cache the
+   `return_value', if possible.  */
+
+void
+bpfinishpy_pre_stop_hook (struct breakpoint_object *bp_obj)
+{
+  struct finish_breakpoint_object *self_finishbp =
+        (struct finish_breakpoint_object *) bp_obj;
+  volatile struct gdb_exception except;
+
+  /* Can compute return_value only once.  */
+  gdb_assert (!self_finishbp->return_value);
+
+  if (!self_finishbp->return_type)
+    return;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      struct value *ret =
+          get_return_value (type_object_to_type (self_finishbp->function_type),
+                            type_object_to_type (self_finishbp->return_type));
+
+      if (ret)
+        {
+          self_finishbp->return_value = value_to_value_object (ret);
+          if (!self_finishbp->return_value)
+              gdbpy_print_stack ();
+        }
+      else
+        {
+          Py_INCREF (Py_None);
+          self_finishbp->return_value = Py_None;
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      gdbpy_print_stack ();
+    }
+}
+
+/* Triggered when gdbpy_should_stop has triggered the `stop' callback
+   of the gdb.FinishBreakpoint object BP_OBJ.  */
+
+void
+bpfinishpy_post_stop_hook (struct breakpoint_object *bp_obj)
+{
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Can't delete it here, but it will be removed at the next stop.  */
+      disable_breakpoint (bp_obj->bp);
+      gdb_assert (bp_obj->bp->disposition == disp_del);
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      gdbpy_print_stack ();
+    }
+}
+
+/* Python function to create a new breakpoint.  */
+
+static int
+bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+  static char *keywords[] = { "frame", "internal", NULL };
+  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 = NULL;
+  struct frame_id frame_id;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs, "|OO", keywords,
+                                    &frame_obj, &internal))
+    return -1;
+
+  /* Default to gdb.newest_frame if necessary.  */
+  if (!frame_obj)
+    frame_obj = gdbpy_newest_frame (NULL, NULL);
+  else
+    Py_INCREF (frame_obj);
+
+  frame = frame_object_to_frame_info (frame_obj);
+  Py_DECREF (frame_obj);
+
+  if (frame == NULL)
+    goto invalid_frame;
+  
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      prev_frame = get_prev_frame (frame);
+      if (prev_frame == 0)
+        {
+          PyErr_SetString (PyExc_ValueError, _("\"FinishBreakpoint\" not "   \
+                                               "meaningful in the outermost "\
+                                               "frame."));
+        }
+      else if (get_frame_type (prev_frame) == DUMMY_FRAME)
+        {
+          PyErr_SetString (PyExc_ValueError, _("\"FinishBreakpoint\" cannot "\
+                                               "be set on a dummy frame."));
+        }
+      else
+        {
+          frame_id = get_frame_id (prev_frame);
+          if (frame_id_eq (frame_id, null_frame_id))
+            PyErr_SetString (PyExc_ValueError,
+                             _("Invalid ID for the `frame' object."));
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      return -1;
+    }
+  else if (PyErr_Occurred ())
+    return -1;
+
+  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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  self_bpfinish->return_type = NULL;
+  self_bpfinish->function_type = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (get_frame_pc_if_available (frame, &pc))
+        {
+          function = find_pc_function (pc);
+          if (function != NULL)
+            {
+              struct type *ret_type =
+                  TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+              /* Remember only non-void return types.  */
+              if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
+                {
+                  /* Ignore Python errors at this stage.  */
+                  self_bpfinish->return_type = type_to_type_object (ret_type);
+                  PyErr_Clear ();
+                  self_bpfinish->function_type =
+                      type_to_type_object (SYMBOL_TYPE (function));
+                  PyErr_Clear ();
+                }
+            }
+        }
+    }
+  if (except.reason < 0
+      || !self_bpfinish->return_type || !self_bpfinish->function_type)
+    {
+      /* Won't be able to compute return value.  */
+      Py_XDECREF (self_bpfinish->return_type);
+      Py_XDECREF (self_bpfinish->function_type);
+
+      self_bpfinish->return_type = NULL;
+      self_bpfinish->function_type = NULL;
+    }
+
+  bppy_pending_object = &self_bpfinish->py_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, -1,
+                         0,
+                         1 /*temp_flag*/,
+                         bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         &bkpt_breakpoint_ops,
+                         0, 1, internal_bp);
+    }
+  GDB_PY_SET_HANDLE_EXCEPTION (except);
+  
+  self_bpfinish->py_bp.bp->frame_id = frame_id;
+  self_bpfinish->py_bp.is_finish_bp = 1;
+  
+  return 0;
+  
+ invalid_frame:
+  PyErr_SetString (PyExc_ValueError, 
+                   _("Invalid ID for the `frame' object."));
+  return -1;
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack.  Triggers the method OUT_OF_SCOPE if implemented,
+   then delete the breakpoint.  */
+
+static void
+bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
+{
+  volatile struct gdb_exception except;
+  breakpoint_object *bp_obj = (breakpoint_object *) bpfinish_obj;
+  PyObject *py_obj = (PyObject *) bp_obj;
+
+  if (bpfinish_obj->py_bp.bp->enable_state == bp_enabled
+      && PyObject_HasAttrString (py_obj, outofscope_func))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+
+  delete_breakpoint (bpfinish_obj->py_bp.bp);
+}
+
+/* Callback for `bpfinishpy_detect_out_scope'.  Triggers Python's
+   `B->out_of_scope' function if B is a FinishBreakpoint out of its scope.  */
+
+static int
+bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
+{
+  volatile struct gdb_exception except;
+  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 ();
+  
+  /* Trigger out_of_scope if this is a FinishBreakpoint and its frame is
+     not anymore in the current callstack.  */
+  if (py_bp != NULL && b->py_bp_object->is_finish_bp)
+    {
+      struct finish_breakpoint_object *finish_bp =
+          (struct finish_breakpoint_object *) py_bp;
+
+      /* Check scope if not currently stopped at the FinishBreakpoint.  */
+      if (b != bp_stopped)
+        {
+          TRY_CATCH (except, RETURN_MASK_ALL)
+            {
+              if (b->pspace == current_inferior ()->pspace
+                 && (!target_has_registers
+                     || frame_find_by_id (b->frame_id) == NULL))
+              {
+                bpfinishpy_out_of_scope (finish_bp);
+              }
+            }
+          if (except.reason < 0)
+            {
+              gdbpy_convert_exception (except);
+              gdbpy_print_stack ();
+            }
+        }
+    }
+
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run
+   out of the scope of any FinishBreakpoint before it has been hit.  */
+
+static void
+bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
+{
+  struct cleanup *cleanup = ensure_python_env (get_current_arch (),
+                                               current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
+                            bs == NULL ? NULL : bs->breakpoint_at);
+
+  do_cleanups (cleanup);
+}
+
+/* Attached to `exit' notifications, triggers all the necessary out of
+   scope notifications.  */
+
+static void
+bpfinishpy_handle_exit (struct inferior *inf)
+{
+  struct cleanup *cleanup = ensure_python_env (target_gdbarch,
+                                               current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
+
+  do_cleanups (cleanup);
+}
+
+/* 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 PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "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*/
+  bpfinishpy_dealloc,             /*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 20064ca..c334f63 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);
@@ -106,7 +107,7 @@ frapy_is_valid (PyObject *self, PyObject *args)
 
   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      frame = frame_object_to_frame_info ((frame_object *) self);
+      frame = frame_object_to_frame_info (self);
     }
   GDB_PY_HANDLE_EXCEPTION (except);
 
@@ -130,7 +131,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);
     }
@@ -159,7 +160,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);
     }
@@ -180,7 +181,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);
 
@@ -201,7 +202,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);
     }
@@ -222,7 +223,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);
@@ -263,7 +264,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));
     }
@@ -330,7 +331,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)
@@ -359,7 +360,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)
@@ -388,7 +389,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);
@@ -444,7 +445,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);
@@ -472,7 +473,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);
     }
@@ -487,12 +488,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 ef39d5d..1ba7133 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -120,9 +120,50 @@ 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;
+
+  /* 1 is this is a FinishBreakpoint object, 0 otherwise.  */
+  int is_finish_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
 {
@@ -188,6 +229,7 @@ struct value *convert_value_from_python (PyObject *obj);
 struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
+struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);
 
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
@@ -202,6 +244,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);
@@ -275,6 +318,9 @@ PyObject *gdbpy_get_varobj_pretty_printer (struct value *value);
 char *gdbpy_get_display_hint (PyObject *printer);
 PyObject *gdbpy_default_visualizer (PyObject *self, PyObject *args);
 
+void bpfinishpy_pre_stop_hook (struct breakpoint_object *bp_obj);
+void bpfinishpy_post_stop_hook (struct breakpoint_object *bp_obj);
+
 extern PyObject *gdbpy_doc_cst;
 extern PyObject *gdbpy_children_cst;
 extern PyObject *gdbpy_to_string_cst;
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 6022572..2f8f828 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1247,6 +1247,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.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index e0dd087..0e3adbd 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.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..7cdbead
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,97 @@
+/* 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>
+#include <stdlib.h>
+#include <unistd.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);
+}
+
+void
+test_exec_exit (int do_exit)
+{
+  if (do_exit)
+    exit (0);
+  else
+    execl ("/bin/echo", "echo", "-1", (char *)0);
+}
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  getpid ();
+
+  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.  */
+
+  test_exec_exit (1);
+
+  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..57dce1b
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,233 @@
+# 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 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_breakpoint "increase_1"
+gdb_test "continue" "Breakpoint .*at.*" "continue to the function to finish"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp_default = gdb.FinishBreakpoint ()" \
+         "Temporary breakpoint.*" "set FinishBreakpoint with default frame value"
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval ('a'), gdb.newest_frame ())" \
+         "Temporary breakpoint.*" "set FinishBreakpoint"
+gdb_test "python print finishbp.return_value" "None.*" \
+         "check return_value at init"
+
+# check normal bp hit
+
+gdb_test "continue" "MyFinishBreakpoint stop with.*return_value is: -5.*#0.*increase.*" \
+         "check MyFinishBreakpoint hit"
+gdb_test "python print finishbp.return_value" "-5.*" "check return_value"
+
+gdb_test "python print finishbp_default.hit_count" "1.*" "check finishBP on default frame has been hit"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer normal hit"
+
+# check FinishBreakpoint in main no allowed
+
+gdb_test "finish" "main.*" "return to main()"
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         "ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \
+         "check FinishBP not allowed in main"
+
+#
+# Test FinishBreakpoint with no debug symbol 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+set cond_line [gdb_get_line_number "Condition Break."]
+
+if ![runto "getpid"] then {
+    fail "Cannot run to getpid."
+    return 0
+}
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "continue" "SimpleFinishBreakpoint stop.*" "check FinishBreakpoint hit"
+gdb_test "python print finishBP.return_value" "None" "check return value without debug symbol"
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "python finishbp = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" \
+         "check FinishBP out of scope notification"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy 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_1(i,8)" "Breakpoint .* at .*" \
+         "set a conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" \
+         "set FinishBP in a breakpoint condition"
+gdb_test "continue" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+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"
+
+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 explicit inferior function call
+#
+
+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
+}
+
+# return address in dummy frame
+
+gdb_test "python TestExplicitBreakpoint('increase_1')" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase_1(&i)" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+
+# return address in normal frame
+
+delete_breakpoints
+gdb_test "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase(&i)" \
+         "SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*The program being debugged stopped while in a function called from GDB.*" \
+         "FinishBP stop at during explicit function call"
+
+
+#
+# Test FinishBreakpoint when inferior exits
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}
+
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
+
+#
+# Test FinishBreakpoint when inferior execs
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}     
+
+gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
+gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ 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..dea2a73
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,89 @@
+# 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 MyFinishBreakpoint (gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		print "MyFinishBreakpoint init"
+		self.val = val
+		
+	def stop(self):
+		print "MyFinishBreakpoint stop with %d" % int (self.val.dereference ())
+		print "return_value is: %d" % int (self.return_value)
+		gdb.execute ("where 1")
+		return True
+	
+	def out_of_scope(self):
+		print "MyFinishBreakpoint out of scope"
+
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__ (self, spec="test_1", internal=1)
+        self.silent = True
+        self.count = 0
+        print "TestBreakpoint init"
+        
+    def stop(self):
+    	self.count += 1
+    	try:
+        	TestFinishBreakpoint (gdb.newest_frame (), self.count)
+        except ValueError as e:
+        	print e
+        return False
+
+class TestFinishBreakpoint (gdb.FinishBreakpoint):
+    def __init__ (self, frame, count):
+    	self.count = count
+        gdb.FinishBreakpoint.__init__ (self, frame, internal=1)
+        
+        
+    def stop(self):
+        print "-->", self.number
+        if (self.count == 3):
+            print "test stop: %d" % self.count
+            return True
+        else:
+            print "test don't stop: %d" % self.count
+            return False 
+        
+    
+    def out_of_scope(self):
+        print "test didn't finish: %d" % self.count
+
+class TestExplicitBreakpoint(gdb.Breakpoint):
+	def stop(self):
+		try:
+			SimpleFinishBreakpoint (gdb.newest_frame ())
+		except ValueError as e:
+			print e
+		return False
+
+class SimpleFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		
+		print "SimpleFinishBreakpoint init"
+		
+	def stop(self):
+		print "SimpleFinishBreakpoint stop" 
+		return True
+	
+	def out_of_scope(self):
+		print "SimpleFinishBreakpoint out of scope"
+
+print "Python script importedd"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.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-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
new file mode 100644
index 0000000..fae798c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -0,0 +1,65 @@
+# 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-breakpoint2"
+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
+}
+
+#
+# Check FinishBreakpoints against C++ exceptions
+#
+
+gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_breakpoint "throw_exception_1"
+gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
+
+gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.py b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
new file mode 100644
index 0000000..0fb6955
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
@@ -0,0 +1,33 @@
+# 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 ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__ (self, frame, internal=1)
+        self.silent = True;
+        print "init ExceptionFinishBreakpoint"
+        
+    def stop(self):
+        print "stopped at ExceptionFinishBreakpoint"
+        return True 
+    
+    def out_of_scope(self):
+        print "exception did not finish ..."
+
+
+print "Python script imported"
-- 
1.7.6.4


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

* Re: [RFC] Python Finish Breakpoints
  2011-12-05  9:29                                                         ` Kevin Pouget
@ 2011-12-06 21:18                                                           ` Tom Tromey
  2011-12-07 13:35                                                             ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Tom Tromey @ 2011-12-06 21:18 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches

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

Kevin> you're right, fixed

Thanks.

Tom> I think this still needs a NEWS entry.

Kevin> nop, it was in the patch :)

Whoops :-)

Kevin> (no regression against current git source tree)

This is ok.  Thank you very much.

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2011-12-06 21:18                                                           ` Tom Tromey
@ 2011-12-07 13:35                                                             ` Kevin Pouget
  2011-12-08 15:30                                                               ` Tom Tromey
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-12-07 13:35 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

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

On Tue, Dec 6, 2011 at 9:45 PM, Tom Tromey <tromey@redhat.com> wrote:
>>>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
>
> Kevin> you're right, fixed
>
> Thanks.
>
> Tom> I think this still needs a NEWS entry.
>
> Kevin> nop, it was in the patch :)
>
> Whoops :-)
>
> Kevin> (no regression against current git source tree)
>
> This is ok.  Thank you very much.
>
> Tom

and we're .... not ready!

there are some regressions this morning, certainly due to your
ambiguous linespec patch!

first one is quite obvious:

a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
-if ![runto "getpid"] then {
-    fail "Cannot run to getpid."
+if ![runto_main] then {
+    fail "Cannot run to main."     return 0 }
+gdb_breakpoint "getpid" {temporary}
+gdb_test "continue" "Temporary breakpoint .*in getpid.*" "continue to getpid"
+

"break getpid" used to work before actually starting the inferior, but
now it's unresolved


for the second one I'll need your opinion:

> bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
> ...
> if (b->pspace == current_inferior ()->pspace
>     && (!target_has_registers
>         || frame_find_by_id (b->frame_id) == NULL))
>           bpfinishpy_out_of_scope (finish_bp);


bpfinishpy_out_of_scope can't be triggered anymore because you set
b->pspace to NULL. I hesitated about how to fix it, but I think that
FinishBP have to be bound to the pspace in which they were set, so I
added this line:

> bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
> ...
>   /* Bind the breakpoint with the current program space.  */
>   self_bpfinish->py_bp.bp->pspace = current_program_space;

I'm not sure whether your plans are to eventually get rid of this
field, but the pspace comments seem to indicate that this is the right
way to do


Cordially,

Kevin

--

2011-12-07  Kevin Pouget  <kevin.pouget@st.com>

	Introduce gdb.FinishBreakpoint in Python

	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-finishbreakpoint.o.
	(SUBDIR_PYTHON_SRCS): Add python/py-finishbreakpoint.c.
	Add build rule for this file.
	* infcmd.c (print_return_value): Split to create get_return_value.
	(get_return_value): New function based on print_return_value. Handle
	case where stop_registers are not set.
	* inferior.h (get_return_value): New prototype.
	* python/py-breakpoint.c (bppy_pending_object): Make non-static.
	(gdbpy_breakpoint_created): Set is_py_finish_bp is necessary.
	(struct breakpoint_object): Move to python-internal.h
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(gdbpy_breakpoint_created): Initialize is_finish_bp.
	(gdbpy_should_stop): Add  pre/post hooks before/after calling stop
	method.
	* python/python-internal.h (breakpoint_object_type): Add as extern.
	(bppy_pending_object): Likewise.
	(typedef struct breakpoint_object) Removed.
	(struct breakpoint_object): Moved from py-breakpoint.c.
	Add field is_finish_bp.
	(BPPY_REQUIRE_VALID): Moved from py-breakpoint.c.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(frame_object_to_frame_info): New prototype.
	(gdbpy_initialize_finishbreakpoints): New prototype.
	(bpfinishpy_is_finish_bp): Likewise.
	(bpfinishpy_pre_stop_hook): Likewise.
	(bpfinishpy_post_stop_hook): Likewise.
	* python/py-finishbreakpoint.c: New file.
	* python/py-frame.c(frame_object_to_frame_info): Make non-static and
	accept PyObject instead of frame_object.
	(frapy_is_valid): Don't cast to frame_object.
	(frapy_name): Likewise.
	(frapy_type): Likewise.
	(frapy_unwind_stop_reason): Likewise.
	(frapy_pc): Likewise.
	(frapy_block): Likewise.
	(frapy_function): Likewise.
	(frapy_older): Likewise.
	(frapy_newer): Likewise.
	(frapy_find_sal): Likewise.
	(frapy_read_var): Likewise.
	(frapy_select): Likewise.
	* python/python.c (gdbpy_is_stopped_at_finish_bp): New noop function.
	(_initialize_python): Add gdbpy_initialize_finishbreakpoints.
	* python/python.h: Include breakpoint.h
	(gdbpy_is_stopped_at_finish_bp): New prototype.

doc/
	* gdb.texinfo (Finish Breakpoints in Python): New subsection.
	(Python API): Add menu entry for Finish Breakpoints.

testsuite/
	* gdb.python/py-breakpoint.exp (mult_line): Define and use variable
	instead of line number.
	* gdb.python/py-finish-breakpoint.c: New file.
	* gdb.python/py-finish-breakpoint.exp: New file.
	* gdb.python/py-finish-breakpoint.py: New file.
	* gdb.python/py-finish-breakpoint2.cc: New file.
	* gdb.python/py-finish-breakpoint2.exp: New file.
	* gdb.python/py-finish-breakpoint2.py: New file.

[-- Attachment #2: 0001-Introduce-gdb.FinishBreakpoint.patch --]
[-- Type: text/x-patch, Size: 56167 bytes --]

From ac57db9b5c90034fb7f8acdb69c8fc7294d41b1c Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Tue, 20 Sep 2011 13:59:23 +0200
Subject: [PATCH]  Introduce gdb.FinishBreakpoint

---
 gdb/Makefile.in                                    |    6 +
 gdb/NEWS                                           |    4 +
 gdb/doc/gdb.texinfo                                |   51 +++
 gdb/infcmd.c                                       |   39 ++-
 gdb/inferior.h                                     |    3 +
 gdb/python/py-breakpoint.c                         |   48 +--
 gdb/python/py-finishbreakpoint.c                   |  453 ++++++++++++++++++++
 gdb/python/py-frame.c                              |   32 +-
 gdb/python/python-internal.h                       |   50 ++-
 gdb/python/python.c                                |    1 +
 gdb/testsuite/gdb.python/py-breakpoint.exp         |    7 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.c    |   97 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp  |  237 ++++++++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py   |   89 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc  |   59 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   65 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.py  |   33 ++
 17 files changed, 1209 insertions(+), 65 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 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
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.py

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b71db33..d8d1b40 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,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 \
@@ -318,6 +319,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 \
@@ -2109,6 +2111,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/NEWS b/gdb/NEWS
index 42782ce..b755605 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -57,6 +57,10 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".
 
+  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
+     of a function.  This class is based on the "finish" command
+     available in the CLI. 
+
   ** Type objects for struct and union types now allow access to
      the fields using standard Python dictionary (mapping) methods.
      For example, "some_type['myfield']" now works, as does
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 50c299e..e31b03e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21501,6 +21501,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Symbol Tables In Python::     Python representation of symbol tables.
 * Lazy Strings In Python::      Python representation of lazy strings.
 * Breakpoints In Python::       Manipulating breakpoints using Python.
+* Finish Breakpoints in Python:: Setting Breakpoints on function return
+                                using Python.
 @end menu
 
 @node Basic Python
@@ -24335,6 +24337,55 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@node Finish Breakpoints in Python
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A finish breakpoint is a temporary breakpoint set at the return address of
+a frame, based on the @code{finish} command.  @code{gdb.FinishBreakpoint}
+extends @code{gdb.Breakpoint}.  The underlying breakpoint will be disabled 
+and deleted when the execution will run out of the breakpoint scope (i.e.@: 
+@code{Breakpoint.stop} or @code{FinishBreakpoint.out_of_scope} triggered).
+
+@defun FinishBreakpoint.__init__ (@r{[}frame@r{]} @r{[}, internal@r{]})
+Create a finish breakpoint at the return address of the @code{gdb.Frame}
+object @var{frame}.  If @var{frame} is not provided, this defaults to the
+newest frame.  The optional @var{internal} argument allows the breakpoint to
+become invisible to the user.  @xref{Breakpoints In Python}, for further 
+details about this argument.
+@end defun
+
+@defun FinishBreakpoint.out_of_scope (self)
+In some circumstances (e.g.@: @code{longjmp}, C@t{++} exceptions, @value{GDBN} 
+@code{return} command, @dots{}), a function may not properly terminate, and
+thus never hit the finish breakpoint.  When @value{GDBN} notices such a
+situation, the @code{out_of_scope} callback will be triggered.
+
+You may want to sub-class @code{gdb.FinishBreakpoint} and override this
+method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+    def stop (self):
+        print "normal finish"
+        return True
+    
+    def out_of_scope ():
+        print "abnormal finish"
+@end smallexample 
+@end defun
+
+@defvar FinishBreakpoint.return_value
+When @value{GDBN} is stopped at a finish breakpoint and the frame 
+used to build the @code{gdb.FinishBreakpoint} object had debug symbols, this
+attribute will contain a @code{gdb.Value} object corresponding to the return
+value of the function.  The value will be @code{None} if the function return 
+type is @code{void} or if the return value was not computable.  This attribute
+is not writable.
+@end defvar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 5d94cd4..cffa194 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1414,16 +1414,26 @@ 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.  */
+/* Return the value of the result of a function 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 regcache *stop_regs = stop_registers;
+  struct gdbarch *gdbarch;
   struct value *value;
   struct ui_out *uiout = current_uiout;
+  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
+
+  /* If stop_registers were not saved, use the current registers.  */
+  if (!stop_regs)
+    {
+      stop_regs = regcache_dup (get_current_regcache ());
+      cleanup = make_cleanup_regcache_xfree (stop_regs);
+    }
+
+  gdbarch = get_regcache_arch (stop_regs);
 
   CHECK_TYPEDEF (value_type);
   gdb_assert (TYPE_CODE (value_type) != TYPE_CODE_VOID);
@@ -1442,7 +1452,7 @@ print_return_value (struct type *func_type, struct type *value_type)
     case RETURN_VALUE_ABI_RETURNS_ADDRESS:
     case RETURN_VALUE_ABI_PRESERVES_ADDRESS:
       value = allocate_value (value_type);
-      gdbarch_return_value (gdbarch, func_type, value_type, stop_registers,
+      gdbarch_return_value (gdbarch, func_type, value_type, stop_regs,
 			    value_contents_raw (value), NULL);
       break;
     case RETURN_VALUE_STRUCT_CONVENTION:
@@ -1452,6 +1462,21 @@ print_return_value (struct type *func_type, struct type *value_type)
       internal_error (__FILE__, __LINE__, _("bad switch"));
     }
 
+  do_cleanups (cleanup);
+
+  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;
+  struct ui_out *uiout = current_uiout;
+
   if (value)
     {
       struct value_print_options opts;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..f198696 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -269,6 +269,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/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 11d60fe..64ef45f 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -31,52 +31,16 @@
 #include "arch-utils.h"
 #include "language.h"
 
-static 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
 {
@@ -762,6 +726,9 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
   struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
   struct cleanup *cleanup = ensure_python_env (garch, current_language);
 
+  if (bp_obj->is_finish_bp)
+    bpfinishpy_pre_stop_hook (bp_obj);
+
   if (PyObject_HasAttrString (py_bp, stop_func))
     {
       PyObject *result = PyObject_CallMethod (py_bp, stop_func, NULL);
@@ -783,6 +750,10 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
       else
 	gdbpy_print_stack ();
     }
+
+  if (bp_obj->is_finish_bp)
+    bpfinishpy_post_stop_hook (bp_obj);
+
   do_cleanups (cleanup);
 
   return stop;
@@ -845,6 +816,7 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->number = bp->number;
       newbp->bp = bp;
       newbp->bp->py_bp_object = newbp;
+      newbp->is_finish_bp = 0;
       Py_INCREF (newbp);
       ++bppy_live;
     }
@@ -1006,7 +978,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..4072675
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,453 @@
+/* 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.  */
+  breakpoint_object py_bp;
+  /* gdb.Type object of the value return by the breakpointed function.
+     May be NULL if no debug information was available or return type
+     was VOID.  */
+  PyObject *return_type;
+  /* gdb.Type object of the function finished by this breakpoint.  Will be
+     NULL if return_type is NULL.  */
+  PyObject *function_type;
+  /* When stopped at this FinishBreakpoint, gdb.Value object returned by
+     the function; Py_None if the value is not computable; NULL if GDB is
+     not stopped at a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* 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;
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* Deallocate FinishBreakpoint object.  */
+
+static void
+bpfinishpy_dealloc (PyObject *self)
+{
+  struct finish_breakpoint_object *self_bpfinish =
+        (struct finish_breakpoint_object *) self;
+
+  Py_XDECREF (self_bpfinish->function_type);
+  Py_XDECREF (self_bpfinish->return_type);
+  Py_XDECREF (self_bpfinish->return_value);
+}
+
+/* Triggered when gdbpy_should_stop is about to execute the `stop' callback
+   of the gdb.FinishBreakpoint object BP_OBJ.  Will compute and cache the
+   `return_value', if possible.  */
+
+void
+bpfinishpy_pre_stop_hook (struct breakpoint_object *bp_obj)
+{
+  struct finish_breakpoint_object *self_finishbp =
+        (struct finish_breakpoint_object *) bp_obj;
+  volatile struct gdb_exception except;
+
+  /* Can compute return_value only once.  */
+  gdb_assert (!self_finishbp->return_value);
+
+  if (!self_finishbp->return_type)
+    return;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      struct value *ret =
+          get_return_value (type_object_to_type (self_finishbp->function_type),
+                            type_object_to_type (self_finishbp->return_type));
+
+      if (ret)
+        {
+          self_finishbp->return_value = value_to_value_object (ret);
+          if (!self_finishbp->return_value)
+              gdbpy_print_stack ();
+        }
+      else
+        {
+          Py_INCREF (Py_None);
+          self_finishbp->return_value = Py_None;
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      gdbpy_print_stack ();
+    }
+}
+
+/* Triggered when gdbpy_should_stop has triggered the `stop' callback
+   of the gdb.FinishBreakpoint object BP_OBJ.  */
+
+void
+bpfinishpy_post_stop_hook (struct breakpoint_object *bp_obj)
+{
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Can't delete it here, but it will be removed at the next stop.  */
+      disable_breakpoint (bp_obj->bp);
+      gdb_assert (bp_obj->bp->disposition == disp_del);
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      gdbpy_print_stack ();
+    }
+}
+
+/* Python function to create a new breakpoint.  */
+
+static int
+bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+  static char *keywords[] = { "frame", "internal", NULL };
+  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 = NULL;
+  struct frame_id frame_id;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs, "|OO", keywords,
+                                    &frame_obj, &internal))
+    return -1;
+
+  /* Default to gdb.newest_frame if necessary.  */
+  if (!frame_obj)
+    frame_obj = gdbpy_newest_frame (NULL, NULL);
+  else
+    Py_INCREF (frame_obj);
+
+  frame = frame_object_to_frame_info (frame_obj);
+  Py_DECREF (frame_obj);
+
+  if (frame == NULL)
+    goto invalid_frame;
+  
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      prev_frame = get_prev_frame (frame);
+      if (prev_frame == 0)
+        {
+          PyErr_SetString (PyExc_ValueError, _("\"FinishBreakpoint\" not "   \
+                                               "meaningful in the outermost "\
+                                               "frame."));
+        }
+      else if (get_frame_type (prev_frame) == DUMMY_FRAME)
+        {
+          PyErr_SetString (PyExc_ValueError, _("\"FinishBreakpoint\" cannot "\
+                                               "be set on a dummy frame."));
+        }
+      else
+        {
+          frame_id = get_frame_id (prev_frame);
+          if (frame_id_eq (frame_id, null_frame_id))
+            PyErr_SetString (PyExc_ValueError,
+                             _("Invalid ID for the `frame' object."));
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      return -1;
+    }
+  else if (PyErr_Occurred ())
+    return -1;
+
+  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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  self_bpfinish->return_type = NULL;
+  self_bpfinish->function_type = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (get_frame_pc_if_available (frame, &pc))
+        {
+          function = find_pc_function (pc);
+          if (function != NULL)
+            {
+              struct type *ret_type =
+                  TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+              /* Remember only non-void return types.  */
+              if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
+                {
+                  /* Ignore Python errors at this stage.  */
+                  self_bpfinish->return_type = type_to_type_object (ret_type);
+                  PyErr_Clear ();
+                  self_bpfinish->function_type =
+                      type_to_type_object (SYMBOL_TYPE (function));
+                  PyErr_Clear ();
+                }
+            }
+        }
+    }
+  if (except.reason < 0
+      || !self_bpfinish->return_type || !self_bpfinish->function_type)
+    {
+      /* Won't be able to compute return value.  */
+      Py_XDECREF (self_bpfinish->return_type);
+      Py_XDECREF (self_bpfinish->function_type);
+
+      self_bpfinish->return_type = NULL;
+      self_bpfinish->function_type = NULL;
+    }
+
+  bppy_pending_object = &self_bpfinish->py_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, -1,
+                         0,
+                         1 /*temp_flag*/,
+                         bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         &bkpt_breakpoint_ops,
+                         0, 1, internal_bp);
+    }
+  GDB_PY_SET_HANDLE_EXCEPTION (except);
+  
+  self_bpfinish->py_bp.bp->frame_id = frame_id;
+  self_bpfinish->py_bp.is_finish_bp = 1;
+  
+  /* Bind the breakpoint with the current program space.  */
+  self_bpfinish->py_bp.bp->pspace = current_program_space;
+
+  return 0;
+  
+ invalid_frame:
+  PyErr_SetString (PyExc_ValueError, 
+                   _("Invalid ID for the `frame' object."));
+  return -1;
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack.  Triggers the method OUT_OF_SCOPE if implemented,
+   then delete the breakpoint.  */
+
+static void
+bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
+{
+  volatile struct gdb_exception except;
+  breakpoint_object *bp_obj = (breakpoint_object *) bpfinish_obj;
+  PyObject *py_obj = (PyObject *) bp_obj;
+
+  if (bpfinish_obj->py_bp.bp->enable_state == bp_enabled
+      && PyObject_HasAttrString (py_obj, outofscope_func))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+
+  delete_breakpoint (bpfinish_obj->py_bp.bp);
+}
+
+/* Callback for `bpfinishpy_detect_out_scope'.  Triggers Python's
+   `B->out_of_scope' function if B is a FinishBreakpoint out of its scope.  */
+
+static int
+bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
+{
+  volatile struct gdb_exception except;
+  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 ();
+  
+  /* Trigger out_of_scope if this is a FinishBreakpoint and its frame is
+     not anymore in the current callstack.  */
+  if (py_bp != NULL && b->py_bp_object->is_finish_bp)
+    {
+      struct finish_breakpoint_object *finish_bp =
+          (struct finish_breakpoint_object *) py_bp;
+
+      /* Check scope if not currently stopped at the FinishBreakpoint.  */
+      if (b != bp_stopped)
+        {
+          TRY_CATCH (except, RETURN_MASK_ALL)
+            {
+              if (b->pspace == current_inferior ()->pspace
+                  && (!target_has_registers
+                      || frame_find_by_id (b->frame_id) == NULL))
+                bpfinishpy_out_of_scope (finish_bp);
+            }
+          if (except.reason < 0)
+            {
+              gdbpy_convert_exception (except);
+              gdbpy_print_stack ();
+            }
+        }
+    }
+
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run
+   out of the scope of any FinishBreakpoint before it has been hit.  */
+
+static void
+bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
+{
+  struct cleanup *cleanup = ensure_python_env (get_current_arch (),
+                                               current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
+                            bs == NULL ? NULL : bs->breakpoint_at);
+
+  do_cleanups (cleanup);
+}
+
+/* Attached to `exit' notifications, triggers all the necessary out of
+   scope notifications.  */
+
+static void
+bpfinishpy_handle_exit (struct inferior *inf)
+{
+  struct cleanup *cleanup = ensure_python_env (target_gdbarch,
+                                               current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
+
+  do_cleanups (cleanup);
+}
+
+/* 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 PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "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*/
+  bpfinishpy_dealloc,             /*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 20064ca..c334f63 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);
@@ -106,7 +107,7 @@ frapy_is_valid (PyObject *self, PyObject *args)
 
   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      frame = frame_object_to_frame_info ((frame_object *) self);
+      frame = frame_object_to_frame_info (self);
     }
   GDB_PY_HANDLE_EXCEPTION (except);
 
@@ -130,7 +131,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);
     }
@@ -159,7 +160,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);
     }
@@ -180,7 +181,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);
 
@@ -201,7 +202,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);
     }
@@ -222,7 +223,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);
@@ -263,7 +264,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));
     }
@@ -330,7 +331,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)
@@ -359,7 +360,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)
@@ -388,7 +389,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);
@@ -444,7 +445,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);
@@ -472,7 +473,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);
     }
@@ -487,12 +488,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 ef39d5d..1ba7133 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -120,9 +120,50 @@ 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;
+
+  /* 1 is this is a FinishBreakpoint object, 0 otherwise.  */
+  int is_finish_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
 {
@@ -188,6 +229,7 @@ struct value *convert_value_from_python (PyObject *obj);
 struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
+struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);
 
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
@@ -202,6 +244,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);
@@ -275,6 +318,9 @@ PyObject *gdbpy_get_varobj_pretty_printer (struct value *value);
 char *gdbpy_get_display_hint (PyObject *printer);
 PyObject *gdbpy_default_visualizer (PyObject *self, PyObject *args);
 
+void bpfinishpy_pre_stop_hook (struct breakpoint_object *bp_obj);
+void bpfinishpy_post_stop_hook (struct breakpoint_object *bp_obj);
+
 extern PyObject *gdbpy_doc_cst;
 extern PyObject *gdbpy_children_cst;
 extern PyObject *gdbpy_to_string_cst;
diff --git a/gdb/python/python.c b/gdb/python/python.c
index b0b9a9c..f3fdf9a 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1247,6 +1247,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.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index e0dd087..0e3adbd 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.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..7cdbead
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,97 @@
+/* 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>
+#include <stdlib.h>
+#include <unistd.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);
+}
+
+void
+test_exec_exit (int do_exit)
+{
+  if (do_exit)
+    exit (0);
+  else
+    execl ("/bin/echo", "echo", "-1", (char *)0);
+}
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  getpid ();
+
+  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.  */
+
+  test_exec_exit (1);
+
+  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..a1aeaab
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,237 @@
+# 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 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_breakpoint "increase_1"
+gdb_test "continue" "Breakpoint .*at.*" "continue to the function to finish"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp_default = gdb.FinishBreakpoint ()" \
+         "Temporary breakpoint.*" "set FinishBreakpoint with default frame value"
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval ('a'), gdb.newest_frame ())" \
+         "Temporary breakpoint.*" "set FinishBreakpoint"
+gdb_test "python print finishbp.return_value" "None.*" \
+         "check return_value at init"
+
+# check normal bp hit
+
+gdb_test "continue" "MyFinishBreakpoint stop with.*return_value is: -5.*#0.*increase.*" \
+         "check MyFinishBreakpoint hit"
+gdb_test "python print finishbp.return_value" "-5.*" "check return_value"
+
+gdb_test "python print finishbp_default.hit_count" "1.*" "check finishBP on default frame has been hit"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer normal hit"
+
+# check FinishBreakpoint in main no allowed
+
+gdb_test "finish" "main.*" "return to main()"
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         "ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \
+         "check FinishBP not allowed in main"
+
+#
+# Test FinishBreakpoint with no debug symbol 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+set cond_line [gdb_get_line_number "Condition Break."]
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+gdb_breakpoint "getpid" {temporary}
+gdb_test "continue" "Temporary breakpoint .*in getpid.*" "continue to getpid"
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "continue" "SimpleFinishBreakpoint stop.*" "check FinishBreakpoint hit"
+gdb_test "python print finishBP.return_value" "None" "check return value without debug symbol"
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "python finishbp = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" \
+         "check FinishBP out of scope notification"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy 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_1(i,8)" "Breakpoint .* at .*" \
+         "set a conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" \
+         "set FinishBP in a breakpoint condition"
+gdb_test "continue" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+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"
+
+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 explicit inferior function call
+#
+
+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
+}
+
+# return address in dummy frame
+
+gdb_test "python TestExplicitBreakpoint('increase_1')" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase_1(&i)" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+
+# return address in normal frame
+
+delete_breakpoints
+gdb_test "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase(&i)" \
+         "SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*The program being debugged stopped while in a function called from GDB.*" \
+         "FinishBP stop at during explicit function call"
+
+
+#
+# Test FinishBreakpoint when inferior exits
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}
+
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
+
+#
+# Test FinishBreakpoint when inferior execs
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}     
+
+gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
+gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ 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..dea2a73
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,89 @@
+# 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 MyFinishBreakpoint (gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		print "MyFinishBreakpoint init"
+		self.val = val
+		
+	def stop(self):
+		print "MyFinishBreakpoint stop with %d" % int (self.val.dereference ())
+		print "return_value is: %d" % int (self.return_value)
+		gdb.execute ("where 1")
+		return True
+	
+	def out_of_scope(self):
+		print "MyFinishBreakpoint out of scope"
+
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__ (self, spec="test_1", internal=1)
+        self.silent = True
+        self.count = 0
+        print "TestBreakpoint init"
+        
+    def stop(self):
+    	self.count += 1
+    	try:
+        	TestFinishBreakpoint (gdb.newest_frame (), self.count)
+        except ValueError as e:
+        	print e
+        return False
+
+class TestFinishBreakpoint (gdb.FinishBreakpoint):
+    def __init__ (self, frame, count):
+    	self.count = count
+        gdb.FinishBreakpoint.__init__ (self, frame, internal=1)
+        
+        
+    def stop(self):
+        print "-->", self.number
+        if (self.count == 3):
+            print "test stop: %d" % self.count
+            return True
+        else:
+            print "test don't stop: %d" % self.count
+            return False 
+        
+    
+    def out_of_scope(self):
+        print "test didn't finish: %d" % self.count
+
+class TestExplicitBreakpoint(gdb.Breakpoint):
+	def stop(self):
+		try:
+			SimpleFinishBreakpoint (gdb.newest_frame ())
+		except ValueError as e:
+			print e
+		return False
+
+class SimpleFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		
+		print "SimpleFinishBreakpoint init"
+		
+	def stop(self):
+		print "SimpleFinishBreakpoint stop" 
+		return True
+	
+	def out_of_scope(self):
+		print "SimpleFinishBreakpoint out of scope"
+
+print "Python script importedd"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.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-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
new file mode 100644
index 0000000..fae798c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -0,0 +1,65 @@
+# 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-breakpoint2"
+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
+}
+
+#
+# Check FinishBreakpoints against C++ exceptions
+#
+
+gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_breakpoint "throw_exception_1"
+gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
+
+gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.py b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
new file mode 100644
index 0000000..0fb6955
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
@@ -0,0 +1,33 @@
+# 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 ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__ (self, frame, internal=1)
+        self.silent = True;
+        print "init ExceptionFinishBreakpoint"
+        
+    def stop(self):
+        print "stopped at ExceptionFinishBreakpoint"
+        return True 
+    
+    def out_of_scope(self):
+        print "exception did not finish ..."
+
+
+print "Python script imported"
-- 
1.7.6.4


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

* Re: [RFC] Python Finish Breakpoints
  2011-12-07 13:35                                                             ` Kevin Pouget
@ 2011-12-08 15:30                                                               ` Tom Tromey
  2011-12-08 16:10                                                                 ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Tom Tromey @ 2011-12-08 15:30 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches

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

Kevin> and we're .... not ready!
Kevin> there are some regressions this morning, certainly due to your
Kevin> ambiguous linespec patch!

Kevin> first one is quite obvious:

Kevin> a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
Kevin> -if ![runto "getpid"] then {
Kevin> -    fail "Cannot run to getpid."
Kevin> +if ![runto_main] then {
Kevin> +    fail "Cannot run to main."     return 0 }
Kevin> +gdb_breakpoint "getpid" {temporary}
Kevin> +gdb_test "continue" "Temporary breakpoint .*in getpid.*" "continue to getpid"
Kevin> +

Kevin> "break getpid" used to work before actually starting the inferior, but
Kevin> now it's unresolved

This test assumes that there is no debuginfo for getpid -- but in my
case, I have glibc debuginfo installed.  This isn't unheard of for gdb
developers :)

The test would be more robust if you put a new function in a new file
and arranged for that file to be compiled without debuginfo.

Sorry I didn't notice this before.

Kevin> bpfinishpy_out_of_scope can't be triggered anymore because you set
Kevin> b-> pspace to NULL. I hesitated about how to fix it, but I think that
Kevin> FinishBP have to be bound to the pspace in which they were set, so I
Kevin> added this line:

It seems to me that one of these breakpoints has to be thread-specific
as well.  Maybe the code to set b->pspace should notice thread-specific
breakpoints and allow pspace to be set there.

I'd rather this be done in breakpoint.c than have other modules modify
struct breakpoint directly.

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2011-12-08 15:30                                                               ` Tom Tromey
@ 2011-12-08 16:10                                                                 ` Kevin Pouget
  2011-12-08 18:08                                                                   ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-12-08 16:10 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Thu, Dec 8, 2011 at 3:56 PM, Tom Tromey <tromey@redhat.com> wrote:
>>>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
>
> Kevin> and we're .... not ready!
> Kevin> there are some regressions this morning, certainly due to your
> Kevin> ambiguous linespec patch!
>
> Kevin> first one is quite obvious:
>
> Kevin> a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
> Kevin> -if ![runto "getpid"] then {
> Kevin> -    fail "Cannot run to getpid."
> Kevin> +if ![runto_main] then {
> Kevin> +    fail "Cannot run to main."     return 0 }
> Kevin> +gdb_breakpoint "getpid" {temporary}
> Kevin> +gdb_test "continue" "Temporary breakpoint .*in getpid.*" "continue to getpid"
> Kevin> +
>
> Kevin> "break getpid" used to work before actually starting the inferior, but
> Kevin> now it's unresolved
>
> This test assumes that there is no debuginfo for getpid -- but in my
> case, I have glibc debuginfo installed.  This isn't unheard of for gdb
> developers :)
>
> The test would be more robust if you put a new function in a new file
> and arranged for that file to be compiled without debuginfo.
>
> Sorry I didn't notice this before.

I'll work on that, thanks for noticing it

> Kevin> bpfinishpy_out_of_scope can't be triggered anymore because you set
> Kevin> b-> pspace to NULL. I hesitated about how to fix it, but I think that
> Kevin> FinishBP have to be bound to the pspace in which they were set, so I
> Kevin> added this line:
>
> It seems to me that one of these breakpoints has to be thread-specific
> as well.  Maybe the code to set b->pspace should notice thread-specific
> breakpoints and allow pspace to be set there.
>
> I'd rather this be done in breakpoint.c than have other modules modify
> struct breakpoint directly.
>
> Tom

you're right about thread-specific, but I'm not sure how to enforce
it. Currently, FinishBreakpoint only accepts a frame as parameter, and
a frame is only bound to a pspace, not a thread ...

My code
> self_bpfinish->py_bp.bp->pspace = current_program_space;
was wrong, it should rather be
> pspace = get_frame_program_space (prev_frame);
and this also implies that we can't really defer this setting to
another place ...

Maybe we can state in the documentation that FinishBreakpoint will be
created by default in the selected thread, and add another parameter
in the constructor to specify another thread?

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

* Re: [RFC] Python Finish Breakpoints
  2011-12-08 16:10                                                                 ` Kevin Pouget
@ 2011-12-08 18:08                                                                   ` Kevin Pouget
  2011-12-09  9:53                                                                     ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-12-08 18:08 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

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

On Thu, Dec 8, 2011 at 4:33 PM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> On Thu, Dec 8, 2011 at 3:56 PM, Tom Tromey <tromey@redhat.com> wrote:
>>>>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
>>
>> Kevin> and we're .... not ready!
>> Kevin> there are some regressions this morning, certainly due to your
>> Kevin> ambiguous linespec patch!
>>
>> Kevin> first one is quite obvious:
>>
>> Kevin> a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
>> Kevin> -if ![runto "getpid"] then {
>> Kevin> -    fail "Cannot run to getpid."
>> Kevin> +if ![runto_main] then {
>> Kevin> +    fail "Cannot run to main."     return 0 }
>> Kevin> +gdb_breakpoint "getpid" {temporary}
>> Kevin> +gdb_test "continue" "Temporary breakpoint .*in getpid.*" "continue to getpid"
>> Kevin> +
>>
>> Kevin> "break getpid" used to work before actually starting the inferior, but
>> Kevin> now it's unresolved
>>
>> This test assumes that there is no debuginfo for getpid -- but in my
>> case, I have glibc debuginfo installed.  This isn't unheard of for gdb
>> developers :)
>>
>> The test would be more robust if you put a new function in a new file
>> and arranged for that file to be compiled without debuginfo.
>>
>> Sorry I didn't notice this before.
>
> I'll work on that, thanks for noticing it
>
>> Kevin> bpfinishpy_out_of_scope can't be triggered anymore because you set
>> Kevin> b-> pspace to NULL. I hesitated about how to fix it, but I think that
>> Kevin> FinishBP have to be bound to the pspace in which they were set, so I
>> Kevin> added this line:
>>
>> It seems to me that one of these breakpoints has to be thread-specific
>> as well.  Maybe the code to set b->pspace should notice thread-specific
>> breakpoints and allow pspace to be set there.
>>
>> I'd rather this be done in breakpoint.c than have other modules modify
>> struct breakpoint directly.
>>
>> Tom
>
> you're right about thread-specific, but I'm not sure how to enforce
> it. Currently, FinishBreakpoint only accepts a frame as parameter, and
> a frame is only bound to a pspace, not a thread ...
>
> My code
>> self_bpfinish->py_bp.bp->pspace = current_program_space;
> was wrong, it should rather be
>> pspace = get_frame_program_space (prev_frame);
> and this also implies that we can't really defer this setting to
> another place ...
>
> Maybe we can state in the documentation that FinishBreakpoint will be
> created by default in the selected thread, and add another parameter
> in the constructor to specify another thread?

so it was not possible to allow the user to specify the thread as I
suggest, for the reason I mentioned two lines above ... :(
I added this line to the doc to express it:

> Finish breakpoints are thread specific and must be create with the right
> thread selected.


cordially,

Kevin

(I'll send getpid fix tomorrow)

[-- Attachment #2: 0001-Introduce-gdb.FinishBreakpoint.patch --]
[-- Type: text/x-patch, Size: 57757 bytes --]

From bfb6a2c9eaf06418198f0edb3bb2aa8072034f45 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Tue, 20 Sep 2011 13:59:23 +0200
Subject: [PATCH]  Introduce gdb.FinishBreakpoint

---
 gdb/Makefile.in                                    |    6 +
 gdb/NEWS                                           |    4 +
 gdb/breakpoint.c                                   |    3 +-
 gdb/breakpoint.h                                   |    2 +-
 gdb/doc/gdb.texinfo                                |   53 +++
 gdb/infcmd.c                                       |   39 ++-
 gdb/inferior.h                                     |    3 +
 gdb/python/py-breakpoint.c                         |   48 +--
 gdb/python/py-finishbreakpoint.c                   |  462 ++++++++++++++++++++
 gdb/python/py-frame.c                              |   32 +-
 gdb/python/python-internal.h                       |   50 ++-
 gdb/python/python.c                                |    1 +
 gdb/testsuite/gdb.python/py-breakpoint.exp         |    7 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.c    |   97 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp  |  237 ++++++++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py   |   89 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc  |   59 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   65 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.py  |   33 ++
 19 files changed, 1223 insertions(+), 67 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 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
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.py

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b71db33..d8d1b40 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,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 \
@@ -318,6 +319,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 \
@@ -2109,6 +2111,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/NEWS b/gdb/NEWS
index 42782ce..b755605 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -57,6 +57,10 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".
 
+  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
+     of a function.  This class is based on the "finish" command
+     available in the CLI. 
+
   ** Type objects for struct and union types now allow access to
      the fields using standard Python dictionary (mapping) methods.
      For example, "some_type['myfield']" now works, as does
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index d9d5bbe..bfd0ca7 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -7933,7 +7933,8 @@ create_breakpoint (struct gdbarch *gdbarch,
       b->disposition = tempflag ? disp_del : disp_donttouch;
       b->condition_not_parsed = 1;
       b->enable_state = enabled ? bp_enabled : bp_disabled;
-      if (type_wanted != bp_breakpoint && type_wanted != bp_hardware_breakpoint)
+      if ((type_wanted != bp_breakpoint
+           && type_wanted != bp_hardware_breakpoint) || thread != -1)
 	b->pspace = current_program_space;
 
       install_breakpoint (internal, b, 0);
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index ddf1881..d22e564 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -569,7 +569,7 @@ struct breakpoint
 
     /* The program space used to set the breakpoint.  This is only set
        for breakpoints which are specific to a program space; for
-       ordinary breakpoints this is NULL.  */
+       non-thread-specific ordinary breakpoints this is NULL.  */
     struct program_space *pspace;
 
     /* String we used to set the breakpoint (malloc'd).  */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 50c299e..950257f 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21501,6 +21501,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Symbol Tables In Python::     Python representation of symbol tables.
 * Lazy Strings In Python::      Python representation of lazy strings.
 * Breakpoints In Python::       Manipulating breakpoints using Python.
+* Finish Breakpoints in Python:: Setting Breakpoints on function return
+                                using Python.
 @end menu
 
 @node Basic Python
@@ -24335,6 +24337,57 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@node Finish Breakpoints in Python
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A finish breakpoint is a temporary breakpoint set at the return address of
+a frame, based on the @code{finish} command.  @code{gdb.FinishBreakpoint}
+extends @code{gdb.Breakpoint}.  The underlying breakpoint will be disabled 
+and deleted when the execution will run out of the breakpoint scope (i.e.@: 
+@code{Breakpoint.stop} or @code{FinishBreakpoint.out_of_scope} triggered).
+Finish breakpoints are thread specific and must be create with the right 
+thread selected.  
+ 
+@defun FinishBreakpoint.__init__ (@r{[}frame@r{]} @r{[}, internal@r{]})
+Create a finish breakpoint at the return address of the @code{gdb.Frame}
+object @var{frame}.  If @var{frame} is not provided, this defaults to the
+newest frame.  The optional @var{internal} argument allows the breakpoint to
+become invisible to the user.  @xref{Breakpoints In Python}, for further 
+details about this argument.
+@end defun
+
+@defun FinishBreakpoint.out_of_scope (self)
+In some circumstances (e.g.@: @code{longjmp}, C@t{++} exceptions, @value{GDBN} 
+@code{return} command, @dots{}), a function may not properly terminate, and
+thus never hit the finish breakpoint.  When @value{GDBN} notices such a
+situation, the @code{out_of_scope} callback will be triggered.
+
+You may want to sub-class @code{gdb.FinishBreakpoint} and override this
+method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+    def stop (self):
+        print "normal finish"
+        return True
+    
+    def out_of_scope ():
+        print "abnormal finish"
+@end smallexample 
+@end defun
+
+@defvar FinishBreakpoint.return_value
+When @value{GDBN} is stopped at a finish breakpoint and the frame 
+used to build the @code{gdb.FinishBreakpoint} object had debug symbols, this
+attribute will contain a @code{gdb.Value} object corresponding to the return
+value of the function.  The value will be @code{None} if the function return 
+type is @code{void} or if the return value was not computable.  This attribute
+is not writable.
+@end defvar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 5d94cd4..cffa194 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1414,16 +1414,26 @@ 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.  */
+/* Return the value of the result of a function 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 regcache *stop_regs = stop_registers;
+  struct gdbarch *gdbarch;
   struct value *value;
   struct ui_out *uiout = current_uiout;
+  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
+
+  /* If stop_registers were not saved, use the current registers.  */
+  if (!stop_regs)
+    {
+      stop_regs = regcache_dup (get_current_regcache ());
+      cleanup = make_cleanup_regcache_xfree (stop_regs);
+    }
+
+  gdbarch = get_regcache_arch (stop_regs);
 
   CHECK_TYPEDEF (value_type);
   gdb_assert (TYPE_CODE (value_type) != TYPE_CODE_VOID);
@@ -1442,7 +1452,7 @@ print_return_value (struct type *func_type, struct type *value_type)
     case RETURN_VALUE_ABI_RETURNS_ADDRESS:
     case RETURN_VALUE_ABI_PRESERVES_ADDRESS:
       value = allocate_value (value_type);
-      gdbarch_return_value (gdbarch, func_type, value_type, stop_registers,
+      gdbarch_return_value (gdbarch, func_type, value_type, stop_regs,
 			    value_contents_raw (value), NULL);
       break;
     case RETURN_VALUE_STRUCT_CONVENTION:
@@ -1452,6 +1462,21 @@ print_return_value (struct type *func_type, struct type *value_type)
       internal_error (__FILE__, __LINE__, _("bad switch"));
     }
 
+  do_cleanups (cleanup);
+
+  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;
+  struct ui_out *uiout = current_uiout;
+
   if (value)
     {
       struct value_print_options opts;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..f198696 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -269,6 +269,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/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 11d60fe..64ef45f 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -31,52 +31,16 @@
 #include "arch-utils.h"
 #include "language.h"
 
-static 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
 {
@@ -762,6 +726,9 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
   struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
   struct cleanup *cleanup = ensure_python_env (garch, current_language);
 
+  if (bp_obj->is_finish_bp)
+    bpfinishpy_pre_stop_hook (bp_obj);
+
   if (PyObject_HasAttrString (py_bp, stop_func))
     {
       PyObject *result = PyObject_CallMethod (py_bp, stop_func, NULL);
@@ -783,6 +750,10 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
       else
 	gdbpy_print_stack ();
     }
+
+  if (bp_obj->is_finish_bp)
+    bpfinishpy_post_stop_hook (bp_obj);
+
   do_cleanups (cleanup);
 
   return stop;
@@ -845,6 +816,7 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->number = bp->number;
       newbp->bp = bp;
       newbp->bp->py_bp_object = newbp;
+      newbp->is_finish_bp = 0;
       Py_INCREF (newbp);
       ++bppy_live;
     }
@@ -1006,7 +978,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..a2d8165
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,462 @@
+/* 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.  */
+  breakpoint_object py_bp;
+  /* gdb.Type object of the value return by the breakpointed function.
+     May be NULL if no debug information was available or return type
+     was VOID.  */
+  PyObject *return_type;
+  /* gdb.Type object of the function finished by this breakpoint.  Will be
+     NULL if return_type is NULL.  */
+  PyObject *function_type;
+  /* When stopped at this FinishBreakpoint, gdb.Value object returned by
+     the function; Py_None if the value is not computable; NULL if GDB is
+     not stopped at a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* 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;
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* Deallocate FinishBreakpoint object.  */
+
+static void
+bpfinishpy_dealloc (PyObject *self)
+{
+  struct finish_breakpoint_object *self_bpfinish =
+        (struct finish_breakpoint_object *) self;
+
+  Py_XDECREF (self_bpfinish->function_type);
+  Py_XDECREF (self_bpfinish->return_type);
+  Py_XDECREF (self_bpfinish->return_value);
+}
+
+/* Triggered when gdbpy_should_stop is about to execute the `stop' callback
+   of the gdb.FinishBreakpoint object BP_OBJ.  Will compute and cache the
+   `return_value', if possible.  */
+
+void
+bpfinishpy_pre_stop_hook (struct breakpoint_object *bp_obj)
+{
+  struct finish_breakpoint_object *self_finishbp =
+        (struct finish_breakpoint_object *) bp_obj;
+  volatile struct gdb_exception except;
+
+  /* Can compute return_value only once.  */
+  gdb_assert (!self_finishbp->return_value);
+
+  if (!self_finishbp->return_type)
+    return;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      struct value *ret =
+          get_return_value (type_object_to_type (self_finishbp->function_type),
+                            type_object_to_type (self_finishbp->return_type));
+
+      if (ret)
+        {
+          self_finishbp->return_value = value_to_value_object (ret);
+          if (!self_finishbp->return_value)
+              gdbpy_print_stack ();
+        }
+      else
+        {
+          Py_INCREF (Py_None);
+          self_finishbp->return_value = Py_None;
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      gdbpy_print_stack ();
+    }
+}
+
+/* Triggered when gdbpy_should_stop has triggered the `stop' callback
+   of the gdb.FinishBreakpoint object BP_OBJ.  */
+
+void
+bpfinishpy_post_stop_hook (struct breakpoint_object *bp_obj)
+{
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Can't delete it here, but it will be removed at the next stop.  */
+      disable_breakpoint (bp_obj->bp);
+      gdb_assert (bp_obj->bp->disposition == disp_del);
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      gdbpy_print_stack ();
+    }
+}
+
+/* Python function to create a new breakpoint.  */
+
+static int
+bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+  static char *keywords[] = { "frame", "internal", NULL };
+  struct finish_breakpoint_object *self_bpfinish =
+      (struct finish_breakpoint_object *) self;
+  int type = bp_breakpoint;
+  PyObject *frame_obj = NULL;
+  int thread;
+  struct frame_info *frame, *prev_frame = NULL;
+  struct frame_id frame_id;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs, "|OO", keywords,
+                                    &frame_obj, &internal))
+    return -1;
+
+  /* Default frame to gdb.newest_frame if necessary.  */
+  if (!frame_obj)
+    frame_obj = gdbpy_newest_frame (NULL, NULL);
+  else
+    Py_INCREF (frame_obj);
+
+  frame = frame_object_to_frame_info (frame_obj);
+  Py_DECREF (frame_obj);
+
+  if (frame == NULL)
+    goto invalid_frame;
+  
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      prev_frame = get_prev_frame (frame);
+      if (prev_frame == 0)
+        {
+          PyErr_SetString (PyExc_ValueError, _("\"FinishBreakpoint\" not "   \
+                                               "meaningful in the outermost "\
+                                               "frame."));
+        }
+      else if (get_frame_type (prev_frame) == DUMMY_FRAME)
+        {
+          PyErr_SetString (PyExc_ValueError, _("\"FinishBreakpoint\" cannot "\
+                                               "be set on a dummy frame."));
+        }
+      else
+        {
+          frame_id = get_frame_id (prev_frame);
+          if (frame_id_eq (frame_id, null_frame_id))
+            PyErr_SetString (PyExc_ValueError,
+                             _("Invalid ID for the `frame' object."));
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      return -1;
+    }
+  else if (PyErr_Occurred ())
+    return -1;
+
+  thread = pid_to_thread_id (inferior_ptid);
+  if (thread == 0)
+    {
+      PyErr_SetString (PyExc_ValueError,
+                       _("No thread currently selected."));
+      return -1;
+    }
+
+  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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  self_bpfinish->return_type = NULL;
+  self_bpfinish->function_type = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (get_frame_pc_if_available (frame, &pc))
+        {
+          function = find_pc_function (pc);
+          if (function != NULL)
+            {
+              struct type *ret_type =
+                  TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+              /* Remember only non-void return types.  */
+              if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
+                {
+                  /* Ignore Python errors at this stage.  */
+                  self_bpfinish->return_type = type_to_type_object (ret_type);
+                  PyErr_Clear ();
+                  self_bpfinish->function_type =
+                      type_to_type_object (SYMBOL_TYPE (function));
+                  PyErr_Clear ();
+                }
+            }
+        }
+    }
+  if (except.reason < 0
+      || !self_bpfinish->return_type || !self_bpfinish->function_type)
+    {
+      /* Won't be able to compute return value.  */
+      Py_XDECREF (self_bpfinish->return_type);
+      Py_XDECREF (self_bpfinish->function_type);
+
+      self_bpfinish->return_type = NULL;
+      self_bpfinish->function_type = NULL;
+    }
+
+  bppy_pending_object = &self_bpfinish->py_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, thread,
+                         0,
+                         1 /*temp_flag*/,
+                         bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         &bkpt_breakpoint_ops,
+                         0, 1, internal_bp);
+    }
+  GDB_PY_SET_HANDLE_EXCEPTION (except);
+  
+  self_bpfinish->py_bp.bp->frame_id = frame_id;
+  self_bpfinish->py_bp.is_finish_bp = 1;
+  
+  /* Bind the breakpoint with the current program space.  */
+  self_bpfinish->py_bp.bp->pspace = current_program_space;
+
+  return 0;
+  
+ invalid_frame:
+  PyErr_SetString (PyExc_ValueError, 
+                   _("Invalid ID for the `frame' object."));
+  return -1;
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack.  Triggers the method OUT_OF_SCOPE if implemented,
+   then delete the breakpoint.  */
+
+static void
+bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
+{
+  volatile struct gdb_exception except;
+  breakpoint_object *bp_obj = (breakpoint_object *) bpfinish_obj;
+  PyObject *py_obj = (PyObject *) bp_obj;
+
+  if (bpfinish_obj->py_bp.bp->enable_state == bp_enabled
+      && PyObject_HasAttrString (py_obj, outofscope_func))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+
+  delete_breakpoint (bpfinish_obj->py_bp.bp);
+}
+
+/* Callback for `bpfinishpy_detect_out_scope'.  Triggers Python's
+   `B->out_of_scope' function if B is a FinishBreakpoint out of its scope.  */
+
+static int
+bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
+{
+  volatile struct gdb_exception except;
+  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 ();
+  
+  /* Trigger out_of_scope if this is a FinishBreakpoint and its frame is
+     not anymore in the current callstack.  */
+  if (py_bp != NULL && b->py_bp_object->is_finish_bp)
+    {
+      struct finish_breakpoint_object *finish_bp =
+          (struct finish_breakpoint_object *) py_bp;
+
+      /* Check scope if not currently stopped at the FinishBreakpoint.  */
+      if (b != bp_stopped)
+        {
+          TRY_CATCH (except, RETURN_MASK_ALL)
+            {
+              if (b->pspace == current_inferior ()->pspace
+                  && (!target_has_registers
+                      || frame_find_by_id (b->frame_id) == NULL))
+                bpfinishpy_out_of_scope (finish_bp);
+            }
+          if (except.reason < 0)
+            {
+              gdbpy_convert_exception (except);
+              gdbpy_print_stack ();
+            }
+        }
+    }
+
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run
+   out of the scope of any FinishBreakpoint before it has been hit.  */
+
+static void
+bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
+{
+  struct cleanup *cleanup = ensure_python_env (get_current_arch (),
+                                               current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
+                            bs == NULL ? NULL : bs->breakpoint_at);
+
+  do_cleanups (cleanup);
+}
+
+/* Attached to `exit' notifications, triggers all the necessary out of
+   scope notifications.  */
+
+static void
+bpfinishpy_handle_exit (struct inferior *inf)
+{
+  struct cleanup *cleanup = ensure_python_env (target_gdbarch,
+                                               current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
+
+  do_cleanups (cleanup);
+}
+
+/* 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 PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "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*/
+  bpfinishpy_dealloc,             /*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 20064ca..c334f63 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);
@@ -106,7 +107,7 @@ frapy_is_valid (PyObject *self, PyObject *args)
 
   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      frame = frame_object_to_frame_info ((frame_object *) self);
+      frame = frame_object_to_frame_info (self);
     }
   GDB_PY_HANDLE_EXCEPTION (except);
 
@@ -130,7 +131,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);
     }
@@ -159,7 +160,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);
     }
@@ -180,7 +181,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);
 
@@ -201,7 +202,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);
     }
@@ -222,7 +223,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);
@@ -263,7 +264,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));
     }
@@ -330,7 +331,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)
@@ -359,7 +360,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)
@@ -388,7 +389,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);
@@ -444,7 +445,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);
@@ -472,7 +473,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);
     }
@@ -487,12 +488,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 ef39d5d..1ba7133 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -120,9 +120,50 @@ 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;
+
+  /* 1 is this is a FinishBreakpoint object, 0 otherwise.  */
+  int is_finish_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
 {
@@ -188,6 +229,7 @@ struct value *convert_value_from_python (PyObject *obj);
 struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
+struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);
 
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
@@ -202,6 +244,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);
@@ -275,6 +318,9 @@ PyObject *gdbpy_get_varobj_pretty_printer (struct value *value);
 char *gdbpy_get_display_hint (PyObject *printer);
 PyObject *gdbpy_default_visualizer (PyObject *self, PyObject *args);
 
+void bpfinishpy_pre_stop_hook (struct breakpoint_object *bp_obj);
+void bpfinishpy_post_stop_hook (struct breakpoint_object *bp_obj);
+
 extern PyObject *gdbpy_doc_cst;
 extern PyObject *gdbpy_children_cst;
 extern PyObject *gdbpy_to_string_cst;
diff --git a/gdb/python/python.c b/gdb/python/python.c
index b0b9a9c..f3fdf9a 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1247,6 +1247,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.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index e0dd087..0e3adbd 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.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..7cdbead
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,97 @@
+/* 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>
+#include <stdlib.h>
+#include <unistd.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);
+}
+
+void
+test_exec_exit (int do_exit)
+{
+  if (do_exit)
+    exit (0);
+  else
+    execl ("/bin/echo", "echo", "-1", (char *)0);
+}
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  getpid ();
+
+  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.  */
+
+  test_exec_exit (1);
+
+  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..a1aeaab
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,237 @@
+# 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 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_breakpoint "increase_1"
+gdb_test "continue" "Breakpoint .*at.*" "continue to the function to finish"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp_default = gdb.FinishBreakpoint ()" \
+         "Temporary breakpoint.*" "set FinishBreakpoint with default frame value"
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval ('a'), gdb.newest_frame ())" \
+         "Temporary breakpoint.*" "set FinishBreakpoint"
+gdb_test "python print finishbp.return_value" "None.*" \
+         "check return_value at init"
+
+# check normal bp hit
+
+gdb_test "continue" "MyFinishBreakpoint stop with.*return_value is: -5.*#0.*increase.*" \
+         "check MyFinishBreakpoint hit"
+gdb_test "python print finishbp.return_value" "-5.*" "check return_value"
+
+gdb_test "python print finishbp_default.hit_count" "1.*" "check finishBP on default frame has been hit"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer normal hit"
+
+# check FinishBreakpoint in main no allowed
+
+gdb_test "finish" "main.*" "return to main()"
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         "ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \
+         "check FinishBP not allowed in main"
+
+#
+# Test FinishBreakpoint with no debug symbol 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+set cond_line [gdb_get_line_number "Condition Break."]
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+gdb_breakpoint "getpid" {temporary}
+gdb_test "continue" "Temporary breakpoint .*in getpid.*" "continue to getpid"
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "continue" "SimpleFinishBreakpoint stop.*" "check FinishBreakpoint hit"
+gdb_test "python print finishBP.return_value" "None" "check return value without debug symbol"
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $remote_python_file" "Python script imported.*" \
+         "import python scripts"
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "python finishbp = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" \
+         "check FinishBP out of scope notification"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy 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_1(i,8)" "Breakpoint .* at .*" \
+         "set a conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" \
+         "set FinishBP in a breakpoint condition"
+gdb_test "continue" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+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"
+
+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 explicit inferior function call
+#
+
+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
+}
+
+# return address in dummy frame
+
+gdb_test "python TestExplicitBreakpoint('increase_1')" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase_1(&i)" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+
+# return address in normal frame
+
+delete_breakpoints
+gdb_test "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase(&i)" \
+         "SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*The program being debugged stopped while in a function called from GDB.*" \
+         "FinishBP stop at during explicit function call"
+
+
+#
+# Test FinishBreakpoint when inferior exits
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}
+
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
+
+#
+# Test FinishBreakpoint when inferior execs
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}     
+
+gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
+gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ 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..dea2a73
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,89 @@
+# 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 MyFinishBreakpoint (gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		print "MyFinishBreakpoint init"
+		self.val = val
+		
+	def stop(self):
+		print "MyFinishBreakpoint stop with %d" % int (self.val.dereference ())
+		print "return_value is: %d" % int (self.return_value)
+		gdb.execute ("where 1")
+		return True
+	
+	def out_of_scope(self):
+		print "MyFinishBreakpoint out of scope"
+
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__ (self, spec="test_1", internal=1)
+        self.silent = True
+        self.count = 0
+        print "TestBreakpoint init"
+        
+    def stop(self):
+    	self.count += 1
+    	try:
+        	TestFinishBreakpoint (gdb.newest_frame (), self.count)
+        except ValueError as e:
+        	print e
+        return False
+
+class TestFinishBreakpoint (gdb.FinishBreakpoint):
+    def __init__ (self, frame, count):
+    	self.count = count
+        gdb.FinishBreakpoint.__init__ (self, frame, internal=1)
+        
+        
+    def stop(self):
+        print "-->", self.number
+        if (self.count == 3):
+            print "test stop: %d" % self.count
+            return True
+        else:
+            print "test don't stop: %d" % self.count
+            return False 
+        
+    
+    def out_of_scope(self):
+        print "test didn't finish: %d" % self.count
+
+class TestExplicitBreakpoint(gdb.Breakpoint):
+	def stop(self):
+		try:
+			SimpleFinishBreakpoint (gdb.newest_frame ())
+		except ValueError as e:
+			print e
+		return False
+
+class SimpleFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		
+		print "SimpleFinishBreakpoint init"
+		
+	def stop(self):
+		print "SimpleFinishBreakpoint stop" 
+		return True
+	
+	def out_of_scope(self):
+		print "SimpleFinishBreakpoint out of scope"
+
+print "Python script importedd"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.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-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
new file mode 100644
index 0000000..fae798c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -0,0 +1,65 @@
+# 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-breakpoint2"
+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
+}
+
+#
+# Check FinishBreakpoints against C++ exceptions
+#
+
+gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_breakpoint "throw_exception_1"
+gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
+
+gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.py b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
new file mode 100644
index 0000000..0fb6955
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
@@ -0,0 +1,33 @@
+# 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 ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__ (self, frame, internal=1)
+        self.silent = True;
+        print "init ExceptionFinishBreakpoint"
+        
+    def stop(self):
+        print "stopped at ExceptionFinishBreakpoint"
+        return True 
+    
+    def out_of_scope(self):
+        print "exception did not finish ..."
+
+
+print "Python script imported"
-- 
1.7.6.4


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

* Re: [RFC] Python Finish Breakpoints
  2011-12-08 18:08                                                                   ` Kevin Pouget
@ 2011-12-09  9:53                                                                     ` Kevin Pouget
  2011-12-18 19:22                                                                       ` Kevin Pouget
                                                                                         ` (2 more replies)
  0 siblings, 3 replies; 74+ messages in thread
From: Kevin Pouget @ 2011-12-09  9:53 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

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

On Thu, Dec 8, 2011 at 5:09 PM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> On Thu, Dec 8, 2011 at 4:33 PM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
>> On Thu, Dec 8, 2011 at 3:56 PM, Tom Tromey <tromey@redhat.com> wrote:
>>>>>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
>>>
>>> Kevin> and we're .... not ready!
>>> Kevin> there are some regressions this morning, certainly due to your
>>> Kevin> ambiguous linespec patch!
>>>
>>> Kevin> first one is quite obvious:
>>>
>>> Kevin> a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
>>> Kevin> -if ![runto "getpid"] then {
>>> Kevin> -    fail "Cannot run to getpid."
>>> Kevin> +if ![runto_main] then {
>>> Kevin> +    fail "Cannot run to main."     return 0 }
>>> Kevin> +gdb_breakpoint "getpid" {temporary}
>>> Kevin> +gdb_test "continue" "Temporary breakpoint .*in getpid.*" "continue to getpid"
>>> Kevin> +
>>>
>>> Kevin> "break getpid" used to work before actually starting the inferior, but
>>> Kevin> now it's unresolved
>>>
>>> This test assumes that there is no debuginfo for getpid -- but in my
>>> case, I have glibc debuginfo installed.  This isn't unheard of for gdb
>>> developers :)
>>>
>>> The test would be more robust if you put a new function in a new file
>>> and arranged for that file to be compiled without debuginfo.
>>>
>>> Sorry I didn't notice this before.
>>
>> I'll work on that, thanks for noticing it
>>
>>> Kevin> bpfinishpy_out_of_scope can't be triggered anymore because you set
>>> Kevin> b-> pspace to NULL. I hesitated about how to fix it, but I think that
>>> Kevin> FinishBP have to be bound to the pspace in which they were set, so I
>>> Kevin> added this line:
>>>
>>> It seems to me that one of these breakpoints has to be thread-specific
>>> as well.  Maybe the code to set b->pspace should notice thread-specific
>>> breakpoints and allow pspace to be set there.
>>>
>>> I'd rather this be done in breakpoint.c than have other modules modify
>>> struct breakpoint directly.
>>>
>>> Tom
>>
>> you're right about thread-specific, but I'm not sure how to enforce
>> it. Currently, FinishBreakpoint only accepts a frame as parameter, and
>> a frame is only bound to a pspace, not a thread ...
>>
>> My code
>>> self_bpfinish->py_bp.bp->pspace = current_program_space;
>> was wrong, it should rather be
>>> pspace = get_frame_program_space (prev_frame);
>> and this also implies that we can't really defer this setting to
>> another place ...
>>
>> Maybe we can state in the documentation that FinishBreakpoint will be
>> created by default in the selected thread, and add another parameter
>> in the constructor to specify another thread?
>
> so it was not possible to allow the user to specify the thread as I
> suggest, for the reason I mentioned two lines above ... :(
> I added this line to the doc to express it:
>
>> Finish breakpoints are thread specific and must be create with the right
>> thread selected.
>
>
> cordially,
>
> Kevin
>
> (I'll send getpid fix tomorrow)

here is a fix for the getpid problem you noticed yesterday,
I now compile the existing py-events-shlib.c into
py-events-shlib-nodebug.so, ensure that it has no debug info, and
continue as before.

I also changed
-set remote_python_file [remote_download host
${srcdir}/${subdir}/${testfile}.py]
+set python_file  ${srcdir}/${subdir}/${testfile}.py
as I think it was not really necessary

I also added
	* Makefile.in (EXECUTABLES): Add py-finish-breakpoint and
	py-finish-breakpoint2
	(MISCELLANEOUS): Add py-events-shlib.so and py-events-shlib-nodebug.so
to allow cleaning up the testsuite files (cf. below)


Finally, I switched

+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}

and

+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}"
executable {debug c++}] != "" } {
+    untested "Couldn't compile ${srcfile}"
+    return -1
+}

in testsuite/gdb.python/py-finish-breakpoint2.exp, because it leaded
to an intermittent ERROR (1st run would fail, next ones would be
successful) -- my testsuite test harness doesn't consider this
situation ...


Cordially,

Kevin

as far as I could test, there is no regression against the current git
tree (x86_64/Fedora)

--

2011-12-09  Kevin Pouget  <kevin.pouget@st.com>

	Introduce gdb.FinishBreakpoint in Python

	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-finishbreakpoint.o.
	(SUBDIR_PYTHON_SRCS): Add python/py-finishbreakpoint.c.
	Add build rule for this file.
	* infcmd.c (print_return_value): Split to create get_return_value.
	(get_return_value): New function based on print_return_value. Handle
	case where stop_registers are not set.
	* inferior.h (get_return_value): New prototype.
	* python/py-breakpoint.c (bppy_pending_object): Make non-static.
	(gdbpy_breakpoint_created): Set is_py_finish_bp is necessary.
	(struct breakpoint_object): Move to python-internal.h
	(BPPY_REQUIRE_VALID): Likewise.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(gdbpy_breakpoint_created): Initialize is_finish_bp.
	(gdbpy_should_stop): Add  pre/post hooks before/after calling stop
	method.
	* python/python-internal.h (breakpoint_object_type): Add as extern.
	(bppy_pending_object): Likewise.
	(typedef struct breakpoint_object) Removed.
	(struct breakpoint_object): Moved from py-breakpoint.c.
	Add field is_finish_bp.
	(BPPY_REQUIRE_VALID): Moved from py-breakpoint.c.
	(BPPY_SET_REQUIRE_VALID): Likewise.
	(frame_object_to_frame_info): New prototype.
	(gdbpy_initialize_finishbreakpoints): New prototype.
	(bpfinishpy_is_finish_bp): Likewise.
	(bpfinishpy_pre_stop_hook): Likewise.
	(bpfinishpy_post_stop_hook): Likewise.
	* python/py-finishbreakpoint.c: New file.
	* python/py-frame.c(frame_object_to_frame_info): Make non-static and
	accept PyObject instead of frame_object.
	(frapy_is_valid): Don't cast to frame_object.
	(frapy_name): Likewise.
	(frapy_type): Likewise.
	(frapy_unwind_stop_reason): Likewise.
	(frapy_pc): Likewise.
	(frapy_block): Likewise.
	(frapy_function): Likewise.
	(frapy_older): Likewise.
	(frapy_newer): Likewise.
	(frapy_find_sal): Likewise.
	(frapy_read_var): Likewise.
	(frapy_select): Likewise.
	* python/python.c (gdbpy_is_stopped_at_finish_bp): New noop function.
	(_initialize_python): Add gdbpy_initialize_finishbreakpoints.
	* python/python.h: Include breakpoint.h
	(gdbpy_is_stopped_at_finish_bp): New prototype.

doc/
	* gdb.texinfo (Finish Breakpoints in Python): New subsection.
	(Python API): Add menu entry for Finish Breakpoints.

testsuite/
	* gdb.python/py-breakpoint.exp (mult_line): Define and use variable
	instead of line number.
	* gdb.python/py-finish-breakpoint.c: New file.
	* gdb.python/py-finish-breakpoint.exp: New file.
	* gdb.python/py-finish-breakpoint.py: New file.
	* gdb.python/py-finish-breakpoint2.cc: New file.
	* gdb.python/py-finish-breakpoint2.exp: New file.
	* gdb.python/py-finish-breakpoint2.py: New file.

[-- Attachment #2: 0001-Introduce-gdb.FinishBreakpoint.patch --]
[-- Type: text/x-patch, Size: 59244 bytes --]

From bdf71570e8744b9bea21c8c6cdd8ab005d18d64f Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Tue, 20 Sep 2011 13:59:23 +0200
Subject: [PATCH]  Introduce gdb.FinishBreakpoint

---
 gdb/Makefile.in                                    |    6 +
 gdb/NEWS                                           |    4 +
 gdb/breakpoint.c                                   |    3 +-
 gdb/breakpoint.h                                   |    2 +-
 gdb/doc/gdb.texinfo                                |   53 +++
 gdb/infcmd.c                                       |   39 ++-
 gdb/inferior.h                                     |    3 +
 gdb/python/py-breakpoint.c                         |   48 +--
 gdb/python/py-finishbreakpoint.c                   |  462 ++++++++++++++++++++
 gdb/python/py-frame.c                              |   32 +-
 gdb/python/python-internal.h                       |   50 ++-
 gdb/python/python.c                                |    1 +
 gdb/testsuite/gdb.python/Makefile.in               |    5 +-
 gdb/testsuite/gdb.python/py-breakpoint.exp         |    7 +-
 gdb/testsuite/gdb.python/py-finish-breakpoint.c    |  100 +++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.exp  |  254 +++++++++++
 gdb/testsuite/gdb.python/py-finish-breakpoint.py   |   89 ++++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc  |   59 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   65 +++
 gdb/testsuite/gdb.python/py-finish-breakpoint2.py  |   33 ++
 20 files changed, 1246 insertions(+), 69 deletions(-)
 create mode 100644 gdb/python/py-finishbreakpoint.c
 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
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
 create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint2.py

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b71db33..d8d1b40 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -287,6 +287,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 \
@@ -318,6 +319,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 \
@@ -2109,6 +2111,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/NEWS b/gdb/NEWS
index 42782ce..b755605 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -57,6 +57,10 @@
   ** The "gdb.breakpoint" function has been deprecated in favor of
      "gdb.breakpoints".
 
+  ** A new class "gdb.FinishBreakpoint" is provided to catch the return
+     of a function.  This class is based on the "finish" command
+     available in the CLI. 
+
   ** Type objects for struct and union types now allow access to
      the fields using standard Python dictionary (mapping) methods.
      For example, "some_type['myfield']" now works, as does
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index d9d5bbe..bfd0ca7 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -7933,7 +7933,8 @@ create_breakpoint (struct gdbarch *gdbarch,
       b->disposition = tempflag ? disp_del : disp_donttouch;
       b->condition_not_parsed = 1;
       b->enable_state = enabled ? bp_enabled : bp_disabled;
-      if (type_wanted != bp_breakpoint && type_wanted != bp_hardware_breakpoint)
+      if ((type_wanted != bp_breakpoint
+           && type_wanted != bp_hardware_breakpoint) || thread != -1)
 	b->pspace = current_program_space;
 
       install_breakpoint (internal, b, 0);
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index ddf1881..d22e564 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -569,7 +569,7 @@ struct breakpoint
 
     /* The program space used to set the breakpoint.  This is only set
        for breakpoints which are specific to a program space; for
-       ordinary breakpoints this is NULL.  */
+       non-thread-specific ordinary breakpoints this is NULL.  */
     struct program_space *pspace;
 
     /* String we used to set the breakpoint (malloc'd).  */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 50c299e..950257f 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21501,6 +21501,8 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Symbol Tables In Python::     Python representation of symbol tables.
 * Lazy Strings In Python::      Python representation of lazy strings.
 * Breakpoints In Python::       Manipulating breakpoints using Python.
+* Finish Breakpoints in Python:: Setting Breakpoints on function return
+                                using Python.
 @end menu
 
 @node Basic Python
@@ -24335,6 +24337,57 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@node Finish Breakpoints in Python
+@subsubsection Finish Breakpoints
+
+@cindex python finish breakpoints
+@tindex gdb.FinishBreakpoint
+
+A finish breakpoint is a temporary breakpoint set at the return address of
+a frame, based on the @code{finish} command.  @code{gdb.FinishBreakpoint}
+extends @code{gdb.Breakpoint}.  The underlying breakpoint will be disabled 
+and deleted when the execution will run out of the breakpoint scope (i.e.@: 
+@code{Breakpoint.stop} or @code{FinishBreakpoint.out_of_scope} triggered).
+Finish breakpoints are thread specific and must be create with the right 
+thread selected.  
+ 
+@defun FinishBreakpoint.__init__ (@r{[}frame@r{]} @r{[}, internal@r{]})
+Create a finish breakpoint at the return address of the @code{gdb.Frame}
+object @var{frame}.  If @var{frame} is not provided, this defaults to the
+newest frame.  The optional @var{internal} argument allows the breakpoint to
+become invisible to the user.  @xref{Breakpoints In Python}, for further 
+details about this argument.
+@end defun
+
+@defun FinishBreakpoint.out_of_scope (self)
+In some circumstances (e.g.@: @code{longjmp}, C@t{++} exceptions, @value{GDBN} 
+@code{return} command, @dots{}), a function may not properly terminate, and
+thus never hit the finish breakpoint.  When @value{GDBN} notices such a
+situation, the @code{out_of_scope} callback will be triggered.
+
+You may want to sub-class @code{gdb.FinishBreakpoint} and override this
+method:
+
+@smallexample
+class MyFinishBreakpoint (gdb.FinishBreakpoint)
+    def stop (self):
+        print "normal finish"
+        return True
+    
+    def out_of_scope ():
+        print "abnormal finish"
+@end smallexample 
+@end defun
+
+@defvar FinishBreakpoint.return_value
+When @value{GDBN} is stopped at a finish breakpoint and the frame 
+used to build the @code{gdb.FinishBreakpoint} object had debug symbols, this
+attribute will contain a @code{gdb.Value} object corresponding to the return
+value of the function.  The value will be @code{None} if the function return 
+type is @code{void} or if the return value was not computable.  This attribute
+is not writable.
+@end defvar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 5d94cd4..cffa194 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -1414,16 +1414,26 @@ 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.  */
+/* Return the value of the result of a function 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 regcache *stop_regs = stop_registers;
+  struct gdbarch *gdbarch;
   struct value *value;
   struct ui_out *uiout = current_uiout;
+  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
+
+  /* If stop_registers were not saved, use the current registers.  */
+  if (!stop_regs)
+    {
+      stop_regs = regcache_dup (get_current_regcache ());
+      cleanup = make_cleanup_regcache_xfree (stop_regs);
+    }
+
+  gdbarch = get_regcache_arch (stop_regs);
 
   CHECK_TYPEDEF (value_type);
   gdb_assert (TYPE_CODE (value_type) != TYPE_CODE_VOID);
@@ -1442,7 +1452,7 @@ print_return_value (struct type *func_type, struct type *value_type)
     case RETURN_VALUE_ABI_RETURNS_ADDRESS:
     case RETURN_VALUE_ABI_PRESERVES_ADDRESS:
       value = allocate_value (value_type);
-      gdbarch_return_value (gdbarch, func_type, value_type, stop_registers,
+      gdbarch_return_value (gdbarch, func_type, value_type, stop_regs,
 			    value_contents_raw (value), NULL);
       break;
     case RETURN_VALUE_STRUCT_CONVENTION:
@@ -1452,6 +1462,21 @@ print_return_value (struct type *func_type, struct type *value_type)
       internal_error (__FILE__, __LINE__, _("bad switch"));
     }
 
+  do_cleanups (cleanup);
+
+  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;
+  struct ui_out *uiout = current_uiout;
+
   if (value)
     {
       struct value_print_options opts;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..f198696 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -269,6 +269,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/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 11d60fe..64ef45f 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -31,52 +31,16 @@
 #include "arch-utils.h"
 #include "language.h"
 
-static 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
 {
@@ -762,6 +726,9 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
   struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
   struct cleanup *cleanup = ensure_python_env (garch, current_language);
 
+  if (bp_obj->is_finish_bp)
+    bpfinishpy_pre_stop_hook (bp_obj);
+
   if (PyObject_HasAttrString (py_bp, stop_func))
     {
       PyObject *result = PyObject_CallMethod (py_bp, stop_func, NULL);
@@ -783,6 +750,10 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj)
       else
 	gdbpy_print_stack ();
     }
+
+  if (bp_obj->is_finish_bp)
+    bpfinishpy_post_stop_hook (bp_obj);
+
   do_cleanups (cleanup);
 
   return stop;
@@ -845,6 +816,7 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       newbp->number = bp->number;
       newbp->bp = bp;
       newbp->bp->py_bp_object = newbp;
+      newbp->is_finish_bp = 0;
       Py_INCREF (newbp);
       ++bppy_live;
     }
@@ -1006,7 +978,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..a2d8165
--- /dev/null
+++ b/gdb/python/py-finishbreakpoint.c
@@ -0,0 +1,462 @@
+/* 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.  */
+  breakpoint_object py_bp;
+  /* gdb.Type object of the value return by the breakpointed function.
+     May be NULL if no debug information was available or return type
+     was VOID.  */
+  PyObject *return_type;
+  /* gdb.Type object of the function finished by this breakpoint.  Will be
+     NULL if return_type is NULL.  */
+  PyObject *function_type;
+  /* When stopped at this FinishBreakpoint, gdb.Value object returned by
+     the function; Py_None if the value is not computable; NULL if GDB is
+     not stopped at a FinishBreakpoint.  */
+  PyObject *return_value;
+};
+
+/* 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;
+
+  if (!self_finishbp->return_value)
+    Py_RETURN_NONE;
+
+  Py_INCREF (self_finishbp->return_value);
+  return self_finishbp->return_value;
+}
+
+/* Deallocate FinishBreakpoint object.  */
+
+static void
+bpfinishpy_dealloc (PyObject *self)
+{
+  struct finish_breakpoint_object *self_bpfinish =
+        (struct finish_breakpoint_object *) self;
+
+  Py_XDECREF (self_bpfinish->function_type);
+  Py_XDECREF (self_bpfinish->return_type);
+  Py_XDECREF (self_bpfinish->return_value);
+}
+
+/* Triggered when gdbpy_should_stop is about to execute the `stop' callback
+   of the gdb.FinishBreakpoint object BP_OBJ.  Will compute and cache the
+   `return_value', if possible.  */
+
+void
+bpfinishpy_pre_stop_hook (struct breakpoint_object *bp_obj)
+{
+  struct finish_breakpoint_object *self_finishbp =
+        (struct finish_breakpoint_object *) bp_obj;
+  volatile struct gdb_exception except;
+
+  /* Can compute return_value only once.  */
+  gdb_assert (!self_finishbp->return_value);
+
+  if (!self_finishbp->return_type)
+    return;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      struct value *ret =
+          get_return_value (type_object_to_type (self_finishbp->function_type),
+                            type_object_to_type (self_finishbp->return_type));
+
+      if (ret)
+        {
+          self_finishbp->return_value = value_to_value_object (ret);
+          if (!self_finishbp->return_value)
+              gdbpy_print_stack ();
+        }
+      else
+        {
+          Py_INCREF (Py_None);
+          self_finishbp->return_value = Py_None;
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      gdbpy_print_stack ();
+    }
+}
+
+/* Triggered when gdbpy_should_stop has triggered the `stop' callback
+   of the gdb.FinishBreakpoint object BP_OBJ.  */
+
+void
+bpfinishpy_post_stop_hook (struct breakpoint_object *bp_obj)
+{
+  volatile struct gdb_exception except;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Can't delete it here, but it will be removed at the next stop.  */
+      disable_breakpoint (bp_obj->bp);
+      gdb_assert (bp_obj->bp->disposition == disp_del);
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      gdbpy_print_stack ();
+    }
+}
+
+/* Python function to create a new breakpoint.  */
+
+static int
+bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
+{
+  static char *keywords[] = { "frame", "internal", NULL };
+  struct finish_breakpoint_object *self_bpfinish =
+      (struct finish_breakpoint_object *) self;
+  int type = bp_breakpoint;
+  PyObject *frame_obj = NULL;
+  int thread;
+  struct frame_info *frame, *prev_frame = NULL;
+  struct frame_id frame_id;
+  PyObject *internal = NULL;
+  int internal_bp = 0;
+  CORE_ADDR finish_pc, pc;
+  volatile struct gdb_exception except;
+  char *addr_str, small_buf[100];
+  struct symbol *function;
+
+  if (!PyArg_ParseTupleAndKeywords (args, kwargs, "|OO", keywords,
+                                    &frame_obj, &internal))
+    return -1;
+
+  /* Default frame to gdb.newest_frame if necessary.  */
+  if (!frame_obj)
+    frame_obj = gdbpy_newest_frame (NULL, NULL);
+  else
+    Py_INCREF (frame_obj);
+
+  frame = frame_object_to_frame_info (frame_obj);
+  Py_DECREF (frame_obj);
+
+  if (frame == NULL)
+    goto invalid_frame;
+  
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      prev_frame = get_prev_frame (frame);
+      if (prev_frame == 0)
+        {
+          PyErr_SetString (PyExc_ValueError, _("\"FinishBreakpoint\" not "   \
+                                               "meaningful in the outermost "\
+                                               "frame."));
+        }
+      else if (get_frame_type (prev_frame) == DUMMY_FRAME)
+        {
+          PyErr_SetString (PyExc_ValueError, _("\"FinishBreakpoint\" cannot "\
+                                               "be set on a dummy frame."));
+        }
+      else
+        {
+          frame_id = get_frame_id (prev_frame);
+          if (frame_id_eq (frame_id, null_frame_id))
+            PyErr_SetString (PyExc_ValueError,
+                             _("Invalid ID for the `frame' object."));
+        }
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      return -1;
+    }
+  else if (PyErr_Occurred ())
+    return -1;
+
+  thread = pid_to_thread_id (inferior_ptid);
+  if (thread == 0)
+    {
+      PyErr_SetString (PyExc_ValueError,
+                       _("No thread currently selected."));
+      return -1;
+    }
+
+  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;
+        }
+    }
+
+  /* Find the function we will return from.  */
+  self_bpfinish->return_type = NULL;
+  self_bpfinish->function_type = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (get_frame_pc_if_available (frame, &pc))
+        {
+          function = find_pc_function (pc);
+          if (function != NULL)
+            {
+              struct type *ret_type =
+                  TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+
+              /* Remember only non-void return types.  */
+              if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
+                {
+                  /* Ignore Python errors at this stage.  */
+                  self_bpfinish->return_type = type_to_type_object (ret_type);
+                  PyErr_Clear ();
+                  self_bpfinish->function_type =
+                      type_to_type_object (SYMBOL_TYPE (function));
+                  PyErr_Clear ();
+                }
+            }
+        }
+    }
+  if (except.reason < 0
+      || !self_bpfinish->return_type || !self_bpfinish->function_type)
+    {
+      /* Won't be able to compute return value.  */
+      Py_XDECREF (self_bpfinish->return_type);
+      Py_XDECREF (self_bpfinish->function_type);
+
+      self_bpfinish->return_type = NULL;
+      self_bpfinish->function_type = NULL;
+    }
+
+  bppy_pending_object = &self_bpfinish->py_bp;
+  bppy_pending_object->number = -1;
+  bppy_pending_object->bp = NULL;
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      /* Set a breakpoint on the return address.  */
+      finish_pc = get_frame_pc (prev_frame);
+      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      addr_str = small_buf;
+
+      create_breakpoint (python_gdbarch,
+                         addr_str, NULL, thread,
+                         0,
+                         1 /*temp_flag*/,
+                         bp_breakpoint,
+                         0,
+                         AUTO_BOOLEAN_TRUE,
+                         &bkpt_breakpoint_ops,
+                         0, 1, internal_bp);
+    }
+  GDB_PY_SET_HANDLE_EXCEPTION (except);
+  
+  self_bpfinish->py_bp.bp->frame_id = frame_id;
+  self_bpfinish->py_bp.is_finish_bp = 1;
+  
+  /* Bind the breakpoint with the current program space.  */
+  self_bpfinish->py_bp.bp->pspace = current_program_space;
+
+  return 0;
+  
+ invalid_frame:
+  PyErr_SetString (PyExc_ValueError, 
+                   _("Invalid ID for the `frame' object."));
+  return -1;
+}
+
+/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
+   the current callstack.  Triggers the method OUT_OF_SCOPE if implemented,
+   then delete the breakpoint.  */
+
+static void
+bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
+{
+  volatile struct gdb_exception except;
+  breakpoint_object *bp_obj = (breakpoint_object *) bpfinish_obj;
+  PyObject *py_obj = (PyObject *) bp_obj;
+
+  if (bpfinish_obj->py_bp.bp->enable_state == bp_enabled
+      && PyObject_HasAttrString (py_obj, outofscope_func))
+    {
+      if (!PyObject_CallMethod (py_obj, outofscope_func, NULL))
+          gdbpy_print_stack ();
+    }
+
+  delete_breakpoint (bpfinish_obj->py_bp.bp);
+}
+
+/* Callback for `bpfinishpy_detect_out_scope'.  Triggers Python's
+   `B->out_of_scope' function if B is a FinishBreakpoint out of its scope.  */
+
+static int
+bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
+{
+  volatile struct gdb_exception except;
+  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 ();
+  
+  /* Trigger out_of_scope if this is a FinishBreakpoint and its frame is
+     not anymore in the current callstack.  */
+  if (py_bp != NULL && b->py_bp_object->is_finish_bp)
+    {
+      struct finish_breakpoint_object *finish_bp =
+          (struct finish_breakpoint_object *) py_bp;
+
+      /* Check scope if not currently stopped at the FinishBreakpoint.  */
+      if (b != bp_stopped)
+        {
+          TRY_CATCH (except, RETURN_MASK_ALL)
+            {
+              if (b->pspace == current_inferior ()->pspace
+                  && (!target_has_registers
+                      || frame_find_by_id (b->frame_id) == NULL))
+                bpfinishpy_out_of_scope (finish_bp);
+            }
+          if (except.reason < 0)
+            {
+              gdbpy_convert_exception (except);
+              gdbpy_print_stack ();
+            }
+        }
+    }
+
+  return 0;
+}
+
+/* Attached to `stop' notifications, check if the execution has run
+   out of the scope of any FinishBreakpoint before it has been hit.  */
+
+static void
+bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
+{
+  struct cleanup *cleanup = ensure_python_env (get_current_arch (),
+                                               current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
+                            bs == NULL ? NULL : bs->breakpoint_at);
+
+  do_cleanups (cleanup);
+}
+
+/* Attached to `exit' notifications, triggers all the necessary out of
+   scope notifications.  */
+
+static void
+bpfinishpy_handle_exit (struct inferior *inf)
+{
+  struct cleanup *cleanup = ensure_python_env (target_gdbarch,
+                                               current_language);
+
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
+
+  do_cleanups (cleanup);
+}
+
+/* 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 PyGetSetDef finish_breakpoint_object_getset[] = {
+  { "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*/
+  bpfinishpy_dealloc,             /*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 20064ca..c334f63 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);
@@ -106,7 +107,7 @@ frapy_is_valid (PyObject *self, PyObject *args)
 
   TRY_CATCH (except, RETURN_MASK_ALL)
     {
-      frame = frame_object_to_frame_info ((frame_object *) self);
+      frame = frame_object_to_frame_info (self);
     }
   GDB_PY_HANDLE_EXCEPTION (except);
 
@@ -130,7 +131,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);
     }
@@ -159,7 +160,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);
     }
@@ -180,7 +181,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);
 
@@ -201,7 +202,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);
     }
@@ -222,7 +223,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);
@@ -263,7 +264,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));
     }
@@ -330,7 +331,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)
@@ -359,7 +360,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)
@@ -388,7 +389,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);
@@ -444,7 +445,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);
@@ -472,7 +473,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);
     }
@@ -487,12 +488,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 ef39d5d..1ba7133 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -120,9 +120,50 @@ 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;
+
+  /* 1 is this is a FinishBreakpoint object, 0 otherwise.  */
+  int is_finish_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
 {
@@ -188,6 +229,7 @@ struct value *convert_value_from_python (PyObject *obj);
 struct type *type_object_to_type (PyObject *obj);
 struct symtab *symtab_object_to_symtab (PyObject *obj);
 struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj);
+struct frame_info *frame_object_to_frame_info (PyObject *frame_obj);
 
 void gdbpy_initialize_auto_load (void);
 void gdbpy_initialize_values (void);
@@ -202,6 +244,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);
@@ -275,6 +318,9 @@ PyObject *gdbpy_get_varobj_pretty_printer (struct value *value);
 char *gdbpy_get_display_hint (PyObject *printer);
 PyObject *gdbpy_default_visualizer (PyObject *self, PyObject *args);
 
+void bpfinishpy_pre_stop_hook (struct breakpoint_object *bp_obj);
+void bpfinishpy_post_stop_hook (struct breakpoint_object *bp_obj);
+
 extern PyObject *gdbpy_doc_cst;
 extern PyObject *gdbpy_children_cst;
 extern PyObject *gdbpy_to_string_cst;
diff --git a/gdb/python/python.c b/gdb/python/python.c
index b0b9a9c..f3fdf9a 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1247,6 +1247,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/Makefile.in b/gdb/testsuite/gdb.python/Makefile.in
index 9c98db9..5890191 100644
--- a/gdb/testsuite/gdb.python/Makefile.in
+++ b/gdb/testsuite/gdb.python/Makefile.in
@@ -4,9 +4,10 @@ srcdir = @srcdir@
 EXECUTABLES = py-type py-value py-prettyprint py-template py-block \
 	py-symbol py-mi py-breakpoint py-inferior py-infthread \
 	py-shared python lib-types py-events py-evthreads py-frame \
-	py-mi py-pp-maint py-progspace py-section-script py-objfile
+	py-mi py-pp-maint py-progspace py-section-script py-objfile \
+	py-finish-breakpoint py-finish-breakpoint2
 
-MISCELLANEOUS = py-shared-sl.sl
+MISCELLANEOUS = py-shared-sl.sl py-events-shlib.so py-events-shlib-nodebug.so 
 
 all info install-info dvi install uninstall installcheck check:
 	@echo "Nothing to be done for $@..."
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index e0dd087..0e3adbd 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.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
new file mode 100644
index 0000000..cf2e06c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -0,0 +1,100 @@
+/* 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>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Defined in py-events-shlib.h.  */
+extern void do_nothing (void);
+
+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);
+}
+
+void
+test_exec_exit (int do_exit)
+{
+  if (do_exit)
+    exit (0);
+  else
+    execl ("/bin/echo", "echo", "-1", (char *)0);
+}
+
+int main (int argc, char *argv[])
+{
+  jmp_buf env;
+  int foo = 5;
+  int bar = 42;
+  int i, j;
+
+  do_nothing ();
+
+  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.  */
+
+  test_exec_exit (1);
+
+  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..4a02a06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -0,0 +1,254 @@
+# 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 libfile "py-events-shlib"
+set libsrc  $srcdir/$subdir/$libfile.c
+set lib_sl  $objdir/$subdir/$libfile-nodebug.so
+set lib_opts ""
+
+set testfile "py-finish-breakpoint"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+set exec_opts [list debug shlib=$lib_sl]
+
+if [get_compiler_info ${binfile}] {
+    return -1
+}
+
+if { [gdb_compile_shlib $libsrc $lib_sl $lib_opts] != ""
+     || [gdb_compile $srcdir/$subdir/$srcfile $binfile executable $exec_opts] != ""} {
+    untested "Could not compile either $libsrc or $srcdir/$subdir/$srcfile."
+    return -1
+}
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+set python_file ${srcdir}/${subdir}/${testfile}.py
+
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+#
+# 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 $python_file" "Python script imported.*" \
+         "import python scripts"
+gdb_breakpoint "increase_1"
+gdb_test "continue" "Breakpoint .*at.*" "continue to the function to finish"
+
+# set FinishBreakpoint
+
+gdb_test "python finishbp_default = gdb.FinishBreakpoint ()" \
+         "Temporary breakpoint.*" "set FinishBreakpoint with default frame value"
+gdb_test "python finishbp = MyFinishBreakpoint (gdb.parse_and_eval ('a'), gdb.newest_frame ())" \
+         "Temporary breakpoint.*" "set FinishBreakpoint"
+gdb_test "python print finishbp.return_value" "None.*" \
+         "check return_value at init"
+
+# check normal bp hit
+
+gdb_test "continue" "MyFinishBreakpoint stop with.*return_value is: -5.*#0.*increase.*" \
+         "check MyFinishBreakpoint hit"
+gdb_test "python print finishbp.return_value" "-5.*" "check return_value"
+
+gdb_test "python print finishbp_default.hit_count" "1.*" "check finishBP on default frame has been hit"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer normal hit"
+
+# check FinishBreakpoint in main no allowed
+
+gdb_test "finish" "main.*" "return to main()"
+gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \
+         "ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \
+         "check FinishBP not allowed in main"
+
+#
+# Test FinishBreakpoint with no debug symbol 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $python_file" "Python script imported.*" \
+         "import python scripts"
+set cond_line [gdb_get_line_number "Condition Break."]
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+gdb_test "print do_nothing" "no debug info.*" "ensure that shared lib has no debug info"
+gdb_breakpoint "do_nothing" {temporary}
+gdb_test "continue" "Temporary breakpoint .*in do_nothing.*" "continue to do_nothing"
+
+gdb_test "python finishBP = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint"
+gdb_test "continue" "SimpleFinishBreakpoint stop.*" "check FinishBreakpoint hit"
+gdb_test "python print finishBP.return_value" "None" "check return value without debug symbol"
+
+#
+# Test FinishBreakpoint in function returned by longjmp 
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $python_file" "Python script imported.*" \
+         "import python scripts"
+
+if ![runto call_longjmp_1] then {
+    perror "couldn't run to breakpoint call_longjmp"
+    continue
+}
+
+gdb_test "python finishbp = SimpleFinishBreakpoint(gdb.newest_frame())" \
+         "SimpleFinishBreakpoint init" \
+         "set finish breakpoint" 
+gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \
+         "set BP after the jump"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" \
+         "check FinishBP out of scope notification"
+gdb_test "python print finishbp.is_valid()" "False.*"\
+         "ensure that finish bp is invalid afer out of scope notification"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in dummy frame)
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $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_1(i,8)" "Breakpoint .* at .*" \
+         "set a conditional BP"
+gdb_test "python TestBreakpoint()" "TestBreakpoint init" \
+         "set FinishBP in a breakpoint condition"
+gdb_test "continue" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+gdb_test "print i" "8" "check stopped location"
+
+#
+# Test FinishBreakpoint in BP condition evaluation 
+# (finish in normal frame)
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $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"
+
+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 explicit inferior function call
+#
+
+clean_restart ${testfile}
+
+gdb_test "source $python_file" "Python script imported.*" \
+         "import python scripts"
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+# return address in dummy frame
+
+gdb_test "python TestExplicitBreakpoint('increase_1')" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase_1(&i)" \
+         "\"FinishBreakpoint\" cannot be set on a dummy frame.*" \
+         "don't allow FinishBreakpoint on dummy frames"
+
+# return address in normal frame
+
+delete_breakpoints
+gdb_test "python TestExplicitBreakpoint(\"increase_1\")" "Breakpoint.*at.*" \
+         "prepare TestExplicitBreakpoint"
+gdb_test "print increase(&i)" \
+         "SimpleFinishBreakpoint init.*SimpleFinishBreakpoint stop.*The program being debugged stopped while in a function called from GDB.*" \
+         "FinishBP stop at during explicit function call"
+
+
+#
+# Test FinishBreakpoint when inferior exits
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}
+
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
+
+#
+# Test FinishBreakpoint when inferior execs
+#
+
+if ![runto "test_exec_exit"] then {
+    fail "Cannot run to test_exec_exit."
+    return 0
+}     
+
+gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
+gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
+gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ 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..dea2a73
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py
@@ -0,0 +1,89 @@
+# 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 MyFinishBreakpoint (gdb.FinishBreakpoint):
+	def __init__(self, val, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		print "MyFinishBreakpoint init"
+		self.val = val
+		
+	def stop(self):
+		print "MyFinishBreakpoint stop with %d" % int (self.val.dereference ())
+		print "return_value is: %d" % int (self.return_value)
+		gdb.execute ("where 1")
+		return True
+	
+	def out_of_scope(self):
+		print "MyFinishBreakpoint out of scope"
+
+class TestBreakpoint(gdb.Breakpoint):
+    def __init__(self):
+        gdb.Breakpoint.__init__ (self, spec="test_1", internal=1)
+        self.silent = True
+        self.count = 0
+        print "TestBreakpoint init"
+        
+    def stop(self):
+    	self.count += 1
+    	try:
+        	TestFinishBreakpoint (gdb.newest_frame (), self.count)
+        except ValueError as e:
+        	print e
+        return False
+
+class TestFinishBreakpoint (gdb.FinishBreakpoint):
+    def __init__ (self, frame, count):
+    	self.count = count
+        gdb.FinishBreakpoint.__init__ (self, frame, internal=1)
+        
+        
+    def stop(self):
+        print "-->", self.number
+        if (self.count == 3):
+            print "test stop: %d" % self.count
+            return True
+        else:
+            print "test don't stop: %d" % self.count
+            return False 
+        
+    
+    def out_of_scope(self):
+        print "test didn't finish: %d" % self.count
+
+class TestExplicitBreakpoint(gdb.Breakpoint):
+	def stop(self):
+		try:
+			SimpleFinishBreakpoint (gdb.newest_frame ())
+		except ValueError as e:
+			print e
+		return False
+
+class SimpleFinishBreakpoint(gdb.FinishBreakpoint):
+	def __init__(self, frame):
+		gdb.FinishBreakpoint.__init__ (self, frame)
+		
+		print "SimpleFinishBreakpoint init"
+		
+	def stop(self):
+		print "SimpleFinishBreakpoint stop" 
+		return True
+	
+	def out_of_scope(self):
+		print "SimpleFinishBreakpoint out of scope"
+
+print "Python script importedd"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc
new file mode 100644
index 0000000..a0eea06
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.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-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
new file mode 100644
index 0000000..433d1e6
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -0,0 +1,65 @@
+# 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-breakpoint2"
+set srcfile ${testfile}.cc
+set binfile ${objdir}/${subdir}/${testfile}
+set pyfile  ${srcdir}/${subdir}/${testfile}.py
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
+    untested "Couldn't compile ${srcfile}"
+    return -1
+}
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+#
+# Check FinishBreakpoints against C++ exceptions
+#
+
+gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
+
+gdb_test "source $pyfile" ".*Python script imported.*" \
+         "import python scripts"
+         
+gdb_breakpoint "throw_exception_1"
+gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
+
+gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
+gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
+gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
+gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.py b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
new file mode 100644
index 0000000..0fb6955
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.py
@@ -0,0 +1,33 @@
+# 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 ExceptionFinishBreakpoint(gdb.FinishBreakpoint):
+    def __init__(self, frame):
+        gdb.FinishBreakpoint.__init__ (self, frame, internal=1)
+        self.silent = True;
+        print "init ExceptionFinishBreakpoint"
+        
+    def stop(self):
+        print "stopped at ExceptionFinishBreakpoint"
+        return True 
+    
+    def out_of_scope(self):
+        print "exception did not finish ..."
+
+
+print "Python script imported"
-- 
1.7.6.4


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

* Re: [RFC] Python Finish Breakpoints
  2011-12-09  9:53                                                                     ` Kevin Pouget
@ 2011-12-18 19:22                                                                       ` Kevin Pouget
  2011-12-20 20:55                                                                       ` Tom Tromey
  2012-01-04 14:49                                                                       ` [RFC] Python Finish Breakpoints Ulrich Weigand
  2 siblings, 0 replies; 74+ messages in thread
From: Kevin Pouget @ 2011-12-18 19:22 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Fri, Dec 9, 2011 at 10:31 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
>
> On Thu, Dec 8, 2011 at 5:09 PM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> > On Thu, Dec 8, 2011 at 4:33 PM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> >> On Thu, Dec 8, 2011 at 3:56 PM, Tom Tromey <tromey@redhat.com> wrote:
> >>>>>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
> >>>
> >>> Kevin> and we're .... not ready!
> >>> Kevin> there are some regressions this morning, certainly due to your
> >>> Kevin> ambiguous linespec patch!
> >>>
> >>> Kevin> first one is quite obvious:
> >>>
> >>> Kevin> a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
> >>> Kevin> -if ![runto "getpid"] then {
> >>> Kevin> -    fail "Cannot run to getpid."
> >>> Kevin> +if ![runto_main] then {
> >>> Kevin> +    fail "Cannot run to main."     return 0 }
> >>> Kevin> +gdb_breakpoint "getpid" {temporary}
> >>> Kevin> +gdb_test "continue" "Temporary breakpoint .*in getpid.*" "continue to getpid"
> >>> Kevin> +
> >>>
> >>> Kevin> "break getpid" used to work before actually starting the inferior, but
> >>> Kevin> now it's unresolved
> >>>
> >>> This test assumes that there is no debuginfo for getpid -- but in my
> >>> case, I have glibc debuginfo installed.  This isn't unheard of for gdb
> >>> developers :)
> >>>
> >>> The test would be more robust if you put a new function in a new file
> >>> and arranged for that file to be compiled without debuginfo.
> >>>
> >>> Sorry I didn't notice this before.
> >>
> >> I'll work on that, thanks for noticing it
> >>
> >>> Kevin> bpfinishpy_out_of_scope can't be triggered anymore because you set
> >>> Kevin> b-> pspace to NULL. I hesitated about how to fix it, but I think that
> >>> Kevin> FinishBP have to be bound to the pspace in which they were set, so I
> >>> Kevin> added this line:
> >>>
> >>> It seems to me that one of these breakpoints has to be thread-specific
> >>> as well.  Maybe the code to set b->pspace should notice thread-specific
> >>> breakpoints and allow pspace to be set there.
> >>>
> >>> I'd rather this be done in breakpoint.c than have other modules modify
> >>> struct breakpoint directly.
> >>>
> >>> Tom
> >>
> >> you're right about thread-specific, but I'm not sure how to enforce
> >> it. Currently, FinishBreakpoint only accepts a frame as parameter, and
> >> a frame is only bound to a pspace, not a thread ...
> >>
> >> My code
> >>> self_bpfinish->py_bp.bp->pspace = current_program_space;
> >> was wrong, it should rather be
> >>> pspace = get_frame_program_space (prev_frame);
> >> and this also implies that we can't really defer this setting to
> >> another place ...
> >>
> >> Maybe we can state in the documentation that FinishBreakpoint will be
> >> created by default in the selected thread, and add another parameter
> >> in the constructor to specify another thread?
> >
> > so it was not possible to allow the user to specify the thread as I
> > suggest, for the reason I mentioned two lines above ... :(
> > I added this line to the doc to express it:
> >
> >> Finish breakpoints are thread specific and must be create with the right
> >> thread selected.
> >
> >
> > cordially,
> >
> > Kevin
> >
> > (I'll send getpid fix tomorrow)
>
> here is a fix for the getpid problem you noticed yesterday,
> I now compile the existing py-events-shlib.c into
> py-events-shlib-nodebug.so, ensure that it has no debug info, and
> continue as before.
>
> I also changed
> -set remote_python_file [remote_download host
> ${srcdir}/${subdir}/${testfile}.py]
> +set python_file  ${srcdir}/${subdir}/${testfile}.py
> as I think it was not really necessary
>
> I also added
>        * Makefile.in (EXECUTABLES): Add py-finish-breakpoint and
>        py-finish-breakpoint2
>        (MISCELLANEOUS): Add py-events-shlib.so and py-events-shlib-nodebug.so
> to allow cleaning up the testsuite files (cf. below)
>
>
> Finally, I switched
>
> +# Start with a fresh gdb.
> +gdb_exit
> +gdb_start
> +gdb_reinitialize_dir $srcdir/$subdir
> +gdb_load ${binfile}
>
> and
>
> +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}"
> executable {debug c++}] != "" } {
> +    untested "Couldn't compile ${srcfile}"
> +    return -1
> +}
>
> in testsuite/gdb.python/py-finish-breakpoint2.exp, because it leaded
> to an intermittent ERROR (1st run would fail, next ones would be
> successful) -- my testsuite test harness doesn't consider this
> situation ...
>
>
> Cordially,
>
> Kevin
>
> as far as I could test, there is no regression against the current git
> tree (x86_64/Fedora)
>
> --
>
> 2011-12-09  Kevin Pouget  <kevin.pouget@st.com>
>
>        Introduce gdb.FinishBreakpoint in Python
>
>        * Makefile.in (SUBDIR_PYTHON_OBS): Add py-finishbreakpoint.o.
>        (SUBDIR_PYTHON_SRCS): Add python/py-finishbreakpoint.c.
>        Add build rule for this file.
>        * infcmd.c (print_return_value): Split to create get_return_value.
>        (get_return_value): New function based on print_return_value. Handle
>        case where stop_registers are not set.
>        * inferior.h (get_return_value): New prototype.
>        * python/py-breakpoint.c (bppy_pending_object): Make non-static.
>        (gdbpy_breakpoint_created): Set is_py_finish_bp is necessary.
>        (struct breakpoint_object): Move to python-internal.h
>        (BPPY_REQUIRE_VALID): Likewise.
>        (BPPY_SET_REQUIRE_VALID): Likewise.
>        (gdbpy_breakpoint_created): Initialize is_finish_bp.
>        (gdbpy_should_stop): Add  pre/post hooks before/after calling stop
>        method.
>        * python/python-internal.h (breakpoint_object_type): Add as extern.
>        (bppy_pending_object): Likewise.
>        (typedef struct breakpoint_object) Removed.
>        (struct breakpoint_object): Moved from py-breakpoint.c.
>        Add field is_finish_bp.
>        (BPPY_REQUIRE_VALID): Moved from py-breakpoint.c.
>        (BPPY_SET_REQUIRE_VALID): Likewise.
>        (frame_object_to_frame_info): New prototype.
>        (gdbpy_initialize_finishbreakpoints): New prototype.
>        (bpfinishpy_is_finish_bp): Likewise.
>        (bpfinishpy_pre_stop_hook): Likewise.
>        (bpfinishpy_post_stop_hook): Likewise.
>        * python/py-finishbreakpoint.c: New file.
>        * python/py-frame.c(frame_object_to_frame_info): Make non-static and
>        accept PyObject instead of frame_object.
>        (frapy_is_valid): Don't cast to frame_object.
>        (frapy_name): Likewise.
>        (frapy_type): Likewise.
>        (frapy_unwind_stop_reason): Likewise.
>        (frapy_pc): Likewise.
>        (frapy_block): Likewise.
>        (frapy_function): Likewise.
>        (frapy_older): Likewise.
>        (frapy_newer): Likewise.
>        (frapy_find_sal): Likewise.
>        (frapy_read_var): Likewise.
>        (frapy_select): Likewise.
>        * python/python.c (gdbpy_is_stopped_at_finish_bp): New noop function.
>        (_initialize_python): Add gdbpy_initialize_finishbreakpoints.
>        * python/python.h: Include breakpoint.h
>        (gdbpy_is_stopped_at_finish_bp): New prototype.
>
> doc/
>        * gdb.texinfo (Finish Breakpoints in Python): New subsection.
>        (Python API): Add menu entry for Finish Breakpoints.
>
> testsuite/
>        * gdb.python/py-breakpoint.exp (mult_line): Define and use variable
>        instead of line number.
>        * gdb.python/py-finish-breakpoint.c: New file.
>        * gdb.python/py-finish-breakpoint.exp: New file.
>        * gdb.python/py-finish-breakpoint.py: New file.
>        * gdb.python/py-finish-breakpoint2.cc: New file.
>        * gdb.python/py-finish-breakpoint2.exp: New file.
>        * gdb.python/py-finish-breakpoint2.py: New file.


ping

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

* Re: [RFC] Python Finish Breakpoints
  2011-12-09  9:53                                                                     ` Kevin Pouget
  2011-12-18 19:22                                                                       ` Kevin Pouget
@ 2011-12-20 20:55                                                                       ` Tom Tromey
  2011-12-20 20:58                                                                         ` Kevin Pouget
  2012-01-04 14:49                                                                       ` [RFC] Python Finish Breakpoints Ulrich Weigand
  2 siblings, 1 reply; 74+ messages in thread
From: Tom Tromey @ 2011-12-20 20:55 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches

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

Kevin> 2011-12-09  Kevin Pouget  <kevin.pouget@st.com>
Kevin> 	Introduce gdb.FinishBreakpoint in Python
[...]

One last little thing, in the test suite.

Kevin> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
Kevin> +if $tracelevel then {
Kevin> +    strace $tracelevel
Kevin> +}

Here, this needs to check whether shlibs are ok, like:

if {[skip_shlib_tests]} {
    untested $name.exp
    return -1
}

Kevin> +# Start with a fresh gdb.
Kevin> +clean_restart ${testfile}

After clean_restart you need

gdb_load_shlibs $lib_sl

I think there are a few of these in this .exp file.

I don't think you really needed shared libraries for this test, but now
that you did it, it is fine.

Patch ok with this fixed up.  Thanks!

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2011-12-20 20:55                                                                       ` Tom Tromey
@ 2011-12-20 20:58                                                                         ` Kevin Pouget
  2011-12-21  7:16                                                                           ` Joel Brobecker
                                                                                             ` (2 more replies)
  0 siblings, 3 replies; 74+ messages in thread
From: Kevin Pouget @ 2011-12-20 20:58 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Tue, Dec 20, 2011 at 9:38 PM, Tom Tromey <tromey@redhat.com> wrote:
>
> >>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
>
> Kevin> 2011-12-09  Kevin Pouget  <kevin.pouget@st.com>
> Kevin>  Introduce gdb.FinishBreakpoint in Python
> [...]
>
> One last little thing, in the test suite.
>
> Kevin> diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
> Kevin> +if $tracelevel then {
> Kevin> +    strace $tracelevel
> Kevin> +}
>
> Here, this needs to check whether shlibs are ok, like:
>
> if {[skip_shlib_tests]} {
>    untested $name.exp
>    return -1
> }
>
> Kevin> +# Start with a fresh gdb.
> Kevin> +clean_restart ${testfile}
>
> After clean_restart you need
>
> gdb_load_shlibs $lib_sl
>
> I think there are a few of these in this .exp file.
>
> I don't think you really needed shared libraries for this test, but now
> that you did it, it is fine.
>
> Patch ok with this fixed up.  Thanks!
>
> Tom

Great! I'll fix this last bits and commit it in the next few days
Is it okay for 7.4 branch as well ?


Cordially,

Kevin

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

* Re: [RFC] Python Finish Breakpoints
  2011-12-20 20:58                                                                         ` Kevin Pouget
@ 2011-12-21  7:16                                                                           ` Joel Brobecker
  2011-12-21 17:13                                                                             ` Tom Tromey
  2011-12-24 23:56                                                                           ` [patch] Fix gdb.python/py-finish-breakpoint.exp new FAIL on x86_64-m32 [Re: [RFC] Python Finish Breakpoints] Jan Kratochvil
  2011-12-26 11:28                                                                           ` [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver) " Jan Kratochvil
  2 siblings, 1 reply; 74+ messages in thread
From: Joel Brobecker @ 2011-12-21  7:16 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: Tom Tromey, gdb-patches

> Great! I'll fix this last bits and commit it in the next few days
> Is it okay for 7.4 branch as well ?

Tom asked me on IRC whether it would be OK for 7.4 as well, with
some doubts on his side.  With the time difference, I wasn't there
anymore, so I thought I'd answer here.  As always, if a Global
Maintainer (such as Tom) is confident about a given patch, that's
always good enough for me to put it in. 

I'm not sure whether Tom was asking me to look at the patch and
make an evaluation myself. I skimmed the patch, and at first sight,
it seems big, and slightly intrusive (in the sense that it touches
several files, many of them critical to all platforms). My initial
impression, which would need a more careful read of your patch
to be confirmed, is that it might be a little risky to do so.

Tom: Just let me know if you'd like me to look at the patch more
in detail.

-- 
Joel

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

* Re: [RFC] Python Finish Breakpoints
  2011-12-21  7:16                                                                           ` Joel Brobecker
@ 2011-12-21 17:13                                                                             ` Tom Tromey
       [not found]                                                                               ` <CAPftXUKXh9ekZ2kiwQ=5zbrjst+9VH9-eZk8h+Z-9SpQ1WqdLw@mail.gmail.com>
  0 siblings, 1 reply; 74+ messages in thread
From: Tom Tromey @ 2011-12-21 17:13 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: Kevin Pouget, gdb-patches

>>>>> "Joel" == Joel Brobecker <brobecker@adacore.com> writes:

>> Great! I'll fix this last bits and commit it in the next few days
>> Is it okay for 7.4 branch as well ?

Joel> Tom asked me on IRC whether it would be OK for 7.4 as well, with
Joel> some doubts on his side.  With the time difference, I wasn't there
Joel> anymore, so I thought I'd answer here.  As always, if a Global
Joel> Maintainer (such as Tom) is confident about a given patch, that's
Joel> always good enough for me to put it in. 

Joel> I'm not sure whether Tom was asking me to look at the patch and
Joel> make an evaluation myself. I skimmed the patch, and at first sight,
Joel> it seems big, and slightly intrusive (in the sense that it touches
Joel> several files, many of them critical to all platforms). My initial
Joel> impression, which would need a more careful read of your patch
Joel> to be confirmed, is that it might be a little risky to do so.

Joel> Tom: Just let me know if you'd like me to look at the patch more
Joel> in detail.

I was thinking about it last night, and I think it is ok to go in.

I think the changes outside of Python are pretty easily verified as
being safe.

As for the Python changes, if something is wrong in the implementation
of the new code -- well, it is a new feature, I think it won't break
anything existing.

So, I think it is ok for 7.4.

Tom

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

* [patch] Fix gdb.python/py-finish-breakpoint.exp new FAIL on x86_64-m32  [Re: [RFC] Python Finish Breakpoints]
  2011-12-20 20:58                                                                         ` Kevin Pouget
  2011-12-21  7:16                                                                           ` Joel Brobecker
@ 2011-12-24 23:56                                                                           ` Jan Kratochvil
  2011-12-27 11:13                                                                             ` Kevin Pouget
  2012-01-04 17:45                                                                             ` Ulrich Weigand
  2011-12-26 11:28                                                                           ` [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver) " Jan Kratochvil
  2 siblings, 2 replies; 74+ messages in thread
From: Jan Kratochvil @ 2011-12-24 23:56 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: Tom Tromey, gdb-patches

On Tue, 20 Dec 2011 21:54:47 +0100, Kevin Pouget wrote:
> Great! I'll fix this last bits and commit it in the next few days

+Continuing.
+process 23029 is executing new program: /bin/echo
+warning: Selected architecture i386:x86-64 is not compatible with reported target architecture i386
+Architecture of file not recognized.
+(gdb) FAIL: gdb.python/py-finish-breakpoint.exp: catch out of scope after exec

On x86_64 with -m32 testcases compilation.

OK to check in this patch?


Thanks,
Jan


gdb/testsuite/
2011-12-24  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.python/py-finish-breakpoint.c (test_exec_exit): Change the
	parameter to self_exec, adjust the function.
	(main): Check for argv[1] "exit".  Use argv[0] for test_exec_exit.
	* gdb.python/py-finish-breakpoint.exp (set var self_exec = 0): New test.
	(set var do_exit = 0): Remove test.
	(newline at end of file): Add one.

--- a/gdb/testsuite/gdb.python/py-finish-breakpoint.c
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c
@@ -59,12 +59,12 @@ call_longjmp (jmp_buf *buf)
 }
 
 void
-test_exec_exit (int do_exit)
+test_exec_exit (const char *self_exec)
 {
-  if (do_exit)
+  if (self_exec == NULL)
     exit (0);
   else
-    execl ("/bin/echo", "echo", "-1", (char *)0);
+    execl (self_exec, self_exec, "exit", (char *)0);
 }
 
 int main (int argc, char *argv[])
@@ -74,6 +74,9 @@ int main (int argc, char *argv[])
   int bar = 42;
   int i, j;
 
+  if (argc == 2 && strcmp (argv[1], "exit") == 0)
+    return 0;
+
   do_nothing ();
 
   i = 0;
@@ -94,7 +97,7 @@ int main (int argc, char *argv[])
   else
     j += 1; /* after longjmp.  */
 
-  test_exec_exit (1);
+  test_exec_exit (argv[0]);
 
   return j; /* Break at end.  */
 }
--- a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -247,6 +247,7 @@ if ![runto "test_exec_exit"] then {
     return 0
 }
 
+gdb_test_no_output "set var self_exec = 0" "switch to exit() test"
 gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exit()"
 gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exit"
 
@@ -259,7 +260,6 @@ if ![runto "test_exec_exit"] then {
     return 0
 }     
 
-gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
 gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
 gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
-gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ No newline at end of file
+gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"

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

* [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver)  [Re: [RFC] Python Finish Breakpoints]
  2011-12-20 20:58                                                                         ` Kevin Pouget
  2011-12-21  7:16                                                                           ` Joel Brobecker
  2011-12-24 23:56                                                                           ` [patch] Fix gdb.python/py-finish-breakpoint.exp new FAIL on x86_64-m32 [Re: [RFC] Python Finish Breakpoints] Jan Kratochvil
@ 2011-12-26 11:28                                                                           ` Jan Kratochvil
  2011-12-27 23:30                                                                             ` [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver) [rediff] Jan Kratochvil
  2 siblings, 1 reply; 74+ messages in thread
From: Jan Kratochvil @ 2011-12-26 11:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey, Kevin Pouget

Hi,

with gdbserver as from
	http://sourceware.org/gdb/wiki/TestingGDB#Testing_gdbserver_in_a_native_configuration

it will crash:

(gdb) PASS: gdb.python/py-finish-breakpoint.exp: set FinishBP after the exit()
continue
Continuing.
Child exited with status 0
GDBserver exiting
[Inferior 1 (Remote target) exited normally]
SimpleFinishBreakpoint out of scope
ERROR: Process no longer exists
Program terminated with signal 11, Segmentation fault.
#0  0x00000000007d48b9 in serial_debug_p (scb=0x0) at serial.c:584
584       return scb->debug_p || global_serial_debug_p;
(gdb) p scb
$1 = (struct serial *) 0x0
(gdb) bt
#0  in serial_debug_p (scb=0x0) at serial.c:584
#1  in serial_write (scb=0x0, str=0x7fff727e3300 "$z0,4008a3,1#93", len=15) at serial.c:427
#2  in putpkt_binary (buf=0x2a279b0 "z0,4008a3,1", cnt=11) at remote.c:6891
#3  in putpkt (buf=0x2a279b0 "z0,4008a3,1") at remote.c:6823
#4  in remote_remove_breakpoint (gdbarch=0x28e10f0, bp_tgt=0x2fb3d38) at remote.c:7749
#5  in target_remove_breakpoint (gdbarch=0x28e10f0, bp_tgt=0x2fb3d38) at target.c:2422
#6  in bkpt_remove_location (bl=0x2fb3cd0) at breakpoint.c:10967
#7  in remove_breakpoint_1 (bl=0x2fb3cd0, is=mark_uninserted) at breakpoint.c:2654
#8  in remove_breakpoint (bl=0x2fb3cd0, is=mark_uninserted) at breakpoint.c:2760
#9  in update_global_location_list (should_insert=0) at breakpoint.c:10539
#10 in delete_breakpoint (bpt=0x2f59290) at breakpoint.c:11392
#11 in bpfinishpy_out_of_scope (bpfinish_obj=0x7fdf3c9f8130) at ./python/py-finishbreakpoint.c:327
#12 in bpfinishpy_detect_out_scope_cb (b=0x2f59290, args=0x0) at ./python/py-finishbreakpoint.c:356
#13 in iterate_over_breakpoints (callback=0x65150d <bpfinishpy_detect_out_scope_cb>, data=0x0) at breakpoint.c:13385
#14 in bpfinishpy_handle_exit (inf=0x281d330) at ./python/py-finishbreakpoint.c:393
#15 in observer_inferior_exit_notification_stub (data=0x6516b1, args_data=0x7fff727e3740) at observer.inc:887
#16 in generic_observer_notify (subject=0x284f300, args=0x7fff727e3740) at observer.c:168
#17 in observer_notify_inferior_exit (inf=0x281d330) at observer.inc:912
#18 in exit_inferior_1 (inftoex=0x281d330, silent=1) at inferior.c:276
#19 in exit_inferior_silent (pid=42000) at inferior.c:305
#20 in discard_all_inferiors () at inferior.c:343
#21 in remote_close (quitting=0) at remote.c:2950
#22 in target_close (targ=0x1d19f80, quitting=0) at target.c:3387
#23 in unpush_target (t=0x1d19f80) at target.c:1024
#24 in remote_mourn_1 (target=0x1d19f80) at remote.c:7456
#25 in remote_mourn (ops=0x1d19f80) at remote.c:7449
#26 in target_mourn_inferior () at target.c:2747
#27 in handle_inferior_event (ecs=0x7fff727e3c30) at infrun.c:3408
#28 in wait_for_inferior () at infrun.c:2711
#29 in proceed (addr=18446744073709551615, siggnal=TARGET_SIGNAL_DEFAULT, step=0) at infrun.c:2276
#30 in continue_1 (all_threads=0) at infcmd.c:713
#31 in continue_command (args=0x0, from_tty=1) at infcmd.c:805


Reproducible with:
../gdbserver/gdbserver :1234 gdb.python/py-finish-breakpoint
gdb -nx -x ~/.gdbinit -ex r --args ../gdb -nx -ex 'file gdb.python/py-finish-breakpoint' -ex 'target remote localhost:1234' -ex 'b test_exec_exit' -ex c -ex 'source gdb.python/py-finish-breakpoint.py' -ex 'python SimpleFinishBreakpoint(gdb.newest_frame())' -ex c

I have seen this serial_debug_p crash in various GDB debugging cases but they
were not well reproducible.  I understand this bug is unrelated to
gdb.python/py-finish-breakpoint.exp .

I also tried to reorder remote_close a bit but this patch seems as the right
one to me.

The .exp patch fixes in fact an unrelated testcase but both happen with
gdbserver and py-finish-breakpoint.exp.

The testcase has been tested on x86_64-fedorarawhide-linux-gnu.


Thanks,
Jan


gdb/
2011-12-25  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* remote.c (remote_remove_breakpoint): Return 0 on NULL REMOTE_DESC.

gdb/testsuite/
2011-12-25  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.python/py-finish-breakpoint.exp (catch out of scope after exec):
	Make it unsupported with gdbserver.

--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -7734,6 +7734,13 @@ remote_remove_breakpoint (struct gdbarch *gdbarch,
   CORE_ADDR addr = bp_tgt->placed_address;
   struct remote_state *rs = get_remote_state ();
 
+  if (remote_desc == NULL)
+    {
+      /* remote_close is being executed as our caller.  Avoid error by
+	 reporting successful removal of the breakpoint.  */
+      return 0;
+    }
+
   if (remote_protocol_packets[PACKET_Z0].support != PACKET_DISABLE)
     {
       char *p = rs->buf;
--- a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -262,4 +262,17 @@ if ![runto "test_exec_exit"] then {
 gdb_test_no_output "set var do_exit = 0" "switch to execve() test"
 gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
 gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
-gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
\ No newline at end of file
+
+set test "catch out of scope after exec"
+gdb_test_multiple "continue" $test {
+    -re "warning: Error inserting catchpoint \[0-9\]+: Your system does not support this type\[ \r\n\]+of catchpoint\\.\r\n.*\r\n$gdb_prompt $" {
+	if [is_remote target] {
+	    unsupported $test
+	} else {
+	    fail $test
+	}
+    }
+    -re "SimpleFinishBreakpoint out of scope\r\n$gdb_prompt $" {
+	pass $test
+    }
+}

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

* Re: [RFC] Python Finish Breakpoints
       [not found]                                                                                     ` <CAPftXULKC8gp3L87+PZEF3dj3crv9bh-uzZpRiYKjqEw_xyptQ@mail.gmail.com>
@ 2011-12-27  4:18                                                                                       ` Joel Brobecker
  2011-12-27  9:40                                                                                         ` Kevin Pouget
  2011-12-27  9:34                                                                                       ` Kevin Pouget
  1 sibling, 1 reply; 74+ messages in thread
From: Joel Brobecker @ 2011-12-27  4:18 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: Tom Tromey, gdb-patches

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

> (i won't be able to fix the ARI until the mid of next week but i'll take a
> look at that ASAP and for sure before the release)

I've checked in a fix in HEAD. Tested on x86_64-linux without any
regression. Not sure if we'll want it for the 7.4 branch or not.

-- 
Joel

[-- Attachment #2: py-bp-sprintf.diff --]
[-- Type: text/x-diff, Size: 998 bytes --]

commit b8c553d40b229b1b25c1e5bb309e0c8f5745ebc2
Author: Joel Brobecker <brobecker@adacore.com>
Date:   Mon Dec 26 15:08:34 2011 +0400

    remove use of sprintf in py-finishbreakpoint.c...

    ... and replace it with call to xsnprintf, following GDB's coding rules.

    gdb/ChangeLog:

            * py-finishbreakpoint.c (bpfinishpy_init): Replace call to
            sprintf by call to xsnprintf.

diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
index a2d8165..c158d47 100644
--- a/gdb/python/py-finishbreakpoint.c
+++ b/gdb/python/py-finishbreakpoint.c
@@ -277,7 +277,7 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
     {
       /* Set a breakpoint on the return address.  */
       finish_pc = get_frame_pc (prev_frame);
-      sprintf (small_buf, "*%s", hex_string (finish_pc));
+      xsnprintf (small_buf, sizeof (small_buf), "*%s", hex_string (finish_pc));
       addr_str = small_buf;
 
       create_breakpoint (python_gdbarch,

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

* Re: [RFC] Python Finish Breakpoints
       [not found]                                                                                     ` <CAPftXULKC8gp3L87+PZEF3dj3crv9bh-uzZpRiYKjqEw_xyptQ@mail.gmail.com>
  2011-12-27  4:18                                                                                       ` Joel Brobecker
@ 2011-12-27  9:34                                                                                       ` Kevin Pouget
  1 sibling, 0 replies; 74+ messages in thread
From: Kevin Pouget @ 2011-12-27  9:34 UTC (permalink / raw)
  To: gdb-patches

On Wed, Dec 21, 2011 at 6:11 PM, Tom Tromey <tromey@redhat.com> wrote:
> So, I think it is ok for 7.4.

(sorry for the repost/delay, X-mas break and HTML-only mail client ...)

From: Kevin Pouget <kevin.pouget@gmail.com>
Date: Sat, Dec 24, 2011 at 9:18 AM

so it's now commited in the trunk
http://sourceware.org/ml/gdb-cvs/2011-12/msg00230.html
and in the 7.4 branch
http://sourceware.org/ml/gdb-cvs/2011-12/msg00231.html
http://sourceware.org/ml/gdb-cvs/2011-12/msg00232.html

Thanks a lot for helping me reviewing this patch and making it
compatible with GDB standards, it was quite a long journey!

(i won't be able to fix the ARI until the mid of next week but i'll
take a look at that ASAP and for sure before the release)

Thanks,

Kevin

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

* Re: [RFC] Python Finish Breakpoints
  2011-12-27  4:18                                                                                       ` Joel Brobecker
@ 2011-12-27  9:40                                                                                         ` Kevin Pouget
  2011-12-27 12:22                                                                                           ` Joel Brobecker
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-12-27  9:40 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: Tom Tromey, gdb-patches

On Tue, Dec 27, 2011 at 5:16 AM, Joel Brobecker <brobecker@adacore.com> wrote:
>> (i won't be able to fix the ARI until the mid of next week but i'll take a
>> look at that ASAP and for sure before the release)
>
> I've checked in a fix in HEAD. Tested on x86_64-linux without any
> regression. Not sure if we'll want it for the 7.4 branch or not.
>
> --
> Joel

Thanks for this fix,
Just out of curiosity, why wouldn't we want it for the 7.4 branch?


Kevin

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

* Re: [patch] Fix gdb.python/py-finish-breakpoint.exp new FAIL on x86_64-m32 [Re: [RFC] Python Finish Breakpoints]
  2011-12-24 23:56                                                                           ` [patch] Fix gdb.python/py-finish-breakpoint.exp new FAIL on x86_64-m32 [Re: [RFC] Python Finish Breakpoints] Jan Kratochvil
@ 2011-12-27 11:13                                                                             ` Kevin Pouget
  2011-12-27 21:39                                                                               ` [commit] " Jan Kratochvil
  2012-01-04 17:45                                                                             ` Ulrich Weigand
  1 sibling, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2011-12-27 11:13 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Tom Tromey, gdb-patches

On Sat, Dec 24, 2011 at 2:56 PM, Jan Kratochvil
<jan.kratochvil@redhat.com> wrote:
> On Tue, 20 Dec 2011 21:54:47 +0100, Kevin Pouget wrote:
>> Great! I'll fix this last bits and commit it in the next few days
>
> +Continuing.
> +process 23029 is executing new program: /bin/echo
> +warning: Selected architecture i386:x86-64 is not compatible with reported target architecture i386
> +Architecture of file not recognized.
> +(gdb) FAIL: gdb.python/py-finish-breakpoint.exp: catch out of scope after exec
>
> On x86_64 with -m32 testcases compilation.
>
> OK to check in this patch?


it makes sense to me, and it also removes an unnecessary dependency
with /bin/echo

thanks for catching that,


Kevin

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

* Re: [RFC] Python Finish Breakpoints
  2011-12-27  9:40                                                                                         ` Kevin Pouget
@ 2011-12-27 12:22                                                                                           ` Joel Brobecker
  0 siblings, 0 replies; 74+ messages in thread
From: Joel Brobecker @ 2011-12-27 12:22 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: Tom Tromey, gdb-patches

> > I've checked in a fix in HEAD. Tested on x86_64-linux without any
> > regression. Not sure if we'll want it for the 7.4 branch or not.
> 
> Thanks for this fix,
> Just out of curiosity, why wouldn't we want it for the 7.4 branch?

I should say first that I haven't rejected the fix for the branch.
One thing to consider, however, is the fact that this patch does
not actually fix a problem. With a buffer of 100 characters, I don't
see how we could overflow.  On the other hand, it seems pretty
straightforward and could add an extra guard. You never know.

-- 
Joel

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

* [commit] [patch] Fix gdb.python/py-finish-breakpoint.exp new FAIL on x86_64-m32 [Re: [RFC] Python Finish Breakpoints]
  2011-12-27 11:13                                                                             ` Kevin Pouget
@ 2011-12-27 21:39                                                                               ` Jan Kratochvil
  0 siblings, 0 replies; 74+ messages in thread
From: Jan Kratochvil @ 2011-12-27 21:39 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: Tom Tromey, gdb-patches

On Tue, 27 Dec 2011 10:39:30 +0100, Kevin Pouget wrote:
> On Sat, Dec 24, 2011 at 2:56 PM, Jan Kratochvil
> <jan.kratochvil@redhat.com> wrote:
> > On Tue, 20 Dec 2011 21:54:47 +0100, Kevin Pouget wrote:
> >> Great! I'll fix this last bits and commit it in the next few days
> >
> > +Continuing.
> > +process 23029 is executing new program: /bin/echo
> > +warning: Selected architecture i386:x86-64 is not compatible with reported target architecture i386
> > +Architecture of file not recognized.
> > +(gdb) FAIL: gdb.python/py-finish-breakpoint.exp: catch out of scope after exec
> >
> > On x86_64 with -m32 testcases compilation.
> >
> > OK to check in this patch?
> 
> it makes sense to me, and it also removes an unnecessary dependency

Checked in:
	http://sourceware.org/ml/gdb-cvs/2011-12/msg00249.html


Thanks,
Jan

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

* [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver)  [rediff]
  2011-12-26 11:28                                                                           ` [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver) " Jan Kratochvil
@ 2011-12-27 23:30                                                                             ` Jan Kratochvil
  2012-01-02 17:57                                                                               ` Tom Tromey
  2012-01-02 19:49                                                                               ` Pedro Alves
  0 siblings, 2 replies; 74+ messages in thread
From: Jan Kratochvil @ 2011-12-27 23:30 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey, Kevin Pouget

[rediff on top of HEAD]

On Sun, 25 Dec 2011 12:37:45 +0100, Jan Kratochvil wrote:
Hi,

with gdbserver as from
	http://sourceware.org/gdb/wiki/TestingGDB#Testing_gdbserver_in_a_native_configuration

it will crash:

(gdb) PASS: gdb.python/py-finish-breakpoint.exp: set FinishBP after the exit()
continue
Continuing.
Child exited with status 0
GDBserver exiting
[Inferior 1 (Remote target) exited normally]
SimpleFinishBreakpoint out of scope
ERROR: Process no longer exists
Program terminated with signal 11, Segmentation fault.
#0  0x00000000007d48b9 in serial_debug_p (scb=0x0) at serial.c:584
584       return scb->debug_p || global_serial_debug_p;
(gdb) p scb
$1 = (struct serial *) 0x0
(gdb) bt
#0  in serial_debug_p (scb=0x0) at serial.c:584
#1  in serial_write (scb=0x0, str=0x7fff727e3300 "$z0,4008a3,1#93", len=15) at serial.c:427
#2  in putpkt_binary (buf=0x2a279b0 "z0,4008a3,1", cnt=11) at remote.c:6891
#3  in putpkt (buf=0x2a279b0 "z0,4008a3,1") at remote.c:6823
#4  in remote_remove_breakpoint (gdbarch=0x28e10f0, bp_tgt=0x2fb3d38) at remote.c:7749
#5  in target_remove_breakpoint (gdbarch=0x28e10f0, bp_tgt=0x2fb3d38) at target.c:2422
#6  in bkpt_remove_location (bl=0x2fb3cd0) at breakpoint.c:10967
#7  in remove_breakpoint_1 (bl=0x2fb3cd0, is=mark_uninserted) at breakpoint.c:2654
#8  in remove_breakpoint (bl=0x2fb3cd0, is=mark_uninserted) at breakpoint.c:2760
#9  in update_global_location_list (should_insert=0) at breakpoint.c:10539
#10 in delete_breakpoint (bpt=0x2f59290) at breakpoint.c:11392
#11 in bpfinishpy_out_of_scope (bpfinish_obj=0x7fdf3c9f8130) at ./python/py-finishbreakpoint.c:327
#12 in bpfinishpy_detect_out_scope_cb (b=0x2f59290, args=0x0) at ./python/py-finishbreakpoint.c:356
#13 in iterate_over_breakpoints (callback=0x65150d <bpfinishpy_detect_out_scope_cb>, data=0x0) at breakpoint.c:13385
#14 in bpfinishpy_handle_exit (inf=0x281d330) at ./python/py-finishbreakpoint.c:393
#15 in observer_inferior_exit_notification_stub (data=0x6516b1, args_data=0x7fff727e3740) at observer.inc:887
#16 in generic_observer_notify (subject=0x284f300, args=0x7fff727e3740) at observer.c:168
#17 in observer_notify_inferior_exit (inf=0x281d330) at observer.inc:912
#18 in exit_inferior_1 (inftoex=0x281d330, silent=1) at inferior.c:276
#19 in exit_inferior_silent (pid=42000) at inferior.c:305
#20 in discard_all_inferiors () at inferior.c:343
#21 in remote_close (quitting=0) at remote.c:2950
#22 in target_close (targ=0x1d19f80, quitting=0) at target.c:3387
#23 in unpush_target (t=0x1d19f80) at target.c:1024
#24 in remote_mourn_1 (target=0x1d19f80) at remote.c:7456
#25 in remote_mourn (ops=0x1d19f80) at remote.c:7449
#26 in target_mourn_inferior () at target.c:2747
#27 in handle_inferior_event (ecs=0x7fff727e3c30) at infrun.c:3408
#28 in wait_for_inferior () at infrun.c:2711
#29 in proceed (addr=18446744073709551615, siggnal=TARGET_SIGNAL_DEFAULT, step=0) at infrun.c:2276
#30 in continue_1 (all_threads=0) at infcmd.c:713
#31 in continue_command (args=0x0, from_tty=1) at infcmd.c:805


Reproducible with:
../gdbserver/gdbserver :1234 gdb.python/py-finish-breakpoint
gdb -nx -x ~/.gdbinit -ex r --args ../gdb -nx -ex 'file gdb.python/py-finish-breakpoint' -ex 'target remote localhost:1234' -ex 'b test_exec_exit' -ex c -ex 'source gdb.python/py-finish-breakpoint.py' -ex 'python SimpleFinishBreakpoint(gdb.newest_frame())' -ex c

I have seen this serial_debug_p crash in various GDB debugging cases but they
were not well reproducible.  I understand this bug is unrelated to
gdb.python/py-finish-breakpoint.exp .

I also tried to reorder remote_close a bit but this patch seems as the right
one to me.

The .exp patch fixes in fact an unrelated testcase but both happen with
gdbserver and py-finish-breakpoint.exp.

The testcase has been tested on x86_64-fedorarawhide-linux-gnu.


Thanks,
Jan


gdb/
2011-12-27  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* remote.c (remote_remove_breakpoint): Return 0 on NULL REMOTE_DESC.

gdb/testsuite/
2011-12-27  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.python/py-finish-breakpoint.exp (catch out of scope after exec):
	Make it unsupported with gdbserver.

--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -7734,6 +7734,13 @@ remote_remove_breakpoint (struct gdbarch *gdbarch,
   CORE_ADDR addr = bp_tgt->placed_address;
   struct remote_state *rs = get_remote_state ();
 
+  if (remote_desc == NULL)
+    {
+      /* remote_close is being executed as our caller.  Avoid error by
+	 reporting successful removal of the breakpoint.  */
+      return 0;
+    }
+
   if (remote_protocol_packets[PACKET_Z0].support != PACKET_DISABLE)
     {
       char *p = rs->buf;
--- a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp
@@ -262,4 +262,17 @@ if ![runto "test_exec_exit"] then {
 
 gdb_test "python SimpleFinishBreakpoint(gdb.newest_frame())" "SimpleFinishBreakpoint init" "set FinishBP after the exec"
 gdb_test "catch exec" "Catchpoint.*\(exec\).*" "catch exec"
-gdb_test "continue" "SimpleFinishBreakpoint out of scope.*" "catch out of scope after exec"
+
+set test "catch out of scope after exec"
+gdb_test_multiple "continue" $test {
+    -re "warning: Error inserting catchpoint \[0-9\]+: Your system does not support this type\[ \r\n\]+of catchpoint\\.\r\n.*\r\n$gdb_prompt $" {
+	if [is_remote target] {
+	    unsupported $test
+	} else {
+	    fail $test
+	}
+    }
+    -re "SimpleFinishBreakpoint out of scope\r\n$gdb_prompt $" {
+	pass $test
+    }
+}

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

* Re: [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver)  [rediff]
  2011-12-27 23:30                                                                             ` [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver) [rediff] Jan Kratochvil
@ 2012-01-02 17:57                                                                               ` Tom Tromey
  2012-01-02 19:49                                                                               ` Pedro Alves
  1 sibling, 0 replies; 74+ messages in thread
From: Tom Tromey @ 2012-01-02 17:57 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, Kevin Pouget

>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:

Jan> with gdbserver as from
Jan> 	http://sourceware.org/gdb/wiki/TestingGDB#Testing_gdbserver_in_a_native_configuration
Jan> it will crash:
[...]

See

    http://sourceware.org/ml/gdb-patches/2011-12/msg00676.html

Given that two people now have come up with your fix, I think you should
just put it in.

Tom

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

* Re: [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver)  [rediff]
  2011-12-27 23:30                                                                             ` [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver) [rediff] Jan Kratochvil
  2012-01-02 17:57                                                                               ` Tom Tromey
@ 2012-01-02 19:49                                                                               ` Pedro Alves
  2012-01-19 16:24                                                                                 ` Call target_close after unpushing, not before (was: Re: [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver)) Pedro Alves
  1 sibling, 1 reply; 74+ messages in thread
From: Pedro Alves @ 2012-01-02 19:49 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, Tom Tromey, Kevin Pouget

On 12/27/2011 11:23 PM, Jan Kratochvil wrote:
> [rediff on top of HEAD]
>
> On Sun, 25 Dec 2011 12:37:45 +0100, Jan Kratochvil wrote:
> Hi,
>
> with gdbserver as from
> 	http://sourceware.org/gdb/wiki/TestingGDB#Testing_gdbserver_in_a_native_configuration
>
> it will crash:
>
> (gdb) PASS: gdb.python/py-finish-breakpoint.exp: set FinishBP after the exit()
> continue
> Continuing.
> Child exited with status 0
> GDBserver exiting
> [Inferior 1 (Remote target) exited normally]
> SimpleFinishBreakpoint out of scope
> ERROR: Process no longer exists
> Program terminated with signal 11, Segmentation fault.
> #0  0x00000000007d48b9 in serial_debug_p (scb=0x0) at serial.c:584
> 584       return scb->debug_p || global_serial_debug_p;
> (gdb) p scb
> $1 = (struct serial *) 0x0
> (gdb) bt
> #0  in serial_debug_p (scb=0x0) at serial.c:584
> #1  in serial_write (scb=0x0, str=0x7fff727e3300 "$z0,4008a3,1#93", len=15) at serial.c:427
> #2  in putpkt_binary (buf=0x2a279b0 "z0,4008a3,1", cnt=11) at remote.c:6891
> #3  in putpkt (buf=0x2a279b0 "z0,4008a3,1") at remote.c:6823
> #4  in remote_remove_breakpoint (gdbarch=0x28e10f0, bp_tgt=0x2fb3d38) at remote.c:7749
> #5  in target_remove_breakpoint (gdbarch=0x28e10f0, bp_tgt=0x2fb3d38) at target.c:2422
> #6  in bkpt_remove_location (bl=0x2fb3cd0) at breakpoint.c:10967
> #7  in remove_breakpoint_1 (bl=0x2fb3cd0, is=mark_uninserted) at breakpoint.c:2654
> #8  in remove_breakpoint (bl=0x2fb3cd0, is=mark_uninserted) at breakpoint.c:2760
> #9  in update_global_location_list (should_insert=0) at breakpoint.c:10539
> #10 in delete_breakpoint (bpt=0x2f59290) at breakpoint.c:11392
> #11 in bpfinishpy_out_of_scope (bpfinish_obj=0x7fdf3c9f8130) at ./python/py-finishbreakpoint.c:327
> #12 in bpfinishpy_detect_out_scope_cb (b=0x2f59290, args=0x0) at ./python/py-finishbreakpoint.c:356
> #13 in iterate_over_breakpoints (callback=0x65150d<bpfinishpy_detect_out_scope_cb>, data=0x0) at breakpoint.c:13385
> #14 in bpfinishpy_handle_exit (inf=0x281d330) at ./python/py-finishbreakpoint.c:393
> #15 in observer_inferior_exit_notification_stub (data=0x6516b1, args_data=0x7fff727e3740) at observer.inc:887
> #16 in generic_observer_notify (subject=0x284f300, args=0x7fff727e3740) at observer.c:168
> #17 in observer_notify_inferior_exit (inf=0x281d330) at observer.inc:912
> #18 in exit_inferior_1 (inftoex=0x281d330, silent=1) at inferior.c:276
> #19 in exit_inferior_silent (pid=42000) at inferior.c:305
> #20 in discard_all_inferiors () at inferior.c:343
> #21 in remote_close (quitting=0) at remote.c:2950
> #22 in target_close (targ=0x1d19f80, quitting=0) at target.c:3387
> #23 in unpush_target (t=0x1d19f80) at target.c:1024
> #24 in remote_mourn_1 (target=0x1d19f80) at remote.c:7456
> #25 in remote_mourn (ops=0x1d19f80) at remote.c:7449
> #26 in target_mourn_inferior () at target.c:2747
> #27 in handle_inferior_event (ecs=0x7fff727e3c30) at infrun.c:3408
> #28 in wait_for_inferior () at infrun.c:2711
> #29 in proceed (addr=18446744073709551615, siggnal=TARGET_SIGNAL_DEFAULT, step=0) at infrun.c:2276
> #30 in continue_1 (all_threads=0) at infcmd.c:713
> #31 in continue_command (args=0x0, from_tty=1) at infcmd.c:805
>
>
> Reproducible with:
> ../gdbserver/gdbserver :1234 gdb.python/py-finish-breakpoint
> gdb -nx -x ~/.gdbinit -ex r --args ../gdb -nx -ex 'file gdb.python/py-finish-breakpoint' -ex 'target remote localhost:1234' -ex 'b test_exec_exit' -ex c -ex 'source gdb.python/py-finish-breakpoint.py' -ex 'python SimpleFinishBreakpoint(gdb.newest_frame())' -ex c
>
> I have seen this serial_debug_p crash in various GDB debugging cases but they
> were not well reproducible.  I understand this bug is unrelated to
> gdb.python/py-finish-breakpoint.exp .
>
> I also tried to reorder remote_close a bit but this patch seems as the right
> one to me.

The issue is that the target is already gone, and we're going through
teardown.  What I think would be a bit better is to add a new function
to breakpoint.c, similar to mark_breakpoints_out, like:

void
mark_all_breakpoints_out (void)
{
   struct bp_location *bl, **blp_tmp;

   ALL_BP_LOCATIONS (bl, blp_tmp)
     bl->inserted = 0;
}

and call that from within remote_close, before discard_all_inferiors.

remote_desc == NULL checks throughout remote.c are plain hacks.

Even better would be to do discard_all_inferiors after target_close,
though that's tricky.  Perhaps it'd work to do it only if the target
just closed is of process_stratum.

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

* Re: [RFC] Python Finish Breakpoints
  2011-12-09  9:53                                                                     ` Kevin Pouget
  2011-12-18 19:22                                                                       ` Kevin Pouget
  2011-12-20 20:55                                                                       ` Tom Tromey
@ 2012-01-04 14:49                                                                       ` Ulrich Weigand
  2012-01-04 15:24                                                                         ` Kevin Pouget
  2012-01-04 16:40                                                                         ` Tom Tromey
  2 siblings, 2 replies; 74+ messages in thread
From: Ulrich Weigand @ 2012-01-04 14:49 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: Tom Tromey, gdb-patches, brobecker

Kevin Pouget wrote:

> 	* gdb.python/py-finish-breakpoint2.cc: New file.
> 	* gdb.python/py-finish-breakpoint2.exp: New file.
> 	* gdb.python/py-finish-breakpoint2.py: New file.

I'm seeing those fail on various platforms (i386, ppc, ppc64):
FAIL: gdb.python/py-finish-breakpoint2.exp: check FinishBreakpoint in catch()
FAIL: gdb.python/py-finish-breakpoint2.exp: check finish BP removal
FAIL: gdb.python/py-finish-breakpoint2.exp: continue to second exception
FAIL: gdb.python/py-finish-breakpoint2.exp: set FinishBP after the exception

However in other cases the test succeeds -- this appears to be related to
the particular compiler version that's being used.

The problem is that the finish-breakpoint mechanism sets a breakpoint on
the regular return address of the current frame, i.e. the instruction
where the "call" instruction would return normally.  However, when an
exception is thrown and then caught, execution continues at a different
call site (in the catch block).  There's now two possibilities:

  try
    {
      throw_exception_1 (10);
    }
  catch (const int *e)
    {
        std::cerr << "Exception #" << *e << std::endl;
    }
  i += 1; /* Break after exception 1.  */


A) The instruction immediately following the "call" instruction to
   throw_exception_1 is actually already one of the instructions used
   to implement the "i += 1" line, and code flow after executing the
   catch block branches back to that location.  I.e. we have a call
   graph along the lines of:

   [...]
       call throw_exception_1 (10)  [ set up catch block at Lc ]
   Lx: compute i += 1
   [...]

   Lc: call std:cerr << ...
       goto Lx

  and the finish breakpoint gets set just at label Lx, and it will
  get hit both after a regular return and after an exception.

B) The instruction immediately following the "call" instruction
   is still part of the (clean up after the) call, or some other
   code flow instruction, and the instructions used to implement
   "i += 1" are elsewhere.  I.e. the call graph looks more like:

   [...]
       call throw_exception_1 (10)  [ set up catch block at Lc ]
   Lx: goto Ly
  
   Lc: call std:cerr <<< ...

   Ly: compute i += 1
   [...]

   In this case the finish breakpoint gets set at Lx, which *never*
   gets executed after an exception.


It seems to me that current GDB code does not (even attempt to) properly
handle the "finish" command and/or the new finish-breakpoint capability
in the presence of exceptions.  Note that even in case A) above, where
the finish breakpoint does hit, it arguably hits in the wrong location:
at the "finish" of the throw_exception_1 line, execution continues *in
the catch block*, so we should stop at the start of the catch block
instead of after it has completed.

Am I missing some piece of GDB code that attempts to handle this, and
is just malfunctioning?  It would certainly be good to properly support
this feature, but until we do, I'd suggest this test case should be
disabled ...

Bye,
Ulrich

-- 
  Dr. Ulrich Weigand
  GNU Toolchain for Linux on System z and Cell BE
  Ulrich.Weigand@de.ibm.com

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

* Re: [RFC] Python Finish Breakpoints
  2012-01-04 14:49                                                                       ` [RFC] Python Finish Breakpoints Ulrich Weigand
@ 2012-01-04 15:24                                                                         ` Kevin Pouget
  2012-01-04 16:29                                                                           ` Ulrich Weigand
  2012-01-04 16:42                                                                           ` Tom Tromey
  2012-01-04 16:40                                                                         ` Tom Tromey
  1 sibling, 2 replies; 74+ messages in thread
From: Kevin Pouget @ 2012-01-04 15:24 UTC (permalink / raw)
  To: Ulrich Weigand; +Cc: Tom Tromey, gdb-patches, brobecker

On Wed, Jan 4, 2012 at 3:49 PM, Ulrich Weigand <uweigand@de.ibm.com> wrote:
> Kevin Pouget wrote:
>
>>       * gdb.python/py-finish-breakpoint2.cc: New file.
>>       * gdb.python/py-finish-breakpoint2.exp: New file.
>>       * gdb.python/py-finish-breakpoint2.py: New file.
>
> I'm seeing those fail on various platforms (i386, ppc, ppc64):
> FAIL: gdb.python/py-finish-breakpoint2.exp: check FinishBreakpoint in catch()
> FAIL: gdb.python/py-finish-breakpoint2.exp: check finish BP removal
> FAIL: gdb.python/py-finish-breakpoint2.exp: continue to second exception
> FAIL: gdb.python/py-finish-breakpoint2.exp: set FinishBP after the exception
>
> However in other cases the test succeeds -- this appears to be related to
> the particular compiler version that's being used.
>
> The problem is that the finish-breakpoint mechanism sets a breakpoint on
> the regular return address of the current frame, i.e. the instruction
> where the "call" instruction would return normally.  However, when an
> exception is thrown and then caught, execution continues at a different
> call site (in the catch block).  There's now two possibilities:
>
>  try
>    {
>      throw_exception_1 (10);
>    }
>  catch (const int *e)
>    {
>        std::cerr << "Exception #" << *e << std::endl;
>    }
>  i += 1; /* Break after exception 1.  */
>
>
> A) The instruction immediately following the "call" instruction to
>   throw_exception_1 is actually already one of the instructions used
>   to implement the "i += 1" line, and code flow after executing the
>   catch block branches back to that location.  I.e. we have a call
>   graph along the lines of:
>
>   [...]
>       call throw_exception_1 (10)  [ set up catch block at Lc ]
>   Lx: compute i += 1
>   [...]
>
>   Lc: call std:cerr << ...
>       goto Lx
>
>  and the finish breakpoint gets set just at label Lx, and it will
>  get hit both after a regular return and after an exception.
>
> B) The instruction immediately following the "call" instruction
>   is still part of the (clean up after the) call, or some other
>   code flow instruction, and the instructions used to implement
>   "i += 1" are elsewhere.  I.e. the call graph looks more like:
>
>   [...]
>       call throw_exception_1 (10)  [ set up catch block at Lc ]
>   Lx: goto Ly
>
>   Lc: call std:cerr <<< ...
>
>   Ly: compute i += 1
>   [...]
>
>   In this case the finish breakpoint gets set at Lx, which *never*
>   gets executed after an exception.
>
>
> It seems to me that current GDB code does not (even attempt to) properly
> handle the "finish" command and/or the new finish-breakpoint capability
> in the presence of exceptions.  Note that even in case A) above, where
> the finish breakpoint does hit, it arguably hits in the wrong location:
> at the "finish" of the throw_exception_1 line, execution continues *in
> the catch block*, so we should stop at the start of the catch block
> instead of after it has completed.
>
> Am I missing some piece of GDB code that attempts to handle this, and
> is just malfunctioning?  It would certainly be good to properly support
> this feature, but until we do, I'd suggest this test case should be
> disabled ...

Hello,

thanks for spotting and explaining this bug

I can say at least that there is no code to handle this situation in
my patch. One thing I wonder is what's the situation C which passes
the test in my environment ? (x86_64)

My knowledge about C++ is quite limited, but based on your
descriptions, a proper support would involve another breakpoint, set
at Lc, to disable the FinishBreakpoint ... but I'm not sure how
computable this Lc is.

I'll let Tom answer about what to do with the testsuite.


Thanks,

Kevin

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

* Re: [RFC] Python Finish Breakpoints
  2012-01-04 15:24                                                                         ` Kevin Pouget
@ 2012-01-04 16:29                                                                           ` Ulrich Weigand
  2012-01-04 16:42                                                                           ` Tom Tromey
  1 sibling, 0 replies; 74+ messages in thread
From: Ulrich Weigand @ 2012-01-04 16:29 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: Tom Tromey, gdb-patches, brobecker

Kevin Pouget wrote:
> I can say at least that there is no code to handle this situation in
> my patch. One thing I wonder is what's the situation C which passes
> the test in my environment ? (x86_64)

Hmm, with C the problem potentially exists as well:

  if (setjmp (env) == 0) /* longjmp caught */
    {
      call_longjmp (&env);
    }
  else
    j += 1; /* after longjmp.  */


The finish breakpoint is set immediately after the call to call_longjmp,
which may or may not coincide with the first instruction of "j += 1".

I guess since the compiler's control flow graph is much simpler as there
are no implicit exception edges (rather, setjmp is a "regular" function
that just happens to "return twice"), it is more likely for GCC to
arrange the resulting assembler in the sequence where everything
appears to work ...

You might be able to force the problem to consistently appear by adding
extra code after call_longjmp, e.g. like so:

  if (setjmp (env) == 0) /* longjmp caught */
    {
      call_longjmp (&env);
      some_other_stuff ();
    }
  else
    j += 1; /* after longjmp.  */


On the other hand, (at least on some platforms), GDB will actively
track calls to longjmp by setting a breakpoint on longjmp and then
stepping through until the destination.  This might give GDB a
chance to notice that the finish breakpoint has gone out of scope
by the longjmp, and trigger the break then.  That would probably
be the best way to implement this feature anyway ...


> My knowledge about C++ is quite limited, but based on your
> descriptions, a proper support would involve another breakpoint, set
> at Lc, to disable the FinishBreakpoint ... but I'm not sure how
> computable this Lc is.

Not very; you could attempt to decode the exception tables, but those
are platform-specific.  It might be preferable to handle "throw"
similar to how we handle "longjmp", see above.


Bye,
Ulrich


-- 
  Dr. Ulrich Weigand
  GNU Toolchain for Linux on System z and Cell BE
  Ulrich.Weigand@de.ibm.com

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

* Re: [RFC] Python Finish Breakpoints
  2012-01-04 14:49                                                                       ` [RFC] Python Finish Breakpoints Ulrich Weigand
  2012-01-04 15:24                                                                         ` Kevin Pouget
@ 2012-01-04 16:40                                                                         ` Tom Tromey
  2012-01-04 17:13                                                                           ` Ulrich Weigand
  1 sibling, 1 reply; 74+ messages in thread
From: Tom Tromey @ 2012-01-04 16:40 UTC (permalink / raw)
  To: Ulrich Weigand; +Cc: Kevin Pouget, gdb-patches, brobecker

>>>>> "Ulrich" == Ulrich Weigand <uweigand@de.ibm.com> writes:

Ulrich> The problem is that the finish-breakpoint mechanism sets a breakpoint on
Ulrich> the regular return address of the current frame, i.e. the instruction
Ulrich> where the "call" instruction would return normally.  However, when an
Ulrich> exception is thrown and then caught, execution continues at a different
Ulrich> call site (in the catch block).  There's now two possibilities:
[...]

I think the test is actually incorrect, and that if it passes, it is
only by accident.

The test assumes that the "finish" will cause the
ExceptionFinishBreakpoint to fire.  However, I don't think this
makes sense.  A Python "finish" breakpoint will only fire if the
"finish" returns normally; it might fire "accidentally" if the
exception lands at exactly the same spot.

So, I think either result -- a call to the stop method or a call to the
out_of_scope method -- is ok in this situation.

Ulrich> Am I missing some piece of GDB code that attempts to handle this, and
Ulrich> is just malfunctioning?  It would certainly be good to properly support
Ulrich> this feature, but until we do, I'd suggest this test case should be
Ulrich> disabled ...

We have support for "finish", and other operations in the presence of
exceptions.  Search for _Unwind_DebugHook (in breakpoint.c and infrun.c).
This code only works if you are using a new-enough version of GCC and
have debuginfo for libgcc (but once the stap probe patches are in, you
will no longer need debuginfo).

We could probably augment Python "finish" breakpoints to add a new
method that is called in the exception case.

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2012-01-04 15:24                                                                         ` Kevin Pouget
  2012-01-04 16:29                                                                           ` Ulrich Weigand
@ 2012-01-04 16:42                                                                           ` Tom Tromey
  1 sibling, 0 replies; 74+ messages in thread
From: Tom Tromey @ 2012-01-04 16:42 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: Ulrich Weigand, gdb-patches, brobecker

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

Kevin> My knowledge about C++ is quite limited, but based on your
Kevin> descriptions, a proper support would involve another breakpoint, set
Kevin> at Lc, to disable the FinishBreakpoint ... but I'm not sure how
Kevin> computable this Lc is.

I think you would have to hook into infrun.c:check_exception_resume
or thereabouts, and also probably add a new method to FinishBreakpoint.

Tom

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

* Re: [RFC] Python Finish Breakpoints
  2012-01-04 16:40                                                                         ` Tom Tromey
@ 2012-01-04 17:13                                                                           ` Ulrich Weigand
  2012-01-09  9:21                                                                             ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Ulrich Weigand @ 2012-01-04 17:13 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Kevin Pouget, gdb-patches, brobecker

Tom Tromey wrote:

> We have support for "finish", and other operations in the presence of
> exceptions.  Search for _Unwind_DebugHook (in breakpoint.c and infrun.c).
> This code only works if you are using a new-enough version of GCC and
> have debuginfo for libgcc (but once the stap probe patches are in, you
> will no longer need debuginfo).

Ah, I see -- that's what I was missing.  Yes, this looks like the
way to handle this situation ...

Thanks,
Ulrich

-- 
  Dr. Ulrich Weigand
  GNU Toolchain for Linux on System z and Cell BE
  Ulrich.Weigand@de.ibm.com

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

* Re: [patch] Fix gdb.python/py-finish-breakpoint.exp new FAIL on x86_64-m32  [Re: [RFC] Python Finish Breakpoints]
  2011-12-24 23:56                                                                           ` [patch] Fix gdb.python/py-finish-breakpoint.exp new FAIL on x86_64-m32 [Re: [RFC] Python Finish Breakpoints] Jan Kratochvil
  2011-12-27 11:13                                                                             ` Kevin Pouget
@ 2012-01-04 17:45                                                                             ` Ulrich Weigand
  2012-01-04 17:58                                                                               ` [commit 7.4] " Jan Kratochvil
  1 sibling, 1 reply; 74+ messages in thread
From: Ulrich Weigand @ 2012-01-04 17:45 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Kevin Pouget, Tom Tromey, gdb-patches, brobecker

Jan Kratochvil wrote:

> +Continuing.
> +process 23029 is executing new program: /bin/echo
> +warning: Selected architecture i386:x86-64 is not compatible with reported target architecture i386
> +Architecture of file not recognized.
> +(gdb) FAIL: gdb.python/py-finish-breakpoint.exp: catch out of scope after exec

> gdb/testsuite/
> 2011-12-24  Jan Kratochvil  <jan.kratochvil@redhat.com>
> 
> 	* gdb.python/py-finish-breakpoint.c (test_exec_exit): Change the
> 	parameter to self_exec, adjust the function.
> 	(main): Check for argv[1] "exit".  Use argv[0] for test_exec_exit.
> 	* gdb.python/py-finish-breakpoint.exp (set var self_exec = 0): New test.
> 	(set var do_exit = 0): Remove test.
> 	(newline at end of file): Add one.

I'm seeing this problem on 7.4 as well ... could you check the testcase fix in
there too?   (I guess Edjunior's follow-on fixes would also be good ...)

Thanks,
Ulrich

-- 
  Dr. Ulrich Weigand
  GNU Toolchain for Linux on System z and Cell BE
  Ulrich.Weigand@de.ibm.com

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

* [commit 7.4] [patch] Fix gdb.python/py-finish-breakpoint.exp new FAIL on x86_64-m32  [Re: [RFC] Python Finish Breakpoints]
  2012-01-04 17:45                                                                             ` Ulrich Weigand
@ 2012-01-04 17:58                                                                               ` Jan Kratochvil
  2012-01-04 18:10                                                                                 ` Ulrich Weigand
  0 siblings, 1 reply; 74+ messages in thread
From: Jan Kratochvil @ 2012-01-04 17:58 UTC (permalink / raw)
  To: Ulrich Weigand
  Cc: Kevin Pouget, Tom Tromey, gdb-patches, brobecker,
	Edjunior Barbosa Machado

On Wed, 04 Jan 2012 18:45:01 +0100, Ulrich Weigand wrote:
> Jan Kratochvil wrote:
> 
> > +Continuing.
> > +process 23029 is executing new program: /bin/echo
> > +warning: Selected architecture i386:x86-64 is not compatible with reported target architecture i386
> > +Architecture of file not recognized.
> > +(gdb) FAIL: gdb.python/py-finish-breakpoint.exp: catch out of scope after exec
> 
> > gdb/testsuite/
> > 2011-12-24  Jan Kratochvil  <jan.kratochvil@redhat.com>
> > 
> > 	* gdb.python/py-finish-breakpoint.c (test_exec_exit): Change the
> > 	parameter to self_exec, adjust the function.
> > 	(main): Check for argv[1] "exit".  Use argv[0] for test_exec_exit.
> > 	* gdb.python/py-finish-breakpoint.exp (set var self_exec = 0): New test.
> > 	(set var do_exit = 0): Remove test.
> > 	(newline at end of file): Add one.
> 
> I'm seeing this problem on 7.4 as well ... could you check the testcase fix in
> there too?

Checked in 7.4 branch:
	http://sourceware.org/ml/gdb-cvs/2012-01/msg00050.html

> (I guess Edjunior's follow-on fixes would also be good ...)

Checked in 7.4 branch also the Edjunior's follow-on when I was at it:
	http://sourceware.org/ml/gdb-cvs/2012-01/msg00051.html


Regards,
Jan

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

* Re: [commit 7.4] [patch] Fix gdb.python/py-finish-breakpoint.exp new FAIL on x86_64-m32  [Re: [RFC] Python Finish Breakpoints]
  2012-01-04 17:58                                                                               ` [commit 7.4] " Jan Kratochvil
@ 2012-01-04 18:10                                                                                 ` Ulrich Weigand
  0 siblings, 0 replies; 74+ messages in thread
From: Ulrich Weigand @ 2012-01-04 18:10 UTC (permalink / raw)
  To: Jan Kratochvil
  Cc: Kevin Pouget, Tom Tromey, gdb-patches, brobecker,
	Edjunior Barbosa Machado

Jan Kratochvil wrote:
> On Wed, 04 Jan 2012 18:45:01 +0100, Ulrich Weigand wrote:
> > I'm seeing this problem on 7.4 as well ... could you check the testcase fix in
> > there too?
> 
> Checked in 7.4 branch:
> 	http://sourceware.org/ml/gdb-cvs/2012-01/msg00050.html
> 
> > (I guess Edjunior's follow-on fixes would also be good ...)
> 
> Checked in 7.4 branch also the Edjunior's follow-on when I was at it:
> 	http://sourceware.org/ml/gdb-cvs/2012-01/msg00051.html

Thanks, Jan!

Bye,
Ulrich

-- 
  Dr. Ulrich Weigand
  GNU Toolchain for Linux on System z and Cell BE
  Ulrich.Weigand@de.ibm.com

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

* Re: [RFC] Python Finish Breakpoints
  2012-01-04 17:13                                                                           ` Ulrich Weigand
@ 2012-01-09  9:21                                                                             ` Kevin Pouget
  2012-01-27 17:01                                                                               ` Kevin Pouget
  0 siblings, 1 reply; 74+ messages in thread
From: Kevin Pouget @ 2012-01-09  9:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey, Ulrich Weigand

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

On Wed, Jan 4, 2012 at 6:12 PM, Ulrich Weigand <uweigand@de.ibm.com> wrote:
> Tom Tromey wrote:
>
>> We have support for "finish", and other operations in the presence of
>> exceptions.  Search for _Unwind_DebugHook (in breakpoint.c and infrun.c).
>> This code only works if you are using a new-enough version of GCC and
>> have debuginfo for libgcc (but once the stap probe patches are in, you
>> will no longer need debuginfo).
>
> Ah, I see -- that's what I was missing.  Yes, this looks like the
> way to handle this situation ...

Hello,

I would like to discuss this patch which intends to fix and improve
the handling for c++ exceptions with the Python FinishBreakpoint.

Based on what I understood of the existing mechanisms which handle
longjmps and and exception, I extended the "momentary breakpoints"
with a "nostop" flag. Indeed, their initial behavior/goal was to stop
unconditionally the execution, whereas I just wanted to receive a
notification when the relevant code location was hit (namely
infrun.c::BPSTAT_WHAT_CLEAR_LONGJMP_RESUME). The nostop flag is
propagated step by step (bp by bp) from the exception throw to its
catchpoint, where I check if the FinishBreakpoint have ran out of
their scope.

As Tom mentioned, c++ debug info are needed for this machinery to work
(otherwise, FinishBreakpoint will fail the same way GDB fails: stop at
the beginning of the catch(), never stop, ...) -- I used xfail in the
testsuite to refer to this situation, I'm not sure that's correct).

I've also fixed a bug which triggered twice the out_of_scope
notification in case of inferior exit (observer_attach_normal_stop and
observer_attach_inferior_exit are both triggered when the inferior
exits)

(This is just a preliminary patch, I'll would like to prepare some
more tests with exceptions and longjmps)

Tested with no regression on f15/X86_64

( these always fail on my system:
 Running PATH/git/gdb/gdb/testsuite/gdb.base/longjmp.exp ...
 FAIL: gdb.base/longjmp.exp: next over longjmp(1)
 FAIL: gdb.base/longjmp.exp: next over call_longjmp (2)
 FAIL: gdb.base/longjmp.exp: next over patt3
)

Thanks for your comments,

Kevin

--

2012-01-09  Kevin Pouget <kevin.pouget@st.com>

	* breakpoint.c (momentary_nostop_breakpoint_ops): New variable.
	(bpstat_what): Set field retval.is_nostop when necessary.
	(set_longjmp_breakpoint): Add parameter nostop and create nostop
	momentary breakpoint ops if necessary.
	(set_momentary_breakpoint): Likewise.
	(set_momentary_breakpoint_at_pc): Likewise.
	(momentary_breakpoint_is_nostop): New function.
	(until_break_command): Call set_momentary_breakpoint and
	set_longjmp_breakpoint with nostop not set.
	(momentary_stop_bkpt_check_status): Rename from
	momentary_bkpt_check_status.
	(momentary_bkpt_check_status): Remove.
	(momentary_nostop_bkpt_check_status): New function.
	(initialize_breakpoint_ops): Initialize
	momentary_breakpoint_ops.check_status to
	momentary_stop_bkpt_check_status, initialize
	momentary_nostop_breakpoint_ops.
	* breakpoint.h (struct bpstat_what): New field: is_nostop.
	(set_momentary_breakpoint): New parameter: nostop.
	(set_momentary_breakpoint_at_pc): Liklewise.
	(set_longjmp_breakpoint): Likewise.
	(momentary_breakpoint_is_nostop): New prototype.
	* arm-linux-tdep.c (arm_linux_copy_svc): Add nostop parameter to
        set_momentary_breakpoint call.
	* elfread.c: (elf_gnu_ifunc_resolver_stop): Likewise
	* infcall.c (call_function_by_hand): Likewise.
	* infcmd.c: (finish_forward): Likewise, add nostop parameter to
        set_longjmp_breakpoint call.
	(step_1): Add nostop parameter to set_longjmp_breakpoint call.
	(until_next_command): Likewise.
	* infrun.c: Include python/python.h.
	(insert_step_resume_breakpoint_at_sal_1): Add nostop parameter to
        set_momentary_breakpoint call.
	(insert_longjmp_resume_breakpoint): Add nostop parameter to prototype.
	Add nostop parameter to set_momentary_breakpoint_at_pc call.
	(insert_exception_resume_breakpoint): Likewise.
	(check_exception_resume): Add nostop parameter to prototype. Add nostop
        parameter to insert_exception_resume_breakpoint call.
	(process_event_stop_test): Set and use nostop to prevent GDB from
	stopping the execution, trigger gdbpy_bpfinish_handle_exception when
	necessary.
	* python/py-finishbreakpoint.c (bpfinishpy_init): Call
	set_longjmp_breakpoint.
	(bpfinishpy_detect_out_scope): Renamed from
	bpfinishpy_detect_out_scope_cb. Cleaned up. Add inc_current parameter.
	(bpfinishpy_detect_out_scope_cb): Removed.
	(bpfinishpy_detect_out_scope_bp_cb): New function.
	(bpfinishpy_detect_out_scope_exception_cb): New function.
	(bpfinishpy_handle_stop): Update with function name
	bpfinishpy_detect_out_scope_bp_cb.
	(bpfinishpy_handle_exit): Removed.
	(gdbpy_bpfinish_handle_exception): New function.
	(gdbpy_initialize_finishbreakpoints): Remove call to
	bpfinishpy_handle_exit.
	* python/python.c (gdbpy_bpfinish_handle_exception): New function.
	* python/python.h (gdbpy_bpfinish_handle_exception): New prototype.

testsuite/
	* gdb.python/py-finish-breakpoint2.exp: Strengthen the exception tests.

[-- Attachment #2: 0001-better-exception-support.patch --]
[-- Type: text/x-patch, Size: 27107 bytes --]

From 75cd8a288787550a58c9dd3a7db1b5b0c99c587f Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Fri, 6 Jan 2012 14:14:49 +0100
Subject: [PATCH] better exception support

---
 gdb/arm-linux-tdep.c                               |    2 +-
 gdb/breakpoint.c                                   |   68 +++++++++++----
 gdb/breakpoint.h                                   |   13 ++-
 gdb/elfread.c                                      |    3 +-
 gdb/infcall.c                                      |    3 +-
 gdb/infcmd.c                                       |    9 +-
 gdb/infrun.c                                       |   46 +++++++---
 gdb/python/py-finishbreakpoint.c                   |   96 +++++++++++++-------
 gdb/python/python.c                                |    6 ++
 gdb/python/python.h                                |    2 +
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   22 ++++-
 11 files changed, 194 insertions(+), 76 deletions(-)

diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c
index ac4860c..8479839 100644
--- a/gdb/arm-linux-tdep.c
+++ b/gdb/arm-linux-tdep.c
@@ -935,7 +935,7 @@ arm_linux_copy_svc (struct gdbarch *gdbarch, struct regcache *regs,
 	    {
 	      inferior_thread ()->control.step_resume_breakpoint
         	= set_momentary_breakpoint (gdbarch, sal, get_frame_id (frame),
-					    bp_step_resume);
+					    bp_step_resume, /* nostop */ 0);
 
 	      /* We need to make sure we actually insert the momentary
 	         breakpoint set above.  */
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 6bcedc4..e578aed 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -257,6 +257,7 @@ static struct breakpoint_ops internal_breakpoint_ops;
 
 /* Momentary breakpoints class type.  */
 static struct breakpoint_ops momentary_breakpoint_ops;
+static struct breakpoint_ops momentary_nostop_breakpoint_ops;
 
 /* The breakpoint_ops structure to be used in regular user created
    breakpoints.  */
@@ -4308,6 +4309,7 @@ bpstat_what (bpstat bs_head)
   retval.main_action = BPSTAT_WHAT_KEEP_CHECKING;
   retval.call_dummy = STOP_NONE;
   retval.is_longjmp = 0;
+  retval.is_nostop = 0;
 
   for (bs = bs_head; bs != NULL; bs = bs->next)
     {
@@ -4364,6 +4366,8 @@ bpstat_what (bpstat bs_head)
 	case bp_exception:
 	  this_action = BPSTAT_WHAT_SET_LONGJMP_RESUME;
 	  retval.is_longjmp = bptype == bp_longjmp;
+	  retval.is_nostop =
+	      momentary_breakpoint_is_nostop (bs->breakpoint_at);
 	  break;
 	case bp_longjmp_resume:
 	case bp_exception_resume:
@@ -5902,13 +5906,18 @@ make_breakpoint_permanent (struct breakpoint *b)
 
 /* Call this routine when stepping and nexting to enable a breakpoint
    if we do a longjmp() or 'throw' in TP.  FRAME is the frame which
-   initiated the operation.  */
+   initiated the operation.  If the flag NOSTOP is set, the execution will
+   not be stopped upon hitting the breakpoint, but internal functions can be
+   triggered.  */
 
 void
-set_longjmp_breakpoint (struct thread_info *tp, struct frame_id frame)
+set_longjmp_breakpoint (struct thread_info *tp, struct frame_id frame,
+                        int nostop)
 {
   struct breakpoint *b, *b_tmp;
   int thread = tp->num;
+  struct breakpoint_ops *ops = (nostop ? &momentary_nostop_breakpoint_ops
+                                       : &momentary_breakpoint_ops);
 
   /* To avoid having to rescan all objfile symbols at every step,
      we maintain a list of continually-inserted but always disabled
@@ -5922,8 +5931,7 @@ set_longjmp_breakpoint (struct thread_info *tp, struct frame_id frame)
 	enum bptype type = b->type == bp_longjmp_master ? bp_longjmp : bp_exception;
 	struct breakpoint *clone;
 
-	clone = momentary_breakpoint_from_master (b, type,
-						  &momentary_breakpoint_ops);
+	clone = momentary_breakpoint_from_master (b, type, ops);
 	clone->thread = thread;
       }
 
@@ -7045,21 +7053,24 @@ enable_breakpoints_after_startup (void)
 }
 
 
-/* Set a breakpoint that will evaporate an end of command
-   at address specified by SAL.
-   Restrict it to frame FRAME if FRAME is nonzero.  */
+/* Set a breakpoint that will evaporate an end of command at address specified
+   by SAL. Restrict it to frame FRAME if FRAME is nonzero.  Will not request
+   execution stop if NOSTOP is set.  */
 
 struct breakpoint *
 set_momentary_breakpoint (struct gdbarch *gdbarch, struct symtab_and_line sal,
-			  struct frame_id frame_id, enum bptype type)
+			  struct frame_id frame_id, enum bptype type,
+			  int nostop)
 {
   struct breakpoint *b;
+  struct breakpoint_ops *ops = (nostop ? &momentary_nostop_breakpoint_ops
+                                       : &momentary_breakpoint_ops);
 
   /* If FRAME_ID is valid, it should be a real frame, not an inlined
      one.  */
   gdb_assert (!frame_id_inlined_p (frame_id));
 
-  b = set_raw_breakpoint (gdbarch, sal, type, &momentary_breakpoint_ops);
+  b = set_raw_breakpoint (gdbarch, sal, type, ops);
   b->enable_state = bp_enabled;
   b->disposition = disp_donttouch;
   b->frame_id = frame_id;
@@ -7127,7 +7138,7 @@ clone_momentary_breakpoint (struct breakpoint *orig)
 
 struct breakpoint *
 set_momentary_breakpoint_at_pc (struct gdbarch *gdbarch, CORE_ADDR pc,
-				enum bptype type)
+				enum bptype type, int nostop)
 {
   struct symtab_and_line sal;
 
@@ -7136,7 +7147,16 @@ set_momentary_breakpoint_at_pc (struct gdbarch *gdbarch, CORE_ADDR pc,
   sal.section = find_pc_overlay (pc);
   sal.explicit_pc = 1;
 
-  return set_momentary_breakpoint (gdbarch, sal, null_frame_id, type);
+  return set_momentary_breakpoint (gdbarch, sal, null_frame_id, type, nostop);
+}
+
+/* Return 1 if the momentary breakpoint BP was created with the NOSTOP
+   flag.  */
+
+int
+momentary_breakpoint_is_nostop (struct breakpoint *bp)
+{
+  return bp->ops == &momentary_nostop_breakpoint_ops;
 }
 \f
 
@@ -9549,13 +9569,14 @@ until_break_command (char *arg, int from_tty, int anywhere)
     /* If the user told us to continue until a specified location,
        we don't specify a frame at which we need to stop.  */
     breakpoint = set_momentary_breakpoint (get_frame_arch (frame), sal,
-					   null_frame_id, bp_until);
+					   null_frame_id, bp_until,
+					   /* nostop */ 0);
   else
     /* Otherwise, specify the selected frame, because we want to stop
        only at the very same frame.  */
     breakpoint = set_momentary_breakpoint (get_frame_arch (frame), sal,
 					   get_stack_frame_id (frame),
-					   bp_until);
+					   bp_until, /* nostop */ 0);
 
   old_chain = make_cleanup_delete_breakpoint (breakpoint);
 
@@ -9572,10 +9593,11 @@ until_break_command (char *arg, int from_tty, int anywhere)
       breakpoint2 = set_momentary_breakpoint (frame_unwind_caller_arch (frame),
 					      sal,
 					      frame_unwind_caller_id (frame),
-					      bp_until);
+					      bp_until, /* nostop */ 0);
       make_cleanup_delete_breakpoint (breakpoint2);
 
-      set_longjmp_breakpoint (tp, frame_unwind_caller_id (frame));
+      set_longjmp_breakpoint (tp, frame_unwind_caller_id (frame),
+                              /* nostop */ 0);
       make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
     }
 
@@ -11176,11 +11198,19 @@ momentary_bkpt_re_set (struct breakpoint *b)
 }
 
 static void
-momentary_bkpt_check_status (bpstat bs)
+momentary_stop_bkpt_check_status (bpstat bs)
 {
   /* Nothing.  The point of these breakpoints is causing a stop.  */
 }
 
+static void
+momentary_nostop_bkpt_check_status (bpstat bs)
+{
+  /* Don't stop, just trigger internal mechanisms.  */
+  bs->stop = 0;
+  bs->print = 0;
+}
+
 static enum print_stop_action
 momentary_bkpt_print_it (bpstat bs)
 {
@@ -13480,10 +13510,14 @@ initialize_breakpoint_ops (void)
   ops = &momentary_breakpoint_ops;
   *ops = bkpt_base_breakpoint_ops;
   ops->re_set = momentary_bkpt_re_set;
-  ops->check_status = momentary_bkpt_check_status;
+  ops->check_status = momentary_stop_bkpt_check_status;
   ops->print_it = momentary_bkpt_print_it;
   ops->print_mention = momentary_bkpt_print_mention;
 
+  ops = &momentary_nostop_breakpoint_ops;
+  *ops = momentary_breakpoint_ops;
+  ops->check_status = momentary_nostop_bkpt_check_status;
+
   /* GNU v3 exception catchpoints.  */
   ops = &gnu_v3_exception_catchpoint_ops;
   *ops = bkpt_breakpoint_ops;
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index c1d3be9..1b6cabd 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -836,6 +836,11 @@ struct bpstat_what
        BPSTAT_WHAT_CLEAR_LONGJMP_RESUME.  True if we are handling a
        longjmp, false if we are handling an exception.  */
     int is_longjmp;
+
+    /* Used for BPSTAT_WHAT_SET_LONGJMP_RESUME and
+       BPSTAT_WHAT_CLEAR_LONGJMP_RESUME.  True if we are handling a
+       nostop momentary breakpoint.  */
+    int is_nostop;
   };
 
 /* The possible return values for print_bpstat, print_it_normal,
@@ -1029,13 +1034,15 @@ extern void breakpoint_re_set (void);
 extern void breakpoint_re_set_thread (struct breakpoint *);
 
 extern struct breakpoint *set_momentary_breakpoint
-  (struct gdbarch *, struct symtab_and_line, struct frame_id, enum bptype);
+  (struct gdbarch *, struct symtab_and_line, struct frame_id, enum bptype,
+   int nostop);
 
 extern struct breakpoint *set_momentary_breakpoint_at_pc
-  (struct gdbarch *, CORE_ADDR pc, enum bptype type);
+  (struct gdbarch *, CORE_ADDR pc, enum bptype type, int nostop);
 
 extern struct breakpoint *clone_momentary_breakpoint (struct breakpoint *bpkt);
 
+extern int momentary_breakpoint_is_nostop (struct breakpoint *bp);
 extern void set_ignore_count (int, int, int);
 
 extern void breakpoint_init_inferior (enum inf_context);
@@ -1163,7 +1170,7 @@ extern int detach_breakpoints (int);
 extern void breakpoint_program_space_exit (struct program_space *pspace);
 
 extern void set_longjmp_breakpoint (struct thread_info *tp,
-				    struct frame_id frame);
+				    struct frame_id frame, int nostop);
 extern void delete_longjmp_breakpoint (int thread);
 
 extern void enable_overlay_breakpoints (void);
diff --git a/gdb/elfread.c b/gdb/elfread.c
index ddae099..6315323 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -993,7 +993,8 @@ elf_gnu_ifunc_resolver_stop (struct breakpoint *b)
       sal.explicit_pc = 1;
       b_return = set_momentary_breakpoint (get_frame_arch (prev_frame), sal,
 					   prev_frame_id,
-					   bp_gnu_ifunc_resolver_return);
+					   bp_gnu_ifunc_resolver_return,
+					   /* nostop */ 0);
 
       /* Add new b_return to the ring list b->related_breakpoint.  */
       gdb_assert (b_return->related_breakpoint == b_return);
diff --git a/gdb/infcall.c b/gdb/infcall.c
index 9af56ba..cca13b2 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -782,7 +782,8 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
     /* Sanity.  The exact same SP value is returned by
        PUSH_DUMMY_CALL, saved as the dummy-frame TOS, and used by
        dummy_id to form the frame ID's stack address.  */
-    bpt = set_momentary_breakpoint (gdbarch, sal, dummy_id, bp_call_dummy);
+    bpt = set_momentary_breakpoint (gdbarch, sal, dummy_id, bp_call_dummy,
+                                    /* nostop */ 0);
     bpt->disposition = disp_del;
   }
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 75dc55b..5c8c9f2 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -889,7 +889,8 @@ step_1 (int skip_subroutines, int single_inst, char *count_string)
       if (in_thread_list (inferior_ptid))
  	thread = pid_to_thread_id (inferior_ptid);
 
-      set_longjmp_breakpoint (tp, get_frame_id (get_current_frame ()));
+      set_longjmp_breakpoint (tp, get_frame_id (get_current_frame ()),
+                              /* nostop */ 0);
 
       make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
     }
@@ -1327,7 +1328,7 @@ until_next_command (int from_tty)
 
   tp->step_multi = 0;		/* Only one call to proceed */
 
-  set_longjmp_breakpoint (tp, get_frame_id (frame));
+  set_longjmp_breakpoint (tp, get_frame_id (frame), /* nostop */ 0);
   old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
 
   proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
@@ -1649,11 +1650,11 @@ finish_forward (struct symbol *function, struct frame_info *frame)
 
   breakpoint = set_momentary_breakpoint (gdbarch, sal,
 					 get_stack_frame_id (frame),
-                                         bp_finish);
+                                         bp_finish, /* nostop */ 0);
 
   old_chain = make_cleanup_delete_breakpoint (breakpoint);
 
-  set_longjmp_breakpoint (tp, get_frame_id (frame));
+  set_longjmp_breakpoint (tp, get_frame_id (frame), /* from_py*/ 0);
   make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
 
   /* We want stop_registers, please...  */
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 24d2720..dbe062a 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -55,6 +55,7 @@
 #include "continuations.h"
 #include "interps.h"
 #include "skip.h"
+#include "python/python.h"
 
 /* Prototypes for local functions */
 
@@ -104,7 +105,8 @@ static void insert_hp_step_resume_breakpoint_at_frame (struct frame_info *);
 
 static void insert_step_resume_breakpoint_at_caller (struct frame_info *);
 
-static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR);
+static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR,
+                                              int);
 
 /* When set, stop the 'step' command if we enter a function which has
    no line number information.  The normal behavior is that we step
@@ -2379,7 +2381,8 @@ static void handle_step_into_function (struct gdbarch *gdbarch,
 static void handle_step_into_function_backward (struct gdbarch *gdbarch,
 						struct execution_control_state *ecs);
 static void check_exception_resume (struct execution_control_state *,
-				    struct frame_info *, struct symbol *);
+				    struct frame_info *, struct symbol *,
+				    int nostop);
 
 static void stop_stepping (struct execution_control_state *ecs);
 static void prepare_to_wait (struct execution_control_state *ecs);
@@ -4388,6 +4391,7 @@ process_event_stop_test:
   {
     CORE_ADDR jmp_buf_pc;
     struct bpstat_what what;
+    int nostop;
 
     what = bpstat_what (ecs->event_thread->control.stop_bpstat);
 
@@ -4434,14 +4438,15 @@ process_event_stop_test:
 	    delete_step_resume_breakpoint (ecs->event_thread);
 
 	    /* Insert a breakpoint at resume address.  */
-	    insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
+	    insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc,
+	                                      what.is_nostop);
 	  }
 	else
 	  {
 	    struct symbol *func = get_frame_function (frame);
 
 	    if (func)
-	      check_exception_resume (ecs, frame, func);
+	      check_exception_resume (ecs, frame, func, what.is_nostop);
 	  }
 	keep_going (ecs);
 	return;
@@ -4455,6 +4460,8 @@ process_event_stop_test:
 	  {
 	    gdb_assert (ecs->event_thread->control.step_resume_breakpoint
 			!= NULL);
+	    nostop = momentary_breakpoint_is_nostop (
+	        ecs->event_thread->control.step_resume_breakpoint);
 	    delete_step_resume_breakpoint (ecs->event_thread);
 	  }
 	else
@@ -4476,6 +4483,8 @@ process_event_stop_test:
 
 	    gdb_assert (ecs->event_thread->control.exception_resume_breakpoint
 			!= NULL);
+	    nostop = momentary_breakpoint_is_nostop (
+	              ecs->event_thread->control.exception_resume_breakpoint);
 	    delete_exception_resume_breakpoint (ecs->event_thread);
 
 	    if (init_frame)
@@ -4500,6 +4509,12 @@ process_event_stop_test:
 	    delete_step_resume_breakpoint (ecs->event_thread);
 	  }
 
+        /* Notify Python that an exception/longjmp occured.  */
+        gdbpy_bpfinish_handle_exception ();
+
+        if (nostop)
+          break;
+
 	ecs->event_thread->control.stop_step = 1;
 	print_end_stepping_range_reason ();
 	stop_stepping (ecs);
@@ -5385,7 +5400,8 @@ insert_step_resume_breakpoint_at_sal_1 (struct gdbarch *gdbarch,
 			paddress (gdbarch, sr_sal.pc));
 
   inferior_thread ()->control.step_resume_breakpoint
-    = set_momentary_breakpoint (gdbarch, sr_sal, sr_id, sr_type);
+    = set_momentary_breakpoint (gdbarch, sr_sal, sr_id, sr_type,
+                                /* nostop */ 0);
 }
 
 void
@@ -5466,7 +5482,8 @@ insert_step_resume_breakpoint_at_caller (struct frame_info *next_frame)
    "step-resume" breakpoints.  */
 
 static void
-insert_longjmp_resume_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc)
+insert_longjmp_resume_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc,
+                                  int nostop)
 {
   /* There should never be more than one step-resume or longjmp-resume
      breakpoint per thread, so we should never be setting a new
@@ -5479,20 +5496,23 @@ insert_longjmp_resume_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc)
 			paddress (gdbarch, pc));
 
   inferior_thread ()->control.step_resume_breakpoint =
-    set_momentary_breakpoint_at_pc (gdbarch, pc, bp_longjmp_resume);
+    set_momentary_breakpoint_at_pc (gdbarch, pc, bp_longjmp_resume,
+                                    nostop);
 }
 
 /* Insert an exception resume breakpoint.  TP is the thread throwing
    the exception.  The block B is the block of the unwinder debug hook
    function.  FRAME is the frame corresponding to the call to this
    function.  SYM is the symbol of the function argument holding the
-   target PC of the exception.  */
+   target PC of the exception.  The breakpoint will not stop the execution
+   if the flag NOSTOP is set.  */
 
 static void
 insert_exception_resume_breakpoint (struct thread_info *tp,
 				    struct block *b,
 				    struct frame_info *frame,
-				    struct symbol *sym)
+				    struct symbol *sym,
+				    int nostop)
 {
   volatile struct gdb_exception e;
 
@@ -5517,7 +5537,8 @@ insert_exception_resume_breakpoint (struct thread_info *tp,
 				(unsigned long) handler);
 
 	  bp = set_momentary_breakpoint_at_pc (get_frame_arch (frame),
-					       handler, bp_exception_resume);
+					       handler, bp_exception_resume,
+					       nostop);
 	  bp->thread = tp->num;
 	  inferior_thread ()->control.exception_resume_breakpoint = bp;
 	}
@@ -5530,7 +5551,8 @@ insert_exception_resume_breakpoint (struct thread_info *tp,
 
 static void
 check_exception_resume (struct execution_control_state *ecs,
-			struct frame_info *frame, struct symbol *func)
+			struct frame_info *frame, struct symbol *func,
+			int nostop)
 {
   volatile struct gdb_exception e;
 
@@ -5566,7 +5588,7 @@ check_exception_resume (struct execution_control_state *ecs,
 	  else
 	    {
 	      insert_exception_resume_breakpoint (ecs->event_thread,
-						  b, frame, sym);
+						  b, frame, sym, nostop);
 	      break;
 	    }
 	}
diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
index bfbf9c3..621a155 100644
--- a/gdb/python/py-finishbreakpoint.c
+++ b/gdb/python/py-finishbreakpoint.c
@@ -298,6 +298,10 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
   /* Bind the breakpoint with the current program space.  */
   self_bpfinish->py_bp.bp->pspace = current_program_space;
 
+  /* Set a watchdog on longjmp/exception.  It will trigger
+    `gdbpy_bpfinish_handle_exception' if necessary.  */
+  set_longjmp_breakpoint (inferior_thread (), frame_id, /* nostop */ 1);
+
   return 0;
   
  invalid_frame:
@@ -327,45 +331,74 @@ bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
   delete_breakpoint (bpfinish_obj->py_bp.bp);
 }
 
-/* Callback for `bpfinishpy_detect_out_scope'.  Triggers Python's
-   `B->out_of_scope' function if B is a FinishBreakpoint out of its scope.  */
+/* Triggers Python's `B->out_of_scope' function if B is a FinishBreakpoint
+   out of its scope.  If INC_CURRENT is set, being on the initial frame will
+   trigger the callback as well.  */
 
 static int
-bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
+bpfinishpy_detect_out_scope (struct breakpoint *b, int inc_current)
 {
   volatile struct gdb_exception except;
-  struct breakpoint *bp_stopped = (struct breakpoint *) args;
-  PyObject *py_bp = (PyObject *) b->py_bp_object;
+  struct finish_breakpoint_object *finish_bp =
+      (struct finish_breakpoint_object *) b->py_bp_object;
   struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
   
-  /* Trigger out_of_scope if this is a FinishBreakpoint and its frame is
-     not anymore in the current callstack.  */
-  if (py_bp != NULL && b->py_bp_object->is_finish_bp)
+  /* Ensure that B is a FinishBreakpoint and we're stopped in its inferior.  */
+  if (finish_bp != NULL && finish_bp->py_bp.is_finish_bp
+      && b->pspace == current_inferior ()->pspace)
     {
-      struct finish_breakpoint_object *finish_bp =
-          (struct finish_breakpoint_object *) py_bp;
-
-      /* Check scope if not currently stopped at the FinishBreakpoint.  */
-      if (b != bp_stopped)
+      TRY_CATCH (except, RETURN_MASK_ALL)
         {
-          TRY_CATCH (except, RETURN_MASK_ALL)
-            {
-              if (b->pspace == current_inferior ()->pspace
-                  && (!target_has_registers
-                      || frame_find_by_id (b->frame_id) == NULL))
-                bpfinishpy_out_of_scope (finish_bp);
-            }
-          if (except.reason < 0)
-            {
-              gdbpy_convert_exception (except);
-              gdbpy_print_stack ();
-            }
+          /* Trigger the callback if the initial frame no longer exists or,
+           if INC_CURRENT is set, if we're back on the initial frame, or if
+           the inferior no longer exists.  */
+          int is_current = 0;
+
+          if (inc_current && target_has_registers)
+            is_current = frame_id_eq (get_frame_id (get_current_frame ()),
+                                      b->frame_id);
+
+          if (!target_has_registers
+              || frame_find_by_id (b->frame_id) == NULL
+              || is_current)
+            bpfinishpy_out_of_scope (finish_bp);
+        }
+      if (except.reason < 0)
+        {
+          gdbpy_convert_exception (except);
+          gdbpy_print_stack ();
         }
     }
 
   return 0;
 }
 
+/* Callback for bpfinishpy_handle_stop.  Check scope of B if not
+   currently stopped on it.  */
+
+static int
+bpfinishpy_detect_out_scope_bp_cb (struct breakpoint *b, void *args)
+{
+  struct breakpoint *bp_stopped = (struct breakpoint *) args;
+
+  if (bp_stopped != b)
+    bpfinishpy_detect_out_scope (b, 0);
+
+  return 0;
+}
+
+/* Callback for bpfinishpy_handle_exception.  Force out_of_scope if stopped
+   in the initial frame.  */
+
+static int
+bpfinishpy_detect_out_scope_exception_cb (struct breakpoint *b, void *args)
+{
+  bpfinishpy_detect_out_scope (b, 1);
+
+  return 0;
+}
+
+
 /* Attached to `stop' notifications, check if the execution has run
    out of the scope of any FinishBreakpoint before it has been hit.  */
 
@@ -375,22 +408,22 @@ bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
   struct cleanup *cleanup = ensure_python_env (get_current_arch (),
                                                current_language);
 
-  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_bp_cb,
                             bs == NULL ? NULL : bs->breakpoint_at);
 
   do_cleanups (cleanup);
 }
 
-/* Attached to `exit' notifications, triggers all the necessary out of
+/* Called when an exception is caught, triggers the necessary out of
    scope notifications.  */
 
-static void
-bpfinishpy_handle_exit (struct inferior *inf)
+void
+gdbpy_bpfinish_handle_exception  (void)
 {
-  struct cleanup *cleanup = ensure_python_env (target_gdbarch,
+  struct cleanup *cleanup = ensure_python_env (get_current_arch (),
                                                current_language);
 
-  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_exception_cb, NULL);
 
   do_cleanups (cleanup);
 }
@@ -408,7 +441,6 @@ gdbpy_initialize_finishbreakpoints (void)
                       (PyObject *) &finish_breakpoint_object_type);
     
   observer_attach_normal_stop (bpfinishpy_handle_stop);
-  observer_attach_inferior_exit (bpfinishpy_handle_exit);
 }
 
 static PyGetSetDef finish_breakpoint_object_getset[] = {
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 5212d4e..a786a55 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1101,6 +1101,12 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj)
 		    "scripting is not supported."));
 }
 
+void
+gdbpy_bpfinish_handle_exception (void)
+{
+  /* Nothing.  */
+}
+
 #endif /* HAVE_PYTHON */
 
 \f
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 9e461f7..8afe28f 100644
--- a/gdb/python/python.h
+++ b/gdb/python/python.h
@@ -47,4 +47,6 @@ int gdbpy_should_stop (struct breakpoint_object *bp_obj);
 
 int gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj);
 
+void gdbpy_bpfinish_handle_exception (void);
+
 #endif /* GDB_PYTHON_H */
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
index 9e65d6c..ffa4f62 100644
--- a/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -50,18 +50,30 @@ if ![runto_main] then {
 # Check FinishBreakpoints against C++ exceptions
 #
 
-gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
-
 gdb_test "source $pyfile" ".*Python script imported.*" \
          "import python scripts"
          
 gdb_breakpoint "throw_exception_1"
+gdb_test "python watch = gdb.Breakpoint (\"[gdb_get_line_number "Break after exception 1"]\")" \
+         "Breakpoint.*" "set watchdog breakpoint after exception handler"
 gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
 
-gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python print len(gdb.breakpoints ())" "3" "check BP count"
 gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
-gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
-gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+set test "don't catch FinishBreakpoint with exception"
+gdb_test_multiple "continue" $test {
+    -re "stopped at ExceptionFinishBreakpoint.*$gdb_prompt $" {
+    	xfail $test
+    }
+    -re "exception did not finish.*Breakpoint .* Break after exception 1.*$gdb_prompt $" {
+        pass $test
+        
+        gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+    }
+}
+
+gdb_py_test_silent_cmd "python watch.delete ()" "delete watchdog breakpoint" 0
 
 gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
 gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
-- 
1.7.6.5


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

* Call target_close after unpushing, not before (was: Re: [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver))
  2012-01-02 19:49                                                                               ` Pedro Alves
@ 2012-01-19 16:24                                                                                 ` Pedro Alves
  2012-01-19 16:27                                                                                   ` Jan Kratochvil
  0 siblings, 1 reply; 74+ messages in thread
From: Pedro Alves @ 2012-01-19 16:24 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, Tom Tromey, Kevin Pouget

On 01/02/2012 07:48 PM, Pedro Alves wrote:
> On 12/27/2011 11:23 PM, Jan Kratochvil wrote:
>> [rediff on top of HEAD]
>>
>> On Sun, 25 Dec 2011 12:37:45 +0100, Jan Kratochvil wrote:
>> Hi,
>>
>> with gdbserver as from
>>     http://sourceware.org/gdb/wiki/TestingGDB#Testing_gdbserver_in_a_native_configuration
>>
>> it will crash:
>>
>> (gdb) PASS: gdb.python/py-finish-breakpoint.exp: set FinishBP after the exit()
>> continue
>> Continuing.
>> Child exited with status 0
>> GDBserver exiting
>> [Inferior 1 (Remote target) exited normally]
>> SimpleFinishBreakpoint out of scope
>> ERROR: Process no longer exists
>> Program terminated with signal 11, Segmentation fault.
>> #0  0x00000000007d48b9 in serial_debug_p (scb=0x0) at serial.c:584
>> 584       return scb->debug_p || global_serial_debug_p;
>> (gdb) p scb
>> $1 = (struct serial *) 0x0
>> (gdb) bt
>> #0  in serial_debug_p (scb=0x0) at serial.c:584
>> #1  in serial_write (scb=0x0, str=0x7fff727e3300 "$z0,4008a3,1#93", len=15) at serial.c:427
>> #2  in putpkt_binary (buf=0x2a279b0 "z0,4008a3,1", cnt=11) at remote.c:6891
>> #3  in putpkt (buf=0x2a279b0 "z0,4008a3,1") at remote.c:6823
>> #4  in remote_remove_breakpoint (gdbarch=0x28e10f0, bp_tgt=0x2fb3d38) at remote.c:7749
>> #5  in target_remove_breakpoint (gdbarch=0x28e10f0, bp_tgt=0x2fb3d38) at target.c:2422
>> #6  in bkpt_remove_location (bl=0x2fb3cd0) at breakpoint.c:10967
>> #7  in remove_breakpoint_1 (bl=0x2fb3cd0, is=mark_uninserted) at breakpoint.c:2654
>> #8  in remove_breakpoint (bl=0x2fb3cd0, is=mark_uninserted) at breakpoint.c:2760
>> #9  in update_global_location_list (should_insert=0) at breakpoint.c:10539
>> #10 in delete_breakpoint (bpt=0x2f59290) at breakpoint.c:11392
>> #11 in bpfinishpy_out_of_scope (bpfinish_obj=0x7fdf3c9f8130) at ./python/py-finishbreakpoint.c:327
>> #12 in bpfinishpy_detect_out_scope_cb (b=0x2f59290, args=0x0) at ./python/py-finishbreakpoint.c:356
>> #13 in iterate_over_breakpoints (callback=0x65150d<bpfinishpy_detect_out_scope_cb>, data=0x0) at breakpoint.c:13385
>> #14 in bpfinishpy_handle_exit (inf=0x281d330) at ./python/py-finishbreakpoint.c:393
>> #15 in observer_inferior_exit_notification_stub (data=0x6516b1, args_data=0x7fff727e3740) at observer.inc:887
>> #16 in generic_observer_notify (subject=0x284f300, args=0x7fff727e3740) at observer.c:168
>> #17 in observer_notify_inferior_exit (inf=0x281d330) at observer.inc:912
>> #18 in exit_inferior_1 (inftoex=0x281d330, silent=1) at inferior.c:276
>> #19 in exit_inferior_silent (pid=42000) at inferior.c:305
>> #20 in discard_all_inferiors () at inferior.c:343
>> #21 in remote_close (quitting=0) at remote.c:2950
>> #22 in target_close (targ=0x1d19f80, quitting=0) at target.c:3387
>> #23 in unpush_target (t=0x1d19f80) at target.c:1024
>> #24 in remote_mourn_1 (target=0x1d19f80) at remote.c:7456
>> #25 in remote_mourn (ops=0x1d19f80) at remote.c:7449
>> #26 in target_mourn_inferior () at target.c:2747
>> #27 in handle_inferior_event (ecs=0x7fff727e3c30) at infrun.c:3408
>> #28 in wait_for_inferior () at infrun.c:2711
>> #29 in proceed (addr=18446744073709551615, siggnal=TARGET_SIGNAL_DEFAULT, step=0) at infrun.c:2276
>> #30 in continue_1 (all_threads=0) at infcmd.c:713
>> #31 in continue_command (args=0x0, from_tty=1) at infcmd.c:805
>>
>>
>> Reproducible with:
>> ../gdbserver/gdbserver :1234 gdb.python/py-finish-breakpoint
>> gdb -nx -x ~/.gdbinit -ex r --args ../gdb -nx -ex 'file gdb.python/py-finish-breakpoint' -ex 'target remote localhost:1234' -ex 'b test_exec_exit' -ex c -ex 'source gdb.python/py-finish-breakpoint.py' -ex 'python SimpleFinishBreakpoint(gdb.newest_frame())' -ex c
>>
>> I have seen this serial_debug_p crash in various GDB debugging cases but they
>> were not well reproducible.  I understand this bug is unrelated to
>> gdb.python/py-finish-breakpoint.exp .
>>
>> I also tried to reorder remote_close a bit but this patch seems as the right
>> one to me.
> 
> The issue is that the target is already gone, and we're going through
> teardown.  What I think would be a bit better is to add a new function
> to breakpoint.c, similar to mark_breakpoints_out, like:
> 
> void
> mark_all_breakpoints_out (void)
> {
>   struct bp_location *bl, **blp_tmp;
> 
>   ALL_BP_LOCATIONS (bl, blp_tmp)
>     bl->inserted = 0;
> }
> 
> and call that from within remote_close, before discard_all_inferiors.
> 
> remote_desc == NULL checks throughout remote.c are plain hacks.

I tried this, and it works.  However,

> 
> Even better would be to do discard_all_inferiors after target_close,
> though that's tricky.  Perhaps it'd work to do it only if the target
> just closed is of process_stratum.

I think there's even a better way.  Any target method call through the
target vector from within target_close, while we're handling teardown,
is likely to end up being broken.  So I thought that it's better to
consider that by the time target_close is called, the target is already
not useable, and removed on the stack.  If the target does want to
call some other of its own target methods, it should call it directly,
instead of through the target vector.  I audited all target_close
implementations, and only linux-nat.c needed such adjustment.

This fixes the gdbserver crash.  Also tested w/ native.

Unfortunately, the py-finish-breakpoint.exp triggers internal errors
in async mode, but that's unrelated.

Any comments on this?

gdb/
2012-01-19  Pedro Alves  <palves@redhat.com>

	* linux-nat.c (linux_nat_close): Call linux_nat_is_async_p and
	linux_nat_async directly instead of going through the target
	vector.
	* target.c (unpush_target): Close target after unpushing it, not
	before.
---

 gdb/linux-nat.c |    4 ++--
 gdb/target.c    |   17 ++++++++---------
 2 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index f6b36a2..30f9062 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -5717,8 +5717,8 @@ static void
 linux_nat_close (int quitting)
 {
   /* Unregister from the event loop.  */
-  if (target_is_async_p ())
-    target_async (NULL, 0);
+  if (linux_nat_is_async_p ())
+    linux_nat_async (NULL, 0);

   if (linux_ops->to_close)
     linux_ops->to_close (quitting);
diff --git a/gdb/target.c b/gdb/target.c
index 9aaa0ea..6af4620 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -1010,16 +1010,10 @@ unpush_target (struct target_ops *t)
 	break;
     }

+  /* If we don't find target_ops, quit.  Only open targets should be
+     closed.  */
   if ((*cur) == NULL)
-    return 0;			/* Didn't find target_ops, quit now.  */
-
-  /* NOTE: cagney/2003-12-06: In '94 the close call was made
-     unconditional by moving it to before the above check that the
-     target was in the target stack (something about "Change the way
-     pushing and popping of targets work to support target overlays
-     and inheritance").  This doesn't make much sense - only open
-     targets should be closed.  */
-  target_close (t, 0);
+    return 0;			

   /* Unchain the target.  */
   tmp = (*cur);
@@ -1028,6 +1022,11 @@ unpush_target (struct target_ops *t)

   update_current_target ();

+  /* Finally close the target.  Note we do this after unchaining, so
+     any target method calls from within the target_close
+     implementation don't end up in T anymore.  */
+  target_close (t, 0);
+
   return 1;
 }


-- 
Pedro Alves

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

* Re: Call target_close after unpushing, not before (was: Re: [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver))
  2012-01-19 16:24                                                                                 ` Call target_close after unpushing, not before (was: Re: [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver)) Pedro Alves
@ 2012-01-19 16:27                                                                                   ` Jan Kratochvil
  2012-01-19 16:37                                                                                     ` Call target_close after unpushing, not before Pedro Alves
  2012-01-19 16:53                                                                                     ` [commit] Re: Call target_close after unpushing, not before (was: Re: [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver)) Jan Kratochvil
  0 siblings, 2 replies; 74+ messages in thread
From: Jan Kratochvil @ 2012-01-19 16:27 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches, Tom Tromey, Kevin Pouget

On Thu, 19 Jan 2012 17:02:31 +0100, Pedro Alves wrote:
> Any comments on this?

I am fine with it with the comment update.


Thanks,
Jan


gdb/
2012-01-19  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* target.h (target_close): Update comment on the target's unpush state.

--- a/gdb/target.h
+++ b/gdb/target.h
@@ -820,10 +820,10 @@ extern struct target_ops current_target;
    longer going to be calling.  QUITTING indicates that GDB is exiting
    and should not get hung on an error (otherwise it is important to
    perform clean termination, even if it takes a while).  This routine
-   is automatically always called when popping the target off the
-   target stack (to_beneath is undefined).  Closing file descriptors
-   and freeing all memory allocated memory are typical things it
-   should do.  */
+   is automatically always called after popping the target off the
+   target stack - the target's own methods are no longer available
+   through the target vector.  Closing file descriptors and freeing all
+   memory allocated memory are typical things it should do.  */
 
 void target_close (struct target_ops *targ, int quitting);
 

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

* Re: Call target_close after unpushing, not before
  2012-01-19 16:27                                                                                   ` Jan Kratochvil
@ 2012-01-19 16:37                                                                                     ` Pedro Alves
  2012-01-19 16:53                                                                                     ` [commit] Re: Call target_close after unpushing, not before (was: Re: [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver)) Jan Kratochvil
  1 sibling, 0 replies; 74+ messages in thread
From: Pedro Alves @ 2012-01-19 16:37 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, Tom Tromey, Kevin Pouget

On 01/19/2012 04:23 PM, Jan Kratochvil wrote:
> On Thu, 19 Jan 2012 17:02:31 +0100, Pedro Alves wrote:
>> Any comments on this?
> 
> I am fine with it with the comment update.

Good idea.  Please go ahead.  I've applied my patch.

Thanks,
-- 
Pedro Alves

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

* [commit] Re: Call target_close after unpushing, not before (was: Re: [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver))
  2012-01-19 16:27                                                                                   ` Jan Kratochvil
  2012-01-19 16:37                                                                                     ` Call target_close after unpushing, not before Pedro Alves
@ 2012-01-19 16:53                                                                                     ` Jan Kratochvil
  1 sibling, 0 replies; 74+ messages in thread
From: Jan Kratochvil @ 2012-01-19 16:53 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches, Tom Tromey, Kevin Pouget

On Thu, 19 Jan 2012 17:23:49 +0100, Jan Kratochvil wrote:
> gdb/
> 2012-01-19  Jan Kratochvil  <jan.kratochvil@redhat.com>
> 
> 	* target.h (target_close): Update comment on the target's unpush state.

Checked in:
	http://sourceware.org/ml/gdb-cvs/2012-01/msg00163.html


Thanks,
Jan

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

* Re: [RFC] Python Finish Breakpoints
  2012-01-09  9:21                                                                             ` Kevin Pouget
@ 2012-01-27 17:01                                                                               ` Kevin Pouget
  0 siblings, 0 replies; 74+ messages in thread
From: Kevin Pouget @ 2012-01-27 17:01 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey, Ulrich Weigand

On Mon, Jan 9, 2012 at 10:20 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> On Wed, Jan 4, 2012 at 6:12 PM, Ulrich Weigand <uweigand@de.ibm.com> wrote:
>> Tom Tromey wrote:
>>
>>> We have support for "finish", and other operations in the presence of
>>> exceptions.  Search for _Unwind_DebugHook (in breakpoint.c and infrun.c).
>>> This code only works if you are using a new-enough version of GCC and
>>> have debuginfo for libgcc (but once the stap probe patches are in, you
>>> will no longer need debuginfo).
>>
>> Ah, I see -- that's what I was missing.  Yes, this looks like the
>> way to handle this situation ...
>
> Hello,
>
> I would like to discuss this patch which intends to fix and improve
> the handling for c++ exceptions with the Python FinishBreakpoint.
>
> Based on what I understood of the existing mechanisms which handle
> longjmps and and exception, I extended the "momentary breakpoints"
> with a "nostop" flag. Indeed, their initial behavior/goal was to stop
> unconditionally the execution, whereas I just wanted to receive a
> notification when the relevant code location was hit (namely
> infrun.c::BPSTAT_WHAT_CLEAR_LONGJMP_RESUME). The nostop flag is
> propagated step by step (bp by bp) from the exception throw to its
> catchpoint, where I check if the FinishBreakpoint have ran out of
> their scope.
>
> As Tom mentioned, c++ debug info are needed for this machinery to work
> (otherwise, FinishBreakpoint will fail the same way GDB fails: stop at
> the beginning of the catch(), never stop, ...) -- I used xfail in the
> testsuite to refer to this situation, I'm not sure that's correct).
>
> I've also fixed a bug which triggered twice the out_of_scope
> notification in case of inferior exit (observer_attach_normal_stop and
> observer_attach_inferior_exit are both triggered when the inferior
> exits)
>
> (This is just a preliminary patch, I'll would like to prepare some
> more tests with exceptions and longjmps)
>
> Tested with no regression on f15/X86_64
>
> ( these always fail on my system:
>  Running PATH/git/gdb/gdb/testsuite/gdb.base/longjmp.exp ...
>  FAIL: gdb.base/longjmp.exp: next over longjmp(1)
>  FAIL: gdb.base/longjmp.exp: next over call_longjmp (2)
>  FAIL: gdb.base/longjmp.exp: next over patt3
> )
>
> Thanks for your comments,
>
> Kevin
>
> --
>
> 2012-01-09  Kevin Pouget <kevin.pouget@st.com>
>
>        * breakpoint.c (momentary_nostop_breakpoint_ops): New variable.
>        (bpstat_what): Set field retval.is_nostop when necessary.
>        (set_longjmp_breakpoint): Add parameter nostop and create nostop
>        momentary breakpoint ops if necessary.
>        (set_momentary_breakpoint): Likewise.
>        (set_momentary_breakpoint_at_pc): Likewise.
>        (momentary_breakpoint_is_nostop): New function.
>        (until_break_command): Call set_momentary_breakpoint and
>        set_longjmp_breakpoint with nostop not set.
>        (momentary_stop_bkpt_check_status): Rename from
>        momentary_bkpt_check_status.
>        (momentary_bkpt_check_status): Remove.
>        (momentary_nostop_bkpt_check_status): New function.
>        (initialize_breakpoint_ops): Initialize
>        momentary_breakpoint_ops.check_status to
>        momentary_stop_bkpt_check_status, initialize
>        momentary_nostop_breakpoint_ops.
>        * breakpoint.h (struct bpstat_what): New field: is_nostop.
>        (set_momentary_breakpoint): New parameter: nostop.
>        (set_momentary_breakpoint_at_pc): Liklewise.
>        (set_longjmp_breakpoint): Likewise.
>        (momentary_breakpoint_is_nostop): New prototype.
>        * arm-linux-tdep.c (arm_linux_copy_svc): Add nostop parameter to
>        set_momentary_breakpoint call.
>        * elfread.c: (elf_gnu_ifunc_resolver_stop): Likewise
>        * infcall.c (call_function_by_hand): Likewise.
>        * infcmd.c: (finish_forward): Likewise, add nostop parameter to
>        set_longjmp_breakpoint call.
>        (step_1): Add nostop parameter to set_longjmp_breakpoint call.
>        (until_next_command): Likewise.
>        * infrun.c: Include python/python.h.
>        (insert_step_resume_breakpoint_at_sal_1): Add nostop parameter to
>        set_momentary_breakpoint call.
>        (insert_longjmp_resume_breakpoint): Add nostop parameter to prototype.
>        Add nostop parameter to set_momentary_breakpoint_at_pc call.
>        (insert_exception_resume_breakpoint): Likewise.
>        (check_exception_resume): Add nostop parameter to prototype. Add nostop
>        parameter to insert_exception_resume_breakpoint call.
>        (process_event_stop_test): Set and use nostop to prevent GDB from
>        stopping the execution, trigger gdbpy_bpfinish_handle_exception when
>        necessary.
>        * python/py-finishbreakpoint.c (bpfinishpy_init): Call
>        set_longjmp_breakpoint.
>        (bpfinishpy_detect_out_scope): Renamed from
>        bpfinishpy_detect_out_scope_cb. Cleaned up. Add inc_current parameter.
>        (bpfinishpy_detect_out_scope_cb): Removed.
>        (bpfinishpy_detect_out_scope_bp_cb): New function.
>        (bpfinishpy_detect_out_scope_exception_cb): New function.
>        (bpfinishpy_handle_stop): Update with function name
>        bpfinishpy_detect_out_scope_bp_cb.
>        (bpfinishpy_handle_exit): Removed.
>        (gdbpy_bpfinish_handle_exception): New function.
>        (gdbpy_initialize_finishbreakpoints): Remove call to
>        bpfinishpy_handle_exit.
>        * python/python.c (gdbpy_bpfinish_handle_exception): New function.
>        * python/python.h (gdbpy_bpfinish_handle_exception): New prototype.
>
> testsuite/
>        * gdb.python/py-finish-breakpoint2.exp: Strengthen the exception tests.

ping

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

end of thread, other threads:[~2012-01-27 15:16 UTC | newest]

Thread overview: 74+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <BANLkTim+YH64zkWvdz2_kVUUj=AJ7v4LKw@mail.gmail.com>
     [not found] ` <BANLkTi=gtHzWzXOJzB+a19=jkfk1hGwQBg@mail.gmail.com>
     [not found]   ` <BANLkTikVdqbMqjguTV8ct0TWiBDhHGYtLg@mail.gmail.com>
2011-05-11  7:44     ` [RFC] Python Finish Breakpoints Kevin Pouget
2011-05-11 10:31       ` Phil Muldoon
2011-05-11 11:29         ` Kevin Pouget
2011-05-12 10:38           ` Kevin Pouget
2011-05-12 10:50         ` Phil Muldoon
2011-05-12 11:29           ` Kevin Pouget
     [not found] ` <BANLkTi=Eu-5B4YyhP2rGdQXgXbBN-EmLKA@mail.gmail.com>
     [not found]   ` <BANLkTikt2hEUcXkGVH44NaUcwiF1SGdMaw@mail.gmail.com>
     [not found]     ` <BANLkTi=UpgogckTD5MZsW+PC5d2F8X-+jA@mail.gmail.com>
     [not found]       ` <BANLkTi==bj50mZgFKq_qA-+3-2CQA3tBVw@mail.gmail.com>
     [not found]         ` <BANLkTimmYEmvKT_984jYEVZnA5RGFpEgNw@mail.gmail.com>
2011-05-19 16:21           ` Tom Tromey
2011-05-24 12:51             ` Kevin Pouget
2011-05-27 20:30               ` Tom Tromey
2011-05-30  9:29                 ` Kevin Pouget
2011-10-13 14:34                   ` Kevin Pouget
2011-10-20 20:12                     ` Tom Tromey
2011-10-20 20:58                   ` Tom Tromey
2011-10-21  8:15                     ` Kevin Pouget
2011-10-24 11:43                       ` Kevin Pouget
2011-10-24 13:20                         ` Eli Zaretskii
2011-10-24 14:30                           ` Kevin Pouget
2011-10-24 17:14                             ` Eli Zaretskii
2011-10-24 15:31                         ` Kevin Pouget
2011-10-24 16:27                           ` Phil Muldoon
2011-10-25 11:05                             ` Kevin Pouget
2011-10-25 11:37                               ` Phil Muldoon
2011-10-25 12:22                                 ` Kevin Pouget
2011-10-28  8:33                                   ` Kevin Pouget
2011-10-28 20:51                                     ` Tom Tromey
2011-11-02 14:44                                       ` Kevin Pouget
2011-11-04 14:25                                         ` Kevin Pouget
2011-11-04 21:04                                           ` Tom Tromey
2011-11-09 14:10                                             ` Kevin Pouget
2011-11-16 10:53                                               ` Kevin Pouget
2011-11-17 17:49                                                 ` Tom Tromey
2011-11-17 17:48                                               ` Tom Tromey
     [not found]                                                 ` <CAPftXUJwtGJhqFyfX6LVK87QH2xeLkvv5kx=yaEnYJMv0YR_rw@mail.gmail.com>
2011-11-24  8:27                                                   ` Kevin Pouget
2011-11-30 16:02                                                     ` Kevin Pouget
2011-12-02 21:49                                                       ` Tom Tromey
2011-12-05  9:29                                                         ` Kevin Pouget
2011-12-06 21:18                                                           ` Tom Tromey
2011-12-07 13:35                                                             ` Kevin Pouget
2011-12-08 15:30                                                               ` Tom Tromey
2011-12-08 16:10                                                                 ` Kevin Pouget
2011-12-08 18:08                                                                   ` Kevin Pouget
2011-12-09  9:53                                                                     ` Kevin Pouget
2011-12-18 19:22                                                                       ` Kevin Pouget
2011-12-20 20:55                                                                       ` Tom Tromey
2011-12-20 20:58                                                                         ` Kevin Pouget
2011-12-21  7:16                                                                           ` Joel Brobecker
2011-12-21 17:13                                                                             ` Tom Tromey
     [not found]                                                                               ` <CAPftXUKXh9ekZ2kiwQ=5zbrjst+9VH9-eZk8h+Z-9SpQ1WqdLw@mail.gmail.com>
     [not found]                                                                                 ` <CAPftXULQFggY3Nz2byC0fMZA1sg4Nymp3hAhe8aSuLNG4cauFg@mail.gmail.com>
     [not found]                                                                                   ` <CAPftXUJALHd=-fajVwgowqfCDfXO6JMxSkorWD6CQArsg-PedQ@mail.gmail.com>
     [not found]                                                                                     ` <CAPftXULKC8gp3L87+PZEF3dj3crv9bh-uzZpRiYKjqEw_xyptQ@mail.gmail.com>
2011-12-27  4:18                                                                                       ` Joel Brobecker
2011-12-27  9:40                                                                                         ` Kevin Pouget
2011-12-27 12:22                                                                                           ` Joel Brobecker
2011-12-27  9:34                                                                                       ` Kevin Pouget
2011-12-24 23:56                                                                           ` [patch] Fix gdb.python/py-finish-breakpoint.exp new FAIL on x86_64-m32 [Re: [RFC] Python Finish Breakpoints] Jan Kratochvil
2011-12-27 11:13                                                                             ` Kevin Pouget
2011-12-27 21:39                                                                               ` [commit] " Jan Kratochvil
2012-01-04 17:45                                                                             ` Ulrich Weigand
2012-01-04 17:58                                                                               ` [commit 7.4] " Jan Kratochvil
2012-01-04 18:10                                                                                 ` Ulrich Weigand
2011-12-26 11:28                                                                           ` [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver) " Jan Kratochvil
2011-12-27 23:30                                                                             ` [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver) [rediff] Jan Kratochvil
2012-01-02 17:57                                                                               ` Tom Tromey
2012-01-02 19:49                                                                               ` Pedro Alves
2012-01-19 16:24                                                                                 ` Call target_close after unpushing, not before (was: Re: [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver)) Pedro Alves
2012-01-19 16:27                                                                                   ` Jan Kratochvil
2012-01-19 16:37                                                                                     ` Call target_close after unpushing, not before Pedro Alves
2012-01-19 16:53                                                                                     ` [commit] Re: Call target_close after unpushing, not before (was: Re: [patch] Fix remote.c crash on gdbserver close (+fix py-finish-breakpoint.exp for gdbserver)) Jan Kratochvil
2012-01-04 14:49                                                                       ` [RFC] Python Finish Breakpoints Ulrich Weigand
2012-01-04 15:24                                                                         ` Kevin Pouget
2012-01-04 16:29                                                                           ` Ulrich Weigand
2012-01-04 16:42                                                                           ` Tom Tromey
2012-01-04 16:40                                                                         ` Tom Tromey
2012-01-04 17:13                                                                           ` Ulrich Weigand
2012-01-09  9:21                                                                             ` Kevin Pouget
2012-01-27 17:01                                                                               ` Kevin Pouget
2011-10-28 19:26                                   ` Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).