public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Add bp_location to Python interface
@ 2011-12-08 10:17 Kevin Pouget
  2011-12-08 13:39 ` Phil Muldoon
  2011-12-08 14:56 ` Phil Muldoon
  0 siblings, 2 replies; 25+ messages in thread
From: Kevin Pouget @ 2011-12-08 10:17 UTC (permalink / raw)
  To: gdb-patches

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

Hello,

I would like to discuss this patch which introduces a new Python class
gdb.BpLocation, mapped from breakpoint.h::struct bp_location.

I noticed that the Python interface doesn't offer so many details
about breakpoint location, just gdb.Breakpoint.location, the string
used to set the breakpoint (its linespec?)

So this new class, which is currently strictly read-only and not
instantiatable, exports the address and inferior in which the
breakpoint was set (and an enabled flag, and a link to its owner
breakpoint).

BpLocation object are available through the gdb.Breakpoint.locations method.

I think that this class would also help Python users to better
control/understand where their breakpoints are set with Tom's recent
changes about ambiguous linespec

+* GDB now handles ambiguous linespecs more consistently; the existing
+  FILE:LINE support has been expanded to other types of linespecs.  A
+  breakpoint will now have locations at all the matching points in all
+  inferiors, and locations will be added or subtracted according to
+  inferior changes.


Please let me know what you think about it,
build and tested with no regression on Fedora/x86_64

cordially,

Kevin


2011-12-08  Kevin Pouget  <kevin.pouget@st.com>
	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-bploc.o
	(SUBDIR_PYTHON_SRCS): Add python/py-bploc.c
	Add build rule for this file.
	* breakpoint.h (struct bploc_object): Forward declaration.
	(struct bp_location): Add py_bploc_obj.
	* breakpoint.c (free_bp_location): Call gdbpy_bplocation_free.
	* python/py-bploc.c: New file.
	* python/py-breakpoint.c (bppy_locations): New function.
	(breakpoint_object_methods): New method binding: locations().
	* python/python-internal.h (bploc_object): New typedef.
	(bplocation_to_bplocation_object): New prototype.
	(gdbpy_initialize_bplocation): Likewise.
	* python/python.c (gdbpy_bplocation_free): New empty stub.
	(_initialize_python): Call gdbpy_initialize_bplocation.
	* python/python.h (gdbpy_bplocation_free): New prototype.
	
doc/
	* gdb.texinfo (Breakpoints In Python): Document
	gdb.Breakpoint.locations and gdb.BpLocation.


testsuite/
	* gdb.python/py-breakpoint.exp: Test gdb.BpLocation.

[-- Attachment #2: 0001-Add-bp_location-to-Python-interface.patch --]
[-- Type: text/x-patch, Size: 22239 bytes --]

From e7c018f9ecdbd92199a86a1178225c7ae6828a39 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Wed, 18 May 2011 10:02:23 -0400
Subject: [PATCH] Add bp_location to Python interface

---
 gdb/Makefile.in                            |    6 +
 gdb/NEWS                                   |    4 +
 gdb/breakpoint.c                           |    3 +
 gdb/breakpoint.h                           |    6 +
 gdb/doc/gdb.texinfo                        |   37 ++++
 gdb/python/py-bploc.c                      |  313 ++++++++++++++++++++++++++++
 gdb/python/py-breakpoint.c                 |   36 ++++
 gdb/python/python-internal.h               |    6 +
 gdb/python/python.c                        |    7 +
 gdb/python/python.h                        |    2 +
 gdb/testsuite/gdb.python/py-breakpoint.exp |   52 +++++
 11 files changed, 472 insertions(+), 0 deletions(-)
 create mode 100644 gdb/python/py-bploc.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b71db33..4893d3b 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -281,6 +281,7 @@ SUBDIR_PYTHON_OBS = \
 	py-block.o \
 	py-bpevent.o \
 	py-breakpoint.o \
+	py-bploc.o \
 	py-cmd.o \
 	py-continueevent.o \
 	py-event.o \
@@ -312,6 +313,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-block.c \
 	python/py-bpevent.c \
 	python/py-breakpoint.c \
+	python/py-bploc.c \
 	python/py-cmd.c \
 	python/py-continueevent.c \
 	python/py-event.c \
@@ -2085,6 +2087,10 @@ py-breakpoint.o: $(srcdir)/python/py-breakpoint.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-breakpoint.c
 	$(POSTCOMPILE)
 
+py-bploc.o: $(srcdir)/python/py-bploc.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bploc.c
+	$(POSTCOMPILE)
+
 py-cmd.o: $(srcdir)/python/py-cmd.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-cmd.c
 	$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index 42782ce..33ebbb0 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 method "gdb.Breakpoint.locations" has been added, as well as
+     the class gdb.BpLocation to provide further details about breakpoint
+     locations.
+
   ** 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..026dcb48 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5681,6 +5681,9 @@ static void
 free_bp_location (struct bp_location *loc)
 {
   loc->ops->dtor (loc);
+
+  gdbpy_bplocation_free (loc);
+
   xfree (loc);
 }
 
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index ddf1881..5a8a1af 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -28,6 +28,7 @@
 struct value;
 struct block;
 struct breakpoint_object;
+struct bploc_object;
 struct get_number_or_range_state;
 struct thread_info;
 struct bpstats;
@@ -405,6 +406,11 @@ struct bp_location
   /* Source file name of this address.  */
 
   char *source_file;
+
+  /* Python object associated with this location.  May be NULL if the location
+     is not yet exported to Python.  */
+
+  struct bploc_object *py_bploc_obj;
 };
 
 /* This structure is a collection of function pointers that, if available,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 50c299e..453b181 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24335,6 +24335,43 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@findex gdb.locations
+@defun gdb.locations ()
+Return a tuple containing the @code{gdb.BpLocation} objects (see below)
+associated with this breakpoint.
+@end defun
+
+A breakpoint location is represented with a @code{gdb.BpLocation} object,
+which offers the following attributes (all read only) and methods.
+Please note that breakpoint locations are very transient entities in
+@value{GDBN}, so one should avoid keeping references to them.
+
+@defvar BpLocation.owner
+This attribute holds a reference to the @code{gdb.Breakpoint} object which
+owns this location.
+@end defvar
+
+@defvar BpLocation.enabled
+This attribute indicates whether this location is currently enabled or not.
+@end defvar
+
+@defvar BpLocation.inferior
+This attribute holds a reference to the @code{gdb.Inferior} inferior object
+in which this breakpoint location has been inserted.  The value will be 
+@code{None} if there is no inferior associated with this location.
+@end defvar
+
+@defvar BpLocation.address
+This attribute holds a @code{gdb.Value} object corresponding to the address 
+at which the breakpoint has been inserted.
+@end defvar
+
+@defun BpLocation.is_valid ()
+Returns @code{True} if the @code{gdb.BpLocation} object is valid,
+@code{False} if not.  A @code{gdb.BpLocation} object will become invalid
+if breakpoint to which is belong is deleted.
+@end defun
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/python/py-bploc.c b/gdb/python/py-bploc.c
new file mode 100644
index 0000000..1f9c74d
--- /dev/null
+++ b/gdb/python/py-bploc.c
@@ -0,0 +1,313 @@
+/* Python interface to breakpoint locations.
+
+   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 "inferior.h"
+#include "python-internal.h"
+#include "observer.h"
+#include "gdbarch.h"
+
+struct bploc_object
+{
+  PyObject_HEAD
+
+  /* The location corresponding to this py object.  NULL is the location
+     has been deleted.  */
+  struct bp_location *loc;
+
+  /* 1 if the owner BP has been deleted, 0 otherwise.  */
+  int invalid_owner;
+
+  /* Cache for the gdb.Value object corresponding to loc->address.  */
+  PyObject *py_address;
+};
+
+/* Require that LOCATION be a valid bp_location; throw a Python
+   exception if it is invalid.  */
+#define BPLOCPY_REQUIRE_VALID(Location)                                 \
+    do {                                                                \
+      if ((Location)->loc == NULL)                                      \
+        return PyErr_Format (PyExc_RuntimeError,                        \
+                             _("BpLocation invalid."));                 \
+    } while (0)
+
+static PyTypeObject bploc_object_type;
+
+/* Call by free_bp_location when loc is about to be freed.  */
+
+void
+gdbpy_bplocation_free (struct bp_location *loc)
+{
+  if (loc->py_bploc_obj)
+    {
+      loc->py_bploc_obj->loc = NULL;
+      Py_DECREF (loc->py_bploc_obj);
+    }
+}
+
+/* Dissociate the bp_location from the Python object.  */
+
+static void
+bplocpy_dealloc (PyObject *self)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc != NULL)
+    self_bploc->loc->py_bploc_obj = NULL;
+
+  Py_XDECREF (self_bploc->py_address);
+
+  self->ob_type->tp_free (self);
+}
+
+/* Create or acquire a ref to the bp_location object (gdb.BpLocation)
+   that encapsulates the struct bp_location from GDB.  */
+
+PyObject *
+bplocation_to_bplocation_object (struct bp_location *loc)
+{
+  bploc_object *bploc_obj;
+
+  gdb_assert (loc != NULL);
+  if (loc->py_bploc_obj)
+    {
+      Py_INCREF (loc->py_bploc_obj);
+      return (PyObject *) loc->py_bploc_obj;
+    }
+
+  bploc_obj = PyObject_New (bploc_object, &bploc_object_type);
+  if (!bploc_obj)
+    {
+      PyErr_SetString (PyExc_MemoryError,
+                       _("Could not allocate BpLocation object."));
+      return NULL;
+    }
+
+  bploc_obj->loc = loc;
+  bploc_obj->invalid_owner = 0;
+  bploc_obj->py_address = NULL;
+  loc->py_bploc_obj = bploc_obj;
+
+  return (PyObject *) bploc_obj;
+}
+
+/* Python function to get the BP owning this location, is any.  */
+
+static PyObject *
+bplocpy_get_owner (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->invalid_owner)
+    Py_RETURN_NONE;
+
+  if (self_bploc->loc->owner
+      && self_bploc->loc->owner->py_bp_object)
+    {
+      Py_INCREF (self_bploc->loc->owner->py_bp_object);
+      return (PyObject *) self_bploc->loc->owner->py_bp_object;
+    }
+
+  Py_RETURN_NONE;
+}
+
+/* Python function to test whether or not this breakpoint location is
+   enabled.  */
+
+static PyObject *
+bplocpy_get_enabled (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->loc->enabled)
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Python function to get the address of this breakpoint location. The
+   gdb.Value object will be cached if this is the first access. Returns
+   NULL in case of failure, with a python exception set.  */
+
+static PyObject *
+bplocpy_get_address (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (!self_bploc->py_address)
+    {
+      /* Get the address Value object as a void *.  */
+      volatile struct gdb_exception except;
+      struct type *void_ptr_type;
+      struct value *val = NULL ; /* Initialize to appease gcc warning.  */
+
+      TRY_CATCH (except, RETURN_MASK_ALL)
+        {
+          void_ptr_type = lookup_pointer_type (
+              builtin_type (python_gdbarch)->builtin_void);
+          val = value_from_pointer (void_ptr_type, self_bploc->loc->address);
+        }
+      GDB_PY_HANDLE_EXCEPTION (except);
+
+      self_bploc->py_address = value_to_value_object (val);
+      Py_XINCREF (self_bploc->py_address);
+    }
+
+  Py_XINCREF (self_bploc->py_address);
+
+  return self_bploc->py_address;
+}
+
+/* Python function to get the inferior hosting this breakpoint location.
+   Return Py_None if there is no inferior associated with the program space of
+   this location, or NULL in case of failure, with a python exception set.  */
+
+static PyObject *
+bplocpy_get_inferior (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+  struct inferior *inf;
+  PyObject *infobj;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  inf = find_inferior_for_program_space (self_bploc->loc->pspace);
+  if (!inf)
+    Py_RETURN_NONE;
+
+  infobj = inferior_to_inferior_object (inf);
+  Py_XINCREF (infobj);
+
+  return infobj;
+}
+
+/* Python function which checks the validity of a bp location object.  */
+
+static PyObject *
+bplocpy_is_valid (PyObject *self, PyObject *args)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+/* Callback triggered when a breakpoint is deleted.  This will invalidate
+   the corresponding bp_location Python object owners.  */
+
+static void
+bplocpy_breakpoint_deleted (struct breakpoint *b) {
+  struct bp_location *loc;
+
+  for (loc = b->loc; loc; loc = loc->next)
+    {
+      if (loc->py_bploc_obj)
+        loc->py_bploc_obj->invalid_owner = 1;
+    }
+}
+
+/* Initialize the Python bp_location code.  */
+
+void
+gdbpy_initialize_bplocation (void)
+{
+  if (PyType_Ready (&bploc_object_type) < 0)
+    return;
+
+  Py_INCREF (&bploc_object_type);
+  if (PyModule_AddObject (gdb_module, "BpLocation",
+                          (PyObject *) &bploc_object_type) < 0)
+    return;
+
+  observer_attach_breakpoint_deleted (bplocpy_breakpoint_deleted);
+}
+
+static PyGetSetDef bploc_object_getset[] =
+{
+  { "owner", bplocpy_get_owner, NULL,
+    "Each breakpoint location must belong to exactly one higher-level \
+breakpoint.  This pointer is NULL iff this bp_location is no \
+longer attached to a breakpoint (read-only).",
+    NULL },
+  { "enabled", bplocpy_get_enabled, NULL,
+    "Is this particular location enabled.", NULL },
+  { "address", bplocpy_get_address, NULL,
+    "The address at which the breakpoint has been set.", NULL },
+  { "inferior", bplocpy_get_inferior, NULL,
+    "The inferior in which this breakpoint location has been set.", NULL },
+  { NULL }  /* Sentinel.  */
+};
+
+
+static PyMethodDef bploc_object_methods[] =
+{
+  { "is_valid", bplocpy_is_valid, METH_NOARGS,
+    "Return true if this breakpoint location is valid, false if not." },
+  { NULL } /* Sentinel.  */
+};
+
+static PyTypeObject bploc_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,                                          /* ob_size */
+  "gdb.BpLocation",                           /* tp_name */
+  sizeof (bploc_object),                      /* tp_basicsize */
+  0,                                          /* tp_itemsize */
+  bplocpy_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 breakpoint location object",           /* tp_doc */
+  0,                                          /* tp_traverse */
+  0,                                          /* tp_clear */
+  0,                                          /* tp_richcompare */
+  0,                                          /* tp_weaklistoffset */
+  0,                                          /* tp_iter */
+  0,                                          /* tp_iternext */
+  bploc_object_methods,                       /* tp_methods */
+  0,                                          /* tp_members */
+  bploc_object_getset,                        /* tp_getset */
+  0,                                          /* tp_base */
+  0,                                          /* tp_dict */
+  0,                                          /* tp_descr_get */
+  0,                                          /* tp_descr_set */
+  0,                                          /* tp_dictoffset */
+  0,                                          /* tp_init */
+  0                                           /* tp_alloc */
+};
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 11d60fe..0fae747 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -620,6 +620,40 @@ bppy_get_ignore_count (PyObject *self, void *closure)
   return PyInt_FromLong (self_bp->bp->ignore_count);
 }
 
+
+/* Python function which returns the BpLocation objects associated
+   with this breakpoint.  */
+
+static PyObject *
+bppy_locations (PyObject *self, PyObject *args)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  PyObject *list;
+  struct bp_location *loc;
+  int err;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  list = PyList_New (0);
+  if (!list)
+    return NULL;
+
+  err = 0;
+  for (loc = self_bp->bp->loc; loc; loc = loc->next)
+    {
+      PyObject *loc_obj =  bplocation_to_bplocation_object (loc);
+      err = PyList_Append (list, loc_obj);
+      if (err == -1)
+        {
+          Py_DECREF (list);
+          return NULL;
+        }
+      Py_DECREF (loc_obj);
+    }
+
+  return PyList_AsTuple (list);
+}
+
 /* Python function to create a new breakpoint.  */
 static int
 bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
@@ -1003,6 +1037,8 @@ static PyMethodDef breakpoint_object_methods[] =
     "Return true if this breakpoint is valid, false if not." },
   { "delete", bppy_delete_breakpoint, METH_NOARGS,
     "Delete the underlying GDB breakpoint." },
+  { "locations", bppy_locations, METH_NOARGS,
+    "Get a list of gdb.BpLocation objects associated with this breakpoint." },
   { NULL } /* Sentinel.  */
 };
 
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index ef39d5d..49bb32e 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -124,6 +124,9 @@ extern PyTypeObject stop_event_object_type;
 /* Defined in py-breakpoint.c */
 typedef struct breakpoint_object breakpoint_object;
 
+/* Defined in py-bploc.c */
+typedef struct bploc_object bploc_object;
+
 typedef struct
 {
   PyObject_HEAD
@@ -176,6 +179,8 @@ PyObject *pspy_get_printers (PyObject *, void *);
 PyObject *objfile_to_objfile_object (struct objfile *);
 PyObject *objfpy_get_printers (PyObject *, void *);
 
+PyObject *bplocation_to_bplocation_object (struct bp_location *loc);
+
 thread_object *create_thread_object (struct thread_info *tp);
 thread_object *find_thread_object (ptid_t ptid);
 PyObject *find_inferior_object (int pid);
@@ -202,6 +207,7 @@ void gdbpy_initialize_functions (void);
 void gdbpy_initialize_pspace (void);
 void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_breakpoints (void);
+void gdbpy_initialize_bplocation (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 b0b9a9c..de6b51f 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1058,6 +1058,12 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj)
 		    "scripting is not supported."));
 }
 
+void
+gdbpy_bplocation_free (struct breakpoint_object *bp_obj)
+{
+  return;
+}
+
 #endif /* HAVE_PYTHON */
 
 \f
@@ -1247,6 +1253,7 @@ Enables or disables printing of Python stack traces."),
   gdbpy_initialize_pspace ();
   gdbpy_initialize_objfile ();
   gdbpy_initialize_breakpoints ();
+  gdbpy_initialize_bplocation ();
   gdbpy_initialize_lazy_string ();
   gdbpy_initialize_thread ();
   gdbpy_initialize_inferior ();
diff --git a/gdb/python/python.h b/gdb/python/python.h
index ae55cc2..cee8a6b 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_bplocation_free (struct bp_location *loc);
+
 #endif /* GDB_PYTHON_H */
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index e0dd087..d946d7b 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -301,3 +301,55 @@ gdb_py_test_silent_cmd  "python wp1 = wp_eval (\"result\", type=gdb.BP_WATCHPOIN
 gdb_test "continue" ".*\[Ww\]atchpoint.*result.*Old value =.*New value = 788.*" "Test watchpoint write"
 gdb_test "python print never_eval_bp1.count" "0" \
     "Check that this unrelated breakpoints eval function was never called."
+
+# gdb.BpLocation
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+delete_breakpoints
+gdb_test_no_output "set detach-on-fork off" "don't detach on fork"
+gdb_test "call fork()" "New process .*" "create a second inferior"
+
+gdb_breakpoint "main"
+gdb_test "py print len(gdb.breakpoints())" "1" "ensure that threre is only one BP"
+gdb_test_no_output {py bp0 = gdb.breakpoints()[0]} "save breakpoint 0"
+gdb_test "py print len(bp0.locations())" "2" "ensure that threre are 2 locations"
+
+gdb_test_no_output {py loc0 = bp0.locations()[0]} "save location 0"
+gdb_test_no_output {py loc1 = bp0.locations()[1]} "save location 1"
+
+gdb_test "py print loc0.owner == loc1.owner == bp0" "True" "verify ownership"
+gdb_test "py print loc0.address == loc1.address " "True" "verify addresses are identical"
+# how to check address location ? != address(main)
+
+gdb_test {py print loc0.inferior == gdb.inferiors()[0]} "True" "verify inferior for loc 0" #inf 2
+gdb_test {py print loc1.inferior == gdb.inferiors()[1]} "True" "verify inferior for loc 1" #inf 1
+
+gdb_test "py print loc0.enabled == loc1.enabled == True" "True" "verify that locations are enabled"
+
+gdb_test "py print loc0.inferior.num" "2" "ensure that loc0 is on inferior 2"
+
+gdb_test "detach inferior 2" "Detaching from program:.*" "detach inferior 2"
+gdb_test "inferior 1" "Switching to inferior .*" "switch to inferior 1"
+gdb_test_no_output "remove-inferiors 2" "remove inferior 2"
+gdb_test "py print loc0.inferior" "None" "removed inferior set to None"
+
+delete_breakpoints
+gdb_test "py print bp0.is_valid()" "False" "verify that BP has been invalidated"
+gdb_test "py bp0.locations()" ".*RuntimeError: Breakpoint .* is invalid.*"\
+         "verify that locations can't accessed on an invalid breakpoint"
+         
+gdb_test "py print loc0.is_valid()" "False" "verify that location is invalid"
+gdb_test "py print loc0.owner" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that owner can't be accessed"
+gdb_test "py print loc0.enabled" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.address" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.inferior" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that inferior can't be accessed"
-- 
1.7.6.4


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

* Re: [PATCH] Add bp_location to Python interface
  2011-12-08 10:17 [PATCH] Add bp_location to Python interface Kevin Pouget
@ 2011-12-08 13:39 ` Phil Muldoon
  2011-12-08 14:28   ` Kevin Pouget
  2011-12-08 14:56 ` Phil Muldoon
  1 sibling, 1 reply; 25+ messages in thread
From: Phil Muldoon @ 2011-12-08 13:39 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches

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

> Hello,
>
> I would like to discuss this patch which introduces a new Python class
> gdb.BpLocation, mapped from breakpoint.h::struct bp_location.

Thanks.

> I noticed that the Python interface doesn't offer so many details
> about breakpoint location, just gdb.Breakpoint.location, the string
> used to set the breakpoint (its linespec?)

Because we use gdb.Breakpoints for watchpoints as well, it also
represents the expression used to set the watchpoint.

> So this new class, which is currently strictly read-only and not
> instantiatable, exports the address and inferior in which the
> breakpoint was set (and an enabled flag, and a link to its owner
> breakpoint).

I think it should only ever be read-only.  

> BpLocation object are available through the gdb.Breakpoint.locations method.

If a user expected a string for a location, delivering ab object here
would break API?

> I think that this class would also help Python users to better
> control/understand where their breakpoints are set with Tom's recent
> changes about ambiguous linespec

Yes I think so too.

Cheers, Phil

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

* Re: [PATCH] Add bp_location to Python interface
  2011-12-08 13:39 ` Phil Muldoon
@ 2011-12-08 14:28   ` Kevin Pouget
  0 siblings, 0 replies; 25+ messages in thread
From: Kevin Pouget @ 2011-12-08 14:28 UTC (permalink / raw)
  To: pmuldoon; +Cc: gdb-patches

On Thu, Dec 8, 2011 at 2:08 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
> Kevin Pouget <kevin.pouget@gmail.com> writes:
>
>> Hello,
>>
>> I would like to discuss this patch which introduces a new Python class
>> gdb.BpLocation, mapped from breakpoint.h::struct bp_location.
>
> Thanks.
>
>> I noticed that the Python interface doesn't offer so many details
>> about breakpoint location, just gdb.Breakpoint.location, the string
>> used to set the breakpoint (its linespec?)
>
> Because we use gdb.Breakpoints for watchpoints as well, it also
> represents the expression used to set the watchpoint.

I didn't consider watchpoints at all during this work, I'll add it to
the testsuite to ensure it's handling correctly

>> So this new class, which is currently strictly read-only and not
>> instantiatable, exports the address and inferior in which the
>> breakpoint was set (and an enabled flag, and a link to its owner
>> breakpoint).
>
> I think it should only ever be read-only.

yes, certainly. My original idea was to allow Python to add new
BpLocations, but that's the way GDB was designed to work! Anyway, I
think that Tom's patch has solved the problem by correctly spreading
breakpoints to new inferiors.

>> BpLocation object are available through the gdb.Breakpoint.locations method.
>
> If a user expected a string for a location, delivering an object here
> would break API?

I'm not sure to get you right, but I didn't change the original API
behavior. There are now two ... method/atribute, `Breakpoint.location`
which returns a string (bp->addr_string) and
`Breakpoint.location_s_(self)' which returns a tuple of gdb.BpLocation
objects.

The name "locations" might be confusing, but I've got no other wording
for it so far, and I think it's too late to rename "location" to
"spec",

(there is a missing Py_DECREF(list) in bppy_locations, I'll fix it for
the next patch)

>> I think that this class would also help Python users to better
>> control/understand where their breakpoints are set with Tom's recent
>> changes about ambiguous linespec
>
> Yes I think so too.

another advantage is to allow the detection of pending breakpoints:

if len(bp.locations()) == 0, the breakpoint is pending

(and we'll have to find a way to find a way to keep GDB quite when a
pending breakpoint is created (I'll post a bug report for that):

> (gdb) py gdb.Breakpoint("pending")
> Function "pending" not defined.
> Breakpoint 2 (pending) pending.


Cordially,

Kevin

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

* Re: [PATCH] Add bp_location to Python interface
  2011-12-08 10:17 [PATCH] Add bp_location to Python interface Kevin Pouget
  2011-12-08 13:39 ` Phil Muldoon
@ 2011-12-08 14:56 ` Phil Muldoon
  2011-12-09 13:49   ` Kevin Pouget
  1 sibling, 1 reply; 25+ messages in thread
From: Phil Muldoon @ 2011-12-08 14:56 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches

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

>  11 files changed, 472 insertions(+), 0 deletions(-)
>  create mode 100644 gdb/python/py-bploc.c
>
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index b71db33..4893d3b 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -281,6 +281,7 @@ SUBDIR_PYTHON_OBS = \
>  	py-block.o \
>  	py-bpevent.o \
>  	py-breakpoint.o \
> +	py-bploc.o \

Nit, alphabetically ordered please.

>  	py-cmd.o \
>  	py-continueevent.o \
>  	py-event.o \
> @@ -312,6 +313,7 @@ SUBDIR_PYTHON_SRCS = \
>  	python/py-block.c \
>  	python/py-bpevent.c \
>  	python/py-breakpoint.c \
> +	python/py-bploc.c \

Ditto.


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

Could you me the order of this to correspond with the order above.

> +  ** A new method "gdb.Breakpoint.locations" has been added, as well as
> +     the class gdb.BpLocation to provide further details about breakpoint
> +     locations.
> +

Not sure if this will make it for 7.4.  If not you would have to start a
new section for Python in NEWS.


> +@findex gdb.locations
> +@defun gdb.locations ()
> +Return a tuple containing the @code{gdb.BpLocation} objects (see below)
> +associated with this breakpoint.
> +@end defun

Perhaps a "containing a sequence of @code{gdb.BpLocation} objects".

> +A breakpoint location is represented with a @code{gdb.BpLocation} object,
> +which offers the following attributes (all read only) and methods.
> +Please note that breakpoint locations are very transient entities in
> +@value{GDBN}, so one should avoid keeping references to them.

If the breakpoint is deleted and the user still has reference to a
location object, I think we should just run a validation routine and
refuse to do anything but raise an exception at that point (like
is_valid, but triggered for all function/attribute calls).  

While it is good to note that, I'm not sure  what we are explaining here
other than when the breakpoint is deleted, all location objects that are
associated with that object are invalid too.  Or, are you noting we
should allow the user to interact after the fact? If that is the case,
what is "is_valid" for?  Why note the transient nature of the object?

> +
> +@defvar BpLocation.owner
> +This attribute holds a reference to the @code{gdb.Breakpoint} object which
> +owns this location.
> +@end defvar

Note which attributes are read only/writable.  This and others.  Also
many different breakpoints can be at one location.  I'm not sure if it
is worth pointing out here that "this breakpoint" only owns the
location insofar as the scope of that breakpoint (there could be other
breakpoints set to that location).

> +@defvar BpLocation.address
> +This attribute holds a @code{gdb.Value} object corresponding to the address 
> +at which the breakpoint has been inserted.
> +@end defvar

To make sure I have the logic correctly, if a breakpoint has multiple
addresses then there will be on  BpLocation, for each  address?

> +@defun BpLocation.is_valid ()
> +Returns @code{True} if the @code{gdb.BpLocation} object is valid,
> +@code{False} if not.  A @code{gdb.BpLocation} object will become invalid
> +if breakpoint to which is belong is deleted.
> +@end defun

Typo, last sentence.  

> +struct bploc_object
> +{
> +  PyObject_HEAD
> +
> +  /* The location corresponding to this py object.  NULL is the location
> +     has been deleted.  */
> +  struct bp_location *loc;

Typo in the comment "NULL is the location has been deleted.".  Also nit
pick shouldn't it be "The location corresponding to a gdb.Breakpoint
object"?

> +
> +  /* 1 if the owner BP has been deleted, 0 otherwise.  */
> +  int invalid_owner;
> +
> +  /* Cache for the gdb.Value object corresponding to loc->address.  */
> +  PyObject *py_address;
> +};

I'm not sure if breakpoint locations can change.  I do not think so, but
why do we need to cache loc->address?

> +
> +/* Require that LOCATION be a valid bp_location; throw a Python
> +   exception if it is invalid.  */
> +#define BPLOCPY_REQUIRE_VALID(Location)                                 \
> +    do {                                                                \
> +      if ((Location)->loc == NULL)                                      \
> +        return PyErr_Format (PyExc_RuntimeError,                        \
> +                             _("BpLocation invalid."));                 \
> +    } while (0)

I prefer error messages a little more descriptive.  That is just a
personal thing of mine, but "BpLocation invalid" would seem cryptic when
thrown in the context of a script.

> +static PyTypeObject bploc_object_type;
> +
> +/* Call by free_bp_location when loc is about to be freed.  */
> +
> +void
> +gdbpy_bplocation_free (struct bp_location *loc)
> +{
> +  if (loc->py_bploc_obj)
> +    {
> +      loc->py_bploc_obj->loc = NULL;
> +      Py_DECREF (loc->py_bploc_obj);
> +    }
> +}
> +
> +/* Dissociate the bp_location from the Python object.  */
> +
> +static void
> +bplocpy_dealloc (PyObject *self)
> +{
> +  bploc_object *self_bploc = (bploc_object *) self;
> +
> +  if (self_bploc->loc != NULL)
> +    self_bploc->loc->py_bploc_obj = NULL;
> +
> +  Py_XDECREF (self_bploc->py_address);
> +
> +  self->ob_type->tp_free (self);
> +}
> +
> +/* Create or acquire a ref to the bp_location object (gdb.BpLocation)
> +   that encapsulates the struct bp_location from GDB.  */
> +
> +PyObject *
> +bplocation_to_bplocation_object (struct bp_location *loc)
> +{
> +  bploc_object *bploc_obj;
> +
> +  gdb_assert (loc != NULL);

Is this a fatal error that we need to shutdown GDB for?  gdb_assert
seems pretty strong from an API point of view.

> +  if (loc->py_bploc_obj)
> +    {
> +      Py_INCREF (loc->py_bploc_obj);
> +      return (PyObject *) loc->py_bploc_obj;
> +    }
> +
> +  bploc_obj = PyObject_New (bploc_object, &bploc_object_type);
> +  if (!bploc_obj)
> +    {
> +      PyErr_SetString (PyExc_MemoryError,
> +                       _("Could not allocate BpLocation object."));
> +      return NULL;
> +    }

Remove the error setting here.  If a new object cannot be allocated, the
exception will already be set by Python.  So we would be overwriting
that with a more generic message.  So just return NULL.


> +/* Python function to get the BP owning this location, is any.  */

Typo, "is".

> +/* Python function to get the address of this breakpoint location. The
> +   gdb.Value object will be cached if this is the first access. Returns
> +   NULL in case of failure, with a python exception set.  */

Two spaces after ".".
Nit, Python not python.


> +static PyObject *
> +bplocpy_get_address (PyObject *self, void *closure)
> +{
> +  bploc_object *self_bploc = (bploc_object *) self;
> +
> +  BPLOCPY_REQUIRE_VALID (self_bploc);
> +
> +  if (!self_bploc->py_address)
> +    {
> +      /* Get the address Value object as a void *.  */
> +      volatile struct gdb_exception except;
> +      struct type *void_ptr_type;
> +      struct value *val = NULL ; /* Initialize to appease gcc warning.  */
> +
> +      TRY_CATCH (except, RETURN_MASK_ALL)
> +        {
> +          void_ptr_type = lookup_pointer_type (
> +              builtin_type (python_gdbarch)->builtin_void);
> +          val = value_from_pointer (void_ptr_type, self_bploc->loc->address);
> +        }
> +      GDB_PY_HANDLE_EXCEPTION (except);
> +
> +      self_bploc->py_address = value_to_value_object (val);
> +      Py_XINCREF (self_bploc->py_address);
> +    }
> +  Py_XINCREF (self_bploc->py_address);

I don't really mind it, but I prefer explicit return NULL when dealing
with cases of exceptions.  I find the other logic hard to read.  This is
not a request for a change. Is there a case where py_address will be
NULL?  Yes, there is, value_to_value_object can return NULL.  If it
returns NULL, then there is an exception set.  I much prefer to exit
then and there, other the conditional XINCREF step, and returning at the
function exit.  Still, this is just a stylistic thing, and probably
personal thing.  The second XINCREF can just be a plain old INCREF as we
already tested for NULL.

This brings me to the address cache argument.  Is it worthwhile managing
the cache increment counts instead of just returning the address each
time?  I ask as I am not sure if you have done any metrics that indicate
this is a slow operation.


> +
> +/* Python function which returns the BpLocation objects associated
> +   with this breakpoint.  */
> +
> +static PyObject *
> +bppy_locations (PyObject *self, PyObject *args)
> +{
> +  breakpoint_object *self_bp = (breakpoint_object *) self;
> +  PyObject *list;
> +  struct bp_location *loc;
> +  int err;
> +
> +  BPPY_REQUIRE_VALID (self_bp);
> +
> +  list = PyList_New (0);
> +  if (!list)
> +    return NULL;
> +
> +  err = 0;
> +  for (loc = self_bp->bp->loc; loc; loc = loc->next)
> +    {
> +      PyObject *loc_obj =  bplocation_to_bplocation_object (loc);
> +      err = PyList_Append (list, loc_obj);
> +      if (err == -1)
> +        {
> +          Py_DECREF (list);
> +          return NULL;
> +        }
> +      Py_DECREF (loc_obj);
> +    }
> +
> +  return PyList_AsTuple (list);
> +}

We need to make sure that the this is not a watchpoint.

Cheers,

Phil

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

* Re: [PATCH] Add bp_location to Python interface
  2011-12-08 14:56 ` Phil Muldoon
@ 2011-12-09 13:49   ` Kevin Pouget
  2011-12-09 14:15     ` Phil Muldoon
  0 siblings, 1 reply; 25+ messages in thread
From: Kevin Pouget @ 2011-12-09 13:49 UTC (permalink / raw)
  To: pmuldoon; +Cc: gdb-patches

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

On Thu, Dec 8, 2011 at 3:28 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:

Thanks for taking a look at the patch, I reply inline

>> +     py-bploc.o \
>
> Nit, alphabetically ordered please.
>
>> +     python/py-bploc.c \
>
> Ditto.
>
>> +py-bploc.o: $(srcdir)/python/py-bploc.c
>> +     $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bploc.c
>> +     $(POSTCOMPILE)
>
> Could you me the order of this to correspond with the order above.

fixed

>> +  ** A new method "gdb.Breakpoint.locations" has been added, as well as
>> +     the class gdb.BpLocation to provide further details about breakpoint
>> +     locations.
>> +
>
> Not sure if this will make it for 7.4.  If not you would have to start a
> new section for Python in NEWS.

I'm not sure either, I changed it to 7.4 for now, we'll change it
later if we can make it in time

>> +@findex gdb.locations
>> +@defun gdb.locations ()
>> +Return a tuple containing the @code{gdb.BpLocation} objects (see below)
>> +associated with this breakpoint.
>> +@end defun
>
> Perhaps a "containing a sequence of @code{gdb.BpLocation} objects".

'tuple' and 'sequence' sound a bit redundant to me but I'll trust your
English and Python skills on that!

>> +A breakpoint location is represented with a @code{gdb.BpLocation} object,
>> +which offers the following attributes (all read only) and methods.
>> +Please note that breakpoint locations are very transient entities in
>> +@value{GDBN}, so one should avoid keeping references to them.

>
> While it is good to note that, I'm not sure  what we are explaining here
> other than when the breakpoint is deleted, all location objects that are
> associated with that object are invalid too.  Or, are you noting we
> should allow the user to interact after the fact? If that is the case,
> what is "is_valid" for?  Why note the transient nature of the object?

> I'm not sure what we are explaining here other than when the breakpoint is deleted

that's what I wanted to emphasize here, bp_locations are cleaned/reset
more often than 'high-level' breakpoints are removed. My knowledge
about that is quite limited, but for instance this (cleaned up) stack:

#0  gdbpy_bplocation_free py-bploc.c:60
#1  free_bp_location (loc=) breakpoint.c:5685
#2  decref_bp_location (blp=) breakpoint.c:5707
#3  update_global_location_list (should_insert=1)  breakpoint.c:10575
#4  update_breakpoint_locations (b=, sals=...,     sals_end=...)
breakpoint.c:11787
#5  breakpoint_re_set_default (b=) breakpoint.c:11937
#6  breakpoint_re_set_one (bint=) breakpoint.c:11968
#8  breakpoint_re_set () breakpoint.c:11992
#9  solib_add (pattern=0x0, from_tty=0, target=,  readsyms=1) solib.c:926
#10 bpstat_what (bs_head=) breakpoint.c:4487
#11 handle_inferior_event (ecs=) infrun.c:4394
#12 wait_for_inferior ()

shows that bplocations might be removed when a shared library is loaded

> If the breakpoint is deleted and the user still has reference to a
> location object, I think we should just run a validation routine and
> refuse to do anything but raise an exception at that point (like
> is_valid, but triggered for all function/attribute calls).

hum, I think that's what I already do in the code :)

I've updated the gdb.BpLocation.is_valid documentation, maybe it's clearer?

> @defun BpLocation.is_valid ()
> Returns @code{True} if the @code{gdb.BpLocation} object is valid,
> @code{False} if not.  A @code{gdb.BpLocation} object may be invalidated by
> GDB at any moment for internal reasons. All other @code{gdb.BpLocation} methods
> and attributes will throw an exception if the object is invalid.
> @end defun

>> +
>> +@defvar BpLocation.owner
>> +This attribute holds a reference to the @code{gdb.Breakpoint} object which
>> +owns this location.
>> +@end defvar

> Note which attributes are read only/writable.  This and others.
I specify a few lines above that all the attributes are read only, but
I've noted it on each attribute as well.


> Also many different breakpoints can be at one location.  I'm not sure if it
> is worth pointing out here that "this breakpoint" only owns the
> location insofar as the scope of that breakpoint (there could be other
> breakpoints set to that location).

from an implementation point of view, the is a BP  <1--------n>
BpLocation relation, even if two locations have the same address.

Also, I think that the end-users won't have problems to understand
these BpLocations, as they're already exposed with "info breakpoints":

(gdb) info breakpoints
Num     Type           Disp Enb Address    What
2       breakpoint     keep y   <MULTIPLE>
2.1                         y     0x08049074 in main at
src/host/matrix/main.c:223 inf 2
2.2                         y     0x00118125 in main at src/entry.c:38 inf 2
2.3                         y     0x08049074 in main at
src/host/matrix/main.c:223 inf 1
2.4                         y     0x00118125 in main at src/entry.c:38 inf 1

 py print "\n".join(["%s -> inf = %d, addr = %s" % (loc,
loc.inferior.num, loc.address) for loc in
gdb.breakpoints()[0].locations()])
<gdb.BpLocation object at 0x258c490> -> inf = 2, addr = 0x8049074
<gdb.BpLocation object at 0x258c6c0> -> inf = 2, addr = 0x118125
<gdb.BpLocation object at 0x258c6e8> -> inf = 1, addr = 0x8049074
<gdb.BpLocation object at 0x258c738> -> inf = 1, addr = 0x118125

>> +@defvar BpLocation.address
>> +This attribute holds a @code{gdb.Value} object corresponding to the address
>> +at which the breakpoint has been inserted.
>> +@end defvar
>
> To make sure I have the logic correctly, if a breakpoint has multiple
> addresses then there will be on  BpLocation, for each  address?

yes, as described above

>> +@defun BpLocation.is_valid ()
>> +Returns @code{True} if the @code{gdb.BpLocation} object is valid,
>> +@code{False} if not.  A @code{gdb.BpLocation} object will become invalid
>> +if breakpoint to which is belong is deleted.
>> +@end defun
>
> Typo, last sentence.

sentence rephrased

>> +struct bploc_object
>> +{
>> +  PyObject_HEAD
>> +
>> +  /* The location corresponding to this py object.  NULL is the location
>> +     has been deleted.  */
>> +  struct bp_location *loc;
>
> Typo in the comment "NULL is the location has been deleted.".  Also nit
> pick shouldn't it be "The location corresponding to a gdb.Breakpoint
> object"?

typo fixed, but not the nit: it's the same idea as breakpoint,
if the 'backend' breakpoint is deleted, 'struct breakpoint_object . bp' is NULL,
if the 'backend' location is deleted, 'struct bploc_object . loc' is NULL

>> +
>> +  /* 1 if the owner BP has been deleted, 0 otherwise.  */
>> +  int invalid_owner;
>> +
>> +  /* Cache for the gdb.Value object corresponding to loc->address.  */
>> +  PyObject *py_address;
>> +};
>
> I'm not sure if breakpoint locations can change.  I do not think so, but
> why do we need to cache loc->address?

I assumed that it can't change, but didn't actually investigate all
the implementation.
My feeling is "caching is easy, creating a Py object has a cost, so
let's cache!", but I've got no idea about the actual cost, so I'll
change it if someone insists [and convinces me] :)

>> +
>> +/* Require that LOCATION be a valid bp_location; throw a Python
>> +   exception if it is invalid.  */
>> +#define BPLOCPY_REQUIRE_VALID(Location)                                 \
>> +    do {                                                                \
>> +      if ((Location)->loc == NULL)                                      \
>> +        return PyErr_Format (PyExc_RuntimeError,                        \
>> +                             _("BpLocation invalid."));                 \
>> +    } while (0)
>
> I prefer error messages a little more descriptive.  That is just a
> personal thing of mine, but "BpLocation invalid" would seem cryptic when
> thrown in the context of a script.

I'm not sure how I could improve it, at this point (checking validity
at the beginning of all the attributes), we just know that the backend
location has been freed, but we don't know why.

