Allow enabling/disabling breakpoint location ranges

When a breakpoint has multiple locations, like e.g.:

 Num  Type       Disp Enb  Address    What
 1    breakpoint keep y    <MULTIPLE>
 1.1                  y    0x080486a2 in void foo<int>()...
 1.2                  y    0x080486ca in void foo<double>()...
 [....]
 1.5                  y    0x080487fa in void foo<long>()...

it's possible to enable/disable the individual locations using the
'<breakpoint_number>.<location_number>' syntax, like e.g.:

 (gdb) disable 1.2 1.3 1.4 1.5

That's inconvenient when you have a long list of locations to disable,
however.

This patch adds shorthand for the above, by making it possible to
specify a range of locations with the following syntax (similar to
thread id ranges):

 <breakpoint_number>.<first_location_number>-<last_location_number>

For example, the command above can now be simplified to:

 (gdb) disable 1.2-5

gdb/ChangeLog:
2017-11-07  Xavier Roirand  <roirand@adacore.com>
	    Pedro Alves  <palves@redhat.com>

	* breakpoint.c (map_breakpoint_number_range): New, factored out
	from ...
	(map_breakpoint_numbers): ... here.
	(find_location_by_number): Change parameters from string to
	breakpoint number and location.
	(extract_bp_number_and_location): New function.
	(enable_disable_bp_num_loc)
	(enable_disable_breakpoint_location_range)
	(enable_disable_command): New functions, factored out ...
	(enable_command, disable_command): ... these functions, and
	adjusted to support ranges.
	* NEWS: Document enable/disable breakpoint location range feature.

gdb/doc/ChangeLog:
2017-11-07  Xavier Roirand  <roirand@adacore.com>
	    Pedro Alves  <palves@redhat.com>

	* gdb.texinfo (Set Breaks): Document support for breakpoint
	location ranges in the enable/disable commands.

gdb/testsuite/ChangeLog:
2017-11-07  Xavier Roirand  <roirand@adacore.com>
	    Pedro Alves  <palves@redhat.com>

	* gdb.base/ena-dis-br.exp: Add reference to
	gdb.cp/ena-dis-br-range.exp.
	* gdb.cp/ena-dis-br-range.exp: New file.
	* gdb.cp/ena-dis-br-range.cc: New file.
This commit is contained in:
Xavier Roirand 2017-11-07 11:00:31 +00:00 committed by Pedro Alves
parent 0198d5e6fc
commit d0fe47010f
9 changed files with 473 additions and 123 deletions

View File

@ -1,3 +1,19 @@
2017-11-07 Xavier Roirand <roirand@adacore.com>
Pedro Alves <palves@redhat.com>
* breakpoint.c (map_breakpoint_number_range): New, factored out
from ...
(map_breakpoint_numbers): ... here.
(find_location_by_number): Change parameters from string to
breakpoint number and location.
(extract_bp_number_and_location): New function.
(enable_disable_bp_num_loc)
(enable_disable_breakpoint_location_range)
(enable_disable_command): New functions, factored out ...
(enable_command, disable_command): ... these functions, and
adjusted to support ranges.
* NEWS: Document enable/disable breakpoint location range feature.
2017-11-06 Luis Machado <luis.machado@linaro.org>
* MAINTAINERS (Write After Approval): Update my e-mail address.

View File

@ -75,6 +75,9 @@ QSetWorkingDir
* The "maintenance selftest" command now takes an optional argument to
filter the tests to be run.
* The "enable", and "disable" commands now accept a range of
breakpoint locations, e.g. "enable 1.3-5".
* New commands
set|show cwd

View File

