aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Tromey <tromey@adacore.com>2021-08-30 13:58:48 -0600
committerTom Tromey <tromey@adacore.com>2021-12-02 08:58:22 -0700
commit8a18382f94515b4be7e51dbe3865d202403d21d5 (patch)
treec3621e18b8693281e0b45d5a1db988bd103f0947
parent36cb9e7e17cae5e05f007f37e555873c4f22594a (diff)
downloadgdb-8a18382f94515b4be7e51dbe3865d202403d21d5.zip
gdb-8a18382f94515b4be7e51dbe3865d202403d21d5.tar.gz
gdb-8a18382f94515b4be7e51dbe3865d202403d21d5.tar.bz2
Add "task" keyword to the "watch" command
Breakpoints in gdb can be made specific to an Ada task using the "task" qualifier. This patch applies this same idea to watchpoints.
-rw-r--r--gdb/NEWS3
-rw-r--r--gdb/breakpoint.c12
-rw-r--r--gdb/doc/gdb.texinfo5
-rw-r--r--gdb/testsuite/gdb.ada/task_watch.exp83
-rw-r--r--gdb/testsuite/gdb.ada/task_watch/foo.adb73
5 files changed, 175 insertions, 1 deletions
diff --git a/gdb/NEWS b/gdb/NEWS
index eeca1d3..b0a3a08 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -24,6 +24,9 @@ show varsize-limit
These are now deprecated aliases for "set max-value-size" and
"show max-value-size".
+watch [...] task ID
+ Watchpoints can now be restricted to a specific Ada task.
+
maint set internal-error backtrace on|off
maint show internal-error backtrace
maint set internal-warning backtrace on|off
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index b1eae3a..acf3881 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -10557,6 +10557,7 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
the hardware watchpoint. */
bool use_mask = false;
CORE_ADDR mask = 0;
+ int task = 0;
/* Make sure that we actually have parameters to parse. */
if (arg != NULL && arg[0] != '\0')
@@ -10612,6 +10613,16 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
thread = thr->global_num;
}
+ else if (toklen == 4 && startswith (tok, "task"))
+ {
+ char *tmp;
+
+ task = strtol (value_start, &tmp, 0);
+ if (tmp == value_start)
+ error (_("Junk after task keyword."));
+ if (!valid_task_id (task))
+ error (_("Unknown task %d."), task);
+ }
else if (toklen == 4 && startswith (tok, "mask"))
{
/* We've found a "mask" token, which means the user wants to
@@ -10785,6 +10796,7 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
init_raw_breakpoint_without_location (w.get (), NULL, bp_type,
&watchpoint_breakpoint_ops);
w->thread = thread;
+ w->task = task;
w->disposition = disp_donttouch;
w->pspace = current_program_space;
w->exp = std::move (exp);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9d50779..40f3e24 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -4860,7 +4860,7 @@ slow down the running of your program.
@table @code
@kindex watch
-@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{thread-id}@r{]} @r{[}mask @var{maskvalue}@r{]}
+@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{thread-id}@r{]} @r{[}mask @var{maskvalue}@r{]} @r{[}task @var{task-id}@r{]}
Set a watchpoint for an expression. @value{GDBN} will break when the
expression @var{expr} is written into by the program and its value
changes. The simplest (and the most popular) use of this command is
@@ -4877,6 +4877,9 @@ change the value of @var{expr}, @value{GDBN} will not break. Note
that watchpoints restricted to a single thread in this way only work
with Hardware Watchpoints.
+Similarly, if the @code{task} argument is given, then the watchpoint
+will be specific to the indicated Ada task (@pxref{Ada Tasks}).
+
Ordinarily a watchpoint respects the scope of variables in @var{expr}
(see below). The @code{-location} argument tells @value{GDBN} to
instead watch the memory referred to by @var{expr}. In this case,
diff --git a/gdb/testsuite/gdb.ada/task_watch.exp b/gdb/testsuite/gdb.ada/task_watch.exp
new file mode 100644
index 0000000..fc276ef
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/task_watch.exp
@@ -0,0 +1,83 @@
+# Copyright 2009-2021 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/>.
+
+# Test task-specific watchpoints.
+
+load_lib "ada.exp"
+
+if { [skip_ada_tests] } { return -1 }
+
+standard_ada_testfile foo
+
+if {[gdb_compile_ada "${srcfile}" "${binfile}" executable {debug}] != ""} {
+ return -1
+}
+
+clean_restart ${testfile}
+
+set bp_location [gdb_get_line_number "STOP_HERE" ${testdir}/foo.adb]
+runto "foo.adb:$bp_location"
+
+# Make sure that all tasks appear in the "info tasks" listing, and
+# that the active task is the environment task.
+gdb_test "info tasks" \
+ [join {" +ID +TID P-ID Pri State +Name" \
+ "\\* +1 .* main_task" \
+ " +2 .* task_list\\(1\\)" \
+ " +3 .* task_list\\(2\\)" \
+ " +4 .* task_list\\(3\\)"} \
+ "\r\n"] \
+ "info tasks before inserting breakpoint"
+
+# Insert a watchpoint that should stop only if task 3 stops, and
+# extract its number.
+set bp_number -1
+set test "watch -location value task 3"
+gdb_test_multiple $test $test {
+ -re "atchpoint ($decimal): -location value\r\n$gdb_prompt $" {
+ set bp_number $expect_out(1,string)
+ pass $test
+ }
+}
+
+if {$bp_number < 0} {
+ return
+}
+
+# Continue to that watchpoint. Task 2 should hit it first, and GDB
+# is expected to ignore that hit and resume the execution. Only then
+# task 3 will hit our watchpoint, and GDB is expected to stop at that
+# point. Also make sure that GDB reports the correct watchpoint number.
+gdb_test "continue" \
+ ".* hit .*atchpoint $bp_number: -location value.*Old value = 1.*New value = 2.*" \
+ "continue to watchpoint"
+
+# Check that it is indeed task 3 that hit the watchpoint by checking
+# which is the active task.
+gdb_test "info tasks" \
+ [join {" +ID +TID P-ID Pri State +Name" \
+ " +1 .* main_task" \
+ " +2 .* task_list\\(1\\)" \
+ "\\* +3 .* task_list\\(2\\)" \
+ " +4 .* task_list\\(3\\)"} \
+ "\r\n"] \
+ "info tasks after hitting watchpoint"
+
+# Now, resume the execution and make sure that GDB does not stop when
+# task 4 hits the watchpoint. Continuing thus results in our program
+# running to completion.
+set bp_location [gdb_get_line_number "STOP_HERE_2" ${testdir}/foo.adb]
+gdb_breakpoint foo.adb:$bp_location
+gdb_continue_to_breakpoint second ".*foo.adb:$bp_location.*null; -- STOP_HERE_2"
diff --git a/gdb/testsuite/gdb.ada/task_watch/foo.adb b/gdb/testsuite/gdb.ada/task_watch/foo.adb
new file mode 100644
index 0000000..f3540ec
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/task_watch/foo.adb
@@ -0,0 +1,73 @@
+-- Copyright 2009-2021 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/>.
+
+procedure Foo is
+
+ Value : Integer := 0;
+
+ task type Caller is
+ entry Initialize;
+ entry Call_Break_Me;
+ entry Finalize;
+ end Caller;
+ type Caller_Ptr is access Caller;
+
+ procedure Break_Me is
+ begin
+ Value := Value + 1;
+ end Break_Me;
+
+ task body Caller is
+ begin
+ accept Initialize do
+ null;
+ end Initialize;
+ accept Call_Break_Me do
+ Break_Me;
+ end Call_Break_Me;
+ accept Finalize do
+ null;
+ end Finalize;
+ end Caller;
+
+ Task_List : array (1 .. 3) of Caller_Ptr;
+
+begin
+
+ -- Start all our tasks, and call the "Initialize" entry to make
+ -- sure all of them have now been started. We call that entry
+ -- immediately after having created the task in order to make sure
+ -- that we wait for that task to be created before we try to create
+ -- another one. That way, we know that the order in our Task_List
+ -- corresponds to the order in the GNAT runtime.
+ for J in Task_List'Range loop
+ Task_List (J) := new Caller;
+ Task_List (J).Initialize;
+ end loop;
+
+ -- Next, call their Call_Break_Me entry of each task, using the same
+ -- order as the order used to create them.
+ for J in Task_List'Range loop -- STOP_HERE
+ Task_List (J).Call_Break_Me;
+ end loop;
+
+ -- And finally, let all the tasks die...
+ for J in Task_List'Range loop
+ Task_List (J).Finalize;
+ end loop;
+
+ null; -- STOP_HERE_2
+
+end Foo;