>> +static PyTypeObject bploc_object_type;
>> +
>> +/* Call by free_bp_location when loc is about to be freed.  */
>> +
>> +void
>> +gdbpy_bplocation_free (struct bp_location *loc)
>> +{
>> +  if (loc->py_bploc_obj)
>> +    {
>> +      loc->py_bploc_obj->loc = NULL;
>> +      Py_DECREF (loc->py_bploc_obj);
>> +    }
>> +}
>> +
>> +/* Dissociate the bp_location from the Python object.  */
>> +
>> +static void
>> +bplocpy_dealloc (PyObject *self)
>> +{
>> +  bploc_object *self_bploc = (bploc_object *) self;
>> +
>> +  if (self_bploc->loc != NULL)
>> +    self_bploc->loc->py_bploc_obj = NULL;
>> +
>> +  Py_XDECREF (self_bploc->py_address);
>> +
>> +  self->ob_type->tp_free (self);
>> +}
>> +
>> +/* Create or acquire a ref to the bp_location object (gdb.BpLocation)
>> +   that encapsulates the struct bp_location from GDB.  */
>> +
>> +PyObject *
>> +bplocation_to_bplocation_object (struct bp_location *loc)
>> +{
>> +  bploc_object *bploc_obj;
>> +
>> +  gdb_assert (loc != NULL);
>
> Is this a fatal error that we need to shutdown GDB for?  gdb_assert
> seems pretty strong from an API point of view.

hum, I'm not sure here,
I wrote this line to actually shutdown GDB instead of segfaulting on
the next line,
for instance 'py-pspace.c::pspace_to_pspace_object' and
'py-objfile.c::objfile_to_objfile_object" will just segfault if
there're called with a NULL parameter.

throwing an Python/GDB exception wouldn't make much sense to me
either, as there end-user won't be able to deal with it

>> +  if (loc->py_bploc_obj)
>> +    {
>> +      Py_INCREF (loc->py_bploc_obj);
>> +      return (PyObject *) loc->py_bploc_obj;
>> +    }
>> +
>> +  bploc_obj = PyObject_New (bploc_object, &bploc_object_type);
>> +  if (!bploc_obj)
>> +    {
>> +      PyErr_SetString (PyExc_MemoryError,
>> +                       _("Could not allocate BpLocation object."));
>> +      return NULL;
>> +    }
>
> Remove the error setting here.  If a new object cannot be allocated, the
> exception will already be set by Python.  So we would be overwriting
> that with a more generic message.  So just return NULL.

fine

>> +/* Python function to get the BP owning this location, is any.  */
>
> Typo, "is".

fixed

>> +/* Python function to get the address of this breakpoint location. The
>> +   gdb.Value object will be cached if this is the first access. Returns
>> +   NULL in case of failure, with a python exception set.  */
>
> Two spaces after ".".
> Nit, Python not python.

fixed

>> +static PyObject *
>> +bplocpy_get_address (PyObject *self, void *closure)
>> +{
>> +  bploc_object *self_bploc = (bploc_object *) self;
>> +
>> +  BPLOCPY_REQUIRE_VALID (self_bploc);
>> +
>> +  if (!self_bploc->py_address)
>> +    {
>> +      /* Get the address Value object as a void *.  */
>> +      volatile struct gdb_exception except;
>> +      struct type *void_ptr_type;
>> +      struct value *val = NULL ; /* Initialize to appease gcc warning.  */
>> +
>> +      TRY_CATCH (except, RETURN_MASK_ALL)
>> +        {
>> +          void_ptr_type = lookup_pointer_type (
>> +              builtin_type (python_gdbarch)->builtin_void);
>> +          val = value_from_pointer (void_ptr_type, self_bploc->loc->address);
>> +        }
>> +      GDB_PY_HANDLE_EXCEPTION (except);
>> +
>> +      self_bploc->py_address = value_to_value_object (val);
>> +      Py_XINCREF (self_bploc->py_address);
>> +    }
>> +  Py_XINCREF (self_bploc->py_address);
>
> I don't really mind it, but I prefer explicit return NULL when dealing
> with cases of exceptions.  I find the other logic hard to read.  This is
> not a request for a change. Is there a case where py_address will be
> NULL?  Yes, there is, value_to_value_object can return NULL.  If it
> returns NULL, then there is an exception set.  I much prefer to exit
> then and there, other the conditional XINCREF step, and returning at the
> function exit.  Still, this is just a stylistic thing, and probably
> personal thing.  The second XINCREF can just be a plain old INCREF as we
> already tested for NULL.

makes sense to me, my "single return point" idea doesn't stand against
error handling

> This brings me to the address cache argument.  Is it worthwhile managing
> the cache increment counts instead of just returning the address each
> time?  I ask as I am not sure if you have done any metrics that indicate
> this is a slow operation.

no, I didn't run any measurement so far; I'll wait for a maintainer
point of view about that

>> +
>> +/* Python function which returns the BpLocation objects associated
>> +   with this breakpoint.  */
>> +
>> +static PyObject *
>> +bppy_locations (PyObject *self, PyObject *args)
>> +{
>> +  breakpoint_object *self_bp = (breakpoint_object *) self;
>> +  PyObject *list;
>> +  struct bp_location *loc;
>> +  int err;
>> +
>> +  BPPY_REQUIRE_VALID (self_bp);
>> +
>> +  list = PyList_New (0);
>> +  if (!list)
>> +    return NULL;
>> +
>> +  err = 0;
>> +  for (loc = self_bp->bp->loc; loc; loc = loc->next)
>> +    {
>> +      PyObject *loc_obj =  bplocation_to_bplocation_object (loc);
>> +      err = PyList_Append (list, loc_obj);
>> +      if (err == -1)
>> +        {
>> +          Py_DECREF (list);
>> +          return NULL;
>> +        }
>> +      Py_DECREF (loc_obj);
>> +    }
>> +
>> +  return PyList_AsTuple (list);
>> +}
>
> We need to make sure that the this is not a watchpoint.

why not?
I don't know much about watchpoints, but as per my quick
investigations, they share the same high-level structure (struct
breakpoint), and they seem to use bp_location the same way ?

> Cheers,
>
> Phil

Thanks for your review, I hope I addressed or discussed everything

cordially,

Kevin

--

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

	Add bp_location to Python interface
	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-bploc.o
	(SUBDIR_PYTHON_SRCS): Add python/py-bploc.c
	Add build rule for this file.
	* breakpoint.h (struct bploc_object): Forward declaration.
	(struct bp_location): Add py_bploc_obj.
	* breakpoint.c (free_bp_location): Call gdbpy_bplocation_free.
	* python/py-bploc.c: New file.
	* python/py-breakpoint.c (bppy_locations): New function.
	(breakpoint_object_methods): New method binding: locations().
	* python/python-internal.h (bploc_object): New typedef.
	(bplocation_to_bplocation_object): New prototype.
	(gdbpy_initialize_bplocation): Likewise.
	* python/python.c (gdbpy_bplocation_free): New empty stub.
	(_initialize_python): Call gdbpy_initialize_bplocation.
	* python/python.h (gdbpy_bplocation_free): New prototype.
	
doc/
	Add bp_location to Python interface
	* gdb.texinfo (Breakpoints In Python): Document
	gdb.Breakpoint.locations and gdb.BpLocation.


testsuite/
	Add bp_location to Python interface
	* gdb.python/py-breakpoint.exp: Test gdb.BpLocation.

[-- Attachment #2: 0001-Add-bp_location-to-Python-interface.patch --]
[-- Type: text/x-patch, Size: 22545 bytes --]

From c3a7e591a45b601aa9b240415798106e4a98c08c Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Wed, 18 May 2011 10:02:23 -0400
Subject: [PATCH] Add bp_location to Python interface

---
 gdb/Makefile.in                            |    6 +
 gdb/NEWS                                   |    8 +
 gdb/breakpoint.c                           |    3 +
 gdb/breakpoint.h                           |    6 +
 gdb/doc/gdb.texinfo                        |   41 ++++
 gdb/python/py-bploc.c                      |  313 ++++++++++++++++++++++++++++
 gdb/python/py-breakpoint.c                 |   39 ++++
 gdb/python/python-internal.h               |    6 +
 gdb/python/python.c                        |    7 +
 gdb/python/python.h                        |    2 +
 gdb/testsuite/gdb.python/py-breakpoint.exp |   52 +++++
 11 files changed, 483 insertions(+), 0 deletions(-)
 create mode 100644 gdb/python/py-bploc.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b71db33..58b4910 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -280,6 +280,7 @@ SUBDIR_PYTHON_OBS = \
 	py-auto-load.o \
 	py-block.o \
 	py-bpevent.o \
+	py-bploc.o \
 	py-breakpoint.o \
 	py-cmd.o \
 	py-continueevent.o \
@@ -311,6 +312,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-auto-load.c \
 	python/py-block.c \
 	python/py-bpevent.c \
+	python/py-bploc.c \
 	python/py-breakpoint.c \
 	python/py-cmd.c \
 	python/py-continueevent.c \
@@ -2081,6 +2083,10 @@ py-bpevent.o: $(srcdir)/python/py-bpevent.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bpevent.c
 	$(POSTCOMPILE)
 
+py-bploc.o: $(srcdir)/python/py-bploc.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bploc.c
+	$(POSTCOMPILE)
+
 py-breakpoint.o: $(srcdir)/python/py-breakpoint.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-breakpoint.c
 	$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index 42782ce..4344fc9 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -1,6 +1,14 @@
 		What has changed in GDB?
 	     (Organized release by release)
 
+*** Changes since GDB 7.4
+
+* Python scripting
+
+  ** A new method "gdb.Breakpoint.locations" has been added, as well as
+     the class gdb.BpLocation to provide further details about breakpoint
+     locations.
+
 *** Changes since GDB 7.3.1
 
 * GDB now handles ambiguous linespecs more consistently; the existing
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index d9d5bbe..026dcb48 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5681,6 +5681,9 @@ static void
 free_bp_location (struct bp_location *loc)
 {
   loc->ops->dtor (loc);
+
+  gdbpy_bplocation_free (loc);
+
   xfree (loc);
 }
 
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index ddf1881..5a8a1af 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -28,6 +28,7 @@
 struct value;
 struct block;
 struct breakpoint_object;
+struct bploc_object;
 struct get_number_or_range_state;
 struct thread_info;
 struct bpstats;
@@ -405,6 +406,11 @@ struct bp_location
   /* Source file name of this address.  */
 
   char *source_file;
+
+  /* Python object associated with this location.  May be NULL if the location
+     is not yet exported to Python.  */
+
+  struct bploc_object *py_bploc_obj;
 };
 
 /* This structure is a collection of function pointers that, if available,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 50c299e..1f0ac7f 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24335,6 +24335,47 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@findex gdb.locations
+@defun gdb.locations ()
+Return a tuple containing a sequence of @code{gdb.BpLocation} objects 
+(see below) associated with this breakpoint.  A breakpoint with no location
+is a pending breakpoint (@xref{Set Breaks, , pending breakpoints}).
+@end defun
+
+A breakpoint location is represented with a @code{gdb.BpLocation} object,
+which offers the following attributes (all read only) and methods.
+Please note that breakpoint locations are very transient entities in
+@value{GDBN}, so one should avoid keeping references to them.
+
+@defvar BpLocation.owner
+This attribute holds a reference to the @code{gdb.Breakpoint} object which
+owns this location.  This attribute is not writable. 
+@end defvar
+
+@defvar BpLocation.enabled
+This attribute indicates whether this location is currently enabled or not.
+This attribute is not writable.
+@end defvar
+
+@defvar BpLocation.inferior
+This attribute holds a reference to the @code{gdb.Inferior} inferior object
+in which this breakpoint location has been inserted.  The value will be 
+@code{None} if there is no inferior associated with this location.   This 
+attribute is not writable.
+@end defvar
+
+@defvar BpLocation.address
+This attribute holds a @code{gdb.Value} object corresponding to the address 
+at which the breakpoint has been inserted.   This attribute is not writable.
+@end defvar
+
+@defun BpLocation.is_valid ()
+Returns @code{True} if the @code{gdb.BpLocation} object is valid,
+@code{False} if not.  A @code{gdb.BpLocation} object may be invalidated by
+GDB at any moment for internal reasons.  All other @code{gdb.BpLocation}
+methods and attributes will throw an exception if the object is invalid.
+@end defun
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/python/py-bploc.c b/gdb/python/py-bploc.c
new file mode 100644
index 0000000..271a44b
--- /dev/null
+++ b/gdb/python/py-bploc.c
@@ -0,0 +1,313 @@
+/* Python interface to breakpoint locations.
+
+   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 "inferior.h"
+#include "python-internal.h"
+#include "observer.h"
+#include "gdbarch.h"
+
+struct bploc_object
+{
+  PyObject_HEAD
+
+  /* The location corresponding to this py object.  NULL if the location
+     has been deleted.  */
+  struct bp_location *loc;
+
+  /* 1 if the owner BP has been deleted, 0 otherwise.  */
+  int invalid_owner;
+
+  /* Cache for the gdb.Value object corresponding to loc->address.  */
+  PyObject *py_address;
+};
+
+/* Require that LOCATION be a valid bp_location; throw a Python
+   exception if it is invalid.  */
+#define BPLOCPY_REQUIRE_VALID(Location)                                 \
+    do {                                                                \
+      if ((Location)->loc == NULL)                                      \
+        return PyErr_Format (PyExc_RuntimeError,                        \
+                             _("BpLocation invalid."));                 \
+    } while (0)
+
+static PyTypeObject bploc_object_type;
+
+/* Call by free_bp_location when loc is about to be freed.  */
+
+void
+gdbpy_bplocation_free (struct bp_location *loc)
+{
+  if (loc->py_bploc_obj)
+    {
+      loc->py_bploc_obj->loc = NULL;
+      Py_DECREF (loc->py_bploc_obj);
+    }
+}
+
+/* Dissociate the bp_location from the Python object.  */
+
+static void
+bplocpy_dealloc (PyObject *self)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc != NULL)
+    self_bploc->loc->py_bploc_obj = NULL;
+
+  Py_XDECREF (self_bploc->py_address);
+
+  self->ob_type->tp_free (self);
+}
+
+/* Create or acquire a ref to the bp_location object (gdb.BpLocation)
+   that encapsulates the struct bp_location from GDB.  */
+
+PyObject *
+bplocation_to_bplocation_object (struct bp_location *loc)
+{
+  bploc_object *bploc_obj;
+
+  gdb_assert (loc != NULL);
+  if (loc->py_bploc_obj)
+    {
+      Py_INCREF (loc->py_bploc_obj);
+      return (PyObject *) loc->py_bploc_obj;
+    }
+
+  bploc_obj = PyObject_New (bploc_object, &bploc_object_type);
+  if (!bploc_obj)
+    return NULL;
+
+  bploc_obj->loc = loc;
+  bploc_obj->invalid_owner = 0;
+  bploc_obj->py_address = NULL;
+  Py_INCREF (bploc_obj);
+  loc->py_bploc_obj = bploc_obj;
+
+  return (PyObject *) bploc_obj;
+}
+
+/* Python function to get the BP owning this location, if any.  */
+
+static PyObject *
+bplocpy_get_owner (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->invalid_owner)
+    Py_RETURN_NONE;
+
+  if (self_bploc->loc->owner
+      && self_bploc->loc->owner->py_bp_object)
+    {
+      Py_INCREF (self_bploc->loc->owner->py_bp_object);
+      return (PyObject *) self_bploc->loc->owner->py_bp_object;
+    }
+
+  Py_RETURN_NONE;
+}
+
+/* Python function to test whether or not this breakpoint location is
+   enabled.  */
+
+static PyObject *
+bplocpy_get_enabled (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->loc->enabled)
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Python function to get the address of this breakpoint location.  The
+   gdb.Value object will be cached if this is the first access.  Returns
+   NULL in case of failure, with a Python exception set.  */
+
+static PyObject *
+bplocpy_get_address (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (!self_bploc->py_address)
+    {
+      /* Get the address Value object as a void *.  */
+      volatile struct gdb_exception except;
+      struct type *void_ptr_type;
+      struct value *val = NULL; /* Initialize to appease gcc warning.  */
+
+      TRY_CATCH (except, RETURN_MASK_ALL)
+        {
+          void_ptr_type = lookup_pointer_type (
+              builtin_type (python_gdbarch)->builtin_void);
+          val = value_from_pointer (void_ptr_type, self_bploc->loc->address);
+        }
+      GDB_PY_HANDLE_EXCEPTION (except);
+
+      self_bploc->py_address = value_to_value_object (val);
+      if (!self_bploc->py_address)
+        return NULL;
+
+      Py_INCREF (self_bploc->py_address);
+    }
+
+  Py_INCREF (self_bploc->py_address);
+
+  return self_bploc->py_address;
+}
+
+/* Python function to get the inferior hosting this breakpoint location.
+   Return Py_None if there is no inferior associated with the program space of
+   this location, or NULL in case of failure, with a python exception set.  */
+
+static PyObject *
+bplocpy_get_inferior (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+  struct inferior *inf;
+  PyObject *infobj;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  inf = find_inferior_for_program_space (self_bploc->loc->pspace);
+  if (!inf)
+    Py_RETURN_NONE;
+
+  infobj = inferior_to_inferior_object (inf);
+  Py_XINCREF (infobj);
+
+  return infobj;
+}
+
+/* Python function which checks the validity of a bp location object.  */
+
+static PyObject *
+bplocpy_is_valid (PyObject *self, PyObject *args)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+/* Callback triggered when a breakpoint is deleted.  This will invalidate
+   the corresponding bp_location Python object owners.  */
+
+static void
+bplocpy_breakpoint_deleted (struct breakpoint *b) {
+  struct bp_location *loc;
+
+  for (loc = b->loc; loc; loc = loc->next)
+    {
+      if (loc->py_bploc_obj)
+        loc->py_bploc_obj->invalid_owner = 1;
+    }
+}
+
+/* Initialize the Python bp_location code.  */
+
+void
+gdbpy_initialize_bplocation (void)
+{
+  if (PyType_Ready (&bploc_object_type) < 0)
+    return;
+
+  Py_INCREF (&bploc_object_type);
+  if (PyModule_AddObject (gdb_module, "BpLocation",
+                          (PyObject *) &bploc_object_type) < 0)
+    return;
+
+  observer_attach_breakpoint_deleted (bplocpy_breakpoint_deleted);
+}
+
+static PyGetSetDef bploc_object_getset[] =
+{
+  { "owner", bplocpy_get_owner, NULL,
+    "Each breakpoint location must belong to exactly one higher-level \
+breakpoint.  This pointer is NULL iff this bp_location is no \
+longer attached to a breakpoint (read-only).",
+    NULL },
+  { "enabled", bplocpy_get_enabled, NULL,
+    "Is this particular location enabled.", NULL },
+  { "address", bplocpy_get_address, NULL,
+    "The address at which the breakpoint has been set.", NULL },
+  { "inferior", bplocpy_get_inferior, NULL,
+    "The inferior in which this breakpoint location has been set.", NULL },
+  { NULL }  /* Sentinel.  */
+};
+
+
+static PyMethodDef bploc_object_methods[] =
+{
+  { "is_valid", bplocpy_is_valid, METH_NOARGS,
+    "Return true if this breakpoint location is valid, false if not." },
+  { NULL } /* Sentinel.  */
+};
+
+static PyTypeObject bploc_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,                                          /* ob_size */
+  "gdb.BpLocation",                           /* tp_name */
+  sizeof (bploc_object),                      /* tp_basicsize */
+  0,                                          /* tp_itemsize */
+  bplocpy_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 breakpoint location object",           /* tp_doc */
+  0,                                          /* tp_traverse */
+  0,                                          /* tp_clear */
+  0,                                          /* tp_richcompare */
+  0,                                          /* tp_weaklistoffset */
+  0,                                          /* tp_iter */
+  0,                                          /* tp_iternext */
+  bploc_object_methods,                       /* tp_methods */
+  0,                                          /* tp_members */
+  bploc_object_getset,                        /* tp_getset */
+  0,                                          /* tp_base */
+  0,                                          /* tp_dict */
+  0,                                          /* tp_descr_get */
+  0,                                          /* tp_descr_set */
+  0,                                          /* tp_dictoffset */
+  0,                                          /* tp_init */
+  0                                           /* tp_alloc */
+};
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 11d60fe..cccbf0e 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -620,6 +620,43 @@ bppy_get_ignore_count (PyObject *self, void *closure)
   return PyInt_FromLong (self_bp->bp->ignore_count);
 }
 
+
+/* Python function which returns the BpLocation objects associated
+   with this breakpoint.  */
+
+static PyObject *
+bppy_locations (PyObject *self, PyObject *args)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  PyObject *list, *tuple;
+  struct bp_location *loc;
+  int err;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  list = PyList_New (0);
+  if (!list)
+    return NULL;
+
+  err = 0;
+  for (loc = self_bp->bp->loc; loc; loc = loc->next)
+    {
+      PyObject *loc_obj =  bplocation_to_bplocation_object (loc);
+      err = PyList_Append (list, loc_obj);
+      if (err == -1)
+        {
+          Py_DECREF (list);
+          return NULL;
+        }
+      Py_DECREF (loc_obj);
+    }
+
+  tuple = PyList_AsTuple (list);
+  Py_DECREF (list);
+
+  return tuple;
+}
+
 /* Python function to create a new breakpoint.  */
 static int
 bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
@@ -1003,6 +1040,8 @@ static PyMethodDef breakpoint_object_methods[] =
     "Return true if this breakpoint is valid, false if not." },
   { "delete", bppy_delete_breakpoint, METH_NOARGS,
     "Delete the underlying GDB breakpoint." },
+  { "locations", bppy_locations, METH_NOARGS,
+    "Get a list of gdb.BpLocation objects associated with this breakpoint." },
   { NULL } /* Sentinel.  */
 };
 
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index ef39d5d..49bb32e 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -124,6 +124,9 @@ extern PyTypeObject stop_event_object_type;
 /* Defined in py-breakpoint.c */
 typedef struct breakpoint_object breakpoint_object;
 
+/* Defined in py-bploc.c */
+typedef struct bploc_object bploc_object;
+
 typedef struct
 {
   PyObject_HEAD
@@ -176,6 +179,8 @@ PyObject *pspy_get_printers (PyObject *, void *);
 PyObject *objfile_to_objfile_object (struct objfile *);
 PyObject *objfpy_get_printers (PyObject *, void *);
 
+PyObject *bplocation_to_bplocation_object (struct bp_location *loc);
+
 thread_object *create_thread_object (struct thread_info *tp);
 thread_object *find_thread_object (ptid_t ptid);
 PyObject *find_inferior_object (int pid);
@@ -202,6 +207,7 @@ void gdbpy_initialize_functions (void);
 void gdbpy_initialize_pspace (void);
 void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_breakpoints (void);
+void gdbpy_initialize_bplocation (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 b0b9a9c..de6b51f 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1058,6 +1058,12 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj)
 		    "scripting is not supported."));
 }
 
+void
+gdbpy_bplocation_free (struct breakpoint_object *bp_obj)
+{
+  return;
+}
+
 #endif /* HAVE_PYTHON */
 
 \f
@@ -1247,6 +1253,7 @@ Enables or disables printing of Python stack traces."),
   gdbpy_initialize_pspace ();
   gdbpy_initialize_objfile ();
   gdbpy_initialize_breakpoints ();
+  gdbpy_initialize_bplocation ();
   gdbpy_initialize_lazy_string ();
   gdbpy_initialize_thread ();
   gdbpy_initialize_inferior ();
diff --git a/gdb/python/python.h b/gdb/python/python.h
index ae55cc2..cee8a6b 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_bplocation_free (struct bp_location *loc);
+
 #endif /* GDB_PYTHON_H */
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index e0dd087..d946d7b 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -301,3 +301,55 @@ gdb_py_test_silent_cmd  "python wp1 = wp_eval (\"result\", type=gdb.BP_WATCHPOIN
 gdb_test "continue" ".*\[Ww\]atchpoint.*result.*Old value =.*New value = 788.*" "Test watchpoint write"
 gdb_test "python print never_eval_bp1.count" "0" \
     "Check that this unrelated breakpoints eval function was never called."
+
+# gdb.BpLocation
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+delete_breakpoints
+gdb_test_no_output "set detach-on-fork off" "don't detach on fork"
+gdb_test "call fork()" "New process .*" "create a second inferior"
+
+gdb_breakpoint "main"
+gdb_test "py print len(gdb.breakpoints())" "1" "ensure that threre is only one BP"
+gdb_test_no_output {py bp0 = gdb.breakpoints()[0]} "save breakpoint 0"
+gdb_test "py print len(bp0.locations())" "2" "ensure that threre are 2 locations"
+
+gdb_test_no_output {py loc0 = bp0.locations()[0]} "save location 0"
+gdb_test_no_output {py loc1 = bp0.locations()[1]} "save location 1"
+
+gdb_test "py print loc0.owner == loc1.owner == bp0" "True" "verify ownership"
+gdb_test "py print loc0.address == loc1.address " "True" "verify addresses are identical"
+# how to check address location ? != address(main)
+
+gdb_test {py print loc0.inferior == gdb.inferiors()[0]} "True" "verify inferior for loc 0" #inf 2
+gdb_test {py print loc1.inferior == gdb.inferiors()[1]} "True" "verify inferior for loc 1" #inf 1
+
+gdb_test "py print loc0.enabled == loc1.enabled == True" "True" "verify that locations are enabled"
+
+gdb_test "py print loc0.inferior.num" "2" "ensure that loc0 is on inferior 2"
+
+gdb_test "detach inferior 2" "Detaching from program:.*" "detach inferior 2"
+gdb_test "inferior 1" "Switching to inferior .*" "switch to inferior 1"
+gdb_test_no_output "remove-inferiors 2" "remove inferior 2"
+gdb_test "py print loc0.inferior" "None" "removed inferior set to None"
+
+delete_breakpoints
+gdb_test "py print bp0.is_valid()" "False" "verify that BP has been invalidated"
+gdb_test "py bp0.locations()" ".*RuntimeError: Breakpoint .* is invalid.*"\
+         "verify that locations can't accessed on an invalid breakpoint"
+         
+gdb_test "py print loc0.is_valid()" "False" "verify that location is invalid"
+gdb_test "py print loc0.owner" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that owner can't be accessed"
+gdb_test "py print loc0.enabled" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.address" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.inferior" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that inferior can't be accessed"
-- 
1.7.6.4


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