@ -14131,59 +14131,66 @@ ignore_command (char *args, int from_tty)
printf_filtered ("\n");
}
/* Call FUNCTION on each of the breakpoints
whose numbers are given in ARGS. */
/* Call FUNCTION on each of the breakpoints with numbers in the range
defined by BP_NUM_RANGE (an inclusive range). */
static void
map_breakpoint_numbers (const char *args,
gdb::function_view<void (breakpoint *)> function)
map_breakpoint_number_range (std::pair<int, int> bp_num_range,
gdb::function_view<void (breakpoint *)> function)
{
int num;
struct breakpoint *b, *tmp;
if (args == 0 || *args == '\0')
error_no_arg (_("one or more breakpoint numbers"));
number_or_range_parser parser (args);
while (!parser.finished ())
if (bp_num_range.first == 0)
{
const char *p = parser.cur_tok ();
bool match = false;
warning (_("bad breakpoint number at or near '%d'"),
bp_num_range.first);
}
else
{
struct breakpoint *b, *tmp;
num = parser.get_number ();
if (num == 0)
{
warning (_("bad breakpoint number at or near '%s'"), p);
}
else
for (int i = bp_num_range.first; i <= bp_num_range.second; i++)
{
bool match = false;
ALL_BREAKPOINTS_SAFE (b, tmp)
if (b->number == num)
if (b->number == i)
{
match = true;
function (b);
break;
}
if (!match)
printf_unfiltered (_("No breakpoint number %d.\n"), num);
printf_unfiltered (_("No breakpoint number %d.\n"), i);
}
}
}
static struct bp_location *
find_location_by_number (const char *number)
{
const char *p1;
int bp_num;
int loc_num;
struct breakpoint *b;
struct bp_location *loc;
/* Call FUNCTION on each of the breakpoints whose numbers are given in
ARGS. */
p1 = number;
bp_num = get_number_trailer (&p1, '.');
if (bp_num == 0 || p1[0] != '.')
error (_("Bad breakpoint number '%s'"), number);
static void
map_breakpoint_numbers (const char *args,
gdb::function_view<void (breakpoint *)> function)
{
if (args == NULL || *args == '\0')
error_no_arg (_("one or more breakpoint numbers"));
number_or_range_parser parser (args);
while (!parser.finished ())
{
int num = parser.get_number ();
map_breakpoint_number_range (std::make_pair (num, num), function);
}
}
/* Return the breakpoint location structure corresponding to the
BP_NUM and LOC_NUM values. */
static struct bp_location *
find_location_by_number (int bp_num, int loc_num)
{
struct breakpoint *b;
ALL_BREAKPOINTS (b)
if (b->number == bp_num)
@ -14192,25 +14199,153 @@ find_location_by_number (const char *number)
}
if (!b || b->number != bp_num)
error (_("Bad breakpoint number '%s'"), number);
error (_("Bad breakpoint number '%d'"), bp_num);
/* Skip the dot. */
++p1;
const char *save = p1;
loc_num = get_number (&p1);
if (loc_num == 0)
error (_("Bad breakpoint location number '%s'"), number);
error (_("Bad breakpoint location number '%d'"), loc_num);
--loc_num;
loc = b->loc;
for (;loc_num && loc; --loc_num, loc = loc->next)
;
if (!loc)
error (_("Bad breakpoint location number '%s'"), save);
return loc;
int n = 0;
for (bp_location *loc = b->loc; loc != NULL; loc = loc->next)
if (++n == loc_num)
return loc;
error (_("Bad breakpoint location number '%d'"), loc_num);
}
/* Extract the breakpoint/location range specified by ARG. Returns
the breakpoint range in BP_NUM_RANGE, and the location range in
BP_LOC_RANGE.
ARG may be in any of the following forms:
x where 'x' is a breakpoint number.
x-y where 'x' and 'y' specify a breakpoint numbers range.
x.y where 'x' is a breakpoint number and 'y' a location number.
x.y-z where 'x' is a breakpoint number and 'y' and 'z' specify a
location number range.
*/
static bool
extract_bp_number_and_location (const std::string &arg,
std::pair<int, int> &bp_num_range,
std::pair<int, int> &bp_loc_range)
{
std::string::size_type dot = arg.find ('.');
if (dot != std::string::npos)
{
/* Handle 'x.y' and 'x.y-z' cases. */
if (arg.length () == dot + 1 || dot == 0)
error (_("bad breakpoint number at or near: '%s'"), arg.c_str ());
const char *ptb = arg.c_str ();
int bp_num = get_number_trailer (&ptb, '.');
if (bp_num == 0)
error (_("Bad breakpoint number '%s'"), arg.c_str ());
bp_num_range.first = bp_num;
bp_num_range.second = bp_num;
const char *bp_loc = &arg[dot + 1];
std::string::size_type dash = arg.find ('-', dot + 1);
if (dash != std::string::npos)
{
/* bp_loc is a range (x-z). */
if (arg.length () == dash + 1)
error (_("bad breakpoint number at or near: '%s'"), bp_loc);
const char *ptlf = bp_loc;
bp_loc_range.first = get_number_trailer (&ptlf, '-');
const char *ptls = &arg[dash + 1];
bp_loc_range.second = get_number_trailer (&ptls, '\0');
}
else
{
/* bp_loc is a single value. */
const char *ptls = bp_loc;
bp_loc_range.first = get_number_trailer (&ptls, '\0');
if (bp_loc_range.first == 0)
{
warning (_("bad breakpoint number at or near '%s'"), arg.c_str ());
return false;
}
bp_loc_range.second = bp_loc_range.first;
}
}
else
{
/* Handle x and x-y cases. */
std::string::size_type dash = arg.find ('-');
if (dash != std::string::npos)
{
if (arg.length () == dash + 1 || dash == 0)
error (_("bad breakpoint number at or near: '%s'"), arg.c_str ());
const char *ptf = arg.c_str ();
bp_num_range.first = get_number_trailer (&ptf, '-');
const char *pts = &arg[dash + 1];
bp_num_range.second = get_number_trailer (&pts, '\0');
}
else
{
const char *ptf = arg.c_str ();
bp_num_range.first = get_number (&ptf);
if (bp_num_range.first == 0)
{
warning (_("bad breakpoint number at or near '%s'"), arg.c_str ());
return false;
}
bp_num_range.second = bp_num_range.first;
}
bp_loc_range.first = 0;
bp_loc_range.second = 0;
}
if (bp_num_range.first == 0 || bp_num_range.second == 0)
error (_("bad breakpoint number at or near: '%s'"), arg.c_str ());
return true;
}
/* Enable or disable a breakpoint location BP_NUM.LOC_NUM. ENABLE
specifies whether to enable or disable. */
static void
enable_disable_bp_num_loc (int bp_num, int loc_num, bool enable)
{
struct bp_location *loc = find_location_by_number (bp_num, loc_num);
if (loc != NULL)
{
if (loc->enabled != enable)
{
loc->enabled = enable;
mark_breakpoint_location_modified (loc);
}
if (target_supports_enable_disable_tracepoint ()
&& current_trace_status ()->running && loc->owner
&& is_tracepoint (loc->owner))
target_disable_tracepoint (loc);
}
update_global_location_list (UGLL_DONT_INSERT);
}
/* Enable or disable a range of breakpoint locations. BP_NUM is the
number of the breakpoint, and BP_LOC_RANGE specifies the
(inclusive) range of location numbers of that breakpoint to
enable/disable. ENABLE specifies whether to enable or disable the
location. */
static void
enable_disable_breakpoint_location_range (int bp_num,
std::pair<int, int> &bp_loc_range,
bool enable)
{
for (int i = bp_loc_range.first; i <= bp_loc_range.second; i++)
enable_disable_bp_num_loc (bp_num, i, enable);
}
/* Set ignore-count of breakpoint number BPTNUM to COUNT.
If from_tty is nonzero, it prints a message to that effect,
@ -14244,8 +14379,13 @@ disable_breakpoint (struct breakpoint *bpt)
observer_notify_breakpoint_modified (bpt);
}
/* Enable or disable the breakpoint(s) or breakpoint location(s)
specified in ARGS. ARGS may be in any of the formats handled by
extract_bp_number_and_location. ENABLE specifies whether to enable
or disable the breakpoints/locations. */
static void
disable_command (const char *args, int from_tty)
enable_disable_command (const char *args, int from_tty, bool enable)
{
if (args == 0)
{
@ -14253,7 +14393,12 @@ disable_command (const char *args, int from_tty)
ALL_BREAKPOINTS (bpt)
if (user_breakpoint_p (bpt))
disable_breakpoint (bpt);
{
if (enable)
enable_breakpoint (bpt);
else
disable_breakpoint (bpt);
}
}
else
{
@ -14261,35 +14406,43 @@ disable_command (const char *args, int from_tty)
while (!num.empty ())
{
if (num.find ('.') != std::string::npos)
{
struct bp_location *loc = find_location_by_number (num.c_str ());
std::pair<int, int> bp_num_range, bp_loc_range;
if (loc)
if (extract_bp_number_and_location (num, bp_num_range, bp_loc_range))
{
if (bp_loc_range.first == bp_loc_range.second
&& bp_loc_range.first == 0)
{
if (loc->enabled)
{
loc->enabled = 0;
mark_breakpoint_location_modified (loc);
}
if (target_supports_enable_disable_tracepoint ()
&& current_trace_status ()->running && loc->owner
&& is_tracepoint (loc->owner))
target_disable_tracepoint (loc);
/* Handle breakpoint ids with formats 'x' or 'x-z'. */
map_breakpoint_number_range (bp_num_range,
enable
? enable_breakpoint
: disable_breakpoint);
}
else
{
/* Handle breakpoint ids with formats 'x.y' or
'x.y-z'. */
enable_disable_breakpoint_location_range
(bp_num_range.first, bp_loc_range, enable);
}
update_global_location_list (UGLL_DONT_INSERT);
}
else
map_breakpoint_numbers
(num.c_str (), [&] (breakpoint *b)
{
iterate_over_related_breakpoints (b, disable_breakpoint);
});
num = extract_arg (&args);
}
}
}
/* The disable command disables the specified breakpoints/locations
(or all defined breakpoints) so they're no longer effective in
stopping the inferior. ARGS may be in any of the forms defined in
extract_bp_number_and_location. */
static void
disable_command (const char *args, int from_tty)
{
enable_disable_command (args, from_tty, false);
}
static void
enable_breakpoint_disp (struct breakpoint *bpt, enum bpdisp disposition,
int count)
@ -14360,54 +14513,15 @@ enable_breakpoint (struct breakpoint *bpt)
enable_breakpoint_disp (bpt, bpt->disposition, 0);
}
/* The enable command enables the specified breakpoints (or all defined
breakpoints) so they once again become (or continue to be) effective
in stopping the inferior. */
/* The enable command enables the specified breakpoints/locations (or
all defined breakpoints) so they once again become (or continue to
be) effective in stopping the inferior. ARGS may be in any of the
forms defined in extract_bp_number_and_location. */
static void
enable_command (const char *args, int from_tty)
{
if (args == 0)
{
struct breakpoint *bpt;
ALL_BREAKPOINTS (bpt)
if (user_breakpoint_p (bpt))
enable_breakpoint (bpt);
}
else
{
std::string num = extract_arg (&args);
while (!num.empty ())
{
if (num.find ('.') != std::string::npos)
{
struct bp_location *loc = find_location_by_number (num.c_str ());
if (loc)
{
if (!loc->enabled)
{
loc->enabled = 1;
mark_breakpoint_location_modified (loc);
}
if (target_supports_enable_disable_tracepoint ()
&& current_trace_status ()->running && loc->owner
&& is_tracepoint (loc->owner))
target_enable_tracepoint (loc);
}
update_global_location_list (UGLL_MAY_INSERT);
}
else
map_breakpoint_numbers
(num.c_str (), [&] (breakpoint *b)
{
iterate_over_related_breakpoints (b, enable_breakpoint);
});
num = extract_arg (&args);
}
}
enable_disable_command (args, from_tty, true);
}
static void

