This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [patch][python] Add breakpoint support.


On 04/05/2010 05:26 PM, Joel Brobecker wrote:

Here is the latest cut of the patch with comments in-line. 

Eli, I've also attached the latest documentation patch, together with
the code changes you commented on.


> General question: How come the python error strings are not i18n'ed?
> For instance: 
> 
>> +      PyErr_SetString (PyExc_TypeError, "cannot delete `silent' attribute");
> 
> This is definitely the standard, currently. Is that normal?


In the archer repository some are and some aren't (for whatever reason).  I just
missed this in my porting check-list.  Thanks for catching it.



>> +/* From breakpoint.c.  */
>> +extern struct breakpoint *breakpoint_chain;
> 
> This shouldn't be needed.  The only use I found was:
> 
>> +  for (bp = breakpoint_chain; bp; bp = bp->next)
>> +    if (bp->number == num)
>> +      break;
> 
> But there is a public function that should return the breakpoint you
> are looking for: get_breakpoint.


OK, fixed.

 
>> +/* Evaluate to true if the breakpoint NUM is valid, false otherwise.  */
>> +#define BPPY_VALID_P(Num)			\
>> +    ((Num) >= 0					\
>> +     && (Num) < bppy_slots			\
>> +     && bppy_breakpoints[Num] != NULL)
> 
> Personally (and I litterally mean it, it's a personal preference),
> I'd rather we did not use macros, but a real function... Being a static
> function, I'm sure the compiler will be capable of performing whatever
> is best for performance (and that way, we can avoid the parens around
> "Num", and the "do/while(0)" dance in the macro that follows).


The use of macros to check consistency of various GDB objects in the
Python API seems pretty consistent with the code that is already in
FSF CVS, and the code that is still in the Archer GIT repository .  As
a lot of this code is an effort from several different people over
several periods of time, I cannot answer why this trend became the
default.  In many respects, I'm porting code that I may not have
necessarily implemented that particular way, but I don't want to brand
my own particular thinking or bias on something that is ok.  

This is just a long way of saying I do not like macros either, but as
they are consistently used I've just let it be taken as a particular
style.  That being said, I have no problem (happily!) removing their
use; but I would like to do it as a separate effort in separate patch
that impacts all of the Python API. Doing it now with breakpoints
would bring no additional benefit -- I'd rather do it in one sweep and
keep the style consistent. What do you think?

 
>> +  breakpoint_object *self_bp = (breakpoint_object *) self;
>> +  if (self_bp->bp)
> 
> Can you add an empty line after the declaration(s)?


OK.

> 
>> +/* Python function to set the thread of a breakpoint.  */
>> +static int
>> +bppy_set_thread (PyObject *self, PyObject *newvalue, void *closure)
> 
> Small request: Can we have the same for tasks (Ada tasks)? The function
> that allows you to verify that a given task number is valid is:
> 
>         extern int valid_task_id (int);
> 
> Similarly, we can also have a getter for the breakpoint task ID?


OK, added -- please check the logic.

 
>> +  if (bp->type != bp_breakpoint && bp->type != bp_watchpoint
>> +      && bp->type != bp_hardware_watchpoint  
>> +      && bp->type != bp_read_watchpoint
>> +      && bp->type != bp_access_watchpoint)
> 
> Small nit-pick? Since we're aligning all conditions, can we split the
> first line in two?
> 
>      if (bp->type != bp_breakpoint
>          && bp->type != bp_watchpoint
>          && bp->type != bp_hardware_watchpoint  
>          && bp->type != bp_read_watchpoint
>          && bp->type != bp_access_watchpoint)
> 
> I think it'll be easier to not miss the "!= bp_watchpoint" condition.


OK.

>> +PyObject *
>> +gdbpy_get_hook_function (const char *name)
> 
> This function needs to be documented...


Looking at it, it is actually redundant.  I removed it, and the
reference to it in gdbpy_breakpoint_created.


> All the above can be simplified into:
> 
>     set testfile "py-breakpoint"
>     set srcfile ${testfile}.c
>     if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
>         return -1
>     }


Thanks, and nice tip. Much clearer!


 
> I mention this, because people rarely seem to know about this...
> 
>> +gdb_test "python print blist\[0\].location" "main." "Check breakpoint location"
>                                                    ^^^
>    Can you check whether you might need to escape the period, here?
> 

Nope, I'm looking for:
 
python print blist[0].location

main^M

>> +# Start with a fresh gdb.
>> +gdb_exit
>> +gdb_start
>> +gdb_reinitialize_dir $srcdir/$subdir
>> +gdb_load ${binfile}
> 
> You can replace this with a call to clean_restart.
> 


Thanks, also much neater

Cheers,

Phil

--

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 72b546d..b507d3e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -268,6 +268,7 @@ SUBDIR_TUI_CFLAGS= \
 SUBDIR_PYTHON_OBS = \
 	python.o \
 	py-block.o \
+	py-breakpoint.o \
 	py-cmd.o \
 	py-frame.o \
 	py-function.o \
@@ -282,6 +283,7 @@ SUBDIR_PYTHON_OBS = \
 SUBDIR_PYTHON_SRCS = \
 	python/python.c \
 	python/py-block.c \
+	python/py-breakpoint.c \
 	python/py-cmd.c \
 	python/py-frame.c \
 	python/py-function.c \
@@ -1986,6 +1988,10 @@ py-block.o: $(srcdir)/python/py-block.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-block.c
 	$(POSTCOMPILE)
 
+py-breakpoint.o: $(srcdir)/python/py-breakpoint.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-breakpoint.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 3d12fbe..8ccdc08 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -25,8 +25,8 @@
 
 * Python scripting
 