* Re: [PATCH] Add bp_location to Python interface
  2011-12-09 13:49   ` Kevin Pouget
@ 2011-12-09 14:15     ` Phil Muldoon
  2011-12-13 15:55       ` Kevin Pouget
  0 siblings, 1 reply; 25+ messages in thread
From: Phil Muldoon @ 2011-12-09 14:15 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches

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

> On Thu, Dec 8, 2011 at 3:28 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
>

>>> +A breakpoint location is represented with a @code{gdb.BpLocation} object,
>>> +which offers the following attributes (all read only) and methods.
>>> +Please note that breakpoint locations are very transient entities in
>>> +@value{GDBN}, so one should avoid keeping references to them.
>
>>
>> While it is good to note that, I'm not sure  what we are explaining here
>> other than when the breakpoint is deleted, all location objects that are
>> associated with that object are invalid too.  Or, are you noting we
>> should allow the user to interact after the fact? If that is the case,
>> what is "is_valid" for?  Why note the transient nature of the object?
>
>> I'm not sure what we are explaining here other than when the breakpoint is deleted
>
> that's what I wanted to emphasize here, bp_locations are cleaned/reset
> more often than 'high-level' breakpoints are removed. My knowledge
> about that is quite limited, but for instance this (cleaned up) stack:
>
> #0  gdbpy_bplocation_free py-bploc.c:60
> #1  free_bp_location (loc=) breakpoint.c:5685
> #2  decref_bp_location (blp=) breakpoint.c:5707
> #3  update_global_location_list (should_insert=1)  breakpoint.c:10575
> #4  update_breakpoint_locations (b=, sals=...,     sals_end=...)
> breakpoint.c:11787
> #5  breakpoint_re_set_default (b=) breakpoint.c:11937
> #6  breakpoint_re_set_one (bint=) breakpoint.c:11968
> #8  breakpoint_re_set () breakpoint.c:11992
> #9  solib_add (pattern=0x0, from_tty=0, target=,  readsyms=1) solib.c:926
> #10 bpstat_what (bs_head=) breakpoint.c:4487
> #11 handle_inferior_event (ecs=) infrun.c:4394
> #12 wait_for_inferior ()
>
> shows that bplocations might be removed when a shared library is loaded

Good point, I did not think of this scenario.  Looking at several
sections of GDB code, update_global_location_list can just be called
whenever.

>> If the breakpoint is deleted and the user still has reference to a
>> location object, I think we should just run a validation routine and
>> refuse to do anything but raise an exception at that point (like
>> is_valid, but triggered for all function/attribute calls).
>
> hum, I think that's what I already do in the code :)
>
> I've updated the gdb.BpLocation.is_valid documentation, maybe it's clearer?

Yeah, I saw it later.  Forgot to delete that comment!

>> @defun BpLocation.is_valid ()
>> Returns @code{True} if the @code{gdb.BpLocation} object is valid,
>> @code{False} if not.  A @code{gdb.BpLocation} object may be invalidated by
>> GDB at any moment for internal reasons. All other @code{gdb.BpLocation} methods
>> and attributes will throw an exception if the object is invalid.
>> @end defun
>
>>> +
>>> +@defvar BpLocation.owner
>>> +This attribute holds a reference to the @code{gdb.Breakpoint} object which
>>> +owns this location.
>>> +@end defvar
>
>> Note which attributes are read only/writable.  This and others.
> I specify a few lines above that all the attributes are read only, but
> I've noted it on each attribute as well.

Right, but there is no guarantee that the user will read the entirety
of that section's documentation.  So we always put attribute access rights
in the attribute section.

>> Also many different breakpoints can be at one location.  I'm not sure if it
>> is worth pointing out here that "this breakpoint" only owns the
>> location insofar as the scope of that breakpoint (there could be other
>> breakpoints set to that location).
>
> from an implementation point of view, the is a BP  <1--------n>
> BpLocation relation, even if two locations have the same address.
>
> Also, I think that the end-users won't have problems to understand
> these BpLocations, as they're already exposed with "info breakpoints":

I was purely talking from a scripting point of view, but ok.  My view
is, if the user has to run experiments to collect data on the actual
behavior, then our documentation needs to be a little better.  You could
just put in that "from an implementation point of view" paragraph in
the documentation somewhere?


>>> +struct bploc_object
>>> +{
>>> +  PyObject_HEAD
>>> +
>>> +  /* The location corresponding to this py object.  NULL is the location
>>> +     has been deleted.  */
>>> +  struct bp_location *loc;
>>
>> Typo in the comment "NULL is the location has been deleted.".  Also nit
>> pick shouldn't it be "The location corresponding to a gdb.Breakpoint
>> object"?
>
> typo fixed, but not the nit: it's the same idea as breakpoint,
> if the 'backend' breakpoint is deleted, 'struct breakpoint_object . bp' is NULL,
> if the 'backend' location is deleted, 'struct bploc_object . loc' is
> NULL

My objection was to "py object", I would just find it clearer to name
the object.  Also, the explanation above would go a long way for future
hackers to understand the relationship.  Maybe add that to the code too?

>
>>> +
>>> +  /* 1 if the owner BP has been deleted, 0 otherwise.  */
>>> +  int invalid_owner;
>>> +
>>> +  /* Cache for the gdb.Value object corresponding to loc->address.  */
>>> +  PyObject *py_address;
>>> +};
>>
>> I'm not sure if breakpoint locations can change.  I do not think so, but
>> why do we need to cache loc->address?
>
> I assumed that it can't change, but didn't actually investigate all
> the implementation.
> My feeling is "caching is easy, creating a Py object has a cost, so
> let's cache!", but I've got no idea about the actual cost, so I'll
> change it if someone insists [and convinces me] :)

I don't know enough about breakpoint locations to be able to advise you,
but what does update_global_location_list do?  Just shuffle add/delete
locations to the list?  I am not against caching.  Just not sure why we
need it.  I tend towards the conservative with things like these, if
only purely because we keep missing reference counts in the existing
code. 


>>> +
>>> +/* Require that LOCATION be a valid bp_location; throw a Python
>>> +   exception if it is invalid.  */
>>> +#define BPLOCPY_REQUIRE_VALID(Location)                                 \
>>> +    do {                                                                \
>>> +      if ((Location)->loc == NULL)                                      \
>>> +        return PyErr_Format (PyExc_RuntimeError,                        \
>>> +                             _("BpLocation invalid."));                 \
>>> +    } while (0)
>>
>> I prefer error messages a little more descriptive.  That is just a
>> personal thing of mine, but "BpLocation invalid" would seem cryptic when
>> thrown in the context of a script.
>
> I'm not sure how I could improve it, at this point (checking validity
> at the beginning of all the attributes), we just know that the backend
> location has been freed, but we don't know why.

"BpLocation object, breakpoint location is NULL". Or something like that.

>>> +PyObject *
>>> +bplocation_to_bplocation_object (struct bp_location *loc)
>>> +{
>>> +  bploc_object *bploc_obj;
>>> +
>>> +  gdb_assert (loc != NULL);
>>
>> Is this a fatal error that we need to shutdown GDB for?  gdb_assert
>> seems pretty strong from an API point of view.
>
> hum, I'm not sure here,
> I wrote this line to actually shutdown GDB instead of segfaulting on
> the next line,
> for instance 'py-pspace.c::pspace_to_pspace_object' and
> 'py-objfile.c::objfile_to_objfile_object" will just segfault if
> there're called with a NULL parameter.
>
> throwing an Python/GDB exception wouldn't make much sense to me
> either, as there end-user won't be able to deal with it

Neither would killing GDB.  I almost never use gdb_assert because of the
possible negative user implications.  Can you discern that GDB has
entered an unstable state because a function has received a NULL
argument for a bp_location?  If you cannot answer "yes" with a high
degree of certainty, it is my opinion we should raise an exception and
return NULL.  We can make sure in the Python API that, if this is the
case, we will return the exception to the script boundary for the script
to deal with.  From an API point-of-view I think we should defer to
script authors the opportunity to deal with and decide what to do in
this scenario.  If GDB is truly unstable, then I agree, assert is
correct.  I think the other two cases are bugs.

The Maintainers may differ on this, wait for their opinion.

>>> +bplocpy_get_address (PyObject *self, void *closure)
>>> +{
>>> +  bploc_object *self_bploc = (bploc_object *) self;
>>> +
>>> +  BPLOCPY_REQUIRE_VALID (self_bploc);
>>> +
>>> +  if (!self_bploc->py_address)
>>> +    {
>>> +      /* Get the address Value object as a void *.  */
>>> +      volatile struct gdb_exception except;
>>> +      struct type *void_ptr_type;
>>> +      struct value *val = NULL ; /* Initialize to appease gcc warning.  */
>>> +
>>> +      TRY_CATCH (except, RETURN_MASK_ALL)
>>> +        {
>>> +          void_ptr_type = lookup_pointer_type (
>>> +              builtin_type (python_gdbarch)->builtin_void);
>>> +          val = value_from_pointer (void_ptr_type, self_bploc->loc->address);
>>> +        }
>>> +      GDB_PY_HANDLE_EXCEPTION (except);
>>> +
>>> +      self_bploc->py_address = value_to_value_object (val);
>>> +      Py_XINCREF (self_bploc->py_address);
>>> +    }
>>> +  Py_XINCREF (self_bploc->py_address);
>>
>> I don't really mind it, but I prefer explicit return NULL when dealing
>> with cases of exceptions.  I find the other logic hard to read.  This is
>> not a request for a change. Is there a case where py_address will be
>> NULL?  Yes, there is, value_to_value_object can return NULL.  If it
>> returns NULL, then there is an exception set.  I much prefer to exit
>> then and there, other the conditional XINCREF step, and returning at the
>> function exit.  Still, this is just a stylistic thing, and probably
>> personal thing.  The second XINCREF can just be a plain old INCREF as we
>> already tested for NULL.
>
> makes sense to me, my "single return point" idea doesn't stand against
> error handling

Note this is purely a personal style issue, there was nothing wrong with
the correctness of your code.

>> This brings me to the address cache argument.  Is it worthwhile managing
>> the cache increment counts instead of just returning the address each
>> time?  I ask as I am not sure if you have done any metrics that indicate
>> this is a slow operation.
>
> no, I didn't run any measurement so far; I'll wait for a maintainer
> point of view about that

Ok.

>> We need to make sure that the this is not a watchpoint.
>
> why not?
> I don't know much about watchpoints, but as per my quick
> investigations, they share the same high-level structure (struct
> breakpoint), and they seem to use bp_location the same way ?

I'm not sure as watchpoints are based on expressions.  Maybe it is ok,
but note that watchpoints are removed and reinserted on scope of the
expression.  It might be fine.  A few tests would prove the issue.

Cheers,

Phil

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

* Re: [PATCH] Add bp_location to Python interface
  2011-12-09 14:15     ` Phil Muldoon
@ 2011-12-13 15:55       ` Kevin Pouget
  2012-01-09 11:47         ` Kevin Pouget
  0 siblings, 1 reply; 25+ messages in thread
From: Kevin Pouget @ 2011-12-13 15:55 UTC (permalink / raw)
  To: pmuldoon; +Cc: gdb-patches

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

On Fri, Dec 9, 2011 at 3:10 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
> Kevin Pouget <kevin.pouget@gmail.com> writes:
>
>> On Thu, Dec 8, 2011 at 3:28 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
>>
>
>>>> +A breakpoint location is represented with a @code{gdb.BpLocation} object,
>>>> +which offers the following attributes (all read only) and methods.
>>>> +Please note that breakpoint locations are very transient entities in
>>>> +@value{GDBN}, so one should avoid keeping references to them.
>>
>>>
>>> While it is good to note that, I'm not sure  what we are explaining here
>>> other than when the breakpoint is deleted, all location objects that are
>>> associated with that object are invalid too.  Or, are you noting we
>>> should allow the user to interact after the fact? If that is the case,
>>> what is "is_valid" for?  Why note the transient nature of the object?
>>
>>> I'm not sure what we are explaining here other than when the breakpoint is deleted
>>
>> that's what I wanted to emphasize here, bp_locations are cleaned/reset
>> more often than 'high-level' breakpoints are removed. My knowledge
>> about that is quite limited, but for instance this (cleaned up) stack:
>>
>> #0  gdbpy_bplocation_free py-bploc.c:60
>> #1  free_bp_location (loc=) breakpoint.c:5685
>> #2  decref_bp_location (blp=) breakpoint.c:5707
>> #3  update_global_location_list (should_insert=1)  breakpoint.c:10575
>> #4  update_breakpoint_locations (b=, sals=...,     sals_end=...)
>> breakpoint.c:11787
>> #5  breakpoint_re_set_default (b=) breakpoint.c:11937
>> #6  breakpoint_re_set_one (bint=) breakpoint.c:11968
>> #8  breakpoint_re_set () breakpoint.c:11992
>> #9  solib_add (pattern=0x0, from_tty=0, target=,  readsyms=1) solib.c:926
>> #10 bpstat_what (bs_head=) breakpoint.c:4487
>> #11 handle_inferior_event (ecs=) infrun.c:4394
>> #12 wait_for_inferior ()
>>
>> shows that bplocations might be removed when a shared library is loaded
>
> Good point, I did not think of this scenario.  Looking at several
> sections of GDB code, update_global_location_list can just be called
> whenever.
>
>>> If the breakpoint is deleted and the user still has reference to a
>>> location object, I think we should just run a validation routine and
>>> refuse to do anything but raise an exception at that point (like
>>> is_valid, but triggered for all function/attribute calls).
>>
>> hum, I think that's what I already do in the code :)
>>
>> I've updated the gdb.BpLocation.is_valid documentation, maybe it's clearer?
>
> Yeah, I saw it later.  Forgot to delete that comment!
>
>>> @defun BpLocation.is_valid ()
>>> Returns @code{True} if the @code{gdb.BpLocation} object is valid,
>>> @code{False} if not.  A @code{gdb.BpLocation} object may be invalidated by
>>> GDB at any moment for internal reasons. All other @code{gdb.BpLocation} methods
>>> and attributes will throw an exception if the object is invalid.
>>> @end defun
>>
>>>> +
>>>> +@defvar BpLocation.owner
>>>> +This attribute holds a reference to the @code{gdb.Breakpoint} object which
>>>> +owns this location.
>>>> +@end defvar
>>
>>> Note which attributes are read only/writable.  This and others.
>> I specify a few lines above that all the attributes are read only, but
>> I've noted it on each attribute as well.
>
> Right, but there is no guarantee that the user will read the entirety
> of that section's documentation.  So we always put attribute access rights
> in the attribute section.

ok, I thought that as soon as it was clearly mentioned somewhere, that
was enough. But it doesn't cost anything to me to write it for each
attribute, so no problem!

>>> Also many different breakpoints can be at one location.  I'm not sure if it
>>> is worth pointing out here that "this breakpoint" only owns the
>>> location insofar as the scope of that breakpoint (there could be other
>>> breakpoints set to that location).
>>
>> from an implementation point of view, the is a BP  <1--------n>
>> BpLocation relation, even if two locations have the same address.
>>
>> Also, I think that the end-users won't have problems to understand
>> these BpLocations, as they're already exposed with "info breakpoints":
>
> I was purely talking from a scripting point of view, but ok.  My view
> is, if the user has to run experiments to collect data on the actual
> behavior, then our documentation needs to be a little better.

yes, no doubt about that!

> You could just put in that "from an implementation point of view" paragraph in
> the documentation somewhere?

I've added it to 'BpLocation.owner' description:

@defvar BpLocation.owner
This attribute holds a reference to the @code{gdb.Breakpoint} object which
owns this location.  This attribute is not writable.  From an implementation
point of view, there is a @code{1 ... n} relation between a breakpoint and
its locations, even if two breakpoints are set on at same address.



>>>> +struct bploc_object
>>>> +{
>>>> +  PyObject_HEAD
>>>> +
>>>> +  /* The location corresponding to this py object.  NULL is the location
>>>> +     has been deleted.  */
>>>> +  struct bp_location *loc;
>>>
>>> Typo in the comment "NULL is the location has been deleted.".  Also nit
>>> pick shouldn't it be "The location corresponding to a gdb.Breakpoint
>>> object"?
>>
>> typo fixed, but not the nit: it's the same idea as breakpoint,
>> if the 'backend' breakpoint is deleted, 'struct breakpoint_object . bp' is NULL,
>> if the 'backend' location is deleted, 'struct bploc_object . loc' is
>> NULL
>
> My objection was to "py object", I would just find it clearer to name
> the object.  Also, the explanation above would go a long way for future
> hackers to understand the relationship.  Maybe add that to the code too?

You're right, it looks obvious to me, but it would take time to figure
out by itself.
Here are a few more bits of comments:

/* The location corresponding to this gdb.BpLocation object.  It's the same
     idea as gdb.Breakpoint, if the 'backend' location is deleted, LOC is
     set to NULL.  No reference to the location is owned here (in terms of
     ref. counting) in order not to change the internal behavior.  */
struct bp_location *loc;

>>
>>>> +
>>>> +  /* 1 if the owner BP has been deleted, 0 otherwise.  */
>>>> +  int invalid_owner;
>>>> +
>>>> +  /* Cache for the gdb.Value object corresponding to loc->address.  */
>>>> +  PyObject *py_address;
>>>> +};
>>>
>>> I'm not sure if breakpoint locations can change.  I do not think so, but
>>> why do we need to cache loc->address?
>>
>> I assumed that it can't change, but didn't actually investigate all
>> the implementation.
>> My feeling is "caching is easy, creating a Py object has a cost, so
>> let's cache!", but I've got no idea about the actual cost, so I'll
>> change it if someone insists [and convinces me] :)
>
> I don't know enough about breakpoint locations to be able to advise you,
> but what does update_global_location_list do?  Just shuffle add/delete
> locations to the list?  I am not against caching.  Just not sure why we
> need it.  I tend towards the conservative with things like these, if
> only purely because we keep missing reference counts in the existing
> code.

I somehow managed to convinced myself to remove this address caching,
1/I'm not sure about the actual py object creation, certainly quite low,
2/BpLocation objects have a short lifespan, so the cached addresses
would be discarded quite frequently
so all in all, I can't see anymore any relevant reason to cache it :)

>>>> +
>>>> +/* Require that LOCATION be a valid bp_location; throw a Python
>>>> +   exception if it is invalid.  */
>>>> +#define BPLOCPY_REQUIRE_VALID(Location)                                 \
>>>> +    do {                                                                \
>>>> +      if ((Location)->loc == NULL)                                      \
>>>> +        return PyErr_Format (PyExc_RuntimeError,                        \
>>>> +                             _("BpLocation invalid."));                 \
>>>> +    } while (0)
>>>
>>> I prefer error messages a little more descriptive.  That is just a
>>> personal thing of mine, but "BpLocation invalid" would seem cryptic when
>>> thrown in the context of a script.
>>
>> I'm not sure how I could improve it, at this point (checking validity
>> at the beginning of all the attributes), we just know that the backend
>> location has been freed, but we don't know why.
>
> "BpLocation object, breakpoint location is NULL". Or something like that.

I'm still not sure, I think that when I read "BpLocation invalid", I'd
take a look at the 'is_valid' method description, and understand
that "A @code{gdb.BpLocation} object may be invalidated by GDB at any
moment for internal reasons."
'breakpoint location is NULL' sounds like an internal error to me ...

Maybe "BpLocation invalid: internal location object freed up/removed by GDB." ?

>>>> +PyObject *
>>>> +bplocation_to_bplocation_object (struct bp_location *loc)
>>>> +{
>>>> +  bploc_object *bploc_obj;
>>>> +
>>>> +  gdb_assert (loc != NULL);
>>>
>>> Is this a fatal error that we need to shutdown GDB for?  gdb_assert
>>> seems pretty strong from an API point of view.
>>
>> hum, I'm not sure here,
>> I wrote this line to actually shutdown GDB instead of segfaulting on
>> the next line,
>> for instance 'py-pspace.c::pspace_to_pspace_object' and
>> 'py-objfile.c::objfile_to_objfile_object" will just segfault if
>> there're called with a NULL parameter.
>>
>> throwing an Python/GDB exception wouldn't make much sense to me
>> either, as there end-user won't be able to deal with it
>
> Neither would killing GDB.  I almost never use gdb_assert because of the
> possible negative user implications.  Can you discern that GDB has
> entered an unstable state because a function has received a NULL
> argument for a bp_location?  If you cannot answer "yes" with a high
> degree of certainty, it is my opinion we should raise an exception and
> return NULL.  We can make sure in the Python API that, if this is the
> case, we will return the exception to the script boundary for the script
> to deal with.  From an API point-of-view I think we should defer to
> script authors the opportunity to deal with and decide what to do in
> this scenario.  If GDB is truly unstable, then I agree, assert is
> correct.  I think the other two cases are bugs.

I get your point and quite agree with you, the only thing is that
we're talking about 'gdb_assert' and not 'assert' itself.

> Neither would killing GDB.
and here's my point, 'gdb_assert' does't kill GDB, but raises a
warning message asking the user if he wants to quit or continue.
My feeling about *assert is that it's a development/beta-test
features, which might/should be turned into noop for production.

> From an API point-of-view I think we should defer to script authors the opportunity to deal with
I don't think so, because (from my developer point of view) I see it
as very improbable that this situation would occur, or even impossible
if I were a good programmer!
I don't think that API should cover internal 'buggy' situations,
otherwise they would be endless!

> The Maintainers may differ on this, wait for their opinion.
yes, I'm curious about different point of views

>>>> +bplocpy_get_address (PyObject *self, void *closure)
>>>> +{
>>>> +  bploc_object *self_bploc = (bploc_object *) self;
>>>> +
>>>> +  BPLOCPY_REQUIRE_VALID (self_bploc);
>>>> +
>>>> +  if (!self_bploc->py_address)
>>>> +    {
>>>> +      /* Get the address Value object as a void *.  */
>>>> +      volatile struct gdb_exception except;
>>>> +      struct type *void_ptr_type;
>>>> +      struct value *val = NULL ; /* Initialize to appease gcc warning.  */
>>>> +
>>>> +      TRY_CATCH (except, RETURN_MASK_ALL)
>>>> +        {
>>>> +          void_ptr_type = lookup_pointer_type (
>>>> +              builtin_type (python_gdbarch)->builtin_void);
>>>> +          val = value_from_pointer (void_ptr_type, self_bploc->loc->address);
>>>> +        }
>>>> +      GDB_PY_HANDLE_EXCEPTION (except);
>>>> +
>>>> +      self_bploc->py_address = value_to_value_object (val);
>>>> +      Py_XINCREF (self_bploc->py_address);
>>>> +    }
>>>> +  Py_XINCREF (self_bploc->py_address);
>>>
>>> I don't really mind it, but I prefer explicit return NULL when dealing
>>> with cases of exceptions.  I find the other logic hard to read.  This is
>>> not a request for a change. Is there a case where py_address will be
>>> NULL?  Yes, there is, value_to_value_object can return NULL.  If it
>>> returns NULL, then there is an exception set.  I much prefer to exit
>>> then and there, other the conditional XINCREF step, and returning at the
>>> function exit.  Still, this is just a stylistic thing, and probably
>>> personal thing.  The second XINCREF can just be a plain old INCREF as we
>>> already tested for NULL.
>>
>> makes sense to me, my "single return point" idea doesn't stand against
>> error handling
>
> Note this is purely a personal style issue, there was nothing wrong with
> the correctness of your code.

sure, I try to make it clear when I don't agree with the suggestion;
this one was perfectly fine,
I think that's actually the way I wrote it at the first time :)

>>> This brings me to the address cache argument.  Is it worthwhile managing
>>> the cache increment counts instead of just returning the address each
>>> time?  I ask as I am not sure if you have done any metrics that indicate
>>> this is a slow operation.
>>
>> no, I didn't run any measurement so far; I'll wait for a maintainer
>> point of view about that
>
> Ok.
>
>>> We need to make sure that the this is not a watchpoint.
>>
>> why not?
>> I don't know much about watchpoints, but as per my quick
>> investigations, they share the same high-level structure (struct
>> breakpoint), and they seem to use bp_location the same way ?
>
> I'm not sure as watchpoints are based on expressions.  Maybe it is ok,
> but note that watchpoints are removed and reinserted on scope of the
> expression.  It might be fine.  A few tests would prove the issue.

I'll try to build some test to ensure that it's fine


thanks for your comments and reviews,


Kevin

--

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

	Add bp_location to Python interface
	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-bploc.o
	(SUBDIR_PYTHON_SRCS): Add python/py-bploc.c
	Add build rule for this file.
	* breakpoint.h (struct bploc_object): Forward declaration.
	(struct bp_location): Add py_bploc_obj.
	* breakpoint.c (free_bp_location): Call gdbpy_bplocation_free.
	* python/py-bploc.c: New file.
	* python/py-breakpoint.c (bppy_locations): New function.
	(breakpoint_object_methods): New method binding: locations().
	* python/python-internal.h (bploc_object): New typedef.
	(bplocation_to_bplocation_object): New prototype.
	(gdbpy_initialize_bplocation): Likewise.
	* python/python.c (gdbpy_bplocation_free): New empty stub.
	(_initialize_python): Call gdbpy_initialize_bplocation.
	* python/python.h (gdbpy_bplocation_free): New prototype.
	
doc/
	Add bp_location to Python interface
	* gdb.texinfo (Breakpoints In Python): Document
	gdb.Breakpoint.locations and gdb.BpLocation.


testsuite/
	Add bp_location to Python interface
	* gdb.python/py-breakpoint.exp: Test gdb.BpLocation.

[-- Attachment #2: 0001-Add-bp_location-to-Python-interface.patch --]
[-- Type: text/x-patch, Size: 22545 bytes --]

From e4f3e7af4686b196ce2d4cbe6d8c7ef00915d352 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Wed, 18 May 2011 10:02:23 -0400
Subject: [PATCH] Add bp_location to Python interface

---
 gdb/Makefile.in                            |    6 +
 gdb/NEWS                                   |    8 +
 gdb/breakpoint.c                           |    3 +
 gdb/breakpoint.h                           |    6 +
 gdb/doc/gdb.texinfo                        |   44 ++++
 gdb/python/py-bploc.c                      |  302 ++++++++++++++++++++++++++++
 gdb/python/py-breakpoint.c                 |   39 ++++
 gdb/python/python-internal.h               |    6 +
 gdb/python/python.c                        |    7 +
 gdb/python/python.h                        |    2 +
 gdb/testsuite/gdb.python/py-breakpoint.exp |   52 +++++
 11 files changed, 475 insertions(+), 0 deletions(-)
 create mode 100644 gdb/python/py-bploc.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b71db33..58b4910 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -280,6 +280,7 @@ SUBDIR_PYTHON_OBS = \
 	py-auto-load.o \
 	py-block.o \
 	py-bpevent.o \
+	py-bploc.o \
 	py-breakpoint.o \
 	py-cmd.o \
 	py-continueevent.o \
@@ -311,6 +312,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-auto-load.c \
 	python/py-block.c \
 	python/py-bpevent.c \
+	python/py-bploc.c \
 	python/py-breakpoint.c \
 	python/py-cmd.c \
 	python/py-continueevent.c \
@@ -2081,6 +2083,10 @@ py-bpevent.o: $(srcdir)/python/py-bpevent.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bpevent.c
 	$(POSTCOMPILE)
 
+py-bploc.o: $(srcdir)/python/py-bploc.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bploc.c
+	$(POSTCOMPILE)
+
 py-breakpoint.o: $(srcdir)/python/py-breakpoint.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-breakpoint.c
 	$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index 42782ce..4344fc9 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -1,6 +1,14 @@
 		What has changed in GDB?
 	     (Organized release by release)
 
+*** Changes since GDB 7.4
+
+* Python scripting
+
+  ** A new method "gdb.Breakpoint.locations" has been added, as well as
+     the class gdb.BpLocation to provide further details about breakpoint
+     locations.
+
 *** Changes since GDB 7.3.1
 
 * GDB now handles ambiguous linespecs more consistently; the existing
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 0686587..2f1357a 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5681,6 +5681,9 @@ static void
 free_bp_location (struct bp_location *loc)
 {
   loc->ops->dtor (loc);
+
+  gdbpy_bplocation_free (loc);
+
   xfree (loc);
 }
 
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index ddf1881..5a8a1af 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -28,6 +28,7 @@
 struct value;
 struct block;
 struct breakpoint_object;
+struct bploc_object;
 struct get_number_or_range_state;
 struct thread_info;
 struct bpstats;
@@ -405,6 +406,11 @@ struct bp_location
   /* Source file name of this address.  */
 
   char *source_file;
+
+  /* Python object associated with this location.  May be NULL if the location
+     is not yet exported to Python.  */
+
+  struct bploc_object *py_bploc_obj;
 };
 
 /* This structure is a collection of function pointers that, if available,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 50c299e..4665772 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24335,6 +24335,50 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@findex gdb.locations
+@defun gdb.locations ()
+Return a tuple containing a sequence of @code{gdb.BpLocation} objects 
+(see below) associated with this breakpoint.  A breakpoint with no location
+is a pending breakpoint (@xref{Set Breaks, , pending breakpoints}).
+@end defun
+
+A breakpoint location is represented with a @code{gdb.BpLocation} object,
+which offers the following attributes (all read only) and methods.
+Please note that breakpoint locations are very transient entities in
+@value{GDBN}, so one should avoid keeping references to them.
+
+@defvar BpLocation.owner
+This attribute holds a reference to the @code{gdb.Breakpoint} object which
+owns this location.  This attribute is not writable.  From an implementation 
+point of view, there is a @code{1 ... n} relation between a breakpoint and
+its locations, even if two breakpoints are set on at same address.
+ 
+@end defvar
+
+@defvar BpLocation.enabled
+This attribute indicates whether this location is currently enabled or not.
+This attribute is not writable.
+@end defvar
+
+@defvar BpLocation.inferior
+This attribute holds a reference to the @code{gdb.Inferior} inferior object
+in which this breakpoint location has been inserted.  The value will be 
+@code{None} if there is no inferior associated with this location.   This 
+attribute is not writable.
+@end defvar
+
+@defvar BpLocation.address
+This attribute holds a @code{gdb.Value} object corresponding to the address 
+at which the breakpoint has been inserted.   This attribute is not writable.
+@end defvar
+
+@defun BpLocation.is_valid ()
+Returns @code{True} if the @code{gdb.BpLocation} object is valid,
+@code{False} if not.  A @code{gdb.BpLocation} object may be invalidated by
+GDB at any moment for internal reasons.  All other @code{gdb.BpLocation}
+methods and attributes will throw an exception if the object is invalid.
+@end defun
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/python/py-bploc.c b/gdb/python/py-bploc.c
new file mode 100644
index 0000000..5ba3948
--- /dev/null
+++ b/gdb/python/py-bploc.c
@@ -0,0 +1,302 @@
+/* Python interface to breakpoint locations.
+
+   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 "inferior.h"
+#include "python-internal.h"
+#include "observer.h"
+#include "gdbarch.h"
+
+struct bploc_object
+{
+  PyObject_HEAD
+
+  /* The location corresponding to this gdb.BpLocation object.  It's the same
+     idea as gdb.Breakpoint, if the 'backend' location is deleted, LOC is
+     set to NULL.  No reference to the location is owned here (in terms of
+     ref. counting) in order not to change the internal behavior.  */
+  struct bp_location *loc;
+
+  /* 1 if the owner BP has been deleted, 0 otherwise.  */
+  int invalid_owner;
+};
+
+/* Require that LOCATION be a valid bp_location; throw a Python
+   exception if it is invalid.  */
+#define BPLOCPY_REQUIRE_VALID(Location)                                 \
+    do {                                                                \
+      if ((Location)->loc == NULL)                                      \
+        return PyErr_Format (PyExc_RuntimeError,                        \
+                             _("BpLocation invalid."));                 \
+    } while (0)
+
+static PyTypeObject bploc_object_type;
+
+/* Call by free_bp_location when loc is about to be freed.  */
+
+void
+gdbpy_bplocation_free (struct bp_location *loc)
+{
+  if (loc->py_bploc_obj)
+    {
+      loc->py_bploc_obj->loc = NULL;
+      Py_DECREF (loc->py_bploc_obj);
+    }
+}
+
+/* Dissociate the bp_location from the Python object.  */
+
+static void
+bplocpy_dealloc (PyObject *self)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc != NULL)
+    self_bploc->loc->py_bploc_obj = NULL;
+
+  self->ob_type->tp_free (self);
+}
+
+/* Create or acquire a ref to the bp_location object (gdb.BpLocation)
+   that encapsulates the struct bp_location from GDB.  */
+
+PyObject *
+bplocation_to_bplocation_object (struct bp_location *loc)
+{
+  bploc_object *bploc_obj;
+
+  gdb_assert (loc != NULL);
+  if (loc->py_bploc_obj)
+    {
+      Py_INCREF (loc->py_bploc_obj);
+      return (PyObject *) loc->py_bploc_obj;
+    }
+
+  bploc_obj = PyObject_New (bploc_object, &bploc_object_type);
+  if (!bploc_obj)
+    return NULL;
+
+  bploc_obj->loc = loc;
+  bploc_obj->invalid_owner = 0;
+  Py_INCREF (bploc_obj);
+  loc->py_bploc_obj = bploc_obj;
+
+  return (PyObject *) bploc_obj;
+}
+
+/* Python function to get the BP owning this location, if any.  */
+
+static PyObject *
+bplocpy_get_owner (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->invalid_owner)
+    Py_RETURN_NONE;
+
+  if (self_bploc->loc->owner
+      && self_bploc->loc->owner->py_bp_object)
+    {
+      Py_INCREF (self_bploc->loc->owner->py_bp_object);
+      return (PyObject *) self_bploc->loc->owner->py_bp_object;
+    }
+
+  Py_RETURN_NONE;
+}
+
+/* Python function to test whether or not this breakpoint location is
+   enabled.  */
+
+static PyObject *
+bplocpy_get_enabled (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->loc->enabled)
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Python function to get the address of this breakpoint location.  The
+   gdb.Value object will be cached if this is the first access.  Returns
+   NULL in case of failure, with a Python exception set.  */
+
+static PyObject *
+bplocpy_get_address (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+  volatile struct gdb_exception except;
+  struct type *void_ptr_type;
+  struct value *val = NULL; /* Initialize to appease gcc warning.  */
+  PyObject *py_address;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  /* Get the address Value object as a void * value.  */
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      void_ptr_type = lookup_pointer_type (
+          builtin_type (python_gdbarch)->builtin_void);
+      val = value_from_pointer (void_ptr_type, self_bploc->loc->address);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  py_address = value_to_value_object (val);
+  if (!py_address)
+    return NULL;
+
+  return py_address;
+}
+
+/* Python function to get the inferior hosting this breakpoint location.
+   Return Py_None if there is no inferior associated with the program space of
+   this location, or NULL in case of failure, with a python exception set.  */
+
+static PyObject *
+bplocpy_get_inferior (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+  struct inferior *inf;
+  PyObject *infobj;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  inf = find_inferior_for_program_space (self_bploc->loc->pspace);
+  if (!inf)
+    Py_RETURN_NONE;
+
+  infobj = inferior_to_inferior_object (inf);
+  Py_XINCREF (infobj);
+
+  return infobj;
+}
+
+/* Python function which checks the validity of a bp location object.  */
+
+static PyObject *
+bplocpy_is_valid (PyObject *self, PyObject *args)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+/* Callback triggered when a breakpoint is deleted.  This will invalidate
+   the corresponding bp_location Python object owners.  */
+
+static void
+bplocpy_breakpoint_deleted (struct breakpoint *b) {
+  struct bp_location *loc;
+
+  for (loc = b->loc; loc; loc = loc->next)
+    {
+      if (loc->py_bploc_obj)
+        loc->py_bploc_obj->invalid_owner = 1;
+    }
+}
+
+/* Initialize the Python bp_location code.  */
+
+void
+gdbpy_initialize_bplocation (void)
+{
+  if (PyType_Ready (&bploc_object_type) < 0)
+    return;
+
+  Py_INCREF (&bploc_object_type);
+  if (PyModule_AddObject (gdb_module, "BpLocation",
+                          (PyObject *) &bploc_object_type) < 0)
+    return;
+
+  observer_attach_breakpoint_deleted (bplocpy_breakpoint_deleted);
+}
+
+static PyGetSetDef bploc_object_getset[] =
+{
+  { "owner", bplocpy_get_owner, NULL,
+    "Each breakpoint location must belong to exactly one higher-level \
+breakpoint.  This pointer is NULL iff this bp_location is no \
+longer attached to a breakpoint (read-only).",
+    NULL },
+  { "enabled", bplocpy_get_enabled, NULL,
+    "Is this particular location enabled.", NULL },
+  { "address", bplocpy_get_address, NULL,
+    "The address at which the breakpoint has been set.", NULL },
+  { "inferior", bplocpy_get_inferior, NULL,
+    "The inferior in which this breakpoint location has been set.", NULL },
+  { NULL }  /* Sentinel.  */
+};
+
+
+static PyMethodDef bploc_object_methods[] =
+{
+  { "is_valid", bplocpy_is_valid, METH_NOARGS,
+    "Return true if this breakpoint location is valid, false if not." },
+  { NULL } /* Sentinel.  */
+};
+
+static PyTypeObject bploc_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,                                          /* ob_size */
+  "gdb.BpLocation",                           /* tp_name */
+  sizeof (bploc_object),                      /* tp_basicsize */
+  0,                                          /* tp_itemsize */
+  bplocpy_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 breakpoint location object",           /* tp_doc */
+  0,                                          /* tp_traverse */
+  0,                                          /* tp_clear */
+  0,                                          /* tp_richcompare */
+  0,                                          /* tp_weaklistoffset */
+  0,                                          /* tp_iter */
+  0,                                          /* tp_iternext */
+  bploc_object_methods,                       /* tp_methods */
+  0,                                          /* tp_members */
+  bploc_object_getset,                        /* tp_getset */
+  0,                                          /* tp_base */
+  0,                                          /* tp_dict */
+  0,                                          /* tp_descr_get */
+  0,                                          /* tp_descr_set */
+  0,                                          /* tp_dictoffset */
+  0,                                          /* tp_init */
+  0                                           /* tp_alloc */
+};
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 11d60fe..cccbf0e 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -620,6 +620,43 @@ bppy_get_ignore_count (PyObject *self, void *closure)
   return PyInt_FromLong (self_bp->bp->ignore_count);
 }
 
