Implement explicit locations for Python breakpoints.
authorPhil Muldoon <pmuldoon@redhat.com>
Thu, 7 Dec 2017 16:47:33 +0000 (16:47 +0000)
committerPhil Muldoon <pmuldoon@redhat.com>
Thu, 7 Dec 2017 16:47:33 +0000 (16:47 +0000)
This introduces several new keywords to the bppy_init constructor.
The spec parameter is now optional but mutually exclusive to the
explicit keywords source, label, function and line.

gdb/ChangeLog

2017-12-07  Phil Muldoon  <pmuldoon@redhat.com>

       * python/py-breakpoint.c (bppy_init): Use string_to_event_location
       over basic location code. Implement explicit location keywords.
       (bppy_init_validate_args): New function.
       * NEWS: Document Python explicit breakpoint locations.

doc/ChangeLog

2017-12-07  Phil Muldoon  <pmuldoon@redhat.com>

       * python.texi (Breakpoints In Python): Add text relating
       to allowed explicit locations and keywords in gdb.Breakpoints.

testsuite/ChangeLog

2017-12-07  Phil Muldoon  <pmuldoon@redhat.com>

       * gdb.python/py-breakpoint.exp (test_bkpt_explicit_loc): Add new
       tests for explicit locations.

gdb/ChangeLog
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/python.texi
gdb/python/py-breakpoint.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.python/py-breakpoint.c
gdb/testsuite/gdb.python/py-breakpoint.exp

index 4d42cf9..9ba0254 100644 (file)
@@ -1,3 +1,10 @@
+2017-12-07  Phil Muldoon  <pmuldoon@redhat.com>
+
+       * python/py-breakpoint.c (bppy_init): Use string_to_event_location
+       over basic location code. Implement explicit location keywords.
+       (bppy_init_validate_args): New function.
+       * NEWS: Document Python explicit breakpoint locations.
+
 2017-12-07  Joel Brobecker  <brobecker@adacore.com>
 
        * MAINTAINERS: Restore target entries for m68hc11-elf,
index 2262b25..72b4057 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
      command allows the setting of a large number of breakpoints via a
      regex pattern in Python.  See the manual for further details.
 
+  ** Python breakpoints can now accept explicit locations.  See the
+     manual for a further description of this feature.
+
+
 * New features in the GDB remote stub, GDBserver
 
   ** GDBserver is now able to start inferior processes with a
index a108427..419efc4 100644 (file)
@@ -1,3 +1,8 @@
+2017-12-07  Phil Muldoon  <pmuldoon@redhat.com>
+
+       * python.texi (Breakpoints In Python): Add text relating
+       to allowed explicit locations and keywords in gdb.Breakpoints.
+
 2017-12-04  Tom Tromey  <tom@tromey.com>
 
        * gdb.texinfo (Rust): Update trait object status
index f411f60..28a7a1a 100644 (file)
@@ -4878,27 +4878,30 @@ represented as Python @code{Long} values.
 Python code can manipulate breakpoints via the @code{gdb.Breakpoint}
 class.
 
-@defun Breakpoint.__init__ (spec @r{[}, type @r{[}, wp_class @r{[},internal @r{[},temporary@r{]]]]})
+@defun Breakpoint.__init__ (spec @r{[}, type @r{[}, wp_class @r{[}, internal @r{[}, temporary @r{]}, source @r{]}, function @r{]}, label @r{]}, line @r{]]]]]]]]})
 Create a new breakpoint according to @var{spec}, which 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{gdb.BP_BREAKPOINT} or @code{gdb.BP_WATCHPOINT}; it
