Skip to content

Commit

Permalink
gdb: implement inferior-specific breakpoints
Browse files Browse the repository at this point in the history
This is a "workaround feature" which is intended to help with
multi-target program debugging until a more fundamental solution is
available.

With this addition, the following command syntax is recognized by gdb:

(gdb) b file.c:42 inferior 1

It willl only insert breakpoint locations into program space that
matches inferior 1 and will only consider a breakpoint hit if the
current inferior is 1.

It is already possible to achieve similar results by using
`if $_inferior == N` conditional breakpoint, but the major advantage of
the implemented feature is that it skips insertion of breakpoints
completely, which can be of a high importance for some platforms.

gdb/ChangeLog:
2019-11-20  Mihails Strasuns  <mihails.strasuns@intel.com>

	* breakpoint.h (struct breakpoint): new field 'inferior'
	* breakpoint.c (find_condition_and_thread): supports new
	break command syntax for specifying an inferior
	(create_breakpoint): pass breakpoint inferior argument around
	(decode_location_default, init_breakpoint_sal): if inferior number is
	present, only match location if inferior pspace matches
	location pspace
	(print_one_breakpoint_location): always print inferior for
	inferior-specific breakpoints
	* linespec.c (linespec_keywords): add "inferior" to the keyword list

gdb/testsuite/ChangeLog:
2019-11-20  Mihails Strasuns  <mihails.strasuns@intel.com>

	* gdb.base/breakpoint-inferior.exp: new test case to cover
	functionaity of inferior breakpoint feature
	* lib/completion-support.exp, gdb.linespec/explicit.exp:
	recognize new keyword

Signed-off-by: Mihails Strasuns <mihails.strasuns@intel.com>
  • Loading branch information
mihails-strasuns-intel authored and felix-willgerodt committed Nov 15, 2022
1 parent 7c5cad0 commit 7d87ac9
Show file tree
Hide file tree
Showing 13 changed files with 387 additions and 49 deletions.
189 changes: 149 additions & 40 deletions gdb/breakpoint.c

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion gdb/breakpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ struct breakpoint_ops
gdb::unique_xmalloc_ptr<char>,
gdb::unique_xmalloc_ptr<char>,
enum bptype, enum bpdisp, int, int,
int, int, const struct breakpoint_ops *,
int, int, int, const struct breakpoint_ops *,
int, int, int, unsigned);

/* Given the location (second parameter), this method decodes it and
Expand Down Expand Up @@ -810,6 +810,10 @@ struct breakpoint
care. */
int task = 0;

/* GDB inferior ID number for inferior-specific breakpoint, or 0
if don't care. */
int inferior = 0;

/* Count of the number of times this breakpoint was taken, dumped
with the info, but not used for anything else. Useful for seeing
how many times you hit a break prior to the program aborting, so
Expand Down Expand Up @@ -1294,6 +1298,10 @@ struct breakpoint_deleter
}
};

/* Deletes all breakpoints with an inferior condition which
have an inferior number match the argument. */
extern void delete_breakpoints_inf (struct inferior*);

typedef std::unique_ptr<struct breakpoint, breakpoint_deleter> breakpoint_up;

extern breakpoint_up set_momentary_breakpoint
Expand Down Expand Up @@ -1562,6 +1570,8 @@ extern void breakpoint_set_silent (struct breakpoint *b, int silent);

extern void breakpoint_set_thread (struct breakpoint *b, int thread);

extern void breakpoint_set_inferior (struct breakpoint *b, int inferior);

extern void breakpoint_set_task (struct breakpoint *b, int task);