+
+/* Python function which returns the BpLocation objects associated
+   with this breakpoint.  */
+
+static PyObject *
+bppy_locations (PyObject *self, PyObject *args)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  PyObject *list, *tuple;
+  struct bp_location *loc;
+  int err;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  list = PyList_New (0);
+  if (!list)
+    return NULL;
+
+  err = 0;
+  for (loc = self_bp->bp->loc; loc; loc = loc->next)
+    {
+      PyObject *loc_obj =  bplocation_to_bplocation_object (loc);
+      err = PyList_Append (list, loc_obj);
+      if (err == -1)
+        {
+          Py_DECREF (list);
+          return NULL;
+        }
+      Py_DECREF (loc_obj);
+    }
+
+  tuple = PyList_AsTuple (list);
+  Py_DECREF (list);
+
+  return tuple;
+}
+
 /* Python function to create a new breakpoint.  */
 static int
 bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
@@ -1003,6 +1040,8 @@ static PyMethodDef breakpoint_object_methods[] =
     "Return true if this breakpoint is valid, false if not." },
   { "delete", bppy_delete_breakpoint, METH_NOARGS,
     "Delete the underlying GDB breakpoint." },
+  { "locations", bppy_locations, METH_NOARGS,
+    "Get a list of gdb.BpLocation objects associated with this breakpoint." },
   { NULL } /* Sentinel.  */
 };
 
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index ef39d5d..49bb32e 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -124,6 +124,9 @@ extern PyTypeObject stop_event_object_type;
 /* Defined in py-breakpoint.c */
 typedef struct breakpoint_object breakpoint_object;
 
+/* Defined in py-bploc.c */
+typedef struct bploc_object bploc_object;
+
 typedef struct
 {
   PyObject_HEAD
@@ -176,6 +179,8 @@ PyObject *pspy_get_printers (PyObject *, void *);
 PyObject *objfile_to_objfile_object (struct objfile *);
 PyObject *objfpy_get_printers (PyObject *, void *);
 
+PyObject *bplocation_to_bplocation_object (struct bp_location *loc);
+
 thread_object *create_thread_object (struct thread_info *tp);
 thread_object *find_thread_object (ptid_t ptid);
 PyObject *find_inferior_object (int pid);
@@ -202,6 +207,7 @@ void gdbpy_initialize_functions (void);
 void gdbpy_initialize_pspace (void);
 void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_breakpoints (void);
+void gdbpy_initialize_bplocation (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 b0b9a9c..de6b51f 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1058,6 +1058,12 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj)
 		    "scripting is not supported."));
 }
 
+void
+gdbpy_bplocation_free (struct breakpoint_object *bp_obj)
+{
+  return;
+}
+
 #endif /* HAVE_PYTHON */
 
 \f
@@ -1247,6 +1253,7 @@ Enables or disables printing of Python stack traces."),
   gdbpy_initialize_pspace ();
   gdbpy_initialize_objfile ();
   gdbpy_initialize_breakpoints ();
+  gdbpy_initialize_bplocation ();
   gdbpy_initialize_lazy_string ();
   gdbpy_initialize_thread ();
   gdbpy_initialize_inferior ();
diff --git a/gdb/python/python.h b/gdb/python/python.h
index ae55cc2..cee8a6b 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_bplocation_free (struct bp_location *loc);
+
 #endif /* GDB_PYTHON_H */
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index e0dd087..d946d7b 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -301,3 +301,55 @@ gdb_py_test_silent_cmd  "python wp1 = wp_eval (\"result\", type=gdb.BP_WATCHPOIN
 gdb_test "continue" ".*\[Ww\]atchpoint.*result.*Old value =.*New value = 788.*" "Test watchpoint write"
 gdb_test "python print never_eval_bp1.count" "0" \
     "Check that this unrelated breakpoints eval function was never called."
+
+# gdb.BpLocation
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+delete_breakpoints
+gdb_test_no_output "set detach-on-fork off" "don't detach on fork"
+gdb_test "call fork()" "New process .*" "create a second inferior"
+
+gdb_breakpoint "main"
+gdb_test "py print len(gdb.breakpoints())" "1" "ensure that threre is only one BP"
+gdb_test_no_output {py bp0 = gdb.breakpoints()[0]} "save breakpoint 0"
+gdb_test "py print len(bp0.locations())" "2" "ensure that threre are 2 locations"
+
+gdb_test_no_output {py loc0 = bp0.locations()[0]} "save location 0"
+gdb_test_no_output {py loc1 = bp0.locations()[1]} "save location 1"
+
+gdb_test "py print loc0.owner == loc1.owner == bp0" "True" "verify ownership"
+gdb_test "py print loc0.address == loc1.address " "True" "verify addresses are identical"
+# how to check address location ? != address(main)
+
+gdb_test {py print loc0.inferior == gdb.inferiors()[0]} "True" "verify inferior for loc 0" #inf 2
+gdb_test {py print loc1.inferior == gdb.inferiors()[1]} "True" "verify inferior for loc 1" #inf 1
+
+gdb_test "py print loc0.enabled == loc1.enabled == True" "True" "verify that locations are enabled"
+
+gdb_test "py print loc0.inferior.num" "2" "ensure that loc0 is on inferior 2"
+
+gdb_test "detach inferior 2" "Detaching from program:.*" "detach inferior 2"
+gdb_test "inferior 1" "Switching to inferior .*" "switch to inferior 1"
+gdb_test_no_output "remove-inferiors 2" "remove inferior 2"
+gdb_test "py print loc0.inferior" "None" "removed inferior set to None"
+
+delete_breakpoints
+gdb_test "py print bp0.is_valid()" "False" "verify that BP has been invalidated"
+gdb_test "py bp0.locations()" ".*RuntimeError: Breakpoint .* is invalid.*"\
+         "verify that locations can't accessed on an invalid breakpoint"
+         
+gdb_test "py print loc0.is_valid()" "False" "verify that location is invalid"
+gdb_test "py print loc0.owner" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that owner can't be accessed"
+gdb_test "py print loc0.enabled" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.address" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.inferior" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that inferior can't be accessed"
-- 
1.7.6.4


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

* Re: [PATCH] Add bp_location to Python interface
  2011-12-13 15:55       ` Kevin Pouget
@ 2012-01-09 11:47         ` Kevin Pouget
  2012-01-09 17:23           ` Eli Zaretskii
  0 siblings, 1 reply; 25+ messages in thread
From: Kevin Pouget @ 2012-01-09 11:47 UTC (permalink / raw)
  To: gdb-patches; +Cc: pmuldoon

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

On Tue, Dec 13, 2011 at 4:28 PM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
>
> On Fri, Dec 9, 2011 at 3:10 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
> > Kevin Pouget <kevin.pouget@gmail.com> writes:
> >
> >> On Thu, Dec 8, 2011 at 3:28 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
> >>
> >
> >>>> +A breakpoint location is represented with a @code{gdb.BpLocation} object,
> >>>> +which offers the following attributes (all read only) and methods.
> >>>> +Please note that breakpoint locations are very transient entities in
> >>>> +@value{GDBN}, so one should avoid keeping references to them.
> >>
> >>>
> >>> While it is good to note that, I'm not sure  what we are explaining here
> >>> other than when the breakpoint is deleted, all location objects that are
> >>> associated with that object are invalid too.  Or, are you noting we
> >>> should allow the user to interact after the fact? If that is the case,
> >>> what is "is_valid" for?  Why note the transient nature of the object?
> >>
> >>> I'm not sure what we are explaining here other than when the breakpoint is deleted
> >>
> >> that's what I wanted to emphasize here, bp_locations are cleaned/reset
> >> more often than 'high-level' breakpoints are removed. My knowledge
> >> about that is quite limited, but for instance this (cleaned up) stack:
> >>
> >> #0  gdbpy_bplocation_free py-bploc.c:60
> >> #1  free_bp_location (loc=) breakpoint.c:5685
> >> #2  decref_bp_location (blp=) breakpoint.c:5707
> >> #3  update_global_location_list (should_insert=1)  breakpoint.c:10575
> >> #4  update_breakpoint_locations (b=, sals=...,     sals_end=...)
> >> breakpoint.c:11787
> >> #5  breakpoint_re_set_default (b=) breakpoint.c:11937
> >> #6  breakpoint_re_set_one (bint=) breakpoint.c:11968
> >> #8  breakpoint_re_set () breakpoint.c:11992
> >> #9  solib_add (pattern=0x0, from_tty=0, target=,  readsyms=1) solib.c:926
> >> #10 bpstat_what (bs_head=) breakpoint.c:4487
> >> #11 handle_inferior_event (ecs=) infrun.c:4394
> >> #12 wait_for_inferior ()
> >>
> >> shows that bplocations might be removed when a shared library is loaded
> >
> > Good point, I did not think of this scenario.  Looking at several
> > sections of GDB code, update_global_location_list can just be called
> > whenever.
> >
> >>> If the breakpoint is deleted and the user still has reference to a
> >>> location object, I think we should just run a validation routine and
> >>> refuse to do anything but raise an exception at that point (like
> >>> is_valid, but triggered for all function/attribute calls).
> >>
> >> hum, I think that's what I already do in the code :)
> >>
> >> I've updated the gdb.BpLocation.is_valid documentation, maybe it's clearer?
> >
> > Yeah, I saw it later.  Forgot to delete that comment!
> >
> >>> @defun BpLocation.is_valid ()
> >>> Returns @code{True} if the @code{gdb.BpLocation} object is valid,
> >>> @code{False} if not.  A @code{gdb.BpLocation} object may be invalidated by
> >>> GDB at any moment for internal reasons. All other @code{gdb.BpLocation} methods
> >>> and attributes will throw an exception if the object is invalid.
> >>> @end defun
> >>
> >>>> +
> >>>> +@defvar BpLocation.owner
> >>>> +This attribute holds a reference to the @code{gdb.Breakpoint} object which
> >>>> +owns this location.
> >>>> +@end defvar
> >>
> >>> Note which attributes are read only/writable.  This and others.
> >> I specify a few lines above that all the attributes are read only, but
> >> I've noted it on each attribute as well.
> >
> > Right, but there is no guarantee that the user will read the entirety
> > of that section's documentation.  So we always put attribute access rights
> > in the attribute section.
>
> ok, I thought that as soon as it was clearly mentioned somewhere, that
> was enough. But it doesn't cost anything to me to write it for each
> attribute, so no problem!
>
> >>> Also many different breakpoints can be at one location.  I'm not sure if it
> >>> is worth pointing out here that "this breakpoint" only owns the
> >>> location insofar as the scope of that breakpoint (there could be other
> >>> breakpoints set to that location).
> >>
> >> from an implementation point of view, the is a BP  <1--------n>
> >> BpLocation relation, even if two locations have the same address.
> >>
> >> Also, I think that the end-users won't have problems to understand
> >> these BpLocations, as they're already exposed with "info breakpoints":
> >
> > I was purely talking from a scripting point of view, but ok.  My view
> > is, if the user has to run experiments to collect data on the actual
> > behavior, then our documentation needs to be a little better.
>
> yes, no doubt about that!
>
> > You could just put in that "from an implementation point of view" paragraph in
> > the documentation somewhere?
>
> I've added it to 'BpLocation.owner' description:
>
> @defvar BpLocation.owner
> This attribute holds a reference to the @code{gdb.Breakpoint} object which
> owns this location.  This attribute is not writable.  From an implementation
> point of view, there is a @code{1 ... n} relation between a breakpoint and
> its locations, even if two breakpoints are set on at same address.
>
>
>
> >>>> +struct bploc_object
> >>>> +{
> >>>> +  PyObject_HEAD
> >>>> +
> >>>> +  /* The location corresponding to this py object.  NULL is the location
> >>>> +     has been deleted.  */
> >>>> +  struct bp_location *loc;
> >>>
> >>> Typo in the comment "NULL is the location has been deleted.".  Also nit
> >>> pick shouldn't it be "The location corresponding to a gdb.Breakpoint
> >>> object"?
> >>
> >> typo fixed, but not the nit: it's the same idea as breakpoint,
> >> if the 'backend' breakpoint is deleted, 'struct breakpoint_object . bp' is NULL,
> >> if the 'backend' location is deleted, 'struct bploc_object . loc' is
> >> NULL
> >
> > My objection was to "py object", I would just find it clearer to name
> > the object.  Also, the explanation above would go a long way for future
> > hackers to understand the relationship.  Maybe add that to the code too?
>
> You're right, it looks obvious to me, but it would take time to figure
> out by itself.
> Here are a few more bits of comments:
>
> /* The location corresponding to this gdb.BpLocation object.  It's the same
>     idea as gdb.Breakpoint, if the 'backend' location is deleted, LOC is
>     set to NULL.  No reference to the location is owned here (in terms of
>     ref. counting) in order not to change the internal behavior.  */
> struct bp_location *loc;
>
> >>
> >>>> +
> >>>> +  /* 1 if the owner BP has been deleted, 0 otherwise.  */
> >>>> +  int invalid_owner;
> >>>> +
> >>>> +  /* Cache for the gdb.Value object corresponding to loc->address.  */
> >>>> +  PyObject *py_address;
> >>>> +};
> >>>
> >>> I'm not sure if breakpoint locations can change.  I do not think so, but
> >>> why do we need to cache loc->address?
> >>
> >> I assumed that it can't change, but didn't actually investigate all
> >> the implementation.
> >> My feeling is "caching is easy, creating a Py object has a cost, so
> >> let's cache!", but I've got no idea about the actual cost, so I'll
> >> change it if someone insists [and convinces me] :)
> >
> > I don't know enough about breakpoint locations to be able to advise you,
> > but what does update_global_location_list do?  Just shuffle add/delete
> > locations to the list?  I am not against caching.  Just not sure why we
> > need it.  I tend towards the conservative with things like these, if
> > only purely because we keep missing reference counts in the existing
> > code.
>
> I somehow managed to convinced myself to remove this address caching,
> 1/I'm not sure about the actual py object creation, certainly quite low,
> 2/BpLocation objects have a short lifespan, so the cached addresses
> would be discarded quite frequently
> so all in all, I can't see anymore any relevant reason to cache it :)
>
> >>>> +
> >>>> +/* Require that LOCATION be a valid bp_location; throw a Python
> >>>> +   exception if it is invalid.  */
> >>>> +#define BPLOCPY_REQUIRE_VALID(Location)                                 \
> >>>> +    do {                                                                \
> >>>> +      if ((Location)->loc == NULL)                                      \
> >>>> +        return PyErr_Format (PyExc_RuntimeError,                        \
> >>>> +                             _("BpLocation invalid."));                 \
> >>>> +    } while (0)
> >>>
> >>> I prefer error messages a little more descriptive.  That is just a
> >>> personal thing of mine, but "BpLocation invalid" would seem cryptic when
> >>> thrown in the context of a script.
> >>
> >> I'm not sure how I could improve it, at this point (checking validity
> >> at the beginning of all the attributes), we just know that the backend
> >> location has been freed, but we don't know why.
> >
> > "BpLocation object, breakpoint location is NULL". Or something like that.
>
> I'm still not sure, I think that when I read "BpLocation invalid", I'd
> take a look at the 'is_valid' method description, and understand
> that "A @code{gdb.BpLocation} object may be invalidated by GDB at any
> moment for internal reasons."
> 'breakpoint location is NULL' sounds like an internal error to me ...
>
> Maybe "BpLocation invalid: internal location object freed up/removed by GDB." ?
>
> >>>> +PyObject *
> >>>> +bplocation_to_bplocation_object (struct bp_location *loc)
> >>>> +{
> >>>> +  bploc_object *bploc_obj;
> >>>> +
> >>>> +  gdb_assert (loc != NULL);
> >>>
> >>> Is this a fatal error that we need to shutdown GDB for?  gdb_assert
> >>> seems pretty strong from an API point of view.
> >>
> >> hum, I'm not sure here,
> >> I wrote this line to actually shutdown GDB instead of segfaulting on
> >> the next line,
> >> for instance 'py-pspace.c::pspace_to_pspace_object' and
> >> 'py-objfile.c::objfile_to_objfile_object" will just segfault if
> >> there're called with a NULL parameter.
> >>
> >> throwing an Python/GDB exception wouldn't make much sense to me
> >> either, as there end-user won't be able to deal with it
> >
> > Neither would killing GDB.  I almost never use gdb_assert because of the
> > possible negative user implications.  Can you discern that GDB has
> > entered an unstable state because a function has received a NULL
> > argument for a bp_location?  If you cannot answer "yes" with a high
> > degree of certainty, it is my opinion we should raise an exception and
> > return NULL.  We can make sure in the Python API that, if this is the
> > case, we will return the exception to the script boundary for the script
> > to deal with.  From an API point-of-view I think we should defer to
> > script authors the opportunity to deal with and decide what to do in
> > this scenario.  If GDB is truly unstable, then I agree, assert is
> > correct.  I think the other two cases are bugs.
>
> I get your point and quite agree with you, the only thing is that
> we're talking about 'gdb_assert' and not 'assert' itself.
>
> > Neither would killing GDB.
> and here's my point, 'gdb_assert' does't kill GDB, but raises a
> warning message asking the user if he wants to quit or continue.
> My feeling about *assert is that it's a development/beta-test
> features, which might/should be turned into noop for production.
>
> > From an API point-of-view I think we should defer to script authors the opportunity to deal with
> I don't think so, because (from my developer point of view) I see it
> as very improbable that this situation would occur, or even impossible
> if I were a good programmer!
> I don't think that API should cover internal 'buggy' situations,
> otherwise they would be endless!
>
> > The Maintainers may differ on this, wait for their opinion.
> yes, I'm curious about different point of views
>
> >>>> +bplocpy_get_address (PyObject *self, void *closure)
> >>>> +{
> >>>> +  bploc_object *self_bploc = (bploc_object *) self;
> >>>> +
> >>>> +  BPLOCPY_REQUIRE_VALID (self_bploc);
> >>>> +
> >>>> +  if (!self_bploc->py_address)
> >>>> +    {
> >>>> +      /* Get the address Value object as a void *.  */
> >>>> +      volatile struct gdb_exception except;
> >>>> +      struct type *void_ptr_type;
> >>>> +      struct value *val = NULL ; /* Initialize to appease gcc warning.  */
> >>>> +
> >>>> +      TRY_CATCH (except, RETURN_MASK_ALL)
> >>>> +        {
> >>>> +          void_ptr_type = lookup_pointer_type (
> >>>> +              builtin_type (python_gdbarch)->builtin_void);
> >>>> +          val = value_from_pointer (void_ptr_type, self_bploc->loc->address);
> >>>> +        }
> >>>> +      GDB_PY_HANDLE_EXCEPTION (except);
> >>>> +
> >>>> +      self_bploc->py_address = value_to_value_object (val);
> >>>> +      Py_XINCREF (self_bploc->py_address);
> >>>> +    }
> >>>> +  Py_XINCREF (self_bploc->py_address);
> >>>
> >>> I don't really mind it, but I prefer explicit return NULL when dealing
> >>> with cases of exceptions.  I find the other logic hard to read.  This is
> >>> not a request for a change. Is there a case where py_address will be
> >>> NULL?  Yes, there is, value_to_value_object can return NULL.  If it
> >>> returns NULL, then there is an exception set.  I much prefer to exit
> >>> then and there, other the conditional XINCREF step, and returning at the
> >>> function exit.  Still, this is just a stylistic thing, and probably
> >>> personal thing.  The second XINCREF can just be a plain old INCREF as we
> >>> already tested for NULL.
> >>
> >> makes sense to me, my "single return point" idea doesn't stand against
> >> error handling
> >
> > Note this is purely a personal style issue, there was nothing wrong with
> > the correctness of your code.
>
> sure, I try to make it clear when I don't agree with the suggestion;
> this one was perfectly fine,
> I think that's actually the way I wrote it at the first time :)
>
> >>> This brings me to the address cache argument.  Is it worthwhile managing
> >>> the cache increment counts instead of just returning the address each
> >>> time?  I ask as I am not sure if you have done any metrics that indicate
> >>> this is a slow operation.
> >>
> >> no, I didn't run any measurement so far; I'll wait for a maintainer
> >> point of view about that
> >
> > Ok.
> >
> >>> We need to make sure that the this is not a watchpoint.
> >>
> >> why not?
> >> I don't know much about watchpoints, but as per my quick
> >> investigations, they share the same high-level structure (struct
> >> breakpoint), and they seem to use bp_location the same way ?
> >
> > I'm not sure as watchpoints are based on expressions.  Maybe it is ok,
> > but note that watchpoints are removed and reinserted on scope of the
> > expression.  It might be fine.  A few tests would prove the issue.
>
> I'll try to build some test to ensure that it's fine
>
>
> thanks for your comments and reviews,

ping

patch refreshed against current HEAD

Thanks,

Kevin

--

2012-01-09  Kevin Pouget  <kevin.pouget@st.com>

	Add bp_location to Python interface
	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-bploc.o
	(SUBDIR_PYTHON_SRCS): Add python/py-bploc.c
	Add build rule for this file.
	* breakpoint.h (struct bploc_object): Forward declaration.
	(struct bp_location): Add py_bploc_obj.
	* breakpoint.c (free_bp_location): Call gdbpy_bplocation_free.
	* python/py-bploc.c: New file.
	* python/py-breakpoint.c (bppy_locations): New function.
	(breakpoint_object_methods): New method binding: locations().
	* python/python-internal.h (bploc_object): New typedef.
	(bplocation_to_bplocation_object): New prototype.
	(gdbpy_initialize_bplocation): Likewise.
	* python/python.c (gdbpy_bplocation_free): New empty stub.
	(_initialize_python): Call gdbpy_initialize_bplocation.
	* python/python.h (gdbpy_bplocation_free): New prototype.
	
doc/
	Add bp_location to Python interface
	* gdb.texinfo (Breakpoints In Python): Document
	gdb.Breakpoint.locations and gdb.BpLocation.


testsuite/
	Add bp_location to Python interface
	* gdb.python/py-breakpoint.exp: Test gdb.BpLocation.

[-- Attachment #2: 0001-Add-bp_location-to-Python-interface.patch --]
[-- Type: text/x-patch, Size: 22496 bytes --]

From e61ed33ace88dd336eea6f5e3b7f750243087822 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Wed, 18 May 2011 10:02:23 -0400
Subject: [PATCH] Add bp_location to Python interface

---
 gdb/Makefile.in                            |    6 +
 gdb/NEWS                                   |    6 +
 gdb/breakpoint.c                           |    3 +
 gdb/breakpoint.h                           |    6 +
 gdb/doc/gdb.texinfo                        |   44 ++++
 gdb/python/py-bploc.c                      |  302 ++++++++++++++++++++++++++++
 gdb/python/py-breakpoint.c                 |   39 ++++
 gdb/python/python-internal.h               |    6 +
 gdb/python/python.c                        |    7 +
 gdb/python/python.h                        |    2 +
 gdb/testsuite/gdb.python/py-breakpoint.exp |   52 +++++
 11 files changed, 473 insertions(+), 0 deletions(-)
 create mode 100644 gdb/python/py-bploc.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index cd93768..e4cdd5a 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -273,6 +273,7 @@ SUBDIR_PYTHON_OBS = \
 	py-auto-load.o \
 	py-block.o \
 	py-bpevent.o \
+	py-bploc.o \
 	py-breakpoint.o \
 	py-cmd.o \
 	py-continueevent.o \
@@ -305,6 +306,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-auto-load.c \
 	python/py-block.c \
 	python/py-bpevent.c \
+	python/py-bploc.c \
 	python/py-breakpoint.c \
 	python/py-cmd.c \
 	python/py-continueevent.c \
@@ -2033,6 +2035,10 @@ py-bpevent.o: $(srcdir)/python/py-bpevent.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bpevent.c
 	$(POSTCOMPILE)
 
+py-bploc.o: $(srcdir)/python/py-bploc.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bploc.c
+	$(POSTCOMPILE)
+
 py-breakpoint.o: $(srcdir)/python/py-breakpoint.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-breakpoint.c
 	$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index a9a7859..c529f96 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9,6 +9,12 @@
 * The binary "gdbtui" can no longer be built or installed.
   Use "gdb -tui" instead.
 
+* Python scripting
+
+  ** A new method "gdb.Breakpoint.locations" has been added, as well as
+     the class gdb.BpLocation to provide further details about breakpoint
+     locations.
+
 *** Changes in GDB 7.4
 
 * GDB now handles ambiguous linespecs more consistently; the existing
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 6bcedc4..623f2df 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5679,6 +5679,9 @@ static void
 free_bp_location (struct bp_location *loc)
 {
   loc->ops->dtor (loc);
+
+  gdbpy_bplocation_free (loc);
+
   xfree (loc);
 }
 
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index c1d3be9..8d6fa19 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -26,6 +26,7 @@
 struct value;
 struct block;
 struct breakpoint_object;
+struct bploc_object;
 struct get_number_or_range_state;
 struct thread_info;
 struct bpstats;
@@ -403,6 +404,11 @@ struct bp_location
   /* Source file name of this address.  */
 
   char *source_file;
+
+  /* Python object associated with this location.  May be NULL if the location
+     is not yet exported to Python.  */
+
+  struct bploc_object *py_bploc_obj;
 };
 
 /* This structure is a collection of function pointers that, if available,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2f4aa4f..f8375d6 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24358,6 +24358,50 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@findex gdb.locations
+@defun gdb.locations ()
+Return a tuple containing a sequence of @code{gdb.BpLocation} objects 
+(see below) associated with this breakpoint.  A breakpoint with no location
+is a pending breakpoint (@xref{Set Breaks, , pending breakpoints}).
+@end defun
+
+A breakpoint location is represented with a @code{gdb.BpLocation} object,
+which offers the following attributes (all read only) and methods.
+Please note that breakpoint locations are very transient entities in
+@value{GDBN}, so one should avoid keeping references to them.
+
+@defvar BpLocation.owner
+This attribute holds a reference to the @code{gdb.Breakpoint} object which
+owns this location.  This attribute is not writable.  From an implementation 
+point of view, there is a @code{1 ... n} relation between a breakpoint and
+its locations, even if two breakpoints are set on at same address.
+ 
+@end defvar
+
+@defvar BpLocation.enabled
+This attribute indicates whether this location is currently enabled or not.
+This attribute is not writable.
+@end defvar
+
+@defvar BpLocation.inferior
+This attribute holds a reference to the @code{gdb.Inferior} inferior object
+in which this breakpoint location has been inserted.  The value will be 
+@code{None} if there is no inferior associated with this location.   This 
+attribute is not writable.
+@end defvar
+
+@defvar BpLocation.address
+This attribute holds a @code{gdb.Value} object corresponding to the address 
+at which the breakpoint has been inserted.   This attribute is not writable.
+@end defvar
+
+@defun BpLocation.is_valid ()
+Returns @code{True} if the @code{gdb.BpLocation} object is valid,
+@code{False} if not.  A @code{gdb.BpLocation} object may be invalidated by
+GDB at any moment for internal reasons.  All other @code{gdb.BpLocation}
+methods and attributes will throw an exception if the object is invalid.
+@end defun
+
 @node Finish Breakpoints in Python
 @subsubsection Finish Breakpoints
 
diff --git a/gdb/python/py-bploc.c b/gdb/python/py-bploc.c
new file mode 100644
index 0000000..5ba3948
--- /dev/null
+++ b/gdb/python/py-bploc.c
@@ -0,0 +1,302 @@
+/* Python interface to breakpoint locations.
+
+   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 "inferior.h"
+#include "python-internal.h"
+#include "observer.h"
+#include "gdbarch.h"
+
+struct bploc_object
+{
+  PyObject_HEAD
+
+  /* The location corresponding to this gdb.BpLocation object.  It's the same
+     idea as gdb.Breakpoint, if the 'backend' location is deleted, LOC is
+     set to NULL.  No reference to the location is owned here (in terms of
+     ref. counting) in order not to change the internal behavior.  */
+  struct bp_location *loc;
+
+  /* 1 if the owner BP has been deleted, 0 otherwise.  */
+  int invalid_owner;
+};
+
+/* Require that LOCATION be a valid bp_location; throw a Python
+   exception if it is invalid.  */
+#define BPLOCPY_REQUIRE_VALID(Location)                                 \
+    do {                                                                \
+      if ((Location)->loc == NULL)                                      \
+        return PyErr_Format (PyExc_RuntimeError,                        \
+                             _("BpLocation invalid."));                 \
+    } while (0)
+
+static PyTypeObject bploc_object_type;
+
+/* Call by free_bp_location when loc is about to be freed.  */
+
+void
+gdbpy_bplocation_free (struct bp_location *loc)
+{
+  if (loc->py_bploc_obj)
+    {
+      loc->py_bploc_obj->loc = NULL;
+      Py_DECREF (loc->py_bploc_obj);
+    }
+}
+
+/* Dissociate the bp_location from the Python object.  */
+
+static void
+bplocpy_dealloc (PyObject *self)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc != NULL)
+    self_bploc->loc->py_bploc_obj = NULL;
+
+  self->ob_type->tp_free (self);
+}
+
+/* Create or acquire a ref to the bp_location object (gdb.BpLocation)
+   that encapsulates the struct bp_location from GDB.  */
+
+PyObject *
+bplocation_to_bplocation_object (struct bp_location *loc)
+{
+  bploc_object *bploc_obj;
+
+  gdb_assert (loc != NULL);
+  if (loc->py_bploc_obj)
+    {
+      Py_INCREF (loc->py_bploc_obj);
+      return (PyObject *) loc->py_bploc_obj;
+    }
+
+  bploc_obj = PyObject_New (bploc_object, &bploc_object_type);
+  if (!bploc_obj)
+    return NULL;
+
+  bploc_obj->loc = loc;
+  bploc_obj->invalid_owner = 0;
+  Py_INCREF (bploc_obj);
+  loc->py_bploc_obj = bploc_obj;
+
+  return (PyObject *) bploc_obj;
+}
+
+/* Python function to get the BP owning this location, if any.  */
+
+static PyObject *
+bplocpy_get_owner (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->invalid_owner)
+    Py_RETURN_NONE;
+
+  if (self_bploc->loc->owner
+      && self_bploc->loc->owner->py_bp_object)
+    {
+      Py_INCREF (self_bploc->loc->owner->py_bp_object);
+      return (PyObject *) self_bploc->loc->owner->py_bp_object;
+    }
+
+  Py_RETURN_NONE;
+}
+
+/* Python function to test whether or not this breakpoint location is
+   enabled.  */
+
+static PyObject *
+bplocpy_get_enabled (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->loc->enabled)
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Python function to get the address of this breakpoint location.  The
+   gdb.Value object will be cached if this is the first access.  Returns
+   NULL in case of failure, with a Python exception set.  */
+
+static PyObject *
+bplocpy_get_address (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+  volatile struct gdb_exception except;
+  struct type *void_ptr_type;
+  struct value *val = NULL; /* Initialize to appease gcc warning.  */
+  PyObject *py_address;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  /* Get the address Value object as a void * value.  */
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      void_ptr_type = lookup_pointer_type (
+          builtin_type (python_gdbarch)->builtin_void);
+      val = value_from_pointer (void_ptr_type, self_bploc->loc->address);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  py_address = value_to_value_object (val);
+  if (!py_address)
+    return NULL;
+
+  return py_address;
+}
+
+/* Python function to get the inferior hosting this breakpoint location.
+   Return Py_None if there is no inferior associated with the program space of
+   this location, or NULL in case of failure, with a python exception set.  */
+
+static PyObject *
+bplocpy_get_inferior (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+  struct inferior *inf;
+  PyObject *infobj;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  inf = find_inferior_for_program_space (self_bploc->loc->pspace);
+  if (!inf)
+    Py_RETURN_NONE;
+
+  infobj = inferior_to_inferior_object (inf);
+  Py_XINCREF (infobj);
+
+  return infobj;
+}
+
+/* Python function which checks the validity of a bp location object.  */
+
+static PyObject *
+bplocpy_is_valid (PyObject *self, PyObject *args)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+/* Callback triggered when a breakpoint is deleted.  This will invalidate
+   the corresponding bp_location Python object owners.  */
+
+static void
+bplocpy_breakpoint_deleted (struct breakpoint *b) {
+  struct bp_location *loc;
+
+  for (loc = b->loc; loc; loc = loc->next)
+    {
+      if (loc->py_bploc_obj)
+        loc->py_bploc_obj->invalid_owner = 1;
+    }
+}
+
+/* Initialize the Python bp_location code.  */
+
+void
+gdbpy_initialize_bplocation (void)
+{
+  if (PyType_Ready (&bploc_object_type) < 0)
+    return;
+
+  Py_INCREF (&bploc_object_type);
+  if (PyModule_AddObject (gdb_module, "BpLocation",
+                          (PyObject *) &bploc_object_type) < 0)
+    return;
+
+  observer_attach_breakpoint_deleted (bplocpy_breakpoint_deleted);
+}
+
+static PyGetSetDef bploc_object_getset[] =
+{
+  { "owner", bplocpy_get_owner, NULL,
+    "Each breakpoint location must belong to exactly one higher-level \
+breakpoint.  This pointer is NULL iff this bp_location is no \
+longer attached to a breakpoint (read-only).",
+    NULL },
+  { "enabled", bplocpy_get_enabled, NULL,
+    "Is this particular location enabled.", NULL },
+  { "address", bplocpy_get_address, NULL,
+    "The address at which the breakpoint has been set.", NULL },
+  { "inferior", bplocpy_get_inferior, NULL,
+    "The inferior in which this breakpoint location has been set.", NULL },
+  { NULL }  /* Sentinel.  */
+};
+
+
+static PyMethodDef bploc_object_methods[] =
+{
+  { "is_valid", bplocpy_is_valid, METH_NOARGS,
+    "Return true if this breakpoint location is valid, false if not." },
+  { NULL } /* Sentinel.  */
+};
+
+static PyTypeObject bploc_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,                                          /* ob_size */
+  "gdb.BpLocation",                           /* tp_name */
+  sizeof (bploc_object),                      /* tp_basicsize */
+  0,                                          /* tp_itemsize */
+  bplocpy_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 breakpoint location object",           /* tp_doc */
+  0,                                          /* tp_traverse */
+  0,                                          /* tp_clear */
+  0,                                          /* tp_richcompare */
+  0,                                          /* tp_weaklistoffset */
+  0,                                          /* tp_iter */
+  0,                                          /* tp_iternext */
+  bploc_object_methods,                       /* tp_methods */
+  0,                                          /* tp_members */
+  bploc_object_getset,                        /* tp_getset */
+  0,                                          /* tp_base */
+  0,                                          /* tp_dict */
+  0,                                          /* tp_descr_get */
+  0,                                          /* tp_descr_set */
+  0,                                          /* tp_dictoffset */
+  0,                                          /* tp_init */
+  0                                           /* tp_alloc */
+};
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 195ed2b..25ef62f 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -584,6 +584,43 @@ bppy_get_ignore_count (PyObject *self, void *closure)
   return PyInt_FromLong (self_bp->bp->ignore_count);
 }
 
+
+/* Python function which returns the BpLocation objects associated
+   with this breakpoint.  */
+
+static PyObject *
+bppy_locations (PyObject *self, PyObject *args)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  PyObject *list, *tuple;
+  struct bp_location *loc;
+  int err;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  list = PyList_New (0);
+  if (!list)
+    return NULL;
+
+  err = 0;
+  for (loc = self_bp->bp->loc; loc; loc = loc->next)
+    {
+      PyObject *loc_obj =  bplocation_to_bplocation_object (loc);
+      err = PyList_Append (list, loc_obj);
+      if (err == -1)
+        {
+          Py_DECREF (list);
+          return NULL;
+        }
+      Py_DECREF (loc_obj);
+    }
+
+  tuple = PyList_AsTuple (list);
+  Py_DECREF (list);
+
+  return tuple;
+}
+
 /* Python function to create a new breakpoint.  */
 static int
 bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
@@ -975,6 +1012,8 @@ static PyMethodDef breakpoint_object_methods[] =
     "Return true if this breakpoint is valid, false if not." },
   { "delete", bppy_delete_breakpoint, METH_NOARGS,
     "Delete the underlying GDB breakpoint." },
+  { "locations", bppy_locations, METH_NOARGS,
+    "Get a list of gdb.BpLocation objects associated with this breakpoint." },
   { NULL } /* Sentinel.  */
 };
 
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 328e5d8..14608c2 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -165,6 +165,9 @@ typedef struct breakpoint_object
 extern breakpoint_object *bppy_pending_object;
 
 
+/* Defined in py-bploc.c */
+typedef struct bploc_object bploc_object;
+
 typedef struct
 {
   PyObject_HEAD
@@ -217,6 +220,8 @@ PyObject *pspy_get_printers (PyObject *, void *);
 PyObject *objfile_to_objfile_object (struct objfile *);
 PyObject *objfpy_get_printers (PyObject *, void *);
 
+PyObject *bplocation_to_bplocation_object (struct bp_location *loc);
+
 thread_object *create_thread_object (struct thread_info *tp);
 thread_object *find_thread_object (ptid_t ptid);
 PyObject *find_inferior_object (int pid);
@@ -245,6 +250,7 @@ void gdbpy_initialize_pspace (void);
 void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_breakpoints (void);
 void gdbpy_initialize_finishbreakpoints (void);
+void gdbpy_initialize_bplocation (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 5212d4e..fb88b5e 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_bplocation_free (struct breakpoint_object *bp_obj)
+{
+  return;
+}
+
 #endif /* HAVE_PYTHON */
 
 \f
@@ -1244,6 +1250,7 @@ message == an error message without a stack will be printed."),
   gdbpy_initialize_objfile ();
   gdbpy_initialize_breakpoints ();
   gdbpy_initialize_finishbreakpoints ();
+  gdbpy_initialize_bplocation ();
   gdbpy_initialize_lazy_string ();
   gdbpy_initialize_thread ();
   gdbpy_initialize_inferior ();
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 9e461f7..516a94c 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_bplocation_free (struct bp_location *loc);
+
 #endif /* GDB_PYTHON_H */
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index 8ed3742..6a1a43b 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -304,3 +304,55 @@ gdb_py_test_silent_cmd  "python wp1 = wp_eval (\"result\", type=gdb.BP_WATCHPOIN
 gdb_test "continue" ".*\[Ww\]atchpoint.*result.*Old value =.*New value = 788.*" "Test watchpoint write"
 gdb_test "python print never_eval_bp1.count" "0" \
     "Check that this unrelated breakpoints eval function was never called."
+
+# gdb.BpLocation
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+delete_breakpoints
+gdb_test_no_output "set detach-on-fork off" "don't detach on fork"
+gdb_test "call fork()" "New process .*" "create a second inferior"
+
+gdb_breakpoint "main"
+gdb_test "py print len(gdb.breakpoints())" "1" "ensure that threre is only one BP"
+gdb_test_no_output {py bp0 = gdb.breakpoints()[0]} "save breakpoint 0"
+gdb_test "py print len(bp0.locations())" "2" "ensure that threre are 2 locations"
+
+gdb_test_no_output {py loc0 = bp0.locations()[0]} "save location 0"
+gdb_test_no_output {py loc1 = bp0.locations()[1]} "save location 1"
+
+gdb_test "py print loc0.owner == loc1.owner == bp0" "True" "verify ownership"
+gdb_test "py print loc0.address == loc1.address " "True" "verify addresses are identical"
+# how to check address location ? != address(main)
+
+gdb_test {py print loc0.inferior == gdb.inferiors()[0]} "True" "verify inferior for loc 0" #inf 2
+gdb_test {py print loc1.inferior == gdb.inferiors()[1]} "True" "verify inferior for loc 1" #inf 1
+
+gdb_test "py print loc0.enabled == loc1.enabled == True" "True" "verify that locations are enabled"
+
+gdb_test "py print loc0.inferior.num" "2" "ensure that loc0 is on inferior 2"
+
+gdb_test "detach inferior 2" "Detaching from program:.*" "detach inferior 2"
+gdb_test "inferior 1" "Switching to inferior .*" "switch to inferior 1"
+gdb_test_no_output "remove-inferiors 2" "remove inferior 2"
+gdb_test "py print loc0.inferior" "None" "removed inferior set to None"
+
+delete_breakpoints
+gdb_test "py print bp0.is_valid()" "False" "verify that BP has been invalidated"
+gdb_test "py bp0.locations()" ".*RuntimeError: Breakpoint .* is invalid.*"\
+         "verify that locations can't accessed on an invalid breakpoint"
+         
+gdb_test "py print loc0.is_valid()" "False" "verify that location is invalid"
+gdb_test "py print loc0.owner" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that owner can't be accessed"
+gdb_test "py print loc0.enabled" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.address" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.inferior" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that inferior can't be accessed"
-- 
1.7.6.5


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

* Re: [PATCH] Add bp_location to Python interface
  2012-01-09 11:47         ` Kevin Pouget
