[patch] [python] Expose some breakpoint operations to Python

Phil Muldoon
2011-06-23T12:35:33Z

This patch exposes to the Python API some breakpoint operations. The
operations exported are:

print_it
print_one
print_one_detail
print_mention

One of the changes made in this patch is to bind the Python breakpoint
and the GDB breakpoint much earlier if the breakpoint is created from
the Python API. This change was needed as some breakpoint operations
are called before the observers are notified. Previously we bound the
Python object and the breakpoint when the observer was notified. In the
case where a breakpoint is not created from the Python API this is still
the case. In any case, the ordering of breakpoint operations and
breakpoint observers means we have to bind some breakpoints at
creation. This has meant we have had to alter some of the breakpoint
APIs to carry the breakpoint object in the parameter list at creation.

I've included both MI and CLI tests. These are very similar. The MI
tests are needed to prove that the exported Python APIs work
seamlessly with MI notifications.

Tested on X8664 Fedora 15 with no regressions.

What do you think?

Cheers,

Phil

--

2011-06-23 Phil Muldoon <pmuldoon@redhat.com>

* python/python.h (gdbpy_bind_breakpoint): Export.
* python/python.c (gdbpy_bind_breakpoint): New function.
* python/python-internal.h (breakpoint_object): Moved from
py-breakpoint.c
* python/py-breakpoint.c (breakpoint_object): Move to
python-internal.h
(py_breakpoint_ops): Define.
(pybp_print_stop_actions): Define.
(find_python_obj): New function.
(pybp_print_one): Ditto.
(pybp_print_one_detail): Ditto.
(pybp_print_mention): Ditto.
(pybp_print_it): Ditto.
(bppy_init): Call bppy_build_ops. Pass bppy_pending_object to
create_breakpoint, watch_command_wrapper, awatch_command_wrapper
and rwatch_command_wrapper.
(gdbpy_breakpoint_created): Check for early binding of breakpoints.
Add initialization to newpb breakpoint. Conditionalize later
binding if an early binding has already occurred.
(gdbpy_initialize_breakpoints): Build pybp_print_stop_action
constants.
* mi/mi-cmd-break.c (mi_cmd_break_insert): Update create_breakpoint
call.
(mi_cmd_break_watch): Update watch_command_wrapper,
rwatch_command_wrapper and awatch_command_wrapper calls.
* breakpoint.h: Update create_breakpoint, watch_command_wrapper,
rwatch_command_wrapper and awatch_command_wrapper definitions.
* breakpoint.c (create_breakpoint_sal): Add Python breakpoint_struct
parameter. Call gdbpy_bind_breakpoint.
(watch_command_1): Ditto.
(create_breakpoints_sal): Add Python breakpoint_struct parameter.
Pass to called functions.
(watch_command_wrapper): Ditto.
(create_breakpoint): Add Python breakpoint_struct parameter. Pass
to called function. Call gdbpy_bind_breakpoint.
(break_command_1): Update create_breakpoint call with
breakpoint_object parameter.
(handle_gnu_v3_exceptions): Ditto
(trace_command): Ditto.
(ftrace_command): Ditto.
(strace_command): Ditto.
(create_tracepoint_from_upload): Ditto.
(watch_maybe_just_location): Update watch_command_1 call with
breakpoint_struct parameter.
(rwatch_command_wrapper): Ditto.
(awatch_command_wrapper): Ditto.
(handle_gnu_v3_exceptions):

2011-06-23 Phil Muldoon <pmuldoon@redhat.com>

* gdb.python/py-breakpoint.exp: Add breakpoint operation tests.
* gdb.python/py-mi.exp: Ditto.
* gdb.python/py-mi-bp.py: New file.

2011-06-23 pmuldoon <pmuldoon@redhat.com>

* gdb.texinfo (Breakpoints In Python): Document breakpoint
operation APIs.

--

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 935cd03..3e2c42c 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -7500,7 +7500,8 @@ bp_loc_is_permanent (struct bp_location *loc)

/* Create a breakpoint with SAL as location. Use ADDR_STRING
as textual description of the location, and COND_STRING
- as condition expression. */
+ as condition expression. PY_OBJ is the Python object previously
+ created to be attached to the breakpoint; this can be NULL. */

static void
create_breakpoint_sal (struct gdbarch *gdbarch,
@@ -7509,7 +7510,8 @@ create_breakpoint_sal (struct gdbarch *gdbarch,
enum bptype type, enum bpdisp disposition,
int thread, int task, int ignore_count,
struct breakpoint_ops *ops, int from_tty,
- int enabled, int internal, int display_canonical)
+ int enabled, int internal, int display_canonical,
+ struct breakpoint_object *py_obj)
{
struct breakpoint *b = NULL;
int i;
@@ -7628,6 +7630,11 @@ create_breakpoint_sal (struct gdbarch *gdbarch,
= xstrprintf ("*%s", paddress (b->loc->gdbarch, b->loc->address));

b->ops = ops;
+
+ /* Now that the breakpoint is created, bind the breakpoint with the
+ Python breakpoint object. */
+ gdbpy_bind_breakpoint (py_obj, b);
+
/* Do not mention breakpoints with a negative number, but do
notify observers. */
if (!internal)
@@ -7774,7 +7781,9 @@ expand_line_sal_maybe (struct symtab_and_line sal)
SALS.nelts is not 1 is when we set a breakpoint on an overloaded
function. In that case, it's still not possible to specify
separate conditions for different overloaded functions, so
- we take just a single condition string.
+ we take just a single condition string. PY_OBJ is the Python object
+ previously created to be attached to the breakpoint; this can be
+ NULL.

NOTE: If the function succeeds, the caller is expected to cleanup
the arrays ADDR_STRING, COND_STRING, and SALS (but not the
@@ -7790,7 +7799,8 @@ create_breakpoints_sal (struct gdbarch *gdbarch,
enum bptype type, enum bpdisp disposition,
int thread, int task, int ignore_count,
struct breakpoint_ops *ops, int from_tty,
- int enabled, int internal)
+ int enabled, int internal,
+ struct breakpoint_object *py_obj)
{
int i;

@@ -7803,7 +7813,7 @@ create_breakpoints_sal (struct gdbarch *gdbarch,
cond_string, type, disposition,
thread, task, ignore_count, ops,
from_tty, enabled, internal,
- canonical->special_display);
+ canonical->special_display, py_obj);
}
}

