From e7c018f9ecdbd92199a86a1178225c7ae6828a39 Mon Sep 17 00:00:00 2001 From: Kevin Pouget 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 . */ + + +#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 */ @@ -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