@ 2012-01-09 17:23           ` Eli Zaretskii
  2012-01-10 15:09             ` Kevin Pouget
  0 siblings, 1 reply; 25+ messages in thread
From: Eli Zaretskii @ 2012-01-09 17:23 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches, pmuldoon

> From: Kevin Pouget <kevin.pouget@gmail.com>
> Date: Mon, 9 Jan 2012 12:46:30 +0100
> Cc: pmuldoon@redhat.com
> 
> ping

Sorry for missing it the first time.

> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -9,6 +9,12 @@
>  * The binary "gdbtui" can no longer be built or installed.
>    Use "gdb -tui" instead.
>  
> +* Python scripting
> +
> +  ** A new method "gdb.Breakpoint.locations" has been added, as well as
> +     the class gdb.BpLocation to provide further details about breakpoint
> +     locations.
> +

This is OK.

> +@defun gdb.locations ()
> +Return a tuple containing a sequence of @code{gdb.BpLocation} objects 
> +(see below) associated with this breakpoint.  A breakpoint with no location
> +is a pending breakpoint (@xref{Set Breaks, , pending breakpoints}).

@pxref, not @xref, as this cross-reference is inside parentheses.

> +A breakpoint location is represented with a @code{gdb.BpLocation} object,
                                        ^^^^
"by"

> +which offers the following attributes (all read only) and methods.
> +Please note that breakpoint locations are very transient entities in
> +@value{GDBN}, so one should avoid keeping references to them.

I'd use "volatile" instead of "transient".  Also, perhaps a sentence
or two about _why_ the locations are volatile would help.  E.g., if I
knew what actions invalidate locations, I could avoid them and leave
the locations valid for longer.

> +owns this location.  This attribute is not writable.  From an implementation 
> +point of view, there is a @code{1 ... n} relation between a breakpoint and

"1 ... n" here means one to many?  If so, I suggest to say that
literally.

In any case, "@code{1 ... n}" is not a good idea, because of the
whitespace and because we use @dots{} instead of literal periods.  If
"one to many" is not what you meant, I can suggest how to mark up
whatever needs to be written here.

> +This attribute holds a reference to the @code{gdb.Inferior} inferior object

I'd drop the second instance of "inferior", it looks redundant.

> +@defun BpLocation.is_valid ()
> +Returns @code{True} if the @code{gdb.BpLocation} object is valid,
> +@code{False} if not.  A @code{gdb.BpLocation} object may be invalidated by
> +GDB at any moment for internal reasons.  All other @code{gdb.BpLocation}
> +methods and attributes will throw an exception if the object is invalid.

@value{GDBN} instead of "GDB".

In any case, the last 2 sentences sound scary: I could interpret them
as meaning I cannot trust the locations at all.  If that is indeed so,
what use are they?

Thanks.

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

* Re: [PATCH] Add bp_location to Python interface
  2012-01-09 17:23           ` Eli Zaretskii
@ 2012-01-10 15:09             ` Kevin Pouget
  2012-01-10 16:03               ` Kevin Pouget
                                 ` (2 more replies)
  0 siblings, 3 replies; 25+ messages in thread
From: Kevin Pouget @ 2012-01-10 15:09 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, pmuldoon

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

On Mon, Jan 9, 2012 at 6:23 PM, Eli Zaretskii <eliz@gnu.org> wrote:
>> From: Kevin Pouget <kevin.pouget@gmail.com>
>> Date: Mon, 9 Jan 2012 12:46:30 +0100
>> Cc: pmuldoon@redhat.com
>>
>> ping
>
> Sorry for missing it the first time.

no problem, thanks for your feedbacks, I replied inline

>> --- a/gdb/NEWS
>> +++ b/gdb/NEWS
>> @@ -9,6 +9,12 @@
>>  * The binary "gdbtui" can no longer be built or installed.
>>    Use "gdb -tui" instead.
>>
>> +* Python scripting
>> +
>> +  ** A new method "gdb.Breakpoint.locations" has been added, as well as
>> +     the class gdb.BpLocation to provide further details about breakpoint
>> +     locations.
>> +
>
> This is OK.
>
>> +@defun gdb.locations ()
>> +Return a tuple containing a sequence of @code{gdb.BpLocation} objects
>> +(see below) associated with this breakpoint.  A breakpoint with no location
>> +is a pending breakpoint (@xref{Set Breaks, , pending breakpoints}).
>
> @pxref, not @xref, as this cross-reference is inside parentheses.
>
>> +A breakpoint location is represented with a @code{gdb.BpLocation} object,
>                                        ^^^^
> "by"

fixed

>> +which offers the following attributes (all read only) and methods.
>> +Please note that breakpoint locations are very transient entities in
>> +@value{GDBN}, so one should avoid keeping references to them.
>
> I'd use "volatile" instead of "transient".

fixed

> Also, perhaps a sentence
> or two about _why_ the locations are volatile would help.  E.g., if I
> knew what actions invalidate locations, I could avoid them and leave
> the locations valid for longer.

That's a point Phil also noted so I've try to explain it a bit
further, although my knowledge about the internal mechanisms involved
is quite limited. A maintainer will have to confirm these words. I
also mentioned explicitly that it's the *objects* related to the
locations which are volatile, because the location (ie, address)
itself actually doesn't change:

> Please note that breakpoint location objects are very volatile entities in
> @value{GDBN}, so one should avoid keeping references to them.  They can be
> destructed and recreated on any breakpoint creation and deletion, shared-
> library event, inferior function call, @dots{}



>> +owns this location.  This attribute is not writable.  From an implementation
>> +point of view, there is a @code{1 ... n} relation between a breakpoint and
>
> "1 ... n" here means one to many?  If so, I suggest to say that
> literally.
>
> In any case, "@code{1 ... n}" is not a good idea, because of the
> whitespace and because we use @dots{} instead of literal periods.  If
> "one to many" is not what you meant, I can suggest how to mark up
> whatever needs to be written here.

No, "one-to-many" is what I meant, I've changed it (spelled with the
dashes, right?)

>> +This attribute holds a reference to the @code{gdb.Inferior} inferior object
>
> I'd drop the second instance of "inferior", it looks redundant.

dropped

>> +@defun BpLocation.is_valid ()
>> +Returns @code{True} if the @code{gdb.BpLocation} object is valid,
>> +@code{False} if not.  A @code{gdb.BpLocation} object may be invalidated by
>> +GDB at any moment for internal reasons.  All other @code{gdb.BpLocation}
>> +methods and attributes will throw an exception if the object is invalid.
>
> @value{GDBN} instead of "GDB".
>
> In any case, the last 2 sentences sound scary: I could interpret them
> as meaning I cannot trust the locations at all.  If that is indeed so,
> what use are they?

that's already discussed above, but I don't want you to be scared, so
let me explain what I meant:
it's not "at any moment", but rather "after any call to GDB's Python
interface". We may want to say that it's only breakpoint or
execution-related calls, but _I_ can't ensure that this is true, and
it 'might' change in the future:

> A @code{gdb.BpLocation} object may be invalidated during
> any call to @{GDB}'s API for internal reasons (for instance, but not limited to,
> breakpoint or execution-related mechanisms).

(I'm not sure what's the right Python term here for 'mechanisms',
reading/writing an attribute may trigger internal functions, so 'call'
or 'function' seem not to suit very well)


Cordially,

Kevin

--

2012-01-10  Kevin Pouget  <kevin.pouget@st.com>

	Add bp_location to Python interface
	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-bploc.o
	(SUBDIR_PYTHON_SRCS): Add python/py-bploc.c
	Add build rule for this file.
	* breakpoint.h (struct bploc_object): Forward declaration.
	(struct bp_location): Add py_bploc_obj.
	* breakpoint.c (free_bp_location): Call gdbpy_bplocation_free.
	* python/py-bploc.c: New file.
	* python/py-breakpoint.c (bppy_locations): New function.
	(breakpoint_object_methods): New method binding: locations().
	* python/python-internal.h (bploc_object): New typedef.
	(bplocation_to_bplocation_object): New prototype.
	(gdbpy_initialize_bplocation): Likewise.
	* python/python.c (gdbpy_bplocation_free): New empty stub.
	(_initialize_python): Call gdbpy_initialize_bplocation.
	* python/python.h (gdbpy_bplocation_free): New prototype.
	
doc/
	Add bp_location to Python interface
	* gdb.texinfo (Breakpoints In Python): Document
	gdb.Breakpoint.locations and gdb.BpLocation.


testsuite/
	Add bp_location to Python interface
	* gdb.python/py-breakpoint.exp: Test gdb.BpLocation.

[-- Attachment #2: 0001-Add-gdb.BpLocation-to-Python-interface.patch --]
[-- Type: text/x-patch, Size: 22721 bytes --]

From 1b097c779842996236636c3cc228b1895c8d5968 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Wed, 18 May 2011 10:02:23 -0400
Subject: [PATCH] Add gdb.BpLocation to Python interface

---
 gdb/Makefile.in                            |    6 +
 gdb/NEWS                                   |    6 +
 gdb/breakpoint.c                           |    3 +
 gdb/breakpoint.h                           |    6 +
 gdb/doc/gdb.texinfo                        |   47 +++++
 gdb/python/py-bploc.c                      |  302 ++++++++++++++++++++++++++++
 gdb/python/py-breakpoint.c                 |   39 ++++
 gdb/python/python-internal.h               |    6 +
 gdb/python/python.c                        |    7 +
 gdb/python/python.h                        |    2 +
 gdb/testsuite/gdb.python/py-breakpoint.exp |   52 +++++
 11 files changed, 476 insertions(+), 0 deletions(-)
 create mode 100644 gdb/python/py-bploc.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index a5196a7..1928c7e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -273,6 +273,7 @@ SUBDIR_PYTHON_OBS = \
 	py-auto-load.o \
 	py-block.o \
 	py-bpevent.o \
+	py-bploc.o \
 	py-breakpoint.o \
 	py-cmd.o \
 	py-continueevent.o \
@@ -305,6 +306,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-auto-load.c \
 	python/py-block.c \
 	python/py-bpevent.c \
+	python/py-bploc.c \
 	python/py-breakpoint.c \
 	python/py-cmd.c \
 	python/py-continueevent.c \
@@ -2032,6 +2034,10 @@ py-bpevent.o: $(srcdir)/python/py-bpevent.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bpevent.c
 	$(POSTCOMPILE)
 
+py-bploc.o: $(srcdir)/python/py-bploc.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bploc.c
+	$(POSTCOMPILE)
+
 py-breakpoint.o: $(srcdir)/python/py-breakpoint.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-breakpoint.c
 	$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index a9a7859..c529f96 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9,6 +9,12 @@
 * The binary "gdbtui" can no longer be built or installed.
   Use "gdb -tui" instead.
 
+* Python scripting
+
+  ** A new method "gdb.Breakpoint.locations" has been added, as well as
+     the class gdb.BpLocation to provide further details about breakpoint
+     locations.
+
 *** Changes in GDB 7.4
 
 * GDB now handles ambiguous linespecs more consistently; the existing
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 7b8d10f..28b7579 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5678,6 +5678,9 @@ static void
 free_bp_location (struct bp_location *loc)
 {
   loc->ops->dtor (loc);
+
+  gdbpy_bplocation_free (loc);
+
   xfree (loc);
 }
 
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index c1d3be9..8d6fa19 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -26,6 +26,7 @@
 struct value;
 struct block;
 struct breakpoint_object;
+struct bploc_object;
 struct get_number_or_range_state;
 struct thread_info;
 struct bpstats;
@@ -403,6 +404,11 @@ struct bp_location
   /* Source file name of this address.  */
 
   char *source_file;
+
+  /* Python object associated with this location.  May be NULL if the location
+     is not yet exported to Python.  */
+
+  struct bploc_object *py_bploc_obj;
 };
 
 /* This structure is a collection of function pointers that, if available,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2f4aa4f..4df7dbe 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24358,6 +24358,53 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@findex gdb.locations
+@defun gdb.locations ()
+Return a tuple containing a sequence of @code{gdb.BpLocation} objects 
+(see below) associated with this breakpoint.  A breakpoint with no location
+is a pending breakpoint (@xref{Set Breaks, , pending breakpoints}).
+@end defun
+
+A breakpoint location is represented by a @code{gdb.BpLocation} object,
+which offers the following attributes (all read only) and methods.
+Please note that breakpoint location objects are very volatile entities in
+@value{GDBN}, so one should avoid keeping references to them.  They can be 
+destructed and recreated on any breakpoint creation and deletion, shared-
+library event, inferior function call, @dots{} 
+
+@defvar BpLocation.owner
+This attribute holds a reference to the @code{gdb.Breakpoint} object which
+owns this location.  This attribute is not writable.  From an implementation 
+point of view, there is a one-to-many relation between a breakpoint and
+its locations, even if two breakpoints are set on at same address.
+ 
+@end defvar
+
+@defvar BpLocation.enabled
+This attribute indicates whether this location is currently enabled or not.
+This attribute is not writable.
+@end defvar
+
+@defvar BpLocation.inferior
+This attribute holds a reference to the @code{gdb.Inferior} object
+in which this breakpoint location has been inserted.  The value will be 
+@code{None} if there is no inferior associated with this location.   This 
+attribute is not writable.
+@end defvar
+
+@defvar BpLocation.address
+This attribute holds a @code{gdb.Value} object corresponding to the address 
+at which the breakpoint has been inserted.   This attribute is not writable.
+@end defvar
+
+@defun BpLocation.is_valid ()
+Returns @code{True} if the @code{gdb.BpLocation} object is valid,
+@code{False} if not.  A @code{gdb.BpLocation} object may be invalidated during
+any call to @{GDB}'s API for internal reasons (for instance, but not limited to,
+breakpoint or execution related mechanisms).  All other @code{gdb.BpLocation}
+methods and attributes will throw an exception if the object is invalid.
+@end defun
+
 @node Finish Breakpoints in Python
 @subsubsection Finish Breakpoints
 
diff --git a/gdb/python/py-bploc.c b/gdb/python/py-bploc.c
new file mode 100644
index 0000000..5ba3948
--- /dev/null
+++ b/gdb/python/py-bploc.c
@@ -0,0 +1,302 @@
+/* Python interface to breakpoint locations.
+
+   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 "inferior.h"
+#include "python-internal.h"
+#include "observer.h"
+#include "gdbarch.h"
+
+struct bploc_object
+{
+  PyObject_HEAD
+
+  /* The location corresponding to this gdb.BpLocation object.  It's the same
+     idea as gdb.Breakpoint, if the 'backend' location is deleted, LOC is
+     set to NULL.  No reference to the location is owned here (in terms of
+     ref. counting) in order not to change the internal behavior.  */
+  struct bp_location *loc;
+
+  /* 1 if the owner BP has been deleted, 0 otherwise.  */
+  int invalid_owner;
+};
+
+/* Require that LOCATION be a valid bp_location; throw a Python
+   exception if it is invalid.  */
+#define BPLOCPY_REQUIRE_VALID(Location)                                 \
+    do {                                                                \
+      if ((Location)->loc == NULL)                                      \
+        return PyErr_Format (PyExc_RuntimeError,                        \
+                             _("BpLocation invalid."));                 \
+    } while (0)
+
+static PyTypeObject bploc_object_type;
+
+/* Call by free_bp_location when loc is about to be freed.  */
+
+void
+gdbpy_bplocation_free (struct bp_location *loc)
+{
+  if (loc->py_bploc_obj)
+    {
+      loc->py_bploc_obj->loc = NULL;
+      Py_DECREF (loc->py_bploc_obj);
+    }
+}
+
+/* Dissociate the bp_location from the Python object.  */
+
+static void
+bplocpy_dealloc (PyObject *self)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc != NULL)
+    self_bploc->loc->py_bploc_obj = NULL;
+
+  self->ob_type->tp_free (self);
+}
+
+/* Create or acquire a ref to the bp_location object (gdb.BpLocation)
+   that encapsulates the struct bp_location from GDB.  */
+
+PyObject *
+bplocation_to_bplocation_object (struct bp_location *loc)
+{
+  bploc_object *bploc_obj;
+
+  gdb_assert (loc != NULL);
+  if (loc->py_bploc_obj)
+    {
+      Py_INCREF (loc->py_bploc_obj);
+      return (PyObject *) loc->py_bploc_obj;
+    }
+
+  bploc_obj = PyObject_New (bploc_object, &bploc_object_type);
+  if (!bploc_obj)
+    return NULL;
+
+  bploc_obj->loc = loc;
+  bploc_obj->invalid_owner = 0;
+  Py_INCREF (bploc_obj);
+  loc->py_bploc_obj = bploc_obj;
+
+  return (PyObject *) bploc_obj;
+}
+
+/* Python function to get the BP owning this location, if any.  */
+
+static PyObject *
+bplocpy_get_owner (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->invalid_owner)
+    Py_RETURN_NONE;
+
+  if (self_bploc->loc->owner
+      && self_bploc->loc->owner->py_bp_object)
+    {
+      Py_INCREF (self_bploc->loc->owner->py_bp_object);
+      return (PyObject *) self_bploc->loc->owner->py_bp_object;
+    }
+
+  Py_RETURN_NONE;
+}
+
+/* Python function to test whether or not this breakpoint location is
+   enabled.  */
+
+static PyObject *
+bplocpy_get_enabled (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->loc->enabled)
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Python function to get the address of this breakpoint location.  The
+   gdb.Value object will be cached if this is the first access.  Returns
+   NULL in case of failure, with a Python exception set.  */
+
+static PyObject *
+bplocpy_get_address (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+  volatile struct gdb_exception except;
+  struct type *void_ptr_type;
+  struct value *val = NULL; /* Initialize to appease gcc warning.  */
+  PyObject *py_address;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  /* Get the address Value object as a void * value.  */
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      void_ptr_type = lookup_pointer_type (
+          builtin_type (python_gdbarch)->builtin_void);
+      val = value_from_pointer (void_ptr_type, self_bploc->loc->address);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  py_address = value_to_value_object (val);
+  if (!py_address)
+    return NULL;
+
+  return py_address;
+}
+
+/* Python function to get the inferior hosting this breakpoint location.
+   Return Py_None if there is no inferior associated with the program space of
+   this location, or NULL in case of failure, with a python exception set.  */
+
+static PyObject *
+bplocpy_get_inferior (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+  struct inferior *inf;
+  PyObject *infobj;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  inf = find_inferior_for_program_space (self_bploc->loc->pspace);
+  if (!inf)
+    Py_RETURN_NONE;
+
+  infobj = inferior_to_inferior_object (inf);
+  Py_XINCREF (infobj);
+
+  return infobj;
+}
+
+/* Python function which checks the validity of a bp location object.  */
+
+static PyObject *
+bplocpy_is_valid (PyObject *self, PyObject *args)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+/* Callback triggered when a breakpoint is deleted.  This will invalidate
+   the corresponding bp_location Python object owners.  */
+
+static void
+bplocpy_breakpoint_deleted (struct breakpoint *b) {
+  struct bp_location *loc;
+
+  for (loc = b->loc; loc; loc = loc->next)
+    {
+      if (loc->py_bploc_obj)
+        loc->py_bploc_obj->invalid_owner = 1;
+    }
+}
+
+/* Initialize the Python bp_location code.  */
+
+void
+gdbpy_initialize_bplocation (void)
+{
+  if (PyType_Ready (&bploc_object_type) < 0)
+    return;
+
+  Py_INCREF (&bploc_object_type);
+  if (PyModule_AddObject (gdb_module, "BpLocation",
+                          (PyObject *) &bploc_object_type) < 0)
+    return;
+
+  observer_attach_breakpoint_deleted (bplocpy_breakpoint_deleted);
+}
+
+static PyGetSetDef bploc_object_getset[] =
+{
+  { "owner", bplocpy_get_owner, NULL,
+    "Each breakpoint location must belong to exactly one higher-level \
+breakpoint.  This pointer is NULL iff this bp_location is no \
+longer attached to a breakpoint (read-only).",
+    NULL },
+  { "enabled", bplocpy_get_enabled, NULL,
+    "Is this particular location enabled.", NULL },
+  { "address", bplocpy_get_address, NULL,
+    "The address at which the breakpoint has been set.", NULL },
+  { "inferior", bplocpy_get_inferior, NULL,
+    "The inferior in which this breakpoint location has been set.", NULL },
+  { NULL }  /* Sentinel.  */
+};
+
+
+static PyMethodDef bploc_object_methods[] =
+{
+  { "is_valid", bplocpy_is_valid, METH_NOARGS,
+    "Return true if this breakpoint location is valid, false if not." },
+  { NULL } /* Sentinel.  */
+};
+
+static PyTypeObject bploc_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,                                          /* ob_size */
+  "gdb.BpLocation",                           /* tp_name */
+  sizeof (bploc_object),                      /* tp_basicsize */
+  0,                                          /* tp_itemsize */
+  bplocpy_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 breakpoint location object",           /* tp_doc */
+  0,                                          /* tp_traverse */
+  0,                                          /* tp_clear */
+  0,                                          /* tp_richcompare */
+  0,                                          /* tp_weaklistoffset */
+  0,                                          /* tp_iter */
+  0,                                          /* tp_iternext */
+  bploc_object_methods,                       /* tp_methods */
+  0,                                          /* tp_members */
+  bploc_object_getset,                        /* tp_getset */
+  0,                                          /* tp_base */
+  0,                                          /* tp_dict */
+  0,                                          /* tp_descr_get */
+  0,                                          /* tp_descr_set */
+  0,                                          /* tp_dictoffset */
+  0,                                          /* tp_init */
+  0                                           /* tp_alloc */
+};
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 195ed2b..25ef62f 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -584,6 +584,43 @@ bppy_get_ignore_count (PyObject *self, void *closure)
   return PyInt_FromLong (self_bp->bp->ignore_count);
 }
 
+
+/* Python function which returns the BpLocation objects associated
+   with this breakpoint.  */
+
+static PyObject *
+bppy_locations (PyObject *self, PyObject *args)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  PyObject *list, *tuple;
+  struct bp_location *loc;
+  int err;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  list = PyList_New (0);
+  if (!list)
+    return NULL;
+
+  err = 0;
+  for (loc = self_bp->bp->loc; loc; loc = loc->next)
+    {
+      PyObject *loc_obj =  bplocation_to_bplocation_object (loc);
+      err = PyList_Append (list, loc_obj);
+      if (err == -1)
+        {
+          Py_DECREF (list);
+          return NULL;
+        }
+      Py_DECREF (loc_obj);
+    }
+
+  tuple = PyList_AsTuple (list);
+  Py_DECREF (list);
+
+  return tuple;
+}
+
 /* Python function to create a new breakpoint.  */
 static int
 bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