@@ -8059,8 +8069,9 @@ decode_static_tracepoint_spec (char **arg_p)
the location of breakpoint, with condition and thread specified by
the COND_STRING and THREAD parameters. If INTERNAL is non-zero,
the breakpoint number will be allocated from the internal
- breakpoint count. Returns true if any breakpoint was created;
- false otherwise. */
+ breakpoint count. PY_OBJ is the Python object previously created
+ to be attached to the breakpoint; this can be NULL. Returns true
+ if any breakpoint was created; false otherwise. */

int
create_breakpoint (struct gdbarch *gdbarch,
@@ -8070,7 +8081,8 @@ create_breakpoint (struct gdbarch *gdbarch,
int ignore_count,
enum auto_boolean pending_break_support,
struct breakpoint_ops *ops,
- int from_tty, int enabled, int internal)
+ int from_tty, int enabled, int internal,
+ struct breakpoint_object *py_obj)
{
volatile struct gdb_exception e;
struct symtabs_and_lines sals;
@@ -8244,7 +8256,7 @@ create_breakpoint (struct gdbarch *gdbarch,
tempflag ? disp_del : disp_donttouch,
thread, task, ignore_count, ops,
from_tty, enabled, internal,
- canonical.special_display);
+ canonical.special_display, py_obj);

do_cleanups (old_chain);

@@ -8269,7 +8281,7 @@ create_breakpoint (struct gdbarch *gdbarch,
type_wanted,
tempflag ? disp_del : disp_donttouch,
thread, task, ignore_count, ops, from_tty,
- enabled, internal);
+ enabled, internal, py_obj);
}
else
{
@@ -8288,7 +8300,10 @@ create_breakpoint (struct gdbarch *gdbarch,
b->ops = ops;
b->enable_state = enabled ? bp_enabled : bp_disabled;
b->pspace = current_program_space;
- b->py_bp_object = NULL;
+
+ /* Now that the breakpoint is created, bind the breakpoint with
+ the Python breakpoint object. */
+ gdbpy_bind_breakpoint (py_obj, b);

if (enabled && b->pspace->executing_startup
&& (b->type == bp_breakpoint
@@ -8345,7 +8360,8 @@ break_command_1 (char *arg, int flag, int from_tty)
NULL /* breakpoint_ops */,
from_tty,
1 /* enabled */,
- 0 /* internal */);
+ 0 /* internal */,
+ NULL /* Python breakpoint object */);
}

@@ -9178,7 +9194,8 @@ is_masked_watchpoint (const struct breakpoint *b)
hw_access: watch access (read or write) */
static void
watch_command_1 (char *arg, int accessflag, int from_tty,
- int just_location, int internal)
+ int just_location, int internal,
+ struct breakpoint_object *py_obj)
{
volatile struct gdb_exception e;
struct breakpoint *b, *scope_breakpoint = NULL;
@@ -9400,6 +9417,11 @@ watch_command_1 (char *arg, int accessflag, int from_tty,
b->exp = exp;
b->exp_valid_block = exp_valid_block;
b->cond_exp_valid_block = cond_exp_valid_block;
+
+ /* Now that the breakpoint is created, bind the breakpoint with the
+ Python breakpoint object. */
+ gdbpy_bind_breakpoint (py_obj, b);
+
if (just_location)
{
struct type *t = value_type (val);
@@ -9569,9 +9591,10 @@ can_use_hardware_watchpoint (struct value *v)
}

void
-watch_command_wrapper (char *arg, int from_tty, int internal)
+watch_command_wrapper (char *arg, int from_tty, int internal,
+ struct breakpoint_object *py_obj)
{
- watch_command_1 (arg, hw_write, from_tty, 0, internal);
+ watch_command_1 (arg, hw_write, from_tty, 0, internal, py_obj);
}

/* A helper function that looks for an argument at the start of a
@@ -9607,7 +9630,7 @@ watch_maybe_just_location (char *arg, int accessflag, int from_tty)
just_location = 1;
}

- watch_command_1 (arg, accessflag, from_tty, just_location, 0);
+ watch_command_1 (arg, accessflag, from_tty, just_location, 0, NULL);
}

static void
@@ -9617,9 +9640,10 @@ watch_command (char *arg, int from_tty)
}

void
-rwatch_command_wrapper (char *arg, int from_tty, int internal)
+rwatch_command_wrapper (char *arg, int from_tty, int internal,
+ struct breakpoint_object *py_obj)
{
- watch_command_1 (arg, hw_read, from_tty, 0, internal);
+ watch_command_1 (arg, hw_read, from_tty, 0, internal, py_obj);
}

static void
@@ -9629,9 +9653,10 @@ rwatch_command (char *arg, int from_tty)
}

void
-awatch_command_wrapper (char *arg, int from_tty, int internal)
+awatch_command_wrapper (char *arg, int from_tty, int internal,
+ struct breakpoint_object *py_obj)
{
- watch_command_1 (arg, hw_access, from_tty, 0, internal);
+ watch_command_1 (arg, hw_access, from_tty, 0, internal, py_obj);
}

static void
@@ -10003,7 +10028,8 @@ handle_gnu_v3_exceptions (int tempflag, char *cond_string,
AUTO_BOOLEAN_TRUE /* pending */,
&gnu_v3_exception_catchpoint_ops, from_tty,
1 /* enabled */,
- 0 /* internal */);
+ 0 /* internal */,
+ NULL /* Python breakpoint object */);

