[PATCH] Add bp_location to Python interface

Kevin Pouget
2011-12-08T09:26:36Z

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