Implement explicit locations for Python breakpoints.

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.
This commit is contained in:
Phil Muldoon
2017-12-07 16:47:33 +00:00
parent 9c226a8689
commit 824cc835aa
8 changed files with 245 additions and 33 deletions

View 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> 2017-12-07 Joel Brobecker <brobecker@adacore.com>
* MAINTAINERS: Restore target entries for m68hc11-elf, * MAINTAINERS: Restore target entries for m68hc11-elf,

View File

@ -115,6 +115,10 @@
command allows the setting of a large number of breakpoints via a command allows the setting of a large number of breakpoints via a
regex pattern in Python. See the manual for further details. 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 * New features in the GDB remote stub, GDBserver
** GDBserver is now able to start inferior processes with a ** GDBserver is now able to start inferior processes with a

View 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> 2017-12-04 Tom Tromey <tom@tromey.com>
* gdb.texinfo (Rust): Update trait object status * gdb.texinfo (Rust): Update trait object status

View File

@ -4878,27 +4878,30 @@ represented as Python @code{Long} values.
Python code can manipulate breakpoints via the @code{gdb.Breakpoint} Python code can manipulate breakpoints via the @code{gdb.Breakpoint}
class. 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 Create a new breakpoint according to @var{spec}, which is a string
naming the location of the breakpoint, or an expression that defines a naming the location of the breakpoint, or an expression that defines a
watchpoint. The contents can be any location recognized by the watchpoint. The contents can be any location recognized by the
@code{break} command, or in the case of a watchpoint, by the @code{break} command or, in the case of a watchpoint, by the
@code{watch} command. The optional @var{type} denotes the breakpoint @code{watch} command. Alternatively, create a new a explicit location
to create from the types defined later in this chapter. This argument breakpoint (@pxref{Explicit Locations}) according to the
can be either @code{gdb.BP_BREAKPOINT} or @code{gdb.BP_WATCHPOINT}; it specifications contained in the key words @var{source},
defaults to @code{gdb.BP_BREAKPOINT}. The optional @var{internal} @var{function}, @var{label} and @var{line}. The optional @var{type}
argument allows the breakpoint to become invisible to the user. The denotes the breakpoint to create from the types defined later in this
breakpoint will neither be reported when created, nor will it be chapter. This argument can be either @code{gdb.BP_BREAKPOINT} or
listed in the output from @code{info breakpoints} (but will be listed @code{gdb.BP_WATCHPOINT}; it defaults to @code{gdb.BP_BREAKPOINT}.
with the @code{maint info breakpoints} command). The optional The optional @var{internal} argument allows the breakpoint to become
@var{temporary} argument makes the breakpoint a temporary breakpoint. invisible to the user. The breakpoint will neither be reported when
Temporary breakpoints are deleted after they have been hit. Any created, nor will it be listed in the output from @code{info
further access to the Python breakpoint after it has been hit will breakpoints} (but will be listed with the @code{maint info
result in a runtime error (as that breakpoint has now been breakpoints} command). The optional @var{temporary} argument makes
automatically deleted). The optional @var{wp_class} argument defines the breakpoint a temporary breakpoint. Temporary breakpoints are
the class of watchpoint to create, if @var{type} is deleted after they have been hit. Any further access to the Python
@code{gdb.BP_WATCHPOINT}. If a watchpoint class is not provided, it breakpoint after it has been hit will result in a runtime error (as
is assumed to be a @code{gdb.WP_WRITE} class. 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 @end defun
The available types are represented by constants defined in the @code{gdb} The available types are represented by constants defined in the @code{gdb}

View File

@ -32,6 +32,7 @@
#include "language.h" #include "language.h"
#include "location.h" #include "location.h"
#include "py-event.h" #include "py-event.h"
#include "linespec.h"
/* Number of live breakpoints. */ /* Number of live breakpoints. */
static int bppy_live; static int bppy_live;
@ -631,25 +632,104 @@ bppy_get_ignore_count (PyObject *self, void *closure)
return PyInt_FromLong (self_bp->bp->ignore_count); 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. */ /* Python function to create a new breakpoint. */
static int static int
bppy_init (PyObject *self, PyObject *args, PyObject *kwargs) bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
{ {
static const char *keywords[] = { "spec", "type", "wp_class", "internal", static const char *keywords[] = { "spec", "type", "wp_class", "internal",
"temporary", NULL }; "temporary","source", "function",
const char *spec; "label", "line", NULL };
int type = bp_breakpoint; const char *spec = NULL;
enum bptype type = bp_breakpoint;
int access_type = hw_write; int access_type = hw_write;
PyObject *internal = NULL; PyObject *internal = NULL;
PyObject *temporary = NULL; PyObject *temporary = NULL;
PyObject *lineobj = NULL;;
int internal_bp = 0; int internal_bp = 0;
int temporary_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, &spec, &type, &access_type,
&internal, &temporary)) &internal,
&temporary, &source,
&function, &label, &lineobj))
return -1; 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) if (internal)
{ {
internal_bp = PyObject_IsTrue (internal); internal_bp = PyObject_IsTrue (internal);
@ -664,23 +744,47 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
return -1; 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 = (gdbpy_breakpoint_object *) self;
bppy_pending_object->number = -1; bppy_pending_object->number = -1;
bppy_pending_object->bp = NULL; bppy_pending_object->bp = NULL;
TRY TRY
{ {
gdb::unique_xmalloc_ptr<char>
copy_holder (xstrdup (skip_spaces (spec)));
const char *copy = copy_holder.get ();
switch (type) switch (type)
{ {
case bp_breakpoint: case bp_breakpoint:
{ {
event_location_up location event_location_up location;
= string_to_event_location_basic (&copy, current_language,
symbol_name_match_type::WILD); 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, create_breakpoint (python_gdbarch,
location.get (), NULL, -1, NULL, location.get (), NULL, -1, NULL,
0, 0,
@ -693,6 +797,10 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
} }
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) if (access_type == hw_write)
watch_command_wrapper (copy, 0, internal_bp); watch_command_wrapper (copy, 0, internal_bp);
else if (access_type == hw_access) else if (access_type == hw_access)

View 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> 2017-12-06 Pedro Alves <palves@redhat.com>
* gdb.arch/i386-avx.exp: If testing with a RSP target, check * gdb.arch/i386-avx.exp: If testing with a RSP target, check

View File

@ -24,7 +24,7 @@ int multiply (int i)
int add (int i) int add (int i)
{ {
return i + i; return i + i; /* Break at function add. */
} }

View File

@ -531,6 +531,85 @@ proc_with_prefix test_bkpt_events {} {
check_last_event breakpoint_deleted 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_basic
test_bkpt_deletion test_bkpt_deletion
test_bkpt_cond_and_cmds test_bkpt_cond_and_cmds
@ -542,3 +621,4 @@ test_bkpt_temporary
test_bkpt_address test_bkpt_address
test_bkpt_pending test_bkpt_pending
test_bkpt_events test_bkpt_events
test_bkpt_explicit_loc