-defaults to @code{gdb.BP_BREAKPOINT}.  The optional @var{internal}
-argument allows the breakpoint to become invisible to the user.  The
-breakpoint will neither be reported when created, nor will it be
-listed in the output from @code{info breakpoints} (but will be listed
-with the @code{maint info breakpoints} command).  The optional
-@var{temporary} argument makes the breakpoint a temporary breakpoint.
-Temporary breakpoints are deleted after they have been hit.  Any
-further access to the Python breakpoint after it has been hit will
-result in a runtime error (as that breakpoint has now been
-automatically deleted).  The optional @var{wp_class} argument defines
-the class of watchpoint to create, if @var{type} is
-@code{gdb.BP_WATCHPOINT}.  If a watchpoint class is not provided, it
-is assumed to be a @code{gdb.WP_WRITE} class.
+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.  Alternatively, create a new a explicit location
+breakpoint (@pxref{Explicit Locations}) according to the
+specifications contained in the key words @var{source},
+@var{function}, @var{label} and @var{line}.  The optional @var{type}
+denotes the breakpoint to create from the types defined later in this
+chapter.  This argument can be either @code{gdb.BP_BREAKPOINT} or
+@code{gdb.BP_WATCHPOINT}; it defaults to @code{gdb.BP_BREAKPOINT}.
+The optional @var{internal} argument allows the breakpoint to become
+invisible to the user.  The breakpoint will neither be reported when
+created, nor will it be listed in the output from @code{info
+breakpoints} (but will be listed with the @code{maint info
+breakpoints} command).  The optional @var{temporary} argument makes
+the breakpoint a temporary breakpoint.  Temporary breakpoints are
+deleted after they have been hit.  Any further access to the Python
+breakpoint after it has been hit will result in a runtime error (as
+that breakpoint has now been automatically deleted).  The optional
+@var{wp_class} argument defines the class of watchpoint to create, if
+@var{type} is @code{gdb.BP_WATCHPOINT}.  If a watchpoint class is not
+provided, it is assumed to be a @code{gdb.WP_WRITE} class.
 @end defun
 
 The available types are represented by constants defined in the @code{gdb}
index 2574683..f865317 100644 (file)
@@ -32,6 +32,7 @@
 #include "language.h"
 #include "location.h"
 #include "py-event.h"
+#include "linespec.h"
 
 /* Number of live breakpoints.  */
 static int bppy_live;
@@ -631,25 +632,104 @@ bppy_get_ignore_count (PyObject *self, void *closure)
   return PyInt_FromLong (self_bp->bp->ignore_count);
 }
 
+/* Internal function to validate the Python parameters/keywords
+   provided to bppy_init.  */
+
+static int
+bppy_init_validate_args (const char *spec, char *source,
+                        char *function, char *label,
+                        char *line, enum bptype type)
+{
+  /* If spec is defined, ensure that none of the explicit location
+     keywords are also defined.  */
+  if (spec != NULL)
+    {
+      if (source != NULL || function != NULL || label != NULL || line != NULL)
+       {
+         PyErr_SetString (PyExc_RuntimeError,
+                          _("Breakpoints specified with spec cannot "
+                            "have source, function, label or line defined."));
+         return -1;
+       }
+    }
+  else
+    {
+      /* If spec isn't defined, ensure that the user is not trying to
+        define a watchpoint with an explicit location.  */
+      if (type == bp_watchpoint)
+       {
+         PyErr_SetString (PyExc_RuntimeError,
+                          _("Watchpoints cannot be set by explicit "
+                            "location parameters."));
+         return -1;
+       }
+      else
+       {
+         /* Otherwise, ensure some explicit locations are defined.  */
+         if (source == NULL && function == NULL && label == NULL
+             && line == NULL)
+           {
+             PyErr_SetString (PyExc_RuntimeError,
+                              _("Neither spec nor explicit location set."));
+             return -1;
+           }
+         /* Finally, if source is specified, ensure that line, label
+            or function are specified too.  */
+         if (source != NULL && function == NULL && label == NULL
+             && line == NULL)
+           {
+             PyErr_SetString (PyExc_RuntimeError,
+                              _("Specifying a source must also include a "
+                                "line, label or function."));
+             return -1;
+           }
+       }
+    }
+  return 1;
+}
+
 /* Python function to create a new breakpoint.  */
 static int
 bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
 {
   static const char *keywords[] = { "spec", "type", "wp_class", "internal",
-                                   "temporary", NULL };
-  const char *spec;
-  int type = bp_breakpoint;
+                                   "temporary","source", "function",
+                                   "label", "line", NULL };
+  const char *spec = NULL;
+  enum bptype type = bp_breakpoint;
   int access_type = hw_write;
   PyObject *internal = NULL;
   PyObject *temporary = NULL;