-** The GDB Python API now has access to symbols, symbol tables, and
-   frame's code blocks.
+** The GDB Python API now has access to breakpoints, symbols, symbol
+   tables, and frame's code blocks.
 
 ** New methods gdb.target_charset and gdb.target_wide_charset.
 
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 36a28f6..49ea011 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -709,6 +709,61 @@ get_breakpoint (int num)
 }
 
 
+
+void
+set_breakpoint_condition (struct breakpoint *b, char *exp,
+			  int from_tty)
+{
+  struct bp_location *loc = b->loc;
+
+  for (; loc; loc = loc->next)
+    {
+      xfree (loc->cond);
+      loc->cond = NULL;
+    }
+  xfree (b->cond_string);
+  b->cond_string = NULL;
+  xfree (b->cond_exp);
+  b->cond_exp = NULL;
+
+  if (*exp == 0)
+    {
+      if (from_tty)
+	printf_filtered (_("Breakpoint %d now unconditional.\n"), b->number);
+    }
+  else
+    {
+      char *arg = exp;
+      /* I don't know if it matters whether this is the string the user
+	 typed in or the decompiled expression.  */
+      b->cond_string = xstrdup (arg);
+      b->condition_not_parsed = 0;
+
+      if (is_watchpoint (b))
+	{
+	  innermost_block = NULL;
+	  arg = exp;
+	  b->cond_exp = parse_exp_1 (&arg, 0, 0);
+	  if (*arg)
+	    error (_("Junk at end of expression"));
+	  b->cond_exp_valid_block = innermost_block;
+	}
+      else
+	{
+	  for (loc = b->loc; loc; loc = loc->next)
+	    {
+	      arg = exp;
+	      loc->cond =
+		parse_exp_1 (&arg, block_for_pc (loc->address), 0);
+	      if (*arg)
+		error (_("Junk at end of expression"));
+	    }
+	}
+    }
+  breakpoints_changed ();
+  observer_notify_breakpoint_modified (b->number);
+}
+
 /* condition N EXP -- set break condition of breakpoint N to EXP.  */
 
 static void
@@ -729,53 +784,7 @@ condition_command (char *arg, int from_tty)
   ALL_BREAKPOINTS (b)
     if (b->number == bnum)
       {
-	struct bp_location *loc = b->loc;
-	for (; loc; loc = loc->next)
-	  {
-	    xfree (loc->cond);
-	    loc->cond = NULL;
-	  }
-	xfree (b->cond_string);
-	b->cond_string = NULL;
-	xfree (b->cond_exp);
-	b->cond_exp = NULL;
-
-	if (*p == 0)
-	  {
-	    if (from_tty)
-	      printf_filtered (_("Breakpoint %d now unconditional.\n"), bnum);
-	  }
-	else
-	  {
-	    arg = p;
-	    /* I don't know if it matters whether this is the string the user
-	       typed in or the decompiled expression.  */
-	    b->cond_string = xstrdup (arg);
-	    b->condition_not_parsed = 0;
-
-	    if (is_watchpoint (b))
-	      {
-		innermost_block = NULL;
-		arg = p;
-		b->cond_exp = parse_exp_1 (&arg, 0, 0);
-		if (*arg)
-		  error (_("Junk at end of expression"));
-		b->cond_exp_valid_block = innermost_block;
-	      }
-	    else
-	      {
-		for (loc = b->loc; loc; loc = loc->next)
-		  {
-		    arg = p;
-		    loc->cond =
-		      parse_exp_1 (&arg, block_for_pc (loc->address), 0);
-		    if (*arg)
-		      error (_("Junk at end of expression"));
-		  }
-	      }
-	  }
-	breakpoints_changed ();
-	observer_notify_breakpoint_modified (b->number);
+	set_breakpoint_condition (b, arg, from_tty);
 	return;
       }
 
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 912cc3c..9963827 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -1005,6 +1005,10 @@ extern int breakpoints_always_inserted_mode (void);
    in our opinion won't ever trigger.  */
 extern void breakpoint_retire_moribund (void);
 
+/* Set break condition of breakpoint B to EXP.  */
+extern void set_breakpoint_condition (struct breakpoint *b, char *exp,
+				      int from_tty);
+
 /* Checks if we are catching syscalls or not.
    Returns 0 if not, greater than 0 if we are.  */
 extern int catch_syscall_enabled (void);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index fd8b0bc..f157690 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -19710,6 +19710,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown.
 * Symbols In Python::           Python representation of symbols.
 * Symbol Tables In Python::     Python representation of symbol tables.
 * Lazy Strings In Python::      Python representation of lazy strings.
+* Breakpoints In Python::       Manipulating breakpoints using Python.
 @end menu
 
 @node Basic Python
@@ -19735,6 +19736,12 @@ command as having originated from the user invoking it interactively.
 It must be a boolean value.  If omitted, it defaults to @code{False}.
 @end defun
 
+@findex gdb.breakpoints
+@defun breakpoints
+Return a sequence holding all of @value{GDBN}'s breakpoints.
+@xref{Breakpoints In Python}, for more information.
+@end defun
+
 @findex gdb.parameter
 @defun parameter parameter
 Return the value of a @value{GDBN} parameter.  @var{parameter} is a
@@ -21275,6 +21282,163 @@ Return the symbol table's source absolute file name.
 @end defmethod
 @end table
 