return 1;
}
@@ -12350,7 +12376,8 @@ trace_command (char *arg, int from_tty)
NULL,
from_tty,
1 /* enabled */,
- 0 /* internal */))
+ 0 /* internal */,
+ NULL /* Python breakpoint object */))
set_tracepoint_count (breakpoint_count);
}

@@ -12367,7 +12394,8 @@ ftrace_command (char *arg, int from_tty)
NULL,
from_tty,
1 /* enabled */,
- 0 /* internal */))
+ 0 /* internal */,
+ NULL /* Python breakpoint object */))
set_tracepoint_count (breakpoint_count);
}

@@ -12386,7 +12414,8 @@ strace_command (char *arg, int from_tty)
NULL,
from_tty,
1 /* enabled */,
- 0 /* internal */))
+ 0 /* internal */,
+ NULL /* Python breakpoint object */))
set_tracepoint_count (breakpoint_count);
}

@@ -12451,7 +12480,8 @@ create_tracepoint_from_upload (struct uploaded_tp *utp)
NULL,
0 /* from_tty */,
utp->enabled /* enabled */,
- 0 /* internal */))
+ 0 /* internal */,
+ NULL /* Python breakpoint object */))
return NULL;

set_tracepoint_count (breakpoint_count);
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 6fca479..9cb175d 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -1020,9 +1020,12 @@ extern void break_command (char *, int);
extern void hbreak_command_wrapper (char *, int);
extern void thbreak_command_wrapper (char *, int);
extern void rbreak_command_wrapper (char *, int);
-extern void watch_command_wrapper (char *, int, int);
-extern void awatch_command_wrapper (char *, int, int);
-extern void rwatch_command_wrapper (char *, int, int);
+extern void watch_command_wrapper (char *, int, int,
+ struct breakpoint_object *);
+extern void awatch_command_wrapper (char *, int, int,
+ struct breakpoint_object *);
+extern void rwatch_command_wrapper (char *, int, int,
+ struct breakpoint_object *);
extern void tbreak_command (char *, int);

/* Arguments to pass as context to some catch command handlers. */
@@ -1067,7 +1070,8 @@ extern int create_breakpoint (struct gdbarch *gdbarch, char *arg,
struct breakpoint_ops *ops,
int from_tty,
int enabled,
- int internal);
+ int internal,
+ struct breakpoint_object *py_obj);

extern void insert_breakpoints (void);

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 3a705c2..7931bad 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -23341,14 +23341,18 @@ argument defines the class of watchpoint to create, if @var{type} is
assumed to be a @var{WP_WRITE} class.
@end defmethod

-@defop Operation {gdb.Breakpoint} stop (self)
The @code{gdb.Breakpoint} class can be sub-classed and, in
-particular, you may choose to implement the @code{stop} method.
-If this method is defined as a sub-class of @code{gdb.Breakpoint},
-it will be called when the inferior reaches any location of a
-breakpoint which instantiates that sub-class. If the method returns
-@code{True}, the inferior will be stopped at the location of the
-breakpoint, otherwise the inferior will continue.
+particular, you may choose to implement several different methods
+detailed below. These methods influence how GDB operates with certain
+breakpoint operations.
+
+@defop Operation {gdb.Breakpoint} stop (self)
+The @code{stop} method may be implemented as part of a
+@code{gdb.Breakpoint}. This method will be called when the inferior
+reaches any location of a breakpoint which instantiates that
+sub-class. If the method returns @code{True}, the inferior will be
+stopped at the location of the breakpoint, otherwise the inferior will
+continue.

If there are multiple breakpoints at the same location with a
@code{stop} method, each one will be called regardless of the
@@ -23369,6 +23373,144 @@ class MyBreakpoint (gdb.Breakpoint):
@end smallexample
@end defop