+  PyObject *lineobj = NULL;;
   int internal_bp = 0;
   int temporary_bp = 0;
+  gdb::unique_xmalloc_ptr<char> line;
+  char *label = NULL;
+  char *source = NULL;
+  char *function = NULL;
 
-  if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "s|iiOO", keywords,
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "|siiOOsssO", keywords,
                                        &spec, &type, &access_type,
-                                       &internal, &temporary))
+                                       &internal,
+                                       &temporary, &source,
+                                       &function, &label, &lineobj))
     return -1;
 
+
+  if (lineobj != NULL)
+    {
+      if (PyInt_Check (lineobj))
+       line.reset (xstrprintf ("%ld", PyInt_AsLong (lineobj)));
+      else if (PyString_Check (lineobj))
+       line = python_string_to_host_string (lineobj);
+      else
+       {
+         PyErr_SetString (PyExc_RuntimeError,
+                          _("Line keyword should be an integer or a string. "));
+         return -1;
+       }
+    }
+
   if (internal)
     {
       internal_bp = PyObject_IsTrue (internal);
@@ -664,23 +744,47 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
        return -1;
     }
 
+  if (bppy_init_validate_args (spec, source, function, label, line.get (),
+                              type) == -1)
+    return -1;
+
   bppy_pending_object = (gdbpy_breakpoint_object *) self;
   bppy_pending_object->number = -1;
   bppy_pending_object->bp = NULL;
 
   TRY
     {
-      gdb::unique_xmalloc_ptr<char>
-       copy_holder (xstrdup (skip_spaces (spec)));
-      const char *copy = copy_holder.get ();
-
       switch (type)
        {
        case bp_breakpoint:
          {
-           event_location_up location
-             = string_to_event_location_basic (&copy, current_language,
-                                               symbol_name_match_type::WILD);
+           event_location_up location;
+
+           if (spec != NULL)
+             {
+               gdb::unique_xmalloc_ptr<char>
+                 copy_holder (xstrdup (skip_spaces (spec)));
+               const char *copy = copy_holder.get ();
+
+               location  = string_to_event_location (&copy,
+                                                     current_language);
+             }
+           else
+             {
+               struct explicit_location explicit_loc;
+
+               initialize_explicit_location (&explicit_loc);
+               explicit_loc.source_filename = source;
+               explicit_loc.function_name = function;
+               explicit_loc.label_name = label;
+
+               if (line != NULL)
+                 explicit_loc.line_offset =
+                   linespec_parse_line_offset (line.get ());
+
+               location = new_explicit_location (&explicit_loc);
+             }
+
            create_breakpoint (python_gdbarch,
                               location.get (), NULL, -1, NULL,
                               0,
@@ -691,8 +795,12 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
                               0, 1, internal_bp, 0);
            break;
          }
-        case bp_watchpoint:
+       case bp_watchpoint:
          {
+           gdb::unique_xmalloc_ptr<char>
+             copy_holder (xstrdup (skip_spaces (spec)));
+           char *copy = copy_holder.get ();
+
            if (access_type == hw_write)
              watch_command_wrapper (copy, 0, internal_bp);
            else if (access_type == hw_access)
index 326af54..87547cb 100644 (file)
@@ -1,3 +1,8 @@
+2017-12-07  Phil Muldoon  <pmuldoon@redhat.com>
+
+       * gdb.python/py-breakpoint.exp (test_bkpt_explicit_loc): Add new
+       tests for explicit locations.
+
 2017-12-06  Pedro Alves  <palves@redhat.com>
 
        * gdb.arch/i386-avx.exp: If testing with a RSP target, check
index df6163e..562cab3 100644 (file)
@@ -24,7 +24,7 @@ int multiply (int i)
 
 int add (int i)
 {
-  return i + i; 
+  return i + i;  /* Break at function add.  */
 }
 
 
index 6c39f13..e89b9b8 100644 (file)
@@ -531,6 +531,85 @@ proc_with_prefix test_bkpt_events {} {
     check_last_event breakpoint_deleted
 }
 
+proc test_bkpt_explicit_loc {} {
+    global srcfile testfile
+
+    with_test_prefix test_bkpt_invisible {
+       # Start with a fresh gdb.
+       clean_restart ${testfile}
+
+       if ![runto_main] then {
+           fail "cannot run to main."
+           return 0
+       }
+
+       delete_breakpoints
+
+       set bp_location1 [gdb_get_line_number "Break at multiply."]
+       set bp_location2 [gdb_get_line_number "Break at add."]
+
+       gdb_py_test_silent_cmd "python bp1 = gdb.Breakpoint (line=$bp_location1)" \
+           "set explicit breakpoint by line" 0
+       gdb_continue_to_breakpoint "break at multiply for explicit line" \
+           ".*Break at multiply.*"
+
+       gdb_py_test_silent_cmd "python bp1 = gdb.Breakpoint (line=\"+1\")" \
+           "set explicit breakpoint by relative line" 0
+       gdb_continue_to_breakpoint "break at add for relative line" \
+           ".*Break at add.*"
+
+       delete_breakpoints
+       gdb_py_test_silent_cmd "python bp1 = gdb.Breakpoint (line=\"-1\")" \
+           "set explicit breakpoint by relative negative line" 0
+       gdb_continue_to_breakpoint "break at multiply for negative line" \
+           ".*Break at multiply.*"
+
+       delete_breakpoints
+       gdb_test "python bp1 = gdb.Breakpoint (line=bp1)" \
+           "RuntimeError: Line keyword should be an integer or a string.*" \
+           "set explicit breakpoint by invalid line type"
+
+       delete_breakpoints
+       gdb_py_test_silent_cmd "python bp1 = gdb.Breakpoint (function=\"add\")" \
+           "set explicit breakpoint by function" 0
+       gdb_continue_to_breakpoint "break at function add for function" \
+           ".*Break at function add.*"
+
+       delete_breakpoints
+       gdb_py_test_silent_cmd "python bp1 = gdb.Breakpoint (source=\"$srcfile\", function=\"add\")" \
+           "set explicit breakpoint by source file and function" 0
+       gdb_continue_to_breakpoint "break at function add for source and function" \
+           ".*Break at function add.*"
+
+       delete_breakpoints
+       gdb_py_test_silent_cmd "python bp1 = gdb.Breakpoint (source=\"$srcfile\", line=\"$bp_location2\")" \
+           "set explicit breakpoint by source file and line number" 0
+       gdb_continue_to_breakpoint "break at add for source and line" \
+           ".*Break at add.*"
+
+       delete_breakpoints
+       gdb_py_test_silent_cmd "python bp1 = gdb.Breakpoint (\"-source $srcfile -line $bp_location2\")" \
+           "set explicit breakpoint by source file and line number in spec" 0
+       gdb_continue_to_breakpoint "break at add for source and line in spec" \
+           ".*Break at add.*"
+
+       delete_breakpoints
+       gdb_test "python bp1 = gdb.Breakpoint (source=\"$srcfile\")" \
+           "RuntimeError: Specifying a source must also include a line, label or function.*" \
+           "set invalid explicit breakpoint by source only"
+
+       gdb_test "python bp1 = gdb.Breakpoint (source=\"foo.c\", line=\"5\")" \
+           "No source file named foo.*" \
+           "set invalid explicit breakpoint by missing source and line"
+       gdb_test "python bp1 = gdb.Breakpoint (source=\"$srcfile\", line=\"900\")" \
+           "No line 900 in file \"$srcfile\".*" \
+           "set invalid explicit breakpoint by source and invalid line"
+       gdb_test "python bp1 = gdb.Breakpoint (function=\"blah\")" \
+           "Function \"blah\" not defined.*" \
+           "set invalid explicit breakpoint by missing function"
+    }
+}
+
 test_bkpt_basic
 test_bkpt_deletion
 test_bkpt_cond_and_cmds
@@ -542,3 +621,4 @@ test_bkpt_temporary
 test_bkpt_address
 test_bkpt_pending
 test_bkpt_events
+test_bkpt_explicit_loc