/* Clear the "inserted" flag in all breakpoints. */
Expand Down
8 changes: 5 additions & 3 deletions gdb/c-exp.y
Original file line number Diff line number Diff line change
Expand Up @@ -2953,11 +2953,13 @@ lex_one_token (struct parser_state *par_state, bool *is_quoted_name)
/* For the same reason (breakpoint conditions), "thread N"
terminates the expression. "thread" could be an identifier, but
an identifier is never followed by a number without intervening
punctuation. "task" is similar. Handle abbreviations of these,
similarly to breakpoint.c:find_condition_and_thread. */
punctuation. "task" and "inferior" are similar. Handle
abbreviations of these, similarly to
breakpoint.c:find_condition_and_thread. */
if (namelen >= 1
&& (strncmp (tokstart, "thread", namelen) == 0
|| strncmp (tokstart, "task", namelen) == 0)
|| strncmp (tokstart, "task", namelen) == 0
|| strncmp (tokstart, "inferior", namelen) == 0)
&& (tokstart[namelen] == ' ' || tokstart[namelen] == '\t')
&& ! scanning_macro_expansion ())
{
Expand Down
44 changes: 42 additions & 2 deletions gdb/doc/gdb.texinfo
Original file line number Diff line number Diff line change
Expand Up @@ -4417,8 +4417,9 @@ C@t{++}, a function name may refer to more than one possible place to break.
that situation.

It is also possible to insert a breakpoint that will stop the program
only if a specific thread (@pxref{Thread-Specific Breakpoints})
or a specific task (@pxref{Ada Tasks}) hits that breakpoint.
only if a specific thread (@pxref{Thread-Specific Breakpoints}),
a specific task (@pxref{Ada Tasks}) or a specific inferior
(@pxref{Inferior-Specific Breakpoints}) hits that breakpoint.

@item break
When called without any arguments, @code{break} sets a breakpoint at
Expand Down Expand Up @@ -6918,6 +6919,7 @@ you examine the stopped thread in the debugger.
* Non-Stop Mode:: Other threads continue to execute
* Background Execution:: Running your program asynchronously
* Thread-Specific Breakpoints:: Controlling breakpoints
* Inferior-Specific Breakpoints:: Controlling breakpoints
* Interrupted System Calls:: GDB may interfere with system calls
* Observer Mode:: GDB does not alter program behavior
@end menu
Expand Down Expand Up @@ -7252,6 +7254,44 @@ Process}), or if @value{GDBN} loses the remote connection
explictly asks for the thread list with the @code{info threads}
command.

@node Inferior-Specific Breakpoints
@subsection Inferior-Specific Breakpoints

It is also possible to limit breakpoints to specific inferior program space,
which can be especially useful when doing multi-target debugging with some
source files shared between targets.

@table @code
@cindex breakpoints and inferiors
@cindex inferior breakpoints
@kindex break @dots{} inferior @var{inferior-num}
@item break @var{location} inferior @var{inferior-num}
@itemx break @var{location} inferior @var{inferior-num} if @dots{}
@var{location} specifies source lines; there are several ways of
writing them (@pxref{Specify Location}), but the effect is always to
specify some source line.

Use the qualifier @samp{inferior @var{inferior-num}} with a breakpoint command
to specify that you only want @value{GDBN} to stop the program if execution
happens in a context of the specified inferior. The @var{inferior-num} number
is one of the inferior numbers as shown by @samp{info inferior} command output.

One major difference compared to @pxref{Thread-Specific Breakpoints} is that
inferior specific breakpoints won't be inserted at all for other inferiors and
thus won't be shown by the @samp{info break} command.

If inferior gets removed while inferior-specific breakpoint is present, a
warning will be printed and the breakpoint will never be hit.

You can use the @code{inferior} qualifier with the other qualifiers and
conditionals too:

@smallexample
(@value{GDBP}) break frik.c:13 inferior 1 thread 2 if bartab > lim
@end smallexample

@end table

@node Interrupted System Calls
@subsection Interrupted System Calls

Expand Down
6 changes: 6 additions & 0 deletions gdb/doc/python.texi
Original file line number Diff line number Diff line change
Expand Up @@ -5943,6 +5943,12 @@ thread's global id. If the breakpoint is not thread-specific, this
attribute is @code{None}. This attribute is writable.
@end defvar

@defvar Breakpoint.inferior
If the breakpoint is inferior-specific, this attribute holds the inferior
number. If the breakpoint is not inferior-specific, this attribute is
@code{None}. This attribute is writable.
@end defvar