+@defop Operation {gdb.Breakpoint} print_mention (self)
+
+The @code{print_mention} method may be implemented as part of a
+@code{gdb.Breakpoint}. This method will be called when a breakpoint is
+created which instantiates that sub-class. This method allows
+customization of the message that @value{GDBN} prints when a
+breakpoint is created. This method must return a string.
+
+Example @code{print_mention} implementation:
+
+@smallexample
+class MyBreakpoint (gdb.Breakpoint):
+
+ def print_mention (self):
+ return "Our Breakpoint " + str(self.number) + " at " + str(self.location)
+@end smallexample
+
+When a user creates a breakpoint using this class, @value{GDBN} will
+print:
+
+@smallexample
+Our Breakpoint 2 at somewhere/some-file.c:10
+@end smallexample
+@end defop
+
+@defop Operation {gdb.Breakpoint} print_stop_action (self)
+
+The @code{print_stop_action} method may be implemented as part of a
+@code{gdb.Breakpoint}. This method will be called when the inferior
+stops at a breakpoint that is instantiated by that sub-class. This
+method allows customization of the message that @value{GDBN} prints
+when the inferior stops at that breakpoint. This method must return a
+Python @code{Tuple}. The first element of the tuple should be a
+string that defines the breakpoint prelude. The prelude is printed
+before the breakpoint location information is printed. If a prelude
+is not needed, Python @code{None} can be returned instead. The second
+element should be one of the following constants defined in the
+@code{gdb} module:
+
+@table @code
+@findex PRINT_SRC_AND_LOC
+@findex gdb.PRINT_SRC_AND_LOC
+@item PRINT_SRC_AND_LOC
+Print the source line number and location information associated with
+this breakpoint.
+@findex PRINT_SRC_ONLY
+@findex gdb.PRINT_SRC_ONLY
+@item PRINT_SRC_ONLY
+Print the source line number information only.
+@findex PRINT_NOTHING
+@findex gdb.PRINT_NOTHING
+@item PRINT_NOTHING
+Do not print any breakpoint information.
+@end table
+
+Example @code{print_stop_action} implementation:
+
+@smallexample
+class MyBreakpoint (gdb.Breakpoint):
+
+ def print_stop_action (self):
+ return ("Our Breakpoint, ", gdb.PRINT_SRC_AND_LOC)
+@end smallexample
+
+When @value{GDBN} stops the inferior at a breakpoint defined by
+this class, @value{GDBN} will print:
+
+@smallexample
+Our Breakpoint, foofunc (argc=1, argv=0xff) somewhere/some-file.c:10
+@end smallexample
+@end defop
+
+@defop Operation {gdb.Breakpoint} print_one (self, address)
+@anchor{print_one}
+
+The @code{print_one} method may be implemented as part of a
+@code{gdb.Breakpoint}. This method will be called when information is
+requested on a breakpoint (such as the @value {GDBN} command,
+@samp{info breakpoints}). This method allows customization of the
+information that @value{GDBN} prints. The @code{address} parameter
+contains the address where @value{GDBN} stopped the inferior. This
+method must return a Python @code{Tuple}. The first element of the
+tuple should be a Python @code{Long} that contains the address that is
+to be printed in the informational output, or @code{None}. If
+@code{None} is returned, address information will not be printed. The
+second element should be a Python string giving a very brief
+description of the breakpoint, or Python @code{None} if you do not
+want to print anything for that field.
+
+Example @code{print_one} implementation:
+
+@smallexample
+class MyBreakpoint (gdb.Breakpoint):
+
+ def print_one (self, address):
+ return (address,"Our Breakpoint")
+@end smallexample
+
+The output from @samp{info breakpoints} from the above example:
+
+@smallexample
+Num Type Disp Enb Address What
+1 breakpoint keep y 0x0000000000400532 Our Breakpoint
+@end smallexample
+@end defop
+
+@defop Operation {gdb.Breakpoint} print_one (self, address)
+
+The @code{print_one_detail} method may be implemented as part of a
+@code{gdb.Breakpoint}. This method is a companion method to
+@samp{print_one} (@pxref{print_one}). This method will be called when
+information is requested on a breakpoint (such as the @value {GDBN}
+command, @samp{info breakpoints}), and allows extra details to be
+printed about that breakpoint. This method must return a Python
+string containing brief supplemental information on that breakpoint.
+
+Example @code{print_one_detail} implementation:
+
+@smallexample
+class MyBreakpoint (gdb.Breakpoint):
+
+ def print_one (self, address):
+ return (address,"Our Breakpoint")
+
+ def print_one_detail (self):
+ return "Our Breakpoint at " + str (self.location) + "\n"
+
+@end smallexample
+
+The output from @samp{info breakpoints} from the above example:
+
+@smallexample
+Num Type Disp Enb Address What
+1 breakpoint keep y 0x0000000000400532 Our Breakpoint
+Our Breakpoint at somwhere/somefile.c:10
+@end smallexample
+@end defop
+
The available watchpoint types represented by constants are defined in the
@code{gdb} module:

diff --git a/gdb/mi/mi-cmd-break.c b/gdb/mi/mi-cmd-break.c
index d68a033..4e84e73 100644
--- a/gdb/mi/mi-cmd-break.c
+++ b/gdb/mi/mi-cmd-break.c
@@ -169,7 +169,7 @@ mi_cmd_break_insert (char *command, char **argv, int argc)
temp_p, type_wanted,
ignore_count,
pending ? AUTO_BOOLEAN_TRUE : AUTO_BOOLEAN_FALSE,
- NULL, 0, enabled, 0);
+ NULL, 0, enabled, 0, NULL);
do_cleanups (back_to);

}
@@ -259,13 +259,13 @@ mi_cmd_break_watch (char *command, char **argv, int argc)
switch (type)
{
case REG_WP:
- watch_command_wrapper (expr, FROM_TTY, 0);
+ watch_command_wrapper (expr, FROM_TTY, 0, NULL);
break;
case READ_WP:
- rwatch_command_wrapper (expr, FROM_TTY, 0);
+ rwatch_command_wrapper (expr, FROM_TTY, 0, NULL);
break;
case ACCESS_WP:
- awatch_command_wrapper (expr, FROM_TTY, 0);
+ awatch_command_wrapper (expr, FROM_TTY, 0, NULL);
break;
default:
error (_("-break-watch: Unknown watchpoint type."));
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 9c33848..1536223 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -30,6 +30,9 @@
#include "ada-lang.h"
#include "arch-utils.h"
#include "language.h"
+#include "valprint.h"
+#include "annotate.h"
+#include "mi/mi-common.h"

static PyTypeObject breakpoint_object_type;

@@ -43,16 +46,21 @@ static breakpoint_object *bppy_pending_object;
/* Function that is called when a Python condition is evaluated. */
static char * const stop_func = "stop";

-struct breakpoint_object
-{
- PyObject_HEAD
-
- /* The breakpoint number according to gdb. */
- int number;
-
- /* The gdb breakpoint object, or NULL if the breakpoint has been
- deleted. */
- struct breakpoint *bp;
+/* The Python breakpoint_ops structure. This structure is dynamically
+ built when bppy_init inspects the functions that have been defined
+ in the Python breakpoint object. */
+static struct breakpoint_ops py_breakpoint_ops =
+ {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
};

/* Require that BREAKPOINT be a valid breakpoint ID; throw a Python
@@ -107,6 +115,16 @@ static struct pybp_code pybp_watch_types[] =
{NULL} /* Sentinel. */
};

+/* Entries related to the type of print action when a breakpoint is
+ hit. */
+static struct pybp_code pybp_print_stop_actions[] =
+{
+ {"PRINT_SRC_AND_LOC", PRINT_SRC_AND_LOC},
+ {"PRINT_SRC_ONLY", PRINT_SRC_ONLY},
+ {"PRINT_NOTHING", PRINT_NOTHING},
+ {NULL} /* Sentinel. */
+};
+
/* Python function which checks the validity of a breakpoint object. */
static PyObject *
bppy_is_valid (PyObject *self, PyObject *args)
@@ -594,6 +612,354 @@ bppy_get_ignore_count (PyObject *self, void *closure)
return PyInt_FromLong (self_bp->bp->ignore_count);
}