@@ -975,6 +1012,8 @@ static PyMethodDef breakpoint_object_methods[] =
     "Return true if this breakpoint is valid, false if not." },
   { "delete", bppy_delete_breakpoint, METH_NOARGS,
     "Delete the underlying GDB breakpoint." },
+  { "locations", bppy_locations, METH_NOARGS,
+    "Get a list of gdb.BpLocation objects associated with this breakpoint." },
   { NULL } /* Sentinel.  */
 };
 
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 328e5d8..14608c2 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -165,6 +165,9 @@ typedef struct breakpoint_object
 extern breakpoint_object *bppy_pending_object;
 
 
+/* Defined in py-bploc.c */
+typedef struct bploc_object bploc_object;
+
 typedef struct
 {
   PyObject_HEAD
@@ -217,6 +220,8 @@ PyObject *pspy_get_printers (PyObject *, void *);
 PyObject *objfile_to_objfile_object (struct objfile *);
 PyObject *objfpy_get_printers (PyObject *, void *);
 
+PyObject *bplocation_to_bplocation_object (struct bp_location *loc);
+
 thread_object *create_thread_object (struct thread_info *tp);
 thread_object *find_thread_object (ptid_t ptid);
 PyObject *find_inferior_object (int pid);
@@ -245,6 +250,7 @@ void gdbpy_initialize_pspace (void);
 void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_breakpoints (void);
 void gdbpy_initialize_finishbreakpoints (void);
+void gdbpy_initialize_bplocation (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 5212d4e..fb88b5e 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_bplocation_free (struct breakpoint_object *bp_obj)
+{
+  return;
+}
+
 #endif /* HAVE_PYTHON */
 
 \f
@@ -1244,6 +1250,7 @@ message == an error message without a stack will be printed."),
   gdbpy_initialize_objfile ();
   gdbpy_initialize_breakpoints ();
   gdbpy_initialize_finishbreakpoints ();
+  gdbpy_initialize_bplocation ();
   gdbpy_initialize_lazy_string ();
   gdbpy_initialize_thread ();
   gdbpy_initialize_inferior ();
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 9e461f7..516a94c 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_bplocation_free (struct bp_location *loc);
+
 #endif /* GDB_PYTHON_H */
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index 8ed3742..6a1a43b 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -304,3 +304,55 @@ gdb_py_test_silent_cmd  "python wp1 = wp_eval (\"result\", type=gdb.BP_WATCHPOIN
 gdb_test "continue" ".*\[Ww\]atchpoint.*result.*Old value =.*New value = 788.*" "Test watchpoint write"
 gdb_test "python print never_eval_bp1.count" "0" \
     "Check that this unrelated breakpoints eval function was never called."
+
+# gdb.BpLocation
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+delete_breakpoints
+gdb_test_no_output "set detach-on-fork off" "don't detach on fork"
+gdb_test "call fork()" "New process .*" "create a second inferior"
+
+gdb_breakpoint "main"
+gdb_test "py print len(gdb.breakpoints())" "1" "ensure that threre is only one BP"
+gdb_test_no_output {py bp0 = gdb.breakpoints()[0]} "save breakpoint 0"
+gdb_test "py print len(bp0.locations())" "2" "ensure that threre are 2 locations"
+
+gdb_test_no_output {py loc0 = bp0.locations()[0]} "save location 0"
+gdb_test_no_output {py loc1 = bp0.locations()[1]} "save location 1"
+
+gdb_test "py print loc0.owner == loc1.owner == bp0" "True" "verify ownership"
+gdb_test "py print loc0.address == loc1.address " "True" "verify addresses are identical"
+# how to check address location ? != address(main)
+
+gdb_test {py print loc0.inferior == gdb.inferiors()[0]} "True" "verify inferior for loc 0" #inf 2
+gdb_test {py print loc1.inferior == gdb.inferiors()[1]} "True" "verify inferior for loc 1" #inf 1
+
+gdb_test "py print loc0.enabled == loc1.enabled == True" "True" "verify that locations are enabled"
+
+gdb_test "py print loc0.inferior.num" "2" "ensure that loc0 is on inferior 2"
+
+gdb_test "detach inferior 2" "Detaching from program:.*" "detach inferior 2"
+gdb_test "inferior 1" "Switching to inferior .*" "switch to inferior 1"
+gdb_test_no_output "remove-inferiors 2" "remove inferior 2"
+gdb_test "py print loc0.inferior" "None" "removed inferior set to None"
+
+delete_breakpoints
+gdb_test "py print bp0.is_valid()" "False" "verify that BP has been invalidated"
+gdb_test "py bp0.locations()" ".*RuntimeError: Breakpoint .* is invalid.*"\
+         "verify that locations can't accessed on an invalid breakpoint"
+         
+gdb_test "py print loc0.is_valid()" "False" "verify that location is invalid"
+gdb_test "py print loc0.owner" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that owner can't be accessed"
+gdb_test "py print loc0.enabled" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.address" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.inferior" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that inferior can't be accessed"
-- 
1.7.6.5


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

* Re: [PATCH] Add bp_location to Python interface
  2012-01-10 15:09             ` Kevin Pouget
@ 2012-01-10 16:03               ` Kevin Pouget
  2012-01-10 17:25               ` Eli Zaretskii
  2012-01-10 22:24               ` Doug Evans
  2 siblings, 0 replies; 25+ messages in thread
From: Kevin Pouget @ 2012-01-10 16:03 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, pmuldoon

On Tue, Jan 10, 2012 at 3:50 PM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
> On Mon, Jan 9, 2012 at 6:23 PM, Eli Zaretskii <eliz@gnu.org> wrote:
>>> From: Kevin Pouget <kevin.pouget@gmail.com>
>>> Date: Mon, 9 Jan 2012 12:46:30 +0100
>>> Cc: pmuldoon@redhat.com
>>>
>>> ping
>>
>> Sorry for missing it the first time.
>
> no problem, thanks for your feedbacks, I replied inline
>
>>> --- a/gdb/NEWS
>>> +++ b/gdb/NEWS
>>> @@ -9,6 +9,12 @@
>>>  * The binary "gdbtui" can no longer be built or installed.
>>>    Use "gdb -tui" instead.
>>>
>>> +* Python scripting
>>> +
>>> +  ** A new method "gdb.Breakpoint.locations" has been added, as well as
>>> +     the class gdb.BpLocation to provide further details about breakpoint
>>> +     locations.
>>> +
>>
>> This is OK.
>>
>>> +@defun gdb.locations ()
>>> +Return a tuple containing a sequence of @code{gdb.BpLocation} objects
>>> +(see below) associated with this breakpoint.  A breakpoint with no location
>>> +is a pending breakpoint (@xref{Set Breaks, , pending breakpoints}).
>>
>> @pxref, not @xref, as this cross-reference is inside parentheses.
>>
>>> +A breakpoint location is represented with a @code{gdb.BpLocation} object,
>>                                        ^^^^
>> "by"
>
> fixed
>
>>> +which offers the following attributes (all read only) and methods.
>>> +Please note that breakpoint locations are very transient entities in
>>> +@value{GDBN}, so one should avoid keeping references to them.
>>
>> I'd use "volatile" instead of "transient".
>
> fixed
>
>> Also, perhaps a sentence
>> or two about _why_ the locations are volatile would help.  E.g., if I
>> knew what actions invalidate locations, I could avoid them and leave
>> the locations valid for longer.
>
> That's a point Phil also noted so I've try to explain it a bit
> further, although my knowledge about the internal mechanisms involved
> is quite limited. A maintainer will have to confirm these words. I
> also mentioned explicitly that it's the *objects* related to the
> locations which are volatile, because the location (ie, address)
> itself actually doesn't change:
>
>> Please note that breakpoint location objects are very volatile entities in
>> @value{GDBN}, so one should avoid keeping references to them.  They can be
>> destructed and recreated on any breakpoint creation and deletion, shared-
>> library event, inferior function call, @dots{}
>
>
>
>>> +owns this location.  This attribute is not writable.  From an implementation
>>> +point of view, there is a @code{1 ... n} relation between a breakpoint and
>>
>> "1 ... n" here means one to many?  If so, I suggest to say that
>> literally.
>>
>> In any case, "@code{1 ... n}" is not a good idea, because of the
>> whitespace and because we use @dots{} instead of literal periods.  If
>> "one to many" is not what you meant, I can suggest how to mark up
>> whatever needs to be written here.
>
> No, "one-to-many" is what I meant, I've changed it (spelled with the
> dashes, right?)
>
>>> +This attribute holds a reference to the @code{gdb.Inferior} inferior object
>>
>> I'd drop the second instance of "inferior", it looks redundant.
>
> dropped
>
>>> +@defun BpLocation.is_valid ()
>>> +Returns @code{True} if the @code{gdb.BpLocation} object is valid,
>>> +@code{False} if not.  A @code{gdb.BpLocation} object may be invalidated by
>>> +GDB at any moment for internal reasons.  All other @code{gdb.BpLocation}
>>> +methods and attributes will throw an exception if the object is invalid.
>>
>> @value{GDBN} instead of "GDB".
>>
>> In any case, the last 2 sentences sound scary: I could interpret them
>> as meaning I cannot trust the locations at all.  If that is indeed so,
>> what use are they?
>
> that's already discussed above, but I don't want you to be scared, so
> let me explain what I meant:
> it's not "at any moment", but rather "after any call to GDB's Python
> interface". We may want to say that it's only breakpoint or
> execution-related calls, but _I_ can't ensure that this is true, and
> it 'might' change in the future:
>
>> A @code{gdb.BpLocation} object may be invalidated during
>> any call to @{GDB}'s API for internal reasons (for instance, but not limited to,
>> breakpoint or execution-related mechanisms).

Sorry there is a missing 'value' in @{GDB} here and in the patch, it
will be fixed for the next submission.

And a typo,
> @defvar BpLocation.owner
> ... even if two breakpoints are set on at same address.
I removed the redundant 'on'.


Kevin

> (I'm not sure what's the right Python term here for 'mechanisms',
> reading/writing an attribute may trigger internal functions, so 'call'
> or 'function' seem not to suit very well)
>
>
> Cordially,
>
> Kevin

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

* Re: [PATCH] Add bp_location to Python interface
  2012-01-10 15:09             ` Kevin Pouget
  2012-01-10 16:03               ` Kevin Pouget
@ 2012-01-10 17:25               ` Eli Zaretskii
  2012-01-11 10:16                 ` Kevin Pouget
  2012-01-10 22:24               ` Doug Evans
  2 siblings, 1 reply; 25+ messages in thread
From: Eli Zaretskii @ 2012-01-10 17:25 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches, pmuldoon

> From: Kevin Pouget <kevin.pouget@gmail.com>
> Date: Tue, 10 Jan 2012 15:50:30 +0100
> Cc: gdb-patches@sourceware.org, pmuldoon@redhat.com
> 
> > In any case, the last 2 sentences sound scary: I could interpret them
> > as meaning I cannot trust the locations at all.  If that is indeed so,
> > what use are they?
> 
> that's already discussed above, but I don't want you to be scared, so
> let me explain what I meant:
> it's not "at any moment", but rather "after any call to GDB's Python
> interface". We may want to say that it's only breakpoint or
> execution-related calls, but _I_ can't ensure that this is true, and
> it 'might' change in the future:
> 
> > A @code{gdb.BpLocation} object may be invalidated during
> > any call to @{GDB}'s API for internal reasons (for instance, but not limited to,
> > breakpoint or execution-related mechanisms).

Sounds okay to me.  But you don't need "but not limited to", because
"for instance" already says that.

> +Return a tuple containing a sequence of @code{gdb.BpLocation} objects 
> +(see below) associated with this breakpoint.  A breakpoint with no location
> +is a pending breakpoint (@xref{Set Breaks, , pending breakpoints}).
                            ^^^^^
Still an @xref...

> +any call to @{GDB}'s API for internal reasons (for instance, but not limited to,
               ^^^^^^
You already know what to fix here...

The documentation parts are OK with those changes.

Thanks.

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

* Re: [PATCH] Add bp_location to Python interface
  2012-01-10 15:09             ` Kevin Pouget
  2012-01-10 16:03               ` Kevin Pouget
  2012-01-10 17:25               ` Eli Zaretskii
@ 2012-01-10 22:24               ` Doug Evans
  2012-01-11  9:05                 ` Kevin Pouget
  2 siblings, 1 reply; 25+ messages in thread
From: Doug Evans @ 2012-01-10 22:24 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: Eli Zaretskii, gdb-patches, pmuldoon

On Tue, Jan 10, 2012 at 6:50 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
>>> +@defun BpLocation.is_valid ()
>>> +Returns @code{True} if the @code{gdb.BpLocation} object is valid,
>>> +@code{False} if not.  A @code{gdb.BpLocation} object may be invalidated by
>>> +GDB at any moment for internal reasons.  All other @code{gdb.BpLocation}
>>> +methods and attributes will throw an exception if the object is invalid.
>>
>> @value{GDBN} instead of "GDB".
>>
>> In any case, the last 2 sentences sound scary: I could interpret them
>> as meaning I cannot trust the locations at all.  If that is indeed so,
>> what use are they?
>
> that's already discussed above, but I don't want you to be scared, so
> let me explain what I meant:
> it's not "at any moment", but rather "after any call to GDB's Python
> interface". We may want to say that it's only breakpoint or
> execution-related calls, but _I_ can't ensure that this is true, and
> it 'might' change in the future:
>
>> A @code{gdb.BpLocation} object may be invalidated during
>> any call to @{GDB}'s API for internal reasons (for instance, but not limited to,
>> breakpoint or execution-related mechanisms).
>
> (I'm not sure what's the right Python term here for 'mechanisms',
> reading/writing an attribute may trigger internal functions, so 'call'
> or 'function' seem not to suit very well)

Don't take this as a requirement, but this volatility isn't a property
of python's BpLocations,
Thus I'd rather see the general discussion of why breakpoint locations
are volatile, scary, whatever, elsewhere, and just have a link to that
documentation here.
I think that will help clear things up: In the breakpoints section of
the manual just state what breakpoint locations are, what their
properties are, etc.  Then in the above docs you don't have to worry
so much about scaryness or whatever: Whatever issues locations have,
users already need to be at least somewhat aware of them.

Another reason to do this is that when Guile scripting gets added, we
don't want to have to document these issues twice. :-)

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

* Re: [PATCH] Add bp_location to Python interface
  2012-01-10 22:24               ` Doug Evans
@ 2012-01-11  9:05                 ` Kevin Pouget
  2012-01-11 19:45                   ` Doug Evans
  0 siblings, 1 reply; 25+ messages in thread
From: Kevin Pouget @ 2012-01-11  9:05 UTC (permalink / raw)
  To: Doug Evans; +Cc: Eli Zaretskii, gdb-patches, pmuldoon

On Tue, Jan 10, 2012 at 11:18 PM, Doug Evans <dje@google.com> wrote:
> On Tue, Jan 10, 2012 at 6:50 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
>>>> +@defun BpLocation.is_valid ()
>>>> +Returns @code{True} if the @code{gdb.BpLocation} object is valid,
>>>> +@code{False} if not.  A @code{gdb.BpLocation} object may be invalidated by
>>>> +GDB at any moment for internal reasons.  All other @code{gdb.BpLocation}
>>>> +methods and attributes will throw an exception if the object is invalid.
>>>
>>> @value{GDBN} instead of "GDB".
>>>
>>> In any case, the last 2 sentences sound scary: I could interpret them
>>> as meaning I cannot trust the locations at all.  If that is indeed so,
>>> what use are they?
>>
>> that's already discussed above, but I don't want you to be scared, so
>> let me explain what I meant:
>> it's not "at any moment", but rather "after any call to GDB's Python
>> interface". We may want to say that it's only breakpoint or
>> execution-related calls, but _I_ can't ensure that this is true, and
>> it 'might' change in the future:
>>
>>> A @code{gdb.BpLocation} object may be invalidated during
>>> any call to @{GDB}'s API for internal reasons (for instance, but not limited to,
>>> breakpoint or execution-related mechanisms).
>>
>> (I'm not sure what's the right Python term here for 'mechanisms',
>> reading/writing an attribute may trigger internal functions, so 'call'
>> or 'function' seem not to suit very well)
>
> Don't take this as a requirement, but this volatility isn't a property
> of python's BpLocations,
> Thus I'd rather see the general discussion of why breakpoint locations
> are volatile, scary, whatever, elsewhere, and just have a link to that
> documentation here.
> I think that will help clear things up: In the breakpoints section of
> the manual just state what breakpoint locations are, what their
> properties are, etc.  Then in the above docs you don't have to worry
> so much about scaryness or whatever: Whatever issues locations have,
> users already need to be at least somewhat aware of them.
>
> Another reason to do this is that when Guile scripting gets added, we
> don't want to have to document these issues twice. :-)

I'm not sure we can really move it away from the Python/Guile API,
because, AFAIU, this volatile aspect if purely internal. 'Normal'
users won't ever see it.

> $ i b
> Num     Type           Disp Enb Address            What
> 1       breakpoint     keep y    <MULTIPLE>
>         1.1                         y     0x4562c0 in main at ../../../git/gdb/gdb/gdb.c:26 inf 2
>         1.2                         y     0x4562c0 in main at ../../../git/gdb/gdb/gdb.c:26 inf 1

will remain the same all the time, but the objects (both in Python and
their GDB backends) in gdb.breakpoints()[0].locations() will be
deleted and re-created at various moments (cf. previous discussions).
So far, I couldn't really understand what's the rational behind that
...

[I'm not sure how well you know this part of GDB, please don't
hesitate to correct me if I'm wrong]


Kevin

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

* Re: [PATCH] Add bp_location to Python interface
  2012-01-10 17:25               ` Eli Zaretskii
@ 2012-01-11 10:16                 ` Kevin Pouget
  2012-01-11 10:27                   ` Eli Zaretskii
  0 siblings, 1 reply; 25+ messages in thread
From: Kevin Pouget @ 2012-01-11 10:16 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, pmuldoon

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

On Tue, Jan 10, 2012 at 6:17 PM, Eli Zaretskii <eliz@gnu.org> wrote:
>> From: Kevin Pouget <kevin.pouget@gmail.com>
>> Date: Tue, 10 Jan 2012 15:50:30 +0100
>> Cc: gdb-patches@sourceware.org, pmuldoon@redhat.com
>>
>> > In any case, the last 2 sentences sound scary: I could interpret them
>> > as meaning I cannot trust the locations at all.  If that is indeed so,
>> > what use are they?
>>
>> that's already discussed above, but I don't want you to be scared, so
>> let me explain what I meant:
>> it's not "at any moment", but rather "after any call to GDB's Python
>> interface". We may want to say that it's only breakpoint or
>> execution-related calls, but _I_ can't ensure that this is true, and
>> it 'might' change in the future:
>>
>> > A @code{gdb.BpLocation} object may be invalidated during
>> > any call to @{GDB}'s API for internal reasons (for instance, but not limited to,
>> > breakpoint or execution-related mechanisms).
>
> Sounds okay to me.  But you don't need "but not limited to", because
> "for instance" already says that.
>
>> +Return a tuple containing a sequence of @code{gdb.BpLocation} objects
>> +(see below) associated with this breakpoint.  A breakpoint with no location
>> +is a pending breakpoint (@xref{Set Breaks, , pending breakpoints}).
>                            ^^^^^
> Still an @xref...

sorry, I missed it in the first run ...

>> +any call to @{GDB}'s API for internal reasons (for instance, but not limited to,
>               ^^^^^^
> You already know what to fix here...
>
> The documentation parts are OK with those changes.

Thanks, I've fixed these parts

-

I've also improved watchpoint handling by adding a regression test
(nothing fancy so far, I just ensure that the location object has the
right properties. I couldn't find in the documentation any corner
cases that worth being tested). I also adapted the BpLocation.address
attribute to cast the address to the watchpoint type (value_type
(wp->val)) if relevant.

Let me know if there are more situations where WP handling can differ
from BP handling, regarding their locations at least.


Cordially,

Kevin

Tested with no regression on f15/x64

--

2012-01-11  Kevin Pouget  <kevin.pouget@st.com>

	Add bp_location to Python interface
	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-bploc.o
	(SUBDIR_PYTHON_SRCS): Add python/py-bploc.c
	Add build rule for this file.
	* breakpoint.h (struct bploc_object): Forward declaration.
	(struct bp_location): Add py_bploc_obj.
	* breakpoint.c (free_bp_location): Call gdbpy_bplocation_free.
	* python/py-bploc.c: New file.
	* python/py-breakpoint.c (bppy_locations): New function.
	(breakpoint_object_methods): New method binding: locations().
	* python/python-internal.h (bploc_object): New typedef.
	(bplocation_to_bplocation_object): New prototype.
	(gdbpy_initialize_bplocation): Likewise.
	* python/python.c (gdbpy_bplocation_free): New empty stub.
	(_initialize_python): Call gdbpy_initialize_bplocation.
	* python/python.h (gdbpy_bplocation_free): New prototype.
	
doc/
	* gdb.texinfo (Breakpoints In Python): Document
	gdb.Breakpoint.locations and gdb.BpLocation.


testsuite/
	* gdb.python/py-breakpoint.exp: Test gdb.BpLocation.

[-- Attachment #2: 0001-Add-gdb.BpLocation-to-Python-interface.patch --]
[-- Type: text/x-patch, Size: 24437 bytes --]

From a38ff471794d544871770008489059a5fdf74c9f Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Wed, 18 May 2011 10:02:23 -0400
Subject: [PATCH] Add gdb.BpLocation to Python interface

---
 gdb/Makefile.in                            |    6 +
 gdb/NEWS                                   |    6 +
 gdb/breakpoint.c                           |    3 +
 gdb/breakpoint.h                           |    6 +
 gdb/doc/gdb.texinfo                        |   51 +++++-
 gdb/python/py-bploc.c                      |  312 ++++++++++++++++++++++++++++
 gdb/python/py-breakpoint.c                 |   39 ++++
 gdb/python/python-internal.h               |    6 +
 gdb/python/python.c                        |    7 +
 gdb/python/python.h                        |    2 +
 gdb/testsuite/gdb.python/py-breakpoint.exp |   57 +++++
 11 files changed, 493 insertions(+), 2 deletions(-)
 create mode 100644 gdb/python/py-bploc.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 25067f1..7c93f65 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -273,6 +273,7 @@ SUBDIR_PYTHON_OBS = \
 	py-auto-load.o \
 	py-block.o \
 	py-bpevent.o \
+	py-bploc.o \
 	py-breakpoint.o \
 	py-cmd.o \
 	py-continueevent.o \
@@ -305,6 +306,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-auto-load.c \
 	python/py-block.c \
 	python/py-bpevent.c \
+	python/py-bploc.c \
 	python/py-breakpoint.c \
 	python/py-cmd.c \
 	python/py-continueevent.c \
@@ -2031,6 +2033,10 @@ py-bpevent.o: $(srcdir)/python/py-bpevent.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bpevent.c
 	$(POSTCOMPILE)
 
+py-bploc.o: $(srcdir)/python/py-bploc.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bploc.c
+	$(POSTCOMPILE)
+
 py-breakpoint.o: $(srcdir)/python/py-breakpoint.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-breakpoint.c
 	$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index a9a7859..c529f96 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9,6 +9,12 @@
 * The binary "gdbtui" can no longer be built or installed.
   Use "gdb -tui" instead.
 
+* Python scripting
+
+  ** A new method "gdb.Breakpoint.locations" has been added, as well as
+     the class gdb.BpLocation to provide further details about breakpoint
+     locations.
+
 *** Changes in GDB 7.4
 
 * GDB now handles ambiguous linespecs more consistently; the existing
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 7b8d10f..28b7579 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5678,6 +5678,9 @@ static void
 free_bp_location (struct bp_location *loc)
 {
   loc->ops->dtor (loc);
+
+  gdbpy_bplocation_free (loc);
+
   xfree (loc);
 }
 
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index c1d3be9..8d6fa19 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -26,6 +26,7 @@
 struct value;
 struct block;
 struct breakpoint_object;
+struct bploc_object;
 struct get_number_or_range_state;
 struct thread_info;
 struct bpstats;
@@ -403,6 +404,11 @@ struct bp_location
   /* Source file name of this address.  */
 
   char *source_file;
+
+  /* Python object associated with this location.  May be NULL if the location
+     is not yet exported to Python.  */
+
+  struct bploc_object *py_bploc_obj;
 };
 
 /* This structure is a collection of function pointers that, if available,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2f4aa4f..8916140 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24358,6 +24358,53 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@findex gdb.locations
+@defun gdb.locations ()
+Return a tuple containing a sequence of @code{gdb.BpLocation} objects 
+(see below) associated with this breakpoint.  A breakpoint with no location
+is a pending breakpoint (@xref{Set Breaks, , pending breakpoints}).
+@end defun
+
+A breakpoint location is represented by a @code{gdb.BpLocation} object,
+which offers the following attributes (all read only) and methods.
+Please note that breakpoint location objects are very volatile entities in
+@value{GDBN}, so one should avoid keeping references to them.  They can be 
+destructed and recreated on any breakpoint creation and deletion, shared-
+library event, inferior function call, @dots{} 
+
+@defvar BpLocation.owner
+This attribute holds a reference to the @code{gdb.Breakpoint} object which
+owns this location.  This attribute is not writable.  From an implementation 
+point of view, there is a one-to-many relation between a breakpoint and
+its locations, even if two breakpoints are set at same address.
+ 
+@end defvar
+
+@defvar BpLocation.enabled
+This attribute indicates whether this location is currently enabled or not.
+This attribute is not writable.
+@end defvar
+
+@defvar BpLocation.inferior
+This attribute holds a reference to the @code{gdb.Inferior} object
+in which this breakpoint location has been inserted.  The value will be 
+@code{None} if there is no inferior associated with this location.   This 
+attribute is not writable.
+@end defvar
+
+@defvar BpLocation.address
+This attribute holds a @code{gdb.Value} object corresponding to the address 
+at which the breakpoint has been inserted.   This attribute is not writable.
+@end defvar
+
+@defun BpLocation.is_valid ()
+Returns @code{True} if the @code{gdb.BpLocation} object is valid,
+@code{False} if not.  A @code{gdb.BpLocation} object may be invalidated during
+any call to @value{GDB}'s API for internal reasons (for instance, breakpoint 
+or execution related mechanisms).  All other @code{gdb.BpLocation} methods
+and attributes will throw an exception if the object is invalid.
+@end defun
+
 @node Finish Breakpoints in Python
 @subsubsection Finish Breakpoints
 
@@ -24371,12 +24418,12 @@ 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 
+become invisible to the user.  @pxref{Breakpoints In Python}, for further 
 details about this argument.
 @end defun
 
diff --git a/gdb/python/py-bploc.c b/gdb/python/py-bploc.c
new file mode 100644
index 0000000..beab926
--- /dev/null
+++ b/gdb/python/py-bploc.c
@@ -0,0 +1,312 @@
+/* Python interface to breakpoint locations.
+
+   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 "inferior.h"
+#include "python-internal.h"
+#include "observer.h"
+#include "gdbarch.h"
+
+struct bploc_object
+{
+  PyObject_HEAD
+
+  /* The location corresponding to this gdb.BpLocation object.  It's the same
+     idea as gdb.Breakpoint, if the 'backend' location is deleted, LOC is
+     set to NULL.  No reference to the location is owned here (in terms of
+     ref. counting) in order not to change the internal behavior.  */
+  struct bp_location *loc;
+
+  /* 1 if the owner BP has been deleted, 0 otherwise.  */
+  int invalid_owner;
+};
+
+/* Require that LOCATION be a valid bp_location; throw a Python
+   exception if it is invalid.  */
+#define BPLOCPY_REQUIRE_VALID(Location)                                 \
+    do {                                                                \
+      if ((Location)->loc == NULL)                                      \
+        return PyErr_Format (PyExc_RuntimeError,                        \
+                             _("BpLocation invalid."));                 \
+    } while (0)
+
+static PyTypeObject bploc_object_type;
+
+/* Call by free_bp_location when loc is about to be freed.  */
+
+void
+gdbpy_bplocation_free (struct bp_location *loc)
+{
+  if (loc->py_bploc_obj)
+    {
+      loc->py_bploc_obj->loc = NULL;
+      Py_DECREF (loc->py_bploc_obj);
+    }
+}
+
+/* Dissociate the bp_location from the Python object.  */
+
+static void
+bplocpy_dealloc (PyObject *self)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc != NULL)
+    self_bploc->loc->py_bploc_obj = NULL;
+
+  self->ob_type->tp_free (self);
+}
+
+/* Create or acquire a ref to the bp_location object (gdb.BpLocation)
+   that encapsulates the struct bp_location from GDB.  */
+
+PyObject *
+bplocation_to_bplocation_object (struct bp_location *loc)
+{
+  bploc_object *bploc_obj;
+
+  gdb_assert (loc != NULL);
+  if (loc->py_bploc_obj)
+    {
+      Py_INCREF (loc->py_bploc_obj);
+      return (PyObject *) loc->py_bploc_obj;
+    }
+
+  bploc_obj = PyObject_New (bploc_object, &bploc_object_type);
+  if (!bploc_obj)
+    return NULL;
+
+  bploc_obj->loc = loc;
+  bploc_obj->invalid_owner = 0;
+  Py_INCREF (bploc_obj);
+  loc->py_bploc_obj = bploc_obj;
+
+  return (PyObject *) bploc_obj;
+}
+
+/* Python function to get the BP owning this location, if any.  */
+
+static PyObject *
+bplocpy_get_owner (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->invalid_owner)
+    Py_RETURN_NONE;
+
+  if (self_bploc->loc->owner
+      && self_bploc->loc->owner->py_bp_object)
+    {
+      Py_INCREF (self_bploc->loc->owner->py_bp_object);
+      return (PyObject *) self_bploc->loc->owner->py_bp_object;
+    }
+
+  Py_RETURN_NONE;
+}
+
+/* Python function to test whether or not this breakpoint location is
+   enabled.  */
+
+static PyObject *
+bplocpy_get_enabled (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->loc->enabled)
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Python function to get the address of this breakpoint location.  The
+   gdb.Value object will be cached if this is the first access.  Returns
+   NULL in case of failure, with a Python exception set.  */
+
+static PyObject *
+bplocpy_get_address (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+  volatile struct gdb_exception except;
+  struct type *val_type = NULL; /* Initialize to appease gcc warning.  */
+  struct value *val = NULL; /* Initialize to appease gcc warning.  */
+  PyObject *py_address;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  /* Get the address Value object as a void * value.  */
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (is_watchpoint (self_bploc->loc->owner))
+        {
+          struct watchpoint *wp = (struct watchpoint *) self_bploc->loc->owner;
+
+          if (wp->val)
+            val_type = value_type (wp->val);
+        }
+
+      if (!val_type)
+        val_type = builtin_type (python_gdbarch)->builtin_void;
+
+      val = value_from_pointer (lookup_pointer_type (val_type),
+                                self_bploc->loc->address);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  py_address = value_to_value_object (val);
+  if (!py_address)
+    return NULL;
+
+  return py_address;
+}
+
+/* Python function to get the inferior hosting this breakpoint location.
+   Return Py_None if there is no inferior associated with the program space of
+   this location, or NULL in case of failure, with a python exception set.  */
+
+static PyObject *
+bplocpy_get_inferior (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+  struct inferior *inf;
+  PyObject *infobj;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  inf = find_inferior_for_program_space (self_bploc->loc->pspace);
+  if (!inf)
+    Py_RETURN_NONE;
+
+  infobj = inferior_to_inferior_object (inf);
+  Py_XINCREF (infobj);
+
+  return infobj;
+}
+
+/* Python function which checks the validity of a bp location object.  */
+
+static PyObject *
+bplocpy_is_valid (PyObject *self, PyObject *args)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+/* Callback triggered when a breakpoint is deleted.  This will invalidate
+   the corresponding bp_location Python object owners.  */
+
+static void
+bplocpy_breakpoint_deleted (struct breakpoint *b) {
+  struct bp_location *loc;
+
+  for (loc = b->loc; loc; loc = loc->next)
+    {
+      if (loc->py_bploc_obj)
+        loc->py_bploc_obj->invalid_owner = 1;
+    }
+}
+
+/* Initialize the Python bp_location code.  */
+
+void
+gdbpy_initialize_bplocation (void)
+{
+  if (PyType_Ready (&bploc_object_type) < 0)
+    return;
+
+  Py_INCREF (&bploc_object_type);
+  if (PyModule_AddObject (gdb_module, "BpLocation",
+                          (PyObject *) &bploc_object_type) < 0)
+    return;
+
+  observer_attach_breakpoint_deleted (bplocpy_breakpoint_deleted);
+}
+
+static PyGetSetDef bploc_object_getset[] =
+{
+  { "owner", bplocpy_get_owner, NULL,
+    "Each breakpoint location must belong to exactly one higher-level \
+breakpoint.  This pointer is NULL iff this bp_location is no \
+longer attached to a breakpoint (read-only).",
+    NULL },
+  { "enabled", bplocpy_get_enabled, NULL,
+    "Is this particular location enabled.", NULL },
+  { "address", bplocpy_get_address, NULL,
+    "The address at which the breakpoint has been set.", NULL },
+  { "inferior", bplocpy_get_inferior, NULL,
+    "The inferior in which this breakpoint location has been set.", NULL },
+  { NULL }  /* Sentinel.  */
+};
+
+
+static PyMethodDef bploc_object_methods[] =
+{
+  { "is_valid", bplocpy_is_valid, METH_NOARGS,
+    "Return true if this breakpoint location is valid, false if not." },
+  { NULL } /* Sentinel.  */
+};
+
+static PyTypeObject bploc_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,                                          /* ob_size */
+  "gdb.BpLocation",                           /* tp_name */
+  sizeof (bploc_object),                      /* tp_basicsize */
+  0,                                          /* tp_itemsize */
+  bplocpy_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 breakpoint location object",           /* tp_doc */
+  0,                                          /* tp_traverse */
+  0,                                          /* tp_clear */
+  0,                                          /* tp_richcompare */
+  0,                                          /* tp_weaklistoffset */
+  0,                                          /* tp_iter */
+  0,                                          /* tp_iternext */
+  bploc_object_methods,                       /* tp_methods */
+  0,                                          /* tp_members */
+  bploc_object_getset,                        /* tp_getset */
+  0,                                          /* tp_base */
+  0,                                          /* tp_dict */
+  0,                                          /* tp_descr_get */
+  0,                                          /* tp_descr_set */
+  0,                                          /* tp_dictoffset */
+  0,                                          /* tp_init */
+  0                                           /* tp_alloc */
+};
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 195ed2b..25ef62f 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -584,6 +584,43 @@ bppy_get_ignore_count (PyObject *self, void *closure)
   return PyInt_FromLong (self_bp->bp->ignore_count);
 }
 
