[RFC][patch 4/9] export breakpoints to Python

Thiago Jung Bauermann
2008-04-29T15:59:35Z

[-- Attachment #1: python-breakpoints.diff --]
[-- Type: text/plain, Size: 15709 bytes --]

2008-04-29 Tom Tromey <tromey@redhat.com>

* Makefile.in (SUBDIR_PYTHON_OBS): Add python-breakpoint.o.
(SUBDIR_PYTHON_SRCS): Add python/breakpoint.c.
(python-breakpoint.o): New target.
* python/breakpoint.c: New file.
* python/hooks.c (gdbpy_initialize_hooks): Register breakpoint
event callback functions.
* python/python-internal.h (gdbpy_get_breakpoints,
gdbpy_initialize_breakpoints, gdbpy_breakpoint_created,
gdbpy_breakpoint_deleted): Declare.
* python/python.c (demand_python): Add "get_breakpoints"
function. Call gdbpy_initialize_breakpoints.

Index: tromey.git/gdb/Makefile.in
===================================================================
--- tromey.git.orig/gdb/Makefile.in 2008-04-29 11:05:09.000000000 -0300
+++ tromey.git/gdb/Makefile.in 2008-04-29 11:05:15.000000000 -0300
@@ -262,10 +262,12 @@ SUBDIR_TUI_CFLAGS= \
#
SUBDIR_PYTHON_OBS = \
python.o \
+ python-breakpoint.o \
python-hooks.o \
python-value.o
SUBDIR_PYTHON_SRCS = \
python/python.c \
+ python/breakpoint.c \
python/hooks.c \
python/value.c
SUBDIR_PYTHON_DEPS =
@@ -3415,6 +3417,11 @@ python.o: $(srcdir)/python/python.c $(de
$(command_h) $(libiberty_h) $(cli_decode_h) $(charset_h) $(top_h) \
$(exceptions_h) $(python_internal_h) $(version_h)
$(CC) -c $(INTERNAL_CFLAGS) $(PYTHON_CFLAGS) $(srcdir)/python/python.c
+python-breakpoint.o: $(srcdir)/python/breakpoint.c $(defs_h) $(python_h) \
+ $(value_h) $(exceptions_h) $(python_internal_h) $(charset_h) \
+ $(breakpoint_h) $(gdbcmd_h)
+ $(CC) -c $(INTERNAL_CFLAGS) $(PYTHON_CFLAGS) \
+ $(srcdir)/python/breakpoint.c -o python-breakpoint.o
python-hooks.o: $(srcdir)/python/hooks.c $(defs_h) $(cli_decode_h) \
$(charset_h) $(gdb_events_h) $(python_h) $(python_internal_h)
$(CC) -c $(INTERNAL_CFLAGS) $(PYTHON_CFLAGS) \
Index: tromey.git/gdb/python/breakpoint.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ tromey.git/gdb/python/breakpoint.c 2008-04-29 11:05:15.000000000 -0300
@@ -0,0 +1,408 @@
+/* Python interface to breakpoints
+
+ Copyright (C) 2008 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 "value.h"
+#include "exceptions.h"
+#include "python-internal.h"
+#include "charset.h"
+#include "breakpoint.h"
+#include "gdbcmd.h"
+
+
+/* From breakpoint.c. */
+extern struct breakpoint *breakpoint_chain;
+
+
+typedef struct breakpoint_object breakpoint_object;
+
+static PyObject *bppy_is_valid (PyObject *, PyObject *);
+static PyObject *bppy_is_enabled (PyObject *, PyObject *);
+static PyObject *bppy_is_silent (PyObject *, PyObject *);
+static PyObject *bppy_set_enabled (PyObject *, PyObject *);
+static PyObject *bppy_set_silent (PyObject *, PyObject *);
+static PyObject *bppy_get_location (PyObject *, PyObject *);
+static PyObject *bppy_get_condition (PyObject *, PyObject *);
+static PyObject *bppy_get_commands (PyObject *, PyObject *);
+
+
+/* A dynamically allocated vector of breakpoint objects. Each
+ breakpoint has a number. A breakpoint is valid if its slot in this
+ vector is non-null. When a breakpoint is deleted, we drop our
+ reference to it and zero its slot; this is how we let the Python
+ object have a lifetime which is independent from that of the gdb
+ breakpoint. */
+static breakpoint_object **bppy_breakpoints;
+
+/* Number of slots in bppy_breakpoints. */
+static int bppy_slots;
+
+/* Number of live breakpoints. */
+static int bppy_live;
+
+/* Variables used to pass information between the Breakpoint
+ constructor and the breakpoint-created hook function. */
+static breakpoint_object *bppy_pending_object;
+
+struct breakpoint_object
+{
+ PyObject_HEAD
+
+ /* The breakpoint number according to gdb. */
+ int number;
+
+ /* The gdb breakpoint object, or NULL if the breakpoint has been
+ deleted. */
+ struct breakpoint *bp;
+};
+
+#define BPPY_VALID_P(Num) \
+ ((Num) >= 0 \
+ && (Num) < bppy_slots \
+ && bppy_breakpoints[Num] != NULL)
+
+#define BPPY_REQUIRE_VALID(Breakpoint) \
+ do { \
+ if (! BPPY_VALID_P ((Breakpoint)->number)) \
+ return PyErr_Format (PyExc_RuntimeError, "breakpoint %d is invalid", \
+ (Breakpoint)->number); \
+ } while (0)
+
+static PyMethodDef breakpoint_object_methods[] =
+{
+ { "is_valid", bppy_is_valid, METH_NOARGS,
+ "Return true if this breakpoint is valid, false if not." },
+ { "is_enabled", bppy_is_enabled, METH_NOARGS,
+ "Return true if this breakpoint is enabled, false if disabled." },
+ { "is_silent", bppy_is_silent, METH_NOARGS,
+ "Return true if this breakpoint is silent, false if verbose." },
+
+ { "set_enabled", bppy_set_enabled, METH_O,
+ "Enable or disable this breakpoint" },
+ { "set_silent", bppy_set_silent, METH_O,
+ "Make this breakpoint quiet or verbose" },
+
+ { "get_location", bppy_get_location, METH_NOARGS,
+ "Return the location of this breakpoint, as specified by the user"},
+ { "get_condition", bppy_get_condition, METH_NOARGS,
+ "Return the condition of this breakpoint, as specified by the user.\n\
+Returns None if no condition set."},
+ { "get_commands", bppy_get_commands, METH_NOARGS,
+ "Return the commands of this breakpoint, as specified by the user"},
+
+ { 0 }
+};
+
+static PyTypeObject breakpoint_object_type =
+{
+ PyObject_HEAD_INIT (NULL)
+ 0, /*ob_size*/
+ "gdb.Breakpoint", /*tp_name*/
+ sizeof (breakpoint_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_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, /*tp_flags*/
+ "GDB breakpoint object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ breakpoint_object_methods /* tp_methods */
+};
+
+static PyObject *
+bppy_is_valid (PyObject *self, PyObject *args)
+{
+ if (((breakpoint_object *) self)->bp)
+ Py_RETURN_TRUE;
+ Py_RETURN_FALSE;
+}
+
+static PyObject *
+bppy_is_enabled (PyObject *self, PyObject *args)
+{
+ if (! ((breakpoint_object *) self)->bp)
+ Py_RETURN_FALSE;
+ /* Not clear what we really want here. */
+ if (((breakpoint_object *) self)->bp->enable_state == bp_enabled)
+ Py_RETURN_TRUE;
+ Py_RETURN_FALSE;
+}
+
+static PyObject *
+bppy_is_silent (PyObject *self, PyObject *args)
+{
+ BPPY_REQUIRE_VALID ((breakpoint_object *) self);
+ if (((breakpoint_object *) self)->bp->silent)
+ Py_RETURN_TRUE;
+ Py_RETURN_FALSE;
+}
+
+static PyObject *
+bppy_set_enabled (PyObject *self, PyObject *newvalue)
+{
+ breakpoint_object *self_bp = (breakpoint_object *) self;
+
+ BPPY_REQUIRE_VALID (self_bp);
+ if (! PyBool_Check (newvalue))
+ return PyErr_Format (PyExc_RuntimeError, "argument must be boolean");
+
+ if (newvalue == Py_True)
+ enable_breakpoint (self_bp->bp);
+ else
+ disable_breakpoint (self_bp->bp);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+bppy_set_silent (PyObject *self, PyObject *newvalue)
+{
+ breakpoint_object *self_bp = (breakpoint_object *) self;
+
+ BPPY_REQUIRE_VALID (self_bp);
+ if (! PyBool_Check (newvalue))
+ return PyErr_Format (PyExc_RuntimeError, "argument must be boolean");
+
+ self_bp->bp->silent = (newvalue == Py_True);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+bppy_get_location (PyObject *self, PyObject *args)
+{
+ char *str;
+
+ BPPY_REQUIRE_VALID ((breakpoint_object *) self);
+ str = ((breakpoint_object *) self)->bp->addr_string;
+ /* FIXME: watchpoints? tracepoints? */
+ if (! str)
+ str = "";
+ return PyString_Decode (str, strlen (str), host_charset (),
+ NULL /* FIXME */);
+}
+
+static PyObject *
+bppy_get_condition (PyObject *self, PyObject *args)
+{
+ char *str;
+ BPPY_REQUIRE_VALID ((breakpoint_object *) self);
+
+ str = ((breakpoint_object *) self)->bp->cond_string;
+ if (! str)
+ Py_RETURN_NONE;
+ return PyString_Decode (str, strlen (str), host_charset (),
+ NULL /* FIXME */);
+}
+
+static PyObject *
+bppy_get_commands (PyObject *self, PyObject *args)
+{
+ breakpoint_object *self_bp = (breakpoint_object *) self;
+ long length;
+ volatile struct gdb_exception except;
+ struct ui_file *string_file;
+ struct cleanup *chain;
+ PyObject *result;
+ char *cmdstr;
+
+ BPPY_REQUIRE_VALID (self_bp);
+
+ if (! self_bp->bp->commands)
+ Py_RETURN_NONE;
+
+ string_file = mem_fileopen ();
+ chain = make_cleanup_ui_file_delete (string_file);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ /* FIXME: this can fail. Maybe we need to be making a new
+ ui_out object here? */
+ ui_out_redirect (uiout, string_file);
+ print_command_lines (uiout, self_bp->bp->commands, 0);
+ ui_out_redirect (uiout, NULL);
+ }
+ cmdstr = ui_file_xstrdup (string_file, &length);
+ GDB_PY_HANDLE_EXCEPTION (except);
+
+ result = PyString_Decode (cmdstr, strlen (cmdstr), host_charset (),
+ NULL /* FIXME */);
+ do_cleanups (chain);
+ xfree (cmdstr);
+ return result;
+}
+
+static PyObject *
+bppy_new (PyTypeObject *subtype, PyObject *args, PyObject *kwargs)
+{
+ PyObject *result;
+ char *spec;
+ volatile struct gdb_exception except;
+
+ /* FIXME: allow condition, thread, temporary, ... ? */
+ if (! PyArg_ParseTuple (args, "s", &spec))
+ return NULL;
+ result = subtype->tp_alloc (subtype, 0);
+ if (! result)
+ return NULL;
+ bppy_pending_object = (breakpoint_object *) result;
+ bppy_pending_object->number = -1;
+ bppy_pending_object->bp = NULL;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ set_breakpoint (spec, NULL, 0, 0, -1, 0, AUTO_BOOLEAN_TRUE);
+ }
+ GDB_PY_HANDLE_EXCEPTION (except);
+
+ BPPY_REQUIRE_VALID ((breakpoint_object *) result);
+ return result;
+}
+
+\f
+
+/* Static function to return a tuple holding all breakpoints. */
+
+PyObject *
+gdbpy_get_breakpoints (PyObject *self, PyObject *args)
+{
+ PyObject *result;
+
+ if (bppy_live == 0)
+ Py_RETURN_NONE;
+
+ result = PyTuple_New (bppy_live);
+ if (result)
+ {
+ int i, out = 0;
+ for (i = 0; out < bppy_live; ++i)
+ {
+ if (! bppy_breakpoints[i])
+ continue;
+ Py_INCREF (bppy_breakpoints[i]);
+ PyTuple_SetItem (result, out, (PyObject *) bppy_breakpoints[i]);
+ ++out;
+ }
+ }
+ return result;
+}
+
+\f
+
+/* Event callback functions. */
+
+void
+gdbpy_breakpoint_created (int num)
+{
+ breakpoint_object *newbp;
+ struct breakpoint *bp;
+
+ if (num < 0)
+ return;
+
+ for (bp = breakpoint_chain; bp; bp = bp->next)
+ if (bp->number == num)
+ break;
+ if (! bp)
+ return;
+
+ if (num >= bppy_slots)
+ {
+ int old = bppy_slots;
+ bppy_slots = bppy_slots * 2 + 10;
+ bppy_breakpoints
+ = (breakpoint_object **) xrealloc (bppy_breakpoints,
+ (bppy_slots
+ * sizeof (breakpoint_object *)));
+ memset (&bppy_breakpoints[old], 0,
+ (bppy_slots - old) * sizeof (PyObject *));
+ }
+
+ ++bppy_live;
+
+ if (bppy_pending_object)
+ {
+ newbp = bppy_pending_object;
+ bppy_pending_object = NULL;
+ }
+ else
+ newbp = PyObject_New (breakpoint_object, &breakpoint_object_type);
+ if (newbp)
+ {
+ PyObject *hookfn;
+
+ newbp->number = num;
+ newbp->bp = bp;
+ bppy_breakpoints[num] = newbp;
+
+ hookfn = gdbpy_get_hook_function ("new_breakpoint");
+ if (hookfn)
+ {
+ /* FIXME: refc? */
+ PyObject_CallFunctionObjArgs (hookfn, newbp, NULL);
+ }
+ }
+
+ if (PyErr_Occurred ())
+ {
+ /* FIXME -- what to do? */
+ PyErr_Print ();
+ }
+}
+
+void
+gdbpy_breakpoint_deleted (int num)
+{
+ if (BPPY_VALID_P (num))
+ {
+ bppy_breakpoints[num]->bp = NULL;
+ Py_DECREF (bppy_breakpoints[num]);
+ bppy_breakpoints[num] = NULL;
+ --bppy_live;
+ }
+}
+
+void
+gdbpy_initialize_breakpoints (void)
+{
+ breakpoint_object_type.tp_new = bppy_new;
+ if (PyType_Ready (&breakpoint_object_type) < 0)
+ return;
+
+ Py_INCREF (&breakpoint_object_type);
+ PyModule_AddObject (gdb_module, "Breakpoint",
+ (PyObject *) &breakpoint_object_type);
+}
Index: tromey.git/gdb/python/hooks.c
===================================================================
--- tromey.git.orig/gdb/python/hooks.c 2008-04-29 11:05:09.000000000 -0300
+++ tromey.git/gdb/python/hooks.c 2008-04-29 11:05:15.000000000 -0300
@@ -112,6 +112,8 @@ void
gdbpy_initialize_hooks (void)
{
handlers.architecture_changed = gdbpy_architecture_changed;
+ handlers.breakpoint_create = gdbpy_breakpoint_created;
+ handlers.breakpoint_delete = gdbpy_breakpoint_deleted;
deprecated_set_gdb_event_hooks (&handlers);

deprecated_set_hook = gdbpy_set_hook;
Index: tromey.git/gdb/python/python-internal.h
===================================================================
--- tromey.git.orig/gdb/python/python-internal.h 2008-04-29 11:05:09.000000000 -0300
+++ tromey.git/gdb/python/python-internal.h 2008-04-29 11:05:15.000000000 -0300
@@ -31,6 +31,7 @@ extern PyTypeObject value_object_type;

PyObject *gdbpy_make_value_from_int (PyObject *self, PyObject *args);
PyObject *gdbpy_get_value_from_history (PyObject *self, PyObject *args);
+PyObject *gdbpy_get_breakpoints (PyObject *, PyObject *);

PyObject *variable_to_python (struct cmd_list_element *);
PyObject *value_to_value_object (struct value *v);
@@ -42,6 +43,7 @@ PyObject *gdbpy_get_hook_function (const

void gdbpy_initialize_values (void);
void gdbpy_initialize_hooks (void);
+void gdbpy_initialize_breakpoints (void);

/* Use this after a TRY_EXCEPT to throw the appropriate Python
exception. FIXME: throw different errors depending on
@@ -54,4 +56,8 @@ void gdbpy_initialize_hooks (void);
"%s", Exception.message); \
} while (0)

+/* Breakpoint hook functions. */
+void gdbpy_breakpoint_created (int);
+void gdbpy_breakpoint_deleted (int);
+
#endif /* GDB_PYTHON_INTERNAL_H */
Index: tromey.git/gdb/python/python.c
===================================================================
--- tromey.git.orig/gdb/python/python.c 2008-04-29 11:05:09.000000000 -0300
+++ tromey.git/gdb/python/python.c 2008-04-29 11:05:15.000000000 -0300
@@ -53,6 +53,9 @@ demand_python ()
{ "show", get_show_variable, METH_VARARGS,
"Return a gdb setting's value" },

+ { "get_breakpoints", gdbpy_get_breakpoints, METH_NOARGS,
+ "Return a tuple of all breakpoint objects" },
+
{NULL, NULL, 0, NULL}
};

@@ -66,6 +69,7 @@ demand_python ()

gdbpy_initialize_hooks ();
gdbpy_initialize_values ();
+ gdbpy_initialize_breakpoints ();

PyRun_SimpleString ("import gdb");

--
--
[]'s
Thiago Jung Bauermann
Software Engineer
IBM Linux Technology Center