+/* Convenience function that given a breakpoint B, return (and cast
+ to PyObject) the corresponding Python object. This function should
+ never be called on an breakpoint that you do not expect to have a
+ Python object. */
+static PyObject *
+find_python_obj (const struct breakpoint *b)
+{
+ if (! b->py_bp_object)
+ internal_error (__FILE__, __LINE__,
+ _("Cannot reference Python object in breakpoint."));
+
+ return (PyObject *) b->py_bp_object;
+}
+
+/* Implement the breakpoint_ops "print_one" function. This function
+ calls the corresponding Python function and expects a Tuple in
+ return. This Tuple must have two elements: a Long or None
+ representing the breakpoint address, and a String representing the
+ data printed in the "What" field of the breakpoint information
+ output. */
+static void pybp_print_one (struct breakpoint *b,
+ struct bp_location **bp_loc)
+{
+ PyObject *self;
+ PyObject *result = NULL;
+ PyObject *address;
+ PyObject *method = PyString_FromString ("print_one");
+ struct cleanup *cleanup;
+ struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
+ struct value_print_options opts;
+ PyObject *what_obj;
+ CORE_ADDR bp_addr;
+
+ self = find_python_obj (b);
+ cleanup = ensure_python_env (garch, current_language);
+ make_cleanup_py_decref (method);
+
+ /* Sometimes the bp_location is NULL. Similar to
+ print_one_breakpoint_location if we cannot find a location here,
+ we access it from the breakpoint. */
+ if (*bp_loc != NULL)
+ bp_addr = (*bp_loc)->address;
+ else
+ bp_addr = b->loc->address;
+
+ address = PyLong_FromUnsignedLong (bp_addr);
+
+ if (! address)
+ goto python_error;
+
+ result = PyObject_CallMethodObjArgs (self, method, address, NULL);
+ if (result)
+ {
+ if (! PyTuple_Check (result) || PyTuple_Size (result) != 2)
+ goto type_error;
+ }
+ else
+ goto python_error;
+
+ get_user_print_options (&opts);
+
+ /* Field 4, the address. */
+ if (opts.addressprint)
+ {
+ PyObject *addr_field = PyTuple_GetItem (result, 0);
+
+ if (! addr_field)
+ goto python_error;
+
+ if (addr_field == Py_None)
+ ui_out_field_skip (uiout, "addr");
+ else
+ {
+ CORE_ADDR addr_value;
+
+ if (! PyLong_Check (addr_field))
+ goto type_error;
+
+ addr_value = PyLong_AsUnsignedLong (addr_field);
+
+ /* Check for overflows. */
+ if (PyErr_Occurred ())
+ goto python_error;
+
+ annotate_field (4);
+ ui_out_field_core_addr (uiout, "addr",
+ garch, addr_value);
+ }
+ }
+
+ /* Field 5, 'What' output. */
+ what_obj = PyTuple_GetItem (result, 1);
+ if (! what_obj)
+ goto python_error;
+
+ if (what_obj == Py_None)
+ ui_out_field_skip (uiout, "what");
+ else
+ {
+ char *what_string = NULL;
+
+ if (! gdbpy_is_string (what_obj))
+ goto type_error;
+
+ what_string = gdbpy_obj_to_string (what_obj);
+ if (! what_string)
+ goto python_error;
+
+ annotate_field (5);
+ ui_out_field_string (uiout, "what", what_string);
+ xfree (what_string);
+ }
+
+ Py_XDECREF (result);
+ do_cleanups (cleanup);
+ return;
+
+ type_error:
+ PyErr_SetString (PyExc_RuntimeError,
+ _("Invalid information returned from Python " \
+ "'print_one' function"));
+ gdbpy_print_stack ();
+ Py_XDECREF (result);
+ do_cleanups (cleanup);
+ return;
+
+ python_error:
+ gdbpy_print_stack ();
+ Py_XDECREF (result);
+ do_cleanups (cleanup);
+ return;
+}
+
+/* Implement the breakpoint_ops "print_one_detail" function. This
+ function calls the corresponding Python function and expects a
+ String in return. The returned string will be printed when
+ information on this breakpoint is requested, and the requesting
+ function prints the extra detail line (see info breakpoints). */
+static void
+pybp_print_one_detail (const struct breakpoint *b,
+ struct ui_out *uiout)
+{
+ PyObject *self;
+ PyObject *result = NULL;
+ struct cleanup *cleanup;
+ struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
+ char *detail_str = NULL;
+
+ self = find_python_obj (b);
+ cleanup = ensure_python_env (garch, current_language);
+ result = PyObject_CallMethod (self, "print_one_detail", NULL);
+
+ if (! result)
+ goto python_error;
+
+ if (! gdbpy_is_string (result))
+ goto type_error;
+
+ detail_str = gdbpy_obj_to_string (result);
+ if (! detail_str)
+ goto python_error;
+
+ ui_out_text (uiout, detail_str);
+ xfree (detail_str);
+ Py_XDECREF (result);
+ do_cleanups (cleanup);
+ return;
+
+ type_error:
+ PyErr_SetString (PyExc_RuntimeError,
+ _("The Python function 'print_one_detail' must return " \
+ "a string."));
+ gdbpy_print_stack ();
+ Py_XDECREF (result);
+ do_cleanups (cleanup);
+ return;
+
+ python_error:
+ gdbpy_print_stack ();
+ Py_XDECREF (result);
+ do_cleanups (cleanup);
+ return;
+}
+
+/* Implement the breakpoint_ops "print_mention" function. This function
+ calls the corresponding Python function and expects a String in
+ return. The returned string will be printed when the breakpoint is
+ created. */
+static void
+pybp_print_mention (struct breakpoint *b)
+{
+ PyObject *self;
+ PyObject *result = NULL;
+ struct cleanup *cleanup;
+ struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
+ char *mention_str = NULL;
+
+ self = find_python_obj (b);
+ cleanup = ensure_python_env (garch, current_language);
+ result = PyObject_CallMethod (self, "print_mention", NULL);
+
+ if (! result)
+ goto python_error;
+
+ if (! gdbpy_is_string (result))
+ goto type_error;
+
+ mention_str = gdbpy_obj_to_string (result);
+ if (! mention_str)
+ goto python_error;
+
+ ui_out_text (uiout, mention_str);
+ xfree (mention_str);
+ Py_XDECREF (result);
+ do_cleanups (cleanup);
+ return;
+
+ type_error:
+ PyErr_SetString (PyExc_RuntimeError,
+ _("The Python function 'print_mention' must return " \
+ "a string."));
+ gdbpy_print_stack ();
+ Py_XDECREF (result);
+ do_cleanups (cleanup);
+ return;
+
+ python_error:
+ gdbpy_print_stack ();
+ Py_XDECREF (result);
+ do_cleanups (cleanup);
+ return;
+}
+
+/* Implement the breakpoint_ops "print_it" function. This function
+ calls the corresponding Python function and expects a Tuple in
+ return. This Tuple must have two elements: a String or None
+ representing the breakpoint prelude, and an enum from
+ pybp_print_stop_actions representing the print action of the
+ breakpoint. */
+static enum print_stop_action
+pybp_print_it (struct breakpoint *b)
+{
+ PyObject *self;
+ PyObject *result = NULL;
+ PyObject *py_str = NULL;
+ PyObject *code;
+ struct cleanup *cleanup;
+ struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
+ long action_code = 0;
+ char *desc = NULL;
+ int print_prelude = 1;
+
+ self = find_python_obj (b);
+ cleanup = ensure_python_env (garch, current_language);
+ result = PyObject_CallMethod (self, "print_stop_action", NULL);
+
+ if (result)
+ {
+ make_cleanup_py_decref (result);
+ if (! PyTuple_Check (result) || PyTuple_Size (result) != 2)
+ goto type_error;
+ }
+ else
+ goto python_error;
+
+ py_str = PyTuple_GetItem (result, 0);
+
+ if (! py_str)
+ goto python_error;
+
+ if (py_str == Py_None)
+ print_prelude = 0;
+ else
+ {
+ if (gdbpy_is_string (py_str))
+ {
+ desc = gdbpy_obj_to_string (py_str);
+ if (! desc)
+ goto python_error;
+ make_cleanup (xfree, desc);
+ }
+ else
+ goto type_error;
+ }
+
+ code = PyTuple_GetItem (result, 1);
+ if (! code)
+ goto python_error;
+
+ if (! PyInt_Check (code))
+ goto type_error;
+
+ action_code = PyInt_AsLong (code);
+
+ /* The action_code might just be -1 'as normal' without a
+ Python error, so we have to check both. */
+ if ((action_code == -1) && (PyErr_Occurred ()))
+ goto python_error;
+
+ if ((action_code != PRINT_SRC_AND_LOC)
+ && (action_code != PRINT_SRC_ONLY)
+ && (action_code != PRINT_NOTHING))
+ goto type_error;
+
+ if (print_prelude)
+ ui_out_text (uiout, desc);
+
+ if (ui_out_is_mi_like_p (uiout))
+ {
+ ui_out_field_string (uiout, "reason",
+ async_reason_lookup (EXEC_ASYNC_BREAKPOINT_HIT));
+ ui_out_field_string (uiout, "disp", bpdisp_text (b->disposition));
+ }
+
+ do_cleanups (cleanup);
+
+ return action_code;
+
+ type_error:
+ PyErr_SetString (PyExc_RuntimeError,
+ _("Function print_stop_action has to return " \
+ "a tuple with two elements."));
+ gdbpy_print_stack ();
+ do_cleanups (cleanup);
+ return PRINT_UNKNOWN;
+
+ python_error:
+ gdbpy_print_stack ();
+ do_cleanups (cleanup);
+ return PRINT_UNKNOWN;
+}
+
+/* Build the breakpoint_ops structure. Examine the contents of the
+ Python object and only add the specific functions that have been
+ implemented. */
+static void
+bppy_build_ops (PyObject *self)
+{
+ if (PyObject_HasAttrString (self, "print_stop_action"))
+ py_breakpoint_ops.print_it = pybp_print_it;
+ if (PyObject_HasAttrString (self, "print_mention"))
+ py_breakpoint_ops.print_mention = pybp_print_mention;
+ if (PyObject_HasAttrString (self, "print_one"))
+ py_breakpoint_ops.print_one = pybp_print_one;
+ if (PyObject_HasAttrString (self, "print_one_detail"))
+ py_breakpoint_ops.print_one_detail = pybp_print_one_detail;
+}
+
/* Python function to create a new breakpoint. */
static int
bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
@@ -610,6 +976,7 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
&spec, &type, &access_type, &internal))
return -1;