View File

@ -1,3 +1,9 @@
2017-11-07 Xavier Roirand <roirand@adacore.com>
Pedro Alves <palves@redhat.com>
* gdb.texinfo (Set Breaks): Document support for breakpoint
location ranges in the enable/disable commands.
2017-10-04 Sergio Durigan Junior <sergiodj@redhat.com>
* gdb.texinfo (Starting your Program) <The working directory.>:

View File

@ -3927,15 +3927,17 @@ Num Type Disp Enb Address What
1.2 y 0x080486ca in void foo<double>() at t.cc:8
@end smallexample
Each location can be individually enabled or disabled by passing
You cannot delete the individual locations from a breakpoint. However,
each location can be individually enabled or disabled by passing
@var{breakpoint-number}.@var{location-number} as argument to the
@code{enable} and @code{disable} commands. Note that you cannot
delete the individual locations from the list, you can only delete the
entire list of locations that belong to their parent breakpoint (with
the @kbd{delete @var{num}} command, where @var{num} is the number of
the parent breakpoint, 1 in the above example). Disabling or enabling
the parent breakpoint (@pxref{Disabling}) affects all of the locations
that belong to that breakpoint.
@code{enable} and @code{disable} commands. It's also possible to
@code{enable} and @code{disable} a range of @var{location-number}
locations using a @var{breakpoint-number} and two @var{location-number}s,
in increasing order, separated by a hyphen, like
@kbd{@var{breakpoint-number}.@var{location-number1}-@var{location-number2}},
in which case @value{GDBN} acts on all the locations in the range (inclusive).
Disabling or enabling the parent breakpoint (@pxref{Disabling}) affects
all of the locations that belong to that breakpoint.
@cindex pending breakpoints
It's quite common to have a breakpoint inside a shared library.