+@node Breakpoints In Python
+@subsubsection Manipulating breakpoints using Python
+
+@cindex breakpoints in python
+@tindex gdb.Breakpoint
+
+Python code can manipulate breakpoints via the @code{gdb.Breakpoint}
+class.
+
+@defmethod Breakpoint __init__ spec @r{[}type@r{]} @r{[}wp_class@r{]}
+Create a new breakpoint.  @var{spec} is a string naming the
+location of the breakpoint, or an expression that defines a
+watchpoint.  The contents can be any location recognized by the
+@code{break} command, or in the case of a watchpoint, by the @code{watch}
+command.  The optional @var{type} denotes the breakpoint to create
+from the types defined later in this chapter.  This argument can be
+either: @code{BP_BREAKPOINT} or @code{BP_WATCHPOINT}.  @var{type}
+defaults to @code{BP_BREAKPOINT}.  The optional @var{wp_class}
+argument defines the class of watchpoint to create, if @var{type} is
+defined as @code{BP_WATCHPOINT}.  If a watchpoint class is not
+provided, it is assumed to be a @var{WP_WRITE} class.
+@end defmethod
+
+The available watchpoint types represented by constants are defined in the
+@code{gdb} module:
+
+@table @code
+@findex WP_READ
+@findex gdb.WP_READ
+@item WP_READ
+Read only watchpoint.
+
+@findex WP_WRITE
+@findex gdb.WP_WRITE
+@item WP_WRITE
+Write only watchpoint.
+
+@findex WP_ACCESS
+@findex gdb.WP_ACCESS
+@item WP_ACCESS
+Read/Write watchpoint.
+@end table
+
+@defmethod Breakpoint is_valid
+Return @code{True} if this @code{Breakpoint} object is valid,
+@code{False} otherwise.  A @code{Breakpoint} object can become invalid
+if the user deletes the breakpoint.  In this case, the object still
+exists, but the underlying breakpoint does not.  In the cases of
+watchpoint scope, the watchpoint remains valid even if execution of the
+inferior leaves the scope of that watchpoint.
+@end defmethod
+
+@defivar Breakpoint enabled
+This attribute is @code{True} if the breakpoint is enabled, and
+@code{False} otherwise.  This attribute is writable.
+@end defivar
+
+@defivar Breakpoint silent
+This attribute is @code{True} if the breakpoint is silent, and
+@code{False} otherwise.  This attribute is writable.
+
+Note that a breakpoint can also be silent if it has commands and the
+first command is @code{silent}.  This is not reported by the
+@code{silent} attribute.
+@end defivar
+
+@defivar Breakpoint thread
+If the breakpoint is thread-specific, this attribute holds the thread
+id.  If the breakpoint is not thread-specific, this attribute is
+@code{None}.  This attribute is writable.
+@end defivar
+
+@defivar Breakpoint task
+If the breakpoint is Ada task-specific, this attribute holds the Ada task
+id.  If the breakpoint is not task-specific (or the underlying
+language is not Ada), this attribute is @code{None}.  This attribute
+is writable.
+@end defivar
+
+@defivar Breakpoint ignore_count
+This attribute holds the ignore count for the breakpoint, an integer.
+This attribute is writable.
+@end defivar
+
+@defivar Breakpoint number
+This attribute holds the breakpoint's number --- the identifier used by
+the user to manipulate the breakpoint.  This attribute is not writable.
+@end defivar
+
+@defivar Breakpoint type
+This attribute holds the breakpoint's type --- the identifier used to
+determine the actual breakpoint type or use-case.  This attribute is not
+writable.
+@end defivar
+
+The available types are represented by constants defined in the @code{gdb}
+module:
+
+@table @code
+@findex BP_BREAKPOINT
+@findex gdb.BP_BREAKPOINT
+@item BP_BREAKPOINT
+Normal code breakpoint.
+
+@findex BP_WATCHPOINT
+@findex gdb.BP_WATCHPOINT
+@item BP_WATCHPOINT
+Watchpoint breakpoint.
+
+@findex BP_HARDWARE_WATCHPOINT
+@findex gdb.BP_HARDWARE_WATCHPOINT
+@item BP_HARDWARE_WATCHPOINT
+Hardware assisted watchpoint.
+
+@findex BP_READ_WATCHPOINT
+@findex gdb.BP_READ_WATCHPOINT
+@item BP_READ_WATCHPOINT
+Hardware assisted read watchpoint.
+
+@findex BP_ACCESS_WATCHPOINT
+@findex gdb.BP_ACCESS_WATCHPOINT
+@item BP_ACCESS_WATCHPOINT
+Hardware assisted access watchpoint.
+@end table
+
+@defivar Breakpoint hit_count
+This attribute holds the hit count for the breakpoint, an integer.
+This attribute is writable, but currently it can only be set to zero.
+@end defivar
+
+@defivar Breakpoint location
+This attribute holds the location of the breakpoint, as specified by
+the user.  It is a string.  If the breakpoint does not have a location
+(that is, it is a watchpoint) the attribute's value is @code{None}.  This
+attribute is not writable.
+@end defivar
+
+@defivar Breakpoint expression
+This attribute holds a breakpoint expression, as specified by
+the user.  It is a string.  If the breakpoint does not have an
+expression (the breakpoint is not a watchpoint) the attribute's value
+is @code{None}.  This attribute is not writable.
+@end defivar
+
+@defivar Breakpoint condition
+This attribute holds the condition of the breakpoint, as specified by
+the user.  It is a string.  If there is no condition, this attribute's
+value is @code{None}.  This attribute is writable.
+@end defivar
+
+@defivar Breakpoint commands
+This attribute holds the commands attached to the breakpoint.  If
+there are commands, this attribute's value is a string holding all the
+commands, separated by newlines.  If there are no commands, this
+attribute is @code{None}.  This attribute is not writable.
+@end defivar
+
 @node Lazy Strings In Python
 @subsubsection Python representation of lazy strings.
 
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
new file mode 100644
index 0000000..a25080c
--- /dev/null
+++ b/gdb/python/py-breakpoint.c
@@ -0,0 +1,856 @@
+/* Python interface to breakpoints
+
+   Copyright (C) 2008, 2009, 2010 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"
+#include "gdbthread.h"
+#include "observer.h"
+#include "cli/cli-script.h"
+#include "ada-lang.h"
+
+/* From breakpoint.c.  */
+typedef struct breakpoint_object breakpoint_object;
+
+static PyTypeObject breakpoint_object_type;
+
+/* 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;
+};
+
+/* Evaluate to true if the breakpoint NUM is valid, false otherwise.  */
+#define BPPY_VALID_P(Num)			\
+    ((Num) >= 0					\
+     && (Num) < bppy_slots			\
+     && bppy_breakpoints[Num] != NULL)
+
+/* Require that BREAKPOINT be a valid breakpoint ID; throw a Python
+   exception if it is invalid.  */
+#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)
+
+/* Require that BREAKPOINT be a valid breakpoint ID; throw a Python
+   exception if it is invalid.  This macro is for use in setter functions.  */
+#define BPPY_SET_REQUIRE_VALID(Breakpoint)				\
+    do {								\
+      if (! BPPY_VALID_P ((Breakpoint)->number))			\
+        {								\
+	  PyErr_Format (PyExc_RuntimeError, _("Breakpoint %d is invalid."), \
+			(Breakpoint)->number);				\
+	  return -1;							\
+	}								\
+    } while (0)
+
+/* This is used to initialize various gdb.bp_* constants.  */
+struct pybp_code
+{
+  /* The name.  */
+  const char *name;
+  /* The code.  */
+  enum type_code code;
+};
+
+/* Entries related to the type of user set breakpoints.  */
+static struct pybp_code pybp_codes[] =
+{
+  { "BP_NONE", bp_none},
+  { "BP_BREAKPOINT", bp_breakpoint},
+  { "BP_WATCHPOINT", bp_watchpoint},
+  { "BP_HARDWARE_WATCHPOINT", bp_hardware_watchpoint},
+  { "BP_READ_WATCHPOINT", bp_read_watchpoint},
+  { "BP_ACCESS_WATCHPOINT", bp_access_watchpoint},
+  {NULL} /* Sentinel. */
+};
+
+/* Entries related to the type of watchpoint.  */
+static struct pybp_code pybp_watch_types[] =
+{
+  { "WP_READ", hw_read},
+  { "WP_WRITE", hw_write},
+  { "WP_ACCESS", hw_access},
+  {NULL} /* Sentinel. */
+};
+
+/* Python function which checks the validity of a breakpoint object.  */
+static PyObject *
+bppy_is_valid (PyObject *self, PyObject *args)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+
+  if (self_bp->bp)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+/* Python function to test whether or not the breakpoint is enabled.  */
+static PyObject *
+bppy_get_enabled (PyObject *self, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (self_bp);
+  if (! self_bp->bp)
+    Py_RETURN_FALSE;
+  if (self_bp->bp->enable_state == bp_enabled)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+/* Python function to test whether or not the breakpoint is silent.  */
+static PyObject *
+bppy_get_silent (PyObject *self, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (self_bp);
+  if (self_bp->bp->silent)
+    Py_RETURN_TRUE;
+  Py_RETURN_FALSE;
+}
+
+/* Python function to set the enabled state of a breakpoint.  */
+static int
+bppy_set_enabled (PyObject *self, PyObject *newvalue, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  int cmp;
+
+  BPPY_SET_REQUIRE_VALID (self_bp);
+
+  if (newvalue == NULL)
+    {
+      PyErr_SetString (PyExc_TypeError, 
+		       _("Cannot delete `enabled' attribute."));
+
+      return -1;
+    }
+  else if (! PyBool_Check (newvalue))
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("The value of `enabled' must be a boolean."));
+      return -1;
+    }
+
+  cmp = PyObject_IsTrue (newvalue);
+  if (cmp < 0)
+    return -1;
+  else if (cmp == 1)
+    enable_breakpoint (self_bp->bp);
+  else 
+    disable_breakpoint (self_bp->bp);
+  return 0;
+}
+
+/* Python function to set the 'silent' state of a breakpoint.  */
+static int
+bppy_set_silent (PyObject *self, PyObject *newvalue, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  int cmp;
+
+  BPPY_SET_REQUIRE_VALID (self_bp);
+
+  if (newvalue == NULL)
+    {
+      PyErr_SetString (PyExc_TypeError, 
+		       _("Cannot delete `silent' attribute."));
+      return -1;
+    }
+  else if (! PyBool_Check (newvalue))
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("The value of `silent' must be a boolean."));
+      return -1;
+    }
+
+  cmp = PyObject_IsTrue (newvalue);
+  if (cmp < 0)
+    return -1;
+  else
+    self_bp->bp->silent = cmp;
+
+  return 0;
+}
+
+/* Python function to set the thread of a breakpoint.  */
+static int
+bppy_set_thread (PyObject *self, PyObject *newvalue, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  int id;
+
+  BPPY_SET_REQUIRE_VALID (self_bp);
+
+  if (newvalue == NULL)
+    {
+      PyErr_SetString (PyExc_TypeError, 
+		       _("Cannot delete `thread' attribute."));
+      return -1;
+    }
+  else if (PyInt_Check (newvalue))
+    {
+      id = (int) PyInt_AsLong (newvalue);
+      if (! valid_thread_id (id))
+	{
+	  PyErr_SetString (PyExc_RuntimeError, 
+			   _("Invalid thread ID."));
+	  return -1;
+	}
+    }
+  else if (newvalue == Py_None)
+    id = -1;
+  else
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("The value of `thread' must be an integer or None."));
+      return -1;
+    }
+
+  self_bp->bp->thread = id;
+
+  return 0;
+}
+
+/* Python function to set the (ADA) task of a breakpoint.  */
+static int
+bppy_set_task (PyObject *self, PyObject *newvalue, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  int id;
+
+  BPPY_SET_REQUIRE_VALID (self_bp);
+
+  if (newvalue == NULL)
+    {
+      PyErr_SetString (PyExc_TypeError, 
+		       _("Cannot delete `task' attribute."));
+      return -1;
+    }
+  else if (PyInt_Check (newvalue))
+    {
+      id = (int) PyInt_AsLong (newvalue);
+      if (! valid_task_id (id))
+	{
+	  PyErr_SetString (PyExc_RuntimeError, 
+			   _("Invalid task ID."));
+	  return -1;
+	}
+    }
+  else if (newvalue == Py_None)
+    id = 0;
+  else
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("The value of `ask' must be an integer or None."));
+      return -1;
+    }
+
+  self_bp->bp->task = id;
+
+  return 0;
+}
+
+
+/* Python function to set the ignore count of a breakpoint.  */
+static int
+bppy_set_ignore_count (PyObject *self, PyObject *newvalue, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  long value;
+
+  BPPY_SET_REQUIRE_VALID (self_bp);
+
+  if (newvalue == NULL)
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("Cannot delete `ignore_count' attribute."));
+      return -1;
+    }
+  else if (! PyInt_Check (newvalue))
+    {
+      PyErr_SetString (PyExc_TypeError,
+		       _("The value of `ignore_count' must be an integer."));
+      return -1;
+    }
+
+  value = PyInt_AsLong (newvalue);
+  if (value < 0)
+    value = 0;
+  set_ignore_count (self_bp->number, (int) value, 0);
+
+  return 0;
+}
+
+/* Python function to set the hit count of a breakpoint.  */
+static int
+bppy_set_hit_count (PyObject *self, PyObject *newvalue, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+
+  BPPY_SET_REQUIRE_VALID (self_bp);
+
+  if (newvalue == NULL)
+    {
+      PyErr_SetString (PyExc_TypeError, 
+		       _("Cannot delete `hit_count' attribute."));
+      return -1;
+    }
+  else if (! PyInt_Check (newvalue) || PyInt_AsLong (newvalue) != 0)
+    {
+      PyErr_SetString (PyExc_AttributeError,
+		       _("The value of `hit_count' must be zero."));
+      return -1;
+    }
+
+  self_bp->bp->hit_count = 0;
+
+  return 0;
+}
+
+/* Python function to get the location of a breakpoint.  */
+static PyObject *
+bppy_get_location (PyObject *self, void *closure)
+{
+  char *str;
+  breakpoint_object *obj = (breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (obj);
+
+  if (obj->bp->type != bp_breakpoint)
+    Py_RETURN_NONE;
+
+  str = obj->bp->addr_string;
+
+  if (! str)
+    str = "";
+  return PyString_Decode (str, strlen (str), host_charset (), NULL);
+}
+
+/* Python function to get the breakpoint expression.  */
+static PyObject *
+bppy_get_expression (PyObject *self, void *closure)
+{
+  char *str;
+  breakpoint_object *obj = (breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (obj);
+
+  if (obj->bp->type != bp_watchpoint
+      && obj->bp->type != bp_hardware_watchpoint  
+      && obj->bp->type != bp_read_watchpoint
+      && obj->bp->type != bp_access_watchpoint)
+    Py_RETURN_NONE;
+
+  str = obj->bp->exp_string;
+  if (! str)
+    str = "";
+
+  return PyString_Decode (str, strlen (str), host_charset (), NULL);
+}
+
+/* Python function to get the condition expression of a breakpoint.  */
+static PyObject *
+bppy_get_condition (PyObject *self, void *closure)
+{
+  char *str;
+  breakpoint_object *obj = (breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (obj);
+
+  str = obj->bp->cond_string;
+  if (! str)
+    Py_RETURN_NONE;
+
+  return PyString_Decode (str, strlen (str), host_charset (), NULL);
+}
+
+static int
+bppy_set_condition (PyObject *self, PyObject *newvalue, void *closure)
+{
+  char *exp;
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  volatile struct gdb_exception except;
+
+  BPPY_SET_REQUIRE_VALID (self_bp);
+
+  if (newvalue == NULL)
+    {
+      PyErr_SetString (PyExc_TypeError, 
+		       _("Cannot delete `condition' attribute."));
+      return -1;
+    }
+  else if (newvalue == Py_None)
+    exp = "";
+  else
+    {
+      exp = python_string_to_host_string (newvalue);
+      if (exp == NULL)
+	return -1;
+    }
+
+  TRY_CATCH (except, RETURN_MASK_ALL)
+    {
+      set_breakpoint_condition (self_bp->bp, exp, 0);
+    }
+  GDB_PY_SET_HANDLE_EXCEPTION (except);
+
+  return 0;
+}
+
+/* Python function to get the commands attached to a breakpoint.  */
+static PyObject *
+bppy_get_commands (PyObject *self, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+  struct breakpoint *bp = self_bp->bp;
+  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)
+    {
+      ui_out_redirect (uiout, string_file);
+      print_command_lines (uiout, breakpoint_commands (bp), 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);
+  do_cleanups (chain);
+  xfree (cmdstr);
+  return result;
+}
+
+/* Python function to get the breakpoint type.  */
+static PyObject *
+bppy_get_type (PyObject *self, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  return PyInt_FromLong (self_bp->bp->type);
+}
+
+/* Python function to get the breakpoint's number.  */
+static PyObject *
+bppy_get_number (PyObject *self, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  return PyInt_FromLong (self_bp->number);
+}
+
+/* Python function to get the breakpoint's thread ID.  */
+static PyObject *
+bppy_get_thread (PyObject *self, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  if (self_bp->bp->thread == -1)
+    Py_RETURN_NONE;
+
+  return PyInt_FromLong (self_bp->bp->thread);
+}
+
+/* Python function to get the breakpoint's task ID (in ADA).  */
+static PyObject *
+bppy_get_task (PyObject *self, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  if (self_bp->bp->task == 0)
+    Py_RETURN_NONE;
+
+  return PyInt_FromLong (self_bp->bp->task);
+}
+
+/* Python function to get the breakpoint's hit count.  */
+static PyObject *
+bppy_get_hit_count (PyObject *self, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  return PyInt_FromLong (self_bp->bp->hit_count);
+}
+
+/* Python function to get the breakpoint's ignore count.  */
+static PyObject *
+bppy_get_ignore_count (PyObject *self, void *closure)
+{
+  breakpoint_object *self_bp = (breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  return PyInt_FromLong (self_bp->bp->ignore_count);
+}
+
+/* Python function to create a new breakpoint.  */
+static PyObject *
+bppy_new (PyTypeObject *subtype, PyObject *args, PyObject *kwargs)
+{
+  PyObject *result;
+  static char *keywords[] = { "spec", "type", "wp_class", NULL };
+  char *spec;
+  int type = bp_breakpoint;
+  int access_type = hw_write;
+  volatile struct gdb_exception except;
+
+  if (! PyArg_ParseTupleAndKeywords (args, kwargs, "s|ii", keywords,
+				     &spec, &type, &access_type))
+    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)
+    {
+      switch (type)
+	{
+	case bp_breakpoint:
+	  {
+	    create_breakpoint (python_gdbarch,
+			       spec, NULL, -1,
+			       0,
+			       0, 0, 0,
+			       0,
+			       AUTO_BOOLEAN_TRUE,
+			       NULL, 0, 1);
+	    break;
+	  }
+        case bp_watchpoint:
+	  {
+	    if (access_type == hw_write)
+	      watch_command_wrapper (spec, 0);
+	    else if (access_type == hw_access)
+	      awatch_command_wrapper (spec, 0);
+	    else if (access_type == hw_read)
+	      rwatch_command_wrapper (spec, 0);
+	    else
+	      error(_("Cannot understand watchpoint access type."));
+	    break;
+	  }
+	default:
+	  error(_("Do not understand breakpoint type to set."));
+	}
+    }
+  if (except.reason < 0)
+    {
+      subtype->tp_free (result);
+      return PyErr_Format (except.reason == RETURN_QUIT
+			   ? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
+			   "%s", except.message);
+    }
+
+  BPPY_REQUIRE_VALID ((breakpoint_object *) result);
+  return result;
+}
+
+
+
+/* Static function to return a tuple holding all breakpoints.  */
+
+PyObject *
+gdbpy_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;
+}
+
+
+
+/* Event callback functions.  */
+
+/* Callback that is used when a breakpoint is created.  This function
+   will create a new Python breakpoint object.  */
+static void
+gdbpy_breakpoint_created (int num)
+{
+  breakpoint_object *newbp;
+  struct breakpoint *bp = NULL;
+  PyGILState_STATE state;
+
+  if (num < 0)
+    return;
+
+  bp = get_breakpoint (num);
+  if (! bp)
+    return;
+
+  if (bp->type != bp_breakpoint 
+      && bp->type != bp_watchpoint
+      && bp->type != bp_hardware_watchpoint  
+      && bp->type != bp_read_watchpoint
+      && bp->type != bp_access_watchpoint)
+    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;
+
+  state = PyGILState_Ensure ();
+
+  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;
+      Py_INCREF (newbp);
+    }
+
+  /* Just ignore errors here.  */
+  PyErr_Clear ();
+
+  PyGILState_Release (state);
+}
+
+/* Callback that is used when a breakpoint is deleted.  This will
+   invalidate the corresponding Python object.  */
+static void
+gdbpy_breakpoint_deleted (int num)
+{
+  PyGILState_STATE state;
+
+  state = PyGILState_Ensure ();
+  if (BPPY_VALID_P (num))
+    {
+      bppy_breakpoints[num]->bp = NULL;
+      Py_DECREF (bppy_breakpoints[num]);
+      bppy_breakpoints[num] = NULL;
+      --bppy_live;
+    }
+  PyGILState_Release (state);
+}
+
+
+
+/* Initialize the Python breakpoint code.  */
+void
+gdbpy_initialize_breakpoints (void)
+{
+  int i;
+
+  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);
+
+  observer_attach_breakpoint_created (gdbpy_breakpoint_created);
+  observer_attach_breakpoint_deleted (gdbpy_breakpoint_deleted);
+
+  /* Add breakpoint types constants.  */
+  for (i = 0; pybp_codes[i].name; ++i)
+    {
+      if (PyModule_AddIntConstant (gdb_module,
+				   /* Cast needed for Python 2.4.  */
+				   (char *) pybp_codes[i].name,
+				   pybp_codes[i].code) < 0)
+	return;
+    }
+
+  /* Add watchpoint types constants.  */
+  for (i = 0; pybp_watch_types[i].name; ++i)
+    {
+      if (PyModule_AddIntConstant (gdb_module,
+				   /* Cast needed for Python 2.4.  */
+				   (char *) pybp_watch_types[i].name,
+				   pybp_watch_types[i].code) < 0)
+	return;
+    }
+
+}
+
+
+
+static PyGetSetDef breakpoint_object_getset[] = {
+  { "enabled", bppy_get_enabled, bppy_set_enabled,
+    "Boolean telling whether the breakpoint is enabled.", NULL },
+  { "silent", bppy_get_silent, bppy_set_silent,
+    "Boolean telling whether the breakpoint is silent.", NULL },
+  { "thread", bppy_get_thread, bppy_set_thread,
+    "Thread ID for the breakpoint.\n\
+If the value is a thread ID (integer), then this is a thread-specific breakpoint.\n\
+If the value is None, then this breakpoint is not thread-specific.\n\
+No other type of value can be used.", NULL },
+  { "task", bppy_get_task, bppy_set_task,
+    "Thread ID for the breakpoint.\n\
+If the value is a task ID (integer), then this is an ADA task-specific breakpoint.\n\
+If the value is None, then this breakpoint is not task-specific.\n\
+No other type of value can be used.", NULL },
+  { "ignore_count", bppy_get_ignore_count, bppy_set_ignore_count,
+    "Number of times this breakpoint should be automatically continued.",
+    NULL },
+  { "number", bppy_get_number, NULL,
+    "Breakpoint's number assigned by GDB.", NULL },
+  { "hit_count", bppy_get_hit_count, bppy_set_hit_count,
+    "Number of times the breakpoint has been hit.\n\
+Can be set to zero to clear the count. No other value is valid\n\
+when setting this property.", NULL },
+  { "location", bppy_get_location, NULL,
+    "Location of the breakpoint, as specified by the user.", NULL},
+  { "expression", bppy_get_expression, NULL,
+    "Expression of the breakpoint, as specified by the user.", NULL},
+  { "condition", bppy_get_condition, bppy_set_condition,
+    "Condition of the breakpoint, as specified by the user,\
+or None if no condition set."},
+  { "commands", bppy_get_commands, NULL,
+    "Commands of the breakpoint, as specified by the user."},
+  { "type", bppy_get_type, NULL,
+    "Type of breakpoint."},
+  { NULL }  /* Sentinel.  */
+};
+
+static PyMethodDef breakpoint_object_methods[] =
+{
+  { "is_valid", bppy_is_valid, METH_NOARGS,
+    "Return true if this breakpoint is valid, false if not." },
+  { NULL } /* Sentinel.  */
+};
+
+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 */
+  0,				  /* tp_members */
+  breakpoint_object_getset	  /* tp_getset */
+};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index e8fcfa9..98d2b3f 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -75,6 +75,7 @@ extern PyTypeObject block_object_type;
 extern PyTypeObject symbol_object_type;
 
 PyObject *gdbpy_history (PyObject *self, PyObject *args);