+ bppy_build_ops (self);
if (internal)
{
internal_bp = PyObject_IsTrue (internal);
@@ -633,17 +1000,23 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
0, bp_breakpoint,
0,
AUTO_BOOLEAN_TRUE,
- NULL, 0, 1, internal_bp);
+ &py_breakpoint_ops,
+ 0, 1, internal_bp,
+ bppy_pending_object);
+
break;
}
case bp_watchpoint:
{
if (access_type == hw_write)
- watch_command_wrapper (spec, 0, internal_bp);
+ watch_command_wrapper (spec, 0, internal_bp,
+ bppy_pending_object);
else if (access_type == hw_access)
- awatch_command_wrapper (spec, 0, internal_bp);
+ awatch_command_wrapper (spec, 0, internal_bp,
+ bppy_pending_object);
else if (access_type == hw_read)
- rwatch_command_wrapper (spec, 0, internal_bp);
+ rwatch_command_wrapper (spec, 0, internal_bp,
+ bppy_pending_object);
else
error(_("Cannot understand watchpoint access type."));
break;
@@ -784,7 +1157,7 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj)
static void
gdbpy_breakpoint_created (struct breakpoint *bp)
{
- breakpoint_object *newbp;
+ breakpoint_object *newbp = NULL;
PyGILState_STATE state;

if (bp->number < 0 && bppy_pending_object == NULL)
@@ -805,12 +1178,25 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
bppy_pending_object = NULL;
}
else
- newbp = PyObject_New (breakpoint_object, &breakpoint_object_type);
+ {
+ /* This breakpoint was not created by a Python API so create a
+ new object, and initialize the other storage elements. */
+ newbp = PyObject_New (breakpoint_object,
+ &breakpoint_object_type);
+ newbp->number = -1;
+ newbp->bp = NULL;
+ }
if (newbp)
{
newbp->number = bp->number;
- newbp->bp = bp;
- newbp->bp->py_bp_object = newbp;
+
+ /* Sometimes the bindings between breakpoint, and breakpoint
+ object are created early in create_breakpoint. If this is
+ the case do not overwrite. */
+ if (! newbp->bp)
+ newbp->bp = bp;
+ if (! newbp->bp->py_bp_object)
+ newbp->bp->py_bp_object = newbp;
Py_INCREF (newbp);
++bppy_live;
}
@@ -887,6 +1273,15 @@ gdbpy_initialize_breakpoints (void)
return;
}