View File

@ -1,3 +1,11 @@
2017-11-07 Xavier Roirand <roirand@adacore.com>
Pedro Alves <palves@redhat.com>
* gdb.base/ena-dis-br.exp: Add reference to
gdb.cp/ena-dis-br-range.exp.
* gdb.cp/ena-dis-br-range.exp: New file.
* gdb.cp/ena-dis-br-range.cc: New file.
2017-11-06 Pedro Alves <palves@redhat.com>
* gdb.base/attach-non-pgrp-leader.c: New.

View File

@ -324,6 +324,9 @@ set b4 [break_at main ""]
#
# WHAT - the command to test (disable/enable).
#
# Note: tests involving location ranges (and more) are found in
# gdb.cp/ena-dis-br-range.exp.
#
proc test_ena_dis_br { what } {
global b1
global b2

View File

@ -0,0 +1,66 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2017 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/>. */
/* Some overloaded functions to test breakpoints with multiple
locations. */
class foo
{
public:
static void overload (void);
static void overload (char);
static void overload (int);
static void overload (double);
};
void
foo::overload ()
{
}
void
foo::overload (char arg)
{
}
void
foo::overload (int arg)
{
}
void
foo::overload (double arg)
{
}
void
marker ()
{
}
int
main ()
{
foo::overload ();
foo::overload (111);
foo::overload ('h');
foo::overload (3.14);
marker ();
return 0;
}

View File

@ -0,0 +1,132 @@
# Copyright 2017 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.
# Test the enable/disable commands with breakpoint location ranges.
# Note: more tests involving involving disable/enable commands on
# multiple locations and breakpoints are found in
# gdb.base/ena-dis-br.exp.
if { [skip_cplus_tests] } { continue }
standard_testfile .cc
if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} {
return -1
}
if ![runto 'marker'] then {
fail "run to marker"
return -1
}
# Returns a buffer corresponding to what GDB replies when asking for
# 'info breakpoint'. The parameters are all the existing breakpoints
# enabled/disable value: 'n' or 'y'.
proc make_info_breakpoint_reply_re {b1 b2 b21 b22 b23 b24} {
set ws "\[\t \]+"
return [multi_line \
"Num Type${ws}Disp Enb Address${ws}What.*" \
"1${ws}breakpoint keep ${b1}${ws}.* in marker\\(\\) at .*" \
"${ws}breakpoint already hit 1 time.*" \
"2${ws}breakpoint${ws}keep${ws}${b2}${ws}<MULTIPLE>.*" \
"2.1${ws}${b21}.*" \
"2.2${ws}${b22}.*" \
"2.3${ws}${b23}.*" \
"2.4${ws}${b24}.*" \
]
}
gdb_test "break foo::overload" \
"Breakpoint \[0-9\]+ at $hex: foo::overload. .4 locations." \
"set breakpoint at overload"
gdb_test "info break" [make_info_breakpoint_reply_re y y y y y y] \
"breakpoint info"
# Test the enable/disable commands, and check the enable/disable state
# of the breakpoints/locations in the "info break" output. CMD is the
# actual disable/enable command. The bNN parameters are the same as
# make_info_breakpoint_reply_re's.
proc test_enable_disable {cmd b1 b2 b21 b22 b23 b24} {
gdb_test_no_output $cmd
set re [make_info_breakpoint_reply_re $b1 $b2 $b21 $b22 $b23 $b24]
gdb_test "info break" $re "breakpoint info $cmd"
}
# Check that we can disable/enable a breakpoint with a single
# location.
test_enable_disable "disable 1" n y y y y y
test_enable_disable "enable 1" y y y y y y
# Check that we can disable/disable a breakpoint with multiple
# locations.
test_enable_disable "disable 2" y n y y y y
test_enable_disable "enable 2" y y y y y y
# Check that we can disable/enable a single location breakpoint.
test_enable_disable "disable 2.2" y y y n y y
test_enable_disable "enable 2.2" y y y y y y
# Check that we can disable/enable a range of breakpoint locations.
test_enable_disable "disable 2.2-3" y y y n n y
test_enable_disable "enable 2.2-3" y y y y y y
# Check that we can disable/enable a breakpoint location range with
# START==END.
test_enable_disable "disable 2.2-2" y y y n y y
test_enable_disable "enable 2.2-2" y y y y y y
# Check that we can disable a location breakpoint range with max >
# existing breakpoint location.
gdb_test "disable 2.3-5" "Bad breakpoint location number '5'" \
"disable location breakpoint range with max > existing"
gdb_test "info break" [make_info_breakpoint_reply_re y y y y n n] \
"breakpoint info disable 2.3 to 2.5"
# Check that we can enable a location breakpoint range with max >
# existing breakpoint location.
gdb_test "enable 2.3-5" "Bad breakpoint location number '5'" \
"enable location breakpoint range with max > existing"
gdb_test "info break" [make_info_breakpoint_reply_re y y y y y y] \
"breakpoint info enable 2.3 to 2.5"
# Check that disabling an reverse location breakpoint range does not
# work.
gdb_test_no_output "disable 2.3-2"
gdb_test "info break" [make_info_breakpoint_reply_re y y y y y y] \
"breakpoint info disable 2.3-2"
# Check that disabling an invalid breakpoint location range does not
# cause unexpected behavior.
gdb_test "disable 2.6-7" "Bad breakpoint location number '6'" \
"disable an unvalid location breakpoint range"
gdb_test "info break" [make_info_breakpoint_reply_re y y y y y y] \
"breakpoint info disable 2.6-7"
# Check that disabling an invalid breakpoint location range does not
# cause trouble.
gdb_test_no_output "disable 2.8-6"
gdb_test "info break" [make_info_breakpoint_reply_re y y y y y y] \
"breakpoint info disable 2.8-6"