+PyObject *gdbpy_breakpoints (PyObject *, PyObject *);
 PyObject *gdbpy_frame_stop_reason_string (PyObject *, PyObject *);
 PyObject *gdbpy_lookup_symbol (PyObject *self, PyObject *args, PyObject *kw);
 PyObject *gdbpy_selected_frame (PyObject *self, PyObject *args);
@@ -82,6 +83,7 @@ PyObject *gdbpy_block_for_pc (PyObject *self, PyObject *args);
 PyObject *gdbpy_lookup_type (PyObject *self, PyObject *args, PyObject *kw);
 PyObject *gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
 					   const char *encoding, struct type *type);
+PyObject *gdbpy_get_hook_function (const char *);
 
 PyObject *symtab_and_line_to_sal_object (struct symtab_and_line sal);
 PyObject *symtab_to_symtab_object (struct symtab *symtab);
@@ -111,6 +113,7 @@ void gdbpy_initialize_blocks (void);
 void gdbpy_initialize_types (void);
 void gdbpy_initialize_functions (void);
 void gdbpy_initialize_objfile (void);
+void gdbpy_initialize_breakpoints (void);
 void gdbpy_initialize_lazy_string (void);
 
 struct cleanup *make_cleanup_py_decref (PyObject *py);
@@ -131,6 +134,18 @@ extern const struct language_defn *python_language;
 			     "%s", Exception.message);			\
     } while (0)
 