+ /* Add print stop types constants. */
+ for (i = 0; pybp_print_stop_actions[i].name; ++i)
+ {
+ if (PyModule_AddIntConstant (gdb_module,
+ /* Cast needed for Python 2.4. */
+ (char *) pybp_print_stop_actions[i].name,
+ pybp_print_stop_actions[i].code) < 0)
+ return;
+ }
}

\f
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index b65109d..ccfd796 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -130,6 +130,18 @@ typedef struct
PyObject *inf_obj;
} thread_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;
+};
+
extern struct cmd_list_element *set_python_list;
extern struct cmd_list_element *show_python_list;

diff --git a/gdb/python/python.c b/gdb/python/python.c
index 8a7bc66..bea34aa 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -190,6 +190,20 @@ eval_python_from_control_command (struct command_line *cmd)
do_cleanups (cleanup);
}

+/* Bind a breakpoint to a breakpoint object. */
+
+void
+gdbpy_bind_breakpoint (struct breakpoint_object *obj,
+ struct breakpoint *b)
+{
+ if (obj && b)
+ {
+ obj->bp = b;
+ obj->number = b->number;
+ b->py_bp_object = obj;
+ }
+}
+
/* Implementation of the gdb "python" command. */

static void
diff --git a/gdb/python/python.h b/gdb/python/python.h
index ce0eb35..a51b592 100644
--- a/gdb/python/python.h
+++ b/gdb/python/python.h
@@ -32,6 +32,9 @@ void eval_python_from_control_command (struct command_line *);

void source_python_script (FILE *stream, const char *file);