@defvar 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
Expand Down
2 changes: 2 additions & 0 deletions gdb/inferior.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ inferior::clear_thread_list (bool silent)
void
delete_inferior (struct inferior *inf)
{
delete_breakpoints_inf (inf);

inf->clear_thread_list (true);

auto it = inferior_list.iterator_to (*inf);
Expand Down
4 changes: 3 additions & 1 deletion gdb/linespec.c
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,9 @@ typedef enum ls_token_type linespec_token_type;

/* List of keywords. This is NULL-terminated so that it can be used
as enum completer. */
const char * const linespec_keywords[] = { "if", "thread", "task", "-force-condition", NULL };
const char * const linespec_keywords[] = { "if", "thread", "task",
"-force-condition",
"inferior", NULL };
#define IF_KEYWORD_INDEX 0
#define FORCE_KEYWORD_INDEX 3

Expand Down
61 changes: 61 additions & 0 deletions gdb/python/py-breakpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,48 @@ bppy_set_thread (PyObject *self, PyObject *newvalue, void *closure)
return 0;
}

/* Python function to set the inferior of a breakpoint. */
static int
bppy_set_inferior (PyObject *self, PyObject *newvalue, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
long num;

BPPY_SET_REQUIRE_VALID (self_bp);

if (newvalue == NULL)
{
PyErr_SetString (PyExc_TypeError,
_("Cannot delete `inferior' attribute."));
return -1;
}
else if (PyInt_Check (newvalue))
{
if (! gdb_py_int_as_long (newvalue, &num))
return -1;

if (find_inferior_id (num) == nullptr)
{
PyErr_SetString (PyExc_RuntimeError,
_("Invalid inferior number."));
return -1;
}
}
else if (newvalue == Py_None)
num = 0;
else
{
PyErr_SetString (
PyExc_TypeError,
_("The value of `inferior' must be an integer or None."));
return -1;
}

breakpoint_set_inferior (self_bp->bp, num);

return 0;
}

/* Python function to set the (Ada) task of a breakpoint. */
static int
bppy_set_task (PyObject *self, PyObject *newvalue, void *closure)
Expand Down Expand Up @@ -656,6 +698,20 @@ bppy_get_thread (PyObject *self, void *closure)
return gdb_py_object_from_longest (self_bp->bp->thread).release ();
}

/* Python function to get the breakpoint's inferior number. */
static PyObject *
bppy_get_inferior (PyObject *self, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;

BPPY_REQUIRE_VALID (self_bp);

if (self_bp->bp->inferior == 0)
Py_RETURN_NONE;

return gdb_py_object_from_longest (self_bp->bp->inferior).release ();
}