+/* Use this after a TRY_EXCEPT to throw the appropriate Python
+   exception.  This macro is for use inside setter functions.  */
+#define GDB_PY_SET_HANDLE_EXCEPTION(Exception)				\
+    do {								\
+      if (Exception.reason < 0)						\
+        {								\
+	  PyErr_Format (Exception.reason == RETURN_QUIT			\
+			? PyExc_KeyboardInterrupt : PyExc_RuntimeError, \
+			"%s", Exception.message);			\
+	  return -1;							\
+	}								\
+    } while (0)
 
 void gdbpy_print_stack (void);
 
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 9a89eed..ba1a309 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -668,6 +668,7 @@ Enables or disables auto-loading of Python code when an object is opened."),
   gdbpy_initialize_functions ();
   gdbpy_initialize_types ();
   gdbpy_initialize_objfile ();
+  gdbpy_initialize_breakpoints ();
   gdbpy_initialize_lazy_string ();
 
   PyRun_SimpleString ("import gdb");
@@ -726,6 +727,9 @@ static PyMethodDef GdbMethods[] =
   { "parameter", gdbpy_parameter, METH_VARARGS,
     "Return a gdb parameter's value" },
 
+  { "breakpoints", gdbpy_breakpoints, METH_NOARGS,
+    "Return a tuple of all breakpoint objects" },
+
   { "default_visualizer", gdbpy_default_visualizer, METH_VARARGS,
     "Find the default visualizer for a Value." },
 