+
+/* Python function which returns the BpLocation objects associated
+   with this breakpoint.  */
+
+static PyObject *
+bppy_locations (PyObject *self, PyObject *args)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  PyObject *list, *tuple;
+  struct bp_location *loc;
+  int err;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  list = PyList_New (0);
+  if (!list)
+    return NULL;
+
+  err = 0;
+  for (loc = self_bp->bp->loc; loc; loc = loc->next)
+    {
+      PyObject *loc_obj =  bplocation_to_bplocation_object (loc);
+      err = PyList_Append (list, loc_obj);
+      if (err == -1)
+        {
+          Py_DECREF (list);
+          return NULL;
+        }
+      Py_DECREF (loc_obj);
+    }
+
+  tuple = PyList_AsTuple (list);
+  Py_DECREF (list);
+
+  return tuple;
+}
+
 /* Python function to create a new breakpoint.  */
 static int
 bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
@@ -975,6 +1012,8 @@ static PyMethodDef breakpoint_object_methods[] =
     "Return true if this breakpoint is valid, false if not." },
   { "delete", bppy_delete_breakpoint, METH_NOARGS,
     "Delete the underlying GDB breakpoint." },
+  { "locations", bppy_locations, METH_NOARGS,
+    "Get a list of gdb.BpLocation objects associated with this breakpoint." },
   { NULL } /* Sentinel.  */
 };
 
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 328e5d8..14608c2 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -165,6 +165,9 @@ typedef struct breakpoint_object
 extern breakpoint_object *bppy_pending_object;
 
 