/* Python function to get the breakpoint's task ID (in Ada). */
static PyObject *
bppy_get_task (PyObject *self, void *closure)
Expand Down Expand Up @@ -1230,6 +1286,11 @@ static gdb_PyGetSetDef breakpoint_object_getset[] = {
"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 },
{ "inferior", bppy_get_inferior, bppy_set_inferior,
"Inferior number for the breakpoint.\n\
If the value is an integer, then this is an inferior-specific breakpoint.\n\
If the value is None, then this breakpoint applies to all inferiors.\n\
No other type of value can be used.", NULL },
{ "task", bppy_get_task, bppy_set_task,
"Thread ID for the breakpoint.\n\
Expand Down
23 changes: 23 additions & 0 deletions gdb/testsuite/gdb.base/breakpoint-inferior.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2019-2020 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
main (void)
{
int i = 1; /* break-here */
return 0;
}
78 changes: 78 additions & 0 deletions gdb/testsuite/gdb.base/breakpoint-inferior.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Copyright 2019-2020 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/>.

standard_testfile

# Check if start command is supported.
if {[use_gdb_stub]} {
return 0
}

if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} {
return -1
}

# Test pending inferior breakpoint

gdb_test "add-inferior"
set bp_loc [gdb_get_line_number "break-here"]
gdb_breakpoint "$bp_loc inferior 2" {allow-pending}

gdb_test "inferior 1" "Switching to inferior 1.*" "inferior 1 (pending)"
if {[gdb_start_cmd] < 0} {
fail "start inferior 1"
return
}
gdb_test "" ".*reakpoint ., main .*${srcfile}.*" "run inferior 1 to main"
gdb_continue_to_end "inferior 1"

gdb_test "inferior 2"
gdb_test "file $binfile" "Reading symbols.*" "inferior 2 symbols (pending)"
if {[gdb_run_cmd] < 0} {
fail "run inferior 2"
return
}
gdb_test_multiple "" "run inferior 2 until bp" {
-re -wrap "Breakpoint .*$srcfile:$bp_loc.*" {
pass $gdb_test_name
gdb_continue_to_end "run inferior 2 until end"
}
-re -wrap "exited normally" {
fail $gdb_test_name
}
}

gdb_test "inferior 1"
gdb_test "remove-inferiors 2"
gdb_test "info b" "No breakpoints or watchpoints."

set main_addr [get_hexadecimal_valueof "&main" 0x0]
gdb_breakpoint "*$main_addr inferior 1"
gdb_test "add-inferior -exec $binfile" "Reading symbols.*" "inferior 3 symbols"
gdb_test_multiple "info b" "only inf 1 after location update" {
-re -wrap "inf ($decimal)" {
set inf_no $expect_out(1,string)
gdb_assert {$inf_no == 1} $gdb_test_name
}
}
gdb_test "set schedule-multiple on"
if {[gdb_run_cmd] < 0} {
fail "run with addr-based bp"
} else {
gdb_test "" "Breakpoint ., main .*${srcfile}.*" "run with addr-based bp"
}

gdb_test "print \$_inferior" " = 1"
gdb_continue_to_end "final run to end"
1 change: 1 addition & 0 deletions gdb/testsuite/gdb.linespec/explicit.exp
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ namespace eval $testfile {
"-qualified"
"-source"
"if"
"inferior"
"task"
"thread"
}
Expand Down
6 changes: 5 additions & 1 deletion gdb/testsuite/gdb.python/py-breakpoint.exp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ proc_with_prefix test_bkpt_basic { } {
"Get Breakpoint List" 0
gdb_test "python print (blist\[1\].thread)" \
"None" "Check breakpoint thread"
gdb_test "python print (blist\[1\].inferior)" \
"None" "Check breakpoint inferior"
gdb_test "python print (blist\[1\].type == gdb.BP_BREAKPOINT)" \
"True" "Check breakpoint type"
gdb_test "python print (blist\[0\].number)" \
Expand Down Expand Up @@ -161,10 +163,12 @@ proc_with_prefix test_bkpt_cond_and_cmds { } {

# Test conditional setting.
set bp_location1 [gdb_get_line_number "Break at multiply."]
gdb_py_test_silent_cmd "python bp1 = gdb.Breakpoint (\"$bp_location1 thread 1\")" \
gdb_py_test_silent_cmd "python bp1 = gdb.Breakpoint (\"$bp_location1 thread 1 inferior 1\")" \
"Set breakpoint" 0
gdb_test "python print (bp1.thread == 1)" "True" \
"Extra thread spec has been parsed"
gdb_test "python print (bp1.inferior == 1)" "True" \
"Extra inferior spec has been parsed"
gdb_continue_to_breakpoint "Break at multiply" \
".*Break at multiply.*"
gdb_py_test_silent_cmd "python bp1.condition = \"i == 5\"" \
Expand Down
2 changes: 1 addition & 1 deletion gdb/testsuite/lib/completion-support.exp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace eval completion {
# List of all quote chars, including no-quote at all.
variable maybe_quoted_list {"" "'" "\""}

variable keyword_list {"-force-condition" "if" "task" "thread"}
variable keyword_list {"-force-condition" "if" "inferior" "task" "thread"}

variable explicit_opts_list \
{"-function" "-label" "-line" "-qualified" "-source"}
Expand Down

0 comments on commit 7d87ac9

Please sign in to comment.