diff --git a/gdb/testsuite/gdb.python/Makefile.in b/gdb/testsuite/gdb.python/Makefile.in
index 578ac54..c6a2678 100644
--- a/gdb/testsuite/gdb.python/Makefile.in
+++ b/gdb/testsuite/gdb.python/Makefile.in
@@ -2,7 +2,7 @@ VPATH = @srcdir@
 srcdir = @srcdir@
 
 EXECUTABLES = py-type py-value py-prettyprint py-template py-block \
-	py-symbol py-mi
+		py-symbol py-mi py-breakpoint
 
 all info install-info dvi install uninstall installcheck check:
 	@echo "Nothing to be done for $@..."
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.c b/gdb/testsuite/gdb.python/py-breakpoint.c
new file mode 100644
index 0000000..b2ad81b
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-breakpoint.c
@@ -0,0 +1,46 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2010 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/>.
+*/
+
+
+
+int multiply (int i)
+{
+  return i * i;
+}
+
+int add (int i)
+{
+  return i + i; 
+}
+
+
+int main (int argc, char *argv[])
+{
+  int foo = 5;
+  int bar = 42;
+  int result = 0;
+  int i;
+
+  for (i = 0; i < 10; i++)
+    {
+      result += multiply (foo);  /* Break at multiply. */
+      result += add (bar); /* Break at add. */
+    }
+
+  return 0; /* Break at end. */
+}
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
new file mode 100644
index 0000000..903912c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -0,0 +1,142 @@
+# Copyright (C) 2010 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 the mechanism
+# exposing values to Python.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set testfile "py-breakpoint"
+set srcfile ${testfile}.c
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
+    return -1
+}
+
+# Run a command in GDB, and report a failure if a Python exception is thrown.
+# If report_pass is true, report a pass if no exception is thrown.
+proc gdb_py_test_silent_cmd {cmd name report_pass} {
+    global gdb_prompt
+
+  gdb_test_multiple $cmd $name {
+      -re "Traceback.*$gdb_prompt $"  { fail $name }
+      -re "$gdb_prompt $"	      { if $report_pass { pass $name } }
+  }
+}
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+global hex decimal
+
+# Initially there should be one breakpoint: main.
+
+gdb_py_test_silent_cmd "python blist = gdb.breakpoints()" "Get Breakpoint List" 0
+gdb_test "python print blist\[0\]" "<gdb.Breakpoint object at $hex>" "Check obj exists"
+gdb_test "python print blist\[0\].location" "main." "Check breakpoint location"
+
+gdb_breakpoint [gdb_get_line_number "Break at multiply."]
+gdb_continue_to_breakpoint "Break at multiply."
+
+# Check that the Python breakpoint code noted the addition of a
+# breakpoint "behind the scenes". 
+gdb_py_test_silent_cmd "python blist = gdb.breakpoints()" "Get Breakpoint List" 0
+gdb_test "python print len(blist)" "2" "Check for two breakpoints"
+gdb_test "python print blist\[0\]" "<gdb.Breakpoint object at $hex>" "Check obj exists"
+gdb_test "python print blist\[0\].location" "main." "Check breakpoint location"
+gdb_test "python print blist\[1\]" "<gdb.Breakpoint object at $hex>" "Check obj exists"
+gdb_test "python print blist\[1\].location" "py-breakpoint\.c:41*" "Check breakpoint location"
+
+# Check hit and ignore counts. 
+gdb_test "python print blist\[1\].hit_count" "1" "Check breakpoint hit count"
+gdb_py_test_silent_cmd "python blist\[1\].ignore_count = 4" "Set breakpoint hit count" 0
+gdb_continue_to_breakpoint "Break at multiply."
+gdb_test "python print blist\[1\].hit_count" "6" "Check breakpoint hit count"
+gdb_test "print result" "545" "Check expected variable result after 6 iterations"
+
+# Test breakpoint is enabled and disabled correctly..
+gdb_breakpoint [gdb_get_line_number "Break at add."]
+gdb_continue_to_breakpoint "Break at add."
+gdb_test "python print blist\[1\].enabled" "True" "Check breakpoint enabled."
+gdb_py_test_silent_cmd  "python blist\[1\].enabled = False" "Set breakpoint disabled." 0
+gdb_continue_to_breakpoint "Break at add."
+gdb_py_test_silent_cmd  "python blist\[1\].enabled = True" "Set breakpoint enabled." 0
+gdb_continue_to_breakpoint "Break at multiply."
+
+# Test other getters and setters.
+gdb_py_test_silent_cmd "python blist = gdb.breakpoints()" "Get Breakpoint List" 0
+gdb_test "python print blist\[1\].thread" "None" "Check breakpoint thread"
+gdb_test "python print blist\[1\].type == gdb.BP_BREAKPOINT" "True" "Check breakpoint type"
+gdb_test "python print blist\[0\].number" "1" "Check breakpoint number"
+gdb_test "python print blist\[1\].number" "2" "Check breakpoint number"
+gdb_test "python print blist\[2\].number" "3" "Check breakpoint number"
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+# Test conditional setting.
+set bp_location1 [gdb_get_line_number "Break at multiply."]
+gdb_py_test_silent_cmd  "python bp1 = gdb.Breakpoint (\"$bp_location1\")" "Set breakpoint" 0
+gdb_continue_to_breakpoint "Break at multiply."
+gdb_py_test_silent_cmd  "python bp1.condition = \"i == 5\"" "Set breakpoint" 0
+gdb_test "python print bp1.condition" "i == 5" "Test conditional has been set"
+gdb_continue_to_breakpoint "Break at multiply."
+gdb_test "print i" "5" "Test conditional breakpoint stopped after five iterations"
+gdb_py_test_silent_cmd  "python bp1.condition = None"  "Clear condition" 0
+gdb_test "python print bp1.condition" "None" "Test conditional read"
+gdb_continue_to_breakpoint "Break at multiply."
+gdb_test "print i" "6" "Test breakpoint stopped after six iterations"
+
+# Test commands.
+gdb_breakpoint [gdb_get_line_number "Break at add."]
+set test {commands $bpnum}
+gdb_test_multiple $test $test { -re "\r\n>$" { pass $test } }
+set test {print "Command for breakpoint has been executed."}
+gdb_test_multiple $test $test { -re "\r\n>$" { pass $test } }
+set test {print result}
+gdb_test_multiple $test $test { -re "\r\n>$" { pass $test } }
+gdb_test "end"
+
+gdb_py_test_silent_cmd "python blist = gdb.breakpoints()" "Get Breakpoint List" 0
+gdb_test "python print blist\[len(blist)-1\].commands" "print \"Command for breakpoint has been executed.\".*print result"
+
+# Watchpoints
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+if ![runto_main] then {
+    fail "Cannot run to main."
+    return 0
+}
+
+gdb_py_test_silent_cmd  "python wp1 = gdb.Breakpoint (\"result\", type=gdb.BP_WATCHPOINT, wp_class=gdb.WP_WRITE )" "Set watchpoint" 0
+gdb_test "continue" ".*watchpoint.*result.*Old value = 0.*New value = 25.*main.*" "Test watchpoint write"
+
+
+


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]