+/* Defined in py-bploc.c */
+typedef struct bploc_object bploc_object;
+
 typedef struct
 {
   PyObject_HEAD
@@ -217,6 +220,8 @@ PyObject *pspy_get_printers (PyObject *, void *);
 PyObject *objfile_to_objfile_object (struct objfile *);
 PyObject *objfpy_get_printers (PyObject *, void *);
 
+PyObject *bplocation_to_bplocation_object (struct bp_location *loc);
+
 thread_object *create_thread_object (struct thread_info *tp);
 thread_object *find_thread_object (ptid_t ptid);
 PyObject *find_inferior_object (int pid);
@@ -245,6 +250,7 @@ void gdbpy_initialize_pspace (void);
 void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_breakpoints (void);
 void gdbpy_initialize_finishbreakpoints (void);
+void gdbpy_initialize_bplocation (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 5a0d776..b9580ce 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_bplocation_free (struct breakpoint_object *bp_obj)
+{
+  return;
+}
+
 #endif /* HAVE_PYTHON */
 
 \f
@@ -1244,6 +1250,7 @@ message == an error message without a stack will be printed."),
   gdbpy_initialize_objfile ();
   gdbpy_initialize_breakpoints ();
   gdbpy_initialize_finishbreakpoints ();
+  gdbpy_initialize_bplocation ();
   gdbpy_initialize_lazy_string ();
   gdbpy_initialize_thread ();
   gdbpy_initialize_inferior ();
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 9e461f7..516a94c 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_bplocation_free (struct bp_location *loc);
+
 #endif /* GDB_PYTHON_H */
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index 8ed3742..e865603 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -182,6 +182,11 @@ if ![runto_main] then {
 }
 
 gdb_py_test_silent_cmd  "python wp1 = gdb.Breakpoint (\"result\", type=gdb.BP_WATCHPOINT, wp_class=gdb.WP_WRITE )" "Set watchpoint" 0
+
+gdb_test "python print len(wp1.locations()) == 1" "True" "check watchpoint location"
+gdb_test "python print wp1.locations()\[0\].inferior.num" "1" "check watchpoint location's inferior"
+gdb_test "python print wp1.locations()\[0\].address == gdb.parse_and_eval(\"&result\")" "True" "check watchpoint location's address"
+
 gdb_test "continue" ".*\[Ww\]atchpoint.*result.*Old value = 0.*New value = 25.*main.*" "Test watchpoint write"
 
 # Internal breakpoints.
@@ -304,3 +309,55 @@ gdb_py_test_silent_cmd  "python wp1 = wp_eval (\"result\", type=gdb.BP_WATCHPOIN
 gdb_test "continue" ".*\[Ww\]atchpoint.*result.*Old value =.*New value = 788.*" "Test watchpoint write"
 gdb_test "python print never_eval_bp1.count" "0" \
     "Check that this unrelated breakpoints eval function was never called."
+
+# gdb.BpLocation
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+delete_breakpoints
+gdb_test_no_output "set detach-on-fork off" "don't detach on fork"
+gdb_test "call fork()" "New process .*" "create a second inferior"
+
+gdb_breakpoint "main"
+gdb_test "py print len(gdb.breakpoints())" "1" "ensure that threre is only one BP"
+gdb_test_no_output {py bp0 = gdb.breakpoints()[0]} "save breakpoint 0"
+gdb_test "py print len(bp0.locations())" "2" "ensure that threre are 2 locations"
+
+gdb_test_no_output {py loc0 = bp0.locations()[0]} "save location 0"
+gdb_test_no_output {py loc1 = bp0.locations()[1]} "save location 1"
+
+gdb_test "py print loc0.owner == loc1.owner == bp0" "True" "verify ownership"
+gdb_test "py print loc0.address == loc1.address " "True" "verify addresses are identical"
+# how to check address location ? != address(main)
+
+gdb_test {py print loc0.inferior == gdb.inferiors()[0]} "True" "verify inferior for loc 0" #inf 2
+gdb_test {py print loc1.inferior == gdb.inferiors()[1]} "True" "verify inferior for loc 1" #inf 1
+
+gdb_test "py print loc0.enabled == loc1.enabled == True" "True" "verify that locations are enabled"
+
+gdb_test "py print loc0.inferior.num" "2" "ensure that loc0 is on inferior 2"
+
+gdb_test "detach inferior 2" "Detaching from program:.*" "detach inferior 2"
+gdb_test "inferior 1" "Switching to inferior .*" "switch to inferior 1"
+gdb_test_no_output "remove-inferiors 2" "remove inferior 2"
+gdb_test "py print loc0.inferior" "None" "removed inferior set to None"
+
+delete_breakpoints
+gdb_test "py print bp0.is_valid()" "False" "verify that BP has been invalidated"
+gdb_test "py bp0.locations()" ".*RuntimeError: Breakpoint .* is invalid.*"\
+         "verify that locations can't accessed on an invalid breakpoint"
+         
+gdb_test "py print loc0.is_valid()" "False" "verify that location is invalid"
+gdb_test "py print loc0.owner" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that owner can't be accessed"
+gdb_test "py print loc0.enabled" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.address" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.inferior" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that inferior can't be accessed"
-- 
1.7.6.5


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

* Re: [PATCH] Add bp_location to Python interface
  2012-01-11 10:16                 ` Kevin Pouget
@ 2012-01-11 10:27                   ` Eli Zaretskii
  0 siblings, 0 replies; 25+ messages in thread
From: Eli Zaretskii @ 2012-01-11 10:27 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches, pmuldoon

> From: Kevin Pouget <kevin.pouget@gmail.com>
> Date: Wed, 11 Jan 2012 10:47:55 +0100
> Cc: gdb-patches@sourceware.org, pmuldoon@redhat.com
> 
> >> +Return a tuple containing a sequence of @code{gdb.BpLocation} objects
> >> +(see below) associated with this breakpoint.  A breakpoint with no location
> >> +is a pending breakpoint (@xref{Set Breaks, , pending breakpoints}).
> >                            ^^^^^
> > Still an @xref...
> 
> sorry, I missed it in the first run ...

See below, it sounds like I totally failed to explain myself in this
matter.

> +@defun gdb.locations ()
> +Return a tuple containing a sequence of @code{gdb.BpLocation} objects 
> +(see below) associated with this breakpoint.  A breakpoint with no location
> +is a pending breakpoint (@xref{Set Breaks, , pending breakpoints}).

This @xref should become @pxref, because the cross reference is in
parentheses (that's the `p' part of `pxref') and is not a complete
sentence.

The other @xref that you changed to @pxref should be left alone.

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

This should be @xref, as it is not in parentheses and is a complete
sentence.

Thanks, and sorry for my evidently unclear wording.

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

* Re: [PATCH] Add bp_location to Python interface
  2012-01-11  9:05                 ` Kevin Pouget
@ 2012-01-11 19:45                   ` Doug Evans
  2012-01-27 13:04                     ` Kevin Pouget
  0 siblings, 1 reply; 25+ messages in thread
From: Doug Evans @ 2012-01-11 19:45 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches, pmuldoon, Eli Zaretskii

On Jan 11, 2012 1:02 AM, "Kevin Pouget" <kevin.pouget@gmail.com> wrote:
>
> I'm not sure we can really move it away from the Python/Guile API,
> because, AFAIU, this volatile aspect if purely internal. 'Normal'
> users won't ever see it.
>
> > $ i b
> > Num     Type           Disp Enb Address            What
> > 1       breakpoint     keep y    <MULTIPLE>
> >         1.1                         y     0x4562c0 in main at ../../../git/gdb/gdb/gdb.c:26 inf 2
> >         1.2                         y     0x4562c0 in main at ../../../git/gdb/gdb/gdb.c:26 inf 1
>
> will remain the same all the time, but the objects (both in Python and
> their GDB backends) in gdb.breakpoints()[0].locations() will be
> deleted and re-created at various moments (cf. previous discussions).
> So far, I couldn't really understand what's the rational behind that
> ...
>
> [I'm not sure how well you know this part of GDB, please don't
> hesitate to correct me if I'm wrong]

Yikes, missed that.
GDB internals are free to change at will, the python API is the exact opposite.
This is the kind of API addition that gives me pause.
Once it's in we're stuck.

I once wrote a doc on managing GDB features/APIs, loosely based on
what I could remember of the SystemV ABI.
The ABI elements had three stages in their life, though I don't fully
remember the details.
IMO, IWBN to have something like it for our Python API.
In particular, one could remove something, after a preset amount of
time had passed since the announcement of its pending removal.
And one could add new things without promising they'll be there forever.
That scheme is old, and my memory is sketchy.
My point is that today we don't have anything other than: "Once it's
in we're stuck." [AFAICT]

For the case at hand, IWBN to have some experience with whether this
addition works well enough that we want to keep it.
And it's not clear we can determine that without some real world use of it.

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

* Re: [PATCH] Add bp_location to Python interface
  2012-01-11 19:45                   ` Doug Evans
@ 2012-01-27 13:04                     ` Kevin Pouget
  2012-03-30 19:51                       ` Tom Tromey
  0 siblings, 1 reply; 25+ messages in thread
From: Kevin Pouget @ 2012-01-27 13:04 UTC (permalink / raw)
  To: gdb-patches; +Cc: pmuldoon, Eli Zaretskii

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

On Wed, Jan 11, 2012 at 8:25 PM, Doug Evans <dje@google.com> wrote:
> On Jan 11, 2012 1:02 AM, "Kevin Pouget" <kevin.pouget@gmail.com> wrote:
>>
>> I'm not sure we can really move it away from the Python/Guile API,
>> because, AFAIU, this volatile aspect if purely internal. 'Normal'
>> users won't ever see it.
>>
>> > $ i b
>> > Num     Type           Disp Enb Address            What
>> > 1       breakpoint     keep y    <MULTIPLE>
>> >         1.1                         y     0x4562c0 in main at ../../../git/gdb/gdb/gdb.c:26 inf 2
>> >         1.2                         y     0x4562c0 in main at ../../../git/gdb/gdb/gdb.c:26 inf 1
>>
>> will remain the same all the time, but the objects (both in Python and
>> their GDB backends) in gdb.breakpoints()[0].locations() will be
>> deleted and re-created at various moments (cf. previous discussions).
>> So far, I couldn't really understand what's the rational behind that
>> ...
>>
>> [I'm not sure how well you know this part of GDB, please don't
>> hesitate to correct me if I'm wrong]
>
> Yikes, missed that.
> GDB internals are free to change at will, the python API is the exact opposite.
> This is the kind of API addition that gives me pause.
> Once it's in we're stuck.
>
> I once wrote a doc on managing GDB features/APIs, loosely based on
> what I could remember of the SystemV ABI.
> The ABI elements had three stages in their life, though I don't fully
> remember the details.
> IMO, IWBN to have something like it for our Python API.
> In particular, one could remove something, after a preset amount of
> time had passed since the announcement of its pending removal.
> And one could add new things without promising they'll be there forever.
> That scheme is old, and my memory is sketchy.
> My point is that today we don't have anything other than: "Once it's
> in we're stuck." [AFAICT]
>
> For the case at hand, IWBN to have some experience with whether this
> addition works well enough that we want to keep it.
> And it's not clear we can determine that without some real world use of it.

ping

I totally agree with Doug's discussion, but I'll let people with more
experience decide about the policy regarding Python API.


I've refreshed my patch against the current HEAD


Cordially,

Kevin

2012-01-27  Kevin Pouget  <kevin.pouget@st.com>

	Add bp_location to Python interface
	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-bploc.o
	(SUBDIR_PYTHON_SRCS): Add python/py-bploc.c
	Add build rule for this file.
	* breakpoint.h (struct bploc_object): Forward declaration.
	(struct bp_location): Add py_bploc_obj.
	* breakpoint.c (free_bp_location): Call gdbpy_bplocation_free.
	* python/py-bploc.c: New file.
	* python/py-breakpoint.c (bppy_locations): New function.
	(breakpoint_object_methods): New method binding: locations().
	* python/python-internal.h (bploc_object): New typedef.
	(bplocation_to_bplocation_object): New prototype.
	(gdbpy_initialize_bplocation): Likewise.
	* python/python.c (gdbpy_bplocation_free): New empty stub.
	(_initialize_python): Call gdbpy_initialize_bplocation.
	* python/python.h (gdbpy_bplocation_free): New prototype.
	
doc/
	Add bp_location to Python interface
	* gdb.texinfo (Breakpoints In Python): Document
	gdb.Breakpoint.locations and gdb.BpLocation.


testsuite/
	Add bp_location to Python interface
	* gdb.python/py-breakpoint.exp: Test gdb.BpLocation.

[-- Attachment #2: 0001-Add-gdb.BpLocation-to-Python-interface.patch --]
[-- Type: text/x-patch, Size: 23692 bytes --]

From 5a232f47c0f1a1c0d03010cfc40e5f485624a34e Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Wed, 18 May 2011 10:02:23 -0400
Subject: [PATCH] Add gdb.BpLocation to Python interface

---
 gdb/Makefile.in                            |    6 +
 gdb/NEWS                                   |    3 +
 gdb/breakpoint.c                           |    3 +
 gdb/breakpoint.h                           |    6 +
 gdb/doc/gdb.texinfo                        |   47 ++++
 gdb/python/py-bploc.c                      |  312 ++++++++++++++++++++++++++++
 gdb/python/py-breakpoint.c                 |   39 ++++
 gdb/python/python-internal.h               |    6 +
 gdb/python/python.c                        |    7 +
 gdb/python/python.h                        |    2 +
 gdb/testsuite/gdb.python/py-breakpoint.exp |   57 +++++
 11 files changed, 488 insertions(+), 0 deletions(-)
 create mode 100644 gdb/python/py-bploc.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 38c93c9..6b9fcea 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -269,6 +269,7 @@ SUBDIR_PYTHON_OBS = \
 	py-auto-load.o \
 	py-block.o \
 	py-bpevent.o \
+	py-bploc.o \
 	py-breakpoint.o \
 	py-cmd.o \
 	py-continueevent.o \
@@ -301,6 +302,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-auto-load.c \
 	python/py-block.c \
 	python/py-bpevent.c \
+	python/py-bploc.c \
 	python/py-breakpoint.c \
 	python/py-cmd.c \
 	python/py-continueevent.c \
@@ -2022,6 +2024,10 @@ py-bpevent.o: $(srcdir)/python/py-bpevent.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bpevent.c
 	$(POSTCOMPILE)
 
+py-bploc.o: $(srcdir)/python/py-bploc.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bploc.c
+	$(POSTCOMPILE)
+
 py-breakpoint.o: $(srcdir)/python/py-breakpoint.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-breakpoint.c
 	$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index 4798b7b..9709cc2 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -10,6 +10,9 @@
   ** A new class, gdb.printing.FlagEnumerationPrinter, can be used to
      apply "flag enum"-style pretty-printing to any enum.
 
+  ** A new method "gdb.Breakpoint.locations" has been added, as well as
+     the class gdb.BpLocation to provide further details about breakpoint
+     locations.
 * GDBserver now supports stdio connections.
   E.g. (gdb) target remote | ssh myhost gdbserver - hello
 
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index ec7f348..639a2b5 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5771,6 +5771,9 @@ static void
 free_bp_location (struct bp_location *loc)
 {
   loc->ops->dtor (loc);
+
+  gdbpy_bplocation_free (loc);
+
   xfree (loc);
 }
 
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index aa66790..139411d 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -26,6 +26,7 @@
 struct value;
 struct block;
 struct breakpoint_object;
+struct bploc_object;
 struct get_number_or_range_state;
 struct thread_info;
 struct bpstats;
@@ -405,6 +406,11 @@ struct bp_location
   /* Source file name of this address.  */
 
   char *source_file;
+
+  /* Python object associated with this location.  May be NULL if the location
+     is not yet exported to Python.  */
+
+  struct bploc_object *py_bploc_obj;
 };
 
 /* This structure is a collection of function pointers that, if available,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index fa728db..d1dca80 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24410,6 +24410,53 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@findex gdb.locations
+@defun gdb.locations ()
+Return a tuple containing a sequence of @code{gdb.BpLocation} objects 
+(see below) associated with this breakpoint.  A breakpoint with no location
+is a pending breakpoint (@pxref{Set Breaks, , pending breakpoints}).
+@end defun
+
+A breakpoint location is represented by a @code{gdb.BpLocation} object,
+which offers the following attributes (all read only) and methods.
+Please note that breakpoint location objects are very volatile entities in
+@value{GDBN}, so one should avoid keeping references to them.  They can be 
+destructed and recreated on any breakpoint creation and deletion, shared-
+library event, inferior function call, @dots{} 
+
+@defvar BpLocation.owner
+This attribute holds a reference to the @code{gdb.Breakpoint} object which
+owns this location.  This attribute is not writable.  From an implementation 
+point of view, there is a one-to-many relation between a breakpoint and
+its locations, even if two breakpoints are set at same address.
+ 
+@end defvar
+
+@defvar BpLocation.enabled
+This attribute indicates whether this location is currently enabled or not.
+This attribute is not writable.
+@end defvar
+
+@defvar BpLocation.inferior
+This attribute holds a reference to the @code{gdb.Inferior} object
+in which this breakpoint location has been inserted.  The value will be 
+@code{None} if there is no inferior associated with this location.   This 
+attribute is not writable.
+@end defvar
+
+@defvar BpLocation.address
+This attribute holds a @code{gdb.Value} object corresponding to the address 
+at which the breakpoint has been inserted.   This attribute is not writable.
+@end defvar
+
+@defun BpLocation.is_valid ()
+Returns @code{True} if the @code{gdb.BpLocation} object is valid,
+@code{False} if not.  A @code{gdb.BpLocation} object may be invalidated during
+any call to @value{GDB}'s API for internal reasons (for instance, breakpoint 
+or execution related mechanisms).  All other @code{gdb.BpLocation} methods
+and attributes will throw an exception if the object is invalid.
+@end defun
+
 @node Finish Breakpoints in Python
 @subsubsection Finish Breakpoints
 
diff --git a/gdb/python/py-bploc.c b/gdb/python/py-bploc.c
new file mode 100644
index 0000000..beab926
--- /dev/null
+++ b/gdb/python/py-bploc.c
@@ -0,0 +1,312 @@
+/* Python interface to breakpoint locations.
+
+   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 "inferior.h"
+#include "python-internal.h"
+#include "observer.h"
+#include "gdbarch.h"
+
+struct bploc_object
+{
+  PyObject_HEAD
+
+  /* The location corresponding to this gdb.BpLocation object.  It's the same
+     idea as gdb.Breakpoint, if the 'backend' location is deleted, LOC is
+     set to NULL.  No reference to the location is owned here (in terms of
+     ref. counting) in order not to change the internal behavior.  */
+  struct bp_location *loc;
+
+  /* 1 if the owner BP has been deleted, 0 otherwise.  */
+  int invalid_owner;
+};
+
+/* Require that LOCATION be a valid bp_location; throw a Python
+   exception if it is invalid.  */
+#define BPLOCPY_REQUIRE_VALID(Location)                                 \
+    do {                                                                \
+      if ((Location)->loc == NULL)                                      \
+        return PyErr_Format (PyExc_RuntimeError,                        \
+                             _("BpLocation invalid."));                 \
+    } while (0)
+
+static PyTypeObject bploc_object_type;
+
+/* Call by free_bp_location when loc is about to be freed.  */
+
+void
+gdbpy_bplocation_free (struct bp_location *loc)
+{
+  if (loc->py_bploc_obj)
+    {
+      loc->py_bploc_obj->loc = NULL;
+      Py_DECREF (loc->py_bploc_obj);
+    }
+}
+
+/* Dissociate the bp_location from the Python object.  */
+
+static void
+bplocpy_dealloc (PyObject *self)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc != NULL)
+    self_bploc->loc->py_bploc_obj = NULL;
+
+  self->ob_type->tp_free (self);
+}
+
+/* Create or acquire a ref to the bp_location object (gdb.BpLocation)
+   that encapsulates the struct bp_location from GDB.  */
+
+PyObject *
+bplocation_to_bplocation_object (struct bp_location *loc)
+{
+  bploc_object *bploc_obj;
+
+  gdb_assert (loc != NULL);
+  if (loc->py_bploc_obj)
+    {
+      Py_INCREF (loc->py_bploc_obj);
+      return (PyObject *) loc->py_bploc_obj;
+    }
+
+  bploc_obj = PyObject_New (bploc_object, &bploc_object_type);
+  if (!bploc_obj)
+    return NULL;
+
+  bploc_obj->loc = loc;
+  bploc_obj->invalid_owner = 0;
+  Py_INCREF (bploc_obj);
+  loc->py_bploc_obj = bploc_obj;
+
+  return (PyObject *) bploc_obj;
+}
+
+/* Python function to get the BP owning this location, if any.  */
+
+static PyObject *
+bplocpy_get_owner (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->invalid_owner)
+    Py_RETURN_NONE;
+
+  if (self_bploc->loc->owner
+      && self_bploc->loc->owner->py_bp_object)
+    {
+      Py_INCREF (self_bploc->loc->owner->py_bp_object);
+      return (PyObject *) self_bploc->loc->owner->py_bp_object;
+    }
+
+  Py_RETURN_NONE;
+}
+
+/* Python function to test whether or not this breakpoint location is
+   enabled.  */
+
+static PyObject *
+bplocpy_get_enabled (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  if (self_bploc->loc->enabled)
+    Py_RETURN_TRUE;
+
+  Py_RETURN_FALSE;
+}
+
+/* Python function to get the address of this breakpoint location.  The
+   gdb.Value object will be cached if this is the first access.  Returns
+   NULL in case of failure, with a Python exception set.  */
+
+static PyObject *
+bplocpy_get_address (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+  volatile struct gdb_exception except;
+  struct type *val_type = NULL; /* Initialize to appease gcc warning.  */
+  struct value *val = NULL; /* Initialize to appease gcc warning.  */
+  PyObject *py_address;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  /* Get the address Value object as a void * value.  */
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      if (is_watchpoint (self_bploc->loc->owner))
+        {
+          struct watchpoint *wp = (struct watchpoint *) self_bploc->loc->owner;
+
+          if (wp->val)
+            val_type = value_type (wp->val);
+        }
+
+      if (!val_type)
+        val_type = builtin_type (python_gdbarch)->builtin_void;
+
+      val = value_from_pointer (lookup_pointer_type (val_type),
+                                self_bploc->loc->address);
+    }
+  GDB_PY_HANDLE_EXCEPTION (except);
+
+  py_address = value_to_value_object (val);
+  if (!py_address)
+    return NULL;
+
+  return py_address;
+}
+
+/* Python function to get the inferior hosting this breakpoint location.
+   Return Py_None if there is no inferior associated with the program space of
+   this location, or NULL in case of failure, with a python exception set.  */
+
+static PyObject *
+bplocpy_get_inferior (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+  struct inferior *inf;
+  PyObject *infobj;
+
+  BPLOCPY_REQUIRE_VALID (self_bploc);
+
+  inf = find_inferior_for_program_space (self_bploc->loc->pspace);
+  if (!inf)
+    Py_RETURN_NONE;
+
+  infobj = inferior_to_inferior_object (inf);
+  Py_XINCREF (infobj);
+
+  return infobj;
+}
+
+/* Python function which checks the validity of a bp location object.  */
+
+static PyObject *
+bplocpy_is_valid (PyObject *self, PyObject *args)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->loc)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+/* Callback triggered when a breakpoint is deleted.  This will invalidate
+   the corresponding bp_location Python object owners.  */
+
+static void
+bplocpy_breakpoint_deleted (struct breakpoint *b) {
+  struct bp_location *loc;
+
+  for (loc = b->loc; loc; loc = loc->next)
+    {
+      if (loc->py_bploc_obj)
+        loc->py_bploc_obj->invalid_owner = 1;
+    }
+}
+
+/* Initialize the Python bp_location code.  */
+
+void
+gdbpy_initialize_bplocation (void)
+{
+  if (PyType_Ready (&bploc_object_type) < 0)
+    return;
+
+  Py_INCREF (&bploc_object_type);
+  if (PyModule_AddObject (gdb_module, "BpLocation",
+                          (PyObject *) &bploc_object_type) < 0)
+    return;
+
+  observer_attach_breakpoint_deleted (bplocpy_breakpoint_deleted);
+}
+
+static PyGetSetDef bploc_object_getset[] =
+{
+  { "owner", bplocpy_get_owner, NULL,
+    "Each breakpoint location must belong to exactly one higher-level \
+breakpoint.  This pointer is NULL iff this bp_location is no \
+longer attached to a breakpoint (read-only).",
+    NULL },
+  { "enabled", bplocpy_get_enabled, NULL,
+    "Is this particular location enabled.", NULL },
+  { "address", bplocpy_get_address, NULL,
+    "The address at which the breakpoint has been set.", NULL },
+  { "inferior", bplocpy_get_inferior, NULL,
+    "The inferior in which this breakpoint location has been set.", NULL },
+  { NULL }  /* Sentinel.  */
+};
+
+
+static PyMethodDef bploc_object_methods[] =
+{
+  { "is_valid", bplocpy_is_valid, METH_NOARGS,
+    "Return true if this breakpoint location is valid, false if not." },
+  { NULL } /* Sentinel.  */
+};
+
+static PyTypeObject bploc_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,                                          /* ob_size */
+  "gdb.BpLocation",                           /* tp_name */
+  sizeof (bploc_object),                      /* tp_basicsize */
+  0,                                          /* tp_itemsize */
+  bplocpy_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 breakpoint location object",           /* tp_doc */
+  0,                                          /* tp_traverse */
+  0,                                          /* tp_clear */
+  0,                                          /* tp_richcompare */
+  0,                                          /* tp_weaklistoffset */
+  0,                                          /* tp_iter */
+  0,                                          /* tp_iternext */
+  bploc_object_methods,                       /* tp_methods */
+  0,                                          /* tp_members */
+  bploc_object_getset,                        /* tp_getset */
+  0,                                          /* tp_base */
+  0,                                          /* tp_dict */
+  0,                                          /* tp_descr_get */
+  0,                                          /* tp_descr_set */
+  0,                                          /* tp_dictoffset */
+  0,                                          /* tp_init */
+  0                                           /* tp_alloc */
+};
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 195ed2b..25ef62f 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -584,6 +584,43 @@ bppy_get_ignore_count (PyObject *self, void *closure)
   return PyInt_FromLong (self_bp->bp->ignore_count);
 }
 
+
+/* Python function which returns the BpLocation objects associated
+   with this breakpoint.  */
+
+static PyObject *
+bppy_locations (PyObject *self, PyObject *args)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  PyObject *list, *tuple;
+  struct bp_location *loc;
+  int err;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  list = PyList_New (0);
+  if (!list)
+    return NULL;
+
+  err = 0;
+  for (loc = self_bp->bp->loc; loc; loc = loc->next)
+    {
+      PyObject *loc_obj =  bplocation_to_bplocation_object (loc);
+      err = PyList_Append (list, loc_obj);
+      if (err == -1)
+        {
+          Py_DECREF (list);
+          return NULL;
+        }
+      Py_DECREF (loc_obj);
+    }
+
+  tuple = PyList_AsTuple (list);
+  Py_DECREF (list);
+
+  return tuple;
+}
+
 /* Python function to create a new breakpoint.  */
 static int
 bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
@@ -975,6 +1012,8 @@ static PyMethodDef breakpoint_object_methods[] =
     "Return true if this breakpoint is valid, false if not." },
   { "delete", bppy_delete_breakpoint, METH_NOARGS,
     "Delete the underlying GDB breakpoint." },
+  { "locations", bppy_locations, METH_NOARGS,
+    "Get a list of gdb.BpLocation objects associated with this breakpoint." },
   { NULL } /* Sentinel.  */
 };
 
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 22e6b41..b95e8c6 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -165,6 +165,9 @@ typedef struct breakpoint_object
 extern breakpoint_object *bppy_pending_object;
 
 
+/* Defined in py-bploc.c */
+typedef struct bploc_object bploc_object;
+
 typedef struct
 {
   PyObject_HEAD
@@ -217,6 +220,8 @@ PyObject *pspy_get_printers (PyObject *, void *);
 PyObject *objfile_to_objfile_object (struct objfile *);
 PyObject *objfpy_get_printers (PyObject *, void *);
 
+PyObject *bplocation_to_bplocation_object (struct bp_location *loc);
+
 thread_object *create_thread_object (struct thread_info *tp);
 thread_object *find_thread_object (ptid_t ptid);
 PyObject *find_inferior_object (int pid);
@@ -245,6 +250,7 @@ void gdbpy_initialize_pspace (void);
 void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_breakpoints (void);
 void gdbpy_initialize_finishbreakpoints (void);
+void gdbpy_initialize_bplocation (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 795dd44..213dd41 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_bplocation_free (struct breakpoint_object *bp_obj)
+{
+  return;
+}
+
 #endif /* HAVE_PYTHON */
 
 \f
@@ -1244,6 +1250,7 @@ message == an error message without a stack will be printed."),
   gdbpy_initialize_objfile ();
   gdbpy_initialize_breakpoints ();
   gdbpy_initialize_finishbreakpoints ();
+  gdbpy_initialize_bplocation ();
   gdbpy_initialize_lazy_string ();
   gdbpy_initialize_thread ();
   gdbpy_initialize_inferior ();
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 597ed2e..a061948 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_bplocation_free (struct bp_location *loc);
+
 #endif /* GDB_PYTHON_H */
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index f3d409e..9806e4a 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -178,6 +178,11 @@ if ![runto_main] then {
 }
 
 gdb_py_test_silent_cmd  "python wp1 = gdb.Breakpoint (\"result\", type=gdb.BP_WATCHPOINT, wp_class=gdb.WP_WRITE )" "Set watchpoint" 0
+
+gdb_test "python print len(wp1.locations()) == 1" "True" "check watchpoint location"
+gdb_test "python print wp1.locations()\[0\].inferior.num" "1" "check watchpoint location's inferior"
+gdb_test "python print wp1.locations()\[0\].address == gdb.parse_and_eval(\"&result\")" "True" "check watchpoint location's address"
+
 gdb_test "continue" ".*\[Ww\]atchpoint.*result.*Old value = 0.*New value = 25.*main.*" "Test watchpoint write"
 
 # Internal breakpoints.
@@ -300,3 +305,55 @@ gdb_py_test_silent_cmd  "python wp1 = wp_eval (\"result\", type=gdb.BP_WATCHPOIN
 gdb_test "continue" ".*\[Ww\]atchpoint.*result.*Old value =.*New value = 788.*" "Test watchpoint write"
 gdb_test "python print never_eval_bp1.count" "0" \
     "Check that this unrelated breakpoints eval function was never called."
+
+# gdb.BpLocation
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+delete_breakpoints
+gdb_test_no_output "set detach-on-fork off" "don't detach on fork"
+gdb_test "call fork()" "New process .*" "create a second inferior"
+
+gdb_breakpoint "main"
+gdb_test "py print len(gdb.breakpoints())" "1" "ensure that threre is only one BP"
+gdb_test_no_output {py bp0 = gdb.breakpoints()[0]} "save breakpoint 0"
+gdb_test "py print len(bp0.locations())" "2" "ensure that threre are 2 locations"
+
+gdb_test_no_output {py loc0 = bp0.locations()[0]} "save location 0"
+gdb_test_no_output {py loc1 = bp0.locations()[1]} "save location 1"
+
+gdb_test "py print loc0.owner == loc1.owner == bp0" "True" "verify ownership"
+gdb_test "py print loc0.address == loc1.address " "True" "verify addresses are identical"
+# how to check address location ? != address(main)
+
+gdb_test {py print loc0.inferior == gdb.inferiors()[0]} "True" "verify inferior for loc 0" #inf 2
+gdb_test {py print loc1.inferior == gdb.inferiors()[1]} "True" "verify inferior for loc 1" #inf 1
+
+gdb_test "py print loc0.enabled == loc1.enabled == True" "True" "verify that locations are enabled"
+
+gdb_test "py print loc0.inferior.num" "2" "ensure that loc0 is on inferior 2"
+
+gdb_test "detach inferior 2" "Detaching from program:.*" "detach inferior 2"
+gdb_test "inferior 1" "Switching to inferior .*" "switch to inferior 1"
+gdb_test_no_output "remove-inferiors 2" "remove inferior 2"
+gdb_test "py print loc0.inferior" "None" "removed inferior set to None"
+
+delete_breakpoints
+gdb_test "py print bp0.is_valid()" "False" "verify that BP has been invalidated"
+gdb_test "py bp0.locations()" ".*RuntimeError: Breakpoint .* is invalid.*"\
+         "verify that locations can't accessed on an invalid breakpoint"
+         
+gdb_test "py print loc0.is_valid()" "False" "verify that location is invalid"
+gdb_test "py print loc0.owner" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that owner can't be accessed"
+gdb_test "py print loc0.enabled" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.address" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that location can't be accessed"
+gdb_test "py print loc0.inferior" ".*RuntimeError: BpLocation invalid.*"\
+         "verify that inferior can't be accessed"
-- 
1.7.6.5


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

* Re: [PATCH] Add bp_location to Python interface
  2012-01-27 13:04                     ` Kevin Pouget
@ 2012-03-30 19:51                       ` Tom Tromey
  2012-04-03 10:35                         ` Kevin Pouget
  0 siblings, 1 reply; 25+ messages in thread
From: Tom Tromey @ 2012-03-30 19:51 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: gdb-patches, pmuldoon, Eli Zaretskii

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

Kevin> ping

I'm sorry about the long delay on this.

Kevin>  free_bp_location (struct bp_location *loc)
[...]
Kevin> +  gdbpy_bplocation_free (loc);

I wonder whether tying these wrapper objects to the bp_location is
really best.

We do take this approach for many other objects exposed to Python, but
those objects tend to be rather long-lived, and also to some extent
visible across gdb -- as opposed to breakpoint locations, which are
entirely managed by the breakpoint module.

A different approach would be to instantiate and fill in the location
objects when the 'location' method is called, and give them "copy"
semantics instead.

I'm not totally convinced either way, and I would like to know what you
think about this.

Kevin> +@findex gdb.locations
Kevin> +@defun gdb.locations ()

The "gdb." here seems incorrect.
This is a method on Breakpoint.

Kevin> +Return a tuple containing a sequence of @code{gdb.BpLocation} objects 

"tuple containing a sequence" sounds weird.  I would say just "a
sequence" and not specify that it must be a tuple; it is commonplace in
Python to operate on sequences generically, and we might as well leave
ourselves whatever leeway we reasonably can.

Kevin> +@defvar BpLocation.inferior

Locations are really tied to program spaces, not inferiors.
Offhand it seems to me that we should respect this in the API.

Kevin> +bplocpy_breakpoint_deleted (struct breakpoint *b) {

Wrong brace placement.

Kevin> +  tuple = PyList_AsTuple (list);
Kevin> +  Py_DECREF (list);

In keeping with the doc change, you could just return the list here,
rather than convert to a tuple.

Kevin> +gdb_test "py print len(gdb.breakpoints())" "1" "ensure that threre is only one BP"

Typo, "threre".
Also the line should be split, here and elsewhere in the .exp.

Kevin> +# how to check address location ? != address(main)

It is tricky due to prologues, but you could check the output of "info
symbol" on the address and see that it is "main".

Tom

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

* Re: [PATCH] Add bp_location to Python interface
  2012-03-30 19:51                       ` Tom Tromey
@ 2012-04-03 10:35                         ` Kevin Pouget
  2012-04-03 12:15                           ` Phil Muldoon
  2012-04-05 16:27                           ` Eli Zaretskii
  0 siblings, 2 replies; 25+ messages in thread
From: Kevin Pouget @ 2012-04-03 10:35 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, pmuldoon, Eli Zaretskii

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

On Fri, Mar 30, 2012 at 9:51 PM, Tom Tromey <tromey@redhat.com> wrote:
>
> >>>>> "Kevin" == Kevin Pouget <kevin.pouget@gmail.com> writes:
>
> Kevin> ping
>
> I'm sorry about the long delay on this.

thanks for the follow-up, I've been busy as well and had no time to ping it.

> Kevin>  free_bp_location (struct bp_location *loc)
> [...]
> Kevin> +  gdbpy_bplocation_free (loc);
>
> I wonder whether tying these wrapper objects to the bp_location is
> really best.
>
> We do take this approach for many other objects exposed to Python, but
> those objects tend to be rather long-lived, and also to some extent
> visible across gdb -- as opposed to breakpoint locations, which are
> entirely managed by the breakpoint module.
>
> A different approach would be to instantiate and fill in the location
> objects when the 'location' method is called, and give them "copy"
> semantics instead.
>
> I'm not totally convinced either way, and I would like to know what you
> think about this.

I think it's a good idea, it avoids the confusion of "short life"
objects which can't be saved, etc.
Now, I state in the documentation that

> Breakpoint location
> objects represent the state of the breakpoint location at the time the method
> @code{Breakpoint.locations} is called, and will not be further updated.

which sounds better than the previous version

> Kevin> +@findex gdb.locations
> Kevin> +@defun gdb.locations ()
>
> The "gdb." here seems incorrect.
> This is a method on Breakpoint.

changed

> Kevin> +Return a tuple containing a sequence of @code{gdb.BpLocation}
> objects
>
> "tuple containing a sequence" sounds weird.  I would say just "a
> sequence" and not specify that it must be a tuple; it is commonplace in
> Python to operate on sequences generically, and we might as well leave
> ourselves whatever leeway we reasonably can.

changed, the original sentence doesn't really make sense to me, "a
tuple containing" was just redundant

> Kevin> +@defvar BpLocation.inferior
>
> Locations are really tied to program spaces, not inferiors.
> Offhand it seems to me that we should respect this in the API.

I'm not convinced here; actually, I'm not sure to understand the point
of the Progspaces class, in its current state at least; and there is
no (as far as I've seen) equivalent of
"find_inferior_for_program_space" in Py, so one of the key interest of
BpLocation would be unavailable ...

> Kevin> +bplocpy_breakpoint_deleted (struct breakpoint *b) {
>
> Wrong brace placement.

fixed

> Kevin> +  tuple = PyList_AsTuple (list);
> Kevin> +  Py_DECREF (list);
>
> In keeping with the doc change, you could just return the list here,
> rather than convert to a tuple.

the idea here was that a tuple is not mutable, whereas a list is -- ()
vs. []. I've removed the tuple, although although I'm not sure when
I'm supposed to use the List or the Tuple.

> Kevin> +gdb_test "py print len(gdb.breakpoints())" "1" "ensure that threre
> is only one BP"
>
> Typo, "threre".
> Also the line should be split, here and elsewhere in the .exp.

fixed

> Kevin> +# how to check address location ? != address(main)
>
> It is tricky due to prologues, but you could check the output of "info
> symbol" on the address and see that it is "main".

I've added a test going this way, but I'm not very happy with the implementation

> gdb_test "py gdb.execute('info symbol %s' % loc1.address)"  "main .* in section \.text.*" "verify location address"
because the exact result is "main + 15" ... but I don't know if there
is any better way to do it.



there's no regression against the current git tree on x86_64/F16

2012-03-04  Kevin Pouget  <kevin.pouget@st.com>

	Add bp_location to Python interface
	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-bploc.o
	(SUBDIR_PYTHON_SRCS): Add python/py-bploc.c
	Add build rule for this file.
	* breakpoint.h (struct bploc_object): Forward declaration.
	(struct bp_location): Add py_bploc_obj.
	* python/py-bploc.c: New file.
	* python/py-breakpoint.c (bppy_locations): New function.
	(breakpoint_object_methods): New method binding: locations().
	* python/python-internal.h (bploc_object): New typedef.
	(bplocation_to_bplocation_object): New prototype.
	(gdbpy_initialize_bplocation): Likewise.
	
doc/
	Add bp_location to Python interface
	* gdb.texinfo (Breakpoints In Python): Document
	gdb.Breakpoint.locations and gdb.BpLocation.


testsuite/
	Add bp_location to Python interface
	* gdb.python/py-breakpoint.exp: Test gdb.BpLocation.

[-- Attachment #2: 0001-Add-gdb.BpLocation-to-Python-interface.patch --]
[-- Type: application/octet-stream, Size: 18784 bytes --]

From 98b63231ccbf93de89a9cb596ce9efee013ee536 Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Wed, 18 May 2011 10:02:23 -0400
Subject: [PATCH] Add gdb.BpLocation to Python interface

---
 gdb/Makefile.in                            |    6 +
 gdb/NEWS                                   |    4 +
 gdb/breakpoint.h                           |    1 +
 gdb/doc/gdb.texinfo                        |   36 ++++
 gdb/python/py-bploc.c                      |  246 ++++++++++++++++++++++++++++
 gdb/python/py-breakpoint.c                 |   36 ++++
 gdb/python/python-internal.h               |    6 +
 gdb/python/python.c                        |    1 +
 gdb/testsuite/gdb.python/py-breakpoint.exp |   54 ++++++
 9 files changed, 390 insertions(+), 0 deletions(-)
 create mode 100644 gdb/python/py-bploc.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 44d76f2..784dc76 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -269,6 +269,7 @@ SUBDIR_PYTHON_OBS = \
 	py-auto-load.o \
 	py-block.o \
 	py-bpevent.o \
+	py-bploc.o \
 	py-breakpoint.o \
 	py-cmd.o \
 	py-continueevent.o \
@@ -301,6 +302,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-auto-load.c \
 	python/py-block.c \
 	python/py-bpevent.c \
+	python/py-bploc.c \
 	python/py-breakpoint.c \
 	python/py-cmd.c \
 	python/py-continueevent.c \
@@ -2037,6 +2039,10 @@ py-bpevent.o: $(srcdir)/python/py-bpevent.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bpevent.c
 	$(POSTCOMPILE)
 
+py-bploc.o: $(srcdir)/python/py-bploc.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bploc.c
+	$(POSTCOMPILE)
+
 py-breakpoint.o: $(srcdir)/python/py-breakpoint.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-breakpoint.c
 	$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index 5885d32..f24499e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -32,6 +32,10 @@
   ** A new method 'referenced_value' on gdb.Value objects which can
      dereference pointer as well as C++ reference values.
 
+  ** A new method "gdb.Breakpoint.locations" has been added, as well as
+     the class gdb.BpLocation to provide further details about breakpoint
+     locations.
+
 * GDBserver now supports stdio connections.
   E.g. (gdb) target remote | ssh myhost gdbserver - hello
 
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index e0eeeaa..11e5cee 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -27,6 +27,7 @@
 struct value;
 struct block;
 struct breakpoint_object;
+struct bploc_object;
 struct get_number_or_range_state;
 struct thread_info;
 struct bpstats;
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 8002429..91c1e2c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24601,6 +24601,42 @@ commands, separated by newlines.  If there are no commands, this
 attribute is @code{None}.  This attribute is not writable.
 @end defvar
 
+@findex Breakpoint.locations
+@defun Breakpoint.locations ()
+Return a sequence containing the @code{gdb.BpLocation} objects 
+(see below) associated with this breakpoint.  A breakpoint with no location
+is a pending breakpoint (@pxref{Set Breaks, , pending breakpoints}).
+@end defun
+
+A breakpoint location is represented by a @code{gdb.BpLocation} object,
+which offers the following attributes (all read only). Breakpoint location 
+objects represent the state of the breakpoint location at the time the method
+@code{Breakpoint.locations} is called, and will not be further updated.
+
+@defvar BpLocation.owner
+This attribute holds a reference to the @code{gdb.Breakpoint} object which
+owns this location.  This attribute is not writable.  From an implementation 
+point of view, there is a one-to-many relation between a breakpoint and
+its locations, even if two breakpoints are set at same address.
+@end defvar
+
+@defvar BpLocation.enabled
+This attribute indicates whether this location is currently enabled or not.
+This attribute is not writable.
+@end defvar
+
+@defvar BpLocation.inferior
+This attribute holds a reference to the @code{gdb.Inferior} object
+in which this breakpoint location has been inserted.  The value will be 
+@code{None} if there is no inferior associated with this location.   This 
+attribute is not writable.
+@end defvar
+
+@defvar BpLocation.address
+This attribute holds a @code{gdb.Value} object corresponding to the address 
+at which the breakpoint has been inserted.   This attribute is not writable.
+@end defvar
+
 @node Finish Breakpoints in Python
 @subsubsection Finish Breakpoints
 
diff --git a/gdb/python/py-bploc.c b/gdb/python/py-bploc.c
new file mode 100644
index 0000000..fdf5b05
--- /dev/null
+++ b/gdb/python/py-bploc.c
@@ -0,0 +1,246 @@
+/* Python interface to breakpoint locations.
+
+   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 "inferior.h"
+#include "python-internal.h"
+#include "observer.h"
+#include "gdbarch.h"
+
+struct bploc_object
+{
+  PyObject_HEAD
+
+  PyObject *inferior;
+  PyObject *owner;
+  int enabled;
+  PyObject *addr;
+};
+
+static PyTypeObject bploc_object_type;
+
+/* Dissociate the bp_location from the Python object.  */
+
+static void
+bplocpy_dealloc (PyObject *self)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  Py_DECREF (self_bploc->inferior);
+  Py_DECREF (self_bploc->owner);
+  Py_DECREF (self_bploc->addr);
+
+  self->ob_type->tp_free (self);
+}
+
+/* Returns a bp_location object (gdb.BpLocation) holding the information of
+   LOC from GDB, or NULL in case of failure, with a Python exception set.  */
+
+PyObject *
+bplocation_to_bplocation_object (struct bp_location *loc)
+{
+  bploc_object *bploc_obj;
+  struct inferior *inferior;
+  volatile struct gdb_exception except;
+  struct value *addr = NULL; /* Initialize to appease gcc warning.  */
+
+  bploc_obj = PyObject_New (bploc_object, &bploc_object_type);
+  if (!bploc_obj)
+    return NULL;
+
+  bploc_obj->enabled = loc->enabled;
+
+  inferior = find_inferior_for_program_space (loc->pspace);
+  if (inferior)
+    bploc_obj->inferior = inferior_to_inferior_object (inferior);
+  else
+    bploc_obj->inferior = Py_None;
+
+  if (!bploc_obj->inferior)
+      return NULL;
+  Py_INCREF (bploc_obj->inferior);
+
+  /* Cannot fail, triggered from Breakpoint.locations.  */
+  if (loc->owner && loc->owner->py_bp_object)
+    bploc_obj->owner = (PyObject *) loc->owner->py_bp_object;
+  else
+    bploc_obj->owner = Py_None;
+  Py_INCREF (bploc_obj->owner);
+
+  /* Get the address Value object.  */
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      struct type *val_type = NULL; /* Initialize to appease gcc warning.  */
+
+      if (is_watchpoint (loc->owner))
+        {
+          struct watchpoint *wp = (struct watchpoint *) loc->owner;
+
+          if (wp->val)
+            val_type = value_type (wp->val);
+        }
+
+      if (!val_type)
+        val_type = builtin_type (python_gdbarch)->builtin_void;
+
+      addr = value_from_pointer (lookup_pointer_type (val_type),
+                                loc->address);
+    }
+  if (except.reason < 0)
+    {
+      gdbpy_convert_exception (except);
+      goto fail;
+    }
+
+  bploc_obj->addr = value_to_value_object (addr);
+  if (!bploc_obj->addr)
+    goto fail;
+
+  Py_INCREF (bploc_obj->addr);
+
+  return (PyObject *) bploc_obj;
+
+fail:
+  Py_XDECREF (bploc_obj->inferior);
+  Py_XDECREF (bploc_obj->owner);
+  return NULL;
+}
+
+/* Python function to get the BP owning this location.  */
+
+static PyObject *
+bplocpy_get_owner (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  Py_INCREF (self_bploc->owner);
+
+  return self_bploc->owner;
+}
+
+/* Python function to test whether or not this breakpoint location is
+   enabled.  */
+
+static PyObject *
+bplocpy_get_enabled (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  if (self_bploc->enabled)
+    Py_RETURN_TRUE;
+  else
+    Py_RETURN_FALSE;
+}
+
+/* Python function to get the address of this breakpoint location.  The
+   gdb.Value object will be cached if this is the first access.  */
+
+static PyObject *
+bplocpy_get_address (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  Py_INCREF (self_bploc->addr);
+
+  return self_bploc->addr;
+}
+
+/* Python function to get the inferior hosting this breakpoint location.
+   Return Py_None if there is no inferior associated with the program space of
+   this location.  */
+
+static PyObject *
+bplocpy_get_inferior (PyObject *self, void *closure)
+{
+  bploc_object *self_bploc = (bploc_object *) self;
+
+  Py_INCREF (self_bploc->inferior);
+
+  return self_bploc->inferior;
+}
+
+/* Initialize the Python bp_location code.  */
+
+void
+gdbpy_initialize_bplocation (void)
+{
+  if (PyType_Ready (&bploc_object_type) < 0)
+    return;
+
+  Py_INCREF (&bploc_object_type);
+  PyModule_AddObject (gdb_module, "BpLocation", (PyObject *) &bploc_object_type);
+}
+
+static PyGetSetDef bploc_object_getset[] =
+{
+  { "owner", bplocpy_get_owner, NULL,
+    "Each breakpoint location must belong to exactly one higher-level \
+breakpoint.",
+    NULL },
+  { "enabled", bplocpy_get_enabled, NULL,
+    "Is this particular location enabled.", NULL },
+  { "address", bplocpy_get_address, NULL,
+    "The address at which the breakpoint has been set.", NULL },
+  { "inferior", bplocpy_get_inferior, NULL,
+    "The inferior in which this breakpoint location has been set.", NULL },
+  { NULL }  /* Sentinel.  */
+};
+
+static PyTypeObject bploc_object_type =
+{
+  PyObject_HEAD_INIT (NULL)
+  0,                                          /* ob_size */
+  "gdb.BpLocation",                           /* tp_name */
+  sizeof (bploc_object),                      /* tp_basicsize */
+  0,                                          /* tp_itemsize */
+  bplocpy_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 breakpoint location 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 */
+  bploc_object_getset,                        /* tp_getset */
+  0,                                          /* tp_base */
+  0,                                          /* tp_dict */
+  0,                                          /* tp_descr_get */
+  0,                                          /* tp_descr_set */
+  0,                                          /* tp_dictoffset */
+  0,                                          /* tp_init */
+  0                                           /* tp_alloc */
+};
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index b2c625f..3da5703 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -585,6 +585,40 @@ bppy_get_ignore_count (PyObject *self, void *closure)
   return PyInt_FromLong (self_bp->bp->ignore_count);
 }
 
+
+/* Python function which returns the BpLocation objects associated
+   with this breakpoint.  */
+
+static PyObject *
+bppy_locations (PyObject *self, PyObject *args)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  PyObject *list;
+  struct bp_location *loc;
+  int err;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  list = PyList_New (0);
+  if (!list)
+    return NULL;
+
+  err = 0;
+  for (loc = self_bp->bp->loc; loc; loc = loc->next)
+    {
+      PyObject *loc_obj =  bplocation_to_bplocation_object (loc);
+      err = PyList_Append (list, loc_obj);
+      if (err == -1)
+        {
+          Py_DECREF (list);
+          return NULL;
+        }
+      Py_DECREF (loc_obj);
+    }
+
+  return list;
+}
+
 /* Python function to create a new breakpoint.  */
 static int
 bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
@@ -976,6 +1010,8 @@ static PyMethodDef breakpoint_object_methods[] =
     "Return true if this breakpoint is valid, false if not." },
   { "delete", bppy_delete_breakpoint, METH_NOARGS,
     "Delete the underlying GDB breakpoint." },
+  { "locations", bppy_locations, METH_NOARGS,
+    "Get a list of gdb.BpLocation objects associated with this breakpoint." },
   { NULL } /* Sentinel.  */
 };
 
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index bae61c2..e852bce 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -166,6 +166,9 @@ typedef struct breakpoint_object
 extern breakpoint_object *bppy_pending_object;
 
 
+/* Defined in py-bploc.c */
+typedef struct bploc_object bploc_object;
+
 typedef struct
 {
   PyObject_HEAD
@@ -218,6 +221,8 @@ PyObject *pspy_get_printers (PyObject *, void *);
 PyObject *objfile_to_objfile_object (struct objfile *);
 PyObject *objfpy_get_printers (PyObject *, void *);
 
+PyObject *bplocation_to_bplocation_object (struct bp_location *loc);
+
 thread_object *create_thread_object (struct thread_info *tp);
 thread_object *find_thread_object (ptid_t ptid);
 PyObject *find_inferior_object (int pid);
@@ -246,6 +251,7 @@ void gdbpy_initialize_pspace (void);
 void gdbpy_initialize_objfile (void);
 void gdbpy_initialize_breakpoints (void);
 void gdbpy_initialize_finishbreakpoints (void);
+void gdbpy_initialize_bplocation (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 938275a..df9deee 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1257,6 +1257,7 @@ message == an error message without a stack will be printed."),
   gdbpy_initialize_objfile ();
   gdbpy_initialize_breakpoints ();
   gdbpy_initialize_finishbreakpoints ();
+  gdbpy_initialize_bplocation ();
   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 f3d409e..f6744e5 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -178,6 +178,15 @@ if ![runto_main] then {
 }
 
 gdb_py_test_silent_cmd  "python wp1 = gdb.Breakpoint (\"result\", type=gdb.BP_WATCHPOINT, wp_class=gdb.WP_WRITE )" "Set watchpoint" 0
+
+gdb_test "python print len(wp1.locations()) == 1" "True" \
+         "check watchpoint location"
+gdb_test "python print wp1.locations()\[0\].inferior.num" "1" \
+         "check watchpoint location's inferior"
+gdb_test "python print wp1.locations()\[0\].address    \
+            == gdb.parse_and_eval(\"&result\")" "True" \
+          "check watchpoint location's address"
+
 gdb_test "continue" ".*\[Ww\]atchpoint.*result.*Old value = 0.*New value = 25.*main.*" "Test watchpoint write"
 
 # Internal breakpoints.
@@ -300,3 +309,48 @@ gdb_py_test_silent_cmd  "python wp1 = wp_eval (\"result\", type=gdb.BP_WATCHPOIN
 gdb_test "continue" ".*\[Ww\]atchpoint.*result.*Old value =.*New value = 788.*" "Test watchpoint write"
 gdb_test "python print never_eval_bp1.count" "0" \
     "Check that this unrelated breakpoints eval function was never called."
+
+# gdb.BpLocation
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+delete_breakpoints
+gdb_test_no_output "set detach-on-fork off" "don't detach on fork"
+gdb_test "call fork()" "New process .*" "create a second inferior"
+
+gdb_breakpoint "main"
+gdb_test "py print len(gdb.breakpoints())" "1" \
+         "ensure that there is only one BP"
+gdb_test_no_output {py bp0 = gdb.breakpoints()[0]} "save breakpoint 0"
+gdb_test "py print len(bp0.locations())" "2" \
+         "ensure that threre are 2 locations"
+
+gdb_test_no_output {py loc0 = bp0.locations()[0]} "save location 0"
+gdb_test_no_output {py loc1 = bp0.locations()[1]} "save location 1"
+
+gdb_test "py print loc0.owner == loc1.owner == bp0" "True" "verify ownership"
+gdb_test "py print loc0.address == loc1.address " "True" \
+         "verify addresses are identical"
+gdb_test "py gdb.execute('info symbol %s' % loc1.address)" \
+         "main .* in section \.text.*" "verify location address"
+
+gdb_test {py print loc0.inferior == gdb.inferiors()[0]} "True" \
+		 "verify inferior for loc 0" #inf 2
+gdb_test {py print loc1.inferior == gdb.inferiors()[1]} "True" \
+         "verify inferior for loc 1" #inf 1
+
+gdb_test "py print loc0.enabled == loc1.enabled == True" "True" \
+         "verify that locations are enabled"
+
+gdb_test "py print loc0.inferior.num" "2" "ensure that loc0 is on inferior 2"
+
+delete_breakpoints
+gdb_test "py print bp0.is_valid()" "False" \
+         "verify that BP has been invalidated"
+gdb_test "py bp0.locations()" ".*RuntimeError: Breakpoint .* is invalid.*"\
+         "verify that locations can't accessed on an invalid breakpoint"
-- 
1.7.7.6


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

* Re: [PATCH] Add bp_location to Python interface
  2012-04-03 10:35                         ` Kevin Pouget
@ 2012-04-03 12:15                           ` Phil Muldoon
  2012-04-03 14:43                             ` Paul_Koning
  2012-04-05 16:27                           ` Eli Zaretskii
  1 sibling, 1 reply; 25+ messages in thread
From: Phil Muldoon @ 2012-04-03 12:15 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: Tom Tromey, gdb-patches, Eli Zaretskii

> On Fri, Mar 30, 2012 at 9:51 PM, Tom Tromey <tromey@redhat.com>
> wrote:

> On 04/03/2012 11:34 AM, Kevin Pouget wrote:

> the idea here was that a tuple is not mutable, whereas a list is --
>  () vs. []. I've removed the tuple, although although I'm not sure
>  when I'm supposed to use the List or the Tuple.

This was my comment way back when, so apologies for the conflicting
comments.  I prefer to return non-mutable objects when they reflect an
internal state of GDB; just to provide a hint that the data exchange
is one-way -- that changing the list won't change state back in GDB
(hence the immutable tuple).

Though I suspect it does not really matter -- the user can quite
easily turn a tuple into a list.  As for returning a tuple versus a
list, again, I guess it does not really matter; the tuple idiom
was just my convention (though with very large lists, the conversion
to a tuple might be expensive).


> 2012-03-04 Kevin Pouget <kevin.pouget@st.com>
>
> Add bp_location to Python interface
> * Makefile.in (SUBDIR_PYTHON_OBS): Add py-bploc.o
> (SUBDIR_PYTHON_SRCS): Add python/py-bploc.c
> Add build rule for this file.
> * breakpoint.h (struct bploc_object): Forward declaration.
> (struct bp_location): Add py_bploc_obj.
> * python/py-bploc.c: New file.
> * python/py-breakpoint.c (bppy_locations): New function.
> (breakpoint_object_methods): New method binding: locations().
> * python/python-internal.h (bploc_object): New typedef.
> (bplocation_to_bplocation_object): New prototype.
> (gdbpy_initialize_bplocation): Likewise.
>
> doc/
> Add bp_location to Python interface
> * gdb.texinfo (Breakpoints In Python): Document
> gdb.Breakpoint.locations and gdb.BpLocation.
>
>
> testsuite/
> Add bp_location to Python interface
> * gdb.python/py-breakpoint.exp: Test gdb.BpLocation.

Thanks for this,

Cheers

Phil

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

* RE: [PATCH] Add bp_location to Python interface
  2012-04-03 12:15                           ` Phil Muldoon
@ 2012-04-03 14:43                             ` Paul_Koning
  2012-04-04  8:36                               ` Kevin Pouget
  0 siblings, 1 reply; 25+ messages in thread
From: Paul_Koning @ 2012-04-03 14:43 UTC (permalink / raw)
  To: pmuldoon, kevin.pouget; +Cc: tromey, gdb-patches, eliz

>...
>Though I suspect it does not really matter -- the user can quite easily turn a tuple into a list.  As for returning a tuple versus a list, again, I guess it does not really matter; the tuple idiom was just my convention (though with very large lists, the conversion to a tuple might be expensive).

If you know the number of items, the API lets you create the tuple with that size and fill it in item by item, so there isn't necessary a conversion from list to tuple involved.

	paul

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

* Re: [PATCH] Add bp_location to Python interface
  2012-04-03 14:43                             ` Paul_Koning
@ 2012-04-04  8:36                               ` Kevin Pouget
  2012-05-09  7:18                                 ` Kevin Pouget
  0 siblings, 1 reply; 25+ messages in thread
From: Kevin Pouget @ 2012-04-04  8:36 UTC (permalink / raw)
  To: Paul_Koning; +Cc: pmuldoon, tromey, gdb-patches, eliz

On Tue, Apr 3, 2012 at 4:42 PM,  <Paul_Koning@dell.com> wrote:
>>...
>>Though I suspect it does not really matter -- the user can quite easily turn a tuple into a list.  As for returning a tuple versus a list, again, I guess it does not really matter; the tuple idiom was just my convention (though with very large lists, the conversion to a tuple might be expensive).
>
> If you know the number of items, the API lets you create the tuple with that size and fill it in item by item, so there isn't necessary a conversion from list to tuple involved.
>


Hello,

On Tue, Apr 3, 2012 at 4:42 PM,  <Paul_Koning@dell.com> wrote:
> If you know the number of items, the API lets you create the tuple with that size
> and fill it in item by item, so there isn't necessary a conversion from list to tuple
> involved.

the sequence size is not "known in advance", although it can be
computed to instantiate the tuple before populating it;

On Tue, Apr 3, 2012 at 2:15 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
> though with very large lists, the conversion to a tuple might be expensive

here [I think that] the list size is limited by the number of program
spaces, which is usually the number of inferiors, so this conversion
can't really be that expensive


I'll wait for Tom's point of view before updating the code.

(I'll also add the missing space in the doc in the next patch
>> the following attributes (all read only). Breakpoint location ...
)


thanks for your advises,

Kevin

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

* Re: [PATCH] Add bp_location to Python interface
  2012-04-03 10:35                         ` Kevin Pouget
  2012-04-03 12:15                           ` Phil Muldoon
@ 2012-04-05 16:27                           ` Eli Zaretskii
  1 sibling, 0 replies; 25+ messages in thread
From: Eli Zaretskii @ 2012-04-05 16:27 UTC (permalink / raw)
  To: Kevin Pouget; +Cc: tromey, gdb-patches, pmuldoon

> From: Kevin Pouget <kevin.pouget@gmail.com>
> Date: Tue, 3 Apr 2012 12:34:50 +0200
> Cc: gdb-patches@sourceware.org, pmuldoon@redhat.com, 
> 	Eli Zaretskii <eliz@gnu.org>
> 
> +A breakpoint location is represented by a @code{gdb.BpLocation} object,
> +which offers the following attributes (all read only). Breakpoint location 
                                                        ^^
Two spaces here, please.

OK with that change.

Thanks.

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

* Re: [PATCH] Add bp_location to Python interface
  2012-04-04  8:36                               ` Kevin Pouget
@ 2012-05-09  7:18                                 ` Kevin Pouget
  0 siblings, 0 replies; 25+ messages in thread
From: Kevin Pouget @ 2012-05-09  7:18 UTC (permalink / raw)
  To: gdb-patches; +Cc: pmuldoon, tromey, eliz, Paul_Koning

On Wed, Apr 4, 2012 at 10:35 AM, Kevin Pouget <kevin.pouget@gmail.com> wrote:
>
> On Tue, Apr 3, 2012 at 4:42 PM,  <Paul_Koning@dell.com> wrote:
> >>...
> >>Though I suspect it does not really matter -- the user can quite easily turn a tuple into a list.  As for returning a tuple versus a list, again, I guess it does not really matter; the tuple idiom was just my convention (though with very large lists, the conversion to a tuple might be expensive).
> >
> > If you know the number of items, the API lets you create the tuple with that size and fill it in item by item, so there isn't necessary a conversion from list to tuple involved.
> >
>
>
> Hello,
>
> On Tue, Apr 3, 2012 at 4:42 PM,  <Paul_Koning@dell.com> wrote:
> > If you know the number of items, the API lets you create the tuple with that size
> > and fill it in item by item, so there isn't necessary a conversion from list to tuple
> > involved.
>
> the sequence size is not "known in advance", although it can be
> computed to instantiate the tuple before populating it;
>
> On Tue, Apr 3, 2012 at 2:15 PM, Phil Muldoon <pmuldoon@redhat.com> wrote:
> > though with very large lists, the conversion to a tuple might be expensive
>
> here [I think that] the list size is limited by the number of program
> spaces, which is usually the number of inferiors, so this conversion
> can't really be that expensive
>
>
> I'll wait for Tom's point of view before updating the code.
>
> (I'll also add the missing space in the doc in the next patch
> >> the following attributes (all read only). Breakpoint location ...
> )
>
>
> thanks for your advises,
>
> Kevin

gentle remainder


thanks;

Kevin

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

end of thread, other threads:[~2012-05-09  7:18 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-08 10:17 [PATCH] Add bp_location to Python interface Kevin Pouget
2011-12-08 13:39 ` Phil Muldoon
2011-12-08 14:28   ` Kevin Pouget
2011-12-08 14:56 ` Phil Muldoon
2011-12-09 13:49   ` Kevin Pouget
2011-12-09 14:15     ` Phil Muldoon
2011-12-13 15:55       ` Kevin Pouget
2012-01-09 11:47         ` Kevin Pouget
2012-01-09 17:23           ` Eli Zaretskii
2012-01-10 15:09             ` Kevin Pouget
2012-01-10 16:03               ` Kevin Pouget
2012-01-10 17:25               ` Eli Zaretskii
2012-01-11 10:16                 ` Kevin Pouget
2012-01-11 10:27                   ` Eli Zaretskii
2012-01-10 22:24               ` Doug Evans
2012-01-11  9:05                 ` Kevin Pouget
2012-01-11 19:45                   ` Doug Evans
2012-01-27 13:04                     ` Kevin Pouget
2012-03-30 19:51                       ` Tom Tromey
2012-04-03 10:35                         ` Kevin Pouget
2012-04-03 12:15                           ` Phil Muldoon
2012-04-03 14:43                             ` Paul_Koning
2012-04-04  8:36                               ` Kevin Pouget
2012-05-09  7:18                                 ` Kevin Pouget
2012-04-05 16:27                           ` Eli Zaretskii

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