+void gdbpy_bind_breakpoint (struct breakpoint_object *obj,
+ struct breakpoint *b);
+
int apply_val_pretty_printer (struct type *type, const gdb_byte *valaddr,
int embedded_offset, CORE_ADDR address,
struct ui_file *stream, int recurse,
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index f0a83f1..945cfee 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -293,3 +293,72 @@ 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."
+
+# Breakpoint operations.
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+if ![runto_main] then {
+ fail "Cannot run to main."
+ return 0
+}
+delete_breakpoints
+
+set bp_location [gdb_get_line_number "Break at multiply."]
+set bp_location2 [gdb_get_line_number "Break at add."]
+set bp_location3 [gdb_get_line_number "Break at end."]
+
+gdb_py_test_multiple "Breakpoint with operations" \
+ "python" "" \
+ "class bp_oper (gdb.Breakpoint):" "" \
+ " def print_mention (self):" "" \
+ " return \"Special Breakpoint \"+str(self.number)+\" at \"+str(self.location)" "" \
+ " def print_stop_action (self):" "" \
+ " return (\"Testcase Breakpoint, \",gdb.PRINT_SRC_AND_LOC)" "" \
+ " def print_one (self, address):" "" \
+ " return (address,\"Test-case breakpoint\")" "" \
+ " def print_one_detail (self):" "" \
+ " return \"TS breakpoint at \"+str(self.location)+\"\\n\"" "" \
+ "end" ""
+
+gdb_py_test_multiple "Breakpoint with operations" \
+ "python" "" \
+ "class bp_oper_source (bp_oper):" "" \
+ " def print_stop_action (self):" "" \
+ " return (\"Testcase Breakpoint, \",gdb.PRINT_SRC_ONLY)" "" \
+ " return gdb.PRINT_SRC_ONLY" "" \
+ "end" ""
+
+gdb_py_test_multiple "Breakpoint with operations" \
+ "python" "" \
+ "class bp_oper_nothing (bp_oper):" "" \
+ " def print_stop_action (self):" "" \
+ " return (\"Print Nothing\\n\", gdb.PRINT_NOTHING)" "" \
+ "end" ""
+
+gdb_test "python foo = bp_oper(\"$bp_location\")" \
+ "Special Breakpoint.*at.*py-breakpoint\.c:41" \
+ "Test breakpoint ops mention"
+
+gdb_test "continue" ".*Testcase Breakpoint.*$srcfile:$bp_location.*" \
+ "Test PRINT_SRC_AND_LOC with preamble"
+delete_breakpoints
+
+gdb_test "python foo = bp_oper_source(\"$bp_location2\")" \
+ "Special Breakpoint.*at.*py-breakpoint\.c:42" \
+ "Test breakpoint ops mention"
+
+gdb_test "continue" ".*Testcase Breakpoint.*$bp_location2.*" \
+ "Test PRINT_SRC_ONLY with preamble"
+delete_breakpoints
+
+gdb_test "python nothing = bp_oper_nothing(\"$bp_location3\")" \
+ "Special Breakpoint.*at.*py-breakpoint\.c:45" \
+ "Test breakpoint ops mention"
+
+gdb_test "continue" ".*Print Nothing.*" \
+ "Test PRINT_NOTHING with preamble"
+
+gdb_test "info breakpoints" \
+ "breakpoint.*keep.*y.*$hex.*Test-case breakpoint.*TS breakpoint.*at.*py-breakpoint.*" \
+ "Check info breakpoints shows breakpoint op breakpoints"
diff --git a/gdb/testsuite/gdb.python/py-mi-bp.py b/gdb/testsuite/gdb.python/py-mi-bp.py
index e69de29..99c12cd 100644
--- a/gdb/testsuite/gdb.python/py-mi-bp.py
+++ b/gdb/testsuite/gdb.python/py-mi-bp.py
@@ -0,0 +1,32 @@
+# Copyright (C) 2011 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite. It tests python pretty
+# printers.
+
+class mi_bp (gdb.Breakpoint):
+
+ def print_mention (self):
+ return "MI Testcase Breakpoint " + str (self.number) + " at " + str (self.location)
+
+ def print_stop_action (self):
+ return ("MI Testcase Breakpoint, ", gdb.PRINT_SRC_AND_LOC)
+
+ def print_one (self, address):
+ return (address,"MI Testcase Breakpoint")
+
+ def print_one_detail (self):
+ return "MI Testcase Breakpoint at " + str (self.location) + "\n"
+
diff --git a/gdb/testsuite/gdb.python/py-mi.exp b/gdb/testsuite/gdb.python/py-mi.exp
index 2e4d12b..0f1da26 100644
--- a/gdb/testsuite/gdb.python/py-mi.exp
+++ b/gdb/testsuite/gdb.python/py-mi.exp
@@ -17,6 +17,8 @@
# pretty-printing for MI.

load_lib mi-support.exp
+load_lib gdb-python.exp
+
set MIFLAGS "-i=mi2"

gdb_exit
@@ -28,6 +30,8 @@ set testfile "py-mi"
set srcfile py-prettyprint.c
set binfile ${objdir}/${subdir}/${testfile}
set pyfile py-prettyprint.py
+set py_bpfile py-mi-bp.py
+
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug additional_flags=-DMI}] != "" } {
untested ${testfile}.exp
return -1
@@ -284,6 +288,32 @@ mi_list_varobj_children nstype2 {
{ {nstype2.<error at 0>} {<error at 0>} 6 {char \[6\]} }
} "list children after setting exception flag"

+# Test breakpoint ops
+gdb_exit
+if [mi_gdb_start] {
+ continue
+}
+
+mi_delete_breakpoints
+mi_gdb_reinitialize_dir $srcdir/$subdir
+mi_gdb_load ${binfile}
+mi_runto main
+set bp_location [gdb_get_line_number {MI breakpoint here} ${srcfile}]
+set remote_python_bp_file [remote_download host ${srcdir}/${subdir}/${py_bpfile}]
+mi_gdb_test "python execfile ('${remote_python_bp_file}')" ""
+
+mi_gdb_test "python mi_bp_i = mi_bp(\"$bp_location\")" \
+ ".*\"MI Testcase Breakpoint.*=breakpoint-created,bkpt=\{number=\".*\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\".*\",what=\"MI Testcase Breakpoint\",times=\"0\",original-location=\".*\"\}.*" \
+ "Test breakpoint ops mention"
+
+mi_gdb_test "continue" \
+ ".*breakpoint-modified,bkpt={number=\".*\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\".*\",what=\"MI Testcase Breakpoint\",times=\"1\",original-location=\".*py-prettyprint.c.*\".*MI Testcase Breakpoint, main.*at.*py-prettyprint.c.*" \
+ "Test print_one"
+
+mi_gdb_test "-break-list" \
+ ".*BreakpointTable=\{nr_rows=\"1\",nr_cols=\"6\",hdr=\\\[\{width=\"7\",alignment=\"-1\",col_name=\"number\",colhdr=\"Num\"\},\{width=\"14\",alignment=\"-1\",col_name=\"type\",colhdr=\"Type\"\},\{width=\"4\",alignment=\"-1\",col_name=\"disp\",colhdr=\"Disp\"\},\{width=\"3\",alignment=\"-1\",col_name=\"enabled\",colhdr=\"Enb\"\},\{width=\"18\",alignment=\"-1\",col_name=\"addr\",colhdr=\"Address\"\},\{width=\"40\",alignment=\"2\",col_name=\"what\",colhdr=\"What\"\}\\\],body=\\\[bkpt=\{number=\"2\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\".*\",what=\"MI Testcase Breakpoint\",times=\"1\",original-location=\".*py-prettyprint.c.*\"\}\\\]\}" \
+ "list of breakpoints"
+
# C++ MI tests
gdb_exit
if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" \